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> { © <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 ©
+ <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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> { © <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 & 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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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> { © <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors and <a
+ href="https://maps.eox.at/#data">others</a>, Rendering © <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> { © <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 & 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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors and <a
+ href="https://maps.eox.at/#data">others</a>, Rendering © <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 © <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 © <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors and <a
+ href="https://maps.eox.at/#data">others</a>, Rendering © <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 & 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 © <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 & 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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors and <a
+ href="https://maps.eox.at/#data">others</a>, Rendering © <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 © <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 © <a
+ href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Rendering ©
+ <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 exist OpenLayers 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 @@
- {{ prop.title }}
- {{ prop.description }}
+
+ {{ prop.title }}
+
+
+
+ {{
+ prop.description
+ }}
+
+
@@ -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 @@
-
+
-
+
- {{ style.title }}
+ {{ style.title || style.name }}
diff --git a/app/src/components/wmts/WmtsEndpoint.vue b/app/src/components/wmts/WmtsEndpoint.vue
new file mode 100644
index 0000000..922dc8b
--- /dev/null
+++ b/app/src/components/wmts/WmtsEndpoint.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+ Analyze
+
+
+
Loading...
+
+
Error: {{ error }}
+
+
+
+
+
+
diff --git a/app/src/components/wmts/WmtsLayerInfo.vue b/app/src/components/wmts/WmtsLayerInfo.vue
new file mode 100644
index 0000000..cd0359e
--- /dev/null
+++ b/app/src/components/wmts/WmtsLayerInfo.vue
@@ -0,0 +1,145 @@
+
+
+
{{ layer.title }}
+
+
+
+ Selected style:
+
+
+ {{ style.title || style.name }}
+
+
+
+
+ Selected matrix set:
+
+
+ {{ matrixSet.identifier }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main.js b/app/src/main.js
index ba2e713..b0341af 100644
--- a/app/src/main.js
+++ b/app/src/main.js
@@ -1,4 +1,5 @@
import './assets/main.css';
+import 'ol/ol.css';
import { marked } from 'marked';
import { mangle } from 'marked-mangle';
import { createApp } from 'vue';
From 79d7be0234b493dad4587d7aa764668d06c81b66 Mon Sep 17 00:00:00 2001
From: Olivia
Date: Thu, 1 Feb 2024 23:07:05 +0100
Subject: [PATCH 7/7] Small doc improvements on other endpoints
---
src/wms/endpoint.ts | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/wms/endpoint.ts b/src/wms/endpoint.ts
index a730715..2385d18 100644
--- a/src/wms/endpoint.ts
+++ b/src/wms/endpoint.ts
@@ -58,7 +58,6 @@ export type WmsVersion = '1.1.0' | '1.1.1' | '1.3.0';
* Represents a WMS endpoint advertising several layers arranged in a tree structure.
*/
export default class WmsEndpoint {
- private _capabilitiesUrl: string;
private _capabilitiesPromise: Promise;
private _info: GenericEndpointInfo | null;
private _layers: WmsLayerFull[] | null;
@@ -101,8 +100,8 @@ export default class WmsEndpoint {
}
/**
- * Returns an array of layers in summary format; use the `path` property
- * to rebuild the tree structure if needed
+ * Returns an array of layers in summary format; layers are organized in a tree
+ * structure with each aving an optional `children` property
*/
getLayers() {
function layerSummaryMapper(layerFull) {
@@ -120,7 +119,6 @@ export default class WmsEndpoint {
/**
* Returns a complete layer based on its name
- * Note: the first matching layer will be returned
* @param name Layer name property (unique in the WMS service)
* @return return null if layer was not found
*/