From 4479d261d47e7f038c31e2904b36ff633fd139ac Mon Sep 17 00:00:00 2001 From: Johannes Baumeister Date: Wed, 13 May 2026 12:51:56 +0200 Subject: [PATCH] Feature: Added real-time GPS location tracking for field use --- app.js | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 3 ++ style.css | 75 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 156 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index e648140..a05bb23 100644 --- a/app.js +++ b/app.js @@ -2084,6 +2084,87 @@ document.addEventListener('DOMContentLoaded', async () => { reader.readAsText(file); }); + // Geolocation Logic + let watchId = null; + let userMarker = null; + let userAccuracyCircle = null; + const btnLocate = document.getElementById('btnLocate'); + + function stopTracking() { + if (watchId !== null) { + navigator.geolocation.clearWatch(watchId); + watchId = null; + } + if (userMarker) state.map.removeLayer(userMarker); + if (userAccuracyCircle) state.map.removeLayer(userAccuracyCircle); + userMarker = null; + userAccuracyCircle = null; + btnLocate.classList.remove('active'); + document.getElementById('statusInfo').innerText = "Standortverfolgung beendet."; + } + + function startTracking() { + if (!("geolocation" in navigator)) { + alert("Geolocation wird von Ihrem Browser nicht unterstützt."); + return; + } + + btnLocate.classList.add('active'); + document.getElementById('statusInfo').innerText = "Suche Standort..."; + + watchId = navigator.geolocation.watchPosition((position) => { + const { latitude, longitude, accuracy } = position.coords; + const latlng = L.latLng(latitude, longitude); + + if (!userMarker) { + userMarker = L.marker(latlng, { + icon: L.divIcon({ + className: 'user-location-marker pulse', + iconSize: [16, 16], + iconAnchor: [8, 8] + }) + }).addTo(state.map); + + userAccuracyCircle = L.circle(latlng, { + radius: accuracy, + className: 'user-location-accuracy' + }).addTo(state.map); + + // Center on first found position + state.map.setView(latlng, 16); + } else { + userMarker.setLatLng(latlng); + userAccuracyCircle.setLatLng(latlng); + userAccuracyCircle.setRadius(accuracy); + } + + document.getElementById('statusInfo').innerText = `Standort aktualisiert (Genauigkeit: ${accuracy.toFixed(0)}m)`; + }, (err) => { + console.error("Geolocation error:", err); + let msg = "Fehler bei der Standorterkennung."; + if (err.code === 1) msg = "Standortzugriff verweigert."; + else if (err.code === 2) msg = "Standort nicht verfügbar."; + else if (err.code === 3) msg = "Zeitüberschreitung."; + + alert(msg); + stopTracking(); + }, { + enableHighAccuracy: true, + timeout: 10000, + maximumAge: 0 + }); + } + + if (btnLocate) { + btnLocate.onclick = () => { + if (watchId === null) { + startTracking(); + } else { + stopTracking(); + } + }; + } + updateLegend(); console.log("WindPlaner initialisiert."); document.getElementById('statusInfo').innerText = "System bereit. Karte geladen."; diff --git a/index.html b/index.html index fb9dbc7..6b95b9a 100644 --- a/index.html +++ b/index.html @@ -258,6 +258,9 @@
+ diff --git a/style.css b/style.css index 35e10ee..53016f6 100644 --- a/style.css +++ b/style.css @@ -788,7 +788,76 @@ body { /* Grey -> Light Blue */ /* Selection specific styles */ -select.status-select option { - background: var(--bg-dark); - color: white; +/* Map Overlay Buttons (GPS, etc.) */ +.map-overlay-btn { + position: absolute; + bottom: 25px; + left: 285px; /* Right of the sidebar */ + z-index: 1000; + width: 44px; + height: 44px; + background: var(--panel-bg); + backdrop-filter: blur(8px); + border: 1px solid var(--primary-color); + border-radius: 50%; + color: var(--primary-color); + font-size: 1.2rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4px 15px rgba(0,0,0,0.4); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.sidebar.collapsed ~ .map-overlay-btn { + left: 25px; +} + +.map-overlay-btn:hover { + background: var(--primary-color); + color: black; + transform: scale(1.1); +} + +.map-overlay-btn.active { + background: var(--primary-color); + color: black; + box-shadow: 0 0 15px var(--primary-color); +} + +/* User Location Marker */ +.user-location-marker { + background: #3498db; + border: 3px solid white; + border-radius: 50%; + box-shadow: 0 0 10px rgba(0,0,0,0.5); +} + +.user-location-accuracy { + stroke: #3498db; + stroke-width: 2; + fill: #3498db; + fill-opacity: 0.1; + transition: all 0.5s ease; +} + +.pulse { + box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.7); + animation: pulse-blue 2s infinite; +} + +@keyframes pulse-blue { + 0% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.7); + } + 70% { + transform: scale(1); + box-shadow: 0 0 0 15px rgba(52, 152, 219, 0); + } + 100% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(52, 152, 219, 0); + } } \ No newline at end of file