123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- import {PointCloudTreeNode} from "./PointCloudTree.js";
- import {PointAttributes, PointAttribute, PointAttributeTypes} from "./loader/PointAttributes.js";
- import * as THREE from "../libs/three.js/build/three.module.js";
- class U {
- static toVector3(v, offset) {
- return new THREE.Vector3().fromArray(v, offset || 0);
- }
- static toBox3(b) {
- return new THREE.Box3(U.toVector3(b), U.toVector3(b, 3));
- };
- static findDim(schema, name) {
- var dim = schema.find((dim) => dim.name == name);
- if (!dim) throw new Error('Failed to find ' + name + ' in schema');
- return dim;
- }
- static sphereFrom(b) {
- return b.getBoundingSphere(new THREE.Sphere());
- }
- };
- export class PointCloudEptGeometry {
- constructor(url, info) {
- let version = info.version;
- let schema = info.schema;
- let bounds = info.bounds;
- let boundsConforming = info.boundsConforming;
- let xyz = [
- U.findDim(schema, 'X'),
- U.findDim(schema, 'Y'),
- U.findDim(schema, 'Z')
- ];
- let scale = xyz.map((d) => d.scale || 1);
- let offset = xyz.map((d) => d.offset || 0);
- this.eptScale = U.toVector3(scale);
- this.eptOffset = U.toVector3(offset);
- this.url = url;
- this.info = info;
- this.type = 'ept';
- this.schema = schema;
- this.span = info.span || info.ticks;
- this.boundingBox = U.toBox3(bounds);
- this.tightBoundingBox = U.toBox3(boundsConforming);
- this.offset = U.toVector3([0, 0, 0]);
- this.boundingSphere = U.sphereFrom(this.boundingBox);
- this.tightBoundingSphere = U.sphereFrom(this.tightBoundingBox);
- this.version = new Potree.Version('1.7');
- this.projection = null;
- this.fallbackProjection = null;
- if (info.srs && info.srs.horizontal) {
- this.projection = info.srs.authority + ':' + info.srs.horizontal;
- }
- if (info.srs.wkt) {
- if (!this.projection) this.projection = info.srs.wkt;
- else this.fallbackProjection = info.srs.wkt;
- }
- {
- // TODO [mschuetz]: named projections that proj4 can't handle seem to cause problems.
- // remove them for now
- try{
- proj4(this.projection);
- }catch(e){
- this.projection = null;
- }
-
- }
-
- {
- const attributes = new PointAttributes();
- attributes.add(PointAttribute.POSITION_CARTESIAN);
- attributes.add(new PointAttribute("rgba", PointAttributeTypes.DATA_TYPE_UINT8, 4));
- attributes.add(new PointAttribute("intensity", PointAttributeTypes.DATA_TYPE_UINT16, 1));
- attributes.add(new PointAttribute("classification", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("gps-time", PointAttributeTypes.DATA_TYPE_DOUBLE, 1));
- attributes.add(new PointAttribute("returnNumber", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("number of returns", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("return number", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("source id", PointAttributeTypes.DATA_TYPE_UINT16, 1));
- this.pointAttributes = attributes;
- }
- this.spacing =
- (this.boundingBox.max.x - this.boundingBox.min.x) / this.span;
- let hierarchyType = info.hierarchyType || 'json';
- const dataType = info.dataType;
- if (dataType == 'laszip') {
- this.loader = new Potree.EptLaszipLoader();
- }
- else if (dataType == 'binary') {
- this.loader = new Potree.EptBinaryLoader();
- }
- else if (dataType == 'zstandard') {
- this.loader = new Potree.EptZstandardLoader();
- }
- else {
- throw new Error('Could not read data type: ' + dataType);
- }
- }
- };
- export class EptKey {
- constructor(ept, b, d, x, y, z) {
- this.ept = ept;
- this.b = b;
- this.d = d;
- this.x = x || 0;
- this.y = y || 0;
- this.z = z || 0;
- }
- name() {
- return this.d + '-' + this.x + '-' + this.y + '-' + this.z;
- }
- step(a, b, c) {
- let min = this.b.min.clone();
- let max = this.b.max.clone();
- let dst = new THREE.Vector3().subVectors(max, min);
- if (a) min.x += dst.x / 2;
- else max.x -= dst.x / 2;
- if (b) min.y += dst.y / 2;
- else max.y -= dst.y / 2;
- if (c) min.z += dst.z / 2;
- else max.z -= dst.z / 2;
- return new Potree.EptKey(
- this.ept,
- new THREE.Box3(min, max),
- this.d + 1,
- this.x * 2 + a,
- this.y * 2 + b,
- this.z * 2 + c);
- }
- children() {
- var result = [];
- for (var a = 0; a < 2; ++a) {
- for (var b = 0; b < 2; ++b) {
- for (var c = 0; c < 2; ++c) {
- var add = this.step(a, b, c).name();
- if (!result.includes(add)) result = result.concat(add);
- }
- }
- }
- return result;
- }
- }
- export class PointCloudEptGeometryNode extends PointCloudTreeNode {
- constructor(ept, b, d, x, y, z) {
- super();
- this.ept = ept;
- this.key = new Potree.EptKey(
- this.ept,
- b || this.ept.boundingBox,
- d || 0,
- x,
- y,
- z);
- this.id = PointCloudEptGeometryNode.IDCount++;
- this.geometry = null;
- this.boundingBox = this.key.b;
- this.tightBoundingBox = this.boundingBox;
- this.spacing = this.ept.spacing / Math.pow(2, this.key.d);
- this.boundingSphere = U.sphereFrom(this.boundingBox);
- // These are set during hierarchy loading.
- this.hasChildren = false;
- this.children = { };
- this.numPoints = -1;
- this.level = this.key.d;
- this.loaded = false;
- this.loading = false;
- this.oneTimeDisposeHandlers = [];
- let k = this.key;
- this.name = this.toPotreeName(k.d, k.x, k.y, k.z);
- this.index = parseInt(this.name.charAt(this.name.length - 1));
- }
- isGeometryNode() { return true; }
- getLevel() { return this.level; }
- isTreeNode() { return false; }
- isLoaded() { return this.loaded; }
- getBoundingSphere() { return this.boundingSphere; }
- getBoundingBox() { return this.boundingBox; }
- url() { return this.ept.url + 'ept-data/' + this.filename(); }
- getNumPoints() { return this.numPoints; }
- filename() { return this.key.name(); }
- getChildren() {
- let children = [];
- for (let i = 0; i < 8; i++) {
- if (this.children[i]) {
- children.push(this.children[i]);
- }
- }
- return children;
- }
- addChild(child) {
- this.children[child.index] = child;
- child.parent = this;
- }
- load() {
- if (this.loaded || this.loading) return;
- if (Potree.numNodesLoading >= Potree.maxNodesLoading) return;
- this.loading = true;
- ++Potree.numNodesLoading;
- if (this.numPoints == -1) this.loadHierarchy();
- this.loadPoints();
- }
- loadPoints(){
- this.ept.loader.load(this);
- }
- async loadHierarchy() {
- let nodes = { };
- nodes[this.filename()] = this;
- this.hasChildren = false;
- let eptHierarchyFile =
- `${this.ept.url}ept-hierarchy/${this.filename()}.json`;
- let response = await fetch(eptHierarchyFile);
- let hier = await response.json();
- // Since we want to traverse top-down, and 10 comes
- // lexicographically before 9 (for example), do a deep sort.
- var keys = Object.keys(hier).sort((a, b) => {
- let [da, xa, ya, za] = a.split('-').map((n) => parseInt(n, 10));
- let [db, xb, yb, zb] = b.split('-').map((n) => parseInt(n, 10));
- if (da < db) return -1; if (da > db) return 1;
- if (xa < xb) return -1; if (xa > xb) return 1;
- if (ya < yb) return -1; if (ya > yb) return 1;
- if (za < zb) return -1; if (za > zb) return 1;
- return 0;
- });
- keys.forEach((v) => {
- let [d, x, y, z] = v.split('-').map((n) => parseInt(n, 10));
- let a = x & 1, b = y & 1, c = z & 1;
- let parentName =
- (d - 1) + '-' + (x >> 1) + '-' + (y >> 1) + '-' + (z >> 1);
- let parentNode = nodes[parentName];
- if (!parentNode) return;
- parentNode.hasChildren = true;
- let key = parentNode.key.step(a, b, c);
- let node = new Potree.PointCloudEptGeometryNode(
- this.ept,
- key.b,
- key.d,
- key.x,
- key.y,
- key.z);
- node.level = d;
- node.numPoints = hier[v];
- parentNode.addChild(node);
- nodes[key.name()] = node;
- });
- }
- doneLoading(bufferGeometry, tightBoundingBox, np, mean) {
- bufferGeometry.boundingBox = this.boundingBox;
- this.geometry = bufferGeometry;
- this.tightBoundingBox = tightBoundingBox;
- this.numPoints = np;
- this.mean = mean;
- this.loaded = true;
- this.loading = false;
- --Potree.numNodesLoading;
- }
- toPotreeName(d, x, y, z) {
- var name = 'r';
- for (var i = 0; i < d; ++i) {
- var shift = d - i - 1;
- var mask = 1 << shift;
- var step = 0;
- if (x & mask) step += 4;
- if (y & mask) step += 2;
- if (z & mask) step += 1;
- name += step;
- }
- return name;
- }
- dispose() {
- if (this.geometry && this.parent != null) {
- this.geometry.dispose();
- this.geometry = null;
- this.loaded = false;
- // this.dispatchEvent( { type: 'dispose' } );
- for (let i = 0; i < this.oneTimeDisposeHandlers.length; i++) {
- let handler = this.oneTimeDisposeHandlers[i];
- handler();
- }
- this.oneTimeDisposeHandlers = [];
- }
- }
- }
- PointCloudEptGeometryNode.IDCount = 0;
|