All apps

§ App / Delivery Route Planner

Build a delivery route planner with React and unmap

How the unmap Delivery Route Planner is put together: one van's round on a live map, stops added by search, an optimized order compared with yours, and an arrival schedule.

Delivery Route Planner is a lightweight dispatcher for a small delivery business: one van leaves a warehouse, visits up to eight customers and comes back. Plan the round on a live map, add stops by searching for them, reorder them, let unmap optimize the order and compare it with yours, read an estimated arrival schedule, and play a simulated courier through the day. It runs at apps.unmap.dev/route-planner, and its source is a standalone Next.js project you can clone and run with your own key.

The round belongs to Chinook Parcel Co., a fictional courier in Calgary. The company, the customers, their addresses, the delivery notes, the service times and the 08:30 departure are sample data, and the courier is a simulation, not GPS or live tracking. The app labels both wherever they appear. The map, the routes, the drive times, the optimized order, the drive-time table and the search are live unmap.

What it does

  • The round on the map: the depot, numbered stops coloured by their state, and the route. Each leg is its own route request, asked for once and kept. A leg with no answer is drawn as a gap, never as a straight line.
  • Edit the stops: add one by searching for it (results lean towards Calgary, and any Canadian place is accepted), remove it, or move it earlier or later with buttons. Up to eight stops.
  • Optimize: one request, only when you press the button. The depot stays fixed as the first and last stop, and only the stops between are reordered.
  • Compare your order with unmap's: both totals side by side and the optimized sequence in your stop numbers. A difference is called a saving only when it is larger than the two calculations could differ by, and "Optimized by Unmap" appears only when the order on screen is the one the API returned.
  • Arrival schedule: the departure time, each leg's drive time and each stop's service time. After a leg with no answer it stops estimating instead of guessing.
  • Drive times between stops: a table from one matrix request over the depot and every stop, loaded only when you open it. A blank cell means the engine had no answer for that pair, not that no road exists.
  • Directions: turn-by-turn steps for each leg, in English or French, on request.
  • Simulated courier: play, pause, skip to the next stop or reset, at 60, 120 or 300 times real speed. It follows each leg's own route at that leg's drive time and waits each stop's service time. With reduced motion it moves a simulated minute at a time and the camera never animates.

What it costs in API calls

Loading the sample round makes one /route call per leg, eight in all. Reordering asks only for legs the app has not seen yet, and switching language asks for nothing new. Optimizing is one /optimized-route call, the drive-time table is one /matrix call, and directions are one /route call per leg and language, each only when you ask. Search is debounced and cancels stale requests. The simulation calls nothing.

Eight stops is the most a round can hold because /optimized-route takes ten points and the depot is sent twice, first and last.

Built from the registry, unmodified

The map, the pins, the route line, the directions and the search are registry items copied in unmodified, and the app's own code calls the API only through @unmap/sdk. Installing the same pieces into your project is one command:

npx @unmap/cli add map marker-popup map-controls route-line route-steps geocoder

What it does not show

These are limits of the API or of the app's scope, and it says so instead of hiding them:

  • Traffic. Drive times are free-flow estimates with no live or predicted traffic, so neither the schedule nor the simulation is a promised arrival time.
  • Real vehicles. The courier is a playback along the route, with no GPS and no reporting.
  • More than one vehicle, time windows or capacity. One van, one round, and no saved plans or proof of delivery.
  • Places outside Canada. Routing and search cover Canada. A stop outside it is refused, and a stop with no road connection is shown on the stop as having no road route.
  • Closures everywhere. Closure warnings cover Ontario, Alberta and British Columbia, where authoritative feeds exist.

Where optimization is switched off on a deployment, the API answers 404; the app keeps your order, says optimization is not available, and stops asking.

Run it yourself

You need Git, Node.js 24, pnpm and a browser key from the dashboard that allows http://localhost:3202.

git clone --depth 1 https://github.com/unmap-dev/route-planner.git
cd route-planner
pnpm install --frozen-lockfile
cp .env.example .env.local

Set NEXT_PUBLIC_UNMAP_KEY in .env.local to your key, run pnpm dev, and open localhost:3202. Start by replacing data/route.sample.json with your own plan: parsePlan() in lib/plan.ts checks it and lists every problem it finds. To follow real vehicles, replace the simulated clock in hooks/use-simulation.ts with your fleet's positions. The Routing API reference covers Optimize and the Matrix behind the table.