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')