108 lines
3.8 KiB
JavaScript
108 lines
3.8 KiB
JavaScript
import { decodeVarintInt32 } from "../../decoding/integerDecodingUtils";
|
|
import { columnTypeHasChildren, columnTypeHasName, decodeColumnType } from "./typeMap";
|
|
const textDecoder = new TextDecoder();
|
|
const SUPPORTED_COLUMN_TYPES = "0-3(ID), 4(GEOMETRY), 10-29(scalars), 30(STRUCT)";
|
|
const SUPPORTED_FIELD_TYPES = "10-29(scalars), 30(STRUCT)";
|
|
/**
|
|
* Decodes a length-prefixed UTF-8 string.
|
|
* Layout: [len: varint32][bytes: len]
|
|
*/
|
|
function decodeString(src, offset) {
|
|
const length = decodeVarintInt32(src, offset, 1)[0];
|
|
if (length === 0) {
|
|
return "";
|
|
}
|
|
const start = offset.get();
|
|
const end = start + length;
|
|
const view = src.subarray(start, end);
|
|
offset.add(length);
|
|
return textDecoder.decode(view);
|
|
}
|
|
/**
|
|
* Converts a Column to a Field.
|
|
* Used when decoding Field metadata which has the same format as Column.
|
|
*/
|
|
function columnToField(column) {
|
|
return {
|
|
name: column.name,
|
|
nullable: column.nullable,
|
|
scalarField: column.scalarType,
|
|
complexField: column.complexType,
|
|
type: column.type === "scalarType" ? "scalarField" : "complexField",
|
|
};
|
|
}
|
|
/**
|
|
* Decodes a Field used as part of complex types (STRUCT children).
|
|
*/
|
|
export function decodeField(src, offset) {
|
|
const typeCode = decodeVarintInt32(src, offset, 1)[0] >>> 0;
|
|
if (typeCode < 10 || typeCode > 30) {
|
|
throw new Error(`Unsupported field type code ${typeCode}. Supported: ${SUPPORTED_FIELD_TYPES}`);
|
|
}
|
|
const column = decodeColumnType(typeCode);
|
|
if (columnTypeHasName(typeCode)) {
|
|
column.name = decodeString(src, offset);
|
|
}
|
|
if (columnTypeHasChildren(typeCode)) {
|
|
const childCount = decodeVarintInt32(src, offset, 1)[0] >>> 0;
|
|
column.complexType.children = new Array(childCount);
|
|
for (let i = 0; i < childCount; i++) {
|
|
column.complexType.children[i] = decodeField(src, offset);
|
|
}
|
|
}
|
|
return columnToField(column);
|
|
}
|
|
/**
|
|
* The typeCode encodes the column type, nullable flag, and whether it has name/children.
|
|
*/
|
|
function decodeColumn(src, offset) {
|
|
const typeCode = decodeVarintInt32(src, offset, 1)[0] >>> 0;
|
|
const column = decodeColumnType(typeCode);
|
|
if (!column) {
|
|
throw new Error(`Unsupported column type code ${typeCode}. Supported: ${SUPPORTED_COLUMN_TYPES}`);
|
|
}
|
|
if (columnTypeHasName(typeCode)) {
|
|
column.name = decodeString(src, offset);
|
|
}
|
|
else {
|
|
// ID and GEOMETRY columns have implicit names
|
|
if (typeCode >= 0 && typeCode <= 3) {
|
|
column.name = "id";
|
|
}
|
|
else if (typeCode === 4) {
|
|
column.name = "geometry";
|
|
}
|
|
}
|
|
if (columnTypeHasChildren(typeCode)) {
|
|
// Only STRUCT (typeCode 30) has children
|
|
const childCount = decodeVarintInt32(src, offset, 1)[0] >>> 0;
|
|
const complexCol = column.complexType;
|
|
complexCol.children = new Array(childCount);
|
|
for (let i = 0; i < childCount; i++) {
|
|
complexCol.children[i] = decodeField(src, offset);
|
|
}
|
|
}
|
|
return column;
|
|
}
|
|
/**
|
|
* Top-level decoder for embedded tileset metadata.
|
|
* Reads exactly ONE FeatureTableSchema from the stream.
|
|
*
|
|
* @param bytes The byte array containing the metadata
|
|
* @param offset The current offset in the byte array (will be advanced)
|
|
*/
|
|
export function decodeEmbeddedTileSetMetadata(bytes, offset) {
|
|
const meta = {};
|
|
meta.featureTables = [];
|
|
const table = {};
|
|
table.name = decodeString(bytes, offset);
|
|
const extent = decodeVarintInt32(bytes, offset, 1)[0] >>> 0;
|
|
const columnCount = decodeVarintInt32(bytes, offset, 1)[0] >>> 0;
|
|
table.columns = new Array(columnCount);
|
|
for (let j = 0; j < columnCount; j++) {
|
|
table.columns[j] = decodeColumn(bytes, offset);
|
|
}
|
|
meta.featureTables.push(table);
|
|
return [meta, extent];
|
|
}
|
|
//# sourceMappingURL=embeddedTilesetMetadataDecoder.js.map
|