Files
Projekt-Visualisierung/node_modules/@maplibre/mlt/dist/mltDecoder.js
2026-04-15 17:08:39 +02:00

153 lines
8.6 KiB
JavaScript

import FeatureTable from "./vector/featureTable";
import { LogicalScalarType, ScalarType } from "./metadata/tileset/tilesetMetadata";
import IntWrapper from "./decoding/intWrapper";
import { decodeStreamMetadata } from "./metadata/tile/streamMetadataDecoder";
import { VectorType } from "./vector/vectorType";
import { Int32FlatVector } from "./vector/flat/int32FlatVector";
import BitVector from "./vector/flat/bitVector";
import { decodeUnsignedConstInt32Stream, decodeUnsignedConstInt64Stream, decodeUnsignedInt64AsFloat64Stream, decodeUnsignedInt32Stream, decodeUnsignedInt64Stream, decodeSequenceInt32Stream, decodeSequenceInt64Stream, getVectorType, } from "./decoding/integerStreamDecoder";
import { Int32SequenceVector } from "./vector/sequence/int32SequenceVector";
import { Int64FlatVector } from "./vector/flat/int64FlatVector";
import { Int64SequenceVector } from "./vector/sequence/int64SequenceVector";
import { decodeVarintInt32 } from "./decoding/integerDecodingUtils";
import { decodeGeometryColumn } from "./decoding/geometryDecoder";
import { decodePropertyColumn } from "./decoding/propertyDecoder";
import { Int32ConstVector } from "./vector/constant/int32ConstVector";
import { Int64ConstVector } from "./vector/constant/int64ConstVector";
import { decodeBooleanRle } from "./decoding/decodingUtils";
import { DoubleFlatVector } from "./vector/flat/doubleFlatVector";
import { decodeEmbeddedTileSetMetadata } from "./metadata/tileset/embeddedTilesetMetadataDecoder";
import { hasStreamCount, isGeometryColumn, isLogicalIdColumn } from "./metadata/tileset/typeMap";
/**
* Decodes a tile with embedded metadata (Tag 0x01 format).
* This is the primary decoder function for MLT tiles.
*
* @param tile The tile data to decode (will be decompressed if gzip-compressed)
* @param geometryScaling Optional geometry scaling parameters
* @param idWithinMaxSafeInteger If true, limits ID values to JavaScript safe integer range (53 bits)
*/
export default function decodeTile(tile, geometryScaling, idWithinMaxSafeInteger = true) {
const offset = new IntWrapper(0);
const featureTables = [];
while (offset.get() < tile.length) {
const blockLength = decodeVarintInt32(tile, offset, 1)[0] >>> 0;
const blockStart = offset.get();
const blockEnd = blockStart + blockLength;
if (blockEnd > tile.length) {
throw new Error(`Block overruns tile: ${blockEnd} > ${tile.length}`);
}
const tag = decodeVarintInt32(tile, offset, 1)[0] >>> 0;
if (tag !== 1) {
// Skip unknown block types
offset.set(blockEnd);
continue;
}
const [metadata, extent] = decodeEmbeddedTileSetMetadata(tile, offset);
const featureTableMetadata = metadata.featureTables[0];
let idVector = null;
let geometryVector = null;
const propertyVectors = [];
let numFeatures = 0;
for (const columnMetadata of featureTableMetadata.columns) {
const columnName = columnMetadata.name;
if (isLogicalIdColumn(columnMetadata)) {
let nullabilityBuffer = null;
// Check column metadata nullable flag, not numStreams (ID columns don't have stream count)
if (columnMetadata.nullable) {
const presentStreamMetadata = decodeStreamMetadata(tile, offset);
const streamDataStart = offset.get();
const values = decodeBooleanRle(tile, presentStreamMetadata.numValues, presentStreamMetadata.byteLength, offset);
offset.set(streamDataStart + presentStreamMetadata.byteLength);
nullabilityBuffer = new BitVector(values, presentStreamMetadata.numValues);
}
const idDataStreamMetadata = decodeStreamMetadata(tile, offset);
// decompressedCount is the count WITHOUT nulls, but we may have nulls
numFeatures = nullabilityBuffer ? nullabilityBuffer.size() : idDataStreamMetadata.decompressedCount;
idVector = decodeIdColumn(tile, columnMetadata, offset, columnName, idDataStreamMetadata, nullabilityBuffer ?? numFeatures, idWithinMaxSafeInteger);
}
else if (isGeometryColumn(columnMetadata)) {
const numStreams = decodeVarintInt32(tile, offset, 1)[0];
// If no ID column, get numFeatures from geometry type stream metadata
if (numFeatures === 0) {
const savedOffset = offset.get();
const geometryTypeMetadata = decodeStreamMetadata(tile, offset);
numFeatures = geometryTypeMetadata.decompressedCount;
offset.set(savedOffset); // Reset to re-read in decodeGeometryColumn
}
if (geometryScaling) {
geometryScaling.scale = geometryScaling.extent / extent;
}
geometryVector = decodeGeometryColumn(tile, numStreams, offset, numFeatures, geometryScaling);
}
else {
const columnHasStreamCount = hasStreamCount(columnMetadata);
const numStreams = columnHasStreamCount ? decodeVarintInt32(tile, offset, 1)[0] : 1;
if (numStreams === 0) {
continue;
}
const propertyVector = decodePropertyColumn(tile, offset, columnMetadata, numStreams, numFeatures, undefined);
if (propertyVector) {
if (Array.isArray(propertyVector)) {
for (const property of propertyVector) {
propertyVectors.push(property);
}
}
else {
propertyVectors.push(propertyVector);
}
}
}
}
const featureTable = new FeatureTable(featureTableMetadata.name, geometryVector, idVector, propertyVectors, extent);
featureTables.push(featureTable);
offset.set(blockEnd);
}
return featureTables;
}
function decodeIdColumn(tile, columnMetadata, offset, columnName, idDataStreamMetadata, sizeOrNullabilityBuffer, idWithinMaxSafeInteger = false) {
const scalarTypeMetadata = columnMetadata.scalarType;
if (!scalarTypeMetadata ||
scalarTypeMetadata.type !== "logicalType" ||
scalarTypeMetadata.logicalType !== LogicalScalarType.ID) {
throw new Error(`ID column must be a logical ID scalar type: ${columnName}`);
}
const idDataType = scalarTypeMetadata.longID ? ScalarType.UINT_64 : ScalarType.UINT_32;
const nullabilityBuffer = typeof sizeOrNullabilityBuffer === "number" ? undefined : sizeOrNullabilityBuffer;
const vectorType = getVectorType(idDataStreamMetadata, sizeOrNullabilityBuffer, tile, offset, idDataType === ScalarType.UINT_64 ? "int64" : "int32");
if (idDataType === ScalarType.UINT_32) {
switch (vectorType) {
case VectorType.FLAT: {
const id = decodeUnsignedInt32Stream(tile, offset, idDataStreamMetadata, undefined, nullabilityBuffer);
return new Int32FlatVector(columnName, id, sizeOrNullabilityBuffer);
}
case VectorType.SEQUENCE: {
const id = decodeSequenceInt32Stream(tile, offset, idDataStreamMetadata);
return new Int32SequenceVector(columnName, id[0], id[1], idDataStreamMetadata.numRleValues);
}
case VectorType.CONST: {
const id = decodeUnsignedConstInt32Stream(tile, offset, idDataStreamMetadata);
return new Int32ConstVector(columnName, id, sizeOrNullabilityBuffer, false);
}
}
}
switch (vectorType) {
case VectorType.FLAT: {
if (idWithinMaxSafeInteger) {
const id = decodeUnsignedInt64AsFloat64Stream(tile, offset, idDataStreamMetadata);
return new DoubleFlatVector(columnName, id, sizeOrNullabilityBuffer);
}
const id = decodeUnsignedInt64Stream(tile, offset, idDataStreamMetadata, nullabilityBuffer);
return new Int64FlatVector(columnName, id, sizeOrNullabilityBuffer);
}
case VectorType.SEQUENCE: {
const id = decodeSequenceInt64Stream(tile, offset, idDataStreamMetadata);
return new Int64SequenceVector(columnName, id[0], id[1], idDataStreamMetadata.numRleValues);
}
case VectorType.CONST: {
const id = decodeUnsignedConstInt64Stream(tile, offset, idDataStreamMetadata);
return new Int64ConstVector(columnName, id, sizeOrNullabilityBuffer, false);
}
}
throw new Error("Vector type not supported for id column.");
}
//# sourceMappingURL=mltDecoder.js.map