modules/validations/duplicate_way_segments.js (91 lines of code) (raw):
import { utilDisplayLabel } from '../util';
import { t } from '../core/localizer';
import { validationIssue, validationIssueFix } from '../core/validation';
// This validation determines whether way segments are duplicated atop one another,
// which is impossible to detect via the tool(the geometries are stacked on top of
// one another, so they are not visible ever).
export function validationDuplicateWaySegments() {
var type = 'duplicate_way_segments';
var validation = function(entity, graph) {
if (entity.type === 'way') {
return getIssuesForWay(entity);
}
return [];
function isRoutableTag(key) {
return key === 'highway' ||
key === 'railway' ||
key === 'waterway';
}
// Consider a way to be routable if it is a highway, railway, or wateray.
// if it is an area of any kind, it is not routable.
function hasRoutableTags(way) {
if (way.isArea()) return false;
return Object.keys(way.tags).some(isRoutableTag);
}
function adjacentNodes(node1, node2, way) {
const nodes = graph.childNodes(way);
return Math.abs(nodes.findIndex(node => node.id === node1.id) - nodes.findIndex(node => node.id === node2.id)) === 1;
}
function getIssuesForWay(way) {
var issues = [];
if (!hasRoutableTags(way)) return issues;
var nodes = graph.childNodes(way);
for (var i = 0; i < nodes.length - 1; i++) {
var node1 = nodes[i];
var node2 = nodes[i+1];
var issue = getWayIssueIfAny(node1, node2, way);
if (issue) issues.push(issue);
}
return issues;
}
function getWayIssueIfAny(node1, node2, way) {
if (node1.id === node2.id ) {
return null;
}
if (node1.loc !== node2.loc) {
var parentWays1 = graph.parentWays(node1);
var parentWays2 = new Set(graph.parentWays(node2));
var sharedWays = parentWays1.filter(function(parentWay) {
return parentWays2.has(parentWay);
});
// Now, we want to filter out any shared ways that aren't routable.
var remainingSharedWays = sharedWays.filter(way => hasRoutableTags(way));
//Finally, get rid of ways where the two nodes in question are not continguous (this indicates a dogleg or u-shaped road splitting off from node1 and then re-joining at node 2)
var waysWithContiguousNodes = remainingSharedWays.filter(way => adjacentNodes(node1, node2, way));
// If the nodes don't share a way, or share 1 way, that's fine!
// We just want to know if they share 2 or more ways, which means we have duplicate way geometries.
if (waysWithContiguousNodes.length <= 1) return null;
}
return new validationIssue({
type: type,
subtype: 'vertices',
severity: 'warning',
message: function(context) {
var entity = context.hasEntity(this.entityIds[0]);
return entity ? t.html('issues.duplicate_way_segments.message', { way: utilDisplayLabel(entity, context.graph()) }) : '';
},
reference: showReference,
entityIds: [way.id, node1.id, node2.id],
loc: node1.loc,
dynamicFixes: function() {
return [
new validationIssueFix({
icon: 'iD-icon-plus',
title: t.html('issues.fix.merge_points.title'),
}),
new validationIssueFix({
icon: 'iD-operation-delete',
title: t.html('issues.fix.remove_way_segments.title')
}),
new validationIssueFix({
icon: 'iD-operation-disconnect',
title: t.html('issues.fix.move_way_segments_apart.title')
})
];
}
});
function showReference(selection) {
var referenceText = t('issues.duplicate_way_segments.reference');
selection.selectAll('.issue-reference')
.data([0])
.enter()
.append('div')
.attr('class', 'issue-reference')
.html(referenceText);
}
}
};
validation.type = type;
return validation;
}