From 5cd5c10d92e2cb4b1cf0c9057680ddaf32468788 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 14 Apr 2023 17:08:04 +0200 Subject: [PATCH 01/12] consolidate similar methods --- modules/ui/fields/address.js | 82 +++++++++++++----------------------- 1 file changed, 30 insertions(+), 52 deletions(-) diff --git a/modules/ui/fields/address.js b/modules/ui/fields/address.js index 005e8d624..53b41cab2 100644 --- a/modules/ui/fields/address.js +++ b/modules/ui/fields/address.js @@ -36,62 +36,51 @@ export function uiFieldAddress(field, context) { .catch(function() { /* ignore */ }); - function getNearStreets() { + function getNear(isAddressable, searchRadius, resultProp) { var extent = combinedEntityExtent(); var l = extent.center(); - var box = geoExtent(l).padByMeters(200); + var box = geoExtent(l).padByMeters(searchRadius); - var streets = context.history().intersects(box) + var features = context.history().intersects(box) .filter(isAddressable) - .map(function(d) { - var loc = context.projection([ - (extent[0][0] + extent[1][0]) / 2, - (extent[0][1] + extent[1][1]) / 2 - ]); - var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection); + .map(d => { + let dist = geoSphericalDistance(d.extent(context.graph()).center(), l); + if (d.type === 'way') { + var loc = context.projection([ + (extent[0][0] + extent[1][0]) / 2, + (extent[0][1] + extent[1][1]) / 2 + ]); + var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection); + dist = Math.min(dist, choice.distance); + } + + let value = resultProp && d.tags[resultProp] ? d.tags[resultProp] : d.tags.name; return { - title: d.tags.name, - value: d.tags.name, - dist: choice.distance + title: value, + value, + dist }; }) .sort(function(a, b) { return a.dist - b.dist; }); - return utilArrayUniqBy(streets, 'value'); + return utilArrayUniqBy(features, 'value'); + } + function getNearStreets() { function isAddressable(d) { return d.tags.highway && d.tags.name && d.type === 'way'; } + + return getNear(isAddressable, 200); } - function getNearCities() { - var extent = combinedEntityExtent(); - var l = extent.center(); - var box = geoExtent(l).padByMeters(200); - - var cities = context.history().intersects(box) - .filter(isAddressable) - .map(function(d) { - return { - title: d.tags['addr:city'] || d.tags.name, - value: d.tags['addr:city'] || d.tags.name, - dist: geoSphericalDistance(d.extent(context.graph()).center(), l) - }; - }) - .sort(function(a, b) { - return a.dist - b.dist; - }); - - return utilArrayUniqBy(cities, 'value'); - - function isAddressable(d) { if (d.tags.name) { - if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true; + if (d.tags.boundary === 'administrative' && d.tags.admin_level === '8') return true; if (d.tags.border_type === 'city') return true; if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true; } @@ -100,27 +89,16 @@ export function uiFieldAddress(field, context) { return false; } + + return getNear(isAddressable, 200, 'addr:city'); } function getNearValues(key) { - var extent = combinedEntityExtent(); - var l = extent.center(); - var box = geoExtent(l).padByMeters(200); + function hasTag(d) { + return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; + } - var results = context.history().intersects(box) - .filter(function hasTag(d) { return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; }) - .map(function(d) { - return { - title: d.tags[key], - value: d.tags[key], - dist: geoSphericalDistance(d.extent(context.graph()).center(), l) - }; - }) - .sort(function(a, b) { - return a.dist - b.dist; - }); - - return utilArrayUniqBy(results, 'value'); + return getNear(hasTag, 200, key); } From a0b5cf53341bb127c5da6d9dfc97966a3ff13f13 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Mon, 17 Apr 2023 18:12:49 +0200 Subject: [PATCH 02/12] filter address combo field while typing --- modules/ui/fields/address.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/ui/fields/address.js b/modules/ui/fields/address.js index 53b41cab2..8d0d5d6f1 100644 --- a/modules/ui/fields/address.js +++ b/modules/ui/fields/address.js @@ -177,8 +177,10 @@ export function uiFieldAddress(field, context) { .call(uiCombobox(context, 'address-' + d.id) .minItems(1) .caseSensitive(true) - .fetcher(function(value, callback) { - callback(nearValues('addr:' + d.id)); + .fetcher(function(typedValue, callback) { + typedValue = typedValue.toLowerCase(); + callback(nearValues('addr:' + d.id) + .filter(v => v.value.toLowerCase().indexOf(typedValue) !== -1)); }) ); } From 026737909f4a08e18f2f3115843c364389633b0e Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Mon, 24 Apr 2023 17:42:26 +0200 Subject: [PATCH 03/12] implement special address field for addr:street/addr:place --- css/80_app.css | 16 ++++ data/address_formats.json | 11 ++- modules/services/streetside.js | 2 +- modules/ui/fields/address.js | 143 +++++++++++++++++++++++++-------- 4 files changed, 136 insertions(+), 36 deletions(-) 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) { From 2808edb57f88fe2106b532d518ddb7c54f6753d9 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 28 Apr 2023 17:12:48 +0200 Subject: [PATCH 04/12] tweak address formats: make street+place default using the "old" `addr:street`-only field on some countries which don't appear to use place-based addresses (according to current use in the OSM database): * Australia * Finland * Iceland * Liechtenstein * Netherlands * Norway * Monaco * Latvia * Peru * Bolivia * China * Ukraine * Taiwan (uses both addr:place and add:street in parallel!) * India * Canada * United States --- data/address_formats.json | 41 ++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/data/address_formats.json b/data/address_formats.json index 9f5b6ef00..5e6f9c79b 100644 --- a/data/address_formats.json +++ b/data/address_formats.json @@ -1,7 +1,7 @@ [ { "format": [ - ["housenumber", "street"], + ["housenumber", "street+place"], ["city", "postcode"] ] }, @@ -16,7 +16,7 @@ "countryCodes": ["gb"], "format": [ ["housename"], - ["housenumber", "street"], + ["housenumber", "street+place"], ["city", "postcode"] ] }, @@ -24,13 +24,13 @@ "countryCodes": ["ie"], "format": [ ["housename"], - ["housenumber", "street"], + ["housenumber", "street+place"], ["city"], ["postcode"] ] }, { - "countryCodes": ["at", "bg", "ch", "de", "si", "pl"], + "countryCodes": ["at", "bg", "ch", "de", "si", "pl", "lt"], "format": [ ["street+place", "housenumber"], ["postcode", "city"] @@ -38,8 +38,8 @@ }, { "countryCodes": [ - "ad", "ba", "be", "cz", "dk", "es", "fi", "gr", "hr", "is", - "it", "li", "nl", "no", "pt", "se", "sk", "sm", "va" + "ad", "ba", "be", "cz", "dk", "es", "gr", "hr", + "it", "pt", "se", "sk", "sm", "va" ], "format": [ ["street+place", "housenumber", "unit"], @@ -47,16 +47,39 @@ ] }, { - "countryCodes": ["fr", "lu", "mo"], + "countryCodes": [ + "fi", "is", "li", "nl", "no" + ], + "format": [ + ["street", "housenumber", "unit"], + ["postcode", "city"] + ] + }, + { + "countryCodes": ["fr", "lu"], + "format": [ + ["housenumber", "street+place"], + ["postcode", "city"] + ] + }, + { + "countryCodes": ["mo"], "format": [ ["housenumber", "street"], ["postcode", "city"] ] }, + { + "countryCodes": ["lv"], + "format": [ + ["street", "housenumber"], + ["city", "postcode"] + ] + }, { "countryCodes": ["br"], "format": [ - ["street"], + ["street+place"], ["housenumber", "suburb"], ["city", "postcode"] ] @@ -121,7 +144,7 @@ "countryCodes": ["tr"], "format": [ ["neighbourhood"], - ["street", "housenumber"], + ["street+place", "housenumber"], ["postcode", "district", "city"] ] }, From caac22fe16f23e2789565334e6fd5dac0c35e112 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 28 Apr 2023 17:50:03 +0200 Subject: [PATCH 05/12] show addr:street when both it and addr:place is present as in this case it is likely a mapping mistake where the "place" should actually be addr:city (or addr:suburb, etc.) --- modules/ui/fields/address.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/modules/ui/fields/address.js b/modules/ui/fields/address.js index 7ef1fac2d..245dd8ee7 100644 --- a/modules/ui/fields/address.js +++ b/modules/ui/fields/address.js @@ -294,9 +294,9 @@ export function uiFieldAddress(field, context) { if (Array.isArray(_tags[key]) && !value) return; if (subfield.isAutoStreetPlace) { - if (key === `${field.key}:street`) { + if (subfield.id === 'street') { tags[`${field.key}:place`] = undefined; - } else if (key === `${field.key}:place`) { + } else if (subfield.id === 'place') { tags[`${field.key}:street`] = undefined; } } @@ -336,8 +336,16 @@ export function uiFieldAddress(field, context) { 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'; + const streetKey = `${field.key}:street`; + const placeKey = `${field.key}:place`; + + if (tags[streetKey] !== undefined || tags[placeKey] === undefined) { + val = tags[streetKey]; + subfield.id = 'street'; + } else { + val = tags[placeKey]; + subfield.id = 'place'; + } } else { val = tags[`${field.key}:${subfield.id}`]; } From 316b819cc419e59b80996785a2158abd6bfe9824 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 28 Apr 2023 19:38:21 +0200 Subject: [PATCH 06/12] addresses in Czech Republic and Slovakia are more complicated see https://wiki.openstreetmap.org/wiki/Cs:%C4%8Cesko/Syst%C3%A9m_adres --- data/address_formats.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/data/address_formats.json b/data/address_formats.json index 5e6f9c79b..ec901f22b 100644 --- a/data/address_formats.json +++ b/data/address_formats.json @@ -38,8 +38,8 @@ }, { "countryCodes": [ - "ad", "ba", "be", "cz", "dk", "es", "gr", "hr", - "it", "pt", "se", "sk", "sm", "va" + "ad", "ba", "be", "dk", "es", "gr", "hr", + "it", "pt", "se", "sm", "va" ], "format": [ ["street+place", "housenumber", "unit"], @@ -216,5 +216,12 @@ ["housenumber", "street+place"], ["city", "postcode"] ] + }, + { + "countryCodes": ["cz", "sk"], + "format": [ + ["street", "housenumber"], + ["postcode", "city"] + ] } ] From aca88bf702f291236629d8f8fbdc81f760655896 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 28 Apr 2023 19:55:43 +0200 Subject: [PATCH 07/12] fix bug in getting nearby postcodes --- modules/ui/fields/address.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ui/fields/address.js b/modules/ui/fields/address.js index 245dd8ee7..44c4624ee 100644 --- a/modules/ui/fields/address.js +++ b/modules/ui/fields/address.js @@ -112,8 +112,8 @@ export function uiFieldAddress(field, context) { function getNearPostcodes() { return [... new Set([] - .concat(getNearValues('postcode'))) - .concat(getNear(d => d.tags.postal_code, 'postcode', 200, 'postal_code'))]; + .concat(getNearValues('postcode')) + .concat(getNear(d => d.tags.postal_code, 'postcode', 200, 'postal_code')))]; } function getNearValues(key) { From 2ed1f0888ecbb536d156a8f8b156ddd5d5ebd00c Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 28 Apr 2023 20:23:56 +0200 Subject: [PATCH 08/12] use addr+place subfield for Taiwan this assumes that the address format included both fields as a "hacky" workaround of iD's inability to choose between streets and places from a single field. This is (at the momemt) only based on the observation that before the format was introduced in iD in mid-2016, there were only a very small number of addresses in Taiwan with both tags, and the number increased only after iD started to show fields for both tags, and even today only a very small percentage of addresses have both tags set (approx 2.1k out of 1.6M). --- data/address_formats.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/address_formats.json b/data/address_formats.json index ec901f22b..a48598661 100644 --- a/data/address_formats.json +++ b/data/address_formats.json @@ -112,7 +112,7 @@ "countryCodes": ["tw"], "format": [ ["postcode", "city", "district"], - ["place", "street"], + ["street+place"], ["housenumber", "floor", "unit"] ] }, From 6f9bb116f3a5ac94bd5091647f4976160fd0dad6 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Sun, 30 Apr 2023 20:56:51 +0200 Subject: [PATCH 09/12] fix logic bug, make condition more readable --- modules/ui/fields/address.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/ui/fields/address.js b/modules/ui/fields/address.js index 44c4624ee..4bff2035e 100644 --- a/modules/ui/fields/address.js +++ b/modules/ui/fields/address.js @@ -87,8 +87,11 @@ export function uiFieldAddress(field, context) { function getNearPlaces() { function isAddressable(d) { - return d.tags.name && d.tags.place || - d.tags.boundary === 'administrative' && d.tags.admin_level >= 8; + if (d.tags.name) { + if (d.tags.place) return true; + if (d.tags.boundary === 'administrative' && d.tags.admin_level >= 8) return true; + } + return false; } return getNear(isAddressable, 'place', 200); From 9aa28faf4a21c74fff934b7edfa38a219837cc7c Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Sun, 30 Apr 2023 21:07:15 +0200 Subject: [PATCH 10/12] only offer admin_level>8 for addr:place as admin_level=8 is for `addr:city` --- modules/ui/fields/address.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/fields/address.js b/modules/ui/fields/address.js index 4bff2035e..e932571d8 100644 --- a/modules/ui/fields/address.js +++ b/modules/ui/fields/address.js @@ -89,7 +89,7 @@ export function uiFieldAddress(field, context) { function isAddressable(d) { if (d.tags.name) { if (d.tags.place) return true; - if (d.tags.boundary === 'administrative' && d.tags.admin_level >= 8) return true; + if (d.tags.boundary === 'administrative' && d.tags.admin_level > 8) return true; } return false; } From dbc857b0fe1d97752493f1c1d8d5cf45e143cd05 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 26 May 2023 20:45:23 +0200 Subject: [PATCH 11/12] update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e61d4326..9216e31f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ _Breaking developer changes, which may affect downstream projects or sites that #### :sparkles: Usability & Accessibility * Make it easier to search for OSM objects by id ([#9520], thanks [@k-yle]) * Localize numbers in numeric fields ([#8769], thanks [@1ec5]) +* The Address field now supports the `addr:place` tag (as an alternative to `addr:street`), this functionality is activated in selected countries ([#9603]) #### :scissors: Operations #### :camera: Street-Level #### :white_check_mark: Validation @@ -65,7 +66,7 @@ _Breaking developer changes, which may affect downstream projects or sites that * Render "right-side" arrows for features with lifecycle prefixes ([#9493], thanks [@k-yle]) * Take regional variants of parent presets into account when resolving preset fields ([#9524]) * Render "right-side" arrows for `man_made=quay` features -* Add support icons also in `multiCombo` and `semiCombo` fields ([#9433]) +* Support icons also in `multiCombo` and `semiCombo` fields ([#9433]) #### :hammer: Development * Upgrade dependencies: `fortawesome` to v6.4, `which-polygon` to v2.2.1, `glob` to v9.2, `temaki` to v5.4, `marked` to v4.3, `core-js-bundle` to v3.30, `osm-auth` to v2.1 * Bundle `package-lock.json` file in repository for faster `clean-install` builds @@ -82,6 +83,7 @@ _Breaking developer changes, which may affect downstream projects or sites that [#9501]: https://github.com/openstreetmap/iD/pull/9501 [#9520]: https://github.com/openstreetmap/iD/pull/9520 [#9524]: https://github.com/openstreetmap/iD/issues/9524 +[#9603]: https://github.com/openstreetmap/iD/pull/9603 [#9630]: https://github.com/openstreetmap/iD/pull/9630 [#9637]: https://github.com/openstreetmap/iD/pull/9637 [#9638]: https://github.com/openstreetmap/iD/pull/9638 From eb30c2e80a86409656232fb4d5ced318232efcd6 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 26 May 2023 20:45:51 +0200 Subject: [PATCH 12/12] drop icon from "regular" addr:street suggestions in dropdown --- css/80_app.css | 3 --- 1 file changed, 3 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index d93fc1104..8121a1b50 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2138,9 +2138,6 @@ input.date-selector { 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==); }