Munich MaxMaps is a transit exploration tool built around a Rust Rocket API backed by Neo4j/SQLite, plus a Leaflet UI. It exposes GTFS-derived stop, route, and departure data, and the UI lets users pin start/end locations, build transit routes, and inspect upcoming departures, individual transport lines or find public transport vehicles on map in real-time. A lightweight Flask shell is used to serve the UI and compute paths via Neo4j; the Rust service is the primary data backend.
Key features
- GTFS/Neo4j-powered stops and routes served via Rust/Rocket.
- Departures (scheduled/live) and trip stop sequences from SQLite + civiguild.
- Leaflet UI: address search, pin start/end, mode-colored legs with distance/time estimates, route summaries.
- Stop popups with grouped departures; click a route number to draw that line (with stops). Mutual exclusion between “constructed” (user) and “transport” routes.
- Multiple basemaps, zoom-gated stop rendering for performance.
Tech stack
- Backend: Rust, Neo4j, SQLite. Flask (Python) remains as a UI/pathfinding shell.
- Frontend: Leaflet + JS/CSS, HTML template.
- External services: civiguild (stops, departures, trip stops), Nominatim (geocoding), OSRM (bus geometry), Carto/OSM tiles.
Knowledge graph usage
- Neo4j stores the transit knowledge graph: Stop nodes with geocoordinates and stop metadata; relationships for transit links (
TRANSIT_ROUTE) and walking links (WALK). Route segments for a givenroute_nameare queried directly from this graph. - Pathfinding (Flask shell) runs Neo4j
shortestPathover the graph, then post-processes legs (merge walks, add connectors, simplify geometry). - Rust endpoints read from Neo4j for stop listings and can be extended to serve graph-driven routes; SQLite holds GTFS tabular data that complements the graph.
Layers
- Rust API (
api/src): Rocket routes for agency, stops, departures, trip stop sequences; uses SQLite (GTFS tables) and Neo4j (stop graph). - UI/pathfinding shell (
app.py): Flask routes for UI, Neo4j shortestPath, route geometry, trip stops proxy, minimal stop info. - Frontend (
templates/index.html,static/script.js,static/style.css): Leaflet map, search/pins, routing render, popups, basemap control. Stop popups and departures are rendered client-side in JS. - Data: Neo4j transit graph (Stop nodes,
TRANSIT_ROUTE/WALKedges). SQLite GTFS for Rocket endpoints. civiguild for departures and trip stops.
Configuration & secrets
- Rust:
NEO4J_URI,NEO4J_USER,NEO4J_PASSWORD; Rocket databasetransportconfigured viaROCKET_DATABASESfor SQLite; optional live update settings. - Flask: same Neo4j vars;
DEPARTURES_DAYS_AGO,DEPARTURES_API_BASEfor legacy use.
Rust (Rocket) routes (api/src/endpoints.rs)
GET /agency/<id>— agency from SQLite.GET /stops— all stops (Neo4j).GET /stops/<id>— stop details from SQLite.GET /departures/<stop_id>— departures (timestamp + delay) from live store/SQLite.GET /trips/allStops/<trip_id>— ordered stop sequence for a trip from SQLite.
Flask shell routes (app.py)
/— serves UI with cached stops./api/path— Neo4jshortestPathoverTRANSIT_ROUTE|WALK, merges legs, adds walk connectors./api/trip_stops/<trip_id>— proxies civiguild trip stops./api/route/<route_name>— Neo4j segments + stops for a route./api/stops/<stop_id>— minimal stop metadata (departures handled in JS directly from civiguild).
Frontend flow
- Constructed route: start/end (search or pin) →
/api/path→ legs drawn; map click clears constructed route/pins once built. - Transport route: click stop popup route → civiguild trip stops or
/api/route/<name>→ draw line + stops; clears constructed route first; map click clears. - Departures: JS fetches civiguild
departures/<stop_id>, groups by route, shows minutes only.
Middleware: None explicit beyond Rocket/Flask defaults; per-route try/except.
Rust (Rocket)
GET /agency/<id>→ 200 JSON or 404.
Example:curl http://localhost:8000/agency/1GET /stops→ 200 JSON array of{stop_id, stop_name, stop_lat, stop_lon}.
Example:curl http://localhost:8000/stopsGET /stops/<id>→ 200 JSON stop info or 404.
Example:curl http://localhost:8000/stops/17651GET /departures/<stop_id>→ 200 JSON array of{route_short_name, trip_id, departure_timestamp, delay, live}.
Example:curl http://localhost:8000/departures/17651GET /trips/allStops/<trip_id>→ 200 JSON array of ordered stops.
Example:curl http://localhost:8000/trips/allStops/17651
Flask (UI/helper)
GET /api/path?start=<stop_id>&end=<stop_id>[&start_lat&start_lon&end_lat&end_lon]GET /api/route/<route_name>GET /api/trip_stops/<trip_id>GET /api/stops/<stop_id>
Frontend-only calls
- civiguild departures:
https://civiguild.com/api/departures/<stop_id> - Nominatim search; OSRM routing for bus geometry.
.
├─ api/
│ └─ src/
│ ├─ main.rs # Rocket launch: DB pool, Neo4j graph, live update store, mounts endpoints
│ ├─ endpoints.rs # Routes for agency, stops, departures, trip stops; uses SQLite + Neo4j
│ ├─ liveupdates.rs # Live update store (DashMap), listener scaffolding
├─ app.py # Flask shell: serves UI, pathfinding via Neo4j, route segments, trip stops proxy
├─ static/
│ ├─ script.js # Leaflet UI: search/pins, routing render, popups, transport routes, clearing logic
│ └─ style.css # Styling for layout, map controls, popups, pins
├─ templates/
│ └─ index.html # HTML shell; injects stop cache and map center
├─ requirements.txt # Python deps for Flask shell
└─ README.md / README(5).md
Rust API
- Prereqs: Rust toolchain, Neo4j running, SQLite GTFS DB configured. Env:
NEO4J_URI,NEO4J_USER,NEO4J_PASSWORD,ROCKET_DATABASESfor SQLitetransport. - From
api/:
cargo run- API on Rocket default port (e.g., 8000).
Flask + UI shell
- Prereqs: Python 3.10+, Neo4j populated, internet for civiguild/Nominatim/OSRM.
- Install:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt- Env:
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=yourpassword
DEPARTURES_DAYS_AGO=0
DEPARTURES_API_BASE=https://civiguild.com/api/departures
- Run UI:
python app.py- Open
http://localhost:8080.
- Rust:
cargo build --release; deploy Rocket with Neo4j + SQLite access; configure env vars andROCKET_DATABASES. - Flask shell: run under WSGI (gunicorn/uwsgi) if retained; ensure Neo4j/env vars.
- Static assets served directly; no bundler. No explicit env profiles; rely on env vars.
Storage
- Neo4j: stop graph with
stop_id,name,lat,lon;TRANSIT_ROUTE/WALKedges. Used by Rust and Flask. - SQLite: GTFS tables (Agency, Stops, Routes, Trips, StopTimes) for Rocket endpoints and departures.
- In-memory: DashMap for live departures (Rust).
Integrations
- civiguild: stops (cached in Python), departures (frontend direct), trip stops (Flask proxy), live data for Rust.
- Nominatim: geocoding (frontend).
- OSRM: bus routing geometry (frontend).
- Carto/OSM: basemap tiles.