This may happen if your browser or graphics card is not supported.
We recommend to use
Chrome
or
Firefox.
Please also visit webglreport.com and
check whether your system supports WebGL.
If you are already using one of the recommended browsers and WebGL is enabled,
consider filing an issue report at github,
including your operating system, graphics card, browser and browser version, as well as the
error message below.
Please do not report errors on unsupported browsers.
`);
let elErrorMessage = elFailPage.find('#potree_error_console');
elErrorMessage.html(error.stack);
$(this.renderArea).append(elFailPage);
}
throw error;
}
// ------------------------------------------------------------------------------------
// Viewer API
// ------------------------------------------------------------------------------------
setScene (scene) {
if (scene === this.scene) {
return;
}
let oldScene = this.scene;
this.scene = scene;
this.dispatchEvent({
type: 'scene_changed',
oldScene: oldScene,
scene: scene
});
{ // Annotations
$('.annotation').detach();
// for(let annotation of this.scene.annotations){
// this.renderArea.appendChild(annotation.domElement[0]);
// }
this.scene.annotations.traverse(annotation => {
this.renderArea.appendChild(annotation.domElement[0]);
});
if (!this.onAnnotationAdded) {
this.onAnnotationAdded = e => {
// console.log("annotation added: " + e.annotation.title);
e.annotation.traverse(node => {
$("#potree_annotation_container").append(node.domElement);
//this.renderArea.appendChild(node.domElement[0]);
node.scene = this.scene;
});
};
}
if (oldScene) {
oldScene.annotations.removeEventListener('annotation_added', this.onAnnotationAdded);
}
this.scene.annotations.addEventListener('annotation_added', this.onAnnotationAdded);
}
};
getControls (navigationMode) {
if (navigationMode === Potree.OrbitControls) {
return this.orbitControls;
} else if (navigationMode === Potree.FirstPersonControls) {
return this.fpControls;
} else if (navigationMode === Potree.EarthControls) {
return this.earthControls;
} else {
return null;
}
}
getMinNodeSize () {
return this.minNodeSize;
};
setMinNodeSize (value) {
if (this.minNodeSize !== value) {
this.minNodeSize = value;
this.dispatchEvent({'type': 'minnodesize_changed', 'viewer': this});
}
};
getBackground () {
return this.background;
};
setBackground(bg){
if (this.background === bg) {
return;
}
if(bg === "skybox"){
this.skybox = Potree.utils.loadSkybox(new URL(Potree.resourcePath + '/textures/skybox2/').href);
}
this.background = bg;
this.dispatchEvent({'type': 'background_changed', 'viewer': this});
}
setDescription (value) {
$('#potree_description')[0].innerHTML = value;
};
setNavigationMode (value) {
this.scene.view.navigationMode = value;
};
setShowBoundingBox (value) {
if (this.showBoundingBox !== value) {
this.showBoundingBox = value;
this.dispatchEvent({'type': 'show_boundingbox_changed', 'viewer': this});
}
};
getShowBoundingBox () {
return this.showBoundingBox;
};
setMoveSpeed (value) {
if (this.moveSpeed !== value) {
this.moveSpeed = value;
this.dispatchEvent({'type': 'move_speed_changed', 'viewer': this, 'speed': value});
}
};
getMoveSpeed () {
return this.moveSpeed;
};
setWeightClassification (w) {
for (let i = 0; i < this.scene.pointclouds.length; i++) {
this.scene.pointclouds[i].material.weightClassification = w;
this.dispatchEvent({'type': 'attribute_weights_changed' + i, 'viewer': this});
}
};
setFreeze (value) {
value = Boolean(value);
if (this.freeze !== value) {
this.freeze = value;
this.dispatchEvent({'type': 'freeze_changed', 'viewer': this});
}
};
getFreeze () {
return this.freeze;
};
getClipTask(){
return this.clipTask;
}
getClipMethod(){
return this.clipMethod;
}
setClipTask(value){
if(this.clipTask !== value){
this.clipTask = value;
this.dispatchEvent({
type: "cliptask_changed",
viewer: this});
}
}
setClipMethod(value){
if(this.clipMethod !== value){
this.clipMethod = value;
this.dispatchEvent({
type: "clipmethod_changed",
viewer: this});
}
}
setPointBudget (value) {
if (Potree.pointBudget !== value) {
Potree.pointBudget = parseInt(value);
this.dispatchEvent({'type': 'point_budget_changed', 'viewer': this});
}
};
getPointBudget () {
return Potree.pointBudget;
};
setShowAnnotations (value) {
if (this.showAnnotations !== value) {
this.showAnnotations = value;
this.dispatchEvent({'type': 'show_annotations_changed', 'viewer': this});
}
}
getShowAnnotations () {
return this.showAnnotations;
}
setDEMCollisionsEnabled(value){
if(this.useDEMCollisions !== value){
this.useDEMCollisions = value;
this.dispatchEvent({'type': 'use_demcollisions_changed', 'viewer': this});
};
};
getDEMCollisionsEnabled () {
return this.useDEMCollisions;
};
setEDLEnabled (value) {
value = Boolean(value);
if (this.useEDL !== value) {
this.useEDL = value;
this.dispatchEvent({'type': 'use_edl_changed', 'viewer': this});
}
};
getEDLEnabled () {
return this.useEDL;
};
setEDLRadius (value) {
if (this.edlRadius !== value) {
this.edlRadius = value;
this.dispatchEvent({'type': 'edl_radius_changed', 'viewer': this});
}
};
getEDLRadius () {
return this.edlRadius;
};
setEDLStrength (value) {
if (this.edlStrength !== value) {
this.edlStrength = value;
this.dispatchEvent({'type': 'edl_strength_changed', 'viewer': this});
}
};
getEDLStrength () {
return this.edlStrength;
};
setFOV (value) {
if (this.fov !== value) {
this.fov = value;
this.dispatchEvent({'type': 'fov_changed', 'viewer': this});
}
};
getFOV () {
return this.fov;
};
disableAnnotations () {
this.scene.annotations.traverse(annotation => {
annotation.domElement.css('pointer-events', 'none');
// return annotation.visible;
});
};
enableAnnotations () {
this.scene.annotations.traverse(annotation => {
annotation.domElement.css('pointer-events', 'auto');
// return annotation.visible;
});
};
setClassificationVisibility (key, value) {
if (!this.classifications[key]) {
this.classifications[key] = {visible: value, name: 'no name'};
this.dispatchEvent({'type': 'classification_visibility_changed', 'viewer': this});
} else if (this.classifications[key].visible !== value) {
this.classifications[key].visible = value;
this.dispatchEvent({'type': 'classification_visibility_changed', 'viewer': this});
}
};
setLengthUnit (value) {
switch (value) {
case 'm':
this.lengthUnit = this.LENGTH_UNITS.METER;
break;
case 'ft':
this.lengthUnit = this.LENGTH_UNITS.FEET;
break;
case 'in':
this.lengthUnit = this.LENGTH_UNITS.INCH;
break;
}
this.dispatchEvent({'type': 'length_unit_changed', 'viewer': this, value: value});
}
zoomTo(node, factor, animationDuration = 0){
let view = this.scene.view;
let camera = this.scene.cameraP.clone();
camera.rotation.copy(this.scene.cameraP.rotation);
camera.rotation.order = "ZXY";
camera.rotation.x = Math.PI / 2 + view.pitch;
camera.rotation.z = view.yaw;
camera.updateMatrix();
camera.updateMatrixWorld();
camera.zoomTo(node, factor);
let bs;
if (node.boundingSphere) {
bs = node.boundingSphere;
} else if (node.geometry && node.geometry.boundingSphere) {
bs = node.geometry.boundingSphere;
} else {
bs = node.boundingBox.getBoundingSphere();
}
bs = bs.clone().applyMatrix4(node.matrixWorld);
let startPosition = view.position.clone();
let endPosition = camera.position.clone();
let startTarget = view.getPivot();
let endTarget = bs.center;
let startRadius = view.radius;
let endRadius = endPosition.distanceTo(endTarget);
let easing = TWEEN.Easing.Quartic.Out;
{ // animate camera position
let pos = startPosition.clone();
let tween = new TWEEN.Tween(pos).to(endPosition, animationDuration);
tween.easing(easing);
tween.onUpdate(() => {
view.position.copy(pos);
});
tween.start();
}
{ // animate camera target
let target = startTarget.clone();
let tween = new TWEEN.Tween(target).to(endTarget, animationDuration);
tween.easing(easing);
tween.onUpdate(() => {
view.lookAt(target);
});
tween.onComplete(() => {
view.lookAt(target);
this.dispatchEvent({type: 'focusing_finished', target: this});
});
this.dispatchEvent({type: 'focusing_started', target: this});
tween.start();
}
};
showAbout () {
$(function () {
$('#about-panel').dialog();
});
};
getBoundingBox (pointclouds) {
return this.scene.getBoundingBox(pointclouds);
};
fitToScreen (factor = 1, animationDuration = 0) {
let box = this.getBoundingBox(this.scene.pointclouds);
let node = new THREE.Object3D();
node.boundingBox = box;
this.zoomTo(node, factor, animationDuration);
this.controls.stop();
};
toggleNavigationCube() {
this.navigationCube.visible = !this.navigationCube.visible;
}
setView(view) {
if(!view) return;
switch(view) {
case "F":
this.setFrontView();
break;
case "B":
this.setBackView();
break;
case "L":
this.setLeftView();
break;
case "R":
this.setRightView();
break;
case "U":
this.setTopView();
break;
case "D":
this.setBottomView();
break;
}
}
setTopView(){
this.scene.view.yaw = 0;
this.scene.view.pitch = -Math.PI / 2;
this.fitToScreen();
};
setBottomView(){
this.scene.view.yaw = -Math.PI;
this.scene.view.pitch = Math.PI / 2;
this.fitToScreen();
};
setFrontView(){
this.scene.view.yaw = 0;
this.scene.view.pitch = 0;
this.fitToScreen();
};
setBackView(){
this.scene.view.yaw = Math.PI;
this.scene.view.pitch = 0;
this.fitToScreen();
};
setLeftView(){
this.scene.view.yaw = -Math.PI / 2;
this.scene.view.pitch = 0;
this.fitToScreen();
};
setRightView () {
this.scene.view.yaw = Math.PI / 2;
this.scene.view.pitch = 0;
this.fitToScreen();
};
flipYZ () {
this.isFlipYZ = !this.isFlipYZ;
// TODO flipyz
console.log('TODO');
}
setCameraMode(mode){
this.scene.cameraMode = mode;
for(let pointcloud of this.scene.pointclouds) {
pointcloud.material.useOrthographicCamera = mode == Potree.CameraMode.ORTHOGRAPHIC;
}
}
loadSettingsFromURL(){
if(Potree.utils.getParameterByName("pointSize")){
this.setPointSize(parseFloat(Potree.utils.getParameterByName("pointSize")));
}
if(Potree.utils.getParameterByName("FOV")){
this.setFOV(parseFloat(Potree.utils.getParameterByName("FOV")));
}
if(Potree.utils.getParameterByName("opacity")){
this.setOpacity(parseFloat(Potree.utils.getParameterByName("opacity")));
}
if(Potree.utils.getParameterByName("edlEnabled")){
let enabled = Potree.utils.getParameterByName("edlEnabled") === "true";
this.setEDLEnabled(enabled);
}
if (Potree.utils.getParameterByName('edlRadius')) {
this.setEDLRadius(parseFloat(Potree.utils.getParameterByName('edlRadius')));
}
if (Potree.utils.getParameterByName('edlStrength')) {
this.setEDLStrength(parseFloat(Potree.utils.getParameterByName('edlStrength')));
}
if (Potree.utils.getParameterByName('pointBudget')) {
this.setPointBudget(parseFloat(Potree.utils.getParameterByName('pointBudget')));
}
if (Potree.utils.getParameterByName('showBoundingBox')) {
let enabled = Potree.utils.getParameterByName('showBoundingBox') === 'true';
if (enabled) {
this.setShowBoundingBox(true);
} else {
this.setShowBoundingBox(false);
}
}
if (Potree.utils.getParameterByName('material')) {
let material = Potree.utils.getParameterByName('material');
this.setMaterial(material);
}
if (Potree.utils.getParameterByName('pointSizing')) {
let sizing = Potree.utils.getParameterByName('pointSizing');
this.setPointSizing(sizing);
}
if (Potree.utils.getParameterByName('quality')) {
let quality = Potree.utils.getParameterByName('quality');
this.setQuality(quality);
}
if (Potree.utils.getParameterByName('position')) {
let value = Potree.utils.getParameterByName('position');
value = value.replace('[', '').replace(']', '');
let tokens = value.split(';');
let x = parseFloat(tokens[0]);
let y = parseFloat(tokens[1]);
let z = parseFloat(tokens[2]);
this.scene.view.position.set(x, y, z);
}
if (Potree.utils.getParameterByName('target')) {
let value = Potree.utils.getParameterByName('target');
value = value.replace('[', '').replace(']', '');
let tokens = value.split(';');
let x = parseFloat(tokens[0]);
let y = parseFloat(tokens[1]);
let z = parseFloat(tokens[2]);
this.scene.view.lookAt(new THREE.Vector3(x, y, z));
}
if (Potree.utils.getParameterByName('background')) {
let value = Potree.utils.getParameterByName('background');
this.setBackground(value);
}
// if(Potree.utils.getParameterByName("elevationRange")){
// let value = Potree.utils.getParameterByName("elevationRange");
// value = value.replace("[", "").replace("]", "");
// let tokens = value.split(";");
// let x = parseFloat(tokens[0]);
// let y = parseFloat(tokens[1]);
//
// this.setElevationRange(x, y);
// //this.scene.view.target.set(x, y, z);
// }
};
// ------------------------------------------------------------------------------------
// Viewer Internals
// ------------------------------------------------------------------------------------
createControls () {
{ // create FIRST PERSON CONTROLS
this.fpControls = new Potree.FirstPersonControls(this);
this.fpControls.enabled = false;
this.fpControls.addEventListener('start', this.disableAnnotations.bind(this));
this.fpControls.addEventListener('end', this.enableAnnotations.bind(this));
// this.fpControls.addEventListener("double_click_move", (event) => {
// let distance = event.targetLocation.distanceTo(event.position);
// this.setMoveSpeed(Math.pow(distance, 0.4));
// });
// this.fpControls.addEventListener("move_speed_changed", (event) => {
// this.setMoveSpeed(this.fpControls.moveSpeed);
// });
}
// { // create GEO CONTROLS
// this.geoControls = new Potree.GeoControls(this.scene.camera, this.renderer.domElement);
// this.geoControls.enabled = false;
// this.geoControls.addEventListener("start", this.disableAnnotations.bind(this));
// this.geoControls.addEventListener("end", this.enableAnnotations.bind(this));
// this.geoControls.addEventListener("move_speed_changed", (event) => {
// this.setMoveSpeed(this.geoControls.moveSpeed);
// });
// }
{ // create ORBIT CONTROLS
this.orbitControls = new Potree.OrbitControls(this);
this.orbitControls.enabled = false;
this.orbitControls.addEventListener('start', this.disableAnnotations.bind(this));
this.orbitControls.addEventListener('end', this.enableAnnotations.bind(this));
}
{ // create EARTH CONTROLS
this.earthControls = new Potree.EarthControls(this);
this.earthControls.enabled = false;
this.earthControls.addEventListener('start', this.disableAnnotations.bind(this));
this.earthControls.addEventListener('end', this.enableAnnotations.bind(this));
}
};
toggleSidebar () {
let renderArea = $('#potree_render_area');
let isVisible = renderArea.css('left') !== '0px';
if (isVisible) {
renderArea.css('left', '0px');
} else {
renderArea.css('left', '300px');
}
};
toggleMap () {
// let map = $('#potree_map');
// map.toggle(100);
if (this.mapView) {
this.mapView.toggle();
}
};
onGUILoaded(callback){
if(this.guiLoaded){
callback();
}else{
this.guiLoadTasks.push(callback);
}
}
loadGUI(callback){
this.onGUILoaded(callback);
let viewer = this;
let sidebarContainer = $('#potree_sidebar_container');
sidebarContainer.load(new URL(Potree.scriptPath + '/sidebar.html').href, () => {
sidebarContainer.css('width', '300px');
sidebarContainer.css('height', '100%');
let imgMenuToggle = document.createElement('img');
imgMenuToggle.src = new URL(Potree.resourcePath + '/icons/menu_button.svg').href;
imgMenuToggle.onclick = this.toggleSidebar;
imgMenuToggle.classList.add('potree_menu_toggle');
let imgMapToggle = document.createElement('img');
imgMapToggle.src = new URL(Potree.resourcePath + '/icons/map_icon.png').href;
imgMapToggle.style.display = 'none';
imgMapToggle.onclick = e => { this.toggleMap(); };
imgMapToggle.id = 'potree_map_toggle';
viewer.renderArea.insertBefore(imgMapToggle, viewer.renderArea.children[0]);
viewer.renderArea.insertBefore(imgMenuToggle, viewer.renderArea.children[0]);
this.mapView = new Potree.MapView(this);
this.mapView.init();
i18n.init({
lng: 'en',
resGetPath: Potree.resourcePath + '/lang/__lng__/__ns__.json',
preload: ['en', 'fr', 'de', 'jp'],
getAsync: true,
debug: false
}, function (t) {
// Start translation once everything is loaded
$('body').i18n();
});
$(() => {
initSidebar(this);
//if (callback) {
// $(callback);
//}
let elProfile = $('
').load(new URL(Potree.scriptPath + '/profile.html').href, () => {
$(document.body).append(elProfile.children());
this.profileWindow = new Potree.ProfileWindow(this);
this.profileWindowController = new Potree.ProfileWindowController(this);
$('#profile_window').draggable({
handle: $('#profile_titlebar'),
containment: $(document.body)
});
$('#profile_window').resizable({
containment: $(document.body),
handles: 'n, e, s, w'
});
$(() => {
this.guiLoaded = true;
for(let task of this.guiLoadTasks){
task();
}
});
});
});
});
}
setLanguage (lang) {
i18n.setLng(lang);
$('body').i18n();
}
setServer (server) {
this.server = server;
}
initThree () {
let width = this.renderArea.clientWidth;
let height = this.renderArea.clientHeight;
this.renderer = new THREE.WebGLRenderer({alpha: true, premultipliedAlpha: false});
this.renderer.sortObjects = false;
this.renderer.setSize(width, height);
this.renderer.autoClear = false;
this.renderArea.appendChild(this.renderer.domElement);
this.renderer.domElement.tabIndex = '2222';
this.renderer.domElement.style.position = 'absolute';
this.renderer.domElement.addEventListener('mousedown', () => {
this.renderer.domElement.focus();
});
// enable frag_depth extension for the interpolation shader, if available
let gl = this.renderer.context;
gl.getExtension('EXT_frag_depth');
gl.getExtension('WEBGL_depth_texture');
let extVAO = gl.getExtension('OES_vertex_array_object');
gl.createVertexArray = extVAO.createVertexArrayOES.bind(extVAO);
gl.bindVertexArray = extVAO.bindVertexArrayOES.bind(extVAO);
//gl.bindVertexArray = extVAO.asdfbindVertexArrayOES.bind(extVAO);
}
updateAnnotations () {
if(!this.visibleAnnotations){
this.visibleAnnotations = new Set();
}
this.scene.annotations.updateBounds();
this.scene.cameraP.updateMatrixWorld();
this.scene.cameraO.updateMatrixWorld();
let distances = [];
let renderAreaWidth = this.renderer.getSize().width;
let renderAreaHeight = this.renderer.getSize().height;
let viewer = this;
let visibleNow = [];
this.scene.annotations.traverse(annotation => {
if (annotation === this.scene.annotations) {
return true;
}
if (!annotation.visible) {
return false;
}
annotation.scene = this.scene;
let element = annotation.domElement;
let position = annotation.position;
if (!position) {
position = annotation.boundingBox.getCenter();
}
let distance = viewer.scene.cameraP.position.distanceTo(position);
let radius = annotation.boundingBox.getBoundingSphere().radius;
let screenPos = new THREE.Vector3();
let screenSize = 0;
{
// SCREEN POS
screenPos.copy(position).project(this.scene.getActiveCamera());
screenPos.x = renderAreaWidth * (screenPos.x + 1) / 2;
screenPos.y = renderAreaHeight * (1 - (screenPos.y + 1) / 2);
// SCREEN SIZE
if(viewer.scene.cameraMode == Potree.CameraMode.PERSPECTIVE) {
let fov = Math.PI * viewer.scene.cameraP.fov / 180;
let slope = Math.tan(fov / 2.0);
let projFactor = 0.5 * renderAreaHeight / (slope * distance);
screenSize = radius * projFactor;
} else {
screenSize = Potree.utils.projectedRadiusOrtho(radius, viewer.scene.cameraO.projectionMatrix, renderAreaWidth, renderAreaHeight);
}
}
element.css("left", screenPos.x + "px");
element.css("top", screenPos.y + "px");
//element.css("display", "block");
let zIndex = 10000000 - distance * (10000000 / this.scene.cameraP.far);
if(annotation.descriptionVisible){
zIndex += 10000000;
}
element.css("z-index", parseInt(zIndex));
if(annotation.children.length > 0){
let expand = screenSize > annotation.collapseThreshold || annotation.boundingBox.containsPoint(this.scene.getActiveCamera().position);
annotation.expand = expand;
if (!expand) {
//annotation.display = (screenPos.z >= -1 && screenPos.z <= 1);
let inFrustum = (screenPos.z >= -1 && screenPos.z <= 1);
if(inFrustum){
visibleNow.push(annotation);
}
}
return expand;
} else {
//annotation.display = (screenPos.z >= -1 && screenPos.z <= 1);
let inFrustum = (screenPos.z >= -1 && screenPos.z <= 1);
if(inFrustum){
visibleNow.push(annotation);
}
}
});
let notVisibleAnymore = new Set(this.visibleAnnotations);
for(let annotation of visibleNow){
annotation.display = true;
notVisibleAnymore.delete(annotation);
}
this.visibleAnnotations = visibleNow;
for(let annotation of notVisibleAnymore){
annotation.display = false;
}
}
update(delta, timestamp){
if(Potree.measureTimings) performance.mark("update-start");
// if(window.urlToggle === undefined){
// window.urlToggle = 0;
// }else{
//
// if(window.urlToggle > 1){
// {
//
// let currentValue = Potree.utils.getParameterByName("position");
// let strPosition = "["
// + this.scene.view.position.x.toFixed(3) + ";"
// + this.scene.view.position.y.toFixed(3) + ";"
// + this.scene.view.position.z.toFixed(3) + "]";
// if(currentValue !== strPosition){
// Potree.utils.setParameter("position", strPosition);
// }
//
// }
//
// {
// let currentValue = Potree.utils.getParameterByName("target");
// let pivot = this.scene.view.getPivot();
// let strTarget = "["
// + pivot.x.toFixed(3) + ";"
// + pivot.y.toFixed(3) + ";"
// + pivot.z.toFixed(3) + "]";
// if(currentValue !== strTarget){
// Potree.utils.setParameter("target", strTarget);
// }
// }
//
// window.urlToggle = 0;
// }
//
// window.urlToggle += delta;
//}
{
let u = Math.sin(0.0005 * timestamp) * 0.5 - 0.4;
let x = Math.cos(u);
let y = Math.sin(u);
this.shadowTestCam.position.set(7 * x, 7 * y, 8.561);
this.shadowTestCam.lookAt(new THREE.Vector3(0, 0, 0));
}
let scene = this.scene;
let camera = scene.getActiveCamera();
Potree.pointLoadLimit = Potree.pointBudget * 2;
this.scene.directionalLight.position.copy(camera.position);
this.scene.directionalLight.lookAt(new THREE.Vector3().addVectors(camera.position, camera.getWorldDirection()));
for (let pointcloud of this.scene.pointclouds) {
if (!pointcloud.material._defaultIntensityRangeChanged) {
let root = pointcloud.pcoGeometry.root;
if (root != null && root.loaded) {
let attributes = pointcloud.pcoGeometry.root.geometry.attributes;
if (attributes.intensity) {
let array = attributes.intensity.array;
// chose max value from the 0.75 percentile
let ordered = [];
for (let j = 0; j < array.length; j++) {
ordered.push(array[j]);
}
ordered.sort();
let capIndex = parseInt((ordered.length - 1) * 0.75);
let cap = ordered[capIndex];
if (cap <= 1) {
pointcloud.material.intensityRange = [0, 1];
} else if (cap <= 256) {
pointcloud.material.intensityRange = [0, 255];
} else {
pointcloud.material.intensityRange = [0, cap];
}
}
// pointcloud._intensityMaxEvaluated = true;
}
}
pointcloud.showBoundingBox = this.showBoundingBox;
pointcloud.generateDEM = this.generateDEM;
pointcloud.minimumNodePixelSize = this.minNodeSize;
}
// update classification visibility
for (let pointcloud of this.scene.pointclouds) {
let classification = pointcloud.material.classification;
let somethingChanged = false;
for (let key of Object.keys(this.classifications)) {
let w = this.classifications[key].visible ? 1 : 0;
if (classification[key]) {
if (classification[key].w !== w) {
classification[key].w = w;
somethingChanged = true;
}
} else if (classification.DEFAULT) {
classification[key] = classification.DEFAULT;
somethingChanged = true;
} else {
classification[key] = new THREE.Vector4(0.3, 0.6, 0.6, 0.5);
somethingChanged = true;
}
}
if (somethingChanged) {
pointcloud.material.recomputeClassification();
}
}
{
if(this.showBoundingBox){
let bbRoot = this.scene.scene.getObjectByName("potree_bounding_box_root");
if(!bbRoot){
let node = new THREE.Object3D();
node.name = "potree_bounding_box_root";
this.scene.scene.add(node);
bbRoot = node;
}
let visibleBoxes = [];
for(let pointcloud of this.scene.pointclouds){
for(let node of pointcloud.visibleNodes.filter(vn => vn.boundingBoxNode !== undefined)){
let box = node.boundingBoxNode;
visibleBoxes.push(box);
}
}
bbRoot.children = visibleBoxes;
}
}
if (!this.freeze) {
let result = Potree.updatePointClouds(scene.pointclouds, camera, this.renderer);
if(result.lowestSpacing !== Infinity){
let near = result.lowestSpacing * 10.0;
let far = -this.getBoundingBox().applyMatrix4(camera.matrixWorldInverse).min.z;
far = Math.max(far * 1.5, 1000);
near = Math.min(100.0, Math.max(0.01, near));
far = Math.max(far, near + 1000);
if(near === Infinity){
near = 0.1;
}
camera.near = near;
camera.far = far;
}else{
// don't change near and far in this case
}
if(this.scene.cameraMode == Potree.CameraMode.ORTHOGRAPHIC) {
camera.near = -camera.far;
}
}
this.scene.cameraP.fov = this.fov;
// Navigation mode changed?
if (this.getControls(scene.view.navigationMode) !== this.controls) {
if (this.controls) {
this.controls.enabled = false;
this.inputHandler.removeInputListener(this.controls);
}
this.controls = this.getControls(scene.view.navigationMode);
this.controls.enabled = true;
this.inputHandler.addInputListener(this.controls);
}
if (this.controls !== null) {
this.controls.setScene(scene);
this.controls.update(delta);
this.scene.cameraP.position.copy(scene.view.position);
this.scene.cameraP.rotation.order = "ZXY";
this.scene.cameraP.rotation.x = Math.PI / 2 + this.scene.view.pitch;
this.scene.cameraP.rotation.z = this.scene.view.yaw;
this.scene.cameraO.position.copy(scene.view.position);
this.scene.cameraO.rotation.order = "ZXY";
this.scene.cameraO.rotation.x = Math.PI / 2 + this.scene.view.pitch;
this.scene.cameraO.rotation.z = this.scene.view.yaw;
}
camera.updateMatrix();
camera.updateMatrixWorld();
camera.matrixWorldInverse.getInverse(camera.matrixWorld);
{
if(this._previousCamera === undefined){
this._previousCamera = this.scene.getActiveCamera().clone();
this._previousCamera.rotation.copy(this.scene.getActiveCamera());
}
if(!this._previousCamera.matrixWorld.equals(camera.matrixWorld)){
this.dispatchEvent({
type: "camera_changed",
previous: this._previousCamera,
camera: camera
});
}else if(!this._previousCamera.projectionMatrix.equals(camera.projectionMatrix)){
this.dispatchEvent({
type: "camera_changed",
previous: this._previousCamera,
camera: camera
});
}
this._previousCamera = this.scene.getActiveCamera().clone();
this._previousCamera.rotation.copy(this.scene.getActiveCamera());
}
{ // update clip boxes
let boxes = [];
// volumes with clipping enabled
boxes.push(...this.scene.volumes.filter(v => v.clip));
// profile segments
for(let profile of this.scene.profiles){
boxes.push(...profile.boxes);
}
let clipBoxes = boxes.map( box => {
box.updateMatrixWorld();
let boxInverse = new THREE.Matrix4().getInverse(box.matrixWorld);
let boxPosition = box.getWorldPosition();
return {box: box, inverse: boxInverse, position: boxPosition};
});
let clipPolygons = this.scene.polygonClipVolumes.filter(vol => vol.initialized);
// set clip volumes in material
for(let pointcloud of this.scene.pointclouds.filter(pc => pc.visible)){
pointcloud.material.setClipBoxes(clipBoxes);
pointcloud.material.setClipPolygons(clipPolygons, this.clippingTool.maxPolygonVertices);
pointcloud.material.clipTask = this.clipTask;
pointcloud.material.clipMethod = this.clipMethod;
}
}
{ // update navigation cube
this.navigationCube.update(camera.rotation);
}
this.updateAnnotations();
if(this.mapView){
this.mapView.update(delta);
if(this.mapView.sceneProjection){
$( "#potree_map_toggle" ).css("display", "block");
}
}
TWEEN.update(timestamp);
this.dispatchEvent({
type: 'update',
delta: delta,
timestamp: timestamp});
if(Potree.measureTimings) {
performance.mark("update-end");
performance.measure("update", "update-start", "update-end");
}
}
render(){
if(Potree.measureTimings) performance.mark("render-start");
{ // resize
let width = this.scaleFactor * this.renderArea.clientWidth;
let height = this.scaleFactor * this.renderArea.clientHeight;
let pixelRatio = this.renderer.getPixelRatio();
let aspect = width / height;
this.scene.cameraP.aspect = aspect;
this.scene.cameraP.updateProjectionMatrix();
//let frustumScale = viewer.moveSpeed * 2.0;
let frustumScale = this.scene.view.radius;
this.scene.cameraO.left = -frustumScale;
this.scene.cameraO.right = frustumScale;
this.scene.cameraO.top = frustumScale * 1 / aspect;
this.scene.cameraO.bottom = -frustumScale * 1 / aspect;
this.scene.cameraO.updateProjectionMatrix();
this.scene.cameraScreenSpace.top = 1/aspect;
this.scene.cameraScreenSpace.bottom = -1/aspect;
this.scene.cameraScreenSpace.updateProjectionMatrix();
this.renderer.setSize(width, height);
}
try{
if(this.useRep){
if (!this.repRenderer) {
this.repRenderer = new RepRenderer(this);
}
this.repRenderer.render(this.renderer);
}else if(this.useHQ){
if (!this.hqRenderer) {
this.hqRenderer = new HQSplatRenderer(this);
}
this.hqRenderer.useEDL = this.useEDL;
this.hqRenderer.render(this.renderer);
}else{
if (this.useEDL && Potree.Features.SHADER_EDL.isSupported()) {
if (!this.edlRenderer) {
this.edlRenderer = new EDLRenderer(this);
}
this.edlRenderer.render(this.renderer);
} else {
if (!this.potreeRenderer) {
this.potreeRenderer = new PotreeRenderer(this);
}
this.potreeRenderer.render();
}
}
//if(this.useRep){
// if (!this.repRenderer) {
// this.repRenderer = new RepRenderer(this);
// }
// this.repRenderer.render(this.renderer);
//} else if (this.useHQ && Potree.Features.SHADER_SPLATS.isSupported()) {
// if (!this.hqRenderer) {
// this.hqRenderer = new HQSplatRenderer(this);
// }
// this.hqRenderer.render(this.renderer);
//} else if (this.useEDL && Potree.Features.SHADER_EDL.isSupported()) {
// if (!this.edlRenderer) {
// this.edlRenderer = new EDLRenderer(this);
// }
// this.edlRenderer.render(this.renderer);
//} else {
// if (!this.potreeRenderer) {
// this.potreeRenderer = new PotreeRenderer(this);
// }
// this.potreeRenderer.render();
//}
this.renderer.render(this.overlay, this.overlayCamera);
}catch(e){
this.onCrash(e);
}
if(Potree.measureTimings){
performance.mark("render-end");
performance.measure("render", "render-start", "render-end");
}
}
resolveTimings(timestamp){
if(Potree.measureTimings){
if(!this.toggle){
this.toggle = timestamp;
}
let duration = timestamp - this.toggle;
if(duration > 1000.0){
let measures = performance.getEntriesByType("measure");
let names = new Set();
for(let measure of measures){
names.add(measure.name);
}
let groups = new Map();
for(let name of names){
groups.set(name, {
measures: [],
sum: 0,
n: 0,
min: Infinity,
max: -Infinity
});
}
for(let measure of measures){
let group = groups.get(measure.name);
group.measures.push(measure);
group.sum += measure.duration;
group.n++;
group.min = Math.min(group.min, measure.duration);
group.max = Math.max(group.max, measure.duration);
}
let glQueries = Potree.resolveQueries(this.renderer.getContext());
for(let [key, value] of glQueries){
let group = {
measures: value.map(v => {return {duration: v}}),
sum: value.reduce( (a, i) => a + i, 0),
n: value.length,
min: Math.min(...value),
max: Math.max(...value)
};
let groupname = `[tq] ${key}`;
groups.set(groupname, group);
names.add(groupname);
}
for(let [name, group] of groups){
group.mean = group.sum / group.n;
group.measures.sort( (a, b) => a.duration - b.duration );
if(group.n === 1){
group.median = group.measures[0].duration;
}else if(group.n > 1){
group.median = group.measures[parseInt(group.n / 2)].duration;
}
}
let cn = Array.from(names).reduce( (a, i) => Math.max(a, i.length), 0) + 5;
let cmin = 10;
let cmed = 10;
let cmax = 10;
let csam = 6;
let message = ` ${"NAME".padEnd(cn)} |`
+ ` ${"MIN".padStart(cmin)} |`
+ ` ${"MEDIAN".padStart(cmed)} |`
+ ` ${"MAX".padStart(cmax)} |`
+ ` ${"SAMPLES".padStart(csam)} \n`;
message += ` ${"-".repeat(message.length) }\n`;
names = Array.from(names).sort();
for(let name of names){
let group = groups.get(name);
let min = group.min.toFixed(3);
let median = group.median.toFixed(3);
let max = group.max.toFixed(3);
let n = group.n;
message += ` ${name.padEnd(cn)} |`
+ ` ${min.padStart(cmin)} |`
+ ` ${median.padStart(cmed)} |`
+ ` ${max.padStart(cmax)} |`
+ ` ${n.toString().padStart(csam)}\n`;
}
message += `\n`;
console.log(message);
performance.clearMarks();
performance.clearMeasures();
this.toggle = timestamp;
}
}
}
loop(timestamp){
requestAnimationFrame(this.loop.bind(this));
let queryAll;
if(Potree.measureTimings){
performance.mark("loop-start");
}
this.update(this.clock.getDelta(), timestamp);
this.render();
if(Potree.measureTimings){
performance.mark("loop-end");
performance.measure("loop", "loop-start", "loop-end");
}
this.resolveTimings(timestamp);
Potree.framenumber++;
}
postError(content, params = {}){
let message = this.postMessage(content, params);
message.element.addClass("potree_message_error");
return message;
}
postMessage(content, params = {}){
let message = new Potree.Message(content);
let animationDuration = 100;
message.element.css("display", "none");
message.elClose.click( () => {
message.element.slideToggle(animationDuration);
let index = this.messages.indexOf(message);
if(index >= 0){
this.messages.splice(index, 1);
}
});
this.elMessages.prepend(message.element);
message.element.slideToggle(animationDuration);
this.messages.push(message);
if(params.duration !== undefined){
let fadeDuration = 500;
let slideOutDuration = 200;
setTimeout(() => {
message.element.animate({
opacity: 0
}, fadeDuration);
message.element.slideToggle(slideOutDuration);
}, params.duration)
}
return message;
}
};
class ProfilePointCloudEntry{
constructor(){
this.points = [];
//let geometry = new THREE.BufferGeometry();
let material = ProfilePointCloudEntry.getMaterialInstance();
material.uniforms.minSize.value = 2;
material.uniforms.maxSize.value = 2;
material.pointColorType = Potree.PointColorType.RGB;
material.opacity = 1.0;
this.material = material;
this.sceneNode = new THREE.Object3D();
//this.sceneNode = new THREE.Points(geometry, material);
}
static releaseMaterialInstance(instance){
ProfilePointCloudEntry.materialPool.add(instance);
}
static getMaterialInstance(){
let instance = ProfilePointCloudEntry.materialPool.values().next().value;
if(!instance){
instance = new Potree.PointCloudMaterial();
}else{
ProfilePointCloudEntry.materialPool.delete(instance);
}
return instance;
}
dispose(){
for(let child of this.sceneNode.children){
ProfilePointCloudEntry.releaseMaterialInstance(child.material);
child.geometry.dispose();
}
this.sceneNode.children = [];
}
addPoints(data){
this.points.push(data);
let batchSize = 10*1000;
let createNewBatch = () => {
let geometry = new THREE.BufferGeometry();
let buffers = {
position: new Float32Array(3 * batchSize),
color: new Uint8Array(4 * batchSize),
intensity: new Uint16Array(batchSize),
classification: new Uint8Array(batchSize),
returnNumber: new Uint8Array(batchSize),
numberOfReturns: new Uint8Array(batchSize),
pointSourceID: new Uint16Array(batchSize)
};
geometry.addAttribute('position', new THREE.BufferAttribute(buffers.position, 3));
geometry.addAttribute('color', new THREE.BufferAttribute(buffers.color, 4, true));
geometry.addAttribute('intensity', new THREE.BufferAttribute(buffers.intensity, 1, false));
geometry.addAttribute('classification', new THREE.BufferAttribute(buffers.classification, 1, false));
geometry.addAttribute('returnNumber', new THREE.BufferAttribute(buffers.returnNumber, 1, false));
geometry.addAttribute('numberOfReturns', new THREE.BufferAttribute(buffers.numberOfReturns, 1, false));
geometry.addAttribute('pointSourceID', new THREE.BufferAttribute(buffers.pointSourceID, 1, false));
geometry.drawRange.start = 0;
geometry.drawRange.count = 0;
this.currentBatch = new THREE.Points(geometry, this.material);
this.sceneNode.add(this.currentBatch);
}
if(!this.currentBatch){
createNewBatch();
}
{ // REBUILD MODEL
let pointsProcessed = 0;
let updateRange = {
start: this.currentBatch.geometry.drawRange.count,
count: 0
};
let projectedBox = new THREE.Box3();
for(let i = 0; i < data.numPoints; i++){
if(updateRange.start + updateRange.count >= batchSize){
// finalize current batch, start new batch
for(let key of Object.keys(this.currentBatch.geometry.attributes)){
let attribute = this.currentBatch.geometry.attributes[key];
attribute.updateRange.offset = updateRange.start;
attribute.updateRange.count = updateRange.count;
attribute.needsUpdate = true;
}
this.currentBatch.geometry.computeBoundingBox();
this.currentBatch.geometry.computeBoundingSphere();
createNewBatch();
updateRange = {
start: 0,
count: 0
};
}
let x = data.data.mileage[i];
let y = 0;
let z = data.data.position[3 * i + 2];
projectedBox.expandByPoint(new THREE.Vector3(x, y, z));
let currentIndex = updateRange.start + updateRange.count;
let attributes = this.currentBatch.geometry.attributes;
{
attributes.position.array[3 * currentIndex + 0] = x;
attributes.position.array[3 * currentIndex + 1] = y;
attributes.position.array[3 * currentIndex + 2] = z;
}
if(data.data.color){
attributes.color.array[4 * currentIndex + 0] = data.data.color[4 * i + 0];
attributes.color.array[4 * currentIndex + 1] = data.data.color[4 * i + 1];
attributes.color.array[4 * currentIndex + 2] = data.data.color[4 * i + 2];
attributes.color.array[4 * currentIndex + 3] = 255;
}
if(data.data.intensity){
attributes.intensity.array[currentIndex] = data.data.intensity[i];
}
if(data.data.classification){
attributes.classification.array[currentIndex] = data.data.classification[i];
}
if(data.data.returnNumber){
attributes.returnNumber.array[currentIndex] = data.data.returnNumber[i];
}
if(data.data.numberOfReturns){
attributes.numberOfReturns.array[currentIndex] = data.data.numberOfReturns[i];
}
if(data.data.pointSourceID){
attributes.pointSourceID.array[currentIndex] = data.data.pointSourceID[i];
}
updateRange.count++;
this.currentBatch.geometry.drawRange.count++;
}
//for(let attribute of Object.values(this.currentBatch.geometry.attributes)){
for(let key of Object.keys(this.currentBatch.geometry.attributes)){
let attribute = this.currentBatch.geometry.attributes[key];
attribute.updateRange.offset = updateRange.start;
attribute.updateRange.count = updateRange.count;
attribute.needsUpdate = true;
}
data.projectedBox = projectedBox;
this.projectedBox = this.points.reduce( (a, i) => a.union(i.projectedBox), new THREE.Box3());
}
}
};
ProfilePointCloudEntry.materialPool = new Set();
Potree.ProfileWindow = class ProfileWindow extends THREE.EventDispatcher {
constructor (viewer) {
super();
this.viewer = viewer;
this.elRoot = $('#profile_window');
this.renderArea = this.elRoot.find('#profileCanvasContainer');
this.svg = d3.select('svg#profileSVG');
this.mouseIsDown = false;
this.projectedBox = new THREE.Box3();
this.pointclouds = new Map();
this.numPoints = 0;
this.lastAddPointsTimestamp = undefined;
this.mouse = new THREE.Vector2(0, 0);
this.scale = new THREE.Vector3(1, 1, 1);
let csvIcon = `${Potree.resourcePath}/icons/file_csv_2d.svg`;
$('#potree_download_csv_icon').attr('src', csvIcon);
let lasIcon = `${Potree.resourcePath}/icons/file_las_3d.svg`;
$('#potree_download_las_icon').attr('src', lasIcon);
let closeIcon = `${Potree.resourcePath}/icons/close.svg`;
$('#closeProfileContainer').attr("src", closeIcon);
this.initTHREE();
this.initSVG();
this.initListeners();
this.elRoot.i18n();
}
initListeners () {
$(window).resize(() => {
this.render();
});
this.renderArea.mousedown(e => {
this.mouseIsDown = true;
});
this.renderArea.mouseup(e => {
this.mouseIsDown = false;
});
let viewerPickSphereSizeHandler = () => {
let camera = this.viewer.scene.getActiveCamera();
let domElement = this.viewer.renderer.domElement;
let distance = this.viewerPickSphere.position.distanceTo(camera.position);
let pr = Potree.utils.projectedRadius(1, camera, distance, domElement.clientWidth, domElement.clientHeight);
let scale = (10 / pr);
this.viewerPickSphere.scale.set(scale, scale, scale);
};
this.renderArea.mousemove(e => {
if (this.pointclouds.size === 0) {
return;
}
let rect = this.renderArea[0].getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
let newMouse = new THREE.Vector2(x, y);
if (this.mouseIsDown) {
// DRAG
this.autoFit = false;
this.lastDrag = new Date().getTime();
let cPos = [this.scaleX.invert(this.mouse.x), this.scaleY.invert(this.mouse.y)];
let ncPos = [this.scaleX.invert(newMouse.x), this.scaleY.invert(newMouse.y)];
this.camera.position.x -= ncPos[0] - cPos[0];
this.camera.position.z -= ncPos[1] - cPos[1];
this.render();
} else if (this.pointclouds.size > 0) {
// FIND HOVERED POINT
let radius = Math.abs(this.scaleX.invert(0) - this.scaleX.invert(40));
let mileage = this.scaleX.invert(newMouse.x);
let elevation = this.scaleY.invert(newMouse.y);
let point = this.selectPoint(mileage, elevation, radius);
if (point) {
this.elRoot.find('#profileSelectionProperties').fadeIn(200);
this.pickSphere.visible = true;
this.pickSphere.scale.set(0.5 * radius, 0.5 * radius, 0.5 * radius);
this.pickSphere.position.set(point.mileage, 0, point.position[2]);
this.viewerPickSphere.position.set(...point.position);
if(!this.viewer.scene.scene.children.includes(this.viewerPickSphere)){
this.viewer.scene.scene.add(this.viewerPickSphere);
if(!this.viewer.hasEventListener("update", viewerPickSphereSizeHandler)){
this.viewer.addEventListener("update", viewerPickSphereSizeHandler);
}
}
let info = this.elRoot.find('#profileSelectionProperties');
let html = '
';
for (let attribute of Object.keys(point)) {
let value = point[attribute];
if (attribute === 'position') {
let values = [...value].map(v => Potree.utils.addCommas(v.toFixed(3)));
html += `
x
${values[0]}
y
${values[1]}
z
${values[2]}
`;
} else if (attribute === 'color') {
html += `
${attribute}
${value.join(', ')}
`;
} else if (attribute === 'normal') {
continue;
} else if (attribute === 'mileage') {
html += `
${attribute}
${value.toFixed(3)}
`;
} else {
html += `
${attribute}
${value}
`;
}
}
html += '
';
info.html(html);
this.selectedPoint = point;
} else {
// this.pickSphere.visible = false;
// this.selectedPoint = null;
this.viewer.scene.scene.add(this.viewerPickSphere);
let index = this.viewer.scene.scene.children.indexOf(this.viewerPickSphere);
if(index >= 0){
this.viewer.scene.scene.children.splice(index, 1);
}
this.viewer.removeEventListener("update", viewerPickSphereSizeHandler);
}
this.render();
}
this.mouse.copy(newMouse);
});
let onWheel = e => {
this.autoFit = false;
let delta = 0;
if (e.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9
delta = e.wheelDelta;
} else if (e.detail !== undefined) { // Firefox
delta = -e.detail;
}
let ndelta = Math.sign(delta);
let cPos = [this.scaleX.invert(this.mouse.x), this.scaleY.invert(this.mouse.y)];
if (ndelta > 0) {
// + 10%
this.scale.multiplyScalar(1.1);
} else {
// - 10%
this.scale.multiplyScalar(100 / 110);
}
this.updateScales();
let ncPos = [this.scaleX.invert(this.mouse.x), this.scaleY.invert(this.mouse.y)];
this.camera.position.x -= ncPos[0] - cPos[0];
this.camera.position.z -= ncPos[1] - cPos[1];
this.render();
this.updateScales();
};
$(this.renderArea)[0].addEventListener('mousewheel', onWheel, false);
$(this.renderArea)[0].addEventListener('DOMMouseScroll', onWheel, false); // Firefox
$('#closeProfileContainer').click(() => {
this.hide();
});
$('#potree_download_csv_icon').click(() => {
let points = new Potree.Points();
for(let [pointcloud, entry] of this.pointclouds){
for(let pointSet of entry.points){
points.add(pointSet);
}
}
let string = Potree.CSVExporter.toString(points);
let blob = new Blob([string], {type: "text/string"});
$('#potree_download_profile_ortho_link').attr('href', URL.createObjectURL(blob));
//let uri = 'data:application/octet-stream;base64,' + btoa(string);
//$('#potree_download_profile_ortho_link').attr('href', uri);
});
$('#potree_download_las_icon').click(() => {
let points = new Potree.Points();
for(let [pointcloud, entry] of this.pointclouds){
for(let pointSet of entry.points){
points.add(pointSet);
}
}
let buffer = Potree.LASExporter.toLAS(points);
let blob = new Blob([buffer], {type: "application/octet-binary"});
$('#potree_download_profile_link').attr('href', URL.createObjectURL(blob));
//let u8view = new Uint8Array(buffer);
//let binString = '';
//for (let i = 0; i < u8view.length; i++) {
// binString += String.fromCharCode(u8view[i]);
//}
//
//let uri = 'data:application/octet-stream;base64,' + btoa(binString);
//$('#potree_download_profile_link').attr('href', uri);
});
}
selectPoint (mileage, elevation, radius) {
let closest = {
distance: Infinity,
pointcloud: null,
points: null,
index: null
};
let pointBox = new THREE.Box2(
new THREE.Vector2(mileage - radius, elevation - radius),
new THREE.Vector2(mileage + radius, elevation + radius));
//let debugNode = this.scene.getObjectByName("select_debug_node");
//if(!debugNode){
// debugNode = new THREE.Object3D();
// debugNode.name = "select_debug_node";
// this.scene.add(debugNode);
//}
//debugNode.children = [];
//let debugPointBox = new THREE.Box3(
// new THREE.Vector3(...pointBox.min.toArray(), -1),
// new THREE.Vector3(...pointBox.max.toArray(), +1)
//);
//debugNode.add(new Potree.Box3Helper(debugPointBox, 0xff0000));
let numTested = 0;
let numSkipped = 0;
let numTestedPoints = 0;
let numSkippedPoints = 0;
for (let [pointcloud, entry] of this.pointclouds) {
for(let points of entry.points){
let collisionBox = new THREE.Box2(
new THREE.Vector2(points.projectedBox.min.x, points.projectedBox.min.z),
new THREE.Vector2(points.projectedBox.max.x, points.projectedBox.max.z)
);
let intersects = collisionBox.intersectsBox(pointBox);
if(!intersects){
numSkipped++;
numSkippedPoints += points.numPoints;
continue;
}
//let debugCollisionBox = new THREE.Box3(
// new THREE.Vector3(...collisionBox.min.toArray(), -1),
// new THREE.Vector3(...collisionBox.max.toArray(), +1)
//);
//debugNode.add(new Potree.Box3Helper(debugCollisionBox));
numTested++;
numTestedPoints += points.numPoints
for (let i = 0; i < points.numPoints; i++) {
let m = points.data.mileage[i] - mileage;
let e = points.data.position[3 * i + 2] - elevation;
let r = Math.sqrt(m * m + e * e);
if (r < radius && r < closest.distance) {
closest = {
distance: r,
pointcloud: pointcloud,
points: points,
index: i
};
}
}
}
}
//console.log(`nodes: ${numTested}, ${numSkipped} || points: ${numTestedPoints}, ${numSkippedPoints}`);
if (closest.distance < Infinity) {
let points = closest.points;
let point = {};
let attributes = Object.keys(points.data);
for (let attribute of attributes) {
let attributeData = points.data[attribute];
let itemSize = attributeData.length / points.numPoints;
let value = attributeData.subarray(itemSize * closest.index, itemSize * closest.index + itemSize);
if (value.length === 1) {
point[attribute] = value[0];
} else {
point[attribute] = value;
}
}
return point;
} else {
return null;
}
}
initTHREE () {
this.renderer = new THREE.WebGLRenderer({alpha: true, premultipliedAlpha: false});
this.renderer.setClearColor(0x000000, 0);
this.renderer.setSize(10, 10);
this.renderer.autoClear = true;
this.renderArea.append($(this.renderer.domElement));
this.renderer.domElement.tabIndex = '2222';
this.renderer.context.getExtension('EXT_frag_depth');
$(this.renderer.domElement).css('width', '100%');
$(this.renderer.domElement).css('height', '100%');
this.camera = new THREE.OrthographicCamera(-1000, 1000, 1000, -1000, -1000, 1000);
this.camera.up.set(0, 0, 1);
this.camera.rotation.order = "ZXY";
this.camera.rotation.x = Math.PI / 2.0;
this.scene = new THREE.Scene();
let sg = new THREE.SphereGeometry(1, 16, 16);
let sm = new THREE.MeshNormalMaterial();
this.pickSphere = new THREE.Mesh(sg, sm);
//this.pickSphere.visible = false;
this.scene.add(this.pickSphere);
this.viewerPickSphere = new THREE.Mesh(sg, sm);
this.pointCloudRoot = new THREE.Object3D();
this.scene.add(this.pointCloudRoot);
}
initSVG () {
let width = this.renderArea[0].clientWidth;
let height = this.renderArea[0].clientHeight;
let marginLeft = this.renderArea[0].offsetLeft;
this.svg.selectAll('*').remove();
this.scaleX = d3.scale.linear()
.domain([this.camera.left + this.camera.position.x, this.camera.right + this.camera.position.x])
.range([0, width]);
this.scaleY = d3.scale.linear()
.domain([this.camera.bottom + this.camera.position.z, this.camera.top + this.camera.position.z])
.range([height, 0]);
this.xAxis = d3.svg.axis()
.scale(this.scaleX)
.orient('bottom')
.innerTickSize(-height)
.outerTickSize(1)
.tickPadding(10)
.ticks(width / 50);
this.yAxis = d3.svg.axis()
.scale(this.scaleY)
.orient('left')
.innerTickSize(-width)
.outerTickSize(1)
.tickPadding(10)
.ticks(height / 20);
this.elXAxis = this.svg.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(${marginLeft}, ${height})`)
.call(this.xAxis);
this.elYAxis = this.svg.append('g')
.attr('class', 'y axis')
.attr('transform', `translate(${marginLeft}, 0)`)
.call(this.yAxis);
}
setProfile (profile) {
this.render();
}
addPoints (pointcloud, points) {
//this.lastAddPointsTimestamp = new Date().getTime();
let entry = this.pointclouds.get(pointcloud);
if(!entry){
entry = new ProfilePointCloudEntry();
this.pointclouds.set(pointcloud, entry);
let materialChanged = () => this.render();
pointcloud.material.addEventListener('material_property_changed', materialChanged);
this.addEventListener("on_reset_once", () => {
pointcloud.material.removeEventListener('material_property_changed', materialChanged);
});
}
entry.addPoints(points);
this.pointCloudRoot.add(entry.sceneNode);
this.projectedBox.union(entry.projectedBox);
//console.log(this.projectedBox.min.toArray().map(v => v.toFixed(2)).join(", "));
//console.log(this.projectedBox.getSize().toArray().map(v => v.toFixed(2)).join(", "));
if (this.autoFit) {
let width = this.renderArea[0].clientWidth;
let height = this.renderArea[0].clientHeight;
let size = this.projectedBox.getSize();
let sx = width / size.x;
let sy = height / size.z;
let scale = Math.min(sx, sy);
let center = this.projectedBox.getCenter();
this.scale.set(scale, scale, 1);
this.camera.position.copy(center);
//console.log("camera: ", this.camera.position.toArray().join(", "));
}
//console.log(entry);
this.render();
let numPoints = 0;
for (let [key, value] of this.pointclouds.entries()) {
numPoints += value.points.reduce( (a, i) => a + i.numPoints, 0);
}
$(`#profile_num_points`).html(Potree.utils.addCommas(numPoints));
}
reset () {
this.lastReset = new Date().getTime();
this.dispatchEvent({type: "on_reset_once"});
this.removeEventListeners("on_reset_once");
this.autoFit = true;
this.projectedBox = new THREE.Box3();
for(let [key, entry] of this.pointclouds){
entry.dispose();
}
this.pointclouds.clear();
this.mouseIsDown = false;
this.mouse.set(0, 0);
this.scale.set(1, 1, 1);
this.pickSphere.visible = false;
this.pointCloudRoot.children = [];
this.elRoot.find('#profileSelectionProperties').hide();
this.render();
}
show () {
this.elRoot.fadeIn();
this.enabled = true;
}
hide () {
this.elRoot.fadeOut();
this.enabled = false;
}
updateScales () {
let width = this.renderArea[0].clientWidth;
let height = this.renderArea[0].clientHeight;
let left = (-width / 2) / this.scale.x;
let right = (+width / 2) / this.scale.x;
let top = (+height / 2) / this.scale.y;
let bottom = (-height / 2) / this.scale.y;
this.camera.left = left;
this.camera.right = right;
this.camera.top = top;
this.camera.bottom = bottom;
this.camera.updateProjectionMatrix();
this.scaleX.domain([this.camera.left + this.camera.position.x, this.camera.right + this.camera.position.x])
.range([0, width]);
this.scaleY.domain([this.camera.bottom + this.camera.position.z, this.camera.top + this.camera.position.z])
.range([height, 0]);
let marginLeft = this.renderArea[0].offsetLeft;
this.xAxis.scale(this.scaleX)
.orient('bottom')
.innerTickSize(-height)
.outerTickSize(1)
.tickPadding(10)
.ticks(width / 50);
this.yAxis.scale(this.scaleY)
.orient('left')
.innerTickSize(-width)
.outerTickSize(1)
.tickPadding(10)
.ticks(height / 20);
this.elXAxis
.attr('transform', `translate(${marginLeft}, ${height})`)
.call(this.xAxis);
this.elYAxis
.attr('transform', `translate(${marginLeft}, 0)`)
.call(this.yAxis);
}
requestScaleUpdate(){
let threshold = 100;
let allowUpdate = ((this.lastReset === undefined) || (this.lastScaleUpdate === undefined))
|| ((new Date().getTime() - this.lastReset) > threshold && (new Date().getTime() - this.lastScaleUpdate) > threshold);
if(allowUpdate){
this.updateScales();
this.lastScaleUpdate = new Date().getTime();
this.scaleUpdatePending = false;
}else if(!this.scaleUpdatePending) {
setTimeout(this.requestScaleUpdate.bind(this), 100);
this.scaleUpdatePending = true;
}
}
render () {
let width = this.renderArea[0].clientWidth;
let height = this.renderArea[0].clientHeight;
//this.updateScales();
{ // THREEJS
let radius = Math.abs(this.scaleX.invert(0) - this.scaleX.invert(5));
this.pickSphere.scale.set(radius, radius, radius);
//this.pickSphere.position.z = this.camera.far - radius;
//this.pickSphere.position.y = 0;
for (let [pointcloud, entry] of this.pointclouds) {
let material = entry.material;
material.pointColorType = pointcloud.material.pointColorType;
material.uniforms.uColor = pointcloud.material.uniforms.uColor;
material.uniforms.intensityRange.value = pointcloud.material.uniforms.intensityRange.value;
material.elevationRange = pointcloud.material.elevationRange;
material.rgbGamma = pointcloud.material.rgbGamma;
material.rgbContrast = pointcloud.material.rgbContrast;
material.rgbBrightness = pointcloud.material.rgbBrightness;
material.intensityRange = pointcloud.material.intensityRange;
material.intensityGamma = pointcloud.material.intensityGamma;
material.intensityContrast = pointcloud.material.intensityContrast;
material.intensityBrightness = pointcloud.material.intensityBrightness;
material.uniforms.wRGB.value = pointcloud.material.uniforms.wRGB.value;
material.uniforms.wIntensity.value = pointcloud.material.uniforms.wIntensity.value;
material.uniforms.wElevation.value = pointcloud.material.uniforms.wElevation.value;
material.uniforms.wClassification.value = pointcloud.material.uniforms.wClassification.value;
material.uniforms.wReturnNumber.value = pointcloud.material.uniforms.wReturnNumber.value;
material.uniforms.wSourceID.value = pointcloud.material.uniforms.wSourceID.value;
}
this.pickSphere.visible = true;
this.renderer.setSize(width, height);
this.renderer.render(this.scene, this.camera);
}
this.requestScaleUpdate();
}
};
Potree.ProfileWindowController = class ProfileWindowController {
constructor (viewer) {
this.viewer = viewer;
this.profileWindow = viewer.profileWindow;
this.profile = null;
this.numPoints = 0;
this.threshold = 60 * 1000;
this.scheduledRecomputeTime = null;
this.enabled = true;
this.requests = [];
this._recompute = () => { this.recompute(); };
this.viewer.addEventListener("scene_changed", e => {
e.oldScene.removeEventListener("pointcloud_added", this._recompute);
e.scene.addEventListener("pointcloud_added", this._recompute);
});
this.viewer.scene.addEventListener("pointcloud_added", this._recompute);
}
setProfile (profile) {
if (this.profile !== null && this.profile !== profile) {
this.profile.removeEventListener('marker_moved', this._recompute);
this.profile.removeEventListener('marker_added', this._recompute);
this.profile.removeEventListener('marker_removed', this._recompute);
this.profile.removeEventListener('width_changed', this._recompute);
}
this.profile = profile;
{
this.profile.addEventListener('marker_moved', this._recompute);
this.profile.addEventListener('marker_added', this._recompute);
this.profile.addEventListener('marker_removed', this._recompute);
this.profile.addEventListener('width_changed', this._recompute);
}
this.recompute();
}
reset () {
this.profileWindow.reset();
this.numPoints = 0;
if (this.profile) {
for (let request of this.requests) {
request.cancel();
}
}
}
progressHandler (pointcloud, progress) {
for (let segment of progress.segments) {
this.profileWindow.addPoints(pointcloud, segment.points);
this.numPoints += segment.points.numPoints;
}
}
cancel () {
for (let request of this.requests) {
request.cancel();
// request.finishLevelThenCancel();
}
this.requests = [];
};
finishLevelThenCancel(){
for (let request of this.requests) {
request.finishLevelThenCancel();
}
this.requests = [];
}
recompute () {
if (!this.profile) {
return;
}
if (this.scheduledRecomputeTime !== null && this.scheduledRecomputeTime > new Date().getTime()) {
return;
} else {
this.scheduledRecomputeTime = new Date().getTime() + 100;
}
this.scheduledRecomputeTime = null;
this.reset();
for (let pointcloud of this.viewer.scene.pointclouds.filter(p => p.visible)) {
let request = pointcloud.getPointsInProfile(this.profile, null, {
'onProgress': (event) => {
if (!this.enabled) {
return;
}
this.progressHandler(pointcloud, event.points);
if (this.numPoints > this.threshold) {
this.finishLevelThenCancel();
}
},
'onFinish': (event) => {
if (!this.enabled) {
}
},
'onCancel': () => {
if (!this.enabled) {
}
}
});
this.requests.push(request);
}
}
};
// http://epsg.io/
proj4.defs('UTM10N', '+proj=utm +zone=10 +ellps=GRS80 +datum=NAD83 +units=m +no_defs');
Potree.MapView = class {
constructor (viewer) {
this.viewer = viewer;
this.webMapService = 'WMTS';
this.mapProjectionName = 'EPSG:3857';
this.mapProjection = proj4.defs(this.mapProjectionName);
this.sceneProjection = null;
this.extentsLayer = null;
this.cameraLayer = null;
this.toolLayer = null;
this.sourcesLayer = null;
this.sourcesLabelLayer = null;
this.enabled = false;
this.createAnnotationStyle = (text) => {
return [
new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: [255, 255, 255, 0.5],
width: 2
}),
fill: new ol.style.Fill({
color: [0, 0, 0, 0.5]
})
})
})/*,
new ol.style.Style({
text: new ol.style.Text({
font: '12px helvetica,sans-serif',
text: text,
fill: new ol.style.Fill({
color: '#000'
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2
})
})
}) */
];
};
this.createLabelStyle = (text) => {
let style = new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
stroke: new ol.style.Stroke({
color: 'white',
width: 2
}),
fill: new ol.style.Fill({
color: 'green'
})
}),
text: new ol.style.Text({
font: '12px helvetica,sans-serif',
text: text,
fill: new ol.style.Fill({
color: '#000'
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2
})
})
});
return style;
};
}
showSources (show) {
this.sourcesLayer.setVisible(show);
this.sourcesLabelLayer.setVisible(show);
}
init () {
this.elMap = $('#potree_map');
this.elMap.draggable({ handle: $('#potree_map_header') });
this.elMap.resizable();
this.elTooltip = $(``);
this.elMap.append(this.elTooltip);
let extentsLayer = this.getExtentsLayer();
let cameraLayer = this.getCameraLayer();
this.getToolLayer();
let sourcesLayer = this.getSourcesLayer();
this.getSourcesLabelLayer();
this.getAnnotationsLayer();
let mousePositionControl = new ol.control.MousePosition({
coordinateFormat: ol.coordinate.createStringXY(5),
projection: 'EPSG:4326',
undefinedHTML: ' '
});
let _this = this;
let DownloadSelectionControl = function (optOptions) {
let options = optOptions || {};
// TOGGLE TILES
let btToggleTiles = document.createElement('button');
btToggleTiles.innerHTML = 'T';
btToggleTiles.addEventListener('click', () => {
let visible = sourcesLayer.getVisible();
_this.showSources(!visible);
}, false);
btToggleTiles.style.float = 'left';
btToggleTiles.title = 'show / hide tiles';
// DOWNLOAD SELECTED TILES
let link = document.createElement('a');
link.href = '#';
link.download = 'list.txt';
link.style.float = 'left';
let button = document.createElement('button');
button.innerHTML = 'D';
link.appendChild(button);
let handleDownload = (e) => {
let features = selectedFeatures.getArray();
let url = [document.location.protocol, '//', document.location.host, document.location.pathname].join('');
if (features.length === 0) {
alert('No tiles were selected. Select area with ctrl + left mouse button!');
e.preventDefault();
e.stopImmediatePropagation();
return false;
} else if (features.length === 1) {
let feature = features[0];
if (feature.source) {
let cloudjsurl = feature.pointcloud.pcoGeometry.url;
let sourceurl = new URL(url + '/../' + cloudjsurl + '/../source/' + feature.source.name);
link.href = sourceurl.href;
link.download = feature.source.name;
}
} else {
let content = '';
for (let i = 0; i < features.length; i++) {
let feature = features[i];
if (feature.source) {
let cloudjsurl = feature.pointcloud.pcoGeometry.url;
let sourceurl = new URL(url + '/../' + cloudjsurl + '/../source/' + feature.source.name);
content += sourceurl.href + '\n';
}
}
let uri = 'data:application/octet-stream;base64,' + btoa(content);
link.href = uri;
link.download = 'list_of_files.txt';
}
};
button.addEventListener('click', handleDownload, false);
// assemble container
let element = document.createElement('div');
element.className = 'ol-unselectable ol-control';
element.appendChild(link);
element.appendChild(btToggleTiles);
element.style.bottom = '0.5em';
element.style.left = '0.5em';
element.title = 'Download file or list of selected tiles. Select tile with left mouse button or area using ctrl + left mouse.';
ol.control.Control.call(this, {
element: element,
target: options.target
});
};
ol.inherits(DownloadSelectionControl, ol.control.Control);
this.map = new ol.Map({
controls: ol.control.defaults({
attributionOptions: ({
collapsible: false
})
}).extend([
// this.controls.zoomToExtent,
new DownloadSelectionControl(),
mousePositionControl
]),
layers: [
new ol.layer.Tile({source: new ol.source.OSM()}),
this.toolLayer,
this.annotationsLayer,
this.sourcesLayer,
this.sourcesLabelLayer,
extentsLayer,
cameraLayer
],
target: 'potree_map_content',
view: new ol.View({
center: this.olCenter,
zoom: 9
})
});
// DRAGBOX / SELECTION
this.dragBoxLayer = new ol.layer.Vector({
source: new ol.source.Vector({}),
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 255, 1)',
width: 2
})
})
});
this.map.addLayer(this.dragBoxLayer);
let select = new ol.interaction.Select();
this.map.addInteraction(select);
let selectedFeatures = select.getFeatures();
let dragBox = new ol.interaction.DragBox({
condition: ol.events.condition.platformModifierKeyOnly
});
this.map.addInteraction(dragBox);
this.map.on('pointermove', evt => {
let pixel = evt.pixel;
let feature = this.map.forEachFeatureAtPixel(pixel, function (feature) {
return feature;
});
// console.log(feature);
// this.elTooltip.css("display", feature ? '' : 'none');
this.elTooltip.css('display', 'none');
if (feature && feature.onHover) {
feature.onHover(evt);
// overlay.setPosition(evt.coordinate);
// tooltip.innerHTML = feature.get('name');
}
});
this.map.on('click', evt => {
let pixel = evt.pixel;
let feature = this.map.forEachFeatureAtPixel(pixel, function (feature) {
return feature;
});
if (feature && feature.onHover) {
feature.onClick(evt);
}
});
dragBox.on('boxend', (e) => {
// features that intersect the box are added to the collection of
// selected features, and their names are displayed in the "info"
// div
let extent = dragBox.getGeometry().getExtent();
this.getSourcesLayer().getSource().forEachFeatureIntersectingExtent(extent, (feature) => {
selectedFeatures.push(feature);
});
});
// clear selection when drawing a new box and when clicking on the map
dragBox.on('boxstart', (e) => {
selectedFeatures.clear();
});
this.map.on('click', () => {
selectedFeatures.clear();
});
this.viewer.addEventListener('scene_changed', e => {
this.setScene(e.scene);
});
this.onPointcloudAdded = e => {
this.load(e.pointcloud);
};
this.onAnnotationAdded = e => {
if (!this.sceneProjection) {
return;
}
let annotation = e.annotation;
let position = annotation.position;
let mapPos = this.toMap.forward([position.x, position.y]);
let feature = new ol.Feature({
geometry: new ol.geom.Point(mapPos),
name: annotation.title
});
feature.setStyle(this.createAnnotationStyle(annotation.title));
feature.onHover = evt => {
let coordinates = feature.getGeometry().getCoordinates();
let p = this.map.getPixelFromCoordinate(coordinates);
this.elTooltip.html(annotation.title);
this.elTooltip.css('display', '');
this.elTooltip.css('left', `${p[0]}px`);
this.elTooltip.css('top', `${p[1]}px`);
};
feature.onClick = evt => {
annotation.clickTitle();
};
this.getAnnotationsLayer().getSource().addFeature(feature);
};
this.setScene(this.viewer.scene);
}
setScene (scene) {
if (this.scene === scene) {
return;
};
if (this.scene) {
this.scene.removeEventListener('pointcloud_added', this.onPointcloudAdded);
this.scene.annotations.removeEventListener('annotation_added', this.onAnnotationAdded);
}
this.scene = scene;
this.scene.addEventListener('pointcloud_added', this.onPointcloudAdded);
this.scene.annotations.addEventListener('annotation_added', this.onAnnotationAdded);
for (let pointcloud of this.viewer.scene.pointclouds) {
this.load(pointcloud);
}
this.viewer.scene.annotations.traverseDescendants(annotation => {
this.onAnnotationAdded({annotation: annotation});
});
}
getExtentsLayer () {
if (this.extentsLayer) {
return this.extentsLayer;
}
this.gExtent = new ol.geom.LineString([[0, 0], [0, 0]]);
let feature = new ol.Feature(this.gExtent);
let featureVector = new ol.source.Vector({
features: [feature]
});
this.extentsLayer = new ol.layer.Vector({
source: featureVector,
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new ol.style.Stroke({
color: '#0000ff',
width: 2
}),
image: new ol.style.Circle({
radius: 3,
fill: new ol.style.Fill({
color: '#0000ff'
})
})
})
});
return this.extentsLayer;
}
getAnnotationsLayer () {
if (this.annotationsLayer) {
return this.annotationsLayer;
}
this.annotationsLayer = new ol.layer.Vector({
source: new ol.source.Vector({
}),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 0, 0, 1)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(255, 0, 0, 1)',
width: 2
})
})
});
return this.annotationsLayer;
}
getCameraLayer () {
if (this.cameraLayer) {
return this.cameraLayer;
}
// CAMERA LAYER
this.gCamera = new ol.geom.LineString([[0, 0], [0, 0], [0, 0], [0, 0]]);
let feature = new ol.Feature(this.gCamera);
let featureVector = new ol.source.Vector({
features: [feature]
});
this.cameraLayer = new ol.layer.Vector({
source: featureVector,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#0000ff',
width: 2
})
})
});
return this.cameraLayer;
}
getToolLayer () {
if (this.toolLayer) {
return this.toolLayer;
}
this.toolLayer = new ol.layer.Vector({
source: new ol.source.Vector({
}),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 0, 0, 1)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(255, 0, 0, 1)',
width: 2
})
})
});
return this.toolLayer;
}
getSourcesLayer () {
if (this.sourcesLayer) {
return this.sourcesLayer;
}
this.sourcesLayer = new ol.layer.Vector({
source: new ol.source.Vector({}),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(0, 0, 150, 0.1)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 150, 1)',
width: 1
})
})
});
return this.sourcesLayer;
}
getSourcesLabelLayer () {
if (this.sourcesLabelLayer) {
return this.sourcesLabelLayer;
}
this.sourcesLabelLayer = new ol.layer.Vector({
source: new ol.source.Vector({
}),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 0, 0, 0.1)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(255, 0, 0, 1)',
width: 2
})
}),
minResolution: 0.01,
maxResolution: 20
});
return this.sourcesLabelLayer;
}
setSceneProjection (sceneProjection) {
this.sceneProjection = sceneProjection;
this.toMap = proj4(this.sceneProjection, this.mapProjection);
this.toScene = proj4(this.mapProjection, this.sceneProjection);
};
getMapExtent () {
let bb = this.viewer.getBoundingBox();
let bottomLeft = this.toMap.forward([bb.min.x, bb.min.y]);
let bottomRight = this.toMap.forward([bb.max.x, bb.min.y]);
let topRight = this.toMap.forward([bb.max.x, bb.max.y]);
let topLeft = this.toMap.forward([bb.min.x, bb.max.y]);
let extent = {
bottomLeft: bottomLeft,
bottomRight: bottomRight,
topRight: topRight,
topLeft: topLeft
};
return extent;
};
getMapCenter () {
let mapExtent = this.getMapExtent();
let mapCenter = [
(mapExtent.bottomLeft[0] + mapExtent.topRight[0]) / 2,
(mapExtent.bottomLeft[1] + mapExtent.topRight[1]) / 2
];
return mapCenter;
};
updateToolDrawings () {
this.toolLayer.getSource().clear();
let profiles = this.viewer.profileTool.profiles;
for (let i = 0; i < profiles.length; i++) {
let profile = profiles[i];
let coordinates = [];
for (let j = 0; j < profile.points.length; j++) {
let point = profile.points[j];
let pointMap = this.toMap.forward([point.x, point.y]);
coordinates.push(pointMap);
}
let line = new ol.geom.LineString(coordinates);
let feature = new ol.Feature(line);
this.toolLayer.getSource().addFeature(feature);
}
let measurements = this.viewer.measuringTool.measurements;
for (let i = 0; i < measurements.length; i++) {
let measurement = measurements[i];
let coordinates = [];
for (let j = 0; j < measurement.points.length; j++) {
let point = measurement.points[j].position;
let pointMap = this.toMap.forward([point.x, point.y]);
coordinates.push(pointMap);
}
if (measurement.closed && measurement.points.length > 0) {
coordinates.push(coordinates[0]);
}
let line = new ol.geom.LineString(coordinates);
let feature = new ol.Feature(line);
this.toolLayer.getSource().addFeature(feature);
}
}
load (pointcloud) {
if (!pointcloud) {
return;
}
if (!pointcloud.projection) {
return;
}
if (!this.sceneProjection) {
this.setSceneProjection(pointcloud.projection);
}
let mapExtent = this.getMapExtent();
let mapCenter = this.getMapCenter();
let view = this.map.getView();
view.setCenter(mapCenter);
this.gExtent.setCoordinates([
mapExtent.bottomLeft,
mapExtent.bottomRight,
mapExtent.topRight,
mapExtent.topLeft,
mapExtent.bottomLeft
]);
view.fit(this.gExtent, [300, 300], {
constrainResolution: false
});
let url = pointcloud.pcoGeometry.url + '/../sources.json';
$.getJSON(url, (data) => {
let sources = data.sources;
for (let i = 0; i < sources.length; i++) {
let source = sources[i];
let name = source.name;
let bounds = source.bounds;
let mapBounds = {
min: this.toMap.forward([bounds.min[0], bounds.min[1]]),
max: this.toMap.forward([bounds.max[0], bounds.max[1]])
};
let mapCenter = [
(mapBounds.min[0] + mapBounds.max[0]) / 2,
(mapBounds.min[1] + mapBounds.max[1]) / 2
];
let p1 = this.toMap.forward([bounds.min[0], bounds.min[1]]);
let p2 = this.toMap.forward([bounds.max[0], bounds.min[1]]);
let p3 = this.toMap.forward([bounds.max[0], bounds.max[1]]);
let p4 = this.toMap.forward([bounds.min[0], bounds.max[1]]);
// let feature = new ol.Feature({
// 'geometry': new ol.geom.LineString([p1, p2, p3, p4, p1])
// });
let feature = new ol.Feature({
'geometry': new ol.geom.Polygon([[p1, p2, p3, p4, p1]])
});
feature.source = source;
feature.pointcloud = pointcloud;
this.getSourcesLayer().getSource().addFeature(feature);
feature = new ol.Feature({
geometry: new ol.geom.Point(mapCenter),
name: name
});
feature.setStyle(this.createLabelStyle(name));
this.sourcesLabelLayer.getSource().addFeature(feature);
}
});
}
toggle () {
if (this.elMap.is(':visible')) {
this.elMap.css('display', 'none');
this.enabled = false;
} else {
this.elMap.css('display', 'block');
this.enabled = true;
}
}
update (delta) {
if (!this.sceneProjection) {
return;
}
let pm = $('#potree_map');
if (!this.enabled) {
return;
}
// resize
let mapSize = this.map.getSize();
let resized = (pm.width() !== mapSize[0] || pm.height() !== mapSize[1]);
if (resized) {
this.map.updateSize();
}
//
let camera = this.viewer.scene.getActiveCamera();
let scale = this.map.getView().getResolution();
let campos = camera.position;
let camdir = camera.getWorldDirection();
let sceneLookAt = camdir.clone().multiplyScalar(30 * scale).add(campos);
let geoPos = camera.position;
let geoLookAt = sceneLookAt;
let mapPos = new THREE.Vector2().fromArray(this.toMap.forward([geoPos.x, geoPos.y]));
let mapLookAt = new THREE.Vector2().fromArray(this.toMap.forward([geoLookAt.x, geoLookAt.y]));
let mapDir = new THREE.Vector2().subVectors(mapLookAt, mapPos).normalize();
mapLookAt = mapPos.clone().add(mapDir.clone().multiplyScalar(30 * scale));
let mapLength = mapPos.distanceTo(mapLookAt);
let mapSide = new THREE.Vector2(-mapDir.y, mapDir.x);
let p1 = mapPos.toArray();
let p2 = mapLookAt.clone().sub(mapSide.clone().multiplyScalar(0.3 * mapLength)).toArray();
let p3 = mapLookAt.clone().add(mapSide.clone().multiplyScalar(0.3 * mapLength)).toArray();
this.gCamera.setCoordinates([p1, p2, p3, p1]);
}
get sourcesVisible () {
return this.getSourcesLayer().getVisible();
}
set sourcesVisible (value) {
this.getSourcesLayer().setVisible(value);
}
};
initSidebar = (viewer) => {
let createToolIcon = function (icon, title, callback) {
let element = $(`
`);
element.click(callback);
return element;
};
let measuringTool = new Potree.MeasuringTool(viewer);
let profileTool = new Potree.ProfileTool(viewer);
let volumeTool = new Potree.VolumeTool(viewer);
function initToolbar () {
// ANGLE
let elToolbar = $('#tools');
elToolbar.append(createToolIcon(
Potree.resourcePath + '/icons/angle.png',
'[title]tt.angle_measurement',
function () {
$('#menu_measurements').next().slideDown();
let measurement = measuringTool.startInsertion({
showDistances: false,
showAngles: true,
showArea: false,
closed: true,
maxMarkers: 3,
name: 'Angle'});
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
// POINT
elToolbar.append(createToolIcon(
Potree.resourcePath + '/icons/point.svg',
'[title]tt.point_measurement',
function () {
$('#menu_measurements').next().slideDown();
let measurement = measuringTool.startInsertion({
showDistances: false,
showAngles: false,
showCoordinates: true,
showArea: false,
closed: true,
maxMarkers: 1,
name: 'Point'});
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
// DISTANCE
elToolbar.append(createToolIcon(
Potree.resourcePath + '/icons/distance.svg',
'[title]tt.distance_measurement',
function () {
$('#menu_measurements').next().slideDown();
let measurement = measuringTool.startInsertion({
showDistances: true,
showArea: false,
closed: false,
name: 'Distance'});
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
// HEIGHT
elToolbar.append(createToolIcon(
Potree.resourcePath + '/icons/height.svg',
'[title]tt.height_measurement',
function () {
$('#menu_measurements').next().slideDown();
let measurement = measuringTool.startInsertion({
showDistances: false,
showHeight: true,
showArea: false,
closed: false,
maxMarkers: 2,
name: 'Height'});
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
// AREA
elToolbar.append(createToolIcon(
Potree.resourcePath + '/icons/area.svg',
'[title]tt.area_measurement',
function () {
$('#menu_measurements').next().slideDown();
let measurement = measuringTool.startInsertion({
showDistances: true,
showArea: true,
closed: true,
name: 'Area'});
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
// VOLUME
elToolbar.append(createToolIcon(
Potree.resourcePath + '/icons/volume.svg',
'[title]tt.volume_measurement',
function () {
let volume = volumeTool.startInsertion();
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === volume.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
// PROFILE
elToolbar.append(createToolIcon(
Potree.resourcePath + '/icons/profile.svg',
'[title]tt.height_profile',
function () {
$('#menu_measurements').next().slideDown(); ;
let profile = profileTool.startInsertion();
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === profile.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
// REMOVE ALL
elToolbar.append(createToolIcon(
Potree.resourcePath + '/icons/reset_tools.svg',
'[title]tt.remove_all_measurement',
function () {
viewer.scene.removeAllMeasurements();
}
));
}
function initScene(){
let elScene = $("#menu_scene");
let elObjects = elScene.next().find("#scene_objects");
let elProperties = elScene.next().find("#scene_object_properties");
{
let elExport = elScene.next().find("#scene_export");
let geoJSONIcon = `${Potree.resourcePath}/icons/file_geojson.svg`;
let dxfIcon = `${Potree.resourcePath}/icons/file_dxf.svg`;
elExport.append(`
Export:
`);
let elDownloadJSON = elExport.find("img[name=geojson_export_button]").parent();
elDownloadJSON.click( () => {
let scene = viewer.scene;
let measurements = [...scene.measurements, ...scene.profiles, ...scene.volumes];
let geoJson = Potree.GeoJSONExporter.toString(measurements);
let url = window.URL.createObjectURL(new Blob([geoJson], {type: 'data:application/octet-stream'}));
elDownloadJSON.attr('href', url);
});
let elDownloadDXF = elExport.find("img[name=dxf_export_button]").parent();
elDownloadDXF.click( () => {
let scene = viewer.scene;
let measurements = [...scene.measurements, ...scene.profiles, ...scene.volumes];
let dxf = Potree.DXFExporter.toString(measurements);
let url = window.URL.createObjectURL(new Blob([dxf], {type: 'data:application/octet-stream'}));
elDownloadDXF.attr('href', url);
});
}
let propertiesPanel = new Potree.PropertiesPanel(elProperties, viewer);
propertiesPanel.setScene(viewer.scene);
localStorage.removeItem('jstree');
let tree = $(``);
elObjects.append(tree);
tree.jstree({
'plugins': ["checkbox", "state"],
'core': {
"dblclick_toggle": false,
"state": {
"checked" : true
},
'check_callback': true,
"expand_selected_onload": true
},
"checkbox" : {
"keep_selected_style": true,
"three_state": false,
"whole_node": false,
"tie_selection": false,
},
});
let createNode = (parent, text, icon, object) => {
let nodeID = tree.jstree('create_node', parent, {
"text": text,
"icon": icon,
"data": object
},
"last", false, false);
if(object.visible){
tree.jstree('check_node', nodeID);
}else{
tree.jstree('uncheck_node', nodeID);
}
return nodeID;
}
let pcID = tree.jstree('create_node', "#", { "text": "Point Clouds", "id": "pointclouds"}, "last", false, false);
let measurementID = tree.jstree('create_node', "#", { "text": "Measurements", "id": "measurements" }, "last", false, false);
let annotationsID = tree.jstree('create_node', "#", { "text": "Annotations", "id": "annotations" }, "last", false, false);
let otherID = tree.jstree('create_node', "#", { "text": "Other", "id": "other" }, "last", false, false);
tree.jstree("check_node", pcID);
tree.jstree("check_node", measurementID);
tree.jstree("check_node", annotationsID);
tree.jstree("check_node", otherID);
tree.on('create_node.jstree', function(e, data){
tree.jstree("open_all");
});
tree.on("select_node.jstree", function(e, data){
let object = data.node.data;
propertiesPanel.set(object);
viewer.inputHandler.deselectAll();
if(object instanceof Potree.Volume){
viewer.inputHandler.toggleSelection(object);
}
$(viewer.renderer.domElement).focus();
});
tree.on("deselect_node.jstree", function(e, data){
propertiesPanel.set(null);
});
tree.on("delete_node.jstree", function(e, data){
propertiesPanel.set(null);
});
tree.on('dblclick','.jstree-anchor', function (e) {
let instance = $.jstree.reference(this);
let node = instance.get_node(this);
let object = node.data;
// ignore double click on checkbox
if(e.target.classList.contains("jstree-checkbox")){
return;
}
if(object instanceof Potree.PointCloudTree){
let box = viewer.getBoundingBox([object]);
let node = new THREE.Object3D();
node.boundingBox = box;
viewer.zoomTo(node, 1, 500);
}else if(object instanceof Potree.Measure){
let points = object.points.map(p => p.position);
let box = new THREE.Box3().setFromPoints(points);
if(box.getSize().length() > 0){
let node = new THREE.Object3D();
node.boundingBox = box;
viewer.zoomTo(node, 2, 500);
}
}else if(object instanceof Potree.Profile){
let points = object.points;
let box = new THREE.Box3().setFromPoints(points);
if(box.getSize().length() > 0){
let node = new THREE.Object3D();
node.boundingBox = box;
viewer.zoomTo(node, 1, 500);
}
}else if(object instanceof Potree.Volume){
let box = object.boundingBox.clone().applyMatrix4(object.matrixWorld);
if(box.getSize().length() > 0){
let node = new THREE.Object3D();
node.boundingBox = box;
viewer.zoomTo(node, 1, 500);
}
}else if(object instanceof Potree.Annotation){
object.moveHere(viewer.scene.getActiveCamera());
}else if(object instanceof Potree.PolygonClipVolume){
let dir = object.camera.getWorldDirection();
let target;
if(object.camera instanceof THREE.OrthographicCamera){
dir.multiplyScalar(object.camera.right)
target = new THREE.Vector3().addVectors(object.camera.position, dir);
viewer.setCameraMode(Potree.CameraMode.ORTHOGRAPHIC);
}else if(object.camera instanceof THREE.PerspectiveCamera){
dir.multiplyScalar(viewer.scene.view.radius);
target = new THREE.Vector3().addVectors(object.camera.position, dir);
viewer.setCameraMode(Potree.CameraMode.PERSPECTIVE);
}
viewer.scene.view.position.copy(object.camera.position);
viewer.scene.view.lookAt(target);
}else if(object instanceof THREE.SpotLight){
let distance = (object.distance > 0) ? object.distance / 4 : 5 * 1000;
let position = object.position;
let target = new THREE.Vector3().addVectors(
position,
object.getWorldDirection().multiplyScalar(distance));
viewer.scene.view.position.copy(object.position);
viewer.scene.view.lookAt(target);
}else if(object instanceof THREE.Object3D){
let box = new THREE.Box3().setFromObject(object);
if(box.getSize().length() > 0){
let node = new THREE.Object3D();
node.boundingBox = box;
viewer.zoomTo(node, 1, 500);
}
}
});
tree.on("uncheck_node.jstree", function(e, data){
let object = data.node.data;
if(object){
object.visible = false;
}
});
tree.on("check_node.jstree", function(e, data){
let object = data.node.data;
if(object){
object.visible = true;
}
});
let onPointCloudAdded = (e) => {
let pointcloud = e.pointcloud;
let cloudIcon = `${Potree.resourcePath}/icons/cloud.svg`;
createNode(pcID, pointcloud.name, cloudIcon, pointcloud);
};
let onMeasurementAdded = (e) => {
let measurement = e.measurement;
let icon = Potree.getMeasurementIcon(measurement);
createNode(measurementID, measurement.name, icon, measurement);
};
let onVolumeAdded = (e) => {
let volume = e.volume;
let icon = Potree.getMeasurementIcon(volume);
let node = createNode(measurementID, volume.name, icon, volume);
volume.addEventListener("visibility_changed", () => {
if(volume.visible){
tree.jstree('check_node', node);
}else{
tree.jstree('uncheck_node', node);
}
});
};
let onProfileAdded = (e) => {
let profile = e.profile;
let icon = Potree.getMeasurementIcon(profile);
createNode(measurementID, profile.name, icon, profile);
};
let onAnnotationAdded = (e) => {
let annotation = e.annotation;
let annotationIcon = `${Potree.resourcePath}/icons/annotation.svg`;
let parentID = this.annotationMapping.get(annotation.parent);
let annotationID = createNode(parentID, annotation.title, annotationIcon, annotation);
this.annotationMapping.set(annotation, annotationID);
//let node = createNode(annotationsID, annotation.name, icon, volume);
//oldScene.annotations.removeEventListener('annotation_added', this.onAnnotationAdded);
};
viewer.scene.addEventListener("pointcloud_added", onPointCloudAdded);
viewer.scene.addEventListener("measurement_added", onMeasurementAdded);
viewer.scene.addEventListener("profile_added", onProfileAdded);
viewer.scene.addEventListener("volume_added", onVolumeAdded);
viewer.scene.addEventListener("polygon_clip_volume_added", onVolumeAdded);
viewer.scene.annotations.addEventListener("annotation_added", onAnnotationAdded);
let onMeasurementRemoved = (e) => {
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.measurement.uuid);
tree.jstree("delete_node", jsonNode.id);
};
let onVolumeRemoved = (e) => {
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.volume.uuid);
tree.jstree("delete_node", jsonNode.id);
};
let onProfileRemoved = (e) => {
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.profile.uuid);
tree.jstree("delete_node", jsonNode.id);
};
viewer.scene.addEventListener("measurement_removed", onMeasurementRemoved);
viewer.scene.addEventListener("volume_removed", onVolumeRemoved);
viewer.scene.addEventListener("profile_removed", onProfileRemoved);
{
let annotationIcon = `${Potree.resourcePath}/icons/annotation.svg`;
this.annotationMapping = new Map();
this.annotationMapping.set(viewer.scene.annotations, annotationsID);
viewer.scene.annotations.traverseDescendants(annotation => {
let parentID = this.annotationMapping.get(annotation.parent);
let annotationID = createNode(parentID, annotation.title, annotationIcon, annotation);
this.annotationMapping.set(annotation, annotationID);
});
}
for(let pointcloud of viewer.scene.pointclouds){
onPointCloudAdded({pointcloud: pointcloud});
}
for(let measurement of viewer.scene.measurements){
onMeasurementAdded({measurement: measurement});
}
for(let volume of [...viewer.scene.volumes, ...viewer.scene.polygonClipVolumes]){
onVolumeAdded({volume: volume});
}
for(let profile of viewer.scene.profiles){
onProfileAdded({profile: profile});
}
{
createNode(otherID, "Camera", null, new THREE.Camera());
}
viewer.addEventListener("scene_changed", (e) => {
propertiesPanel.setScene(e.scene);
e.oldScene.removeEventListener("pointcloud_added", onPointCloudAdded);
e.oldScene.removeEventListener("measurement_added", onMeasurementAdded);
e.oldScene.removeEventListener("profile_added", onProfileAdded);
e.oldScene.removeEventListener("volume_added", onVolumeAdded);
e.oldScene.removeEventListener("polygon_clip_volume_added", onVolumeAdded);
e.oldScene.removeEventListener("measurement_removed", onMeasurementRemoved);
e.scene.addEventListener("pointcloud_added", onPointCloudAdded);
e.scene.addEventListener("measurement_added", onMeasurementAdded);
e.scene.addEventListener("profile_added", onProfileAdded);
e.scene.addEventListener("volume_added", onVolumeAdded);
e.scene.addEventListener("polygon_clip_volume_added", onVolumeAdded);
e.scene.addEventListener("measurement_removed", onMeasurementRemoved);
});
}
function initClippingTool() {
viewer.addEventListener("cliptask_changed", function(event){
console.log("TODO");
});
viewer.addEventListener("clipmethod_changed", function(event){
console.log("TODO");
});
{
let elClipTask = $("#cliptask_options");
elClipTask.selectgroup({title: "Clip Task"});
elClipTask.find("input").click( (e) => {
viewer.setClipTask(Potree.ClipTask[e.target.value]);
});
let currentClipTask = Object.keys(Potree.ClipTask)
.filter(key => Potree.ClipTask[key] === viewer.clipTask);
elClipTask.find(`input[value=${currentClipTask}]`).trigger("click");
}
{
let elClipMethod = $("#clipmethod_options");
elClipMethod.selectgroup({title: "Clip Method"});
elClipMethod.find("input").click( (e) => {
viewer.setClipMethod(Potree.ClipMethod[e.target.value]);
});
let currentClipMethod = Object.keys(Potree.ClipMethod)
.filter(key => Potree.ClipMethod[key] === viewer.clipMethod);
elClipMethod.find(`input[value=${currentClipMethod}]`).trigger("click");
}
let clippingToolBar = $("#clipping_tools");
// CLIP VOLUME
clippingToolBar.append(createToolIcon(
Potree.resourcePath + '/icons/clip_volume.svg',
'[title]tt.clip_volume',
() => {
let item = volumeTool.startInsertion({clip: true});
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === item.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
// CLIP POLYGON
clippingToolBar.append(createToolIcon(
Potree.resourcePath + "/icons/clip-polygon.svg",
"[title]tt.clip_polygon",
() => {
let item = viewer.clippingTool.startInsertion({type: "polygon"});
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === item.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
{// SCREEN BOX SELECT
let boxSelectTool = new Potree.ScreenBoxSelectTool(viewer);
clippingToolBar.append(createToolIcon(
Potree.resourcePath + "/icons/clip-screen.svg",
"[title]tt.screen_clip_box",
() => {
if(!(viewer.scene.getActiveCamera() instanceof THREE.OrthographicCamera)){
viewer.postMessage(`Switch to Orthographic Camera Mode before using the Screen-Box-Select tool.`,
{duration: 2000});
return;
}
let item = boxSelectTool.startInsertion();
let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
let jsonNode = measurementsRoot.children.find(child => child.data.uuid === item.uuid);
$.jstree.reference(jsonNode.id).deselect_all();
$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
}
));
}
{ // REMOVE CLIPPING TOOLS
clippingToolBar.append(createToolIcon(
Potree.resourcePath + "/icons/remove.svg",
"[title]tt.remove_all_measurement",
() => {
viewer.scene.removeAllClipVolumes();
}
));
}
}
function initClassificationList () {
let elClassificationList = $('#classificationList');
let addClassificationItem = function (code, name) {
let inputID = 'chkClassification_' + code;
let element = $(`
`);
let copyIconPath = Potree.resourcePath + '/icons/copy.svg';
for (let point of points) {
let x = Potree.utils.addCommas(point.x.toFixed(3));
let y = Potree.utils.addCommas(point.y.toFixed(3));
let z = Potree.utils.addCommas(point.z.toFixed(3));
let row = $(`
${x}
${y}
${z}
`);
this.elCopy = row.find("img[name=copy]");
this.elCopy.click( () => {
let msg = point.toArray().map(c => c.toFixed(3)).join(", ");
Potree.utils.clipboardCopy(msg);
this.viewer.postMessage(
`Copied value to clipboard: '${msg}'`,
{duration: 3000});
});
table.append(row);
}
return table;
};
createAttributesTable(){
let elTable = $('
');
let point = this.measurement.points[0];
if(point.color){
let color = point.color;
let text = color.join(', ');
elTable.append($(`