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