decodeGoogleEarthEnterprisePacket.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /**
  2. * Cesium - https://github.com/AnalyticalGraphicsInc/cesium
  3. *
  4. * Copyright 2011-2017 Cesium Contributors
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. *
  18. * Columbus View (Pat. Pend.)
  19. *
  20. * Portions licensed separately.
  21. * See https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md for full licensing details.
  22. */
  23. define(['./when-8d13db60', './Check-70bec281', './RuntimeError-ba10bc3e', './createTaskProcessorWorker', './pako_inflate-8ea163f9'], function (when, Check, RuntimeError, createTaskProcessorWorker, pako_inflate) { 'use strict';
  24. var compressedMagic = 0x7468dead;
  25. var compressedMagicSwap = 0xadde6874;
  26. /**
  27. * Decodes data that is received from the Google Earth Enterprise server.
  28. *
  29. * @param {ArrayBuffer} key The key used during decoding.
  30. * @param {ArrayBuffer} data The data to be decoded.
  31. *
  32. * @private
  33. */
  34. function decodeGoogleEarthEnterpriseData(key, data) {
  35. if (decodeGoogleEarthEnterpriseData.passThroughDataForTesting) {
  36. return data;
  37. }
  38. //>>includeStart('debug', pragmas.debug);
  39. Check.Check.typeOf.object('key', key);
  40. Check.Check.typeOf.object('data', data);
  41. //>>includeEnd('debug');
  42. var keyLength = key.byteLength;
  43. if (keyLength === 0 || (keyLength % 4) !== 0) {
  44. throw new RuntimeError.RuntimeError('The length of key must be greater than 0 and a multiple of 4.');
  45. }
  46. var dataView = new DataView(data);
  47. var magic = dataView.getUint32(0, true);
  48. if (magic === compressedMagic || magic === compressedMagicSwap) {
  49. // Occasionally packets don't come back encoded, so just return
  50. return data;
  51. }
  52. var keyView = new DataView(key);
  53. var dp = 0;
  54. var dpend = data.byteLength;
  55. var dpend64 = dpend - (dpend % 8);
  56. var kpend = keyLength;
  57. var kp;
  58. var off = 8;
  59. // This algorithm is intentionally asymmetric to make it more difficult to
  60. // guess. Security through obscurity. :-(
  61. // while we have a full uint64 (8 bytes) left to do
  62. // assumes buffer is 64bit aligned (or processor doesn't care)
  63. while (dp < dpend64) {
  64. // rotate the key each time through by using the offets 16,0,8,16,0,8,...
  65. off = (off + 8) % 24;
  66. kp = off;
  67. // run through one key length xor'ing one uint64 at a time
  68. // then drop out to rotate the key for the next bit
  69. while ((dp < dpend64) && (kp < kpend)) {
  70. dataView.setUint32(dp, dataView.getUint32(dp, true) ^ keyView.getUint32(kp, true), true);
  71. dataView.setUint32(dp + 4, dataView.getUint32(dp + 4, true) ^ keyView.getUint32(kp + 4, true), true);
  72. dp += 8;
  73. kp += 24;
  74. }
  75. }
  76. // now the remaining 1 to 7 bytes
  77. if (dp < dpend) {
  78. if (kp >= kpend) {
  79. // rotate the key one last time (if necessary)
  80. off = (off + 8) % 24;
  81. kp = off;
  82. }
  83. while (dp < dpend) {
  84. dataView.setUint8(dp, dataView.getUint8(dp) ^ keyView.getUint8(kp));
  85. dp++;
  86. kp++;
  87. }
  88. }
  89. }
  90. decodeGoogleEarthEnterpriseData.passThroughDataForTesting = false;
  91. /**
  92. * @private
  93. */
  94. function isBitSet(bits, mask) {
  95. return ((bits & mask) !== 0);
  96. }
  97. // Bitmask for checking tile properties
  98. var childrenBitmasks = [0x01, 0x02, 0x04, 0x08];
  99. var anyChildBitmask = 0x0F;
  100. var cacheFlagBitmask = 0x10; // True if there is a child subtree
  101. var imageBitmask = 0x40;
  102. var terrainBitmask = 0x80;
  103. /**
  104. * Contains information about each tile from a Google Earth Enterprise server
  105. *
  106. * @param {Number} bits Bitmask that contains the type of data and available children for each tile.
  107. * @param {Number} cnodeVersion Version of the request for subtree metadata.
  108. * @param {Number} imageryVersion Version of the request for imagery tile.
  109. * @param {Number} terrainVersion Version of the request for terrain tile.
  110. * @param {Number} imageryProvider Id of imagery provider.
  111. * @param {Number} terrainProvider Id of terrain provider.
  112. *
  113. * @private
  114. */
  115. function GoogleEarthEnterpriseTileInformation(bits, cnodeVersion, imageryVersion, terrainVersion, imageryProvider, terrainProvider) {
  116. this._bits = bits;
  117. this.cnodeVersion = cnodeVersion;
  118. this.imageryVersion = imageryVersion;
  119. this.terrainVersion = terrainVersion;
  120. this.imageryProvider = imageryProvider;
  121. this.terrainProvider = terrainProvider;
  122. this.ancestorHasTerrain = false; // Set it later once we find its parent
  123. this.terrainState = undefined;
  124. }
  125. /**
  126. * Creates GoogleEarthEnterpriseTileInformation from an object
  127. *
  128. * @param {Object} info Object to be cloned
  129. * @param {GoogleEarthEnterpriseTileInformation} [result] The object onto which to store the result.
  130. * @returns {GoogleEarthEnterpriseTileInformation} The modified result parameter or a new GoogleEarthEnterpriseTileInformation instance if none was provided.
  131. */
  132. GoogleEarthEnterpriseTileInformation.clone = function(info, result) {
  133. if (!when.defined(result)) {
  134. result = new GoogleEarthEnterpriseTileInformation(info._bits, info.cnodeVersion, info.imageryVersion, info.terrainVersion,
  135. info.imageryProvider, info.terrainProvider);
  136. } else {
  137. result._bits = info._bits;
  138. result.cnodeVersion = info.cnodeVersion;
  139. result.imageryVersion = info.imageryVersion;
  140. result.terrainVersion = info.terrainVersion;
  141. result.imageryProvider = info.imageryProvider;
  142. result.terrainProvider = info.terrainProvider;
  143. }
  144. result.ancestorHasTerrain = info.ancestorHasTerrain;
  145. result.terrainState = info.terrainState;
  146. return result;
  147. };
  148. /**
  149. * Sets the parent for the tile
  150. *
  151. * @param {GoogleEarthEnterpriseTileInformation} parent Parent tile
  152. */
  153. GoogleEarthEnterpriseTileInformation.prototype.setParent = function(parent) {
  154. this.ancestorHasTerrain = parent.ancestorHasTerrain || this.hasTerrain();
  155. };
  156. /**
  157. * Gets whether a subtree is available
  158. *
  159. * @returns {Boolean} true if subtree is available, false otherwise.
  160. */
  161. GoogleEarthEnterpriseTileInformation.prototype.hasSubtree = function() {
  162. return isBitSet(this._bits, cacheFlagBitmask);
  163. };
  164. /**
  165. * Gets whether imagery is available
  166. *
  167. * @returns {Boolean} true if imagery is available, false otherwise.
  168. */
  169. GoogleEarthEnterpriseTileInformation.prototype.hasImagery = function() {
  170. return isBitSet(this._bits, imageBitmask);
  171. };
  172. /**
  173. * Gets whether terrain is available
  174. *
  175. * @returns {Boolean} true if terrain is available, false otherwise.
  176. */
  177. GoogleEarthEnterpriseTileInformation.prototype.hasTerrain = function() {
  178. return isBitSet(this._bits, terrainBitmask);
  179. };
  180. /**
  181. * Gets whether any children are present
  182. *
  183. * @returns {Boolean} true if any children are available, false otherwise.
  184. */
  185. GoogleEarthEnterpriseTileInformation.prototype.hasChildren = function() {
  186. return isBitSet(this._bits, anyChildBitmask);
  187. };
  188. /**
  189. * Gets whether a specified child is available
  190. *
  191. * @param {Number} index Index of child tile
  192. *
  193. * @returns {Boolean} true if child is available, false otherwise
  194. */
  195. GoogleEarthEnterpriseTileInformation.prototype.hasChild = function(index) {
  196. return isBitSet(this._bits, childrenBitmasks[index]);
  197. };
  198. /**
  199. * Gets bitmask containing children
  200. *
  201. * @returns {Number} Children bitmask
  202. */
  203. GoogleEarthEnterpriseTileInformation.prototype.getChildBitmask = function() {
  204. return this._bits & anyChildBitmask;
  205. };
  206. // Datatype sizes
  207. var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT;
  208. var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT;
  209. var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
  210. var Types = {
  211. METADATA : 0,
  212. TERRAIN : 1,
  213. DBROOT : 2
  214. };
  215. Types.fromString = function(s) {
  216. if (s === 'Metadata') {
  217. return Types.METADATA;
  218. } else if (s === 'Terrain') {
  219. return Types.TERRAIN;
  220. } else if (s === 'DbRoot') {
  221. return Types.DBROOT;
  222. }
  223. };
  224. function decodeGoogleEarthEnterprisePacket(parameters, transferableObjects) {
  225. var type = Types.fromString(parameters.type);
  226. var buffer = parameters.buffer;
  227. decodeGoogleEarthEnterpriseData(parameters.key, buffer);
  228. var uncompressedTerrain = uncompressPacket(buffer);
  229. buffer = uncompressedTerrain.buffer;
  230. var length = uncompressedTerrain.length;
  231. switch (type) {
  232. case Types.METADATA:
  233. return processMetadata(buffer, length, parameters.quadKey);
  234. case Types.TERRAIN:
  235. return processTerrain(buffer, length, transferableObjects);
  236. case Types.DBROOT:
  237. transferableObjects.push(buffer);
  238. return {
  239. buffer : buffer
  240. };
  241. }
  242. }
  243. var qtMagic = 32301;
  244. function processMetadata(buffer, totalSize, quadKey) {
  245. var dv = new DataView(buffer);
  246. var offset = 0;
  247. var magic = dv.getUint32(offset, true);
  248. offset += sizeOfUint32;
  249. if (magic !== qtMagic) {
  250. throw new RuntimeError.RuntimeError('Invalid magic');
  251. }
  252. var dataTypeId = dv.getUint32(offset, true);
  253. offset += sizeOfUint32;
  254. if (dataTypeId !== 1) {
  255. throw new RuntimeError.RuntimeError('Invalid data type. Must be 1 for QuadTreePacket');
  256. }
  257. // Tile format version
  258. var quadVersion = dv.getUint32(offset, true);
  259. offset += sizeOfUint32;
  260. if (quadVersion !== 2) {
  261. throw new RuntimeError.RuntimeError('Invalid QuadTreePacket version. Only version 2 is supported.');
  262. }
  263. var numInstances = dv.getInt32(offset, true);
  264. offset += sizeOfInt32;
  265. var dataInstanceSize = dv.getInt32(offset, true);
  266. offset += sizeOfInt32;
  267. if (dataInstanceSize !== 32) {
  268. throw new RuntimeError.RuntimeError('Invalid instance size.');
  269. }
  270. var dataBufferOffset = dv.getInt32(offset, true);
  271. offset += sizeOfInt32;
  272. var dataBufferSize = dv.getInt32(offset, true);
  273. offset += sizeOfInt32;
  274. var metaBufferSize = dv.getInt32(offset, true);
  275. offset += sizeOfInt32;
  276. // Offset from beginning of packet (instances + current offset)
  277. if (dataBufferOffset !== (numInstances * dataInstanceSize + offset)) {
  278. throw new RuntimeError.RuntimeError('Invalid dataBufferOffset');
  279. }
  280. // Verify the packets is all there header + instances + dataBuffer + metaBuffer
  281. if (dataBufferOffset + dataBufferSize + metaBufferSize !== totalSize) {
  282. throw new RuntimeError.RuntimeError('Invalid packet offsets');
  283. }
  284. // Read all the instances
  285. var instances = [];
  286. for (var i = 0; i < numInstances; ++i) {
  287. var bitfield = dv.getUint8(offset);
  288. ++offset;
  289. ++offset; // 2 byte align
  290. var cnodeVersion = dv.getUint16(offset, true);
  291. offset += sizeOfUint16;
  292. var imageVersion = dv.getUint16(offset, true);
  293. offset += sizeOfUint16;
  294. var terrainVersion = dv.getUint16(offset, true);
  295. offset += sizeOfUint16;
  296. // Number of channels stored in the dataBuffer
  297. offset += sizeOfUint16;
  298. offset += sizeOfUint16; // 4 byte align
  299. // Channel type offset into dataBuffer
  300. offset += sizeOfInt32;
  301. // Channel version offset into dataBuffer
  302. offset += sizeOfInt32;
  303. offset += 8; // Ignore image neighbors for now
  304. // Data providers
  305. var imageProvider = dv.getUint8(offset++);
  306. var terrainProvider = dv.getUint8(offset++);
  307. offset += sizeOfUint16; // 4 byte align
  308. instances.push(new GoogleEarthEnterpriseTileInformation(bitfield, cnodeVersion,
  309. imageVersion, terrainVersion, imageProvider, terrainProvider));
  310. }
  311. var tileInfo = [];
  312. var index = 0;
  313. function populateTiles(parentKey, parent, level) {
  314. var isLeaf = false;
  315. if (level === 4) {
  316. if (parent.hasSubtree()) {
  317. return; // We have a subtree, so just return
  318. }
  319. isLeaf = true; // No subtree, so set all children to null
  320. }
  321. for (var i = 0; i < 4; ++i) {
  322. var childKey = parentKey + i.toString();
  323. if (isLeaf) {
  324. // No subtree so set all children to null
  325. tileInfo[childKey] = null;
  326. } else if (level < 4) {
  327. // We are still in the middle of the subtree, so add child
  328. // only if their bits are set, otherwise set child to null.
  329. if (!parent.hasChild(i)) {
  330. tileInfo[childKey] = null;
  331. } else {
  332. if (index === numInstances) {
  333. console.log('Incorrect number of instances');
  334. return;
  335. }
  336. var instance = instances[index++];
  337. tileInfo[childKey] = instance;
  338. populateTiles(childKey, instance, level + 1);
  339. }
  340. }
  341. }
  342. }
  343. var level = 0;
  344. var root = instances[index++];
  345. if (quadKey === '') {
  346. // Root tile has data at its root and one less level
  347. ++level;
  348. } else {
  349. tileInfo[quadKey] = root; // This will only contain the child bitmask
  350. }
  351. populateTiles(quadKey, root, level);
  352. return tileInfo;
  353. }
  354. function processTerrain(buffer, totalSize, transferableObjects) {
  355. var dv = new DataView(buffer);
  356. var offset = 0;
  357. var terrainTiles = [];
  358. while (offset < totalSize) {
  359. // Each tile is split into 4 parts
  360. var tileStart = offset;
  361. for (var quad = 0; quad < 4; ++quad) {
  362. var size = dv.getUint32(offset, true);
  363. offset += sizeOfUint32;
  364. offset += size;
  365. }
  366. var tile = buffer.slice(tileStart, offset);
  367. transferableObjects.push(tile);
  368. terrainTiles.push(tile);
  369. }
  370. return terrainTiles;
  371. }
  372. var compressedMagic$1 = 0x7468dead;
  373. var compressedMagicSwap$1 = 0xadde6874;
  374. function uncompressPacket(data) {
  375. // The layout of this decoded data is
  376. // Magic Uint32
  377. // Size Uint32
  378. // [GZipped chunk of Size bytes]
  379. // Pullout magic and verify we have the correct data
  380. var dv = new DataView(data);
  381. var offset = 0;
  382. var magic = dv.getUint32(offset, true);
  383. offset += sizeOfUint32;
  384. if (magic !== compressedMagic$1 && magic !== compressedMagicSwap$1) {
  385. throw new RuntimeError.RuntimeError('Invalid magic');
  386. }
  387. // Get the size of the compressed buffer - the endianness depends on which magic was used
  388. var size = dv.getUint32(offset, (magic === compressedMagic$1));
  389. offset += sizeOfUint32;
  390. var compressedPacket = new Uint8Array(data, offset);
  391. var uncompressedPacket = pako_inflate.pako.inflate(compressedPacket);
  392. if (uncompressedPacket.length !== size) {
  393. throw new RuntimeError.RuntimeError('Size of packet doesn\'t match header');
  394. }
  395. return uncompressedPacket;
  396. }
  397. var decodeGoogleEarthEnterprisePacket$1 = createTaskProcessorWorker(decodeGoogleEarthEnterprisePacket);
  398. return decodeGoogleEarthEnterprisePacket$1;
  399. });