From 0dbde262e5e448c5b83215e7b3275402381882b7 Mon Sep 17 00:00:00 2001 From: Damon Ulmi Date: Thu, 11 Jul 2024 11:36:11 -0700 Subject: [PATCH] feat(basemap): satellite basemap added Closes #1849 --- .../default-config-basemap-panel.json | 10 + packages/geoview-basemap-panel/schema.json | 2 +- .../src/basemap-panel-style.ts | 2 +- .../src/basemap-panel.tsx | 114 +++++----- .../public/templates/layers/geojson.html | 4 +- packages/geoview-core/schema.json | 2 +- .../src/api/config/types/config-constants.ts | 2 +- .../types/config-validation-schema.json | 2 +- .../src/api/config/types/map-schema-types.ts | 2 +- .../src/geo/layer/basemap/basemap.ts | 200 ++++++++++-------- 10 files changed, 187 insertions(+), 153 deletions(-) diff --git a/packages/geoview-basemap-panel/default-config-basemap-panel.json b/packages/geoview-basemap-panel/default-config-basemap-panel.json index 5b175dfee48..5ab39e263ff 100644 --- a/packages/geoview-basemap-panel/default-config-basemap-panel.json +++ b/packages/geoview-basemap-panel/default-config-basemap-panel.json @@ -15,6 +15,11 @@ "shaded": true, "labeled": false }, + { + "basemapId": "satellite", + "shaded": false, + "labeled": false + }, { "basemapId": "osm", "shaded": true, @@ -51,6 +56,11 @@ "shaded": true, "labeled": false }, + { + "basemapId": "satellite", + "shaded": false, + "labeled": false + }, { "basemapId": "osm", "shaded": true, diff --git a/packages/geoview-basemap-panel/schema.json b/packages/geoview-basemap-panel/schema.json index 0528efc68e2..febf923af06 100644 --- a/packages/geoview-basemap-panel/schema.json +++ b/packages/geoview-basemap-panel/schema.json @@ -95,7 +95,7 @@ "basemapId": { "type": "string", "description": "the basemap id", - "enum": ["transport", "simple", "shaded", "osm", "nogeom"] + "enum": ["transport", "simple", "shaded", "osm", "nogeom", "satellite"] }, "shaded": { "type": "boolean", diff --git a/packages/geoview-basemap-panel/src/basemap-panel-style.ts b/packages/geoview-basemap-panel/src/basemap-panel-style.ts index c90000e6e21..f917fcfc8de 100644 --- a/packages/geoview-basemap-panel/src/basemap-panel-style.ts +++ b/packages/geoview-basemap-panel/src/basemap-panel-style.ts @@ -58,7 +58,7 @@ export const getSxClasses = (theme): any => ({ height: '100%', width: '100%', position: 'absolute', - backgroundColor: theme.palette.geoViewColor.grey.lighten(0.5, 0.85), + backgroundColor: theme.palette.geoViewColor.grey.lighten(0.5, 0.6), transition: 'all 0.3s ease-in-out', }, }, diff --git a/packages/geoview-basemap-panel/src/basemap-panel.tsx b/packages/geoview-basemap-panel/src/basemap-panel.tsx index c37b7d0090e..972ccbd1ac9 100644 --- a/packages/geoview-basemap-panel/src/basemap-panel.tsx +++ b/packages/geoview-basemap-panel/src/basemap-panel.tsx @@ -26,7 +26,7 @@ export function BasemapPanel(props: BaseMapPanelProps): JSX.Element { const theme = ui.useTheme(); const sxClasses = getSxClasses(theme); - // internal state and store values + // Internal state and store values const [basemapList, setBasemapList] = useState([]); const [activeBasemapId, setActiveBasemapId] = useState(''); const [canSwichProjection] = useState(config.canSwichProjection); @@ -37,14 +37,13 @@ export function BasemapPanel(props: BaseMapPanelProps): JSX.Element { const language = useAppDisplayLanguage(); // #region PRIVATE UTILITY FUNCTIONS + /** - * Get basemap thumbnail url - * - * @param {string[]} basemapTypes basemap layer type (shaded, transport, label, simple) - * @param {TypeValidMapProjectionCodes} projection basemap projection - * @param {TypeDisplayLanguage} displayLanguage basemap language - * - * @returns {string[]} array of thumbnail urls + * Get basemap thumbnail url. + * @param {string[]} basemapTypes - Basemap layer type (shaded, transport, label, simple, satellite). + * @param {TypeValidMapProjectionCodes} projection - Basemap projection. + * @param {TypeDisplayLanguage} displayLanguage - Basemap language. + * @returns {string[]} Array of thumbnail urls. */ function getThumbnailUrl( basemapTypes: string[], @@ -100,16 +99,26 @@ export function BasemapPanel(props: BaseMapPanelProps): JSX.Element { if (type === 'osm') { thumbnailUrls.push('https://tile.openstreetmap.org/0/0/0.png'); } + + if (type === 'satellite') { + if (myMap.basemap.basemapsList[projection].satellite?.url) { + thumbnailUrls.push( + (myMap.basemap.basemapsList[projection].satellite?.url as string) + .replace('{z}', '8') + .replace('{y}', projection === 3978 ? '285' : '91') + .replace('{x}', projection === 3978 ? '268' : '74') + ); + } + } } return thumbnailUrls; } /** - * Get basemap information (name and description) - * - * @param {string[]} basemapTypes basemap layer type (shaded, transport, label, simple) - * @returns { name: string; description: string } array with information [name, description] + * Get basemap information (name and description). + * @param {string[]} basemapTypes - Basemap layer type (shaded, transport, label, simple). + * @returns { name: string; description: string } Array with information [name, description]. */ function getInfo(basemapTypes: string[]): { name: string; description: string } { let name = ''; @@ -136,15 +145,14 @@ export function BasemapPanel(props: BaseMapPanelProps): JSX.Element { // #endregion /** - * Update the basemap with the layers on the map - * - * @param {string} id update the basemap on the map + * Update the basemap with the layers on the map. + * @param {string} basemapId - The id to update the basemap on the map. */ const setBasemap = (basemapId: string): void => { - // get basemap from id + // Get basemap from id const basemap = basemapList.find((item) => item.basemapId === basemapId); - // set the new basemap and update the active basemap variable + // Set the new basemap and update the active basemap variable if (basemap !== undefined) { myMap.basemap.setBasemap(basemap); setActiveBasemapId(basemapId); @@ -152,78 +160,76 @@ export function BasemapPanel(props: BaseMapPanelProps): JSX.Element { }; /** - * Add basemaps from configuration for selected projection - * - * @param {number} projection the projection to create basemaps for + * Add basemaps from configuration for selected projection. + * @param {number} projection - The projection to create basemaps for. * @returns {Promise} */ const createBasemapArray = async (projection: TypeValidMapProjectionCodes): Promise => { const basemapsArray = toJsonObject( (config.supportedProjections as Array).find((obj: TypeJsonObject) => obj.projectionCode === projection) ); + let isInit = false; - // reset the basemaps array + // Reset the basemaps array setBasemapList([]); - // create the custom config basemap - for (let basemapIndex = 0; basemapIndex < (basemapsArray.customBasemaps.length as number); basemapIndex++) { - const customBasemap = basemapsArray.customBasemaps[basemapIndex] as TypeJsonObject; - const basemap = api.maps[mapId].basemap.createCustomBasemap(customBasemap as unknown as TypeBasemapProps, projection); - if (basemap) setBasemapList((prevArray) => [...prevArray, basemap]); - - // custom basemap are provided set it by default (can't be set as basemap from geoview config) - if (basemap && basemapIndex === 0 && activeBasemapId === '') { - setBasemap(basemap.basemapId!); - isInit = true; - } - } - - // create the core basemap + // Create the core basemaps + const coreBasemaps: TypeBasemapProps[] = []; for (let basemapIndex = 0; basemapIndex < (basemapsArray.coreBasemaps.length as number); basemapIndex++) { - const basemapOptions = basemapsArray.coreBasemaps[basemapIndex] as TypeJsonObject; + const basemapTypes = coreBasemaps.map((listedBasemap) => listedBasemap.type); + const basemapOptions = basemapsArray.coreBasemaps[basemapIndex] as TypeJsonObject as unknown as TypeBasemapOptions; // TODO: Check - Should probably move the await outside of the loop so that all core basemaps start processing in parallel? // TO.DOCONT: If doing so, be mindful of the isInit which seems to prioritize the first basemap in the list (and maybe why this await is in the loop?) // eslint-disable-next-line no-await-in-loop - const basemap = await api.maps[mapId].basemap.createCoreBasemap(basemapOptions as unknown as TypeBasemapOptions, projection); - - if (basemap) { - // get thumbnail and info (name and description) for core basemap + const basemap = await api.maps[mapId].basemap.createCoreBasemap(basemapOptions, projection); + if (basemap && !basemapTypes.includes(basemap.type)) { + // Get thumbnail and info (name and description) for core basemap const { name, description } = getInfo(basemap.type.split('-')); basemap.thumbnailUrl = getThumbnailUrl(basemap.type.split('-'), storeProjection, language); basemap.name = name; basemap.description = description; - setBasemapList((prevArray) => [...prevArray, basemap]); + coreBasemaps.push(basemap); } + } - // set basemap if previously selected in previous projection - const id = `${basemapOptions.shaded ? 'shaded' : ''}${basemapOptions.id}${basemapOptions.labeled ? 'label' : ''}`; - if (basemap && id === activeBasemapId && !isInit) { - setBasemap(activeBasemapId); - isInit = true; - } + // Create the custom config basemap + const customBasemaps: TypeBasemapProps[] = []; + for (let basemapIndex = 0; basemapIndex < (basemapsArray.customBasemaps.length as number); basemapIndex++) { + const customBasemap = basemapsArray.customBasemaps[basemapIndex]; + const basemap = api.maps[mapId].basemap.createCustomBasemap(customBasemap, projection); + const basemapTypes = customBasemaps.map((listedBasemap) => listedBasemap.type); + if (basemap && !basemapTypes.includes(basemap.type)) customBasemaps.push(basemap); + } + + setBasemapList([...coreBasemaps, ...customBasemaps]); + + // Set to previous basemap, if it is in new basemaps + const prevSetBaseMap = [...customBasemaps, ...coreBasemaps].filter((basemap) => basemap.basemapId === activeBasemapId); + if (prevSetBaseMap) { + setBasemap(activeBasemapId); + isInit = true; } - // if previous basemap does not exist in previous projection, init first one + // If previous basemap does not exist in current projection, init first one if (!isInit) setBasemap(basemapList[0] as unknown as string); }; /** - * Set new projection view and basemap array - * - * @param {SelectChangeEvent} event select change element event + * Set new projection view and basemap array. + * @param {SelectChangeEvent} event - Select change element event. */ const setSelectedProjection = (event: SelectChangeEvent): void => { const projection = event.target.value as TypeValidMapProjectionCodes; - // set basemap to no geom to clean up the view + // Set basemap to no geom to clean up the view setBasemap('nogeom'); setMapProjection(projection as TypeValidMapProjectionCodes); createBasemapArray(projection) .then(() => { - // emit an event to let know map view projection as changed + // Set projection through map viewer myMap.setProjection(projection); }) .catch((error) => { @@ -233,7 +239,7 @@ export function BasemapPanel(props: BaseMapPanelProps): JSX.Element { }; /** - * load existing basemaps and create new basemaps + * Load existing basemaps and create new basemaps */ useEffect(() => { createBasemapArray(mapProjection).catch((error) => { diff --git a/packages/geoview-core/public/templates/layers/geojson.html b/packages/geoview-core/public/templates/layers/geojson.html index 957f8c032ed..23f7151fcb0 100644 --- a/packages/geoview-core/public/templates/layers/geojson.html +++ b/packages/geoview-core/public/templates/layers/geojson.html @@ -64,8 +64,8 @@

1. Many GeoJSON Layers

'projection': 3978 }, 'basemapOptions': { - 'basemapId': 'transport', - 'shaded': true, + 'basemapId': 'satellite', + 'shaded': false, 'labeled': false }, 'listOfGeoviewLayerConfig': [ diff --git a/packages/geoview-core/schema.json b/packages/geoview-core/schema.json index 7f6ea66aa73..6671ead06bd 100644 --- a/packages/geoview-core/schema.json +++ b/packages/geoview-core/schema.json @@ -1374,7 +1374,7 @@ "required": ["basemapId", "shaded", "labeled"] }, "TypeBasemapId": { - "enum": ["transport", "osm", "simple", "nogeom", "shaded"], + "enum": ["transport", "osm", "simple", "nogeom", "shaded", "satellite"], "default": "transport", "description": "Id of the basemap to use." }, diff --git a/packages/geoview-core/src/api/config/types/config-constants.ts b/packages/geoview-core/src/api/config/types/config-constants.ts index a4708262d85..75287d269a1 100644 --- a/packages/geoview-core/src/api/config/types/config-constants.ts +++ b/packages/geoview-core/src/api/config/types/config-constants.ts @@ -105,7 +105,7 @@ export const VALID_PROJECTION_CODES = [3978, 3857]; /** * Definition of the basemap options type. */ -export const CV_VALID_BASEMAP_ID: TypeBasemapId[] = ['transport', 'osm', 'simple', 'nogeom', 'shaded']; +export const CV_VALID_BASEMAP_ID: TypeBasemapId[] = ['transport', 'osm', 'simple', 'nogeom', 'shaded', 'satellite']; /** default configuration if provided configuration is missing or wrong */ // valid basemap ids diff --git a/packages/geoview-core/src/api/config/types/config-validation-schema.json b/packages/geoview-core/src/api/config/types/config-validation-schema.json index 3901e01d65b..97b549995d5 100644 --- a/packages/geoview-core/src/api/config/types/config-validation-schema.json +++ b/packages/geoview-core/src/api/config/types/config-validation-schema.json @@ -310,7 +310,7 @@ }, "TypeBasemapId": { "description": "Id of the basemap to use.", - "enum": ["transport", "osm", "simple", "nogeom", "shaded"], + "enum": ["transport", "osm", "simple", "nogeom", "shaded", "satellite"], "default": "transport" }, "TypeInteraction": { diff --git a/packages/geoview-core/src/api/config/types/map-schema-types.ts b/packages/geoview-core/src/api/config/types/map-schema-types.ts index 95443fa69f5..a019ba66ea5 100644 --- a/packages/geoview-core/src/api/config/types/map-schema-types.ts +++ b/packages/geoview-core/src/api/config/types/map-schema-types.ts @@ -156,7 +156,7 @@ export type TypeBasemapOptions = { }; /** Definition of the basemap options type. */ -export type TypeBasemapId = 'transport' | 'osm' | 'simple' | 'nogeom' | 'shaded'; +export type TypeBasemapId = 'transport' | 'osm' | 'simple' | 'nogeom' | 'shaded' | 'satellite'; /** Definition of the valid map interactiom values. If map is dynamic (pan/zoom) or static to act as a thumbnail (no nav bar). */ export type TypeInteraction = 'static' | 'dynamic'; diff --git a/packages/geoview-core/src/geo/layer/basemap/basemap.ts b/packages/geoview-core/src/geo/layer/basemap/basemap.ts index 076ed800b27..3d870f8ca4e 100644 --- a/packages/geoview-core/src/geo/layer/basemap/basemap.ts +++ b/packages/geoview-core/src/geo/layer/basemap/basemap.ts @@ -28,39 +28,38 @@ export class Basemap { // The maximum delay to wait before we abandon(?) a basemap static REQUEST_DELAY_MAX = 3000; - // active basemap + // Active basemap activeBasemap?: TypeBasemapProps; - // default origin + // Default origin defaultOrigin?: number[]; - // default resolution + // Default resolution defaultResolutions?: number[]; - // default extent + // Default extent defaultExtent?: Extent; - // default overview map layer + // Default overview map layer overviewMap?: TypeBasemapProps; - // the basemap options passed from the map config + // The basemap options passed from the map config basemapOptions: TypeBasemapOptions; - // the map id to be used in events + // The map id to be used in events mapId: string; /** - * initialize basemap - * - * @param {TypeBasemapOptions} basemapOptions optional basemap option properties, passed in from map config - * @param {string} mapId the map id + * Initialize basemap. + * @param {TypeBasemapOptions} basemapOptions - Optional basemap option properties, passed in from map config. + * @param {string} mapId - The map id. */ constructor(basemapOptions: TypeBasemapOptions, mapId: string) { this.mapId = mapId; this.basemapOptions = basemapOptions; - // create the overview default basemap (no label, no shaded) + // Create the overview default basemap (no label, no shaded) this.setOverviewMap().catch((error) => { // Log logger.logPromiseFailed('setOverviewMap in constructor of layer/basemap', error); @@ -68,7 +67,7 @@ export class Basemap { } /** - * basemap list + * Basemap list */ basemapsList: TypeJsonObject = toJsonObject({ 3978: { @@ -88,6 +87,10 @@ export class Basemap { url: 'https://maps-cartes.services.geo.ca/server2_serveur2/rest/services/BaseMaps/xxxx_TXT_3978/MapServer/WMTS/tile/1.0.0/xxxx_TXT_3978/default/default028mm/{z}/{y}/{x}.jpg', jsonUrl: 'https://maps-cartes.services.geo.ca/server2_serveur2/rest/services/BaseMaps/xxxx_TXT_3978/MapServer?f=json', }, + satellite: { + url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + jsonUrl: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/?f=json', + }, }, 3857: { transport: { @@ -106,6 +109,10 @@ export class Basemap { url: 'https://maps-cartes.services.geo.ca/server2_serveur2/rest/services/BaseMaps/xxxx_TXT_3857/MapServer/WMTS/tile/1.0.0/xxxx_TXT_3857/default/default028mm/{z}/{y}/{x}.jpg', jsonUrl: 'https://maps-cartes.services.geo.ca/server2_serveur2/rest/services/BaseMaps/xxxx_TXT_3857/MapServer?f=json', }, + satellite: { + url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + jsonUrl: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/?f=json', + }, }, }); @@ -113,13 +120,13 @@ export class Basemap { #onBasemapChangedHandlers: BasemapChangedDelegate[] = []; // #region PRIVATE UTILITY FUNCTIONS + /** * Get projection from basemap url * Because OpenLayers can reproject on the fly raster, some like Shaded and Simple even if only available in 3978 * can be use in 3857. For this we need to make a difference between map projection and url use for the basemap - * - * @param {string} url basemap url - * @returns {number} projection code + * @param {string} url - Basemap url + * @returns {number} Projection code * @private */ static #getProjectionFromUrl(url: string): number { @@ -152,14 +159,12 @@ export class Basemap { // #region CREATE BASEMAPS /** - * Create a basemap layer - * - * @param {string} basemapId the id of the layer - * @param {TypeJsonObject} basemapLayer the basemap layer url and json url - * @param {number} opacity the opacity to use for this layer - * @param {boolean} rest should we do a get request to get the info from the server - * - * @returns {TypeBasemapLayer} return the created basemap layer + * Create a basemap layer. + * @param {string} basemapId - The id of the layer. + * @param {TypeJsonObject} basemapLayer - The basemap layer url and json url. + * @param {number} opacity - The opacity to use for this layer. + * @param {boolean} rest - Should we do a get request to get the info from the server. + * @returns {TypeBasemapLayer} The created basemap layer. * @private */ async #createBasemapLayer( @@ -184,59 +189,57 @@ export class Basemap { }); } - // should we do a get request to get the layer information from the server? + // Should we do a get request to get the layer information from the server? if (rest && (basemapLayer.jsonUrl as string)) { try { - // get info from server + // Get info from server // TODO: Check/Refactor - Document the necessity to explicitely reject after Basemap.REQUEST_DELAY_MAX const request = await requestBasemap(basemapLayer.jsonUrl as string, Basemap.REQUEST_DELAY_MAX); if (request) { const result = toJsonObject(request.data); - // get minimum scale + // Get minimum scale const minScale = result.minScale as number; - // get maximum scale + // Get maximum scale const maxScale = result.maxScale as number; - // get extent + // Get extent const fullExtent = toJsonObject(result.fullExtent); - // get the tile grid info + // Get the tile grid info const tileInfo = toJsonObject(result.tileInfo); const lods: TypeJsonObject = {}; - // get resolutions and scale from tile grid info + // Get resolutions and scale from tile grid info (tileInfo.lods as TypeJsonArray)?.forEach((lod) => { const scale = lod.scale as number; const resolution = lod.resolution as number; - if (scale <= minScale && scale >= maxScale) { - resolutions.push(resolution); + resolutions.push(resolution); - lods[scale] = lod; - } + lods[scale] = lod; }); - // set layer origin + // Set layer origin origin = [tileInfo?.origin?.x || 0, tileInfo?.origin?.y || 0] as number[]; // set minimum zoom for this layer - minZoom = lods[minScale].level as number; + minZoom = lods[minScale] ? (lods[minScale].level as number) : 0; - // set max zoom for this layer - maxZoom = lods[maxScale].level as number; + // Set max zoom for this layer + maxZoom = lods[maxScale] ? (lods[maxScale].level as number) : 23; - // set extent for this layer + // Set extent for this layer extent = [fullExtent.xmin as number, fullExtent.ymin as number, fullExtent.xmax as number, fullExtent.ymax as number]; // Because OpenLayers can reproject on the fly raster, some like Shaded and Simple even if only available in 3978 // can be use in 3857. For this we need to make a difference between map projection and url use for the basemap urlProj = Basemap.#getProjectionFromUrl(basemapLayer.url as string); - // return a basemap layer + // Return a basemap layer return { basemapId, type: basemapId, @@ -269,13 +272,11 @@ export class Basemap { } /** - * Create the core basemap and add the layers to it - * - * @param {TypeBasemapOptions} basemapOptions basemap options - * @param {TypeValidMapProjectionCodes} projection optional projection code - * @param {TypeDisplayLanguage} language optional language - * - * @return {Promise} the core basemap + * Create the core basemap and add the layers to it. + * @param {TypeBasemapOptions} basemapOptions - Basemap options. + * @param {TypeValidMapProjectionCodes} projection - Optional projection code. + * @param {TypeDisplayLanguage} language - Optional language. + * @return {Promise} The core basemap. */ async createCoreBasemap( basemapOptions: TypeBasemapOptions, @@ -292,17 +293,17 @@ export class Basemap { let minZoom = 0; let maxZoom = 17; - // check if projection is provided for the basemap creation + // Check if projection is provided for the basemap creation const projectionCode = projection === undefined ? MapEventProcessor.getMapState(this.mapId).currentProjection : projection; - // check if language is provided for the basemap creation + // Check if language is provided for the basemap creation const languageCode = language === undefined ? AppEventProcessor.getDisplayLanguage(this.mapId) : language; - // check if basemap options are provided for the basemap creation + // Check if basemap options are provided for the basemap creation const coreBasemapOptions = basemapOptions === undefined ? this.basemapOptions : basemapOptions; if (coreBasemapOptions) { - // create shaded layer + // Create shaded layer if (coreBasemapOptions.shaded && this.basemapsList[projectionCode].shaded) { const shadedLayer = await this.#createBasemapLayer('shaded', this.basemapsList[projectionCode].shaded, defaultOpacity, true); if (shadedLayer) { @@ -311,7 +312,7 @@ export class Basemap { } } - // create transport layer + // Create transport layer if (coreBasemapOptions.basemapId === 'transport' && this.basemapsList[projectionCode].transport) { const transportLayer = await this.#createBasemapLayer( 'transport', @@ -323,7 +324,7 @@ export class Basemap { basemapLayers.push(transportLayer); basemaplayerTypes.push('transport'); - // set default origin,extent,resolutions from layer + // Set default origin,extent,resolutions from layer defaultOrigin = transportLayer.origin; defaultExtent = transportLayer.extent; defaultResolutions = transportLayer.resolutions; @@ -332,7 +333,7 @@ export class Basemap { } } - // create simple layer + // Create simple layer if (coreBasemapOptions.basemapId === 'simple' && this.basemapsList[projectionCode].simple) { const simpleLayer = await this.#createBasemapLayer( 'simple', @@ -345,7 +346,7 @@ export class Basemap { basemapLayers.push(simpleLayer); basemaplayerTypes.push('simple'); - // set default origin,extent,resolutions from layer + // Set default origin,extent,resolutions from layer defaultOrigin = simpleLayer.origin; defaultExtent = simpleLayer.extent; defaultResolutions = simpleLayer.resolutions; @@ -354,7 +355,7 @@ export class Basemap { } } - // create open street maps layer + // Create open street maps layer if (coreBasemapOptions.basemapId === 'osm') { basemapLayers.push({ basemapId: 'osm', @@ -370,11 +371,32 @@ export class Basemap { basemaplayerTypes.push('osm'); } - // no geometry basemap layer + // No geometry basemap layer if (coreBasemapOptions.basemapId === 'nogeom') { basemaplayerTypes.push('nogeom'); } + // Create satellite layer + if (coreBasemapOptions.basemapId === 'satellite' && this.basemapsList[projectionCode].satellite) { + const satelliteLayer = await this.#createBasemapLayer( + 'satellite', + this.basemapsList[projectionCode].satellite, + coreBasemapOptions.shaded ? 0.75 : defaultOpacity, + true + ); + if (satelliteLayer) { + basemapLayers.push(satelliteLayer); + basemaplayerTypes.push('satellite'); + + // Set default origin,extent,resolutions from layer + defaultOrigin = satelliteLayer.origin; + defaultExtent = satelliteLayer.extent; + defaultResolutions = satelliteLayer.resolutions; + minZoom = satelliteLayer.minScale; + maxZoom = satelliteLayer.maxScale; + } + } + if (basemapLayers.length && coreBasemapOptions.labeled) { const labelLayer = await this.#createBasemapLayer( 'label', @@ -396,7 +418,7 @@ export class Basemap { } if (basemapLayers.length > 0 || (basemapLayers.length === 0 && coreBasemapOptions.basemapId === 'nogeom')) { - // id and type are derived from the basemap type composition (shaded, label, transport, simple) + // Id and type are derived from the basemap type composition (shaded, label, transport, simple) const basemap = { basemapId: basemaplayerTypes.join(''), layers: basemapLayers, @@ -428,13 +450,11 @@ export class Basemap { } /** - * Create a custom basemap - * - * @param {TypeBasemapProps} basemapProps basemap properties - * @param {TypeValidMapProjectionCodes} projection projection code - * @param {TypeDisplayLanguage} language optional language - * - * @returns {TypeBasemapProps} the created custom basemap + * Create a custom basemap. + * @param {TypeBasemapProps} basemapProps - Basemap properties. + * @param {TypeValidMapProjectionCodes} projection - Projection code. + * @param {TypeDisplayLanguage} language - Optional language. + * @returns {TypeBasemapProps} The created custom basemap. */ createCustomBasemap( basemapProps: TypeBasemapProps, @@ -446,16 +466,16 @@ export class Basemap { fr: string; } - // extract bilangual sections + // Extract bilangual sections const name: bilingual = basemapProps.name as unknown as bilingual; const description: bilingual = basemapProps.description as unknown as bilingual; const thumbnailUrl: bilingual = basemapProps.thumbnailUrl as unknown as bilingual; const attribution: bilingual = basemapProps.attribution as unknown as bilingual; - // check if language is provided for the basemap creation + // Check if language is provided for the basemap creation const languageCode = language === undefined ? AppEventProcessor.getDisplayLanguage(this.mapId) : language; - // create the basemap properties + // Create the basemap properties const formatProps: TypeBasemapProps = { ...basemapProps }; formatProps.name = languageCode === 'en' ? name.en : name.fr; formatProps.layers = basemapProps.layers.map((layer) => { @@ -486,16 +506,15 @@ export class Basemap { // #endregion /** - * Load the default basemap that was passed in the map config - * - * @param {TypeValidMapProjectionCodes} projection optional projection code - * @param {TypeDisplayLanguage} language optional language + * Load the default basemap that was passed in the map config. + * @param {TypeValidMapProjectionCodes} projection - Optional projection code. + * @param {TypeDisplayLanguage} language - Optional language. */ async loadDefaultBasemaps(projection?: TypeValidMapProjectionCodes, language?: TypeDisplayLanguage): Promise { const basemap = await this.createCoreBasemap(MapEventProcessor.getBasemapOptions(this.mapId), projection, language); if (basemap) { - // info used by create custom basemap + // Info used by create custom basemap this.defaultOrigin = basemap?.defaultOrigin; this.defaultResolutions = basemap?.defaultResolutions; this.defaultExtent = basemap?.defaultExtent; @@ -505,50 +524,49 @@ export class Basemap { } /** - * Set the current basemap and update the basemap layers on the map - * - * @param {TypeBasemapProps} basemap the basemap + * Set the current basemap and update the basemap layers on the map. + * @param {TypeBasemapProps} basemap - The basemap. */ setBasemap(basemap: TypeBasemapProps): void { - // set active basemap + // Set active basemap this.activeBasemap = basemap; - // set store attribution for the selected basemap or empty string if not provided + // Set store attribution for the selected basemap or empty string if not provided MapEventProcessor.setMapAttribution(this.mapId, basemap ? basemap.attribution : ['']); - // update the basemap layers on the map + // Update the basemap layers on the map if (basemap?.layers) { - // remove previous basemaps + // Remove previous basemaps const layers = MapEventProcessor.getMapViewer(this.mapId).map.getAllLayers(); - // loop through all layers on the map + // Loop through all layers on the map for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) { const layer = layers[layerIndex]; - // get group id that this layer belongs to + // Get group id that this layer belongs to const layerId = layer.get('mapId'); - // check if the group id matches basemap + // Check if the group id matches basemap if (layerId && layerId === 'basemap') { - // remove the basemap layer + // Remove the basemap layer MapEventProcessor.getMapViewer(this.mapId).map.removeLayer(layer); } } - // add basemap layers + // Add basemap layers basemap.layers.forEach((layer, index) => { const basemapLayer = new TileLayer({ opacity: layer.opacity, source: layer.source, }); - // set this basemap's group id to basemap + // Set this basemap's group id to basemap basemapLayer.set('mapId', 'basemap'); - // add the basemap layer + // Add the basemap layer MapEventProcessor.getMapViewer(this.mapId).map.getLayers().insertAt(index, basemapLayer); - // render the layer + // Render the layer basemapLayer.changed(); }); @@ -586,13 +604,13 @@ export class Basemap { } /** - * Define an event for the delegate + * Define an event for the delegate. */ export type BasemapChangedEvent = { basemap: TypeBasemapProps; }; /** - * Define a delegate for the event handler function signature + * Define a delegate for the event handler function signature. */ type BasemapChangedDelegate = EventDelegateBase;