GoogleEarthEnterpriseMetadata.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. import * as protobuf from "protobufjs/dist/minimal/protobuf.js";
  2. import buildModuleUrl from "./buildModuleUrl.js";
  3. import Check from "./Check.js";
  4. import Credit from "./Credit.js";
  5. import defaultValue from "./defaultValue.js";
  6. import defined from "./defined.js";
  7. import deprecationWarning from "./deprecationWarning.js";
  8. import GoogleEarthEnterpriseTileInformation from "./GoogleEarthEnterpriseTileInformation.js";
  9. import isBitSet from "./isBitSet.js";
  10. import loadAndExecuteScript from "./loadAndExecuteScript.js";
  11. import CesiumMath from "./Math.js";
  12. import Request from "./Request.js";
  13. import Resource from "./Resource.js";
  14. import RuntimeError from "./RuntimeError.js";
  15. import TaskProcessor from "./TaskProcessor.js";
  16. function stringToBuffer(str) {
  17. const len = str.length;
  18. const buffer = new ArrayBuffer(len);
  19. const ui8 = new Uint8Array(buffer);
  20. for (let i = 0; i < len; ++i) {
  21. ui8[i] = str.charCodeAt(i);
  22. }
  23. return buffer;
  24. }
  25. // Decodes packet with a key that has been around since the beginning of Google Earth Enterprise
  26. const defaultKey = stringToBuffer(
  27. "\x45\xf4\xbd\x0b\x79\xe2\x6a\x45\x22\x05\x92\x2c\x17\xcd\x06\x71\xf8\x49\x10\x46\x67\x51\x00\x42\x25\xc6\xe8\x61\x2c\x66\x29\x08\xc6\x34\xdc\x6a\x62\x25\x79\x0a\x77\x1d\x6d\x69\xd6\xf0\x9c\x6b\x93\xa1\xbd\x4e\x75\xe0\x41\x04\x5b\xdf\x40\x56\x0c\xd9\xbb\x72\x9b\x81\x7c\x10\x33\x53\xee\x4f\x6c\xd4\x71\x05\xb0\x7b\xc0\x7f\x45\x03\x56\x5a\xad\x77\x55\x65\x0b\x33\x92\x2a\xac\x19\x6c\x35\x14\xc5\x1d\x30\x73\xf8\x33\x3e\x6d\x46\x38\x4a\xb4\xdd\xf0\x2e\xdd\x17\x75\x16\xda\x8c\x44\x74\x22\x06\xfa\x61\x22\x0c\x33\x22\x53\x6f\xaf\x39\x44\x0b\x8c\x0e\x39\xd9\x39\x13\x4c\xb9\xbf\x7f\xab\x5c\x8c\x50\x5f\x9f\x22\x75\x78\x1f\xe9\x07\x71\x91\x68\x3b\xc1\xc4\x9b\x7f\xf0\x3c\x56\x71\x48\x82\x05\x27\x55\x66\x59\x4e\x65\x1d\x98\x75\xa3\x61\x46\x7d\x61\x3f\x15\x41\x00\x9f\x14\x06\xd7\xb4\x34\x4d\xce\x13\x87\x46\xb0\x1a\xd5\x05\x1c\xb8\x8a\x27\x7b\x8b\xdc\x2b\xbb\x4d\x67\x30\xc8\xd1\xf6\x5c\x8f\x50\xfa\x5b\x2f\x46\x9b\x6e\x35\x18\x2f\x27\x43\x2e\xeb\x0a\x0c\x5e\x10\x05\x10\xa5\x73\x1b\x65\x34\xe5\x6c\x2e\x6a\x43\x27\x63\x14\x23\x55\xa9\x3f\x71\x7b\x67\x43\x7d\x3a\xaf\xcd\xe2\x54\x55\x9c\xfd\x4b\xc6\xe2\x9f\x2f\x28\xed\xcb\x5c\xc6\x2d\x66\x07\x88\xa7\x3b\x2f\x18\x2a\x22\x4e\x0e\xb0\x6b\x2e\xdd\x0d\x95\x7d\x7d\x47\xba\x43\xb2\x11\xb2\x2b\x3e\x4d\xaa\x3e\x7d\xe6\xce\x49\x89\xc6\xe6\x78\x0c\x61\x31\x05\x2d\x01\xa4\x4f\xa5\x7e\x71\x20\x88\xec\x0d\x31\xe8\x4e\x0b\x00\x6e\x50\x68\x7d\x17\x3d\x08\x0d\x17\x95\xa6\x6e\xa3\x68\x97\x24\x5b\x6b\xf3\x17\x23\xf3\xb6\x73\xb3\x0d\x0b\x40\xc0\x9f\xd8\x04\x51\x5d\xfa\x1a\x17\x22\x2e\x15\x6a\xdf\x49\x00\xb9\xa0\x77\x55\xc6\xef\x10\x6a\xbf\x7b\x47\x4c\x7f\x83\x17\x05\xee\xdc\xdc\x46\x85\xa9\xad\x53\x07\x2b\x53\x34\x06\x07\xff\x14\x94\x59\x19\x02\xe4\x38\xe8\x31\x83\x4e\xb9\x58\x46\x6b\xcb\x2d\x23\x86\x92\x70\x00\x35\x88\x22\xcf\x31\xb2\x26\x2f\xe7\xc3\x75\x2d\x36\x2c\x72\x74\xb0\x23\x47\xb7\xd3\xd1\x26\x16\x85\x37\x72\xe2\x00\x8c\x44\xcf\x10\xda\x33\x2d\x1a\xde\x60\x86\x69\x23\x69\x2a\x7c\xcd\x4b\x51\x0d\x95\x54\x39\x77\x2e\x29\xea\x1b\xa6\x50\xa2\x6a\x8f\x6f\x50\x99\x5c\x3e\x54\xfb\xef\x50\x5b\x0b\x07\x45\x17\x89\x6d\x28\x13\x77\x37\x1d\xdb\x8e\x1e\x4a\x05\x66\x4a\x6f\x99\x20\xe5\x70\xe2\xb9\x71\x7e\x0c\x6d\x49\x04\x2d\x7a\xfe\x72\xc7\xf2\x59\x30\x8f\xbb\x02\x5d\x73\xe5\xc9\x20\xea\x78\xec\x20\x90\xf0\x8a\x7f\x42\x17\x7c\x47\x19\x60\xb0\x16\xbd\x26\xb7\x71\xb6\xc7\x9f\x0e\xd1\x33\x82\x3d\xd3\xab\xee\x63\x99\xc8\x2b\x53\xa0\x44\x5c\x71\x01\xc6\xcc\x44\x1f\x32\x4f\x3c\xca\xc0\x29\x3d\x52\xd3\x61\x19\x58\xa9\x7d\x65\xb4\xdc\xcf\x0d\xf4\x3d\xf1\x08\xa9\x42\xda\x23\x09\xd8\xbf\x5e\x50\x49\xf8\x4d\xc0\xcb\x47\x4c\x1c\x4f\xf7\x7b\x2b\xd8\x16\x18\xc5\x31\x92\x3b\xb5\x6f\xdc\x6c\x0d\x92\x88\x16\xd1\x9e\xdb\x3f\xe2\xe9\xda\x5f\xd4\x84\xe2\x46\x61\x5a\xde\x1c\x55\xcf\xa4\x00\xbe\xfd\xce\x67\xf1\x4a\x69\x1c\x97\xe6\x20\x48\xd8\x5d\x7f\x7e\xae\x71\x20\x0e\x4e\xae\xc0\x56\xa9\x91\x01\x3c\x82\x1d\x0f\x72\xe7\x76\xec\x29\x49\xd6\x5d\x2d\x83\xe3\xdb\x36\x06\xa9\x3b\x66\x13\x97\x87\x6a\xd5\xb6\x3d\x50\x5e\x52\xb9\x4b\xc7\x73\x57\x78\xc9\xf4\x2e\x59\x07\x95\x93\x6f\xd0\x4b\x17\x57\x19\x3e\x27\x27\xc7\x60\xdb\x3b\xed\x9a\x0e\x53\x44\x16\x3e\x3f\x8d\x92\x6d\x77\xa2\x0a\xeb\x3f\x52\xa8\xc6\x55\x5e\x31\x49\x37\x85\xf4\xc5\x1f\x26\x2d\xa9\x1c\xbf\x8b\x27\x54\xda\xc3\x6a\x20\xe5\x2a\x78\x04\xb0\xd6\x90\x70\x72\xaa\x8b\x68\xbd\x88\xf7\x02\x5f\x48\xb1\x7e\xc0\x58\x4c\x3f\x66\x1a\xf9\x3e\xe1\x65\xc0\x70\xa7\xcf\x38\x69\xaf\xf0\x56\x6c\x64\x49\x9c\x27\xad\x78\x74\x4f\xc2\x87\xde\x56\x39\x00\xda\x77\x0b\xcb\x2d\x1b\x89\xfb\x35\x4f\x02\xf5\x08\x51\x13\x60\xc1\x0a\x5a\x47\x4d\x26\x1c\x33\x30\x78\xda\xc0\x9c\x46\x47\xe2\x5b\x79\x60\x49\x6e\x37\x67\x53\x0a\x3e\xe9\xec\x46\x39\xb2\xf1\x34\x0d\xc6\x84\x53\x75\x6e\xe1\x0c\x59\xd9\x1e\xde\x29\x85\x10\x7b\x49\x49\xa5\x77\x79\xbe\x49\x56\x2e\x36\xe7\x0b\x3a\xbb\x4f\x03\x62\x7b\xd2\x4d\x31\x95\x2f\xbd\x38\x7b\xa8\x4f\x21\xe1\xec\x46\x70\x76\x95\x7d\x29\x22\x78\x88\x0a\x90\xdd\x9d\x5c\xda\xde\x19\x51\xcf\xf0\xfc\x59\x52\x65\x7c\x33\x13\xdf\xf3\x48\xda\xbb\x2a\x75\xdb\x60\xb2\x02\x15\xd4\xfc\x19\xed\x1b\xec\x7f\x35\xa8\xff\x28\x31\x07\x2d\x12\xc8\xdc\x88\x46\x7c\x8a\x5b\x22"
  28. );
  29. /**
  30. * <div class="notice">
  31. * To construct GoogleEarthEnterpriseMetadata, call {@link GoogleEarthEnterpriseMetadata.fromUrl}. Do not call the constructor directly.
  32. * </div>
  33. *
  34. * Provides metadata using the Google Earth Enterprise REST API. This is used by the GoogleEarthEnterpriseImageryProvider
  35. * and GoogleEarthEnterpriseTerrainProvider to share metadata requests.
  36. *
  37. * @alias GoogleEarthEnterpriseMetadata
  38. * @constructor
  39. *
  40. * @param {Resource|string} [resourceOrUrl] The url of the Google Earth Enterprise server hosting the imagery. Deprecated.
  41. *
  42. * @see GoogleEarthEnterpriseImageryProvider
  43. * @see GoogleEarthEnterpriseTerrainProvider
  44. *
  45. */
  46. function GoogleEarthEnterpriseMetadata(resourceOrUrl) {
  47. /**
  48. * True if imagery is available.
  49. * @type {boolean}
  50. * @default true
  51. */
  52. this.imageryPresent = true;
  53. /**
  54. * True if imagery is sent as a protocol buffer, false if sent as plain images. If undefined we will try both.
  55. * @type {boolean}
  56. * @default undefined
  57. */
  58. this.protoImagery = undefined;
  59. /**
  60. * True if terrain is available.
  61. * @type {boolean}
  62. * @default true
  63. */
  64. this.terrainPresent = true;
  65. /**
  66. * Exponent used to compute constant to calculate negative height values.
  67. * @type {number}
  68. * @default 32
  69. */
  70. this.negativeAltitudeExponentBias = 32;
  71. /**
  72. * Threshold where any numbers smaller are actually negative values. They are multiplied by -2^negativeAltitudeExponentBias.
  73. * @type {number}
  74. * @default EPSILON12
  75. */
  76. this.negativeAltitudeThreshold = CesiumMath.EPSILON12;
  77. /**
  78. * Dictionary of provider id to copyright strings.
  79. * @type {object}
  80. * @default {}
  81. */
  82. this.providers = {};
  83. /**
  84. * Key used to decode packets
  85. * @type {ArrayBuffer}
  86. */
  87. this.key = undefined;
  88. this._resource = undefined;
  89. this._quadPacketVersion = 1;
  90. this._tileInfo = {};
  91. this._subtreePromises = {};
  92. this._readyPromise = Promise.resolve(true);
  93. if (defined(resourceOrUrl)) {
  94. deprecationWarning(
  95. "GoogleEarthEnterpriseMetadata options.url",
  96. "GoogleEarthEnterpriseMetadata constructor parmeter resourceOrUrl was deprecated in CesiumJS 1.104. It will be in CesiumJS 1.107. Use GoogleEarthEnterpriseMetadata.fromUrl instead."
  97. );
  98. let url = resourceOrUrl;
  99. if (typeof url !== "string" && !(url instanceof Resource)) {
  100. url = resourceOrUrl.url;
  101. }
  102. const resource = Resource.createIfNeeded(url);
  103. resource.appendForwardSlash();
  104. this._resource = resource;
  105. const that = this;
  106. this._readyPromise = requestDbRoot(this)
  107. .then(function () {
  108. return that.getQuadTreePacket("", that._quadPacketVersion);
  109. })
  110. .then(function () {
  111. return true;
  112. })
  113. .catch(function (e) {
  114. const message = `An error occurred while accessing ${
  115. getMetadataResource(that, "", 1).url
  116. }.`;
  117. return Promise.reject(new RuntimeError(message));
  118. });
  119. }
  120. }
  121. Object.defineProperties(GoogleEarthEnterpriseMetadata.prototype, {
  122. /**
  123. * Gets the name of the Google Earth Enterprise server.
  124. * @memberof GoogleEarthEnterpriseMetadata.prototype
  125. * @type {string}
  126. * @readonly
  127. */
  128. url: {
  129. get: function () {
  130. return this._resource.url;
  131. },
  132. },
  133. /**
  134. * Gets the proxy used for metadata requests.
  135. * @memberof GoogleEarthEnterpriseMetadata.prototype
  136. * @type {Proxy}
  137. * @readonly
  138. */
  139. proxy: {
  140. get: function () {
  141. return this._resource.proxy;
  142. },
  143. },
  144. /**
  145. * Gets the resource used for metadata requests.
  146. * @memberof GoogleEarthEnterpriseMetadata.prototype
  147. * @type {Resource}
  148. * @readonly
  149. */
  150. resource: {
  151. get: function () {
  152. return this._resource;
  153. },
  154. },
  155. /**
  156. * Gets a promise that resolves to true when the metadata is ready for use.
  157. * @memberof GoogleEarthEnterpriseMetadata.prototype
  158. * @type {Promise<boolean>}
  159. * @readonly
  160. * @deprecated
  161. */
  162. readyPromise: {
  163. get: function () {
  164. deprecationWarning(
  165. "GoogleEarthEnterpriseMetadata.readyPromise",
  166. "GoogleEarthEnterpriseMetadata.readyPromise was deprecated in CesiumJS 1.104. It will be in CesiumJS 1.107. Use GoogleEarthEnterpriseMetadata.fromUrl instead."
  167. );
  168. return this._readyPromise;
  169. },
  170. },
  171. });
  172. /**
  173. * Creates a metadata object using the Google Earth Enterprise REST API. This is used by the GoogleEarthEnterpriseImageryProvider
  174. * and GoogleEarthEnterpriseTerrainProvider to share metadata requests.
  175. *
  176. * @param {Resource|String} resourceOrUrl The url of the Google Earth Enterprise server hosting the imagery.
  177. *
  178. * @returns {Promise<GoogleEarthEnterpriseMetadata>} A promise which resolves to the created GoogleEarthEnterpriseMetadata instance/
  179. */
  180. GoogleEarthEnterpriseMetadata.fromUrl = async function (resourceOrUrl) {
  181. //>>includeStart('debug', pragmas.debug);
  182. Check.defined("resourceOrUrl", resourceOrUrl);
  183. //>>includeEnd('debug');
  184. let url = resourceOrUrl;
  185. if (typeof url !== "string" && !(url instanceof Resource)) {
  186. //>>includeStart('debug', pragmas.debug);
  187. Check.typeOf.string("resourceOrUrl.url", resourceOrUrl.url);
  188. //>>includeEnd('debug');
  189. url = resourceOrUrl.url;
  190. }
  191. const resource = Resource.createIfNeeded(url);
  192. resource.appendForwardSlash();
  193. const metadata = new GoogleEarthEnterpriseMetadata();
  194. metadata._resource = resource;
  195. try {
  196. await requestDbRoot(metadata);
  197. await metadata.getQuadTreePacket("", metadata._quadPacketVersion);
  198. } catch (error) {
  199. const message = `An error occurred while accessing ${
  200. getMetadataResource(metadata, "", 1).url
  201. }: ${error}`;
  202. throw new RuntimeError(message);
  203. }
  204. return metadata;
  205. };
  206. /**
  207. * Converts a tiles (x, y, level) position into a quadkey used to request an image
  208. * from a Google Earth Enterprise server.
  209. *
  210. * @param {number} x The tile's x coordinate.
  211. * @param {number} y The tile's y coordinate.
  212. * @param {number} level The tile's zoom level.
  213. *
  214. * @see GoogleEarthEnterpriseMetadata#quadKeyToTileXY
  215. */
  216. GoogleEarthEnterpriseMetadata.tileXYToQuadKey = function (x, y, level) {
  217. let quadkey = "";
  218. for (let i = level; i >= 0; --i) {
  219. const bitmask = 1 << i;
  220. let digit = 0;
  221. // Tile Layout
  222. // ___ ___
  223. //| | |
  224. //| 3 | 2 |
  225. //|-------|
  226. //| 0 | 1 |
  227. //|___|___|
  228. //
  229. if (!isBitSet(y, bitmask)) {
  230. // Top Row
  231. digit |= 2;
  232. if (!isBitSet(x, bitmask)) {
  233. // Right to left
  234. digit |= 1;
  235. }
  236. } else if (isBitSet(x, bitmask)) {
  237. // Left to right
  238. digit |= 1;
  239. }
  240. quadkey += digit;
  241. }
  242. return quadkey;
  243. };
  244. /**
  245. * Converts a tile's quadkey used to request an image from a Google Earth Enterprise server into the
  246. * (x, y, level) position.
  247. *
  248. * @param {string} quadkey The tile's quad key
  249. *
  250. * @see GoogleEarthEnterpriseMetadata#tileXYToQuadKey
  251. */
  252. GoogleEarthEnterpriseMetadata.quadKeyToTileXY = function (quadkey) {
  253. let x = 0;
  254. let y = 0;
  255. const level = quadkey.length - 1;
  256. for (let i = level; i >= 0; --i) {
  257. const bitmask = 1 << i;
  258. const digit = +quadkey[level - i];
  259. if (isBitSet(digit, 2)) {
  260. // Top Row
  261. if (!isBitSet(digit, 1)) {
  262. // // Right to left
  263. x |= bitmask;
  264. }
  265. } else {
  266. y |= bitmask;
  267. if (isBitSet(digit, 1)) {
  268. // Left to right
  269. x |= bitmask;
  270. }
  271. }
  272. }
  273. return {
  274. x: x,
  275. y: y,
  276. level: level,
  277. };
  278. };
  279. GoogleEarthEnterpriseMetadata.prototype.isValid = function (quadKey) {
  280. let info = this.getTileInformationFromQuadKey(quadKey);
  281. if (defined(info)) {
  282. return info !== null;
  283. }
  284. let valid = true;
  285. let q = quadKey;
  286. let last;
  287. while (q.length > 1) {
  288. last = q.substring(q.length - 1);
  289. q = q.substring(0, q.length - 1);
  290. info = this.getTileInformationFromQuadKey(q);
  291. if (defined(info)) {
  292. if (!info.hasSubtree() && !info.hasChild(parseInt(last))) {
  293. // We have no subtree or child available at some point in this node's ancestry
  294. valid = false;
  295. }
  296. break;
  297. } else if (info === null) {
  298. // Some node in the ancestry was loaded and said there wasn't a subtree
  299. valid = false;
  300. break;
  301. }
  302. }
  303. return valid;
  304. };
  305. const taskProcessor = new TaskProcessor("decodeGoogleEarthEnterprisePacket");
  306. /**
  307. * Retrieves a Google Earth Enterprise quadtree packet.
  308. *
  309. * @param {string} [quadKey=''] The quadkey to retrieve the packet for.
  310. * @param {number} [version=1] The cnode version to be used in the request.
  311. * @param {Request} [request] The request object. Intended for internal use only.
  312. *
  313. * @private
  314. */
  315. GoogleEarthEnterpriseMetadata.prototype.getQuadTreePacket = function (
  316. quadKey,
  317. version,
  318. request
  319. ) {
  320. version = defaultValue(version, 1);
  321. quadKey = defaultValue(quadKey, "");
  322. const resource = getMetadataResource(this, quadKey, version, request);
  323. const promise = resource.fetchArrayBuffer();
  324. if (!defined(promise)) {
  325. return undefined; // Throttled
  326. }
  327. const tileInfo = this._tileInfo;
  328. const key = this.key;
  329. return promise.then(function (metadata) {
  330. const decodePromise = taskProcessor.scheduleTask(
  331. {
  332. buffer: metadata,
  333. quadKey: quadKey,
  334. type: "Metadata",
  335. key: key,
  336. },
  337. [metadata]
  338. );
  339. return decodePromise.then(function (result) {
  340. let root;
  341. let topLevelKeyLength = -1;
  342. if (quadKey !== "") {
  343. // Root tile has no data except children bits, so put them into the tile info
  344. topLevelKeyLength = quadKey.length + 1;
  345. const top = result[quadKey];
  346. root = tileInfo[quadKey];
  347. root._bits |= top._bits;
  348. delete result[quadKey];
  349. }
  350. // Copy the resulting objects into tileInfo
  351. // Make sure we start with shorter quadkeys first, so we know the parents have
  352. // already been processed. Otherwise we can lose ancestorHasTerrain along the way.
  353. const keys = Object.keys(result);
  354. keys.sort(function (a, b) {
  355. return a.length - b.length;
  356. });
  357. const keysLength = keys.length;
  358. for (let i = 0; i < keysLength; ++i) {
  359. const key = keys[i];
  360. const r = result[key];
  361. if (r !== null) {
  362. const info = GoogleEarthEnterpriseTileInformation.clone(result[key]);
  363. const keyLength = key.length;
  364. if (keyLength === topLevelKeyLength) {
  365. info.setParent(root);
  366. } else if (keyLength > 1) {
  367. const parent = tileInfo[key.substring(0, key.length - 1)];
  368. info.setParent(parent);
  369. }
  370. tileInfo[key] = info;
  371. } else {
  372. tileInfo[key] = null;
  373. }
  374. }
  375. });
  376. });
  377. };
  378. /**
  379. * Populates the metadata subtree down to the specified tile.
  380. *
  381. * @param {number} x The tile X coordinate.
  382. * @param {number} y The tile Y coordinate.
  383. * @param {number} level The tile level.
  384. * @param {Request} [request] The request object. Intended for internal use only.
  385. *
  386. * @returns {Promise<GoogleEarthEnterpriseTileInformation>} A promise that resolves to the tile info for the requested quad key
  387. *
  388. * @private
  389. */
  390. GoogleEarthEnterpriseMetadata.prototype.populateSubtree = function (
  391. x,
  392. y,
  393. level,
  394. request
  395. ) {
  396. const quadkey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level);
  397. return populateSubtree(this, quadkey, request);
  398. };
  399. function populateSubtree(that, quadKey, request) {
  400. const tileInfo = that._tileInfo;
  401. let q = quadKey;
  402. let t = tileInfo[q];
  403. // If we have tileInfo make sure sure it is not a node with a subtree that's not loaded
  404. if (defined(t) && (!t.hasSubtree() || t.hasChildren())) {
  405. return t;
  406. }
  407. while (t === undefined && q.length > 1) {
  408. q = q.substring(0, q.length - 1);
  409. t = tileInfo[q];
  410. }
  411. let subtreeRequest;
  412. const subtreePromises = that._subtreePromises;
  413. let promise = subtreePromises[q];
  414. if (defined(promise)) {
  415. return promise.then(function () {
  416. // Recursively call this in case we need multiple subtree requests
  417. subtreeRequest = new Request({
  418. throttle: request.throttle,
  419. throttleByServer: request.throttleByServer,
  420. type: request.type,
  421. priorityFunction: request.priorityFunction,
  422. });
  423. return populateSubtree(that, quadKey, subtreeRequest);
  424. });
  425. }
  426. // t is either
  427. // null so one of its parents was a leaf node, so this tile doesn't exist
  428. // exists but doesn't have a subtree to request
  429. // undefined so no parent exists - this shouldn't ever happen once the provider is ready
  430. if (!defined(t) || !t.hasSubtree()) {
  431. return Promise.reject(
  432. new RuntimeError(`Couldn't load metadata for tile ${quadKey}`)
  433. );
  434. }
  435. // We need to split up the promise here because when will execute syncronously if getQuadTreePacket
  436. // is already resolved (like in the tests), so subtreePromises will never get cleared out.
  437. // Only the initial request will also remove the promise from subtreePromises.
  438. promise = that.getQuadTreePacket(q, t.cnodeVersion, request);
  439. if (!defined(promise)) {
  440. return undefined;
  441. }
  442. subtreePromises[q] = promise;
  443. return promise
  444. .then(function () {
  445. // Recursively call this in case we need multiple subtree requests
  446. subtreeRequest = new Request({
  447. throttle: request.throttle,
  448. throttleByServer: request.throttleByServer,
  449. type: request.type,
  450. priorityFunction: request.priorityFunction,
  451. });
  452. return populateSubtree(that, quadKey, subtreeRequest);
  453. })
  454. .finally(function () {
  455. delete subtreePromises[q];
  456. });
  457. }
  458. /**
  459. * Gets information about a tile
  460. *
  461. * @param {number} x The tile X coordinate.
  462. * @param {number} y The tile Y coordinate.
  463. * @param {number} level The tile level.
  464. * @returns {GoogleEarthEnterpriseTileInformation|undefined} Information about the tile or undefined if it isn't loaded.
  465. *
  466. * @private
  467. */
  468. GoogleEarthEnterpriseMetadata.prototype.getTileInformation = function (
  469. x,
  470. y,
  471. level
  472. ) {
  473. const quadkey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level);
  474. return this._tileInfo[quadkey];
  475. };
  476. /**
  477. * Gets information about a tile from a quadKey
  478. *
  479. * @param {string} quadkey The quadkey for the tile
  480. * @returns {GoogleEarthEnterpriseTileInformation|undefined} Information about the tile or undefined if it isn't loaded.
  481. *
  482. * @private
  483. */
  484. GoogleEarthEnterpriseMetadata.prototype.getTileInformationFromQuadKey = function (
  485. quadkey
  486. ) {
  487. return this._tileInfo[quadkey];
  488. };
  489. function getMetadataResource(that, quadKey, version, request) {
  490. return that._resource.getDerivedResource({
  491. url: `flatfile?q2-0${quadKey}-q.${version.toString()}`,
  492. request: request,
  493. });
  494. }
  495. let dbrootParser;
  496. let dbrootParserPromise;
  497. function requestDbRoot(that) {
  498. const resource = that._resource.getDerivedResource({
  499. url: "dbRoot.v5",
  500. queryParameters: {
  501. output: "proto",
  502. },
  503. });
  504. if (!defined(dbrootParserPromise)) {
  505. const url = buildModuleUrl("ThirdParty/google-earth-dbroot-parser.js");
  506. const oldValue = window.cesiumGoogleEarthDbRootParser;
  507. dbrootParserPromise = loadAndExecuteScript(url).then(function () {
  508. dbrootParser = window.cesiumGoogleEarthDbRootParser(protobuf);
  509. if (defined(oldValue)) {
  510. window.cesiumGoogleEarthDbRootParser = oldValue;
  511. } else {
  512. delete window.cesiumGoogleEarthDbRootParser;
  513. }
  514. });
  515. }
  516. return dbrootParserPromise
  517. .then(function () {
  518. return resource.fetchArrayBuffer();
  519. })
  520. .then(function (buf) {
  521. const encryptedDbRootProto = dbrootParser.EncryptedDbRootProto.decode(
  522. new Uint8Array(buf)
  523. );
  524. let byteArray = encryptedDbRootProto.encryptionData;
  525. let offset = byteArray.byteOffset;
  526. let end = offset + byteArray.byteLength;
  527. const key = (that.key = byteArray.buffer.slice(offset, end));
  528. byteArray = encryptedDbRootProto.dbrootData;
  529. offset = byteArray.byteOffset;
  530. end = offset + byteArray.byteLength;
  531. const dbRootCompressed = byteArray.buffer.slice(offset, end);
  532. return taskProcessor.scheduleTask(
  533. {
  534. buffer: dbRootCompressed,
  535. type: "DbRoot",
  536. key: key,
  537. },
  538. [dbRootCompressed]
  539. );
  540. })
  541. .then(function (result) {
  542. const dbRoot = dbrootParser.DbRootProto.decode(
  543. new Uint8Array(result.buffer)
  544. );
  545. that.imageryPresent = defaultValue(
  546. dbRoot.imageryPresent,
  547. that.imageryPresent
  548. );
  549. that.protoImagery = dbRoot.protoImagery;
  550. that.terrainPresent = defaultValue(
  551. dbRoot.terrainPresent,
  552. that.terrainPresent
  553. );
  554. if (defined(dbRoot.endSnippet) && defined(dbRoot.endSnippet.model)) {
  555. const model = dbRoot.endSnippet.model;
  556. that.negativeAltitudeExponentBias = defaultValue(
  557. model.negativeAltitudeExponentBias,
  558. that.negativeAltitudeExponentBias
  559. );
  560. that.negativeAltitudeThreshold = defaultValue(
  561. model.compressedNegativeAltitudeThreshold,
  562. that.negativeAltitudeThreshold
  563. );
  564. }
  565. if (defined(dbRoot.databaseVersion)) {
  566. that._quadPacketVersion = defaultValue(
  567. dbRoot.databaseVersion.quadtreeVersion,
  568. that._quadPacketVersion
  569. );
  570. }
  571. const providers = that.providers;
  572. const providerInfo = defaultValue(dbRoot.providerInfo, []);
  573. const count = providerInfo.length;
  574. for (let i = 0; i < count; ++i) {
  575. const provider = providerInfo[i];
  576. const copyrightString = provider.copyrightString;
  577. if (defined(copyrightString)) {
  578. providers[provider.providerId] = new Credit(copyrightString.value);
  579. }
  580. }
  581. })
  582. .catch(function () {
  583. // Just eat the error and use the default values.
  584. console.log(`Failed to retrieve ${resource.url}. Using defaults.`);
  585. that.key = defaultKey;
  586. });
  587. }
  588. export default GoogleEarthEnterpriseMetadata;