โ“˜ How we estimate transit times

โ† Back to map

Travel times in SUSTech Transit are computed from real geometry (OpenStreetMap footways) + real elevation (Open-Elevation API), using three simple formulas. You can tune all the constants by editing state.PARAMS in web/static/app.js.

Speeds (defaults)

ConstantDefaultWhat
walkFlatKmh4.5 km/hPedestrian flat-ground speed
bikeFlatKmh16.0 km/hCasual cyclist flat-ground speed
toblerMaxKmh6.0 km/hPeak Tobler speed (at -5% downhill)
toblerAscentCoef3.5Tobler's slope-sensitivity k
bikeClimbCoef10.0Bike climb-fraction multiplier
transferPenaltyMin5 minPer-bus-transfer pad
waitHeadwayMin10 minTypical bus headway

๐Ÿšถ Walk โ€” Tobler's hiking function (1993)

The classic model: speed is maximized at a gentle downhill slope (-5%) and falls off rapidly uphill. Slope s is in m/m (rise/run); positive = uphill.

v(s) = toblerMaxKmh ยท exp(-toblerAscentCoef ยท |s + 0.05|)
duration_min = (distance_m / 1000) / v(s) ยท 60

At +5% uphill: v โ‰ˆ 2.4 km/h. At -15% downhill: v โ‰ˆ 3.0 km/h. Symmetric around the optimum.

๐Ÿšด Bike โ€” climb-fraction model

Cycling is dominated by vertical gain per unit distance. Downhill is essentially free (gravity + freewheel). Uphill requires real work proportional to the climb fraction.

climbFraction = max(0, climb_m) / max(distance_m, 1)
speed_kmh = bikeFlatKmh / (1 + bikeClimbCoef ยท climbFraction)
duration_min = (distance_m / 1000) / speed_kmh ยท 60

At 6.25% climb (typical ่”ๅ›ญโ†”ๆฌฃๅ›ญ over ่Œถๅ…‰ๅฑฑ): ~8 km/h. At 15% climb: ~5.3 km/h. Flat: 16 km/h.

๐ŸšŒ Bus โ€” 2.5 min/km + transfer penalty

duration_min = (distance_m / 1000) ยท 2.5 + 0.1 + transferPenaltyMin / 2

The 2.5 min/km rate matches the campus schedule's minuteOnRoad = 25 for the ~10 km inner loop (โ‰ˆ10 km/h average). Elevation doesn't matter for a diesel bus. The half-transfer penalty approximates average wait time; full transfer cost is a future TODO.

How the graph is built

Three kinds of nodes:

Three kinds of edges:

If footways.json is missing or empty, the graph falls back to direct facility-to-facility edges within 250 m (the old "straight line" behavior).

Mode filtering

UI modeAllowed edge modes
walkwalk only
bikewalk only (cyclist on path)
busbus only
transitwalk + bus

Tuning guide

All formulas are in web/static/app.js, function segmentDuration(lat1, lng1, lat2, lng2, mode). Edit state.PARAMS at the top of the file to taste. For best results, calibrate against your own timing:

  1. Pick a route you walk regularly (e.g. dorm โ†’ ้ฃŸๅ ‚).
  2. Time it with a stopwatch.
  3. Compare to the model's estimate โ€” adjust walkFlatKmh if it's consistently off.
  4. For a hilly route, adjust toblerAscentCoef if the uphill portion is over/underestimated.
Want more realism? Future improvements (in priority order):
  1. Schedule-aware wait time โ€” use the bus timetable to compute actual wait at the next departure.
  2. Per-transfer cost โ€” charge the full transfer penalty only when the path actually changes bus lines.
  3. Live bus mode โ€” if a bus is currently within 200 m of your origin, treat it as a free ride.
  4. Snap-to-2-vertices โ€” currently each facility snaps to the single nearest footway node; for proper path geometry we should snap to the two nearest (one in each direction).
  5. UI sliders โ€” let users pick their own walk/bike speeds ("I'm fast", "I hate hills").
โ† Back to map