esri-map-view
Version:
Display an Esri map view as a web component.
1,288 lines (1,268 loc) • 81.2 kB
JavaScript
'use strict';
var index = require('./index-Df6oH7es.js');
var esriLoader$1 = {exports: {}};
var esriLoader = esriLoader$1.exports;
var hasRequiredEsriLoader;
function requireEsriLoader () {
if (hasRequiredEsriLoader) return esriLoader$1.exports;
hasRequiredEsriLoader = 1;
(function (module, exports) {
(function (global, factory) {
factory(exports) ;
}(esriLoader, (function (exports) {
/* Copyright (c) 2022 Environmental Systems Research Institute, Inc.
* Apache-2.0 */
var isBrowser = typeof window !== 'undefined';
// allow consuming libraries to provide their own Promise implementations
var utils = {
Promise: isBrowser ? window['Promise'] : undefined
};
/* Copyright (c) 2022 Environmental Systems Research Institute, Inc.
* Apache-2.0 */
var DEFAULT_VERSION = '4.25';
var NEXT = 'next';
function parseVersion(version) {
if (version.toLowerCase() === NEXT) {
return NEXT;
}
var match = version && version.match(/^(\d)\.(\d+)/);
return match && {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10)
};
}
/**
* Get the CDN url for a given version
*
* @param version Ex: '4.25' or '3.42'. Defaults to the latest 4.x version.
*/
function getCdnUrl(version) {
if (version === void 0) { version = DEFAULT_VERSION; }
return "https://js.arcgis.com/".concat(version, "/");
}
/**
* Get the CDN url for a the CSS for a given version and/or theme
*
* @param version Ex: '4.25', '3.42', or 'next'. Defaults to the latest 4.x version.
*/
function getCdnCssUrl(version) {
if (version === void 0) { version = DEFAULT_VERSION; }
var baseUrl = getCdnUrl(version);
var parsedVersion = parseVersion(version);
if (parsedVersion !== NEXT && parsedVersion.major === 3) {
// NOTE: at 3.11 the CSS moved from the /js folder to the root
var path = parsedVersion.minor <= 10 ? 'js/' : '';
return "".concat(baseUrl).concat(path, "esri/css/esri.css");
}
else {
// assume 4.x
return "".concat(baseUrl, "esri/themes/light/main.css");
}
}
/* Copyright (c) 2022 Environmental Systems Research Institute, Inc.
* Apache-2.0 */
function createStylesheetLink(href) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
return link;
}
function insertLink(link, before) {
if (before) {
// the link should be inserted before a specific node
var beforeNode = document.querySelector(before);
beforeNode.parentNode.insertBefore(link, beforeNode);
}
else {
// append the link to then end of the head tag
document.head.appendChild(link);
}
}
// check if the css url has been injected or added manually
function getCss(url) {
return document.querySelector("link[href*=\"".concat(url, "\"]"));
}
function getCssUrl(urlOrVersion) {
return !urlOrVersion || parseVersion(urlOrVersion)
// if it's a valid version string return the CDN URL
? getCdnCssUrl(urlOrVersion)
// otherwise assume it's a URL and return that
: urlOrVersion;
}
// lazy load the CSS needed for the ArcGIS API
function loadCss(urlOrVersion, before) {
var url = getCssUrl(urlOrVersion);
var link = getCss(url);
if (!link) {
// create & load the css link
link = createStylesheetLink(url);
insertLink(link, before);
}
return link;
}
/* Copyright (c) 2022 Environmental Systems Research Institute, Inc.
* Apache-2.0 */
var defaultOptions = {};
function createScript(url) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.setAttribute('data-esri-loader', 'loading');
return script;
}
// add a one-time load handler to script
// and optionally add a one time error handler as well
function handleScriptLoad(script, callback, errback) {
var onScriptError;
if (errback) {
// set up an error handler as well
onScriptError = handleScriptError(script, errback);
}
var onScriptLoad = function () {
// pass the script to the callback
callback(script);
// remove this event listener
script.removeEventListener('load', onScriptLoad, false);
if (onScriptError) {
// remove the error listener as well
script.removeEventListener('error', onScriptError, false);
}
};
script.addEventListener('load', onScriptLoad, false);
}
// add a one-time error handler to the script
function handleScriptError(script, callback) {
var onScriptError = function (e) {
// reject the promise and remove this event listener
callback(e.error || new Error("There was an error attempting to load ".concat(script.src)));
// remove this event listener
script.removeEventListener('error', onScriptError, false);
};
script.addEventListener('error', onScriptError, false);
return onScriptError;
}
// allow the user to configure default script options rather than passing options to `loadModules` each time
function setDefaultOptions(options) {
if (options === void 0) { options = {}; }
defaultOptions = options;
}
// get the script injected by this library
function getScript() {
return document.querySelector('script[data-esri-loader]');
}
// has ArcGIS API been loaded on the page yet?
function isLoaded() {
var globalRequire = window['require'];
// .on() ensures that it's Dojo's AMD loader
return globalRequire && globalRequire.on;
}
// load the ArcGIS API on the page
function loadScript(options) {
if (options === void 0) { options = {}; }
// we would have liked to use spread like { ...defaultOptions, ...options }
// but TS would inject a polyfill that would require use to configure rollup w content: 'window'
// if we have another occasion to use spread, let's do that and replace this for...in
var opts = {};
[defaultOptions, options].forEach(function (obj) {
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
opts[prop] = obj[prop];
}
}
});
// URL to load
var version = opts.version;
var url = opts.url || getCdnUrl(version);
return new utils.Promise(function (resolve, reject) {
var script = getScript();
if (script) {
// the API is already loaded or in the process of loading...
// NOTE: have to test against scr attribute value, not script.src
// b/c the latter will return the full url for relative paths
var src = script.getAttribute('src');
if (src !== url) {
// potentially trying to load a different version of the API
reject(new Error("The ArcGIS API for JavaScript is already loaded (".concat(src, ").")));
}
else {
if (isLoaded()) {
// the script has already successfully loaded
resolve(script);
}
else {
// wait for the script to load and then resolve
handleScriptLoad(script, resolve, reject);
}
}
}
else {
if (isLoaded()) {
// the API has been loaded by some other means
// potentially trying to load a different version of the API
reject(new Error("The ArcGIS API for JavaScript is already loaded."));
}
else {
// this is the first time attempting to load the API
var css = opts.css;
if (css) {
var useVersion = css === true;
// load the css before loading the script
loadCss(useVersion ? version : css, opts.insertCssBefore);
}
// create a script object whose source points to the API
script = createScript(url);
// _currentUrl = url;
// once the script is loaded...
handleScriptLoad(script, function () {
// update the status of the script
script.setAttribute('data-esri-loader', 'loaded');
// return the script
resolve(script);
}, reject);
// load the script
document.body.appendChild(script);
}
}
});
}
/* Copyright (c) 2022 Environmental Systems Research Institute, Inc.
* Apache-2.0 */
// wrap Dojo's require() in a promise
function requireModules(modules) {
return new utils.Promise(function (resolve, reject) {
// If something goes wrong loading the esri/dojo scripts, reject with the error.
var errorHandler = window['require'].on('error', reject);
window['require'](modules, function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
// remove error handler
errorHandler.remove();
// Resolve with the parameters from dojo require as an array.
resolve(args);
});
});
}
// returns a promise that resolves with an array of the required modules
// also will attempt to lazy load the ArcGIS API if it has not already been loaded
function loadModules(modules, loadScriptOptions) {
if (loadScriptOptions === void 0) { loadScriptOptions = {}; }
if (!isLoaded()) {
// script is not yet loaded, is it in the process of loading?
var script = getScript();
var src = script && script.getAttribute('src');
if (!loadScriptOptions.url && src) {
// script is still loading and user did not specify a URL
// in this case we want to default to the URL that's being loaded
// instead of defaulting to the latest 4.x URL
loadScriptOptions.url = src;
}
// attempt to load the script then load the modules
return loadScript(loadScriptOptions).then(function () { return requireModules(modules); });
}
else {
// script is already loaded, just load the modules
return requireModules(modules);
}
}
/*
Copyright (c) 2022 Esri
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// re-export the functions that are part of the public API
exports.utils = utils;
exports.loadModules = loadModules;
exports.getScript = getScript;
exports.isLoaded = isLoaded;
exports.loadScript = loadScript;
exports.setDefaultOptions = setDefaultOptions;
exports.loadCss = loadCss;
Object.defineProperty(exports, '__esModule', { value: true });
})));
} (esriLoader$1, esriLoader$1.exports));
return esriLoader$1.exports;
}
var esriLoaderExports = requireEsriLoader();
/**
* Utility functions and helpers.
*/
function parseViewpoint(viewpoint) {
const defaultLOD = 2;
if (viewpoint === undefined || viewpoint === null || viewpoint == "") {
viewpoint = "0,0," + defaultLOD;
}
let values = viewpoint.split(",");
if (values.length < 3) {
values[2] = defaultLOD;
}
return ({
longitude: parseFloat(values[0]) || 0,
latitude: parseFloat(values[1]) || 0,
levelOfDetail: parseInt(values[2]) || defaultLOD,
scale: 0
});
}
function parseMinMax(minMax, defaultMin, defaultMax) {
let min;
let max;
if (minMax === undefined || minMax === null || minMax == "") {
minMax = `${defaultMin},${defaultMax}`;
}
let values = minMax.split(",");
if (values.length < 1) {
values[0] = defaultMin;
values[1] = defaultMax;
}
else if (values.length < 2) {
// if only 1 value provided then min and max should be the same value.
values[1] = values[0];
}
min = parseInt(values[0]);
if (Number.isNaN(min)) {
min = defaultMin;
}
max = parseInt(values[1]);
if (Number.isNaN(max)) {
max = defaultMax;
}
if (max < min) {
// if numbers are swapped then reverse them.
const hold = max;
max = min;
min = hold;
}
if (min < defaultMin) {
min = defaultMin;
}
if (max > defaultMax) {
max = defaultMax;
}
return ({
min: min,
max: max,
});
}
function parseOffset(offset) {
if (offset === undefined || offset === null) {
offset = "0,0";
}
let values = offset.split(",");
return ({
x: parseFloat(values[0]) || 0,
y: parseFloat(values[1]) || 0
});
}
function parseCameraPosition(cameraPosition) {
const defaultZ = 50000;
const defaultHeading = 90;
const defaultTilt = 0;
if (cameraPosition === undefined || cameraPosition === null) {
cameraPosition = `0,0,${defaultZ},${defaultHeading},${defaultTilt}`;
}
let values = cameraPosition.split(",");
if (values.length < 5) {
const defaultFill = [0, 0, defaultZ, defaultHeading, defaultTilt];
for (let i = values.length; i < 5; i++) {
values[i] = defaultFill[i];
}
}
return ({
x: parseFloat(values[0]) || 0,
y: parseFloat(values[1]) || 0,
z: parseFloat(values[2]) || defaultZ,
heading: parseFloat(values[3]) || defaultHeading,
tilt: parseFloat(values[4]) || defaultTilt,
});
}
/**
* Helper function to test a string to see if it is a valid ArcGIS Online item ID.
* @param itemID {string} A proposed ArcGIS Online item ID.
* @returns {boolean} `true` if the input appears to be a valid item ID, otherwise `false`.
*/
function isValidItemID(itemID) {
return /^[a-f0-9]{32}$/.test(itemID);
}
/**
* Helper function to test a string to see if it is a valid URL.
* @param itemID {string} A proposed URL.
* @returns {boolean} `true` if the input appears to be a valid URL, otherwise `false`.
*/
function isValidURL(url) {
return /^http(s)?:\/\//.test(url);
}
/**
* Verify a proposed search widget position is an expected value.
* @param position {string} A search position specification.
* @returns {boolean} `true` when OK, `false` when bad.
*/
function isValidSearchPosition(position) {
return ["top-left", "top-right", "bottom-left", "bottom-right", "off"].indexOf(position) >= 0;
}
/**
* Given a user attribute setting for the UI show setting, return either true to show the UI
* or false to hide it.
* @param uiSetting {string} The string to check.
* @returns {boolean} Either true or false.
*/
function showUI(uiSetting) {
const allLower = uiSetting ? uiSetting.toLocaleLowerCase() : "";
if (["false", "off", "hide", "disable", "0"].indexOf(allLower) >= 0) {
return false;
}
return true;
}
const esriMapViewCss = () => `.esri-map-view{height:100%;width:100%;padding:0;margin:0}`;
const EsriMapView = class {
watchAPIKeyHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.esriConfig.apiKey = newValue;
}
}
watchBasemapHandler(newValue, oldValue) {
const componentContext = this;
if (componentContext.viewChangePending || componentContext.mapChangePending) {
componentContext.mapLoadError.emit(new Error("Cannot change the map while a viewpoint change is in process."));
return;
}
if (newValue != oldValue) {
componentContext.mapChangePending = true;
componentContext.updateEsriMap(newValue)
.then(() => {
componentContext.basemap = newValue;
componentContext.mapChangePending = false;
componentContext.mapLoaded.emit("loaded");
if (componentContext.layers) {
componentContext.addLayers(componentContext.layers);
}
})
.catch((mapLoadingException) => {
componentContext.mapChangePending = false;
componentContext.mapLoadError.emit(mapLoadingException.toString());
});
}
}
watchWebmapHandler(newValue, oldValue) {
if (this.viewChangePending || this.mapChangePending) {
this.mapLoadError.emit(new Error("Cannot change the map while a viewpoint change is in process."));
return;
}
if (newValue != oldValue) {
this.mapChangePending = true;
this.webmap = newValue;
this.createEsriMap()
.then(() => {
if (this.esriMapView && this.esriWebMap) {
this.esriMapView.map = this.esriWebMap;
}
this.mapChangePending = false;
this.mapLoaded.emit("loaded");
if (this.layers) {
this.addLayers(this.layers);
}
})
.catch((mapLoadingException) => {
console.log(`web map change, Map loading failed ${mapLoadingException.toString()}`);
this.mapChangePending = false;
this.mapLoadError.emit(mapLoadingException.toString());
});
}
}
watchViewPointHandler(newValue, oldValue) {
const componentContext = this;
if (componentContext.viewChangePending || componentContext.mapChangePending) {
componentContext.mapLoadError.emit(new Error("Cannot change the viewpoint while a map change is in process."));
return;
}
if (newValue != oldValue) {
componentContext.parsedViewpoint = false;
componentContext.viewpoint = newValue;
if (componentContext.verifyViewpoint()) {
if (componentContext.esriMapView == null) {
return;
}
// notify the map view to change the viewpoint
componentContext.viewChangePending = true;
componentContext.esriMapView.goTo({
center: [componentContext.longitude, componentContext.latitude],
zoom: componentContext.levelOfDetail
})
.then(function () {
componentContext.viewChangePending = false;
componentContext.addGraphics();
})
.catch(function () {
componentContext.viewChangePending = false;
componentContext.viewpoint = oldValue;
componentContext.verifyViewpoint();
});
}
else {
// the new viewpoint failed so restore the old viewpoint.
componentContext.viewpoint = oldValue;
componentContext.verifyViewpoint();
}
}
}
watchLayersHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.layers = newValue;
this.addLayers(newValue);
}
}
watchSearchHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.removeSearchWidget();
this.search = newValue;
if (this.verifySearch()) {
this.createSearchWidget(this.search);
}
}
}
watchSymbolHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.symbol = newValue;
this.addGraphics();
}
}
watchSymbolOffsetHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.symboloffset = newValue;
this.parsedOffset = parseOffset(newValue);
this.addGraphics();
}
}
watchPopupTitleHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.popuptitle = newValue;
this.addGraphics();
}
}
watchPopupInfoHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.popupinfo = newValue;
this.addGraphics();
}
}
watchUIHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.constrainUI(newValue, false);
}
}
watchMinMaxZoomHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.minmaxzoom = newValue;
if (!this.verifyMinMaxZoom()) {
this.minmaxzoom = oldValue;
this.verifyMinMaxZoom();
}
}
}
constructor(hostRef) {
index.registerInstance(this, hostRef);
this.mapLoaded = index.createEvent(this, "mapLoaded");
this.mapLoadError = index.createEvent(this, "mapLoadError");
this.ArcGISJavaScriptVersion = "4.34";
this.assetPath = index.getAssetPath("./assets/");
this.mapViewWidgets = ["zoom"];
this.esriMap = null;
this.esriWebMap = null;
this.esriMapView = null;
this.symbolGraphic = null;
this.searchWidget = null;
this.longitude = 0;
this.latitude = 0;
this.levelOfDetail = 2;
this.minZoom = 0;
this.maxZoom = 24;
this.parsedViewpoint = false;
this.defaultBasemap = "osm-streets";
this.viewChangePending = false;
this.mapChangePending = false;
/**
* Options for esri-loader
*/
this.esriMapOptions = {
url: `https://js.arcgis.com/${this.ArcGISJavaScriptVersion}/`,
css: true
};
/**
* Set your API key. Learn more about [API keys](https://developers.arcgis.com/documentation/mapping-apis-and-services/security/api-keys/).
*/
this.apikey = "YOUR_API_KEY";
/**
* Indicate a basemap id to use for the map. Select one of the basemap style options from the enumeration https://developers.arcgis.com/javascript/latest/api-reference/esri-Map.html#basemap,
* or the item ID of a custom basemap style. This property will be ignored by `webmap` if that attribute is provided. If neither `webmap` nor `basemap` are set, then
* a default basemap is assigned. Options for `basemap` are defined in the [ArcGIS API for JavaScript](https://developers.arcgis.com/javascript/latest/api-reference/esri-Map.html#basemap).
*/
this.basemap = "osm-streets";
/**
* Indicate a web map id to use for the map. If neither `webmap` nor `basemap`
* are set, then a default basemap is assigned.
*/
this.webmap = "";
/**
* Indicate an initial viewpoint to focus the map. This is a string of 3 comma-separated numbers
* expected: longitude (x), latitude (y), and levelOfDetail (LOD). Example: "22.7783,34.1234,9".
* You should set this if you set a `basemap`. You do not need to set this if you set `webmap` as
* the web map's initial viewpoint is used. If you do set `viewpoint` and `webmap` then this
* setting will override the initial viewpoint of the web map.
*/
this.viewpoint = "";
/**
* Specify 0 or more layers to add on top of the basemap. Each layer is a string that is either a URL
* to the feature service, or the item ID of the feature service. Multiple layers are separated
* with a comma.
*/
this.layers = "";
/**
* Include a search widget by indicating where on the map view it should appear. The valid values for this
* attribute are `top-left`, `top-right`, `bottom-left`, `bottom-right`. If this attribute is empty/missing
* or an invalid value then a search widget will not show.
*/
this.search = "";
/**
* Indicate a symbol to use to mark the location of the initial viewpoint. This is the fully qualified URL
* to a 64x64 px PNG image. CORS is respected when accessing the image. You can also specify `green-pin` to
* use a green map pin as the symbol. You can also specify `pin:{color}` to use a text symbol marker
* and the color value. Use a 6-digit HTML color value or the standard HTML color name.
*/
this.symbol = "";
/**
* Some symbols will require an x/y offset so that the registration point of the
* symbol is exactly on the map point. Here you can specify an x,y offset, in pixels, to adjust
* the symbol. Use a comma separated coordinate pair.
*/
this.symboloffset = "";
/**
* If `symbol` is set, tapping the symbol will show a pop-up. This is the `title` for that pop-up.
*/
this.popuptitle = "";
/**
* If `symbol` is set, tapping the symbol will show a pop-up. This is the `content` for that pop-up.
*/
this.popupinfo = "";
/**
* You can show or hide the map UI (Pan/zoom controls) with the `ui` attribute. Setting `ui=hide` or `ui=off` to hide it,
* set `ui=show` or `ui=on` to show it. The default value is to show the ui.
*/
this.ui = "";
/**
* Set the minimum and maximum zoom levels allowed by either the UI zoom controls or
* the mouse/touch interaction. This is a comma separated string of 2 numbers, the first is
* the minimum and the second is the maximum. For example, 14,16 will constrain the zoom to
* a minimum of 14 and a maximum of 16. If only one number is provided (or valid) then
* both min and max are set to that value.
*/
this.minmaxzoom = "";
this.verifyProps();
esriLoaderExports.setDefaultOptions(this.esriMapOptions);
esriLoaderExports.loadCss(`${this.esriMapOptions.url}/esri/css/main.css`);
this.setAuthentication()
.then(() => {
this.createEsriMap()
.then(() => {
this.mapLoaded.emit("map-created");
})
.catch((mapLoadingException) => {
console.log(`Map loading failed ${mapLoadingException.toString()}`);
this.mapLoadError.emit(mapLoadingException.toString());
});
})
.catch((authenticationException) => {
console.log(`Authentication failed ${authenticationException.toString()}`);
this.mapLoadError.emit(authenticationException.toString());
});
}
/**
* The component is loaded and has rendered. Attach the MapView to the HTML element.
* Only called once per component life cycle.
*/
componentDidLoad() {
this.createEsriMapView()
.then(() => {
this.mapLoaded.emit("view-created");
if (this.symbol) {
this.addGraphics();
}
});
}
/**
* Set the configuration object authentication based on the user provided apikey. If no apikey is provided
* then a login pop up will appear if authentication is required.
* The esri configuration object https://developers.arcgis.com/javascript/latest/api-reference/esri-config.html
*/
setAuthentication() {
return new Promise((configSet, authenticationFailed) => {
esriLoaderExports.loadModules(["esri/config"])
.then(([esriConfig]) => {
this.esriConfig = esriConfig;
if (this.apikey) {
this.esriConfig.apiKey = this.apikey;
}
else {
this.esriConfig.request.useIdentity = true;
}
configSet();
})
.catch((loadException) => {
authenticationFailed(loadException);
});
});
}
/**
* Create a map object. Review the element attributes to determine which type of map should be created:
* either a standard basemap, a custom vector basemap, or a web map.
*/
async createEsriMap() {
return new Promise((mapCreated, mapFailed) => {
if (isValidItemID(this.webmap)) {
// If web map provided, assume a valid item ID and try to create a WebMap from it.
this.updateEsriWebMap(this.webmap)
.then(() => {
mapCreated();
})
.catch((loadException) => {
mapFailed(loadException);
});
}
else {
// If not an item id then it's a URL or a basemap id
this.updateEsriMap(this.basemap)
.then(() => {
mapCreated();
})
.catch((loadException) => {
mapFailed(loadException);
});
}
});
}
/**
* Create a web map.
*/
async updateEsriWebMap(webMapItemId) {
return new Promise((mapCreated, mapFailed) => {
esriLoaderExports.loadModules(["esri/WebMap"])
.then(([WebMap]) => {
this.esriWebMap = new WebMap({
portalItem: {
id: webMapItemId
}
});
mapCreated();
})
.catch((loadException) => {
mapFailed(loadException);
});
});
}
/**
* Create a map object. Review the element attributes to determine which type of map should be created.
* Given the attributes set on the element, creates either a standard basemap, a custom vector basemap,
* or a web scene.
*/
async updateEsriMap(newBasemapId) {
return new Promise((mapCreated, mapFailed) => {
if (isValidItemID(newBasemapId)) {
// if the basemap looks like an item ID then assume it is the user's custom vector map. If it is not (e.g. it's actually a web map)
// then this isn't going to work. use `webmap` instead!
esriLoaderExports.loadModules([
"esri/Map",
"esri/Basemap",
"esri/layers/VectorTileLayer"
])
.then(([Map, Basemap, VectorTileLayer]) => {
const customBasemap = new Basemap({
baseLayers: [
new VectorTileLayer({
portalItem: {
id: newBasemapId
}
})
]
});
if (this.esriMap) {
this.esriMap.basemap = customBasemap;
}
else {
this.esriMap = new Map({
basemap: customBasemap
});
}
mapCreated();
})
.catch((loadException) => {
mapFailed(loadException);
});
}
else {
// basemap is expected to be one of the string enumerations in the API (https://developers.arcgis.com/javascript/latest/api-reference/esri-Map.html#basemap)
esriLoaderExports.loadModules(["esri/Map", "esri/Basemap"])
.then(([Map, Basemap]) => {
const newBasemap = new Basemap({
style: {
id: newBasemapId,
language: "en"
}
});
if (this.esriMap) {
this.esriMap.basemap = newBasemap;
}
else {
this.esriMap = new Map({
basemap: newBasemap
});
}
mapCreated();
})
.catch((loadException) => {
mapFailed(loadException);
});
}
});
}
/**
* Creates the map view used in the component. Assumes the map was created before getting here.
*/
async createEsriMapView() {
const [MapView] = await esriLoaderExports.loadModules(["esri/views/MapView"]);
const mapDiv = this.hostElement.querySelector("div");
if (this.webmap && !this.parsedViewpoint) {
// A web map and no initial viewpoint specified will use the viewpoint set in the web map.
if (this.esriMapView == null) {
this.esriMapView = new MapView({
container: mapDiv,
map: this.esriWebMap
});
}
else {
this.esriMapView.map = this.esriWebMap;
}
}
else {
if (this.esriMapView == null) {
this.esriMapView = new MapView({
container: mapDiv,
zoom: this.levelOfDetail,
center: [this.longitude, this.latitude],
map: this.esriMap || this.esriWebMap
});
}
else {
this.esriMapView.map = this.esriMap || this.esriWebMap;
}
}
if (this.esriMapView) {
this.constrainUI(this.ui, true);
if (this.layers) {
this.addLayers(this.layers);
}
if (isValidSearchPosition(this.search) && this.search != "off") {
this.createSearchWidget(this.search);
}
}
}
/**
* Add layers to the map. Each layer can be specified by its item ID or its service URL.
* @param {string} layers A list of 1 or more layers where multiple layers are separated with commas.
*/
addLayers(layers) {
const layersList = layers.split(",");
// Review the proposed layer list and remove anything that doesn't look like a layer specification.
for (let i = layersList.length; i >= 0; i--) {
const layerId = layersList[i];
if (!isValidItemID(layerId) && !isValidURL(layerId)) {
layersList.splice(i, 1);
}
}
// Only proceed with layer construction if we have layers we think we can load.
if (layersList.length > 0) {
esriLoaderExports.loadModules(["esri/layers/FeatureLayer", "esri/layers/Layer", "esri/portal/PortalItem"])
.then(([FeatureLayer, Layer, PortalItem]) => {
this.esriMap.layers.removeAll();
layersList.forEach((layerId) => {
if (isValidItemID(layerId)) {
const portalItem = new PortalItem({
id: layerId
});
Layer.fromPortalItem({ portalItem: portalItem })
.then((itemLayer) => {
this.esriMap.layers.add(itemLayer);
})
.catch((exception) => {
console.log(`Layer ${layerId} loading failed ${exception.toString()}`);
});
}
else if (isValidURL(layerId)) {
const featureLayer = new FeatureLayer({
url: layerId
});
if (featureLayer) {
this.esriMap.layers.add(featureLayer);
}
}
});
});
}
}
/**
* Create a search widget and add it to the view at the given UI position.
* @param {string} searchWidgetPosition The UI position where to place the search widget in the view.
*/
async createSearchWidget(searchWidgetPosition) {
const [SearchWidget] = await esriLoaderExports.loadModules(["esri/widgets/Search"]);
const searchWidget = new SearchWidget({
view: this.esriMapView
});
this.esriMapView.ui.add(searchWidget, {
position: searchWidgetPosition,
index: 0
});
this.searchWidget = searchWidget;
}
/**
* Remove the search widget from the map view.
*/
async removeSearchWidget() {
if (this.searchWidget) {
this.esriMapView.ui.remove(this.searchWidget);
this.searchWidget = null;
}
}
/**
* Add graphics to the graphics layer depending on which properties the user requested.
*/
async addGraphics() {
if (this.esriMapView) {
if (this.symbol.indexOf("pin:") === 0) {
this.showPin(this.symbol.substring(4));
}
else if (this.symbol.length > 0) {
this.showSymbol(this.symbol);
}
}
}
/**
* Add a graphic symbol to the map view graphics layer, and remove a prior graphic.
* @param symbolGraphic A Graphic to add to the graphics layer of the map view.
*/
updateGraphicSymbol(symbolGraphic) {
if (this.symbolGraphic != null) {
// a previous symbol must first be removed from the graphics layer
this.esriMapView.graphics.remove(this.symbolGraphic);
}
this.symbolGraphic = symbolGraphic;
this.esriMapView.graphics.add(symbolGraphic);
}
/**
* Show a symbol on the map at the initial viewpoint location.
* @param {string} symbol Either an asset id of a local symbol asset or a fully qualified URL to a PNG to use as the symbol.
*/
async showSymbol(symbol) {
const xoffset = this.parsedOffset.x.toString();
const yoffset = this.parsedOffset.y.toString();
let symbolURL;
if (symbol.match(/https?:\/\//) == null) {
symbolURL = this.assetPath + symbol + ".png";
}
else {
symbolURL = symbol;
}
const [PictureMarkerSymbol, Graphic, Point] = await esriLoaderExports.loadModules([
"esri/symbols/PictureMarkerSymbol",
"esri/Graphic",
"esri/geometry/Point"
]);
const point = new Point({
longitude: this.longitude,
latitude: this.latitude
});
const pointSymbol = new PictureMarkerSymbol({
url: symbolURL,
width: "64px",
height: "64px",
xoffset: xoffset,
yoffset: yoffset
});
const symbolGraphic = new Graphic({
geometry: point,
symbol: pointSymbol,
popupTemplate: {
title: this.popuptitle,
content: this.popupinfo
}
});
this.updateGraphicSymbol(symbolGraphic);
}
/**
* Show a pin on the map at the initial viewpoint location.
* @param {string} pinColor The color value of the pin symbol.
*/
async showPin(pinColor) {
const [TextSymbol, Graphic, Point] = await esriLoaderExports.loadModules([
"esri/symbols/TextSymbol",
"esri/Graphic",
"esri/geometry/Point"
], this.esriMapOptions);
let xoffset = this.parsedOffset.x;
let yoffset = this.parsedOffset.y;
const point = new Point({
longitude: this.longitude,
latitude: this.latitude
});
const pointSymbol = new TextSymbol({
color: pinColor,
haloColor: "black",
haloSize: "1px",
text: "\ue61d",
font: {
size: 30,
family: "CalciteWebCoreIcons"
},
xoffset: xoffset,
yoffset: yoffset
});
const symbolGraphic = new Graphic({
geometry: point,
symbol: pointSymbol,
popupTemplate: {
title: this.popuptitle,
content: this.popupinfo
}
});
this.updateGraphicSymbol(symbolGraphic);
}
render() {
return index.h("div", { key: '956ea553424f9064fec8976fc2e3b63b74b45090', class: "esri-map-view" });
}
/**
* Constrain the UI to restrict the user from panning or zooming the map. If the UI is hidden then the
* map view is constrained to the current extent. If the UI controls are showing then the map view is
* constrained to the min/max zoom that was configured.
* @param uiSetting Show or hide the UI controls (pan/zoom)
* @param isInitializing True if we are in the initialization stage of the component, false if the component was previously initialized.
* @returns {boolean} True if the UI is constrained.
*/
constrainUI(uiSetting, isInitializing) {
let isConstrained;
// const zoom = this.esriMapView.zoom;
if (!showUI(uiSetting)) {
this.esriMapView.ui.remove(this.mapViewWidgets);
this.esriMapView.constraints = {
minZoom: this.minZoom,
maxZoom: this.maxZoom,
geometry: this.esriMapView.extent
};
isConstrained = true;
}
else if (!isInitializing) {
this.esriMapView.ui.add(this.mapViewWidgets);
this.esriMapView.constraints = {};
isConstrained = false;
}
return isConstrained;
}
/**
* Validate the map properties to verify if we can render a map. Either a basemap or a webmap
* must be specified. If neither are set then a default basemap is used.
* @returns {boolean} True if we think we can render a map.
*/
verifyMapProps() {
let isValid = true;
if (this.webmap && (!isValidItemID(this.webmap) || !isValidURL(this.webmap))) {
// if a web map is specified but it is not an item ID or a service URL then ignore it.
this.webmap = null;
}
if (!this.basemap && !this.webmap) {
// If there is no basemap and no web map then use a default basemap, no point to rendering nothing.
this.basemap = this.defaultBasemap;
}
return isValid;
}
/**
* Verify the viewpoint attribute and break it down to its individual parts.
* @returns {boolean} True if a viewpoint is parsed.
*/
verifyViewpoint() {
let isValid = false;
if (!this.viewpoint && !this.webmap) {
// if no initial viewpoint is specified then set a default
this.viewpoint = "0,0,2";
}
if (this.viewpoint) {
// if given an initial viewpoint then try to valid each part
const parsedViewpoint = parseViewpoint(this.viewpoint);
this.longitude = parsedViewpoint.longitude;
this.latitude = parsedViewpoint.latitude;
this.levelOfDetail = parsedViewpoint.levelOfDetail;
this.parsedViewpoint = true;
isValid = true;
}
return isValid;
}
/**
* Verify the search widget position is valid.
* @returns {boolean} True if a valid search widget position is specified.
*/
verifySearch() {
let isValid = true;
this.search = this.search.toLocaleLowerCase();
if (this.search && !isValidSearchPosition(this.search)) {
// if given a search widget and it's not a valid UI position then ignore it.
this.search = null;
isValid = false;
}
else if (this.search == "off") {
this.search = null;
}
return isValid;
}
/**
* Verify the min and max zoom values are set properly.
* @returns {boolean} True if min/max zoom is set.
*/
verifyMinMaxZoom() {
let isValid = true;
const zoom = parseMinMax(this.minmaxzoom, 0, 24);
this.minZoom = zoom.min;
this.maxZoom = zoom.max;
return isValid;
}
/**
* Verify and validate any attributes set on the component to make sure
* we are in a valid starting state we can render.
* @returns {boolean} True if all props are acceptable and we can render the map.
*/
verifyProps() {
let isValid = true;
isValid = this.verifyMapProps();
isValid = this.verifyViewpoint();
isValid = this.verifySearch();
isValid = this.verifyMinMaxZoom();
this.parsedOffset = parseOffset(this.symboloffset);
return isValid;
}
static get assetsDirs() { return ["assets"]; }
get hostElement() { return index.getElement(this); }
static get watchers() { return {
"apikey": [{
"watchAPIKeyHandler": 0
}],
"basemap": [{
"watchBasemapHandler": 0
}],
"webmap": [{
"watchWebmapHandler": 0
}],
"viewpoint": [{
"watchViewPointHandler": 0
}],
"layers": [{
"watchLayersHandler": 0
}],
"search": [{
"watchSearchHandler": 0
}],
"symbol": [{
"watchSymbolHandler": 0
}],
"symboloffset": [{
"watchSymbolOffsetHandler": 0
}],
"popuptitle": [{
"watchPopupTitleHandler": 0
}],
"popupinfo": [{
"watchPopupInfoHandler": 0
}],
"ui": [{
"watchUIHandler": 0
}],
"minmaxzoom": [{
"watchMinMaxZoomHandler": 0
}]
}; }
};
EsriMapView.style = esriMapViewCss();
const esriSceneViewCss = () => `.esri-scene-view{height:100%;width:100%;padding:0;margin:0}`;
const EsriSceneView = class {
watchAPIKeyHandler(newValue, oldValue) {
if (newValue != oldValue) {
this.esriConfig.apiKey = newValue;
}
}
watchBasemapHandler(newValue, oldValue) {
const componentContext = this;
if (componentContext.viewChangePending || componentContext.mapChangePending) {
componentContext.mapLoadError.emit(new Error("Cannot change the map while a viewpoint change is in process."));
return;
}
if (newValue != oldValue) {
componentContext.mapChangePending = true;
componentContext.updateEsriScene(newValue)
.then(() => {
componentContext.basemap = newValue;
componentContext.mapChangePending = false;
componentContext.mapLoaded.emit("loaded");
if (componentContext.layers) {
componentContext.addLayers(componentContext.layers);
}
})
.catch((mapLoadingException) => {
componentContext.mapChangePending = false;
componentContext.mapLoadError.emit(mapLoadingException.toString());
});
}
}
watchWebSceneHandler(newValue, oldValue) {
if (this.viewChangePending || this.mapChangePending) {
this.mapLoadError.emit(new Error("Cannot change the map while a viewpoint change is in process."));
return;
}
if (newValue != oldValue) {
this.mapChangePending = true;
this.webscene = newValue;
this.updateEsriWebScene(newValue)
.then(() => {
// @todo: wait for map loaded then dispatch map loaded event
console.log("web scene change mapLoaded, new web scene should be showing");
if (this.esriSceneView && this.esriWebScene) {
this.esriSceneView.map = this.esriWebScene;
}
this.mapChangePending = false;
this.mapLoaded.emit("loaded");
if (this.layers) {
this.addLayers(this.layers);
}
})
.catch((mapLoadingException) => {
console.log(`web scene change, Map loading failed ${mapLoadingException.toString()}`);
this.mapChangePending = false;
this.mapLoadError.emit(mapLoadingException.toString());
});
}
}
watchViewPointHandler(newValue, oldValue) {
const componentContext = this;
if (componentContext.viewChangePending || componentContext.mapChangePending) {
componentContext.mapLoadError.emit(new Error("Cannot change the viewpoint while a map change is in process."));
return;
}
if (newValue != oldValue) {
componentContext.parsedViewpoint = false;
componentContext.viewpoint = newValue;
if (componentContext.verifyViewpoint()) {
if (componentContext.esriSceneView == null) {
return;
}
// notify the map view to change the viewpoint
componentContext.viewChange