diff --git a/examples/wmts.html b/examples/wmts.html new file mode 100644 index 0000000000..daa29875d6 --- /dev/null +++ b/examples/wmts.html @@ -0,0 +1,31 @@ + + + + OpenLayers WMTS Example + + + + + + + +

Web Map Tile Service (WMTS) Layer

+ +

+ The WMTS layer allows viewing of tiles from a server implementing + the OGC Web Map Tile Service (WMTS) standard version 1.0.0. +

+ +
+ +
+

+ This example uses an OpenLayers.Layer.WMTS layer to display + cached tiles over an OSM layer in spherical mercator coordinates. +

+ See the + wmts.js source to see how this is done. +

+
+ + diff --git a/examples/wmts.js b/examples/wmts.js new file mode 100644 index 0000000000..784e596b53 --- /dev/null +++ b/examples/wmts.js @@ -0,0 +1,40 @@ +var map; + +function init() { + + map = new OpenLayers.Map({ + div: "map", + projection: "EPSG:900913", + units: "m", + maxExtent: new OpenLayers.Bounds( + -20037508.34, -20037508.34, 20037508.34, 20037508.34 + ), + maxResolution: 156543.0339 + }); + + var osm = new OpenLayers.Layer.OSM(); + + // If tile matrix identifiers differ from zoom levels (0, 1, 2, ...) + // then they must be explicitly provided. + var matrixIds = new Array(26); + for (var i=0; i<26; ++i) { + matrixIds[i] = "EPSG:900913:" + i; + } + + var wmts = new OpenLayers.Layer.WMTS({ + name: "Medford Buildings", + url: "http://v2.suite.opengeo.org/geoserver/gwc/service/wmts/", + layer: "medford:buildings", + matrixSet: "EPSG:900913", + matrixIds: matrixIds, + format: "image/png", + style: "_null", + opacity: 0.7, + isBaseLayer: false + }); + + map.addLayers([osm, wmts]); + map.addControl(new OpenLayers.Control.LayerSwitcher()); + map.setCenter(new OpenLayers.LonLat(-13677832, 5213272), 13); + +} diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index b5d924a467..9d53295c28 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -124,6 +124,7 @@ "OpenLayers/Layer/WMS.js", "OpenLayers/Layer/WMS/Untiled.js", "OpenLayers/Layer/WMS/Post.js", + "OpenLayers/Layer/WMTS.js", "OpenLayers/Layer/ArcIMS.js", "OpenLayers/Layer/GeoRSS.js", "OpenLayers/Layer/Boxes.js", @@ -289,6 +290,8 @@ "OpenLayers/Format/SOSGetFeatureOfInterest.js", "OpenLayers/Format/OWSContext.js", "OpenLayers/Format/OWSContext/v0_3_1.js", + "OpenLayers/Format/WMTSCapabilities.js", + "OpenLayers/Format/WMTSCapabilities/v1_0_0.js", "OpenLayers/Layer/WFS.js", "OpenLayers/Control/GetFeature.js", "OpenLayers/Control/MouseToolbar.js", diff --git a/lib/OpenLayers/Format/OWSCommon/v1_1_0.js b/lib/OpenLayers/Format/OWSCommon/v1_1_0.js index 310dbe5269..3539735102 100644 --- a/lib/OpenLayers/Format/OWSCommon/v1_1_0.js +++ b/lib/OpenLayers/Format/OWSCommon/v1_1_0.js @@ -48,6 +48,9 @@ OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.OWSCommo }, "MaximumValue": function(node, range) { range.maxValue = this.getChildValue(node); + }, + "Identifier": function(node, obj) { + obj.identifier = this.getChildValue(node); } }, OpenLayers.Format.OWSCommon.v1.prototype.readers["ows"]) }, diff --git a/lib/OpenLayers/Format/WMTSCapabilities.js b/lib/OpenLayers/Format/WMTSCapabilities.js new file mode 100644 index 0000000000..10ee22c871 --- /dev/null +++ b/lib/OpenLayers/Format/WMTSCapabilities.js @@ -0,0 +1,80 @@ +/* Copyright (c) 2006-2009 MetaCarta, Inc., published under the Clear BSD + * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/XML.js + */ + +/** + * Class: OpenLayers.Format.WMTSCapabilities + * Read WMTS Capabilities. + * + * Inherits from: + * - + */ +OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML, { + + /** + * APIProperty: defaultVersion + * {String} Version number to assume if none found. Default is "1.0.0". + */ + defaultVersion: "1.0.0", + + /** + * APIProperty: version + * {String} Specify a version string if one is known. + */ + version: null, + + /** + * Property: parser + * {} A cached versioned format used for reading. + */ + parser: null, + + /** + * Constructor: OpenLayers.Format.WMTSCapabilities + * Create a new parser for WMTS capabilities. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + this.options = options; + }, + + /** + * APIMethod: read + * Read capabilities data from a string, and return information about + * the service (offering and observedProperty mostly). + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. + * + * Returns: + * {Object} Info about the WMTS Capabilities + */ + read: function(data) { + if (typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + var root = data.documentElement; + var version = this.version || root.getAttribute("version") || this.defaultVersion; + if (!this.parser || this.parser.version !== version) { + var constr = OpenLayers.Format.WMTSCapabilities[ + "v" + version.replace(/\./g, "_") + ]; + if (!constr) { + throw new Error("Can't find a WMTS capabilities parser for version " + version); + } + var parser = new constr(this.options); + } + return parser.read(data); + }, + + CLASS_NAME: "OpenLayers.Format.WMTSCapabilities" + +}); diff --git a/lib/OpenLayers/Format/WMTSCapabilities/v1_0_0.js b/lib/OpenLayers/Format/WMTSCapabilities/v1_0_0.js new file mode 100644 index 0000000000..aa4111a00b --- /dev/null +++ b/lib/OpenLayers/Format/WMTSCapabilities/v1_0_0.js @@ -0,0 +1,186 @@ +/* Copyright (c) 2006-2009 MetaCarta, Inc., published under the Clear BSD + * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Format/WMTSCapabilities.js + * @requires OpenLayers/Format/OWSCommon/v1_1_0.js + */ + +/** + * Class: OpenLayers.Format.WMTSCapabilities.v1_0_0 + * Read WMTS Capabilities version 1.0.0. + * + * Inherits from: + * - + */ +OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class( + OpenLayers.Format.OWSCommon.v1_1_0, { + + /** + * Property: version + * {String} The parser version ("1.0.0"). + */ + version: "1.0.0", + + /** + * Property: namespaces + * {Object} Mapping of namespace aliases to namespace URIs. + */ + namespaces: { + ows: "http://www.opengis.net/ows/1.1", + wmts: "http://www.opengis.net/wmts/1.0", + xlink: "http://www.w3.org/1999/xlink" + }, + + /** + * Property: defaultPrefix + * {String} The default namespace alias for creating element nodes. + */ + defaultPrefix: "wmts", + + /** + * Constructor: OpenLayers.Format.WMTSCapabilities.v1_0_0 + * Create a new parser for WMTS capabilities version 1.0.0. + * + * Parameters: + * options - {Object} An optional object whose properties will be set on + * this instance. + */ + initialize: function(options) { + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); + this.options = options; + }, + + /** + * APIMethod: read + * Read capabilities data from a string, and return info about the WMTS. + * + * Parameters: + * data - {String} or {DOMElement} data to read/parse. + * + * Returns: + * {Object} Information about the SOS service. + */ + read: function(data) { + if(typeof data == "string") { + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); + } + if(data && data.nodeType == 9) { + data = data.documentElement; + } + var capabilities = {}; + this.readNode(data, capabilities); + capabilities.version = this.version; + return capabilities; + }, + + /** + * Property: readers + * Contains public functions, grouped by namespace prefix, that will + * be applied when a namespaced node is found matching the function + * name. The function will be applied in the scope of this parser + * with two arguments: the node being read and a context object passed + * from the parent. + */ + readers: { + "wmts": { + "Capabilities": function(node, obj) { + this.readChildNodes(node, obj); + }, + "Contents": function(node, obj) { + obj.contents = {}; + obj.contents.layers = []; + obj.contents.tileMatrixSets = {}; + this.readChildNodes(node, obj.contents); + }, + "Layer": function(node, obj) { + var layer = { + styles: [], + formats: [], + tileMatrixSetLinks: [] + }; + layer.layers = []; + this.readChildNodes(node, layer); + obj.layers.push(layer); + }, + "Style": function(node, obj) { + var style = {}; + style.isDefault = node.getAttribute("isDefault"); + this.readChildNodes(node, style); + obj.styles.push(style); + }, + "Format": function(node, obj) { + obj.formats.push(this.getChildValue(node)); + }, + "TileMatrixSetLink": function(node, obj) { + var tileMatrixSetLink = {}; + this.readChildNodes(node, tileMatrixSetLink); + obj.tileMatrixSetLinks.push(tileMatrixSetLink); + }, + "TileMatrixSet": function(node, obj) { + // node could be child of wmts:Contents or wmts:TileMatrixSetLink + // duck type wmts:Contents by looking for layers + if (obj.layers) { + // TileMatrixSet as object type in schema + var tileMatrixSet = {}; + tileMatrixSet.matrixIds = []; + this.readChildNodes(node, tileMatrixSet); + obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet; + } else { + // TileMatrixSet as string type in schema + obj.tileMatrixSet = this.getChildValue(node); + } + }, + "TileMatrix": function(node, obj) { + var tileMatrix = {}; + this.readChildNodes(node, tileMatrix); + obj.matrixIds.push(tileMatrix); + }, + "ScaleDenominator": function(node, obj) { + obj.scaleDenominator = parseFloat(this.getChildValue(node)); + }, + "TopLeftCorner": function(node, obj) { + var topLeftCorner = this.getChildValue(node); + var coords = topLeftCorner.split(" "); + obj.topLeftCorner = new OpenLayers.LonLat(parseFloat(coords[1]), parseFloat(coords[0])); + }, + "TileWidth": function(node, obj) { + obj.tileWidth = parseInt(this.getChildValue(node)); + }, + "TileHeight": function(node, obj) { + obj.tileHeight = parseInt(this.getChildValue(node)); + }, + "MatrixWidth": function(node, obj) { + obj.matrixWidth = parseInt(this.getChildValue(node)); + }, + "MatrixHeight": function(node, obj) { + obj.matrixHeight = parseInt(this.getChildValue(node)); + }, + // not used for now, can be added in the future though + /*"Themes": function(node, obj) { + obj.themes = []; + this.readChildNodes(node, obj.themes); + }, + "Theme": function(node, obj) { + var theme = {}; + this.readChildNodes(node, theme); + obj.push(theme); + },*/ + "WSDL": function(node, obj) { + obj.wsdl = {}; + obj.wsdl.href = node.getAttribute("xlink:href"); + // TODO: other attributes of element + }, + "ServiceMetadataURL": function(node, obj) { + obj.serviceMetadataUrl = {}; + obj.serviceMetadataUrl.href = node.getAttribute("xlink:href"); + // TODO: other attributes of element + } + }, + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] + }, + + CLASS_NAME: "OpenLayers.Format.WMTSCapabilities.v1_0_0" + +}); diff --git a/lib/OpenLayers/Layer/WMTS.js b/lib/OpenLayers/Layer/WMTS.js new file mode 100644 index 0000000000..39c3f5e168 --- /dev/null +++ b/lib/OpenLayers/Layer/WMTS.js @@ -0,0 +1,394 @@ +/* Copyright (c) 2006-2009 MetaCarta, Inc., published under the Clear BSD + * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Layer/Grid.js + * @requires OpenLayers/Tile/Image.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.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { + + /** + * APIProperty: isBaseLayer + * {Boolean} + */ + 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} The base URL for the WMTS service. + */ + url: null, + + /** + * APIProperty: layer + * {String} The layer identifier advertised by the WMTS service. + */ + 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 + * {} The top-left corner of the tile matrix in map + * units. + */ + tileOrigin: null, + + /** + * APIProperty: tileFullExtent + * {} The full extent of the tile set. If not supplied, + * the layer's 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 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). + * topLeftCorner - {} The top left corner of the + * matrix. Must be provided if different than the layer . + * tileWidth - {Number} The tile width for the matrix. Must be provided + * if different than the width given in the layer . + * tileHeight - {Number} The tile height for the matrix. Must be provided + * if different than the height given in the layer . + */ + matrixIds: null, + + /** + * APIProperty: dimensions + * {Array} For RESTful request encoding, extra dimensions may be specified. + * Items in this list should be property names in the object. + * Values of extra dimensions will be determined from the corresponding + * values in the object. + */ + dimensions: null, + + /** + * APIProperty: params + * {Object} Extra parameters to include in tile requests. For KVP + * , these properties will be encoded in the request + * query string. For REST , these properties will + * become part of the request path, with order determined by the + * list. + */ + params: 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" + }, + + /** + * Constructor: OpenLayers.Layer.WMTS + * Create a new WMTS layer. + * + * Parameters: + * config - {Object} Configuration properties for the layer. + * + * Required configuration properties: + * url - {String} The base url for the service. See the property. + * layer - {String} The layer identifier. See the property. + * matrixSet - {String} The tile matrix set identifier. See the + * property. + * + * Any other documented layer properties can be provided in the config object. + */ + initialize: function(config) { + config.params = OpenLayers.Util.upperCaseObject(config.params); + var args = [config.name, config.url, config.params, config]; + OpenLayers.Layer.Grid.prototype.initialize.apply(this, args); + + // confirm required properties are supplied + var required = { + url: true, + layer: true, + style: true, + matrixSet: true + }; + for (var prop in required) { + if (!(prop in this)) { + throw new Error("Missing property '" + prop + "' in layer configuration."); + } + } + + // 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 from Layer + * + * Parameters: + * map - {} + */ + setMap: function(map) { + OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); + this.map.events.on({ + zoomend: this.updateMatrixProperties, + scope: this + }); + this.updateMatrixProperties(); + if (!this.tileOrigin) { + this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.top); + } + if (!this.tileFullExtent) { + this.tileFullExtent = this.maxExtent; + } + }, + + /** + * Method: removeMap + */ + removeMap: function() { + if (this.map) { + this.map.events.un({ + zoomend: this.updateMatrixProperties, + scope: this + }); + } + OpenLayers.Layer.Grid.prototype.removeMap.apply(this, arguments); + }, + + /** + * APIMethod: clone + * + * Parameters: + * obj - {Object} + * + * Returns: + * {} An exact clone of this + */ + 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: getMatrixId + * Determine the appropriate matrix id for the given map resolution. + */ + getMatrixId: function() { + var id; + var zoom = this.map.getZoom(); + if (!this.matrixIds || this.matrixIds.length === 0) { + id = zoom; + } else { + // TODO: get appropriate matrix id given the map resolution + id = this.matrixIds[zoom].identifier; + } + return id; + }, + + /** + * Method: getURL + * + * Parameters: + * 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 res = this.map.getResolution(); + var zoom = this.map.getZoom(); + var center = bounds.getCenterLonLat(); + + var col = Math.floor((center.lon - this.tileOrigin.lon) / (res * this.tileSize.w)); + var row = Math.floor((this.tileOrigin.lat - center.lat) / (res * this.tileSize.h)); + + var matrixId = this.getMatrixId(); + + if (this.requestEncoding.toUpperCase() === "REST") { + + // include 'version', 'layer' and 'style' in tile resource url + var path = this.version + "/" + this.layer + "/" + this.style + "/"; + + // append optional dimension path elements + if (this.dimensions) { + for (var i=0; i with new properties. Tiles will be + * reloaded with updated params in the request. + * + * Parameters: + * newParams - {Object} Properties to extend to existing . + */ + mergeNewParams: function(newParams) { + if (this.requestEncoding.toUpperCase() === "KVP") { + return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply( + this, [OpenLayers.Util.upperCaseObject(newParams)] + ); + } + }, + + /** + * Method: addTile + * Create a tile, initialize it, and add it to the layer div. + * + * Parameters: + * bounds - {} + * position - {} + * + * Returns: + * {} The added OpenLayers.Tile.Image + */ + addTile: function(bounds,position) { + return new OpenLayers.Tile.Image(this, position, bounds, + null, this.tileSize); + }, + + CLASS_NAME: "OpenLayers.Layer.WMTS" +}); diff --git a/tests/Format/WMTSCapabilities.html b/tests/Format/WMTSCapabilities.html new file mode 100644 index 0000000000..7039707308 --- /dev/null +++ b/tests/Format/WMTSCapabilities.html @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/tests/Format/WMTSCapabilities/v1_0_0.html b/tests/Format/WMTSCapabilities/v1_0_0.html new file mode 100644 index 0000000000..11765986c1 --- /dev/null +++ b/tests/Format/WMTSCapabilities/v1_0_0.html @@ -0,0 +1,275 @@ + + + + + + + + +
+ + diff --git a/tests/Layer/WMTS.html b/tests/Layer/WMTS.html new file mode 100644 index 0000000000..0c275da019 --- /dev/null +++ b/tests/Layer/WMTS.html @@ -0,0 +1,1415 @@ + + + + + + +
+
+
+ + diff --git a/tests/list-tests.html b/tests/list-tests.html index 0f9bad4768..1c3b49b3b0 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -85,6 +85,8 @@
  • Format/WMSCapabilities/v1_3_0.html
  • Format/WMSDescribeLayer.html
  • Format/WMSGetFeatureInfo.html
  • +
  • Format/WMTSCapabilities.html
  • +
  • Format/WMTSCapabilities/v1_0_0.html
  • Format/CSWGetDomain.html
  • Format/CSWGetDomain/v2_0_2.html
  • Format/CSWGetRecords.html
  • @@ -144,6 +146,7 @@
  • Layer/WFS.html
  • Layer/WMS.html
  • Layer/WMS/Post.html
  • +
  • Layer/WMTS.html
  • Layer/WrapDateLine.html
  • Layer/XYZ.html
  • Layer/Yahoo.html