Add files via upload
This commit is contained in:
1 parent
055077803c
commit
63da9cdbba
7 files changed
+1046
-612
No files matched your search
+336
-321
@@ -1,321 +1,336 @@
|
||||
// Live-Karte mit mehreren Modi:
|
||||
// - "radar": zeigt alle Fahrzeuge im aktuellen Kartenausschnitt (Standard),
|
||||
// gefiltert nach den gewählten Verkehrsmitteln
|
||||
// - "isolated": ein Fahrzeug wurde angeklickt -> nur dieses wird live weiter-
|
||||
// verfolgt (serverseitig via tripId gefiltert), sein Streckenverlauf
|
||||
// wird eingezeichnet, alle anderen Fahrzeuge sind ausgeblendet
|
||||
// - "trip": verfolgt ein bekanntes Fahrzeug (z.B. aus einer Verbindung heraus)
|
||||
// Nutzt bewusst reines OpenStreetMap ohne zusätzlichen Kartenstil.
|
||||
// Zusätzlich: createMiniMap() für kleine, unabhängige Vorschau-Karten in den
|
||||
// Verbindungsergebnissen (Routenplaner).
|
||||
|
||||
const LiveMap = (() => {
|
||||
let map = null;
|
||||
let radarLayer = null;
|
||||
let routeLayer = null;
|
||||
let userLayer = null;
|
||||
let tripMarker = null;
|
||||
|
||||
let mode = 'radar';
|
||||
let radarPollHandle = null;
|
||||
let tripPollHandle = null;
|
||||
let currentTripId = null;
|
||||
let isolatedVehicleMeta = null;
|
||||
let getModesParam = () => null;
|
||||
|
||||
function init() {
|
||||
if (map) return;
|
||||
|
||||
map = L.map('live-map', { zoomControl: true }).setView([52.5200, 13.4050], 13);
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>-Mitwirkende',
|
||||
maxZoom: 19,
|
||||
}).addTo(map);
|
||||
|
||||
radarLayer = L.layerGroup().addTo(map);
|
||||
routeLayer = L.layerGroup().addTo(map);
|
||||
userLayer = L.layerGroup().addTo(map);
|
||||
|
||||
map.on('moveend', () => {
|
||||
if (mode === 'radar') refreshRadar();
|
||||
});
|
||||
}
|
||||
|
||||
function setModeFilter(fn) {
|
||||
getModesParam = fn;
|
||||
}
|
||||
|
||||
function vehicleIcon(label, bg, fg) {
|
||||
return L.divIcon({
|
||||
className: 'vehicle-marker',
|
||||
html: `<span style="background:${bg || '#F5A623'};color:${fg || '#fff'};">${label || '?'}</span>`,
|
||||
iconSize: [null, null],
|
||||
});
|
||||
}
|
||||
|
||||
function polylineToLatLngs(geojson) {
|
||||
if (!geojson || !geojson.features) return [];
|
||||
return geojson.features.map((f) => [f.geometry.coordinates[1], f.geometry.coordinates[0]]);
|
||||
}
|
||||
|
||||
async function refreshRadar() {
|
||||
if (!map || mode !== 'radar') return;
|
||||
const bounds = map.getBounds();
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await API.getRadar({
|
||||
north: bounds.getNorth().toFixed(5),
|
||||
west: bounds.getWest().toFixed(5),
|
||||
south: bounds.getSouth().toFixed(5),
|
||||
east: bounds.getEast().toFixed(5),
|
||||
modes: getModesParam(),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Radar-Fehler:', err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
radarLayer.clearLayers();
|
||||
data.vehicles.forEach((v) => {
|
||||
if (v.latitude == null || v.longitude == null) return;
|
||||
const marker = L.marker([v.latitude, v.longitude], { icon: vehicleIcon(v.line, v.color, v.textColor) });
|
||||
marker.bindPopup(
|
||||
`<strong>${v.line || 'Linie unbekannt'}</strong><br>${v.direction || ''}<br>` +
|
||||
`<small>Nächster Halt: ${v.nextStopover || '–'}</small>`,
|
||||
);
|
||||
marker.on('click', () => isolateVehicle(v));
|
||||
marker.addTo(radarLayer);
|
||||
});
|
||||
}
|
||||
|
||||
function startRadar() {
|
||||
init();
|
||||
setMode('radar');
|
||||
refreshRadar();
|
||||
if (radarPollHandle) clearInterval(radarPollHandle);
|
||||
radarPollHandle = setInterval(refreshRadar, 15000);
|
||||
setTimeout(() => map && map.invalidateSize(), 150);
|
||||
}
|
||||
|
||||
function stopRadar() {
|
||||
if (radarPollHandle) {
|
||||
clearInterval(radarPollHandle);
|
||||
radarPollHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshIsolated() {
|
||||
if (!map || mode !== 'isolated' || !currentTripId) return;
|
||||
const bounds = map.getBounds();
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await API.getRadar({
|
||||
north: bounds.getNorth().toFixed(5),
|
||||
west: bounds.getWest().toFixed(5),
|
||||
south: bounds.getSouth().toFixed(5),
|
||||
east: bounds.getEast().toFixed(5),
|
||||
tripId: currentTripId,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Radar-Fehler (isoliert):', err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
radarLayer.clearLayers();
|
||||
const v = data.vehicles[0];
|
||||
if (v && v.latitude != null && v.longitude != null) {
|
||||
const marker = L.marker([v.latitude, v.longitude], { icon: vehicleIcon(v.line, v.color, v.textColor) });
|
||||
marker.bindPopup(`<strong>${v.line || ''}</strong><br>${v.direction || ''}<br><small>Nächster Halt: ${v.nextStopover || '–'}</small>`);
|
||||
marker.addTo(radarLayer);
|
||||
}
|
||||
}
|
||||
|
||||
async function isolateVehicle(vehicle) {
|
||||
if (!vehicle || !vehicle.tripId) return;
|
||||
setMode('isolated');
|
||||
currentTripId = vehicle.tripId;
|
||||
isolatedVehicleMeta = { line: vehicle.line, color: vehicle.color, textColor: vehicle.textColor };
|
||||
updateIsolationBanner();
|
||||
|
||||
routeLayer.clearLayers();
|
||||
try {
|
||||
const trip = await API.getTrip(vehicle.tripId, { polyline: true });
|
||||
if (trip.polyline) {
|
||||
const latlngs = polylineToLatLngs(trip.polyline);
|
||||
if (latlngs.length > 0) {
|
||||
L.polyline(latlngs, { color: vehicle.color || '#F5A623', weight: 5, opacity: 0.85 }).addTo(routeLayer);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Trip-Polyline-Fehler:', err.message);
|
||||
}
|
||||
|
||||
refreshIsolated();
|
||||
if (radarPollHandle) clearInterval(radarPollHandle);
|
||||
radarPollHandle = setInterval(refreshIsolated, 10000);
|
||||
}
|
||||
|
||||
function clearIsolation() {
|
||||
currentTripId = null;
|
||||
isolatedVehicleMeta = null;
|
||||
updateIsolationBanner();
|
||||
startRadar();
|
||||
}
|
||||
|
||||
function updateIsolationBanner() {
|
||||
const banner = document.getElementById('map-isolation-banner');
|
||||
if (!banner) return;
|
||||
if (!isolatedVehicleMeta) {
|
||||
banner.classList.remove('is-visible');
|
||||
return;
|
||||
}
|
||||
const lineEl = document.getElementById('map-isolation-line');
|
||||
if (lineEl) {
|
||||
lineEl.textContent = isolatedVehicleMeta.line || '?';
|
||||
lineEl.style.background = isolatedVehicleMeta.color || '#F5A623';
|
||||
lineEl.style.color = isolatedVehicleMeta.textColor || '#fff';
|
||||
}
|
||||
banner.classList.add('is-visible');
|
||||
}
|
||||
|
||||
async function refreshTrip() {
|
||||
if (!currentTripId) return;
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await API.getTrip(currentTripId, { polyline: !tripMarker });
|
||||
} catch (err) {
|
||||
console.error('Trip-Fehler:', err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.polyline && routeLayer) {
|
||||
routeLayer.clearLayers();
|
||||
const latlngs = polylineToLatLngs(data.polyline);
|
||||
if (latlngs.length > 0) {
|
||||
L.polyline(latlngs, { color: data.color || '#F5A623', weight: 4, opacity: 0.7 }).addTo(routeLayer);
|
||||
map.fitBounds(L.latLngBounds(latlngs), { padding: [30, 30] });
|
||||
}
|
||||
}
|
||||
|
||||
if (data.currentLocation) {
|
||||
const pos = [data.currentLocation.latitude, data.currentLocation.longitude];
|
||||
if (tripMarker) {
|
||||
tripMarker.setLatLng(pos);
|
||||
} else {
|
||||
tripMarker = L.marker(pos, { icon: vehicleIcon(data.line, data.color, data.textColor) }).addTo(routeLayer);
|
||||
}
|
||||
tripMarker.bindPopup(`<strong>${data.line || ''}</strong><br>→ ${data.direction || ''}`);
|
||||
}
|
||||
}
|
||||
|
||||
function trackTrip(tripId) {
|
||||
init();
|
||||
setMode('trip');
|
||||
currentTripId = tripId;
|
||||
tripMarker = null;
|
||||
routeLayer.clearLayers();
|
||||
|
||||
refreshTrip();
|
||||
if (tripPollHandle) clearInterval(tripPollHandle);
|
||||
tripPollHandle = setInterval(refreshTrip, 10000);
|
||||
setTimeout(() => map && map.invalidateSize(), 150);
|
||||
}
|
||||
|
||||
function stopTracking() {
|
||||
if (tripPollHandle) {
|
||||
clearInterval(tripPollHandle);
|
||||
tripPollHandle = null;
|
||||
}
|
||||
currentTripId = null;
|
||||
tripMarker = null;
|
||||
}
|
||||
|
||||
function locateUser() {
|
||||
init();
|
||||
if (!navigator.geolocation) {
|
||||
alert('Geolocation wird von diesem Browser nicht unterstützt.');
|
||||
return;
|
||||
}
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos) => {
|
||||
const { latitude, longitude } = pos.coords;
|
||||
userLayer.clearLayers();
|
||||
L.marker([latitude, longitude], {
|
||||
icon: L.divIcon({ className: 'user-location-marker', iconSize: [16, 16] }),
|
||||
}).addTo(userLayer);
|
||||
map.setView([latitude, longitude], 16);
|
||||
},
|
||||
(err) => alert('Standort konnte nicht ermittelt werden: ' + err.message),
|
||||
{ enableHighAccuracy: true, timeout: 10000 },
|
||||
);
|
||||
}
|
||||
|
||||
function setMode(newMode) {
|
||||
if (mode === newMode) return;
|
||||
if (mode === 'radar') stopRadar();
|
||||
if (mode === 'isolated') stopRadar();
|
||||
if (mode === 'trip') stopTracking();
|
||||
if (newMode === 'radar') routeLayer.clearLayers();
|
||||
mode = newMode;
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
if (mode === 'radar' || mode === 'idle') startRadar();
|
||||
else if (map) setTimeout(() => map.invalidateSize(), 150);
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
stopRadar();
|
||||
stopTracking();
|
||||
}
|
||||
|
||||
function createMiniMap(containerId, journey) {
|
||||
const miniMap = L.map(containerId, {
|
||||
zoomControl: false,
|
||||
attributionControl: false,
|
||||
dragging: true,
|
||||
scrollWheelZoom: false,
|
||||
tap: true,
|
||||
}).setView([52.5200, 13.4050], 12);
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap-Mitwirkende',
|
||||
maxZoom: 19,
|
||||
}).addTo(miniMap);
|
||||
|
||||
const allPoints = [];
|
||||
(journey.legs || []).forEach((leg) => {
|
||||
if (!leg.polyline) return;
|
||||
const latlngs = polylineToLatLngs(leg.polyline);
|
||||
if (latlngs.length === 0) return;
|
||||
L.polyline(latlngs, {
|
||||
color: leg.walking ? '#8E99A6' : leg.color || '#F5A623',
|
||||
weight: leg.walking ? 3 : 4,
|
||||
dashArray: leg.walking ? '5, 7' : null,
|
||||
opacity: 0.9,
|
||||
}).addTo(miniMap);
|
||||
allPoints.push(...latlngs);
|
||||
});
|
||||
|
||||
if (allPoints.length > 0) {
|
||||
miniMap.fitBounds(L.latLngBounds(allPoints), { padding: [16, 16] });
|
||||
}
|
||||
|
||||
setTimeout(() => miniMap.invalidateSize(), 50);
|
||||
return miniMap;
|
||||
}
|
||||
|
||||
return {
|
||||
startPolling,
|
||||
stopPolling,
|
||||
trackTrip,
|
||||
setModeFilter,
|
||||
clearIsolation,
|
||||
locateUser,
|
||||
createMiniMap,
|
||||
};
|
||||
})();
|
||||
// Live-Karte mit mehreren Modi:
|
||||
// - "radar": zeigt alle Fahrzeuge im aktuellen Kartenausschnitt (Standard),
|
||||
// gefiltert nach den gewählten Verkehrsmitteln
|
||||
// - "isolated": ein Fahrzeug wurde angeklickt -> nur dieses wird live weiter-
|
||||
// verfolgt (serverseitig via tripId gefiltert), sein Streckenverlauf
|
||||
// wird eingezeichnet, alle anderen Fahrzeuge sind ausgeblendet
|
||||
// - "trip": verfolgt ein bekanntes Fahrzeug (z.B. aus einer Verbindung heraus)
|
||||
// Nutzt bewusst reines OpenStreetMap ohne zusätzlichen Kartenstil.
|
||||
// Zusätzlich: createMiniMap() für kleine, unabhängige Vorschau-Karten in den
|
||||
// Verbindungsergebnissen (Routenplaner).
|
||||
|
||||
const LiveMap = (() => {
|
||||
let map = null;
|
||||
let radarLayer = null;
|
||||
let routeLayer = null;
|
||||
let userLayer = null;
|
||||
let tripMarker = null;
|
||||
|
||||
let mode = 'radar';
|
||||
let radarPollHandle = null;
|
||||
let tripPollHandle = null;
|
||||
let currentTripId = null;
|
||||
let isolatedVehicleMeta = null; // { line, color, textColor } für die Banner-Anzeige
|
||||
let getModesParam = () => null; // wird von außen (ModeFilter) gesetzt
|
||||
|
||||
function init() {
|
||||
if (map) return;
|
||||
|
||||
map = L.map('live-map', { zoomControl: true }).setView([52.5200, 13.4050], 13);
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>-Mitwirkende',
|
||||
maxZoom: 19,
|
||||
}).addTo(map);
|
||||
|
||||
radarLayer = L.layerGroup().addTo(map);
|
||||
routeLayer = L.layerGroup().addTo(map);
|
||||
userLayer = L.layerGroup().addTo(map);
|
||||
|
||||
map.on('moveend', () => {
|
||||
if (mode === 'radar') refreshRadar();
|
||||
});
|
||||
}
|
||||
|
||||
function setModeFilter(fn) {
|
||||
getModesParam = fn;
|
||||
}
|
||||
|
||||
function vehicleIcon(label, bg, fg) {
|
||||
return L.divIcon({
|
||||
className: 'vehicle-marker',
|
||||
html: `<span style="background:${bg || '#F5A623'};color:${fg || '#fff'};">${label || '?'}</span>`,
|
||||
iconSize: [null, null],
|
||||
});
|
||||
}
|
||||
|
||||
function polylineToLatLngs(geojson) {
|
||||
if (!geojson || !geojson.features) return [];
|
||||
return geojson.features.map((f) => [f.geometry.coordinates[1], f.geometry.coordinates[0]]);
|
||||
}
|
||||
|
||||
// ---------- Radar-Modus (Standard: alle Fahrzeuge im Ausschnitt) ----------
|
||||
|
||||
async function refreshRadar() {
|
||||
if (!map || mode !== 'radar') return;
|
||||
const bounds = map.getBounds();
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await API.getRadar({
|
||||
north: bounds.getNorth().toFixed(5),
|
||||
west: bounds.getWest().toFixed(5),
|
||||
south: bounds.getSouth().toFixed(5),
|
||||
east: bounds.getEast().toFixed(5),
|
||||
modes: getModesParam(),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Radar-Fehler:', err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
radarLayer.clearLayers();
|
||||
data.vehicles.forEach((v) => {
|
||||
if (v.latitude == null || v.longitude == null) return;
|
||||
const marker = L.marker([v.latitude, v.longitude], { icon: vehicleIcon(v.line, v.color, v.textColor) });
|
||||
marker.bindPopup(
|
||||
`<strong>${v.line || 'Linie unbekannt'}</strong><br>${v.direction || ''}<br>` +
|
||||
`<small>Nächster Halt: ${v.nextStopover || '–'}</small>`,
|
||||
);
|
||||
// Klick auf eine Linie: isoliert sie (alle anderen Fahrzeuge ausblenden,
|
||||
// Streckenverlauf einzeichnen, live weiterverfolgen).
|
||||
marker.on('click', () => isolateVehicle(v));
|
||||
marker.addTo(radarLayer);
|
||||
});
|
||||
}
|
||||
|
||||
function startRadar() {
|
||||
init();
|
||||
setMode('radar');
|
||||
refreshRadar();
|
||||
if (radarPollHandle) clearInterval(radarPollHandle);
|
||||
radarPollHandle = setInterval(refreshRadar, 15000);
|
||||
setTimeout(() => map && map.invalidateSize(), 150);
|
||||
}
|
||||
|
||||
function stopRadar() {
|
||||
if (radarPollHandle) {
|
||||
clearInterval(radarPollHandle);
|
||||
radarPollHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Isolations-Modus (eine angeklickte Linie live verfolgen) ----------
|
||||
|
||||
async function refreshIsolated() {
|
||||
if (!map || mode !== 'isolated' || !currentTripId) return;
|
||||
const bounds = map.getBounds();
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await API.getRadar({
|
||||
north: bounds.getNorth().toFixed(5),
|
||||
west: bounds.getWest().toFixed(5),
|
||||
south: bounds.getSouth().toFixed(5),
|
||||
east: bounds.getEast().toFixed(5),
|
||||
tripId: currentTripId,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Radar-Fehler (isoliert):', err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
radarLayer.clearLayers();
|
||||
const v = data.vehicles[0];
|
||||
if (v && v.latitude != null && v.longitude != null) {
|
||||
const marker = L.marker([v.latitude, v.longitude], { icon: vehicleIcon(v.line, v.color, v.textColor) });
|
||||
marker.bindPopup(`<strong>${v.line || ''}</strong><br>${v.direction || ''}<br><small>Nächster Halt: ${v.nextStopover || '–'}</small>`);
|
||||
marker.addTo(radarLayer);
|
||||
}
|
||||
}
|
||||
|
||||
async function isolateVehicle(vehicle) {
|
||||
if (!vehicle || !vehicle.tripId) return;
|
||||
setMode('isolated');
|
||||
currentTripId = vehicle.tripId;
|
||||
isolatedVehicleMeta = { line: vehicle.line, color: vehicle.color, textColor: vehicle.textColor };
|
||||
updateIsolationBanner();
|
||||
|
||||
routeLayer.clearLayers();
|
||||
try {
|
||||
const trip = await API.getTrip(vehicle.tripId, { polyline: true });
|
||||
if (trip.polyline) {
|
||||
const latlngs = polylineToLatLngs(trip.polyline);
|
||||
if (latlngs.length > 0) {
|
||||
L.polyline(latlngs, { color: vehicle.color || '#F5A623', weight: 5, opacity: 0.85 }).addTo(routeLayer);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Trip-Polyline-Fehler:', err.message);
|
||||
}
|
||||
|
||||
refreshIsolated();
|
||||
if (radarPollHandle) clearInterval(radarPollHandle);
|
||||
radarPollHandle = setInterval(refreshIsolated, 10000);
|
||||
}
|
||||
|
||||
function clearIsolation() {
|
||||
currentTripId = null;
|
||||
isolatedVehicleMeta = null;
|
||||
updateIsolationBanner();
|
||||
startRadar();
|
||||
}
|
||||
|
||||
function updateIsolationBanner() {
|
||||
const banner = document.getElementById('map-isolation-banner');
|
||||
if (!banner) return;
|
||||
if (!isolatedVehicleMeta) {
|
||||
banner.classList.remove('is-visible');
|
||||
return;
|
||||
}
|
||||
const lineEl = document.getElementById('map-isolation-line');
|
||||
if (lineEl) {
|
||||
lineEl.textContent = isolatedVehicleMeta.line || '?';
|
||||
lineEl.style.background = isolatedVehicleMeta.color || '#F5A623';
|
||||
lineEl.style.color = isolatedVehicleMeta.textColor || '#fff';
|
||||
}
|
||||
banner.classList.add('is-visible');
|
||||
}
|
||||
|
||||
// ---------- Trip-Modus (bekanntes Fahrzeug aus einer Verbindung heraus verfolgen) ----------
|
||||
|
||||
async function refreshTrip() {
|
||||
if (!currentTripId) return;
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await API.getTrip(currentTripId, { polyline: !tripMarker });
|
||||
} catch (err) {
|
||||
console.error('Trip-Fehler:', err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.polyline && routeLayer) {
|
||||
routeLayer.clearLayers();
|
||||
const latlngs = polylineToLatLngs(data.polyline);
|
||||
if (latlngs.length > 0) {
|
||||
L.polyline(latlngs, { color: data.color || '#F5A623', weight: 4, opacity: 0.7 }).addTo(routeLayer);
|
||||
map.fitBounds(L.latLngBounds(latlngs), { padding: [30, 30] });
|
||||
}
|
||||
}
|
||||
|
||||
if (data.currentLocation) {
|
||||
const pos = [data.currentLocation.latitude, data.currentLocation.longitude];
|
||||
if (tripMarker) {
|
||||
tripMarker.setLatLng(pos);
|
||||
} else {
|
||||
tripMarker = L.marker(pos, { icon: vehicleIcon(data.line, data.color, data.textColor) }).addTo(routeLayer);
|
||||
}
|
||||
tripMarker.bindPopup(`<strong>${data.line || ''}</strong><br>→ ${data.direction || ''}`);
|
||||
}
|
||||
}
|
||||
|
||||
function trackTrip(tripId) {
|
||||
init();
|
||||
setMode('trip');
|
||||
currentTripId = tripId;
|
||||
tripMarker = null;
|
||||
routeLayer.clearLayers();
|
||||
|
||||
refreshTrip();
|
||||
if (tripPollHandle) clearInterval(tripPollHandle);
|
||||
tripPollHandle = setInterval(refreshTrip, 10000);
|
||||
setTimeout(() => map && map.invalidateSize(), 150);
|
||||
}
|
||||
|
||||
function stopTracking() {
|
||||
if (tripPollHandle) {
|
||||
clearInterval(tripPollHandle);
|
||||
tripPollHandle = null;
|
||||
}
|
||||
currentTripId = null;
|
||||
tripMarker = null;
|
||||
}
|
||||
|
||||
// ---------- Standort ----------
|
||||
|
||||
function locateUser() {
|
||||
init();
|
||||
if (!navigator.geolocation) {
|
||||
alert('Geolocation wird von diesem Browser nicht unterstützt.');
|
||||
return;
|
||||
}
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos) => {
|
||||
const { latitude, longitude } = pos.coords;
|
||||
userLayer.clearLayers();
|
||||
L.marker([latitude, longitude], {
|
||||
icon: L.divIcon({ className: 'user-location-marker', iconSize: [16, 16] }),
|
||||
}).addTo(userLayer);
|
||||
map.setView([latitude, longitude], 16);
|
||||
},
|
||||
(err) => alert('Standort konnte nicht ermittelt werden: ' + err.message),
|
||||
{ enableHighAccuracy: true, timeout: 10000 },
|
||||
);
|
||||
}
|
||||
|
||||
// ---------- Modus-Verwaltung ----------
|
||||
|
||||
function setMode(newMode) {
|
||||
if (mode === newMode) return;
|
||||
if (mode === 'radar') stopRadar();
|
||||
if (mode === 'isolated') stopRadar();
|
||||
if (mode === 'trip') stopTracking();
|
||||
if (newMode === 'radar') routeLayer.clearLayers();
|
||||
mode = newMode;
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
// Rückwärtskompatibler Einstieg: Karten-Tab wurde geöffnet.
|
||||
if (mode === 'radar' || mode === 'idle') startRadar();
|
||||
else if (map) setTimeout(() => map.invalidateSize(), 150);
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
stopRadar();
|
||||
stopTracking();
|
||||
}
|
||||
|
||||
// ---------- Mini-Karte für Verbindungsvorschau (Routenplaner) ----------
|
||||
|
||||
function createMiniMap(containerId, journey) {
|
||||
const miniMap = L.map(containerId, {
|
||||
zoomControl: false,
|
||||
attributionControl: false,
|
||||
dragging: true,
|
||||
scrollWheelZoom: false,
|
||||
tap: true,
|
||||
}).setView([52.5200, 13.4050], 12);
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap-Mitwirkende',
|
||||
maxZoom: 19,
|
||||
}).addTo(miniMap);
|
||||
|
||||
const allPoints = [];
|
||||
(journey.legs || []).forEach((leg) => {
|
||||
if (!leg.polyline) return;
|
||||
const latlngs = polylineToLatLngs(leg.polyline);
|
||||
if (latlngs.length === 0) return;
|
||||
L.polyline(latlngs, {
|
||||
color: leg.walking ? '#8E99A6' : leg.color || '#F5A623',
|
||||
weight: leg.walking ? 3 : 4,
|
||||
dashArray: leg.walking ? '5, 7' : null,
|
||||
opacity: 0.9,
|
||||
}).addTo(miniMap);
|
||||
allPoints.push(...latlngs);
|
||||
});
|
||||
|
||||
if (allPoints.length > 0) {
|
||||
miniMap.fitBounds(L.latLngBounds(allPoints), { padding: [16, 16] });
|
||||
}
|
||||
|
||||
setTimeout(() => miniMap.invalidateSize(), 50);
|
||||
return miniMap;
|
||||
}
|
||||
|
||||
return {
|
||||
startPolling,
|
||||
stopPolling,
|
||||
trackTrip,
|
||||
setModeFilter,
|
||||
clearIsolation,
|
||||
locateUser,
|
||||
createMiniMap,
|
||||
};
|
||||
})();
|
||||
Reference in new issue
Block a user