WMTS.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Layer/Grid.js
  7. */
  8. /**
  9. * Class: OpenLayers.Layer.WMTS
  10. * Instances of the WMTS class allow viewing of tiles from a service that
  11. * implements the OGC WMTS specification version 1.0.0.
  12. *
  13. * Inherits from:
  14. * - <OpenLayers.Layer.Grid>
  15. */
  16. OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
  17. /**
  18. * APIProperty: isBaseLayer
  19. * {Boolean} The layer will be considered a base layer. Default is true.
  20. */
  21. isBaseLayer: true,
  22. /**
  23. * Property: version
  24. * {String} WMTS version. Default is "1.0.0".
  25. */
  26. version: "1.0.0",
  27. /**
  28. * APIProperty: requestEncoding
  29. * {String} Request encoding. Can be "REST" or "KVP". Default is "KVP".
  30. */
  31. requestEncoding: "KVP",
  32. /**
  33. * APIProperty: url
  34. * {String|Array(String)} The base URL or request URL template for the WMTS
  35. * service. Must be provided. Array is only supported for base URLs, not
  36. * for request URL templates. URL templates are only supported for
  37. * REST <requestEncoding>.
  38. */
  39. url: null,
  40. /**
  41. * APIProperty: layer
  42. * {String} The layer identifier advertised by the WMTS service. Must be
  43. * provided.
  44. */
  45. layer: null,
  46. /**
  47. * APIProperty: matrixSet
  48. * {String} One of the advertised matrix set identifiers. Must be provided.
  49. */
  50. matrixSet: null,
  51. /**
  52. * APIProperty: style
  53. * {String} One of the advertised layer styles. Must be provided.
  54. */
  55. style: null,
  56. /**
  57. * APIProperty: format
  58. * {String} The image MIME type. Default is "image/jpeg".
  59. */
  60. format: "image/jpeg",
  61. /**
  62. * APIProperty: tileOrigin
  63. * {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map
  64. * units. If the tile origin for each matrix in a set is different,
  65. * the <matrixIds> should include a topLeftCorner property. If
  66. * not provided, the tile origin will default to the top left corner
  67. * of the layer <maxExtent>.
  68. */
  69. tileOrigin: null,
  70. /**
  71. * APIProperty: tileFullExtent
  72. * {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied,
  73. * the layer's <maxExtent> property will be used.
  74. */
  75. tileFullExtent: null,
  76. /**
  77. * APIProperty: formatSuffix
  78. * {String} For REST request encoding, an image format suffix must be
  79. * included in the request. If not provided, the suffix will be derived
  80. * from the <format> property.
  81. */
  82. formatSuffix: null,
  83. /**
  84. * APIProperty: matrixIds
  85. * {Array} A list of tile matrix identifiers. If not provided, the matrix
  86. * identifiers will be assumed to be integers corresponding to the
  87. * map zoom level. If a list of strings is provided, each item should
  88. * be the matrix identifier that corresponds to the map zoom level.
  89. * Additionally, a list of objects can be provided. Each object should
  90. * describe the matrix as presented in the WMTS capabilities. These
  91. * objects should have the propertes shown below.
  92. *
  93. * Matrix properties:
  94. * identifier - {String} The matrix identifier (required).
  95. * scaleDenominator - {Number} The matrix scale denominator.
  96. * topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the
  97. * matrix. Must be provided if different than the layer <tileOrigin>.
  98. * tileWidth - {Number} The tile width for the matrix. Must be provided
  99. * if different than the width given in the layer <tileSize>.
  100. * tileHeight - {Number} The tile height for the matrix. Must be provided
  101. * if different than the height given in the layer <tileSize>.
  102. */
  103. matrixIds: null,
  104. /**
  105. * APIProperty: dimensions
  106. * {Array} For RESTful request encoding, extra dimensions may be specified.
  107. * Items in this list should be property names in the <params> object.
  108. * Values of extra dimensions will be determined from the corresponding
  109. * values in the <params> object.
  110. */
  111. dimensions: null,
  112. /**
  113. * APIProperty: params
  114. * {Object} Extra parameters to include in tile requests. For KVP
  115. * <requestEncoding>, these properties will be encoded in the request
  116. * query string. For REST <requestEncoding>, these properties will
  117. * become part of the request path, with order determined by the
  118. * <dimensions> list.
  119. */
  120. params: null,
  121. /**
  122. * APIProperty: zoomOffset
  123. * {Number} If your cache has more levels than you want to provide
  124. * access to with this layer, supply a zoomOffset. This zoom offset
  125. * is added to the current map zoom level to determine the level
  126. * for a requested tile. For example, if you supply a zoomOffset
  127. * of 3, when the map is at the zoom 0, tiles will be requested from
  128. * level 3 of your cache. Default is 0 (assumes cache level and map
  129. * zoom are equivalent). Additionally, if this layer is to be used
  130. * as an overlay and the cache has fewer zoom levels than the base
  131. * layer, you can supply a negative zoomOffset. For example, if a
  132. * map zoom level of 1 corresponds to your cache level zero, you would
  133. * supply a -1 zoomOffset (and set the maxResolution of the layer
  134. * appropriately). The zoomOffset value has no effect if complete
  135. * matrix definitions (including scaleDenominator) are supplied in
  136. * the <matrixIds> property. Defaults to 0 (no zoom offset).
  137. */
  138. zoomOffset: 0,
  139. /**
  140. * APIProperty: serverResolutions
  141. * {Array} A list of all resolutions available on the server. Only set this
  142. * property if the map resolutions differ from the server. This
  143. * property serves two purposes. (a) <serverResolutions> can include
  144. * resolutions that the server supports and that you don't want to
  145. * provide with this layer; you can also look at <zoomOffset>, which is
  146. * an alternative to <serverResolutions> for that specific purpose.
  147. * (b) The map can work with resolutions that aren't supported by
  148. * the server, i.e. that aren't in <serverResolutions>. When the
  149. * map is displayed in such a resolution data for the closest
  150. * server-supported resolution is loaded and the layer div is
  151. * stretched as necessary.
  152. */
  153. serverResolutions: null,
  154. /**
  155. * Property: formatSuffixMap
  156. * {Object} a map between WMTS 'format' request parameter and tile image file suffix
  157. */
  158. formatSuffixMap: {
  159. "image/png": "png",
  160. "image/png8": "png",
  161. "image/png24": "png",
  162. "image/png32": "png",
  163. "png": "png",
  164. "image/jpeg": "jpg",
  165. "image/jpg": "jpg",
  166. "jpeg": "jpg",
  167. "jpg": "jpg"
  168. },
  169. /**
  170. * Property: matrix
  171. * {Object} Matrix definition for the current map resolution. Updated by
  172. * the <updateMatrixProperties> method.
  173. */
  174. matrix: null,
  175. /**
  176. * Constructor: OpenLayers.Layer.WMTS
  177. * Create a new WMTS layer.
  178. *
  179. * Example:
  180. * (code)
  181. * var wmts = new OpenLayers.Layer.WMTS({
  182. * name: "My WMTS Layer",
  183. * url: "http://example.com/wmts",
  184. * layer: "layer_id",
  185. * style: "default",
  186. * matrixSet: "matrix_id"
  187. * });
  188. * (end)
  189. *
  190. * Parameters:
  191. * config - {Object} Configuration properties for the layer.
  192. *
  193. * Required configuration properties:
  194. * url - {String} The base url for the service. See the <url> property.
  195. * layer - {String} The layer identifier. See the <layer> property.
  196. * style - {String} The layer style identifier. See the <style> property.
  197. * matrixSet - {String} The tile matrix set identifier. See the <matrixSet>
  198. * property.
  199. *
  200. * Any other documented layer properties can be provided in the config object.
  201. */
  202. initialize: function(config) {
  203. // confirm required properties are supplied
  204. var required = {
  205. url: true,
  206. layer: true,
  207. style: true,
  208. matrixSet: true
  209. };
  210. for (var prop in required) {
  211. if (!(prop in config)) {
  212. throw new Error("Missing property '" + prop + "' in layer configuration.");
  213. }
  214. }
  215. config.params = OpenLayers.Util.upperCaseObject(config.params);
  216. var args = [config.name, config.url, config.params, config];
  217. OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);
  218. // determine format suffix (for REST)
  219. if (!this.formatSuffix) {
  220. this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop();
  221. }
  222. // expand matrixIds (may be array of string or array of object)
  223. if (this.matrixIds) {
  224. var len = this.matrixIds.length;
  225. if (len && typeof this.matrixIds[0] === "string") {
  226. var ids = this.matrixIds;
  227. this.matrixIds = new Array(len);
  228. for (var i=0; i<len; ++i) {
  229. this.matrixIds[i] = {identifier: ids[i]};
  230. }
  231. }
  232. }
  233. },
  234. /**
  235. * Method: setMap
  236. */
  237. setMap: function() {
  238. OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
  239. },
  240. /**
  241. * Method: updateMatrixProperties
  242. * Called when map resolution changes to update matrix related properties.
  243. */
  244. updateMatrixProperties: function() {
  245. this.matrix = this.getMatrix();
  246. if (this.matrix) {
  247. if (this.matrix.topLeftCorner) {
  248. this.tileOrigin = this.matrix.topLeftCorner;
  249. }
  250. if (this.matrix.tileWidth && this.matrix.tileHeight) {
  251. this.tileSize = new OpenLayers.Size(
  252. this.matrix.tileWidth, this.matrix.tileHeight
  253. );
  254. }
  255. if (!this.tileOrigin) {
  256. this.tileOrigin = new OpenLayers.LonLat(
  257. this.maxExtent.left, this.maxExtent.top
  258. );
  259. }
  260. if (!this.tileFullExtent) {
  261. this.tileFullExtent = this.maxExtent;
  262. }
  263. }
  264. },
  265. /**
  266. * Method: moveTo
  267. *
  268. * Parameters:
  269. * bounds - {<OpenLayers.Bounds>}
  270. * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
  271. * do some init work in that case.
  272. * dragging - {Boolean}
  273. */
  274. moveTo:function(bounds, zoomChanged, dragging) {
  275. if (zoomChanged || !this.matrix) {
  276. this.updateMatrixProperties();
  277. }
  278. return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments);
  279. },
  280. /**
  281. * APIMethod: clone
  282. *
  283. * Parameters:
  284. * obj - {Object}
  285. *
  286. * Returns:
  287. * {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS>
  288. */
  289. clone: function(obj) {
  290. if (obj == null) {
  291. obj = new OpenLayers.Layer.WMTS(this.options);
  292. }
  293. //get all additions from superclasses
  294. obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
  295. // copy/set any non-init, non-simple values here
  296. return obj;
  297. },
  298. /**
  299. * Method: getIdentifier
  300. * Get the current index in the matrixIds array.
  301. */
  302. getIdentifier: function() {
  303. return this.getServerZoom();
  304. },
  305. /**
  306. * Method: getMatrix
  307. * Get the appropriate matrix definition for the current map resolution.
  308. */
  309. getMatrix: function() {
  310. var matrix;
  311. if (!this.matrixIds || this.matrixIds.length === 0) {
  312. matrix = {identifier: this.getIdentifier()};
  313. } else {
  314. // get appropriate matrix given the map scale if possible
  315. if ("scaleDenominator" in this.matrixIds[0]) {
  316. // scale denominator calculation based on WMTS spec
  317. var denom =
  318. OpenLayers.METERS_PER_INCH *
  319. OpenLayers.INCHES_PER_UNIT[this.units] *
  320. this.getServerResolution() / 0.28E-3;
  321. var diff = Number.POSITIVE_INFINITY;
  322. var delta;
  323. for (var i=0, ii=this.matrixIds.length; i<ii; ++i) {
  324. delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom));
  325. if (delta < diff) {
  326. diff = delta;
  327. matrix = this.matrixIds[i];
  328. }
  329. }
  330. } else {
  331. // fall back on zoom as index
  332. matrix = this.matrixIds[this.getIdentifier()];
  333. }
  334. }
  335. return matrix;
  336. },
  337. /**
  338. * Method: getTileInfo
  339. * Get tile information for a given location at the current map resolution.
  340. *
  341. * Parameters:
  342. * loc - {<OpenLayers.LonLat} A location in map coordinates.
  343. *
  344. * Returns:
  345. * {Object} An object with "col", "row", "i", and "j" properties. The col
  346. * and row values are zero based tile indexes from the top left. The
  347. * i and j values are the number of pixels to the left and top
  348. * (respectively) of the given location within the target tile.
  349. */
  350. getTileInfo: function(loc) {
  351. var res = this.getServerResolution();
  352. var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);
  353. var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);
  354. var col = Math.floor(fx);
  355. var row = Math.floor(fy);
  356. return {
  357. col: col,
  358. row: row,
  359. i: Math.floor((fx - col) * this.tileSize.w),
  360. j: Math.floor((fy - row) * this.tileSize.h)
  361. };
  362. },
  363. /**
  364. * Method: getURL
  365. *
  366. * Parameters:
  367. * bounds - {<OpenLayers.Bounds>}
  368. *
  369. * Returns:
  370. * {String} A URL for the tile corresponding to the given bounds.
  371. */
  372. getURL: function(bounds) {
  373. bounds = this.adjustBounds(bounds);
  374. var url = "";
  375. if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {
  376. var center = bounds.getCenterLonLat();
  377. var info = this.getTileInfo(center);
  378. var matrixId = this.matrix.identifier;
  379. var dimensions = this.dimensions, params;
  380. if (OpenLayers.Util.isArray(this.url)) {
  381. url = this.selectUrl([
  382. this.version, this.style, this.matrixSet,
  383. this.matrix.identifier, info.row, info.col
  384. ].join(","), this.url);
  385. } else {
  386. url = this.url;
  387. }
  388. if (this.requestEncoding.toUpperCase() === "REST") {
  389. params = this.params;
  390. if (url.indexOf("{") !== -1) {
  391. var template = url.replace(/\{/g, "${");
  392. var context = {
  393. // spec does not make clear if capital S or not
  394. style: this.style, Style: this.style,
  395. TileMatrixSet: this.matrixSet,
  396. TileMatrix: this.matrix.identifier,
  397. TileRow: info.row,
  398. TileCol: info.col
  399. };
  400. if (dimensions) {
  401. var dimension, i;
  402. for (i=dimensions.length-1; i>=0; --i) {
  403. dimension = dimensions[i];
  404. context[dimension] = params[dimension.toUpperCase()];
  405. }
  406. }
  407. url = OpenLayers.String.format(template, context);
  408. } else {
  409. // include 'version', 'layer' and 'style' in tile resource url
  410. var path = this.version + "/" + this.layer + "/" + this.style + "/";
  411. // append optional dimension path elements
  412. if (dimensions) {
  413. for (var i=0; i<dimensions.length; i++) {
  414. if (params[dimensions[i]]) {
  415. path = path + params[dimensions[i]] + "/";
  416. }
  417. }
  418. }
  419. // append other required path elements
  420. path = path + this.matrixSet + "/" + this.matrix.identifier +
  421. "/" + info.row + "/" + info.col + "." + this.formatSuffix;
  422. if (!url.match(/\/$/)) {
  423. url = url + "/";
  424. }
  425. url = url + path;
  426. }
  427. } else if (this.requestEncoding.toUpperCase() === "KVP") {
  428. // assemble all required parameters
  429. params = {
  430. SERVICE: "WMTS",
  431. REQUEST: "GetTile",
  432. VERSION: this.version,
  433. LAYER: this.layer,
  434. STYLE: this.style,
  435. TILEMATRIXSET: this.matrixSet,
  436. TILEMATRIX: this.matrix.identifier,
  437. TILEROW: info.row,
  438. TILECOL: info.col,
  439. FORMAT: this.format
  440. };
  441. url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]);
  442. }
  443. }
  444. return url;
  445. },
  446. /**
  447. * APIMethod: mergeNewParams
  448. * Extend the existing layer <params> with new properties. Tiles will be
  449. * reloaded with updated params in the request.
  450. *
  451. * Parameters:
  452. * newParams - {Object} Properties to extend to existing <params>.
  453. */
  454. mergeNewParams: function(newParams) {
  455. if (this.requestEncoding.toUpperCase() === "KVP") {
  456. return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(
  457. this, [OpenLayers.Util.upperCaseObject(newParams)]
  458. );
  459. }
  460. },
  461. CLASS_NAME: "OpenLayers.Layer.WMTS"
  462. });