From 409968dfc201f31715eac18f6c1af768bb6d4cc0 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 14 May 2019 12:09:44 -0400 Subject: [PATCH] Add index of tags that positively identify points or vertices, derived from the presets Delete nodes that can only be vertices when deleting parent ways (close #6230) Allow snapping ways to nodes that cannot be positively identified as vertex-only --- modules/actions/delete_way.js | 17 ++++++++-- modules/core/context.js | 6 +++- modules/osm/index.js | 14 ++++++-- modules/osm/tags.js | 31 ++++++++++++++++- modules/presets/index.js | 63 +++++++++++++++++++++++++---------- 5 files changed, 106 insertions(+), 25 deletions(-) diff --git a/modules/actions/delete_way.js b/modules/actions/delete_way.js index dab4f0981..567d7a54a 100644 --- a/modules/actions/delete_way.js +++ b/modules/actions/delete_way.js @@ -1,3 +1,4 @@ +import { osmNodeGeometriesForTags } from '../osm/tags'; import { actionDeleteRelation } from './delete_relation'; @@ -5,9 +6,19 @@ import { actionDeleteRelation } from './delete_relation'; export function actionDeleteWay(wayID) { function canDeleteNode(node, graph) { - return !graph.parentWays(node).length && - !graph.parentRelations(node).length && - !node.hasInterestingTags(); + // don't delete nodes still attached to ways or relations + if (graph.parentWays(node).length || + graph.parentRelations(node).length) return false; + + var geometries = osmNodeGeometriesForTags(node.tags); + // don't delete if this node can be a standalone point + if (geometries.point) return false; + // delete if this node only be a vertex + if (geometries.vertex) return true; + + // iD doesn't know if this should be a point or vertex, + // so only delete if there are no interesting tags + return !node.hasInterestingTags(); } diff --git a/modules/core/context.js b/modules/core/context.js index 9facd82e3..e9ac76134 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -6,7 +6,7 @@ import { select as d3_select } from 'd3-selection'; import { t, currentLocale, addTranslation, setLocale } from '../util/locale'; -import { osmSetAreaKeys } from '../osm/tags'; +import { osmSetAreaKeys, osmSetPointTags, osmSetVertexTags } from '../osm/tags'; import { coreHistory } from './history'; import { coreValidator } from './validator'; @@ -553,10 +553,14 @@ export function coreContext() { presets.fromExternal(external, function(externalPresets) { context.presets = function() { return externalPresets; }; // default + external presets... osmSetAreaKeys(presets.areaKeys()); + osmSetPointTags(presets.pointTags()); + osmSetVertexTags(presets.vertexTags()); }); } else { presets.init(); osmSetAreaKeys(presets.areaKeys()); + osmSetPointTags(presets.pointTags()); + osmSetVertexTags(presets.vertexTags()); } return context; diff --git a/modules/osm/index.js b/modules/osm/index.js index 4b74c912b..87b34bdd4 100644 --- a/modules/osm/index.js +++ b/modules/osm/index.js @@ -24,7 +24,17 @@ export { } from './multipolygon'; export { + osmAreaKeys, + osmSetAreaKeys, + osmPointTags, + osmSetPointTags, + osmVertexTags, + osmSetVertexTags, + osmNodeGeometriesForTags, osmOneWayTags, osmPavedTags, - osmIsInterestingTag -} from './tags'; \ No newline at end of file + osmIsInterestingTag, + osmRoutableHighwayTagValues, + osmFlowingWaterwayTagValues, + osmRailwayTrackTagValues +} from './tags'; diff --git a/modules/osm/tags.js b/modules/osm/tags.js index 10fb9acfd..fc8ab9cd1 100644 --- a/modules/osm/tags.js +++ b/modules/osm/tags.js @@ -7,11 +7,40 @@ export function osmIsInterestingTag(key) { } export var osmAreaKeys = {}; - export function osmSetAreaKeys(value) { osmAreaKeys = value; } +// Tags that indicate a node can be a standalone point +// e.g. { amenity: { bar: true, parking: true, ... } ... } +export var osmPointTags = {}; +export function osmSetPointTags(value) { + osmPointTags = value; +} +// Tags that indicate a node can be part of a way +// e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... } +export var osmVertexTags = {}; +export function osmSetVertexTags(value) { + osmVertexTags = value; +} + +export function osmNodeGeometriesForTags(nodeTags) { + var geometries = {}; + for (var key in nodeTags) { + if (osmPointTags[key] && + (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) { + geometries.point = true; + } + if (osmVertexTags[key] && + (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) { + geometries.vertex = true; + } + // break early if both are already supported + if (geometries.point && geometries.vertex) break; + } + return geometries; +} + export var osmOneWayTags = { 'aerialway': { 'chair_lift': true, diff --git a/modules/presets/index.js b/modules/presets/index.js index a0611263b..b8c9ee48e 100644 --- a/modules/presets/index.js +++ b/modules/presets/index.js @@ -2,6 +2,7 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; import { json as d3_json } from 'd3-fetch'; import { data } from '../../data/index'; +import { osmNodeGeometriesForTags } from '../osm/tags'; import { presetCategory } from './category'; import { presetCollection } from './collection'; import { presetField } from './field'; @@ -86,24 +87,14 @@ export function presetIndex(context) { if (Object.keys(entity.tags).length === 0) return true; return resolver.transient(entity, 'vertexMatch', function() { - var vertexPresets = _index.vertex; - if (entity.isOnAddressLine(resolver)) { - return true; - } else { - var didFindMatches = false; - for (var k in entity.tags) { - var keyMatches = vertexPresets[k]; - if (!keyMatches) continue; - didFindMatches = true; - for (var i = 0; i < keyMatches.length; i++) { - var preset = keyMatches[i]; - if (preset.searchable !== false && preset.matchScore(entity.tags) > -1) { - return preset; - } - } - } - return !didFindMatches; - } + // address lines allow vertices to act as standalone points + if (entity.isOnAddressLine(resolver)) return true; + + var geometries = osmNodeGeometriesForTags(entity.tags); + if (geometries.vertex) return true; + if (geometries.point) return false; + // allow vertices for unspecified points + return true; }); }; @@ -156,6 +147,42 @@ export function presetIndex(context) { return areaKeys; }; + all.pointTags = function() { + return all.collection.reduce(function(pointTags, d) { + // ignore name-suggestion-index, deprecated, and generic presets + if (d.suggestion || d.replacement || d.searchable === false) return pointTags; + + // only care about the primary tag + for (var key in d.tags) break; + if (!key) return pointTags; + + // if this can be a point + if (d.geometry.indexOf('point') !== -1) { + pointTags[key] = pointTags[key] || {}; + pointTags[key][d.tags[key]] = true; + } + return pointTags; + }, {}); + }; + + all.vertexTags = function() { + return all.collection.reduce(function(vertexTags, d) { + // ignore name-suggestion-index, deprecated, and generic presets + if (d.suggestion || d.replacement || d.searchable === false) return vertexTags; + + // only care about the primary tag + for (var key in d.tags) break; + if (!key) return vertexTags; + + // if this can be a vertex + if (d.geometry.indexOf('vertex') !== -1) { + vertexTags[key] = vertexTags[key] || {}; + vertexTags[key][d.tags[key]] = true; + } + return vertexTags; + }, {}); + }; + all.build = function(d, visible) { if (d.fields) { Object.keys(d.fields).forEach(function(id) {