diff --git a/js/id/core/entity.js b/js/id/core/entity.js index 53a7ca17f..71471b0ce 100644 --- a/js/id/core/entity.js +++ b/js/id/core/entity.js @@ -97,6 +97,12 @@ iD.Entity.prototype = { return this.extent(resolver).intersects(extent); }, + features: function(feat, resolver) { + return resolver.transient(this, 'features', function() { + return feat.match(this, resolver); + }); + }, + isUsed: function(resolver) { return _.without(Object.keys(this.tags), 'area').length > 0 || resolver.parentRelations(this).length > 0; diff --git a/js/id/core/graph.js b/js/id/core/graph.js index 8455d5096..d206088aa 100644 --- a/js/id/core/graph.js +++ b/js/id/core/graph.js @@ -50,7 +50,16 @@ iD.Graph.prototype = { }, parentWays: function(entity) { - return _.map(this._parentWays[entity.id], this.entity, this); + var parents = this._parentWays[entity.id], + result = []; + + if (parents) { + for (var i = 0, imax = parents.length; i !== imax; i++) { + result.push(this.entity(parents[i])); + } + } + return result; + // return _.map(this._parentWays[entity.id], this.entity, this); }, isPoi: function(entity) { @@ -64,7 +73,16 @@ iD.Graph.prototype = { }, parentRelations: function(entity) { - return _.map(this._parentRels[entity.id], this.entity, this); + var parents = this._parentRels[entity.id], + result = []; + + if (parents) { + for (var i = 0, imax = parents.length; i !== imax; i++) { + result.push(this.entity(parents[i])); + } + } + return result; + // return _.map(this._parentRels[entity.id], this.entity, this); }, childNodes: function(entity) { diff --git a/js/id/modes/drag_node.js b/js/id/modes/drag_node.js index aa0176288..3388eaa3c 100644 --- a/js/id/modes/drag_node.js +++ b/js/id/modes/drag_node.js @@ -48,7 +48,9 @@ iD.modes.DragNode = function(context) { } function start(entity) { - cancelled = d3.event.sourceEvent.shiftKey || context.features().hasHiddenConnections(entity); + cancelled = d3.event.sourceEvent.shiftKey || + context.features().hasHiddenConnections(entity, context.graph()); + if (cancelled) return behavior.cancel(); wasMidpoint = entity.type === 'midpoint'; diff --git a/js/id/operations/continue.js b/js/id/operations/continue.js index bc1f7ba9a..642458721 100644 --- a/js/id/operations/continue.js +++ b/js/id/operations/continue.js @@ -24,7 +24,7 @@ iD.operations.Continue = function(selectedIDs, context) { operation.available = function() { return geometries.vertex.length === 1 && geometries.line.length <= 1 && - !context.features().hasHiddenConnections(vertex); + !context.features().hasHiddenConnections(vertex, context.graph()); }; operation.disabled = function() { diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 30da65e89..7041c35ed 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -42,16 +42,19 @@ iD.Features = function(context) { }; var dispatch = d3.dispatch('change', 'redraw'), - resolver = context.graph(), feature = {}, - cullFactor = 1; + cullFactor = 1, + _keys = [], + _hidden = []; function update() { + _hidden = features.hidden(); dispatch.change(); dispatch.redraw(); } function defineFeature(k, filter, max) { + _keys.push(k); feature[k] = { filter: filter, enabled: true, // whether the user wants it enabled.. @@ -66,23 +69,23 @@ iD.Features = function(context) { } - defineFeature('points', function(entity) { + defineFeature('points', function(entity, resolver) { return entity.geometry(resolver) === 'point'; }, 200); - defineFeature('major_roads', function(entity) { + defineFeature('major_roads', function(entity, resolver) { return entity.geometry(resolver) === 'line' && major_roads[entity.tags.highway]; }); - defineFeature('minor_roads', function(entity) { + defineFeature('minor_roads', function(entity, resolver) { return entity.geometry(resolver) === 'line' && minor_roads[entity.tags.highway]; }); - defineFeature('paths', function(entity) { + defineFeature('paths', function(entity, resolver) { return entity.geometry(resolver) === 'line' && paths[entity.tags.highway]; }); - defineFeature('buildings', function(entity) { + defineFeature('buildings', function(entity, resolver) { return ( entity.geometry(resolver) === 'area' && ( (!!entity.tags.building && entity.tags.building !== 'no') || @@ -95,10 +98,10 @@ iD.Features = function(context) { ) || !!entity.tags['building:part']; }, 250); - defineFeature('landuse', function(entity) { + defineFeature('landuse', function(entity, resolver) { return entity.geometry(resolver) === 'area' && - !feature.buildings.filter(entity) && - !feature.water.filter(entity); + !feature.buildings.filter(entity, resolver) && + !feature.water.filter(entity, resolver); }); defineFeature('boundaries', function(entity) { @@ -118,11 +121,11 @@ iD.Features = function(context) { ); }); - defineFeature('rail', function(entity) { + defineFeature('rail', function(entity, resolver) { return !( - feature.major_roads.filter(entity) || - feature.minor_roads.filter(entity) || - feature.paths.filter(entity) + feature.major_roads.filter(entity, resolver) || + feature.minor_roads.filter(entity, resolver) || + feature.paths.filter(entity, resolver) ) && ( !!entity.tags.railway || entity.tags.landuse === 'railway' @@ -134,57 +137,55 @@ iD.Features = function(context) { }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc.. - defineFeature('past_future', function(entity) { + defineFeature('past_future', function(entity, resolver) { var strings = _.flatten(_.pairs(entity.tags)); return !( - feature.major_roads.filter(entity) || - feature.minor_roads.filter(entity) || - feature.paths.filter(entity) + feature.major_roads.filter(entity, resolver) || + feature.minor_roads.filter(entity, resolver) || + feature.paths.filter(entity, resolver) ) && _.any(strings, function(s) { return past_futures[s]; }); }); // lines or areas that don't match another feature filter. - defineFeature('others', function(entity) { - return ( - entity.geometry(resolver) === 'line' || - entity.geometry(resolver) === 'area' - ) && - _.reduce(_.omit(feature, 'others'), function(result, v) { - return result && !v.filter(entity); - }, true); + defineFeature('others', function(entity, resolver) { + var geom = entity.geometry(resolver); + return (geom === 'line' || geom === 'area') && + _.reduce(_.omit(feature, 'others'), function(result, v) { + return result && !v.filter(entity, resolver); + }, true); }); function features() {} features.keys = function() { - return _.keys(feature); + return _keys; }; features.enabled = function(k) { if (!arguments.length) { - return _.filter(features.keys(), function(k) { return feature[k].enabled; }); + return _.filter(_keys, function(k) { return feature[k].enabled; }); } return feature[k] && feature[k].enabled; }; features.disabled = function(k) { if (!arguments.length) { - return _.reject(features.keys(), function(k) { return feature[k].enabled; }); + return _.reject(_keys, function(k) { return feature[k].enabled; }); } return feature[k] && !feature[k].enabled; }; features.hidden = function(k) { if (!arguments.length) { - return _.filter(features.keys(), function(k) { return feature[k].hidden(); }); + return _.filter(_keys, function(k) { return feature[k].hidden(); }); } return feature[k] && feature[k].hidden(); }; features.autoHidden = function(k) { if (!arguments.length) { - return _.filter(features.keys(), function(k) { return feature[k].autoHidden(); }); + return _.filter(_keys, function(k) { return feature[k].autoHidden(); }); } return feature[k] && feature[k].autoHidden(); }; @@ -210,12 +211,9 @@ iD.Features = function(context) { } }; - features.gatherStats = function(d, graph, dimensions) { - var hidden = features.hidden(), - keys = features.keys(), - needsRedraw = false; - - resolver = graph || resolver; + features.gatherStats = function(d, resolver, dimensions) { + var needsRedraw = false, + currHidden; _.each(feature, function(f) { f.count = 0; }); @@ -223,15 +221,25 @@ iD.Features = function(context) { // a cullFactor of 1 corresponds to a 1000x1000px viewport.. cullFactor = dimensions[0] * dimensions[1] / 1000000; - _.each(d, function(entity) { - _.each(keys, function(k) { - if (feature[k].filter(entity)) { - feature[k].count++; - } - }); - }); + for (var i = 0, imax = d.length; i !== imax; i++) { + var feats = d[i].features(this, resolver); + for (var j = 0, jmax = feats.length; j !== jmax; j++) { + feature[feats[j]].count++; + } + } - if (hidden !== features.hidden()) { + // _.each(d, function(entity) { + // _.each(entity.features(this, resolver), function(k) { feature[k].count++; }); + // // _.each(_keys, function(k) { + // // if (feature[k].filter(entity)) { + // // feature[k].count++; + // // } + // // }); + // }); + + currHidden = features.hidden(); + if (currHidden !== _hidden) { + _hidden = currHidden; dispatch.change(); needsRedraw = true; } @@ -241,30 +249,57 @@ iD.Features = function(context) { features.stats = function() { var stats = {}; - _.each(features.keys(), function(k) { + _.each(_keys, function(k) { stats[k] = feature[k].count; }); return stats; }; - features.isHiddenFeature = function(entity, graph) { - resolver = graph || resolver; - return _.any(features.hidden(), function(k) { return feature[k].filter(entity); }); + features.match = function(entity, resolver) { + var result = []; + + for (var i = 0, imax = _keys.length; i !== imax; i++) { + if (feature[_keys[i]].filter(entity, resolver)) { result.push(_keys[i]); } + } + return result; + // return _.filter(_keys, function(k) { return feature[k].filter(entity, resolver); }); }; - features.isHiddenChild = function(entity, graph) { - var parents; - resolver = graph || resolver; + features.isHiddenFeature = function(entity, resolver) { + var feats = entity.features(this, resolver); - parents = _.union(resolver.parentWays(entity), resolver.parentRelations(entity)); - return parents.length ? _.all(parents, function(e) { - return features.isHidden(e, resolver); - }) : false; + for (var i = 0, imax = _hidden.length; i !== imax; i++) { + for (var j = 0, jmax = feats.length; j !== jmax; j++) { + if (_hidden[i] === feats[j]) { return true; } + } + } + return false; + // return _.any(features.hidden(), function(k) { return feature[k].filter(entity, resolver); }); }; - features.hasHiddenConnections = function(entity, graph) { + features.isHiddenChild = function(entity, resolver) { + var parents = []; + parents.push.apply(parents, resolver.parentWays(entity)); + parents.push.apply(parents, resolver.parentRelations(entity)); + + if (!parents.length) { + return false; + } + for (var i = 0, imax = parents.length; i !== imax; i++) { + if (!features.isHidden(parents[i], resolver)) { + return false; + } + } + return true; + + // var parents = _.union(resolver.parentWays(entity), resolver.parentRelations(entity)); + // return parents.length ? _.all(parents, function(e) { + // return features.isHidden(e, resolver); + // }) : false; + }; + + features.hasHiddenConnections = function(entity, resolver) { var childNodes, connections; - resolver = graph || resolver; if (entity.type === 'midpoint') { childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])]; @@ -284,23 +319,42 @@ iD.Features = function(context) { }) : false; }; - features.isHidden = function(entity, graph) { - resolver = graph || resolver; + features.isHidden = function(entity, resolver) { return !!entity.version && (features.isHiddenFeature(entity, resolver) || features.isHiddenChild(entity, resolver)); }; - features.filter = function(d, graph) { - var selected = context.selectedIDs(); - resolver = graph || resolver; + features.filter = function(d, resolver) { + var selected = context.selectedIDs(), + result = []; - return features.hidden().length ? _.reject(d, function(e) { - var hidden = features.isHidden(e, resolver); - if (hidden && _.contains(selected, e.id)) { - context.enter(iD.modes.Browse(context)); + if (!_hidden.length) { + return d; + } else { + for (var i = 0, imax = d.length; i !== imax; i++) { + if (features.isHidden(d[i], resolver)) { + for (var j = 0, jmax = selected.length; j !== jmax; j++) { + if (selected[j] === d[i].id) { + context.enter(iD.modes.Browse(context)); + break; + } + } + } else { + result.push(d[i]); + } } - return hidden; - }) : d; + return result; + } + + // return features.hidden().length ? _.reject(d, function(e) { + // var isHidden = features.isHidden(e, resolver); + // if (isHidden && selected.length) { + // if _.contains(selected, e.id)) { + // context.enter(iD.modes.Browse(context)); + // } + // } + // return isHidden; + // }) : d; }; return d3.rebind(features, dispatch, 'on'); diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 15c22a5a6..016b896ce 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -80,7 +80,7 @@ iD.Map = function(context) { filter = d3.functor(true), graph = context.graph(); - all = context.features().filter(all); + all = context.features().filter(all, graph); surface.call(vertices, graph, all, filter, map.extent(), map.zoom()); surface.call(midpoints, graph, all, filter, map.trimmedExtent()); dispatch.drawn({full: false}); diff --git a/js/id/svg/vertices.js b/js/id/svg/vertices.js index bc76ac615..d70d751a8 100644 --- a/js/id/svg/vertices.js +++ b/js/id/svg/vertices.js @@ -12,7 +12,7 @@ iD.svg.Vertices = function(projection, context) { var vertices = {}; function addChildVertices(entity) { - if (!context.features().isHiddenFeature(entity)) { + if (!context.features().isHiddenFeature(entity, graph)) { var i; if (entity.type === 'way') { for (i = 0; i < entity.nodes.length; i++) {