diff --git a/css/80_app.css b/css/80_app.css index 738d3a268..a11605bd2 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2100,6 +2100,22 @@ input.date-selector { .ideditor[dir='rtl'] .addr-row:last-of-type input:last-of-type { border-radius: 0 0 0 4px; } +.combobox-address-street-place .combobox-option.address-street, +.combobox-address-street-place .combobox-option.address-place { + padding-right: 20px; +} +.combobox-address-street-place .combobox-option.address-street::after, +.combobox-address-street-place .combobox-option.address-place::after { + position: absolute; + right: 2px; + opacity: 0.4; +} +.combobox-address-street-place .combobox-option.address-street::after { + content: url(data:image/svg+xml;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMCIgeT0iMCIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiB2aWV3Qm94PSIwIDAgMjAgMjAiPg0KICAgIDxwYXRoIGQ9Ik0xNC41NTgsMyBDMTMuMjEsMyAxMi4xMTYsNC4wOTMgMTIuMTE2LDUuNDQyIEMxMi4xMTYsNS42ODggMTIuMTYxLDUuOTIgMTIuMjI4LDYuMTQ0IEw2LjE0NCwxMi4yMjggQzUuOTIsMTIuMTYxIDUuNjg4LDEyLjExNiA1LjQ0MiwxMi4xMTYgQzQuMDkzLDEyLjExNiAzLDEzLjIxIDMsMTQuNTU4IEMzLDE1LjkwNyA0LjA5MywxNyA1LjQ0MiwxNyBDNi43OSwxNyA3Ljg4NCwxNS45MDcgNy44ODQsMTQuNTU4IEM3Ljg4NCwxNC4zMTIgNy44MzksMTQuMDggNy43NzIsMTMuODU2IEwxMy44NTYsNy43NzIgQzE0LjA4LDcuODM5IDE0LjMxMiw3Ljg4NCAxNC41NTgsNy44ODQgQzE1LjkwNyw3Ljg4NCAxNyw2Ljc5IDE3LDUuNDQyIEMxNyw0LjA5MyAxNS45MDcsMyAxNC41NTgsMyB6IE0xNC41NTgsNC40NjUgQzE1LjA5OCw0LjQ2NSAxNS41MzUsNC45MDIgMTUuNTM1LDUuNDQyIEMxNS41MzUsNS45ODEgMTUuMDk4LDYuNDE5IDE0LjU1OCw2LjQxOSBDMTQuMDE5LDYuNDE5IDEzLjU4MSw1Ljk4MSAxMy41ODEsNS40NDIgQzEzLjU4MSw0LjkwMiAxNC4wMTksNC40NjUgMTQuNTU4LDQuNDY1IHogTTUuNDQyLDEzLjU4MSBDNS45ODEsMTMuNTgxIDYuNDE5LDE0LjAxOSA2LjQxOSwxNC41NTggQzYuNDE5LDE1LjA5OCA1Ljk4MSwxNS41MzUgNS40NDIsMTUuNTM1IEM0LjkwMiwxNS41MzUgNC40NjUsMTUuMDk4IDQuNDY1LDE0LjU1OCBDNC40NjUsMTQuMDE5IDQuOTAyLDEzLjU4MSA1LjQ0MiwxMy41ODEgeiIgZmlsbD0iY3VycmVudENvbG9yIi8+DQo8L3N2Zz4=); +} +.combobox-address-street-place .combobox-option.address-place::after { + content: url(data:image/svg+xml;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMCIgeT0iMCIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiB2aWV3Qm94PSIwIDAgMjAgMjAiPg0KICAgIDxwYXRoIGQ9Ik0xMCwzIEM2LjY4NiwzIDQsNS42MTIgNCw4LjgzMyBDNCwxMi4wNTUgMTAsMTcgMTAsMTcgQzEwLDE3IDE2LDEyLjA1NSAxNiw4LjgzMyBDMTYsNS42MTIgMTMuMzE0LDMgMTAsMyB6IE0xMC4xODcsNi41IEMxMS41MTMsNi41IDEyLjU4Nyw3LjU0NSAxMi41ODcsOC44MzMgQzEyLjU4NywxMC4xMjIgMTEuNTEzLDExLjE2NyAxMC4xODcsMTEuMTY3IEM4Ljg2MiwxMS4xNjcgNy43ODcsMTAuMTIyIDcuNzg3LDguODMzIEM3Ljc4Nyw3LjU0NSA4Ljg2Miw2LjUgMTAuMTg3LDYuNSB6IiBmaWxsPSJjdXJyZW50Q29sb3IiLz4NCjwvc3ZnPg==); +} /* Field - Wikipedia diff --git a/data/address_formats.json b/data/address_formats.json index 26cd5ef6a..9f5b6ef00 100644 --- a/data/address_formats.json +++ b/data/address_formats.json @@ -32,7 +32,7 @@ { "countryCodes": ["at", "bg", "ch", "de", "si", "pl"], "format": [ - ["street", "housenumber"], + ["street+place", "housenumber"], ["postcode", "city"] ] }, @@ -42,7 +42,7 @@ "it", "li", "nl", "no", "pt", "se", "sk", "sm", "va" ], "format": [ - ["street", "housenumber", "unit"], + ["street+place", "housenumber", "unit"], ["postcode", "city"] ] }, @@ -186,5 +186,12 @@ ["city", "postcode"], ["district"] ] + }, + { + "countryCodes": ["ru"], + "format": [ + ["housenumber", "street+place"], + ["city", "postcode"] + ] } ] diff --git a/modules/services/streetside.js b/modules/services/streetside.js index 5a5b37f7f..52af3c3ef 100644 --- a/modules/services/streetside.js +++ b/modules/services/streetside.js @@ -852,7 +852,7 @@ export default { bubbleIdQuadKey = '0' + bubbleIdQuadKey; } const imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey; - const imgUrlSuffix = '.jpg?g=13305&n=z'; + const imgUrlSuffix = '.jpg?g=13515&n=z'; // Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12 const faceKeys = ['01','02','03','10','11','12']; diff --git a/modules/ui/fields/address.js b/modules/ui/fields/address.js index 8d0d5d6f1..7ef1fac2d 100644 --- a/modules/ui/fields/address.js +++ b/modules/ui/fields/address.js @@ -36,7 +36,7 @@ export function uiFieldAddress(field, context) { .catch(function() { /* ignore */ }); - function getNear(isAddressable, searchRadius, resultProp) { + function getNear(isAddressable, type, searchRadius, resultProp) { var extent = combinedEntityExtent(); var l = extent.center(); var box = geoExtent(l).padByMeters(searchRadius); @@ -55,11 +55,19 @@ export function uiFieldAddress(field, context) { dist = Math.min(dist, choice.distance); } - let value = resultProp && d.tags[resultProp] ? d.tags[resultProp] : d.tags.name; + const value = resultProp && d.tags[resultProp] ? d.tags[resultProp] : d.tags.name; + let title = value; + if (type === 'street') { + title = `${addrField.t('placeholders.street')}: ${title}`; + } else if (type === 'place') { + title = `${addrField.t('placeholders.place')}: ${title}`; + } return { - title: value, + title, value, - dist + dist, + type, + klass: `address-${type}` }; }) .sort(function(a, b) { @@ -74,7 +82,16 @@ export function uiFieldAddress(field, context) { return d.tags.highway && d.tags.name && d.type === 'way'; } - return getNear(isAddressable, 200); + return getNear(isAddressable, 'street', 200); + } + + function getNearPlaces() { + function isAddressable(d) { + return d.tags.name && d.tags.place || + d.tags.boundary === 'administrative' && d.tags.admin_level >= 8; + } + + return getNear(isAddressable, 'place', 200); } function getNearCities() { @@ -85,20 +102,28 @@ export function uiFieldAddress(field, context) { if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true; } - if (d.tags['addr:city']) return true; + if (d.tags[`${field.key}:city`]) return true; return false; } - return getNear(isAddressable, 200, 'addr:city'); + return getNear(isAddressable, 'city', 200, `${field.key}:city`); + } + + function getNearPostcodes() { + return [... new Set([] + .concat(getNearValues('postcode'))) + .concat(getNear(d => d.tags.postal_code, 'postcode', 200, 'postal_code'))]; } function getNearValues(key) { + const tagKey = `${field.key}:${key}`; + function hasTag(d) { - return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; + return _entityIDs.indexOf(d.id) === -1 && d.tags[tagKey]; } - return getNear(hasTag, 200, key); + return getNear(hasTag, key, 200, tagKey); } @@ -120,11 +145,11 @@ export function uiFieldAddress(field, context) { var dropdowns = addressFormat.dropdowns || [ 'city', 'county', 'country', 'district', 'hamlet', 'neighbourhood', 'place', 'postcode', 'province', - 'quarter', 'state', 'street', 'subdistrict', 'suburb' + 'quarter', 'state', 'street', 'street+place', 'subdistrict', 'suburb' ]; var widths = addressFormat.widths || { - housenumber: 1/3, street: 2/3, + housenumber: 1/5, unit: 1/5, street: 1/2, place: 1/2, city: 2/3, state: 1/4, postcode: 1/3 }; @@ -169,19 +194,46 @@ export function uiFieldAddress(field, context) { function addDropdown(d) { if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown - var nearValues = (d.id === 'street') ? getNearStreets - : (d.id === 'city') ? getNearCities - : getNearValues; + var nearValues; + switch (d.id) { + case 'street': + nearValues = getNearStreets; + break; + case 'place': + nearValues = getNearPlaces; + break; + case 'street+place': + nearValues = () => [] + .concat(getNearStreets()) + .concat(getNearPlaces()); + d.isAutoStreetPlace = true; + d.id = _tags[`${field.key}:place`] ? 'place' : 'street'; + break; + case 'city': + nearValues = getNearCities; + break; + case 'postcode': + nearValues = getNearPostcodes; + break; + default: + nearValues = getNearValues; + } d3_select(this) - .call(uiCombobox(context, 'address-' + d.id) + .call(uiCombobox(context, `address-${d.isAutoStreetPlace ? 'street-place' : d.id}`) .minItems(1) .caseSensitive(true) .fetcher(function(typedValue, callback) { typedValue = typedValue.toLowerCase(); - callback(nearValues('addr:' + d.id) + callback(nearValues(d.id) .filter(v => v.value.toLowerCase().indexOf(typedValue) !== -1)); }) + .on('accept', function(selected) { + if (d.isAutoStreetPlace) { + // set subtag depending on selected entry + d.id = selected ? selected.type : 'street'; + } + }) ); } @@ -228,42 +280,67 @@ export function uiFieldAddress(field, context) { function change(onInput) { return function() { - var tags = {}; + setTimeout(() => { + var tags = {}; - _wrap.selectAll('input') - .each(function (subfield) { - var key = field.key + ':' + subfield.id; + _wrap.selectAll('input') + .each(function (subfield) { + var key = field.key + ':' + subfield.id; - var value = this.value; - if (!onInput) value = context.cleanTagValue(value); + var value = this.value; + if (!onInput) value = context.cleanTagValue(value); - // don't override multiple values with blank string - if (Array.isArray(_tags[key]) && !value) return; + // don't override multiple values with blank string + if (Array.isArray(_tags[key]) && !value) return; - tags[key] = value || undefined; - }); + if (subfield.isAutoStreetPlace) { + if (key === `${field.key}:street`) { + tags[`${field.key}:place`] = undefined; + } else if (key === `${field.key}:place`) { + tags[`${field.key}:street`] = undefined; + } + } - dispatch.call('change', this, tags, onInput); + tags[key] = value || undefined; + }); + + dispatch.call('change', this, tags, onInput); + }, 0); }; } + function updatePlaceholder(inputSelection) { return inputSelection.attr('placeholder', function(subfield) { if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) { return t('inspector.multiple_values'); } - if (_countryCode) { - var localkey = subfield.id + '!' + _countryCode; - var tkey = addrField.hasTextForStringId('placeholders.' + localkey) ? localkey : subfield.id; - return addrField.t('placeholders.' + tkey); + if (subfield.isAutoStreetPlace) { + return `${getLocalPlaceholder('street')} / ${getLocalPlaceholder('place')}`; } + return getLocalPlaceholder(subfield.id); }); } + function getLocalPlaceholder(key) { + if (_countryCode) { + var localkey = key + '!' + _countryCode; + var tkey = addrField.hasTextForStringId('placeholders.' + localkey) ? localkey : key; + return addrField.t('placeholders.' + tkey); + } + } + + function updateTags(tags) { - utilGetSetValue(_wrap.selectAll('input'), function (subfield) { - var val = tags[field.key + ':' + subfield.id]; + utilGetSetValue(_wrap.selectAll('input'), subfield => { + var val; + if (subfield.isAutoStreetPlace) { + val = tags[`${field.key}:place`] || tags[`${field.key}:street`]; + subfield.id = _tags[`${field.key}:place`] ? 'place' : 'street'; + } else { + val = tags[`${field.key}:${subfield.id}`]; + } return typeof val === 'string' ? val : ''; }) .attr('title', function(subfield) {