UNPKG

@loaders.gl/potree

Version:

potree loaders for large point clouds.

409 lines (395 loc) 13.4 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // dist/index.js var dist_exports = {}; __export(dist_exports, { PotreeBinLoader: () => PotreeBinLoader, PotreeHierarchyChunkLoader: () => PotreeHierarchyChunkLoader, PotreeLoader: () => PotreeLoader, PotreeSource: () => PotreeSource }); module.exports = __toCommonJS(dist_exports); // dist/potree-loader.js var VERSION = true ? "4.4.2" : "latest"; var PotreeLoader = { dataType: null, batchType: null, name: "potree metadata", id: "potree", module: "potree", version: VERSION, extensions: ["js"], mimeTypes: ["application/json"], testText: (text) => text.indexOf("octreeDir") >= 0, parse: (data) => JSON.parse(new TextDecoder().decode(data)), parseTextSync: (text) => JSON.parse(text), options: { potree: {} } }; // dist/parsers/parse-potree-hierarchy-chunk.js function parsePotreeHierarchyChunk(arrayBuffer) { const tileHeaders = parseBinaryChunk(arrayBuffer); return buildHierarchy(tileHeaders); } function parseBinaryChunk(arrayBuffer, byteOffset = 0) { const dataView = new DataView(arrayBuffer); const stack = []; const topTileHeader = {}; byteOffset = decodeRow(dataView, byteOffset, topTileHeader); stack.push(topTileHeader); const tileHeaders = [topTileHeader]; while (stack.length > 0) { const snode = stack.shift(); let mask = 1; for (let i = 0; i < 8; i++) { if (snode && (snode.header.childMask & mask) !== 0) { const tileHeader = {}; byteOffset = decodeRow(dataView, byteOffset, tileHeader); tileHeader.name = snode.name + i; stack.push(tileHeader); tileHeaders.push(tileHeader); snode.header.childCount++; } mask = mask * 2; } if (byteOffset === dataView.byteLength) { break; } } return tileHeaders; } function decodeRow(dataView, byteOffset, tileHeader) { tileHeader.header = tileHeader.header || {}; tileHeader.header.childMask = dataView.getUint8(byteOffset); tileHeader.header.childCount = 0; tileHeader.pointCount = dataView.getUint32(byteOffset + 1, true); tileHeader.name = ""; byteOffset += 5; return byteOffset; } function buildHierarchy(flatNodes, options = {}) { const DEFAULT_OPTIONS = { spacing: 100 }; options = { ...DEFAULT_OPTIONS, ...options }; const topNode = flatNodes[0]; const nodes = {}; for (const node of flatNodes) { const { name } = node; const index = parseInt(name.charAt(name.length - 1), 10); const parentName = name.substring(0, name.length - 1); const parentNode = nodes[parentName]; const level = name.length - 1; node.level = level; node.hasChildren = Boolean(node.header.childCount); node.children = []; node.childrenByIndex = new Array(8).fill(null); node.spacing = ((options == null ? void 0 : options.spacing) || 0) / Math.pow(2, level); node.type = "pointcloud"; node.id = node.name; if (parentNode) { parentNode.children.push(node); parentNode.childrenByIndex[index] = node; } nodes[name] = node; } return topNode; } // dist/potree-hierarchy-chunk-loader.js var VERSION2 = true ? "4.4.2" : "latest"; var PotreeHierarchyChunkLoader = { dataType: null, batchType: null, name: "potree Hierarchy Chunk", id: "potree-hrc", module: "potree", version: VERSION2, extensions: ["hrc"], mimeTypes: ["application/octet-stream"], // binary potree files have no header bytes, no content test function possible // test: ['...'], parse: async (arrayBuffer, options) => parsePotreeHierarchyChunk(arrayBuffer), parseSync: (arrayBuffer, options) => parsePotreeHierarchyChunk(arrayBuffer), options: { potree: {} }, binary: true }; // dist/parsers/parse-potree-bin.js function parsePotreeBin(arrayBuffer, byteOffset, options, index) { return null; } // dist/potree-bin-loader.js var PotreeBinLoader = { dataType: null, batchType: null, name: "potree Binary Point Attributes", id: "potree", extensions: ["bin"], mimeTypes: ["application/octet-stream"], // Unfortunately binary potree files have no header bytes, no test possible // test: ['...'], parseSync, binary: true, options: {} // @ts-ignore }; function parseSync(arrayBuffer, options) { const index = {}; const byteOffset = 0; parsePotreeBin(arrayBuffer, byteOffset, options, index); return index; } // dist/lib/potree-node-source.js var import_core = require("@loaders.gl/core"); var import_loader_utils = require("@loaders.gl/loader-utils"); var import_las = require("@loaders.gl/las"); // dist/utils/parse-version.js function parseVersion(version) { const parts = version.split(".").map(Number); return { major: parts[0], minor: parts[1] }; } // dist/lib/potree-node-source.js var import_proj42 = require("@math.gl/proj4"); // dist/utils/projection-utils.js var import_proj4 = require("@math.gl/proj4"); var createProjection = (projectionData) => { if (!projectionData) { return null; } return new import_proj4.Proj4Projection({ from: projectionData, to: "WGS84" }); }; // dist/utils/bounding-box-utils.js var getCartographicOriginFromBoundingBox = (projection, boundingBox) => { if (!boundingBox) { return [0, 0, 0]; } const [minXOriginal, minYOriginal, minZ] = boundingBox[0]; const [maxXOriginal, maxYOriginal, maxZ] = boundingBox[1]; let minX = minXOriginal; let minY = minYOriginal; let maxX = maxXOriginal; let maxY = maxYOriginal; if (projection) { [minX, minY] = projection.project([minX, minY]); [maxX, maxY] = projection.project([maxX, maxY]); } return [minX + (maxX - minX) / 2, minY + (maxY - minY) / 2, minZ + (maxZ - minZ) / 2]; }; // dist/lib/potree-node-source.js var COORDINATE_SYSTEM; (function(COORDINATE_SYSTEM2) { COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["DEFAULT"] = -1] = "DEFAULT"; COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["LNGLAT"] = 1] = "LNGLAT"; COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["METER_OFFSETS"] = 2] = "METER_OFFSETS"; COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["LNGLAT_OFFSETS"] = 3] = "LNGLAT_OFFSETS"; COORDINATE_SYSTEM2[COORDINATE_SYSTEM2["CARTESIAN"] = 0] = "CARTESIAN"; })(COORDINATE_SYSTEM || (COORDINATE_SYSTEM = {})); var PotreeNodesSource = class extends import_loader_utils.DataSource { /** Dataset base URL */ baseUrl = ""; /** Meta information from `cloud.js` */ metadata = null; /** Root node */ root = null; /** Is data source ready to use after initial loading */ isReady = false; /** local CRS to WGS84 projection */ projection = null; /** The data set minimum bounding box */ boundingBox; initPromise = null; /** * @constructor * @param data - if string - data set path url or path to `cloud.js` metadata file * - if Blob - single file data * @param options - data source properties */ constructor(data, options) { super(data, options); this.makeBaseUrl(this.data); this.initPromise = this.init(); } /** Initial data source loading */ async init() { var _a; if (this.initPromise) { await this.initPromise; return; } this.metadata = await (0, import_core.load)(`${this.baseUrl}/cloud.js`, PotreeLoader); this.projection = createProjection((_a = this.metadata) == null ? void 0 : _a.projection); this.parseBoundingVolume(); await this.loadHierarchy(); this.isReady = true; } /** Is data set supported */ isSupported() { var _a, _b, _c; const { minor, major } = parseVersion(((_a = this.metadata) == null ? void 0 : _a.version) ?? ""); return this.isReady && major === 1 && minor <= 8 && typeof ((_b = this.metadata) == null ? void 0 : _b.pointAttributes) === "string" && ["LAS", "LAZ"].includes((_c = this.metadata) == null ? void 0 : _c.pointAttributes); } /** Get content files extension */ getContentExtension() { var _a; if (!this.isReady) { return null; } switch ((_a = this.metadata) == null ? void 0 : _a.pointAttributes) { case "LAS": return "las"; case "LAZ": return "laz"; default: return "bin"; } } /** * Load octree node content * @param nodeName name of a node, string of numbers in range 0..7 * @return node content geometry or null if the node doesn't exist */ async loadNodeContent(nodeName) { var _a, _b, _c; await this.initPromise; if (!this.isSupported()) { return null; } const isAvailable = await this.isNodeAvailable(nodeName); if (isAvailable) { const result = await (0, import_core.load)(`${this.baseUrl}/${(_a = this.metadata) == null ? void 0 : _a.octreeDir}/r/r${nodeName}.${this.getContentExtension()}`, import_las.LASLoader); if (result) { result.cartographicOrigin = getCartographicOriginFromBoundingBox(this.projection, (_b = result.header) == null ? void 0 : _b.boundingBox); const position = result.attributes.POSITION.value; for (let i = 0; i < (((_c = result.header) == null ? void 0 : _c.vertexCount) ?? 0); i++) { let vertex = position.slice(i * 3, i * 3 + 2); if (this.projection) { vertex = this.projection.project(Array.from(vertex)); } const offsets = [ vertex[0] - result.cartographicOrigin[0], vertex[1] - result.cartographicOrigin[1], position[i * 3 + 2] - result.cartographicOrigin[2] ]; position.set(offsets, i * 3); } result.attributes.positions = result.attributes.POSITION; result.attributes.colors = result.attributes.COLOR_0; result.attributes.normals = result.attributes.NORMAL; result.coordinateSystem = COORDINATE_SYSTEM.LNGLAT_OFFSETS; return result; } } return null; } /** * Check if a node exists in the octree * @param nodeName name of a node, string of numbers in range 0..7 * @returns true - the node does exist, false - the nodes doesn't exist */ async isNodeAvailable(nodeName) { var _a; if ((_a = this.metadata) == null ? void 0 : _a.hierarchy) { return this.metadata.hierarchy.findIndex((item) => item[0] === `r${nodeName}`) !== -1; } if (!this.root) { return false; } let currentParent = this.root; let name = ""; let result = true; for (const char of nodeName) { const newName = `${name}${char}`; const node = currentParent.children.find((child) => child.name === newName); if (node) { currentParent = node; name = newName; } else { result = false; break; } } return result; } /** * Load data source hierarchy into tree of available nodes */ async loadHierarchy() { var _a; this.root = await (0, import_core.load)(`${this.baseUrl}/${(_a = this.metadata) == null ? void 0 : _a.octreeDir}/r/r.hrc`, PotreeHierarchyChunkLoader); } /** * Deduce base url from the input url sring * @param data - data source input data */ makeBaseUrl(data) { this.baseUrl = typeof data === "string" ? (0, import_loader_utils.resolvePath)(data) : ""; if (this.baseUrl.endsWith("cloud.js")) { this.baseUrl = this.baseUrl.substring(0, -8); } if (this.baseUrl.endsWith("/")) { this.baseUrl = this.baseUrl.substring(0, -1); } } parseBoundingVolume() { var _a, _b; if (((_a = this.metadata) == null ? void 0 : _a.projection) && this.metadata.tightBoundingBox) { const projection = new import_proj42.Proj4Projection({ from: this.metadata.projection, to: "WGS84" }); const { lx, ly, ux, uy } = this.metadata.tightBoundingBox; const lCoord = [lx, ly]; const wgs84LCood = projection.project(lCoord); const uCoord = [ux, uy]; const wgs84UCood = projection.project(uCoord); this.boundingBox = { ...this.metadata.tightBoundingBox, lx: wgs84LCood[0], ly: wgs84LCood[1], ux: wgs84UCood[0], uy: wgs84UCood[1] }; } else { this.boundingBox = (_b = this.metadata) == null ? void 0 : _b.tightBoundingBox; } } }; // dist/potree-source.js var VERSION3 = "1.7"; var PotreeSource = { name: "Potree", id: "potree", module: "potree", version: VERSION3, extensions: ["bin", "las", "laz"], mimeTypes: ["application/octet-stream"], type: "potree", fromUrl: true, fromBlob: true, defaultOptions: { potree: {} }, testURL: (url) => url.endsWith(".js"), createDataSource: (url, options) => new PotreeNodesSource(url, options) // , PotreeNodesSource.defaultOptions) }; //# sourceMappingURL=index.cjs.map