Skip to content

RunFlow

RunFlow plans running routes through Vienna with 72 % fewer traffic lights per kilometre – for 7 % more distance.

Data sheet

Identification no.JS-2026-01
Period01–09/2026
RoleConcept, evaluation and testing; code written with AI assistance
ToolsPython · FastAPI · networkx · OpenStreetMap · Leaflet · Docker
Key result3.63 → 1.00 traffic lights per km (−72 %) for 7 % more distance; 20 of 20 test loops within ±10 % of the target distance
StatusLive demo
Legal owner
Jakob Steinberger
Identification no.
JS-2026-01
Title
RunFlow
Document type
Test report
Date of issue
08/10/2026
Created by
Jakob Steinberger
Approved by
Rev. · Sheet
F · 2/7
Language
ENDE
ISO 5456-2

1Result

Zone B
AB

Example route A → B: 4 traffic lights over 2.51 km. Dashed: the routes at the other values.

Lights / km
1.00
Detour (median)
+33 %
0 m50 m100 m150 mDetour vs. straight line %Lights / km

Measured on 40 random routes (1.5–4 km) in the inner districts, current network, 05/10/2026. A trade-off as in vehicle routing: every metre of detour has to pay for itself through less waiting.

Live demo

2Problem and motivation

Zone C

Red lights break a runner's rhythm, and common running apps only optimise distance. In Vienna that means a signalised crossing every few hundred metres.

3Set-up

Zone D
  1. 01Map data: OpenStreetMap
  2. 02Path network: 131,000 nodes, metres per path type
  3. 03Cost function: length × factor + 100 m per lightMy part
  4. 04Search: A*
  5. 05Loop calibration: ±8 %My part

The cost function is a trade-off just like in logistics: each traffic light costs 100 “virtual metres” (≈ 36 s of waiting at 6:00 min/km). Quiet paths count 1.0, sidewalks along main roads 1.2, stairs 1.5.

This is the real decision: what is a traffic light worth?

4Verification

Zone E

What went wrong

The first version looked finished and drew plausible routes. Only measuring 40 test routes revealed that, because of a detail in the networkx library, every edge got the same weight – the router counted street segments instead of metres and traffic lights. Re-measuring on the rebuilt network (Oct 2026) gave clearly higher values: the old network most likely lost traffic lights during simplification, so the old absolute figures were too optimistic.

Looked finished. Wasn't.
First version vs. fixed router, 40 routes, old network (29 Sep 2026). Valid only as a comparison between the two.
MetricFirst versionFixed
Traffic lights per km (A → B)1.580.38
Route length vs. straight line (median)1.41×1.27×
Loops within ±10 % of target distance0 %95 %
Start-up time / memory15 s / 1.8 GB1.5 s / 0.4 GB

5Insight and next iteration

Zone F
  • Code that works in a demo can still be wrong. A measurement with numbers catches what a glance at the map misses.
  • Separate facts (stored in the network) from policy (applied at start-up): the weighting can change without downloading data again.
  • Real data is messy: OpenStreetMap maps one crossing as 2–5 signal nodes, which had to be grouped first.

Next iteration

Movement-aware penalty: turning right at a signalised junction without crossing should not be penalised. That needs an edge-based graph.

Technical details: Cost function and search

w(u,v) = Σ length per path type × factor (+10 % on cobblestones) + 100 m when v enters a new signalised crossing.

All factors ≥ 1, so the straight-line distance never overestimates the remaining cost: A* returns the optimal route for this cost model.

Signal nodes joined by an edge ≤ 30 m count as one crossing (88 % of signal nodes have a neighbour within 30 m).

Technical details: Loops

Two helper points form a right-angled triangle with the start. Streets already used cost ten times as much (bridges twice) so the way back differs; dead-end spurs are removed.

If the length is off by more than 8 %, the triangle is rescaled (max. 5 attempts). Test on the current network: 20 of 20 loops (5 and 10 km) within ±10 %, median deviation 3.8 %.