From 8ae5d214b3409ef75f0182f63ef2d80002d61870 Mon Sep 17 00:00:00 2001 From: enrico ferreguti Date: Thu, 21 Mar 2024 09:35:03 +0100 Subject: [PATCH 1/5] generalize wfs endpoint --- src/factory/Layer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/factory/Layer.js b/src/factory/Layer.js index 4afb8808..a7e891f6 100644 --- a/src/factory/Layer.js +++ b/src/factory/Layer.js @@ -199,7 +199,8 @@ export const LayerFactory = { format: new this.formatMapping[lConf.format](lConf.formatConfig), loader: (extent) => { // assemble WFS GetFeature request - let wfsRequest = lConf.url + '?service=WFS&' + + const pre = lConf.url.includes("?") ? "&" : "?" + let wfsRequest = lConf.url + pre + 'service=WFS&' + 'version=' + lConf.version + '&request=GetFeature&' + 'typename=' + lConf.typeName + '&' + 'outputFormat=' + outputFormat + '&srsname=' + lConf.projection; From 857f19e7ef52fa4c48b334c194be5d6c32e2a27c Mon Sep 17 00:00:00 2001 From: enrico ferreguti Date: Wed, 1 May 2024 09:25:44 +0200 Subject: [PATCH 2/5] wmts support with tilegrid --- src/components/ol/Map.vue | 4 +-- src/factory/Layer.js | 66 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/components/ol/Map.vue b/src/components/ol/Map.vue index 86dc6e5e..ec7aff90 100644 --- a/src/components/ol/Map.vue +++ b/src/components/ol/Map.vue @@ -16,6 +16,7 @@ import { import RotateControl from 'ol/control/Rotate'; import Projection from 'ol/proj/Projection'; import TileGrid from 'ol/tilegrid/TileGrid'; +import WMTSTileGrid from 'ol/tilegrid/WMTS.js'; import { register as olproj4 } from 'ol/proj/proj4'; import { get as getProj } from 'ol/proj'; import { GPX, GeoJSON, IGC, KML, TopoJSON } from 'ol/format'; @@ -163,7 +164,7 @@ export default { // Optional TileGrid definitions by name, for ref in Layers Object.keys(this.tileGridDefs).forEach(name => { - this.tileGrids[name] = new TileGrid(this.tileGridDefs[name]); + this.tileGrids[name] = this.tileGridDefs[name].type === 'WMTS' ? new WMTSTileGrid(this.tileGridDefs[name]) : new TileGrid(this.tileGridDefs[name]); }); this.map = new Map({ @@ -207,7 +208,6 @@ export default { // Remarks: Passing null instead of undefined as parameters into the // constructor of OpenLayers sources overwrites OpenLayers defaults. lConf.tileGrid = lConf.tileGridRef ? me.tileGrids[lConf.tileGridRef] : undefined; - // Automatically set the appropriate z-index for the layer type, // if not defined explicitly. lConf.zIndex = lConf.zIndex ?? (lConf.isBaseLayer ? -1 : 0); diff --git a/src/factory/Layer.js b/src/factory/Layer.js index 20fdb5ea..b0e14f0d 100644 --- a/src/factory/Layer.js +++ b/src/factory/Layer.js @@ -1,6 +1,8 @@ import { Image as ImageLayer, Tile as TileLayer } from 'ol/layer'; import ImageWMS from 'ol/source/ImageWMS'; import TileWmsSource from 'ol/source/TileWMS'; +import WMTS, {optionsFromCapabilities} from 'ol/source/WMTS.js'; +import WMTSCapabilities from 'ol/format/WMTSCapabilities.js'; import OsmSource from 'ol/source/OSM'; import VectorTileLayer from 'ol/layer/VectorTile' import VectorTileSource from 'ol/source/VectorTile' @@ -65,6 +67,8 @@ export const LayerFactory = { return this.createTileWmsLayer(lConf); } else if (lConf.type === 'IMAGEWMS') { return this.createImageWmsLayer(lConf); + } else if (lConf.type === 'WMTS') { + return this.createTileWmtsLayer(lConf); } else if (lConf.type === 'WFS') { return this.createWfsLayer(lConf, olMap); } else if (lConf.type === 'XYZ') { @@ -165,6 +169,68 @@ export const LayerFactory = { return layer; }, + /** + * Returns an OpenLayers Tiled WMTS layer instance due to given config. + * + * @param {Object} lConf Layer config object + * @return {ol.layer.Tile} OL Tiled WMTS layer instance + */ + createTileWmtsLayer (lConf) { + // apply additional HTTP params + + const WMTSlayer = new TileLayer({ + ...this.getCommonLayerOptions(lConf), + source: new TileWmsSource({ // fake source, will be replaced + url: "" + }) + }) + + if (lConf.optionsFromCapabilities) { + const parser = new WMTSCapabilities(); + + fetch(lConf.optionsFromCapabilities.url) + .then(function (response) { + return response.text(); + }) + .then(function (text) { + const capabilities = parser.read(text); + const Source = new WMTS(optionsFromCapabilities(capabilities, { + layer: lConf.optionsFromCapabilities.layername, + matrixSet: lConf.optionsFromCapabilities.matrixSet, + })) + + WMTSlayer.setSource(Source) + + }); + + } else { + WMTSlayer.setSource(new WMTS({ + url: lConf.url, + serverType: lConf.serverType, + tileGrid: lConf.tileGrid, + cacheSize: lConf.cacheSize, + interpolate: lConf.interpolate, + reprojectionErrorThreshold: lConf.reprojectionErrorThreshold, + projection: lConf.projection, + crossOrigin: lConf.crossOrigin, + layer: lConf.layer, + style: lConf.style, + tilePixelRatio: lConf.tilePixelRatio, + format: lConf.format, + version: lConf.version, + matrixSet: lConf.matrixSet, + urls: lConf.urls, + wrapX: lConf.wrapX, + transition: lConf.transition, + zDirection: lConf.zDirection + }) + ) + } + + return WMTSlayer; + + }, + /** * Returns an OpenLayers WFS layer instance due to given config. * From a5316ad09874139db175370daa63f254a07bcfcc Mon Sep 17 00:00:00 2001 From: enrico ferreguti Date: Wed, 1 May 2024 22:23:24 +0200 Subject: [PATCH 3/5] attributions visibility issue fix --- src/factory/Layer.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/factory/Layer.js b/src/factory/Layer.js index b0e14f0d..54d6826a 100644 --- a/src/factory/Layer.js +++ b/src/factory/Layer.js @@ -194,10 +194,22 @@ export const LayerFactory = { }) .then(function (text) { const capabilities = parser.read(text); - const Source = new WMTS(optionsFromCapabilities(capabilities, { - layer: lConf.optionsFromCapabilities.layername, + const options = optionsFromCapabilities(capabilities, { + layer: lConf.optionsFromCapabilities.layer, matrixSet: lConf.optionsFromCapabilities.matrixSet, - })) + projection: lConf.optionsFromCapabilities.projection, + requestEncoding: lConf.optionsFromCapabilities.requestEncoding, + style: lConf.optionsFromCapabilities.style, + format: lConf.optionsFromCapabilities.format, + crossOrigin: lConf.optionsFromCapabilities.crossOrigin, + }) + const Source = new WMTS({ + ...options, + attributions: lConf.attributions, + hoverable: lConf.hoverable, + hoverAttribute: lConf.hoverAttribute, + hoverOverlay: lConf.hoverOverlay + }) WMTSlayer.setSource(Source) @@ -206,7 +218,6 @@ export const LayerFactory = { } else { WMTSlayer.setSource(new WMTS({ url: lConf.url, - serverType: lConf.serverType, tileGrid: lConf.tileGrid, cacheSize: lConf.cacheSize, interpolate: lConf.interpolate, @@ -222,7 +233,10 @@ export const LayerFactory = { urls: lConf.urls, wrapX: lConf.wrapX, transition: lConf.transition, - zDirection: lConf.zDirection + zDirection: lConf.zDirection, + hoverable: lConf.hoverable, + hoverAttribute: lConf.hoverAttribute, + hoverOverlay: lConf.hoverOverlay }) ) } From fccd7eefd657ef408b74d13144fea2c38d7c5918 Mon Sep 17 00:00:00 2001 From: enrico ferreguti Date: Wed, 1 May 2024 22:25:36 +0200 Subject: [PATCH 4/5] docs and sample implementation --- app-starter/static/app-conf.json | 58 ++++++++++++++++++++++++++++++++ docs/map-layer-configuration.md | 18 ++++++++++ 2 files changed, 76 insertions(+) diff --git a/app-starter/static/app-conf.json b/app-starter/static/app-conf.json index 1b7bca7b..90bdf8a3 100644 --- a/app-starter/static/app-conf.json +++ b/app-starter/static/app-conf.json @@ -50,6 +50,21 @@ "history": true }, + "tileGridDefs": { + "usgs": { + "type": "WMTS", + "origin": [ -20037508.342789244, 20037508.342789244 ], + "resolutions": [156543.03,78271.51,39135.75,19567.87,9783.93,4891.96,2445.984,1222.99,611.49,305.748,152.87,76.437,38.218,19.109,9.55,4.77,2.388,1.194,0.595], + "matrixIds": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18] + }, + "ignfr": { + "type": "WMTS", + "origin": [ -20037508.342789244, 20037508.342789244 ], + "resolutions": [156543.03392804097,78271.51696402048,39135.75848201024,19567.87924100512,9783.93962050256,4891.96981025128,2445.98490512564,1222.99245256282,611.49622628141,305.748113140705,152.8740565703525,76.43702828517625,38.21851414258813,19.109257071294063,9.554628535647032,4.777314267823516,2.388657133911758,1.194328566955879,0.5971642834779395,0.29858214173896974], + "matrixIds": ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19"] + } + }, + "mapLayers": [ { @@ -145,6 +160,49 @@ "fillColor": "rgb(255, 255, 0, 0.2)" } }, + { + "type": "WMTS", + "name": "USGS demo", + "format": "image/png", + "url": "https://mrdata.usgs.gov/mapcache/wmts", + "lid": "usgs_demo", + "isBaseLayer": false, + "layers": "", + "projection": "EPSG:3857", + "layer": "sgmc2", + "matrixSet": "GoogleMapsCompatible", + "attributions": "Tiles © USGS", + "tileGridRef": "usgs", + "style": "default", + "wrapX": true + }, + { + "type": "WMTS", + "name": "IGN Plan", + "format": "image/png", + "url": "https://wxs.ign.fr/choisirgeoportail/geoportail/wmts", + "lid": "ign_demo", + "isBaseLayer": true, + "projection": "EPSG:3857", + "layer": "GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2", + "matrixSet": "PM", + "attributions": "Basemap © '\"IGN\"", + "tileGridRef": "ignfr", + "style": "normal" + }, + { + "type": "WMTS", + "name": "RV_DTM", + "lid": "rv-dtm", + "optionsFromCapabilities": { + "url": "https://idt2.regione.veneto.it/gwc/service/wmts?request=GetCapabilities", + "layer": "rv:DTM_RV_5m_3003", + "format": "image/png", + "matrixSet": "EPSG:4326", + "crossOrigin": "anonymous" + }, + "attributions": "DTM © 2024 Regione del Veneto" + }, { "type": "TILEWMS", "lid": "ahocevar-wms", diff --git a/docs/map-layer-configuration.md b/docs/map-layer-configuration.md index 3b460b4a..83c45f12 100644 --- a/docs/map-layer-configuration.md +++ b/docs/map-layer-configuration.md @@ -112,6 +112,24 @@ Similar properties as Tiled WMS, with these exceptions: | interpolate | By default, linear interpolation is used when resampling. Set to false to use the nearest neighbor instead. | `"interpolate": false` | | tileGridRef | Parameter is not used for `IMAGEWMS` | | +## WMTS (tiled) + +| Property | Meaning (from Openlayers [API docs](https://openlayers.org/en/latest/apidoc/module-ol_source_WMTS.html)) | Example | +|--------------------|:----------|---------| +| **type** | Indicator that the layer is a WMTS, use `WMTS` | `"type": "WMTS"` | +| **layer** | Layer name as advertised in the WMTS capabilities. | `"layer": "sgmc2"` | +| **url** | A URL for the service. For the RESTful request encoding, this is a URL template. For KVP encoding, it is normal URL. A `{?-?}` template pattern, for example `subdomain{a-f}.domain.com`, may be used instead of defining each one separately in the `urls` option. | `"url": "https://mrdata.usgs.gov/mapcache/wmts"` | +| projection | The projection of the layer. Has to be defined in `projectionDefs` if not `EPSG:4326` or `EPSG:3857`. if not set the projection of the map is used | `"projection": "EPSG:3857"` | +| format | Image format. Only used when requestEncoding is 'KVP' | `"format": "image/png"` | +| transparent | Boolean value, whether the WMS layer should be queried with a transparent background | `"transparent": true` | +| tileGridRef | Identifier of the tile grid to use for this layer (has to be defined in `tileGridDefs`) The grid has to be correctly identified with `"type": "WMTS"` | `"tileGridRef": "usgs"` | +| crossOrigin | Provides support for CORS, defining how the layers source handles crossorigin requests. For more information and the supported values see [HTML attribute: crossorigin](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) | `"crossOrigin": "anonymous"` | +| cacheSize, interpolate, reprojectionErrorThreshold, tilePixelRatio, version, matrixSet, urls, wrapX, transition, zDirection | The module wraps ol/source/WMTS layer options: https://openlayers.org/en/latest/apidoc/module-ol_source_WMTS.html | `"cacheSize": 16` | +| optionsFromCapabilities | In WMTS layers options can be retrieved parsing the service capabilities document, the option is an object containing at least the following keys: `url`, `layer`, `matrixSet` or `projection` | `"optionsFromCapabilities": {"url": "https://idt2.regione.veneto.it/gwc/service/wmts?request=GetCapabilities", "layer": "rv:DTM_RV_5m_3003", "matrixSet": "EPSG:4326"}` | +| hoverAttribute | Attribute to be shown if a feature of the layer is hovered. Only has an effect if `hoverable` is set to `true`. | `"hoverAttribute": "name"` | +| hoverOverlay | ID of a custom map overlay to display when a feature of the layer is hovered. Only has an effect if `hoverable` is set to `true`. For more information on how to implement a map overlay see the [reusable components](reusable-components?id=map-overlay) section. | `"hoverOverlay": "my-custom-overlay"` | +| params | This allows to inject custom HTTP parameters to the GetMap request of the layer. | `"params": {"FEATUREID": 1}"` | + ## XYZ From 99c208cf3095efe86027d74da92a4c932b51b5f6 Mon Sep 17 00:00:00 2001 From: enrico ferreguti Date: Wed, 1 May 2024 22:43:00 +0200 Subject: [PATCH 5/5] linting fix --- src/factory/Layer.js | 60 +++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/src/factory/Layer.js b/src/factory/Layer.js index 54d6826a..0aef4b7a 100644 --- a/src/factory/Layer.js +++ b/src/factory/Layer.js @@ -1,7 +1,7 @@ import { Image as ImageLayer, Tile as TileLayer } from 'ol/layer'; import ImageWMS from 'ol/source/ImageWMS'; import TileWmsSource from 'ol/source/TileWMS'; -import WMTS, {optionsFromCapabilities} from 'ol/source/WMTS.js'; +import WMTS, { optionsFromCapabilities } from 'ol/source/WMTS.js'; import WMTSCapabilities from 'ol/format/WMTSCapabilities.js'; import OsmSource from 'ol/source/OSM'; import VectorTileLayer from 'ol/layer/VectorTile' @@ -181,18 +181,16 @@ export const LayerFactory = { const WMTSlayer = new TileLayer({ ...this.getCommonLayerOptions(lConf), source: new TileWmsSource({ // fake source, will be replaced - url: "" + url: '' }) }) if (lConf.optionsFromCapabilities) { const parser = new WMTSCapabilities(); - fetch(lConf.optionsFromCapabilities.url) - .then(function (response) { + fetch(lConf.optionsFromCapabilities.url).then(function (response) { return response.text(); - }) - .then(function (text) { + }).then(function (text) { const capabilities = parser.read(text); const options = optionsFromCapabilities(capabilities, { layer: lConf.optionsFromCapabilities.layer, @@ -201,7 +199,7 @@ export const LayerFactory = { requestEncoding: lConf.optionsFromCapabilities.requestEncoding, style: lConf.optionsFromCapabilities.style, format: lConf.optionsFromCapabilities.format, - crossOrigin: lConf.optionsFromCapabilities.crossOrigin, + crossOrigin: lConf.optionsFromCapabilities.crossOrigin }) const Source = new WMTS({ ...options, @@ -210,39 +208,33 @@ export const LayerFactory = { hoverAttribute: lConf.hoverAttribute, hoverOverlay: lConf.hoverOverlay }) - WMTSlayer.setSource(Source) - }); - } else { WMTSlayer.setSource(new WMTS({ - url: lConf.url, - tileGrid: lConf.tileGrid, - cacheSize: lConf.cacheSize, - interpolate: lConf.interpolate, - reprojectionErrorThreshold: lConf.reprojectionErrorThreshold, - projection: lConf.projection, - crossOrigin: lConf.crossOrigin, - layer: lConf.layer, - style: lConf.style, - tilePixelRatio: lConf.tilePixelRatio, - format: lConf.format, - version: lConf.version, - matrixSet: lConf.matrixSet, - urls: lConf.urls, - wrapX: lConf.wrapX, - transition: lConf.transition, - zDirection: lConf.zDirection, - hoverable: lConf.hoverable, - hoverAttribute: lConf.hoverAttribute, - hoverOverlay: lConf.hoverOverlay - }) - ) + url: lConf.url, + tileGrid: lConf.tileGrid, + cacheSize: lConf.cacheSize, + interpolate: lConf.interpolate, + reprojectionErrorThreshold: lConf.reprojectionErrorThreshold, + projection: lConf.projection, + crossOrigin: lConf.crossOrigin, + layer: lConf.layer, + style: lConf.style, + tilePixelRatio: lConf.tilePixelRatio, + format: lConf.format, + version: lConf.version, + matrixSet: lConf.matrixSet, + urls: lConf.urls, + wrapX: lConf.wrapX, + transition: lConf.transition, + zDirection: lConf.zDirection, + hoverable: lConf.hoverable, + hoverAttribute: lConf.hoverAttribute, + hoverOverlay: lConf.hoverOverlay + })) } - return WMTSlayer; - }, /**