UNPKG

esri-leaflet

Version:

Leaflet plugins for consuming ArcGIS Online and ArcGIS Server services.

248 lines (214 loc) 7.48 kB
import { CRS, DomEvent, TileLayer, Util } from "leaflet"; import { warn, getUrlParams, setEsriAttribution, removeEsriAttribution, } from "../Util.js"; import mapService from "../Services/MapService.js"; export const TiledMapLayer = TileLayer.extend({ options: { zoomOffsetAllowance: 0.1, errorTileUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEABAMAAACuXLVVAAAAA1BMVEUzNDVszlHHAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAAAAAAAAAB6mUWpAAAADZJREFUeJztwQEBAAAAgiD/r25IQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7waBAAABw08RwAAAAABJRU5ErkJggg==", }, statics: { MercatorZoomLevels: { 0: 156543.03392799999, 1: 78271.516963999893, 2: 39135.758482000099, 3: 19567.879240999901, 4: 9783.9396204999593, 5: 4891.9698102499797, 6: 2445.9849051249898, 7: 1222.9924525624899, 8: 611.49622628138002, 9: 305.74811314055802, 10: 152.874056570411, 11: 76.437028285073197, 12: 38.218514142536598, 13: 19.109257071268299, 14: 9.5546285356341496, 15: 4.7773142679493699, 16: 2.38865713397468, 17: 1.1943285668550501, 18: 0.59716428355981699, 19: 0.29858214164761698, 20: 0.14929107082381, 21: 0.07464553541191, 22: 0.0373227677059525, 23: 0.0186613838529763, }, }, initialize(options) { options = Util.setOptions(this, options); // support apikey property being passed in if (options.apikey) { options.token = options.apikey; } // set the urls options = getUrlParams(options); this.tileUrl = `${(options.proxy ? `${options.proxy}?` : "") + options.url}tile/{z}/{y}/{x}${options.requestParams && Object.keys(options.requestParams).length > 0 ? Util.getParamString(options.requestParams) : ""}`; // Remove subdomain in url // https://github.com/Esri/esri-leaflet/issues/991 if (options.url.indexOf("{s}") !== -1 && options.subdomains) { options.url = options.url.replace("{s}", options.subdomains[0]); } this.service = mapService(options); this.service.addEventParent(this); const arcgisonline = new RegExp(/tiles.arcgis(online)?\.com/g); if (arcgisonline.test(options.url)) { this.tileUrl = this.tileUrl.replace("://tiles", "://tiles{s}"); options.subdomains = ["1", "2", "3", "4"]; } if (this.options.token) { this.tileUrl += `?token=${this.options.token}`; } // init layer by calling TileLayers initialize method TileLayer.prototype.initialize.call(this, this.tileUrl, options); }, getTileUrl(tilePoint) { const zoom = this._getZoomForUrl(); return Util.template( this.tileUrl, Util.extend( { s: this._getSubdomain(tilePoint), x: tilePoint.x, y: tilePoint.y, // try lod map first, then just default to zoom level z: this._lodMap && this._lodMap[zoom] !== undefined ? this._lodMap[zoom] : zoom, }, this.options, ), ); }, createTile(coords, done) { const tile = document.createElement("img"); DomEvent.on(tile, "load", Util.bind(this._tileOnLoad, this, done, tile)); DomEvent.on(tile, "error", Util.bind(this._tileOnError, this, done, tile)); if (this.options.crossOrigin) { tile.crossOrigin = ""; } /* Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons http://www.w3.org/TR/WCAG20-TECHS/H67 */ tile.alt = ""; // if there is no lod map or an lod map with a proper zoom load the tile // otherwise wait for the lod map to become available if ( !this._lodMap || (this._lodMap && this._lodMap[this._getZoomForUrl()] !== undefined) ) { tile.src = this.getTileUrl(coords); } else { this.once( "lodmap", function () { tile.src = this.getTileUrl(coords); }, this, ); } return tile; }, onAdd(map) { // include 'Powered by Esri' in map attribution setEsriAttribution(map); if (!this._lodMap) { this.metadata(function (error, metadata) { if (!error && metadata.spatialReference) { const sr = metadata.spatialReference.latestWkid || metadata.spatialReference.wkid; // display the copyright text from the service using leaflet's attribution control if ( !this.options.attribution && map.attributionControl && metadata.copyrightText ) { this.options.attribution = metadata.copyrightText; map.attributionControl.addAttribution(this.getAttribution()); } // if the service tiles were published in web mercator using conventional LODs but missing levels, we can try and remap them if ( map.options.crs === CRS.EPSG3857 && (sr === 102100 || sr === 3857) ) { this._lodMap = {}; // create the zoom level data const arcgisLODs = metadata.tileInfo.lods; const correctResolutions = TiledMapLayer.MercatorZoomLevels; for (let i = 0; i < arcgisLODs.length; i++) { const arcgisLOD = arcgisLODs[i]; for (const ci in correctResolutions) { const correctRes = correctResolutions[ci]; if ( this._withinPercentage( arcgisLOD.resolution, correctRes, this.options.zoomOffsetAllowance, ) ) { this._lodMap[ci] = arcgisLOD.level; break; } } } this.fire("lodmap"); } else if ( map.options.crs && map.options.crs.code && map.options.crs.code.indexOf(sr) > -1 ) { // if the projection is WGS84, or the developer is using Proj4 to define a custom CRS, no action is required } else { // if the service was cached in a custom projection and an appropriate LOD hasn't been defined in the map, guide the developer to our Proj4 sample warn( "L.esri.TiledMapLayer is using a non-mercator spatial reference. Support may be available through Proj4Leaflet https://developers.arcgis.com/esri-leaflet/samples/non-mercator-projection/", ); } } }, this); } TileLayer.prototype.onAdd.call(this, map); }, onRemove(map) { removeEsriAttribution(map); TileLayer.prototype.onRemove.call(this, map); }, metadata(callback, context) { this.service.metadata(callback, context); return this; }, identify() { return this.service.identify(); }, find() { return this.service.find(); }, query() { return this.service.query(); }, authenticate(token) { const tokenQs = `?token=${token}`; this.tileUrl = this.options.token ? this.tileUrl.replace(/\?token=(.+)/g, tokenQs) : this.tileUrl + tokenQs; this.options.token = token; this.service.authenticate(token); return this; }, _withinPercentage(a, b, percentage) { const diff = Math.abs(a / b - 1); return diff < percentage; }, }); export function tiledMapLayer(url, options) { return new TiledMapLayer(url, options); } export default tiledMapLayer;