From e5994881217c2fcd1f095ece26bd3ce24b148a49 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 20 May 2019 11:21:50 -0400 Subject: [PATCH] Lower the very close nodes warning threshold for buildings, building parts, and paths (re: #6374) Flag very close nodes in indoor features but with an extremely small threshold --- modules/osm/tags.js | 4 ++ modules/validations/close_nodes.js | 57 +++++++++++++++++++++------- modules/validations/crossing_ways.js | 14 +++---- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/modules/osm/tags.js b/modules/osm/tags.js index 8286fcb35..27c5fd842 100644 --- a/modules/osm/tags.js +++ b/modules/osm/tags.js @@ -115,6 +115,10 @@ export var osmRoutableHighwayTagValues = { unclassified: true, road: true, service: true, track: true, living_street: true, bus_guideway: true, path: true, footway: true, cycleway: true, bridleway: true, pedestrian: true, corridor: true, steps: true }; +// "highway" tag values that generally do not allow motor vehicles +export var osmPathHighwayTagValues = { + path: true, footway: true, cycleway: true, bridleway: true, pedestrian: true, corridor: true, steps: true +}; // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned') export var osmRailwayTrackTagValues = { diff --git a/modules/validations/close_nodes.js b/modules/validations/close_nodes.js index ad9d9d990..8a63c4724 100644 --- a/modules/validations/close_nodes.js +++ b/modules/validations/close_nodes.js @@ -2,12 +2,42 @@ import { actionMergeNodes } from '../actions/merge_nodes'; import { utilDisplayLabel } from '../util'; import { t } from '../util/locale'; import { validationIssue, validationIssueFix } from '../core/validation'; +import { osmPathHighwayTagValues } from '../osm/tags'; import { geoSphericalDistance } from '../geo/geo'; export function validationCloseNodes() { var type = 'close_nodes'; - var thresholdMeters = 0.2; + + // expect some features to be mapped with higher levels of detail + var indoorThresholdMeters = 0.01; + var buildingThresholdMeters = 0.05; + var pathThresholdMeters = 0.1; + var defaultThresholdMeters = 0.2; + + function featureTypeForWay(way, graph) { + + if (osmPathHighwayTagValues[way.tags.highway]) return 'path'; + + if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor'; + if ((way.tags.building && way.tags.building !== 'no') || + (way.tags['building:part'] && way.tags['building:part'] !== 'no')) return 'building'; + if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary'; + + var parentRelations = graph.parentRelations(way); + for (var i in parentRelations) { + var relation = parentRelations[i]; + if (relation.isMultipolygon()) { + if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor'; + if ((relation.tags.building && relation.tags.building !== 'no') || + (relation.tags['building:part'] && relation.tags['building:part'] !== 'no')) return 'building'; + } else { + if (relation.tags.type === 'boundary') return 'boundary'; + } + } + + return 'other'; + } function shouldCheckWay(way, context) { @@ -15,16 +45,8 @@ export function validationCloseNodes() { if (way.nodes.length <= 2 || (way.isClosed() && way.nodes.length <= 4)) return false; - // expect that indoor features may be mapped in very fine detail - if (way.tags.indoor) return false; - - var parentRelations = context.graph().parentRelations(way); - - // don't flag close nodes in boundaries since it's unlikely the user can accurately resolve them - if (way.tags.boundary) return false; - if (parentRelations.length && parentRelations.some(function(parentRelation) { - return parentRelation.tags.type === 'boundary'; - })) return false; + var featureType = featureTypeForWay(way, context.graph()); + if (featureType === 'boundary') return false; var bbox = way.extent(context.graph()).bbox(); var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); @@ -88,10 +110,17 @@ export function validationCloseNodes() { return null; } - var nodesAreVeryClose = node1.loc === node2.loc || - geoSphericalDistance(node1.loc, node2.loc) < thresholdMeters; + if (node1.loc !== node2.loc) { - if (!nodesAreVeryClose) return null; + var featureType = featureTypeForWay(way, context.graph()); + var threshold = defaultThresholdMeters; + if (featureType === 'indoor') threshold = indoorThresholdMeters; + else if (featureType === 'building') threshold = buildingThresholdMeters; + else if (featureType === 'path') threshold = pathThresholdMeters; + + var distance = geoSphericalDistance(node1.loc, node2.loc); + if (distance > threshold) return null; + } return new validationIssue({ type: type, diff --git a/modules/validations/crossing_ways.js b/modules/validations/crossing_ways.js index aab478e2c..0e04dd0a1 100644 --- a/modules/validations/crossing_ways.js +++ b/modules/validations/crossing_ways.js @@ -3,7 +3,7 @@ import { actionChangeTags } from '../actions/change_tags'; import { actionMergeNodes } from '../actions/merge_nodes'; import { geoExtent, geoLineIntersection, geoSphericalClosestNode } from '../geo'; import { osmNode } from '../osm/node'; -import { osmFlowingWaterwayTagValues, osmRailwayTrackTagValues, osmRoutableHighwayTagValues } from '../osm/tags'; +import { osmFlowingWaterwayTagValues, osmPathHighwayTagValues, osmRailwayTrackTagValues, osmRoutableHighwayTagValues } from '../osm/tags'; import { t } from '../util/locale'; import { utilDisplayLabel } from '../util'; import { validationIssue, validationIssueFix } from '../core/validation'; @@ -146,10 +146,6 @@ export function validationCrossingWays() { motorway: true, motorway_link: true, trunk: true, trunk_link: true, primary: true, primary_link: true, secondary: true, secondary_link: true }; - var pathHighways = { - path: true, footway: true, cycleway: true, bridleway: true, - pedestrian: true, steps: true, corridor: true - }; var nonCrossingHighways = { track: true }; function tagsForConnectionNodeIfAllowed(entity1, entity2) { @@ -157,8 +153,8 @@ export function validationCrossingWays() { var featureType2 = getFeatureTypeForTags(entity2.tags); if (featureType1 === featureType2) { if (featureType1 === 'highway') { - var entity1IsPath = pathHighways[entity1.tags.highway]; - var entity2IsPath = pathHighways[entity2.tags.highway]; + var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway]; + var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway]; if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) { // one feature is a path but not both @@ -184,8 +180,8 @@ export function validationCrossingWays() { var featureTypes = [featureType1, featureType2]; if (featureTypes.indexOf('highway') !== -1) { if (featureTypes.indexOf('railway') !== -1) { - if (pathHighways[entity1.tags.highway] || - pathHighways[entity2.tags.highway]) { + if (osmPathHighwayTagValues[entity1.tags.highway] || + osmPathHighwayTagValues[entity2.tags.highway]) { // path-rail connections use this tag return { railway: 'crossing' }; } else {