6.6 Routes Pages

The Routes section now exposes two views through a shared server-rendered shell:

  • /routes: the route browser for comparing Atlas GTFS and OSM route definitions
  • /routes/gtfs-stop-id-sloid: a diagnostic map for the canonical GTFS stop_id <-> ATLAS sloid mapping

Both views are rendered through templates/pages/routes.html with an active_view switch, so the page keeps one shared tab layout while loading view-specific data and JavaScript.

1. Shared Shell and Routing

backend/blueprints/routes.py provides:

  • routes_page() for the route list tab
  • routes_gtfs_stop_id_sloid_page() for the GTFS diagnostic map tab
  • _render_routes_template() to pass shared tab state and GTFS map limits into the template

The template renders page tabs at the top and then chooses between:

  • the existing filterable route-card UI
  • the GTFS map shell with summary overlay, banner, and full-size Leaflet map container

2. Route Browser Tab

The default /routes tab is still 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)

End-to-end flow

  1. User submits filters using the top toolbar.
  2. Flask route [backend/blueprints/routes.py](https://github.com/openTdataCH/stop_sync_osm_atlas/blob/main/backend/blueprints/routes.py)::routes_page normalizes query params.
  3. Backend builds a combined route query and applies matched-state filters, optional Atlas operator constraints, and optional route-id search.
  4. Matched pairs are returned once, then unmatched Atlas-only and OSM-only rows are appended into the same paginated result set.
  5. Results are expanded with route-stop details, itinerary matches, and route metadata.
  6. Template templates/pages/routes.html renders compact route cards.
  7. Each card starts collapsed and can be unfolded to inspect stops by direction or preview route position on an embedded map.

Stops and embedded map rendering

Stops are loaded from:

  • normalized stop_calls joined back to atlas_stops, osm_nodes, and stops_matched

Direction groups are sorted numeric-first, then grouped by uic_ref for a compact two-level stop rendering.

The embedded route preview map reuses the shared main-map stack:

  • the card exposes a route-scoped /api/data filter payload
  • markers use shared map helpers (createAtlasMarker, createOsmMarker)
  • bounds are fit after the route points load

3. GTFS stop_id <-> sloid Map Tab

The /routes/gtfs-stop-id-sloid tab is a full-page diagnostic Leaflet map backed by the imported gtfs_stops_raw and gtfs_stop_identity_resolution tables.

What it shows

  • GTFS stops with at least one imported coordinate source
  • ATLAS stops represented in the imported GTFS/ATLAS state table
  • visible matched line segments between GTFS and ATLAS points
  • a compact summary overlay showing GTFS coverage, ATLAS coverage, and assignment counts

Marker colors are intentionally fixed:

  • matched ATLAS: blue
  • unmatched ATLAS: red
  • matched GTFS: green
  • unmatched GTFS: gray

Backend APIs

The tab reads three dedicated JSON endpoints from backend/blueprints/routes.py:

  • /api/routes/gtfs-stop-id-sloid/summary
  • /api/routes/gtfs-stop-id-sloid/map
  • /api/routes/gtfs-stop-id-sloid/popup

The query and serialization logic lives in backend/services/gtfs_stop_id_sloid.py.

That service builds deduplicated coordinate subqueries from gtfs_stop_identity_resolution, computes matched/unmatched counts per side, and returns:

  • gtfs_stops
  • atlas_stops
  • matches
  • meta (zoom, cap state, overview/detail mode)

Overview/detail behavior

The map uses two density modes:

  • overview mode below zoom 11: limit 3000 GTFS rows and 3000 ATLAS rows
  • detail mode at zoom 11 and above: limit 5000 GTFS rows and 5000 ATLAS rows

Rows are fetched in a balanced way so matched and unmatched states do not starve each other inside a dense viewport.

Popup behavior

The frontend lazy-loads popup payloads per clicked marker.

GTFS popups include:

  • stop identifiers and coordinates
  • matched SLOID previews
  • same-UIC ATLAS candidate previews

ATLAS popups include:

  • SLOID and ATLAS metadata
  • matched GTFS stop previews
  • same-UIC GTFS candidate previews

The map keeps the shared draggable multi-popup behavior by reusing the common popup helpers and line handler logic.

4. Files and Responsibilities

  • backend/blueprints/routes.py
    • route-list query construction and pagination
    • GTFS tab page route and three GTFS JSON endpoints
    • shared routes template rendering
    • loading line_families, line_family_matches, itineraries, itinerary_matches, and stop_calls for route cards
  • backend/services/gtfs_stop_id_sloid.py
    • GTFS summary payloads
    • viewport payloads for GTFS/ATLAS markers and line segments
    • GTFS and ATLAS popup payload builders
  • templates/pages/routes.html
    • tab shell shared by both Routes views
    • route list toolbar, cards, and pagination
    • GTFS map shell, banner, and summary overlay host
  • static/js/pages/routes.js
    • route browser toolbar behavior and embedded route preview maps
  • static/js/pages/routes-gtfs-stop-id-sloid.js
    • GTFS map initialization, viewport fetching, marker rendering, summary updates, and popup loading

5. Maintenance Guidelines

  • Keep the canonical GTFS stop_id <-> sloid matcher in matching_and_import_db/downloader/get_atlas_gtfs.py.
  • Keep GTFS map query logic in backend/services/gtfs_stop_id_sloid.py, not in the template or page controller.
  • Keep the Routes tab shell shared; add view-specific behavior behind active_view rather than splitting into parallel templates.
  • Preserve the fixed marker-color semantics because they are part of the debugging vocabulary used elsewhere in the docs.
Data update in progress
Elapsed: -- ETA: -- Phase: idle