diff --git a/combobox.html b/combobox.html index 6eaa944a9..34deb2d21 100644 --- a/combobox.html +++ b/combobox.html @@ -81,7 +81,7 @@ - + @@ -103,7 +103,7 @@ - + diff --git a/index.html b/index.html index f62adccdc..7d76a8712 100644 --- a/index.html +++ b/index.html @@ -108,7 +108,7 @@ - + diff --git a/js/id/actions/move.js b/js/id/actions/move.js new file mode 100644 index 000000000..09a69f09c --- /dev/null +++ b/js/id/actions/move.js @@ -0,0 +1,31 @@ +// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java +// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as +iD.actions.Move = function(ids, delta, projection) { + function addNodes(ids, nodes, graph) { + ids.forEach(function(id) { + var entity = graph.entity(id); + if (entity.type === 'node') { + nodes.push(id); + } else if (entity.type === 'way') { + nodes.push.apply(nodes, entity.nodes); + } else { + addNodes(_.pluck(entity.members, 'id'), nodes, graph); + } + }); + } + + return function(graph) { + var nodes = []; + + addNodes(ids, nodes, graph); + + _.uniq(nodes).forEach(function(id) { + var node = graph.entity(id), + start = projection(node.loc), + end = projection.invert([start[0] + delta[0], start[1] + delta[1]]); + graph = graph.replace(node.move(end)); + }); + + return graph; + }; +}; diff --git a/js/id/actions/move_way.js b/js/id/actions/move_way.js deleted file mode 100644 index 376e31d6e..000000000 --- a/js/id/actions/move_way.js +++ /dev/null @@ -1,14 +0,0 @@ -iD.actions.MoveWay = function(wayId, delta, projection) { - return function(graph) { - return graph.update(function(graph) { - var way = graph.entity(wayId); - - _.uniq(way.nodes).forEach(function(id) { - var node = graph.entity(id), - start = projection(node.loc), - end = projection.invert([start[0] + delta[0], start[1] + delta[1]]); - graph = graph.replace(node.move(end)); - }); - }); - }; -}; diff --git a/js/id/modes/move.js b/js/id/modes/move.js index 766ee8453..d2089a2ad 100644 --- a/js/id/modes/move.js +++ b/js/id/modes/move.js @@ -4,14 +4,13 @@ iD.modes.Move = function(context, entityIDs) { button: 'browse' }; - var keybinding = d3.keybinding('move'), - entities = entityIDs.map(context.entity); + var keybinding = d3.keybinding('move'); mode.enter = function() { var origin, nudgeInterval, - annotation = entities.length === 1 ? - t('operations.move.annotation.' + context.geometry(entities[0].id)) : + annotation = entityIDs.length === 1 ? + t('operations.move.annotation.' + context.geometry(entityIDs[0])) : t('operations.move.annotation.multiple'); context.perform( @@ -57,19 +56,9 @@ iD.modes.Move = function(context, entityIDs) { origin = context.map().mouseCoordinates(); - entities.forEach(function(entity) { - if (entity.type === 'way') { - context.replace( - iD.actions.MoveWay(entity.id, delta, context.projection)); - } else if (entity.type === 'node') { - var start = context.projection(context.entity(entity.id).loc), - end = [start[0] + delta[0], start[1] + delta[1]], - loc = context.projection.invert(end); - context.replace(iD.actions.MoveNode(entity.id, loc)); - } - }); - - context.replace(iD.actions.Noop(), annotation); + context.replace( + iD.actions.Move(entityIDs, delta, context.projection), + annotation); } function finish() { diff --git a/js/id/operations/move.js b/js/id/operations/move.js index dd001be8d..22d913ce0 100644 --- a/js/id/operations/move.js +++ b/js/id/operations/move.js @@ -6,7 +6,7 @@ iD.operations.Move = function(selection, context) { operation.available = function() { return selection.length > 1 || - context.entity(selection[0]).type === 'way'; + context.entity(selection[0]).type !== 'node'; }; operation.enabled = function() { diff --git a/test/index.html b/test/index.html index 603f29a08..948a4bc57 100644 --- a/test/index.html +++ b/test/index.html @@ -96,7 +96,7 @@ - + @@ -177,7 +177,7 @@ - + diff --git a/test/index_packaged.html b/test/index_packaged.html index c7d8ad7de..c8b6984fe 100644 --- a/test/index_packaged.html +++ b/test/index_packaged.html @@ -38,7 +38,7 @@ - + diff --git a/test/spec/actions/move.js b/test/spec/actions/move.js new file mode 100644 index 000000000..2017409c7 --- /dev/null +++ b/test/spec/actions/move.js @@ -0,0 +1,51 @@ +describe("iD.actions.Move", function() { + it("moves all nodes in a way by the given amount", function() { + var node1 = iD.Node({loc: [0, 0]}), + node2 = iD.Node({loc: [5, 10]}), + way = iD.Way({nodes: [node1.id, node2.id]}), + delta = [2, 3], + projection = d3.geo.mercator(), + graph = iD.actions.Move([way.id], delta, projection)(iD.Graph([node1, node2, way])), + loc1 = graph.entity(node1.id).loc, + loc2 = graph.entity(node2.id).loc; + expect(loc1[0]).to.be.closeTo( 1.440, 0.001); + expect(loc1[1]).to.be.closeTo(-2.159, 0.001); + expect(loc2[0]).to.be.closeTo( 6.440, 0.001); + expect(loc2[1]).to.be.closeTo( 7.866, 0.001); + }); + + it("moves repeated nodes only once", function() { + var node = iD.Node({loc: [0, 0]}), + way = iD.Way({nodes: [node.id, node.id]}), + delta = [2, 3], + projection = d3.geo.mercator(), + graph = iD.actions.Move([way.id], delta, projection)(iD.Graph([node, way])), + loc = graph.entity(node.id).loc; + expect(loc[0]).to.be.closeTo( 1.440, 0.001); + expect(loc[1]).to.be.closeTo(-2.159, 0.001); + }); + + it("moves multiple ways", function() { + var node = iD.Node({loc: [0, 0]}), + way1 = iD.Way({nodes: [node.id]}), + way2 = iD.Way({nodes: [node.id]}), + delta = [2, 3], + projection = d3.geo.mercator(), + graph = iD.actions.Move([way1.id, way2.id], delta, projection)(iD.Graph([node, way1, way2])), + loc = graph.entity(node.id).loc; + expect(loc[0]).to.be.closeTo( 1.440, 0.001); + expect(loc[1]).to.be.closeTo(-2.159, 0.001); + }); + + it("moves leaf nodes of a relation", function() { + var node = iD.Node({loc: [0, 0]}), + way = iD.Way({nodes: [node.id]}), + relation = iD.Relation({members: [{id: way.id}]}), + delta = [2, 3], + projection = d3.geo.mercator(), + graph = iD.actions.Move([relation.id], delta, projection)(iD.Graph([node, way, relation])), + loc = graph.entity(node.id).loc; + expect(loc[0]).to.be.closeTo( 1.440, 0.001); + expect(loc[1]).to.be.closeTo(-2.159, 0.001); + }); +}); diff --git a/test/spec/actions/move_way.js b/test/spec/actions/move_way.js deleted file mode 100644 index 24bc81593..000000000 --- a/test/spec/actions/move_way.js +++ /dev/null @@ -1,27 +0,0 @@ -describe("iD.actions.MoveWay", function() { - it("moves all nodes in a way by the given amount", function() { - var node1 = iD.Node({loc: [0, 0]}), - node2 = iD.Node({loc: [5, 10]}), - way = iD.Way({nodes: [node1.id, node2.id]}), - delta = [2, 3], - projection = d3.geo.mercator(), - graph = iD.actions.MoveWay(way.id, delta, projection)(iD.Graph([node1, node2, way])), - loc1 = graph.entity(node1.id).loc, - loc2 = graph.entity(node2.id).loc; - expect(loc1[0]).to.be.closeTo( 1.440, 0.001); - expect(loc1[1]).to.be.closeTo(-2.159, 0.001); - expect(loc2[0]).to.be.closeTo( 6.440, 0.001); - expect(loc2[1]).to.be.closeTo( 7.866, 0.001); - }); - - it("moves repeated nodes only once", function() { - var node = iD.Node({loc: [0, 0]}), - way = iD.Way({nodes: [node.id, node.id]}), - delta = [2, 3], - projection = d3.geo.mercator(), - graph = iD.actions.MoveWay(way.id, delta, projection)(iD.Graph([node, way])), - loc = graph.entity(node.id).loc; - expect(loc[0]).to.be.closeTo( 1.440, 0.001); - expect(loc[1]).to.be.closeTo(-2.159, 0.001); - }); -});