diff --git a/app.js b/app.js index 0f1bede..55221f0 100644 --- a/app.js +++ b/app.js @@ -36,31 +36,6 @@ const symbols = { regional: '🚆', express: '🚄', longDistance: '🚅', ferry: '🛥️' }; -// ============================= -// Aktive Filter -// ============================= -let activeProducts = Array.from(document.querySelectorAll('#controls input[type="checkbox"]')) - .filter(c => c.checked) - .map(c => c.value); - -// Checkbox Event -document.querySelectorAll('#controls input[type="checkbox"]').forEach(cb => { - cb.addEventListener('change', () => { - activeProducts = Array.from(document.querySelectorAll('#controls input[type="checkbox"]')) - .filter(c => c.checked) - .map(c => c.value); - - Object.values(vehicleMarkers).forEach(marker => { - const prod = marker.options.vehicleProduct || ''; - if (activeProducts.includes(prod)) { - if (!map.hasLayer(marker)) map.addLayer(marker); - } else { - if (map.hasLayer(marker)) map.removeLayer(marker); - } - }); - }); -}); - // ============================= // Hilfsfunktionen // ============================= @@ -78,7 +53,7 @@ function getLineColor(lineName, product) { } function createDivIcon(vehicle) { - const product = vehicle.line?.product || ''; + const product = vehicle.line?.product; const lineName = vehicle.line?.name || '?'; const symbol = symbols[product] || '❓'; @@ -104,9 +79,7 @@ function createDivIcon(vehicle) { `; - const icon = L.divIcon({ html: iconHtml, className: '', iconSize: [30, 40], iconAnchor: [15, 20] }); - icon.product = product; - return icon; + return L.divIcon({ html: iconHtml, className: '', iconSize: [30, 40], iconAnchor: [15, 20] }); } const lineColor = getLineColor(lineName, product); @@ -119,9 +92,7 @@ function createDivIcon(vehicle) { `; - const icon = L.divIcon({ html: iconHtml, className: '', iconSize: [30, 40], iconAnchor: [15, 20] }); - icon.product = product; - return icon; + return L.divIcon({ html: iconHtml, className: '', iconSize: [30, 40], iconAnchor: [15, 20] }); } // ============================= @@ -149,18 +120,16 @@ async function loadVehicles() { if (vehicleMarkers[id]) { vehicleMarkers[id].setLatLng([loc.latitude, loc.longitude]); } else { - const marker = L.marker([loc.latitude, loc.longitude], { icon }); - marker.options.vehicleProduct = vehicle.line?.product || ''; - if (activeProducts.includes(marker.options.vehicleProduct)) { - marker.addTo(map); - } + const marker = L.marker([loc.latitude, loc.longitude], { icon }) + .bindPopup(`Linie: ${vehicle.line?.name || 'unbekannt'}
Typ: ${vehicle.line?.product || 'unbekannt'}
Richtung: ${vehicle.direction || 'unbekannt'}`) + .addTo(vehiclesLayer); vehicleMarkers[id] = marker; } }); Object.keys(vehicleMarkers).forEach(id => { if (!newIds.has(id)) { - if (map.hasLayer(vehicleMarkers[id])) map.removeLayer(vehicleMarkers[id]); + vehiclesLayer.removeLayer(vehicleMarkers[id]); delete vehicleMarkers[id]; } }); @@ -188,3 +157,69 @@ function locateAndLoad() { locateAndLoad(); 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)); +}); diff --git a/index.html b/index.html index 4fc4d5c..8dcc62a 100644 --- a/index.html +++ b/index.html @@ -8,8 +8,6 @@ -
-
@@ -17,7 +15,9 @@ +
+
diff --git a/old/README.md b/old/README.md deleted file mode 100644 index 9030bd8..0000000 --- a/old/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# VBB-Live-Maps -VBB Live Maps ist eine interaktive Live-Karte, die Fahrzeuge des Berliner Nahverkehrs (z. B. Busse, U-Bahnen, S-Bahnen etc.) in Echtzeit anzeigt. - -https://vbb-live-maps.netlify.app/ diff --git a/old/VBB-Live-Maps-BG/app.js b/old/VBB-Live-Maps-BG/app.js deleted file mode 100644 index 31fc441..0000000 --- a/old/VBB-Live-Maps-BG/app.js +++ /dev/null @@ -1,224 +0,0 @@ -// ============================= -// Karte initialisieren -// ============================= -const map = L.map('map', { zoomControl: false }).setView([52.52, 13.405], 13); - -L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 19, - attribution: '© OpenStreetMap contributors' -}).addTo(map); - - -const vehiclesLayer = L.layerGroup().addTo(map); -const vehicleMarkers = {}; - -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', - 'S1': '#DA6BA2', 'S15': '#DA6BA2', 'S16': '#DA6BA2', 'U0': '#DA6BA2', - '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: '🛥️' -}; - -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'; - return '#666'; -} - -function createDivIcon(vehicle) { - const product = vehicle.line?.product; - const lineName = vehicle.line?.name || '?'; - const symbol = symbols[product] || '❓'; - - if(lineName === 'U12') { - const iconHtml = ` -
- - - - - - - - - - - -
- ${symbol} -
-
- U12 -
-
- `; - return L.divIcon({ - html: iconHtml, - className: '', - iconSize: [30, 40], - iconAnchor: [15, 20] - }); - } - - const lineColor = getLineColor(lineName, product); - const labelStyle = `background: ${lineColor}; color: white; padding: 2px 6px; border-radius: 3px; display: inline-block; font-weight: bold; font-size: 12px; user-select: none; font-family: Arial, sans-serif;`; - - const iconHtml = ` -
-
${symbol}
-
${lineName}
-
- `; - - return L.divIcon({ - html: iconHtml, - className: '', - iconSize: [30, 40], - iconAnchor: [15, 20] - }); -} - -async function loadVehicles() { - const bounds = map.getBounds(); - const bbox = `north=${bounds.getNorth()}&west=${bounds.getWest()}&south=${bounds.getSouth()}&east=${bounds.getEast()}`; - - try { - const res = await fetch(`https://v6.vbb.transport.rest/radar?${bbox}&results=200&duration=30`); - const result = await res.json(); - const data = result.movements || []; - - const newIds = new Set(); - - data.forEach(vehicle => { - const id = vehicle.tripId; - const loc = vehicle.location; - if (!loc || !loc.latitude || !loc.longitude) return; - - newIds.add(id); - const icon = createDivIcon(vehicle); - - if (vehicleMarkers[id]) { - 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'} - `) - .addTo(vehiclesLayer); - vehicleMarkers[id] = marker; - } - }); - - Object.keys(vehicleMarkers).forEach(id => { - if (!newIds.has(id)) { - vehiclesLayer.removeLayer(vehicleMarkers[id]); - delete vehicleMarkers[id]; - } - }); - } catch (e) { - console.error('Fehler beim Laden der Fahrzeuge:', e); - } -} - -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); - loadVehicles(); - }, () => { - map.setView([52.52, 13.405], 13); - loadVehicles(); - }); -} - -locateAndLoad(); -setInterval(loadVehicles, 610); -map.on('moveend', loadVehicles); - -// ==================== -// Uhr unten rechts -// ==================== -const ssTimeEl = document.getElementById('ss-time'); -const ssDateEl = document.getElementById('ss-date'); - -function updateClock() { - const now = new Date(); - ssTimeEl.textContent = now.toLocaleTimeString('de-DE', { hour12: false }); - ssDateEl.textContent = now.toLocaleDateString('de-DE', { - weekday: 'long', - year: 'numeric', - month: 'long', - day: '2-digit' - }); -} - -updateClock(); -setInterval(updateClock, 1000); - - - - - - - - - diff --git a/old/VBB-Live-Maps-BG/index.html b/old/VBB-Live-Maps-BG/index.html deleted file mode 100644 index 5e7fd13..0000000 --- a/old/VBB-Live-Maps-BG/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - VBB Live Tracker - - - - - -
- - -
-
--:--:--
-
----
-
- - - - - diff --git a/old/VBB-Live-Maps-BG/style.css b/old/VBB-Live-Maps-BG/style.css deleted file mode 100644 index e85d1cd..0000000 --- a/old/VBB-Live-Maps-BG/style.css +++ /dev/null @@ -1,69 +0,0 @@ -/* ==================== */ -/* Grundlayout */ -/* ==================== */ -html, body, #map { - height: 100%; - margin: 0; - padding: 0; -} - -.leaflet-marker-icon { - transition: transform 1s linear; -} - -.line-label { - padding: 2px 6px; - border-radius: 3px; - font-weight: bold; - font-size: 10px; - display: inline-block; - color: white; - margin-top: 2px; - user-select: none; - font-family: Arial, sans-serif; -} - -.user-location-icon { - background-color: #007bff; - border: 2px solid white; - border-radius: 50%; - width: 12px; - height: 12px; - box-shadow: 0 0 6px rgba(0, 123, 255, 0.6); -} - -/* ==================== */ -/* Uhr unten rechts */ -/* ==================== */ -#clock { - position: fixed; - right: 40px; - bottom: 40px; - z-index: 9999; - text-align: right; - font-family: "Comic Sans MS", Comic Sans, cursive; /* Comic Sans einfügen */ - color: #ffffff; - text-shadow: 0 0 8px rgba(0,0,0,0.7); - user-select: none; - - /* Hintergrund-Box */ - background: rgba(0, 0, 0, 0.6); - padding: 16px 24px; - border-radius: 12px; - backdrop-filter: blur(4px); - -} - - -#clock .time { - font-size: clamp(42px, 6vw, 72px); - line-height: 1.1; - font-weight: 700; -} - -#clock .date { - font-size: clamp(18px, 3vw, 28px); - line-height: 1.2; - opacity: 0.9; -} - diff --git a/old/app.js b/old/app.js deleted file mode 100644 index 55221f0..0000000 --- a/old/app.js +++ /dev/null @@ -1,225 +0,0 @@ -// ============================= -// Karte initialisieren -// ============================= -const map = L.map('map').setView([52.52, 13.405], 13); - -L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 19, - 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', - 'S1': '#DA6BA2', 'S15': '#DA6BA2', 'S16': '#DA6BA2', 'U0': '#DA6BA2', - '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: '🛥️' -}; - -// ============================= -// 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'; - return '#666'; -} - -function createDivIcon(vehicle) { - const product = vehicle.line?.product; - const lineName = vehicle.line?.name || '?'; - const symbol = symbols[product] || '❓'; - - if(lineName === 'U12') { - const iconHtml = ` -
- - - - - - - - - - - -
- ${symbol} -
-
- U12 -
-
- `; - return L.divIcon({ html: iconHtml, className: '', iconSize: [30, 40], iconAnchor: [15, 20] }); - } - - const lineColor = getLineColor(lineName, product); - const labelStyle = `background: ${lineColor}; color: white; padding: 2px 6px; border-radius: 3px; display: inline-block; font-weight: bold; font-size: 12px; user-select: none; font-family: Arial, sans-serif;`; - - const iconHtml = ` -
-
${symbol}
-
${lineName}
-
- `; - - 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()}`; - - try { - const res = await fetch(`https://v6.vbb.transport.rest/radar?${bbox}&results=100&duration=30`); - const result = await res.json(); - const data = result.movements || []; - - const newIds = new Set(); - - data.forEach(vehicle => { - const id = vehicle.tripId; - const loc = vehicle.location; - if (!loc || !loc.latitude || !loc.longitude) return; - - newIds.add(id); - const icon = createDivIcon(vehicle); - - if (vehicleMarkers[id]) { - 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'}`) - .addTo(vehiclesLayer); - vehicleMarkers[id] = marker; - } - }); - - Object.keys(vehicleMarkers).forEach(id => { - if (!newIds.has(id)) { - vehiclesLayer.removeLayer(vehicleMarkers[id]); - delete vehicleMarkers[id]; - } - }); - } catch (e) { - console.error('Fehler beim Laden der Fahrzeuge:', e); - } -} - -// ============================= -// Geolocation & Auto-Update -// ============================= -function locateAndLoad() { - if (!navigator.geolocation) { - loadVehicles(); - return; - } - navigator.geolocation.getCurrentPosition(pos => { - map.setView([pos.coords.latitude, pos.coords.longitude], 14); - loadVehicles(); - }, () => { - loadVehicles(); - }); -} - -locateAndLoad(); -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)); -}); diff --git a/old/app.js.backup b/old/app.js.backup deleted file mode 100644 index b095f3c..0000000 --- a/old/app.js.backup +++ /dev/null @@ -1,192 +0,0 @@ -const map = L.map('map').setView([52.52, 13.405], 13); - -L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 19, - attribution: '© OpenStreetMap contributors' -}).addTo(map); - -const vehiclesLayer = L.layerGroup().addTo(map); -const vehicleMarkers = {}; - -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', - 'S1': '#DA6BA2', 'S15': '#DA6BA2', 'S16': '#DA6BA2', 'U0': '#DA6BA2', - '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: '🛥️' -}; - -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'; - return '#666'; -} - -function createDivIcon(vehicle) { - const product = vehicle.line?.product; - const lineName = vehicle.line?.name || '?'; - const symbol = symbols[product] || '❓'; - - if(lineName === 'U12') { - const iconHtml = ` -
- - - - - - - - - - - -
- ${symbol} -
-
- U12 -
-
- `; - return L.divIcon({ - html: iconHtml, - className: '', - iconSize: [30, 40], - iconAnchor: [15, 20] - }); - } - - const lineColor = getLineColor(lineName, product); - const labelStyle = `background: ${lineColor}; color: white; padding: 2px 6px; border-radius: 3px; display: inline-block; font-weight: bold; font-size: 12px; user-select: none; font-family: Arial, sans-serif;`; - - const iconHtml = ` -
-
${symbol}
-
${lineName}
-
- `; - - return L.divIcon({ - html: iconHtml, - className: '', - iconSize: [30, 40], - iconAnchor: [15, 20] - }); -} - -async function loadVehicles() { - const bounds = map.getBounds(); - const bbox = `north=${bounds.getNorth()}&west=${bounds.getWest()}&south=${bounds.getSouth()}&east=${bounds.getEast()}`; - - try { - const res = await fetch(`https://v6.vbb.transport.rest/radar?${bbox}&results=100&duration=30`); - const result = await res.json(); - const data = result.movements || []; - - const newIds = new Set(); - - data.forEach(vehicle => { - const id = vehicle.tripId; - const loc = vehicle.location; - if (!loc || !loc.latitude || !loc.longitude) return; - - newIds.add(id); - const icon = createDivIcon(vehicle); - - if (vehicleMarkers[id]) { - 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'} - `) - .addTo(vehiclesLayer); - vehicleMarkers[id] = marker; - } - }); - - Object.keys(vehicleMarkers).forEach(id => { - if (!newIds.has(id)) { - vehiclesLayer.removeLayer(vehicleMarkers[id]); - delete vehicleMarkers[id]; - } - }); - } catch (e) { - console.error('Fehler beim Laden der Fahrzeuge:', e); - } -} - -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); - loadVehicles(); - }, () => { - map.setView([52.52, 13.405], 13); - loadVehicles(); - }); -} - -locateAndLoad(); -setInterval(loadVehicles, 610); -map.on('moveend', loadVehicles); - diff --git a/old/index.html b/old/index.html deleted file mode 100644 index 8dcc62a..0000000 --- a/old/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - VBB Live Tracker - - - - -
- - - - - - - -
-
- - - - - diff --git a/old/style.css b/old/style.css deleted file mode 100644 index 030147b..0000000 --- a/old/style.css +++ /dev/null @@ -1,43 +0,0 @@ -html, body, #map { - height: 100%; - margin: 0; - padding: 0; -} - -#controls { - position: absolute; - top: 10px; - right: 10px; - background: rgba(255,255,255,0.9); - padding: 8px 12px; - border-radius: 5px; - z-index: 1000; - font-family: Arial, sans-serif; - display: flex; - flex-wrap: wrap; /* wenn zu viele, umbrechen */ - gap: 8px; -} - -#controls label { - display: flex; - align-items: center; - gap: 4px; - font-size: 12px; - cursor: pointer; -} - -.leaflet-marker-icon { - transition: transform 1s linear; -} - -.line-label { - padding: 2px 6px; - border-radius: 3px; - font-weight: bold; - font-size: 10px; - display: inline-block; - color: white; - margin-top: 2px; - user-select: none; - font-family: Arial, sans-serif; -} diff --git a/style.css b/style.css index 31a3213..030147b 100644 --- a/style.css +++ b/style.css @@ -8,13 +8,13 @@ html, body, #map { position: absolute; top: 10px; right: 10px; - background: rgba(255, 255, 255, 0.9); + background: rgba(255,255,255,0.9); padding: 8px 12px; border-radius: 5px; z-index: 1000; font-family: Arial, sans-serif; display: flex; - flex-wrap: wrap; + flex-wrap: wrap; /* wenn zu viele, umbrechen */ gap: 8px; }