diff --git a/conf/default.toml b/conf/default.toml index 52ff28de20..50d40a6fb0 100644 --- a/conf/default.toml +++ b/conf/default.toml @@ -122,6 +122,9 @@ background_color = "#fdfbff" # Optional; Will limit the possibility to zoom in past a certain zoom level # max_zoom = 10 +# Optional; Will not tile WMS. False by default. +# do_not_tile_wms = false + # Optional; will limit the possibility to pan or zoom out outside of an extent # Expressed in the map view projection (EPSG:3857) # max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855] diff --git a/docs/guide/configure.md b/docs/guide/configure.md index 5c07b54f26..967ec3480f 100644 --- a/docs/guide/configure.md +++ b/docs/guide/configure.md @@ -206,6 +206,10 @@ The map section lets you customize how maps appear and behave across GeoNetwork- Will limit the possibility to zoom in past a certain zoom level. +- `do_not_tile_wms` (optional) + + Will not use tiling when requesting WMS services. Defaults to `false` (WMS are tiled). Not using tiles for WMS might incur performance loss since the client will not benefit from an eventual tile cache anymore. On the other hand, visual quality might improve in case a map tile server does not handle neighbouring tiles correctly, e.g. symbols or text being cropped at tile boundaries. + - `max_extent` (optional) Will limit the possibility to pan or zoom outside of an extent. Expressed as an array of _minX_, _minY_, _maxX_ and _maxY_ numerical components in the map view projection (EPSG:3857 by default), e.g.: diff --git a/libs/feature/map/src/lib/map-context/map-context.service.spec.ts b/libs/feature/map/src/lib/map-context/map-context.service.spec.ts index 9de1279924..6ad4b96600 100644 --- a/libs/feature/map/src/lib/map-context/map-context.service.spec.ts +++ b/libs/feature/map/src/lib/map-context/map-context.service.spec.ts @@ -1,6 +1,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing' import { TestBed } from '@angular/core/testing' -import { MAP_CONFIG_FIXTURE } from '@geonetwork-ui/util/app-config' +import { MAP_CONFIG_FIXTURE, MapConfig } from '@geonetwork-ui/util/app-config' import { FeatureCollection } from 'geojson' import { Geometry } from 'ol/geom' import TileLayer from 'ol/layer/Tile' @@ -33,6 +33,8 @@ import { MapContextService, } from './map-context.service' import Feature from 'ol/Feature' +import ImageWMS from 'ol/source/ImageWMS' +import ImageLayer from 'ol/layer/Image' const mapStyleServiceMock = { createDefaultStyle: jest.fn(() => new Style()), @@ -130,35 +132,60 @@ describe('MapContextService', () => { }) describe('WMS', () => { - beforeEach(() => { - ;(layerModel = MAP_CTX_LAYER_WMS_FIXTURE), - (layer = service.createLayer(layerModel)) - }) - it('create a tile layer', () => { - expect(layer).toBeTruthy() - expect(layer).toBeInstanceOf(TileLayer) - }) - it('create a TileWMS source', () => { - const source = layer.getSource() - expect(source).toBeInstanceOf(TileWMS) - }) - it('set correct WMS params', () => { - const source = layer.getSource() - const params = source.getParams() - expect(params.LAYERS).toBe(layerModel.name) - }) - it('set correct url without existing REQUEST and SERVICE params', () => { - const source = layer.getSource() - const urls = source.getUrls() - expect(urls.length).toBe(1) - expect(urls[0]).toBe( - 'https://www.geograndest.fr/geoserver/region-grand-est/ows?REQUEST=GetCapabilities&SERVICE=WMS' - ) + describe('when mapConfig.DO_NOT_TILE_WMS === false', () => { + beforeEach(() => { + const mapConfig: MapConfig = { + ...MAP_CONFIG_FIXTURE, + DO_NOT_TILE_WMS: false, + } + ;(layerModel = MAP_CTX_LAYER_WMS_FIXTURE), + (layer = service.createLayer(layerModel, mapConfig)) + }) + it('create a tile layer', () => { + expect(layer).toBeTruthy() + expect(layer).toBeInstanceOf(TileLayer) + }) + it('create a TileWMS source', () => { + const source = layer.getSource() + expect(source).toBeInstanceOf(TileWMS) + }) + it('set correct WMS params', () => { + const source = layer.getSource() + const params = source.getParams() + expect(params.LAYERS).toBe(layerModel.name) + }) + it('set correct url without existing REQUEST and SERVICE params', () => { + const source = layer.getSource() + const urls = source.getUrls() + expect(urls.length).toBe(1) + expect(urls[0]).toBe( + 'https://www.geograndest.fr/geoserver/region-grand-est/ows?REQUEST=GetCapabilities&SERVICE=WMS' + ) + }) }) - it('set WMS gutter of 20px', () => { - const source = layer.getSource() - const gutter = source.gutter_ - expect(gutter).toBe(20) + + describe('when mapConfig.DO_NOT_TILE_WMS === true', () => { + beforeEach(() => { + const mapConfig: MapConfig = { + ...MAP_CONFIG_FIXTURE, + DO_NOT_TILE_WMS: true, + } + ;(layerModel = MAP_CTX_LAYER_WMS_FIXTURE), + (layer = service.createLayer(layerModel, mapConfig)) + }) + it('create an image layer', () => { + expect(layer).toBeTruthy() + expect(layer).toBeInstanceOf(ImageLayer) + }) + it('create an ImageWMS source', () => { + const source = layer.getSource() + expect(source).toBeInstanceOf(ImageWMS) + }) + it('set correct WMS params', () => { + const source = layer.getSource() + const params = source.getParams() + expect(params.LAYERS).toBe(layerModel.name) + }) }) }) diff --git a/libs/feature/map/src/lib/map-context/map-context.service.ts b/libs/feature/map/src/lib/map-context/map-context.service.ts index 8b63e20b48..eed2f51d89 100644 --- a/libs/feature/map/src/lib/map-context/map-context.service.ts +++ b/libs/feature/map/src/lib/map-context/map-context.service.ts @@ -29,6 +29,8 @@ import OGCVectorTile from 'ol/source/OGCVectorTile.js' import { MVT } from 'ol/format' import VectorTileLayer from 'ol/layer/VectorTile' import OGCMapTile from 'ol/source/OGCMapTile.js' +import ImageLayer from 'ol/layer/Image' +import ImageWMS from 'ol/source/ImageWMS' export const DEFAULT_BASELAYER_CONTEXT: MapContextLayerXyzModel = { type: MapContextLayerTypeEnum.XYZ, @@ -73,11 +75,13 @@ export class MapContextService { } map.setView(this.createView(mapContext.view, map)) map.getLayers().clear() - mapContext.layers.forEach((layer) => map.addLayer(this.createLayer(layer))) + mapContext.layers.forEach((layer) => + map.addLayer(this.createLayer(layer, mapConfig)) + ) return map } - createLayer(layerModel: MapContextLayerModel): Layer { + createLayer(layerModel: MapContextLayerModel, mapConfig?: MapConfig): Layer { const { type } = layerModel const style = this.styleService.styles.default switch (type) { @@ -112,13 +116,22 @@ export class MapContextService { }), }) case MapContextLayerTypeEnum.WMS: - return new TileLayer({ - source: new TileWMS({ - url: layerModel.url, - params: { LAYERS: layerModel.name }, - gutter: 20, - }), - }) + if (mapConfig?.DO_NOT_TILE_WMS) { + return new ImageLayer({ + source: new ImageWMS({ + url: layerModel.url, + params: { LAYERS: layerModel.name }, + }), + }) + } else { + return new TileLayer({ + source: new TileWMS({ + url: layerModel.url, + params: { LAYERS: layerModel.name, TILED: true }, + }), + }) + } + case MapContextLayerTypeEnum.WMTS: { // TODO: isolate this in utils service const olLayer = new TileLayer({}) 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 137fa856ad..24870f16b6 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 @@ -15,7 +15,7 @@ import { MapUtilsService, } from '@geonetwork-ui/feature/map' import { getOptionalMapConfig, MapConfig } from '@geonetwork-ui/util/app-config' -import { getLinkLabel, ProxyService } from '@geonetwork-ui/util/shared' +import { getLinkLabel } from '@geonetwork-ui/util/shared' import Feature from 'ol/Feature' import { Geometry } from 'ol/geom' import { StyleLike } from 'ol/style/Style' @@ -23,10 +23,8 @@ import { BehaviorSubject, combineLatest, from, - lastValueFrom, Observable, of, - startWith, Subscription, throwError, withLatestFrom, @@ -117,7 +115,9 @@ export class MapViewComponent implements OnInit, OnDestroy { }, } as MapContextModel) ), - tap(() => this.resetSelection()) + tap((res) => { + this.resetSelection() + }) ) ), withLatestFrom(this.mdViewFacade.metadata$), diff --git a/libs/util/app-config/src/lib/app-config.ts b/libs/util/app-config/src/lib/app-config.ts index 35edc71193..04ca6b87fd 100644 --- a/libs/util/app-config/src/lib/app-config.ts +++ b/libs/util/app-config/src/lib/app-config.ts @@ -144,6 +144,7 @@ export function loadAppConfig() { [], [ 'max_zoom', + 'do_not_tile_wms', 'max_extent', 'baselayer', 'do_not_use_default_basemap', @@ -158,6 +159,7 @@ export function loadAppConfig() { ? null : ({ MAX_ZOOM: parsedMapSection.max_zoom, + DO_NOT_TILE_WMS: parsedMapSection.do_not_tile_wms, MAX_EXTENT: parsedMapSection.max_extent, EXTERNAL_VIEWER_URL_TEMPLATE: parsedMapSection.external_viewer_url_template, diff --git a/libs/util/app-config/src/lib/fixtures.ts b/libs/util/app-config/src/lib/fixtures.ts index 243a955c65..cd86492fff 100644 --- a/libs/util/app-config/src/lib/fixtures.ts +++ b/libs/util/app-config/src/lib/fixtures.ts @@ -81,6 +81,7 @@ export const MAP_CONFIG_FIXTURE: MapConfig = { MAX_ZOOM: 10, MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855], DO_NOT_USE_DEFAULT_BASEMAP: false, + DO_NOT_TILE_WMS: false, EXTERNAL_VIEWER_URL_TEMPLATE: 'https://example.com/myviewer/#/?actions=[{"type":"CATALOG:ADD_LAYERS_FROM_CATALOGS","layers":["${layer_name}"],"sources":[{"url":"${service_url}","type":"${service_type}"}]}]', EXTERNAL_VIEWER_OPEN_NEW_TAB: true, diff --git a/libs/util/app-config/src/lib/model.ts b/libs/util/app-config/src/lib/model.ts index ebf926f2ff..84682b6eee 100644 --- a/libs/util/app-config/src/lib/model.ts +++ b/libs/util/app-config/src/lib/model.ts @@ -20,6 +20,7 @@ export interface LayerConfig { export interface MapConfig { MAX_ZOOM?: number + DO_NOT_TILE_WMS: boolean MAX_EXTENT?: [number, number, number, number] // Expressed as [minx, miny, maxx, maxy] EXTERNAL_VIEWER_URL_TEMPLATE?: string EXTERNAL_VIEWER_OPEN_NEW_TAB?: boolean