diff --git a/src/parser/ShogunApplicationUtil.spec.ts b/src/parser/ShogunApplicationUtil.spec.ts index 8d96ad862..e8eba427e 100644 --- a/src/parser/ShogunApplicationUtil.spec.ts +++ b/src/parser/ShogunApplicationUtil.spec.ts @@ -1,23 +1,20 @@ -import OlView, { ViewOptions } from 'ol/View'; -import OlSourceTileWMS from 'ol/source/TileWMS'; -import OlLayerTile from 'ol/layer/Tile'; import OlSourceImageWMS from 'ol/source/ImageWMS'; -import OlSourceWMTS, { optionsFromCapabilities } from 'ol/source/WMTS'; +import OlSourceTileWMS from 'ol/source/TileWMS'; import OlLayerImage from 'ol/layer/Image'; -import OlLayerBase from 'ol/layer/Base'; -import OlLayerGroup from 'ol/layer/Group'; +import OlLayerTile from 'ol/layer/Tile'; +import OlLayerVector from 'ol/layer/Vector'; +import OlSourceVector from 'ol/source/Vector'; +import OlFormatGeoJSON from 'ol/format/GeoJSON'; import OlTileGrid from 'ol/tilegrid/TileGrid'; +import { bbox as olStrategyBbox } from 'ol/loadingstrategy'; +import { getUid } from 'ol/util'; import Application from '../model/Application'; import Layer from '../model/Layer'; -import fetchSpy, { - failureResponse, - successResponse -} from '../spec/fetchSpy'; import ShogunApplicationUtil from './ShogunApplicationUtil'; -describe('AuthService', () => { +describe('ShogunApplicationUtil', () => { let fetchMock: jest.SpyInstance; let util: ShogunApplicationUtil; @@ -103,15 +100,12 @@ describe('AuthService', () => { maxResolution: myLayer.clientConfig?.maxResolution }); - // expected.ol_uid = expect.any(String); - // expected.getSource().ol_uid = expect.any(String); - // @ts-ignore // eslint-disable-next-line camelcase - expected.ol_uid = layer.ol_uid; + expected.ol_uid = getUid(layer); // @ts-ignore // eslint-disable-next-line camelcase - expected.getSource().ol_uid = layer.getSource().ol_uid; + expected.getSource().ol_uid = getUid(layer.getSource()); expected.set('shogunId', myLayer.id); expected.set('name', myLayer.name); @@ -132,15 +126,109 @@ describe('AuthService', () => { name: 'Layer A', type: 'TILEWMS', clientConfig: {}, + sourceConfig: { + url: 'https://ows.terrestris.de/osm/service?', + layerNames: 'OSM-WMS', + tileSize: 512, + tileOrigin: [0, 0], + resolutions: [ + 16, + 8, + 4, + 2, + 1 + ] + } + }; + + const layer = await util.parseLayer(myLayer); + + const expected = new OlLayerTile({ + source: new OlSourceTileWMS({ + url: myLayer.sourceConfig.url, + tileGrid: new OlTileGrid({ + resolutions: myLayer.sourceConfig.resolutions!, + tileSize: [ + myLayer.sourceConfig.tileSize!, + myLayer.sourceConfig.tileSize! + ], + origin: myLayer.sourceConfig.tileOrigin + }), + attributions: myLayer.sourceConfig.attribution, + params: { + 'LAYERS': myLayer.sourceConfig.layerNames, + 'TRANSPARENT': true + }, + crossOrigin: myLayer.clientConfig?.crossOrigin, + projection: 'EPSG:3857' + }), + minResolution: myLayer.clientConfig?.minResolution, + maxResolution: myLayer.clientConfig?.maxResolution + }); + + // @ts-ignore + // eslint-disable-next-line camelcase + expected.ol_uid = getUid(layer); + // @ts-ignore + // eslint-disable-next-line camelcase + expected.getSource().ol_uid = getUid(layer.getSource()); + + expected.set('shogunId', myLayer.id); + expected.set('name', myLayer.name); + expected.set('type', myLayer.type); + expected.set('searchable', myLayer.clientConfig?.searchable); + expected.set('propertyConfig', myLayer.clientConfig?.propertyConfig); + expected.set('legendUrl', myLayer.sourceConfig.legendUrl); + expected.set('hoverable', myLayer.clientConfig?.hoverable); + + expect(JSON.stringify(layer)).toEqual(JSON.stringify(expected)); + }); + + it('is capable to parse a WFS layer (parseWFSLayer)', async () => { + const myLayer: Layer = { + id: 1909, + created: new Date(), + modified: new Date(), + name: 'Layer A', + type: 'WFS', + clientConfig: {}, sourceConfig: { url: 'https://ows.terrestris.de/osm/service?', layerNames: 'OSM-WMS' } }; - const mapView = await util.parseLayer(myLayer); + const layer = await util.parseLayer(myLayer); - expect(mapView).toBeDefined(); + const expected = new OlLayerVector({ + source: new OlSourceVector({ + format: new OlFormatGeoJSON(), + attributions: myLayer.sourceConfig.attribution, + strategy: olStrategyBbox, + url: () => { + return ''; + } + }), + minResolution: myLayer.clientConfig?.minResolution, + maxResolution: myLayer.clientConfig?.maxResolution + }); + + expected.set('shogunId', myLayer.id); + expected.set('name', myLayer.name); + expected.set('type', myLayer.type); + expected.set('searchable', myLayer.clientConfig?.searchable); + expected.set('propertyConfig', myLayer.clientConfig?.propertyConfig); + expected.set('legendUrl', myLayer.sourceConfig.legendUrl); + expected.set('hoverable', myLayer.clientConfig?.hoverable); + + // @ts-ignore + // eslint-disable-next-line camelcase + expected.ol_uid = getUid(layer); + // @ts-ignore + // eslint-disable-next-line camelcase + expected.getSource().ol_uid = getUid(layer.getSource()); + + expect(JSON.stringify(layer)).toEqual(JSON.stringify(expected)); }); }); diff --git a/src/parser/ShogunApplicationUtil.ts b/src/parser/ShogunApplicationUtil.ts index 329e69da3..293cf29f2 100644 --- a/src/parser/ShogunApplicationUtil.ts +++ b/src/parser/ShogunApplicationUtil.ts @@ -7,11 +7,15 @@ import OlImageLayer from 'ol/layer/Image'; import OlLayerBase from 'ol/layer/Base'; import OlLayerGroup from 'ol/layer/Group'; import OlTileGrid from 'ol/tilegrid/TileGrid'; +import OlSourceVector from 'ol/source/Vector'; import OlWMTSCapabilities from 'ol/format/WMTSCapabilities'; +import OlFormatGeoJSON from 'ol/format/GeoJSON'; +import OlLayerVector from 'ol/layer/Vector'; import { fromLonLat, ProjectionLike } from 'ol/proj'; +import { bbox as olStrategyBbox } from 'ol/loadingstrategy'; import { UrlUtil } from '@terrestris/base-util/dist/UrlUtil/UrlUtil'; @@ -107,6 +111,7 @@ class ShogunApplicationUtil { continue; } + // TODO Fetch via graphlQL (multiple at once) const layer = await this.client.layer().findOne(node.layerId); const olLayer = await this.parseLayer(layer, projection); @@ -146,8 +151,12 @@ class ShogunApplicationUtil { return await this.parseWMTSLayer(layer, projection); } - // TODO Support others - throw new Error('Currently only WMTS, WMS and TILEWMS layers are supported.'); + if (layer.type === 'WFS') { + return this.parseWFSLayer(layer, projection); + } + + // TODO Add support for VECTORTILE, XYZ and WMSTime + throw new Error('Currently only WMTS, WMS, TILEWMS and WFS layers are supported.'); } parseImageLayer(layer: S) { @@ -184,7 +193,7 @@ class ShogunApplicationUtil { return imageLayer; } - parseTileLayer(layer: S, projection?: ProjectionLike) { + parseTileLayer(layer: S, projection: ProjectionLike = 'EPSG:3857') { const { attribution, url, @@ -215,7 +224,8 @@ class ShogunApplicationUtil { attributions: attribution, projection, params: { - 'LAYERS': layerNames + 'LAYERS': layerNames, + 'TRANSPARENT': true }, crossOrigin }); @@ -287,6 +297,48 @@ class ShogunApplicationUtil { return wmtsLayer; } + parseWFSLayer(layer: S, projection: ProjectionLike = 'EPSG:3857') { + const { + attribution, + url, + layerNames + } = layer.sourceConfig || {}; + + const { + minResolution, + maxResolution + } = layer.clientConfig || {}; + + const source = new OlSourceVector({ + format: new OlFormatGeoJSON(), + attributions: attribution, + url: extent => { + const params = UrlUtil.objectToRequestString({ + SERVICE: 'WFS', + VERSION: '2.0.0', + REQUEST: 'GetFeature', + TYPENAMES: layerNames, + OUTPUTFORMAT: 'application/json', + SRSNAME: projection, + BBOX: `${extent.join(',')},${projection}` + }); + + return `${url}${url.endsWith('?') ? '' : '?'}${params}`; + }, + strategy: olStrategyBbox + }); + + const vectorLayer = new OlLayerVector({ + source, + minResolution, + maxResolution + }); + + this.setLayerProperties(vectorLayer, layer); + + return vectorLayer; + } + getMapScales(resolutions: number[], projUnit: string = 'm'): number[] { return resolutions .map((res: number) =>