mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-20 15:34:49 +02:00
Fix circularize boundary cases (fixes #494)
There are still more boundary cases even farther out. But this is probably sufficient for the real world.
This commit is contained in:
@@ -1,52 +1,56 @@
|
||||
iD.actions.Circularize = function(wayId, projection) {
|
||||
iD.actions.Circularize = function(wayId, projection, count) {
|
||||
count = count || 12;
|
||||
|
||||
function closestIndex(nodes, loc) {
|
||||
var idx, min = Infinity, dist;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
dist = iD.geo.dist(nodes[i].loc, loc);
|
||||
if (dist < min) {
|
||||
min = dist;
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
var action = function(graph) {
|
||||
var way = graph.entity(wayId),
|
||||
nodes = _.uniq(graph.childNodes(way));
|
||||
|
||||
var points = nodes.map(function(n) {
|
||||
return projection(n.loc);
|
||||
}),
|
||||
nodes = _.uniq(graph.childNodes(way)),
|
||||
points = nodes.map(function(n) { return projection(n.loc); }),
|
||||
centroid = d3.geom.polygon(points).centroid(),
|
||||
radius = d3.median(points, function(p) {
|
||||
return iD.geo.dist(centroid, p);
|
||||
}),
|
||||
circular_nodes = [];
|
||||
ids = [];
|
||||
|
||||
for (var i = 0; i < 12; i++) {
|
||||
circular_nodes.push(iD.Node({ loc: projection.invert([
|
||||
centroid[0] + Math.cos((i / 12) * Math.PI * 2) * radius,
|
||||
centroid[1] + Math.sin((i / 12) * Math.PI * 2) * radius])
|
||||
}));
|
||||
}
|
||||
for (var i = 0; i < count; i++) {
|
||||
var node,
|
||||
loc = projection.invert([
|
||||
centroid[0] + Math.cos((i / 12) * Math.PI * 2) * radius,
|
||||
centroid[1] + Math.sin((i / 12) * Math.PI * 2) * radius]);
|
||||
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
if (graph.parentWays(nodes[i]).length > 1) {
|
||||
var closest, closest_dist = Infinity, dist;
|
||||
for (var j = 0; j < circular_nodes.length; j++) {
|
||||
dist = iD.geo.dist(circular_nodes[j].loc, nodes[i].loc);
|
||||
if (dist < closest_dist) {
|
||||
closest_dist = dist;
|
||||
closest = j;
|
||||
}
|
||||
}
|
||||
circular_nodes.splice(closest, 1, nodes[i]);
|
||||
if (nodes.length) {
|
||||
var idx = closestIndex(nodes, loc);
|
||||
node = nodes[idx];
|
||||
nodes.splice(idx, 1);
|
||||
} else {
|
||||
node = iD.Node();
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < circular_nodes.length; i++) {
|
||||
graph = graph.replace(circular_nodes[i]);
|
||||
ids.push(node.id);
|
||||
graph = graph.replace(node.move(loc));
|
||||
}
|
||||
|
||||
var ids = _.pluck(circular_nodes, 'id'),
|
||||
difference = _.difference(_.uniq(way.nodes), ids);
|
||||
|
||||
ids.push(ids[0]);
|
||||
|
||||
graph = graph.replace(way.update({nodes: ids}));
|
||||
|
||||
for (i = 0; i < difference.length; i++) {
|
||||
graph = iD.actions.DeleteNode(difference[i])(graph);
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
graph.parentWays(nodes[i]).forEach(function(parent) {
|
||||
graph = graph.replace(parent.replaceNode(nodes[i].id,
|
||||
ids[closestIndex(graph.childNodes(way), nodes[i].loc)]));
|
||||
});
|
||||
|
||||
graph = iD.actions.DeleteNode(nodes[i].id)(graph);
|
||||
}
|
||||
|
||||
return graph;
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
<script src="spec/actions/add_midpoint.js"></script>
|
||||
<script src="spec/actions/add_entity.js"></script>
|
||||
<script src="spec/actions/change_tags.js"></script>
|
||||
<script src='spec/actions/circularize.js'></script>
|
||||
<script src='spec/actions/connect.js'></script>
|
||||
<script src="spec/actions/delete_multiple.js"></script>
|
||||
<script src="spec/actions/delete_node.js"></script>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<script src="spec/actions/add_midpoint.js"></script>
|
||||
<script src="spec/actions/add_entity.js"></script>
|
||||
<script src="spec/actions/change_tags.js"></script>
|
||||
<script src='spec/actions/circularize.js'></script>
|
||||
<script src='spec/actions/connect.js'></script>
|
||||
<script src="spec/actions/delete_multiple.js"></script>
|
||||
<script src="spec/actions/delete_node.js"></script>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
describe("iD.actions.Circularize", function () {
|
||||
var projection = d3.geo.mercator();
|
||||
|
||||
it("creates a circle of 12 nodes", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a', loc: [0, 0]}),
|
||||
'b': iD.Node({id: 'b', loc: [2, 0]}),
|
||||
'c': iD.Node({id: 'c', loc: [2, 2]}),
|
||||
'd': iD.Node({id: 'd', loc: [0, 2]}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
});
|
||||
|
||||
graph = iD.actions.Circularize('-', projection)(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.have.length(13);
|
||||
});
|
||||
|
||||
it("reuses existing nodes", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a', loc: [0, 0]}),
|
||||
'b': iD.Node({id: 'b', loc: [2, 0]}),
|
||||
'c': iD.Node({id: 'c', loc: [2, 2]}),
|
||||
'd': iD.Node({id: 'd', loc: [0, 2]}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
});
|
||||
|
||||
graph = iD.actions.Circularize('-', projection)(graph);
|
||||
|
||||
expect(graph.entity('-').nodes.slice(0, 4)).to.eql(['c', 'b', 'a', 'd']);
|
||||
});
|
||||
|
||||
it("deletes unused nodes that are not members of other ways", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a', loc: [0, 0]}),
|
||||
'b': iD.Node({id: 'b', loc: [2, 0]}),
|
||||
'c': iD.Node({id: 'c', loc: [2, 2]}),
|
||||
'd': iD.Node({id: 'd', loc: [0, 2]}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
});
|
||||
|
||||
graph = iD.actions.Circularize('-', projection, 3)(graph);
|
||||
|
||||
expect(graph.entity('d')).to.be.undefined;
|
||||
});
|
||||
|
||||
it("reconnects unused nodes that are members of other ways", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a', loc: [0, 0]}),
|
||||
'b': iD.Node({id: 'b', loc: [2, 0]}),
|
||||
'c': iD.Node({id: 'c', loc: [2, 2]}),
|
||||
'd': iD.Node({id: 'd', loc: [0, 2]}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']}),
|
||||
'=': iD.Way({id: '=', nodes: ['d']})
|
||||
});
|
||||
|
||||
graph = iD.actions.Circularize('-', projection, 3)(graph);
|
||||
|
||||
expect(graph.entity('d')).to.be.undefined;
|
||||
expect(graph.entity('=').nodes).to.eql(['c']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user