feat(rail): enrich TriMet data with GTFS route shapes and per-route colors#69
Merged
Merged
Conversation
…olors The rail track layer previously used OSM/Overpass geometry with a two-value colour scheme (violet for light_rail, amber for rail). TriMet's own GTFS static bundle contains authoritative route geometry (shapes.txt) and official hex brand colours (route_color in routes.txt) that we were downloading but not using. Changes: - poller/pollers/gtfs_rt.py: on each 24-hour static GTFS refresh, parse trips.txt + shapes.txt alongside routes.txt; group all shape LineStrings into one MultiLineString GeoJSON feature per route; write the result to Redis key cache:gtfs:trimet:shapes (25-hour TTL) as a fire-and-forget asyncio task so vehicle-position polling is not blocked. Also extracts route_color and route_text_color fields from routes.txt. - backend/routers/rail.py: new GET /rail/gtfs-shapes endpoint that reads the poller-written Redis key with the same in-process memory cache pattern as /rail/tracks. Returns an empty FeatureCollection gracefully when TRIMET_GTFS_ENABLED=false or the cache has not warmed yet. - frontend/src/components/layers/RailLayer.tsx: adds a second MapLibre source/layer (rail-gtfs-src / rail-gtfs-line) that renders GTFS route shapes using the route_color property from each feature, giving each MAX line, WES, and Portland Streetcar its official brand colour. OSM tracks remain as a dimmer base layer for mainline/freight coverage not in GTFS. Layer insertion order is enforced so GTFS always renders above OSM regardless of which endpoint responds first. Both layers respond to the railTracksVisible toggle and are cleaned up on unmount. https://claude.ai/code/session_011PCqaW8iDZHJMbQWKae7GT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The rail track layer previously used OSM/Overpass geometry with a
two-value colour scheme (violet for light_rail, amber for rail).
TriMet's own GTFS static bundle contains authoritative route geometry
(shapes.txt) and official hex brand colours (route_color in routes.txt)
that we were downloading but not using.
Changes:
poller/pollers/gtfs_rt.py: on each 24-hour static GTFS refresh, parse
trips.txt + shapes.txt alongside routes.txt; group all shape LineStrings
into one MultiLineString GeoJSON feature per route; write the result to
Redis key cache:gtfs:trimet:shapes (25-hour TTL) as a fire-and-forget
asyncio task so vehicle-position polling is not blocked.
Also extracts route_color and route_text_color fields from routes.txt.
backend/routers/rail.py: new GET /rail/gtfs-shapes endpoint that reads
the poller-written Redis key with the same in-process memory cache
pattern as /rail/tracks. Returns an empty FeatureCollection gracefully
when TRIMET_GTFS_ENABLED=false or the cache has not warmed yet.
frontend/src/components/layers/RailLayer.tsx: adds a second MapLibre
source/layer (rail-gtfs-src / rail-gtfs-line) that renders GTFS route
shapes using the route_color property from each feature, giving each MAX
line, WES, and Portland Streetcar its official brand colour. OSM tracks
remain as a dimmer base layer for mainline/freight coverage not in GTFS.
Layer insertion order is enforced so GTFS always renders above OSM
regardless of which endpoint responds first. Both layers respond to the
railTracksVisible toggle and are cleaned up on unmount.
https://claude.ai/code/session_011PCqaW8iDZHJMbQWKae7GT