Skip to content

Commit

Permalink
Merge pull request #22 from TDT4290-group-4/9-coastline-overlay
Browse files Browse the repository at this point in the history
#9 Coastline overlay
  • Loading branch information
adriahso authored Oct 6, 2025
2 parents 9861b48 + 66aa934 commit c32efa6
Show file tree
Hide file tree
Showing 8 changed files with 319 additions and 50 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Molloy Explorer is a 3D seabed visualization tool built with **Potree**. It allo

### Add point cloud data

Place the point cloud data (in Potree format) in `public/pointclouds/data_converted`.
Place the point cloud data (in Potree format with EPSG:4978 coordinates) in `public/pointclouds/data_converted`.

**Note:** Point cloud files should not be committed to Git.

Expand Down
64 changes: 17 additions & 47 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
type="text/css"
href="/libs/jstree/themes/mixed/style.css"
/>
<link
rel="stylesheet"
type="text/css"
href="/libs/Cesium/Widgets/CesiumWidget/CesiumWidget.css"
/>
<link rel="stylesheet" type="text/css" href="src/style.css" />
</head>

Expand All @@ -40,60 +45,25 @@
<script src="/build/potree/potree.js"></script>
<script src="/libs/plasio/js/laslaz.js"></script>
<script src="/libs/three.js/build/three.js"></script>
<script src="/libs/Cesium/Cesium.js"></script>

<div
class="potree_container"
style="position: absolute; width: 100%; height: 100%; left: 0px; top: 0px"
style="
position: absolute;
width: 100%;
height: 100%;
left: 0px;
top: 0px;
background-color: black;
"
>
<div
id="potree_render_area"
style="
background-image: url('/build/potree/resources/images/background.jpg');
"
></div>
<div id="potree_render_area">
<div id="cesiumContainer"></div>
</div>
<div id="potree_sidebar_container"></div>
</div>

<script src="src/ElevationControl/elevationControl.js"></script>
<script type="module">
window.viewer = new Potree.Viewer(
document.getElementById('potree_render_area')
)

viewer.setEDLEnabled(true)
viewer.setFOV(60)
viewer.setPointBudget(5_000_000)
viewer.loadSettingsFromURL()

viewer.setDescription('Molloy Explorer')

viewer.loadGUI(() => {
viewer.setLanguage('en')
$('#menu_appearance').next().show()
$('#menu_tools').next().show()
$('#menu_scene').next().show()
$('#menu_filters').next().show()
viewer.toggleSidebar()

if (window.initElevationControls) {
window.initElevationControls(viewer)
}
})

let url = './pointclouds/data_converted/metadata.json'
Potree.loadPointCloud(url).then((e) => {
let pointcloud = e.pointcloud
let material = pointcloud.material

material.pointSizeType = Potree.PointSizeType.ADAPTIVE
material.shape = Potree.PointShape.CIRCLE
material.activeAttributeName = 'elevation'
material.gradient = Potree.Gradients['VIRIDIS']

viewer.scene.addPointCloud(pointcloud)
viewer.fitToScreen()
})
</script>
<script type="module" src="src/main.js"></script>
</body>
</html>
10 changes: 8 additions & 2 deletions src/ElevationControl/elevationControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ function rebindElevationLabel() {
label.textContent = `${low.toFixed(2)} to ${high.toFixed(2)}`
}

// Adjust slider limits
slider.slider({
min: -10000,
max: 0,
values: [-10000, 0]
})

//clear any old namespaced handlers and attach fresh ones
slider.off('slide.custom slidestop.custom change.custom')
slider.on('slide.custom', update)
Expand All @@ -81,8 +88,7 @@ function moveElevationContainer() {
}

//initiate and orchestrate all funcitons to render the Evelation control section of the sidebar propperly
window.initElevationControls = function initElevationControls(viewer) {

export function initElevationControls(viewer) {
//Creates the section
createElevationPanel(viewer)

Expand Down
101 changes: 101 additions & 0 deletions src/cameraSync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Syncs Potree's point cloud with Cesium's globe.
*
* @param potreeViewer - used for point cloud
* @param cesiumViewer - used for globe
*/
export function syncCameras(potreeViewer, cesiumViewer) {
const camera = potreeViewer.scene.getActiveCamera()

// Compute camera position, up vector, and target (pivot) in world coordinates
const pPos = new THREE.Vector3(0, 0, 0).applyMatrix4(camera.matrixWorld)
const pUp = new THREE.Vector3(0, 600, 0).applyMatrix4(camera.matrixWorld)
const pTarget = potreeViewer.scene.view.getPivot()

const toCes = (v) => new Cesium.Cartesian3(v.x, v.y, v.z)

const cPos = toCes(pPos)
const cUpTarget = toCes(pUp)
const cTarget = toCes(pTarget)

// Compute Cesium camera direction and up vectors
const cDir = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(cTarget, cPos, new Cesium.Cartesian3()),
new Cesium.Cartesian3()
)
const cUp = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(cUpTarget, cPos, new Cesium.Cartesian3()),
new Cesium.Cartesian3()
)

// Hide globe when the camera is below the surface, blocked by the curvature of the Earth or directly above the pivot
const showGlobe = shouldShowGlobe(cPos, cTarget, cDir)
cesiumViewer.scene.globe.show = showGlobe
cesiumViewer.scene.skyAtmosphere.show = showGlobe

// Sync Cesium camera position and orientation with Potree
cesiumViewer.camera.setView({
destination: cPos,
orientation: {
direction: cDir,
up: cUp
}
})

// Match FOV
const aspect = camera.aspect
const fovy = Math.PI * (camera.fov / 180)
if (aspect < 1) {
cesiumViewer.camera.frustum.fov = fovy
} else {
const fovx = Math.atan(Math.tan(0.5 * fovy) * aspect) * 2
cesiumViewer.camera.frustum.fov = fovx
}
}

/**
* Determines whether the globe should be visible based on the camera position.
*
* Returns false if the camera is below the globe surface, if the pivot
* point would be blocked by the curvature of the Earth or if the camera
* is looking almost straight down at the pivot.
*
* @param cameraPos - The camera position in Cesium.Cartesian3 coordinates
* @param pivot - The pivot point in Cesium.Cartesian3 coordinates
* @param direction - The camera direction as a Cesium.Cartesian3 unit vector
* @returns true if the globe should be visible, false if it should be hidden
*/
function shouldShowGlobe(cameraPos, pivot, direction) {
const ellipsoid = Cesium.Ellipsoid.WGS84
const earthCenter = Cesium.Cartesian3.ZERO

// Get point on globe surface directly above the pivot
const carto = Cesium.Cartographic.fromCartesian(pivot)
const pivotSurface = Cesium.Cartesian3.fromRadians(
carto.longitude,
carto.latitude,
0,
ellipsoid
)

// Axis vector from Earth center through pivot
const axis = Cesium.Cartesian3.subtract(
pivotSurface,
earthCenter,
new Cesium.Cartesian3()
)
Cesium.Cartesian3.normalize(axis, axis)

// Project camera and pivot onto this axis
const camProj = Cesium.Cartesian3.dot(cameraPos, axis)
const pivotProj = Cesium.Cartesian3.dot(pivotSurface, axis)

// Compute the dot product between camera direction and local vertical
// Used to detect if the camera is looking almost straight down
const targetNormal = Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(pivot)
const dotProduct = Math.abs(Cesium.Cartesian3.dot(direction, targetNormal))

// If camera is "above" pivot on the axis, and not looking nearly straight down, the globe should be visible
// Otherwise, the globe should not be visible
return camProj >= pivotProj && dotProduct < 0.99
}
26 changes: 26 additions & 0 deletions src/cesiumViewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Initializes the Cesium viewer used to visualize the globe.
*
* @param containerId - id of the container
* @returns Cesium viewer
*/
export function createCesiumViewer(containerId) {
const viewer = new Cesium.Viewer(containerId, {
useDefaultRenderLoop: false,
animation: false,
baseLayerPicker: false,
fullscreenButton: false,
geocoder: false,
homeButton: false,
infoBox: false,
sceneModePicker: false,
selectionIndicator: false,
timeline: false,
navigationHelpButton: false,
imageryProvider: Cesium.createOpenStreetMapImageryProvider({
url: 'https://a.tile.openstreetmap.org/'
}),
terrainShadows: Cesium.ShadowMode.DISABLED
})
return viewer
}
7 changes: 7 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const POTREE_POINTCLOUD_URL = '/pointclouds/data_converted/metadata.json'

export const POTREE_SETTINGS = {
edl: true,
fov: 60,
pointBudget: 1_000_000
}
26 changes: 26 additions & 0 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { POTREE_POINTCLOUD_URL, POTREE_SETTINGS } from './config.js'
import { createCesiumViewer } from './cesiumViewer.js'
import { createPotreeViewer } from './potreeViewer.js'
import { syncCameras } from './cameraSync.js'

async function init() {
window.cesiumViewer = createCesiumViewer('cesiumContainer')

window.potreeViewer = await createPotreeViewer(
'potree_render_area',
POTREE_POINTCLOUD_URL,
POTREE_SETTINGS
)

function loop(timestamp) {
requestAnimationFrame(loop)
potreeViewer.update(potreeViewer.clock.getDelta(), timestamp)
potreeViewer.render()
syncCameras(potreeViewer, cesiumViewer)
cesiumViewer.render()
}

requestAnimationFrame(loop)
}

init()
Loading

0 comments on commit c32efa6

Please sign in to comment.