From bd8c9d6a00f5deac4d947673948c81ceb7ab2cc6 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Wed, 6 Feb 2013 13:31:06 -0800 Subject: [PATCH] Support merging points into an area (#435) --- index.html | 1 + js/id/actions/merge.js | 35 +++++++++++++++++++++++++++++++++++ js/id/operations/merge.js | 18 ++++++++++++++---- test/index.html | 2 ++ test/index_packaged.html | 1 + test/spec/actions/merge.js | 20 ++++++++++++++++++++ 6 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 js/id/actions/merge.js create mode 100644 test/spec/actions/merge.js diff --git a/index.html b/index.html index 09d73b8f6..bdc523685 100644 --- a/index.html +++ b/index.html @@ -83,6 +83,7 @@ + diff --git a/js/id/actions/merge.js b/js/id/actions/merge.js new file mode 100644 index 000000000..8ba2a2318 --- /dev/null +++ b/js/id/actions/merge.js @@ -0,0 +1,35 @@ +iD.actions.Merge = function(ids) { + function groupEntitiesByGeometry(graph) { + var entities = ids.map(function(id) { return graph.entity(id); }); + return _.extend({point: [], area: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); })); + } + + var action = function(graph) { + var geometries = groupEntitiesByGeometry(graph), + area = geometries['area'][0], + points = geometries['point']; + + points.forEach(function (point) { + area = area.mergeTags(point.tags); + + graph.parentRelations(point).forEach(function (parent) { + graph = graph.replace(parent.replaceMember(point, area)); + }); + + graph = graph.remove(point); + }); + + graph = graph.replace(area); + + return graph; + }; + + action.enabled = function(graph) { + var geometries = groupEntitiesByGeometry(graph); + return geometries['area'].length === 1 && + geometries['point'].length > 0 && + (geometries['area'].length + geometries['point'].length) === ids.length; + }; + + return action; +}; diff --git a/js/id/operations/merge.js b/js/id/operations/merge.js index b2073db52..6202f1b3f 100644 --- a/js/id/operations/merge.js +++ b/js/id/operations/merge.js @@ -1,18 +1,28 @@ iD.operations.Merge = function(selection, context) { - var action = iD.actions.Join(selection); + var join = iD.actions.Join(selection), + merge = iD.actions.Merge(selection); var operation = function() { var annotation = t('operations.merge.annotation', {n: selection.length}), - difference = context.perform(action, annotation); + action; + + if (join.enabled(context.graph())) { + action = join; + } else { + action = merge; + } + + var difference = context.perform(action, annotation); context.enter(iD.modes.Select(context, difference.extantIDs())); }; operation.available = function() { - return selection.length > 1; + return selection.length >= 2; }; operation.enabled = function() { - return action.enabled(context.graph()); + return join.enabled(context.graph()) || + merge.enabled(context.graph()); }; operation.id = "merge"; diff --git a/test/index.html b/test/index.html index 3f2eba72e..ecad5ca75 100644 --- a/test/index.html +++ b/test/index.html @@ -80,6 +80,7 @@ + @@ -157,6 +158,7 @@ + diff --git a/test/index_packaged.html b/test/index_packaged.html index 8dd73d666..f85ac1f0b 100644 --- a/test/index_packaged.html +++ b/test/index_packaged.html @@ -42,6 +42,7 @@ + diff --git a/test/spec/actions/merge.js b/test/spec/actions/merge.js new file mode 100644 index 000000000..5ea0b3dea --- /dev/null +++ b/test/spec/actions/merge.js @@ -0,0 +1,20 @@ +describe("iD.actions.Merge", function () { + it("merges multiple points to an area", function () { + var graph = iD.Graph({ + 'a': iD.Node({id: 'a', tags: {a: 'a'}}), + 'b': iD.Node({id: 'b', tags: {b: 'b'}}), + 'w': iD.Way({id: 'w', tags: {area: 'yes'}}), + 'r': iD.Relation({id: 'r', members: [{id: 'a', role: 'r', type: 'node'}]}) + }), + action = iD.actions.Merge(['a', 'b', 'w']); + + expect(action.enabled(graph)).to.be.true; + + graph = action(graph); + + expect(graph.entity('a')).to.be.undefined; + expect(graph.entity('b')).to.be.undefined; + expect(graph.entity('w').tags).to.eql({a: 'a', b: 'b', area: 'yes'}); + expect(graph.entity('r').members).to.eql([{id: 'w', role: 'r', type: 'way'}]); + }); +});