From 4ff634b90f1ede4bc5595ae2b5c5c4299c3b1b79 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 5 Feb 2020 09:57:33 -0500 Subject: [PATCH] Enable the circularize operation for multiple selected features (close #7326) --- data/core.yaml | 32 +++++++++----- dist/locales/en.json | 36 ++++++++++++---- modules/operations/circularize.js | 72 +++++++++++++++++++++++-------- 3 files changed, 104 insertions(+), 36 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 877512046..a4d725318 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -85,17 +85,29 @@ en: circularize: title: Circularize description: - line: Make this line circular. - area: Make this area circular. + single: Make this feature circular. + multiple: Make these features circular. key: O annotation: - line: Made a line circular. - area: Made an area circular. - not_closed: This can't be made circular because it's not a loop. - too_large: This can't be made circular because not enough of it is currently visible. - connected_to_hidden: This can't be made circular because it is connected to a hidden feature. - not_downloaded: This can't be made circular because parts of it have not yet been downloaded. - already_circular: This can't be made circular because it's already circular. + single: Made a feature circular. + multiple: Made features circular. + multiple_blockers: + multiple: These can't be made circular for multiple reasons. + not_closed: + single: This can't be made circular because it's not a loop. + multiple: These can't be made circular because they aren't loops. + too_large: + single: This can't be made circular because not enough of it is currently visible. + multiple: These can't be made circular because not enough of them are currently visible. + connected_to_hidden: + single: This can't be made circular because it is connected to a hidden feature. + multiple: These can't be made circular because some are connected to hidden features. + not_downloaded: + single: This can't be made circular because parts of it have not yet been downloaded. + multiple: These can't be made circular because parts of them have not yet been downloaded. + already_circular: + single: This can't be made more circular than it already is. + multiple: These can't be made more circular than they already are. orthogonalize: title: Square description: @@ -2067,4 +2079,4 @@ en: wikidata: identifier: "Identifier" label: "Label" - description: "Description" \ No newline at end of file + description: "Description" diff --git a/dist/locales/en.json b/dist/locales/en.json index 9e8e76c4b..2fe02c059 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -108,19 +108,37 @@ "circularize": { "title": "Circularize", "description": { - "line": "Make this line circular.", - "area": "Make this area circular." + "single": "Make this feature circular.", + "multiple": "Make these features circular." }, "key": "O", "annotation": { - "line": "Made a line circular.", - "area": "Made an area circular." + "single": "Made a feature circular.", + "multiple": "Made features circular." }, - "not_closed": "This can't be made circular because it's not a loop.", - "too_large": "This can't be made circular because not enough of it is currently visible.", - "connected_to_hidden": "This can't be made circular because it is connected to a hidden feature.", - "not_downloaded": "This can't be made circular because parts of it have not yet been downloaded.", - "already_circular": "This can't be made circular because it's already circular." + "multiple_blockers": { + "multiple": "These can't be made circular for multiple reasons." + }, + "not_closed": { + "single": "This can't be made circular because it's not a loop.", + "multiple": "These can't be made circular because they aren't loops." + }, + "too_large": { + "single": "This can't be made circular because not enough of it is currently visible.", + "multiple": "These can't be made circular because not enough of them are currently visible." + }, + "connected_to_hidden": { + "single": "This can't be made circular because it is connected to a hidden feature.", + "multiple": "These can't be made circular because some are connected to hidden features." + }, + "not_downloaded": { + "single": "This can't be made circular because parts of it have not yet been downloaded.", + "multiple": "These can't be made circular because parts of them have not yet been downloaded." + }, + "already_circular": { + "single": "This can't be made more circular than it already is.", + "multiple": "These can't be made more circular than they already are." + } }, "orthogonalize": { "title": "Square", diff --git a/modules/operations/circularize.js b/modules/operations/circularize.js index 9ee795475..21563d0c8 100644 --- a/modules/operations/circularize.js +++ b/modules/operations/circularize.js @@ -5,16 +5,41 @@ import { utilGetAllNodes } from '../util'; export function operationCircularize(selectedIDs, context) { - var entityID = selectedIDs[0]; - var entity = context.entity(entityID); - var extent = entity.extent(context.graph()); - var geometry = context.geometry(entityID); - var action = actionCircularize(entityID, context.projection); - var nodes = utilGetAllNodes(selectedIDs, context.graph()); - var coords = nodes.map(function(n) { return n.loc; }); + var _extent; + var _actions = selectedIDs.map(getAction).filter(Boolean); + var _amount = _actions.length === 1 ? 'single' : 'multiple'; + var _coords = utilGetAllNodes(selectedIDs, context.graph()) + .map(function(n) { return n.loc; }); + + function getAction(entityID) { + + var entity = context.entity(entityID); + + if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null; + + if (!_extent) { + _extent = entity.extent(context.graph()); + } else { + _extent = _extent.extend(entity.extent(context.graph())); + } + + return actionCircularize(entityID, context.projection); + } var operation = function() { - context.perform(action, operation.annotation()); + if (!_actions.length) return; + + var combinedAction = function(graph, t) { + _actions.forEach(function(action) { + if (!action.disabled(graph)) { + graph = action(graph, t); + } + }); + return graph; + }; + combinedAction.transitionable = true; + + context.perform(combinedAction, operation.annotation()); window.setTimeout(function() { context.validator().validate(); @@ -23,18 +48,31 @@ export function operationCircularize(selectedIDs, context) { operation.available = function() { - return selectedIDs.length === 1 && - entity.type === 'way' && - new Set(entity.nodes).size > 1; + return _actions.length && selectedIDs.length === _actions.length; }; // don't cache this because the visible extent could change operation.disabled = function() { - var actionDisabled = action.disabled(context.graph()); + if (!_actions.length) return ''; + + var actionDisabled; + var actionDisableds = {}; + + if (_actions.every(function(action) { + var disabled = action.disabled(context.graph()); + if (disabled) actionDisableds[disabled] = true; + return disabled; + })) { + actionDisabled = _actions[0].disabled(context.graph()); + } + if (actionDisabled) { + if (Object.keys(actionDisableds).length > 1) { + return 'multiple_blockers'; + } return actionDisabled; - } else if (extent.percentContainedIn(context.extent()) < 0.8) { + } else if (_extent.percentContainedIn(context.extent()) < 0.8) { return 'too_large'; } else if (someMissing()) { return 'not_downloaded'; @@ -49,7 +87,7 @@ export function operationCircularize(selectedIDs, context) { if (context.inIntro()) return false; var osm = context.connection(); if (osm) { - var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); + var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); if (missing.length) { missing.forEach(function(loc) { context.loadTileAtLoc(loc); }); return true; @@ -63,13 +101,13 @@ export function operationCircularize(selectedIDs, context) { operation.tooltip = function() { var disable = operation.disabled(); return disable ? - t('operations.circularize.' + disable) : - t('operations.circularize.description.' + geometry); + t('operations.circularize.' + disable + '.' + _amount) : + t('operations.circularize.description.' + _amount); }; operation.annotation = function() { - return t('operations.circularize.annotation.' + geometry); + return t('operations.circularize.annotation.' + _amount); };