diff --git a/data/core.yaml b/data/core.yaml index 902a4e78c..82df21868 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -278,6 +278,10 @@ en: title: Background zoom: Zoom vintage: Vintage + source: Source + description: Description + resolution: Resolution + accuracy: Accuracy unknown: Unknown show_tiles: Show Tiles hide_tiles: Hide Tiles diff --git a/modules/renderer/background_source.js b/modules/renderer/background_source.js index 8cde7b1da..1a0814f21 100644 --- a/modules/renderer/background_source.js +++ b/modules/renderer/background_source.js @@ -133,7 +133,7 @@ export function rendererBackgroundSource(data) { source.copyrightNotices = function() {}; - source.getVintage = function(center, tileCoord, callback) { + source.getMetadata = function(center, tileCoord, callback) { var vintage = { start: localeDateString(source.startDate), end: localeDateString(source.endDate) @@ -190,7 +190,7 @@ rendererBackgroundSource.Bing = function(data, dispatch) { }; - bing.getVintage = function(center, tileCoord, callback) { + bing.getMetadata = function(center, tileCoord, callback) { var tileId = tileCoord.slice(0, 3).join('/'), zoom = Math.min(tileCoord[2], 21), centerPoint = center[1] + ',' + center[0], // lat,lng @@ -232,6 +232,7 @@ rendererBackgroundSource.Bing = function(data, dispatch) { rendererBackgroundSource.Esri = function(data) { // don't request blank tiles, instead overzoom real tiles - #4327 + // deprecated technique, but it works (for now) if (data.template.match(/blankTile/) === null) { data.template = data.template + '?blankTile=false'; } @@ -239,13 +240,26 @@ rendererBackgroundSource.Esri = function(data) { var esri = rendererBackgroundSource(data), cache = {}; - esri.getVintage = function(center, tileCoord, callback) { - var tileId = tileCoord.slice(0, 3).join('/'); - // FIXME: construct service URL - // zoom = Math.min(tileCoord[2], esri.scaleExtent[1]), - // centerPoint = center[1] + ',' + center[0], // lat,lng - // url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + - // '?zl=' + zoom + '&key=' + key + '&jsonp={callback}'; + esri.getMetadata = function(center, tileCoord, callback) { + var tileId = tileCoord.slice(0, 3).join('/'), + zoom = Math.min(tileCoord[2], esri.scaleExtent[1]), + centerPoint = center[0] + ',' + center[1], // long, lat (as it should be) + metadataLayer; + switch (true) { + case zoom >= 19: + metadataLayer = 3; + break; + case zoom >= 17: + metadataLayer = 2; + break; + case zoom >= 13: + metadataLayer = 0; + break; + default: + metadataLayer = 99; + } + // build up query using the layer appropriate to the current zoom + var url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/' + metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json&callback={callback}'; if (!cache[tileId]) { cache[tileId] = {}; @@ -254,24 +268,36 @@ rendererBackgroundSource.Esri = function(data) { return callback(null, cache[tileId].vintage); } - // FIXME: remove dummy result: - callback(null, { start: null, end: null, range: '? - ?'}); + // accurate metadata is only available >= 13 + if (metadataLayer === 99) { + callback(null, { + range: 'Unknown', + source: 'Unknown', + description: 'Unknown', + resolution: 'Unknown', + accuracy: 'Unknown' + }); + } else { + jsonpRequest(url, function(result) { + var err = !result || result.features.length < 1; + if (err) { + return callback(err); + } else { + var vintage = { + // pass through the discrete capture date from metadata + range: localeDateString(result.features[0].attributes.SRC_DATE2), + source: result.features[0].attributes.NICE_NAME, + description: result.features[0].attributes.NICE_DESC, + resolution: result.features[0].attributes.SRC_RES, + accuracy: result.features[0].attributes.SRC_ACC, - // FIXME: call service instead: - // jsonpRequest(url, function(result) { - // var err = (!result && 'Unknown Error') || result.errorDetails; - // if (err) { - // return callback(err); - // } else { - // 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); - // } - // }); + }; + + cache[tileId].vintage = vintage; + return callback(null, vintage); + } + }); + } }; return esri; diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js index 4a1735fa4..1e9927be2 100644 --- a/modules/renderer/tile_layer.js +++ b/modules/renderer/tile_layer.js @@ -255,7 +255,7 @@ export function rendererTileLayer(context) { .each(function(d) { var span = d3.select(this); var center = context.projection.invert(tileCenter(d)); - source.getVintage(center, d, function(err, result) { + source.getMetadata(center, d, function(err, result) { span.text((result && result.range) || t('info_panels.background.vintage') + ': ' + t('info_panels.background.unknown') ); diff --git a/modules/ui/panels/background.js b/modules/ui/panels/background.js index 44725af1b..393e5e8f4 100644 --- a/modules/ui/panels/background.js +++ b/modules/ui/panels/background.js @@ -9,6 +9,10 @@ export function uiPanelBackground(context) { var currZoom = ''; var currVintage = ''; + var currProvider = ''; + var currDescription = ''; + var currResolution = ''; + var currAccuracy = ''; function redraw(selection) { if (currSource !== background.baseLayerSource().name()) { @@ -42,9 +46,35 @@ export function uiPanelBackground(context) { .text(currVintage); if (!currVintage) { - debouncedGetVintage(selection); + debouncedGetMetadata(selection); } + if (currSource === 'Esri World Imagery') { + list + .append('li') + .text(t('info_panels.background.source') + ': ') + .append('span') + .attr('class', 'source') + .text(currProvider); + list + .append('li') + .text(t('info_panels.background.description') + ': ') + .append('span') + .attr('class', 'description') + .text(currDescription); + list + .append('li') + .text(t('info_panels.background.resolution') + ': ') + .append('span') + .attr('class', 'resolution') + .text(currResolution); + list + .append('li') + .text(t('info_panels.background.accuracy') + ': ') + .append('span') + .attr('class', 'accuracy') + .text(currAccuracy); + } var toggle = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles'; selection @@ -60,8 +90,8 @@ export function uiPanelBackground(context) { } - var debouncedGetVintage = _.debounce(getVintage, 250); - function getVintage(selection) { + var debouncedGetMetadata = _.debounce(getMetadata, 250); + function getMetadata(selection) { var tile = d3.select('.layer-background img.tile-center'); // tile near viewport center if (tile.empty()) return; @@ -74,10 +104,25 @@ export function uiPanelBackground(context) { .text(currZoom); if (!d || !d.length >= 3) return; - background.baseLayerSource().getVintage(center, d, function(err, result) { + background.baseLayerSource().getMetadata(center, d, function(err, result) { currVintage = (result && result.range) || t('info_panels.background.unknown'); selection.selectAll('.vintage') .text(currVintage); + // metadata from Esri can tell us the specific provider + if (result.source) { + currSource = result.source; + selection.selectAll('.source') + .text(currSource); + currDescription = result.description; + selection.selectAll('.description') + .text(currDescription); + currResolution = result.resolution; + selection.selectAll('.resolution') + .text(currResolution + ' (m)'); + currAccuracy = result.accuracy; + selection.selectAll('.accuracy') + .text(currAccuracy + ' (m)'); + } }); } @@ -90,7 +135,7 @@ export function uiPanelBackground(context) { selection.call(redraw); }) .on('move.info-background', function() { - selection.call(debouncedGetVintage); + selection.call(debouncedGetMetadata); }); }; diff --git a/modules/ui/status.js b/modules/ui/status.js index d9ba2bd0e..9d9dbc3e2 100644 --- a/modules/ui/status.js +++ b/modules/ui/status.js @@ -29,6 +29,7 @@ export function uiStatus(context) { osm.authenticate(); }); } else { + // eslint-disable-next-line no-warning-comments // TODO: nice messages for different error types selection.text(t('status.error')); } diff --git a/test/spec/spec_helpers.js b/test/spec/spec_helpers.js index 50fba97a4..083969397 100644 --- a/test/spec/spec_helpers.js +++ b/test/spec/spec_helpers.js @@ -20,4 +20,5 @@ mocha.setup({ }); expect = chai.expect; -var d3 = iD.d3; +// eslint-disable-next-line no-unused-vars +var d3 = iD.d3; \ No newline at end of file