diff --git a/src/objectliterals.exports b/src/objectliterals.exports index c90a6c0a85..b2a4019bce 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -150,6 +150,22 @@ @exportObjectLiteralProperty ol.source.TiledWMSOptions.url string|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.urls Array.|undefined +@exportObjectLiteral ol.source.WMTSOptions +@exportObjectLiteralProperty ol.source.WMTSOptions.attributions Array.|undefined +@exportObjectLiteralProperty ol.source.WMTSOptions.crossOrigin string|null|undefined +@exportObjectLiteralProperty ol.source.WMTSOptions.extent ol.Extent|undefined +@exportObjectLiteralProperty ol.source.WMTSOptions.tileGrid ol.tilegrid.WMTS +@exportObjectLiteralProperty ol.source.WMTSOptions.projection ol.Projection|undefined +@exportObjectLiteralProperty ol.source.WMTSOptions.requestEncoding ol.source.WMTSRequestEncoding|undefined +@exportObjectLiteralProperty ol.source.WMTSOptions.layer string +@exportObjectLiteralProperty ol.source.WMTSOptions.style string +@exportObjectLiteralProperty ol.source.WMTSOptions.format string|undefined +@exportObjectLiteralProperty ol.source.WMTSOptions.matrixSet string +@exportObjectLiteralProperty ol.source.WMTSOptions.dimensions Object|undefined +@exportObjectLiteralProperty ol.source.WMTSOptions.url string|undefined +@exportObjectLiteralProperty ol.source.WMTSOptions.maxZoom number|undefined +@exportObjectLiteralProperty ol.source.WMTSOptions.urls Array.|undefined + @exportObjectLiteral ol.tilegrid.TileGridOptions @exportObjectLiteralProperty ol.tilegrid.TileGridOptions.origin ol.Coordinate|undefined @exportObjectLiteralProperty ol.tilegrid.TileGridOptions.origins Array.|undefined diff --git a/src/ol/source/wmtssource.exports b/src/ol/source/wmtssource.exports new file mode 100644 index 0000000000..13ed62b207 --- /dev/null +++ b/src/ol/source/wmtssource.exports @@ -0,0 +1 @@ +@exportClass ol.source.WMTS ol.source.WMTSOptions diff --git a/src/ol/source/wmtssource.js b/src/ol/source/wmtssource.js new file mode 100644 index 0000000000..657a9d65e2 --- /dev/null +++ b/src/ol/source/wmtssource.js @@ -0,0 +1,168 @@ +goog.provide('ol.source.WMTS'); +goog.provide('ol.source.WMTSRequestEncoding'); + +goog.require('ol.Attribution'); +goog.require('ol.Extent'); +goog.require('ol.Projection'); +goog.require('ol.TileCoord'); +goog.require('ol.TileUrlFunction'); +goog.require('ol.TileUrlFunctionType'); +goog.require('ol.projection'); +goog.require('ol.source.ImageTileSource'); +goog.require('ol.tilegrid.WMTS'); + + +/** + * @enum {string} + */ +ol.source.WMTSRequestEncoding = { + KVP: 'KVP', // see spec §8 + REST: 'REST' // see spec §10 +}; + + + +/** + * @constructor + * @extends {ol.source.ImageTileSource} + * @param {ol.source.WMTSOptions} wmtsOptions WMTS options. + */ +ol.source.WMTS = function(wmtsOptions) { + + // TODO: add support for TileMatrixLimits + + var version = goog.isDef(wmtsOptions.version) ? + wmtsOptions.version : '1.0.0'; + var format = goog.isDef(wmtsOptions.format) ? + wmtsOptions.format : 'image/jpeg'; + var dimensions = wmtsOptions.dimensions || {}; + + // FIXME: should we guess this requestEncoding from wmtsOptions.url(s) + // structure? that would mean KVP only if a template is not provided. + var requestEncoding = goog.isDef(wmtsOptions.requestEncoding) ? + wmtsOptions.requestEncoding : ol.source.WMTSRequestEncoding.KVP; + + // FIXME: should we create a default tileGrid? + // we could issue a getCapabilities xhr to retrieve missing configuration + var tileGrid = wmtsOptions.tileGrid; + + var context = { + 'Layer': wmtsOptions.layer, + 'style': wmtsOptions.style, + 'TileMatrixSet': wmtsOptions.matrixSet + }; + goog.object.extend(context, dimensions); + var kvpParams; + if (requestEncoding == ol.source.WMTSRequestEncoding.KVP) { + kvpParams = { + 'Service': 'WMTS', + 'Request': 'GetTile', + 'Version': version, + 'Format': format, + 'TileMatrix': '{TileMatrix}', + 'TileRow': '{TileRow}', + 'TileCol': '{TileCol}' + }; + goog.object.extend(kvpParams, context); + } + + // TODO: factorize the code below so that it is usable by all sources + var urls = wmtsOptions.urls; + if (!goog.isDef(urls)) { + urls = []; + var url = wmtsOptions.url; + goog.asserts.assert(goog.isDef(url)); + var match = /\{(\d)-(\d)\}/.exec(url) || /\{([a-z])-([a-z])\}/.exec(url); + if (match) { + var startCharCode = match[1].charCodeAt(0); + var stopCharCode = match[2].charCodeAt(0); + var charCode; + for (charCode = startCharCode; charCode <= stopCharCode; ++charCode) { + urls.push(url.replace(match[0], String.fromCharCode(charCode))); + } + } else { + urls.push(url); + } + } + + /** + * @param {string} template Template. + * @return {ol.TileUrlFunctionType} Tile URL function. + */ + function createFromWMTSTemplate(template) { + return function(tileCoord) { + if (goog.isNull(tileCoord)) { + return undefined; + } else { + var localContext = { + 'TileMatrix': tileGrid.getMatrixId(tileCoord.z), + 'TileCol': tileCoord.x, + 'TileRow': tileCoord.y + }; + if (requestEncoding != ol.source.WMTSRequestEncoding.KVP) { + goog.object.extend(localContext, context); + } + var url = template; + for (var key in localContext) { + // FIXME: should we filter properties with hasOwnProperty? + url = url.replace('{' + key + '}', localContext[key]) + .replace('%7B' + key + '%7D', localContext[key]); + } + return url; + } + }; + } + + // TODO: update createFromTileUrlFunctions so that if + // tileUrlFunctions.length == 1, it returns the only tileUrlFunction + var tileUrlFunction = ol.TileUrlFunction.createFromTileUrlFunctions( + goog.array.map(urls, function(url) { + if (goog.isDef(kvpParams)) { + // TODO: we may want to create our own appendParams function + // so that params order conforms to wmts spec guidance, + // and so that we can avoid to escape special template params + url = goog.uri.utils.appendParamsFromMap(url, kvpParams); + } + return createFromWMTSTemplate(url); + })); + + tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform( + function(tileCoord, tileGrid, projection) { + if (tileGrid.getResolutions().length <= tileCoord.z) { + return null; + } + var x = tileCoord.x; + var y = -tileCoord.y - 1; + var tileExtent = tileGrid.getTileCoordExtent(tileCoord); + var projectionExtent = projection.getExtent(); + var extent = goog.isDef(wmtsOptions.extent) ? + wmtsOptions.extent : projectionExtent; + + if (!goog.isNull(extent) && projection.isGlobal() && + extent.minX === projectionExtent.minX && + extent.maxX === projectionExtent.maxX) { + var numCols = Math.ceil( + (extent.maxX - extent.minX) / + (tileExtent.maxX - tileExtent.minX)); + x = goog.math.modulo(x, numCols); + tileExtent = tileGrid.getTileCoordExtent( + new ol.TileCoord(tileCoord.z, x, tileCoord.y)); + } + if (!tileExtent.intersects(extent)) { + return null; + } + return new ol.TileCoord(tileCoord.z, x, y); + }, + tileUrlFunction); + + goog.base(this, { + attributions: wmtsOptions.attributions, + crossOrigin: wmtsOptions.crossOrigin, + extent: wmtsOptions.extent, + projection: wmtsOptions.projection, + tileGrid: tileGrid, + tileUrlFunction: tileUrlFunction + }); + +}; +goog.inherits(ol.source.WMTS, ol.source.ImageTileSource);