Skip to main content

The problem with straight-line distance

The drive time ML model was trained on OSRM-derived road times for ~1,800 airport pairs. When a requested route is not in that set, the predictor needs to estimate the road distance somehow. A naive approach — using great-circle (haversine) distance as a proxy for road distance — works acceptably on flat terrain but fails badly in mountainous regions. Roads through the Appalachians, Rockies, or Ozarks wind significantly more than the crow flies. On a route like Roanoke VA → Pittsburgh PA (219 mi straight-line), the actual road distance is closer to 290 mi with a drive time of ~420 minutes — a haversine-based model would predict roughly 250 minutes, a 40% underestimate.

Fallback chain

For any route not found in the training route_stats, DriveTimePredictor follows this chain:
The same chain applies to both predict() and estimate(). Routes in route_stats (those seen during training) always use the stored road distance and the ML ensemble — OSRM is not queried.

OSRM public endpoint

The predictor uses the public OSRM API at routing.openstreetmap.de. No API key is required. Responses are cached in memory for the lifetime of the TransportPredictor instance, so repeated calls for the same pair incur only one network request.
If the OSRM query times out or returns an error after two attempts, the predictor falls back to the haversine formula.

West Appalachia accuracy

After this fix, drive time accuracy across West Appalachian routes: Routes already in route_stats (CRW, CKB, BKW) were unaffected — they were accurate before and remain so. Mean absolute error across 20 WA routes: 0.9%.

Air fare — sparse route handling

A related fix applies to AirFarePredictor. Routes with fewer than 10 DB1B tickets (too sparse to pass training filters) fall back to generic market defaults. Previously the default used a hardcoded distance = 1500 miles, which inflated fare estimates for short regional routes. The fix resolves the actual great-circle distance between origin and destination and uses that instead: