diff --git a/js/id/actions/orthogonalize.js b/js/id/actions/orthogonalize.js index d87b03798..6789768c8 100644 --- a/js/id/actions/orthogonalize.js +++ b/js/id/actions/orthogonalize.js @@ -7,6 +7,7 @@ iD.actions.Orthogonalize = function(wayId, projection) { var way = graph.entity(wayId), nodes = graph.childNodes(way), corner = {i: 0, dotp: 1}, + epsilon = 1e-8, points, i, j, score, motions; if (nodes.length === 4) { @@ -26,7 +27,7 @@ iD.actions.Orthogonalize = function(wayId, projection) { } else { var best; points = _.uniq(nodes).map(function(n) { return projection(n.loc); }); - score = squareness(); + score = Infinity; for (i = 0; i < 1000; i++) { motions = points.map(calcMotion); @@ -38,7 +39,7 @@ iD.actions.Orthogonalize = function(wayId, projection) { best = _.clone(points); score = newScore; } - if (score < 1.0e-8) { + if (score < epsilon) { break; } } @@ -49,6 +50,30 @@ iD.actions.Orthogonalize = function(wayId, projection) { graph = graph.replace(graph.entity(nodes[i].id) .move(projection.invert(points[i]))); } + + // remove empty nodes on straight sections + for (i = 0; i < points.length; i++) { + var node = nodes[i], + a, b, c, p, q, dotp; + + if (graph.parentWays(node).length > 1 || + graph.parentRelations(node).length || + node.hasInterestingTags()) { + + continue; + } + + a = points[(i - 1 + points.length) % points.length]; + b = points[i]; + c = points[(i + 1) % points.length]; + p = normalizePoint(subtractPoints(a, b), 1.0); + q = normalizePoint(subtractPoints(c, b), 1.0); + dotp = p[0] * q[0] + p[1] * q[1]; + + if (dotp < -1 + epsilon) { + graph = iD.actions.DeleteNode(nodes[i].id)(graph); + } + } } return graph; diff --git a/js/id/actions/straighten.js b/js/id/actions/straighten.js index 3dc703d8d..535f90a7a 100644 --- a/js/id/actions/straighten.js +++ b/js/id/actions/straighten.js @@ -21,7 +21,10 @@ iD.actions.Straighten = function(wayId, projection) { var node = nodes[i], point = points[i]; - if (graph.parentWays(node).length > 1 || (node.tags && Object.keys(node.tags).length)) { + if (graph.parentWays(node).length > 1 || + graph.parentRelations(node).length || + node.hasInterestingTags()) { + var u = positionAlongWay(point, startPoint, endPoint), p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]), p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]), diff --git a/test/spec/actions/orthogonalize.js b/test/spec/actions/orthogonalize.js index 7a5e2f193..396403749 100644 --- a/test/spec/actions/orthogonalize.js +++ b/test/spec/actions/orthogonalize.js @@ -1,6 +1,20 @@ describe("iD.actions.Orthogonalize", function () { var projection = d3.geo.mercator(); + it("orthogonalizes a perfect quad", 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.Orthogonalize('-', projection)(graph); + + expect(graph.entity('-').nodes).to.have.length(5); + }); + it("orthogonalizes a quad", function () { var graph = iD.Graph({ 'a': iD.Node({id: 'a', loc: [0, 0]}), @@ -28,6 +42,37 @@ describe("iD.actions.Orthogonalize", function () { expect(graph.entity('-').nodes).to.have.length(4); }); + it("deletes empty redundant 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: [1, 2]}), + 'e': iD.Node({id: 'e', loc: [0, 2]}), + '-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'a']}) + }); + + graph = iD.actions.Orthogonalize('-', projection)(graph); + + expect(graph.hasEntity('d')).to.eq(undefined); + }); + + it("preserves non empty redundant 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: [1, 2], tags: {foo: 'bar'}}), + 'e': iD.Node({id: 'e', loc: [0, 2]}), + '-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'a']}) + }); + + graph = iD.actions.Orthogonalize('-', projection)(graph); + + expect(graph.entity('-').nodes).to.have.length(6); + expect(graph.hasEntity('d')).to.not.eq(undefined); + }); + it("preserves the shape of skinny quads", function () { var tests = [ [ diff --git a/test/spec/actions/straighten.js b/test/spec/actions/straighten.js index d1f6ca43c..9278d3bc8 100644 --- a/test/spec/actions/straighten.js +++ b/test/spec/actions/straighten.js @@ -11,7 +11,7 @@ describe("iD.actions.Straighten", function () { graph = iD.actions.Straighten('-', projection)(graph); - expect(graph.hasEntity('b')).to.be.undefined; + expect(graph.hasEntity('b')).to.eq(undefined); }); it("does not delete tagged nodes", function() {