From 6c2b097eb7cd6d29d13335008cbb5ea62a3f864c Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 3 Jun 2020 14:51:31 -0400 Subject: [PATCH] Enable disconnection of multiple selected features from each other, if connected, and otherwise from everything (close #7652) Add specific descriptions for a wider range of disconnection scenarios --- data/core.yaml | 30 ++++++++-- dist/locales/en.json | 41 +++++++++++--- modules/operations/disconnect.js | 97 ++++++++++++++++++++++++-------- 3 files changed, 131 insertions(+), 37 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 4ce9a17ab..a14817f71 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -246,15 +246,33 @@ en: restriction: "These features can't be connected because it would damage a \"{relation}\" relation." disconnect: title: Disconnect - description: Disconnect these lines/areas from each other. - line: - description: Disconnect this line from other features. - area: - description: Disconnect this area from other features. + description: + no_points: + single_way: + line: Disconnect this line from other features. + area: Disconnect this area from other features. + multiple_ways: + conjoined: Disconnect these features from each other. + separate: Disconnect these features from everything. + single_point: + no_ways: Disconnect the features at this point. + single_way: + line: Disconnect the selected line at this point. + area: Disconnect the selected area at this point. + multiple_ways: Disconnect the selected features at this point. + multiple_points: + no_ways: Disconnect the features at these points. + single_way: + line: Disconnect the selected line at these points. + area: Disconnect the selected area at these points. + multiple_ways: Disconnect the selected features at these points. key: D - annotation: Disconnected lines/areas. + annotation: + features: Disconnected features. + from_each_other: Disconnected features from each other. too_large: single: This can't be disconnected because not enough of it is currently visible. + multiple: These can't be disconnected because not enough of them are currently visible. not_connected: There aren't enough lines/areas here to disconnect. not_downloaded: This can't be disconnected because parts of it have not yet been downloaded. connected_to_hidden: This can't be disconnected because it is connected to a hidden feature. diff --git a/dist/locales/en.json b/dist/locales/en.json index d52f2b4a6..bebecf940 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -324,17 +324,42 @@ }, "disconnect": { "title": "Disconnect", - "description": "Disconnect these lines/areas from each other.", - "line": { - "description": "Disconnect this line from other features." - }, - "area": { - "description": "Disconnect this area from other features." + "description": { + "no_points": { + "single_way": { + "line": "Disconnect this line from other features.", + "area": "Disconnect this area from other features." + }, + "multiple_ways": { + "conjoined": "Disconnect these features from each other.", + "separate": "Disconnect these features from everything." + } + }, + "single_point": { + "no_ways": "Disconnect the features at this point.", + "single_way": { + "line": "Disconnect the selected line at this point.", + "area": "Disconnect the selected area at this point." + }, + "multiple_ways": "Disconnect the selected features at this point." + }, + "multiple_points": { + "no_ways": "Disconnect the features at these points.", + "single_way": { + "line": "Disconnect the selected line at these points.", + "area": "Disconnect the selected area at these points." + }, + "multiple_ways": "Disconnect the selected features at these points." + } }, "key": "D", - "annotation": "Disconnected lines/areas.", + "annotation": { + "features": "Disconnected features.", + "from_each_other": "Disconnected features from each other." + }, "too_large": { - "single": "This can't be disconnected because not enough of it is currently visible." + "single": "This can't be disconnected because not enough of it is currently visible.", + "multiple": "These can't be disconnected because not enough of them are currently visible." }, "not_connected": "There aren't enough lines/areas here to disconnect.", "not_downloaded": "This can't be disconnected because parts of it have not yet been downloaded.", diff --git a/modules/operations/disconnect.js b/modules/operations/disconnect.js index 33288a29f..15d411ce9 100644 --- a/modules/operations/disconnect.js +++ b/modules/operations/disconnect.js @@ -1,6 +1,7 @@ import { t } from '../core/localizer'; import { actionDisconnect } from '../actions/disconnect'; import { behaviorOperation } from '../behavior/operation'; +import { geoExtent } from '../geo'; import { utilGetAllNodes } from '../util/index'; @@ -21,23 +22,12 @@ export function operationDisconnect(context, selectedIDs) { } }); - var disconnectingWayID = vertexIDs.length === 0 && wayIDs.length === 1 && wayIDs[0]; - var extent, nodes, coords; + var extent, nodes, coords, descriptionID = '', annotationID = 'features'; - if (disconnectingWayID) { // disconnecting a way - var way = context.entity(disconnectingWayID); - extent = way.extent(context.graph()); - nodes = utilGetAllNodes([disconnectingWayID], context.graph()); - coords = nodes.map(function(n) { return n.loc; }); + if (vertexIDs.length > 0) { + // At the selected vertices, disconnect the selected ways, if any, else + // disconnect all connected ways - way.nodes.forEach(function(vertexID) { - var action = actionDisconnect(vertexID).limitWays(wayIDs); - if (action.disabled(context.graph()) !== 'not_connected') { - actions.push(action); - } - }); - - } else { // disconnecting a vertex vertexIDs.forEach(function(vertexID) { var action = actionDisconnect(vertexID); @@ -50,6 +40,70 @@ export function operationDisconnect(context, selectedIDs) { } actions.push(action); }); + + descriptionID += actions.length === 1 ? 'single_point.' : 'multiple_points.'; + if (wayIDs.length === 1) { + descriptionID += 'single_way.' + context.graph().geometry(wayIDs[0]); + } else { + descriptionID += wayIDs.length === 0 ? 'no_ways' : 'multiple_ways'; + } + + } else if (wayIDs.length > 0) { + // Disconnect the selected ways from each other, if they're connected, + // else disconnect them from all connected ways + + var ways = wayIDs.map(function(id) { + return context.entity(id); + }); + extent = ways.reduce(function(extent, entity) { + return extent.extend(entity.extent(context.graph())); + }, geoExtent()); + nodes = utilGetAllNodes(wayIDs, context.graph()); + coords = nodes.map(function(n) { return n.loc; }); + + // actions for connected nodes shared by at least two selected ways + var sharedActions = []; + // actions for connected nodes + var unsharedActions = []; + + nodes.forEach(function(node) { + var action = actionDisconnect(node.id).limitWays(wayIDs); + if (action.disabled(context.graph()) !== 'not_connected') { + + var count = 0; + for (var i in ways) { + var way = ways[i]; + if (way.nodes.indexOf(node.id) !== -1) { + count += 1; + } + if (count > 1) break; + } + + if (count > 1) { + sharedActions.push(action); + } else { + unsharedActions.push(action); + } + } + }); + + descriptionID += 'no_points.'; + descriptionID += wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.'; + + if (sharedActions.length) { + // if any nodes are shared, only disconnect the selected ways from each other + actions = sharedActions; + descriptionID += 'conjoined'; + annotationID = 'from_each_other'; + } else { + // if no nodes are shared, disconnect the selected ways from all connected ways + actions = unsharedActions; + if (wayIDs.length === 1) { + descriptionID += context.graph().geometry(wayIDs[0]); + } else { + descriptionID += 'separate'; + } + } } @@ -84,9 +138,9 @@ export function operationDisconnect(context, selectedIDs) { if (reason) return reason; } - if (disconnectingWayID && extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large.single'; - } else if (disconnectingWayID && someMissing()) { + if (extent && extent.percentContainedIn(context.map().extent()) < 0.8) { + return 'too_large.' + (wayIDs.length === 1 ? 'single' : 'multiple'); + } else if (coords && someMissing()) { return 'not_downloaded'; } else if (selectedIDs.some(context.hasHiddenConnections)) { return 'connected_to_hidden'; @@ -115,15 +169,12 @@ export function operationDisconnect(context, selectedIDs) { if (disable) { return t('operations.disconnect.' + disable); } - if (disconnectingWayID) { - return t('operations.disconnect.' + context.graph().geometry(disconnectingWayID) + '.description'); - } - return t('operations.disconnect.description'); + return t('operations.disconnect.description.' + descriptionID); }; operation.annotation = function() { - return t('operations.disconnect.annotation'); + return t('operations.disconnect.annotation.' + annotationID); };