📓 travel-time.ipynb

A map of Zürich drawn in minutes, not metres.

Transitous/MOTIS | OSRM | OpenStreetMap
In [1]:
# Why this map?

Why this map?

In Zürich, how far something is says surprisingly little about how long it takes to get there. Stettbach to Stadelhofen is 5 km and 6 minutes on the S-Bahn through the Zürichberg; Seefeld to Wollishofen is 2 km across the lake and close to half an hour by public transport. Uetliberg is 6 km from Hottingen, yet 11 minutes by car and over 40 by tram and train. And the ranking flips with the mode: in free-flowing traffic the car wins almost every pair, on a bike the hills decide, on foot the lake and the rivers do.

This page precomputes door-to-door travel times between 49 places (all 34 statistical Quartiere plus 15 landmarks) for four modes. Public transport uses the real timetable for a Tuesday-morning departure; car, bike and walking use OpenStreetMap routing. Everything runs in your browser from a static JSON file.

In [2]:
# 1. Zürich redrawn by travel time

Zürich with every neighbourhood moved so that the distance between any two places matches how long the trip takes. Well-connected places get pulled together; badly connected ones get pushed out.

Minutes from
🗺️ real map ⏱️ travel time
loading dataset…
Drag the slider to morph between the real map and the travel-time map, and switch the mode to see how the city changes shape for a driver, a cyclist or someone on foot. Click a zone to colour the map by minutes from there. The 49 places are positioned from the data; the zone outlines are stretched along with them, so shapes are approximate.
In [3]:
# 2. Biggest gaps, where the choice of mode matters most
Mode
vs

The other tabs rank pairs where distance and time disagree the most. Click any row to redraw the travel-time map above from that starting point, with the destination outlined.
In [4]:
# Data & method

Data & method

  • Places. All 34 statistical Quartiere of the city of Zürich, each represented by a central, well-served stop inside its boundary, plus 15 landmarks (Hauptbahnhof, the airport, the two universities' hill campuses, Uetliberg, the Zoo, stadiums, …). Zone boundaries come from the city's open-data portal.
  • Public transport. The Swiss GTFS timetable routed by Transitous (MOTIS). Each place is anchored to a central stop, and for every ordered pair I ask for connections from stop to stop leaving at , keeping the fastest itinerary. Transfers are walked along real paths with at least 2 minutes per change, and 1 minute is added at each end for reaching or leaving the platform. Routing between stops rather than map coordinates matters: from a bare coordinate the router sometimes cannot reach every platform of a stop and misses direct lines. Waiting for the first departure is not counted, so this is the "you know the timetable" time. Spot checks against Google Maps for the same Tuesday-morning departures agree within 1.3 minutes on average.
  • Car. OSRM on OpenStreetMap: free-flow driving time. No traffic, no parking, no walking from the parking spot. This is the car's best case, and the gap to public transport in reality is smaller than shown.
  • Bike and walking. MOTIS street routing on OpenStreetMap, with elevation-aware bike profiles; the Zürichberg and Uetliberg are why some bike times look asymmetric.
  • Rankings. Effective speed is straight-line distance divided by travel time. "Close but far" looks at pairs under 3 km; "Far but quick" at pairs over 4 km.
  • Time-space map. Classical MDS on the symmetrised travel-time matrix, aligned to geography with an orthogonal Procrustes fit. Zone polygons are warped with inverse-distance-weighted displacement of the 49 anchor points, so shapes are indicative only.
  • Federal data. The timetable behind the transit numbers is the federal open timetable (opentransportdata.swiss), routed by Transitous; the federal geoportal has no routing service of its own.
  • Why not Google Maps? Its routing APIs need a billed key that cannot ship in a static page, and their terms forbid keeping results beyond 30 days or drawing them on a non-Google map, which rules out precomputed matrices, rankings and the warped map.

Everything is generated by scripts/travel-time/build.mjs from a small city config (scripts/travel-time/cities/zurich.json). Adding another city is a matter of listing ~40 places and, optionally, a GeoJSON of zone boundaries; the routers cover most of the world.