Verspätungsfarbe als Verlauf: bis +2′ grün, gelb 10′, orange 20′, rot 30′, lila 40′, ab 50′ blinkend; Legende als Skala
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X48pE4bzjHej8w9HyZXCjb
This commit is contained in:
+23
-10
@@ -22,12 +22,24 @@ const LAYERS = {
|
||||
};
|
||||
|
||||
// Verspätung in Sekunden -> Farbe (SBB zählt < 3 min als pünktlich)
|
||||
const delayColor = ["case",
|
||||
["==", ["get", "delay"], null], "#94a3b8",
|
||||
["<", ["get", "delay"], 180], "#16a34a",
|
||||
["<", ["get", "delay"], 360], "#eab308",
|
||||
["<", ["get", "delay"], 900], "#f97316",
|
||||
"#dc2626"];
|
||||
// Verspätung (s) -> Farbe, fliessend: bis +2′ grün (wie SBB "pünktlich"), dann über gelb (10′),
|
||||
// orange (20′), rot (30′) zu lila (40′); ab 50′ blinkt das Symbol zusätzlich (BLINK_S)
|
||||
const DELAY_STOPS = [[150, "#16a34a"], [600, "#eab308"], [1200, "#f97316"], [1800, "#dc2626"], [2400, "#9333ea"]];
|
||||
const NO_RT = "#94a3b8", BLINK_S = 3000;
|
||||
const delayColor = ["case", ["==", ["get", "delay"], null], NO_RT,
|
||||
["interpolate", ["linear"], ["get", "delay"], ...DELAY_STOPS.flat()]];
|
||||
function delayRGB(sec) { // dasselbe für die Halteliste (Badges, +x′)
|
||||
const hex = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16));
|
||||
if (sec <= DELAY_STOPS[0][0]) return DELAY_STOPS[0][1];
|
||||
for (let i = 1; i < DELAY_STOPS.length; i++) {
|
||||
const [s0, c0] = DELAY_STOPS[i - 1], [s1, c1] = DELAY_STOPS[i];
|
||||
if (sec <= s1) {
|
||||
const k = (sec - s0) / (s1 - s0), a = hex(c0), b = hex(c1);
|
||||
return `rgb(${a.map((v, j) => Math.round(v + (b[j] - v) * k)).join(",")})`;
|
||||
}
|
||||
}
|
||||
return DELAY_STOPS.at(-1)[1];
|
||||
}
|
||||
|
||||
const map = new maplibregl.Map({
|
||||
container: "map",
|
||||
@@ -305,6 +317,9 @@ function render() {
|
||||
f.properties.bearing = cur === undefined ? target : cur + turn(cur, target) * ease;
|
||||
}
|
||||
map.getSource("trains").setData(trains);
|
||||
// ab 50′ Verspätung sanft ein- und ausblenden (ein Zyklus ~1,6 s)
|
||||
const pulse = 0.55 + 0.45 * Math.cos((performance.now() / 1600) * 2 * Math.PI);
|
||||
map.setPaintProperty("trains", "icon-opacity", ["case", [">=", ["coalesce", ["get", "delay"], 0], BLINK_S], pulse, 1]);
|
||||
if (hoverId) { // Hover-Kärtchen fährt mit dem Zug mit
|
||||
const f = trains.features.find((x) => x.properties.id === hoverId);
|
||||
if (f) hover.setLngLat(f.geometry.coordinates); else { hover.remove(); hoverId = null; }
|
||||
@@ -360,15 +375,13 @@ const esc = (s) => String(s ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&", "<
|
||||
function delayBadge(sec) {
|
||||
if (sec === null || sec === undefined) return '<span class="badge" style="background:var(--na)">ohne Echtzeit</span>';
|
||||
const min = Math.round(sec / 60);
|
||||
const c = sec < 180 ? "--ok" : sec < 360 ? "--d1" : sec < 900 ? "--d2" : "--d3";
|
||||
return `<span class="badge" style="background:var(${c})">${min <= 0 ? "pünktlich" : "+" + min + "′"}</span>`;
|
||||
return `<span class="badge" style="background:${delayRGB(sec)}">${min <= 0 ? "pünktlich" : "+" + min + "′"}</span>`;
|
||||
}
|
||||
|
||||
function delayText(sec) {
|
||||
if (sec === null || sec === undefined || Math.round(sec / 60) === 0) return "";
|
||||
const min = Math.round(sec / 60);
|
||||
const c = sec < 180 ? "--ok" : sec < 360 ? "--d1" : sec < 900 ? "--d2" : "--d3";
|
||||
return `<span class="d" style="color:var(${c})">${min > 0 ? "+" : ""}${min}′</span>`;
|
||||
return `<span class="d" style="color:${delayRGB(sec)}">${min > 0 ? "+" : ""}${min}′</span>`;
|
||||
}
|
||||
|
||||
async function showTrip(id) {
|
||||
|
||||
Reference in New Issue
Block a user