From 51d915c8a66676ed983cff21e15a98482ce5ea36 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Wed, 20 Mar 2013 13:32:42 -0400 Subject: [PATCH] Refactoring and fixing layers * Fixed opacity control * Reorganize layer switcher * Add GPX item to menu * Allow user to cinch to GPX extent Needs: * Moderate refactoring * Possibly a specific 'layerchange event' * Dropping GPX on the map should be a behavior, probably, instead of in the layer * Layers, if we want them, should be named rather than just have list indices * A better icon for zoom to extent and for that icon to be properly placed --- css/app.css | 17 ++- data/core.yaml | 3 + js/id/renderer/localgpx.js | 24 +++- js/id/renderer/map.js | 8 +- js/id/ui/background.js | 225 ++++++++++++++++++++++--------------- 5 files changed, 174 insertions(+), 103 deletions(-) diff --git a/css/app.css b/css/app.css index 50b48896c..56f1c1e9f 100644 --- a/css/app.css +++ b/css/app.css @@ -1415,6 +1415,14 @@ div.combobox { height:18px; } +.background-control .layer-toggle-gpx .layer-extent { + display:none; +} + +.background-control .layer-toggle-gpx.selected .layer-extent { + display:inline-block; +} + /* Geocoder */ .geocode-control, .geocode-control form { @@ -1471,9 +1479,12 @@ div.combobox { background:#000; } -#surface, #tile-g { +#surface, #layer-g, .layer-layer { position:absolute; top:0; + left: 0; + right: 0; + bottom: 0; transform-origin:0 0; -ms-transform-origin:0 0; -webkit-transform-origin:0 0; @@ -1489,10 +1500,6 @@ div.combobox { position: static; } -#tile-g { - opacity: 0.5; -} - /* About Section ------------------------------------------------------- */ diff --git a/data/core.yaml b/data/core.yaml index c8f94ba1b..742a06c90 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -184,3 +184,6 @@ en: out: Zoom Out imagery: provided_by: "Imagery provided by {source}" + gpx: + local_layer: "Local GPX file" + drag_drop: "Drag and drop a .gpx file on the page" diff --git a/js/id/renderer/localgpx.js b/js/id/renderer/localgpx.js index 65419bd20..cd28f6fd5 100644 --- a/js/id/renderer/localgpx.js +++ b/js/id/renderer/localgpx.js @@ -1,7 +1,8 @@ -iD.LocalGpx = function() { +iD.LocalGpx = function(context) { var tileSize = 256, projection, gj = {}, + enable = true, size = [0, 0], transformProp = iD.util.prefixCSSProperty('Transform'), path = d3.geo.path().projection(projection), @@ -12,7 +13,7 @@ iD.LocalGpx = function() { path.projection(projection); var surf = selection.selectAll('svg') - .data([gj]); + .data(enable ? [gj] : []); surf.exit().remove(); @@ -43,17 +44,30 @@ iD.LocalGpx = function() { return render; }; + render.enable = function(_) { + if (!arguments.length) return enable; + enable = _; + return render; + }; + + render.geojson = function(_) { + if (!arguments.length) return gj; + gj = _; + return render; + }; + render.size = function(_) { if (!arguments.length) return size; size = _; return render; }; + render.id = 'layer-gpx'; + function over() { d3.event.stopPropagation(); d3.event.preventDefault(); d3.event.dataTransfer.dropEffect = 'copy'; - console.log('here'); } d3.select('body') @@ -65,7 +79,9 @@ iD.LocalGpx = function() { reader = new FileReader(); reader.onload = function(e) { - gj = toGeoJSON.gpx(toDom(e.target.result)); + render.geojson(toGeoJSON.gpx(toDom(e.target.result))); + context.redraw(); + context.map().pan([0, 0]); }; reader.readAsText(f); diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 1088b4c55..4651b764e 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -13,7 +13,7 @@ iD.Map = function(context) { minzoom = 0, layers = [ iD.Background().projection(projection), - iD.LocalGpx().projection(projection)], + iD.LocalGpx(context).projection(projection)], transformProp = iD.util.prefixCSSProperty('Transform'), points = iD.svg.Points(roundedProjection, context), vertices = iD.svg.Vertices(roundedProjection, context), @@ -31,7 +31,7 @@ iD.Map = function(context) { selection.call(zoom); layergroup = selection.append('div') - .attr('id', 'layers-g'); + .attr('id', 'layer-g'); var supersurface = selection.append('div') .style('position', 'absolute'); @@ -166,13 +166,13 @@ iD.Map = function(context) { if (!difference) { var sel = layergroup - .selectAll('.tile-layer-group') + .selectAll('.layer-layer') .data(layers); sel.exit().remove(); sel.enter().append('div') - .attr('class', 'tile-layer-group'); + .attr('class', 'layer-layer'); sel.each(function(layer) { d3.select(this).call(layer); diff --git a/js/id/ui/background.js b/js/id/ui/background.js index f0b3eeab0..b863742a7 100644 --- a/js/id/ui/background.js +++ b/js/id/ui/background.js @@ -1,9 +1,13 @@ iD.ui.Background = function(context) { var event = d3.dispatch('cancel', 'save'), key = 'b', - opacities = [1, 0.5, 0]; - - var layers = context.backgroundSources(); + opacities = [1, 0.5, 0], + directions = [ + ['left', [1, 0]], + ['top', [0, -1]], + ['right', [-1, 0]], + ['bottom', [0, 1]]], + layers = context.backgroundSources(); function getSources() { var ext = context.map().extent(); @@ -15,24 +19,6 @@ iD.ui.Background = function(context) { function background(selection) { - var content = selection.append('div') - .attr('class', 'content fillD map-overlay hide'), - shown = false; - - var tooltip = bootstrap.tooltip() - .placement('right') - .html(true) - .title(iD.ui.tooltipHtml(t('background.description'), key)); - - var button = selection.append('button') - .attr('tabindex', -1) - .attr('class', 'fillD') - .on('click.background-toggle', toggle) - .call(tooltip); - - button.append('span') - .attr('class', 'layers icon'); - function toggle() { tooltip.hide(button); setVisible(content.classed('hide')); @@ -55,56 +41,23 @@ iD.ui.Background = function(context) { } } - context.surface().on('mousedown.background-outside', function() { - setVisible(false); - }); - - context.container().on('mousedown.background-outside', function() { - setVisible(false); - }); - - var opa = content - .append('div') - .attr('class', 'opacity-options-wrapper'); - - opa.append('h4') - .text(t('background.title')); - - var opacityList = opa.append('ul') - .attr('class', 'opacity-options'); - function setOpacity(d) { - context.map().tilesurface + context.map().layersurface.selectAll('.layer-layer') + .filter(function(d) { return d == context.map().layers[0]; }) .transition() .style('opacity', d) .attr('data-opacity', d); + opacityList.selectAll('li') .classed('selected', false); - d3.select(this) - .classed('selected', true); + + if (d3.event) { + d3.select(this) + .classed('selected', true); + } } - opacityList.selectAll('div.opacity') - .data(opacities) - .enter() - .append('li') - .attr('data-original-title', function(d) { - return t('background.percent_brightness', { opacity: (d * 100) }); - }) - .on('click.set-opacity', setOpacity) - .html("
") - .call(bootstrap.tooltip() - .placement('top')) - .append('div') - .attr('class', 'opacity') - .style('opacity', String); - - // Make sure there is an active selection by default - opa.select('.opacity-options li:nth-child(2)') - .classed('selected', true); - function selectLayer(d) { - content.selectAll('a.layer') .classed('selected', function(d) { return d.data.name === context.background().source().data.name; @@ -135,9 +88,16 @@ iD.ui.Background = function(context) { selectLayer(d); } - var layerList = content - .append('ul') - .attr('class', 'toggle-list fillL'); + function clickGpx(d) { + d3.event.preventDefault(); + if (!_.isEmpty(context.map().layers[1].geojson())) { + context.map().layers[1] + .enable(!context.map().layers[1].enable()); + d3.select(this) + .classed('selected', context.map().layers[1].enable()); + context.redraw(); + } + } function update() { var layerLinks = layerList.selectAll('a.layer') @@ -169,26 +129,121 @@ iD.ui.Background = function(context) { return d.data.name; }); + gpxLayerItem + .classed('selected', function() { + var gpxLayer = context.map().layers[1]; + return !_.isEmpty(gpxLayer.geojson()) && + gpxLayer.enable(); + }); + layerLinks.exit() .remove(); selectLayer(context.background().source()); } - context.map().on('move.background-update', _.debounce(update, 1000)); + function clickNudge(d) { + var interval = window.setInterval(nudge, 100); - update(); + d3.select(this).on('mouseup', function() { + window.clearInterval(interval); + nudge(); + }); + + function nudge() { + context.background().nudge(d[1], context.map().zoom()); + context.redraw(); + } + } + + var content = selection.append('div') + .attr('class', 'content fillD map-overlay hide'), + tooltip = bootstrap.tooltip() + .placement('right') + .html(true) + .title(iD.ui.tooltipHtml(t('background.description'), key)), + button = selection.append('button') + .attr('tabindex', -1) + .attr('class', 'fillD') + .on('click.background-toggle', toggle) + .call(tooltip), + opa = content + .append('div') + .attr('class', 'opacity-options-wrapper'), + shown = false; + + button.append('span') + .attr('class', 'layers icon'); + + opa.append('h4') + .text(t('background.title')); + + context.surface().on('mousedown.background-outside', function() { + setVisible(false); + }); + + context.container().on('mousedown.background-outside', function() { + setVisible(false); + }); + + var opacityList = opa.append('ul') + .attr('class', 'opacity-options'); + + opacityList.selectAll('div.opacity') + .data(opacities) + .enter() + .append('li') + .attr('data-original-title', function(d) { + return t('background.percent_brightness', { opacity: (d * 100) }); + }) + .on('click.set-opacity', setOpacity) + .html("
") + .call(bootstrap.tooltip() + .placement('top')) + .append('div') + .attr('class', 'opacity') + .style('opacity', String); + + // Make sure there is an active selection by default + opa.select('.opacity-options li:nth-child(2)') + .classed('selected', true); + + var layerList = content + .append('ul') + .attr('class', 'toggle-list fillL'); + + var gpxLayerItem = content + .append('ul') + .attr('class', 'toggle-list fillL') + .append('li') + .append('a') + .classed('layer-toggle-gpx', true) + .call(bootstrap.tooltip() + .title(t('gpx.drag_drop')) + .placement('right')) + .on('click.set-gpx', clickGpx); + + gpxLayerItem + .append('span') + .attr('class', 'icon toggle'); + + gpxLayerItem.append('span') + .text(t('gpx.local_layer')); + + gpxLayerItem + .append('a') + .attr('class', 'icon geocode layer-extent') + .on('click', function() { + d3.event.preventDefault(); + d3.event.stopPropagation(); + context.map() + .extent(d3.geo.bounds(context.map().layers[1].geojson())); + }); var adjustments = content .append('div') .attr('class', 'adjustments pad1'); - var directions = [ - ['left', [1, 0]], - ['top', [0, -1]], - ['right', [-1, 0]], - ['bottom', [0, 1]]]; - adjustments.append('a') .text(t('background.fix_misalignment')) .attr('href', '#') @@ -197,8 +252,7 @@ iD.ui.Background = function(context) { .on('click', function() { var exp = d3.select(this).classed('expanded'); nudge_container.style('display', exp ? 'none' : 'block'); - d3.select(this) - .classed('expanded', !exp); + d3.select(this).classed('expanded', !exp); d3.event.preventDefault(); }); @@ -212,20 +266,7 @@ iD.ui.Background = function(context) { .append('button') .attr('class', function(d) { return d[0] + ' nudge'; }) .text(function(d) { return d[0]; }) - .on('mousedown', function(d) { - - var interval = window.setInterval(nudge, 100); - - d3.select(this).on('mouseup', function() { - window.clearInterval(interval); - nudge(); - }); - - function nudge() { - context.background().nudge(d[1], context.map().zoom()); - context.redraw(); - } - }); + .on('mousedown', clickNudge); nudge_container.append('button') .text(t('background.reset')) @@ -235,8 +276,12 @@ iD.ui.Background = function(context) { context.redraw(); }); - var keybinding = d3.keybinding('background'); + context.map() + .on('move.background-update', _.debounce(update, 1000)); + update(); + setOpacity(0.5); + var keybinding = d3.keybinding('background'); keybinding.on(key, toggle); d3.select(document)