"tz". See Dates and time zones.
Stops of one order
Thestops array of an order is a sequence. The engine plans these stops on one resource, in the order of the array. A pickup then a delivery in one order carry the same goods. The resource loads them at the first stop and unloads them at the second.
- One vehicle, array order. In the sandbox, an order with a far stop and then a near stop was planned in that order. The same two stops in two separate orders were planned near first. With two vans 20 km apart, an order with one stop near each van went to a single van.
- All stops or none. If one stop of an order can’t be served, the engine leaves the whole order out. The stop that fits is not planned alone. In the sandbox, an order with a second stop that opened after the working day of the van went to
unaffectedStopIdswith both stops, and the solution gave no violation. - A limit of 4 stops by default. An order without
successiveStopscan have at most 4 stops. In the sandbox, an order with 5 stops was refused with a403error. See Limits and quotas for the error and for how to raise the limit.
Order for the rule, and Order, stop for the loads of a pickup and a delivery.
Optional orders
An order withoptional: true is one that the engine can leave out. It leaves the maximizeMandatoryStops objective, so leaving it out costs nothing there. Without maximizeOptionalStops in the objectives, the engine has no reason to plan it, so it is unlikely to be planned. The default list of objectives contains maximizeOptionalStops.
objectives list, add maximizeOptionalStops yourself. In the sandbox, a plan with one mandatory and one optional order planned both with the default list. With a list that left out maximizeOptionalStops, the optional stop went to unaffectedStopIds. See maximizeOptionalStops for the objective.
An optional order is not the same as an order with a low priority. A mandatory order, however low its priority, still counts in maximizeMandatoryStops.
See Order.optional for the rule.
Stops back to back
successiveStops: true keeps the stops of one order consecutive. The vehicle visits them one after the other, with no stop of another order in between. Without it, the engine can interleave the stops of two orders.
successiveStops, the engine planned both pickups, then both deliveries. With successiveStops on both orders, it planned the first pickup and its delivery, then the second pair. The flag applies to each order on its own. With the flag on one order only, that order stayed consecutive and the other order was planned around it.
A tour that breaks the rule reports a violation of type successiveStops.
The API reference calls successiveStops and maxStopSpan mutually exclusive, so use one or the other on an order. The API doesn’t refuse a plan that sets both. See Order.successiveStops for the rule.
Time from first to last stop
maxStopSpan limits the time that an order takes, without forcing its stops to be consecutive. It is a duration such as PT4H. The span runs from the end of service at the first stop of the order to the start of service at its last stop. Waiting time counts.
Order.maxStopSpan for the rule.
One site among several
AnAlternativesStop is a stop with several candidates. A candidate is a single stop. The engine serves exactly one candidate when it plans the order. A waste collection with several disposal sites is an example. The engine picks the site that fits the tour.
- One candidate is served. In the sandbox, an order with three candidate sites planned the nearest one. When the nearest site was closed, the engine planned another candidate. An order made only of an alternatives group was planned too.
- All candidates have the same
kind. A candidate withoutkindcounts as adelivery. A group that mixes kinds is refused with a400error. - The tour names the candidate, never the group. A waypoint carries the id of the chosen candidate, here
drop-near. The id of the group, heredrop-site, appears nowhere in the solution. - The other candidates are listed apart. The candidates that were not chosen are in
unaffectedAlternativeIds, not inunaffectedStopIds. - If no candidate fits, the whole order is left out. The id of one candidate appears in
unaffectedStopIds, and the ids of the others appear inunaffectedAlternativeIds. The API reference doesn’t say which candidate goes in which list. Don’t write code that depends on it.
AlternativesStop can switch the engine to a slower algorithm. See Sizing maxOptimizationDuration for a large problem. Use it for a choice between sites. If you only want a stop that the engine can leave out, use an optional order.
This guide shows no “only if needed” pattern, such as a stop that the vehicle visits only if it doesn’t already carry an item. A group that mixes kinds is refused, and a candidate with no kind counts as a delivery.
See AlternativesStop for the rule. The Waste collection case study uses a group of three disposal sites.
Same vehicle in any order
An order with several stops already keeps them on one vehicle, but in the order of the array. Sometimes you need the opposite: a set of stops that must share one vehicle, while the vehicle picks the best order. A set of key-gated deliveries is the usual case, or the stops of one customer that a single driver should handle. AmaxCumulatedCost entry in globalConstraints does this. It counts how many vehicles serve the set, and it allows one.
The plan below has two vans tagged driver, and three orders with one stop each, all tagged keyset-1. Two stops are close to van-1 and one is close to van-2, so without the cap the stops would be split between the two vans. The plan carries "tz" because it is a whole plan, and it can be sent as it is to POST /plans.
maxCumulatedCost entry. costCoeff: 1 counts one for each stop of the set that a vehicle visits. overcostFloor: 1 with overcostCoeff: 0 stops the count at 1, so a vehicle that takes one stop of the set costs 1, and a vehicle that takes all three also costs 1. Each vehicle that takes at least one stop of the set therefore costs 1, and maximum: 1 across the fleet leaves room for one vehicle only. On the sandbox, this plan puts all three stops on one van and leaves unaffectedStopIds empty. The same plan without the globalConstraints entry splits the set across both vans.
A few traps are worth knowing before you use this pattern:
- Every resource that may serve the set must carry the tag in
costsByResourceTag, heredriver. The cap only counts resources that carry the tag, and the wildcard"*"is refused. A resource without the tag could take stops of the set freely. - Write one entry per set, and give each set its own stop tag. Two sets that share a tag would be counted as one set.
- If no single vehicle can serve the whole set, for example because of time windows or capacity, the extra stops are not given to a second vehicle. They stay in
unaffectedStopIds, andglobalViolationsstays empty. See Capping a fleet-wide cost withmaxCumulatedCostand Handling infeasibility. - A vehicle that does several trips can serve the set across its trips. See Multi-trip tours.
Preferred order between orders
Between two different orders, the engine has no hard precedence. The stops of two orders can come in any order, and on different vehicles. If you want a preference, tag the stops and add themaximizePrecedences objective with pairs of tags.
first was 8 km from the depot, and a stop tagged then was 2 km away. Without the objective, the engine planned the near stop first. With the objective, it planned the stop tagged first first. This is a preference, not a rule: the engine can break a pair. See maximizePrecedences for the objective and its limits.
For a rule that the engine always keeps, put the stops in one order. To fix a sequence yourself, see fixed assignments.
Checking the solution
The engine plans the stops that fit, and it lists the others. A stop that the engine can’t serve is not an error. These fields of the solution help:toursholds the waypoints. A stop that the engine planned appears there with its own id.unaffectedStopIdslists the stops that the engine left out.unaffectedAlternativeIdslists the candidates of anAlternativesStopthat were not chosen.- The
violationsof a tour name the hard rules that the tour breaks. A stop that the engine dropped doesn’t appear there.
unaffectedStopIds, or in unaffectedAlternativeIds. The id of an alternatives group is in none of them. Check each of the three lists. A candidate in unaffectedAlternativeIds is not a lost stop.
Example plan
This plan has one van and three orders. The first two orders are a pickup then a delivery, withsuccessiveStops: true. The third order is a collection, then an AlternativesStop with two drop sites. All the candidates are deliveries. Every stop is open all day, so the order of the stops doesn’t depend on road times.
What the solution shows
The sandbox returns this for the plan, with the offset ofEurope/Paris and with seconds. Only the fields that matter here are kept. The times come from the sandbox and depend on its routing. The order of the stops is the same in production.
- Each order with
successiveStopsis visited as a pair:pickup-1anddelivery-1, thenpickup-2anddelivery-2. - The same plan without
successiveStopsgave another sequence in the sandbox:pickup-1,pickup-2,delivery-2,delivery-1. So the flag changed the plan. - The tour names
drop-near, the chosen candidate. The group iddrop-siteappears nowhere. drop-faris inunaffectedAlternativeIds. It is not a lost stop.unaffectedStopIdsis empty, because the engine planned every order.
A check in code
This check compares the solution with the plan that you sent. It maps each candidate id to its group, so it can tell a lost order from a candidate that was not chosen. It reports a single stop that is left out, and a group that has no candidate in a tour. It doesn’t report a candidate that is inunaffectedAlternativeIds.
drop-far is in unaffectedAlternativeIds.
Then look at the violations of each tour. The API reference names the violation types for the order rules: successiveStops, order and maxStopSpan.
