Integrate delivery route planning¶
This document describes how to combine Geocode v4, Place v4, VRP and Route v4 to plan delivery routes for a fleet, from a list of orders to a route per vehicle.
The order of calls:
| Step | Job | API |
|---|---|---|
| 1 | Address to coordinates | Geocode v4, then Place v4 |
| 2 | Split stops between vehicles and order them | VRP; for a single vehicle, TSP v4 |
| 3 | Turn-by-turn directions and tolls | Route v4 |
| 4 | Confirm the vehicle reached the stop | Geofencing |
| 5 | Reconcile the distance driven | Map Matching |
Steps 1–2: from addresses to a route per vehicle¶
Geocode v4 and Place v4 turn addresses into coordinates. VRP takes the vehicles and the stops and returns a route per vehicle. The code below assigns three orders to two vehicles that start and end at the same depot.
// Node.js 18+. Call from your server so the API key stays private.
const KEY = process.env.VIETMAP_API_KEY;
const API = "https://maps.vietmap.vn/api";
async function getJson(url, init) {
const res = await fetch(url, init);
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
return res.json();
}
// Address -> [longitude, latitude] (Geocode v4, then Place v4)
async function toLngLat(address) {
const q = new URLSearchParams({ apikey: KEY, text: address });
const [best] = await getJson(`${API}/search/v4?${q}`);
if (!best) throw new Error(`Address not found: ${address}`);
const p = new URLSearchParams({ apikey: KEY, refid: best.ref_id });
const place = await getJson(`${API}/place/v4?${p}`);
return [place.lng, place.lat]; // VRP takes [longitude, latitude]
}
async function planRoutes(depot, addresses, vehicleCount) {
const depotLngLat = await toLngLat(depot);
const jobs = [];
for (const [i, address] of addresses.entries()) {
jobs.push({ id: i + 1, location: await toLngLat(address), service: 300 });
}
const vehicles = Array.from({ length: vehicleCount }, (_, i) => ({
id: i + 1, profile: "car", start: depotLngLat, end: depotLngLat,
}));
const plan = await getJson(`${API}/vrp?api-version=1.1&apikey=${KEY}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ vehicles, jobs }),
});
if (plan.code !== 0) throw new Error(`VRP: code ${plan.code}`);
return plan;
}
const plan = await planRoutes("197 Trần Phú, Phường 4, Quận 5, TP. Hồ Chí Minh", [
"10 Trần Huy Liệu, Phường 11, Phú Nhuận, TP. Hồ Chí Minh",
"56 Cô Giang, Phường 2, Phú Nhuận, TP. Hồ Chí Minh",
"1 Nguyễn Huệ, Quận 1, TP. Hồ Chí Minh",
], 2);
for (const route of plan.routes) {
const stops = route.steps.filter((s) => s.type === "job").map((s) => s.id);
console.log(`Vehicle ${route.vehicle}: orders ${stops.join(" → ")}, ${route.distance} m`);
}
console.log("Unassigned:", plan.unassigned.map((j) => j.id));
import os
import requests
KEY = os.environ["VIETMAP_API_KEY"]
API = "https://maps.vietmap.vn/api"
def to_lng_lat(address):
"""Address -> [longitude, latitude] (Geocode v4, then Place v4)."""
resp = requests.get(f"{API}/search/v4", params={"apikey": KEY, "text": address}, timeout=10)
resp.raise_for_status()
results = resp.json()
if not results:
raise ValueError(f"Address not found: {address}")
resp = requests.get(f"{API}/place/v4", params={"apikey": KEY, "refid": results[0]["ref_id"]}, timeout=10)
resp.raise_for_status()
place = resp.json()
return [place["lng"], place["lat"]] # VRP takes [longitude, latitude]
def plan_routes(depot, addresses, vehicle_count):
depot_lng_lat = to_lng_lat(depot)
jobs = [{"id": i + 1, "location": to_lng_lat(a), "service": 300} for i, a in enumerate(addresses)]
vehicles = [
{"id": i + 1, "profile": "car", "start": depot_lng_lat, "end": depot_lng_lat}
for i in range(vehicle_count)
]
resp = requests.post(
f"{API}/vrp",
params={"api-version": "1.1", "apikey": KEY},
json={"vehicles": vehicles, "jobs": jobs},
timeout=60,
)
resp.raise_for_status()
plan = resp.json()
if plan["code"] != 0:
raise RuntimeError(f"VRP: code {plan['code']}")
return plan
plan = plan_routes("197 Trần Phú, Phường 4, Quận 5, TP. Hồ Chí Minh", [
"10 Trần Huy Liệu, Phường 11, Phú Nhuận, TP. Hồ Chí Minh",
"56 Cô Giang, Phường 2, Phú Nhuận, TP. Hồ Chí Minh",
"1 Nguyễn Huệ, Quận 1, TP. Hồ Chí Minh",
], 2)
for route in plan["routes"]:
stops = [s["id"] for s in route["steps"] if s["type"] == "job"]
print(f"Vehicle {route['vehicle']}: orders {stops}, {route['distance']} m")
print("Unassigned:", [j["id"] for j in plan["unassigned"]])
Build delivery route planning for a fleet with the VIETMAP APIs.
1. For each order address: GET https://maps.vietmap.vn/api/search/v4 (text=address) to get a ref_id,
then GET https://maps.vietmap.vn/api/place/v4 (refid=ref_id) to get lat, lng. Store the coordinates for reuse.
2. POST https://maps.vietmap.vn/api/vrp?api-version=1.1 with body {vehicles, jobs}.
VRP coordinates are [longitude, latitude]. The depot is each vehicle's start and end.
code = 0 means solved; routes holds one route per vehicle, unassigned the orders left out.
3. For each vehicle's route, call Route v4 for turn-by-turn directions and toll costs.
Requirements:
- Call the APIs from the server. Read the API key from the VIETMAP_API_KEY environment variable and send it as `apikey`; never hard-code it.
- Check the HTTP status of every call (401: wrong key, 423: quota used up or API not enabled).
- Read the docs below first. Use only parameters and response fields documented there.
Docs:
- https://maps.vietmap.vn/docs/map-api/use-cases/delivery-routing-guide/index.txt
- https://maps.vietmap.vn/docs/map-api/vrp/index.txt
- https://maps.vietmap.vn/docs/map-api/route-version/route-v4/index.txt
- https://maps.vietmap.vn/docs/assets/agents/logistics.txt
VRP supports vehicle capacity, time windows, driver hours, skills and priorities. See the VRP page for details.
Step 3: directions and tolls¶
VRP returns the visiting order and the geometry of each route. Route v4 adds turn-by-turn directions and tolls by vehicle type, using the points in the order VRP chose. Tolls are calculated leg by leg; see Toll costs for trucks and containers.
Steps 4–5: during and after the trip¶
- Confirm arrival: send the position from the driver app to Geofencing with the stop coordinates and a radius in meters. The API returns
insideper stop. - Reconcile after the trip: Map Matching takes the trip's GPS trace and returns the route driven and the actual distance.
Coordinate formats and units¶
Coordinate format by API:
| Where | Format |
|---|---|
Autocomplete and Geocode focus; Route v4, Matrix v4 and TSP v4 point |
String lat,lng, latitude first |
| Place v4 and Reverse v4 results | Two fields, lat and lng |
VRP location, start, end; Map Matching body |
Array [lng, lat], longitude first |
| Geofencing | Two fields, lat and long |
VietMap GL JS (center, setLngLat) |
Array [lng, lat] |
Units: Route v4 returns distance in meters and time in milliseconds; VRP uses seconds for service, duration and time_windows, and meters for distance; Map Matching returns distance in kilometers.
Estimating cost¶
For a day with 3 vehicles and 20 stops:
- Geocode v4 and Place v4 for 20 addresses: 20 × 2 = 40 transactions. Stored coordinates can be reused for returning customers.
- VRP: 3 vehicles × 20 stops = 60 transactions.
- Route v4: ⌊points / 5⌋ + 1 transactions per request.
Convert to VND with the converter.
Related APIs¶
- Matrix v4 — time and distance between every pair of points, when you plan yourself.
- Isochrone — the area a vehicle can reach in a given time, to split delivery zones between depots.
- Error codes — handling
401,423,429andcodein responses.