diff --git a/modules/actions/index.js b/modules/actions/index.js index ff768cdd0..7c4682530 100644 --- a/modules/actions/index.js +++ b/modules/actions/index.js @@ -18,6 +18,7 @@ export { actionDiscardTags } from './discard_tags'; export { actionDisconnect } from './disconnect'; export { actionJoin } from './join'; export { actionMerge } from './merge'; +export { actionMergeWayNodes } from './merge_way_nodes'; export { actionMergePolygon } from './merge_polygon'; export { actionMergeRemoteChanges } from './merge_remote_changes'; export { actionMove } from './move'; diff --git a/modules/actions/merge_way_nodes.js b/modules/actions/merge_way_nodes.js new file mode 100644 index 000000000..1062af7a8 --- /dev/null +++ b/modules/actions/merge_way_nodes.js @@ -0,0 +1,55 @@ +import _sum from 'lodash-es/sum'; + +import { osmNode } from '../osm/node'; + + +export function actionMergeWayNodes (ids) { + function getSelectedEntities (graph) { + return ids.map(function (id) { return graph.entity(id); }); + } + + function calcAverageLoc (nodes) { + return [ + _sum(nodes.map(function (node) { return node.loc[0]; })) / nodes.length, + _sum(nodes.map(function (node) { return node.loc[1]; })) / nodes.length + ]; + } + + function replaceWithinWays (newNode) { + return function (graph, node) { + return graph.parentWays(node).reduce(function (graph, way) { + return graph.replace(way.replaceNode(node.id, newNode.id)); + }, graph); + }; + } + + function removeFromGraph (graph, entity) { + return graph.remove(entity); + } + + var action = function (graph) { + var nodes = getSelectedEntities(graph), + newNode = new osmNode({ loc: calcAverageLoc(nodes) }); + + graph = graph.replace(newNode); + + graph = nodes.reduce(replaceWithinWays(newNode), graph); + + graph = nodes.reduce(removeFromGraph, graph); + + return graph; + }; + + action.disabled = function (graph) { + function isNotWayNode (entity) { return entity.type !== 'node' || graph.parentWays(entity) <= 0; } + function isNotExtremeNode (node) { return !node.isEndpoint(graph); } + + var entities = getSelectedEntities(graph); + + if (entities.some(isNotWayNode) || entities.some(isNotExtremeNode)) { + return 'not_eligible'; + } + }; + + return action; +} \ No newline at end of file diff --git a/modules/operations/merge.js b/modules/operations/merge.js index 95fdbf860..bd3ac1201 100644 --- a/modules/operations/merge.js +++ b/modules/operations/merge.js @@ -3,7 +3,8 @@ import { actionChangePreset, actionJoin, actionMerge, - actionMergePolygon + actionMergePolygon, + actionMergeWayNodes } from '../actions'; import { behaviorOperation } from '../behavior'; @@ -26,7 +27,8 @@ export function operationMerge(selectedIDs, context) { var join = actionJoin(selectedIDs), merge = actionMerge(selectedIDs), - mergePolygon = actionMergePolygon(selectedIDs); + mergePolygon = actionMergePolygon(selectedIDs), + mergeWayNodes = actionMergeWayNodes(selectedIDs); var operation = function() { @@ -37,8 +39,10 @@ export function operationMerge(selectedIDs, context) { action = join; } else if (!merge.disabled(origGraph)) { action = merge; - } else { + } else if (!mergePolygon.disabled(origGraph)) { action = mergePolygon; + } else { + action = mergeWayNodes; } context.perform(action, operation.annotation()); @@ -65,25 +69,27 @@ export function operationMerge(selectedIDs, context) { operation.disabled = function() { return join.disabled(context.graph()) && merge.disabled(context.graph()) && - mergePolygon.disabled(context.graph()); + mergePolygon.disabled(context.graph()) && + mergeWayNodes.disabled(context.graph()); }; operation.tooltip = function() { var j = join.disabled(context.graph()), m = merge.disabled(context.graph()), - p = mergePolygon.disabled(context.graph()); + p = mergePolygon.disabled(context.graph()), + n = mergeWayNodes.disabled(context.graph()); - if (j === 'restriction' && m && p) { + if (j === 'restriction' && m && p && n) { return t('operations.merge.restriction', { relation: context.presets().item('type/restriction').name() }); } - if (p === 'incomplete_relation' && j && m) { + if (p === 'incomplete_relation' && j && m && n) { return t('operations.merge.incomplete_relation'); } - if (j && m && p) { + if (j && m && p && n) { return t('operations.merge.' + j); }