5.6 Routes Pages

The /routes page is a server-rendered route browser for comparing Atlas GTFS and OSM route definitions.

It is designed around two primary filter dimensions plus free-text route search:

  • Atlas Operator (index-style searchable multi-select dropdown)
  • Status (all, matched, unmatched, unmatched_atlas, unmatched_osm)

1. End-to-End Flow

  1. User submits filters using the top toolbar.
  2. Flask route backend/blueprints/routes.py::routes_page normalizes query params.
  3. Backend builds a combined route query and applies:
  • matched/unmatched status constraints
  • optional Atlas operator constraints
  • optional route-id search
  1. Matched pairs are returned once, then unmatched Atlas-only and OSM-only rows are appended into the same paginated result set.
  2. Results are expanded with route-stop details and route metadata.
  3. Template templates/pages/routes.html renders compact route cards.
  4. Each card starts collapsed and can be unfolded to:
    • view stops by direction
  • view route extent in an embedded Leaflet map

2. Query Semantics

Status

  • matched=all: include matched pairs, unmatched Atlas routes, and unmatched OSM routes.
  • matched=matched: include only routes present in routes_matched.
  • matched=unmatched: include both unmatched Atlas routes and unmatched OSM routes.
  • matched=unmatched_atlas: include only Atlas routes without a matched OSM route.
  • matched=unmatched_osm: include only OSM routes without a matched Atlas route.

Atlas Operator

  • Matched and unmatched Atlas rows: filter is applied directly through route_atlas_stops -> atlas_stops.
  • Unmatched OSM rows are excluded when an Atlas operator filter is active, because they have no Atlas-side operator to match against.

3. Route Card Structure

Each route card keeps a dual-source header:

  • Atlas panel (blue)
  • OSM panel (green)

Depending on match state, one side can collapse to an Atlas-only or OSM-only summary.

The visible header now surfaces:

  • Atlas operator summary
  • Atlas representative_headsign summary or route_long_name
  • OSM GTFS route ID
  • OSM operator

Below the header, two native collapsible panels (details/summary) are rendered:

  1. Stops list
  2. Route on map

This keeps the default page compact while preserving full route detail on demand.

4. Stops Rendering

Stops are loaded from:

  • route_atlas_stops + atlas_stops
  • route_osm_stops + osm_nodes

Then grouped by direction and UIC:

  • Direction groups are sorted with numeric-first ordering.
  • Inside each direction, stops are grouped by uic_ref.
  • Each group exposes member rows (SLOID or OSM node id) for traceability.

5. Embedded Leaflet Map Rendering

Map previews now use the same map stack as the main page (Leaflet + shared marker helpers):

  • each route card exposes a route-scoped filter payload (station_filter + filter_types=route)
  • the frontend loads stops via /api/data with those route filters
  • markers/lines are rendered using shared map marker functions (createAtlasMarker, createOsmMarker)
  • map bounds are fit to loaded route points after rendering

If no geolocated points are returned, the map panel shows a fallback message.

6. Files and Responsibilities

  • backend/blueprints/routes.py
    • parameter normalization
    • combined matched/unmatched query construction and pagination
    • stop/group assembly
    • Atlas and OSM route metadata loading
    • route map filter payload assembly
  • templates/pages/routes.html
    • one-line filter toolbar (index-style search + Operator dropdown)
    • summary + pagination
    • route card markup and collapsible panels
  • static/css/pages/routes.css
    • one-line toolbar and filter-pill styles
    • collapsible panel visuals
    • responsive card and embedded map layout
  • static/js/pages/routes.js
    • Atlas operator dropdown initialization
    • route panel map loading and rendering
    • route filter request construction for /api/data

7. Maintenance Guidelines

  • Keep query logic in backend helpers, not in templates.
  • Keep card panels collapsed by default to protect performance and readability.
  • Keep route-filter payload generation centralized in backend helpers.
  • Prefer adding new filters as normalized query params with explicit allowed values.
  • Preserve pagination query params when adding links/actions in the template.
Data update running in background
Preparing update... | Phase: initializing
Data update in progress
Core data is being refreshed. Use this time to read the documentation.
Elapsed: -- ETA: -- Phase: idle