diff --git a/packages/layers/src/index.ts b/packages/layers/src/index.ts index 945c398c2a..faafea0d10 100644 --- a/packages/layers/src/index.ts +++ b/packages/layers/src/index.ts @@ -1,6 +1,13 @@ +import { registerProj4 } from '@swissgeo/coordinates' +import proj4 from 'proj4' + // let's export the types "globally" export * from '@/types/layers' export * from '@/types/capabilities' export * from '@/types/timeConfig' export * from '@/validation' +import { register } from 'ol/proj/proj4' + +registerProj4(proj4) +register(proj4) diff --git a/packages/layers/src/parsers/WMSCapabilitiesParser.ts b/packages/layers/src/parsers/WMSCapabilitiesParser.ts index 63786f7d59..877ccfb0f7 100644 --- a/packages/layers/src/parsers/WMSCapabilitiesParser.ts +++ b/packages/layers/src/parsers/WMSCapabilitiesParser.ts @@ -467,41 +467,50 @@ function getFeatureInfoCapability( * @param options.params URL parameters to pass to WMS server * @param options.ignoreError Don't throw exception in case of error, but return a default value or * undefined + * @param options.parentsArray Optional parents array (internal parameter for recursion) * @returns Layer object, or undefined in case of error (and ignoreError is equal to true) */ function getExternalLayer( capabilities: WMSCapabilitiesResponse, layerOrLayerId: WMSCapabilityLayer | string, - options?: ExternalLayerParsingOptions + options?: ExternalLayerParsingOptions, ): ExternalWMSLayer | undefined { if (!layerOrLayerId) { // without a layer object or layer ID we can do nothing return } - const { outputProjection = WGS84, initialValues = {}, ignoreErrors = true } = options ?? {} + const { outputProjection = WGS84, initialValues = {}, ignoreErrors = true, parentsArray } = options ?? {} const { currentYear, params } = initialValues - let layerId: string - if (typeof layerOrLayerId === 'string') { - layerId = layerOrLayerId + let layer: WMSCapabilityLayer | undefined + let parents: WMSCapabilityLayer[] | undefined + + // If we have a layer object, use it directly with provided parents + if (typeof layerOrLayerId !== 'string') { + layer = layerOrLayerId + parents = (parentsArray as WMSCapabilityLayer[]) ?? [] } else { - layerId = layerOrLayerId.Name - } - if (!capabilities.Capability?.Layer?.Layer) { - return - } + // If we have a layer ID, search for it + const layerId = layerOrLayerId + if (!capabilities.Capability?.Layer?.Layer) { + return + } - const layerAndParents = findLayerRecurse( - layerId, - [capabilities.Capability.Layer], - [capabilities.Capability.Layer] - ) - if (!layerAndParents) { - return + const layerAndParents = findLayerRecurse( + layerId, + [capabilities.Capability.Layer], + [capabilities.Capability.Layer] + ) + if (!layerAndParents) { + return + } + layer = layerAndParents.layer + parents = layerAndParents.parents } - const { layer, parents } = layerAndParents + if (!layer) { + const layerId = typeof layerOrLayerId === 'string' ? layerOrLayerId : layerOrLayerId.Name const msg = `No WMS layer ${layerId} found in Capabilities ${capabilities.originUrl.toString()}` log.error({ title: 'WMS Capabilities parser', @@ -513,6 +522,7 @@ function getExternalLayer( } throw new CapabilitiesError(msg, 'no_layer_found') } + // Go through the child to get valid layers let layers: ExternalWMSLayer[] = [] diff --git a/packages/layers/src/parsers/WMTSCapabilitiesParser.ts b/packages/layers/src/parsers/WMTSCapabilitiesParser.ts index 4db1c98b32..48649de44b 100644 --- a/packages/layers/src/parsers/WMTSCapabilitiesParser.ts +++ b/packages/layers/src/parsers/WMTSCapabilitiesParser.ts @@ -445,22 +445,39 @@ function getExternalLayer( } const attributes = getLayerAttributes(capabilities, layer, outputProjection, ignoreErrors) - if (!attributes) { + if (!attributes || !attributes.id) { log.error(`No attributes found for layer ${layer.Identifier}`) return } + let olOptions + try { + olOptions = optionsFromCapabilities(capabilities, { + layer: attributes.id, + projection: outputProjection.epsg, + }) ?? undefined + } catch (error) { + log.warn({ + title: 'WMTS Capabilities parser', + titleColor: LogPreDefinedColor.Indigo, + messages: [`Failed to get OpenLayers options for layer ${attributes.id}`, error], + }) + if (!ignoreErrors) { + throw new CapabilitiesError( + `Failed to parse WMTS layer options: ${error?.toString()}`, + 'invalid_wmts_layer' + ) + } + olOptions = undefined + } + return layerUtils.makeExternalWMTSLayer({ type: LayerType.WMTS, ...attributes, opacity, isVisible, isLoading: false, - options: - optionsFromCapabilities(capabilities, { - layer: attributes.id, - projection: outputProjection.epsg, - }) ?? undefined, + options: olOptions, timeConfig: getTimeConfig(attributes.dimensions), currentYear, }) diff --git a/packages/layers/src/types/capabilities.ts b/packages/layers/src/types/capabilities.ts index 65899c4f54..f3f565c17d 100644 --- a/packages/layers/src/types/capabilities.ts +++ b/packages/layers/src/types/capabilities.ts @@ -255,6 +255,11 @@ export interface ExternalLayerParsingOptions { * layer's visibility or opacity after parsing, without using its default values */ initialValues?: Partial + /** + * Optional parents array (internal parameter for recursion) + * @internal + */ + parentsArray?: unknown[] } export interface CapabilitiesParser< diff --git a/packages/viewer/src/modules/menu/components/LayerCatalogueItem.vue b/packages/viewer/src/modules/menu/components/LayerCatalogueItem.vue index b3693ca2c3..174bcbaf22 100644 --- a/packages/viewer/src/modules/menu/components/LayerCatalogueItem.vue +++ b/packages/viewer/src/modules/menu/components/LayerCatalogueItem.vue @@ -6,7 +6,7 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' import { type FlatExtent, LV95, type NormalizedExtent } from '@swissgeo/coordinates' -import { type GeoAdminGroupOfLayers, type Layer, LayerType } from '@swissgeo/layers' +import { type ExternalWMSLayer, type GeoAdminGroupOfLayers, type Layer, LayerType } from '@swissgeo/layers' import log from '@swissgeo/log' import { booleanContains, polygon } from '@turf/turf' import { computed, onMounted, ref, watch } from 'vue' @@ -61,8 +61,11 @@ const showItem = computed(() => { const isGroupOfLayers = (layer: Layer): layer is GeoAdminGroupOfLayers => { return layer.type === LayerType.GROUP } +const isWmsLayer = (layer: Layer): layer is ExternalWMSLayer => { + return layer.type === LayerType.WMS +} -const hasChildren = computed(() => isGroupOfLayers(item) && item?.layers?.length > 0) +const hasChildren = computed(() => (isGroupOfLayers(item) || isWmsLayer(item)) && item?.layers?.length && item?.layers?.length > 0) const hasDescription = computed(() => canBeAddedToTheMap.value && item?.hasDescription) const isPhoneMode = computed(() => uiStore.isPhoneMode) @@ -72,8 +75,8 @@ const isPhoneMode = computed(() => uiStore.isPhoneMode) */ const hasChildrenMatchSearch = computed(() => { if (search) { - if (isGroupOfLayers(item) && hasChildren.value) { - return containsLayer(item.layers, search) + if (hasChildren.value) { + return containsLayer((item as GeoAdminGroupOfLayers | ExternalWMSLayer).layers as Layer[], search) } return false } @@ -320,12 +323,12 @@ function containsLayer(layers: Layer[], searchText: string): boolean {