Skip to content

feat(rail): enrich TriMet data with GTFS route shapes and per-route colors#69

Merged
d3mocide merged 1 commit into
mainfrom
claude/trimet-data-caching-2puNv
May 16, 2026
Merged

feat(rail): enrich TriMet data with GTFS route shapes and per-route colors#69
d3mocide merged 1 commit into
mainfrom
claude/trimet-data-caching-2puNv

Conversation

@d3mocide
Copy link
Copy Markdown
Owner

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

…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
@d3mocide d3mocide merged commit dd25d41 into main May 16, 2026
6 checks passed
@d3mocide d3mocide deleted the claude/trimet-data-caching-2puNv branch May 16, 2026 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants