GoogleEarthEnterpriseTerrainProvider.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. import Check from "./Check.js";
  2. import Credit from "./Credit.js";
  3. import defaultValue from "./defaultValue.js";
  4. import defined from "./defined.js";
  5. import deprecationWarning from "./deprecationWarning.js";
  6. import Event from "./Event.js";
  7. import GeographicTilingScheme from "./GeographicTilingScheme.js";
  8. import GoogleEarthEnterpriseMetadata from "./GoogleEarthEnterpriseMetadata.js";
  9. import GoogleEarthEnterpriseTerrainData from "./GoogleEarthEnterpriseTerrainData.js";
  10. import HeightmapTerrainData from "./HeightmapTerrainData.js";
  11. import JulianDate from "./JulianDate.js";
  12. import CesiumMath from "./Math.js";
  13. import Rectangle from "./Rectangle.js";
  14. import Request from "./Request.js";
  15. import RequestState from "./RequestState.js";
  16. import RequestType from "./RequestType.js";
  17. import Resource from "./Resource.js";
  18. import RuntimeError from "./RuntimeError.js";
  19. import TaskProcessor from "./TaskProcessor.js";
  20. import TileProviderError from "./TileProviderError.js";
  21. const TerrainState = {
  22. UNKNOWN: 0,
  23. NONE: 1,
  24. SELF: 2,
  25. PARENT: 3,
  26. };
  27. const julianDateScratch = new JulianDate();
  28. function TerrainCache() {
  29. this._terrainCache = {};
  30. this._lastTidy = JulianDate.now();
  31. }
  32. TerrainCache.prototype.add = function (quadKey, buffer) {
  33. this._terrainCache[quadKey] = {
  34. buffer: buffer,
  35. timestamp: JulianDate.now(),
  36. };
  37. };
  38. TerrainCache.prototype.get = function (quadKey) {
  39. const terrainCache = this._terrainCache;
  40. const result = terrainCache[quadKey];
  41. if (defined(result)) {
  42. delete this._terrainCache[quadKey];
  43. return result.buffer;
  44. }
  45. };
  46. TerrainCache.prototype.tidy = function () {
  47. JulianDate.now(julianDateScratch);
  48. if (JulianDate.secondsDifference(julianDateScratch, this._lastTidy) > 10) {
  49. const terrainCache = this._terrainCache;
  50. const keys = Object.keys(terrainCache);
  51. const count = keys.length;
  52. for (let i = 0; i < count; ++i) {
  53. const k = keys[i];
  54. const e = terrainCache[k];
  55. if (JulianDate.secondsDifference(julianDateScratch, e.timestamp) > 10) {
  56. delete terrainCache[k];
  57. }
  58. }
  59. JulianDate.clone(julianDateScratch, this._lastTidy);
  60. }
  61. };
  62. /**
  63. * @typedef {Object} GoogleEarthEnterpriseTerrainProvider.ConstructorOptions
  64. *
  65. * Initialization options for GoogleEarthEnterpriseTerrainProvider constructor
  66. *
  67. * @property {Ellipsoid} [ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used.
  68. * @property {Credit|string} [credit] A credit for the data source, which is displayed on the canvas.
  69. * @property {Resource|string} [url] The url of the Google Earth Enterprise server hosting the imagery. Deprecated.
  70. * @property {GoogleEarthEnterpriseMetadata} [metadata] A metadata object that can be used to share metadata requests with a GoogleEarthEnterpriseImageryProvider. Deprecated.
  71. */
  72. /**
  73. * <div class="notice">
  74. * To construct a GoogleEarthEnterpriseTerrainProvider, call {@link GoogleEarthEnterpriseTerrainProvider.fromMetadata}. Do not call the constructor directly.
  75. * </div>
  76. *
  77. * Provides tiled terrain using the Google Earth Enterprise REST API.
  78. *
  79. * @alias GoogleEarthEnterpriseTerrainProvider
  80. * @constructor
  81. *
  82. * @param {GoogleEarthEnterpriseTerrainProvider.ConstructorOptions} options An object describing initialization options
  83. *
  84. * @see GoogleEarthEnterpriseTerrainProvider.fromMetadata
  85. * @see GoogleEarthEnterpriseMetadata.fromUrl
  86. * @see GoogleEarthEnterpriseImageryProvider
  87. * @see CesiumTerrainProvider
  88. *
  89. * @example
  90. * const geeMetadata = await GoogleEarthEnterpriseMetadata.fromUrl("http://www.example.com");
  91. * const gee = Cesium.GoogleEarthEnterpriseTerrainProvider.fromMetadata(geeMetadata);
  92. *
  93. * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing}
  94. */
  95. function GoogleEarthEnterpriseTerrainProvider(options) {
  96. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  97. this._tilingScheme = new GeographicTilingScheme({
  98. numberOfLevelZeroTilesX: 2,
  99. numberOfLevelZeroTilesY: 2,
  100. rectangle: new Rectangle(
  101. -CesiumMath.PI,
  102. -CesiumMath.PI,
  103. CesiumMath.PI,
  104. CesiumMath.PI
  105. ),
  106. ellipsoid: options.ellipsoid,
  107. });
  108. let credit = options.credit;
  109. if (typeof credit === "string") {
  110. credit = new Credit(credit);
  111. }
  112. this._credit = credit;
  113. // Pulled from Google's documentation
  114. this._levelZeroMaximumGeometricError = 40075.16;
  115. this._terrainCache = new TerrainCache();
  116. this._terrainPromises = {};
  117. this._terrainRequests = {};
  118. this._errorEvent = new Event();
  119. this._ready = false;
  120. if (defined(options.url)) {
  121. deprecationWarning(
  122. "GoogleEarthEnterpriseTerrainProvider options.url",
  123. "options.url was deprecated in CesiumJS 1.104. It will be in CesiumJS 1.107. Use GoogleEarthEnterpriseTerrainProvider.fromMetadata instead."
  124. );
  125. const resource = Resource.createIfNeeded(options.url);
  126. const that = this;
  127. let metadataError;
  128. this._readyPromise = GoogleEarthEnterpriseMetadata.fromUrl(resource)
  129. .then((metadata) => {
  130. if (!metadata.terrainPresent) {
  131. const e = new RuntimeError(
  132. `The server ${metadata.url} doesn't have terrain`
  133. );
  134. return Promise.reject(e);
  135. }
  136. TileProviderError.reportSuccess(metadataError);
  137. that._metadata = metadata;
  138. that._ready = true;
  139. return true;
  140. })
  141. .catch((e) => {
  142. metadataError = TileProviderError.reportError(
  143. metadataError,
  144. that,
  145. that._errorEvent,
  146. e.message,
  147. undefined,
  148. undefined,
  149. undefined,
  150. e
  151. );
  152. throw e;
  153. });
  154. } else if (defined(options.metadata)) {
  155. deprecationWarning(
  156. "GoogleEarthEnterpriseTerrainProvider options.metadata",
  157. "options.metadata was deprecated in CesiumJS 1.104. It will be in CesiumJS 1.107. Use GoogleEarthEnterpriseTerrainProvider.fromMetadata instead."
  158. );
  159. const metadata = options.metadata;
  160. this._metadata = metadata;
  161. const that = this;
  162. this._readyPromise = Promise.resolve(this._metadata._readyPromise).then(
  163. () => {
  164. if (!metadata.terrainPresent) {
  165. throw new RuntimeError(
  166. `The server ${metadata.url} doesn't have terrain`
  167. );
  168. }
  169. that._ready = true;
  170. }
  171. );
  172. }
  173. }
  174. Object.defineProperties(GoogleEarthEnterpriseTerrainProvider.prototype, {
  175. /**
  176. * Gets the name of the Google Earth Enterprise server url hosting the imagery.
  177. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  178. * @type {string}
  179. * @readonly
  180. */
  181. url: {
  182. get: function () {
  183. return this._metadata.url;
  184. },
  185. },
  186. /**
  187. * Gets the proxy used by this provider.
  188. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  189. * @type {Proxy}
  190. * @readonly
  191. */
  192. proxy: {
  193. get: function () {
  194. return this._metadata.proxy;
  195. },
  196. },
  197. /**
  198. * Gets the tiling scheme used by this provider.
  199. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  200. * @type {TilingScheme}
  201. * @readonly
  202. */
  203. tilingScheme: {
  204. get: function () {
  205. return this._tilingScheme;
  206. },
  207. },
  208. /**
  209. * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing
  210. * to the event, you will be notified of the error and can potentially recover from it. Event listeners
  211. * are passed an instance of {@link TileProviderError}.
  212. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  213. * @type {Event}
  214. * @readonly
  215. */
  216. errorEvent: {
  217. get: function () {
  218. return this._errorEvent;
  219. },
  220. },
  221. /**
  222. * Gets a value indicating whether or not the provider is ready for use.
  223. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  224. * @type {boolean}
  225. * @readonly
  226. * @deprecated
  227. */
  228. ready: {
  229. get: function () {
  230. deprecationWarning(
  231. "GoogleEarthEnterpriseTerrainProvider.ready",
  232. "GoogleEarthEnterpriseTerrainProvider.ready was deprecated in CesiumJS 1.104. It will be in CesiumJS 1.107."
  233. );
  234. return this._ready;
  235. },
  236. },
  237. /**
  238. * Gets a promise that resolves to true when the provider is ready for use.
  239. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  240. * @type {Promise<boolean>}
  241. * @readonly
  242. * @deprecated
  243. */
  244. readyPromise: {
  245. get: function () {
  246. deprecationWarning(
  247. "GoogleEarthEnterpriseTerrainProvider.readyPromise",
  248. "GoogleEarthEnterpriseTerrainProvider.readyPromise was deprecated in CesiumJS 1.104. It will be in CesiumJS 1.107."
  249. );
  250. return this._readyPromise;
  251. },
  252. },
  253. /**
  254. * Gets the credit to display when this terrain provider is active. Typically this is used to credit
  255. * the source of the terrain.
  256. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  257. * @type {Credit}
  258. * @readonly
  259. */
  260. credit: {
  261. get: function () {
  262. return this._credit;
  263. },
  264. },
  265. /**
  266. * Gets a value indicating whether or not the provider includes a water mask. The water mask
  267. * indicates which areas of the globe are water rather than land, so they can be rendered
  268. * as a reflective surface with animated waves.
  269. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  270. * @type {boolean}
  271. * @readonly
  272. */
  273. hasWaterMask: {
  274. get: function () {
  275. return false;
  276. },
  277. },
  278. /**
  279. * Gets a value indicating whether or not the requested tiles include vertex normals.
  280. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  281. * @type {boolean}
  282. * @readonly
  283. */
  284. hasVertexNormals: {
  285. get: function () {
  286. return false;
  287. },
  288. },
  289. /**
  290. * Gets an object that can be used to determine availability of terrain from this provider, such as
  291. * at points and in rectangles. This property may be undefined if availability
  292. * information is not available.
  293. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype
  294. * @type {TileAvailability}
  295. * @readonly
  296. */
  297. availability: {
  298. get: function () {
  299. return undefined;
  300. },
  301. },
  302. });
  303. /**
  304. * Creates a GoogleEarthTerrainProvider from GoogleEarthEnterpriseMetadata
  305. *
  306. * @param {GoogleEarthEnterpriseMetadata} metadata A metadata object that can be used to share metadata requests with a GoogleEarthEnterpriseImageryProvider.
  307. * @param {GoogleEarthEnterpriseTerrainProvider.ConstructorOptions} options An object describing initialization options
  308. * @returns {GoogleEarthEnterpriseTerrainProvider}
  309. *
  310. * @see GoogleEarthEnterpriseMetadata.fromUrl
  311. *
  312. * @exception {RuntimeError} metadata does not specify terrain
  313. *
  314. * @example
  315. * const geeMetadata = await GoogleEarthEnterpriseMetadata.fromUrl("http://www.example.com");
  316. * const gee = Cesium.GoogleEarthEnterpriseTerrainProvider.fromMetadata(geeMetadata);
  317. */
  318. GoogleEarthEnterpriseTerrainProvider.fromMetadata = function (
  319. metadata,
  320. options
  321. ) {
  322. //>>includeStart('debug', pragmas.debug);
  323. Check.defined("metadata", metadata);
  324. //>>includeEnd('debug');
  325. if (!metadata.terrainPresent) {
  326. throw new RuntimeError(`The server ${metadata.url} doesn't have terrain`);
  327. }
  328. const provider = new GoogleEarthEnterpriseTerrainProvider(options);
  329. provider._metadata = metadata;
  330. provider._readyPromise = Promise.resolve(true);
  331. provider._ready = true;
  332. return provider;
  333. };
  334. const taskProcessor = new TaskProcessor("decodeGoogleEarthEnterprisePacket");
  335. // If the tile has its own terrain, then you can just use its child bitmask. If it was requested using it's parent
  336. // then you need to check all of its children to see if they have terrain.
  337. function computeChildMask(quadKey, info, metadata) {
  338. let childMask = info.getChildBitmask();
  339. if (info.terrainState === TerrainState.PARENT) {
  340. childMask = 0;
  341. for (let i = 0; i < 4; ++i) {
  342. const child = metadata.getTileInformationFromQuadKey(
  343. quadKey + i.toString()
  344. );
  345. if (defined(child) && child.hasTerrain()) {
  346. childMask |= 1 << i;
  347. }
  348. }
  349. }
  350. return childMask;
  351. }
  352. /**
  353. * Requests the geometry for a given tile. The result must include terrain data and
  354. * may optionally include a water mask and an indication of which child tiles are available.
  355. *
  356. * @param {number} x The X coordinate of the tile for which to request geometry.
  357. * @param {number} y The Y coordinate of the tile for which to request geometry.
  358. * @param {number} level The level of the tile for which to request geometry.
  359. * @param {Request} [request] The request object. Intended for internal use only.
  360. * @returns {Promise<TerrainData>|undefined} A promise for the requested geometry. If this method
  361. * returns undefined instead of a promise, it is an indication that too many requests are already
  362. * pending and the request will be retried later.
  363. */
  364. GoogleEarthEnterpriseTerrainProvider.prototype.requestTileGeometry = function (
  365. x,
  366. y,
  367. level,
  368. request
  369. ) {
  370. const quadKey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level);
  371. const terrainCache = this._terrainCache;
  372. const metadata = this._metadata;
  373. const info = metadata.getTileInformationFromQuadKey(quadKey);
  374. // Check if this tile is even possibly available
  375. if (!defined(info)) {
  376. return Promise.reject(new RuntimeError("Terrain tile doesn't exist"));
  377. }
  378. let terrainState = info.terrainState;
  379. if (!defined(terrainState)) {
  380. // First time we have tried to load this tile, so set terrain state to UNKNOWN
  381. terrainState = info.terrainState = TerrainState.UNKNOWN;
  382. }
  383. // If its in the cache, return it
  384. const buffer = terrainCache.get(quadKey);
  385. if (defined(buffer)) {
  386. const credit = metadata.providers[info.terrainProvider];
  387. return Promise.resolve(
  388. new GoogleEarthEnterpriseTerrainData({
  389. buffer: buffer,
  390. childTileMask: computeChildMask(quadKey, info, metadata),
  391. credits: defined(credit) ? [credit] : undefined,
  392. negativeAltitudeExponentBias: metadata.negativeAltitudeExponentBias,
  393. negativeElevationThreshold: metadata.negativeAltitudeThreshold,
  394. })
  395. );
  396. }
  397. // Clean up the cache
  398. terrainCache.tidy();
  399. // We have a tile, check to see if no ancestors have terrain or that we know for sure it doesn't
  400. if (!info.ancestorHasTerrain) {
  401. // We haven't reached a level with terrain, so return the ellipsoid
  402. return Promise.resolve(
  403. new HeightmapTerrainData({
  404. buffer: new Uint8Array(16 * 16),
  405. width: 16,
  406. height: 16,
  407. })
  408. );
  409. } else if (terrainState === TerrainState.NONE) {
  410. // Already have info and there isn't any terrain here
  411. return Promise.reject(new RuntimeError("Terrain tile doesn't exist"));
  412. }
  413. // Figure out where we are getting the terrain and what version
  414. let parentInfo;
  415. let q = quadKey;
  416. let terrainVersion = -1;
  417. switch (terrainState) {
  418. case TerrainState.SELF: // We have terrain and have retrieved it before
  419. terrainVersion = info.terrainVersion;
  420. break;
  421. case TerrainState.PARENT: // We have terrain in our parent
  422. q = q.substring(0, q.length - 1);
  423. parentInfo = metadata.getTileInformationFromQuadKey(q);
  424. terrainVersion = parentInfo.terrainVersion;
  425. break;
  426. case TerrainState.UNKNOWN: // We haven't tried to retrieve terrain yet
  427. if (info.hasTerrain()) {
  428. terrainVersion = info.terrainVersion; // We should have terrain
  429. } else {
  430. q = q.substring(0, q.length - 1);
  431. parentInfo = metadata.getTileInformationFromQuadKey(q);
  432. if (defined(parentInfo) && parentInfo.hasTerrain()) {
  433. terrainVersion = parentInfo.terrainVersion; // Try checking in the parent
  434. }
  435. }
  436. break;
  437. }
  438. // We can't figure out where to get the terrain
  439. if (terrainVersion < 0) {
  440. return Promise.reject(new RuntimeError("Terrain tile doesn't exist"));
  441. }
  442. // Load that terrain
  443. const terrainPromises = this._terrainPromises;
  444. const terrainRequests = this._terrainRequests;
  445. let sharedPromise;
  446. let sharedRequest;
  447. if (defined(terrainPromises[q])) {
  448. // Already being loaded possibly from another child, so return existing promise
  449. sharedPromise = terrainPromises[q];
  450. sharedRequest = terrainRequests[q];
  451. } else {
  452. // Create new request for terrain
  453. sharedRequest = request;
  454. const requestPromise = buildTerrainResource(
  455. this,
  456. q,
  457. terrainVersion,
  458. sharedRequest
  459. ).fetchArrayBuffer();
  460. if (!defined(requestPromise)) {
  461. return undefined; // Throttled
  462. }
  463. sharedPromise = requestPromise.then(function (terrain) {
  464. if (defined(terrain)) {
  465. return taskProcessor
  466. .scheduleTask(
  467. {
  468. buffer: terrain,
  469. type: "Terrain",
  470. key: metadata.key,
  471. },
  472. [terrain]
  473. )
  474. .then(function (terrainTiles) {
  475. // Add requested tile and mark it as SELF
  476. const requestedInfo = metadata.getTileInformationFromQuadKey(q);
  477. requestedInfo.terrainState = TerrainState.SELF;
  478. terrainCache.add(q, terrainTiles[0]);
  479. const provider = requestedInfo.terrainProvider;
  480. // Add children to cache
  481. const count = terrainTiles.length - 1;
  482. for (let j = 0; j < count; ++j) {
  483. const childKey = q + j.toString();
  484. const child = metadata.getTileInformationFromQuadKey(childKey);
  485. if (defined(child)) {
  486. terrainCache.add(childKey, terrainTiles[j + 1]);
  487. child.terrainState = TerrainState.PARENT;
  488. if (child.terrainProvider === 0) {
  489. child.terrainProvider = provider;
  490. }
  491. }
  492. }
  493. });
  494. }
  495. return Promise.reject(new RuntimeError("Failed to load terrain."));
  496. });
  497. terrainPromises[q] = sharedPromise; // Store promise without delete from terrainPromises
  498. terrainRequests[q] = sharedRequest;
  499. // Set promise so we remove from terrainPromises just one time
  500. sharedPromise = sharedPromise.finally(function () {
  501. delete terrainPromises[q];
  502. delete terrainRequests[q];
  503. });
  504. }
  505. return sharedPromise
  506. .then(function () {
  507. const buffer = terrainCache.get(quadKey);
  508. if (defined(buffer)) {
  509. const credit = metadata.providers[info.terrainProvider];
  510. return new GoogleEarthEnterpriseTerrainData({
  511. buffer: buffer,
  512. childTileMask: computeChildMask(quadKey, info, metadata),
  513. credits: defined(credit) ? [credit] : undefined,
  514. negativeAltitudeExponentBias: metadata.negativeAltitudeExponentBias,
  515. negativeElevationThreshold: metadata.negativeAltitudeThreshold,
  516. });
  517. }
  518. return Promise.reject(new RuntimeError("Failed to load terrain."));
  519. })
  520. .catch(function (error) {
  521. if (sharedRequest.state === RequestState.CANCELLED) {
  522. request.state = sharedRequest.state;
  523. return Promise.reject(error);
  524. }
  525. info.terrainState = TerrainState.NONE;
  526. return Promise.reject(error);
  527. });
  528. };
  529. /**
  530. * Gets the maximum geometric error allowed in a tile at a given level.
  531. *
  532. * @param {number} level The tile level for which to get the maximum geometric error.
  533. * @returns {number} The maximum geometric error.
  534. */
  535. GoogleEarthEnterpriseTerrainProvider.prototype.getLevelMaximumGeometricError = function (
  536. level
  537. ) {
  538. return this._levelZeroMaximumGeometricError / (1 << level);
  539. };
  540. /**
  541. * Determines whether data for a tile is available to be loaded.
  542. *
  543. * @param {number} x The X coordinate of the tile for which to request geometry.
  544. * @param {number} y The Y coordinate of the tile for which to request geometry.
  545. * @param {number} level The level of the tile for which to request geometry.
  546. * @returns {boolean|undefined} Undefined if not supported, otherwise true or false.
  547. */
  548. GoogleEarthEnterpriseTerrainProvider.prototype.getTileDataAvailable = function (
  549. x,
  550. y,
  551. level
  552. ) {
  553. const metadata = this._metadata;
  554. let quadKey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level);
  555. const info = metadata.getTileInformation(x, y, level);
  556. if (info === null) {
  557. return false;
  558. }
  559. if (defined(info)) {
  560. if (!info.ancestorHasTerrain) {
  561. return true; // We'll just return the ellipsoid
  562. }
  563. const terrainState = info.terrainState;
  564. if (terrainState === TerrainState.NONE) {
  565. return false; // Terrain is not available
  566. }
  567. if (!defined(terrainState) || terrainState === TerrainState.UNKNOWN) {
  568. info.terrainState = TerrainState.UNKNOWN;
  569. if (!info.hasTerrain()) {
  570. quadKey = quadKey.substring(0, quadKey.length - 1);
  571. const parentInfo = metadata.getTileInformationFromQuadKey(quadKey);
  572. if (!defined(parentInfo) || !parentInfo.hasTerrain()) {
  573. return false;
  574. }
  575. }
  576. }
  577. return true;
  578. }
  579. if (metadata.isValid(quadKey)) {
  580. // We will need this tile, so request metadata and return false for now
  581. const request = new Request({
  582. throttle: false,
  583. throttleByServer: true,
  584. type: RequestType.TERRAIN,
  585. });
  586. metadata.populateSubtree(x, y, level, request);
  587. }
  588. return false;
  589. };
  590. /**
  591. * Makes sure we load availability data for a tile
  592. *
  593. * @param {number} x The X coordinate of the tile for which to request geometry.
  594. * @param {number} y The Y coordinate of the tile for which to request geometry.
  595. * @param {number} level The level of the tile for which to request geometry.
  596. * @returns {undefined}
  597. */
  598. GoogleEarthEnterpriseTerrainProvider.prototype.loadTileDataAvailability = function (
  599. x,
  600. y,
  601. level
  602. ) {
  603. return undefined;
  604. };
  605. //
  606. // Functions to handle imagery packets
  607. //
  608. function buildTerrainResource(terrainProvider, quadKey, version, request) {
  609. version = defined(version) && version > 0 ? version : 1;
  610. return terrainProvider._metadata.resource.getDerivedResource({
  611. url: `flatfile?f1c-0${quadKey}-t.${version.toString()}`,
  612. request: request,
  613. });
  614. }
  615. export default GoogleEarthEnterpriseTerrainProvider;