ArcGisMapServerImageryProvider.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918
  1. import Cartesian2 from "../Core/Cartesian2.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import Cartographic from "../Core/Cartographic.js";
  4. import Credit from "../Core/Credit.js";
  5. import defaultValue from "../Core/defaultValue.js";
  6. import defer from "../Core/defer.js";
  7. import defined from "../Core/defined.js";
  8. import DeveloperError from "../Core/DeveloperError.js";
  9. import Event from "../Core/Event.js";
  10. import GeographicProjection from "../Core/GeographicProjection.js";
  11. import GeographicTilingScheme from "../Core/GeographicTilingScheme.js";
  12. import CesiumMath from "../Core/Math.js";
  13. import Rectangle from "../Core/Rectangle.js";
  14. import Resource from "../Core/Resource.js";
  15. import RuntimeError from "../Core/RuntimeError.js";
  16. import TileProviderError from "../Core/TileProviderError.js";
  17. import WebMercatorProjection from "../Core/WebMercatorProjection.js";
  18. import WebMercatorTilingScheme from "../Core/WebMercatorTilingScheme.js";
  19. import DiscardMissingTileImagePolicy from "./DiscardMissingTileImagePolicy.js";
  20. import ImageryLayerFeatureInfo from "./ImageryLayerFeatureInfo.js";
  21. import ImageryProvider from "./ImageryProvider.js";
  22. /**
  23. * @typedef {Object} ArcGisMapServerImageryProvider.ConstructorOptions
  24. *
  25. * Initialization options for the ArcGisMapServerImageryProvider constructor
  26. *
  27. * @property {Resource|String} url The URL of the ArcGIS MapServer service.
  28. * @property {String} [token] The ArcGIS token used to authenticate with the ArcGIS MapServer service.
  29. * @property {TileDiscardPolicy} [tileDiscardPolicy] The policy that determines if a tile
  30. * is invalid and should be discarded. If this value is not specified, a default
  31. * {@link DiscardMissingTileImagePolicy} is used for tiled map servers, and a
  32. * {@link NeverTileDiscardPolicy} is used for non-tiled map servers. In the former case,
  33. * we request tile 0,0 at the maximum tile level and check pixels (0,0), (200,20), (20,200),
  34. * (80,110), and (160, 130). If all of these pixels are transparent, the discard check is
  35. * disabled and no tiles are discarded. If any of them have a non-transparent color, any
  36. * tile that has the same values in these pixel locations is discarded. The end result of
  37. * these defaults should be correct tile discarding for a standard ArcGIS Server. To ensure
  38. * that no tiles are discarded, construct and pass a {@link NeverTileDiscardPolicy} for this
  39. * parameter.
  40. * @property {Boolean} [usePreCachedTilesIfAvailable=true] If true, the server's pre-cached
  41. * tiles are used if they are available. If false, any pre-cached tiles are ignored and the
  42. * 'export' service is used.
  43. * @property {String} [layers] A comma-separated list of the layers to show, or undefined if all layers should be shown.
  44. * @property {Boolean} [enablePickFeatures=true] If true, {@link ArcGisMapServerImageryProvider#pickFeatures} will invoke
  45. * the Identify service on the MapServer and return the features included in the response. If false,
  46. * {@link ArcGisMapServerImageryProvider#pickFeatures} will immediately return undefined (indicating no pickable features)
  47. * without communicating with the server. Set this property to false if you don't want this provider's features to
  48. * be pickable. Can be overridden by setting the {@link ArcGisMapServerImageryProvider#enablePickFeatures} property on the object.
  49. * @property {Rectangle} [rectangle=Rectangle.MAX_VALUE] The rectangle of the layer. This parameter is ignored when accessing
  50. * a tiled layer.
  51. * @property {TilingScheme} [tilingScheme=new GeographicTilingScheme()] The tiling scheme to use to divide the world into tiles.
  52. * This parameter is ignored when accessing a tiled server.
  53. * @property {Ellipsoid} [ellipsoid] The ellipsoid. If the tilingScheme is specified and used,
  54. * this parameter is ignored and the tiling scheme's ellipsoid is used instead. If neither
  55. * parameter is specified, the WGS84 ellipsoid is used.
  56. * @property {Credit|String} [credit] A credit for the data source, which is displayed on the canvas. This parameter is ignored when accessing a tiled server.
  57. * @property {Number} [tileWidth=256] The width of each tile in pixels. This parameter is ignored when accessing a tiled server.
  58. * @property {Number} [tileHeight=256] The height of each tile in pixels. This parameter is ignored when accessing a tiled server.
  59. * @property {Number} [maximumLevel] The maximum tile level to request, or undefined if there is no maximum. This parameter is ignored when accessing
  60. * a tiled server.
  61. */
  62. /**
  63. * Provides tiled imagery hosted by an ArcGIS MapServer. By default, the server's pre-cached tiles are
  64. * used, if available.
  65. *
  66. * @alias ArcGisMapServerImageryProvider
  67. * @constructor
  68. *
  69. * @param {ArcGisMapServerImageryProvider.ConstructorOptions} options Object describing initialization options
  70. *
  71. * @see BingMapsImageryProvider
  72. * @see GoogleEarthEnterpriseMapsProvider
  73. * @see OpenStreetMapImageryProvider
  74. * @see SingleTileImageryProvider
  75. * @see TileMapServiceImageryProvider
  76. * @see WebMapServiceImageryProvider
  77. * @see WebMapTileServiceImageryProvider
  78. * @see UrlTemplateImageryProvider
  79. *
  80. *
  81. * @example
  82. * const esri = new Cesium.ArcGisMapServerImageryProvider({
  83. * url : 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
  84. * });
  85. *
  86. * @see {@link https://developers.arcgis.com/rest/|ArcGIS Server REST API}
  87. * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing}
  88. */
  89. function ArcGisMapServerImageryProvider(options) {
  90. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  91. //>>includeStart('debug', pragmas.debug);
  92. if (!defined(options.url)) {
  93. throw new DeveloperError("options.url is required.");
  94. }
  95. //>>includeEnd('debug');
  96. /**
  97. * The default alpha blending value of this provider, with 0.0 representing fully transparent and
  98. * 1.0 representing fully opaque.
  99. *
  100. * @type {Number|undefined}
  101. * @default undefined
  102. */
  103. this.defaultAlpha = undefined;
  104. /**
  105. * The default alpha blending value on the night side of the globe of this provider, with 0.0 representing fully transparent and
  106. * 1.0 representing fully opaque.
  107. *
  108. * @type {Number|undefined}
  109. * @default undefined
  110. */
  111. this.defaultNightAlpha = undefined;
  112. /**
  113. * The default alpha blending value on the day side of the globe of this provider, with 0.0 representing fully transparent and
  114. * 1.0 representing fully opaque.
  115. *
  116. * @type {Number|undefined}
  117. * @default undefined
  118. */
  119. this.defaultDayAlpha = undefined;
  120. /**
  121. * The default brightness of this provider. 1.0 uses the unmodified imagery color. Less than 1.0
  122. * makes the imagery darker while greater than 1.0 makes it brighter.
  123. *
  124. * @type {Number|undefined}
  125. * @default undefined
  126. */
  127. this.defaultBrightness = undefined;
  128. /**
  129. * The default contrast of this provider. 1.0 uses the unmodified imagery color. Less than 1.0 reduces
  130. * the contrast while greater than 1.0 increases it.
  131. *
  132. * @type {Number|undefined}
  133. * @default undefined
  134. */
  135. this.defaultContrast = undefined;
  136. /**
  137. * The default hue of this provider in radians. 0.0 uses the unmodified imagery color.
  138. *
  139. * @type {Number|undefined}
  140. * @default undefined
  141. */
  142. this.defaultHue = undefined;
  143. /**
  144. * The default saturation of this provider. 1.0 uses the unmodified imagery color. Less than 1.0 reduces the
  145. * saturation while greater than 1.0 increases it.
  146. *
  147. * @type {Number|undefined}
  148. * @default undefined
  149. */
  150. this.defaultSaturation = undefined;
  151. /**
  152. * The default gamma correction to apply to this provider. 1.0 uses the unmodified imagery color.
  153. *
  154. * @type {Number|undefined}
  155. * @default undefined
  156. */
  157. this.defaultGamma = undefined;
  158. /**
  159. * The default texture minification filter to apply to this provider.
  160. *
  161. * @type {TextureMinificationFilter}
  162. * @default undefined
  163. */
  164. this.defaultMinificationFilter = undefined;
  165. /**
  166. * The default texture magnification filter to apply to this provider.
  167. *
  168. * @type {TextureMagnificationFilter}
  169. * @default undefined
  170. */
  171. this.defaultMagnificationFilter = undefined;
  172. const resource = Resource.createIfNeeded(options.url);
  173. resource.appendForwardSlash();
  174. if (defined(options.token)) {
  175. resource.setQueryParameters({
  176. token: options.token,
  177. });
  178. }
  179. this._resource = resource;
  180. this._tileDiscardPolicy = options.tileDiscardPolicy;
  181. this._tileWidth = defaultValue(options.tileWidth, 256);
  182. this._tileHeight = defaultValue(options.tileHeight, 256);
  183. this._maximumLevel = options.maximumLevel;
  184. this._tilingScheme = defaultValue(
  185. options.tilingScheme,
  186. new GeographicTilingScheme({ ellipsoid: options.ellipsoid })
  187. );
  188. this._useTiles = defaultValue(options.usePreCachedTilesIfAvailable, true);
  189. this._rectangle = defaultValue(
  190. options.rectangle,
  191. this._tilingScheme.rectangle
  192. );
  193. this._layers = options.layers;
  194. let credit = options.credit;
  195. if (typeof credit === "string") {
  196. credit = new Credit(credit);
  197. }
  198. this._credit = credit;
  199. /**
  200. * Gets or sets a value indicating whether feature picking is enabled. If true, {@link ArcGisMapServerImageryProvider#pickFeatures} will
  201. * invoke the "identify" operation on the ArcGIS server and return the features included in the response. If false,
  202. * {@link ArcGisMapServerImageryProvider#pickFeatures} will immediately return undefined (indicating no pickable features)
  203. * without communicating with the server.
  204. * @type {Boolean}
  205. * @default true
  206. */
  207. this.enablePickFeatures = defaultValue(options.enablePickFeatures, true);
  208. this._errorEvent = new Event();
  209. this._ready = false;
  210. this._readyPromise = defer();
  211. // Grab the details of this MapServer.
  212. const that = this;
  213. let metadataError;
  214. function metadataSuccess(data) {
  215. const tileInfo = data.tileInfo;
  216. if (!defined(tileInfo)) {
  217. that._useTiles = false;
  218. } else {
  219. that._tileWidth = tileInfo.rows;
  220. that._tileHeight = tileInfo.cols;
  221. if (
  222. tileInfo.spatialReference.wkid === 102100 ||
  223. tileInfo.spatialReference.wkid === 102113
  224. ) {
  225. that._tilingScheme = new WebMercatorTilingScheme({
  226. ellipsoid: options.ellipsoid,
  227. });
  228. } else if (data.tileInfo.spatialReference.wkid === 4326) {
  229. that._tilingScheme = new GeographicTilingScheme({
  230. ellipsoid: options.ellipsoid,
  231. });
  232. } else {
  233. const message = `Tile spatial reference WKID ${data.tileInfo.spatialReference.wkid} is not supported.`;
  234. metadataError = TileProviderError.handleError(
  235. metadataError,
  236. that,
  237. that._errorEvent,
  238. message,
  239. undefined,
  240. undefined,
  241. undefined,
  242. requestMetadata
  243. );
  244. if (!metadataError.retry) {
  245. that._readyPromise.reject(new RuntimeError(message));
  246. }
  247. return;
  248. }
  249. that._maximumLevel = data.tileInfo.lods.length - 1;
  250. if (defined(data.fullExtent)) {
  251. if (
  252. defined(data.fullExtent.spatialReference) &&
  253. defined(data.fullExtent.spatialReference.wkid)
  254. ) {
  255. if (
  256. data.fullExtent.spatialReference.wkid === 102100 ||
  257. data.fullExtent.spatialReference.wkid === 102113
  258. ) {
  259. const projection = new WebMercatorProjection();
  260. const extent = data.fullExtent;
  261. const sw = projection.unproject(
  262. new Cartesian3(
  263. Math.max(
  264. extent.xmin,
  265. -that._tilingScheme.ellipsoid.maximumRadius * Math.PI
  266. ),
  267. Math.max(
  268. extent.ymin,
  269. -that._tilingScheme.ellipsoid.maximumRadius * Math.PI
  270. ),
  271. 0.0
  272. )
  273. );
  274. const ne = projection.unproject(
  275. new Cartesian3(
  276. Math.min(
  277. extent.xmax,
  278. that._tilingScheme.ellipsoid.maximumRadius * Math.PI
  279. ),
  280. Math.min(
  281. extent.ymax,
  282. that._tilingScheme.ellipsoid.maximumRadius * Math.PI
  283. ),
  284. 0.0
  285. )
  286. );
  287. that._rectangle = new Rectangle(
  288. sw.longitude,
  289. sw.latitude,
  290. ne.longitude,
  291. ne.latitude
  292. );
  293. } else if (data.fullExtent.spatialReference.wkid === 4326) {
  294. that._rectangle = Rectangle.fromDegrees(
  295. data.fullExtent.xmin,
  296. data.fullExtent.ymin,
  297. data.fullExtent.xmax,
  298. data.fullExtent.ymax
  299. );
  300. } else {
  301. const extentMessage = `fullExtent.spatialReference WKID ${data.fullExtent.spatialReference.wkid} is not supported.`;
  302. metadataError = TileProviderError.handleError(
  303. metadataError,
  304. that,
  305. that._errorEvent,
  306. extentMessage,
  307. undefined,
  308. undefined,
  309. undefined,
  310. requestMetadata
  311. );
  312. if (!metadataError.retry) {
  313. that._readyPromise.reject(new RuntimeError(extentMessage));
  314. }
  315. return;
  316. }
  317. }
  318. } else {
  319. that._rectangle = that._tilingScheme.rectangle;
  320. }
  321. // Install the default tile discard policy if none has been supplied.
  322. if (!defined(that._tileDiscardPolicy)) {
  323. that._tileDiscardPolicy = new DiscardMissingTileImagePolicy({
  324. missingImageUrl: buildImageResource(that, 0, 0, that._maximumLevel)
  325. .url,
  326. pixelsToCheck: [
  327. new Cartesian2(0, 0),
  328. new Cartesian2(200, 20),
  329. new Cartesian2(20, 200),
  330. new Cartesian2(80, 110),
  331. new Cartesian2(160, 130),
  332. ],
  333. disableCheckIfAllPixelsAreTransparent: true,
  334. });
  335. }
  336. that._useTiles = true;
  337. }
  338. if (defined(data.copyrightText) && data.copyrightText.length > 0) {
  339. that._credit = new Credit(data.copyrightText);
  340. }
  341. that._ready = true;
  342. that._readyPromise.resolve(true);
  343. TileProviderError.handleSuccess(metadataError);
  344. }
  345. function metadataFailure(e) {
  346. const message = `An error occurred while accessing ${that._resource.url}.`;
  347. metadataError = TileProviderError.handleError(
  348. metadataError,
  349. that,
  350. that._errorEvent,
  351. message,
  352. undefined,
  353. undefined,
  354. undefined,
  355. requestMetadata
  356. );
  357. that._readyPromise.reject(new RuntimeError(message));
  358. }
  359. function requestMetadata() {
  360. const resource = that._resource.getDerivedResource({
  361. queryParameters: {
  362. f: "json",
  363. },
  364. });
  365. resource
  366. .fetchJsonp()
  367. .then(function (result) {
  368. metadataSuccess(result);
  369. })
  370. .catch(function (e) {
  371. metadataFailure(e);
  372. });
  373. }
  374. if (this._useTiles) {
  375. requestMetadata();
  376. } else {
  377. this._ready = true;
  378. this._readyPromise.resolve(true);
  379. }
  380. }
  381. function buildImageResource(imageryProvider, x, y, level, request) {
  382. let resource;
  383. if (imageryProvider._useTiles) {
  384. resource = imageryProvider._resource.getDerivedResource({
  385. url: `tile/${level}/${y}/${x}`,
  386. request: request,
  387. });
  388. } else {
  389. const nativeRectangle = imageryProvider._tilingScheme.tileXYToNativeRectangle(
  390. x,
  391. y,
  392. level
  393. );
  394. const bbox = `${nativeRectangle.west},${nativeRectangle.south},${nativeRectangle.east},${nativeRectangle.north}`;
  395. const query = {
  396. bbox: bbox,
  397. size: `${imageryProvider._tileWidth},${imageryProvider._tileHeight}`,
  398. format: "png32",
  399. transparent: true,
  400. f: "image",
  401. };
  402. if (
  403. imageryProvider._tilingScheme.projection instanceof GeographicProjection
  404. ) {
  405. query.bboxSR = 4326;
  406. query.imageSR = 4326;
  407. } else {
  408. query.bboxSR = 3857;
  409. query.imageSR = 3857;
  410. }
  411. if (imageryProvider.layers) {
  412. query.layers = `show:${imageryProvider.layers}`;
  413. }
  414. resource = imageryProvider._resource.getDerivedResource({
  415. url: "export",
  416. request: request,
  417. queryParameters: query,
  418. });
  419. }
  420. return resource;
  421. }
  422. Object.defineProperties(ArcGisMapServerImageryProvider.prototype, {
  423. /**
  424. * Gets the URL of the ArcGIS MapServer.
  425. * @memberof ArcGisMapServerImageryProvider.prototype
  426. * @type {String}
  427. * @readonly
  428. */
  429. url: {
  430. get: function () {
  431. return this._resource._url;
  432. },
  433. },
  434. /**
  435. * Gets the ArcGIS token used to authenticate with the ArcGis MapServer service.
  436. * @memberof ArcGisMapServerImageryProvider.prototype
  437. * @type {String}
  438. * @readonly
  439. */
  440. token: {
  441. get: function () {
  442. return this._resource.queryParameters.token;
  443. },
  444. },
  445. /**
  446. * Gets the proxy used by this provider.
  447. * @memberof ArcGisMapServerImageryProvider.prototype
  448. * @type {Proxy}
  449. * @readonly
  450. */
  451. proxy: {
  452. get: function () {
  453. return this._resource.proxy;
  454. },
  455. },
  456. /**
  457. * Gets the width of each tile, in pixels. This function should
  458. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  459. * @memberof ArcGisMapServerImageryProvider.prototype
  460. * @type {Number}
  461. * @readonly
  462. */
  463. tileWidth: {
  464. get: function () {
  465. //>>includeStart('debug', pragmas.debug);
  466. if (!this._ready) {
  467. throw new DeveloperError(
  468. "tileWidth must not be called before the imagery provider is ready."
  469. );
  470. }
  471. //>>includeEnd('debug');
  472. return this._tileWidth;
  473. },
  474. },
  475. /**
  476. * Gets the height of each tile, in pixels. This function should
  477. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  478. * @memberof ArcGisMapServerImageryProvider.prototype
  479. * @type {Number}
  480. * @readonly
  481. */
  482. tileHeight: {
  483. get: function () {
  484. //>>includeStart('debug', pragmas.debug);
  485. if (!this._ready) {
  486. throw new DeveloperError(
  487. "tileHeight must not be called before the imagery provider is ready."
  488. );
  489. }
  490. //>>includeEnd('debug');
  491. return this._tileHeight;
  492. },
  493. },
  494. /**
  495. * Gets the maximum level-of-detail that can be requested. This function should
  496. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  497. * @memberof ArcGisMapServerImageryProvider.prototype
  498. * @type {Number|undefined}
  499. * @readonly
  500. */
  501. maximumLevel: {
  502. get: function () {
  503. //>>includeStart('debug', pragmas.debug);
  504. if (!this._ready) {
  505. throw new DeveloperError(
  506. "maximumLevel must not be called before the imagery provider is ready."
  507. );
  508. }
  509. //>>includeEnd('debug');
  510. return this._maximumLevel;
  511. },
  512. },
  513. /**
  514. * Gets the minimum level-of-detail that can be requested. This function should
  515. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  516. * @memberof ArcGisMapServerImageryProvider.prototype
  517. * @type {Number}
  518. * @readonly
  519. */
  520. minimumLevel: {
  521. get: function () {
  522. //>>includeStart('debug', pragmas.debug);
  523. if (!this._ready) {
  524. throw new DeveloperError(
  525. "minimumLevel must not be called before the imagery provider is ready."
  526. );
  527. }
  528. //>>includeEnd('debug');
  529. return 0;
  530. },
  531. },
  532. /**
  533. * Gets the tiling scheme used by this provider. This function should
  534. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  535. * @memberof ArcGisMapServerImageryProvider.prototype
  536. * @type {TilingScheme}
  537. * @readonly
  538. */
  539. tilingScheme: {
  540. get: function () {
  541. //>>includeStart('debug', pragmas.debug);
  542. if (!this._ready) {
  543. throw new DeveloperError(
  544. "tilingScheme must not be called before the imagery provider is ready."
  545. );
  546. }
  547. //>>includeEnd('debug');
  548. return this._tilingScheme;
  549. },
  550. },
  551. /**
  552. * Gets the rectangle, in radians, of the imagery provided by this instance. This function should
  553. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  554. * @memberof ArcGisMapServerImageryProvider.prototype
  555. * @type {Rectangle}
  556. * @readonly
  557. */
  558. rectangle: {
  559. get: function () {
  560. //>>includeStart('debug', pragmas.debug);
  561. if (!this._ready) {
  562. throw new DeveloperError(
  563. "rectangle must not be called before the imagery provider is ready."
  564. );
  565. }
  566. //>>includeEnd('debug');
  567. return this._rectangle;
  568. },
  569. },
  570. /**
  571. * Gets the tile discard policy. If not undefined, the discard policy is responsible
  572. * for filtering out "missing" tiles via its shouldDiscardImage function. If this function
  573. * returns undefined, no tiles are filtered. This function should
  574. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  575. * @memberof ArcGisMapServerImageryProvider.prototype
  576. * @type {TileDiscardPolicy}
  577. * @readonly
  578. */
  579. tileDiscardPolicy: {
  580. get: function () {
  581. //>>includeStart('debug', pragmas.debug);
  582. if (!this._ready) {
  583. throw new DeveloperError(
  584. "tileDiscardPolicy must not be called before the imagery provider is ready."
  585. );
  586. }
  587. //>>includeEnd('debug');
  588. return this._tileDiscardPolicy;
  589. },
  590. },
  591. /**
  592. * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing
  593. * to the event, you will be notified of the error and can potentially recover from it. Event listeners
  594. * are passed an instance of {@link TileProviderError}.
  595. * @memberof ArcGisMapServerImageryProvider.prototype
  596. * @type {Event}
  597. * @readonly
  598. */
  599. errorEvent: {
  600. get: function () {
  601. return this._errorEvent;
  602. },
  603. },
  604. /**
  605. * Gets a value indicating whether or not the provider is ready for use.
  606. * @memberof ArcGisMapServerImageryProvider.prototype
  607. * @type {Boolean}
  608. * @readonly
  609. */
  610. ready: {
  611. get: function () {
  612. return this._ready;
  613. },
  614. },
  615. /**
  616. * Gets a promise that resolves to true when the provider is ready for use.
  617. * @memberof ArcGisMapServerImageryProvider.prototype
  618. * @type {Promise.<Boolean>}
  619. * @readonly
  620. */
  621. readyPromise: {
  622. get: function () {
  623. return this._readyPromise.promise;
  624. },
  625. },
  626. /**
  627. * Gets the credit to display when this imagery provider is active. Typically this is used to credit
  628. * the source of the imagery. This function should not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  629. * @memberof ArcGisMapServerImageryProvider.prototype
  630. * @type {Credit}
  631. * @readonly
  632. */
  633. credit: {
  634. get: function () {
  635. return this._credit;
  636. },
  637. },
  638. /**
  639. * Gets a value indicating whether this imagery provider is using pre-cached tiles from the
  640. * ArcGIS MapServer. If the imagery provider is not yet ready ({@link ArcGisMapServerImageryProvider#ready}), this function
  641. * will return the value of `options.usePreCachedTilesIfAvailable`, even if the MapServer does
  642. * not have pre-cached tiles.
  643. * @memberof ArcGisMapServerImageryProvider.prototype
  644. *
  645. * @type {Boolean}
  646. * @readonly
  647. * @default true
  648. */
  649. usingPrecachedTiles: {
  650. get: function () {
  651. return this._useTiles;
  652. },
  653. },
  654. /**
  655. * Gets a value indicating whether or not the images provided by this imagery provider
  656. * include an alpha channel. If this property is false, an alpha channel, if present, will
  657. * be ignored. If this property is true, any images without an alpha channel will be treated
  658. * as if their alpha is 1.0 everywhere. When this property is false, memory usage
  659. * and texture upload time are reduced.
  660. * @memberof ArcGisMapServerImageryProvider.prototype
  661. *
  662. * @type {Boolean}
  663. * @readonly
  664. * @default true
  665. */
  666. hasAlphaChannel: {
  667. get: function () {
  668. return true;
  669. },
  670. },
  671. /**
  672. * Gets the comma-separated list of layer IDs to show.
  673. * @memberof ArcGisMapServerImageryProvider.prototype
  674. *
  675. * @type {String}
  676. */
  677. layers: {
  678. get: function () {
  679. return this._layers;
  680. },
  681. },
  682. });
  683. /**
  684. * Gets the credits to be displayed when a given tile is displayed.
  685. *
  686. * @param {Number} x The tile X coordinate.
  687. * @param {Number} y The tile Y coordinate.
  688. * @param {Number} level The tile level;
  689. * @returns {Credit[]} The credits to be displayed when the tile is displayed.
  690. *
  691. * @exception {DeveloperError} <code>getTileCredits</code> must not be called before the imagery provider is ready.
  692. */
  693. ArcGisMapServerImageryProvider.prototype.getTileCredits = function (
  694. x,
  695. y,
  696. level
  697. ) {
  698. return undefined;
  699. };
  700. /**
  701. * Requests the image for a given tile. This function should
  702. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  703. *
  704. * @param {Number} x The tile X coordinate.
  705. * @param {Number} y The tile Y coordinate.
  706. * @param {Number} level The tile level.
  707. * @param {Request} [request] The request object. Intended for internal use only.
  708. * @returns {Promise.<ImageryTypes>|undefined} A promise for the image that will resolve when the image is available, or
  709. * undefined if there are too many active requests to the server, and the request should be retried later.
  710. *
  711. * @exception {DeveloperError} <code>requestImage</code> must not be called before the imagery provider is ready.
  712. */
  713. ArcGisMapServerImageryProvider.prototype.requestImage = function (
  714. x,
  715. y,
  716. level,
  717. request
  718. ) {
  719. //>>includeStart('debug', pragmas.debug);
  720. if (!this._ready) {
  721. throw new DeveloperError(
  722. "requestImage must not be called before the imagery provider is ready."
  723. );
  724. }
  725. //>>includeEnd('debug');
  726. return ImageryProvider.loadImage(
  727. this,
  728. buildImageResource(this, x, y, level, request)
  729. );
  730. };
  731. /**
  732. /**
  733. * Asynchronously determines what features, if any, are located at a given longitude and latitude within
  734. * a tile. This function should not be called before {@link ImageryProvider#ready} returns true.
  735. *
  736. * @param {Number} x The tile X coordinate.
  737. * @param {Number} y The tile Y coordinate.
  738. * @param {Number} level The tile level.
  739. * @param {Number} longitude The longitude at which to pick features.
  740. * @param {Number} latitude The latitude at which to pick features.
  741. * @return {Promise.<ImageryLayerFeatureInfo[]>|undefined} A promise for the picked features that will resolve when the asynchronous
  742. * picking completes. The resolved value is an array of {@link ImageryLayerFeatureInfo}
  743. * instances. The array may be empty if no features are found at the given location.
  744. *
  745. * @exception {DeveloperError} <code>pickFeatures</code> must not be called before the imagery provider is ready.
  746. */
  747. ArcGisMapServerImageryProvider.prototype.pickFeatures = function (
  748. x,
  749. y,
  750. level,
  751. longitude,
  752. latitude
  753. ) {
  754. //>>includeStart('debug', pragmas.debug);
  755. if (!this._ready) {
  756. throw new DeveloperError(
  757. "pickFeatures must not be called before the imagery provider is ready."
  758. );
  759. }
  760. //>>includeEnd('debug');
  761. if (!this.enablePickFeatures) {
  762. return undefined;
  763. }
  764. const rectangle = this._tilingScheme.tileXYToNativeRectangle(x, y, level);
  765. let horizontal;
  766. let vertical;
  767. let sr;
  768. if (this._tilingScheme.projection instanceof GeographicProjection) {
  769. horizontal = CesiumMath.toDegrees(longitude);
  770. vertical = CesiumMath.toDegrees(latitude);
  771. sr = "4326";
  772. } else {
  773. const projected = this._tilingScheme.projection.project(
  774. new Cartographic(longitude, latitude, 0.0)
  775. );
  776. horizontal = projected.x;
  777. vertical = projected.y;
  778. sr = "3857";
  779. }
  780. let layers = "visible";
  781. if (defined(this._layers)) {
  782. layers += `:${this._layers}`;
  783. }
  784. const query = {
  785. f: "json",
  786. tolerance: 2,
  787. geometryType: "esriGeometryPoint",
  788. geometry: `${horizontal},${vertical}`,
  789. mapExtent: `${rectangle.west},${rectangle.south},${rectangle.east},${rectangle.north}`,
  790. imageDisplay: `${this._tileWidth},${this._tileHeight},96`,
  791. sr: sr,
  792. layers: layers,
  793. };
  794. const resource = this._resource.getDerivedResource({
  795. url: "identify",
  796. queryParameters: query,
  797. });
  798. return resource.fetchJson().then(function (json) {
  799. const result = [];
  800. const features = json.results;
  801. if (!defined(features)) {
  802. return result;
  803. }
  804. for (let i = 0; i < features.length; ++i) {
  805. const feature = features[i];
  806. const featureInfo = new ImageryLayerFeatureInfo();
  807. featureInfo.data = feature;
  808. featureInfo.name = feature.value;
  809. featureInfo.properties = feature.attributes;
  810. featureInfo.configureDescriptionFromProperties(feature.attributes);
  811. // If this is a point feature, use the coordinates of the point.
  812. if (feature.geometryType === "esriGeometryPoint" && feature.geometry) {
  813. const wkid =
  814. feature.geometry.spatialReference &&
  815. feature.geometry.spatialReference.wkid
  816. ? feature.geometry.spatialReference.wkid
  817. : 4326;
  818. if (wkid === 4326 || wkid === 4283) {
  819. featureInfo.position = Cartographic.fromDegrees(
  820. feature.geometry.x,
  821. feature.geometry.y,
  822. feature.geometry.z
  823. );
  824. } else if (wkid === 102100 || wkid === 900913 || wkid === 3857) {
  825. const projection = new WebMercatorProjection();
  826. featureInfo.position = projection.unproject(
  827. new Cartesian3(
  828. feature.geometry.x,
  829. feature.geometry.y,
  830. feature.geometry.z
  831. )
  832. );
  833. }
  834. }
  835. result.push(featureInfo);
  836. }
  837. return result;
  838. });
  839. };
  840. export default ArcGisMapServerImageryProvider;