From 912151ff38971b04af62347d27d6982373720524 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 12 Dec 2019 16:54:16 -0500 Subject: [PATCH] Allow viewing and editing the tags/relations of selected features at any zoom level in 2.x (re: #5001) --- modules/modes/drag_node.js | 2 +- modules/modes/select.js | 12 ++++++++- modules/renderer/map.js | 51 +++++++++++++++++++++++++++-------- modules/svg/midpoints.js | 2 +- modules/svg/vertices.js | 17 +++++++++++- modules/ui/tools/undo_redo.js | 2 +- modules/util/util.js | 10 ++++++- 7 files changed, 79 insertions(+), 17 deletions(-) diff --git a/modules/modes/drag_node.js b/modules/modes/drag_node.js index 73b08350c..ce85f717e 100644 --- a/modules/modes/drag_node.js +++ b/modules/modes/drag_node.js @@ -131,7 +131,7 @@ export function modeDragNode(context) { function start(entity) { _wasMidpoint = entity.type === 'midpoint'; var hasHidden = context.features().hasHiddenConnections(entity, context.graph()); - _isCancelled = d3_event.sourceEvent.shiftKey || hasHidden; + _isCancelled = !context.editable() || d3_event.sourceEvent.shiftKey || hasHidden; if (_isCancelled) { diff --git a/modules/modes/select.js b/modules/modes/select.js index 382428ddc..201f078db 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -306,7 +306,8 @@ export function modeSelect(context, selectedIDs) { context.map() .on('move.select', closeMenu) - .on('drawn.select', selectElements); + .on('drawn.select', selectElements) + .on('crossEditableZoom.select', selectElements); context.surface() .on('dblclick.select', dblclick); @@ -348,6 +349,8 @@ export function modeSelect(context, selectedIDs) { function dblclick() { + if (!context.map().withinEditableZoom()) return; + var target = d3_select(d3_event.target); var datum = target.datum(); @@ -398,6 +401,13 @@ export function modeSelect(context, selectedIDs) { .classed('related', true); } + // Don't highlight selected features past the editable zoom + if (!context.map().withinEditableZoom()) { + surface.selectAll('.selected').classed('selected', false); + surface.selectAll('.selected-member').classed('selected-member', false); + return; + } + var selection = context.surface() .selectAll(utilEntityOrMemberSelector(selectedIDs, context.graph())); diff --git a/modules/renderer/map.js b/modules/renderer/map.js index 98918c973..a5eea4cf1 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -11,11 +11,11 @@ import { geoExtent, geoRawMercator, geoScaleToZoom, geoZoomToScale } from '../ge import { modeBrowse } from '../modes/browse'; import { svgAreas, svgLabels, svgLayers, svgLines, svgMidpoints, svgPoints, svgVertices } from '../svg'; import { uiFlash } from '../ui/flash'; -import { utilFastMouse, utilFunctor, utilRebind, utilSetTransform } from '../util'; +import { utilFastMouse, utilFunctor, utilSetTransform, utilEntityAndDeepMemberIDs } from '../util/util'; import { utilBindOnce } from '../util/bind_once'; import { utilDetect } from '../util/detect'; import { utilGetDimensions } from '../util/dimensions'; - +import { utilRebind } from '../util/rebind'; // constants var TILESIZE = 256; @@ -30,7 +30,7 @@ function clamp(num, min, max) { export function rendererMap(context) { - var dispatch = d3_dispatch('move', 'drawn'); + var dispatch = d3_dispatch('move', 'drawn', 'crossEditableZoom'); var projection = context.projection; var curtainProjection = context.curtainProjection; var drawLayers = svgLayers(projection, context); @@ -56,6 +56,7 @@ export function rendererMap(context) { var _minzoom = 0; var _getMouseCoords; var _mouseEvent; + var _lastWithinEditableZoom; var zoom = d3_zoom() .scaleExtent([kMin, kMax]) @@ -182,7 +183,7 @@ export function rendererMap(context) { }); context.on('enter.map', function() { - if (map.editableDataEnabled() && !_isTransformed) { + if (map.editableDataEnabled(true /* skip zoom check */) && !_isTransformed) { // redraw immediately any objects affected by a change in selectedIDs. var graph = context.graph(); var selectedAndParents = {}; @@ -268,7 +269,16 @@ export function rendererMap(context) { var set; var filter; - if (difference) { + if (map.isInWideSelection()) { + data = []; + utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function(id) { + var entity = context.hasEntity(id); + if (entity) data.push(entity); + }); + fullRedraw = true; + filter = utilFunctor(true); + + } else if (difference) { var complete = difference.complete(map.extent()); data = Object.values(complete).filter(Boolean); set = new Set(Object.keys(complete)); @@ -492,6 +502,15 @@ export function rendererMap(context) { } + var withinEditableZoom = map.withinEditableZoom(); + if (_lastWithinEditableZoom !== withinEditableZoom) { + if (_lastWithinEditableZoom !== undefined) { + // notify that the map zoomed in or out over the editable zoom threshold + dispatch.call('crossEditableZoom', this, map); + } + _lastWithinEditableZoom = withinEditableZoom; + } + if (geoScaleToZoom(k, TILESIZE) < _minzoom) { surface.interrupt(); uiFlash().text(t('cannot_zoom'))(); @@ -580,7 +599,7 @@ export function rendererMap(context) { } // OSM - if (map.editableDataEnabled()) { + if (map.editableDataEnabled() || map.isInWideSelection()) { context.loadTiles(projection); drawEditable(difference, extent); } else { @@ -802,7 +821,7 @@ export function rendererMap(context) { var extent = entity.extent(context.graph()); if (!isFinite(extent.area())) return map; - var z2 = clamp(map.trimmedExtentZoom(extent), context.minEditableZoom(), 20); + var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20); return map.centerZoom(extent.center(), z2); }; @@ -839,7 +858,7 @@ export function rendererMap(context) { var extent = entity.extent(context.graph()); if (!isFinite(extent.area())) return map; - var z2 = clamp(map.trimmedExtentZoom(extent), context.minEditableZoom(), 20); + var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20); return map.centerZoomEase(extent.center(), z2, duration); }; @@ -915,12 +934,22 @@ export function rendererMap(context) { }; - map.editableDataEnabled = function() { + map.withinEditableZoom = function() { + return map.zoom() >= context.minEditableZoom(); + }; + + + map.isInWideSelection = function() { + return !map.withinEditableZoom() && context.mode() && context.mode().id === 'select'; + }; + + + map.editableDataEnabled = function(skipZoomCheck) { var layer = context.layers().layer('osm'); if (!layer || !layer.enabled()) return false; - return map.zoom() >= context.minEditableZoom(); + return skipZoomCheck || map.withinEditableZoom(); }; @@ -928,7 +957,7 @@ export function rendererMap(context) { var layer = context.layers().layer('notes'); if (!layer || !layer.enabled()) return false; - return map.zoom() >= context.minEditableZoom(); + return map.withinEditableZoom(); }; diff --git a/modules/svg/midpoints.js b/modules/svg/midpoints.js index 278203883..891542609 100644 --- a/modules/svg/midpoints.js +++ b/modules/svg/midpoints.js @@ -48,7 +48,7 @@ export function svgMidpoints(projection, context) { var touchLayer = selection.selectAll('.layer-touch.points'); var mode = context.mode(); - if (mode && mode.id !== 'select') { + if ((mode && mode.id !== 'select') || !context.map().withinEditableZoom()) { drawLayer.selectAll('.midpoint').remove(); touchLayer.selectAll('.midpoint.target').remove(); return; diff --git a/modules/svg/vertices.js b/modules/svg/vertices.js index 4dc14a45f..1ef0debf2 100644 --- a/modules/svg/vertices.js +++ b/modules/svg/vertices.js @@ -426,7 +426,22 @@ export function svgVertices(projection, context) { var zoom = geoScaleToZoom(projection.scale()); _prevSelected = _currSelected || {}; - _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom); + if (context.map().isInWideSelection()) { + _currSelected = {}; + context.selectedIDs().forEach(function(id) { + var entity = graph.hasEntity(id); + if (!entity) return; + + if (entity.type === 'node') { + if (renderAsVertex(entity, graph, wireframe, zoom)) { + _currSelected[entity.id] = entity; + } + } + }); + + } else { + _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom); + } // note that drawVertices will add `_currSelected` automatically if needed.. var filter = function(d) { return d.id in _prevSelected; }; diff --git a/modules/ui/tools/undo_redo.js b/modules/ui/tools/undo_redo.js index 7e1b216da..f0328c629 100644 --- a/modules/ui/tools/undo_redo.js +++ b/modules/ui/tools/undo_redo.js @@ -33,7 +33,7 @@ export function uiToolUndoRedo(context) { function editable() { - return context.editable(); + return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true /* ignore min zoom */); } diff --git a/modules/util/util.js b/modules/util/util.js index a5bfc2534..e37a165cc 100644 --- a/modules/util/util.js +++ b/modules/util/util.js @@ -72,9 +72,17 @@ export function utilEntityOrMemberSelector(ids, graph) { // - entityIDs passed in // - deep descendant entityIDs for any of those entities that are relations export function utilEntityOrDeepMemberSelector(ids, graph) { + return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph)); +} + + +// returns an selector to select entity ids for: +// - entityIDs passed in +// - deep descendant entityIDs for any of those entities that are relations +export function utilEntityAndDeepMemberIDs(ids, graph) { var seen = new Set(); ids.forEach(collectDeepDescendants); - return utilEntitySelector(Array.from(seen)); + return Array.from(seen); function collectDeepDescendants(id) { if (seen.has(id)) return;