Feature: Added real-time GPS location tracking for field use
Deploy Bürgerwind / deploy (push) Successful in 16s
Details
Deploy Bürgerwind / deploy (push) Successful in 16s
Details
This commit is contained in:
parent
0d9306741e
commit
4479d261d4
81
app.js
81
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.";
|
||||
|
|
|
|||
|
|
@ -258,6 +258,9 @@
|
|||
</div>
|
||||
|
||||
<main id="map"></main>
|
||||
<button id="btnLocate" class="map-overlay-btn" title="Standort anzeigen">
|
||||
<span class="gps-icon">🎯</span>
|
||||
</button>
|
||||
|
||||
<!-- JS Dependencies -->
|
||||
<script src="libs/proj4.js"></script>
|
||||
|
|
|
|||
75
style.css
75
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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue