mirror of
https://github.com/FoggedLens/iD.git
synced 2026-03-08 04:11:23 +00:00
more clever splitting of closed ways
if a way is closed, iD needs to choose a second node to split the way at. This algorithm looks for a node that is both far away from the initial node in terms of way segment length and nearby in terms of beeline-distance. This assures that areas get split on the most "natural" points (independent of the number of nodes). For example: bone-shaped areas get split across their waist- line, circles across the diameter.
This commit is contained in:
@@ -16,6 +16,16 @@ iD.actions.Split = function(nodeId, newWayIds) {
|
||||
var wayIds;
|
||||
|
||||
function split(graph, wayA, newWayId) {
|
||||
// if the way is closed, we need to search for a partner node
|
||||
// to split the way at.
|
||||
//
|
||||
// The following looks for a node that is both far away from
|
||||
// the initial node in terms of way segment length and nearby
|
||||
// in terms of beeline-distance. This assures that areas get
|
||||
// split on the most "natural" points (independent of the number
|
||||
// of nodes).
|
||||
// For example: bone-shaped areas get split across their waist
|
||||
// line, circles across the diameter.
|
||||
var wayB = iD.Way({id: newWayId, tags: wayA.tags}),
|
||||
nodesA,
|
||||
nodesB,
|
||||
@@ -24,10 +34,41 @@ iD.actions.Split = function(nodeId, newWayIds) {
|
||||
if (wayA.isClosed()) {
|
||||
var nodes = wayA.nodes.slice(0, -1),
|
||||
idxA = _.indexOf(nodes, nodeId),
|
||||
idxB = idxA + Math.floor(nodes.length / 2);
|
||||
idxB,
|
||||
lengths = Array(nodes.length),
|
||||
cum_length,
|
||||
i,
|
||||
best = 0.0;
|
||||
|
||||
if (idxB >= nodes.length) {
|
||||
idxB %= nodes.length;
|
||||
function _wrap(index) {
|
||||
return iD.util.wrap(index,nodes.length);
|
||||
}
|
||||
function _dist(nA, nB) {
|
||||
return iD.geo.dist(graph.entity(nA).loc, graph.entity(nB).loc);
|
||||
}
|
||||
|
||||
// calculate lengths
|
||||
cum_length = 0.0;
|
||||
for (i = _wrap(idxA+1); i != idxA; i = _wrap(i+1)) {
|
||||
cum_length += _dist(nodes[i], nodes[_wrap(i-1)]);
|
||||
lengths[i] = cum_length;
|
||||
}
|
||||
cum_length = 0.0;
|
||||
for (i = _wrap(idxA-1); i != idxA; i = _wrap(i-1)) {
|
||||
cum_length += _dist(nodes[i], nodes[_wrap(i+1)]);
|
||||
if (cum_length < lengths[i])
|
||||
lengths[i] = cum_length;
|
||||
}
|
||||
// determine best opposite node to split
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
var cost = lengths[i] / _dist(nodes[idxA], nodes[i]);
|
||||
if (cost > best) {
|
||||
idxB = i;
|
||||
best = cost;
|
||||
}
|
||||
}
|
||||
|
||||
if (idxB < idxA) {
|
||||
nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
|
||||
nodesB = nodes.slice(idxB, idxA + 1);
|
||||
} else {
|
||||
|
||||
@@ -146,3 +146,10 @@ iD.util.asyncMap = function(inputs, func, callback) {
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// wraps an index to an interval [0..length-1]
|
||||
iD.util.wrap = function(index, length) {
|
||||
if (index < 0)
|
||||
index += Math.ceil(-index/length)*length;
|
||||
return index % length;
|
||||
};
|
||||
|
||||
@@ -259,10 +259,10 @@ describe("iD.actions.Split", function () {
|
||||
// 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'}),
|
||||
'a': iD.Node({id: 'a', loc: [0,1]}),
|
||||
'b': iD.Node({id: 'b', loc: [1,1]}),
|
||||
'c': iD.Node({id: 'c', loc: [1,0]}),
|
||||
'd': iD.Node({id: 'd', loc: [0,0]}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
});
|
||||
|
||||
@@ -285,10 +285,10 @@ describe("iD.actions.Split", function () {
|
||||
|
||||
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'}),
|
||||
'a': iD.Node({id: 'a', loc: [0,1]}),
|
||||
'b': iD.Node({id: 'b', loc: [1,1]}),
|
||||
'c': iD.Node({id: 'c', loc: [1,0]}),
|
||||
'd': iD.Node({id: 'd', loc: [0,0]}),
|
||||
'-': iD.Way({id: '-', tags: {building: 'yes'}, nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user