Improvements to Split

* Split a closed way at selected and antipode point (fixes #651)
* Split an area into a multipolygon (fixes #572)
This commit is contained in:
John Firebaugh
2013-02-06 15:58:13 -08:00
parent 246481ad92
commit 0af51a0ef6
2 changed files with 110 additions and 19 deletions
+50 -19
View File
@@ -15,37 +15,55 @@ iD.actions.Split = function(nodeId, newWayId) {
parents = graph.parentWays(node);
return parents.filter(function (parent) {
return parent.first() !== nodeId &&
parent.last() !== nodeId;
return parent.isClosed() ||
(parent.first() !== nodeId &&
parent.last() !== nodeId);
});
}
var action = function(graph) {
if (!action.enabled(graph))
return graph;
var wayA = candidateWays(graph)[0],
wayB = iD.Way({id: newWayId, tags: wayA.tags}),
nodesA,
nodesB,
isArea = wayA.isArea();
var way = candidateWays(graph)[0],
idx = _.indexOf(way.nodes, nodeId);
if (wayA.isClosed()) {
var nodes = wayA.nodes.slice(0, -1),
idxA = _.indexOf(nodes, nodeId),
idxB = idxA + Math.floor(nodes.length / 2);
// Create a 'b' way that contains all of the tags in the second
// half of this way
var newWay = iD.Way({id: newWayId, tags: way.tags, nodes: way.nodes.slice(idx)});
graph = graph.replace(newWay);
if (idxB >= nodes.length) {
idxB %= nodes.length;
nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
nodesB = nodes.slice(idxB, idxA + 1);
} else {
nodesA = nodes.slice(idxA, idxB + 1);
nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
}
} else {
var idx = _.indexOf(wayA.nodes, nodeId);
nodesA = wayA.nodes.slice(0, idx + 1);
nodesB = wayA.nodes.slice(idx);
}
// Reduce the original way to only contain the first set of nodes
graph = graph.replace(way.update({nodes: way.nodes.slice(0, idx + 1)}));
wayA = wayA.update({nodes: nodesA});
wayB = wayB.update({nodes: nodesB});
graph.parentRelations(way).forEach(function(relation) {
graph = graph.replace(wayA);
graph = graph.replace(wayB);
graph.parentRelations(wayA).forEach(function(relation) {
if (relation.isRestriction()) {
var via = relation.memberByRole('via');
if (via && newWay.contains(via.id)) {
relation = relation.updateMember({id: newWay.id}, relation.memberById(way.id).index);
if (via && wayB.contains(via.id)) {
relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
graph = graph.replace(relation);
}
} else {
var role = relation.memberById(way.id).role,
last = newWay.last(),
i = relation.memberById(way.id).index,
var role = relation.memberById(wayA.id).role,
last = wayB.last(),
i = relation.memberById(wayA.id).index,
j;
for (j = 0; j < relation.members.length; j++) {
@@ -55,11 +73,24 @@ iD.actions.Split = function(nodeId, newWayId) {
}
}
relation = relation.addMember({id: newWay.id, type: 'way', role: role}, i <= j ? i + 1 : i);
relation = relation.addMember({id: wayB.id, type: 'wayA', role: role}, i <= j ? i + 1 : i);
graph = graph.replace(relation);
}
});
if (isArea) {
var multipolygon = iD.Relation({
tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
members: [
{id: wayA.id, role: 'outer', type: 'way'},
{id: wayB.id, role: 'outer', type: 'way'}
]});
graph = graph.replace(multipolygon);
graph = graph.replace(wayA.update({tags: {}}));
graph = graph.replace(wayB.update({tags: {}}));
}
return graph;
};
+60
View File
@@ -99,6 +99,66 @@ describe("iD.actions.Split", function () {
expect(graph.entity('|').nodes).to.eql(['d', 'b']);
});
it("splits a closed way at the given point and its antipode", function () {
// Situation:
// a ---- b
// | |
// d ---- c
//
// Split at a.
//
// Expected result:
// a ---- b
// || |
// d ==== c
//
var graph = iD.Graph({
'a': iD.Node({id: 'a'}),
'b': iD.Node({id: 'b'}),
'c': iD.Node({id: 'c'}),
'd': iD.Node({id: 'd'}),
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
});
var g1 = iD.actions.Split('a', '=')(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);
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);
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);
expect(g4.entity('-').nodes).to.eql(['d', 'a', 'b']);
expect(g4.entity('=').nodes).to.eql(['b', 'c', 'd']);
});
it("splits an area by converting it to a multipolygon", function () {
var graph = iD.Graph({
'a': iD.Node({id: 'a'}),
'b': iD.Node({id: 'b'}),
'c': iD.Node({id: 'c'}),
'd': iD.Node({id: 'd'}),
'-': iD.Way({id: '-', tags: {building: 'yes'}, nodes: ['a', 'b', 'c', 'd', 'a']})
});
graph = iD.actions.Split('a', '=')(graph);
expect(graph.entity('-').tags).to.eql({});
expect(graph.entity('=').tags).to.eql({});
expect(graph.parentRelations(graph.entity('-'))).to.have.length(1);
var relation = graph.parentRelations(graph.entity('-'))[0];
expect(relation.tags).to.eql({type: 'multipolygon', building: 'yes'});
expect(relation.members).to.eql([
{id: '-', role: 'outer', type: 'way'},
{id: '=', role: 'outer', type: 'way'}
]);
});
it("adds the new way to parent relations (no connections)", function () {
// Situation:
// a ---- b ---- c