From 76d797551a44451b83ecc4e08ae3159dd0fff16c Mon Sep 17 00:00:00 2001 From: Florent Gravin Date: Tue, 19 Dec 2023 14:51:12 +0100 Subject: [PATCH 1/3] refactor(wmts): return the layer from the getCapabilities instead of just returning the Options --- .../add-layer-record-preview.component.ts | 7 +---- .../src/lib/utils/map-utils.service.spec.ts | 28 +++++++++++-------- .../map/src/lib/utils/map-utils.service.ts | 16 ++++++++--- .../lib/map-view/map-view.component.spec.ts | 4 +-- .../src/lib/map-view/map-view.component.ts | 7 +---- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.ts b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.ts index 07bc6804a1..278dca0fc2 100644 --- a/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.ts +++ b/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component.ts @@ -59,12 +59,7 @@ export class AddLayerRecordPreviewComponent extends RecordPreviewComponent { name: link.name, }) } else if (link.accessServiceProtocol === 'wmts') { - return this.mapUtils.getWmtsOptionsFromCapabilities(link).pipe( - map((options) => ({ - type: MapContextLayerTypeEnum.WMTS, - options: options, - })) - ) + return this.mapUtils.getWmtsLayerFromCapabilities(link) } return throwError(() => 'protocol not supported') } diff --git a/libs/feature/map/src/lib/utils/map-utils.service.spec.ts b/libs/feature/map/src/lib/utils/map-utils.service.spec.ts index 2dc0bf257a..9ac0a29341 100644 --- a/libs/feature/map/src/lib/utils/map-utils.service.spec.ts +++ b/libs/feature/map/src/lib/utils/map-utils.service.spec.ts @@ -27,6 +27,7 @@ import { } from 'ol/interaction' import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record' import MapBrowserEvent from 'ol/MapBrowserEvent' +import { MapContextLayerWmtsModel } from '@geonetwork-ui/feature/map' jest.mock('ol/proj/proj4', () => { const fromEPSGCodeMock = jest.fn() @@ -444,7 +445,7 @@ describe('MapUtilsService', () => { window.fetch = originalFetch }) describe('nominal', () => { - let wmtsOptions: Options + let wmtsLayer: MapContextLayerWmtsModel beforeEach(async () => { ;(window as any).fetch = jest.fn(() => Promise.resolve({ @@ -453,8 +454,8 @@ describe('MapUtilsService', () => { text: () => Promise.resolve(SAMPLE_WMTS_CAPABILITIES), }) ) - wmtsOptions = await readFirst( - service.getWmtsOptionsFromCapabilities(SAMPLE_WMTS_LINK) + wmtsLayer = await readFirst( + service.getWmtsLayerFromCapabilities(SAMPLE_WMTS_LINK) ) }) it('appends query params to the URL', () => { @@ -463,13 +464,16 @@ describe('MapUtilsService', () => { ) }) it('returns appropriate WMTS options', () => { - expect(wmtsOptions).toMatchObject({ - format: 'image/jpeg', - layer: 'GEOGRAPHICALGRIDSYSTEMS.ETATMAJOR10', - matrixSet: 'PM', - requestEncoding: 'KVP', - style: 'normal', - urls: ['https://wxs.ign.fr/cartes/geoportail/wmts?'], + expect(wmtsLayer).toMatchObject({ + type: 'wmts', + options: { + format: 'image/jpeg', + layer: 'GEOGRAPHICALGRIDSYSTEMS.ETATMAJOR10', + matrixSet: 'PM', + requestEncoding: 'KVP', + style: 'normal', + urls: ['https://wxs.ign.fr/cartes/geoportail/wmts?'], + }, }) }) }) @@ -489,7 +493,7 @@ describe('MapUtilsService', () => { ) try { await readFirst( - service.getWmtsOptionsFromCapabilities(SAMPLE_WMTS_LINK) + service.getWmtsLayerFromCapabilities(SAMPLE_WMTS_LINK) ) } catch (e) { error = e @@ -515,7 +519,7 @@ describe('MapUtilsService', () => { ) try { await readFirst( - service.getWmtsOptionsFromCapabilities(SAMPLE_WMTS_LINK) + service.getWmtsLayerFromCapabilities(SAMPLE_WMTS_LINK) ) } catch (e) { error = e diff --git a/libs/feature/map/src/lib/utils/map-utils.service.ts b/libs/feature/map/src/lib/utils/map-utils.service.ts index 03871ce085..daf9304b5a 100644 --- a/libs/feature/map/src/lib/utils/map-utils.service.ts +++ b/libs/feature/map/src/lib/utils/map-utils.service.ts @@ -23,7 +23,11 @@ import { import WMTSCapabilities from 'ol/format/WMTSCapabilities' import { from, Observable, of } from 'rxjs' import { map } from 'rxjs/operators' -import { MapContextLayerModel } from '../map-context/map-context.model' +import { + MapContextLayerModel, + MapContextLayerTypeEnum, + MapContextLayerWmtsModel, +} from '../map-context/map-context.model' import { MapUtilsWMSService } from './map-utils-wms.service' import Collection from 'ol/Collection' import MapBrowserEvent from 'ol/MapBrowserEvent' @@ -163,9 +167,9 @@ export class MapUtilsService { ) } - getWmtsOptionsFromCapabilities( + getWmtsLayerFromCapabilities( link: DatasetDistribution - ): Observable { + ): Observable { const getCapabilitiesUrl = new URL(link.url, window.location.toString()) getCapabilitiesUrl.searchParams.set('SERVICE', 'WMTS') getCapabilitiesUrl.searchParams.set('REQUEST', 'GetCapabilities') @@ -183,10 +187,14 @@ ${await response.text()}`) .then(function (text) { try { const result = new WMTSCapabilities().read(text) - return optionsFromCapabilities(result, { + const options = optionsFromCapabilities(result, { layer: link.name, matrixSet: 'EPSG:3857', }) + return { + options, + type: MapContextLayerTypeEnum.WMTS as 'wmts', + } } catch (e: any) { throw new Error(`WMTS GetCapabilities parsing failed: ${e.stack || e.message || e}`) diff --git a/libs/feature/record/src/lib/map-view/map-view.component.spec.ts b/libs/feature/record/src/lib/map-view/map-view.component.spec.ts index d8a495eda7..54680f2ba2 100644 --- a/libs/feature/record/src/lib/map-view/map-view.component.spec.ts +++ b/libs/feature/record/src/lib/map-view/map-view.component.spec.ts @@ -80,9 +80,9 @@ class MapUtilsServiceMock { } }) }) - getWmtsOptionsFromCapabilities = jest.fn(function () { + getWmtsLayerFromCapabilities = jest.fn(function () { return new Observable((observer) => { - observer.next(null) + observer.next({ type: 'wmts', options: null }) }) }) prioritizePageScroll = jest.fn() diff --git a/libs/feature/record/src/lib/map-view/map-view.component.ts b/libs/feature/record/src/lib/map-view/map-view.component.ts index e638a14263..2adc81284c 100644 --- a/libs/feature/record/src/lib/map-view/map-view.component.ts +++ b/libs/feature/record/src/lib/map-view/map-view.component.ts @@ -173,12 +173,7 @@ export class MapViewComponent implements OnInit, OnDestroy { link.type === 'service' && link.accessServiceProtocol === 'wmts' ) { - return this.mapUtils.getWmtsOptionsFromCapabilities(link).pipe( - map((options) => ({ - type: MapContextLayerTypeEnum.WMTS, - options: options, - })) - ) + return this.mapUtils.getWmtsLayerFromCapabilities(link) } else if ( (link.type === 'service' && (link.accessServiceProtocol === 'wfs' || From afc7fe62869a0735eac5d6d6d3faebf8333c7bb0 Mon Sep 17 00:00:00 2001 From: Florent Gravin Date: Tue, 19 Dec 2023 15:27:51 +0100 Subject: [PATCH 2/3] feat(mapContext): add optional extent in WMTS model --- libs/feature/map/src/lib/map-context/map-context.model.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/feature/map/src/lib/map-context/map-context.model.ts b/libs/feature/map/src/lib/map-context/map-context.model.ts index 6c40ecd8ed..e005842a20 100644 --- a/libs/feature/map/src/lib/map-context/map-context.model.ts +++ b/libs/feature/map/src/lib/map-context/map-context.model.ts @@ -25,6 +25,7 @@ export interface MapContextLayerWmsModel { export interface MapContextLayerWmtsModel { type: 'wmts' options: Options + extent?: Extent } interface MapContextLayerWfsModel { From 206676fb0ed22d912866c8849501c2f09531d824 Mon Sep 17 00:00:00 2001 From: Florent Gravin Date: Tue, 19 Dec 2023 15:28:58 +0100 Subject: [PATCH 3/3] fix(wmts): get extent from capabilities instead of from the tilegrid This fix the zoom to layer option on the map context while loading a WMTS layer --- .../src/lib/utils/map-utils.service.spec.ts | 36 +++++++++++++++++-- .../map/src/lib/utils/map-utils.service.ts | 12 ++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/libs/feature/map/src/lib/utils/map-utils.service.spec.ts b/libs/feature/map/src/lib/utils/map-utils.service.spec.ts index 9ac0a29341..545e746b09 100644 --- a/libs/feature/map/src/lib/utils/map-utils.service.spec.ts +++ b/libs/feature/map/src/lib/utils/map-utils.service.spec.ts @@ -9,7 +9,6 @@ import Map from 'ol/Map' import ImageWMS from 'ol/source/ImageWMS' import TileWMS from 'ol/source/TileWMS' import XYZ from 'ol/source/XYZ' -import { Options } from 'ol/source/WMTS' import { of } from 'rxjs' import { MapUtilsWMSService } from './map-utils-wms.service' import { @@ -27,7 +26,7 @@ import { } from 'ol/interaction' import { DatasetServiceDistribution } from '@geonetwork-ui/common/domain/model/record' import MapBrowserEvent from 'ol/MapBrowserEvent' -import { MapContextLayerWmtsModel } from '@geonetwork-ui/feature/map' +import type { MapContextLayerWmtsModel } from '../map-context/map-context.model' jest.mock('ol/proj/proj4', () => { const fromEPSGCodeMock = jest.fn() @@ -476,6 +475,39 @@ describe('MapUtilsService', () => { }, }) }) + describe('layer extent', () => { + describe('when the WGS84BoundingBox is defined', () => { + it('set the WGS84BoundingBox', () => { + expect(wmtsLayer.extent).toEqual([ + 1.82682, 48.3847, 2.79738, 49.5142, + ]) + }) + }) + describe('when the WGS84BoundingBox is not defined', () => { + beforeEach(async () => { + ;(window as any).fetch = jest.fn(() => + Promise.resolve({ + ok: true, + status: 200, + text: () => + Promise.resolve( + SAMPLE_WMTS_CAPABILITIES.replace( + /WGS84BoundingBox/g, + 'NoWGS84BoundingBox' + ) + ), + }) + ) + wmtsLayer = await readFirst( + service.getWmtsLayerFromCapabilities(SAMPLE_WMTS_LINK) + ) + }) + + it('set the WGS84BoundingBox', () => { + expect(wmtsLayer.extent).toBeUndefined() + }) + }) + }) }) describe('http error', () => { let error diff --git a/libs/feature/map/src/lib/utils/map-utils.service.ts b/libs/feature/map/src/lib/utils/map-utils.service.ts index daf9304b5a..92ab59ac87 100644 --- a/libs/feature/map/src/lib/utils/map-utils.service.ts +++ b/libs/feature/map/src/lib/utils/map-utils.service.ts @@ -154,7 +154,11 @@ export class MapUtilsService { } else if (layer && layer.type === 'wms') { geographicExtent = this.wmsUtils.getLayerLonLatBBox(layer) } else if (layer && layer.type === 'wmts') { - return of(layer.options.tileGrid.getExtent()) + if (layer.extent) { + geographicExtent = of(layer.extent) + } else { + return of(layer.options.tileGrid.getExtent()) + } } else { return of(null) } @@ -191,9 +195,15 @@ ${await response.text()}`) layer: link.name, matrixSet: 'EPSG:3857', }) + const layerCap = result?.Contents?.Layer.find( + (layer) => layer.Identifier === link.name + ) return { options, type: MapContextLayerTypeEnum.WMTS as 'wmts', + ...(layerCap?.WGS84BoundingBox + ? { extent: layerCap.WGS84BoundingBox } + : {}), } } catch (e: any) { throw new Error(`WMTS GetCapabilities parsing failed: