Version: 0.54.0 (pre-release) — see CHANGELOG.md License: See LICENSE
The open, free, self-hostable operating system between open mobility data and political decisions. For any municipality. In any country.
OpenMobility OS is a decision, prioritization, and transparency platform for the mobility transition. It is not a consumer routing tool. It is built for municipalities, city planning departments, mobility and climate offices, local politics, civic initiatives, journalists, and researchers.
It answers questions like:
- Which measures should a municipality prioritize first?
- Where are the biggest levers for climate, safety, and quality of life?
- Which streets, corridors, or neighborhoods need intervention first?
- Which measures are quick to implement, politically viable, and data-backed?
- How can these decisions be explained to the public?
Leipzig (Germany) and Utrecht (Netherlands) are the two real-city demo workspaces — one developing its cycling infrastructure, the other a world-leading cycling city. Both ship real data connectors out of the box (Leipzig adds a live UBA air-quality feed; Utrecht adds the OVapi Netherlands transit GTFS), and any remaining placeholder layer is clearly labelled illustrative demo so it is never mistaken for real data. The platform is city-agnostic from day one. Any city, small town, municipality, or region worldwide can be added through open data, APIs, and configuration — regardless of country, language, data source, or administrative structure.
- Deployment Modes
- Screenshots
- Quickstart
- Core Features (MVP)
- Using the Platform → full guide in docs/USER_GUIDE.md
- Architecture
- Production Deployment → full guide in docs/DEPLOYMENT.md
- Documentation
- Contributing
- Roadmap
OpenMobility OS supports three realistic operating models from the same codebase:
- Single-city — one municipality self-hosts its own instance. Set
DEPLOYMENT_MODE=single-cityandDEFAULT_WORKSPACE_SLUG=<your-city>. - Multi-city — a county, region, transport association, or federal
state hosts one instance for many places as separate workspaces. Set
DEPLOYMENT_MODE=multi-city. - Public demo — an open public instance showcasing example workspaces.
Set
DEPLOYMENT_MODE=public-demo. This is the default.
| Platform landing | Workspace dashboard | Interactive map |
|---|---|---|
![]() |
![]() |
![]() |
| Measures list | Measure detail | Data hub |
|---|---|---|
![]() |
![]() |
![]() |
Run
docker compose up --buildlocally to see the full platform today.
Requirements: Docker and Docker Compose (see docker.com for installation).
git clone https://github.com/richardkfm/openmobility-os.git
cd openmobility-os
cp .env.example .env
# Auto-generate secure random values for SECRET_KEY and ADMIN_TOKEN
python3 -c "import secrets; print(f'SECRET_KEY={secrets.token_hex(50)}'); print(f'ADMIN_TOKEN={secrets.token_hex(32)}')" >> .env
docker compose up --buildOpen http://localhost:8000 — you should see the platform landing page with four demo workspaces: Leipzig, Utrecht, Musterstadt, and Muster-Landkreis.
First boot fetches live OpenStreetMap data, so it can take a few minutes before the map layers are populated. Later restarts reuse what is already stored and come straight up. To refresh that data on purpose, run
docker compose exec web python manage.py seed_demo --resync.
-
Install Docker (if not already installed)
- Download Docker Desktop
- Run it and follow the installer
-
Clone the repository
git clone https://github.com/richardkfm/openmobility-os.git cd openmobility-os -
Create
.envwith secure secretscp .env.example .env
-
Fill in random values (copy-paste the entire command below)
python3 << 'EOF' import secrets with open('.env', 'a') as f: f.write(f'\nSECRET_KEY={secrets.token_hex(50)}\n') f.write(f'ADMIN_TOKEN={secrets.token_hex(32)}\n') print("✓ Secrets added to .env") EOF
-
Start the platform
docker compose up --build
-
Open in your browser
- Visit http://localhost:8000
- You should see four demo workspaces immediately
- To add a new workspace: click "New workspace" (requires ADMIN_TOKEN from
.env)
-
To stop, press
Ctrl+Cin the terminal. Data persists in thepostgres_datavolume until you rundocker compose down -v.
Deploying to a remote server (not
localhost)? The steps above are tuned for local use. Before you open the site by its domain or IP, add that host toALLOWED_HOSTSin.env— otherwise Django answers every request with a bare400 Bad Request(DisallowedHost). If you terminate TLS at a reverse proxy, also setCSRF_TRUSTED_ORIGINS. See Production Deployment and docs/DEPLOYMENT.md for the full hardening checklist.
- Multi-workspace — arbitrary number of cities per installation
- Target areas — draw an area on the map, attach a goal to it, and get a ranked plan of what to rebuild there: expected effect per segment with a source and an uncertainty range, and an explicit space budget saying how many parking spaces or traffic lanes the rebuild would cost. Targets for people killed or seriously injured are always zero — no percentage target is offered, and the plan reports how many people are still expected to be harmed rather than hiding it behind a percentage. See docs/AREA_TARGETS.md
- Interactive maps — MapLibre GL JS with configurable tile sources
- Parked cars, made visible — a map layer that fills kerbside parking and off-street car parks with one symbol per car, so the space a city gives to cars at rest can be counted instead of asserted. Where OpenStreetMap records parking the cars are drawn solid and counted as surveyed; where it is silent on a residential street they are drawn as a dashed outline and counted separately as modelled, and a street surveyed as having no parking is never filled in. Zoomed out, the estimate falls back to a density view on the kerbs and footprints it came from; where a city has too many cars to draw one by one, the legend says how many each symbol stands for. It reports capacity, not occupancy — how many cars fit, not how many are there now — and every number behind it (bay length, square metres per space, which street classes get a modelled kerb) is a parameter a workspace can override. See docs/PARKING_AND_WALKING.md
- A walking score for every street — each street is rated comfortable,
usable, tight, hostile or not enough data from ten inputs: how
separated the pavement is, how fast and wide the traffic is, whether there are
crossings, whether cars park on the kerb, and how wide, lit, step-free and
well-surfaced the walk is. A factor the data cannot speak to is dropped from
the average and named on the street — never scored as a middling value — and a
street whose known inputs fall below the coverage threshold gets no rating at
all rather than a guessed one. A pavement mapped as its own line is matched to
its street by proximity, but a street surveyed as having no pavement is never
overridden by a line that happens to run nearby. Every weight and threshold is
a parameter a workspace can override, and all of them are printed on the
workspace's Methodology page. On the map it is a coloured line in five named
bands, faint where the rating rests on thin data and pale grey where there is
not enough of it — no number is ever drawn over a street, and the 0–100
values appear only in a popup, only if you ask for them. A second mode colours
the same streets by how their width splits between parked cars and people on
foot. The Parking vs walking story view brings both halves up together.
Available at
/api/v1/workspaces/<slug>/walkability/; see docs/PARKING_AND_WALKING.md - Pedestrian space and off-street parking on the map — sidewalks, footpaths,
pedestrian streets and steps come in from OpenStreetMap under either of the two
ways it records them (a footway mapped as its own line, or a
sidewalktag on the street), and car parks arrive with their footprint and area rather than as a pin. A street that was surveyed and found to have no pavement is kept distinct from one nobody has mapped yet — the first is evidence, the second is silence, and a walkability reading that confused them would condemn every unmapped street - Map legend & distinct markers — an always-on legend below the map lists every active layer with a swatch shaped like how it is drawn; place-type point layers (schools, parking, transit stops, EV chargers, public buildings) use recognisable glyph icons instead of identical dots
- Per-layer display controls — each layer has a display-mode switch (dots / icons / heatmap for points, normal / thick / dotted lines, filled / outline areas), an opacity slider, and a "focus" toggle that dims every other layer so one stands out. All choices are remembered in the browser
- Base map switcher — pick a Light, Dark, or Satellite base map from a control on the map, independent of the UI theme; the choice is remembered
- Full-screen map mode — expand the map, its on-map controls, and the legend to the whole screen for presentations; Escape returns to the page
- Light & dark mode — a header toggle switches the whole UI between light and dark; the choice is remembered and defaults to the visitor's OS preference. The map base map follows the theme until you pick one explicitly
- Data hub — browser-based connector management:
- Add, edit, sync, enable/disable, and delete data sources from the UI
- Upload local CSV or GeoJSON files directly (no remote URL required)
- Connector description and config-field reference shown inline when adding a source
- Activate / deactivate toggle: disabled sources disappear from the map without being deleted
- Data connectors (fully implemented):
- CSV (URL or direct file upload) with column mapping and encoding detection
- GeoJSON URL with property remapping
- OpenStreetMap via Overpass API — twenty-two built-in templates
(
streets,streets_with_speed,bike_network,dedicated_bike_network,transit_stops,schools,parking,trees,parks_and_green,water_bodies,sealed_surfaces,districts,kindergartens,hospitals,public_buildings,pedestrian_crossings,ev_chargers_osm,street_parking,car_lanes,obstacles,parking_lots,footways) plus a custom-query escape hatch. The last five are the heavy ones — they pull every street or car park in the bounding box with its full geometry — so they are never synced for you: add them from the data hub when you want them.pedestrian_crossingsandstreets_with_speednow arrive normalized: crossings carry whether they are signalised, marked, dropped-kerb and tactile, and a speed limit is parsed into a comparable figure whatever the local tagging habit (km/h, mph, or a national zone such asDE:urban, whose value you set per workspace rather than the software guessing it) - Static GTFS zip (transit stops, routes, coverage) — enriches stops with average headway, night service, and barrier-free status from the schedule
- GBFS shared mobility — reads any operator's GBFS auto-discovery feed (bike share, e-scooters, mopeds, car sharing) and emits either available vehicles (free-floating, with form factor and propulsion) or stations (capacity, available vehicles, free docks, availability ratio). A planner's tool, not a rider app: pair with the map's heatmap mode to spot where shared vehicles cluster and where the pick-up gaps are. GBFS v2/v3. Optional availability gap analysis over time records snapshots (one click in the data hub, or on a schedule) and shows them as a map overlay colouring areas from "always available" to "usually empty", filterable by time window, hour of day, weekday and form factor — e.g. where do free cars run out on weekday mornings (see docs/SHARED_MOBILITY.md)
- Accident CSV — Destatis Unfallatlas (Germany) and generic international, both with optional bounding-box clipping to the workspace
- BikeMaps.org — global crowdsourced cycling collisions, near-misses, and hazards. Closes the well-documented under-reporting of vulnerable road users in police accident records (CC BY 4.0)
- CKAN open-data portal — pulls resources from any CKAN-based portal (GovData.de, opendata.leipzig.de, daten.berlin.de, EU Open Data Portal, …) and delegates parsing to the GeoJSON or CSV connector by format preference
- OGC WFS service — fetches a layer from any WFS endpoint (federal BKG WFS, state geoportals such as Geoportal Sachsen / NRW / Bayern, Umgebungslärm noise maps, …); auto-applies the workspace bbox
- Generic REST/JSON — pulls a feature list out of any JSON endpoint (UBA Luftqualität, Sensor.Community, OpenChargeMap, BNetzA Ladesäulenregister, ADAC, municipal APIs) with configurable list path and geometry mapping
- Mobilithek (German NAP) — gateway to the federal mobility-data
access point (BMDV, successor to mCLOUD); dispatches to the matching
parser based on a format hint. Supports both open distributions and
subscriber mode with an X.509 client certificate (DATEX II realtime,
restricted GTFS-RT). Built-in catalog browser (
browse_catalog()/python manage.py browse_mobilithek --keyword GTFS --formats) parses the Mobilithek DCAT-AP feed so operators can discover dataset titles, publishers, and distribution URLs without manually searching the portal - German federal presets — one-URL onboarding for four key German open-data sources: Bundesnetzagentur EV charging register, UBA air-quality stations, DWD climate stations, and BASt traffic counts. Each preset encodes the source's column names, encoding, and geometry mapping so operators don't need to configure them manually
- Zensus 2022 population grid — reads the Destatis 100 m grid-cell CSV (INSPIRE grid IDs in EPSG:3035), converts to WGS84 polygons, and emits demographic indicators per cell (population, under 18, 65+). Workspace-bbox-aware. Powers the equity-overlay rule that turns mobility measures into political arguments ("this serves 18 000 residents, 22 % of whom are children")
- KPI importers —
python manage.py import_kpis adfc|midreads survey CSVs and writes results into workspace goals:- ADFC Fahrradklimatest — biennial cycling satisfaction grades (1–6) per city, matched to workspaces by name
- MiD 2017 modal-split — walking, cycling, transit, and car share (%) per city/Kreis from the federal household travel survey
seed_unfallatlascommand — bootstraps a German workspace with real Destatis accident data clipped to the workspace bounds, replacing the illustrative demo layer- Rule-based measures engine — generates prioritized interventions from available data
- Transparent scoring — nine dimensions, every value traceable to its source
- Honest data provenance — every layer is labelled live source, official snapshot, or illustrative demo, shown publicly on the dashboard ("Data basis"), the map layer list, and the methodology page, so visitors always know when a number is real versus an example
- Public shareable URLs for every measure
- Methodology pages — every formula and data source documented
- New-workspace wizard — add any city in three steps; search for the place by name and the bounding box is filled in for you (geocoded via OpenStreetMap)
- Internationalization — German and English out of the box, extensible to any language
- Admin-token protection for write actions
- Modern, sober UI — an emerald-accented design with the Inter typeface and the OMOS brand mark, kept professional for a public-sector audience. The web font is loaded from a privacy-friendly CDN with a system-font fallback, so offline self-hosted installs render correctly with no proprietary dependency.
OpenMobility OS has a public read layer (no login) and an admin layer protected
by ADMIN_TOKEN. In short, you can:
- Explore workspaces from the landing page, dashboards (KPIs, goals, top measures, and a "Data basis" provenance summary), and the interactive map with toggleable layers, accident view modes, one-click story views, district score choropleths, PNG export, and saved views.
- Read measures — a filterable list and a fully transparent detail page where every score exposes its inputs, formula, confidence, and data sources.
- Administer (with the admin token) — log in, add a workspace via the geocoding wizard, manage and sync data sources in the data hub, browse upstream catalogs, import Unfallatlas accident data, and generate rule-based measures.
📖 The full step-by-step walkthrough lives in docs/USER_GUIDE.md — landing page, dashboard, map, measures, and every admin task (workspaces, data hub, connectors, catalogs, Unfallatlas, Mobilithek, Django admin, generating measures).
- Backend: Django 5 + GeoDjango + PostGIS + Django REST Framework
- Frontend: Django templates + Tailwind CSS + HTMX + Alpine.js + MapLibre GL JS
- Database: PostgreSQL 16 with PostGIS 3
- Multi-tenancy: path-based URLs (
/<workspace-slug>/...)
See CLAUDE.md for the full architecture overview, contribution workflow, versioning policy, and code style.
For a production instance exposed to the internet: harden .env
(DEBUG=False, strong SECRET_KEY/ADMIN_TOKEN, ALLOWED_HOSTS), run behind
an Nginx or Caddy reverse proxy with TLS (the web container listens on 8000;
keep the db port private), persist the postgres_data volume with regular
pg_dump backups, and optionally point the map at self-hosted tile and Overpass
servers.
To collect shared-mobility availability history automatically, start the opt-in
collector sidecar alongside your stack with
docker compose --profile snapshots up -d (see
docs/SHARED_MOBILITY.md).
🚀 The full step-by-step guide — including reverse-proxy snippets, backups,
and all map-tile / Overpass environment variables — lives in
docs/DEPLOYMENT.md. See
.env.example for the complete environment-variable reference.
This README is the concise entry point. Longer, detailed guides live as
separate Markdown files under docs/ — keep the README scannable and
move any in-depth content into a docs/*.md file with a short summary and link
back here.
- docs/USER_GUIDE.md — full walkthrough of every page and admin task (dashboard, map, measures, data hub, connectors, generating measures)
- docs/AREA_TARGETS.md — setting a goal for an area of the city, the space budget behind a rebuild proposal, and why targets for road deaths are always zero
- docs/PARKING_AND_WALKING.md — the parked-car layer and the walking score: what each one claims, the ten factors and their weights, the class thresholds, the per-workspace overrides and the API
- docs/SHARED_MOBILITY.md — connecting GBFS feeds (bikes/scooters/cars) and running availability gap analysis over time
- docs/DEPLOYMENT.md — production hosting: reverse proxy, TLS, backups, tile servers, environment variables
- CLAUDE.md — project philosophy, architecture, contributor guide
- CONTRIBUTING.md — first-contribution guide
- ROADMAP.md — development phases and upcoming features
- CHANGELOG.md — release notes
- NOTICE — third-party license attributions
/methodology/(inside a running instance) — scoring methodology, data sources, connector reference/about/(inside a running instance) — self-hosting guide, version info
Contributions are welcome from anyone — municipalities, developers, data journalists, planners, and researchers. Read CONTRIBUTING.md for the quickstart and workflow, and CLAUDE.md for project principles.
Near-term (post-MVP):
- Climate adaptation layer: trees, green areas, heat corridors, desealing
- Before/after map slider for measures
- Citizen feedback on measures
- GTFS-RT adapter for live transit delays (extends the Phase 9 static GTFS layers with realtime data)
See ROADMAP.md for the full phase-by-phase breakdown.





