You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
6.9 KiB
JavaScript

import decodeGoogleEarthEnterpriseData from "../Core/decodeGoogleEarthEnterpriseData.js";
import GoogleEarthEnterpriseTileInformation from "../Core/GoogleEarthEnterpriseTileInformation.js";
import RuntimeError from "../Core/RuntimeError.js";
import pako from "../ThirdParty/pako_inflate.js";
import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
// Datatype sizes
var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT;
var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT;
var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
var Types = {
METADATA: 0,
TERRAIN: 1,
DBROOT: 2,
};
Types.fromString = function (s) {
if (s === "Metadata") {
return Types.METADATA;
} else if (s === "Terrain") {
return Types.TERRAIN;
} else if (s === "DbRoot") {
return Types.DBROOT;
}
};
function decodeGoogleEarthEnterprisePacket(parameters, transferableObjects) {
var type = Types.fromString(parameters.type);
var buffer = parameters.buffer;
decodeGoogleEarthEnterpriseData(parameters.key, buffer);
var uncompressedTerrain = uncompressPacket(buffer);
buffer = uncompressedTerrain.buffer;
var length = uncompressedTerrain.length;
switch (type) {
case Types.METADATA:
return processMetadata(buffer, length, parameters.quadKey);
case Types.TERRAIN:
return processTerrain(buffer, length, transferableObjects);
case Types.DBROOT:
transferableObjects.push(buffer);
return {
buffer: buffer,
};
}
}
var qtMagic = 32301;
function processMetadata(buffer, totalSize, quadKey) {
var dv = new DataView(buffer);
var offset = 0;
var magic = dv.getUint32(offset, true);
offset += sizeOfUint32;
if (magic !== qtMagic) {
throw new RuntimeError("Invalid magic");
}
var dataTypeId = dv.getUint32(offset, true);
offset += sizeOfUint32;
if (dataTypeId !== 1) {
throw new RuntimeError("Invalid data type. Must be 1 for QuadTreePacket");
}
// Tile format version
var quadVersion = dv.getUint32(offset, true);
offset += sizeOfUint32;
if (quadVersion !== 2) {
throw new RuntimeError(
"Invalid QuadTreePacket version. Only version 2 is supported."
);
}
var numInstances = dv.getInt32(offset, true);
offset += sizeOfInt32;
var dataInstanceSize = dv.getInt32(offset, true);
offset += sizeOfInt32;
if (dataInstanceSize !== 32) {
throw new RuntimeError("Invalid instance size.");
}
var dataBufferOffset = dv.getInt32(offset, true);
offset += sizeOfInt32;
var dataBufferSize = dv.getInt32(offset, true);
offset += sizeOfInt32;
var metaBufferSize = dv.getInt32(offset, true);
offset += sizeOfInt32;
// Offset from beginning of packet (instances + current offset)
if (dataBufferOffset !== numInstances * dataInstanceSize + offset) {
throw new RuntimeError("Invalid dataBufferOffset");
}
// Verify the packets is all there header + instances + dataBuffer + metaBuffer
if (dataBufferOffset + dataBufferSize + metaBufferSize !== totalSize) {
throw new RuntimeError("Invalid packet offsets");
}
// Read all the instances
var instances = [];
for (var i = 0; i < numInstances; ++i) {
var bitfield = dv.getUint8(offset);
++offset;
++offset; // 2 byte align
var cnodeVersion = dv.getUint16(offset, true);
offset += sizeOfUint16;
var imageVersion = dv.getUint16(offset, true);
offset += sizeOfUint16;
var terrainVersion = dv.getUint16(offset, true);
offset += sizeOfUint16;
// Number of channels stored in the dataBuffer
offset += sizeOfUint16;
offset += sizeOfUint16; // 4 byte align
// Channel type offset into dataBuffer
offset += sizeOfInt32;
// Channel version offset into dataBuffer
offset += sizeOfInt32;
offset += 8; // Ignore image neighbors for now
// Data providers
var imageProvider = dv.getUint8(offset++);
var terrainProvider = dv.getUint8(offset++);
offset += sizeOfUint16; // 4 byte align
instances.push(
new GoogleEarthEnterpriseTileInformation(
bitfield,
cnodeVersion,
imageVersion,
terrainVersion,
imageProvider,
terrainProvider
)
);
}
var tileInfo = [];
var index = 0;
function populateTiles(parentKey, parent, level) {
var isLeaf = false;
if (level === 4) {
if (parent.hasSubtree()) {
return; // We have a subtree, so just return
}
isLeaf = true; // No subtree, so set all children to null
}
for (var i = 0; i < 4; ++i) {
var childKey = parentKey + i.toString();
if (isLeaf) {
// No subtree so set all children to null
tileInfo[childKey] = null;
} else if (level < 4) {
// We are still in the middle of the subtree, so add child
// only if their bits are set, otherwise set child to null.
if (!parent.hasChild(i)) {
tileInfo[childKey] = null;
} else {
if (index === numInstances) {
console.log("Incorrect number of instances");
return;
}
var instance = instances[index++];
tileInfo[childKey] = instance;
populateTiles(childKey, instance, level + 1);
}
}
}
}
var level = 0;
var root = instances[index++];
if (quadKey === "") {
// Root tile has data at its root and one less level
++level;
} else {
tileInfo[quadKey] = root; // This will only contain the child bitmask
}
populateTiles(quadKey, root, level);
return tileInfo;
}
function processTerrain(buffer, totalSize, transferableObjects) {
var dv = new DataView(buffer);
var offset = 0;
var terrainTiles = [];
while (offset < totalSize) {
// Each tile is split into 4 parts
var tileStart = offset;
for (var quad = 0; quad < 4; ++quad) {
var size = dv.getUint32(offset, true);
offset += sizeOfUint32;
offset += size;
}
var tile = buffer.slice(tileStart, offset);
transferableObjects.push(tile);
terrainTiles.push(tile);
}
return terrainTiles;
}
var compressedMagic = 0x7468dead;
var compressedMagicSwap = 0xadde6874;
function uncompressPacket(data) {
// The layout of this decoded data is
// Magic Uint32
// Size Uint32
// [GZipped chunk of Size bytes]
// Pullout magic and verify we have the correct data
var dv = new DataView(data);
var offset = 0;
var magic = dv.getUint32(offset, true);
offset += sizeOfUint32;
if (magic !== compressedMagic && magic !== compressedMagicSwap) {
throw new RuntimeError("Invalid magic");
}
// Get the size of the compressed buffer - the endianness depends on which magic was used
var size = dv.getUint32(offset, magic === compressedMagic);
offset += sizeOfUint32;
var compressedPacket = new Uint8Array(data, offset);
var uncompressedPacket = pako.inflate(compressedPacket);
if (uncompressedPacket.length !== size) {
throw new RuntimeError("Size of packet doesn't match header");
}
return uncompressedPacket;
}
export default createTaskProcessorWorker(decodeGoogleEarthEnterprisePacket);