ArcGisMapServerImageryProvider.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  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 defined from '../Core/defined.js';
  7. import defineProperties from '../Core/defineProperties.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 when from '../ThirdParty/when.js';
  20. import DiscardMissingTileImagePolicy from './DiscardMissingTileImagePolicy.js';
  21. import ImageryLayerFeatureInfo from './ImageryLayerFeatureInfo.js';
  22. import ImageryProvider from './ImageryProvider.js';
  23. /**
  24. * Provides tiled imagery hosted by an ArcGIS MapServer. By default, the server's pre-cached tiles are
  25. * used, if available.
  26. *
  27. * @alias ArcGisMapServerImageryProvider
  28. * @constructor
  29. *
  30. * @param {Object} options Object with the following properties:
  31. * @param {Resource|String} options.url The URL of the ArcGIS MapServer service.
  32. * @param {String} [options.token] The ArcGIS token used to authenticate with the ArcGIS MapServer service.
  33. * @param {TileDiscardPolicy} [options.tileDiscardPolicy] The policy that determines if a tile
  34. * is invalid and should be discarded. If this value is not specified, a default
  35. * {@link DiscardMissingTileImagePolicy} is used for tiled map servers, and a
  36. * {@link NeverTileDiscardPolicy} is used for non-tiled map servers. In the former case,
  37. * we request tile 0,0 at the maximum tile level and check pixels (0,0), (200,20), (20,200),
  38. * (80,110), and (160, 130). If all of these pixels are transparent, the discard check is
  39. * disabled and no tiles are discarded. If any of them have a non-transparent color, any
  40. * tile that has the same values in these pixel locations is discarded. The end result of
  41. * these defaults should be correct tile discarding for a standard ArcGIS Server. To ensure
  42. * that no tiles are discarded, construct and pass a {@link NeverTileDiscardPolicy} for this
  43. * parameter.
  44. * @param {Boolean} [options.usePreCachedTilesIfAvailable=true] If true, the server's pre-cached
  45. * tiles are used if they are available. If false, any pre-cached tiles are ignored and the
  46. * 'export' service is used.
  47. * @param {String} [options.layers] A comma-separated list of the layers to show, or undefined if all layers should be shown.
  48. * @param {Boolean} [options.enablePickFeatures=true] If true, {@link ArcGisMapServerImageryProvider#pickFeatures} will invoke
  49. * the Identify service on the MapServer and return the features included in the response. If false,
  50. * {@link ArcGisMapServerImageryProvider#pickFeatures} will immediately return undefined (indicating no pickable features)
  51. * without communicating with the server. Set this property to false if you don't want this provider's features to
  52. * be pickable. Can be overridden by setting the {@link ArcGisMapServerImageryProvider#enablePickFeatures} property on the object.
  53. * @param {Rectangle} [options.rectangle=Rectangle.MAX_VALUE] The rectangle of the layer. This parameter is ignored when accessing
  54. * a tiled layer.
  55. * @param {TilingScheme} [options.tilingScheme=new GeographicTilingScheme()] The tiling scheme to use to divide the world into tiles.
  56. * This parameter is ignored when accessing a tiled server.
  57. * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If the tilingScheme is specified and used,
  58. * this parameter is ignored and the tiling scheme's ellipsoid is used instead. If neither
  59. * parameter is specified, the WGS84 ellipsoid is used.
  60. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas. This parameter is ignored when accessing a tiled server.
  61. * @param {Number} [options.tileWidth=256] The width of each tile in pixels. This parameter is ignored when accessing a tiled server.
  62. * @param {Number} [options.tileHeight=256] The height of each tile in pixels. This parameter is ignored when accessing a tiled server.
  63. * @param {Number} [options.maximumLevel] The maximum tile level to request, or undefined if there is no maximum. This parameter is ignored when accessing
  64. * a tiled server.
  65. *
  66. * @see BingMapsImageryProvider
  67. * @see GoogleEarthEnterpriseMapsProvider
  68. * @see OpenStreetMapImageryProvider
  69. * @see SingleTileImageryProvider
  70. * @see TileMapServiceImageryProvider
  71. * @see WebMapServiceImageryProvider
  72. * @see WebMapTileServiceImageryProvider
  73. * @see UrlTemplateImageryProvider
  74. *
  75. *
  76. * @example
  77. * var esri = new Cesium.ArcGisMapServerImageryProvider({
  78. * url : 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
  79. * });
  80. *
  81. * @see {@link https://developers.arcgis.com/rest/|ArcGIS Server REST API}
  82. * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing}
  83. */
  84. function ArcGisMapServerImageryProvider(options) {
  85. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  86. //>>includeStart('debug', pragmas.debug);
  87. if (!defined(options.url)) {
  88. throw new DeveloperError('options.url is required.');
  89. }
  90. //>>includeEnd('debug');
  91. var resource = Resource.createIfNeeded(options.url);
  92. resource.appendForwardSlash();
  93. if (defined(options.token)) {
  94. resource.setQueryParameters({
  95. token: options.token
  96. });
  97. }
  98. this._resource = resource;
  99. this._tileDiscardPolicy = options.tileDiscardPolicy;
  100. this._tileWidth = defaultValue(options.tileWidth, 256);
  101. this._tileHeight = defaultValue(options.tileHeight, 256);
  102. this._maximumLevel = options.maximumLevel;
  103. this._tilingScheme = defaultValue(options.tilingScheme, new GeographicTilingScheme({ ellipsoid : options.ellipsoid }));
  104. this._useTiles = defaultValue(options.usePreCachedTilesIfAvailable, true);
  105. this._rectangle = defaultValue(options.rectangle, this._tilingScheme.rectangle);
  106. this._layers = options.layers;
  107. var credit = options.credit;
  108. if (typeof credit === 'string') {
  109. credit = new Credit(credit);
  110. }
  111. this._credit = credit;
  112. /**
  113. * Gets or sets a value indicating whether feature picking is enabled. If true, {@link ArcGisMapServerImageryProvider#pickFeatures} will
  114. * invoke the "identify" operation on the ArcGIS server and return the features included in the response. If false,
  115. * {@link ArcGisMapServerImageryProvider#pickFeatures} will immediately return undefined (indicating no pickable features)
  116. * without communicating with the server.
  117. * @type {Boolean}
  118. * @default true
  119. */
  120. this.enablePickFeatures = defaultValue(options.enablePickFeatures, true);
  121. this._errorEvent = new Event();
  122. this._ready = false;
  123. this._readyPromise = when.defer();
  124. // Grab the details of this MapServer.
  125. var that = this;
  126. var metadataError;
  127. function metadataSuccess(data) {
  128. var tileInfo = data.tileInfo;
  129. if (!defined(tileInfo)) {
  130. that._useTiles = false;
  131. } else {
  132. that._tileWidth = tileInfo.rows;
  133. that._tileHeight = tileInfo.cols;
  134. if (tileInfo.spatialReference.wkid === 102100 ||
  135. tileInfo.spatialReference.wkid === 102113) {
  136. that._tilingScheme = new WebMercatorTilingScheme({ ellipsoid : options.ellipsoid });
  137. } else if (data.tileInfo.spatialReference.wkid === 4326) {
  138. that._tilingScheme = new GeographicTilingScheme({ ellipsoid : options.ellipsoid });
  139. } else {
  140. var message = 'Tile spatial reference WKID ' + data.tileInfo.spatialReference.wkid + ' is not supported.';
  141. metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata);
  142. return;
  143. }
  144. that._maximumLevel = data.tileInfo.lods.length - 1;
  145. if (defined(data.fullExtent)) {
  146. if (defined(data.fullExtent.spatialReference) && defined(data.fullExtent.spatialReference.wkid)) {
  147. if (data.fullExtent.spatialReference.wkid === 102100 ||
  148. data.fullExtent.spatialReference.wkid === 102113) {
  149. var projection = new WebMercatorProjection();
  150. var extent = data.fullExtent;
  151. var sw = projection.unproject(new Cartesian3(Math.max(extent.xmin, -that._tilingScheme.ellipsoid.maximumRadius * Math.PI), Math.max(extent.ymin, -that._tilingScheme.ellipsoid.maximumRadius * Math.PI), 0.0));
  152. var ne = projection.unproject(new Cartesian3(Math.min(extent.xmax, that._tilingScheme.ellipsoid.maximumRadius * Math.PI), Math.min(extent.ymax, that._tilingScheme.ellipsoid.maximumRadius * Math.PI), 0.0));
  153. that._rectangle = new Rectangle(sw.longitude, sw.latitude, ne.longitude, ne.latitude);
  154. } else if (data.fullExtent.spatialReference.wkid === 4326) {
  155. that._rectangle = Rectangle.fromDegrees(data.fullExtent.xmin, data.fullExtent.ymin, data.fullExtent.xmax, data.fullExtent.ymax);
  156. } else {
  157. var extentMessage = 'fullExtent.spatialReference WKID ' + data.fullExtent.spatialReference.wkid + ' is not supported.';
  158. metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, extentMessage, undefined, undefined, undefined, requestMetadata);
  159. return;
  160. }
  161. }
  162. } else {
  163. that._rectangle = that._tilingScheme.rectangle;
  164. }
  165. // Install the default tile discard policy if none has been supplied.
  166. if (!defined(that._tileDiscardPolicy)) {
  167. that._tileDiscardPolicy = new DiscardMissingTileImagePolicy({
  168. missingImageUrl : buildImageResource(that, 0, 0, that._maximumLevel).url,
  169. pixelsToCheck : [new Cartesian2(0, 0), new Cartesian2(200, 20), new Cartesian2(20, 200), new Cartesian2(80, 110), new Cartesian2(160, 130)],
  170. disableCheckIfAllPixelsAreTransparent : true
  171. });
  172. }
  173. that._useTiles = true;
  174. }
  175. if (defined(data.copyrightText) && data.copyrightText.length > 0) {
  176. that._credit = new Credit(data.copyrightText);
  177. }
  178. that._ready = true;
  179. that._readyPromise.resolve(true);
  180. TileProviderError.handleSuccess(metadataError);
  181. }
  182. function metadataFailure(e) {
  183. var message = 'An error occurred while accessing ' + that._resource.url + '.';
  184. metadataError = TileProviderError.handleError(metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata);
  185. that._readyPromise.reject(new RuntimeError(message));
  186. }
  187. function requestMetadata() {
  188. var resource = that._resource.getDerivedResource({
  189. queryParameters: {
  190. f: 'json'
  191. }
  192. });
  193. var metadata = resource.fetchJsonp();
  194. when(metadata, metadataSuccess, metadataFailure);
  195. }
  196. if (this._useTiles) {
  197. requestMetadata();
  198. } else {
  199. this._ready = true;
  200. this._readyPromise.resolve(true);
  201. }
  202. }
  203. function buildImageResource(imageryProvider, x, y, level, request) {
  204. var resource;
  205. if (imageryProvider._useTiles) {
  206. resource = imageryProvider._resource.getDerivedResource({
  207. url: 'tile/' + level + '/' + y + '/' + x,
  208. request: request
  209. });
  210. } else {
  211. var nativeRectangle = imageryProvider._tilingScheme.tileXYToNativeRectangle(x, y, level);
  212. var bbox = nativeRectangle.west + ',' + nativeRectangle.south + ',' + nativeRectangle.east + ',' + nativeRectangle.north;
  213. var query = {
  214. bbox: bbox,
  215. size: imageryProvider._tileWidth + ',' + imageryProvider._tileHeight,
  216. format: 'png',
  217. transparent: true,
  218. f: 'image'
  219. };
  220. if (imageryProvider._tilingScheme.projection instanceof GeographicProjection) {
  221. query.bboxSR = 4326;
  222. query.imageSR = 4326;
  223. } else {
  224. query.bboxSR = 3857;
  225. query.imageSR = 3857;
  226. }
  227. if (imageryProvider.layers) {
  228. query.layers = 'show:' + imageryProvider.layers;
  229. }
  230. resource = imageryProvider._resource.getDerivedResource({
  231. url: 'export',
  232. request: request,
  233. queryParameters: query
  234. });
  235. }
  236. return resource;
  237. }
  238. defineProperties(ArcGisMapServerImageryProvider.prototype, {
  239. /**
  240. * Gets the URL of the ArcGIS MapServer.
  241. * @memberof ArcGisMapServerImageryProvider.prototype
  242. * @type {String}
  243. * @readonly
  244. */
  245. url : {
  246. get : function() {
  247. return this._resource._url;
  248. }
  249. },
  250. /**
  251. * Gets the ArcGIS token used to authenticate with the ArcGis MapServer service.
  252. * @memberof ArcGisMapServerImageryProvider.prototype
  253. * @type {String}
  254. * @readonly
  255. */
  256. token : {
  257. get : function() {
  258. return this._resource.queryParameters.token;
  259. }
  260. },
  261. /**
  262. * Gets the proxy used by this provider.
  263. * @memberof ArcGisMapServerImageryProvider.prototype
  264. * @type {Proxy}
  265. * @readonly
  266. */
  267. proxy : {
  268. get : function() {
  269. return this._resource.proxy;
  270. }
  271. },
  272. /**
  273. * Gets the width of each tile, in pixels. This function should
  274. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  275. * @memberof ArcGisMapServerImageryProvider.prototype
  276. * @type {Number}
  277. * @readonly
  278. */
  279. tileWidth : {
  280. get : function() {
  281. //>>includeStart('debug', pragmas.debug);
  282. if (!this._ready) {
  283. throw new DeveloperError('tileWidth must not be called before the imagery provider is ready.');
  284. }
  285. //>>includeEnd('debug');
  286. return this._tileWidth;
  287. }
  288. },
  289. /**
  290. * Gets the height of each tile, in pixels. This function should
  291. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  292. * @memberof ArcGisMapServerImageryProvider.prototype
  293. * @type {Number}
  294. * @readonly
  295. */
  296. tileHeight: {
  297. get : function() {
  298. //>>includeStart('debug', pragmas.debug);
  299. if (!this._ready) {
  300. throw new DeveloperError('tileHeight must not be called before the imagery provider is ready.');
  301. }
  302. //>>includeEnd('debug');
  303. return this._tileHeight;
  304. }
  305. },
  306. /**
  307. * Gets the maximum level-of-detail that can be requested. This function should
  308. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  309. * @memberof ArcGisMapServerImageryProvider.prototype
  310. * @type {Number}
  311. * @readonly
  312. */
  313. maximumLevel : {
  314. get : function() {
  315. //>>includeStart('debug', pragmas.debug);
  316. if (!this._ready) {
  317. throw new DeveloperError('maximumLevel must not be called before the imagery provider is ready.');
  318. }
  319. //>>includeEnd('debug');
  320. return this._maximumLevel;
  321. }
  322. },
  323. /**
  324. * Gets the minimum level-of-detail that can be requested. This function should
  325. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  326. * @memberof ArcGisMapServerImageryProvider.prototype
  327. * @type {Number}
  328. * @readonly
  329. */
  330. minimumLevel : {
  331. get : function() {
  332. //>>includeStart('debug', pragmas.debug);
  333. if (!this._ready) {
  334. throw new DeveloperError('minimumLevel must not be called before the imagery provider is ready.');
  335. }
  336. //>>includeEnd('debug');
  337. return 0;
  338. }
  339. },
  340. /**
  341. * Gets the tiling scheme used by this provider. This function should
  342. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  343. * @memberof ArcGisMapServerImageryProvider.prototype
  344. * @type {TilingScheme}
  345. * @readonly
  346. */
  347. tilingScheme : {
  348. get : function() {
  349. //>>includeStart('debug', pragmas.debug);
  350. if (!this._ready) {
  351. throw new DeveloperError('tilingScheme must not be called before the imagery provider is ready.');
  352. }
  353. //>>includeEnd('debug');
  354. return this._tilingScheme;
  355. }
  356. },
  357. /**
  358. * Gets the rectangle, in radians, of the imagery provided by this instance. This function should
  359. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  360. * @memberof ArcGisMapServerImageryProvider.prototype
  361. * @type {Rectangle}
  362. * @readonly
  363. */
  364. rectangle : {
  365. get : function() {
  366. //>>includeStart('debug', pragmas.debug);
  367. if (!this._ready) {
  368. throw new DeveloperError('rectangle must not be called before the imagery provider is ready.');
  369. }
  370. //>>includeEnd('debug');
  371. return this._rectangle;
  372. }
  373. },
  374. /**
  375. * Gets the tile discard policy. If not undefined, the discard policy is responsible
  376. * for filtering out "missing" tiles via its shouldDiscardImage function. If this function
  377. * returns undefined, no tiles are filtered. This function should
  378. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  379. * @memberof ArcGisMapServerImageryProvider.prototype
  380. * @type {TileDiscardPolicy}
  381. * @readonly
  382. */
  383. tileDiscardPolicy : {
  384. get : function() {
  385. //>>includeStart('debug', pragmas.debug);
  386. if (!this._ready) {
  387. throw new DeveloperError('tileDiscardPolicy must not be called before the imagery provider is ready.');
  388. }
  389. //>>includeEnd('debug');
  390. return this._tileDiscardPolicy;
  391. }
  392. },
  393. /**
  394. * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing
  395. * to the event, you will be notified of the error and can potentially recover from it. Event listeners
  396. * are passed an instance of {@link TileProviderError}.
  397. * @memberof ArcGisMapServerImageryProvider.prototype
  398. * @type {Event}
  399. * @readonly
  400. */
  401. errorEvent : {
  402. get : function() {
  403. return this._errorEvent;
  404. }
  405. },
  406. /**
  407. * Gets a value indicating whether or not the provider is ready for use.
  408. * @memberof ArcGisMapServerImageryProvider.prototype
  409. * @type {Boolean}
  410. * @readonly
  411. */
  412. ready : {
  413. get : function() {
  414. return this._ready;
  415. }
  416. },
  417. /**
  418. * Gets a promise that resolves to true when the provider is ready for use.
  419. * @memberof ArcGisMapServerImageryProvider.prototype
  420. * @type {Promise.<Boolean>}
  421. * @readonly
  422. */
  423. readyPromise : {
  424. get : function() {
  425. return this._readyPromise.promise;
  426. }
  427. },
  428. /**
  429. * Gets the credit to display when this imagery provider is active. Typically this is used to credit
  430. * the source of the imagery. This function should not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  431. * @memberof ArcGisMapServerImageryProvider.prototype
  432. * @type {Credit}
  433. * @readonly
  434. */
  435. credit : {
  436. get : function() {
  437. return this._credit;
  438. }
  439. },
  440. /**
  441. * Gets a value indicating whether this imagery provider is using pre-cached tiles from the
  442. * ArcGIS MapServer. If the imagery provider is not yet ready ({@link ArcGisMapServerImageryProvider#ready}), this function
  443. * will return the value of `options.usePreCachedTilesIfAvailable`, even if the MapServer does
  444. * not have pre-cached tiles.
  445. * @memberof ArcGisMapServerImageryProvider.prototype
  446. *
  447. * @type {Boolean}
  448. * @readonly
  449. * @default true
  450. */
  451. usingPrecachedTiles : {
  452. get : function() {
  453. return this._useTiles;
  454. }
  455. },
  456. /**
  457. * Gets a value indicating whether or not the images provided by this imagery provider
  458. * include an alpha channel. If this property is false, an alpha channel, if present, will
  459. * be ignored. If this property is true, any images without an alpha channel will be treated
  460. * as if their alpha is 1.0 everywhere. When this property is false, memory usage
  461. * and texture upload time are reduced.
  462. * @memberof ArcGisMapServerImageryProvider.prototype
  463. *
  464. * @type {Boolean}
  465. * @readonly
  466. * @default true
  467. */
  468. hasAlphaChannel : {
  469. get : function() {
  470. return true;
  471. }
  472. },
  473. /**
  474. * Gets the comma-separated list of layer IDs to show.
  475. * @memberof ArcGisMapServerImageryProvider.prototype
  476. *
  477. * @type {String}
  478. */
  479. layers : {
  480. get : function() {
  481. return this._layers;
  482. }
  483. }
  484. });
  485. /**
  486. * Gets the credits to be displayed when a given tile is displayed.
  487. *
  488. * @param {Number} x The tile X coordinate.
  489. * @param {Number} y The tile Y coordinate.
  490. * @param {Number} level The tile level;
  491. * @returns {Credit[]} The credits to be displayed when the tile is displayed.
  492. *
  493. * @exception {DeveloperError} <code>getTileCredits</code> must not be called before the imagery provider is ready.
  494. */
  495. ArcGisMapServerImageryProvider.prototype.getTileCredits = function(x, y, level) {
  496. return undefined;
  497. };
  498. /**
  499. * Requests the image for a given tile. This function should
  500. * not be called before {@link ArcGisMapServerImageryProvider#ready} returns true.
  501. *
  502. * @param {Number} x The tile X coordinate.
  503. * @param {Number} y The tile Y coordinate.
  504. * @param {Number} level The tile level.
  505. * @param {Request} [request] The request object. Intended for internal use only.
  506. * @returns {Promise.<Image|Canvas>|undefined} A promise for the image that will resolve when the image is available, or
  507. * undefined if there are too many active requests to the server, and the request
  508. * should be retried later. The resolved image may be either an
  509. * Image or a Canvas DOM object.
  510. *
  511. * @exception {DeveloperError} <code>requestImage</code> must not be called before the imagery provider is ready.
  512. */
  513. ArcGisMapServerImageryProvider.prototype.requestImage = function(x, y, level, request) {
  514. //>>includeStart('debug', pragmas.debug);
  515. if (!this._ready) {
  516. throw new DeveloperError('requestImage must not be called before the imagery provider is ready.');
  517. }
  518. //>>includeEnd('debug');
  519. return ImageryProvider.loadImage(this, buildImageResource(this, x, y, level, request));
  520. };
  521. /**
  522. /**
  523. * Asynchronously determines what features, if any, are located at a given longitude and latitude within
  524. * a tile. This function should not be called before {@link ImageryProvider#ready} returns true.
  525. *
  526. * @param {Number} x The tile X coordinate.
  527. * @param {Number} y The tile Y coordinate.
  528. * @param {Number} level The tile level.
  529. * @param {Number} longitude The longitude at which to pick features.
  530. * @param {Number} latitude The latitude at which to pick features.
  531. * @return {Promise.<ImageryLayerFeatureInfo[]>|undefined} A promise for the picked features that will resolve when the asynchronous
  532. * picking completes. The resolved value is an array of {@link ImageryLayerFeatureInfo}
  533. * instances. The array may be empty if no features are found at the given location.
  534. *
  535. * @exception {DeveloperError} <code>pickFeatures</code> must not be called before the imagery provider is ready.
  536. */
  537. ArcGisMapServerImageryProvider.prototype.pickFeatures = function(x, y, level, longitude, latitude) {
  538. //>>includeStart('debug', pragmas.debug);
  539. if (!this._ready) {
  540. throw new DeveloperError('pickFeatures must not be called before the imagery provider is ready.');
  541. }
  542. //>>includeEnd('debug');
  543. if (!this.enablePickFeatures) {
  544. return undefined;
  545. }
  546. var rectangle = this._tilingScheme.tileXYToNativeRectangle(x, y, level);
  547. var horizontal;
  548. var vertical;
  549. var sr;
  550. if (this._tilingScheme.projection instanceof GeographicProjection) {
  551. horizontal = CesiumMath.toDegrees(longitude);
  552. vertical = CesiumMath.toDegrees(latitude);
  553. sr = '4326';
  554. } else {
  555. var projected = this._tilingScheme.projection.project(new Cartographic(longitude, latitude, 0.0));
  556. horizontal = projected.x;
  557. vertical = projected.y;
  558. sr = '3857';
  559. }
  560. var layers = 'visible';
  561. if (defined(this._layers)) {
  562. layers += ':' + this._layers;
  563. }
  564. var query = {
  565. f: 'json',
  566. tolerance: 2,
  567. geometryType: 'esriGeometryPoint',
  568. geometry: horizontal + ',' + vertical,
  569. mapExtent: rectangle.west + ',' + rectangle.south + ',' + rectangle.east + ',' + rectangle.north,
  570. imageDisplay: this._tileWidth + ',' + this._tileHeight + ',96',
  571. sr: sr,
  572. layers: layers
  573. };
  574. var resource = this._resource.getDerivedResource({
  575. url: 'identify',
  576. queryParameters: query
  577. });
  578. return resource.fetchJson().then(function(json) {
  579. var result = [];
  580. var features = json.results;
  581. if (!defined(features)) {
  582. return result;
  583. }
  584. for (var i = 0; i < features.length; ++i) {
  585. var feature = features[i];
  586. var featureInfo = new ImageryLayerFeatureInfo();
  587. featureInfo.data = feature;
  588. featureInfo.name = feature.value;
  589. featureInfo.properties = feature.attributes;
  590. featureInfo.configureDescriptionFromProperties(feature.attributes);
  591. // If this is a point feature, use the coordinates of the point.
  592. if (feature.geometryType === 'esriGeometryPoint' && feature.geometry) {
  593. var wkid = feature.geometry.spatialReference && feature.geometry.spatialReference.wkid ? feature.geometry.spatialReference.wkid : 4326;
  594. if (wkid === 4326 || wkid === 4283) {
  595. featureInfo.position = Cartographic.fromDegrees(feature.geometry.x, feature.geometry.y, feature.geometry.z);
  596. } else if (wkid === 102100 || wkid === 900913 || wkid === 3857) {
  597. var projection = new WebMercatorProjection();
  598. featureInfo.position = projection.unproject(new Cartesian3(feature.geometry.x, feature.geometry.y, feature.geometry.z));
  599. }
  600. }
  601. result.push(featureInfo);
  602. }
  603. return result;
  604. });
  605. };
  606. export default ArcGisMapServerImageryProvider;