WebMapTileServiceImageryProvider.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. import combine from '../Core/combine.js';
  2. import Credit from '../Core/Credit.js';
  3. import defaultValue from '../Core/defaultValue.js';
  4. import defined from '../Core/defined.js';
  5. import defineProperties from '../Core/defineProperties.js';
  6. import DeveloperError from '../Core/DeveloperError.js';
  7. import Event from '../Core/Event.js';
  8. import freezeObject from '../Core/freezeObject.js';
  9. import isArray from '../Core/isArray.js';
  10. import Rectangle from '../Core/Rectangle.js';
  11. import Resource from '../Core/Resource.js';
  12. import WebMercatorTilingScheme from '../Core/WebMercatorTilingScheme.js';
  13. import when from '../ThirdParty/when.js';
  14. import ImageryProvider from './ImageryProvider.js';
  15. import TimeDynamicImagery from './TimeDynamicImagery.js';
  16. var defaultParameters = freezeObject({
  17. service : 'WMTS',
  18. version : '1.0.0',
  19. request : 'GetTile'
  20. });
  21. /**
  22. * Provides tiled imagery served by {@link http://www.opengeospatial.org/standards/wmts|WMTS 1.0.0} compliant servers.
  23. * This provider supports HTTP KVP-encoded and RESTful GetTile requests, but does not yet support the SOAP encoding.
  24. *
  25. * @alias WebMapTileServiceImageryProvider
  26. * @constructor
  27. *
  28. * @param {Object} options Object with the following properties:
  29. * @param {Resource|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.
  30. * @param {String} [options.format='image/jpeg'] The MIME type for images to retrieve from the server.
  31. * @param {String} options.layer The layer name for WMTS requests.
  32. * @param {String} options.style The style name for WMTS requests.
  33. * @param {String} options.tileMatrixSetID The identifier of the TileMatrixSet to use for WMTS requests.
  34. * @param {Array} [options.tileMatrixLabels] A list of identifiers in the TileMatrix to use for WMTS requests, one per TileMatrix level.
  35. * @param {Clock} [options.clock] A Clock instance that is used when determining the value for the time dimension. Required when options.times is specified.
  36. * @param {TimeIntervalCollection} [options.times] TimeIntervalCollection with its <code>data</code> property being an object containing time dynamic dimension and their values.
  37. * @param {Object} [options.dimensions] A object containing static dimensions and their values.
  38. * @param {Number} [options.tileWidth=256] The tile width in pixels.
  39. * @param {Number} [options.tileHeight=256] The tile height in pixels.
  40. * @param {TilingScheme} [options.tilingScheme] The tiling scheme corresponding to the organization of the tiles in the TileMatrixSet.
  41. * @param {Rectangle} [options.rectangle=Rectangle.MAX_VALUE] The rectangle covered by the layer.
  42. * @param {Number} [options.minimumLevel=0] The minimum level-of-detail supported by the imagery provider.
  43. * @param {Number} [options.maximumLevel] The maximum level-of-detail supported by the imagery provider, or undefined if there is no limit.
  44. * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used.
  45. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas.
  46. * @param {String|String[]} [options.subdomains='abc'] The subdomains to use for the <code>{s}</code> placeholder in the URL template.
  47. * If this parameter is a single string, each character in the string is a subdomain. If it is
  48. * an array, each element in the array is a subdomain.
  49. *
  50. * @demo {@link https://sandcastle.cesium.com/index.html?src=Web%20Map%20Tile%20Service%20with%20Time.html|Cesium Sandcastle Web Map Tile Service with Time Demo}
  51. *
  52. * @example
  53. * // Example 1. USGS shaded relief tiles (KVP)
  54. * var shadedRelief1 = new Cesium.WebMapTileServiceImageryProvider({
  55. * url : 'http://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/WMTS',
  56. * layer : 'USGSShadedReliefOnly',
  57. * style : 'default',
  58. * format : 'image/jpeg',
  59. * tileMatrixSetID : 'default028mm',
  60. * // tileMatrixLabels : ['default028mm:0', 'default028mm:1', 'default028mm:2' ...],
  61. * maximumLevel: 19,
  62. * credit : new Cesium.Credit('U. S. Geological Survey')
  63. * });
  64. * viewer.imageryLayers.addImageryProvider(shadedRelief1);
  65. *
  66. * @example
  67. * // Example 2. USGS shaded relief tiles (RESTful)
  68. * var shadedRelief2 = new Cesium.WebMapTileServiceImageryProvider({
  69. * url : 'http://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/WMTS/tile/1.0.0/USGSShadedReliefOnly/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpg',
  70. * layer : 'USGSShadedReliefOnly',
  71. * style : 'default',
  72. * format : 'image/jpeg',
  73. * tileMatrixSetID : 'default028mm',
  74. * maximumLevel: 19,
  75. * credit : new Cesium.Credit('U. S. Geological Survey')
  76. * });
  77. * viewer.imageryLayers.addImageryProvider(shadedRelief2);
  78. *
  79. * @example
  80. * // Example 3. NASA time dynamic weather data (RESTful)
  81. * var times = Cesium.TimeIntervalCollection.fromIso8601({
  82. * iso8601: '2015-07-30/2017-06-16/P1D',
  83. * dataCallback: function dataCallback(interval, index) {
  84. * return {
  85. * Time: Cesium.JulianDate.toIso8601(interval.start)
  86. * };
  87. * }
  88. * });
  89. * var weather = new Cesium.WebMapTileServiceImageryProvider({
  90. * url : 'https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/AMSR2_Snow_Water_Equivalent/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png',
  91. * layer : 'AMSR2_Snow_Water_Equivalent',
  92. * style : 'default',
  93. * tileMatrixSetID : '2km',
  94. * maximumLevel : 5,
  95. * format : 'image/png',
  96. * clock: clock,
  97. * times: times,
  98. * credit : new Cesium.Credit('NASA Global Imagery Browse Services for EOSDIS')
  99. * });
  100. * viewer.imageryLayers.addImageryProvider(weather);
  101. *
  102. * @see ArcGisMapServerImageryProvider
  103. * @see BingMapsImageryProvider
  104. * @see GoogleEarthEnterpriseMapsProvider
  105. * @see OpenStreetMapImageryProvider
  106. * @see SingleTileImageryProvider
  107. * @see TileMapServiceImageryProvider
  108. * @see WebMapServiceImageryProvider
  109. * @see UrlTemplateImageryProvider
  110. */
  111. function WebMapTileServiceImageryProvider(options) {
  112. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  113. //>>includeStart('debug', pragmas.debug);
  114. if (!defined(options.url)) {
  115. throw new DeveloperError('options.url is required.');
  116. }
  117. if (!defined(options.layer)) {
  118. throw new DeveloperError('options.layer is required.');
  119. }
  120. if (!defined(options.style)) {
  121. throw new DeveloperError('options.style is required.');
  122. }
  123. if (!defined(options.tileMatrixSetID)) {
  124. throw new DeveloperError('options.tileMatrixSetID is required.');
  125. }
  126. if (defined(options.times) && !defined(options.clock)) {
  127. throw new DeveloperError('options.times was specified, so options.clock is required.');
  128. }
  129. //>>includeEnd('debug');
  130. var resource = Resource.createIfNeeded(options.url);
  131. var style = options.style;
  132. var tileMatrixSetID = options.tileMatrixSetID;
  133. var url = resource.url;
  134. if (url.indexOf('{') >= 0) {
  135. var templateValues = {
  136. style : style,
  137. Style : style,
  138. TileMatrixSet : tileMatrixSetID
  139. };
  140. resource.setTemplateValues(templateValues);
  141. this._useKvp = false;
  142. } else {
  143. resource.setQueryParameters(defaultParameters);
  144. this._useKvp = true;
  145. }
  146. this._resource = resource;
  147. this._layer = options.layer;
  148. this._style = style;
  149. this._tileMatrixSetID = tileMatrixSetID;
  150. this._tileMatrixLabels = options.tileMatrixLabels;
  151. this._format = defaultValue(options.format, 'image/jpeg');
  152. this._tileDiscardPolicy = options.tileDiscardPolicy;
  153. this._tilingScheme = defined(options.tilingScheme) ? options.tilingScheme : new WebMercatorTilingScheme({ellipsoid : options.ellipsoid});
  154. this._tileWidth = defaultValue(options.tileWidth, 256);
  155. this._tileHeight = defaultValue(options.tileHeight, 256);
  156. this._minimumLevel = defaultValue(options.minimumLevel, 0);
  157. this._maximumLevel = options.maximumLevel;
  158. this._rectangle = defaultValue(options.rectangle, this._tilingScheme.rectangle);
  159. this._dimensions = options.dimensions;
  160. var that = this;
  161. this._reload = undefined;
  162. if (defined(options.times)) {
  163. this._timeDynamicImagery = new TimeDynamicImagery({
  164. clock : options.clock,
  165. times : options.times,
  166. requestImageFunction : function(x, y, level, request, interval) {
  167. return requestImage(that, x, y, level, request, interval);
  168. },
  169. reloadFunction : function() {
  170. if (defined(that._reload)) {
  171. that._reload();
  172. }
  173. }
  174. });
  175. }
  176. this._readyPromise = when.resolve(true);
  177. // Check the number of tiles at the minimum level. If it's more than four,
  178. // throw an exception, because starting at the higher minimum
  179. // level will cause too many tiles to be downloaded and rendered.
  180. var swTile = this._tilingScheme.positionToTileXY(Rectangle.southwest(this._rectangle), this._minimumLevel);
  181. var neTile = this._tilingScheme.positionToTileXY(Rectangle.northeast(this._rectangle), this._minimumLevel);
  182. var tileCount = (Math.abs(neTile.x - swTile.x) + 1) * (Math.abs(neTile.y - swTile.y) + 1);
  183. //>>includeStart('debug', pragmas.debug);
  184. if (tileCount > 4) {
  185. 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.');
  186. }
  187. //>>includeEnd('debug');
  188. this._errorEvent = new Event();
  189. var credit = options.credit;
  190. this._credit = typeof credit === 'string' ? new Credit(credit) : credit;
  191. this._subdomains = options.subdomains;
  192. if (isArray(this._subdomains)) {
  193. this._subdomains = this._subdomains.slice();
  194. } else if (defined(this._subdomains) && this._subdomains.length > 0) {
  195. this._subdomains = this._subdomains.split('');
  196. } else {
  197. this._subdomains = ['a', 'b', 'c'];
  198. }
  199. }
  200. function requestImage(imageryProvider, col, row, level, request, interval) {
  201. var labels = imageryProvider._tileMatrixLabels;
  202. var tileMatrix = defined(labels) ? labels[level] : level.toString();
  203. var subdomains = imageryProvider._subdomains;
  204. var staticDimensions = imageryProvider._dimensions;
  205. var dynamicIntervalData = defined(interval) ? interval.data : undefined;
  206. var resource;
  207. if (!imageryProvider._useKvp) {
  208. var templateValues = {
  209. TileMatrix: tileMatrix,
  210. TileRow: row.toString(),
  211. TileCol: col.toString(),
  212. s: subdomains[(col + row + level) % subdomains.length]
  213. };
  214. resource = imageryProvider._resource.getDerivedResource({
  215. request: request
  216. });
  217. resource.setTemplateValues(templateValues);
  218. if (defined(staticDimensions)) {
  219. resource.setTemplateValues(staticDimensions);
  220. }
  221. if (defined(dynamicIntervalData)) {
  222. resource.setTemplateValues(dynamicIntervalData);
  223. }
  224. } else {
  225. // build KVP request
  226. var query = {};
  227. query.tilematrix = tileMatrix;
  228. query.layer = imageryProvider._layer;
  229. query.style = imageryProvider._style;
  230. query.tilerow = row;
  231. query.tilecol = col;
  232. query.tilematrixset = imageryProvider._tileMatrixSetID;
  233. query.format = imageryProvider._format;
  234. if (defined(staticDimensions)) {
  235. query = combine(query, staticDimensions);
  236. }
  237. if (defined(dynamicIntervalData)) {
  238. query = combine(query, dynamicIntervalData);
  239. }
  240. resource = imageryProvider._resource.getDerivedResource({
  241. queryParameters: query,
  242. request: request
  243. });
  244. }
  245. return ImageryProvider.loadImage(imageryProvider, resource);
  246. }
  247. defineProperties(WebMapTileServiceImageryProvider.prototype, {
  248. /**
  249. * Gets the URL of the service hosting the imagery.
  250. * @memberof WebMapTileServiceImageryProvider.prototype
  251. * @type {String}
  252. * @readonly
  253. */
  254. url : {
  255. get : function() {
  256. return this._resource.url;
  257. }
  258. },
  259. /**
  260. * Gets the proxy used by this provider.
  261. * @memberof WebMapTileServiceImageryProvider.prototype
  262. * @type {Proxy}
  263. * @readonly
  264. */
  265. proxy : {
  266. get : function() {
  267. return this._resource.proxy;
  268. }
  269. },
  270. /**
  271. * Gets the width of each tile, in pixels. This function should
  272. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  273. * @memberof WebMapTileServiceImageryProvider.prototype
  274. * @type {Number}
  275. * @readonly
  276. */
  277. tileWidth : {
  278. get : function() {
  279. return this._tileWidth;
  280. }
  281. },
  282. /**
  283. * Gets the height of each tile, in pixels. This function should
  284. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  285. * @memberof WebMapTileServiceImageryProvider.prototype
  286. * @type {Number}
  287. * @readonly
  288. */
  289. tileHeight : {
  290. get : function() {
  291. return this._tileHeight;
  292. }
  293. },
  294. /**
  295. * Gets the maximum level-of-detail that can be requested. This function should
  296. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  297. * @memberof WebMapTileServiceImageryProvider.prototype
  298. * @type {Number}
  299. * @readonly
  300. */
  301. maximumLevel : {
  302. get : function() {
  303. return this._maximumLevel;
  304. }
  305. },
  306. /**
  307. * Gets the minimum level-of-detail that can be requested. This function should
  308. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  309. * @memberof WebMapTileServiceImageryProvider.prototype
  310. * @type {Number}
  311. * @readonly
  312. */
  313. minimumLevel : {
  314. get : function() {
  315. return this._minimumLevel;
  316. }
  317. },
  318. /**
  319. * Gets the tiling scheme used by this provider. This function should
  320. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  321. * @memberof WebMapTileServiceImageryProvider.prototype
  322. * @type {TilingScheme}
  323. * @readonly
  324. */
  325. tilingScheme : {
  326. get : function() {
  327. return this._tilingScheme;
  328. }
  329. },
  330. /**
  331. * Gets the rectangle, in radians, of the imagery provided by this instance. This function should
  332. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  333. * @memberof WebMapTileServiceImageryProvider.prototype
  334. * @type {Rectangle}
  335. * @readonly
  336. */
  337. rectangle : {
  338. get : function() {
  339. return this._rectangle;
  340. }
  341. },
  342. /**
  343. * Gets the tile discard policy. If not undefined, the discard policy is responsible
  344. * for filtering out "missing" tiles via its shouldDiscardImage function. If this function
  345. * returns undefined, no tiles are filtered. This function should
  346. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  347. * @memberof WebMapTileServiceImageryProvider.prototype
  348. * @type {TileDiscardPolicy}
  349. * @readonly
  350. */
  351. tileDiscardPolicy : {
  352. get : function() {
  353. return this._tileDiscardPolicy;
  354. }
  355. },
  356. /**
  357. * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing
  358. * to the event, you will be notified of the error and can potentially recover from it. Event listeners
  359. * are passed an instance of {@link TileProviderError}.
  360. * @memberof WebMapTileServiceImageryProvider.prototype
  361. * @type {Event}
  362. * @readonly
  363. */
  364. errorEvent : {
  365. get : function() {
  366. return this._errorEvent;
  367. }
  368. },
  369. /**
  370. * Gets the mime type of images returned by this imagery provider.
  371. * @memberof WebMapTileServiceImageryProvider.prototype
  372. * @type {String}
  373. * @readonly
  374. */
  375. format : {
  376. get : function() {
  377. return this._format;
  378. }
  379. },
  380. /**
  381. * Gets a value indicating whether or not the provider is ready for use.
  382. * @memberof WebMapTileServiceImageryProvider.prototype
  383. * @type {Boolean}
  384. * @readonly
  385. */
  386. ready : {
  387. value : true
  388. },
  389. /**
  390. * Gets a promise that resolves to true when the provider is ready for use.
  391. * @memberof WebMapTileServiceImageryProvider.prototype
  392. * @type {Promise.<Boolean>}
  393. * @readonly
  394. */
  395. readyPromise : {
  396. get : function() {
  397. return this._readyPromise;
  398. }
  399. },
  400. /**
  401. * Gets the credit to display when this imagery provider is active. Typically this is used to credit
  402. * the source of the imagery. This function should not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  403. * @memberof WebMapTileServiceImageryProvider.prototype
  404. * @type {Credit}
  405. * @readonly
  406. */
  407. credit : {
  408. get : function() {
  409. return this._credit;
  410. }
  411. },
  412. /**
  413. * Gets a value indicating whether or not the images provided by this imagery provider
  414. * include an alpha channel. If this property is false, an alpha channel, if present, will
  415. * be ignored. If this property is true, any images without an alpha channel will be treated
  416. * as if their alpha is 1.0 everywhere. When this property is false, memory usage
  417. * and texture upload time are reduced.
  418. * @memberof WebMapTileServiceImageryProvider.prototype
  419. * @type {Boolean}
  420. * @readonly
  421. */
  422. hasAlphaChannel : {
  423. get : function() {
  424. return true;
  425. }
  426. },
  427. /**
  428. * Gets or sets a clock that is used to get keep the time used for time dynamic parameters.
  429. * @memberof WebMapTileServiceImageryProvider.prototype
  430. * @type {Clock}
  431. */
  432. clock : {
  433. get : function() {
  434. return this._timeDynamicImagery.clock;
  435. },
  436. set : function(value) {
  437. this._timeDynamicImagery.clock = value;
  438. }
  439. },
  440. /**
  441. * Gets or sets a time interval collection that is used to get time dynamic parameters. The data of each
  442. * TimeInterval is an object containing the keys and values of the properties that are used during
  443. * tile requests.
  444. * @memberof WebMapTileServiceImageryProvider.prototype
  445. * @type {TimeIntervalCollection}
  446. */
  447. times : {
  448. get : function() {
  449. return this._timeDynamicImagery.times;
  450. },
  451. set : function(value) {
  452. this._timeDynamicImagery.times = value;
  453. }
  454. },
  455. /**
  456. * Gets or sets an object that contains static dimensions and their values.
  457. * @memberof WebMapTileServiceImageryProvider.prototype
  458. * @type {Object}
  459. */
  460. dimensions : {
  461. get : function() {
  462. return this._dimensions;
  463. },
  464. set : function(value) {
  465. if (this._dimensions !== value) {
  466. this._dimensions = value;
  467. if (defined(this._reload)) {
  468. this._reload();
  469. }
  470. }
  471. }
  472. }
  473. });
  474. /**
  475. * Gets the credits to be displayed when a given tile is displayed.
  476. *
  477. * @param {Number} x The tile X coordinate.
  478. * @param {Number} y The tile Y coordinate.
  479. * @param {Number} level The tile level;
  480. * @returns {Credit[]} The credits to be displayed when the tile is displayed.
  481. *
  482. * @exception {DeveloperError} <code>getTileCredits</code> must not be called before the imagery provider is ready.
  483. */
  484. WebMapTileServiceImageryProvider.prototype.getTileCredits = function(x, y, level) {
  485. return undefined;
  486. };
  487. /**
  488. * Requests the image for a given tile. This function should
  489. * not be called before {@link WebMapTileServiceImageryProvider#ready} returns true.
  490. *
  491. * @param {Number} x The tile X coordinate.
  492. * @param {Number} y The tile Y coordinate.
  493. * @param {Number} level The tile level.
  494. * @param {Request} [request] The request object. Intended for internal use only.
  495. * @returns {Promise.<Image|Canvas>|undefined} A promise for the image that will resolve when the image is available, or
  496. * undefined if there are too many active requests to the server, and the request
  497. * should be retried later. The resolved image may be either an
  498. * Image or a Canvas DOM object.
  499. *
  500. * @exception {DeveloperError} <code>requestImage</code> must not be called before the imagery provider is ready.
  501. */
  502. WebMapTileServiceImageryProvider.prototype.requestImage = function(x, y, level, request) {
  503. var result;
  504. var timeDynamicImagery = this._timeDynamicImagery;
  505. var currentInterval;
  506. // Try and load from cache
  507. if (defined(timeDynamicImagery)) {
  508. currentInterval = timeDynamicImagery.currentInterval;
  509. result = timeDynamicImagery.getFromCache(x, y, level, request);
  510. }
  511. // Couldn't load from cache
  512. if (!defined(result)) {
  513. result = requestImage(this, x, y, level, request, currentInterval);
  514. }
  515. // If we are approaching an interval, preload this tile in the next interval
  516. if (defined(result) && defined(timeDynamicImagery)) {
  517. timeDynamicImagery.checkApproachingInterval(x, y, level, request);
  518. }
  519. return result;
  520. };
  521. /**
  522. * Picking features is not currently supported by this imagery provider, so this function simply returns
  523. * undefined.
  524. *
  525. * @param {Number} x The tile X coordinate.
  526. * @param {Number} y The tile Y coordinate.
  527. * @param {Number} level The tile level.
  528. * @param {Number} longitude The longitude at which to pick features.
  529. * @param {Number} latitude The latitude at which to pick features.
  530. * @return {Promise.<ImageryLayerFeatureInfo[]>|undefined} A promise for the picked features that will resolve when the asynchronous
  531. * picking completes. The resolved value is an array of {@link ImageryLayerFeatureInfo}
  532. * instances. The array may be empty if no features are found at the given location.
  533. * It may also be undefined if picking is not supported.
  534. */
  535. WebMapTileServiceImageryProvider.prototype.pickFeatures = function(x, y, level, longitude, latitude) {
  536. return undefined;
  537. };
  538. export default WebMapTileServiceImageryProvider;