Skip to main content
A resource is a vehicle with its driver. A resource limit is a bound on the day of that resource. For example, a driver works from 08:00 to 17:00, for at most 8 hours, and is back at the depot in the evening. A tour is the route of one resource, from its start to its end. In the solution, a tour is a list of waypoints. This guide covers the limits of a resource: its hours, its working time, its distance, and its longest drive. Each section states one rule, with a link to the API reference where the exact behavior is written. The last section shows how to check a solution against these limits. These snippets use local times where they have times. The plan you put them in needs "tz". See Dates and time zones.

Which limit for which rule

These limits are hard constraints. When a stop would break one, the engine doesn’t plan it. Checking the solution shows where to find it.

Working hours

workingTimeWindow is the period when the resource can work. The tour must fit inside it. The vehicle can’t leave before begin, and the tour must end before end.
The rules are these:
  • The return counts. When the resource has an arrival, the drive back to it must also end inside the window. In the sandbox, a van with a window of 08:00 to 09:00 left out a stop 30 km away. The van would have been back at 09:22.
  • Without arrival, the tour ends at the last stop. Only that stop must fit in the window. In the sandbox, the same van served the same stop when the resource had no arrival.
  • A wide window is not a short shift. The window says when the resource may work, not for how long. Pair it with maxWorkingDuration to limit the length of the shift.
See Resource.workingTimeWindow for the rule.

Maximum working time

maxWorkingDuration is the longest tour a resource can make. The engine compares it with the whole tour, from the departure to the end. The tour includes all of these:
  • the drive from the depot to the first stop, and the drive back to the arrival;
  • the drives between the stops;
  • the work at the stops;
  • the breaks;
  • the waiting, when the resource arrives before a stop opens.
The rules are these:
  • Both depot drives count. In the sandbox, a stop 30 km away needed a tour of 1 h 22 min: two drives of 36 min and 10 min of service. A cap of 1 h 20 min left the stop out.
  • Without arrival, the return drive doesn’t count. In the sandbox, a cap of 50 min left the stop out when the van had to return. The same cap planned it when the van had no arrival, because the tour then lasted 46 min.
  • Breaks count. A van with a stop 10 km away and a 45-minute break planned the stop with no cap. With a cap of 1 hour, it left the stop out. The same cap, without the break, planned the stop.
  • A wait before the first stop is absorbed. Suppose the only stop opens at 11:00 and the cap is 1 hour. The engine leaves the depot later, at about 10:48, instead of leaving at 08:00 and waiting. This holds even when lateDeparture is false, because leaving at 08:00 is not feasible with this cap. See plan-level fields.
  • A wait between two stops still counts. Stop A is open from 08:00 to 09:00. Stop B is open from 11:00 to 12:00. With no cap, the tour lasted 3 h 17 min, and the van waited 2 h 43 min at B. With a cap of 2 hours, the engine left B out. Don’t expect a cap to remove every wait.
To size the cap, add up what the tour contains. A driver makes two drives of 30 minutes, works 7 hours at the stops, and takes a 45-minute break. The cap needs to be at least PT8H45M. A cap of PT8H for such a driver leaves stops unplaced, with no error. Size the cap from production results too, because drive times differ between the sandbox and production. In the example plan, the same three stops took 1 h 39 min in the sandbox and 2 h 10 min in production. See Resource.maxWorkingDuration for the rule.

Maximum distance

maxDistanceInKm is the longest distance a resource can drive in its tour, in kilometers. It covers the whole tour, so the drives to and from the depot count.
The rules are these:
  • The return counts. In the sandbox, a stop 30 km away needs a round trip of 60 km. A limit of 50 km left the stop out. A limit of 70 km planned it.
  • Without arrival, the return doesn’t count. In the sandbox, the limit of 50 km planned the stop when the van had no arrival, because the tour was 30 km long.
  • The engine measures the distance along the roads in production. The sandbox draws straight lines. In production, a stop 30 km east of the depot in a straight line gave a round trip of 75.26 km. A limit of 65 km left it out, while the sandbox would have planned it with its 60 km.
Size a distance limit from production results. A distance read on a map, or from a sandbox run, is shorter than the road distance. Stops can then fall out of the plan in production, with no error. See Resource.maxDistanceInKm for the rule.

Limits between two stops

maxInterStopDistanceInKm and maxInterStopDuration bound one drive at a time, not the whole day. For example, no drive is longer than 30 minutes, so the driver is never far from the next stop. Each field has two forms.
  • A single value bounds every drive of the tour, including the first drive from the depot and the last drive back.
  • A structured value bounds each kind of drive on its own. firstTravel is the drive from the depot to the first stop. interStop is each drive between two stops. lastTravel is the drive back. A key that you leave out bounds nothing.
In this example, the distance is the single form. The duration is the structured form: it bounds only the drives between two stops. The rules are these:
  • A single value counts the depot drives. In the sandbox, a single limit of 20 km left out a stop 30 km from the depot. The same happened with a single duration of 20 minutes.
  • interStop alone doesn’t. The same stop, with { "interStop": 20 }, was planned, because no drive between two stops was too long.
  • interStop cuts a tour at the long gap. With stops at 5 km and 35 km on one side of the depot, interStop of 20 km left one of the two out. They are 30 km apart. Without the limit, both were planned.
  • Distance and duration work the same way. The two fields follow the same pattern in the sandbox.
See Resource.maxInterStopDistanceInKm and Resource.maxInterStopDuration for the rule. The distance is measured along the roads, as in Maximum distance. Size maxInterStopDuration from production results too, because drive times differ between the sandbox and production.

Split shifts

A split shift has a long gap in the middle of the day. For example, a driver works from 08:00 to 12:00, then from 14:00 to 18:00. There are two ways to write it. Choose by one question: does the driver go back to the depot between the two halves? One resource and a break. Give the resource one workingTimeWindow for the whole day, and a timeWindowBreak that fills the gap:
  • No stop falls in the gap. In the sandbox, a stop that opened at 12:30 was left out. The stops of the morning and of the afternoon were planned.
  • The van stays where it is. It waits at its last stop before the gap and doesn’t go back to the depot.
  • The break waypoint can start before the gap. In the sandbox, the break waypoint ran from 09:10 to 14:00 at the first stop, because it included the wait. The break itself stays inside the gap. See Breaks for the difference between the waypoint and the break.
  • The gap counts in maxWorkingDuration. A break counts in the cap, as Maximum working time says. For 8 hours of work and a gap of 2 hours, the cap needs to be PT10H.
Two resources. Write one resource per half, with the same vehicle profile. For example, van-am works from 08:00 to 12:00 and van-pm works from 14:00 to 18:00:
  • Each half is its own tour. Each one starts and ends at the depot. In the sandbox, the stop that opened at 12:30 was left out here too.
  • Each half has its own cap. A maxWorkingDuration on each resource bounds each half. Nothing bounds the day as a whole.
See Resource.breaks and Breaks for the rule.

Checking the solution

A limit is not reported as an error. The engine plans the stops that fit, and lists the others in unaffectedStopIds. The tour stays valid (isValid is true), and it carries no violation. A resource with nothing left to do goes to unusedResourceIds. So you check the limits yourself. These fields of a tour help:
  • workingDuration is the whole time of the tour, from its departure to its end. It is the value that the engine compares with maxWorkingDuration.
  • travelDuration and waitingDuration are two parts of it that are not work at stops. Breaks are a third part, and no tour field gives their time.
  • distanceInKm is the distance of the tour. It is the value that the engine compares with maxDistanceInKm.
Another resource without the limit can take a stop that the first one dropped. In the sandbox, a van capped at 50 minutes left a stop 30 km away to a second van with no cap. To find which limit left a stop out, lock the stop on the resource and read the violations. See Testing a stop on a specific resource with state.assignments. On a locked tour, the sandbox named maxWorkingDuration, maxDistanceInKm and workingTimeWindow, each with the amount over the limit.

Example plan

This plan has one van with a cap of 3 hours, and four deliveries of 30 minutes each. Three stops are close to the depot. stop-far is about 40 km east of it. Every stop is open all day, so the van never waits.
The same plan with the SDKs:

What the solution shows

The sandbox returns this tour for the plan, with the offset of Europe/Paris and with seconds. The other fields are left out. The times come from the sandbox and depend on its routing. In production, the same stop is left out.
  • stop-far is in unaffectedStopIds. The tour is valid and has no violation. The stop is out because of the cap.
  • The tour lasts 1 h 39 min, which is well under the cap. The far stop doesn’t fit in the 1 h 20 min that are left, because it adds two long drives and 30 minutes of service.
  • With maxWorkingDuration set to PT6H, the engine planned all four stops. The tour then lasted 3 h 36 min in the sandbox and 3 h 44 min in production. Both are over the cap of 3 hours.
  • The distance differs too. With the cap at PT6H, the tour was 80.5 km in the sandbox and 103.4 km in production. This is why a distance limit needs sizing from production results.

A check in code

This check compares a tour with the limits of its resource. It takes one tour and one resource as dictionaries, such as the JSON in the previous section and the resource of your plan. Call it on each entry of solution["tours"], with the resource of the same resourceId. It checks the limits that the solution reports as numbers: the working time and the distance.
A valid solution returns an empty list for each tour, because the engine never plans a tour over these limits. The check is useful for a tour that you lock or that you build yourself. Then read unaffectedStopIds, the list of stops that the engine left out. See Handling infeasibility to find the reason for each stop in that list.