From b504e305255c276db49079dcb0f0f0749ba57a61 Mon Sep 17 00:00:00 2001 From: Serverlele30 <46974281+Serverlele30@users.noreply.github.com> Date: Wed, 2 Jul 2025 23:17:58 +0200 Subject: [PATCH] Add files via upload --- app.js | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 16 +++++ style.css | 21 ++++++ 3 files changed, 223 insertions(+) create mode 100644 app.js create mode 100644 index.html create mode 100644 style.css diff --git a/app.js b/app.js new file mode 100644 index 0000000..ac93d90 --- /dev/null +++ b/app.js @@ -0,0 +1,186 @@ +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 = {}; + +// BVG / DB S-Bahn Farben mit Hex + Namen +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' +}; + +// Produkt-Symbole +const symbols = { + bus: '🚌', + tram: '🚎', + subway: '🚉', + suburban: '🚋', + regional: '🚆', + express: '🚄', + longDistance: '🚅' +}; + +// Hilfsfunktion, um Farbe für Linie zu bestimmen +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'; +} + +// Erzeuge DivIcon mit farbiger Linie als Hintergrund +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); + L.circle([latitude, longitude], { radius: 1000, color: 'blue', fillOpacity: 0.1 }).addTo(map); + loadVehicles(); + }, () => { + map.setView([52.52, 13.405], 13); + loadVehicles(); + }); +} + +locateAndLoad(); +setInterval(loadVehicles, 610); +map.on('moveend', loadVehicles); diff --git a/index.html b/index.html new file mode 100644 index 0000000..9049967 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + VBB Live Tracker + + + + +
+ + + + + diff --git a/style.css b/style.css new file mode 100644 index 0000000..7b02d23 --- /dev/null +++ b/style.css @@ -0,0 +1,21 @@ +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; +}