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,289 @@
import { decodeStreamMetadata } from "../metadata/tile/streamMetadataDecoder";
import { decodeSignedInt32Stream, decodeLengthStreamToOffsetBuffer, decodeUnsignedConstInt32Stream, decodeUnsignedInt32Stream, getVectorType, } from "./integerStreamDecoder";
import { VectorType } from "../vector/vectorType";
import { PhysicalStreamType } from "../metadata/tile/physicalStreamType";
import { LengthType } from "../metadata/tile/lengthType";
import { DictionaryType } from "../metadata/tile/dictionaryType";
import { createConstGeometryVector, createMortonEncodedConstGeometryVector, } from "../vector/geometry/constGeometryVector";
import { createFlatGeometryVector, createFlatGeometryVectorMortonEncoded } from "../vector/geometry/flatGeometryVector";
import { OffsetType } from "../metadata/tile/offsetType";
import { createConstGpuVector } from "../vector/geometry/constGpuVector";
import { createFlatGpuVector } from "../vector/geometry/flatGpuVector";
// TODO: get rid of numFeatures parameter
export function decodeGeometryColumn(tile, numStreams, offset, numFeatures, scalingData) {
const geometryTypeMetadata = decodeStreamMetadata(tile, offset);
const geometryTypesVectorType = getVectorType(geometryTypeMetadata, numFeatures, tile, offset);
let vertexOffsets;
let vertexBuffer;
let mortonSettings;
let indexBuffer;
if (geometryTypesVectorType === VectorType.CONST) {
/* All geometries in the column have the same geometry type */
const geometryType = decodeUnsignedConstInt32Stream(tile, offset, geometryTypeMetadata);
// Variables for const geometry path (directly decoded as offsets)
let geometryOffsets;
let partOffsets;
let ringOffsets;
//TODO: use geometryOffsets for that? -> but then tessellated polygons can't be used with normal polygons
// in one FeatureTable?
let triangleOffsets;
for (let i = 0; i < numStreams - 1; i++) {
const geometryStreamMetadata = decodeStreamMetadata(tile, offset);
switch (geometryStreamMetadata.physicalStreamType) {
case PhysicalStreamType.LENGTH:
switch (geometryStreamMetadata.logicalStreamType.lengthType) {
case LengthType.GEOMETRIES:
geometryOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
break;
case LengthType.PARTS:
partOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
break;
case LengthType.RINGS:
ringOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
break;
case LengthType.TRIANGLES:
triangleOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
}
break;
case PhysicalStreamType.OFFSET: {
switch (geometryStreamMetadata.logicalStreamType.offsetType) {
case OffsetType.VERTEX:
vertexOffsets = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case OffsetType.INDEX:
indexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
}
break;
}
case PhysicalStreamType.DATA: {
if (DictionaryType.VERTEX === geometryStreamMetadata.logicalStreamType.dictionaryType) {
vertexBuffer = decodeSignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);
}
else {
const mortonMetadata = geometryStreamMetadata;
mortonSettings = {
numBits: mortonMetadata.numBits,
coordinateShift: mortonMetadata.coordinateShift,
};
vertexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);
}
break;
}
}
}
if (indexBuffer) {
if (geometryOffsets !== undefined || partOffsets !== undefined) {
/* Case when the indices of a Polygon outline are encoded in the tile */
const topologyVector = { geometryOffsets, partOffsets, ringOffsets };
return createConstGpuVector(numFeatures, geometryType, triangleOffsets, indexBuffer, vertexBuffer, topologyVector);
}
/* Case when the no Polygon outlines are encoded in the tile */
return createConstGpuVector(numFeatures, geometryType, triangleOffsets, indexBuffer, vertexBuffer);
}
return mortonSettings === undefined
? /* Currently only 2D coordinates (Vec2) are implemented in the encoder */
createConstGeometryVector(numFeatures, geometryType, { geometryOffsets, partOffsets, ringOffsets }, vertexOffsets, vertexBuffer)
: createMortonEncodedConstGeometryVector(numFeatures, geometryType, { geometryOffsets, partOffsets, ringOffsets }, vertexOffsets, vertexBuffer, mortonSettings);
}
/* Different geometry types are mixed in the geometry column */
const geometryTypeVector = decodeUnsignedInt32Stream(tile, offset, geometryTypeMetadata);
// Variables for flat geometry path (decoded as lengths, then converted to offsets)
let geometryLengths;
let partLengths;
let ringLengths;
//TODO: use geometryOffsets for that? -> but then tessellated polygons can't be used with normal polygons
// in one FeatureTable?
let triangleOffsets;
for (let i = 0; i < numStreams - 1; i++) {
const geometryStreamMetadata = decodeStreamMetadata(tile, offset);
switch (geometryStreamMetadata.physicalStreamType) {
case PhysicalStreamType.LENGTH:
switch (geometryStreamMetadata.logicalStreamType.lengthType) {
case LengthType.GEOMETRIES:
geometryLengths = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case LengthType.PARTS:
partLengths = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case LengthType.RINGS:
ringLengths = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case LengthType.TRIANGLES:
triangleOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
}
break;
case PhysicalStreamType.OFFSET:
switch (geometryStreamMetadata.logicalStreamType.offsetType) {
case OffsetType.VERTEX:
vertexOffsets = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case OffsetType.INDEX:
indexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
}
break;
case PhysicalStreamType.DATA:
if (DictionaryType.VERTEX === geometryStreamMetadata.logicalStreamType.dictionaryType) {
vertexBuffer = decodeSignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);
}
else {
const mortonMetadata = geometryStreamMetadata;
mortonSettings = {
numBits: mortonMetadata.numBits,
coordinateShift: mortonMetadata.coordinateShift,
};
vertexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);
}
break;
}
}
// TODO: refactor the following instructions -> decode in one pass for performance reasons
/* Calculate the offsets from the length buffer for util access */
let geometryOffsets;
let partOffsets;
let ringOffsets;
if (geometryLengths) {
geometryOffsets = decodeRootLengthStream(geometryTypeVector, geometryLengths, 2);
if (partLengths && ringLengths) {
partOffsets = decodeLevel1LengthStream(geometryTypeVector, geometryOffsets, partLengths, false);
ringOffsets = decodeLevel2LengthStream(geometryTypeVector, geometryOffsets, partOffsets, ringLengths);
}
else if (partLengths) {
partOffsets = decodeLevel1WithoutRingBufferLengthStream(geometryTypeVector, geometryOffsets, partLengths);
}
}
else if (partLengths && ringLengths) {
partOffsets = decodeRootLengthStream(geometryTypeVector, partLengths, 1);
ringOffsets = decodeLevel1LengthStream(geometryTypeVector, partOffsets, ringLengths, true);
}
else if (partLengths) {
partOffsets = decodeRootLengthStream(geometryTypeVector, partLengths, 0);
}
if (indexBuffer && !partOffsets) {
/* Case when the indices of a Polygon outline are not encoded in the data so no
* topology data are present in the tile */
return createFlatGpuVector(geometryTypeVector, triangleOffsets, indexBuffer, vertexBuffer);
}
if (indexBuffer) {
/* Case when the indices of a Polygon outline are encoded in the tile */
return createFlatGpuVector(geometryTypeVector, triangleOffsets, indexBuffer, vertexBuffer, {
geometryOffsets,
partOffsets,
ringOffsets,
});
}
return mortonSettings === undefined /* Currently only 2D coordinates (Vec2) are implemented in the encoder */
? createFlatGeometryVector(geometryTypeVector, { geometryOffsets, partOffsets, ringOffsets }, vertexOffsets, vertexBuffer)
: createFlatGeometryVectorMortonEncoded(geometryTypeVector, { geometryOffsets, partOffsets, ringOffsets }, vertexOffsets, vertexBuffer, mortonSettings);
}
/*
* Handle the parsing of the different topology length buffers separate not generic to reduce the
* branching and improve the performance
*/
function decodeRootLengthStream(geometryTypes, rootLengthStream, bufferId) {
const rootBufferOffsets = new Uint32Array(geometryTypes.length + 1);
let previousOffset = 0;
rootBufferOffsets[0] = previousOffset;
let rootLengthCounter = 0;
for (let i = 0; i < geometryTypes.length; i++) {
/* Test if the geometry has and entry in the root buffer
* BufferId: 2 GeometryOffsets -> MultiPolygon, MultiLineString, MultiPoint
* BufferId: 1 PartOffsets -> Polygon
* BufferId: 0 PartOffsets, RingOffsets -> LineString
* */
previousOffset = rootBufferOffsets[i + 1] =
previousOffset + (geometryTypes[i] > bufferId ? rootLengthStream[rootLengthCounter++] : 1);
}
return rootBufferOffsets;
}
function decodeLevel1LengthStream(geometryTypes, rootOffsetBuffer, level1LengthBuffer, isLineStringPresent) {
const level1BufferOffsets = new Uint32Array(rootOffsetBuffer[rootOffsetBuffer.length - 1] + 1);
let previousOffset = 0;
level1BufferOffsets[0] = previousOffset;
let level1BufferCounter = 1;
let level1LengthBufferCounter = 0;
for (let i = 0; i < geometryTypes.length; i++) {
const geometryType = geometryTypes[i];
const numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];
if (geometryType === 5 ||
geometryType === 2 ||
(isLineStringPresent && (geometryType === 4 || geometryType === 1))) {
/* For MultiPolygon, Polygon and in some cases for MultiLineString and LineString
* a value in the level1LengthBuffer exists */
for (let j = 0; j < numGeometries; j++) {
previousOffset = level1BufferOffsets[level1BufferCounter++] =
previousOffset + level1LengthBuffer[level1LengthBufferCounter++];
}
}
else {
/* For MultiPoint and Point and in some cases for MultiLineString and LineString no value in the
* level1LengthBuffer exists */
for (let j = 0; j < numGeometries; j++) {
level1BufferOffsets[level1BufferCounter++] = ++previousOffset;
}
}
}
return level1BufferOffsets;
}
/*
* Case where no ring buffer exists so no MultiPolygon or Polygon geometry is part of the buffer
*/
function decodeLevel1WithoutRingBufferLengthStream(geometryTypes, rootOffsetBuffer, level1LengthBuffer) {
const level1BufferOffsets = new Uint32Array(rootOffsetBuffer[rootOffsetBuffer.length - 1] + 1);
let previousOffset = 0;
level1BufferOffsets[0] = previousOffset;
let level1OffsetBufferCounter = 1;
let level1LengthCounter = 0;
for (let i = 0; i < geometryTypes.length; i++) {
const geometryType = geometryTypes[i];
const numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];
if (geometryType === 4 || geometryType === 1) {
/* For MultiLineString and LineString a value in the level1LengthBuffer exists */
for (let j = 0; j < numGeometries; j++) {
previousOffset = level1BufferOffsets[level1OffsetBufferCounter++] =
previousOffset + level1LengthBuffer[level1LengthCounter++];
}
}
else {
/* For MultiPoint and Point no value in level1LengthBuffer exists */
for (let j = 0; j < numGeometries; j++) {
level1BufferOffsets[level1OffsetBufferCounter++] = ++previousOffset;
}
}
}
return level1BufferOffsets;
}
function decodeLevel2LengthStream(geometryTypes, rootOffsetBuffer, level1OffsetBuffer, level2LengthBuffer) {
const level2BufferOffsets = new Uint32Array(level1OffsetBuffer[level1OffsetBuffer.length - 1] + 1);
let previousOffset = 0;
level2BufferOffsets[0] = previousOffset;
let level1OffsetBufferCounter = 1;
let level2OffsetBufferCounter = 1;
let level2LengthBufferCounter = 0;
for (let i = 0; i < geometryTypes.length; i++) {
const geometryType = geometryTypes[i];
const numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];
if (geometryType !== 0 && geometryType !== 3) {
/* For MultiPolygon, MultiLineString, Polygon and LineString a value in level2LengthBuffer
* exists */
for (let j = 0; j < numGeometries; j++) {
const numParts = level1OffsetBuffer[level1OffsetBufferCounter] - level1OffsetBuffer[level1OffsetBufferCounter - 1];
level1OffsetBufferCounter++;
for (let k = 0; k < numParts; k++) {
previousOffset = level2BufferOffsets[level2OffsetBufferCounter++] =
previousOffset + level2LengthBuffer[level2LengthBufferCounter++];
}
}
}
else {
/* For MultiPoint and Point no value in level2LengthBuffer exists */
for (let j = 0; j < numGeometries; j++) {
level2BufferOffsets[level2OffsetBufferCounter++] = ++previousOffset;
level1OffsetBufferCounter++;
}
}
}
return level2BufferOffsets;
}
//# sourceMappingURL=geometryDecoder.js.map