From a2f38f5d5172350b03349fa96b6f956e47fced17 Mon Sep 17 00:00:00 2001 From: Johann Levesque Date: Wed, 27 Nov 2024 14:20:27 -0500 Subject: [PATCH 1/7] refactor(geo): Set hybrid mode as default to use gv-layers class Closes #2617 --- .../templates/demos/demo-function-event.html | 10 +- .../public/templates/ui-components.html | 6 +- .../legend-event-processor.ts | 8 +- .../map-event-processor.ts | 4 +- .../time-slider-event-processor.ts | 9 +- .../geoview-layers/abstract-geoview-layers.ts | 45 --- .../geoview-layers/raster/esri-dynamic.ts | 28 +- .../layer/geoview-layers/raster/esri-image.ts | 28 +- .../geoview-layers/raster/image-static.ts | 23 +- .../geoview-layers/raster/vector-tiles.ts | 26 +- .../geo/layer/geoview-layers/raster/wms.ts | 27 +- .../layer/geoview-layers/raster/xyz-tiles.ts | 23 +- .../vector/abstract-geoview-vector.ts | 38 +-- .../layer/gv-layers/raster/gv-esri-dynamic.ts | 10 +- .../layer/gv-layers/vector/gv-vector-tiles.ts | 3 +- .../layer/layer-sets/abstract-layer-set.ts | 6 +- .../layer-sets/all-feature-info-layer-set.ts | 2 +- .../layer-sets/feature-info-layer-set.ts | 2 +- .../hover-feature-info-layer-set.ts | 2 +- .../geo/layer/layer-sets/legends-layer-set.ts | 4 +- packages/geoview-core/src/geo/layer/layer.ts | 298 ++++++------------ .../src/geo/map/map-schema-types.ts | 2 +- .../geoview-core/src/geo/map/map-viewer.ts | 15 +- packages/geoview-time-slider/src/index.tsx | 8 +- 24 files changed, 157 insertions(+), 470 deletions(-) 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 fb0c44b6659..76051d0f411 100644 --- a/packages/geoview-core/public/templates/demos/demo-function-event.html +++ b/packages/geoview-core/public/templates/demos/demo-function-event.html @@ -304,7 +304,7 @@

Events that will generate notifications:

}); // listen to individual layer loaded event - cgpv.api.maps.Map1.layer.getGeoviewLayerHybrid(LYR_PATH_NON_METAL)?.onIndividualLayerLoaded((sender, payload) => { + cgpv.api.maps.Map1.layer.getGeoviewLayer(LYR_PATH_NON_METAL)?.onIndividualLayerLoaded((sender, payload) => { cgpv.api.maps.Map1.notifications.addNotificationSuccess('Nonmetal mines has finished loading'); console.log(sender.olRootLayer.getSource().getFeatures()); }); @@ -331,22 +331,22 @@

Events that will generate notifications:

// !! // listen to layer visibility changed event (individual geoview layer) - cgpv.api.maps.Map1.layer.getGeoviewLayerHybrid(LYR_PATH_UNIQUE)?.onVisibleChanged((sender, payload) => { + cgpv.api.maps.Map1.layer.getGeoviewLayer(LYR_PATH_UNIQUE)?.onVisibleChanged((sender, payload) => { cgpv.api.maps.Map1.notifications.addNotificationSuccess(`${LYR_PATH_UNIQUE} visibility set to ${payload.visible} - individual`); }); // listen to layer visibility changed event (individual geoview layer) - cgpv.api.maps.Map1.layer.getGeoviewLayerHybrid(LYR_PATH_GEOCORE)?.onVisibleChanged((sender, payload) => { + cgpv.api.maps.Map1.layer.getGeoviewLayer(LYR_PATH_GEOCORE)?.onVisibleChanged((sender, payload) => { cgpv.api.maps.Map1.notifications.addNotificationSuccess(`layer ${payload.layerPath} visibility set to ${payload.visible} - individual`); }); // listen to layer opacity changed event - cgpv.api.maps.Map1.layer.getGeoviewLayerHybrid(LYR_PATH_UNIQUE)?.onLayerOpacityChanged((sender, payload) => { + cgpv.api.maps.Map1.layer.getGeoviewLayer(LYR_PATH_UNIQUE)?.onLayerOpacityChanged((sender, payload) => { cgpv.api.maps.Map1.notifications.addNotificationSuccess(`${payload.layerPath} opacity changed to ${payload.opacity}`); }); // listen to layer filter applied event - cgpv.api.maps.Map1.layer.getGeoviewLayerHybrid(LYR_PATH_UNIQUE)?.onLayerFilterApplied((sender, payload) => { + cgpv.api.maps.Map1.layer.getGeoviewLayer(LYR_PATH_UNIQUE)?.onLayerFilterApplied((sender, payload) => { cgpv.api.maps.Map1.notifications.addNotificationSuccess(`Filter ${payload.filter} applied to ${payload.layerPath}`); }); diff --git a/packages/geoview-core/public/templates/ui-components.html b/packages/geoview-core/public/templates/ui-components.html index c653d1e7740..a2ad8680901 100644 --- a/packages/geoview-core/public/templates/ui-components.html +++ b/packages/geoview-core/public/templates/ui-components.html @@ -173,10 +173,10 @@

Accessing slider value from outside of the core viewer using api event liste track: 'normal', onChange: (dates) => { const field = cgpv.api.maps.UI1.layer - .getGeoviewLayerHybrid('historical-flood/0') - .getTemporalDimension('historical-flood/0').field; + .getGeoviewLayer('historical-flood/0') + .getTemporalDimension().field; cgpv.api.maps.UI1.layer - .getGeoviewLayerHybrid('historical-flood/0') + .getGeoviewLayer('historical-flood/0') .applyViewFilter('historical-flood/0', `${field} >= date '${dates[0]}-01-01' and ${field} <= date '${dates[1]}-12-31'`); }, }) diff --git a/packages/geoview-core/src/api/event-processors/event-processor-children/legend-event-processor.ts b/packages/geoview-core/src/api/event-processors/event-processor-children/legend-event-processor.ts index 2bce99e9fd1..ed8a497fa9d 100644 --- a/packages/geoview-core/src/api/event-processors/event-processor-children/legend-event-processor.ts +++ b/packages/geoview-core/src/api/event-processors/event-processor-children/legend-event-processor.ts @@ -132,9 +132,7 @@ export class LegendEventProcessor extends AbstractEventProcessor { objectIds: string[], outfield?: string ): Promise | undefined { - return MapEventProcessor.getMapViewerLayerAPI(mapId) - .getGeoviewLayerHybrid(layerPath) - ?.getExtentFromFeatures(layerPath, objectIds, outfield); + return MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(layerPath)?.getExtentFromFeatures(layerPath, objectIds, outfield); } static getLayerIconImage(layerLegend: TypeLegend | null): TypeLegendLayerItem[] | undefined { @@ -247,7 +245,7 @@ export class LegendEventProcessor extends AbstractEventProcessor { if (!layerConfig) return; // Get the layer - const layer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayerHybrid(entryLayerPath); + const layer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(entryLayerPath); // Interpret the layer name the best we can const layerName = @@ -544,7 +542,7 @@ export class LegendEventProcessor extends AbstractEventProcessor { const layer = LegendEventProcessor.findLayerByPath(curLayers, layerPath); if (layer) { layer.opacity = opacity; - MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayerHybrid(layerPath)?.setOpacity(opacity, layerPath); + MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(layerPath)?.setOpacity(opacity); if (isChild) { layer.opacityFromParent = opacity; } diff --git a/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts b/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts index 4f50671147d..ed2cfd64590 100644 --- a/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts +++ b/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts @@ -1253,7 +1253,7 @@ export class MapEventProcessor extends AbstractEventProcessor { * @param {string} layerPath The path of the layer to apply filters to. */ static applyLayerFilters(mapId: string, layerPath: string): void { - const geoviewLayer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayerHybrid(layerPath); + const geoviewLayer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(layerPath); if (geoviewLayer) { if ( geoviewLayer instanceof WMS || @@ -1282,7 +1282,7 @@ export class MapEventProcessor extends AbstractEventProcessor { * @param {string} layerPath The path for the layer to get filters from. */ static getActiveVectorFilters(mapId: string, layerPath: string): (string | undefined)[] | undefined { - const geoviewLayer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayerHybrid(layerPath); + const geoviewLayer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(layerPath); if (geoviewLayer) { const initialFilter = this.getInitialFilter(mapId, layerPath); const tableFilter = DataTableEventProcessor.getTableFilter(mapId, layerPath); diff --git a/packages/geoview-core/src/api/event-processors/event-processor-children/time-slider-event-processor.ts b/packages/geoview-core/src/api/event-processors/event-processor-children/time-slider-event-processor.ts index b6c9ebe6795..77b5383c050 100644 --- a/packages/geoview-core/src/api/event-processors/event-processor-children/time-slider-event-processor.ts +++ b/packages/geoview-core/src/api/event-processors/event-processor-children/time-slider-event-processor.ts @@ -4,7 +4,6 @@ import { TimeSliderLayerSet, TypeTimeSliderValues, } from '@/core/stores/store-interface-and-intial-values/time-slider-state'; -import { AbstractGeoViewLayer } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; import { WMS } from '@/geo/layer/geoview-layers/raster/wms'; import { TypeFeatureInfoLayerConfig, TypeLayerEntryConfig, layerEntryIsGroupLayer } from '@/geo/map/map-schema-types'; import { EsriImage } from '@/geo/layer/geoview-layers/raster/esri-image'; @@ -123,16 +122,16 @@ export class TimeSliderEventProcessor extends AbstractEventProcessor { static getInitialTimeSliderValues(mapId: string, layerConfig: TypeLayerEntryConfig): TypeTimeSliderValues | undefined { // Get the layer using the map event processor, If no temporal dimension OR layerPath, return undefined if (!layerConfig.layerPath) return undefined; - const geoviewLayer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayerHybrid(layerConfig.layerPath)!; + const geoviewLayer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(layerConfig.layerPath)!; // If a group if (layerEntryIsGroupLayer(layerConfig)) return undefined; // Cast the layer - const geoviewLayerCasted = geoviewLayer as AbstractGeoViewLayer | AbstractGVLayer; + const geoviewLayerCasted = geoviewLayer as AbstractGVLayer | AbstractGVLayer; // Get the temporal dimension information - const temporalDimensionInfo = geoviewLayerCasted.getTemporalDimension(layerConfig.layerPath); + const temporalDimensionInfo = geoviewLayerCasted.getTemporalDimension(); // If no temporal dimension information if (!temporalDimensionInfo || !temporalDimensionInfo.range) return undefined; @@ -259,7 +258,7 @@ export class TimeSliderEventProcessor extends AbstractEventProcessor { values: number[] ): void { // Get the layer using the map event processor - const geoviewLayer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayerHybrid(layerPath)!; + const geoviewLayer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(layerPath)!; let filter: string; if (geoviewLayer instanceof WMS || geoviewLayer instanceof GVWMS) { diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts b/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts index f3f20735d1f..bd6cb2fe4b6 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts @@ -33,7 +33,6 @@ import { TypeLayerStatus, TypeStyleGeometry, CONST_LAYER_ENTRY_TYPES, - TypeLoadEndListenerType, TypeFeatureInfoEntry, codedValueType, rangeDomainType, @@ -43,7 +42,6 @@ import { import { GeoViewLayerCreatedTwiceError } from '@/geo/layer/exceptions/layer-exceptions'; import { getLegendStyles, getFeatureCanvas } from '@/geo/utils/renderer/geoview-renderer'; import { ConfigBaseClass } from '@/core/utils/config/validation-classes/config-base-class'; -import { LayerApi } from '../layer'; import { TypeLegend } from '@/core/stores/store-interface-and-intial-values/layer-state'; import { MapViewer } from '@/geo/map/map-viewer'; @@ -1462,49 +1460,6 @@ export abstract class AbstractGeoViewLayer { return Promise.resolve(); } - /** - * The olLayerAndLoadEndListeners setter method for the ConfigBaseClass class and its descendant classes. - * @param {AbstractBaseLayerEntryConfig} layerConfig - The layer configuration we are creating a layer for. - * @param {BaseLayer} olLayer - The OpenLayer we are creating - * @param {TypeLoadEndListenerType} listenerType - The layer listener type. - */ - setLayerAndLoadEndListeners(layerConfig: AbstractBaseLayerEntryConfig, olLayer: BaseLayer, listenerType: TypeLoadEndListenerType): void { - // Precond: - if (!olLayer) throw new Error(`An OpenLayer must be provided to register listeners. Layer path ${layerConfig.layerPath}`); - if (!listenerType) throw new Error(`A listenerType must be provided to register listeners. Layer path ${layerConfig.layerPath}`); - - // If in old LAYERS_HYBRID_MODE (in the new LAYERS_HYBRID_MODE we want the new classes to handle that) - if (!LayerApi.LAYERS_HYBRID_MODE) { - // Group layers have no listener - if (layerConfig.entryType !== CONST_LAYER_ENTRY_TYPES.GROUP) { - let loadErrorListener: () => void; - - // Definition of the load end listener functions - const loadEndListener = (): void => { - // Call the overridable loaded function - this.onLoaded(layerConfig); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (olLayer.get('source') as any).un(`${listenerType}loaderror`, loadErrorListener); - }; - - loadErrorListener = (): void => { - // Call the overridable error function - this.onError(layerConfig); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (olLayer.get('source') as any).un(`${listenerType}loadend`, loadEndListener); - }; - - // Activation of the load end listeners - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (olLayer.get('source') as any).once(`${listenerType}loaderror`, loadErrorListener); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (olLayer.get('source') as any).once(`${listenerType}loadend`, loadEndListener); - } - } - } - /** * Recursively gets all layer entry configs in the GeoView Layer. * @returns {ConfigBaseClass[]} The list of layer entry configs diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts index e5de8496b10..764be70ea2f 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts @@ -2,7 +2,6 @@ // We have many reassign for layerPath-layerConfig. We keep it global... import { ImageArcGISRest } from 'ol/source'; import { Options as SourceOptions } from 'ol/source/ImageArcGISRest'; -import { Options as ImageOptions } from 'ol/layer/BaseImage'; import BaseLayer from 'ol/layer/Base'; import { Image as ImageLayer } from 'ol/layer'; import { Coordinate } from 'ol/coordinate'; @@ -286,32 +285,7 @@ export class EsriDynamic extends AbstractGeoViewRaster { if (requestResult.length > 0) { // Get the OpenLayer that was created olLayer = requestResult[0] as ImageLayer; - } - - // If no olLayer was obtained - if (!olLayer) { - // We're working in old LAYERS_HYBRID_MODE (in the new mode the code below is handled in the new classes) - const imageLayerOptions: ImageOptions = { - source, - properties: { layerConfig }, - }; - // layerConfig.initialSettings cannot be undefined because config-validation set it to {} if it is undefined. - if (layerConfig.initialSettings?.className !== undefined) imageLayerOptions.className = layerConfig.initialSettings.className; - if (layerConfig.initialSettings?.extent !== undefined) imageLayerOptions.extent = layerConfig.initialSettings.extent; - if (layerConfig.initialSettings?.maxZoom !== undefined) imageLayerOptions.maxZoom = layerConfig.initialSettings.maxZoom; - if (layerConfig.initialSettings?.minZoom !== undefined) imageLayerOptions.minZoom = layerConfig.initialSettings.minZoom; - if (layerConfig.initialSettings?.states?.opacity !== undefined) - imageLayerOptions.opacity = layerConfig.initialSettings.states.opacity; - // If a layer on the map has an initialSettings.visible set to false, its status will never reach the status 'loaded' because - // nothing is drawn on the map. We must wait until the 'loaded' status is reached to set the visibility to false. The call - // will be done in the layerConfig.loadedFunction() which is called right after the 'loaded' signal. - - // Create the OpenLayer layer - olLayer = new ImageLayer(imageLayerOptions); - - // Hook the loaded event - this.setLayerAndLoadEndListeners(layerConfig, olLayer, 'image'); - } + } else throw new Error('Error on layerRequesting event'); // GV Time to emit about the layer creation! this.emitLayerCreation({ config: layerConfig, layer: olLayer }); diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-image.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-image.ts index e022499e394..28d35c362ee 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-image.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-image.ts @@ -1,7 +1,6 @@ import { ImageArcGISRest } from 'ol/source'; import { Options as SourceOptions } from 'ol/source/ImageArcGISRest'; import BaseLayer from 'ol/layer/Base'; -import { Options as ImageOptions } from 'ol/layer/BaseImage'; import { Image as ImageLayer } from 'ol/layer'; import { Extent } from 'ol/extent'; @@ -336,32 +335,7 @@ export class EsriImage extends AbstractGeoViewRaster { if (requestResult.length > 0) { // Get the OpenLayer that was created olLayer = requestResult[0] as ImageLayer; - } - - // If no olLayer was obtained - if (!olLayer) { - // We're working in old LAYERS_HYBRID_MODE (in the new mode the code below is handled in the new classes) - const imageLayerOptions: ImageOptions = { - source, - properties: { layerConfig }, - }; - // layerConfig.initialSettings cannot be undefined because config-validation set it to {} if it is undefined. - if (layerConfig.initialSettings?.className !== undefined) imageLayerOptions.className = layerConfig.initialSettings.className; - if (layerConfig.initialSettings?.extent !== undefined) imageLayerOptions.extent = layerConfig.initialSettings.extent; - if (layerConfig.initialSettings?.maxZoom !== undefined) imageLayerOptions.maxZoom = layerConfig.initialSettings.maxZoom; - if (layerConfig.initialSettings?.minZoom !== undefined) imageLayerOptions.minZoom = layerConfig.initialSettings.minZoom; - if (layerConfig.initialSettings?.states?.opacity !== undefined) - imageLayerOptions.opacity = layerConfig.initialSettings.states.opacity; - // If a layer on the map has an initialSettings.visible set to false, its status will never reach the status 'loaded' because - // nothing is drawn on the map. We must wait until the 'loaded' status is reached to set the visibility to false. The call - // will be done in the layerConfig.loadedFunction() which is called right after the 'loaded' signal. - - // Create the OpenLayer layer - olLayer = new ImageLayer(imageLayerOptions); - - // Hook the loaded event - this.setLayerAndLoadEndListeners(layerConfig, olLayer, 'image'); - } + } else throw new Error('Error on layerRequesting event'); // GV Time to emit about the layer creation! this.emitLayerCreation({ config: layerConfig, layer: olLayer }); diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/image-static.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/image-static.ts index 2c66ce5871b..becfd9836da 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/image-static.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/image-static.ts @@ -2,7 +2,6 @@ import axios from 'axios'; import Static, { Options as SourceOptions } from 'ol/source/ImageStatic'; import BaseLayer from 'ol/layer/Base'; -import { Options as ImageOptions } from 'ol/layer/BaseImage'; import ImageLayer from 'ol/layer/Image'; import { Extent } from 'ol/extent'; @@ -274,27 +273,7 @@ export class ImageStatic extends AbstractGeoViewRaster { if (requestResult.length > 0) { // Get the OpenLayer that was created olLayer = requestResult[0] as ImageLayer; - } - - // If no olLayer was obtained - if (!olLayer) { - // We're working in old LAYERS_HYBRID_MODE (in the new mode the code below is handled in the new classes) - const staticImageOptions: ImageOptions = { source }; - // layerConfig.initialSettings cannot be undefined because config-validation set it to {} if it is undefined. - if (layerConfig.initialSettings?.extent !== undefined) staticImageOptions.extent = layerConfig.initialSettings.extent; - if (layerConfig.initialSettings?.maxZoom !== undefined) staticImageOptions.maxZoom = layerConfig.initialSettings.maxZoom; - if (layerConfig.initialSettings?.minZoom !== undefined) staticImageOptions.minZoom = layerConfig.initialSettings.minZoom; - if (layerConfig.initialSettings?.states?.opacity !== undefined) - staticImageOptions.opacity = layerConfig.initialSettings.states.opacity; - // GV IMPORTANT: The initialSettings.visible flag must be set in the layerConfig.loadedFunction otherwise the layer will stall - // GV in the 'loading' state if the flag value is false. - - // Create the OpenLayer layer - olLayer = new ImageLayer(staticImageOptions); - - // Hook the loaded event - this.setLayerAndLoadEndListeners(layerConfig, olLayer, 'image'); - } + } else throw new Error('Error on layerRequesting event'); // GV Time to emit about the layer creation! this.emitLayerCreation({ config: layerConfig, layer: olLayer }); diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts index b79fabd7ef7..85602f5ae49 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts @@ -1,7 +1,6 @@ import BaseLayer from 'ol/layer/Base'; import TileLayer from 'ol/layer/Tile'; import VectorTileLayer from 'ol/layer/VectorTile'; -import { Options as TileOptions } from 'ol/layer/BaseTile'; import VectorTileSource, { Options as SourceOptions } from 'ol/source/VectorTile'; import TileGrid, { Options as TileGridOptions } from 'ol/tilegrid/TileGrid'; import { Extent } from 'ol/extent'; @@ -213,30 +212,7 @@ export class VectorTiles extends AbstractGeoViewRaster { if (requestResult.length > 0) { // Get the OpenLayer that was created olLayer = requestResult[0] as VectorTileLayer; - } - - // If no olLayer was obtained - if (!olLayer) { - // We're working in old LAYERS_HYBRID_MODE (in the new mode the code below is handled in the new classes) - const tileLayerOptions: TileOptions = { source }; - // layerConfig.initialSettings cannot be undefined because config-validation set it to {} if it is undefined. - if (layerConfig.initialSettings?.className !== undefined) tileLayerOptions.className = layerConfig.initialSettings.className; - if (layerConfig.initialSettings?.extent !== undefined) tileLayerOptions.extent = layerConfig.initialSettings.extent; - if (layerConfig.initialSettings?.maxZoom !== undefined) tileLayerOptions.maxZoom = layerConfig.initialSettings.maxZoom; - if (layerConfig.initialSettings?.minZoom !== undefined) tileLayerOptions.minZoom = layerConfig.initialSettings.minZoom; - if (layerConfig.initialSettings?.states?.opacity !== undefined) tileLayerOptions.opacity = layerConfig.initialSettings.states.opacity; - // GV IMPORTANT: The initialSettings.visible flag must be set in the layerConfig.loadedFunction otherwise the layer will stall - // GV in the 'loading' state if the flag value is false. - - // TODO remove after demoing again - const declutter = this.mapId !== 'LYR2'; - - // Create the OpenLayer layer - olLayer = new VectorTileLayer({ ...tileLayerOptions, declutter }); - - // Hook the loaded event - this.setLayerAndLoadEndListeners(layerConfig, olLayer, 'tile'); - } + } else throw new Error('Error on layerRequesting event'); // GV Time to emit about the layer creation! this.emitLayerCreation({ config: layerConfig, layer: olLayer }); 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 00cb4f736d1..65a6ed7fec3 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 @@ -6,7 +6,6 @@ import ImageLayer from 'ol/layer/Image'; import { Coordinate } from 'ol/coordinate'; import { Pixel } from 'ol/pixel'; import BaseLayer from 'ol/layer/Base'; -import { Options as ImageOptions } from 'ol/layer/BaseImage'; import { ImageWMS } from 'ol/source'; import { Options as SourceOptions } from 'ol/source/ImageWMS'; import WMSCapabilities from 'ol/format/WMSCapabilities'; @@ -552,31 +551,7 @@ export class WMS extends AbstractGeoViewRaster { if (requestResult.length > 0) { // Get the OpenLayer that was created olLayer = requestResult[0] as ImageLayer; - } - - // If no olLayer was obtained - if (!olLayer) { - // We're working in old LAYERS_HYBRID_MODE (in the new mode the code below is handled in the new classes) - const imageLayerOptions: ImageOptions = { - source, - properties: { layerCapabilities, layerConfig }, - }; - // layerConfig.initialSettings cannot be undefined because config-validation set it to {} if it is undefined. - if (layerConfig.initialSettings?.className !== undefined) imageLayerOptions.className = layerConfig.initialSettings.className; - if (layerConfig.initialSettings?.extent !== undefined) imageLayerOptions.extent = layerConfig.initialSettings.extent; - if (layerConfig.initialSettings?.maxZoom !== undefined) imageLayerOptions.maxZoom = layerConfig.initialSettings.maxZoom; - if (layerConfig.initialSettings?.minZoom !== undefined) imageLayerOptions.minZoom = layerConfig.initialSettings.minZoom; - if (layerConfig.initialSettings?.states?.opacity !== undefined) - imageLayerOptions.opacity = layerConfig.initialSettings.states.opacity; - // GV IMPORTANT: The initialSettings.visible flag must be set in the layerConfig.loadedFunction otherwise the layer will stall - // GV in the 'loading' state if the flag value is false. - - // Create the OpenLayer layer - olLayer = new ImageLayer(imageLayerOptions); - - // Hook the loaded event - this.setLayerAndLoadEndListeners(layerConfig, olLayer, 'image'); - } + } else throw new Error('Error on layerRequesting event'); // GV Time to emit about the layer creation! this.emitLayerCreation({ config: layerConfig, layer: olLayer }); diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/xyz-tiles.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/xyz-tiles.ts index c7776b9b9e3..46fbac200ea 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/xyz-tiles.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/xyz-tiles.ts @@ -1,6 +1,5 @@ import BaseLayer from 'ol/layer/Base'; import TileLayer from 'ol/layer/Tile'; -import { Options as TileOptions } from 'ol/layer/BaseTile'; import XYZ, { Options as SourceOptions } from 'ol/source/XYZ'; import TileGrid, { Options as TileGridOptions } from 'ol/tilegrid/TileGrid'; import { Extent } from 'ol/extent'; @@ -213,27 +212,7 @@ export class XYZTiles extends AbstractGeoViewRaster { if (requestResult.length > 0) { // Get the OpenLayer that was created olLayer = requestResult[0] as TileLayer; - } - - // If no olLayer was obtained - if (!olLayer) { - // We're working in old LAYERS_HYBRID_MODE (in the new mode the code below is handled in the new classes) - const tileLayerOptions: TileOptions = { source }; - // layerConfig.initialSettings cannot be undefined because config-validation set it to {} if it is undefined. - if (layerConfig.initialSettings?.className !== undefined) tileLayerOptions.className = layerConfig.initialSettings.className; - if (layerConfig.initialSettings?.extent !== undefined) tileLayerOptions.extent = layerConfig.initialSettings.extent; - if (layerConfig.initialSettings?.maxZoom !== undefined) tileLayerOptions.maxZoom = layerConfig.initialSettings.maxZoom; - if (layerConfig.initialSettings?.minZoom !== undefined) tileLayerOptions.minZoom = layerConfig.initialSettings.minZoom; - if (layerConfig.initialSettings?.states?.opacity !== undefined) tileLayerOptions.opacity = layerConfig.initialSettings.states.opacity; - // GV IMPORTANT: The initialSettings.visible flag must be set in the layerConfig.loadedFunction otherwise the layer will stall - // GV in the 'loading' state if the flag value is false. - - // Create the OpenLayer layer - olLayer = new TileLayer(tileLayerOptions); - - // Hook the loaded event - this.setLayerAndLoadEndListeners(layerConfig, olLayer, 'tile'); - } + } else throw new Error('Error on layerRequesting event'); // GV Time to emit about the layer creation! this.emitLayerCreation({ config: layerConfig, layer: olLayer }); diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts b/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts index 9b74f5d1d2e..53e2fa2f71c 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts @@ -4,7 +4,6 @@ import Feature from 'ol/Feature'; import { Vector as VectorSource } from 'ol/source'; import { Options as SourceOptions } from 'ol/source/Vector'; import { VectorImage as VectorLayer } from 'ol/layer'; -import { Options as VectorLayerOptions } from 'ol/layer/VectorImage'; import { GeoJSON as FormatGeoJSON } from 'ol/format'; import { all, bbox } from 'ol/loadingstrategy'; import { ReadOptions } from 'ol/format/Feature'; @@ -34,7 +33,6 @@ import { logger } from '@/core/utils/logger'; import { VectorLayerEntryConfig } from '@/core/utils/config/validation-classes/vector-layer-entry-config'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; import { analyzeLayerFilter } from '@/geo/utils/renderer/geoview-renderer'; -import { AbstractGVVector } from '../../gv-layers/vector/abstract-gv-vector'; import { MapEventProcessor } from '@/api/event-processors/event-processor-children/map-event-processor'; import { Projection } from '@/geo/utils/projection'; import { getMinOrMaxExtents } from '@/geo/utils/utilities'; @@ -335,9 +333,6 @@ export abstract class AbstractGeoViewVector extends AbstractGeoViewLayer { */ // GV Layers Refactoring - Obsolete (this is bridging between config and layers, okay) protected createVectorLayer(layerConfig: VectorLayerEntryConfig, vectorSource: VectorSource): VectorLayer { - // Get the style label - const label = layerConfig.layerName || layerConfig.layerId; - // GV Time to request an OpenLayers layer! const requestResult = this.emitLayerRequesting({ config: layerConfig, source: vectorSource }); @@ -346,38 +341,7 @@ export abstract class AbstractGeoViewVector extends AbstractGeoViewLayer { if (requestResult.length > 0) { // Get the OpenLayer that was created olLayer = requestResult[0] as VectorLayer; - } - - // If no olLayer was obtained - if (!olLayer) { - // We're working in old LAYERS_HYBRID_MODE (in the new mode the code below is handled in the new classes) - // Create the vector layer options. - const layerOptions: VectorLayerOptions = { - properties: { layerConfig }, - source: vectorSource, - style: (feature) => { - return AbstractGVVector.calculateStyleForFeature( - this, - feature, - label, - layerConfig.layerPath, - layerConfig.filterEquation, - layerConfig.legendFilterIsOff - ); - }, - }; - - if (layerConfig.initialSettings?.extent !== undefined) layerOptions.extent = layerConfig.initialSettings.extent; - if (layerConfig.initialSettings?.maxZoom !== undefined) layerOptions.maxZoom = layerConfig.initialSettings.maxZoom; - if (layerConfig.initialSettings?.minZoom !== undefined) layerOptions.minZoom = layerConfig.initialSettings.minZoom; - if (layerConfig.initialSettings?.states?.opacity !== undefined) layerOptions.opacity = layerConfig.initialSettings.states.opacity; - - // Create the OpenLayer layer - olLayer = new VectorLayer(layerOptions); - - // Hook the loaded event - this.setLayerAndLoadEndListeners(layerConfig, olLayer, 'features'); - } + } else throw new Error('Error on layerRequesting event'); // GV Time to emit about the layer creation! this.emitLayerCreation({ config: layerConfig, layer: olLayer }); 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 9f7b343a399..baa3952ef8d 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 @@ -309,10 +309,7 @@ export class GVEsriDynamic extends AbstractGVRaster { static #countFieldOfTheSameValue(styleSettings: TypeLayerStyleSettings): TypeFieldOfTheSameValue[][] { return styleSettings.info.reduce( (counter, styleEntry): TypeFieldOfTheSameValue[][] => { - if ( - (styleEntry.visible === false && styleSettings.info[styleSettings.info.length - 1].visible !== false) || - (styleEntry.visible !== false && styleSettings.info[styleSettings.info.length - 1].visible === false) - ) { + if (styleEntry.visible !== false) { styleEntry.values.forEach((styleValue, i) => { const valueExist = counter[i].find((counterEntry) => counterEntry.value === styleValue); if (valueExist) valueExist.nbOccurence++; @@ -547,10 +544,7 @@ export class GVEsriDynamic extends AbstractGVRaster { ): TypeQueryTree { const queryTree: TypeQueryTree = []; styleSettings.info.forEach((styleEntry) => { - if ( - (styleEntry.visible === false && styleSettings.info[styleSettings.info.length - 1].visible !== false) || - (styleEntry.visible !== false && styleSettings.info[styleSettings.info.length - 1].visible === false) - ) { + if (styleEntry.visible !== false) { let levelToSearch = queryTree; for (let i = 0; i < fieldOrder.length; i++) { if (fieldOfTheSameValue[fieldOrder[i]].find((field) => field.value === styleEntry.values[fieldOrder[i]])) { diff --git a/packages/geoview-core/src/geo/layer/gv-layers/vector/gv-vector-tiles.ts b/packages/geoview-core/src/geo/layer/gv-layers/vector/gv-vector-tiles.ts index fb418da78a1..7a039e89fa3 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/vector/gv-vector-tiles.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/vector/gv-vector-tiles.ts @@ -30,7 +30,8 @@ export class GVVectorTiles extends AbstractGVVectorTile { AbstractGVVectorTile.initOptionsWithInitialSettings(tileLayerOptions, layerConfig); // Create and set the OpenLayer layer - this.olLayer = new VectorTileLayer({ ...tileLayerOptions }); + const declutter = true; + this.olLayer = new VectorTileLayer({ ...tileLayerOptions, declutter }); } /** diff --git a/packages/geoview-core/src/geo/layer/layer-sets/abstract-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/abstract-layer-set.ts index a4b65606987..7c9bca21bfd 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/abstract-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/abstract-layer-set.ts @@ -138,7 +138,7 @@ export abstract class AbstractLayerSet { } // Get the layer - const layer = this.layerApi.getGeoviewLayerHybrid(layerConfig.layerPath); + const layer = this.layerApi.getGeoviewLayer(layerConfig.layerPath); // If the layer could be found if (layer) { @@ -228,7 +228,7 @@ export abstract class AbstractLayerSet { // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done // Override this function to perform registration condition logic in the inherited classes // By default, a layer-set always registers layers except when they are group layers - if (this.layerApi.getGeoviewLayerHybrid(layerPath)?.getLayerConfig(layerPath)?.entryType === 'group') { + if (this.layerApi.getGeoviewLayer(layerPath)?.getLayerConfig()?.entryType === 'group') { // Skip groups return false; } @@ -277,7 +277,7 @@ export abstract class AbstractLayerSet { this.onUnregisterLayerConfig(this.layerApi.getLayerEntryConfig(layerPath)); // Call the unregistration function for the layer-set. This method is different for each child. - this.onUnregisterLayer(this.layerApi.getGeoviewLayerHybrid(layerPath)); + this.onUnregisterLayer(this.layerApi.getGeoviewLayer(layerPath)); // Delete from the store this.onDeleteFromStore(layerPath); diff --git a/packages/geoview-core/src/geo/layer/layer-sets/all-feature-info-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/all-feature-info-layer-set.ts index e2d539d7e43..1c2099afb9d 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/all-feature-info-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/all-feature-info-layer-set.ts @@ -107,7 +107,7 @@ export class AllFeatureInfoLayerSet extends AbstractLayerSet { if (!this.resultSet[layerPath].eventListenerEnabled) return Promise.resolve(); // Get the layer config and layer associated with the layer path - const layer = this.layerApi.getGeoviewLayerHybrid(layerPath); + const layer = this.layerApi.getGeoviewLayer(layerPath); // If layer was found if (layer && (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer)) { diff --git a/packages/geoview-core/src/geo/layer/layer-sets/feature-info-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/feature-info-layer-set.ts index a1a787edb55..5cb8c1b3b1a 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/feature-info-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/feature-info-layer-set.ts @@ -131,7 +131,7 @@ export class FeatureInfoLayerSet extends AbstractLayerSet { if (!this.resultSet[layerPath].eventListenerEnabled) return; // Get the layer config and layer associated with the layer path - const layer = this.layerApi.getGeoviewLayerHybrid(layerPath); + const layer = this.layerApi.getGeoviewLayer(layerPath); // If layer was found if (layer && (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer)) { diff --git a/packages/geoview-core/src/geo/layer/layer-sets/hover-feature-info-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/hover-feature-info-layer-set.ts index 676f50fbead..7ef4716b420 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/hover-feature-info-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/hover-feature-info-layer-set.ts @@ -106,7 +106,7 @@ export class HoverFeatureInfoLayerSet extends AbstractLayerSet { if (!this.resultSet[layerPath].eventListenerEnabled) return; // Get the layer config and layer associated with the layer path - const layer = this.layerApi.getGeoviewLayerHybrid(layerPath); + const layer = this.layerApi.getGeoviewLayer(layerPath); // If layer was found if (layer && (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer)) { diff --git a/packages/geoview-core/src/geo/layer/layer-sets/legends-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/legends-layer-set.ts index 8fcef23a855..c7d9aadd369 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/legends-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/legends-layer-set.ts @@ -135,8 +135,8 @@ export class LegendsLayerSet extends AbstractLayerSet { */ #checkQueryLegend(layerPath: string, forced: boolean): void { // Get the layer - const layer = this.layerApi.getGeoviewLayerHybrid(layerPath); - const layerConfig = layer?.getLayerConfig(layerPath); + const layer = this.layerApi.getGeoviewLayer(layerPath); + const layerConfig = layer?.getLayerConfig(); // If the layer legend should be queried (and not already querying). // GV Gotta make sure that we're not already querying, because EsriImage layers, for example, adjust the diff --git a/packages/geoview-core/src/geo/layer/layer.ts b/packages/geoview-core/src/geo/layer/layer.ts index 4f139e8deb4..187371d1434 100644 --- a/packages/geoview-core/src/geo/layer/layer.ts +++ b/packages/geoview-core/src/geo/layer/layer.ts @@ -164,11 +164,6 @@ export class LayerApi { // Maximum time duration to wait when registering a layer for the time slider static #MAX_WAIT_TIME_SLIDER_REGISTRATION = 20000; - // ************************************************************ - // INDICATES IF USING HYBRID MODE WITH THE NEW GVLAYERS CLASSES - // ************************************************************ - static LAYERS_HYBRID_MODE = true; - /** * Initializes layer types and listen to add/remove layer events from outside * @param {MapViewer} mapViewer - A reference to the map viewer @@ -195,101 +190,31 @@ export class LayerApi { /** * Gets the GeoView Layer Ids. - * Note: those are different than the layer paths. - * @returns {string[]} The GeoView Layer Ids + * @returns The ids of the new Geoview Layers */ getGeoviewLayerIds(): string[] { - return Object.keys(this.#geoviewLayers); + return Object.keys(this.#gvLayers); } /** * Gets all GeoView Layers - * @returns {AbstractGeoViewLayer[]} The GeoView Layers + * @returns The list of new Geoview Layers */ - getGeoviewLayers(): AbstractGeoViewLayer[] { - return Object.values(this.#geoviewLayers); + getGeoviewLayers(): AbstractBaseLayer[] { + return Object.values(this.#gvLayers); } /** * Returns the GeoView instance associated to the layer path. * The first element of the layerPath is the geoviewLayerId and this function will * work with either the geoViewLayerId or the layerPath. - * @param {string} layerPath - The layer path (or in this case the geoviewLayerId) of the layer's configuration. - * @returns {AbstractGeoViewLayer} Returns the geoview instance associated to the layer path. - */ - getGeoviewLayer(layerPath: string): AbstractGeoViewLayer | undefined { - // TODO: Refactor - This function will be replaced to return the new layer classes model - // The first element of the layerPath is the geoviewLayerId - return this.#geoviewLayers[layerPath.split('/')[0]]; - } - - /** - * Temporary new function for migration purposes, replacing getGeoviewLayerIds - * @returns The ids of the new Geoview Layers - */ - getGeoviewLayerIdsNew(): string[] { - return Object.keys(this.#gvLayers); - } - - /** - * Temporary new function for migration purposes, replacing getGeoviewLayers - * @returns The list of new Geoview Layers - */ - getGeoviewLayersNew(): AbstractBaseLayer[] { - return Object.values(this.#gvLayers); - } - - /** - * New function for migration purposes, replacing getGeoviewLayer * @param {string} layerPath - The layer path * @returns The new Geoview Layer */ - getGeoviewLayerNew(layerPath: string): AbstractBaseLayer | undefined { + getGeoviewLayer(layerPath: string): AbstractBaseLayer | undefined { return this.#gvLayers[layerPath]; } - /** - * Hybrid function for migration purposes, bridging the gap between getGeoviewLayer and getGeoviewLayerNew - * @param {string} layerPath - The layer path - * @returns The new Geoview Layer - */ - getGeoviewLayerIdsHybrid(): string[] { - // TODO: Refactor - This function will be replaced to jump to the new layer classes model - // If new mode - if (LayerApi.LAYERS_HYBRID_MODE) return this.getGeoviewLayerIdsNew(); - - // Old mode - return this.getGeoviewLayerIds(); - } - - /** - * Hybrid function for migration purposes, bridging the gap between getGeoviewLayer and getGeoviewLayerNew - * @param {string} layerPath - The layer path - * @returns The new Geoview Layer - */ - getGeoviewLayersHybrid(): AbstractGeoViewLayer[] | AbstractBaseLayer[] { - // TODO: Refactor - This function will be replaced to jump to the new layer classes model - // If new mode - if (LayerApi.LAYERS_HYBRID_MODE) return this.getGeoviewLayersNew(); - - // Old mode - return this.getGeoviewLayers(); - } - - /** - * Hybrid function for migration purposes, bridging the gap between getGeoviewLayer and getGeoviewLayerNew - * @param {string} layerPath - The layer path - * @returns The new Geoview Layer or the old Geoview Layer - */ - getGeoviewLayerHybrid(layerPath: string): AbstractGeoViewLayer | AbstractBaseLayer | undefined { - // TODO: Refactor - This function will be replaced to jump to the new layer classes model - // If new mode - if (LayerApi.LAYERS_HYBRID_MODE) return this.getGeoviewLayerNew(layerPath); - - // Old mode - return this.getGeoviewLayer(layerPath); - } - /** * Verifies if a layer is registered. Returns true if registered. * @param {string} layerPath - The layer path to check. @@ -348,11 +273,8 @@ export class LayerApi { * @returns {BaseLayer} Returns the geoview instance associated to the layer path. */ getOLLayer(layerPath: string): BaseLayer | undefined { - // If new mode, get the OpenLayer layer as part of the new GVLayer design - if (LayerApi.LAYERS_HYBRID_MODE) return this.getGeoviewLayerNew(layerPath)?.getOLLayer(); - - // Old mode, get the OpenLayer layer stored in the dictionary - return this.#olLayers[layerPath]; + // Get the OpenLayer layer as part of the new GVLayer design + return this.getGeoviewLayer(layerPath)?.getOLLayer(); } /** @@ -766,24 +688,18 @@ export class LayerApi { // Log logger.logDebug(`Requesting layer for ${event.config.layerPath} on map ${this.getMapId()}`, event.config); - // If new layers mode, create the corresponding GVLayer - if (LayerApi.LAYERS_HYBRID_MODE) { - const gvLayer = this.#createGVLayer(this.getMapId(), geoviewLayer, event.source, event.config, event.extraConfig); - - // If found the GV layer - if (gvLayer) { - // Register a hook when a layer is loaded on the map + // Create the corresponding GVLayer + const gvLayer = this.#createGVLayer(this.getMapId(), geoviewLayer, event.source, event.config, event.extraConfig); + if (gvLayer) { + // Register a hook when a layer is loaded on the map gvLayer!.onIndividualLayerLoaded((sender, payload) => { // Log logger.logDebug(`${payload.layerPath} loaded on map ${this.getMapId()}`); this.#emitLayerLoaded({ layer: sender, layerPath: payload.layerPath }); }); - return gvLayer.getOLLayer(); - } + return gvLayer.getOLLayer(); } - - // Don't provide any layer, working in old mode - return undefined; + throw new Error('Error, no corresponding GV layer'); }); // Register hook when an OpenLayer layer has been created @@ -795,13 +711,10 @@ export class LayerApi { // This is tempting to put in the onLayerRequesting handler, but this one here also traps the LayerGroups this.#olLayers[event.config.layerPath] = event.layer; - // If new layers mode, create the corresponding GVLayer - if (LayerApi.LAYERS_HYBRID_MODE) { - // If group layer was created - if (event.layer instanceof LayerGroup && event.config instanceof GroupLayerEntryConfig) { - // Create the GV Group Layer - this.#createGVGroupLayer(this.getMapId(), event.layer, event.config); - } + // Create the corresponding GVLayer. If group layer was created + if (event.layer instanceof LayerGroup && event.config instanceof GroupLayerEntryConfig) { + // Create the GV Group Layer + this.#createGVGroupLayer(this.getMapId(), event.layer, event.config); } }); @@ -921,63 +834,57 @@ export class LayerApi { // eslint-disable-next-line @typescript-eslint/no-explicit-any extraConfig?: any ): AbstractGVLayer | undefined { - // If new mode - let metadata; - let layerMetadata; - let timeDimension; - let style; - if (LayerApi.LAYERS_HYBRID_MODE) { - // Get the metadata and the time dimension information as processed - metadata = geoviewLayer.metadata; - layerMetadata = geoviewLayer.getLayerMetadata(layerConfig.layerPath); - timeDimension = geoviewLayer.getTemporalDimension(layerConfig.layerPath); - style = geoviewLayer.getStyle(layerConfig.layerPath); - - // HACK: INJECT CONFIGURATION STUFF PRETENDNG THEY WERE PROCESSED - // GV Keep this code commented in the source base for now - // if (layerConfig.layerPath === 'esriFeatureLYR5/0') { - // metadata = LayerMockup.configTop100Metadata(); - // } else if (layerConfig.layerPath === 'nonmetalmines/5') { - // metadata = LayerMockup.configNonMetalMetadata(); - // } else if (layerConfig.layerPath === 'airborne_radioactivity/1') { - // metadata = LayerMockup.configAirborneMetadata(); - // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/polygons.json') { - // metadata = LayerMockup.configPolygonsMetadata(); - // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/lines.json') { - // metadata = LayerMockup.configLinesMetadata(); - // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/icon_points.json') { - // metadata = LayerMockup.configIconPointsMetadata(); - // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/points.json') { - // metadata = LayerMockup.configPointsMetadata(); - // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/points_1.json') { - // metadata = LayerMockup.configPoints1Metadata(); - // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/points_2.json') { - // metadata = LayerMockup.configPoints2Metadata(); - // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/points_3.json') { - // metadata = LayerMockup.configPoints3Metadata(); - // } else if (layerConfig.layerPath === 'historical-flood/0') { - // metadata = LayerMockup.configHistoricalFloodMetadata(); - // timeDimension = LayerMockup.configHistoricalFloodTemporalDimension(); - // } else if (layerConfig.layerPath === 'uniqueValueId/1') { - // metadata = LayerMockup.configCESIMetadata(); - // // timeDimension = LayerMockup.configHistoricalFloodTemporalDimension(); - // } else if (layerConfig.layerPath === 'esriFeatureLYR1/0') { - // metadata = LayerMockup.configTemporalTestBedMetadata(); - // // timeDimension = LayerMockup.configHistoricalFloodTemporalDimension(); - // } else if (layerConfig.layerPath === 'wmsLYR1-spatiotemporel/RADAR_1KM_RSNO') { - // metadata = LayerMockup.configRadarMetadata(); - // timeDimension = LayerMockup.configRadarTemporalDimension(); - // } else if (layerConfig.layerPath === 'MSI/msi-94-or-more') { - // metadata = LayerMockup.configMSIMetadata(); - // timeDimension = LayerMockup.configMSITemporalDimension(); - // } - - // If good config - if (layerConfig instanceof AbstractBaseLayerEntryConfig) { - // If any metadata - if (metadata) layerConfig.setServiceMetadata(metadata); - if (layerMetadata) layerConfig.setLayerMetadata(layerMetadata); - } + // Get the metadata and the time dimension information as processed + // GV: We use the old abstractGeoviewLayer format as the GV layer is not created yet + const { metadata } = geoviewLayer; + const layerMetadata = geoviewLayer.getLayerMetadata(layerConfig.layerPath); + const timeDimension = geoviewLayer.getTemporalDimension(layerConfig.layerPath); + const style = geoviewLayer.getStyle(layerConfig.layerPath); + + // HACK: INJECT CONFIGURATION STUFF PRETENDNG THEY WERE PROCESSED + // GV Keep this code commented in the source base for now + // if (layerConfig.layerPath === 'esriFeatureLYR5/0') { + // metadata = LayerMockup.configTop100Metadata(); + // } else if (layerConfig.layerPath === 'nonmetalmines/5') { + // metadata = LayerMockup.configNonMetalMetadata(); + // } else if (layerConfig.layerPath === 'airborne_radioactivity/1') { + // metadata = LayerMockup.configAirborneMetadata(); + // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/polygons.json') { + // metadata = LayerMockup.configPolygonsMetadata(); + // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/lines.json') { + // metadata = LayerMockup.configLinesMetadata(); + // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/icon_points.json') { + // metadata = LayerMockup.configIconPointsMetadata(); + // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/points.json') { + // metadata = LayerMockup.configPointsMetadata(); + // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/points_1.json') { + // metadata = LayerMockup.configPoints1Metadata(); + // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/points_2.json') { + // metadata = LayerMockup.configPoints2Metadata(); + // } else if (layerConfig.layerPath === 'geojsonLYR1/geojsonLYR1/point-feature-group/points_3.json') { + // metadata = LayerMockup.configPoints3Metadata(); + // } else if (layerConfig.layerPath === 'historical-flood/0') { + // metadata = LayerMockup.configHistoricalFloodMetadata(); + // timeDimension = LayerMockup.configHistoricalFloodTemporalDimension(); + // } else if (layerConfig.layerPath === 'uniqueValueId/1') { + // metadata = LayerMockup.configCESIMetadata(); + // // timeDimension = LayerMockup.configHistoricalFloodTemporalDimension(); + // } else if (layerConfig.layerPath === 'esriFeatureLYR1/0') { + // metadata = LayerMockup.configTemporalTestBedMetadata(); + // // timeDimension = LayerMockup.configHistoricalFloodTemporalDimension(); + // } else if (layerConfig.layerPath === 'wmsLYR1-spatiotemporel/RADAR_1KM_RSNO') { + // metadata = LayerMockup.configRadarMetadata(); + // timeDimension = LayerMockup.configRadarTemporalDimension(); + // } else if (layerConfig.layerPath === 'MSI/msi-94-or-more') { + // metadata = LayerMockup.configMSIMetadata(); + // timeDimension = LayerMockup.configMSITemporalDimension(); + // } + + // If good config + if (layerConfig instanceof AbstractBaseLayerEntryConfig) { + // If any metadata + if (metadata) layerConfig.setServiceMetadata(metadata); + if (layerMetadata) layerConfig.setLayerMetadata(layerMetadata); } // Create the right GV Layer based on the OLLayer and config type @@ -1140,7 +1047,7 @@ export class LayerApi { try { // Wait until the layer is loaded (or processed?) await whenThisThen(() => layerConfig.isGreaterThanOrEqualTo('processed'), LayerApi.#MAX_WAIT_TIME_SLIDER_REGISTRATION); - const geoviewLayer = this.getGeoviewLayerHybrid(layerConfig.layerPath); + const geoviewLayer = this.getGeoviewLayer(layerConfig.layerPath); // If the layer is loaded AND flag is true to use time dimension, continue if ((geoviewLayer instanceof AbstractGeoViewLayer || geoviewLayer instanceof AbstractGVLayer) && geoviewLayer.getIsTimeAware()) { @@ -1226,14 +1133,14 @@ export class LayerApi { checkLayerStatus( status: TypeLayerStatus, layerEntriesToCheck: MapConfigLayerEntry[] | undefined, - callbackNotGood?: (geoviewLayer: AbstractGeoViewLayer) => void + callbackNotGood?: (geoviewLayer: AbstractBaseLayer) => void ): [boolean, number] { // If no layer entries at all or there are layer entries and there are geoview layers to check let allGood = layerEntriesToCheck?.length === 0 || Object.keys(this.#geoviewLayers).length > 0; // For each registered layer entry this.getGeoviewLayers().forEach((geoviewLayer) => { - const layerIsGood = geoviewLayer.allLayerStatusAreGreaterThanOrEqualTo(status); + const layerIsGood = ConfigBaseClass.allLayerStatusAreGreaterThanOrEqualTo(status, [geoviewLayer.getLayerConfig()]); if (!layerIsGood) { // Callback about it callbackNotGood?.(geoviewLayer); @@ -1278,7 +1185,7 @@ export class LayerApi { // FIX.MECONT: Therefore, this can't remove the layers that failed due to for example bad metadataUrl. // FIX.MECONT: To effectively remove all layers in the UI boxes, this removal process should be using the 'LayerConfigs' (and the layer paths). Not the 'Layer' classes. // For each Geoview layers - this.getGeoviewLayersHybrid().forEach((geoviewLayer) => { + this.getGeoviewLayers().forEach((geoviewLayer) => { // Remove it this.removeLayerUsingPath(geoviewLayer.getGeoviewLayerId()); }); @@ -1371,28 +1278,27 @@ export class LayerApi { */ highlightLayer(layerPath: string): void { this.removeHighlightLayer(); - const theLayerMain = this.getGeoviewLayerHybrid(layerPath); + const theLayerMain = this.getGeoviewLayer(layerPath); - this.#highlightedLayer = { layerPath, originalOpacity: theLayerMain?.getOpacity(layerPath) }; - theLayerMain?.setOpacity(1, layerPath); + this.#highlightedLayer = { layerPath, originalOpacity: theLayerMain?.getOpacity() }; + theLayerMain?.setOpacity(1); // If it is a group layer, highlight sublayers if (layerEntryIsGroupLayer(this.#layerEntryConfigs[layerPath])) { Object.keys(this.#layerEntryConfigs).forEach((registeredLayerPath) => { - // Trying to get the layer associated with the layer path, can be undefined because the layer might be in error - const theLayer = this.getGeoviewLayerHybrid(registeredLayerPath); + const theLayer = this.getGeoviewLayer(registeredLayerPath)!; if (!registeredLayerPath.startsWith(layerPath) && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { - const otherOpacity = theLayer?.getOpacity(registeredLayerPath); - theLayer?.setOpacity((otherOpacity || 1) * 0.25, registeredLayerPath); - } else this.getOLLayer(registeredLayerPath)?.setZIndex(999); + const otherOpacity = theLayer.getOpacity(); + theLayer.setOpacity((otherOpacity || 1) * 0.25); + } else this.getOLLayer(registeredLayerPath)!.setZIndex(999); }); } else { Object.keys(this.#layerEntryConfigs).forEach((registeredLayerPath) => { - // Trying to get the layer associated with the layer path, can be undefined because the layer might be in error - const theLayer = this.getGeoviewLayerHybrid(registeredLayerPath); + const theLayer = this.getGeoviewLayer(registeredLayerPath)!; + // check for otherOlLayer is undefined. It would be undefined if a layer status is error if (registeredLayerPath !== layerPath && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { - const otherOpacity = theLayer?.getOpacity(registeredLayerPath); - theLayer?.setOpacity((otherOpacity || 1) * 0.25, registeredLayerPath); + const otherOpacity = theLayer.getOpacity(); + theLayer.setOpacity((otherOpacity || 1) * 0.25); } }); this.getOLLayer(layerPath)?.setZIndex(999); @@ -1409,20 +1315,24 @@ export class LayerApi { if (layerEntryIsGroupLayer(this.#layerEntryConfigs[layerPath])) { Object.keys(this.#layerEntryConfigs).forEach((registeredLayerPath) => { // Trying to get the layer associated with the layer path, can be undefined because the layer might be in error - const theLayer = this.getGeoviewLayerHybrid(registeredLayerPath); - if (!registeredLayerPath.startsWith(layerPath) && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { - const otherOpacity = theLayer?.getOpacity(registeredLayerPath); - theLayer?.setOpacity(otherOpacity ? otherOpacity * 4 : 1, registeredLayerPath); - } else theLayer?.setOpacity(originalOpacity || 1, registeredLayerPath); + const theLayer = this.getGeoviewLayer(registeredLayerPath); + if (theLayer) { + if (!registeredLayerPath.startsWith(layerPath) && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { + const otherOpacity = theLayer.getOpacity(); + theLayer.setOpacity(otherOpacity ? otherOpacity * 4 : 1); + } else theLayer.setOpacity(originalOpacity || 1); + } }); } else { Object.keys(this.#layerEntryConfigs).forEach((registeredLayerPath) => { // Trying to get the layer associated with the layer path, can be undefined because the layer might be in error - const theLayer = this.getGeoviewLayerHybrid(registeredLayerPath); - if (registeredLayerPath !== layerPath && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { - const otherOpacity = theLayer?.getOpacity(registeredLayerPath); - theLayer?.setOpacity(otherOpacity ? otherOpacity * 4 : 1, registeredLayerPath); - } else theLayer?.setOpacity(originalOpacity || 1, registeredLayerPath); + const theLayer = this.getGeoviewLayer(registeredLayerPath); + if (theLayer) { + if (registeredLayerPath !== layerPath && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { + const otherOpacity = theLayer.getOpacity(); + theLayer.setOpacity(otherOpacity ? otherOpacity * 4 : 1); + } else theLayer.setOpacity(originalOpacity || 1); + } }); } MapEventProcessor.setLayerZIndices(this.getMapId()); @@ -1467,7 +1377,7 @@ export class LayerApi { refreshLayers(): void { // For each geoview layer this.getGeoviewLayers().forEach((geoviewLayer) => { - if (geoviewLayer.olRootLayer) this.refreshBaseLayer(geoviewLayer.olRootLayer); + if (geoviewLayer) this.refreshBaseLayer(geoviewLayer.getOLLayer()); }); } @@ -1499,7 +1409,7 @@ export class LayerApi { */ setItemVisibility(layerPath: string, item: TypeLegendItem, visibility: boolean, updateLegendLayers: boolean = true): void { // Get registered layer config - const layer = this.getGeoviewLayerHybrid(layerPath); + const layer = this.getGeoviewLayer(layerPath); if (visibility && !MapEventProcessor.getMapVisibilityFromOrderedLayerInfo(this.getMapId(), layerPath)) { MapEventProcessor.setOrToggleMapLayerVisibility(this.getMapId(), layerPath, true); @@ -1556,7 +1466,7 @@ export class LayerApi { // Go for it // eslint-disable-next-line no-param-reassign layerInfo.visible = newVisibility; - this.getGeoviewLayerHybrid(layerInfo.layerPath)?.setVisible(layerInfo.visible, layerInfo.layerPath); + this.getGeoviewLayer(layerInfo.layerPath)?.setVisible(layerInfo.visible); // Emit event this.#emitLayerVisibilityToggled({ layerPath: layerInfo.layerPath, visibility: layerInfo.visible }); } @@ -1573,7 +1483,7 @@ export class LayerApi { if ((!layerVisibility || newValue) && parentLayerVisibility === false) { if (parentLayerInfo) { parentLayerInfo.visible = true; - this.getGeoviewLayerHybrid(parentLayerPath)?.setVisible(true, parentLayerPath); + this.getGeoviewLayer(parentLayerPath)?.setVisible(true); // Emit event this.#emitLayerVisibilityToggled({ layerPath: parentLayerPath, visibility: true }); @@ -1609,7 +1519,7 @@ export class LayerApi { */ setLayerName(layerPath: string, name: string): void { // Get the layer - const layer = this.getGeoviewLayerHybrid(layerPath); + const layer = this.getGeoviewLayer(layerPath); // If found if (layer) { @@ -1697,7 +1607,7 @@ export class LayerApi { // If a leaf if (!layerEntryIsGroupLayer(layerConfig)) { // Get the layer - const layer = this.getGeoviewLayerHybrid(layerConfig.layerPath); + const layer = this.getGeoviewLayer(layerConfig.layerPath) as AbstractGeoViewLayer | AbstractGVLayer; // If the layer is of right type (should be, because we checked if not a group) if (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer) { diff --git a/packages/geoview-core/src/geo/map/map-schema-types.ts b/packages/geoview-core/src/geo/map/map-schema-types.ts index 435b1c2fa4c..7de93eb05c9 100644 --- a/packages/geoview-core/src/geo/map/map-schema-types.ts +++ b/packages/geoview-core/src/geo/map/map-schema-types.ts @@ -265,7 +265,7 @@ export type TypeFeatureInfoEntryPartial = Pick { - logger.logTraceDetailed('checkMapReady - 1 - waiting on layer registration...', geoviewLayer.geoviewLayerId); + logger.logTraceDetailed( + 'checkMapReady - 1 - waiting on layer registration...', + geoviewLayer.getLayerConfig().geoviewLayerConfig.geoviewLayerId + ); } ); @@ -675,7 +678,10 @@ export class MapViewer { 'processed', this.mapFeaturesConfig.map.listOfGeoviewLayerConfig, (geoviewLayer) => { - logger.logTraceDetailed('checkMapReady - 2 - waiting on layer processed...', geoviewLayer.geoviewLayerId); + logger.logTraceDetailed( + 'checkMapReady - 2 - waiting on layer processed...', + geoviewLayer.getLayerConfig().geoviewLayerConfig.geoviewLayerId + ); } ); @@ -712,7 +718,10 @@ export class MapViewer { 'loaded', this.mapFeaturesConfig.map.listOfGeoviewLayerConfig, (geoviewLayer) => { - logger.logTraceDetailed('checkMapReady - 3 - waiting on layer loaded/error status...', geoviewLayer.geoviewLayerId); + logger.logTraceDetailed( + 'checkMapReady - 3 - waiting on layer loaded/error status...', + geoviewLayer.getLayerConfig().geoviewLayerConfig.geoviewLayerId + ); } ); diff --git a/packages/geoview-time-slider/src/index.tsx b/packages/geoview-time-slider/src/index.tsx index 33d35a576f0..9ace5a36e02 100644 --- a/packages/geoview-time-slider/src/index.tsx +++ b/packages/geoview-time-slider/src/index.tsx @@ -139,18 +139,18 @@ class TimeSliderPlugin extends FooterPlugin { // TO.DOCONT: the later will override the plugin settings (can be tested by adding fake delays). // TO.DOCONT: If this Plugin has temporal dimension settings, for various layers, those should be set in synch with the layers // TO.DOCONT: using event listeners, not at Plugin creation. - this.mapViewer().layer.getGeoviewLayerHybrid(obj.layerPaths[0])?.setTemporalDimension(obj.layerPaths[0], timeDimension); + this.mapViewer().layer.getGeoviewLayer(obj.layerPaths[0])?.setTemporalDimension(timeDimension); } // Set override default value under time dimension if applicable if (obj.defaultValue) { const layerPath = obj.layerPaths[0]; - const timeDimension = this.mapViewer().layer.getGeoviewLayerHybrid(layerPath)?.getTemporalDimension(layerPath); + const timeDimension = this.mapViewer().layer.getGeoviewLayer(layerPath)?.getTemporalDimension(); if (timeDimension) { this.mapViewer() .layer.getGeoviewLayerHybrid(layerPath) - ?.setTemporalDimension(layerPath, { + ?.setTemporalDimension({ ...timeDimension, default: obj.defaultValue, }); @@ -216,7 +216,7 @@ class TimeSliderPlugin extends FooterPlugin { #filterTimeSliderLayers(layerPaths: string[]): string[] { const filteredLayerPaths = layerPaths.filter((layerPath) => { // Return the temporal dimension for the layer if any - return this.mapViewer().layer.getGeoviewLayerHybrid(layerPath)?.getTemporalDimension(layerPath); + return this.mapViewer().layer.getGeoviewLayer(layerPath)?.getTemporalDimension(); }); return filteredLayerPaths; } From 0e06b6aa51b64999a0444be137f244bbfaf20091 Mon Sep 17 00:00:00 2001 From: Johann Levesque Date: Fri, 29 Nov 2024 16:20:53 -0500 Subject: [PATCH 2/7] Start cleaning geoview-layers --- .../templates/demos/demo-function-event.html | 6 +- .../templates/demos/demo-geojson-inject.html | 12 +- .../public/templates/ui-components.html | 2 +- .../legend-event-processor.ts | 7 +- .../map-event-processor.ts | 18 +- .../time-slider-event-processor.ts | 5 +- .../geoview-layers/abstract-geoview-layers.ts | 961 ------------------ .../geoview-layers/raster/esri-dynamic.ts | 753 +------------- .../layer/geoview-layers/raster/esri-image.ts | 235 +---- .../geoview-layers/raster/image-static.ts | 116 +-- .../geoview-layers/raster/vector-tiles.ts | 46 - .../geo/layer/geoview-layers/raster/wms.ts | 570 +---------- .../layer/geoview-layers/raster/xyz-tiles.ts | 44 - .../vector/abstract-geoview-vector.ts | 247 +---- .../geoview-layers/vector/esri-feature.ts | 37 +- .../layer/geoview-layers/vector/geojson.ts | 50 - .../geoview-layers/vector/ogc-feature.ts | 17 - .../geo/layer/geoview-layers/vector/wfs.ts | 40 +- .../layer/gv-layers/abstract-base-layer.ts | 19 +- .../geo/layer/gv-layers/abstract-gv-layer.ts | 27 +- .../layer/gv-layers/raster/gv-esri-dynamic.ts | 21 +- .../layer/gv-layers/raster/gv-esri-image.ts | 19 +- .../layer/gv-layers/raster/gv-image-static.ts | 3 +- .../src/geo/layer/gv-layers/raster/gv-wms.ts | 16 +- .../geo/layer/gv-layers/tile/gv-xyz-tiles.ts | 3 +- .../vector/abstract-gv-vector-tile.ts | 3 +- .../gv-layers/vector/abstract-gv-vector.ts | 40 +- .../geo/layer/gv-layers/vector/gv-geojson.ts | 55 + .../layer/layer-sets/abstract-layer-set.ts | 101 +- .../layer-sets/all-feature-info-layer-set.ts | 24 +- .../layer-sets/feature-info-layer-set.ts | 26 +- .../hover-feature-info-layer-set.ts | 24 +- .../geo/layer/layer-sets/legends-layer-set.ts | 30 +- packages/geoview-core/src/geo/layer/layer.ts | 80 +- packages/geoview-time-slider/src/index.tsx | 2 +- 35 files changed, 278 insertions(+), 3381 deletions(-) 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 76051d0f411..3a42a289674 100644 --- a/packages/geoview-core/public/templates/demos/demo-function-event.html +++ b/packages/geoview-core/public/templates/demos/demo-function-event.html @@ -96,7 +96,7 @@

API Functions:

cgpv.api.maps.Map1.layer.setAllLayersVisibility(false)

  • - cgpv.api.maps.Map1.layer.setLayerName('uniqueValueId/1', 'Water Quantity') + cgpv.api.maps.Map1.layer.setLayerName('Water Quantity')

  • cgpv.api.maps.Map1.clickMarkerIconShow({lnglat: [-90, 60]}) @@ -304,6 +304,8 @@

    Events that will generate notifications:

    }); // listen to individual layer loaded event + // TODO: The gv layer does not exist at the time we attach the event. To make this work, we would + // TODO.CONT: need to implement a when-this-then internal approach. cgpv.api.maps.Map1.layer.getGeoviewLayer(LYR_PATH_NON_METAL)?.onIndividualLayerLoaded((sender, payload) => { cgpv.api.maps.Map1.notifications.addNotificationSuccess('Nonmetal mines has finished loading'); console.log(sender.olRootLayer.getSource().getFeatures()); @@ -454,7 +456,7 @@

    Events that will generate notifications:

    // add an event listener when a button is clicked renameLayerButton.addEventListener('click', async () => { - cgpv.api.maps.Map1.layer.setLayerName(LYR_PATH_UNIQUE, 'Water Quantity') + cgpv.api.maps.Map1.layer.setLayerName('Water Quantity') }); // Place Marker Button======================================================================================================== diff --git a/packages/geoview-core/public/templates/demos/demo-geojson-inject.html b/packages/geoview-core/public/templates/demos/demo-geojson-inject.html index 9de3a12ae7e..eab1468c421 100644 --- a/packages/geoview-core/public/templates/demos/demo-geojson-inject.html +++ b/packages/geoview-core/public/templates/demos/demo-geojson-inject.html @@ -97,15 +97,15 @@

    API Functions:


    • - cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource('geojsonLYR1/blank.json', points1) + cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource(points1)

    • - cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource('geojsonLYR1/blank.json', points2) + cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource(points2)

    • - cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource('geojsonLYR1/blank.json', points3) + cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource(points3)
    @@ -172,7 +172,7 @@

    API Functions:

    }; // add an event listener when a button is clicked addPoints1Button.addEventListener('click', () => { - cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource('geojsonLYR1/blank.json', points1); + cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource(points1); }); // Add points 2 button ==================================================================================== @@ -184,7 +184,7 @@

    API Functions:

    // add an event listener when a button is clicked addPoints2Button.addEventListener('click', () => { - cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource('geojsonLYR1/blank.json', points2); + cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource(points2); }); // Add points 3 button ==================================================================================== @@ -243,7 +243,7 @@

    API Functions:

    }; // add an event listener when a button is clicked addPoints3Button.addEventListener('click', () => { - cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource('geojsonLYR1/blank.json', points3); + cgpv.api.maps.LYR1.layer.getGeoviewLayer('geojsonLYR1/blank.json').overrideGeojsonSource(points3); }); diff --git a/packages/geoview-core/public/templates/ui-components.html b/packages/geoview-core/public/templates/ui-components.html index a2ad8680901..6e6ae8991ed 100644 --- a/packages/geoview-core/public/templates/ui-components.html +++ b/packages/geoview-core/public/templates/ui-components.html @@ -177,7 +177,7 @@

    Accessing slider value from outside of the core viewer using api event liste .getTemporalDimension().field; cgpv.api.maps.UI1.layer .getGeoviewLayer('historical-flood/0') - .applyViewFilter('historical-flood/0', `${field} >= date '${dates[0]}-01-01' and ${field} <= date '${dates[1]}-12-31'`); + .applyViewFilter(`${field} >= date '${dates[0]}-01-01' and ${field} <= date '${dates[1]}-12-31'`); }, }) ); diff --git a/packages/geoview-core/src/api/event-processors/event-processor-children/legend-event-processor.ts b/packages/geoview-core/src/api/event-processors/event-processor-children/legend-event-processor.ts index ed8a497fa9d..6c5de3b477d 100644 --- a/packages/geoview-core/src/api/event-processors/event-processor-children/legend-event-processor.ts +++ b/packages/geoview-core/src/api/event-processors/event-processor-children/legend-event-processor.ts @@ -132,7 +132,7 @@ export class LegendEventProcessor extends AbstractEventProcessor { objectIds: string[], outfield?: string ): Promise | undefined { - return MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(layerPath)?.getExtentFromFeatures(layerPath, objectIds, outfield); + return MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(layerPath)?.getExtentFromFeatures(objectIds, outfield); } static getLayerIconImage(layerLegend: TypeLegend | null): TypeLegendLayerItem[] | undefined { @@ -249,10 +249,7 @@ export class LegendEventProcessor extends AbstractEventProcessor { // Interpret the layer name the best we can const layerName = - layer?.getLayerName(entryLayerPath) || - layerConfig.layerName || - layerConfig.geoviewLayerConfig.geoviewLayerName || - layerConfig.layerPath; + layer?.getLayerName() || layerConfig.layerName || layerConfig.geoviewLayerConfig.geoviewLayerName || layerConfig.layerPath; let entryIndex = existingEntries.findIndex((entry) => entry.layerPath === entryLayerPath); if (layerEntryIsGroupLayer(layerConfig)) { diff --git a/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts b/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts index ed2cfd64590..dc3ca4f1ed0 100644 --- a/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts +++ b/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts @@ -55,13 +55,9 @@ import { TypeLegendLayer } from '@/core/components/layers/types'; import { ConfigBaseClass } from '@/core/utils/config/validation-classes/config-base-class'; import { VectorLayerEntryConfig } from '@/core/utils/config/validation-classes/vector-layer-entry-config'; -import { WMS } from '@/geo/layer/geoview-layers/raster/wms'; import { GVWMS } from '@/geo/layer/gv-layers/raster/gv-wms'; -import { EsriImage } from '@/geo/layer/geoview-layers/raster/esri-image'; import { GVEsriImage } from '@/geo/layer/gv-layers/raster/gv-esri-image'; -import { AbstractGeoViewVector } from '@/geo/layer/geoview-layers/vector/abstract-geoview-vector'; import { AbstractGVVector } from '@/geo/layer/gv-layers/vector/abstract-gv-vector'; -import { EsriDynamic } from '@/geo/layer/geoview-layers/raster/esri-dynamic'; import { GVEsriDynamic } from '@/geo/layer/gv-layers/raster/gv-esri-dynamic'; // GV The paradigm when working with MapEventProcessor vs MapState goes like this: @@ -1255,22 +1251,14 @@ export class MapEventProcessor extends AbstractEventProcessor { static applyLayerFilters(mapId: string, layerPath: string): void { const geoviewLayer = MapEventProcessor.getMapViewerLayerAPI(mapId).getGeoviewLayer(layerPath); if (geoviewLayer) { - if ( - geoviewLayer instanceof WMS || - geoviewLayer instanceof GVWMS || - geoviewLayer instanceof EsriImage || - geoviewLayer instanceof GVEsriImage - ) { + if (geoviewLayer instanceof GVWMS || geoviewLayer instanceof GVEsriImage) { const filter = TimeSliderEventProcessor.getTimeSliderFilter(mapId, layerPath); - if (filter) geoviewLayer.applyViewFilter(layerPath, filter); + if (filter) geoviewLayer.applyViewFilter(filter); } else { const filters = this.getActiveVectorFilters(mapId, layerPath) || ['']; // Force the layer to applyfilter so it refresh for layer class selection (esri layerDef) even if no other filter are applied. - (geoviewLayer as AbstractGeoViewVector | AbstractGVVector | EsriDynamic | GVEsriDynamic).applyViewFilter( - layerPath, - filters.join(' and ') - ); + (geoviewLayer as AbstractGVVector | GVEsriDynamic).applyViewFilter(filters.join(' and ')); } } } diff --git a/packages/geoview-core/src/api/event-processors/event-processor-children/time-slider-event-processor.ts b/packages/geoview-core/src/api/event-processors/event-processor-children/time-slider-event-processor.ts index 77b5383c050..e37199d407c 100644 --- a/packages/geoview-core/src/api/event-processors/event-processor-children/time-slider-event-processor.ts +++ b/packages/geoview-core/src/api/event-processors/event-processor-children/time-slider-event-processor.ts @@ -6,7 +6,6 @@ import { } from '@/core/stores/store-interface-and-intial-values/time-slider-state'; import { WMS } from '@/geo/layer/geoview-layers/raster/wms'; import { TypeFeatureInfoLayerConfig, TypeLayerEntryConfig, layerEntryIsGroupLayer } from '@/geo/map/map-schema-types'; -import { EsriImage } from '@/geo/layer/geoview-layers/raster/esri-image'; import { MapEventProcessor } from './map-event-processor'; import { UIEventProcessor } from './ui-event-processor'; import { GVWMS } from '@/geo/layer/gv-layers/raster/gv-wms'; @@ -128,7 +127,7 @@ export class TimeSliderEventProcessor extends AbstractEventProcessor { if (layerEntryIsGroupLayer(layerConfig)) return undefined; // Cast the layer - const geoviewLayerCasted = geoviewLayer as AbstractGVLayer | AbstractGVLayer; + const geoviewLayerCasted = geoviewLayer as AbstractGVLayer; // Get the temporal dimension information const temporalDimensionInfo = geoviewLayerCasted.getTemporalDimension(); @@ -268,7 +267,7 @@ export class TimeSliderEventProcessor extends AbstractEventProcessor { } else { filter = `${field}=date '${defaultValue}'`; } - } else if (geoviewLayer instanceof EsriImage || geoviewLayer instanceof GVEsriImage) { + } else if (geoviewLayer instanceof GVEsriImage) { if (filtering) { filter = `time=${minAndMax[0]},${values[0]}`; } else { diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts b/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts index bd6cb2fe4b6..9a1e6e19fd6 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts @@ -3,23 +3,13 @@ // TODO: refactor eslint - we have few files with many reassing should wee if we can build better... import BaseLayer from 'ol/layer/Base'; import Collection from 'ol/Collection'; -import { Coordinate } from 'ol/coordinate'; -import { Pixel } from 'ol/pixel'; -import { Extent } from 'ol/extent'; import LayerGroup, { Options as LayerGroupOptions } from 'ol/layer/Group'; -import Feature from 'ol/Feature'; import Source from 'ol/source/Source'; -import { shared as iconImageCache } from 'ol/style/IconImageCache'; - -import { TypeOutfieldsType } from '@config/types/map-schema-types'; import { generateId, getXMLHttpRequest, isJsonString, whenThisThen } from '@/core/utils/utilities'; import { TypeJsonObject, toJsonObject } from '@/core/types/global-types'; import { TimeDimension, TypeDateFragments, DateMgt } from '@/core/utils/date-mgt'; import { logger } from '@/core/utils/logger'; -import { EsriDynamicLayerEntryConfig } from '@/core/utils/config/validation-classes/raster-validation-classes/esri-dynamic-layer-entry-config'; -import { OgcWmsLayerEntryConfig } from '@/core/utils/config/validation-classes/raster-validation-classes/ogc-wms-layer-entry-config'; -import { VectorLayerEntryConfig } from '@/core/utils/config/validation-classes/vector-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 EventHelper, { EventDelegateBase } from '@/api/events/event-helper'; @@ -33,14 +23,8 @@ import { TypeLayerStatus, TypeStyleGeometry, CONST_LAYER_ENTRY_TYPES, - TypeFeatureInfoEntry, - codedValueType, - rangeDomainType, - TypeLocation, - QueryType, } from '@/geo/map/map-schema-types'; import { GeoViewLayerCreatedTwiceError } from '@/geo/layer/exceptions/layer-exceptions'; -import { getLegendStyles, getFeatureCanvas } from '@/geo/utils/renderer/geoview-renderer'; import { ConfigBaseClass } from '@/core/utils/config/validation-classes/config-base-class'; import { TypeLegend } from '@/core/stores/store-interface-and-intial-values/layer-state'; import { MapViewer } from '@/geo/map/map-viewer'; @@ -112,9 +96,6 @@ export abstract class AbstractGeoViewLayer { /** The service metadata. */ metadata: TypeJsonObject | null = null; - /** Layer name */ - #layerName: Record = {}; - /** Layer metadata */ #layerMetadata: Record = {}; @@ -136,21 +117,9 @@ export abstract class AbstractGeoViewLayer { /** Boolean indicating if the layer should be included in time awareness functions such as the Time Slider. True by default. */ #isTimeAware: boolean = true; - // Keep all callback delegates references - #onLayerNameChangedHandlers: LayerNameChangedDelegate[] = []; - // Keep all callback delegates references #onLayerStyleChangedHandlers: LayerStyleChangedDelegate[] = []; - // Keep all callback delegate references - #onLegendQueryingHandlers: LegendQueryingDelegate[] = []; - - // Keep all callback delegate references - #onLegendQueriedHandlers: LegendQueriedDelegate[] = []; - - // Keep all callback delegate references - #onVisibleChangedHandlers: VisibleChangedDelegate[] = []; - // Keep all callback delegate references #onLayerEntryProcessedHandlers: LayerEntryProcessedDelegate[] = []; @@ -160,12 +129,6 @@ export abstract class AbstractGeoViewLayer { // Keep all callback delegate references #onLayerCreationHandlers: LayerCreationDelegate[] = []; - // Keep all callback delegate references - #onLayerFilterAppliedHandlers: LayerFilterAppliedDelegate[] = []; - - // Keep all callback delegate references - #onLayerOpacityChangedHandlers: LayerOpacityChangedDelegate[] = []; - // Keep all callback delegates references #onIndividualLayerLoadedHandlers: IndividualLayerLoadedDelegate[] = []; @@ -252,22 +215,6 @@ export abstract class AbstractGeoViewLayer { return this.getMapViewer().layer.getOLLayer(layerPath); } - /** *************************************************************************************************************************** - * Gets the Geoview layer id. - * @returns {string} The geoview layer id - */ - getGeoviewLayerId(): string { - return this.geoviewLayerId; - } - - /** *************************************************************************************************************************** - * Gets the Geoview layer name. - * @returns {string | undefined} The geoview layer name - */ - getGeoviewLayerName(): string | undefined { - return this.geoviewLayerName; - } - /** * Gets the layer status * @returns The layer status @@ -277,28 +224,6 @@ export abstract class AbstractGeoViewLayer { return this.getLayerConfig(layerPath)!.layerStatus; } - /** *************************************************************************************************************************** - * Gets the layer name. - * @returns {string | undefined} The geoview layer name - */ - getLayerName(layerPath: string): string | undefined { - // If a new layer name is set - if (this.#layerName[layerPath]) return this.#layerName[layerPath]; - // TODO: Refactor - Temporary patch until configs refactoring is done, the style should have been set already - // Take name from config - return this.getLayerConfig(layerPath)?.layerName; - } - - /** *************************************************************************************************************************** - * Sets the layer name. - * @param {string} layerPath The layer path. - * @param {string} name The layer name. - */ - setLayerName(layerPath: string, name: string | undefined): void { - this.#layerName[layerPath] = name; - this.#emitLayerNameChanged({ layerPath, layerName: name }); - } - /** * Gets the layer style * @returns The layer style @@ -719,244 +644,6 @@ export abstract class AbstractGeoViewLayer { return Promise.resolve(undefined); } - /** *************************************************************************************************************************** - * Return feature information for the layer specified. - * - * @param {QueryType} queryType The type of query to perform. - * @param {string} layerPath The layer path to the layer's configuration. - * @param {TypeLocation} location An optionsl pixel, coordinate or polygon that will be used by the query. - * - * @returns {Promise} The feature info table. - */ - // GV Things important to know about the get feature info family of methods - /* - * There's no doubt that the layerConfig is correctly defined when we call these methods. The layerConfig object is created in - * the GeoView layer constructor and has all the necessary flags to inform programmers and users whether the layer referenced by - * a layerConfig or its layerPath is viable or not. If the layer is not visible on the map, it has probably not yet been loaded - * or an error has occurred. If clicked on, these layers will return an empty array, as they have no features on the map. So - * users can't expect anything to be returned after a click. They have to wait until they see something on the map to know where - * the features are so they can click on them. - */ - async getFeatureInfo( - queryType: QueryType, - layerPath: string, - location: TypeLocation = null - ): Promise { - try { - // TODO: Refactor - Remove the layerPath parameter once hybrid work is done - // Get the layer config - const layerConfig = this.getLayerConfig(layerPath) as AbstractBaseLayerEntryConfig; - - if (!layerConfig?.source?.featureInfo?.queryable) { - logger.logError(`Layer at path ${layerConfig.layerPath} is not queryable`); - return null; - } - - // Log - logger.logTraceCore('ABSTRACT-GEOVIEW-LAYERS - getFeatureInfo', queryType, layerPath); - const logMarkerKey = `${queryType} | ${layerPath}`; - logger.logMarkerStart(logMarkerKey); - - let promiseGetFeature: Promise; - switch (queryType) { - case 'all': - promiseGetFeature = this.getAllFeatureInfo(layerPath); - break; - case 'at_pixel': - promiseGetFeature = this.getFeatureInfoAtPixel(location as Pixel, layerPath); - break; - case 'at_coordinate': - promiseGetFeature = this.getFeatureInfoAtCoordinate(location as Coordinate, layerPath); - break; - case 'at_long_lat': - promiseGetFeature = this.getFeatureInfoAtLongLat(location as Coordinate, layerPath); - break; - case 'using_a_bounding_box': - promiseGetFeature = this.getFeatureInfoUsingBBox(location as Coordinate[], layerPath); - break; - case 'using_a_polygon': - promiseGetFeature = this.getFeatureInfoUsingPolygon(location as Coordinate[], layerPath); - break; - default: - // Default is empty array - promiseGetFeature = Promise.resolve([]); - - // Log - logger.logError(`Queries using ${queryType} are invalid.`); - } - - // Wait for results - const arrayOfFeatureInfoEntries = await promiseGetFeature; - - // Log - logger.logMarkerCheck(logMarkerKey, 'to getFeatureInfo', arrayOfFeatureInfoEntries); - - // Return the result - return arrayOfFeatureInfoEntries; - } catch (error) { - // Log - logger.logError(error); - return null; - } - } - - /** *************************************************************************************************************************** - * Return feature information for all the features on a layer. Returns an empty array [] when the layer is - * not queryable. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this - protected getAllFeatureInfo(layerPath: string): Promise { - // Log - logger.logError(`getAllFeatureInfo is not implemented! for ${layerPath}`); - return Promise.resolve(null); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided Pixel. Returns an empty array [] when the layer is - * not queryable. - * - * @param {Coordinate} location The pixel coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this - protected getFeatureInfoAtPixel(location: Pixel, layerPath: string): Promise { - // Log - logger.logError(`getFeatureInfoAtPixel is not implemented! for ${layerPath} - ${location}`); - return Promise.resolve(null); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided coordinate. Returns an empty array [] when the layer is - * not queryable. - * - * @param {Coordinate} location The coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this - protected getFeatureInfoAtCoordinate(location: Coordinate, layerPath: string): Promise { - // Log - logger.logError(`getFeatureInfoAtCoordinate is not implemented! for ${layerPath} - ${location}`); - return Promise.resolve(null); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided longitude latitude. Returns an empty array [] when the - * layer is not queryable. - * - * @param {Coordinate} location The coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this - protected getFeatureInfoAtLongLat(location: Coordinate, layerPath: string): Promise { - // Log - logger.logError(`getFeatureInfoAtLongLat is not implemented for ${layerPath} - ${location}!`); - return Promise.resolve(null); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features in the provided bounding box. Returns an empty array [] when the layer is - * not queryable. - * - * @param {Coordinate} location The coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this - protected getFeatureInfoUsingBBox(location: Coordinate[], layerPath: string): Promise { - // Log - logger.logError(`getFeatureInfoUsingBBox is not implemented! for ${layerPath} - ${location}`); - return Promise.resolve(null); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features in the provided polygon. Returns an empty array [] when the layer is - * not queryable. - * - * @param {Coordinate} location The coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this - protected getFeatureInfoUsingPolygon(location: Coordinate[], layerPath: string): Promise { - // Log - logger.logError(`getFeatureInfoUsingPolygon is not implemented! for ${layerPath} - ${location}`); - return Promise.resolve(null); - } - - /** - * Queries the legend. - * This function raises legend querying and queried events. - * @returns {Promise} The promise when the legend (or null) will be received - */ - queryLegend(layerPath: string): Promise { - // Emit that the legend has been queried - this.#emitLegendQuerying({ layerPath }); - - // Get the legend - const promiseLegend = this.getLegend(layerPath); - - // Whenever the promise resolves - promiseLegend - .then((legend) => { - // If legend was received - if (legend) { - // Check for possible number of icons and set icon cache size - this.updateIconImageCache(legend); - // Emit legend information once retrieved - this.#emitLegendQueried({ layerPath, legend }); - } - }) - .catch((error) => { - // Log - logger.logPromiseFailed('promiseLegend in queryLegend in AbstractGeoviewLayer', error); - }); - - // Return the promise - return promiseLegend; - } - - /** - * Update the size of the icon image list based on styles. - * @param {TypeLegend} legend - The legend to check. - */ - updateIconImageCache(legend: TypeLegend): void { - // GV This will need to be revised if functionality to add additional icons to a layer is added - let styleCount = this.getMapViewer().iconImageCacheSize; - if (legend.styleConfig) - Object.keys(legend.styleConfig).forEach((geometry) => { - if ( - legend.styleConfig && - (legend.styleConfig[geometry as TypeStyleGeometry]?.type === 'uniqueValue' || - legend.styleConfig[geometry as TypeStyleGeometry]?.type === 'classBreaks') - ) { - if (legend.styleConfig[geometry as TypeStyleGeometry]!.info.length) - styleCount += legend.styleConfig[geometry as TypeStyleGeometry]!.info.length; - } - }); - // Set the openlayers icon image cache - iconImageCache.setSize(styleCount); - // Update the cache size for the map viewer - this.getMapViewer().iconImageCacheSize = styleCount; - } - /** *************************************************************************************************************************** * Creates a layer group. * @param {TypeLayerEntryConfig} layerConfig The layer configuration. @@ -983,392 +670,6 @@ export abstract class AbstractGeoViewLayer { return layerGroup; } - // TODO: Obsolete - Delete? Commenting out for now - // /** *************************************************************************************************************************** - // * Returns the layer bounds or undefined if not defined in the layer configuration or the metadata. If projectionCode is - // * defined, returns the bounds in the specified projection otherwise use the map projection. The bounds are different from the - // * extent. They are mainly used for display purposes to show the bounding box in which the data resides and to zoom in on the - // * entire layer data. It is not used by openlayer to limit the display of data on the map. - // * - // * @param {string} layerPath The layer path to the layer's configuration. - // * @param {string | number | undefined} projectionCode Optional projection code to use for the returned bounds. - // * - // * @returns {Extent} The layer bounding box. - // */ - // getMetadataBounds(layerPath: string, projectionCode: string | number | undefined = undefined): Extent | undefined { - // let bounds: Extent | undefined; - // const processGroupLayerBounds = (listOfLayerEntryConfig: ConfigBaseClass[]): void => { - // listOfLayerEntryConfig.forEach((layerConfig) => { - // if (layerEntryIsGroupLayer(layerConfig)) processGroupLayerBounds(layerConfig.listOfLayerEntryConfig); - // else if (layerConfig.initialSettings?.bounds) { - // if (!bounds) - // bounds = [ - // layerConfig.initialSettings.bounds[0], - // layerConfig.initialSettings.bounds[1], - // layerConfig.initialSettings.bounds[2], - // layerConfig.initialSettings.bounds[3], - // ]; - // else - // bounds = [ - // Math.min(layerConfig.initialSettings.bounds[0], bounds[0]), - // Math.min(layerConfig.initialSettings.bounds[1], bounds[1]), - // Math.max(layerConfig.initialSettings.bounds[2], bounds[2]), - // Math.max(layerConfig.initialSettings.bounds[3], bounds[3]), - // ]; - // } - // }); - // }; - // // GV The following code will need to be modified when the topmost layer of a GeoView - // // GV layer creates dynamicaly a group out of a list of layers. - // const layerConfig: ConfigBaseClass | ConfigBaseClass[] | undefined = layerPath.includes('/') - // ? this.getLayerConfig(layerPath) - // : this.listOfLayerEntryConfig; - // if (layerConfig) { - // if (Array.isArray(layerConfig)) processGroupLayerBounds(layerConfig); - // else processGroupLayerBounds([layerConfig]); - - // // TODO: Check - Are the bounds initially always 4326? - // if (projectionCode && bounds) { - // bounds = validateExtent(bounds); - // return Projection.transformExtentFromProj(bounds, Projection.PROJECTION_NAMES.LNGLAT, `EPSG:${projectionCode}`); - // } - // } - // return bounds; - // } - - /** *************************************************************************************************************************** - * Returns the domain of the specified field or null if the field has no domain. - * - * @param {string} fieldName field name for which we want to get the domain. - * @param {TypeLayerEntryConfig} layerConfig layer configuration. - * - * @returns {null | codedValueType | rangeDomainType} The domain of the field. - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this, @typescript-eslint/no-unused-vars - protected getFieldDomain(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): null | codedValueType | rangeDomainType { - // Log - REMOVED as it is trigger for every row of data table, just enable for debuggin purpose - // logger.logWarning(`getFieldDomain is not implemented for ${fieldName} - ${layerConfig}`); - return null; - } - - /** *************************************************************************************************************************** - * Extract the type of the specified field from the metadata. If the type can not be found, return 'string'. - * - * @param {string} fieldName field name for which we want to get the type. - * @param {TypeLayerEntryConfig} layerConfig layer configuration. - * - * @returns {TypeOutfieldsType} The type of the field. - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this - protected getFieldType(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): TypeOutfieldsType { - // Log - logger.logWarning(`getFieldType is not implemented for ${fieldName} - ${layerConfig}`); - return 'string'; - } - - /** *************************************************************************************************************************** - * Return the extent of the layer or undefined if it will be visible regardless of extent. The layer extent is an array of - * numbers representing an extent: [minx, miny, maxx, maxy]. This routine return undefined when the layer path can't be found. - * The extent is used to clip the data displayed on the map. - * - * @param {string} layerPath Layer path to the layer's configuration. - * - * @returns {Extent | undefined} The layer extent. - */ - getExtent(layerPath: string): Extent | undefined { - const olLayer = this.getOLLayer(layerPath); - return olLayer?.getExtent(); - } - - /** *************************************************************************************************************************** - * set the extent of the layer. Use undefined if it will be visible regardless of extent. The layer extent is an array of - * numbers representing an extent: [minx, miny, maxx, maxy]. This routine does nothing when the layerPath specified is not - * found. - * - * @param {Extent} layerExtent The extent to assign to the layer. - * @param {string} layerPath The layer path to the layer's configuration. - */ - setExtent(layerExtent: Extent, layerPath: string): void { - const olLayer = this.getOLLayer(layerPath); - if (olLayer) olLayer.setExtent(layerExtent); - } - - /** *************************************************************************************************************************** - * Return the opacity of the layer (between 0 and 1). This routine return undefined when the layerPath specified is not found. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {number | undefined} The opacity of the layer. - */ - getOpacity(layerPath: string): number | undefined { - const olLayer = this.getOLLayer(layerPath); - return olLayer?.getOpacity(); - } - - /** *************************************************************************************************************************** - * Set the opacity of the layer (between 0 and 1). This routine does nothing when the layerPath specified is not found. - * - * @param {number} layerOpacity The opacity of the layer. - * @param {string} layerPath The layer path to the layer's configuration. - * - */ - setOpacity(layerOpacity: number, layerPath: string): void { - const olLayer = this.getOLLayer(layerPath); - if (olLayer) { - olLayer.setOpacity(layerOpacity); - this.#emitLayerOpacityChanged({ layerPath, opacity: layerOpacity }); - } - } - - /** *************************************************************************************************************************** - * Return the visibility of the layer (true or false). This routine return undefined when the layerPath specified is not found. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {boolean | undefined} The visibility of the layer. - */ - getVisible(layerPath: string): boolean | undefined { - const olLayer = this.getOLLayer(layerPath); - return olLayer?.getVisible(); - } - - /** *************************************************************************************************************************** - * Set the visibility of the layer (true or false). This routine does nothing when the layerPath specified is not found. - * - * @param {boolean} layerVisibility The visibility of the layer. - * @param {string} layerPath The layer path to the layer's configuration. - */ - setVisible(layerVisibility: boolean, layerPath: string): void { - const olLayer = this.getOLLayer(layerPath); - if (olLayer) { - const curVisible = this.getVisible(layerPath); - olLayer.setVisible(layerVisibility); - // olLayer.changed(); - if (layerVisibility !== curVisible) this.#emitVisibleChanged({ layerPath, visible: layerVisibility }); - } - } - - /** *************************************************************************************************************************** - * Return the min zoom of the layer. This routine return undefined when the layerPath specified is not found. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {number | undefined} The min zoom of the layer. - */ - getMinZoom(layerPath: string): number | undefined { - const olLayer = this.getOLLayer(layerPath); - return olLayer?.getMinZoom(); - } - - /** *************************************************************************************************************************** - * Set the min zoom of the layer. This routine does nothing when the layerPath specified is not found. - * - * @param {boolean} layerVisibility The min zoom of the layer. - * @param {string} layerPath The layer path to the layer's configuration. - */ - setMinZoom(minZoom: number, layerPath: string): void { - const olLayer = this.getOLLayer(layerPath); - if (olLayer) olLayer.setMinZoom(minZoom); - } - - /** *************************************************************************************************************************** - * Return the max zoom of the layer. This routine return undefined when the layerPath specified is not found. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {number | undefined} The max zoom of the layer. - */ - getMaxZoom(layerPath: string): number | undefined { - const olLayer = this.getOLLayer(layerPath); - return olLayer?.getMaxZoom(); - } - - /** *************************************************************************************************************************** - * Set the max zoom of the layer. This routine does nothing when the layerPath specified is not found. - * - * @param {boolean} layerVisibility The max zoom of the layer. - * @param {string} layerPath The layer path to the layer's configuration. - */ - setMaxZoom(maxZoom: number, layerPath: string): void { - const olLayer = this.getOLLayer(layerPath); - if (olLayer) olLayer.setMaxZoom(maxZoom); - } - - /** *************************************************************************************************************************** - * Overridable function returning the legend of the layer. Returns null when the layerPath specified is not found. If the style property - * of the layerConfig object is undefined, the legend property of the object returned will be null. - * @param {string} layerPath The layer path to the layer's configuration. - * @returns {Promise} The legend of the layer. - */ - async getLegend(layerPath: string): Promise { - try { - // Get the legend using the layer information and layer styling - const legend: TypeLegend = { - type: this.type, - styleConfig: this.getStyle(layerPath), - legend: await getLegendStyles(this.getStyle(layerPath)), - }; - return legend; - } catch (error) { - // Log - logger.logError(error); - return null; - } - } - - /** *************************************************************************************************************************** - * Get and format the value of the field with the name passed in parameter. Vector GeoView layers convert dates to milliseconds - * since the base date. Vector feature dates must be in ISO format. - * - * @param {Feature} feature - The features that hold the field values. - * @param {string} fieldName - The field name. - * @param {'number' | 'string' | 'date'} fieldType - The field type. - * - * @returns {string | number | Date} The formatted value of the field. - */ - protected getFieldValue(feature: Feature, fieldName: string, fieldType: TypeOutfieldsType): string | number | Date { - const fieldValue = feature.get(fieldName); - let returnValue: string | number | Date; - if (fieldType === 'date') { - if (typeof fieldValue === 'string') { - if (!this.serverDateFragmentsOrder) - this.serverDateFragmentsOrder = DateMgt.getDateFragmentsOrder(DateMgt.deduceDateFormat(fieldValue)); - returnValue = DateMgt.applyInputDateFormat(fieldValue, this.serverDateFragmentsOrder); - } else { - // All vector dates are kept internally in UTC. - returnValue = DateMgt.convertToUTC(`${DateMgt.convertMilisecondsToDate(fieldValue)}Z`); - } - const reverseTimeZone = true; - if (this.externalFragmentsOrder) - returnValue = DateMgt.applyOutputDateFormat(returnValue, this.externalFragmentsOrder, reverseTimeZone); - return returnValue; - } - return fieldValue; - } - - /** *************************************************************************************************************************** - * Convert the feature information to an array of TypeFeatureInfoEntry[] | undefined | null. - * - * @param {Feature[]} features The array of features to convert. - * @param {ImageLayerEntryConfig | VectorLayerEntryConfig} layerConfig The layer configuration. - * - * @returns {Promise} The Array of feature information. - */ - protected async formatFeatureInfoResult( - features: Feature[], - layerConfig: OgcWmsLayerEntryConfig | EsriDynamicLayerEntryConfig | VectorLayerEntryConfig - ): Promise { - try { - if (!features.length) return []; - - const featureInfo = layerConfig?.source?.featureInfo; - - // Loop on the features to build the array holding the promises for their canvas - const promisedAllCanvasFound: Promise<{ feature: Feature; canvas: HTMLCanvasElement }>[] = []; - features.forEach((featureNeedingItsCanvas) => { - promisedAllCanvasFound.push( - new Promise((resolveCanvas) => { - // GV: Call the function with layerConfig.legendFilterIsOff = true to force the feature to get is canvas - // GV: If we don't, it will create canvas only for visible elements and because tables are stored feature will never get its canvas - getFeatureCanvas(featureNeedingItsCanvas, this.getStyle(layerConfig.layerPath)!, layerConfig.filterEquation, true, true) - .then((canvas) => { - resolveCanvas({ feature: featureNeedingItsCanvas, canvas }); - }) - .catch((error) => { - // Log - logger.logPromiseFailed( - 'getFeatureCanvas in featureNeedingItsCanvas loop in formatFeatureInfoResult in AbstractGeoViewLayer', - error - ); - }); - }) - ); - }); - - // Loop on the promised feature infos - let featureKeyCounter = 0; - let fieldKeyCounter = 0; - const queryResult: TypeFeatureInfoEntry[] = []; - const arrayOfFeatureInfo = await Promise.all(promisedAllCanvasFound); - arrayOfFeatureInfo.forEach(({ feature, canvas }) => { - let extent; - if (feature.getGeometry()) extent = feature.getGeometry()!.getExtent(); - - const featureInfoEntry: TypeFeatureInfoEntry = { - // feature key for building the data-grid - featureKey: featureKeyCounter++, - geoviewLayerType: this.type, - extent, - geometry: feature, - featureIcon: canvas, - fieldInfo: {}, - nameField: layerConfig?.source?.featureInfo?.nameField || null, - }; - - const featureFields = feature.getKeys(); - featureFields.forEach((fieldName) => { - if (fieldName !== 'geometry') { - const currentOutfield = featureInfo?.outfields?.length - ? featureInfo.outfields.find((outfield) => outfield.name === fieldName) - : undefined; - - // Calculate the field domain if not already calculated - const fieldDomain = currentOutfield?.domain || this.getFieldDomain(fieldName, layerConfig); - - // Calculate the field type if not already calculated - const fieldType = currentOutfield?.type || this.getFieldType(fieldName, layerConfig); - - featureInfoEntry.fieldInfo[fieldName] = { - fieldKey: fieldKeyCounter++, - value: this.getFieldValue(feature, fieldName, fieldType), - dataType: fieldType, - alias: currentOutfield?.alias || fieldName, - domain: fieldDomain, - }; - } - }); - queryResult.push(featureInfoEntry); - }); - return queryResult; - } catch (error) { - // Log - logger.logError(error); - return []; - } - } - - /** *************************************************************************************************************************** - * Get the layerFilter that is associated to the layer. Returns undefined when the layer config can't be found using the layer - * path. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {string | undefined} The filter associated to the layer or undefined. - */ - getLayerFilter(layerPath: string): string | undefined { - const layerConfig = this.getLayerConfig(layerPath); - // TODO: Refactor to put the 'layerFilter' at the right place. Meanwhile, using `any` here - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (layerConfig as any)?.layerFilter; - } - - /** - * Overridable function called when the layer gets in loaded status. - * @param layerConfig - The layer configuration - */ - onLoaded(layerConfig: AbstractBaseLayerEntryConfig): void { - // Set loaded - layerConfig.layerStatus = 'loaded'; - - // Set visibility - this.setVisible(layerConfig.initialSettings?.states?.visible !== false, layerConfig.layerPath); - - // Emit event - this.#emitIndividualLayerLoaded({ layerPath: layerConfig.layerPath }); - } - /** * Overridable function called when the layer gets in error status. * @param layerConfig - The layer configuration @@ -1380,29 +681,6 @@ export abstract class AbstractGeoViewLayer { layerConfig.layerStatus = 'error'; } - /** *************************************************************************************************************************** - * Get the bounds of the layer represented in the layerConfig pointed to by the layerPath, returns updated bounds - * - * @param {string} layerPath The Layer path to the layer's configuration. - * - * @returns {Extent} The new layer bounding box. - */ - abstract getBounds(layerPath: string): Extent | undefined; - - /** - * Overridable function that gets the extent of an array of features. - * @param {string} layerPath - The layer path - * @param {string[]} objectIds - The IDs of features to get extents from. - * @param {string} outfield - ID field to return for services that require a value in outfields. - * @returns {Promise} The extent of the features, if available - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this, @typescript-eslint/no-unused-vars - getExtentFromFeatures(layerPath: string, objectIds: string[], outfield?: string): Promise { - logger.logError(`Feature geometry for ${objectIds} is unavailable from ${layerPath}`); - return Promise.resolve(undefined); - } - /** *************************************************************************************************************************** * Set the layerStatus code of all layers in the listOfLayerEntryConfig. * @@ -1496,118 +774,6 @@ export abstract class AbstractGeoViewLayer { // #region EVENTS - /** - * Emits an event to all handlers. - * @param {LayerNameChangedEvent} event - The event to emit - * @private - */ - #emitLayerNameChanged(event: LayerNameChangedEvent): void { - // Emit the event for all handlers - EventHelper.emitEvent(this, this.#onLayerNameChangedHandlers, event); - } - - /** - * Registers a layer name changed event handler. - * @param {LayerNameChangedDelegate} callback - The callback to be executed whenever the event is emitted - */ - onLayerNameChanged(callback: LayerNameChangedDelegate): void { - // Register the event handler - EventHelper.onEvent(this.#onLayerNameChangedHandlers, callback); - } - - /** - * Unregisters a layer name changed event handler. - * @param {LayerNameChangedDelegate} callback - The callback to stop being called whenever the event is emitted - */ - offLayerNameChanged(callback: LayerNameChangedDelegate): void { - // Unregister the event handler - EventHelper.offEvent(this.#onLayerNameChangedHandlers, callback); - } - - /** - * Emits an event to all handlers. - * @param {LegendQueryingEvent} event The event to emit - * @private - */ - #emitLegendQuerying(event: LegendQueryingEvent): void { - // Emit the event for all handlers - EventHelper.emitEvent(this, this.#onLegendQueryingHandlers, event); - } - - /** - * Registers a legend querying event handler. - * @param {LegendQueryingDelegate} callback The callback to be executed whenever the event is emitted - */ - onLegendQuerying(callback: LegendQueryingDelegate): void { - // Register the event handler - EventHelper.onEvent(this.#onLegendQueryingHandlers, callback); - } - - /** - * Unregisters a legend querying event handler. - * @param {LegendQueryingDelegate} callback The callback to stop being called whenever the event is emitted - */ - offLegendQuerying(callback: LegendQueryingDelegate): void { - // Unregister the event handler - EventHelper.offEvent(this.#onLegendQueryingHandlers, callback); - } - - /** - * Emits an event to all handlers. - * @param {LegendQueriedEvent} event The event to emit - * @private - */ - #emitLegendQueried(event: LegendQueriedEvent): void { - // Emit the event for all handlers - EventHelper.emitEvent(this, this.#onLegendQueriedHandlers, event); - } - - /** - * Registers a legend queried event handler. - * @param {LegendQueriedDelegate} callback The callback to be executed whenever the event is emitted - */ - onLegendQueried(callback: LegendQueriedDelegate): void { - // Register the event handler - EventHelper.onEvent(this.#onLegendQueriedHandlers, callback); - } - - /** - * Unregisters a legend queried event handler. - * @param {LegendQueriedDelegate} callback The callback to stop being called whenever the event is emitted - */ - offLegendQueried(callback: LegendQueriedDelegate): void { - // Unregister the event handler - EventHelper.offEvent(this.#onLegendQueriedHandlers, callback); - } - - /** - * Emits an event to all handlers. - * @param {VisibleChangedEvent} event The event to emit - * @private - */ - #emitVisibleChanged(event: VisibleChangedEvent): void { - // Emit the event for all handlers - EventHelper.emitEvent(this, this.#onVisibleChangedHandlers, event); - } - - /** - * Registers a visible changed event handler. - * @param {VisibleChangedDelegate} callback The callback to be executed whenever the event is emitted - */ - onVisibleChanged(callback: VisibleChangedDelegate): void { - // Register the event handler - EventHelper.onEvent(this.#onVisibleChangedHandlers, callback); - } - - /** - * Unregisters a visible changed event handler. - * @param {VisibleChangedDelegate} callback The callback to stop being called whenever the event is emitted - */ - offVisibleChanged(callback: VisibleChangedDelegate): void { - // Unregister the event handler - EventHelper.offEvent(this.#onVisibleChangedHandlers, callback); - } - /** * Emits an event to all handlers. * @param {LayerEntryProcessedEvent} event The event to emit @@ -1692,34 +858,6 @@ export abstract class AbstractGeoViewLayer { EventHelper.offEvent(this.#onLayerCreationHandlers, callback); } - /** - * Emits filter applied event. - * @param {FilterAppliedEvent} event - The event to emit - * @private - */ - protected emitLayerFilterApplied(event: LayerFilterAppliedEvent): void { - // Emit the event for all handlers - EventHelper.emitEvent(this, this.#onLayerFilterAppliedHandlers, event); - } - - /** - * Registers a filter applied event handler. - * @param {FilterAppliedDelegate} callback - The callback to be executed whenever the event is emitted - */ - onLayerFilterApplied(callback: LayerFilterAppliedDelegate): void { - // Register the event handler - EventHelper.onEvent(this.#onLayerFilterAppliedHandlers, callback); - } - - /** - * Unregisters a filter applied event handler. - * @param {FilterAppliedDelegate} callback - The callback to stop being called whenever the event is emitted - */ - offLayerFilterApplied(callback: LayerFilterAppliedDelegate): void { - // Unregister the event handler - EventHelper.offEvent(this.#onLayerFilterAppliedHandlers, callback); - } - /** * Emits an event to all handlers. * @param {LayerStyleChangedEvent} event - The event to emit @@ -1747,44 +885,6 @@ export abstract class AbstractGeoViewLayer { EventHelper.offEvent(this.#onLayerStyleChangedHandlers, callback); } - /** - * Emits opacity changed event. - * @param {LayerOpacityChangedEvent} event - The event to emit - * @private - */ - #emitLayerOpacityChanged(event: LayerOpacityChangedEvent): void { - // Emit the event for all handlers - EventHelper.emitEvent(this, this.#onLayerOpacityChangedHandlers, event); - } - - /** - * Registers an opacity changed event handler. - * @param {LayerOpacityChangedDelegate} callback - The callback to be executed whenever the event is emitted - */ - onLayerOpacityChanged(callback: LayerOpacityChangedDelegate): void { - // Register the event handler - EventHelper.onEvent(this.#onLayerOpacityChangedHandlers, callback); - } - - /** - * Unregisters an opacity changed event handler. - * @param {LayerOpacityChangedDelegate} callback - The callback to stop being called whenever the event is emitted - */ - offLayerOpacityChanged(callback: LayerOpacityChangedDelegate): void { - // Unregister the event handler - EventHelper.offEvent(this.#onLayerOpacityChangedHandlers, callback); - } - - /** - * Emits an event to all handlers when the layer's features have been loaded on the map. - * @param {IndividualLayerLoadedEvent} event - The event to emit - * @private - */ - #emitIndividualLayerLoaded(event: IndividualLayerLoadedEvent): void { - // Emit the event for all handlers - EventHelper.emitEvent(this, this.#onIndividualLayerLoadedHandlers, event); - } - /** * Registers an individual layer loaded event handler. * @param {IndividualLayerLoadedDelegate} callback - The callback to be executed whenever the event is emitted @@ -1813,11 +913,6 @@ export type LegendQueryingEvent = { layerPath: string; }; -/** - * Define a delegate for the event handler function signature - */ -type LegendQueryingDelegate = EventDelegateBase; - /** * Define an event for the delegate */ @@ -1826,11 +921,6 @@ export type LegendQueriedEvent = { legend: TypeLegend; }; -/** - * Define a delegate for the event handler function signature - */ -type LegendQueriedDelegate = EventDelegateBase; - /** * Define an event for the delegate */ @@ -1839,11 +929,6 @@ export type VisibleChangedEvent = { visible: boolean; }; -/** - * Define a delegate for the event handler function signature - */ -type VisibleChangedDelegate = EventDelegateBase; - /** * Define an event for the delegate */ @@ -1893,52 +978,6 @@ export interface TypeWmsLegendStyle { legend: HTMLCanvasElement | null; } -/** - * Define a delegate for the event handler function signature - */ -type LayerFilterAppliedDelegate = EventDelegateBase; - -/** - * Define an event for the delegate - */ -export type LayerFilterAppliedEvent = { - // The layer path of the affected layer - layerPath: string; - // The filter - filter: string; -}; - -/** - * Define a delegate for the event handler function signature. - */ -type LayerNameChangedDelegate = EventDelegateBase; - -/** - * Define an event for the delegate. - */ -export type LayerNameChangedEvent = { - // The new layer name. - layerName?: string; - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done - // The layer path. - layerPath: string; -}; - -/** - * Define a delegate for the event handler function signature - */ -type LayerOpacityChangedDelegate = EventDelegateBase; - -/** - * Define an event for the delegate - */ -export type LayerOpacityChangedEvent = { - // The layer path of the affected layer - layerPath: string; - // The filter - opacity: number; -}; - /** * Define a delegate for the event handler function signature */ diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts index 764be70ea2f..dee70d1c51d 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-dynamic.ts @@ -4,34 +4,14 @@ import { ImageArcGISRest } from 'ol/source'; import { Options as SourceOptions } from 'ol/source/ImageArcGISRest'; import BaseLayer from 'ol/layer/Base'; import { Image as ImageLayer } from 'ol/layer'; -import { Coordinate } from 'ol/coordinate'; -import { Pixel } from 'ol/pixel'; -import { EsriJSON } from 'ol/format'; -import { Extent } from 'ol/extent'; -import Feature from 'ol/Feature'; -import Geometry from 'ol/geom/Geometry'; 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 { validateExtent, getMinOrMaxExtents } from '@/geo/utils/utilities'; -import { Projection } from '@/geo/utils/projection'; import { TypeJsonObject } from '@/core/types/global-types'; -import { logger } from '@/core/utils/logger'; -import { DateMgt } from '@/core/utils/date-mgt'; import { EsriDynamicLayerEntryConfig } from '@/core/utils/config/validation-classes/raster-validation-classes/esri-dynamic-layer-entry-config'; -import { - TypeLayerEntryConfig, - TypeGeoviewLayerConfig, - TypeLayerStyleSettings, - TypeFeatureInfoLayerConfig, - codedValueType, - rangeDomainType, - TypeFeatureInfoEntry, -} from '@/geo/map/map-schema-types'; +import { TypeLayerEntryConfig, TypeGeoviewLayerConfig } from '@/geo/map/map-schema-types'; import { - commonGetFieldDomain, - commonGetFieldType, commonfetchServiceMetadata, commonProcessFeatureInfoConfig, commonProcessInitialSettings, @@ -40,10 +20,7 @@ import { commonValidateListOfLayerEntryConfig, } from '@/geo/layer/geoview-layers/esri-layer-common'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; -import { TypeOutfieldsType } from '@/api/config/types/map-schema-types'; - -type TypeFieldOfTheSameValue = { value: string | number | Date; nbOccurence: number }; -type TypeQueryTree = { fieldValue: string | number | Date; nextField: TypeQueryTree }[]; +import { logger } from '@/core/utils/logger'; // GV: CONFIG EXTRACTION // GV: This section of code was extracted and copied to the geoview config section @@ -173,32 +150,6 @@ export class EsriDynamic extends AbstractGeoViewRaster { return false; } - /** *************************************************************************************************************************** - * Extract the type of the specified field from the metadata. If the type can not be found, return 'string'. - * - * @param {string} fieldName field name for which we want to get the type. - * @param {AbstractBaseLayerEntryConfig} layerConfig layer configuration. - * - * @returns {TypeOutfieldsType} The type of the field. - */ - // GV Layers Refactoring - Obsolete (in config?) - protected override getFieldType(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): TypeOutfieldsType { - return commonGetFieldType(this, fieldName, layerConfig); - } - - /** *************************************************************************************************************************** - * Return the domain of the specified field. - * - * @param {string} fieldName field name for which we want to get the domain. - * @param {AbstractBaseLayerEntryConfig} layerConfig layer configuration. - * - * @returns {null | codedValueType | rangeDomainType} The domain of the field. - */ - // GV Layers Refactoring - Obsolete (in config?) - protected override getFieldDomain(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): null | codedValueType | rangeDomainType { - return commonGetFieldDomain(this, fieldName, layerConfig); - } - /** *************************************************************************************************************************** * This method will create a Geoview temporal dimension if it exist in the service metadata * @param {TypeJsonObject} esriTimeDimension The ESRI time dimension object @@ -292,704 +243,4 @@ export class EsriDynamic extends AbstractGeoViewRaster { return Promise.resolve(olLayer); } - - /** *************************************************************************************************************************** - * Returns feature information for all the features stored in the layer. - * @param {string} layerPath - The layer path to the layer's configuration. - * @returns {Promise} The feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override async getAllFeatureInfo(layerPath: string): Promise { - try { - // Get the layer config in a loaded phase - const layerConfig = this.getLayerConfig(layerPath)! as EsriDynamicLayerEntryConfig; - - // Fetch the features - let urlRoot = layerConfig.geoviewLayerConfig.metadataAccessPath!; - if (!urlRoot.endsWith('/')) urlRoot += '/'; - // GV: we put returnGeometry=false so on heavy geometry, dynamic layer can load datatable. If not the fetch fails. - const url = `${urlRoot}${layerConfig.layerId}/query?where=1=1&outFields=*&f=json&returnGeometry=false`; - - const response = await fetch(url); - const jsonResponse = await response.json(); - - // If any features - if (jsonResponse.features) { - // Parse the JSON response and create features - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const features = jsonResponse.features.map((featureData: any) => { - // We do not query the geometry anymore (set as undefine). It will query if needed by later - const properties = featureData.attributes; - return new Feature({ ...properties, undefined }); - }); - - // Check if there are additional features and get them - if (jsonResponse.exceededTransferLimit) { - // Get response json for additional features - const getAdditionalFeaturesArray = await this.#getAdditionalFeatures(layerConfig, url, features.length); - - // Parse them and add features - getAdditionalFeaturesArray.forEach((responseJson) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const additionalFeatures: Feature[] = (responseJson as any).features.map((featureData: any) => { - // We do not query the geometry anymore (set as undefine). It will query if needed by later - const properties = featureData.attributes; - return new Feature({ ...properties, undefined }); - }); - - features.push(...additionalFeatures); - }); - } - - // Format and return the result - // Not having geometry have an effect on the style as it use the geometry to define wich one to use - // The formatFeatureInfoResult (abstact-geoview-layer) / getFeatureCanvas (geoview-renderer) use geometry stored in style - return this.formatFeatureInfoResult(features, layerConfig); - } - - // Error - throw new Error('Error querying service. No features were returned.'); - } catch (error) { - // Log - logger.logError('esri-dynamic.getAllFeatureInfo()\n', error); - return null; - } - } - - /** *************************************************************************************************************************** - * Fetch additional features from service with a max record count. - * - * @param {AbstractBaseLayerEntryConfig} layerConfig - The layer entry configuration. - * @param {string} url - The base url for the service. - * @param {number} maxRecordCount - The max record count from the service. - * @param {number} resultOffset - The current offset to use for the features. - * @returns {Promise<[]>} An array of the response text for the features. - * @private - */ - async #getAdditionalFeatures( - layerConfig: AbstractBaseLayerEntryConfig, - url: string, - maxRecordCount: number, - resultOffset?: number - ): Promise { - const responseArray: unknown[] = []; - // Add resultOffset to layer query - const nextUrl = `${url}&resultOffset=${resultOffset || maxRecordCount}`; - try { - // Fetch response text and push to array - const response = await fetch(nextUrl); - const jsonResponse = await response.json(); - responseArray.push(jsonResponse); - // Check if there are additional features to fetch - if (jsonResponse.exceededTransferLimit) - responseArray.push( - ...(await this.#getAdditionalFeatures( - layerConfig, - url, - maxRecordCount, - resultOffset ? resultOffset + maxRecordCount : 2 * maxRecordCount - )) - ); - } catch (error) { - logger.logError(`Error loading additional features for ${layerConfig.layerPath} from ${nextUrl}`, error); - } - return responseArray; - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided Pixel. - * - * @param {Coordinate} location The pixel coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFeatureInfoAtPixel(location: Pixel, layerPath: string): Promise { - // Redirect to getFeatureInfoAtCoordinate - return this.getFeatureInfoAtCoordinate(this.getMapViewer().map.getCoordinateFromPixel(location), layerPath); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided projection coordinate. - * - * @param {Coordinate} location The coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The promised feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFeatureInfoAtCoordinate( - location: Coordinate, - layerPath: string - ): Promise { - // Transform coordinate from map project to lntlat - const projCoordinate = this.getMapViewer().convertCoordinateMapProjToLngLat(location); - - // Redirect to getFeatureInfoAtLongLat - return this.getFeatureInfoAtLongLat(projCoordinate, layerPath); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided coordinate. - * - * @param {Coordinate} lnglat The coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The promised feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override async getFeatureInfoAtLongLat( - lnglat: Coordinate, - layerPath: string - ): Promise { - try { - // If invisible - if (!this.getVisible(layerPath)) return []; - - // Get the layer config in a loaded phase - const layerConfig = this.getLayerConfig(layerPath) as EsriDynamicLayerEntryConfig; - const layer = this.getOLLayer(layerPath) as ImageLayer; - - // If not queryable - if (!layerConfig.source?.featureInfo?.queryable) return []; - - let identifyUrl = layerConfig.source?.dataAccessPath; - if (!identifyUrl) return []; - - // GV: We cannot directly use the view extent and reproject. If we do so some layers (issue #2413) identify will return empty resultset - // GV-CONT: This happen with max extent as initial extent and 3978 projection. If we use only the LL and UP corners for the repojection it works - const mapViewer = this.getMapViewer(); - const mapExtent = mapViewer.getView().calculateExtent(); - const boundsLL = mapViewer.convertCoordinateMapProjToLngLat([mapExtent[0], mapExtent[1]]); - const boundsUR = mapViewer.convertCoordinateMapProjToLngLat([mapExtent[2], mapExtent[3]]); - const extent = { xmin: boundsLL[0], ymin: boundsLL[1], xmax: boundsUR[0], ymax: boundsUR[1] }; - - const source = layer.getSource(); - const layerDefs = source?.getParams().layerDefs || ''; - const size = mapViewer.map.getSize()!; - - identifyUrl = - `${identifyUrl}identify?f=json&tolerance=${this.hitTolerance}` + - `&mapExtent=${extent.xmin},${extent.ymin},${extent.xmax},${extent.ymax}` + - `&imageDisplay=${size[0]},${size[1]},96` + - `&layers=visible:${layerConfig.layerId}` + - `&layerDefs=${layerDefs}` + - `&returnFieldName=true&sr=4326&returnGeometry=true` + - `&geometryType=esriGeometryPoint&geometry=${lnglat[0]},${lnglat[1]}`; - - const response = await fetch(identifyUrl); - const jsonResponse = await response.json(); - if (jsonResponse.error) { - logger.logInfo('There is a problem with this query: ', identifyUrl); - throw new Error(`Error code = ${jsonResponse.error.code} ${jsonResponse.error.message}` || ''); - } - const features = new EsriJSON().readFeatures( - { features: jsonResponse.results }, - { dataProjection: Projection.PROJECTION_NAMES.LNGLAT, featureProjection: mapViewer.getProjection().getCode() } - ) as Feature[]; - const arrayOfFeatureInfoEntries = await this.formatFeatureInfoResult(features, layerConfig); - return arrayOfFeatureInfoEntries; - } catch (error) { - // Log - logger.logError('esri-dynamic.getFeatureInfoAtLongLat()\n', error); - return null; - } - } - - /** *************************************************************************************************************************** - * Count the number of times the value of a field is used by the unique value style information object. Depending on the - * visibility of the default, we count visible or invisible settings. - * - * @param {TypeLayerStyleSettings} styleSettings The unique value style settings to evaluate. - * - * @returns {TypeFieldOfTheSameValue[][]} The result of the evaluation. The first index of the array correspond to the field's - * index in the style settings and the second one to the number of different values the field may have based on visibility of - * the feature. - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - static #countFieldOfTheSameValue(styleSettings: TypeLayerStyleSettings): TypeFieldOfTheSameValue[][] { - return styleSettings.info.reduce( - (counter, styleEntry): TypeFieldOfTheSameValue[][] => { - if (styleEntry.visible !== false) { - styleEntry.values.forEach((styleValue, i) => { - const valueExist = counter[i].find((counterEntry) => counterEntry.value === styleValue); - if (valueExist) valueExist.nbOccurence++; - else counter[i].push({ value: styleValue, nbOccurence: 1 }); - }); - } - return counter; - }, - styleSettings.fields.map(() => []) - ); - } - - /** *************************************************************************************************************************** - * Sort the number of times the value of a field is used by the unique value style information object. Depending on the - * visibility of the default value, we count the visible or invisible parameters. The order goes from the highest number of - * occurrences to the lowest number of occurrences. - * - * @param {TypeLayerStyleSettings} styleSettings The unique value style settings to evaluate. - * @param {TypeFieldOfTheSameValue[][]} fieldOfTheSameValue The count information that contains the number of occurrences - * of a value. - * - * @returns {number[]} An array that gives the field order to use to build the query tree. - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - static #sortFieldOfTheSameValue(styleSettings: TypeLayerStyleSettings, fieldOfTheSameValue: TypeFieldOfTheSameValue[][]): number[] { - const fieldNotUsed = styleSettings.fields.map(() => true); - const fieldOrder: number[] = []; - for (let entrySelected = 0; entrySelected !== -1; entrySelected = fieldNotUsed.findIndex((flag) => flag)) { - let entrySelectedTotalEntryCount = fieldOfTheSameValue[entrySelected].reduce((accumulator, fieldEntry) => { - return accumulator + fieldEntry.nbOccurence; - }, 0); - for (let i = 0; i < styleSettings.fields.length; i++) { - if (fieldNotUsed[i] && i !== entrySelected) { - const newEntrySelectedTotalEntryCount = fieldOfTheSameValue[i].reduce((accumulator, fieldEntry) => { - return accumulator + fieldEntry.nbOccurence; - }, 0); - if ( - fieldOfTheSameValue[entrySelected].length > fieldOfTheSameValue[i].length || - (fieldOfTheSameValue[entrySelected].length === fieldOfTheSameValue[i].length && - entrySelectedTotalEntryCount < newEntrySelectedTotalEntryCount) - ) { - entrySelected = i; - entrySelectedTotalEntryCount = newEntrySelectedTotalEntryCount; - } - } - } - fieldNotUsed[entrySelected] = false; - fieldOrder.push(entrySelected); - } - return fieldOrder; - } - - /** *************************************************************************************************************************** - * Get the query tree. The tree structure is a representation of the optimized query we have to create. It contains the field - * values in the order specified by the fieldOrder parameter. The optimization is based on the distributivity and associativity - * of the Boolean algebra. The form is the following: - * - * (f1 = v11 and (f2 = v21 and f3 in (v31, v32) or f2 = v22 and f3 in (v31, v32, v33)) or f1 = v12 and (f2 = v21 and ...))) - * - * which is equivalent to: - * - * f1 = v11 and f2 = v21 and f3 = v31 or f1 = v11 and f2 = v21 and f3 = v32 or f1 = v11 and f2 = v22 and f3 = v31 ... - * - * @param {TypeLayerStyleSettings} styleSettings The unique value style settings to evaluate. - * @param {TypeFieldOfTheSameValue[][]} fieldOfTheSameValue The count information that contains the number of occurrences - * of a value. - * @param {number[]} fieldOrder The field order to use when building the tree. - * - * @returns {TypeQueryTree} The query tree to use when building the final query string. - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - static #getQueryTree( - styleSettings: TypeLayerStyleSettings, - fieldOfTheSameValue: TypeFieldOfTheSameValue[][], - fieldOrder: number[] - ): TypeQueryTree { - const queryTree: TypeQueryTree = []; - styleSettings.info.forEach((styleEntry) => { - if (styleEntry.visible !== false) { - let levelToSearch = queryTree; - for (let i = 0; i < fieldOrder.length; i++) { - if (fieldOfTheSameValue[fieldOrder[i]].find((field) => field.value === styleEntry.values[fieldOrder[i]])) { - const treeElementFound = levelToSearch.find((treeElement) => styleEntry.values[fieldOrder[i]] === treeElement.fieldValue); - if (!treeElementFound) { - levelToSearch.push({ fieldValue: styleEntry.values[fieldOrder[i]], nextField: [] }); - levelToSearch = levelToSearch[levelToSearch.length - 1].nextField; - } else levelToSearch = treeElementFound.nextField; - } - } - } - }); - return queryTree; - } - - /** *************************************************************************************************************************** - * format the field value to use in the query. - * - * @param {string} fieldName The field name. - * @param {string | number | Date} rawValue The unformatted field value. - * @param {TypeFeatureInfoLayerConfig} sourceFeatureInfo The source feature information that knows the field type. - * - * @returns {string} The resulting field value. - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - static #formatFieldValue(fieldName: string, rawValue: string | number | Date, sourceFeatureInfo: TypeFeatureInfoLayerConfig): string { - const { outfields } = sourceFeatureInfo; - let selectedOutfield; - if (outfields?.length) selectedOutfield = outfields.find((outfield) => outfield.name === fieldName); - switch (selectedOutfield?.type) { - case 'date': - return `date '${rawValue}'`; - case 'string': - return `'${rawValue}'`; - default: - return `${rawValue}`; - } - } - - /** *************************************************************************************************************************** - * Build the query using the provided query tree. - * - * @param {TypeQueryTree} queryTree The query tree to use. - * @param {number} level The level to use for solving the tree. - * @param {number[]} fieldOrder The field order to use for solving the tree. - * @param {TypeLayerStyleSettings} styleSettings The unique value style settings to evaluate. - * @param {TypeFeatureInfoLayerConfig} sourceFeatureInfo The source feature information that knows the field type. - * - * @returns {string} The resulting query. - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - #buildQuery( - queryTree: TypeQueryTree, - level: number, - fieldOrder: number[], - styleSettings: TypeLayerStyleSettings, - sourceFeatureInfo: TypeFeatureInfoLayerConfig - ): string { - let queryString = styleSettings.info[styleSettings.info.length - 1].visible !== false && !level ? 'not (' : '('; - for (let i = 0; i < queryTree.length; i++) { - const value = EsriDynamic.#formatFieldValue(styleSettings.fields[fieldOrder[level]], queryTree[i].fieldValue, sourceFeatureInfo); - // The nextField array is not empty, then it is is not the last field - if (queryTree[i].nextField.length) { - // If i > 0 (true) then we add a OR clause - if (i) queryString = `${queryString} or `; - // Add to the query the 'fieldName = value and ' + the result of the recursive call to buildQuery using the next field and level - queryString = `${queryString}${styleSettings.fields[fieldOrder[level]]} = ${value} and ${this.#buildQuery( - queryTree[i].nextField, - level + 1, - fieldOrder, - styleSettings, - sourceFeatureInfo - )}`; - } else { - // We have reached the last field and i = 0 (false) we concatenate 'fieldName in (value' else we concatenate ', value' - queryString = i ? `${queryString}, ${value}` : `${styleSettings.fields[fieldOrder[level]]} in (${value}`; - } - // If i points to the last element of the queryTree, close the parenthesis. - if (i === queryTree.length - 1) queryString = `${queryString})`; - } - return queryString === '(' ? '(1=0)' : queryString; - } - - /** *************************************************************************************************************************** - * Get the layer view filter. The filter is derived from the uniqueValue or the classBreak visibility flags and a layerFilter - * associated to the layer. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {string} the filter associated to the layerPath - */ - // GV Layers Refactoring - Obsolete (in layers) - getViewFilter(layerPath: string): string { - const layerConfig = this.getLayerConfig(layerPath) as EsriDynamicLayerEntryConfig; - const { layerFilter } = layerConfig; - - // Get the style - const style = this.getStyle(layerConfig.layerPath); - - if (style) { - const setAllUndefinedVisibilityFlagsToYes = (styleConfig: TypeLayerStyleSettings): void => { - const settings = styleConfig.info; - for (let i = 0; i < settings.length; i++) if (settings[i].visible === undefined) settings[i].visible = true; - }; - - const featuresAreAllVisible = (settings: { visible: boolean }[]): boolean => { - return settings.every((setting) => setting.visible !== false); - }; - - // Get the first style settings - const styleSettings = layerConfig.getFirstStyleSettings()!; - - if (styleSettings.type === 'simple') { - return layerFilter || '(1=1)'; - } - if (styleSettings.type === 'uniqueValue') { - setAllUndefinedVisibilityFlagsToYes(styleSettings); - if (featuresAreAllVisible(styleSettings.info as { visible: boolean }[])) - return `(1=1)${layerFilter ? ` and (${layerFilter})` : ''}`; - - // This section of code optimize the query to reduce it at it shortest expression. - const fieldOfTheSameValue = EsriDynamic.#countFieldOfTheSameValue(styleSettings); - const fieldOrder = EsriDynamic.#sortFieldOfTheSameValue(styleSettings, fieldOfTheSameValue); - const queryTree = EsriDynamic.#getQueryTree(styleSettings, fieldOfTheSameValue, fieldOrder); - // TODO: Refactor - Layers refactoring. We should use the source.featureInfo from the layer, not the layerConfig anymore, here and below - const query = this.#buildQuery(queryTree, 0, fieldOrder, styleSettings, layerConfig.source.featureInfo!); - return `${query}${layerFilter ? ` and (${layerFilter})` : ''}`; - } - - if (styleSettings.type === 'classBreaks') { - setAllUndefinedVisibilityFlagsToYes(styleSettings); - if (featuresAreAllVisible(styleSettings.info as { visible: boolean }[])) - return `(1=1)${layerFilter ? ` and (${layerFilter})` : ''}`; - - const filterArray: string[] = []; - let visibleWhenGreatherThisIndex = -1; - for (let i = 0; i < styleSettings.info.length; i++) { - if (filterArray.length % 2 === 0) { - if (i === 0) { - if (styleSettings.info[0].visible !== false && styleSettings.info[styleSettings.info.length - 1].visible === false) - filterArray.push( - `${styleSettings.fields[0]} >= ${EsriDynamic.#formatFieldValue( - styleSettings.fields[0], - styleSettings.info[0].values[0]!, - layerConfig.source.featureInfo! - )}` - ); - else if (styleSettings.info[0].visible === false && styleSettings.info[styleSettings.info.length - 1].visible !== false) { - filterArray.push( - `${styleSettings.fields[0]} < ${EsriDynamic.#formatFieldValue( - styleSettings.fields[0], - styleSettings.info[0].values[0], - layerConfig.source.featureInfo! - )}` - ); - visibleWhenGreatherThisIndex = i; - } - } else if (styleSettings.info[i].visible !== false && styleSettings.info[styleSettings.info.length - 1].visible === false) { - filterArray.push( - `${styleSettings.fields[0]} > ${EsriDynamic.#formatFieldValue( - styleSettings.fields[0], - styleSettings.info[i].values[0], - layerConfig.source.featureInfo! - )}` - ); - if (i + 1 === styleSettings.info.length) - filterArray.push( - `${styleSettings.fields[0]} <= ${EsriDynamic.#formatFieldValue( - styleSettings.fields[0], - styleSettings.info[i].values[1], - layerConfig.source.featureInfo! - )}` - ); - } else if (styleSettings.info[i].visible === false && styleSettings.info[styleSettings.info.length - 1].visible !== false) { - filterArray.push( - `${styleSettings.fields[0]} <= ${EsriDynamic.#formatFieldValue( - styleSettings.fields[0], - styleSettings.info[i].values[0], - layerConfig.source.featureInfo! - )}` - ); - visibleWhenGreatherThisIndex = i; - } - } else if (styleSettings.info[styleSettings.info.length - 1].visible === false) { - if (styleSettings.info[i].visible === false) { - filterArray.push( - `${styleSettings.fields[0]} <= ${EsriDynamic.#formatFieldValue( - styleSettings.fields[0], - styleSettings.info[i - 1].values[1], - layerConfig.source.featureInfo! - )}` - ); - } else if (i + 1 === styleSettings.info.length) { - filterArray.push( - `${styleSettings.fields[0]} <= ${EsriDynamic.#formatFieldValue( - styleSettings.fields[0], - styleSettings.info[i].values[1], - layerConfig.source.featureInfo! - )}` - ); - } - } else if (styleSettings.info[i].visible !== false) { - filterArray.push( - `${styleSettings.fields[0]} > ${EsriDynamic.#formatFieldValue( - styleSettings.fields[0], - styleSettings.info[i - 1].values[1], - layerConfig.source.featureInfo! - )}` - ); - visibleWhenGreatherThisIndex = -1; - } else { - visibleWhenGreatherThisIndex = i; - } - } - if (visibleWhenGreatherThisIndex !== -1) - filterArray.push( - `${styleSettings.fields[0]} > ${EsriDynamic.#formatFieldValue( - styleSettings.fields[0], - styleSettings.info[visibleWhenGreatherThisIndex].values[1], - layerConfig.source.featureInfo! - )}` - ); - - if (styleSettings.info[styleSettings.info.length - 1].visible !== false) { - const filterValue = `${filterArray.slice(0, -1).reduce((previousFilterValue, filterNode, i) => { - if (i === 0) return `(${filterNode} or `; - if (i % 2 === 0) return `${previousFilterValue} and ${filterNode}) or `; - return `${previousFilterValue}(${filterNode}`; - }, '')}${filterArray.slice(-1)[0]})`; - return `${filterValue}${layerFilter ? ` and (${layerFilter})` : ''}`; - } - - const filterValue = filterArray.length - ? `${filterArray.reduce((previousFilterValue, filterNode, i) => { - if (i === 0) return `((${filterNode} and `; - if (i % 2 === 0) return `${previousFilterValue} or (${filterNode} and `; - return `${previousFilterValue}${filterNode})`; - }, '')})` - : // We use '(1=0)' as false to select nothing - '(1=0)'; - return `${filterValue}${layerFilter ? ` and (${layerFilter})` : ''}`; - } - } - return '(1=1)'; - } - - /** - * Overrides when the layer gets in loaded status. - */ - // GV Layers Refactoring - Obsolete (in layers) - override onLoaded(layerConfig: AbstractBaseLayerEntryConfig): void { - // Call parent - super.onLoaded(layerConfig); - - // Apply view filter immediately - this.applyViewFilter(layerConfig.layerPath, (layerConfig as EsriDynamicLayerEntryConfig).layerFilter || ''); - } - - /** *************************************************************************************************************************** - * Applies a view filter to the layer. When the combineLegendFilter flag is false, the filter paramater is used alone to display - * the features. Otherwise, the legend filter and the filter parameter are combined together to define the view filter. The - * legend filters are derived from the uniqueValue or classBreaks style of the layer. When the layer config is invalid, nothing - * is done. - * - * @param {string} layerPath The layer path to the layer's configuration. - * @param {string} filter An optional filter to be used in place of the getViewFilter value. - * @param {boolean} combineLegendFilter Flag used to combine the legend filter and the filter together (default: true) - */ - // GV Layers Refactoring - Obsolete (in layers) - applyViewFilter(layerPath: string, filter: string, combineLegendFilter = true): void { - // Log - logger.logTraceCore('ESRI-DYNAMIC - applyViewFilter', layerPath); - - const layerConfig = this.getLayerConfig(layerPath) as EsriDynamicLayerEntryConfig; - const olLayer = this.getOLLayer(layerPath) as ImageLayer | undefined; - - let filterValueToUse = filter.replaceAll(/\s{2,}/g, ' ').trim(); - layerConfig.legendFilterIsOff = !combineLegendFilter; - layerConfig.layerFilter = filterValueToUse; - if (combineLegendFilter) filterValueToUse = this.getViewFilter(layerPath); - - // Convert date constants using the externalFragmentsOrder derived from the externalDateFormat - // TODO: Standardize the regex across all layer types - // OLD REGEX, not working anymore, test before standardization - // ...`${filterValueToUse?.replaceAll(/\s{2,}/g, ' ').trim()} `.matchAll( - // /(?<=^date\b\s')[\d/\-T\s:+Z]{4,25}(?=')|(?<=[(\s]date\b\s')[\d/\-T\s:+Z]{4,25}(?=')/gi - // ), - const searchDateEntry = [ - ...filterValueToUse.matchAll( - /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/gi - ), - ]; - - searchDateEntry.reverse(); - searchDateEntry.forEach((dateFound) => { - // If the date has a time zone, keep it as is, otherwise reverse its time zone by changing its sign - const reverseTimeZone = ![20, 25].includes(dateFound[0].length); - let reformattedDate = DateMgt.applyInputDateFormat(dateFound[0], this.externalFragmentsOrder, reverseTimeZone); - // GV ESRI Dynamic layers doesn't accept the ISO date format. The time zone must be removed. The 'T' separator - // GV normally placed between the date and the time must be replaced by a space. - reformattedDate = reformattedDate.slice(0, reformattedDate.length === 20 ? -1 : -6); // drop time zone. - reformattedDate = reformattedDate.replace('T', ' '); - filterValueToUse = `${filterValueToUse!.slice(0, dateFound.index)}${reformattedDate}${filterValueToUse!.slice( - dateFound.index! + dateFound[0].length - )}`; - }); - - olLayer?.getSource()!.updateParams({ layerDefs: `{"${layerConfig.layerId}": "${filterValueToUse}"}` }); - olLayer?.changed(); - - // Emit event - this.emitLayerFilterApplied({ - layerPath, - filter: filterValueToUse, - }); - } - - /** *************************************************************************************************************************** - * Get the bounds of the layer represented in the layerConfig pointed to by the layerPath, returns updated bounds - * - * @param {string} layerPath The Layer path to the layer's configuration. - * - * @returns {Extent | undefined} The new layer bounding box. - */ - // GV Layers Refactoring - Obsolete (in layers) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - override getBounds(layerPath: string): Extent | undefined { - // Get the metadata extent - const metadataExtent = this.getMetadataExtent(layerPath); - - // If found - let layerBounds; - if (metadataExtent) { - // Get the metadata projection - const metadataProjection = this.getMetadataProjection(); - layerBounds = this.getMapViewer().convertExtentFromProjToMapProj(metadataExtent, metadataProjection); - layerBounds = validateExtent(layerBounds, this.getMapViewer().getProjection().getCode()); - } - - // Return the calculated layer bounds - return layerBounds; - } - - /** - * Sends a query to get ESRI Dynamic feature geometries and calculates an extent from them. - * @param {string} layerPath - The layer path. - * @param {string[]} objectIds - The IDs of the features to calculate the extent from. - * @param {string} outfield - ID field to return for services that require a value in outfields. - * @returns {Promise} The extent of the features, if available. - */ - override async getExtentFromFeatures(layerPath: string, objectIds: string[], outfield?: string): Promise { - // Get url for service from layer entry config - const layerEntryConfig = this.getLayerConfig(layerPath)! as EsriDynamicLayerEntryConfig; - let baseUrl = layerEntryConfig.source.dataAccessPath; - - const idString = objectIds.join('%2C'); - if (baseUrl) { - // Construct query - if (!baseUrl.endsWith('/')) baseUrl += '/'; - // GV: Outfields here is not wanted, it is included because some sevices require it in the query. It would be possible to use - // GV cont: objectid, but it is not universal through the services, so we pass a value through. - const outfieldQuery = outfield ? `&outFields=${outfield}` : ''; - const queryUrl = `${baseUrl}${layerEntryConfig.layerId}/query?&f=json&where=&objectIds=${idString}${outfieldQuery}&returnGeometry=true`; - - try { - const response = await fetch(queryUrl); - const responseJson = await response.json(); - - // Convert response json to OL features - const responseFeatures = new EsriJSON().readFeatures( - { features: responseJson.features }, - { - dataProjection: `EPSG:${responseJson.spatialReference.wkid}`, - featureProjection: this.getMapViewer().getProjection().getCode(), - } - ); - - // Determine max extent from features - let calculatedExtent: Extent | undefined; - responseFeatures.forEach((feature) => { - const extent = feature.getGeometry()?.getExtent(); - - if (extent) { - // If extent has not been defined, set it to extent - if (!calculatedExtent) calculatedExtent = extent; - else getMinOrMaxExtents(calculatedExtent, extent); - } - }); - - return calculatedExtent; - } catch (error) { - logger.logError(`Error fetching geometry from ${queryUrl}`, error); - } - } - return undefined; - } } diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-image.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-image.ts index 28d35c362ee..b0edd1d88d2 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-image.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/esri-image.ts @@ -2,64 +2,26 @@ import { ImageArcGISRest } from 'ol/source'; import { Options as SourceOptions } from 'ol/source/ImageArcGISRest'; import BaseLayer from 'ol/layer/Base'; import { Image as ImageLayer } from 'ol/layer'; -import { Extent } from 'ol/extent'; -import { DateMgt } from '@/core/utils/date-mgt'; import { TypeJsonObject } from '@/core/types/global-types'; -import { logger } from '@/core/utils/logger'; import { EsriImageLayerEntryConfig } from '@/core/utils/config/validation-classes/raster-validation-classes/esri-image-layer-entry-config'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; 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, - TypeLayerStyleSettings, - TypeLayerStyleConfig, - layerEntryIsGroupLayer, - codedValueType, - rangeDomainType, - TypeLayerStyleConfigInfo, -} from '@/geo/map/map-schema-types'; +import { TypeLayerEntryConfig, TypeGeoviewLayerConfig, layerEntryIsGroupLayer } from '@/geo/map/map-schema-types'; import { - commonGetFieldDomain, - commonGetFieldType, commonProcessFeatureInfoConfig, commonProcessInitialSettings, commonProcessLayerMetadata, commonProcessTemporalDimension, } from '@/geo/layer/geoview-layers/esri-layer-common'; -import { validateExtent } from '@/geo/utils/utilities'; -import { getLegendStyles } from '@/geo/utils/renderer/geoview-renderer'; -import { TypeLegend } from '@/core/stores/store-interface-and-intial-values/layer-state'; -import { TypeOutfieldsType } from '@/api/config/types/map-schema-types'; export interface TypeEsriImageLayerConfig extends TypeGeoviewLayerConfig { geoviewLayerType: typeof CONST_LAYER_TYPES.ESRI_IMAGE; listOfLayerEntryConfig: EsriImageLayerEntryConfig[]; } -interface TypeEsriImageLayerLegend { - layers: { - layerId: number | string; - layerName: string; - layerType: string; - minScale: number; - maxScale: number; - legendType: string; - legend: { - label: string; - url: string; - imageData: string; - contentType: string; - height: number; - width: number; - values: string[]; - }[]; - }[]; -} - /** ****************************************************************************************************************************** * type guard function that redefines a TypeGeoviewLayerConfig as a TypeEsriImageLayerConfig if the geoviewLayerType attribute of * the verifyIfLayer parameter is ESRI_IMAGE. The type ascention applies only to the true block of the if clause that use @@ -122,80 +84,6 @@ export class EsriImage extends AbstractGeoViewRaster { super(CONST_LAYER_TYPES.ESRI_IMAGE, layerConfig, mapId); } - /** *************************************************************************************************************************** - * Return the legend of the layer.This routine return null when the layerPath specified is not found. If the legend can't be - * read, the legend property of the object returned will be null. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The legend of the layer. - */ - // GV Layers Refactoring - Obsolete (in layers) - override async getLegend(layerPath: string): Promise { - try { - const layerConfig = this.getLayerConfig(layerPath) as EsriImageLayerEntryConfig | undefined | null; - if (!layerConfig) return null; - const legendUrl = `${layerConfig.geoviewLayerConfig.metadataAccessPath}/legend?f=json`; - const response = await fetch(legendUrl); - const legendJson: TypeEsriImageLayerLegend = await response.json(); - let legendInfo; - if (legendJson.layers && legendJson.layers.length === 1) { - legendInfo = legendJson.layers[0].legend; - } else if (legendJson.layers.length) { - const layerInfo = legendJson.layers.find((layer) => layer.layerId === layerConfig.layerId); - if (layerInfo) legendInfo = layerInfo.legend; - } - if (!legendInfo) { - const legend: TypeLegend = { - type: CONST_LAYER_TYPES.ESRI_IMAGE, - styleConfig: this.getStyle(layerPath), - legend: null, - }; - return legend; - } - const uniqueValueStyleInfo: TypeLayerStyleConfigInfo[] = []; - legendInfo.forEach((info) => { - const styleInfo: TypeLayerStyleConfigInfo = { - label: info.label, - visible: layerConfig.initialSettings.states?.visible || true, - values: info.label.split(','), - settings: { - type: 'iconSymbol', - mimeType: info.contentType, - src: info.imageData, - width: info.width, - height: info.height, - }, - }; - uniqueValueStyleInfo.push(styleInfo); - }); - const styleSettings: TypeLayerStyleSettings = { - type: 'uniqueValue', - fields: ['default'], - hasDefault: true, - info: uniqueValueStyleInfo, - }; - const styleConfig: TypeLayerStyleConfig = { - Point: styleSettings, - }; - - // TODO: Refactor - Find a better place to set the style than in a getter or rename this function like another TODO suggests - // TO.DOCONT: This setter is also dangerous, because it triggers a style changed event which is listened by the legends-layer-set. - // TO.DOCONT: Fortunately, we have a loop check barrier, but setting a style during a legend fetch getter could lead to be more problematic. - this.setStyle(layerPath, styleConfig); - - const legend: TypeLegend = { - type: CONST_LAYER_TYPES.ESRI_IMAGE, - styleConfig, - legend: await getLegendStyles(this.getStyle(layerPath)), - }; - return legend; - } catch (error) { - logger.logError(`Get Legend for ${layerPath} error`, error); - return null; - } - } - /** *************************************************************************************************************************** * This method recursively validates the layer configuration entries by filtering and reporting invalid layers. If needed, * extra configuration may be done here. @@ -222,33 +110,6 @@ export class EsriImage extends AbstractGeoViewRaster { }); } - /** *************************************************************************************************************************** - * Extract the type of the specified field from the metadata. If the type can not be found, return 'string'. - * - * @param {string} fieldName field name for which we want to get the type. - * @param {TypeLayerEntryConfig} layerConfig layer configuration. - * - * @returns {TypeOutfieldsType} The type of the field. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFieldType(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): TypeOutfieldsType { - // TODO: Refactor - Layers refactoring. Is this function really valid for an esri-image? Remove? - return commonGetFieldType(this, fieldName, layerConfig); - } - - /** *************************************************************************************************************************** - * Return the domain of the specified field. - * - * @param {string} fieldName field name for which we want to get the domain. - * @param {TypeLayerEntryConfig} layerConfig layer configuration. - * - * @returns {null | codedValueType | rangeDomainType} The domain of the field. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFieldDomain(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): null | codedValueType | rangeDomainType { - return commonGetFieldDomain(this, fieldName, layerConfig); - } - /** *************************************************************************************************************************** * This method will create a Geoview temporal dimension if it exist in the service metadata * @param {TypeJsonObject} esriTimeDimension The ESRI time dimension object @@ -342,98 +203,4 @@ export class EsriImage extends AbstractGeoViewRaster { return Promise.resolve(olLayer); } - - /** - * Overrides when the layer gets in loaded status. - */ - // GV Layers Refactoring - Obsolete (in layers) - override onLoaded(layerConfig: AbstractBaseLayerEntryConfig): void { - // Call parent - super.onLoaded(layerConfig); - - // Apply view filter immediately - this.applyViewFilter(layerConfig.layerPath, (layerConfig as EsriImageLayerEntryConfig).layerFilter || ''); - } - - /** *************************************************************************************************************************** - * Applies a view filter to the layer. When the combineLegendFilter flag is false, the filter paramater is used alone to display - * the features. Otherwise, the legend filter and the filter parameter are combined together to define the view filter. The - * legend filters are derived from the uniqueValue or classBreaks style of the layer. When the layer config is invalid, nothing - * is done. - * - * @param {string} layerPath The layer path to the layer's configuration. - * @param {string} filter An optional filter to be used in place of the getViewFilter value. - * @param {boolean} combineLegendFilter Flag used to combine the legend filter and the filter together (default: true) - */ - // GV Layers Refactoring - Obsolete (in layers) - applyViewFilter(layerPath: string, filter: string, combineLegendFilter?: boolean): void { - // Log - logger.logTraceCore('ESRIImage - applyViewFilter', layerPath); - - const layerConfig = this.getLayerConfig(layerPath) as EsriImageLayerEntryConfig; - const olLayer = this.getOLLayer(layerPath) as ImageLayer; - - // Get source - const source = olLayer.getSource(); - if (source) { - let filterValueToUse = filter; - layerConfig.legendFilterIsOff = !combineLegendFilter; - if (combineLegendFilter) layerConfig.layerFilter = filter; - - if (filterValueToUse) { - filterValueToUse = filterValueToUse.replaceAll(/\s{2,}/g, ' ').trim(); - const queryElements = filterValueToUse.split(/(?<=\b)\s*=/); - const dimension = queryElements[0].trim(); - filterValueToUse = queryElements[1].trim(); - - // Convert date constants using the externalFragmentsOrder derived from the externalDateFormat - const searchDateEntry = [ - ...`${filterValueToUse} `.matchAll(/(?<=^date\b\s')[\d/\-T\s:+Z]{4,25}(?=')|(?<=[(\s]date\b\s')[\d/\-T\s:+Z]{4,25}(?=')/gi), - ]; - searchDateEntry.reverse(); - searchDateEntry.forEach((dateFound) => { - // If the date has a time zone, keep it as is, otherwise reverse its time zone by changing its sign - const reverseTimeZone = ![20, 25].includes(dateFound[0].length); - const reformattedDate = DateMgt.applyInputDateFormat(dateFound[0], this.externalFragmentsOrder, reverseTimeZone); - filterValueToUse = `${filterValueToUse!.slice(0, dateFound.index! - 6)}${reformattedDate}${filterValueToUse!.slice( - dateFound.index! + dateFound[0].length + 2 - )}`; - }); - source.updateParams({ [dimension]: filterValueToUse.replace(/\s*/g, '') }); - olLayer.changed(); - - // Emit event - this.emitLayerFilterApplied({ - layerPath, - filter: filterValueToUse, - }); - } - } - } - - /** *************************************************************************************************************************** - * Get the bounds of the layer represented in the layerConfig pointed to by the layerPath, returns updated bounds - * - * @param {string} layerPath The Layer path to the layer's configuration. - * - * @returns {Extent | undefined} The new layer bounding box. - */ - // GV Layers Refactoring - Obsolete (in layers) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - override getBounds(layerPath: string): Extent | undefined { - // Get the metadata extent - const metadataExtent = this.getMetadataExtent(layerPath); - - // If found - let layerBounds; - if (metadataExtent) { - // Get the metadata projection - const metadataProjection = this.getMetadataProjection(); - layerBounds = this.getMapViewer().convertExtentFromProjToMapProj(metadataExtent, metadataProjection); - layerBounds = validateExtent(layerBounds, this.getMapViewer().getProjection().getCode()); - } - - // Return the calculated layer bounds - return layerBounds; - } } diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/image-static.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/image-static.ts index becfd9836da..7310a25f8f2 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/image-static.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/image-static.ts @@ -1,19 +1,13 @@ -import axios from 'axios'; - import Static, { Options as SourceOptions } from 'ol/source/ImageStatic'; import BaseLayer from 'ol/layer/Base'; import ImageLayer from 'ol/layer/Image'; -import { Extent } from 'ol/extent'; -import { Cast, TypeJsonObject } from '@/core/types/global-types'; +import { Cast } 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, layerEntryIsGroupLayer } from '@/geo/map/map-schema-types'; -import { logger } from '@/core/utils/logger'; import { ImageStaticLayerEntryConfig } from '@/core/utils/config/validation-classes/raster-validation-classes/image-static-layer-entry-config'; -import { loadImage } from '@/geo/utils/renderer/geoview-renderer'; -import { TypeLegend } from '@/core/stores/store-interface-and-intial-values/layer-state'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; export interface TypeImageStaticLayerConfig extends Omit { @@ -96,88 +90,6 @@ export class ImageStatic extends AbstractGeoViewRaster { return promisedExecution; } - /** *************************************************************************************************************************** - * Get the legend image of a layer. - * - * @param {ImageStaticLayerEntryConfig} layerConfig layer configuration. - * - * @returns {blob} image blob - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - static #getLegendImage(layerConfig: ImageStaticLayerEntryConfig): Promise { - const promisedImage = new Promise((resolve) => { - const readImage = (blob: Blob): Promise => - // eslint-disable-next-line @typescript-eslint/no-shadow - new Promise((resolve) => { - const reader = new FileReader(); - reader.onloadend = () => resolve(reader.result); - reader.onerror = () => resolve(null); - reader.readAsDataURL(blob); - }); - - let legendUrl: string | undefined = layerConfig.source.dataAccessPath; - - if (legendUrl) { - legendUrl = legendUrl.toLowerCase().startsWith('http:') ? `https${legendUrl.slice(4)}` : legendUrl; - - axios - .get(legendUrl, { responseType: 'blob', withCredentials: false }) - .then((response) => { - resolve(readImage(Cast(response.data))); - }) - .catch(() => resolve(null)); - } else resolve(null); - }); - return promisedImage; - } - - /** *************************************************************************************************************************** - * Return the legend of the layer.This routine return null when the layerPath specified is not found. If the legend can't be - * read, the legend property of the object returned will be null. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The legend of the layer. - */ - // GV Layers Refactoring - Obsolete (in layers) - override async getLegend(layerPath: string): Promise { - try { - const layerConfig = this.getLayerConfig(layerPath) as ImageStaticLayerEntryConfig | undefined | null; - if (!layerConfig) return null; - - const legendImage = await ImageStatic.#getLegendImage(layerConfig!); - if (!legendImage) { - const legend: TypeLegend = { - type: CONST_LAYER_TYPES.IMAGE_STATIC, - legend: null, - }; - return legend; - } - const image = await loadImage(legendImage as string); - if (image) { - const drawingCanvas = document.createElement('canvas'); - drawingCanvas.width = image.width; - drawingCanvas.height = image.height; - const drawingContext = drawingCanvas.getContext('2d')!; - drawingContext.drawImage(image, 0, 0); - const legend: TypeLegend = { - type: CONST_LAYER_TYPES.IMAGE_STATIC, - legend: drawingCanvas, - }; - return legend; - } - const legend: TypeLegend = { - type: CONST_LAYER_TYPES.IMAGE_STATIC, - legend: null, - }; - return legend; - } catch (error) { - logger.logError(`Error getting legend for ${layerPath}`, error); - return null; - } - } - /** *************************************************************************************************************************** * This method recursively validates the layer configuration entries by filtering and reporting invalid layers. If needed, * extra configuration may be done here. @@ -280,30 +192,4 @@ export class ImageStatic extends AbstractGeoViewRaster { return Promise.resolve(olLayer); } - - /** *************************************************************************************************************************** - * Get the bounds of the layer represented in the layerConfig pointed to by the layerPath, returns updated bounds - * - * @param {string} layerPath The Layer path to the layer's configuration. - * - * @returns {Extent | undefined} The new layer bounding box. - */ - // GV Layers Refactoring - Obsolete (in layers) - override getBounds(layerPath: string): Extent | undefined { - // Get the layer - const layer = this.getOLLayer(layerPath) as ImageLayer | undefined; - - // Get the source projection - const sourceProjection = this.getSourceProjection(layerPath); - - // Get the layer bounds - let sourceExtent = layer?.getSource()?.getImageExtent(); - if (sourceExtent) { - // Make sure we're in the map projection - sourceExtent = this.getMapViewer().convertExtentFromProjToMapProj(sourceExtent, sourceProjection); - } - - // Return the calculated layer bounds - return sourceExtent; - } } diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts index 85602f5ae49..629a6656c1e 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts @@ -1,9 +1,7 @@ import BaseLayer from 'ol/layer/Base'; -import TileLayer from 'ol/layer/Tile'; import VectorTileLayer from 'ol/layer/VectorTile'; import VectorTileSource, { Options as SourceOptions } from 'ol/source/VectorTile'; import TileGrid, { Options as TileGridOptions } from 'ol/tilegrid/TileGrid'; -import { Extent } from 'ol/extent'; import { applyStyle } from 'ol-mapbox-style'; @@ -16,7 +14,6 @@ import { TypeGeoviewLayerConfig, TypeTileGrid, layerEntryIsGroupLayer, - TypeFeatureInfoLayerConfig, } from '@/geo/map/map-schema-types'; import { TypeJsonObject } from '@/core/types/global-types'; import { validateExtentWhenDefined } from '@/geo/utils/utilities'; @@ -24,7 +21,6 @@ import { api } from '@/app'; import { VectorTilesLayerEntryConfig } from '@/core/utils/config/validation-classes/raster-validation-classes/vector-tiles-layer-entry-config'; import { logger } from '@/core/utils/logger'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; -import { TypeOutfieldsType } from '@/api/config/types/map-schema-types'; // TODO: Implement method to validate Vector Tiles service // TODO: Add more customization (minZoom, maxZoom, TMS) @@ -107,22 +103,6 @@ export class VectorTiles extends AbstractGeoViewRaster { super(CONST_LAYER_TYPES.VECTOR_TILES, layerConfig, mapId); } - /** *************************************************************************************************************************** - * Extract the type of the specified field from the metadata. If the type can not be found, return 'string'. - * - * @param {string} fieldName field name for which we want to get the type. - * @param {TypeLayerEntryConfig} layerConfig layer configuration. - * - * @returns {TypeOutfieldsType} The type of the field. - */ - // GV Layers Refactoring - Obsolete (in layers) - // TODO refactor - this looks like it will not work, investigate further - protected override getFieldType(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): TypeOutfieldsType { - const fieldDefinitions = this.getLayerMetadata(layerConfig.layerPath).source.featureInfo as unknown as TypeFeatureInfoLayerConfig; - const outFieldEntry = fieldDefinitions.outfields?.find((fieldDefinition) => fieldDefinition.name === fieldName); - return outFieldEntry?.type || 'string'; - } - /** *************************************************************************************************************************** * This method recursively validates the layer configuration entries by filtering and reporting invalid layers. If needed, * extra configuration may be done here. @@ -261,32 +241,6 @@ export class VectorTiles extends AbstractGeoViewRaster { return Promise.resolve(layerConfig); } - /** *************************************************************************************************************************** - * Get the bounds of the layer represented in the layerConfig pointed to by the layerPath, returns updated bounds - * - * @param {string} layerPath The Layer path to the layer's configuration. - * - * @returns {Extent | undefined} The new layer bounding box. - */ - // GV Layers Refactoring - Obsolete (in layers) - override getBounds(layerPath: string): Extent | undefined { - // Get the layer - const layer = this.getOLLayer(layerPath) as TileLayer | undefined; - - // Get the source projection - const sourceProjection = this.getSourceProjection(layerPath); - - // Get the layer bounds - let sourceExtent = layer?.getSource()?.getTileGrid()?.getExtent(); - if (sourceExtent) { - // Make sure we're in the map projection - sourceExtent = this.getMapViewer().convertExtentFromProjToMapProj(sourceExtent, sourceProjection); - } - - // Return the calculated layer bounds - return sourceExtent; - } - /** * Set Vector Tile style * 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 65a6ed7fec3..6ef92de3a5e 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 @@ -1,45 +1,26 @@ /* eslint-disable no-param-reassign */ // We have many reassign for layer-layerConfig. We keep it global... -import axios from 'axios'; - import ImageLayer from 'ol/layer/Image'; -import { Coordinate } from 'ol/coordinate'; -import { Pixel } from 'ol/pixel'; import BaseLayer from 'ol/layer/Base'; import { ImageWMS } from 'ol/source'; import { Options as SourceOptions } from 'ol/source/ImageWMS'; import WMSCapabilities from 'ol/format/WMSCapabilities'; -import { Layer as OlLayer } from 'ol/layer'; import { Extent } from 'ol/extent'; import cloneDeep from 'lodash/cloneDeep'; import { Cast, TypeJsonArray, TypeJsonObject } from '@/core/types/global-types'; -import { - AbstractGeoViewLayer, - CONST_LAYER_TYPES, - TypeWmsLegend, - TypeWmsLegendStyle, -} from '@/geo/layer/geoview-layers/abstract-geoview-layers'; +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, - TypeFeatureInfoEntry, -} from '@/geo/map/map-schema-types'; -import { xmlToJson } from '@/core/utils/utilities'; +import { TypeLayerEntryConfig, TypeGeoviewLayerConfig, CONST_LAYER_ENTRY_TYPES, layerEntryIsGroupLayer } from '@/geo/map/map-schema-types'; import { DateMgt } from '@/core/utils/date-mgt'; -import { getExtentIntersection, validateExtent, validateExtentWhenDefined } from '@/geo/utils/utilities'; +import { validateExtent, validateExtentWhenDefined } from '@/geo/utils/utilities'; import { api } from '@/app'; import { MapEventProcessor } from '@/api/event-processors/event-processor-children/map-event-processor'; 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 { TypeLegend } from '@/core/stores/store-interface-and-intial-values/layer-state'; -import { loadImage } from '@/geo/utils/renderer/geoview-renderer'; export interface TypeWMSLayerConfig extends Omit { geoviewLayerType: typeof CONST_LAYER_TYPES.WMS; @@ -627,402 +608,6 @@ export class WMS extends AbstractGeoViewRaster { } } - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided Pixel. - * - * @param {Coordinate} location The pixel coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFeatureInfoAtPixel(location: Pixel, layerPath: string): Promise { - // Redirect to getFeatureInfoAtCoordinate - return this.getFeatureInfoAtCoordinate(this.getMapViewer().map.getCoordinateFromPixel(location), layerPath); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided projection coordinate. - * - * @param {Coordinate} location The coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The promised feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFeatureInfoAtCoordinate( - location: Coordinate, - layerPath: string - ): Promise { - const convertedLocation = this.getMapViewer().convertCoordinateMapProjToLngLat(location); - return this.getFeatureInfoAtLongLat(convertedLocation, layerPath); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided coordinate. - * - * @param {Coordinate} lnglat The coordinate that will be used by the query. - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The promised feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override async getFeatureInfoAtLongLat( - lnglat: Coordinate, - layerPath: string - ): Promise { - try { - // Get the layer config in a loaded phase - const layerConfig = this.getLayerConfig(layerPath) as OgcWmsLayerEntryConfig; - const layer = this.getOLLayer(layerPath) as OlLayer; - - if (!this.getVisible(layerPath)) return []; - - const clickCoordinate = this.getMapViewer().convertCoordinateLngLatToMapProj(lnglat); - if ( - lnglat[0] < layerConfig.initialSettings!.bounds![0] || - layerConfig.initialSettings!.bounds![2] < lnglat[0] || - lnglat[1] < layerConfig.initialSettings!.bounds![1] || - layerConfig.initialSettings!.bounds![3] < lnglat[1] - ) - return []; - - const wmsSource = layer.getSource() as ImageWMS; - let infoFormat = ''; - const featureInfoFormat = this.metadata?.Capability?.Request?.GetFeatureInfo?.Format as TypeJsonArray; - if (featureInfoFormat) - if (featureInfoFormat.includes('text/xml' as TypeJsonObject)) infoFormat = 'text/xml'; - else if (featureInfoFormat.includes('application/json' as TypeJsonObject)) infoFormat = 'application/json'; - else if (featureInfoFormat.includes('text/plain' as TypeJsonObject)) infoFormat = 'text/plain'; - else throw new Error('Parameter info_format of GetFeatureInfo only support text/xml and text/plain for WMS services.'); - - const viewResolution = this.getMapViewer().getView().getResolution()!; - const featureInfoUrl = wmsSource.getFeatureInfoUrl(clickCoordinate, viewResolution, this.getMapViewer().getProjection().getCode(), { - INFO_FORMAT: infoFormat, - }); - if (featureInfoUrl) { - let featureMember: TypeJsonObject | undefined; - const response = await axios(featureInfoUrl); - if (infoFormat === 'text/xml') { - const xmlDomResponse = new DOMParser().parseFromString(response.data, 'text/xml'); - const jsonResponse = xmlToJson(xmlDomResponse); - // GV TODO: We should use a WMS format setting in the schema to decide what feature info response interpreter to use - // GV For the moment, we try to guess the response format based on properties returned from the query - const featureCollection = WMS.#getAttribute(jsonResponse, 'FeatureCollection'); - if (featureCollection) featureMember = WMS.#getAttribute(featureCollection, 'featureMember'); - else { - const featureInfoResponse = WMS.#getAttribute(jsonResponse, 'FeatureInfoResponse'); - if (featureInfoResponse) { - featureMember = WMS.#getAttribute(featureInfoResponse, 'FIELDS'); - if (featureMember) featureMember = WMS.#getAttribute(featureMember, '@attributes'); - } else { - const getFeatureInfoResponse = WMS.#getAttribute(jsonResponse, 'GetFeatureInfoResponse'); - if (getFeatureInfoResponse?.Layer) { - featureMember = {}; - featureMember['Layer name'] = getFeatureInfoResponse?.Layer?.['@attributes']?.name; - if (getFeatureInfoResponse?.Layer?.Attribute?.['@attributes']) { - const fieldName = getFeatureInfoResponse.Layer.Attribute['@attributes'].name as string; - const fieldValue = getFeatureInfoResponse.Layer.Attribute['@attributes'].value; - featureMember[fieldName] = fieldValue; - } - } - } - } - } else if (infoFormat === 'application/json') { - if (response.data.type === 'FeatureCollection') { - const { features } = response.data as { features: TypeJsonArray }; - featureMember = features[0]?.properties; - } - } else { - featureMember = { plain_text: { '#text': response.data } }; - } - if (featureMember) { - const featureInfoResult = this.#formatWmsFeatureInfoResult(featureMember, clickCoordinate); - return featureInfoResult; - } - } - return []; - } catch (error) { - // Log - logger.logError('wms.getFeatureInfoAtLongLat()\n', error); - return null; - } - } - - /** *************************************************************************************************************************** - * Get the legend image URL of a layer from the capabilities. Return null if it does not exist. - * - * @param {OgcWmsLayerEntryConfig} layerConfig layer configuration. - * @param {string} style the style to get the url for - * - * @returns {TypeJsonObject | null} URL of a Legend image in png format or null - * @private - */ - // GV Layers Refactoring - Obsolete (in layers - in config) - #getLegendUrlFromCapabilities(layerConfig: OgcWmsLayerEntryConfig, chosenStyle?: string): TypeJsonObject | null { - const layerCapabilities = this.#getLayerMetadataEntry(layerConfig.layerId); - if (Array.isArray(layerCapabilities?.Style)) { - // check if WMS as a default legend style - let isDefaultStyle = false; - layerCapabilities!.Style.forEach((style) => { - if (style.Name === 'default') isDefaultStyle = true; - }); - - let legendStyle; - if (chosenStyle) { - [legendStyle] = layerCapabilities!.Style.filter((style) => { - return style.Name === chosenStyle; - }); - } else { - legendStyle = layerCapabilities?.Style.find((style) => { - if (layerConfig?.source?.wmsStyle && !Array.isArray(layerConfig?.source?.wmsStyle)) - return layerConfig.source.wmsStyle === style.Name; - - // no style found, if default apply, if not use the available style - return isDefaultStyle ? style.Name === 'default' : style.Name; - }); - } - - if (Array.isArray(legendStyle?.LegendURL)) { - const legendUrl = legendStyle!.LegendURL.find((urlEntry) => { - if (urlEntry.Format === 'image/png') return true; - return false; - }); - return legendUrl || null; - } - } - return null; - } - - /** *************************************************************************************************************************** - * Get the legend image of a layer. - * - * @param {OgcWmsLayerEntryConfig} layerConfig layer configuration. - * @param {striung} chosenStyle Style to get the legend image for. - * - * @returns {blob} image blob - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - #getLegendImage(layerConfig: OgcWmsLayerEntryConfig, chosenStyle?: string): Promise { - const promisedImage = new Promise((resolve) => { - const readImage = (blob: Blob): Promise => - new Promise((resolveImage) => { - const reader = new FileReader(); - reader.onloadend = () => resolveImage(reader.result); - reader.onerror = () => resolveImage(null); - reader.readAsDataURL(blob); - }); - - let queryUrl: string | undefined; - const legendUrlFromCapabilities = this.#getLegendUrlFromCapabilities(layerConfig, chosenStyle); - if (legendUrlFromCapabilities) queryUrl = legendUrlFromCapabilities.OnlineResource as string; - else if (Object.keys(this.metadata?.Capability.Request || {}).includes('GetLegendGraphic')) - queryUrl = `${this.metadataAccessPath}service=WMS&version=1.3.0&request=GetLegendGraphic&FORMAT=image/png&layer=${layerConfig.layerId}`; - - if (queryUrl) { - queryUrl = queryUrl.toLowerCase().startsWith('http:') ? `https${queryUrl.slice(4)}` : queryUrl; - axios - .get(queryUrl, { responseType: 'blob' }) - .then((response) => { - if (response.data.type === 'text/xml') { - resolve(null); - } - resolve(readImage(Cast(response.data))); - }) - .catch(() => resolve(null)); - } else resolve(null); - }); - return promisedImage; - } - - /** *************************************************************************************************************************** - * Get the legend info of a style. - * - * @param {OgcWmsLayerEntryConfig} layerConfig layer configuration. - * @param {number} position index number of style to get - * - * @returns {Promise} The legend of the style. - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - async #getStyleLegend(layerConfig: OgcWmsLayerEntryConfig, wmsStyle: string): Promise { - try { - const chosenStyle: string | undefined = wmsStyle; - let styleLegend: TypeWmsLegendStyle; - const styleLegendImage = await this.#getLegendImage(layerConfig!, chosenStyle); - if (!styleLegendImage) { - styleLegend = { - name: wmsStyle, - legend: null, - }; - return styleLegend; - } - - const styleImage = await loadImage(styleLegendImage as string); - if (styleImage) { - const drawingCanvas = document.createElement('canvas'); - drawingCanvas.width = styleImage.width; - drawingCanvas.height = styleImage.height; - const drawingContext = drawingCanvas.getContext('2d')!; - drawingContext.drawImage(styleImage, 0, 0); - styleLegend = { - name: wmsStyle, - legend: drawingCanvas, - }; - return styleLegend; - } - - return { - name: wmsStyle, - legend: null, - } as TypeWmsLegendStyle; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (error) { - return { - name: wmsStyle, - legend: null, - } as TypeWmsLegendStyle; - } - } - - /** *************************************************************************************************************************** - * Return the legend of the layer. This routine return null when the layerPath specified is not found. If the legend can't be - * read, the legend property of the object returned will be null. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The legend of the layer or null. - */ - // GV Layers Refactoring - Obsolete (in layers) - override async getLegend(layerPath: string): Promise { - try { - // Get the layer config in a loaded phase - const layerConfig = this.getLayerConfig(layerPath) as OgcWmsLayerEntryConfig; - - let legend: TypeWmsLegend; - const legendImage = await this.#getLegendImage(layerConfig!); - const styleLegends: TypeWmsLegendStyle[] = []; - - // 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 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). - // TO.DOCONT: If we remove the await, be mindful of that (maybe add this remark in the TODO?). - // TO.DOCONT: In any case, I'd suggest to remove the await indeed, for performance, and rewrite the code to make it work (probably not 2 distinct arrays). - // eslint-disable-next-line no-await-in-loop - const styleLegend = await this.#getStyleLegend(layerConfig!, this.WMSStyles[i]); - styleLegends.push(styleLegend); - } - } - - if (legendImage) { - const image = await loadImage(legendImage as string); - if (image) { - const drawingCanvas = document.createElement('canvas'); - drawingCanvas.width = image.width; - drawingCanvas.height = image.height; - const drawingContext = drawingCanvas.getContext('2d')!; - drawingContext.drawImage(image, 0, 0); - legend = { - type: CONST_LAYER_TYPES.WMS, - legend: drawingCanvas, - styles: styleLegends.length ? styleLegends : undefined, - }; - return legend; - } - } - - legend = { - type: CONST_LAYER_TYPES.WMS, - legend: null, - styles: styleLegends.length > 1 ? styleLegends : undefined, - }; - return legend; - } catch (error) { - // Log - logger.logError('wms.getLegend()\n', error); - return null; - } - } - - /** *************************************************************************************************************************** - * Translate the get feature information result set to the TypeFeatureInfoEntry[] used by GeoView. - * - * @param {TypeJsonObject} featureMember An object formatted using the query syntax. - * @param {Coordinate} clickCoordinate The coordinate where the user has clicked. - * - * @returns {TypeFeatureInfoEntry[]} The feature info table. - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - #formatWmsFeatureInfoResult(featureMember: TypeJsonObject, clickCoordinate: Coordinate): TypeFeatureInfoEntry[] { - const queryResult: TypeFeatureInfoEntry[] = []; - - let featureKeyCounter = 0; - let fieldKeyCounter = 0; - const featureInfoEntry: TypeFeatureInfoEntry = { - // feature key for building the data-grid - featureKey: featureKeyCounter++, - geoviewLayerType: this.type, - extent: [clickCoordinate[0], clickCoordinate[1], clickCoordinate[0], clickCoordinate[1]], - geometry: null, - featureIcon: document.createElement('canvas'), - fieldInfo: {}, - nameField: null, - }; - const createFieldEntries = (entry: TypeJsonObject, prefix = ''): void => { - const keys = Object.keys(entry); - keys.forEach((key) => { - if (!key.endsWith('Geometry') && !key.startsWith('@')) { - const splitedKey = key.split(':'); - const fieldName = splitedKey.slice(-1)[0]; - if (typeof entry[key] === 'object') { - if ('#text' in entry[key]) - featureInfoEntry.fieldInfo[`${prefix}${prefix ? '.' : ''}${fieldName}`] = { - fieldKey: fieldKeyCounter++, - value: entry[key]['#text'] as string, - dataType: 'string', - alias: `${prefix}${prefix ? '.' : ''}${fieldName}`, - domain: null, - }; - else createFieldEntries(entry[key], fieldName); - } else - featureInfoEntry.fieldInfo[`${prefix}${prefix ? '.' : ''}${fieldName}`] = { - fieldKey: fieldKeyCounter++, - value: entry[key] as string, - dataType: 'string', - alias: `${prefix}${prefix ? '.' : ''}${fieldName}`, - domain: null, - }; - } - }); - }; - createFieldEntries(featureMember); - queryResult.push(featureInfoEntry); - - return queryResult; - } - - /** *************************************************************************************************************************** - * Return the attribute of an object that ends with the specified ending string or null if not found. - * - * @param {TypeJsonObject} jsonObject The object that is supposed to have the needed attribute. - * @param {string} attribute The attribute searched. - * - * @returns {TypeJsonObject | undefined} The promised feature info table. - * @private - */ - // GV Layers Refactoring - Obsolete (in layers) - static #getAttribute(jsonObject: TypeJsonObject, attributeEnding: string): TypeJsonObject | undefined { - const keyFound = Object.keys(jsonObject).find((key) => key.endsWith(attributeEnding)); - return keyFound ? jsonObject[keyFound] : undefined; - } - /** *************************************************************************************************************************** * Set the style to be used by the wms layer. This methode does nothing if the layer path can't be found. * @@ -1037,153 +622,4 @@ export class WMS extends AbstractGeoViewRaster { // TODO: Verify if we can apply more than one style at the same time since the parameter name is STYLES if (layer) layer.getSource()?.updateParams({ STYLES: wmsStyleId }); } - - /** - * Overrides when the layer gets in loaded status. - */ - // GV Layers Refactoring - Obsolete (in layers) - override onLoaded(layerConfig: AbstractBaseLayerEntryConfig): void { - // Call parent - super.onLoaded(layerConfig); - - // Apply view filter immediately - this.applyViewFilter(layerConfig.layerPath, (layerConfig as OgcWmsLayerEntryConfig).layerFilter || ''); - } - - /** *************************************************************************************************************************** - * Applies a view filter to the layer. When the combineLegendFilter flag is false, the filter paramater is used alone to display - * the features. Otherwise, the legend filter and the filter parameter are combined together to define the view filter. The - * legend filters are derived from the uniqueValue or classBreaks style of the layer. When the layer config is invalid, nothing - * is done. - * TODO ! The combination of the legend filter and the dimension filter probably does not apply to WMS. The code can be simplified. - * - * @param {string} layerPath The layer path to the layer's configuration. - * @param {string} filter An optional filter to be used in place of the getViewFilter value. - * @param {boolean} combineLegendFilter Flag used to combine the legend filter and the filter together (default: true) - */ - // GV Layers Refactoring - Obsolete (in layers) - applyViewFilter(layerPath: string, filter: string, combineLegendFilter = true): void { - const layerConfig = this.getLayerConfig(layerPath) as OgcWmsLayerEntryConfig; - const olLayer = this.getOLLayer(layerPath) as ImageLayer; - - // Log - logger.logTraceCore('WMS - applyViewFilter', layerPath); - - // Get source - const source = olLayer.getSource(); - if (source) { - let filterValueToUse = filter; - layerConfig.legendFilterIsOff = !combineLegendFilter; - if (combineLegendFilter) layerConfig.layerFilter = filter; - - if (filterValueToUse) { - filterValueToUse = filterValueToUse.replaceAll(/\s{2,}/g, ' ').trim(); - const queryElements = filterValueToUse.split(/(?<=\b)\s*=/); - const dimension = queryElements[0].trim(); - filterValueToUse = queryElements[1].trim(); - - // Convert date constants using the externalFragmentsOrder derived from the externalDateFormat - const searchDateEntry = [ - ...`${filterValueToUse} `.matchAll(/(?<=^date\b\s')[\d/\-T\s:+Z]{4,25}(?=')|(?<=[(\s]date\b\s')[\d/\-T\s:+Z]{4,25}(?=')/gi), - ]; - searchDateEntry.reverse(); - searchDateEntry.forEach((dateFound) => { - // If the date has a time zone, keep it as is, otherwise reverse its time zone by changing its sign - const reverseTimeZone = ![20, 25].includes(dateFound[0].length); - const reformattedDate = DateMgt.applyInputDateFormat(dateFound[0], this.externalFragmentsOrder, reverseTimeZone); - filterValueToUse = `${filterValueToUse!.slice(0, dateFound.index! - 6)}${reformattedDate}${filterValueToUse!.slice( - dateFound.index! + dateFound[0].length + 2 - )}`; - }); - source.updateParams({ [dimension]: filterValueToUse.replace(/\s*/g, '') }); - olLayer.changed(); - - // Emit event - this.emitLayerFilterApplied({ - layerPath, - filter: filterValueToUse, - }); - } - } - } - - /** *************************************************************************************************************************** - * Get the bounds of the layer represented in the layerConfig pointed to by the layerPath, returns updated bounds - * - * @param {string} layerPath The Layer path to the layer's configuration. - * - * @returns {Extent | undefined} The new layer bounding box. - */ - // GV Layers Refactoring - Obsolete (in layers) - override getBounds(layerPath: string): Extent | undefined { - // Get the layer config - const layerConfig = this.getLayerConfig(layerPath); - - // Get the source projection - const sourceProjection = this.getSourceProjection(layerPath); - - // Get the layer config bounds - let layerConfigBounds = layerConfig?.initialSettings?.bounds; - - // If layer bounds were found, project - if (layerConfigBounds) { - // Make sure we're in the map projection. Always EPSG:4326 when coming from our configuration. - layerConfigBounds = this.getMapViewer().convertExtentFromProjToMapProj(layerConfigBounds, 'EPSG:4326'); - } - - // Get the layer bounds from metadata - const metadataExtent = this.#getBoundsExtentFromMetadata(sourceProjection?.getCode() || ''); - - // If any - let layerBounds; - if (metadataExtent) { - const [metadataProj, metadataBounds] = metadataExtent; - layerBounds = this.getMapViewer().convertExtentFromProjToMapProj(metadataBounds, metadataProj); - } - - // If both layer config had bounds and layer has real bounds, take the intersection between them - if (layerConfigBounds && layerBounds) layerBounds = getExtentIntersection(layerBounds, layerConfigBounds); - - // Validate - layerBounds = validateExtentWhenDefined(layerBounds, this.getMapViewer().getProjection().getCode()); - - // Return the calculated layer bounds (favor metadataExtent, but if things are going bad, pick config bounds at least) - return layerBounds || layerConfigBounds; - } - - /** - * Gets the bounds as defined in the metadata, favoring the ones in the given projection or returning the first one found - * @param {string} projection - The projection to favor when looking for the bounds inside the metadata - * @returns {[string, Extent]} The projection and its extent as provided by the metadata - */ - #getBoundsExtentFromMetadata(projection: string): [string, Extent] | undefined { - // Get the bounding boxes in the metadata - const boundingBoxes = this.metadata?.Capability.Layer.BoundingBox as TypeJsonArray; - - // If found any - if (boundingBoxes) { - // Find the one with the right projection - for (let i = 0; i < (boundingBoxes.length as number); i++) { - if (boundingBoxes[i].crs === projection) - return [ - boundingBoxes[i].crs as string, - // TODO: Check - Is it always in that order, 1, 0, 3, 2 or does that depend on the projection? - [boundingBoxes[i].extent[1], boundingBoxes[i].extent[0], boundingBoxes[i].extent[3], boundingBoxes[i].extent[2]] as Extent, - ]; - } - - // Not found. If any - if (boundingBoxes.length > 0) { - // Take the first one and return the bounds and projection - return [ - boundingBoxes[0].crs as string, - // TODO: Check - Is it always in that order, 1, 0, 3, 2 or does that depend on the projection? - [boundingBoxes[0].extent[1], boundingBoxes[0].extent[0], boundingBoxes[0].extent[3], boundingBoxes[0].extent[2]] as Extent, - ]; - } - } - - // Really not found - return undefined; - } } diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/xyz-tiles.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/xyz-tiles.ts index 46fbac200ea..b5439e41f5a 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/xyz-tiles.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/xyz-tiles.ts @@ -2,7 +2,6 @@ import BaseLayer from 'ol/layer/Base'; import TileLayer from 'ol/layer/Tile'; import XYZ, { Options as SourceOptions } from 'ol/source/XYZ'; import TileGrid, { Options as TileGridOptions } from 'ol/tilegrid/TileGrid'; -import { Extent } from 'ol/extent'; import defaultsDeep from 'lodash/defaultsDeep'; @@ -13,13 +12,11 @@ import { TypeSourceTileInitialConfig, TypeGeoviewLayerConfig, layerEntryIsGroupLayer, - TypeFeatureInfoLayerConfig, } from '@/geo/map/map-schema-types'; import { Cast, toJsonObject } from '@/core/types/global-types'; import { validateExtentWhenDefined } from '@/geo/utils/utilities'; import { XYZTilesLayerEntryConfig } from '@/core/utils/config/validation-classes/raster-validation-classes/xyz-layer-entry-config'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; -import { TypeOutfieldsType } from '@/api/config/types/map-schema-types'; // ? Do we keep this TODO ? Dynamic parameters can be placed on the dataAccessPath and initial settings can be used on xyz-tiles. // TODO: Implement method to validate XYZ tile service @@ -98,21 +95,6 @@ export class XYZTiles extends AbstractGeoViewRaster { super(CONST_LAYER_TYPES.XYZ_TILES, layerConfig, mapId); } - /** *************************************************************************************************************************** - * Extract the type of the specified field from the metadata. If the type can not be found, return 'string'. - * - * @param {string} fieldName field name for which we want to get the type. - * @param {TypeLayerEntryConfig} layerConfig layer configuration. - * - * @returns {TypeOutfieldsType} The type of the field. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFieldType(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): TypeOutfieldsType { - const fieldDefinitions = this.getLayerMetadata(layerConfig.layerPath).source.featureInfo as unknown as TypeFeatureInfoLayerConfig; - const outFieldEntry = fieldDefinitions.outfields?.find((fieldDefinition) => fieldDefinition.name === fieldName); - return outFieldEntry?.type || 'string'; - } - /** *************************************************************************************************************************** * This method recursively validates the layer configuration entries by filtering and reporting invalid layers. If needed, * extra configuration may be done here. @@ -248,30 +230,4 @@ export class XYZTiles extends AbstractGeoViewRaster { } return Promise.resolve(layerConfig); } - - /** *************************************************************************************************************************** - * Get the bounds of the layer represented in the layerConfig pointed to by the layerPath, returns updated bounds - * - * @param {string} layerPath The Layer path to the layer's configuration. - * - * @returns {Extent | undefined} The new layer bounding box. - */ - // GV Layers Refactoring - Obsolete (in layers) - override getBounds(layerPath: string): Extent | undefined { - // Get the layer - const layer = this.getOLLayer(layerPath) as TileLayer | undefined; - - // Get the source projection - const sourceProjection = this.getSourceProjection(layerPath); - - // Get the layer bounds - let sourceExtent = layer?.getSource()?.getTileGrid()?.getExtent(); - if (sourceExtent) { - // Make sure we're in the map projection - sourceExtent = this.getMapViewer().convertExtentFromProjToMapProj(sourceExtent, sourceProjection); - } - - // Return the calculated layer bounds - return sourceExtent; - } } diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts b/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts index 53e2fa2f71c..a497a889fb2 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/vector/abstract-geoview-vector.ts @@ -9,33 +9,22 @@ import { all, bbox } from 'ol/loadingstrategy'; import { ReadOptions } from 'ol/format/Feature'; import BaseLayer from 'ol/layer/Base'; import LayerGroup from 'ol/layer/Group'; -import { Coordinate } from 'ol/coordinate'; -import { Extent } from 'ol/extent'; -import { Pixel } from 'ol/pixel'; import { ProjectionLike } from 'ol/proj'; import { Point } from 'ol/geom'; import { getUid } from 'ol/util'; -import { TypeOutfields, TypeOutfieldsType } from '@config/types/map-schema-types'; +import { TypeOutfields } from '@config/types/map-schema-types'; import { api } from '@/app'; import { AbstractGeoViewLayer, CONST_LAYER_TYPES } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; -import { - TypeBaseSourceVectorInitialConfig, - TypeFeatureInfoEntry, - TypeFeatureInfoLayerConfig, - TypeLayerEntryConfig, -} from '@/geo/map/map-schema-types'; +import { TypeBaseSourceVectorInitialConfig, TypeLayerEntryConfig } from '@/geo/map/map-schema-types'; import { DateMgt } from '@/core/utils/date-mgt'; -import { NodeType } from '@/geo/utils/renderer/geoview-renderer-types'; import { VECTOR_LAYER } from '@/core/utils/constant'; import { logger } from '@/core/utils/logger'; import { VectorLayerEntryConfig } from '@/core/utils/config/validation-classes/vector-layer-entry-config'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; -import { analyzeLayerFilter } from '@/geo/utils/renderer/geoview-renderer'; import { MapEventProcessor } from '@/api/event-processors/event-processor-children/map-event-processor'; import { Projection } from '@/geo/utils/projection'; -import { getMinOrMaxExtents } from '@/geo/utils/utilities'; /* ******************************************************************************************************************************* * AbstractGeoViewVector types @@ -88,23 +77,6 @@ export abstract class AbstractGeoViewVector extends AbstractGeoViewLayer { // GV Layers Refactoring - Obsolete (in config?) protected abstract override validateListOfLayerEntryConfig(listOfLayerEntryConfig: TypeLayerEntryConfig[]): void; - /** *************************************************************************************************************************** - * Extract the type of the specified field from the metadata. If the type can not be found, return 'string'. - * - * @param {string} fieldName field name for which we want to get the type. - * @param {AbstractBaseLayerEntryConfig} layerConfig layer configuration. - * - * @returns {TypeOutfieldsType} The type of the field. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFieldType(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): TypeOutfieldsType { - const fieldDefinitions = - (this.getLayerMetadata(layerConfig.layerPath)?.source.featureInfo as unknown as TypeFeatureInfoLayerConfig) || - layerConfig.source?.featureInfo; - const outFieldEntry = fieldDefinitions.outfields?.find((fieldDefinition) => fieldDefinition.name === fieldName); - return outFieldEntry?.type || 'string'; - } - /** *************************************************************************************************************************** * This method creates a GeoView layer using the definition provided in the layerConfig parameter. * @@ -334,6 +306,8 @@ export abstract class AbstractGeoViewVector extends AbstractGeoViewLayer { // GV Layers Refactoring - Obsolete (this is bridging between config and layers, okay) protected createVectorLayer(layerConfig: VectorLayerEntryConfig, vectorSource: VectorSource): VectorLayer { // GV Time to request an OpenLayers layer! + // TODO: There may be some additional enhancements to be done now that we can notice how emitLayerRequesting and emitLayerCreation are getting "close" to each other. + // TODO.CONT: This whole will be removed when migration to config api... do we invest time in it? const requestResult = this.emitLayerRequesting({ config: layerConfig, source: vectorSource }); // If any response @@ -352,143 +326,6 @@ export abstract class AbstractGeoViewVector extends AbstractGeoViewLayer { return olLayer; } - /** *************************************************************************************************************************** - * Return feature information for all the features stored in the layer. - * - * @param {string} layerPath The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override async getAllFeatureInfo(layerPath: string): Promise { - try { - // Get the layer config in a loaded phase - const layerConfig = this.getLayerConfig(layerPath) as VectorLayerEntryConfig; - const layer = this.getOLLayer(layerPath) as VectorLayer; - const features = layer.getSource()!.getFeatures(); - const arrayOfFeatureInfoEntries = await this.formatFeatureInfoResult(features, layerConfig); - return arrayOfFeatureInfoEntries; - } catch (error) { - // Log - logger.logError('abstract-geoview-vector.getAllFeatureInfo()\n', error); - return null; - } - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided Pixel. - * - * @param {Coordinate} location - The pixel coordinate that will be used by the query. - * @param {string} layerPath - The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table or null if an error occured. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFeatureInfoAtPixel(location: Pixel, layerPath: string): Promise { - try { - // Get the layer source - const layerSource = this.getOLLayer(layerPath)?.get('source'); - - // Prepare a filter by layer to know on which layer we want to query features - const layerFilter = (layerCandidate: BaseLayer): boolean => { - // We know it's the right layer to query on if the source is the same as the current layer - const candidateSource = layerCandidate.get('source'); - return layerSource && candidateSource && layerSource === candidateSource; - }; - - // Query the map using the layer filter and a hit tolerance - const features = this.getMapViewer().map.getFeaturesAtPixel(location, { hitTolerance: this.hitTolerance, layerFilter }) as Feature[]; - - // Format and return the features - return this.formatFeatureInfoResult(features, this.getLayerConfig(layerPath) as VectorLayerEntryConfig); - } catch (error) { - // Log - logger.logError('abstract-geoview-vector.getFeatureInfoAtPixel()\n', error); - return Promise.resolve(null); - } - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided projected coordinate. - * - * @param {Coordinate} location - The pixel coordinate that will be used by the query. - * @param {string} layerPath - The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFeatureInfoAtCoordinate( - location: Coordinate, - layerPath: string - ): Promise { - // Redirect to getFeatureInfoAtPixel - return this.getFeatureInfoAtPixel(this.getMapViewer().map.getPixelFromCoordinate(location), layerPath); - } - - /** *************************************************************************************************************************** - * Return feature information for all the features around the provided longitude latitude. - * - * @param {Coordinate} lnglat - The coordinate that will be used by the query. - * @param {string} layerPath - The layer path to the layer's configuration. - * - * @returns {Promise} The feature info table. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFeatureInfoAtLongLat(lnglat: Coordinate, layerPath: string): Promise { - // Convert Coordinates LngLat to map projection - const projCoordinate = this.getMapViewer().convertCoordinateLngLatToMapProj(lnglat); - - // Redirect to getFeatureInfoAtPixel - return this.getFeatureInfoAtPixel(this.getMapViewer().map.getPixelFromCoordinate(projCoordinate), layerPath); - } - - /** *************************************************************************************************************************** - * Get the bounds of the layer represented in the layerConfig pointed to by the layerPath, returns updated bounds - * - * @param {string} layerPath The Layer path to the layer's configuration. - * - * @returns {Extent | undefined} The new layer bounding box. - */ - // GV Layers Refactoring - Obsolete (in layers) - override getBounds(layerPath: string): Extent | undefined { - const layer = this.getOLLayer(layerPath) as VectorLayer | undefined; - const layerBounds = layer?.getSource()?.getExtent(); - - // Return the calculated layer bounds - return layerBounds; - } - - /** - * Gets the extent of an array of features. - * @param {string} layerPath - The layer path. - * @param {string[]} objectIds - The uids of the features to calculate the extent from. - * @returns {Promise} The extent of the features, if available. - */ - // Added eslint-disable here, because we do want to override this method in children and keep 'this'. - // eslint-disable-next-line @typescript-eslint/class-methods-use-this - override getExtentFromFeatures(layerPath: string, objectIds: string[]): Promise { - // Get array of features - const requestedFeatures = objectIds.map((id) => (this.getOLLayer(layerPath) as VectorLayer).getSource()?.getFeatureById(id)); - - if (requestedFeatures) { - // Determine max extent from features - let calculatedExtent: Extent | undefined; - requestedFeatures.forEach((feature) => { - if (feature?.getGeometry()) { - const extent = feature.getGeometry()?.getExtent(); - if (extent) { - // If calculatedExtent has not been defined, set it to extent - if (!calculatedExtent) calculatedExtent = extent; - else getMinOrMaxExtents(calculatedExtent, extent); - } - } - }); - - return Promise.resolve(calculatedExtent); - } - return Promise.resolve(undefined); - } - /** * Return the vector layer as a GeoJSON object * @param {string} layerPath - Layer path to get GeoJSON @@ -507,82 +344,6 @@ export abstract class AbstractGeoViewVector extends AbstractGeoViewLayer { return JSON.parse(geoJsonStr); } - /** - * Overrides when the layer gets in loaded status. - */ - // GV Layers Refactoring - Obsolete (in layers) - override onLoaded(layerConfig: AbstractBaseLayerEntryConfig): void { - // Call parent - super.onLoaded(layerConfig); - - // Apply view filter immediately - this.applyViewFilter(layerConfig.layerPath, (layerConfig as VectorLayerEntryConfig).layerFilter || ''); - } - - /** *************************************************************************************************************************** - * Applies a view filter to the layer. When the combineLegendFilter flag is false, the filter parameter is used alone to display - * the features. Otherwise, the legend filter and the filter parameter are combined together to define the view filter. The - * legend filters are derived from the uniqueValue or classBreaks style of the layer. When the layer config is invalid, nothing - * is done. - * - * @param {string} layerPath The layer path to the layer's configuration. - * @param {string} filter A filter to be used in place of the getViewFilter value. - * @param {boolean} combineLegendFilter Flag used to combine the legend filter and the filter together (default: true) - */ - // GV Layers Refactoring - Obsolete (in layers) - applyViewFilter(layerPath: string, filter: string, combineLegendFilter: boolean = true): void { - // Log - logger.logTraceCore('ABSTRACT-GEOVIEW-VECTOR - applyViewFilter', layerPath); - - const layerConfig = this.getLayerConfig(layerPath) as VectorLayerEntryConfig; - const olLayer = this.getOLLayer(layerPath); - - let filterValueToUse = filter.replaceAll(/\s{2,}/g, ' ').trim(); - layerConfig.legendFilterIsOff = !combineLegendFilter; - if (combineLegendFilter) layerConfig.layerFilter = filter; - - // Convert date constants using the externalFragmentsOrder derived from the externalDateFormat - // TODO: Standardize the regex across all layer types - // OLD REGEX, not working anymore, test before standardization - // ...`${filterValueToUse?.replaceAll(/\s{2,}/g, ' ').trim()} `.matchAll( - // /(?<=^date\b\s')[\d/\-T\s:+Z]{4,25}(?=')|(?<=[(\s]date\b\s')[\d/\-T\s:+Z]{4,25}(?=')/gi - // ), - const searchDateEntry = [ - ...filterValueToUse.matchAll( - /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/gi - ), - ]; - - searchDateEntry.reverse(); - searchDateEntry.forEach((dateFound) => { - // If the date has a time zone, keep it as is, otherwise reverse its time zone by changing its sign - const reverseTimeZone = ![20, 25].includes(dateFound[0].length); - const reformattedDate = DateMgt.applyInputDateFormat(dateFound[0], this.externalFragmentsOrder, reverseTimeZone); - filterValueToUse = `${filterValueToUse!.slice(0, dateFound.index)}${reformattedDate}${filterValueToUse!.slice( - dateFound.index! + dateFound[0].length - )}`; - }); - - try { - const filterEquation = analyzeLayerFilter([{ nodeType: NodeType.unprocessedNode, nodeValue: filterValueToUse }]); - layerConfig.filterEquation = filterEquation; - } catch (error) { - throw new Error( - `Invalid vector layer filter (${(error as { message: string }).message}).\nfilter = ${this.getLayerFilter( - layerPath - )}\ninternal filter = ${filterValueToUse}` - ); - } - - olLayer?.changed(); - - // Emit event - this.emitLayerFilterApplied({ - layerPath, - filter: filterValueToUse, - }); - } - /** *************************************************************************************************************************** * Converts csv text to feature array. * diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/vector/esri-feature.ts b/packages/geoview-core/src/geo/layer/geoview-layers/vector/esri-feature.ts index 03f8fd3362b..24be8ac79c6 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/vector/esri-feature.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/vector/esri-feature.ts @@ -8,18 +8,10 @@ import { AbstractGeoViewVector } from './abstract-geoview-vector'; import { TypeJsonObject } from '@/core/types/global-types'; import { EsriFeatureLayerEntryConfig } from '@/core/utils/config/validation-classes/vector-validation-classes/esri-feature-layer-entry-config'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; -import { - TypeLayerEntryConfig, - TypeVectorSourceInitialConfig, - TypeGeoviewLayerConfig, - codedValueType, - rangeDomainType, -} from '@/geo/map/map-schema-types'; +import { TypeLayerEntryConfig, TypeVectorSourceInitialConfig, TypeGeoviewLayerConfig } from '@/geo/map/map-schema-types'; import { AbstractGeoViewLayer, CONST_LAYER_TYPES } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; import { - commonGetFieldDomain, - commonGetFieldType, commonfetchServiceMetadata, commonProcessFeatureInfoConfig, commonProcessInitialSettings, @@ -27,7 +19,6 @@ import { commonProcessTemporalDimension, commonValidateListOfLayerEntryConfig, } from '@/geo/layer/geoview-layers/esri-layer-common'; -import { TypeOutfieldsType } from '@/api/config/types/map-schema-types'; export interface TypeSourceEsriFeatureInitialConfig extends Omit { format: 'EsriJSON'; @@ -141,32 +132,6 @@ export class EsriFeature extends AbstractGeoViewVector { return false; } - /** *************************************************************************************************************************** - * Extract the type of the specified field from the metadata. If the type can not be found, return 'string'. - * - * @param {string} fieldName field name for which we want to get the type. - * @param {TypeLayerEntryConfig} layerConfig layer configuration. - * - * @returns {TypeOutfieldsType} The type of the field. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFieldType(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): TypeOutfieldsType { - return commonGetFieldType(this, fieldName, layerConfig); - } - - /** *************************************************************************************************************************** - * Return the domain of the specified field. - * - * @param {string} fieldName field name for which we want to get the domain. - * @param {TypeLayerEntryConfig} layerConfig layer configuration. - * - * @returns {null | codedValueType | rangeDomainType} The domain of the field. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFieldDomain(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): null | codedValueType | rangeDomainType { - return commonGetFieldDomain(this, fieldName, layerConfig); - } - /** *************************************************************************************************************************** * This method will create a Geoview temporal dimension if it exist in the service metadata * @param {TypeJsonObject} esriTimeDimension The ESRI time dimension object diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/vector/geojson.ts b/packages/geoview-core/src/geo/layer/geoview-layers/vector/geojson.ts index 6f065f6d6e2..f3dfc028548 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/vector/geojson.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/vector/geojson.ts @@ -8,8 +8,6 @@ import Feature from 'ol/Feature'; import defaultsDeep from 'lodash/defaultsDeep'; -import VectorLayer from 'ol/layer/Vector'; -import { GeoJSONObject } from 'ol/format/GeoJSON'; import { AbstractGeoViewLayer, CONST_LAYER_TYPES } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; import { AbstractGeoViewVector } from '@/geo/layer/geoview-layers/vector/abstract-geoview-vector'; import { @@ -21,14 +19,9 @@ import { } from '@/geo/map/map-schema-types'; import { validateExtentWhenDefined } from '@/geo/utils/utilities'; import { Cast, TypeJsonObject } from '@/core/types/global-types'; -import { logger } from '@/core/utils/logger'; import { GeoJSONLayerEntryConfig } from '@/core/utils/config/validation-classes/vector-validation-classes/geojson-layer-entry-config'; import { VectorLayerEntryConfig } from '@/core/utils/config/validation-classes/vector-layer-entry-config'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; -import { Projection } from '@/geo/utils/projection'; -import { LegendEventProcessor } from '@/api/event-processors/event-processor-children/legend-event-processor'; -import { DataTableEventProcessor } from '@/api/event-processors/event-processor-children/data-table-event-processor'; -import { FeatureInfoEventProcessor } from '@/api/event-processors/event-processor-children/feature-info-event-processor'; export interface TypeSourceGeoJSONInitialConfig extends Omit { format: 'GeoJSON'; @@ -238,47 +231,4 @@ export class GeoJSON extends AbstractGeoViewVector { const vectorSource = super.createVectorSource(layerConfig, sourceOptions, readOptions); return vectorSource; } - - /** *************************************************************************************************************************** - * Override the features of a geojson layer with new geojson. - * @param {string} layerPath - The path of the layer to override. - * @param {GeoJSONObject | string} geojson - The new geoJSON. - */ - overrideGeojsonSource(layerPath: string, geojson: GeoJSONObject | string): void { - // Convert string to geoJSON if necessary - const geojsonObject = typeof geojson === 'string' ? JSON.parse(geojson) : geojson; - - // Create features from geoJSON - const dataProjection = geojsonObject.crs?.properties?.name || Projection.PROJECTION_NAMES.LNGLAT; - const features = new FormatGeoJSON().readFeatures(geojsonObject, { - dataProjection, - featureProjection: this.getMapViewer().getProjection(), - }); - - const olLayer = this.getOLLayer(layerPath) as VectorLayer>; - - if (olLayer && features.length) { - // Remove current features and add new ones - olLayer!.getSource()?.clear(); - olLayer!.getSource()?.addFeatures(features); - olLayer.changed(); - - // TODO: This is coupled with the processor. Maybe we should have a processor event to trigger this and - // TO.DOCONT: keep this functio not tie with UI. - // Update the bounds in the store - const bounds = this.getBounds(layerPath); - if (bounds) { - LegendEventProcessor.setLayerBounds(this.mapId, layerPath, bounds); - } - - // Reset the feature info result set - FeatureInfoEventProcessor.resetResultSet(this.mapId, layerPath); - - // Update feature info - DataTableEventProcessor.triggerGetAllFeatureInfo(this.mapId, layerPath).catch((error) => { - // Log - logger.logPromiseFailed(`Update all feature info in overrideGeojsonSource failed for layer ${layerPath}`, error); - }); - } - } } diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/vector/ogc-feature.ts b/packages/geoview-core/src/geo/layer/geoview-layers/vector/ogc-feature.ts index f7b783a4e65..130ee645283 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/vector/ogc-feature.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/vector/ogc-feature.ts @@ -105,23 +105,6 @@ export class OgcFeature extends AbstractGeoViewVector { super(CONST_LAYER_TYPES.OGC_FEATURE, layerConfig, mapId); } - /** *************************************************************************************************************************** - * Extract the type of the specified field from the metadata. If the type can not be found, return 'string'. - * - * @param {string} fieldName field name for which we want to get the type. - * @param {AbstractBaseLayerEntryConfig} layerConfig layer configuration. - * - * @returns {'string' | 'date' | 'number'} The type of the field. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFieldType(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): 'string' | 'date' | 'number' { - const fieldDefinitions = this.getLayerMetadata(layerConfig.layerPath); - const fieldEntryType = (fieldDefinitions[fieldName].type as string).split(':').slice(-1)[0] as string; - if (fieldEntryType === 'date') return 'date'; - if (['int', 'number'].includes(fieldEntryType)) return 'number'; - return 'string'; - } - /** *************************************************************************************************************************** * This method reads the service metadata from the metadataAccessPath. * diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/vector/wfs.ts b/packages/geoview-core/src/geo/layer/geoview-layers/vector/wfs.ts index b0586335576..9ed10d4ed3f 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/vector/wfs.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/vector/wfs.ts @@ -26,7 +26,7 @@ import { WfsLayerEntryConfig } from '@/core/utils/config/validation-classes/vect import { VectorLayerEntryConfig } from '@/core/utils/config/validation-classes/vector-layer-entry-config'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; import { validateExtentWhenDefined } from '@/geo/utils/utilities'; -import { TypeOutfields } from '@/api/config/types/map-schema-types'; +import { TypeOutfields, TypeOutfieldsType } from '@/api/config/types/map-schema-types'; export interface TypeSourceWFSVectorInitialConfig extends TypeVectorSourceInitialConfig { format: 'WFS'; @@ -99,25 +99,6 @@ export class WFS extends AbstractGeoViewVector { super(CONST_LAYER_TYPES.WFS, layerConfig, mapId); } - /** *************************************************************************************************************************** - * Extract the type of the specified field from the metadata. If the type can not be found, return 'string'. - * - * @param {string} fieldName field name for which we want to get the type. - * @param {AbstractBaseLayerEntryConfig} layerConfig layer configuration. - * - * @returns {'string' | 'date' | 'number'} The type of the field. - */ - // GV Layers Refactoring - Obsolete (in layers) - protected override getFieldType(fieldName: string, layerConfig: AbstractBaseLayerEntryConfig): 'string' | 'date' | 'number' { - const fieldDefinitions = this.getLayerMetadata(layerConfig.layerPath) as TypeJsonArray; - const fieldDefinition = fieldDefinitions.find((metadataEntry) => metadataEntry.name === fieldName); - if (!fieldDefinition) return 'string'; - const fieldEntryType = (fieldDefinition.type as string).split(':').slice(-1)[0] as string; - if (fieldEntryType === 'date') return 'date'; - if (['int', 'number'].includes(fieldEntryType)) return 'number'; - return 'string'; - } - /** *************************************************************************************************************************** * This method reads the service metadata from the metadataAccessPath. * @@ -265,7 +246,7 @@ export class WFS extends AbstractGeoViewVector { const layerMetadata = (await (await fetch(describeFeatureUrl)).json()) as TypeJsonObject; if (Array.isArray(layerMetadata.featureTypes) && Array.isArray(layerMetadata.featureTypes[0].properties)) { this.setLayerMetadata(layerConfig.layerPath, layerMetadata.featureTypes[0].properties); - this.#processFeatureInfoConfig(layerMetadata.featureTypes[0].properties as TypeJsonArray, layerConfig); + WFS.#processFeatureInfoConfig(layerMetadata.featureTypes[0].properties as TypeJsonArray, layerConfig); } } else if (describeFeatureUrl && outputFormat.toUpperCase().includes('XML')) { const layerMetadata = (await (await fetch(describeFeatureUrl)).text()) as string; @@ -289,7 +270,7 @@ export class WFS extends AbstractGeoViewVector { }); this.setLayerMetadata(layerConfig.layerPath, featureTypeProperties as TypeJsonObject); - this.#processFeatureInfoConfig(featureTypeProperties as TypeJsonArray, layerConfig); + WFS.#processFeatureInfoConfig(featureTypeProperties as TypeJsonArray, layerConfig); } } } catch (error) { @@ -307,7 +288,7 @@ export class WFS extends AbstractGeoViewVector { * @private */ // GV Layers Refactoring - Obsolete (in config) - #processFeatureInfoConfig(fields: TypeJsonArray, layerConfig: VectorLayerEntryConfig): void { + static #processFeatureInfoConfig(fields: TypeJsonArray, layerConfig: VectorLayerEntryConfig): void { if (!layerConfig.source) layerConfig.source = {}; if (!layerConfig.source.featureInfo) layerConfig.source.featureInfo = { queryable: true }; @@ -322,7 +303,7 @@ export class WFS extends AbstractGeoViewVector { const newOutfield: TypeOutfields = { name: fieldEntry.name as string, alias: fieldEntry.name as string, - type: this.getFieldType(fieldEntry.name as string, layerConfig), + type: WFS.getFieldType(fieldEntry.name as string, layerConfig), domain: null, }; @@ -339,6 +320,17 @@ export class WFS extends AbstractGeoViewVector { layerConfig.source.featureInfo.nameField = layerConfig.source.featureInfo!.outfields[1].name; } + // Patch for field type only use for WFS + static getFieldType(fieldName: string, layerConfig: VectorLayerEntryConfig): TypeOutfieldsType { + const fieldDefinitions = layerConfig.getLayerMetadata() as TypeJsonArray; + const fieldDefinition = fieldDefinitions.find((metadataEntry) => metadataEntry.name === fieldName); + if (!fieldDefinition) return 'string'; + const fieldEntryType = (fieldDefinition.type as string).split(':').slice(-1)[0] as string; + if (fieldEntryType === 'date') return 'date'; + if (['int', 'number'].includes(fieldEntryType)) return 'number'; + return 'string'; + } + /** *************************************************************************************************************************** * Create a source configuration for the vector layer. * diff --git a/packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts b/packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts index 65081e6489b..46ab381da02 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts @@ -101,8 +101,7 @@ export abstract class AbstractBaseLayer { * @returns The layer status */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - getLayerStatus(layerPath: string): TypeLayerStatus { - // TODO: Refactor - After layers refactoring, remove the layerPath parameter here (gotta keep it in the signature for now for the layers-set active switch) + getLayerStatus(): TypeLayerStatus { // Take the layer status from the config return this.getLayerConfig()!.layerStatus; } @@ -112,8 +111,7 @@ export abstract class AbstractBaseLayer { * @returns The layer name */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - getLayerName(layerPath: string): string | undefined { - // TODO: Refactor - After layers refactoring, remove the layerPath parameter here (gotta keep it in the signature for now for the layers-set active switch) + getLayerName(): string | undefined { return this.#layerName; } @@ -121,10 +119,9 @@ export abstract class AbstractBaseLayer { * Sets the layer name * @param {string | undefined} name - The layer name */ - setLayerName(layerPath: string, name: string | undefined): void { - // TODO: Refactor - After layers refactoring, remove the layerPath parameter here (gotta keep it in the signature for now for the layers-set active switch) + setLayerName(name: string | undefined): void { this.#layerName = name; - this.#emitLayerNameChanged({ layerPath, layerName: name }); + this.#emitLayerNameChanged({ layerName: name }); } /** @@ -148,14 +145,13 @@ export abstract class AbstractBaseLayer { /** * Overridable function that gets the extent of an array of features. - * @param {string} layerPath - The layer path * @param {string[]} objectIds - The IDs of the features to calculate the extent from. * @returns {Promise} The extent of the features, if available */ // Added eslint-disable here, because we do want to override this method in children and keep 'this'. // eslint-disable-next-line @typescript-eslint/class-methods-use-this - getExtentFromFeatures(layerPath: string, objectIds: string[]): Promise { - logger.logError(`Feature geometry for ${objectIds} is unavailable from ${layerPath}`); + getExtentFromFeatures(objectIds: string[]): Promise { + logger.logError(`Feature geometry for ${objectIds} is unavailable from ${this.getLayerPath()}`); return Promise.resolve(undefined); } @@ -317,9 +313,6 @@ export abstract class AbstractBaseLayer { export type LayerNameChangedEvent = { // The new layer name. layerName?: string; - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done - // The layer path. - layerPath: string; }; /** 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 d22d327a240..35f29637f1b 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 @@ -94,11 +94,10 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer { } /** - * Gets the bounds of the layer represented in the layerConfig pointed to by the layerPath, returns updated bounds. + * Gets the bounds of the layer. * @returns {Extent} The layer bounding box. */ - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done - abstract getBounds(layerPath: string): Extent | undefined; + abstract getBounds(): Extent | undefined; /** * Initializes the GVLayer. This function checks if the source is ready and if so it calls onLoaded() to pursue initialization of the layer. @@ -152,8 +151,7 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer { * @returns The layer style */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - getStyle(layerPath: string): TypeLayerStyleConfig | undefined { - // TODO: Refactor - After layers refactoring, remove the layerPath parameter here (gotta keep it in the signature for now for the layers-set active switch) + getStyle(): TypeLayerStyleConfig | undefined { return this.#layerStyle; } @@ -161,10 +159,9 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer { * Sets the layer style * @param {TypeStyleConfig | undefined} style - The layer style */ - setStyle(layerPath: string, style: TypeLayerStyleConfig): void { - // TODO: Refactor - After layers refactoring, remove the layerPath parameter here (gotta keep it in the signature for now for the layers-set active switch) + setStyle(style: TypeLayerStyleConfig): void { this.#layerStyle = style; - this.#emitLayerStyleChanged({ style, layerPath }); + this.#emitLayerStyleChanged({ style }); } /** @@ -460,8 +457,8 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer { try { const legend: TypeLegend = { type: this.getLayerConfig().geoviewLayerConfig.geoviewLayerType, - styleConfig: this.getStyle(this.getLayerPath()), - legend: await getLegendStyles(this.getStyle(this.getLayerPath())), + styleConfig: this.getStyle(), + legend: await getLegendStyles(this.getStyle()), }; return legend; } catch (error) { @@ -524,7 +521,7 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer { // GV: Call the function with layerConfig.legendFilterIsOff = true to force the feature to get is canvas // GV: If we don't, it will create canvas only for visible elements and because tables are stored feature will never get its canvas - getFeatureCanvas(featureNeedingItsCanvas, this.getStyle(layerConfig.layerPath)!, layerConfig.filterEquation, true, true) + getFeatureCanvas(featureNeedingItsCanvas, this.getStyle()!, layerConfig.filterEquation, true, true) .then((canvas) => { resolveCanvas({ feature: featureNeedingItsCanvas, canvas }); }) @@ -619,8 +616,7 @@ export abstract class AbstractGVLayer extends AbstractBaseLayer { * @returns {string | undefined} The filter associated to the layer or undefined. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - getLayerFilter(layerPath: string): string | undefined { - // TODO: Refactor - After layers refactoring, remove the layerPath parameter here (gotta keep it in the signature for now for the layers-set active switch) + getLayerFilter(): string | undefined { const layerConfig = this.getLayerConfig(); // TODO: Refactor to put the 'layerFilter' at the right place. Meanwhile, using `any` here // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -800,9 +796,6 @@ type LayerStyleChangedDelegate = EventDelegateBase { @@ -630,8 +630,8 @@ export class GVEsriDynamic extends AbstractGVRaster { // Call parent super.onLoaded(); - // Apply view filter immediately (no need to provide a layer path here so '' is sent (hybrid work)) - this.applyViewFilter('', this.getLayerConfig().layerFilter || ''); + // Apply view filter immediately + this.applyViewFilter(this.getLayerConfig().layerFilter || ''); } /** @@ -642,10 +642,9 @@ export class GVEsriDynamic extends AbstractGVRaster { * @param {string} filter - An optional filter to be used in place of the getViewFilter value. * @param {boolean} combineLegendFilter - Flag used to combine the legend filter and the filter together (default: true) */ - applyViewFilter(layerPath: string, filter: string, combineLegendFilter = true): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + applyViewFilter(filter: string, combineLegendFilter = true): void { // Log - logger.logTraceCore('GV-ESRI-DYNAMIC - applyViewFilter'); + logger.logTraceCore('GV-ESRI-DYNAMIC - applyViewFilter', this.getLayerPath()); const layerConfig = this.getLayerConfig(); const olLayer = this.getOLLayer() as ImageLayer; @@ -686,7 +685,6 @@ export class GVEsriDynamic extends AbstractGVRaster { // Emit event this.emitLayerFilterApplied({ - layerPath, filter: filterValueToUse, }); } @@ -696,8 +694,7 @@ export class GVEsriDynamic extends AbstractGVRaster { * @returns {Extent | undefined} The layer bounding box. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - override getBounds(layerPath: string): Extent | undefined { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + override getBounds(): Extent | undefined { // Get the metadata extent const metadataExtent = this.getMetadataExtent(); @@ -716,11 +713,11 @@ export class GVEsriDynamic extends AbstractGVRaster { /** * Sends a query to get ESRI Dynamic feature geometries and calculates an extent from them. - * @param {string} layerPath - The layer path. * @param {string[]} objectIds - The IDs of the features to calculate the extent from. + * @param {string} outfield - ID field to return for services that require a value in outfields. * @returns {Promise} The extent of the features, if available. */ - override async getExtentFromFeatures(layerPath: string, objectIds: string[], outfield?: string): Promise { + override async getExtentFromFeatures(objectIds: string[], outfield?: string): Promise { // Get url for service from layer entry config const layerEntryConfig = this.getLayerConfig(); const serviceMetaData = layerEntryConfig.getServiceMetadata() as TypeJsonObject; 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 736db7a61fa..ab58078efff 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 @@ -119,7 +119,7 @@ export class GVEsriImage extends AbstractGVRaster { if (!legendInfo) { const legend: TypeLegend = { type: CONST_LAYER_TYPES.ESRI_IMAGE, - styleConfig: this.getStyle(layerConfig.layerPath), + styleConfig: this.getStyle(), legend: null, }; return legend; @@ -152,12 +152,12 @@ export class GVEsriImage extends AbstractGVRaster { // TODO: Refactor - Find a better place to set the style than in a getter or rename this function like another TODO suggests // Set the style - this.setStyle(layerConfig.layerPath, styleConfig); + this.setStyle(styleConfig); const legend: TypeLegend = { type: CONST_LAYER_TYPES.ESRI_IMAGE, styleConfig, - legend: await getLegendStyles(this.getStyle(layerConfig.layerPath)), + legend: await getLegendStyles(this.getStyle()), }; return legend; } catch (error) { @@ -173,8 +173,8 @@ export class GVEsriImage extends AbstractGVRaster { // Call parent super.onLoaded(); - // Apply view filter immediately (no need to provide a layer path here so '' is sent (hybrid work)) - this.applyViewFilter('', this.getLayerConfig().layerFilter || ''); + // Apply view filter immediately + this.applyViewFilter(this.getLayerConfig().layerFilter || ''); } /** @@ -185,10 +185,9 @@ export class GVEsriImage extends AbstractGVRaster { * @param {string} filter - An optional filter to be used in place of the getViewFilter value. * @param {boolean} combineLegendFilter - Flag used to combine the legend filter and the filter together (default: true) */ - applyViewFilter(layerPath: string, filter: string, combineLegendFilter?: boolean): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + applyViewFilter(filter: string, combineLegendFilter?: boolean): void { // Log - logger.logTraceCore('GV-ESRI-IMAGE - applyViewFilter', layerPath); + logger.logTraceCore('GV-ESRI-IMAGE - applyViewFilter', this.getLayerPath()); const layerConfig = this.getLayerConfig(); const olLayer = this.getOLLayer(); @@ -224,7 +223,6 @@ export class GVEsriImage extends AbstractGVRaster { // Emit event this.emitLayerFilterApplied({ - layerPath, filter: filterValueToUse, }); } @@ -236,8 +234,7 @@ export class GVEsriImage extends AbstractGVRaster { * @returns {Extent | undefined} The layer bounding box. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - override getBounds(layerPath: string): Extent | undefined { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + override getBounds(): Extent | undefined { // Get the metadata extent const metadataExtent = this.getMetadataExtent(); 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 897a23c52cc..8e3960f7c02 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 @@ -142,8 +142,7 @@ export class GVImageStatic extends AbstractGVRaster { * @returns {Extent | undefined} The layer bounding box. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - override getBounds(layerPath: string): Extent | undefined { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + override getBounds(): Extent | undefined { // Get the source projection const sourceProjection = this.getOLSource().getProjection() || undefined; 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 d482cf3f506..320e152feb0 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 @@ -484,8 +484,7 @@ export class GVWMS extends AbstractGVRaster { * @param {string} wmsStyleId - The style identifier that will be used. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - setWmsStyle(wmsStyleId: string, layerPath: string): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done (it should be moved when calling getLayerFilter below too) + setWmsStyle(wmsStyleId: string): void { // TODO: Verify if we can apply more than one style at the same time since the parameter name is STYLES this.getOLSource()?.updateParams({ STYLES: wmsStyleId }); } @@ -497,8 +496,8 @@ export class GVWMS extends AbstractGVRaster { // Call parent super.onLoaded(); - // Apply view filter immediately (no need to provide a layer path here so '' is sent (hybrid work)) - this.applyViewFilter('', this.getLayerConfig().layerFilter || ''); + // Apply view filter immediately + this.applyViewFilter(this.getLayerConfig().layerFilter || ''); } /** @@ -510,13 +509,12 @@ export class GVWMS extends AbstractGVRaster { * @param {string} filter - An optional filter to be used in place of the getViewFilter value. * @param {boolean} combineLegendFilter - Flag used to combine the legend filter and the filter together (default: true) */ - applyViewFilter(layerPath: string, filter: string, combineLegendFilter = true): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done (it should be moved when calling getLayerFilter below too) + applyViewFilter(filter: string, combineLegendFilter = true): void { const layerConfig = this.getLayerConfig(); const olLayer = this.getOLLayer(); // Log - logger.logTraceCore('GVWMS - applyViewFilter', layerPath); + logger.logTraceCore('GVWMS - applyViewFilter', this.getLayerPath()); // Get source const source = olLayer.getSource(); @@ -549,7 +547,6 @@ export class GVWMS extends AbstractGVRaster { // Emit event this.emitLayerFilterApplied({ - layerPath, filter: filterValueToUse, }); } @@ -561,8 +558,7 @@ export class GVWMS extends AbstractGVRaster { * @returns {Extent | undefined} The layer bounding box. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - override getBounds(layerPath: string): Extent | undefined { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + override getBounds(): Extent | undefined { const layerConfig = this.getLayerConfig(); // Get the layer config bounds diff --git a/packages/geoview-core/src/geo/layer/gv-layers/tile/gv-xyz-tiles.ts b/packages/geoview-core/src/geo/layer/gv-layers/tile/gv-xyz-tiles.ts index 89fdc9508ef..27da6c2bda2 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/tile/gv-xyz-tiles.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/tile/gv-xyz-tiles.ts @@ -77,8 +77,7 @@ export class GVXYZTiles extends AbstractGVTile { * @returns {Extent | undefined} The layer bounding box. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - override getBounds(layerPath: string): Extent | undefined { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + override getBounds(): Extent | undefined { // Get the layer const layer = this.getOLLayer() as TileLayer | undefined; diff --git a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector-tile.ts b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector-tile.ts index 5b929db5c17..a9b5f665969 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector-tile.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector-tile.ts @@ -36,8 +36,7 @@ export abstract class AbstractGVVectorTile extends AbstractGVLayer { * @returns {Extent | undefined} The layer bounding box. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - override getBounds(layerPath: string): Extent | undefined { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + override getBounds(): Extent | undefined { // Get the source projection const sourceProjection = this.getOLSource().getProjection() || undefined; diff --git a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts index 0903b3cf620..29107c48526 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts @@ -18,7 +18,6 @@ import { TypeFeatureInfoEntry } from '@/geo/map/map-schema-types'; import { analyzeLayerFilter, getAndCreateFeatureStyle } from '@/geo/utils/renderer/geoview-renderer'; import { featureInfoGetFieldType } from '../utils'; import { AbstractGVLayer } from '../abstract-gv-layer'; -import { AbstractGeoViewLayer } from '../../geoview-layers/abstract-geoview-layers'; import { getMinOrMaxExtents } from '@/geo/utils/utilities'; import { TypeOutfieldsType } from '@/api/config/types/map-schema-types'; @@ -43,14 +42,7 @@ export abstract class AbstractGVVector extends AbstractGVLayer { properties: { layerConfig }, source: olSource, style: (feature) => { - return AbstractGVVector.calculateStyleForFeature( - this, - feature, - label, - layerConfig.layerPath, - layerConfig.filterEquation, - layerConfig.legendFilterIsOff - ); + return AbstractGVVector.calculateStyleForFeature(this, feature, label, layerConfig.filterEquation, layerConfig.legendFilterIsOff); }, }; @@ -178,8 +170,8 @@ export abstract class AbstractGVVector extends AbstractGVLayer { // Call parent super.onLoaded(); - // Apply view filter immediately (no need to provide a layer path here so '' is sent (hybrid work)) - this.applyViewFilter('', this.getLayerConfig().layerFilter || ''); + // Apply view filter immediately + this.applyViewFilter(this.getLayerConfig().layerFilter || ''); } /** @@ -190,10 +182,9 @@ export abstract class AbstractGVVector extends AbstractGVLayer { * @param {string} filter - A filter to be used in place of the getViewFilter value. * @param {boolean} combineLegendFilter - Flag used to combine the legend filter and the filter together (default: true) */ - applyViewFilter(layerPath: string, filter: string, combineLegendFilter = true): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done (it should be moved when calling getLayerFilter below too) + applyViewFilter(filter: string, combineLegendFilter = true): void { // Log - logger.logTraceCore('ABSTRACT-GV-VECTOR - applyViewFilter'); + logger.logTraceCore('ABSTRACT-GV-VECTOR - applyViewFilter', this.getLayerPath()); const layerConfig = this.getLayerConfig(); const olLayer = this.getOLLayer(); @@ -229,9 +220,7 @@ export abstract class AbstractGVVector extends AbstractGVLayer { layerConfig.filterEquation = filterEquation; } catch (error) { throw new Error( - `Invalid vector layer filter (${(error as { message: string }).message}).\nfilter = ${this.getLayerFilter( - layerPath - )}\ninternal filter = ${filterValueToUse}` + `Invalid vector layer filter (${(error as { message: string }).message}).\nfilter = ${this.getLayerFilter()}\ninternal filter = ${filterValueToUse}` ); } @@ -239,7 +228,6 @@ export abstract class AbstractGVVector extends AbstractGVLayer { // Emit event this.emitLayerFilterApplied({ - layerPath, filter: filterValueToUse, }); } @@ -249,8 +237,7 @@ export abstract class AbstractGVVector extends AbstractGVLayer { * @returns {Extent | undefined} The layer bounding box. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - override getBounds(layerPath: string): Extent | undefined { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + override getBounds(): Extent | undefined { const sourceExtent = this.getOLSource().getExtent(); // Return the calculated layer bounds @@ -259,13 +246,12 @@ export abstract class AbstractGVVector extends AbstractGVLayer { /** * Gets the extent of an array of features. - * @param {string} layerPath - The layer path. * @param {string[]} objectIds - The uids of the features to calculate the extent from. * @returns {Promise} The extent of the features, if available. */ // Added eslint-disable here, because we do want to override this method in children and keep 'this'. // eslint-disable-next-line @typescript-eslint/class-methods-use-this - override getExtentFromFeatures(layerPath: string, objectIds: string[]): Promise { + override getExtentFromFeatures(objectIds: string[]): Promise { // Get array of features const requestedFeatures = objectIds.map((id) => this.getOLLayer().getSource()?.getFeatureById(id)); @@ -307,7 +293,7 @@ export abstract class AbstractGVVector extends AbstractGVLayer { /** * Calculates a style for the given feature, based on the layer current style and options. - * @param {AbstractGeoViewLayer | AbstractGVLayer} layer - The layer on which to work for the style. + * @param {AbstractGVLayer} layer - The layer on which to work for the style. * @param {FeatureLike} feature - Feature that need its style to be defined. * @param {string} label - The style label when one has to be created * @param {FilterNodeArrayType} filterEquation - Filter equation associated to the layer. @@ -315,23 +301,21 @@ export abstract class AbstractGVVector extends AbstractGVLayer { * @returns {Style} The style for the feature */ static calculateStyleForFeature( - layer: AbstractGeoViewLayer | AbstractGVLayer, + layer: AbstractGVLayer, feature: FeatureLike, label: string, - layerPath: string, filterEquation?: FilterNodeArrayType, legendFilterIsOff?: boolean ): Style | undefined { - // TODO: Refactor - After layers refactoring, remove the layerPath parameter here. // Get the style - const style = layer.getStyle(layerPath) || {}; + const style = layer.getStyle() || {}; // Get and create Feature style if necessary return getAndCreateFeatureStyle(feature, style, label, filterEquation, legendFilterIsOff, (geometryType, theStyle) => { // A new style has been created logger.logDebug('A new style has been created on-the-fly', geometryType, layer); // Update the layer style - layer.setStyle(layerPath, { + layer.setStyle({ ...style, ...{ [geometryType]: { type: 'simple', hasDefault: false, fields: [], info: [theStyle] } }, }); diff --git a/packages/geoview-core/src/geo/layer/gv-layers/vector/gv-geojson.ts b/packages/geoview-core/src/geo/layer/gv-layers/vector/gv-geojson.ts index 6037577105c..ad0cffc5016 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/vector/gv-geojson.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/vector/gv-geojson.ts @@ -1,6 +1,16 @@ import VectorSource from 'ol/source/Vector'; +import { GeoJSONObject } from 'ol/format/GeoJSON'; +import { GeoJSON as FormatGeoJSON } from 'ol/format'; +import Feature from 'ol/Feature'; +import VectorLayer from 'ol/layer/Vector'; + import { GeoJSONLayerEntryConfig } from '@/core/utils/config/validation-classes/vector-validation-classes/geojson-layer-entry-config'; import { AbstractGVVector } from './abstract-gv-vector'; +import { Projection } from '@/geo/utils/projection'; +import { LegendEventProcessor } from '@/api/event-processors/event-processor-children/legend-event-processor'; +import { FeatureInfoEventProcessor } from '@/api/event-processors/event-processor-children/feature-info-event-processor'; +import { DataTableEventProcessor } from '@/api/event-processors/event-processor-children/data-table-event-processor'; +import { logger } from '@/core/utils/logger'; /** * Manages a GeoJSON layer. @@ -29,4 +39,49 @@ export class GVGeoJSON extends AbstractGVVector { // Call parent and cast return super.getLayerConfig() as GeoJSONLayerEntryConfig; } + + /** *************************************************************************************************************************** + * Override the features of a geojson layer with new geojson. + * @param {GeoJSONObject | string} geojson - The new geoJSON. + */ + overrideGeojsonSource(geojson: GeoJSONObject | string): void { + // Convert string to geoJSON if necessary + const geojsonObject = typeof geojson === 'string' ? JSON.parse(geojson) : geojson; + + // Create features from geoJSON + const dataProjection = geojsonObject.crs?.properties?.name || Projection.PROJECTION_NAMES.LNGLAT; + const features = new FormatGeoJSON().readFeatures(geojsonObject, { + dataProjection, + featureProjection: this.getMapViewer().getProjection(), + }); + + const olLayer = this.getOLLayer() as VectorLayer>; + + if (olLayer && features.length) { + const layerPath = this.getLayerPath(); + const mapId = this.getMapId(); + + // Remove current features and add new ones + olLayer!.getSource()?.clear(); + olLayer!.getSource()?.addFeatures(features); + olLayer.changed(); + + // TODO: This is coupled with the processor. Maybe we should have a processor event to trigger this and + // TODO.CONT: keep this functio not tie with UI. + // Update the bounds in the store + const bounds = this.getBounds(); + if (bounds) { + LegendEventProcessor.setLayerBounds(mapId, layerPath, bounds); + } + + // Reset the feature info result set + FeatureInfoEventProcessor.resetResultSet(mapId, layerPath); + + // Update feature info + DataTableEventProcessor.triggerGetAllFeatureInfo(mapId, layerPath).catch((error) => { + // Log + logger.logPromiseFailed(`Update all feature info in overrideGeojsonSource failed for layer ${layerPath}`, error); + }); + } + } } diff --git a/packages/geoview-core/src/geo/layer/layer-sets/abstract-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/abstract-layer-set.ts index 7c9bca21bfd..6f8b2142ef5 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/abstract-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/abstract-layer-set.ts @@ -11,19 +11,15 @@ import { } from '@/geo/map/map-schema-types'; import { TypeAllFeatureInfoResultSetEntry } from '@/core/stores/store-interface-and-intial-values/data-table-state'; import { TypeFeatureInfoResultSetEntry, TypeHoverResultSetEntry } from '@/core/stores/store-interface-and-intial-values/feature-info-state'; -import { AbstractGeoViewLayer, LayerNameChangedEvent } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; import { generateId, whenThisThen } from '@/core/utils/utilities'; import { ConfigBaseClass, LayerStatusChangedEvent } from '@/core/utils/config/validation-classes/config-base-class'; import { LayerApi } from '@/geo/layer/layer'; import { AbstractGVLayer } from '../gv-layers/abstract-gv-layer'; import { AbstractBaseLayerEntryConfig } from '@/core/utils/config/validation-classes/abstract-base-layer-entry-config'; -import { EsriDynamic } from '../geoview-layers/raster/esri-dynamic'; -import { AbstractGeoViewVector } from '../geoview-layers/vector/abstract-geoview-vector'; -import { WMS } from '../geoview-layers/raster/wms'; import { GVEsriDynamic } from '../gv-layers/raster/gv-esri-dynamic'; import { AbstractGVVector } from '../gv-layers/vector/abstract-gv-vector'; import { GVWMS } from '../gv-layers/raster/gv-wms'; -import { AbstractBaseLayer } from '../gv-layers/abstract-base-layer'; +import { AbstractBaseLayer, LayerNameChangedEvent } from '../gv-layers/abstract-base-layer'; import { logger } from '@/core/utils/logger'; /** @@ -56,7 +52,7 @@ export abstract class AbstractLayerSet { #boundHandleLayerStatusChanged: (config: ConfigBaseClass, layerStatusEvent: LayerStatusChangedEvent) => void; // Keep a bounded reference to the handle layer status changed - #boundHandleLayerNameChanged: (layer: AbstractGeoViewLayer | AbstractBaseLayer, layerNameEvent: LayerNameChangedEvent) => void; + #boundHandleLayerNameChanged: (layer: AbstractBaseLayer, layerNameEvent: LayerNameChangedEvent) => void; /** * Constructs a new LayerSet instance. @@ -142,8 +138,8 @@ export abstract class AbstractLayerSet { // If the layer could be found if (layer) { - // Register the layer itself (not the layer config) (recall the hybrid mode) automatically in the layer set - this.registerLayer(layer, layerConfig.layerPath).catch((error) => { + // Register the layer itself (not the layer config) automatically in the layer set + this.registerLayer(layer).catch((error) => { // Log logger.logPromiseFailed('in registerLayer in registerLayerConfig', error); }); @@ -191,44 +187,41 @@ export abstract class AbstractLayerSet { /** * Registers the layer in the layer-set. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer */ - async registerLayer(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): Promise { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done - + async registerLayer(layer: AbstractBaseLayer): Promise { // Wait a maximum of 20 seconds for the layer to get to loaded state so that it can get registered, otherwise another attempt will have to be made // This await is important when devs call this method directly to register ad-hoc layers. - await whenThisThen(() => layer.getLayerStatus(layerPath) === 'loaded', 20000); + await whenThisThen(() => layer.getLayerStatus() === 'loaded', 20000); // If the layer is already registered, skip it, we don't register twice - if (this.#registeredLayerLayerPaths.includes(layerPath)) return; + if (this.#registeredLayerLayerPaths.includes(layer.getLayerPath())) return; // Update the registration of all layer sets - if (this.onRegisterLayerCheck(layer, layerPath)) { + if (this.onRegisterLayerCheck(layer)) { // Call the registration function for the layer-set. This method is different for each child. - this.onRegisterLayer(layer, layerPath); + this.onRegisterLayer(layer); // Call for propagation to the store upon registration - this.onPropagateToStore(this.resultSet[layerPath], 'layer-registration'); + this.onPropagateToStore(this.resultSet[layer.getLayerPath()], 'layer-registration'); // Inform that the layer set has been updated - this.onLayerSetUpdatedProcess(layerPath); + this.onLayerSetUpdatedProcess(layer.getLayerPath()); } } /** * An overridable registration condition function for a layer-set to check if the registration * should happen for a specific geoview layer and layer path. By default, a layer-set always registers layers except when they are group layers. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer * @returns {boolean} True if the layer should be registered, false otherwise */ // Added eslint-disable here, because we do want to override this method in children and keep 'this'. // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/class-methods-use-this - protected onRegisterLayerCheck(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): boolean { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + protected onRegisterLayerCheck(layer: AbstractBaseLayer): boolean { // Override this function to perform registration condition logic in the inherited classes // By default, a layer-set always registers layers except when they are group layers - if (this.layerApi.getGeoviewLayer(layerPath)?.getLayerConfig()?.entryType === 'group') { + if (layer.getLayerConfig()?.entryType === 'group') { // Skip groups return false; } @@ -240,24 +233,23 @@ export abstract class AbstractLayerSet { /** * An overridable registration function for a layer-set that the registration process will use to * create a new entry in the layer set for a specific geoview layer and layer path. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer config + * @param {AbstractBaseLayer} layer - The layer config */ - protected onRegisterLayer(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done - + protected onRegisterLayer(layer: AbstractBaseLayer): void { // Get layer name - const layerName = layer.getLayerName(layerPath)!; + const layerName = layer.getLayerName()!; + const layerPath = layer.getLayerPath(); // If not there (wasn't pre-registered via a config-registration) if (!(layerPath in this.resultSet)) { this.resultSet[layerPath] = { layerPath, - layerStatus: layer.getLayerStatus(layerPath), + layerStatus: layer.getLayerStatus(), layerName, }; } else { // Already there, update it - this.resultSet[layerPath].layerStatus = layer.getLayerStatus(layerPath); + this.resultSet[layerPath].layerStatus = layer.getLayerStatus(); this.resultSet[layerPath].layerName = layerName; } @@ -305,9 +297,9 @@ export abstract class AbstractLayerSet { /** * An overridable unregistration function for a layer-set that the registration process will use to * unregister a specific geoview layer. - * @param {AbstractGeoViewLayer | AbstractBaseLayer | undefined} layer - The layer + * @param {AbstractBaseLayer | undefined} layer - The layer */ - protected onUnregisterLayer(layer: AbstractGeoViewLayer | AbstractBaseLayer | undefined): void { + protected onUnregisterLayer(layer: AbstractBaseLayer | undefined): void { // Unregister the layer name changed handler layer?.offLayerNameChanged(this.#boundHandleLayerNameChanged); } @@ -338,25 +330,27 @@ export abstract class AbstractLayerSet { /** * Handles when a layer status changed on a layer config. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer * @param {LayerNameChangedEvent} layerNameEvent - The new layer name */ - #handleLayerNameChanged(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerNameEvent: LayerNameChangedEvent): void { + #handleLayerNameChanged(layer: AbstractBaseLayer, layerNameEvent: LayerNameChangedEvent): void { + const layerPath = layer.getLayerPath(); + try { // If the layer path exists for the layer name that changed - if (this.resultSet[layerNameEvent.layerPath]) { + if (this.resultSet[layerPath]) { // Call the overridable function to process a layer name change - this.onProcessNameChanged(layerNameEvent.layerPath, layerNameEvent.layerName!); + this.onProcessNameChanged(layerPath, layerNameEvent.layerName!); // Propagate to the store - this.onPropagateToStore(this.resultSet[layerNameEvent.layerPath], 'layerName'); + this.onPropagateToStore(this.resultSet[layerPath], 'layerName'); // Inform that the layer set has been updated - this.onLayerSetUpdatedProcess(layerNameEvent.layerPath); + this.onLayerSetUpdatedProcess(layerPath); } } catch (error) { // Log - logger.logError('CAUGHT in handleLayerStatusChanged', layerNameEvent.layerPath, error); + logger.logError('CAUGHT in handleLayerStatusChanged', layerPath, error); } } @@ -396,14 +390,14 @@ export abstract class AbstractLayerSet { /** * Processes layer data to query features on it, if the layer path can be queried. * @param {TypeFeatureInfoResultSetEntry | TypeAllFeatureInfoResultSetEntry | TypeHoverResultSetEntry} data - The layer data - * @param {AbstractGeoViewLayer | AbstractGVLayer} geoviewLayer - The geoview layer + * @param {AbstractGVLayer} geoviewLayer - The geoview layer * @param {QueryType} queryType - The query type * @param {TypeLocation} location - The location for the query * @returns {Promise} A promise resolving to the query results */ protected static queryLayerFeatures( data: TypeFeatureInfoResultSetEntry | TypeAllFeatureInfoResultSetEntry | TypeHoverResultSetEntry, - geoviewLayer: AbstractGeoViewLayer | AbstractGVLayer, + geoviewLayer: AbstractGVLayer, queryType: QueryType, location: TypeLocation ): Promise { @@ -413,39 +407,30 @@ export abstract class AbstractLayerSet { /** * Checks if the layer is of queryable type based on its class definition - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer * @returns True if the layer is of queryable type */ - protected static isQueryableType(layer: AbstractGeoViewLayer | AbstractBaseLayer): boolean { - return ( - layer instanceof AbstractGeoViewVector || - layer instanceof AbstractGVVector || - layer instanceof EsriDynamic || - layer instanceof GVEsriDynamic || - layer instanceof WMS || - layer instanceof GVWMS - ); + protected static isQueryableType(layer: AbstractBaseLayer): boolean { + return layer instanceof AbstractGVVector || layer instanceof GVEsriDynamic || layer instanceof GVWMS; } /** * Checks if the layer config source is queryable. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer * @returns {boolean} True if the source is queryable or undefined */ - protected static isSourceQueryable(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): boolean { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done - return !((layer.getLayerConfig(layerPath) as AbstractBaseLayerEntryConfig)?.source?.featureInfo?.queryable === false); + protected static isSourceQueryable(layer: AbstractBaseLayer): boolean { + return !((layer.getLayerConfig() as AbstractBaseLayerEntryConfig)?.source?.featureInfo?.queryable === false); } /** * Checks if the layer config state is queryable. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer * @returns {boolean} True if the state is queryable or undefined */ - protected static isStateQueryable(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): boolean { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + protected static isStateQueryable(layer: AbstractBaseLayer): boolean { // Return false when it's clearly false, otherwise, return true - return !((layer.getLayerConfig(layerPath) as AbstractBaseLayerEntryConfig)?.initialSettings?.states?.queryable === false); + return !((layer.getLayerConfig() as AbstractBaseLayerEntryConfig)?.initialSettings?.states?.queryable === false); } /** diff --git a/packages/geoview-core/src/geo/layer/layer-sets/all-feature-info-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/all-feature-info-layer-set.ts index 1c2099afb9d..8998b329f9c 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/all-feature-info-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/all-feature-info-layer-set.ts @@ -1,9 +1,7 @@ import { DataTableEventProcessor } from '@/api/event-processors/event-processor-children/data-table-event-processor'; import { QueryType, TypeLayerEntryConfig } from '@/geo/map/map-schema-types'; -import { AbstractGeoViewLayer } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; import { AbstractGVLayer } from '../gv-layers/abstract-gv-layer'; import { AbstractBaseLayer } from '../gv-layers/abstract-base-layer'; -import { WMS } from '../geoview-layers/raster/wms'; import { GVWMS } from '../gv-layers/raster/gv-wms'; import { AbstractLayerSet, PropagationType } from './abstract-layer-set'; import { @@ -23,31 +21,29 @@ export class AllFeatureInfoLayerSet extends AbstractLayerSet { /** * Overrides the behavior to apply when a feature-info-layer-set wants to check for condition to register a layer in its set. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer * @returns {boolean} True when the layer should be registered to this all-feature-info-layer-set. */ - protected override onRegisterLayerCheck(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): boolean { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + protected override onRegisterLayerCheck(layer: AbstractBaseLayer): boolean { // Return if the layer is of queryable type and source is queryable return ( - super.onRegisterLayerCheck(layer, layerPath) && + super.onRegisterLayerCheck(layer) && AbstractLayerSet.isQueryableType(layer) && - !(layer instanceof WMS) && !(layer instanceof GVWMS) && - AbstractLayerSet.isSourceQueryable(layer, layerPath) + AbstractLayerSet.isSourceQueryable(layer) ); } /** * Overrides the behavior to apply when an all-feature-info-layer-set wants to register a layer in its set. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer */ - protected override onRegisterLayer(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + protected override onRegisterLayer(layer: AbstractBaseLayer): void { // Call parent - super.onRegisterLayer(layer, layerPath); + super.onRegisterLayer(layer); // Update the resultSet data + const layerPath = layer.getLayerPath(); this.resultSet[layerPath].eventListenerEnabled = true; this.resultSet[layerPath].queryStatus = 'processed'; this.resultSet[layerPath].features = []; @@ -110,9 +106,9 @@ export class AllFeatureInfoLayerSet extends AbstractLayerSet { const layer = this.layerApi.getGeoviewLayer(layerPath); // If layer was found - if (layer && (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer)) { + if (layer && layer instanceof AbstractGVLayer) { // If state is not queryable - if (!AbstractLayerSet.isStateQueryable(layer, layerPath)) return Promise.resolve(); + if (!AbstractLayerSet.isStateQueryable(layer)) return Promise.resolve(); // Flag processing this.resultSet[layerPath].queryStatus = 'processing'; diff --git a/packages/geoview-core/src/geo/layer/layer-sets/feature-info-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/feature-info-layer-set.ts index 5cb8c1b3b1a..a17eadb3272 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/feature-info-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/feature-info-layer-set.ts @@ -3,7 +3,6 @@ import { FeatureInfoEventProcessor } from '@/api/event-processors/event-processo import EventHelper, { EventDelegateBase } from '@/api/events/event-helper'; import { logger } from '@/core/utils/logger'; import { TypeFeatureInfoEntry, TypeFeatureInfoLayerConfig, TypeLayerEntryConfig, TypeResultSet } from '@/geo/map/map-schema-types'; -import { AbstractGeoViewLayer } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; import { AbstractGVLayer } from '../gv-layers/abstract-gv-layer'; import { AbstractBaseLayer } from '../gv-layers/abstract-base-layer'; import { EventType, AbstractLayerSet, PropagationType } from './abstract-layer-set'; @@ -45,31 +44,24 @@ export class FeatureInfoLayerSet extends AbstractLayerSet { /** * Overrides the behavior to apply when a feature-info-layer-set wants to check for condition to register a layer in its set. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer * @returns {boolean} True when the layer should be registered to this feature-info-layer-set. */ - protected override onRegisterLayerCheck(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): boolean { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done - + protected override onRegisterLayerCheck(layer: AbstractBaseLayer): boolean { // Return if the layer is of queryable type and source is queryable - return ( - super.onRegisterLayerCheck(layer, layerPath) && - AbstractLayerSet.isQueryableType(layer) && - AbstractLayerSet.isSourceQueryable(layer, layerPath) - ); + return super.onRegisterLayerCheck(layer) && AbstractLayerSet.isQueryableType(layer) && AbstractLayerSet.isSourceQueryable(layer); } /** * Overrides the behavior to apply when a feature-info-layer-set wants to register a layer in its set. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer */ - protected override onRegisterLayer(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done - + protected override onRegisterLayer(layer: AbstractBaseLayer): void { // Call parent - super.onRegisterLayer(layer, layerPath); + super.onRegisterLayer(layer); // Update the resultSet data + const layerPath = layer.getLayerPath(); this.resultSet[layerPath].eventListenerEnabled = true; this.resultSet[layerPath].queryStatus = 'processed'; this.resultSet[layerPath].features = []; @@ -134,9 +126,9 @@ export class FeatureInfoLayerSet extends AbstractLayerSet { const layer = this.layerApi.getGeoviewLayer(layerPath); // If layer was found - if (layer && (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer)) { + if (layer && layer instanceof AbstractGVLayer) { // If state is not queryable - if (!AbstractLayerSet.isStateQueryable(layer, layerPath)) return; + if (!AbstractLayerSet.isStateQueryable(layer)) return; // Flag processing this.resultSet[layerPath].features = undefined; diff --git a/packages/geoview-core/src/geo/layer/layer-sets/hover-feature-info-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/hover-feature-info-layer-set.ts index 7ef4716b420..8005db6bd38 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/hover-feature-info-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/hover-feature-info-layer-set.ts @@ -2,10 +2,8 @@ import debounce from 'lodash/debounce'; import { Coordinate } from 'ol/coordinate'; import { logger } from '@/core/utils/logger'; -import { AbstractGeoViewLayer } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; import { AbstractGVLayer } from '../gv-layers/abstract-gv-layer'; import { AbstractBaseLayer } from '../gv-layers/abstract-base-layer'; -import { WMS } from '../geoview-layers/raster/wms'; import { GVWMS } from '../gv-layers/raster/gv-wms'; import { AbstractLayerSet, PropagationType } from './abstract-layer-set'; import { LayerApi } from '@/geo/layer/layer'; @@ -40,31 +38,29 @@ export class HoverFeatureInfoLayerSet extends AbstractLayerSet { /** * Overrides the behavior to apply when a hover-feature-info-layer-set wants to check for condition to register a layer in its set. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer * @returns {boolean} True when the layer should be registered to this hover-feature-info-layer-set. */ - protected override onRegisterLayerCheck(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): boolean { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + protected override onRegisterLayerCheck(layer: AbstractBaseLayer): boolean { // Return if the layer is of queryable type and source is queryable return ( - super.onRegisterLayerCheck(layer, layerPath) && + super.onRegisterLayerCheck(layer) && AbstractLayerSet.isQueryableType(layer) && - !(layer instanceof WMS) && !(layer instanceof GVWMS) && - AbstractLayerSet.isSourceQueryable(layer, layerPath) + AbstractLayerSet.isSourceQueryable(layer) ); } /** * Overrides the behavior to apply when a hover-feature-info-layer-set wants to register a layer in its set. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer */ - protected override onRegisterLayer(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + protected override onRegisterLayer(layer: AbstractBaseLayer): void { // Call parent - super.onRegisterLayer(layer, layerPath); + super.onRegisterLayer(layer); // Update the resultSet data + const layerPath = layer.getLayerPath(); this.resultSet[layerPath].eventListenerEnabled = true; this.resultSet[layerPath].queryStatus = 'processed'; this.resultSet[layerPath].feature = undefined; @@ -109,9 +105,9 @@ export class HoverFeatureInfoLayerSet extends AbstractLayerSet { const layer = this.layerApi.getGeoviewLayer(layerPath); // If layer was found - if (layer && (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer)) { + if (layer && layer instanceof AbstractGVLayer) { // If state is not queryable - if (!AbstractLayerSet.isStateQueryable(layer, layerPath)) return; + if (!AbstractLayerSet.isStateQueryable(layer)) return; // Flag processing this.resultSet[layerPath].feature = undefined; diff --git a/packages/geoview-core/src/geo/layer/layer-sets/legends-layer-set.ts b/packages/geoview-core/src/geo/layer/layer-sets/legends-layer-set.ts index c7d9aadd369..086bba1750e 100644 --- a/packages/geoview-core/src/geo/layer/layer-sets/legends-layer-set.ts +++ b/packages/geoview-core/src/geo/layer/layer-sets/legends-layer-set.ts @@ -4,8 +4,7 @@ import { logger } from '@/core/utils/logger'; import { TypeLayerStatus } from '@/geo/map/map-schema-types'; import { AbstractLayerSet, PropagationType } from './abstract-layer-set'; import { TypeLegend, TypeLegendResultSet, TypeLegendResultSetEntry } from '@/core/stores/store-interface-and-intial-values/layer-state'; -import { AbstractGeoViewLayer, LayerStyleChangedEvent } from '../geoview-layers/abstract-geoview-layers'; -import { AbstractGVLayer } from '../gv-layers/abstract-gv-layer'; +import { AbstractGVLayer, LayerStyleChangedEvent } from '../gv-layers/abstract-gv-layer'; import { AbstractBaseLayer } from '../gv-layers/abstract-base-layer'; import { LayerApi } from '../layer'; @@ -20,7 +19,7 @@ export class LegendsLayerSet extends AbstractLayerSet { declare resultSet: TypeLegendResultSet; // Keep a bounded reference to the handle layer status changed - #boundHandleLayerStyleChanged: (layer: AbstractGeoViewLayer | AbstractGVLayer, layerStyleEvent: LayerStyleChangedEvent) => void; + #boundHandleLayerStyleChanged: (layer: AbstractGVLayer, layerStyleEvent: LayerStyleChangedEvent) => void; /** * Constructs a Legends LayerSet to manage layers legends. @@ -44,12 +43,12 @@ export class LegendsLayerSet extends AbstractLayerSet { /** * Overrides the behavior to apply when an all-feature-info-layer-set wants to check for condition to register a layer in its set. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer * @param {string} layerPath - The layer path * @returns {boolean} True when the layer should be registered to this legends-layer-set */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected override onRegisterLayerCheck(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): boolean { + protected override onRegisterLayerCheck(layer: AbstractBaseLayer): boolean { // Always register layers for the legends-layer-set, because we want 'the box' in the UI to show the layer status progression return true; } @@ -71,15 +70,14 @@ export class LegendsLayerSet extends AbstractLayerSet { /** * Overrides the behavior to apply when a legends-layer-set wants to register a layer in its set. - * @param {AbstractGeoViewLayer | AbstractBaseLayer} layer - The layer + * @param {AbstractBaseLayer} layer - The layer */ - protected override onRegisterLayer(layer: AbstractGeoViewLayer | AbstractBaseLayer, layerPath: string): void { - // TODO: Refactor - After layers refactoring, remove the layerPath parameter here + protected override onRegisterLayer(layer: AbstractBaseLayer): void { // Call parent - super.onRegisterLayer(layer, layerPath); + super.onRegisterLayer(layer); // If regular layer - if (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer) { + if (layer instanceof AbstractGVLayer) { // Register handler on layer style change layer.onLayerStyleChanged(this.#boundHandleLayerStyleChanged); } @@ -144,7 +142,7 @@ export class LegendsLayerSet extends AbstractLayerSet { if ( layer && layerConfig && - (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer) && + layer instanceof AbstractGVLayer && this.resultSet[layerPath].legendQueryStatus !== 'querying' && (this.#legendShouldBeQueried(layerConfig) || forced) ) { @@ -155,7 +153,7 @@ export class LegendsLayerSet extends AbstractLayerSet { this.#propagateToStore(this.resultSet[layerPath]); // Query the legend - const legendPromise = layer.queryLegend(layerPath); + const legendPromise = layer.queryLegend(); // Whenever the legend response comes in legendPromise @@ -193,12 +191,12 @@ export class LegendsLayerSet extends AbstractLayerSet { /** * Handles when a layer style changes on a registered layer - * @param {AbstractGeoViewLayer | AbstractGVLayer} layer - The layer which changed its styles + * @param {AbstractGVLayer} layer - The layer which changed its styles * @param {LayerStyleChangedEvent} event - The layer style changed event */ - #handleLayerStyleChanged(layer: AbstractGeoViewLayer | AbstractGVLayer, event: LayerStyleChangedEvent): void { - // TODO: Refactor - Layers refactoring. Replace event.layerPath by something like AbstractGVLayer.getLayerPath() + // eslint-disable-next-line @typescript-eslint/no-unused-vars + #handleLayerStyleChanged(layer: AbstractGVLayer, event: LayerStyleChangedEvent): void { // Force query the legend as we have a new style - this.#checkQueryLegend(event.layerPath, true); + this.#checkQueryLegend(layer.getLayerPath(), true); } } diff --git a/packages/geoview-core/src/geo/layer/layer.ts b/packages/geoview-core/src/geo/layer/layer.ts index 187371d1434..7b7c2d72097 100644 --- a/packages/geoview-core/src/geo/layer/layer.ts +++ b/packages/geoview-core/src/geo/layer/layer.ts @@ -555,7 +555,7 @@ export class LayerApi { function setLayerVisibility(sender: LayerApi, event: LayerLoadedEvent): void { if (layerInfo.layerPath === event.layerPath) { const { visible } = originalMapOrderedLayerInfo.filter((info) => info.layerPath === event.layerPath)[0]; - event.layer?.setVisible(visible, event.layerPath); + event.layer?.setVisible(visible); sender.offLayerLoaded(setLayerVisibility); } } @@ -690,13 +690,15 @@ export class LayerApi { // Create the corresponding GVLayer const gvLayer = this.#createGVLayer(this.getMapId(), geoviewLayer, event.source, event.config, event.extraConfig); + + // If found the GV layer if (gvLayer) { // Register a hook when a layer is loaded on the map - gvLayer!.onIndividualLayerLoaded((sender, payload) => { - // Log - logger.logDebug(`${payload.layerPath} loaded on map ${this.getMapId()}`); - this.#emitLayerLoaded({ layer: sender, layerPath: payload.layerPath }); - }); + gvLayer.onIndividualLayerLoaded((sender, payload) => { + // Log + logger.logDebug(`${payload.layerPath} loaded on map ${this.getMapId()}`); + this.#emitLayerLoaded({ layer: sender, layerPath: payload.layerPath }); + }); return gvLayer.getOLLayer(); } throw new Error('Error, no corresponding GV layer'); @@ -724,16 +726,6 @@ export class LayerApi { layerBeingAdded! .createGeoViewLayers() .then(() => { - // If not HYBRID MODE - if (!LayerApi.LAYERS_HYBRID_MODE) { - // Register a hook when a layer is loaded on the map - layerBeingAdded!.onIndividualLayerLoaded((sender, payload) => { - // Log - logger.logDebug(`${payload.layerPath} loaded on map ${this.getMapId()}`); - this.#emitLayerLoaded({ layer: sender, layerPath: payload.layerPath }); - }); - } - // Add the layer on the map this.#addToMap(layerBeingAdded!); @@ -806,12 +798,11 @@ export class LayerApi { * This function may be used to start managing a layer in the UI when said layer has been created outside of the regular config->layer flow. * @param {AbstractGVLayer} layer - The layer to register */ - registerLayerInLayerSets(layer: AbstractGVLayer, layerPath: string): void { - // TODO: Refactor - Layers refactoring. Remove the layerPath parameter once hybrid work is done + registerLayerInLayerSets(layer: AbstractGVLayer): void { // Tell the layer sets about it this.#allLayerSets.forEach((layerSet) => { // Register the layer to the layer set - layerSet.registerLayer(layer, layerPath).catch((error) => { + layerSet.registerLayer(layer).catch((error) => { // Log logger.logPromiseFailed('in registerLayer in registerLayerUpdate', error); }); @@ -921,7 +912,7 @@ export class LayerApi { if (timeDimension) gvLayer.setTemporalDimension(timeDimension); // If any style to inject - if (style) gvLayer.setStyle(layerConfig.layerPath, style); + if (style) gvLayer.setStyle(style); // Initialize the layer, triggering the loaded/error status gvLayer.init(); @@ -1050,7 +1041,7 @@ export class LayerApi { const geoviewLayer = this.getGeoviewLayer(layerConfig.layerPath); // If the layer is loaded AND flag is true to use time dimension, continue - if ((geoviewLayer instanceof AbstractGeoViewLayer || geoviewLayer instanceof AbstractGVLayer) && geoviewLayer.getIsTimeAware()) { + if (geoviewLayer instanceof AbstractGVLayer && geoviewLayer.getIsTimeAware()) { // Check and add time slider layer when needed TimeSliderEventProcessor.checkInitTimeSliderLayerAndApplyFilters(this.getMapId(), layerConfig); } @@ -1286,19 +1277,24 @@ export class LayerApi { // If it is a group layer, highlight sublayers if (layerEntryIsGroupLayer(this.#layerEntryConfigs[layerPath])) { Object.keys(this.#layerEntryConfigs).forEach((registeredLayerPath) => { - const theLayer = this.getGeoviewLayer(registeredLayerPath)!; - if (!registeredLayerPath.startsWith(layerPath) && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { - const otherOpacity = theLayer.getOpacity(); - theLayer.setOpacity((otherOpacity || 1) * 0.25); - } else this.getOLLayer(registeredLayerPath)!.setZIndex(999); + // Trying to get the layer associated with the layer path, can be undefined because the layer might be in error + const theLayer = this.getGeoviewLayer(registeredLayerPath); + if (theLayer) { + if (!registeredLayerPath.startsWith(layerPath) && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { + const otherOpacity = theLayer.getOpacity(); + theLayer.setOpacity((otherOpacity || 1) * 0.25); + } else this.getOLLayer(registeredLayerPath)!.setZIndex(999); + } }); } else { Object.keys(this.#layerEntryConfigs).forEach((registeredLayerPath) => { - const theLayer = this.getGeoviewLayer(registeredLayerPath)!; - // check for otherOlLayer is undefined. It would be undefined if a layer status is error - if (registeredLayerPath !== layerPath && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { - const otherOpacity = theLayer.getOpacity(); - theLayer.setOpacity((otherOpacity || 1) * 0.25); + // Trying to get the layer associated with the layer path, can be undefined because the layer might be in error + const theLayer = this.getGeoviewLayer(registeredLayerPath); + if (theLayer) { + if (registeredLayerPath !== layerPath && !layerEntryIsGroupLayer(this.#layerEntryConfigs[registeredLayerPath])) { + const otherOpacity = theLayer.getOpacity(); + theLayer.setOpacity((otherOpacity || 1) * 0.25); + } } }); this.getOLLayer(layerPath)?.setZIndex(999); @@ -1416,10 +1412,10 @@ export class LayerApi { } // If the layer is a regular layer (not a group) - if (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer) { + if (layer instanceof AbstractGVLayer) { // Assign value to registered layer. This is use by applyFilter function to set visibility // TODO: check if we need to refactor to centralize attribute setting.... - const geometryStyleConfig = layer.getStyle(layerPath)![item.geometryType]; + const geometryStyleConfig = layer.getStyle()![item.geometryType]; const toggledStyleInfo = geometryStyleConfig?.info.find((styleInfo) => styleInfo.label === item.name); if (toggledStyleInfo) toggledStyleInfo.visible = visibility; } @@ -1524,7 +1520,7 @@ export class LayerApi { // If found if (layer) { // Set the layer name on the layer - layer.setLayerName(layerPath, name); + layer.setLayerName(name); } else { logger.logError(`Unable to find layer ${layerPath}`); } @@ -1607,14 +1603,11 @@ export class LayerApi { // If a leaf if (!layerEntryIsGroupLayer(layerConfig)) { // Get the layer - const layer = this.getGeoviewLayer(layerConfig.layerPath) as AbstractGeoViewLayer | AbstractGVLayer; + const layer = this.getGeoviewLayer(layerConfig.layerPath) as AbstractGVLayer; - // If the layer is of right type (should be, because we checked if not a group) - if (layer instanceof AbstractGeoViewLayer || layer instanceof AbstractGVLayer) { - // Get the bounds of the layer - const calculatedBounds = layer.getBounds(layerConfig.layerPath); - if (calculatedBounds) bounds.push(calculatedBounds); - } + // Get the bounds of the layer + const calculatedBounds = layer.getBounds(); + if (calculatedBounds) bounds.push(calculatedBounds); } else { // Is a group layerConfig.listOfLayerEntryConfig.forEach((subLayerConfig) => { @@ -1822,7 +1815,8 @@ type LayerAddedDelegate = EventDelegateBase; */ export type LayerAddedEvent = { // The added layer - layer: AbstractGeoViewLayer; + // GV: We need the AbstractGeoViewLayer because of addToMap function + layer: AbstractGeoViewLayer | AbstractGVLayer; }; /** @@ -1835,7 +1829,7 @@ type LayerLoadedDelegate = EventDelegateBase; */ export type LayerLoadedEvent = { // The loaded layer - layer: AbstractGeoViewLayer | AbstractGVLayer; + layer: AbstractGVLayer; layerPath: string; }; diff --git a/packages/geoview-time-slider/src/index.tsx b/packages/geoview-time-slider/src/index.tsx index 9ace5a36e02..14bee8cd7db 100644 --- a/packages/geoview-time-slider/src/index.tsx +++ b/packages/geoview-time-slider/src/index.tsx @@ -149,7 +149,7 @@ class TimeSliderPlugin extends FooterPlugin { if (timeDimension) { this.mapViewer() - .layer.getGeoviewLayerHybrid(layerPath) + .layer.getGeoviewLayer(layerPath) ?.setTemporalDimension({ ...timeDimension, default: obj.defaultValue, From 63fef846a28e8f5f87f1dda57152d699f371a70c Mon Sep 17 00:00:00 2001 From: Johann Levesque Date: Fri, 29 Nov 2024 16:45:54 -0500 Subject: [PATCH 3/7] progress --- .../geoview-core/src/geo/layer/geoview-layers/vector/wfs.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/vector/wfs.ts b/packages/geoview-core/src/geo/layer/geoview-layers/vector/wfs.ts index 9ed10d4ed3f..9767286cd43 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/vector/wfs.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/vector/wfs.ts @@ -323,7 +323,8 @@ export class WFS extends AbstractGeoViewVector { // Patch for field type only use for WFS static getFieldType(fieldName: string, layerConfig: VectorLayerEntryConfig): TypeOutfieldsType { const fieldDefinitions = layerConfig.getLayerMetadata() as TypeJsonArray; - const fieldDefinition = fieldDefinitions.find((metadataEntry) => metadataEntry.name === fieldName); + const fieldDefinition = + fieldDefinitions !== undefined ? fieldDefinitions.find((metadataEntry) => metadataEntry.name === fieldName) : undefined; if (!fieldDefinition) return 'string'; const fieldEntryType = (fieldDefinition.type as string).split(':').slice(-1)[0] as string; if (fieldEntryType === 'date') return 'date'; From e17f09dd53003e7e3b3f09fae736eacfe29e290a Mon Sep 17 00:00:00 2001 From: jolevesq Date: Mon, 2 Dec 2024 09:12:20 -0500 Subject: [PATCH 4/7] Cont cleaning --- .../types/classes/map-feature-config.ts | 2 - .../geo/layer/exceptions/layer-exceptions.ts | 10 +-- .../geoview-layers/abstract-geoview-layers.ts | 11 ++- .../geoview-layers/raster/vector-tiles.ts | 13 --- .../geo/layer/geoview-layers/raster/wms.ts | 15 ---- .../layer/gv-layers/raster/gv-esri-dynamic.ts | 4 +- .../gv-layers/vector/abstract-gv-vector.ts | 4 +- packages/geoview-core/src/geo/layer/layer.ts | 6 +- .../geoview-core/src/geo/utils/utilities.ts | 79 ++++--------------- 9 files changed, 36 insertions(+), 108 deletions(-) diff --git a/packages/geoview-core/src/api/config/types/classes/map-feature-config.ts b/packages/geoview-core/src/api/config/types/classes/map-feature-config.ts index b5793088747..0851e967616 100644 --- a/packages/geoview-core/src/api/config/types/classes/map-feature-config.ts +++ b/packages/geoview-core/src/api/config/types/classes/map-feature-config.ts @@ -289,8 +289,6 @@ export class MapFeatureConfig { const { projection } = this.map.viewSettings; const center = this.map.viewSettings.initialView!.zoomAndCenter![1]; const maxExtent = this.map.viewSettings.maxExtent!; - // TODO: Which one do we want, the commented one or the next one? - // const [extentMinX, extentMinY, extentMaxX, extentMaxY] = getMinOrMaxExtents(maxExtent, CV_MAP_EXTENTS[projection], 'min'); const [extentMinX, extentMinY, extentMaxX, extentMaxY] = maxExtent; const minX = !Number.isNaN(extentMinX) && extentMinX < center[0] ? extentMinX : CV_VALID_MAP_CENTER[projection].long[0]; diff --git a/packages/geoview-core/src/geo/layer/exceptions/layer-exceptions.ts b/packages/geoview-core/src/geo/layer/exceptions/layer-exceptions.ts index eca6210f48b..c77cd9a4fc9 100644 --- a/packages/geoview-core/src/geo/layer/exceptions/layer-exceptions.ts +++ b/packages/geoview-core/src/geo/layer/exceptions/layer-exceptions.ts @@ -1,7 +1,7 @@ /* eslint-disable max-classes-per-file */ // We want more than 1 Error class here to save files import { GeoViewError } from '@/core/exceptions/geoview-exceptions'; -import { AbstractGeoViewLayer } from '@/geo/layer/geoview-layers/abstract-geoview-layers'; +import { AbstractGVLayer } from '@/geo/layer/gv-layers/abstract-gv-layer'; export class GeoViewLayerError extends GeoViewError { // The layer id @@ -37,13 +37,13 @@ export class GeoViewLayerNotCreatedError extends GeoViewLayerError { export class GeoViewLayerCreatedTwiceError extends GeoViewLayerError { // The layer - geoviewLayer: AbstractGeoViewLayer; + geoviewLayer: AbstractGVLayer; - constructor(geoviewLayer: AbstractGeoViewLayer, mapId: string) { - super(geoviewLayer.geoviewLayerId, mapId); + constructor(geoviewLayer: AbstractGVLayer, mapId: string) { + super(geoviewLayer.getGeoviewLayerId(), mapId); // Override the message - this.message = `Can not execute twice the createGeoViewLayers method for layer ${geoviewLayer.geoviewLayerId} on map ${mapId}`; + this.message = `Can not execute twice the createGeoViewLayers method for layer ${geoviewLayer.getGeoviewLayerId()} on map ${mapId}`; // Keep the informations this.geoviewLayer = geoviewLayer; diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts b/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts index 9a1e6e19fd6..dea141af4bf 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts @@ -28,6 +28,7 @@ import { GeoViewLayerCreatedTwiceError } from '@/geo/layer/exceptions/layer-exce import { ConfigBaseClass } from '@/core/utils/config/validation-classes/config-base-class'; import { TypeLegend } from '@/core/stores/store-interface-and-intial-values/layer-state'; import { MapViewer } from '@/geo/map/map-viewer'; +import { AbstractGVLayer } from '../gv-layers/abstract-gv-layer'; // Constant used to define the default layer names const DEFAULT_LAYER_NAMES: Record = { @@ -191,6 +192,14 @@ export abstract class AbstractGeoViewLayer { return MapEventProcessor.getMapViewer(this.mapId); } + /** + * Gets the Geoview layer id. + * @returns {string} The geoview layer id + */ + getGeoviewLayerId(): string { + return this.geoviewLayerId; + } + /** *************************************************************************************************************************** * Gets the layer configuration of the specified layer path. * @@ -367,7 +376,7 @@ export abstract class AbstractGeoViewLayer { if (logTimingsKey) logger.logMarkerCheck(logTimingsKey, 'to process list of layer entry config'); } else { // Raise error - throw new GeoViewLayerCreatedTwiceError(this, this.mapId); + throw new GeoViewLayerCreatedTwiceError(this as unknown as AbstractGVLayer, this.mapId); } } diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts b/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts index 629a6656c1e..34834f17edf 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/raster/vector-tiles.ts @@ -240,17 +240,4 @@ export class VectorTiles extends AbstractGeoViewRaster { } return Promise.resolve(layerConfig); } - - /** - * Set Vector Tile style - * - * @param {string} layerPath Path of layer to style. - * @param {string} styleUrl The url of the styles to apply. - * @returns {Promise} - */ - // GV Layers Refactoring - Obsolete (just should be removed?) - setVectorTileStyle(layerPath: string, styleUrl: string): Promise { - // FIXME: Check if this should be removed or done somewhere else? - return applyStyle(this.getMapViewer().layer.getOLLayer(layerPath) as VectorTileLayer, styleUrl); - } } 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 6ef92de3a5e..f6bd073d178 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 @@ -607,19 +607,4 @@ export class WMS extends AbstractGeoViewRaster { this.setTemporalDimension(layerConfig.layerPath, DateMgt.createDimensionFromOGC(wmsTimeDimension)); } } - - /** *************************************************************************************************************************** - * Set the style to be used by the wms layer. This methode does nothing if the layer path can't be found. - * - * @param {string} wmsStyleId The style identifier that will be used. - * @param {string} layerPath The layer path to the layer's configuration. - */ - // GV Layers Refactoring - Obsolete (in layers) - setWmsStyle(wmsStyleId: string, layerPath: string): void { - // Get the Layer using the trick for now - const layer = this.getOLLayer(layerPath) as ImageLayer | undefined; - - // TODO: Verify if we can apply more than one style at the same time since the parameter name is STYLES - if (layer) layer.getSource()?.updateParams({ STYLES: wmsStyleId }); - } } 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 18f4dfdd6f2..5b45539cdde 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 @@ -8,7 +8,7 @@ import { Extent } from 'ol/extent'; import Feature from 'ol/Feature'; import Geometry from 'ol/geom/Geometry'; -import { validateExtent, getMinOrMaxExtents } from '@/geo/utils/utilities'; +import { validateExtent, getExtentUnion } from '@/geo/utils/utilities'; import { Projection } from '@/geo/utils/projection'; import { logger } from '@/core/utils/logger'; import { DateMgt } from '@/core/utils/date-mgt'; @@ -760,7 +760,7 @@ export class GVEsriDynamic extends AbstractGVRaster { if (extent) { // If extent has not been defined, set it to extent if (!calculatedExtent) calculatedExtent = extent; - else getMinOrMaxExtents(calculatedExtent, extent); + else getExtentUnion(calculatedExtent, extent); } }); diff --git a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts index 29107c48526..0d9088f2dc2 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts @@ -18,7 +18,7 @@ import { TypeFeatureInfoEntry } from '@/geo/map/map-schema-types'; import { analyzeLayerFilter, getAndCreateFeatureStyle } from '@/geo/utils/renderer/geoview-renderer'; import { featureInfoGetFieldType } from '../utils'; import { AbstractGVLayer } from '../abstract-gv-layer'; -import { getMinOrMaxExtents } from '@/geo/utils/utilities'; +import { getExtentUnion } from '@/geo/utils/utilities'; import { TypeOutfieldsType } from '@/api/config/types/map-schema-types'; /** @@ -264,7 +264,7 @@ export abstract class AbstractGVVector extends AbstractGVLayer { if (extent) { // If calculatedExtent has not been defined, set it to extent if (!calculatedExtent) calculatedExtent = extent; - else getMinOrMaxExtents(calculatedExtent, extent); + else getExtentUnion(calculatedExtent, extent); } } }); diff --git a/packages/geoview-core/src/geo/layer/layer.ts b/packages/geoview-core/src/geo/layer/layer.ts index 7b7c2d72097..5ba3005e8ed 100644 --- a/packages/geoview-core/src/geo/layer/layer.ts +++ b/packages/geoview-core/src/geo/layer/layer.ts @@ -44,7 +44,7 @@ import { AllFeatureInfoLayerSet } from '@/geo/layer/layer-sets/all-feature-info- import { LegendsLayerSet } from '@/geo/layer/layer-sets/legends-layer-set'; import { FeatureInfoLayerSet } from '@/geo/layer/layer-sets/feature-info-layer-set'; import { GeoViewLayerCreatedTwiceError, GeoViewLayerNotCreatedError } from '@/geo/layer/exceptions/layer-exceptions'; -import { getExtentUnionMaybe, getMinOrMaxExtents } from '@/geo/utils/utilities'; +import { getExtentUnion } from '@/geo/utils/utilities'; import EventHelper, { EventDelegateBase } from '@/api/events/event-helper'; import { TypeOrderedLayerInfo } from '@/core/stores/store-interface-and-intial-values/map-state'; @@ -1358,7 +1358,7 @@ export class LayerApi { // If bounds has not yet been defined, set to this layers bounds. if (!bounds.length && layerBounds) bounds = layerBounds; - else if (layerBounds) bounds = getMinOrMaxExtents(bounds, layerBounds); + else if (layerBounds) bounds = getExtentUnion(bounds, layerBounds)!; }); } }); @@ -1576,7 +1576,7 @@ export class LayerApi { let boundsUnion: Extent | undefined; boundsArray.forEach((bounds) => { // Union the bounds with each other - boundsUnion = getExtentUnionMaybe(boundsUnion, bounds); + boundsUnion = getExtentUnion(boundsUnion, bounds); }); // Return the unioned bounds diff --git a/packages/geoview-core/src/geo/utils/utilities.ts b/packages/geoview-core/src/geo/utils/utilities.ts index a86d26610f2..39dae427b5b 100644 --- a/packages/geoview-core/src/geo/utils/utilities.ts +++ b/packages/geoview-core/src/geo/utils/utilities.ts @@ -308,42 +308,16 @@ export function convertTypeFeatureStyleToOpenLayersStyle(style?: TypeFeatureStyl return getDefaultDrawingStyle(style?.strokeColor, style?.strokeWidth, style?.fillColor); } -/** - * Compare sets of extents of the same projection and return the smallest or largest set. - * Extents must be in OpenLayers extent format - [minx, miny, maxx, maxy] - * - * @param {Extent} extentsA First set of extents - * @param {Extent} extentsB Second set of extents - * @param {string} minmax Decides whether to get smallest or largest extent - * @returns {Extent} the smallest or largest set from the extents - */ -export function getMinOrMaxExtents(extentsA: Extent, extentsB: Extent, minmax = 'max'): Extent { - // TODO: Check - Obsolete function? Use getExtentUnion or getExtentIntersection - let bounds: Extent = []; - if (minmax === 'max') - bounds = [ - Math.min(extentsA[0], extentsB[0]), - Math.min(extentsA[1], extentsB[1]), - Math.max(extentsA[2], extentsB[2]), - Math.max(extentsA[3], extentsB[3]), - ]; - else if (minmax === 'min') - bounds = [ - Math.max(extentsA[0], extentsB[0]), - Math.max(extentsA[1], extentsB[1]), - Math.min(extentsA[2], extentsB[2]), - Math.min(extentsA[3], extentsB[3]), - ]; - return bounds; -} - /** * Returns the union of 2 extents. - * @param {Extent} extentA First extent - * @param {Extent} extentB Optional second extent - * @returns {Extent} The union of the extents + * @param {Extent | undefined} extentA First extent + * @param {Extent | undefined} extentB Optional second extent + * @returns {Extent | undefined} The union of the extents */ -export function getExtentUnion(extentA: Extent, extentB?: Extent): Extent { +export function getExtentUnion(extentA: Extent | undefined, extentB?: Extent | undefined): Extent | undefined { + // If no A, return B which may be undefined too + if (!extentA) return extentB; + // If no B, return A if (!extentB) return extentA; @@ -356,30 +330,19 @@ export function getExtentUnion(extentA: Extent, extentB?: Extent): Extent { ]; } -/** - * Returns the union of 2 extents supporting the case where extentA might be undefined. - * @param {Extent | undefined} extentA First extent or undefined - * @param {Extent | undefined} extentB Optional second extent - * @returns {Extent | undefined} The union of the extents - */ -export function getExtentUnionMaybe(extentA: Extent | undefined, extentB?: Extent): Extent | undefined { - // If no A, return B which may be undefined too - if (!extentA) return extentB; - - // Redirect - return getExtentUnion(extentA, extentB); -} - /** * Returns the intersection of 2 extents. - * @param {Extent} extentA First extent - * @param {Extent} extentB Optional second extent - * @returns {Extent} The intersection of the extents + * @param {Extent | undefined} extentA First extent + * @param {Extent | undefined} extentB Optional second extent + * @returns {Extent | undefined} The intersection of the extents */ -export function getExtentIntersection(extentA: Extent, extentB?: Extent): Extent { +export function getExtentIntersection(extentA: Extent | undefined, extentB?: Extent | undefined): Extent | undefined { // If no B, return A if (!extentB) return extentA; + // If no A, return B which may be undefined too + if (!extentA) return extentB; + // Return the intersection of A and B return [ Math.max(extentA[0], extentB[0]), @@ -389,20 +352,6 @@ export function getExtentIntersection(extentA: Extent, extentB?: Extent): Extent ]; } -/** - * Returns the intersection of 2 extents supporting the case where extentA might be undefined. - * @param {Extent | undefined} extentA First extent or undefined - * @param {Extent | undefined} extentB Optional second extent - * @returns {Extent | undefined} The intersection of the extents - */ -export function getExtentIntersectionMaybe(extentA: Extent | undefined, extentB?: Extent): Extent | undefined { - // If no A, return B which may be undefined too - if (!extentA) return extentB; - - // Redirect - return getExtentIntersection(extentA, extentB); -} - /** * Converts an extent to a polygon * @param {Extent} extent - The extent to convert From 21ecabf65c9adb68b2a77b95ebe9cf163bbab5ed Mon Sep 17 00:00:00 2001 From: jolevesq Date: Mon, 2 Dec 2024 09:37:17 -0500 Subject: [PATCH 5/7] rebase --- packages/geoview-core/src/geo/map/point-markers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/geoview-core/src/geo/map/point-markers.ts b/packages/geoview-core/src/geo/map/point-markers.ts index 6e4120b662a..8fb2eede6a7 100644 --- a/packages/geoview-core/src/geo/map/point-markers.ts +++ b/packages/geoview-core/src/geo/map/point-markers.ts @@ -176,7 +176,7 @@ export class PointMarkers { if (coordinates.length) { let extent = coordinates[0] as number[]; for (let i = 1; i < coordinates.length; i++) { - extent = getExtentUnion(extent, coordinates[i]); + extent = getExtentUnion(extent, coordinates[i])!; } return extent; From 5ad36feb9d8f1dc7b39f0a0148c4d16de3125314 Mon Sep 17 00:00:00 2001 From: jolevesq Date: Tue, 10 Dec 2024 11:27:17 -0500 Subject: [PATCH 6/7] Rebase --- .../src/geo/layer/gv-layers/raster/gv-wms.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) 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 320e152feb0..af8dde51d58 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 @@ -119,17 +119,16 @@ export class GVWMS extends AbstractGVRaster { const layerConfig = this.getLayerConfig(); // Check if bounds are properly set - // TODO: We always do the check if not bounds are not set properly from layer creation process - // if (!layerConfig.initialSettings!.bounds) { - const newBounds = this.getBounds(this.getLayerPath()); - if (newBounds) - layerConfig.initialSettings!.bounds = Projection.transformExtentFromProj( - newBounds, - this.getMapViewer().getView().getProjection(), - Projection.PROJECTION_NAMES.LNGLAT - ); - // else return []; - // } + if (!layerConfig.initialSettings!.bounds) { + const newBounds = this.getBounds(); + if (newBounds) + layerConfig.initialSettings!.bounds = Projection.transformExtentFromProj( + newBounds, + this.getMapViewer().getView().getProjection(), + Projection.PROJECTION_NAMES.LNGLAT + ); + else return []; + } const clickCoordinate = this.getMapViewer().convertCoordinateLngLatToMapProj(lnglat); if ( From 2e284e029043285002b81e046adcf4ec6524dd57 Mon Sep 17 00:00:00 2001 From: jolevesq Date: Fri, 13 Dec 2024 16:04:05 -0500 Subject: [PATCH 7/7] rebase 3 pr --- .../src/geo/layer/gv-layers/abstract-base-layer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts b/packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts index 46ab381da02..445285d43c2 100644 --- a/packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts +++ b/packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts @@ -146,12 +146,13 @@ export abstract class AbstractBaseLayer { /** * Overridable function that gets the extent of an array of features. * @param {string[]} objectIds - The IDs of the features to calculate the extent from. + * @param {string} outfield - ID field to return for services that require a value in outfields. * @returns {Promise} The extent of the features, if available */ // Added eslint-disable here, because we do want to override this method in children and keep 'this'. // eslint-disable-next-line @typescript-eslint/class-methods-use-this - getExtentFromFeatures(objectIds: string[]): Promise { - logger.logError(`Feature geometry for ${objectIds} is unavailable from ${this.getLayerPath()}`); + getExtentFromFeatures(objectIds: string[], outfield?: string): Promise { + logger.logError(`Feature geometry for ${objectIds}-${outfield} is unavailable from ${this.getLayerPath()}`); return Promise.resolve(undefined); }