implement special address field for addr:street/addr:place

This commit is contained in:
Martin Raifer
2023-04-24 17:42:26 +02:00
parent a0b5cf5334
commit 026737909f
4 changed files with 136 additions and 36 deletions

View File

@@ -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

View File

@@ -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"]
]
}
]

View File

@@ -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'];

View File

@@ -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) {