diff --git a/modules/actions/straighten.js b/modules/actions/straighten.js index e9456d220..b88eaccf8 100644 --- a/modules/actions/straighten.js +++ b/modules/actions/straighten.js @@ -1,4 +1,5 @@ import { actionDeleteNode } from './delete_node'; +import _difference from 'lodash-es/difference'; import { geoVecInterp, @@ -9,20 +10,47 @@ import { /* * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as */ -export function actionStraighten(wayId, projection) { +export function actionStraighten(wayIds, projection) { function positionAlongWay(n, s, e) { return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1])) / (Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2)); } + // Return all ways as a continuous, ordered array of nodes + var getAllNodes = function(graph) { + var nodes = [], + startNodes = [], + endNodes = [], + ways = {}; + + for (var i = 0; i < wayIds.length; i++) { + var way = graph.entity(wayIds[i]); + nodes = graph.childNodes(way); + ways[nodes[0].id] = nodes; + startNodes.push(nodes[0]); + endNodes.push(nodes[nodes.length-1]); + } + + var startNode = _difference(startNodes, endNodes)[0], + endNode = _difference(endNodes, startNodes)[0]; + + nodes = ways[startNode.id]; + + while (nodes[nodes.length-1] !== endNode) { + var lastNode = nodes[nodes.length-1]; + nodes = nodes.concat(ways[lastNode.id]); + } + + return nodes; + }; + var action = function(graph, t) { if (t === null || !isFinite(t)) t = 1; t = Math.min(Math.max(+t, 0), 1); - var way = graph.entity(wayId), - nodes = graph.childNodes(way), + var nodes = getAllNodes(graph), points = nodes.map(function(n) { return projection(n.loc); }), startPoint = points[0], endPoint = points[points.length-1], @@ -64,8 +92,7 @@ export function actionStraighten(wayId, projection) { action.disabled = function(graph) { // check way isn't too bendy - var way = graph.entity(wayId), - nodes = graph.childNodes(way), + var nodes = getAllNodes(graph), points = nodes.map(function(n) { return projection(n.loc); }), startPoint = points[0], endPoint = points[points.length-1], diff --git a/modules/operations/straighten.js b/modules/operations/straighten.js index b20c5846f..e969311de 100644 --- a/modules/operations/straighten.js +++ b/modules/operations/straighten.js @@ -1,4 +1,5 @@ import _uniq from 'lodash-es/uniq'; +import _difference from 'lodash-es/difference'; import { t } from '../util/locale'; import { actionStraighten } from '../actions/index'; @@ -6,8 +7,7 @@ import { behaviorOperation } from '../behavior/index'; export function operationStraighten(selectedIDs, context) { - var entityId = selectedIDs[0], - action = actionStraighten(entityId, context.projection); + var action = actionStraighten(selectedIDs, context.projection); function operation() { @@ -16,18 +16,41 @@ export function operationStraighten(selectedIDs, context) { operation.available = function() { - var entity = context.entity(entityId); - return selectedIDs.length === 1 && - entity.type === 'way' && - !entity.isClosed() && - _uniq(entity.nodes).length > 2; + var nodes = [], + startNodes = [], + endNodes = []; + + for (var i = 0; i < selectedIDs.length; i++) { + if (!context.hasEntity(selectedIDs[i])) return false; + + var entity = context.entity(selectedIDs[i]); + + if (entity.type !== 'way' || + entity.isClosed()) { + return false; + } + + nodes = nodes.concat(entity.nodes); + startNodes.push(entity.nodes[0]); + endNodes.push(entity.nodes[entity.nodes.length-1]); + } + + if (_uniq(nodes).length <= 2) return false; + + // Ensure all ways are connected (i.e. only one unique start point and one unique end point) + if (_difference(startNodes, endNodes).length !== 1 || + _difference(endNodes, startNodes).length !== 1) return false; + + return true; }; operation.disabled = function() { var reason; - if (context.hasHiddenConnections(entityId)) { - reason = 'connected_to_hidden'; + for (var i = 0; i < selectedIDs.length; i++) { + if (context.hasHiddenConnections(selectedIDs[i])) { + reason = 'connected_to_hidden'; + } } return action.disabled(context.graph()) || reason; };