From 6c1df372987d92a62ff49492605451af62ee8293 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 18 Feb 2016 13:49:29 -0500 Subject: [PATCH 01/15] Move GPX, Mapillary Images, Mapillary Signs into svg namespace --- index.html | 8 ++++---- js/id/renderer/background.js | 6 +++--- js/id/{renderer/gpx_layer.js => svg/gpx.js} | 2 +- .../mapillary_image_layer.js => svg/mapillary_images.js} | 2 +- .../mapillary_sign_layer.js => svg/mapillary_signs.js} | 2 +- js/id/ui/map_in_map.js | 3 +-- test/index.html | 6 +++--- 7 files changed, 14 insertions(+), 15 deletions(-) rename js/id/{renderer/gpx_layer.js => svg/gpx.js} (98%) rename js/id/{renderer/mapillary_image_layer.js => svg/mapillary_images.js} (99%) rename js/id/{renderer/mapillary_sign_layer.js => svg/mapillary_signs.js} (99%) diff --git a/index.html b/index.html index f45f10d6c..fb5c184ff 100644 --- a/index.html +++ b/index.html @@ -56,18 +56,18 @@ - - - - + + + + diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index cfc8d37e0..305ab66e0 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -2,7 +2,7 @@ iD.Background = function(context) { var dispatch = d3.dispatch('change'), baseLayer = iD.TileLayer() .projection(context.projection), - gpxLayer = iD.GpxLayer(context, dispatch) + gpxLayer = iD.svg.Gpx(context, dispatch) .projection(context.projection), mapillaryImageLayer, mapillarySignLayer, @@ -98,7 +98,7 @@ iD.Background = function(context) { .attr('class', 'layer-layer layer-mapillary-images'); if (supportsMapillaryImages) { - if (!mapillaryImageLayer) { mapillaryImageLayer = iD.MapillaryImageLayer(context); } + if (!mapillaryImageLayer) { mapillaryImageLayer = iD.svg.MapillaryImages(context); } mapillaryImages.call(mapillaryImageLayer); } else { mapillaryImageLayer = null; @@ -115,7 +115,7 @@ iD.Background = function(context) { .attr('class', 'layer-layer layer-mapillary-signs'); if (supportsMapillarySigns) { - if (!mapillarySignLayer) { mapillarySignLayer = iD.MapillarySignLayer(context); } + if (!mapillarySignLayer) { mapillarySignLayer = iD.svg.MapillarySigns(context); } mapillarySigns.call(mapillarySignLayer); } else { mapillarySignLayer = null; diff --git a/js/id/renderer/gpx_layer.js b/js/id/svg/gpx.js similarity index 98% rename from js/id/renderer/gpx_layer.js rename to js/id/svg/gpx.js index 9557aa1a4..c12326a1e 100644 --- a/js/id/renderer/gpx_layer.js +++ b/js/id/svg/gpx.js @@ -1,4 +1,4 @@ -iD.GpxLayer = function(context) { +iD.svg.Gpx = function(context) { var projection, gj = {}, enable = true, diff --git a/js/id/renderer/mapillary_image_layer.js b/js/id/svg/mapillary_images.js similarity index 99% rename from js/id/renderer/mapillary_image_layer.js rename to js/id/svg/mapillary_images.js index 544ef011b..6f061cd8b 100644 --- a/js/id/renderer/mapillary_image_layer.js +++ b/js/id/svg/mapillary_images.js @@ -1,4 +1,4 @@ -iD.MapillaryImageLayer = function(context) { +iD.svg.MapillaryImages = function(context) { var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000), enabled = false, minZoom = 12, diff --git a/js/id/renderer/mapillary_sign_layer.js b/js/id/svg/mapillary_signs.js similarity index 99% rename from js/id/renderer/mapillary_sign_layer.js rename to js/id/svg/mapillary_signs.js index 88dea7292..56375bd42 100644 --- a/js/id/renderer/mapillary_sign_layer.js +++ b/js/id/svg/mapillary_signs.js @@ -1,4 +1,4 @@ -iD.MapillarySignLayer = function(context) { +iD.svg.MapillarySigns = function(context) { var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000), enabled = false, minZoom = 12, diff --git a/js/id/ui/map_in_map.js b/js/id/ui/map_in_map.js index d57026aa3..58a08874b 100644 --- a/js/id/ui/map_in_map.js +++ b/js/id/ui/map_in_map.js @@ -2,11 +2,10 @@ iD.ui.MapInMap = function(context) { var key = '/'; function map_in_map(selection) { - var backgroundLayer = iD.TileLayer(), overlayLayers = {}, dispatch = d3.dispatch('change'), - gpxLayer = iD.GpxLayer(context, dispatch), + gpxLayer = iD.svg.Gpx(context, dispatch), projection = iD.geo.RawMercator(), zoom = d3.behavior.zoom() .scaleExtent([ztok(0.5), ztok(24)]) diff --git a/test/index.html b/test/index.html index 83f8b99fc..febd5a40a 100644 --- a/test/index.html +++ b/test/index.html @@ -62,17 +62,17 @@ - - - + + + From f37a809001c30e049a42fc1f352b1d4370f442cb Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 22 Feb 2016 16:52:49 -0500 Subject: [PATCH 02/15] Make point draw function more consistent with other draw functions --- js/id/renderer/map.js | 2 +- js/id/svg/points.js | 7 +++---- test/spec/svg/points.js | 5 +++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index a8d70826d..8e21e7a8b 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -138,7 +138,7 @@ iD.Map = function(context) { .call(areas, graph, data, filter) .call(midpoints, graph, data, filter, map.trimmedExtent()) .call(labels, graph, data, filter, dimensions, !difference && !extent) - .call(points, data, filter); + .call(points, graph, data, filter); dispatch.drawn({full: true}); } diff --git a/js/id/svg/points.js b/js/id/svg/points.js index 378b52d49..d6deb8707 100644 --- a/js/id/svg/points.js +++ b/js/id/svg/points.js @@ -10,9 +10,8 @@ iD.svg.Points = function(projection, context) { return b.loc[1] - a.loc[1]; } - return function drawPoints(surface, entities, filter) { - var graph = context.graph(), - wireframe = surface.classed('fill-wireframe'), + return function drawPoints(surface, graph, entities, filter) { + var wireframe = surface.classed('fill-wireframe'), points = wireframe ? [] : _.filter(entities, function(e) { return e.geometry(graph) === 'point'; }); @@ -49,7 +48,7 @@ iD.svg.Points = function(projection, context) { groups.select('.stroke'); groups.select('.icon') .attr('xlink:href', function(entity) { - var preset = context.presets().match(entity, context.graph()); + var preset = context.presets().match(entity, graph); return preset.icon ? '#' + preset.icon + '-12' : ''; }); diff --git a/test/spec/svg/points.js b/test/spec/svg/points.js index d6a7f5e85..80790bba6 100644 --- a/test/spec/svg/points.js +++ b/test/spec/svg/points.js @@ -10,9 +10,10 @@ describe("iD.svg.Points", function () { }); it("adds tag classes", function () { - var point = iD.Node({tags: {amenity: "cafe"}, loc: [0, 0]}); + var point = iD.Node({tags: {amenity: "cafe"}, loc: [0, 0]}), + graph = iD.Graph([point]); - surface.call(iD.svg.Points(projection, context), [point]); + surface.call(iD.svg.Points(projection, context), graph, [point]); expect(surface.select('.point')).to.be.classed('tag-amenity'); expect(surface.select('.point')).to.be.classed('tag-amenity-cafe'); From a5d8e1b0cb86cf60ec03ddb5d348776050f01a0f Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 22 Feb 2016 23:31:43 -0500 Subject: [PATCH 03/15] Move gpx layer from background to surface --- js/id/renderer/background.js | 78 ++++--------- js/id/renderer/map.js | 2 +- js/id/svg/gpx.js | 219 ++++++++++++++++++++++------------- js/id/svg/surface.js | 2 +- js/id/ui/map_data.js | 15 ++- js/id/ui/map_in_map.js | 22 ++-- 6 files changed, 180 insertions(+), 158 deletions(-) diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index 305ab66e0..7a420e014 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -1,9 +1,7 @@ iD.Background = function(context) { var dispatch = d3.dispatch('change'), - baseLayer = iD.TileLayer() - .projection(context.projection), - gpxLayer = iD.svg.Gpx(context, dispatch) - .projection(context.projection), + baseLayer = iD.TileLayer().projection(context.projection), + gpxLayer = iD.svg.Gpx(context.projection, context), mapillaryImageLayer, mapillarySignLayer, overlayLayers = []; @@ -78,13 +76,20 @@ 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 gpx = selection.selectAll('.layer-gpx') + // .data([0]); + + // gpx.enter().insert('div') + // .attr('class', 'layer-layer layer-gpx'); + + // gpx.call(gpxLayer); + + selection.selectAll('#surface') + .call(gpxLayer); + + var mapillary = iD.services.mapillary, @@ -133,7 +138,6 @@ iD.Background = function(context) { background.dimensions = function(_) { baseLayer.dimensions(_); - gpxLayer.dimensions(_); if (mapillaryImageLayer) mapillaryImageLayer.dimensions(_); if (mapillarySignLayer) mapillarySignLayer.dimensions(_); @@ -156,51 +160,20 @@ iD.Background = function(context) { background.baseLayerSource(findSource('Bing')); }; + background.gpxLayer = function() { + return gpxLayer; + }; + background.hasGpxLayer = function() { return !_.isEmpty(gpxLayer.geojson()); }; background.showsGpxLayer = function() { - return background.hasGpxLayer() && gpxLayer.enable(); - }; - - function toDom(x) { - return (new DOMParser()).parseFromString(x, 'text/xml'); - } - - background.gpxLayerFiles = function(fileList) { - var f = fileList[0], - reader = new FileReader(); - - reader.onload = function(e) { - gpxLayer.geojson(toGeoJSON.gpx(toDom(e.target.result))); - iD.ui.MapInMap.gpxLayer.geojson(toGeoJSON.gpx(toDom(e.target.result))); - background.zoomToGpxLayer(); - dispatch.change(); - }; - - reader.readAsText(f); - }; - - background.zoomToGpxLayer = function() { - if (background.hasGpxLayer()) { - var map = context.map(), - viewport = map.trimmedExtent().polygon(), - coords = _.reduce(gpxLayer.geojson().features, function(coords, feature) { - var c = feature.geometry.coordinates; - return _.union(coords, feature.geometry.type === 'Point' ? [c] : c); - }, []); - - if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) { - var extent = iD.geo.Extent(d3.geo.bounds(gpxLayer.geojson())); - map.centerZoom(extent.center(), map.trimmedExtentZoom(extent)); - } - } + return background.hasGpxLayer() && gpxLayer.enabled(); }; background.toggleGpxLayer = function() { - gpxLayer.enable(!gpxLayer.enable()); - iD.ui.MapInMap.gpxLayer.enable(!iD.ui.MapInMap.gpxLayer.enable()); + gpxLayer.enabled(!gpxLayer.enabled()); dispatch.change(); }; @@ -317,15 +290,8 @@ iD.Background = function(context) { if (overlay) background.toggleOverlayLayer(overlay); }); - var gpx = q.gpx; - if (gpx) { - d3.text(gpx, function(err, gpxTxt) { - if (!err) { - gpxLayer.geojson(toGeoJSON.gpx(toDom(gpxTxt))); - iD.ui.MapInMap.gpxLayer.geojson(toGeoJSON.gpx(toDom(gpxTxt))); - dispatch.change(); - } - }); + if (q.gpx) { + gpxLayer.url(q.gpx); } }; diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 8e21e7a8b..3c606061f 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -145,7 +145,7 @@ iD.Map = function(context) { function editOff() { context.features().resetStats(); - surface.selectAll('.layer *').remove(); + surface.selectAll('.layer-osm *').remove(); dispatch.drawn({full: true}); } diff --git a/js/id/svg/gpx.js b/js/id/svg/gpx.js index c12326a1e..bc4699fef 100644 --- a/js/id/svg/gpx.js +++ b/js/id/svg/gpx.js @@ -1,101 +1,160 @@ -iD.svg.Gpx = function(context) { - var projection, - gj = {}, - enable = true, - svg; +iD.svg.Gpx = function(projection, context) { + var showLabels = true, + layer; - function render(selection) { - svg = selection.selectAll('svg') - .data([render]); - svg.enter() - .append('svg'); + function init() { + if (iD.svg.Gpx.initialized) return; // run once - svg.style('display', enable ? 'block' : 'none'); + iD.svg.Gpx.geojson = {}; + iD.svg.Gpx.enabled = true; - var paths = svg + function over() { + d3.event.stopPropagation(); + d3.event.preventDefault(); + d3.event.dataTransfer.dropEffect = 'copy'; + } + + d3.select('body') + .attr('dropzone', 'copy') + .on('drop.localgpx', function() { + d3.event.stopPropagation(); + d3.event.preventDefault(); + if (!iD.detect().filedrop) return; + gpx.files(d3.event.dataTransfer.files); + }) + .on('dragenter.localgpx', over) + .on('dragexit.localgpx', over) + .on('dragover.localgpx', over); + + iD.svg.Gpx.initialized = true; + } + + + function gpx(surface) { + var geojson = iD.svg.Gpx.geojson, + enabled = iD.svg.Gpx.enabled; + + layer = surface.selectAll('.layer-gpx') + .data(enabled ? [0] : []); + + layer.enter() + .append('g') + .attr('class', 'layer layer-gpx'); + + layer.exit() + .remove(); + + + var paths = layer .selectAll('path') - .data([gj]); + .data([geojson]); - paths - .enter() + paths.enter() .append('path') .attr('class', 'gpx'); + paths.exit() + .remove(); + var path = d3.geo.path() .projection(projection); paths .attr('d', path); - if (typeof gj.features !== 'undefined') { - svg - .selectAll('text') - .remove(); - svg - .selectAll('path') - .data(gj.features) - .enter() - .append('text') - .attr('class', 'gpx') - .text(function(d) { - return d.properties.desc || d.properties.name; - }) - .attr('x', function(d) { - var centroid = path.centroid(d); - return centroid[0] + 5; - }) - .attr('y', function(d) { - var centroid = path.centroid(d); - return centroid[1]; - }); + var labels = layer.selectAll('text') + .data(showLabels && geojson.features ? geojson.features : []); + + labels.enter() + .append('text') + .attr('class', 'gpx') + .text(function(d) { + return d.properties.desc || d.properties.name; + }); + + labels.exit() + .remove(); + + labels + .attr('x', function(d) { + var centroid = path.centroid(d); + return centroid[0] + 7; + }) + .attr('y', function(d) { + var centroid = path.centroid(d); + return centroid[1]; + }); + + } + + function toDom(x) { + return (new DOMParser()).parseFromString(x, 'text/xml'); + } + + + gpx.showLabels = function(_) { + if (!arguments.length) return showLabels; + showLabels = _; + return gpx; + }; + + gpx.enabled = function(_) { + if (!arguments.length) return iD.svg.Gpx.enabled; + iD.svg.Gpx.enabled = _; + return gpx; + }; + + gpx.geojson = function(gj) { + if (!arguments.length) return iD.svg.Gpx.geojson; + if (_.isEmpty(gj) || _.isEmpty(gj.features)) return gpx; + iD.svg.Gpx.geojson = gj; + return gpx; + }; + + gpx.url = function(url) { + d3.text(url, function(err, data) { + if (!err) { + gpx.geojson(toGeoJSON.gpx(toDom(data))); + // dispatch.change(); + } + }); + return gpx; + }; + + gpx.files = function(fileList) { + var f = fileList[0], + reader = new FileReader(); + + reader.onload = function(e) { + gpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom(); + // dispatch.change(); + }; + + reader.readAsText(f); + return gpx; + }; + + gpx.fitZoom = function() { + var geojson = iD.svg.Gpx.geojson; + if (_.isEmpty(geojson) || _.isEmpty(geojson.features)) return gpx; + + var map = context.map(), + viewport = map.trimmedExtent().polygon(), + coords = _.reduce(geojson.features, function(coords, feature) { + var c = feature.geometry.coordinates; + return _.union(coords, feature.geometry.type === 'Point' ? [c] : c); + }, []); + + if (!iD.geo.polygonIntersectsPolygon(viewport, coords, true)) { + var extent = iD.geo.Extent(d3.geo.bounds(geojson)); + map.centerZoom(extent.center(), map.trimmedExtentZoom(extent)); } - } - render.projection = function(_) { - if (!arguments.length) return projection; - projection = _; - return render; + return gpx; }; - render.enable = function(_) { - if (!arguments.length) return enable; - enable = _; - return render; - }; - - render.geojson = function(_) { - if (!arguments.length) return gj; - gj = _; - return render; - }; - - render.dimensions = function(_) { - if (!arguments.length) return svg.dimensions(); - svg.dimensions(_); - return render; - }; - - render.id = 'layer-gpx'; - - function over() { - d3.event.stopPropagation(); - d3.event.preventDefault(); - d3.event.dataTransfer.dropEffect = 'copy'; - } - - d3.select('body') - .attr('dropzone', 'copy') - .on('drop.localgpx', function() { - d3.event.stopPropagation(); - d3.event.preventDefault(); - if (!iD.detect().filedrop) return; - context.background().gpxLayerFiles(d3.event.dataTransfer.files); - }) - .on('dragenter.localgpx', over) - .on('dragexit.localgpx', over) - .on('dragover.localgpx', over); - - return render; + init(); + return gpx; }; diff --git a/js/id/svg/surface.js b/js/id/svg/surface.js index fbd7ba99d..0aafadd1b 100644 --- a/js/id/svg/surface.js +++ b/js/id/svg/surface.js @@ -9,6 +9,6 @@ iD.svg.Surface = function() { .data(['areas', 'lines', 'hit', 'halo', 'label']); layers.enter().append('g') - .attr('class', function(d) { return 'layer layer-' + d; }); + .attr('class', function(d) { return 'layer layer-osm layer-' + d; }); }; }; diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index 0298910e4..b3dee69b5 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -133,10 +133,9 @@ iD.ui.MapData = function(context) { function drawGpxItem(selection) { - var supportsGpx = iD.detect().filedrop, - gpxLayerItem = selection - .selectAll('.layer-gpx') - .data(supportsGpx ? [0] : []); + var gpxLayerItem = selection + .selectAll('.layer-gpx') + .data([0]); // Enter var enter = gpxLayerItem.enter() @@ -153,7 +152,7 @@ iD.ui.MapData = function(context) { .on('click', function() { d3.event.preventDefault(); d3.event.stopPropagation(); - context.background().zoomToGpxLayer(); + context.background().gpxLayer().fitZoom(); }) .call(iD.svg.Icon('#icon-search')); @@ -166,7 +165,7 @@ iD.ui.MapData = function(context) { d3.select(document.createElement('input')) .attr('type', 'file') .on('change', function() { - context.background().gpxLayerFiles(d3.event.target.files); + context.background().gpxLayer().files(d3.event.target.files); }) .node().click(); }) @@ -185,8 +184,8 @@ iD.ui.MapData = function(context) { .text(t('gpx.local_layer')); // Update - var hasGpx = supportsGpx && context.background().hasGpxLayer(), - showsGpx = supportsGpx && context.background().showsGpxLayer(); + var hasGpx = context.background().hasGpxLayer(), + showsGpx = context.background().showsGpxLayer(); gpxLayerItem .classed('active', showsGpx) diff --git a/js/id/ui/map_in_map.js b/js/id/ui/map_in_map.js index 58a08874b..07a7e4cf6 100644 --- a/js/id/ui/map_in_map.js +++ b/js/id/ui/map_in_map.js @@ -4,18 +4,15 @@ iD.ui.MapInMap = function(context) { function map_in_map(selection) { var backgroundLayer = iD.TileLayer(), overlayLayers = {}, - dispatch = d3.dispatch('change'), - gpxLayer = iD.svg.Gpx(context, dispatch), projection = iD.geo.RawMercator(), + gpxLayer = iD.svg.Gpx(projection, context).showLabels(false), zoom = d3.behavior.zoom() .scaleExtent([ztok(0.5), ztok(24)]) .on('zoom', zoomPan), transformed = false, panning = false, zDiff = 6, // by default, minimap renders at (main zoom - 6) - tStart, tLast, tCurr, kLast, kCurr, tiles, svg, gpx, timeoutId; - - iD.ui.MapInMap.gpxLayer = gpxLayer; + tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId; function ztok(z) { return 256 * Math.pow(2, z); } function ktoz(k) { return Math.log(k) / Math.LN2 - 8; } @@ -155,6 +152,7 @@ iD.ui.MapInMap = function(context) { background .call(backgroundLayer); + // redraw overlay var overlaySources = context.background().overlayLayerSources(); var activeOverlayLayers = []; @@ -188,20 +186,20 @@ iD.ui.MapInMap = function(context) { overlays.exit() .remove(); - // redraw gpx overlay - gpxLayer - .projection(projection); - gpx = tiles + var gpx = tiles .selectAll('.map-in-map-gpx') - .data([0]); + .data(gpxLayer.enabled() ? [0] : []); gpx.enter() - .append('div') + .append('svg') .attr('class', 'map-in-map-gpx'); + gpx.exit() + .remove(); + gpx.call(gpxLayer); - gpx.dimensions(dMini); + // redraw bounding box if (!panning) { From 1f337aa9602ea85252cc28dea1542ff8d3ebfb70 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 23 Feb 2016 16:15:26 -0500 Subject: [PATCH 04/15] Move more data layers from Background into Surface --- index.html | 1 + js/id/renderer/background.js | 66 +++++++++++++++------------------ js/id/renderer/map.js | 2 +- js/id/svg/gpx.js | 7 +++- js/id/svg/mapillary_images.js | 10 ++--- js/id/svg/mapillary_signs.js | 10 ++--- js/id/svg/osm.js | 9 +++++ js/id/svg/surface.js | 56 +++++++++++++++++++++++++--- js/id/ui/preset/restrictions.js | 21 +++++++---- test/index.html | 1 + test/rendering.html | 1 + 11 files changed, 120 insertions(+), 64 deletions(-) create mode 100644 js/id/svg/osm.js diff --git a/index.html b/index.html index fb5c184ff..b5706274f 100644 --- a/index.html +++ b/index.html @@ -69,6 +69,7 @@ + diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index 7a420e014..8bed81f87 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -78,56 +78,48 @@ iD.Background = function(context) { - // var gpx = selection.selectAll('.layer-gpx') - // .data([0]); - - // gpx.enter().insert('div') - // .attr('class', 'layer-layer layer-gpx'); - - // gpx.call(gpxLayer); - - selection.selectAll('#surface') - .call(gpxLayer); + // selection.selectAll('#surface') + // .call(gpxLayer); - var mapillary = iD.services.mapillary, - supportsMapillaryImages = !!mapillary, - supportsMapillarySigns = !!mapillary && mapillary().signsSupported(); + // var mapillary = iD.services.mapillary, + // supportsMapillaryImages = !!mapillary, + // supportsMapillarySigns = !!mapillary && mapillary().signsSupported(); - var mapillaryImages = selection.selectAll('.layer-mapillary-images') - .data(supportsMapillaryImages ? [0] : []); + // var mapillaryImages = selection.selectAll('.layer-mapillary-images') + // .data(supportsMapillaryImages ? [0] : []); - mapillaryImages.enter().insert('div') - .attr('class', 'layer-layer layer-mapillary-images'); + // mapillaryImages.enter().insert('div') + // .attr('class', 'layer-layer layer-mapillary-images'); - if (supportsMapillaryImages) { - if (!mapillaryImageLayer) { mapillaryImageLayer = iD.svg.MapillaryImages(context); } - mapillaryImages.call(mapillaryImageLayer); - } else { - mapillaryImageLayer = null; - } + // if (supportsMapillaryImages) { + // if (!mapillaryImageLayer) { mapillaryImageLayer = iD.svg.MapillaryImages(context); } + // mapillaryImages.call(mapillaryImageLayer); + // } else { + // mapillaryImageLayer = null; + // } - mapillaryImages.exit() - .remove(); + // mapillaryImages.exit() + // .remove(); - var mapillarySigns = selection.selectAll('.layer-mapillary-signs') - .data(supportsMapillarySigns ? [0] : []); + // var mapillarySigns = selection.selectAll('.layer-mapillary-signs') + // .data(supportsMapillarySigns ? [0] : []); - mapillarySigns.enter().insert('div') - .attr('class', 'layer-layer layer-mapillary-signs'); + // mapillarySigns.enter().insert('div') + // .attr('class', 'layer-layer layer-mapillary-signs'); - if (supportsMapillarySigns) { - if (!mapillarySignLayer) { mapillarySignLayer = iD.svg.MapillarySigns(context); } - mapillarySigns.call(mapillarySignLayer); - } else { - mapillarySignLayer = null; - } + // if (supportsMapillarySigns) { + // if (!mapillarySignLayer) { mapillarySignLayer = iD.svg.MapillarySigns(context); } + // mapillarySigns.call(mapillarySignLayer); + // } else { + // mapillarySignLayer = null; + // } - mapillarySigns.exit() - .remove(); + // mapillarySigns.exit() + // .remove(); } background.sources = function(extent) { diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 3c606061f..bb3ca1d50 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -56,7 +56,7 @@ iD.Map = function(context) { if (resetTransform()) redraw(); }) .attr('id', 'surface') - .call(iD.svg.Surface(context)); + .call(iD.svg.Surface(projection, context)); supersurface.call(context.background()); diff --git a/js/id/svg/gpx.js b/js/id/svg/gpx.js index bc4699fef..8bf4ab9cd 100644 --- a/js/id/svg/gpx.js +++ b/js/id/svg/gpx.js @@ -93,6 +93,9 @@ iD.svg.Gpx = function(projection, context) { return (new DOMParser()).parseFromString(x, 'text/xml'); } + function redraw() { + context.pan([0,0]); + } gpx.showLabels = function(_) { if (!arguments.length) return showLabels; @@ -117,7 +120,7 @@ iD.svg.Gpx = function(projection, context) { d3.text(url, function(err, data) { if (!err) { gpx.geojson(toGeoJSON.gpx(toDom(data))); - // dispatch.change(); + redraw(); } }); return gpx; @@ -129,7 +132,7 @@ iD.svg.Gpx = function(projection, context) { reader.onload = function(e) { gpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom(); - // dispatch.change(); + redraw(); }; reader.readAsText(f); diff --git a/js/id/svg/mapillary_images.js b/js/id/svg/mapillary_images.js index 6f061cd8b..075828cd8 100644 --- a/js/id/svg/mapillary_images.js +++ b/js/id/svg/mapillary_images.js @@ -1,4 +1,4 @@ -iD.svg.MapillaryImages = function(context) { +iD.svg.MapillaryImages = function(projection, context) { var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000), enabled = false, minZoom = 12, @@ -19,7 +19,7 @@ iD.svg.MapillaryImages = function(context) { if (!mapillary) return; var thumb = mapillary.selectedThumbnail(), - posX = context.projection(image.loc)[0], + posX = projection(image.loc)[0], width = layer.dimensions()[0], position = (posX < width / 2) ? 'right' : 'left'; @@ -71,14 +71,14 @@ iD.svg.MapillaryImages = function(context) { } function transform(d) { - var t = iD.svg.PointTransform(context.projection)(d); + var t = iD.svg.PointTransform(projection)(d); if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; return t; } function drawMarkers() { var mapillary = getMapillary(), - data = (mapillary ? mapillary.images(context.projection, layer.dimensions()) : []); + data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []); var markers = layer.selectAll('.viewfield-group') .data(data, function(d) { return d.key; }); @@ -153,7 +153,7 @@ iD.svg.MapillaryImages = function(context) { if (mapillary && ~~context.map().zoom() >= minZoom) { editOn(); drawMarkers(); - mapillary.loadImages(context.projection, layer.dimensions()); + mapillary.loadImages(projection, layer.dimensions()); } else { editOff(); } diff --git a/js/id/svg/mapillary_signs.js b/js/id/svg/mapillary_signs.js index 56375bd42..98a25115b 100644 --- a/js/id/svg/mapillary_signs.js +++ b/js/id/svg/mapillary_signs.js @@ -1,4 +1,4 @@ -iD.svg.MapillarySigns = function(context) { +iD.svg.MapillarySigns = function(projection, context) { var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000), enabled = false, minZoom = 12, @@ -19,7 +19,7 @@ iD.svg.MapillarySigns = function(context) { if (!mapillary) return; var thumb = mapillary.selectedThumbnail(), - posX = context.projection(image.loc)[0], + posX = projection(image.loc)[0], width = layer.dimensions()[0], position = (posX < width / 2) ? 'right' : 'left'; @@ -63,7 +63,7 @@ iD.svg.MapillarySigns = function(context) { function drawSigns() { var mapillary = getMapillary(), - data = (mapillary ? mapillary.signs(context.projection, layer.dimensions()) : []); + data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []); var signs = layer.select('.mapillary-sign-offset') .selectAll('.icon-sign') @@ -111,7 +111,7 @@ iD.svg.MapillarySigns = function(context) { // Update signs - .attr('transform', iD.svg.PointTransform(context.projection)); + .attr('transform', iD.svg.PointTransform(projection)); } function render(selection) { @@ -135,7 +135,7 @@ iD.svg.MapillarySigns = function(context) { if (mapillary && ~~context.map().zoom() >= minZoom) { editOn(); drawSigns(); - mapillary.loadSigns(context, context.projection, layer.dimensions()); + mapillary.loadSigns(context, projection, layer.dimensions()); } else { editOff(); } diff --git a/js/id/svg/osm.js b/js/id/svg/osm.js new file mode 100644 index 000000000..31ba891ac --- /dev/null +++ b/js/id/svg/osm.js @@ -0,0 +1,9 @@ +iD.svg.Osm = function() { + return function (selection) { + var layers = selection.selectAll('.layer-osm') + .data(['areas', 'lines', 'hit', 'halo', 'label']); + + layers.enter().append('g') + .attr('class', function(d) { return 'layer layer-osm layer-' + d; }); + }; +}; diff --git a/js/id/svg/surface.js b/js/id/svg/surface.js index 0aafadd1b..06df27eac 100644 --- a/js/id/svg/surface.js +++ b/js/id/svg/surface.js @@ -1,14 +1,58 @@ -iD.svg.Surface = function() { - return function (selection) { +iD.svg.Surface = function (projection, context) { + var all = [ + { order: 1, id: 'osm', render: iD.svg.Osm(projection, context) }, + { order: 2, id: 'gpx', render: iD.svg.Gpx(projection, context) }, + { order: 3, id: 'mapillary-images', render: iD.svg.MapillaryImages(projection, context) }, + { order: 4, id: 'mapillary-signs', render: iD.svg.MapillarySigns(projection, context) } + ]; + + + function surface (selection) { selection.selectAll('defs') .data([0]) .enter() .append('defs'); - var layers = selection.selectAll('.layer') - .data(['areas', 'lines', 'hit', 'halo', 'label']); + var groups = selection.selectAll('.data-layer') + .data(all); - layers.enter().append('g') - .attr('class', function(d) { return 'layer layer-osm layer-' + d; }); + groups.enter() + .append('g') + .attr('class', function(d) { return 'layer data-layer data-layer-' + d.id; }); + + groups + .sort(function(a, b) { return a.order - b.order; }) + .each(function(d) { d3.select(this).call(d.render); }); + + groups.exit() + .remove(); + } + + + surface.only = function (what) { + var arr = [].concat(what); + surface.remove(_.difference(_.pluck(all, 'id'), arr)); + return surface; }; + + surface.remove = function (what) { + var arr = [].concat(what); + _.each(arr, function(id) { + all = _.reject(all, function(d) { return d.id === id; }); + }); + return surface; + }; + + surface.add = function (what) { + var arr = [].concat(what); + _.each(arr, function(obj) { + if ('order' in obj && 'id' in obj && 'render' in obj) { + all.push(obj); + } + }); + return surface; + }; + + + return surface; }; diff --git a/js/id/ui/preset/restrictions.js b/js/id/ui/preset/restrictions.js index aa0b91ee6..6a57ecbf9 100644 --- a/js/id/ui/preset/restrictions.js +++ b/js/id/ui/preset/restrictions.js @@ -7,21 +7,19 @@ iD.ui.preset.restrictions = function(field, context) { var wrap = selection.selectAll('.preset-input-wrap') .data([0]); - var enter = wrap.enter().append('div') + var enter = wrap.enter() + .append('div') .attr('class', 'preset-input-wrap'); - enter.append('div') + enter + .append('div') .attr('class', 'restriction-help'); - enter.append('svg') - .call(iD.svg.Surface(context)) - .call(iD.behavior.Hover(context)); var intersection = iD.geo.Intersection(context.graph(), vertexID), graph = intersection.graph, vertex = graph.entity(vertexID), - surface = wrap.selectAll('svg'), - filter = function () { return true; }, + filter = d3.functor(true), extent = iD.geo.Extent(), projection = iD.geo.RawMercator(), lines = iD.svg.Lines(projection, context), @@ -30,7 +28,7 @@ iD.ui.preset.restrictions = function(field, context) { var d = wrap.dimensions(), c = [d[0] / 2, d[1] / 2], - z = 21; + z = 24; projection .scale(256 * Math.pow(2, z) / (2 * Math.PI)); @@ -41,6 +39,13 @@ iD.ui.preset.restrictions = function(field, context) { .translate([c[0] - s[0], c[1] - s[1]]) .clipExtent([[0, 0], d]); + enter + .append('svg') + .call(iD.svg.Surface(projection, context).only('osm')) + .call(iD.behavior.Hover(context)); + + var surface = wrap.selectAll('svg'); + surface .call(vertices, graph, [vertex], filter, extent, z) .call(lines, graph, intersection.ways, filter) diff --git a/test/index.html b/test/index.html index febd5a40a..abd89e8ea 100644 --- a/test/index.html +++ b/test/index.html @@ -74,6 +74,7 @@ + diff --git a/test/rendering.html b/test/rendering.html index c6a7f9437..dfe463a48 100644 --- a/test/rendering.html +++ b/test/rendering.html @@ -21,6 +21,7 @@ + From 9233167f522daeb680cbb5cc1846cb93be182fdb Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 23 Feb 2016 23:58:40 -0500 Subject: [PATCH 05/15] Allow d3.dimensions to accept null to reset to current bounding rect --- js/lib/d3.dimensions.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/js/lib/d3.dimensions.js b/js/lib/d3.dimensions.js index eef257401..7bba5516b 100644 --- a/js/lib/d3.dimensions.js +++ b/js/lib/d3.dimensions.js @@ -1,17 +1,23 @@ d3.selection.prototype.dimensions = function (dimensions) { - if (!arguments.length) { - var node = this.node(); - if (!node) return; - - var prop = this.property('__dimensions__'); - if (!prop) { - var cr = node.getBoundingClientRect(); - prop = [cr.width, cr.height]; - this.property('__dimensions__', prop); - } + var refresh = (function(node) { + var cr = node.getBoundingClientRect(); + prop = [cr.width, cr.height]; + this.property('__dimensions__', prop); return prop; + }).bind(this); + + var node = this.node(); + + if (!arguments.length) { + if (!node) return [0,0]; + return this.property('__dimensions__') || refresh(node); + } + if (dimensions === null) { + if (!node) return [0,0]; + return refresh(node); } - this.property('__dimensions__', [dimensions[0], dimensions[1]]); - return this.attr({width: dimensions[0], height: dimensions[1]}); + return this + .property('__dimensions__', [dimensions[0], dimensions[1]]) + .attr({width: dimensions[0], height: dimensions[1]}); }; From ef5a47dbb2add9e0cf7de9c33c0f0da7d75db04d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Feb 2016 00:02:59 -0500 Subject: [PATCH 06/15] Reset dimensions when resizing --- js/id/ui.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/id/ui.js b/js/id/ui.js index ddcd31649..403e92aee 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -28,7 +28,7 @@ iD.ui = function(context) { .attr('id', 'bar') .attr('class', 'fillD'); - var m = content.append('div') + content.append('div') .attr('id', 'map') .call(map); @@ -169,8 +169,8 @@ iD.ui = function(context) { var mapDimensions = map.dimensions(); d3.select(window).on('resize.editor', function() { - mapDimensions = m.dimensions(); - map.dimensions(m.dimensions()); + mapDimensions = content.dimensions(null); + map.dimensions(mapDimensions); }); function pan(d) { From caf5ff815c2c6293c094e5c0164cfccb867c22ac Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Feb 2016 00:07:04 -0500 Subject: [PATCH 07/15] Move svg into iD.svg.Surface, adjust layer names --- css/app.css | 2 +- css/map.css | 8 ++-- js/id/renderer/background.js | 12 +++--- js/id/renderer/map.js | 65 +++++++++++++++++---------------- js/id/svg/gpx.js | 2 +- js/id/svg/mapillary_images.js | 7 ++-- js/id/svg/mapillary_signs.js | 11 ++---- js/id/svg/osm.js | 2 +- js/id/svg/surface.js | 62 +++++++++++++++++++------------ js/id/ui/background.js | 2 +- js/id/ui/preset/restrictions.js | 14 +++++-- 11 files changed, 104 insertions(+), 83 deletions(-) diff --git a/css/app.css b/css/app.css index 243b1c43c..79b45a37a 100644 --- a/css/app.css +++ b/css/app.css @@ -2132,7 +2132,7 @@ div.full-screen > button:hover { user-select: none; } -#supersurface, .layer-layer { +#supersurface, .layer { position: absolute; top: 0; left: 0; diff --git a/css/map.css b/css/map.css index 88fcb6003..aadefb962 100644 --- a/css/map.css +++ b/css/map.css @@ -24,11 +24,11 @@ img.tile-removing { use { pointer-events: none; } /* base styles */ -.layer path:not(.oneway) { fill: none; } /* IE needs :not(.oneway) */ +.layer-osm path:not(.oneway) { fill: none; } /* IE needs :not(.oneway) */ /* the above fill: none rule affects paths in shadow dom only in Firefox */ -.layer use.icon path { fill: #333; } /* FF svg Maki icons */ -.layer .turn use path { fill: #000; } /* FF turn restriction icons */ +.layer-osm use.icon path { fill: #333; } /* FF svg Maki icons */ +.layer-osm .turn use path { fill: #000; } /* FF turn restriction icons */ #turn-only-shape2, #turn-only-u-shape2 { fill: #7092FF; } /* FF turn-only, turn-only-u */ #turn-no-shape2, #turn-no-u-shape2 { fill: #E06D5F; } /* FF turn-no, turn-no-u */ #turn-yes-shape2, #turn-yes-u-shape2 { fill: #8CD05F; } /* FF turn-yes, turn-yes-u */ @@ -1500,7 +1500,7 @@ g.turn circle { } /* GPX Paths */ -div.layer-gpx { +.layer-gpx { pointer-events: none; } diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index 8bed81f87..d04f471f9 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -55,19 +55,21 @@ iD.Background = function(context) { } function background(selection) { - var base = selection.selectAll('.background-layer') + var base = selection.selectAll('.layer-background') .data([0]); - base.enter().insert('div', '.layer-data') - .attr('class', 'layer-layer background-layer'); + base.enter() + .insert('div', '.layer-data') + .attr('class', 'layer layer-background'); base.call(baseLayer); var overlays = selection.selectAll('.layer-overlay') .data(overlayLayers, function(d) { return d.source().name(); }); - overlays.enter().insert('div', '.layer-data') - .attr('class', 'layer-layer layer-overlay'); + overlays.enter() + .insert('div', '.layer-data') + .attr('class', 'layer layer-overlay'); overlays.each(function(layer) { d3.select(this).call(layer); diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index bb3ca1d50..39492ceaa 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -12,13 +12,15 @@ iD.Map = function(context) { transformStart, transformed = false, minzoom = 0, + drawSurface = iD.svg.Surface(projection, context), points = iD.svg.Points(projection, context), vertices = iD.svg.Vertices(projection, context), lines = iD.svg.Lines(projection), areas = iD.svg.Areas(projection), midpoints = iD.svg.Midpoints(projection, context), labels = iD.svg.Labels(projection, context), - supersurface, surface, + surface, + supersurface, mouse, mousemove; @@ -35,18 +37,21 @@ iD.Map = function(context) { .call(zoom); supersurface = selection.append('div') - .attr('id', 'supersurface'); - - // Mapillary streetsigns require supersurface transform to have - // a value in order to do correct foreignObject positioning in Chrome - iD.util.setTransform(supersurface, 0, 0); + .attr('id', 'supersurface') + .call(iD.util.setTransform, 0, 0); // Need a wrapper div because Opera can't cope with an absolutely positioned // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16 - var dataLayer = supersurface.append('div') - .attr('class', 'layer-layer layer-data'); + var dataLayer = supersurface + .append('div') + .attr('class', 'layer layer-data'); - map.surface = surface = dataLayer.append('svg') + map.surface = surface = dataLayer + .call(drawSurface) + .select('.surface') + .attr('id', 'surface'); + + surface .on('mousedown.zoom', function() { if (d3.event.button === 2) { d3.event.stopPropagation(); @@ -55,30 +60,28 @@ iD.Map = function(context) { .on('mouseup.zoom', function() { if (resetTransform()) redraw(); }) - .attr('id', 'surface') - .call(iD.svg.Surface(projection, context)); + .on('mousemove.map', function() { + mousemove = d3.event; + }) + .on('mouseover.vertices', function() { + if (map.editable() && !transformed) { + var hover = d3.event.target.__data__; + surface.call(vertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); + dispatch.drawn({full: false}); + } + }) + .on('mouseout.vertices', function() { + if (map.editable() && !transformed) { + var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__; + surface.call(vertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); + dispatch.drawn({full: false}); + } + }); - supersurface.call(context.background()); - surface.on('mousemove.map', function() { - mousemove = d3.event; - }); + supersurface + .call(context.background()); - surface.on('mouseover.vertices', function() { - if (map.editable() && !transformed) { - var hover = d3.event.target.__data__; - surface.call(vertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); - dispatch.drawn({full: false}); - } - }); - - surface.on('mouseout.vertices', function() { - if (map.editable() && !transformed) { - var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__; - surface.call(vertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); - dispatch.drawn({full: false}); - } - }); context.on('enter.map', function() { if (map.editable() && !transformed) { @@ -328,7 +331,7 @@ iD.Map = function(context) { if (!arguments.length) return dimensions; var center = map.center(); dimensions = _; - surface.dimensions(dimensions); + drawSurface.dimensions(dimensions); context.background().dimensions(dimensions); projection.clipExtent([[0, 0], dimensions]); mouse = iD.util.fastMouse(supersurface.node()); diff --git a/js/id/svg/gpx.js b/js/id/svg/gpx.js index 8bf4ab9cd..7073b8a26 100644 --- a/js/id/svg/gpx.js +++ b/js/id/svg/gpx.js @@ -40,7 +40,7 @@ iD.svg.Gpx = function(projection, context) { layer.enter() .append('g') - .attr('class', 'layer layer-gpx'); + .attr('class', 'layer-gpx'); layer.exit() .remove(); diff --git a/js/id/svg/mapillary_images.js b/js/id/svg/mapillary_images.js index 075828cd8..3ba2889e9 100644 --- a/js/id/svg/mapillary_images.js +++ b/js/id/svg/mapillary_images.js @@ -110,13 +110,13 @@ iD.svg.MapillaryImages = function(projection, context) { function render(selection) { var mapillary = getMapillary(); - layer = selection.selectAll('svg') + layer = selection.selectAll('.layer-mapillary-images') .data(mapillary ? [0] : []); layer.enter() - .append('svg') + .append('g') + .attr('class', 'layer-mapillary-images') .style('display', enabled ? 'block' : 'none') - .dimensions(context.map().dimensions()) .on('click', function() { // deselect/select var mapillary = getMapillary(); if (!mapillary) return; @@ -172,7 +172,6 @@ iD.svg.MapillaryImages = function(projection, context) { }; render.dimensions = function(_) { - if (layer.empty()) return null; if (!arguments.length) return layer.dimensions(); layer.dimensions(_); return render; diff --git a/js/id/svg/mapillary_signs.js b/js/id/svg/mapillary_signs.js index 98a25115b..d0648256c 100644 --- a/js/id/svg/mapillary_signs.js +++ b/js/id/svg/mapillary_signs.js @@ -65,7 +65,7 @@ iD.svg.MapillarySigns = function(projection, context) { var mapillary = getMapillary(), data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []); - var signs = layer.select('.mapillary-sign-offset') + var signs = layer.select('.layer-mapillary-signs') .selectAll('.icon-sign') .data(data, function(d) { return d.key; }); @@ -117,15 +117,13 @@ iD.svg.MapillarySigns = function(projection, context) { function render(selection) { var mapillary = getMapillary(); - layer = selection.selectAll('svg') + layer = selection.selectAll('.layer-mapillary-signs') .data(mapillary ? [0] : []); layer.enter() - .append('svg') - .style('display', enabled ? 'block' : 'none') - .dimensions(context.map().dimensions()) .append('g') - .attr('class', 'mapillary-sign-offset') + .attr('class', 'layer-mapillary-signs') + .style('display', enabled ? 'block' : 'none') .attr('transform', 'translate(-16, -16)'); // center signs on loc layer.exit() @@ -154,7 +152,6 @@ iD.svg.MapillarySigns = function(projection, context) { }; render.dimensions = function(_) { - if (layer.empty()) return null; if (!arguments.length) return layer.dimensions(); layer.dimensions(_); return render; diff --git a/js/id/svg/osm.js b/js/id/svg/osm.js index 31ba891ac..72f93bbb8 100644 --- a/js/id/svg/osm.js +++ b/js/id/svg/osm.js @@ -4,6 +4,6 @@ iD.svg.Osm = function() { .data(['areas', 'lines', 'hit', 'halo', 'label']); layers.enter().append('g') - .attr('class', function(d) { return 'layer layer-osm layer-' + d; }); + .attr('class', function(d) { return 'layer-osm layer-' + d; }); }; }; diff --git a/js/id/svg/surface.js b/js/id/svg/surface.js index 06df27eac..2f017622d 100644 --- a/js/id/svg/surface.js +++ b/js/id/svg/surface.js @@ -1,27 +1,30 @@ -iD.svg.Surface = function (projection, context) { - var all = [ - { order: 1, id: 'osm', render: iD.svg.Osm(projection, context) }, - { order: 2, id: 'gpx', render: iD.svg.Gpx(projection, context) }, - { order: 3, id: 'mapillary-images', render: iD.svg.MapillaryImages(projection, context) }, - { order: 4, id: 'mapillary-signs', render: iD.svg.MapillarySigns(projection, context) } - ]; +iD.svg.Surface = function(projection, context) { + var svg = d3.select(null), + layers = [ + { id: 'osm', render: iD.svg.Osm(projection, context) }, + { id: 'gpx', render: iD.svg.Gpx(projection, context) }, + { id: 'mapillary-images', render: iD.svg.MapillaryImages(projection, context) }, + { id: 'mapillary-signs', render: iD.svg.MapillarySigns(projection, context) } + ]; - function surface (selection) { - selection.selectAll('defs') - .data([0]) - .enter() + function surface(selection) { + svg = selection.selectAll('.surface') + .data([0]); + + svg.enter() + .append('svg') + .attr('class', 'surface') .append('defs'); - var groups = selection.selectAll('.data-layer') - .data(all); + var groups = svg.selectAll('.data-layer') + .data(layers); groups.enter() .append('g') - .attr('class', function(d) { return 'layer data-layer data-layer-' + d.id; }); + .attr('class', function(d) { return 'data-layer data-layer-' + d.id; }); groups - .sort(function(a, b) { return a.order - b.order; }) .each(function(d) { d3.select(this).call(d.render); }); groups.exit() @@ -29,25 +32,36 @@ iD.svg.Surface = function (projection, context) { } - surface.only = function (what) { + surface.only = function(what) { var arr = [].concat(what); - surface.remove(_.difference(_.pluck(all, 'id'), arr)); + surface.remove(_.difference(_.pluck(layers, 'id'), arr)); return surface; }; - surface.remove = function (what) { + surface.remove = function(what) { var arr = [].concat(what); - _.each(arr, function(id) { - all = _.reject(all, function(d) { return d.id === id; }); + arr.forEach(function(id) { + layers = _.reject(layers, function(d) { return d.id === id; }); }); return surface; }; - surface.add = function (what) { + surface.add = function(what) { var arr = [].concat(what); - _.each(arr, function(obj) { - if ('order' in obj && 'id' in obj && 'render' in obj) { - all.push(obj); + arr.forEach(function(obj) { + if ('id' in obj && 'render' in obj) { + layers.push(obj); + } + }); + return surface; + }; + + surface.dimensions = function(_) { + if (!arguments.length) return svg.dimensions(); + svg.dimensions(_); + layers.forEach(function(layer) { + if (layer.render.dimensions) { + layer.render.dimensions(_); } }); return surface; diff --git a/js/id/ui/background.js b/js/id/ui/background.js index adb0a893c..3a2abf730 100644 --- a/js/id/ui/background.js +++ b/js/id/ui/background.js @@ -24,7 +24,7 @@ iD.ui.Background = function(context) { } function setOpacity(d) { - var bg = context.container().selectAll('.background-layer') + var bg = context.container().selectAll('.layer-background') .transition() .style('opacity', d) .attr('data-opacity', d); diff --git a/js/id/ui/preset/restrictions.js b/js/id/ui/preset/restrictions.js index 6a57ecbf9..9c5081010 100644 --- a/js/id/ui/preset/restrictions.js +++ b/js/id/ui/preset/restrictions.js @@ -39,14 +39,17 @@ iD.ui.preset.restrictions = function(field, context) { .translate([c[0] - s[0], c[1] - s[1]]) .clipExtent([[0, 0], d]); + enter - .append('svg') - .call(iD.svg.Surface(projection, context).only('osm')) + .call(iD.svg.Surface(projection, context).only('osm').dimensions(d)) + .select('.surface') .call(iD.behavior.Hover(context)); - var surface = wrap.selectAll('svg'); + + var surface = wrap.select('.surface'); surface + .dimensions(d) .call(vertices, graph, [vertex], filter, extent, z) .call(lines, graph, intersection.ways, filter) .call(turns, graph, intersection.turns(fromNodeID)); @@ -72,7 +75,10 @@ iD.ui.preset.restrictions = function(field, context) { .on('change.restrictions', render); d3.select(window) - .on('resize.restrictions', render); + .on('resize.restrictions', function() { + wrap.dimensions(null); + render(); + }); function click() { var datum = d3.event.target.__data__; From d710ed9c95fcb91f18ec488c7e000c9591af33d6 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Feb 2016 00:54:01 -0500 Subject: [PATCH 08/15] use selectAll to preserve __data__ --- test/spec/behavior/select.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/spec/behavior/select.js b/test/spec/behavior/select.js index b393dd555..9e7df8d0f 100644 --- a/test/spec/behavior/select.js +++ b/test/spec/behavior/select.js @@ -15,7 +15,7 @@ describe("iD.behavior.Select", function() { .append('div') .attr('class', 'inspector-wrap'); - context.surface().selectAll('circle') + context.surface().select('.data-layer-osm').selectAll('circle') .data([a, b]) .enter().append('circle') .attr('class', function(d) { return d.id; }); @@ -33,7 +33,7 @@ describe("iD.behavior.Select", function() { }); specify("click on entity selects the entity", function() { - happen.click(context.surface().select('.' + a.id).node()); + happen.click(context.surface().selectAll('.' + a.id).node()); expect(context.selectedIDs()).to.eql([a.id]); }); @@ -45,19 +45,19 @@ describe("iD.behavior.Select", function() { specify("shift-click on unselected entity adds it to the selection", function() { context.enter(iD.modes.Select(context, [a.id])); - happen.click(context.surface().select('.' + b.id).node(), {shiftKey: true}); + happen.click(context.surface().selectAll('.' + b.id).node(), {shiftKey: true}); expect(context.selectedIDs()).to.eql([a.id, b.id]); }); specify("shift-click on selected entity removes it from the selection", function() { context.enter(iD.modes.Select(context, [a.id, b.id])); - happen.click(context.surface().select('.' + b.id).node(), {shiftKey: true}); + happen.click(context.surface().selectAll('.' + b.id).node(), {shiftKey: true}); expect(context.selectedIDs()).to.eql([a.id]); }); specify("shift-click on last selected entity clears the selection", function() { context.enter(iD.modes.Select(context, [a.id])); - happen.click(context.surface().select('.' + a.id).node(), {shiftKey: true}); + happen.click(context.surface().selectAll('.' + a.id).node(), {shiftKey: true}); expect(context.mode().id).to.eql('browse'); }); From c595fdbf57fd72cc270564c643dc27f27a3e2e71 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Feb 2016 12:04:44 -0500 Subject: [PATCH 09/15] Avoid calling getComputedStyle and triggering reflows --- css/app.css | 2 +- js/id/ui.js | 6 +--- js/id/ui/contributors.js | 43 ++++++++++++++++------------ js/id/ui/info.js | 56 ++++++++++++++++++++---------------- js/id/ui/map_in_map.js | 61 +++++++++++++++++++--------------------- 5 files changed, 87 insertions(+), 81 deletions(-) diff --git a/css/app.css b/css/app.css index 79b45a37a..52122e1d2 100644 --- a/css/app.css +++ b/css/app.css @@ -2208,7 +2208,7 @@ div.full-screen > button:hover { border-bottom: 1px solid black; } -.infobox .selection-heading { +.infobox .infobox-heading { display: block; border-radius: 4px 0 0 0; padding: 5px 10px; diff --git a/js/id/ui.js b/js/id/ui.js index 403e92aee..3c7a51b90 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -32,14 +32,10 @@ iD.ui = function(context) { .attr('id', 'map') .call(map); - content.append('div') - .attr('class', 'map-in-map') - .style('display', 'none') + content .call(iD.ui.MapInMap(context)); content.append('div') - .attr('class', 'infobox fillD2') - .style('display', 'none') .call(iD.ui.Info(context)); bar.append('div') diff --git a/js/id/ui/contributors.js b/js/id/ui/contributors.js index 86a38c067..8bf709e71 100644 --- a/js/id/ui/contributors.js +++ b/js/id/ui/contributors.js @@ -1,7 +1,11 @@ iD.ui.Contributors = function(context) { - function update(selection) { + var debouncedUpdate = _.debounce(function() { update(); }, 1000), + limit = 4, + hidden = false, + wrap = d3.select(null); + + function update() { var users = {}, - limit = 4, entities = context.intersects(context.map().extent()); entities.forEach(function(entity) { @@ -11,7 +15,7 @@ iD.ui.Contributors = function(context) { var u = Object.keys(users), subset = u.slice(0, u.length > limit ? limit - 1 : limit); - selection.html('') + wrap.html('') .call(iD.svg.Icon('#icon-nearby', 'pre-text light')); var userList = d3.select(document.createElement('span')); @@ -37,29 +41,32 @@ iD.ui.Contributors = function(context) { }) .text(u.length - limit + 1); - selection.append('span') - .html(t('contributors.truncated_list', {users: userList.html(), count: count.html()})); + wrap.append('span') + .html(t('contributors.truncated_list', { users: userList.html(), count: count.html() })); + } else { - selection.append('span') - .html(t('contributors.list', {users: userList.html()})); + wrap.append('span') + .html(t('contributors.list', { users: userList.html() })); } if (!u.length) { - selection.transition().style('opacity', 0); - } else if (selection.style('opacity') === '0') { - selection.transition().style('opacity', 1); + hidden = true; + wrap + .transition() + .style('opacity', 0); + + } else if (hidden) { + wrap + .transition() + .style('opacity', 1); } } return function(selection) { - update(selection); + wrap = selection; + update(); - context.connection().on('loaded.contributors', function() { - update(selection); - }); - - context.map().on('move.contributors', _.debounce(function() { - update(selection); - }, 500)); + context.connection().on('loaded.contributors', debouncedUpdate); + context.map().on('move.contributors', debouncedUpdate); }; }; diff --git a/js/id/ui/info.js b/js/id/ui/info.js index fee64d1ba..948d08f19 100644 --- a/js/id/ui/info.js +++ b/js/id/ui/info.js @@ -1,6 +1,7 @@ iD.ui.Info = function(context) { var key = iD.ui.cmd('⌘I'), - imperial = (iD.detect().locale.toLowerCase() === 'en-us'); + imperial = (iD.detect().locale.toLowerCase() === 'en-us'), + hidden = true; function info(selection) { function radiansToMeters(r) { @@ -95,7 +96,7 @@ iD.ui.Info = function(context) { function redraw() { - if (hidden()) return; + if (hidden) return; var resolver = context.graph(), selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), @@ -103,9 +104,9 @@ iD.ui.Info = function(context) { extent = iD.geo.Extent(), entity; - selection.html(''); - selection.append('h4') - .attr('class', 'selection-heading fillD') + wrap.html(''); + wrap.append('h4') + .attr('class', 'infobox-heading fillD') .text(singular || t('infobox.selected', { n: selected.length })); if (!selected.length) return; @@ -118,16 +119,16 @@ iD.ui.Info = function(context) { center = extent.center(); - var list = selection.append('ul'); + var list = wrap.append('ul'); - // multiple selection, just display extent center.. + // multiple wrap, just display extent center.. if (!singular) { list.append('li') .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); return; } - // single selection, display details.. + // single wrap, display details.. if (!entity) return; var geometry = entity.geometry(resolver); @@ -156,7 +157,7 @@ iD.ui.Info = function(context) { var toggle = imperial ? 'imperial' : 'metric'; - selection.append('a') + wrap.append('a') .text(t('infobox.' + toggle)) .attr('href', '#') .attr('class', 'button') @@ -178,26 +179,13 @@ iD.ui.Info = function(context) { } - function hidden() { - return selection.style('display') === 'none'; - } - - function toggle() { if (d3.event) d3.event.preventDefault(); - if (hidden()) { - selection - .style('display', 'block') - .style('opacity', 0) - .transition() - .duration(200) - .style('opacity', 1); + hidden = !hidden; - redraw(); - - } else { - selection + if (hidden) { + wrap .style('display', 'block') .style('opacity', 1) .transition() @@ -206,9 +194,27 @@ iD.ui.Info = function(context) { .each('end', function() { d3.select(this).style('display', 'none'); }); + } else { + wrap + .style('display', 'block') + .style('opacity', 0) + .transition() + .duration(200) + .style('opacity', 1); + + redraw(); } } + + var wrap = selection.selectAll('.infobox') + .data([0]); + + wrap.enter() + .append('div') + .attr('class', 'infobox fillD2') + .style('display', (hidden ? 'none' : 'block')); + context.map() .on('drawn.info', redraw); diff --git a/js/id/ui/map_in_map.js b/js/id/ui/map_in_map.js index 07a7e4cf6..f0a5ead02 100644 --- a/js/id/ui/map_in_map.js +++ b/js/id/ui/map_in_map.js @@ -11,6 +11,7 @@ iD.ui.MapInMap = function(context) { .on('zoom', zoomPan), transformed = false, panning = false, + hidden = true, zDiff = 6, // by default, minimap renders at (main zoom - 6) tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId; @@ -68,7 +69,7 @@ iD.ui.MapInMap = function(context) { panning = false; if (tCurr[0] !== tStart[0] && tCurr[1] !== tStart[1]) { - var dMini = selection.dimensions(), + var dMini = wrap.dimensions(), cMini = [ dMini[0] / 2, dMini[1] / 2 ]; context.map().center(projection.invert(cMini)); @@ -78,7 +79,7 @@ iD.ui.MapInMap = function(context) { function updateProjection() { var loc = context.map().center(), - dMini = selection.dimensions(), + dMini = wrap.dimensions(), cMini = [ dMini[0] / 2, dMini[1] / 2 ], tMain = context.projection.translate(), kMain = context.projection.scale(), @@ -118,15 +119,15 @@ iD.ui.MapInMap = function(context) { function redraw() { - if (hidden()) return; + if (hidden) return; updateProjection(); - var dMini = selection.dimensions(), + var dMini = wrap.dimensions(), zMini = ktoz(projection.scale() * 2 * Math.PI); // setup tile container - tiles = selection + tiles = wrap .selectAll('.map-in-map-tiles') .data([0]); @@ -206,7 +207,7 @@ iD.ui.MapInMap = function(context) { var getPath = d3.geo.path().projection(projection), bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] }; - svg = selection.selectAll('.map-in-map-svg') + svg = wrap.selectAll('.map-in-map-svg') .data([0]); svg.enter() @@ -233,31 +234,17 @@ iD.ui.MapInMap = function(context) { } - function hidden() { - return selection.style('display') === 'none'; - } - - function toggle() { if (d3.event) d3.event.preventDefault(); + hidden = !hidden; + var label = d3.select('.minimap-toggle'); + label.classed('active', !hidden) + .select('input').property('checked', !hidden); - if (hidden()) { - selection - .style('display', 'block') - .style('opacity', 0) - .transition() - .duration(200) - .style('opacity', 1); - - label.classed('active', true) - .select('input').property('checked', true); - - redraw(); - - } else { - selection + if (hidden) { + wrap .style('display', 'block') .style('opacity', 1) .transition() @@ -266,19 +253,29 @@ iD.ui.MapInMap = function(context) { .each('end', function() { d3.select(this).style('display', 'none'); }); + } else { + wrap + .style('display', 'block') + .style('opacity', 0) + .transition() + .duration(200) + .style('opacity', 1); - label.classed('active', false) - .select('input').property('checked', false); + redraw(); } } iD.ui.MapInMap.toggle = toggle; - selection - .on('mousedown.map-in-map', startMouse) - .on('mouseup.map-in-map', endMouse); + var wrap = selection.selectAll('.map-in-map') + .data([0]); - selection + wrap.enter() + .append('div') + .attr('class', 'map-in-map') + .style('display', (hidden ? 'none' : 'block')) + .on('mousedown.map-in-map', startMouse) + .on('mouseup.map-in-map', endMouse) .call(zoom) .on('dblclick.zoom', null); From 45e4e730a83369f4c61058db18b959ea54ed73ff Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Feb 2016 15:41:22 -0500 Subject: [PATCH 10/15] iD.svg.Surface -> iD.svg.Layers --- ARCHITECTURE.md | 2 +- index.html | 2 +- js/id/renderer/map.js | 53 ++++++++++++++++------------- js/id/svg/defs.js | 2 +- js/id/svg/gpx.js | 39 +++++++++++---------- js/id/svg/icon.js | 2 +- js/id/svg/labels.js | 8 ++--- js/id/svg/{surface.js => layers.js} | 37 ++++++++++++-------- js/id/svg/mapillary_images.js | 16 ++++----- js/id/svg/mapillary_signs.js | 16 ++++----- js/id/svg/osm.js | 2 +- js/id/svg/turns.js | 2 +- js/id/ui/preset/restrictions.js | 17 ++++----- test/index.html | 2 +- test/rendering.html | 10 +++--- test/spec/svg/areas.js | 2 +- test/spec/svg/lines.js | 2 +- test/spec/svg/midpoints.js | 2 +- test/spec/svg/points.js | 2 +- test/spec/svg/vertices.js | 2 +- 20 files changed, 116 insertions(+), 104 deletions(-) rename js/id/svg/{surface.js => layers.js} (69%) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b230a443a..51c8e1802 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -296,7 +296,7 @@ correspondence with entities: two vertices. * `iD.svg.Labels` renders the textual [labels](http://mapbox.com/osmdev/2013/02/12/labeling-id/). -* `iD.svg.Surface` sets up a number of layers that ensure that map elements +* `iD.svg.Layers` sets up a number of layers that ensure that map elements appear in an appropriate z-order. ## Other UI diff --git a/index.html b/index.html index b5706274f..9f81877df 100644 --- a/index.html +++ b/index.html @@ -71,7 +71,7 @@ - + diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 39492ceaa..24c5f4c91 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -12,13 +12,13 @@ iD.Map = function(context) { transformStart, transformed = false, minzoom = 0, - drawSurface = iD.svg.Surface(projection, context), - points = iD.svg.Points(projection, context), - vertices = iD.svg.Vertices(projection, context), - lines = iD.svg.Lines(projection), - areas = iD.svg.Areas(projection), - midpoints = iD.svg.Midpoints(projection, context), - labels = iD.svg.Labels(projection, context), + drawLayers = iD.svg.Layers(projection, context), + drawPoints = iD.svg.Points(projection, context), + drawVertices = iD.svg.Vertices(projection, context), + drawLines = iD.svg.Lines(projection), + drawAreas = iD.svg.Areas(projection), + drawMidpoints = iD.svg.Midpoints(projection, context), + drawLabels = iD.svg.Labels(projection, context), surface, supersurface, mouse, @@ -42,13 +42,15 @@ iD.Map = function(context) { // Need a wrapper div because Opera can't cope with an absolutely positioned // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16 - var dataLayer = supersurface + var wrap = supersurface .append('div') .attr('class', 'layer layer-data'); - map.surface = surface = dataLayer - .call(drawSurface) - .select('.surface') + map.layers = drawLayers; + + map.surface = surface = wrap + .call(drawLayers) + .selectAll('.surface') .attr('id', 'surface'); surface @@ -66,14 +68,14 @@ iD.Map = function(context) { .on('mouseover.vertices', function() { if (map.editable() && !transformed) { var hover = d3.event.target.__data__; - surface.call(vertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); + surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); dispatch.drawn({full: false}); } }) .on('mouseout.vertices', function() { if (map.editable() && !transformed) { var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__; - surface.call(vertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); + surface.call(drawVertices.drawHover, context.graph(), hover, map.extent(), map.zoom()); dispatch.drawn({full: false}); } }); @@ -90,15 +92,16 @@ iD.Map = function(context) { graph = context.graph(); all = context.features().filter(all, graph); - surface.call(vertices, graph, all, filter, map.extent(), map.zoom()); - surface.call(midpoints, graph, all, filter, map.trimmedExtent()); + surface + .call(drawVertices, graph, all, filter, map.extent(), map.zoom()) + .call(drawMidpoints, graph, all, filter, map.trimmedExtent()); dispatch.drawn({full: false}); } }); map.dimensions(selection.dimensions()); - labels.supersurface(supersurface); + drawLabels.supersurface(supersurface); } function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; } @@ -136,12 +139,12 @@ iD.Map = function(context) { data = features.filter(data, graph); surface - .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, graph, data, filter); + .call(drawVertices, graph, data, filter, map.extent(), map.zoom()) + .call(drawLines, graph, data, filter) + .call(drawAreas, graph, data, filter) + .call(drawMidpoints, graph, data, filter, map.trimmedExtent()) + .call(drawLabels, graph, data, filter, dimensions, !difference && !extent) + .call(drawPoints, graph, data, filter); dispatch.drawn({full: true}); } @@ -215,6 +218,7 @@ iD.Map = function(context) { supersurface.call(context.background()); } + // OSM if (map.editable()) { context.loadTiles(projection, dimensions); drawVector(difference, extent); @@ -222,6 +226,9 @@ iD.Map = function(context) { editOff(); } + surface + .call(drawLayers); + transformStart = [ projection.scale() * 2 * Math.PI, projection.translate().slice()]; @@ -331,7 +338,7 @@ iD.Map = function(context) { if (!arguments.length) return dimensions; var center = map.center(); dimensions = _; - drawSurface.dimensions(dimensions); + drawLayers.dimensions(dimensions); context.background().dimensions(dimensions); projection.clipExtent([[0, 0], dimensions]); mouse = iD.util.fastMouse(supersurface.node()); diff --git a/js/id/svg/defs.js b/js/id/svg/defs.js index 03054e377..cc6f8ad48 100644 --- a/js/id/svg/defs.js +++ b/js/id/svg/defs.js @@ -15,7 +15,7 @@ iD.svg.Defs = function(context) { }; } - return function (selection) { + return function drawDefs(selection) { var defs = selection.append('defs'); // marker diff --git a/js/id/svg/gpx.js b/js/id/svg/gpx.js index 7073b8a26..d77003cd7 100644 --- a/js/id/svg/gpx.js +++ b/js/id/svg/gpx.js @@ -2,7 +2,6 @@ iD.svg.Gpx = function(projection, context) { var showLabels = true, layer; - function init() { if (iD.svg.Gpx.initialized) return; // run once @@ -21,7 +20,7 @@ iD.svg.Gpx = function(projection, context) { d3.event.stopPropagation(); d3.event.preventDefault(); if (!iD.detect().filedrop) return; - gpx.files(d3.event.dataTransfer.files); + drawGpx.files(d3.event.dataTransfer.files); }) .on('dragenter.localgpx', over) .on('dragexit.localgpx', over) @@ -31,7 +30,7 @@ iD.svg.Gpx = function(projection, context) { } - function gpx(surface) { + function drawGpx(surface) { var geojson = iD.svg.Gpx.geojson, enabled = iD.svg.Gpx.enabled; @@ -97,51 +96,51 @@ iD.svg.Gpx = function(projection, context) { context.pan([0,0]); } - gpx.showLabels = function(_) { + drawGpx.showLabels = function(_) { if (!arguments.length) return showLabels; showLabels = _; - return gpx; + return drawGpx; }; - gpx.enabled = function(_) { + drawGpx.enabled = function(_) { if (!arguments.length) return iD.svg.Gpx.enabled; iD.svg.Gpx.enabled = _; - return gpx; + return drawGpx; }; - gpx.geojson = function(gj) { + drawGpx.geojson = function(gj) { if (!arguments.length) return iD.svg.Gpx.geojson; - if (_.isEmpty(gj) || _.isEmpty(gj.features)) return gpx; + if (_.isEmpty(gj) || _.isEmpty(gj.features)) return drawGpx; iD.svg.Gpx.geojson = gj; - return gpx; + return drawGpx; }; - gpx.url = function(url) { + drawGpx.url = function(url) { d3.text(url, function(err, data) { if (!err) { - gpx.geojson(toGeoJSON.gpx(toDom(data))); + drawGpx.geojson(toGeoJSON.gpx(toDom(data))); redraw(); } }); - return gpx; + return drawGpx; }; - gpx.files = function(fileList) { + drawGpx.files = function(fileList) { var f = fileList[0], reader = new FileReader(); reader.onload = function(e) { - gpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom(); + drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom(); redraw(); }; reader.readAsText(f); - return gpx; + return drawGpx; }; - gpx.fitZoom = function() { + drawGpx.fitZoom = function() { var geojson = iD.svg.Gpx.geojson; - if (_.isEmpty(geojson) || _.isEmpty(geojson.features)) return gpx; + if (_.isEmpty(geojson) || _.isEmpty(geojson.features)) return drawGpx; var map = context.map(), viewport = map.trimmedExtent().polygon(), @@ -155,9 +154,9 @@ iD.svg.Gpx = function(projection, context) { map.centerZoom(extent.center(), map.trimmedExtentZoom(extent)); } - return gpx; + return drawGpx; }; init(); - return gpx; + return drawGpx; }; diff --git a/js/id/svg/icon.js b/js/id/svg/icon.js index 3c8d2f02a..61a432d5a 100644 --- a/js/id/svg/icon.js +++ b/js/id/svg/icon.js @@ -1,5 +1,5 @@ iD.svg.Icon = function(name, svgklass, useklass) { - return function (selection) { + return function drawIcon(selection) { selection.selectAll('svg') .data([0]) .enter() diff --git a/js/id/svg/labels.js b/js/id/svg/labels.js index e85a9df9c..c3315effb 100644 --- a/js/id/svg/labels.js +++ b/js/id/svg/labels.js @@ -131,7 +131,6 @@ iD.svg.Labels = function(projection, context) { } function drawPointLabels(group, entities, filter, classes, labels) { - var texts = group.selectAll('text.' + classes) .filter(filter) .data(entities, iD.Entity.key); @@ -248,8 +247,7 @@ iD.svg.Labels = function(projection, context) { var rtree = rbush(), rectangles = {}; - function labels(surface, graph, entities, filter, dimensions, fullRedraw) { - + function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) { var hidePoints = !surface.select('.node.point').node(); var labelable = [], i, k, entity; @@ -427,7 +425,7 @@ iD.svg.Labels = function(projection, context) { drawAreaIcons(label, labelled.area, filter, 'arealabel-icon', positions.area); } - labels.supersurface = function(supersurface) { + drawLabels.supersurface = function(supersurface) { supersurface .on('mousemove.hidelabels', hideOnMouseover) .on('mousedown.hidelabels', function () { @@ -438,5 +436,5 @@ iD.svg.Labels = function(projection, context) { }); }; - return labels; + return drawLabels; }; diff --git a/js/id/svg/surface.js b/js/id/svg/layers.js similarity index 69% rename from js/id/svg/surface.js rename to js/id/svg/layers.js index 2f017622d..bad49f0ab 100644 --- a/js/id/svg/surface.js +++ b/js/id/svg/layers.js @@ -1,4 +1,4 @@ -iD.svg.Surface = function(projection, context) { +iD.svg.Layers = function(projection, context) { var svg = d3.select(null), layers = [ { id: 'osm', render: iD.svg.Osm(projection, context) }, @@ -8,7 +8,7 @@ iD.svg.Surface = function(projection, context) { ]; - function surface(selection) { + function drawLayers(selection) { svg = selection.selectAll('.surface') .data([0]); @@ -31,32 +31,39 @@ iD.svg.Surface = function(projection, context) { .remove(); } - - surface.only = function(what) { - var arr = [].concat(what); - surface.remove(_.difference(_.pluck(layers, 'id'), arr)); - return surface; + drawLayers.all = function() { + return layers; }; - surface.remove = function(what) { + drawLayers.layer = function(id) { + return _.find(layers, 'id', id); + }; + + drawLayers.only = function(what) { + var arr = [].concat(what); + drawLayers.remove(_.difference(_.pluck(layers, 'id'), arr)); + return drawLayers; + }; + + drawLayers.remove = function(what) { var arr = [].concat(what); arr.forEach(function(id) { - layers = _.reject(layers, function(d) { return d.id === id; }); + layers = _.reject(layers, 'id', id); }); - return surface; + return drawLayers; }; - surface.add = function(what) { + drawLayers.add = function(what) { var arr = [].concat(what); arr.forEach(function(obj) { if ('id' in obj && 'render' in obj) { layers.push(obj); } }); - return surface; + return drawLayers; }; - surface.dimensions = function(_) { + drawLayers.dimensions = function(_) { if (!arguments.length) return svg.dimensions(); svg.dimensions(_); layers.forEach(function(layer) { @@ -64,9 +71,9 @@ iD.svg.Surface = function(projection, context) { layer.render.dimensions(_); } }); - return surface; + return drawLayers; }; - return surface; + return drawLayers; }; diff --git a/js/id/svg/mapillary_images.js b/js/id/svg/mapillary_images.js index 3ba2889e9..6ddd9e92f 100644 --- a/js/id/svg/mapillary_images.js +++ b/js/id/svg/mapillary_images.js @@ -76,7 +76,7 @@ iD.svg.MapillaryImages = function(projection, context) { return t; } - function drawMarkers() { + function update() { var mapillary = getMapillary(), data = (mapillary ? mapillary.images(projection, layer.dimensions()) : []); @@ -107,7 +107,7 @@ iD.svg.MapillaryImages = function(projection, context) { .attr('transform', transform); } - function render(selection) { + function drawImages(selection) { var mapillary = getMapillary(); layer = selection.selectAll('.layer-mapillary-images') @@ -152,7 +152,7 @@ iD.svg.MapillaryImages = function(projection, context) { if (enabled) { if (mapillary && ~~context.map().zoom() >= minZoom) { editOn(); - drawMarkers(); + update(); mapillary.loadImages(projection, layer.dimensions()); } else { editOff(); @@ -160,7 +160,7 @@ iD.svg.MapillaryImages = function(projection, context) { } } - render.enable = function(_) { + drawImages.enable = function(_) { if (!arguments.length) return enabled; enabled = _; if (enabled) { @@ -168,14 +168,14 @@ iD.svg.MapillaryImages = function(projection, context) { } else { hideLayer(); } - return render; + return drawImages; }; - render.dimensions = function(_) { + drawImages.dimensions = function(_) { if (!arguments.length) return layer.dimensions(); layer.dimensions(_); - return render; + return drawImages; }; - return render; + return drawImages; }; diff --git a/js/id/svg/mapillary_signs.js b/js/id/svg/mapillary_signs.js index d0648256c..bc53f543b 100644 --- a/js/id/svg/mapillary_signs.js +++ b/js/id/svg/mapillary_signs.js @@ -61,7 +61,7 @@ iD.svg.MapillarySigns = function(projection, context) { layer.style('display', 'none'); } - function drawSigns() { + function update() { var mapillary = getMapillary(), data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []); @@ -114,7 +114,7 @@ iD.svg.MapillarySigns = function(projection, context) { .attr('transform', iD.svg.PointTransform(projection)); } - function render(selection) { + function drawSigns(selection) { var mapillary = getMapillary(); layer = selection.selectAll('.layer-mapillary-signs') @@ -132,7 +132,7 @@ iD.svg.MapillarySigns = function(projection, context) { if (enabled) { if (mapillary && ~~context.map().zoom() >= minZoom) { editOn(); - drawSigns(); + update(); mapillary.loadSigns(context, projection, layer.dimensions()); } else { editOff(); @@ -140,7 +140,7 @@ iD.svg.MapillarySigns = function(projection, context) { } } - render.enable = function(_) { + drawSigns.enable = function(_) { if (!arguments.length) return enabled; enabled = _; if (enabled) { @@ -148,14 +148,14 @@ iD.svg.MapillarySigns = function(projection, context) { } else { hideLayer(); } - return render; + return drawSigns; }; - render.dimensions = function(_) { + drawSigns.dimensions = function(_) { if (!arguments.length) return layer.dimensions(); layer.dimensions(_); - return render; + return drawSigns; }; - return render; + return drawSigns; }; diff --git a/js/id/svg/osm.js b/js/id/svg/osm.js index 72f93bbb8..ac6387bdb 100644 --- a/js/id/svg/osm.js +++ b/js/id/svg/osm.js @@ -1,5 +1,5 @@ iD.svg.Osm = function() { - return function (selection) { + return function drawOsm(selection) { var layers = selection.selectAll('.layer-osm') .data(['areas', 'lines', 'hit', 'halo', 'label']); diff --git a/js/id/svg/turns.js b/js/id/svg/turns.js index 98955ec98..9cbfe80c9 100644 --- a/js/id/svg/turns.js +++ b/js/id/svg/turns.js @@ -1,5 +1,5 @@ iD.svg.Turns = function(projection) { - return function(surface, graph, turns) { + return function drawTurns(surface, graph, turns) { function key(turn) { return [turn.from.node + turn.via.node + turn.to.node].join('-'); } diff --git a/js/id/ui/preset/restrictions.js b/js/id/ui/preset/restrictions.js index 9c5081010..2c7912074 100644 --- a/js/id/ui/preset/restrictions.js +++ b/js/id/ui/preset/restrictions.js @@ -21,10 +21,7 @@ iD.ui.preset.restrictions = function(field, context) { vertex = graph.entity(vertexID), filter = d3.functor(true), extent = iD.geo.Extent(), - projection = iD.geo.RawMercator(), - lines = iD.svg.Lines(projection, context), - vertices = iD.svg.Vertices(projection, context), - turns = iD.svg.Turns(projection, context); + projection = iD.geo.RawMercator(); var d = wrap.dimensions(), c = [d[0] / 2, d[1] / 2], @@ -39,9 +36,13 @@ iD.ui.preset.restrictions = function(field, context) { .translate([c[0] - s[0], c[1] - s[1]]) .clipExtent([[0, 0], d]); + var drawLayers = iD.svg.Layers(projection, context).only('osm').dimensions(d), + drawVertices = iD.svg.Vertices(projection, context), + drawLines = iD.svg.Lines(projection, context), + drawTurns = iD.svg.Turns(projection, context); enter - .call(iD.svg.Surface(projection, context).only('osm').dimensions(d)) + .call(drawLayers) .select('.surface') .call(iD.behavior.Hover(context)); @@ -50,9 +51,9 @@ iD.ui.preset.restrictions = function(field, context) { surface .dimensions(d) - .call(vertices, graph, [vertex], filter, extent, z) - .call(lines, graph, intersection.ways, filter) - .call(turns, graph, intersection.turns(fromNodeID)); + .call(drawVertices, graph, [vertex], filter, extent, z) + .call(drawLines, graph, intersection.ways, filter) + .call(drawTurns, graph, intersection.turns(fromNodeID)); surface .on('click.restrictions', click) diff --git a/test/index.html b/test/index.html index abd89e8ea..2325206c7 100644 --- a/test/index.html +++ b/test/index.html @@ -76,7 +76,7 @@ - + diff --git a/test/rendering.html b/test/rendering.html index dfe463a48..35d8d8dfa 100644 --- a/test/rendering.html +++ b/test/rendering.html @@ -23,7 +23,7 @@ - + @@ -147,7 +147,7 @@ .attr('width', 30) .attr('height', 40) .attr('data-zoom', function (d) { return d.zoom; }) - .call(iD.svg.Surface(context)) + .call(iD.svg.Layers(context)) .each(function (d) { var n = node.update({tags: d}), graph = iD.Graph([n]); @@ -215,7 +215,7 @@ .attr('width', 30) .attr('height', 30) .attr('data-zoom', function (d) { return d.zoom; }) - .call(iD.svg.Surface(context)) + .call(iD.svg.Layers(context)) .each(function (d) { var n = node.update({tags: d.tags}), graph = iD.Graph([n, way]); @@ -303,7 +303,7 @@ .attr('width', 200) .attr('height', 30) .attr('data-zoom', function (d) { return d.zoom; }) - .call(iD.svg.Surface(context)) + .call(iD.svg.Layers(context)) .each(function (d) { var highway = way.update({tags: d.tags}), graph = iD.Graph([a, b, highway]); @@ -365,7 +365,7 @@ .append('svg') .attr('width', 100) .attr('height', 100) - .call(iD.svg.Surface(context)) + .call(iD.svg.Layers(context)) .each(function (datum) { var area = way.update({tags: datum.tags}), graph = iD.Graph([a, b, c, d, area]); diff --git a/test/spec/svg/areas.js b/test/spec/svg/areas.js index fcbcb1792..8e6d06fe3 100644 --- a/test/spec/svg/areas.js +++ b/test/spec/svg/areas.js @@ -7,7 +7,7 @@ describe("iD.svg.Areas", function () { beforeEach(function () { surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg')) - .call(iD.svg.Surface(iD())); + .call(iD.svg.Layers(iD())); }); it("adds way and area classes", function () { diff --git a/test/spec/svg/lines.js b/test/spec/svg/lines.js index b94575a60..27fc0ec10 100644 --- a/test/spec/svg/lines.js +++ b/test/spec/svg/lines.js @@ -7,7 +7,7 @@ describe("iD.svg.Lines", function () { beforeEach(function () { surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg')) - .call(iD.svg.Surface(iD())); + .call(iD.svg.Layers(iD())); }); it("adds way and line classes", function () { diff --git a/test/spec/svg/midpoints.js b/test/spec/svg/midpoints.js index eaef0d16e..4d012aae3 100644 --- a/test/spec/svg/midpoints.js +++ b/test/spec/svg/midpoints.js @@ -7,7 +7,7 @@ describe("iD.svg.Midpoints", function () { beforeEach(function () { context = iD(); surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg')) - .call(iD.svg.Surface(context)); + .call(iD.svg.Layers(context)); }); it("creates midpoint on segment completely within the extent", function () { diff --git a/test/spec/svg/points.js b/test/spec/svg/points.js index 80790bba6..38d043430 100644 --- a/test/spec/svg/points.js +++ b/test/spec/svg/points.js @@ -6,7 +6,7 @@ describe("iD.svg.Points", function () { beforeEach(function () { context = iD().presets(iD.data.presets); surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg')) - .call(iD.svg.Surface(context)); + .call(iD.svg.Layers(context)); }); it("adds tag classes", function () { diff --git a/test/spec/svg/vertices.js b/test/spec/svg/vertices.js index 20182756b..0eec0e45b 100644 --- a/test/spec/svg/vertices.js +++ b/test/spec/svg/vertices.js @@ -6,7 +6,7 @@ describe("iD.svg.Vertices", function () { beforeEach(function () { context = iD(); surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg')) - .call(iD.svg.Surface(context)); + .call(iD.svg.Layers(context)); }); it("adds the .shared class to vertices that are members of two or more ways", function () { From 69bc50aafb12f80e2da83a1f443d9f498c85082a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Feb 2016 22:38:03 -0500 Subject: [PATCH 11/15] Move data layers out of Background --- js/id/renderer/background.js | 93 ++--------------------------------- js/id/renderer/map.js | 13 ++--- js/id/svg/gpx.js | 25 ++++++---- js/id/svg/layers.js | 29 +++++------ js/id/svg/mapillary_images.js | 28 ++++++++--- js/id/svg/mapillary_signs.js | 32 ++++++++---- js/id/ui/map_data.js | 55 ++++++++++++--------- 7 files changed, 114 insertions(+), 161 deletions(-) diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index d04f471f9..dd5e427a6 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -1,9 +1,6 @@ iD.Background = function(context) { var dispatch = d3.dispatch('change'), baseLayer = iD.TileLayer().projection(context.projection), - gpxLayer = iD.svg.Gpx(context.projection, context), - mapillaryImageLayer, - mapillarySignLayer, overlayLayers = []; var backgroundSources; @@ -47,7 +44,8 @@ iD.Background = function(context) { } }); - if (background.showsGpxLayer()) { + var gpx = context.layers().layer('gpx'); + if (gpx && gpx.enabled() && gpx.hasGpx()) { imageryUsed.push('Local GPX'); } @@ -77,51 +75,6 @@ iD.Background = function(context) { overlays.exit() .remove(); - - - - // selection.selectAll('#surface') - // .call(gpxLayer); - - - - - // var mapillary = iD.services.mapillary, - // supportsMapillaryImages = !!mapillary, - // supportsMapillarySigns = !!mapillary && mapillary().signsSupported(); - - // var mapillaryImages = selection.selectAll('.layer-mapillary-images') - // .data(supportsMapillaryImages ? [0] : []); - - // mapillaryImages.enter().insert('div') - // .attr('class', 'layer-layer layer-mapillary-images'); - - // if (supportsMapillaryImages) { - // if (!mapillaryImageLayer) { mapillaryImageLayer = iD.svg.MapillaryImages(context); } - // mapillaryImages.call(mapillaryImageLayer); - // } else { - // mapillaryImageLayer = null; - // } - - // mapillaryImages.exit() - // .remove(); - - - // var mapillarySigns = selection.selectAll('.layer-mapillary-signs') - // .data(supportsMapillarySigns ? [0] : []); - - // mapillarySigns.enter().insert('div') - // .attr('class', 'layer-layer layer-mapillary-signs'); - - // if (supportsMapillarySigns) { - // if (!mapillarySignLayer) { mapillarySignLayer = iD.svg.MapillarySigns(context); } - // mapillarySigns.call(mapillarySignLayer); - // } else { - // mapillarySignLayer = null; - // } - - // mapillarySigns.exit() - // .remove(); } background.sources = function(extent) { @@ -132,8 +85,6 @@ iD.Background = function(context) { background.dimensions = function(_) { baseLayer.dimensions(_); - if (mapillaryImageLayer) mapillaryImageLayer.dimensions(_); - if (mapillarySignLayer) mapillarySignLayer.dimensions(_); overlayLayers.forEach(function(layer) { layer.dimensions(_); @@ -154,43 +105,6 @@ iD.Background = function(context) { background.baseLayerSource(findSource('Bing')); }; - background.gpxLayer = function() { - return gpxLayer; - }; - - background.hasGpxLayer = function() { - return !_.isEmpty(gpxLayer.geojson()); - }; - - background.showsGpxLayer = function() { - return background.hasGpxLayer() && gpxLayer.enabled(); - }; - - background.toggleGpxLayer = function() { - gpxLayer.enabled(!gpxLayer.enabled()); - dispatch.change(); - }; - - background.showsMapillaryImageLayer = function() { - return mapillaryImageLayer && mapillaryImageLayer.enable(); - }; - - background.showsMapillarySignLayer = function() { - return mapillarySignLayer && mapillarySignLayer.enable(); - }; - - background.toggleMapillaryImageLayer = function() { - if (!mapillaryImageLayer) return; - mapillaryImageLayer.enable(!mapillaryImageLayer.enable()); - dispatch.change(); - }; - - background.toggleMapillarySignLayer = function() { - if (!mapillarySignLayer) return; - mapillarySignLayer.enable(!mapillarySignLayer.enable()); - dispatch.change(); - }; - background.showsLayer = function(d) { return d === baseLayer.source() || (d.id === 'custom' && baseLayer.source().id === 'custom') || @@ -285,7 +199,8 @@ iD.Background = function(context) { }); if (q.gpx) { - gpxLayer.url(q.gpx); + var gpx = context.layers().layer('gpx'); + if (gpx) { gpx.url(q.gpx); } } }; diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 24c5f4c91..22ad6027a 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -19,8 +19,9 @@ iD.Map = function(context) { drawAreas = iD.svg.Areas(projection), drawMidpoints = iD.svg.Midpoints(projection, context), drawLabels = iD.svg.Labels(projection, context), - surface, supersurface, + wrapper, + surface, mouse, mousemove; @@ -42,13 +43,11 @@ iD.Map = function(context) { // Need a wrapper div because Opera can't cope with an absolutely positioned // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16 - var wrap = supersurface + wrapper = supersurface .append('div') .attr('class', 'layer layer-data'); - map.layers = drawLayers; - - map.surface = surface = wrap + map.surface = surface = wrapper .call(drawLayers) .selectAll('.surface') .attr('id', 'surface'); @@ -226,7 +225,7 @@ iD.Map = function(context) { editOff(); } - surface + wrapper .call(drawLayers); transformStart = [ @@ -484,5 +483,7 @@ iD.Map = function(context) { return map; }; + map.layers = drawLayers; + return d3.rebind(map, dispatch, 'on'); }; diff --git a/js/id/svg/gpx.js b/js/id/svg/gpx.js index d77003cd7..b0700ccbc 100644 --- a/js/id/svg/gpx.js +++ b/js/id/svg/gpx.js @@ -99,30 +99,35 @@ iD.svg.Gpx = function(projection, context) { drawGpx.showLabels = function(_) { if (!arguments.length) return showLabels; showLabels = _; - return drawGpx; + return this; }; drawGpx.enabled = function(_) { if (!arguments.length) return iD.svg.Gpx.enabled; iD.svg.Gpx.enabled = _; - return drawGpx; + return this; + }; + + drawGpx.hasGpx = function() { + var geojson = iD.svg.Gpx.geojson; + return (!(_.isEmpty(geojson) || _.isEmpty(geojson.features))); }; drawGpx.geojson = function(gj) { if (!arguments.length) return iD.svg.Gpx.geojson; - if (_.isEmpty(gj) || _.isEmpty(gj.features)) return drawGpx; + if (_.isEmpty(gj) || _.isEmpty(gj.features)) return this; iD.svg.Gpx.geojson = gj; - return drawGpx; + return this; }; drawGpx.url = function(url) { d3.text(url, function(err, data) { if (!err) { - drawGpx.geojson(toGeoJSON.gpx(toDom(data))); + this.geojson(toGeoJSON.gpx(toDom(data))); redraw(); } }); - return drawGpx; + return this; }; drawGpx.files = function(fileList) { @@ -130,17 +135,17 @@ iD.svg.Gpx = function(projection, context) { reader = new FileReader(); reader.onload = function(e) { - drawGpx.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom(); + this.geojson(toGeoJSON.gpx(toDom(e.target.result))).fitZoom(); redraw(); }; reader.readAsText(f); - return drawGpx; + return this; }; drawGpx.fitZoom = function() { + if (!this.hasGpx()) return this; var geojson = iD.svg.Gpx.geojson; - if (_.isEmpty(geojson) || _.isEmpty(geojson.features)) return drawGpx; var map = context.map(), viewport = map.trimmedExtent().polygon(), @@ -154,7 +159,7 @@ iD.svg.Gpx = function(projection, context) { map.centerZoom(extent.center(), map.trimmedExtentZoom(extent)); } - return drawGpx; + return this; }; init(); diff --git a/js/id/svg/layers.js b/js/id/svg/layers.js index bad49f0ab..55733d14f 100644 --- a/js/id/svg/layers.js +++ b/js/id/svg/layers.js @@ -1,10 +1,10 @@ iD.svg.Layers = function(projection, context) { var svg = d3.select(null), layers = [ - { id: 'osm', render: iD.svg.Osm(projection, context) }, - { id: 'gpx', render: iD.svg.Gpx(projection, context) }, - { id: 'mapillary-images', render: iD.svg.MapillaryImages(projection, context) }, - { id: 'mapillary-signs', render: iD.svg.MapillarySigns(projection, context) } + { id: 'osm', layer: iD.svg.Osm(projection, context) }, + { id: 'gpx', layer: iD.svg.Gpx(projection, context) }, + { id: 'mapillary-images', layer: iD.svg.MapillaryImages(projection, context) }, + { id: 'mapillary-signs', layer: iD.svg.MapillarySigns(projection, context) } ]; @@ -25,7 +25,7 @@ iD.svg.Layers = function(projection, context) { .attr('class', function(d) { return 'data-layer data-layer-' + d.id; }); groups - .each(function(d) { d3.select(this).call(d.render); }); + .each(function(d) { d3.select(this).call(d.layer); }); groups.exit() .remove(); @@ -36,13 +36,14 @@ iD.svg.Layers = function(projection, context) { }; drawLayers.layer = function(id) { - return _.find(layers, 'id', id); + var obj = _.find(layers, 'id', id); + return obj && obj.layer; }; drawLayers.only = function(what) { var arr = [].concat(what); drawLayers.remove(_.difference(_.pluck(layers, 'id'), arr)); - return drawLayers; + return this; }; drawLayers.remove = function(what) { @@ -50,28 +51,28 @@ iD.svg.Layers = function(projection, context) { arr.forEach(function(id) { layers = _.reject(layers, 'id', id); }); - return drawLayers; + return this; }; drawLayers.add = function(what) { var arr = [].concat(what); arr.forEach(function(obj) { - if ('id' in obj && 'render' in obj) { + if ('id' in obj && 'layer' in obj) { layers.push(obj); } }); - return drawLayers; + return this; }; drawLayers.dimensions = function(_) { if (!arguments.length) return svg.dimensions(); svg.dimensions(_); - layers.forEach(function(layer) { - if (layer.render.dimensions) { - layer.render.dimensions(_); + layers.forEach(function(obj) { + if (obj.layer.dimensions) { + obj.layer.dimensions(_); } }); - return drawLayers; + return this; }; diff --git a/js/id/svg/mapillary_images.js b/js/id/svg/mapillary_images.js index 6ddd9e92f..bc8d0d6a6 100644 --- a/js/id/svg/mapillary_images.js +++ b/js/id/svg/mapillary_images.js @@ -1,10 +1,16 @@ iD.svg.MapillaryImages = function(projection, context) { var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000), - enabled = false, minZoom = 12, layer = d3.select(null), _mapillary; + + function init() { + if (iD.svg.MapillaryImages.initialized) return; // run once + iD.svg.MapillaryImages.enabled = false; + iD.svg.MapillaryImages.initialized = true; + } + function getMapillary() { if (iD.services.mapillary && !_mapillary) { _mapillary = iD.services.mapillary().on('loadedImages', debouncedRedraw); @@ -108,7 +114,8 @@ iD.svg.MapillaryImages = function(projection, context) { } function drawImages(selection) { - var mapillary = getMapillary(); + var enabled = iD.svg.MapillaryImages.enabled, + mapillary = getMapillary(); layer = selection.selectAll('.layer-mapillary-images') .data(mapillary ? [0] : []); @@ -160,22 +167,27 @@ iD.svg.MapillaryImages = function(projection, context) { } } - drawImages.enable = function(_) { - if (!arguments.length) return enabled; - enabled = _; - if (enabled) { + drawImages.enabled = function(_) { + if (!arguments.length) return iD.svg.MapillaryImages.enabled; + iD.svg.MapillaryImages.enabled = _; + if (iD.svg.MapillaryImages.enabled) { showLayer(); } else { hideLayer(); } - return drawImages; + return this; + }; + + drawImages.supported = function() { + return !!getMapillary(); }; drawImages.dimensions = function(_) { if (!arguments.length) return layer.dimensions(); layer.dimensions(_); - return drawImages; + return this; }; + init(); return drawImages; }; diff --git a/js/id/svg/mapillary_signs.js b/js/id/svg/mapillary_signs.js index bc53f543b..57b0ca443 100644 --- a/js/id/svg/mapillary_signs.js +++ b/js/id/svg/mapillary_signs.js @@ -1,10 +1,16 @@ iD.svg.MapillarySigns = function(projection, context) { var debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000), - enabled = false, minZoom = 12, layer = d3.select(null), _mapillary; + + function init() { + if (iD.svg.MapillarySigns.initialized) return; // run once + iD.svg.MapillarySigns.enabled = false; + iD.svg.MapillarySigns.initialized = true; + } + function getMapillary() { if (iD.services.mapillary && !_mapillary) { _mapillary = iD.services.mapillary().on('loadedSigns', debouncedRedraw); @@ -65,8 +71,7 @@ iD.svg.MapillarySigns = function(projection, context) { var mapillary = getMapillary(), data = (mapillary ? mapillary.signs(projection, layer.dimensions()) : []); - var signs = layer.select('.layer-mapillary-signs') - .selectAll('.icon-sign') + var signs = layer.selectAll('.icon-sign') .data(data, function(d) { return d.key; }); // Enter @@ -115,7 +120,8 @@ iD.svg.MapillarySigns = function(projection, context) { } function drawSigns(selection) { - var mapillary = getMapillary(); + var enabled = iD.svg.MapillarySigns.enabled, + mapillary = getMapillary(); layer = selection.selectAll('.layer-mapillary-signs') .data(mapillary ? [0] : []); @@ -140,22 +146,28 @@ iD.svg.MapillarySigns = function(projection, context) { } } - drawSigns.enable = function(_) { - if (!arguments.length) return enabled; - enabled = _; - if (enabled) { + drawSigns.enabled = function(_) { + if (!arguments.length) return iD.svg.MapillarySigns.enabled; + iD.svg.MapillarySigns.enabled = _; + if (iD.svg.MapillarySigns.enabled) { showLayer(); } else { hideLayer(); } - return drawSigns; + return this; + }; + + drawSigns.supported = function() { + var mapillary = getMapillary(); + return (mapillary && mapillary.signsSupported()); }; drawSigns.dimensions = function(_) { if (!arguments.length) return layer.dimensions(); layer.dimensions(_); - return drawSigns; + return this; }; + init(); return drawSigns; }; diff --git a/js/id/ui/map_data.js b/js/id/ui/map_data.js index b3dee69b5..9ca8b9ace 100644 --- a/js/id/ui/map_data.js +++ b/js/id/ui/map_data.js @@ -1,6 +1,7 @@ iD.ui.MapData = function(context) { var key = 'F', features = context.features().keys(), + layers = context.layers(), fills = ['wireframe', 'partial', 'full'], fillDefault = context.storage('area-fill') || 'partial', fillSelected = fillDefault; @@ -38,30 +39,38 @@ iD.ui.MapData = function(context) { update(); } + function toggleLayer(which) { + var layer = layers.layer(which); + if (layer) { + layer.enabled(!layer.enabled()); + update(); + } + } + function clickGpx() { - context.background().toggleGpxLayer(); - update(); + toggleLayer('gpx'); } function clickMapillaryImages() { - context.background().toggleMapillaryImageLayer(); - update(); + toggleLayer('mapillary-images'); } function clickMapillarySigns() { - context.background().toggleMapillarySignLayer(); - update(); + toggleLayer('mapillary-signs'); } function drawMapillaryItems(selection) { - var mapillary = iD.services.mapillary, - supportsMapillaryImages = !!mapillary, - supportsMapillarySigns = !!mapillary && mapillary().signsSupported(); + var mapillaryImages = layers.layer('mapillary-images'), + mapillarySigns = layers.layer('mapillary-signs'), + supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(), + supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(), + showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(), + showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled(); var mapillaryList = selection - .selectAll('.mapillary-list') - .data([0]); + .selectAll('.mapillary-list') + .data([0]); // Enter mapillaryList @@ -70,8 +79,8 @@ iD.ui.MapData = function(context) { .attr('class', 'layer-list mapillary-list'); var mapillaryImageLayerItem = mapillaryList - .selectAll('.item-mapillary-images') - .data(supportsMapillaryImages ? [0] : []); + .selectAll('.item-mapillary-images') + .data(supportsMapillaryImages ? [0] : []); var enterImages = mapillaryImageLayerItem.enter() .append('li') @@ -91,8 +100,8 @@ iD.ui.MapData = function(context) { var mapillarySignLayerItem = mapillaryList - .selectAll('.item-mapillary-signs') - .data(supportsMapillarySigns ? [0] : []); + .selectAll('.item-mapillary-signs') + .data(supportsMapillarySigns ? [0] : []); var enterSigns = mapillarySignLayerItem.enter() .append('li') @@ -111,9 +120,6 @@ iD.ui.MapData = function(context) { .text(t('mapillary_signs.title')); // Update - var showsMapillaryImages = supportsMapillaryImages && context.background().showsMapillaryImageLayer(), - showsMapillarySigns = supportsMapillarySigns && context.background().showsMapillarySignLayer(); - mapillaryImageLayerItem .classed('active', showsMapillaryImages) .selectAll('input') @@ -133,9 +139,13 @@ iD.ui.MapData = function(context) { function drawGpxItem(selection) { + var gpx = layers.layer('gpx'), + hasGpx = gpx && gpx.hasGpx(), + showsGpx = hasGpx && gpx.enabled(); + var gpxLayerItem = selection .selectAll('.layer-gpx') - .data([0]); + .data(gpx ? [0] : []); // Enter var enter = gpxLayerItem.enter() @@ -152,7 +162,7 @@ iD.ui.MapData = function(context) { .on('click', function() { d3.event.preventDefault(); d3.event.stopPropagation(); - context.background().gpxLayer().fitZoom(); + gpx.fitZoom(); }) .call(iD.svg.Icon('#icon-search')); @@ -165,7 +175,7 @@ iD.ui.MapData = function(context) { d3.select(document.createElement('input')) .attr('type', 'file') .on('change', function() { - context.background().gpxLayer().files(d3.event.target.files); + gpx.files(d3.event.target.files); }) .node().click(); }) @@ -184,9 +194,6 @@ iD.ui.MapData = function(context) { .text(t('gpx.local_layer')); // Update - var hasGpx = context.background().hasGpxLayer(), - showsGpx = context.background().showsGpxLayer(); - gpxLayerItem .classed('active', showsGpx) .selectAll('input') From a40a6fae2afc2e1ee23b6e2ff234046015ad1a00 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 25 Feb 2016 16:54:52 -0500 Subject: [PATCH 12/15] Avoid sneaky subselection data inheritance --- js/id/svg/areas.js | 2 +- js/id/svg/labels.js | 6 +++--- js/id/svg/lines.js | 2 +- js/id/svg/midpoints.js | 2 +- js/id/svg/points.js | 2 +- js/id/svg/turns.js | 2 +- js/id/svg/vertices.js | 28 +++++++++------------------- js/id/ui/preset/restrictions.js | 2 +- 8 files changed, 18 insertions(+), 28 deletions(-) diff --git a/js/id/svg/areas.js b/js/id/svg/areas.js index ebc77101f..e4121e33d 100644 --- a/js/id/svg/areas.js +++ b/js/id/svg/areas.js @@ -82,7 +82,7 @@ iD.svg.Areas = function(projection) { .remove(); var areagroup = surface - .select('.layer-areas') + .selectAll('.layer-areas') .selectAll('g.areagroup') .data(['fill', 'shadow', 'stroke']); diff --git a/js/id/svg/labels.js b/js/id/svg/labels.js index c3315effb..d4639a71b 100644 --- a/js/id/svg/labels.js +++ b/js/id/svg/labels.js @@ -248,7 +248,7 @@ iD.svg.Labels = function(projection, context) { rectangles = {}; function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) { - var hidePoints = !surface.select('.node.point').node(); + var hidePoints = !surface.selectAll('.node.point').node(); var labelable = [], i, k, entity; for (i = 0; i < label_stack.length; i++) labelable.push([]); @@ -407,8 +407,8 @@ iD.svg.Labels = function(projection, context) { return v; } - var label = surface.select('.layer-label'), - halo = surface.select('.layer-halo'); + var label = surface.selectAll('.layer-label'), + halo = surface.selectAll('.layer-halo'); // points drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point); diff --git a/js/id/svg/lines.js b/js/id/svg/lines.js index d62d0c74e..216612416 100644 --- a/js/id/svg/lines.js +++ b/js/id/svg/lines.js @@ -50,7 +50,7 @@ iD.svg.Lines = function(projection) { }); var layergroup = surface - .select('.layer-lines') + .selectAll('.layer-lines') .selectAll('g.layergroup') .data(d3.range(-10, 11)); diff --git a/js/id/svg/midpoints.js b/js/id/svg/midpoints.js index 86a6a12a1..3daf82e0e 100644 --- a/js/id/svg/midpoints.js +++ b/js/id/svg/midpoints.js @@ -67,7 +67,7 @@ iD.svg.Midpoints = function(projection, context) { return false; } - var groups = surface.select('.layer-hit').selectAll('g.midpoint') + var groups = surface.selectAll('.layer-hit').selectAll('g.midpoint') .filter(midpointFilter) .data(_.values(midpoints), function(d) { return d.id; }); diff --git a/js/id/svg/points.js b/js/id/svg/points.js index d6deb8707..8bcf86fd9 100644 --- a/js/id/svg/points.js +++ b/js/id/svg/points.js @@ -18,7 +18,7 @@ iD.svg.Points = function(projection, context) { points.sort(sortY); - var groups = surface.select('.layer-hit').selectAll('g.point') + var groups = surface.selectAll('.layer-hit').selectAll('g.point') .filter(filter) .data(points, iD.Entity.key); diff --git a/js/id/svg/turns.js b/js/id/svg/turns.js index 9cbfe80c9..c5007a745 100644 --- a/js/id/svg/turns.js +++ b/js/id/svg/turns.js @@ -13,7 +13,7 @@ iD.svg.Turns = function(projection) { (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u; } - var groups = surface.select('.layer-hit').selectAll('g.turn') + var groups = surface.selectAll('.layer-hit').selectAll('g.turn') .data(turns, key); // Enter diff --git a/js/id/svg/vertices.js b/js/id/svg/vertices.js index 73b971880..c838c7daa 100644 --- a/js/id/svg/vertices.js +++ b/js/id/svg/vertices.js @@ -48,19 +48,10 @@ iD.svg.Vertices = function(projection, context) { function draw(selection, vertices, klass, graph, zoom) { var icons = {}, - z; + z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2); - if (zoom < 17) { - z = 0; - } else if (zoom < 18) { - z = 1; - } else { - z = 2; - } - - var groups = selection.data(vertices, function(entity) { - return iD.Entity.key(entity); - }); + var groups = selection + .data(vertices, iD.Entity.key); function icon(entity) { if (entity.id in icons) return icons[entity.id]; @@ -162,7 +153,7 @@ iD.svg.Vertices = function(projection, context) { } } - surface.select('.layer-hit').selectAll('g.vertex.vertex-persistent') + surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-persistent') .filter(filter) .call(draw, vertices, 'vertex-persistent', graph, zoom); @@ -172,15 +163,14 @@ iD.svg.Vertices = function(projection, context) { function drawHover(surface, graph, extent, zoom) { var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent) : {}; - surface.select('.layer-hit').selectAll('g.vertex.vertex-hover') + surface.selectAll('.layer-hit').selectAll('g.vertex.vertex-hover') .call(draw, d3.values(hovered), 'vertex-hover', graph, zoom); } - drawVertices.drawHover = function(surface, graph, _, extent, zoom) { - if (hover !== _) { - hover = _; - drawHover(surface, graph, extent, zoom); - } + drawVertices.drawHover = function(surface, graph, target, extent, zoom) { + if (target === hover) return; + hover = target; + drawHover(surface, graph, extent, zoom); }; return drawVertices; diff --git a/js/id/ui/preset/restrictions.js b/js/id/ui/preset/restrictions.js index 2c7912074..f2ef9cfac 100644 --- a/js/id/ui/preset/restrictions.js +++ b/js/id/ui/preset/restrictions.js @@ -43,7 +43,7 @@ iD.ui.preset.restrictions = function(field, context) { enter .call(drawLayers) - .select('.surface') + .selectAll('.surface') .call(iD.behavior.Hover(context)); From 34901d11b9f755f953fafc1ed07d363b836d0d30 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 25 Feb 2016 21:42:49 -0500 Subject: [PATCH 13/15] Remove radial menu at old position when resetting surface transform --- js/id/renderer/map.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 22ad6027a..f2fa3242f 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -190,6 +190,8 @@ iD.Map = function(context) { function resetTransform() { if (!transformed) return false; + + surface.selectAll('.radial-menu').interrupt().remove(); iD.util.setTransform(supersurface, 0, 0); transformed = false; return true; From a866856ec2a0319c97d8b404e25974508e950b1f Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 26 Feb 2016 00:14:09 -0500 Subject: [PATCH 14/15] Cleanup turn restriction handlers when form not visible --- js/id/ui/preset/restrictions.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/js/id/ui/preset/restrictions.js b/js/id/ui/preset/restrictions.js index f2ef9cfac..fe95a9138 100644 --- a/js/id/ui/preset/restrictions.js +++ b/js/id/ui/preset/restrictions.js @@ -1,9 +1,17 @@ iD.ui.preset.restrictions = function(field, context) { var dispatch = d3.dispatch('change'), + hover = iD.behavior.Hover(context), vertexID, fromNodeID; + function restrictions(selection) { + // if form field is hidden or has detached from dom, clean up. + if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { + selection.call(restrictions.off); + return; + } + var wrap = selection.selectAll('.preset-input-wrap') .data([0]); @@ -44,10 +52,10 @@ iD.ui.preset.restrictions = function(field, context) { enter .call(drawLayers) .selectAll('.surface') - .call(iD.behavior.Hover(context)); + .call(hover); - var surface = wrap.select('.surface'); + var surface = wrap.selectAll('.surface'); surface .dimensions(d) @@ -148,5 +156,19 @@ iD.ui.preset.restrictions = function(field, context) { restrictions.tags = function() {}; restrictions.focus = function() {}; + restrictions.off = function(selection) { + selection.selectAll('.surface') + .call(hover.off) + .on('click.restrictions', null) + .on('mouseover.restrictions', null) + .on('mouseout.restrictions', null); + + context.history() + .on('change.restrictions', null); + + d3.select(window) + .on('resize.restrictions', null); + }; + return d3.rebind(restrictions, dispatch, 'on'); }; From 1bf90fad1874330799f8f1cc981ff7cf90cee469 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 26 Feb 2016 16:35:29 -0500 Subject: [PATCH 15/15] Adjust bumpy surface style to make it visible in turn restriction editor (closes #3004) --- css/map.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/map.css b/css/map.css index aadefb962..33379001a 100644 --- a/css/map.css +++ b/css/map.css @@ -1268,7 +1268,7 @@ path.shadow.tag-cutting { /* Surface - unpaved */ path.casing.tag-unpaved { - stroke: #eaeaea; + stroke: #ccc; stroke-linecap: butt; stroke-dasharray: 4, 3; }