Vẽ vùng phục vụ cửa hàng bằng isochrone: khách nào tới được trong 10 phút

Trả lời nhanh: vùng phục vụ của một cửa hàng là vùng mà khách đi tới được trong một số phút, theo đường thật. Bản đồ gọi đó là isochrone (đường đẳng thời). Một lệnh gọi GET /api/v1/isochrone?at=kinh_độ,vĩ_độ&minutes=5,10,15&mode=motorbike trả về các vùng 5, 10 và 15 phút đi xe máy dưới dạng GeoJSON, vẽ được ngay trên MapLibre, Leaflet hoặc QGIS.

Vì sao không dùng bán kính

Một vòng tròn bán kính 2 km coi mọi hướng như nhau. Thực tế khách phải đi theo đường: sông, kênh, đường một chiều, cầu và đường cao tốc làm vùng đi được méo đi. Hai cửa hàng cách nhau 2 km qua sông có thể phục vụ hai nhóm khách khác hẳn. Isochrone tính theo mạng lưới đường nên phản ánh điều đó. Bấm vài điểm hai bên sông Sài Gòn trên bản đồ trên để thấy.

Bước 1 — Gọi API isochrone

curl -G "https://map.vapp.vn/api/v1/isochrone" \
  --data-urlencode "at=106.6980,10.7725" \
  --data-urlencode "minutes=5,10,15" \
  --data-urlencode "mode=motorbike"

Kết quả là GeoJSON FeatureCollection, mỗi mốc thời gian một Polygon, mốc lớn nhất đứng đầu. properties.contour là số phút; properties.fill là màu gợi ý.

Tham sốÝ nghĩa
atVị trí cửa hàng, theo thứ tự kinh độ, vĩ độ (lng,lat). Điểm cách đường tới 500 m vẫn được gắn vào đường gần nhất.
minutesCác mốc thời gian, cách nhau bằng dấu phẩy: tối đa 4 mốc, mỗi mốc không quá 120 phút. Quá giới hạn, API trả mã 400 kèm error.
modemotorbike (xe máy), car (ô tô), bike (xe đạp), foot (đi bộ). Mặc định là ô tô.

Bước 2 — Vẽ lên bản đồ của bạn

Đưa thẳng kết quả vào một nguồn GeoJSON, rồi tô màu theo properties.fill:

<link href="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css" rel="stylesheet" />
<script src="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js"></script>
<div id="map" style="height: 480px"></div>
<script>
  const store = [106.698, 10.7725]; // [kinh độ, vĩ độ]
  const map = new maplibregl.Map({ container: "map", style: "https://map.vapp.vn/style.json", center: store, zoom: 12 });
  map.on("load", async () => {
    const url = "https://map.vapp.vn/api/v1/isochrone?at=" + store.join(",") + "&minutes=5,10,15&mode=motorbike";
    const zones = await (await fetch(url)).json();
    map.addSource("zones", { type: "geojson", data: zones });
    map.addLayer({ id: "zones", type: "fill", source: "zones",
      paint: { "fill-color": ["get", "fill"], "fill-opacity": 0.25 } });
    new maplibregl.Marker().setLngLat(store).addTo(map);
  });
</script>

Vì mốc lớn nhất đứng đầu, vùng 5 phút được vẽ sau cùng và nằm trên. Gửi khóa trong header x-api-key như mọi API khác (xem hướng dẫn chuyển từ Google Maps).

Bước 3 — Chuỗi cửa hàng: cửa hàng nào phục vụ khách này

Khi có nhiều cửa hàng, câu hỏi thường gặp là: đơn hàng của khách ở địa chỉ X nên giao từ cửa hàng nào? Dùng API ma trận: sources là các cửa hàng, targets là khách. Ví dụ với ba cửa hàng ở TP.HCM và một khách ở Landmark 81:

HàngCửa hànglng,lat
0Bến Thành106.6980,10.7725
1Thảo Điền106.7320,10.8030
2Phú Nhuận106.6800,10.7990
curl -G "https://map.vapp.vn/api/v1/matrix" \
  --data-urlencode "sources=106.6980,10.7725;106.7320,10.8030;106.6800,10.7990" \
  --data-urlencode "targets=106.7220,10.7950" \
  --data-urlencode "mode=motorbike"

Mỗi hàng của durations là một cửa hàng theo thứ tự gửi, mỗi cột là một khách; đơn vị là giây. Cửa hàng có số nhỏ nhất ở cột của khách là cửa hàng tới nhanh nhất. Một lệnh gọi trả lời cho nhiều khách cùng lúc: thêm khách vào targets, cách nhau bằng dấu chấm phẩy.

Bước 4 — Dùng vùng phục vụ để quyết định

Giới hạn cần biết

Ví dụ API trên được kiểm tra tự động với API thật trước mỗi lần cập nhật trang. Diện tích trong công cụ thử là ước tính từ đa giác, dùng để so sánh, không phải số liệu đo đạc.