From 5e3f780f926c4ff5ba1438e4d3dcfe7866a24a72 Mon Sep 17 00:00:00 2001 From: Olivia Date: Fri, 5 Jan 2024 15:51:51 +0100 Subject: [PATCH 1/7] Update CI to run tests and other checks --- .github/workflows/gh-pages.yml | 24 +++++++----------------- .github/workflows/qa.yml | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/qa.yml diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 254d5fc..84d912f 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -1,32 +1,22 @@ -name: Build +name: Deploy to GitHub Pages on: push: branches: - main jobs: - lint-test-build: + deploy: + name: Build app & deploy runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install Node & NPM - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: - node-version: '18' - - - name: Cache node modules - uses: actions/cache@v1 - env: - cache-name: cache-node-modules - with: - path: ~/.npm - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- + node-version: '20' + cache: 'npm' - name: Install lib dependencies run: npm ci diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml new file mode 100644 index 0000000..9e26a02 --- /dev/null +++ b/.github/workflows/qa.yml @@ -0,0 +1,33 @@ +name: Quality Assurance +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, ready_for_review] + +jobs: + format-typecheck-test: + name: Formatting, types and tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Node & NPM + uses: actions/setup-node@v3 + with: + node-version: '20' + cache: 'npm' + + - name: Install lib dependencies + run: npm ci + + - name: Formatting + run: npm run format:check + + - name: Type check + run: npm run typecheck + + - name: Run tests + run: npm test From 3949d16c9955cb0d2edd7c639395764393d09921 Mon Sep 17 00:00:00 2001 From: Olivia Date: Thu, 1 Feb 2024 23:02:27 +0100 Subject: [PATCH 2/7] Add WMTS endpoint implementation --- __mocks__/ol/proj.js | 5 + __mocks__/ol/proj/proj4.js | 1 + __mocks__/ol/tilegrid/WMTS.js | 1 + fixtures/wmts/arcgis.xml | 413 +++++ ...capabilities_epsg4326_with_boundingbox.xml | 1476 +++++++++++++++++ fixtures/wmts/capabilities_wgs84.xml | 172 ++ .../capabilities_wgs84_with_boundingbox.xml | 705 ++++++++ .../capabilities_with_tilematrixsetlink.xml | 287 ++++ fixtures/wmts/capabilities_wrapx.xml | 1109 +++++++++++++ fixtures/wmts/ign.xml | 501 ++++++ fixtures/wmts/ogcsample.xml | 414 +++++ src/wmts/capabilities.spec.ts | 740 +++++++++ src/wmts/capabilities.ts | 235 +++ src/wmts/endpoint.spec.ts | 442 +++++ src/wmts/endpoint.ts | 188 +++ src/wmts/model.ts | 103 ++ src/wmts/ol-tilegrid.spec.ts | 205 +++ src/wmts/ol-tilegrid.ts | 41 + src/wmts/url.spec.ts | 40 + src/wmts/url.ts | 36 + src/worker/index.ts | 15 + src/worker/worker.ts | 12 + 22 files changed, 7141 insertions(+) create mode 100644 __mocks__/ol/proj.js create mode 100644 __mocks__/ol/proj/proj4.js create mode 100644 __mocks__/ol/tilegrid/WMTS.js create mode 100644 fixtures/wmts/arcgis.xml create mode 100644 fixtures/wmts/capabilities_epsg4326_with_boundingbox.xml create mode 100644 fixtures/wmts/capabilities_wgs84.xml create mode 100644 fixtures/wmts/capabilities_wgs84_with_boundingbox.xml create mode 100644 fixtures/wmts/capabilities_with_tilematrixsetlink.xml create mode 100644 fixtures/wmts/capabilities_wrapx.xml create mode 100644 fixtures/wmts/ign.xml create mode 100644 fixtures/wmts/ogcsample.xml create mode 100644 src/wmts/capabilities.spec.ts create mode 100644 src/wmts/capabilities.ts create mode 100644 src/wmts/endpoint.spec.ts create mode 100644 src/wmts/endpoint.ts create mode 100644 src/wmts/model.ts create mode 100644 src/wmts/ol-tilegrid.spec.ts create mode 100644 src/wmts/ol-tilegrid.ts create mode 100644 src/wmts/url.spec.ts create mode 100644 src/wmts/url.ts diff --git a/__mocks__/ol/proj.js b/__mocks__/ol/proj.js new file mode 100644 index 0000000..e1dab48 --- /dev/null +++ b/__mocks__/ol/proj.js @@ -0,0 +1,5 @@ +module.exports = { + get: jest.fn(() => ({ + code: '3857', + })), +}; diff --git a/__mocks__/ol/proj/proj4.js b/__mocks__/ol/proj/proj4.js new file mode 100644 index 0000000..7337781 --- /dev/null +++ b/__mocks__/ol/proj/proj4.js @@ -0,0 +1 @@ +module.exports = { fromEPSGCode: jest.fn(), register: jest.fn() }; diff --git a/__mocks__/ol/tilegrid/WMTS.js b/__mocks__/ol/tilegrid/WMTS.js new file mode 100644 index 0000000..1d70a63 --- /dev/null +++ b/__mocks__/ol/tilegrid/WMTS.js @@ -0,0 +1 @@ +module.exports = { createFromCapabilitiesMatrixSet: jest.fn(() => {}) }; diff --git a/fixtures/wmts/arcgis.xml b/fixtures/wmts/arcgis.xml new file mode 100644 index 0000000..7b2dbe5 --- /dev/null +++ b/fixtures/wmts/arcgis.xml @@ -0,0 +1,413 @@ + + + + + Demographics_USA_Population_Density + OGC WMTS + 1.0.0 + + + + + + + + + + RESTful + + + + + + + + KVP + + + + + + + + + + + + + RESTful + + + + + + + KVP + + + + + + + + + + + Demographics_USA_Population_Density + Demographics_USA_Population_Density + + -1.98402303899E7 2144435.3407000005 + -7452840.4651999995 1.1536810662600003E7 + + + -178.2278219969978 18.910787002877576 + -66.95000499993604 71.38957425051252 + + + image/png + + default028mm + + + + GoogleMapsCompatible + + + + + + + TileMatrix using 0.28mm + The tile matrix set that has scale values calculated based on the dpi + defined by OGC specification (dpi assumes 0.28mm as the physical distance of a + pixel). + + default028mm + urn:ogc:def:crs:EPSG::3857 + + 0 + 5.590822640285016E8 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 1 + 1 + + + 1 + 2.7954113201425034E8 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 1 + 1 + + + 2 + 1.3977056600712562E8 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 2 + 2 + + + 3 + 6.988528300356235E7 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 3 + 4 + + + 4 + 3.494264150178117E7 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 6 + 8 + + + 5 + 1.7471320750890587E7 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 11 + 15 + + + 6 + 8735660.375445293 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 21 + 29 + + + 7 + 4367830.187722647 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 41 + 58 + + + 8 + 2183915.0938617955 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 81 + 115 + + + 9 + 1091957.5469304253 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 161 + 229 + + + 10 + 545978.7734656851 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 322 + 458 + + + 11 + 272989.38673237007 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 644 + 915 + + + 12 + 136494.69336618503 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 1287 + 1829 + + + 13 + 68247.34668309252 + -2.0037508342787E7 2.0037508342787E7 + 256 + 256 + 2573 + 3658 + + + + GoogleMapsCompatible + the wellknown 'GoogleMapsCompatible' tile matrix set defined by OGC WMTS + specification + + GoogleMapsCompatible + urn:ogc:def:crs:EPSG:6.18.3:3857 + urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible + + 0 + 559082264.0287178 + -20037508.34278925 20037508.34278925 + 256 + 256 + 1 + 1 + + + 1 + 279541132.0143589 + -20037508.34278925 20037508.34278925 + 256 + 256 + 2 + 2 + + + 2 + 139770566.0071794 + -20037508.34278925 20037508.34278925 + 256 + 256 + 4 + 4 + + + 3 + 69885283.00358972 + -20037508.34278925 20037508.34278925 + 256 + 256 + 8 + 8 + + + 4 + 34942641.50179486 + -20037508.34278925 20037508.34278925 + 256 + 256 + 16 + 16 + + + 5 + 17471320.75089743 + -20037508.34278925 20037508.34278925 + 256 + 256 + 32 + 32 + + + 6 + 8735660.375448715 + -20037508.34278925 20037508.34278925 + 256 + 256 + 64 + 64 + + + 7 + 4367830.187724357 + -20037508.34278925 20037508.34278925 + 256 + 256 + 128 + 128 + + + 8 + 2183915.093862179 + -20037508.34278925 20037508.34278925 + 256 + 256 + 256 + 256 + + + 9 + 1091957.546931089 + -20037508.34278925 20037508.34278925 + 256 + 256 + 512 + 512 + + + 10 + 545978.7734655447 + -20037508.34278925 20037508.34278925 + 256 + 256 + 1024 + 1024 + + + 11 + 272989.3867327723 + -20037508.34278925 20037508.34278925 + 256 + 256 + 2048 + 2048 + + + 12 + 136494.6933663862 + -20037508.34278925 20037508.34278925 + 256 + 256 + 4096 + 4096 + + + 13 + 68247.34668319309 + -20037508.34278925 20037508.34278925 + 256 + 256 + 8192 + 8192 + + + 14 + 34123.67334159654 + -20037508.34278925 20037508.34278925 + 256 + 256 + 16384 + 16384 + + + 15 + 17061.83667079827 + -20037508.34278925 20037508.34278925 + 256 + 256 + 32768 + 32768 + + + 16 + 8530.918335399136 + -20037508.34278925 20037508.34278925 + 256 + 256 + 65536 + 65536 + + + 17 + 4265.459167699568 + -20037508.34278925 20037508.34278925 + 256 + 256 + 131072 + 131072 + + + 18 + 2132.729583849784 + -20037508.34278925 20037508.34278925 + 256 + 256 + 262144 + 262144 + + + + + diff --git a/fixtures/wmts/capabilities_epsg4326_with_boundingbox.xml b/fixtures/wmts/capabilities_epsg4326_with_boundingbox.xml new file mode 100644 index 0000000..d266173 --- /dev/null +++ b/fixtures/wmts/capabilities_epsg4326_with_boundingbox.xml @@ -0,0 +1,1476 @@ + + + + EOX::Maps + EOX::Maps are background and overlay maps made of Open Data and designed and provided by EOX. + + + maps + + OGC WMTS + 1.0.0 + Proper attribution is required for any usage. The attribution shall follow the example of + the demo map at https://maps.eox.at in the lower right corner including the respective links e.g. "Terrain { + Data © OpenStreetMap contributers and others, Rendering © EOX }" with links to + http://www.openstreetmap.org/copyright, https://maps.eox.at/#data, and https://eox.at. Additional + restrictions may apply for individual layers as indicated in the respective abstract. + + + + EOX + + + Stephan Meissl + + + +43 664 9688701 + + + EOX IT Services GmbH + Vienna + 1090 + Austria + stephan.meissl@eox.at + + + + + + + + + + + + KVP + + + + + + + + + + + + + KVP + + + + + + + + + + + + + KVP + + + + + + + + + TBD + application/vnd.iso.19139+xml + + + + eng + + + + eng + + + + + + Blue marble background layer by EOX - 4326 + <a href="https://maps.eox.at">Blue Marble</a> { &copy; <a + href="http://nasa.gov">NASA</a> } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + bluemarble + + image/jpeg + + WGS84 + + + + + Sentinel-2 cloudless layer for 2021 by EOX - 3857 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2021) released under <a rel="license" + href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons + Attribution-NonCommercial-ShareAlike 4.0 International License</a>. For commercial usage please + see <a href="https://cloudless.eox.at">https://cloudless.eox.at</a> + + s2cloudless-2021_3857 + + image/jpeg + + g + + + GoogleMapsCompatible + + + + + Black coastline overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Black coastline overlay</a> { Rendering &copy; + <a href="https://eox.at">EOX</a> } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + coastline_black + + image/png + + WGS84 + + + + + Sentinel-2 cloudless layer for 2017 by EOX - 3857 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2017) released under <a rel="license" + href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International + License</a>. + + s2cloudless-2017_3857 + + image/jpeg + + g + + + GoogleMapsCompatible + + + + + Streets overlay layer by EOX - 3857 + <a href="https://maps.eox.at">Streets overlay</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + streets_3857 + + image/png + + g + + + GoogleMapsCompatible + + + + + Bright overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Overlay bright</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + overlay_bright + + image/png + + WGS84 + + + + + Sentinel-2 cloudless layer for 2019 by EOX - 3857 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2019) released under <a rel="license" + href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons + Attribution-NonCommercial-ShareAlike 4.0 International License</a>. For commercial usage please + see <a href="https://cloudless.eox.at">https://cloudless.eox.at</a> + + s2cloudless-2019_3857 + + image/jpeg + + g + + + GoogleMapsCompatible + + + + + Overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Overlay</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + overlay + + image/png + + WGS84 + + + + + Black marble background layer by EOX - 3857 + <a href="https://maps.eox.at">Black Marble</a> { &copy; <a + href="http://nasa.gov">NASA</a> } + + blackmarble_3857 + + image/jpeg + + g + + + + + Hydrography overlay layer by EOX - 3857 + <a href="https://maps.eox.at">Hydrography overlay</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + hydrography_3857 + + image/png + + g + + + GoogleMapsCompatible + + + + + Sentinel-2 cloudless layer for 2017 by EOX - 4326 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2017) released under <a rel="license" + href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International + License</a>. + + + -180.000000 -90.000000 + 180.000000 90.000000 + + s2cloudless-2017 + + image/jpeg + + WGS84 + + + + + Streets overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Streets overlay</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + streets + + image/png + + WGS84 + + + + + Sentinel-2 cloudless layer for 2018 by EOX - 4326 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2017 &amp; 2018) released under <a rel="license" + href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons + Attribution-NonCommercial-ShareAlike 4.0 International License</a>. For commercial usage please + see <a href="https://cloudless.eox.at">https://cloudless.eox.at</a> + + + -180.000000 -90.000000 + 180.000000 90.000000 + + s2cloudless-2018 + + image/jpeg + + WGS84 + + + + + Sentinel-2 cloudless layer for 2019 by EOX - 4326 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2019) released under <a rel="license" + href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons + Attribution-NonCommercial-ShareAlike 4.0 International License</a>. For commercial usage please + see <a href="https://cloudless.eox.at">https://cloudless.eox.at</a> + + + -180.000000 -90.000000 + 180.000000 90.000000 + + s2cloudless-2019 + + image/jpeg + + WGS84 + + + + + OpenStreetMap background layer by EOX - 4326 + <a href="https://maps.eox.at">OpenStreetMap</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + osm + + image/jpeg + + WGS84 + + + + + Black marble background layer by EOX - 4326 + <a href="https://maps.eox.at">Black Marble</a> { &copy; <a + href="http://nasa.gov">NASA</a> } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + blackmarble + + image/jpeg + + WGS84 + + + + + Terrain Light background layer by EOX - 3857 + <a href="https://maps.eox.at">Terrain Light</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors and <a + href="https://maps.eox.at/#data">others</a>, Rendering &copy; <a href="https://eox.at">EOX</a> + } + + terrain-light_3857 + + image/jpeg + + g + + + GoogleMapsCompatible + + + + + Blue marble background layer by EOX - 3857 + <a href="https://maps.eox.at">Blue Marble</a> { &copy; <a + href="http://nasa.gov">NASA</a> } + + bluemarble_3857 + + image/jpeg + + g + + + + + Overlay layer by EOX - 3857 + <a href="https://maps.eox.at">Overlay</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + overlay_base_3857 + + image/png + + g + + + GoogleMapsCompatible + + + + + Sentinel-2 cloudless layer for 2016 by EOX - 4326 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2016 &amp; 2017) released under <a rel="license" + href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International + License</a>. + + + -180.000000 -90.000000 + 180.000000 90.000000 + + s2cloudless + + image/jpeg + + WGS84 + + + + + Terrain background layer by EOX - 4326 + <a href="https://maps.eox.at">Terrain</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors and <a + href="https://maps.eox.at/#data">others</a>, Rendering &copy; <a href="https://eox.at">EOX</a> + } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + terrain + + image/jpeg + + WGS84 + + + + + Bright overlay layer by EOX - 3857 + <a href="https://maps.eox.at">Overlay bright</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + overlay_bright_3857 + + image/png + + g + + + GoogleMapsCompatible + + + + + Coastline overlay layer by EOX - 3857 + <a href="https://maps.eox.at">Coastline overlay</a> { Rendering &copy; <a + href="https://eox.at">EOX</a> } + + coastline_3857 + + image/png + + g + + + + + Sentinel-2 cloudless layer for 2020 by EOX - 3857 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2020) released under <a rel="license" + href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons + Attribution-NonCommercial-ShareAlike 4.0 International License</a>. For commercial usage please + see <a href="https://cloudless.eox.at">https://cloudless.eox.at</a> + + s2cloudless-2020_3857 + + image/jpeg + + g + + + GoogleMapsCompatible + + + + + Magnetic Graticules overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Magnetic Graticules</a> { Rendering &copy; <a + href="https://eox.at">EOX</a> } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + magnetic_graticules + + image/png + + WGS84 + + + + + Terrain Light background layer by EOX - 4326 + <a href="https://maps.eox.at">Terrain Light</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors and <a + href="https://maps.eox.at/#data">others</a>, Rendering &copy; <a href="https://eox.at">EOX</a> + } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + terrain-light + + image/jpeg + + WGS84 + + + + + Bright overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Overlay bright</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + overlay_base_bright + + image/png + + WGS84 + + + + + Overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Overlay</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + overlay_base + + image/png + + WGS84 + + + + + Sentinel-2 cloudless layer for 2018 by EOX - 3857 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2017 &amp; 2018) released under <a rel="license" + href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons + Attribution-NonCommercial-ShareAlike 4.0 International License</a>. For commercial usage please + see <a href="https://cloudless.eox.at">https://cloudless.eox.at</a> + + s2cloudless-2018_3857 + + image/jpeg + + g + + + GoogleMapsCompatible + + + + + Coastline overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Coastline overlay</a> { Rendering &copy; <a + href="https://eox.at">EOX</a> } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + coastline + + image/png + + WGS84 + + + + + Sentinel-2 cloudless layer for 2020 by EOX - 4326 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2020) released under <a rel="license" + href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons + Attribution-NonCommercial-ShareAlike 4.0 International License</a>. For commercial usage please + see <a href="https://cloudless.eox.at">https://cloudless.eox.at</a> + + + -180.000000 -90.000000 + 180.000000 90.000000 + + s2cloudless-2020 + + image/jpeg + + WGS84 + + + + + Sentinel-2 cloudless layer for 2021 by EOX - 4326 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2021) released under <a rel="license" + href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons + Attribution-NonCommercial-ShareAlike 4.0 International License</a>. For commercial usage please + see <a href="https://cloudless.eox.at">https://cloudless.eox.at</a> + + + -180.000000 -90.000000 + 180.000000 90.000000 + + s2cloudless-2021 + + image/jpeg + + WGS84 + + + + + OpenStreetMap background layer by EOX - 3857 + <a href="https://maps.eox.at">OpenStreetMap</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + osm_3857 + + image/jpeg + + g + + + GoogleMapsCompatible + + + + + Sentinel-2 cloudless layer for 2016 by EOX - 3857 + <a xmlns:dct="http://purl.org/dc/terms/" href="https://s2maps.eu" property="dct:title">Sentinel-2 + cloudless - https://s2maps.eu</a> by <a xmlns:cc="http://creativecommons.org/ns#" + href="https://eox.at" property="cc:attributionName" rel="cc:attributionURL">EOX IT Services GmbH</a> + (Contains modified Copernicus Sentinel data 2016 &amp; 2017) released under <a rel="license" + href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International + License</a>. + + s2cloudless_3857 + + image/jpeg + + g + + + GoogleMapsCompatible + + + + + Terrain background layer by EOX - 3857 + <a href="https://maps.eox.at">Terrain</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors and <a + href="https://maps.eox.at/#data">others</a>, Rendering &copy; <a href="https://eox.at">EOX</a> + } + + terrain_3857 + + image/jpeg + + g + + + + + Bright overlay layer by EOX - 3857 + <a href="https://maps.eox.at">Overlay bright</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + overlay_base_bright_3857 + + image/png + + g + + + GoogleMapsCompatible + + + + + Hydrography overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Hydrography overlay</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + hydrography + + image/png + + WGS84 + + + + + Graticules overlay layer by EOX - 4326 + <a href="https://maps.eox.at">Graticules</a> { Rendering &copy; <a + href="https://eox.at">EOX</a> } + + + -180.000000 -90.000000 + 180.000000 90.000000 + + graticules + + image/png + + WGS84 + + + + + Overlay layer by EOX - 3857 + <a href="https://maps.eox.at">Overlay</a> { Data &copy; <a + href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering &copy; + <a href="https://eox.at">EOX</a> and <a href="https://github.com/mapserver/basemaps">MapServer</a> + } + + overlay_3857 + + image/png + + g + + + GoogleMapsCompatible + + + + + WGS84 + + -90.000000 -180.000000 + 90.000000 180.000000 + + urn:ogc:def:crs:EPSG:6.3:4326 + urn:ogc:def:wkss:OGC:1.0:GoogleCRS84Quad + + 0 + 279541132.01435887813568115234 + 90.000000 -180.000000 + 256 + 256 + 2 + 1 + + + 1 + 139770566.00717943906784057617 + 90.000000 -180.000000 + 256 + 256 + 4 + 2 + + + 2 + 69885283.00358971953392028809 + 90.000000 -180.000000 + 256 + 256 + 8 + 4 + + + 3 + 34942641.50179485976696014404 + 90.000000 -180.000000 + 256 + 256 + 16 + 8 + + + 4 + 17471320.75089742988348007202 + 90.000000 -180.000000 + 256 + 256 + 32 + 16 + + + 5 + 8735660.37544871494174003601 + 90.000000 -180.000000 + 256 + 256 + 64 + 32 + + + 6 + 4367830.18772435747087001801 + 90.000000 -180.000000 + 256 + 256 + 128 + 64 + + + 7 + 2183915.09386217873543500900 + 90.000000 -180.000000 + 256 + 256 + 256 + 128 + + + 8 + 1091957.54693108936771750450 + 90.000000 -180.000000 + 256 + 256 + 512 + 256 + + + 9 + 545978.77346554468385875225 + 90.000000 -180.000000 + 256 + 256 + 1024 + 512 + + + 10 + 272989.38673277234192937613 + 90.000000 -180.000000 + 256 + 256 + 2048 + 1024 + + + 11 + 136494.69336638617096468806 + 90.000000 -180.000000 + 256 + 256 + 4096 + 2048 + + + 12 + 68247.34668319308548234403 + 90.000000 -180.000000 + 256 + 256 + 8192 + 4096 + + + 13 + 34123.67334159654274117202 + 90.000000 -180.000000 + 256 + 256 + 16384 + 8192 + + + 14 + 17061.83667079825318069197 + 90.000000 -180.000000 + 256 + 256 + 32768 + 16384 + + + 15 + 8530.91833539912659034599 + 90.000000 -180.000000 + 256 + 256 + 65536 + 32768 + + + 16 + 4265.45916769956329517299 + 90.000000 -180.000000 + 256 + 256 + 131072 + 65536 + + + 17 + 2132.72958384978574031265 + 90.000000 -180.000000 + 256 + 256 + 262144 + 131072 + + + + GoogleMapsCompatible + + -20037508.342789 -20037508.342789 + 20037508.342789 20037508.342789 + + urn:ogc:def:crs:EPSG:6.3:3857 + urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible + + 0 + 559082264.02871787548065185547 + -20037508.342789 20037508.342789 + 256 + 256 + 1 + 1 + + + 1 + 279541132.01435887813568115234 + -20037508.342789 20037508.342789 + 256 + 256 + 2 + 2 + + + 2 + 139770566.00717940926551818848 + -20037508.342789 20037508.342789 + 256 + 256 + 4 + 4 + + + 3 + 69885283.00358971953392028809 + -20037508.342789 20037508.342789 + 256 + 256 + 8 + 8 + + + 4 + 34942641.50179485976696014404 + -20037508.342789 20037508.342789 + 256 + 256 + 16 + 16 + + + 5 + 17471320.75089742988348007202 + -20037508.342789 20037508.342789 + 256 + 256 + 32 + 32 + + + 6 + 8735660.37544871494174003601 + -20037508.342789 20037508.342789 + 256 + 256 + 64 + 64 + + + 7 + 4367830.18772435747087001801 + -20037508.342789 20037508.342789 + 256 + 256 + 128 + 128 + + + 8 + 2183915.09386217873543500900 + -20037508.342789 20037508.342789 + 256 + 256 + 256 + 256 + + + 9 + 1091957.54693108866922557354 + -20037508.342789 20037508.342789 + 256 + 256 + 512 + 512 + + + 10 + 545978.77346554468385875225 + -20037508.342789 20037508.342789 + 256 + 256 + 1024 + 1024 + + + 11 + 272989.38673277228372171521 + -20037508.342789 20037508.342789 + 256 + 256 + 2048 + 2048 + + + 12 + 136494.69336638617096468806 + -20037508.342789 20037508.342789 + 256 + 256 + 4096 + 4096 + + + 13 + 68247.34668319307093042880 + -20037508.342789 20037508.342789 + 256 + 256 + 8192 + 8192 + + + 14 + 34123.67334159654274117202 + -20037508.342789 20037508.342789 + 256 + 256 + 16384 + 16384 + + + 15 + 17061.83667079827137058601 + -20037508.342789 20037508.342789 + 256 + 256 + 32768 + 32768 + + + 16 + 8530.91833539913568529300 + -20037508.342789 20037508.342789 + 256 + 256 + 65536 + 65536 + + + 17 + 4265.45916769956784264650 + -20037508.342789 20037508.342789 + 256 + 256 + 131072 + 131072 + + + 18 + 2132.72958384978392132325 + -20037508.342789 20037508.342789 + 256 + 256 + 262144 + 262144 + + + + g + + -20037508.342789 -20037508.342789 + 20037508.342789 20037508.342789 + + urn:ogc:def:crs:EPSG:6.3:900913 + urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible + + 0 + 559082264.02871787548065185547 + -20037508.342789 20037508.342789 + 256 + 256 + 1 + 1 + + + 1 + 279541132.01435887813568115234 + -20037508.342789 20037508.342789 + 256 + 256 + 2 + 2 + + + 2 + 139770566.00717940926551818848 + -20037508.342789 20037508.342789 + 256 + 256 + 4 + 4 + + + 3 + 69885283.00358971953392028809 + -20037508.342789 20037508.342789 + 256 + 256 + 8 + 8 + + + 4 + 34942641.50179485976696014404 + -20037508.342789 20037508.342789 + 256 + 256 + 16 + 16 + + + 5 + 17471320.75089742988348007202 + -20037508.342789 20037508.342789 + 256 + 256 + 32 + 32 + + + 6 + 8735660.37544871494174003601 + -20037508.342789 20037508.342789 + 256 + 256 + 64 + 64 + + + 7 + 4367830.18772435747087001801 + -20037508.342789 20037508.342789 + 256 + 256 + 128 + 128 + + + 8 + 2183915.09386217873543500900 + -20037508.342789 20037508.342789 + 256 + 256 + 256 + 256 + + + 9 + 1091957.54693108866922557354 + -20037508.342789 20037508.342789 + 256 + 256 + 512 + 512 + + + 10 + 545978.77346554468385875225 + -20037508.342789 20037508.342789 + 256 + 256 + 1024 + 1024 + + + 11 + 272989.38673277228372171521 + -20037508.342789 20037508.342789 + 256 + 256 + 2048 + 2048 + + + 12 + 136494.69336638617096468806 + -20037508.342789 20037508.342789 + 256 + 256 + 4096 + 4096 + + + 13 + 68247.34668319307093042880 + -20037508.342789 20037508.342789 + 256 + 256 + 8192 + 8192 + + + 14 + 34123.67334159654274117202 + -20037508.342789 20037508.342789 + 256 + 256 + 16384 + 16384 + + + 15 + 17061.83667079827137058601 + -20037508.342789 20037508.342789 + 256 + 256 + 32768 + 32768 + + + 16 + 8530.91833539913568529300 + -20037508.342789 20037508.342789 + 256 + 256 + 65536 + 65536 + + + 17 + 4265.45916769956784264650 + -20037508.342789 20037508.342789 + 256 + 256 + 131072 + 131072 + + + 18 + 2132.72958384978392132325 + -20037508.342789 20037508.342789 + 256 + 256 + 262144 + 262144 + + + + diff --git a/fixtures/wmts/capabilities_wgs84.xml b/fixtures/wmts/capabilities_wgs84.xml new file mode 100644 index 0000000..c9e0c1d --- /dev/null +++ b/fixtures/wmts/capabilities_wgs84.xml @@ -0,0 +1,172 @@ + + + + Sample WMTS + OGC WMTS + 1.0.0 + None + none + + + + + + + + + + + + + + + + + + Baselayer + Baselayer + baselayer + + -180.0 -90.0 + 180.0 90.0 + + + -90.0 -180.0 + 90.0 180.0 + + + image/png + + inspire_quad + + + + + InspireCRS84Quad + inspire_quad + urn:ogc:def:crs:EPSG::4326 + + 0 + 279541132.014357 + 90.0 -180.0 + 256 + 256 + 2 + 1 + + + 1 + 1.3977056600717938E8 + 90.0 -180.0 + 256 + 256 + 4 + 2 + + + 2 + 6.988528300358969E7 + 90.0 -180.0 + 256 + 256 + 8 + 4 + + + 3 + 3.4942641501794845E7 + 90.0 -180.0 + 256 + 256 + 16 + 8 + + + 4 + 1.7471320750897422E7 + 90.0 -180.0 + 256 + 256 + 32 + 16 + + + 5 + 8735660.375448711 + 90.0 -180.0 + 256 + 256 + 64 + 32 + + + 6 + 4367830.187724356 + 90.0 -180.0 + 256 + 256 + 128 + 64 + + + 7 + 2183915.093862178 + 90.0 -180.0 + 256 + 256 + 256 + 128 + + + 8 + 1091957.546931089 + 90.0 -180.0 + 256 + 256 + 512 + 256 + + + 9 + 545978.7734655445 + 90.0 -180.0 + 256 + 256 + 1024 + 512 + + + 10 + 272989.3867327722 + 90.0 -180.0 + 256 + 256 + 2048 + 1024 + + + 11 + 136494.6933663861 + 90.0 -180.0 + 256 + 256 + 4096 + 2048 + + + 12 + 68247.34668319306 + 90.0 -180.0 + 256 + 256 + 8192 + 4096 + + + + + diff --git a/fixtures/wmts/capabilities_wgs84_with_boundingbox.xml b/fixtures/wmts/capabilities_wgs84_with_boundingbox.xml new file mode 100644 index 0000000..45693a6 --- /dev/null +++ b/fixtures/wmts/capabilities_wgs84_with_boundingbox.xml @@ -0,0 +1,705 @@ + + + basemap.at + basemap.at ist eine Rasterkarte in Form eines vorgenerierten Kachel-Caches, in der Web Mercator + Auxiliary Sphere und damit kompatibel zu den gängigen weltweiten Basiskarten wie beispielsweise jenen von + OpenStreetMap, Google Maps und Bing Maps. + + OGC WMTS + 1.0.0 + none + https://www.basemap.at + + + City of Vienna + + + + + Vienna + Austria + viennagis@post.wien.gv.at + + + + + + + + + + + + RESTful + + + + + + + RESTful + + + + + + + RESTful + + + + + + + RESTful + + + + + + + RESTful + + + + + + + + + + + + + RESTful + + + + + + + RESTful + + + + + + + RESTful + + + + + + + RESTful + + + + + + + RESTful + + + + + + + + + + Geoland Basemap + Basemap von Österreich in Farbe + + 8.782379 46.358770 + 17.5 49.037872 + + geolandbasemap + + image/png + + google3857 + + + + + + + + + Geoland Basemap Overlay + Basemap von Österreich nur Beschriftung als transparenter Layer + + 8.782379 46.358770 + 17.5 49.037872 + + bmapoverlay + + image/png + + google3857 + + + + + + + + + Geoland Basemap Grau + Basemap von Österreich in Grau + + 8.782379 46.358770 + 17.5 49.037872 + + bmapgrau + + image/png + + google3857 + + + + + + + + + Basemap High DPI + Basemap mit 512x512px Kacheln für Unterstützung von User Endgeräten mit high dpi Displays + (iPad retina, smartphones mit HD Auflösung) + + + 8.782379 46.358770 + 17.5 49.037872 + + bmaphidpi + + image/jpeg + + google3857 + + + + + + + + + Geoland Basemap Orthofoto + Basemap als farbiges Orthofoto + + 8.782379 46.358770 + 17.5 49.037872 + + bmaporthofoto30cm + + image/jpeg + + google3857 + + + + + + + + + Geoland Basemap Gelände + Basemap Geländedarstellung von Österreich in Grau + + 8.782379 46.358770 + 17.5 49.037872 + + bmapgelaende + + image/jpeg + + google3857_0-17 + + + + + + + + + Geoland Basemap Oberfläche + Basemap Oberflächendarstellung von Österreich in Grau + + 8.782379 46.358770 + 17.5 49.037872 + + bmapoberflaeche + + image/jpeg + + google3857_0-17 + + + + + + + + + google3857 + + 977650 5838030 + 1913530 6281290 + + urn:ogc:def:crs:EPSG:6.18.3:3857 + urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible + + 0 + 559082264.029 + -20037508.3428 20037508.3428 + 256 + 256 + 1 + 1 + + + 1 + 279541132.015 + -20037508.3428 20037508.3428 + 256 + 256 + 2 + 2 + + + 2 + 139770566.007 + -20037508.3428 20037508.3428 + 256 + 256 + 4 + 4 + + + 3 + 69885283.0036 + -20037508.3428 20037508.3428 + 256 + 256 + 8 + 8 + + + 4 + 34942641.5018 + -20037508.3428 20037508.3428 + 256 + 256 + 16 + 16 + + + 5 + 17471320.7509 + -20037508.3428 20037508.3428 + 256 + 256 + 32 + 32 + + + 6 + 8735660.37545 + -20037508.3428 20037508.3428 + 256 + 256 + 64 + 64 + + + 7 + 4367830.18773 + -20037508.3428 20037508.3428 + 256 + 256 + 128 + 128 + + + 8 + 2183915.09386 + -20037508.3428 20037508.3428 + 256 + 256 + 256 + 256 + + + 9 + 1091957.54693 + -20037508.3428 20037508.3428 + 256 + 256 + 512 + 512 + + + 10 + 545978.773466 + -20037508.3428 20037508.3428 + 256 + 256 + 1024 + 1024 + + + 11 + 272989.386733 + -20037508.3428 20037508.3428 + 256 + 256 + 2048 + 2048 + + + 12 + 136494.693366 + -20037508.3428 20037508.3428 + 256 + 256 + 4096 + 4096 + + + 13 + 68247.3466832 + -20037508.3428 20037508.3428 + 256 + 256 + 8192 + 8192 + + + 14 + 34123.6733416 + -20037508.3428 20037508.3428 + 256 + 256 + 16384 + 16384 + + + 15 + 17061.8366708 + -20037508.3428 20037508.3428 + 256 + 256 + 32768 + 32768 + + + 16 + 8530.91833540 + -20037508.3428 20037508.3428 + 256 + 256 + 65536 + 65536 + + + 17 + 4265.45916770 + -20037508.3428 20037508.3428 + 256 + 256 + 131072 + 131072 + + + 18 + 2132.72958385 + -20037508.3428 20037508.3428 + 256 + 256 + 262144 + 262144 + + + 19 + 1066.36479193 + -20037508.3428 20037508.3428 + 256 + 256 + 524288 + 524288 + + + 20 + 533.18239597 + -20037508.3428 20037508.3428 + 256 + 256 + 1048576 + 1048576 + + + + google3857_0-17 + + 977650 5838030 + 1913530 6281290 + + urn:ogc:def:crs:EPSG:6.18.3:3857 + urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible + + 0 + 559082264.029 + -20037508.3428 20037508.3428 + 256 + 256 + 1 + 1 + + + 1 + 279541132.015 + -20037508.3428 20037508.3428 + 256 + 256 + 2 + 2 + + + 2 + 139770566.007 + -20037508.3428 20037508.3428 + 256 + 256 + 4 + 4 + + + 3 + 69885283.0036 + -20037508.3428 20037508.3428 + 256 + 256 + 8 + 8 + + + 4 + 34942641.5018 + -20037508.3428 20037508.3428 + 256 + 256 + 16 + 16 + + + 5 + 17471320.7509 + -20037508.3428 20037508.3428 + 256 + 256 + 32 + 32 + + + 6 + 8735660.37545 + -20037508.3428 20037508.3428 + 256 + 256 + 64 + 64 + + + 7 + 4367830.18773 + -20037508.3428 20037508.3428 + 256 + 256 + 128 + 128 + + + 8 + 2183915.09386 + -20037508.3428 20037508.3428 + 256 + 256 + 256 + 256 + + + 9 + 1091957.54693 + -20037508.3428 20037508.3428 + 256 + 256 + 512 + 512 + + + 10 + 545978.773466 + -20037508.3428 20037508.3428 + 256 + 256 + 1024 + 1024 + + + 11 + 272989.386733 + -20037508.3428 20037508.3428 + 256 + 256 + 2048 + 2048 + + + 12 + 136494.693366 + -20037508.3428 20037508.3428 + 256 + 256 + 4096 + 4096 + + + 13 + 68247.3466832 + -20037508.3428 20037508.3428 + 256 + 256 + 8192 + 8192 + + + 14 + 34123.6733416 + -20037508.3428 20037508.3428 + 256 + 256 + 16384 + 16384 + + + 15 + 17061.8366708 + -20037508.3428 20037508.3428 + 256 + 256 + 32768 + 32768 + + + 16 + 8530.91833540 + -20037508.3428 20037508.3428 + 256 + 256 + 65536 + 65536 + + + 17 + 4265.45916770 + -20037508.3428 20037508.3428 + 256 + 256 + 131072 + 131072 + + + + + diff --git a/fixtures/wmts/capabilities_with_tilematrixsetlink.xml b/fixtures/wmts/capabilities_with_tilematrixsetlink.xml new file mode 100644 index 0000000..3dfb454 --- /dev/null +++ b/fixtures/wmts/capabilities_with_tilematrixsetlink.xml @@ -0,0 +1,287 @@ + + + + Sample WMTS + OGC WMTS + 1.0.0 + None + none + + + + + + + + + + + + + + + + + + Baselayer + Baselayer + baselayer + + -180.0 -90.0 + 180.0 90.0 + + + -90.0 -180.0 + 90.0 180.0 + + + image/png + + inspire_quad + + + + + Mean depth full coverage with land coverage + + mean_atlas_land + + -36.0 15.0 + 43.0 90.0 + + + 14.999942759061003 -36.0 + 90.0 42.999938986416 + + + image/png + + inspire_quad + + + 0 + 0 + 0 + 0 + 1 + + + 1 + 0 + 0 + 1 + 2 + + + 2 + 0 + 1 + 3 + 4 + + + 3 + 0 + 3 + 6 + 9 + + + 4 + 0 + 6 + 12 + 19 + + + 5 + 0 + 13 + 25 + 39 + + + 6 + 0 + 26 + 51 + 79 + + + 7 + 0 + 53 + 102 + 158 + + + 8 + 0 + 106 + 204 + 317 + + + 9 + 0 + 213 + 409 + 634 + + + 10 + 0 + 426 + 819 + 1268 + + + 11 + 0 + 853 + 1638 + 2537 + + + 12 + 0 + 1706 + 3276 + 5074 + + + + + + + InspireCRS84Quad + inspire_quad + urn:ogc:def:crs:EPSG::4326 + + 0 + 279541132.014357 + 90.0 -180.0 + 256 + 256 + 2 + 1 + + + 1 + 1.3977056600717938E8 + 90.0 -180.0 + 256 + 256 + 4 + 2 + + + 2 + 6.988528300358969E7 + 90.0 -180.0 + 256 + 256 + 8 + 4 + + + 3 + 3.4942641501794845E7 + 90.0 -180.0 + 256 + 256 + 16 + 8 + + + 4 + 1.7471320750897422E7 + 90.0 -180.0 + 256 + 256 + 32 + 16 + + + 5 + 8735660.375448711 + 90.0 -180.0 + 256 + 256 + 64 + 32 + + + 6 + 4367830.187724356 + 90.0 -180.0 + 256 + 256 + 128 + 64 + + + 7 + 2183915.093862178 + 90.0 -180.0 + 256 + 256 + 256 + 128 + + + 8 + 1091957.546931089 + 90.0 -180.0 + 256 + 256 + 512 + 256 + + + 9 + 545978.7734655445 + 90.0 -180.0 + 256 + 256 + 1024 + 512 + + + 10 + 272989.3867327722 + 90.0 -180.0 + 256 + 256 + 2048 + 1024 + + + 11 + 136494.6933663861 + 90.0 -180.0 + 256 + 256 + 4096 + 2048 + + + 12 + 68247.34668319306 + 90.0 -180.0 + 256 + 256 + 8192 + 4096 + + + + + diff --git a/fixtures/wmts/capabilities_wrapx.xml b/fixtures/wmts/capabilities_wrapx.xml new file mode 100644 index 0000000..a410e03 --- /dev/null +++ b/fixtures/wmts/capabilities_wrapx.xml @@ -0,0 +1,1109 @@ + + + + OGC WMTS + 1.0.0 + + + + + + + + + + + + + + KVP + + + + + + + + + + + + + KVP + + + + + + + + + + + + + KVP + + + + + + + + + + no-bb + no-bb + + image/png; mode=8bit + + wrap + + + wrap:0 + 0 + 0 + 0 + 0 + + + wrap:1 + 0 + 1 + 0 + 1 + + + wrap:2 + 0 + 3 + 0 + 3 + + + wrap:3 + 0 + 7 + 0 + 7 + + + wrap:4 + 0 + 15 + 0 + 15 + + + wrap:5 + 0 + 31 + 0 + 31 + + + wrap:6 + 0 + 63 + 0 + 63 + + + wrap:7 + 0 + 127 + 0 + 127 + + + wrap:8 + 0 + 255 + 0 + 255 + + + wrap:9 + 0 + 511 + 0 + 511 + + + wrap:10 + 0 + 1023 + 0 + 1023 + + + wrap:11 + 0 + 2047 + 0 + 2047 + + + wrap:12 + 0 + 4095 + 0 + 4095 + + + wrap:13 + 0 + 8191 + 0 + 8191 + + + wrap:14 + 0 + 16383 + 0 + 16383 + + + wrap:15 + 0 + 32767 + 0 + 32767 + + + wrap:16 + 0 + 65535 + 0 + 65535 + + + wrap:17 + 0 + 131071 + 0 + 131071 + + + wrap:18 + 0 + 262143 + 0 + 262143 + + + wrap:19 + 0 + 524287 + 0 + 524287 + + + wrap:20 + 0 + 1048575 + 0 + 1048575 + + + + + + + only-wgs84-bb + only-wgs84-bb + + -180.0 -90.0 + 180.0 90.0 + + + image/png; mode=8bit + + wrap + + + wrap:0 + 0 + 0 + 0 + 0 + + + wrap:1 + 0 + 1 + 0 + 1 + + + wrap:2 + 0 + 3 + 0 + 3 + + + wrap:3 + 0 + 7 + 0 + 7 + + + wrap:4 + 0 + 15 + 0 + 15 + + + wrap:5 + 0 + 31 + 0 + 31 + + + wrap:6 + 0 + 63 + 0 + 63 + + + wrap:7 + 0 + 127 + 0 + 127 + + + wrap:8 + 0 + 255 + 0 + 255 + + + wrap:9 + 0 + 511 + 0 + 511 + + + wrap:10 + 0 + 1023 + 0 + 1023 + + + wrap:11 + 0 + 2047 + 0 + 2047 + + + wrap:12 + 0 + 4095 + 0 + 4095 + + + wrap:13 + 0 + 8191 + 0 + 8191 + + + wrap:14 + 0 + 16383 + 0 + 16383 + + + wrap:15 + 0 + 32767 + 0 + 32767 + + + wrap:16 + 0 + 65535 + 0 + 65535 + + + wrap:17 + 0 + 131071 + 0 + 131071 + + + wrap:18 + 0 + 262143 + 0 + 262143 + + + wrap:19 + 0 + 524287 + 0 + 524287 + + + wrap:20 + 0 + 1048575 + 0 + 1048575 + + + + + + + no-wrap-wgs84-bb + no-wrap-wgs84-bb + + -179.9 -90.0 + 180.0 90.0 + + + image/png; mode=8bit + + wrap + + + wrap:0 + 0 + 0 + 0 + 0 + + + wrap:1 + 0 + 1 + 0 + 1 + + + wrap:2 + 0 + 3 + 0 + 3 + + + wrap:3 + 0 + 7 + 0 + 7 + + + wrap:4 + 0 + 15 + 0 + 15 + + + wrap:5 + 0 + 31 + 0 + 31 + + + wrap:6 + 0 + 63 + 0 + 63 + + + wrap:7 + 0 + 127 + 0 + 127 + + + wrap:8 + 0 + 255 + 0 + 255 + + + wrap:9 + 0 + 511 + 0 + 511 + + + wrap:10 + 0 + 1023 + 0 + 1023 + + + wrap:11 + 0 + 2047 + 0 + 2047 + + + wrap:12 + 0 + 4095 + 0 + 4095 + + + wrap:13 + 0 + 8191 + 0 + 8191 + + + wrap:14 + 0 + 16383 + 0 + 16383 + + + wrap:15 + 0 + 32767 + 0 + 32767 + + + wrap:16 + 0 + 65535 + 0 + 65535 + + + wrap:17 + 0 + 131071 + 0 + 131071 + + + wrap:18 + 0 + 262143 + 0 + 262143 + + + wrap:19 + 0 + 524287 + 0 + 524287 + + + wrap:20 + 0 + 1048575 + 0 + 1048575 + + + + + + + no-wrap-tm + no-wrap-tm + + image/png; mode=8bit + + no-wrap + + + no-wrap:0 + 0 + 0 + 0 + 0 + + + no-wrap:1 + 0 + 1 + 0 + 1 + + + no-wrap:2 + 0 + 3 + 0 + 3 + + + no-wrap:3 + 0 + 7 + 0 + 7 + + + no-wrap:4 + 0 + 15 + 0 + 15 + + + no-wrap:5 + 0 + 31 + 0 + 31 + + + no-wrap:6 + 0 + 63 + 0 + 63 + + + no-wrap:7 + 0 + 127 + 0 + 127 + + + no-wrap:8 + 0 + 255 + 0 + 255 + + + no-wrap:9 + 0 + 511 + 0 + 511 + + + no-wrap:10 + 0 + 1023 + 0 + 1023 + + + no-wrap:11 + 0 + 2047 + 0 + 2047 + + + no-wrap:12 + 0 + 4095 + 0 + 4095 + + + no-wrap:13 + 0 + 8191 + 0 + 8191 + + + no-wrap:14 + 0 + 16383 + 0 + 16383 + + + no-wrap:15 + 0 + 32767 + 0 + 32767 + + + no-wrap:16 + 0 + 65535 + 0 + 65535 + + + no-wrap:17 + 0 + 131071 + 0 + 131071 + + + no-wrap:18 + 0 + 262143 + 0 + 262143 + + + no-wrap:19 + 0 + 524287 + 0 + 524287 + + + no-wrap:20 + 0 + 1048575 + 0 + 1048575 + + + + + + + wrap + + -20037508.342789 -20037508.342789 + 20037508.342789 20037508.342789 + + urn:ogc:def:crs:EPSG:6.3:3857 + + 0 + 559082264.02871787548065185547 + -20037508.342789 20037508.342789 + 256 + 256 + 1 + 1 + + + 1 + 279541132.01435893774032592773 + -20037508.342789 20037508.342789 + 256 + 256 + 2 + 2 + + + 2 + 139770566.00717946887016296387 + -20037508.342789 20037508.342789 + 256 + 256 + 4 + 4 + + + 3 + 69885283.00358973443508148193 + -20037508.342789 20037508.342789 + 256 + 256 + 8 + 8 + + + 4 + 34942641.50179486721754074097 + -20037508.342789 20037508.342789 + 256 + 256 + 16 + 16 + + + 5 + 17471320.75089743360877037048 + -20037508.342789 20037508.342789 + 256 + 256 + 32 + 32 + + + 6 + 8735660.37544871680438518524 + -20037508.342789 20037508.342789 + 256 + 256 + 64 + 64 + + + 7 + 4367830.18772435840219259262 + -20037508.342789 20037508.342789 + 256 + 256 + 128 + 128 + + + 8 + 2183915.09386217920109629631 + -20037508.342789 20037508.342789 + 256 + 256 + 256 + 256 + + + 9 + 1091957.54693108960054814816 + -20037508.342789 20037508.342789 + 256 + 256 + 512 + 512 + + + 10 + 545978.77346554480027407408 + -20037508.342789 20037508.342789 + 256 + 256 + 1024 + 1024 + + + 11 + 272989.38673277240013703704 + -20037508.342789 20037508.342789 + 256 + 256 + 2048 + 2048 + + + 12 + 136494.69336638620006851852 + -20037508.342789 20037508.342789 + 256 + 256 + 4096 + 4096 + + + 13 + 68247.34668319310003425926 + -20037508.342789 20037508.342789 + 256 + 256 + 8192 + 8192 + + + 14 + 34123.67334159655001712963 + -20037508.342789 20037508.342789 + 256 + 256 + 16384 + 16384 + + + 15 + 17061.83667079827500856481 + -20037508.342789 20037508.342789 + 256 + 256 + 32768 + 32768 + + + 16 + 8530.91833539913750428241 + -20037508.342789 20037508.342789 + 256 + 256 + 65536 + 65536 + + + 17 + 4265.45916769956875214120 + -20037508.342789 20037508.342789 + 256 + 256 + 131072 + 131072 + + + 18 + 2132.72958384978437607060 + -20037508.342789 20037508.342789 + 256 + 256 + 262144 + 262144 + + + 19 + 1066.36479192489218803530 + -20037508.342789 20037508.342789 + 256 + 256 + 524288 + 524288 + + + 20 + 533.18239596244609401765 + -20037508.342789 20037508.342789 + 256 + 256 + 1048576 + 1048576 + + + + no-wrap + + -19037508.342789 -20037508.342789 + 20037508.342789 20037508.342789 + + urn:ogc:def:crs:EPSG:6.3:3857 + + 0 + 559082264.02871787548065185547 + -20037508.342789 20037508.342789 + 256 + 256 + 1 + 1 + + + 1 + 279541132.01435893774032592773 + -20037508.342789 20037508.342789 + 256 + 256 + 2 + 2 + + + 2 + 139770566.00717946887016296387 + -20037508.342789 20037508.342789 + 256 + 256 + 4 + 4 + + + 3 + 69885283.00358973443508148193 + -20037508.342789 20037508.342789 + 256 + 256 + 8 + 8 + + + 4 + 34942641.50179486721754074097 + -20037508.342789 20037508.342789 + 256 + 256 + 16 + 16 + + + 5 + 17471320.75089743360877037048 + -20037508.342789 20037508.342789 + 256 + 256 + 32 + 32 + + + 6 + 8735660.37544871680438518524 + -20037508.342789 20037508.342789 + 256 + 256 + 64 + 64 + + + 7 + 4367830.18772435840219259262 + -20037508.342789 20037508.342789 + 256 + 256 + 128 + 128 + + + 8 + 2183915.09386217920109629631 + -20037508.342789 20037508.342789 + 256 + 256 + 256 + 256 + + + 9 + 1091957.54693108960054814816 + -20037508.342789 20037508.342789 + 256 + 256 + 512 + 512 + + + 10 + 545978.77346554480027407408 + -20037508.342789 20037508.342789 + 256 + 256 + 1024 + 1024 + + + 11 + 272989.38673277240013703704 + -20037508.342789 20037508.342789 + 256 + 256 + 2048 + 2048 + + + 12 + 136494.69336638620006851852 + -20037508.342789 20037508.342789 + 256 + 256 + 4096 + 4096 + + + 13 + 68247.34668319310003425926 + -20037508.342789 20037508.342789 + 256 + 256 + 8192 + 8192 + + + 14 + 34123.67334159655001712963 + -20037508.342789 20037508.342789 + 256 + 256 + 16384 + 16384 + + + 15 + 17061.83667079827500856481 + -20037508.342789 20037508.342789 + 256 + 256 + 32768 + 32768 + + + 16 + 8530.91833539913750428241 + -20037508.342789 20037508.342789 + 256 + 256 + 65536 + 65536 + + + 17 + 4265.45916769956875214120 + -20037508.342789 20037508.342789 + 256 + 256 + 131072 + 131072 + + + 18 + 2132.72958384978437607060 + -20037508.342789 20037508.342789 + 256 + 256 + 262144 + 262144 + + + 19 + 1066.36479192489218803530 + -20037508.342789 20037508.342789 + 256 + 256 + 524288 + 524288 + + + 20 + 533.18239596244609401765 + -20037508.342789 20037508.342789 + 256 + 256 + 1048576 + 1048576 + + + + diff --git a/fixtures/wmts/ign.xml b/fixtures/wmts/ign.xml new file mode 100644 index 0000000..6cd8d54 --- /dev/null +++ b/fixtures/wmts/ign.xml @@ -0,0 +1,501 @@ + + + + Service de visualisation WMTS + + Ce service permet la visualisation de couches de données raster IGN au travers d'un flux WMTS + + + Unités administratives + Limites administratives + Surfaces bâties + Réseaux de transport + Routes + Réseaux ferroviaires + Aérodromes + Réseau hydrographique + Parcelles cadastrales + Bâtiments + Services d'utilité publique et services publics + Réseaux de transport + Hydrographie + Photographies aériennes + Cartes + Cartes historiques + Altitude + + OGC WMTS + 1.0.0 + licences + + Conditions Générales d'Utilisation disponibles ici : + http://professionnels.ign.fr/doc/Conditions_d_utilisation_des_licences_et_des_services_en_ligne.pdf + + + + IGN + + + Géoportail SAV + custodian + + + + + + + 73 avenue de Paris + Saint Mandé + + 94160 + France + geop_services@geoportail.fr + + + + + + + + + + + + KVP + + + + + + + + + + + + + KVP + + + + + + + + + + Photographies aériennes + Photographies aériennes + + Photographies + + + -180 -86 + 180 84 + + ORTHOIMAGERY.ORTHOPHOTOS + + image/jpeg + + PM + + + 0 + 0 + 1 + 0 + 1 + + + 1 + 0 + 2 + 0 + 2 + + + 10 + 31 + 1024 + 0 + 1024 + + + 11 + 62 + 2048 + 0 + 2048 + + + 12 + 125 + 4096 + 0 + 4096 + + + 13 + 2739 + 4628 + 41 + 7917 + + + 14 + 5478 + 9256 + 82 + 15835 + + + 15 + 10956 + 18513 + 165 + 31670 + + + 16 + 21912 + 37026 + 330 + 63341 + + + 17 + 43825 + 74052 + 660 + 126683 + + + 18 + 87651 + 148105 + 1320 + 253366 + + + 19 + 175302 + 294060 + 170159 + 343473 + + + 2 + 0 + 4 + 0 + 4 + + + 3 + 0 + 8 + 0 + 8 + + + 4 + 0 + 16 + 0 + 16 + + + 5 + 0 + 32 + 0 + 32 + + + 6 + 1 + 64 + 0 + 64 + + + 7 + 3 + 128 + 0 + 128 + + + 8 + 7 + 256 + 0 + 256 + + + 9 + 15 + 512 + 0 + 512 + + + + + Prefixed + + + Prefixed:0 + 0 + 1 + 0 + 1 + + + Prefixed:1 + 0 + 2 + 0 + 2 + + + + + + PM + EPSG:3857 + + 0 + 559082264.0287178958533332 + -20037508 20037508 + 256 + 256 + 1 + 1 + + + 1 + 279541132.0143588959472254 + -20037508 20037508 + 256 + 256 + 2 + 2 + + + 10 + 545978.7734655447186469 + -20037508 20037508 + 256 + 256 + 1024 + 1024 + + + 11 + 272989.3867327723085907 + -20037508 20037508 + 256 + 256 + 2048 + 2048 + + + 12 + 136494.6933663861796617 + -20037508 20037508 + 256 + 256 + 4096 + 4096 + + + 13 + 68247.3466831930771477 + -20037508 20037508 + 256 + 256 + 8192 + 8192 + + + 14 + 34123.6733415965449154 + -20037508 20037508 + 256 + 256 + 16384 + 16384 + + + 15 + 17061.8366707982724577 + -20037508 20037508 + 256 + 256 + 32768 + 32768 + + + 16 + 8530.9183353991362289 + -20037508 20037508 + 256 + 256 + 65536 + 65536 + + + 17 + 4265.4591676995681144 + -20037508 20037508 + 256 + 256 + 131072 + 131072 + + + 18 + 2132.7295838497840572 + -20037508 20037508 + 256 + 256 + 262144 + 262144 + + + 19 + 1066.3647919248918304 + -20037508 20037508 + 256 + 256 + 524288 + 524288 + + + 2 + 139770566.0071793960087234 + -20037508 20037508 + 256 + 256 + 4 + 4 + + + 20 + 533.1823959624461134 + -20037508 20037508 + 256 + 256 + 1048576 + 1048576 + + + 21 + 266.5911979812228585 + -20037508 20037508 + 256 + 256 + 2097152 + 2097152 + + + 3 + 69885283.0035897239868063 + -20037508 20037508 + 256 + 256 + 8 + 8 + + + 4 + 34942641.5017948619934032 + -20037508 20037508 + 256 + 256 + 16 + 16 + + + 5 + 17471320.7508974309967016 + -20037508 20037508 + 256 + 256 + 32 + 32 + + + 6 + 8735660.3754487154983508 + -20037508 20037508 + 256 + 256 + 64 + 64 + + + 7 + 4367830.1877243577491754 + -20037508 20037508 + 256 + 256 + 128 + 128 + + + 8 + 2183915.0938621788745877 + -20037508 20037508 + 256 + 256 + 256 + 256 + + + 9 + 1091957.5469310886252288 + -20037508 20037508 + 256 + 256 + 512 + 512 + + + + Prefixed + EPSG:3857 + + 0 + 559082264.0287178958533332 + -20037508 20037508 + 256 + 256 + 1 + 1 + + + 1 + 279541132.0143588959472254 + -20037508 20037508 + 256 + 256 + 2 + 2 + + + + diff --git a/fixtures/wmts/ogcsample.xml b/fixtures/wmts/ogcsample.xml new file mode 100644 index 0000000..524afd8 --- /dev/null +++ b/fixtures/wmts/ogcsample.xml @@ -0,0 +1,414 @@ + + + + Web Map Tile Service + Service that contrains the map + access interface to some TileMatrixSets + + + tile + tile matrix set + map + + OGC WMTS + 1.0.0 + none + none + + + MiraMon + + + Joan Maso Pau + Senior Software Engineer + + + +34 93 581 1312 + +34 93 581 4151 + + + Fac Ciencies UAB + Bellaterra + Barcelona + 08193 + Spain + joan.maso@uab.cat + + + + + + + + + + + + KVP + SOAP + + + + + + + + + + + + + KVP + + + + + + + + + + Blue Marble Next Generation + Blue Marble Next Generation NASA Product + + -180 -90 + 180 90 + + BlueMarbleNextGeneration + + + image/jpeg + image/gif + + BigWorldPixel + + + google3857 + + + google3857subset + + + + + Time + 20110805 + 20110805 + 20081024 + + + OtherDimension + abcd + + + + + google3857 + + 1799448.394855 6124949.747770 + 1848250.442089 6162571.828177 + + urn:ogc:def:crs:EPSG:6.18:3:3857 + urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible + + 0 + 559082264.029 + -20037508.3428 20037508.3428 + 256 + 256 + 1 + 1 + + + 1 + 279541132.015 + -20037508.3428 20037508.3428 + 256 + 256 + 2 + 2 + + + 2 + 139770566.007 + -20037508.3428 20037508.3428 + 256 + 256 + 4 + 4 + + + 3 + 69885283.0036 + -20037508.3428 20037508.3428 + 256 + 256 + 8 + 8 + + + 4 + 34942641.5018 + -20037508.3428 20037508.3428 + 256 + 256 + 16 + 16 + + + 5 + 17471320.7509 + -20037508.3428 20037508.3428 + 256 + 256 + 32 + 32 + + + 6 + 8735660.37545 + -20037508.3428 20037508.3428 + 256 + 256 + 64 + 64 + + + 7 + 4367830.18773 + -20037508.3428 20037508.3428 + 256 + 256 + 128 + 128 + + + 8 + 2183915.09386 + -20037508.3428 20037508.3428 + 256 + 256 + 256 + 256 + + + 9 + 1091957.54693 + -20037508.3428 20037508.3428 + 256 + 256 + 512 + 512 + + + 10 + 545978.773466 + -20037508.3428 20037508.3428 + 256 + 256 + 1024 + 1024 + + + 11 + 272989.386733 + -20037508.3428 20037508.3428 + 256 + 256 + 2048 + 2048 + + + 12 + 136494.693366 + -20037508.3428 20037508.3428 + 256 + 256 + 4096 + 4096 + + + 13 + 68247.3466832 + -20037508.3428 20037508.3428 + 256 + 256 + 8192 + 8192 + + + 14 + 34123.6733416 + -20037508.3428 20037508.3428 + 256 + 256 + 16384 + 16384 + + + 15 + 17061.8366708 + -20037508.3428 20037508.3428 + 256 + 256 + 32768 + 32768 + + + 16 + 8530.91833540 + -20037508.3428 20037508.3428 + 256 + 256 + 65536 + 65536 + + + 17 + 4265.45916770 + -20037508.3428 20037508.3428 + 256 + 256 + 131072 + 131072 + + + 18 + 2132.72958385 + -20037508.3428 20037508.3428 + 256 + 256 + 262144 + 262144 + + + 19 + 1066.36479193 + -20037508.3428 20037508.3428 + 256 + 256 + 524288 + 524288 + + + + BigWorldPixel + urn:ogc:def:crs:OGC:1.3:CRS84 + urn:ogc:def:wkss:OGC:1.0:GlobalCRS84Pixel + + 10000m + 33130800.83133142 + -180 90 + 640 + 480 + 7 + 5 + + + 20000m + 66261601.66266284 + -180 90 + 640 + 480 + 4 + 3 + + + 40000m + 132523203.3253257 + -180 90 + 640 + 480 + 2 + 2 + + + 60000m + 198784804.9879885 + -180 90 + 640 + 480 + 1 + 1 + + + 120000m + 397569609.9759771 + -180 90 + 640 + 480 + 1 + 1 + + + 240000m + 795139219.9519541 + -180 90 + 640 + 480 + 1 + 1 + + + + BigWorld + urn:ogc:def:crs:OGC:1.3:CRS84 + + 1e6 + 1e6 + -180 84 + 256 + 256 + 60000 + 50000 + + + 2.5e6 + 2.5e6 + -180 84 + 256 + 256 + 9000 + 7000 + + + + + google3857subset + urn:ogc:def:crs:EPSG:6.18:3:3857 + + 18 + 2132.72958385 + -10000000 10000000 + 256 + 256 + 1 + 1 + + + 18 + 1066.36479193 + -10000000 10000000 + 256 + 256 + 2 + 2 + + + + + diff --git a/src/wmts/capabilities.spec.ts b/src/wmts/capabilities.spec.ts new file mode 100644 index 0000000..e94c1ea --- /dev/null +++ b/src/wmts/capabilities.spec.ts @@ -0,0 +1,740 @@ +import { + readInfoFromCapabilities, + readLayersFromCapabilities, + readMatrixSetsFromCapabilities, +} from './capabilities'; +import { parseXmlString } from '../shared/xml-utils'; +// @ts-ignore +import ogcsample from '../../fixtures/wmts/ogcsample.xml'; +// @ts-ignore +import arcgis from '../../fixtures/wmts/arcgis.xml'; +// @ts-ignore +import ign from '../../fixtures/wmts/ign.xml'; + +describe('WMTS Capabilities', () => { + describe('readInfoFromCapabilities', () => { + describe('ogcsample.xml', () => { + it('parses the service info', () => { + const doc = parseXmlString(ogcsample); + expect(readInfoFromCapabilities(doc)).toEqual({ + abstract: + 'Service that contrains the map\n access interface to some TileMatrixSets\n ', + constraints: 'none', + fees: 'none', + keywords: ['tile', 'tile matrix set', 'map'], + name: 'OGC WMTS', + title: 'Web Map Tile Service', + getTileUrls: { + kvp: 'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?', + }, + }); + }); + }); + describe('arcgis.xml', () => { + it('parses the service info', () => { + const doc = parseXmlString(arcgis); + expect(readInfoFromCapabilities(doc)).toEqual({ + abstract: '', + constraints: '', + fees: '', + keywords: [], + name: 'OGC WMTS', + title: 'Demographics_USA_Population_Density', + getTileUrls: { + rest: 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS/tile/1.0.0/', + kvp: 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS?', + }, + }); + }); + }); + describe('ign.xml', () => { + it('parses the service info', () => { + const doc = parseXmlString(ign); + expect(readInfoFromCapabilities(doc)).toEqual({ + abstract: + "\n Ce service permet la visualisation de couches de données raster IGN au travers d'un flux WMTS\n ", + constraints: + "\n Conditions Générales d'Utilisation disponibles ici :\n http://professionnels.ign.fr/doc/Conditions_d_utilisation_des_licences_et_des_services_en_ligne.pdf\n ", + fees: 'licences', + keywords: [ + 'Unités administratives', + 'Limites administratives', + 'Surfaces bâties', + 'Réseaux de transport', + 'Routes', + 'Réseaux ferroviaires', + 'Aérodromes', + 'Réseau hydrographique', + 'Parcelles cadastrales', + 'Bâtiments', + "Services d'utilité publique et services publics", + 'Réseaux de transport', + 'Hydrographie', + 'Photographies aériennes', + 'Cartes', + 'Cartes historiques', + 'Altitude', + ], + name: 'OGC WMTS', + title: 'Service de visualisation WMTS', + getTileUrls: { + kvp: 'http://wxs.ign.fr/geoportail/wmts?', + }, + }); + }); + }); + }); + + describe('readMatrixSetsFromCapabilities', () => { + describe('ogcsample.xml', () => { + it('parses the matrix sets', () => { + const doc = parseXmlString(ogcsample); + expect(readMatrixSetsFromCapabilities(doc)).toEqual([ + { + boundingBox: [ + 1799448.394855, 6124949.74777, 1848250.442089, 6162571.828177, + ], + crs: 'urn:ogc:def:crs:EPSG:6.18:3:3857', + identifier: 'google3857', + tileMatrices: [ + { + identifier: '0', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 559082264.029, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '1', + matrixHeight: 2, + matrixWidth: 2, + scaleDenominator: 279541132.015, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '2', + matrixHeight: 4, + matrixWidth: 4, + scaleDenominator: 139770566.007, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '3', + matrixHeight: 8, + matrixWidth: 8, + scaleDenominator: 69885283.0036, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '4', + matrixHeight: 16, + matrixWidth: 16, + scaleDenominator: 34942641.5018, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '5', + matrixHeight: 32, + matrixWidth: 32, + scaleDenominator: 17471320.7509, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '6', + matrixHeight: 64, + matrixWidth: 64, + scaleDenominator: 8735660.37545, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '7', + matrixHeight: 128, + matrixWidth: 128, + scaleDenominator: 4367830.18773, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '8', + matrixHeight: 256, + matrixWidth: 256, + scaleDenominator: 2183915.09386, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '9', + matrixHeight: 512, + matrixWidth: 512, + scaleDenominator: 1091957.54693, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '10', + matrixHeight: 1024, + matrixWidth: 1024, + scaleDenominator: 545978.773466, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '11', + matrixHeight: 2048, + matrixWidth: 2048, + scaleDenominator: 272989.386733, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '12', + matrixHeight: 4096, + matrixWidth: 4096, + scaleDenominator: 136494.693366, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '13', + matrixHeight: 8192, + matrixWidth: 8192, + scaleDenominator: 68247.3466832, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '14', + matrixHeight: 16384, + matrixWidth: 16384, + scaleDenominator: 34123.6733416, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '15', + matrixHeight: 32768, + matrixWidth: 32768, + scaleDenominator: 17061.8366708, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '16', + matrixHeight: 65536, + matrixWidth: 65536, + scaleDenominator: 8530.9183354, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '17', + matrixHeight: 131072, + matrixWidth: 131072, + scaleDenominator: 4265.4591677, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '18', + matrixHeight: 262144, + matrixWidth: 262144, + scaleDenominator: 2132.72958385, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '19', + matrixHeight: 524288, + matrixWidth: 524288, + scaleDenominator: 1066.36479193, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + ], + wellKnownScaleSet: 'urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible', + }, + { + crs: 'urn:ogc:def:crs:OGC:1.3:CRS84', + identifier: 'BigWorldPixel', + tileMatrices: [ + { + identifier: '10000m', + matrixHeight: 5, + matrixWidth: 7, + scaleDenominator: 33130800.83133142, + tileHeight: 480, + tileWidth: 640, + topLeft: [-180, 90], + }, + { + identifier: '20000m', + matrixHeight: 3, + matrixWidth: 4, + scaleDenominator: 66261601.66266284, + tileHeight: 480, + tileWidth: 640, + topLeft: [-180, 90], + }, + { + identifier: '40000m', + matrixHeight: 2, + matrixWidth: 2, + scaleDenominator: 132523203.3253257, + tileHeight: 480, + tileWidth: 640, + topLeft: [-180, 90], + }, + { + identifier: '60000m', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 198784804.9879885, + tileHeight: 480, + tileWidth: 640, + topLeft: [-180, 90], + }, + { + identifier: '120000m', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 397569609.9759771, + tileHeight: 480, + tileWidth: 640, + topLeft: [-180, 90], + }, + { + identifier: '240000m', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 795139219.9519541, + tileHeight: 480, + tileWidth: 640, + topLeft: [-180, 90], + }, + ], + wellKnownScaleSet: 'urn:ogc:def:wkss:OGC:1.0:GlobalCRS84Pixel', + }, + { + crs: 'urn:ogc:def:crs:OGC:1.3:CRS84', + identifier: 'BigWorld', + tileMatrices: [ + { + identifier: '1e6', + matrixHeight: 50000, + matrixWidth: 60000, + scaleDenominator: 1000000, + tileHeight: 256, + tileWidth: 256, + topLeft: [-180, 84], + }, + { + identifier: '2.5e6', + matrixHeight: 7000, + matrixWidth: 9000, + scaleDenominator: 2500000, + tileHeight: 256, + tileWidth: 256, + topLeft: [-180, 84], + }, + ], + }, + { + crs: 'urn:ogc:def:crs:EPSG:6.18:3:3857', + identifier: 'google3857subset', + tileMatrices: [ + { + identifier: '18', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 2132.72958385, + tileHeight: 256, + tileWidth: 256, + topLeft: [-10000000, 10000000], + }, + { + identifier: '18', + matrixHeight: 2, + matrixWidth: 2, + scaleDenominator: 1066.36479193, + tileHeight: 256, + tileWidth: 256, + topLeft: [-10000000, 10000000], + }, + ], + }, + ]); + }); + }); + describe('arcgis.xml', () => { + it('parses the matrix sets', () => { + const doc = parseXmlString(arcgis); + expect(readMatrixSetsFromCapabilities(doc)).toEqual([ + { + crs: 'urn:ogc:def:crs:EPSG::3857', + identifier: 'default028mm', + tileMatrices: expect.arrayContaining([ + { + identifier: '0', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 559082264.0285016, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.342787, 20037508.342787], + }, + { + identifier: '1', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 279541132.01425034, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.342787, 20037508.342787], + }, + { + identifier: '2', + matrixHeight: 2, + matrixWidth: 2, + scaleDenominator: 139770566.00712562, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.342787, 20037508.342787], + }, + ]), + }, + { + crs: 'urn:ogc:def:crs:EPSG:6.18.3:3857', + identifier: 'GoogleMapsCompatible', + wellKnownScaleSet: 'urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible', + tileMatrices: expect.arrayContaining([ + { + identifier: '0', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 559082264.0287178, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.34278925, 20037508.34278925], + }, + { + identifier: '1', + matrixHeight: 2, + matrixWidth: 2, + scaleDenominator: 279541132.0143589, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.34278925, 20037508.34278925], + }, + { + identifier: '2', + matrixHeight: 4, + matrixWidth: 4, + scaleDenominator: 139770566.0071794, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.34278925, 20037508.34278925], + }, + ]), + }, + ]); + }); + }); + describe('ign.xml', () => { + it('parses the matrix sets', () => { + const doc = parseXmlString(ign); + expect(readMatrixSetsFromCapabilities(doc)).toEqual([ + { + crs: 'EPSG:3857', + identifier: 'PM', + tileMatrices: expect.arrayContaining([ + { + identifier: '0', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 559082264.0287179, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508, 20037508], + }, + { + identifier: '1', + matrixHeight: 2, + matrixWidth: 2, + scaleDenominator: 279541132.0143589, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508, 20037508], + }, + { + identifier: '2', + matrixHeight: 4, + matrixWidth: 4, + scaleDenominator: 139770566.0071794, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508, 20037508], + }, + ]), + }, + { + crs: 'EPSG:3857', + identifier: 'Prefixed', + tileMatrices: [ + { + identifier: '0', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 559082264.0287179, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508, 20037508], + }, + { + identifier: '1', + matrixHeight: 2, + matrixWidth: 2, + scaleDenominator: 279541132.0143589, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508, 20037508], + }, + ], + }, + ]); + }); + }); + }); + + describe('readLayersFromCapabilities', () => { + describe('ogcsample.xml', () => { + it('parses the layers', () => { + const doc = parseXmlString(ogcsample); + expect(readLayersFromCapabilities(doc)).toEqual([ + { + name: 'BlueMarbleNextGeneration', + title: 'Blue Marble Next Generation', + abstract: 'Blue Marble Next Generation NASA Product', + latLonBoundingBox: [-180, -90, 180, 90], + styles: [ + { + name: 'DarkBlue', + title: 'Dark Blue', + legendUrl: + 'http://www.miramon.uab.es/wmts/Coastlines/coastlines_darkBlue.png', + }, + { + name: 'thickAndRed', + title: 'Thick And Red', + }, + ], + defaultStyle: 'DarkBlue', + matrixSets: [ + { + identifier: 'BigWorldPixel', + crs: 'urn:ogc:def:crs:EPSG:6.18:3:3857', + limits: [], + }, + { + identifier: 'google3857', + crs: 'urn:ogc:def:crs:EPSG:6.18:3:3857', + limits: [], + }, + { + identifier: 'google3857subset', + crs: 'urn:ogc:def:crs:EPSG:6.18:3:3857', + limits: [], + }, + ], + resourceUrls: [ + { + encoding: 'REST', + format: 'image/png', + url: 'http://www.example.com/wmts/coastlines/{TileMatrix}/{TileRow}/{TileCol}.png', + }, + { + encoding: 'KVP', + format: 'image/jpeg', + url: 'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?', + }, + { + encoding: 'KVP', + format: 'image/gif', + url: 'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?', + }, + ], + dimensions: [ + { + defaultValue: '20110805', + identifier: 'Time', + values: [], + }, + { + defaultValue: 'abcd', + identifier: 'OtherDimension', + values: [], + }, + ], + }, + ]); + }); + }); + describe('arcgis.xml', () => { + it('parses the layers', () => { + const doc = parseXmlString(arcgis); + expect(readLayersFromCapabilities(doc)).toEqual([ + { + abstract: '', + defaultStyle: 'default', + dimensions: [], + latLonBoundingBox: [ + -178.2278219969978, 18.910787002877576, -66.95000499993604, + 71.38957425051252, + ], + matrixSets: [ + { + identifier: 'default028mm', + crs: 'urn:ogc:def:crs:EPSG::3857', + limits: [], + }, + { + identifier: 'GoogleMapsCompatible', + crs: 'urn:ogc:def:crs:EPSG::3857', + limits: [], + }, + ], + name: 'Demographics_USA_Population_Density', + styles: [ + { + name: 'default', + title: 'Default Style', + }, + ], + title: 'Demographics_USA_Population_Density', + resourceUrls: [ + { + encoding: 'REST', + format: 'image/png', + url: 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS/tile/1.0.0/Demographics_USA_Population_Density/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png', + }, + { + encoding: 'REST', + format: 'image/jpeg', + url: 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS/tile/1.0.0/Demographics_USA_Population_Density/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpeg', + }, + { + encoding: 'KVP', + format: 'image/png', + url: 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS?', + }, + ], + }, + ]); + }); + }); + describe('ign.xml', () => { + it('parses the layers', () => { + const doc = parseXmlString(ign); + expect(readLayersFromCapabilities(doc)).toEqual([ + { + abstract: 'Photographies aériennes', + defaultStyle: 'normal', + dimensions: [], + latLonBoundingBox: [-180, -86, 180, 84], + matrixSets: [ + { + identifier: 'PM', + crs: 'EPSG:3857', + limits: expect.arrayContaining([ + { + maxTileCol: 1, + maxTileRow: 1, + minTileCol: 0, + minTileRow: 0, + tileMatrix: '0', + }, + { + maxTileCol: 2, + maxTileRow: 2, + minTileCol: 0, + minTileRow: 0, + tileMatrix: '1', + }, + { + maxTileCol: 4, + maxTileRow: 4, + minTileCol: 0, + minTileRow: 0, + tileMatrix: '2', + }, + ]), + }, + { + identifier: 'Prefixed', + crs: 'EPSG:3857', + limits: [ + { + maxTileCol: 1, + maxTileRow: 1, + minTileCol: 0, + minTileRow: 0, + tileMatrix: 'Prefixed:0', + }, + { + maxTileCol: 2, + maxTileRow: 2, + minTileCol: 0, + minTileRow: 0, + tileMatrix: 'Prefixed:1', + }, + ], + }, + ], + name: 'ORTHOIMAGERY.ORTHOPHOTOS', + resourceUrls: [ + { + encoding: 'KVP', + format: 'image/jpeg', + url: 'http://wxs.ign.fr/geoportail/wmts?', + }, + ], + styles: [ + { + legendUrl: 'http://www.geoportail.gouv.fr/depot/LEGEND.jpg', + name: 'normal', + title: 'Données Brutes', + }, + ], + title: 'Photographies aériennes', + }, + ]); + }); + }); + }); +}); diff --git a/src/wmts/capabilities.ts b/src/wmts/capabilities.ts new file mode 100644 index 0000000..94d4d56 --- /dev/null +++ b/src/wmts/capabilities.ts @@ -0,0 +1,235 @@ +import { XmlDocument, XmlElement } from '@rgrove/parse-xml'; +import { BoundingBox } from '../shared/models'; +import { + findChildElement, + findChildrenElement, + getElementAttribute, + getElementText, + getRootElement, +} from '../shared/xml-utils'; +import { + LayerResourceUrl, + LayerStyle, + MatrixSetLink, + TileMatrix, + WmtsEndpointInfo, + WmtsLayer, + WmtsMatrixSet, +} from './model'; + +function parseBBox(xmlElement: XmlElement): BoundingBox { + const result = ['LowerCorner', 'UpperCorner'] + .map((elName) => findChildElement(xmlElement, elName)) + .map((cornerEl) => getElementText(cornerEl).split(' ')) + .reduce((prev, curr) => [...prev, ...curr]) + .map(parseFloat) as BoundingBox; + if (result.some(Number.isNaN)) return null; + return result; +} + +export function readInfoFromCapabilities( + capabilitiesDoc: XmlDocument +): WmtsEndpointInfo { + const rootEl = getRootElement(capabilitiesDoc); + const service = findChildElement(rootEl, 'ServiceIdentification'); + const keywords = findChildrenElement( + findChildElement(service, 'Keywords'), + 'Keyword' + ).map(getElementText); + const metadata = findChildElement(rootEl, 'OperationsMetadata'); + const getTileOperation = findChildrenElement(metadata, 'Operation').find( + (el) => getElementAttribute(el, 'name') == 'GetTile' + ); + const getTileUrls = findChildrenElement(getTileOperation, 'Get', true).reduce( + (prev, curr) => { + const encodingType = getElementText( + findChildElement(curr, 'Value', true) + ); + const url = getElementAttribute(curr, 'xlink:href'); + if (encodingType.toLowerCase() === 'restful') + return { ...prev, rest: url }; + return { ...prev, kvp: url }; + }, + {} + ); + + return { + title: getElementText(findChildElement(service, 'Title')), + name: getElementText(findChildElement(service, 'ServiceType')), + abstract: getElementText(findChildElement(service, 'Abstract')), + fees: getElementText(findChildElement(service, 'Fees')), + constraints: getElementText(findChildElement(service, 'AccessConstraints')), + keywords, + getTileUrls, + }; +} + +export function readMatrixSetsFromCapabilities( + capabilitiesDoc: XmlDocument +): WmtsMatrixSet[] { + function parseMatrixSet(element: XmlElement): TileMatrix { + const topLeft = getElementText(findChildElement(element, 'TopLeftCorner')) + .split(' ') + .map(parseFloat) as [number, number]; + return { + identifier: getElementText(findChildElement(element, 'Identifier')), + tileWidth: parseInt( + getElementText(findChildElement(element, 'TileWidth')) + ), + tileHeight: parseInt( + getElementText(findChildElement(element, 'TileHeight')) + ), + matrixWidth: parseInt( + getElementText(findChildElement(element, 'MatrixWidth')) + ), + matrixHeight: parseInt( + getElementText(findChildElement(element, 'MatrixHeight')) + ), + scaleDenominator: parseFloat( + getElementText(findChildElement(element, 'ScaleDenominator')) + ), + topLeft, + }; + } + const contents = findChildElement( + getRootElement(capabilitiesDoc), + 'Contents' + ); + const matrixSets = findChildrenElement(contents, 'TileMatrixSet'); + return matrixSets.map((element) => { + const wellKnownScaleSet = getElementText( + findChildElement(element, 'WellKnownScaleSet') + ); + const boundingBox = parseBBox(findChildElement(element, 'BoundingBox')); + return { + identifier: getElementText(findChildElement(element, 'Identifier')), + crs: getElementText(findChildElement(element, 'SupportedCRS')), + tileMatrices: findChildrenElement(element, 'TileMatrix').map( + parseMatrixSet + ), + ...(boundingBox && { boundingBox }), + ...(wellKnownScaleSet && { wellKnownScaleSet }), + }; + }); +} + +export function readLayersFromCapabilities( + capabilitiesDoc: XmlDocument +): WmtsLayer[] { + const rootEl = getRootElement(capabilitiesDoc); + const contentsEl = findChildElement(rootEl, 'Contents'); + function parseMatrixSetLink(element: XmlElement): MatrixSetLink { + const fullMatrixSet = findChildrenElement(contentsEl, 'TileMatrixSet').find( + (el) => getElementText(findChildElement(el, 'Identifier')) + ); + return { + identifier: getElementText(findChildElement(element, 'TileMatrixSet')), + crs: getElementText(findChildElement(fullMatrixSet, 'SupportedCRS')), + limits: findChildrenElement(element, 'TileMatrixLimits', true).map( + (element) => ({ + tileMatrix: getElementText(findChildElement(element, 'TileMatrix')), + minTileRow: parseInt( + getElementText(findChildElement(element, 'MinTileRow')) + ), + minTileCol: parseInt( + getElementText(findChildElement(element, 'MinTileCol')) + ), + maxTileRow: parseInt( + getElementText(findChildElement(element, 'MaxTileRow')) + ), + maxTileCol: parseInt( + getElementText(findChildElement(element, 'MaxTileCol')) + ), + }) + ), + }; + } + const getTileOperation = findChildrenElement( + findChildElement(rootEl, 'OperationsMetadata'), + 'Operation' + ).find((el) => getElementAttribute(el, 'name') == 'GetTile'); + const getKvpElt = findChildrenElement(getTileOperation, 'Get', true).filter( + (elt) => { + const encodingType = getElementText(findChildElement(elt, 'Value', true)); + return encodingType.toLowerCase() === 'kvp'; + } + )[0]; + const getKvpUrl = getKvpElt + ? getElementAttribute(getKvpElt, 'xlink:href') + : ''; + const contents = findChildElement(rootEl, 'Contents'); + const layers = findChildrenElement(contents, 'Layer'); + return layers.map((element) => { + const latLonBoundingBox = parseBBox( + findChildElement(element, 'WGS84BoundingBox') + ); + let defaultStyle = ''; + const styles = findChildrenElement(element, 'Style').map((element) => { + const legendUrl = getElementAttribute( + findChildElement(element, 'LegendURL'), + 'xlink:href' + ); + const style: LayerStyle = { + title: getElementText(findChildElement(element, 'Title')), + name: getElementText(findChildElement(element, 'Identifier')), + ...(legendUrl && { legendUrl }), + }; + if (getElementAttribute(element, 'isDefault') === 'true') { + defaultStyle = style.name; + } + return style; + }); + const outputFormats = findChildrenElement(element, 'Format').map( + getElementText + ); + const resourceUrls: LayerResourceUrl[] = findChildrenElement( + element, + 'ResourceURL' + ) + .filter( + (element) => getElementAttribute(element, 'resourceType') === 'tile' + ) + .map((element) => { + const format = getElementAttribute(element, 'format'); + const url = getElementAttribute(element, 'template'); + return { format, url, encoding: 'REST' as const }; + }); + if (getKvpUrl) { + resourceUrls.push( + ...outputFormats.map((format) => ({ + encoding: 'KVP' as const, + url: getKvpUrl, + format, + })) + ); + } + const matrixSets = findChildrenElement(element, 'TileMatrixSetLink').map( + parseMatrixSetLink + ); + const dimensions = findChildrenElement(element, 'Dimension').map( + (element) => { + const identifier = getElementText( + findChildElement(element, 'Identifier') + ); + const defaultValue = getElementText( + findChildElement(element, 'Default') + ); + const values = findChildrenElement(element, 'Values').map( + getElementText + ); + return { identifier, defaultValue, values }; + } + ); + return { + name: getElementText(findChildElement(element, 'Identifier')), + title: getElementText(findChildElement(element, 'Title')), + abstract: getElementText(findChildElement(element, 'Abstract')), + styles, + resourceUrls, + matrixSets, + defaultStyle, + ...(latLonBoundingBox && { latLonBoundingBox }), + ...(dimensions && { dimensions }), + } as WmtsLayer; + }); +} diff --git a/src/wmts/endpoint.spec.ts b/src/wmts/endpoint.spec.ts new file mode 100644 index 0000000..ea9e8f1 --- /dev/null +++ b/src/wmts/endpoint.spec.ts @@ -0,0 +1,442 @@ +import WmtsEndpoint from './endpoint'; +import { useCache } from '../shared/cache'; +// @ts-ignore +import ogcsample from '../../fixtures/wmts/ogcsample.xml'; +// @ts-ignore +import arcgis from '../../fixtures/wmts/arcgis.xml'; +// @ts-ignore +import ign from '../../fixtures/wmts/ign.xml'; +import { buildOpenLayersTileGrid } from './ol-tilegrid'; + +jest.mock('../shared/cache', () => ({ + useCache: jest.fn((factory) => factory()), +})); + +jest.mock('./ol-tilegrid', () => ({ + buildOpenLayersTileGrid: jest.fn(() => ({ tileGrid: true })), +})); + +const global = window as any; + +describe('WmtsEndpoint', () => { + let endpoint: WmtsEndpoint; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('OGC WMTS', () => { + beforeEach(() => { + global.fetchResponseFactory = () => ogcsample; + endpoint = new WmtsEndpoint('https://my.test.service/ogc/wmts?bb=c'); + }); + + it('makes a getcapabilities request', async () => { + await endpoint.isReady(); + expect(global.fetch).toHaveBeenCalledWith( + 'https://my.test.service/ogc/wmts?bb=c&SERVICE=WMTS&REQUEST=GetCapabilities', + { method: 'GET' } + ); + }); + + describe('caching', () => { + beforeEach(async () => { + await endpoint.isReady(); + }); + it('uses cache once', () => { + expect(useCache).toHaveBeenCalledTimes(1); + }); + it('stores the parsed capabilities in cache', async () => { + await expect( + (useCache as any).mock.results[0].value + ).resolves.toMatchObject({ + info: { + title: 'Web Map Tile Service', + }, + }); + }); + }); + + describe('#isReady', () => { + it('resolves with the endpoint object', async () => { + await expect(endpoint.isReady()).resolves.toEqual(endpoint); + }); + }); + + describe('#getLayers', () => { + it('returns a list of layers', async () => { + await endpoint.isReady(); + expect(endpoint.getLayers()).toEqual([ + expect.objectContaining({ + name: 'BlueMarbleNextGeneration', + title: 'Blue Marble Next Generation', + }), + ]); + }); + }); + + describe('#getLayerByName', () => { + it('returns a layer', async () => { + await endpoint.isReady(); + expect( + endpoint.getLayerByName('BlueMarbleNextGeneration') + ).toMatchObject({ + abstract: 'Blue Marble Next Generation NASA Product', + name: 'BlueMarbleNextGeneration', + defaultStyle: 'DarkBlue', + dimensions: [ + { + defaultValue: '20110805', + identifier: 'Time', + values: [], + }, + { + defaultValue: 'abcd', + identifier: 'OtherDimension', + values: [], + }, + ], + latLonBoundingBox: [-180, -90, 180, 90], + }); + }); + }); + + describe('#getLayerResourceUrl', () => { + it('returns a layer resource url without type hint', async () => { + await endpoint.isReady(); + expect( + endpoint.getLayerResourceUrl('BlueMarbleNextGeneration') + ).toEqual({ + encoding: 'REST', + format: 'image/png', + url: 'http://www.example.com/wmts/coastlines/{TileMatrix}/{TileRow}/{TileCol}.png', + }); + }); + it('returns a layer resource url with type hint', async () => { + await endpoint.isReady(); + expect( + endpoint.getLayerResourceUrl('BlueMarbleNextGeneration', 'image/jpeg') + ).toEqual({ + encoding: 'KVP', + format: 'image/jpeg', + url: 'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?', + }); + }); + }); + + describe('#getDefaultDimensions', () => { + it('returns a specific matrix set', async () => { + await endpoint.isReady(); + expect( + endpoint.getDefaultDimensions('BlueMarbleNextGeneration') + ).toEqual({ OtherDimension: 'abcd', Time: '20110805' }); + }); + }); + + describe('#getOpenLayersTileGrid', () => { + it('returns a tile grid using the first matrix set', async () => { + await endpoint.isReady(); + const tileGrid = await endpoint.getOpenLayersTileGrid( + 'BlueMarbleNextGeneration' + ); + expect(buildOpenLayersTileGrid).toHaveBeenCalledWith( + { + crs: 'urn:ogc:def:crs:OGC:1.3:CRS84', + identifier: 'BigWorldPixel', + tileMatrices: expect.arrayContaining([ + { + identifier: '10000m', + matrixHeight: 5, + matrixWidth: 7, + scaleDenominator: 33130800.83133142, + tileHeight: 480, + tileWidth: 640, + topLeft: [-180, 90], + }, + ]), + wellKnownScaleSet: 'urn:ogc:def:wkss:OGC:1.0:GlobalCRS84Pixel', + }, + [] + ); + expect(tileGrid).toEqual({ tileGrid: true }); + }); + it('returns a tile grid using a specific matrix set', async () => { + await endpoint.isReady(); + await endpoint.getOpenLayersTileGrid( + 'BlueMarbleNextGeneration', + 'google3857' + ); + expect(buildOpenLayersTileGrid).toHaveBeenCalledWith( + { + boundingBox: [ + 1799448.394855, 6124949.74777, 1848250.442089, 6162571.828177, + ], + crs: 'urn:ogc:def:crs:EPSG:6.18:3:3857', + identifier: 'google3857', + tileMatrices: expect.arrayContaining([ + { + identifier: '0', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 559082264.029, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + ]), + wellKnownScaleSet: 'urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible', + }, + [] + ); + }); + }); + + describe('#getMatrixSets', () => { + it('returns a list of matrix sets', async () => { + await endpoint.isReady(); + expect(endpoint.getMatrixSets()).toEqual([ + expect.objectContaining({ + identifier: 'google3857', + }), + expect.objectContaining({ + identifier: 'BigWorldPixel', + }), + expect.objectContaining({ + identifier: 'BigWorld', + }), + expect.objectContaining({ + identifier: 'google3857subset', + }), + ]); + }); + }); + + describe('#getMatrixSetByIdentifier', () => { + it('returns a specific matrix set', async () => { + await endpoint.isReady(); + // TODO + }); + }); + + describe('#getServiceInfo', () => { + it('returns service info', async () => { + await endpoint.isReady(); + expect(endpoint.getServiceInfo()).toEqual({ + abstract: + 'Service that contrains the map\n access interface to some TileMatrixSets\n ', + constraints: 'none', + fees: 'none', + keywords: ['tile', 'tile matrix set', 'map'], + name: 'OGC WMTS', + title: 'Web Map Tile Service', + getTileUrls: { + kvp: 'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?', + }, + }); + }); + }); + + describe('#getTileUrl', () => { + it('returns a full tile url (KVP)', async () => { + await endpoint.isReady(); + expect( + endpoint.getTileUrl( + 'BlueMarbleNextGeneration', + 'DarkBlue', + 'BigWorldPixel', + '3', + 2, + 1, + 'image/gif' + ) + ).toEqual( + 'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?layer=BlueMarbleNextGeneration&style=DarkBlue&tilematrixset=BigWorldPixel&Service=WMTS&Request=GetTile&Format=image%2Fgif&TileMatrix=3&TileCol=1&TileRow=2' + ); + }); + it('returns a full tile url (REST)', async () => { + await endpoint.isReady(); + expect( + endpoint.getTileUrl( + 'BlueMarbleNextGeneration', + 'thickAndRed', + 'google3857subset', + '18', + 2, + 3 + ) + ).toBe('http://www.example.com/wmts/coastlines/18/2/3.png'); + }); + }); + }); + + describe('ArcGIS WMTS', () => { + beforeEach(() => { + global.fetchResponseFactory = () => arcgis; + endpoint = new WmtsEndpoint('https://my.test.service/ogc/wmts?bb=c'); + }); + + describe('#getLayers', () => { + it('returns a list of layers', async () => { + await endpoint.isReady(); + expect(endpoint.getLayers()).toEqual([ + expect.objectContaining({ + name: 'Demographics_USA_Population_Density', + title: 'Demographics_USA_Population_Density', + }), + ]); + }); + }); + + describe('#getLayerByName', () => { + it('returns a layer', async () => { + await endpoint.isReady(); + expect( + endpoint.getLayerByName('Demographics_USA_Population_Density') + ).toMatchObject({ + abstract: '', + name: 'Demographics_USA_Population_Density', + defaultStyle: 'default', + dimensions: [], + latLonBoundingBox: [ + -178.2278219969978, 18.910787002877576, -66.95000499993604, + 71.38957425051252, + ], + }); + }); + }); + + describe('#getMatrixSets', () => { + it('returns a list of matrix sets', async () => { + await endpoint.isReady(); + expect(endpoint.getMatrixSets()).toEqual([ + expect.objectContaining({ + identifier: 'default028mm', + }), + expect.objectContaining({ + identifier: 'GoogleMapsCompatible', + }), + ]); + }); + }); + + describe('#getServiceInfo', () => { + it('returns service info', async () => { + await endpoint.isReady(); + expect(endpoint.getServiceInfo()).toEqual({ + abstract: '', + constraints: '', + fees: '', + getTileUrls: { + kvp: 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS?', + rest: 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS/tile/1.0.0/', + }, + keywords: [], + name: 'OGC WMTS', + title: 'Demographics_USA_Population_Density', + }); + }); + }); + + describe('#getTileUrl', () => { + it('returns a full tile url (REST)', async () => { + await endpoint.isReady(); + expect( + endpoint.getTileUrl( + 'Demographics_USA_Population_Density', + 'default', + 'default028mm', + '3', + 2, + 1 + ) + ).toEqual( + 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS/tile/1.0.0/Demographics_USA_Population_Density/default/default028mm/3/2/1.png' + ); + }); + it('returns a full tile url with type hint (REST)', async () => { + await endpoint.isReady(); + expect( + endpoint.getTileUrl( + 'Demographics_USA_Population_Density', + 'default', + 'default028mm', + '3', + 2, + 1, + 'image/jpeg' + ) + ).toBe( + 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS/tile/1.0.0/Demographics_USA_Population_Density/default/default028mm/3/2/1.jpeg' + ); + }); + }); + }); + + describe('IGN WMTS', () => { + beforeEach(() => { + global.fetchResponseFactory = () => ign; + endpoint = new WmtsEndpoint('https://my.test.service/ogc/wmts?bb=c'); + }); + + describe('#getOpenLayersTileGrid', () => { + it('returns a tile grid including tile limits', async () => { + await endpoint.isReady(); + await endpoint.getOpenLayersTileGrid('ORTHOIMAGERY.ORTHOPHOTOS'); + expect(buildOpenLayersTileGrid).toHaveBeenCalledWith( + { + crs: 'EPSG:3857', + identifier: 'PM', + tileMatrices: expect.arrayContaining([ + { + identifier: '0', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 559082264.0287179, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508, 20037508], + }, + ]), + }, + expect.arrayContaining([ + { + maxTileCol: 1, + maxTileRow: 1, + minTileCol: 0, + minTileRow: 0, + tileMatrix: '0', + }, + { + maxTileCol: 2, + maxTileRow: 2, + minTileCol: 0, + minTileRow: 0, + tileMatrix: '1', + }, + { + maxTileCol: 1024, + maxTileRow: 1024, + minTileCol: 0, + minTileRow: 31, + tileMatrix: '10', + }, + { + maxTileCol: 2048, + maxTileRow: 2048, + minTileCol: 0, + minTileRow: 62, + tileMatrix: '11', + }, + { + maxTileCol: 4096, + maxTileRow: 4096, + minTileCol: 0, + minTileRow: 125, + tileMatrix: '12', + }, + ]) + ); + }); + }); + }); +}); diff --git a/src/wmts/endpoint.ts b/src/wmts/endpoint.ts new file mode 100644 index 0000000..bf7ca3e --- /dev/null +++ b/src/wmts/endpoint.ts @@ -0,0 +1,188 @@ +import { MimeType } from '../shared/models'; +import { setQueryParams } from '../shared/http-utils'; +import { useCache } from '../shared/cache'; +import { parseWmtsCapabilities } from '../worker'; +import { + LayerDimensionValue, + LayerResourceUrl, + WmtsEndpointInfo, + WmtsLayer, + WmtsMatrixSet, +} from './model'; +import { generateGetTileUrl } from './url'; +import type WMTSTileGrid from 'ol/tilegrid/WMTS'; + +/** + * Represents a WMTS endpoint advertising several layers. + */ +export default class WmtsEndpoint { + private _capabilitiesPromise: Promise; + private _info: WmtsEndpointInfo = null; + private _layers: WmtsLayer[] = null; + private _matrixSets: WmtsMatrixSet[] = null; + + /** + * @param url WMTS endpoint url; can contain any query parameters, these will be used to + * initialize the endpoint + */ + constructor(url: string) { + const capabilitiesUrl = setQueryParams(url, { + SERVICE: 'WMTS', + REQUEST: 'GetCapabilities', + }); + + /** + * This fetches the capabilities doc and parses its contents + */ + this._capabilitiesPromise = useCache( + () => parseWmtsCapabilities(capabilitiesUrl), + 'WMTS', + 'CAPABILITIES', + capabilitiesUrl + ).then(({ info, layers, matrixSets }) => { + this._info = info; + this._layers = layers; + this._matrixSets = matrixSets; + }); + } + + /** + * @throws {EndpointError} + */ + isReady() { + return this._capabilitiesPromise.then(() => this); + } + + getServiceInfo() { + return this._info; + } + + getLayers() { + return this._layers; + } + + getMatrixSets() { + return this._matrixSets; + } + + /** + * Returns a matrix set by identifier + * @param identifier Matrix set identifier + * @return return null if matrix set was not found + */ + getMatrixSetByIdentifier(identifier: string): WmtsMatrixSet { + if (!this._matrixSets) return null; + return ( + this._matrixSets.find( + (matrixSet) => matrixSet.identifier === identifier + ) ?? null + ); + } + + /** + * Returns a complete layer based on its name + * Note: the first matching layer will be returned + * @param name Layer name property + * @return return null if layer was not found + */ + getLayerByName(name: string): WmtsLayer { + if (!this._layers) return null; + return this._layers.find((layer) => layer.name === name) ?? null; + } + + getLayerResourceUrl( + layerName: string, + formatHint?: MimeType + ): LayerResourceUrl { + if (!this._layers) return null; + const layer = this.getLayerByName(layerName); + let resourceUrlIndex = 0; + if (formatHint) { + resourceUrlIndex = + layer.resourceUrls.findIndex( + (resourceUrl) => resourceUrl.format === formatHint + ) || 0; + } + const resourceUrl = layer.resourceUrls[resourceUrlIndex]; + if (formatHint && resourceUrl.format !== formatHint) { + console.warn( + `[ogc-client] Requested '${formatHint}' format for the WMTS layer but it is not available in REST encoding, falling back to '${resourceUrl.format}'` + ); + } + return resourceUrl; + } + + /** + * Generates a URL for a specific tile of a specific layer + */ + getTileUrl( + layerName: string, + styleName: string, + matrixSetName: string, + tileMatrix: string, + tileRow: number, + tileCol: number, + outputFormat?: MimeType + ): string { + if (!this._layers) return null; + const resourceUrl = this.getLayerResourceUrl(layerName, outputFormat); + return generateGetTileUrl( + resourceUrl.url, + resourceUrl.encoding, + layerName, + styleName, + matrixSetName, + tileMatrix, + tileRow, + tileCol, + resourceUrl.format + ); + } + + /** + * Return an object with all defined dimensions for the layer, as well as their default values. + * @param layerName + */ + getDefaultDimensions(layerName: string): Record { + if (!this._layers) return null; + const layer = this.getLayerByName(layerName); + if (!layer.dimensions) return {}; + return layer.dimensions.reduce( + (prev, curr) => ({ ...prev, [curr.identifier]: curr.defaultValue }), + {} + ); + } + + tileGridModule: Promise; + + /** + * Creates a WMTSTileGrid instance from the 'ol' package, for a given layer. Optionally, a matrix set + * can be provided; + * @param layerName + * @param matrixSetIdentifier + */ + getOpenLayersTileGrid( + layerName: string, + matrixSetIdentifier?: string + ): Promise { + if (!this._layers) return null; + if (!this.tileGridModule) { + this.tileGridModule = import('./ol-tilegrid').catch((e) => { + console.warn( + `[ogc-client] Cannot use getOpenLayersTileGrid, the 'ol' package is probably not available.\n`, + e + ); + return null; + }); + } + const layer = this.getLayerByName(layerName); + const matrixSetLink = + layer.matrixSets.find( + (matrixSet) => matrixSet.identifier === matrixSetIdentifier + ) ?? layer.matrixSets[0]; + const matrixSet = this.getMatrixSetByIdentifier(matrixSetLink.identifier); + return this.tileGridModule.then(({ buildOpenLayersTileGrid }) => + buildOpenLayersTileGrid(matrixSet, matrixSetLink.limits) + ); + } +} diff --git a/src/wmts/model.ts b/src/wmts/model.ts new file mode 100644 index 0000000..443f5cc --- /dev/null +++ b/src/wmts/model.ts @@ -0,0 +1,103 @@ +import { + BoundingBox, + CrsCode, + GenericEndpointInfo, + MimeType, +} from '../shared/models'; + +export interface WmtsEndpointInfo extends GenericEndpointInfo { + getTileUrls: { + kvp?: string; + rest?: string; + }; +} + +export interface TileMatrix { + identifier: string; + scaleDenominator: number; + resolution?: number; // FOR OL??? or computeResolution? + /** + * coordinates of the top left origin of the tile matrix + */ + topLeft: [number, number]; + /** + * width in pixels + */ + tileWidth: number; + /** + * height in pixels + */ + tileHeight: number; + /** + * horizontal tile count + */ + matrixWidth: number; + /** + * vertical tile count + */ + matrixHeight: number; +} + +export interface WmtsMatrixSet { + identifier: string; + wellKnownScaleSet?: string; // from fixed list? + crs: CrsCode; + boundingBox?: BoundingBox; + tileMatrices: TileMatrix[]; +} + +export interface LayerStyle { + name: string; + title: string; + legendUrl?: string; +} + +export interface MatrixSetLink { + identifier: string; + crs: string; + limits: MatrixSetLimit[]; +} + +export interface MatrixSetLimit { + tileMatrix: string; + minTileRow: number; + maxTileRow: number; + minTileCol: number; + maxTileCol: number; +} + +export interface LayerResourceUrl { + url: string; + encoding: WmtsRequestEncoding; + format: MimeType; +} + +export type LayerDimensionValue = string; + +export interface LayerDimension { + identifier: string; + defaultValue: LayerDimensionValue; + values: LayerDimensionValue[]; +} + +export interface WmtsLayer { + name: string; + resourceUrls: LayerResourceUrl[]; + styles: LayerStyle[]; + defaultStyle: string; + matrixSets: MatrixSetLink[]; + latLonBoundingBox?: BoundingBox; + dimensions?: LayerDimension[]; +} + +export interface WmtsTileGrid { + minZoom: number; + /** + * for these arrays the index is the zoom value, so items with an index lower than minZoom are undefined + */ + origins: [number, number][]; + sizes: [number, number][]; + tileSizes: [number, number][]; +} + +export type WmtsRequestEncoding = 'KVP' | 'REST'; diff --git a/src/wmts/ol-tilegrid.spec.ts b/src/wmts/ol-tilegrid.spec.ts new file mode 100644 index 0000000..d0335c2 --- /dev/null +++ b/src/wmts/ol-tilegrid.spec.ts @@ -0,0 +1,205 @@ +import { createFromCapabilitiesMatrixSet } from 'ol/tilegrid/WMTS'; +import { buildOpenLayersTileGrid } from './ol-tilegrid'; + +describe('buildOpenLayersTileGrid', () => { + it('calls the OL function accordingly', () => { + buildOpenLayersTileGrid( + { + crs: 'EPSG:3857', + identifier: 'PM', + tileMatrices: [ + { + identifier: '0', + matrixHeight: 1, + matrixWidth: 1, + scaleDenominator: 559082264.029, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '1', + matrixHeight: 2, + matrixWidth: 2, + scaleDenominator: 279541132.015, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '2', + matrixHeight: 4, + matrixWidth: 4, + scaleDenominator: 139770566.007, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '3', + matrixHeight: 8, + matrixWidth: 8, + scaleDenominator: 69885283.0036, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '4', + matrixHeight: 16, + matrixWidth: 16, + scaleDenominator: 34942641.5018, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '5', + matrixHeight: 32, + matrixWidth: 32, + scaleDenominator: 17471320.7509, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + { + identifier: '6', + matrixHeight: 64, + matrixWidth: 64, + scaleDenominator: 8735660.37545, + tileHeight: 256, + tileWidth: 256, + topLeft: [-20037508.3428, 20037508.3428], + }, + ], + }, + [ + { + maxTileCol: 1, + maxTileRow: 1, + minTileCol: 0, + minTileRow: 0, + tileMatrix: '0', + }, + { + maxTileCol: 2, + maxTileRow: 2, + minTileCol: 0, + minTileRow: 0, + tileMatrix: '1', + }, + { + maxTileCol: 1024, + maxTileRow: 1024, + minTileCol: 0, + minTileRow: 31, + tileMatrix: '10', + }, + { + maxTileCol: 2048, + maxTileRow: 2048, + minTileCol: 0, + minTileRow: 62, + tileMatrix: '11', + }, + { + maxTileCol: 4096, + maxTileRow: 4096, + minTileCol: 0, + minTileRow: 125, + tileMatrix: '12', + }, + ] + ); + expect(createFromCapabilitiesMatrixSet).toHaveBeenCalledWith( + { + SupportedCRS: { + code: '3857', + }, + TileMatrix: [ + { + Identifier: '0', + MatrixHeight: 1, + MatrixWidth: 1, + ScaleDenominator: 559082264.029, + TileHeight: 256, + TileWidth: 256, + TopLeftCorner: [-20037508.3428, 20037508.3428], + }, + { + Identifier: '1', + MatrixHeight: 2, + MatrixWidth: 2, + ScaleDenominator: 279541132.015, + TileHeight: 256, + TileWidth: 256, + TopLeftCorner: [-20037508.3428, 20037508.3428], + }, + { + Identifier: '2', + MatrixHeight: 4, + MatrixWidth: 4, + ScaleDenominator: 139770566.007, + TileHeight: 256, + TileWidth: 256, + TopLeftCorner: [-20037508.3428, 20037508.3428], + }, + { + Identifier: '3', + MatrixHeight: 8, + MatrixWidth: 8, + ScaleDenominator: 69885283.0036, + TileHeight: 256, + TileWidth: 256, + TopLeftCorner: [-20037508.3428, 20037508.3428], + }, + { + Identifier: '4', + MatrixHeight: 16, + MatrixWidth: 16, + ScaleDenominator: 34942641.5018, + TileHeight: 256, + TileWidth: 256, + TopLeftCorner: [-20037508.3428, 20037508.3428], + }, + { + Identifier: '5', + MatrixHeight: 32, + MatrixWidth: 32, + ScaleDenominator: 17471320.7509, + TileHeight: 256, + TileWidth: 256, + TopLeftCorner: [-20037508.3428, 20037508.3428], + }, + { + Identifier: '6', + MatrixHeight: 64, + MatrixWidth: 64, + ScaleDenominator: 8735660.37545, + TileHeight: 256, + TileWidth: 256, + TopLeftCorner: [-20037508.3428, 20037508.3428], + }, + ], + }, + null, + [ + { + TileMatrix: '0', + }, + { + TileMatrix: '1', + }, + { + TileMatrix: '10', + }, + { + TileMatrix: '11', + }, + { + TileMatrix: '12', + }, + ] + ); + }); +}); diff --git a/src/wmts/ol-tilegrid.ts b/src/wmts/ol-tilegrid.ts new file mode 100644 index 0000000..0a36463 --- /dev/null +++ b/src/wmts/ol-tilegrid.ts @@ -0,0 +1,41 @@ +import { MatrixSetLimit, WmtsMatrixSet } from './model'; +import WMTSTileGrid, { + createFromCapabilitiesMatrixSet, +} from 'ol/tilegrid/WMTS'; +import { get as getProjection } from 'ol/proj'; +import { fromEPSGCode, register } from 'ol/proj/proj4'; +import proj4 from 'proj4'; + +register(proj4); + +export async function buildOpenLayersTileGrid( + matrixSet: WmtsMatrixSet, + limits: MatrixSetLimit[] +): Promise { + // if the matrix set crs is not known, load it + let projection = getProjection(matrixSet.crs); + if (!projection) { + projection = await fromEPSGCode(matrixSet.crs); + } + if (!projection) { + throw new Error( + `[ogc-client] could not create OpenLayers tile grid, the following projection is unknown: ${matrixSet.crs}` + ); + } + const matrixSetInfo = { + SupportedCRS: projection, + TileMatrix: matrixSet.tileMatrices.map((tileMatrix) => ({ + Identifier: tileMatrix.identifier, + ScaleDenominator: tileMatrix.scaleDenominator, + TopLeftCorner: tileMatrix.topLeft, + TileWidth: tileMatrix.tileWidth, + TileHeight: tileMatrix.tileHeight, + MatrixWidth: tileMatrix.matrixWidth, + MatrixHeight: tileMatrix.matrixHeight, + })), + }; + const matrixSetLimits = limits.map((limit) => ({ + TileMatrix: limit.tileMatrix, + })); + return createFromCapabilitiesMatrixSet(matrixSetInfo, null, matrixSetLimits); +} diff --git a/src/wmts/url.spec.ts b/src/wmts/url.spec.ts new file mode 100644 index 0000000..7238a74 --- /dev/null +++ b/src/wmts/url.spec.ts @@ -0,0 +1,40 @@ +import { generateGetTileUrl } from './url'; + +describe('URL utils', () => { + describe('generateGetTileUrl', () => { + it('generates URL with KVP encoding', () => { + const url = generateGetTileUrl( + 'http://my.service.org/wmts', + 'KVP', + 'myLayer', + 'myStyle', + 'webMercator', + 'zoom:3', + 4, + 5, + 'image/png' + ); + expect(url).toBe( + 'http://my.service.org/wmts?layer=myLayer&style=myStyle&tilematrixset=webMercator&Service=WMTS&Request=GetTile&Format=image%2Fpng&TileMatrix=zoom%3A3&TileCol=5&TileRow=4' + ); + }); + }); + describe('generateGetTileUrl', () => { + it('generates URL with KVP encoding', () => { + const url = generateGetTileUrl( + 'http://my.service.org/wmts/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png', + 'REST', + 'myLayer', + 'myStyle', + 'webMercator', + 'zoom:3', + 4, + 5, + 'image/png' + ); + expect(url).toBe( + 'http://my.service.org/wmts/myStyle/webMercator/zoom:3/4/5.png' + ); + }); + }); +}); diff --git a/src/wmts/url.ts b/src/wmts/url.ts new file mode 100644 index 0000000..e93d3b4 --- /dev/null +++ b/src/wmts/url.ts @@ -0,0 +1,36 @@ +import { WmtsRequestEncoding } from './model'; +import { MimeType } from '../shared/models'; +import { setQueryParams } from '../shared/http-utils'; + +export function generateGetTileUrl( + baseUrl: string, + requestEncoding: WmtsRequestEncoding, + layerName: string, + styleName: string, + matrixSetName: string, + tileMatrix: string, + tileRow: number, + tileCol: number, + outputFormat: MimeType +): string { + const context = { + layer: layerName, + style: styleName, + tilematrixset: matrixSetName, + Service: 'WMTS', + Request: 'GetTile', + Format: outputFormat, + TileMatrix: tileMatrix, + TileCol: tileCol.toString(), + TileRow: tileRow.toString(), + }; + if (requestEncoding === 'REST') { + let url = baseUrl; + for (const key in context) { + url = url.replace(new RegExp(`{${key}}`, 'ig'), context[key]); + } + return url; + } else { + return setQueryParams(baseUrl, context); + } +} diff --git a/src/worker/index.ts b/src/worker/index.ts index 1baa444..0bbe618 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -8,6 +8,7 @@ import { import { GenericEndpointInfo } from '../shared/models'; import { WmsLayerFull, WmsVersion } from '../wms/endpoint'; import { setFetchOptionsUpdateCallback } from '../shared/http-utils'; +import { WmtsEndpointInfo, WmtsLayer, WmtsMatrixSet } from '../wmts/model'; let fallbackWithoutWorker = false; @@ -81,6 +82,20 @@ export function queryWfsFeatureTypeDetails( }); } +/** + * Parses the capabilities document and return all relevant information + * @param capabilitiesUrl This url should point to the capabilities document + */ +export function parseWmtsCapabilities(capabilitiesUrl: string): Promise<{ + info: WmtsEndpointInfo; + layers: WmtsLayer[]; + matrixSets: WmtsMatrixSet[]; +}> { + return sendTaskRequest('parseWmtsCapabilities', getWorkerInstance(), { + url: capabilitiesUrl, + }); +} + setFetchOptionsUpdateCallback((options) => { const worker = getWorkerInstance(); if (!worker) return; diff --git a/src/worker/worker.ts b/src/worker/worker.ts index 628f890..e0e24d1 100644 --- a/src/worker/worker.ts +++ b/src/worker/worker.ts @@ -2,6 +2,7 @@ import { addTaskHandler } from './utils'; import { queryXmlDocument, setFetchOptions } from '../shared/http-utils'; import * as wmsCapabilities from '../wms/capabilities'; import * as wfsCapabilities from '../wfs/capabilities'; +import * as wmtsCapabilities from '../wmts/capabilities'; import { computeFeaturePropsDetails, parseFeatureProps, @@ -62,3 +63,14 @@ addTaskHandler( return Promise.resolve({}); } ); + +addTaskHandler( + 'parseWmtsCapabilities', + globalThis, + ({ url }: { url: string }) => + queryXmlDocument(url).then((xmlDoc) => ({ + info: wmtsCapabilities.readInfoFromCapabilities(xmlDoc), + layers: wmtsCapabilities.readLayersFromCapabilities(xmlDoc), + matrixSets: wmtsCapabilities.readMatrixSetsFromCapabilities(xmlDoc), + })) +); From 2041f2640ce14f5ae67d682920c671c77cdfc9b8 Mon Sep 17 00:00:00 2001 From: Olivia Date: Thu, 1 Feb 2024 23:03:12 +0100 Subject: [PATCH 3/7] Add ol as peer dependency as well as dev dependency --- package-lock.json | 372 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 14 ++ 2 files changed, 386 insertions(+) diff --git a/package-lock.json b/package-lock.json index 1f98f2a..23adeaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,12 +30,26 @@ "jest-environment-jsdom": "^29.7.0", "mitt": "^3.0.0", "node-fetch": "^3.3.1", + "ol": "^8.2.0", "prettier": "2.8.8", + "proj4": "^2.10.0", "regenerator-runtime": "^0.13.11", "rollup": "^3.24.0", "ts-jest": "^29.1.1", "tslib": "^2.5.3", "typescript": "^4.9.5" + }, + "peerDependencies": { + "ol": ">5.x", + "proj4": ">2.8" + }, + "peerDependenciesMeta": { + "ol": { + "optional": true + }, + "proj4": { + "optional": true + } } }, "node_modules/@ampproject/remapping": { @@ -1699,6 +1713,12 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, + "node_modules/@petamoriken/float16": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.4.tgz", + "integrity": "sha512-kB+NJ5Br56ZhElKsf0pM7/PQfrDdDVMRz8f0JM6eVOGE+L89z9hwcst9QvWBBnazzuqGTGtPsJNZoQ1JdNiGSQ==", + "dev": true + }, "node_modules/@rgrove/parse-xml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@rgrove/parse-xml/-/parse-xml-4.1.0.tgz", @@ -2610,6 +2630,31 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/color-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-2.0.0.tgz", + "integrity": "sha512-g2Z+QnWsdHLppAbrpcFWo629kLOnOPtpxYV69GCqm92gqSgyXbzlfyN3MXs0412fPBkFmiuS+rXposgBgBa6Kg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/color-rgba": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-3.0.0.tgz", + "integrity": "sha512-PPwZYkEY3M2THEHHV6Y95sGUie77S7X8v+h1r6LSAPF3/LL2xJ8duUXSrkic31Nzc4odPwHgUbiX/XuTYzQHQg==", + "dev": true, + "dependencies": { + "color-parse": "^2.0.0", + "color-space": "^2.0.0" + } + }, + "node_modules/color-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-2.0.1.tgz", + "integrity": "sha512-nKqUYlo0vZATVOFHY810BSYjmCARrG7e5R3UE3CQlyjJTvv5kSSmPG1kzm/oDyyqjehM+lW1RnEt9It9GNa5JA==", + "dev": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2889,6 +2934,12 @@ "node": ">=12" } }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "dev": true + }, "node_modules/electron-to-chromium": { "version": "1.4.425", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.425.tgz", @@ -3240,6 +3291,25 @@ "node": ">=6.9.0" } }, + "node_modules/geotiff": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.1.1.tgz", + "integrity": "sha512-Ss6HQEhrlR2v0FmOGq88l0wa2oCmmGi6rXAMiUxR/T7Xe98evypEmyiji7lvVeVR/AXuxK0xDCWcwfWkSmOrAA==", + "dev": true, + "dependencies": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2", + "zstddec": "^0.1.0" + }, + "engines": { + "node": ">=10.19" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -5496,6 +5566,12 @@ "node": ">=6" } }, + "node_modules/lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==", + "dev": true + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -5632,6 +5708,12 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "node_modules/mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==", + "dev": true + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -5781,6 +5863,24 @@ "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", "dev": true }, + "node_modules/ol": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ol/-/ol-8.2.0.tgz", + "integrity": "sha512-/m1ddd7Jsp4Kbg+l7+ozR5aKHAZNQOBAoNZ5pM9Jvh4Etkf0WGkXr9qXd7PnhmwiC1Hnc2Toz9XjCzBBvexfXw==", + "dev": true, + "dependencies": { + "color-rgba": "^3.0.0", + "color-space": "^2.0.1", + "earcut": "^2.2.3", + "geotiff": "^2.0.7", + "pbf": "3.2.1", + "rbush": "^3.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openlayers" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5873,6 +5973,18 @@ "node": ">=6" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true + }, + "node_modules/parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==", + "dev": true + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -5936,6 +6048,19 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dev": true, + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -6025,6 +6150,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/proj4": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.10.0.tgz", + "integrity": "sha512-0eyB8h1PDoWxucnq88/EZqt7UZlvjhcfbXCcINpE7hqRN0iRPWE/4mXINGulNa/FAvK+Ie7F+l2OxH/0uKV36A==", + "dev": true, + "dependencies": { + "mgrs": "1.0.0", + "wkt-parser": "^1.3.3" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -6038,6 +6173,12 @@ "node": ">= 6" } }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "dev": true + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -6075,6 +6216,24 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, + "node_modules/quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "dev": true + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -6084,6 +6243,15 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dev": true, + "dependencies": { + "quickselect": "^2.0.0" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -6149,6 +6317,15 @@ "node": ">=8" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dev": true, + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -6788,6 +6965,12 @@ "node": ">= 8" } }, + "node_modules/web-worker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", + "integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==", + "dev": true + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -6852,6 +7035,12 @@ "node": ">= 8" } }, + "node_modules/wkt-parser": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.3.tgz", + "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==", + "dev": true + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -6960,6 +7149,12 @@ "node": ">=12" } }, + "node_modules/xml-utils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz", + "integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==", + "dev": true + }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", @@ -7019,6 +7214,12 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zstddec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", + "integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==", + "dev": true } }, "dependencies": { @@ -8168,6 +8369,12 @@ } } }, + "@petamoriken/float16": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.4.tgz", + "integrity": "sha512-kB+NJ5Br56ZhElKsf0pM7/PQfrDdDVMRz8f0JM6eVOGE+L89z9hwcst9QvWBBnazzuqGTGtPsJNZoQ1JdNiGSQ==", + "dev": true + }, "@rgrove/parse-xml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@rgrove/parse-xml/-/parse-xml-4.1.0.tgz", @@ -8819,6 +9026,31 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "color-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-2.0.0.tgz", + "integrity": "sha512-g2Z+QnWsdHLppAbrpcFWo629kLOnOPtpxYV69GCqm92gqSgyXbzlfyN3MXs0412fPBkFmiuS+rXposgBgBa6Kg==", + "dev": true, + "requires": { + "color-name": "^1.0.0" + } + }, + "color-rgba": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-3.0.0.tgz", + "integrity": "sha512-PPwZYkEY3M2THEHHV6Y95sGUie77S7X8v+h1r6LSAPF3/LL2xJ8duUXSrkic31Nzc4odPwHgUbiX/XuTYzQHQg==", + "dev": true, + "requires": { + "color-parse": "^2.0.0", + "color-space": "^2.0.0" + } + }, + "color-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-2.0.1.tgz", + "integrity": "sha512-nKqUYlo0vZATVOFHY810BSYjmCARrG7e5R3UE3CQlyjJTvv5kSSmPG1kzm/oDyyqjehM+lW1RnEt9It9GNa5JA==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -9030,6 +9262,12 @@ "webidl-conversions": "^7.0.0" } }, + "earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "dev": true + }, "electron-to-chromium": { "version": "1.4.425", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.425.tgz", @@ -9284,6 +9522,22 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, + "geotiff": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.1.1.tgz", + "integrity": "sha512-Ss6HQEhrlR2v0FmOGq88l0wa2oCmmGi6rXAMiUxR/T7Xe98evypEmyiji7lvVeVR/AXuxK0xDCWcwfWkSmOrAA==", + "dev": true, + "requires": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2", + "zstddec": "^0.1.0" + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -10951,6 +11205,12 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==", + "dev": true + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -11062,6 +11322,12 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==", + "dev": true + }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -11170,6 +11436,20 @@ "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", "dev": true }, + "ol": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ol/-/ol-8.2.0.tgz", + "integrity": "sha512-/m1ddd7Jsp4Kbg+l7+ozR5aKHAZNQOBAoNZ5pM9Jvh4Etkf0WGkXr9qXd7PnhmwiC1Hnc2Toz9XjCzBBvexfXw==", + "dev": true, + "requires": { + "color-rgba": "^3.0.0", + "color-space": "^2.0.1", + "earcut": "^2.2.3", + "geotiff": "^2.0.7", + "pbf": "3.2.1", + "rbush": "^3.0.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -11237,6 +11517,18 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true + }, + "parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==", + "dev": true + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -11282,6 +11574,16 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dev": true, + "requires": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -11340,6 +11642,16 @@ } } }, + "proj4": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.10.0.tgz", + "integrity": "sha512-0eyB8h1PDoWxucnq88/EZqt7UZlvjhcfbXCcINpE7hqRN0iRPWE/4mXINGulNa/FAvK+Ie7F+l2OxH/0uKV36A==", + "dev": true, + "requires": { + "mgrs": "1.0.0", + "wkt-parser": "^1.3.3" + } + }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -11350,6 +11662,12 @@ "sisteransi": "^1.0.5" } }, + "protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "dev": true + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -11374,6 +11692,18 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, + "quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "dev": true + }, + "quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -11383,6 +11713,15 @@ "safe-buffer": "^5.1.0" } }, + "rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dev": true, + "requires": { + "quickselect": "^2.0.0" + } + }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -11433,6 +11772,15 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dev": true, + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, "resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -11886,6 +12234,12 @@ "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", "dev": true }, + "web-worker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", + "integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==", + "dev": true + }, "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -11932,6 +12286,12 @@ "isexe": "^2.0.0" } }, + "wkt-parser": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.3.tgz", + "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==", + "dev": true + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -12004,6 +12364,12 @@ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", "dev": true }, + "xml-utils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz", + "integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==", + "dev": true + }, "xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", @@ -12048,6 +12414,12 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zstddec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", + "integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==", + "dev": true } } } diff --git a/package.json b/package.json index f1a2927..ea34477 100644 --- a/package.json +++ b/package.json @@ -33,13 +33,27 @@ "jest-environment-jsdom": "^29.7.0", "mitt": "^3.0.0", "node-fetch": "^3.3.1", + "ol": "^8.2.0", "prettier": "2.8.8", + "proj4": "^2.10.0", "regenerator-runtime": "^0.13.11", "rollup": "^3.24.0", "ts-jest": "^29.1.1", "tslib": "^2.5.3", "typescript": "^4.9.5" }, + "peerDependencies": { + "ol": ">5.x", + "proj4": ">2.8" + }, + "peerDependenciesMeta": { + "ol": { + "optional": true + }, + "proj4": { + "optional": true + } + }, "scripts": { "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest", "format:write": "prettier --write .", From 503c482bbc8f722231e728c3fca5c7ec1727f679 Mon Sep 17 00:00:00 2001 From: Olivia Date: Thu, 1 Feb 2024 23:03:26 +0100 Subject: [PATCH 4/7] Change typescript module resolution to allow imports from OL also change jest config to make tests pass --- jest.config.js | 2 +- tsconfig.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jest.config.js b/jest.config.js index cda209e..cc9c527 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ module.exports = { testEnvironment: 'jsdom', transform: { - '^.+\\.(js|ts|xml)$': [ + '^.+\\.(ts|xml)$': [ '/jest.ts-transformer.js', { isolatedModules: true, diff --git a/tsconfig.json b/tsconfig.json index 54f7959..5b35523 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "target": "ES2017", "lib": ["es2017", "dom", "webworker"], "skipLibCheck": true, - "moduleResolution": "nodenext", + "moduleResolution": "node", "esModuleInterop": true, "module": "esnext" }, From aeb39b68337476d35d08975f18c962b715ab353d Mon Sep 17 00:00:00 2001 From: Olivia Date: Thu, 1 Feb 2024 23:05:25 +0100 Subject: [PATCH 5/7] Document WMTS endpoint (and remove useless file) --- README.md | 9 +-- app/src/Docs.vue | 126 +++++++++++++++++++++++++-------------- app/src/api-utils.js | 2 + app/src/data/api.js | 87 +++++++++++++++++++++++++++ app/src/data/examples.js | 38 ------------ 5 files changed, 173 insertions(+), 89 deletions(-) delete mode 100644 app/src/data/examples.js diff --git a/README.md b/README.md index 308dd6d..571b2f6 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,9 @@ The following standards are partially implemented: - WMS - _Web Map Service_ - WFS - _Web Feature Service_ +- WMTS - _Web Map Tile Service_ - OGC API (Records and Features) -Why no WMTS support? Because [OpenLayers](https://www.github.com/openlayers/openlayers) has an incredibly thorough and well-tested WMTS capabilities parser and you should just use it. -Reimplementing it in **ogc-client** currently does not bring any significant value. - ## Why use it? 1. **ogc-client** will abstract the service version so you don't have to worry about it @@ -23,8 +21,7 @@ Reimplementing it in **ogc-client** currently does not bring any significant val 3. **ogc-client** will hide the complexity of OGC standards behind straightforward APIs 4. **ogc-client** will run heavy tasks in a worker to avoid blocking the main thread 5. **ogc-client** will keep a persistent cache of operations to minimize requests and processing -6. **ogc-client** will handle errors in a graceful way and extract relevant messages for you -7. **ogc-client** will tell you if a service is not usable for [CORS-related issues](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) +6. **ogc-client** will tell you if a service is not usable for [CORS-related issues](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) ## Instructions @@ -59,7 +56,7 @@ To start it locally, clone the repository and run the following commands: ```bash $ cd app $ npm install -$ npm run serve +$ npm start ``` The app is based on [Vue.js](https://vuejs.org/) and will showcase most features implemented in the library. diff --git a/app/src/Docs.vue b/app/src/Docs.vue index 269a287..d530956 100644 --- a/app/src/Docs.vue +++ b/app/src/Docs.vue @@ -17,8 +17,10 @@
  • - Support for WFS and - WMS protocols + Support for WFS, + WMS, + WMTS and + OGC API protocols
  • Elaborate cache system to minimize network requests
  • @@ -47,14 +49,15 @@

    -  import { WfsEndpoint } from '@camptocamp/ogc-client';
    -
    -  new WfsEndpoint("https://my.server.org/ows")
    -    .isReady()
    -    .then(
    -      (endpoint) => console.log(endpoint.getFeatureTypes())
    -      )
    -    
    +import { WfsEndpoint } from '@camptocamp/ogc-client'; + +new WfsEndpoint("https://my.server.org/ows") + .isReady() + .then( + (endpoint) => console.log(endpoint.getFeatureTypes()) + ) +

    @@ -125,23 +128,12 @@
    • - No WMTS parsing: - alternatives already exist, such as the OpenLayers - WMTS Capabilities - class (example) -
    • -
    • - No GML geometry parsing: again, + No GML geometry parsing: the alternatives do existOpenLayers GML parser + offers extensive support of the GML format
    @@ -152,13 +144,13 @@

    -  import { WmsEndpoint } from '@camptocamp/ogc-client';
    +import { WmsEndpoint } from '@camptocamp/ogc-client';
     
    -  async function readExtent() {
    -    const endpoint = await new WmsEndpoint('https://my.server.org/ows').isReady();
    -    const layer = endpoint.getLayerByName();
    -    const extent = layer.boundingBoxes['EPSG:4326'];
    -  }

    @@ -168,15 +160,15 @@

    -  import { WfsEndpoint } from '@camptocamp/ogc-client';
    -
    -  async function getFeatureUrl() {
    -    const endpoint = await new WfsEndpoint('https://my.server.org/ows').isReady();
    -    const url = endpoint.getFeatureUrl('my:featureType', {
    -      asJson: true,
    -      maxFeature: 1000
    -    });
    -  }

    @@ -186,13 +178,57 @@

    -  import { OgcApiEndpoint } from '@camptocamp/ogc-client';
    +import { OgcApiEndpoint } from '@camptocamp/ogc-client';
     
    -  async function getFirstTenRecords() {
    -    const endpoint = await new OgcApiEndpoint('https://my.server.org/main')
    -    const firstCollection = (await endpoint.recordCollections)[0];
    -    return endpoint.getCollectionItems(firstCollection, 10, 0);
    -  }
    + +

    + +
    + Add a WMTS layer to an + OpenLayers map +
    + +

    + +

    +import TileLayer from 'ol/layer/Tile';
    +import WMTS from 'ol/source/WMTS';
    +import { WmtsEndpoint } from '@camptocamp/ogc-client';
    +
    +// create the OpenLayers map
    +// ...
    +
    +async function addWmtsLayer() {
    +  const endpoint = await new WmtsEndpoint('https://my.server.org/wmts').isReady();
    +  const layer = endpoint.getLayers()[0];
    +  const matrixSet = layer.matrixSets[0];
    +  const tileGrid = await endpoint.getOpenLayersTileGrid(
    +    layer.name,
    +    matrixSet.identifier
    +  );
    +  const resourceUrl = layer.resourceUrls[0];
    +  const dimensions = endpoint.getDefaultDimensions(layer.name);
    +  const layer = new TileLayer({
    +    source: new WMTS({
    +      layer: layer.name,
    +      style: layer.defaultStyle,
    +      matrixSet: matrixSet.identifier,
    +      format: resourceUrl.format,
    +      url: resourceUrl.url,
    +      requestEncoding: resourceUrl.encoding,
    +      tileGrid,
    +      projection: matrixSet.crs,
    +      dimensions,
    +    }),
    +  });
    +  openLayersMap.addLayer(layer);
    +}

    diff --git a/app/src/api-utils.js b/app/src/api-utils.js index 157a8f7..93df5b1 100644 --- a/app/src/api-utils.js +++ b/app/src/api-utils.js @@ -27,6 +27,8 @@ export function formatTypeToString(typeObj) { switch (typeObj.type || typeObj) { case 'Array': return `${subType}[]`; + case 'Record': + return `Record`; case 'Response': return `[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response)`; case 'Promise': diff --git a/app/src/data/api.js b/app/src/data/api.js index d92605f..53b8a57 100644 --- a/app/src/data/api.js +++ b/app/src/data/api.js @@ -243,6 +243,93 @@ available layers, bounding boxes etc. Layer name is case sensitive.`, }, ], }, + { + name: 'WmtsEndpoint', + type: 'Class', + constructor: { + params: [{ name: 'url', type: 'string' }], + description: `Creates a new WMTS endpoint; wait for the \`isReady()\` promise before using the endpoint methods.`, + }, + methods: [ + { + name: 'isReady', + description: `Resolves when the endpoint is ready to use. Returns the same endpoint object for convenience.`, + params: [], + return: { type: 'Promise', subType: 'WmtsEndpoint' }, + }, + { + name: 'getServiceInfo', + description: `Returns the service info.`, + params: [], + return: { type: 'WmtsEndpointInfo' }, + }, + { + name: 'getLayers', + description: `Returns the layers advertised in the endpoint.`, + params: [], + return: { type: 'Array', subType: 'WmtsLayer' }, + }, + { + name: 'getMatrixSets', + description: `Returns the matrix sets available for that endpoint. Each matrix set contains a list of tile matrices +as well as a supported CRS.`, + params: [], + return: { type: 'Array', subType: 'WmtsMatrixSet' }, + }, + { + name: 'getLayerByName', + description: `Returns a layer object based on its name.`, + params: [{ name: 'name', type: 'string' }], + return: { type: 'WmtsLayer' }, + }, + { + name: 'getMatrixSetByIdentifier', + description: `Returns a matrix set object based on its identifier.`, + params: [{ name: 'identifier', type: 'string' }], + return: { type: 'WmtsMatrixSet' }, + }, + { + name: 'getLayerResourceUrl', + description: `Returns a layer resource info. If no type hint is specified, the first resource will be returned. A resource +info contains a URL as well as an image format and a request encoding (KVP or REST).`, + params: [ + { name: 'layerName', type: 'string' }, + { name: 'formatHint', type: 'MimeType', optional: true }, + ], + return: { type: 'LayerResourceUrl' }, + }, + { + name: 'getTileUrl', + description: `Generates a tile URL for a layer and a set of parameters.`, + params: [ + { name: 'layerName', type: 'string' }, + { name: 'styleName', type: 'string' }, + { name: 'matrixSetName', type: 'string' }, + { name: 'tileMatrix', type: 'string' }, + { name: 'tileRow', type: 'number' }, + { name: 'tileCol', type: 'number' }, + { name: 'outputFormat', type: 'MimeType' }, + ], + return: { type: 'string' }, + }, + { + name: 'getDefaultDimensions', + description: `Return an object with all defined dimensions for the layer, as well as their default values.`, + params: [{ name: 'layerName', type: 'string' }], + return: { type: 'Record', subType: 'LayerDimensionValue' }, + }, + { + name: 'getOpenLayersTileGrid', + description: `Creates a \`WMTSTileGrid\` instance from the [\`ol\` package](https://www.npmjs.com/package/ol), for a given layer. Optionally, a matrix set + can be provided. Will return \`null\` if the \`ol\` package is not present (as it is considered an optional peer dependency).`, + params: [ + { name: 'layerName', type: 'string' }, + { name: 'matrixSetIdentifier', type: 'string', optional: true }, + ], + return: { type: 'WMTSTileGrid' }, + }, + ], + }, { name: 'useCache', type: 'Function', diff --git a/app/src/data/examples.js b/app/src/data/examples.js deleted file mode 100644 index 20fafb1..0000000 --- a/app/src/data/examples.js +++ /dev/null @@ -1,38 +0,0 @@ -const EXAMPLES = [ - { - description: 'Read a WMS layer extent', - code: `import { WmsEndpoint } from '@camptocamp/ogc-client'; - -async function readExtent() { - const endpoint = await new WmsEndpoint('https://my.server.org/ows').isReady() - const layer = endpoint.getLayerByName(); - const extent = layer.boundingBoxes['EPSG:4326']; - return extent; -}`, - }, - { - description: 'Compute a WFS GetFeature url', - code: `import { WfsEndpoint } from '@camptocamp/ogc-client'; - -async function getFeatureUrl() { - const endpoint = await new WfsEndpoint('https://my.server.org/ows').isReady() - const url = endpoint.getFeatureUrl('my:featureType', { - asJson: true, - maxFeature: 1000 - }); - return url; -}`, - }, - { - description: 'Query the first 10 items of an OGC API Records collection', - code: `import { OgcApiEndpoint } from '@camptocamp/ogc-client'; - -async function getFirstTenRecords() { - const endpoint = await new OgcApiEndpoint('https://my.server.org/main') - const firstCollection = (await endpoint.recordCollections)[0]; - return endpoint.getCollectionItems(firstCollection, 10, 0); -}`, - }, -]; - -export default EXAMPLES; From 09c6e787ad6abe6453d61f42c64db8c4e17fa6df Mon Sep 17 00:00:00 2001 From: Olivia Date: Thu, 1 Feb 2024 23:06:11 +0100 Subject: [PATCH 6/7] Add WMTS example in demo tab Also improve layout of all demos --- app/package-lock.json | 336 ++++++++++++++++++- app/package.json | 2 + app/src/Demo.vue | 10 +- app/src/components/presentation/InfoList.vue | 26 +- app/src/components/wfs/WfsEndpoint.vue | 2 +- app/src/components/wms/WmsEndpoint.vue | 2 +- app/src/components/wms/WmsLayerInfo.vue | 2 +- app/src/components/wmts/WmtsEndpoint.vue | 84 +++++ app/src/components/wmts/WmtsLayerInfo.vue | 145 ++++++++ app/src/main.js | 1 + 10 files changed, 596 insertions(+), 14 deletions(-) create mode 100644 app/src/components/wmts/WmtsEndpoint.vue create mode 100644 app/src/components/wmts/WmtsLayerInfo.vue diff --git a/app/package-lock.json b/app/package-lock.json index b5ac4d9..81ca4f3 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -12,6 +12,8 @@ "marked": "^5.0.5", "marked-gfm-heading-id": "^3.1.0", "marked-mangle": "^1.1.4", + "ol": "^8.2.0", + "proj4": "^2.10.0", "vue": "^3.3.4" }, "devDependencies": { @@ -458,6 +460,11 @@ "optional": true, "peer": true }, + "node_modules/@petamoriken/float16": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.4.tgz", + "integrity": "sha512-kB+NJ5Br56ZhElKsf0pM7/PQfrDdDVMRz8f0JM6eVOGE+L89z9hwcst9QvWBBnazzuqGTGtPsJNZoQ1JdNiGSQ==" + }, "node_modules/@types/node": { "version": "20.2.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", @@ -1614,6 +1621,33 @@ "node": ">=0.10.0" } }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-2.0.0.tgz", + "integrity": "sha512-g2Z+QnWsdHLppAbrpcFWo629kLOnOPtpxYV69GCqm92gqSgyXbzlfyN3MXs0412fPBkFmiuS+rXposgBgBa6Kg==", + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/color-rgba": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-3.0.0.tgz", + "integrity": "sha512-PPwZYkEY3M2THEHHV6Y95sGUie77S7X8v+h1r6LSAPF3/LL2xJ8duUXSrkic31Nzc4odPwHgUbiX/XuTYzQHQg==", + "dependencies": { + "color-parse": "^2.0.0", + "color-space": "^2.0.0" + } + }, + "node_modules/color-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-2.0.1.tgz", + "integrity": "sha512-nKqUYlo0vZATVOFHY810BSYjmCARrG7e5R3UE3CQlyjJTvv5kSSmPG1kzm/oDyyqjehM+lW1RnEt9It9GNa5JA==" + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1867,6 +1901,11 @@ "stream-shift": "^1.0.0" } }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, "node_modules/elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -2427,6 +2466,29 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/geotiff": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.1.1.tgz", + "integrity": "sha512-Ss6HQEhrlR2v0FmOGq88l0wa2oCmmGi6rXAMiUxR/T7Xe98evypEmyiji7lvVeVR/AXuxK0xDCWcwfWkSmOrAA==", + "dependencies": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2", + "zstddec": "^0.1.0" + }, + "engines": { + "node": ">=10.19" + } + }, + "node_modules/geotiff/node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -2623,7 +2685,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -2637,8 +2698,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "peer": true + ] }, "node_modules/iferr": { "version": "0.1.5", @@ -2872,6 +2932,11 @@ "node": ">=0.10.0" } }, + "node_modules/lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==" + }, "node_modules/loader-runner": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", @@ -3005,6 +3070,11 @@ "readable-stream": "^2.0.1" } }, + "node_modules/mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==" + }, "node_modules/micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -3384,6 +3454,23 @@ "node": ">=0.10.0" } }, + "node_modules/ol": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ol/-/ol-8.2.0.tgz", + "integrity": "sha512-/m1ddd7Jsp4Kbg+l7+ozR5aKHAZNQOBAoNZ5pM9Jvh4Etkf0WGkXr9qXd7PnhmwiC1Hnc2Toz9XjCzBBvexfXw==", + "dependencies": { + "color-rgba": "^3.0.0", + "color-space": "^2.0.1", + "earcut": "^2.2.3", + "geotiff": "^2.0.7", + "pbf": "3.2.1", + "rbush": "^3.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openlayers" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3460,6 +3547,11 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==" + }, "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -3495,6 +3587,18 @@ "node": ">=0.10.0" } }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -3568,6 +3672,15 @@ "dev": true, "peer": true }, + "node_modules/proj4": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.10.0.tgz", + "integrity": "sha512-0eyB8h1PDoWxucnq88/EZqt7UZlvjhcfbXCcINpE7hqRN0iRPWE/4mXINGulNa/FAvK+Ie7F+l2OxH/0uKV36A==", + "dependencies": { + "mgrs": "1.0.0", + "wkt-parser": "^1.3.3" + } + }, "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -3575,6 +3688,11 @@ "dev": true, "peer": true }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -3669,6 +3787,22 @@ "node": ">=0.4.x" } }, + "node_modules/quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3690,6 +3824,14 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dependencies": { + "quickselect": "^2.0.0" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -3762,6 +3904,14 @@ "node": ">=0.10" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -5134,6 +5284,11 @@ "node": ">=0.10" } }, + "node_modules/web-worker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", + "integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==" + }, "node_modules/webpack": { "version": "4.46.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", @@ -5233,6 +5388,11 @@ "node": ">= 4" } }, + "node_modules/wkt-parser": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.3.tgz", + "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==" + }, "node_modules/worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", @@ -5262,6 +5422,11 @@ "dev": true, "peer": true }, + "node_modules/xml-utils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz", + "integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -5285,6 +5450,11 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "peer": true + }, + "node_modules/zstddec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", + "integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==" } }, "dependencies": { @@ -5515,6 +5685,11 @@ } } }, + "@petamoriken/float16": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.4.tgz", + "integrity": "sha512-kB+NJ5Br56ZhElKsf0pM7/PQfrDdDVMRz8f0JM6eVOGE+L89z9hwcst9QvWBBnazzuqGTGtPsJNZoQ1JdNiGSQ==" + }, "@types/node": { "version": "20.2.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", @@ -6518,6 +6693,33 @@ "object-visit": "^1.0.0" } }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "color-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-2.0.0.tgz", + "integrity": "sha512-g2Z+QnWsdHLppAbrpcFWo629kLOnOPtpxYV69GCqm92gqSgyXbzlfyN3MXs0412fPBkFmiuS+rXposgBgBa6Kg==", + "requires": { + "color-name": "^1.0.0" + } + }, + "color-rgba": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-3.0.0.tgz", + "integrity": "sha512-PPwZYkEY3M2THEHHV6Y95sGUie77S7X8v+h1r6LSAPF3/LL2xJ8duUXSrkic31Nzc4odPwHgUbiX/XuTYzQHQg==", + "requires": { + "color-parse": "^2.0.0", + "color-space": "^2.0.0" + } + }, + "color-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-2.0.1.tgz", + "integrity": "sha512-nKqUYlo0vZATVOFHY810BSYjmCARrG7e5R3UE3CQlyjJTvv5kSSmPG1kzm/oDyyqjehM+lW1RnEt9It9GNa5JA==" + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -6751,6 +6953,11 @@ "stream-shift": "^1.0.0" } }, + "earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, "elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -7226,6 +7433,28 @@ "dev": true, "optional": true }, + "geotiff": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.1.1.tgz", + "integrity": "sha512-Ss6HQEhrlR2v0FmOGq88l0wa2oCmmGi6rXAMiUxR/T7Xe98evypEmyiji7lvVeVR/AXuxK0xDCWcwfWkSmOrAA==", + "requires": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2", + "zstddec": "^0.1.0" + }, + "dependencies": { + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + } + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -7386,9 +7615,7 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "peer": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "iferr": { "version": "0.1.5", @@ -7582,6 +7809,11 @@ "dev": true, "peer": true }, + "lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==" + }, "loader-runner": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", @@ -7688,6 +7920,11 @@ "readable-stream": "^2.0.1" } }, + "mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==" + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -8006,6 +8243,19 @@ "isobject": "^3.0.1" } }, + "ol": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ol/-/ol-8.2.0.tgz", + "integrity": "sha512-/m1ddd7Jsp4Kbg+l7+ozR5aKHAZNQOBAoNZ5pM9Jvh4Etkf0WGkXr9qXd7PnhmwiC1Hnc2Toz9XjCzBBvexfXw==", + "requires": { + "color-rgba": "^3.0.0", + "color-space": "^2.0.1", + "earcut": "^2.2.3", + "geotiff": "^2.0.7", + "pbf": "3.2.1", + "rbush": "^3.0.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8073,6 +8323,11 @@ "safe-buffer": "^5.1.1" } }, + "parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==" + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -8102,6 +8357,15 @@ "dev": true, "peer": true }, + "pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "requires": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + } + }, "pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -8157,6 +8421,15 @@ "dev": true, "peer": true }, + "proj4": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.10.0.tgz", + "integrity": "sha512-0eyB8h1PDoWxucnq88/EZqt7UZlvjhcfbXCcINpE7hqRN0iRPWE/4mXINGulNa/FAvK+Ie7F+l2OxH/0uKV36A==", + "requires": { + "mgrs": "1.0.0", + "wkt-parser": "^1.3.3" + } + }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -8164,6 +8437,11 @@ "dev": true, "peer": true }, + "protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -8252,6 +8530,16 @@ "dev": true, "peer": true }, + "quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==" + }, + "quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -8273,6 +8561,14 @@ "safe-buffer": "^5.1.0" } }, + "rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "requires": { + "quickselect": "^2.0.0" + } + }, "readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -8333,6 +8629,14 @@ "dev": true, "peer": true }, + "resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -9447,6 +9751,11 @@ } } }, + "web-worker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", + "integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==" + }, "webpack": { "version": "4.46.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", @@ -9520,6 +9829,11 @@ } } }, + "wkt-parser": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.3.tgz", + "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==" + }, "worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", @@ -9546,6 +9860,11 @@ "dev": true, "peer": true }, + "xml-utils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz", + "integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -9566,6 +9885,11 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "peer": true + }, + "zstddec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", + "integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==" } } } diff --git a/app/package.json b/app/package.json index d9eb063..eff5a0e 100644 --- a/app/package.json +++ b/app/package.json @@ -13,6 +13,8 @@ "marked": "^5.0.5", "marked-gfm-heading-id": "^3.1.0", "marked-mangle": "^1.1.4", + "ol": "^8.2.0", + "proj4": "^2.10.0", "vue": "^3.3.4" }, "devDependencies": { diff --git a/app/src/Demo.vue b/app/src/Demo.vue index 1a77ace..f4c0fb3 100644 --- a/app/src/Demo.vue +++ b/app/src/Demo.vue @@ -20,6 +20,13 @@ collections from it.

    + +

    WMTS

    +

    + Enter a WMTS endpoint URL below to get some information and a list of + layers from it. +

    + @@ -29,9 +36,10 @@ import WmsEndpoint from './components/wms/WmsEndpoint.vue'; import WfsEndpoint from './components/wfs/WfsEndpoint.vue'; import OgcApiEndpoint from './components/ogc-api/OgcApiEndpoint.vue'; +import WmtsEndpoint from '@/components/wmts/WmtsEndpoint.vue'; export default { name: 'App', - components: { OgcApiEndpoint, WfsEndpoint, WmsEndpoint }, + components: { WmtsEndpoint, OgcApiEndpoint, WfsEndpoint, WmsEndpoint }, }; diff --git a/app/src/components/presentation/InfoList.vue b/app/src/components/presentation/InfoList.vue index 78a23f3..4e4af88 100644 --- a/app/src/components/presentation/InfoList.vue +++ b/app/src/components/presentation/InfoList.vue @@ -1,8 +1,23 @@ @@ -30,8 +45,11 @@ export default { computed: { propList() { return Object.keys(this.info).map((key) => ({ - title: `${key.substr(0, 1).toUpperCase()}${key.substr(1)}`, - description: `${this.info[key]}`, + title: `${key.substring(0, 1).toUpperCase()}${key.substring(1)}`, + description: + this.info[key] instanceof Object + ? this.info[key] + : `${this.info[key]}`, })); }, }, diff --git a/app/src/components/wfs/WfsEndpoint.vue b/app/src/components/wfs/WfsEndpoint.vue index 22eaf5f..4654214 100644 --- a/app/src/components/wfs/WfsEndpoint.vue +++ b/app/src/components/wfs/WfsEndpoint.vue @@ -1,6 +1,6 @@