diff options
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Layer/WMTS.js')
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Layer/WMTS.js | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Layer/WMTS.js b/misc/openlayers/lib/OpenLayers/Layer/WMTS.js new file mode 100644 index 0000000..9c41629 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Layer/WMTS.js @@ -0,0 +1,510 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * @requires OpenLayers/Layer/Grid.js + */ + +/** + * Class: OpenLayers.Layer.WMTS + * Instances of the WMTS class allow viewing of tiles from a service that + * implements the OGC WMTS specification version 1.0.0. + * + * Inherits from: + * - <OpenLayers.Layer.Grid> + */ +OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { + + /** + * APIProperty: isBaseLayer + * {Boolean} The layer will be considered a base layer. Default is true. + */ + isBaseLayer: true, + + /** + * Property: version + * {String} WMTS version. Default is "1.0.0". + */ + version: "1.0.0", + + /** + * APIProperty: requestEncoding + * {String} Request encoding. Can be "REST" or "KVP". Default is "KVP". + */ + requestEncoding: "KVP", + + /** + * APIProperty: url + * {String|Array(String)} The base URL or request URL template for the WMTS + * service. Must be provided. Array is only supported for base URLs, not + * for request URL templates. URL templates are only supported for + * REST <requestEncoding>. + */ + url: null, + + /** + * APIProperty: layer + * {String} The layer identifier advertised by the WMTS service. Must be + * provided. + */ + layer: null, + + /** + * APIProperty: matrixSet + * {String} One of the advertised matrix set identifiers. Must be provided. + */ + matrixSet: null, + + /** + * APIProperty: style + * {String} One of the advertised layer styles. Must be provided. + */ + style: null, + + /** + * APIProperty: format + * {String} The image MIME type. Default is "image/jpeg". + */ + format: "image/jpeg", + + /** + * APIProperty: tileOrigin + * {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map + * units. If the tile origin for each matrix in a set is different, + * the <matrixIds> should include a topLeftCorner property. If + * not provided, the tile origin will default to the top left corner + * of the layer <maxExtent>. + */ + tileOrigin: null, + + /** + * APIProperty: tileFullExtent + * {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied, + * the layer's <maxExtent> property will be used. + */ + tileFullExtent: null, + + /** + * APIProperty: formatSuffix + * {String} For REST request encoding, an image format suffix must be + * included in the request. If not provided, the suffix will be derived + * from the <format> property. + */ + formatSuffix: null, + + /** + * APIProperty: matrixIds + * {Array} A list of tile matrix identifiers. If not provided, the matrix + * identifiers will be assumed to be integers corresponding to the + * map zoom level. If a list of strings is provided, each item should + * be the matrix identifier that corresponds to the map zoom level. + * Additionally, a list of objects can be provided. Each object should + * describe the matrix as presented in the WMTS capabilities. These + * objects should have the propertes shown below. + * + * Matrix properties: + * identifier - {String} The matrix identifier (required). + * scaleDenominator - {Number} The matrix scale denominator. + * topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the + * matrix. Must be provided if different than the layer <tileOrigin>. + * tileWidth - {Number} The tile width for the matrix. Must be provided + * if different than the width given in the layer <tileSize>. + * tileHeight - {Number} The tile height for the matrix. Must be provided + * if different than the height given in the layer <tileSize>. + */ + matrixIds: null, + + /** + * APIProperty: dimensions + * {Array} For RESTful request encoding, extra dimensions may be specified. + * Items in this list should be property names in the <params> object. + * Values of extra dimensions will be determined from the corresponding + * values in the <params> object. + */ + dimensions: null, + + /** + * APIProperty: params + * {Object} Extra parameters to include in tile requests. For KVP + * <requestEncoding>, these properties will be encoded in the request + * query string. For REST <requestEncoding>, these properties will + * become part of the request path, with order determined by the + * <dimensions> list. + */ + params: null, + + /** + * APIProperty: zoomOffset + * {Number} If your cache has more levels than you want to provide + * access to with this layer, supply a zoomOffset. This zoom offset + * is added to the current map zoom level to determine the level + * for a requested tile. For example, if you supply a zoomOffset + * of 3, when the map is at the zoom 0, tiles will be requested from + * level 3 of your cache. Default is 0 (assumes cache level and map + * zoom are equivalent). Additionally, if this layer is to be used + * as an overlay and the cache has fewer zoom levels than the base + * layer, you can supply a negative zoomOffset. For example, if a + * map zoom level of 1 corresponds to your cache level zero, you would + * supply a -1 zoomOffset (and set the maxResolution of the layer + * appropriately). The zoomOffset value has no effect if complete + * matrix definitions (including scaleDenominator) are supplied in + * the <matrixIds> property. Defaults to 0 (no zoom offset). + */ + zoomOffset: 0, + + /** + * APIProperty: serverResolutions + * {Array} A list of all resolutions available on the server. Only set this + * property if the map resolutions differ from the server. This + * property serves two purposes. (a) <serverResolutions> can include + * resolutions that the server supports and that you don't want to + * provide with this layer; you can also look at <zoomOffset>, which is + * an alternative to <serverResolutions> for that specific purpose. + * (b) The map can work with resolutions that aren't supported by + * the server, i.e. that aren't in <serverResolutions>. When the + * map is displayed in such a resolution data for the closest + * server-supported resolution is loaded and the layer div is + * stretched as necessary. + */ + serverResolutions: null, + + /** + * Property: formatSuffixMap + * {Object} a map between WMTS 'format' request parameter and tile image file suffix + */ + formatSuffixMap: { + "image/png": "png", + "image/png8": "png", + "image/png24": "png", + "image/png32": "png", + "png": "png", + "image/jpeg": "jpg", + "image/jpg": "jpg", + "jpeg": "jpg", + "jpg": "jpg" + }, + + /** + * Property: matrix + * {Object} Matrix definition for the current map resolution. Updated by + * the <updateMatrixProperties> method. + */ + matrix: null, + + /** + * Constructor: OpenLayers.Layer.WMTS + * Create a new WMTS layer. + * + * Example: + * (code) + * var wmts = new OpenLayers.Layer.WMTS({ + * name: "My WMTS Layer", + * url: "http://example.com/wmts", + * layer: "layer_id", + * style: "default", + * matrixSet: "matrix_id" + * }); + * (end) + * + * Parameters: + * config - {Object} Configuration properties for the layer. + * + * Required configuration properties: + * url - {String} The base url for the service. See the <url> property. + * layer - {String} The layer identifier. See the <layer> property. + * style - {String} The layer style identifier. See the <style> property. + * matrixSet - {String} The tile matrix set identifier. See the <matrixSet> + * property. + * + * Any other documented layer properties can be provided in the config object. + */ + initialize: function(config) { + + // confirm required properties are supplied + var required = { + url: true, + layer: true, + style: true, + matrixSet: true + }; + for (var prop in required) { + if (!(prop in config)) { + throw new Error("Missing property '" + prop + "' in layer configuration."); + } + } + + config.params = OpenLayers.Util.upperCaseObject(config.params); + var args = [config.name, config.url, config.params, config]; + OpenLayers.Layer.Grid.prototype.initialize.apply(this, args); + + + // determine format suffix (for REST) + if (!this.formatSuffix) { + this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop(); + } + + // expand matrixIds (may be array of string or array of object) + if (this.matrixIds) { + var len = this.matrixIds.length; + if (len && typeof this.matrixIds[0] === "string") { + var ids = this.matrixIds; + this.matrixIds = new Array(len); + for (var i=0; i<len; ++i) { + this.matrixIds[i] = {identifier: ids[i]}; + } + } + } + + }, + + /** + * Method: setMap + */ + setMap: function() { + OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); + }, + + /** + * Method: updateMatrixProperties + * Called when map resolution changes to update matrix related properties. + */ + updateMatrixProperties: function() { + this.matrix = this.getMatrix(); + if (this.matrix) { + if (this.matrix.topLeftCorner) { + this.tileOrigin = this.matrix.topLeftCorner; + } + if (this.matrix.tileWidth && this.matrix.tileHeight) { + this.tileSize = new OpenLayers.Size( + this.matrix.tileWidth, this.matrix.tileHeight + ); + } + if (!this.tileOrigin) { + this.tileOrigin = new OpenLayers.LonLat( + this.maxExtent.left, this.maxExtent.top + ); + } + if (!this.tileFullExtent) { + this.tileFullExtent = this.maxExtent; + } + } + }, + + /** + * Method: moveTo + * + * Parameters: + * bounds - {<OpenLayers.Bounds>} + * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to + * do some init work in that case. + * dragging - {Boolean} + */ + moveTo:function(bounds, zoomChanged, dragging) { + if (zoomChanged || !this.matrix) { + this.updateMatrixProperties(); + } + return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments); + }, + + /** + * APIMethod: clone + * + * Parameters: + * obj - {Object} + * + * Returns: + * {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS> + */ + clone: function(obj) { + if (obj == null) { + obj = new OpenLayers.Layer.WMTS(this.options); + } + //get all additions from superclasses + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); + // copy/set any non-init, non-simple values here + return obj; + }, + + /** + * Method: getIdentifier + * Get the current index in the matrixIds array. + */ + getIdentifier: function() { + return this.getServerZoom(); + }, + + /** + * Method: getMatrix + * Get the appropriate matrix definition for the current map resolution. + */ + getMatrix: function() { + var matrix; + if (!this.matrixIds || this.matrixIds.length === 0) { + matrix = {identifier: this.getIdentifier()}; + } else { + // get appropriate matrix given the map scale if possible + if ("scaleDenominator" in this.matrixIds[0]) { + // scale denominator calculation based on WMTS spec + var denom = + OpenLayers.METERS_PER_INCH * + OpenLayers.INCHES_PER_UNIT[this.units] * + this.getServerResolution() / 0.28E-3; + var diff = Number.POSITIVE_INFINITY; + var delta; + for (var i=0, ii=this.matrixIds.length; i<ii; ++i) { + delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom)); + if (delta < diff) { + diff = delta; + matrix = this.matrixIds[i]; + } + } + } else { + // fall back on zoom as index + matrix = this.matrixIds[this.getIdentifier()]; + } + } + return matrix; + }, + + /** + * Method: getTileInfo + * Get tile information for a given location at the current map resolution. + * + * Parameters: + * loc - {<OpenLayers.LonLat} A location in map coordinates. + * + * Returns: + * {Object} An object with "col", "row", "i", and "j" properties. The col + * and row values are zero based tile indexes from the top left. The + * i and j values are the number of pixels to the left and top + * (respectively) of the given location within the target tile. + */ + getTileInfo: function(loc) { + var res = this.getServerResolution(); + + var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w); + var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h); + + var col = Math.floor(fx); + var row = Math.floor(fy); + + return { + col: col, + row: row, + i: Math.floor((fx - col) * this.tileSize.w), + j: Math.floor((fy - row) * this.tileSize.h) + }; + }, + + /** + * Method: getURL + * + * Parameters: + * bounds - {<OpenLayers.Bounds>} + * + * Returns: + * {String} A URL for the tile corresponding to the given bounds. + */ + getURL: function(bounds) { + bounds = this.adjustBounds(bounds); + var url = ""; + if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) { + + var center = bounds.getCenterLonLat(); + var info = this.getTileInfo(center); + var matrixId = this.matrix.identifier; + var dimensions = this.dimensions, params; + + if (OpenLayers.Util.isArray(this.url)) { + url = this.selectUrl([ + this.version, this.style, this.matrixSet, + this.matrix.identifier, info.row, info.col + ].join(","), this.url); + } else { + url = this.url; + } + + if (this.requestEncoding.toUpperCase() === "REST") { + params = this.params; + if (url.indexOf("{") !== -1) { + var template = url.replace(/\{/g, "${"); + var context = { + // spec does not make clear if capital S or not + style: this.style, Style: this.style, + TileMatrixSet: this.matrixSet, + TileMatrix: this.matrix.identifier, + TileRow: info.row, + TileCol: info.col + }; + if (dimensions) { + var dimension, i; + for (i=dimensions.length-1; i>=0; --i) { + dimension = dimensions[i]; + context[dimension] = params[dimension.toUpperCase()]; + } + } + url = OpenLayers.String.format(template, context); + } else { + // include 'version', 'layer' and 'style' in tile resource url + var path = this.version + "/" + this.layer + "/" + this.style + "/"; + + // append optional dimension path elements + if (dimensions) { + for (var i=0; i<dimensions.length; i++) { + if (params[dimensions[i]]) { + path = path + params[dimensions[i]] + "/"; + } + } + } + + // append other required path elements + path = path + this.matrixSet + "/" + this.matrix.identifier + + "/" + info.row + "/" + info.col + "." + this.formatSuffix; + + if (!url.match(/\/$/)) { + url = url + "/"; + } + url = url + path; + } + } else if (this.requestEncoding.toUpperCase() === "KVP") { + + // assemble all required parameters + params = { + SERVICE: "WMTS", + REQUEST: "GetTile", + VERSION: this.version, + LAYER: this.layer, + STYLE: this.style, + TILEMATRIXSET: this.matrixSet, + TILEMATRIX: this.matrix.identifier, + TILEROW: info.row, + TILECOL: info.col, + FORMAT: this.format + }; + url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]); + + } + } + return url; + }, + + /** + * APIMethod: mergeNewParams + * Extend the existing layer <params> with new properties. Tiles will be + * reloaded with updated params in the request. + * + * Parameters: + * newParams - {Object} Properties to extend to existing <params>. + */ + mergeNewParams: function(newParams) { + if (this.requestEncoding.toUpperCase() === "KVP") { + return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply( + this, [OpenLayers.Util.upperCaseObject(newParams)] + ); + } + }, + + CLASS_NAME: "OpenLayers.Layer.WMTS" +}); |