mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 09:12:52 +00:00
Check for valid multipolygon geometry when dragging nodes
(this can get a bit expensive for large/complex multipolygons)
This commit is contained in:
@@ -157,6 +157,20 @@ export function geoPathIntersections(path1, path2) {
|
||||
return intersections;
|
||||
}
|
||||
|
||||
export function geoPathHasIntersections(path1, path2) {
|
||||
for (var i = 0; i < path1.length - 1; i++) {
|
||||
for (var j = 0; j < path2.length - 1; j++) {
|
||||
var a = [ path1[i], path1[i+1] ];
|
||||
var b = [ path2[j], path2[j+1] ];
|
||||
var hit = geoLineIntersection(a, b);
|
||||
if (hit) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Return whether point is contained in polygon.
|
||||
//
|
||||
@@ -195,24 +209,13 @@ export function geoPolygonContainsPolygon(outer, inner) {
|
||||
|
||||
|
||||
export function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
|
||||
function testSegments(outer, inner) {
|
||||
for (var i = 0; i < outer.length - 1; i++) {
|
||||
for (var j = 0; j < inner.length - 1; j++) {
|
||||
var a = [ outer[i], outer[i + 1] ];
|
||||
var b = [ inner[j], inner[j + 1] ];
|
||||
if (geoLineIntersection(a, b)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function testPoints(outer, inner) {
|
||||
return _some(inner, function(point) {
|
||||
return geoPointInPolygon(point, outer);
|
||||
});
|
||||
}
|
||||
|
||||
return testPoints(outer, inner) || (!!checkSegments && testSegments(outer, inner));
|
||||
return testPoints(outer, inner) || (!!checkSegments && geoPathHasIntersections(outer, inner));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export { geoEdgeEqual } from './geom.js';
|
||||
export { geoHasSelfIntersections } from './geom.js';
|
||||
export { geoRotate } from './geom.js';
|
||||
export { geoLineIntersection } from './geom.js';
|
||||
export { geoPathHasIntersections } from './geom.js';
|
||||
export { geoPathIntersections } from './geom.js';
|
||||
export { geoPathLength } from './geom.js';
|
||||
export { geoPointInPolygon } from './geom.js';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import _find from 'lodash-es/find';
|
||||
|
||||
import {
|
||||
event as d3_event,
|
||||
select as d3_select
|
||||
@@ -21,12 +23,13 @@ import {
|
||||
import {
|
||||
geoChooseEdge,
|
||||
geoHasSelfIntersections,
|
||||
geoPathHasIntersections,
|
||||
geoVecSubtract,
|
||||
geoViewportEdge
|
||||
} from '../geo';
|
||||
|
||||
import { modeBrowse, modeSelect } from './index';
|
||||
import { osmNode } from '../osm';
|
||||
import { osmJoinWays, osmNode } from '../osm';
|
||||
import { uiFlash } from '../ui';
|
||||
|
||||
|
||||
@@ -174,15 +177,53 @@ export function modeDragNode(context) {
|
||||
|
||||
function invalidGeometry(entity, graph) {
|
||||
var parents = graph.parentWays(entity);
|
||||
var i, j, k;
|
||||
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
for (i = 0; i < parents.length; i++) {
|
||||
var parent = parents[i];
|
||||
var nodes = parent.nodes.map(function(nodeID) { return graph.entity(nodeID); });
|
||||
if (parent.isClosed()) {
|
||||
if (geoHasSelfIntersections(nodes, entity.id)) {
|
||||
var nodes = [];
|
||||
var activeIndex = null; // which multipolygon ring contains node being dragged
|
||||
|
||||
// test any parent multipolygons for valid geometry
|
||||
var relations = graph.parentRelations(parent);
|
||||
for (j = 0; j < relations.length; j++) {
|
||||
if (!relations[j].isMultipolygon()) continue;
|
||||
|
||||
var rings = osmJoinWays(relations[j].members, graph);
|
||||
|
||||
// find active ring and test it for self intersections
|
||||
for (k = 0; k < rings.length; k++) {
|
||||
nodes = rings[k].nodes;
|
||||
if (_find(nodes, function(n) { return n.id === entity.id; })) {
|
||||
activeIndex = k;
|
||||
if (geoHasSelfIntersections(nodes, entity.id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
rings[k].coords = nodes.map(function(n) { return n.loc; });
|
||||
}
|
||||
|
||||
// test active ring for intersections with other rings in the multipolygon
|
||||
for (k = 0; k < rings.length; k++) {
|
||||
if (k === activeIndex) continue;
|
||||
|
||||
// make sure active ring doesnt cross passive rings
|
||||
if (geoPathHasIntersections(rings[activeIndex].coords, rings[k].coords)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we still haven't tested this node's parent way for self-intersections.
|
||||
// (because it's not a member of a multipolygon), test it now.
|
||||
if (activeIndex !== null && parent.isClosed()) {
|
||||
nodes = parent.nodes.map(function(nodeID) { return graph.entity(nodeID); });
|
||||
if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -342,6 +342,12 @@ describe('iD.geo - geometry', function() {
|
||||
expect(iD.geoPolygonIntersectsPolygon(outer, inner)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns false when inner polygon fully contains outer', function() {
|
||||
var inner = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var outer = [[1, 1], [1, 2], [2, 2], [2, 1], [1, 1]];
|
||||
expect(iD.geoPolygonIntersectsPolygon(outer, inner)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns true when outer polygon partially contains inner (some vertices contained)', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[-1, -1], [1, 2], [2, 2], [2, 1], [1, 1]];
|
||||
|
||||
Reference in New Issue
Block a user