Skip to content

Commit

Permalink
refacctor(EntiwnePointCloud): Multisoure
Browse files Browse the repository at this point in the history
  • Loading branch information
ftoromanoff committed Sep 6, 2024
1 parent 342b973 commit 649be9f
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 100 deletions.
10 changes: 8 additions & 2 deletions examples/entwine_3d_loader.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,17 @@

function onLayerReady() {
var lookAt = new itowns.THREE.Vector3();
eptLayer.root.bbox.getCenter(lookAt);

const bboxes = eptLayer.root.map(root => root.bbox);
let bbox = bboxes[0];
for (let i = 1; i < bboxes.length; i++) {
bbox = bbox.union(bboxes[i]);
}
bbox.getCenter(lookAt);
var coordLookAt = new itowns.Coordinates(view.referenceCrs, lookAt);

var size = new itowns.THREE.Vector3();
eptLayer.root.bbox.getSize(size);
bbox.getSize(size);

view.controls.lookAtCoordinate({
coord: coordLookAt,
Expand Down
14 changes: 10 additions & 4 deletions examples/entwine_simple_loader.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,19 @@
function onLayerReady() {
var lookAt = new itowns.THREE.Vector3();
var size = new itowns.THREE.Vector3();
eptLayer.root.bbox.getSize(size);
eptLayer.root.bbox.getCenter(lookAt);

const bboxes = eptLayer.root.map(root => root.bbox);
let bbox = bboxes[0];
for (let i = 1; i < bboxes.length; i++) {
bbox = bbox.union(bboxes[i]);
}
bbox.getSize(size);
bbox.getCenter(lookAt);

view.camera3D.far = 2.0 * size.length();

controls.groundLevel = eptLayer.root.bbox.min.z;
var position = eptLayer.root.bbox.max.clone().add(
controls.groundLevel = bbox.min.z;
var position = bbox.max.clone().add(
size.multiply({ x: 0, y: 0, z: size.x / size.z })
);

Expand Down
10 changes: 6 additions & 4 deletions src/Core/EntwinePointTileNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,25 @@ class EntwinePointTileNode extends PointCloudNode {
* [Entwine
* documentation](https://entwine.io/entwine-point-tile.html#ept-data)
* @param {EntwinePointTileLayer} layer - The layer the node is attached to.
* @param {EntwinePointTileSource} source - The data source for the node.
* @param {number} [numPoints=0] - The number of points in this node. If
* `-1`, it means that the octree hierarchy associated to this node needs to
* be loaded.
*/
constructor(depth, x, y, z, layer, numPoints = 0) {
constructor(depth, x, y, z, layer, source, numPoints = 0) {
super(numPoints, layer);

this.isEntwinePointTileNode = true;

this.source = source;
this.depth = depth;
this.x = x;
this.y = y;
this.z = z;

this.id = buildId(depth, x, y, z);

this.url = `${this.layer.source.url}/ept-data/${this.id}.${this.layer.source.extension}`;
this.url = `${this.source.url}/ept-data/${this.id}.${this.source.extension}`;
}

createChildAABB(childNode) {
Expand Down Expand Up @@ -116,7 +118,7 @@ class EntwinePointTileNode extends PointCloudNode {
}

loadOctree() {
return Fetcher.json(`${this.layer.source.url}/ept-hierarchy/${this.id}.json`, this.layer.source.networkOptions)
return Fetcher.json(`${this.source.url}/ept-hierarchy/${this.id}.json`, this.source.networkOptions)
.then((hierarchy) => {
this.numPoints = hierarchy[this.id];

Expand Down Expand Up @@ -147,7 +149,7 @@ class EntwinePointTileNode extends PointCloudNode {
const numPoints = hierarchy[id];

if (typeof numPoints == 'number') {
const child = new EntwinePointTileNode(depth, x, y, z, this.layer, numPoints);
const child = new EntwinePointTileNode(depth, x, y, z, this.layer, this.source, numPoints);
this.add(child);
stack.push(child);
}
Expand Down
193 changes: 103 additions & 90 deletions src/Layer/EntwinePointTileLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,96 +47,109 @@ class EntwinePointTileLayer extends PointCloudLayer {
this.scale = new THREE.Vector3(1, 1, 1);

const resolve = this.addInitializationStep();
this.whenReady = this.source.whenReady.then(() => {
if (this.crs !== config.crs) { console.warn('layer.crs is different from View.crs'); }
this.root = new EntwinePointTileNode(0, 0, 0, 0, this, -1);

let forward = (x => x);
if (this.source.crs !== this.crs) {
try {
forward = proj4(this.source.crs, this.crs).forward;
} catch (err) {
throw new Error(`${err} is not defined in proj4`);
}
}

// for BBOX
const boundsConforming = [
...forward(this.source.boundsConforming.slice(0, 3)),
...forward(this.source.boundsConforming.slice(3, 6)),
];
this.clamp = {
zmin: boundsConforming[2],
zmax: boundsConforming[5],
};

this.minElevationRange = this.source.boundsConforming[2];
this.maxElevationRange = this.source.boundsConforming[5];

const bounds = [
...forward(this.source.bounds.slice(0, 3)),
...forward(this.source.bounds.slice(3, 6)),
];

this.root.bbox.setFromArray(bounds);

// Get the transformation between the data coordinate syteme and the view's.
const centerZ0 = this.source.boundsConforming
.slice(0, 2)
.map((val, i) => Math.floor((val + this.source.boundsConforming[i + 3]) * 0.5));
centerZ0.push(0);

const geometry = new THREE.BufferGeometry();
const points = new THREE.Points(geometry);

const matrixWorld = new THREE.Matrix4();
const matrixWorldInverse = new THREE.Matrix4();

let origin = new Coordinates(this.crs);
if (this.crs === 'EPSG:4978') {
const axisZ = new THREE.Vector3(0, 0, 1);
const alignYtoEast = new THREE.Quaternion();
const center = new Coordinates(this.source.crs, centerZ0);
origin = center.as('EPSG:4978');
const center4326 = origin.as('EPSG:4326');

// align Z axe to geodesic normal.
points.quaternion.setFromUnitVectors(axisZ, origin.geodesicNormal);
// align Y axe to East
alignYtoEast.setFromAxisAngle(axisZ, THREE.MathUtils.degToRad(90 + center4326.longitude));
points.quaternion.multiply(alignYtoEast);
}
points.updateMatrixWorld();

matrixWorld.copy(points.matrixWorld);
matrixWorldInverse.copy(matrixWorld).invert();

// proj in repere local (apply rotation) to get obb from bbox
const boundsLocal = [];
for (let i = 0; i < bounds.length; i += 3) {
const coord = new THREE.Vector3(...bounds.slice(i, i + 3)).sub(origin.toVector3());
const coordlocal = coord.applyMatrix4(matrixWorldInverse);
boundsLocal.push(...coordlocal);
}

const positionsArray = new Float32Array(boundsLocal);
const positionBuffer = new THREE.BufferAttribute(positionsArray, 3);
geometry.setAttribute('position', positionBuffer);

geometry.computeBoundingBox();

this.root.obb.fromBox3(geometry.boundingBox);
this.root.obb.applyMatrix4(matrixWorld);
this.root.obb.position = origin.toVector3();

// NOTE: this spacing is kinda arbitrary here, we take the width and
// length (height can be ignored), and we divide by the specified
// span in ept.json. This needs improvements.
this.spacing = (Math.abs(this.source.bounds[3] - this.source.bounds[0])
+ Math.abs(this.source.bounds[4] - this.source.bounds[1])) / (2 * this.source.span);

return this.root.loadOctree().then(resolve);
});

const promises = [];
this.root = [];
this.whenReady = this.source.whenReady
.then((sources) => {
if (sources.isSource) { sources = [sources]; }
sources.forEach((source) => {
promises.push(source.whenReady
.then((source) => {
if (this.crs !== config.crs) { console.warn('layer.crs is different from View.crs'); }
const root = new EntwinePointTileNode(0, 0, 0, 0, this, source, -1);

let forward = (x => x);
if (source.crs !== this.crs) {
try {
forward = proj4(source.crs, this.crs).forward;
} catch (err) {
throw new Error(`${err} is not defined in proj4`);
}
}

// for BBOX
const boundsConforming = [
...forward(source.boundsConforming.slice(0, 3)),
...forward(source.boundsConforming.slice(3, 6)),
];
this.clamp = {
zmin: boundsConforming[2],
zmax: boundsConforming[5],
};

this.minElevationRange = source.boundsConforming[2];
this.maxElevationRange = source.boundsConforming[5];

const bounds = [
...forward(source.bounds.slice(0, 3)),
...forward(source.bounds.slice(3, 6)),
];

root.bbox.setFromArray(bounds);

// Get the transformation between the data coordinate syteme and the view's.
const centerZ0 = source.boundsConforming
.slice(0, 2)
.map((val, i) => Math.floor((val + source.boundsConforming[i + 3]) * 0.5));
centerZ0.push(0);

const geometry = new THREE.BufferGeometry();
const points = new THREE.Points(geometry);

const matrixWorld = new THREE.Matrix4();
const matrixWorldInverse = new THREE.Matrix4();

let origin = new Coordinates(this.crs);
if (this.crs === 'EPSG:4978') {
const axisZ = new THREE.Vector3(0, 0, 1);
const alignYtoEast = new THREE.Quaternion();
const center = new Coordinates(source.crs, centerZ0);
origin = center.as('EPSG:4978');
const center4326 = origin.as('EPSG:4326');

// align Z axe to geodesic normal.
points.quaternion.setFromUnitVectors(axisZ, origin.geodesicNormal);
// align Y axe to East
alignYtoEast.setFromAxisAngle(axisZ, THREE.MathUtils.degToRad(90 + center4326.longitude));
points.quaternion.multiply(alignYtoEast);
}
points.updateMatrixWorld();

matrixWorld.copy(points.matrixWorld);
matrixWorldInverse.copy(matrixWorld).invert();

// proj in repere local (apply rotation) to get obb from bbox
const boundsLocal = [];
for (let i = 0; i < bounds.length; i += 3) {
const coord = new THREE.Vector3(...bounds.slice(i, i + 3)).sub(origin.toVector3());
const coordlocal = coord.applyMatrix4(matrixWorldInverse);
boundsLocal.push(...coordlocal);
}

const positionsArray = new Float32Array(boundsLocal);
const positionBuffer = new THREE.BufferAttribute(positionsArray, 3);
geometry.setAttribute('position', positionBuffer);

geometry.computeBoundingBox();

root.obb.fromBox3(geometry.boundingBox);
root.obb.applyMatrix4(matrixWorld);
root.obb.position = origin.toVector3();

this.root.push(root);

// NOTE: this spacing is kinda arbitrary here, we take the width and
// length (height can be ignored), and we divide by the specified
// span in ept.json. This needs improvements.
this.spacing = (Math.abs(source.bounds[3] - source.bounds[0])
+ Math.abs(source.bounds[4] - source.bounds[1])) / (2 * source.span);

return root.loadOctree().then(resolve);
}));
});
this.whenReady = Promise.all(promises);
});
}
}

Expand Down

0 comments on commit 649be9f

Please sign in to comment.