IonImageryProvider.js 18 KB

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