From 8fd177935d917b682265f7e97823106126260471 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 17 Aug 2014 22:16:45 -0400 Subject: [PATCH 01/41] Feature filtering (in progress) (rebased to master post-Mapillary merge) --- css/app.css | 76 +++++++---- data/core.yaml | 41 ++++++ dist/locales/en.json | 55 ++++++++ index.html | 3 + js/id/id.js | 4 + js/id/renderer/features.js | 265 +++++++++++++++++++++++++++++++++++++ js/id/renderer/map.js | 16 ++- js/id/svg/points.js | 22 +-- js/id/ui.js | 20 ++- js/id/ui/feature_info.js | 32 +++++ js/id/ui/features.js | 125 +++++++++++++++++ test/index.html | 3 + 12 files changed, 606 insertions(+), 56 deletions(-) create mode 100644 js/id/renderer/features.js create mode 100644 js/id/ui/feature_info.js create mode 100644 js/id/ui/features.js diff --git a/css/app.css b/css/app.css index cd55957d1..27cd5e96b 100644 --- a/css/app.css +++ b/css/app.css @@ -2224,12 +2224,49 @@ img.wiki-image { /* About Section ------------------------------------------------------- */ -#footer { +#about { width: 100%; position: absolute; right:0; bottom:0; border-radius: 0; +} + +#attrib { + width: 100%; + height: 20px; + float: left; + clear: both; +} + +.base-layer-attribution, +.overlay-layer-attribution { + position: absolute; + color: #888; + font-size: 10px; +} + +.base-layer-attribution { + left: 10px; +} + +.overlay-layer-attribution { + right: 10px; +} + +.overlay-layer-attribution .attribution:not(:last-child):after { + content: '; '; +} + +.source-image { + height:20px; + vertical-align:top; +} + +#footer { + width: 100%; + float: left; + clear: both; opacity: .625; -webkit-transition: opacity 200ms; -moz-transition: opacity 200ms; @@ -2288,48 +2325,33 @@ img.wiki-image { color:#fff; } -/* Attribution overlay */ -.base-layer-attribution, -.overlay-layer-attribution { - position: absolute; - bottom: 35px; - color: #888; - font-size: 10px; -} - -.base-layer-attribution { - left: 10px; -} - -.overlay-layer-attribution { - right: 10px; -} - -.overlay-layer-attribution .attribution:not(:last-child):after { - content: '; '; -} - -.source-image { - height:20px; - vertical-align:top; +@media screen and (max-width: 1200px) { + .user-list { + display: none !important; + } } .user-list a:not(:last-child):after { content: ', '; } -.api-status { +.api-status, +.feature-info { float: right; clear: both; text-align: right; width: 100%; + padding: 0px 5px; } .api-status.offline, .api-status.readonly, .api-status.error { background: red; - padding: 0px 5px; +} + +.feature-info { + background: #1e90ff; } /* Modals diff --git a/data/core.yaml b/data/core.yaml index 1cc2feab8..169ebaf1a 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -172,6 +172,8 @@ en: logout: logout loading_auth: "Connecting to OpenStreetMap..." report_a_bug: report a bug + feature_info: + hidden_features: "Hidden: {features}" status: error: Unable to connect to API. offline: The API is offline. Please try editing later. @@ -237,6 +239,45 @@ en: custom_prompt: "Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme." fix_misalignment: Fix alignment reset: reset + features: + title: Map Features + description: Map Features + points: + description: Points + tooltip: "Points of Interest" + major_roads: + description: Major Roads + tooltip: "Highways, Streets, etc." + minor_roads: + description: Minor Roads + tooltip: "Service Roads, Parking Aisles, Tracks, etc." + paths: + description: Paths + tooltip: "Sidewalks, Foot Paths, Cycle Paths, etc." + buildings: + description: Buildings + tooltip: "Buildings, Shelters, Garages, etc." + landuse: + description: Landuse Features + tooltip: "Forests, Farmland, Parks, Residential, Commercial, etc." + boundaries: + description: Boundaries + tooltip: "Administrative Boundaries" + water: + description: Water Features + tooltip: "Rivers, Lakes, Ponds, Basins, etc." + rail: + description: Rail Features + tooltip: "Railways" + power: + description: Power Features + tooltip: "Power Lines, Power Plants, Substations, etc." + past_future: + description: Past/Future + tooltip: "Proposed, Construction, Abandoned, Demolished, etc." + others: + description: Others + tooltip: "Everything Else" restore: heading: You have unsaved changes description: "Do you wish to restore unsaved changes from a previous editing session?" diff --git a/dist/locales/en.json b/dist/locales/en.json index feabccf91..d92e89242 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -218,6 +218,9 @@ "logout": "logout", "loading_auth": "Connecting to OpenStreetMap...", "report_a_bug": "report a bug", + "feature_info": { + "hidden_features": "Hidden: {features}" + }, "status": { "error": "Unable to connect to API.", "offline": "The API is offline. Please try editing later.", @@ -291,6 +294,58 @@ "fix_misalignment": "Fix alignment", "reset": "reset" }, + "features": { + "title": "Map Features", + "description": "Map Features", + "points": { + "description": "Points", + "tooltip": "Points of Interest" + }, + "major_roads": { + "description": "Major Roads", + "tooltip": "Highways, Streets, etc." + }, + "minor_roads": { + "description": "Minor Roads", + "tooltip": "Service Roads, Parking Aisles, Tracks, etc." + }, + "paths": { + "description": "Paths", + "tooltip": "Sidewalks, Foot Paths, Cycle Paths, etc." + }, + "buildings": { + "description": "Buildings", + "tooltip": "Buildings, Shelters, Garages, etc." + }, + "landuse": { + "description": "Landuse Features", + "tooltip": "Forests, Farmland, Parks, Residential, Commercial, etc." + }, + "boundaries": { + "description": "Boundaries", + "tooltip": "Administrative Boundaries" + }, + "water": { + "description": "Water Features", + "tooltip": "Rivers, Lakes, Ponds, Basins, etc." + }, + "rail": { + "description": "Rail Features", + "tooltip": "Railways" + }, + "power": { + "description": "Power Features", + "tooltip": "Power Lines, Power Plants, Substations, etc." + }, + "past_future": { + "description": "Past/Future", + "tooltip": "Proposed, Construction, Abandoned, Demolished, etc." + }, + "others": { + "description": "Others", + "tooltip": "Everything Else" + } + }, "restore": { "heading": "You have unsaved changes", "description": "Do you wish to restore unsaved changes from a previous editing session?", diff --git a/index.html b/index.html index cdff162bf..271cde234 100644 --- a/index.html +++ b/index.html @@ -55,6 +55,7 @@ + @@ -85,12 +86,14 @@ + + diff --git a/js/id/id.js b/js/id/id.js index 96d9c347d..5ea20ca86 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -203,6 +203,10 @@ window.iD = function () { var background = iD.Background(context); context.background = function() { return background; }; + /* Features */ + var features = iD.Features(context); + context.features = function() { return features; }; + /* Map */ var map = iD.Map(context); context.map = function() { return map; }; diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js new file mode 100644 index 000000000..12a3a9c11 --- /dev/null +++ b/js/id/renderer/features.js @@ -0,0 +1,265 @@ +iD.Features = function(context) { + var major_roads = { + 'motorway': true, + 'motorway_link': true, + 'trunk': true, + 'trunk_link': true, + 'primary': true, + 'primary_link': true, + 'secondary': true, + 'secondary_link': true, + 'tertiary': true, + 'tertiary_link': true, + 'residential': true + }; + + var minor_roads = { + 'service': true, + 'living_street': true, + 'road': true, + 'unclassified': true, + 'track': true + }; + + var paths = { + 'path': true, + 'footway': true, + 'cycleway': true, + 'bridleway': true, + 'steps': true, + 'pedestrian': true + }; + + var past_futures = { + 'proposed': true, + 'construction': true, + 'abandoned': true, + 'dismantled': true, + 'disused': true, + 'razed': true, + 'demolished': true, + 'obliterated': true + }; + + var graph = context.graph(), + dispatch = d3.dispatch('change', 'redraw'), + feature = {}; + + function defineFeature(k, filter, max) { + feature[k] = { + filter: filter, + enabled: true, // whether the user wants it enabled.. + count: 0, + currentMax: (max || Infinity), + defaultMax: (max || Infinity), + enable: function() { this.enabled = true; this.currentMax = this.defaultMax; }, + disable: function() { this.enabled = false; this.currentMax = 0; }, + hidden: function() { return this.count > this.currentMax; } + }; + } + + function update() { + dispatch.change(); + dispatch.redraw(); + } + + defineFeature('points', function(entity) { + return entity.geometry(graph) === 'point'; + }, 100); + + defineFeature('major_roads', function(entity) { + return entity.geometry(graph) === 'line' && major_roads[entity.tags.highway]; + }); + + defineFeature('minor_roads', function(entity) { + return entity.geometry(graph) === 'line' && minor_roads[entity.tags.highway]; + }); + + defineFeature('paths', function(entity) { + return entity.geometry(graph) === 'line' && paths[entity.tags.highway]; + }); + + defineFeature('buildings', function(entity) { + return ( + entity.geometry(graph) === 'area' && ( + (!!entity.tags.building && entity.tags.building !== 'no') || + entity.tags.amenity === 'shelter' || + entity.tags.parking === 'multi-storey' || + entity.tags.parking === 'sheds' || + entity.tags.parking === 'carports' || + entity.tags.parking === 'garage_boxes' + ) + ) || !!entity.tags['building:part']; + }, 100); + + defineFeature('landuse', function(entity) { + return entity.geometry(graph) === 'area' && + !feature.buildings.filter(entity) && + !feature.water.filter(entity); + }); + + defineFeature('boundaries', function(entity) { + return !!entity.tags.boundary; + }); + + defineFeature('water', function(entity) { + return ( + !!entity.tags.waterway || + entity.tags.natural === 'water' || + entity.tags.natural === 'coastline' || + entity.tags.natural === 'bay' || + entity.tags.landuse === 'pond' || + entity.tags.landuse === 'basin' || + entity.tags.landuse === 'reservoir' || + entity.tags.landuse === 'salt_pond' + ); + }); + + defineFeature('rail', function(entity) { + return ( + !!entity.tags.railway || + entity.tags.landuse === 'railway' + ); + }); + + defineFeature('power', function(entity) { + return !!entity.tags.power; + }); + + // contains a past/future tag, but not in active use as a road/path/cycleway/etc.. + defineFeature('past_future', function(entity) { + var strings = _.flatten(_.pairs(entity.tags)); + return _.any(strings, function(s) { return past_futures[s]; }) && + !feature.major_roads.filter(entity) && + !feature.minor_roads.filter(entity) && + !feature.paths.filter(entity); + }); + + // lines or areas that don't match another feature filter. + defineFeature('others', function(entity) { + return ( + entity.geometry(graph) === 'line' || + entity.geometry(graph) === 'area' + ) && + _.reduce(_.omit(feature, 'others'), function(result, v) { + return result && !v.filter(entity); + }, true); + }); + + + function features() {} + + features.keys = function() { + return _.keys(feature); + }; + + features.enabled = function(k) { + if (!arguments.length) { + return _.filter(features.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 feature[k] && !feature[k].enabled; + }; + + features.hidden = function(k) { + if (!arguments.length) { + return _.filter(features.keys(), function(k) { return feature[k].hidden(); }); + } + return feature[k] && feature[k].hidden(); + }; + + features.enable = function(k) { + if (feature[k] && !feature[k].enabled) { + feature[k].enable(); + update(); + } + }; + + features.disable = function(k) { + if (feature[k] && feature[k].enabled) { + feature[k].disable(); + update(); + } + }; + + features.toggle = function(k) { + if (feature[k]) { + (function(f) { return f.enabled ? f.disable() : f.enable(); }(feature[k])); + update(); + } + }; + + features.setAll = function(val) { + if (val !== undefined) { + _.each(feature, function(f) { return val ? f.enable() : f.disable(); }); + update(); + } + }; + + features.enableAll = function() { + features.setAll(true); + }; + + features.disableAll = function() { + features.setAll(false); + }; + + features.count = function(k) { + return feature[k] && feature[k].count; + }; + + features.resetStats = function() { + _.each(feature, function(f) { f.count = 0; }); + }; + + features.gatherStats = function(d) { + var hidden = features.hidden(), + keys = features.keys(); + + features.resetStats(); + _.each(d, function(entity) { + _.each(keys, function(k) { + if (feature[k].filter(entity)) { + feature[k].count++; + } + }); + }); + + if (hidden !== features.hidden()) { + dispatch.change(); + } + }; + + features.stats = function() { + var stats = {}; + _.each(features.keys(), function(k) { + stats[k] = feature[k].count; + }); + return stats; + }; + + features.isHidden = function(entity) { + var hidden = features.hidden(); + + function isHiddenFeature(entity) { + return _.any(hidden, function(k) { return feature[k].filter(entity); }); + } + function isHiddenChild(entity) { + var parents = _.union(graph.parentWays(entity), graph.parentRelations(entity)); + return parents.length ? _.all(parents, features.isHidden) : false; + } + return isHiddenFeature(entity) || isHiddenChild(entity); + }; + + features.filter = function(d) { + return features.hidden().length ? _.reject(d, features.isHidden) : d; + }; + + return d3.rebind(features, dispatch, 'on'); +}; diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 989c7188b..c33678418 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -27,6 +27,8 @@ iD.Map = function(context) { .on('change.map', redraw); context.background() .on('change.map', redraw); + context.features() + .on('redraw.map', redraw); selection.call(zoom); @@ -77,6 +79,8 @@ iD.Map = function(context) { var all = context.intersects(map.extent()), filter = d3.functor(true), graph = context.graph(); + + all = context.features().filter(all); surface.call(vertices, graph, all, filter, map.extent(), map.zoom()); surface.call(midpoints, graph, all, filter, map.trimmedExtent()); dispatch.drawn({full: false}); @@ -109,18 +113,16 @@ iD.Map = function(context) { filter = d3.functor(true); } + context.features().gatherStats(context.intersects(map.extent())); + all = context.features().filter(all); + surface .call(vertices, graph, all, filter, map.extent(), map.zoom()) .call(lines, graph, all, filter) .call(areas, graph, all, filter) .call(midpoints, graph, all, filter, map.trimmedExtent()) - .call(labels, graph, all, filter, dimensions, !difference && !extent); - - if (points.points(context.intersects(map.extent()), 100).length >= 100) { - surface.select('.layer-hit').selectAll('g.point').remove(); - } else { - surface.call(points, points.points(all), filter); - } + .call(labels, graph, all, filter, dimensions, !difference && !extent) + .call(points, all, filter); dispatch.drawn({full: true}); } diff --git a/js/id/svg/points.js b/js/id/svg/points.js index cd3de9d8a..33d563208 100644 --- a/js/id/svg/points.js +++ b/js/id/svg/points.js @@ -10,7 +10,10 @@ iD.svg.Points = function(projection, context) { return b.loc[1] - a.loc[1]; } - function drawPoints(surface, points, filter) { + return function drawPoints(surface, entities, filter) { + var graph = context.graph(), + points = _.filter(entities, function(e) { return e.geometry(graph) === 'point'; }); + points.sort(sortY); var groups = surface.select('.layer-hit').selectAll('g.point') @@ -48,22 +51,5 @@ iD.svg.Points = function(projection, context) { groups.exit() .remove(); - } - - drawPoints.points = function(entities, limit) { - var graph = context.graph(), - points = []; - - for (var i = 0; i < entities.length; i++) { - var entity = entities[i]; - if (entity.geometry(graph) === 'point') { - points.push(entity); - if (limit && points.length >= limit) break; - } - } - - return points; }; - - return drawPoints; }; diff --git a/js/id/ui.js b/js/id/ui.js index eacf19f8f..0e2425359 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -54,9 +54,6 @@ iD.ui = function(context) { .attr('class', 'spinner') .call(iD.ui.Spinner(context)); - content - .call(iD.ui.Attribution(context)); - content.append('div') .style('display', 'none') .attr('class', 'help-wrap map-overlay fillL col5 content'); @@ -76,11 +73,22 @@ iD.ui = function(context) { .attr('class', 'map-control background-control') .call(iD.ui.Background(context)); + controls.append('div') + .attr('class', 'map-control features-control') + .call(iD.ui.Features(context)); + controls.append('div') .attr('class', 'map-control help-control') .call(iD.ui.Help(context)); - var footer = content.append('div') + var about = content.append('div') + .attr('id', 'about'); + + about.append('div') + .attr('id', 'attrib') + .call(iD.ui.Attribution(context)); + + var footer = about.append('div') .attr('id', 'footer') .attr('class', 'fillD'); @@ -124,6 +132,10 @@ iD.ui = function(context) { .attr('tabindex', -1) .call(iD.ui.Contributors(context)); + footer.append('div') + .attr('class', 'feature-info') + .call(iD.ui.FeatureInfo(context)); + footer.append('div') .attr('class', 'api-status') .call(iD.ui.Status(context)); diff --git a/js/id/ui/feature_info.js b/js/id/ui/feature_info.js new file mode 100644 index 000000000..8ff0e5699 --- /dev/null +++ b/js/id/ui/feature_info.js @@ -0,0 +1,32 @@ +iD.ui.FeatureInfo = function(context) { + function update(selection) { + var features = context.features(), + hidden = features.hidden(); + + selection.html(''); + + if (hidden.length) { + var stats = features.stats(), + hiddenList = _.map(hidden, function(k) { + return String(stats[k]) + ' ' + t('features.' + k + '.description'); + }); + + selection.append('span') + .html(t('feature_info.hidden_features', { features: hiddenList.join(', ') })); + } + + if (!hidden.length) { + selection.transition().duration(200).style('opacity', 0); + } else if (selection.style('opacity') === '0') { + selection.transition().duration(200).style('opacity', 1); + } + } + + return function(selection) { + update(selection); + + context.features().on('change.feature_info', function() { + update(selection); + }); + }; +}; diff --git a/js/id/ui/features.js b/js/id/ui/features.js new file mode 100644 index 000000000..5a93a274f --- /dev/null +++ b/js/id/ui/features.js @@ -0,0 +1,125 @@ +iD.ui.Features = function(context) { + var key = 'f'; + + function features(selection) { + + function showsFeature(d) { + return context.features().enabled(d); + } + + function clickFeature(d) { + context.features().toggle(d); + } + + function drawList(selection) { + var data = context.features().keys(); + + var layerLinks = selection.selectAll('li.layer') + .data(data); + + var enter = layerLinks.enter() + .insert('li', '.custom_layer') + .attr('class', 'layer'); + + // only set tooltips for layers with tooltips + enter.filter(function(d) { return d; }) + .call(bootstrap.tooltip() + .title(function(d) { return t('features.' + d + '.tooltip'); }) + .placement('top')); + + var label = enter.append('label'); + + label.append('input') + .attr('type', 'checkbox') + .attr('name', 'layers') + .property('checked', showsFeature) + .on('change', clickFeature); + + label.append('span') + .text(function(d) { return t('features.' + d + '.description'); }); + + layerLinks.exit() + .remove(); + + selection.style('display', selection.selectAll('li.layer').data().length > 0 ? 'block' : 'none'); + } + + function update() { + featureList.call(drawList); + } + + var content = selection.append('div') + .attr('class', 'fillL map-overlay col3 content hide'), + tooltip = bootstrap.tooltip() + .placement('left') + .html(true) + .title(iD.ui.tooltipHtml(t('features.description'), key)); + + function hide() { setVisible(false); } + + function toggle() { + if (d3.event) d3.event.preventDefault(); + tooltip.hide(button); + setVisible(!button.classed('active')); + } + + function setVisible(show) { + if (show !== shown) { + button.classed('active', show); + shown = show; + + if (show) { + selection.on('mousedown.features-inside', function() { + return d3.event.stopPropagation(); + }); + content.style('display', 'block') + .style('right', '-300px') + .transition() + .duration(200) + .style('right', '0px'); + } else { + content.style('display', 'block') + .style('right', '0px') + .transition() + .duration(200) + .style('right', '-300px') + .each('end', function() { + d3.select(this).style('display', 'none'); + }); + selection.on('mousedown.features-inside', null); + } + } + } + + var button = selection.append('button') + .attr('tabindex', -1) + .on('click', toggle) + .call(tooltip), + shown = false; + + button.append('span') + .attr('class', 'icon layers light'); + + content.append('h4') + .text(t('features.title')); + + var featureList = content.append('ul') + .attr('class', 'layer-list'); + + context.features() + .on('change.features-update', update); + + update(); + + var keybinding = d3.keybinding('features'); + keybinding.on(key, toggle); + + d3.select(document) + .call(keybinding); + + context.surface().on('mousedown.features-outside', hide); + context.container().on('mousedown.features-outside', hide); + } + + return features; +}; diff --git a/test/index.html b/test/index.html index a3fdcba7e..8b702ccf6 100644 --- a/test/index.html +++ b/test/index.html @@ -52,6 +52,7 @@ + @@ -80,11 +81,13 @@ + + From 35e7474829d2714d048ee1c8d943d37c411b63f0 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Sep 2014 11:40:25 -0400 Subject: [PATCH 02/41] Rename Features to Map Data.. * move GPX and Mapillary layers to Map Data * put feature filters and layer data into show/hide containers * cleanup css --- css/app.css | 22 +++- data/core.yaml | 11 +- dist/locales/en.json | 12 +- index.html | 2 +- js/id/ui.js | 4 +- js/id/ui/background.js | 88 -------------- js/id/ui/feature_info.js | 2 +- js/id/ui/features.js | 125 ------------------- js/id/ui/map_data.js | 255 +++++++++++++++++++++++++++++++++++++++ test/index.html | 2 +- 10 files changed, 292 insertions(+), 231 deletions(-) delete mode 100644 js/id/ui/features.js create mode 100644 js/id/ui/map_data.js diff --git a/css/app.css b/css/app.css index 27cd5e96b..ae88d830a 100644 --- a/css/app.css +++ b/css/app.css @@ -1886,12 +1886,14 @@ img.wiki-image { border-radius: 4px 0 0 0; } -/* Background Settings */ +/* Background / Map Data Settings */ +.map-data-control button, .background-control button { border-radius: 4px 0 0 0; } +.map-data-control, .background-control { position: relative; } @@ -1904,7 +1906,8 @@ img.wiki-image { border: 0; } -.background-control .hide-toggle { +.map-data-control .hide-toggle, +.background-control .hide-toggle { padding-bottom: 10px; } @@ -2049,6 +2052,8 @@ img.wiki-image { z-index: 9999; } +.map-data-control li:hover .select-box, +.map-data-control li.selected .select-box, .background-control li:hover .select-box, .background-control li.selected .select-box { border: 2px solid #7092ff; @@ -2056,6 +2061,8 @@ img.wiki-image { opacity: .5; } +.map-data-control li.selected:hover .select-box, +.map-data-control li.selected .select-box, .background-control li.selected:hover .select-box, .background-control li.selected .select-box { opacity: 1; @@ -2068,6 +2075,7 @@ img.wiki-image { height:18px; } +.map-data-control .layer-list button, .background-control .layer-list button { float: right; height: 100%; @@ -2076,14 +2084,21 @@ img.wiki-image { border-radius: 0; } +.map-data-control .layer-list button .icon, .background-control .layer-list button .icon { opacity: 0.5; } +.map-data-control .layer-list button:first-of-type, .background-control .layer-list button:first-of-type { border-radius: 0 3px 3px 0; } +.map-data-control .map-overlay, +.background-control .map-overlay { + z-index: -1; +} + /* Geolocator */ .geolocate-control { @@ -2103,9 +2118,6 @@ img.wiki-image { overflow: auto; } -.background-control .map-overlay { - z-index: -1; -} /* Help */ .help-control button { diff --git a/data/core.yaml b/data/core.yaml index 169ebaf1a..5bd0c3703 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -239,9 +239,12 @@ en: custom_prompt: "Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme." fix_misalignment: Fix alignment reset: reset - features: - title: Map Features - description: Map Features + map_data: + title: Map Data + description: Map Data + show_features: Show Features + show_layers: Show Layers + feature: points: description: Points tooltip: "Points of Interest" @@ -460,7 +463,7 @@ en: To use a GPX track for mapping, drag and drop the GPX file onto the map editor. If it's recognized, it will be added to the map as a bright green - line. Click on the 'Background Settings' menu on the right side to enable, + line. Click on the 'Map Data' menu on the right side to enable, disable, or zoom to this new GPX-powered layer. The GPX track isn't directly uploaded to OpenStreetMap - the best way to diff --git a/dist/locales/en.json b/dist/locales/en.json index d92e89242..e6eaa4354 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -294,9 +294,13 @@ "fix_misalignment": "Fix alignment", "reset": "reset" }, - "features": { - "title": "Map Features", - "description": "Map Features", + "map_data": { + "title": "Map Data", + "description": "Map Data", + "show_features": "Show Features", + "show_layers": "Show Layers" + }, + "feature": { "points": { "description": "Points", "tooltip": "Points of Interest" @@ -421,7 +425,7 @@ "help": "# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need a\n[free OpenStreetMap account](https://www.openstreetmap.org/user/new).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n", "editing_saving": "# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n", "roads": "# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n", - "gps": "# GPS\n\nGPS data is the most trusted source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Surveying with a GPS](http://learnosm.org/en/beginner/using-gps/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright green\nline. Click on the 'Background Settings' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n", + "gps": "# GPS\n\nGPS data is the most trusted source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Surveying with a GPS](http://learnosm.org/en/beginner/using-gps/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright green\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n", "imagery": "# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n", "addresses": "# Addresses\n\nAddresses are some of the most useful information for the map.\n\nAlthough addresses are often represented as parts of streets, in OpenStreetMap\nthey're recorded as attributes of buildings and places along streets.\n\nYou can add address information to places mapped as building outlines\nas well as those mapped as single points. The optimal source of address\ndata is from an on-the-ground survey or personal knowledge - as with any\nother feature, copying from commercial sources like Google Maps is strictly\nforbidden.\n", "inspector": "# Using the Inspector\n\nThe inspector is the section on the left side of the page that allows you to\nedit the details of the selected feature.\n\n### Selecting a Feature Type\n\nAfter you add a point, line, or area, you can choose what type of feature it\nis, like whether it's a highway or residential road, supermarket or cafe.\nThe inspector will display buttons for common feature types, and you can\nfind others by typing what you're looking for in the search box.\n\nClick the 'i' in the bottom-right-hand corner of a feature type button to\nlearn more about it. Click a button to choose that type.\n\n### Using Forms and Editing Tags\n\nAfter you choose a feature type, or when you select a feature that already\nhas a type assigned, the inspector will display fields with details about\nthe feature like its name and address.\n\nBelow the fields you see, you can click icons to add other details,\nlike [Wikipedia](http://www.wikipedia.org/) information, wheelchair\naccess, and more.\n\nAt the bottom of the inspector, click 'Additional tags' to add arbitrary\nother tags to the element. [Taginfo](http://taginfo.openstreetmap.org/) is a\ngreat resource for learn more about popular tag combinations.\n\nChanges you make in the inspector are automatically applied to the map.\nYou can undo them at any time by clicking the 'Undo' button.\n", diff --git a/index.html b/index.html index 271cde234..0f0c10bd4 100644 --- a/index.html +++ b/index.html @@ -86,7 +86,7 @@ - + diff --git a/js/id/ui.js b/js/id/ui.js index 0e2425359..3e8652162 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -74,8 +74,8 @@ iD.ui = function(context) { .call(iD.ui.Background(context)); controls.append('div') - .attr('class', 'map-control features-control') - .call(iD.ui.Features(context)); + .attr('class', 'map-control map-data-control') + .call(iD.ui.MapData(context)); controls.append('div') .attr('class', 'map-control help-control') diff --git a/js/id/ui/background.js b/js/id/ui/background.js index 9e88acef7..623a664df 100644 --- a/js/id/ui/background.js +++ b/js/id/ui/background.js @@ -72,16 +72,6 @@ iD.ui.Background = function(context) { selectLayer(); } - function clickGpx() { - context.background().toggleGpxLayer(); - update(); - } - - function clickMapillary() { - context.background().toggleMapillaryLayer(); - update(); - } - function drawList(layerList, type, change, filter) { var sources = context.background() .sources(context.map().extent()) @@ -120,22 +110,6 @@ iD.ui.Background = function(context) { backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.overlay; }); overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return d.overlay; }); - var hasGpx = context.background().hasGpxLayer(), - showsGpx = context.background().showsGpxLayer(); - - gpxLayerItem - .classed('active', showsGpx) - .selectAll('input') - .property('disabled', !hasGpx) - .property('checked', showsGpx); - - var showsMapillary = context.background().showsMapillaryLayer(); - - mapillaryLayerItem - .classed('active', showsMapillary) - .selectAll('input') - .property('checked', showsMapillary); - selectLayer(); var source = context.background().baseLayerSource(); @@ -276,68 +250,6 @@ iD.ui.Background = function(context) { var overlayList = content.append('ul') .attr('class', 'layer-list'); - var mapillaryLayerItem = overlayList.append('li'); - - label = mapillaryLayerItem.append('label') - .call(bootstrap.tooltip() - .title(t('mapillary.tooltip')) - .placement('top')); - - label.append('input') - .attr('type', 'checkbox') - .on('change', clickMapillary); - - label.append('span') - .text(t('mapillary.title')); - - var gpxLayerItem = content.append('ul') - .style('display', iD.detect().filedrop ? 'block' : 'none') - .attr('class', 'layer-list') - .append('li') - .classed('layer-toggle-gpx', true); - - gpxLayerItem.append('button') - .attr('class', 'layer-extent') - .call(bootstrap.tooltip() - .title(t('gpx.zoom')) - .placement('left')) - .on('click', function() { - d3.event.preventDefault(); - d3.event.stopPropagation(); - context.background().zoomToGpxLayer(); - }) - .append('span') - .attr('class', 'icon geolocate'); - - gpxLayerItem.append('button') - .attr('class', 'layer-browse') - .call(bootstrap.tooltip() - .title(t('gpx.browse')) - .placement('left')) - .on('click', function() { - d3.select(document.createElement('input')) - .attr('type', 'file') - .on('change', function() { - context.background().gpxLayerFiles(d3.event.target.files); - }) - .node().click(); - }) - .append('span') - .attr('class', 'icon geocode'); - - label = gpxLayerItem.append('label') - .call(bootstrap.tooltip() - .title(t('gpx.drag_drop')) - .placement('top')); - - label.append('input') - .attr('type', 'checkbox') - .property('disabled', true) - .on('change', clickGpx); - - label.append('span') - .text(t('gpx.local_layer')); - var adjustments = content.append('div') .attr('class', 'adjustments'); diff --git a/js/id/ui/feature_info.js b/js/id/ui/feature_info.js index 8ff0e5699..a90d69eae 100644 --- a/js/id/ui/feature_info.js +++ b/js/id/ui/feature_info.js @@ -8,7 +8,7 @@ iD.ui.FeatureInfo = function(context) { if (hidden.length) { var stats = features.stats(), hiddenList = _.map(hidden, function(k) { - return String(stats[k]) + ' ' + t('features.' + k + '.description'); + return String(stats[k]) + ' ' + t('feature.' + k + '.description'); }); selection.append('span') diff --git a/js/id/ui/features.js b/js/id/ui/features.js deleted file mode 100644 index 5a93a274f..000000000 --- a/js/id/ui/features.js +++ /dev/null @@ -1,125 +0,0 @@ -iD.ui.Features = function(context) { - var key = 'f'; - - function features(selection) { - - function showsFeature(d) { - return context.features().enabled(d); - } - - function clickFeature(d) { - context.features().toggle(d); - } - - function drawList(selection) { - var data = context.features().keys(); - - var layerLinks = selection.selectAll('li.layer') - .data(data); - - var enter = layerLinks.enter() - .insert('li', '.custom_layer') - .attr('class', 'layer'); - - // only set tooltips for layers with tooltips - enter.filter(function(d) { return d; }) - .call(bootstrap.tooltip() - .title(function(d) { return t('features.' + d + '.tooltip'); }) - .placement('top')); - - var label = enter.append('label'); - - label.append('input') - .attr('type', 'checkbox') - .attr('name', 'layers') - .property('checked', showsFeature) - .on('change', clickFeature); - - label.append('span') - .text(function(d) { return t('features.' + d + '.description'); }); - - layerLinks.exit() - .remove(); - - selection.style('display', selection.selectAll('li.layer').data().length > 0 ? 'block' : 'none'); - } - - function update() { - featureList.call(drawList); - } - - var content = selection.append('div') - .attr('class', 'fillL map-overlay col3 content hide'), - tooltip = bootstrap.tooltip() - .placement('left') - .html(true) - .title(iD.ui.tooltipHtml(t('features.description'), key)); - - function hide() { setVisible(false); } - - function toggle() { - if (d3.event) d3.event.preventDefault(); - tooltip.hide(button); - setVisible(!button.classed('active')); - } - - function setVisible(show) { - if (show !== shown) { - button.classed('active', show); - shown = show; - - if (show) { - selection.on('mousedown.features-inside', function() { - return d3.event.stopPropagation(); - }); - content.style('display', 'block') - .style('right', '-300px') - .transition() - .duration(200) - .style('right', '0px'); - } else { - content.style('display', 'block') - .style('right', '0px') - .transition() - .duration(200) - .style('right', '-300px') - .each('end', function() { - d3.select(this).style('display', 'none'); - }); - selection.on('mousedown.features-inside', null); - } - } - } - - var button = selection.append('button') - .attr('tabindex', -1) - .on('click', toggle) - .call(tooltip), - shown = false; - - button.append('span') - .attr('class', 'icon layers light'); - - content.append('h4') - .text(t('features.title')); - - var featureList = content.append('ul') - .attr('class', 'layer-list'); - - context.features() - .on('change.features-update', update); - - update(); - - var keybinding = d3.keybinding('features'); - keybinding.on(key, toggle); - - d3.select(document) - .call(keybinding); - - context.surface().on('mousedown.features-outside', hide); - context.container().on('mousedown.features-outside', hide); - } - - return features; -}; diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js new file mode 100644 index 000000000..6acef6277 --- /dev/null +++ b/js/id/ui/map_data.js @@ -0,0 +1,255 @@ +iD.ui.MapData = function(context) { + var key = 'f'; + + function features(selection) { + + function showsFeature(d) { + return context.features().enabled(d); + } + + function clickFeature(d) { + context.features().toggle(d); + } + + function clickGpx() { + context.background().toggleGpxLayer(); + update(); + } + + function clickMapillary() { + context.background().toggleMapillaryLayer(); + update(); + } + + function drawFeatureList(selection) { + var data = context.features().keys(); + + var layerLinks = selection.selectAll('li.layer') + .data(data); + + //enter + var enter = layerLinks.enter() + .insert('li', '.custom_layer') + .attr('class', 'layer'); + + enter.filter(function(d) { return d; }) + .call(bootstrap.tooltip() + .title(function(d) { return t('feature.' + d + '.tooltip'); }) + .placement('top')); + + var label = enter.append('label'); + + label.append('input') + .attr('type', 'checkbox') + .attr('name', function(d) { return d; }) + .on('change', clickFeature); + + label.append('span') + .text(function(d) { return t('feature.' + d + '.description'); }); + + //update + layerLinks + .classed('active', showsFeature) + .selectAll('input') + .property('checked', showsFeature); + + //exit + layerLinks.exit() + .remove(); + + selection.style('display', selection.selectAll('li.layer').data().length > 0 ? 'block' : 'none'); + } + + function update() { + featureList.call(drawFeatureList); + + var hasGpx = context.background().hasGpxLayer(), + showsGpx = context.background().showsGpxLayer(); + + gpxLayerItem + .classed('active', showsGpx) + .selectAll('input') + .property('disabled', !hasGpx) + .property('checked', showsGpx); + + var showsMapillary = context.background().showsMapillaryLayer(); + + mapillaryLayerItem + .classed('active', showsMapillary) + .selectAll('input') + .property('checked', showsMapillary); + } + + var content = selection.append('div') + .attr('class', 'fillL map-overlay col3 content hide'), + tooltip = bootstrap.tooltip() + .placement('left') + .html(true) + .title(iD.ui.tooltipHtml(t('map_data.description'), key)); + + function hide() { setVisible(false); } + + function toggle() { + if (d3.event) d3.event.preventDefault(); + tooltip.hide(button); + setVisible(!button.classed('active')); + } + + function setVisible(show) { + if (show !== shown) { + button.classed('active', show); + shown = show; + + if (show) { + selection.on('mousedown.map_data-inside', function() { + return d3.event.stopPropagation(); + }); + content.style('display', 'block') + .style('right', '-300px') + .transition() + .duration(200) + .style('right', '0px'); + } else { + content.style('display', 'block') + .style('right', '0px') + .transition() + .duration(200) + .style('right', '-300px') + .each('end', function() { + d3.select(this).style('display', 'none'); + }); + selection.on('mousedown.map_data-inside', null); + } + } + } + + var button = selection.append('button') + .attr('tabindex', -1) + .on('click', toggle) + .call(tooltip), + shown = false; + + button.append('span') + .attr('class', 'icon layers light'); + + content.append('h4') + .text(t('map_data.title')); + + content.append('a') + .text(t('map_data.show_features')) + .attr('href', '#') + .classed('hide-toggle', true) + .classed('expanded', false) + .on('click', function() { + var exp = d3.select(this).classed('expanded'); + featureContainer.style('display', exp ? 'none' : 'block'); + d3.select(this).classed('expanded', !exp); + d3.event.preventDefault(); + }); + + var featureContainer = content.append('div') + .attr('class', 'filters') + .style('display', 'none'); + + var featureList = featureContainer.append('ul') + .attr('class', 'layer-list'); + + + content.append('a') + .text(t('map_data.show_layers')) + .attr('href', '#') + .classed('hide-toggle', true) + .classed('expanded', true) + .on('click', function() { + var exp = d3.select(this).classed('expanded'); + layerContainer.style('display', exp ? 'none' : 'block'); + d3.select(this).classed('expanded', !exp); + d3.event.preventDefault(); + }); + + var layerContainer = content.append('div') + .attr('class', 'filters') + .style('display', 'block'); + + var mapillaryLayerItem = layerContainer.append('ul') + .attr('class', 'layer-list') + .append('li'); + + var label = mapillaryLayerItem.append('label') + .call(bootstrap.tooltip() + .title(t('mapillary.tooltip')) + .placement('top')); + + label.append('input') + .attr('type', 'checkbox') + .on('change', clickMapillary); + + label.append('span') + .text(t('mapillary.title')); + + var gpxLayerItem = layerContainer.append('ul') + .style('display', iD.detect().filedrop ? 'block' : 'none') + .attr('class', 'layer-list') + .append('li') + .classed('layer-toggle-gpx', true); + + gpxLayerItem.append('button') + .attr('class', 'layer-extent') + .call(bootstrap.tooltip() + .title(t('gpx.zoom')) + .placement('left')) + .on('click', function() { + d3.event.preventDefault(); + d3.event.stopPropagation(); + context.background().zoomToGpxLayer(); + }) + .append('span') + .attr('class', 'icon geolocate'); + + gpxLayerItem.append('button') + .attr('class', 'layer-browse') + .call(bootstrap.tooltip() + .title(t('gpx.browse')) + .placement('left')) + .on('click', function() { + d3.select(document.createElement('input')) + .attr('type', 'file') + .on('change', function() { + context.background().gpxLayerFiles(d3.event.target.files); + }) + .node().click(); + }) + .append('span') + .attr('class', 'icon geocode'); + + label = gpxLayerItem.append('label') + .call(bootstrap.tooltip() + .title(t('gpx.drag_drop')) + .placement('top')); + + label.append('input') + .attr('type', 'checkbox') + .property('disabled', true) + .on('change', clickGpx); + + label.append('span') + .text(t('gpx.local_layer')); + + + context.features() + .on('change.map_data-update', update); + + update(); + + var keybinding = d3.keybinding('features'); + keybinding.on(key, toggle); + + d3.select(document) + .call(keybinding); + + context.surface().on('mousedown.map_data-outside', hide); + context.container().on('mousedown.map_data-outside', hide); + } + + return features; +}; diff --git a/test/index.html b/test/index.html index 8b702ccf6..6ad75a745 100644 --- a/test/index.html +++ b/test/index.html @@ -81,7 +81,7 @@ - + From 670c228fc7d737018b9d299e9871278306fb3338 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Sep 2014 12:47:25 -0400 Subject: [PATCH 03/41] put layer-gpx after layer-data (closes #2352, #2335) --- js/id/renderer/background.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index bb441592c..8b74795e7 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -64,19 +64,11 @@ iD.Background = function(context) { base.call(baseLayer); - var gpx = selection.selectAll('.gpx-layer') - .data([0]); - - gpx.enter().insert('div', '.layer-data') - .attr('class', 'layer-layer gpx-layer'); - - gpx.call(gpxLayer); - - var overlays = selection.selectAll('.overlay-layer') + var overlays = selection.selectAll('.layer-overlay') .data(overlayLayers, function(d) { return d.source().name(); }); overlays.enter().insert('div', '.layer-data') - .attr('class', 'layer-layer overlay-layer'); + .attr('class', 'layer-layer layer-overlay'); overlays.each(function(layer) { d3.select(this).call(layer); @@ -85,6 +77,14 @@ iD.Background = function(context) { overlays.exit() .remove(); + var gpx = selection.selectAll('.layer-gpx') + .data([0]); + + gpx.enter().insert('div') + .attr('class', 'layer-layer layer-gpx'); + + gpx.call(gpxLayer); + var mapillary = selection.selectAll('.layer-mapillary') .data([0]); From 51cadd5b824ccd8f2779eb1711f544d229d9cba7 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Sep 2014 15:34:26 -0400 Subject: [PATCH 04/41] initial wireframe style (key 'w') (also fix gpx layer, now on top of data, to not steal pointer events) --- css/map.css | 25 ++++++++++++++++++++++--- js/id/ui/map_data.js | 13 +++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/css/map.css b/css/map.css index 1329cbf44..a6ff78d42 100644 --- a/css/map.css +++ b/css/map.css @@ -1128,16 +1128,18 @@ g.turn circle { } /* GPX Paths */ +div.layer-gpx { + pointer-events: none; +} + path.gpx { stroke: #FF26D4; stroke-width: 2; fill: none; - pointer-events: none; } -/* GPS Labels */ text.gpx { - fill:#FF26D4; + fill: #FF26D4; } /* Mapillary Layer */ @@ -1193,3 +1195,20 @@ text.gpx { .mode-drag-node .area.fill { pointer-events: none; } + + +/* Styles */ +.style-wireframe path.stroke { + stroke-width: 1.5; + stroke-opacity: 0.5; + stroke-dasharray: none; + fill: none; +} + +.style-wireframe .point, +.style-wireframe path.shadow, +.style-wireframe path.casing, +.style-wireframe path.fill, +.style-wireframe path.oneway { + display: none; +} diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index 6acef6277..4bb4d2744 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -241,8 +241,17 @@ iD.ui.MapData = function(context) { update(); - var keybinding = d3.keybinding('features'); - keybinding.on(key, toggle); + var keybinding = d3.keybinding('features') + .on(key, toggle) + .on('w', function toggleWireframe() { + if (d3.event) d3.event.preventDefault(); + var surface = context.surface(), + wf = surface.classed('style-wireframe'); + + surface + .classed('style-wireframe', !wf); + + }); d3.select(document) .call(keybinding); From 7d5189f3b0315574f7e735699ae2e489de7b0dc6 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 25 Sep 2014 15:32:46 -0400 Subject: [PATCH 05/41] Area filling options: wireframe, partial, full.. * implemented as css classes on surface element * patterned fills can be done with css too.. removed the code from areas.js to set patterned fill on each element) * dummy test code in `map_data.js` uses 'w' key to toggle.. --- css/map.css | 164 +++++++++++++++++++++++++++++-------------- js/id/svg/areas.js | 42 +---------- js/id/ui/map_data.js | 23 ++++-- 3 files changed, 131 insertions(+), 98 deletions(-) diff --git a/css/map.css b/css/map.css index a6ff78d42..1c33ed7bd 100644 --- a/css/map.css +++ b/css/map.css @@ -180,6 +180,8 @@ path.area.stroke { stroke-width: 1; } path.area.fill { + stroke-width: 0; + stroke: rgba(255, 255, 255, 0.3); fill: rgba(255, 255, 255, 0.3); fill-rule: evenodd; } @@ -192,6 +194,7 @@ path.stroke.tag-natural { stroke: rgb(182, 225, 153); } path.fill.tag-natural { + stroke: rgba(182, 225, 153, 0.3); fill: rgba(182, 225, 153, 0.3); } .preset-icon-fill-area.tag-natural { @@ -199,12 +202,19 @@ path.fill.tag-natural { background-color: rgba(182, 225, 153, 0.3); } -path.stroke.tag-natural-water { +path.stroke.tag-natural-water, +path.stroke.tag-landuse-basin, +path.stroke.tag-landuse-reservoir { stroke: rgb(119, 211, 222); } +path.fill.tag-landuse-basin, +path.fill.tag-landuse-reservoir, path.fill.tag-natural-water { + stroke: rgba(119, 211, 222, 0.3); fill: rgba(119, 211, 222, 0.3); } +.preset-icon-fill-area.tag-landuse-basin, +.preset-icon-fill-area.tag-landuse-reservoir, .preset-icon-fill-area.tag-natural-water { border-color: rgb(119, 211, 222); background-color: rgba(119, 211, 222, 0.3); @@ -222,6 +232,7 @@ path.fill.tag-amenity-kindergarten, path.fill.tag-amenity-school, path.fill.tag-amenity-college, path.fill.tag-amenity-university { + stroke: rgba(255, 255, 148, 0.15); fill: rgba(255, 255, 148, 0.15); } .preset-icon-fill-area.tag-amenity-childcare, @@ -249,7 +260,8 @@ path.fill.tag-natural-grassland, path.fill.tag-natural-grass, path.fill.tag-leisure-pitch, path.fill.tag-leisure-park { - fill: rgba(140, 208, 95, 0.2); + stroke: rgba(140, 208, 95, 0.3); + fill: rgba(140, 208, 95, 0.3); } .preset-icon-fill-area.tag-landuse, .preset-icon-fill-area.tag-natural-wood, @@ -259,15 +271,7 @@ path.fill.tag-leisure-park { .preset-icon-fill-area.tag-leisure-pitch, .preset-icon-fill-area.tag-leisure-park { border-color: rgb(140, 208, 95); - background-color: rgba(140, 208, 95, 0.2); -} - -path.fill.tag-landuse-residential, -path.fill.tag-landuse-retail, -path.fill.tag-landuse-commercial, -path.fill.tag-landuse-industrial { - fill: none; - stroke-width: 60px; + background-color: rgba(140, 208, 95, 0.3); } path.stroke.tag-landuse-residential { @@ -275,6 +279,7 @@ path.stroke.tag-landuse-residential { } path.fill.tag-landuse-residential { stroke: rgba(196, 189, 25, 0.3); + fill: rgba(196, 189, 25, 0.3); } .preset-icon-fill-area.tag-landuse-residential { border-color: rgb(196, 189, 25); @@ -289,6 +294,7 @@ path.stroke.tag-landuse-commercial { path.fill.tag-landuse-retail, path.fill.tag-landuse-commercial { stroke: rgba(214, 136, 26, 0.3); + fill: rgba(214, 136, 26, 0.3); } .preset-icon-fill-area.tag-landuse-retail, .preset-icon-fill-area.tag-landuse-commercial { @@ -302,6 +308,7 @@ path.stroke.tag-landuse-industrial { } path.fill.tag-landuse-industrial { stroke: rgba(228, 164, 245, 0.3); + fill: rgba(228, 164, 245, 0.3); } .preset-icon-fill-area.tag-landuse-industrial { border-color: rgb(228, 164, 245); @@ -309,24 +316,11 @@ path.fill.tag-landuse-industrial { box-shadow: inset 0 0 0 5px rgba(228, 164, 245, 0.3); } -path.stroke.tag-landuse-basin, -path.stroke.tag-landuse-reservoir { - stroke: rgb(119, 211, 222); -} -path.fill.tag-landuse-basin, -path.fill.tag-landuse-reservoir { - fill: rgba(119, 211, 222, 0.3); -} -.preset-icon-fill-area.tag-landuse-basin, -.preset-icon-fill-area.tag-landuse-reservoir { - border-color: rgb(119, 211, 222); - background-color: rgba(119, 211, 222, 0.3); -} - path.stroke.tag-landuse-quarry { stroke: rgb(166, 149, 123); } path.fill.tag-landuse-quarry { + stroke: rgba(166, 149, 123, 0.2); fill: rgba(166, 149, 123, 0.2); } .preset-icon-fill-area.tag-landuse-quarry { @@ -338,6 +332,7 @@ path.stroke.tag-landuse-landfill { stroke: rgb(255, 153, 51); } path.fill.tag-landuse-landfill { + stroke: rgba(255, 153, 51, 0.2); fill: rgba(255, 153, 51, 0.2); } .preset-icon-fill-area.tag-landuse-landfill { @@ -345,11 +340,15 @@ path.fill.tag-landuse-landfill { background-color: rgba(255, 153, 51, 0.2); } +.pattern-color-construction { + fill: rgba(196, 189, 25, 0.2); +} path.stroke.tag-landuse-construction { stroke: rgb(196, 189, 25); } -.pattern-color-construction { - fill: rgba(196, 189, 25, 0.2); +path.fill.tag-landuse-construction { + stroke: url('/#pattern-construction'); + fill: url('/#pattern-construction'); } .preset-icon-fill-area.tag-landuse-construction { border-color: rgb(196, 189, 25); @@ -359,49 +358,69 @@ path.stroke.tag-landuse-construction { path.stroke.tag-landuse-military { stroke: rgb(214, 136, 26); } +path.fill.tag-landuse-military { + stroke: rgba(214, 136, 26, 0.2); + fill: rgba(214, 136, 26, 0.2); +} .preset-icon-fill-area.tag-landuse-military { border-color: rgb(214, 136, 26); background-color: rgba(214, 136, 26, 0.2); } +.pattern-color-wetland { + fill: rgba(182, 225, 153, 0.2); +} path.stroke.tag-natural-wetland { stroke: rgb(182, 225, 153); } -.pattern-color-wetland { - fill: rgba(182, 225, 153, 0.2); +path.fill.tag-natural-wetland { + stroke: url('/#pattern-wetland'); + fill: url('/#pattern-wetland'); } .preset-icon-fill-area.tag-natural-wetland { border-color: rgb(182, 225, 153); background-color: rgba(182, 225, 153, 0.2); } +.pattern-color-meadow { + fill: rgba(182, 225, 153, 0.2); +} path.stroke.tag-landuse-meadow { stroke: rgb(182, 225, 153); } -.pattern-color-meadow { - fill: rgba(182, 225, 153, 0.2); +path.fill.tag-landuse-meadow { + stroke: url('/#pattern-meadow'); + fill: url('/#pattern-meadow'); } .preset-icon-fill-area.tag-landuse-meadow { border-color: rgb(182, 225, 153); background-color: rgba(182, 225, 153, 0.2); } +.pattern-color-beach { + fill: rgba(255, 255, 126, 0.2); +} path.stroke.tag-natural-beach { stroke: rgb(255, 255, 126); } -.pattern-color-beach { - fill: rgba(255, 255, 126, 0.2); +path.fill.tag-natural-beach { + stroke: url('/#pattern-beach'); + fill: url('/#pattern-beach'); } .preset-icon-fill-area.tag-natural-beach { border-color: rgb(255, 255, 126); background-color: rgba(255, 255, 126, 0.2); } +.pattern-color-scrub { + fill: rgba(219, 240, 139, 0.2); +} path.stroke.tag-natural-scrub { stroke: rgb(219, 240, 139); } -.pattern-color-scrub { - fill: rgba(219, 240, 139, 0.2); +path.fill.tag-natural-scrub { + stroke: url('/#pattern-scrub'); + fill: url('/#pattern-scrub'); } .preset-icon-fill-area.tag-natural-scrub { border-color: rgb(219, 240, 139); @@ -412,29 +431,46 @@ path.stroke.tag-natural-scrub { .pattern-color-farmland { fill: rgba(140, 208, 95, 0.2); } +path.stroke.tag-landuse-farm, +path.stroke.tag-landuse-farmland { + stroke: rgb(140, 208, 95); +} +path.fill.tag-landuse-farm, +path.fill.tag-landuse-farmland { + stroke: url('/#pattern-farmland'); + fill: url('/#pattern-farmland'); +} .preset-icon-fill-area.tag-landuse-farm, .preset-icon-fill-area.tag-landuse-farmland { background-color: rgba(140, 208, 95, 0.2); } -.pattern-color-cemetery { - fill: rgba(140, 208, 95, 0.2) +.pattern-color-cemetery, +.pattern-color-orchard { + fill: rgba(140, 208, 95, 0.2); } +path.stroke.tag-landuse-cemetery, +path.stroke.tag-landuse-orchard { + stroke: rgb(140, 208, 95); +} +path.fill.tag-landuse-cemetery { + stroke: url('/#pattern-cemetery'); + fill: url('/#pattern-cemetery'); +} +path.fill.tag-landuse-orchard { + stroke: url('/#pattern-orchard'); + fill: url('/#pattern-orchard'); +} +.preset-icon-fill-area.tag-landuse-cemetery, .preset-icon-fill-area.tag-landuse-cemetery { background-color: rgba(140, 208, 95, 0.2); } -.pattern-color-orchard { - fill: rgba(140, 208, 95, 0.2) -} -.preset-icon-fill-area.tag-landuse-orchard { - background-color: rgba(140, 208, 95, 0.8); -} - path.stroke.tag-amenity-parking { stroke: rgb(170, 170, 170); } path.fill.tag-amenity-parking { + stroke: rgba(170, 170, 170, 0.3); fill: rgba(170, 170, 170, 0.3); } .preset-icon-fill-area.tag-amenity-parking { @@ -679,12 +715,14 @@ path.casing.tag-aeroway-runway { stroke-linecap: square; } path.fill.tag-aeroway-runway { + stroke: rgba(0, 0, 0, 0.6); fill: rgba(0, 0, 0, 0.6); } path.stroke.tag-aeroway-apron { stroke: #805C80; } path.fill.tag-aeroway-apron { + stroke: rgba(128, 92, 128, 0.2); fill: rgba(128, 92, 128, 0.2); } @@ -738,6 +776,7 @@ path.casing.tag-railway-platform { /* waterways */ path.fill.tag-waterway { + stroke: rgba(119, 211, 222, 0.3); fill: rgba(119, 211, 222, 0.3); } @@ -872,8 +911,6 @@ path.shadow.tag-highway-bridleway.tag-bridge { stroke-width: 13; } - - /* tunnels */ path.stroke.tag-tunnel { @@ -953,6 +990,7 @@ path.stroke.tag-amenity-shelter { } path.fill.tag-building, path.fill.tag-amenity-shelter { + stroke: rgba(224, 110, 95, 0.3); fill: rgba(224, 110, 95, 0.3); } .preset-icon-fill-area.tag-building, @@ -1197,18 +1235,36 @@ text.gpx { } -/* Styles */ -.style-wireframe path.stroke { - stroke-width: 1.5; +/* Fill Styles */ +.low-zoom.fill-wireframe path.stroke, +.fill-wireframe path.stroke { + stroke-width: 2; stroke-opacity: 0.5; stroke-dasharray: none; fill: none; } -.style-wireframe .point, -.style-wireframe path.shadow, -.style-wireframe path.casing, -.style-wireframe path.fill, -.style-wireframe path.oneway { +.low-zoom.fill-wireframe path.shadow, +.fill-wireframe path.shadow { + stroke-width: 8; +} + +.fill-wireframe path.shadow.hover:not(.selected) { + stroke-opacity: 0.2; +} +.fill-wireframe path.shadow.selected { + stroke-opacity: 0.4; +} + +.fill-wireframe .point, +.fill-wireframe .icon, +.fill-wireframe path.casing, +.fill-wireframe path.fill, +.fill-wireframe path.oneway { display: none; } + +.fill-partial path.fill { + fill: none; + stroke-width: 60px; +} diff --git a/js/id/svg/areas.js b/js/id/svg/areas.js index 12070f993..f3f61f11c 100644 --- a/js/id/svg/areas.js +++ b/js/id/svg/areas.js @@ -1,38 +1,4 @@ iD.svg.Areas = function(projection) { - // Patterns only work in Firefox when set directly on element. - // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632) - var patterns = { - wetland: 'wetland', - beach: 'beach', - scrub: 'scrub', - construction: 'construction', - military: 'construction', - cemetery: 'cemetery', - grave_yard: 'cemetery', - meadow: 'meadow', - farm: 'farmland', - farmland: 'farmland', - orchard: 'orchard' - }; - - var patternKeys = ['landuse', 'natural', 'amenity']; - - var clipped = ['residential', 'commercial', 'retail', 'industrial']; - - function clip(entity) { - return clipped.indexOf(entity.tags.landuse) !== -1; - } - - function setPattern(d) { - for (var i = 0; i < patternKeys.length; i++) { - if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) { - this.style.fill = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")'; - return; - } - } - this.style.fill = ''; - } - return function drawAreas(surface, graph, entities, filter) { var path = iD.svg.Path(projection, graph, true), areas = {}, @@ -65,7 +31,7 @@ iD.svg.Areas = function(projection) { }); var data = { - clip: areas.filter(clip), + clip: areas, shadow: strokes, stroke: strokes, fill: areas @@ -125,12 +91,8 @@ iD.svg.Areas = function(projection) { this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id); - if (layer === 'fill' && clip(entity)) { - this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)'); - } - if (layer === 'fill') { - setPattern.apply(this, arguments); + this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)'); } }) .call(iD.svg.TagClasses()); diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index 4bb4d2744..7b09e50c4 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -245,11 +245,26 @@ iD.ui.MapData = function(context) { .on(key, toggle) .on('w', function toggleWireframe() { if (d3.event) d3.event.preventDefault(); - var surface = context.surface(), - wf = surface.classed('style-wireframe'); - surface - .classed('style-wireframe', !wf); + var surface = context.surface(), + fw = surface.classed('fill-wireframe'), + fp = surface.classed('fill-partial'); + + if (fw) { + surface + .classed('fill-wireframe', false) + .classed('fill-partial', true); + } + else if (fp) { + surface + .classed('fill-wireframe', false) + .classed('fill-partial', false); + + } else { + surface + .classed('fill-wireframe', true) + .classed('fill-partial', false); + } }); From 8a98339bc28bce2bd3fc0d9962a91920bd67528f Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 26 Sep 2014 00:25:37 -0400 Subject: [PATCH 06/41] Add area fill selector (wireframe/partial/full) to map data panel also: * style dark tooltips with shortcut keys * use uppercase for shortcut keys --- css/app.css | 1 + data/core.yaml | 15 ++++- dist/locales/en.json | 19 +++++- js/id/ui.js | 8 ++- js/id/ui/background.js | 5 +- js/id/ui/help.js | 2 +- js/id/ui/map_data.js | 147 +++++++++++++++++++++++++---------------- 7 files changed, 130 insertions(+), 67 deletions(-) diff --git a/css/app.css b/css/app.css index ae88d830a..d34087bbb 100644 --- a/css/app.css +++ b/css/app.css @@ -2816,6 +2816,7 @@ img.wiki-image { } .map-overlay .tooltip-inner, +.map-overlay .keyhint-wrap, .entity-editor-pane .tooltip-inner, .warning-section .tooltip-inner { background: #000; diff --git a/data/core.yaml b/data/core.yaml index 5bd0c3703..7f5a8f245 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -242,8 +242,9 @@ en: map_data: title: Map Data description: Map Data - show_features: Show Features - show_layers: Show Layers + show_features: Show Map Features + show_layers: Show Data Layers + fill_area: Fill Areas feature: points: description: Points @@ -281,6 +282,16 @@ en: others: description: Others tooltip: "Everything Else" + area_fill: + wireframe: + description: No Fill (Wireframe) + tooltip: "Enabling wireframe mode makes it easy to see the background imagery." + partial: + description: Partial Fill + tooltip: "Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)" + full: + description: Full Fill + tooltip: "Areas are drawn fully filled." restore: heading: You have unsaved changes description: "Do you wish to restore unsaved changes from a previous editing session?" diff --git a/dist/locales/en.json b/dist/locales/en.json index e6eaa4354..fcb5e38d2 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -297,8 +297,9 @@ "map_data": { "title": "Map Data", "description": "Map Data", - "show_features": "Show Features", - "show_layers": "Show Layers" + "show_features": "Show Map Features", + "show_layers": "Show Data Layers", + "fill_area": "Fill Areas" }, "feature": { "points": { @@ -350,6 +351,20 @@ "tooltip": "Everything Else" } }, + "area_fill": { + "wireframe": { + "description": "No Fill (Wireframe)", + "tooltip": "Enabling wireframe mode makes it easy to see the background imagery." + }, + "partial": { + "description": "Partial Fill", + "tooltip": "Areas are drawn with fill only around their inner edges. (Recommended for beginner mappers)" + }, + "full": { + "description": "Full Fill", + "tooltip": "Areas are drawn fully filled." + } + }, "restore": { "heading": "You have unsaved changes", "description": "Do you wish to restore unsaved changes from a previous editing session?", diff --git a/js/id/ui.js b/js/id/ui.js index 3e8652162..66505eef0 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -203,5 +203,11 @@ iD.ui = function(context) { }; iD.ui.tooltipHtml = function(text, key) { - return '' + text + '' + '
' + ' ' + (t('tooltip_keyhint')) + ' ' + ' ' + key + '
'; + var s = '' + text + ''; + if (key) { + s += '
' + + ' ' + (t('tooltip_keyhint')) + ' ' + + ' ' + key + '
'; + } + return s; }; diff --git a/js/id/ui/background.js b/js/id/ui/background.js index 623a664df..f2e240f6a 100644 --- a/js/id/ui/background.js +++ b/js/id/ui/background.js @@ -1,5 +1,5 @@ iD.ui.Background = function(context) { - var key = 'b', + var key = 'B', opacities = [1, 0.75, 0.5, 0.25], directions = [ ['left', [1, 0]], @@ -296,9 +296,6 @@ iD.ui.Background = function(context) { var keybinding = d3.keybinding('background'); keybinding.on(key, toggle); - keybinding.on('m', function() { - context.enter(iD.modes.SelectImage(context)); - }); d3.select(document) .call(keybinding); diff --git a/js/id/ui/help.js b/js/id/ui/help.js index eef828b1d..c4e073c80 100644 --- a/js/id/ui/help.js +++ b/js/id/ui/help.js @@ -1,5 +1,5 @@ iD.ui.Help = function(context) { - var key = 'h'; + var key = 'H'; var docKeys = [ 'help.help', diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index 7b09e50c4..df84ecb18 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -1,7 +1,11 @@ iD.ui.MapData = function(context) { - var key = 'f'; + var key = 'F', + features = context.features().keys(), + fills = ['wireframe', 'partial', 'full'], + fillDefault = context.storage('area-fill') || 'partial', + fillSelected = fillDefault; - function features(selection) { + function map_data(selection) { function showsFeature(d) { return context.features().enabled(d); @@ -9,6 +13,24 @@ iD.ui.MapData = function(context) { function clickFeature(d) { context.features().toggle(d); + update(); + } + + function showsFill(d) { + return fillSelected === d; + } + + function setFill(d) { + _.each(fills, function(opt) { + context.surface().classed('fill-' + opt, Boolean(opt === d)); + }); + + fillSelected = d; + if (d !== 'wireframe') { + fillDefault = d; + context.storage('area-fill', d); + } + update(); } function clickGpx() { @@ -21,50 +43,51 @@ iD.ui.MapData = function(context) { update(); } - function drawFeatureList(selection) { - var data = context.features().keys(); - - var layerLinks = selection.selectAll('li.layer') + function drawList(selection, data, type, name, change, active) { + var items = selection.selectAll('li') .data(data); //enter - var enter = layerLinks.enter() - .insert('li', '.custom_layer') - .attr('class', 'layer'); - - enter.filter(function(d) { return d; }) + var enter = items.enter() + .append('li') + .attr('class', 'layer') .call(bootstrap.tooltip() - .title(function(d) { return t('feature.' + d + '.tooltip'); }) + .html(true) + .title(function(d) { + return iD.ui.tooltipHtml( + t(name + '.' + d + '.tooltip'), (d === 'wireframe' ? 'W' : null) + ); + }) .placement('top')); var label = enter.append('label'); label.append('input') - .attr('type', 'checkbox') - .attr('name', function(d) { return d; }) - .on('change', clickFeature); + .attr('type', type) + .attr('name', name) + .on('change', change); label.append('span') - .text(function(d) { return t('feature.' + d + '.description'); }); + .text(function(d) { return t(name + '.' + d + '.description'); }); //update - layerLinks - .classed('active', showsFeature) + items + .classed('active', active) .selectAll('input') - .property('checked', showsFeature); + .property('checked', active); //exit - layerLinks.exit() + items.exit() .remove(); - - selection.style('display', selection.selectAll('li.layer').data().length > 0 ? 'block' : 'none'); } function update() { - featureList.call(drawFeatureList); + featureList.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature); + fillList.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill); var hasGpx = context.background().hasGpxLayer(), - showsGpx = context.background().showsGpxLayer(); + showsGpx = context.background().showsGpxLayer(), + showsMapillary = context.background().showsMapillaryLayer(); gpxLayerItem .classed('active', showsGpx) @@ -72,8 +95,6 @@ iD.ui.MapData = function(context) { .property('disabled', !hasGpx) .property('checked', showsGpx); - var showsMapillary = context.background().showsMapillaryLayer(); - mapillaryLayerItem .classed('active', showsMapillary) .selectAll('input') @@ -87,14 +108,22 @@ iD.ui.MapData = function(context) { .html(true) .title(iD.ui.tooltipHtml(t('map_data.description'), key)); - function hide() { setVisible(false); } + function hidePanel() { setVisible(false); } - function toggle() { + function togglePanel() { if (d3.event) d3.event.preventDefault(); tooltip.hide(button); setVisible(!button.classed('active')); } + function toggleWireframe() { + if (d3.event) { + d3.event.preventDefault(); + d3.event.stopPropagation(); + } + setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe')); + } + function setVisible(show) { if (show !== shown) { button.classed('active', show); @@ -125,7 +154,7 @@ iD.ui.MapData = function(context) { var button = selection.append('button') .attr('tabindex', -1) - .on('click', toggle) + .on('click', togglePanel) .call(tooltip), shown = false; @@ -135,6 +164,7 @@ iD.ui.MapData = function(context) { content.append('h4') .text(t('map_data.title')); + // feature filters content.append('a') .text(t('map_data.show_features')) .attr('href', '#') @@ -154,7 +184,7 @@ iD.ui.MapData = function(context) { var featureList = featureContainer.append('ul') .attr('class', 'layer-list'); - + // data layers content.append('a') .text(t('map_data.show_layers')) .attr('href', '#') @@ -236,44 +266,47 @@ iD.ui.MapData = function(context) { .text(t('gpx.local_layer')); + // area fills + content.append('a') + .text(t('map_data.fill_area')) + .attr('href', '#') + .classed('hide-toggle', true) + .classed('expanded', true) + .on('click', function() { + var exp = d3.select(this).classed('expanded'); + fillContainer.style('display', exp ? 'none' : 'block'); + d3.select(this).classed('expanded', !exp); + d3.event.preventDefault(); + }); + + var fillContainer = content.append('div') + .attr('class', 'filters') + .style('display', 'block'); + + var fillList = fillContainer.append('ul') + .attr('class', 'layer-list'); + + context.features() .on('change.map_data-update', update); update(); + setFill(fillDefault); var keybinding = d3.keybinding('features') - .on(key, toggle) - .on('w', function toggleWireframe() { - if (d3.event) d3.event.preventDefault(); + .on(key, togglePanel) + .on('W', toggleWireframe); - var surface = context.surface(), - fw = surface.classed('fill-wireframe'), - fp = surface.classed('fill-partial'); - - if (fw) { - surface - .classed('fill-wireframe', false) - .classed('fill-partial', true); - } - else if (fp) { - surface - .classed('fill-wireframe', false) - .classed('fill-partial', false); - - } else { - surface - .classed('fill-wireframe', true) - .classed('fill-partial', false); - } - - }); + // keybinding.on('m', function() { + // context.enter(iD.modes.SelectImage(context)); + // }); d3.select(document) .call(keybinding); - context.surface().on('mousedown.map_data-outside', hide); - context.container().on('mousedown.map_data-outside', hide); + context.surface().on('mousedown.map_data-outside', hidePanel); + context.container().on('mousedown.map_data-outside', hidePanel); } - return features; + return map_data; }; From dcc9812986c3f1b1fc3cfd4bc2c7dbffe963c8f6 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 11 Oct 2014 00:58:08 -0400 Subject: [PATCH 07/41] prevent most operations on things connected to hidden features.. --- data/core.yaml | 8 +++++ dist/locales/en.json | 24 ++++++++----- js/id/core/graph.js | 6 ++-- js/id/id.js | 4 +++ js/id/modes/drag_node.js | 2 +- js/id/operations/circularize.js | 2 ++ js/id/operations/continue.js | 3 +- js/id/operations/delete.js | 6 +++- js/id/operations/disconnect.js | 6 +++- js/id/operations/move.js | 2 ++ js/id/operations/orthogonalize.js | 2 ++ js/id/operations/rotate.js | 2 ++ js/id/operations/split.js | 6 +++- js/id/operations/straighten.js | 6 +++- js/id/renderer/features.js | 58 +++++++++++++++++++++---------- 15 files changed, 102 insertions(+), 35 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 7f5a8f245..0d7c35ff9 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -55,6 +55,7 @@ en: 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. orthogonalize: title: Square description: @@ -66,12 +67,14 @@ en: area: Squared the corners of an area. not_squarish: This can't be made square because it is not squarish. too_large: This can't be made square because not enough of it is currently visible. + connected_to_hidden: This can't be made square because it is connected to a hidden feature. straighten: title: Straighten description: Straighten this line. key: S annotation: Straightened a line. too_bendy: This can't be straightened because it bends too much. + connected_to_hidden: This line can't be straightened because it is connected to a hidden feature. delete: title: Delete description: Delete object permanently. @@ -83,6 +86,7 @@ en: relation: Deleted a relation. multiple: "Deleted {n} objects." incomplete_relation: This feature can't be deleted because it hasn't been fully downloaded. + connected_to_hidden: This can't be deleted because it is connected to a hidden feature. add_member: annotation: Added a member to a relation. delete_member: @@ -99,6 +103,7 @@ en: key: D annotation: Disconnected lines/areas. not_connected: There aren't enough lines/areas here to disconnect. + connected_to_hidden: This can't be disconnected because it is connected to a hidden feature. merge: title: Merge description: Merge these lines. @@ -120,6 +125,7 @@ en: multiple: Moved multiple objects. incomplete_relation: This feature can't be moved because it hasn't been fully downloaded. too_large: This can't be moved because not enough of it is currently visible. + connected_to_hidden: This can't be moved because it is connected to a hidden feature. rotate: title: Rotate description: Rotate this object around its center point. @@ -128,6 +134,7 @@ en: line: Rotated a line. area: Rotated an area. too_large: This can't be rotated because not enough of it is currently visible. + connected_to_hidden: This can't be rotated because it is connected to a hidden feature. reverse: title: Reverse description: Make this line go in the opposite direction. @@ -146,6 +153,7 @@ en: multiple: "Split {n} lines/area boundaries." not_eligible: Lines can't be split at their beginning or end. multiple_ways: There are too many lines here to split. + connected_to_hidden: This can't be split because it is connected to a hidden feature. restriction: help: select: Click to select a road segment. diff --git a/dist/locales/en.json b/dist/locales/en.json index fcb5e38d2..476187314 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -72,7 +72,8 @@ "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." + "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." }, "orthogonalize": { "title": "Square", @@ -86,14 +87,16 @@ "area": "Squared the corners of an area." }, "not_squarish": "This can't be made square because it is not squarish.", - "too_large": "This can't be made square because not enough of it is currently visible." + "too_large": "This can't be made square because not enough of it is currently visible.", + "connected_to_hidden": "This can't be made square because it is connected to a hidden feature." }, "straighten": { "title": "Straighten", "description": "Straighten this line.", "key": "S", "annotation": "Straightened a line.", - "too_bendy": "This can't be straightened because it bends too much." + "too_bendy": "This can't be straightened because it bends too much.", + "connected_to_hidden": "This line can't be straightened because it is connected to a hidden feature." }, "delete": { "title": "Delete", @@ -106,7 +109,8 @@ "relation": "Deleted a relation.", "multiple": "Deleted {n} objects." }, - "incomplete_relation": "This feature can't be deleted because it hasn't been fully downloaded." + "incomplete_relation": "This feature can't be deleted because it hasn't been fully downloaded.", + "connected_to_hidden": "This can't be deleted because it is connected to a hidden feature." }, "add_member": { "annotation": "Added a member to a relation." @@ -127,7 +131,8 @@ "description": "Disconnect these lines/areas from each other.", "key": "D", "annotation": "Disconnected lines/areas.", - "not_connected": "There aren't enough lines/areas here to disconnect." + "not_connected": "There aren't enough lines/areas here to disconnect.", + "connected_to_hidden": "This can't be disconnected because it is connected to a hidden feature." }, "merge": { "title": "Merge", @@ -151,7 +156,8 @@ "multiple": "Moved multiple objects." }, "incomplete_relation": "This feature can't be moved because it hasn't been fully downloaded.", - "too_large": "This can't be moved because not enough of it is currently visible." + "too_large": "This can't be moved because not enough of it is currently visible.", + "connected_to_hidden": "This can't be moved because it is connected to a hidden feature." }, "rotate": { "title": "Rotate", @@ -161,7 +167,8 @@ "line": "Rotated a line.", "area": "Rotated an area." }, - "too_large": "This can't be rotated because not enough of it is currently visible." + "too_large": "This can't be rotated because not enough of it is currently visible.", + "connected_to_hidden": "This can't be rotated because it is connected to a hidden feature." }, "reverse": { "title": "Reverse", @@ -183,7 +190,8 @@ "multiple": "Split {n} lines/area boundaries." }, "not_eligible": "Lines can't be split at their beginning or end.", - "multiple_ways": "There are too many lines here to split." + "multiple_ways": "There are too many lines here to split.", + "connected_to_hidden": "This can't be split because it is connected to a hidden feature." }, "restriction": { "help": { diff --git a/js/id/core/graph.js b/js/id/core/graph.js index 3f9ab7c9c..8455d5096 100644 --- a/js/id/core/graph.js +++ b/js/id/core/graph.js @@ -72,8 +72,10 @@ iD.Graph.prototype = { return this._childNodes[entity.id]; var nodes = []; - for (var i = 0, l = entity.nodes.length; i < l; i++) { - nodes[i] = this.entity(entity.nodes[i]); + if (entity.nodes) { + for (var i = 0, l = entity.nodes.length; i < l; i++) { + nodes[i] = this.entity(entity.nodes[i]); + } } if (iD.debug) Object.freeze(nodes); diff --git a/js/id/id.js b/js/id/id.js index 5ea20ca86..fd18f9a5a 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -206,6 +206,10 @@ window.iD = function () { /* Features */ var features = iD.Features(context); context.features = function() { return features; }; + context.hasHiddenConnections = function(id) { + var entity = history.graph().entity(id); + return features.hasHiddenConnections(entity); + }; /* Map */ var map = iD.Map(context); diff --git a/js/id/modes/drag_node.js b/js/id/modes/drag_node.js index be2956181..aa0176288 100644 --- a/js/id/modes/drag_node.js +++ b/js/id/modes/drag_node.js @@ -48,7 +48,7 @@ iD.modes.DragNode = function(context) { } function start(entity) { - cancelled = d3.event.sourceEvent.shiftKey; + cancelled = d3.event.sourceEvent.shiftKey || context.features().hasHiddenConnections(entity); if (cancelled) return behavior.cancel(); wasMidpoint = entity.type === 'midpoint'; diff --git a/js/id/operations/circularize.js b/js/id/operations/circularize.js index a786627c9..f15a11d8f 100644 --- a/js/id/operations/circularize.js +++ b/js/id/operations/circularize.js @@ -20,6 +20,8 @@ iD.operations.Circularize = function(selectedIDs, context) { var reason; if (extent.percentContainedIn(context.extent()) < 0.8) { reason = 'too_large'; + } else if (context.hasHiddenConnections(entityId)) { + reason = 'connected_to_hidden'; } return action.disabled(context.graph()) || reason; }; diff --git a/js/id/operations/continue.js b/js/id/operations/continue.js index 9ebcdac1e..bc1f7ba9a 100644 --- a/js/id/operations/continue.js +++ b/js/id/operations/continue.js @@ -23,7 +23,8 @@ iD.operations.Continue = function(selectedIDs, context) { }; operation.available = function() { - return geometries.vertex.length === 1 && geometries.line.length <= 1; + return geometries.vertex.length === 1 && geometries.line.length <= 1 && + !context.features().hasHiddenConnections(vertex); }; operation.disabled = function() { diff --git a/js/id/operations/delete.js b/js/id/operations/delete.js index 15c41c067..bdd1483a4 100644 --- a/js/id/operations/delete.js +++ b/js/id/operations/delete.js @@ -52,7 +52,11 @@ iD.operations.Delete = function(selectedIDs, context) { }; operation.disabled = function() { - return action.disabled(context.graph()); + var reason; + if (_.any(selectedIDs, context.hasHiddenConnections)) { + reason = 'connected_to_hidden'; + } + return action.disabled(context.graph()) || reason; }; operation.tooltip = function() { diff --git a/js/id/operations/disconnect.js b/js/id/operations/disconnect.js index ea12b0419..baa1ff1d4 100644 --- a/js/id/operations/disconnect.js +++ b/js/id/operations/disconnect.js @@ -19,7 +19,11 @@ iD.operations.Disconnect = function(selectedIDs, context) { }; operation.disabled = function() { - return action.disabled(context.graph()); + var reason; + if (_.any(selectedIDs, context.hasHiddenConnections)) { + reason = 'connected_to_hidden'; + } + return action.disabled(context.graph()) || reason; }; operation.tooltip = function() { diff --git a/js/id/operations/move.js b/js/id/operations/move.js index 525ee7047..6f877a0cc 100644 --- a/js/id/operations/move.js +++ b/js/id/operations/move.js @@ -16,6 +16,8 @@ iD.operations.Move = function(selectedIDs, context) { var reason; if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) { reason = 'too_large'; + } else if (_.any(selectedIDs, context.hasHiddenConnections)) { + reason = 'connected_to_hidden'; } return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason; }; diff --git a/js/id/operations/orthogonalize.js b/js/id/operations/orthogonalize.js index 851e7e248..851f3b7a9 100644 --- a/js/id/operations/orthogonalize.js +++ b/js/id/operations/orthogonalize.js @@ -21,6 +21,8 @@ iD.operations.Orthogonalize = function(selectedIDs, context) { var reason; if (extent.percentContainedIn(context.extent()) < 0.8) { reason = 'too_large'; + } else if (context.hasHiddenConnections(entityId)) { + reason = 'connected_to_hidden'; } return action.disabled(context.graph()) || reason; }; diff --git a/js/id/operations/rotate.js b/js/id/operations/rotate.js index 3edc3e4d9..485d05d24 100644 --- a/js/id/operations/rotate.js +++ b/js/id/operations/rotate.js @@ -22,6 +22,8 @@ iD.operations.Rotate = function(selectedIDs, context) { operation.disabled = function() { if (extent.percentContainedIn(context.extent()) < 0.8) { return 'too_large'; + } else if (context.hasHiddenConnections(entityId)) { + return 'connected_to_hidden'; } else { return false; } diff --git a/js/id/operations/split.js b/js/id/operations/split.js index 9a9130df1..43bc72946 100644 --- a/js/id/operations/split.js +++ b/js/id/operations/split.js @@ -29,7 +29,11 @@ iD.operations.Split = function(selectedIDs, context) { }; operation.disabled = function() { - return action.disabled(context.graph()); + var reason; + if (_.any(selectedIDs, context.hasHiddenConnections)) { + reason = 'connected_to_hidden'; + } + return action.disabled(context.graph()) || reason; }; operation.tooltip = function() { diff --git a/js/id/operations/straighten.js b/js/id/operations/straighten.js index 85e9bcd17..fd0a5e823 100644 --- a/js/id/operations/straighten.js +++ b/js/id/operations/straighten.js @@ -16,7 +16,11 @@ iD.operations.Straighten = function(selectedIDs, context) { }; operation.disabled = function() { - return action.disabled(context.graph()); + var reason; + if (context.hasHiddenConnections(entityId)) { + reason = 'connected_to_hidden'; + } + return action.disabled(context.graph()) || reason; }; operation.tooltip = function() { diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 12a3a9c11..a43f3e64c 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -41,8 +41,7 @@ iD.Features = function(context) { 'obliterated': true }; - var graph = context.graph(), - dispatch = d3.dispatch('change', 'redraw'), + var dispatch = d3.dispatch('change', 'redraw'), feature = {}; function defineFeature(k, filter, max) { @@ -64,24 +63,24 @@ iD.Features = function(context) { } defineFeature('points', function(entity) { - return entity.geometry(graph) === 'point'; + return entity.geometry(context.graph()) === 'point'; }, 100); defineFeature('major_roads', function(entity) { - return entity.geometry(graph) === 'line' && major_roads[entity.tags.highway]; + return entity.geometry(context.graph()) === 'line' && major_roads[entity.tags.highway]; }); defineFeature('minor_roads', function(entity) { - return entity.geometry(graph) === 'line' && minor_roads[entity.tags.highway]; + return entity.geometry(context.graph()) === 'line' && minor_roads[entity.tags.highway]; }); defineFeature('paths', function(entity) { - return entity.geometry(graph) === 'line' && paths[entity.tags.highway]; + return entity.geometry(context.graph()) === 'line' && paths[entity.tags.highway]; }); defineFeature('buildings', function(entity) { return ( - entity.geometry(graph) === 'area' && ( + entity.geometry(context.graph()) === 'area' && ( (!!entity.tags.building && entity.tags.building !== 'no') || entity.tags.amenity === 'shelter' || entity.tags.parking === 'multi-storey' || @@ -93,7 +92,7 @@ iD.Features = function(context) { }, 100); defineFeature('landuse', function(entity) { - return entity.geometry(graph) === 'area' && + return entity.geometry(context.graph()) === 'area' && !feature.buildings.filter(entity) && !feature.water.filter(entity); }); @@ -138,8 +137,8 @@ iD.Features = function(context) { // lines or areas that don't match another feature filter. defineFeature('others', function(entity) { return ( - entity.geometry(graph) === 'line' || - entity.geometry(graph) === 'area' + entity.geometry(context.graph()) === 'line' || + entity.geometry(context.graph()) === 'area' ) && _.reduce(_.omit(feature, 'others'), function(result, v) { return result && !v.filter(entity); @@ -244,17 +243,38 @@ iD.Features = function(context) { return stats; }; - features.isHidden = function(entity) { - var hidden = features.hidden(); + features.isHiddenFeature = function(entity) { + return _.any(features.hidden(), function(k) { return feature[k].filter(entity); }); + }; - function isHiddenFeature(entity) { - return _.any(hidden, function(k) { return feature[k].filter(entity); }); + features.isHiddenChild = function(entity) { + var g = context.graph(), + parents = _.union(g.parentWays(entity), g.parentRelations(entity)); + return parents.length ? _.all(parents, features.isHidden) : false; + }; + + features.hasHiddenConnections = function(entity) { + var g = context.graph(), + childNodes, connections; + + if (entity.type === 'midpoint') { + childNodes = [context.entity(entity.edge[0]), context.entity(entity.edge[1])]; + } else { + childNodes = g.childNodes(entity); } - function isHiddenChild(entity) { - var parents = _.union(graph.parentWays(entity), graph.parentRelations(entity)); - return parents.length ? _.all(parents, features.isHidden) : false; - } - return isHiddenFeature(entity) || isHiddenChild(entity); + + // gather parents.. + connections = _.union(g.parentWays(entity), g.parentRelations(entity)); + // gather ways connected to child nodes.. + connections = _.reduce(childNodes, function(result, e) { + return g.isShared(e) ? _.union(result, g.parentWays(e)) : result; + }, connections); + + return connections.length ? _.any(connections, features.isHidden) : false; + }; + + features.isHidden = function(entity) { + return features.isHiddenFeature(entity) || features.isHiddenChild(entity); }; features.filter = function(d) { From 59918431f22857b49f4e0f87699adbe01aaf33b3 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 13 Oct 2014 23:52:31 -0400 Subject: [PATCH 08/41] Fix the source switcher --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 0f0c10bd4..f3129da2d 100644 --- a/index.html +++ b/index.html @@ -241,7 +241,7 @@ d3.select('#id-container') .call(id.ui()); - d3.select('#about').insert('li', '.user-list') + d3.select('#about-list').insert('li', '.user-list') .attr('class', 'source-switch') .call(iD.ui.SourceSwitch(id) .keys([ From e761414a150745e93be17535ab6984cd840d5746 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 15 Oct 2014 00:40:22 -0400 Subject: [PATCH 09/41] 'Rail' feature filter should not match if it is also a highway. --- js/id/renderer/features.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index a43f3e64c..1a87a683a 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -62,6 +62,8 @@ iD.Features = function(context) { dispatch.redraw(); } + + defineFeature('points', function(entity) { return entity.geometry(context.graph()) === 'point'; }, 100); @@ -115,7 +117,11 @@ iD.Features = function(context) { }); defineFeature('rail', function(entity) { - return ( + return !( + feature.major_roads.filter(entity) || + feature.minor_roads.filter(entity) || + feature.paths.filter(entity) + ) && ( !!entity.tags.railway || entity.tags.landuse === 'railway' ); @@ -128,10 +134,11 @@ 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) { var strings = _.flatten(_.pairs(entity.tags)); - return _.any(strings, function(s) { return past_futures[s]; }) && - !feature.major_roads.filter(entity) && - !feature.minor_roads.filter(entity) && - !feature.paths.filter(entity); + return !( + feature.major_roads.filter(entity) || + feature.minor_roads.filter(entity) || + feature.paths.filter(entity) + ) && _.any(strings, function(s) { return past_futures[s]; }); }); // lines or areas that don't match another feature filter. From da32150c4ac074ea51e923eed252dc81fe08c233 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 20 Oct 2014 20:28:52 +0000 Subject: [PATCH 10/41] Revert some of 2832041 because of Firefox pattern fill issues Looks like styling the element is the only way to achieve this after all.. --- css/map.css | 101 ++++++++++++++++----------------------------- js/id/svg/areas.js | 29 +++++++++++++ 2 files changed, 64 insertions(+), 66 deletions(-) diff --git a/css/map.css b/css/map.css index 1c33ed7bd..149f6748d 100644 --- a/css/map.css +++ b/css/map.css @@ -202,6 +202,36 @@ path.fill.tag-natural { background-color: rgba(182, 225, 153, 0.3); } +path.stroke.tag-landuse, +path.stroke.tag-natural-wood, +path.stroke.tag-natural-tree, +path.stroke.tag-natural-grassland, +path.stroke.tag-natural-grass, +path.stroke.tag-leisure-pitch, +path.stroke.tag-leisure-park { + stroke: rgb(140, 208, 95); +} +path.fill.tag-landuse, +path.fill.tag-natural-wood, +path.fill.tag-natural-tree, +path.fill.tag-natural-grassland, +path.fill.tag-natural-grass, +path.fill.tag-leisure-pitch, +path.fill.tag-leisure-park { + stroke: rgba(140, 208, 95, 0.3); + fill: rgba(140, 208, 95, 0.3); +} +.preset-icon-fill-area.tag-landuse, +.preset-icon-fill-area.tag-natural-wood, +.preset-icon-fill-area.tag-natural-tree, +.preset-icon-fill-area.tag-natural-grassland, +.preset-icon-fill-area.tag-natural-grass, +.preset-icon-fill-area.tag-leisure-pitch, +.preset-icon-fill-area.tag-leisure-park { + border-color: rgb(140, 208, 95); + background-color: rgba(140, 208, 95, 0.3); +} + path.stroke.tag-natural-water, path.stroke.tag-landuse-basin, path.stroke.tag-landuse-reservoir { @@ -244,36 +274,6 @@ path.fill.tag-amenity-university { background-color: rgba(255, 255, 148, 0.15); } -path.stroke.tag-landuse, -path.stroke.tag-natural-wood, -path.stroke.tag-natural-tree, -path.stroke.tag-natural-grassland, -path.stroke.tag-natural-grass, -path.stroke.tag-leisure-pitch, -path.stroke.tag-leisure-park { - stroke: rgb(140, 208, 95); -} -path.fill.tag-landuse, -path.fill.tag-natural-wood, -path.fill.tag-natural-tree, -path.fill.tag-natural-grassland, -path.fill.tag-natural-grass, -path.fill.tag-leisure-pitch, -path.fill.tag-leisure-park { - stroke: rgba(140, 208, 95, 0.3); - fill: rgba(140, 208, 95, 0.3); -} -.preset-icon-fill-area.tag-landuse, -.preset-icon-fill-area.tag-natural-wood, -.preset-icon-fill-area.tag-natural-tree, -.preset-icon-fill-area.tag-natural-grassland, -.preset-icon-fill-area.tag-natural-grass, -.preset-icon-fill-area.tag-leisure-pitch, -.preset-icon-fill-area.tag-leisure-park { - border-color: rgb(140, 208, 95); - background-color: rgba(140, 208, 95, 0.3); -} - path.stroke.tag-landuse-residential { stroke: rgb(196, 189, 25); } @@ -346,10 +346,6 @@ path.fill.tag-landuse-landfill { path.stroke.tag-landuse-construction { stroke: rgb(196, 189, 25); } -path.fill.tag-landuse-construction { - stroke: url('/#pattern-construction'); - fill: url('/#pattern-construction'); -} .preset-icon-fill-area.tag-landuse-construction { border-color: rgb(196, 189, 25); background-color: rgba(196, 189, 25, 0.2); @@ -373,10 +369,6 @@ path.fill.tag-landuse-military { path.stroke.tag-natural-wetland { stroke: rgb(182, 225, 153); } -path.fill.tag-natural-wetland { - stroke: url('/#pattern-wetland'); - fill: url('/#pattern-wetland'); -} .preset-icon-fill-area.tag-natural-wetland { border-color: rgb(182, 225, 153); background-color: rgba(182, 225, 153, 0.2); @@ -388,10 +380,6 @@ path.fill.tag-natural-wetland { path.stroke.tag-landuse-meadow { stroke: rgb(182, 225, 153); } -path.fill.tag-landuse-meadow { - stroke: url('/#pattern-meadow'); - fill: url('/#pattern-meadow'); -} .preset-icon-fill-area.tag-landuse-meadow { border-color: rgb(182, 225, 153); background-color: rgba(182, 225, 153, 0.2); @@ -403,10 +391,6 @@ path.fill.tag-landuse-meadow { path.stroke.tag-natural-beach { stroke: rgb(255, 255, 126); } -path.fill.tag-natural-beach { - stroke: url('/#pattern-beach'); - fill: url('/#pattern-beach'); -} .preset-icon-fill-area.tag-natural-beach { border-color: rgb(255, 255, 126); background-color: rgba(255, 255, 126, 0.2); @@ -418,10 +402,6 @@ path.fill.tag-natural-beach { path.stroke.tag-natural-scrub { stroke: rgb(219, 240, 139); } -path.fill.tag-natural-scrub { - stroke: url('/#pattern-scrub'); - fill: url('/#pattern-scrub'); -} .preset-icon-fill-area.tag-natural-scrub { border-color: rgb(219, 240, 139); background-color: rgba(219, 240, 139, 0.2); @@ -435,11 +415,6 @@ path.stroke.tag-landuse-farm, path.stroke.tag-landuse-farmland { stroke: rgb(140, 208, 95); } -path.fill.tag-landuse-farm, -path.fill.tag-landuse-farmland { - stroke: url('/#pattern-farmland'); - fill: url('/#pattern-farmland'); -} .preset-icon-fill-area.tag-landuse-farm, .preset-icon-fill-area.tag-landuse-farmland { background-color: rgba(140, 208, 95, 0.2); @@ -453,16 +428,8 @@ path.stroke.tag-landuse-cemetery, path.stroke.tag-landuse-orchard { stroke: rgb(140, 208, 95); } -path.fill.tag-landuse-cemetery { - stroke: url('/#pattern-cemetery'); - fill: url('/#pattern-cemetery'); -} -path.fill.tag-landuse-orchard { - stroke: url('/#pattern-orchard'); - fill: url('/#pattern-orchard'); -} .preset-icon-fill-area.tag-landuse-cemetery, -.preset-icon-fill-area.tag-landuse-cemetery { +.preset-icon-fill-area.tag-landuse-orchard { background-color: rgba(140, 208, 95, 0.2); } @@ -1238,7 +1205,9 @@ text.gpx { /* Fill Styles */ .low-zoom.fill-wireframe path.stroke, .fill-wireframe path.stroke { - stroke-width: 2; + /* stroke-width: 2; */ + /* stroke-opacity: 0.5; */ + stroke-width: 1; stroke-opacity: 0.5; stroke-dasharray: none; fill: none; @@ -1265,6 +1234,6 @@ text.gpx { } .fill-partial path.fill { - fill: none; + fill-opacity: 0; stroke-width: 60px; } diff --git a/js/id/svg/areas.js b/js/id/svg/areas.js index f3f61f11c..ebc77101f 100644 --- a/js/id/svg/areas.js +++ b/js/id/svg/areas.js @@ -1,4 +1,32 @@ iD.svg.Areas = function(projection) { + // Patterns only work in Firefox when set directly on element. + // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632) + var patterns = { + wetland: 'wetland', + beach: 'beach', + scrub: 'scrub', + construction: 'construction', + military: 'construction', + cemetery: 'cemetery', + grave_yard: 'cemetery', + meadow: 'meadow', + farm: 'farmland', + farmland: 'farmland', + orchard: 'orchard' + }; + + var patternKeys = ['landuse', 'natural', 'amenity']; + + function setPattern(d) { + for (var i = 0; i < patternKeys.length; i++) { + if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) { + this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")'; + return; + } + } + this.style.fill = this.style.stroke = ''; + } + return function drawAreas(surface, graph, entities, filter) { var path = iD.svg.Path(projection, graph, true), areas = {}, @@ -93,6 +121,7 @@ iD.svg.Areas = function(projection) { if (layer === 'fill') { this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)'); + setPattern.apply(this, arguments); } }) .call(iD.svg.TagClasses()); From 70e7987943c2cef0f1c65257c4c29c759ad800a9 Mon Sep 17 00:00:00 2001 From: samanpwbb Date: Tue, 21 Oct 2014 14:54:31 -0400 Subject: [PATCH 11/41] add data icon, fix button style --- css/app.css | 9 +- dist/img/sprite.svg | 2190 +++++++++++++++++++----------------------- js/id/ui/map_data.js | 2 +- 3 files changed, 975 insertions(+), 1226 deletions(-) diff --git a/css/app.css b/css/app.css index d34087bbb..013e35db8 100644 --- a/css/app.css +++ b/css/app.css @@ -539,6 +539,7 @@ button.save.has-count .count::before { .icon.relation.route { background-position: -540px 0;} .icon.relation.multipolygon { background-position: -560px 0;} .icon.vertex { background-position: -580px 0;} +.icon.data { background-position: -600px 0;} .icon.inspect.light { background-position: -220px -20px;} .icon.plus.light { background-position: -240px -20px;} @@ -551,6 +552,7 @@ button.save.has-count .count::before { .icon.geolocate.light { background-position: -360px -20px;} .icon.bug.light { background-position: -400px -20px;} .icon.help.light { background-position: -460px -20px;} +.icon.data.light { background-position: -600px -20px;} .icon.back.blue { background-position: -420px -20px;} .icon.forward.blue { background-position: -440px -20px;} @@ -572,6 +574,7 @@ button[disabled] .icon.geocode { background-position: -280px -40px;} button[disabled] .icon.layers { background-position: -300px -40px;} button[disabled] .icon.avatar { background-position: -320px -40px;} button[disabled] .icon.nearby { background-position: -340px -40px;} +button[disabled] .icon.data { background-position: -600px -40px;} .icon.point.deleted { background-position: -302px -80px;} .icon.line.deleted { background-position: -320px -80px;} @@ -1888,7 +1891,11 @@ img.wiki-image { /* Background / Map Data Settings */ -.map-data-control button, + +.map-data-control button { + border-radius: 0; +} + .background-control button { border-radius: 4px 0 0 0; } diff --git a/dist/img/sprite.svg b/dist/img/sprite.svg index e6c15a29b..9eaf11a5e 100644 --- a/dist/img/sprite.svg +++ b/dist/img/sprite.svg @@ -7,64 +7,10 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" width="800" height="560" - id="svg12393" - inkscape:version="0.48.4 r" - sodipodi:docname="sprite.svg"> - - - - - - - + id="svg12393"> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ff00ff;fill-opacity:1;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> image/svg+xml - + - + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999905;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#ffffff;fill-opacity:1"> + id="g58871" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter8013-4);enable-background:accumulate" /> + id="g58873" + style="fill:#ffffff;fill-opacity:1" /> + style="fill:#6bc641;fill-opacity:1;display:inline" /> + style="opacity:0.25;fill:#000000;fill-opacity:1;display:inline" /> + style="display:inline" /> + transform="translate(80.03464,-6.1042359)" + id="g58885"> + id="g58887" + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter8013-4);enable-background:accumulate" /> + style="fill:#ffffff;fill-opacity:1;display:inline"> + style="fill:#ffffff;fill-opacity:1;display:inline" /> + style="fill:#ffffff;fill-opacity:1;display:inline" /> + style="fill:#1a1a1a;fill-opacity:1;display:inline"> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#1a1a1a;fill-opacity:1"> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="g9599" + style="fill:#1a1a1a;fill-opacity:1"> + style="opacity:0.2;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.1;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.4;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.6;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.1;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.1;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.55555558;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.55555558;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#ffffff;fill-opacity:1;display:inline"> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;fill:#000000;fill-opacity:1;display:inline"> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#000000;fill-opacity:1"> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="g9701" + style="fill:#000000;fill-opacity:1"> + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.4;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.6;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#1c1c1c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;fill:#000000;fill-opacity:1;display:inline"> + id="path47763" + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;fill:#000000;fill-opacity:1;display:inline"> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#ffffff;fill-opacity:1;display:inline"> + id="path33297" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#ffffff;fill-opacity:1"> + x="-131" + y="657.36218" + id="rect33301" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="g33303" + style="fill:#ffffff;fill-opacity:1"> + id="path33305" + style="opacity:0.2;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path33307" + style="opacity:0.1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.4;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.6;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.55555558;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#7092ff;fill-opacity:1;stroke:none;stroke-width:0.50000125;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#222222;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#222222;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(301,239.63781)" + id="g20251-9"> + transform="translate(10.000004,-1.9999957)" + id="g20247-4"> + id="path20243-7" + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path20245-0" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(301,200.63781)" + id="g20283"> + transform="translate(10.000004,-1.9999957)" + id="g20285"> + id="path20287" + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path20289" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(340,239.63781)" + id="g20299"> + transform="translate(10.000004,-1.9999957)" + id="g20301"> + id="path20303" + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path20305" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.009009;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="matrix(0.390625,0,0,0.390625,842.1875,238.60852)" + id="g4611"> + x="-2156" + y="-47.637821" + id="rect4565" + style="color:#000000;fill:none;stroke:none;stroke-width:34;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(-781,47)" + id="g4494"> + id="path6718" + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="matrix(0.5,0,0,0.5,-342.5001,125.49999)" + id="g8629"> + x="935" + y="238.99998" + id="rect8627" + style="opacity:0.15;color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#1a1a1a;fill-opacity:1"> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(-25,70.000003)" + id="g3822"> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(-26,3.0624999e-6)" + id="g3837" /> + style="fill:#ffffff" /> + y="220" + id="rect6623-9" + style="opacity:0.15;color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="display:inline"> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + y="220" + id="rect6623-9-2" + style="opacity:0.15;color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(-25,3.0624999e-6)" + id="g7659" /> + transform="translate(-53,28.000003)" + id="g7663" /> + transform="translate(-23.375,1.375)" + id="g7690" /> + transform="translate(-50.375,1.375)" + id="g7694" /> + transform="translate(-23.375,-25.625)" + id="g7700" /> + transform="translate(-50.375,-25.625)" + id="g7706" /> + y="220" + id="rect6623-9-2-3" + style="opacity:0.15;color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.15;color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.15;color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(270,5.0000031)" + id="g4221"> - + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:12;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#7092ff;fill-opacity:1;stroke:none;stroke-width:11.66666508;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + id="rect8373" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.055;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(350,15.000003)" + id="g10113"> + x="-350" + y="465" + id="rect8477" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#7092ff;fill-opacity:1"> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#7092ff;fill-opacity:1"> + x="730" + y="77.362183" + id="rect22091" + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - + - + - + + x="731" + y="78.362183" + id="rect22099" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path26156" + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path26158" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path26160" + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path26162" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#7092ff;fill-opacity:1"> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + x="181" + y="78.362183" + id="rect7157" + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#7092ff;fill-opacity:1"> + id="path7161" + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path7163" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path7165" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path7169" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + x="0" + y="400" + id="rect9903" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#8cd05f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(340,-200)" + id="g10052"> + style="color:#000000;fill:#c1c1c1;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:2;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#c1c1c1;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:2;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - + style="color:#000000;fill:#a9a9a9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(-260,452.63781)" + id="waterway-river"> + x="400" + y="77.362183" + id="rect24309" + style="color:#000000;fill:#60d4de;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#60d4de;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path25543" + style="opacity:0.5;color:#000000;fill:#60d4de;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path25545" + style="opacity:0.5;color:#000000;fill:#60d4de;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path25547" + style="opacity:0.5;color:#000000;fill:#60d4de;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path25549" + style="opacity:0.5;color:#000000;fill:#60d4de;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path26103" + style="color:#000000;fill:#60d4de;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path26105" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path26107" + style="color:#000000;fill:#60d4de;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path26109" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter8013-4);enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.50000125;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#597be7;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#ffffff;fill-opacity:1;display:inline"> + id="path4661" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + y="155" + id="rect4769" + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999905;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + x="163" + y="147" + id="rect4769-6" + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999905;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - - - - - - - - - - - - - - + id="rect4789" + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999905;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + + + + + + + + + + + + + + + x="600" + y="220" + id="rect4219-0" + style="opacity:0.15;color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:17.49651146px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:120.00000477%;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#e4a4be;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Avenir;-inkscape-font-specification:Avenir" /> + style="font-size:16.71258354px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#222222;fill-rule:nonzero;enable-background:accumulate;font-family:Helvetica Neue" /> + style="font-size:16.71258354px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-rule:nonzero;enable-background:accumulate;font-family:Helvetica Neue" /> + style="font-size:16.71258354px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ffffff;fill-rule:nonzero;enable-background:accumulate;font-family:Helvetica Neue" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#7f7f7f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(-5,3.0624999e-6)" + id="g16825"> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + transform="translate(5432.5,-1245.375)" + id="g17870"> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(-74,-0.99999694)" + id="g16816"> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="fill:#ffffff"> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="fill:#ffffff"> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#ffffff"> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="opacity:0.5"> + id="path16862" + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="opacity:0.5"> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5"> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> - + id="path6484" + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path6503" + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path6774" + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="display:inline"> + id="g6893-0" + style="display:inline"> + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="g7098" + style="fill:#222222;fill-opacity:1;display:inline"> + y="145" + id="rect7100" + style="opacity:0.5;color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="fill:#222222;fill-opacity:1;display:inline"> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#8cd05f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="path3414" + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.009009;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.009009;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.009009;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="rect15727" + style="opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.009009;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.009009;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="rect15731" + style="opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.009009;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + id="rect15733" + style="opacity:0.5;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.009009;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="matrix(0,1,-1,0,45,203)" + id="g3360"> + id="path12188" + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#8cd05f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + id="path12188-3" + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + transform="matrix(0,1,1,0,116.9905,-80.00893)" + id="g4190"> - - - - - + + + + + + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> ONLY + y="126.18999" + id="tspan3360">ONLY + transform="matrix(0,1,1,0,206.9905,-80.00893)" + id="g4190-7"> + id="path4163-9" + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index df84ecb18..df61e7e37 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -159,7 +159,7 @@ iD.ui.MapData = function(context) { shown = false; button.append('span') - .attr('class', 'icon layers light'); + .attr('class', 'icon data light'); content.append('h4') .text(t('map_data.title')); From 794a4bcb30e5b74a7f072cca60d93644dd5689dd Mon Sep 17 00:00:00 2001 From: samanpwbb Date: Tue, 21 Oct 2014 15:12:18 -0400 Subject: [PATCH 12/41] new icon --- dist/img/sprite.svg | 2743 ++++++++++++++++++++++++------------------- 1 file changed, 1523 insertions(+), 1220 deletions(-) diff --git a/dist/img/sprite.svg b/dist/img/sprite.svg index 9eaf11a5e..a59937bdf 100644 --- a/dist/img/sprite.svg +++ b/dist/img/sprite.svg @@ -7,10 +7,78 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" width="800" height="560" - id="svg12393"> + id="svg12393" + inkscape:version="0.48.5 r10040" + sodipodi:docname="sprite.svg"> + + + + + + + + + + + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ff00ff;fill-opacity:1;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" + inkscape:connector-curvature="0" /> - + transform="translate(80.03464,-25.104236)"> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter8013-4);enable-background:accumulate" + id="g58871" /> + style="fill:#ffffff;fill-opacity:1" + id="g58873" /> + transform="translate(-346,-101.36218)" /> + transform="translate(-346,-62.362177)" /> + transform="matrix(-0.70710678,0.70710679,0.70710679,0.70710678,852.1436,-601.56033)" /> + id="g58885" + transform="translate(80.03464,-6.1042359)"> + style="color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter8013-4);enable-background:accumulate" + id="g58887" /> + transform="translate(-379,-135)"> + transform="matrix(0.70710678,0.70710679,-0.70710679,0.70710678,-113.14357,-447.56033)" /> + transform="matrix(-0.70710678,0.70710679,0.70710679,0.70710678,1293.1436,-447.56033)" /> + transform="translate(480,-653.36218)"> + transform="translate(0,52.362183)" + d="m -130.5,603 c -4.14213,0 -7.5,3.35787 -7.5,7.5 0,4.14214 3.35787,7.5 7.5,7.5 4.14213,0 7.5,-3.35786 7.5,-7.5 0,-4.14213 -3.35787,-7.5 -7.5,-7.5 z m 0,1 c 3.58393,0 6.5,2.91607 6.5,6.5 0,3.58393 -2.91607,6.5 -6.5,6.5 -3.58393,0 -6.5,-2.91607 -6.5,-6.5 0,-3.58393 2.91607,-6.5 6.5,-6.5 z" /> + clip-path="url(#clipPath9141-6)"> + y="657.36218" + x="-131" + height="6" + width="1" /> + style="fill:#1a1a1a;fill-opacity:1" + id="g9599"> + transform="matrix(1.5714241,0,0,1.5714241,72.999423,-294.92081)" + d="m -126,609.5 c 0,1.933 -1.567,3.5 -3.5,3.5 l 0,-3.5 z" /> + transform="translate(0,52.362183)" + d="m -126,615 -4.5,-4.5 0,6.5 z" /> + d="m -135.99999,662.86218 c 0,3.03756 2.46242,5.49998 5.49999,5.49998 l 0,-5.49998 z" /> + d="m -135.99999,662.86216 c 0,-3.03756 2.46242,-5.49998 5.49999,-5.49998 l 0,5.49998 z" /> + d="m -130.5,662.86216 -4,4.50002 -2.68804,-4.50002 z" /> + d="m -135,658.36218 4.5,4.5 0,-6.5 z" /> + d="m 363,10 0,-2 10,-4 2,0 0,2 -4,10 -2,0 0,-6 z" /> + d="m 363,30 0,-2 10,-4 2,0 0,2 -4,10 -2,0 0,-6 z" /> + d="m 363,49.999997 0,-2 10,-4 2,0 0,2 -4,10 -2,0 0,-6 z" /> + d="m 89,2 -5,4.5 5,4.5 0,-3 3,0 1,1 0,3 -1,1 -5,0 -1,1 0,1 1,1 5,0 2,-1 1,-1 1,-2 L 96,9 95,7 94,6 92,5 89,5 z m 22,0 0,3 -3,0 -2,1 -1,1 -1,2 0,3 1,2 1,1 2,1 5,0 1,-1 0,-1 -1,-1 -5,0 -1,-1 0,-3 1,-1 3,0 0,3 5,-4.5 z M 54,3 53,4 53,5.59375 46.59375,12 45,12 l -1,1 0,2 1,1 2,0 1,-1 0,-1.59375 L 54.40625,7 56,7 57,6 57,4 56,3 z m 96,0 -6,6 1,1 3,0 0,6 1,1 2,0 1,-1 0,-6 3,0 1,-1 z M 32,4 c -2.76142,0 -5,2.23858 -5,5 0,2.76143 5,7 5,7 0,0 5,-4.23857 5,-7 0,-2.76142 -2.23858,-5 -5,-5 z m 23,0 c 0.55228,0 1,0.44772 1,1 0,0.55229 -0.44772,1 -1,1 C 54.74848,6 54.51948,5.9033 54.34375,5.75 54.30935,5.71998 54.28,5.69066 54.25,5.65625 54.09665,5.480519 54,5.251521 54,5 54,4.44772 54.44772,4 55,4 z m 10,0 -1,1 0,2 1,1 0,4 -1,1 0,2 1,1 2,0 1,-1 4,0 1,1 2,0 1,-1 0,-2 -1,-1 0,-4 1,-1 0,-2 -1,-1 -2,0 -1,1 -4,0 -1,-1 z m 69.5,0 -4.9375,8.5625 L 126,9 l -1,0 -1,1 0,1 5,5 1,0 1,0 6,-10 0,-1 -1,-1 z m 30.5,0 -1,1 0,1 4,4 -4,4 0,1 1,1 1,0 4,-4 4,4 1,0 1,-1 0,-1 -4,-4 4,-4 0,-1 -1,-1 -1,0 -4,4 -4,-4 z m 21,0 -1,1 0,1 1,1 8,0 1,-1 0,-1 -1,-1 z M 66,5 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z m 8,0 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 C 73.44772,7 73,6.552285 73,6 73,5.447715 73.44772,5 74,5 z M 32.15625,7 c 1.10457,0 2,0.89543 2,2 0,1.10457 -0.89543,2 -2,2 -1.104569,0 -2,-0.89543 -2,-2 0,-1.10457 0.895431,-2 2,-2 z M 68,7 l 4,0 1,1 0,4 -1,1 -4,0 -1,-1 0,-4 z m 118,1 0,7 1,1 6,0 1,-1 0,-7 z m 2,2 1,0 0,4 -1,0 0,-1 z m 3,0 1,0 0,3 0,1 -1,0 z M 46,13 c 0.25152,0 0.48052,0.0967 0.65625,0.25 L 46.75,13.34375 C 46.90335,13.519481 47,13.748484 47,14 c 0,0.55229 -0.44772,1 -1,1 -0.55228,0 -1,-0.44771 -1,-1 0,-0.55228 0.44772,-1 1,-1 z m 20,0 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z m 8,0 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z" /> + transform="translate(-536.00001,-43.999997)"> + transform="matrix(0.64285714,0,0,0.64285714,332.85714,42.142857)"> + d="m 925.00003,71.333338 c -3.86599,0 -7,3.13401 -7,7 0,1.12542 0.30585,2.16901 0.77778,3.11111 l -5.44445,5.444436 0,3.11111 3.11112,0 5.44444,-5.44444 c 0.94211,0.47192 1.98568,0.77778 3.11111,0.77778 3.86599,0 7,-3.134006 7,-6.999996 0,-3.86599 -3.13401,-7 -7,-7 z m 0,3.11111 c 2.14777,0 3.88889,1.74112 3.88889,3.88889 0,2.14777 -1.74112,3.88889 -3.88889,3.88889 -2.14777,0 -3.88889,-1.74112 -3.88889,-3.88889 0,-2.14777 1.74112,-3.88889 3.88889,-3.88889 z" /> + transform="matrix(0.35714364,0,0,0.35714364,597.14212,77.857044)" /> + transform="translate(-681,73.63782)"> - - - - - - - - - - - - - - - - - - - - - - - - + d="m 174,3 -1,1 0,4 -4,0 -1,1 0,1 1,1 4,0 0,4 1,1 1,0 1,-1 0,-4 4,0 1,-1 0,-1 -1,-1 -4,0 0,-4 -1,-1 -1,0 z" /> + inkscape:connector-curvature="0" + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="path9631" + d="m 265,8.0000031 11,0 1,1.00002 0,0.9999999 -1,0.99998 -11,0 -1,-1 0,-0.9999999 z" /> + inkscape:connector-curvature="0" + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="path3048-0-7-5-6" + d="m 288.5,3.0000131 c -3.03757,0 -5.5,2.46243 -5.5,5.5 0,3.0375699 2.46243,5.4999999 5.5,5.4999999 1.00612,0 1.93866,-0.27827 2.75,-0.75 l 3.75,3.75 1,0 1,-1 0,-1 -3.75,-3.75 c 0.47173,-0.81134 0.75,-1.7438699 0.75,-2.7499999 0,-3.03757 -2.46243,-5.5 -5.5,-5.5 z m -0.5,2 1,0 2,1 1,2 0,1 -1,1.9999999 -2,1 -1,0 -2,-1 -1,-1.9999999 0,-1 1,-2 2,-1 z" /> + + style="opacity:0.5;fill:#000000;fill-opacity:1;display:inline" + id="g9693" + transform="translate(480,-613.36218)"> + d="m -130.5,603 c -4.14213,0 -7.5,3.35787 -7.5,7.5 0,4.14214 3.35787,7.5 7.5,7.5 4.14213,0 7.5,-3.35786 7.5,-7.5 0,-4.14213 -3.35787,-7.5 -7.5,-7.5 z m 0,1 c 3.58393,0 6.5,2.91607 6.5,6.5 0,3.58393 -2.91607,6.5 -6.5,6.5 -3.58393,0 -6.5,-2.91607 -6.5,-6.5 0,-3.58393 2.91607,-6.5 6.5,-6.5 z" /> + style="fill:#000000;fill-opacity:1" + id="g9697" + clip-path="url(#clipPath9141-6)"> + x="-131" + height="6" + width="1" /> + style="fill:#000000;fill-opacity:1" + id="g9701"> + d="m -126,609.5 c 0,1.933 -1.567,3.5 -3.5,3.5 l 0,-3.5 z" /> + d="m -126,615 -4.5,-4.5 0,6.5 z" /> + inkscape:connector-curvature="0" + style="opacity:0.4;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="path9707" + d="m -135.99999,662.86218 c 0,3.03756 2.46242,5.49998 5.49999,5.49998 l 0,-5.49998 z" /> + inkscape:connector-curvature="0" + style="opacity:0.6;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="path9709" + d="m -135.99999,662.86216 c 0,-3.03756 2.46242,-5.49998 5.49999,-5.49998 l 0,5.49998 z" /> + inkscape:connector-curvature="0" + style="opacity:0.1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="path9711" + d="m -130.5,662.86216 -4,4.50002 -2.68804,-4.50002 z" /> + inkscape:connector-curvature="0" + style="opacity:0.1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="path9713" + d="m -135,658.36218 4.5,4.5 0,-6.5 z" /> + + + + + + + + + + + + + + + + + + + + + + + + d="m 391.5,23.999999 c -2.48528,0 -4.5,2.01472 -4.5,4.499999 0,0.723491 0.19662,1.394364 0.50001,2.000006 L 384,33.999999 l 0,1.999999 2.00001,0 3.49999,-3.499997 c 0.60565,0.303377 1.27651,0.499995 2,0.499995 2.48528,0 4.5,-2.014712 4.5,-4.499998 0,-2.485279 -2.01472,-4.499999 -4.5,-4.499999 z m 0,1.999999 c 1.38071,0 2.5,1.119291 2.5,2.5 0,1.380716 -1.11929,2.500001 -2.5,2.500001 -1.38071,0 -2.5,-1.119285 -2.5,-2.500001 0,-1.380709 1.11929,-2.5 2.5,-2.5 z" /> + d="m 6,144 -1,1 0,1 1,1 8,0 1,-1 0,-1 -1,-1 z m 0,4 0,7 1,1 6,0 1,-1 0,-7 z m 2,2 1,0 0,4 -1,0 0,-1 z m 3,0 1,0 0,3 0,1 -1,0 z" /> + d="m 64,145.01282 c -1,0 -2,1 -2,2 l 0,2 3,0 2,1 -2,1 -3,0 0,2 c 0,1 1,2 2,2 l 1,0 c 2,0 2.48722,-0.97443 3,-2 l 1,-2 9,-5 c 0,0 0,-1 -2,-1 L 68.96875,148.91907 68,147.01282 c -0.49406,-0.98812 -1,-2 -3,-2 z m 0,1 2,0 1,2 -4,0 c 0,0 0,-0.66667 0,-1 0,-1.01282 1,-1 1,-1 z m 8.09375,4.71875 -2,1 L 76,155.01282 c 2,0 2,-1 2,-1 z M 63,152.01282 l 4,0 -1,2 -2,0 c 0,0 -1,0 -1,-1 z" /> + d="m 30,141 c -1.104569,0 -2,0.89543 -2,2 0,1.10457 0.895431,2 2,2 1.104569,0 2,-0.89543 2,-2 0,-1.10457 -0.895431,-2 -2,-2 z" /> + d="m 164,142 c -1.10457,0 -2,0.89543 -2,2 0,1.10457 0.89543,2 2,2 1.10457,0 2,-0.89543 2,-2 0,-1.10457 -0.89543,-2 -2,-2 z" /> + d="m 108,145 -4,5 4,5 2,0 0,-1 -3.5,-4 3.5,-4 0,-1 -2,0 z" /> + d="m 114,145 -4,5 4,5 2,0 0,-1 -3.5,-4 3.5,-4 0,-1 -2,0 z" /> + d="m 22.84375,146 c -0.77652,0.0669 -1.494002,0.58712 -1.75,1.375 -0.341331,1.05051 0.230742,2.18992 1.28125,2.53125 1.050508,0.34133 2.189919,-0.23074 2.53125,-1.28125 0.341331,-1.05051 -0.230742,-2.18992 -1.28125,-2.53125 -0.262627,-0.0853 -0.52241,-0.11611 -0.78125,-0.0937 z" /> + d="m 36.78125,146 c -0.132056,0.0149 -0.274936,0.0512 -0.40625,0.0937 -1.050508,0.34133 -1.622581,1.48074 -1.28125,2.53125 0.341331,1.05051 1.480742,1.62258 2.53125,1.28125 1.050508,-0.34133 1.622581,-1.48074 1.28125,-2.53125 -0.298664,-0.91919 -1.20061,-1.47926 -2.125,-1.375 z" /> + d="m 42,148 c -1.104569,0 -2,0.89543 -2,2 0,1.10457 0.895431,2 2,2 1.104569,0 2,-0.89543 2,-2 0,-1.10457 -0.895431,-2 -2,-2 z" /> + d="m 50,148 c -1.104569,0 -2,0.89543 -2,2 0,1.10457 0.895431,2 2,2 1.104569,0 2,-0.89543 2,-2 0,-1.10457 -0.895431,-2 -2,-2 z" /> + d="m 58,148 c -1.104569,0 -2,0.89543 -2,2 0,1.10457 0.895431,2 2,2 1.104569,0 2,-0.89543 2,-2 0,-1.10457 -0.895431,-2 -2,-2 z" /> + d="m 90,148 c -1.10457,0 -2,0.89543 -2,2 0,1.10457 0.89543,2 2,2 1.10457,0 2,-0.89543 2,-2 0,-1.10457 -0.89543,-2 -2,-2 z" /> + d="m 130,141 -3,3 1,1 1,0 0,2 2,0 0,-2 1,0 1,-1 -3,-3 z" /> + d="m 124,147 -3,3 3,3 1,-1 0,-1 2,0 0,-2 -2,0 0,-1 -1,-1 z" /> + d="m 136,147 -1,1 0,1 -2,0 0,2 2,0 0,1 1,1 3,-3 -3,-3 z" /> + d="m 130,148 c -1.10457,0 -2,0.89543 -2,2 0,1.10457 0.89543,2 2,2 1.10457,0 2,-0.89543 2,-2 0,-1.10457 -0.89543,-2 -2,-2 z" /> + d="m 129,153 0,2 -1,0 -1,1 3,3 3,-3 -1,-1 -1,0 0,-2 -2,0 z" /> + d="m 190,148 c -1.10457,0 -2,0.89543 -2,2 0,1.10457 0.89543,2 2,2 1.10457,0 2,-0.89543 2,-2 0,-1.10457 -0.89543,-2 -2,-2 z" /> + d="m 164,154 c -1.10457,0 -2,0.89543 -2,2 0,1.10457 0.89543,2 2,2 1.10457,0 2,-0.89543 2,-2 0,-1.10457 -0.89543,-2 -2,-2 z" /> + d="m 176,154 c -1.10457,0 -2,0.89543 -2,2 0,1.10457 0.89543,2 2,2 1.10457,0 2,-0.89543 2,-2 0,-1.10457 -0.89543,-2 -2,-2 z" /> + d="m 26.0625,154 c -0.641658,-0.0219 -1.281719,0.25399 -1.6875,0.8125 -0.64925,0.89362 -0.456116,2.16325 0.4375,2.8125 0.893616,0.64925 2.16325,0.45612 2.8125,-0.4375 0.64925,-0.89362 0.456116,-2.16325 -0.4375,-2.8125 -0.335106,-0.24347 -0.740005,-0.36184 -1.125,-0.375 z" /> + d="m 33.9375,154 c -0.384995,0.0132 -0.789894,0.13153 -1.125,0.375 -0.893615,0.64925 -1.08675,1.91888 -0.4375,2.8125 0.64925,0.89362 1.918885,1.08675 2.8125,0.4375 0.893615,-0.64925 1.08675,-1.91888 0.4375,-2.8125 -0.405781,-0.55851 -1.045842,-0.83443 -1.6875,-0.8125 z" /> + d="m 27.03125,142.60657 c -1.32908,0.53118 -2.444369,1.41916 -3.3125,2.53125 0.07092,0.0184 0.149734,0.009 0.21875,0.0312 0.520689,0.16918 0.941853,0.48986 1.28125,0.875 0.583365,-0.69731 1.320919,-1.23792 2.15625,-1.625 -0.228003,-0.42194 -0.375,-0.89709 -0.375,-1.4062 0,-0.14028 0.0122,-0.27085 0.03125,-0.40625 z m 5.9375,0 C 32.9878,142.74197 33,142.87254 33,143.01282 c 0,0.50911 -0.146997,0.98426 -0.375,1.40625 0.835331,0.38708 1.572885,0.92769 2.15625,1.625 0.339397,-0.38514 0.760561,-0.70582 1.28125,-0.875 0.06748,-0.0219 0.146303,-0.0395 0.21875,-0.0625 -0.866395,-1.10291 -1.991152,-1.97191 -3.3125,-2.5 z m -10.9375,8.21875 c 0.132811,1.35398 0.618313,2.61506 1.34375,3.6875 0.05422,-0.0956 0.121594,-0.19054 0.1875,-0.28125 0.298579,-0.41096 0.689964,-0.72255 1.125,-0.9375 -0.439267,-0.72096 -0.728373,-1.5606 -0.84375,-2.4375 -0.563589,0.16464 -1.184566,0.16262 -1.78125,-0.0312 -0.009,-0.003 -0.02228,0.003 -0.03125,0 z m 15.90625,0 c -0.596684,0.19387 -1.217661,0.19589 -1.78125,0.0312 -0.115377,0.8769 -0.404483,1.71654 -0.84375,2.4375 0.438042,0.20421 0.821177,0.51932 1.125,0.9375 0.06591,0.0907 0.133279,0.18566 0.1875,0.28125 0.725438,-1.07244 1.210939,-2.33352 1.34375,-3.6875 -0.009,0.003 -0.02224,-0.003 -0.03125,0 z M 29,156.16907 c -0.03014,0.55835 -0.21394,1.114 -0.5625,1.59375 -0.027,0.0372 -0.06534,0.0582 -0.09375,0.0937 0.540476,0.11583 1.081942,0.1875 1.65625,0.1875 0.574308,0 1.115774,-0.0717 1.65625,-0.1875 -0.02841,-0.0355 -0.06675,-0.0566 -0.09375,-0.0937 -0.34856,-0.47975 -0.532362,-1.0354 -0.5625,-1.59375 -0.329444,0.0535 -0.654988,0.0937 -1,0.0937 -0.345012,0 -0.670556,-0.0403 -1,-0.0937 z" /> + d="m 84,146.01282 -4,4 4,4 1,-1 0,-2 2.1875,0 c -0.11352,-0.31647 -0.1875,-0.64447 -0.1875,-1 0,-0.34518 0.0802,-0.69136 0.1875,-1 l -2.1875,0 0,-2 -1,-1 z m 12,0 -1,1 0,2 -2.1875,0 c 0.10728,0.30864 0.1875,0.65482 0.1875,1 0,0.35553 -0.074,0.68353 -0.1875,1 l 2.1875,0 0,2 1,1 4,-4 -4,-4 z" /> + d="m 149,143 -1,1 0,4 -4,0 -1,1 0,2 1,1 4,0 0,4 1,1 2,0 1,-1 0,-4 4,0 1,-1 0,-2 -1,-1 -4,0 0,-4 -1,-1 z" /> + d="m 206,45.000002 -1,1 0,1 3,3 -3,3 0,1 1,1 1,0 3,-3 3,3 1,0 1,-1 0,-1 -3,-3 3,-3 0,-1 -1,-1 -1,0 -3,3 -3,-3 z" /> + d="m 190,140 -1,1 0,1.0625 c -3.94444,0.49381 -7,3.85922 -7,7.9375 0,4.41827 3.58173,8 8,8 4.41827,0 8,-3.58173 8,-8 0,-2.46731 -1.11852,-4.65856 -2.875,-6.125 l -1.40625,1.40625 C 195.11409,146.37996 196,148.08592 196,150 c 0,3.3137 -2.6863,6 -6,6 -3.3137,0 -6,-2.6863 -6,-6 0,-2.97561 2.15859,-5.43327 5,-5.90625 l 0,0.90625 1,1 3,-3 -3,-3 z" /> + d="m 208,142 c -1.33333,0 -2.21875,0.78125 -2.71875,1.28125 -0.5,0.5 -0.61458,0.71875 -1.28125,0.71875 l -1,0 0,2 1,0 c 1.33333,0 2.21875,-0.78125 2.71875,-1.28125 0.5,-0.5 0.61458,-0.71875 1.28125,-0.71875 0.16667,0 0.20569,0.009 0.40625,0.25 0.20056,0.24067 0.4375,0.6875 0.6875,1.1875 0.25,0.5 0.51306,1.05317 0.9375,1.5625 0.42444,0.50933 1.13542,1 1.96875,1 1.33333,0 2.21875,-0.78125 2.71875,-1.28125 0.5,-0.5 0.61458,-0.71875 1.28125,-0.71875 l 1,0 0,-2 -1,0 c -1.33333,0 -2.21875,0.78125 -2.71875,1.28125 -0.5,0.5 -0.61458,0.71875 -1.28125,0.71875 -0.16667,0 -0.20569,-0.009 -0.40625,-0.25 -0.20056,-0.24067 -0.4375,-0.6875 -0.6875,-1.1875 -0.25,-0.5 -0.51306,-1.05317 -0.9375,-1.5625 -0.42444,-0.50933 -1.13542,-1 -1.96875,-1 z" /> + d="m 203,156 0,2 14,0 0,-2 -14,0 z" /> + d="m 425,10 8,7 1,-1 0,-12 -1,-1 z" /> + d="m 24,205 -1,1 0,9 1,1 12,0 1,-1 0,-9 -1,-1 z m 1,3 5,2.8125 5,-2.8125 0,1.5 -5,3 -5,-3 z" /> + d="m 46,203 -1,1 0,12 1,1 8,0 1,-1 0,-12 -1,-1 -8,0 z m 1,2 6,0 0,5 -6,0 0,-5 z m 0,7 6,0 0,1 -6,0 0,-1 z m 0,2 6,0 0,1 -6,0 0,-1 z" /> + d="m 65,204 -1,1 0,8 -2,2 1,1 14,0 1,-1 -2,-2 0,-8 -1,-1 -10,0 z m 1,2 8,0 0,5 -8,0 0,-5 z" /> + d="m 90,202 -5,4 1,1 2,0 0,2 4,0 0,-2 2,0 1,-1 z m -2,9 0,2 -2,0 -1,1 5,4 5,-4 -1,-1 -2,0 0,-2 z" /> + d="m 102.5,205 -1,1 5,10 1.21875,0 2.28125,-3.9375 2.28125,3.9375 1.21875,0 5,-10 -1,-1 -1,0 -3.5,7.5 -1.5,-2.5 2,-4 -0.5,-1 -1.5,0 -1.5,3.5 -1.5,-3.5 -1.5,0 -0.5,1 2,4 -1.5,2.5 -3.5,-7.5 z" /> + d="m 10,202 c -4.418278,0 -8,3.58172 -8,8 0,4.41828 3.581722,8 8,8 4.418278,0 8,-3.58172 8,-8 0,-4.41828 -3.581722,-8 -8,-8 z m 0,2 c 3.313708,0 6,2.68629 6,6 0,3.31371 -2.686292,6 -6,6 -3.3137085,0 -6,-2.68629 -6,-6 0,-3.31371 2.6862915,-6 6,-6 z" /> + d="m 12,207 a 2,2 0 0 1 -4,0 2,2 0 1 1 4,0 z" /> + d="m 8,211 1,-1 2,0 1,1 0,3 -4,0 z" /> + d="m 125,204 -1,1 0,11 1,1 8,0 3,-3 0,-9 -1,-1 -10,0 z m 5,2 c 0.55228,0 1,0.44772 1,1 0,0.55228 -0.44772,1 -1,1 -0.55228,0 -1,-0.44772 -1,-1 0,-0.55228 0.44772,-1 1,-1 z m -4,4 8,0 0,1 -8,0 0,-1 z m 0,2 8,0 0,1 -8,0 0,-1 z m 0,2 5,0 0,1 -5,0 0,-1 z" /> + d="M 146.0625,208.40625 C 144.26792,209.16529 143,210.92893 143,213 c 0,2.76142 2.23858,5 5,5 1.99494,0 3.69772,-1.18524 4.5,-2.875 l -0.65625,-1.125 -1.03125,0 c -0.41551,1.15835 -1.51118,2 -2.8125,2 -1.65685,0 -3,-1.34315 -3,-3 0,-0.88447 0.39109,-1.66968 1,-2.21875 L 146,209 c 0,-0.20559 0.0222,-0.39929 0.0625,-0.59375 z" /> + d="m 149,202 c -1.10456,0 -2,0.89544 -2,2 0,1.10456 0.89544,2 2,2 1.10456,0 2,-0.89544 2,-2 0,-1.10456 -0.89544,-2 -2,-2 z m 0,5 c -1.108,0 -2,0.892 -2,2 l 0,2 c 0,1.108 0.892,2 2,2 l 3.5,0 1.6875,2.90625 c 0.26893,0.4628 0.8482,0.64134 1.3125,0.375 l 0.0312,0 c 0.4643,-0.26634 0.61268,-0.88095 0.34375,-1.34375 l -2.03125,-3.46875 c -0.0158,-0.0271 -0.0446,-0.0375 -0.0625,-0.0625 -0.0468,-0.0617 -0.0966,-0.10697 -0.15625,-0.15625 -0.0773,-0.0673 -0.15761,-0.11687 -0.25,-0.15625 -0.1049,-0.0447 -0.19835,-0.0878 -0.3125,-0.0937 -0.0224,-10e-4 -0.04,-4e-4 -0.0625,0 l -0.0937,0 -1.90625,0 0,-2 c 0,-1.108 -0.892,-2 -2,-2 z" /> + id="g20251-9" + transform="translate(301,239.63781)"> + id="g20247-4" + transform="translate(10.000004,-1.9999957)"> + transform="matrix(1.009009,0,0,1.009009,-410.98649,14.371188)" + d="m 106.03125,-129.34375 a 3.46875,3.46875 0 1 1 -6.9375,0 3.46875,3.46875 0 1 1 6.9375,0 z" /> + transform="matrix(0.72072059,0,0,0.72072059,-381.41891,-22.917117)" + d="m 106.03125,-129.34375 a 3.46875,3.46875 0 1 1 -6.9375,0 3.46875,3.46875 0 1 1 6.9375,0 z" /> + id="g20283" + transform="translate(301,200.63781)"> + id="g20285" + transform="translate(10.000004,-1.9999957)"> + transform="matrix(1.009009,0,0,1.009009,-410.98649,15.371188)" + d="m 106.03125,-129.34375 a 3.46875,3.46875 0 1 1 -6.9375,0 3.46875,3.46875 0 1 1 6.9375,0 z" /> + transform="matrix(0.72072059,0,0,0.72072059,-381.41891,-21.917117)" + d="m 106.03125,-129.34375 a 3.46875,3.46875 0 1 1 -6.9375,0 3.46875,3.46875 0 1 1 6.9375,0 z" /> + id="g20299" + transform="translate(340,239.63781)"> + id="g20301" + transform="translate(10.000004,-1.9999957)"> + transform="matrix(1.009009,0,0,1.009009,-411.98649,14.371188)" + d="m 106.03125,-129.34375 a 3.46875,3.46875 0 1 1 -6.9375,0 3.46875,3.46875 0 1 1 6.9375,0 z" /> + transform="matrix(0.72072059,0,0,0.72072059,-382.41891,-22.917117)" + d="m 106.03125,-129.34375 a 3.46875,3.46875 0 1 1 -6.9375,0 3.46875,3.46875 0 1 1 6.9375,0 z" /> + d="m 44.999996,83.499995 a 3.5,3.5 0 0 1 -7,0 3.5,3.5 0 1 1 7,0 z" /> + d="m 43.999995,83.499989 a 2.4999995,2.4999995 0 0 1 -4.999999,0 2.4999995,2.4999995 0 1 1 4.999999,0 z" /> + style="opacity:0.2;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.009009;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + d="m 454,10 -8,7 -1,-1 0,-12 1,-1 z" /> + id="g4611" + transform="matrix(0.390625,0,0,0.390625,842.1875,238.60852)"> + y="-47.637821" + x="-2156" + height="256" + width="256" /> + id="g4494" + transform="translate(-781,47)"> + transform="matrix(2.56,0,0,2.56,-1375,-657.83782)" + d="m 3.90625,224.6875 7.8125,20.5 -7.8125,20.5 7.8125,20.53125 -7.8125,20.5 4.8125,1.84375 L 34.75,282.5 l 4.6875,0.71875 5.6875,-5.6875 c -3.349219,-3.88012 -5.88,-8.51217 -7.25,-13.625 L 40.5625,261.75 c -0.457539,-2.1233 -0.71875,-4.33618 -0.71875,-6.59375 0,-11.27802 6.012891,-21.19153 15,-26.6875 L 44.9375,224.6875 24.40625,232.5 l -20.5,-7.8125 z M 71,229 c -14.359404,0 -26,11.6406 -26,26 0,14.3594 11.640596,26 26,26 14.359404,0 26,-11.6406 26,-26 0,-14.3594 -11.640596,-26 -26,-26 z m 20.0625,50.15625 c -5.42082,4.51861 -12.380625,7.25 -19.96875,7.25 -2.257578,0 -4.47043,-0.26122 -6.59375,-0.71875 l -2.15625,2.65625 c -4.855312,-1.30096 -9.306172,-3.58895 -13.0625,-6.6875 l -5.875,5.875 0.71875,4.34375 -21.8125,21.84375 2.09375,0.8125 20.53125,-7.8125 20.5,7.8125 20.5,-7.8125 7.8125,-20.5 -2.6875,-7.0625 z" /> + id="g8629" + transform="matrix(0.5,0,0,0.5,-342.5001,125.49999)"> + y="238.99998" + x="935" + height="100.00001" + width="100.00001" /> + d="m 1035,259.46976 c -3.679,1.6066 -7.6336,2.69227 -11.7831,3.18013 4.2351,-2.49939 7.4885,-6.45736 9.0205,-11.17402 -3.9645,2.31516 -8.3552,3.99577 -13.0286,4.90089 -3.7418,-3.92491 -9.0738,-6.37759 -14.9751,-6.37759 -11.33021,0 -20.51679,9.04283 -20.51679,20.19639 0,1.58324 0.18008,3.12476 0.53217,4.60303 -17.05108,-0.8424 -32.16868,-8.88327 -42.28747,-21.10256 -1.76581,2.98278 -2.77795,6.45238 -2.77795,10.15396 0,7.00715 3.62183,13.1887 9.12688,16.81077 -3.36304,-0.1076 -6.52651,-1.01351 -9.29247,-2.52616 -0.002,0.084 -0.002,0.16796 -0.002,0.25194 0,9.78524 7.07195,17.94866 16.45764,19.80431 -1.72156,0.46135 -3.53416,0.70856 -5.40515,0.70856 -1.32202,0 -2.60724,-0.12596 -3.85969,-0.36215 2.61039,8.02355 10.18701,13.86315 19.16488,14.02559 -7.02158,5.41762 -15.8675,8.64629 -25.48033,8.64629 -1.65593,0 -3.28903,-0.0945 -4.89377,-0.28289 9.07933,5.73043 19.86348,9.07458 31.44972,9.07458 37.73703,0 58.37353,-30.77522 58.37353,-57.46492 0,-0.87573 -0.037,-1.74674 -0.063,-2.61276 4.0095,-2.84789 7.4872,-6.40461 10.238,-10.45497" /> + transform="translate(-25,50.000003)"> - - - + + + + transform="translate(25,-50.000003)" + d="m 464,2 -1,1 0,13 1,1 11,0 1,-1 0,-10 -4,0 0,6 -2.5,-1.5 -2.5,1.5 0,-6 -2,0 0,-1 0,-1 11,0 0,-1 0,-1 -12,0 z" /> + id="g3837" + transform="translate(-26,3.0624999e-6)" /> + transform="translate(-25,20.000003)" /> + y="220" + x="200" + height="100" + width="100" /> + transform="translate(550,-25)"> + d="m -145,255 c 0,24.88542 0,49.77083 0,74.65625 7.5,3.44792 15,6.89583 22.5,10.34375 7.5,-3.33333 15,-6.66667 22.5,-10 7.5,3.33333 15,6.66667 22.5,10 7.5,-3.44792 15,-6.89583 22.5,-10.34375 0,-24.88542 0,-49.77083 0,-74.65625 -7.5,3.33333 -15,6.66667 -22.5,10 -7.5,-3.33333 -15,-6.66667 -22.5,-10 -7.5,3.33333 -15,6.66667 -22.5,10 -7.5,-3.33333 -15,-6.66667 -22.5,-10 z m 57.40625,23.59375 5.59375,5.59375 5.59375,-5.59375 2.46236,3.16264 -5.24361,5.24361 5.59375,5.59375 -3.16264,2.46236 L -82,289.8125 l -5.59375,5.59375 -3.08736,-2.97514 5.86861,-5.43111 -5.59375,-5.59375 z m -25.1875,12.3125 c 3.02949,0.26492 6.03959,1.32487 8.375,3.31225 l -2.90625,2.78125 c -1.69778,-1.26665 -3.7758,-1.93771 -5.87485,-2.1247 0.13531,-1.3229 0.27106,-2.64608 0.4061,-3.9688 z m -4.84375,0.375 c 0.23428,1.25604 1.33288,2.86593 1.28125,3.81245 -1.9797,1.00126 -3.28203,2.99612 -4.4062,4.74985 l -3.14878,-2.51673 c 1.59525,-2.42637 3.50946,-4.89188 6.27373,-6.04557 z m 33.46875,6.28125 3.6875,1.59375 c -0.85495,2.6344 -2.05943,5.19957 -3.8749,7.3124 l -2.78125,-2.90625 c 1.37974,-1.77526 2.31524,-3.85778 2.96865,-5.9999 z m -17.21875,0.0937 c 1.52998,2.21288 2.46049,4.7627 3.84365,7.06245 l -3.59375,1.78125 c -1.26185,-2.16828 -2.15272,-4.55761 -3.5936,-6.6249 1.11453,-0.7396 2.22935,-1.47918 3.3437,-2.2188 z m -24.75,3.8125 3.4375,2.03125 c -0.94627,2.15394 -1.82847,4.38347 -2.53105,6.59365 l -3.66832,-1.66122 c 0.79087,-2.367 1.68957,-4.70832 2.76187,-6.96368 z m 36.25,4.625 1.60017,3.68116 c -2.71921,1.52252 -6.25719,2.08348 -9.13142,0.66264 l 2.16859,-3.41112 c 1.76867,0.54341 3.84526,0.007 5.36266,-0.93268 z" /> + y="220" + x="300" + height="100" + width="100" /> + d="m 567.5314,229 c -8.2842,0 -15,6.71574 -15,15 0,8.28429 15,21 15,21 0,0 15,-12.71571 15,-21 0,-8.28426 -6.7158,-15 -15,-15 z M 568,238 c 3.3138,0 6,2.68629 6,6 0,3.31371 -2.6862,6 -6,6 -3.3135,0 -6,-2.68629 -6,-6 0,-3.31371 2.6865,-6 6,-6 z" /> + d="m 583.5,266 c -4.14214,0 -7.5,3.35786 -7.5,7.5 0,0.75557 0.13591,1.46849 0.34375,2.15625 l -18.6875,18.6875 C 556.96849,294.13591 556.25557,294 555.5,294 c -4.14214,0 -7.5,3.35786 -7.5,7.5 0,4.14214 3.35786,7.5 7.5,7.5 4.14214,0 7.5,-3.35786 7.5,-7.5 0,-0.75557 -0.13591,-1.46849 -0.34375,-2.15625 l 18.6875,-18.6875 C 582.03151,280.86409 582.74443,281 583.5,281 c 4.14214,0 7.5,-3.35786 7.5,-7.5 0,-4.14214 -3.35786,-7.5 -7.5,-7.5 z m 0,4.5 c 1.65685,0 3,1.34315 3,3 0,1.65685 -1.34315,3 -3,3 -1.65685,0 -3,-1.34315 -3,-3 0,-1.65685 1.34315,-3 3,-3 z m -28,28 c 1.65685,0 3,1.34315 3,3 0,1.65685 -1.34315,3 -3,3 -1.65685,0 -3,-1.34315 -3,-3 0,-1.65685 1.34315,-3 3,-3 z" /> + d="m 516.5,251 c -4.14214,0 -7.5,3.35786 -7.5,7.5 0,2.8726 1.62846,5.3652 4,6.625 l 0,13.75 c -2.37154,1.2598 -4,3.7524 -4,6.625 0,4.14214 3.35786,7.5 7.5,7.5 2.8726,0 5.3652,-1.62846 6.625,-4 l 13.75,0 c 1.2598,2.37154 3.7524,4 6.625,4 4.14214,0 7.5,-3.35786 7.5,-7.5 0,-2.8726 -1.62846,-5.3652 -4,-6.625 l 0,-13.75 c 2.37154,-1.2598 4,-3.7524 4,-6.625 0,-4.14214 -3.35786,-7.5 -7.5,-7.5 -2.90753,0 -5.41167,1.67488 -6.65625,4.09375 L 536.65625,255 523.125,255 c -1.2598,-2.37154 -3.7524,-4 -6.625,-4 z m 0,4.5 c 1.65685,0 3,1.34315 3,3 0,1.65685 -1.34315,3 -3,3 -1.65685,0 -3,-1.34315 -3,-3 0,-1.65685 1.34315,-3 3,-3 z m 27,0 c 1.65685,0 3,1.34315 3,3 0,1.65685 -1.34315,3 -3,3 -1.65685,0 -3,-1.34315 -3,-3 0,-1.65685 1.34315,-3 3,-3 z m -20.375,6.5 13.75,0 c 0.70304,1.32345 1.80155,2.42196 3.125,3.125 l 0,13.75 c -1.32345,0.70304 -2.42196,1.80155 -3.125,3.125 l -13.75,0 c -0.70304,-1.32345 -1.80155,-2.42196 -3.125,-3.125 l 0,-13.75 c 1.32345,-0.70304 2.42196,-1.80155 3.125,-3.125 z m -6.625,20.5 c 1.65685,0 3,1.34315 3,3 0,1.65685 -1.34315,3 -3,3 -1.65685,0 -3,-1.34315 -3,-3 0,-1.65685 1.34315,-3 3,-3 z m 27,0 c 1.65685,0 3,1.34315 3,3 0,1.65685 -1.34315,3 -3,3 -1.65685,0 -3,-1.34315 -3,-3 0,-1.65685 1.34315,-3 3,-3 z" /> + id="g7659" + transform="translate(-25,3.0624999e-6)" /> + id="g7663" + transform="translate(-53,28.000003)" /> + id="g7690" + transform="translate(-23.375,1.375)" /> + id="g7694" + transform="translate(-50.375,1.375)" /> + id="g7700" + transform="translate(-23.375,-25.625)" /> + id="g7706" + transform="translate(-50.375,-25.625)" /> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + y="220" + x="600" + height="100" + width="100" /> + transform="translate(-25,3.0624999e-6)" /> + d="m 228.78571,5.07143 -1,1 v 1 l 1,1 h 1 l 1,-1 v -1 l -1,-1 h -1 z m 0,4 -1,1 v 5 l 1,1 h 1 l 1,-1 v -5 l -1,-1 h -1 z" /> + d="m 228.78571,45.07143 -1,1 0,1 1,1 1,0 1,-1 0,-1 -1,-1 -1,0 z m 0,4 -1,1 0,5 1,1 1,0 1,-1 0,-5 -1,-1 -1,0 z" /> + d="m 228.78571,25.07143 -1,1 0,1 1,1 1,0 1,-1 0,-1 -1,-1 -1,0 z m 0,4 -1,1 0,5 1,1 1,0 1,-1 0,-5 -1,-1 -1,0 z" /> + d="m 406,3 0,1 2,2.40625 L 408,8 412,8 412,6.40625 414,4 l 0,-1 -1,0 -2,2 -2,0 -2,-2 -1,0 z m 1,6 -1,1 -3,0 -1,1 1,1 3,0 0,1 -2,1 -1,1 0,1 1,0 2,-1 2,2 4,0 2,-2 2,1 1,0 0,-1 -1,-1 -2,-1 0,-1 3,0 1,-1 -1,-1 -3,0 -1,-1 -6,0 z" /> + d="m 406,23 0,1 2,2.40625 0,1.59375 4,0 0,-1.59375 2,-2.40625 0,-1 -1,0 -2,2 -2,0 -2,-2 -1,0 z m 1,6 -1,1 -3,0 -1,1 1,1 3,0 0,1 -2,1 -1,1 0,1 1,0 2,-1 2,2 4,0 2,-2 2,1 1,0 0,-1 -1,-1 -2,-1 0,-1 3,0 1,-1 -1,-1 -3,0 -1,-1 -6,0 z" /> + d="m 406,43 0,1 2,2.40625 0,1.59375 4,0 0,-1.59375 2,-2.40625 0,-1 -1,0 -2,2 -2,0 -2,-2 -1,0 z m 1,6 -1,1 -3,0 -1,1 1,1 3,0 0,1 -2,1 -1,1 0,1 1,0 2,-1 2,2 4,0 2,-2 2,1 1,0 0,-1 -1,-1 -2,-1 0,-1 3,0 1,-1 -1,-1 -3,0 -1,-1 -6,0 z" /> + d="m 482,27 2,0 3,3 4.7735,-6 2.2265,0 0,2 -5,6 -1,1 -2,0 -1,-1 -3,-3 z" /> + d="m 482,7.0000001 2,0 L 487,10 l 4.7735,-5.9999999 2.2265,0 0,2 L 489,12 l -1,1 -2,0 -1,-1 -3,-2.9999999 z" /> + id="g16825" + transform="translate(-5,3.0624999e-6)"> + transform="translate(25,-3.0624999e-6)" + d="m 524,5 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 10,0 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m -9,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m 10,0 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m -6,6 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m 558.0625,9.21875 -1.5,2.5 0.875,0.53125 1.5,-2.5 -0.875,-0.53125 z" /> + d="m 551.9375,9.21875 -0.875,0.53125 1.5,2.5 0.875,-0.53125 -1.5,-2.5 z" /> + id="g17870" + transform="translate(5432.5,-1245.375)"> + d="m -4868.5,1248.3622 -1,1 0,2 1,1 0,6 -1,1 0,2 1,1 2,0 1,-1 6,0 1,1 2,0 1,-1 0,-2 -1,-1 0,-6 1,-1 0,-2 -1,-1 -2,0 -1,1 -6,0 -1,-1 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m 10,0 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m -8,2 6,0 1,1 0,6 -1,1 -6,0 -1,-1 0,-6 z m -2,8 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m 10,0 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m -4863.5,1253.3622 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + id="g16816" + transform="translate(-74,-0.99999694)"> + d="m 598,4.9872 0,0 -1,1 0,1 0,1 0,1 1,1 0,0 1,1 1,0 1,0 1,0 1,-1 0,0 1,-1 0,-1 0,-1 0,-1 -1,-1 0,0 -1,-1 -1,0 -1,0 -1,0 z m 2,1 1,0 1,1 0,1 -1,1 -1,0 -1,-1 0,-1 z" /> + d="m 608,5.9872 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m 606,11.9872 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m 600,13.9872 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m 604.34375,10.625 -0.6875,0.71875 1,1 0.6875,-0.71875 -1,-1 z" /> + d="m 605,7 0,1 1,0 0,-1 -1,0 z" /> + d="m 600,12 0,1 1,0 0,-1 -1,0 z" /> + transform="translate(-5,20.000003)"> - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + transform="translate(25,-3.0624999e-6)" + d="m 524,5 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 10,0 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m -9,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m 10,0 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m -6,6 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m 558.0625,9.21875 -1.5,2.5 0.875,0.53125 1.5,-2.5 -0.875,-0.53125 z" /> + d="m 551.9375,9.21875 -0.875,0.53125 1.5,2.5 0.875,-0.53125 -1.5,-2.5 z" /> + transform="translate(5432.5,-1205.375)"> + d="m -4868.5,1248.3622 -1,1 0,2 1,1 0,6 -1,1 0,2 1,1 2,0 1,-1 6,0 1,1 2,0 1,-1 0,-2 -1,-1 0,-6 1,-1 0,-2 -1,-1 -2,0 -1,1 -6,0 -1,-1 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m 10,0 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m -8,2 6,0 1,1 0,6 -1,1 -6,0 -1,-1 0,-6 z m -2,8 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z m 10,0 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m -4863.5,1253.3622 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + transform="translate(-74,39.000003)"> + d="m 598,4.9872 0,0 -1,1 0,1 0,1 0,1 1,1 0,0 1,1 1,0 1,0 1,0 1,-1 0,0 1,-1 0,-1 0,-1 0,-1 -1,-1 0,0 -1,-1 -1,0 -1,0 -1,0 z m 2,1 1,0 1,1 0,1 -1,1 -1,0 -1,-1 0,-1 z" /> + d="m 608,5.9872 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m 606,11.9872 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m 600,13.9872 -1,1 0,2 1,1 2,0 1,-1 0,-2 -1,-1 -2,0 z m 1,1 c 0.5523,0 1,0.4477 1,1 0,0.5523 -0.4477,1 -1,1 -0.5523,0 -1,-0.4477 -1,-1 0,-0.5523 0.4477,-1 1,-1 z" /> + d="m 604.34375,10.625 -0.6875,0.71875 1,1 0.6875,-0.71875 -1,-1 z" /> + d="m 605,7 0,1 1,0 0,-1 -1,0 z" /> + d="m 600,12 0,1 1,0 0,-1 -1,0 z" /> + id="path6484" /> + d="m 9,44 -7,11 1,1 14,0 1,-1 -7,-11 z m 1,3 1,1 0,2 -1,1 -1,-1 0,-2 z m 0,5 1,1 -1,1 -1,-1 z" + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + d="m 332.5,251.25 c -1.385,0 -2.5,1.115 -2.5,2.5 l 0,37.5 c 0,1.385 1.115,2.5 2.5,2.5 l 35,0 c 1.385,0 2.5,-1.115 2.5,-2.5 l 0,-37.5 c 0,-1.385 -1.115,-2.5 -2.5,-2.5 l -35,0 z m 12.42187,8.95312 7.73438,0 -1.73438,1.26563 -2.45312,0 c 1.62608,0.62541 2.48437,2.50808 2.48437,4.45312 0,1.6339 -0.8932,3.04778 -2.17187,4.04688 -1.2477,0.97471 -1.48438,1.37445 -1.48438,2.20312 0,0.70735 1.33236,1.91155 2.03125,2.40625 2.04292,1.44503 2.70313,2.79195 2.70313,5.03125 0,2.79312 -2.69863,5.5625 -7.59375,5.5625 -4.29379,0 -7.92188,-1.73845 -7.92188,-4.53125 0,-2.83563 3.30028,-5.57812 7.59375,-5.57812 l 1.34375,0 c -0.58757,-0.57164 -1.04687,-1.27693 -1.04687,-2.14063 0,-0.51252 0.16172,-1.01502 0.39062,-1.45312 -0.23328,0.0169 -0.47328,0.0312 -0.71875,0.0312 -3.52671,0 -5.89062,-2.51638 -5.89062,-5.625 0,-3.04233 3.2677,-5.67188 6.73437,-5.67188 z M 359.5,260.75 l 1.5,0 0,4.5 4.5,0 0,1.5 -4.5,0 0,4.5 -1.5,0 0,-4.5 -4.5,0 0,-1.5 4.5,0 0,-4.5 z m -15.90625,0.5625 c -1.78652,0.18034 -2.94719,2.09397 -2.625,4.54687 0.34366,2.61549 2.24211,4.78433 4.23437,4.84375 0.0279,10e-4 0.0503,0 0.0781,0 1.94503,0 3.24491,-2.04547 2.90625,-4.625 -0.34398,-2.6158 -2.24274,-4.70589 -4.23438,-4.76562 -0.12447,-0.004 -0.24027,-0.012 -0.35937,0 z m 1.34375,14.46875 c -2.96791,-0.0322 -5.625,1.81947 -5.625,4.03125 0,2.2565 2.14147,4.125 5.10937,4.125 4.17246,0 5.625,-1.75882 5.625,-4.01563 0,-0.27206 -0.0325,-0.53796 -0.0937,-0.79687 -0.32583,-1.2771 -1.48237,-1.91083 -3.09375,-3.03125 -0.58601,-0.18951 -1.22922,-0.30529 -1.92187,-0.3125 z" + style="color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + style="display:inline" + transform="translate(276,-4.9999996)"> + style="display:inline" + id="g6893-0"> + inkscape:connector-curvature="0" /> + style="fill:#222222;fill-opacity:1;display:inline" + id="g7098"> + y="145" /> + style="fill:#222222;fill-opacity:1;display:inline" + transform="translate(-21,0.2499569)"> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + style="opacity:0.5;color:#000000;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + d="m 514,84 -1,1 0,1.59375 L 506.59375,93 505,93 l -1,1 0,2 1,1 2,0 1,-1 0,-1.59375 L 514.40625,88 516,88 l 1,-1 0,-2 -1,-1 z m -22,1 c -2.76142,0 -5,2.23858 -5,5 0,2.76143 5,7 5,7 0,0 5,-4.23857 5,-7 0,-2.76142 -2.23858,-5 -5,-5 z m 23,0 c 0.55228,0 1,0.44772 1,1 0,0.55229 -0.44772,1 -1,1 -0.25152,0 -0.48052,-0.0967 -0.65625,-0.25 -0.0344,-0.03002 -0.0638,-0.05934 -0.0937,-0.09375 -0.15335,-0.175731 -0.25,-0.404729 -0.25,-0.65625 0,-0.55228 0.44772,-1 1,-1 z m 10,0 -1,1 0,2 1,1 0,4 -1,1 0,2 1,1 2,0 1,-1 4,0 1,1 2,0 1,-1 0,-2 -1,-1 0,-4 1,-1 0,-2 -1,-1 -2,0 -1,1 -4,0 -1,-1 z m 1,1 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z m 8,0 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z m -41.84375,2 c 1.10457,0 2,0.89543 2,2 0,1.10457 -0.89543,2 -2,2 -1.10457,0 -2,-0.89543 -2,-2 0,-1.10457 0.89543,-2 2,-2 z M 528,88 l 4,0 1,1 0,4 -1,1 -4,0 -1,-1 0,-4 z m -22,6 c 0.25152,0 0.48052,0.0967 0.65625,0.25 l 0.0937,0.09375 c 0.15335,0.175731 0.25,0.404734 0.25,0.65625 0,0.55229 -0.44772,1 -1,1 -0.55228,0 -1,-0.44771 -1,-1 0,-0.55228 0.44772,-1 1,-1 z m 20,0 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z m 8,0 c 0.55228,0 1,0.447715 1,1 0,0.552285 -0.44772,1 -1,1 -0.55228,0 -1,-0.447715 -1,-1 0,-0.552285 0.44772,-1 1,-1 z" /> + style="color:#000000;fill:#8cd05f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccccccccccccsscsscssccscccccccccccccccccccccccccccssssssssssssssscccccccccsccsssssssssssssss" /> + style="color:#000000;fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + style="opacity:0.25;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + id="path3414" /> + d="m 41.499631,103.99936 a 1.5000005,1.5000005 0 0 1 10e-7,-3 1.5000005,1.5000005 0 1 1 -10e-7,3 z" /> + d="m 24.000004,121.5 a 1.5,1.5 0 0 1 -3,0 1.5,1.5 0 1 1 3,0 z" /> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00900900000000004;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;opacity:0.20000000000000001" + inkscape:connector-curvature="0" /> - + - + + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00900900000000004;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;opacity:0.5" + transform="matrix(0,1,-1,0,0,0)" + rx="0.5" + ry="0.5" /> + rx="0.5" + ry="0.5" /> + id="g3360" + transform="matrix(0,1,-1,0,45,203)"> + d="m -111,-45 -8,9.01282 1,1 4,0 0,10.59375 c -2.92023,1.19221 -4.99998,4.05725 -5,7.40625 4e-5,4.41829 3.58172,7.98721 8,7.98718 4.41827,10e-6 8,-3.5689 8,-7.98718 2e-5,-3.36351 -2.05966,-6.25472 -5,-7.4375 l 0,-10.5625 4,0 1,-1 z" + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#8cd05f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000000000000000;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + d="m 135,92 -9.01282,-8 -1,1 0,4 -10.59375,0 c -1.19221,-2.92023 -4.05725,-4.99998 -7.40625,-5 -4.41829,4e-5 -7.98721,3.58172 -7.98718,8 -1e-5,4.41827 3.5689,8 7.98718,8 3.36351,2e-5 6.25472,-2.05966 7.4375,-5 l 10.5625,0 0,4 1,1 z" + style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000000000000000;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" /> + id="g4190" + transform="matrix(0,1,1,0,116.9905,-80.00893)"> + transform="matrix(0,1,1,0,80.00893,-71.9905)" + inkscape:connector-curvature="0" /> + inkscape:connector-curvature="0" /> + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + id="g4206" + transform="matrix(0,1,1,0,167,-115)"> + inkscape:connector-curvature="0" + sodipodi:nodetypes="ssccccccsscccccss" /> + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccccssccccc" /> + inkscape:connector-curvature="0" /> ONLY + y="126.18999">ONLY + id="g4190-7" + transform="matrix(0,1,1,0,206.9905,-80.00893)"> + transform="matrix(0,1,1,0,80.00893,-71.9905)" + inkscape:connector-curvature="0" /> + inkscape:connector-curvature="0" /> + style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + inkscape:connector-curvature="0" /> + + + + + + + style="fill:#ffffff;fill-opacity:1" + transform="translate(-7.84375,79.90625)" + id="g6884"> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + d="m 615.84375,-57.90625 -1,1 0,1.59375 -4.40625,4.40625 -1.59375,0 -1,1 0,2 1,1 2,0 1,-1 0,-1.59375 4.40625,-4.40625 1.59375,0 1,-1 0,-2 -1,-1 z" + id="path6886" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccccccccccccccc" /> - + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + d="m 615.84375,-50.90625 -1,1 0,2 1,1 0,2 -1,1 0,2 1,1 2,0 1,-1 2,0 1,1 2,0 1,-1 0,-2 -1,-1 0,-2 1,-1 0,-2 -1,-1 -2,0 -1,1 -2,0 -1,-1 z m 3,3 2,0 1,1 0,2 -1,1 -2,0 -1,-1 0,-2 z" + id="path6888" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccccccccccccccccccccccccccccc" /> + style="fill:#000000;fill-opacity:1;opacity:0.5" + transform="translate(-7.84375,99.90625)" + id="g6890"> + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + d="m 615.84375,-57.90625 -1,1 0,1.59375 -4.40625,4.40625 -1.59375,0 -1,1 0,2 1,1 2,0 1,-1 0,-1.59375 4.40625,-4.40625 1.59375,0 1,-1 0,-2 -1,-1 z" + id="path6892" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccccccccccccccc" /> - - - - - - - - - - - - - - - - - - - - - + style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + d="m 615.84375,-50.90625 -1,1 0,2 1,1 0,2 -1,1 0,2 1,1 2,0 1,-1 2,0 1,1 2,0 1,-1 0,-2 -1,-1 0,-2 1,-1 0,-2 -1,-1 -2,0 -1,1 -2,0 -1,-1 z m 3,3 2,0 1,1 0,2 -1,1 -2,0 -1,-1 0,-2 z" + id="path6894" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccccccccccccccccccccccccccccc" /> From 8c423651b24ee2e22f06c0d1916ca50b52841b27 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 22 Oct 2014 13:19:49 -0400 Subject: [PATCH 13/41] Update help text because GPS tracks are purple now --- data/core.yaml | 2 +- dist/locales/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 0d7c35ff9..3f2de3b85 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -481,7 +481,7 @@ en: [Surveying with a GPS](http://learnosm.org/en/beginner/using-gps/). To use a GPX track for mapping, drag and drop the GPX file onto the map - editor. If it's recognized, it will be added to the map as a bright green + editor. If it's recognized, it will be added to the map as a bright purple line. Click on the 'Map Data' menu on the right side to enable, disable, or zoom to this new GPX-powered layer. diff --git a/dist/locales/en.json b/dist/locales/en.json index 476187314..507abc9d9 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -448,7 +448,7 @@ "help": "# Help\n\nThis is an editor for [OpenStreetMap](http://www.openstreetmap.org/), the\nfree and editable map of the world. You can use it to add and update\ndata in your area, making an open-source and open-data map of the world\nbetter for everyone.\n\nEdits that you make on this map will be visible to everyone who uses\nOpenStreetMap. In order to make an edit, you'll need a\n[free OpenStreetMap account](https://www.openstreetmap.org/user/new).\n\nThe [iD editor](http://ideditor.com/) is a collaborative project with [source\ncode available on GitHub](https://github.com/openstreetmap/iD).\n", "editing_saving": "# Editing & Saving\n\nThis editor is designed to work primarily online, and you're accessing\nit through a website right now.\n\n### Selecting Features\n\nTo select a map feature, like a road or point of interest, click\non it on the map. This will highlight the selected feature, open a panel with\ndetails about it, and show a menu of things you can do with the feature.\n\nTo select multiple features, hold down the 'Shift' key. Then either click\non the features you want to select, or drag on the map to draw a rectangle.\nThis will draw a box and select all the points within it.\n\n### Saving Edits\n\nWhen you make changes like editing roads, buildings, and places, these are\nstored locally until you save them to the server. Don't worry if you make\na mistake - you can undo changes by clicking the undo button, and redo\nchanges by clicking the redo button.\n\nClick 'Save' to finish a group of edits - for instance, if you've completed\nan area of town and would like to start on a new area. You'll have a chance\nto review what you've done, and the editor supplies helpful suggestions\nand warnings if something doesn't seem right about the changes.\n\nIf everything looks good, you can enter a short comment explaining the change\nyou made, and click 'Save' again to post the changes\nto [OpenStreetMap.org](http://www.openstreetmap.org/), where they are visible\nto all other users and available for others to build and improve upon.\n\nIf you can't finish your edits in one sitting, you can leave the editor\nwindow and come back (on the same browser and computer), and the\neditor application will offer to restore your work.\n", "roads": "# Roads\n\nYou can create, fix, and delete roads with this editor. Roads can be all\nkinds: paths, highways, trails, cycleways, and more - any often-crossed\nsegment should be mappable.\n\n### Selecting\n\nClick on a road to select it. An outline should become visible, along\nwith a small tools menu on the map and a sidebar showing more information\nabout the road.\n\n### Modifying\n\nOften you'll see roads that aren't aligned to the imagery behind them\nor to a GPS track. You can adjust these roads so they are in the correct\nplace.\n\nFirst click on the road you want to change. This will highlight it and show\ncontrol points along it that you can drag to better locations. If\nyou want to add new control points for more detail, double-click a part\nof the road without a node, and one will be added.\n\nIf the road connects to another road, but doesn't properly connect on\nthe map, you can drag one of its control points onto the other road in\norder to join them. Having roads connect is important for the map\nand essential for providing driving directions.\n\nYou can also click the 'Move' tool or press the `M` shortcut key to move the entire road at\none time, and then click again to save that movement.\n\n### Deleting\n\nIf a road is entirely incorrect - you can see that it doesn't exist in satellite\nimagery and ideally have confirmed locally that it's not present - you can delete\nit, which removes it from the map. Be cautious when deleting features -\nlike any other edit, the results are seen by everyone and satellite imagery\nis often out of date, so the road could simply be newly built.\n\nYou can delete a road by clicking on it to select it, then clicking the\ntrash can icon or pressing the 'Delete' key.\n\n### Creating\n\nFound somewhere there should be a road but there isn't? Click the 'Line'\nicon in the top-left of the editor or press the shortcut key `2` to start drawing\na line.\n\nClick on the start of the road on the map to start drawing. If the road\nbranches off from an existing road, start by clicking on the place where they connect.\n\nThen click on points along the road so that it follows the right path, according\nto satellite imagery or GPS. If the road you are drawing crosses another road, connect\nit by clicking on the intersection point. When you're done drawing, double-click\nor press 'Return' or 'Enter' on your keyboard.\n", - "gps": "# GPS\n\nGPS data is the most trusted source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Surveying with a GPS](http://learnosm.org/en/beginner/using-gps/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright green\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n", + "gps": "# GPS\n\nGPS data is the most trusted source of data for OpenStreetMap. This editor\nsupports local traces - `.gpx` files on your local computer. You can collect\nthis kind of GPS trace with a number of smartphone applications as well as\npersonal GPS hardware.\n\nFor information on how to perform a GPS survey, read\n[Surveying with a GPS](http://learnosm.org/en/beginner/using-gps/).\n\nTo use a GPX track for mapping, drag and drop the GPX file onto the map\neditor. If it's recognized, it will be added to the map as a bright purple\nline. Click on the 'Map Data' menu on the right side to enable,\ndisable, or zoom to this new GPX-powered layer.\n\nThe GPX track isn't directly uploaded to OpenStreetMap - the best way to\nuse it is to draw on the map, using it as a guide for the new features that\nyou add, and also to [upload it to OpenStreetMap](http://www.openstreetmap.org/trace/create)\nfor other users to use.\n", "imagery": "# Imagery\n\nAerial imagery is an important resource for mapping. A combination of\nairplane flyovers, satellite views, and freely-compiled sources are available\nin the editor under the 'Background Settings' menu on the right.\n\nBy default a [Bing Maps](http://www.bing.com/maps/) satellite layer is\npresented in the editor, but as you pan and zoom the map to new geographical\nareas, new sources will become available. Some countries, like the United\nStates, France, and Denmark have very high-quality imagery available for some areas.\n\nImagery is sometimes offset from the map data because of a mistake on the\nimagery provider's side. If you see a lot of roads shifted from the background,\ndon't immediately move them all to match the background. Instead you can adjust\nthe imagery so that it matches the existing data by clicking 'Fix alignment' at\nthe bottom of the Background Settings UI.\n", "addresses": "# Addresses\n\nAddresses are some of the most useful information for the map.\n\nAlthough addresses are often represented as parts of streets, in OpenStreetMap\nthey're recorded as attributes of buildings and places along streets.\n\nYou can add address information to places mapped as building outlines\nas well as those mapped as single points. The optimal source of address\ndata is from an on-the-ground survey or personal knowledge - as with any\nother feature, copying from commercial sources like Google Maps is strictly\nforbidden.\n", "inspector": "# Using the Inspector\n\nThe inspector is the section on the left side of the page that allows you to\nedit the details of the selected feature.\n\n### Selecting a Feature Type\n\nAfter you add a point, line, or area, you can choose what type of feature it\nis, like whether it's a highway or residential road, supermarket or cafe.\nThe inspector will display buttons for common feature types, and you can\nfind others by typing what you're looking for in the search box.\n\nClick the 'i' in the bottom-right-hand corner of a feature type button to\nlearn more about it. Click a button to choose that type.\n\n### Using Forms and Editing Tags\n\nAfter you choose a feature type, or when you select a feature that already\nhas a type assigned, the inspector will display fields with details about\nthe feature like its name and address.\n\nBelow the fields you see, you can click icons to add other details,\nlike [Wikipedia](http://www.wikipedia.org/) information, wheelchair\naccess, and more.\n\nAt the bottom of the inspector, click 'Additional tags' to add arbitrary\nother tags to the element. [Taginfo](http://taginfo.openstreetmap.org/) is a\ngreat resource for learn more about popular tag combinations.\n\nChanges you make in the inspector are automatically applied to the map.\nYou can undo them at any time by clicking the 'Undo' button.\n", From c9426531d59e27ee0c01405e6e61e09a7cb3de29 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 22 Oct 2014 13:24:02 -0400 Subject: [PATCH 14/41] Remove footer opacity dimming re: https://github.com/openstreetmap/iD/pull/2357#issuecomment-60106754 --- css/app.css | 8 -------- 1 file changed, 8 deletions(-) diff --git a/css/app.css b/css/app.css index 013e35db8..3d8fd3747 100644 --- a/css/app.css +++ b/css/app.css @@ -2286,14 +2286,6 @@ img.wiki-image { width: 100%; float: left; clear: both; - opacity: .625; - -webkit-transition: opacity 200ms; - -moz-transition: opacity 200ms; - transition: opacity 200ms; -} - -#footer:hover { - opacity: 1; } #scale-block { From 9666af30c63df0641edc9f7ed25d848f465875b9 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 23 Oct 2014 01:06:50 -0400 Subject: [PATCH 15/41] better point/building culling based on viewport size --- js/id/renderer/features.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 1a87a683a..3d9c5e9fa 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -42,7 +42,8 @@ iD.Features = function(context) { }; var dispatch = d3.dispatch('change', 'redraw'), - feature = {}; + feature = {}, + cullFactor = 1; function defineFeature(k, filter, max) { feature[k] = { @@ -53,7 +54,7 @@ iD.Features = function(context) { defaultMax: (max || Infinity), enable: function() { this.enabled = true; this.currentMax = this.defaultMax; }, disable: function() { this.enabled = false; this.currentMax = 0; }, - hidden: function() { return this.count > this.currentMax; } + hidden: function() { return this.count > this.currentMax * cullFactor; } }; } @@ -66,7 +67,7 @@ iD.Features = function(context) { defineFeature('points', function(entity) { return entity.geometry(context.graph()) === 'point'; - }, 100); + }, 200); defineFeature('major_roads', function(entity) { return entity.geometry(context.graph()) === 'line' && major_roads[entity.tags.highway]; @@ -91,7 +92,7 @@ iD.Features = function(context) { entity.tags.parking === 'garage_boxes' ) ) || !!entity.tags['building:part']; - }, 100); + }, 250); defineFeature('landuse', function(entity) { return entity.geometry(context.graph()) === 'area' && @@ -221,7 +222,12 @@ iD.Features = function(context) { }; features.resetStats = function() { + var dimensions = context.map().dimensions(); _.each(feature, function(f) { f.count = 0; }); + + // adjust the threshold for point/building culling based on viewport size.. + // a cullFactor of 1 corresponds to a 1000x1000px viewport.. + cullFactor = dimensions[0] * dimensions[1] / 1000000; }; features.gatherStats = function(d) { From a085ce6dc858d25b961d1727bc571e5332436137 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 23 Oct 2014 10:07:54 -0400 Subject: [PATCH 16/41] Don't hide features that the user just created --- js/id/renderer/features.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 3d9c5e9fa..ffe4b11a0 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -287,7 +287,8 @@ iD.Features = function(context) { }; features.isHidden = function(entity) { - return features.isHiddenFeature(entity) || features.isHiddenChild(entity); + return !!entity.version && + (features.isHiddenFeature(entity) || features.isHiddenChild(entity)); }; features.filter = function(d) { From 094db692eaef5ac1c9e474a03b831c50ce1adc02 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 23 Oct 2014 10:24:00 -0400 Subject: [PATCH 17/41] Don't include hidden vertices in drawHover --- js/id/svg/vertices.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/js/id/svg/vertices.js b/js/id/svg/vertices.js index 211c49721..bc76ac615 100644 --- a/js/id/svg/vertices.js +++ b/js/id/svg/vertices.js @@ -12,20 +12,22 @@ iD.svg.Vertices = function(projection, context) { var vertices = {}; function addChildVertices(entity) { - var i; - if (entity.type === 'way') { - for (i = 0; i < entity.nodes.length; i++) { - addChildVertices(graph.entity(entity.nodes[i])); - } - } else if (entity.type === 'relation') { - for (i = 0; i < entity.members.length; i++) { - var member = context.hasEntity(entity.members[i].id); - if (member) { - addChildVertices(member); + if (!context.features().isHiddenFeature(entity)) { + var i; + if (entity.type === 'way') { + for (i = 0; i < entity.nodes.length; i++) { + addChildVertices(graph.entity(entity.nodes[i])); } + } else if (entity.type === 'relation') { + for (i = 0; i < entity.members.length; i++) { + var member = context.hasEntity(entity.members[i].id); + if (member) { + addChildVertices(member); + } + } + } else if (entity.intersects(extent, graph)) { + vertices[entity.id] = entity; } - } else if (entity.intersects(extent, graph)) { - vertices[entity.id] = entity; } } From 7d9b267858c4ebcf472128670ed2e014969fa97c Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 25 Oct 2014 21:57:10 -0400 Subject: [PATCH 18/41] Show auto-hidden features as disabled checkboxes with tooltip also rearrange map data panel to put features last --- data/core.yaml | 1 + dist/locales/en.json | 3 +- js/id/renderer/features.js | 21 +++++++---- js/id/ui/map_data.js | 72 ++++++++++++++++++++++++-------------- 4 files changed, 63 insertions(+), 34 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 3f2de3b85..362a49eb6 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -253,6 +253,7 @@ en: show_features: Show Map Features show_layers: Show Data Layers fill_area: Fill Areas + autohidden: "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them." feature: points: description: Points diff --git a/dist/locales/en.json b/dist/locales/en.json index 507abc9d9..1e7a82206 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -307,7 +307,8 @@ "description": "Map Data", "show_features": "Show Map Features", "show_layers": "Show Data Layers", - "fill_area": "Fill Areas" + "fill_area": "Fill Areas", + "autohidden": "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them." }, "feature": { "points": { diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index ffe4b11a0..734c70bd7 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -45,6 +45,11 @@ iD.Features = function(context) { feature = {}, cullFactor = 1; + function update() { + dispatch.change(); + dispatch.redraw(); + } + function defineFeature(k, filter, max) { feature[k] = { filter: filter, @@ -54,16 +59,11 @@ iD.Features = function(context) { defaultMax: (max || Infinity), enable: function() { this.enabled = true; this.currentMax = this.defaultMax; }, disable: function() { this.enabled = false; this.currentMax = 0; }, - hidden: function() { return this.count > this.currentMax * cullFactor; } + hidden: function() { return this.count > this.currentMax * cullFactor; }, + autoHidden: function() { return this.hidden() && this.currentMax > 0; } }; } - function update() { - dispatch.change(); - dispatch.redraw(); - } - - defineFeature('points', function(entity) { return entity.geometry(context.graph()) === 'point'; @@ -181,6 +181,13 @@ iD.Features = function(context) { return feature[k] && feature[k].hidden(); }; + features.autoHidden = function(k) { + if (!arguments.length) { + return _.filter(features.keys(), function(k) { return feature[k].autoHidden(); }); + } + return feature[k] && feature[k].autoHidden(); + }; + features.enable = function(k) { if (feature[k] && !feature[k].enabled) { feature[k].enable(); diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index df61e7e37..16d308ad2 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -8,7 +8,11 @@ iD.ui.MapData = function(context) { function map_data(selection) { function showsFeature(d) { - return context.features().enabled(d); + return autoHiddenFeature(d) ? null : !context.features().hidden(d); + } + + function autoHiddenFeature(d) { + return context.features().autoHidden(d); } function clickFeature(d) { @@ -54,11 +58,16 @@ iD.ui.MapData = function(context) { .call(bootstrap.tooltip() .html(true) .title(function(d) { - return iD.ui.tooltipHtml( - t(name + '.' + d + '.tooltip'), (d === 'wireframe' ? 'W' : null) - ); + var tip = t(name + '.' + d + '.tooltip'), + key = (d === 'wireframe' ? 'W' : null); + + if (name === 'feature' && autoHiddenFeature(d)) { + tip += '
' + t('map_data.autohidden') + '
'; + } + return iD.ui.tooltipHtml(tip, key); }) - .placement('top')); + .placement('top') + ); var label = enter.append('label'); @@ -76,6 +85,13 @@ iD.ui.MapData = function(context) { .selectAll('input') .property('checked', active); + if (name === 'feature') { + items + .selectAll('input') + .property('disabled', autoHiddenFeature) + .property('indeterminate', autoHiddenFeature); + } + //exit items.exit() .remove(); @@ -164,25 +180,6 @@ iD.ui.MapData = function(context) { content.append('h4') .text(t('map_data.title')); - // feature filters - content.append('a') - .text(t('map_data.show_features')) - .attr('href', '#') - .classed('hide-toggle', true) - .classed('expanded', false) - .on('click', function() { - var exp = d3.select(this).classed('expanded'); - featureContainer.style('display', exp ? 'none' : 'block'); - d3.select(this).classed('expanded', !exp); - d3.event.preventDefault(); - }); - - var featureContainer = content.append('div') - .attr('class', 'filters') - .style('display', 'none'); - - var featureList = featureContainer.append('ul') - .attr('class', 'layer-list'); // data layers content.append('a') @@ -201,6 +198,7 @@ iD.ui.MapData = function(context) { .attr('class', 'filters') .style('display', 'block'); + // mapillary var mapillaryLayerItem = layerContainer.append('ul') .attr('class', 'layer-list') .append('li'); @@ -217,6 +215,7 @@ iD.ui.MapData = function(context) { label.append('span') .text(t('mapillary.title')); + // gpx var gpxLayerItem = layerContainer.append('ul') .style('display', iD.detect().filedrop ? 'block' : 'none') .attr('class', 'layer-list') @@ -271,7 +270,7 @@ iD.ui.MapData = function(context) { .text(t('map_data.fill_area')) .attr('href', '#') .classed('hide-toggle', true) - .classed('expanded', true) + .classed('expanded', false) .on('click', function() { var exp = d3.select(this).classed('expanded'); fillContainer.style('display', exp ? 'none' : 'block'); @@ -281,12 +280,33 @@ iD.ui.MapData = function(context) { var fillContainer = content.append('div') .attr('class', 'filters') - .style('display', 'block'); + .style('display', 'none'); var fillList = fillContainer.append('ul') .attr('class', 'layer-list'); + // feature filters + content.append('a') + .text(t('map_data.show_features')) + .attr('href', '#') + .classed('hide-toggle', true) + .classed('expanded', false) + .on('click', function() { + var exp = d3.select(this).classed('expanded'); + featureContainer.style('display', exp ? 'none' : 'block'); + d3.select(this).classed('expanded', !exp); + d3.event.preventDefault(); + }); + + var featureContainer = content.append('div') + .attr('class', 'filters') + .style('display', 'none'); + + var featureList = featureContainer.append('ul') + .attr('class', 'layer-list'); + + context.features() .on('change.map_data-update', update); From d5ecd5d52d6b0c5096940b419e7e4ba5876fbb93 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 26 Oct 2014 13:21:40 -0400 Subject: [PATCH 19/41] add deep equals to extent --- js/id/geo/extent.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/id/geo/extent.js b/js/id/geo/extent.js index b86d687b7..a32b5783c 100644 --- a/js/id/geo/extent.js +++ b/js/id/geo/extent.js @@ -14,6 +14,13 @@ iD.geo.Extent = function geoExtent(min, max) { iD.geo.Extent.prototype = new Array(2); _.extend(iD.geo.Extent.prototype, { + equals: function (obj) { + return this[0][0] === obj[0][0] && + this[0][1] === obj[0][1] && + this[1][0] === obj[1][0] && + this[1][1] === obj[1][1]; + }, + extend: function(obj) { if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj); return iD.geo.Extent([Math.min(obj[0][0], this[0][0]), From 1f544e883bb3b02f9dc339876007f9fcc37a569a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 26 Oct 2014 13:26:26 -0400 Subject: [PATCH 20/41] bump chai to 1.9.2 and fix tests affected by change in equality testing re #2388 --- package.json | 2 +- test/spec/core/history.js | 4 ++-- test/spec/core/node.js | 2 +- test/spec/core/relation.js | 6 +++--- test/spec/core/way.js | 2 +- test/spec/geo.js | 2 +- test/spec/geo/extent.js | 30 ++++++++++++++++++++---------- 7 files changed, 29 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index d0e5d9595..e5a92805b 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "jshint": "2.3.0", "mocha": "1", "mocha-phantomjs": "3", - "chai": "~1.4", + "chai": "~1.9.2", "sinon": "~1.6", "sinon-chai": "~2.3.1", "happen": "0.1.2", diff --git a/test/spec/core/history.js b/test/spec/core/history.js index 05660a3fa..f46907f21 100644 --- a/test/spec/core/history.js +++ b/test/spec/core/history.js @@ -240,8 +240,8 @@ describe("iD.History", function () { history.perform(iD.actions.DeleteNode('n3')); // deletion var json = JSON.parse(history.toJSON()); expect(json.version).to.eql(3); - expect(json.entities).to.eql([node_1, node2.update({tags: {k: 'v'}})]); - expect(json.baseEntities).to.eql([node2, node3]); + expect( _.isEqual(json.entities, [node_1, node2.update({tags: {k: 'v'}})]) ).to.be.ok; + expect( _.isEqual(json.baseEntities, [node2, node3]) ).to.be.ok; }); }); diff --git a/test/spec/core/node.js b/test/spec/core/node.js index 810da7b4e..dfaaf19d8 100644 --- a/test/spec/core/node.js +++ b/test/spec/core/node.js @@ -14,7 +14,7 @@ describe('iD.Node', function () { describe("#extent", function() { it("returns a point extent", function() { - expect(iD.Node({loc: [5, 10]}).extent()).to.eql([[5, 10], [5, 10]]); + expect(iD.Node({loc: [5, 10]}).extent().equals([[5, 10], [5, 10]])).to.be.ok; }); }); diff --git a/test/spec/core/relation.js b/test/spec/core/relation.js index 451a53321..4a617421b 100644 --- a/test/spec/core/relation.js +++ b/test/spec/core/relation.js @@ -33,7 +33,7 @@ describe('iD.Relation', function () { r = iD.Relation({members: [{id: a.id}, {id: b.id}]}), graph = iD.Graph([a, b, r]); - expect(r.extent(graph)).to.eql([[0, 0], [5, 10]]) + expect(r.extent(graph).equals([[0, 0], [5, 10]])).to.be.ok; }); it("returns the known extent of incomplete relations", function () { @@ -42,13 +42,13 @@ describe('iD.Relation', function () { r = iD.Relation({members: [{id: a.id}, {id: b.id}]}), graph = iD.Graph([a, r]); - expect(r.extent(graph)).to.eql([[0, 0], [0, 0]]) + expect(r.extent(graph).equals([[0, 0], [0, 0]])).to.be.ok; }); it("does not error on self-referencing relations", function () { var r = iD.Relation(); r = r.addMember({id: r.id}); - expect(r.extent(iD.Graph([r]))).to.eql(iD.geo.Extent()) + expect(r.extent(iD.Graph([r]))).to.eql(iD.geo.Extent()); }); }); diff --git a/test/spec/core/way.js b/test/spec/core/way.js index a551db1ce..0ff1a56fc 100644 --- a/test/spec/core/way.js +++ b/test/spec/core/way.js @@ -69,7 +69,7 @@ describe('iD.Way', function() { node2 = iD.Node({loc: [5, 10]}), way = iD.Way({nodes: [node1.id, node2.id]}), graph = iD.Graph([node1, node2, way]); - expect(way.extent(graph)).to.eql([[0, 0], [5, 10]]); + expect(way.extent(graph).equals([[0, 0], [5, 10]])).to.be.ok; }); }); diff --git a/test/spec/geo.js b/test/spec/geo.js index b0919e8ff..e3f5b22e7 100644 --- a/test/spec/geo.js +++ b/test/spec/geo.js @@ -35,7 +35,7 @@ describe('iD.geo', function() { var o = [0, 0], a = [-2, 0], b = [2, 0]; - expect(iD.geo.cross(o, a, b)).to.eql(0); + expect(iD.geo.cross(o, a, b)).to.equal(0); }); }); diff --git a/test/spec/geo/extent.js b/test/spec/geo/extent.js index c299f4279..1cbeba94d 100644 --- a/test/spec/geo/extent.js +++ b/test/spec/geo/extent.js @@ -1,31 +1,31 @@ describe("iD.geo.Extent", function () { describe("constructor", function () { it("defaults to infinitely empty extent", function () { - expect(iD.geo.Extent()).to.eql([[Infinity, Infinity], [-Infinity, -Infinity]]); + expect(iD.geo.Extent().equals([[Infinity, Infinity], [-Infinity, -Infinity]])).to.be.ok; }); it("constructs via a point", function () { var p = [0, 0]; - expect(iD.geo.Extent(p)).to.eql([p, p]); + expect(iD.geo.Extent(p).equals([p, p])).to.be.ok; }); it("constructs via two points", function () { var min = [0, 0], max = [5, 10]; - expect(iD.geo.Extent(min, max)).to.eql([min, max]); + expect(iD.geo.Extent(min, max).equals([min, max])).to.be.ok; }); it("constructs via an extent", function () { var min = [0, 0], max = [5, 10]; - expect(iD.geo.Extent([min, max])).to.eql([min, max]); + expect(iD.geo.Extent([min, max]).equals([min, max])).to.be.ok; }); it("constructs via an iD.geo.Extent", function () { var min = [0, 0], max = [5, 10], extent = iD.geo.Extent(min, max); - expect(iD.geo.Extent(extent)).to.equal(extent); + expect(iD.geo.Extent(extent).equals(extent)).to.be.ok; }); it("has length 2", function () { @@ -45,6 +45,16 @@ describe("iD.geo.Extent", function () { }); }); + describe("#equals", function () { + it("tests extent equality", function () { + var e1 = iD.geo.Extent([0, 0], [10, 10]), + e2 = iD.geo.Extent([0, 0], [10, 10]), + e3 = iD.geo.Extent([0, 0], [12, 12]); + expect(e1.equals(e2)).to.be.ok; + expect(e1.equals(e3)).to.be.not.ok; + }); + }); + describe("#center", function () { it("returns the center point", function () { expect(iD.geo.Extent([0, 0], [5, 10]).center()).to.eql([2.5, 5]); @@ -73,17 +83,17 @@ describe("iD.geo.Extent", function () { it("does not modify self", function () { var extent = iD.geo.Extent([0, 0], [0, 0]); extent.extend([1, 1]); - expect(extent).to.eql([[0, 0], [0, 0]]); + expect(extent.equals([[0, 0], [0, 0]])).to.be.ok; }); it("returns the minimal extent containing self and the given point", function () { - expect(iD.geo.Extent().extend([0, 0])).to.eql([[0, 0], [0, 0]]); - expect(iD.geo.Extent([0, 0], [0, 0]).extend([5, 10])).to.eql([[0, 0], [5, 10]]); + expect(iD.geo.Extent().extend([0, 0]).equals([[0, 0], [0, 0]])).to.be.ok; + expect(iD.geo.Extent([0, 0], [0, 0]).extend([5, 10]).equals([[0, 0], [5, 10]])).to.be.ok; }); it("returns the minimal extent containing self and the given extent", function () { - expect(iD.geo.Extent().extend([[0, 0], [5, 10]])).to.eql([[0, 0], [5, 10]]); - expect(iD.geo.Extent([0, 0], [0, 0]).extend([[4, -1], [5, 10]])).to.eql([[0, -1], [5, 10]]); + expect(iD.geo.Extent().extend([[0, 0], [5, 10]]).equals([[0, 0], [5, 10]])).to.be.ok; + expect(iD.geo.Extent([0, 0], [0, 0]).extend([[4, -1], [5, 10]]).equals([[0, -1], [5, 10]])).to.be.ok; }); }); From 6b6ca8d523dd49e29ab80d31d2e4ac225f37e1c5 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 26 Oct 2014 13:32:05 -0400 Subject: [PATCH 21/41] initial checkin of features unit tests --- js/id/renderer/features.js | 15 ------------- test/index.html | 1 + test/index_packaged.html | 1 + test/spec/renderer/features.js | 39 ++++++++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 test/spec/renderer/features.js diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 734c70bd7..b69a6e1b4 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -209,21 +209,6 @@ iD.Features = function(context) { } }; - features.setAll = function(val) { - if (val !== undefined) { - _.each(feature, function(f) { return val ? f.enable() : f.disable(); }); - update(); - } - }; - - features.enableAll = function() { - features.setAll(true); - }; - - features.disableAll = function() { - features.setAll(false); - }; - features.count = function(k) { return feature[k] && feature[k].count; }; diff --git a/test/index.html b/test/index.html index 6ad75a745..f5cc7da2a 100644 --- a/test/index.html +++ b/test/index.html @@ -259,6 +259,7 @@ + diff --git a/test/index_packaged.html b/test/index_packaged.html index 4a4f3f135..9df77e3cc 100644 --- a/test/index_packaged.html +++ b/test/index_packaged.html @@ -67,6 +67,7 @@ + diff --git a/test/spec/renderer/features.js b/test/spec/renderer/features.js new file mode 100644 index 000000000..f83da4acb --- /dev/null +++ b/test/spec/renderer/features.js @@ -0,0 +1,39 @@ +describe('iD.Features', function() { + var context, features; + + beforeEach(function() { + context = iD(); + features = context.features(); + }); + + it('returns feature keys', function() { + var keys = features.keys(); + expect(keys).to.have.members([ + 'points', 'major_roads', 'minor_roads', 'paths', + 'buildings', 'landuse', 'boundaries', 'water', 'rail', + 'power', 'past_future', 'others' + ]); + }); + + it('disables and enables features', function() { + var enabled, disabled; + + features.disable('water'); + features.disable('rail'); + enabled = features.enabled(); + disabled = features.disabled(); + + expect(enabled).to.not.have.members(['water', 'rail']); + expect(disabled).to.have.members(['water', 'rail']); + + features.enable('water'); + enabled = features.enabled(); + disabled = features.disabled(); + + expect(enabled).to.include('water'); + expect(enabled).to.not.include('rail'); + expect(disabled).to.include('rail'); + expect(disabled).to.not.include('water'); + }); + +}); From 2b260f9a6cefa99fd038186a8d41321f05bbb20d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 28 Oct 2014 01:51:21 -0400 Subject: [PATCH 22/41] added tests for feature filtering (some tests failing because I need to decouple graph from context) --- js/id/renderer/features.js | 2 +- test/spec/renderer/features.js | 349 +++++++++++++++++++++++++++++++++ 2 files changed, 350 insertions(+), 1 deletion(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index b69a6e1b4..d1f32adda 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -59,7 +59,7 @@ iD.Features = function(context) { defaultMax: (max || Infinity), enable: function() { this.enabled = true; this.currentMax = this.defaultMax; }, disable: function() { this.enabled = false; this.currentMax = 0; }, - hidden: function() { return this.count > this.currentMax * cullFactor; }, + hidden: function() { return this.count >= this.currentMax * cullFactor; }, autoHidden: function() { return this.hidden() && this.currentMax > 0; } }; } diff --git a/test/spec/renderer/features.js b/test/spec/renderer/features.js index f83da4acb..1213501ca 100644 --- a/test/spec/renderer/features.js +++ b/test/spec/renderer/features.js @@ -34,6 +34,355 @@ describe('iD.Features', function() { expect(enabled).to.not.include('rail'); expect(disabled).to.include('rail'); expect(disabled).to.not.include('water'); + features.enable('rail'); + }); + + describe("matching", function() { + var graph = iD.Graph([ + // Points + iD.Node({id: 'point_bar', tags: {amenity: 'bar'}, version: 1}), + iD.Node({id: 'point_dock', tags: {waterway: 'dock'}, version: 1}), + iD.Node({id: 'point_rail_station', tags: {railway: 'station'}, version: 1}), + iD.Node({id: 'point_generator', tags: {power: 'generator'}, version: 1}), + iD.Node({id: 'point_old_rail_station', tags: {railway: 'station', disused: 'yes'}, version: 1}), + + // Major Roads + iD.Way({id: 'motorway', tags: {highway: 'motorway'}, version: 1}), + iD.Way({id: 'motorway_link', tags: {highway: 'motorway_link'}, version: 1}), + iD.Way({id: 'trunk', tags: {highway: 'trunk'}, version: 1}), + iD.Way({id: 'trunk_link', tags: {highway: 'trunk_link'}, version: 1}), + iD.Way({id: 'primary', tags: {highway: 'primary'}, version: 1}), + iD.Way({id: 'primary_link', tags: {highway: 'primary_link'}, version: 1}), + iD.Way({id: 'secondary', tags: {highway: 'secondary'}, version: 1}), + iD.Way({id: 'secondary_link', tags: {highway: 'secondary_link'}, version: 1}), + iD.Way({id: 'tertiary', tags: {highway: 'tertiary'}, version: 1}), + iD.Way({id: 'tertiary_link', tags: {highway: 'tertiary_link'}, version: 1}), + iD.Way({id: 'residential', tags: {highway: 'residential'}, version: 1}), + + // Minor Roads + iD.Way({id: 'service', tags: {highway: 'service'}, version: 1}), + iD.Way({id: 'living_street', tags: {highway: 'living_street'}, version: 1}), + iD.Way({id: 'road', tags: {highway: 'road'}, version: 1}), + iD.Way({id: 'unclassified', tags: {highway: 'unclassified'}, version: 1}), + iD.Way({id: 'track', tags: {highway: 'track'}, version: 1}), + + // Paths + iD.Way({id: 'path', tags: {highway: 'path'}, version: 1}), + iD.Way({id: 'footway', tags: {highway: 'footway'}, version: 1}), + iD.Way({id: 'cycleway', tags: {highway: 'cycleway'}, version: 1}), + iD.Way({id: 'bridleway', tags: {highway: 'bridleway'}, version: 1}), + iD.Way({id: 'steps', tags: {highway: 'steps'}, version: 1}), + iD.Way({id: 'pedestrian', tags: {highway: 'pedestrian'}, version: 1}), + + // Buildings + iD.Way({id: 'building_yes', tags: {area: 'yes', amenity: 'school', building: 'yes'}, version: 1}), + iD.Way({id: 'building_no', tags: {area: 'yes', amenity: 'school', building: 'no'}, version: 1}), + iD.Way({id: 'building_part', tags: { 'building:part': 'yes'}, version: 1}), + iD.Way({id: 'shelter', tags: {area: 'yes', amenity: 'shelter'}, version: 1}), + iD.Way({id: 'garage1', tags: {area: 'yes', amenity: 'parking', parking: 'multi-storey'}, version: 1}), + iD.Way({id: 'garage2', tags: {area: 'yes', amenity: 'parking', parking: 'sheds'}, version: 1}), + iD.Way({id: 'garage3', tags: {area: 'yes', amenity: 'parking', parking: 'carports'}, version: 1}), + iD.Way({id: 'garage4', tags: {area: 'yes', amenity: 'parking', parking: 'garage_boxes'}, version: 1}), + + // Landuse + iD.Way({id: 'forest', tags: {area: 'yes', landuse: 'forest'}, version: 1}), + iD.Way({id: 'scrub', tags: {area: 'yes', natural: 'scrub'}, version: 1}), + iD.Way({id: 'industrial', tags: {area: 'yes', landuse: 'industrial'}, version: 1}), + iD.Way({id: 'parkinglot', tags: {area: 'yes', amenity: 'parking', parking: 'surface'}, version: 1}), + + // Boundaries + iD.Way({id: 'boundary', tags: {boundary: 'administrative'}, version: 1}), + + // Water + iD.Way({id: 'water', tags: {area: 'yes', natural: 'water'}, version: 1}), + iD.Way({id: 'coastline', tags: {natural: 'coastline'}, version: 1}), + iD.Way({id: 'bay', tags: {area: 'yes', natural: 'bay'}, version: 1}), + iD.Way({id: 'pond', tags: {area: 'yes', landuse: 'pond'}, version: 1}), + iD.Way({id: 'basin', tags: {area: 'yes', landuse: 'basin'}, version: 1}), + iD.Way({id: 'reservoir', tags: {area: 'yes', landuse: 'reservoir'}, version: 1}), + iD.Way({id: 'salt_pond', tags: {area: 'yes', landuse: 'salt_pond'}, version: 1}), + iD.Way({id: 'river', tags: {waterway: 'river'}, version: 1}), + + // Rail + iD.Way({id: 'railway', tags: {railway: 'rail'}, version: 1}), + iD.Way({id: 'rail_landuse', tags: {area: 'yes', landuse: 'railway'}, version: 1}), + iD.Way({id: 'rail_disused', tags: {railway: 'disused'}, version: 1}), + iD.Way({id: 'rail_streetcar', tags: {railway: 'tram', highway: 'residential'}, version: 1}), + iD.Way({id: 'rail_trail', tags: {railway: 'disused', highway: 'cycleway'}, version: 1}), + + // Power + iD.Way({id: 'power_line', tags: {power: 'line'}, version: 1}), + + // Past/Future + iD.Way({id: 'motorway_construction', tags: {highway: 'construction', construction: 'motorway'}, version: 1}), + iD.Way({id: 'cycleway_proposed', tags: {highway: 'proposed', proposed: 'cycleway'}, version: 1}), + iD.Way({id: 'landuse_construction', tags: {area: 'yes', landuse: 'construction'}, version: 1}), + + // Others + iD.Way({id: 'fence', tags: {barrier: 'fence'}, version: 1}), + iD.Way({id: 'pipeline', tags: {man_made: 'pipeline'}, version: 1}) + ]); + + function doMatch(ids) { + _.each(ids, function(id) { + expect(features.isHidden(graph.entity(id)), id).to.be.true; + }); + } + + function dontMatch(ids) { + _.each(ids, function(id) { + expect(features.isHidden(graph.entity(id)), id).to.be.false; + }); + } + + + it("matches points", function () { + features.disable('points'); + + doMatch([ + 'point_bar', 'point_dock', 'point_rail_station', + 'point_generator', 'point_old_rail_station' + ]); + + dontMatch([ + 'motorway', 'service', 'path', 'building_yes', + 'forest', 'boundary', 'water', 'railway', 'power_line', + 'motorway_construction', 'fence' + ]); + + features.enable('points'); + }); + + + it("matches major roads", function () { + features.disable('major_roads'); + + doMatch([ + 'motorway', 'motorway_link', 'trunk', 'trunk_link', + 'primary', 'primary_link', 'secondary', 'secondary_link', + 'tertiary', 'tertiary_link', 'residential' + ]); + + dontMatch([ + 'point_bar', 'service', 'path', 'building_yes', + 'forest', 'boundary', 'water', 'railway', 'power_line', + 'motorway_construction', 'fence' + ]); + + features.enable('major_roads'); + }); + + + it("matches minor roads", function () { + features.disable('minor_roads'); + + doMatch([ + 'service', 'living_street', 'road', 'unclassified', 'track' + ]); + + dontMatch([ + 'point_bar', 'motorway', 'path', 'building_yes', + 'forest', 'boundary', 'water', 'railway', 'power_line', + 'motorway_construction', 'fence' + ]); + + features.enable('minor_roads'); + }); + + + it("matches paths", function () { + features.disable('paths'); + + doMatch([ + 'path', 'footway', 'cycleway', 'bridleway', + 'steps', 'pedestrian' + ]); + + dontMatch([ + 'point_bar', 'motorway', 'service', 'building_yes', + 'forest', 'boundary', 'water', 'railway', 'power_line', + 'motorway_construction', 'fence' + ]); + + features.enable('paths'); + }); + + + it("matches buildings", function () { + features.disable('buildings'); + + doMatch([ + 'building_yes', 'building_part', 'shelter', + 'garage1', 'garage2', 'garage3', 'garage4' + ]); + + dontMatch([ + 'building_no', 'point_bar', 'motorway', 'service', 'path', + 'forest', 'boundary', 'water', 'railway', 'power_line', + 'motorway_construction', 'fence' + ]); + + features.enable('buildings'); + }); + + + it("matches landuse", function () { + features.disable('landuse'); + + doMatch([ + 'forest', 'scrub', 'industrial', 'parkinglot', 'building_no', + 'rail_landuse', 'landuse_construction' + ]); + + dontMatch([ + 'point_bar', 'motorway', 'service', 'path', 'building_yes', + 'boundary', 'water', 'railway', 'power_line', + 'motorway_construction', 'fence' + ]); + + features.enable('landuse'); + }); + + + it("matches boundaries", function () { + features.disable('boundaries'); + + doMatch([ + 'boundary' + ]); + + dontMatch([ + 'point_bar', 'motorway', 'service', 'path', 'building_yes', + 'forest', 'water', 'railway', 'power_line', + 'motorway_construction', 'fence' + ]); + + features.enable('boundaries'); + }); + + + it("matches water", function () { + features.disable('water'); + + doMatch([ + 'point_dock', 'water', 'coastline', 'bay', 'pond', + 'basin', 'reservoir', 'salt_pond', 'river' + ]); + + dontMatch([ + 'point_bar', 'motorway', 'service', 'path', 'building_yes', + 'forest', 'boundary', 'railway', 'power_line', + 'motorway_construction', 'fence' + ]); + + features.enable('water'); + }); + + + it("matches rail", function () { + features.disable('rail'); + + doMatch([ + 'point_rail_station', 'point_old_rail_station', + 'railway', 'rail_landuse', 'rail_disused' + ]); + + dontMatch([ + 'rail_streetcar', 'rail_trail', // because rail also used as highway + 'point_bar', 'motorway', 'service', 'path', 'building_yes', + 'forest', 'boundary', 'water', 'power_line', + 'motorway_construction', 'fence' + ]); + + features.enable('rail'); + }); + + + it("matches power", function () { + features.disable('power'); + + doMatch([ + 'point_generator', 'power_line' + ]); + + dontMatch([ + 'point_bar', 'motorway', 'service', 'path', 'building_yes', + 'forest', 'boundary', 'water', 'railway', + 'motorway_construction', 'fence' + ]); + + features.enable('power'); + }); + + + it("matches past/future", function () { + features.disable('past_future'); + + doMatch([ + 'point_old_rail_station', 'rail_disused', + 'motorway_construction', 'cycleway_proposed', 'landuse_construction' + ]); + + dontMatch([ + 'rail_trail', // because rail also used as highway + 'point_bar', 'motorway', 'service', 'path', 'building_yes', + 'forest', 'boundary', 'water', 'railway', 'power_line', 'fence' + ]); + + features.enable('past_future'); + }); + + + it("matches others", function () { + features.disable('others'); + + doMatch([ + 'fence', 'pipeline' + ]); + + dontMatch([ + 'point_bar', 'motorway', 'service', 'path', 'building_yes', + 'forest', 'boundary', 'water', 'railway', 'power_line', + 'motorway_construction', + ]); + + features.enable('others'); + }); + + }); + + it('hides child vertices on a hidden way', function() { + var a = iD.Node({id: 'a', version: 1}), + b = iD.Node({id: 'b', version: 1}), + w = iD.Way({id: 'w', nodes: [a.id, b.id], tags: {highway: 'path'}, version: 1}), + graph = iD.Graph([a, b, w]); + + features.disable('paths'); + + expect(features.isHiddenChild(graph.entity('a'))).to.be.true; + expect(features.isHidden(graph.entity('a'))).to.be.true; + + features.enable('paths'); + }); + + it('hides child ways on a hidden relation', function() { + var a = iD.Node({id: 'a'}), + b = iD.Node({id: 'b'}), + c = iD.Node({id: 'c'}), + d = iD.Node({id: 'd'}), + e = iD.Node({id: 'e'}), + f = iD.Node({id: 'f'}), + outer = iD.Way({id: 'outer', nodes: [a.id, b.id, c.id, a.id], tags: {natural: 'wood'}}), + inner = iD.Way({id: 'inner', nodes: [d.id, e.id, f.id, d.id]}), + r = iD.Relation({members: [{id: outer.id, type: 'way'}, {id: inner.id, role: 'inner', type: 'way'}]}), + graph = iD.Graph([a, b, c, d, e, f, outer, inner, r]); + + features.disable('landuse'); + + expect(features.isHiddenChild(graph.entity('inner'))).to.be.true; + expect(features.isHidden(graph.entity('inner'))).to.be.true; + + features.enable('landuse'); }); }); From 43f1cdd3eb6600a8bc4816a9606b120d583a0c85 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 28 Oct 2014 13:18:55 -0400 Subject: [PATCH 23/41] decoupled graph from context for better testing fixed feature matching tests for vertices and relations --- js/id/id.js | 5 ++- js/id/renderer/features.js | 78 +++++++++++++++++++--------------- js/id/renderer/map.js | 30 +++++++------ test/spec/renderer/features.js | 68 ++++++++++++++++++++--------- 4 files changed, 110 insertions(+), 71 deletions(-) diff --git a/js/id/id.js b/js/id/id.js index fd18f9a5a..20c2795e0 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -207,8 +207,9 @@ window.iD = function () { var features = iD.Features(context); context.features = function() { return features; }; context.hasHiddenConnections = function(id) { - var entity = history.graph().entity(id); - return features.hasHiddenConnections(entity); + var graph = history.graph(), + entity = graph.entity(id); + return features.hasHiddenConnections(entity, graph); }; /* Map */ diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index d1f32adda..f1fbd8fb2 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -42,6 +42,7 @@ iD.Features = function(context) { }; var dispatch = d3.dispatch('change', 'redraw'), + resolver = context.graph(), feature = {}, cullFactor = 1; @@ -59,31 +60,31 @@ iD.Features = function(context) { defaultMax: (max || Infinity), enable: function() { this.enabled = true; this.currentMax = this.defaultMax; }, disable: function() { this.enabled = false; this.currentMax = 0; }, - hidden: function() { return this.count >= this.currentMax * cullFactor; }, + hidden: function() { return this.count > this.currentMax * cullFactor; }, autoHidden: function() { return this.hidden() && this.currentMax > 0; } }; } defineFeature('points', function(entity) { - return entity.geometry(context.graph()) === 'point'; + return entity.geometry(resolver) === 'point'; }, 200); defineFeature('major_roads', function(entity) { - return entity.geometry(context.graph()) === 'line' && major_roads[entity.tags.highway]; + return entity.geometry(resolver) === 'line' && major_roads[entity.tags.highway]; }); defineFeature('minor_roads', function(entity) { - return entity.geometry(context.graph()) === 'line' && minor_roads[entity.tags.highway]; + return entity.geometry(resolver) === 'line' && minor_roads[entity.tags.highway]; }); defineFeature('paths', function(entity) { - return entity.geometry(context.graph()) === 'line' && paths[entity.tags.highway]; + return entity.geometry(resolver) === 'line' && paths[entity.tags.highway]; }); defineFeature('buildings', function(entity) { return ( - entity.geometry(context.graph()) === 'area' && ( + entity.geometry(resolver) === 'area' && ( (!!entity.tags.building && entity.tags.building !== 'no') || entity.tags.amenity === 'shelter' || entity.tags.parking === 'multi-storey' || @@ -95,7 +96,7 @@ iD.Features = function(context) { }, 250); defineFeature('landuse', function(entity) { - return entity.geometry(context.graph()) === 'area' && + return entity.geometry(resolver) === 'area' && !feature.buildings.filter(entity) && !feature.water.filter(entity); }); @@ -145,8 +146,8 @@ iD.Features = function(context) { // lines or areas that don't match another feature filter. defineFeature('others', function(entity) { return ( - entity.geometry(context.graph()) === 'line' || - entity.geometry(context.graph()) === 'area' + entity.geometry(resolver) === 'line' || + entity.geometry(resolver) === 'area' ) && _.reduce(_.omit(feature, 'others'), function(result, v) { return result && !v.filter(entity); @@ -213,20 +214,18 @@ iD.Features = function(context) { return feature[k] && feature[k].count; }; - features.resetStats = function() { - var dimensions = context.map().dimensions(); + + features.gatherStats = function(d, graph, dimensions) { + var hidden = features.hidden(), + keys = features.keys(); + resolver = graph || resolver; + _.each(feature, function(f) { f.count = 0; }); // adjust the threshold for point/building culling based on viewport size.. // a cullFactor of 1 corresponds to a 1000x1000px viewport.. cullFactor = dimensions[0] * dimensions[1] / 1000000; - }; - features.gatherStats = function(d) { - var hidden = features.hidden(), - keys = features.keys(); - - features.resetStats(); _.each(d, function(entity) { _.each(keys, function(k) { if (feature[k].filter(entity)) { @@ -248,43 +247,54 @@ iD.Features = function(context) { return stats; }; - features.isHiddenFeature = function(entity) { + features.isHiddenFeature = function(entity, graph) { + resolver = graph || resolver; return _.any(features.hidden(), function(k) { return feature[k].filter(entity); }); }; - features.isHiddenChild = function(entity) { - var g = context.graph(), - parents = _.union(g.parentWays(entity), g.parentRelations(entity)); - return parents.length ? _.all(parents, features.isHidden) : false; + features.isHiddenChild = function(entity, graph) { + var parents; + resolver = graph || resolver; + + 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) { - var g = context.graph(), - childNodes, connections; + features.hasHiddenConnections = function(entity, graph) { + var childNodes, connections; + resolver = graph || resolver; if (entity.type === 'midpoint') { - childNodes = [context.entity(entity.edge[0]), context.entity(entity.edge[1])]; + childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])]; } else { - childNodes = g.childNodes(entity); + childNodes = resolver.childNodes(entity); } // gather parents.. - connections = _.union(g.parentWays(entity), g.parentRelations(entity)); + connections = _.union(resolver.parentWays(entity), resolver.parentRelations(entity)); // gather ways connected to child nodes.. connections = _.reduce(childNodes, function(result, e) { - return g.isShared(e) ? _.union(result, g.parentWays(e)) : result; + return resolver.isShared(e) ? _.union(result, resolver.parentWays(e)) : result; }, connections); - return connections.length ? _.any(connections, features.isHidden) : false; + return connections.length ? _.any(connections, function(e) { + return features.isHidden(e, resolver); + }) : false; }; - features.isHidden = function(entity) { + features.isHidden = function(entity, graph) { + resolver = graph || resolver; return !!entity.version && - (features.isHiddenFeature(entity) || features.isHiddenChild(entity)); + (features.isHiddenFeature(entity, resolver) || features.isHiddenChild(entity, resolver)); }; - features.filter = function(d) { - return features.hidden().length ? _.reject(d, features.isHidden) : d; + features.filter = function(d, graph) { + resolver = graph || resolver; + return features.hidden().length ? _.reject(d, function(e) { + return features.isHidden(e, resolver); + }) : d; }; return d3.rebind(features, dispatch, 'on'); diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index c33678418..e26b5cb0a 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -95,34 +95,36 @@ iD.Map = function(context) { function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; } function drawVector(difference, extent) { - var filter, all, - graph = context.graph(); + var graph = context.graph(), + features = context.features(), + all = context.intersects(map.extent()), + data, filter; if (difference) { var complete = difference.complete(map.extent()); - all = _.compact(_.values(complete)); + data = _.compact(_.values(complete)); filter = function(d) { return d.id in complete; }; } else if (extent) { - all = context.intersects(map.extent().intersection(extent)); - var set = d3.set(_.pluck(all, 'id')); + data = context.intersects(map.extent().intersection(extent)); + var set = d3.set(_.pluck(data, 'id')); filter = function(d) { return set.has(d.id); }; } else { - all = context.intersects(map.extent()); + data = all; filter = d3.functor(true); } - context.features().gatherStats(context.intersects(map.extent())); - all = context.features().filter(all); + features.gatherStats(all, graph, dimensions); + data = features.filter(data, graph); surface - .call(vertices, graph, all, filter, map.extent(), map.zoom()) - .call(lines, graph, all, filter) - .call(areas, graph, all, filter) - .call(midpoints, graph, all, filter, map.trimmedExtent()) - .call(labels, graph, all, filter, dimensions, !difference && !extent) - .call(points, all, filter); + .call(vertices, graph, data, filter, map.extent(), map.zoom()) + .call(lines, graph, data, filter) + .call(areas, graph, data, filter) + .call(midpoints, graph, data, filter, map.trimmedExtent()) + .call(labels, graph, data, filter, dimensions, !difference && !extent) + .call(points, data, filter); dispatch.drawn({full: true}); } diff --git a/test/spec/renderer/features.js b/test/spec/renderer/features.js index 1213501ca..b31c642d8 100644 --- a/test/spec/renderer/features.js +++ b/test/spec/renderer/features.js @@ -1,9 +1,9 @@ describe('iD.Features', function() { - var context, features; + var dimensions = [1000, 1000], + features; beforeEach(function() { - context = iD(); - features = context.features(); + features = iD().features(); }); it('returns feature keys', function() { @@ -121,23 +121,26 @@ describe('iD.Features', function() { // Others iD.Way({id: 'fence', tags: {barrier: 'fence'}, version: 1}), iD.Way({id: 'pipeline', tags: {man_made: 'pipeline'}, version: 1}) - ]); + ]), + all = _.values(graph.base().entities); + function doMatch(ids) { _.each(ids, function(id) { - expect(features.isHidden(graph.entity(id)), id).to.be.true; + expect(features.isHidden(graph.entity(id), graph), 'doMatch: ' + id).to.be.true; }); } function dontMatch(ids) { _.each(ids, function(id) { - expect(features.isHidden(graph.entity(id)), id).to.be.false; + expect(features.isHidden(graph.entity(id), graph), 'dontMatch: ' + id).to.be.false; }); } it("matches points", function () { features.disable('points'); + features.gatherStats(all, graph, dimensions); doMatch([ 'point_bar', 'point_dock', 'point_rail_station', @@ -156,6 +159,7 @@ describe('iD.Features', function() { it("matches major roads", function () { features.disable('major_roads'); + features.gatherStats(all, graph, dimensions); doMatch([ 'motorway', 'motorway_link', 'trunk', 'trunk_link', @@ -175,6 +179,7 @@ describe('iD.Features', function() { it("matches minor roads", function () { features.disable('minor_roads'); + features.gatherStats(all, graph, dimensions); doMatch([ 'service', 'living_street', 'road', 'unclassified', 'track' @@ -192,6 +197,7 @@ describe('iD.Features', function() { it("matches paths", function () { features.disable('paths'); + features.gatherStats(all, graph, dimensions); doMatch([ 'path', 'footway', 'cycleway', 'bridleway', @@ -210,6 +216,7 @@ describe('iD.Features', function() { it("matches buildings", function () { features.disable('buildings'); + features.gatherStats(all, graph, dimensions); doMatch([ 'building_yes', 'building_part', 'shelter', @@ -228,6 +235,7 @@ describe('iD.Features', function() { it("matches landuse", function () { features.disable('landuse'); + features.gatherStats(all, graph, dimensions); doMatch([ 'forest', 'scrub', 'industrial', 'parkinglot', 'building_no', @@ -246,6 +254,7 @@ describe('iD.Features', function() { it("matches boundaries", function () { features.disable('boundaries'); + features.gatherStats(all, graph, dimensions); doMatch([ 'boundary' @@ -263,6 +272,7 @@ describe('iD.Features', function() { it("matches water", function () { features.disable('water'); + features.gatherStats(all, graph, dimensions); doMatch([ 'point_dock', 'water', 'coastline', 'bay', 'pond', @@ -281,6 +291,7 @@ describe('iD.Features', function() { it("matches rail", function () { features.disable('rail'); + features.gatherStats(all, graph, dimensions); doMatch([ 'point_rail_station', 'point_old_rail_station', @@ -300,6 +311,7 @@ describe('iD.Features', function() { it("matches power", function () { features.disable('power'); + features.gatherStats(all, graph, dimensions); doMatch([ 'point_generator', 'power_line' @@ -317,6 +329,7 @@ describe('iD.Features', function() { it("matches past/future", function () { features.disable('past_future'); + features.gatherStats(all, graph, dimensions); doMatch([ 'point_old_rail_station', 'rail_disused', @@ -335,6 +348,7 @@ describe('iD.Features', function() { it("matches others", function () { features.disable('others'); + features.gatherStats(all, graph, dimensions); doMatch([ 'fence', 'pipeline' @@ -355,32 +369,44 @@ describe('iD.Features', function() { var a = iD.Node({id: 'a', version: 1}), b = iD.Node({id: 'b', version: 1}), w = iD.Way({id: 'w', nodes: [a.id, b.id], tags: {highway: 'path'}, version: 1}), - graph = iD.Graph([a, b, w]); + graph = iD.Graph([a, b, w]), + all = _.values(graph.base().entities); features.disable('paths'); + features.gatherStats(all, graph, dimensions); - expect(features.isHiddenChild(graph.entity('a'))).to.be.true; - expect(features.isHidden(graph.entity('a'))).to.be.true; + expect(features.isHiddenChild(graph.entity('a'), graph)).to.be.true; + expect(features.isHidden(graph.entity('a'), graph)).to.be.true; features.enable('paths'); }); it('hides child ways on a hidden relation', function() { - var a = iD.Node({id: 'a'}), - b = iD.Node({id: 'b'}), - c = iD.Node({id: 'c'}), - d = iD.Node({id: 'd'}), - e = iD.Node({id: 'e'}), - f = iD.Node({id: 'f'}), - outer = iD.Way({id: 'outer', nodes: [a.id, b.id, c.id, a.id], tags: {natural: 'wood'}}), - inner = iD.Way({id: 'inner', nodes: [d.id, e.id, f.id, d.id]}), - r = iD.Relation({members: [{id: outer.id, type: 'way'}, {id: inner.id, role: 'inner', type: 'way'}]}), - graph = iD.Graph([a, b, c, d, e, f, outer, inner, r]); + var a = iD.Node({id: 'a', version: 1}), + b = iD.Node({id: 'b', version: 1}), + c = iD.Node({id: 'c', version: 1}), + d = iD.Node({id: 'd', version: 1}), + e = iD.Node({id: 'e', version: 1}), + f = iD.Node({id: 'f', version: 1}), + outer = iD.Way({id: 'outer', nodes: [a.id, b.id, c.id, a.id], tags: {area: 'yes', natural: 'wood'}, version: 1}), + inner = iD.Way({id: 'inner', nodes: [d.id, e.id, f.id, d.id], version: 1}), + r = iD.Relation({ + id: 'r', + tags: {type: 'multipolygon'}, + members: [ + {id: outer.id, role: 'outer', type: 'way'}, + {id: inner.id, role: 'inner', type: 'way'} + ], + version: 1 + }), + graph = iD.Graph([a, b, c, d, e, f, outer, inner, r]), + all = _.values(graph.base().entities); features.disable('landuse'); + features.gatherStats(all, graph, dimensions); - expect(features.isHiddenChild(graph.entity('inner'))).to.be.true; - expect(features.isHidden(graph.entity('inner'))).to.be.true; + expect(features.isHiddenChild(graph.entity('inner'), graph)).to.be.true; + expect(features.isHidden(graph.entity('inner'), graph)).to.be.true; features.enable('landuse'); }); From 927c533bb4dd387c820b579896122d651ba09655 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 28 Oct 2014 15:12:14 -0400 Subject: [PATCH 24/41] add feature tests for auto-hiding --- js/id/renderer/features.js | 12 --- test/spec/renderer/features.js | 143 +++++++++++++++++++++++++-------- 2 files changed, 110 insertions(+), 45 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index f1fbd8fb2..3660567b1 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -203,18 +203,6 @@ iD.Features = function(context) { } }; - features.toggle = function(k) { - if (feature[k]) { - (function(f) { return f.enabled ? f.disable() : f.enable(); }(feature[k])); - update(); - } - }; - - features.count = function(k) { - return feature[k] && feature[k].count; - }; - - features.gatherStats = function(d, graph, dimensions) { var hidden = features.hidden(), keys = features.keys(); diff --git a/test/spec/renderer/features.js b/test/spec/renderer/features.js index b31c642d8..07d77f423 100644 --- a/test/spec/renderer/features.js +++ b/test/spec/renderer/features.js @@ -34,7 +34,6 @@ describe('iD.Features', function() { expect(enabled).to.not.include('rail'); expect(disabled).to.include('rail'); expect(disabled).to.not.include('water'); - features.enable('rail'); }); describe("matching", function() { @@ -152,8 +151,6 @@ describe('iD.Features', function() { 'forest', 'boundary', 'water', 'railway', 'power_line', 'motorway_construction', 'fence' ]); - - features.enable('points'); }); @@ -172,8 +169,6 @@ describe('iD.Features', function() { 'forest', 'boundary', 'water', 'railway', 'power_line', 'motorway_construction', 'fence' ]); - - features.enable('major_roads'); }); @@ -190,8 +185,6 @@ describe('iD.Features', function() { 'forest', 'boundary', 'water', 'railway', 'power_line', 'motorway_construction', 'fence' ]); - - features.enable('minor_roads'); }); @@ -209,8 +202,6 @@ describe('iD.Features', function() { 'forest', 'boundary', 'water', 'railway', 'power_line', 'motorway_construction', 'fence' ]); - - features.enable('paths'); }); @@ -228,8 +219,6 @@ describe('iD.Features', function() { 'forest', 'boundary', 'water', 'railway', 'power_line', 'motorway_construction', 'fence' ]); - - features.enable('buildings'); }); @@ -247,8 +236,6 @@ describe('iD.Features', function() { 'boundary', 'water', 'railway', 'power_line', 'motorway_construction', 'fence' ]); - - features.enable('landuse'); }); @@ -265,8 +252,6 @@ describe('iD.Features', function() { 'forest', 'water', 'railway', 'power_line', 'motorway_construction', 'fence' ]); - - features.enable('boundaries'); }); @@ -284,8 +269,6 @@ describe('iD.Features', function() { 'forest', 'boundary', 'railway', 'power_line', 'motorway_construction', 'fence' ]); - - features.enable('water'); }); @@ -304,8 +287,6 @@ describe('iD.Features', function() { 'forest', 'boundary', 'water', 'power_line', 'motorway_construction', 'fence' ]); - - features.enable('rail'); }); @@ -322,8 +303,6 @@ describe('iD.Features', function() { 'forest', 'boundary', 'water', 'railway', 'motorway_construction', 'fence' ]); - - features.enable('power'); }); @@ -341,8 +320,6 @@ describe('iD.Features', function() { 'point_bar', 'motorway', 'service', 'path', 'building_yes', 'forest', 'boundary', 'water', 'railway', 'power_line', 'fence' ]); - - features.enable('past_future'); }); @@ -359,8 +336,6 @@ describe('iD.Features', function() { 'forest', 'boundary', 'water', 'railway', 'power_line', 'motorway_construction', ]); - - features.enable('others'); }); }); @@ -375,13 +350,11 @@ describe('iD.Features', function() { features.disable('paths'); features.gatherStats(all, graph, dimensions); - expect(features.isHiddenChild(graph.entity('a'), graph)).to.be.true; - expect(features.isHidden(graph.entity('a'), graph)).to.be.true; - - features.enable('paths'); + expect(features.isHiddenChild(a, graph)).to.be.true; + expect(features.isHidden(a, graph)).to.be.true; }); - it('hides child ways on a hidden relation', function() { + it('hides child ways on a hidden multipolygon relation', function() { var a = iD.Node({id: 'a', version: 1}), b = iD.Node({id: 'b', version: 1}), c = iD.Node({id: 'c', version: 1}), @@ -405,10 +378,114 @@ describe('iD.Features', function() { features.disable('landuse'); features.gatherStats(all, graph, dimensions); - expect(features.isHiddenChild(graph.entity('inner'), graph)).to.be.true; - expect(features.isHidden(graph.entity('inner'), graph)).to.be.true; + expect(features.isHiddenChild(inner, graph)).to.be.true; + expect(features.isHidden(inner, graph)).to.be.true; + }); - features.enable('landuse'); + it('hides only versioned entities', function() { + var a = iD.Node({id: 'a', version: 1}), + b = iD.Node({id: 'b'}), + graph = iD.Graph([a, b]), + all = _.values(graph.base().entities); + + features.disable('points'); + features.gatherStats(all, graph, dimensions); + + expect(features.isHidden(a, graph)).to.be.true; + expect(features.isHidden(b, graph)).to.be.false; + }); + + it('counts features', function() { + var graph = iD.Graph([ + iD.Node({id: 'point_bar', tags: {amenity: 'bar'}, version: 1}), + iD.Node({id: 'point_dock', tags: {waterway: 'dock'}, version: 1}), + iD.Node({id: 'point_rail_station', tags: {railway: 'station'}, version: 1}), + iD.Node({id: 'point_generator', tags: {power: 'generator'}, version: 1}), + iD.Node({id: 'point_old_rail_station', tags: {railway: 'station', disused: 'yes'}, version: 1}), + iD.Way({id: 'motorway', tags: {highway: 'motorway'}, version: 1}), + iD.Way({id: 'building_yes', tags: {area: 'yes', amenity: 'school', building: 'yes'}, version: 1}), + iD.Way({id: 'boundary', tags: {boundary: 'administrative'}, version: 1}), + iD.Way({id: 'fence', tags: {barrier: 'fence'}, version: 1}) + ]), + all = _.values(graph.base().entities), + stats; + + features.gatherStats(all, graph, dimensions); + stats = features.stats(); + + expect(stats.boundaries).to.eql(1); + expect(stats.buildings).to.eql(1); + expect(stats.landuse).to.eql(0); + expect(stats.major_roads).to.eql(1); + expect(stats.minor_roads).to.eql(0); + expect(stats.others).to.eql(1); + expect(stats.past_future).to.eql(1); + expect(stats.paths).to.eql(0); + expect(stats.points).to.eql(5); + expect(stats.power).to.eql(1); + expect(stats.rail).to.eql(2); + expect(stats.water).to.eql(1); + }); + + it('auto-hides features', function() { + var graph = iD.Graph([]), + maxPoints = 200, + all, hidden, autoHidden, i, msg; + + for(i = 0; i < maxPoints; i++) { + graph.rebase([iD.Node({version: 1})], [graph]); + } + + all = _.values(graph.base().entities); + features.gatherStats(all, graph, dimensions); + hidden = features.hidden(); + autoHidden = features.autoHidden(); + msg = i + ' points'; + + expect(hidden, msg).to.not.include('points'); + expect(autoHidden, msg).to.not.include('points'); + + graph.rebase([iD.Node({version: 1})], [graph]); + + all = _.values(graph.base().entities); + features.gatherStats(all, graph, dimensions); + hidden = features.hidden(); + autoHidden = features.autoHidden(); + msg = ++i + ' points'; + + expect(hidden, msg).to.include('points'); + expect(autoHidden, msg).to.include('points'); + }); + + it('doubles auto-hide threshold when doubling viewport size', function() { + var graph = iD.Graph([]), + maxPoints = 400, + dimensions = [2000, 1000], + all, hidden, autoHidden, i, msg; + + for(i = 0; i < maxPoints; i++) { + graph.rebase([iD.Node({version: 1})], [graph]); + } + + all = _.values(graph.base().entities); + features.gatherStats(all, graph, dimensions); + hidden = features.hidden(); + autoHidden = features.autoHidden(); + msg = i + ' points'; + + expect(hidden, msg).to.not.include('points'); + expect(autoHidden, msg).to.not.include('points'); + + graph.rebase([iD.Node({version: 1})], [graph]); + + all = _.values(graph.base().entities); + features.gatherStats(all, graph, dimensions); + hidden = features.hidden(); + autoHidden = features.autoHidden(); + msg = ++i + ' points'; + + expect(hidden, msg).to.include('points'); + expect(autoHidden, msg).to.include('points'); }); }); From a2703f10c83ee3ca70bc34226cf06669a59d7041 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 28 Oct 2014 21:49:31 -0400 Subject: [PATCH 25/41] fix tests affected by change in equality testing re #2388 --- test/spec/geo/extent.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/spec/geo/extent.js b/test/spec/geo/extent.js index 1cbeba94d..fb880548e 100644 --- a/test/spec/geo/extent.js +++ b/test/spec/geo/extent.js @@ -101,11 +101,11 @@ describe("iD.geo.Extent", function () { it("extends self to the minimal extent containing self and the given extent", function () { var e = iD.geo.Extent(); e._extend([[0, 0], [5, 10]]); - expect(e).to.eql([[0, 0], [5, 10]]); + expect(e.equals([[0, 0], [5, 10]])).to.be.ok; e = iD.geo.Extent([0, 0], [0, 0]); e._extend([[4, -1], [5, 10]]); - expect(e).to.eql([[0, -1], [5, 10]]); + expect(e.equals([[0, -1], [5, 10]])).to.be.ok; }); }); From b20f1495de3e571410dc85302ebe7d8fd182d736 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 29 Oct 2014 15:06:43 -0400 Subject: [PATCH 26/41] restore toggle function and add test --- js/id/renderer/features.js | 7 + test/spec/renderer/features.js | 324 +++++++++++++++++---------------- 2 files changed, 176 insertions(+), 155 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 3660567b1..98d8cdba4 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -203,6 +203,13 @@ iD.Features = function(context) { } }; + features.toggle = function(k) { + if (feature[k]) { + (function(f) { return f.enabled ? f.disable() : f.enable(); }(feature[k])); + update(); + } + }; + features.gatherStats = function(d, graph, dimensions) { var hidden = features.hidden(), keys = features.keys(); diff --git a/test/spec/renderer/features.js b/test/spec/renderer/features.js index 07d77f423..c887d43c8 100644 --- a/test/spec/renderer/features.js +++ b/test/spec/renderer/features.js @@ -6,34 +6,78 @@ describe('iD.Features', function() { features = iD().features(); }); - it('returns feature keys', function() { - var keys = features.keys(); - expect(keys).to.have.members([ - 'points', 'major_roads', 'minor_roads', 'paths', - 'buildings', 'landuse', 'boundaries', 'water', 'rail', - 'power', 'past_future', 'others' - ]); + describe('#keys', function() { + it('returns feature keys', function() { + var keys = features.keys(); + expect(keys).to.have.members([ + 'points', 'major_roads', 'minor_roads', 'paths', + 'buildings', 'landuse', 'boundaries', 'water', 'rail', + 'power', 'past_future', 'others' + ]); + }); }); - it('disables and enables features', function() { - var enabled, disabled; + describe('#disable', function() { + it('disables features', function() { + features.disable('water'); + expect(features.disabled()).to.include('water'); + expect(features.enabled()).to.not.include('water'); + }); + }); - features.disable('water'); - features.disable('rail'); - enabled = features.enabled(); - disabled = features.disabled(); + describe('#enable', function() { + it('enables features', function() { + features.disable('water'); + features.enable('water'); + expect(features.disabled()).to.not.include('water'); + expect(features.enabled()).to.include('water'); + }); + }); - expect(enabled).to.not.have.members(['water', 'rail']); - expect(disabled).to.have.members(['water', 'rail']); + describe('#toggle', function() { + it('toggles features', function() { + features.toggle('water'); + expect(features.disabled()).to.include('water'); + expect(features.enabled()).to.not.include('water'); - features.enable('water'); - enabled = features.enabled(); - disabled = features.disabled(); + features.toggle('water'); + expect(features.disabled()).to.not.include('water'); + expect(features.enabled()).to.include('water'); + }); + }); - expect(enabled).to.include('water'); - expect(enabled).to.not.include('rail'); - expect(disabled).to.include('rail'); - expect(disabled).to.not.include('water'); + describe('#gatherStats', function() { + it('counts features', function() { + var graph = iD.Graph([ + iD.Node({id: 'point_bar', tags: {amenity: 'bar'}, version: 1}), + iD.Node({id: 'point_dock', tags: {waterway: 'dock'}, version: 1}), + iD.Node({id: 'point_rail_station', tags: {railway: 'station'}, version: 1}), + iD.Node({id: 'point_generator', tags: {power: 'generator'}, version: 1}), + iD.Node({id: 'point_old_rail_station', tags: {railway: 'station', disused: 'yes'}, version: 1}), + iD.Way({id: 'motorway', tags: {highway: 'motorway'}, version: 1}), + iD.Way({id: 'building_yes', tags: {area: 'yes', amenity: 'school', building: 'yes'}, version: 1}), + iD.Way({id: 'boundary', tags: {boundary: 'administrative'}, version: 1}), + iD.Way({id: 'fence', tags: {barrier: 'fence'}, version: 1}) + ]), + all = _.values(graph.base().entities), + stats; + + features.gatherStats(all, graph, dimensions); + stats = features.stats(); + + expect(stats.boundaries).to.eql(1); + expect(stats.buildings).to.eql(1); + expect(stats.landuse).to.eql(0); + expect(stats.major_roads).to.eql(1); + expect(stats.minor_roads).to.eql(0); + expect(stats.others).to.eql(1); + expect(stats.past_future).to.eql(1); + expect(stats.paths).to.eql(0); + expect(stats.points).to.eql(5); + expect(stats.power).to.eql(1); + expect(stats.rail).to.eql(2); + expect(stats.water).to.eql(1); + }); }); describe("matching", function() { @@ -337,155 +381,125 @@ describe('iD.Features', function() { 'motorway_construction', ]); }); - }); - it('hides child vertices on a hidden way', function() { - var a = iD.Node({id: 'a', version: 1}), - b = iD.Node({id: 'b', version: 1}), - w = iD.Way({id: 'w', nodes: [a.id, b.id], tags: {highway: 'path'}, version: 1}), - graph = iD.Graph([a, b, w]), + + describe('hiding', function() { + it('hides child vertices on a hidden way', function() { + var a = iD.Node({id: 'a', version: 1}), + b = iD.Node({id: 'b', version: 1}), + w = iD.Way({id: 'w', nodes: [a.id, b.id], tags: {highway: 'path'}, version: 1}), + graph = iD.Graph([a, b, w]), + all = _.values(graph.base().entities); + + features.disable('paths'); + features.gatherStats(all, graph, dimensions); + + expect(features.isHiddenChild(a, graph)).to.be.true; + expect(features.isHidden(a, graph)).to.be.true; + }); + + it('hides child ways on a hidden multipolygon relation', function() { + var a = iD.Node({id: 'a', version: 1}), + b = iD.Node({id: 'b', version: 1}), + c = iD.Node({id: 'c', version: 1}), + d = iD.Node({id: 'd', version: 1}), + e = iD.Node({id: 'e', version: 1}), + f = iD.Node({id: 'f', version: 1}), + outer = iD.Way({id: 'outer', nodes: [a.id, b.id, c.id, a.id], tags: {area: 'yes', natural: 'wood'}, version: 1}), + inner = iD.Way({id: 'inner', nodes: [d.id, e.id, f.id, d.id], version: 1}), + r = iD.Relation({ + id: 'r', + tags: {type: 'multipolygon'}, + members: [ + {id: outer.id, role: 'outer', type: 'way'}, + {id: inner.id, role: 'inner', type: 'way'} + ], + version: 1 + }), + graph = iD.Graph([a, b, c, d, e, f, outer, inner, r]), + all = _.values(graph.base().entities); + + features.disable('landuse'); + features.gatherStats(all, graph, dimensions); + + expect(features.isHiddenChild(inner, graph)).to.be.true; + expect(features.isHidden(inner, graph)).to.be.true; + }); + + it('hides only versioned entities', function() { + var a = iD.Node({id: 'a', version: 1}), + b = iD.Node({id: 'b'}), + graph = iD.Graph([a, b]), + all = _.values(graph.base().entities); + + features.disable('points'); + features.gatherStats(all, graph, dimensions); + + expect(features.isHidden(a, graph)).to.be.true; + expect(features.isHidden(b, graph)).to.be.false; + }); + + it('auto-hides features', function() { + var graph = iD.Graph([]), + maxPoints = 200, + all, hidden, autoHidden, i, msg; + + for(i = 0; i < maxPoints; i++) { + graph.rebase([iD.Node({version: 1})], [graph]); + } + all = _.values(graph.base().entities); + features.gatherStats(all, graph, dimensions); + hidden = features.hidden(); + autoHidden = features.autoHidden(); + msg = i + ' points'; - features.disable('paths'); - features.gatherStats(all, graph, dimensions); + expect(hidden, msg).to.not.include('points'); + expect(autoHidden, msg).to.not.include('points'); - expect(features.isHiddenChild(a, graph)).to.be.true; - expect(features.isHidden(a, graph)).to.be.true; - }); - - it('hides child ways on a hidden multipolygon relation', function() { - var a = iD.Node({id: 'a', version: 1}), - b = iD.Node({id: 'b', version: 1}), - c = iD.Node({id: 'c', version: 1}), - d = iD.Node({id: 'd', version: 1}), - e = iD.Node({id: 'e', version: 1}), - f = iD.Node({id: 'f', version: 1}), - outer = iD.Way({id: 'outer', nodes: [a.id, b.id, c.id, a.id], tags: {area: 'yes', natural: 'wood'}, version: 1}), - inner = iD.Way({id: 'inner', nodes: [d.id, e.id, f.id, d.id], version: 1}), - r = iD.Relation({ - id: 'r', - tags: {type: 'multipolygon'}, - members: [ - {id: outer.id, role: 'outer', type: 'way'}, - {id: inner.id, role: 'inner', type: 'way'} - ], - version: 1 - }), - graph = iD.Graph([a, b, c, d, e, f, outer, inner, r]), - all = _.values(graph.base().entities); - - features.disable('landuse'); - features.gatherStats(all, graph, dimensions); - - expect(features.isHiddenChild(inner, graph)).to.be.true; - expect(features.isHidden(inner, graph)).to.be.true; - }); - - it('hides only versioned entities', function() { - var a = iD.Node({id: 'a', version: 1}), - b = iD.Node({id: 'b'}), - graph = iD.Graph([a, b]), - all = _.values(graph.base().entities); - - features.disable('points'); - features.gatherStats(all, graph, dimensions); - - expect(features.isHidden(a, graph)).to.be.true; - expect(features.isHidden(b, graph)).to.be.false; - }); - - it('counts features', function() { - var graph = iD.Graph([ - iD.Node({id: 'point_bar', tags: {amenity: 'bar'}, version: 1}), - iD.Node({id: 'point_dock', tags: {waterway: 'dock'}, version: 1}), - iD.Node({id: 'point_rail_station', tags: {railway: 'station'}, version: 1}), - iD.Node({id: 'point_generator', tags: {power: 'generator'}, version: 1}), - iD.Node({id: 'point_old_rail_station', tags: {railway: 'station', disused: 'yes'}, version: 1}), - iD.Way({id: 'motorway', tags: {highway: 'motorway'}, version: 1}), - iD.Way({id: 'building_yes', tags: {area: 'yes', amenity: 'school', building: 'yes'}, version: 1}), - iD.Way({id: 'boundary', tags: {boundary: 'administrative'}, version: 1}), - iD.Way({id: 'fence', tags: {barrier: 'fence'}, version: 1}) - ]), - all = _.values(graph.base().entities), - stats; - - features.gatherStats(all, graph, dimensions); - stats = features.stats(); - - expect(stats.boundaries).to.eql(1); - expect(stats.buildings).to.eql(1); - expect(stats.landuse).to.eql(0); - expect(stats.major_roads).to.eql(1); - expect(stats.minor_roads).to.eql(0); - expect(stats.others).to.eql(1); - expect(stats.past_future).to.eql(1); - expect(stats.paths).to.eql(0); - expect(stats.points).to.eql(5); - expect(stats.power).to.eql(1); - expect(stats.rail).to.eql(2); - expect(stats.water).to.eql(1); - }); - - it('auto-hides features', function() { - var graph = iD.Graph([]), - maxPoints = 200, - all, hidden, autoHidden, i, msg; - - for(i = 0; i < maxPoints; i++) { graph.rebase([iD.Node({version: 1})], [graph]); - } - all = _.values(graph.base().entities); - features.gatherStats(all, graph, dimensions); - hidden = features.hidden(); - autoHidden = features.autoHidden(); - msg = i + ' points'; + all = _.values(graph.base().entities); + features.gatherStats(all, graph, dimensions); + hidden = features.hidden(); + autoHidden = features.autoHidden(); + msg = ++i + ' points'; - expect(hidden, msg).to.not.include('points'); - expect(autoHidden, msg).to.not.include('points'); + expect(hidden, msg).to.include('points'); + expect(autoHidden, msg).to.include('points'); + }); - graph.rebase([iD.Node({version: 1})], [graph]); + it('doubles auto-hide threshold when doubling viewport size', function() { + var graph = iD.Graph([]), + maxPoints = 400, + dimensions = [2000, 1000], + all, hidden, autoHidden, i, msg; - all = _.values(graph.base().entities); - features.gatherStats(all, graph, dimensions); - hidden = features.hidden(); - autoHidden = features.autoHidden(); - msg = ++i + ' points'; + for(i = 0; i < maxPoints; i++) { + graph.rebase([iD.Node({version: 1})], [graph]); + } - expect(hidden, msg).to.include('points'); - expect(autoHidden, msg).to.include('points'); - }); + all = _.values(graph.base().entities); + features.gatherStats(all, graph, dimensions); + hidden = features.hidden(); + autoHidden = features.autoHidden(); + msg = i + ' points'; - it('doubles auto-hide threshold when doubling viewport size', function() { - var graph = iD.Graph([]), - maxPoints = 400, - dimensions = [2000, 1000], - all, hidden, autoHidden, i, msg; + expect(hidden, msg).to.not.include('points'); + expect(autoHidden, msg).to.not.include('points'); - for(i = 0; i < maxPoints; i++) { graph.rebase([iD.Node({version: 1})], [graph]); - } - all = _.values(graph.base().entities); - features.gatherStats(all, graph, dimensions); - hidden = features.hidden(); - autoHidden = features.autoHidden(); - msg = i + ' points'; + all = _.values(graph.base().entities); + features.gatherStats(all, graph, dimensions); + hidden = features.hidden(); + autoHidden = features.autoHidden(); + msg = ++i + ' points'; - expect(hidden, msg).to.not.include('points'); - expect(autoHidden, msg).to.not.include('points'); - - graph.rebase([iD.Node({version: 1})], [graph]); - - all = _.values(graph.base().entities); - features.gatherStats(all, graph, dimensions); - hidden = features.hidden(); - autoHidden = features.autoHidden(); - msg = ++i + ' points'; - - expect(hidden, msg).to.include('points'); - expect(autoHidden, msg).to.include('points'); + expect(hidden, msg).to.include('points'); + expect(autoHidden, msg).to.include('points'); + }); }); }); From 7c50c01bd3ccc8fc058a063be8dd1180d17fd21b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 29 Oct 2014 16:06:41 -0400 Subject: [PATCH 27/41] mapdata checkboxes should use features.enabled() --- js/id/ui/map_data.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index 16d308ad2..1fd7fb64c 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -8,7 +8,7 @@ iD.ui.MapData = function(context) { function map_data(selection) { function showsFeature(d) { - return autoHiddenFeature(d) ? null : !context.features().hidden(d); + return autoHiddenFeature(d) ? null : context.features().enabled(d); } function autoHiddenFeature(d) { @@ -88,7 +88,6 @@ iD.ui.MapData = function(context) { if (name === 'feature') { items .selectAll('input') - .property('disabled', autoHiddenFeature) .property('indeterminate', autoHiddenFeature); } From 9c32b89e6ac7a24113fc1ab2b0331ef2e4cf88ec Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 29 Oct 2014 16:14:43 -0400 Subject: [PATCH 28/41] When showing mapdata, background, or help panel, hide others --- js/id/ui/background.js | 6 ++++-- js/id/ui/help.js | 4 +++- js/id/ui/map_data.js | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/js/id/ui/background.js b/js/id/ui/background.js index f2e240f6a..b9fc8eedf 100644 --- a/js/id/ui/background.js +++ b/js/id/ui/background.js @@ -294,8 +294,10 @@ iD.ui.Background = function(context) { update(); setOpacity(opacityDefault); - var keybinding = d3.keybinding('background'); - keybinding.on(key, toggle); + var keybinding = d3.keybinding('background') + .on(key, toggle) + .on('F', hide) + .on('H', hide); d3.select(document) .call(keybinding); diff --git a/js/id/ui/help.js b/js/id/ui/help.js index c4e073c80..820318925 100644 --- a/js/id/ui/help.js +++ b/js/id/ui/help.js @@ -140,7 +140,9 @@ iD.ui.Help = function(context) { clickHelp(docs[0], 0); var keybinding = d3.keybinding('help') - .on(key, toggle); + .on(key, toggle) + .on('B', hide) + .on('F', hide); d3.select(document) .call(keybinding); diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index 1fd7fb64c..e4acc44e3 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -314,7 +314,9 @@ iD.ui.MapData = function(context) { var keybinding = d3.keybinding('features') .on(key, togglePanel) - .on('W', toggleWireframe); + .on('W', toggleWireframe) + .on('B', hidePanel) + .on('H', hidePanel); // keybinding.on('m', function() { // context.enter(iD.modes.SelectImage(context)); From 6b31eab5a7bd5ba292fbecde52eebf886fae3b39 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 30 Oct 2014 15:01:03 -0400 Subject: [PATCH 29/41] Deselect features that are hidden.. --- js/id/renderer/features.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 98d8cdba4..20b516fd8 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -286,9 +286,15 @@ iD.Features = function(context) { }; features.filter = function(d, graph) { + var selected = context.selectedIDs(); resolver = graph || resolver; + return features.hidden().length ? _.reject(d, function(e) { - return features.isHidden(e, resolver); + var hidden = features.isHidden(e, resolver); + if (hidden && _.contains(selected, e.id)) { + context.enter(iD.modes.Browse(context)); + } + return hidden; }) : d; }; From 224db06ca45341738ecbe0f25ee4917634cb9507 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 31 Oct 2014 10:55:00 -0400 Subject: [PATCH 30/41] design enhancements.. * replace full width bottom bar with subtle list item * move hidden feature details to list item tooltip * simplify some of the text labels on the Map Data panel * progressively hide some footer elements with media queries --- css/app.css | 57 ++++++++++++++++++++++++---------------- data/core.yaml | 7 ++--- dist/locales/en.json | 7 ++--- js/id/ui.js | 22 ++++++++-------- js/id/ui/account.js | 8 +++--- js/id/ui/feature_info.js | 29 ++++++++++++++------ js/id/ui/map_data.js | 8 ++---- 7 files changed, 80 insertions(+), 58 deletions(-) diff --git a/css/app.css b/css/app.css index 3d8fd3747..601326ed9 100644 --- a/css/app.css +++ b/css/app.css @@ -275,18 +275,6 @@ ul li { list-style: none;} background: #E8EBFF; } -.link-list li { - float: right; - border-left: 1px solid rgba(255,255,255,.5); - padding: 5px 0 5px 5px; - margin-left: 5px; -} - -ul.link-list li:last-child { - border-left: 0; - margin-left: 0; - padding-left: 0; -} /* Utility Classes ------------------------------------------------------- */ @@ -316,7 +304,8 @@ ul.link-list li:last-child { div.hide, form.hide, button.hide, -a.hide { +a.hide, +li.hide { display: none; } @@ -2327,6 +2316,19 @@ img.wiki-image { clear: right; } +#about-list li { + float: right; + border-left: 1px solid rgba(255,255,255,.5); + padding: 5px 0 5px 5px; + margin-left: 5px; +} + +#about-list li:last-child { + border-left: 0; + margin-left: 0; + padding-left: 0; +} + .source-switch a { padding: 2px 4px 4px 4px; border-radius: 2px; @@ -2336,18 +2338,18 @@ img.wiki-image { color:#fff; } -@media screen and (max-width: 1200px) { - .user-list { - display: none !important; - } +.feature-warning a { + background: #1e90ff; + padding: 2px 4px 4px 4px; + border-radius: 2px; + color: #eee; } .user-list a:not(:last-child):after { content: ', '; } -.api-status, -.feature-info { +.api-status { float: right; clear: both; text-align: right; @@ -2361,10 +2363,6 @@ img.wiki-image { background: red; } -.feature-info { - background: #1e90ff; -} - /* Modals ------------------------------------------------------- */ @@ -2886,6 +2884,19 @@ img.wiki-image { #bar .save .label { display: block;} } +@media screen and (max-width: 1200px) { + .user-list { display: none !important; } +} + +@media screen and (max-width: 1000px) { + #userLink { display: none !important; } +} + +@media screen and (max-width: 900px) { + #scale-block { display: none !important; } +} + + /* Scrollbars ----------------------------------------------------- */ diff --git a/data/core.yaml b/data/core.yaml index 362a49eb6..095778d28 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -181,7 +181,8 @@ en: loading_auth: "Connecting to OpenStreetMap..." report_a_bug: report a bug feature_info: - hidden_features: "Hidden: {features}" + hidden_warning: "{count} hidden features" + hidden_details: "These features are currently hidden: {details}" status: error: Unable to connect to API. offline: The API is offline. Please try editing later. @@ -250,9 +251,9 @@ en: map_data: title: Map Data description: Map Data - show_features: Show Map Features - show_layers: Show Data Layers + data_layers: Data Layers fill_area: Fill Areas + map_features: Map Features autohidden: "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them." feature: points: diff --git a/dist/locales/en.json b/dist/locales/en.json index 1e7a82206..46267ce6c 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -227,7 +227,8 @@ "loading_auth": "Connecting to OpenStreetMap...", "report_a_bug": "report a bug", "feature_info": { - "hidden_features": "Hidden: {features}" + "hidden_warning": "{count} hidden features", + "hidden_details": "These features are currently hidden: {details}" }, "status": { "error": "Unable to connect to API.", @@ -305,9 +306,9 @@ "map_data": { "title": "Map Data", "description": "Map Data", - "show_features": "Show Map Features", - "show_layers": "Show Data Layers", + "data_layers": "Data Layers", "fill_area": "Fill Areas", + "map_features": "Map Features", "autohidden": "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them." }, "feature": { diff --git a/js/id/ui.js b/js/id/ui.js index 66505eef0..23acc2649 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -96,24 +96,23 @@ iD.ui = function(context) { .attr('id', 'scale-block') .call(iD.ui.Scale(context)); - var linkList = footer.append('div') + var aboutList = footer.append('div') .attr('id', 'info-block') .append('ul') - .attr('id', 'about-list') - .attr('class', 'link-list'); + .attr('id', 'about-list'); if (!context.embed()) { - linkList.call(iD.ui.Account(context)); + aboutList.call(iD.ui.Account(context)); } - linkList.append('li') + aboutList.append('li') .append('a') .attr('target', '_blank') .attr('tabindex', -1) .attr('href', 'http://github.com/openstreetmap/iD') .text(iD.version); - var bugReport = linkList.append('li') + var bugReport = aboutList.append('li') .append('a') .attr('target', '_blank') .attr('tabindex', -1) @@ -127,15 +126,16 @@ iD.ui = function(context) { .placement('top') ); - linkList.append('li') + aboutList.append('li') + .attr('class', 'feature-warning') + .attr('tabindex', -1) + .call(iD.ui.FeatureInfo(context)); + + aboutList.append('li') .attr('class', 'user-list') .attr('tabindex', -1) .call(iD.ui.Contributors(context)); - footer.append('div') - .attr('class', 'feature-info') - .call(iD.ui.FeatureInfo(context)); - footer.append('div') .attr('class', 'api-status') .call(iD.ui.Status(context)); diff --git a/js/id/ui/account.js b/js/id/ui/account.js index d7f32b0b7..a7360561c 100644 --- a/js/id/ui/account.js +++ b/js/id/ui/account.js @@ -4,7 +4,7 @@ iD.ui.Account = function(context) { function update(selection) { if (!connection.authenticated()) { selection.selectAll('#userLink, #logoutLink') - .style('display', 'none'); + .classed('hide', true); return; } @@ -18,7 +18,7 @@ iD.ui.Account = function(context) { if (err) return; selection.selectAll('#userLink, #logoutLink') - .style('display', 'list-item'); + .classed('hide', false); // Link userLink.append('a') @@ -54,11 +54,11 @@ iD.ui.Account = function(context) { return function(selection) { selection.append('li') .attr('id', 'logoutLink') - .style('display', 'none'); + .classed('hide', true); selection.append('li') .attr('id', 'userLink') - .style('display', 'none'); + .classed('hide', true); connection.on('auth.account', function() { update(selection); }); update(selection); diff --git a/js/id/ui/feature_info.js b/js/id/ui/feature_info.js index a90d69eae..81e429ec2 100644 --- a/js/id/ui/feature_info.js +++ b/js/id/ui/feature_info.js @@ -7,19 +7,32 @@ iD.ui.FeatureInfo = function(context) { if (hidden.length) { var stats = features.stats(), + count = 0, hiddenList = _.map(hidden, function(k) { + count += stats[k]; return String(stats[k]) + ' ' + t('feature.' + k + '.description'); + }), + tooltip = bootstrap.tooltip() + .placement('top') + .html(true) + .title(function() { + return iD.ui.tooltipHtml(hiddenList.join('
')); + }); + + var warning = selection.append('a') + .attr('href', '#') + .attr('tabindex', -1) + .html(t('feature_info.hidden_warning', { count: count })) + .call(tooltip) + .on('click', function() { + tooltip.hide(warning); + // open map data panel? + d3.event.preventDefault(); }); - - selection.append('span') - .html(t('feature_info.hidden_features', { features: hiddenList.join(', ') })); } - if (!hidden.length) { - selection.transition().duration(200).style('opacity', 0); - } else if (selection.style('opacity') === '0') { - selection.transition().duration(200).style('opacity', 1); - } + selection + .classed('hide', !hidden.length); } return function(selection) { diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index e4acc44e3..a5cc8584a 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -182,7 +182,7 @@ iD.ui.MapData = function(context) { // data layers content.append('a') - .text(t('map_data.show_layers')) + .text(t('map_data.data_layers')) .attr('href', '#') .classed('hide-toggle', true) .classed('expanded', true) @@ -287,7 +287,7 @@ iD.ui.MapData = function(context) { // feature filters content.append('a') - .text(t('map_data.show_features')) + .text(t('map_data.map_features')) .attr('href', '#') .classed('hide-toggle', true) .classed('expanded', false) @@ -318,10 +318,6 @@ iD.ui.MapData = function(context) { .on('B', hidePanel) .on('H', hidePanel); - // keybinding.on('m', function() { - // context.enter(iD.modes.SelectImage(context)); - // }); - d3.select(document) .call(keybinding); From c941bd5a4b3742ffeadae1d170d70f15e08d1996 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 31 Oct 2014 11:58:02 -0400 Subject: [PATCH 31/41] 2 things related to gatherStats auto-hiding.. * skip gatherStats on differenced redraws.. * force full redraw when gatherStats autohides/unhides features (before: extent redraws would happen, counts would update, but buildings/points would stay on the screen) --- js/id/renderer/features.js | 7 ++++++- js/id/renderer/map.js | 23 +++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 20b516fd8..30da65e89 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -212,7 +212,9 @@ iD.Features = function(context) { features.gatherStats = function(d, graph, dimensions) { var hidden = features.hidden(), - keys = features.keys(); + keys = features.keys(), + needsRedraw = false; + resolver = graph || resolver; _.each(feature, function(f) { f.count = 0; }); @@ -231,7 +233,10 @@ iD.Features = function(context) { if (hidden !== features.hidden()) { dispatch.change(); + needsRedraw = true; } + + return needsRedraw; }; features.stats = function() { diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index e26b5cb0a..15c22a5a6 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -105,17 +105,24 @@ iD.Map = function(context) { data = _.compact(_.values(complete)); filter = function(d) { return d.id in complete; }; - } else if (extent) { - data = context.intersects(map.extent().intersection(extent)); - var set = d3.set(_.pluck(data, 'id')); - filter = function(d) { return set.has(d.id); }; - } else { - data = all; - filter = d3.functor(true); + // force a full redraw if gatherStats detects that a feature + // should be auto-hidden (e.g. points or buildings).. + if (features.gatherStats(all, graph, dimensions)) { + extent = undefined; + } + + if (extent) { + data = context.intersects(map.extent().intersection(extent)); + var set = d3.set(_.pluck(data, 'id')); + filter = function(d) { return set.has(d.id); }; + + } else { + data = all; + filter = d3.functor(true); + } } - features.gatherStats(all, graph, dimensions); data = features.filter(data, graph); surface From 2f6004fcfa1fc34a37e530be0470917a16e6c673 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 5 Nov 2014 00:27:14 -0500 Subject: [PATCH 32/41] performance tuning work-in-progress * store entity's matched features in a graph transient * replace fn calls to get commonly used _keys and _hidden arrays * replace lodash calls with faster for loops (in some places) * always pass graph/resolver as a param --- js/id/core/entity.js | 6 ++ js/id/core/graph.js | 22 +++- js/id/modes/drag_node.js | 4 +- js/id/operations/continue.js | 2 +- js/id/renderer/features.js | 192 ++++++++++++++++++++++------------- js/id/renderer/map.js | 2 +- js/id/svg/vertices.js | 2 +- 7 files changed, 155 insertions(+), 75 deletions(-) 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++) { From 788dedc595328580a3afc7082b2741acfb132363 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 6 Nov 2014 00:18:45 -0500 Subject: [PATCH 33/41] more performance improvements for feature filters * use named filter functions for easier profiling * remove some unnecessary line/area geometry checks * replace some lodash code with for loops * reorder some tests to put cheap condition early --- js/id/renderer/features.js | 112 +++++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 43 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 7041c35ed..005382eaa 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -69,46 +69,45 @@ iD.Features = function(context) { } - defineFeature('points', function(entity, resolver) { + defineFeature('points', function isPoint(entity, resolver) { return entity.geometry(resolver) === 'point'; }, 200); - defineFeature('major_roads', function(entity, resolver) { - return entity.geometry(resolver) === 'line' && major_roads[entity.tags.highway]; + defineFeature('major_roads', function isMajorRoad(entity) { + return major_roads[entity.tags.highway]; }); - defineFeature('minor_roads', function(entity, resolver) { - return entity.geometry(resolver) === 'line' && minor_roads[entity.tags.highway]; + defineFeature('minor_roads', function isMinorRoad(entity) { + return minor_roads[entity.tags.highway]; }); - defineFeature('paths', function(entity, resolver) { - return entity.geometry(resolver) === 'line' && paths[entity.tags.highway]; + defineFeature('paths', function isPath(entity) { + return paths[entity.tags.highway]; }); - defineFeature('buildings', function(entity, resolver) { + defineFeature('buildings', function isBuilding(entity) { return ( - entity.geometry(resolver) === 'area' && ( - (!!entity.tags.building && entity.tags.building !== 'no') || - entity.tags.amenity === 'shelter' || - entity.tags.parking === 'multi-storey' || - entity.tags.parking === 'sheds' || - entity.tags.parking === 'carports' || - entity.tags.parking === 'garage_boxes' - ) - ) || !!entity.tags['building:part']; + !!entity.tags['building:part'] || + (!!entity.tags.building && entity.tags.building !== 'no') || + entity.tags.amenity === 'shelter' || + entity.tags.parking === 'multi-storey' || + entity.tags.parking === 'sheds' || + entity.tags.parking === 'carports' || + entity.tags.parking === 'garage_boxes' + ); }, 250); - defineFeature('landuse', function(entity, resolver) { + defineFeature('landuse', function isLanduse(entity, resolver) { return entity.geometry(resolver) === 'area' && - !feature.buildings.filter(entity, resolver) && - !feature.water.filter(entity, resolver); + !feature.buildings.filter(entity) && + !feature.water.filter(entity); }); - defineFeature('boundaries', function(entity) { + defineFeature('boundaries', function isBoundary(entity) { return !!entity.tags.boundary; }); - defineFeature('water', function(entity) { + defineFeature('water', function isWater(entity) { return ( !!entity.tags.waterway || entity.tags.natural === 'water' || @@ -121,38 +120,66 @@ iD.Features = function(context) { ); }); - defineFeature('rail', function(entity, resolver) { - return !( - feature.major_roads.filter(entity, resolver) || - feature.minor_roads.filter(entity, resolver) || - feature.paths.filter(entity, resolver) - ) && ( + defineFeature('rail', function isRail(entity) { + return ( !!entity.tags.railway || entity.tags.landuse === 'railway' + ) && !( + major_roads[entity.tags.highway] || + minor_roads[entity.tags.highway] || + paths[entity.tags.highway] ); }); - defineFeature('power', function(entity) { + defineFeature('power', function isPower(entity) { return !!entity.tags.power; }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc.. - defineFeature('past_future', function(entity, resolver) { - var strings = _.flatten(_.pairs(entity.tags)); - return !( - 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]; }); + defineFeature('past_future', function isPastFuture(entity) { + if ( + major_roads[entity.tags.highway] || + minor_roads[entity.tags.highway] || + paths[entity.tags.highway] + ) { return false; } + + var strings = Object.keys(entity.tags); + + for (var i = 0, imax = strings.length; i !== imax; i++) { + var s = strings[i]; + if (past_futures[s] || past_futures[entity.tags[s]]) { return true; } + } + return false; + + + // var strings = _.flatten(_.pairs(entity.tags)); + // return !( + // major_roads[entity.tags.highway] || + // minor_roads[entity.tags.highway] || + // paths[entity.tags.highway] + // ) && _.any(strings, function(s) { return past_futures[s]; }); }); // lines or areas that don't match another feature filter. - defineFeature('others', function(entity, resolver) { + defineFeature('others', function isOther(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); + return (geom === 'line' || geom === 'area') && !( + feature.major_roads.filter(entity, resolver) || + feature.minor_roads.filter(entity, resolver) || + feature.paths.filter(entity, resolver) || + feature.buildings.filter(entity, resolver) || + feature.landuse.filter(entity, resolver) || + feature.boundaries.filter(entity, resolver) || + feature.water.filter(entity, resolver) || + feature.rail.filter(entity, resolver) || + feature.power.filter(entity, resolver) || + feature.past_future.filter(entity, resolver) + ); + + // return (geom === 'line' || geom === 'area') && + // _.reduce(_.omit(feature, 'others'), function(result, v) { + // return result && !v.filter(entity, resolver); + // }, true); }); @@ -278,8 +305,7 @@ iD.Features = function(context) { }; features.isHiddenChild = function(entity, resolver) { - var parents = []; - parents.push.apply(parents, resolver.parentWays(entity)); + var parents = resolver.parentWays(entity); parents.push.apply(parents, resolver.parentRelations(entity)); if (!parents.length) { From ace44d2e7d3631c51c58de87e0d2d43cba1c1198 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 6 Nov 2014 00:22:19 -0500 Subject: [PATCH 34/41] fix highway-pedestrian line width to work in wireframe --- css/map.css | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/css/map.css b/css/map.css index 149f6748d..8ec63785b 100644 --- a/css/map.css +++ b/css/map.css @@ -560,12 +560,12 @@ path.casing.tag-highway-living_street { path.stroke.line.tag-highway-pedestrian { stroke:#fff; stroke-dasharray: 2, 8; - stroke-width:4 !important; + stroke-width:4; shapeRendering: auto; } path.casing.line.tag-highway-pedestrian { stroke:#8cd05f; - stroke-width:6 !important; + stroke-width:6; } path.stroke.area.tag-highway-pedestrian { stroke:#fff; @@ -1205,8 +1205,6 @@ text.gpx { /* Fill Styles */ .low-zoom.fill-wireframe path.stroke, .fill-wireframe path.stroke { - /* stroke-width: 2; */ - /* stroke-opacity: 0.5; */ stroke-width: 1; stroke-opacity: 0.5; stroke-dasharray: none; From 0d6d4b3d24ce59e3ab2512c0405fc720eed7eb5b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 6 Nov 2014 00:33:45 -0500 Subject: [PATCH 35/41] remove footer "features hidden" warning in editOff --- js/id/renderer/features.js | 5 +++++ js/id/renderer/map.js | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 005382eaa..a9fddb499 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -238,6 +238,11 @@ iD.Features = function(context) { } }; + features.resetStats = function() { + _.each(feature, function(f) { f.count = 0; }); + dispatch.change(); + }; + features.gatherStats = function(d, resolver, dimensions) { var needsRedraw = false, currHidden; diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 016b896ce..2b9eff547 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -138,11 +138,14 @@ iD.Map = function(context) { function editOff() { var mode = context.mode(); + + context.features().resetStats(); surface.selectAll('.layer *').remove(); - dispatch.drawn({full: true}); if (!(mode && mode.id === 'browse')) { context.enter(iD.modes.Browse(context)); } + + dispatch.drawn({full: true}); } function zoomPan() { From 55e6f9ec936c1a432f69ce823ba1a5fc410e3375 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 6 Nov 2014 12:54:58 -0500 Subject: [PATCH 36/41] faster tests for certain geometry (vertex, point) --- js/id/renderer/features.js | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index a9fddb499..4aefe6e6a 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -288,7 +288,10 @@ iD.Features = function(context) { }; features.match = function(entity, resolver) { - var result = []; + var result = [], + geometry = entity.geometry(resolver); + + if (geometry === 'vertex') { return []; } for (var i = 0, imax = _keys.length; i !== imax; i++) { if (feature[_keys[i]].filter(entity, resolver)) { result.push(_keys[i]); } @@ -309,13 +312,20 @@ iD.Features = function(context) { // return _.any(features.hidden(), function(k) { return feature[k].filter(entity, resolver); }); }; - features.isHiddenChild = function(entity, resolver) { - var parents = resolver.parentWays(entity); - parents.push.apply(parents, resolver.parentRelations(entity)); + features.isHiddenChild = function(entity, resolver, geom) { + var geometry = geom || entity.geometry(resolver), + parents; - if (!parents.length) { + if (geometry === 'point') { return false; + } else if (geometry === 'vertex') { + parents = resolver.parentWays(entity); + } else { // 'line', 'area', 'relation' + 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; @@ -351,8 +361,14 @@ iD.Features = function(context) { }; features.isHidden = function(entity, resolver) { - return !!entity.version && - (features.isHiddenFeature(entity, resolver) || features.isHiddenChild(entity, resolver)); + if (!entity.version) return false; + + var geometry = entity.geometry(resolver); + if (geometry === 'vertex') return features.isHiddenChild(entity, resolver, geometry); + if (geometry === 'point') return features.isHiddenFeature(entity, resolver); + + return (features.isHiddenFeature(entity, resolver) || + features.isHiddenChild(entity, resolver, geometry)); }; features.filter = function(d, resolver) { From d5879fa855808a0795ef54b041cadcf4ca6edf8d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 6 Nov 2014 23:04:25 -0500 Subject: [PATCH 37/41] reuse _stats, don't count vertices and relations --- js/id/renderer/features.js | 106 ++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 4aefe6e6a..8bf76df88 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -42,8 +42,9 @@ iD.Features = function(context) { }; var dispatch = d3.dispatch('change', 'redraw'), - feature = {}, - cullFactor = 1, + _cullFactor = 1, + _feature = {}, + _stats = {}, _keys = [], _hidden = []; @@ -55,7 +56,7 @@ iD.Features = function(context) { function defineFeature(k, filter, max) { _keys.push(k); - feature[k] = { + _feature[k] = { filter: filter, enabled: true, // whether the user wants it enabled.. count: 0, @@ -63,7 +64,7 @@ iD.Features = function(context) { defaultMax: (max || Infinity), enable: function() { this.enabled = true; this.currentMax = this.defaultMax; }, disable: function() { this.enabled = false; this.currentMax = 0; }, - hidden: function() { return this.count > this.currentMax * cullFactor; }, + hidden: function() { return this.count > this.currentMax * _cullFactor; }, autoHidden: function() { return this.hidden() && this.currentMax > 0; } }; } @@ -99,8 +100,8 @@ iD.Features = function(context) { defineFeature('landuse', function isLanduse(entity, resolver) { return entity.geometry(resolver) === 'area' && - !feature.buildings.filter(entity) && - !feature.water.filter(entity); + !_feature.buildings.filter(entity) && + !_feature.water.filter(entity); }); defineFeature('boundaries', function isBoundary(entity) { @@ -164,16 +165,16 @@ iD.Features = function(context) { defineFeature('others', function isOther(entity, resolver) { var geom = entity.geometry(resolver); return (geom === 'line' || geom === 'area') && !( - feature.major_roads.filter(entity, resolver) || - feature.minor_roads.filter(entity, resolver) || - feature.paths.filter(entity, resolver) || - feature.buildings.filter(entity, resolver) || - feature.landuse.filter(entity, resolver) || - feature.boundaries.filter(entity, resolver) || - feature.water.filter(entity, resolver) || - feature.rail.filter(entity, resolver) || - feature.power.filter(entity, resolver) || - feature.past_future.filter(entity, resolver) + _feature.major_roads.filter(entity, resolver) || + _feature.minor_roads.filter(entity, resolver) || + _feature.paths.filter(entity, resolver) || + _feature.buildings.filter(entity, resolver) || + _feature.landuse.filter(entity, resolver) || + _feature.boundaries.filter(entity, resolver) || + _feature.water.filter(entity, resolver) || + _feature.rail.filter(entity, resolver) || + _feature.power.filter(entity, resolver) || + _feature.past_future.filter(entity, resolver) ); // return (geom === 'line' || geom === 'area') && @@ -191,80 +192,83 @@ iD.Features = function(context) { features.enabled = function(k) { if (!arguments.length) { - return _.filter(_keys, function(k) { return feature[k].enabled; }); + return _.filter(_keys, function(k) { return _feature[k].enabled; }); } - return feature[k] && feature[k].enabled; + return _feature[k] && _feature[k].enabled; }; features.disabled = function(k) { if (!arguments.length) { - return _.reject(_keys, function(k) { return feature[k].enabled; }); + return _.reject(_keys, function(k) { return _feature[k].enabled; }); } - return feature[k] && !feature[k].enabled; + return _feature[k] && !_feature[k].enabled; }; features.hidden = function(k) { if (!arguments.length) { - return _.filter(_keys, function(k) { return feature[k].hidden(); }); + return _.filter(_keys, function(k) { return _feature[k].hidden(); }); } - return feature[k] && feature[k].hidden(); + return _feature[k] && _feature[k].hidden(); }; features.autoHidden = function(k) { if (!arguments.length) { - return _.filter(_keys, function(k) { return feature[k].autoHidden(); }); + return _.filter(_keys, function(k) { return _feature[k].autoHidden(); }); } - return feature[k] && feature[k].autoHidden(); + return _feature[k] && _feature[k].autoHidden(); }; features.enable = function(k) { - if (feature[k] && !feature[k].enabled) { - feature[k].enable(); + if (_feature[k] && !_feature[k].enabled) { + _feature[k].enable(); update(); } }; features.disable = function(k) { - if (feature[k] && feature[k].enabled) { - feature[k].disable(); + if (_feature[k] && _feature[k].enabled) { + _feature[k].disable(); update(); } }; features.toggle = function(k) { - if (feature[k]) { - (function(f) { return f.enabled ? f.disable() : f.enable(); }(feature[k])); + if (_feature[k]) { + (function(f) { return f.enabled ? f.disable() : f.enable(); }(_feature[k])); update(); } }; features.resetStats = function() { - _.each(feature, function(f) { f.count = 0; }); + _.each(_feature, function(f) { f.count = 0; }); dispatch.change(); }; features.gatherStats = function(d, resolver, dimensions) { var needsRedraw = false, - currHidden; + currHidden, geometry, feats; - _.each(feature, function(f) { f.count = 0; }); + _.each(_feature, function(f) { f.count = 0; }); // adjust the threshold for point/building culling based on viewport size.. - // a cullFactor of 1 corresponds to a 1000x1000px viewport.. - cullFactor = dimensions[0] * dimensions[1] / 1000000; + // a _cullFactor of 1 corresponds to a 1000x1000px viewport.. + _cullFactor = dimensions[0] * dimensions[1] / 1000000; 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++; + geometry = d[i].geometry(resolver); + if (!(geometry === 'vertex' || geometry === 'relation')) { + feats = d[i].features(this, resolver); + for (var j = 0, jmax = feats.length; j !== jmax; j++) { + _feature[feats[j]].count++; + } } } // _.each(d, function(entity) { - // _.each(entity.features(this, resolver), function(k) { feature[k].count++; }); + // _.each(entity.features(this, resolver), function(k) { _feature[k].count++; }); // // _.each(_keys, function(k) { - // // if (feature[k].filter(entity)) { - // // feature[k].count++; + // // if (_feature[k].filter(entity)) { + // // _feature[k].count++; // // } // // }); // }); @@ -272,19 +276,16 @@ iD.Features = function(context) { currHidden = features.hidden(); if (currHidden !== _hidden) { _hidden = currHidden; - dispatch.change(); needsRedraw = true; + dispatch.change(); } return needsRedraw; }; features.stats = function() { - var stats = {}; - _.each(_keys, function(k) { - stats[k] = feature[k].count; - }); - return stats; + _.each(_keys, function(k) { _stats[k] = _feature[k].count; }); + return _stats; }; features.match = function(entity, resolver) { @@ -294,10 +295,15 @@ iD.Features = function(context) { if (geometry === 'vertex') { return []; } for (var i = 0, imax = _keys.length; i !== imax; i++) { - if (feature[_keys[i]].filter(entity, resolver)) { result.push(_keys[i]); } + if (_keys[i] === 'others' && result.length) { + continue; + } + if (_feature[_keys[i]].filter(entity, resolver)) { + result.push(_keys[i]); + } } return result; - // return _.filter(_keys, function(k) { return feature[k].filter(entity, resolver); }); + // return _.filter(_keys, function(k) { return _feature[k].filter(entity, resolver); }); }; features.isHiddenFeature = function(entity, resolver) { @@ -309,7 +315,7 @@ iD.Features = function(context) { } } return false; - // return _.any(features.hidden(), function(k) { return feature[k].filter(entity, resolver); }); + // return _.any(features.hidden(), function(k) { return _feature[k].filter(entity, resolver); }); }; features.isHiddenChild = function(entity, resolver, geom) { From 30dde37e0283bb25dc2e13862323dec05792b3f0 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 6 Nov 2014 23:47:54 -0500 Subject: [PATCH 38/41] fix partial fill mode - pointer events visible stroke only also, fix wireframe mode - pedestrian style --- css/map.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/css/map.css b/css/map.css index 8ec63785b..b5d3d6613 100644 --- a/css/map.css +++ b/css/map.css @@ -557,18 +557,18 @@ path.casing.tag-highway-living_street { stroke-width:6; } -path.stroke.line.tag-highway-pedestrian { +path.stroke.tag-highway-pedestrian { stroke:#fff; stroke-dasharray: 2, 8; stroke-width:4; - shapeRendering: auto; } -path.casing.line.tag-highway-pedestrian { +path.casing.tag-highway-pedestrian { stroke:#8cd05f; stroke-width:6; } path.stroke.area.tag-highway-pedestrian { stroke:#fff; + stroke-dasharray: none; stroke-width: 2; } @@ -1234,4 +1234,5 @@ text.gpx { .fill-partial path.fill { fill-opacity: 0; stroke-width: 60px; + pointer-events: visibleStroke; } From c6ca68ce80f514c8216266458d7d44afd61bf868 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 8 Nov 2014 00:19:36 -0500 Subject: [PATCH 39/41] create feature _cache to store matches per entity also remove old commented code --- js/id/core/entity.js | 6 -- js/id/core/graph.js | 2 - js/id/renderer/features.js | 162 +++++++++++++++++-------------------- js/id/renderer/map.js | 1 + 4 files changed, 73 insertions(+), 98 deletions(-) diff --git a/js/id/core/entity.js b/js/id/core/entity.js index 71471b0ce..53a7ca17f 100644 --- a/js/id/core/entity.js +++ b/js/id/core/entity.js @@ -97,12 +97,6 @@ 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 d206088aa..9422acc73 100644 --- a/js/id/core/graph.js +++ b/js/id/core/graph.js @@ -59,7 +59,6 @@ iD.Graph.prototype = { } } return result; - // return _.map(this._parentWays[entity.id], this.entity, this); }, isPoi: function(entity) { @@ -82,7 +81,6 @@ iD.Graph.prototype = { } } return result; - // return _.map(this._parentRels[entity.id], this.entity, this); }, childNodes: function(entity) { diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 8bf76df88..41f066ed3 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -43,7 +43,8 @@ iD.Features = function(context) { var dispatch = d3.dispatch('change', 'redraw'), _cullFactor = 1, - _feature = {}, + _cache = {}, + _features = {}, _stats = {}, _keys = [], _hidden = []; @@ -56,7 +57,7 @@ iD.Features = function(context) { function defineFeature(k, filter, max) { _keys.push(k); - _feature[k] = { + _features[k] = { filter: filter, enabled: true, // whether the user wants it enabled.. count: 0, @@ -100,8 +101,8 @@ iD.Features = function(context) { defineFeature('landuse', function isLanduse(entity, resolver) { return entity.geometry(resolver) === 'area' && - !_feature.buildings.filter(entity) && - !_feature.water.filter(entity); + !_features.buildings.filter(entity) && + !_features.water.filter(entity); }); defineFeature('boundaries', function isBoundary(entity) { @@ -151,36 +152,23 @@ iD.Features = function(context) { if (past_futures[s] || past_futures[entity.tags[s]]) { return true; } } return false; - - - // var strings = _.flatten(_.pairs(entity.tags)); - // return !( - // major_roads[entity.tags.highway] || - // minor_roads[entity.tags.highway] || - // paths[entity.tags.highway] - // ) && _.any(strings, function(s) { return past_futures[s]; }); }); // lines or areas that don't match another feature filter. defineFeature('others', function isOther(entity, resolver) { var geom = entity.geometry(resolver); return (geom === 'line' || geom === 'area') && !( - _feature.major_roads.filter(entity, resolver) || - _feature.minor_roads.filter(entity, resolver) || - _feature.paths.filter(entity, resolver) || - _feature.buildings.filter(entity, resolver) || - _feature.landuse.filter(entity, resolver) || - _feature.boundaries.filter(entity, resolver) || - _feature.water.filter(entity, resolver) || - _feature.rail.filter(entity, resolver) || - _feature.power.filter(entity, resolver) || - _feature.past_future.filter(entity, resolver) + _features.major_roads.filter(entity, resolver) || + _features.minor_roads.filter(entity, resolver) || + _features.paths.filter(entity, resolver) || + _features.buildings.filter(entity, resolver) || + _features.landuse.filter(entity, resolver) || + _features.boundaries.filter(entity, resolver) || + _features.water.filter(entity, resolver) || + _features.rail.filter(entity, resolver) || + _features.power.filter(entity, resolver) || + _features.past_future.filter(entity, resolver) ); - - // return (geom === 'line' || geom === 'area') && - // _.reduce(_.omit(feature, 'others'), function(result, v) { - // return result && !v.filter(entity, resolver); - // }, true); }); @@ -192,55 +180,55 @@ iD.Features = function(context) { features.enabled = function(k) { if (!arguments.length) { - return _.filter(_keys, function(k) { return _feature[k].enabled; }); + return _.filter(_keys, function(k) { return _features[k].enabled; }); } - return _feature[k] && _feature[k].enabled; + return _features[k] && _features[k].enabled; }; features.disabled = function(k) { if (!arguments.length) { - return _.reject(_keys, function(k) { return _feature[k].enabled; }); + return _.reject(_keys, function(k) { return _features[k].enabled; }); } - return _feature[k] && !_feature[k].enabled; + return _features[k] && !_features[k].enabled; }; features.hidden = function(k) { if (!arguments.length) { - return _.filter(_keys, function(k) { return _feature[k].hidden(); }); + return _.filter(_keys, function(k) { return _features[k].hidden(); }); } - return _feature[k] && _feature[k].hidden(); + return _features[k] && _features[k].hidden(); }; features.autoHidden = function(k) { if (!arguments.length) { - return _.filter(_keys, function(k) { return _feature[k].autoHidden(); }); + return _.filter(_keys, function(k) { return _features[k].autoHidden(); }); } - return _feature[k] && _feature[k].autoHidden(); + return _features[k] && _features[k].autoHidden(); }; features.enable = function(k) { - if (_feature[k] && !_feature[k].enabled) { - _feature[k].enable(); + if (_features[k] && !_features[k].enabled) { + _features[k].enable(); update(); } }; features.disable = function(k) { - if (_feature[k] && _feature[k].enabled) { - _feature[k].disable(); + if (_features[k] && _features[k].enabled) { + _features[k].disable(); update(); } }; features.toggle = function(k) { - if (_feature[k]) { - (function(f) { return f.enabled ? f.disable() : f.enable(); }(_feature[k])); + if (_features[k]) { + (function(f) { return f.enabled ? f.disable() : f.enable(); }(_features[k])); update(); } }; features.resetStats = function() { - _.each(_feature, function(f) { f.count = 0; }); + _.each(_features, function(f) { f.count = 0; }); dispatch.change(); }; @@ -248,7 +236,7 @@ iD.Features = function(context) { var needsRedraw = false, currHidden, geometry, feats; - _.each(_feature, function(f) { f.count = 0; }); + _.each(_features, function(f) { f.count = 0; }); // adjust the threshold for point/building culling based on viewport size.. // a _cullFactor of 1 corresponds to a 1000x1000px viewport.. @@ -257,22 +245,13 @@ iD.Features = function(context) { for (var i = 0, imax = d.length; i !== imax; i++) { geometry = d[i].geometry(resolver); if (!(geometry === 'vertex' || geometry === 'relation')) { - feats = d[i].features(this, resolver); + feats = Object.keys(features.matchEntity(d[i], resolver)); for (var j = 0, jmax = feats.length; j !== jmax; j++) { - _feature[feats[j]].count++; + _features[feats[j]].count++; } } } - // _.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; @@ -284,38 +263,56 @@ iD.Features = function(context) { }; features.stats = function() { - _.each(_keys, function(k) { _stats[k] = _feature[k].count; }); + _.each(_keys, function(k) { _stats[k] = _features[k].count; }); return _stats; }; - features.match = function(entity, resolver) { - var result = [], - geometry = entity.geometry(resolver); - - if (geometry === 'vertex') { return []; } - - for (var i = 0, imax = _keys.length; i !== imax; i++) { - if (_keys[i] === 'others' && result.length) { - continue; - } - if (_feature[_keys[i]].filter(entity, resolver)) { - result.push(_keys[i]); - } + features.reset = function(d) { + for (var i = 0, imax = d.length; i !== imax; i++) { + features.resetEntity(d[i]); } - return result; - // return _.filter(_keys, function(k) { return _feature[k].filter(entity, resolver); }); + }; + + features.resetEntity = function(entity) { + delete _cache[iD.Entity.key(entity)]; + }; + + features.match = function(d) { + for (var i = 0, imax = d.length; i !== imax; i++) { + features.matchEntity(d[i]); + } + }; + + features.matchEntity = function(entity, resolver) { + var ent = iD.Entity.key(entity); + + if (!_cache[ent]) { + var geometry = entity.geometry(resolver), + matches = {}, + hasMatch = false; + + if (!(geometry === 'vertex' || geometry === 'relation')) { + for (var i = 0, imax = _keys.length; i !== imax; i++) { + if (hasMatch && _keys[i] === 'others') { + continue; + } + if (_features[_keys[i]].filter(entity, resolver)) { + matches[_keys[i]] = hasMatch = true; + } + } + } + _cache[ent] = matches; + } + return _cache[ent]; }; features.isHiddenFeature = function(entity, resolver) { - var feats = entity.features(this, resolver); + var matches = features.matchEntity(entity, resolver); 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; } - } + if (matches[_hidden[i]]) { return true; } } return false; - // return _.any(features.hidden(), function(k) { return _feature[k].filter(entity, resolver); }); }; features.isHiddenChild = function(entity, resolver, geom) { @@ -338,11 +335,6 @@ iD.Features = function(context) { } } 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) { @@ -398,16 +390,6 @@ iD.Features = function(context) { } 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 2b9eff547..f862703af 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -104,6 +104,7 @@ iD.Map = function(context) { var complete = difference.complete(map.extent()); data = _.compact(_.values(complete)); filter = function(d) { return d.id in complete; }; + features.reset(data); } else { // force a full redraw if gatherStats detects that a feature From af3c307b17880ce849a3444f6bd98f5e9a1c00ca Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 10 Nov 2014 22:47:07 -0500 Subject: [PATCH 40/41] cache invalidation and naming things --- js/id/id.js | 1 + js/id/renderer/features.js | 22 +++++++++++++++------- js/id/renderer/map.js | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/js/id/id.js b/js/id/id.js index 20c2795e0..e21d83119 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -97,6 +97,7 @@ window.iD = function () { context.flush = function() { connection.flush(); + features.reset(); history.reset(); return context; }; diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 41f066ed3..7fe20b809 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -267,16 +267,20 @@ iD.Features = function(context) { return _stats; }; - features.reset = function(d) { + features.clear = function(d) { for (var i = 0, imax = d.length; i !== imax; i++) { - features.resetEntity(d[i]); + features.clearEntity(d[i]); } }; - features.resetEntity = function(entity) { + features.clearEntity = function(entity) { delete _cache[iD.Entity.key(entity)]; }; + features.reset = function() { + _cache = {}; + }; + features.match = function(d) { for (var i = 0, imax = d.length; i !== imax; i++) { features.matchEntity(d[i]); @@ -309,6 +313,8 @@ iD.Features = function(context) { features.isHiddenFeature = function(entity, resolver) { var matches = features.matchEntity(entity, resolver); + if (!entity.version) return false; + for (var i = 0, imax = _hidden.length; i !== imax; i++) { if (matches[_hidden[i]]) { return true; } } @@ -319,9 +325,9 @@ iD.Features = function(context) { var geometry = geom || entity.geometry(resolver), parents; - if (geometry === 'point') { - return false; - } else if (geometry === 'vertex') { + if (!entity.version || geometry === 'point') { return false; } + + if (geometry === 'vertex') { parents = resolver.parentWays(entity); } else { // 'line', 'area', 'relation' parents = resolver.parentRelations(entity); @@ -359,9 +365,11 @@ iD.Features = function(context) { }; features.isHidden = function(entity, resolver) { + var geometry; + if (!entity.version) return false; - var geometry = entity.geometry(resolver); + geometry = entity.geometry(resolver); if (geometry === 'vertex') return features.isHiddenChild(entity, resolver, geometry); if (geometry === 'point') return features.isHiddenFeature(entity, resolver); diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index f862703af..d5826faf5 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -104,7 +104,7 @@ iD.Map = function(context) { var complete = difference.complete(map.extent()); data = _.compact(_.values(complete)); filter = function(d) { return d.id in complete; }; - features.reset(data); + features.clear(data); } else { // force a full redraw if gatherStats detects that a feature From 2024e2333debe789d0c30a392050ba267429babb Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 10 Nov 2014 23:23:28 -0500 Subject: [PATCH 41/41] Exit select mode if selected DOM elements have disappeared --- js/id/id.js | 2 +- js/id/modes/select.js | 13 ++++++++++--- js/id/renderer/features.js | 14 +++----------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/js/id/id.js b/js/id/id.js index e21d83119..2f52c2f9c 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -205,7 +205,7 @@ window.iD = function () { context.background = function() { return background; }; /* Features */ - var features = iD.Features(context); + var features = iD.Features(); context.features = function() { return features; }; context.hasHiddenConnections = function(id) { var graph = history.graph(), diff --git a/js/id/modes/select.js b/js/id/modes/select.js index 5acee7a84..be2746ba8 100644 --- a/js/id/modes/select.js +++ b/js/id/modes/select.js @@ -137,9 +137,16 @@ iD.modes.Select = function(context, selectedIDs) { .call(keybinding); function selectElements() { - context.surface() - .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph())) - .classed('selected', true); + var selection = context.surface() + .selectAll(iD.util.entityOrMemberSelector(selectedIDs, context.graph())); + + if (selection.empty()) { + // Exit mode if selected DOM elements have disappeared.. + context.enter(iD.modes.Browse(context)); + } else { + selection + .classed('selected', true); + } } context.map().on('drawn.select', selectElements); diff --git a/js/id/renderer/features.js b/js/id/renderer/features.js index 7fe20b809..400932594 100644 --- a/js/id/renderer/features.js +++ b/js/id/renderer/features.js @@ -1,4 +1,4 @@ -iD.Features = function(context) { +iD.Features = function() { var major_roads = { 'motorway': true, 'motorway_link': true, @@ -378,21 +378,13 @@ iD.Features = function(context) { }; features.filter = function(d, resolver) { - var selected = context.selectedIDs(), - result = []; + var result = []; 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 { + if (!features.isHidden(d[i], resolver)) { result.push(d[i]); } }