Compare commits
2 Commits
d6a98a4f05
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| da29cbeb7e | |||
| 267952ef71 |
Vendored
+3
-3
@@ -2,16 +2,16 @@
|
||||
"first": {
|
||||
"title": "Fläche im unteren Bereich",
|
||||
"text": "Dieser Bereich des Steinbruchs zeigt eine natürliche Oberfläche aus Obernkirchener Sandstein. Zwischen einzelnen Sandsteinblöcken haben sich Sträucher und andere Pflanzen angesiedelt. Der Sandstein entstand vor etwa 140 Millionen Jahren in einer küstennahen Landschaft der Unterkreide.",
|
||||
"images": ["stand1_1.jpeg", "stand1_2.jpeg"]
|
||||
"images": ["/stand1_1.jpeg", "/stand1_2.jpeg"]
|
||||
},
|
||||
"second": {
|
||||
"title": "Abbauwand",
|
||||
"text": "An dieser Felswand wird der Obernkirchener Sandstein abgebaut. Das Gestein ist für seine hohe Festigkeit und Witterungsbeständigkeit bekannt und wird seit Jahrhunderten als Bau- und Werkstein genutzt. Es findet sich unter anderem an zahlreichen historischen Gebäuden in Deutschland.",
|
||||
"images": ["stand2_1.jpeg"]
|
||||
"images": ["/stand2_1.jpeg"]
|
||||
},
|
||||
"third": {
|
||||
"title": "Dinosaurier-Spuren",
|
||||
"text": "Auf dieser öffentlich zugänglichen Fläche sind fossile Fußspuren von Dinosauriern erhalten geblieben. Die Spuren entstanden vor rund 140 Millionen Jahren in feuchtem Sand und wurden später durch weitere Sedimentschichten konserviert. Sie gehören zu den bekanntesten Fossilienfunden im Obernkirchener Sandstein.",
|
||||
"images": ["stand3_1.jpeg", "stand3_2.jpeg"]
|
||||
"images": ["/stand3_1.jpeg", "/stand3_2.jpeg"]
|
||||
}
|
||||
}
|
||||
+15
-27
@@ -3,22 +3,12 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>LiDAR App</title>
|
||||
<link
|
||||
href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css" rel="stylesheet"/>
|
||||
<link rel="stylesheet" type="text/css" href="./libs/potree/potree.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="./libs/jquery-ui/jquery-ui.min.css"
|
||||
/>
|
||||
<link rel="stylesheet" type="text/css" href="./libs/jquery-ui/jquery-ui.min.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<header id="header">
|
||||
@@ -26,8 +16,7 @@
|
||||
</header>
|
||||
<h1 id="title">- Obernkirchener Sandstein -</h1>
|
||||
<div id="coordinates" title="Koordinaten an Mausposition"></div>
|
||||
<div id="openButtonOuter">
|
||||
</div>
|
||||
<div id="openButtonOuter"></div>
|
||||
<div id="sideBarOuter">
|
||||
<aside id="sidebar">
|
||||
<button class="sideBarButtons" id="closeSideBarButton" title="Menü einklappen"><<</button>
|
||||
@@ -69,13 +58,15 @@
|
||||
<span>Klein</span>
|
||||
<span>Groß</span>
|
||||
</div>
|
||||
<input id="pointSizeSlider" type="range" min="1" max="10" value="2" step="1">
|
||||
<input id="pointSizeSlider" type="range" min="1" max="10" value="2" step="1"/>
|
||||
<p class="sideBarText">Renderer</p>
|
||||
<button id="rendererToggle" data-active="deckgl" title="Renderer wechseln">
|
||||
<span id="rendererLabel">Renderer: Deck.gl</span>
|
||||
<span id="rendererIcon">⇄</span>
|
||||
</button>
|
||||
<button id="openInfoPanel" title="Informationen zur Punktwolke"><img src="info.png"/>Informationen</button>
|
||||
<button id="openInfoPanel" title="Informationen zur Punktwolke">
|
||||
<img src="info.png" />Informationen
|
||||
</button>
|
||||
<div class="logos">
|
||||
<a href="http://www.jade-hs.de">
|
||||
<img id="logoJadeHs" src="logoJadeHs.png" />
|
||||
@@ -85,31 +76,28 @@
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
<main id="main">
|
||||
<div id="location-button">
|
||||
<img id="location" title="Standort zurücksetzen" src="standort.png" />
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
<div id="potree_render_area"></div>
|
||||
<footer>
|
||||
© Arne Zitting u. Hauke Kahrs
|
||||
</footer>
|
||||
<footer>© Arne Zitting u. Hauke Kahrs</footer>
|
||||
<div id="info-panel">
|
||||
<div id="info-panel-inner">
|
||||
<button class="sideBarButtons" id="closeInfoPanel" title="Info einklappen">>></button>
|
||||
<h3 id="info-panel-title">Info</h3>
|
||||
<img id="info-panel-image" src="" alt="Standortbild" style="display:none;" />
|
||||
<div id="info-image-nav" style="display:none;">
|
||||
<img id="info-panel-image" src="" alt="Standortbild" style="display: none"/>
|
||||
<div id="info-image-nav" style="display: none">
|
||||
<button id="info-img-prev">←</button>
|
||||
<span id="info-image-counter"></span>
|
||||
<button id="info-img-next">→</button>
|
||||
</div>
|
||||
<p id="info-panel-text">Bitte wählen Sie eine Punktwolke aus.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="info-button-outer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="info-button-outer"></div>
|
||||
</main>
|
||||
<script src="./libs/jquery/jquery-3.1.1.min.js"></script>
|
||||
<script src="./libs/other/BinaryHeap.js"></script>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
import maplibregl from "maplibre-gl";
|
||||
import proj4 from "proj4";
|
||||
import * as THREE from "three";
|
||||
@@ -5,15 +6,27 @@ import { LidarControl } from "maplibre-gl-lidar";
|
||||
import "maplibre-gl/dist/maplibre-gl.css";
|
||||
import "maplibre-gl-lidar/style.css";
|
||||
|
||||
// UTM Zone 32N für Koordinatentransformation registrieren
|
||||
proj4.defs("EPSG:25832", "+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs");
|
||||
|
||||
|
||||
// Koordinaten des Obernkirchener Sandstein
|
||||
// Koordinaten des Obernkirchener Sandstein (Kartenmittelpunkt)
|
||||
const center = [9.209116842757239, 52.26520546238239];
|
||||
|
||||
// die Grundkarte
|
||||
// Zustandsvariablen
|
||||
let currentPointCloud = null; // aktuell geladene Potree-Punktwolke
|
||||
let isVisible = true; // Punktwolke sichtbar?
|
||||
let currentRenderer = "deckgl"; // Default-Renderer deck.gl"
|
||||
let currentColorMode = "rgb"; // Farbmodus: "rgb" oder "elevation"
|
||||
let currentPointSize = Number(document.getElementById("pointSizeSlider").value);
|
||||
let currentPointcloudKey = null; // ausgewählte Punktwolke
|
||||
let lidarControl = null; // Deck.gl LidarControl-Instanz
|
||||
var quality = "medium"; // Qualitätsstufe
|
||||
|
||||
|
||||
//-------------------------------- Karten-Initialisierung --------------------------------------------------
|
||||
// Grundkarte: OpenFreeMap hell
|
||||
const map = new maplibregl.Map({
|
||||
container: 'map',
|
||||
container: "map",
|
||||
style: "https://tiles.openfreemap.org/styles/bright",
|
||||
center: center,
|
||||
zoom: 17,
|
||||
@@ -21,40 +34,27 @@ const map = new maplibregl.Map({
|
||||
});
|
||||
|
||||
// Karten-Bedienelemente
|
||||
map.addControl(new maplibregl.NavigationControl({
|
||||
map.addControl(
|
||||
new maplibregl.NavigationControl({
|
||||
visualizePitch: true,
|
||||
showZoom: true,
|
||||
showCompass: true
|
||||
}));
|
||||
|
||||
let currentPointCloud = null;
|
||||
let isVisible = true;
|
||||
let currentRenderer='deckgl';
|
||||
let currentColorMode='rgb';
|
||||
let currentPointSize=Number(document.getElementById("pointSizeSlider").value);
|
||||
let currentPointcloudKey = null;
|
||||
let lidarControl=null;
|
||||
var quality = 'medium';
|
||||
showCompass: true,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
// WICHTIG: Layer/Controls immer erst im 'load'-Event der Karte hinzufügen
|
||||
map.on('load', () => {
|
||||
// Karten Load-Event: LidarControl anlegen, JSON-Datei laden
|
||||
map.on("load", () => {
|
||||
lidarControl = new LidarControl({
|
||||
title: 'Mein LiDAR-Viewer',
|
||||
title: "LiDAR-Viewer",
|
||||
collapsed: false,
|
||||
pointSize: 2,
|
||||
colorScheme: 'rgb', // 'elevation' oder 'rgb'
|
||||
colorScheme: "rgb", // 'elevation' oder 'rgb'
|
||||
pointBudget: 3000000,
|
||||
maxRequests: 32,
|
||||
pickable: false,
|
||||
});
|
||||
|
||||
// COPC-Datei laden
|
||||
const selectBox=document.querySelector('select[name="pointcloud"]');
|
||||
if (selectBox && selectBox.value){
|
||||
currentPointcloudKey=selectBox.value;
|
||||
loadCurrentPointCloud();
|
||||
}
|
||||
loadInfoJSON();
|
||||
});
|
||||
|
||||
@@ -62,7 +62,7 @@ map.on('load', () => {
|
||||
//---------------Potree Eigenschaften--------------------------
|
||||
var pointSize = document.getElementById("pointSizeSlider").value;
|
||||
const elRenderArea = document.getElementById("potree_render_area");
|
||||
const viewer = new Potree.Viewer(elRenderArea, {noDragAndDrop: true});
|
||||
const viewer = new Potree.Viewer(elRenderArea, { noDragAndDrop: true });
|
||||
viewer.setEDLEnabled(false);
|
||||
viewer.setFOV(60);
|
||||
viewer.setMinNodeSize(pointSize);
|
||||
@@ -70,29 +70,35 @@ viewer.setBackground("none");
|
||||
viewer.orbitControls.enabled = false;
|
||||
viewer.fpControls.enabled = false;
|
||||
viewer.deviceControls.enabled = false;
|
||||
elRenderArea.style.display='none';
|
||||
viewer.setPointBudget(3000000)
|
||||
elRenderArea.style.display = "none";
|
||||
viewer.setPointBudget(3000000);
|
||||
|
||||
|
||||
//-------------------------- LIDARCONTROL (DECK.GL) NEU AUFBAUEN ----------------------------------------
|
||||
// entfernt das bestehende LidarControl und erzeugt es mit den aktuellen Einstellungen neu
|
||||
function resetLidarControl() {
|
||||
if (!lidarControl) return;
|
||||
if (!lidarControl)
|
||||
return;
|
||||
try {
|
||||
map.removeControl(lidarControl);
|
||||
} catch(e) {}
|
||||
} catch (e) {}
|
||||
lidarControl = null;
|
||||
|
||||
lidarControl = new LidarControl({
|
||||
title: 'Mein LiDAR-Viewer',
|
||||
title: "LiDAR-Viewer",
|
||||
collapsed: false,
|
||||
pointSize: currentPointSize > 0 ? currentPointSize : 2,
|
||||
colorScheme: currentColorMode === 'rgb' ? 'rgb' : 'elevation',
|
||||
colorScheme: currentColorMode === "rgb" ? "rgb" : "elevation",
|
||||
pointBudget: 3000000,
|
||||
maxRequests: 32,
|
||||
pickable: false
|
||||
pickable: false,
|
||||
});
|
||||
map.addControl(lidarControl, 'top-right');
|
||||
map.addControl(lidarControl, "top-right");
|
||||
}
|
||||
|
||||
|
||||
//-------------- PUNKTWOLKEN-DATENQUELLEN (Pfade je Renderer-Format) - aktuell auf Server der Jade-HS --------------------
|
||||
|
||||
// Punktwolkendaten im Potree-Format
|
||||
function getPointCloudFilesPOTREE() {
|
||||
return {
|
||||
@@ -112,114 +118,142 @@ function getPointCloudFilesCOPC() {
|
||||
}
|
||||
|
||||
|
||||
function loadCurrentPointCloud() {
|
||||
if (!currentPointcloudKey) return;
|
||||
if (currentRenderer === 'deckgl') {
|
||||
//---------------------------- PUNKTWOLKEN LADEN (je nach aktivem Renderer) -------------------------------------
|
||||
|
||||
// wählt automatisch Potree- oder Deck.gl-Renderer und lädt die Punktwolke
|
||||
function loadPointCloud() {
|
||||
if (!currentPointcloudKey)
|
||||
return;
|
||||
if (currentRenderer === "deckgl") {
|
||||
loadDeckGL(getPointCloudFilesCOPC()[currentPointcloudKey]);
|
||||
} else {
|
||||
loadPointCloud(getPointCloudFilesPOTREE()[currentPointcloudKey]);
|
||||
loadPointCloudPotree(getPointCloudFilesPOTREE()[currentPointcloudKey]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------- Punktwolken laden über den deck.gl-Renderer ----------------------
|
||||
function loadDeckGL(path) {
|
||||
if (!lidarControl || !path) return;
|
||||
if (!lidarControl || !path)
|
||||
return;
|
||||
resetLidarControl();
|
||||
lidarControl.loadPointCloudStreaming(path);
|
||||
setTimeout(() => applyDeckGLSettings(), 500);
|
||||
}
|
||||
|
||||
// Punktgröße, Farbschema und Colormap auf LidarControl anwenden
|
||||
function applyDeckGLSettings() {
|
||||
if (!lidarControl) return;
|
||||
if (!lidarControl)
|
||||
return;
|
||||
try {
|
||||
lidarControl.setPointSize(currentPointSize > 0 ? currentPointSize : 2);
|
||||
lidarControl.setColorScheme(currentColorMode === 'rgb' ? 'rgb' : 'elevation');
|
||||
lidarControl.setColormap('jet');
|
||||
//lidarControl.setPointBudget(3000000);
|
||||
} catch(e) { console.warn("LidarControl API:", e); }
|
||||
lidarControl.setColorScheme(currentColorMode === "rgb" ? "rgb" : "elevation",);
|
||||
lidarControl.setColormap("jet");
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
|
||||
//------------------------- Renderer zwischen deck.gl und potree umstellen -----------------------------------
|
||||
function switchRenderer(renderer) {
|
||||
currentRenderer = renderer;
|
||||
const slider = document.getElementById("pointSizeSlider");
|
||||
if (renderer === 'deckgl') {
|
||||
elRenderArea.style.display = 'none';
|
||||
if (currentPointCloud) currentPointCloud.visible = false;
|
||||
|
||||
if (renderer === "deckgl") {
|
||||
elRenderArea.style.display = "none";
|
||||
if (currentPointCloud){
|
||||
currentPointCloud.visible = false;
|
||||
}
|
||||
currentPointSize = 2;
|
||||
slider.min = 1;
|
||||
slider.max = 10;
|
||||
slider.value = 2;
|
||||
if (lidarControl && currentPointcloudKey) loadDeckGL(getPointCloudFilesCOPC()[currentPointcloudKey]);
|
||||
document.getElementById('rendererToggle').dataset.active = 'deckgl';
|
||||
document.getElementById('rendererLabel').textContent = 'Renderer: Deck.gl';
|
||||
if (lidarControl && currentPointcloudKey){
|
||||
loadDeckGL(getPointCloudFilesCOPC()[currentPointcloudKey]);
|
||||
}
|
||||
document.getElementById("rendererToggle").dataset.active = "deckgl";
|
||||
document.getElementById("rendererLabel").textContent = "Renderer: Deck.gl";
|
||||
} else {
|
||||
resetLidarControl();
|
||||
elRenderArea.style.display = 'block';
|
||||
elRenderArea.style.display = "block";
|
||||
|
||||
currentPointSize = 0;
|
||||
slider.min = 0;
|
||||
slider.max = 1000;
|
||||
slider.value = 0;
|
||||
|
||||
if (currentPointcloudKey) loadPointCloud(getPointCloudFilesPOTREE()[currentPointcloudKey]);
|
||||
document.getElementById('rendererToggle').dataset.active = 'potree';
|
||||
document.getElementById('rendererLabel').textContent = 'Renderer: Potree';
|
||||
if (currentPointcloudKey){
|
||||
loadPointCloudPotree(getPointCloudFilesPOTREE()[currentPointcloudKey]);
|
||||
}
|
||||
document.getElementById("rendererToggle").dataset.active = "potree";
|
||||
document.getElementById("rendererLabel").textContent = "Renderer: Potree";
|
||||
}
|
||||
applyVisibility();
|
||||
}
|
||||
|
||||
function applyVisibility() {
|
||||
|
||||
if (currentRenderer === 'potree' && currentPointCloud) {
|
||||
// Sichtbarkeit der Punktwolke je nach aktivem Renderer setzen
|
||||
function applyVisibility() {
|
||||
if (currentRenderer === "potree" && currentPointCloud) {
|
||||
currentPointCloud.visible = isVisible;
|
||||
}
|
||||
|
||||
if (currentRenderer === 'deckgl') {
|
||||
|
||||
if (currentRenderer === "deckgl") {
|
||||
// Trick: Punktgröße auf 0 setzen
|
||||
lidarControl.setPointSize(isVisible ? 2 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Farbmodus (rgb/elevation) auf die Potree-Punktwolke anwenden
|
||||
function applyColorModePotree() {
|
||||
if (!currentPointCloud) return;
|
||||
if (!currentPointCloud)
|
||||
return;
|
||||
let mat = currentPointCloud.material;
|
||||
mat.activeAttributeName = currentColorMode === 'rgb' ? "rgba" : "elevation";
|
||||
mat.activeAttributeName = currentColorMode === "rgb" ? "rgba" : "elevation";
|
||||
viewer.renderer.resetState();
|
||||
viewer.render();
|
||||
map.triggerRepaint();
|
||||
}
|
||||
|
||||
|
||||
// Farbmodus je nach aktivem Renderer anwenden
|
||||
function applyColorMode() {
|
||||
if (currentRenderer === 'potree') { applyColorModePotree(); }
|
||||
else { applyDeckGLSettings(); }
|
||||
if (currentRenderer === "potree") {
|
||||
applyColorModePotree();
|
||||
} else {
|
||||
applyDeckGLSettings();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------- INFO-JSON laden (Texte/Bilder zu den Punktwolken) -----------------
|
||||
function loadInfoJSON() {
|
||||
fetch("info.json")
|
||||
.then(r => r.json())
|
||||
.then(data => { pointCloudInfo = data; })
|
||||
.catch(e => console.error("Fehler beim Laden der JSON:", e));
|
||||
fetch("info.json").then((r) => r.json()).then((data) => {
|
||||
pointCloudInfo = data;
|
||||
})
|
||||
.catch((e) => console.error("Fehler beim Laden der JSON:", e));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------lädt Punktwolke im Potree Format-----------------------------
|
||||
function loadPointCloud(path) {
|
||||
//------------------------ Punktwolke im Potree Format laden -----------------------------
|
||||
function loadPointCloudPotree(path) {
|
||||
viewer.scene.view.yaw = 0;
|
||||
viewer.scene.view.pitch = 0;
|
||||
|
||||
// vorhandene Punktwolke aus der Szene entfernen
|
||||
if (currentPointCloud) {
|
||||
const index = viewer.scene.pointclouds.indexOf(currentPointCloud);
|
||||
if (index !== -1) viewer.scene.pointclouds.splice(index, 1);
|
||||
if (currentPointCloud.parent) currentPointCloud.parent.remove(currentPointCloud);
|
||||
if (index !== -1)
|
||||
viewer.scene.pointclouds.splice(index, 1);
|
||||
if (currentPointCloud.parent){
|
||||
currentPointCloud.parent.remove(currentPointCloud);
|
||||
}
|
||||
currentPointCloud = null;
|
||||
viewer.render();
|
||||
}
|
||||
|
||||
if (!path || !isVisible) return;
|
||||
if (!path || !isVisible)
|
||||
return;
|
||||
|
||||
Potree.loadPointCloud(path, "punktwolke", function(e) {
|
||||
Potree.loadPointCloud(path, "punktwolke", function (e) {
|
||||
currentPointCloud = e.pointcloud;
|
||||
viewer.scene.addPointCloud(currentPointCloud);
|
||||
|
||||
@@ -252,45 +286,34 @@ function loadPointCloud(path) {
|
||||
// Variable, um Endlosschleifen zu verhindern
|
||||
let isSyncing = false;
|
||||
|
||||
/*
|
||||
Kamera von Potree und MapLibre syncronisieren
|
||||
*/
|
||||
//------------------ Kamera von Potree und MapLibre syncronisieren ------------------------------------
|
||||
function syncCamera() {
|
||||
|
||||
if (!currentPointCloud || isSyncing || currentRenderer!== 'potree') return;
|
||||
if (!currentPointCloud || isSyncing || currentRenderer !== "potree")
|
||||
return;
|
||||
isSyncing = true;
|
||||
|
||||
const transform = map.transform;
|
||||
|
||||
// 1. Dimensionen auf den Pixel genau angleichen
|
||||
if (viewer.renderer) {
|
||||
viewer.renderer.setSize(map.getCanvas().clientWidth, map.getCanvas().clientHeight);
|
||||
viewer.renderer.setSize(map.getCanvas().clientWidth, map.getCanvas().clientHeight,);
|
||||
}
|
||||
|
||||
// 2. Mathematische Parameter aus dem MapLibre-Kern extrahieren
|
||||
const pitch = transform.pitch * Math.PI / 180;
|
||||
const bearing = transform.bearing * Math.PI / 180;
|
||||
|
||||
const pitch = (transform.pitch * Math.PI) / 180;
|
||||
const bearing = (transform.bearing * Math.PI) / 180;
|
||||
const mapCenter = map.getCenter();
|
||||
const [utmX, utmY] = proj4("EPSG:4326", "EPSG:25832", [mapCenter.lng, mapCenter.lat]);
|
||||
const [utmX, utmY] = proj4("EPSG:4326", "EPSG:25832", [mapCenter.lng, mapCenter.lat,]);
|
||||
|
||||
// WICHTIG: Nutze MapLibres exakte mathematische Kameradistanz
|
||||
// MapLibres exakte mathematische Kameradistanz nutzen
|
||||
const distanceInMeters = transform.cameraToCenterDistance / transform.pixelsPerMeter;
|
||||
|
||||
const target = new THREE.Vector3(utmX, utmY, 0);
|
||||
const offset = new THREE.Vector3(
|
||||
0,
|
||||
-distanceInMeters * Math.sin(pitch),
|
||||
distanceInMeters * Math.cos(pitch)
|
||||
);
|
||||
|
||||
const offset = new THREE.Vector3(0, -distanceInMeters * Math.sin(pitch), distanceInMeters * Math.cos(pitch),);
|
||||
offset.applyAxisAngle(new THREE.Vector3(0, 0, 1), -bearing);
|
||||
const cameraPosition = target.clone().add(offset);
|
||||
|
||||
// 3. Potree-Kamera absolut starr setzen
|
||||
viewer.scene.view.position.copy(cameraPosition);
|
||||
viewer.scene.view.lookAt(target);
|
||||
|
||||
viewer.setFOV(transform.fov);
|
||||
|
||||
// 4. Potree rendern
|
||||
@@ -300,156 +323,173 @@ function syncCamera() {
|
||||
isSyncing = false;
|
||||
}
|
||||
|
||||
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
||||
const hookRenderLoop = () => {
|
||||
// Erzwinge die Kamerasynchronisation bei jedem Karten-Update
|
||||
syncCamera();
|
||||
};
|
||||
|
||||
// Verwendet MapLibres internes Repaint-System, um absolut latenzfrei zu synchronisieren
|
||||
map.on('movestart', () => {
|
||||
map.getCanvasContainer().style.cursor = 'grabbing';
|
||||
// Kartenbewegungs-Events: Kamera-Synchronisation
|
||||
map.on("movestart", () => {
|
||||
map.getCanvasContainer().style.cursor = "grabbing";
|
||||
});
|
||||
|
||||
// bei jeder Kartenbwegeung die Kameras synchronisieren
|
||||
map.on('zoom', syncCamera);
|
||||
map.on('move', syncCamera);
|
||||
map.on('rotate', syncCamera);
|
||||
map.on('pitch', syncCamera);
|
||||
map.on('draw', syncCamera);
|
||||
// bei jeder Kartenbewegung die Kameras synchronisieren
|
||||
map.on("zoom", syncCamera);
|
||||
map.on("move", syncCamera);
|
||||
map.on("rotate", syncCamera);
|
||||
map.on("pitch", syncCamera);
|
||||
map.on("draw", syncCamera);
|
||||
|
||||
// Potree-Canvas soll keine Mausereignisse abfangen, nur die MapLibre-Canvas
|
||||
elRenderArea.style.pointerEvents = "none";
|
||||
map.getCanvas().style.pointerEvents = "auto";
|
||||
|
||||
|
||||
/*
|
||||
Hintergrundkarte ändern, Optionen: hell, dunkel, 3D-Gebäudemodelle, Satellit, Terrain, Satellit+Terrain
|
||||
*/
|
||||
//---------------------- Hintergrundkarte ändern ---------------------------------------------------
|
||||
function changeBaseMap(newMap) {
|
||||
var basemapStyle;
|
||||
switch(newMap) {
|
||||
switch (newMap) {
|
||||
// dunkle Karte
|
||||
case "openfree_dark":
|
||||
basemapStyle = "https://tiles.openfreemap.org/styles/dark";
|
||||
break;
|
||||
|
||||
// helle Karte
|
||||
case "openfree_bright":
|
||||
basemapStyle = "https://tiles.openfreemap.org/styles/bright";
|
||||
break;
|
||||
|
||||
// mit LOD1-GEbäudemodellen
|
||||
case "openfree_liberty":
|
||||
basemapStyle = "https://tiles.openfreemap.org/styles/liberty";
|
||||
break;
|
||||
|
||||
// Satellitenbild
|
||||
case "satellite":
|
||||
basemapStyle = {
|
||||
version: 8,
|
||||
sources: {
|
||||
"raster-tiles": {
|
||||
type: "raster",
|
||||
tiles: ["https://api.maptiler.com/tiles/satellite-v2/{z}/{x}/{y}.jpg?key=6mG881AthmTTWyLvFyjH"],
|
||||
tiles: [
|
||||
"https://api.maptiler.com/tiles/satellite-v2/{z}/{x}/{y}.jpg?key=6mG881AthmTTWyLvFyjH",
|
||||
],
|
||||
tileSize: 256,
|
||||
attribution: "© MapTiler"
|
||||
}
|
||||
attribution: "© MapTiler",
|
||||
},
|
||||
layers: [{
|
||||
},
|
||||
layers: [
|
||||
{
|
||||
id: "satellite-layer",
|
||||
type: "raster",
|
||||
source: "raster-tiles"
|
||||
}]
|
||||
source: "raster-tiles",
|
||||
},
|
||||
],
|
||||
};
|
||||
break;
|
||||
|
||||
// Gelände
|
||||
case "terrain":
|
||||
basemapStyle = {
|
||||
version: 8,
|
||||
sources: {
|
||||
topo: {
|
||||
type: 'raster',
|
||||
url: 'https://api.maptiler.com/maps/topo-v4/tiles.json?key=6mG881AthmTTWyLvFyjH',
|
||||
tileSize: 256
|
||||
type: "raster",
|
||||
url: "https://api.maptiler.com/maps/topo-v4/tiles.json?key=6mG881AthmTTWyLvFyjH",
|
||||
tileSize: 256,
|
||||
},
|
||||
terrainSource: {
|
||||
type: 'raster-dem',
|
||||
url: 'https://tiles.mapterhorn.com/tilejson.json'
|
||||
type: "raster-dem",
|
||||
url: "https://tiles.mapterhorn.com/tilejson.json",
|
||||
},
|
||||
hillshadeSource: {
|
||||
type: 'raster-dem',
|
||||
url: 'https://tiles.mapterhorn.com/tilejson.json'
|
||||
}
|
||||
type: "raster-dem",
|
||||
url: "https://tiles.mapterhorn.com/tilejson.json",
|
||||
},
|
||||
layers: [{
|
||||
id: 'topo',
|
||||
type: 'raster',
|
||||
source: 'topo'
|
||||
}, {
|
||||
id: 'hills',
|
||||
type: 'hillshade',
|
||||
source: 'hillshadeSource',
|
||||
},
|
||||
layers: [
|
||||
{
|
||||
id: "topo",
|
||||
type: "raster",
|
||||
source: "topo",
|
||||
},
|
||||
{
|
||||
id: "hills",
|
||||
type: "hillshade",
|
||||
source: "hillshadeSource",
|
||||
layout: {
|
||||
visibility: 'visible'
|
||||
visibility: "visible",
|
||||
},
|
||||
paint: {
|
||||
'hillshade-shadow-color': '#473B24'
|
||||
}
|
||||
}],
|
||||
terrain: {
|
||||
source: 'terrainSource',
|
||||
exaggeration: 1
|
||||
"hillshade-shadow-color": "#473B24",
|
||||
},
|
||||
sky: {}
|
||||
},
|
||||
],
|
||||
terrain: {
|
||||
source: "terrainSource",
|
||||
exaggeration: 1,
|
||||
},
|
||||
sky: {},
|
||||
};
|
||||
break;
|
||||
|
||||
// Gelände mit Satellitenbild
|
||||
case "satellite_terrain":
|
||||
basemapStyle = {
|
||||
version: 8,
|
||||
sources: {
|
||||
"raster-tiles": {
|
||||
type: "raster",
|
||||
tiles: ["https://api.maptiler.com/tiles/satellite-v2/{z}/{x}/{y}.jpg?key=6mG881AthmTTWyLvFyjH"],
|
||||
tiles: [
|
||||
"https://api.maptiler.com/tiles/satellite-v2/{z}/{x}/{y}.jpg?key=6mG881AthmTTWyLvFyjH",
|
||||
],
|
||||
tileSize: 256,
|
||||
attribution: "© MapTiler"
|
||||
attribution: "© MapTiler",
|
||||
},
|
||||
terrainSource: {
|
||||
type: 'raster-dem',
|
||||
url: 'https://tiles.mapterhorn.com/tilejson.json'
|
||||
type: "raster-dem",
|
||||
url: "https://tiles.mapterhorn.com/tilejson.json",
|
||||
},
|
||||
hillshadeSource: {
|
||||
type: 'raster-dem',
|
||||
url: 'https://tiles.mapterhorn.com/tilejson.json'
|
||||
}
|
||||
type: "raster-dem",
|
||||
url: "https://tiles.mapterhorn.com/tilejson.json",
|
||||
},
|
||||
layers: [{
|
||||
id: 'raster-tiles',
|
||||
type: 'raster',
|
||||
source: 'raster-tiles'
|
||||
}, {
|
||||
id: 'hills',
|
||||
type: 'hillshade',
|
||||
source: 'hillshadeSource',
|
||||
},
|
||||
layers: [
|
||||
{
|
||||
id: "raster-tiles",
|
||||
type: "raster",
|
||||
source: "raster-tiles",
|
||||
},
|
||||
{
|
||||
id: "hills",
|
||||
type: "hillshade",
|
||||
source: "hillshadeSource",
|
||||
layout: {
|
||||
visibility: 'visible'
|
||||
visibility: "visible",
|
||||
},
|
||||
paint: {
|
||||
'hillshade-shadow-color': '#473B24'
|
||||
}
|
||||
}],
|
||||
terrain: {
|
||||
source: 'terrainSource',
|
||||
exaggeration: 1
|
||||
"hillshade-shadow-color": "#473B24",
|
||||
},
|
||||
sky: {}
|
||||
},
|
||||
],
|
||||
terrain: {
|
||||
source: "terrainSource",
|
||||
exaggeration: 1,
|
||||
},
|
||||
sky: {},
|
||||
};
|
||||
break;
|
||||
}
|
||||
map.setStyle(basemapStyle);
|
||||
}
|
||||
|
||||
//------------------------------------------------------Action-Listener und Event-Handler---------------------------------------------------------------------------------
|
||||
|
||||
// Punktwolke aus/einblenden
|
||||
document.querySelector('#disable').addEventListener('click', function() {
|
||||
|
||||
//----------------------------------Action-Listener und Event-Handler--------------------------------------
|
||||
|
||||
// Punktwolke über Button aus/einblenden
|
||||
document.querySelector("#disable").addEventListener("click", function () {
|
||||
isVisible = !isVisible;
|
||||
|
||||
//if (currentPointCloud)
|
||||
// currentPointCloud.visible = isVisible;
|
||||
applyVisibility();
|
||||
|
||||
if(isVisible) {
|
||||
if (isVisible) {
|
||||
this.textContent = "Punktwolke ausblenden";
|
||||
this.classList.add("active-state");
|
||||
this.classList.remove("inactive-state");
|
||||
@@ -462,39 +502,43 @@ document.querySelector('#disable').addEventListener('click', function() {
|
||||
|
||||
|
||||
// Koordinaten an der Mausposition darstellen (auf 5 Nachkommastellen begrenzt)
|
||||
const coordinatesDiv = document.getElementById('coordinates');
|
||||
map.on('mousemove', (e) => {
|
||||
const coordinatesDiv = document.getElementById("coordinates");
|
||||
map.on("mousemove", (e) => {
|
||||
coordinatesDiv.innerHTML = `Lon: ${e.lngLat.lng.toFixed(5)}° | Lat: ${e.lngLat.lat.toFixed(5)}°`;
|
||||
});
|
||||
|
||||
|
||||
// Hintergrundkarte ändern, wenn anderes Element im DropDown gewählt wird
|
||||
document.querySelector('select[name="basemap"]').addEventListener('change', (e) => changeBaseMap(e.target.value));
|
||||
|
||||
document.querySelector('select[name="basemap"]').addEventListener("change", (e) => {
|
||||
changeBaseMap(e.target.value)
|
||||
});
|
||||
|
||||
// Punktwolke austauschen, wenn anderes Element im DropDown gewählt wird
|
||||
document.querySelector('select[name="pointcloud"]').addEventListener('change', (e) => {
|
||||
//loadPointCloud(getPointCloudFilesPOTREE()[e.target.value]);
|
||||
document.querySelector('select[name="pointcloud"]').addEventListener("change", (e) => {
|
||||
currentPointcloudKey = e.target.value;
|
||||
loadCurrentPointCloud();
|
||||
loadPointCloud();
|
||||
});
|
||||
|
||||
|
||||
// Kartenausschnitt auf Ursprung zurücksetzen
|
||||
document.getElementById("location").addEventListener("click", () => map.flyTo({center, zoom: 17}));
|
||||
document.getElementById("location").addEventListener("click", () => map.flyTo({ center, zoom: 17 }));
|
||||
|
||||
|
||||
document.getElementById("pointSizeSlider").oninput = function() {
|
||||
//viewer.setMinNodeSize(this.value);
|
||||
// Punktgröße mit Slider anpassen
|
||||
document.getElementById("pointSizeSlider").oninput = function () {
|
||||
currentPointSize = Number(this.value);
|
||||
if (currentRenderer === 'potree') {
|
||||
if (currentRenderer === "potree") {
|
||||
viewer.setMinNodeSize(currentPointSize);
|
||||
} else if (lidarControl) {
|
||||
try { lidarControl.setPointSize(currentPointSize); } catch(e) {}
|
||||
}
|
||||
} else if (lidarControl) {
|
||||
try {
|
||||
lidarControl.setPointSize(currentPointSize);
|
||||
} catch (e) {}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//--------------------------- Sidepannel ein/ausklappen ----------------------------------------------------
|
||||
const closeButton = document.getElementById("closeSideBarButton");
|
||||
const sidebar = document.getElementById("sidebar");
|
||||
const openOuter = document.getElementById("openButtonOuter");
|
||||
@@ -513,23 +557,24 @@ if (closeButton) {
|
||||
}
|
||||
|
||||
|
||||
document.querySelectorAll('.qualityButtons').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
//------------------------- QUALITÄTS- UND FARB-BUTTONS ----------------------------------------------------
|
||||
// Qualitätsstufe wählen
|
||||
document.querySelectorAll(".qualityButtons").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
quality = btn.id;
|
||||
//if (currentPointCloud) loadPointCloud(getPointCloudFiles()[document.getElementById("pointcloud").value]);
|
||||
if (currentPointcloudKey) loadCurrentPointCloud();
|
||||
document.querySelectorAll('.qualityButtons').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
if (currentPointcloudKey)
|
||||
loadPointCloud();
|
||||
document.querySelectorAll(".qualityButtons").forEach((b) => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.querySelectorAll('.colorButtons').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
//if (!currentPointCloud) return; // Abbrechen, falls noch keine Wolke geladen ist
|
||||
|
||||
document.querySelectorAll('.colorButtons').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
// Farbmodus (rgb/elevation) wählen
|
||||
document.querySelectorAll(".colorButtons").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
document.querySelectorAll(".colorButtons").forEach((b) => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
currentColorMode = btn.id;
|
||||
applyColorMode();
|
||||
});
|
||||
@@ -537,26 +582,25 @@ document.querySelectorAll('.colorButtons').forEach(btn => {
|
||||
|
||||
|
||||
// Sobald die Karte das allererste Mal stabil steht, Kamera abgleichen
|
||||
map.once('idle', () => {
|
||||
map.once("idle", () => {
|
||||
syncCamera();
|
||||
});
|
||||
|
||||
|
||||
|
||||
//--------------- INFO-PANEL (Texte & Bilder zu den Punktwolken) -------------------------------
|
||||
let pointCloudInfo = {};
|
||||
const infoPanel = document.getElementById("info-panel");
|
||||
const infoPanelTitle = document.getElementById("info-panel-title");
|
||||
const infoPanelText = document.getElementById("info-panel-text");
|
||||
const openInfoPanel = document.getElementById("openInfoPanel");
|
||||
const closeInfoPanel = document.getElementById("closeInfoPanel");
|
||||
|
||||
let currentImageIndex = 0;
|
||||
|
||||
|
||||
// Info-Panel mit Titel, Text und Bild/Bildern der gewählten Punktwolke befüllen
|
||||
function updateInfoPanel() {
|
||||
const key = document.querySelector('select[name="pointcloud"]').value;
|
||||
|
||||
const info = pointCloudInfo[key];
|
||||
|
||||
currentImageIndex = 0;
|
||||
|
||||
if (info) {
|
||||
@@ -572,6 +616,7 @@ function updateInfoPanel() {
|
||||
}
|
||||
|
||||
|
||||
// aktuelles Bild + Bildzähler im Info-Panel anzeigen
|
||||
function updateImage(info) {
|
||||
const img = document.getElementById("info-panel-image");
|
||||
const nav = document.getElementById("info-image-nav");
|
||||
@@ -589,44 +634,49 @@ function updateImage(info) {
|
||||
}
|
||||
|
||||
|
||||
// vorheriges Bild im Info-Panel anzeigen
|
||||
document.getElementById("info-img-prev").addEventListener("click", () => {
|
||||
const key = document.querySelector('select[name="pointcloud"]').value;
|
||||
const info = pointCloudInfo[key];
|
||||
if (!info) return;
|
||||
if (!info)
|
||||
return;
|
||||
currentImageIndex = (currentImageIndex - 1 + info.images.length) % info.images.length;
|
||||
updateImage(info);
|
||||
});
|
||||
|
||||
|
||||
// nächstes Bild im Info-Panel anzeigen
|
||||
document.getElementById("info-img-next").addEventListener("click", () => {
|
||||
const key = document.querySelector('select[name="pointcloud"]').value;
|
||||
const info = pointCloudInfo[key];
|
||||
if (!info) return;
|
||||
if (!info)
|
||||
return;
|
||||
currentImageIndex = (currentImageIndex + 1) % info.images.length;
|
||||
updateImage(info);
|
||||
});
|
||||
|
||||
|
||||
// Info-Panel öffnen
|
||||
openInfoPanel.addEventListener("click", () => {
|
||||
infoPanel.classList.add("open");
|
||||
//openInfoPanel.style.display = "none";
|
||||
});
|
||||
|
||||
|
||||
// Info-Panel schließen
|
||||
closeInfoPanel.addEventListener("click", () => {
|
||||
infoPanel.classList.remove("open");
|
||||
openInfoPanel.style.display = "flex";
|
||||
});
|
||||
|
||||
|
||||
// Panel aktualisieren wenn Punktwolke gewechselt wird
|
||||
// Info-Panel aktualisieren, wenn Punktwolke gewechselt wird
|
||||
document.querySelector('select[name="pointcloud"]').addEventListener("change", () => {
|
||||
updateInfoPanel();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.getElementById('rendererToggle').addEventListener('click', function() {
|
||||
const next = currentRenderer === 'deckgl' ? 'potree' : 'deckgl';
|
||||
//Renderer Toggle Button
|
||||
document.getElementById("rendererToggle").addEventListener("click", function () {
|
||||
const next = currentRenderer === "deckgl" ? "potree" : "deckgl";
|
||||
switchRenderer(next);
|
||||
});
|
||||
|
||||
});
|
||||
+3
-3
@@ -2,16 +2,16 @@
|
||||
"first": {
|
||||
"title": "Fläche im unteren Bereich",
|
||||
"text": "Dieser Bereich des Steinbruchs zeigt eine natürliche Oberfläche aus Obernkirchener Sandstein. Zwischen einzelnen Sandsteinblöcken haben sich Sträucher und andere Pflanzen angesiedelt. Der Sandstein entstand vor etwa 140 Millionen Jahren in einer küstennahen Landschaft der Unterkreide.",
|
||||
"images": ["stand1_1.jpeg", "stand1_2.jpeg"]
|
||||
"images": ["/stand1_1.jpeg", "/stand1_2.jpeg"]
|
||||
},
|
||||
"second": {
|
||||
"title": "Abbauwand",
|
||||
"text": "An dieser Felswand wird der Obernkirchener Sandstein abgebaut. Das Gestein ist für seine hohe Festigkeit und Witterungsbeständigkeit bekannt und wird seit Jahrhunderten als Bau- und Werkstein genutzt. Es findet sich unter anderem an zahlreichen historischen Gebäuden in Deutschland.",
|
||||
"images": ["stand2_1.jpeg"]
|
||||
"images": ["/stand2_1.jpeg"]
|
||||
},
|
||||
"third": {
|
||||
"title": "Dinosaurier-Spuren",
|
||||
"text": "Auf dieser öffentlich zugänglichen Fläche sind fossile Fußspuren von Dinosauriern erhalten geblieben. Die Spuren entstanden vor rund 140 Millionen Jahren in feuchtem Sand und wurden später durch weitere Sedimentschichten konserviert. Sie gehören zu den bekanntesten Fossilienfunden im Obernkirchener Sandstein.",
|
||||
"images": ["stand3_1.jpeg", "stand3_2.jpeg"]
|
||||
"images": ["/stand3_1.jpeg", "/stand3_2.jpeg"]
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,30 @@
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body{
|
||||
margin:0;
|
||||
overflow:hidden;
|
||||
font-family: 'Inter', sans-serif;
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-family: "Inter", sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#main{
|
||||
width:100vw;
|
||||
height:100vh;
|
||||
position:relative;
|
||||
#main {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#header{
|
||||
position:absolute;
|
||||
top:20px;
|
||||
left:50%;
|
||||
transform:translateX(-50%);
|
||||
z-index:30;
|
||||
padding:14px 28px;
|
||||
#header {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 30;
|
||||
padding: 14px 28px;
|
||||
border-radius: 20px;
|
||||
background:
|
||||
linear-gradient(
|
||||
@@ -32,28 +33,28 @@ body{
|
||||
rgba(79, 79, 79, 0.756) 60%,
|
||||
rgba(47, 47, 47, 0.864) 100%
|
||||
),
|
||||
rgba(24,24,27,0.78);
|
||||
rgba(24, 24, 27, 0.78);
|
||||
backdrop-filter: blur(16px);
|
||||
color:white;
|
||||
font-size:24px;
|
||||
font-weight:600;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.536);
|
||||
}
|
||||
|
||||
.logos{
|
||||
margin-top:20px;
|
||||
.logos {
|
||||
margin-top: 20px;
|
||||
margin-left: 12px;
|
||||
opacity:0.8;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.logos img{
|
||||
width:80px;
|
||||
.logos img {
|
||||
width: 80px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.logos img:hover{
|
||||
transform:translateY(-1px);
|
||||
box-shadow: 0 6px 18px rgba(0,0,0,0.12);
|
||||
.logos img:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
#location-button {
|
||||
@@ -69,7 +70,9 @@ body{
|
||||
background-color: #f8f9fa;
|
||||
border: none !important;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
box-shadow:
|
||||
0 0 0 2px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -78,28 +81,28 @@ body{
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
#location{
|
||||
width:20px;
|
||||
height:20px;
|
||||
object-fit:contain;
|
||||
#location {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
object-fit: contain;
|
||||
margin-left: 1px;
|
||||
margin-top: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#location:hover{
|
||||
transform:scale(1.05);
|
||||
background:rgb(233, 232, 232);
|
||||
#location:hover {
|
||||
transform: scale(1.05);
|
||||
background: rgb(233, 232, 232);
|
||||
}
|
||||
|
||||
#location-button:hover{
|
||||
background:rgb(233, 232, 232);
|
||||
#location-button:hover {
|
||||
background: rgb(233, 232, 232);
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
position: absolute;
|
||||
top: 48%;
|
||||
transform:translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
left: 20px;
|
||||
width: 210px;
|
||||
padding: 22px;
|
||||
@@ -107,13 +110,13 @@ body{
|
||||
padding-top: 10px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(18px);
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:8px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#main {
|
||||
@@ -126,9 +129,9 @@ body{
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#potree_render_area{
|
||||
position:absolute;
|
||||
top:0;
|
||||
#potree_render_area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -140,49 +143,48 @@ body{
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
select{
|
||||
appearance:none;
|
||||
-webkit-appearance:none;
|
||||
-moz-appearance:none;
|
||||
width:100%;
|
||||
padding:10px 16px;
|
||||
border:none;
|
||||
border-radius:14px;
|
||||
background:rgba(255,255,255,0.92);
|
||||
font-size:14px;
|
||||
font-weight:500;
|
||||
color:#0f172a;
|
||||
select {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
width: 100%;
|
||||
padding: 10px 16px;
|
||||
border: none;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #0f172a;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.278);
|
||||
cursor:pointer;
|
||||
transition:0.2s;
|
||||
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.selectWrapper{
|
||||
position:relative;
|
||||
width:100%;
|
||||
.selectWrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.selectArrow{
|
||||
position:absolute;
|
||||
right:14px;
|
||||
top:48%;
|
||||
transform:translateY(-50%);
|
||||
pointer-events:none;
|
||||
font-size:14px;
|
||||
color:#000000;
|
||||
.selectArrow {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 48%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
font-size: 14px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#disable{
|
||||
appearance:none;
|
||||
-webkit-appearance:none;
|
||||
-moz-appearance:none;
|
||||
width:100%;
|
||||
padding:10px 16px;
|
||||
border:none;
|
||||
border-radius:14px;
|
||||
font-size:14px;
|
||||
font-weight:500;
|
||||
#disable {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
width: 100%;
|
||||
padding: 10px 16px;
|
||||
border: none;
|
||||
border-radius: 14px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
background:
|
||||
linear-gradient(
|
||||
135deg,
|
||||
@@ -193,8 +195,8 @@ select{
|
||||
rgba(6, 45, 135, 0.92);
|
||||
color: white;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.278);
|
||||
cursor:pointer;
|
||||
transition:0.2s;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@@ -212,33 +214,35 @@ select{
|
||||
}
|
||||
|
||||
#disable.inactive-state {
|
||||
background: rgba(255,255,255,0.92);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
select:hover{
|
||||
transform:translateY(-1px);
|
||||
box-shadow: 0 6px 18px rgba(0,0,0,0.12);
|
||||
select:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
select:focus{
|
||||
outline:none;
|
||||
box-shadow:0 0 0 4px rgba(37,99,235,0.15), 0 8px 24px rgba(0,0,0,0.12);
|
||||
select:focus {
|
||||
outline: none;
|
||||
box-shadow:
|
||||
0 0 0 4px rgba(37, 99, 235, 0.15),
|
||||
0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
#disable:hover{
|
||||
transform:translateY(-1px);
|
||||
box-shadow: 0 6px 18px rgba(0,0,0,0.12);
|
||||
#disable:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
*{
|
||||
* {
|
||||
transition:
|
||||
background 0.2s,
|
||||
transform 0.01s,
|
||||
opacity 0.2s;
|
||||
}
|
||||
|
||||
footer{
|
||||
footer {
|
||||
z-index: 30;
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
@@ -246,13 +250,13 @@ footer{
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.sideBarText{
|
||||
font-size:80%;
|
||||
margin:0;
|
||||
margin-bottom: -2px;
|
||||
.sideBarText {
|
||||
font-size: 80%;
|
||||
margin: 0;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
#pointSizeSlider{
|
||||
#pointSizeSlider {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -260,7 +264,7 @@ margin-bottom: -2px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
input[type="range"]{
|
||||
input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
@@ -272,11 +276,11 @@ input[type="range"]{
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb{
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width:16px;
|
||||
height:16px;
|
||||
border-radius:50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background:
|
||||
linear-gradient(
|
||||
135deg,
|
||||
@@ -285,50 +289,50 @@ input[type="range"]::-webkit-slider-thumb{
|
||||
rgba(6, 45, 135, 0.92) 100%
|
||||
),
|
||||
rgba(6, 45, 135, 0.92);
|
||||
cursor:pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider-labels{
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
font-size:0.5rem;
|
||||
color:#868686;
|
||||
margin-bottom:0px;
|
||||
.slider-labels {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.5rem;
|
||||
color: #868686;
|
||||
margin-bottom: 0px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
#title{
|
||||
#title {
|
||||
z-index: 30;
|
||||
position: absolute;
|
||||
top: 75px;
|
||||
left: 50%;
|
||||
transform:translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
font-size: small;
|
||||
color: #ffffff;
|
||||
text-shadow: 1px 1.5px #00000059;
|
||||
}
|
||||
|
||||
#coordinates{
|
||||
#coordinates {
|
||||
z-index: 30;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform:translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
font-size: smaller;
|
||||
color: #000000cf;
|
||||
border: none;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.278);
|
||||
padding:8px;
|
||||
padding: 8px;
|
||||
border-radius: 15px;
|
||||
background-color: rgba(255, 255, 255, 0.751);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.sideBarButtons{
|
||||
font-family:monospace;
|
||||
border:none;
|
||||
border-radius:10px;
|
||||
.sideBarButtons {
|
||||
font-family: monospace;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
background:
|
||||
linear-gradient(
|
||||
135deg,
|
||||
@@ -336,54 +340,56 @@ input[type="range"]::-webkit-slider-thumb{
|
||||
rgba(79, 79, 79, 0.756) 60%,
|
||||
rgba(47, 47, 47, 0.864) 100%
|
||||
),
|
||||
rgba(24,24,27,0.78);
|
||||
font-size:15px;
|
||||
color:#ffffff;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
||||
cursor:pointer;
|
||||
transition:0.2s;
|
||||
rgba(24, 24, 27, 0.78);
|
||||
font-size: 15px;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
#closeSideBarButton{
|
||||
#closeSideBarButton {
|
||||
align-self: flex-end;
|
||||
right: 0%;
|
||||
padding:5px 15px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
#openSideBarButton{
|
||||
position: absolute;
|
||||
#openSideBarButton {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
padding:5px 15px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.buttonRow{
|
||||
.buttonRow {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
overflow:hidden;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.278);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.qualityButtons, .colorButtons{
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
appearance:none;
|
||||
-webkit-appearance:none;
|
||||
-moz-appearance:none;
|
||||
border:none;
|
||||
background:rgba(255,255,255,0.92);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.278);
|
||||
font-size:12px;
|
||||
color:#0f172a;
|
||||
padding:5px;
|
||||
cursor:pointer;
|
||||
transition:0.2s;
|
||||
}
|
||||
|
||||
.qualityButtons.active, .colorButtons.active{
|
||||
.qualityButtons,
|
||||
.colorButtons {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.278);
|
||||
font-size: 12px;
|
||||
color: #0f172a;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.qualityButtons.active,
|
||||
.colorButtons.active {
|
||||
background:
|
||||
linear-gradient(
|
||||
135deg,
|
||||
@@ -397,9 +403,9 @@ position: absolute;
|
||||
|
||||
#potree_render_area {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
#info-button-outer {
|
||||
#info-button-outer {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 20px;
|
||||
@@ -417,31 +423,33 @@ position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,0.278);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.278);
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
#rendererToggle[data-active="deckgl"] {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(18,77,216,0.92) 10%,
|
||||
rgba(13,61,175,0.92) 60%,
|
||||
rgba(6,45,135,0.92) 100%);
|
||||
rgba(18, 77, 216, 0.92) 10%,
|
||||
rgba(13, 61, 175, 0.92) 60%,
|
||||
rgba(6, 45, 135, 0.92) 100%
|
||||
);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#rendererToggle[data-active="potree"] {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(22,163,74,0.92) 10%,
|
||||
rgba(15,118,54,0.92) 60%,
|
||||
rgba(6,78,32,0.92) 100%);
|
||||
rgba(22, 163, 74, 0.92) 10%,
|
||||
rgba(15, 118, 54, 0.92) 60%,
|
||||
rgba(6, 78, 32, 0.92) 100%
|
||||
);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#rendererToggle:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 18px rgba(0,0,0,0.12);
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
#rendererIcon {
|
||||
@@ -457,8 +465,8 @@ position: absolute;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
background: rgba(255,255,255,0.9);
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,0.278);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.278);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -467,7 +475,7 @@ position: absolute;
|
||||
}
|
||||
|
||||
#openInfoPanel:hover {
|
||||
background: rgb(233,232,232);
|
||||
background: rgb(233, 232, 232);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@@ -485,11 +493,11 @@ position: absolute;
|
||||
transform: translateY(-50%);
|
||||
width: 340px;
|
||||
padding: 18px 22px;
|
||||
background: rgba(255,255,255,0.9);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(18px);
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||
z-index: 20;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
@@ -536,18 +544,20 @@ position: absolute;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
#info-img-prev, #info-img-next {
|
||||
#info-img-prev,
|
||||
#info-img-next {
|
||||
border: none;
|
||||
background: rgba(255,255,255,0.9);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 8px;
|
||||
padding: 4px 10px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
#info-img-prev:hover, #info-img-next:hover {
|
||||
background: rgb(233,232,232);
|
||||
#info-img-prev:hover,
|
||||
#info-img-next:hover {
|
||||
background: rgb(233, 232, 232);
|
||||
}
|
||||
|
||||
#info-image-counter {
|
||||
@@ -570,4 +580,3 @@ button:has(.lidar-control-icon) {
|
||||
padding: 0 !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user