Skip to content

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 inside per 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.

  • 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, 429 and code in responses.
Hotline
Tổng đài hỗ trợ
089.616.4567
Messenger Chat Facebook zalo Chat Zalo