decodeGoogleEarthEnterprisePacket.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import decodeGoogleEarthEnterpriseData from '../Core/decodeGoogleEarthEnterpriseData.js';
  2. import GoogleEarthEnterpriseTileInformation from '../Core/GoogleEarthEnterpriseTileInformation.js';
  3. import RuntimeError from '../Core/RuntimeError.js';
  4. import pako from '../ThirdParty/pako_inflate.js';
  5. import createTaskProcessorWorker from './createTaskProcessorWorker.js';
  6. // Datatype sizes
  7. var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT;
  8. var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT;
  9. var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
  10. var Types = {
  11. METADATA : 0,
  12. TERRAIN : 1,
  13. DBROOT : 2
  14. };
  15. Types.fromString = function(s) {
  16. if (s === 'Metadata') {
  17. return Types.METADATA;
  18. } else if (s === 'Terrain') {
  19. return Types.TERRAIN;
  20. } else if (s === 'DbRoot') {
  21. return Types.DBROOT;
  22. }
  23. };
  24. function decodeGoogleEarthEnterprisePacket(parameters, transferableObjects) {
  25. var type = Types.fromString(parameters.type);
  26. var buffer = parameters.buffer;
  27. decodeGoogleEarthEnterpriseData(parameters.key, buffer);
  28. var uncompressedTerrain = uncompressPacket(buffer);
  29. buffer = uncompressedTerrain.buffer;
  30. var length = uncompressedTerrain.length;
  31. switch (type) {
  32. case Types.METADATA:
  33. return processMetadata(buffer, length, parameters.quadKey);
  34. case Types.TERRAIN:
  35. return processTerrain(buffer, length, transferableObjects);
  36. case Types.DBROOT:
  37. transferableObjects.push(buffer);
  38. return {
  39. buffer : buffer
  40. };
  41. }
  42. }
  43. var qtMagic = 32301;
  44. function processMetadata(buffer, totalSize, quadKey) {
  45. var dv = new DataView(buffer);
  46. var offset = 0;
  47. var magic = dv.getUint32(offset, true);
  48. offset += sizeOfUint32;
  49. if (magic !== qtMagic) {
  50. throw new RuntimeError('Invalid magic');
  51. }
  52. var dataTypeId = dv.getUint32(offset, true);
  53. offset += sizeOfUint32;
  54. if (dataTypeId !== 1) {
  55. throw new RuntimeError('Invalid data type. Must be 1 for QuadTreePacket');
  56. }
  57. // Tile format version
  58. var quadVersion = dv.getUint32(offset, true);
  59. offset += sizeOfUint32;
  60. if (quadVersion !== 2) {
  61. throw new RuntimeError('Invalid QuadTreePacket version. Only version 2 is supported.');
  62. }
  63. var numInstances = dv.getInt32(offset, true);
  64. offset += sizeOfInt32;
  65. var dataInstanceSize = dv.getInt32(offset, true);
  66. offset += sizeOfInt32;
  67. if (dataInstanceSize !== 32) {
  68. throw new RuntimeError('Invalid instance size.');
  69. }
  70. var dataBufferOffset = dv.getInt32(offset, true);
  71. offset += sizeOfInt32;
  72. var dataBufferSize = dv.getInt32(offset, true);
  73. offset += sizeOfInt32;
  74. var metaBufferSize = dv.getInt32(offset, true);
  75. offset += sizeOfInt32;
  76. // Offset from beginning of packet (instances + current offset)
  77. if (dataBufferOffset !== (numInstances * dataInstanceSize + offset)) {
  78. throw new RuntimeError('Invalid dataBufferOffset');
  79. }
  80. // Verify the packets is all there header + instances + dataBuffer + metaBuffer
  81. if (dataBufferOffset + dataBufferSize + metaBufferSize !== totalSize) {
  82. throw new RuntimeError('Invalid packet offsets');
  83. }
  84. // Read all the instances
  85. var instances = [];
  86. for (var i = 0; i < numInstances; ++i) {
  87. var bitfield = dv.getUint8(offset);
  88. ++offset;
  89. ++offset; // 2 byte align
  90. var cnodeVersion = dv.getUint16(offset, true);
  91. offset += sizeOfUint16;
  92. var imageVersion = dv.getUint16(offset, true);
  93. offset += sizeOfUint16;
  94. var terrainVersion = dv.getUint16(offset, true);
  95. offset += sizeOfUint16;
  96. // Number of channels stored in the dataBuffer
  97. offset += sizeOfUint16;
  98. offset += sizeOfUint16; // 4 byte align
  99. // Channel type offset into dataBuffer
  100. offset += sizeOfInt32;
  101. // Channel version offset into dataBuffer
  102. offset += sizeOfInt32;
  103. offset += 8; // Ignore image neighbors for now
  104. // Data providers
  105. var imageProvider = dv.getUint8(offset++);
  106. var terrainProvider = dv.getUint8(offset++);
  107. offset += sizeOfUint16; // 4 byte align
  108. instances.push(new GoogleEarthEnterpriseTileInformation(bitfield, cnodeVersion,
  109. imageVersion, terrainVersion, imageProvider, terrainProvider));
  110. }
  111. var tileInfo = [];
  112. var index = 0;
  113. function populateTiles(parentKey, parent, level) {
  114. var isLeaf = false;
  115. if (level === 4) {
  116. if (parent.hasSubtree()) {
  117. return; // We have a subtree, so just return
  118. }
  119. isLeaf = true; // No subtree, so set all children to null
  120. }
  121. for (var i = 0; i < 4; ++i) {
  122. var childKey = parentKey + i.toString();
  123. if (isLeaf) {
  124. // No subtree so set all children to null
  125. tileInfo[childKey] = null;
  126. } else if (level < 4) {
  127. // We are still in the middle of the subtree, so add child
  128. // only if their bits are set, otherwise set child to null.
  129. if (!parent.hasChild(i)) {
  130. tileInfo[childKey] = null;
  131. } else {
  132. if (index === numInstances) {
  133. console.log('Incorrect number of instances');
  134. return;
  135. }
  136. var instance = instances[index++];
  137. tileInfo[childKey] = instance;
  138. populateTiles(childKey, instance, level + 1);
  139. }
  140. }
  141. }
  142. }
  143. var level = 0;
  144. var root = instances[index++];
  145. if (quadKey === '') {
  146. // Root tile has data at its root and one less level
  147. ++level;
  148. } else {
  149. tileInfo[quadKey] = root; // This will only contain the child bitmask
  150. }
  151. populateTiles(quadKey, root, level);
  152. return tileInfo;
  153. }
  154. function processTerrain(buffer, totalSize, transferableObjects) {
  155. var dv = new DataView(buffer);
  156. var offset = 0;
  157. var terrainTiles = [];
  158. while (offset < totalSize) {
  159. // Each tile is split into 4 parts
  160. var tileStart = offset;
  161. for (var quad = 0; quad < 4; ++quad) {
  162. var size = dv.getUint32(offset, true);
  163. offset += sizeOfUint32;
  164. offset += size;
  165. }
  166. var tile = buffer.slice(tileStart, offset);
  167. transferableObjects.push(tile);
  168. terrainTiles.push(tile);
  169. }
  170. return terrainTiles;
  171. }
  172. var compressedMagic = 0x7468dead;
  173. var compressedMagicSwap = 0xadde6874;
  174. function uncompressPacket(data) {
  175. // The layout of this decoded data is
  176. // Magic Uint32
  177. // Size Uint32
  178. // [GZipped chunk of Size bytes]
  179. // Pullout magic and verify we have the correct data
  180. var dv = new DataView(data);
  181. var offset = 0;
  182. var magic = dv.getUint32(offset, true);
  183. offset += sizeOfUint32;
  184. if (magic !== compressedMagic && magic !== compressedMagicSwap) {
  185. throw new RuntimeError('Invalid magic');
  186. }
  187. // Get the size of the compressed buffer - the endianness depends on which magic was used
  188. var size = dv.getUint32(offset, (magic === compressedMagic));
  189. offset += sizeOfUint32;
  190. var compressedPacket = new Uint8Array(data, offset);
  191. var uncompressedPacket = pako.inflate(compressedPacket);
  192. if (uncompressedPacket.length !== size) {
  193. throw new RuntimeError('Size of packet doesn\'t match header');
  194. }
  195. return uncompressedPacket;
  196. }
  197. export default createTaskProcessorWorker(decodeGoogleEarthEnterprisePacket);