IonImageryProvider.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. import Check from '../Core/Check.js';
  2. import defaultValue from '../Core/defaultValue.js';
  3. import defined from '../Core/defined.js';
  4. import defineProperties from '../Core/defineProperties.js';
  5. import DeveloperError from '../Core/DeveloperError.js';
  6. import Event from '../Core/Event.js';
  7. import IonResource from '../Core/IonResource.js';
  8. import RuntimeError from '../Core/RuntimeError.js';
  9. import when from '../ThirdParty/when.js';
  10. import ArcGisMapServerImageryProvider from './ArcGisMapServerImageryProvider.js';
  11. import BingMapsImageryProvider from './BingMapsImageryProvider.js';
  12. import TileMapServiceImageryProvider from './TileMapServiceImageryProvider.js';
  13. import GoogleEarthEnterpriseMapsProvider from './GoogleEarthEnterpriseMapsProvider.js';
  14. import MapboxImageryProvider from './MapboxImageryProvider.js';
  15. import SingleTileImageryProvider from './SingleTileImageryProvider.js';
  16. import UrlTemplateImageryProvider from './UrlTemplateImageryProvider.js';
  17. import WebMapServiceImageryProvider from './WebMapServiceImageryProvider.js';
  18. import WebMapTileServiceImageryProvider from './WebMapTileServiceImageryProvider.js';
  19. function createFactory(Type) {
  20. return function(options) {
  21. return new Type(options);
  22. };
  23. }
  24. // These values are the list of supported external imagery
  25. // assets in the Cesium ion beta. They are subject to change.
  26. var ImageryProviderMapping = {
  27. ARCGIS_MAPSERVER: createFactory(ArcGisMapServerImageryProvider),
  28. BING: createFactory(BingMapsImageryProvider),
  29. GOOGLE_EARTH: createFactory(GoogleEarthEnterpriseMapsProvider),
  30. MAPBOX: createFactory(MapboxImageryProvider),
  31. SINGLE_TILE: createFactory(SingleTileImageryProvider),
  32. TMS: createFactory(TileMapServiceImageryProvider),
  33. URL_TEMPLATE: createFactory(UrlTemplateImageryProvider),
  34. WMS: createFactory(WebMapServiceImageryProvider),
  35. WMTS: createFactory(WebMapTileServiceImageryProvider)
  36. };
  37. /**
  38. * Provides tiled imagery using the Cesium ion REST API.
  39. *
  40. * @alias IonImageryProvider
  41. * @constructor
  42. *
  43. * @param {Object} options Object with the following properties:
  44. * @param {Number} options.assetId An ion imagery asset ID;
  45. * @param {String} [options.accessToken=Ion.defaultAccessToken] The access token to use.
  46. * @param {String|Resource} [options.server=Ion.defaultServer] The resource to the Cesium ion API server.
  47. *
  48. * @example
  49. * viewer.imageryLayers.addImageryProvider(new Cesium.IonImageryProvider({ assetId : 23489024 }));
  50. */
  51. function IonImageryProvider(options) {
  52. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  53. var assetId = options.assetId;
  54. //>>includeStart('debug', pragmas.debug);
  55. Check.typeOf.number('options.assetId', assetId);
  56. //>>includeEnd('debug');
  57. /**
  58. * The default alpha blending value of this provider, with 0.0 representing fully transparent and
  59. * 1.0 representing fully opaque.
  60. *
  61. * @type {Number}
  62. * @default undefined
  63. */
  64. this.defaultAlpha = undefined;
  65. /**
  66. * The default brightness of this provider. 1.0 uses the unmodified imagery color. Less than 1.0
  67. * makes the imagery darker while greater than 1.0 makes it brighter.
  68. *
  69. * @type {Number}
  70. * @default undefined
  71. */
  72. this.defaultBrightness = undefined;
  73. /**
  74. * The default contrast of this provider. 1.0 uses the unmodified imagery color. Less than 1.0 reduces
  75. * the contrast while greater than 1.0 increases it.
  76. *
  77. * @type {Number}
  78. * @default undefined
  79. */
  80. this.defaultContrast = undefined;
  81. /**
  82. * The default hue of this provider in radians. 0.0 uses the unmodified imagery color.
  83. *
  84. * @type {Number}
  85. * @default undefined
  86. */
  87. this.defaultHue = undefined;
  88. /**
  89. * The default saturation of this provider. 1.0 uses the unmodified imagery color. Less than 1.0 reduces the
  90. * saturation while greater than 1.0 increases it.
  91. *
  92. * @type {Number}
  93. * @default undefined
  94. */
  95. this.defaultSaturation = undefined;
  96. /**
  97. * The default gamma correction to apply to this provider. 1.0 uses the unmodified imagery color.
  98. *
  99. * @type {Number}
  100. * @default undefined
  101. */
  102. this.defaultGamma = undefined;
  103. /**
  104. * The default texture minification filter to apply to this provider.
  105. *
  106. * @type {TextureMinificationFilter}
  107. * @default undefined
  108. */
  109. this.defaultMinificationFilter = undefined;
  110. /**
  111. * The default texture magnification filter to apply to this provider.
  112. *
  113. * @type {TextureMagnificationFilter}
  114. * @default undefined
  115. */
  116. this.defaultMagnificationFilter = undefined;
  117. this._ready = false;
  118. this._tileCredits = undefined;
  119. this._errorEvent = new Event();
  120. var that = this;
  121. var endpointResource = IonResource._createEndpointResource(assetId, options);
  122. // A simple cache to avoid making repeated requests to ion for endpoints we've
  123. // already retrieved. This exists mainly to support Bing caching to reduce
  124. // world imagery sessions, but provides a small boost of performance in general
  125. // if constantly reloading assets
  126. var cacheKey = options.assetId.toString() + options.accessToken + options.server;
  127. var promise = IonImageryProvider._endpointCache[cacheKey];
  128. if (!defined(promise)) {
  129. promise = endpointResource.fetchJson();
  130. IonImageryProvider._endpointCache[cacheKey] = promise;
  131. }
  132. this._readyPromise = promise
  133. .then(function(endpoint) {
  134. if (endpoint.type !== 'IMAGERY') {
  135. return when.reject(new RuntimeError('Cesium ion asset ' + assetId + ' is not an imagery asset.'));
  136. }
  137. var imageryProvider;
  138. var externalType = endpoint.externalType;
  139. if (!defined(externalType)) {
  140. imageryProvider = new TileMapServiceImageryProvider({
  141. url: new IonResource(endpoint, endpointResource)
  142. });
  143. } else {
  144. var factory = ImageryProviderMapping[externalType];
  145. if (!defined(factory)) {
  146. return when.reject(new RuntimeError('Unrecognized Cesium ion imagery type: ' + externalType));
  147. }
  148. imageryProvider = factory(endpoint.options);
  149. }
  150. that._tileCredits = IonResource.getCreditsFromEndpoint(endpoint, endpointResource);
  151. imageryProvider.errorEvent.addEventListener(function(tileProviderError) {
  152. //Propagate the errorEvent but set the provider to this instance instead
  153. //of the inner instance.
  154. tileProviderError.provider = that;
  155. that._errorEvent.raiseEvent(tileProviderError);
  156. });
  157. that._imageryProvider = imageryProvider;
  158. return imageryProvider.readyPromise.then(function() {
  159. that._ready = true;
  160. return true;
  161. });
  162. });
  163. }
  164. defineProperties(IonImageryProvider.prototype, {
  165. /**
  166. * Gets a value indicating whether or not the provider is ready for use.
  167. * @memberof IonImageryProvider.prototype
  168. * @type {Boolean}
  169. * @readonly
  170. */
  171. ready : {
  172. get: function() {
  173. return this._ready;
  174. }
  175. },
  176. /**
  177. * Gets a promise that resolves to true when the provider is ready for use.
  178. * @memberof IonImageryProvider.prototype
  179. * @type {Promise.<Boolean>}
  180. * @readonly
  181. */
  182. readyPromise : {
  183. get: function() {
  184. return this._readyPromise;
  185. }
  186. },
  187. /**
  188. * Gets the rectangle, in radians, of the imagery provided by the instance. This function should
  189. * not be called before {@link IonImageryProvider#ready} returns true.
  190. * @memberof IonImageryProvider.prototype
  191. * @type {Rectangle}
  192. * @readonly
  193. */
  194. rectangle: {
  195. get: function() {
  196. //>>includeStart('debug', pragmas.debug);
  197. if (!this._ready) {
  198. throw new DeveloperError('tileHeight must not be called before the imagery provider is ready.');
  199. }
  200. //>>includeEnd('debug');
  201. return this._imageryProvider.rectangle;
  202. }
  203. },
  204. /**
  205. * Gets the width of each tile, in pixels. This function should
  206. * not be called before {@link IonImageryProvider#ready} returns true.
  207. * @memberof IonImageryProvider.prototype
  208. * @type {Number}
  209. * @readonly
  210. */
  211. tileWidth : {
  212. get: function() {
  213. //>>includeStart('debug', pragmas.debug);
  214. if (!this._ready) {
  215. throw new DeveloperError('tileWidth must not be called before the imagery provider is ready.');
  216. }
  217. //>>includeEnd('debug');
  218. return this._imageryProvider.tileWidth;
  219. }
  220. },
  221. /**
  222. * Gets the height of each tile, in pixels. This function should
  223. * not be called before {@link IonImageryProvider#ready} returns true.
  224. * @memberof IonImageryProvider.prototype
  225. * @type {Number}
  226. * @readonly
  227. */
  228. tileHeight : {
  229. get: function() {
  230. //>>includeStart('debug', pragmas.debug);
  231. if (!this._ready) {
  232. throw new DeveloperError('tileHeight must not be called before the imagery provider is ready.');
  233. }
  234. //>>includeEnd('debug');
  235. return this._imageryProvider.tileHeight;
  236. }
  237. },
  238. /**
  239. * Gets the maximum level-of-detail that can be requested. This function should
  240. * not be called before {@link IonImageryProvider#ready} returns true.
  241. * @memberof IonImageryProvider.prototype
  242. * @type {Number}
  243. * @readonly
  244. */
  245. maximumLevel : {
  246. get: function() {
  247. //>>includeStart('debug', pragmas.debug);
  248. if (!this._ready) {
  249. throw new DeveloperError('maximumLevel must not be called before the imagery provider is ready.');
  250. }
  251. //>>includeEnd('debug');
  252. return this._imageryProvider.maximumLevel;
  253. }
  254. },
  255. /**
  256. * Gets the minimum level-of-detail that can be requested. This function should
  257. * not be called before {@link IonImageryProvider#ready} returns true. Generally,
  258. * a minimum level should only be used when the rectangle of the imagery is small
  259. * enough that the number of tiles at the minimum level is small. An imagery
  260. * provider with more than a few tiles at the minimum level will lead to
  261. * rendering problems.
  262. * @memberof IonImageryProvider.prototype
  263. * @type {Number}
  264. * @readonly
  265. */
  266. minimumLevel : {
  267. get: function() {
  268. //>>includeStart('debug', pragmas.debug);
  269. if (!this._ready) {
  270. throw new DeveloperError('minimumLevel must not be called before the imagery provider is ready.');
  271. }
  272. //>>includeEnd('debug');
  273. return this._imageryProvider.minimumLevel;
  274. }
  275. },
  276. /**
  277. * Gets the tiling scheme used by the provider. This function should
  278. * not be called before {@link IonImageryProvider#ready} returns true.
  279. * @memberof IonImageryProvider.prototype
  280. * @type {TilingScheme}
  281. * @readonly
  282. */
  283. tilingScheme : {
  284. get: function() {
  285. //>>includeStart('debug', pragmas.debug);
  286. if (!this._ready) {
  287. throw new DeveloperError('tilingScheme must not be called before the imagery provider is ready.');
  288. }
  289. //>>includeEnd('debug');
  290. return this._imageryProvider.tilingScheme;
  291. }
  292. },
  293. /**
  294. * Gets the tile discard policy. If not undefined, the discard policy is responsible
  295. * for filtering out "missing" tiles via its shouldDiscardImage function. If this function
  296. * returns undefined, no tiles are filtered. This function should
  297. * not be called before {@link IonImageryProvider#ready} returns true.
  298. * @memberof IonImageryProvider.prototype
  299. * @type {TileDiscardPolicy}
  300. * @readonly
  301. */
  302. tileDiscardPolicy : {
  303. get: function() {
  304. //>>includeStart('debug', pragmas.debug);
  305. if (!this._ready) {
  306. throw new DeveloperError('tileDiscardPolicy must not be called before the imagery provider is ready.');
  307. }
  308. //>>includeEnd('debug');
  309. return this._imageryProvider.tileDiscardPolicy;
  310. }
  311. },
  312. /**
  313. * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing
  314. * to the event, you will be notified of the error and can potentially recover from it. Event listeners
  315. * are passed an instance of {@link TileProviderError}.
  316. * @memberof IonImageryProvider.prototype
  317. * @type {Event}
  318. * @readonly
  319. */
  320. errorEvent : {
  321. get: function() {
  322. return this._errorEvent;
  323. }
  324. },
  325. /**
  326. * Gets the credit to display when this imagery provider is active. Typically this is used to credit
  327. * the source of the imagery. This function should
  328. * not be called before {@link IonImageryProvider#ready} returns true.
  329. * @memberof IonImageryProvider.prototype
  330. * @type {Credit}
  331. * @readonly
  332. */
  333. credit : {
  334. get: function() {
  335. //>>includeStart('debug', pragmas.debug);
  336. if (!this._ready) {
  337. throw new DeveloperError('credit must not be called before the imagery provider is ready.');
  338. }
  339. //>>includeEnd('debug');
  340. return this._imageryProvider.credit;
  341. }
  342. },
  343. /**
  344. * Gets a value indicating whether or not the images provided by this imagery provider
  345. * include an alpha channel. If this property is false, an alpha channel, if present, will
  346. * be ignored. If this property is true, any images without an alpha channel will be treated
  347. * as if their alpha is 1.0 everywhere. When this property is false, memory usage
  348. * and texture upload time are reduced.
  349. * @memberof IonImageryProvider.prototype
  350. * @type {Boolean}
  351. * @readonly
  352. */
  353. hasAlphaChannel: {
  354. get: function() {
  355. //>>includeStart('debug', pragmas.debug);
  356. if (!this._ready) {
  357. throw new DeveloperError('hasAlphaChannel must not be called before the imagery provider is ready.');
  358. }
  359. //>>includeEnd('debug');
  360. return this._imageryProvider.hasAlphaChannel;
  361. }
  362. }
  363. });
  364. /**
  365. * Gets the credits to be displayed when a given tile is displayed.
  366. * @function
  367. *
  368. * @param {Number} x The tile X coordinate.
  369. * @param {Number} y The tile Y coordinate.
  370. * @param {Number} level The tile level;
  371. * @returns {Credit[]} The credits to be displayed when the tile is displayed.
  372. *
  373. * @exception {DeveloperError} <code>getTileCredits</code> must not be called before the imagery provider is ready.
  374. */
  375. IonImageryProvider.prototype.getTileCredits = function(x, y, level) {
  376. //>>includeStart('debug', pragmas.debug);
  377. if (!this._ready) {
  378. throw new DeveloperError('getTileCredits must not be called before the imagery provider is ready.');
  379. }
  380. //>>includeEnd('debug');
  381. var innerCredits = this._imageryProvider.getTileCredits(x, y, level);
  382. if (!defined(innerCredits)) {
  383. return this._tileCredits;
  384. }
  385. return this._tileCredits.concat(innerCredits);
  386. };
  387. /**
  388. * Requests the image for a given tile. This function should
  389. * not be called before {@link IonImageryProvider#ready} returns true.
  390. * @function
  391. *
  392. * @param {Number} x The tile X coordinate.
  393. * @param {Number} y The tile Y coordinate.
  394. * @param {Number} level The tile level.
  395. * @param {Request} [request] The request object. Intended for internal use only.
  396. * @returns {Promise.<Image|Canvas>|undefined} A promise for the image that will resolve when the image is available, or
  397. * undefined if there are too many active requests to the server, and the request
  398. * should be retried later. The resolved image may be either an
  399. * Image or a Canvas DOM object.
  400. *
  401. * @exception {DeveloperError} <code>requestImage</code> must not be called before the imagery provider is ready.
  402. */
  403. IonImageryProvider.prototype.requestImage = function(x, y, level, request) {
  404. //>>includeStart('debug', pragmas.debug);
  405. if (!this._ready) {
  406. throw new DeveloperError('requestImage must not be called before the imagery provider is ready.');
  407. }
  408. //>>includeEnd('debug');
  409. return this._imageryProvider.requestImage(x, y, level, request);
  410. };
  411. /**
  412. * Asynchronously determines what features, if any, are located at a given longitude and latitude within
  413. * a tile. This function should not be called before {@link IonImageryProvider#ready} returns true.
  414. * This function is optional, so it may not exist on all ImageryProviders.
  415. *
  416. * @function
  417. *
  418. * @param {Number} x The tile X coordinate.
  419. * @param {Number} y The tile Y coordinate.
  420. * @param {Number} level The tile level.
  421. * @param {Number} longitude The longitude at which to pick features.
  422. * @param {Number} latitude The latitude at which to pick features.
  423. * @return {Promise.<ImageryLayerFeatureInfo[]>|undefined} A promise for the picked features that will resolve when the asynchronous
  424. * picking completes. The resolved value is an array of {@link ImageryLayerFeatureInfo}
  425. * instances. The array may be empty if no features are found at the given location.
  426. * It may also be undefined if picking is not supported.
  427. *
  428. * @exception {DeveloperError} <code>pickFeatures</code> must not be called before the imagery provider is ready.
  429. */
  430. IonImageryProvider.prototype.pickFeatures = function(x, y, level, longitude, latitude) {
  431. //>>includeStart('debug', pragmas.debug);
  432. if (!this._ready) {
  433. throw new DeveloperError('pickFeatures must not be called before the imagery provider is ready.');
  434. }
  435. //>>includeEnd('debug');
  436. return this._imageryProvider.pickFeatures(x, y, level, longitude, latitude);
  437. };
  438. //exposed for testing
  439. IonImageryProvider._endpointCache = {};
  440. export default IonImageryProvider;