Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: Enable both lazperf and laz-rs-wasm
Browse files Browse the repository at this point in the history
ibgreen committed Dec 2, 2024
1 parent eb386e3 commit 60cf386
Showing 22 changed files with 21,427 additions and 25 deletions.
9 changes: 8 additions & 1 deletion modules/las/src/index.ts
Original file line number Diff line number Diff line change
@@ -7,5 +7,12 @@
export {LASFormat} from './las-format';

export type {LASLoaderOptions} from './las-loader';
export {LASWorkerLoader, LASLoader} from './las-loader';
export {LASWorkerLoader} from './las-loader';

// Export the laz-perf based loader as default LASLoader until we have done more testing
export {LAZPerfLoader as LASLoader} from './lazperf-loader';
export {LASArrowLoader} from './las-arrow-loader';

// Implementation specific loaders, for bench marking and testing
export {LAZPerfLoader} from './lazperf-loader';
export {LAZRsLoader} from './laz-rs-loader';
10 changes: 6 additions & 4 deletions modules/las/src/las-arrow-loader.ts
Original file line number Diff line number Diff line change
@@ -4,21 +4,23 @@

import type {LoaderWithParser} from '@loaders.gl/loader-utils';
import type {ArrowTable} from '@loaders.gl/schema';

import {LASLoaderOptions, LASLoader} from './las-loader';
import {convertMeshToTable} from '@loaders.gl/schema-utils';

import type {LASLoaderOptions} from './las-loader';
import {LAZPerfLoader} from './lazperf-loader';

/**
* Worker loader for LAS - Point Cloud Data
*/
export const LASArrowLoader = {
...LASLoader,
...LAZPerfLoader,
dataType: null as unknown as ArrowTable,
batchType: null as never,
worker: false,
parse: async (arrayBuffer: ArrayBuffer) => {
const mesh = await LASLoader.parse(arrayBuffer);
const mesh = await LAZPerfLoader.parse(arrayBuffer);
const arrowTable = convertMeshToTable(mesh, 'arrow-table');
return arrowTable;
}
// @ts-expect-error parseSync
} as const satisfies LoaderWithParser<ArrowTable, never, LASLoaderOptions>;
15 changes: 1 addition & 14 deletions modules/las/src/las-loader.ts
Original file line number Diff line number Diff line change
@@ -3,11 +3,9 @@
// Copyright (c) vis.gl contributors

// LASER (LAS) FILE FORMAT
import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';
import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils';
import type {LASMesh} from './lib/las-types';
import {LASFormat} from './las-format';
import {parseLAS} from './lib/parse-las';
import initLazRsWasm from './lib/libs/laz_rs_wasm';

// __VERSION__ is injected by babel-plugin-version-inline
// @ts-ignore TS2304: Cannot find name '__VERSION__'.
@@ -45,14 +43,3 @@ export const LASWorkerLoader = {
}
}
} as const satisfies Loader<LASMesh, never, LASLoaderOptions>;

/**
* Loader for the LAS (LASer) point cloud format
*/
export const LASLoader = {
...LASWorkerLoader,
parse: async (arrayBuffer: ArrayBuffer, options?: LASLoaderOptions) => {
await initLazRsWasm();
return parseLAS(arrayBuffer, {...options});
}
} as const satisfies LoaderWithParser<LASMesh, never, LASLoaderOptions>;
22 changes: 22 additions & 0 deletions modules/las/src/laz-rs-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

// LASER (LAS) FILE FORMAT
import type {LoaderWithParser} from '@loaders.gl/loader-utils';
import type {LASLoaderOptions} from './las-loader';
import {LASWorkerLoader} from './las-loader';
import type {LASMesh} from './lib/las-types';
import {parseLAS} from './lib/laz-rs-wasm/parse-las';
import initLazRsWasm from './libs/laz-rs-wasm/laz_rs_wasm';

/**
* Loader for the LAS (LASer) point cloud format
*/
export const LAZRsLoader = {
...LASWorkerLoader,
parse: async (arrayBuffer: ArrayBuffer, options?: LASLoaderOptions) => {
await initLazRsWasm();
return parseLAS(arrayBuffer, {...options});
}
} as const satisfies LoaderWithParser<LASMesh, never, LASLoaderOptions>;
22 changes: 22 additions & 0 deletions modules/las/src/lazperf-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

// LASER (LAS) FILE FORMAT
import type {LoaderWithParser} from '@loaders.gl/loader-utils';
import type {LASLoaderOptions} from './las-loader';
import {LASWorkerLoader} from './las-loader';
import type {LASMesh} from './lib/las-types';
import {parseLAS} from './lib/laz-perf/parse-las';

/**
* Loader for the LAS (LASer) point cloud format
* @note Does not support LAS v1.4
*/
export const LAZPerfLoader = {
...LASWorkerLoader,
parse: async (arrayBuffer: ArrayBuffer, options?: LASLoaderOptions) =>
parseLAS(arrayBuffer, options),
parseSync: (arrayBuffer: ArrayBuffer, options?: LASLoaderOptions) =>
parseLAS(arrayBuffer, options)
} as const satisfies LoaderWithParser<LASMesh, never, LASLoaderOptions>;
481 changes: 481 additions & 0 deletions modules/las/src/lib/laz-perf/laslaz-decoder.ts

Large diffs are not rendered by default.

236 changes: 236 additions & 0 deletions modules/las/src/lib/laz-perf/parse-las.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

// ported and es6-ified from https://github.com/verma/plasio/
// import type {ArrowTable, ColumnarTable} from '@loaders.gl/schema';
import type {LASLoaderOptions} from '../../las-loader';
import type {LASMesh, LASHeader} from '../las-types';
import {getMeshBoundingBox /* , convertMeshToTable */} from '@loaders.gl/schema-utils';
import {getLASSchema} from '../get-las-schema';
import {LASFile} from './laslaz-decoder';

type LASChunk = {
count: number;
buffer: ArrayBuffer;
hasMoreData: boolean;
versionAsString?: string;
isCompressed?: boolean;
};

/**
* Parsing of .las file
* @param arrayBuffer
* @param options
* @returns LASHeader
*/
export function parseLAS(arrayBuffer: ArrayBuffer, options?: LASLoaderOptions): LASMesh {
return parseLASMesh(arrayBuffer, options);
// This code breaks pointcloud example on the website
// const mesh = parseLASMesh(arrayBuffer, options);
// return convertMeshToTable(mesh, options?.las?.shape || 'mesh') as LASMesh | ArrowTable | ColumnarTable;
}

/**
* Parsing of .las file
* @param arrayBuffer
* @param options
* @returns LASHeader
*/
function parseLASMesh(arrayBuffer: ArrayBuffer, options: LASLoaderOptions = {}): LASMesh {
let pointIndex: number = 0;

let positions: Float32Array | Float64Array;
let colors: Uint8Array | null;
let intensities: Uint16Array;
let classifications: Uint8Array;
let originalHeader: any;

const lasMesh: LASMesh = {
loader: 'las',
loaderData: {} as LASHeader,
// shape: 'mesh',
schema: {fields: [], metadata: {}},
header: {
vertexCount: 0,
boundingBox: [
[0, 0, 0],
[0, 0, 0]
]
},
attributes: {},
topology: 'point-list',
mode: 0 // GL.POINTS
};

/* eslint-disable max-statements */
// @ts-ignore Possibly undefined
parseLASChunked(arrayBuffer, options.las?.skip, (decoder: any = {}, lasHeader: LASHeader) => {
if (!originalHeader) {
originalHeader = lasHeader;
const total = lasHeader.totalToRead;

const PositionsType = options.las?.fp64 ? Float64Array : Float32Array;
positions = new PositionsType(total * 3);
// laslaz-decoder.js `pointFormatReaders`
colors = lasHeader.pointsFormatId >= 2 ? new Uint8Array(total * 4) : null;
intensities = new Uint16Array(total);
classifications = new Uint8Array(total);

lasMesh.loaderData = lasHeader;
lasMesh.attributes = {
POSITION: {value: positions, size: 3},
// non-gltf attributes, use non-capitalized names for now
intensity: {value: intensities, size: 1},
classification: {value: classifications, size: 1}
};

if (colors) {
lasMesh.attributes.COLOR_0 = {value: colors, size: 4};
}
}

const batchSize = decoder.pointsCount;
const {
scale: [scaleX, scaleY, scaleZ],
offset: [offsetX, offsetY, offsetZ]
} = lasHeader;

const twoByteColor = detectTwoByteColors(decoder, batchSize, options.las?.colorDepth);

for (let i = 0; i < batchSize; i++) {
const {position, color, intensity, classification} = decoder.getPoint(i);

positions[pointIndex * 3] = position[0] * scaleX + offsetX;
positions[pointIndex * 3 + 1] = position[1] * scaleY + offsetY;
positions[pointIndex * 3 + 2] = position[2] * scaleZ + offsetZ;

if (color && colors) {
if (twoByteColor) {
colors[pointIndex * 4] = color[0] / 256;
colors[pointIndex * 4 + 1] = color[1] / 256;
colors[pointIndex * 4 + 2] = color[2] / 256;
} else {
colors[pointIndex * 4] = color[0];
colors[pointIndex * 4 + 1] = color[1];
colors[pointIndex * 4 + 2] = color[2];
}
colors[pointIndex * 4 + 3] = 255;
}

intensities[pointIndex] = intensity;
classifications[pointIndex] = classification;

pointIndex++;
}

const meshBatch = {
...lasMesh,
header: {
vertexCount: lasHeader.totalRead
},
progress: lasHeader.totalRead / lasHeader.totalToRead
};

options?.onProgress?.(meshBatch);
});
/* eslint-enable max-statements */

lasMesh.header = {
vertexCount: originalHeader.totalToRead,
boundingBox: getMeshBoundingBox(lasMesh?.attributes || {})
};

if (lasMesh) {
lasMesh.schema = getLASSchema(lasMesh.loaderData, lasMesh.attributes);
}
return lasMesh;
}

/**
* parse laz data
* @param rawData
* @param skip
* @param onParseData
* @return parsed point cloud
*/
/* eslint-enable max-statements */
export function parseLASChunked(rawData: ArrayBuffer, skip: number, onParseData: any = {}): void {
const dataHandler = new LASFile(rawData);

try {
// open data
dataHandler.open();

const header = dataHandler.getHeader();
// start loading
const Unpacker = dataHandler.getUnpacker();

const totalToRead = Math.ceil(header.pointsCount / Math.max(1, skip));
header.totalToRead = totalToRead;
let totalRead = 0;

/* eslint-disable no-constant-condition */
while (true) {
const chunk: LASChunk = dataHandler.readData(1000 * 100, 0, skip);

totalRead += chunk.count;

header.totalRead = totalRead;
header.versionAsString = chunk.versionAsString;
header.isCompressed = chunk.isCompressed;

const unpacker = new Unpacker(chunk.buffer, chunk.count, header);

// surface unpacker and progress via call back
// use unpacker.pointsCount and unpacker.getPoint(i) to handle data in app
onParseData(unpacker, header);

if (!chunk.hasMoreData || totalRead >= totalToRead) {
break;
}
}
} catch (e) {
throw e;
} finally {
dataHandler.close();
}
}

/**
* @param decoder
* @param batchSize
* @param colorDepth
* @returns boolean
*/
function detectTwoByteColors(
decoder: any = {},
batchSize: number,
colorDepth?: number | string
): boolean {
let twoByteColor = false;
switch (colorDepth) {
case 8:
twoByteColor = false;
break;
case 16:
twoByteColor = true;
break;
case 'auto':
if (decoder.getPoint(0).color) {
for (let i = 0; i < batchSize; i++) {
const {color} = decoder.getPoint(i);
// eslint-disable-next-line max-depth
if (color[0] > 255 || color[1] > 255 || color[2] > 255) {
twoByteColor = true;
}
}
}
break;
default:
// eslint-disable-next-line
console.warn('las: illegal value for options.las.colorDepth');
break;
}
return twoByteColor;
}
Original file line number Diff line number Diff line change
@@ -8,8 +8,8 @@
MIT License
*/
// laslaz.js - treat as compiled code
import type {LASHeader} from './las-types';
import {WasmLasZipDecompressor} from './libs/laz_rs_wasm';
import type {LASHeader} from '../las-types';
import {WasmLasZipDecompressor} from '../../libs/laz-rs-wasm/laz_rs_wasm';

type LASPoint = {
position: [number, number, number];
@@ -186,7 +186,8 @@ function parseLASHeader(arraybuffer: ArrayBuffer): LASHeader {

const colorPointFormats = new Set([2, 3, 5, 7, 8, 10]);
o.hasColor = colorPointFormats.has(pointsFormatId);
return o as LASHeader;
// @ts-expect-error Caused when restoring lazperf
return o;
}

// LAS Loader
Original file line number Diff line number Diff line change
@@ -4,11 +4,11 @@

// ported and es6-ified from https://github.com/verma/plasio/
// import type {ArrowTable, ColumnarTable} from '@loaders.gl/schema';
import type {LASLoaderOptions} from '../las-loader';
import type {LASMesh, LASHeader} from './las-types';
import type {LASLoaderOptions} from '../../las-loader';
import type {LASMesh, LASHeader} from '../las-types';
import {getMeshBoundingBox /* , convertMeshToTable */} from '@loaders.gl/schema-utils';
import {getLASSchema} from '../get-las-schema';
import {LASFile} from './laslaz-decoder';
import {getLASSchema} from './get-las-schema';

type LASChunk = {
count: number;
20,616 changes: 20,616 additions & 0 deletions modules/las/src/libs/laz-perf/laz-perf.ts

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 4 additions & 0 deletions test/bench/node.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import '@loaders.gl/polyfills';
import {Bench} from '@probe.gl/bench';

4 changes: 4 additions & 0 deletions test/common/conformance.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

export function validateLoader(t, loader, name = '') {
t.ok(typeof loader.id === 'string', `Loader ${name} loader.id is not defined`);
t.ok(loader, `Loader ${name} defined`);
4 changes: 4 additions & 0 deletions test/render/test-cases/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import meshTestCases from './mesh';
import pointCloudTestCases from './point-cloud';

4 changes: 4 additions & 0 deletions test/render/test-cases/mesh.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {load} from '@loaders.gl/core';
import {PLYLoader} from '@loaders.gl/ply';
import {getModel, drawModelInViewport} from '../test-utils/get-model';
4 changes: 4 additions & 0 deletions test/render/test-cases/point-cloud.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {load, fetchFile, parse, encode} from '@loaders.gl/core';
import {DracoWriter, DracoLoader} from '@loaders.gl/draco';
import {LASLoader} from '@loaders.gl/las';
4 changes: 4 additions & 0 deletions test/render/test-utils/get-model.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {mat4} from '@math.gl/core';
import {Model, Geometry} from '@luma.gl/engine';

4 changes: 4 additions & 0 deletions test/render/test-utils/normalize-attributes.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

const DEFAULT_COLOR = {constant: true, size: 4, value: new Float32Array([0, 0, 0, 255])};
const DEFAULT_NORMAL = {constant: true, size: 3, value: new Float32Array([0, 0, 1])};
const DEFAULT_TEX_COORDS = {constant: true, size: 2, value: new Float32Array([0, 0])};

0 comments on commit 60cf386

Please sign in to comment.