From 7061660e7d475fe3ed016858a49a0c9b7ba10c18 Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Fri, 21 Nov 2025 10:53:03 +0100 Subject: Show path from bus position to terminus --- src/frontend/app/components/StopMapSheet.tsx | 76 +++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) (limited to 'src/frontend/app/components/StopMapSheet.tsx') diff --git a/src/frontend/app/components/StopMapSheet.tsx b/src/frontend/app/components/StopMapSheet.tsx index 71a1095..7dab82b 100644 --- a/src/frontend/app/components/StopMapSheet.tsx +++ b/src/frontend/app/components/StopMapSheet.tsx @@ -1,8 +1,8 @@ import maplibregl from "maplibre-gl"; import React, { useEffect, useMemo, useRef, useState } from "react"; -import Map, { Marker, type MapRef } from "react-map-gl/maplibre"; +import Map, { Layer, Marker, Source, type MapRef } from "react-map-gl/maplibre"; import { useApp } from "~/AppContext"; -import type { RegionId } from "~/config/RegionConfig"; +import { getRegionConfig, type RegionId } from "~/config/RegionConfig"; import { getLineColor } from "~/data/LineColors"; import type { Stop } from "~/data/StopDataProvider"; import { loadStyle } from "~/maps/styleloader"; @@ -12,12 +12,16 @@ export interface Position { latitude: number; longitude: number; orientationDegrees: number; + shapeIndex?: number; } export interface ConsolidatedCirculationForMap { line: string; route: string; currentPosition?: Position; + schedule?: { + shapeId?: string; + }; } interface StopMapProps { @@ -44,6 +48,36 @@ export const StopMap: React.FC = ({ const [zoom, setZoom] = useState(16); const [moveTick, setMoveTick] = useState(0); const [showAttribution, setShowAttribution] = useState(false); + const [shapes, setShapes] = useState>({}); + + const regionConfig = getRegionConfig(region); + + useEffect(() => { + circulations.forEach((c) => { + if ( + c.schedule?.shapeId && + c.currentPosition?.shapeIndex !== undefined && + regionConfig.shapeEndpoint + ) { + const key = `${c.schedule.shapeId}_${c.currentPosition.shapeIndex}`; + if (!shapes[key]) { + fetch( + `${regionConfig.shapeEndpoint}?shapeId=${c.schedule.shapeId}&startPointIndex=${c.currentPosition.shapeIndex}` + ) + .then((res) => { + if (res.ok) return res.json(); + return null; + }) + .then((data) => { + if (data) { + setShapes((prev) => ({ ...prev, [key]: data })); + } + }) + .catch((err) => console.error("Failed to load shape", err)); + } + } + }); + }, [circulations, regionConfig.shapeEndpoint, shapes]); type Pt = { lat: number; lon: number }; const haversineKm = (a: Pt, b: Pt) => { @@ -308,6 +342,44 @@ export const StopMap: React.FC = ({ setMoveTick((t) => (t + 1) % 1000000); }} > + {/* Shapes */} + {circulations.map((c, idx) => { + if ( + !c.schedule?.shapeId || + c.currentPosition?.shapeIndex === undefined + ) + return null; + const key = `${c.schedule.shapeId}_${c.currentPosition.shapeIndex}`; + const shapeData = shapes[key]; + if (!shapeData) return null; + const lineColor = getLineColor(region, c.line); + + return ( + + + + + ); + })} {/* Stop marker (center) */} {stop.latitude && stop.longitude && ( -- cgit v1.3