Tích hợp lập tuyến giao hàng¶
Tài liệu này mô tả cách kết hợp Geocode v4, Place v4, VRP và Route v4 để lập tuyến giao hàng cho đội xe, từ danh sách đơn hàng tới tuyến đường của từng xe.
Thứ tự gọi API:
| Bước | Việc | API |
|---|---|---|
| 1 | Đổi địa chỉ ra toạ độ | Geocode v4 rồi Place v4 |
| 2 | Chia điểm cho các xe và sắp thứ tự | VRP; chỉ một xe thì TSP v4 |
| 3 | Đường đi từng chặng, phí cầu đường | Route v4 |
| 4 | Xác nhận xe đã tới điểm giao | Geofencing |
| 5 | Đối soát quãng đường đã chạy | Map Matching |
Bước 1–2: từ địa chỉ tới tuyến của từng xe¶
Geocode v4 và Place v4 chuyển địa chỉ thành toạ độ. VRP nhận danh sách xe và điểm giao, trả về tuyến của từng xe. Đoạn mã dưới đây phân ba đơn hàng cho hai xe xuất phát và quay về cùng một kho.
// Node.js 18+. Gọi từ server để không lộ API key.
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();
}
// Địa chỉ -> [kinh độ, vĩ độ] (Geocode v4 rồi 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(`Không tìm thấy địa chỉ: ${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 nhận [kinh độ, vĩ độ]
}
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(`Xe ${route.vehicle}: đơn ${stops.join(" → ")}, ${route.distance} m`);
}
console.log("Không xếp được:", 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):
"""Địa chỉ -> [kinh độ, vĩ độ] (Geocode v4 rồi 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"Không tìm thấy địa chỉ: {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 nhận [kinh độ, vĩ độ]
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"Xe {route['vehicle']}: đơn {stops}, {route['distance']} m")
print("Không xếp được:", [j["id"] for j in plan["unassigned"]])
Xây chức năng lập tuyến giao hàng cho đội xe bằng API của VIETMAP.
1. Mỗi địa chỉ đơn hàng: GET https://maps.vietmap.vn/api/search/v4 (text=địa chỉ) lấy ref_id,
rồi GET https://maps.vietmap.vn/api/place/v4 (refid=ref_id) lấy lat, lng. Lưu lại toạ độ để lần sau khỏi gọi.
2. POST https://maps.vietmap.vn/api/vrp?api-version=1.1 với body {vehicles, jobs}.
Toạ độ trong VRP là [kinh độ, vĩ độ]. Kho là start và end của mỗi xe.
code = 0 là giải xong; routes là tuyến từng xe, unassigned là đơn không xếp được.
3. Với tuyến của từng xe, gọi Route v4 để có chỉ dẫn từng chặng và phí cầu đường.
Yêu cầu:
- Gọi API từ server. Đọc API key từ biến môi trường VIETMAP_API_KEY và truyền qua `apikey`, không ghi cứng trong code.
- Kiểm tra mã HTTP của mọi lần gọi (401: sai key, 423: hết hạn mức hoặc API chưa bật).
- Đọc tài liệu bên dưới trước. Chỉ dùng tham số và trường dữ liệu có trong tài liệu.
Tài liệu:
- https://maps.vietmap.vn/docs/vi/map-api/use-cases/delivery-routing-guide/index.txt
- https://maps.vietmap.vn/docs/vi/map-api/vrp/index.txt
- https://maps.vietmap.vn/docs/vi/map-api/route-version/route-v4/index.txt
- https://maps.vietmap.vn/docs/assets/agents/logistics.txt
VRP hỗ trợ tải trọng xe, khung giờ giao, giờ làm của tài xế, xe chuyên dụng và đơn ưu tiên. Chi tiết ở trang VRP.
Bước 3: chỉ đường và phí cầu đường¶
VRP trả về thứ tự ghé và hình học của từng tuyến. Route v4 bổ sung chỉ dẫn từng chặng và phí cầu đường theo loại xe, với các điểm theo đúng thứ tự VRP đã xếp. Phí cầu đường được tính theo từng chặng, xem Tính phí cầu đường cho xe tải, container.
Bước 4–5: trong và sau chuyến¶
- Xác nhận tới điểm giao: gửi vị trí từ ứng dụng tài xế cùng toạ độ các điểm giao và bán kính (mét) tới Geofencing. API trả
insidecho từng điểm. - Đối soát sau chuyến: Map Matching nhận vệt GPS của cả chuyến, trả tuyến đã chạy và quãng đường thực tế.
Định dạng toạ độ và đơn vị¶
Định dạng toạ độ theo từng API:
| Ở đâu | Định dạng |
|---|---|
focus của Autocomplete, Geocode; point của Route v4, Matrix v4, TSP v4 |
Chuỗi lat,lng, vĩ độ trước |
| Kết quả Place v4, Reverse v4 | Hai trường lat, lng |
location, start, end của VRP; body của Map Matching |
Mảng [lng, lat], kinh độ trước |
| Geofencing | Hai trường lat, long |
VietMap GL JS (center, setLngLat) |
Mảng [lng, lat] |
Đơn vị: Route v4 trả distance theo mét và time theo mili giây; VRP dùng giây cho service, duration, time_windows và mét cho distance; Map Matching trả distance theo km.
Ước tính chi phí¶
Ví dụ một ngày có 3 xe và 20 điểm giao:
- Geocode v4 và Place v4 cho 20 địa chỉ: 20 × 2 = 40 transaction. Toạ độ đã lưu dùng lại được cho khách hàng cũ.
- VRP: 3 xe × 20 điểm = 60 transaction.
- Route v4: ⌊số điểm / 5⌋ + 1 transaction cho mỗi request.
Quy ra tiền bằng công cụ quy đổi.