This commit is contained in:
2026-05-11 23:43:22 +02:00
parent b682c7b324
commit 270ea2b771
3 changed files with 129 additions and 73 deletions

134
main.js
View File

@@ -1,4 +1,11 @@
import maplibregl from "maplibre-gl"; import maplibregl from "maplibre-gl";
import proj4 from "proj4";
import * as THREE from "three";
console.log(THREE);
proj4.defs(
"EPSG:25832",
"+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs"
);
const center = [9.209116842757239, 52.26520546238239] const center = [9.209116842757239, 52.26520546238239]
@@ -28,7 +35,6 @@ viewer.setPointBudget(3*1000*1000);
viewer.setMinNodeSize(0); viewer.setMinNodeSize(0);
viewer.setBackground("none"); viewer.setBackground("none");
//viewer.setControls(null);
//Dateipfade der Punktwolken //Dateipfade der Punktwolken
const pointCloudFiles={ const pointCloudFiles={
@@ -37,6 +43,8 @@ const pointCloudFiles={
second: 'standpunkt2/metadata.json', second: 'standpunkt2/metadata.json',
third: 'punktwolke_3_converted/metadata.json', third: 'punktwolke_3_converted/metadata.json',
} }
//Aktuelle Datei und Sichtbarkeit //Aktuelle Datei und Sichtbarkeit
let currentPointCloud=null; let currentPointCloud=null;
let isVisible=true; let isVisible=true;
@@ -70,59 +78,99 @@ function loadPointCloud(path){
}); });
} }
// Geo-Koordinaten der Punktwolke (müssen zu euren Daten passen!)
// Das ist der Georeferenzpunkt idealerweise aus den Metadaten der Punktwolke
const CLOUD_GEO_ORIGIN = {
lng: 9.209116842757239,
lat: 52.26520546238239
};
// Hilfsfunktion: MapLibre Mercator → Meter-Offset relativ zum Ursprung
function lngLatToMeters(lng, lat, originLng, originLat) {
const R = 6378137; // Erdradius in Metern (WGS84)
const dLng = (lng - originLng) * Math.PI / 180;
const dLat = (lat - originLat) * Math.PI / 180;
const dx = R * dLng * Math.cos(originLat * Math.PI / 180);
const dy = R * dLat;
return { x: dx, y: dy };
}
function syncCamera() { function syncCamera() {
if (!currentPointCloud) return; if (!currentPointCloud) return;
const mapCenter = map.getCenter(); const center = map.getCenter();
const zoom = map.getZoom();
const pitch = map.getPitch(); // Grad, 0 = top-down
const bearing = map.getBearing(); // Grad
const box = currentPointCloud.boundingBox; // -----------------------------------------
const cx = (box.min.x + box.max.x) / 2; // WGS84 -> ETRS89 / UTM32
const cy = (box.min.y + box.max.y) / 2; // -----------------------------------------
const cz = (box.min.z + box.max.z) / 2;
// Radius aus Zoom ableiten (Abstand Kamera → Target) const [x, y] = proj4(
const earthCircumference = 2 * Math.PI * 6378137; "EPSG:4326",
const canvas = map.getCanvas(); "EPSG:25832",
const metersPerPixel = (earthCircumference * Math.cos(mapCenter.lat * Math.PI / 180)) [center.lng, center.lat]
/ (Math.pow(2, zoom) * 512); );
const radius = Math.pow(2,20 -zoom)*2;
// Potree View direkt setzen // -----------------------------------------
const view = viewer.scene.view; // Map Parameter
// -----------------------------------------
// Target = Wolken-Zentrum (lookAt mit 3 Argumenten nutzt new Vector3(...arguments)) const pitch = map.getPitch() * Math.PI / 180;
view.lookAt(cx, cy, cz); const bearing = map.getBearing() * Math.PI / 180;
const zoom = map.getZoom();
// yaw = Himmelsrichtung: MapLibre bearing 0° = Nord, Potree yaw 0° = ?? // -----------------------------------------
// bearing in Potree-yaw umrechnen (ggf. Vorzeichen anpassen) // Abstand abhängig vom Zoom
view.yaw = -bearing * Math.PI / 180; // -----------------------------------------
// pitch: MapLibre 0° = top-down, 60° = schräg const earthCircumference = 40075016.686;
// Potree pitch: 0 = horizontal, -PI/2 = top-down → invertieren
view.pitch = -(90 - pitch) * Math.PI / 180;
// Radius = Zoom-abhängiger Abstand const metersPerPixel =
view.radius = radius; earthCircumference *
Math.cos(center.lat * Math.PI / 180) /
Math.pow(2, zoom + 8);
const distance =
metersPerPixel * map.getCanvas().height;
// -----------------------------------------
// Zielpunkt = Kartenzentrum
// -----------------------------------------
const target = new THREE.Vector3(
x,
y,
0
);
// -----------------------------------------
// Kameraoffset
// -----------------------------------------
const offset = new THREE.Vector3(
0,
-distance * Math.cos(pitch),
distance * Math.sin(pitch)
);
// Bearing rotieren
offset.applyAxisAngle(
new THREE.Vector3(0, 0, 1),
-bearing
);
// -----------------------------------------
// Kameraposition
// -----------------------------------------
const cameraPosition =
target.clone().add(offset);
// -----------------------------------------
// Potree Kamera setzen
// -----------------------------------------
const camera =
viewer.scene.getActiveCamera();
camera.position.copy(cameraPosition);
camera.lookAt(target);
viewer.scene.view.position.copy(cameraPosition);
viewer.scene.view.lookAt(target);
camera.updateMatrixWorld();
viewer.renderer.render(
viewer.scene.scene,
camera
);
} }
map.on('move', ()=>{ map.on('move', ()=>{

66
package-lock.json generated
View File

@@ -13,6 +13,8 @@
"@loaders.gl/las": "^4.4.1", "@loaders.gl/las": "^4.4.1",
"deck.gl": "^9.3.1", "deck.gl": "^9.3.1",
"maplibre-gl": "^5.22.0", "maplibre-gl": "^5.22.0",
"proj4": "^2.20.8",
"three": "^0.184.0",
"vite": "^8.0.7" "vite": "^8.0.7"
} }
}, },
@@ -1380,9 +1382,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1399,9 +1398,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1418,9 +1414,6 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1437,9 +1430,6 @@
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1456,9 +1446,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1475,9 +1462,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4069,9 +4053,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4092,9 +4073,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4115,9 +4093,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4138,9 +4113,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4335,6 +4307,12 @@
"is-buffer": "~1.1.6" "is-buffer": "~1.1.6"
} }
}, },
"node_modules/mgrs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz",
"integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==",
"license": "MIT"
},
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
@@ -4582,6 +4560,19 @@
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/proj4": {
"version": "2.20.8",
"resolved": "https://registry.npmjs.org/proj4/-/proj4-2.20.8.tgz",
"integrity": "sha512-1C8sfT4xY4PAPwk0MroFBTGF4R4bzDXdmPQTGYVLsoNssrZ9odzObxS2dTeGBty8jW8KO7h16C1Hs2JP+ctfFw==",
"license": "MIT",
"dependencies": {
"mgrs": "1.0.0",
"wkt-parser": "^1.5.5"
},
"funding": {
"url": "https://github.com/sponsors/ahocevar"
}
},
"node_modules/protocol-buffers-schema": { "node_modules/protocol-buffers-schema": {
"version": "3.6.1", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz",
@@ -4924,6 +4915,12 @@
"texture-compressor": "bin/texture-compressor.js" "texture-compressor": "bin/texture-compressor.js"
} }
}, },
"node_modules/three": {
"version": "0.184.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.184.0.tgz",
"integrity": "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==",
"license": "MIT"
},
"node_modules/timezone-groups": { "node_modules/timezone-groups": {
"version": "0.10.4", "version": "0.10.4",
"resolved": "https://registry.npmjs.org/timezone-groups/-/timezone-groups-0.10.4.tgz", "resolved": "https://registry.npmjs.org/timezone-groups/-/timezone-groups-0.10.4.tgz",
@@ -5110,6 +5107,15 @@
} }
} }
}, },
"node_modules/wkt-parser": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.5.5.tgz",
"integrity": "sha512-/zMYi94/7D7fxcOSlVmWn6vnOMj3Gq5d1xvVjaYOS9n6h0qOJ4I7YYVxBWYcH1vq9+suhqzXkn05Yx47zQNUIA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ahocevar"
}
},
"node_modules/wordwrapjs": { "node_modules/wordwrapjs": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz",

View File

@@ -16,6 +16,8 @@
"@loaders.gl/las": "^4.4.1", "@loaders.gl/las": "^4.4.1",
"deck.gl": "^9.3.1", "deck.gl": "^9.3.1",
"maplibre-gl": "^5.22.0", "maplibre-gl": "^5.22.0",
"proj4": "^2.20.8",
"three": "^0.184.0",
"vite": "^8.0.7" "vite": "^8.0.7"
} }
} }