mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
Merge pull request #9603 from openstreetmap/addr_place
Support addr:place tag in Address field
This commit is contained in:
+3
-1
@@ -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
|
||||
|
||||
@@ -2128,6 +2128,19 @@ 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-place::after {
|
||||
content: url(data:image/svg+xml;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMCIgeT0iMCIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiB2aWV3Qm94PSIwIDAgMjAgMjAiPg0KICAgIDxwYXRoIGQ9Ik0xMCwzIEM2LjY4NiwzIDQsNS42MTIgNCw4LjgzMyBDNCwxMi4wNTUgMTAsMTcgMTAsMTcgQzEwLDE3IDE2LDEyLjA1NSAxNiw4LjgzMyBDMTYsNS42MTIgMTMuMzE0LDMgMTAsMyB6IE0xMC4xODcsNi41IEMxMS41MTMsNi41IDEyLjU4Nyw3LjU0NSAxMi41ODcsOC44MzMgQzEyLjU4NywxMC4xMjIgMTEuNTEzLDExLjE2NyAxMC4xODcsMTEuMTY3IEM4Ljg2MiwxMS4xNjcgNy43ODcsMTAuMTIyIDcuNzg3LDguODMzIEM3Ljc4Nyw3LjU0NSA4Ljg2Miw2LjUgMTAuMTg3LDYuNSB6IiBmaWxsPSJjdXJyZW50Q29sb3IiLz4NCjwvc3ZnPg==);
|
||||
}
|
||||
|
||||
|
||||
/* Field - Wikipedia
|
||||
|
||||
+48
-11
@@ -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,22 +24,31 @@
|
||||
"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", "housenumber"],
|
||||
["street+place", "housenumber"],
|
||||
["postcode", "city"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"countryCodes": [
|
||||
"ad", "ba", "be", "cz", "dk", "es", "fi", "gr", "hr", "is",
|
||||
"it", "li", "nl", "no", "pt", "se", "sk", "sm", "va"
|
||||
"ad", "ba", "be", "dk", "es", "gr", "hr",
|
||||
"it", "pt", "se", "sm", "va"
|
||||
],
|
||||
"format": [
|
||||
["street+place", "housenumber", "unit"],
|
||||
["postcode", "city"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"countryCodes": [
|
||||
"fi", "is", "li", "nl", "no"
|
||||
],
|
||||
"format": [
|
||||
["street", "housenumber", "unit"],
|
||||
@@ -47,16 +56,30 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"countryCodes": ["fr", "lu", "mo"],
|
||||
"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"]
|
||||
]
|
||||
@@ -89,7 +112,7 @@
|
||||
"countryCodes": ["tw"],
|
||||
"format": [
|
||||
["postcode", "city", "district"],
|
||||
["place", "street"],
|
||||
["street+place"],
|
||||
["housenumber", "floor", "unit"]
|
||||
]
|
||||
},
|
||||
@@ -121,7 +144,7 @@
|
||||
"countryCodes": ["tr"],
|
||||
"format": [
|
||||
["neighbourhood"],
|
||||
["street", "housenumber"],
|
||||
["street+place", "housenumber"],
|
||||
["postcode", "district", "city"]
|
||||
]
|
||||
},
|
||||
@@ -187,6 +210,20 @@
|
||||
["district"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"countryCodes": ["ru"],
|
||||
"format": [
|
||||
["housenumber", "street+place"],
|
||||
["city", "postcode"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"countryCodes": ["cz", "sk"],
|
||||
"format": [
|
||||
["street", "housenumber"],
|
||||
["postcode", "city"]
|
||||
]
|
||||
},
|
||||
{
|
||||
"countryCodes": ["ph"],
|
||||
"format": [
|
||||
|
||||
@@ -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'];
|
||||
|
||||
+146
-78
@@ -36,91 +36,97 @@ export function uiFieldAddress(field, context) {
|
||||
.catch(function() { /* ignore */ });
|
||||
|
||||
|
||||
function getNearStreets() {
|
||||
function getNear(isAddressable, type, 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);
|
||||
}
|
||||
|
||||
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: d.tags.name,
|
||||
value: d.tags.name,
|
||||
dist: choice.distance
|
||||
title,
|
||||
value,
|
||||
dist,
|
||||
type,
|
||||
klass: `address-${type}`
|
||||
};
|
||||
})
|
||||
.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, 'street', 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 getNearPlaces() {
|
||||
function isAddressable(d) {
|
||||
if (d.tags.name) {
|
||||
if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true;
|
||||
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);
|
||||
}
|
||||
|
||||
function getNearCities() {
|
||||
function isAddressable(d) {
|
||||
if (d.tags.name) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (d.tags['addr:city']) return true;
|
||||
if (d.tags[`${field.key}:city`]) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
var extent = combinedEntityExtent();
|
||||
var l = extent.center();
|
||||
var box = geoExtent(l).padByMeters(200);
|
||||
const tagKey = `${field.key}:${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;
|
||||
});
|
||||
function hasTag(d) {
|
||||
return _entityIDs.indexOf(d.id) === -1 && d.tags[tagKey];
|
||||
}
|
||||
|
||||
return utilArrayUniqBy(results, 'value');
|
||||
return getNear(hasTag, key, 200, tagKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -142,11 +148,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
|
||||
};
|
||||
|
||||
@@ -191,16 +197,45 @@ 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(value, callback) {
|
||||
callback(nearValues('addr:' + d.id));
|
||||
.fetcher(function(typedValue, callback) {
|
||||
typedValue = typedValue.toLowerCase();
|
||||
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';
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -248,42 +283,75 @@ 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 (subfield.id === 'street') {
|
||||
tags[`${field.key}:place`] = undefined;
|
||||
} else if (subfield.id === '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) {
|
||||
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}`];
|
||||
}
|
||||
return typeof val === 'string' ? val : '';
|
||||
})
|
||||
.attr('title', function(subfield) {
|
||||
|
||||
Reference in New Issue
Block a user