mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-25 01:24:05 +02:00
Multiselect vertex/way to control splitting
This commit is contained in:
+8
-2
@@ -109,9 +109,15 @@ en:
|
||||
annotation: Reversed a line.
|
||||
split:
|
||||
title: Split
|
||||
description: Split this into two ways at this point.
|
||||
description:
|
||||
line: Split this line into two at this point.
|
||||
area: Split the boundary of this area into two.
|
||||
multiple: Split the lines/area boundaries at this point into two.
|
||||
key: X
|
||||
annotation: Split a way.
|
||||
annotation:
|
||||
line: Split a line.
|
||||
area: Split an area boundary.
|
||||
multiple: "Split {n} lines/area boundaries."
|
||||
not_eligible: Lines can't be split at their beginning or end.
|
||||
multiple_ways: There are too many lines here to split.
|
||||
nothing_to_undo: Nothing to undo.
|
||||
|
||||
+10
-2
@@ -139,9 +139,17 @@ locale.en = {
|
||||
},
|
||||
"split": {
|
||||
"title": "Split",
|
||||
"description": "Split this into two ways at this point.",
|
||||
"description": {
|
||||
"line": "Split this line into two at this point.",
|
||||
"area": "Split the boundary of this area into two.",
|
||||
"multiple": "Split the lines/area boundaries at this point into two."
|
||||
},
|
||||
"key": "X",
|
||||
"annotation": "Split a way.",
|
||||
"annotation": {
|
||||
"line": "Split a line.",
|
||||
"area": "Split an area boundary.",
|
||||
"multiple": "Split {n} lines/area boundaries."
|
||||
},
|
||||
"not_eligible": "Lines can't be split at their beginning or end.",
|
||||
"multiple_ways": "There are too many lines here to split."
|
||||
}
|
||||
|
||||
+29
-23
@@ -1,5 +1,8 @@
|
||||
// Split a way at the given node.
|
||||
//
|
||||
// Optionally, split only the given ways, if multiple ways share
|
||||
// the given node.
|
||||
//
|
||||
// This is the inverse of `iD.actions.Join`.
|
||||
//
|
||||
// For testing convenience, accepts an ID to assign to the new way.
|
||||
@@ -9,26 +12,7 @@
|
||||
// Reference:
|
||||
// https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
|
||||
//
|
||||
iD.actions.Split = function(nodeId, newWayIds) {
|
||||
function candidateWays(graph) {
|
||||
var node = graph.entity(nodeId),
|
||||
parents = graph.parentWays(node);
|
||||
|
||||
return parents.filter(function(parent) {
|
||||
if (parent.isClosed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var i = 1; i < parent.nodes.length - 1; i++) {
|
||||
if (parent.nodes[i] === nodeId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
iD.actions.Split = function(nodeId, wayIds, newWayIds) {
|
||||
function split(graph, wayA, newWayId) {
|
||||
var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
|
||||
nodesA,
|
||||
@@ -102,16 +86,38 @@ iD.actions.Split = function(nodeId, newWayIds) {
|
||||
}
|
||||
|
||||
var action = function(graph) {
|
||||
var candidates = candidateWays(graph);
|
||||
var candidates = action.ways(graph);
|
||||
for (var i = 0; i < candidates.length; i++) {
|
||||
graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
|
||||
}
|
||||
return graph;
|
||||
};
|
||||
|
||||
action.ways = function(graph) {
|
||||
var node = graph.entity(nodeId),
|
||||
parents = graph.parentWays(node);
|
||||
|
||||
return parents.filter(function(parent) {
|
||||
if (wayIds && wayIds.length && wayIds.indexOf(parent.id) === -1)
|
||||
return false;
|
||||
|
||||
if (parent.isClosed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var i = 1; i < parent.nodes.length - 1; i++) {
|
||||
if (parent.nodes[i] === nodeId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
action.disabled = function(graph) {
|
||||
var candidates = candidateWays(graph);
|
||||
if (candidates.length === 0)
|
||||
var candidates = action.ways(graph);
|
||||
if (candidates.length === 0 || (wayIds && wayIds.length && wayIds.length !== candidates.length))
|
||||
return 'not_eligible';
|
||||
};
|
||||
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
iD.operations.Split = function(selection, context) {
|
||||
var entityId = selection[0],
|
||||
action = iD.actions.Split(entityId);
|
||||
var vertices = _.filter(selection, function vertex(entityId) {
|
||||
return context.geometry(entityId) === 'vertex'
|
||||
});
|
||||
|
||||
var entityId = vertices[0],
|
||||
action = iD.actions.Split(entityId, _.without(selection, entityId));
|
||||
|
||||
var operation = function() {
|
||||
var annotation = t('operations.split.annotation'),
|
||||
difference = context.perform(action, annotation);
|
||||
var annotation;
|
||||
|
||||
var ways = action.ways(context.graph());
|
||||
if (ways.length === 1) {
|
||||
annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
|
||||
} else {
|
||||
annotation = t('operations.split.annotation.multiple', {n: ways.length});
|
||||
}
|
||||
|
||||
var difference = context.perform(action, annotation);
|
||||
context.enter(iD.modes.Select(context, difference.extantIDs()));
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
return selection.length === 1 &&
|
||||
context.geometry(entityId) === 'vertex';
|
||||
return vertices.length === 1;
|
||||
};
|
||||
|
||||
operation.disabled = function() {
|
||||
@@ -19,9 +30,16 @@ iD.operations.Split = function(selection, context) {
|
||||
|
||||
operation.tooltip = function() {
|
||||
var disable = operation.disabled();
|
||||
return disable ?
|
||||
t('operations.split.' + disable) :
|
||||
t('operations.split.description');
|
||||
if (disable) {
|
||||
return t('operations.split.' + disable);
|
||||
}
|
||||
|
||||
var ways = action.ways(context.graph());
|
||||
if (ways.length === 1) {
|
||||
return t('operations.split.description.' + context.geometry(ways[0].id));
|
||||
} else {
|
||||
return t('operations.split.description.multiple');
|
||||
}
|
||||
};
|
||||
|
||||
operation.id = "split";
|
||||
|
||||
+73
-17
@@ -25,6 +25,20 @@ describe("iD.actions.Split", function () {
|
||||
expect(iD.actions.Split('*').disabled(graph)).not.to.be.ok;
|
||||
});
|
||||
|
||||
it("returns falsy for an intersection of two ways with parent way specified", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a'}),
|
||||
'b': iD.Node({id: 'b'}),
|
||||
'c': iD.Node({id: 'c'}),
|
||||
'd': iD.Node({id: 'c'}),
|
||||
'*': iD.Node({id: '*'}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', '*', 'b']}),
|
||||
'|': iD.Way({id: '|', nodes: ['c', '*', 'd']})
|
||||
});
|
||||
|
||||
expect(iD.actions.Split('*', ['-']).disabled(graph)).not.to.be.ok;
|
||||
});
|
||||
|
||||
it("returns falsy for a self-intersection", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a'}),
|
||||
@@ -56,6 +70,20 @@ describe("iD.actions.Split", function () {
|
||||
|
||||
expect(iD.actions.Split('b').disabled(graph)).to.equal('not_eligible');
|
||||
});
|
||||
|
||||
it("returns 'not_eligible' for an intersection of two ways with non-parent way specified", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a'}),
|
||||
'b': iD.Node({id: 'b'}),
|
||||
'c': iD.Node({id: 'c'}),
|
||||
'd': iD.Node({id: 'c'}),
|
||||
'*': iD.Node({id: '*'}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', '*', 'b']}),
|
||||
'|': iD.Way({id: '|', nodes: ['c', '*', 'd']})
|
||||
});
|
||||
|
||||
expect(iD.actions.Split('*', ['-', '=']).disabled(graph)).to.equal('not_eligible');
|
||||
});
|
||||
});
|
||||
|
||||
it("creates a new way with the appropriate nodes", function () {
|
||||
@@ -74,7 +102,7 @@ describe("iD.actions.Split", function () {
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c']})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b']);
|
||||
expect(graph.entity('=').nodes).to.eql(['b', 'c']);
|
||||
@@ -89,7 +117,7 @@ describe("iD.actions.Split", function () {
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c'], tags: tags})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
// Immutable tags => should be shared by identity.
|
||||
expect(graph.entity('-').tags).to.equal(tags);
|
||||
@@ -118,7 +146,7 @@ describe("iD.actions.Split", function () {
|
||||
'|': iD.Way({id: '|', nodes: ['d', 'b']})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b']);
|
||||
expect(graph.entity('=').nodes).to.eql(['b', 'c']);
|
||||
@@ -152,7 +180,7 @@ describe("iD.actions.Split", function () {
|
||||
'|': iD.Way({id: '|', nodes: ['c', '*', 'd']})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('*', ['=', '¦'])(graph);
|
||||
graph = iD.actions.Split('*', undefined, ['=', '¦'])(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.eql(['a', '*']);
|
||||
expect(graph.entity('=').nodes).to.eql(['*', 'b']);
|
||||
@@ -160,6 +188,34 @@ describe("iD.actions.Split", function () {
|
||||
expect(graph.entity('¦').nodes).to.eql(['*', 'd']);
|
||||
});
|
||||
|
||||
it("splits the specified ways at an intersection", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a'}),
|
||||
'b': iD.Node({id: 'b'}),
|
||||
'c': iD.Node({id: 'c'}),
|
||||
'd': iD.Node({id: 'c'}),
|
||||
'*': iD.Node({id: '*'}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', '*', 'b']}),
|
||||
'|': iD.Way({id: '|', nodes: ['c', '*', 'd']})
|
||||
});
|
||||
|
||||
var g1 = iD.actions.Split('*', ['-'], ['='])(graph);
|
||||
expect(g1.entity('-').nodes).to.eql(['a', '*']);
|
||||
expect(g1.entity('=').nodes).to.eql(['*', 'b']);
|
||||
expect(g1.entity('|').nodes).to.eql(['c', '*', 'd']);
|
||||
|
||||
var g2 = iD.actions.Split('*', ['|'], ['¦'])(graph);
|
||||
expect(g2.entity('-').nodes).to.eql(['a', '*', 'b']);
|
||||
expect(g2.entity('|').nodes).to.eql(['c', '*']);
|
||||
expect(g2.entity('¦').nodes).to.eql(['*', 'd']);
|
||||
|
||||
var g3 = iD.actions.Split('*', ['-', '|'], ['=', '¦'])(graph);
|
||||
expect(g3.entity('-').nodes).to.eql(['a', '*']);
|
||||
expect(g3.entity('=').nodes).to.eql(['*', 'b']);
|
||||
expect(g3.entity('|').nodes).to.eql(['c', '*']);
|
||||
expect(g3.entity('¦').nodes).to.eql(['*', 'd']);
|
||||
});
|
||||
|
||||
it("splits self-intersecting ways", function () {
|
||||
// Situation:
|
||||
// b
|
||||
@@ -183,7 +239,7 @@ describe("iD.actions.Split", function () {
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'a', 'd']})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('a', ['='])(graph);
|
||||
graph = iD.actions.Split('a', undefined, ['='])(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c', 'a']);
|
||||
expect(graph.entity('=').nodes).to.eql(['a', 'd']);
|
||||
@@ -210,19 +266,19 @@ describe("iD.actions.Split", function () {
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
});
|
||||
|
||||
var g1 = iD.actions.Split('a', ['='])(graph);
|
||||
var g1 = iD.actions.Split('a', undefined, ['='])(graph);
|
||||
expect(g1.entity('-').nodes).to.eql(['a', 'b', 'c']);
|
||||
expect(g1.entity('=').nodes).to.eql(['c', 'd', 'a']);
|
||||
|
||||
var g2 = iD.actions.Split('b', ['='])(graph);
|
||||
var g2 = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
expect(g2.entity('-').nodes).to.eql(['b', 'c', 'd']);
|
||||
expect(g2.entity('=').nodes).to.eql(['d', 'a', 'b']);
|
||||
|
||||
var g3 = iD.actions.Split('c', ['='])(graph);
|
||||
var g3 = iD.actions.Split('c', undefined, ['='])(graph);
|
||||
expect(g3.entity('-').nodes).to.eql(['c', 'd', 'a']);
|
||||
expect(g3.entity('=').nodes).to.eql(['a', 'b', 'c']);
|
||||
|
||||
var g4 = iD.actions.Split('d', ['='])(graph);
|
||||
var g4 = iD.actions.Split('d', undefined, ['='])(graph);
|
||||
expect(g4.entity('-').nodes).to.eql(['d', 'a', 'b']);
|
||||
expect(g4.entity('=').nodes).to.eql(['b', 'c', 'd']);
|
||||
});
|
||||
@@ -236,7 +292,7 @@ describe("iD.actions.Split", function () {
|
||||
'-': iD.Way({id: '-', tags: {building: 'yes'}, nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('a', ['='])(graph);
|
||||
graph = iD.actions.Split('a', undefined, ['='])(graph);
|
||||
expect(graph.entity('-').tags).to.eql({});
|
||||
expect(graph.entity('=').tags).to.eql({});
|
||||
expect(graph.parentRelations(graph.entity('-'))).to.have.length(1);
|
||||
@@ -268,7 +324,7 @@ describe("iD.actions.Split", function () {
|
||||
'r': iD.Relation({id: 'r', members: [{id: '-', type: 'way', role: 'forward'}]})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
expect(graph.entity('r').members).to.eql([
|
||||
{id: '-', type: 'way', role: 'forward'},
|
||||
@@ -297,7 +353,7 @@ describe("iD.actions.Split", function () {
|
||||
'r': iD.Relation({id: 'r', members: [{id: '-', type: 'way'}, {id: '~', type: 'way'}]})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
expect(_.pluck(graph.entity('r').members, 'id')).to.eql(['-', '=', '~']);
|
||||
});
|
||||
@@ -323,7 +379,7 @@ describe("iD.actions.Split", function () {
|
||||
'r': iD.Relation({id: 'r', members: [{id: '~', type: 'way'}, {id: '-', type: 'way'}]})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
expect(_.pluck(graph.entity('r').members, 'id')).to.eql(['~', '=', '-']);
|
||||
});
|
||||
@@ -337,7 +393,7 @@ describe("iD.actions.Split", function () {
|
||||
'r': iD.Relation({id: 'r', members: [{id: '~', type: 'way'}, {id: '-', type: 'way'}]})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
expect(_.pluck(graph.entity('r').members, 'id')).to.eql(['~', '-', '=']);
|
||||
});
|
||||
@@ -367,7 +423,7 @@ describe("iD.actions.Split", function () {
|
||||
{id: 'c', role: 'via'}]})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
expect(graph.entity('r').members).to.eql([
|
||||
{id: '=', role: 'from'},
|
||||
@@ -399,7 +455,7 @@ describe("iD.actions.Split", function () {
|
||||
{id: 'c', role: 'via'}]})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
expect(graph.entity('r').members).to.eql([
|
||||
{id: '~', role: 'from'},
|
||||
@@ -431,7 +487,7 @@ describe("iD.actions.Split", function () {
|
||||
{id: 'c', role: 'via'}]})
|
||||
});
|
||||
|
||||
graph = iD.actions.Split('b', ['='])(graph);
|
||||
graph = iD.actions.Split('b', undefined, ['='])(graph);
|
||||
|
||||
expect(graph.entity('r').members).to.eql([
|
||||
{id: '-', role: 'from'},
|
||||
|
||||
Reference in New Issue
Block a user