diff --git a/modules/modes/select_error.js b/modules/modes/select_error.js index de12b4dd1..3350fcf2a 100644 --- a/modules/modes/select_error.js +++ b/modules/modes/select_error.js @@ -148,8 +148,9 @@ export function modeSelectError(context, selectedErrorID, selectedErrorService) .hide(); context.selectedErrorID(null); + context.features().forceVisible([]); }; return mode; -} \ No newline at end of file +} diff --git a/modules/renderer/features.js b/modules/renderer/features.js index e8cb1008d..4894794a0 100644 --- a/modules/renderer/features.js +++ b/modules/renderer/features.js @@ -8,10 +8,7 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; import { osmEntity } from '../osm'; import { utilRebind } from '../util/rebind'; -import { - utilQsString, - utilStringQs -} from '../util'; +import { utilQsString, utilStringQs } from '../util'; export function rendererFeatures(context) { @@ -58,13 +55,14 @@ export function rendererFeatures(context) { 'obliterated': true }; - var dispatch = d3_dispatch('change', 'redraw'), - _cullFactor = 1, - _cache = {}, - _features = {}, - _stats = {}, - _keys = [], - _hidden = []; + var dispatch = d3_dispatch('change', 'redraw'); + var _cullFactor = 1; + var _cache = {}; + var _features = {}; + var _stats = {}; + var _keys = []; + var _hidden = []; + var _forceVisible = {}; function update() { @@ -277,10 +275,10 @@ export function rendererFeatures(context) { features.gatherStats = function(d, resolver, dimensions) { - var needsRedraw = false, - type = _groupBy(d, function(ent) { return ent.type; }), - entities = [].concat(type.relation || [], type.way || [], type.node || []), - currHidden, geometry, matches, i, j; + var needsRedraw = false; + var type = _groupBy(d, function(ent) { return ent.type; }); + var entities = [].concat(type.relation || [], type.way || [], type.node || []); + var currHidden, geometry, matches, i, j; for (i = 0; i < _keys.length; i++) { _features[_keys[i]].count = 0; @@ -346,8 +344,8 @@ export function rendererFeatures(context) { } if (!_cache[ent].matches) { - var matches = {}, - hasMatch = false; + var matches = {}; + var hasMatch = false; for (var i = 0; i < _keys.length; i++) { if (_keys[i] === 'others') { @@ -462,6 +460,7 @@ export function rendererFeatures(context) { features.isHidden = function(entity, resolver, geometry) { if (!_hidden.length) return false; if (!entity.version) return false; + if (_forceVisible[entity.id]) return false; var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature); return fn(entity, resolver, geometry); @@ -482,6 +481,16 @@ export function rendererFeatures(context) { }; + features.forceVisible = function(entityIDs) { + if (!arguments.length) return Object.keys(_forceVisible); + _forceVisible = {}; + for (var i = 0; i < entityIDs.length; i++) { + _forceVisible[entityIDs[i]] = true; + } + return features; + }; + + features.init = function() { var storage = context.storage('disabled-features'); if (storage) { diff --git a/modules/ui/improveOSM_details.js b/modules/ui/improveOSM_details.js index 9f016e4df..e1bc288ab 100644 --- a/modules/ui/improveOSM_details.js +++ b/modules/ui/improveOSM_details.js @@ -64,6 +64,7 @@ export function uiImproveOsmDetails(context) { .html(errorDetail); // If there are entity links in the error message.. + var relatedEntities = []; descriptionEnter.selectAll('.error_entity_link, .error_object_link') .each(function() { var link = d3_select(this); @@ -73,6 +74,8 @@ export function uiImproveOsmDetails(context) { : this.textContent; var entity = context.hasEntity(entityID); + relatedEntities.push(entityID); + // Add click handler link .on('mouseover', function() { @@ -116,6 +119,9 @@ export function uiImproveOsmDetails(context) { } } }); + + // Don't hide entities related to this error - #5880 + context.features().forceVisible(relatedEntities); } @@ -127,4 +133,4 @@ export function uiImproveOsmDetails(context) { return improveOsmDetails; -} \ No newline at end of file +} diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 96b6536b6..e28d620a3 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -66,6 +66,7 @@ export function uiKeepRightDetails(context) { .html(errorDetail); // If there are entity links in the error message.. + var relatedEntities = []; descriptionEnter.selectAll('.error_entity_link, .error_object_link') .each(function() { var link = d3_select(this); @@ -75,6 +76,8 @@ export function uiKeepRightDetails(context) { : this.textContent; var entity = context.hasEntity(entityID); + relatedEntities.push(entityID); + // Add click handler link .on('mouseover', function() { @@ -118,6 +121,9 @@ export function uiKeepRightDetails(context) { } } }); + + // Don't hide entities related to this error - #5880 + context.features().forceVisible(relatedEntities); } diff --git a/test/spec/renderer/features.js b/test/spec/renderer/features.js index f8488d32a..234a60e71 100644 --- a/test/spec/renderer/features.js +++ b/test/spec/renderer/features.js @@ -1,6 +1,6 @@ describe('iD.Features', function() { - var dimensions = [1000, 1000], - context, features; + var dimensions = [1000, 1000]; + var context, features; function _values(obj) { var result = []; @@ -64,18 +64,18 @@ describe('iD.Features', function() { describe('#gatherStats', function() { it('counts features', function() { var graph = iD.coreGraph([ - iD.osmNode({id: 'point_bar', tags: {amenity: 'bar'}, version: 1}), - iD.osmNode({id: 'point_dock', tags: {waterway: 'dock'}, version: 1}), - iD.osmNode({id: 'point_rail_station', tags: {railway: 'station'}, version: 1}), - iD.osmNode({id: 'point_generator', tags: {power: 'generator'}, version: 1}), - iD.osmNode({id: 'point_old_rail_station', tags: {railway: 'station', disused: 'yes'}, version: 1}), - iD.osmWay({id: 'motorway', tags: {highway: 'motorway'}, version: 1}), - iD.osmWay({id: 'building_yes', tags: {area: 'yes', amenity: 'school', building: 'yes'}, version: 1}), - iD.osmWay({id: 'boundary', tags: {boundary: 'administrative'}, version: 1}), - iD.osmWay({id: 'fence', tags: {barrier: 'fence'}, version: 1}) - ]), - all = _values(graph.base().entities), - stats; + iD.osmNode({id: 'point_bar', tags: {amenity: 'bar'}, version: 1}), + iD.osmNode({id: 'point_dock', tags: {waterway: 'dock'}, version: 1}), + iD.osmNode({id: 'point_rail_station', tags: {railway: 'station'}, version: 1}), + iD.osmNode({id: 'point_generator', tags: {power: 'generator'}, version: 1}), + iD.osmNode({id: 'point_old_rail_station', tags: {railway: 'station', disused: 'yes'}, version: 1}), + iD.osmWay({id: 'motorway', tags: {highway: 'motorway'}, version: 1}), + iD.osmWay({id: 'building_yes', tags: {area: 'yes', amenity: 'school', building: 'yes'}, version: 1}), + iD.osmWay({id: 'boundary', tags: {boundary: 'administrative'}, version: 1}), + iD.osmWay({id: 'fence', tags: {barrier: 'fence'}, version: 1}) + ]); + var all = _values(graph.base().entities); + var stats; features.gatherStats(all, graph, dimensions); stats = features.stats(); @@ -205,8 +205,8 @@ describe('iD.Features', function() { version: 1 }) - ]), - all = _values(graph.base().entities); + ]); + var all = _values(graph.base().entities); function doMatch(ids) { @@ -435,12 +435,12 @@ describe('iD.Features', function() { describe('hiding', function() { it('hides child vertices on a hidden way', function() { - var a = iD.osmNode({id: 'a', version: 1}), - b = iD.osmNode({id: 'b', version: 1}), - w = iD.osmWay({id: 'w', nodes: [a.id, b.id], tags: {highway: 'path'}, version: 1}), - graph = iD.coreGraph([a, b, w]), - geometry = a.geometry(graph), - all = _values(graph.base().entities); + var a = iD.osmNode({id: 'a', version: 1}); + var b = iD.osmNode({id: 'b', version: 1}); + var w = iD.osmWay({id: 'w', nodes: [a.id, b.id], tags: {highway: 'path'}, version: 1}); + var graph = iD.coreGraph([a, b, w]); + var geometry = a.geometry(graph); + var all = _values(graph.base().entities); features.disable('paths'); features.gatherStats(all, graph, dimensions); @@ -452,23 +452,23 @@ describe('iD.Features', function() { }); it('hides uninteresting (e.g. untagged or "other") member ways on a hidden multipolygon relation', function() { - var outer = iD.osmWay({id: 'outer', tags: {area: 'yes', natural: 'wood'}, version: 1}), - inner1 = iD.osmWay({id: 'inner1', tags: {barrier: 'fence'}, version: 1}), - inner2 = iD.osmWay({id: 'inner2', version: 1}), - inner3 = iD.osmWay({id: 'inner3', tags: {highway: 'residential'}, version: 1}), - r = iD.osmRelation({ - id: 'r', - tags: {type: 'multipolygon'}, - members: [ - {id: outer.id, role: 'outer', type: 'way'}, - {id: inner1.id, role: 'inner', type: 'way'}, - {id: inner2.id, role: 'inner', type: 'way'}, - {id: inner3.id, role: 'inner', type: 'way'} - ], - version: 1 - }), - graph = iD.coreGraph([outer, inner1, inner2, inner3, r]), - all = _values(graph.base().entities); + var outer = iD.osmWay({id: 'outer', tags: {area: 'yes', natural: 'wood'}, version: 1}); + var inner1 = iD.osmWay({id: 'inner1', tags: {barrier: 'fence'}, version: 1}); + var inner2 = iD.osmWay({id: 'inner2', version: 1}); + var inner3 = iD.osmWay({id: 'inner3', tags: {highway: 'residential'}, version: 1}); + var r = iD.osmRelation({ + id: 'r', + tags: {type: 'multipolygon'}, + members: [ + {id: outer.id, role: 'outer', type: 'way'}, + {id: inner1.id, role: 'inner', type: 'way'}, + {id: inner2.id, role: 'inner', type: 'way'}, + {id: inner3.id, role: 'inner', type: 'way'} + ], + version: 1 + }); + var graph = iD.coreGraph([outer, inner1, inner2, inner3, r]); + var all = _values(graph.base().entities); features.disable('landuse'); features.gatherStats(all, graph, dimensions); @@ -480,12 +480,12 @@ describe('iD.Features', function() { }); it('hides only versioned entities', function() { - var a = iD.osmNode({id: 'a', version: 1}), - b = iD.osmNode({id: 'b'}), - graph = iD.coreGraph([a, b]), - ageo = a.geometry(graph), - bgeo = b.geometry(graph), - all = _values(graph.base().entities); + var a = iD.osmNode({id: 'a', version: 1}); + var b = iD.osmNode({id: 'b'}); + var graph = iD.coreGraph([a, b]); + var ageo = a.geometry(graph); + var bgeo = b.geometry(graph); + var all = _values(graph.base().entities); features.disable('points'); features.gatherStats(all, graph, dimensions); @@ -494,10 +494,23 @@ describe('iD.Features', function() { expect(features.isHidden(b, graph, bgeo)).to.be.false; }); + it('#forceVisible', function() { + var a = iD.osmNode({id: 'a', version: 1}); + var graph = iD.coreGraph([a]); + var ageo = a.geometry(graph); + var all = _values(graph.base().entities); + + features.disable('points'); + features.gatherStats(all, graph, dimensions); + features.forceVisible(['a']); + + expect(features.isHidden(a, graph, ageo)).to.be.false; + }); + it('auto-hides features', function() { - var graph = iD.coreGraph([]), - maxPoints = 200, - all, hidden, autoHidden, i, msg; + var graph = iD.coreGraph([]); + var maxPoints = 200; + var all, hidden, autoHidden, i, msg; for (i = 0; i < maxPoints; i++) { graph.rebase([iD.osmNode({version: 1})], [graph]); @@ -525,10 +538,10 @@ describe('iD.Features', function() { }); it('doubles auto-hide threshold when doubling viewport size', function() { - var graph = iD.coreGraph([]), - maxPoints = 400, - dimensions = [2000, 1000], - all, hidden, autoHidden, i, msg; + var graph = iD.coreGraph([]); + var maxPoints = 400; + var dimensions = [2000, 1000]; + var all, hidden, autoHidden, i, msg; for (i = 0; i < maxPoints; i++) { graph.rebase([iD.osmNode({version: 1})], [graph]);