From 8dda162566e9eb63f71a6a262f6bb293135c0151 Mon Sep 17 00:00:00 2001 From: Tobias Kohr Date: Thu, 17 Feb 2022 16:05:22 +0100 Subject: [PATCH 1/4] feat(app-config): enable configuring layers --- conf/default.toml | 20 +++++- .../app-config/src/lib/app-config.spec.ts | 24 +++++++ libs/util/app-config/src/lib/app-config.ts | 68 ++++++++++++++++++- libs/util/app-config/src/lib/fixtures.ts | 26 +++++++ 4 files changed, 134 insertions(+), 4 deletions(-) diff --git a/conf/default.toml b/conf/default.toml index b75c7e49b0..c65612c55d 100644 --- a/conf/default.toml +++ b/conf/default.toml @@ -15,9 +15,27 @@ proxy_path = "" # The map section allows to override default settings of the map view. # Example: # -# [map] +[map] # max_zoom = 10 # max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855] +# use_basemap_from_layers = false +# +# Layers (basemaps, overlays, masks) can be added to the map view with three properties: +# - type (mandatory): Indicates the layer type. Possible values are 'xyz', 'wms', 'wfs'. +# - url (mandatory): Layer endpoint URL. +# - name: Mandatory for 'wms', 'wfs' types where it indicates the layer name. +# Layer order in the config is the same as in the map, the first one being the bottom one. +# If you want to override the default basemap with the first layer of your config set use_basemap_from_layers = true (default: false) +# Each layer is defined in its own [[layers]] section. +# Example: +# [[layers]] +# type = "xyz" +# url = "https://{a-c}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png" +# [[layers]] +# type = "wfs" +# url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" +# name = "masque_hdf_ign_carto_latin1" +# ### VISUAL THEME diff --git a/libs/util/app-config/src/lib/app-config.spec.ts b/libs/util/app-config/src/lib/app-config.spec.ts index 624c2f5d00..72a0d5de61 100644 --- a/libs/util/app-config/src/lib/app-config.spec.ts +++ b/libs/util/app-config/src/lib/app-config.spec.ts @@ -32,6 +32,17 @@ another_path = '/whatever' [map] max_zoom = 10 max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855] +use_basemap_from_layers = false +another_zoom = 15 +[[layers]] +type = "wms" +url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" +name = "masque_hdf_ign_carto_latin1" +[[layers]] +type = "wfs" +url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" +name = "masque_hdf_ign_carto_latin1" +another_layer = "wrong layer definition" [theme] primary_color = "#093564" @@ -146,6 +157,19 @@ describe('app config utils', () => { MAX_EXTENT: [ -418263.418776, 5251529.591305, 961272.067714, 6706890.609855, ], + USE_BASEMAP_FROM_LAYERS: false, + LAYERS: [ + { + TYPE: 'wms', + URL: 'https://www.geo2france.fr/geoserver/cr_hdf/ows', + NAME: 'masque_hdf_ign_carto_latin1', + }, + { + TYPE: 'wfs', + URL: 'https://www.geo2france.fr/geoserver/cr_hdf/ows', + NAME: 'masque_hdf_ign_carto_latin1', + }, + ], }) }) }) diff --git a/libs/util/app-config/src/lib/app-config.ts b/libs/util/app-config/src/lib/app-config.ts index 666526248a..22d793214e 100644 --- a/libs/util/app-config/src/lib/app-config.ts +++ b/libs/util/app-config/src/lib/app-config.ts @@ -17,9 +17,16 @@ export function getGlobalConfig(): GlobalConfig { return globalConfig } +export interface LayerConfig { + TYPE: 'xyz' | 'wms' | 'wfs' + URL: string + NAME?: string +} export interface MapConfig { MAX_ZOOM?: number MAX_EXTENT?: Extent + USE_BASEMAP_FROM_LAYERS?: boolean + LAYERS?: LayerConfig[] } let mapConfig: MapConfig = null @@ -88,8 +95,13 @@ export function loadAppConfig() { `An error occurred when parsing the configuration file: ${e.message}` ) } - - const { global, map, theme, translations: translationsNested } = parsed + const { + global, + map, + layers, + theme, + translations: translationsNested, + } = parsed const errors = [] const warnings = [] @@ -107,7 +119,11 @@ export function loadAppConfig() { ) } - const mapCheck = checkKeys(map || {}, [], ['max_zoom', 'max_extent']) + const mapCheck = checkKeys( + map || {}, + [], + ['max_zoom', 'max_extent', 'baselayer', 'use_basemap_from_layers'] + ) if (mapCheck.missing.length) { errors.push(`In the [map] section: ${mapCheck.missing.join(', ')}`) } else if (mapCheck.unrecognized.length) { @@ -116,6 +132,38 @@ export function loadAppConfig() { ) } + let layersCheck = { missing: [], unrecognized: [] } + if (layers) { + layers.forEach((layer) => { + const { missing, unrecognized } = checkKeys( + layer || {}, + ['type', 'url'], + ['name'] + ) + layersCheck = { + missing: [ + ...layersCheck.missing, + ...(missing.length ? [missing] : []), + ], + unrecognized: [ + ...layersCheck.unrecognized, + ...(unrecognized.length ? [unrecognized] : []), + ], + } + }) + if (layersCheck.missing.length) { + errors.push( + `In some [layers] definition: ${layersCheck.missing.join(', ')}` + ) + } else if (layersCheck.unrecognized.length) { + warnings.push( + `In some [layers] definition: ${layersCheck.unrecognized.join( + ', ' + )}` + ) + } + } + const themeCheck = checkKeys( theme || {}, ['primary_color', 'secondary_color', 'main_color', 'background_color'], @@ -156,10 +204,24 @@ ${warnings.join('\n')}`) GN4_API_URL: global.geonetwork4_api_url, PROXY_PATH: global.proxy_path, } + const layersConfig: LayerConfig[] = [] + if (layers) { + layers.forEach((layerConfig) => { + layersConfig.push({ + TYPE: layerConfig.type, + URL: layerConfig.url, + NAME: layerConfig.name, + }) + }) + } mapConfig = map ? { MAX_ZOOM: map.max_zoom, MAX_EXTENT: map.max_extent, + USE_BASEMAP_FROM_LAYERS: map.use_basemap_from_layers, + ...(layersConfig.length && { + LAYERS: layersConfig, + }), } : {} themeConfig = { diff --git a/libs/util/app-config/src/lib/fixtures.ts b/libs/util/app-config/src/lib/fixtures.ts index 60d98cd967..38aa8a8805 100644 --- a/libs/util/app-config/src/lib/fixtures.ts +++ b/libs/util/app-config/src/lib/fixtures.ts @@ -6,6 +6,15 @@ proxy_path = "/proxy/?url=" [map] max_zoom = 10 max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855] +use_basemap_from_layers = false +[[layers]] +type = "wms" +url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" +name = "masque_hdf_ign_carto_latin1" +[[layers]] +type = "wfs" +url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" +name = "masque_hdf_ign_carto_latin1" [theme] primary_color = "#093564" @@ -33,4 +42,21 @@ my.sample.text = "Un bon exemple de texte." export const MAP_CONFIG_FIXTURE = { MAX_ZOOM: 10, MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855], + USE_BASEMAP_FROM_LAYERS: false, + LAYERS: [ + { + TYPE: 'xyz', + URL: 'https://some-basemap-server', + }, + { + TYPE: 'wms', + URL: 'https://some-wms-server', + NAME: 'some_layername', + }, + { + TYPE: 'wfs', + URL: 'https://some-wfs-server', + NAME: 'some_layername', + }, + ], } From e7e02547609274e9b41153240ae0590fe85dc60d Mon Sep 17 00:00:00 2001 From: Tobias Kohr Date: Thu, 17 Feb 2022 16:05:48 +0100 Subject: [PATCH 2/4] feat(data-view-map): do not add baselayer --- .../data-view-map.component.spec.ts | 38 +++++++++++-------- .../data-view-map/data-view-map.component.ts | 19 ++-------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/libs/feature/record/src/lib/data-view-map/data-view-map.component.spec.ts b/libs/feature/record/src/lib/data-view-map/data-view-map.component.spec.ts index 6ee01b87d6..fb136e3aab 100644 --- a/libs/feature/record/src/lib/data-view-map/data-view-map.component.spec.ts +++ b/libs/feature/record/src/lib/data-view-map/data-view-map.component.spec.ts @@ -38,7 +38,23 @@ import { delay } from 'rxjs/operators' import { FEATURE_COLLECTION_POINT_FIXTURE_4326 } from '@geonetwork-ui/util/shared' import { MapConfig } from '@geonetwork-ui/util/app-config' -const mapConfigMock = { MAX_ZOOM: 10 } +const mapConfigMock = { + MAX_ZOOM: 10, + MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855], + USE_BASEMAP_FROM_LAYERS: false, + CUSTOMLAYERS: [ + { + TYPE: 'wms', + URL: 'https://some-wms-server', + NAME: 'some_layername', + }, + { + TYPE: 'wfs', + URL: 'https://some-wfs-server', + NAME: 'some_layername', + }, + ], +} jest.mock('@geonetwork-ui/util/app-config', () => ({ getMapConfig: () => mapConfigMock, isConfigLoaded: jest.fn(() => true), @@ -208,9 +224,9 @@ describe('DataViewMapComponent', () => { mdViewFacade.geoDataLinks$.next([]) fixture.detectChanges() }) - it('emits a map context with only the base layer', () => { + it('emits a map context with no layer', () => { expect(mapComponent.context).toEqual({ - layers: [component.getBackgroundLayer()], + layers: [], view: expect.any(Object), }) }) @@ -244,10 +260,9 @@ describe('DataViewMapComponent', () => { mdViewFacade.geoDataLinks$.next([]) fixture.detectChanges() }) - it('emits a map context with the base layer and the first compatible link', () => { + it('emits a map context with the first compatible link', () => { expect(mapComponent.context).toEqual({ layers: [ - component.getBackgroundLayer(), { url: 'http://abcd.com/', name: 'layer1', @@ -326,10 +341,9 @@ describe('DataViewMapComponent', () => { tick(200) fixture.detectChanges() })) - it('emits a map context with the base layer and the downloaded data from WFS', () => { + it('emits a map context with the downloaded data from WFS', () => { expect(mapComponent.context).toEqual({ layers: [ - component.getBackgroundLayer(), { type: 'geojson', data: SAMPLE_GEOJSON, @@ -353,10 +367,9 @@ describe('DataViewMapComponent', () => { tick(200) fixture.detectChanges() })) - it('emits a map context with the base layer and the downloaded data from WFS', () => { + it('emits a map context with the the downloaded data from WFS', () => { expect(mapComponent.context).toEqual({ layers: [ - component.getBackgroundLayer(), { type: 'geojson', data: SAMPLE_GEOJSON, @@ -422,11 +435,10 @@ describe('DataViewMapComponent', () => { fixture.detectChanges() tick(200) })) - it('emits a map context after loading with the base layer and the downloaded data', () => { + it('emits a map context after loading with the downloaded data', () => { fixture.detectChanges() expect(mapComponent.context).toEqual({ layers: [ - component.getBackgroundLayer(), { type: 'geojson', data: SAMPLE_GEOJSON, @@ -468,7 +480,6 @@ describe('DataViewMapComponent', () => { it('emits a map context with the link from the last record', () => { expect(mapComponent.context).toEqual({ layers: [ - component.getBackgroundLayer(), { url: 'http://abcd.com/', name: 'layer', @@ -520,7 +531,6 @@ describe('DataViewMapComponent', () => { it('emits a new map context with the selected layer and the computed extent', () => { expect(mapComponent.context).toEqual({ layers: [ - component.getBackgroundLayer(), { url: 'http://abcd.com/', name: 'layer2', @@ -541,7 +551,6 @@ describe('DataViewMapComponent', () => { it('emits a new map context with the selected layer and a default view', () => { expect(mapComponent.context).toEqual({ layers: [ - component.getBackgroundLayer(), { url: 'http://abcd.com/', name: 'layer2', @@ -560,7 +569,6 @@ describe('DataViewMapComponent', () => { })) it('does not emit another map context', () => { expect(mapComponent.context.layers).toEqual([ - component.getBackgroundLayer(), { url: 'http://abcd.com/', name: 'layer2', diff --git a/libs/feature/record/src/lib/data-view-map/data-view-map.component.ts b/libs/feature/record/src/lib/data-view-map/data-view-map.component.ts index 259e721789..b5b436e97f 100644 --- a/libs/feature/record/src/lib/data-view-map/data-view-map.component.ts +++ b/libs/feature/record/src/lib/data-view-map/data-view-map.component.ts @@ -80,15 +80,15 @@ export class DataViewMapComponent implements OnInit, OnDestroy { map(([links, index]) => links[index]), switchMap((link) => { if (!link) { - return of([this.getBackgroundLayer()]) + return of([]) } this.loading = true this.error = null return this.getLayerFromLink(link).pipe( - map((layer) => [this.getBackgroundLayer(), layer]), + map((layer) => [layer]), catchError((e) => { this.error = e.message - return of([this.getBackgroundLayer()]) + return of([]) }), finalize(() => (this.loading = false)) ) @@ -97,7 +97,7 @@ export class DataViewMapComponent implements OnInit, OnDestroy { mapContext$ = this.currentLayers$.pipe( switchMap((layers) => - this.mapUtils.getLayerExtent(layers[1]).pipe( + this.mapUtils.getLayerExtent(layers[0]).pipe( catchError((error) => { console.warn(error) // FIXME: report this to the user somehow return of(undefined) @@ -164,17 +164,6 @@ export class DataViewMapComponent implements OnInit, OnDestroy { this.selection = null } - getBackgroundLayer(): MapContextLayerModel { - return { - type: MapContextLayerTypeEnum.XYZ, - urls: [ - `https://a.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png`, - `https://b.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png`, - `https://c.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png`, - ], - } - } - getLayerFromLink(link: MetadataLinkValid): Observable { if (this.linkHelper.isWmsLink(link)) { return of({ From 70dfbddadb76da36487780b940fba9ea6d44ca78 Mon Sep 17 00:00:00 2001 From: Tobias Kohr Date: Thu, 17 Feb 2022 16:07:28 +0100 Subject: [PATCH 3/4] feat(map-context): merge configured layers into mapContext and set default basemap if necessary --- .../map-context/map-context.service.spec.ts | 62 ++++++++++++++++++- .../lib/map-context/map-context.service.ts | 34 +++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) 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 18512bef42..61ed2ef849 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 @@ -22,7 +22,10 @@ import { MAP_CTX_LAYER_XYZ_FIXTURE, } from './map-context.fixtures' -import { MapContextService } from './map-context.service' +import { + DEFAULT_BASELAYER_CONTEXT, + MapContextService, +} from './map-context.service' const mapStyleServiceMock = { createDefaultStyle: jest.fn(() => new Style()), @@ -191,12 +194,69 @@ describe('MapContextService', () => { const mapContext = MAP_CTX_FIXTURE const mapConfig = MAP_CONFIG_FIXTURE beforeEach(() => { + mapConfig.USE_BASEMAP_FROM_LAYERS = true service.resetMapFromContext(map, mapContext, mapConfig) }) it('set maxZoom', () => { const maxZoom = map.getView().getMaxZoom() expect(maxZoom).toBe(10) }) + it('set first layer as baselayer', () => { + const baselayerUrls = map.getLayers().item(0).getSource().getUrls() + expect(baselayerUrls).toEqual(['https://some-basemap-server']) + }) + it('add one WMS layer from config on top of baselayer', () => { + const layerWMSUrl = map.getLayers().item(1).getSource().getUrls()[0] + expect(layerWMSUrl).toEqual('https://some-wms-server') + }) + it('add one WFS layer from config on top of baselayer', () => { + const layerWFSSource = map.getLayers().item(2).getSource() + expect(layerWFSSource).toBeInstanceOf(VectorSource) + }) + }) + describe('with config, but keeping default basemap', () => { + const map = new Map({}) + const mapContext = MAP_CTX_FIXTURE + const mapConfig = MAP_CONFIG_FIXTURE + beforeEach(() => { + mapConfig.USE_BASEMAP_FROM_LAYERS = false + service.resetMapFromContext(map, mapContext, mapConfig) + }) + it('set first layer as baselayer', () => { + const baselayerUrls = map.getLayers().item(0).getSource().getUrls() + expect(baselayerUrls).toEqual(DEFAULT_BASELAYER_CONTEXT.urls) + }) + }) + }) + describe('#mergeMapConfigWithContext', () => { + const mapContext = MAP_CTX_FIXTURE + const mapConfig = MAP_CONFIG_FIXTURE + beforeEach(() => { + mapConfig.USE_BASEMAP_FROM_LAYERS = true + }) + it('merges mapconfig into existing mapcontext', () => { + const mergedMapContext = service.mergeMapConfigWithContext( + mapContext, + mapConfig + ) + const layersContext = service.getLayersContextFromConfig( + MAP_CONFIG_FIXTURE.LAYERS + ) + + expect(mergedMapContext).toEqual({ + ...MAP_CTX_FIXTURE, + view: { + ...MAP_CTX_FIXTURE.view, + maxZoom: MAP_CONFIG_FIXTURE.MAX_ZOOM, + maxExtent: MAP_CONFIG_FIXTURE.MAX_EXTENT, + }, + layers: [ + layersContext[0], + layersContext[1], + layersContext[2], + ...MAP_CTX_FIXTURE.layers, + ], + }) }) }) }) 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 747332381d..9faa700288 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 @@ -17,7 +17,16 @@ import VectorSource from 'ol/source/Vector' import { MapUtilsService } from '../utils/map-utils.service' import { bbox as bboxStrategy } from 'ol/loadingstrategy' import GeoJSON from 'ol/format/GeoJSON' -import { MapConfig } from '@geonetwork-ui/util/app-config' +import { LayerConfig, MapConfig } from '@geonetwork-ui/util/app-config' + +export const DEFAULT_BASELAYER_CONTEXT: MapContextLayerModel = { + type: MapContextLayerTypeEnum.XYZ, + urls: [ + `https://a.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png`, + `https://b.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png`, + `https://c.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png`, + ], +} @Injectable({ providedIn: 'root', @@ -122,6 +131,29 @@ export class MapContextService { maxExtent: mapConfig.MAX_EXTENT, }), }, + layers: [ + ...(mapConfig.USE_BASEMAP_FROM_LAYERS + ? [] + : [DEFAULT_BASELAYER_CONTEXT]), + ...(mapConfig.LAYERS + ? this.getLayersContextFromConfig(mapConfig.LAYERS) + : []), + ...mapContext.layers, + ], } } + + getLayersContextFromConfig( + layersConfig: LayerConfig[] + ): MapContextLayerModel[] { + const layersModel: MapContextLayerModel[] = [] + layersConfig.forEach((layerConfig) => { + layersModel.push({ + type: MapContextLayerTypeEnum[layerConfig.TYPE.toUpperCase()], + url: layerConfig.URL, + name: layerConfig.NAME, + }) + }) + return layersModel + } } From cc9a43be744a291c6fa089dca29c79fe50b30b36 Mon Sep 17 00:00:00 2001 From: Tobias Kohr Date: Fri, 18 Feb 2022 11:50:30 +0100 Subject: [PATCH 4/4] refactor(map-config): rename params and improve tests Co-authored-by: Olivier Guyot --- conf/default.toml | 28 +++-- .../map-context/map-context.service.spec.ts | 8 +- .../lib/map-context/map-context.service.ts | 6 +- .../data-view-map.component.spec.ts | 4 +- .../app-config/src/lib/app-config.spec.ts | 108 +++++++++++++----- libs/util/app-config/src/lib/app-config.ts | 41 +++---- libs/util/app-config/src/lib/fixtures.ts | 10 +- 7 files changed, 131 insertions(+), 74 deletions(-) diff --git a/conf/default.toml b/conf/default.toml index c65612c55d..c71521cea9 100644 --- a/conf/default.toml +++ b/conf/default.toml @@ -12,30 +12,34 @@ geonetwork4_api_url = "/geonetwork/srv/api" proxy_path = "" ### MAP SETTINGS -# The map section allows to override default settings of the map view. -# Example: -# + +# The map section allows to customize how maps are configured. [map] +# Optional; Will limit the possibility to zoom in past a certain zoom level # max_zoom = 10 + +# 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] -# use_basemap_from_layers = false -# -# Layers (basemaps, overlays, masks) can be added to the map view with three properties: + +# Optional; if true, the default basemap will not be added to the map. +# Use [[map_layer]] sections to define your own custom layers (see below) +# do_not_use_default_basemap = false + +# One or several layers (as background or overlay) can be added to the map with the following properties: # - type (mandatory): Indicates the layer type. Possible values are 'xyz', 'wms', 'wfs'. # - url (mandatory): Layer endpoint URL. -# - name: Mandatory for 'wms', 'wfs' types where it indicates the layer name. +# - name: Mandatory for 'wms', 'wfs' types where it indicates the layer name or feature type. # Layer order in the config is the same as in the map, the first one being the bottom one. -# If you want to override the default basemap with the first layer of your config set use_basemap_from_layers = true (default: false) -# Each layer is defined in its own [[layers]] section. +# Each layer is defined in its own [[map_layer]] section. # Example: -# [[layers]] +# [[map_layer]] # type = "xyz" # url = "https://{a-c}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png" -# [[layers]] +# [[map_layer]] # type = "wfs" # url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" # name = "masque_hdf_ign_carto_latin1" -# ### VISUAL THEME 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 61ed2ef849..c2322f05e8 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 @@ -194,7 +194,7 @@ describe('MapContextService', () => { const mapContext = MAP_CTX_FIXTURE const mapConfig = MAP_CONFIG_FIXTURE beforeEach(() => { - mapConfig.USE_BASEMAP_FROM_LAYERS = true + mapConfig.DO_NOT_USE_DEFAULT_BASEMAP = true service.resetMapFromContext(map, mapContext, mapConfig) }) it('set maxZoom', () => { @@ -219,7 +219,7 @@ describe('MapContextService', () => { const mapContext = MAP_CTX_FIXTURE const mapConfig = MAP_CONFIG_FIXTURE beforeEach(() => { - mapConfig.USE_BASEMAP_FROM_LAYERS = false + mapConfig.DO_NOT_USE_DEFAULT_BASEMAP = false service.resetMapFromContext(map, mapContext, mapConfig) }) it('set first layer as baselayer', () => { @@ -232,7 +232,7 @@ describe('MapContextService', () => { const mapContext = MAP_CTX_FIXTURE const mapConfig = MAP_CONFIG_FIXTURE beforeEach(() => { - mapConfig.USE_BASEMAP_FROM_LAYERS = true + mapConfig.DO_NOT_USE_DEFAULT_BASEMAP = true }) it('merges mapconfig into existing mapcontext', () => { const mergedMapContext = service.mergeMapConfigWithContext( @@ -240,7 +240,7 @@ describe('MapContextService', () => { mapConfig ) const layersContext = service.getLayersContextFromConfig( - MAP_CONFIG_FIXTURE.LAYERS + MAP_CONFIG_FIXTURE.MAP_LAYERS ) expect(mergedMapContext).toEqual({ 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 9faa700288..2c5aee6e71 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 @@ -132,11 +132,11 @@ export class MapContextService { }), }, layers: [ - ...(mapConfig.USE_BASEMAP_FROM_LAYERS + ...(mapConfig.DO_NOT_USE_DEFAULT_BASEMAP ? [] : [DEFAULT_BASELAYER_CONTEXT]), - ...(mapConfig.LAYERS - ? this.getLayersContextFromConfig(mapConfig.LAYERS) + ...(mapConfig.MAP_LAYERS + ? this.getLayersContextFromConfig(mapConfig.MAP_LAYERS) : []), ...mapContext.layers, ], diff --git a/libs/feature/record/src/lib/data-view-map/data-view-map.component.spec.ts b/libs/feature/record/src/lib/data-view-map/data-view-map.component.spec.ts index fb136e3aab..d8c92b1a14 100644 --- a/libs/feature/record/src/lib/data-view-map/data-view-map.component.spec.ts +++ b/libs/feature/record/src/lib/data-view-map/data-view-map.component.spec.ts @@ -41,8 +41,8 @@ import { MapConfig } from '@geonetwork-ui/util/app-config' const mapConfigMock = { MAX_ZOOM: 10, MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855], - USE_BASEMAP_FROM_LAYERS: false, - CUSTOMLAYERS: [ + DO_NOT_USE_DEFAULT_BASEMAP: false, + MAP_LAYERS: [ { TYPE: 'wms', URL: 'https://some-wms-server', diff --git a/libs/util/app-config/src/lib/app-config.spec.ts b/libs/util/app-config/src/lib/app-config.spec.ts index 72a0d5de61..879247579e 100644 --- a/libs/util/app-config/src/lib/app-config.spec.ts +++ b/libs/util/app-config/src/lib/app-config.spec.ts @@ -32,13 +32,13 @@ another_path = '/whatever' [map] max_zoom = 10 max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855] -use_basemap_from_layers = false +do_not_use_default_basemap = false another_zoom = 15 -[[layers]] +[[map_layer]] type = "wms" url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" name = "masque_hdf_ign_carto_latin1" -[[layers]] +[[map_layer]] type = "wfs" url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" name = "masque_hdf_ign_carto_latin1" @@ -150,29 +150,6 @@ describe('app config utils', () => { }) }) }) - describe('getMapConfig', () => { - it('returns the map config', () => { - expect(getMapConfig()).toEqual({ - MAX_ZOOM: 10, - MAX_EXTENT: [ - -418263.418776, 5251529.591305, 961272.067714, 6706890.609855, - ], - USE_BASEMAP_FROM_LAYERS: false, - LAYERS: [ - { - TYPE: 'wms', - URL: 'https://www.geo2france.fr/geoserver/cr_hdf/ows', - NAME: 'masque_hdf_ign_carto_latin1', - }, - { - TYPE: 'wfs', - URL: 'https://www.geo2france.fr/geoserver/cr_hdf/ows', - NAME: 'masque_hdf_ign_carto_latin1', - }, - ], - }) - }) - }) describe('getThemeConfig', () => { it('returns the theme config', () => { expect(getThemeConfig()).toEqual({ @@ -206,4 +183,83 @@ describe('app config utils', () => { }) }) }) + + describe('getMapConfig', () => { + const baseConfig = ` + [global] + geonetwork4_api_url = "/geonetwork/srv/api" + [theme] + primary_color = "#093564" + secondary_color = "#c2e9dc" + main_color = "#212029" # All-purpose text color + background_color = "#fdfbff" +` + + describe('when all properties are present', () => { + beforeEach(async () => { + fetchMock.get( + 'end:default.toml', + () => + baseConfig + + ` + [map] + max_zoom = 10 + max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855] + do_not_use_default_basemap = true + [[map_layer]] + type = "wms" + url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" + name = "masque_hdf_ign_carto_latin1" + [[map_layer]] + type = "wfs" + url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" + name = "masque_hdf_ign_carto_latin1"` + ) + await loadAppConfig() + }) + + it('returns the map config', () => { + expect(getMapConfig()).toEqual({ + MAX_ZOOM: 10, + MAX_EXTENT: [ + -418263.418776, 5251529.591305, 961272.067714, 6706890.609855, + ], + DO_NOT_USE_DEFAULT_BASEMAP: true, + MAP_LAYERS: [ + { + TYPE: 'wms', + URL: 'https://www.geo2france.fr/geoserver/cr_hdf/ows', + NAME: 'masque_hdf_ign_carto_latin1', + }, + { + TYPE: 'wfs', + URL: 'https://www.geo2france.fr/geoserver/cr_hdf/ows', + NAME: 'masque_hdf_ign_carto_latin1', + }, + ], + }) + }) + }) + describe('when all properties are missing', () => { + beforeEach(async () => { + fetchMock.get( + 'end:default.toml', + () => + baseConfig + + ` + [map]` + ) + await loadAppConfig() + }) + + it('returns the map config', () => { + expect(getMapConfig()).toEqual({ + MAX_ZOOM: undefined, + MAX_EXTENT: undefined, + DO_NOT_USE_DEFAULT_BASEMAP: false, + MAP_LAYERS: [], + }) + }) + }) + }) }) diff --git a/libs/util/app-config/src/lib/app-config.ts b/libs/util/app-config/src/lib/app-config.ts index 22d793214e..fb26d5447e 100644 --- a/libs/util/app-config/src/lib/app-config.ts +++ b/libs/util/app-config/src/lib/app-config.ts @@ -1,5 +1,4 @@ import * as TOML from '@ltd/j-toml' -import { Extent } from 'ol/extent' const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly. This error might show up in case of an invalid/malformed configuration file. @@ -24,9 +23,9 @@ export interface LayerConfig { } export interface MapConfig { MAX_ZOOM?: number - MAX_EXTENT?: Extent - USE_BASEMAP_FROM_LAYERS?: boolean - LAYERS?: LayerConfig[] + MAX_EXTENT?: [number, number, number, number] // Expressed as [minx, miny, maxx, maxy] + DO_NOT_USE_DEFAULT_BASEMAP: boolean + MAP_LAYERS: LayerConfig[] } let mapConfig: MapConfig = null @@ -98,7 +97,7 @@ export function loadAppConfig() { const { global, map, - layers, + map_layer, theme, translations: translationsNested, } = parsed @@ -122,7 +121,7 @@ export function loadAppConfig() { const mapCheck = checkKeys( map || {}, [], - ['max_zoom', 'max_extent', 'baselayer', 'use_basemap_from_layers'] + ['max_zoom', 'max_extent', 'baselayer', 'do_not_use_default_basemap'] ) if (mapCheck.missing.length) { errors.push(`In the [map] section: ${mapCheck.missing.join(', ')}`) @@ -133,8 +132,8 @@ export function loadAppConfig() { } let layersCheck = { missing: [], unrecognized: [] } - if (layers) { - layers.forEach((layer) => { + if (map_layer) { + map_layer.forEach((layer) => { const { missing, unrecognized } = checkKeys( layer || {}, ['type', 'url'], @@ -153,11 +152,13 @@ export function loadAppConfig() { }) if (layersCheck.missing.length) { errors.push( - `In some [layers] definition: ${layersCheck.missing.join(', ')}` + `In one of the [map_layer] definitions: ${layersCheck.missing.join( + ', ' + )}` ) } else if (layersCheck.unrecognized.length) { warnings.push( - `In some [layers] definition: ${layersCheck.unrecognized.join( + `In one of the [map_layer] definitions: ${layersCheck.unrecognized.join( ', ' )}` ) @@ -205,8 +206,8 @@ ${warnings.join('\n')}`) PROXY_PATH: global.proxy_path, } const layersConfig: LayerConfig[] = [] - if (layers) { - layers.forEach((layerConfig) => { + if (map_layer) { + map_layer.forEach((layerConfig) => { layersConfig.push({ TYPE: layerConfig.type, URL: layerConfig.url, @@ -214,16 +215,12 @@ ${warnings.join('\n')}`) }) }) } - mapConfig = map - ? { - MAX_ZOOM: map.max_zoom, - MAX_EXTENT: map.max_extent, - USE_BASEMAP_FROM_LAYERS: map.use_basemap_from_layers, - ...(layersConfig.length && { - LAYERS: layersConfig, - }), - } - : {} + mapConfig = { + MAX_ZOOM: map.max_zoom, + MAX_EXTENT: map.max_extent, + DO_NOT_USE_DEFAULT_BASEMAP: !!map.do_not_use_default_basemap, + MAP_LAYERS: layersConfig, + } themeConfig = { PRIMARY_COLOR: theme.primary_color, SECONDARY_COLOR: theme.secondary_color, diff --git a/libs/util/app-config/src/lib/fixtures.ts b/libs/util/app-config/src/lib/fixtures.ts index 38aa8a8805..eafaa71df4 100644 --- a/libs/util/app-config/src/lib/fixtures.ts +++ b/libs/util/app-config/src/lib/fixtures.ts @@ -6,12 +6,12 @@ proxy_path = "/proxy/?url=" [map] max_zoom = 10 max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855] -use_basemap_from_layers = false -[[layers]] +do_not_use_default_basemap = false +[[map_layer]] type = "wms" url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" name = "masque_hdf_ign_carto_latin1" -[[layers]] +[[map_layer]] type = "wfs" url = "https://www.geo2france.fr/geoserver/cr_hdf/ows" name = "masque_hdf_ign_carto_latin1" @@ -42,8 +42,8 @@ my.sample.text = "Un bon exemple de texte." export const MAP_CONFIG_FIXTURE = { MAX_ZOOM: 10, MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855], - USE_BASEMAP_FROM_LAYERS: false, - LAYERS: [ + DO_NOT_USE_DEFAULT_BASEMAP: false, + MAP_LAYERS: [ { TYPE: 'xyz', URL: 'https://some-basemap-server',