01 · Optimisation

Route Optimiser

Turning a list of places to visit into drivable, walkable days: built for field-sales routing, shown here as a trip planner on the same engine.

Problem

Anyone planning a week of visits (a field-sales rep, a delivery run, a tourist) plans by list or by map, and neither is travel time. Two stops five minutes apart on a map can be forty apart by road. The result is wasted travel, missed opening windows, and no way to explain why Tuesday looks the way it does. I built the engine for field-sales routing at Broadplace, where the client data can't be shown; the public version plans a multi-day trip with the same code.

Method

Stops are geocoded, then a travel-time matrix is pulled from the Google Distance Matrix API, per mode, keeping the fastest, with a short-hop rule that walking wins under twelve minutes. A hand-rolled k-means clusters stops on that matrix rather than on coordinates, so days group by how long it takes to get between places. Each day is ordered nearest-neighbour from the start point, then walked with a clock: wait for openings, honour fixed appointments, flag anything that runs past closing. Engine in Python and FastAPI, Docker on Railway; front end Next.js with Google Maps on Vercel; an Airflow DAG re-plans on a schedule. A separate offer page frames it as a pilot or full build for a client.

Evaluation

On an 8-stop, 5-day test itinerary scored with the same travel-time matrix and the same scheduler, the optimiser's plan needs 203 minutes of walking against 240 for an alphabetical baseline (15% less) and 305 for the places in the order they were entered (33% less), with no opening-hour or appointment constraints broken by any of the three. The test is small, so treat the percentages as indicative; nearest-neighbour ordering is typically 10 to 25% off the true optimum, so this is a floor, not a ceiling. The script is scripts/evaluate_baseline.py in the repo.

The decision it drives

Which stops go on which day, in what order, and a defensible answer when someone asks why.