Skip to content

7 elevation range control #20

Merged
merged 4 commits into from
Oct 1, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
type="text/css"
href="/libs/jstree/themes/mixed/style.css"
/>
<link rel="stylesheet" type="text/css" href="src/style.css" />
<link rel="stylesheet" type="text/css" href="src/elevation_control.css" />
</head>

<body>
Expand Down Expand Up @@ -67,6 +69,7 @@

viewer.loadGUI(() => {
viewer.setLanguage('en')
$('#menu_customised').next().show()
$('#menu_appearance').next().show()
$('#menu_tools').next().show()
$('#menu_scene').next().show()
Expand All @@ -82,11 +85,13 @@
material.pointSizeType = Potree.PointSizeType.ADAPTIVE
material.shape = Potree.PointShape.CIRCLE
material.activeAttributeName = 'elevation'
material.gradient = Potree.Gradients['RAINBOW']
material.gradient = Potree.Gradients['VIRIDIS']

viewer.scene.addPointCloud(pointcloud)
viewer.fitToScreen()
})
</script>
<script type="module" src="src/main.js"></script>
<script type="module" src="src/elevation_control.js"></script>
</body>
</html>
11 changes: 11 additions & 0 deletions src/elevation_control.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* making sure that the divider span the whole sidebar (as the original code) */
#customised_list > .divider {
margin: 0;
padding: 0;
}

/* Padding of list items in the elevation control section as the original code*/
#customised_list li {
list-style-type: none;
padding: 0 20px;
}
138 changes: 138 additions & 0 deletions src/elevation_control.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* Creating and inserting a new customised "Elevation Range" section in the sidebar above Apperance
* with the same css style features as Apperance
*/
(function () {

//Insert new the sidebar above Apperance
function insertCustomisedAboveAppearance() {
const appearanceHeader = document.querySelector('#menu_appearance');
if (!appearanceHeader) return false;
if (document.querySelector('#menu_customised_header')) return true;

//Clone the header for identical style to apperance
const customHeader = appearanceHeader.cloneNode(true);
customHeader.removeAttribute('id');
customHeader.id = 'menu_customised_header';
customHeader.textContent = 'Elevation Control';
customHeader.style.cursor = 'pointer';
customHeader.setAttribute('tabindex', '0');

//Creating a new "div" in the customised section similar to the Apperance div
const appearanceBody = appearanceHeader.nextElementSibling;
const bodyClass = appearanceBody ? appearanceBody.className : 'pv-menu-list';
const customBody = document.createElement('div');
customBody.className = bodyClass;
customBody.id = 'customised_list';
customBody.style.display = ''; // start expanded

//Insert both right before Appearance
appearanceHeader.parentElement.insertBefore(customHeader, appearanceHeader);
appearanceHeader.parentElement.insertBefore(customBody, appearanceHeader);

//collapse/expand
const toggle = () => {
const hidden = customBody.style.display === 'none';
customBody.style.display = hidden ? '' : 'none';
};
customHeader.addEventListener('click', toggle);
customHeader.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); }
});

return true;
}

function init() {
if (insertCustomisedAboveAppearance()) return;
let tries = 0;
const t = setInterval(() => {
tries++;
if (insertCustomisedAboveAppearance() || tries > 50) clearInterval(t);
}, 200);
}

if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();



/**
* A function that:
* - Autoselects the first pointcloud
* - Move the Elevation block from the Scene section into the custimosed Elevation Control section
*
* Nice to know: In the oroginal code the Elevation section in the Properties panel is only built when the pointcloud is clicked on
*/
(function () {
const $ = (sel, root = document) => root.querySelector(sel);

//Select the fist pointcloud in the sidebar so that the Elevation section is built
function autoSelectFirstPointCloud() {
const cloudIcon = document.querySelector('#scene_objects i.jstree-themeicon-custom');
if (cloudIcon) {
cloudIcon.dispatchEvent(new MouseEvent('click', { bubbles: true }));
return true;
}
return false;
}

//(re)connect the elevation labels to the slider after the container is moved (was not handled by default)
function rebindElevationLabel() {
const slider = window.jQuery ? window.jQuery("#sldHeightRange") : null;
const label = document.getElementById("lblHeightRange");
if (!slider || !slider.length || !label) return;

const update = () => {
const low = $slider.slider("values", 0);
const high = $slider.slider("values", 1);
label.textContent = `${low.toFixed(2)} to ${high.toFixed(2)}`;
};

//clear any old namespaced handlers and attach fresh ones
slider.off("slide.custom slidestop.custom change.custom");
slider.on("slide.custom", update);
slider.on("slidestop.custom change.custom", update);
update();
}

//Move the elevation range section to the customised "Elevation Control" section
function moveElevationContainer() {
const target = $('#customised_list');
const elevationContainer = document.querySelector('#materials\\.elevation_container');
if (!elevationContainer) return false;
target.appendChild(elevationContainer);
rebindElevationLabel();
return true;

}

function init() {
let tries = 0;
const t = setInterval(() => {
const hasCloud = !!(window.viewer?.scene?.pointclouds?.length);
if (hasCloud) {
autoSelectFirstPointCloud();
// Wait until potree builds the Properties, then move the container
let innerTries = 0;
const t2 = setInterval(() => {
const movedElevation = moveElevationContainer();
if (movedElevation || ++innerTries > 100) clearInterval(t2);
}, 100);

clearInterval(t);
}
if (++tries > 30) clearInterval(t);
}, 100);
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();



1 change: 0 additions & 1 deletion src/main.js
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
/* Empty for now, add logic later */
1 change: 0 additions & 1 deletion src/style.css
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
/* Empty for now, add styles later */