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);
- });
-});