Source: Scene/WebMapTileServiceImageryProvider.js

  1. /*global define*/
  2. define([
  3. '../Core/combine',
  4. '../Core/Credit',
  5. '../Core/defaultValue',
  6. '../Core/defined',
  7. '../Core/defineProperties',
  8. '../Core/DeveloperError',
  9. '../Core/Event',
  10. '../Core/freezeObject',
  11. '../Core/isArray',
  12. '../Core/objectToQuery',
  13. '../Core/queryToObject',
  14. '../Core/Rectangle',
  15. '../Core/WebMercatorTilingScheme',
  16. '../ThirdParty/Uri',
  17. '../ThirdParty/when',
  18. './ImageryProvider'
  19. ], function(
  20. combine,
  21. Credit,
  22. defaultValue,
  23. defined,
  24. defineProperties,
  25. DeveloperError,
  26. Event,
  27. freezeObject,
  28. isArray,
  29. objectToQuery,
  30. queryToObject,
  31. Rectangle,
  32. WebMercatorTilingScheme,
  33. Uri,
  34. when,
  35. ImageryProvider) {
  36. 'use strict';
  37. /**
  38. * Provides tiled imagery served by {@link http://www.opengeospatial.org/standards/wmts|WMTS 1.0.0} compliant servers.
  39. * This provider supports HTTP KVP-encoded and RESTful GetTile requests, but does not yet support the SOAP encoding.
  40. *
  41. * @alias WebMapTileServiceImageryProvider
  42. * @constructor
  43. *
  44. * @param {Object} options Object with the following properties:
  45. * @param {String} options.url The base URL for the WMTS GetTile operation (for KVP-encoded requests) or the tile-URL template (for RESTful requests). The tile-URL template should contain the following variables: {style}, {TileMatrixSet}, {TileMatrix}, {TileRow}, {TileCol}. The first two are optional if actual values are hardcoded or not required by the server. The {s} keyword may be used to specify subdomains.
  46. * @param {String} [options.format='image/jpeg'] The MIME type for images to retrieve from the server.
  47. * @param {String} options.layer The layer name for WMTS requests.
  48. * @param {String} options.style The style name for WMTS requests.
  49. * @param {String} options.tileMatrixSetID The identifier of the TileMatrixSet to use for WMTS requests.
  50. * @param {Array} [options.tileMatrixLabels] A list of identifiers in the TileMatrix to use for WMTS requests, one per TileMatrix level.
  51. * @param {Number} [options.tileWidth=256] The tile width in pixels.
  52. * @param {Number} [options.tileHeight=256] The tile height in pixels.
  53. * @param {TilingScheme} [options.tilingScheme] The tiling scheme corresponding to the organization of the tiles in the TileMatrixSet.
  54. * @param {Object} [options.proxy] A proxy to use for requests. This object is expected to have a getURL function which returns the proxied URL.
  55. * @param {Rectangle} [options.rectangle=Rectangle.MAX_VALUE] The rectangle covered by the layer.
  56. * @param {Number} [options.minimumLevel=0] The minimum level-of-detail supported by the imagery provider.
  57. * @param {Number} [options.maximumLevel] The maximum level-of-detail supported by the imagery provider, or undefined if there is no limit.
  58. * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used.
  59. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas.
  60. * @param {String|String[]} [options.subdomains='abc'] The subdomains to use for the <code>{s}</code> placeholder in the URL template.
  61. * If this parameter is a single string, each character in the string is a subdomain. If it is
  62. * an array, each element in the array is a subdomain.
  63. *
  64. *
  65. * @example
  66. * // Example 1. USGS shaded relief tiles (KVP)
  67. * var shadedRelief1 = new Cesium.WebMapTileServiceImageryProvider({
  68. * url : 'http://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/WMTS',
  69. * layer : 'USGSShadedReliefOnly',
  70. * style : 'default',
  71. * format : 'image/jpeg',
  72. * tileMatrixSetID : 'default028mm',
  73. * // tileMatrixLabels : ['default028mm:0', 'default028mm:1', 'default028mm:2' ...],
  74. * maximumLevel: 19,
  75. * credit : new Cesium.Credit('U. S. Geological Survey')
  76. * });
  77. * viewer.imageryLayers.addImageryProvider(shadedRelief1);
  78. *
  79. * @example
  80. * // Example 2. USGS shaded relief tiles (RESTful)
  81. * var shadedRelief2 = new Cesium.WebMapTileServiceImageryProvider({
  82. * url : 'http://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/WMTS/tile/1.0.0/USGSShadedReliefOnly/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpg',
  83. * layer : 'USGSShadedReliefOnly',
  84. * style : 'default',
  85. * format : 'image/jpeg',
  86. * tileMatrixSetID : 'default028mm',
  87. * maximumLevel: 19,
  88. * credit : new Cesium.Credit('U. S. Geological Survey')
  89. * });
  90. * viewer.imageryLayers.addImageryProvider(shadedRelief2);
  91. *
  92. * @see ArcGisMapServerImageryProvider
  93. * @see BingMapsImageryProvider
  94. * @see GoogleEarthImageryProvider
  95. * @see createOpenStreetMapImageryProvider
  96. * @see SingleTileImageryProvider
  97. * @see createTileMapServiceImageryProvider
  98. * @see WebMapServiceImageryProvider
  99. * @see UrlTemplateImageryProvider
  100. */
  101. function WebMapTileServiceImageryProvider(options) {
  102. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  103. //>>includeStart('debug', pragmas.debug);
  104. if (!defined(options.url)) {
  105. throw new DeveloperError('options.url is required.');
  106. }
  107. if (!defined(options.layer)) {
  108. throw new DeveloperError('options.layer is required.');
  109. }
  110. if (!defined(options.style)) {
  111. throw new DeveloperError('options.style is required.');
  112. }
  113. if (!defined(options.tileMatrixSetID)) {
  114. throw new DeveloperError('options.tileMatrixSetID is required.');
  115. }
  116. //>>includeEnd('debug');
  117. this._url = options.url;
  118. this._layer = options.layer;
  119. this._style = options.style;
  120. this._tileMatrixSetID = options.tileMatrixSetID;
  121. this._tileMatrixLabels = options.tileMatrixLabels;
  122. this._format = defaultValue(options.format, 'image/jpeg');
  123. this._proxy = options.proxy;
  124. this._tileDiscardPolicy = options.tileDiscardPolicy;
  125. this._tilingScheme = defined(options.tilingScheme) ? options.tilingScheme : new WebMercatorTilingScheme({ ellipsoid : options.ellipsoid });
  126. this._tileWidth = defaultValue(options.tileWidth, 256);
  127. this._tileHeight = defaultValue(options.tileHeight, 256);
  128. this._minimumLevel = defaultValue(options.minimumLevel, 0);
  129. this._maximumLevel = options.maximumLevel;
  130. this._rectangle = defaultValue(options.rectangle, this._tilingScheme.rectangle);
  131. this._readyPromise = when.resolve(true);
  132. // Check the number of tiles at the minimum level. If it's more than four,
  133. // throw an exception, because starting at the higher minimum
  134. // level will cause too many tiles to be downloaded and rendered.
  135. var swTile = this._tilingScheme.positionToTileXY(Rectangle.southwest(this._rectangle), this._minimumLevel);
  136. var neTile = this._tilingScheme.positionToTileXY(Rectangle.northeast(this._rectangle), this._minimumLevel);
  137. var tileCount = (Math.abs(neTile.x - swTile.x) + 1) * (Math.abs(neTile.y - swTile.y) + 1);
  138. //>>includeStart('debug', pragmas.debug);
  139. if (tileCount > 4) {
  140. throw new DeveloperError('The imagery provider\'s rectangle and minimumLevel indicate that there are ' + tileCount + ' tiles at the minimum level. Imagery providers with more than four tiles at the minimum level are not supported.');
  141. }
  142. //>>includeEnd('debug');
  143. this._errorEvent = new Event();
  144. var credit = options.credit;
  145. this._credit = typeof credit === 'string' ? new Credit(credit) : credit;
  146. this._subdomains = options.subdomains;
  147. if (isArray(this._subdomains)) {
  148. this._subdomains = this._subdomains.slice();
  149. } else if (defined(this._subdomains) && this._subdomains.length > 0) {
  150. this._subdomains = this._subdomains.split('');
  151. } else {
  152. this._subdomains = ['a', 'b', 'c'];
  153. }
  154. }
  155. var defaultParameters = freezeObject({
  156. service : 'WMTS',
  157. version : '1.0.0',
  158. request : 'GetTile'
  159. });
  160. function buildImageUrl(imageryProvider, col, row, level) {
  161. var labels = imageryProvider._tileMatrixLabels;
  162. var tileMatrix = defined(labels) ? labels[level] : level.toString();
  163. var subdomains = imageryProvider._subdomains;
  164. var url;
  165. if (imageryProvider._url.indexOf('{') >= 0) {
  166. // resolve tile-URL template
  167. url = imageryProvider._url
  168. .replace('{style}', imageryProvider._style)
  169. .replace('{Style}', imageryProvider._style)
  170. .replace('{TileMatrixSet}', imageryProvider._tileMatrixSetID)
  171. .replace('{TileMatrix}', tileMatrix)
  172. .replace('{TileRow}', row.toString())
  173. .replace('{TileCol}', col.toString())
  174. .replace('{s}', subdomains[(col + row + level) % subdomains.length]);
  175. }
  176. else {
  177. // build KVP request
  178. var uri = new Uri(imageryProvider._url);
  179. var queryOptions = queryToObject(defaultValue(uri.query, ''));
  180. queryOptions = combine(defaultParameters, queryOptions);
  181. queryOptions.tilematrix = tileMatrix;
  182. queryOptions.layer = imageryProvider._layer;
  183. queryOptions.style = imageryProvider._style;
  184. queryOptions.tilerow = row;
  185. queryOptions.tilecol = col;
  186. queryOptions.tilematrixset = imageryProvider._tileMatrixSetID;
  187. queryOptions.format = imageryProvider._format;
  188. uri.query = objectToQuery(queryOptions);
  189. url = uri.toString();
  190. }
  191. var proxy = imageryProvider._proxy;
  192. if (defined(proxy)) {
  193. url = proxy.getURL(url);
  194. }
  195. return url;
  196. }
  197. defineProperties(WebMapTileServiceImageryProvider.prototype, {
  198. /**
  199. * Gets the URL of the service hosting the imagery.
  200. * @memberof WebMapTileServiceImageryProvider.prototype
  201. * @type {String}
  202. * @readonly
  203. */
  204. url : {
  205. get : function() {
  206. return this._url;
  207. }
  208. },
  209. /**
  210. * Gets the proxy used by this provider.
  211. * @memberof WebMapTileServiceImageryProvider.prototype
  212. * @type {Proxy}
  213. * @readonly
  214. */
  215. proxy : {
  216. get : function() {
  217. return this._proxy;
  218. }
  219. },
  220. /**
  221. * Gets the width of each tile, in pixels. This function should
  222. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  223. * @memberof WebMapTileServiceImageryProvider.prototype
  224. * @type {Number}
  225. * @readonly
  226. */
  227. tileWidth : {
  228. get : function() {
  229. return this._tileWidth;
  230. }
  231. },
  232. /**
  233. * Gets the height of each tile, in pixels. This function should
  234. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  235. * @memberof WebMapTileServiceImageryProvider.prototype
  236. * @type {Number}
  237. * @readonly
  238. */
  239. tileHeight : {
  240. get : function() {
  241. return this._tileHeight;
  242. }
  243. },
  244. /**
  245. * Gets the maximum level-of-detail that can be requested. This function should
  246. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  247. * @memberof WebMapTileServiceImageryProvider.prototype
  248. * @type {Number}
  249. * @readonly
  250. */
  251. maximumLevel : {
  252. get : function() {
  253. return this._maximumLevel;
  254. }
  255. },
  256. /**
  257. * Gets the minimum level-of-detail that can be requested. This function should
  258. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  259. * @memberof WebMapTileServiceImageryProvider.prototype
  260. * @type {Number}
  261. * @readonly
  262. */
  263. minimumLevel : {
  264. get : function() {
  265. return this._minimumLevel;
  266. }
  267. },
  268. /**
  269. * Gets the tiling scheme used by this provider. This function should
  270. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  271. * @memberof WebMapTileServiceImageryProvider.prototype
  272. * @type {TilingScheme}
  273. * @readonly
  274. */
  275. tilingScheme : {
  276. get : function() {
  277. return this._tilingScheme;
  278. }
  279. },
  280. /**
  281. * Gets the rectangle, in radians, of the imagery provided by this instance. This function should
  282. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  283. * @memberof WebMapTileServiceImageryProvider.prototype
  284. * @type {Rectangle}
  285. * @readonly
  286. */
  287. rectangle : {
  288. get : function() {
  289. return this._rectangle;
  290. }
  291. },
  292. /**
  293. * Gets the tile discard policy. If not undefined, the discard policy is responsible
  294. * for filtering out "missing" tiles via its shouldDiscardImage function. If this function
  295. * returns undefined, no tiles are filtered. This function should
  296. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  297. * @memberof WebMapTileServiceImageryProvider.prototype
  298. * @type {TileDiscardPolicy}
  299. * @readonly
  300. */
  301. tileDiscardPolicy : {
  302. get : function() {
  303. return this._tileDiscardPolicy;
  304. }
  305. },
  306. /**
  307. * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing
  308. * to the event, you will be notified of the error and can potentially recover from it. Event listeners
  309. * are passed an instance of {@link TileProviderError}.
  310. * @memberof WebMapTileServiceImageryProvider.prototype
  311. * @type {Event}
  312. * @readonly
  313. */
  314. errorEvent : {
  315. get : function() {
  316. return this._errorEvent;
  317. }
  318. },
  319. /**
  320. * Gets the mime type of images returned by this imagery provider.
  321. * @memberof WebMapTileServiceImageryProvider.prototype
  322. * @type {String}
  323. * @readonly
  324. */
  325. format : {
  326. get : function() {
  327. return this._format;
  328. }
  329. },
  330. /**
  331. * Gets a value indicating whether or not the provider is ready for use.
  332. * @memberof WebMapTileServiceImageryProvider.prototype
  333. * @type {Boolean}
  334. * @readonly
  335. */
  336. ready : {
  337. value: true
  338. },
  339. /**
  340. * Gets a promise that resolves to true when the provider is ready for use.
  341. * @memberof WebMapTileServiceImageryProvider.prototype
  342. * @type {Promise.<Boolean>}
  343. * @readonly
  344. */
  345. readyPromise : {
  346. get : function() {
  347. return this._readyPromise;
  348. }
  349. },
  350. /**
  351. * Gets the credit to display when this imagery provider is active. Typically this is used to credit
  352. * the source of the imagery. This function should not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  353. * @memberof WebMapTileServiceImageryProvider.prototype
  354. * @type {Credit}
  355. * @readonly
  356. */
  357. credit : {
  358. get : function() {
  359. return this._credit;
  360. }
  361. },
  362. /**
  363. * Gets a value indicating whether or not the images provided by this imagery provider
  364. * include an alpha channel. If this property is false, an alpha channel, if present, will
  365. * be ignored. If this property is true, any images without an alpha channel will be treated
  366. * as if their alpha is 1.0 everywhere. When this property is false, memory usage
  367. * and texture upload time are reduced.
  368. * @memberof WebMapTileServiceImageryProvider.prototype
  369. * @type {Boolean}
  370. * @readonly
  371. */
  372. hasAlphaChannel : {
  373. get : function() {
  374. return true;
  375. }
  376. }
  377. });
  378. /**
  379. * Gets the credits to be displayed when a given tile is displayed.
  380. *
  381. * @param {Number} x The tile X coordinate.
  382. * @param {Number} y The tile Y coordinate.
  383. * @param {Number} level The tile level;
  384. * @returns {Credit[]} The credits to be displayed when the tile is displayed.
  385. *
  386. * @exception {DeveloperError} <code>getTileCredits</code> must not be called before the imagery provider is ready.
  387. */
  388. WebMapTileServiceImageryProvider.prototype.getTileCredits = function(x, y, level) {
  389. return undefined;
  390. };
  391. /**
  392. * Requests the image for a given tile. This function should
  393. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  394. *
  395. * @param {Number} x The tile X coordinate.
  396. * @param {Number} y The tile Y coordinate.
  397. * @param {Number} level The tile level.
  398. * @returns {Promise.<Image|Canvas>|undefined} A promise for the image that will resolve when the image is available, or
  399. * undefined if there are too many active requests to the server, and the request
  400. * should be retried later. The resolved image may be either an
  401. * Image or a Canvas DOM object.
  402. *
  403. * @exception {DeveloperError} <code>requestImage</code> must not be called before the imagery provider is ready.
  404. */
  405. WebMapTileServiceImageryProvider.prototype.requestImage = function(x, y, level) {
  406. var url = buildImageUrl(this, x, y, level);
  407. return ImageryProvider.loadImage(this, url);
  408. };
  409. /**
  410. * Picking features is not currently supported by this imagery provider, so this function simply returns
  411. * undefined.
  412. *
  413. * @param {Number} x The tile X coordinate.
  414. * @param {Number} y The tile Y coordinate.
  415. * @param {Number} level The tile level.
  416. * @param {Number} longitude The longitude at which to pick features.
  417. * @param {Number} latitude The latitude at which to pick features.
  418. * @return {Promise.<ImageryLayerFeatureInfo[]>|undefined} A promise for the picked features that will resolve when the asynchronous
  419. * picking completes. The resolved value is an array of {@link ImageryLayerFeatureInfo}
  420. * instances. The array may be empty if no features are found at the given location.
  421. * It may also be undefined if picking is not supported.
  422. */
  423. WebMapTileServiceImageryProvider.prototype.pickFeatures = function() {
  424. return undefined;
  425. };
  426. return WebMapTileServiceImageryProvider;
  427. });