mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-21 07:46:58 +02:00
Merge pull request #8675 from openstreetmap/jfirebaugh/join-conflicting-relations
Disable merge operation when it would damage relations
This commit is contained in:
+2
-1
@@ -312,10 +312,11 @@ en:
|
||||
other: "Merged {n} features."
|
||||
not_eligible: These features can't be merged.
|
||||
not_adjacent: These features can't be merged because their endpoints aren't connected.
|
||||
restriction: "These features can't be merged because it would damage a \"{relation}\" relation."
|
||||
damage_relation: "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_relations: These features can't be merged because they belong to conflicting relations.
|
||||
paths_intersect: These features can't be merged because the resulting path would intersect itself.
|
||||
too_many_vertices: These features can't be merged because the resulting path would have too many points.
|
||||
move:
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+22
-4
@@ -3,7 +3,7 @@ import { actionDeleteWay } from './delete_way';
|
||||
import { osmIsInterestingTag } from '../osm/tags';
|
||||
import { osmJoinWays } from '../osm/multipolygon';
|
||||
import { geoPathIntersections } from '../geo';
|
||||
import { utilArrayGroupBy, utilArrayIntersection } from '../util';
|
||||
import { utilArrayGroupBy, utilArrayIdentical, utilArrayIntersection } from '../util';
|
||||
|
||||
|
||||
// Join ways at the end node they share.
|
||||
@@ -129,9 +129,27 @@ export function actionJoin(ids) {
|
||||
return 'not_adjacent';
|
||||
}
|
||||
|
||||
var i;
|
||||
|
||||
// All joined ways must belong to the same set of (non-restriction) relations.
|
||||
// Restriction relations have different logic, below, which allows some cases
|
||||
// this prohibits, and prohibits some cases this allows.
|
||||
var sortedParentRelations = function (id) {
|
||||
return graph.parentRelations(graph.entity(id))
|
||||
.filter((rel) => !rel.isRestriction() && !rel.isConnectivity())
|
||||
.sort((a, b) => a.id - b.id);
|
||||
};
|
||||
var relsA = sortedParentRelations(ids[0]);
|
||||
for (i = 1; i < ids.length; i++) {
|
||||
var relsB = sortedParentRelations(ids[i]);
|
||||
if (!utilArrayIdentical(relsA, relsB)) {
|
||||
return 'conflicting_relations';
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through all combinations of path-pairs
|
||||
// to check potential intersections between all pairs
|
||||
for (var i = 0; i < ids.length - 1; i++) {
|
||||
for (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; });
|
||||
@@ -159,7 +177,7 @@ export function actionJoin(ids) {
|
||||
joined[0].forEach(function(way) {
|
||||
var parents = graph.parentRelations(way);
|
||||
parents.forEach(function(parent) {
|
||||
if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; })) {
|
||||
if ((parent.isRestriction() || parent.isConnectivity()) && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; })) {
|
||||
relation = parent;
|
||||
}
|
||||
});
|
||||
@@ -174,7 +192,7 @@ export function actionJoin(ids) {
|
||||
});
|
||||
|
||||
if (relation) {
|
||||
return 'restriction';
|
||||
return relation.isRestriction() ? 'restriction' : 'connectivity';
|
||||
}
|
||||
|
||||
if (conflicting) {
|
||||
|
||||
@@ -74,9 +74,12 @@ export function operationMerge(context, selectedIDs) {
|
||||
operation.tooltip = function() {
|
||||
var disabled = operation.disabled();
|
||||
if (disabled) {
|
||||
if (disabled === 'restriction') {
|
||||
return t('operations.merge.restriction',
|
||||
{ relation: presetManager.item('type/restriction').name() });
|
||||
if (disabled === 'conflicting_relations') {
|
||||
return t('operations.merge.conflicting_relations');
|
||||
}
|
||||
if (disabled === 'restriction' || disabled === 'connectivity') {
|
||||
return t('operations.merge.damage_relation',
|
||||
{ relation: presetManager.item('type/' + disabled).name() });
|
||||
}
|
||||
return t('operations.merge.' + disabled);
|
||||
}
|
||||
|
||||
@@ -289,6 +289,9 @@ Object.assign(osmRelation.prototype, {
|
||||
return true;
|
||||
},
|
||||
|
||||
isConnectivity: function() {
|
||||
return !!(this.tags.type && this.tags.type.match(/^connectivity:?/));
|
||||
},
|
||||
|
||||
// Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
|
||||
// where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
|
||||
|
||||
+116
-73
@@ -96,34 +96,132 @@ describe('iD.actionJoin', function () {
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).to.equal('not_adjacent');
|
||||
});
|
||||
|
||||
it('returns \'restriction\' in situations where a turn restriction would be damaged (a)', function () {
|
||||
['restriction', 'connectivity'].forEach(function (type) {
|
||||
it(`returns '${type}' in situations where a ${type} relation would be damaged (a)`, function () {
|
||||
// a --> b ==> c
|
||||
// from: -
|
||||
// to: =
|
||||
// via: b
|
||||
var graph = iD.coreGraph([
|
||||
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}, members: [
|
||||
{type: 'way', id: '-', role: 'from'},
|
||||
{type: 'way', id: '=', role: 'to'},
|
||||
{type: 'node', id: 'b', role: 'via'}
|
||||
]})
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).to.equal(type);
|
||||
});
|
||||
|
||||
it(`returns '${type}' in situations where a ${type} relation would be damaged (b)`, function () {
|
||||
// a --> b ==> c
|
||||
// |
|
||||
// d
|
||||
// from: -
|
||||
// to: |
|
||||
// via: b
|
||||
var graph = iD.coreGraph([
|
||||
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']}),
|
||||
iD.osmRelation({id: 'r', tags: {type}, members: [
|
||||
{type: 'way', id: '-', role: 'from'},
|
||||
{type: 'way', id: '|', role: 'to'},
|
||||
{type: 'node', id: 'b', role: 'via'}
|
||||
]})
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).to.equal(type);
|
||||
});
|
||||
|
||||
it(`returns falsy in situations where a ${type} relation wouldn't be damaged (a)`, function () {
|
||||
// a --> b ==> c
|
||||
// |
|
||||
// d
|
||||
// from: -
|
||||
// to: |
|
||||
// via: a
|
||||
var graph = iD.coreGraph([
|
||||
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']}),
|
||||
iD.osmRelation({id: 'r', tags: {type}, members: [
|
||||
{type: 'way', id: '-', role: 'from'},
|
||||
{type: 'way', id: '|', role: 'to'},
|
||||
{type: 'node', id: 'a', role: 'via'}
|
||||
]})
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).not.to.be.ok;
|
||||
});
|
||||
|
||||
it(`returns falsy in situations where a ${type} restriction wouldn't be damaged (b)`, function () {
|
||||
// d
|
||||
// |
|
||||
// a --> b ==> c
|
||||
// \
|
||||
// e
|
||||
// from: |
|
||||
// to: \
|
||||
// via: b
|
||||
var graph = iD.coreGraph([
|
||||
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']}),
|
||||
iD.osmWay({id: '\\', nodes: ['b', 'e']}),
|
||||
iD.osmRelation({id: 'r', tags: {type}, members: [
|
||||
{type: 'way', id: '|', role: 'from'},
|
||||
{type: 'way', id: '\\', role: 'to'},
|
||||
{type: 'node', id: 'b', role: 'via'}
|
||||
]})
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).not.to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns \'conflicting_relations\' when a relation would be extended', function () {
|
||||
// a --> b ==> c
|
||||
// from: -
|
||||
// to: =
|
||||
// via: b
|
||||
// members: -
|
||||
// not member: =
|
||||
var graph = iD.coreGraph([
|
||||
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: [
|
||||
{type: 'way', id: '-', role: 'from'},
|
||||
{type: 'way', id: '=', role: 'to'},
|
||||
{type: 'node', id: 'b', role: 'via'}
|
||||
]})
|
||||
iD.osmRelation({id: 'r', tags: {}, members: [
|
||||
{type: 'way', id: '-'},
|
||||
]})
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).to.equal('restriction');
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).to.equal('conflicting_relations');
|
||||
});
|
||||
|
||||
it('returns \'restriction\' in situations where a turn restriction would be damaged (b)', function () {
|
||||
it('returns \'conflicting_relations\' when a relation would be forked', function () {
|
||||
// a --> b ==> c
|
||||
// |
|
||||
// d
|
||||
// from: -
|
||||
// to: |
|
||||
// via: b
|
||||
// members: -, =
|
||||
// not member: |
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
iD.osmNode({id: 'b', loc: [2,0]}),
|
||||
@@ -132,14 +230,13 @@ describe('iD.actionJoin', function () {
|
||||
iD.osmWay({id: '-', nodes: ['a', 'b']}),
|
||||
iD.osmWay({id: '=', nodes: ['b', 'c']}),
|
||||
iD.osmWay({id: '|', nodes: ['b', 'd']}),
|
||||
iD.osmRelation({id: 'r', tags: {type: 'restriction'}, members: [
|
||||
{type: 'way', id: '-', role: 'from'},
|
||||
{type: 'way', id: '|', role: 'to'},
|
||||
{type: 'node', id: 'b', role: 'via'}
|
||||
iD.osmRelation({id: 'r', tags: {}, members: [
|
||||
{type: 'way', id: '-'},
|
||||
{type: 'way', id: '='},
|
||||
]})
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).to.equal('restriction');
|
||||
expect(iD.actionJoin(['-', '|']).disabled(graph)).to.equal('conflicting_relations');
|
||||
});
|
||||
|
||||
it('returns \'paths_intersect\' if resulting way intersects itself', function () {
|
||||
@@ -161,60 +258,6 @@ describe('iD.actionJoin', function () {
|
||||
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
|
||||
// |
|
||||
// d
|
||||
// from: -
|
||||
// to: |
|
||||
// via: a
|
||||
var graph = iD.coreGraph([
|
||||
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']}),
|
||||
iD.osmRelation({id: 'r', tags: {type: 'restriction'}, members: [
|
||||
{type: 'way', id: '-', role: 'from'},
|
||||
{type: 'way', id: '|', role: 'to'},
|
||||
{type: 'node', id: 'a', role: 'via'}
|
||||
]})
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).not.to.be.ok;
|
||||
});
|
||||
|
||||
it('returns falsy in situations where a turn restriction wouldn\'t be damaged (b)', function () {
|
||||
// d
|
||||
// |
|
||||
// a --> b ==> c
|
||||
// \
|
||||
// e
|
||||
// from: |
|
||||
// to: \
|
||||
// via: b
|
||||
var graph = iD.coreGraph([
|
||||
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']}),
|
||||
iD.osmWay({id: '\\', nodes: ['b', 'e']}),
|
||||
iD.osmRelation({id: 'r', tags: {type: 'restriction'}, members: [
|
||||
{type: 'way', id: '|', role: 'from'},
|
||||
{type: 'way', id: '\\', role: 'to'},
|
||||
{type: 'node', id: 'b', role: 'via'}
|
||||
]})
|
||||
]);
|
||||
|
||||
expect(iD.actionJoin(['-', '=']).disabled(graph)).not.to.be.ok;
|
||||
});
|
||||
|
||||
it('returns \'conflicting_tags\' for two entities that have conflicting tags', function () {
|
||||
var graph = iD.coreGraph([
|
||||
iD.osmNode({id: 'a', loc: [0,0]}),
|
||||
|
||||
Reference in New Issue
Block a user