Merge pull request #4575 from openstreetmap/background_pane

Enhancements to Background, Map Data, Help panes
This commit is contained in:
Bryan Housel
2018-01-03 15:32:18 -05:00
committed by GitHub
17 changed files with 1508 additions and 1245 deletions

View File

@@ -130,7 +130,6 @@ a, button, input, textarea {
a,
button,
.checkselect label:hover,
.opacity-options li,
.radial-menu-item {
cursor: pointer;
}
@@ -268,7 +267,7 @@ table th {
}
table.tags, table.tags td, table.tags th {
border: 1px solid #CCC;
border: 1px solid #ccc;
padding: 4px;
}
@@ -304,7 +303,7 @@ ul li { list-style: none;}
display: block;
height: 30px;
background-color: white;
color: #7092FF;
color: #7092ff;
cursor: pointer;
}
@@ -740,6 +739,30 @@ button.save.has-count .count::before {
position: absolute;
}
/* Hide-Toggle
------------------------------------------------------- */
.hide-toggle .icon.pre-text {
vertical-align: text-top;
width: 16px;
height: 16px;
margin-left: -3px;
}
[dir='rtl'] .hide-toggle .icon.pre-text {
margin-left: 0;
margin-right: -3px;
}
a:visited.hide-toggle,
a.hide-toggle {
display: inline-block;
font-size: 14px;
font-weight: bold;
padding-bottom: 5px;
}
/* Inspector
------------------------------------------------------- */
@@ -782,7 +805,6 @@ button.save.has-count .count::before {
bottom: 0;
}
.feature-list-pane .inspector-body {
top: 120px;
}
@@ -1071,7 +1093,7 @@ button.save.has-count .count::before {
.preset-list-item button.tag-reference-button {
height: 100%;
border: 1px solid #CCC;
border: 1px solid #ccc;
border-radius: 0 3px 3px 0;
position: absolute;
top: 0;
@@ -1143,7 +1165,7 @@ button.save.has-count .count::before {
}
.preset-editor a.hide-toggle {
margin: 0 20px 10px 20px;
margin: 0 20px 5px 20px;
}
.preset-editor .form-fields-container {
@@ -1218,7 +1240,7 @@ button.save.has-count .count::before {
}
[dir='rtl'] .form-label button {
border-left: none;
border-right: 1px solid #CCC;
border-right: 1px solid #ccc;
border-radius: 4px 0 0 0;
width: 31px;
}
@@ -1574,13 +1596,13 @@ input[type=number] {
float: left;
height: 100%;
width: 32px;
border-left: 1px solid #CCC;
border-left: 1px solid #ccc;
border-radius: 0;
background: rgba(0, 0, 0, 0);
}
[dir='rtl'] .spin-control button{
border-left: 0;
border-right: 1px solid #CCC;
border-right: 1px solid #ccc;
}
.spin-control button.decrement {
@@ -1604,13 +1626,13 @@ input[type=number] {
}
.spin-control button.decrement::after {
border-top: 5px solid #CCC;
border-top: 5px solid #ccc;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
.spin-control button.increment::after {
border-bottom: 5px solid #CCC;
border-bottom: 5px solid #ccc;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
@@ -1622,7 +1644,7 @@ input[type=number] {
display: block;
background: white;
padding: 5px 10px;
color: #7092FF;
color: #7092ff;
}
.checkselect label:hover {
@@ -1720,7 +1742,7 @@ input[type=number] {
right: 1px;
width: 32px;
margin-left: -32px;
border: 1px solid #CCC;
border: 1px solid #ccc;
border-top-width: 0;
border-right-width: 0;
border-radius: 0 0 4px 0;
@@ -1938,12 +1960,12 @@ div.combobox {
height: 31px;
border: 0;
border-radius: 0;
border-bottom: 1px solid #CCC;
border-left: 1px solid #CCC;
border-bottom: 1px solid #ccc;
border-left: 1px solid #ccc;
}
[dir='rtl'] .tag-row input {
border-left: none;
border-right: 1px solid #CCC;
border-right: 1px solid #ccc;
}
.tag-row .key-wrap,
@@ -1963,14 +1985,14 @@ div.combobox {
}
.tag-row input.value {
border-right: 1px solid #CCC;
border-right: 1px solid #ccc;
}
[dir='rtl'] .tag-row input.value {
border-left: 1px solid #CCC;
border-left: 1px solid #ccc;
}
.tag-row:first-child input.key {
border-top: 1px solid #CCC;
border-top: 1px solid #ccc;
border-top-left-radius: 4px;
}
[dir='rtl'] .tag-row:first-child input.key {
@@ -1979,14 +2001,14 @@ div.combobox {
}
.tag-row:first-child input.value {
border-top: 1px solid #CCC;
border-top: 1px solid #ccc;
}
.tag-row button {
position: absolute;
height: 31px;
right: 10%;
border: 1px solid #CCC;
border: 1px solid #ccc;
border-top-width: 0;
border-left-width: 0;
}
@@ -2246,16 +2268,12 @@ div.full-screen > button:hover {
.imagery-faq {
margin-bottom: 10px;
}
.map-data-control .hide-toggle,
.background-control .hide-toggle {
padding-bottom: 10px;
white-space: nowrap;
}
.layer-list, .controls-list {
margin-bottom: 10px;
border: 1px solid #CCC;
border: 1px solid #ccc;
border-radius: 4px;
}
@@ -2263,7 +2281,7 @@ div.full-screen > button:hover {
position: relative;
height: 30px;
background-color: white;
color: #7092FF;
color: #7092ff;
}
.layer-list:empty {
@@ -2292,7 +2310,7 @@ div.full-screen > button:hover {
.layer-list li.active,
.layer-list li.switch {
background: #E8EBFF;
background: #e8ebff;
}
.layer-list li.best > div.best {
@@ -2324,60 +2342,38 @@ div.full-screen > button:hover {
text-overflow: ellipsis;
}
.minimap-toggle {
display: block;
padding: 5px 10px;
cursor: pointer;
color: #7092FF;
border-radius: 3px;
/* Background Display Options */
.display-options-container {
padding: 10px;
}
.minimap-toggle.active {
background: #E8EBFF;
.display-control h5 {
padding-bottom: 0;
padding-top: 10px;
}
.minimap-toggle:hover {
background-color: #ececec;
.display-control h5 span {
margin: 5px;
}
.hide-toggle {
display: block;
padding-left: 12px;
position: relative;
}
[dir='rtl'] .hide-toggle {
padding-left: 0;
padding-right: 12px;
.display-control .display-option-input {
height: 20px;
width: 160px;
}
.hide-toggle:before {
content: '';
display: block;
position: absolute;
height: 0;
width: 0;
left: 0;
top: 5px;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 8px solid #7092ff;
.display-control button {
height: 30px;
width: 30px;
margin-left: 5px;
margin-right: 0px;
vertical-align: text-bottom;
border-radius: 4px;
}
[dir='rtl'] .hide-toggle:before {
left: auto;
right: 0;
border-left: none;
border-right: 8px solid #7092ff;
}
.hide-toggle.expanded:before {
border-top: 8px solid #7092ff;
border-bottom: 0;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
}
[dir='rtl'] .hide-toggle.expanded:before {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
[dir='rtl'] .display-control button {
margin-left: 0px;
margin-right: 5px;
}
@@ -2433,7 +2429,7 @@ div.full-screen > button:hover {
}
.nudge-container input.error {
border: 1px solid #FF7878;
border: 1px solid #ff7878;
border-radius: 2px;
background: #ffb;
}
@@ -2485,92 +2481,43 @@ div.full-screen > button:hover {
}
.background-control .nudge.right::after {
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid #222;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid #222;
}
.background-control .nudge.left::after {
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-right: 5px solid #222;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-right: 5px solid #222;
}
.background-control .nudge.top::after {
border-right: 5px solid transparent;
border-left: 5px solid transparent;
border-bottom: 5px solid #222;
border-right: 5px solid transparent;
border-left: 5px solid transparent;
border-bottom: 5px solid #222;
}
.background-control .nudge.bottom::after {
border-right: 5px solid transparent;
border-left: 5px solid transparent;
border-top: 5px solid #222;
border-right: 5px solid transparent;
border-left: 5px solid transparent;
border-top: 5px solid #222;
}
.opacity-options {
background: url(img/background-pattern-opacity.png) 0 0 repeat;
height: 20px;
width: 82px;
position: absolute;
right: 50px;
top: 20px;
border: 1px solid #ccc;
}
[dir='rtl'] .opacity-options {
left: 50px;
right: auto;
}
.opacity-options li {
height: 100%;
display: block;
float: left;
}
.opacity-options li .select-box{
position: absolute;
width: 20px;
height: 18px;
z-index: 9999;
}
.map-data-control li:hover .select-box,
.map-data-control li.selected .select-box,
.background-control li:hover .select-box,
.background-control li.selected .select-box {
border: 2px solid #7092ff;
background: rgba(89, 123, 231, .5);
opacity: .5;
}
.map-data-control li.selected:hover .select-box,
.map-data-control li.selected .select-box,
.background-control li.selected:hover .select-box,
.background-control li.selected .select-box {
opacity: 1;
}
.background-control .opacity {
background:#222;
display:inline-block;
width:20px;
height:18px;
}
.map-data-control .layer-list button,
.background-control .layer-list button {
float: right;
height: 100%;
width: 10%;
border-left: 1px solid #CCC;
border-left: 1px solid #ccc;
border-radius: 0;
}
[dir='rtl'] .map-data-control .layer-list button,
[dir='rtl'] .background-control .layer-list button {
float: left;
border-left: none;
border-right: 1px solid #CCC;
border-right: 1px solid #ccc;
}
.map-data-control .layer-list button .icon,
@@ -2603,12 +2550,12 @@ div.full-screen > button:hover {
border-radius: 0 0 0 4px;
}
[dir='rtl'] .geolocate-control button {
border-radius: 0 0 4px 0;
border-radius: 0 0 4px 0;
}
.map-overlay.content {
position: fixed;
top:60px;
top: 60px;
bottom: 30px;
padding: 20px 50px 20px 20px;
right: 0;
@@ -2620,13 +2567,17 @@ div.full-screen > button:hover {
right: auto !important;
}
.map-overlay.content > div {
padding-bottom: 15px;
}
/* Help */
.help-control button {
border-radius: 0 0 0 4px;
}
[dir='rtl'] .help-control button {
border-radius: 0 0 4px 0;
border-radius: 0 0 4px 0;
}
.help-wrap p {
@@ -2655,7 +2606,7 @@ div.full-screen > button:hover {
}
.help-wrap .toc {
width:40%;
width: 40%;
float:right;
margin-left: 20px;
margin-bottom: 20px;
@@ -2732,12 +2683,12 @@ div.full-screen > button:hover {
------------------------------------------------------- */
img.tile {
position:absolute;
transform-origin:0 0;
-ms-transform-origin:0 0;
-webkit-transform-origin:0 0;
-moz-transform-origin:0 0;
-o-transform-origin:0 0;
position: absolute;
transform-origin: 0 0;
-ms-transform-origin: 0 0;
-webkit-transform-origin: 0 0;
-moz-transform-origin: 0 0;
-o-transform-origin: 0 0;
-moz-user-select: none;
-webkit-user-select: none;
@@ -2747,8 +2698,16 @@ img.tile {
opacity: 0;
-webkit-transition: opacity 200ms linear;
transition: opacity 200ms linear;
-moz-transition: opacity 200ms linear;
transition: opacity 200ms linear;
}
img.tile-loaded {
opacity: 1;
}
img.tile-removing {
opacity: 0;
}
.tile-label-debug {
@@ -2763,11 +2722,11 @@ img.tile {
margin-left: -70px;
margin-top: -20px;
transform-origin:0 0;
-ms-transform-origin:0 0;
-webkit-transform-origin:0 0;
-moz-transform-origin:0 0;
-o-transform-origin:0 0;
transform-origin: 0 0;
-ms-transform-origin: 0 0;
-webkit-transform-origin: 0 0;
-moz-transform-origin: 0 0;
-o-transform-origin: 0 0;
-moz-user-select: none;
-webkit-user-select: none;
@@ -2779,23 +2738,15 @@ img.tile-debug {
outline: 1px solid red;
}
img.tile-loaded {
opacity: 1;
}
img.tile-removing {
opacity: 0;
}
/* Map
------------------------------------------------------- */
#map {
position:relative;
overflow:hidden;
height:100%;
background:#000;
position: relative;
overflow: hidden;
height: 100%;
background: #000;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
@@ -2803,11 +2754,11 @@ img.tile-removing {
}
#supersurface {
transform-origin:0 0;
-ms-transform-origin:0 0;
-webkit-transform-origin:0 0;
-moz-transform-origin:0 0;
-o-transform-origin:0 0;
transform-origin: 0 0;
-ms-transform-origin: 0 0;
-webkit-transform-origin: 0 0;
-moz-transform-origin: 0 0;
-o-transform-origin: 0 0;
}
#supersurface, .layer {
@@ -3368,7 +3319,7 @@ img.tile-removing {
.modal-section {
padding: 20px;
border-bottom: 1px solid #CCC;
border-bottom: 1px solid #ccc;
}
.modal-section.header h3 {
@@ -3401,8 +3352,8 @@ img.tile-removing {
.modal-actions button,
.save-success a.button {
font-weight: normal;
color: #7092FF;
border-bottom: 1px solid #CCC;
color: #7092ff;
border-bottom: 1px solid #ccc;
border-radius: 0;
height: 160px;
text-align: center;
@@ -3422,7 +3373,7 @@ img.tile-removing {
}
.modal-actions > :first-child {
border-right: 1px solid #CCC;
border-right: 1px solid #ccc;
}
.modal-section:last-child {
@@ -3432,7 +3383,7 @@ img.tile-removing {
/* Restore Modal
------------------------------------------------------- */
.modal-actions .logo-restore {
color: #7092FF;
color: #7092ff;
}
.modal-actions .logo-reset {
color: #E06C5E;
@@ -3450,7 +3401,7 @@ img.tile-removing {
padding-top: 15px;
}
.save-success .logo-osm {
color: #7092FF;
color: #7092ff;
margin-bottom: 10px;
}
.save-success a.button.social {
@@ -3460,14 +3411,14 @@ img.tile-removing {
.save-success .icon.social {
height: 80px;
width: 80px;
color: #7092FF;
color: #7092ff;
}
/* Splash Modal
------------------------------------------------------- */
.modal-actions .logo-walkthrough,
.modal-actions .logo-features {
color: #7092FF;
color: #7092ff;
}
@@ -3658,7 +3609,7 @@ svg.mouseclick use.right {
}
.mode-save .commit-section .changeset-list button {
border-left: 1px solid #CCC;
border-left: 1px solid #ccc;
}
.changeset-list li span.count:before { content: '('; }
@@ -4215,7 +4166,7 @@ li.hide + li.version .badge .tooltip .tooltip-arrow {
.curtain-tooltip .tooltip-inner .instruction {
font-weight: bold;
display: block;
border-top: 1px solid #CCC;
border-top: 1px solid #ccc;
margin-top: 10px;
margin-left: -20px;
margin-right: -20px;
@@ -4293,5 +4244,5 @@ li.hide + li.version .badge .tooltip .tooltip-arrow {
.huge-modal-button .illustration {
height: 100px;
width: 100px;
color: #7092FF;
color: #7092ff;
}

View File

@@ -375,21 +375,27 @@ en:
title: Background
description: Background settings
key: B
percent_brightness: "{opacity}% brightness"
backgrounds: Backgrounds
none: None
best_imagery: Best known imagery source for this location
switch: Switch back to this background
custom: Custom
custom_button: Edit custom background
custom_prompt: "Enter a tile URL template. Valid tokens are:\n - {zoom}/{z}, {x}, {y} for Z/X/Y tile scheme\n - {ty} for flipped TMS-style Y coordinates\n - {u} for quadtile scheme\n - {switch:a,b,c} for DNS server multiplexing\n\nExample:\n{example}"
fix_misalignment: Adjust imagery offset
imagery_source_faq: Where does this imagery come from?
overlays: Overlays
imagery_source_faq: Imagery Info / Report a Problem
reset: reset
offset: "Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters."
display_options: Display Options
brightness: Brightness
contrast: Contrast
saturation: Saturation
sharpness: Sharpness
minimap:
description: Minimap
description: Show Minimap
tooltip: Show a zoomed out map to help locate the area currently displayed.
key: '/'
fix_misalignment: Adjust imagery offset
offset: "Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters."
map_data:
title: Map Data
description: Map Data

18
dist/locales/en.json vendored
View File

@@ -464,22 +464,28 @@
"title": "Background",
"description": "Background settings",
"key": "B",
"percent_brightness": "{opacity}% brightness",
"backgrounds": "Backgrounds",
"none": "None",
"best_imagery": "Best known imagery source for this location",
"switch": "Switch back to this background",
"custom": "Custom",
"custom_button": "Edit custom background",
"custom_prompt": "Enter a tile URL template. Valid tokens are:\n - {zoom}/{z}, {x}, {y} for Z/X/Y tile scheme\n - {ty} for flipped TMS-style Y coordinates\n - {u} for quadtile scheme\n - {switch:a,b,c} for DNS server multiplexing\n\nExample:\n{example}",
"fix_misalignment": "Adjust imagery offset",
"imagery_source_faq": "Where does this imagery come from?",
"overlays": "Overlays",
"imagery_source_faq": "Imagery Info / Report a Problem",
"reset": "reset",
"offset": "Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters.",
"display_options": "Display Options",
"brightness": "Brightness",
"contrast": "Contrast",
"saturation": "Saturation",
"sharpness": "Sharpness",
"minimap": {
"description": "Minimap",
"description": "Show Minimap",
"tooltip": "Show a zoomed out map to help locate the area currently displayed.",
"key": "/"
}
},
"fix_misalignment": "Adjust imagery offset",
"offset": "Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters."
},
"map_data": {
"title": "Map Data",

View File

@@ -1,6 +1,7 @@
import _find from 'lodash-es/find';
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { interpolateNumber as d3_interpolateNumber } from 'd3-interpolate';
import { select as d3_select } from 'd3-selection';
import { data } from '../../data';
@@ -12,24 +13,83 @@ import { utilRebind } from '../util/rebind';
export function rendererBackground(context) {
var dispatch = d3_dispatch('change'),
baseLayer = rendererTileLayer(context).projection(context.projection),
overlayLayers = [],
backgroundSources;
var dispatch = d3_dispatch('change');
var baseLayer = rendererTileLayer(context).projection(context.projection);
var _overlayLayers = [];
var _backgroundSources = [];
var _brightness = 1;
var _contrast = 1;
var _saturation = 1;
var _sharpness = 1;
function background(selection) {
var baseFilter = '';
if (_brightness !== 1) {
baseFilter += 'brightness(' + _brightness + ')';
}
if (_contrast !== 1) {
baseFilter += 'contrast(' + _contrast + ')';
}
if (_saturation !== 1) {
baseFilter += 'saturate(' + _saturation + ')';
}
if (_sharpness < 1) { // gaussian blur
var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
baseFilter += 'blur(' + blur + 'px)';
}
var base = selection.selectAll('.layer-background')
.data([0]);
base.enter()
base = base.enter()
.insert('div', '.layer-data')
.attr('class', 'layer layer-background')
.merge(base)
.style('filter', baseFilter || null);
var imagery = base.selectAll('.layer-imagery')
.data([0]);
imagery.enter()
.append('div')
.attr('class', 'layer layer-imagery')
.merge(imagery)
.call(baseLayer);
var maskFilter = '';
var mixBlendMode = '';
if (_sharpness > 1) { // apply unsharp mask
mixBlendMode = 'overlay';
maskFilter = 'saturate(0) blur(3px) invert(1)';
var contrast = _sharpness - 1;
maskFilter += ' contrast(' + contrast + ')';
var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
maskFilter += ' brightness(' + brightness + ')';
}
var mask = base.selectAll('.layer-unsharp-mask')
.data(_sharpness > 1 ? [0] : []);
mask.exit()
.remove();
mask.enter()
.append('div')
.attr('class', 'layer layer-mask layer-unsharp-mask')
.merge(mask)
.call(baseLayer)
.style('filter', maskFilter || null)
.style('mix-blend-mode', mixBlendMode || null);
var overlays = selection.selectAll('.layer-overlay')
.data(overlayLayers, function(d) { return d.source().name(); });
.data(_overlayLayers, function(d) { return d.source().name(); });
overlays.exit()
.remove();
@@ -46,7 +106,7 @@ export function rendererBackground(context) {
if (context.inIntro()) return;
var b = background.baseLayerSource(),
o = overlayLayers
o = _overlayLayers
.filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
.map(function (d) { return d.source().id; })
.join(','),
@@ -85,7 +145,7 @@ export function rendererBackground(context) {
var imageryUsed = [b.imageryUsed()];
overlayLayers
_overlayLayers
.filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
.forEach(function (d) { imageryUsed.push(d.source().imageryUsed()); });
@@ -117,7 +177,7 @@ export function rendererBackground(context) {
background.sources = function(extent) {
return backgroundSources.filter(function(source) {
return _backgroundSources.filter(function(source) {
return source.intersects(extent);
});
};
@@ -127,7 +187,7 @@ export function rendererBackground(context) {
if (!_) return;
baseLayer.dimensions(_);
overlayLayers.forEach(function(layer) {
_overlayLayers.forEach(function(layer) {
layer.dimensions(_);
});
};
@@ -172,7 +232,7 @@ export function rendererBackground(context) {
background.findSource = function(id) {
return _find(backgroundSources, function(d) {
return _find(_backgroundSources, function(d) {
return d.id && d.id === id;
});
};
@@ -185,22 +245,22 @@ export function rendererBackground(context) {
background.showsLayer = function(d) {
return d.id === baseLayer.source().id ||
overlayLayers.some(function(layer) { return d.id === layer.source().id; });
_overlayLayers.some(function(layer) { return d.id === layer.source().id; });
};
background.overlayLayerSources = function() {
return overlayLayers.map(function (l) { return l.source(); });
return _overlayLayers.map(function (l) { return l.source(); });
};
background.toggleOverlayLayer = function(d) {
var layer;
for (var i = 0; i < overlayLayers.length; i++) {
layer = overlayLayers[i];
for (var i = 0; i < _overlayLayers.length; i++) {
layer = _overlayLayers[i];
if (layer.source() === d) {
overlayLayers.splice(i, 1);
_overlayLayers.splice(i, 1);
dispatch.call('change');
background.updateImagery();
return;
@@ -210,9 +270,10 @@ export function rendererBackground(context) {
layer = rendererTileLayer(context)
.source(d)
.projection(context.projection)
.dimensions(baseLayer.dimensions());
.dimensions(baseLayer.dimensions()
);
overlayLayers.push(layer);
_overlayLayers.push(layer);
dispatch.call('change');
background.updateImagery();
};
@@ -235,6 +296,38 @@ export function rendererBackground(context) {
};
background.brightness = function(d) {
if (!arguments.length) return _brightness;
_brightness = d;
if (context.mode()) dispatch.call('change');
return background;
};
background.contrast = function(d) {
if (!arguments.length) return _contrast;
_contrast = d;
if (context.mode()) dispatch.call('change');
return background;
};
background.saturation = function(d) {
if (!arguments.length) return _saturation;
_saturation = d;
if (context.mode()) dispatch.call('change');
return background;
};
background.sharpness = function(d) {
if (!arguments.length) return _sharpness;
_sharpness = d;
if (context.mode()) dispatch.call('change');
return background;
};
background.init = function() {
function parseMap(qmap) {
if (!qmap) return false;
@@ -251,7 +344,7 @@ export function rendererBackground(context) {
best;
// Add all the available imagery sources
backgroundSources = dataImagery.map(function(source) {
_backgroundSources = dataImagery.map(function(source) {
if (source.type === 'bing') {
return rendererBackgroundSource.Bing(source, dispatch);
} else if (source.id === 'EsriWorldImagery') {
@@ -261,15 +354,15 @@ export function rendererBackground(context) {
}
});
first = backgroundSources.length && backgroundSources[0];
first = _backgroundSources.length && _backgroundSources[0];
// Add 'None'
backgroundSources.unshift(rendererBackgroundSource.None());
_backgroundSources.unshift(rendererBackgroundSource.None());
// Add 'Custom'
var template = context.storage('background-custom-template') || '';
var custom = rendererBackgroundSource.Custom(template);
backgroundSources.unshift(custom);
_backgroundSources.unshift(custom);
// Decide which background layer to display
@@ -290,7 +383,7 @@ export function rendererBackground(context) {
);
}
var locator = _find(backgroundSources, function(d) {
var locator = _find(_backgroundSources, function(d) {
return d.overlay && d.default;
});

View File

@@ -7,439 +7,237 @@ import {
import {
event as d3_event,
select as d3_select,
selectAll as d3_selectAll
select as d3_select
} from 'd3-selection';
import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js';
import { t, textDirection } from '../util/locale';
import { geoMetersToOffset, geoOffsetToMeters } from '../geo';
import { utilDetect } from '../util/detect';
import { utilSetTransform, utilCallWhenIdle } from '../util';
import { svgIcon } from '../svg';
import { uiMapInMap } from './map_in_map';
import { uiBackgroundDisplayOptions } from './background_display_options';
import { uiBackgroundOffset } from './background_offset';
import { uiCmd } from './cmd';
import { uiDisclosure } from './disclosure';
import { uiHelp } from './help';
import { uiMapData } from './map_data';
import { uiMapInMap } from './map_in_map';
import { uiTooltipHtml } from './tooltipHtml';
import { utilCallWhenIdle } from '../util';
import { tooltip } from '../util/tooltip';
export function uiBackground(context) {
var key = t('background.key'),
detected = utilDetect(),
opacities = [1, 0.75, 0.5, 0.25],
directions = [
['right', [0.5, 0]],
['top', [0, -0.5]],
['left', [-0.5, 0]],
['bottom', [0, 0.5]]],
opacityDefault = (context.storage('background-opacity') !== null) ?
(+context.storage('background-opacity')) : 1.0,
customSource = context.background().findSource('custom'),
previous;
var key = t('background.key');
// Can be 0 from <1.3.0 use or due to issue #1923.
if (opacityDefault === 0) opacityDefault = 1.0;
var _customSource = context.background().findSource('custom');
var _previousBackground;
var _shown = false;
var _backgroundList = d3_select(null);
var _overlayList = d3_select(null);
var _displayOptionsContainer = d3_select(null);
var _offsetContainer = d3_select(null);
var backgroundDisplayOptions = uiBackgroundDisplayOptions(context);
var backgroundOffset = uiBackgroundOffset(context);
function background(selection) {
function setTooltips(selection) {
selection.each(function(d, i, nodes) {
var item = d3_select(this).select('label');
var span = item.select('span');
var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
var description = d.description();
var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
if (d === _previousBackground) {
item.call(tooltip()
.placement(placement)
.html(true)
.title(function() {
var tip = '<div>' + t('background.switch') + '</div>';
return uiTooltipHtml(tip, uiCmd('⌘' + key));
})
);
} else if (description || isOverflowing) {
item.call(tooltip()
.placement(placement)
.title(description || d.name())
);
} else {
item.call(tooltip().destroy);
}
});
}
function updateLayerSelections(selection) {
function active(d) {
return context.background().showsLayer(d);
}
selection.selectAll('.layer')
.classed('active', active)
.classed('switch', function(d) { return d === _previousBackground; })
.call(setTooltips)
.selectAll('input')
.property('checked', active);
}
function chooseBackground(d) {
if (d.id === 'custom' && !d.template()) {
return editCustom();
}
d3_event.preventDefault();
_previousBackground = context.background().baseLayerSource();
context.background().baseLayerSource(d);
_backgroundList.call(updateLayerSelections);
document.activeElement.blur();
}
function editCustom() {
d3_event.preventDefault();
var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
var template = window.prompt(
t('background.custom_prompt', { example: example }),
_customSource.template() || example
);
if (template) {
context.storage('background-custom-template', template);
_customSource.template(template);
chooseBackground(_customSource);
} else {
_backgroundList.call(updateLayerSelections);
}
}
function chooseOverlay(d) {
d3_event.preventDefault();
context.background().toggleOverlayLayer(d);
_overlayList.call(updateLayerSelections);
document.activeElement.blur();
}
function drawListItems(layerList, type, change, filter) {
var sources = context.background()
.sources(context.map().extent())
.filter(filter);
var layerLinks = layerList.selectAll('li.layer')
.data(sources, function(d) { return d.name(); });
layerLinks.exit()
.remove();
var enter = layerLinks.enter()
.append('li')
.attr('class', 'layer')
.classed('layer-custom', function(d) { return d.id === 'custom'; })
.classed('best', function(d) { return d.best(); });
enter.filter(function(d) { return d.id === 'custom'; })
.append('button')
.attr('class', 'layer-browse')
.call(tooltip()
.title(t('background.custom_button'))
.placement((textDirection === 'rtl') ? 'right' : 'left')
)
.on('click', editCustom)
.call(svgIcon('#icon-search'));
enter.filter(function(d) { return d.best(); })
.append('div')
.attr('class', 'best')
.call(tooltip()
.title(t('background.best_imagery'))
.placement((textDirection === 'rtl') ? 'right' : 'left')
)
.append('span')
.html('&#9733;');
var label = enter
.append('label');
label
.append('input')
.attr('type', type)
.attr('name', 'layers')
.on('change', change);
label
.append('span')
.text(function(d) { return d.name(); });
layerList.selectAll('li.layer')
.sort(sortSources)
.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
layerList
.call(updateLayerSelections);
function sortSources(a, b) {
return a.best() && !b.best() ? -1
: b.best() && !a.best() ? 1
: d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
}
}
function setOpacity(d) {
var bg = context.container().selectAll('.layer-background')
.transition()
.style('opacity', d)
.attr('data-opacity', d);
function renderBackgroundList(selection) {
if (!detected.opera) {
utilSetTransform(bg, 0, 0);
}
// the background list
var container = selection.selectAll('.layer-background-list')
.data([0]);
opacityList.selectAll('li')
.classed('active', function(_) { return _ === d; });
context.storage('background-opacity', d);
}
_backgroundList = container.enter()
.append('ul')
.attr('class', 'layer-list layer-background-list')
.attr('dir', 'auto')
.merge(container);
function setTooltips(selection) {
selection.each(function(d, i, nodes) {
var item = d3_select(this).select('label'),
span = item.select('span'),
placement = (i < nodes.length / 2) ? 'bottom' : 'top',
description = d.description(),
isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
// add minimap toggle below list
var minimapEnter = selection.selectAll('.minimap-toggle-list')
.data([0])
.enter()
.append('ul')
.attr('class', 'layer-list minimap-toggle-list')
.append('li')
.attr('class', 'layer minimap-toggle-item');
if (d === previous) {
item.call(tooltip()
.placement(placement)
.html(true)
.title(function() {
var tip = '<div>' + t('background.switch') + '</div>';
return uiTooltipHtml(tip, uiCmd('⌘' + key));
})
);
} else if (description || isOverflowing) {
item.call(tooltip()
.placement(placement)
.title(description || d.name())
);
} else {
item.call(tooltip().destroy);
}
});
}
function selectLayer() {
function active(d) {
return context.background().showsLayer(d);
}
content.selectAll('.layer')
.classed('active', active)
.classed('switch', function(d) { return d === previous; })
.call(setTooltips)
.selectAll('input')
.property('checked', active);
}
function clickSetSource(d) {
if (d.id === 'custom' && !d.template()) {
return editCustom();
}
d3_event.preventDefault();
previous = context.background().baseLayerSource();
context.background().baseLayerSource(d);
selectLayer();
document.activeElement.blur();
}
function editCustom() {
d3_event.preventDefault();
var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
var template = window.prompt(
t('background.custom_prompt', { example: example }),
customSource.template() || example
var minimapLabelEnter = minimapEnter
.append('label')
.call(tooltip()
.html(true)
.title(uiTooltipHtml(t('background.minimap.tooltip'), t('background.minimap.key')))
.placement('top')
);
if (template) {
context.storage('background-custom-template', template);
customSource.template(template);
clickSetSource(customSource);
} else {
selectLayer();
}
}
function clickSetOverlay(d) {
d3_event.preventDefault();
context.background().toggleOverlayLayer(d);
selectLayer();
document.activeElement.blur();
}
function drawList(layerList, type, change, filter) {
var sources = context.background()
.sources(context.map().extent())
.filter(filter);
var layerLinks = layerList.selectAll('li.layer')
.data(sources, function(d) { return d.name(); });
layerLinks.exit()
.remove();
var enter = layerLinks.enter()
.append('li')
.attr('class', 'layer')
.classed('layer-custom', function(d) { return d.id === 'custom'; })
.classed('best', function(d) { return d.best(); });
enter.filter(function(d) { return d.id === 'custom'; })
.append('button')
.attr('class', 'layer-browse')
.call(tooltip()
.title(t('background.custom_button'))
.placement((textDirection === 'rtl') ? 'right' : 'left'))
.on('click', editCustom)
.call(svgIcon('#icon-search'));
enter.filter(function(d) { return d.best(); })
.append('div')
.attr('class', 'best')
.call(tooltip()
.title(t('background.best_imagery'))
.placement((textDirection === 'rtl') ? 'right' : 'left'))
.append('span')
.html('&#9733;');
var label = enter
.append('label');
label
.append('input')
.attr('type', type)
.attr('name', 'layers')
.on('change', change);
label
.append('span')
.text(function(d) { return d.name(); });
layerList.selectAll('li.layer')
.sort(sortSources)
.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
}
function update() {
backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.isHidden() && !d.overlay; });
overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return !d.isHidden() && d.overlay; });
selectLayer();
updateOffsetVal();
}
function updateOffsetVal() {
var meters = geoOffsetToMeters(context.background().offset()),
x = +meters[0].toFixed(2),
y = +meters[1].toFixed(2);
d3_selectAll('.nudge-inner-rect')
.select('input')
.classed('error', false)
.property('value', x + ', ' + y);
d3_selectAll('.nudge-reset')
.classed('disabled', function() {
return (x === 0 && y === 0);
});
}
function resetOffset() {
if (d3_event.button !== 0) return;
context.background().offset([0, 0]);
updateOffsetVal();
}
function nudge(d) {
context.background().nudge(d, context.map().zoom());
updateOffsetVal();
}
function buttonOffset(d) {
if (d3_event.button !== 0) return;
var timeout = window.setTimeout(function() {
interval = window.setInterval(nudge.bind(null, d), 100);
}, 500),
interval;
function doneNudge() {
window.clearTimeout(timeout);
window.clearInterval(interval);
d3_select(window)
.on('mouseup.buttonoffset', null, true)
.on('mousedown.buttonoffset', null, true);
}
d3_select(window)
.on('mouseup.buttonoffset', doneNudge, true)
.on('mousedown.buttonoffset', doneNudge, true);
nudge(d);
}
function inputOffset() {
if (d3_event.button !== 0) return;
var input = d3_select(this);
var d = input.node().value;
if (d === '') return resetOffset();
d = d.replace(/;/g, ',').split(',').map(function(n) {
// if n is NaN, it will always get mapped to false.
return !isNaN(n) && n;
minimapLabelEnter
.append('input')
.attr('type', 'checkbox')
.on('change', function() {
d3_event.preventDefault();
uiMapInMap.toggle();
});
if (d.length !== 2 || !d[0] || !d[1]) {
input.classed('error', true);
return;
}
context.background().offset(geoMetersToOffset(d));
updateOffsetVal();
}
minimapLabelEnter
.append('span')
.text(t('background.minimap.description'));
function dragOffset() {
if (d3_event.button !== 0) return;
var origin = [d3_event.clientX, d3_event.clientY];
context.container()
.append('div')
.attr('class', 'nudge-surface');
d3_select(window)
.on('mousemove.offset', function() {
var latest = [d3_event.clientX, d3_event.clientY];
var d = [
-(origin[0] - latest[0]) / 4,
-(origin[1] - latest[1]) / 4
];
origin = latest;
nudge(d);
})
.on('mouseup.offset', function() {
if (d3_event.button !== 0) return;
d3_selectAll('.nudge-surface')
.remove();
d3_select(window)
.on('mousemove.offset', null)
.on('mouseup.offset', null);
});
d3_event.preventDefault();
}
function hide() {
setVisible(false);
}
function toggle() {
if (d3_event) {
d3_event.preventDefault();
}
tooltipBehavior.hide(button);
setVisible(!button.classed('active'));
}
function quickSwitch() {
if (d3_event) {
d3_event.stopImmediatePropagation();
d3_event.preventDefault();
}
if (previous) {
clickSetSource(previous);
}
}
function setVisible(show) {
if (show !== shown) {
button.classed('active', show);
shown = show;
if (show) {
selection
.on('mousedown.background-inside', function() {
d3_event.stopPropagation();
});
content
.style('display', 'block')
.style('right', '-300px')
.transition()
.duration(200)
.style('right', '0px');
content.selectAll('.layer')
.call(setTooltips);
} else {
content
.style('display', 'block')
.style('right', '0px')
.transition()
.duration(200)
.style('right', '-300px')
.on('end', function() {
d3_select(this).style('display', 'none');
});
selection
.on('mousedown.background-inside', null);
}
}
}
var content = selection
.append('div')
.attr('class', 'fillL map-overlay col3 content hide'),
tooltipBehavior = tooltip()
.placement((textDirection === 'rtl') ? 'right' : 'left')
.html(true)
.title(uiTooltipHtml(t('background.description'), key)),
button = selection
.append('button')
.attr('tabindex', -1)
.on('click', toggle)
.call(svgIcon('#icon-layers', 'light'))
.call(tooltipBehavior),
shown = false;
/* opacity switcher */
var opawrap = content
.append('div')
.attr('class', 'opacity-options-wrapper');
opawrap
.append('h4')
.text(t('background.title'));
var opacityList = opawrap
.append('ul')
.attr('class', 'opacity-options');
opacityList.selectAll('div.opacity')
.data(opacities)
// "Info / Report a Problem" link
selection.selectAll('.imagery-faq')
.data([0])
.enter()
.append('li')
.attr('data-original-title', function(d) {
return t('background.percent_brightness', { opacity: (d * 100) });
})
.on('click.set-opacity', setOpacity)
.html('<div class="select-box"></div>')
.call(tooltip()
.placement((textDirection === 'rtl') ? 'right' : 'left'))
.append('div')
.attr('class', 'opacity')
.style('opacity', function(d) { return 1.25 - d; });
/* background list */
var backgroundList = content
.append('ul')
.attr('class', 'layer-list')
.attr('dir', 'auto');
content
.append('div')
.attr('class', 'imagery-faq')
.append('a')
@@ -449,108 +247,141 @@ export function uiBackground(context) {
.attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
.append('span')
.text(t('background.imagery_source_faq'));
}
/* overlay list */
function renderOverlayList(selection) {
var container = selection.selectAll('.layer-overlay-list')
.data([0]);
var overlayList = content
_overlayList = container.enter()
.append('ul')
.attr('class', 'layer-list');
.attr('class', 'layer-list layer-overlay-list')
.attr('dir', 'auto')
.merge(container);
}
var controls = content
function update() {
_backgroundList
.call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
_overlayList
.call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
_displayOptionsContainer
.call(backgroundDisplayOptions);
_offsetContainer
.call(backgroundOffset);
}
function quickSwitch() {
if (d3_event) {
d3_event.stopImmediatePropagation();
d3_event.preventDefault();
}
if (_previousBackground) {
chooseBackground(_previousBackground);
}
}
function background(selection) {
function hidePane() {
setVisible(false);
}
function togglePane() {
if (d3_event) d3_event.preventDefault();
paneTooltip.hide(button);
setVisible(!button.classed('active'));
}
function setVisible(show) {
if (show !== _shown) {
button.classed('active', show);
_shown = show;
if (show) {
uiMapData.hidePane();
uiHelp.hidePane();
update();
pane
.style('display', 'block')
.style('right', '-300px')
.transition()
.duration(200)
.style('right', '0px');
} else {
pane
.style('display', 'block')
.style('right', '0px')
.transition()
.duration(200)
.style('right', '-300px')
.on('end', function() {
d3_select(this).style('display', 'none');
});
}
}
}
var pane = selection
.append('div')
.attr('class', 'controls-list');
.attr('class', 'fillL map-overlay col3 content hide');
var paneTooltip = tooltip()
.placement((textDirection === 'rtl') ? 'right' : 'left')
.html(true)
.title(uiTooltipHtml(t('background.description'), key));
/* minimap toggle */
var button = selection
.append('button')
.attr('tabindex', -1)
.on('click', togglePane)
.call(svgIcon('#icon-layers', 'light'))
.call(paneTooltip);
var minimapLabel = controls
.append('label')
.call(tooltip()
.html(true)
.title(uiTooltipHtml(t('background.minimap.tooltip'), t('background.minimap.key')))
.placement('top')
pane
.append('h2')
.text(t('background.title'));
// background list
pane
.append('div')
.attr('class', 'background-background-list-container')
.call(uiDisclosure(context, 'background_list', true)
.title(t('background.backgrounds'))
.content(renderBackgroundList)
);
minimapLabel
.classed('minimap-toggle', true)
.append('input')
.attr('type', 'checkbox')
.on('change', function() {
uiMapInMap.toggle();
d3_event.preventDefault();
});
minimapLabel
.append('span')
.text(t('background.minimap.description'));
/* imagery offset controls */
var adjustments = content
// overlay list
pane
.append('div')
.attr('class', 'adjustments');
adjustments
.append('a')
.text(t('background.fix_misalignment'))
.attr('href', '#')
.classed('hide-toggle', true)
.classed('expanded', false)
.on('click', function() {
if (d3_event.button !== 0) return;
var exp = d3_select(this).classed('expanded');
nudgeContainer.style('display', exp ? 'none' : 'block');
d3_select(this).classed('expanded', !exp);
d3_event.preventDefault();
});
var nudgeContainer = adjustments
.append('div')
.attr('class', 'nudge-container cf')
.style('display', 'none');
nudgeContainer
.append('div')
.attr('class', 'nudge-instructions')
.text(t('background.offset'));
var nudgeRect = nudgeContainer
.append('div')
.attr('class', 'nudge-outer-rect')
.on('mousedown', dragOffset);
nudgeRect
.append('div')
.attr('class', 'nudge-inner-rect')
.append('input')
.on('change', inputOffset)
.on('mousedown', function() {
if (d3_event.button !== 0) return;
d3_event.stopPropagation();
});
nudgeContainer
.append('div')
.selectAll('button')
.data(directions).enter()
.append('button')
.attr('class', function(d) { return d[0] + ' nudge'; })
.on('mousedown', function(d) {
if (d3_event.button !== 0) return;
buttonOffset(d[1]);
});
nudgeContainer
.append('button')
.attr('title', t('background.reset'))
.attr('class', 'nudge-reset disabled')
.on('click', resetOffset)
.call(
(textDirection === 'rtl') ? svgIcon('#icon-redo') : svgIcon('#icon-undo')
.attr('class', 'background-overlay-list-container')
.call(uiDisclosure(context, 'overlay_list', true)
.title(t('background.overlays'))
.content(renderOverlayList)
);
// display options
_displayOptionsContainer = pane
.append('div')
.attr('class', 'background-display-options');
// offset controls
_offsetContainer = pane
.append('div')
.attr('class', 'background-offset');
// add listeners
context.map()
.on('move.background-update', _debounce(utilCallWhenIdle(update), 1000));
@@ -559,15 +390,18 @@ export function uiBackground(context) {
update();
setOpacity(opacityDefault);
var keybinding = d3_keybinding('background')
.on(key, toggle)
.on(key, togglePane)
.on(uiCmd('⌘' + key), quickSwitch)
.on([t('map_data.key'), t('help.key')], hide);
.on([t('map_data.key'), t('help.key')], hidePane);
d3_select(document)
.call(keybinding);
uiBackground.hidePane = hidePane;
uiBackground.togglePane = togglePane;
uiBackground.setVisible = setVisible;
}
return background;

View File

@@ -0,0 +1,125 @@
import {
event as d3_event,
select as d3_select
} from 'd3-selection';
import { t, textDirection } from '../util/locale';
import { svgIcon } from '../svg';
import { uiDisclosure } from './disclosure';
export function uiBackgroundDisplayOptions(context) {
var _selection = d3_select(null);
var sliders = ['brightness', 'contrast', 'saturation', 'sharpness'];
var storedOpacity = context.storage('background-opacity');
var _options = {
brightness: (storedOpacity !== null ? (+storedOpacity) : 1),
contrast: 1,
saturation: 1,
sharpness: 1
};
function clamp(x, min, max) {
return Math.max(min, Math.min(x, max));
}
function updateValue(d, val) {
if (!val && d3_event && d3_event.target) {
val = d3_event.target.value;
}
val = clamp(val, 0.25, 2);
_options[d] = val;
context.background()[d](val);
if (d === 'brightness') {
context.storage('background-opacity', val);
}
_selection
.call(render);
}
function render(selection) {
var container = selection.selectAll('.display-options-container')
.data([0]);
var containerEnter = container.enter()
.append('div')
.attr('class', 'display-options-container controls-list');
// add slider controls
var slidersEnter = containerEnter.selectAll('.display-control')
.data(sliders)
.enter()
.append('div')
.attr('class', function(d) { return 'display-control display-control-' + d; });
slidersEnter
.append('h5')
.text(function(d) { return t('background.' + d); })
.append('span')
.attr('class', function(d) { return 'display-option-value display-option-value-' + d; });
slidersEnter
.append('input')
.attr('class', function(d) { return 'display-option-input display-option-input-' + d; })
.attr('type', 'range')
.attr('min', '0.25')
.attr('max', '2')
.attr('step', '0.05')
.on('input', function(d) {
var val = d3_select(this).property('value');
updateValue(d, val);
});
slidersEnter
.append('button')
.attr('title', t('background.reset'))
.attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; })
.on('click', function(d) {
if (d3_event.button !== 0) return;
updateValue(d, 1);
})
.call(svgIcon('#icon-' + (textDirection === 'rtl' ? 'redo' : 'undo')));
// update
container = containerEnter
.merge(container);
container.selectAll('.display-option-input')
.property('value', function(d) { return _options[d]; });
container.selectAll('.display-option-value')
.text(function(d) { return Math.floor(_options[d] * 100) + '%'; });
container.selectAll('.display-option-reset')
.classed('disabled', function(d) { return _options[d] === 1; });
// first time only, set brightness if needed
if (containerEnter.size() && _options.brightness !== 1) {
context.background().brightness(_options.brightness);
}
}
function backgroundDisplayOptions(selection) {
_selection = selection;
selection
.call(uiDisclosure(context, 'background_display_options', true)
.title(t('background.display_options'))
.content(render)
);
}
return backgroundDisplayOptions;
}

View File

@@ -0,0 +1,197 @@
import {
event as d3_event,
select as d3_select,
selectAll as d3_selectAll
} from 'd3-selection';
import { t, textDirection } from '../util/locale';
import { geoMetersToOffset, geoOffsetToMeters } from '../geo';
import { svgIcon } from '../svg';
import { uiDisclosure } from './disclosure';
export function uiBackgroundOffset(context) {
var directions = [
['right', [0.5, 0]],
['top', [0, -0.5]],
['left', [-0.5, 0]],
['bottom', [0, 0.5]]
];
function d3_eventCancel() {
d3_event.stopPropagation();
d3_event.preventDefault();
}
function updateValue() {
var meters = geoOffsetToMeters(context.background().offset());
var x = +meters[0].toFixed(2);
var y = +meters[1].toFixed(2);
d3_selectAll('.nudge-inner-rect')
.select('input')
.classed('error', false)
.property('value', x + ', ' + y);
d3_selectAll('.nudge-reset')
.classed('disabled', function() {
return (x === 0 && y === 0);
});
}
function resetOffset() {
context.background().offset([0, 0]);
updateValue();
}
function nudge(d) {
context.background().nudge(d, context.map().zoom());
updateValue();
}
function clickNudgeButton(d) {
var interval;
var timeout = window.setTimeout(function() {
interval = window.setInterval(nudge.bind(null, d), 100);
}, 500);
function doneNudge() {
window.clearTimeout(timeout);
window.clearInterval(interval);
d3_select(window)
.on('mouseup.buttonoffset', null, true)
.on('mousedown.buttonoffset', null, true);
}
d3_select(window)
.on('mouseup.buttonoffset', doneNudge, true)
.on('mousedown.buttonoffset', doneNudge, true);
nudge(d);
}
function inputOffset() {
var input = d3_select(this);
var d = input.node().value;
if (d === '') return resetOffset();
d = d.replace(/;/g, ',').split(',').map(function(n) {
// if n is NaN, it will always get mapped to false.
return !isNaN(n) && n;
});
if (d.length !== 2 || !d[0] || !d[1]) {
input.classed('error', true);
return;
}
context.background().offset(geoMetersToOffset(d));
updateValue();
}
function dragOffset() {
d3_event.preventDefault();
if (d3_event.button !== 0) return;
var origin = [d3_event.clientX, d3_event.clientY];
context.container()
.append('div')
.attr('class', 'nudge-surface');
d3_select(window)
.on('mousemove.offset', function() {
var latest = [d3_event.clientX, d3_event.clientY];
var d = [
-(origin[0] - latest[0]) / 4,
-(origin[1] - latest[1]) / 4
];
origin = latest;
nudge(d);
})
.on('mouseup.offset', function() {
if (d3_event.button !== 0) return;
d3_selectAll('.nudge-surface')
.remove();
d3_select(window)
.on('mousemove.offset', null)
.on('mouseup.offset', null);
});
}
function render(selection) {
var container = selection.selectAll('.nudge-container')
.data([0]);
var containerEnter = container.enter()
.append('div')
.attr('class', 'nudge-container cf');
containerEnter
.append('div')
.attr('class', 'nudge-instructions')
.text(t('background.offset'));
var nudgeEnter = containerEnter
.append('div')
.attr('class', 'nudge-outer-rect')
.on('mousedown', dragOffset);
nudgeEnter
.append('div')
.attr('class', 'nudge-inner-rect')
.append('input')
.on('change', inputOffset);
containerEnter
.append('div')
.selectAll('button')
.data(directions).enter()
.append('button')
.attr('class', function(d) { return d[0] + ' nudge'; })
.on('contextmenu', d3_eventCancel)
.on('mousedown', function(d) {
if (d3_event.button !== 0) return;
clickNudgeButton(d[1]);
});
containerEnter
.append('button')
.attr('title', t('background.reset'))
.attr('class', 'nudge-reset disabled')
.on('contextmenu', d3_eventCancel)
.on('click', function() {
if (d3_event.button !== 0) return;
resetOffset();
})
.call(svgIcon('#icon-' + (textDirection === 'rtl' ? 'redo' : 'undo')));
updateValue();
}
function backgroundOffset(selection) {
selection
.call(uiDisclosure(context, 'background_offset', false)
.title(t('background.fix_misalignment'))
.content(render)
);
}
context.background()
.on('change.backgroundOffset-update', updateValue);
return backgroundOffset;
}

View File

@@ -269,7 +269,6 @@ export function uiCommit(context) {
updateChangeset({ review_requested: (rr ? 'yes' : undefined) });
var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty();
tagSection
.call(rawTagEditor
.expanded(expanded)

View File

@@ -1,70 +1,115 @@
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { event as d3_event } from 'd3-selection';
import { svgIcon } from '../svg';
import { utilRebind } from '../util/rebind';
import { uiToggle } from './toggle';
import { textDirection } from '../util/locale';
export function uiDisclosure() {
export function uiDisclosure(context, key, expandedDefault) {
var dispatch = d3_dispatch('toggled'),
title,
expanded = false,
content = function () {};
_preference = (context.storage('disclosure.' + key + '.expanded')),
_expanded = (_preference === null ? !!expandedDefault : (_preference === 'true')),
_title,
_updatePreference = true,
_content = function () {};
var disclosure = function(selection) {
var hideToggle = selection.selectAll('.hide-toggle')
var hideToggle = selection.selectAll('.hide-toggle-' + key)
.data([0]);
hideToggle = hideToggle.enter()
// enter
var hideToggleEnter = hideToggle.enter()
.append('a')
.attr('href', '#')
.attr('class', 'hide-toggle')
.attr('class', 'hide-toggle hide-toggle-' + key)
.call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
hideToggleEnter
.append('span')
.attr('class', 'hide-toggle-text');
// update
hideToggle = hideToggleEnter
.merge(hideToggle);
hideToggle
.text(title)
.on('click', toggle)
.classed('expanded', expanded);
.classed('expanded', _expanded);
hideToggle.selectAll('.hide-toggle-text')
.text(_title);
hideToggle.selectAll('.hide-toggle-icon')
.attr('xlink:href', _expanded ? '#icon-down'
: (textDirection === 'rtl') ? '#icon-backward' : '#icon-forward'
);
var wrap = selection.selectAll('div')
var wrap = selection.selectAll('.disclosure-wrap')
.data([0]);
wrap = wrap.enter()
.append('div')
.attr('class', 'disclosure-wrap disclosure-wrap-' + key)
.merge(wrap);
wrap
.classed('hide', !expanded)
.call(content);
.classed('hide', !_expanded)
.call(_content);
function toggle() {
expanded = !expanded;
hideToggle.classed('expanded', expanded);
wrap.call(uiToggle(expanded));
dispatch.call('toggled', this, expanded);
d3_event.preventDefault();
_expanded = !_expanded;
if (_updatePreference) {
context.storage('disclosure.' + key + '.expanded', _expanded);
}
hideToggle
.classed('expanded', _expanded);
hideToggle.selectAll('.hide-toggle-icon')
.attr('xlink:href', _expanded ? '#icon-down'
: (textDirection === 'rtl') ? '#icon-backward' : '#icon-forward'
);
wrap
.call(uiToggle(_expanded));
dispatch.call('toggled', this, _expanded);
}
};
disclosure.title = function(_) {
if (!arguments.length) return title;
title = _;
if (!arguments.length) return _title;
_title = _;
return disclosure;
};
disclosure.expanded = function(_) {
if (!arguments.length) return expanded;
expanded = _;
if (!arguments.length) return _expanded;
_expanded = _;
return disclosure;
};
disclosure.updatePreference = function(_) {
if (!arguments.length) return _updatePreference;
_updatePreference = _;
return disclosure;
};
disclosure.content = function(_) {
if (!arguments.length) return content;
content = _;
if (!arguments.length) return _content;
_content = _;
return disclosure;
};

View File

@@ -9,7 +9,9 @@ import marked from 'marked';
import { t, textDirection } from '../util/locale';
import { svgIcon } from '../svg';
import { uiCmd } from './cmd';
import { uiBackground } from './background';
import { uiIntro } from './intro';
import { uiMapData } from './map_data';
import { uiShortcuts } from './shortcuts';
import { uiTooltipHtml } from './tooltipHtml';
import { tooltip } from '../util/tooltip';
@@ -260,12 +262,12 @@ export function uiHelp(context) {
function help(selection) {
function hide() {
function hidePane() {
setVisible(false);
}
function toggle() {
function togglePane() {
if (d3_event) d3_event.preventDefault();
tooltipBehavior.hide(button);
setVisible(!button.classed('active'));
@@ -278,14 +280,15 @@ export function uiHelp(context) {
shown = show;
if (show) {
selection.on('mousedown.help-inside', function() {
return d3_event.stopPropagation();
});
uiBackground.hidePane();
uiMapData.hidePane();
pane.style('display', 'block')
.style('right', '-500px')
.transition()
.duration(200)
.style('right', '0px');
} else {
pane.style('right', '0px')
.transition()
@@ -294,7 +297,6 @@ export function uiHelp(context) {
.on('end', function() {
d3_select(this).style('display', 'none');
});
selection.on('mousedown.help-inside', null);
}
}
}
@@ -375,13 +377,14 @@ export function uiHelp(context) {
.title(uiTooltipHtml(t('help.title'), key)),
button = selection.append('button')
.attr('tabindex', -1)
.on('click', toggle)
.on('click', togglePane)
.call(svgIcon('#icon-help', 'light'))
.call(tooltipBehavior),
shown = false;
var toc = pane.append('ul')
var toc = pane
.append('ul')
.attr('class', 'toc');
var menuItems = toc.selectAll('li')
@@ -424,26 +427,34 @@ export function uiHelp(context) {
.text(t('splash.walkthrough'));
var content = pane.append('div')
var content = pane
.append('div')
.attr('class', 'left-content');
var doctitle = content.append('h2')
var doctitle = content
.append('h2')
.text(t('help.title'));
var body = content.append('div')
var body = content
.append('div')
.attr('class', 'body');
var nav = content.append('div')
var nav = content
.append('div')
.attr('class', 'nav');
clickHelp(docs[0], 0);
var keybinding = d3_keybinding('help')
.on(key, toggle)
.on([t('background.key'), t('map_data.key')], hide);
.on(key, togglePane)
.on([t('background.key'), t('map_data.key')], hidePane);
d3_select(document)
.call(keybinding);
uiHelp.hidePane = hidePane;
uiHelp.togglePane = togglePane;
uiHelp.setVisible = setVisible;
}
return help;

View File

@@ -2,6 +2,8 @@ export { uiInit } from './init';
export { uiAccount } from './account';
export { uiAttribution } from './attribution';
export { uiBackground } from './background';
export { uiBackgroundDisplayOptions } from './background_display_options';
export { uiBackgroundOffset } from './background_offset';
export { uiChangesetEditor } from './changeset_editor';
export { uiCmd } from './cmd';
export { uiCommit } from './commit';

View File

@@ -5,380 +5,423 @@ import {
import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js';
import { t, textDirection } from '../util/locale';
import { svgIcon } from '../svg';
import { uiTooltipHtml } from './tooltipHtml';
import { t, textDirection } from '../util/locale';
import { tooltip } from '../util/tooltip';
import { uiBackground } from './background';
import { uiDisclosure } from './disclosure';
import { uiHelp } from './help';
import { uiTooltipHtml } from './tooltipHtml';
export function uiMapData(context) {
var key = t('map_data.key'),
features = context.features().keys(),
layers = context.layers(),
fills = ['wireframe', 'partial', 'full'],
fillDefault = context.storage('area-fill') || 'partial',
fillSelected = fillDefault;
var key = t('map_data.key');
var features = context.features().keys();
var layers = context.layers();
var fills = ['wireframe', 'partial', 'full'];
var _fillDefault = context.storage('area-fill') || 'partial';
var _fillSelected = _fillDefault;
var _shown = false;
var _dataLayerContainer = d3_select(null);
var _fillList = d3_select(null);
var _featureList = d3_select(null);
function map_data(selection) {
function showsFeature(d) {
return context.features().enabled(d);
}
function showsFeature(d) {
return context.features().enabled(d);
function autoHiddenFeature(d) {
return context.features().autoHidden(d);
}
function clickFeature(d) {
context.features().toggle(d);
update();
}
function showsFill(d) {
return _fillSelected === d;
}
function setFill(d) {
fills.forEach(function(opt) {
context.surface().classed('fill-' + opt, Boolean(opt === d));
});
_fillSelected = d;
if (d !== 'wireframe') {
_fillDefault = d;
context.storage('area-fill', d);
}
update();
}
function autoHiddenFeature(d) {
return context.features().autoHidden(d);
function showsLayer(which) {
var layer = layers.layer(which);
if (layer) {
return layer.enabled();
}
return false;
}
function clickFeature(d) {
context.features().toggle(d);
function setLayer(which, enabled) {
var layer = layers.layer(which);
if (layer) {
layer.enabled(enabled);
update();
}
}
function showsFill(d) {
return fillSelected === d;
function toggleLayer(which) {
setLayer(which, !showsLayer(which));
}
function drawPhotoItems(selection) {
var photoKeys = ['mapillary-images', 'mapillary-signs', 'openstreetcam-images'];
var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
function layerSupported(d) {
return d.layer && d.layer.supported();
}
function layerEnabled(d) {
return layerSupported(d) && d.layer.enabled();
}
var ul = selection
.selectAll('.layer-list-photos')
.data([0]);
function setFill(d) {
fills.forEach(function(opt) {
context.surface().classed('fill-' + opt, Boolean(opt === d));
ul = ul.enter()
.append('ul')
.attr('class', 'layer-list layer-list-photos')
.merge(ul);
var li = ul.selectAll('.list-item-photos')
.data(data);
li.exit()
.remove();
var liEnter = li.enter()
.append('li')
.attr('class', function(d) { return 'list-item-photos list-item-' + d.id; });
var labelEnter = liEnter
.append('label')
.each(function(d) {
d3_select(this)
.call(tooltip()
.title(t(d.id.replace('-', '_') + '.tooltip'))
.placement('top')
);
});
fillSelected = d;
if (d !== 'wireframe') {
fillDefault = d;
context.storage('area-fill', d);
}
update();
}
labelEnter
.append('input')
.attr('type', 'checkbox')
.on('change', function(d) { toggleLayer(d.id); });
labelEnter
.append('span')
.text(function(d) { return t(d.id.replace('-', '_') + '.title'); });
function showsLayer(which) {
var layer = layers.layer(which);
if (layer) {
return layer.enabled();
}
return false;
}
// Update
li = li
.merge(liEnter);
li
.classed('active', layerEnabled)
.selectAll('input')
.property('checked', layerEnabled);
}
function setLayer(which, enabled) {
var layer = layers.layer(which);
if (layer) {
layer.enabled(enabled);
update();
}
}
function drawOsmItem(selection) {
var osm = layers.layer('osm'),
showsOsm = osm.enabled();
var ul = selection
.selectAll('.layer-list-osm')
.data(osm ? [0] : []);
// Exit
ul.exit()
.remove();
// Enter
var ulEnter = ul.enter()
.append('ul')
.attr('class', 'layer-list layer-list-osm');
var liEnter = ulEnter
.append('li')
.attr('class', 'list-item-osm');
var labelEnter = liEnter
.append('label')
.call(tooltip()
.title(t('map_data.layers.osm.tooltip'))
.placement('top')
);
labelEnter
.append('input')
.attr('type', 'checkbox')
.on('change', function() { toggleLayer('osm'); });
labelEnter
.append('span')
.text(t('map_data.layers.osm.title'));
// Update
ul = ul
.merge(ulEnter);
ul.selectAll('.list-item-osm')
.classed('active', showsOsm)
.selectAll('input')
.property('checked', showsOsm);
}
function toggleLayer(which) {
setLayer(which, !showsLayer(which));
}
function drawGpxItem(selection) {
var gpx = layers.layer('gpx'),
hasGpx = gpx && gpx.hasGpx(),
showsGpx = hasGpx && gpx.enabled();
var ul = selection
.selectAll('.layer-list-gpx')
.data(gpx ? [0] : []);
function drawPhotoItems(selection) {
var photoKeys = ['mapillary-images', 'mapillary-signs', 'openstreetcam-images'];
var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
// Exit
ul.exit()
.remove();
function layerSupported(d) {
return d.layer && d.layer.supported();
}
function layerEnabled(d) {
return layerSupported(d) && d.layer.enabled();
}
// Enter
var ulEnter = ul.enter()
.append('ul')
.attr('class', 'layer-list layer-list-gpx');
var ul = selection
.selectAll('.layer-list-photos')
.data([0]);
var liEnter = ulEnter
.append('li')
.attr('class', 'list-item-gpx');
ul = ul.enter()
.append('ul')
.attr('class', 'layer-list layer-list-photos')
.merge(ul);
liEnter
.append('button')
.attr('class', 'list-item-gpx-extent')
.call(tooltip()
.title(t('gpx.zoom'))
.placement((textDirection === 'rtl') ? 'right' : 'left')
)
.on('click', function() {
d3_event.preventDefault();
d3_event.stopPropagation();
gpx.fitZoom();
})
.call(svgIcon('#icon-search'));
var li = ul.selectAll('.list-item-photos')
.data(data);
li.exit()
.remove();
var liEnter = li.enter()
.append('li')
.attr('class', function(d) { return 'list-item-photos list-item-' + d.id; });
var labelEnter = liEnter
.append('label')
.each(function(d) {
d3_select(this)
.call(tooltip()
.title(t(d.id.replace('-', '_') + '.tooltip'))
.placement('top')
);
});
labelEnter
.append('input')
.attr('type', 'checkbox')
.on('change', function(d) { toggleLayer(d.id); });
labelEnter
.append('span')
.text(function(d) { return t(d.id.replace('-', '_') + '.title'); });
// Update
li = li
.merge(liEnter);
li
.classed('active', layerEnabled)
.selectAll('input')
.property('checked', layerEnabled);
}
function drawOsmItem(selection) {
var osm = layers.layer('osm'),
showsOsm = osm.enabled();
var ul = selection
.selectAll('.layer-list-osm')
.data(osm ? [0] : []);
// Exit
ul.exit()
.remove();
// Enter
var ulEnter = ul.enter()
.append('ul')
.attr('class', 'layer-list layer-list-osm');
var liEnter = ulEnter
.append('li')
.attr('class', 'list-item-osm');
var labelEnter = liEnter
.append('label')
.call(tooltip()
.title(t('map_data.layers.osm.tooltip'))
.placement('top')
);
labelEnter
.append('input')
.attr('type', 'checkbox')
.on('change', function() { toggleLayer('osm'); });
labelEnter
.append('span')
.text(t('map_data.layers.osm.title'));
// Update
ul = ul
.merge(ulEnter);
ul.selectAll('.list-item-osm')
.classed('active', showsOsm)
.selectAll('input')
.property('checked', showsOsm);
}
function drawGpxItem(selection) {
var gpx = layers.layer('gpx'),
hasGpx = gpx && gpx.hasGpx(),
showsGpx = hasGpx && gpx.enabled();
var ul = selection
.selectAll('.layer-list-gpx')
.data(gpx ? [0] : []);
// Exit
ul.exit()
.remove();
// Enter
var ulEnter = ul.enter()
.append('ul')
.attr('class', 'layer-list layer-list-gpx');
var liEnter = ulEnter
.append('li')
.attr('class', 'list-item-gpx');
liEnter
.append('button')
.attr('class', 'list-item-gpx-extent')
.call(tooltip()
.title(t('gpx.zoom'))
.placement((textDirection === 'rtl') ? 'right' : 'left'))
.on('click', function() {
d3_event.preventDefault();
d3_event.stopPropagation();
gpx.fitZoom();
})
.call(svgIcon('#icon-search'));
liEnter
.append('button')
.attr('class', 'list-item-gpx-browse')
.call(tooltip()
.title(t('gpx.browse'))
.placement((textDirection === 'rtl') ? 'right' : 'left')
)
.on('click', function() {
d3_select(document.createElement('input'))
.attr('type', 'file')
.on('change', function() {
gpx.files(d3_event.target.files);
})
.node().click();
})
.call(svgIcon('#icon-geolocate'));
var labelEnter = liEnter
.append('label')
.call(tooltip()
.title(t('gpx.drag_drop'))
.placement('top')
);
labelEnter
.append('input')
.attr('type', 'checkbox')
.on('change', function() { toggleLayer('gpx'); });
labelEnter
.append('span')
.text(t('gpx.local_layer'));
// Update
ul = ul
.merge(ulEnter);
ul.selectAll('.list-item-gpx')
.classed('active', showsGpx)
.selectAll('label')
.classed('deemphasize', !hasGpx)
.selectAll('input')
.property('disabled', !hasGpx)
.property('checked', showsGpx);
}
function drawList(selection, data, type, name, change, active) {
var items = selection.selectAll('li')
.data(data);
// Exit
items.exit()
.remove();
// Enter
var enter = items.enter()
.append('li')
.attr('class', 'layer')
.call(tooltip()
.html(true)
.title(function(d) {
var tip = t(name + '.' + d + '.tooltip'),
key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null);
if (name === 'feature' && autoHiddenFeature(d)) {
var msg = showsLayer('osm') ? t('map_data.autohidden') : t('map_data.osmhidden');
tip += '<div>' + msg + '</div>';
}
return uiTooltipHtml(tip, key);
liEnter
.append('button')
.attr('class', 'list-item-gpx-browse')
.call(tooltip()
.title(t('gpx.browse'))
.placement((textDirection === 'rtl') ? 'right' : 'left')
)
.on('click', function() {
d3_select(document.createElement('input'))
.attr('type', 'file')
.on('change', function() {
gpx.files(d3_event.target.files);
})
.placement('top')
);
.node().click();
})
.call(svgIcon('#icon-geolocate'));
var label = enter
.append('label');
var labelEnter = liEnter
.append('label')
.call(tooltip()
.title(t('gpx.drag_drop'))
.placement('top')
);
label
.append('input')
.attr('type', type)
.attr('name', name)
.on('change', change);
labelEnter
.append('input')
.attr('type', 'checkbox')
.on('change', function() { toggleLayer('gpx'); });
label
.append('span')
.text(function(d) { return t(name + '.' + d + '.description'); });
labelEnter
.append('span')
.text(t('gpx.local_layer'));
// Update
items = items
.merge(enter);
// Update
ul = ul
.merge(ulEnter);
items
.classed('active', active)
.selectAll('input')
.property('checked', active)
.property('indeterminate', function(d) {
return (name === 'feature' && autoHiddenFeature(d));
});
ul.selectAll('.list-item-gpx')
.classed('active', showsGpx)
.selectAll('label')
.classed('deemphasize', !hasGpx)
.selectAll('input')
.property('disabled', !hasGpx)
.property('checked', showsGpx);
}
function drawListItems(selection, data, type, name, change, active) {
var items = selection.selectAll('li')
.data(data);
// Exit
items.exit()
.remove();
// Enter
var enter = items.enter()
.append('li')
.attr('class', 'layer')
.call(tooltip()
.html(true)
.title(function(d) {
var tip = t(name + '.' + d + '.tooltip'),
key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null);
if (name === 'feature' && autoHiddenFeature(d)) {
var msg = showsLayer('osm') ? t('map_data.autohidden') : t('map_data.osmhidden');
tip += '<div>' + msg + '</div>';
}
return uiTooltipHtml(tip, key);
})
.placement('top')
);
var label = enter
.append('label');
label
.append('input')
.attr('type', type)
.attr('name', name)
.on('change', change);
label
.append('span')
.text(function(d) { return t(name + '.' + d + '.description'); });
// Update
items = items
.merge(enter);
items
.classed('active', active)
.selectAll('input')
.property('checked', active)
.property('indeterminate', function(d) {
return (name === 'feature' && autoHiddenFeature(d));
});
}
function renderDataLayers(selection) {
var container = selection.selectAll('data-layer-container')
.data([0]);
_dataLayerContainer = container.enter()
.append('div')
.attr('class', 'data-layer-container')
.merge(container);
}
function renderFillList(selection) {
var container = selection.selectAll('layer-fill-list')
.data([0]);
_fillList = container.enter()
.append('ul')
.attr('class', 'layer-list layer-fill-list')
.merge(container);
}
function renderFeatureList(selection) {
var container = selection.selectAll('layer-feature-list')
.data([0]);
_featureList = container.enter()
.append('ul')
.attr('class', 'layer-list layer-feature-list')
.merge(container);
}
function update() {
_dataLayerContainer
.call(drawOsmItem)
.call(drawPhotoItems)
.call(drawGpxItem);
_fillList
.call(drawListItems, fills, 'radio', 'area_fill', setFill, showsFill);
_featureList
.call(drawListItems, features, 'checkbox', 'feature', clickFeature, showsFeature);
}
function toggleWireframe() {
if (d3_event) {
d3_event.preventDefault();
d3_event.stopPropagation();
}
setFill((_fillSelected === 'wireframe' ? _fillDefault : 'wireframe'));
context.map().pan([0,0]); // trigger a redraw
}
function update() {
dataLayerContainer
.call(drawOsmItem)
.call(drawPhotoItems)
.call(drawGpxItem);
function mapData(selection) {
fillList
.call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
featureList
.call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
}
function hidePanel() {
function hidePane() {
setVisible(false);
}
function togglePanel() {
function togglePane() {
if (d3_event) d3_event.preventDefault();
tooltipBehavior.hide(button);
paneTooltip.hide(button);
setVisible(!button.classed('active'));
}
function toggleWireframe() {
if (d3_event) {
d3_event.preventDefault();
d3_event.stopPropagation();
}
setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
context.map().pan([0,0]); // trigger a redraw
}
function setVisible(show) {
if (show !== shown) {
if (show !== _shown) {
button.classed('active', show);
shown = show;
_shown = show;
if (show) {
uiBackground.hidePane();
uiHelp.hidePane();
update();
selection.on('mousedown.map_data-inside', function() {
return d3_event.stopPropagation();
});
content.style('display', 'block')
pane
.style('display', 'block')
.style('right', '-300px')
.transition()
.duration(200)
.style('right', '0px');
} else {
content.style('display', 'block')
pane
.style('display', 'block')
.style('right', '0px')
.transition()
.duration(200)
@@ -386,113 +429,80 @@ export function uiMapData(context) {
.on('end', function() {
d3_select(this).style('display', 'none');
});
selection.on('mousedown.map_data-inside', null);
}
}
}
var content = selection
.append('div')
.attr('class', 'fillL map-overlay col3 content hide'),
tooltipBehavior = tooltip()
.placement((textDirection === 'rtl') ? 'right' : 'left')
.html(true)
.title(uiTooltipHtml(t('map_data.description'), key)),
button = selection
.append('button')
.attr('tabindex', -1)
.on('click', togglePanel)
.call(svgIcon('#icon-data', 'light'))
.call(tooltipBehavior),
shown = false;
var pane = selection
.append('div')
.attr('class', 'fillL map-overlay col3 content hide');
content
.append('h4')
var paneTooltip = tooltip()
.placement((textDirection === 'rtl') ? 'right' : 'left')
.html(true)
.title(uiTooltipHtml(t('map_data.description'), key));
var button = selection
.append('button')
.attr('tabindex', -1)
.on('click', togglePane)
.call(svgIcon('#icon-data', 'light'))
.call(paneTooltip);
pane
.append('h2')
.text(t('map_data.title'));
// data layers
content
.append('a')
.text(t('map_data.data_layers'))
.attr('href', '#')
.classed('hide-toggle', true)
.classed('expanded', true)
.on('click', function() {
var exp = d3_select(this).classed('expanded');
dataLayerContainer.style('display', exp ? 'none' : 'block');
d3_select(this).classed('expanded', !exp);
d3_event.preventDefault();
});
var dataLayerContainer = content
pane
.append('div')
.attr('class', 'data-data-layers')
.style('display', 'block');
.attr('class', 'map-data-data-layers')
.call(uiDisclosure(context, 'data_layers', true)
.title(t('map_data.data_layers'))
.content(renderDataLayers)
);
// area fills
content
.append('a')
.text(t('map_data.fill_area'))
.attr('href', '#')
.classed('hide-toggle', true)
.classed('expanded', false)
.on('click', function() {
var exp = d3_select(this).classed('expanded');
fillContainer.style('display', exp ? 'none' : 'block');
d3_select(this).classed('expanded', !exp);
d3_event.preventDefault();
});
var fillContainer = content
pane
.append('div')
.attr('class', 'data-area-fills')
.style('display', 'none');
var fillList = fillContainer
.append('ul')
.attr('class', 'layer-list layer-fill-list');
.attr('class', 'map-data-area-fills')
.call(uiDisclosure(context, 'fill_area', false)
.title(t('map_data.fill_area'))
.content(renderFillList)
);
// feature filters
content
.append('a')
.text(t('map_data.map_features'))
.attr('href', '#')
.classed('hide-toggle', true)
.classed('expanded', false)
.on('click', function() {
var exp = d3_select(this).classed('expanded');
featureContainer.style('display', exp ? 'none' : 'block');
d3_select(this).classed('expanded', !exp);
d3_event.preventDefault();
});
var featureContainer = content
pane
.append('div')
.attr('class', 'data-feature-filters')
.style('display', 'none');
var featureList = featureContainer
.append('ul')
.attr('class', 'layer-list layer-feature-list');
.attr('class', 'map-data-feature-filters')
.call(uiDisclosure(context, 'map_features', false)
.title(t('map_data.map_features'))
.content(renderFeatureList)
);
// add listeners
context.features()
.on('change.map_data-update', update);
setFill(fillDefault);
update();
setFill(_fillDefault);
var keybinding = d3_keybinding('features')
.on(key, togglePanel)
.on(key, togglePane)
.on(t('area_fill.wireframe.key'), toggleWireframe)
.on([t('background.key'), t('help.key')], hidePanel);
.on([t('background.key'), t('help.key')], hidePane);
d3_select(document)
.call(keybinding);
uiMapData.hidePane = hidePane;
uiMapData.togglePane = togglePane;
uiMapData.setVisible = setVisible;
}
return map_data;
return mapData;
}

View File

@@ -284,9 +284,10 @@ export function uiMapInMap(context) {
isHidden = !isHidden;
var label = d3_select('.minimap-toggle');
label.classed('active', !isHidden)
.select('input').property('checked', !isHidden);
d3_select('.minimap-toggle-item')
.classed('active', !isHidden)
.select('input')
.property('checked', !isHidden);
if (isHidden) {
wrap

View File

@@ -16,7 +16,6 @@ import { utilRebind } from '../util';
export function uiPresetEditor(context) {
var dispatch = d3_dispatch('change'),
formFields = uiFormFields(context),
expandedPreference = (context.storage('preset_fields.expanded') !== 'false'),
state,
fieldsArr,
preset,
@@ -25,17 +24,10 @@ export function uiPresetEditor(context) {
function presetEditor(selection) {
selection.call(uiDisclosure()
selection.call(uiDisclosure(context, 'preset_fields', true)
.title(t('inspector.all_fields'))
.expanded(expandedPreference)
.on('toggled', toggled)
.content(render)
);
function toggled(expanded) {
expandedPreference = expanded;
context.storage('preset_fields.expanded', expanded);
}
}

View File

@@ -20,8 +20,8 @@ import {
export function uiRawMemberEditor(context) {
var id,
taginfo = services.taginfo;
var taginfo = services.taginfo,
_entityID;
function selectMember(d) {
@@ -53,7 +53,7 @@ export function uiRawMemberEditor(context) {
function rawMemberEditor(selection) {
var entity = context.entity(id),
var entity = context.entity(_entityID),
memberships = [];
entity.members.slice(0, 1000).forEach(function(member, index) {
@@ -68,21 +68,17 @@ export function uiRawMemberEditor(context) {
});
var gt = entity.members.length > 1000 ? '>' : '';
selection.call(uiDisclosure()
selection.call(uiDisclosure(context, 'raw_member_editor', true)
.title(t('inspector.all_members') + ' (' + gt + memberships.length + ')')
.expanded(true)
.on('toggled', toggled)
.updatePreference(false)
.on('toggled', function(expanded) {
if (expanded) { selection.node().parentNode.scrollTop += 200; }
})
.content(content)
);
function toggled(expanded) {
if (expanded) {
selection.node().parentNode.scrollTop += 200;
}
}
function content(wrap) {
var list = wrap.selectAll('.member-list')
.data([0]);
@@ -201,8 +197,8 @@ export function uiRawMemberEditor(context) {
rawMemberEditor.entityID = function(_) {
if (!arguments.length) return id;
id = _;
if (!arguments.length) return _entityID;
_entityID = _;
return rawMemberEditor;
};

View File

@@ -28,7 +28,8 @@ import { utilDisplayName, utilNoAuto } from '../util';
export function uiRawMembershipEditor(context) {
var taginfo = services.taginfo,
id, showBlank;
_entityID,
_showBlank;
function selectRelation(d) {
@@ -47,11 +48,13 @@ export function uiRawMembershipEditor(context) {
function addMembership(d, role) {
showBlank = false;
_showBlank = false;
var member = { id: _entityID, type: context.entity(_entityID).type, role: role };
if (d.relation) {
context.perform(
actionAddMember(d.relation.id, { id: id, type: context.entity(id).type, role: role }),
actionAddMember(d.relation.id, member),
t('operations.add_member.annotation')
);
@@ -59,7 +62,7 @@ export function uiRawMembershipEditor(context) {
var relation = osmRelation();
context.perform(
actionAddEntity(relation),
actionAddMember(relation.id, { id: id, type: context.entity(id).type, role: role }),
actionAddMember(relation.id, member),
t('operations.add.annotation.relation')
);
@@ -77,15 +80,12 @@ export function uiRawMembershipEditor(context) {
function relations(q) {
var newRelation = {
relation: null,
value: t('inspector.new_relation')
},
result = [],
graph = context.graph();
var newRelation = { relation: null, value: t('inspector.new_relation') };
var result = [];
var graph = context.graph();
context.intersects(context.extent()).forEach(function(entity) {
if (entity.type !== 'relation' || entity.id === id)
if (entity.type !== 'relation' || entity.id === _entityID)
return;
var matched = context.presets().match(entity, graph),
@@ -96,10 +96,7 @@ export function uiRawMembershipEditor(context) {
if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
return;
result.push({
relation: entity,
value: value
});
result.push({ relation: entity, value: value });
});
result.sort(function(a, b) {
@@ -124,7 +121,7 @@ export function uiRawMembershipEditor(context) {
function rawMembershipEditor(selection) {
var entity = context.entity(id),
var entity = context.entity(_entityID),
parents = context.graph().parentRelations(entity),
memberships = [];
@@ -137,21 +134,17 @@ export function uiRawMembershipEditor(context) {
});
var gt = parents.length > 1000 ? '>' : '';
selection.call(uiDisclosure()
selection.call(uiDisclosure(context, 'raw_membership_editor', true)
.title(t('inspector.all_relations') + ' (' + gt + memberships.length + ')')
.expanded(true)
.on('toggled', toggled)
.updatePreference(false)
.on('toggled', function(expanded) {
if (expanded) { selection.node().parentNode.scrollTop += 200; }
})
.content(content)
);
function toggled(expanded) {
if (expanded) {
selection.node().parentNode.scrollTop += 200;
}
}
function content(wrap) {
var list = wrap.selectAll('.member-list')
.data([0]);
@@ -218,7 +211,7 @@ export function uiRawMembershipEditor(context) {
var newrow = list.selectAll('.member-row-new')
.data(showBlank ? [0] : []);
.data(_showBlank ? [0] : []);
newrow.exit()
.remove();
@@ -272,7 +265,7 @@ export function uiRawMembershipEditor(context) {
addrel
.call(svgIcon('#icon-plus', 'light'))
.on('click', function() {
showBlank = true;
_showBlank = true;
content(wrap);
list.selectAll('.member-entity-input').node().focus();
});
@@ -308,7 +301,7 @@ export function uiRawMembershipEditor(context) {
taginfo.roles({
debounce: true,
rtype: rtype || '',
geometry: context.geometry(id),
geometry: context.geometry(_entityID),
query: role
}, function(err, data) {
if (!err) callback(sort(role, data));
@@ -328,8 +321,8 @@ export function uiRawMembershipEditor(context) {
rawMembershipEditor.entityID = function(_) {
if (!arguments.length) return id;
id = _;
if (!arguments.length) return _entityID;
_entityID = _;
return rawMembershipEditor;
};

View File

@@ -25,34 +25,36 @@ import {
export function uiRawTagEditor(context) {
var taginfo = services.taginfo,
dispatch = d3_dispatch('change'),
expandedPreference = (context.storage('raw_tag_editor.expanded') === 'true'),
expandedCurrent = expandedPreference,
updatePreference = true,
readOnlyTags = [],
showBlank = false,
newRow,
state,
preset,
tags,
id;
_readOnlyTags = [],
_showBlank = false,
_updatePreference = true,
_expanded = false,
_newRow,
_state,
_preset,
_tags,
_entityID;
function rawTagEditor(selection) {
var count = Object.keys(tags).filter(function(d) { return d; }).length;
var count = Object.keys(_tags).filter(function(d) { return d; }).length;
selection.call(uiDisclosure()
var disclosure = uiDisclosure(context, 'raw_tag_editor', false)
.title(t('inspector.all_tags') + ' (' + count + ')')
.expanded(expandedCurrent)
.on('toggled', toggled)
.content(content)
);
.updatePreference(_updatePreference)
.content(content);
// Sometimes we want to force the raw_tag_editor to be opened/closed..
// When undefined, uiDisclosure will use the user's stored preference.
if (_expanded !== undefined) {
disclosure.expanded(_expanded);
}
selection.call(disclosure);
function toggled(expanded) {
expandedCurrent = expanded;
if (updatePreference) {
expandedPreference = expanded;
context.storage('raw_tag_editor.expanded', expanded);
}
_expanded = expanded;
if (expanded) {
selection.node().parentNode.scrollTop += 200;
}
@@ -61,14 +63,14 @@ export function uiRawTagEditor(context) {
function content(wrap) {
var entries = _map(tags, function(v, k) {
var entries = _map(_tags, function(v, k) {
return { key: k, value: v };
});
if (!entries.length || showBlank) {
showBlank = false;
if (!entries.length || _showBlank) {
_showBlank = false;
entries.push({key: '', value: ''});
newRow = '';
_newRow = '';
}
var list = wrap.selectAll('.tag-list')
@@ -138,8 +140,8 @@ export function uiRawTagEditor(context) {
items = items
.merge(enter)
.sort(function(a, b) {
return (a.key === newRow && b.key !== newRow) ? 1
: (a.key !== newRow && b.key === newRow) ? -1
return (a.key === _newRow && b.key !== _newRow) ? 1
: (a.key !== _newRow && b.key === _newRow) ? -1
: d3_ascending(a.key, b.key);
});
@@ -149,11 +151,11 @@ export function uiRawTagEditor(context) {
key = row.select('input.key'), // propagate bound data to child
value = row.select('input.value'); // propagate bound data to child
if (id && taginfo) {
if (_entityID && taginfo) {
bindTypeahead(key, value);
}
var isRelation = (id && context.entity(id).type === 'relation'),
var isRelation = (_entityID && context.entity(_entityID).type === 'relation'),
reference;
if (isRelation && tag.key === 'type') {
@@ -162,7 +164,7 @@ export function uiRawTagEditor(context) {
reference = uiTagReference({ key: tag.key, value: tag.value }, context);
}
if (state === 'hover') {
if (_state === 'hover') {
reference.showing(false);
}
@@ -187,8 +189,8 @@ export function uiRawTagEditor(context) {
function isReadOnly(d) {
for (var i = 0; i < readOnlyTags.length; i++) {
if (d.key.match(readOnlyTags[i]) !== null) {
for (var i = 0; i < _readOnlyTags.length; i++) {
if (d.key.match(_readOnlyTags[i]) !== null) {
return true;
}
}
@@ -206,7 +208,7 @@ export function uiRawTagEditor(context) {
function bindTypeahead(key, value) {
if (isReadOnly({ key: key })) return;
var geometry = context.geometry(id);
var geometry = context.geometry(_entityID);
key.call(d3_combobox()
.container(context.container())
@@ -275,7 +277,7 @@ export function uiRawTagEditor(context) {
var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
base = match[1],
suffix = +(match[2] || 1);
while (tags[kNew]) { // rename key if already in use
while (_tags[kNew]) { // rename key if already in use
kNew = base + '_' + suffix++;
}
}
@@ -284,8 +286,8 @@ export function uiRawTagEditor(context) {
d.key = kNew; // Maintain DOM identity through the subsequent update.
if (newRow === kOld) { // see if this row is still a new row
newRow = ((d.value === '' || kNew === '') ? kNew : undefined);
if (_newRow === kOld) { // see if this row is still a new row
_newRow = ((d.value === '' || kNew === '') ? kNew : undefined);
}
this.value = kNew;
@@ -298,8 +300,8 @@ export function uiRawTagEditor(context) {
var tag = {};
tag[d.key] = this.value;
if (newRow === d.key && d.key !== '' && d.value !== '') { // not a new row anymore
newRow = undefined;
if (_newRow === d.key && d.key !== '' && d.value !== '') { // not a new row anymore
_newRow = undefined;
}
dispatch.call('change', this, tag);
@@ -320,7 +322,7 @@ export function uiRawTagEditor(context) {
// handler. Without the setTimeout, the call to `content` would
// wipe out the pending value change.
setTimeout(function() {
showBlank = true;
_showBlank = true;
content(wrap);
list.selectAll('li:last-child input.key').node().focus();
}, 0);
@@ -329,51 +331,51 @@ export function uiRawTagEditor(context) {
rawTagEditor.state = function(_) {
if (!arguments.length) return state;
state = _;
if (!arguments.length) return _state;
_state = _;
return rawTagEditor;
};
rawTagEditor.preset = function(_) {
if (!arguments.length) return preset;
preset = _;
if (preset.isFallback()) {
expandedCurrent = true;
updatePreference = false;
if (!arguments.length) return _preset;
_preset = _;
if (_preset.isFallback()) {
_expanded = true;
_updatePreference = false;
} else {
expandedCurrent = expandedPreference;
updatePreference = true;
_expanded = undefined;
_updatePreference = true;
}
return rawTagEditor;
};
rawTagEditor.tags = function(_) {
if (!arguments.length) return tags;
tags = _;
if (!arguments.length) return _tags;
_tags = _;
return rawTagEditor;
};
rawTagEditor.entityID = function(_) {
if (!arguments.length) return id;
id = _;
if (!arguments.length) return _entityID;
_entityID = _;
return rawTagEditor;
};
rawTagEditor.expanded = function(_) {
if (!arguments.length) return expandedCurrent;
expandedCurrent = _;
updatePreference = false;
if (!arguments.length) return _expanded;
_expanded = _;
_updatePreference = false;
return rawTagEditor;
};
rawTagEditor.readOnlyTags = function(_) {
if (!arguments.length) return readOnlyTags;
readOnlyTags = _;
if (!arguments.length) return _readOnlyTags;
_readOnlyTags = _;
return rawTagEditor;
};