diff --git a/modules/renderer/background.js b/modules/renderer/background.js index dc2bb3d3e..fa2fe6c46 100644 --- a/modules/renderer/background.js +++ b/modules/renderer/background.js @@ -26,6 +26,15 @@ export function rendererBackground(context) { function background(selection) { + // If we are displaying an Esri basemap at high zoom, + // check its tilemap to see how high the zoom can go + if (context.map().zoom() > 18) { + var basemap = baseLayer.source(); + if (basemap && /^EsriWorldImagery/.test(basemap.id)) { + var center = context.map().center(); + basemap.fetchTilemap(center); + } + } var baseFilter = ''; if (detected.cssfilters) { @@ -114,16 +123,17 @@ export function rendererBackground(context) { background.updateImagery = function() { if (context.inIntro()) return; - var b = background.baseLayerSource(), - o = _overlayLayers - .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); }) - .map(function (d) { return d.source().id; }) - .join(','), - meters = geoOffsetToMeters(b.offset()), - epsilon = 0.01, - x = +meters[0].toFixed(2), - y = +meters[1].toFixed(2), - q = utilStringQs(window.location.hash.substring(1)); + var b = background.baseLayerSource(); + var o = _overlayLayers + .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); }) + .map(function (d) { return d.source().id; }) + .join(','); + + var meters = geoOffsetToMeters(b.offset()); + var epsilon = 0.01; + var x = +meters[0].toFixed(2); + var y = +meters[1].toFixed(2); + var q = utilStringQs(window.location.hash.substring(1)); var id = b.id; if (id === 'custom') { @@ -215,13 +225,12 @@ export function rendererBackground(context) { if (!osm) return background; var blacklists = context.connection().imageryBlacklists(); + var template = d.template(); + var fail = false; + var tested = 0; + var regex; - var template = d.template(), - fail = false, - tested = 0, - regex, i; - - for (i = 0; i < blacklists.length; i++) { + for (var i = 0; i < blacklists.length; i++) { try { regex = new RegExp(blacklists[i]); fail = regex.test(template); @@ -270,7 +279,6 @@ export function rendererBackground(context) { background.toggleOverlayLayer = function(d) { var layer; - for (var i = 0; i < _overlayLayers.length; i++) { layer = _overlayLayers[i]; if (layer.source() === d) { @@ -350,12 +358,12 @@ export function rendererBackground(context) { return geoExtent([args[2], args[1]]); } - var dataImagery = data.imagery || [], - q = utilStringQs(window.location.hash.substring(1)), - requested = q.background || q.layer, - extent = parseMap(q.map), - first, - best; + var dataImagery = data.imagery || []; + var q = utilStringQs(window.location.hash.substring(1)); + var requested = q.background || q.layer; + var extent = parseMap(q.map); + var first; + var best; // Add all the available imagery sources _backgroundSources = dataImagery.map(function(source) { diff --git a/modules/renderer/background_source.js b/modules/renderer/background_source.js index 60a870104..074796972 100644 --- a/modules/renderer/background_source.js +++ b/modules/renderer/background_source.js @@ -9,7 +9,13 @@ import { import { json as d3_json } from 'd3-request'; import { t } from '../util/locale'; -import { geoExtent, geoPolygonIntersectsPolygon } from '../geo'; + +import { + geoExtent, + geoPolygonIntersectsPolygon, + geoSphericalDistance +} from '../geo'; + import { jsonpRequest } from '../util/jsonp_request'; import { utilDetect } from '../util/detect'; @@ -111,19 +117,20 @@ export function rendererBackgroundSource(data) { var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize))); switch (this.projection) { - case 'EPSG:4326': // todo: alternative codes of WGS 84? - return { - x: lon * 180 / Math.PI, - y: lat * 180 / Math.PI - }; - default: // EPSG:3857 and synonyms - var mercCoords = d3_geoMercatorRaw(lon, lat); - return { - x: 20037508.34 / Math.PI * mercCoords[0], - y: 20037508.34 / Math.PI * mercCoords[1] - }; + case 'EPSG:4326': // todo: alternative codes of WGS 84? + return { + x: lon * 180 / Math.PI, + y: lat * 180 / Math.PI + }; + default: // EPSG:3857 and synonyms + var mercCoords = d3_geoMercatorRaw(lon, lat); + return { + x: 20037508.34 / Math.PI * mercCoords[0], + y: 20037508.34 / Math.PI * mercCoords[1] + }; } }).bind(this); + var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]); var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]); return template @@ -207,13 +214,13 @@ rendererBackgroundSource.Bing = function(data, dispatch) { data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z'; - var bing = rendererBackgroundSource(data), - 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 = {}, - inflight = {}, - providers = []; + var bing = rendererBackgroundSource(data); + var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // Same as P2 and JOSM + var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + + key + '&jsonp={callback}'; + var cache = {}; + var inflight = {}; + var providers = []; jsonpRequest(url, function(json) { providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) { @@ -246,10 +253,10 @@ rendererBackgroundSource.Bing = function(data, dispatch) { 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 - url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + + var tileId = tileCoord.slice(0, 3).join('/'); + var zoom = Math.min(tileCoord[2], 21); + var centerPoint = center[1] + ',' + center[0]; // lat,lng + var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key + '&jsonp={callback}'; if (inflight[tileId]) return; @@ -292,13 +299,18 @@ rendererBackgroundSource.Bing = function(data, dispatch) { rendererBackgroundSource.Esri = function(data) { - var esri = rendererBackgroundSource(data), - cache = {}, - inflight = {}; + var esri = rendererBackgroundSource(data); + var cache = {}; + var inflight = {}; + var _prevCenter; // use a tilemap service to set maximum zoom for esri tiles dynamically // https://developers.arcgis.com/documentation/tiled-elevation-service/ esri.fetchTilemap = function(center) { + // skip if we have already fetched a tilemap within 5km + if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return; + _prevCenter = center; + // tiles are available globally to zoom level 19, afterward they may or may not be present var z = 20; @@ -316,30 +328,28 @@ rendererBackgroundSource.Esri = function(data) { d3_json(tilemapUrl, function (err, tilemap) { if (err || !tilemap) return; - var tiles = true; - for (var i=0; i= context.minEditableZoom(); div.style('display', canEdit ? 'none' : 'block'); - // if an Esri basemap is being displayed and native zoom past 19 is enabled, check the tilemap - if (canEdit) { - var basemap = context.background().baseLayerSource(); - if (/^EsriWorldImagery/.test(basemap.id) && basemap.scaleExtent[1] > 19) { - var center = context.map().center(); - basemap.fetchTilemap(center); - } - } } context.map()