mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 01:02:58 +00:00
Merge pull request #5745 from jguthrie100/stop_merge_overlapping_self
Add check to stop joining ways if resulting way intersects itself
This commit is contained in:
4
dist/locales/en.json
vendored
4
dist/locales/en.json
vendored
@@ -193,7 +193,9 @@
|
||||
"restriction": "These features can't be merged because it would damage a \"{relation}\" relation.",
|
||||
"relation": "These features can't be merged because they have conflicting relation roles.",
|
||||
"incomplete_relation": "These features can't be merged because at least one hasn't been fully downloaded.",
|
||||
"conflicting_tags": "These features can't be merged because some of their tags have conflicting values."
|
||||
"conflicting_tags": "These features can't be merged because some of their tags have conflicting values.",
|
||||
"paths_intersect": "These features can't be merged because the resulting path would intersect itself"
|
||||
|
||||
},
|
||||
"move": {
|
||||
"title": "Move",
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import _extend from 'lodash-es/extend';
|
||||
import _groupBy from 'lodash-es/groupBy';
|
||||
import _intersection from 'lodash-es/intersection';
|
||||
|
||||
import { actionDeleteWay } from './delete_way';
|
||||
import { osmIsInterestingTag, osmJoinWays } from '../osm';
|
||||
import { geoPathIntersections } from '../geo';
|
||||
|
||||
|
||||
// Join ways at the end node they share.
|
||||
@@ -70,6 +72,27 @@ export function actionJoin(ids) {
|
||||
if (joined.length > 1)
|
||||
return 'not_adjacent';
|
||||
|
||||
// Loop through all combinations of path-pairs to check potential intersections
|
||||
// between all pairs
|
||||
for (var i = 0; i < ids.length-1; i++) {
|
||||
for (var j = i+1; j < ids.length; j++) {
|
||||
var path1 = graph.childNodes(graph.entity(ids[i])).map(function(e) {
|
||||
return e.loc;
|
||||
});
|
||||
var path2 = graph.childNodes(graph.entity(ids[j])).map(function(e) {
|
||||
return e.loc;
|
||||
});
|
||||
var intersections = geoPathIntersections(path1, path2);
|
||||
|
||||
// Check if intersections are just nodes lying on top of each other/the line,
|
||||
// as opposed to crossing it
|
||||
if (_intersection(
|
||||
joined[0].nodes.map(function(n) { return n.loc.toString(); }),
|
||||
intersections.map(function(n) { return n.toString(); })
|
||||
).length !== intersections.length) return 'paths_intersect';
|
||||
}
|
||||
}
|
||||
|
||||
var nodeIds = joined[0].nodes.map(function(n) { return n.id; }).slice(1, -1);
|
||||
var relation;
|
||||
var tags = {};
|
||||
|
||||
@@ -3,9 +3,9 @@ describe('iD.actionJoin', function () {
|
||||
it('returns falsy for ways that share an end/start node', function () {
|
||||
// a --> b ==> c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']})
|
||||
]);
|
||||
@@ -16,9 +16,9 @@ describe('iD.actionJoin', function () {
|
||||
it('returns falsy for ways that share a start/end node', function () {
|
||||
// a <-- b <== c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['b', 'a']}),
|
||||
iD.osmWay({id: '=', nodes: ['c', 'b']})
|
||||
]);
|
||||
@@ -29,9 +29,9 @@ describe('iD.actionJoin', function () {
|
||||
it('returns falsy for ways that share a start/start node', function () {
|
||||
// a <-- b ==> c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['b', 'a']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']})
|
||||
]);
|
||||
@@ -42,9 +42,9 @@ describe('iD.actionJoin', function () {
|
||||
it('returns falsy for ways that share an end/end node', function () {
|
||||
// a --> b <== c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['c', 'b']})
|
||||
]);
|
||||
@@ -55,10 +55,10 @@ describe('iD.actionJoin', function () {
|
||||
it('returns falsy for more than two ways when connected, regardless of order', function () {
|
||||
// a --> b ==> c ~~> d
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'd'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmNode({id: 'd', loc: [6,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']}),
|
||||
iD.osmWay({id: '~', nodes: ['c', 'd']})
|
||||
@@ -74,7 +74,7 @@ describe('iD.actionJoin', function () {
|
||||
|
||||
it('returns \'not_eligible\' for non-line geometries', function () {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'})
|
||||
iD.osmNode({id: 'a', loc: [0,0]})
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['a']).disabled(graph)).to.equal('not_eligible');
|
||||
@@ -85,10 +85,10 @@ describe('iD.actionJoin', function () {
|
||||
// |
|
||||
// d
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'd'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmNode({id: 'd', loc: [2,2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'd']})
|
||||
]);
|
||||
@@ -102,9 +102,9 @@ describe('iD.actionJoin', function () {
|
||||
// to: =
|
||||
// via: b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']}),
|
||||
iD.osmRelation({id: 'r', tags: {type: 'restriction'}, members: [
|
||||
@@ -125,10 +125,10 @@ describe('iD.actionJoin', function () {
|
||||
// to: |
|
||||
// via: b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'd'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmNode({id: 'd', loc: [2,2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']}),
|
||||
iD.osmWay({id: '|', nodes: ['b', 'd']}),
|
||||
@@ -142,6 +142,25 @@ describe('iD.actionJoin', function () {
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).to.equal('restriction');
|
||||
});
|
||||
|
||||
it('returns \'paths_intersect\' if resulting way intersects itself', function () {
|
||||
// d
|
||||
// |
|
||||
// a ---b
|
||||
// | /
|
||||
// | /
|
||||
// c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [0,10]}),
|
||||
iD.osmNode({id: 'c', loc: [5,5]}),
|
||||
iD.osmNode({id: 'd', loc: [-5,5]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b', 'c']}),
|
||||
iD.osmWay({id: '=', nodes: ['c', 'd']}),
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).to.equal('paths_intersect');
|
||||
});
|
||||
|
||||
it('returns falsy in situations where a turn restriction wouldn\'t be damaged (a)', function () {
|
||||
// a --> b ==> c
|
||||
// |
|
||||
@@ -150,10 +169,10 @@ describe('iD.actionJoin', function () {
|
||||
// to: |
|
||||
// via: a
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'd'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmNode({id: 'd', loc: [0,2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']}),
|
||||
iD.osmWay({id: '|', nodes: ['a', 'd']}),
|
||||
@@ -177,10 +196,11 @@ describe('iD.actionJoin', function () {
|
||||
// to: \
|
||||
// via: b
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'd'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmNode({id: 'd', loc: [2,-2]}),
|
||||
iD.osmNode({id: 'e', loc: [3,2]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']}),
|
||||
iD.osmWay({id: '|', nodes: ['d', 'b']}),
|
||||
@@ -197,9 +217,9 @@ describe('iD.actionJoin', function () {
|
||||
|
||||
it('returns \'conflicting_tags\' for two entities that have conflicting tags', function () {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b'], tags: {highway: 'primary'}}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c'], tags: {highway: 'secondary'}})
|
||||
]);
|
||||
@@ -209,9 +229,9 @@ describe('iD.actionJoin', function () {
|
||||
|
||||
it('takes tag reversals into account when calculating conflicts', function () {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b'], tags: {'oneway': 'yes'}}),
|
||||
iD.osmWay({id: '=', nodes: ['c', 'b'], tags: {'oneway': '-1'}})
|
||||
]);
|
||||
@@ -221,9 +241,9 @@ describe('iD.actionJoin', function () {
|
||||
|
||||
it('returns falsy for exceptions to tag conflicts: missing tag', function () {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b'], tags: {highway: 'primary'}}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c'], tags: {}})
|
||||
]);
|
||||
@@ -233,9 +253,9 @@ describe('iD.actionJoin', function () {
|
||||
|
||||
it('returns falsy for exceptions to tag conflicts: uninteresting tag', function () {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b'], tags: {'tiger:cfcc': 'A41'}}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c'], tags: {'tiger:cfcc': 'A42'}})
|
||||
]);
|
||||
@@ -248,9 +268,9 @@ describe('iD.actionJoin', function () {
|
||||
// Expected result:
|
||||
// a --> b --> c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']})
|
||||
]);
|
||||
@@ -265,9 +285,9 @@ describe('iD.actionJoin', function () {
|
||||
// Expected result:
|
||||
// a <-- b <-- c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['b', 'a']}),
|
||||
iD.osmWay({id: '=', nodes: ['c', 'b']})
|
||||
]);
|
||||
@@ -281,9 +301,9 @@ describe('iD.actionJoin', function () {
|
||||
// Expected result:
|
||||
// a --> b --> c
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['b', 'a'], tags: {'lanes:forward': 2}}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']})
|
||||
]);
|
||||
@@ -300,9 +320,9 @@ describe('iD.actionJoin', function () {
|
||||
// a --> b --> c
|
||||
// tags on === reversed
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['c', 'b'], tags: {'lanes:forward': 2}})
|
||||
]);
|
||||
@@ -319,11 +339,11 @@ describe('iD.actionJoin', function () {
|
||||
// a --> b --> c --> d --> e
|
||||
// tags on === reversed
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'd'}),
|
||||
iD.osmNode({id: 'e'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmNode({id: 'd', loc: [6,0]}),
|
||||
iD.osmNode({id: 'e', loc: [8,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['c', 'b'], tags: {'lanes:forward': 2}}),
|
||||
iD.osmWay({id: '+', nodes: ['d', 'c']}),
|
||||
@@ -345,10 +365,10 @@ describe('iD.actionJoin', function () {
|
||||
// Expected result:
|
||||
// a ==> b ==> c ==> d
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'd'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmNode({id: 'd', loc: [6,0]}),
|
||||
iD.osmWay({id: 'w-1', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: 'w1', nodes: ['b', 'c']}),
|
||||
iD.osmWay({id: 'w-2', nodes: ['c', 'd']})
|
||||
@@ -363,10 +383,11 @@ describe('iD.actionJoin', function () {
|
||||
|
||||
it('merges tags', function () {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'd'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmNode({id: 'd', loc: [6,0]}),
|
||||
iD.osmNode({id: 'e', loc: [8,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b'], tags: {a: 'a', b: '-', c: 'c'}}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c'], tags: {a: 'a', b: '=', d: 'd'}}),
|
||||
iD.osmWay({id: '+', nodes: ['c', 'd'], tags: {a: 'a', b: '=', e: 'e'}})
|
||||
@@ -379,9 +400,9 @@ describe('iD.actionJoin', function () {
|
||||
|
||||
it('merges relations', function () {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a'}),
|
||||
iD.osmNode({id: 'b'}),
|
||||
iD.osmNode({id: 'c'}),
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
iD.osmNode({id: 'c', loc: [4,0]}),
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']}),
|
||||
iD.osmRelation({id: 'r1', members: [
|
||||
|
||||
Reference in New Issue
Block a user