Aller au contenu
Tous les exemplesItinéraires

Planifier du service sur le terrain avec pauses

Trois techniciens à Ottawa, qui partent de chez eux et y reviennent : Kanata, Orléans et Alta Vista. Neuf travaux durent de 30 à 90 minutes chacun (duration), et certains doivent commencer dans une fenêtre. Deux travaux exigent une certification que seuls les techniciens de Kanata et d'Alta Vista possèdent (allowedVehicleIndices), un travail à Kanata ne peut aller qu'au technicien de Kanata, et un travail à Orléans va au technicien d'Orléans.

Chaque technicien prend une pause du dîner de 30 minutes qui commence entre 12 h et 13 h (breakRule.breakRequests), et aucune journée ne peut durer plus de six heures et demie entre le départ de la maison et le retour (routeDurationLimit).

Ce qu'il faut lire dans la réponse :

  • routes[].breaks[] donne le début et la durée de chaque pause.
  • transitions[].breakDuration montre sur quel trajet une pause est tombée. Une pause peut aussi venir avant le départ du véhicule ou après son arrivée (la règle de pause permet les deux); une telle pause figure dans routes[].breaks[] et sur aucun trajet.
  • metrics.breakDuration et metrics.visitDuration additionnent les pauses sur les trajets de la tournée et le temps sur place.

L'envoyer

Enregistrez le corps ci-dessous dans field-service-with-breaks.json et envoyez-le à /optimize. Donnez à UNMAP_API_KEY la valeur de votre clé d'API.

curl -X POST "https://api.unmap.dev/optimize" \
  -H "X-API-Key: $UNMAP_API_KEY" \
  -H "Content-Type: application/json" \
  --data @field-service-with-breaks.json

Le corps utilise des trajets en ligne droite à vitesse fixe (useGeodesicDistances avec geodesicMetersPerSecond) et n'a donc pas besoin du réseau routier. Supprimez ces deux champs pour planifier plutôt avec les temps de conduite du réseau routier canadien. Les heures sont en UTC : 12:00:00Z est 8 h à Ottawa en octobre. "timeout": "10s" est la résolution la plus longue du forfait Hobby, et la requête reste dans les limites de Hobby (25 envois, 5 véhicules). Le solveur peut renvoyer un ordre de visite différent d'une exécution à l'autre.

Ce corps est un fichier de la suite de conformité d'Unmap, qui le valide et vérifie que les réponses du solveur sont réalisables. Après l'avoir modifié, envoyez-le avec "solvingMode": "VALIDATE_ONLY" pour vérifier ses champs, puis résolvez-le pour vérifier que vos contraintes tiennent ensemble.

{
  "useGeodesicDistances": true,
  "geodesicMetersPerSecond": 11,
  "timeout": "10s",
  "model": {
    "globalStartTime": "2026-10-13T12:00:00Z",
    "globalEndTime": "2026-10-13T23:00:00Z",
    "shipments": [
      {
        "label": "job-201",
        "deliveries": [
          {
            "arrivalLocation": { "latitude": 45.414, "longitude": -75.697 },
            "duration": "3600s",
            "timeWindows": [{ "startTime": "2026-10-13T13:00:00Z", "endTime": "2026-10-13T16:00:00Z" }]
          }
        ]
      },
      {
        "label": "job-202",
        "deliveries": [
          {
            "arrivalLocation": { "latitude": 45.401, "longitude": -75.688 },
            "duration": "2700s"
          }
        ]
      },
      {
        "label": "job-203",
        "deliveries": [
          {
            "arrivalLocation": { "latitude": 45.392, "longitude": -75.755 },
            "duration": "5400s"
          }
        ],
        "allowedVehicleIndices": [0, 2]
      },
      {
        "label": "job-204",
        "deliveries": [
          {
            "arrivalLocation": { "latitude": 45.469, "longitude": -75.516 },
            "duration": "1800s",
            "timeWindows": [{ "startTime": "2026-10-13T17:00:00Z", "endTime": "2026-10-13T20:00:00Z" }]
          }
        ],
        "allowedVehicleIndices": [1]
      },
      {
        "label": "job-205",
        "deliveries": [
          {
            "arrivalLocation": { "latitude": 45.309, "longitude": -75.907 },
            "duration": "3600s"
          }
        ],
        "allowedVehicleIndices": [0]
      },
      {
        "label": "job-206",
        "deliveries": [
          {
            "arrivalLocation": { "latitude": 45.274, "longitude": -75.736 },
            "duration": "2700s",
            "timeWindows": [{ "startTime": "2026-10-13T12:30:00Z", "endTime": "2026-10-13T15:00:00Z" }]
          }
        ]
      },
      {
        "label": "job-207",
        "deliveries": [
          {
            "arrivalLocation": { "latitude": 45.437, "longitude": -75.662 },
            "duration": "1800s"
          }
        ]
      },
      {
        "label": "job-208",
        "deliveries": [
          {
            "arrivalLocation": { "latitude": 45.387, "longitude": -75.662 },
            "duration": "4500s"
          }
        ],
        "allowedVehicleIndices": [0, 2]
      },
      {
        "label": "job-209",
        "deliveries": [
          {
            "arrivalLocation": { "latitude": 45.35, "longitude": -75.74 },
            "duration": "2700s",
            "timeWindows": [{ "startTime": "2026-10-13T18:00:00Z", "endTime": "2026-10-13T21:00:00Z" }]
          }
        ]
      }
    ],
    "vehicles": [
      {
        "label": "tech-kanata",
        "startLocation": { "latitude": 45.32, "longitude": -75.89 },
        "endLocation": { "latitude": 45.32, "longitude": -75.89 },
        "startTimeWindows": [{ "startTime": "2026-10-13T12:00:00Z", "endTime": "2026-10-13T12:30:00Z" }],
        "endTimeWindows": [{ "endTime": "2026-10-13T21:30:00Z" }],
        "breakRule": {
          "breakRequests": [{ "earliestStartTime": "2026-10-13T16:00:00Z", "latestStartTime": "2026-10-13T17:00:00Z", "minDuration": "1800s" }]
        },
        "routeDurationLimit": { "maxDuration": "23400s" },
        "costPerHour": 45,
        "costPerKilometer": 0.4
      },
      {
        "label": "tech-orleans",
        "startLocation": { "latitude": 45.475, "longitude": -75.53 },
        "endLocation": { "latitude": 45.475, "longitude": -75.53 },
        "startTimeWindows": [{ "startTime": "2026-10-13T12:00:00Z", "endTime": "2026-10-13T12:30:00Z" }],
        "endTimeWindows": [{ "endTime": "2026-10-13T21:30:00Z" }],
        "breakRule": {
          "breakRequests": [{ "earliestStartTime": "2026-10-13T16:00:00Z", "latestStartTime": "2026-10-13T17:00:00Z", "minDuration": "1800s" }]
        },
        "routeDurationLimit": { "maxDuration": "23400s" },
        "costPerHour": 45,
        "costPerKilometer": 0.4
      },
      {
        "label": "tech-alta-vista",
        "startLocation": { "latitude": 45.38, "longitude": -75.67 },
        "endLocation": { "latitude": 45.38, "longitude": -75.67 },
        "startTimeWindows": [{ "startTime": "2026-10-13T12:00:00Z", "endTime": "2026-10-13T12:30:00Z" }],
        "endTimeWindows": [{ "endTime": "2026-10-13T21:30:00Z" }],
        "breakRule": {
          "breakRequests": [{ "earliestStartTime": "2026-10-13T16:00:00Z", "latestStartTime": "2026-10-13T17:00:00Z", "minDuration": "1800s" }]
        },
        "routeDurationLimit": { "maxDuration": "23400s" },
        "costPerHour": 45,
        "costPerKilometer": 0.4
      }
    ]
  }
}

Prochaines étapes

  • API Optimize : points de terminaison, champs pris en charge, limites et tarification.
  • Erreurs d'Optimize : le sens de la raison d'un envoi laissé de côté, et chaque erreur et avertissement.
  • banc d'essai Optimize : exécutez cette requête avec votre clé et voyez les tournées sur une carte.