/** * Transit presentation helpers * ───────────────────────────────────────────────────────────────────────────── * Shared by RouteDialog (the itinerary) and StopDepartures (the departure * board), so the mode → icon/colour vocabulary lives in exactly one place. * Modes are MOTIS's own `mode` values. */ import type { TransitItinerary, TransitLeg } from './routing'; /** Line colour for a transit leg, keyed by the MOTIS mode. */ export function transitLegColor(mode: string): string { switch (mode) { case 'BUS': case 'COACH': return '#22c55e'; case 'TRAM': return '#eab308'; case 'SUBWAY': case 'METRO': return '#3b82f6'; case 'RAIL': case 'REGIONAL_RAIL': case 'REGIONAL_FAST_RAIL': case 'LONG_DISTANCE': case 'HIGHSPEED_RAIL': case 'NIGHT_RAIL': case 'SUBURBAN': return '#ef4444'; case 'FERRY': return '#06b6d4'; default: return '#ccff00'; } } /** Emoji for a transit leg's mode. */ export function transitModeEmoji(mode: string): string { switch (mode) { case 'BUS': case 'COACH': return '🚌'; case 'TRAM': return '🚊'; case 'SUBWAY': case 'METRO': return '🚇'; case 'RAIL': case 'REGIONAL_RAIL': case 'REGIONAL_FAST_RAIL': case 'LONG_DISTANCE': case 'HIGHSPEED_RAIL': case 'NIGHT_RAIL': case 'SUBURBAN': return '🚆'; case 'FERRY': return '⛴️'; case 'WALK': return '🚶'; case 'BIKE': return '🚲'; default: return '🚇'; } } /** Leading emoji for a whole itinerary — its first vehicle leg, else walking. */ export function transitPrimaryEmoji(it: TransitItinerary): string { const vehicle = it.legs.find((l) => l.mode !== 'WALK' && l.mode !== 'BIKE'); return transitModeEmoji(vehicle?.mode ?? 'WALK'); } /** Seconds between a leg's start and end (0 when either time is missing). */ export function legDurationS(leg: TransitLeg): number { if (!leg.startISO || !leg.endISO) return 0; const ms = Date.parse(leg.endISO) - Date.parse(leg.startISO); return Number.isFinite(ms) && ms > 0 ? Math.round(ms / 1000) : 0; } /** * The transit mode picker's groups, each mapping to MOTIS `transitModes` * values. ⚠️ MOTIS classifies a German S-Bahn as `METRO` (verified live), so it * lands under "Metro", not "Train" — that is the feed's own classification. */ export const TRANSIT_MODE_GROUPS: ReadonlyArray<{ key: string; label: string; emoji: string; modes: readonly string[]; }> = [ { key: 'bus', label: 'Bus', emoji: '🚌', modes: ['BUS', 'COACH'] }, { key: 'tram', label: 'Tram', emoji: '🚊', modes: ['TRAM'] }, { key: 'metro', label: 'Metro', emoji: '🚇', modes: ['SUBWAY', 'METRO'] }, { key: 'train', label: 'Train', emoji: '🚆', modes: [ 'RAIL', 'REGIONAL_RAIL', 'REGIONAL_FAST_RAIL', 'HIGHSPEED_RAIL', 'LONG_DISTANCE', 'NIGHT_RAIL', 'SUBURBAN', ], }, { key: 'ferry', label: 'Ferry', emoji: '⛴️', modes: ['FERRY'] }, ];