esri-leaflet-vector
Version:
Esri vector basemap and vector tile layer plugin for Leaflet.
235 lines (214 loc) • 7.5 kB
JavaScript
import { Util } from "esri-leaflet";
import {
getBasemapStyleUrl,
getAttributionData,
getBasemapStyleV2Url,
} from "./Util.js";
import { VectorTileLayer } from "./VectorTileLayer.js";
const POWERED_BY_ESRI_ATTRIBUTION_STRING =
'Powered by <a href="https://www.esri.com">Esri</a>';
export const VectorBasemapLayer = VectorTileLayer.extend({
/**
* Populates "this.options" to be used in the rest of the module.
*
* @param {string} key
* @param {object} options optional
*/
initialize(key, options) {
// Default to the v1 service endpoint
if (!options.version) {
if (key.includes("/")) {
options.version = 2;
} else {
options.version = 1;
}
}
if (!key) {
// Default style enum if none provided
key = options.version === 1 ? "ArcGIS:Streets" : "arcgis/streets";
}
// If no API Key or token is provided (support outdated casing apiKey of apikey)
if (!(options.apikey || options.apiKey || options.token)) {
throw new Error(
"An API Key or token is required for vectorBasemapLayer.",
);
}
// Validate v2 service params
if (options.version !== 2) {
if (options.language) {
throw new Error(
"The language parameter is only supported by the basemap styles service v2. Provide a v2 style enumeration to use this option.",
);
}
if (options.worldview) {
throw new Error(
"The worldview parameter is only supported by the basemap styles service v2. Provide a v2 style enumeration to use this option.",
);
}
if (options.places) {
throw new Error(
"The places parameter is only supported by the basemap styles service v2. Provide a v2 style enumeration to use this option.",
);
}
}
// Determine layer order
if (!options.pane) {
if (key.includes(":Label") || key.includes("/label")) {
options.pane = "esri-labels";
} else if (key.includes(":Detail") || key.includes("/detail")) {
options.pane = "esri-detail";
} else {
// Create layer in the tilePane by default
options.pane = "tilePane";
}
}
// Options has been configured, continue on to create the layer:
VectorTileLayer.prototype.initialize.call(this, key, options);
},
/**
* Creates the maplibreGLJSLayer using "this.options"
*/
_createLayer() {
let styleUrl;
if (this.options.version && this.options.version === 2) {
styleUrl = getBasemapStyleV2Url(this.options.key, this.options.apikey, {
language: this.options.language,
worldview: this.options.worldview,
places: this.options.places,
baseUrl: this.options.baseUrl,
});
} else {
styleUrl = getBasemapStyleUrl(this.options.key, this.options.apikey);
}
// show error warning on successful response for previous version(1)
if (this.options.version && this.options.version === 1) {
fetch(styleUrl)
.then((response) => response.json())
.then((styleData) => {
if (styleData.error) {
console.warn("Error:", styleData.error.message);
}
})
.catch((error) => {
console.warn("Error:", error.message);
});
}
this._createMaplibreLayer(styleUrl);
},
_setupAttribution() {
if (this.options.key.length === 32) {
// this is an itemId
const sources =
this._maplibreGL.getMaplibreMap().style.stylesheet.sources;
const allAttributions = [];
Object.keys(sources).forEach((key) => {
allAttributions.push(sources[key].attribution);
if (
sources[key].copyrightText &&
sources[key].copyrightText &&
sources[key].copyrightText !== "" &&
sources[key].attribution !== sources[key].copyrightText
) {
allAttributions.push(sources[key].copyrightText);
}
});
// In the case of an enum, since the attribution is dynamic, Esri Leaflet
// will add the "Powered by Esri" string. But in this case we are not
// dynamic so we must add it ourselves.
this._map.attributionControl.addAttribution(
`<span class="esri-dynamic-attribution">${POWERED_BY_ESRI_ATTRIBUTION_STRING} | ${allAttributions.join(
", ",
)}</span>`,
);
} else {
// setup dynamic attribution
Util.setEsriAttribution(this._map);
// this is an enum
if (!this.options.attributionUrls) {
this.options.attributionUrls = this._getAttributionUrls(
this.options.key,
);
}
if (this._map && this.options.attributionUrls) {
if (this._map.attributionControl) {
for (
let index = 0;
index < this.options.attributionUrls.length;
index++
) {
const attributionUrl = this.options.attributionUrls[index];
getAttributionData(attributionUrl, this._map);
}
this._map.attributionControl.addAttribution(
'<span class="esri-dynamic-attribution"></span>',
);
}
Util._updateMapAttribution({ target: this._map });
}
}
},
/**
* Given a key, return the attribution url(s).
* @param {string} key
*/
_getAttributionUrls(key) {
if (key.indexOf("OSM:") === 0 || key.indexOf("osm/") === 0) {
return ["https://static.arcgis.com/attribution/Vector/OpenStreetMap_v2"];
} else if (
key.indexOf("ArcGIS:Imagery") === 0 ||
key.indexOf("arcgis/imagery") === 0
) {
return [
"https://static.arcgis.com/attribution/World_Imagery",
"https://static.arcgis.com/attribution/Vector/World_Basemap_v2",
];
}
// default:
return ["https://static.arcgis.com/attribution/Vector/World_Basemap_v2"];
},
_initPane() {
if (!this._map.getPane(this.options.pane)) {
const pane = this._map.createPane(this.options.pane);
pane.style.pointerEvents = "none";
let zIndex = 500;
if (this.options.pane === "esri-detail") {
zIndex = 250;
} else if (this.options.pane === "esri-labels") {
zIndex = 300;
}
pane.style.zIndex = zIndex;
}
},
onRemove(map) {
map.off("moveend", Util._updateMapAttribution);
map.removeLayer(this._maplibreGL);
if (map.attributionControl) {
if (Util.removeEsriAttribution) {
Util.removeEsriAttribution(map);
}
const element = document.getElementsByClassName(
"esri-dynamic-attribution",
);
if (element && element.length > 0) {
const vectorAttribution = element[0].outerHTML;
// call removeAttribution twice here
// this is needed due to the 2 different ways that addAttribution is called inside _setupAttribution.
// leaflet attributionControl.removeAttribution method ignore a call when the attribution sent is not present there
map.attributionControl.removeAttribution(vectorAttribution);
map.attributionControl.removeAttribution(
'<span class="esri-dynamic-attribution"></span>',
);
}
}
},
_asyncAdd() {
const map = this._map;
this._initPane();
map.on("moveend", Util._updateMapAttribution);
this._maplibreGL.addTo(map, this);
},
});
export function vectorBasemapLayer(key, options) {
return new VectorBasemapLayer(key, options);
}
export default vectorBasemapLayer;