diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/esri-layer-common.ts b/packages/geoview-core/src/geo/layer/geoview-layers/esri-layer-common.ts index 263fd5e7b5c..3a425c35211 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/esri-layer-common.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/esri-layer-common.ts @@ -268,19 +268,22 @@ export function commonProcessFeatureInfoConfig( const queryable = (layerMetadata.capabilities as string).includes('Query'); if (layerConfig.source.featureInfo) { // if queryable flag is undefined, set it accordingly to what is specified in the metadata - if (layerConfig.source.featureInfo.queryable === undefined) layerConfig.source.featureInfo.queryable = queryable; + if (layerConfig.source.featureInfo.queryable === undefined && layerMetadata.fields?.length) + layerConfig.source.featureInfo.queryable = queryable; // else the queryable flag comes from the user config. - else if (layerConfig.source.featureInfo.queryable && !layerMetadata.fields && layerMetadata.type !== 'Group Layer') { + else if (layerConfig.source.featureInfo.queryable && layerMetadata.type !== 'Group Layer') { layerConfig.layerStatus = 'error'; throw new Error( `The config whose layer path is ${layerPath} cannot set a layer as queryable because it does not have field definitions` ); } - } else layerConfig.source.featureInfo = layerConfig.isMetadataLayerGroup ? { queryable: false } : { queryable }; + } else + layerConfig.source.featureInfo = + layerConfig.isMetadataLayerGroup || !layerMetadata.fields?.length ? { queryable: false } : { queryable }; MapEventProcessor.setMapLayerQueryable(layer.mapId, layerPath, layerConfig.source.featureInfo.queryable); // dynamic group layer doesn't have fields definition - if (layerMetadata.type !== 'Group Layer') { + if (layerMetadata.type !== 'Group Layer' && layerMetadata.fields) { // Process undefined outfields or aliasFields if (!layerConfig.source.featureInfo.outfields?.length) { if (!layerConfig.source.featureInfo.outfields) layerConfig.source.featureInfo.outfields = []; @@ -371,6 +374,7 @@ export async function commonProcessLayerMetadata< if (queryUrl) { if (layerConfig.geoviewLayerConfig.geoviewLayerType !== CONST_LAYER_TYPES.ESRI_IMAGE) queryUrl = queryUrl.endsWith('/') ? `${queryUrl}${layerConfig.layerId}` : `${queryUrl}/${layerConfig.layerId}`; + try { const { data } = await axios.get(`${queryUrl}?f=json`); if (data?.error) { 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..fc5cfc9a69c 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 @@ -278,6 +278,12 @@ export class EsriDynamic extends AbstractGeoViewRaster { // Create the source const source = new ImageArcGISRest(sourceOptions); + // Raster layer queries do not accept any layerDefs + if (this.metadata?.layers[0].type === 'Raster Layer') { + const params = source.getParams(); + source.updateParams({ ...params, layerDefs: '' }); + } + // GV Time to request an OpenLayers layer! const requestResult = this.emitLayerRequesting({ config: layerConfig, source }); 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 a3cb646d83c..927a809633b 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 @@ -19,11 +19,17 @@ import { TypeFeatureInfoEntry, rangeDomainType, codedValueType, + TypeLayerStyleConfig, + TypeLayerStyleConfigInfo, } from '@/geo/map/map-schema-types'; import { esriGetFieldType, esriGetFieldDomain } from '../utils'; import { AbstractGVRaster } from './abstract-gv-raster'; import { TypeOutfieldsType } from '@/api/config/types/map-schema-types'; import { TypeJsonObject } from '@/api/config/types/config-types'; +import { getLegendStyles } from '@/geo/utils/renderer/geoview-renderer'; +import { CONST_LAYER_TYPES } from '../../geoview-layers/abstract-geoview-layers'; +import { TypeLegend } from '@/core/stores/store-interface-and-intial-values/layer-state'; +import { TypeEsriImageLayerLegend } from './gv-esri-image'; type TypeFieldOfTheSameValue = { value: string | number | Date; nbOccurence: number }; type TypeQueryTree = { fieldValue: string | number | Date; nextField: TypeQueryTree }[]; @@ -656,6 +662,84 @@ export class GVEsriDynamic extends AbstractGVRaster { } } + /** + * Overrides the fetching of the legend for an Esri Dynamic layer. + * @returns {Promise} The legend of the layer or null. + */ + override async getLegend(): Promise { + const layerConfig = this.getLayerConfig(); + // Only raster layers need the alternate code + if (layerConfig.getLayerMetadata()?.type !== 'Raster Layer') return super.getLegend(); + + try { + 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.toString() === layerConfig.layerId); + if (layerInfo) legendInfo = layerInfo.legend; + } + + if (!legendInfo) { + const legend: TypeLegend = { + type: CONST_LAYER_TYPES.ESRI_IMAGE, + styleConfig: this.getStyle(layerConfig.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: false, + 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 + // Set the style + this.setStyle(layerConfig.layerPath, styleConfig); + + const legend: TypeLegend = { + type: CONST_LAYER_TYPES.ESRI_IMAGE, + styleConfig, + legend: await getLegendStyles(this.getStyle(layerConfig.layerPath)), + }; + + return legend; + } catch (error) { + logger.logError(`Get Legend for ${layerConfig.layerPath} error`, error); + return null; + } + } + /** * Overrides when the layer gets in loaded status. */ @@ -714,7 +798,9 @@ export class GVEsriDynamic extends AbstractGVRaster { )}`; }); - olLayer?.getSource()!.updateParams({ layerDefs: `{"${layerConfig.layerId}": "${filterValueToUse}"}` }); + // Raster layer queries do not accept any layerDefs + const layerDefs = layerConfig.getLayerMetadata()?.type === 'Raster Layer' ? '' : `{"${layerConfig.layerId}": "${filterValueToUse}"}`; + olLayer?.getSource()!.updateParams({ layerDefs }); olLayer?.changed(); // Emit event 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..e3b857510d0 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 @@ -255,7 +255,8 @@ export class GVEsriImage extends AbstractGVRaster { } } -interface TypeEsriImageLayerLegend { +// Exported for use in ESRI Dynamic raster layers +export interface TypeEsriImageLayerLegend { layers: { layerId: number | string; layerName: string; diff --git a/packages/geoview-core/src/geo/utils/projection.ts b/packages/geoview-core/src/geo/utils/projection.ts index 3a0e9f4542c..2163caa1d0a 100644 --- a/packages/geoview-core/src/geo/utils/projection.ts +++ b/packages/geoview-core/src/geo/utils/projection.ts @@ -38,6 +38,7 @@ export abstract class Projection { CRS84: 'CRS:84', // Supporting CRS:84 which is equivalent to 4326 except it's long-lat, whereas the 4326 standard is lat-long. CSRS: 'EPSG:4617', CSRS98: 'EPSG:4140', + 3400: 'EPSG:3400', }; // Incremental number when creating custom WKTs on the fly @@ -473,6 +474,21 @@ function init102190Projection(): void { if (projection) Projection.PROJECTIONS['102190'] = projection; } +/** + * Initializes the EPSG:3400 projection + */ +function init3400Projection(): void { + proj4.defs( + Projection.PROJECTION_NAMES[3400], + '+proj=tmerc +lat_0=0 +lon_0=-115 +k=0.9992 +x_0=500000 +y_0=0 +datum=NAD83 +units=m +no_defs +type=crs' + ); + register(proj4); + + const projection = olGetProjection(Projection.PROJECTION_NAMES[3400]); + + if (projection) Projection.PROJECTIONS['3400'] = projection; +} + // Initialize the supported projections initCRS84Projection(); init4326Projection(); @@ -486,4 +502,5 @@ init4269Projection(); init102100Projection(); init102184Projection(); init102190Projection(); +init3400Projection(); logger.logInfo('Projections initialized'); diff --git a/packages/geoview-core/src/geo/utils/renderer/geoview-renderer.ts b/packages/geoview-core/src/geo/utils/renderer/geoview-renderer.ts index 81f487b0cd2..dea415558b5 100644 --- a/packages/geoview-core/src/geo/utils/renderer/geoview-renderer.ts +++ b/packages/geoview-core/src/geo/utils/renderer/geoview-renderer.ts @@ -1136,7 +1136,6 @@ async function getPointStyleSubRoutine( export async function getLegendStyles(styleConfig: TypeLayerStyleConfig | undefined): Promise { try { if (!styleConfig) return {}; - const legendStyles: TypeVectorLayerStyles = {}; if (styleConfig.Point) { // ======================================================================================================================