From 2a9aca302485bc08f5b2dd2a54987de6f80fc338 Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Fri, 19 Dec 2025 13:06:27 +0100 Subject: Implement loading stops as tiles from OTP --- src/frontend/app/routes/home.tsx | 2 +- src/frontend/app/routes/map.tsx | 142 ++++++++++++++++------------------ src/frontend/app/routes/planner.tsx | 10 ++- src/frontend/app/routes/stops-$id.tsx | 7 +- 4 files changed, 77 insertions(+), 84 deletions(-) (limited to 'src/frontend/app/routes') diff --git a/src/frontend/app/routes/home.tsx b/src/frontend/app/routes/home.tsx index e97659a..36565bd 100644 --- a/src/frontend/app/routes/home.tsx +++ b/src/frontend/app/routes/home.tsx @@ -31,7 +31,7 @@ export default function StopList() { () => new Fuse(data || [], { threshold: 0.3, - keys: ["name.original", "name.intersect", "stopId"], + keys: ["name", "stopId"], }), [data] ); diff --git a/src/frontend/app/routes/map.tsx b/src/frontend/app/routes/map.tsx index 39fc062..db9de59 100644 --- a/src/frontend/app/routes/map.tsx +++ b/src/frontend/app/routes/map.tsx @@ -1,8 +1,7 @@ -import StopDataProvider, { type Stop } from "../data/StopDataProvider"; +import StopDataProvider from "../data/StopDataProvider"; import "./map.css"; import { DEFAULT_STYLE, loadStyle } from "app/maps/styleloader"; -import type { Feature as GeoJsonFeature, Point } from "geojson"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import Map, { @@ -16,8 +15,11 @@ import Map, { } from "react-map-gl/maplibre"; import { useNavigate } from "react-router"; import { PlannerOverlay } from "~/components/PlannerOverlay"; -import { StopSheet } from "~/components/StopSummarySheet"; -import { REGION_DATA } from "~/config/RegionConfig"; +import { + StopSheet, + type StopSheetProps, +} from "~/components/map/StopSummarySheet"; +import { APP_CONSTANTS } from "~/config/constants"; import { usePageTitle } from "~/contexts/PageTitleContext"; import { usePlanner } from "~/hooks/usePlanner"; import { useApp } from "../AppContext"; @@ -28,19 +30,9 @@ export default function StopMap() { const { t } = useTranslation(); const navigate = useNavigate(); usePageTitle(t("navbar.map", "Mapa")); - const [stops, setStops] = useState< - GeoJsonFeature< - Point, - { - stopId: string; - name: string; - lines: string[]; - cancelled?: boolean; - prefix: string; - } - >[] - >([]); - const [selectedStop, setSelectedStop] = useState(null); + const [selectedStop, setSelectedStop] = useState< + StopSheetProps["stop"] | null + >(null); const [isSheetOpen, setIsSheetOpen] = useState(false); const { mapState, updateMapState, theme } = useApp(); const mapRef = useRef(null); @@ -63,45 +55,10 @@ export default function StopMap() { return; } const feature = features[0]; - console.debug("Map click feature:", feature); - const props: any = feature.properties; handlePointClick(feature); }; - useEffect(() => { - StopDataProvider.getStops().then((data) => { - const features: GeoJsonFeature< - Point, - { - stopId: string; - name: string; - lines: string[]; - cancelled?: boolean; - prefix: string; - } - >[] = data.map((s) => ({ - type: "Feature", - geometry: { - type: "Point", - coordinates: [s.longitude as number, s.latitude as number], - }, - properties: { - stopId: s.stopId, - name: s.name.original, - lines: s.lines, - cancelled: s.cancelled ?? false, - prefix: s.stopId.startsWith("renfe:") - ? "stop-renfe" - : s.cancelled - ? "stop-vitrasa-cancelled" - : "stop-vitrasa", - }, - })); - setStops(features); - }); - }, []); - useEffect(() => { //const styleName = "carto"; const styleName = "openfreemap"; @@ -166,26 +123,29 @@ export default function StopMap() { const handlePointClick = (feature: any) => { const props: any = feature.properties; - if (!props || !props.stopId) { + // TODO: Move ID to constant, improve type checking + if (!props || feature.layer.id !== "stops") { console.warn("Invalid feature properties:", props); return; } - const stopId = props.stopId; + const stopId = props.id; - // fetch full stop to get lines array - StopDataProvider.getStopById(stopId) - .then((stop) => { - if (!stop) { - console.warn("Stop not found:", stopId); - return; - } - setSelectedStop(stop); - setIsSheetOpen(true); - }) - .catch((err) => { - console.error("Error fetching stop details:", err); - }); + console.debug("Stop clicked:", stopId, props); + + setSelectedStop({ + stopId: props.id, + stopCode: props.code, + name: props.name || "Unknown Stop", + lines: JSON.parse(props.routes || "[]").map((route) => { + return { + line: route.shortName, + colour: route.colour, + textColour: route.textColour, + }; + }), + }); + setIsSheetOpen(true); }; return ( @@ -203,7 +163,7 @@ export default function StopMap() { style={{ width: "100%", height: "100%" }} interactiveLayerIds={["stops", "stops-label"]} onClick={onMapClick} - minZoom={11} + minZoom={5} scrollZoom pitch={0} roll={0} @@ -214,7 +174,7 @@ export default function StopMap() { zoom: mapState.zoom, }} attributionControl={{ compact: false }} - maxBounds={[REGION_DATA.bounds.sw, REGION_DATA.bounds.ne]} + maxBounds={[APP_CONSTANTS.bounds.sw, APP_CONSTANTS.bounds.ne]} > => { const resp = await fetch( - `${REGION_DATA.consolidatedCirculationsEndpoint}?stopId=${stopId}`, + `${APP_CONSTANTS.consolidatedCirculationsEndpoint}?stopId=${stopId}`, { headers: { Accept: "application/json", @@ -123,8 +123,7 @@ export default function Estimates() { // Helper function to get the display name for the stop const getStopDisplayName = useCallback(() => { if (customName) return customName; - if (stopData?.name.intersect) return stopData.name.intersect; - if (stopData?.name.original) return stopData.name.original; + if (stopData?.name) return stopData.name; return `Parada ${stopId}`; }, [customName, stopData, stopId]); -- cgit v1.3