Don't validate vertices which extend beyond the downloaded map

(closes #5938)
This commit is contained in:
Bryan Housel
2019-04-10 13:40:06 -04:00
parent f309e925d8
commit 9100ce3ee4
2 changed files with 151 additions and 140 deletions
+97 -90
View File
@@ -28,95 +28,6 @@ export function validationAlmostJunction() {
return node.tags.noexit && node.tags.noexit === 'yes';
}
function isExtendableCandidate(node, way, graph) {
if (isNoexit(node) || graph.parentWays(node).length !== 1) {
return false;
}
var occurences = 0;
for (var index in way.nodes) {
if (way.nodes[index] === node.id) {
occurences += 1;
if (occurences > 1) {
return false;
}
}
}
return true;
}
function findConnectableEndNodesByExtension(way, graph, tree) {
var results = [];
if (way.isClosed()) return results;
var testNodes;
var endpointIndicies = [0, way.nodes.length - 1];
endpointIndicies.forEach(function(nodeIndex) {
var nodeID = way.nodes[nodeIndex];
var node = graph.entity(nodeID);
if (!isExtendableCandidate(node, way, graph)) return;
var connectionInfo = canConnectByExtend(way, nodeIndex, graph, tree);
if (!connectionInfo) return;
testNodes = graph.childNodes(way).slice(); // shallow copy
testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc);
// don't flag issue if connecting the ways would cause self-intersection
if (geoHasSelfIntersections(testNodes, nodeID)) return;
results.push(connectionInfo);
});
return results;
}
function canConnectByExtend(way, endNodeIdx, graph, tree) {
var EXTEND_TH_METERS = 5;
var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point
var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge
var tipNode = graph.entity(tipNid);
var midNode = graph.entity(midNid);
var lon = tipNode.loc[0];
var lat = tipNode.loc[1];
var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
var queryExtent = geoExtent([
[lon - lon_range, lat - lat_range],
[lon + lon_range, lat + lat_range]
]);
// first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location
var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
var t = EXTEND_TH_METERS / edgeLen + 1.0;
var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t);
// then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
var intersected = tree.intersects(queryExtent, graph);
for (var i = 0; i < intersected.length; i++) {
if (!isHighway(intersected[i]) || intersected[i].id === way.id) continue;
var way2 = intersected[i];
for (var j = 0; j < way2.nodes.length - 1; j++) {
var nA = graph.entity(way2.nodes[j]);
var nB = graph.entity(way2.nodes[j + 1]);
var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
if (crossLoc) {
return {
node: tipNode,
wid: way2.id,
edge: [nA.id, nB.id],
cross_loc: crossLoc
};
}
}
}
return null;
}
var validation = function checkAlmostJunction(entity, context) {
if (!isHighway(entity)) return [];
@@ -126,7 +37,7 @@ export function validationAlmostJunction() {
var tree = context.history().tree();
var issues = [];
var extendableNodeInfos = findConnectableEndNodesByExtension(entity, graph, tree);
var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
extendableNodeInfos.forEach(function(extendableNodeInfo) {
var node = extendableNodeInfo.node;
var edgeHighway = graph.entity(extendableNodeInfo.wid);
@@ -192,6 +103,102 @@ export function validationAlmostJunction() {
});
return issues;
function isExtendableCandidate(node, way) {
// can not accurately test vertices on tiles not downloaded from osm - #5938
var osm = context.connection();
if (osm && !osm.isDataLoaded(node.loc)) {
return false;
}
if (isNoexit(node) || graph.parentWays(node).length !== 1) {
return false;
}
var occurences = 0;
for (var index in way.nodes) {
if (way.nodes[index] === node.id) {
occurences += 1;
if (occurences > 1) {
return false;
}
}
}
return true;
}
function findConnectableEndNodesByExtension(way) {
var results = [];
if (way.isClosed()) return results;
var testNodes;
var indices = [0, way.nodes.length - 1];
indices.forEach(function(nodeIndex) {
var nodeID = way.nodes[nodeIndex];
var node = graph.entity(nodeID);
if (!isExtendableCandidate(node, way)) return;
var connectionInfo = canConnectByExtend(way, nodeIndex);
if (!connectionInfo) return;
testNodes = graph.childNodes(way).slice(); // shallow copy
testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc);
// don't flag issue if connecting the ways would cause self-intersection
if (geoHasSelfIntersections(testNodes, nodeID)) return;
results.push(connectionInfo);
});
return results;
}
function canConnectByExtend(way, endNodeIdx) {
var EXTEND_TH_METERS = 5;
var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point
var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge
var tipNode = graph.entity(tipNid);
var midNode = graph.entity(midNid);
var lon = tipNode.loc[0];
var lat = tipNode.loc[1];
var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
var queryExtent = geoExtent([
[lon - lon_range, lat - lat_range],
[lon + lon_range, lat + lat_range]
]);
// first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location
var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
var t = EXTEND_TH_METERS / edgeLen + 1.0;
var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t);
// then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
var intersected = tree.intersects(queryExtent, graph);
for (var i = 0; i < intersected.length; i++) {
if (!isHighway(intersected[i]) || intersected[i].id === way.id) continue;
var way2 = intersected[i];
for (var j = 0; j < way2.nodes.length - 1; j++) {
var nA = graph.entity(way2.nodes[j]);
var nB = graph.entity(way2.nodes[j + 1]);
var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
if (crossLoc) {
return {
node: tipNode,
wid: way2.id,
edge: [nA.id, nB.id],
cross_loc: crossLoc
};
}
}
}
return null;
}
};
validation.type = type;
+54 -50
View File
@@ -22,59 +22,11 @@ export function validationDisconnectedWay() {
}
function vertexIsDisconnected(way, vertex, graph, relation) {
var parents = graph.parentWays(vertex);
// standalone vertex
if (parents.length === 1) return true;
// entrances are considered connected
if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return false;
return !parents.some(function(parentWay) {
// ignore the way we're testing
if (parentWay === way) return false;
if (isTaggedAsHighway(parentWay)) return true;
return graph.parentMultipolygons(parentWay).some(function(parentRelation) {
// ignore the relation we're testing, if any
if (relation && parentRelation === relation) return false;
return isTaggedAsHighway(parentRelation);
});
});
}
function isDisconnectedWay(entity, graph) {
if (entity.type !== 'way') return false;
return graph.childNodes(entity).every(function(vertex) {
return vertexIsDisconnected(entity, vertex, graph);
});
}
function isDisconnectedMultipolygon(entity, graph) {
if (entity.type !== 'relation' || !entity.isMultipolygon()) return false;
return entity.members.every(function(member) {
if (member.type !== 'way') return true;
var way = graph.hasEntity(member.id);
if (!way) return true;
return graph.childNodes(way).every(function(vertex) {
return vertexIsDisconnected(way, vertex, graph, entity);
});
});
}
var validation = function checkDisconnectedWay(entity, context) {
var graph = context.graph();
if (!isTaggedAsHighway(entity)) return [];
if (!isDisconnectedWay(entity, graph) && !isDisconnectedMultipolygon(entity, graph)) return [];
if (!isDisconnectedWay(entity) && !isDisconnectedMultipolygon(entity)) return [];
var entityLabel = utilDisplayLabel(entity, context);
var fixes = [];
@@ -131,6 +83,58 @@ export function validationDisconnectedWay() {
})];
function vertexIsDisconnected(way, vertex, relation) {
// can not accurately test vertices on tiles not downloaded from osm - #5938
var osm = context.connection();
if (osm && !osm.isDataLoaded(vertex.loc)) {
return false;
}
var parents = graph.parentWays(vertex);
// standalone vertex
if (parents.length === 1) return true;
// entrances are considered connected
if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return false;
return !parents.some(function(parentWay) {
if (parentWay === way) return false; // ignore the way we're testing
if (isTaggedAsHighway(parentWay)) return true;
return graph.parentMultipolygons(parentWay).some(function(parentRelation) {
// ignore the relation we're testing, if any
if (relation && parentRelation === relation) return false;
return isTaggedAsHighway(parentRelation);
});
});
}
function isDisconnectedWay(entity) {
if (entity.type !== 'way') return false;
return graph.childNodes(entity).every(function(vertex) {
return vertexIsDisconnected(entity, vertex);
});
}
function isDisconnectedMultipolygon(entity) {
if (entity.type !== 'relation' || !entity.isMultipolygon()) return false;
return entity.members.every(function(member) {
if (member.type !== 'way') return true;
var way = graph.hasEntity(member.id);
if (!way) return true;
return graph.childNodes(way).every(function(vertex) {
return vertexIsDisconnected(way, vertex, entity);
});
});
}
function continueDrawing(way, vertex) {
// make sure the vertex is actually visible and editable
var map = context.map();