diff --git a/css/app.css b/css/app.css index 39a6b2c76..c2cb031d4 100644 --- a/css/app.css +++ b/css/app.css @@ -1800,7 +1800,7 @@ img.wiki-image { padding-bottom: 10px; } -.layer-list { +.layer-list, .controls-list { margin-bottom: 10px; border: 1px solid #CCC; border-radius: 4px; @@ -1850,6 +1850,22 @@ img.wiki-image { text-overflow: ellipsis; } +.minimap-toggle { + display: block; + padding: 5px 10px; + cursor: pointer; + color: #7092FF; + border-radius: 3px; +} + +.minimap-toggle.active { + background: #E8EBFF; +} + +.minimap-toggle:hover { + background-color: #ececec; +} + .hide-toggle { display: block; padding-left:12px; @@ -2151,13 +2167,24 @@ img.wiki-image { user-select: none; } -.map-in-map-svg { - position: relative; +.map-in-map-svg, +.map-in-map-gpx { + top: 0; + left: 0; overflow: hidden; height: 100%; width: 100%; } +.map-in-map-svg { + position: absolute; +} + +.map-in-map-gpx { + position: relative; + z-index: 10; +} + .map-in-map-bbox { fill: none; stroke: rgba(255, 255, 0, 0.75); diff --git a/data/core.yaml b/data/core.yaml index ad4af61e7..953dd7df9 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -268,6 +268,9 @@ en: custom_prompt: "Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme." fix_misalignment: Fix alignment reset: reset + minimap: + description: Minimap + tooltip: Show a zoomed out map to help locate the area currently displayed. map_data: title: Map Data description: Map Data diff --git a/dist/locales/en.json b/dist/locales/en.json index f50c68a23..58c1b2526 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -323,7 +323,11 @@ "custom_button": "Edit custom background", "custom_prompt": "Enter a tile URL template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.", "fix_misalignment": "Fix alignment", - "reset": "reset" + "reset": "reset", + "minimap": { + "description": "Minimap", + "tooltip": "Show a zoomed out map to help locate the area currently displayed." + } }, "map_data": { "title": "Map Data", diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index e274891ca..1cf3c29bb 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -142,6 +142,7 @@ iD.Background = function(context) { 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(); }; @@ -167,6 +168,7 @@ iD.Background = function(context) { background.toggleGpxLayer = function() { gpxLayer.enable(!gpxLayer.enable()); + iD.ui.MapInMap.gpxLayer.enable(!iD.ui.MapInMap.gpxLayer.enable()); dispatch.change(); }; @@ -264,6 +266,7 @@ iD.Background = function(context) { 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(); } }); diff --git a/js/id/ui/background.js b/js/id/ui/background.js index 73c3f0bbd..5507002ce 100644 --- a/js/id/ui/background.js +++ b/js/id/ui/background.js @@ -252,6 +252,28 @@ iD.ui.Background = function(context) { var overlayList = content.append('ul') .attr('class', 'layer-list'); + var controls = content.append('div') + .attr('class', 'controls-list'); + + var minimapLabel = controls + .append('label') + .call(bootstrap.tooltip() + .html(true) + .title(iD.ui.tooltipHtml(t('background.minimap.tooltip'), '/')) + .placement('top') + ); + + minimapLabel.classed('minimap-toggle', true) + .append('input') + .attr('type', 'checkbox') + .on('change', function() { + iD.ui.MapInMap.toggle(); + d3.event.preventDefault(); + }) + + minimapLabel.append('span') + .text(t('background.minimap.description')); + var adjustments = content.append('div') .attr('class', 'adjustments'); diff --git a/js/id/ui/map_in_map.js b/js/id/ui/map_in_map.js index 385807854..27cbc232c 100644 --- a/js/id/ui/map_in_map.js +++ b/js/id/ui/map_in_map.js @@ -2,7 +2,10 @@ iD.ui.MapInMap = function(context) { var key = '/'; function map_in_map(selection) { + var backgroundLayer = iD.TileLayer(), + dispatch = d3.dispatch('change'), + gpxLayer = iD.GpxLayer(context, dispatch), overlayLayer = iD.TileLayer(), projection = iD.geo.RawMercator(), zoom = d3.behavior.zoom() @@ -11,7 +14,9 @@ iD.ui.MapInMap = function(context) { transformed = false, panning = false, zDiff = 6, // by default, minimap renders at (main zoom - 6) - tStart, tLast, tCurr, kLast, kCurr, tiles, svg, timeoutId; + tStart, tLast, tCurr, kLast, kCurr, tiles, svg, gpx, timeoutId; + + iD.ui.MapInMap.gpxLayer = gpxLayer; function ztok(z) { return 256 * Math.pow(2, z); } function ktoz(k) { return Math.log(k) / Math.LN2 - 8; } @@ -50,6 +55,7 @@ iD.ui.MapInMap = function(context) { iD.util.setTransform(tiles, tX, tY, scale); iD.util.setTransform(svg, 0, 0, scale); + iD.util.setTransform(gpx, 0, 0, scale); transformed = true; queueRedraw(); @@ -111,6 +117,7 @@ iD.ui.MapInMap = function(context) { if (transformed) { iD.util.setTransform(tiles, 0, 0); iD.util.setTransform(svg, 0, 0); + iD.util.setTransform(gpx, 0, 0); transformed = false; } } @@ -134,7 +141,6 @@ iD.ui.MapInMap = function(context) { .append('div') .attr('class', 'map-in-map-tiles'); - // redraw background backgroundLayer .source(context.background().baseLayerSource()) @@ -184,6 +190,20 @@ iD.ui.MapInMap = function(context) { .call(overlayLayer); } + gpxLayer + .projection(projection); + + gpx = tiles + .selectAll('.map-in-map-gpx') + .data([0]); + + gpx.enter() + .append('div') + .attr('class', 'map-in-map-gpx'); + + gpx.call(gpxLayer); + gpx.dimensions(dMini); + // redraw bounding box if (!panning) { var getPath = d3.geo.path().projection(projection), @@ -224,6 +244,8 @@ iD.ui.MapInMap = function(context) { function toggle() { if (d3.event) d3.event.preventDefault(); + var label = d3.select('.minimap-toggle'); + if (hidden()) { selection .style('display', 'block') @@ -232,6 +254,9 @@ iD.ui.MapInMap = function(context) { .duration(200) .style('opacity', 1); + label.classed('active', true) + .select('input').property('checked', true); + redraw(); } else { @@ -244,9 +269,13 @@ iD.ui.MapInMap = function(context) { .each('end', function() { d3.select(this).style('display', 'none'); }); + + label.classed('active', false) + .select('input').property('checked', false); } } + iD.ui.MapInMap.toggle = toggle; selection .on('mousedown.map-in-map', startMouse)