Skip to content

Commit

Permalink
decouple metadata decoding from tile decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
springmeyer committed Jun 17, 2024
1 parent 61f790d commit ae056b9
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 23 deletions.
47 changes: 36 additions & 11 deletions js/src/decoder/MltDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Layer } from '../data/Layer';
import { MapLibreTile } from '../data/MapLibreTile';
import { PhysicalLevelTechnique } from '../metadata/stream/PhysicalLevelTechnique';
import { StreamMetadataDecoder } from '../metadata/stream/StreamMetadataDecoder';
import { FeatureTableSchema, TileSetMetadata } from "../metadata/mlt_tileset_metadata_pb";
import { IntWrapper } from './IntWrapper';
import { DecodingUtils } from './DecodingUtils';
import { IntegerDecoder } from './IntegerDecoder';
Expand All @@ -14,7 +13,32 @@ class MltDecoder {
private static ID_COLUMN_NAME = "id";
private static GEOMETRY_COLUMN_NAME = "geometry";

public static decodeMlTile(tile: Uint8Array, tileMetadata: TileSetMetadata): MapLibreTile {
public static generateFeatureTables(tilesetMetadata: any): any {
const featureTables = [];
for (let i=0; i<tilesetMetadata.featureTables.length; i++) {
const featureTable = tilesetMetadata.featureTables[i];
const table = {"name": featureTable.name};
const columns = [];
featureTable.columns.forEach((column) => {
// https://github.com/bufbuild/protobuf-es/blob/main/docs/runtime_api.md#accessing-oneof-groups
if (column.name === "geometry" || column.name === "id") {
columns.push({
"name": column.name
});
} else {
columns.push({
"name": column.name,
"type": column.type.value.type.value
});
}
table['columns'] = columns;
featureTables[i] = table;
});
}
return featureTables;
}

public static decodeMlTile(tile: Uint8Array, featureTables: any): MapLibreTile {
const offset = new IntWrapper(0);
const mltLayers: Layer[] = [];
while (offset.get() < tile.length) {
Expand All @@ -31,12 +55,13 @@ class MltDecoder {
const maxTileExtent = infos[2];
const featureTableId = infos[0];
const numFeatures = infos[3];
const metadata = tileMetadata.featureTables[featureTableId];
if (!metadata) {
// Optimize metadata usage
const featureTableMeta = featureTables[featureTableId];
if (!featureTableMeta) {
console.log(`could not find metadata for feature table id: ${featureTableId}`);
return;
}
for (const columnMetadata of metadata.columns) {
for (const columnMetadata of featureTableMeta.columns) {
const columnName = columnMetadata.name;
const numStreams = DecodingUtils.decodeVarint(tile, offset, 1)[0];
if (columnName === "id") {
Expand All @@ -45,7 +70,7 @@ class MltDecoder {
const presentStream = DecodingUtils.decodeBooleanRle(tile, presentStreamMetadata.numValues(), presentStreamMetadata.byteLength(), offset);
}
// TODO: handle switching on physicalType
// const physicalType = columnMetadata.type.value.type.value;
// const physicalType = columnMetadata.type;

const idDataStreamMetadata = StreamMetadataDecoder.decode(tile, offset);
ids = idDataStreamMetadata.physicalLevelTechnique() === PhysicalLevelTechnique.FAST_PFOR
Expand All @@ -56,7 +81,7 @@ class MltDecoder {
const geometryColumn = GeometryDecoder.decodeGeometryColumn(tile, numStreams, offset);
geometries = GeometryDecoder.decodeGeometry(geometryColumn);
} else {
const propertyColumn = PropertyDecoder.decodePropertyColumn(tile, offset, columnMetadata, numStreams);
const propertyColumn = PropertyDecoder.decodePropertyColumn(tile, offset, columnMetadata.type, numStreams);
if (propertyColumn instanceof Map) {
for (const [key, value] of propertyColumn.entries()) {
properties[key] = value;
Expand All @@ -67,14 +92,14 @@ class MltDecoder {
}
}

const layer = MltDecoder.convertToLayer(ids, geometries, properties, metadata, numFeatures);
const layer = MltDecoder.convertToLayer(ids, geometries, properties, featureTableMeta.name, numFeatures);
mltLayers.push(layer);
}

return new MapLibreTile(mltLayers);
}

private static convertToLayer(ids: number[], geometries, properties, metadata: FeatureTableSchema, numFeatures: number): Layer {
private static convertToLayer(ids: number[], geometries, properties, name: string, numFeatures: number): Layer {
// if (numFeatures != geometries.length || numFeatures != ids.length) {
// console.log(
// "Warning, in convertToLayer the size of ids("
Expand All @@ -84,7 +109,7 @@ class MltDecoder {
// + "), and features("
// + numFeatures
// + ") are not equal for layer: "
// + metadata.name);
// + name);
// }
const features: Feature[] = new Array(numFeatures);
const vals = Object.entries(properties);
Expand All @@ -98,7 +123,7 @@ class MltDecoder {
features[j] = feature;
}

return new Layer(metadata.name, features);
return new Layer(name, features);
}
}

Expand Down
21 changes: 15 additions & 6 deletions js/src/decoder/PropertyDecoder.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
import { StreamMetadata } from '../metadata/stream/StreamMetadata';
import { StreamMetadataDecoder } from '../metadata/stream/StreamMetadataDecoder';
import { Column, ScalarType } from "../metadata/mlt_tileset_metadata_pb";
import { IntWrapper } from './IntWrapper';
import { DecodingUtils } from './DecodingUtils';
import { IntegerDecoder } from './IntegerDecoder';
import { FloatDecoder } from './FloatDecoder';
import { DoubleDecoder } from './DoubleDecoder';
import { StringDecoder } from './StringDecoder';

enum 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
}

class PropertyDecoder {

public static decodePropertyColumn(data: Uint8Array, offset: IntWrapper, column: Column, numStreams: number) {
public static decodePropertyColumn(data: Uint8Array, offset: IntWrapper, physicalType: number, numStreams: number) {
let presentStreamMetadata: StreamMetadata | null = null;

// https://github.com/bufbuild/protobuf-es/blob/main/docs/runtime_api.md#accessing-oneof-groups
const scalarColumn = column.type.case;
if (scalarColumn !== undefined) {
if (physicalType !== undefined) {
let presentStream = null;
let numValues = 0;
if (numStreams > 1) {
presentStreamMetadata = StreamMetadataDecoder.decode(data, offset);
numValues = presentStreamMetadata.numValues();
presentStream = DecodingUtils.decodeBooleanRle(data, presentStreamMetadata.numValues(), presentStreamMetadata.byteLength(), offset);
}
const physicalType = column.type.value.type.value;
switch (physicalType) {
case ScalarType.BOOLEAN: {
const dataStreamMetadata = StreamMetadataDecoder.decode(data, offset);
Expand Down
9 changes: 4 additions & 5 deletions js/src/decoder/StringDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { IntWrapper } from './IntWrapper';
import { StreamMetadataDecoder } from '../metadata/stream/StreamMetadataDecoder';
import { IntegerDecoder } from './IntegerDecoder';

const textDecoder = new TextDecoder("utf-8");

export class StringDecoder {

/*
Expand Down Expand Up @@ -67,19 +69,17 @@ export class StringDecoder {
const decodedValues: string[] = [];
let lengthOffset = 0;
let strOffset = 0;
const decoder = new TextDecoder("utf-8");
for (let i = 0; i < numValues; i++) {
const present = presentStream[i];
if (present) {
const length = lengthStream[lengthOffset++];
const value = decoder.decode(utf8Values.slice(strOffset, strOffset + length));
const value = textDecoder.decode(utf8Values.slice(strOffset, strOffset + length));
decodedValues.push(value);
strOffset += length;
} else {
decodedValues.push(null);
}
}

return decodedValues;
}

Expand All @@ -89,9 +89,8 @@ export class StringDecoder {
): string[] {
const dictionary: string[] = [];
let dictionaryOffset = 0;
const decoder = new TextDecoder("utf-8");
for (const length of lengthStream) {
const value = decoder.decode(utf8Values.slice(dictionaryOffset, dictionaryOffset + length));
const value = textDecoder.decode(utf8Values.slice(dictionaryOffset, dictionaryOffset + length));
dictionary.push(value);
dictionaryOffset += length;
}
Expand Down
1 change: 0 additions & 1 deletion js/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { MltDecoder } from './decoder/MltDecoder';
export { TileSetMetadata } from './metadata/mlt_tileset_metadata_pb';

0 comments on commit ae056b9

Please sign in to comment.