diff --git a/app.js b/app.js index 095c6ad..55221f0 100644 --- a/app.js +++ b/app.js @@ -1,3 +1,6 @@ +// ============================= +// Karte initialisieren +// ============================= const map = L.map('map').setView([52.52, 13.405], 13); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { @@ -5,60 +8,44 @@ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: 'ยฉ OpenStreetMap contributors' }).addTo(map); +// Layer fรผr Fahrzeuge const vehiclesLayer = L.layerGroup().addTo(map); const vehicleMarkers = {}; +// ============================= +// Farben & Symbole +// ============================= const lineColors = { 'S45': '#CD9C53', 'S46': '#CD9C53', 'S47': '#CD9C53', 'S6': '#CD9C53', 'S65': '#CD9C53', 'S66': '#CD9C53', - 'S42': '#CB6418', - 'S4': '#AD5937', 'S41': '#AD5937', - 'U5': '#7E5330', 'U55': '#7E5330', - 'S9': '#992746', 'S95': '#992746', 'S96': '#992746', - 'U12': ['#7DAD4C', '#DA421E'], - 'U2': '#DA421E', - 'S3': '#EB7405', 'S5': '#EB7405', 'S55': '#EB7405', 'S56': '#EB7405', - 'U9': '#F3791D', - 'S3-wannsee': '#F0D722', 'U4': '#F0D722', - 'U1': '#7DAD4C', 'U15': '#7DAD4C', - 'S8': '#66AA22', 'S85': '#66AA22', 'S86': '#66AA22', - 'S2': '#007734', 'S21': '#007734', 'S25': '#007734', 'S26': '#007734', - 'U3': '#16683D', 'U35': '#16683D', - 'S3-charlottenburg': '#009BD5', 'U7': '#009BD5', - 'S10': '#0082C4', 'S19': '#0082C4', - 'S3-berlin-schoenefeld': '#0066AD', 'S35': '#0066AD', 'S36': '#0066AD', - 'U8': '#224F86', - 'S7': '#816DA6', 'S75': '#816DA6', 'S76': '#816DA6', - 'U6': '#8C6DAB', + 'S42': '#CB6418', 'S4': '#AD5937', 'S41': '#AD5937', 'U5': '#7E5330', 'U55': '#7E5330', + 'S9': '#992746', 'S95': '#992746', 'S96': '#992746', 'U12': ['#7DAD4C', '#DA421E'], 'U2': '#DA421E', + 'S3': '#EB7405', 'S5': '#EB7405', 'S55': '#EB7405', 'S56': '#EB7405', 'U9': '#F3791D', + 'S3-wannsee': '#F0D722', 'U4': '#F0D722', 'U1': '#7DAD4C', 'U15': '#7DAD4C', + 'S8': '#66AA22', 'S85': '#66AA22', 'S86': '#66AA22', 'S2': '#007734', 'S21': '#007734', + 'S25': '#007734', 'S26': '#007734', 'U3': '#16683D', 'U35': '#16683D', + 'S3-charlottenburg': '#009BD5', 'U7': '#009BD5', 'S10': '#0082C4', 'S19': '#0082C4', + 'S3-berlin-schoenefeld': '#0066AD', 'S35': '#0066AD', 'S36': '#0066AD', 'U8': '#224F86', + 'S7': '#816DA6', 'S75': '#816DA6', 'S76': '#816DA6', 'U6': '#8C6DAB', 'S1': '#DA6BA2', 'S15': '#DA6BA2', 'S16': '#DA6BA2', 'U0': '#DA6BA2', - 'U10': '#808080', 'U11': '#808080', - 'F10': '#528DBA', - 'F11': '#528DBA', - 'F12': '#528DBA', - 'F21': '#528DBA', - 'F23': '#528DBA', - 'F24': '#528DBA' + 'U10': '#808080', 'U11': '#808080', 'F10': '#528DBA', 'F11': '#528DBA', + 'F12': '#528DBA', 'F21': '#528DBA', 'F23': '#528DBA', 'F24': '#528DBA' }; const symbols = { - bus: '๐ŸšŒ', - tram: '๐ŸšŽ', - subway: '๐Ÿš‰', - suburban: '๐Ÿš‹', - regional: '๐Ÿš†', - express: '๐Ÿš„', - longDistance: '๐Ÿš…', - ferry: '๐Ÿ›ฅ๏ธ' + bus: '๐ŸšŒ', tram: '๐ŸšŽ', subway: '๐Ÿš‰', suburban: '๐Ÿš‹', + regional: '๐Ÿš†', express: '๐Ÿš„', longDistance: '๐Ÿš…', ferry: '๐Ÿ›ฅ๏ธ' }; +// ============================= +// Hilfsfunktionen +// ============================= function getLineColor(lineName, product) { if(lineName === 'U12') return null; - if(lineName.startsWith('S3 ')) { if(lineName.toLowerCase().includes('wannsee')) return lineColors['S3-wannsee']; if(lineName.toLowerCase().includes('charlottenburg') || lineName.toLowerCase().includes('schรถnefeld')) return lineColors['S3-berlin-schoenefeld']; return lineColors['S3']; } - if(lineColors[lineName]) return lineColors[lineName]; if(product === 'suburban') return '#CD9C53'; if(product === 'subway') return '#7DAD4C'; @@ -92,12 +79,7 @@ function createDivIcon(vehicle) { `; - return L.divIcon({ - html: iconHtml, - className: '', - iconSize: [30, 40], - iconAnchor: [15, 20] - }); + return L.divIcon({ html: iconHtml, className: '', iconSize: [30, 40], iconAnchor: [15, 20] }); } const lineColor = getLineColor(lineName, product); @@ -110,14 +92,12 @@ function createDivIcon(vehicle) { `; - return L.divIcon({ - html: iconHtml, - className: '', - iconSize: [30, 40], - iconAnchor: [15, 20] - }); + return L.divIcon({ html: iconHtml, className: '', iconSize: [30, 40], iconAnchor: [15, 20] }); } +// ============================= +// Fahrzeuge laden +// ============================= async function loadVehicles() { const bounds = map.getBounds(); const bbox = `north=${bounds.getNorth()}&west=${bounds.getWest()}&south=${bounds.getSouth()}&east=${bounds.getEast()}`; @@ -141,11 +121,7 @@ async function loadVehicles() { vehicleMarkers[id].setLatLng([loc.latitude, loc.longitude]); } else { const marker = L.marker([loc.latitude, loc.longitude], { icon }) - .bindPopup(` - Linie: ${vehicle.line?.name || 'unbekannt'}
- Typ: ${vehicle.line?.product || 'unbekannt'}
- Richtung: ${vehicle.direction || 'unbekannt'} - `) + .bindPopup(`Linie: ${vehicle.line?.name || 'unbekannt'}
Typ: ${vehicle.line?.product || 'unbekannt'}
Richtung: ${vehicle.direction || 'unbekannt'}`) .addTo(vehiclesLayer); vehicleMarkers[id] = marker; } @@ -162,31 +138,88 @@ async function loadVehicles() { } } +// ============================= +// Geolocation & Auto-Update +// ============================= function locateAndLoad() { if (!navigator.geolocation) { - alert('Geolocation nicht unterstรผtzt'); loadVehicles(); return; } navigator.geolocation.getCurrentPosition(pos => { - const { latitude, longitude } = pos.coords; - map.setView([latitude, longitude], 14); - - const locationIcon = L.divIcon({ - className: 'user-location-icon', - iconSize: [12, 12], - iconAnchor: [6, 6] - }); - - L.marker([latitude, longitude], { icon: locationIcon }).addTo(map); + map.setView([pos.coords.latitude, pos.coords.longitude], 14); loadVehicles(); }, () => { - map.setView([52.52, 13.405], 13); loadVehicles(); }); } locateAndLoad(); -setInterval(loadVehicles, 610); +setInterval(loadVehicles, 30000); map.on('moveend', loadVehicles); +// ============================= +// Bahnhรถfe mit Live-Abfahrten +// ============================= +const stations = [ + { name: "Berlin Zoologischer Garten", id: "900023201", coords: [52.5069, 13.3326] }, + { name: "Berlin Alexanderplatz", id: "900100003", coords: [52.5215, 13.4115] }, + { name: "Berlin Gesundbrunnen", id: "900007102", coords: [52.5483, 13.3889] }, + { name: "Berlin Spandau", id: "900029101", coords: [52.5341, 13.1977] }, + { name: "Berlin Sรผdkreuz", id: "900058101", coords: [52.4752, 13.3653] }, + { name: "Berlin Ostbahnhof", id: "900100004", coords: [52.5100, 13.4340] } +]; + +async function loadStationDepartures(stationId, marker, stationName) { + const symbols = { + bus: '๐ŸšŒ', tram: '๐ŸšŽ', subway: '๐Ÿš‰', suburban: '๐Ÿš‹', + regional: '๐Ÿš†', express: '๐Ÿš„', longDistance: '๐Ÿš…', ferry: '๐Ÿ›ฅ๏ธ' + }; + + try { + const res = await fetch(`https://v6.vbb.transport.rest/stops/${stationId}/departures?duration=30&results=10`); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const data = await res.json(); + const departures = data.departures || []; + + let html = `${stationName}
`; + html += ``; + html += ``; + + departures.forEach((dep, index) => { + const line = dep.line?.name || "?"; + const product = dep.line?.product; + const icon = symbols[product] || 'โ“'; + const direction = dep.direction || "?"; + const when = dep.when ? new Date(dep.when) : null; + const planned = dep.plannedWhen ? new Date(dep.plannedWhen) : null; + const delay = when && planned ? Math.round((when - planned) / 60000) : 0; + const timeStr = when ? when.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" }) : "n/a"; + const delayStr = delay > 0 ? `+${delay} min` : ""; + + const color = getLineColor(line, product); + const bg = index % 2 === 0 ? '#f9f9f9' : '#fff'; + + html += ` + + + + `; + }); + + html += `
LinieRichtungZeit
${icon} ${line}${direction}${timeStr} ${delayStr}
`; + marker.setPopupContent(html).openPopup(); + } catch (err) { + marker.setPopupContent(`${stationName}
Fehler beim Laden`); + console.error(err); + } +} + + +stations.forEach(station => { + const marker = L.marker(station.coords) + .addTo(map) + .bindPopup(`${station.name}
Lade Abfahrten...`); + + marker.on('click', () => loadStationDepartures(station.id, marker, station.name)); +});