mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
Flag detached points that are very close together (close #6394)
Fix lint error
This commit is contained in:
+4
-1
@@ -1338,9 +1338,12 @@ en:
|
||||
reference: Intersecting highways should share a junction vertex.
|
||||
close_nodes:
|
||||
title: "Very Close Points"
|
||||
tip: "Find redundant and crowded points"
|
||||
message: "Two points in {way} are very close together"
|
||||
tip: "Find redundant points in ways"
|
||||
reference: "Redundant points in a way should be merged or moved apart."
|
||||
detached:
|
||||
message: "{feature} is too close to {feature2}"
|
||||
reference: "Separate points should not share a location."
|
||||
crossing_ways:
|
||||
title: Crossings Ways
|
||||
message: "{feature} crosses {feature2}"
|
||||
|
||||
Vendored
+6
-2
@@ -1644,9 +1644,13 @@
|
||||
},
|
||||
"close_nodes": {
|
||||
"title": "Very Close Points",
|
||||
"tip": "Find redundant and crowded points",
|
||||
"message": "Two points in {way} are very close together",
|
||||
"tip": "Find redundant points in ways",
|
||||
"reference": "Redundant points in a way should be merged or moved apart."
|
||||
"reference": "Redundant points in a way should be merged or moved apart.",
|
||||
"detached": {
|
||||
"message": "{feature} is too close to {feature2}",
|
||||
"reference": "Separate points should not share a location."
|
||||
}
|
||||
},
|
||||
"crossing_ways": {
|
||||
"title": "Crossings Ways",
|
||||
|
||||
@@ -239,7 +239,7 @@ export function validationAlmostJunction() {
|
||||
|
||||
for (var j = 0; j < way2.nodes.length - 1; j++) {
|
||||
var nAid = way2.nodes[j],
|
||||
nBid = way2.nodes[j + 1]
|
||||
nBid = way2.nodes[j + 1];
|
||||
|
||||
if (nAid === tipNid || nBid === tipNid) continue;
|
||||
|
||||
|
||||
@@ -3,36 +3,38 @@ 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';
|
||||
|
||||
import { geoMetersToLat, geoMetersToLon, geoSphericalDistance } from '../geo/geo';
|
||||
import { geoExtent } from '../geo/extent';
|
||||
|
||||
export function validationCloseNodes() {
|
||||
var type = 'close_nodes';
|
||||
|
||||
var pointThresholdMeters = 0.2;
|
||||
|
||||
var defaultWayThresholdMeters = 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.boundary && way.tags.boundary !== 'no') return 'boundary';
|
||||
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';
|
||||
if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
|
||||
|
||||
var parentRelations = graph.parentRelations(way);
|
||||
for (var i in parentRelations) {
|
||||
var relation = parentRelations[i];
|
||||
|
||||
if (relation.tags.type === 'boundary') return 'boundary';
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,22 +67,20 @@ export function validationCloseNodes() {
|
||||
var node1 = nodes[i];
|
||||
var node2 = nodes[i+1];
|
||||
|
||||
var issue = getIssueIfAny(node1, node2, way, context);
|
||||
var issue = getWayIssueIfAny(node1, node2, way, context);
|
||||
if (issue) issues.push(issue);
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
function getIssuesForNode(node, context) {
|
||||
function getIssuesForVertex(node, parentWays, context) {
|
||||
var issues = [];
|
||||
|
||||
function checkForCloseness(node1, node2, way) {
|
||||
var issue = getIssueIfAny(node1, node2, way, context);
|
||||
var issue = getWayIssueIfAny(node1, node2, way, context);
|
||||
if (issue) issues.push(issue);
|
||||
}
|
||||
|
||||
var parentWays = context.graph().parentWays(node);
|
||||
|
||||
for (var i = 0; i < parentWays.length; i++) {
|
||||
var parentWay = parentWays[i];
|
||||
|
||||
@@ -100,11 +100,78 @@ export function validationCloseNodes() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
function getIssueIfAny(node1, node2, way, context) {
|
||||
function getIssuesForDetachedPoint(node, context) {
|
||||
|
||||
var issues = [];
|
||||
|
||||
var lon = node.loc[0];
|
||||
var lat = node.loc[1];
|
||||
var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
|
||||
var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
|
||||
var queryExtent = geoExtent([
|
||||
[lon - lon_range, lat - lat_range],
|
||||
[lon + lon_range, lat + lat_range]
|
||||
]);
|
||||
|
||||
var intersected = context.history().tree().intersects(queryExtent, context.graph());
|
||||
for (var j = 0; j < intersected.length; j++) {
|
||||
var nearby = intersected[j];
|
||||
|
||||
if (nearby.id === node.id) continue;
|
||||
if (nearby.type !== 'node' || nearby.geometry(context.graph()) !== 'point') continue;
|
||||
|
||||
if (nearby.loc === node.loc ||
|
||||
geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
|
||||
|
||||
issues.push(new validationIssue({
|
||||
type: type,
|
||||
severity: 'warning',
|
||||
message: function() {
|
||||
var entity = context.hasEntity(this.entityIds[0]),
|
||||
entity2 = context.hasEntity(this.entityIds[1]);
|
||||
return (entity && entity2) ? t('issues.close_nodes.detached.message', {
|
||||
feature: utilDisplayLabel(entity, context),
|
||||
feature2: utilDisplayLabel(entity2, context)
|
||||
}) : '';
|
||||
},
|
||||
reference: showReference,
|
||||
entityIds: [node.id, nearby.id],
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-disconnect',
|
||||
title: t('issues.fix.move_points_apart.title')
|
||||
})
|
||||
]
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return issues;
|
||||
|
||||
function showReference(selection) {
|
||||
var referenceText = t('issues.close_nodes.detached.reference');
|
||||
selection.selectAll('.issue-reference')
|
||||
.data([0])
|
||||
.enter()
|
||||
.append('div')
|
||||
.attr('class', 'issue-reference')
|
||||
.text(referenceText);
|
||||
}
|
||||
}
|
||||
|
||||
function getIssuesForNode(node, context) {
|
||||
var parentWays = context.graph().parentWays(node);
|
||||
if (parentWays.length) {
|
||||
return getIssuesForVertex(node, parentWays, context);
|
||||
} else {
|
||||
return getIssuesForDetachedPoint(node, context);
|
||||
}
|
||||
}
|
||||
|
||||
function getWayIssueIfAny(node1, node2, way, context) {
|
||||
if (node1.id === node2.id ||
|
||||
(node1.hasInterestingTags() && node2.hasInterestingTags())) {
|
||||
return null;
|
||||
@@ -113,7 +180,7 @@ export function validationCloseNodes() {
|
||||
if (node1.loc !== node2.loc) {
|
||||
|
||||
var featureType = featureTypeForWay(way, context.graph());
|
||||
var threshold = defaultThresholdMeters;
|
||||
var threshold = defaultWayThresholdMeters;
|
||||
if (featureType === 'indoor') threshold = indoorThresholdMeters;
|
||||
else if (featureType === 'building') threshold = buildingThresholdMeters;
|
||||
else if (featureType === 'path') threshold = pathThresholdMeters;
|
||||
|
||||
Reference in New Issue
Block a user