Reference Source

src/parsers/parseLASIntoXKTModel.js

import {parse} from '@loaders.gl/core';
import {LASLoader} from '@loaders.gl/las';
import {math} from "../lib/math.js";


/**
 * @desc Parses LAS and LAZ point cloud data into an {@link XKTModel}.
 *
 * This parser handles both the LASER file format (LAS) and its compressed version (LAZ),
 * a public format for the interchange of 3-dimensional point cloud data data, developed
 * for LIDAR mapping purposes.
 *
 * ## Usage
 *
 * In the example below we'll create an {@link XKTModel}, then load an LAZ point cloud model into it.
 *
 * ````javascript
 * utils.loadArraybuffer("./models/laz/autzen.laz", async (data) => {
 *
 *     const xktModel = new XKTModel();
 *
 *     await parseLASIntoXKTModel({
 *          data,
 *          xktModel,
 *          log: (msg) => { console.log(msg); }
 *     }).then(()=>{
 *        xktModel.finalize();
 *     },
 *     (msg) => {
 *         console.error(msg);
 *     });
 * });
 * ````
 *
 * @param {Object} params Parsing params.
 * @param {ArrayBuffer} params.data LAS/LAZ file data.
 * @param {XKTModel} params.xktModel XKTModel to parse into.
 * @param {Boolean} [params.rotateX=true] Whether to rotate the model 90 degrees about the X axis to make the Y axis "up", if necessary.
 * @param {Object} [params.stats] Collects statistics.
 * @param {function} [params.log] Logging callback.
 */
async function parseLASIntoXKTModel({data, xktModel, rotateX = true, stats, log}) {

    if (!data) {
        throw "Argument expected: data";
    }

    if (!xktModel) {
        throw "Argument expected: xktModel";
    }

    if (log) {
        log("Converting LAZ/LAS");
    }

    let parsedData;
    try {
        parsedData = await parse(data, LASLoader);
    } catch (e) {
        if (log) {
            log("Error: " + e);
        }
        return;
    }

    const attributes = parsedData.attributes;
    const positionsValue = attributes.POSITION.value;
    const colorsValue = attributes.COLOR_0.value;

    if (rotateX) {
        if (log) {
            log("Rotating model about X-axis");
        }
        if (positionsValue) {
            for (let i = 0, len = positionsValue.length; i < len; i += 3) {
                const temp = positionsValue[i + 1];
                positionsValue[i + 1] = positionsValue[i + 2];
                positionsValue[i + 2] = temp;
            }
        }
    }

    xktModel.createGeometry({
        geometryId: "pointsGeometry",
        primitiveType: "points",
        positions: positionsValue,
        colorsCompressed: colorsValue
    });

    xktModel.createMesh({
        meshId: "pointsMesh",
        geometryId: "pointsGeometry"
    });

    const entityId = math.createUUID();

    xktModel.createEntity({
        entityId: entityId,
        meshIds: ["pointsMesh"]
    });

    const rootMetaObjectId = math.createUUID();

    xktModel.createMetaObject({
        metaObjectId: rootMetaObjectId,
        metaObjectType: "Model",
        metaObjectName: "Model"
    });

    xktModel.createMetaObject({
        metaObjectId: entityId,
        metaObjectType: "PointCloud",
        metaObjectName: "PointCloud (LAZ)",
        parentMetaObjectId: rootMetaObjectId
    });

    if (stats) {
        stats.sourceFormat = "LAS";
        stats.schemaVersion = "";
        stats.title = "";
        stats.author = "";
        stats.created = "";
        stats.numMetaObjects = 2;
        stats.numPropertySets = 0;
        stats.numObjects = 1;
        stats.numGeometries = 1;
        stats.numVertices = positionsValue.length / 3;
    }
}

export {parseLASIntoXKTModel};