esri-leaflet
Version:
Leaflet plugins for consuming ArcGIS Online and ArcGIS Server services.
248 lines (214 loc) • 7.48 kB
JavaScript
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;