Initial commit

This commit is contained in:
2026-04-15 17:08:39 +02:00
parent ae164c47a8
commit 47fd1c2b7a
1819 changed files with 685388 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
import type IntWrapper from "../../decoding/intWrapper";
import type { Field, TileSetMetadata } from "./tilesetMetadata";
/**
* Decodes a Field used as part of complex types (STRUCT children).
*/
export declare function decodeField(src: Uint8Array, offset: IntWrapper): Field;
/**
* 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 declare function decodeEmbeddedTileSetMetadata(bytes: Uint8Array, offset: IntWrapper): [TileSetMetadata, number];

View File

@@ -0,0 +1,108 @@
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

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,80 @@
export declare const ColumnScope: {
readonly FEATURE: 0;
readonly VERTEX: 1;
};
export declare const ScalarType: {
readonly BOOLEAN: 0;
readonly INT_8: 1;
readonly UINT_8: 2;
readonly INT_32: 3;
readonly UINT_32: 4;
readonly INT_64: 5;
readonly UINT_64: 6;
readonly FLOAT: 7;
readonly DOUBLE: 8;
readonly STRING: 9;
};
export declare const ComplexType: {
readonly GEOMETRY: 0;
readonly STRUCT: 1;
};
export declare const LogicalScalarType: {
readonly ID: 0;
};
export declare const LogicalComplexType: {
readonly BINARY: 0;
readonly RANGE_MAP: 1;
};
export interface TileSetMetadata {
version?: number | null;
featureTables: FeatureTableSchema[];
name?: string | null;
description?: string | null;
attribution?: string | null;
minZoom?: number | null;
maxZoom?: number | null;
bounds: number[];
center: number[];
}
export interface FeatureTableSchema {
name?: string | null;
columns: Column[];
}
export interface Column {
name?: string | null;
nullable?: boolean | null;
columnScope?: number | null;
scalarType?: ScalarColumn | null;
complexType?: ComplexColumn | null;
type?: "scalarType" | "complexType";
}
export interface ScalarColumn {
longID?: boolean | null;
physicalType?: number | null;
logicalType?: number | null;
type?: "physicalType" | "logicalType";
}
export interface ComplexColumn {
physicalType?: number | null;
logicalType?: number | null;
children: Field[];
type?: "physicalType" | "logicalType";
}
export interface Field {
name?: string | null;
nullable?: boolean | null;
scalarField?: ScalarField | null;
complexField?: ComplexField | null;
type?: "scalarField" | "complexField";
}
export interface ScalarField {
physicalType?: number | null;
logicalType?: number | null;
type?: "physicalType" | "logicalType";
}
export interface ComplexField {
physicalType?: number | null;
logicalType?: number | null;
children: Field[];
type?: "physicalType" | "logicalType";
}

View File

@@ -0,0 +1,29 @@
// based on ../spec/schema/mlt_tileset_metadata.proto
export const ColumnScope = {
FEATURE: 0,
VERTEX: 1,
};
export const ScalarType = {
BOOLEAN: 0,
INT_8: 1,
UINT_8: 2,
INT_32: 3,
UINT_32: 4,
INT_64: 5,
UINT_64: 6,
FLOAT: 7,
DOUBLE: 8,
STRING: 9,
};
export const ComplexType = {
GEOMETRY: 0,
STRUCT: 1,
};
export const LogicalScalarType = {
ID: 0,
};
export const LogicalComplexType = {
BINARY: 0,
RANGE_MAP: 1,
};
//# sourceMappingURL=tilesetMetadata.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"tilesetMetadata.js","sourceRoot":"","sources":["../../../src/metadata/tileset/tilesetMetadata.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,CAAC,MAAM,WAAW,GAAG;IACvB,OAAO,EAAE,CAAC;IACV,MAAM,EAAE,CAAC;CACH,CAAC;AAEX,MAAM,CAAC,MAAM,UAAU,GAAG;IACtB,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;IACV,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;CACH,CAAC;AAEX,MAAM,CAAC,MAAM,WAAW,GAAG;IACvB,QAAQ,EAAE,CAAC;IACX,MAAM,EAAE,CAAC;CACH,CAAC;AAEX,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC7B,EAAE,EAAE,CAAC;CACC,CAAC;AAEX,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAC9B,MAAM,EAAE,CAAC;IACT,SAAS,EAAE,CAAC;CACN,CAAC","sourcesContent":["// based on ../spec/schema/mlt_tileset_metadata.proto\nexport const ColumnScope = {\n FEATURE: 0,\n VERTEX: 1,\n} as const;\n\nexport const ScalarType = {\n BOOLEAN: 0,\n INT_8: 1,\n UINT_8: 2,\n INT_32: 3,\n UINT_32: 4,\n INT_64: 5,\n UINT_64: 6,\n FLOAT: 7,\n DOUBLE: 8,\n STRING: 9,\n} as const;\n\nexport const ComplexType = {\n GEOMETRY: 0,\n STRUCT: 1,\n} as const;\n\nexport const LogicalScalarType = {\n ID: 0,\n} as const;\n\nexport const LogicalComplexType = {\n BINARY: 0,\n RANGE_MAP: 1,\n} as const;\n\nexport interface TileSetMetadata {\n version?: number | null;\n featureTables: FeatureTableSchema[];\n name?: string | null;\n description?: string | null;\n attribution?: string | null;\n minZoom?: number | null;\n maxZoom?: number | null;\n bounds: number[];\n center: number[];\n}\n\nexport interface FeatureTableSchema {\n name?: string | null;\n columns: Column[];\n}\n\nexport interface Column {\n name?: string | null;\n nullable?: boolean | null;\n columnScope?: number | null;\n scalarType?: ScalarColumn | null;\n complexType?: ComplexColumn | null;\n type?: \"scalarType\" | \"complexType\";\n}\n\nexport interface ScalarColumn {\n longID?: boolean | null;\n physicalType?: number | null;\n logicalType?: number | null;\n type?: \"physicalType\" | \"logicalType\";\n}\n\nexport interface ComplexColumn {\n physicalType?: number | null;\n logicalType?: number | null;\n children: Field[];\n type?: \"physicalType\" | \"logicalType\";\n}\n\nexport interface Field {\n name?: string | null;\n nullable?: boolean | null;\n scalarField?: ScalarField | null;\n complexField?: ComplexField | null;\n type?: \"scalarField\" | \"complexField\";\n}\n\nexport interface ScalarField {\n physicalType?: number | null;\n logicalType?: number | null;\n type?: \"physicalType\" | \"logicalType\";\n}\n\nexport interface ComplexField {\n physicalType?: number | null;\n logicalType?: number | null;\n children: Field[];\n type?: \"physicalType\" | \"logicalType\";\n}\n"]}

View File

@@ -0,0 +1,38 @@
import { type Column } from "./tilesetMetadata";
/**
* The type code is a single varint32 that encodes:
* - Physical or logical type
* - Nullable flag
* - Whether the column has a name (typeCode >= 10)
* - Whether the column has children (typeCode == 30 for STRUCT)
* - For ID types: whether it uses long (64-bit) IDs
*/
/**
* Decodes a type code into a Column structure.
*
* ID type codes (0..3):
* - Bit 0: nullable
* - Bit 1: longID (0/1 -> uint32 IDs, 2/3 -> uint64 IDs)
*
* ID columns are kept as logical types so they remain distinguishable
* from feature properties that may also be named "id".
*/
export declare function decodeColumnType(typeCode: number): Column | null;
/**
* Returns true if this type code requires a name to be stored.
* ID (0-3) and GEOMETRY (4) columns have implicit names.
* All other types (>= 10) require explicit names.
*/
export declare function columnTypeHasName(typeCode: number): boolean;
/**
* Returns true if this type code has child fields.
* Only STRUCT (typeCode 30) has children.
*/
export declare function columnTypeHasChildren(typeCode: number): boolean;
/**
* Determines if a stream count needs to be read for this column.
* Mirrors the logic in cpp/include/mlt/metadata/type_map.hpp lines 85-122
*/
export declare function hasStreamCount(column: Column): boolean;
export declare function isLogicalIdColumn(column: Column): boolean;
export declare function isGeometryColumn(column: Column): boolean;

View File

@@ -0,0 +1,197 @@
import { ColumnScope, ComplexType, LogicalScalarType, ScalarType, } from "./tilesetMetadata";
/**
* The type code is a single varint32 that encodes:
* - Physical or logical type
* - Nullable flag
* - Whether the column has a name (typeCode >= 10)
* - Whether the column has children (typeCode == 30 for STRUCT)
* - For ID types: whether it uses long (64-bit) IDs
*/
/**
* Decodes a type code into a Column structure.
*
* ID type codes (0..3):
* - Bit 0: nullable
* - Bit 1: longID (0/1 -> uint32 IDs, 2/3 -> uint64 IDs)
*
* ID columns are kept as logical types so they remain distinguishable
* from feature properties that may also be named "id".
*/
export function decodeColumnType(typeCode) {
switch (typeCode) {
case 0:
case 1:
case 2:
case 3: {
const column = {};
column.nullable = (typeCode & 1) !== 0;
column.columnScope = ColumnScope.FEATURE;
const scalarCol = {};
scalarCol.type = "logicalType";
scalarCol.logicalType = LogicalScalarType.ID;
scalarCol.longID = (typeCode & 2) !== 0;
column.scalarType = scalarCol;
column.type = "scalarType";
return column;
}
case 4: {
// GEOMETRY (non-nullable, no children)
const column = {};
column.nullable = false;
column.columnScope = ColumnScope.FEATURE;
const complexCol = {};
complexCol.type = "physicalType";
complexCol.physicalType = ComplexType.GEOMETRY;
column.type = "complexType";
column.complexType = complexCol;
return column;
}
case 30: {
// STRUCT (non-nullable with children)
const column = {};
column.nullable = false;
column.columnScope = ColumnScope.FEATURE;
const complexCol = {};
complexCol.type = "physicalType";
complexCol.physicalType = ComplexType.STRUCT;
column.type = "complexType";
column.complexType = complexCol;
return column;
}
default:
return mapScalarType(typeCode);
}
}
/**
* Returns true if this type code requires a name to be stored.
* ID (0-3) and GEOMETRY (4) columns have implicit names.
* All other types (>= 10) require explicit names.
*/
export function columnTypeHasName(typeCode) {
return typeCode >= 10;
}
/**
* Returns true if this type code has child fields.
* Only STRUCT (typeCode 30) has children.
*/
export function columnTypeHasChildren(typeCode) {
return typeCode === 30;
}
/**
* Determines if a stream count needs to be read for this column.
* Mirrors the logic in cpp/include/mlt/metadata/type_map.hpp lines 85-122
*/
export function hasStreamCount(column) {
if (column.type === "scalarType") {
const scalarCol = column.scalarType;
if (scalarCol.type === "physicalType") {
const physicalType = scalarCol.physicalType;
switch (physicalType) {
case ScalarType.BOOLEAN:
case ScalarType.INT_8:
case ScalarType.UINT_8:
case ScalarType.INT_32:
case ScalarType.UINT_32:
case ScalarType.INT_64:
case ScalarType.UINT_64:
case ScalarType.FLOAT:
case ScalarType.DOUBLE:
return false;
case ScalarType.STRING:
return true;
default:
return false;
}
}
if (scalarCol.type === "logicalType") {
return false;
}
}
else if (column.type === "complexType") {
const complexCol = column.complexType;
if (complexCol.type === "physicalType") {
const physicalType = complexCol.physicalType;
switch (physicalType) {
case ComplexType.GEOMETRY:
case ComplexType.STRUCT:
return true;
default:
return false;
}
}
}
console.warn("Unexpected column type in hasStreamCount", column);
return false;
}
export function isLogicalIdColumn(column) {
return (column.type === "scalarType" &&
column.scalarType?.type === "logicalType" &&
column.scalarType.logicalType === LogicalScalarType.ID);
}
export function isGeometryColumn(column) {
return (column.type === "complexType" &&
column.complexType?.type === "physicalType" &&
column.complexType.physicalType === ComplexType.GEOMETRY);
}
/**
* Maps a scalar type code to a Column with ScalarType.
* Type codes 10-29 encode scalar types with nullable flag.
* Even codes are non-nullable, odd codes are nullable.
*/
function mapScalarType(typeCode) {
let scalarType;
switch (typeCode) {
case 10:
case 11:
scalarType = ScalarType.BOOLEAN;
break;
case 12:
case 13:
scalarType = ScalarType.INT_8;
break;
case 14:
case 15:
scalarType = ScalarType.UINT_8;
break;
case 16:
case 17:
scalarType = ScalarType.INT_32;
break;
case 18:
case 19:
scalarType = ScalarType.UINT_32;
break;
case 20:
case 21:
scalarType = ScalarType.INT_64;
break;
case 22:
case 23:
scalarType = ScalarType.UINT_64;
break;
case 24:
case 25:
scalarType = ScalarType.FLOAT;
break;
case 26:
case 27:
scalarType = ScalarType.DOUBLE;
break;
case 28:
case 29:
scalarType = ScalarType.STRING;
break;
default:
return null;
}
const column = {};
column.nullable = (typeCode & 1) !== 0;
column.columnScope = ColumnScope.FEATURE;
const scalarCol = {};
scalarCol.type = "physicalType";
scalarCol.physicalType = scalarType;
column.type = "scalarType";
column.scalarType = scalarCol;
return column;
}
//# sourceMappingURL=typeMap.js.map

File diff suppressed because one or more lines are too long