From 6f4bd86c1806c32295f1f63a34953bf1f1dcd992 Mon Sep 17 00:00:00 2001
From: Serverlele30 <46974281+Serverlele30@users.noreply.github.com>
Date: Fri, 22 Aug 2025 09:50:14 +0200
Subject: [PATCH] Add files via upload
---
old/README.md | 4 +
old/VBB-Live-Maps-BG/app.js | 224 +++++++++++++++++++++++++++++++
old/VBB-Live-Maps-BG/index.html | 23 ++++
old/VBB-Live-Maps-BG/style.css | 69 ++++++++++
old/app.js | 225 ++++++++++++++++++++++++++++++++
old/app.js.backup | 192 +++++++++++++++++++++++++++
old/index.html | 25 ++++
old/style.css | 43 ++++++
8 files changed, 805 insertions(+)
create mode 100644 old/README.md
create mode 100644 old/VBB-Live-Maps-BG/app.js
create mode 100644 old/VBB-Live-Maps-BG/index.html
create mode 100644 old/VBB-Live-Maps-BG/style.css
create mode 100644 old/app.js
create mode 100644 old/app.js.backup
create mode 100644 old/index.html
create mode 100644 old/style.css
diff --git a/old/README.md b/old/README.md
new file mode 100644
index 0000000..9030bd8
--- /dev/null
+++ b/old/README.md
@@ -0,0 +1,4 @@
+# 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
new file mode 100644
index 0000000..31fc441
--- /dev/null
+++ b/old/VBB-Live-Maps-BG/app.js
@@ -0,0 +1,224 @@
+// =============================
+// 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
new file mode 100644
index 0000000..5e7fd13
--- /dev/null
+++ b/old/VBB-Live-Maps-BG/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+ VBB Live Tracker
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/old/VBB-Live-Maps-BG/style.css b/old/VBB-Live-Maps-BG/style.css
new file mode 100644
index 0000000..e85d1cd
--- /dev/null
+++ b/old/VBB-Live-Maps-BG/style.css
@@ -0,0 +1,69 @@
+/* ==================== */
+/* 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
new file mode 100644
index 0000000..55221f0
--- /dev/null
+++ b/old/app.js
@@ -0,0 +1,225 @@
+// =============================
+// 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 += `| Linie | Richtung | Zeit |
`;
+
+ 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 += `
+ | ${icon} ${line} |
+ ${direction} |
+ ${timeStr} ${delayStr} |
+
`;
+ });
+
+ html += `
`;
+ 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
new file mode 100644
index 0000000..b095f3c
--- /dev/null
+++ b/old/app.js.backup
@@ -0,0 +1,192 @@
+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
new file mode 100644
index 0000000..8dcc62a
--- /dev/null
+++ b/old/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ VBB Live Tracker
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/old/style.css b/old/style.css
new file mode 100644
index 0000000..030147b
--- /dev/null
+++ b/old/style.css
@@ -0,0 +1,43 @@
+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;
+}