suggest housenumber/housename values from surrounding features (#10949)

housenumber/housename are taken from surrounding areas, as well as address points that are inside (or on the perimeter of) the same building as the selected feature
This commit is contained in:
Martin Raifer
2025-04-17 12:40:30 +02:00
committed by GitHub
parent a8f53e0ad4
commit 95143219a2
2 changed files with 87 additions and 4 deletions
+3
View File
@@ -49,10 +49,13 @@ _Breaking developer changes, which may affect downstream projects or sites that
#### :earth_asia: Localization
#### :hourglass: Performance
#### :mortar_board: Walkthrough / Help
#### :rocket: Presets
* Suggest housenumber/housename values from surrounding areas ([#10946])
#### :hammer: Development
[#9873]: https://github.com/openstreetmap/iD/issues/9873
[#10104]: https://github.com/openstreetmap/iD/issues/10104
[#10946]: https://github.com/openstreetmap/iD/issues/10946
[#10959]: https://github.com/openstreetmap/iD/issues/10959
[#10966]: https://github.com/openstreetmap/iD/issues/10966
+84 -4
View File
@@ -4,7 +4,7 @@ import * as countryCoder from '@rapideditor/country-coder';
import { presetManager } from '../../presets';
import { fileFetcher } from '../../core/file_fetcher';
import { geoExtent, geoChooseEdge, geoSphericalDistance } from '../../geo';
import { geoChooseEdge, geoSphericalDistance, geoPolygonContainsPolygon, geoPointInPolygon } from '../../geo';
import { uiCombobox } from '../combobox';
import { utilArrayUniqBy, utilGetSetValue, utilNoAuto, utilRebind, utilTotalExtent, utilTriggerEvent } from '../../util';
import { t } from '../../core/localizer';
@@ -39,7 +39,7 @@ export function uiFieldAddress(field, context) {
function getNear(isAddressable, type, searchRadius, resultProp) {
var extent = combinedEntityExtent();
var l = extent.center();
var box = geoExtent(l).padByMeters(searchRadius);
var box = extent.padByMeters(searchRadius);
var features = context.history().intersects(box)
.filter(isAddressable)
@@ -77,6 +77,35 @@ export function uiFieldAddress(field, context) {
return utilArrayUniqBy(features, 'value');
}
function getEnclosing(isAddressable, type, resultProp) {
var extent = combinedEntityExtent();
var features = context.history().intersects(extent)
.filter(isAddressable)
.map(d => {
if (d.geometry(context.graph()) !== 'area') {
return false;
}
const geom = d.asGeoJSON(context.graph()).coordinates[0];
if (!geoPolygonContainsPolygon(geom, extent.polygon())) {
return false;
}
const value = resultProp && d.tags[resultProp] ? d.tags[resultProp] : d.tags.name;
return {
title: value,
value,
dist: 0,
geom,
type,
klass: `address-${type}`
};
}).filter(Boolean);
return utilArrayUniqBy(features, 'value');
}
function getNearStreets() {
function isAddressable(d) {
return d.tags.highway && d.tags.name && d.type === 'way';
@@ -130,6 +159,37 @@ export function uiFieldAddress(field, context) {
return getNear(hasTag, key, 200, tagKey);
}
function getEnclosingValues(key) {
const tagKey = `${field.key}:${key}`;
// 1. areas encompassing the feature that have the address tag
function hasTag(d) {
return _entityIDs.indexOf(d.id) === -1 && d.tags[tagKey];
}
const enclosingAddresses = getEnclosing(hasTag, key, tagKey);
// 2. also include addresses from points which are encompassed by
// the same building area as the current feature
function isBuilding(d) {
return _entityIDs.indexOf(d.id) === -1 && d.tags.building && d.tags.building !== 'no';
}
const enclosingBuildings = getEnclosing(isBuilding, 'building', 'building').map(d => d.geom);
function isInNearbyBuilding(d) {
return hasTag(d) &&
d.type === 'node' &&
enclosingBuildings.some(geom =>
geoPointInPolygon(d.loc, geom) ||
geom.indexOf(d.loc) !== -1
);
}
const nearPointAddresses = getNear(isInNearbyBuilding, key, 100, tagKey);
return utilArrayUniqBy([
...enclosingAddresses,
...nearPointAddresses
], 'value').sort((a, b) => a.value > b.value ? 1 : -1);
}
function updateForCountryCode() {
@@ -146,6 +206,10 @@ export function uiFieldAddress(field, context) {
}
}
const maybeDropdowns = new Set([
'housenumber',
'housename'
]);
const dropdowns = new Set([
'block_number',
'city',
@@ -164,7 +228,8 @@ export function uiFieldAddress(field, context) {
'street+place',
'subdistrict',
'suburb',
'town'
'town',
...maybeDropdowns
]);
var widths = addressFormat.widths || {
@@ -211,7 +276,9 @@ export function uiFieldAddress(field, context) {
function addDropdown(d) {
if (!dropdowns.has(d.id)) return; // not a dropdown
if (!dropdowns.has(d.id)) {
return false; // not a dropdown
}
var nearValues;
switch (d.id) {
@@ -234,10 +301,23 @@ export function uiFieldAddress(field, context) {
case 'postcode':
nearValues = getNearPostcodes;
break;
case 'housenumber':
case 'housename':
nearValues = getEnclosingValues;
break;
default:
nearValues = getNearValues;
}
if (maybeDropdowns.has(d.id)) {
const candidates = nearValues(d.id);
// only add dropdown if there are possible values for the
// corresponding tag: e.g. only show ▼ caret for
// housenumber/housename if the feature is actually
// encompassed by another feature with such an address
if (candidates.length === 0) return false;
}
d3_select(this)
.call(uiCombobox(context, `address-${d.isAutoStreetPlace ? 'street-place' : d.id}`)
.minItems(1)