diff --git a/fixtures/wmts/arcgis.xml b/fixtures/wmts/arcgis.xml
index f81b9d8..7b2dbe5 100644
--- a/fixtures/wmts/arcgis.xml
+++ b/fixtures/wmts/arcgis.xml
@@ -86,6 +86,9 @@
+
diff --git a/src/wmts/capabilities.spec.ts b/src/wmts/capabilities.spec.ts
index 6891fa1..e7f7337 100644
--- a/src/wmts/capabilities.spec.ts
+++ b/src/wmts/capabilities.spec.ts
@@ -570,9 +570,10 @@ describe('WMTS Capabilities', () => {
matrixSetLimits: [],
},
],
- urlTemplates: [
- 'http://www.example.com/wmts/coastlines/{TileMatrix}/{TileRow}/{TileCol}.png',
- ],
+ urlTemplates: {
+ 'image/png':
+ 'http://www.example.com/wmts/coastlines/{TileMatrix}/{TileRow}/{TileCol}.png',
+ },
dimensions: ['Time'],
},
]);
@@ -609,9 +610,10 @@ describe('WMTS Capabilities', () => {
},
],
title: 'Demographics_USA_Population_Density',
- urlTemplates: [
- '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',
- ],
+ urlTemplates: {
+ 'image/png':
+ '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',
+ },
},
]);
});
@@ -682,7 +684,7 @@ describe('WMTS Capabilities', () => {
},
],
title: 'Photographies aƩriennes',
- urlTemplates: [],
+ urlTemplates: {},
},
]);
});
diff --git a/src/wmts/capabilities.ts b/src/wmts/capabilities.ts
index c778e9a..9377ad7 100644
--- a/src/wmts/capabilities.ts
+++ b/src/wmts/capabilities.ts
@@ -1,10 +1,5 @@
import { XmlDocument, XmlElement } from '@rgrove/parse-xml';
-import {
- BoundingBox,
- CrsCode,
- GenericEndpointInfo,
- MimeType,
-} from '../shared/models';
+import { BoundingBox } from '../shared/models';
import {
findChildElement,
findChildrenElement,
@@ -12,8 +7,16 @@ import {
getElementText,
getRootElement,
} from '../shared/xml-utils';
+import {
+ LayerStyle,
+ MatrixSetLink,
+ TileMatrix,
+ WmtsEndpointInfo,
+ WmtsLayer,
+ WmtsMatrixSet,
+} from './model';
-function parseBBox(xmlElement): BoundingBox {
+function parseBBox(xmlElement: XmlElement): BoundingBox {
const result = ['LowerCorner', 'UpperCorner']
.map((elName) => findChildElement(xmlElement, elName))
.map((cornerEl) => getElementText(cornerEl).split(' '))
@@ -23,13 +26,6 @@ function parseBBox(xmlElement): BoundingBox {
return result;
}
-export interface WmtsEndpointInfo extends GenericEndpointInfo {
- getTileUrls: {
- kvp?: string;
- rest?: string;
- };
-}
-
export function readInfoFromCapabilities(
capabilitiesDoc: XmlDocument
): WmtsEndpointInfo {
@@ -67,29 +63,10 @@ export function readInfoFromCapabilities(
};
}
-interface TileMatrix {
- identifier: string;
- scaleDenominator: number;
- resolution?: number; // FOR OL??? or computeResolution?
- topLeft: [number, number];
- tileWidth: number;
- tileHeight: number;
- matrixWidth: number;
- matrixHeight: number;
-}
-
-export interface WmtsMatrixSet {
- identifier: string;
- wellKnownScaleSet?: string; // from fixed list?
- crs: CrsCode;
- boundingBox: BoundingBox;
- tileMatrices: TileMatrix[];
-}
-
export function readMatrixSetsFromCapabilities(
capabilitiesDoc: XmlDocument
): WmtsMatrixSet[] {
- function parseMatrixSet(element): TileMatrix {
+ function parseMatrixSet(element: XmlElement): TileMatrix {
const topLeft = getElementText(findChildElement(element, 'TopLeftCorner'))
.split(' ')
.map(parseFloat) as [number, number];
@@ -135,36 +112,6 @@ export function readMatrixSetsFromCapabilities(
});
}
-interface LayerStyle {
- name: string;
- title: string;
- legendUrl?: string;
-}
-
-interface MatrixSetLink {
- matrixSetId: string;
- matrixSetLimits: MatrixSetLimit[];
-}
-
-interface MatrixSetLimit {
- tileMatrix: string;
- minTileRow: number;
- maxTileRow: number;
- minTileCol: number;
- maxTileCol: number;
-}
-
-export interface WmtsLayer {
- name: string;
- urlTemplates: string[];
- styles: LayerStyle[];
- defaultStyle: string;
- matrixSets: MatrixSetLink[];
- outputFormats: MimeType[];
- latLonBoundingBox?: BoundingBox;
- dimensions?: string[];
-}
-
export function readLayersFromCapabilities(
capabilitiesDoc: XmlDocument
): WmtsLayer[] {
@@ -224,7 +171,11 @@ export function readLayersFromCapabilities(
.filter(
(element) => getElementAttribute(element, 'resourceType') === 'tile'
)
- .map((element) => getElementAttribute(element, 'template'));
+ .reduce((prev, element) => {
+ const mimeType = getElementAttribute(element, 'format');
+ const urlTemplate = getElementAttribute(element, 'template');
+ return { ...prev, [mimeType]: urlTemplate };
+ }, {});
const matrixSets = findChildrenElement(element, 'TileMatrixSetLink').map(
parseMatrixSetLink
);
diff --git a/src/wmts/endpoint.spec.ts b/src/wmts/endpoint.spec.ts
index 239a68a..cf44488 100644
--- a/src/wmts/endpoint.spec.ts
+++ b/src/wmts/endpoint.spec.ts
@@ -1,7 +1,9 @@
-// @ts-ignore
-import ogcsample from '../../fixtures/wmts/ogcsample.xml';
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';
jest.mock('../shared/cache', () => ({
useCache: jest.fn((factory) => factory()),
@@ -14,85 +16,308 @@ describe('WmtsEndpoint', () => {
beforeEach(() => {
jest.clearAllMocks();
- 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('OGC WMTS', () => {
+ beforeEach(() => {
+ global.fetchResponseFactory = () => ogcsample;
+ endpoint = new WmtsEndpoint('https://my.test.service/ogc/wmts?bb=c');
+ });
- describe('caching', () => {
- beforeEach(async () => {
+ 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' }
+ );
});
- it('uses cache once', () => {
- expect(useCache).toHaveBeenCalledTimes(1);
+
+ 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',
+ },
+ });
+ });
});
- 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('#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('#getLayers', () => {
- it('returns a list of layers', async () => {
- await endpoint.isReady();
- expect(endpoint.getLayers()).toEqual([
- expect.objectContaining({
+ describe('#getRequestEncoding', () => {
+ it('returns KVP encoding', async () => {
+ await endpoint.isReady();
+ expect(endpoint.getRequestEncoding()).toEqual('KVP');
+ });
+ });
+
+ describe('#getLayerByName', () => {
+ it('returns a layer', async () => {
+ await endpoint.isReady();
+ expect(
+ endpoint.getLayerByName('BlueMarbleNextGeneration')
+ ).toMatchObject({
+ abstract: 'Blue Marble Next Generation NASA Product',
name: 'BlueMarbleNextGeneration',
- title: 'Blue Marble Next Generation',
- }),
- ]);
+ defaultStyle: 'DarkBlue',
+ dimensions: ['Time'],
+ latLonBoundingBox: [-180, -90, 180, 90],
+ });
+ });
});
- });
- 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('#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('#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('#getCompatibleOutputFormat', () => {
+ beforeEach(async () => {
+ await endpoint.isReady();
+ });
+ it('returns hinted format if supported', () => {
+ expect(
+ endpoint.getCompatibleOutputFormat(
+ 'BlueMarbleNextGeneration',
+ 'image/gif'
+ )
+ ).toEqual('image/gif');
+ });
+ it('returns the first supported format if hinted format is not supported', () => {
+ expect(
+ endpoint.getCompatibleOutputFormat(
+ 'BlueMarbleNextGeneration',
+ 'image/bmp'
+ )
+ ).toEqual('image/jpeg');
+ });
+ it('returns the first supported format if no hint', () => {
+ expect(
+ endpoint.getCompatibleOutputFormat('BlueMarbleNextGeneration')
+ ).toEqual('image/jpeg');
+ });
+ });
+
+ describe('#getLayerBaseTileUrl', () => {
+ it('returns the layer base GetTile url', async () => {
+ await endpoint.isReady();
+ expect(
+ endpoint.getLayerBaseTileUrl('BlueMarbleNextGeneration', 'image/gif')
+ ).toEqual('http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?');
+ });
+ });
+
+ describe('#getTileUrl', () => {
+ it('returns a complete GetTile url', 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'
+ );
+ });
});
});
- 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',
+ 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('#getRequestEncoding', () => {
+ it('returns REST encoding', async () => {
+ await endpoint.isReady();
+ expect(endpoint.getRequestEncoding()).toEqual('REST');
+ });
+ });
+
+ 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('#getCompatibleOutputFormat', () => {
+ beforeEach(async () => {
+ await endpoint.isReady();
+ });
+ it('returns hinted format if supported', () => {
+ expect(
+ endpoint.getCompatibleOutputFormat(
+ 'Demographics_USA_Population_Density',
+ 'image/jpeg'
+ )
+ ).toEqual('image/jpeg');
+ });
+ it('returns the first supported format if hinted format is not supported', () => {
+ expect(
+ endpoint.getCompatibleOutputFormat(
+ 'Demographics_USA_Population_Density',
+ 'image/bmp'
+ )
+ ).toEqual('image/png');
+ });
+ it('returns the first supported format if no hint', () => {
+ expect(
+ endpoint.getCompatibleOutputFormat(
+ 'Demographics_USA_Population_Density'
+ )
+ ).toEqual('image/png');
+ });
+ });
+
+ describe('#getLayerBaseTileUrl', () => {
+ it('returns the layer base GetTile url', async () => {
+ await endpoint.isReady();
+ expect(
+ endpoint.getLayerBaseTileUrl(
+ 'Demographics_USA_Population_Density',
+ 'image/jpeg'
+ )
+ ).toEqual(
+ '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'
+ );
+ });
+ });
+
+ describe('#getTileUrl', () => {
+ it('returns a complete GetTile url', async () => {
+ await endpoint.isReady();
+ expect(
+ endpoint.getTileUrl(
+ 'Demographics_USA_Population_Density',
+ 'default',
+ 'default028mm',
+ '3',
+ 2,
+ 1,
+ 'image/jpeg'
+ )
+ ).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.jpeg'
+ );
});
});
});
diff --git a/src/wmts/endpoint.ts b/src/wmts/endpoint.ts
index db40f57..6bfb849 100644
--- a/src/wmts/endpoint.ts
+++ b/src/wmts/endpoint.ts
@@ -1,15 +1,23 @@
-import { GenericEndpointInfo, MimeType } from '../shared/models';
+import { MimeType } from '../shared/models';
import { setQueryParams } from '../shared/http-utils';
import { useCache } from '../shared/cache';
-import { WmtsLayer, WmtsMatrixSet } from './capabilities';
import { parseWmtsCapabilities } from '../worker';
+import { buildTileGrid } from './tilegrid';
+import {
+ WmtsEndpointInfo,
+ WmtsLayer,
+ WmtsMatrixSet,
+ WmtsRequestEncoding,
+ WmtsTileGrid,
+} from './model';
+import { generateGetTileUrl } from './url';
/**
* Represents a WMTS endpoint advertising several layers.
*/
export default class WmtsEndpoint {
private _capabilitiesPromise: Promise;
- private _info: GenericEndpointInfo = null;
+ private _info: WmtsEndpointInfo = null;
private _layers: WmtsLayer[] = null;
private _matrixSets: WmtsMatrixSet[] = null;
@@ -57,16 +65,105 @@ export default class WmtsEndpoint {
return this._matrixSets;
}
+ getRequestEncoding(): WmtsRequestEncoding {
+ if (!this._info) return null;
+ return 'rest' in this._info.getTileUrls ? 'REST' : 'KVP';
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Will return a format available for a layer; a hint can be provided but only supported formats will be returned
+ * @param layerName
+ * @param formatHint
+ */
+ getCompatibleOutputFormat(layerName: string, formatHint?: MimeType) {
+ if (!this._layers) return null;
+ const layer = this.getLayerByName(layerName);
+ const formats =
+ this.getRequestEncoding() === 'REST'
+ ? Object.keys(layer.urlTemplates)
+ : layer.outputFormats;
+ if (formatHint && formats.includes(formatHint)) {
+ return formatHint;
+ }
+ return formats[0];
+ }
+
/**
- * Generates a URL template containing the {TileMatrix}, {TileCol} and {TileRow} tokens
+ * Generates either a URL template potentially containing the {Style}, {TileMatrixSet},
+ * {TileMatrix}, {TileCol} and {TileRow} tokens (REST encoding),
+ * or a base URL to which query parameters will be added (KVP encoding)
*/
- generateGetTileTemplateUrl(
- urlTemplate: string,
+ getLayerBaseTileUrl(layerName: string, outputFormat?: MimeType): string {
+ if (!this._layers) return null;
+ const layer = this.getLayerByName(layerName);
+ if (this.getRequestEncoding() === 'REST') {
+ let format = this.getCompatibleOutputFormat(layerName, outputFormat);
+ if (outputFormat && format !== outputFormat) {
+ console.warn(
+ `[ogc-client] Requested '${outputFormat}' format for the WMTS layer but it is not available in REST encoding, falling back to '${format}'`
+ );
+ }
+ return layer.urlTemplates[format];
+ } else {
+ return this.getServiceInfo().getTileUrls.kvp;
+ }
+ }
+
+ /**
+ * Generates a URL for a specific tile of a specific layer
+ */
+ getTileUrl(
layerName: string,
- tileMatrixSet: string,
styleName: string,
+ matrixSetName: string,
+ tileMatrix: string,
+ tileRow: number,
+ tileCol: number,
outputFormat?: MimeType
): string {
if (!this._layers) return null;
+ const baseUrl = this.getLayerBaseTileUrl(layerName, outputFormat);
+ const format = this.getCompatibleOutputFormat(layerName, outputFormat);
+ return generateGetTileUrl(
+ baseUrl,
+ this.getRequestEncoding(),
+ layerName,
+ styleName,
+ matrixSetName,
+ tileMatrix,
+ tileRow,
+ tileCol,
+ format
+ );
}
+
+ getTileGrid(layerName: string): WmtsTileGrid {
+ if (!this._layers) return null;
+ const layer = this.getLayerByName(layerName);
+ return buildTileGrid(layer, this.getMatrixSets());
+ }
+
+ /**
+ * Needed for an OL layer
+ * - urls: string[]
+ * - matrix set: string
+ * - format: string
+ * - projection: string
+ * - request encoding: string
+ * - tile grid: extent, minimum zoom, origins (one [x,y] per zoom level),
+ * - style: string
+ * - dimensions: object with dimension names -> values
+ * - global: boolean, true if the matrix set covers the whole world
+ */
}
diff --git a/src/wmts/model.ts b/src/wmts/model.ts
new file mode 100644
index 0000000..b3c6a7a
--- /dev/null
+++ b/src/wmts/model.ts
@@ -0,0 +1,89 @@
+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 {
+ matrixSetId: string;
+ matrixSetLimits: MatrixSetLimit[];
+}
+
+export interface MatrixSetLimit {
+ tileMatrix: string;
+ minTileRow: number;
+ maxTileRow: number;
+ minTileCol: number;
+ maxTileCol: number;
+}
+
+export interface WmtsLayer {
+ name: string;
+ urlTemplates: Record; // key is the image format
+ styles: LayerStyle[];
+ defaultStyle: string;
+ matrixSets: MatrixSetLink[];
+ outputFormats: MimeType[];
+ latLonBoundingBox?: BoundingBox;
+ dimensions?: string[];
+}
+
+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/tilegrid.ts b/src/wmts/tilegrid.ts
index 0b189ae..ae1b460 100644
--- a/src/wmts/tilegrid.ts
+++ b/src/wmts/tilegrid.ts
@@ -1,10 +1,4 @@
-import { WmtsLayer, WmtsMatrixSet } from './capabilities';
-
-export interface WmtsTileGrid {
- origins: [number, number][];
- sizes: [number, number][];
- tileSizes: [number, number][];
-}
+import { WmtsLayer, WmtsMatrixSet, WmtsTileGrid } from './model';
export function buildTileGrid(
layer: WmtsLayer,
diff --git a/src/wmts/url.spec.ts b/src/wmts/url.spec.ts
index 6c9402a..7238a74 100644
--- a/src/wmts/url.spec.ts
+++ b/src/wmts/url.spec.ts
@@ -1,5 +1,40 @@
-describe('url utils', () => {
- describe('generateGetTileTemplateUrl', () => {
- describe('with KVP and REST options', () => {});
+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
index ec077b1..e93d3b4 100644
--- a/src/wmts/url.ts
+++ b/src/wmts/url.ts
@@ -1,12 +1,36 @@
-import { WmtsEndpointInfo, WmtsLayer } from './capabilities';
+import { WmtsRequestEncoding } from './model';
+import { MimeType } from '../shared/models';
+import { setQueryParams } from '../shared/http-utils';
-/**
- * Generates a URL template containing the {TileMatrix}, {TileCol} and {TileRow} tokens
- */
-export function generateGetTileTemplateUrl(
- endpointInfo: WmtsEndpointInfo,
- layer: WmtsLayer,
- matrixSetName: string
+export function generateGetTileUrl(
+ baseUrl: string,
+ requestEncoding: WmtsRequestEncoding,
+ layerName: string,
+ styleName: string,
+ matrixSetName: string,
+ tileMatrix: string,
+ tileRow: number,
+ tileCol: number,
+ outputFormat: MimeType
): string {
- return layer.urlTemplates[0];
+ 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 7852953..0bbe618 100644
--- a/src/worker/index.ts
+++ b/src/worker/index.ts
@@ -7,8 +7,8 @@ import {
} from '../wfs/endpoint';
import { GenericEndpointInfo } from '../shared/models';
import { WmsLayerFull, WmsVersion } from '../wms/endpoint';
-import { WmtsLayer, WmtsMatrixSet } from '../wmts/capabilities';
import { setFetchOptionsUpdateCallback } from '../shared/http-utils';
+import { WmtsEndpointInfo, WmtsLayer, WmtsMatrixSet } from '../wmts/model';
let fallbackWithoutWorker = false;
@@ -87,7 +87,7 @@ export function queryWfsFeatureTypeDetails(
* @param capabilitiesUrl This url should point to the capabilities document
*/
export function parseWmtsCapabilities(capabilitiesUrl: string): Promise<{
- info: GenericEndpointInfo;
+ info: WmtsEndpointInfo;
layers: WmtsLayer[];
matrixSets: WmtsMatrixSet[];
}> {