Ladeanzeige statt Zwischenständen, Echtzeit sofort nach Start, keine Autobahn-Schilder

- /api/status liefert ready, sobald Positionen mit Echtzeit berechnet sind
- Server holt Echtzeit direkt nach dem Laden des Fahrplans (statt nach ~35 s)
- Browser zeigt bis dahin nur 'Züge werden geladen …'
- Statuszeile ohne Technik: 'N Züge unterwegs', ggf. 'Verspätungen zurzeit nicht verfügbar'
- Autobahn-/Strassenschilder (roads_shields) ausgeblendet

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X48pE4bzjHej8w9HyZXCjb
This commit is contained in:
Claude
2026-10-21 21:02:35 +02:00
parent 4df29e7e6c
commit 2e3c10a6c4
4 changed files with 48 additions and 19 deletions
+27 -18
View File
@@ -6,7 +6,7 @@ maplibregl.addProtocol("pmtiles", protocol.tile);
// Drei Grundkarten-Sätze im selben Style, umgeschaltet per Sichtbarkeit:
// hell, dunkel, und Beschriftung für Satellit (weisse Schrift mit dunklem Rand, wie bei Google Maps)
const prefixed = (layers, pre, extra = {}) => layers.map((l) => ({ ...l, id: pre + l.id,
const prefixed = (layers, pre, extra = {}) => layers.filter((l) => l.id !== "roads_shields").map((l) => ({ ...l, id: pre + l.id,
layout: { ...l.layout, visibility: "none" }, paint: { ...l.paint, ...(l.type === "symbol" ? extra : {}) } }));
const LAYERS = {
light: prefixed(basemaps.layers("osm", basemaps.namedFlavor("light"), { lang: "de" }), "l-"),
@@ -114,8 +114,10 @@ for (const m of MODES) document.getElementById("btn-" + m).onclick = () => setMo
let trains = { type: "FeatureCollection", features: [] };
let ts = 0, step = 2, clockOffset = 0;
let ready = false; // erst Züge zeigen, wenn der Server fertige Daten (inkl. Echtzeit) hat
async function refreshTrains() {
if (document.hidden) return;
if (document.hidden || !ready) return;
try {
const r = await fetch("api/vehicles");
if (!r.ok) return;
@@ -228,25 +230,34 @@ function animate(ms) {
}
async function refreshStatus() {
const el = document.getElementById("status");
const el = document.getElementById("status"), loading = document.getElementById("loading");
try {
const s = await (await fetch("api/status")).json();
if (!s.feed_version) { el.textContent = "Noch kein Fahrplan geladen …"; el.className = "warn"; return; }
const v = s.feed_version;
const parts = [`Fahrplan ${v.slice(6, 8)}.${v.slice(4, 6)}.${v.slice(0, 4)}`, `${s.vehicles} Züge`];
let warn = !s.shapes;
if (!s.realtime) { parts.push("keine Echtzeit (API-Key fehlt)"); warn = true; }
else if (s.rt_error || s.rt_age === null || s.rt_age > 180) { parts.push("Echtzeit gestört"); warn = true; }
else parts.push(`Echtzeit vor ${s.rt_age} s`);
if (!s.shapes) parts.push("Strecken: Luftlinie");
el.textContent = parts.join(" · ");
el.className = warn ? "warn" : "";
if (!s.ready) {
loading.textContent = s.feed_version ? "Züge werden geladen …"
: "Fahrplan wird vorbereitet … beim ersten Start dauert das 15–20 Minuten.";
loading.hidden = false;
return;
}
if (!ready) {
ready = true;
await refreshTrains();
loading.hidden = true;
}
const rtOk = s.realtime && !s.rt_error && s.rt_age !== null && s.rt_age < 180;
el.textContent = `${s.vehicles} Züge unterwegs` + (rtOk ? "" : " · Verspätungen zurzeit nicht verfügbar");
el.className = rtOk ? "" : "warn";
} catch (e) {
el.textContent = "Server nicht erreichbar";
el.className = "warn";
loading.textContent = "Keine Verbindung zum Server …";
loading.hidden = false;
}
}
async function statusLoop() { // bis alles bereit ist alle 2 s nachfragen, danach alle 30 s
await refreshStatus();
setTimeout(statusLoop, ready ? 30000 : 2000);
}
// ---------- Zugdetails ----------
let openId = null;
@@ -315,10 +326,8 @@ map.on("load", () => {
let mode = "light";
try { mode = localStorage.getItem("mode") || (localStorage.getItem("sat") === "1" ? "sat" : "light"); } catch (e) { /* egal */ }
setMode(mode);
refreshTrains();
refreshStatus();
statusLoop();
setInterval(refreshTrains, 5000);
requestAnimationFrame(animate);
setInterval(refreshStatus, 30000);
setInterval(() => openId && showTrip(openId), 30000);
});