mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
Don't validate vertices which extend beyond the downloaded map
(closes #5938)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user