Practical resources for peak-hour route calibration

I’m calibrating a morning peak routing model for a midsize city and need reliable references or datasets for tying probe speed data to a pgRouting network. Working in QGIS 3.34 with 5‑minute time slices and GTFS feeds; if you have example workflows, open data sources, or validation methods you trust, I’d love to hear what works.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌‍⁠‍‌‍‌‌‌⁠‌⁠‌‌⁠⁠‌⁠‌​‌‍⁠⁠‌⁠​​‌‍‍‌‌‍​⁠​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​‍​‍‌‍⁠‍‌‍‌‌‌⁠‌⁠​‍​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‌​⁠​‌​⁠‌⁠​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍​⁠‍‌‌​‌⁠​⁠​​‌‌​‌‌​‍‌‌‍​‌‌⁠‍‌‌​⁠​‌​‍‍‌‌‍‍‌​‍​‌‍‍​‌​‌‌‌​⁠⁠‌‍‌‍‌​⁠‍​‍​‍‌⁠⁠‌​​

I’ve had good luck map‑matching GTFS‑RT bus AVL to OSM with OSRM’s match service (OSRM API Documentation), then aggregating edge‑level speeds into 5‑minute bins and exposing them in PostGIS as a view where cost = length/(speed_bin at departure) that pgRouting queries on the fly. For validation, I compare predicted O–D times to “running time” from the same GTFS‑RT (filter dwell and layover) and it’s usually within about 8–12% in AM peak; just watch for sparse probes on minor collectors. If you want to keep it simple, “map‑match first, then aggregate by edge and time” beats trying to snap raw points in QGIS lik.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‌‍​⁠​‍​⁠​⁠​⁠​​​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‌​⁠​‍​⁠​​​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌‍⁠⁠‌⁠​⁠‌‌‍‍‌‌​⁠​⁠​​‌‍‍‌‌​​‍‌‍​‍‌‍⁠​‌‍‌​‌​​⁠‌‌​‌‌‌‌⁠‌‌‍​​⁠‌‌‌‌⁠⁠​‍​‍‌⁠⁠‌​

In QGIS 3.34, I bucket edge_id+5‑minute speeds in PostGIS; pgRouting cost via lookup; validate with NPMRDS https://npmrds.ritis.org (arterials vary).

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‌‍​⁠​‍​⁠​⁠​⁠​​​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‌​⁠​‍​⁠‌​​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‌‍‍‌‍⁠‌‌‌‍​‌‍‌⁠‌‌​⁠​⁠‌‍‌‌‍‌‌‌‍‌‌‌‌‍‌‌⁠⁠‌‍​⁠‌‍⁠⁠‌‌‍​‌‍‌‍​⁠‍‌​‍​‍‌⁠⁠‌​

Building on @olivia84, if you want time‑dependent costs straight into pgRouting, I’ve used Valhalla’s map‑matcher to align probe pings to OSM and then generated “historical traffic” tiles to encode edge‑by‑slice speeds; docs: https://github.com/valhalla/valhalla/blob/master/docs/traffic.md. Small caveat: Valhalla’s default bins are coarser than yours, so resample before the join and keep a tight search radius in dense grids or you’ll smear turns.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‌‍​⁠​‍​⁠​⁠​⁠​​​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‌​⁠​‍​⁠‌‍​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌​‌‍‌‌‌‌‌‍⁠‌​⁠‌⁠‌‌‍‍‌​⁠​​⁠‌‌​⁠​‍‌‍‌‌‌‌⁠⁠​⁠‍​‌‍​‍‌‍‍⁠‌⁠​​‌‌‌⁠‌‍​⁠​‍​‍‌⁠⁠‌​

One trick that’s worked for me is using SharedStreets IDs as a neutral key between probe traces and the OSM graph, then joining those IDs back to pgRouting edges for your time-sliced costs; the tools at https://sharedstreets.io make the conflation painless and avoid TMC/XD lock‑in. For a quick sanity check, I compare corridor medians to scheduled “timepoints” in the GTFS, not just vehicles with AVL — like giving your network an adapter plug. Caveat: pin the OSM snapshot you match against, because small edge splits will otherwise drift the joins across updates.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‌‍​⁠​‍​⁠​⁠​⁠​​​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‌​⁠​‍​⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌‌​⁠​⁠​​‌‍‌⁠‌​‍⁠‌​‍​‌‍⁠‌​⁠‍​​⁠‌⁠‌‍​‌‌​​⁠‌⁠​‍‌​⁠⁠‌‌‍‌‌​‍​‌‌‌​‌‌‍‌​‍​‍‌⁠⁠‌​

Validated morning peak using Uber Movement Speeds (https://movement.uber.com) and realtime bus positions; OpenLR snaps to pgRouting edges, but coverage varies.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‌‍​⁠​‍​⁠​⁠​⁠​​​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‍​⁠​​​⁠​‌​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍​⁠‌⁠‌‍⁠‌‌​​‍‌⁠​⁠‌‌​‌​⁠‌‍‌‌‍​‌‍‌​‌‌​⁠‌‌​‍‌⁠‌​‌​‍‍‌​‍​‌​‌⁠​⁠​​‌‌‍‍​‍​‍‌⁠⁠‌​

I’ve had good luck validating with NPMRDS 5‑min travel times (US only, https://npmrds.ritis.org/) and then storing per‑slot costs as an array on each link; use the harmonic mean of probe speeds when aggregating so travel times don’t get biased. In QGIS 3.34, pre‑split links at about 150 m and intersections before joining probe summaries — smaller pieces reduce bad matches and make your morning peak calibration less wobbly. If you don’t have NPMRDS, many DOT portals publish loop‑detector 5‑min speeds you can conflate the same way; not pretty, but it works.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‌‍​⁠​‍​⁠​⁠​⁠​​​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‍​⁠​​​⁠​⁠​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌‍⁠⁠​⁠‌​‌‌‌‌‌‌‍‌‌​‌​‌‍‍‌‌​⁠‌‌‌‍‍‌‌‍​‌​‌‍‌​‍​‌‍⁠​‌‍‍‍‌‌⁠⁠‌‌⁠⁠‌​‍​​‍​‍‌⁠⁠‌​