Exemple : matrice d’attribution
Confiez chaque nouvelle commande au livreur qui peut l’atteindre le premier, avec de vrais temps de trajet au lieu de distances à vol d’oiseau.
Sur cette page
Les nouvelles commandes arrivent ; les livreurs sont dispersés dans la ville. La distance à vol d’oiseau est un mauvais guide à Lagos ou à Accra : le livreur le plus proche peut être de l’autre côté d’une lagune, ou du mauvais côté d’une voie express sans demi-tour sur trois kilomètres. Cet exemple demande à la matrice d’itinéraires le vrai temps de trajet de chaque livreur libre vers chaque commande en attente, en une seule requête, puis confie chaque commande au livreur qui arrive le premier.
Comment ça marche
- Rassemblez les dernières positions des livreurs libres (depuis votre appli livreur, ou le suivi de flotte) et les points d’enlèvement des commandes en attente.
- Envoyez les livreurs comme
originset les points d’enlèvement commedestinationsàPOST /maps/v1/routes/matrix: jusqu’à 50 de chaque et 625 paires par requête. - Parcourez les paires du temps le plus court au plus long, et retenez une paire quand le livreur et la commande sont tous deux encore libres (une attribution gloutonne : simple, rapide, et proche du meilleur résultat pour l’attribution des courses).
- Écartez les paires que la matrice déclare impossibles (
ROUTE_NOT_FOUND), et celles qui dépassent le délai promis au client.
L’exemple calcule les motos de vos coursiers en dispatch : elles évitent les routes fermées à toutes les motos, mais les interdictions d’okada commercial ne s’appliquent pas. Si vos livreurs transportent des passagers (okadas), utilisez okada : les paires dans une zone interdite reviennent alors NOT_ALLOWED.
Le code
Exécutez-le sur votre serveur avec une clé restreinte à l’adresse IP du serveur et à l’API Matrice d’itinéraires. Node.js 18 ou plus récent, ou Python 3.8 ou plus récent, rien à installer.
// Confie chaque commande en attente au livreur libre qui atteint le premier son point d’enlèvement.
// node dispatch.mjs (NWAYS_API_KEY dans l’environnement)
const KEY = process.env.NWAYS_API_KEY
const MAX_MINUTES = 25 // n’envoyez pas un livreur plus loin que ça
const riders = [
{ id: 'rider-ade', at: { lat: 6.6018, lng: 3.3515 } }, // Ikeja, Allen Avenue
{ id: 'rider-bisi', at: { lat: 6.5005, lng: 3.3581 } }, // Surulere
{ id: 'rider-chidi', at: { lat: 6.4474, lng: 3.4733 } }, // Lekki Phase 1
]
const orders = [
{ id: 'order-1041', pickup: { lat: 6.5965, lng: 3.3421 } }, // Ikeja GRA
{ id: 'order-1042', pickup: { lat: 6.4413, lng: 3.4250 } }, // Victoria Island
{ id: 'order-1043', pickup: { lat: 6.5095, lng: 3.3711 } }, // Yaba
]
async function matrix(origins, destinations) {
const res = await fetch('https://nways.ng/maps/v1/routes/matrix', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-NWays-Api-Key': KEY },
body: JSON.stringify({ origins, destinations, mode: 'dispatch' }),
})
const data = await res.json()
if (!res.ok) throw new Error(`${res.status} ${data.error?.reason ?? ''}: ${data.error?.message ?? ''}`)
return data.elements
}
const elements = await matrix(riders.map((r) => r.at), orders.map((o) => o.pickup))
const pairs = elements
.filter((e) => e.status === 'OK' && e.durationS <= MAX_MINUTES * 60)
.sort((a, b) => a.durationS - b.durationS)
const busy = new Set()
const done = new Set()
for (const e of pairs) {
const rider = riders[e.originIndex]
const order = orders[e.destinationIndex]
if (busy.has(rider.id) || done.has(order.id)) continue
busy.add(rider.id)
done.add(order.id)
console.log(`${order.id} → ${rider.id}: ${Math.round(e.durationS / 60)} min, ${(e.distanceM / 1000).toFixed(1)} km`)
}
for (const o of orders) if (!done.has(o.id)) console.log(`${o.id}: no free rider within ${MAX_MINUTES} min`)"""Confie chaque commande en attente au livreur libre qui atteint le premier son point d’enlèvement.
python3 dispatch.py (NWAYS_API_KEY dans l’environnement)"""
import json
import os
import urllib.error
import urllib.request
KEY = os.environ["NWAYS_API_KEY"]
MAX_MINUTES = 25 # n’envoyez pas un livreur plus loin que ça
riders = [
{"id": "rider-ade", "at": {"lat": 6.6018, "lng": 3.3515}}, # Ikeja, Allen Avenue
{"id": "rider-bisi", "at": {"lat": 6.5005, "lng": 3.3581}}, # Surulere
{"id": "rider-chidi", "at": {"lat": 6.4474, "lng": 3.4733}}, # Lekki Phase 1
]
orders = [
{"id": "order-1041", "pickup": {"lat": 6.5965, "lng": 3.3421}}, # Ikeja GRA
{"id": "order-1042", "pickup": {"lat": 6.4413, "lng": 3.4250}}, # Victoria Island
{"id": "order-1043", "pickup": {"lat": 6.5095, "lng": 3.3711}}, # Yaba
]
def matrix(origins, destinations):
body = json.dumps({"origins": origins, "destinations": destinations, "mode": "dispatch"}).encode()
req = urllib.request.Request(
"https://nways.ng/maps/v1/routes/matrix",
data=body,
headers={"Content-Type": "application/json", "X-NWays-Api-Key": KEY},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=20) as res:
return json.load(res)["elements"]
except urllib.error.HTTPError as e:
err = json.load(e).get("error", {})
raise SystemExit(f"{e.code} {err.get('reason')}: {err.get('message')}")
elements = matrix([r["at"] for r in riders], [o["pickup"] for o in orders])
pairs = sorted(
(e for e in elements if e["status"] == "OK" and e["durationS"] <= MAX_MINUTES * 60),
key=lambda e: e["durationS"],
)
busy, done = set(), set()
for e in pairs:
rider, order = riders[e["originIndex"]], orders[e["destinationIndex"]]
if rider["id"] in busy or order["id"] in done:
continue
busy.add(rider["id"])
done.add(order["id"])
print(f"{order['id']} -> {rider['id']}: {round(e['durationS'] / 60)} min, {e['distanceM'] / 1000:.1f} km")
for o in orders:
if o["id"] not in done:
print(f"{o['id']}: no free rider within {MAX_MINUTES} min")Ce qu’il affiche
Quelque chose comme ceci :
order-1041 → rider-ade: 6 min, 2.1 km
order-1043 → rider-bisi: 9 min, 3.0 km
order-1042 → rider-chidi: 14 min, 6.2 kmL’adapter
- Plus de 50 livreurs ou commandes ? Répartissez les livreurs en groupes de 25 et les commandes en groupes de 25, et demandez une matrice pour chaque paire de groupes (625 paires chacune).
- Regroupement. Pour confier plusieurs commandes à un livreur dans le meilleur ordre, utilisez plutôt l’optimisation de tournées.
- Équité. Avant le tri, ajoutez une petite pénalité aux livreurs qui viennent de finir une course lointaine, ou qui ont déjà fait plus de courses que les autres aujourd’hui.
- Coût. La matrice est facturée par paire (élément) : 3 livreurs × 3 commandes font 9 éléments. Tarifs