+
${symbol}
-
`;
- return L.divIcon({ html: iconHtml, className: '', iconSize: [30, 40], iconAnchor: [15, 20] });
+ return L.divIcon({ html: iconHtml, className: 'vehicle-marker', iconSize: [30, 42], iconAnchor: [15, 21] });
}
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}
+
+
${symbol}
+
${lineName}
`;
- return L.divIcon({ html: iconHtml, className: '', iconSize: [30, 40], iconAnchor: [15, 20] });
+ return L.divIcon({ html: iconHtml, className: 'vehicle-marker', iconSize: [30, 42], iconAnchor: [15, 21] });
}
// =============================
-// Fahrzeuge laden
+// Update Stats
+// =============================
+function updateStats(vehicleCount) {
+ document.getElementById('vehicleCount').textContent = vehicleCount;
+ const now = new Date();
+ document.getElementById('updateTime').textContent = now.toLocaleTimeString('de-DE', {
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+}
+
+// =============================
+// Load Vehicles
// =============================
async function loadVehicles() {
const bounds = map.getBounds();
@@ -117,11 +170,32 @@ async function loadVehicles() {
newIds.add(id);
const icon = createDivIcon(vehicle);
+ const lineName = vehicle.line?.name || 'Unbekannt';
+ const product = vehicle.line?.product || 'unknown';
+ const productName = productNames[product] || 'Unbekannt';
+ const direction = vehicle.direction || 'Unbekannt';
+
+ const popupContent = `
+
+
+
+ Typ
+ ${productName}
+
+
+ Richtung
+ ${direction}
+
+
+ `;
+
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'}`)
+ .bindPopup(popupContent, { maxWidth: 300 })
.addTo(vehiclesLayer);
vehicleMarkers[id] = marker;
}
@@ -133,14 +207,38 @@ async function loadVehicles() {
delete vehicleMarkers[id];
}
});
+
+ updateStats(newIds.size);
} catch (e) {
console.error('Fehler beim Laden der Fahrzeuge:', e);
}
}
// =============================
-// Geolocation & Auto-Update
+// Geolocation
// =============================
+let locationMarker = null;
+
+const locationIcon = L.divIcon({
+ html: `
+
+ `,
+ className: '',
+ iconSize: [40, 40],
+ iconAnchor: [20, 20]
+});
+
function locateAndLoad() {
if (!navigator.geolocation) {
loadVehicles();
@@ -148,6 +246,16 @@ function locateAndLoad() {
}
navigator.geolocation.getCurrentPosition(pos => {
map.setView([pos.coords.latitude, pos.coords.longitude], 14);
+
+ // Standort-Marker hinzufรผgen oder aktualisieren
+ if (locationMarker) {
+ locationMarker.setLatLng([pos.coords.latitude, pos.coords.longitude]);
+ } else {
+ locationMarker = L.marker([pos.coords.latitude, pos.coords.longitude], { icon: locationIcon })
+ .addTo(map)
+ .bindPopup('
๐ Dein Standort');
+ }
+
loadVehicles();
}, () => {
loadVehicles();
@@ -159,7 +267,7 @@ setInterval(loadVehicles, 30000);
map.on('moveend', loadVehicles);
// =============================
-// Bahnhรถfe mit Live-Abfahrten
+// Stations
// =============================
const stations = [
{ name: "Berlin Zoologischer Garten", id: "900023201", coords: [52.5069, 13.3326] },
@@ -171,22 +279,29 @@ const stations = [
];
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 |
`;
+ let html = `
+
+
+
+
+
+ | Linie |
+ Richtung |
+ Zeit |
+
+
+
+ `;
- departures.forEach((dep, index) => {
+ departures.forEach(dep => {
const line = dep.line?.name || "?";
const product = dep.line?.product;
const icon = symbols[product] || 'โ';
@@ -195,31 +310,65 @@ async function loadStationDepartures(stationId, marker, stationName) {
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 delayStr = delay > 0 ? `+${delay}'` : "";
const color = getLineColor(line, product);
- const bg = index % 2 === 0 ? '#f9f9f9' : '#fff';
- html += `
- | ${icon} ${line} |
- ${direction} |
- ${timeStr} ${delayStr} |
-
`;
+ html += `
+
+ | ${icon} ${line} |
+ ${direction} |
+ ${timeStr}${delayStr} |
+
+ `;
});
- html += `
`;
+ html += `
+
+
+
+ `;
+
marker.setPopupContent(html).openPopup();
} catch (err) {
- marker.setPopupContent(`
${stationName}Fehler beim Laden`);
+ marker.setPopupContent(`
+
+
+
Fehler beim Laden der Abfahrten
+
+ `);
console.error(err);
}
}
+const stationIcon = L.divIcon({
+ html: `
+
+ `,
+ className: 'station-marker',
+ iconSize: [50, 50],
+ iconAnchor: [25, 45]
+});
stations.forEach(station => {
- const marker = L.marker(station.coords)
+ const marker = L.marker(station.coords, { icon: stationIcon })
.addTo(map)
- .bindPopup(`
${station.name}Lade Abfahrten...`);
+ .bindPopup(`
+
+
+ `, { maxWidth: 400 });
marker.on('click', () => loadStationDepartures(station.id, marker, station.name));
-});
+});
\ No newline at end of file
diff --git a/index.html b/index.html
index cfc3aaf..e3fd9f3 100644
--- a/index.html
+++ b/index.html
@@ -8,10 +8,33 @@
+
+
+
+
+ ๐
+ VBB Live
+
+
+
+ 0
+ Fahrzeuge
+
+
+
+ --:--
+ Aktualisiert
+
+
+
+
+
+
-