diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 297f5e903f0..ba9065963d5 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -143,7 +143,7 @@ importers: version: 6.3.1(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@types/react@18.3.18)(react@18.3.1) '@mui/x-date-pickers': specifier: ^7.20.0 - version: 7.23.3(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/material@6.3.1)(@mui/system@6.3.1)(@types/react@18.3.18)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) + version: 7.23.6(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/material@6.3.1)(@mui/system@6.3.1)(@types/react@18.3.18)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) '@nieuwlandgeo/sldreader': specifier: ^0.4.3 version: 0.4.3(ol@10.3.1) @@ -200,7 +200,7 @@ importers: version: 7.5.1(react@18.3.1) material-react-table: specifier: ^3.0.1 - version: 3.1.0(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/icons-material@6.3.1)(@mui/material@6.3.1)(@mui/x-date-pickers@7.23.3)(react-dom@18.3.1)(react@18.3.1) + version: 3.1.0(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/icons-material@6.3.1)(@mui/material@6.3.1)(@mui/x-date-pickers@7.23.6)(react-dom@18.3.1)(react@18.3.1) ol: specifier: ^10.2.1 version: 10.3.1 @@ -2962,8 +2962,8 @@ packages: react-is: 19.0.0 dev: false - /@mui/x-date-pickers@7.23.3(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/material@6.3.1)(@mui/system@6.3.1)(@types/react@18.3.18)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-bjTYX/QzD5ZhVZNNnastMUS3j2Hy4p4IXmJgPJ0vKvQBvUdfEO+ZF42r3PJNNde0FVT1MmTzkmdTlz0JZ6ukdw==} + /@mui/x-date-pickers@7.23.6(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/material@6.3.1)(@mui/system@6.3.1)(@types/react@18.3.18)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-jt6rEAYLju3NZe3y2S+I5KcTiSHV79FW0jeNUEUTceg1qsPzseHbND66k3zVF0hO3N2oZtLtPywof6vN5Doe+Q==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.9.0 @@ -2971,7 +2971,7 @@ packages: '@mui/material': ^5.15.14 || ^6.0.0 '@mui/system': ^5.15.14 || ^6.0.0 date-fns: ^2.25.0 || ^3.2.0 || ^4.0.0 - date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 + date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0 dayjs: ^1.10.7 luxon: ^3.0.2 moment: ^2.29.4 @@ -3005,7 +3005,7 @@ packages: '@mui/material': 6.3.1(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) '@mui/system': 6.3.1(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@types/react@18.3.18)(react@18.3.1) '@mui/utils': 6.3.1(@types/react@18.3.18)(react@18.3.1) - '@mui/x-internals': 7.23.0(@types/react@18.3.18)(react@18.3.1) + '@mui/x-internals': 7.23.6(@types/react@18.3.18)(react@18.3.1) '@types/react-transition-group': 4.4.12(@types/react@18.3.18) clsx: 2.1.1 dayjs: 1.11.13 @@ -3017,8 +3017,8 @@ packages: - '@types/react' dev: false - /@mui/x-internals@7.23.0(@types/react@18.3.18)(react@18.3.1): - resolution: {integrity: sha512-bPclKpqUiJYIHqmTxSzMVZi6MH51cQsn5U+8jskaTlo3J4QiMeCYJn/gn7YbeR9GOZFp8hetyHjoQoVHKRXCig==} + /@mui/x-internals@7.23.6(@types/react@18.3.18)(react@18.3.1): + resolution: {integrity: sha512-hT1Pa4PNCnxwiauPbYMC3p4DiEF1x05Iu4C1MtC/jMJ1LtthymLmTuQ6ZQ53/R9FeqK6sYd6A6noR+vNMjp5DA==} engines: {node: '>=14.0.0'} peerDependencies: react: ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -4517,8 +4517,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001690 - electron-to-chromium: 1.5.79 + caniuse-lite: 1.0.30001692 + electron-to-chromium: 1.5.80 node-releases: 2.0.19 update-browserslist-db: 1.1.2(browserslist@4.24.4) dev: true @@ -4592,8 +4592,8 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite@1.0.30001690: - resolution: {integrity: sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==} + /caniuse-lite@1.0.30001692: + resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==} dev: true /ccount@2.0.1: @@ -5288,8 +5288,8 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: true - /electron-to-chromium@1.5.79: - resolution: {integrity: sha512-nYOxJNxQ9Om4EC88BE4pPoNI8xwSFf8pU/BAeOl4Hh/b/i6V4biTAzwV7pXi3ARKeoYO5JZKMIXTryXSVer5RA==} + /electron-to-chromium@1.5.80: + resolution: {integrity: sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==} dev: true /email-addresses@5.0.0: @@ -6594,8 +6594,8 @@ packages: toidentifier: 1.0.1 dev: true - /http-parser-js@0.5.8: - resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + /http-parser-js@0.5.9: + resolution: {integrity: sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==} dev: true /http-proxy-middleware@2.0.7(@types/express@4.17.21): @@ -7878,7 +7878,7 @@ packages: hasBin: true dev: true - /material-react-table@3.1.0(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/icons-material@6.3.1)(@mui/material@6.3.1)(@mui/x-date-pickers@7.23.3)(react-dom@18.3.1)(react@18.3.1): + /material-react-table@3.1.0(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/icons-material@6.3.1)(@mui/material@6.3.1)(@mui/x-date-pickers@7.23.6)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-/zPn38QhxQE7mkwLex4CojX3UP2+/+/u7NVq7CHS5d6P8LdTteJPVYXVzym/uhaXzAzFB1ojsbP7zI/y6iUdtQ==} engines: {node: '>=16'} peerDependencies: @@ -7894,7 +7894,7 @@ packages: '@emotion/styled': 11.14.0(@emotion/react@11.14.0)(@types/react@18.3.18)(react@18.3.1) '@mui/icons-material': 6.3.1(@mui/material@6.3.1)(@types/react@18.3.18)(react@18.3.1) '@mui/material': 6.3.1(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) - '@mui/x-date-pickers': 7.23.3(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/material@6.3.1)(@mui/system@6.3.1)(@types/react@18.3.18)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) + '@mui/x-date-pickers': 7.23.6(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@mui/material@6.3.1)(@mui/system@6.3.1)(@types/react@18.3.18)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) '@tanstack/match-sorter-utils': 8.19.4 '@tanstack/react-table': 8.20.6(react-dom@18.3.1)(react@18.3.1) '@tanstack/react-virtual': 3.11.2(react-dom@18.3.1)(react@18.3.1) @@ -7931,8 +7931,8 @@ packages: engines: {node: '>= 0.6'} dev: true - /memfs@4.15.3: - resolution: {integrity: sha512-vR/g1SgqvKJgAyYla+06G4p/EOcEmwhYuVb1yc1ixcKf8o/sh7Zngv63957ZSNd1xrZJoinmNyDf2LzuP8WJXw==} + /memfs@4.17.0: + resolution: {integrity: sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==} engines: {node: '>= 4.0.0'} dependencies: '@jsonjoy.com/json-pack': 1.1.1(tslib@2.8.1) @@ -10198,7 +10198,7 @@ packages: optional: true dependencies: colorette: 2.0.20 - memfs: 4.15.3 + memfs: 4.17.0 mime-types: 2.1.35 on-finished: 2.4.1 range-parser: 1.2.1 @@ -10322,7 +10322,7 @@ packages: resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} engines: {node: '>=0.8.0'} dependencies: - http-parser-js: 0.5.8 + http-parser-js: 0.5.9 safe-buffer: 5.2.1 websocket-extensions: 0.1.4 dev: true diff --git a/packages/geoview-core/public/templates/demos/demo-function-event.html b/packages/geoview-core/public/templates/demos/demo-function-event.html index 2e2b02ce8ac..65e538da857 100644 --- a/packages/geoview-core/public/templates/demos/demo-function-event.html +++ b/packages/geoview-core/public/templates/demos/demo-function-event.html @@ -207,6 +207,9 @@

Events that will generate notifications:

const LYR_PATH_FEATURE = "esriFeatureLYR5/0"; const LYR_PATH_GEOCORE = "f4c51eaa-a6ca-48b9-a1fc-b0651da20509"; + var CNT_GENERIC_STATUS_CHANGE = 0; + var CNT_SPECIFIC_STATUS_CHANGE = 0; + // Register a handler when the map is init cgpv.onMapInit((mapId) => { // !! @@ -269,7 +272,7 @@

Events that will generate notifications:

// listen to ANY/ALL layer status at ANY time (generic event catcher) cgpv.api.maps[mapId].layer.legendsLayerSet.onLayerStatusUpdated((sender, payload) => { //cgpv.api.maps.Map1.notifications.addNotificationSuccess(`${payload.layer.layerPath} (generic event) status changed to ${payload.layer.layerStatus}`); - console.log(`${payload.layer.layerPath} (generic event) status changed to ${payload.layer.layerStatus}`); + console.log(`${payload.layer.layerPath} (generic event ${++CNT_GENERIC_STATUS_CHANGE}) status changed to ${payload.layer.layerStatus}`); }); // listen to layer item visibility changed event (any layers) @@ -298,9 +301,10 @@

Events that will generate notifications:

// Check the layer status of the particular layer before registering the hook - to really know at which status the layer is. // GV If you really want to make sure to track ALL status changes for ANY particular layer, you can use a hook such as: // `cgpv.api.maps[mapId].layer.legendsLayerSet.onLayerStatusUpdated()`. See example in cgpv.onMapInit handler above. + cgpv.api.maps.Map1.layer.getLayerEntryConfig(LYR_PATH_UNIQUE)?.onLayerStatusChanged((sender, payload) => { cgpv.api.maps.Map1.notifications.addNotificationSuccess(`${LYR_PATH_UNIQUE} (specific event) status changed to ${payload.layerStatus}`); - console.log(`${LYR_PATH_UNIQUE} (specific event) status changed to ${payload.layerStatus}`); + console.log(`${LYR_PATH_UNIQUE} (specific event ${++CNT_SPECIFIC_STATUS_CHANGE}) status changed to ${payload.layerStatus}`); }); // listen to individual layer loaded event diff --git a/packages/geoview-core/public/templates/demos/demo-osdp-integration.html b/packages/geoview-core/public/templates/demos/demo-osdp-integration.html index 8eeb08b529a..640d8efa948 100644 --- a/packages/geoview-core/public/templates/demos/demo-osdp-integration.html +++ b/packages/geoview-core/public/templates/demos/demo-osdp-integration.html @@ -243,7 +243,7 @@

OSDP Integration

/** OSDP function * Adds layers to the map. - * + * * @param layers Array of layers. */ function addLayers(layers) { diff --git a/packages/geoview-core/src/core/components/layers/left-panel/add-new-layer/add-new-layer.tsx b/packages/geoview-core/src/core/components/layers/left-panel/add-new-layer/add-new-layer.tsx index 10389f713cd..5af4aac8779 100644 --- a/packages/geoview-core/src/core/components/layers/left-panel/add-new-layer/add-new-layer.tsx +++ b/packages/geoview-core/src/core/components/layers/left-panel/add-new-layer/add-new-layer.tsx @@ -248,7 +248,7 @@ export function AddNewLayer(): JSX.Element { geoviewLayerConfig: wmsGeoviewLayerConfig, layerId: childLayer.Name as string, layerName: childLayer.Title as string, - } as OgcWmsLayerEntryConfig) + } as unknown as OgcWmsLayerEntryConfig) ); } diff --git a/packages/geoview-core/src/core/utils/config/validation-classes/config-base-class.ts b/packages/geoview-core/src/core/utils/config/validation-classes/config-base-class.ts index b570ef3e581..20f69c5f0e0 100644 --- a/packages/geoview-core/src/core/utils/config/validation-classes/config-base-class.ts +++ b/packages/geoview-core/src/core/utils/config/validation-classes/config-base-class.ts @@ -149,6 +149,9 @@ export abstract class ConfigBaseClass { } if (newLayerStatus === 'processed' && this.#waitForProcessedBeforeSendingLoaded) this.layerStatus = 'loaded'; + // GV For quick debug, uncomment the line + // if (newLayerStatus === 'error') debugger; + // TODO: Cleanup - Commenting this and leaving it here for now.. It turns out that the parentLayerConfig property can't be trusted // GV due to a bug with different instances of entryconfigs stored in the objects and depending how you navigate the objects, you get // GV different instances. Example below (where 'parentLayerConfig.listOfLayerEntryConfig[0]' is indeed going back to 'uniqueValueId/uniqueValueId/4') @@ -222,6 +225,28 @@ export abstract class ConfigBaseClass { } as unknown as TypeJsonObject; } + /** + * Clones the configuration class. + * + * @returns {ConfigBaseClass} The cloned ConfigBaseClass object. + */ + clone(): ConfigBaseClass { + // Redirect to clone the object and return it + return this.onClone(); + } + + /** + * Overridable function to clone a child of a ConfigBaseClass. + * + * @returns {ConfigBaseClass} The cloned child object of a ConfigBaseClass. + */ + protected onClone(): ConfigBaseClass { + // Crash on purpose. + // GV Make sure to implement a 'protected override onClone(): ConfigBaseClass' in the child-class to + // GV use this cloning feature. See OgcWMSLayerEntryConfig for example. + throw new Error(`Not implemented exception onClone on layer path ${this.layerPath}`); + } + /** * Recursively checks the list of layer entries to see if all of them are greater than or equal to the provided layer status. * diff --git a/packages/geoview-core/src/core/utils/config/validation-classes/raster-validation-classes/ogc-wms-layer-entry-config.ts b/packages/geoview-core/src/core/utils/config/validation-classes/raster-validation-classes/ogc-wms-layer-entry-config.ts index 87796dd0ba7..2a42a3b7874 100644 --- a/packages/geoview-core/src/core/utils/config/validation-classes/raster-validation-classes/ogc-wms-layer-entry-config.ts +++ b/packages/geoview-core/src/core/utils/config/validation-classes/raster-validation-classes/ogc-wms-layer-entry-config.ts @@ -1,5 +1,6 @@ import { CONST_LAYER_TYPES } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; import { CONST_LAYER_ENTRY_TYPES, TypeSourceImageWmsInitialConfig } from '@/geo/map/map-schema-types'; +import { ConfigBaseClass } from '@/core/utils/config/validation-classes/config-base-class'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; /** ****************************************************************************************************************************** @@ -40,4 +41,13 @@ export class OgcWmsLayerEntryConfig extends AbstractBaseLayerEntryConfig { // Default value for layerConfig.source.serverType is 'mapserver'. if (!this.source.serverType) this.source.serverType = 'mapserver'; } + + /** + * Clones an instance of a OgcWmsLayerEntryConfig. + * + * @returns {ConfigBaseClass} The cloned OgcWmsLayerEntryConfig instance + */ + protected override onClone(): ConfigBaseClass { + return new OgcWmsLayerEntryConfig(this); + } } diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/esri-layer-common.ts b/packages/geoview-core/src/geo/layer/geoview-layers/esri-layer-common.ts index 3a425c35211..323200c9a6b 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/esri-layer-common.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/esri-layer-common.ts @@ -142,7 +142,8 @@ export function commonValidateListOfLayerEntryConfig( // TODO: Refactor: Do not do this on the fly here anymore with the new configs (quite unpredictable)... // Don't forget to replace the old version in the registered layers - MapEventProcessor.getMapViewerLayerAPI(layer.mapId).setLayerEntryConfigObsolete(groupLayerConfig); + // TODO: Officially remove setLayerEntryConfigObsolete once passed testing + // MapEventProcessor.getMapViewerLayerAPI(layer.mapId).setLayerEntryConfigObsolete(groupLayerConfig); (layer.metadata!.layers[esriIndex].subLayerIds as TypeJsonArray).forEach((layerId) => { // Make sure to copy the layerConfig source before recycling it in the constructors. This was causing the 'source' value to leak between layer entry configs @@ -164,7 +165,8 @@ export function commonValidateListOfLayerEntryConfig( // FIXME: Temporary patch to keep the behavior until those layer classes don't exist // TODO: Refactor: Do not do this on the fly here anymore with the new configs (quite unpredictable)... (standardizing this call with the other one above for now) - MapEventProcessor.getMapViewerLayerAPI(layer.mapId).setLayerEntryConfigObsolete(subLayerEntryConfig); + // TODO: Officially remove setLayerEntryConfigObsolete once passed testing + // MapEventProcessor.getMapViewerLayerAPI(layer.mapId).setLayerEntryConfigObsolete(subLayerEntryConfig); }); layer.validateListOfLayerEntryConfig(newListOfLayerEntryConfig); diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/wms.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/wms.ts index f6bd073d178..03023a20c08 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/wms.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/wms.ts @@ -7,9 +7,7 @@ import { Options as SourceOptions } from 'ol/source/ImageWMS'; import WMSCapabilities from 'ol/format/WMSCapabilities'; import { Extent } from 'ol/extent'; -import cloneDeep from 'lodash/cloneDeep'; - -import { Cast, TypeJsonArray, TypeJsonObject } from '@/core/types/global-types'; +import { TypeJsonArray, TypeJsonObject } from '@/core/types/global-types'; import { AbstractGeoViewLayer, CONST_LAYER_TYPES } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; import { AbstractGeoViewRaster } from '@/geo/layer/geoview-layers/raster/abstract-geoview-raster'; import { TypeLayerEntryConfig, TypeGeoviewLayerConfig, CONST_LAYER_ENTRY_TYPES, layerEntryIsGroupLayer } from '@/geo/map/map-schema-types'; @@ -21,6 +19,7 @@ import { logger } from '@/core/utils/logger'; import { OgcWmsLayerEntryConfig } from '@/core/utils/config/validation-classes/raster-validation-classes/ogc-wms-layer-entry-config'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; import { GroupLayerEntryConfig } from '@/core/utils/config/validation-classes/group-layer-entry-config'; +import { ConfigBaseClass } from '@/core/utils/config/validation-classes/config-base-class'; export interface TypeWMSLayerConfig extends Omit { geoviewLayerType: typeof CONST_LAYER_TYPES.WMS; @@ -97,17 +96,21 @@ export class WMS extends AbstractGeoViewRaster { // GV Layers Refactoring - Obsolete (in config) protected override async fetchServiceMetadata(): Promise { const metadataUrl = this.metadataAccessPath; - if (metadataUrl) { - const metadataAccessPathIsXmlFile = metadataUrl.slice(-4).toLowerCase() === '.xml'; + let curatedMetadataUrl = metadataUrl; + if (!metadataUrl.includes('request=GetCapabilities')) { + curatedMetadataUrl = `${metadataUrl}?service=WMS&version=1.3.0&request=GetCapabilities`; + } + if (curatedMetadataUrl) { + const metadataAccessPathIsXmlFile = curatedMetadataUrl.slice(-4).toLowerCase() === '.xml'; if (metadataAccessPathIsXmlFile) { // XML metadata is a special case that does not use GetCapabilities to get the metadata - await this.#fetchXmlServiceMetadata(metadataUrl); + await this.#fetchXmlServiceMetadata(curatedMetadataUrl); } else { const layerConfigsToQuery = this.#getLayersToQuery(); if (layerConfigsToQuery.length === 0) { // Use GetCapabilities to get the metadata try { - const metadata = await this.#getServiceMetadata(`${metadataUrl}?service=WMS&version=1.3.0&request=GetCapabilities`); + const metadata = await this.#getServiceMetadata(curatedMetadataUrl); this.metadata = metadata; this.#processMetadataInheritance(); } catch (error) { @@ -126,9 +129,7 @@ export class WMS extends AbstractGeoViewRaster { for (i = 0; layerConfigsToQuery[i].layerId !== layerConfig.layerId; i++); if (i === layerIndex) // This is the first time we execute this query - promisedArrayOfMetadata.push( - this.#getServiceMetadata(`${metadataUrl}?service=WMS&version=1.3.0&request=GetCapabilities&Layers=${layerConfig.layerId}`) - ); + promisedArrayOfMetadata.push(this.#getServiceMetadata(`${curatedMetadataUrl}&Layers=${layerConfig.layerId}`)); // query already done. Use previous returned value else promisedArrayOfMetadata.push(promisedArrayOfMetadata[i]); }); @@ -396,7 +397,7 @@ export class WMS extends AbstractGeoViewRaster { } if ('Layer' in layerFound) { - this.#createGroupLayer(layerFound, layerConfig as AbstractBaseLayerEntryConfig); + this.#createGroupLayer(layerFound, layerConfig as unknown as GroupLayerEntryConfig); return; } @@ -409,11 +410,11 @@ export class WMS extends AbstractGeoViewRaster { * This method create recursively dynamic group layers from the service metadata. * * @param {TypeJsonObject} layer The dynamic group layer metadata. - * @param {AbstractBaseLayerEntryConfig} layerConfig The layer configurstion associated to the dynamic group. + * @param {GroupLayerEntryConfig} layerConfig The group layer configuration associated to the dynamic group. * @private */ // GV Layers Refactoring - Obsolete (in config) - #createGroupLayer(layer: TypeJsonObject, layerConfig: AbstractBaseLayerEntryConfig): void { + #createGroupLayer(layer: TypeJsonObject, layerConfig: GroupLayerEntryConfig): void { // TODO: Refactor - createGroup is the same thing for all the layers type? group is a geoview structure. // TO.DOCONT: Should it be handle upper in abstract class to loop in structure and launch the creation of a leaf? // TODO: The answer is no. Even if the final structure is the same, the input structure is different for each geoview layer types. @@ -423,17 +424,17 @@ export class WMS extends AbstractGeoViewRaster { arrayOfLayerMetadata.forEach((subLayer) => { // Log for pertinent debugging purposes logger.logTraceCore('WMS - createGroupLayer', 'Cloning the layer config', layerConfig.layerPath); - const subLayerEntryConfig: TypeLayerEntryConfig = cloneDeep(layerConfig); - subLayerEntryConfig.parentLayerConfig = Cast(layerConfig); + const subLayerEntryConfig: ConfigBaseClass = layerConfig.clone(); + subLayerEntryConfig.parentLayerConfig = layerConfig; subLayerEntryConfig.layerId = subLayer.Name as string; subLayerEntryConfig.layerName = subLayer.Title as string; - newListOfLayerEntryConfig.push(subLayerEntryConfig); + newListOfLayerEntryConfig.push(subLayerEntryConfig as TypeLayerEntryConfig); // FIXME: Temporary patch to keep the behavior until those layer classes don't exist this.getMapViewer().layer.registerLayerConfigInit(subLayerEntryConfig); }); - const switchToGroupLayer = Cast(layerConfig); + const switchToGroupLayer = layerConfig; switchToGroupLayer.entryType = CONST_LAYER_ENTRY_TYPES.GROUP; switchToGroupLayer.layerName = layer.Title as string; switchToGroupLayer.isMetadataLayerGroup = true; diff --git a/packages/geoview-core/src/geo/layer/gv-layers/abstract-gv-layer.ts b/packages/geoview-core/src/geo/layer/gv-layers/abstract-gv-layer.ts index 35f29637f1b..947c01b2398 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/abstract-gv-layer.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/abstract-gv-layer.ts @@ -393,15 +393,15 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer { /** * Queries the legend. - * This function raises legend querying and queried events. It calls the overridable getLegend() function. + * This function raises legend querying and queried events. It calls the overridable onFetchLegend() function. * @returns {Promise} The promise when the legend (or null) will be received */ queryLegend(): Promise { // Emit that the legend has been queried this.#emitLegendQuerying(); - // Get the legend - const promiseLegend = this.getLegend(); + // Fetch the legend by calling the overridable function + const promiseLegend = this.onFetchLegend(); // Whenever the promise resolves promiseLegend @@ -452,8 +452,7 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer { * of the layerConfig object is undefined, the legend property of the object returned will be null. * @returns {Promise} The legend of the layer. */ - async getLegend(): Promise { - // TODO: Refactor - Layers refactoring. Rename this function to onFetchLegend() once the layers refactoring is done + async onFetchLegend(): Promise { try { const legend: TypeLegend = { type: this.getLayerConfig().geoviewLayerConfig.geoviewLayerType, diff --git a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-dynamic.ts b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-dynamic.ts index 00cd055c094..a2014201b40 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-dynamic.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-dynamic.ts @@ -665,10 +665,10 @@ export class GVEsriDynamic extends AbstractGVRaster { * Overrides the fetching of the legend for an Esri Dynamic layer. * @returns {Promise} The legend of the layer or null. */ - override async getLegend(): Promise { + override async onFetchLegend(): Promise { const layerConfig = this.getLayerConfig(); // Only raster layers need the alternate code - if (layerConfig.getLayerMetadata()?.type !== 'Raster Layer') return super.getLegend(); + if (layerConfig.getLayerMetadata()?.type !== 'Raster Layer') return super.onFetchLegend(); try { if (!layerConfig) return null; diff --git a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-image.ts b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-image.ts index e4f6802064b..a13b91cbcf9 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-image.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-esri-image.ts @@ -102,7 +102,7 @@ export class GVEsriImage extends AbstractGVRaster { * Overrides the fetching of the legend for an Esri image layer. * @returns {Promise} The legend of the layer or null. */ - override async getLegend(): Promise { + override async onFetchLegend(): Promise { const layerConfig = this.getLayerConfig(); try { if (!layerConfig) return null; diff --git a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-image-static.ts b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-image-static.ts index 8e3960f7c02..7b89315e784 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-image-static.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-image-static.ts @@ -102,7 +102,7 @@ export class GVImageStatic extends AbstractGVRaster { * Overrides the fetching of the legend for an Esri image layer. * @returns {Promise} The legend of the layer or null. */ - override async getLegend(): Promise { + override async onFetchLegend(): Promise { const layerConfig = this.getLayerConfig(); try { const legendImage = await GVImageStatic.#getLegendImage(layerConfig!); diff --git a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-wms.ts b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-wms.ts index af8dde51d58..83d7599f46b 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-wms.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/raster/gv-wms.ts @@ -180,9 +180,12 @@ export class GVWMS extends AbstractGVRaster { } } } - } else if (infoFormat === 'text/html') { - featureMember = { html: response.data }; - } else featureMember = { plain_text: { '#text': response.data } }; + } else if (response.data && response.data.length > 0) { + // The response has any data to show + if (infoFormat === 'text/html') { + featureMember = { html: response.data }; + } else featureMember = { plain_text: { '#text': response.data } }; + } if (featureMember) { const featureInfoResult = GVWMS.#formatWmsFeatureInfoResult(featureMember, clickCoordinate); @@ -202,7 +205,7 @@ export class GVWMS extends AbstractGVRaster { * Overrides the fetching of the legend for a WMS layer. * @returns {Promise} The legend of the layer or null. */ - override async getLegend(): Promise { + override async onFetchLegend(): Promise { try { // Get the layer config in a loaded phase const layerConfig = this.getLayerConfig(); @@ -214,7 +217,7 @@ export class GVWMS extends AbstractGVRaster { // If more than 1 if (this.WMSStyles.length > 1) { for (let i = 0; i < this.WMSStyles.length; i++) { - // TODO: refactor - does this await in a loop may haev an impact on performance? + // TODO: refactor - does this await in a loop may have an impact on performance? // TO.DOCONT: In this case here, when glancing at the code, the only reason to await would be if the order that the styleLegend // TO.DOCONT: get added to the styleLegends array MUST be the same order as they are in the WMSStyles array (as in they are 2 arrays with same indexes pointers). // TO.DOCONT: Without the await, WMSStyles[2] stuff could be associated with something in styleLegends[1] position for example (1<>2). @@ -251,7 +254,7 @@ export class GVWMS extends AbstractGVRaster { return legend; } catch (error) { // Log - logger.logError('gv-wms.getLegend()\n', error); + logger.logError('gv-wms.onFetchLegend()\n', error); return null; } } diff --git a/packages/geoview-core/src/geo/layer/layer.ts b/packages/geoview-core/src/geo/layer/layer.ts index 418f05da3cd..9e74429fac6 100644 --- a/packages/geoview-core/src/geo/layer/layer.ts +++ b/packages/geoview-core/src/geo/layer/layer.ts @@ -250,22 +250,23 @@ export class LayerApi { return this.#layerEntryConfigs?.[layerPath]; } - /** - * Obsolete function to set the layer configuration in the registered layers. - */ - setLayerEntryConfigObsolete(layerConfig: ConfigBaseClass): void { - // FIXME: This function should be deleted once the Layers refactoring is done. It unregisters and registers an updated layer entry config. - // FIX.MECONT: This is because of the EsriDynamic and EsriFeature entry config being generated on-the-fly when registration of layer entry config has already happened. - // Get the config already existing if any - const alreadyExisting = this.#layerEntryConfigs[layerConfig.layerPath]; - if (alreadyExisting) { - // Unregister the old one - this.unregisterLayerConfig(alreadyExisting, false); - } - - // Register this new one - this.registerLayerConfigInit(layerConfig); - } + // TODO: Officially remove setLayerEntryConfigObsolete once passed testing + // /** + // * Obsolete function to set the layer configuration in the registered layers. + // */ + // setLayerEntryConfigObsolete(layerConfig: ConfigBaseClass): void { + // // FIXME: This function should be deleted once the Layers refactoring is done. It unregisters and registers an updated layer entry config. + // // FIX.MECONT: This is because of the EsriDynamic and EsriFeature entry config being generated on-the-fly when registration of layer entry config has already happened. + // // Get the config already existing if any + // const alreadyExisting = this.#layerEntryConfigs[layerConfig.layerPath]; + // if (alreadyExisting) { + // // Unregister the old one + // this.unregisterLayerConfig(alreadyExisting, false); + // } + + // // Register this new one + // this.registerLayerConfigInit(layerConfig); + // } /** * Returns the OpenLayer instance associated with the layer path. @@ -1202,12 +1203,19 @@ export class LayerApi { // Remove layer info from registered layers this.getLayerEntryConfigIds().forEach((registeredLayerPath) => { if (registeredLayerPath.startsWith(layerPath)) { - // Remove ol layer + // Remove actual OL layer from the map if (this.getOLLayer(registeredLayerPath)) this.mapViewer.map.removeLayer(this.getOLLayer(registeredLayerPath) as BaseLayer); - // Unregister layer + + // Unregister layer config from the application this.unregisterLayerConfig(this.getLayerEntryConfig(registeredLayerPath)!); - // Remove from registered layers + + // Remove from registered layer configs delete this.#layerEntryConfigs[registeredLayerPath]; + delete this.#geoviewLayers[registeredLayerPath]; + + // Remove from registered layers + delete this.#gvLayers[registeredLayerPath]; + delete this.#olLayers[registeredLayerPath]; } }); diff --git a/packages/geoview-core/src/geo/utils/projection.ts b/packages/geoview-core/src/geo/utils/projection.ts index 2163caa1d0a..b5dde4d2366 100644 --- a/packages/geoview-core/src/geo/utils/projection.ts +++ b/packages/geoview-core/src/geo/utils/projection.ts @@ -29,6 +29,7 @@ export abstract class Projection { 3578: 'EPSG:3578', LCC: 'EPSG:3978', 3979: 'EPSG:3979', + 42101: 'EPSG:42101', 102100: 'EPSG:102100', // TODO: Minor - The official name of this projection is ESRI:102100 (not EPSG:102100). However, for the purpose of simplification in GeoView code base, we name it with EPSG prefix. 102184: 'EPSG:102184', // TODO: Minor - The official name of this projection is ESRI:102184 (not EPSG:102184). However, for the purpose of simplification in GeoView code base, we name it with EPSG prefix. 102190: 'EPSG:102190', // TODO: Minor - The official name of this projection is ESRI:102190 (not EPSG:102190). However, for the purpose of simplification in GeoView code base, we name it with EPSG prefix. @@ -414,6 +415,21 @@ function init4269Projection(): void { if (projection) Projection.PROJECTIONS['4269'] = projection; } +/** + * Initializes the EPSG:42101 projection + */ +function init42101Projection(): void { + proj4.defs( + Projection.PROJECTION_NAMES[42101], + '+proj=lcc +lat_0=0 +lon_0=-95 +lat_1=49 +lat_2=77 +x_0=0 +y_0=-8000000 +datum=WGS84 +units=m +no_defs +type=crs' + ); + register(proj4); + + const projection = olGetProjection(Projection.PROJECTION_NAMES[42101]); + + if (projection) Projection.PROJECTIONS['42101'] = projection; +} + /** * Initializes the EPSG:3979 projection */ @@ -499,6 +515,7 @@ initCSRS98Projection(); init3578Projection(); init3979Projection(); init4269Projection(); +init42101Projection(); init102100Projection(); init102184Projection(); init102190Projection();