Skip to content

About

Order a list of stops into the shortest driving route — nearest-neighbour plus 2-opt, in a single dependency-free HTML file.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

Track Optimizer

Drop in a list of stops, get the shortest driving order — and a Google Maps link you can hand to whoever is doing the driving.

Live demo No build step No billing account

Google Maps will happily route you through ten stops. What it will not do is tell you what order to visit them in — it drives them in the order you typed. For a delivery round, a day of client visits, or a sales route, the order is the entire problem: the same ten addresses can be a 40 km afternoon or a 65 km one.

This is a single HTML file that solves that ordering, draws it, and exports it.


What it does

  • Orders the stops, not just routes them — the whole point.
  • Autocomplete on every field, so "Obelisco" becomes a real address with real coordinates instead of a string that fails at routing time. Biased to Argentina, and to Buenos Aires within it.
  • Tells you what you saved — the result is compared against the order you typed the stops in, so the number is honest about what the solver bought you.
  • Exports two ways: a plain-text itinerary for WhatsApp, and a google.com/maps/dir link that opens the whole multi-stop route in the Maps app on a phone.
  • Nothing leaves the browser except the map, geocoding and routing calls themselves. No backend, no account, no stored routes.

The routing problem

Ordering N stops to minimise total distance is the Travelling Salesman Problem. It is NP-hard: 12 stops is already ~20 million possible orders, so checking them all is off the table. The practical answer is a fast heuristic that gets close, and this uses the standard two-stage one.

Stage 1 — nearest neighbour

Start at the origin, repeatedly hop to the closest unvisited stop.

It is instant and it is usually mediocre, for a structural reason: greedily taking cheap hops early strands the far-flung stops for the end, so the route finishes with one long, expensive limp home. Nearest neighbour typically lands 20–25% above optimal.

Stage 2 — 2-opt

Take the greedy route and repeatedly ask: if I reverse the segment between stops i and k, does the total get shorter? If yes, keep it. Repeat until no reversal helps.

before:  A ─── B      after:  A     B
              ╳                │     │
         D ─── C               D ─── C

Geometrically, a crossed path is always longer than the uncrossed version of the same two edges — triangle inequality. So 2-opt is precisely the repair for what nearest neighbour gets wrong: it uncrosses the route. It usually pulls the result to within 5% of optimal, which for a dozen stops is close enough that the remaining gap is smaller than traffic variance.

The origin stays pinned at index 0 throughout — you asked to start somewhere, so the solver is not allowed to reorder that away.

Distance: haversine to decide, roads to draw

The solver runs on great-circle (haversine) distance, not driving distance. That is a deliberate trade: driving distances for N stops would need an N×N matrix — quadratic in stops and in requests — for an ordering that straight-line distance already gets right in the overwhelming majority of urban cases. The routing service is called once, at the end, to draw the real road path and measure the order the solver picked.

Where this shows: a river, a motorway with few crossings, a one-way grid. Two stops 800 m apart across water can be a 6 km drive, and the solver does not know that. For dense city rounds — which is what this is for — it does not matter.


Services

Everything runs on open infrastructure. No billing account, no credit card, no Cloud console — which is the point: a clone of this repo works immediately for whoever cloned it.

Concern Service Credential
Basemap tiles CARTO Positron, over OpenStreetMap data free key, no account
Geocoding / autocomplete Nominatim (OSM) none
Driving routes OSRM demo server none
Export link google.com/maps/dir none — it is just a URL

Two notes on doing this politely:

  • Nominatim allows about one request per second. The address box therefore debounces at 450 ms and aborts the in-flight request on every new keystroke, rather than firing a lookup per character. If you fork this and expect real traffic, run your own Nominatim or use a paid geocoder.
  • The OSRM demo server is for light use. Same advice: self-host for anything beyond a demo.

The CARTO key sits in a named constant at the top of the script. It is a client credential — it ships to every browser that loads the page, exactly like a tile URL — so it is not, and cannot be, a secret. Swap in your own if you fork.

Exporting with coordinates, not addresses

The Maps link carries lat,lng pairs rather than address text. Nominatim returns the full administrative hierarchy as an address — 2353, Miñones, Bajo Belgrano, Belgrano, Comuna 13, C1428AID, Argentina — which Google's geocoder rejects outright. Coordinates cannot be misread the way a street name shared by three suburbs can.


Running it

There is no build step, no npm install, no bundler. One file.

git clone https://github.com/DecoudJuan/TrackOptimizer.git
cd TrackOptimizer
python -m http.server 8000       # or any static server

Then open http://localhost:8000.

React, ReactDOM and Babel come from a CDN and JSX is compiled in the browser. That is a genuinely bad idea for a production app and a genuinely good one for a tool that has to stay forkable and readable for years — the whole thing is index.html, and it will still open in 2035.


Stack

UI React 18 via CDN, JSX compiled in-browser by Babel standalone
Map Leaflet 1.9 + CARTO Positron raster tiles
Geocoding Nominatim, with a debounced abortable autocomplete written by hand
Routing OSRM /route/v1/driving, GeoJSON geometry drawn as a polyline
Solver Plain JS, no dependencies — haversine, nearest neighbour, 2-opt
Icons Inline SVG sprite, no icon library
Build None

The solver functions (haversine, nearestNeighbour, twoOpt) are pure and touch no map object, so they are readable and testable on their own.


Limits

  • Straight-line optimisation, as explained above. Rivers and motorways can fool it.
  • 2-opt is O(n²) per pass. Fine up to a few hundred stops.
  • Public demo services, rate-limited by courtesy rather than by quota. Fine for a round of stops, not for a fleet.
  • No time windows, no vehicle capacity, no multi-vehicle. This solves TSP, not VRP. Those are different, much larger problems.

License

MIT

About

Order a list of stops into the shortest driving route — nearest-neighbour plus 2-opt, in a single dependency-free HTML file.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages