From e1f88f4a18fec18cd2674a751fb687412e116408 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 24 Jun 2017 22:26:14 -0400 Subject: [PATCH 01/36] WIP towards multi-pane info box --- modules/ui/info/measurement.js | 253 +++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 modules/ui/info/measurement.js diff --git a/modules/ui/info/measurement.js b/modules/ui/info/measurement.js new file mode 100644 index 000000000..74e2ceecd --- /dev/null +++ b/modules/ui/info/measurement.js @@ -0,0 +1,253 @@ +import * as d3 from 'd3'; +import _ from 'lodash'; +import { d3keybinding } from '../lib/d3.keybinding.js'; +import { t } from '../util/locale'; +import { geoExtent } from '../geo/index'; +import { utilDetect } from '../util/detect'; +import { uiCmd } from './cmd'; + +import { + geoLength as d3GeoLength, + geoCentroid as d3GeoCentroid +} from 'd3'; + + +export function uiInfoMeasurement(context) { + var isImperial = (utilDetect().locale.toLowerCase() === 'en-us'), + isHidden = true; + + + function info(selection) { + + function radiansToMeters(r) { + // using WGS84 authalic radius (6371007.1809 m) + return r * 6371007.1809; + } + + function steradiansToSqmeters(r) { + // http://gis.stackexchange.com/a/124857/40446 + return r / (4 * Math.PI) * 510065621724000; + } + + + function toLineString(feature) { + if (feature.type === 'LineString') return feature; + + var result = { type: 'LineString', coordinates: [] }; + if (feature.type === 'Polygon') { + result.coordinates = feature.coordinates[0]; + } else if (feature.type === 'MultiPolygon') { + result.coordinates = feature.coordinates[0][0]; + } + + return result; + } + + + function displayLength(m) { + var d = m * (isImperial ? 3.28084 : 1), + p, unit; + + if (isImperial) { + if (d >= 5280) { + d /= 5280; + unit = 'mi'; + } else { + unit = 'ft'; + } + } else { + if (d >= 1000) { + d /= 1000; + unit = 'km'; + } else { + unit = 'm'; + } + } + + // drop unnecessary precision + p = d > 1000 ? 0 : d > 100 ? 1 : 2; + + return String(d.toFixed(p)) + ' ' + unit; + } + + + function displayArea(m2) { + var d = m2 * (isImperial ? 10.7639111056 : 1), + d1, d2, p1, p2, unit1, unit2; + + if (isImperial) { + if (d >= 6969600) { // > 0.25mi² show mi² + d1 = d / 27878400; + unit1 = 'mi²'; + } else { + d1 = d; + unit1 = 'ft²'; + } + + if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres + d2 = d / 43560; + unit2 = 'ac'; + } + + } else { + if (d >= 250000) { // > 0.25km² show km² + d1 = d / 1000000; + unit1 = 'km²'; + } else { + d1 = d; + unit1 = 'm²'; + } + + if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares + d2 = d / 10000; + unit2 = 'ha'; + } + } + + // drop unnecessary precision + p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2; + p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2; + + return String(d1.toFixed(p1)) + ' ' + unit1 + + (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : ''); + } + + + function redraw() { + if (isHidden) return; + + var resolver = context.graph(), + selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), + singular = selected.length === 1 ? selected[0] : null, + extent = geoExtent(), + entity; + + wrap.html(''); + wrap.append('h4') + .attr('class', 'infobox-heading fillD') + .text(singular || t('infobox.selected', { n: selected.length })); + + if (!selected.length) return; + + var center; + for (var i = 0; i < selected.length; i++) { + entity = context.entity(selected[i]); + extent._extend(entity.extent(resolver)); + } + center = extent.center(); + + + var list = wrap.append('ul'); + + // multiple features, just display extent center.. + if (!singular) { + list.append('li') + .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); + return; + } + + // single feature, display details.. + if (!entity) return; + var geometry = entity.geometry(resolver); + + if (geometry === 'line' || geometry === 'area') { + var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()), + feature = entity.asGeoJSON(resolver), + length = radiansToMeters(d3GeoLength(toLineString(feature))), + lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')), + centroid = d3GeoCentroid(feature); + + list.append('li') + .text(t('infobox.geometry') + ': ' + + (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) ); + + if (closed) { + var area = steradiansToSqmeters(entity.area(resolver)); + list.append('li') + .text(t('infobox.area') + ': ' + displayArea(area)); + } + + list.append('li') + .text(lengthLabel + ': ' + displayLength(length)); + + list.append('li') + .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5)); + + + var toggle = isImperial ? 'imperial' : 'metric'; + wrap.append('a') + .text(t('infobox.' + toggle)) + .attr('href', '#') + .attr('class', 'button') + .on('click', function() { + d3.event.preventDefault(); + isImperial = !isImperial; + redraw(); + }); + + } else { + var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center')); + + list.append('li') + .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry)); + + list.append('li') + .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); + } + } + + + function toggle() { + if (d3.event) { + d3.event.preventDefault(); + } + + isHidden = !isHidden; + + if (isHidden) { + wrap + .style('display', 'block') + .style('opacity', 1) + .transition() + .duration(200) + .style('opacity', 0) + .on('end', function() { + d3.select(this).style('display', 'none'); + }); + } else { + wrap + .style('display', 'block') + .style('opacity', 0) + .transition() + .duration(200) + .style('opacity', 1) + .on('end', function() { + redraw(); + }); + } + } + + + var wrap = selection.selectAll('.infobox') + .data([0]); + + wrap = wrap.enter() + .append('div') + .attr('class', 'infobox fillD2') + .style('display', (isHidden ? 'none' : 'block')) + .merge(wrap); + + context.map() + .on('drawn.info', redraw); + + redraw(); + + var keybinding = d3keybinding('info') + .on(uiCmd('⌘' + t('infobox.key')), toggle); + + d3.select(document) + .call(keybinding); + } + + return info; +} From 22c707a6e23445b4650a92d5f9ca94f84e41d50c Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 26 Jun 2017 21:10:47 -0400 Subject: [PATCH 02/36] Make infobox a container for widgets, measurement code a widget --- data/core.yaml | 25 +- dist/locales/en.json | 26 +- modules/index.js | 1 + modules/ui/info.js | 234 ++++------------- modules/ui/info/index.js | 7 + modules/ui/info/measurement.js | 448 ++++++++++++++++----------------- modules/ui/init.js | 5 +- 7 files changed, 296 insertions(+), 450 deletions(-) create mode 100644 modules/ui/info/index.js diff --git a/data/core.yaml b/data/core.yaml index b26bb755b..3c81c9dda 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -274,17 +274,20 @@ en: truncated_list: "Edits by {users} and {count} others" infobox: key: I - selected: "{n} selected" - geometry: Geometry - closed: closed - center: Center - perimeter: Perimeter - length: Length - area: Area - centroid: Centroid - location: Location - metric: Metric - imperial: Imperial + measurement: + key: M + title: Measurement + selected: "{n} selected" + geometry: Geometry + closed: closed + center: Center + perimeter: Perimeter + length: Length + area: Area + centroid: Centroid + location: Location + metric: Metric + imperial: Imperial geometry: point: point vertex: vertex diff --git a/dist/locales/en.json b/dist/locales/en.json index de125c471..fd9719b20 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -350,17 +350,21 @@ }, "infobox": { "key": "I", - "selected": "{n} selected", - "geometry": "Geometry", - "closed": "closed", - "center": "Center", - "perimeter": "Perimeter", - "length": "Length", - "area": "Area", - "centroid": "Centroid", - "location": "Location", - "metric": "Metric", - "imperial": "Imperial" + "measurement": { + "key": "M", + "title": "Measurement", + "selected": "{n} selected", + "geometry": "Geometry", + "closed": "closed", + "center": "Center", + "perimeter": "Perimeter", + "length": "Length", + "area": "Area", + "centroid": "Centroid", + "location": "Location", + "metric": "Metric", + "imperial": "Imperial" + } }, "geometry": { "point": "point", diff --git a/modules/index.js b/modules/index.js index 9277223f4..d2885a685 100644 --- a/modules/index.js +++ b/modules/index.js @@ -11,6 +11,7 @@ export * from './renderer/index'; export * from './services/index'; export * from './svg/index'; export * from './ui/fields/index'; +export * from './ui/info/index'; export * from './ui/intro/index'; export * from './ui/index'; export * from './util/index'; diff --git a/modules/ui/info.js b/modules/ui/info.js index eee4d8613..0f0bfd8db 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -1,203 +1,40 @@ import * as d3 from 'd3'; -import _ from 'lodash'; import { d3keybinding } from '../lib/d3.keybinding.js'; import { t } from '../util/locale'; -import { geoExtent } from '../geo/index'; -import { utilDetect } from '../util/detect'; import { uiCmd } from './cmd'; - -import { - geoLength as d3GeoLength, - geoCentroid as d3GeoCentroid -} from 'd3'; +import { uiInfoWidgets } from './info/index'; export function uiInfo(context) { - var isImperial = (utilDetect().locale.toLowerCase() === 'en-us'), - isHidden = true; + var isHidden = true, + ids = Object.keys(uiInfoWidgets), + widgets = {}, + current; + + // create widgets + ids.forEach(function(k) { + current = current || k; + if (!widgets[k]) { + widgets[k] = uiInfoWidgets[k](context); + } + }); + function info(selection) { - function radiansToMeters(r) { - // using WGS84 authalic radius (6371007.1809 m) - return r * 6371007.1809; - } - - function steradiansToSqmeters(r) { - // http://gis.stackexchange.com/a/124857/40446 - return r / (4 * Math.PI) * 510065621724000; - } - - - function toLineString(feature) { - if (feature.type === 'LineString') return feature; - - var result = { type: 'LineString', coordinates: [] }; - if (feature.type === 'Polygon') { - result.coordinates = feature.coordinates[0]; - } else if (feature.type === 'MultiPolygon') { - result.coordinates = feature.coordinates[0][0]; - } - - return result; - } - - - function displayLength(m) { - var d = m * (isImperial ? 3.28084 : 1), - p, unit; - - if (isImperial) { - if (d >= 5280) { - d /= 5280; - unit = 'mi'; - } else { - unit = 'ft'; - } - } else { - if (d >= 1000) { - d /= 1000; - unit = 'km'; - } else { - unit = 'm'; - } - } - - // drop unnecessary precision - p = d > 1000 ? 0 : d > 100 ? 1 : 2; - - return String(d.toFixed(p)) + ' ' + unit; - } - - - function displayArea(m2) { - var d = m2 * (isImperial ? 10.7639111056 : 1), - d1, d2, p1, p2, unit1, unit2; - - if (isImperial) { - if (d >= 6969600) { // > 0.25mi² show mi² - d1 = d / 27878400; - unit1 = 'mi²'; - } else { - d1 = d; - unit1 = 'ft²'; - } - - if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres - d2 = d / 43560; - unit2 = 'ac'; - } - - } else { - if (d >= 250000) { // > 0.25km² show km² - d1 = d / 1000000; - unit1 = 'km²'; - } else { - d1 = d; - unit1 = 'm²'; - } - - if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares - d2 = d / 10000; - unit2 = 'ha'; - } - } - - // drop unnecessary precision - p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2; - p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2; - - return String(d1.toFixed(p1)) + ' ' + unit1 + - (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : ''); - } - - function redraw() { - if (isHidden) return; + if (isHidden || !current) return; + var widget = widgets[current]; - var resolver = context.graph(), - selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), - singular = selected.length === 1 ? selected[0] : null, - extent = geoExtent(), - entity; - - wrap.html(''); - wrap.append('h4') - .attr('class', 'infobox-heading fillD') - .text(singular || t('infobox.selected', { n: selected.length })); - - if (!selected.length) return; - - var center; - for (var i = 0; i < selected.length; i++) { - entity = context.entity(selected[i]); - extent._extend(entity.extent(resolver)); - } - center = extent.center(); - - - var list = wrap.append('ul'); - - // multiple features, just display extent center.. - if (!singular) { - list.append('li') - .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); - return; - } - - // single feature, display details.. - if (!entity) return; - var geometry = entity.geometry(resolver); - - if (geometry === 'line' || geometry === 'area') { - var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()), - feature = entity.asGeoJSON(resolver), - length = radiansToMeters(d3GeoLength(toLineString(feature))), - lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')), - centroid = d3GeoCentroid(feature); - - list.append('li') - .text(t('infobox.geometry') + ': ' + - (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) ); - - if (closed) { - var area = steradiansToSqmeters(entity.area(resolver)); - list.append('li') - .text(t('infobox.area') + ': ' + displayArea(area)); - } - - list.append('li') - .text(lengthLabel + ': ' + displayLength(length)); - - list.append('li') - .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5)); - - - var toggle = isImperial ? 'imperial' : 'metric'; - wrap.append('a') - .text(t('infobox.' + toggle)) - .attr('href', '#') - .attr('class', 'button') - .on('click', function() { - d3.event.preventDefault(); - isImperial = !isImperial; - redraw(); - }); - - } else { - var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center')); - - list.append('li') - .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry)); - - list.append('li') - .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); - } + var content = selection.selectAll('.widget-content-' + current); + content.call(widget.redraw); } - function toggle() { + function toggle(setCurrent) { + current = setCurrent || current; + if (d3.event) { d3.event.preventDefault(); } @@ -237,14 +74,39 @@ export function uiInfo(context) { .style('display', (isHidden ? 'none' : 'block')) .merge(wrap); - context.map() - .on('drawn.info', redraw); + + var containers = wrap.selectAll('.widget-container') + .data(ids); + + containers.exit() + .remove(); + + var enter = containers.enter() + .append('div') + .attr('class', function(d) { return 'widget-container widget-container-' + d; }); + + enter + .append('h4') + .attr('class', 'title') + .text(function(d) { return widgets[d].title; }); + + enter + .append('div') + .attr('class', function(d) { return 'widget-content widget-content-' + d; }); + redraw(); var keybinding = d3keybinding('info') .on(uiCmd('⌘' + t('infobox.key')), toggle); + ids.forEach(function(k) { + var key = t('infobox.' + k + '.key', { default: null }); + if (!key) return; + keybinding + .on(uiCmd('⌘' + key), function() { toggle(k); }); + }); + d3.select(document) .call(keybinding); } diff --git a/modules/ui/info/index.js b/modules/ui/info/index.js new file mode 100644 index 000000000..aaa40591c --- /dev/null +++ b/modules/ui/info/index.js @@ -0,0 +1,7 @@ +export * from './measurement'; + +import { uiInfoMeasurement } from './measurement'; + +export var uiInfoWidgets = { + measurement: uiInfoMeasurement, +}; diff --git a/modules/ui/info/measurement.js b/modules/ui/info/measurement.js index 74e2ceecd..3c250026d 100644 --- a/modules/ui/info/measurement.js +++ b/modules/ui/info/measurement.js @@ -1,10 +1,8 @@ import * as d3 from 'd3'; import _ from 'lodash'; -import { d3keybinding } from '../lib/d3.keybinding.js'; -import { t } from '../util/locale'; -import { geoExtent } from '../geo/index'; -import { utilDetect } from '../util/detect'; -import { uiCmd } from './cmd'; +import { t } from '../../util/locale'; +import { geoExtent } from '../../geo'; +import { utilDetect } from '../../util/detect'; import { geoLength as d3GeoLength, @@ -13,241 +11,215 @@ import { export function uiInfoMeasurement(context) { - var isImperial = (utilDetect().locale.toLowerCase() === 'en-us'), - isHidden = true; + var isImperial = (utilDetect().locale.toLowerCase() === 'en-us'); - function info(selection) { - - function radiansToMeters(r) { - // using WGS84 authalic radius (6371007.1809 m) - return r * 6371007.1809; - } - - function steradiansToSqmeters(r) { - // http://gis.stackexchange.com/a/124857/40446 - return r / (4 * Math.PI) * 510065621724000; - } - - - function toLineString(feature) { - if (feature.type === 'LineString') return feature; - - var result = { type: 'LineString', coordinates: [] }; - if (feature.type === 'Polygon') { - result.coordinates = feature.coordinates[0]; - } else if (feature.type === 'MultiPolygon') { - result.coordinates = feature.coordinates[0][0]; - } - - return result; - } - - - function displayLength(m) { - var d = m * (isImperial ? 3.28084 : 1), - p, unit; - - if (isImperial) { - if (d >= 5280) { - d /= 5280; - unit = 'mi'; - } else { - unit = 'ft'; - } - } else { - if (d >= 1000) { - d /= 1000; - unit = 'km'; - } else { - unit = 'm'; - } - } - - // drop unnecessary precision - p = d > 1000 ? 0 : d > 100 ? 1 : 2; - - return String(d.toFixed(p)) + ' ' + unit; - } - - - function displayArea(m2) { - var d = m2 * (isImperial ? 10.7639111056 : 1), - d1, d2, p1, p2, unit1, unit2; - - if (isImperial) { - if (d >= 6969600) { // > 0.25mi² show mi² - d1 = d / 27878400; - unit1 = 'mi²'; - } else { - d1 = d; - unit1 = 'ft²'; - } - - if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres - d2 = d / 43560; - unit2 = 'ac'; - } - - } else { - if (d >= 250000) { // > 0.25km² show km² - d1 = d / 1000000; - unit1 = 'km²'; - } else { - d1 = d; - unit1 = 'm²'; - } - - if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares - d2 = d / 10000; - unit2 = 'ha'; - } - } - - // drop unnecessary precision - p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2; - p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2; - - return String(d1.toFixed(p1)) + ' ' + unit1 + - (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : ''); - } - - - function redraw() { - if (isHidden) return; - - var resolver = context.graph(), - selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), - singular = selected.length === 1 ? selected[0] : null, - extent = geoExtent(), - entity; - - wrap.html(''); - wrap.append('h4') - .attr('class', 'infobox-heading fillD') - .text(singular || t('infobox.selected', { n: selected.length })); - - if (!selected.length) return; - - var center; - for (var i = 0; i < selected.length; i++) { - entity = context.entity(selected[i]); - extent._extend(entity.extent(resolver)); - } - center = extent.center(); - - - var list = wrap.append('ul'); - - // multiple features, just display extent center.. - if (!singular) { - list.append('li') - .text(t('infobox.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); - return; - } - - // single feature, display details.. - if (!entity) return; - var geometry = entity.geometry(resolver); - - if (geometry === 'line' || geometry === 'area') { - var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()), - feature = entity.asGeoJSON(resolver), - length = radiansToMeters(d3GeoLength(toLineString(feature))), - lengthLabel = t('infobox.' + (closed ? 'perimeter' : 'length')), - centroid = d3GeoCentroid(feature); - - list.append('li') - .text(t('infobox.geometry') + ': ' + - (closed ? t('infobox.closed') + ' ' : '') + t('geometry.' + geometry) ); - - if (closed) { - var area = steradiansToSqmeters(entity.area(resolver)); - list.append('li') - .text(t('infobox.area') + ': ' + displayArea(area)); - } - - list.append('li') - .text(lengthLabel + ': ' + displayLength(length)); - - list.append('li') - .text(t('infobox.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5)); - - - var toggle = isImperial ? 'imperial' : 'metric'; - wrap.append('a') - .text(t('infobox.' + toggle)) - .attr('href', '#') - .attr('class', 'button') - .on('click', function() { - d3.event.preventDefault(); - isImperial = !isImperial; - redraw(); - }); - - } else { - var centerLabel = t('infobox.' + (entity.type === 'node' ? 'location' : 'center')); - - list.append('li') - .text(t('infobox.geometry') + ': ' + t('geometry.' + geometry)); - - list.append('li') - .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); - } - } - - - function toggle() { - if (d3.event) { - d3.event.preventDefault(); - } - - isHidden = !isHidden; - - if (isHidden) { - wrap - .style('display', 'block') - .style('opacity', 1) - .transition() - .duration(200) - .style('opacity', 0) - .on('end', function() { - d3.select(this).style('display', 'none'); - }); - } else { - wrap - .style('display', 'block') - .style('opacity', 0) - .transition() - .duration(200) - .style('opacity', 1) - .on('end', function() { - redraw(); - }); - } - } - - - var wrap = selection.selectAll('.infobox') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'infobox fillD2') - .style('display', (isHidden ? 'none' : 'block')) - .merge(wrap); - - context.map() - .on('drawn.info', redraw); - - redraw(); - - var keybinding = d3keybinding('info') - .on(uiCmd('⌘' + t('infobox.key')), toggle); - - d3.select(document) - .call(keybinding); + function radiansToMeters(r) { + // using WGS84 authalic radius (6371007.1809 m) + return r * 6371007.1809; } - return info; + function steradiansToSqmeters(r) { + // http://gis.stackexchange.com/a/124857/40446 + return r / (4 * Math.PI) * 510065621724000; + } + + + function toLineString(feature) { + if (feature.type === 'LineString') return feature; + + var result = { type: 'LineString', coordinates: [] }; + if (feature.type === 'Polygon') { + result.coordinates = feature.coordinates[0]; + } else if (feature.type === 'MultiPolygon') { + result.coordinates = feature.coordinates[0][0]; + } + + return result; + } + + + function displayLength(m) { + var d = m * (isImperial ? 3.28084 : 1), + p, unit; + + if (isImperial) { + if (d >= 5280) { + d /= 5280; + unit = 'mi'; + } else { + unit = 'ft'; + } + } else { + if (d >= 1000) { + d /= 1000; + unit = 'km'; + } else { + unit = 'm'; + } + } + + // drop unnecessary precision + p = d > 1000 ? 0 : d > 100 ? 1 : 2; + + return String(d.toFixed(p)) + ' ' + unit; + } + + + function displayArea(m2) { + var d = m2 * (isImperial ? 10.7639111056 : 1), + d1, d2, p1, p2, unit1, unit2; + + if (isImperial) { + if (d >= 6969600) { // > 0.25mi² show mi² + d1 = d / 27878400; + unit1 = 'mi²'; + } else { + d1 = d; + unit1 = 'ft²'; + } + + if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres + d2 = d / 43560; + unit2 = 'ac'; + } + + } else { + if (d >= 250000) { // > 0.25km² show km² + d1 = d / 1000000; + unit1 = 'km²'; + } else { + d1 = d; + unit1 = 'm²'; + } + + if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares + d2 = d / 10000; + unit2 = 'ha'; + } + } + + // drop unnecessary precision + p1 = d1 > 1000 ? 0 : d1 > 100 ? 1 : 2; + p2 = d2 > 1000 ? 0 : d2 > 100 ? 1 : 2; + + return String(d1.toFixed(p1)) + ' ' + unit1 + + (d2 ? ' (' + String(d2.toFixed(p2)) + ' ' + unit2 + ')' : ''); + } + + + function redraw(selection) { + + // check if visible + + var resolver = context.graph(), + selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), + singular = selected.length === 1 ? selected[0] : null, + extent = geoExtent(), + entity; + + selection.html(''); + + selection + .append('h4') + .attr('class', 'infobox-heading fillD') + .text(singular || t('infobox.measurement.selected', { n: selected.length })); + + if (!selected.length) return; + + var center; + for (var i = 0; i < selected.length; i++) { + entity = context.entity(selected[i]); + extent._extend(entity.extent(resolver)); + } + center = extent.center(); + + + var list = selection + .append('ul'); + + // multiple features, just display extent center.. + if (!singular) { + list + .append('li') + .text(t('infobox.measurement.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); + return; + } + + // single feature, display details.. + if (!entity) return; + var geometry = entity.geometry(resolver); + + if (geometry === 'line' || geometry === 'area') { + var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()), + feature = entity.asGeoJSON(resolver), + length = radiansToMeters(d3GeoLength(toLineString(feature))), + lengthLabel = t('infobox.measurement.' + (closed ? 'perimeter' : 'length')), + centroid = d3GeoCentroid(feature); + + list + .append('li') + .text(t('infobox.measurement.geometry') + ': ' + + (closed ? t('infobox.measurement.closed') + ' ' : '') + t('geometry.' + geometry) ); + + if (closed) { + var area = steradiansToSqmeters(entity.area(resolver)); + list + .append('li') + .text(t('infobox.measurement.area') + ': ' + displayArea(area)); + } + + list + .append('li') + .text(lengthLabel + ': ' + displayLength(length)); + + list + .append('li') + .text(t('infobox.measurement.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5)); + + + var toggle = isImperial ? 'imperial' : 'metric'; + + selection + .append('a') + .text(t('infobox.measurement.' + toggle)) + .attr('href', '#') + .attr('class', 'button') + .on('click', function() { + d3.event.preventDefault(); + isImperial = !isImperial; + redraw(); + }); + + } else { + var centerLabel = t('infobox.measurement.' + (entity.type === 'node' ? 'location' : 'center')); + + list + .append('li') + .text(t('infobox.measurement.geometry') + ': ' + t('geometry.' + geometry)); + + list + .append('li') + .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); + } + } + + + + var widget = {}; + + widget.id = 'measurement'; + widget.title = t('infobox.measurement.title'); + widget.key = t('infobox.measurement.key'); + + widget.redraw = function(selection) { + selection.call(redraw); + + context.map() + .on('drawn.info-measurement', function() { + selection.call(redraw); + }); + }; + + return widget; } diff --git a/modules/ui/init.js b/modules/ui/init.js index cdfb5c6d4..5a88f51cd 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -81,10 +81,7 @@ export function uiInit(context) { .call(map); content - .call(uiMapInMap(context)); - - content - .append('div') + .call(uiMapInMap(context)) .call(uiInfo(context)); bar From 5b95246157aeeceb5ac187d501438d9ab4a07774 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 26 Jun 2017 23:19:50 -0400 Subject: [PATCH 03/36] Adjust styles, don't redraw measurement info if infobox is hidden --- css/80_app.css | 11 +++++++--- modules/ui/info.js | 39 +++++++++++++++++----------------- modules/ui/info/measurement.js | 25 ++++++++++++---------- 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index e2e267712..96e1fe949 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2645,6 +2645,7 @@ img.tile-removing { /* Info Box ------------------------------------------------------- */ .infobox { + display: block; position: absolute; z-index: 1; right: 0; @@ -2655,18 +2656,22 @@ img.tile-removing { -ms-user-select: element; } -.infobox .infobox-heading { +.widget-title { + padding: 5px 10px; +} + +.widget-content-measurement .measurement-heading { display: block; border-radius: 4px 0 0 0; padding: 5px 10px; height: 30px; } -.infobox ul { +.widget-content-measurement ul { padding: 5px 10px; } -.infobox .button { +.widget-content-measurement .button { position: absolute; background: #7092ff; border-radius: 2px; diff --git a/modules/ui/info.js b/modules/ui/info.js index 0f0bfd8db..72a32e1b9 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -28,7 +28,7 @@ export function uiInfo(context) { var widget = widgets[current]; var content = selection.selectAll('.widget-content-' + current); - content.call(widget.redraw); + content.call(widget); } @@ -42,40 +42,40 @@ export function uiInfo(context) { isHidden = !isHidden; if (isHidden) { - wrap - .style('display', 'block') + infobox + .classed('hide', false) .style('opacity', 1) .transition() .duration(200) .style('opacity', 0) .on('end', function() { - d3.select(this).style('display', 'none'); + d3.select(this).classed('hide', true); }); } else { - wrap - .style('display', 'block') - .style('opacity', 0) + infobox + .classed('hide', false) + .style('opacity', 0); + + redraw(); + + infobox .transition() .duration(200) - .style('opacity', 1) - .on('end', function() { - redraw(); - }); + .style('opacity', 1); } } - var wrap = selection.selectAll('.infobox') + var infobox = selection.selectAll('.infobox') .data([0]); - wrap = wrap.enter() + infobox = infobox.enter() .append('div') - .attr('class', 'infobox fillD2') - .style('display', (isHidden ? 'none' : 'block')) - .merge(wrap); + .attr('class', 'infobox fillD2' + (isHidden ? ' hide' : '')) + .merge(infobox); - var containers = wrap.selectAll('.widget-container') + var containers = infobox.selectAll('.widget-container') .data(ids); containers.exit() @@ -86,8 +86,9 @@ export function uiInfo(context) { .attr('class', function(d) { return 'widget-container widget-container-' + d; }); enter - .append('h4') - .attr('class', 'title') + .append('div') + .attr('class', 'widget-title fillD2') + .append('h3') .text(function(d) { return widgets[d].title; }); enter diff --git a/modules/ui/info/measurement.js b/modules/ui/info/measurement.js index 3c250026d..cd86be9ee 100644 --- a/modules/ui/info/measurement.js +++ b/modules/ui/info/measurement.js @@ -109,8 +109,7 @@ export function uiInfoMeasurement(context) { function redraw(selection) { - - // check if visible + if (d3.selectAll('.infobox.hide').size()) return; // infobox is hidden var resolver = context.graph(), selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), @@ -122,7 +121,7 @@ export function uiInfoMeasurement(context) { selection .append('h4') - .attr('class', 'infobox-heading fillD') + .attr('class', 'measurement-heading') .text(singular || t('infobox.measurement.selected', { n: selected.length })); if (!selected.length) return; @@ -188,7 +187,7 @@ export function uiInfoMeasurement(context) { .on('click', function() { d3.event.preventDefault(); isImperial = !isImperial; - redraw(); + selection.call(redraw); }); } else { @@ -206,13 +205,7 @@ export function uiInfoMeasurement(context) { - var widget = {}; - - widget.id = 'measurement'; - widget.title = t('infobox.measurement.title'); - widget.key = t('infobox.measurement.key'); - - widget.redraw = function(selection) { + var widget = function(selection) { selection.call(redraw); context.map() @@ -221,5 +214,15 @@ export function uiInfoMeasurement(context) { }); }; + widget.off = function() { + context.map() + .on('drawn.info-measurement', null); + }; + + widget.id = 'measurement'; + widget.title = t('infobox.measurement.title'); + widget.key = t('infobox.measurement.key'); + + return widget; } From 01fe0db42a80f414156987996c25a975f5ece742 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 27 Jun 2017 01:54:45 -0400 Subject: [PATCH 04/36] Add close 'x' to infobox --- css/80_app.css | 17 +++++++++++++++++ modules/ui/info.js | 24 ++++++++++++++++++------ modules/ui/info/measurement.js | 7 ++++++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 96e1fe949..88b05c3e0 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2660,6 +2660,23 @@ img.tile-removing { padding: 5px 10px; } +.widget-title h3 { + display: inline-block; + margin-bottom: 0; +} + +.widget-title button.close { + float: right; + height: 20px; + background: none; + color: white; +} + +.widget-title button.close .icon { + height: 20px; + width: 16px; +} + .widget-content-measurement .measurement-heading { display: block; border-radius: 4px 0 0 0; diff --git a/modules/ui/info.js b/modules/ui/info.js index 72a32e1b9..b5f7086d1 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -1,6 +1,7 @@ import * as d3 from 'd3'; import { d3keybinding } from '../lib/d3.keybinding.js'; import { t } from '../util/locale'; +import { svgIcon } from '../svg/index'; import { uiCmd } from './cmd'; import { uiInfoWidgets } from './info/index'; @@ -33,7 +34,10 @@ export function uiInfo(context) { function toggle(setCurrent) { - current = setCurrent || current; + if (setCurrent && current !== setCurrent) { + current = setCurrent; + return; + } if (d3.event) { d3.event.preventDefault(); @@ -75,22 +79,30 @@ export function uiInfo(context) { .merge(infobox); - var containers = infobox.selectAll('.widget-container') + var container = infobox.selectAll('.widget-container') .data(ids); - containers.exit() + container.exit() .remove(); - var enter = containers.enter() + var enter = container.enter() .append('div') .attr('class', function(d) { return 'widget-container widget-container-' + d; }); - enter + var title = enter .append('div') - .attr('class', 'widget-title fillD2') + .attr('class', 'widget-title fillD2'); + + title .append('h3') .text(function(d) { return widgets[d].title; }); + title + .append('button') + .attr('class', 'close') + .on('click', function () { if (!isHidden) toggle(); }) + .call(svgIcon('#icon-close')); + enter .append('div') .attr('class', function(d) { return 'widget-content widget-content-' + d; }); diff --git a/modules/ui/info/measurement.js b/modules/ui/info/measurement.js index cd86be9ee..de033e678 100644 --- a/modules/ui/info/measurement.js +++ b/modules/ui/info/measurement.js @@ -11,7 +11,8 @@ import { export function uiInfoMeasurement(context) { - var isImperial = (utilDetect().locale.toLowerCase() === 'en-us'); + var isImperial = (utilDetect().locale.toLowerCase() === 'en-us'), + lastLength, lastSingular; function radiansToMeters(r) { @@ -117,6 +118,10 @@ export function uiInfoMeasurement(context) { extent = geoExtent(), entity; + if (selected.length === lastLength && singular === lastSingular) return; // no change + lastLength = selected.length; + lastSingular = singular; + selection.html(''); selection From 07190958a5f841b00b22af0762b4bf25e84bf4c6 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 27 Jun 2017 14:09:19 -0400 Subject: [PATCH 05/36] Force redraw of measurement box when switching between imperial/metric --- modules/ui/info/measurement.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ui/info/measurement.js b/modules/ui/info/measurement.js index de033e678..7898ffb11 100644 --- a/modules/ui/info/measurement.js +++ b/modules/ui/info/measurement.js @@ -192,6 +192,7 @@ export function uiInfoMeasurement(context) { .on('click', function() { d3.event.preventDefault(); isImperial = !isImperial; + lastLength = null; // force redraw selection.call(redraw); }); From f2d066226655899db021bbf8145262831fbedd94 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 28 Jun 2017 16:01:13 -0400 Subject: [PATCH 06/36] Add location widget (closes #2183) --- css/80_app.css | 50 +++++++++----- data/core.yaml | 4 ++ dist/locales/en.json | 5 ++ modules/ui/info.js | 130 ++++++++++++++++-------------------- modules/ui/info/index.js | 3 + modules/ui/info/location.js | 58 ++++++++++++++++ 6 files changed, 163 insertions(+), 87 deletions(-) create mode 100644 modules/ui/info/location.js diff --git a/css/80_app.css b/css/80_app.css index 88b05c3e0..87b725bf8 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2651,20 +2651,41 @@ img.tile-removing { right: 0; bottom: 30px; width: 240px; - border-radius: 4px 0 0 0; - border-bottom: 1px solid black; -ms-user-select: element; } +.infobox h1, +.infobox h2, +.infobox h3, +.infobox h4, +.infobox h5 { + display: inline-block; + margin-bottom: 0; +} + +.infobox h1, +.infobox h2, +.infobox h3 { + color: #ff8; +} + +.widget-container { + padding-bottom: 10px; +} + +.widget-container:first-of-type, +.widget-container:first-of-type .widget-title { + border-radius: 4px 0 0 0; +} + +.widget-container:last-of-type { + padding-bottom: 0px; +} + .widget-title { padding: 5px 10px; } -.widget-title h3 { - display: inline-block; - margin-bottom: 0; -} - .widget-title button.close { float: right; height: 20px; @@ -2677,16 +2698,15 @@ img.tile-removing { width: 16px; } -.widget-content-measurement .measurement-heading { - display: block; - border-radius: 4px 0 0 0; +.widget-content { padding: 5px 10px; - height: 30px; + position: relative; } -.widget-content-measurement ul { - padding: 5px 10px; -} +.widget-content-measurement .measurement-heading { +/* display: inline-block; + height: 30px; +*/} .widget-content-measurement .button { position: absolute; @@ -2694,7 +2714,7 @@ img.tile-removing { border-radius: 2px; padding: 0 4px; color: white; - top: 40px; + top: 6px; right: 10px; } diff --git a/data/core.yaml b/data/core.yaml index 3c81c9dda..c5219023c 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -274,6 +274,10 @@ en: truncated_list: "Edits by {users} and {count} others" infobox: key: I + location: + key: L + title: Location + pointer: Pointer measurement: key: M title: Measurement diff --git a/dist/locales/en.json b/dist/locales/en.json index fd9719b20..e3140f0f8 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -350,6 +350,11 @@ }, "infobox": { "key": "I", + "location": { + "key": "L", + "title": "Location", + "pointer": "Pointer" + }, "measurement": { "key": "M", "title": "Measurement", diff --git a/modules/ui/info.js b/modules/ui/info.js index b5f7086d1..7c4046477 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -7,66 +7,82 @@ import { uiInfoWidgets } from './info/index'; export function uiInfo(context) { - var isHidden = true, - ids = Object.keys(uiInfoWidgets), + var ids = Object.keys(uiInfoWidgets), widgets = {}, - current; + active = {}; // create widgets ids.forEach(function(k) { - current = current || k; if (!widgets[k]) { widgets[k] = uiInfoWidgets[k](context); + active[k] = false; } }); - function info(selection) { function redraw() { - if (isHidden || !current) return; - var widget = widgets[current]; + var activeids = ids.filter(function(k) { return active[k]; }); - var content = selection.selectAll('.widget-content-' + current); - content.call(widget); + var containers = infobox.selectAll('.widget-container') + .data(activeids, function(k) { return k; }); + + containers.exit() + .style('opacity', 1) + .transition() + .duration(200) + .style('opacity', 0) + .on('end', function(d) { + d3.select(this) + .call(widgets[d].off) + .remove(); + }); + + var enter = containers.enter() + .append('div') + .attr('class', function(d) { return 'fillD2 widget-container widget-container-' + d; }); + + enter + .style('opacity', 0) + .transition() + .duration(200) + .style('opacity', 1); + + var title = enter + .append('div') + .attr('class', 'widget-title fillD2'); + + title + .append('h3') + .text(function(d) { return widgets[d].title; }); + + title + .append('button') + .attr('class', 'close') + .on('click', function (d) { toggle(d); }) + .call(svgIcon('#icon-close')); + + enter + .append('div') + .attr('class', function(d) { return 'widget-content widget-content-' + d; }); + + + // redraw the widgets + infobox.selectAll('.widget-content') + .each(function(d) { + d3.select(this).call(widgets[d]); + }); } - function toggle(setCurrent) { - if (setCurrent && current !== setCurrent) { - current = setCurrent; - return; - } + function toggle(which) { + if (d3.event) d3.event.preventDefault(); - if (d3.event) { - d3.event.preventDefault(); - } + if (!which) which = 'measurement'; + active[which] = !active[which]; - isHidden = !isHidden; - - if (isHidden) { - infobox - .classed('hide', false) - .style('opacity', 1) - .transition() - .duration(200) - .style('opacity', 0) - .on('end', function() { - d3.select(this).classed('hide', true); - }); - } else { - infobox - .classed('hide', false) - .style('opacity', 0); - - redraw(); - - infobox - .transition() - .duration(200) - .style('opacity', 1); - } + redraw(); } @@ -75,39 +91,9 @@ export function uiInfo(context) { infobox = infobox.enter() .append('div') - .attr('class', 'infobox fillD2' + (isHidden ? ' hide' : '')) + .attr('class', 'infobox') .merge(infobox); - - var container = infobox.selectAll('.widget-container') - .data(ids); - - container.exit() - .remove(); - - var enter = container.enter() - .append('div') - .attr('class', function(d) { return 'widget-container widget-container-' + d; }); - - var title = enter - .append('div') - .attr('class', 'widget-title fillD2'); - - title - .append('h3') - .text(function(d) { return widgets[d].title; }); - - title - .append('button') - .attr('class', 'close') - .on('click', function () { if (!isHidden) toggle(); }) - .call(svgIcon('#icon-close')); - - enter - .append('div') - .attr('class', function(d) { return 'widget-content widget-content-' + d; }); - - redraw(); var keybinding = d3keybinding('info') diff --git a/modules/ui/info/index.js b/modules/ui/info/index.js index aaa40591c..093f322b6 100644 --- a/modules/ui/info/index.js +++ b/modules/ui/info/index.js @@ -1,7 +1,10 @@ +export * from './location'; export * from './measurement'; +import { uiInfoLocation } from './location'; import { uiInfoMeasurement } from './measurement'; export var uiInfoWidgets = { + location: uiInfoLocation, measurement: uiInfoMeasurement, }; diff --git a/modules/ui/info/location.js b/modules/ui/info/location.js new file mode 100644 index 000000000..fe63eb70b --- /dev/null +++ b/modules/ui/info/location.js @@ -0,0 +1,58 @@ +import * as d3 from 'd3'; +import { t } from '../../util/locale'; + + +export function uiInfoLocation(context) { + var OSM_PRECISION = 7; + + function wrap(x, min, max) { + var d = max - min; + return ((x - min) % d + d) % d + min; + } + + function clamp(x, min, max) { + return Math.max(min, Math.min(x, max)); + } + + + function redraw(selection) { + if (d3.selectAll('.infobox.hide').size()) return; // infobox is hidden + + selection.html(''); + + var list = selection + .append('ul'); + + // Mouse coordinates + var coord = context.map().mouseCoordinates(); + var coordStr = + clamp(coord[1], -90, 90).toFixed(OSM_PRECISION) + ', ' + + wrap(coord[0], -180, 180).toFixed(OSM_PRECISION); + + list + .append('li') + .text(t('infobox.location.pointer') + ': ' + coordStr); + } + + + var widget = function(selection) { + selection.call(redraw); + + context.surface() + .on('mousemove.info-location', function() { + selection.call(redraw); + }); + }; + + widget.off = function() { + context.surface() + .on('mousemove.info-location', null); + }; + + widget.id = 'location'; + widget.title = t('infobox.location.title'); + widget.key = t('infobox.location.key'); + + + return widget; +} From b12fad8d9739a044a8383f35d2b1fba9b7cf5f7a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 28 Jun 2017 16:24:47 -0400 Subject: [PATCH 07/36] Remove code that prevented draws, report all numbers at OSM precision --- modules/ui/info/measurement.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/modules/ui/info/measurement.js b/modules/ui/info/measurement.js index 7898ffb11..044fde4b5 100644 --- a/modules/ui/info/measurement.js +++ b/modules/ui/info/measurement.js @@ -2,6 +2,7 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { t } from '../../util/locale'; import { geoExtent } from '../../geo'; +import { osmEntity } from '../../osm'; import { utilDetect } from '../../util/detect'; import { @@ -11,8 +12,8 @@ import { export function uiInfoMeasurement(context) { - var isImperial = (utilDetect().locale.toLowerCase() === 'en-us'), - lastLength, lastSingular; + var isImperial = (utilDetect().locale.toLowerCase() === 'en-us'); + var OSM_PRECISION = 7; function radiansToMeters(r) { @@ -115,13 +116,10 @@ export function uiInfoMeasurement(context) { var resolver = context.graph(), selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), singular = selected.length === 1 ? selected[0] : null, + singularKey = singular && osmEntity.key(context.entity(singular)), extent = geoExtent(), entity; - if (selected.length === lastLength && singular === lastSingular) return; // no change - lastLength = selected.length; - lastSingular = singular; - selection.html(''); selection @@ -146,7 +144,9 @@ export function uiInfoMeasurement(context) { if (!singular) { list .append('li') - .text(t('infobox.measurement.center') + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); + .text(t('infobox.measurement.center') + ': ' + + center[0].toFixed(OSM_PRECISION) + ', ' + center[1].toFixed(OSM_PRECISION) + ); return; } @@ -179,7 +179,9 @@ export function uiInfoMeasurement(context) { list .append('li') - .text(t('infobox.measurement.centroid') + ': ' + centroid[0].toFixed(5) + ', ' + centroid[1].toFixed(5)); + .text(t('infobox.measurement.centroid') + ': ' + + centroid[0].toFixed(OSM_PRECISION) + ', ' + centroid[1].toFixed(OSM_PRECISION) + ); var toggle = isImperial ? 'imperial' : 'metric'; @@ -192,7 +194,6 @@ export function uiInfoMeasurement(context) { .on('click', function() { d3.event.preventDefault(); isImperial = !isImperial; - lastLength = null; // force redraw selection.call(redraw); }); @@ -205,7 +206,9 @@ export function uiInfoMeasurement(context) { list .append('li') - .text(centerLabel + ': ' + center[0].toFixed(5) + ', ' + center[1].toFixed(5)); + .text(centerLabel + ': ' + + center[0].toFixed(OSM_PRECISION) + ', ' + center[1].toFixed(OSM_PRECISION) + ); } } From 63f50a7499e69533af6fbb11bb5ff7c0561cb522 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 28 Jun 2017 16:32:48 -0400 Subject: [PATCH 08/36] Hoverstyle for widget close 'x' buttons --- css/80_app.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/css/80_app.css b/css/80_app.css index 87b725bf8..a81a60b49 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2690,7 +2690,11 @@ img.tile-removing { float: right; height: 20px; background: none; - color: white; + color: #ddd; +} + +.widget-title button.close:hover { + color: #fff; } .widget-title button.close .icon { From 991eb58bcc7ad39a62f70dc2f0772f277b74df0a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 28 Jun 2017 17:17:34 -0400 Subject: [PATCH 09/36] Remove unused singularKey --- modules/ui/info/measurement.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/ui/info/measurement.js b/modules/ui/info/measurement.js index 044fde4b5..15b65b245 100644 --- a/modules/ui/info/measurement.js +++ b/modules/ui/info/measurement.js @@ -2,7 +2,6 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { t } from '../../util/locale'; import { geoExtent } from '../../geo'; -import { osmEntity } from '../../osm'; import { utilDetect } from '../../util/detect'; import { @@ -116,7 +115,6 @@ export function uiInfoMeasurement(context) { var resolver = context.graph(), selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), singular = selected.length === 1 ? selected[0] : null, - singularKey = singular && osmEntity.key(context.entity(singular)), extent = geoExtent(), entity; From ece73a9be455799f8248a404236520eaa181cb88 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 28 Jun 2017 17:21:15 -0400 Subject: [PATCH 10/36] =?UTF-8?q?Infobox=20=E2=8C=98I=20now=20toggle=20all?= =?UTF-8?q?=20widgets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ui/info.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/modules/ui/info.js b/modules/ui/info.js index 7c4046477..02e8cf62a 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -8,6 +8,7 @@ import { uiInfoWidgets } from './info/index'; export function uiInfo(context) { var ids = Object.keys(uiInfoWidgets), + wasActive = ['measurement'], widgets = {}, active = {}; @@ -23,7 +24,7 @@ export function uiInfo(context) { function info(selection) { function redraw() { - var activeids = ids.filter(function(k) { return active[k]; }); + var activeids = ids.filter(function(k) { return active[k]; }).sort(); var containers = infobox.selectAll('.widget-container') .data(activeids, function(k) { return k; }); @@ -79,8 +80,21 @@ export function uiInfo(context) { function toggle(which) { if (d3.event) d3.event.preventDefault(); - if (!which) which = 'measurement'; - active[which] = !active[which]; + var activeids = ids.filter(function(k) { return active[k]; }); + + if (which) { // toggle one + active[which] = !active[which]; + if (activeids.length === 1 && activeids[0] === which) { // none active anymore + wasActive = [which]; + } + } else { // toggle all + if (activeids.length) { + wasActive = activeids; + activeids.forEach(function(k) { active[k] = false; }); + } else { + wasActive.forEach(function(k) { active[k] = true; }); + } + } redraw(); } From 9a0deaa484897a71c018c9167836d82801f0c566 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 28 Jun 2017 22:34:03 -0400 Subject: [PATCH 11/36] Rename widget -> panel --- css/80_app.css | 25 ++++++++++--------------- modules/ui/info.js | 30 +++++++++++++++--------------- modules/ui/info/index.js | 10 +++++----- modules/ui/info/location.js | 14 +++++++------- modules/ui/info/measurement.js | 14 +++++++------- 5 files changed, 44 insertions(+), 49 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index a81a60b49..727f5ba44 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2669,50 +2669,45 @@ img.tile-removing { color: #ff8; } -.widget-container { +.panel-container { padding-bottom: 10px; } -.widget-container:first-of-type, -.widget-container:first-of-type .widget-title { +.panel-container:first-of-type, +.panel-container:first-of-type .panel-title { border-radius: 4px 0 0 0; } -.widget-container:last-of-type { +.panel-container:last-of-type { padding-bottom: 0px; } -.widget-title { +.panel-title { padding: 5px 10px; } -.widget-title button.close { +.panel-title button.close { float: right; height: 20px; background: none; color: #ddd; } -.widget-title button.close:hover { +.panel-title button.close:hover { color: #fff; } -.widget-title button.close .icon { +.panel-title button.close .icon { height: 20px; width: 16px; } -.widget-content { +.panel-content { padding: 5px 10px; position: relative; } -.widget-content-measurement .measurement-heading { -/* display: inline-block; - height: 30px; -*/} - -.widget-content-measurement .button { +.panel-content-measurement .button { position: absolute; background: #7092ff; border-radius: 2px; diff --git a/modules/ui/info.js b/modules/ui/info.js index 02e8cf62a..426459bb9 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -3,19 +3,19 @@ import { d3keybinding } from '../lib/d3.keybinding.js'; import { t } from '../util/locale'; import { svgIcon } from '../svg/index'; import { uiCmd } from './cmd'; -import { uiInfoWidgets } from './info/index'; +import { uiInfoPanels } from './info/index'; export function uiInfo(context) { - var ids = Object.keys(uiInfoWidgets), + var ids = Object.keys(uiInfoPanels), wasActive = ['measurement'], - widgets = {}, + panels = {}, active = {}; - // create widgets + // create panels ids.forEach(function(k) { - if (!widgets[k]) { - widgets[k] = uiInfoWidgets[k](context); + if (!panels[k]) { + panels[k] = uiInfoPanels[k](context); active[k] = false; } }); @@ -26,7 +26,7 @@ export function uiInfo(context) { function redraw() { var activeids = ids.filter(function(k) { return active[k]; }).sort(); - var containers = infobox.selectAll('.widget-container') + var containers = infobox.selectAll('.panel-container') .data(activeids, function(k) { return k; }); containers.exit() @@ -36,13 +36,13 @@ export function uiInfo(context) { .style('opacity', 0) .on('end', function(d) { d3.select(this) - .call(widgets[d].off) + .call(panels[d].off) .remove(); }); var enter = containers.enter() .append('div') - .attr('class', function(d) { return 'fillD2 widget-container widget-container-' + d; }); + .attr('class', function(d) { return 'fillD2 panel-container panel-container-' + d; }); enter .style('opacity', 0) @@ -52,11 +52,11 @@ export function uiInfo(context) { var title = enter .append('div') - .attr('class', 'widget-title fillD2'); + .attr('class', 'panel-title fillD2'); title .append('h3') - .text(function(d) { return widgets[d].title; }); + .text(function(d) { return panels[d].title; }); title .append('button') @@ -66,13 +66,13 @@ export function uiInfo(context) { enter .append('div') - .attr('class', function(d) { return 'widget-content widget-content-' + d; }); + .attr('class', function(d) { return 'panel-content panel-content-' + d; }); - // redraw the widgets - infobox.selectAll('.widget-content') + // redraw the panels + infobox.selectAll('.panel-content') .each(function(d) { - d3.select(this).call(widgets[d]); + d3.select(this).call(panels[d]); }); } diff --git a/modules/ui/info/index.js b/modules/ui/info/index.js index 093f322b6..9fc7475ac 100644 --- a/modules/ui/info/index.js +++ b/modules/ui/info/index.js @@ -1,10 +1,10 @@ export * from './location'; export * from './measurement'; -import { uiInfoLocation } from './location'; -import { uiInfoMeasurement } from './measurement'; +import { uiPanelLocation } from './location'; +import { uiPanelMeasurement } from './measurement'; -export var uiInfoWidgets = { - location: uiInfoLocation, - measurement: uiInfoMeasurement, +export var uiInfoPanels = { + location: uiPanelLocation, + measurement: uiPanelMeasurement, }; diff --git a/modules/ui/info/location.js b/modules/ui/info/location.js index fe63eb70b..1de1ef6b9 100644 --- a/modules/ui/info/location.js +++ b/modules/ui/info/location.js @@ -2,7 +2,7 @@ import * as d3 from 'd3'; import { t } from '../../util/locale'; -export function uiInfoLocation(context) { +export function uiPanelLocation(context) { var OSM_PRECISION = 7; function wrap(x, min, max) { @@ -35,7 +35,7 @@ export function uiInfoLocation(context) { } - var widget = function(selection) { + var panel = function(selection) { selection.call(redraw); context.surface() @@ -44,15 +44,15 @@ export function uiInfoLocation(context) { }); }; - widget.off = function() { + panel.off = function() { context.surface() .on('mousemove.info-location', null); }; - widget.id = 'location'; - widget.title = t('infobox.location.title'); - widget.key = t('infobox.location.key'); + panel.id = 'location'; + panel.title = t('infobox.location.title'); + panel.key = t('infobox.location.key'); - return widget; + return panel; } diff --git a/modules/ui/info/measurement.js b/modules/ui/info/measurement.js index 15b65b245..35afd1e6d 100644 --- a/modules/ui/info/measurement.js +++ b/modules/ui/info/measurement.js @@ -10,7 +10,7 @@ import { } from 'd3'; -export function uiInfoMeasurement(context) { +export function uiPanelMeasurement(context) { var isImperial = (utilDetect().locale.toLowerCase() === 'en-us'); var OSM_PRECISION = 7; @@ -212,7 +212,7 @@ export function uiInfoMeasurement(context) { - var widget = function(selection) { + var panel = function(selection) { selection.call(redraw); context.map() @@ -221,15 +221,15 @@ export function uiInfoMeasurement(context) { }); }; - widget.off = function() { + panel.off = function() { context.map() .on('drawn.info-measurement', null); }; - widget.id = 'measurement'; - widget.title = t('infobox.measurement.title'); - widget.key = t('infobox.measurement.key'); + panel.id = 'measurement'; + panel.title = t('infobox.measurement.title'); + panel.key = t('infobox.measurement.key'); - return widget; + return panel; } From 1c303edf18eb90710bdf139b50a52446a86a56a0 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 29 Jun 2017 01:23:33 -0400 Subject: [PATCH 12/36] Add generic reverse geocoder function to nominatim service --- modules/services/nominatim.js | 34 ++++++++++++++++++++---------- test/spec/services/nominatim.js | 37 ++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/modules/services/nominatim.js b/modules/services/nominatim.js index b5434306a..4eb13eedd 100644 --- a/modules/services/nominatim.js +++ b/modules/services/nominatim.js @@ -25,32 +25,44 @@ export default { countryCode: function (location, callback) { - var countryCodes = nominatimCache.search( + this.reverse(location, function(err, result) { + if (err) { + return callback(err); + } else if (result.address) { + return callback(null, result.address.country_code); + } else { + return callback('Unable to geocode', null); + } + }); + }, + + + reverse: function (location, callback) { + var cached = nominatimCache.search( { minX: location[0], minY: location[1], maxX: location[0], maxY: location[1] } ); - if (countryCodes.length > 0) { - return callback(null, countryCodes[0].data); + if (cached.length > 0) { + return callback(null, cached[0].data); } - var params = { format: 'json', addressdetails: 1, lat: location[1], lon: location[0] }; + var params = { zoom: 13, format: 'json', addressdetails: 1, lat: location[1], lon: location[0] }; var url = apibase + 'reverse?' + utilQsString(params); if (inflight[url]) return; inflight[url] = d3.json(url, function(err, result) { delete inflight[url]; - if (err) + if (err) { return callback(err); - else if (result && result.error) + } else if (result && result.error) { return callback(result.error); + } - var extent = geoExtent(location).padByMeters(1000); - nominatimCache.insert(_.assign(extent.bbox(), - { data: result.address.country_code } - )); + var extent = geoExtent(location).padByMeters(200); + nominatimCache.insert(_.assign(extent.bbox(), {data: result})); - callback(null, result.address.country_code); + callback(null, result); }); }, diff --git a/test/spec/services/nominatim.js b/test/spec/services/nominatim.js index 554479834..37e5a60ee 100644 --- a/test/spec/services/nominatim.js +++ b/test/spec/services/nominatim.js @@ -17,7 +17,6 @@ describe('iD.serviceNominatim', function() { describe('#countryCode', function() { - it('calls the given callback with the results of the country code query', function() { var callback = sinon.spy(); nominatim.countryCode([16, 48], callback); @@ -28,13 +27,15 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '48', lon: '16'}); + {zoom: '13', format: 'json', addressdetails: '1', lat: '48', lon: '16'}); expect(callback).to.have.been.calledWithExactly(null, 'at'); }); + }); - it('should not cache the first country code result', function() { + describe('#reverse', function() { + it('should not cache distant result', function() { var callback = sinon.spy(); - nominatim.countryCode([16, 48], callback); + nominatim.reverse([16, 48], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, @@ -42,13 +43,14 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '48', lon: '16'}); - expect(callback).to.have.been.calledWithExactly(null, 'at'); + {zoom: '13', format: 'json', addressdetails: '1', lat: '48', lon: '16'}); + expect(callback).to.have.been.calledWithExactly(null, {address: {country_code:'at'}}); server.restore(); server = sinon.fakeServer.create(); - nominatim.countryCode([17, 49], callback); + callback = sinon.spy(); + nominatim.reverse([17, 49], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, @@ -56,13 +58,13 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '49', lon: '17'}); - expect(callback).to.have.been.calledWithExactly(null, 'cz'); + {zoom: '13', format: 'json', addressdetails: '1', lat: '49', lon: '17'}); + expect(callback).to.have.been.calledWithExactly(null, {address: {country_code:'cz'}}); }); - it('should cache the first country code result', function() { + it('should cache nearby result', function() { var callback = sinon.spy(); - nominatim.countryCode([16, 48], callback); + nominatim.reverse([16, 48], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, @@ -70,24 +72,25 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '48', lon: '16'}); - expect(callback).to.have.been.calledWithExactly(null, 'at'); + {zoom: '13', format: 'json', addressdetails: '1', lat: '48', lon: '16'}); + expect(callback).to.have.been.calledWithExactly(null, {address: {country_code:'at'}}); server.restore(); server = sinon.fakeServer.create(); - nominatim.countryCode([16.01, 48.01], callback); + callback = sinon.spy(); + nominatim.reverse([16.000001, 48.000001], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, '{"address":{"country_code":"cz"}}']); server.respond(); - expect(callback).to.have.been.calledWithExactly(null, 'at'); + expect(callback).to.have.been.calledWithExactly(null, {address: {country_code:'at'}}); }); it('calls the given callback with an error', function() { var callback = sinon.spy(); - nominatim.countryCode([1000, 1000], callback); + nominatim.reverse([1000, 1000], callback); server.respondWith('GET', new RegExp('https://nominatim.openstreetmap.org/reverse'), [200, { 'Content-Type': 'application/json' }, @@ -95,7 +98,7 @@ describe('iD.serviceNominatim', function() { server.respond(); expect(query(server.requests[0].url)).to.eql( - {format: 'json', addressdetails: '1', lat: '1000', lon: '1000'}); + {zoom: '13', format: 'json', addressdetails: '1', lat: '1000', lon: '1000'}); expect(callback).to.have.been.calledWithExactly('Unable to geocode'); }); }); From 79466e2893e57e83329c384b6ca3fe8abe55246a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 29 Jun 2017 01:24:09 -0400 Subject: [PATCH 13/36] Display reverse geolocated location in location panel (closes #2515) --- css/80_app.css | 4 ++++ data/core.yaml | 1 - dist/locales/en.json | 3 +-- modules/ui/info/location.js | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 727f5ba44..68bdd3b6f 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2707,6 +2707,10 @@ img.tile-removing { position: relative; } +.panel-content-location .location-name { + padding-top: 10px; +} + .panel-content-measurement .button { position: absolute; background: #7092ff; diff --git a/data/core.yaml b/data/core.yaml index c5219023c..2a044289f 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -277,7 +277,6 @@ en: location: key: L title: Location - pointer: Pointer measurement: key: M title: Measurement diff --git a/dist/locales/en.json b/dist/locales/en.json index e3140f0f8..d6f11033f 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -352,8 +352,7 @@ "key": "I", "location": { "key": "L", - "title": "Location", - "pointer": "Pointer" + "title": "Location" }, "measurement": { "key": "M", diff --git a/modules/ui/info/location.js b/modules/ui/info/location.js index 1de1ef6b9..056e8ec6f 100644 --- a/modules/ui/info/location.js +++ b/modules/ui/info/location.js @@ -1,15 +1,21 @@ import * as d3 from 'd3'; +import _ from 'lodash'; import { t } from '../../util/locale'; +import { services } from '../../services'; export function uiPanelLocation(context) { + var lastLocation = ''; + var debouncedUpdate = _.debounce(updateLocation, 250); var OSM_PRECISION = 7; + function wrap(x, min, max) { var d = max - min; return ((x - min) % d + d) % d + min; } + function clamp(x, min, max) { return Math.max(min, Math.min(x, max)); } @@ -25,13 +31,39 @@ export function uiPanelLocation(context) { // Mouse coordinates var coord = context.map().mouseCoordinates(); + if (coord.some(isNaN)) { + coord = context.map().center(); + } + var coordStr = clamp(coord[1], -90, 90).toFixed(OSM_PRECISION) + ', ' + wrap(coord[0], -180, 180).toFixed(OSM_PRECISION); list .append('li') - .text(t('infobox.location.pointer') + ': ' + coordStr); + .text(coordStr); + + // Location Name + if (services.geocoder) { + selection + .append('p') + .attr('class', 'location-name') + .text(lastLocation); + + debouncedUpdate(selection, coord); + } + } + + + function updateLocation(selection, coord) { + if (!services.geocoder) return; + services.geocoder.reverse(coord, function(err, result) { + if (result) { + lastLocation = result.display_name; + selection.selectAll('.location-name') + .text(lastLocation); + } + }); } From fdd6b329a8a4ca542779da340de59f7b8a06d793 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 29 Jun 2017 22:02:54 -0400 Subject: [PATCH 14/36] Get changeset, timestamp, uid for all elements; changeset_count for users --- modules/services/osm.js | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/modules/services/osm.js b/modules/services/osm.js index de1d977e6..b5013e4df 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -104,11 +104,14 @@ var parsers = { var attrs = obj.attributes; return new osmNode({ id: osmEntity.id.fromOSM('node', attrs.id.value), - loc: getLoc(attrs), + visible: getVisible(attrs), version: attrs.version.value, + changeset: attrs.changeset && attrs.changeset.value, + timestamp: attrs.timestamp && attrs.timestamp.value, user: attrs.user && attrs.user.value, - tags: getTags(obj), - visible: getVisible(attrs) + uid: attrs.uid && attrs.uid.value, + loc: getLoc(attrs), + tags: getTags(obj) }); }, @@ -116,11 +119,14 @@ var parsers = { var attrs = obj.attributes; return new osmWay({ id: osmEntity.id.fromOSM('way', attrs.id.value), + visible: getVisible(attrs), version: attrs.version.value, + changeset: attrs.changeset && attrs.changeset.value, + timestamp: attrs.timestamp && attrs.timestamp.value, user: attrs.user && attrs.user.value, + uid: attrs.uid && attrs.uid.value, tags: getTags(obj), nodes: getNodes(obj), - visible: getVisible(attrs) }); }, @@ -128,11 +134,14 @@ var parsers = { var attrs = obj.attributes; return new osmRelation({ id: osmEntity.id.fromOSM('relation', attrs.id.value), + visible: getVisible(attrs), version: attrs.version.value, + changeset: attrs.changeset && attrs.changeset.value, + timestamp: attrs.timestamp && attrs.timestamp.value, user: attrs.user && attrs.user.value, + uid: attrs.uid && attrs.uid.value, tags: getTags(obj), - members: getMembers(obj), - visible: getVisible(attrs) + members: getMembers(obj) }); } }; @@ -347,10 +356,18 @@ export default { image_url = img[0].getAttribute('href'); } + var changesets = u.getElementsByTagName('changesets'), + changesets_count = 0; + + if (changesets && changesets[0] && changesets[0].getAttribute('count')) { + changesets_count = changesets[0].getAttribute('count'); + } + userDetails = { + id: u.attributes.id.value, display_name: u.attributes.display_name.value, image_url: image_url, - id: u.attributes.id.value + changesets_count: changesets_count }; callback(undefined, userDetails); From 92a057bf3c0d805679fa05256a524061beebc4c5 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 29 Jun 2017 23:04:57 -0400 Subject: [PATCH 15/36] git mv modules/ui/info -> modules/ui/panels --- modules/index.js | 2 +- modules/ui/info.js | 4 ++-- modules/ui/{info => panels}/index.js | 0 modules/ui/{info => panels}/location.js | 0 modules/ui/{info => panels}/measurement.js | 0 5 files changed, 3 insertions(+), 3 deletions(-) rename modules/ui/{info => panels}/index.js (100%) rename modules/ui/{info => panels}/location.js (100%) rename modules/ui/{info => panels}/measurement.js (100%) diff --git a/modules/index.js b/modules/index.js index d2885a685..7b79ecf9f 100644 --- a/modules/index.js +++ b/modules/index.js @@ -11,8 +11,8 @@ export * from './renderer/index'; export * from './services/index'; export * from './svg/index'; export * from './ui/fields/index'; -export * from './ui/info/index'; export * from './ui/intro/index'; +export * from './ui/panels/index'; export * from './ui/index'; export * from './util/index'; export * from './lib/index'; diff --git a/modules/ui/info.js b/modules/ui/info.js index 426459bb9..9452b0f30 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -1,9 +1,9 @@ import * as d3 from 'd3'; import { d3keybinding } from '../lib/d3.keybinding.js'; import { t } from '../util/locale'; -import { svgIcon } from '../svg/index'; +import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; -import { uiInfoPanels } from './info/index'; +import { uiInfoPanels } from './panels/index'; export function uiInfo(context) { diff --git a/modules/ui/info/index.js b/modules/ui/panels/index.js similarity index 100% rename from modules/ui/info/index.js rename to modules/ui/panels/index.js diff --git a/modules/ui/info/location.js b/modules/ui/panels/location.js similarity index 100% rename from modules/ui/info/location.js rename to modules/ui/panels/location.js diff --git a/modules/ui/info/measurement.js b/modules/ui/panels/measurement.js similarity index 100% rename from modules/ui/info/measurement.js rename to modules/ui/panels/measurement.js From b6c2645409c1b1c29fa7469c863573fb06256c76 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 30 Jun 2017 00:52:05 -0400 Subject: [PATCH 16/36] Add history urls to osm service and more URL tests --- modules/services/osm.js | 5 +++++ test/spec/services/osm.js | 40 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/modules/services/osm.js b/modules/services/osm.js index b5013e4df..a395f85cd 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -203,6 +203,11 @@ export default { }, + historyURL: function(entity) { + return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history'; + }, + + userURL: function(username) { return urlroot + '/user/' + username; }, diff --git a/test/spec/services/osm.js b/test/spec/services/osm.js index 4644b310b..259a0cc7d 100644 --- a/test/spec/services/osm.js +++ b/test/spec/services/osm.js @@ -39,12 +39,50 @@ describe('iD.serviceOsm', function () { expect(connection.changesetURL(2)).to.match(/^https:/); }); - describe('#changesetUrl', function() { + describe('#changesetURL', function() { it('provides a changeset url', function() { expect(connection.changesetURL(2)).to.eql('http://www.openstreetmap.org/changeset/2'); }); }); + describe('#changesetsURL', function() { + it('provides a local changesets url', function() { + var center = [-74.65, 40.65]; + var zoom = 17; + expect(connection.changesetsURL(center, zoom)).to.eql('http://www.openstreetmap.org/history#map=17/40.65000/-74.65000'); + }); + }); + + describe('#entityURL', function() { + it('provides an entity url for a node', function() { + var e = iD.Node({id: 'n1'}); + expect(connection.entityURL(e)).to.eql('http://www.openstreetmap.org/node/1'); + }); + it('provides an entity url for a way', function() { + var e = iD.Way({id: 'w1'}); + expect(connection.entityURL(e)).to.eql('http://www.openstreetmap.org/way/1'); + }); + it('provides an entity url for a relation', function() { + var e = iD.Relation({id: 'r1'}); + expect(connection.entityURL(e)).to.eql('http://www.openstreetmap.org/relation/1'); + }); + }); + + describe('#historyURL', function() { + it('provides a history url for a node', function() { + var e = iD.Node({id: 'n1'}); + expect(connection.historyURL(e)).to.eql('http://www.openstreetmap.org/node/1/history'); + }); + it('provides a history url for a way', function() { + var e = iD.Way({id: 'w1'}); + expect(connection.historyURL(e)).to.eql('http://www.openstreetmap.org/way/1/history'); + }); + it('provides a history url for a relation', function() { + var e = iD.Relation({id: 'r1'}); + expect(connection.historyURL(e)).to.eql('http://www.openstreetmap.org/relation/1/history'); + }); + }); + describe('#userURL', function() { it('provides a user url', function() { expect(connection.userURL('bob')).to.eql('http://www.openstreetmap.org/user/bob'); From 946432d6f1c17876a98d177a3668ea6e9d3ada88 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 30 Jun 2017 02:09:56 -0400 Subject: [PATCH 17/36] Add history panel (closes #2273) (closes #3761) --- css/80_app.css | 13 +++ data/core.yaml | 10 +++ dist/locales/en.json | 11 +++ modules/ui/panels/history.js | 161 +++++++++++++++++++++++++++++++++++ modules/ui/panels/index.js | 3 + 5 files changed, 198 insertions(+) create mode 100644 modules/ui/panels/history.js diff --git a/css/80_app.css b/css/80_app.css index 68bdd3b6f..2a1e3184f 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2707,6 +2707,19 @@ img.tile-removing { position: relative; } +.panel-content-history .links a { + padding-left: 10px; +} +[dir='rtl'] .panel-content-history .links a { + padding-left: auto; + padding-right: 10px; +} + +.panel-content-history .view-history-on-osm { + display: block; + padding: 10px 0; +} + .panel-content-location .location-name { padding-top: 10px; } diff --git a/data/core.yaml b/data/core.yaml index 2a044289f..e9518957b 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -274,6 +274,16 @@ en: truncated_list: "Edits by {users} and {count} others" infobox: key: I + history: + key: H + title: History + selected: "{n} selected" + version: Version + last_edit: Last Edit + edited_by: Edited By + changeset: Changeset + unknown: Unknown + link_text: History on openstreetmap.org location: key: L title: Location diff --git a/dist/locales/en.json b/dist/locales/en.json index d6f11033f..13ca2d42f 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -350,6 +350,17 @@ }, "infobox": { "key": "I", + "history": { + "key": "H", + "title": "History", + "selected": "{n} selected", + "version": "Version", + "last_edit": "Last Edit", + "edited_by": "Edited By", + "changeset": "Changeset", + "unknown": "Unknown", + "link_text": "History on openstreetmap.org" + }, "location": { "key": "L", "title": "Location" diff --git a/modules/ui/panels/history.js b/modules/ui/panels/history.js new file mode 100644 index 000000000..e5bea1d95 --- /dev/null +++ b/modules/ui/panels/history.js @@ -0,0 +1,161 @@ +import * as d3 from 'd3'; +import _ from 'lodash'; +import { t } from '../../util/locale'; +import { svgIcon } from '../../svg'; + + +export function uiPanelHistory(context) { + + + function displayTimestamp(entity) { + if (!entity.timestamp) return t('infobox.history.unknown'); + + var d = new Date(entity.timestamp); + if (isNaN(d.getTime())) return t('infobox.history.unknown'); + + return d.toLocaleString(); + } + + + function displayUser(selection, entity) { + if (!entity.user) { + selection + .append('span') + .text(t('infobox.history.unknown')); + return; + } + + selection + .append('span') + .attr('class', 'user-name') + .text(entity.user); + + var links = selection + .append('div') + .attr('class', 'links'); + + links + .append('a') + .attr('class', 'user-osm-link') + .attr('href', context.connection().userURL(entity.user)) + .attr('target', '_blank') + .attr('tabindex', -1) + .text('OSM'); + + links + .append('a') + .attr('class', 'user-hdyc-link') + .attr('href', 'https://hdyc.neis-one.org/?' + entity.user) + .attr('target', '_blank') + .attr('tabindex', -1) + .text('HDYC'); + } + + + function displayChangeset(selection, entity) { + if (!entity.changeset) { + selection + .append('span') + .text(t('infobox.history.unknown')); + return; + } + + selection + .append('span') + .attr('class', 'changeset-id') + .text(entity.changeset); + + var links = selection + .append('div') + .attr('class', 'links'); + + links + .append('a') + .attr('class', 'changeset-osm-link') + .attr('href', context.connection().userURL(entity.changeset)) + .attr('target', '_blank') + .attr('tabindex', -1) + .text('OSM'); + + links + .append('a') + .attr('class', 'changeset-osmcha-link') + .attr('href', 'https://osmcha.mapbox.com/changesets/' + entity.changeset) + .attr('target', '_blank') + .attr('tabindex', -1) + .text('OSMCha'); + } + + + function redraw(selection) { + if (d3.selectAll('.infobox.hide').size()) return; // infobox is hidden + + var selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), + singular = selected.length === 1 ? selected[0] : null; + + selection.html(''); + + selection + .append('h4') + .attr('class', 'history-heading') + .text(singular || t('infobox.history.selected', { n: selected.length })); + + if (!singular) return; + + var entity = context.entity(singular); + + var list = selection + .append('ul'); + + list + .append('li') + .text(t('infobox.history.version') + ': ' + entity.version); + + list + .append('li') + .text(t('infobox.history.last_edit') + ': ' + displayTimestamp(entity)); + + list + .append('li') + .text(t('infobox.history.edited_by') + ': ') + .call(displayUser, entity); + + list + .append('li') + .text(t('infobox.history.changeset') + ': ') + .call(displayChangeset, entity); + + selection + .append('a') + .attr('class', 'view-history-on-osm') + .attr('target', '_blank') + .attr('tabindex', -1) + .attr('href', context.connection().historyURL(entity)) + .call(svgIcon('#icon-out-link', 'inline')) + .append('span') + .text(t('infobox.history.link_text')); + } + + + + var panel = function(selection) { + selection.call(redraw); + + context.map() + .on('drawn.info-history', function() { + selection.call(redraw); + }); + }; + + panel.off = function() { + context.map() + .on('drawn.info-history', null); + }; + + panel.id = 'history'; + panel.title = t('infobox.history.title'); + panel.key = t('infobox.history.key'); + + + return panel; +} diff --git a/modules/ui/panels/index.js b/modules/ui/panels/index.js index 9fc7475ac..a7a4e2ed1 100644 --- a/modules/ui/panels/index.js +++ b/modules/ui/panels/index.js @@ -1,10 +1,13 @@ +export * from './history'; export * from './location'; export * from './measurement'; +import { uiPanelHistory } from './history'; import { uiPanelLocation } from './location'; import { uiPanelMeasurement } from './measurement'; export var uiInfoPanels = { + history: uiPanelHistory, location: uiPanelLocation, measurement: uiPanelMeasurement, }; From 150985a57d89cbb9f6c779ec07fafe6b5c437e0e Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 30 Jun 2017 18:52:17 -0400 Subject: [PATCH 18/36] Fetch imagery vintage from Bing source, add info to location panel --- data/core.yaml | 3 ++ dist/locales/en.json | 5 +- modules/renderer/background_source.js | 37 +++++++++++++ modules/ui/panels/location.js | 78 +++++++++++++++++++++------ 4 files changed, 107 insertions(+), 16 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index e9518957b..8f29823d7 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -287,6 +287,9 @@ en: location: key: L title: Location + zoom: Zoom + unknown_location: Unknown Location + unknown_imagery_age: Unknown Imagery Age measurement: key: M title: Measurement diff --git a/dist/locales/en.json b/dist/locales/en.json index 13ca2d42f..78a101e60 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -363,7 +363,10 @@ }, "location": { "key": "L", - "title": "Location" + "title": "Location", + "zoom": "Zoom", + "unknown_location": "Unknown Location", + "unknown_imagery_age": "Unknown Imagery Age" }, "measurement": { "key": "M", diff --git a/modules/renderer/background_source.js b/modules/renderer/background_source.js index 38252f1ff..c8b2cff63 100644 --- a/modules/renderer/background_source.js +++ b/modules/renderer/background_source.js @@ -106,6 +106,11 @@ export function rendererBackgroundSource(data) { source.copyrightNotices = function() {}; + source.getVintage = function(center, zoom, callback) { + callback(null, { start: null, end: null }); + }; + + return source; } @@ -137,6 +142,7 @@ rendererBackgroundSource.Bing = function(data, dispatch) { dispatch.call('change'); }); + bing.copyrightNotices = function(zoom, extent) { zoom = Math.min(zoom, 21); return providers.filter(function(provider) { @@ -150,8 +156,31 @@ rendererBackgroundSource.Bing = function(data, dispatch) { }).join(', '); }; + + bing.getVintage = function(center, zoom, callback) { + zoom = Math.min(zoom, 21); + + var centerPoint = center[1] + ',' + center[0], + url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + + '?zl=' + zoom + '&key=' + key + '&jsonp={callback}'; + + jsonpRequest(url, function(result) { + var error = (!result && 'Unknown Error') || result.errorDetails; + if (error) { + return callback(error); + } else { + return callback(null, { + start: result.resourceSets[0].resources[0].vintageStart, + end: result.resourceSets[0].resources[0].vintageEnd + }); + } + }); + }; + + bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details'; + return bing; }; @@ -159,18 +188,22 @@ rendererBackgroundSource.Bing = function(data, dispatch) { rendererBackgroundSource.None = function() { var source = rendererBackgroundSource({ id: 'none', template: '' }); + source.name = function() { return t('background.none'); }; + source.imageryUsed = function() { return 'None'; }; + source.area = function() { return -1; // sources in background pane are sorted by area }; + return source; }; @@ -178,17 +211,21 @@ rendererBackgroundSource.None = function() { rendererBackgroundSource.Custom = function(template) { var source = rendererBackgroundSource({ id: 'custom', template: template }); + source.name = function() { return t('background.custom'); }; + source.imageryUsed = function() { return 'Custom (' + template + ')'; }; + source.area = function() { return -2; // sources in background pane are sorted by area }; + return source; }; diff --git a/modules/ui/panels/location.js b/modules/ui/panels/location.js index 056e8ec6f..acc2bbd1b 100644 --- a/modules/ui/panels/location.js +++ b/modules/ui/panels/location.js @@ -6,7 +6,7 @@ import { services } from '../../services'; export function uiPanelLocation(context) { var lastLocation = ''; - var debouncedUpdate = _.debounce(updateLocation, 250); + var lastImagery = ''; var OSM_PRECISION = 7; @@ -43,26 +43,61 @@ export function uiPanelLocation(context) { .append('li') .text(coordStr); - // Location Name - if (services.geocoder) { - selection - .append('p') - .attr('class', 'location-name') - .text(lastLocation); + list + .append('li') + .text(t('infobox.location.zoom') + ': ' + context.map().zoom().toFixed(2)); - debouncedUpdate(selection, coord); + // Date of Imagery + selection + .append('p') + .attr('class', 'imagery-vintage') + .text(lastImagery); + + // Location Name + selection + .append('p') + .attr('class', 'location-name') + .text(lastLocation); + + debouncedLocation(selection, coord); + } + + + var debouncedLocation = _.debounce(updateLocation, 250); + function updateLocation(selection, coord) { + if (!services.geocoder) { + lastLocation = t('infobox.location.unknown_location'); + selection.selectAll('.location-name') + .text(lastLocation); + } else { + services.geocoder.reverse(coord, function(err, result) { + lastLocation = result ? result.display_name : t('infobox.location.unknown_location'); + selection.selectAll('.location-name') + .text(lastLocation); + }); } } - function updateLocation(selection, coord) { - if (!services.geocoder) return; - services.geocoder.reverse(coord, function(err, result) { - if (result) { - lastLocation = result.display_name; - selection.selectAll('.location-name') - .text(lastLocation); + var debouncedImageryVintage = _.debounce(updateImageryVintage, 250); + function updateImageryVintage(selection) { + var tiledata = d3.select('.layer-background img').datum(), + zoom = tiledata[2] || Math.floor(context.map().zoom()), + center = context.map().center(); + + context.background().baseLayerSource().getVintage(center, zoom, function(err, result) { + if (!result) { + lastImagery = t('infobox.location.unknown_imagery_age'); + } else { + if (result.start || result.end) { + lastImagery = (result.start || '?') + ' - ' + (result.end || '?'); + } else { + lastImagery = t('infobox.location.unknown_imagery_age'); + } } + + selection.selectAll('.imagery-vintage') + .text(lastImagery); }); } @@ -74,11 +109,24 @@ export function uiPanelLocation(context) { .on('mousemove.info-location', function() { selection.call(redraw); }); + + context.map() + .on('drawn.info-location', function() { + selection.call(redraw); + }) + .on('move.info-location', function() { + selection.call(debouncedImageryVintage); + }); + }; panel.off = function() { context.surface() .on('mousemove.info-location', null); + + context.map() + .on('drawn.info-location', null) + .on('move.info-location', null); }; panel.id = 'location'; From 0d27743b816cab7477357c01765a7e1439d075dc Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 1 Jul 2017 00:47:16 -0400 Subject: [PATCH 19/36] Improve formatting for imagery capture dates --- css/80_app.css | 3 +- data/core.yaml | 1 + dist/locales/en.json | 1 + modules/renderer/background_source.js | 17 ++++-- modules/ui/panels/location.js | 85 +++++++++++++++++---------- 5 files changed, 71 insertions(+), 36 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 2a1e3184f..67e038b49 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2720,7 +2720,8 @@ img.tile-removing { padding: 10px 0; } -.panel-content-location .location-name { +.panel-content-location .imagery-info, +.panel-content-location .location-info { padding-top: 10px; } diff --git a/data/core.yaml b/data/core.yaml index 8f29823d7..0a40a0d49 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -288,6 +288,7 @@ en: key: L title: Location zoom: Zoom + imagery_capture_dates: Imagery Capture Dates unknown_location: Unknown Location unknown_imagery_age: Unknown Imagery Age measurement: diff --git a/dist/locales/en.json b/dist/locales/en.json index 78a101e60..c28197caa 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -365,6 +365,7 @@ "key": "L", "title": "Location", "zoom": "Zoom", + "imagery_capture_dates": "Imagery Capture Dates", "unknown_location": "Unknown Location", "unknown_imagery_age": "Unknown Imagery Age" }, diff --git a/modules/renderer/background_source.js b/modules/renderer/background_source.js index c8b2cff63..a5c6a8c63 100644 --- a/modules/renderer/background_source.js +++ b/modules/renderer/background_source.js @@ -165,15 +165,22 @@ rendererBackgroundSource.Bing = function(data, dispatch) { '?zl=' + zoom + '&key=' + key + '&jsonp={callback}'; jsonpRequest(url, function(result) { - var error = (!result && 'Unknown Error') || result.errorDetails; - if (error) { - return callback(error); + var err = (!result && 'Unknown Error') || result.errorDetails; + if (err) { + return callback(err); } else { return callback(null, { - start: result.resourceSets[0].resources[0].vintageStart, - end: result.resourceSets[0].resources[0].vintageEnd + start: localeDateString(result.resourceSets[0].resources[0].vintageStart), + end: localeDateString(result.resourceSets[0].resources[0].vintageEnd) }); } + + function localeDateString(s) { + if (!s) return null; + var d = new Date(s); + if (isNaN(d.getTime())) return null; + return d.toLocaleDateString(); + } }); }; diff --git a/modules/ui/panels/location.js b/modules/ui/panels/location.js index acc2bbd1b..ea5a6b1a3 100644 --- a/modules/ui/panels/location.js +++ b/modules/ui/panels/location.js @@ -5,8 +5,10 @@ import { services } from '../../services'; export function uiPanelLocation(context) { - var lastLocation = ''; - var lastImagery = ''; + var background = context.background(); + var currLocation = ''; + var currImagerySource = null; + var currImageryDates = ''; var OSM_PRECISION = 7; @@ -47,57 +49,80 @@ export function uiPanelLocation(context) { .append('li') .text(t('infobox.location.zoom') + ': ' + context.map().zoom().toFixed(2)); - // Date of Imagery - selection - .append('p') - .attr('class', 'imagery-vintage') - .text(lastImagery); - // Location Name - selection - .append('p') - .attr('class', 'location-name') - .text(lastLocation); + // Imagery Info + if (currImagerySource !== background.baseLayerSource().name()) { + currImagerySource = background.baseLayerSource().name(); + currImageryDates = ''; + } - debouncedLocation(selection, coord); + var imageryList = selection + .append('ul') + .attr('class', 'imagery-info'); + + imageryList + .append('li') + .text(currImagerySource); + + imageryList + .append('li') + .text(t('infobox.location.imagery_capture_dates') + ':'); + + imageryList + .append('li') + .attr('class', 'imagery-dates') + .text(currImageryDates || ' '); + + if (!currImageryDates) { + debouncedGetImageryDates(selection); + } + + + // Location Info + selection + .append('div') + .attr('class', 'location-info') + .text(currLocation || ' '); + + debouncedGetLocation(selection, coord); } - var debouncedLocation = _.debounce(updateLocation, 250); - function updateLocation(selection, coord) { + var debouncedGetLocation = _.debounce(getLocation, 250); + function getLocation(selection, coord) { if (!services.geocoder) { - lastLocation = t('infobox.location.unknown_location'); - selection.selectAll('.location-name') - .text(lastLocation); + currLocation = t('infobox.location.unknown_location'); + selection.selectAll('.location-info') + .text(currLocation); } else { services.geocoder.reverse(coord, function(err, result) { - lastLocation = result ? result.display_name : t('infobox.location.unknown_location'); - selection.selectAll('.location-name') - .text(lastLocation); + currLocation = result ? result.display_name : t('infobox.location.unknown_location'); + selection.selectAll('.location-info') + .text(currLocation); }); } } - var debouncedImageryVintage = _.debounce(updateImageryVintage, 250); - function updateImageryVintage(selection) { + var debouncedGetImageryDates = _.debounce(getImageryDates, 250); + function getImageryDates(selection) { var tiledata = d3.select('.layer-background img').datum(), zoom = tiledata[2] || Math.floor(context.map().zoom()), center = context.map().center(); - context.background().baseLayerSource().getVintage(center, zoom, function(err, result) { + background.baseLayerSource().getVintage(center, zoom, function(err, result) { if (!result) { - lastImagery = t('infobox.location.unknown_imagery_age'); + currImageryDates = t('infobox.location.unknown_imagery_age'); } else { if (result.start || result.end) { - lastImagery = (result.start || '?') + ' - ' + (result.end || '?'); + currImageryDates = (result.start || '?') + ' - ' + (result.end || '?'); } else { - lastImagery = t('infobox.location.unknown_imagery_age'); + currImageryDates = t('infobox.location.unknown_imagery_age'); } } - selection.selectAll('.imagery-vintage') - .text(lastImagery); + selection.selectAll('.imagery-dates') + .text(currImageryDates); }); } @@ -115,7 +140,7 @@ export function uiPanelLocation(context) { selection.call(redraw); }) .on('move.info-location', function() { - selection.call(debouncedImageryVintage); + selection.call(debouncedGetImageryDates); }); }; From c669bc72cd1cb70d1fef16210dce9ea1904b7958 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 1 Jul 2017 02:04:59 -0400 Subject: [PATCH 20/36] Move imagery info from location panel to its own panel --- data/core.yaml | 9 ++- dist/locales/en.json | 12 ++-- modules/ui/panels/imagery.js | 110 ++++++++++++++++++++++++++++++++++ modules/ui/panels/index.js | 3 + modules/ui/panels/location.js | 72 ---------------------- 5 files changed, 127 insertions(+), 79 deletions(-) create mode 100644 modules/ui/panels/imagery.js diff --git a/data/core.yaml b/data/core.yaml index 0a40a0d49..a46e6b760 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -284,13 +284,16 @@ en: changeset: Changeset unknown: Unknown link_text: History on openstreetmap.org + imagery: + key: G + title: Imagery + zoom: Zoom + vintage: Vintage + unknown: Unknown location: key: L title: Location - zoom: Zoom - imagery_capture_dates: Imagery Capture Dates unknown_location: Unknown Location - unknown_imagery_age: Unknown Imagery Age measurement: key: M title: Measurement diff --git a/dist/locales/en.json b/dist/locales/en.json index c28197caa..3729d70db 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -361,13 +361,17 @@ "unknown": "Unknown", "link_text": "History on openstreetmap.org" }, + "imagery": { + "key": "G", + "title": "Imagery", + "zoom": "Zoom", + "vintage": "Vintage", + "unknown": "Unknown" + }, "location": { "key": "L", "title": "Location", - "zoom": "Zoom", - "imagery_capture_dates": "Imagery Capture Dates", - "unknown_location": "Unknown Location", - "unknown_imagery_age": "Unknown Imagery Age" + "unknown_location": "Unknown Location" }, "measurement": { "key": "M", diff --git a/modules/ui/panels/imagery.js b/modules/ui/panels/imagery.js new file mode 100644 index 000000000..15b697268 --- /dev/null +++ b/modules/ui/panels/imagery.js @@ -0,0 +1,110 @@ +import * as d3 from 'd3'; +import _ from 'lodash'; +import { t } from '../../util/locale'; + + +export function uiPanelImagery(context) { + var background = context.background(); + var currSource = null; + var currZoom = ''; + var currVintage = ''; + + + function redraw(selection) { + if (d3.selectAll('.infobox.hide').size()) return; // infobox is hidden + + if (currSource !== background.baseLayerSource().name()) { + currSource = background.baseLayerSource().name(); + currZoom = ''; + currVintage = ''; + } + + selection.html(''); + + var list = selection + .append('ul') + .attr('class', 'imagery-info'); + + list + .append('li') + .text(currSource); + + list + .append('li') + .text(t('infobox.imagery.zoom') + ': ') + .append('span') + .attr('class', 'zoom') + .text(currZoom); + + list + .append('li') + .text(t('infobox.imagery.vintage') + ': ') + .append('span') + .attr('class', 'vintage') + .text(currVintage); + + if (!currVintage) { + debouncedGetVintage(selection); + } + } + + + var debouncedGetVintage = _.debounce(getVintage, 250); + function getVintage(selection) { + var tile = d3.select('.layer-background img'); + if (tile.empty()) return; + + var tiledata = tile.datum(), + zoom = tiledata[2] || Math.floor(context.map().zoom()), + center = context.map().center(); + + currZoom = String(zoom); + selection.selectAll('.zoom') + .text(currZoom); + + background.baseLayerSource().getVintage(center, currZoom, function(err, result) { + if (!result) { + currVintage = t('infobox.imagery.unknown'); + } else { + if (result.start || result.end) { + currVintage = (result.start || '?'); + if (result.start !== result.end) { + currVintage += ' - ' + (result.end || '?'); + } + } else { + currVintage = t('infobox.imagery.unknown'); + } + } + + selection.selectAll('.vintage') + .text(currVintage); + }); + } + + + var panel = function(selection) { + selection.call(redraw); + + context.map() + .on('drawn.info-imagery', function() { + selection.call(redraw); + }) + .on('move.info-imagery', function() { + selection.call(debouncedGetVintage); + }); + + }; + + panel.off = function() { + context.map() + .on('drawn.info-imagery', null) + .on('move.info-imagery', null); + }; + + panel.id = 'imagery'; + panel.title = t('infobox.imagery.title'); + panel.key = t('infobox.imagery.key'); + + + return panel; +} diff --git a/modules/ui/panels/index.js b/modules/ui/panels/index.js index a7a4e2ed1..e83df2706 100644 --- a/modules/ui/panels/index.js +++ b/modules/ui/panels/index.js @@ -1,13 +1,16 @@ export * from './history'; +export * from './imagery'; export * from './location'; export * from './measurement'; import { uiPanelHistory } from './history'; +import { uiPanelImagery } from './imagery'; import { uiPanelLocation } from './location'; import { uiPanelMeasurement } from './measurement'; export var uiInfoPanels = { history: uiPanelHistory, + imagery: uiPanelImagery, location: uiPanelLocation, measurement: uiPanelMeasurement, }; diff --git a/modules/ui/panels/location.js b/modules/ui/panels/location.js index ea5a6b1a3..ffb4c9b0a 100644 --- a/modules/ui/panels/location.js +++ b/modules/ui/panels/location.js @@ -5,10 +5,7 @@ import { services } from '../../services'; export function uiPanelLocation(context) { - var background = context.background(); var currLocation = ''; - var currImagerySource = null; - var currImageryDates = ''; var OSM_PRECISION = 7; @@ -45,39 +42,6 @@ export function uiPanelLocation(context) { .append('li') .text(coordStr); - list - .append('li') - .text(t('infobox.location.zoom') + ': ' + context.map().zoom().toFixed(2)); - - - // Imagery Info - if (currImagerySource !== background.baseLayerSource().name()) { - currImagerySource = background.baseLayerSource().name(); - currImageryDates = ''; - } - - var imageryList = selection - .append('ul') - .attr('class', 'imagery-info'); - - imageryList - .append('li') - .text(currImagerySource); - - imageryList - .append('li') - .text(t('infobox.location.imagery_capture_dates') + ':'); - - imageryList - .append('li') - .attr('class', 'imagery-dates') - .text(currImageryDates || ' '); - - if (!currImageryDates) { - debouncedGetImageryDates(selection); - } - - // Location Info selection .append('div') @@ -104,29 +68,6 @@ export function uiPanelLocation(context) { } - var debouncedGetImageryDates = _.debounce(getImageryDates, 250); - function getImageryDates(selection) { - var tiledata = d3.select('.layer-background img').datum(), - zoom = tiledata[2] || Math.floor(context.map().zoom()), - center = context.map().center(); - - background.baseLayerSource().getVintage(center, zoom, function(err, result) { - if (!result) { - currImageryDates = t('infobox.location.unknown_imagery_age'); - } else { - if (result.start || result.end) { - currImageryDates = (result.start || '?') + ' - ' + (result.end || '?'); - } else { - currImageryDates = t('infobox.location.unknown_imagery_age'); - } - } - - selection.selectAll('.imagery-dates') - .text(currImageryDates); - }); - } - - var panel = function(selection) { selection.call(redraw); @@ -134,24 +75,11 @@ export function uiPanelLocation(context) { .on('mousemove.info-location', function() { selection.call(redraw); }); - - context.map() - .on('drawn.info-location', function() { - selection.call(redraw); - }) - .on('move.info-location', function() { - selection.call(debouncedGetImageryDates); - }); - }; panel.off = function() { context.surface() .on('mousemove.info-location', null); - - context.map() - .on('drawn.info-location', null) - .on('move.info-location', null); }; panel.id = 'location'; From 54a8ae439a827fa5d07d8d7e41bfae446e702984 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 1 Jul 2017 02:31:08 -0400 Subject: [PATCH 21/36] Include start/end imagery dates from editor-layer-index --- data/imagery.json | 173 ++++++++++++++++++++++++++ data/update_imagery.js | 25 +++- modules/renderer/background_source.js | 20 +-- 3 files changed, 204 insertions(+), 14 deletions(-) diff --git a/data/imagery.json b/data/imagery.json index 37efa3ea6..dddc404ee 100644 --- a/data/imagery.json +++ b/data/imagery.json @@ -5,6 +5,8 @@ "name": "2013 aerial imagery for San Juan County WA", "type": "tms", "template": "http://sjcgis.org/arcgis/rest/services/Basemaps/Aerials_2013_WM/MapServer/tile/{zoom}/{y}/{x}", + "endDate": "2013-06-01T00:00:00.000Z", + "startDate": "2013-05-01T00:00:00.000Z", "scaleExtent": [ 0, 19 @@ -162,6 +164,8 @@ "name": "2016 aerial imagery for San Juan County WA", "type": "tms", "template": "http://sjcgis.org/arcgis/rest/services/Basemaps/Aerials_2016_WM/MapServer/tile/{zoom}/{y}/{x}", + "endDate": "2016-07-01T00:00:00.000Z", + "startDate": "2016-05-01T00:00:00.000Z", "scaleExtent": [ 0, 19 @@ -1631,6 +1635,8 @@ "name": "Basemap geoportail.lu", "type": "tms", "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/basemap/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.png", + "endDate": "2010-07-20T00:00:00.000Z", + "startDate": "2013-07-19T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -6528,6 +6534,8 @@ "name": "British Columbia Mosaic", "type": "tms", "template": "http://{switch:a,b,c,d}.imagery.paulnorman.ca/tiles/bc_mosaic/{zoom}/{x}/{y}.png", + "endDate": "2013-06-01T00:00:00.000Z", + "startDate": "2009-01-01T00:00:00.000Z", "scaleExtent": [ 9, 20 @@ -8645,6 +8653,8 @@ "name": "City of Cape Town 2013 Aerial", "type": "tms", "template": "http://{switch:a,b,c}.coct.aerial.openstreetmap.org.za/layer/za_coct_aerial_2013/{zoom}/{x}/{y}.jpg", + "endDate": "2015-01-01T00:00:00.000Z", + "startDate": "2013-01-01T00:00:00.000Z", "scaleExtent": [ 1, 21 @@ -9450,6 +9460,8 @@ "name": "City of Cape Town 2015 Aerial", "type": "tms", "template": "http://{switch:a,b,c}.coct.aerial.openstreetmap.org.za/layer/za_coct_aerial_2015/{zoom}/{x}/{y}.jpg", + "endDate": "2016-01-01T00:00:00.000Z", + "startDate": "2015-01-01T00:00:00.000Z", "scaleExtent": [ 1, 21 @@ -14343,6 +14355,8 @@ "name": "FÖMI orthophoto 2000", "type": "tms", "template": "http://e.tile.openstreetmap.hu/ortofoto2000/{zoom}/{x}/{y}.jpg", + "endDate": "2000-01-01T00:00:00.000Z", + "startDate": "2000-01-01T00:00:00.000Z", "scaleExtent": [ 0, 17 @@ -16651,6 +16665,8 @@ "name": "FÖMI orthophoto 2005", "type": "tms", "template": "http://e.tile.openstreetmap.hu/ortofoto2005/{zoom}/{x}/{y}.jpg", + "endDate": "2005-01-01T00:00:00.000Z", + "startDate": "2005-01-01T00:00:00.000Z", "scaleExtent": [ 0, 17 @@ -24559,6 +24575,8 @@ "name": "imagico.de OSM images for mapping: Adams Bridge", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R119_N09_20160327T050917&z={zoom}&x={x}&y={-y}", + "endDate": "2016-03-27T00:00:00.000Z", + "startDate": "2016-03-27T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -24597,6 +24615,8 @@ "name": "imagico.de OSM images for mapping: Alaska Range", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80700162014211LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2014-07-31T00:00:00.000Z", + "startDate": "2014-07-31T00:00:00.000Z", "scaleExtent": [ 0, 12 @@ -24639,6 +24659,8 @@ "name": "imagico.de OSM images for mapping: Bakun Reservoir", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81190582014075LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2014-03-16T00:00:00.000Z", + "startDate": "2014-03-16T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -24681,6 +24703,8 @@ "name": "imagico.de OSM images for mapping: Batam", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81250592016107LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2016-01-01T00:00:00.000Z", + "startDate": "2014-01-01T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -24719,6 +24743,8 @@ "name": "imagico.de OSM images for mapping: Bouvet Island", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81800982013291LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2013-10-18T00:00:00.000Z", + "startDate": "2013-10-18T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -24758,6 +24784,8 @@ "name": "imagico.de OSM images for mapping: Cental Alps in late September 2016", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R065_N47_20160929T102022&z={zoom}&x={x}&y={-y}", + "endDate": "2016-09-29T00:00:00.000Z", + "startDate": "2016-09-29T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -24808,6 +24836,8 @@ "name": "imagico.de OSM images for mapping: Clerke Rocks", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82050982015344LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2015-12-10T00:00:00.000Z", + "startDate": "2015-12-10T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -24850,6 +24880,8 @@ "name": "imagico.de OSM images for mapping: Coropuna", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=EO1A0040712016264110KF&z={zoom}&x={x}&y={-y}", + "endDate": "2016-09-21T00:00:00.000Z", + "startDate": "2016-09-21T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -24896,6 +24928,8 @@ "name": "imagico.de OSM images for mapping: Cotonou", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R022_N06_20151221T103009&z={zoom}&x={x}&y={-y}", + "endDate": "2015-12-21T00:00:00.000Z", + "startDate": "2015-12-21T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -24938,6 +24972,8 @@ "name": "imagico.de OSM images for mapping: Darwin and Wolf islands, Galapagos", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R040_N01_20160311T164128&z={zoom}&x={x}&y={-y}", + "endDate": "2016-03-11T00:00:00.000Z", + "startDate": "2016-03-11T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -24976,6 +25012,8 @@ "name": "imagico.de OSM images for mapping: Eastern Devon Island coast", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80360072014245LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2014-09-02T00:00:00.000Z", + "startDate": "2014-09-02T00:00:00.000Z", "scaleExtent": [ 0, 11 @@ -25014,6 +25052,8 @@ "name": "imagico.de OSM images for mapping: Eastern Iceland", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82160152013239LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2013-08-27T00:00:00.000Z", + "startDate": "2013-08-27T00:00:00.000Z", "scaleExtent": [ 0, 12 @@ -25052,6 +25092,8 @@ "name": "imagico.de OSM images for mapping: El Altar", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=AST_L1T_00302052007154424_20150518041444_91492&z={zoom}&x={x}&y={-y}", + "endDate": "2012-02-05T00:00:00.000Z", + "startDate": "2012-02-05T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -25090,6 +25132,8 @@ "name": "imagico.de OSM images for mapping: Elephant Island/Clarence Island", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R009_S61_20160109&z={zoom}&x={x}&y={-y}", + "endDate": "2016-01-09T00:00:00.000Z", + "startDate": "2016-01-09T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -25132,6 +25176,8 @@ "name": "imagico.de OSM images for mapping: Enderby Land and Kemp Coast", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=enderby&z={zoom}&x={x}&y={-y}", + "endDate": "2017-03-27T00:00:00.000Z", + "startDate": "2017-01-25T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -25182,6 +25228,8 @@ "name": "imagico.de OSM images for mapping: Fogo, Cape Verde", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82100502015347LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2015-12-13T00:00:00.000Z", + "startDate": "2015-12-13T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -25220,6 +25268,8 @@ "name": "imagico.de OSM images for mapping: Greenland mosaic", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=greenland&z={zoom}&x={x}&y={-y}", + "endDate": "2015-01-01T00:00:00.000Z", + "startDate": "2013-01-01T00:00:00.000Z", "scaleExtent": [ 0, 12 @@ -25870,6 +25920,8 @@ "name": "imagico.de OSM images for mapping: Heard Island coast", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R047_S54_20160411T044330&z={zoom}&x={x}&y={-y}", + "endDate": "2016-04-12T00:00:00.000Z", + "startDate": "2016-04-12T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -25912,6 +25964,8 @@ "name": "imagico.de OSM images for mapping: Isla Londonderry", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82280982013259LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2013-09-16T00:00:00.000Z", + "startDate": "2013-09-16T00:00:00.000Z", "scaleExtent": [ 0, 12 @@ -25962,6 +26016,8 @@ "name": "imagico.de OSM images for mapping: Kerch Strait", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R021_N44_20160807T083013&z={zoom}&x={x}&y={-y}", + "endDate": "2016-08-07T00:00:00.000Z", + "startDate": "2016-08-07T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -26000,6 +26056,8 @@ "name": "imagico.de OSM images for mapping: Landsat off-nadir July 2016", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=ls_polar2&z={zoom}&x={x}&y={-y}", + "endDate": "2016-07-17T00:00:00.000Z", + "startDate": "2016-07-17T00:00:00.000Z", "scaleExtent": [ 0, 10 @@ -26050,6 +26108,8 @@ "name": "imagico.de OSM images for mapping: Leskov Island ASTER", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=AST_L1T_00311162013112731_20150618142416_109190&z={zoom}&x={x}&y={-y}", + "endDate": "2013-11-16T00:00:00.000Z", + "startDate": "2013-11-16T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -26088,6 +26148,8 @@ "name": "imagico.de OSM images for mapping: Leskov Island Landsat", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81991002015286LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2015-10-13T00:00:00.000Z", + "startDate": "2015-10-13T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -26130,6 +26192,8 @@ "name": "imagico.de OSM images for mapping: May 2013 off-nadir Landsat", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=ls_polar&z={zoom}&x={x}&y={-y}", + "endDate": "2013-05-17T00:00:00.000Z", + "startDate": "2013-05-17T00:00:00.000Z", "scaleExtent": [ 0, 10 @@ -26200,6 +26264,8 @@ "name": "imagico.de OSM images for mapping: Mount Kenya 2016", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R092_S02_20160613T075613&z={zoom}&x={x}&y={-y}", + "endDate": "2016-06-13T00:00:00.000Z", + "startDate": "2016-06-13T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -26238,6 +26304,8 @@ "name": "imagico.de OSM images for mapping: Mount Kilimanjaro 2016", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R092_S05_20160802T075556&z={zoom}&x={x}&y={-y}", + "endDate": "2016-08-02T00:00:00.000Z", + "startDate": "2016-08-02T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -26276,6 +26344,8 @@ "name": "imagico.de OSM images for mapping: New Ireland", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80940622015159LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2015-06-08T00:00:00.000Z", + "startDate": "2015-06-08T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -26314,6 +26384,8 @@ "name": "imagico.de OSM images for mapping: North Sea Coast 2016", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=northsea_s2_2016&z={zoom}&x={x}&y={-y}", + "endDate": "2016-09-25T00:00:00.000Z", + "startDate": "2016-09-25T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -26360,6 +26432,8 @@ "name": "imagico.de OSM images for mapping: Northern and Polar Ural mountains August 2016", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=ural_s2_2016&z={zoom}&x={x}&y={-y}", + "endDate": "2016-08-12T00:00:00.000Z", + "startDate": "2016-08-12T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -26406,6 +26480,8 @@ "name": "imagico.de OSM images for mapping: Northern Ellesmere Island", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=nellesmere_ast&z={zoom}&x={x}&y={-y}", + "endDate": "2012-07-09T00:00:00.000Z", + "startDate": "2012-07-09T00:00:00.000Z", "scaleExtent": [ 0, 10 @@ -26452,6 +26528,8 @@ "name": "imagico.de OSM images for mapping: Northern Ellesmere Island July 2016", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=nellesmere_ast_2016&z={zoom}&x={x}&y={-y}", + "endDate": "2012-07-15T00:00:00.000Z", + "startDate": "2012-07-08T00:00:00.000Z", "scaleExtent": [ 0, 10 @@ -26502,6 +26580,8 @@ "name": "imagico.de OSM images for mapping: Northern German west coast tidalflats", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81960222015233LGN00vis&z={zoom}&x={x}&y={-y}", + "endDate": "2015-08-21T00:00:00.000Z", + "startDate": "2015-08-21T00:00:00.000Z", "scaleExtent": [ 0, 12 @@ -26544,6 +26624,8 @@ "name": "imagico.de OSM images for mapping: Northern German west coast tidalflats (infrared)", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81960222015233LGN00ir&z={zoom}&x={x}&y={-y}", + "endDate": "2015-08-21T00:00:00.000Z", + "startDate": "2015-08-21T00:00:00.000Z", "scaleExtent": [ 0, 12 @@ -26586,6 +26668,8 @@ "name": "imagico.de OSM images for mapping: Northern Greenland ASTER", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=ngreenland_ast&z={zoom}&x={x}&y={-y}", + "endDate": "2012-08-13T00:00:00.000Z", + "startDate": "2005-06-21T00:00:00.000Z", "scaleExtent": [ 0, 10 @@ -26644,6 +26728,8 @@ "name": "imagico.de OSM images for mapping: Northwest Heard Island", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=EO1A1350972013086110KF&z={zoom}&x={x}&y={-y}", + "endDate": "2013-03-13T00:00:00.000Z", + "startDate": "2013-03-13T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -26690,6 +26776,8 @@ "name": "imagico.de OSM images for mapping: Panama Canal", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R111_N09_20160604T154554&z={zoom}&x={x}&y={-y}", + "endDate": "2016-06-07T00:00:00.000Z", + "startDate": "2016-06-07T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -26728,6 +26816,8 @@ "name": "imagico.de OSM images for mapping: Panama Canal - Pacific side", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=EO1A0120532016364110KF&z={zoom}&x={x}&y={-y}", + "endDate": "2016-12-30T00:00:00.000Z", + "startDate": "2016-12-30T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -26774,6 +26864,8 @@ "name": "imagico.de OSM images for mapping: Pechora Sea Coast", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R078_N68_20160930T081002&z={zoom}&x={x}&y={-y}", + "endDate": "2016-09-30T00:00:00.000Z", + "startDate": "2016-09-30T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -26832,6 +26924,8 @@ "name": "imagico.de OSM images for mapping: Pensacola Mountains", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81511242016033LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2016-02-02T00:00:00.000Z", + "startDate": "2016-02-02T00:00:00.000Z", "scaleExtent": [ 0, 10 @@ -26878,6 +26972,8 @@ "name": "imagico.de OSM images for mapping: Prokletije Mountains", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z={zoom}&x={x}&y={-y}", + "endDate": "2015-08-31T00:00:00.000Z", + "startDate": "2015-08-31T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -26916,6 +27012,8 @@ "name": "imagico.de OSM images for mapping: Qasigiannguit", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=DMS_1142622_03746_20110415_17533956&z={zoom}&x={x}&y={-y}", + "endDate": "2011-04-15T00:00:00.000Z", + "startDate": "2011-04-15T00:00:00.000Z", "scaleExtent": [ 0, 15 @@ -26954,6 +27052,8 @@ "name": "imagico.de OSM images for mapping: Rann of Kutch", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81510432015030LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2015-01-01T00:00:00.000Z", + "startDate": "2015-01-01T00:00:00.000Z", "scaleExtent": [ 0, 12 @@ -26996,6 +27096,8 @@ "name": "imagico.de OSM images for mapping: Rila and Pirin Mountains", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R093_N41_20150828T092005&z={zoom}&x={x}&y={-y}", + "endDate": "2015-08-28T00:00:00.000Z", + "startDate": "2015-08-28T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -27038,6 +27140,8 @@ "name": "imagico.de OSM images for mapping: Rwenzori Mountains", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81730602015040LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2015-02-09T00:00:00.000Z", + "startDate": "2015-02-09T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -27076,6 +27180,8 @@ "name": "imagico.de OSM images for mapping: Rwenzori Mountains 2016", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R078_N01_20160702T082522&z={zoom}&x={x}&y={-y}", + "endDate": "2016-07-02T00:00:00.000Z", + "startDate": "2016-07-02T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -27114,6 +27220,8 @@ "name": "imagico.de OSM images for mapping: Scott Island", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80611072014036LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2014-02-05T00:00:00.000Z", + "startDate": "2014-02-05T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -27152,6 +27260,8 @@ "name": "imagico.de OSM images for mapping: Shag Rocks", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82100972015347LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2015-12-13T00:00:00.000Z", + "startDate": "2015-12-13T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -27190,6 +27300,8 @@ "name": "imagico.de OSM images for mapping: Southeastern Sulawesi", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81130622013270LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2013-09-27T00:00:00.000Z", + "startDate": "2013-09-27T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -27236,6 +27348,8 @@ "name": "imagico.de OSM images for mapping: Southern Transantarctic Mountains", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80281222016035LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2016-02-04T00:00:00.000Z", + "startDate": "2016-02-04T00:00:00.000Z", "scaleExtent": [ 0, 10 @@ -27294,6 +27408,8 @@ "name": "imagico.de OSM images for mapping: Svalbard mosaic", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=s2sval&z={zoom}&x={x}&y={-y}", + "endDate": "2016-01-01T00:00:00.000Z", + "startDate": "2016-01-01T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -27404,6 +27520,8 @@ "name": "imagico.de OSM images for mapping: Thule Air Base", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=DMS_1142636_160xx_20110507_1822xxxx&z={zoom}&x={x}&y={-y}", + "endDate": "2011-05-07T00:00:00.000Z", + "startDate": "2011-05-07T00:00:00.000Z", "scaleExtent": [ 0, 15 @@ -27450,6 +27568,8 @@ "name": "imagico.de OSM images for mapping: Thule Airbase DMS low altitude overflight September 2015", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=dms_thule2_2015.09.25&z={zoom}&x={x}&y={-y}", + "endDate": "2015-09-25T00:00:00.000Z", + "startDate": "2015-09-25T00:00:00.000Z", "scaleExtent": [ 0, 17 @@ -27504,6 +27624,8 @@ "name": "imagico.de OSM images for mapping: Thule Airbase DMS overflight October 2015", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=dms_thule_2015.10.06&z={zoom}&x={x}&y={-y}", + "endDate": "2015-10-06T00:00:00.000Z", + "startDate": "2015-10-06T00:00:00.000Z", "scaleExtent": [ 0, 16 @@ -27554,6 +27676,8 @@ "name": "imagico.de OSM images for mapping: Thule Airbase DMS overflight September 2015", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=dms_thule_2015.09.25&z={zoom}&x={x}&y={-y}", + "endDate": "2015-09-25T00:00:00.000Z", + "startDate": "2015-09-25T00:00:00.000Z", "scaleExtent": [ 0, 16 @@ -27600,6 +27724,8 @@ "name": "imagico.de OSM images for mapping: Ushakov Island August 2016", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R094_N79_20160812T105622&z={zoom}&x={x}&y={-y}", + "endDate": "2016-08-12T00:00:00.000Z", + "startDate": "2016-08-12T00:00:00.000Z", "scaleExtent": [ 0, 12 @@ -27638,6 +27764,8 @@ "name": "imagico.de OSM images for mapping: Vanatinai", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC80910682014358LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2014-12-24T00:00:00.000Z", + "startDate": "2014-12-24T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -27680,6 +27808,8 @@ "name": "imagico.de OSM images for mapping: Volcán Calbuco", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC82330892016031LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2016-01-31T00:00:00.000Z", + "startDate": "2016-01-31T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -27722,6 +27852,8 @@ "name": "imagico.de OSM images for mapping: Vostochny Cosmodrome", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R089_N52_20160623T024048&z={zoom}&x={x}&y={-y}", + "endDate": "2016-06-23T00:00:00.000Z", + "startDate": "2016-06-23T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -27760,6 +27892,8 @@ "name": "imagico.de OSM images for mapping: Western Karakoram", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=LC81490352013282LGN00&z={zoom}&x={x}&y={-y}", + "endDate": "2013-10-09T00:00:00.000Z", + "startDate": "2013-10-09T00:00:00.000Z", "scaleExtent": [ 0, 13 @@ -27798,6 +27932,8 @@ "name": "imagico.de OSM images for mapping: Willkanuta Mountains and Quelccaya Ice Cap", "type": "tms", "template": "http://imagico.de/map/osmim_tiles.php?layer=S2A_R039_S15_20160510T145731&z={zoom}&x={x}&y={-y}", + "endDate": "2016-05-10T00:00:00.000Z", + "startDate": "2016-05-10T00:00:00.000Z", "scaleExtent": [ 0, 14 @@ -28589,6 +28725,8 @@ "name": "Kanton Aargau 25cm (AGIS 2011)", "type": "tms", "template": "http://tiles.poole.ch/AGIS/OF2011/{zoom}/{x}/{y}.png", + "endDate": "2011-01-01T00:00:00.000Z", + "startDate": "2011-01-01T00:00:00.000Z", "scaleExtent": [ 14, 19 @@ -29240,6 +29378,8 @@ "name": "Kanton Aargau 25cm (AGIS 2014)", "type": "tms", "template": "http://mapproxy.osm.ch:8080/tiles/AGIS2014/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", + "endDate": "2014-01-01T00:00:00.000Z", + "startDate": "2014-01-01T00:00:00.000Z", "scaleExtent": [ 8, 19 @@ -29891,6 +30031,8 @@ "name": "Kanton Aargau 25cm (AGIS 2016)", "type": "tms", "template": "http://mapproxy.osm.ch:8080/tiles/AGIS2016/EPSG900913/{zoom}/{x}/{y}.png?origin=nw", + "endDate": "2016-01-01T00:00:00.000Z", + "startDate": "2016-01-01T00:00:00.000Z", "scaleExtent": [ 8, 19 @@ -32526,6 +32668,8 @@ "name": "Kelowna 2012", "type": "tms", "template": "http://{switch:a,b,c,d}.tile.paulnorman.ca/kelowna2012/{zoom}/{x}/{y}.png", + "endDate": "2012-05-14T00:00:00.000Z", + "startDate": "2012-05-13T00:00:00.000Z", "scaleExtent": [ 9, 20 @@ -33324,6 +33468,8 @@ "name": "Landsat 233055", "type": "tms", "template": "http://{switch:a,b,c,d}.tile.paulnorman.ca/landsat_233055/{zoom}/{x}/{y}.png", + "endDate": "2013-09-03T00:00:00.000Z", + "startDate": "2013-09-03T00:00:00.000Z", "scaleExtent": [ 5, 14 @@ -33359,6 +33505,8 @@ "name": "Latest southwest British Columbia Landsat", "type": "tms", "template": "http://{switch:a,b,c,d}.tile.paulnorman.ca/landsat_047026/{zoom}/{x}/{y}.png", + "endDate": "2013-09-12T00:00:00.000Z", + "startDate": "2013-09-12T00:00:00.000Z", "scaleExtent": [ 5, 13 @@ -34067,6 +34215,8 @@ "name": "Lithuania - NŽT ORT10LT", "type": "tms", "template": "http://ort10lt.openmap.lt/g16/{zoom}/{x}/{y}.jpeg", + "endDate": "2016-01-01T00:00:00.000Z", + "startDate": "2010-01-01T00:00:00.000Z", "scaleExtent": [ 4, 18 @@ -36754,6 +36904,8 @@ "name": "MD Latest 6 Inch Aerial Imagery", "type": "tms", "template": "http://whoots.mapwarper.net/tms/{zoom}/{x}/{y}/MD_SixInchImagery/http://geodata.md.gov/imap/services/Imagery/MD_SixInchImagery/MapServer/WmsServer", + "endDate": "2016-01-01T00:00:00.000Z", + "startDate": "2013-01-01T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -37641,6 +37793,8 @@ "name": "NJ 2015 Aerial Imagery (Infrared)", "type": "tms", "template": "http://whoots.mapwarper.net/tms/{zoom}/{x}/{y}/Infrared2015/http://geodata.state.nj.us/imagerywms/Infrared2015", + "endDate": "2015-05-03T00:00:00.000Z", + "startDate": "2015-03-29T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -38098,6 +38252,8 @@ "name": "NJ 2015 Aerial Imagery (Natural Color)", "type": "tms", "template": "http://whoots.mapwarper.net/tms/{zoom}/{x}/{y}/Natural2015/http://geodata.state.nj.us/imagerywms/Natural2015", + "endDate": "2015-05-03T00:00:00.000Z", + "startDate": "2015-03-29T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -45994,6 +46150,7 @@ "name": "NLSC General Map with Contour line", "type": "tms", "template": "http://wmts.nlsc.gov.tw/wmts/EMAP5_OPENDATA/default/EPSG:3857/{zoom}/{y}/{x}", + "startDate": "2015-01-01T00:00:00.000Z", "scaleExtent": [ 0, 15 @@ -48853,6 +49010,8 @@ "name": "Ortho 2010 geoportail.lu", "type": "tms", "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/ortho_2010/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.jpeg", + "endDate": "2010-07-02T00:00:00.000Z", + "startDate": "2010-06-24T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -49774,6 +49933,8 @@ "name": "Ortho 2013 geoportail.lu", "type": "tms", "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/ortho_2013/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.jpeg", + "endDate": "2013-07-20T00:00:00.000Z", + "startDate": "2013-07-19T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -50695,6 +50856,8 @@ "name": "Ortho 2016 geoportail.lu", "type": "tms", "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/ortho_2016/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.jpeg", + "endDate": "2016-08-16T00:00:00.000Z", + "startDate": "2013-08-30T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -57237,6 +57400,7 @@ "name": "Sóskút, Pusztazámor, Tárnok, Diósd ortophoto 2017", "type": "tms", "template": "http://adam.openstreetmap.hu/mapproxy/tiles/1.0.0/Soskut-Tarnok-Pusztazamor-Diosd/mercator/{zoom}/{x}/{y}.png", + "startDate": "2017-03-01T00:00:00.000Z", "polygon": [ [ [ @@ -64443,6 +64607,8 @@ "name": "Surrey Air Survey", "type": "tms", "template": "http://gravitystorm.dev.openstreetmap.org/surrey/{zoom}/{x}/{y}.png", + "endDate": "2009-01-01T00:00:00.000Z", + "startDate": "2007-01-01T00:00:00.000Z", "scaleExtent": [ 8, 19 @@ -65162,6 +65328,7 @@ "name": "Texas Orthophoto", "type": "tms", "template": "https://txgi.tnris.org/login/path/ecology-fiona-poem-romeo/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=texas&STYLE=&FORMAT=image/png&tileMatrixSet=0to20&tileMatrix=0to20:{zoom}&tileRow={y}&tileCol={x}", + "startDate": "2012-01-01T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -65289,6 +65456,8 @@ "name": "Topographical Map geoportail.lu", "type": "tms", "template": "https://{switch:wmts3,wmts4}.geoportail.lu/opendata/wmts/topo/GLOBAL_WEBMERCATOR_4_V3/{zoom}/{x}/{y}.png", + "endDate": "2010-07-20T00:00:00.000Z", + "startDate": "2013-07-19T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -67258,6 +67427,8 @@ "name": "Tours - Orthophotos 2008-2010", "type": "tms", "template": "http://wms.openstreetmap.fr/tms/1.0.0/tours/{zoom}/{x}/{y}", + "endDate": "2011-01-01T00:00:00.000Z", + "startDate": "2008-01-01T00:00:00.000Z", "scaleExtent": [ 0, 20 @@ -67862,6 +68033,8 @@ "name": "Tours - Orthophotos 2013", "type": "tms", "template": "http://wms.openstreetmap.fr/tms/1.0.0/tours_2013/{zoom}/{x}/{y}", + "endDate": "2013-01-01T00:00:00.000Z", + "startDate": "2013-01-01T00:00:00.000Z", "scaleExtent": [ 0, 22 diff --git a/data/update_imagery.js b/data/update_imagery.js index b0ea6ef59..cdbe7d1ba 100644 --- a/data/update_imagery.js +++ b/data/update_imagery.js @@ -41,12 +41,6 @@ sources.concat(whitelist).forEach(function(source) { if (source.type !== 'tms' && source.type !== 'bing') return; if (source.id in blacklist) return; - if (source.end_date) { - var endDate = new Date(source.end_date), - isValid = !isNaN(endDate.getTime()); - if (isValid && endDate <= cutoffDate) return; - } - var im = { id: source.id, name: source.name, @@ -54,6 +48,25 @@ sources.concat(whitelist).forEach(function(source) { template: source.url }; + var startDate, endDate, isValid; + + if (source.end_date) { + endDate = new Date(source.end_date); + isValid = !isNaN(endDate.getTime()); + if (isValid) { + if (endDate <= cutoffDate) return; // too old + im.endDate = endDate; + } + } + + if (source.start_date) { + startDate = new Date(source.start_date); + isValid = !isNaN(startDate.getTime()); + if (isValid) { + im.startDate = startDate; + } + } + var extent = source.extent || {}; if (extent.min_zoom || extent.max_zoom) { im.scaleExtent = [ diff --git a/modules/renderer/background_source.js b/modules/renderer/background_source.js index a5c6a8c63..01b7dff90 100644 --- a/modules/renderer/background_source.js +++ b/modules/renderer/background_source.js @@ -5,6 +5,14 @@ import { geoExtent, geoPolygonIntersectsPolygon } from '../geo/index'; import { jsonpRequest } from '../util/jsonp_request'; +function localeDateString(s) { + if (!s) return null; + var d = new Date(s); + if (isNaN(d.getTime())) return null; + return d.toLocaleDateString(); +} + + export function rendererBackgroundSource(data) { var source = _.clone(data), offset = [0, 0], @@ -107,7 +115,10 @@ export function rendererBackgroundSource(data) { source.getVintage = function(center, zoom, callback) { - callback(null, { start: null, end: null }); + callback(null, { + start: localeDateString(source.startDate), + end: localeDateString(source.endDate) + }); }; @@ -174,13 +185,6 @@ rendererBackgroundSource.Bing = function(data, dispatch) { end: localeDateString(result.resourceSets[0].resources[0].vintageEnd) }); } - - function localeDateString(s) { - if (!s) return null; - var d = new Date(s); - if (isNaN(d.getTime())) return null; - return d.toLocaleDateString(); - } }); }; From ee783a0f16b91c35ca8482d8651b28aef1554101 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 1 Jul 2017 09:09:06 -0400 Subject: [PATCH 22/36] Do not push translation strings to Transifex for pull requests --- .travis.yml | 8 +------- scripts/txpush.sh | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 scripts/txpush.sh diff --git a/.travis.yml b/.travis.yml index b74ebb9f4..2463156b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,4 @@ node_js: - "8" sudo: required after_success: - - if [[ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "master" ]]; then exit 0; fi - - pip install virtualenv - - virtualenv ~/env - - source ~/env/bin/activate - - pip install transifex-client - - sudo echo $'[https://www.transifex.com]\nhostname = https://www.transifex.com\nusername = '"$TRANSIFEX_USER"$'\npassword = '"$TRANSIFEX_PASSWORD"$'\n' > ~/.transifexrc - - tx push -s + - ./scripts/txpush.sh diff --git a/scripts/txpush.sh b/scripts/txpush.sh new file mode 100644 index 000000000..5351be876 --- /dev/null +++ b/scripts/txpush.sh @@ -0,0 +1,17 @@ +#/bin/bash + +# This script runs on TravisCI to push new translation strings to transifex + +echo $"TRAVIS" +echo $"TRAVIS_PULL_REQUEST" +echo $"TRAVIS_BRANCH" + +if [[ "$TRAVIS" != "true" -o $"TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "master" ]]; then exit 0; fi + +echo "Pushing source strings to Transifex..." +pip install virtualenv +virtualenv ~/env +source ~/env/bin/activate +pip install transifex-client +sudo echo $'[https://www.transifex.com]\nhostname = https://www.transifex.com\nusername = '"$TRANSIFEX_USER"$'\npassword = '"$TRANSIFEX_PASSWORD"$'\n' > ~/.transifexrc +tx push -s From 8dd7644b20e40c3cb8a582a6baea78e8a9c7d7c2 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 1 Jul 2017 09:13:14 -0400 Subject: [PATCH 23/36] fix permissions --- scripts/deploy.sh | 0 scripts/txpush.sh | 12 ++++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) mode change 100644 => 100755 scripts/deploy.sh mode change 100644 => 100755 scripts/txpush.sh diff --git a/scripts/deploy.sh b/scripts/deploy.sh old mode 100644 new mode 100755 diff --git a/scripts/txpush.sh b/scripts/txpush.sh old mode 100644 new mode 100755 index 5351be876..84b36fcac --- a/scripts/txpush.sh +++ b/scripts/txpush.sh @@ -2,11 +2,15 @@ # This script runs on TravisCI to push new translation strings to transifex -echo $"TRAVIS" -echo $"TRAVIS_PULL_REQUEST" -echo $"TRAVIS_BRANCH" +echo "TRAVIS=$TRAVIS" +echo "TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST" +echo "TRAVIS_BRANCH=$TRAVIS_BRANCH" +echo "TRAVIS_NODE_VERSION=$TRAVIS_NODE_VERSION" -if [[ "$TRAVIS" != "true" -o $"TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "master" ]]; then exit 0; fi +if [[ "$TRAVIS" != "true" ]]; then exit 0; fi +if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then exit 0; fi +if [[ "$TRAVIS_BRANCH" != "master" ]]; then exit 0; fi +if [[ "$TRAVIS_NODE_VERSION" != "6" ]]; then exit 0; fi echo "Pushing source strings to Transifex..." pip install virtualenv From 7df458e4dfffed3ff47ba36f7d55457c310f43b5 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 2 Jul 2017 10:21:46 -0400 Subject: [PATCH 24/36] Display imagery vintage when tile debugging is enabled.. --- css/80_app.css | 2 +- modules/renderer/tile_layer.js | 64 ++++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 67e038b49..aafb070df 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2488,7 +2488,7 @@ img.tile { color: #fff; position: absolute; text-align: center; - width: 128px; + padding: 5px; border-radius: 3px; z-index: 2; diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index 8f17930d9..0c141a28e 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -1,4 +1,5 @@ import * as d3 from 'd3'; +import { t } from '../util/locale'; import { d3geoTile } from '../lib/d3.geo.tile'; import { utilPrefixCSSProperty } from '../util/index'; import { rendererBackgroundSource } from './background_source.js'; @@ -145,14 +146,19 @@ export function rendererTileLayer(context) { 'scale(' + scale + ',' + scale + ')'; } - function debugTransform(d) { + function debugCoordinates(d) { var _ts = tileSize * Math.pow(2, z - d[2]); var scale = tileSizeAtZoom(d, z); - return 'translate(' + - ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0] + scale * (tileSize / 4)) + 'px,' + - ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1] + scale * (tileSize / 2)) + 'px)'; + return [ + ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0] + scale * (tileSize / 4)), + ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1] + scale * (tileSize / 2)) + ]; } + function debugTransform(d) { + var coord = debugCoordinates(d); + return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)'; + } var image = selection.selectAll('img') .data(requests, function(d) { return d[3]; }); @@ -187,12 +193,50 @@ export function rendererTileLayer(context) { debug.exit() .remove(); - debug.enter() - .append('div') - .attr('class', 'tile-label-debug') - .merge(debug) - .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; }) - .style(transformProp, debugTransform); + if (showDebug) { + var debugEnter = debug.enter() + .append('div') + .attr('class', 'tile-label-debug'); + + debugEnter + .append('div') + .attr('class', 'tile-label-debug-coord'); + + debugEnter + .append('div') + .attr('class', 'tile-label-debug-vintage'); + + debug = debug.merge(debugEnter); + + debug + .style(transformProp, debugTransform); + + debug + .selectAll('.tile-label-debug-coord') + .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; }); + + debug + .selectAll('.tile-label-debug-vintage') + .each(function(d) { + var span = d3.select(this); + var center = context.projection.invert(debugCoordinates(d)); + source.getVintage(center, d[2], function(err, result) { + var vintage = ''; + if (result) { + if (result.start || result.end) { + vintage = (result.start || '?'); + if (result.start !== result.end) { + vintage += ' - ' + (result.end || '?'); + } + } + } + + span + .text(vintage || t('infobox.imagery.vintage') + ': ' + t('infobox.imagery.unknown')); + }); + }); + } + } From 39621f99fa7053d51a7d0f9c079ab99f96472e00 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 3 Jul 2017 11:24:39 -0400 Subject: [PATCH 25/36] Add a show/hide tiles toggle button, position buttons relatively --- css/80_app.css | 28 ++++++++++++++-------------- data/core.yaml | 2 ++ dist/locales/en.json | 4 +++- modules/ui/panels/imagery.js | 13 +++++++++++++ modules/ui/panels/measurement.js | 2 +- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index aafb070df..040001c1f 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2707,33 +2707,33 @@ img.tile-removing { position: relative; } +.panel-content .button { + display: inline-block; + background: #7092ff; + border-radius: 2px; + padding: 0 4px; + margin-top: 5px; + color: white; +} + .panel-content-history .links a { - padding-left: 10px; + margin-left: 10px; } [dir='rtl'] .panel-content-history .links a { - padding-left: auto; - padding-right: 10px; + margin-left: auto; + margin-right: 10px; } .panel-content-history .view-history-on-osm { display: block; - padding: 10px 0; + margin-top: 10px; } .panel-content-location .imagery-info, .panel-content-location .location-info { - padding-top: 10px; + margin-top: 10px; } -.panel-content-measurement .button { - position: absolute; - background: #7092ff; - border-radius: 2px; - padding: 0 4px; - color: white; - top: 6px; - right: 10px; -} /* About Section ------------------------------------------------------- */ diff --git a/data/core.yaml b/data/core.yaml index a46e6b760..6df29a85e 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -290,6 +290,8 @@ en: zoom: Zoom vintage: Vintage unknown: Unknown + show_tiles: Show Tiles + hide_tiles: Hide Tiles location: key: L title: Location diff --git a/dist/locales/en.json b/dist/locales/en.json index 3729d70db..3a7d6752b 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -366,7 +366,9 @@ "title": "Imagery", "zoom": "Zoom", "vintage": "Vintage", - "unknown": "Unknown" + "unknown": "Unknown", + "show_tiles": "Show Tiles", + "hide_tiles": "Hide Tiles" }, "location": { "key": "L", diff --git a/modules/ui/panels/imagery.js b/modules/ui/panels/imagery.js index 15b697268..3d57fa65f 100644 --- a/modules/ui/panels/imagery.js +++ b/modules/ui/panels/imagery.js @@ -46,6 +46,19 @@ export function uiPanelImagery(context) { if (!currVintage) { debouncedGetVintage(selection); } + + var toggle = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles'; + + selection + .append('a') + .text(t('infobox.imagery.' + toggle)) + .attr('href', '#') + .attr('class', 'button button-toggle-tiles') + .on('click', function() { + d3.event.preventDefault(); + context.setDebug('tile', !context.getDebug('tile')); + selection.call(redraw); + }); } diff --git a/modules/ui/panels/measurement.js b/modules/ui/panels/measurement.js index 35afd1e6d..499ed29bf 100644 --- a/modules/ui/panels/measurement.js +++ b/modules/ui/panels/measurement.js @@ -188,7 +188,7 @@ export function uiPanelMeasurement(context) { .append('a') .text(t('infobox.measurement.' + toggle)) .attr('href', '#') - .attr('class', 'button') + .attr('class', 'button button-toggle-units') .on('click', function() { d3.event.preventDefault(); isImperial = !isImperial; From 1d8898778e0760a3a9ef44e16296fae71eabd956 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 3 Jul 2017 11:30:01 -0400 Subject: [PATCH 26/36] For tile boundary debug, switch border -> outline to not affect layout --- css/80_app.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/css/80_app.css b/css/80_app.css index 040001c1f..9845da69d 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2505,7 +2505,7 @@ img.tile { } img.tile-debug { - border: 1px solid red; + outline: 1px solid red; } img.tile-loaded { From b9df6df03fcda875af46e6d5727b1d83673e8f47 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 3 Jul 2017 15:12:59 -0400 Subject: [PATCH 27/36] Imagery vintage improvements: - Cache vintage lookups from Bing API - Actually sample a tile at the center of the viewport, rather than just divinding tiles/2 (not a good guess) - Refactor the start-end range code into one place --- modules/renderer/background_source.js | 42 ++++++++++++++++----- modules/renderer/tile_layer.js | 54 +++++++++++++++++---------- modules/ui/panels/imagery.js | 23 +++--------- 3 files changed, 74 insertions(+), 45 deletions(-) diff --git a/modules/renderer/background_source.js b/modules/renderer/background_source.js index 01b7dff90..611086449 100644 --- a/modules/renderer/background_source.js +++ b/modules/renderer/background_source.js @@ -12,6 +12,17 @@ function localeDateString(s) { return d.toLocaleDateString(); } +function vintageRange(vintage) { + var s; + if (vintage.start || vintage.end) { + s = (vintage.start || '?'); + if (vintage.start !== vintage.end) { + s += ' - ' + (vintage.end || '?'); + } + } + return s; +} + export function rendererBackgroundSource(data) { var source = _.clone(data), @@ -114,11 +125,13 @@ export function rendererBackgroundSource(data) { source.copyrightNotices = function() {}; - source.getVintage = function(center, zoom, callback) { - callback(null, { + source.getVintage = function(center, tileCoord, callback) { + var vintage = { start: localeDateString(source.startDate), end: localeDateString(source.endDate) - }); + }; + vintage.range = vintageRange(vintage); + callback(null, vintage); }; @@ -136,6 +149,7 @@ rendererBackgroundSource.Bing = function(data, dispatch) { key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU', // Same as P2 and JOSM url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key + '&jsonp={callback}', + cache = {}, providers = []; jsonpRequest(url, function(json) { @@ -168,22 +182,32 @@ rendererBackgroundSource.Bing = function(data, dispatch) { }; - bing.getVintage = function(center, zoom, callback) { - zoom = Math.min(zoom, 21); - - var centerPoint = center[1] + ',' + center[0], + bing.getVintage = function(center, tileCoord, callback) { + var tileId = tileCoord.slice(0, 3).join('/'), + zoom = Math.min(tileCoord[2], 21), + centerPoint = center[1] + ',' + center[0], // lat,lng url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key + '&jsonp={callback}'; + if (!cache[tileId]) { + cache[tileId] = {}; + } + if (cache[tileId] && cache[tileId].vintage) { + return callback(null, cache[tileId].vintage); + } + jsonpRequest(url, function(result) { var err = (!result && 'Unknown Error') || result.errorDetails; if (err) { return callback(err); } else { - return callback(null, { + var vintage = { start: localeDateString(result.resourceSets[0].resources[0].vintageStart), end: localeDateString(result.resourceSets[0].resources[0].vintageEnd) - }); + }; + vintage.range = vintageRange(vintage); + cache[tileId].vintage = vintage; + return callback(null, vintage); } }); }; diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index 0c141a28e..292391d6d 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -1,7 +1,8 @@ import * as d3 from 'd3'; import { t } from '../util/locale'; import { d3geoTile } from '../lib/d3.geo.tile'; -import { utilPrefixCSSProperty } from '../util/index'; +import { geoEuclideanDistance } from '../geo'; +import { utilPrefixCSSProperty } from '../util'; import { rendererBackgroundSource } from './background_source.js'; @@ -98,7 +99,7 @@ export function rendererTileLayer(context) { tile().forEach(function(d) { addSource(d); if (d[3] === '') return; - if (typeof d[3] !== 'string') return; // Workaround for chrome crash https://github.com/openstreetmap/iD/issues/2295 + if (typeof d[3] !== 'string') return; // Workaround for #2295 requests.push(d); if (cache[d[3]] === false && lookUp(d)) { requests.push(addSource(lookUp(d))); @@ -119,6 +120,7 @@ export function rendererTileLayer(context) { source.offset()[1] * Math.pow(2, z) ]; + function load(d) { cache[d[3]] = true; d3.select(this) @@ -146,9 +148,11 @@ export function rendererTileLayer(context) { 'scale(' + scale + ',' + scale + ')'; } - function debugCoordinates(d) { + function tileCenter(d) { var _ts = tileSize * Math.pow(2, z - d[2]); var scale = tileSizeAtZoom(d, z); + // FIXME: this scale * tileSize/number stuff is hacky, and more for displaying the debug text. + // It's not really the center of the tile, but it is guaranteed to be somewhere in the tile. return [ ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0] + scale * (tileSize / 4)), ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1] + scale * (tileSize / 2)) @@ -156,16 +160,35 @@ export function rendererTileLayer(context) { } function debugTransform(d) { - var coord = debugCoordinates(d); + var coord = tileCenter(d); return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)'; } + + // Pick a representative tile near the center of the viewport + // (This is useful for sampling the imagery vintage) + var dims = tile.size(), + mapCenter = [dims[0] / 2, dims[1] / 2], + minDist = Math.max(dims[0], dims[1]), + nearCenter; + + requests.forEach(function(d) { + var c = tileCenter(d); + var dist = geoEuclideanDistance(c, mapCenter); + if (dist < minDist) { + minDist = dist; + nearCenter = d; + } + }); + + var image = selection.selectAll('img') .data(requests, function(d) { return d[3]; }); image.exit() .style(transformProp, imageTransform) .classed('tile-removing', true) + .classed('tile-center', false) .each(function() { var tile = d3.select(this); window.setTimeout(function() { @@ -184,7 +207,9 @@ export function rendererTileLayer(context) { .merge(image) .style(transformProp, imageTransform) .classed('tile-debug', showDebug) - .classed('tile-removing', false); + .classed('tile-removing', false) + .classed('tile-center', function(d) { return d === nearCenter; }); + var debug = selection.selectAll('.tile-label-debug') @@ -219,20 +244,11 @@ export function rendererTileLayer(context) { .selectAll('.tile-label-debug-vintage') .each(function(d) { var span = d3.select(this); - var center = context.projection.invert(debugCoordinates(d)); - source.getVintage(center, d[2], function(err, result) { - var vintage = ''; - if (result) { - if (result.start || result.end) { - vintage = (result.start || '?'); - if (result.start !== result.end) { - vintage += ' - ' + (result.end || '?'); - } - } - } - - span - .text(vintage || t('infobox.imagery.vintage') + ': ' + t('infobox.imagery.unknown')); + var center = context.projection.invert(tileCenter(d)); + source.getVintage(center, d, function(err, result) { + span.text((result && result.range) || + t('infobox.imagery.vintage') + ': ' + t('infobox.imagery.unknown') + ); }); }); } diff --git a/modules/ui/panels/imagery.js b/modules/ui/panels/imagery.js index 3d57fa65f..7e22d09d0 100644 --- a/modules/ui/panels/imagery.js +++ b/modules/ui/panels/imagery.js @@ -64,31 +64,20 @@ export function uiPanelImagery(context) { var debouncedGetVintage = _.debounce(getVintage, 250); function getVintage(selection) { - var tile = d3.select('.layer-background img'); + var tile = d3.select('.layer-background img.tile-center'); // tile near viewport center if (tile.empty()) return; - var tiledata = tile.datum(), - zoom = tiledata[2] || Math.floor(context.map().zoom()), + var d = tile.datum(), + zoom = (d && d.length >= 3 && d[2]) || Math.floor(context.map().zoom()), center = context.map().center(); currZoom = String(zoom); selection.selectAll('.zoom') .text(currZoom); - background.baseLayerSource().getVintage(center, currZoom, function(err, result) { - if (!result) { - currVintage = t('infobox.imagery.unknown'); - } else { - if (result.start || result.end) { - currVintage = (result.start || '?'); - if (result.start !== result.end) { - currVintage += ' - ' + (result.end || '?'); - } - } else { - currVintage = t('infobox.imagery.unknown'); - } - } - + if (!d || !d.length >= 3) return; + background.baseLayerSource().getVintage(center, d, function(err, result) { + currVintage = (result && result.range) || t('infobox.imagery.unknown'); selection.selectAll('.vintage') .text(currVintage); }); From 1f826851b2619d387223f396bf19c3df205258ed Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 3 Jul 2017 16:03:41 -0400 Subject: [PATCH 28/36] Make tileCenter return the point actually at the tile center Then use css to adjust the position of the debug div --- css/80_app.css | 3 +++ modules/renderer/tile_layer.js | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 9845da69d..9cdb65e54 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2484,6 +2484,7 @@ img.tile { } .tile-label-debug { + font-size: 10px; background: rgba(0, 0, 0, 0.7); color: #fff; position: absolute; @@ -2491,6 +2492,8 @@ img.tile { padding: 5px; border-radius: 3px; z-index: 2; + margin-left: -50px; + margin-top: -20px; transform-origin:0 0; -ms-transform-origin:0 0; diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index 292391d6d..975909686 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -151,11 +151,9 @@ export function rendererTileLayer(context) { function tileCenter(d) { var _ts = tileSize * Math.pow(2, z - d[2]); var scale = tileSizeAtZoom(d, z); - // FIXME: this scale * tileSize/number stuff is hacky, and more for displaying the debug text. - // It's not really the center of the tile, but it is guaranteed to be somewhere in the tile. return [ - ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0] + scale * (tileSize / 4)), - ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1] + scale * (tileSize / 2)) + ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0] + (_ts / 2)), + ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1] + (_ts / 2)) ]; } From 71ecdbd57513b80ad85178137210cd30d7e97b78 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 3 Jul 2017 19:09:43 -0400 Subject: [PATCH 29/36] Switch panel layout to flexbox, grows right-to-left, bottom-to-top --- css/80_app.css | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 9cdb65e54..d83d4abf9 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2648,12 +2648,12 @@ img.tile-removing { /* Info Box ------------------------------------------------------- */ .infobox { - display: block; + display: flex; + flex-flow: row-reverse wrap-reverse; position: absolute; z-index: 1; right: 0; bottom: 30px; - width: 240px; -ms-user-select: element; } @@ -2673,16 +2673,16 @@ img.tile-removing { } .panel-container { + flex: 0 0 auto; + margin: 2px 0 0 2px; + border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.75); padding-bottom: 10px; + width: 250px; } -.panel-container:first-of-type, -.panel-container:first-of-type .panel-title { - border-radius: 4px 0 0 0; -} - -.panel-container:last-of-type { - padding-bottom: 0px; +.panel-container .panel-title { + border-radius: 4px 4px 0 0; } .panel-title { @@ -2715,7 +2715,7 @@ img.tile-removing { background: #7092ff; border-radius: 2px; padding: 0 4px; - margin-top: 5px; + margin-top: 10px; color: white; } From 448b71cbce717ab2f3bc7e99a51c41a15c19fa9c Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 4 Jul 2017 01:03:23 -0400 Subject: [PATCH 30/36] =?UTF-8?q?Bind=20panel=20toggles=20to=20=E2=8C=98?= =?UTF-8?q?=E2=87=A7=20+=20key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ui/info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/info.js b/modules/ui/info.js index 9452b0f30..8f2b94fab 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -117,7 +117,7 @@ export function uiInfo(context) { var key = t('infobox.' + k + '.key', { default: null }); if (!key) return; keybinding - .on(uiCmd('⌘' + key), function() { toggle(k); }); + .on(uiCmd('⌘⇧' + key), function() { toggle(k); }); }); d3.select(document) From cb249cc28b98a0e9aff85e398a2d17a92c9dcd70 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 4 Jul 2017 01:15:36 -0400 Subject: [PATCH 31/36] Rename panel Imagery -> Background, change key G -> B --- css/80_app.css | 1 - data/core.yaml | 6 ++--- dist/locales/en.json | 6 ++--- modules/renderer/tile_layer.js | 3 +-- .../ui/panels/{imagery.js => background.js} | 26 +++++++++---------- modules/ui/panels/index.js | 6 ++--- 6 files changed, 23 insertions(+), 25 deletions(-) rename modules/ui/panels/{imagery.js => background.js} (80%) diff --git a/css/80_app.css b/css/80_app.css index d83d4abf9..c6fd5e36d 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2732,7 +2732,6 @@ img.tile-removing { margin-top: 10px; } -.panel-content-location .imagery-info, .panel-content-location .location-info { margin-top: 10px; } diff --git a/data/core.yaml b/data/core.yaml index 6df29a85e..b96b4c482 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -284,9 +284,9 @@ en: changeset: Changeset unknown: Unknown link_text: History on openstreetmap.org - imagery: - key: G - title: Imagery + background: + key: B + title: Background zoom: Zoom vintage: Vintage unknown: Unknown diff --git a/dist/locales/en.json b/dist/locales/en.json index 3a7d6752b..e821455f6 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -361,9 +361,9 @@ "unknown": "Unknown", "link_text": "History on openstreetmap.org" }, - "imagery": { - "key": "G", - "title": "Imagery", + "background": { + "key": "B", + "title": "Background", "zoom": "Zoom", "vintage": "Vintage", "unknown": "Unknown", diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index 975909686..3c6ccd16c 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -150,7 +150,6 @@ export function rendererTileLayer(context) { function tileCenter(d) { var _ts = tileSize * Math.pow(2, z - d[2]); - var scale = tileSizeAtZoom(d, z); return [ ((d[0] * _ts) - tileOrigin[0] + pixelOffset[0] + (_ts / 2)), ((d[1] * _ts) - tileOrigin[1] + pixelOffset[1] + (_ts / 2)) @@ -245,7 +244,7 @@ export function rendererTileLayer(context) { var center = context.projection.invert(tileCenter(d)); source.getVintage(center, d, function(err, result) { span.text((result && result.range) || - t('infobox.imagery.vintage') + ': ' + t('infobox.imagery.unknown') + t('infobox.background.vintage') + ': ' + t('infobox.background.unknown') ); }); }); diff --git a/modules/ui/panels/imagery.js b/modules/ui/panels/background.js similarity index 80% rename from modules/ui/panels/imagery.js rename to modules/ui/panels/background.js index 7e22d09d0..d1e80e4ac 100644 --- a/modules/ui/panels/imagery.js +++ b/modules/ui/panels/background.js @@ -3,7 +3,7 @@ import _ from 'lodash'; import { t } from '../../util/locale'; -export function uiPanelImagery(context) { +export function uiPanelBackground(context) { var background = context.background(); var currSource = null; var currZoom = ''; @@ -23,7 +23,7 @@ export function uiPanelImagery(context) { var list = selection .append('ul') - .attr('class', 'imagery-info'); + .attr('class', 'background-info'); list .append('li') @@ -31,14 +31,14 @@ export function uiPanelImagery(context) { list .append('li') - .text(t('infobox.imagery.zoom') + ': ') + .text(t('infobox.background.zoom') + ': ') .append('span') .attr('class', 'zoom') .text(currZoom); list .append('li') - .text(t('infobox.imagery.vintage') + ': ') + .text(t('infobox.background.vintage') + ': ') .append('span') .attr('class', 'vintage') .text(currVintage); @@ -51,7 +51,7 @@ export function uiPanelImagery(context) { selection .append('a') - .text(t('infobox.imagery.' + toggle)) + .text(t('infobox.background.' + toggle)) .attr('href', '#') .attr('class', 'button button-toggle-tiles') .on('click', function() { @@ -77,7 +77,7 @@ export function uiPanelImagery(context) { if (!d || !d.length >= 3) return; background.baseLayerSource().getVintage(center, d, function(err, result) { - currVintage = (result && result.range) || t('infobox.imagery.unknown'); + currVintage = (result && result.range) || t('infobox.background.unknown'); selection.selectAll('.vintage') .text(currVintage); }); @@ -88,10 +88,10 @@ export function uiPanelImagery(context) { selection.call(redraw); context.map() - .on('drawn.info-imagery', function() { + .on('drawn.info-background', function() { selection.call(redraw); }) - .on('move.info-imagery', function() { + .on('move.info-background', function() { selection.call(debouncedGetVintage); }); @@ -99,13 +99,13 @@ export function uiPanelImagery(context) { panel.off = function() { context.map() - .on('drawn.info-imagery', null) - .on('move.info-imagery', null); + .on('drawn.info-background', null) + .on('move.info-background', null); }; - panel.id = 'imagery'; - panel.title = t('infobox.imagery.title'); - panel.key = t('infobox.imagery.key'); + panel.id = 'background'; + panel.title = t('infobox.background.title'); + panel.key = t('infobox.background.key'); return panel; diff --git a/modules/ui/panels/index.js b/modules/ui/panels/index.js index e83df2706..b7a283445 100644 --- a/modules/ui/panels/index.js +++ b/modules/ui/panels/index.js @@ -1,16 +1,16 @@ +export * from './background'; export * from './history'; -export * from './imagery'; export * from './location'; export * from './measurement'; +import { uiPanelBackground } from './background'; import { uiPanelHistory } from './history'; -import { uiPanelImagery } from './imagery'; import { uiPanelLocation } from './location'; import { uiPanelMeasurement } from './measurement'; export var uiInfoPanels = { + background: uiPanelBackground, history: uiPanelHistory, - imagery: uiPanelImagery, location: uiPanelLocation, measurement: uiPanelMeasurement, }; From f72d5cb988f9fa4345ad4f8734f0817cb456cc60 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 4 Jul 2017 01:22:41 -0400 Subject: [PATCH 32/36] Remove unused code (infobox isn't classed show/hide anymore) --- modules/ui/panels/background.js | 2 -- modules/ui/panels/history.js | 3 --- modules/ui/panels/location.js | 2 -- modules/ui/panels/measurement.js | 3 --- 4 files changed, 10 deletions(-) diff --git a/modules/ui/panels/background.js b/modules/ui/panels/background.js index d1e80e4ac..3faea68ac 100644 --- a/modules/ui/panels/background.js +++ b/modules/ui/panels/background.js @@ -11,8 +11,6 @@ export function uiPanelBackground(context) { function redraw(selection) { - if (d3.selectAll('.infobox.hide').size()) return; // infobox is hidden - if (currSource !== background.baseLayerSource().name()) { currSource = background.baseLayerSource().name(); currZoom = ''; diff --git a/modules/ui/panels/history.js b/modules/ui/panels/history.js index e5bea1d95..f6595c77f 100644 --- a/modules/ui/panels/history.js +++ b/modules/ui/panels/history.js @@ -88,8 +88,6 @@ export function uiPanelHistory(context) { function redraw(selection) { - if (d3.selectAll('.infobox.hide').size()) return; // infobox is hidden - var selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), singular = selected.length === 1 ? selected[0] : null; @@ -137,7 +135,6 @@ export function uiPanelHistory(context) { } - var panel = function(selection) { selection.call(redraw); diff --git a/modules/ui/panels/location.js b/modules/ui/panels/location.js index ffb4c9b0a..054cb6079 100644 --- a/modules/ui/panels/location.js +++ b/modules/ui/panels/location.js @@ -21,8 +21,6 @@ export function uiPanelLocation(context) { function redraw(selection) { - if (d3.selectAll('.infobox.hide').size()) return; // infobox is hidden - selection.html(''); var list = selection diff --git a/modules/ui/panels/measurement.js b/modules/ui/panels/measurement.js index 499ed29bf..5dd13005b 100644 --- a/modules/ui/panels/measurement.js +++ b/modules/ui/panels/measurement.js @@ -110,8 +110,6 @@ export function uiPanelMeasurement(context) { function redraw(selection) { - if (d3.selectAll('.infobox.hide').size()) return; // infobox is hidden - var resolver = context.graph(), selected = _.filter(context.selectedIDs(), function(e) { return context.hasEntity(e); }), singular = selected.length === 1 ? selected[0] : null, @@ -211,7 +209,6 @@ export function uiPanelMeasurement(context) { } - var panel = function(selection) { selection.call(redraw); From fd399de9d79a84d017ee4d74a77ca55dde1f609a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 4 Jul 2017 01:26:47 -0400 Subject: [PATCH 33/36] Fix changeset URL --- modules/ui/panels/history.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/panels/history.js b/modules/ui/panels/history.js index f6595c77f..127628e57 100644 --- a/modules/ui/panels/history.js +++ b/modules/ui/panels/history.js @@ -72,7 +72,7 @@ export function uiPanelHistory(context) { links .append('a') .attr('class', 'changeset-osm-link') - .attr('href', context.connection().userURL(entity.changeset)) + .attr('href', context.connection().changesetURL(entity.changeset)) .attr('target', '_blank') .attr('tabindex', -1) .text('OSM'); From 562edc8121340ce3c4cbdce440655ece7bb65035 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 4 Jul 2017 02:00:39 -0400 Subject: [PATCH 34/36] Remove unused d3 imports --- modules/ui/panels/history.js | 1 - modules/ui/panels/location.js | 1 - 2 files changed, 2 deletions(-) diff --git a/modules/ui/panels/history.js b/modules/ui/panels/history.js index 127628e57..1682d77f7 100644 --- a/modules/ui/panels/history.js +++ b/modules/ui/panels/history.js @@ -1,4 +1,3 @@ -import * as d3 from 'd3'; import _ from 'lodash'; import { t } from '../../util/locale'; import { svgIcon } from '../../svg'; diff --git a/modules/ui/panels/location.js b/modules/ui/panels/location.js index 054cb6079..d6180b078 100644 --- a/modules/ui/panels/location.js +++ b/modules/ui/panels/location.js @@ -1,4 +1,3 @@ -import * as d3 from 'd3'; import _ from 'lodash'; import { t } from '../../util/locale'; import { services } from '../../services'; From 3043323ba93c8abde03245e86fede7f44e046b9f Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 4 Jul 2017 02:14:30 -0400 Subject: [PATCH 35/36] Rename infobox to info-panels --- css/80_app.css | 18 +++++++++--------- data/core.yaml | 4 ++-- data/shortcuts.json | 4 ++-- dist/locales/en.json | 4 ++-- modules/renderer/tile_layer.js | 2 +- modules/ui/info.js | 16 ++++++++-------- modules/ui/panels/background.js | 12 ++++++------ modules/ui/panels/history.js | 24 ++++++++++++------------ modules/ui/panels/location.js | 8 ++++---- modules/ui/panels/measurement.js | 24 ++++++++++++------------ 10 files changed, 58 insertions(+), 58 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index c6fd5e36d..e2ec1530c 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2647,7 +2647,7 @@ img.tile-removing { /* Info Box ------------------------------------------------------- */ -.infobox { +.info-panels { display: flex; flex-flow: row-reverse wrap-reverse; position: absolute; @@ -2657,18 +2657,18 @@ img.tile-removing { -ms-user-select: element; } -.infobox h1, -.infobox h2, -.infobox h3, -.infobox h4, -.infobox h5 { +.info-panels h1, +.info-panels h2, +.info-panels h3, +.info-panels h4, +.info-panels h5 { display: inline-block; margin-bottom: 0; } -.infobox h1, -.infobox h2, -.infobox h3 { +.info-panels h1, +.info-panels h2, +.info-panels h3 { color: #ff8; } diff --git a/data/core.yaml b/data/core.yaml index b96b4c482..3653dd58f 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -272,7 +272,7 @@ en: contributors: list: "Edits by {users}" truncated_list: "Edits by {users} and {count} others" - infobox: + info_panels: key: I history: key: H @@ -1101,7 +1101,7 @@ en: lasso: "Draw a selection lasso around features" with_selected: title: "With feature selected" - infobox: "Toggle info / measurement box" + info_panels: "Toggle information panels" edit_menu: "Toggle edit menu" vertex_selected: title: "With node selected" diff --git a/data/shortcuts.json b/data/shortcuts.json index e2b04c9fa..9b9f602f8 100644 --- a/data/shortcuts.json +++ b/data/shortcuts.json @@ -94,8 +94,8 @@ "text": "shortcuts.browsing.with_selected.edit_menu" }, { "modifiers": ["⌘"], - "shortcuts": ["infobox.key"], - "text": "shortcuts.browsing.with_selected.infobox" + "shortcuts": ["info_panels.key"], + "text": "shortcuts.browsing.with_selected.info_panels" }, { diff --git a/dist/locales/en.json b/dist/locales/en.json index e821455f6..dacd734dd 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -348,7 +348,7 @@ "list": "Edits by {users}", "truncated_list": "Edits by {users} and {count} others" }, - "infobox": { + "info_panels": { "key": "I", "history": { "key": "H", @@ -979,7 +979,7 @@ }, "with_selected": { "title": "With feature selected", - "infobox": "Toggle info / measurement box", + "info_panels": "Toggle information panels", "edit_menu": "Toggle edit menu" }, "vertex_selected": { diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index 3c6ccd16c..ffc89a1c5 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -244,7 +244,7 @@ export function rendererTileLayer(context) { var center = context.projection.invert(tileCenter(d)); source.getVintage(center, d, function(err, result) { span.text((result && result.range) || - t('infobox.background.vintage') + ': ' + t('infobox.background.unknown') + t('info_panels.background.vintage') + ': ' + t('info_panels.background.unknown') ); }); }); diff --git a/modules/ui/info.js b/modules/ui/info.js index 8f2b94fab..6f02ca501 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -26,7 +26,7 @@ export function uiInfo(context) { function redraw() { var activeids = ids.filter(function(k) { return active[k]; }).sort(); - var containers = infobox.selectAll('.panel-container') + var containers = infoPanels.selectAll('.panel-container') .data(activeids, function(k) { return k; }); containers.exit() @@ -70,7 +70,7 @@ export function uiInfo(context) { // redraw the panels - infobox.selectAll('.panel-content') + infoPanels.selectAll('.panel-content') .each(function(d) { d3.select(this).call(panels[d]); }); @@ -100,21 +100,21 @@ export function uiInfo(context) { } - var infobox = selection.selectAll('.infobox') + var infoPanels = selection.selectAll('.info-panels') .data([0]); - infobox = infobox.enter() + infoPanels = infoPanels.enter() .append('div') - .attr('class', 'infobox') - .merge(infobox); + .attr('class', 'info-panels') + .merge(infoPanels); redraw(); var keybinding = d3keybinding('info') - .on(uiCmd('⌘' + t('infobox.key')), toggle); + .on(uiCmd('⌘' + t('info_panels.key')), toggle); ids.forEach(function(k) { - var key = t('infobox.' + k + '.key', { default: null }); + var key = t('info_panels.' + k + '.key', { default: null }); if (!key) return; keybinding .on(uiCmd('⌘⇧' + key), function() { toggle(k); }); diff --git a/modules/ui/panels/background.js b/modules/ui/panels/background.js index 3faea68ac..44725af1b 100644 --- a/modules/ui/panels/background.js +++ b/modules/ui/panels/background.js @@ -29,14 +29,14 @@ export function uiPanelBackground(context) { list .append('li') - .text(t('infobox.background.zoom') + ': ') + .text(t('info_panels.background.zoom') + ': ') .append('span') .attr('class', 'zoom') .text(currZoom); list .append('li') - .text(t('infobox.background.vintage') + ': ') + .text(t('info_panels.background.vintage') + ': ') .append('span') .attr('class', 'vintage') .text(currVintage); @@ -49,7 +49,7 @@ export function uiPanelBackground(context) { selection .append('a') - .text(t('infobox.background.' + toggle)) + .text(t('info_panels.background.' + toggle)) .attr('href', '#') .attr('class', 'button button-toggle-tiles') .on('click', function() { @@ -75,7 +75,7 @@ export function uiPanelBackground(context) { if (!d || !d.length >= 3) return; background.baseLayerSource().getVintage(center, d, function(err, result) { - currVintage = (result && result.range) || t('infobox.background.unknown'); + currVintage = (result && result.range) || t('info_panels.background.unknown'); selection.selectAll('.vintage') .text(currVintage); }); @@ -102,8 +102,8 @@ export function uiPanelBackground(context) { }; panel.id = 'background'; - panel.title = t('infobox.background.title'); - panel.key = t('infobox.background.key'); + panel.title = t('info_panels.background.title'); + panel.key = t('info_panels.background.key'); return panel; diff --git a/modules/ui/panels/history.js b/modules/ui/panels/history.js index 1682d77f7..a1c604d00 100644 --- a/modules/ui/panels/history.js +++ b/modules/ui/panels/history.js @@ -7,10 +7,10 @@ export function uiPanelHistory(context) { function displayTimestamp(entity) { - if (!entity.timestamp) return t('infobox.history.unknown'); + if (!entity.timestamp) return t('info_panels.history.unknown'); var d = new Date(entity.timestamp); - if (isNaN(d.getTime())) return t('infobox.history.unknown'); + if (isNaN(d.getTime())) return t('info_panels.history.unknown'); return d.toLocaleString(); } @@ -20,7 +20,7 @@ export function uiPanelHistory(context) { if (!entity.user) { selection .append('span') - .text(t('infobox.history.unknown')); + .text(t('info_panels.history.unknown')); return; } @@ -55,7 +55,7 @@ export function uiPanelHistory(context) { if (!entity.changeset) { selection .append('span') - .text(t('infobox.history.unknown')); + .text(t('info_panels.history.unknown')); return; } @@ -95,7 +95,7 @@ export function uiPanelHistory(context) { selection .append('h4') .attr('class', 'history-heading') - .text(singular || t('infobox.history.selected', { n: selected.length })); + .text(singular || t('info_panels.history.selected', { n: selected.length })); if (!singular) return; @@ -106,20 +106,20 @@ export function uiPanelHistory(context) { list .append('li') - .text(t('infobox.history.version') + ': ' + entity.version); + .text(t('info_panels.history.version') + ': ' + entity.version); list .append('li') - .text(t('infobox.history.last_edit') + ': ' + displayTimestamp(entity)); + .text(t('info_panels.history.last_edit') + ': ' + displayTimestamp(entity)); list .append('li') - .text(t('infobox.history.edited_by') + ': ') + .text(t('info_panels.history.edited_by') + ': ') .call(displayUser, entity); list .append('li') - .text(t('infobox.history.changeset') + ': ') + .text(t('info_panels.history.changeset') + ': ') .call(displayChangeset, entity); selection @@ -130,7 +130,7 @@ export function uiPanelHistory(context) { .attr('href', context.connection().historyURL(entity)) .call(svgIcon('#icon-out-link', 'inline')) .append('span') - .text(t('infobox.history.link_text')); + .text(t('info_panels.history.link_text')); } @@ -149,8 +149,8 @@ export function uiPanelHistory(context) { }; panel.id = 'history'; - panel.title = t('infobox.history.title'); - panel.key = t('infobox.history.key'); + panel.title = t('info_panels.history.title'); + panel.key = t('info_panels.history.key'); return panel; diff --git a/modules/ui/panels/location.js b/modules/ui/panels/location.js index d6180b078..a0051ab50 100644 --- a/modules/ui/panels/location.js +++ b/modules/ui/panels/location.js @@ -52,12 +52,12 @@ export function uiPanelLocation(context) { var debouncedGetLocation = _.debounce(getLocation, 250); function getLocation(selection, coord) { if (!services.geocoder) { - currLocation = t('infobox.location.unknown_location'); + currLocation = t('info_panels.location.unknown_location'); selection.selectAll('.location-info') .text(currLocation); } else { services.geocoder.reverse(coord, function(err, result) { - currLocation = result ? result.display_name : t('infobox.location.unknown_location'); + currLocation = result ? result.display_name : t('info_panels.location.unknown_location'); selection.selectAll('.location-info') .text(currLocation); }); @@ -80,8 +80,8 @@ export function uiPanelLocation(context) { }; panel.id = 'location'; - panel.title = t('infobox.location.title'); - panel.key = t('infobox.location.key'); + panel.title = t('info_panels.location.title'); + panel.key = t('info_panels.location.key'); return panel; diff --git a/modules/ui/panels/measurement.js b/modules/ui/panels/measurement.js index 5dd13005b..3c9345a97 100644 --- a/modules/ui/panels/measurement.js +++ b/modules/ui/panels/measurement.js @@ -121,7 +121,7 @@ export function uiPanelMeasurement(context) { selection .append('h4') .attr('class', 'measurement-heading') - .text(singular || t('infobox.measurement.selected', { n: selected.length })); + .text(singular || t('info_panels.measurement.selected', { n: selected.length })); if (!selected.length) return; @@ -140,7 +140,7 @@ export function uiPanelMeasurement(context) { if (!singular) { list .append('li') - .text(t('infobox.measurement.center') + ': ' + + .text(t('info_panels.measurement.center') + ': ' + center[0].toFixed(OSM_PRECISION) + ', ' + center[1].toFixed(OSM_PRECISION) ); return; @@ -154,19 +154,19 @@ export function uiPanelMeasurement(context) { var closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()), feature = entity.asGeoJSON(resolver), length = radiansToMeters(d3GeoLength(toLineString(feature))), - lengthLabel = t('infobox.measurement.' + (closed ? 'perimeter' : 'length')), + lengthLabel = t('info_panels.measurement.' + (closed ? 'perimeter' : 'length')), centroid = d3GeoCentroid(feature); list .append('li') - .text(t('infobox.measurement.geometry') + ': ' + - (closed ? t('infobox.measurement.closed') + ' ' : '') + t('geometry.' + geometry) ); + .text(t('info_panels.measurement.geometry') + ': ' + + (closed ? t('info_panels.measurement.closed') + ' ' : '') + t('geometry.' + geometry) ); if (closed) { var area = steradiansToSqmeters(entity.area(resolver)); list .append('li') - .text(t('infobox.measurement.area') + ': ' + displayArea(area)); + .text(t('info_panels.measurement.area') + ': ' + displayArea(area)); } list @@ -175,7 +175,7 @@ export function uiPanelMeasurement(context) { list .append('li') - .text(t('infobox.measurement.centroid') + ': ' + + .text(t('info_panels.measurement.centroid') + ': ' + centroid[0].toFixed(OSM_PRECISION) + ', ' + centroid[1].toFixed(OSM_PRECISION) ); @@ -184,7 +184,7 @@ export function uiPanelMeasurement(context) { selection .append('a') - .text(t('infobox.measurement.' + toggle)) + .text(t('info_panels.measurement.' + toggle)) .attr('href', '#') .attr('class', 'button button-toggle-units') .on('click', function() { @@ -194,11 +194,11 @@ export function uiPanelMeasurement(context) { }); } else { - var centerLabel = t('infobox.measurement.' + (entity.type === 'node' ? 'location' : 'center')); + var centerLabel = t('info_panels.measurement.' + (entity.type === 'node' ? 'location' : 'center')); list .append('li') - .text(t('infobox.measurement.geometry') + ': ' + t('geometry.' + geometry)); + .text(t('info_panels.measurement.geometry') + ': ' + t('geometry.' + geometry)); list .append('li') @@ -224,8 +224,8 @@ export function uiPanelMeasurement(context) { }; panel.id = 'measurement'; - panel.title = t('infobox.measurement.title'); - panel.key = t('infobox.measurement.key'); + panel.title = t('info_panels.measurement.title'); + panel.key = t('info_panels.measurement.key'); return panel; From 76f9397bb55490999a24213964aa412f654965c1 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 5 Jul 2017 18:12:51 -0400 Subject: [PATCH 36/36] Add Tools tab to keyboard shortcuts, add panels key shortcuts --- css/80_app.css | 6 ++++++ data/core.yaml | 26 +++++++++++++++++--------- data/shortcuts.json | 40 ++++++++++++++++++++++++++++++++++++---- dist/locales/en.json | 30 ++++++++++++++++++++---------- modules/ui/shortcuts.js | 2 +- 5 files changed, 80 insertions(+), 24 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index e2ec1530c..190766206 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -3172,6 +3172,7 @@ img.tile-removing { .modal-shortcuts .modal-section:last-child { padding-top: 10px; + min-height: 275px; } .modal-shortcuts .tabs-bar { @@ -3206,6 +3207,11 @@ img.tile-removing { width: 50%; } +.modal-shortcuts .shortcut-tab-tools .shortcut-column { + flex: 1 1 100%; + width: 100%; +} + .modal-shortcuts td { padding-bottom: 5px; } diff --git a/data/core.yaml b/data/core.yaml index 3653dd58f..4d2ce97f7 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -274,6 +274,14 @@ en: truncated_list: "Edits by {users} and {count} others" info_panels: key: I + background: + key: B + title: Background + zoom: Zoom + vintage: Vintage + unknown: Unknown + show_tiles: Show Tiles + hide_tiles: Hide Tiles history: key: H title: History @@ -284,14 +292,6 @@ en: changeset: Changeset unknown: Unknown link_text: History on openstreetmap.org - background: - key: B - title: Background - zoom: Zoom - vintage: Vintage - unknown: Unknown - show_tiles: Show Tiles - hide_tiles: Hide Tiles location: key: L title: Location @@ -1101,7 +1101,6 @@ en: lasso: "Draw a selection lasso around features" with_selected: title: "With feature selected" - info_panels: "Toggle information panels" edit_menu: "Toggle edit menu" vertex_selected: title: "With node selected" @@ -1141,3 +1140,12 @@ en: undo: "Undo last action" redo: "Redo last action" save: "Save changes" + tools: + title: "Tools" + info: + title: "Information" + all: "Toggle all information panels" + background: "Toggle background panel" + history: "Toggle history panel" + location: "Toggle location panel" + measurement: "Toggle measurement panel" diff --git a/data/shortcuts.json b/data/shortcuts.json index 9b9f602f8..924d9b868 100644 --- a/data/shortcuts.json +++ b/data/shortcuts.json @@ -93,9 +93,8 @@ "shortcuts": ["Right-click", "shortcuts.key.space"], "text": "shortcuts.browsing.with_selected.edit_menu" }, { - "modifiers": ["⌘"], - "shortcuts": ["info_panels.key"], - "text": "shortcuts.browsing.with_selected.info_panels" + "shortcuts": [], + "text": "" }, { @@ -166,7 +165,7 @@ "shortcuts": ["Z"], "text": "shortcuts.editing.commands.undo" }, { - "modifiers": ["⌘","⇧"], + "modifiers": ["⌘", "⇧"], "shortcuts": ["Z"], "text": "shortcuts.editing.commands.redo" }, { @@ -221,5 +220,38 @@ ] } ] + }, { + "tab": "tools", + "text": "shortcuts.tools.title", + "columns" : [ + { + "rows": [ + { + "section": "info", + "text": "shortcuts.tools.info.title" + }, { + "modifiers": ["⌘"], + "shortcuts": ["info_panels.key"], + "text": "shortcuts.tools.info.all" + }, { + "modifiers": ["⌘", "⇧"], + "shortcuts": ["info_panels.background.key"], + "text": "shortcuts.tools.info.background" + }, { + "modifiers": ["⌘", "⇧"], + "shortcuts": ["info_panels.history.key"], + "text": "shortcuts.tools.info.history" + }, { + "modifiers": ["⌘", "⇧"], + "shortcuts": ["info_panels.location.key"], + "text": "shortcuts.tools.info.location" + }, { + "modifiers": ["⌘", "⇧"], + "shortcuts": ["info_panels.measurement.key"], + "text": "shortcuts.tools.info.measurement" + } + ] + } + ] } ]} diff --git a/dist/locales/en.json b/dist/locales/en.json index dacd734dd..d307aa31b 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -350,6 +350,15 @@ }, "info_panels": { "key": "I", + "background": { + "key": "B", + "title": "Background", + "zoom": "Zoom", + "vintage": "Vintage", + "unknown": "Unknown", + "show_tiles": "Show Tiles", + "hide_tiles": "Hide Tiles" + }, "history": { "key": "H", "title": "History", @@ -361,15 +370,6 @@ "unknown": "Unknown", "link_text": "History on openstreetmap.org" }, - "background": { - "key": "B", - "title": "Background", - "zoom": "Zoom", - "vintage": "Vintage", - "unknown": "Unknown", - "show_tiles": "Show Tiles", - "hide_tiles": "Hide Tiles" - }, "location": { "key": "L", "title": "Location", @@ -979,7 +979,6 @@ }, "with_selected": { "title": "With feature selected", - "info_panels": "Toggle information panels", "edit_menu": "Toggle edit menu" }, "vertex_selected": { @@ -1025,6 +1024,17 @@ "redo": "Redo last action", "save": "Save changes" } + }, + "tools": { + "title": "Tools", + "info": { + "title": "Information", + "all": "Toggle all information panels", + "background": "Toggle background panel", + "history": "Toggle history panel", + "location": "Toggle location panel", + "measurement": "Toggle measurement panel" + } } }, "presets": { diff --git a/modules/ui/shortcuts.js b/modules/ui/shortcuts.js index bf5245990..2e2a63d8f 100644 --- a/modules/ui/shortcuts.js +++ b/modules/ui/shortcuts.js @@ -105,7 +105,7 @@ export function uiShortcuts() { var shortcutsEnter = shortcuts .enter() .append('div') - .attr('class', 'shortcut-tab'); + .attr('class', function(d) { return 'shortcut-tab shortcut-tab-' + d.tab; }); var columnsEnter = shortcutsEnter .selectAll('.shortcut-column')