Vehicle properties

Orders in RouteQ can be delivered by regular vehicles or couriers traveling on foot or by public transit. Vehicle and courier have a common set of properties.

The vehicles field is used in the request to determine the vehicle or courier.

Vehicle or courier ID

Each vehicle or courier must have a unique numeric or string ID set in the id field. The ID must be unique within a single service request. This identifier is used as the courier's username when exporting data to Track & Trace.

You can also specify the vehicle's or the courier's numeric or string ID from your tracking system in the ref field and the courier's mobile phone number in the phone field so that they can be contacted from Track & Trace.

Vehicle or courier capacity

In RouteQ you can define the vehicle capacity in terms of the maximum weight or cargo the vehicle/courier can transport over a single route.

To determine the vehicle or courier carrying capacity, use the capacity field (you can specify just one option):

  • capacity.units: Number of units (pallets, boxes, kegs).

  • capacity.weight_kg: Weight in kilograms or bulk weight.

  • capacity.volume: The volume, which is measured in cubic meters and set as the product of dimensions:

    • capacity.volume.width_m: Width in meters.

    • capacity.volume.depth_m: Depth in meters.

    • capacity.volume.height_m: Height in meters.

    If values are unknown, use capacity.volume_cbm.

  • capacity.volume_cbm: Volume in cubic meters.

If the vehicle has both size and volume specified, capacity.volume defines the dimensions and capacity.volume_cbm defines the carrying capacity.

For more information about weight determination, see Weight and volume.

Note

Please note that if the order weight is set in kilograms or as a volume weight, the vehicle capacity must be set in the same units. Similarly, if the order weight is set as the number of units, the vehicle capacity must specify the maximum number of units in each vehicle.

Additionally, you can set the maximum load of a vehicle as a percentage of the specified load capacity. To do this, use the limits request field:

  • limits.volume_perc: A percentage of the value specified in capacity.volume.width_m * capacity.volume.depth_mcapacity.volume.height_m.

  • limits.weight_perc: A percentage of the value specified in capacity.weight_kg.

  • limits.volume_perc: A percentage of the value specified in capacity.volume.

To indicate that overload is allowed relative to a given value, specify a value greater than 100. To leave spare load capacity, for example, to allow for the inaccurate estimation of order dimensions, specify a value less than 100. For example, a value of 110 allows for 10% overload and a value of 90 allows for a maximum load of 90% of the specified load capacity.

The vehicle capacity (based on the limits) is a hard restriction that won't be violated. Note that, when calculating the vehicle load, the system takes into account the orders for pickup and delivery. For more information, see Pickup and delivery.

Note

You can use the vehicle capacity to indirectly manage the number of orders that should be added to it. For example, if you set the volume per order to 1 unit (regardless of the actual size of the order) and the vehicle capacity to 10 units, then each vehicle will be assigned no more than 10 orders.

Example

There are two vehicles with a load capacity of 1.5 tons (Vehicle 1) and 3 tons (Vehicle 2) and three orders for delivery with a weight of 1 ton, 1.2 tons, and 1.4 tons, respectively.

When optimizing routes, the 1.2-ton order is placed in Vehicle 1 and the 1-ton and 1.4-ton orders are placed in Vehicle 2.

API request (JSON)API responseView on map

Custom capacity units

A custom unit restricts the capacity of the vehicle according to the custom unit of measurement specified for orders.

You can set several custom capacity units for vehicles. Each of them should have its own ordinal number N (numbering starts from 0). Each unit is described by two parameters:

  • capacity.custom.N.name: Name of the custom unit.

  • capacity.custom.N.size: Vehicle capacity in custom units.

By default, the capacity is not limited by the parameter capacity.custom.N.size.

Alert

The name of the custom capacity unit for the vehicle should match the one given for the order.

If custom capacity units are specified for orders or explicitly specified for some vehicles, but not for others, then the vehicles with unspecified units will be taken to have unlimited carrying capacity in custom units.

Example of filling in Excel file sheets for a custom price parameter (order cost):

Order cost

shipment_size.custom.0.name

shipment_size.custom.0.size

price

100

Total order cost

capacity.custom.0.name

capacity.custom.0.size

price

1000

Example 1.1

4 vehicles deliver 16 orders. Weight, volume, and load capacity (number of units) are specified for the vehicles. Custom units have been set for orders (to limit the cost, location.shipment_size.custom.0) and for the vehicles (to limit the total cost of transported orders, vehicle.capacity.custom.0).

There are 2 vehicles transporting 1 order each. However, their weight, volume, and number of units is small, because the order cost is approaching the vehicle's limit.

API request (JSON)API responseView on map

Example 1.2

The same as example 1.1, but the maximum number of orders that the vehicle can transport is also specified. The restriction is set using the location.shipment_size.custom.1 and vehicle.capacity.custom.1 parameters. Each vehicle can now carry no more than 5 orders.

The number of orders in three vehicles has changed. Now one vehicle carries one order, and orders cannot be added due to the total cost limit. For the rest, maximum utilization by total cost in each vehicle was achieved, and it was possible to maintain the limit on the number of orders.

API request (JSON)API responseView on map

Example 2

Two vehicles deliver 20 orders. Orders have different lengths. This property is described by two custom units:

  • Unit Length5 (location.shipment_size.custom.0) is 1 for orders with a length of 5 meters (these are orders from 10 to 15) and 0 for all other orders.
  • Unit Length6 (location.shipment_size.custom.1) is 1 for orders with a length of 6 meters (these are orders from 16 to 20) and 0 for all other orders. Orders from 1 to 9 have a length of less than 5 meters. For them, both custom units Length5 and Length6 are 0.

Vehicles have the same load capacity, but different body lengths:

  • Vehicle6 has a body length of 6 meters and can transport any orders. For this vehicle, vehicle.capacity.custom.0 and vehicle.capacity.custom.1 are both 10 (taking into account the weight of the orders and the vehicle's load capacity).
  • Vehicle5 has a body length of 5 meters and can't transport orders with a length of 6 meters. For this vehicle, vehicle.capacity.custom.0 = 10, while vehicle.capacity.custom.1 = 0.

In the planned solution, Vehicle5 transports orders that are 5 meters long or less, while Vehicle6 transports orders that are 6 meters long, 5 meters long, or less than 5 meters long.

API request (JSON)API responseView on map

Vehicle properties

You can specify vehicle properties in the vehicle.specs field. The following parameters can be specified:

  • width: Vehicle width in meters.

  • height: Vehicle height in meters.

  • length: Vehicle length in meters.

  • max_weight: Maximum vehicle weight in tons.

  • max_weight_kg: Maximum vehicle weight in kilograms (rounded up to the ton when addressing the planning task).

Note

If you set the vehicle.specs.max_weight and vehicle.specs.max_weight_kg parameters simultaneously in the planning task, the solution will use vehicle.specs.max_weight.

The minimum value is 0 for all the listed properties.

Alert

You can specify these parameters only for trucks (routing_mode = truck).

Load handling time at depot

The loading or unloading time at the depot may depend both on orders and vehicle properties (for example, dimensions or equipment).

To specify the amount of additional time required for loading at the depot, use the vehicle.depot_extra_service_duration_s parameter. The time is set in seconds.

Note

The time assigned in the vehicle.depot_extra_service_duration_s parameter is summed up with the depot handling time.

Example

2 vehicles with the same load capacity are delivering 13 large orders. The handling time at the depot depot.service_duration_s is 5 minutes (300 seconds). Additional time is set for loading orders into a vehicle at the depot vehicle.depot_extra_service_duration_s: 30 minutes (1800 seconds). As a result of planning, each vehicle will spend 35 minutes at the depot before starting their routes.

API request (JSON)API responseView on map

Vehicle started not from the depot

By default, vehicles and couriers start from the depot. However, sometimes they need to start the route from another point (for example, from their home or parking spot). That's a usual task in the case of sales representatives, service engineers, and other specialists who don't deliver goods, but provide services.

You can implement this requirement using the property vehicle.start_at. In this case, the courier or vehicle will start their route from the specified point with the type garage, rather than from the depot.

Visiting the depot before the start of the working day

At the beginning of the working day, couriers may need to visit the depot to get their orders there. Use the property vehicle.visit_depot_at_start to enable this. If you specify true, the courier or the vehicle must visit the depot before starting the delivery (this is default behavior). Otherwise, they can immediately start delivering orders. For more information, see Start or end of the route at an arbitrary point.

If couriers start from a garage location, but they need to pick up some orders at the depot, enable the option Can visit the depot at the beginning of the route (can_visit_depot_at_start in the API, false by default). This option only works for couriers that have the parameter visit_depot_at_start = false. For orders that need to be picked up from the depot, specify one of the following parameters:

depot_id to link orders to the depot.

depot_ready_time, the time when the order is ready at the depot.

depot_expiring_time, the deadline for picking up the order from the depot.

You can also implicitly allow couriers to visit the depot at the beginning of the route with the can_visit_depot_at_start option disabled by explicitly specifying the depot in planned_route, fixed part of the route, or during additional planning.

Example 1

Three vehicles deliver orders: The first one starts the delivery from the depot. The second one starts and ends the route without visiting the depot. The starting point for the courier is order 6, which has the garage type. The third one starts the route not from the depot, but from order 4 (the garage type), but must visit the depot before starting the delivery.

API request (JSON)API responseView on map

Example 2

Three vehicles deliver orders: The first one starts the delivery from the depot. The second and the third vehicles start and end the route without visiting the depot. Orders 1 and 7 have the pickup type, and the algorithm selects them as the first route points.

Three separate routes are planned in the solution because of the orders' priorities.

API request (JSON)API responseView on map

Example 3

Two vehicles deliver orders (visit_depot_at_start = false and can_visit_depot_at_start = true). The first one starts from a garage location and visits the depot to pick up order 1 at the beginning of the route. The second vehicle's route doesn't include a visit to the depot, it goes straight to the delivery.

API request (JSON)API responseView on map

Start from one of several depot

A courier can start from one of several depots. These depots have to be listed in the depot_id field (depots that the courier can visit) or starting_depot_id (depots from which the courier can start their route). These lists can overlap. If there are several depots, the algorithm will choose the optimal depot to start from. If the fields depot_id and starting_depot_id are not entered for a courier, they start from the first depot in the depots list.

Alert

You can't specify both regular depots and cross dockings in the vehicle.depot_id and vehicle.starting_depot_id fields. The courier may visit either regular depots or cross dockings in a single route.

If you use the delivery from one of several depots functionality, a courier will be able to pick up orders from any depot they can visit.

You can additionally set the allow_different_depots_in_route and max_middle_depots parameters for a courier. For more information, see Choosing the optimal depot to start from.

Additional loading at an intermediate depot

For a courier to be able to visit additional depots for reloading orders while following the route, set the allow_different_depots_in_route = true parameter. These depots have to be listed in the depot_id field (depots that the courier can visit) or middle_depot_id (depots for reloading). Depot lists can overlap. If there are orders in several depots, the algorithm will select the optimal depot for reloading.

Alert

You can't specify both regular depots and cross dockings in the vehicle.depot_id and vehicle.middle_depot_id fields. The courier may visit either regular depots or cross dockings in a single route.

If the courier can stop at intermediate depots for reloading only at the beginning of the route (before completing the first order), set depots_only_at_run_beginning =  true (the default is false).

You can additionally set the max_middle_depots parameter for a courier. For more information, see Visiting an additional depot.

Return to a depot at the end of the route or run

By default, the courier completes the route at the depot where they started. To allow the courier to visit several depots along the route, set allow_different_depots_in_route = true. In this case, the courier can end the route at a depot other than the first one.

Depots where a courier can end their route are specified in the depot_id field (depots that the courier can visit) or ending_depot_id field (depots from which the courier can complete their route). These lists can overlap. If there are several depots, the algorithm will choose the optimal depot to complete the route at.

Alert

You can't specify both regular depots and cross dockings in the vehicle.depot_id and vehicle.ending_depot_id fields. The courier may visit either regular depots or cross dockings in a single route.

To have the courier end the route at the same depot where they started, set finish_route_in_starting_depot = true (the default is false). If the courier starts the route not from a depot (visit_depot_at_start = false), they are to return to the depot they stopped at during the second run of the route.

To have the courier end each run at the same depot where that run started, set finish_run_in_starting_depot = true (the default is false). To allow the courier to change depots between runs, set can_change_depot_between_runs = true (see Start of a new run from a different depot).

Note

Parameters finish_route_in_starting_depot = true and finish_run_in_starting_depot = true are only viable if allow_different_depots_in_route = true.

If the courier doesn't have to return to the depot after all the orders are completed, use the return_to_depot = false option.

Example 1.1

Two couriers are to deliver two orders: one is a delivery order that needs to be picked up from Depot 1, and the other one is a pickup order to be delivered to Depot 2. The couriers can only make 1 run each, starting at any depot and ending the route at any depot. Both couriers are allowed to stop at different depots during one run, but they cannot stop at intermediate depots.

The solution uses only one courier: they leave Depot 1 after picking up the delivery order, deliver it, pick up the pickup order, and deliver it to Depot 2, where they end the route.

API request (JSON)API responseView on map

Example 1.2

The same as in example 1.1, but the couriers need to end the route at the same depot where they started (finish_route_in_starting_depot = true).

The solution uses both couriers. Courier 1 leaves Depot 1 after picking up the delivery order, delivers it, and returns to Depot 1. The second courier leaves Depot 2, picks up the pickup order, and returns with it to Depot 2.

API request (JSON)API responseView on map

Return to any location at the end of the route

If your work scenario needs to take into account the distance of the last order from the courier's parking spot (or home address, for example), you can implement it as follows:

  • Create a point with the garage type (the locations.type field). This point will only be used to end the routes.

  • For a courier who needs to return home or to the parking spot at the end of their working day, enter the ID of this point in the vehicle.finish_at field.

In the solution, all vehicles or couriers that have the finish_atproperty specified return to the specified point. If the option to return to the depot is enabled (vehicle.return_to_depot = true), the route will first go the depot and then to the parking spot.

Example

The example below uses two vehicles with different load capacities and four orders with different weights. Two vehicles are used for delivery because of the weight restrictions. Vehicle 2 doesn't have to return to the depot after it delivers the last order.

Note

Note that in the RouteQ response, vehicle 2 completes the route at the last order location.

API request (JSON)API responseView on map

Start or end of the route as close to the depot as possible

You can plan a route so that the courier starts or finishes on the order that is closest to or furthest from the depot. For example, if you expect to get new orders in the morning, then it would be better to start the route with the orders closest to the depot. That way, it will be easier for the courier to return to the depot to pick them up. On the other hand, by starting the route with the farthest order, the courier would finish closer to the depot in the evening.

If you are not using advanced cost settings, set the following parameters:

  • first_edges_penalty_factor: A penalty for the distance from the depot to the first order. The default is 0. If the value is greater than 0, the algorithm minimizes this distance, and if the value is less than 0, it maximizes the distance.

  • last_edges_penalty_factor: A penalty for the distance from the depot to the last order. The default is 0. If the value is greater than 0, the algorithm minimizes this distance, and if the value is less than 0, it maximizes the distance.

Penalties are calculated similarly to the duration and length of route segments (see the Travel between route points group under Advanced cost settings).

If penalty values are high, routes may be suboptimal in terms of mileage. If penalty values are low, then the first or last order of a route may be selected in violation of any requirements provided it saves on mileage.

If you use advanced cost settings, use the keywords from the Travel from or to the depot group.

Example

In the example below, there is 1 courier and 10 orders. Two penalties are set: first_edges_penalty_factor = 2 and last_edges_penalty_factor = -2. Planning produces a route that begins with the order closest to the depot and ends with the furthest order.

API request (JSON)API responseView on map

Vehicle or courier shifts

You can define one or more work shifts for a vehicle. Use the vehicle.shifts field to define the shifts.

A shift is a time range when the vehicle is allowed to operate. Shift time includes the loading time at the depot, travel time, and handling time at order locations, as well as unloading time at the depot (if the vehicle returns to the depot).

To use one or more shifts in Excel, define the following fields:

  • shifts.0.time_window: Soft time window corresponding to the vehicle's operating hours.

  • shifts.0.hard_window: Hard time window flag. If the time window is hard, the algorithm can't go outside the specified interval.

  • shifts.0.service_duration_s: Time between shifts (in seconds). For example, it can be the time needed to replace the driver or exchange documents.

You can also restrict the maximum shift duration (shifts.N.max_duration_s and shifts.N.hard_max_duration_s).

In the case of a soft time window, you can set additional penalties:

  • For time window violation:

    • shifts.0.penalty.out_of_time.fixed: A fixed penalty for one-time violation of the shift's time window.
    • shifts.0.penalty.out_of_time.minute: A penalty per minute of the shift's time window violation.
  • For early departure:

    • shifts.0.penalty.early.fixed: A fixed penalty for one-time start of work before the shift's time window.
    • shifts.0.penalty.early.minute: A penalty per minute of work start before the shift's time window.
  • For late arrival:

    • shifts.0.penalty.late.fixed: A fixed penalty for one-time work completion later than the shift's time window.
    • shifts.0.penalty.late.minute: A penalty per minute of work end after the shift's time window.

To limit permissible violations of the shift's time window, you can set a hard time window around a soft one.

Start, end, and duration of shift

Let's say a shift's time_window is from 9:00 to 18:00. The properties used are hard time window (hard_window), maximum shift duration (max_duration_s), and flexible start time from the depot (depot.flexible_start_time).

How to plan courier's work

Parameters

We recommend making the work interval no more than 6 hours, strictly between 9:00 and 18:00

hard_window = true

max_duration_s = 6 hours (less than the shift window)

flexible_start_time = true

Start the work strictly at 9:00, end it preferably before 15:00, or before 18:00, if necessary, but strictly no later than that (this parameter combination corresponds to time_window = 9:00 - 15:00, hard_time_window = 9:00 - 18:00, flexible_start_time = false)

hard_window = true

max_duration_s = 6 hours (less than the shift window)

flexible_start_time = false

We recommend making the work interval no more than 6 hours, preferably between 9:00 and 18:00

hard_window = false

max_duration_s = 6 hours (less than the shift window)

flexible_start_time = true

Start the work strictly at 9:00, preferably end it before 15:00, or before 18:00, if necessary, and preferably not later than that (this parameter combination corresponds to time_window = 9:00 - 15:00, max_duration_s = 9 hours)

hard_window = false

max_duration_s = 6 hours (less than the shift window)

flexible_start_time = false

Can start the work later than 9:00, preferably end the work before 18:00, preferably make work no longer than 10 hours

hard_window = false

max_duration_s = 10 hours (more than the shift window)

flexible_start_time = true

Start the work strictly at 9:00, preferably end the work before 18:00, and preferably no later than 19:00 (this parameter combination corresponds to time_window = 9:00 - 19:00, max_duration_s = 9 hours)

hard_window = false

max_duration_s = 10 hours (more than the shift window)

flexible_start_time = false

This combination of parameters won't let you plan a solution

hard_window = true

max_duration_s = 10 hours (more than the shift window)

flexible_start_time = true или flexible_start_time = false

Note

Note that to define multiple shifts, you need multiple Excel fields with an increasing numeric index.

For example. shifts.0.time_window refers to the first shift, and shifts.1.time_window refers to the second one.

Example

The request to RouteQ includes one vehicle with three shifts (morning, afternoon, and evening) and three orders, of which one order has a delivery time in the morning, and two others in the evening.

Note

Note that the RouteQ response will only use the morning and evening shifts, since there are no delivery orders for the day shift.

API request (JSON)API responseView on map

Multiple courier runs per day

By default, a courier starts and ends their route at a depot, that is they complete exactly one route during a working day (shift).

If a courier can return to the depot anytime during the work shift to reload additional orders, you can limit the number of runs using one of the parameters:

  • vehicle.shifts.N.max_runs: The maximum number of runs per shift. The default is 1.

  • vehicle.max_runs: The maximum number of runs for all the courier's shifts summed up (for example, the courier can perform all the runs in one shift). The number of courier runs can be lower than the number of shifts. The default is 1.

  • If none of the parameters are specified, the courier can make as many runs as they have shifts (one run per shift).

Note

You can use vehicle.max_runs or vehicle.shifts.N.max_runs in the planning task, but not both parameters at the same time.

At least one shift should be set for the courier.

Example

The example below uses one vehicle with the limited carrying capacity of 2 tons and three orders with a weight of 0.8 tons. The vehicle is allowed to do two runs. This means that after completing two orders, the vehicle can return to the depot to pick up and deliver another order.

API request (JSON)API responseView on map

Limit the number of stops per shift

When planning a route, you can specify the minimum and maximum number of stops in the route. This can be useful, for example, to limit the maximum courier workload per day or, on the contrary, make sure that the courier gets on the route with a certain minimum number of stops.

For this, RouteQ uses the minimal_stops and maximal_stops shift parameters. You can set the number of stops for each courier.

The restriction is not strict and may be violated. If the limit on the minimum number of stops is violated, a penalty applies. The penalty is specified in the following shift fields: penalty.stop_lack.fixed (for making fewer stops than the minimum number) and penalty.stop_lack.per_stop (for each stop missing under the minimum number).

In the same way, if the limit on the maximum number of stops is violated, a penalty applies. The penalty is specified in the following shift fields: penalty.stop_excess.fixed (for making more stops than the maximum number) and penalty.stop_excess.per_stop (for each stop above the maximum number).

Note

Usually when you restrict the number of stops, the solution metrics degrade since the solution favors restrictions at the cost of the optimal route.

Unique stops

When using the minimal_stops parameter, orders with matching coordinates are considered different stops if they aren't consecutive in the route. For the minimal_stops condition to be fulfilled, the algorithm may plan the route to have the courier visit the same address multiple times.

To avoid repeat visits, use the minimal_unique_stops parameter and penalty.unique_stop_lack.fixed and penalty.unique_stop_lack.per_stop penalties instead of minimal_stops.

The minimal_unique_stops parameter considers only orders with unique coordinates, so there won't be any repeat visits planned for the courier. However, orders to the same address may be assigned to different couriers, because such stops are considered different in this case, and the algorithm can use this to enforce the minimal_unique_stops limit.

It makes sense to use the minimal_unique_stops limit if the route has a lot of multi-orders.

Stops for non-active couriers

By default, the minimal_stops and minimal_unique_stops limits apply to all assigned couriers. When you specify more couriers than necessary, and the penalty.stop_lack or penalty.unique_stop_lack penalty value is too high, this can result in suboptimal planning.

To set the minimum number of stops in a shift and build an effective route at the same time, use the option ignore_min_stops_for_unused (see Counting the minimum number of stops for active vehicles only).

Example 1

In the example, the route is built for 3 couriers who deliver to 15 points. Since there are no restrictions for the route, the route is built the usual way.

API request (JSON)API responseView on map

Example 2

This example has the same conditions as example 1, but sets the limit of 4 minimum stops per courier. As a result, the routes changed so that each courier visited at least 4 delivery locations.

API request (JSON)API responseView on map

Example 3

This example has the same conditions as example 1, but sets the limit of 5 maximum stops per courier. As a result, orders are distributed evenly among all couriers, even though several orders are located next to each other and can be completed by one courier.

API request (JSON)API responseView on map

Maximum shift duration

Sometimes you may need to extend the courier's working hours. But you also need to limit the maximum shift length and set the window you want to fit into. For example, there's a specific 14-hour time window during which couriers can make deliveries: from 8:00 to 22:00. However, the duration of each courier's shift must not exceed 10 hours (max_duration_s), and there should be no more than 8 working hours per shift (max_working_duration_s).

RouteQ helps you schedule shifts with hard time windows and preferred lengths. In this case, the courier's shift length doesn't affect the work start time. It only affects the time that they spend completing orders.

The maximum shift length can be set as a soft restriction shifts.N.max_duration_s that can be violated for a penalty or as a hard restriction shifts.N.hard_max_duration_s that can't be violated:

  • When planning with a soft restriction, the workload that doesn't fit into one courier's preferred shift is distributed among others or to the same courier, but with penalties for violating the shift length. All orders that can't be completed in max_duration_s are added to the route with the penalty specified in the shift.penalty.late field (if applicable) or in the shift.penalty.out_of_time field all other times. The total route penalty for violating the maximum shift length is shown in the API response in the overtime_penalty field. If the estimated order cost including penalties is higher than the cost of completing the order by another courier, the order is handed over to another courier.

  • When planning with a hard restriction, the courier's shift never exceeds the hard_max_duration_s value.

The maximum working time per shift can also be defined by a soft restriction shifts.N.max_working_duration_s or hard restriction shifts.N.hard_max_working_duration_s.

  • If the courier violates a soft restriction, the workload is redistributed to other couriers or given back to the same courier, but with penalties. Orders that are not completed within the shift's working time max_working_duration_s will be added to the route with a penalty working_overtime:

    • shifts.N.penalty.working_overtime.fixed: Penalty for exceeding the maximum working time per shift.

    • shifts.N.penalty.working_overtime.minute: Penalty for each minute of such overtime.

    If the penalty value isn't specified, the penalty for violating the time window shift.penalty.out_of_time will be applied to the orders. You can find the total penalty amount in the working_overtime_penalty field of the API response. The order will be transferred to another courier if the estimated cost of the order (including penalties) exceeds the cost of completing the order with another courier.

  • When planning with a hard restriction, the maximum duration of working time per shift never exceeds the value specified in hard_max_working_duration_s.

If you use both soft and hard restrictions for max_duration_s and max_working_duration_s, the hard one can't be lower than the soft one. By default, max_duration_s and max_working_duration_s are set to 2 days, while hard_max_duration_s and hard_max_working_duration_s are set to 30 days.

Example 1

Let's say the shift window shifts.N.time_window is from 8:00 to 23:00. By default, routes will be built to use the minimum number of couriers.

If you specify max_duration_s = 14400, the load is distributed between couriers, and each of them works for approximately 4 hours (the shift length is set in seconds). Some couriers may still work more than 4 hours, because it's less costly to pay for extra hours than to hand over the order to another courier.

As a result of planning, orders are distributed among three couriers. One courier's shift went over 4 hours. The penalty amount is shown in the API response in the overtime_penalty field.

API request (JSON)API responseView on map

Example 2

The same as in Example 1, but with hard restrictions on the maximum shift length: hard_max_duration_s = 14400. When planning, all couriers are given shifts that are 4 hours or less. As a result, the number of couriers increased to 4.

API request (JSON)API responseView on map

Example 3

Three couriers are delivering 26 orders. The shift window shifts.N.time_window is 8:00 to 23:00. Penalties are set for exceeding the maximum working time per shift: shifts.N.penalty.working_overtime.fixed for overtime, and shifts.N.penalty.working_overtime.minute for overtime per minute.

If you specify shifts.0.max_working_duration_s = 14,400, each courier will work for approximately 4 hours. The restriction is soft, and one courier worked overtime.

As a result of the planning, all orders are distributed among couriers, and the total penalty amount for overtime is indicated in the field working_overtime_penalty of the API response.

API request (JSON)API responseView on map

Example 4

Same as in Example 3, but with a hard limit on the maximum working time per shift shifts.N.hard_max_working_duration_s = 14,400.

As a result of planning, all couriers are busy and work for a maximum of 4 hours. However, one of the orders remains unassigned.

API request (JSON)API responseView on map

Maximum mileage per shift

In some cases, you need to limit the mileage per vehicle. For example:

  • The planning includes hired cars, and their rate implies limited mileage.

  • You can assign specific vehicles for far-away points. Then you can limit the mileage for other vehicles.

The shifts.max_mileage_km parameter determines the maximum vehicle mileage per shift. The entire mileage is taken into account, including:

  • Traveling from the depot or starting point to the first point from the order list.

  • Returning to the depot or end point of the route from the last point in the order list.

The restriction is soft: the algorithm may violate it. Penalties are set in the fields:

  • shifts.penalty.max_mileage.fixed: A fixed penalty for exceeding the maximum mileage (1000 by default).

  • shifts.penalty.max_mileage.km: A penalty per each kilometer in excess of the maximum mileage (100 by default).

If the maximum mileage per run is critical to you, and the vehicle can do several runs per shift, you can create multiple identical shifts (see Example 2).

If the courier uses public transit, only the walking part of the route is returned as the mileage.

Example 1

The mileage limit per courier is 50 km, with a big penalty for violating this restriction. In the result, the planned mileage on any route doesn't exceed 50 km.

API request (JSON)API responseView on map

Example 2

There's one courier and each of their runs should be no more than 50 km. To achieve this, the courier has several shifts with the same time windows and mileage restrictions. The result includes 3 routes, each one less than 50 km long.

API request (JSON)API responseView on map

Route grouping

To set the preferred area of work for a vehicle so that the route includes orders near a certain location, use the global_proximity_attraction_point parameter. In the parameter value, specify the location id with the garage type. This is used as a hotspot: the algorithm aims to reduce the total distance between orders on the route and this location.

The density of routes around the hotspot is determined by the options.global_proximity_factor option: the greater its value, the denser the grouping. For more information about this option, see Grouped routes.

The maximum distance from the route locations to the hotspot is stored in the max_distance_to_attraction_point_m parameter, which can be used to calculate the cost of the route. The total sum of distances from all orders to the hotspot on the route affects the penalty for insufficient route grouping.

Other ways to group orders geographically
  • You can use the global_proximity_factor option to build grouped routes without setting a hotspot. This option only works for vehicles that don't have the global_proximity_attraction_point parameter set.
  • By using geofences for vehicles, you can set restrictions on delivery zones. The geofence restriction can't be violated. In contrast, the route grouping settings are a more flexible tool.

Example 1

Three vehicles have to deliver 15 orders. The route is calculated based on the order weight and vehicle capacity. There are no other restrictions on the route.

API request (JSON)API responseView on map

Example 2

The same as in example 1, but each courier has a set hotspot that shows their preferred area of work. As a result, the routes are more grouped. The maximum distance from the order to the hotspot on the route is 20,663 m.

API request (JSON)API responseView on map

Example 3

The same as in example 2, but one of the couriers has a different hotspot. Because orders are regrouped, the location of routes on the map has changed. The maximum distance from the order to the hotspot increased to 28,528 m.

API request (JSON)API responseView on map

Vehicle or courier cost

You can specify different cost-of-use components for each vehicle in the query to RouteQ. To set the cost-of-use for the vehicle or courier, use the optional vehicle.cost field.

RouteQ lets you define the following main cost components:

  • cost.fixed: The fixed cost-of-use for the vehicle.

  • cost.hour: The cost per hour of work.

  • cost.km: The cost per kilometer of mileage.

  • cost.location: The cost per visit of one order.

  • cost.run: The cost per run.

  • cost.tonne_km: The cost per ton-kilometer of transport work.

In this case, the cost at which the algorithm selects the optimal routing option is used. That's not the actual rate for using the vehicle, but rather the algorithm's settings.

You can also set the cost-of-use for the vehicle or courier as an arithmetic expression. For more information, see Advanced cost settings.

Cost values

Please note that some components of vehicle cost are already set by default:

  • cost.fixed = 3000

  • cost.hour = 100

  • cost.km = 8

They apply in the following cases:

  • If you use Excel and leave these fields blank.

  • If you make an API request and don't fill in the vehicle.cost field.

The default values often need to be adjusted. For example, adjust them in the following business cases:

Note

Always set cost.hour and cost.km to values other than 0, otherwise, the routes will be chaotic. That happens because the algorithm optimizes the total cost. If the cost per hour or kilometer equals 0, the route will cost the same with any mileage.

If you want to minimize the route cost, the cost.hour and cost.km values need to be above 0.

The cost of a ton-kilometer of transport work

Heavily loaded vehicles use more fuel and wear out faster. That's why routes for transporting heavy goods need to be built in such a way that the vehicle travels the minimal possible distance while heavily loaded.

RouteQ uses the cost.tonne_km property to do this. It determines the cost of one kilometer for each ton of cargo in the vehicle. Default value: 0.

The total amount of transport work in ton-kilometers is returned to the total_transport_work_tonne_km field of the API response, and its total cost is in the total_transport_work_cost field.

Example 1

You need to deliver 3 cargos weighing 100, 200, and 2000 kg. The cost.tonne_km cost per ton-kilometer is set to zero. As a result, the heaviest order was delivered last: the vehicle load wasn't taken into account when building the route, and based on other criteria, this option was optimal.

API request (JSON)API responseView on map

Example 2

The source data is the same, but cost.tonne_km = 8. The resulting solution is optimized, taking the load into account: the heaviest order is unloaded first.

API request (JSON)API responseView on map

Advanced cost settings

You can set the cost-of-use for the vehicle or courier as an arithmetic expression. When planning, you can calculate the cost with one expression in the vehicles.cost field or use multiple expressions in nested fields:

  • cost.route: For a route.
  • cost.shift: For a shift.
  • cost.run: For a run.

Alert

If the vehicle.cost field has formulas, then the algorithm calculates the cost-of-use for the vehicle or courier based only on these formulas. Standard cost components are included in the calculation only if they are explicitly specified in the formulas.

The expression can use keywords and mathematical notation given in the tables below. To check the expression, use the HTTP request.

Keywords for route parameters

Group

Keyword and explanation

Runs

  • run_index_in_route: The run number in the route, starting with 1. Only for runs.
  • run_count_in_route: The number of runs in the route. Only for routes.
  • run_index_in_shift: The run number in the shift, starting with 1. Only for runs.
  • run_count_in_shift: The number of runs in the shift. Only for runs or shifts.
  • runs: The number of runs. If the variable is used for a run, its value is 1. For a shift, it's runs = run_count_in_shift, and for the entire route, runs = run_count_in_route.

Shifts

  • shift_index_in_route: The shift number in the route, starting with 1. Empty shifts are skipped and not counted.
  • shift_count_in_route: The number of used (non-empty) shifts in the route.

Orders and stops

  • locations: The number of orders.
  • stops: The number of stops, including orders, depots, parking lots, anchor locations, and other locations (locations with the same coordinates are considered as a single stop).
  • unique_stops: The number of stops with unique coordinates.
  • total_custom_value: The sum of the custom_value field values (additional order parameter) for all orders in a route.
  • max_custom_value: The maximum value of the custom_value field for all orders in a route.

Duration

  • duration_h: The total duration of routes, in hours.
  • transit_duration_h: The duration of traveling.
  • service_duration_h: The duration of handling orders.
  • wait_duration_h: The waiting duration.
  • rest_duration_h: The rest duration.

Distance

  • distance_km: The total length of routes, in km.
  • max_distance_from_depot_m: The distance from the depot to the farthest location in the route.
  • max_distance_to_garage_m: The maximum distance to a garage at the end of the route.
  • max_distance_to_attraction_point_m: The maximum distance from the route locations to the hotspot specified in global_proximity_attraction_point. If the option isn't set, max_distance_to_attraction_point_m = 0.

To set the preferred area of work for a vehicle so that the route includes orders near a certain location, use the global_proximity_attraction_point parameter. In the parameter value, specify the location id with the garage type. This is used as a hotspot: the algorithm aims to reduce the total distance between orders on the route and this location.

Transport work

  • transport_work_tonne_km: The transport work, in ton-kilometers.

Depots

  • middle_depots: The number of intermediate depots on the route.

Start and end of the route

  • start_route_time_s: The start time of the route in seconds from the beginning of the day.
  • finish_route_time_s: The end time of the route in seconds from the beginning of the day.

Travel between route points

  • first_edges_distance_km: The length of the way from a depot or garage (garage-type point) to the first order of the run, in km.
  • first_edges_duration_h: The duration of the way from a depot or garage to the first order of the run, in hours.
  • max_first_edges_distance_km: The maximum length of the way from a depot or garage to the first order for all the runs of the route, in km.
  • max_first_edges_duration_h: The maximum duration of the way from a depot or garage to the first order for all the runs of the route, in hours.
  • last_edges_distance_km: The length of the way from the last order of the run to a depot or garage, in km.
  • last_edges_duration_h: The duration of the way from the last order of the run to a depot or garage, in hours.
  • max_last_edges_distance_km: The maximum length of the way from the last order to a depot or garage for all the runs of the route, in km.
  • max_last_edges_duration_h: The maximum duration of the way from the last order to a depot or garage for all the runs of the route, in hours.
  • max_edge_distance_m: The maximum distance between orders for all routes, in meters.
  • max_edge_duration_h: The maximum duration of travel between orders for all routes, in hours.

For the purposes of calculating the distance and travel duration between route points, the delivery, pickup, and drop_off types are considered orders. Cross docking locations are treated as regular depots. Parking spots (type: parking), anchor locations for trailers (anchor), and rest locations (rest_place) are ignored. For example, if route building results in an "order — parking spot — order" sequence, then the distance and travel duration are calculated directly between the two orders.

These parameters are used to implement the same scenarios as those found in penalties for the start or end of the route as close to the depot as possible.

Travel with and without orders

  • empty_distance_km: The distance traveled without orders (empty runs), in km.
  • non_empty_distance_km: The distance traveled with at least one order, in km.

Capacity

  • utilization_cbm: The maximum load capacity of the vehicle.
  • utilization_units: The maximum order load capacity of the vehicle.
  • utilization_kg: The maximum load weight of the vehicle.
  • min_free_units: The minimum free space in the vehicle.
  • min_free_weight_kg: The minimum free weight in the vehicle.
  • min_free_volume_cbm: The minimum free volume in the vehicle.
  • total_weight_kg: The total weight of transported cargo.
  • total_units: The total amount of transported cargo.
  • total_volume_cbm: The total volume of transported cargo.

Trailer

  • trailer_used: If a trailer is used on the route, trailer_used = 1. If it isn't used, trailer_used = 0.
  • trailer_duration_h: The total duration of trailer use, in hours.
  • trailer_distance_km: The distance traveled with the trailer, in km.

Walking routes

  • walking_transit_duration_h: The time spent walking.
  • driving_transit_duration_h: The time spent driving.
  • walking_distance_km: The distance the courier walked.
  • driving_distance_km: The distance the courier drove.

Route metrics are calculated using the following formulas:

  • total route duration transit_duration_h = walking_transit_duration_h + driving_transit_duration_h
  • total route length distance_km = walking_distance_km + driving_distance_km

The calculation of variables that are used in routes with walking parts depends on the transportation method:

  • walking: The entire route is traveled on foot, and the distance driving_distance_km and duration driving_transit_duration_h equal 0.
  • transit:
    • driving_distance_km = 0: It's assumed that the courier visited all orders on foot (there isn't enough data to calculate the distance traveled by public transport).
    • driving_transit_duration_h is calculated based on other data received when solving the problem (it may be non-zero).

Mathematical notations and functions

Mathematical notations

Explanation

Numbers

Integers (positive, negative), rational.

Use a dot as a separator for decimals.

Arithmetic operations

+, -, *, /

Brackets

(…)

Mathematical functions

  • max()/min(): With an arbitrary number of operands.
  • Round(): Rounding to an integer.
  • Ceil(): Rounding to a larger integer.
  • Floor(): Rounding to a smaller integer.
  • Abs(): Calculating the absolute value (modulus) of a number.
  • RoundTo()/CeilTо()/FloorTo(): Rounding fractions to the digit set by a user. For example, (RoundTo(x, 100) == 100*Round(x/100)).

Logical functions

bool(): Converts any nonzero real number to 1.0, and zero to 0.0

Comparison operations

<, >, =

Logical operations

  • &: Logical AND.
  • |: Logical OR.
  • ! : logical negation.

Functions

  • has_location(<condition>): Checks whether there's a stop in the route (run, shift) that matches the criteria.
  • has_order(<condition>): Checks whether there's an order (pickup or delivery) in the route (run, shift) that matches the criteria.
  • order_count(<condition>): Returns the number of orders (pickup or delivery) in the route (run, shift) that match the criteria.
  • stop_count(<condition>): Returns the number of stops (pickup, delivery, or dropoff) in the route (run, shift) that match the criteria. In a multi-order, all orders will be counted as one stop.
  • location_count(<condition>): Returns the number of stops of any type in the route (run, shift) that match the criteria. In a multi-order, each order is counted as a separate stop.

Function conditions

The following functions can only be used inside the has_location, has_order, order_count, stop_count, and location_count function conditions:

  • in_zone(<zone name>): Checks if the stop is in the specified zone.
  • has_load_type(<load type name>): Checks if the order has the specified load type.
  • has_required_tag(<tag name>): Checks if the order has the specified required tag.
  • has_optional_tag(<tag name>): Checks if the order has the specified optional tag.
  • has_tag(<tag name>): Checks if the order has the specified tag (required or optional).
  • is_order(): Checks if the stop is an order (pickup or delivery).
  • is_pickup(): Checks if the stop is a pickup order.
  • is_delivery(): Checks if the stop is a delivery order.
Expression examples
  • 500 + 500 * has_location(in_zone('West')): If the route has a stop in the West zone, the expression returns 1000. Otherwise, it returns 500.
  • 450 * location_count(in_zone('West') | in_zone('North')): Counts the number of stops in at least one of the two zones (West and North) and returns that number multiplied by 450.
  • 1000 + has_location(in_zone('West') & has_load_type('Frozen')) * 500: If the route has at least one stop in the West zone and has the Frozen load type, the expression returns 1500. Otherwise, it returns 1000.
  • 100 * (has_location(in_zone('West')) & has_location(in_zone('North'))): If the route has at least one stop in the West zone and at least one stop in the North zone (it can be one stop in both zones or two different stops), the expression returns 100.
  • 100 * has_location(in_zone('West') & in_zone('North')): If there's a stop that falls into both West and North, the expression returns 100.
  • 100 * location_count(is_pickup() & in_zone('West') & !has_tag('Return')): The expression counts the number of pickup orders that are in the West zone and don't have the Return tag, and returns their number multiplied by 100.

Solution metrics

If the vehicle or courier have advanced cost settings set, you'll see the following fields in the solution metrics:

  • run_custom_cost: The cost of the run calculated using the cost.run formula.
  • shift_custom_cost: The cost of the shift calculated using the cost.shift formula. Shown only for the first run of the shift.
  • shift_total_custom_cost: The total cost of the shift. It includes the cost of the shift calculated using the formula plus the cost of all the shift's runs. Shown only for the first run of the shift.
  • route_custom_cost: The cost of the route calculated using the cost.route formula. Shown only for the first run of the route.
  • total_custom_cost: The full cost of the route, including the cost of the route calculated using the formula plus the cost of all the shifts. Shown only for the first run of the route, the other runs have it as 0.

Example 1

The task uses the following pricing plan:

  • The basic cost of one multi-order is 170 units.

  • The cost of a multi-order increases by 20 units every 100 kilometers.

  • The courier receives at least 4000 units per shift even if the number of orders is insufficient.

This pricing corresponds to the following cost: 100 * duration_h + 8 * distance_km + max(4000, (170 + 20 * Floor(distance_km / 100)) * unique_stops).

As a result, the cost of using each vehicle is at least 4000. The cost in excess of the shift payment (or delivered orders) is formed based on the total time and route length.

API request (JSON)API responseView on map

Example 2

You need to plan a route, provided that the cost depends on the mileage and is determined using the formula:

<vehicle cost> = maximum {<minimum route cost>, <number of locations on the route> * <cost of delivery to the location>}

The minimum route cost and cost of delivery to the point are indicated in the table:

Mileage

The cost of one multi-order (rubles)

The minimum route cost (rubles)

No more than 150 km

510

6000

No more than 450 km

530

7000

No more than 750 km

550

8500

More than 750 km

570

11,000

When planning, the vehicle cost is calculated using the formula:

max(6000 + (distance_km > 150)*1000 + (distance_km > 450)*1500 + (distance_km > 750)*2500, stops*(510 + min(60, Ceil(max(0, distance_km - 150)/300)*20)))

More about the calculation formula
  • The minimum route cost: 6000 + (distance_km > 150)*1000 + (distance_km > 450)*1500 + (distance_km > 750)*2500.
  • The number of points on the route is determined using the stops parameter.
  • The cost of delivery to the point is determined as follows. The minimum multi-order cost is 510, the maximum possible addition to it is 570 - 510 = 60. It's important that the cost of multi-orders increases uniformly in increments of 20 for every 300 km of mileage. This can be represented using the formula Ceil(max(0, distance_km - 150)/300)*20. Then the cost of delivery to the point, based on the data from the table, is equal to 510 + min(60, Ceil(max(0, distance_km - 150)/300)*20)).

As a result, vehicle 1 has a route length of more than 150 km, the number of stops is 18 (1 multi-order). The final cost is 9540. Vehicle 2 has a route length of no more than 150 km, the number of stops is 21 (1 multi-order). The total cost is 10,710.

API request (JSON)API responseView on map

Example 3

You need to plan the route so as to minimize underloading the vehicle. To do this, you can set an additional vehicle cost in the formula, provided that its load is below a certain limit. For example, when planning vehicles with a load capacity of 3000 kg, load them by at least 80%. Note that we assume that the remaining cost (per hour, per km, for the fact of use) will be set by default. The vehicle cost is calculated using the formula:

3000 + duration_h*100 + distance_km*8 + max(0, 2400 - utilization_kg) * 100

More about the calculation formula

3000, 100, 8 in the first three summands are default values of the cost per use, per hour, and per kilometer, respectively. The formula adds the cost of 100 units for each underloaded kilogram from 2400: * = 3000 * 80% = 2400.

As a result, the algorithm will aim to load all vehicles by at least 80% (if possible).

API request (JSON)API responseView on map

Example 4

You need to plan a route, which includes some retail chains that open earlier. The task uses the following pricing plan:

  • The base cost of the route is made up of the time spent and the distance traveled.

  • Routes should be started earlier to avoid morning traffic. To encourage this, a penalty for departing after 8 AM is added to the cost calculation formula.

This pricing corresponds to the following cost: 100 * duration_h + 8 * distance_km + 50 * (start_route_time_s > 28800)

As a result of planning, all vehicles arrive at the depot before 8 AM.

API request (JSON)API responseView on map

Example 5

You need to plan routes within the city, and it's not possible to transport light and heavy orders in one vehicle at the same time. The planning task has the City geofence specified, and all orders are tagged Light or Heavy based on their weight.

These inputs correspond to the following cost:

(has_location(in_zone('City')) & has_location(has_tag('Light')) & has_location(has_tag('Heavy')))*100,000,000

You can't assign incompatible order types here, because compatibility restrictions only apply within one geofence.

API request (JSON)API responseView on map

Calculation of the courier's payout

Use the payout field to calculate what payment the courier will receive for completing the route. During planning, you can calculate payouts using one expression in the vehicle.payout field or use multiple expressions in nested fields:

  • payout.route: For a route.
  • payout.shift: For a shift.
  • payout.run: For a run.

In the expressions, you can use the same keywords and mathematical notation you use to calculate the cost of the route for the company (the vehicle.cost field).

Note

The vehicle.payout field doesn't affect the route optimization, it's calculated after the planning task has been solved.

If the courier has the vehicle.payout field set, you'll see the following fields in the solution metrics:

  • run_payout: Payout for the run calculated using the payout.run formula.
  • shift_payout: Payout for the shift calculated using the payout.shift formula. Shown only for the first run of the shift.
  • shift_total_payout: Total payout for the shift. It includes the payout for the shift calculated using the formula plus the payout for all the shift's runs. Shown only for the first run of the shift.
  • route_payout: Payout for the route calculated using the payout.route formula. Shown only for the first run of the route.
  • total_payout: Full payout for the route, including the payout for the route calculated using the formula plus the payout for all the shifts. Shown only for the first run of the route, the other runs have it as 0.

If the payout field is set for at least one courier, the planning results also have the total_payout metric, which is the total amount to be paid to couriers.

Example 1

Two couriers are delivering 40 orders, each courier makes 2 runs. The couriers' cost for the company is set by the expression in the cost field. For courier 1, the payout for the completed route is calculated using the expression in the payout field: this amount is displayed in the metrics of the first run and is equal to 5000 units. For Courier 2, the payout is not calculated, so the total_payout amount is also equal to 5000 units.

API request (JSON)API responseView on map

Example 2

Two couriers are delivering 40 orders. For Courier 1, the payout calculation is set as follows: starting the shift — 3000 units, each kilometer of the route — 10 units, each delivered order — 50 units. If they deliver more than 10 orders during the shift, they receive a bonus of 500 units. For Courier 2, the payout is not calculated.

As a result of planning, Courier 1 makes two runs and delivers 20 orders. For each run, run_payout is applied (1232 and 1053 units), as well as shift_total_payout for the shift (2785 units, including the shift_payout bonus of 500 units) and route_payout for the route (3000 units). In total, the courier gets total_payout of 5785 units.

API request (JSON)API responseView on map

Vehicle tags

When you set up a vehicle or courier in a request to RouteQ, you can define the rules (or tags) for its compatibility with orders.

Compatibility rules may be required if a vehicle has special equipment that's necessary to fulfil a particular order, like an isothermal van for transporting food.

Vehicle and order compatibility rules (vehicle tags) are set in the following fields:

  • vehicle.tags: The vehicle properties that can partially match the properties required to fulfill the order.

  • vehicle.excluded_tags: The vehicle properties that must not match the properties required to fulfill the order.

To set the order tags, use the location.required_tags field in the request.

Alert

The order tags identify the vehicle characteristics that an order requires, whereas vehicle tags define the actual rules for compatibility of a particular vehicle with different orders.

Vehicle tags can be set as strings or regular expressions. Tags can be separated by commas in strings. In this case, the vehicle can deliver the order if at least one of the listed vehicle tags matches the order tag or if the order has no tags. Regular expressions are used to describe the more complex logic and vehicle/order compatibility rules.

RouteQ uses the POSIX Extended regular expression syntax. You can test your regular expressions at Regex101.

Let's look at some real-life cases of using tags.

Example 1

Let's take a use case when a transportation company delivers food and drinks in small bulk quantities. Some foodstuff must be delivered in isothermal trucks only. At the same time, all drinks and some foodstuff can be delivered without refrigerators. In addition, some customers can only accept a cargo if a vehicle is equipped with a tail lift.

To set these order restrictions, specify the following tags in the location.required_tags field:

  • TAIL_LIFT: For orders that can only be unloaded if a vehicle is equipped with a tail lift.

  • ISOTHERMAL_TRUCK: For orders containing goods that require temperature-controlled transportation.

  • NORMAL_TRUCK: For orders that do not require special conditions of transportation.

To set vehicle features, use the following tags in the vehicle.tags field:

  • TAIL_LIFT: For vehicles equipped with a tail lift.

  • ISOTHERMAL_TRUCK: For isothermal vehicles.

  • NORMAL_TRUCK: For regular vehicles without an isothermal van.

Note

A vehicle may have one or more tags, or it may not have any at all, depending on its actual configuration. The same rule applies to order tags.

API request (JSON)API responseView on map

Example 2

Let's take a use case when some orders have a restriction on the vehicle body height due to different height of unloading zones.

For example, there are orders with the restriction on body height of 2 meters, 2.3 meters, and 2.5 meters. Then they should have the appropriate tags: Max_200, Max_230, and Max_250.

For the vehicles, tags are set in a more complex way, since a vehicle up to 2 meters high can deliver any orders, whereas a vehicle up to 2.8 meters high can only deliver some orders. That is, if you only use tags, then at a vehicle's height:

  • Up to 2 meters, the vehicle.tags field will contain all Max_200, Max_230, and Max_250 values.

  • From 2 to 2.3 meters, the vehicle.tags field will contain the Max_230 and Max_250 values.

  • From 2 to 2.3 meters, the vehicle.tags field will contain the Max_250 value.

  • More than 2.5 meters, the vehicle.required_tags field will be empty.

API request (JSON)API responseView on map

Example 3

Let's take a use case when some orders have a restriction on the arrival of vehicles with a certain load capacity (the OR condition applies when any of the corresponding vehicles is suitable).

For example, there are vehicles with different load capacity: 1, 3, 9, and 15 tons. Let's fix these characteristics using vehicle.tags tags. Because you have to combine these tags when describing orders, let's set them using regular expressions: .*1TON.*, .*3TON.*, .*9TON.* and .*15TON.* (.* means any substring).

The vehicle load capacity limit for some orders is from 1 to 15 tons, from 3 to 9 tons, and from 1 to 3 tons. This limit is set for the order by a string with a regular expression that includes tags of all suitable vehicles. That is, the location.required_tags field will contain the following regular expressions at the required load capacity:

  • From 1 to 15 tons: ##1TON##3TON##9TON##15TON##.
  • From 3 to 9 tons: ##3TON##9TON##.
  • From 1 to 3 tons: ##1TON##3TON##.

Note

The order tags in example 2 are set by a simple list, because the vehicle must have all the listed tags (AND condition). You have to set at least one of the order tags (OR condition) for the vehicle in example 3. To do this, you can use regular expressions or create separate tags for each possible combination of OR conditions.

API request (JSON)API responseView on map

Optional tags

When planning, you may sometimes need to consider which vehicles are best to use (or avoid) for certain orders. For example, it's better to give VIP orders to specially trained couriers.

For such requirements, there are optional tags. They're listed in the location.optional_tags field. If preferences can't be met, they may be violated (for example, because of weight restrictions or delivery time). That's the difference between optional tags and required vehicle characteristics.

Note

If you'd prefer to have vehicles used in a certain zone, set optional geofences for them.

You can use the location.optional_tags.value tag weight to prioritize your preferences. Its value can be positive or negative. When planning, the optional tag is compared to the tags of the vehicle that is delivering the order. If the tag from location.optional_tags matches a vehicle tag from the following lists:

  • vehicle.tags: value is deducted from the route cost.

  • vehicle.excluded_tags: value is added to the route cost.

The value that optional tags add to the route cost is returned in the result.routes.metrics.total_optional_tags_cost field (it can be negative).

Example

You need to deliver 10 orders, some of them have the following optional tags:

  • vip: 6 orders. Its weight varies from 100 to 350 at different points.
  • morning: 3 orders. Its weight is the same at all points — 500.

One of the vehicles has vip and morning tags specified in the vehicle.excluded_tags field. This means that it's best not to give vip-tagged and morning-tagged orders to this courier due to the late start time of the route.

As a result, in most cases the requirements aren't violated: five out of six vip orders are delivered by the courier that doesn't have any limits on the vip tag. There are no violations related to the morning tag.

But one vip order is scheduled for a vehicle with the vip tag in excluded_tags due to capacity restrictions. 100 is returned in the total_optional_tags_cost field for this route: using an "unsuitable" vehicle increased the cost by 100 units. The preference was violated for the order with the lowest location.optional_tags.value weight.

API request (JSON)API responseView on map

Order incompatibility

Orders may have incompatible types, which you can set using the following parameters:

  • options.incompatible_load_types: Sets order incompatibility for all vehicles in the same way. For more information, see the Order incompatibility section.

  • vehicle.incompatible_load_types: Sets order incompatibility for a specific vehicle by enabling you to take vehicle features into account.

  • vehicle.onboard_incompatible_load_types: When this is set to false (by default), order incompatibility remains in effect throughout the entire run, and when the value is true, then only while the orders are in the vehicle.

Note

The vehicle.incompatible_load_types parameter has a higher priority and completely redefines the options.incompatible_load_types parameters for this vehicle.

For example, different temperature modes are required to transport milk and ice cream. If the vehicle has one compartment, it can transport either milk or ice cream. If the vehicle has two compartments with different temperature modes, it can transport milk and ice cream at the same time in different compartments. In this case, specify the order incompatibility vehicle.incompatible_load_types for vehicles with one compartment. Don't use this parameter for vehicles with two compartments and don't specify incompatible types options.incompatible_load_types for orders in general.

If orders are incompatible for reasons other than vehicle characteristics, set vehicle.onboard_incompatible_load_types = true. For example, if orders from competing companies shouldn't be transported at the same time, the courier will pick up and deliver each company's orders separately without stopping at a depot. This reduces mileage and time en route.

Example 1

The delivery consists of 4 addresses with the following order types:

  • Order 1 (id = 1): flowers.

  • Order 2 (id = 2): flowers.

  • Order 3 (id = 3): flowers, sweets.

  • Order 4 (id = 4): flowers, ice-cream.

Flowers and sweets are incompatible for delivery in the same vehicle. Moreover, simultaneous delivery of flowers and ice-cream in vehicle 2 is prohibited, but simultaneous delivery of flowers and sweets is permitted. Then the solution will contain delivery of orders 2 and 4 in the first vehicle and delivery of orders 1 and 3 in the second vehicle.

API request (JSON)API responseView on map

Example 2.1

You need to have 3 orders from different companies picked up and delivered. Orders 1 and 2 and orders 2 and 3 cannot be delivered together, because they belong to competing companies.

As a result of planning, the courier picks up and delivers orders 1 and 3, then returns to the depot to load and deliver order 2 as part of a new run.

API request (JSON)API responseView on map

Example 2.2

The same as in example 2.1, but vehicle.onboard_incompatible_load_types = true.

As a result of planning, the courier picks up and delivers order 2, and then picks up and delivers orders 1 and 3 without visiting the depot. This means less mileage and time en route.

API request (JSON)API responseView on map

Geofences

You can set limits for vehicles by delivery zones. You can set geofences within which the vehicle can deliver orders in the Allowed geofences field on the Vehicles sheet (vehicles.allowed_zones in the API):

  • The vehicle can deliver orders within any geofences specified in this field.

  • If you don't specify allowed geofences, the courier can deliver orders without restrictions.

  • The order can't be delivered if all couriers have allowed geofences specified and the delivery location doesn't fall within any of them.

Geofences restricted for delivery are set in the field Forbidden geofences(vehicle.forbidden_zones in the API). The courier can't deliver orders to within geofences specified in this field.

Note

If an order is located in an area that overlaps both allowed and forbidden geofences, it can't be delivered, because the Forbidden geofences field values (forbidden_zones) take priority over Allowed geofences field values (allowed_zones).

If certain geofences are forbidden for all couriers, use the global option options.avoid_zones. Couriers are prohibited from making deliveries within geofences specified in vehicle.forbidden_zones, but they can travel through them. On the contrary, couriers are restricted from even entering the geofences specified in options.avoid_zones. For more information, see Excluded geofences.

When assigning orders, the algorithm takes into account geofence incompatibilities — both set by the global option and specified for individual couriers. When planning routes, you can also cancel all geofence restrictions.

Example

The example features 4 orders, 2 vehicles, and 4 geofences specified in the interface. Coordinates automatically determine how orders pertain to geofences:

  • Order 1 — simultaneously in zone1 and zone2.

  • Order 2 — in zone1.

  • Order 3 — simultaneously in zone2 and zone3.

  • Order 4 — in zone4.

The availability of geofences for vehicles determined:

Courier username

**Allowed geofences ** (allowed_zones)

Forbidden geofences (forbidden_zones)

Vehicle 1

zone1, zone2

zone3

Vehicle 2

Geofences not defined

zone2

As a result, Vehicle 1 delivers Orders 1 and 2, Vehicle 2 delivers Order 4. And Order 3 remains unassigned, because its address is located in the zones forbidden for both vehicles.

API request (JSON)API responseView on map

Optional geofences

When assigning orders to vehicles, you can make use of optional geofences, which have bonuses or penalties associated with visiting them. You can set one or more such geofences for each vehicle.

Optional geofences represent a soft restriction. They help designate zones that are preferred for delivery (high-priority and low-priority).

Optional geofences are set using the vehicle.optional_zones array:

  • optional_zones.N.zone: Name of the geofence.
  • optional_zones.N.value: If the value is greater than 0, the vehicle receives a bonus for visiting the specified geofence (the value of the parameter is deducted from the route cost), and if it's less than 0, a penalty (the value is added to the route cost).

Example 1

You need to deliver 10 orders with two vehicles. For the first vehicle, the South optional geofence is set, and for the second one, North. Visiting these geofences carries a bonus of 1000 units. As a result, the first vehicle delivers orders in the south, and the second one, in the north. Order 5 is an exception: it belongs to the South geofence, but was delivered by the Courier north vehicle.

API request (JSON)API responseView on map

Example 2

4 couriers have to deliver 27 orders. The city is divided into 4 optional geofences: Northwest, Northeast, Southwest, and Southeast. Each courier has been set one high-priority zone that carries a bonus, two low-priority zones, and one zone that carries a penalty. As a result of planning, couriers end up delivering orders in their high-priority zones. The exceptions are orders 18, 20, and 23 due to vehicle capacity limitations.

API request (JSON)API responseView on map

Incompatible geofences

You can set geofence incompatibility in order to prevent orders belonging to different delivery zones from ending up part of the same courier run.

Using the vehicles.incompatible_zones parameter, you can set the list of such zones both for the entire solution and for a specific courier. Incompatible zones defined for the courier take precedence over globally defined zones. In order for the global restriction options.incompatible_zones not to apply to the courier, set an empty list in the vehicles.incompatible_zones field.

Alert

When planning in Excel, for the global zone incompatibility restriction not to apply to the courier, put a space in the incompatible_zones.N cells for them (it corresponds to an empty list).

Example

Each of the three couriers must deliver 4 orders to different parts of the city: northwest, southwest, northeast, and southeast. These areas correspond to the following geofences defined during planning: Northwest, Southwest, Northeast, Southeast.

Global geofence incompatibility has been set: southern zones (Southwest and Southeast) are incompatible with the northern ones (Northwest and Northeast). Additionally, two couriers have the vehicles.incompatible_zones parameter set for them: for the first courier, it contains an empty list, and for the second, it makes the western zones (Northwest and Southwest) incompatible with the eastern ones (Northeast and Southeast).

As a result of planning:

  • Courier 1 is unaffected by the global incompatibility, and all the 4 orders from different zones get included in their run.
  • Courier 2 is subject to the zone incompatibility restrictions set for them in the vehicles.incompatible_zones parameter: one run includes orders to the eastern zones, and the other, to the western ones.
  • Courier 3 is subject to the global restrictions: orders to the southern and northern zones get assigned to different runs.

API request (JSON)API responseView on map

Transportation method

When building routes, it is assumed by default that a courier uses a passenger car for delivery (load capacity less than 2.5 tons). To specify delivery on foot, by truck, or by public transit, use the vehicle.routing_mode parameter. If all couriers have the same delivery method, you can use the options.routing_mode routing option.

Note

The vehicle.routing_mode parameter has a higher priority than the options.routing_mode parameter.

Possible values:

  • driving: The default method, routing for vehicles whose load capacity is less than 2.5 tons.

  • truck: Routing for trucks whose default load capacity starts at 2.5 tons. You can specify additional parameters in vehicle properties. When planning for this transportation method, the algorithm considers road signs with cargo vehicle restrictions and the Moscow cargo frame restrictions.

  • walking: A pedestrian route. The route uses only roads that can be walked.

  • transit: A route that includes traveling by public transit and walking from the public transit stop to the delivery location.

For couriers who use bicycles or scooters, specify the transportation method as on foot (routing_mode = walking) and use the speed adjustment factor (travel_time_multiplier). We recommend personalizing adjustment factors for each individual courier. For example, using the Plan/Fact report, you can compare the actual delivery time for a courier on a bicycle or scooter with the planned walking time.

Example 1

The example below uses the transit transportation method for all couriers. As a result, couriers deliver orders using public transit with optimal order allocation.

API request (JSON)API responseView on map

Note

For the built routes, the mileage is specified only for the distances that couriers walk. The distances traveled by public transit are taken into account for optimization purposes, but are not counted in metrics.

Example 2

The example below uses the transit transportation method for one courier and the driving method for another courier. Orders are distributed among couriers so that the vehicle delivers to locations situated further away from public transit stops or from each other.

API request (JSON)API responseView on map

Courier working hours

By default, the service plans a route only with stops that are necessary for cargo delivery. However, couriers often can't work without stopping: they need to have lunch breaks, and you must take these into account when you build routes. Moreover, on long-distance runs, drivers are required to make stops at certain intervals to meet the work and rest requirements.

To control the couriers' working mode, use the vehicle.rest_schedule.breaks object. You can describe simple or complex patterns of rest and maintenance breaks and other activities.

Break presets

You can set a courier's working hours using the parameter vehicles.rest_schedule_preset, which contains a set of settings you can use right away:

  • The courier will take a 45-minute break no later than 4.5 hours of work.

  • Restrictions will be applied to the shift duration (excluding breaks):

    • Soft: 9 hours.

    • Hard: 10 hours.

Possible values for the rest_schedule_preset parameter:

  • public_transport: You can divide the break into multiple parts, each at least 10 minutes.

  • private_transport: You can divide the break into two parts: 15 and 30 minutes.

  • multiday_private_transport: The private_transport mode for multi-day routes. Besides regular breaks, you can also add:

    • Daily break: the last 11 hours of each weekday.
    • Weekly break: the last 45 hours of each week.

A route break will be divided into parts if the following conditions are met:

  • There's not enough time between orders for a full break.

  • There are waiting times between orders, where you can fit breaks.

You can also prohibit couriers from splitting breaks. To do this, use the option can_split_preset_work_breaks = false (by default it's can_split_preset_work_breaks = true).

Example 1

The courier has working hours rest_schedule_preset = private_transport. They contain a break that can be split into two parts. There's an interval between orders that a full break can fit into. As a result, a single break of 45 minutes is scheduled.

API request (JSON)API responseView on map

Example 2

The courier has working hours rest_schedule_preset = public_transport. They contain a break that can be split into multiple parts. There's not enough time between orders for a full break. There's also a waiting time that can accommodate the part of the break. As a result, 2 breaks are scheduled: 32 and 12 minutes.

API request (JSON)API responseView on map

Example 3

The courier has working hours rest_schedule_preset = public_transport. The courier has 14 orders. The shift duration has a hard limit of 10 hours. As a result, one order remained undelivered because it would have required the courier to work more than 10 hours to deliver it.

API request (JSON)API responseView on map

Example 4

The courier has working hours rest_schedule_preset = multiday_private_transport. They have to deliver one order to a remote location. As a result, the courier delivers the order on the following day. They start at 8:00 and make three stops along the way:

  • A 45-minute break after 1 hour and 15 minutes of travel.
  • An 11-hour break from the end of the workday to 8:00 the next day.
  • A 45-minute break after 4 hours and 30 minutes of travel.

API request (JSON)API responseView on map

Schedule templates

You can describe breaks separately for each courier or create templates for multiple couriers. Moreover, you can combine individual and template schedules.

To create a template, add the template_rest_schedules parameter to options and specify an array of schedules in it: [{"id": string, "breaks": [...]}].

To use the template, add the rest_schedule_id parameter to vehicles and specify id of the schedule template from options in it.

Break duration

You can set the break duration using the rest_duration_s parameter mandatory for every break.

Parameter

Value

Example

rest_duration_s

Break duration in seconds.

"rest_duration_s": 600

The break should last 10 minutes (600 seconds).

Break start conditions

The break start is indicated by the time range (from and to). You can set the break start in several ways that are indicated in the table below.

Parameter

Value

Example

work_time_range_from_start

Working hours from the start of the route. Includes: travel time, waiting time, and handling time at order locations and at the depots.

The presence and duration of other breaks don't matter.

"work_time_range_from_start": "01:00:00-03:00:00"

Start the break no earlier than 1 hour and no later than 3 hours after the start of the route.

work_time_range_till_rest

Working hours from the last break (or from the start of the route if it's the first break). Includes: travel time, waiting time, and handling time at order locations and at the depots.

"work_time_range_till_rest": "02:00:00-04:00:00"

Start the break no earlier than 2 hours and no later than 4 hours after the start of the shift (including arrival at the depot, handling time, and periods of travel and waiting).

travel_time_range

Travel time and waiting time from the start of the route. Handling time is not taken into account.

"travel_time_range": "01:00:00-03:00:00"

Start the break no earlier than 1 hour and no later than 3 hours after the start of the route, taking into account the periods of waiting and travel and not counting handling time at order locations and at depots.

continuous_travel_time_range

Travel time and waiting time from the completion of the previous order. Handling time is not taken into account.

"continuous_travel_time_range": "02:00:00-03:00:00"

Start the break no earlier than 2 hours and no later than after 3 hours of continuous travel.

driving_time_range

Travel time from the start of the route. Waiting time and handling time are not taken into account.

"driving_time_range": "01:00:00-03:00:00"

Start the break no earlier than 1 hour and no later than 3 hours from the start of the travel.

exact_time_range

Break start time. The break can't be repeated.

The work start time, the presence and duration of other breaks don't matter.

The break won't start if the route ends before the break start time.

"exact_time_range": "17:00:00-20:00:00"

Start a break from 17:00 to 20:00.

If there are multiple breaks, each range specifies the working range after the end of the previous break. For example, if the first break is 04:00–05:00 and the second break is 02:00–03:00, the first break must start between 4 and 5 hours after work starts, and the second break must start between 2 and 3 hours after the end of the first break.

Break planning conditions

You can specify additional parameters: make a break mandatory or set the minimum working time for the break start.

Parameter

Value

Example

necessary_route_duration_s

The time from the start of the route after which a break must be made. Set in seconds.

"necessary_route_duration_s": 14400

Be sure to start the break after 4 hours (14,400 seconds) have passed from the start of the route.

route_duration_s

The parameter sets the minimum working time at which a break should be scheduled. If the total working time is less, a break isn't scheduled. Set in seconds.

"route_duration_s": 14400

Start the break only if the route lasts at least 4 hours (14,400 seconds).

Combining breaks conditions

Parameter

Value

Example

before_first_location

The possibility to combine a break with other breaks before the first order. The break can only be combined with the breaks whose types are specified in merge_with_types.

"before_first_location": true

The break will be combined with the last break before the first location where the break type is specified in merge_with_types. If there is no such break, this break will be made right before the first location. All such breaks should be in a separate chain.

after_last_location

The possibility to combinу a break with other breaks after the last order. The break can only be combined with the breaks whose types are specified in merge_with_types.

"after_last_location": true

The break will be combined with the first break after the last location where the break type is specified in merge_with_types. If there is no such break, this break will be made right after the last location. All such breaks should be in a separate chain.

type

Break type.

"type": "Lunch"

A lunch break.

merge_with_types

The parameter sets with which break types you can combine the specified break. You can only use it in combination with after_last_location or before_first_location.

"merge_with_types": ["Lunch", "Sleep"]

You can combine this break with a lunch or a sleep break.

Splitting breaks

You can split the break into smaller parts if needed. For example, if there's not enough time between orders for a full break. To do this, specify the parameter can_be_split = true in the breaks schedule.

You can also specify additional parameters for the split break:

Parameter

Value

Example

max_split_parts

The parameter determines how many parts the break will be split into. By default, it's 2 parts.

"max_split_parts": 5

You can split the break into 5 parts.

min_split_part_duration_s

The minimum duration of a single part of the break. Set in seconds. By default, it's 10 minutes.

"min_split_part_duration_s": 1200

You can split the break into parts with a minimum duration of 20 minutes (1200 seconds).

min_first_split_part_duration_s

The parameter specifies the minimum duration of the first part of the break. Set in seconds. By default, this is 1/3 of the total break duration.

"min_first_split_part_duration_s": 1800

The first part of the break should last at least 30 minutes (1800 seconds).

min_last_split_part_duration_s

The parameter specifies the minimum duration of the final part of the break. Set in seconds. By default, this is 2/3 of the total break duration.

"min_last_split_part_duration_s": 2400

The final part of the break should be at least 40 minutes (2400 seconds).

Example

For the courier, one break of 2 hours is provided: "rest_duration_s": 7200. The first three orders have narrow time windows, so you can fill the waiting time between them with breaks. Since there isn't enough time between orders for a full break, it's split into 3 parts.

API request (JSON)API responseView on map

Breaks during order handling

Parameter

Value

Example

can_rest_during_service

Break parameter. Determines whether this break can overlap with the handling time of the order or the depot. Default value: true.

"breaks": [
  {
    "can_rest_during_service": true,
    "rest_duration_s": 3000,
    "work_time_range_from_start": "1:30 - 2:00"
  },
  ...
]    

can_have_rest_during_service

Order or depot parameter. Determines whether this break can overlap with the handling time of this order or depot. Default value: true.

"locations": [
  {
    "id": 123,
    "can_have_rest_during_service": true,    
    "service_duration_s": 600,  
    ...
  }
]

By default, breaks can overlap with the handling time of an order or depot. In routes, these breaks are indicated before the order or depot. The start time arrival_time_s and end time departure_time_s of the break coincide with the arrival_time_s and departure_time_s of the order or depot. Inside this overall interval, the handling time duration is determined by the parameter service_duration_s. In the response for such an order or depot, the field is_after_service_work_break = true is added to indicate that handling starts after the break.

If can_rest_during_service = true, the break can be scheduled as the first location in the shift. For regular breaks that don't overlap with the handling time, this is impossible.

To prevent breaks from overlapping with the handling time, set can_have_rest_during_service to false for the order or depot and can_rest_during_service to false for the break.

Example 1

A 50-minute break should be scheduled 1.5 hours after the start of the route. There are 2 orders in the route, with a handling time of 1 hour and the parameter can_have_rest_during_service = true. The break overlaps with the handling time for the first order, for which the courier arrives 1 hour and 8 minutes after the start of the route and leaves 2 hours and 20 minutes later.

API request (JSON)API responseView on map

Example 2

The same as in example 1, but orders have the parameter can_have_rest_during_service = false. The break is scheduled at the first order's location, but before handling the order and too early: just 1 hour and 9 minutes after the start of the route.

API request (JSON)API responseView on map

Break place

Parameter

Value

Example

at_rest_place

An option to have a break in a location with the rest_place type. If this option is enabled, either all breaks should be in such locations, or all breaks should be outside such locations.

"at_rest_place": true

All breaks will take place in locations with the location.type type equal to rest_place.

The courier can't visit break locations that are situated within the geofences that are restricted for them.

You can set requirements for visiting a break location with the rest_place type using the incompatible_load_types parameter (see Order incompatibility). For example, a courier is delivering an order with the load_type = ice-cream type, and the rest_place location's type is load_type = no-refrigerator. To restrict the courier from visiting this location for a break, set ice-cream as incompatible with no-refrigerator on the Incompatible_load_types sheet.

Break chains

The rest_schedule can contain many independent breaks break chains. Each chain can contain many breaks with special conditions. The start of a break in every chain depends only on the built route. It doesn't depend on breaks in other chains.

Example

Explanation

How to set

Chain 1: Rest for 15 minutes after every 3 hours of transit.

Chain 2: Rest for 6 hours 8 hours after the shift started.

Breaks in chain 2 depend only on the shift start. They don't depend on the transit and rest schedule in chain 1.

Chain 1: Rest for 15 minutes after every 3 hours of transit.

Chain 2: Rest for 6 hours after every 8 hours of transit.

The start of the break in chain 2 depends on the transit and rest schedule in chain 1.

The order of chains doesn't matter, but you must specify breaks in one chain sequentially.

If the route is completed before the stop (for example, the route is completed in 3 hours, and the stop is scheduled after 4 hours), all the unmade stops are reset.

Example

The route is built with 30-minute rest breaks after 4–4.5 hours of work. 15 hours after the start of the route, a sleep break is planned. As a result, a sleep break is scheduled 1.5 hours after a rest break.

API request (JSON)API responseView on map

Penalties

In some cases, RouteQ can plan routes that violate the rest window. To apply penalties for such violations, use the fields penalty.late and penalty.early. If the penalty is higher than the savings resulting from the window violation, the route is planned without violations.

Parameter

Value

Example

penalty.early.fixed

Penalty for an early break start.

"penalty": {"early": {"fixed": 1000}}

Sets a penalty of 1000 for an early break.

penalty.early.minute

A penalty for every minute of an early break.

"penalty": {"early": {"minute": 17}}

Sets a penalty of 17 for every minute of an early break.

penalty.late.fixed

Penalty for a late break.

"penalty": {"late": {"fixed": 1000}}

Sets a penalty of 1000 for a late break.

penalty.late.minute

A penalty for every minute of a late break.

"penalty": {"late": {"minute": 17}}

Sets a penalty of 17 for every minute of a late break.

Repeat conditions

Parameter

Value

Example

repeatable

The parameter specifies that the break should be repeated. It can only be used for the last break in the chain.

"repeatable": true

The break will be repeated before the shift end.

Example 1

We built a route with a lunch break after 4–5 hours of work in this example. As a result, the algorithm schedules a lunch stop for a courier.

API request (JSON)API responseView on map

Example 2

In this example, a route is built between Moscow and St. Petersburg with the first break after 4-4.5 hours, and then with breaks every 2 hours. The algorithm scheduled two breaks.

API request (JSON)API responseView on map

Example 3

For two couriers a break is set with the work_time_range_from_start condition in 3–4 hours after the start of work, but only if the total duration route_duration_s is at least 6 hours. The first courier finishes work in 5 hours, so they work without rest. The second courier works for more than 6 hours, so they get one break.

API request (JSON)API responseView on map

Example 4

The courier delivers orders to a neighboring city and periodic breaks. Breaks are set every 60–80 minutes of the route (travel_time_range) using the repeat condition repeatable. As a result, the algorithm scheduled two breaks.

API request (JSON)API responseView on map

Example 5

As in example 4, but the route lasts several days, and breaks are set for sleep every 12 hours:

  • The repeat condition repeatable is used.

  • The minimum and maximum route duration before rest travel_time_range: "12:00–12:00".

API request (JSON)API responseView on map

Example 6

10-minute breaks are set for every 55–60 minutes of continuous traveling continuous_travel_time_range. As a result, a break is scheduled only between points where the travel time is more than 55 minutes.

API request (JSON)API responseView on map

Example 7

For couriers, two breaks with the break start condition work_time_range_till_rest are scheduled for 2–3 hours after the last break or start of work if this is the first break.

  • The first break is 20 minutes.

  • The second break is 30 minutes.

The first courier finishes work earlier and gets only the first break. The second courier works longer and gets both breaks.

API request (JSON)API responseView on map

Example 8

Two couriers have 19 orders scheduled for them. The first courier should rest every 2–3 hours, and the second should rest every 3–4 hours. Both couriers have the same rest location specified for them: type rest_place.

The first courier starts the route at 7:00 and arrives to the rest location at 9:48, where they rest for 30 minutes before continuing on the route. The second courier starts their route at 8:00 and manages to complete all of their orders before 12 without stopping to rest.

API request (JSON)API responseView on map

Planned route

The user can specify which courier should deliver particular orders. This is useful:

  • For additional planning when you have pre-planned routes.

  • When the order requires a specific courier.

To specify pre-planned orders for the courier, use the vehicle.planned_route option. This option has the following properties:

  • When planning, all orders that you specify in vehicle.planned_route can't become unassigned, even if strict restrictions are violated, such as vehicle capacity or time windows for which hard_window = true.

  • By default, the order sequence set in planned_route isn't fixed. When re-planning, orders can be arranged in a new sequence.

  • If you need to save the route that's specified in planned_route without saving changes (sequence changes and adding new orders), use vehicle.fixed_planned_route = true.

For each order in planned_route, be sure to specify the vehicle shift (even if the vehicle only has 1 shift).

If in planned_route several shifts are specified, you can lock orders for shifts using the fix_planned_shifts = true option. By default, the value is false.

If a courier arrives too early, delivery time of the order is determined by the vehicle.wait_if_early parameter:

  • true: The courier waits for the order time window to start (default value).
  • false: The courier handles the order as soon as they arrive.

Alert

If there is a hard time window for an order location.hard_window = true, the vehicle.wait_if_early parameter value is true.

If the courier needs to visit depots for reloading while following the route, specify them in the middle_depot_id field. They may be the same as the depots the courier visits at the start or at the end of the route.

If the courier needs to visit depots for reloading in planned_route, set is_middle_depot = true (it's false by default). If you set is_middle_depot = true for all depots, the algorithm will plan starting and ending depots based on the courier settings, see sections Start from one of several depot and Return to a depot at the end of the route or run.

Example 1

Both couriers had pre-planned orders in planned_route. As a result, these orders were given to these two couriers.

API request (JSON)API responseView on map

Example 2

This example involves 1 vehicle that has orders linked to it via planned_route. The total weight of the orders exceeds the vehicle capacity, but the route is still being planned (because orders specified in this option can't be unassigned). But the ask status is UNFEASIBLE.

API request (JSON)API responseView on map

Example 3

Theoretically, 2 couriers could deliver all 20 orders without overload and without being late. But a fixed sequence is specified for courier 1 using the vehicle.fixed_planned_route = true parameter, so they'll only deliver orders from this sequence. Courier 2 isn't limited by a strict plan, so they'll take as many orders as they can carry. Several orders will remain in the depot, despite the fact that courier 1 has the space and time.

API request (JSON)API responseView on map

Example 4.1

2 couriers need to deliver 15 orders in 2 shifts. Using the planned_route option, orders 1-8 are assigned to courier 1, and orders 9-15 to courier 2. At the same time, it's indicated that orders 1-4 and 9-12 must be delivered during the first shift, and orders 5-8 and 13-15 during the second shift. Since fix_planned_shifts = false and couriers manage to deliver all orders within one shift, the distribution of orders by shifts is not taken into account.

API request (JSON)API responseView on map

Example 4.2

The conditions are the same as in example 4.1, but the option fix_planned_shifts = true. This means the shifts during which orders have to be delivered are taken into account.

API request (JSON)API responseView on map

Loaded orders

If the order is already in the vehicle, the courier can immediately start delivery. To run this scenario, specify the parameters in the planned_route field:

Parameter

Description

Value

location.loaded_orders*

List of order ids with the delivery type that are already loaded to the vehicle at a depot or at a garage-type location. Other orders need to be additionally picked up at the depot. You can bring any orders with the pickup type to the location regardless of the loaded_orders value.

id

location.delivery_in_current_run*

All orders loaded to the vehicle should be serviced during the same run before the vehicle returns to the depot.

true / false

planned_runs_first

The vehicle will visit all depots in planned_route before making unplanned runs.

Use if:

  • Scheduled orders at depots have already been prepared for loading since last night, and it's more convenient to pick them up earlier.

  • You apply the delivery_in_current_run parameter to first deliver orders from planned_route.

true / false

* Specified for depots and orders of the garage type.

Example

Several orders were loaded in the courier's vehicle last night. The courier doesn't have to pick them up at the depot. Therefore, the point of the garage type is specified in the place where the courier will go.

The loaded_orders parameter shows that orders 1 and 2 have been loaded. The courier can immediately deliver them to the destination.

The following parameters are also set in the conditions:

  • planned_runs_first = true.

  • delivery_in_current_run = true for depot and garage.

The courier starts from the garage and immediately delivers orders 1 and 2. They then go to the depot to pick up orders 3 and 4, which they must deliver immediately after loading. After these two deliveries, the courier returns to the depot to pick up orders 5 and 6 and follows the route further.

API request (JSON)API responseView on map

Fixed part of the route

The visited_locations feature is useful if:

  • The beginning of route must follow a particular path.

  • You need to change the start point when doing additional planning.

The route must be fixed for each courier individually.

To do this, specify the visited_locations array in vehicles and describe the points of the fixed route using the following parameters.

Parameter

Description

id*

Order ID from locations or depot ID from depots. Please note that the order and depot IDs should be different.

shift_id

Courier shift ID from vehicle.shifts. Determines the shift in which to serve this point.

time

The time of departure from the point. If the first point of a fixed part of the route:

  • Has a specified time, that's the starting point.
  • Has no specified time, the route starts from the depot, and the start of work is calculated according to the depot or shift window.

You can specify this parameter for several orders, so that the visit time is calculated according to the time specified in time.

wait_if_early

The need to wait for the start of the time window in case of early arrival.

  • Its default value istrue, meaning that the courier will wait for the start of the time window.
  • If you specify false, the courier will start service immediately after arrival.

* Required parameter

Example 1

There are 5 orders in this example. The sequence of orders 1, 2, 3 is fixed in visited_locations. Orders 4 and 5 can be delivered in any sequence.

API request (JSON)API responseView on map

Example 2

In visited_locations, it's specified that the route must start with order 2 at 10:00.

API request (JSON)API responseView on map

Example 3

The order sequence 1, 2, 3 is set as in example 1. It's also specified that departure from order 1 must be at 08:20 and from order 3 at 11:00.

API request (JSON)API responseView on map

Example 4

There are 8 orders in this example. The specified sequence is orders 1, 2, 3, then the depot, then order 4. The courier may deliver the remaining orders in any sequence.

API request (JSON)API responseView on map

Adjusting the vehicle's travel time

To calculate the speed of the vehicle when building a route, RouteQ uses Yandex Maps data about traffic and the speed specified in the request. Although the trucks usually move slower in traffic jams, some drivers drive more aggressively and arrive earlier than the calculated timeframes.

To compensate for such deviations, RouteQ has the travel_time_multiplier option where you can specify a fractional value. If the value is 1, the calculation is made for the vehicle with the expected average speed. At smaller values (for example, 0.8), the calculation accounts for faster vehicles. At higher values (for example, 1.2), the calculation accounts for slower vehicles.

Note

The travel_time_multiplier parameter only affects the vehicle speed. The handover time at the delivery point remains the same.

Example 1

In the example below, the routing request is sent without adjusting the transit time. As a result, the vehicle delivers the order in 4 hours and 20 minutes, with 3 hours and 40 minutes spent moving between the points.

API request (JSON)API responseView on map

Example 2

The same as in example 1, but with the travel_time_multiplier parameter set to 0.5. In the resulting route, the courier delivers the order in 2 hours and 25 minutes, spending 1 hour and 45 minutes moving between the points. However, time spent on parking and handover remains unchanged.

API request (JSON)API responseView on map

Example 3

The same as in example 1, but with the travel_time_multiplier parameter set to 2. In the resulting route, the courier delivers the order in 7 hours and 58 minutes, spending 7 hours and 18 minutes moving between the points. Traveling the route takes more than twice as long as in example 1, because part of the route occurs at peak hours, and the vehicle can't drive at the maximum speed. Time spent on parking and handover remains unchanged.

API request (JSON)API responseView on map

Adjusting handling time

To the courier needs some time to hand over the order on arrival. This time includes parking, riding the elevator, handing over the delivery, and paperwork. RouteQ ensures that by using the order's service_duration and shared_service_duration fields. However, some couriers deliver goods faster than others. For example:

  • In the case of parallel planning for vehicles and couriers using public transport, couriers who travel by public transport don't have to spend time parking. As a result, they need lower handling time.

  • Some couriers are faster than others. Experienced couriers usually spend less time handing over orders.

To compensate for the difference in courier time, RouteQ has the service_duration_multiplier and shared_service_duration_multiplier options that affect the handling time for a specific courier. If the value is 1, the handling time is the same as in service_duration or shared_service_duration. If the value is less than 1, the handling time is lower. If the value is greater than 1, the handling time is higher.

Note

The service_duration_multiplier and shared_service_duration_multiplier parameters only affect the handling time. The transit time between the points remains the same.

Example 1

In the example below, the routing request is sent without adjusting the handling time.

API request (JSON)API responseView on map

Example 2

In the example below, the same request is sent to RouteQ as in example 1, but with the service_duration_multiplier parameter set to 0.5. The calculated service order is half the time specified in example 1.

API request (JSON)API responseView on map

Example 3

In the example below, the same request is sent to RouteQ as in example 1, but with the service_duration_multiplier parameter set to 2. Handling time is twice as long as in example 1.

API request (JSON)API responseView on map

Prompt delivery

To build an optimal route, the algorithm minimizes the wait time between route points. But there are cases when it's useful to shift delivery to an earlier time. For example:

  • You have to minimize the risk of late arrival.
  • It's more comfortable for the courier to travel in the daytime.

To deliver goods as early as possible, use the penalty.arrival_after_start penalty, which consists of two parameters:

  • average_h: Sets the penalty amount for the average arrival time after the start of the time window. When using the option, we always recommend specifying this value.
  • as_soon_as_possible: Determines whether to start the route as early as possible. The parameter takes the value true or false (by default). It is specified in addition to average_h.

Note

The algorithm can shift the start and arrival time of a vehicle only if the flexible start time is enabled for the depot.

If you don't use the penalty.arrival_after_start penalty, the route will be built with the minimal wait time between points and the latest possible start time from the depot.

When using the penalty, the algorithm will build routes to start them earlier, provided it doesn't reduce quality.

If the route must start as early as possible (even if it causes additional waiting), specify penalty.arrival_after_start.as_soon_as_possible = true.

Example 1

The route includes 4 orders with different time windows. The earliest start of the time window for order 5 is 08:00. The courier starts from the depot at 11:02 and arrives at the first delivery point at 11:40. Order 5 is delivered at 12:59.

API request (JSON)API responseView on map

Example 2

The same data is used as in example 1, but the penalty is set for the average arrival time after the start of the time window using the penalty.arrival_after_start.average_h parameter. The courier starts from the depot at 06:59, waits for 8 minutes, and then delivers the earliest order (order 5) at 08:00.

API request (JSON)API responseView on map

Example 3

The same data is used as in example 2, but the penalty.arrival_after_start.as_soon_as_possible = true parameter is additionally set. The courier starts from the depot at 06:00, waits for 1 hour and 7 minutes, and delivers the earliest order (order 5) at 08:00.

API request (JSON)API responseView on map

Trailers

A trailer is a truck with a trailer attached. Orders can be placed in both the truck and trailer. An order can be partially loaded in the truck and in the trailer.

To describe the trailer, use the trailer object.

Field

Description

trailer.сapacity

The trailer carrying capacity is described by the fields:

  • weight_kg: Capacity in kilograms.
  • units: Capacity in units.

It's added to the truck carrying capacity.

trailer.capacity.custom

The trailer carrying capacity in custom units of measurement.

It's set in the "name": size format, where:

  • name: The custom unit name (set instead of the additionalProperties field).
  • size: The vehicle carrying capacity in appropriate units, expressed as a non-negative real number.

If the unit name is present in deliveries or other vehicles, but not in this one, the capacity is assumed to be unlimited.

trailer.capacity.limits

The trailer loading limit is described by the fields:

  • units_perc: In custom units, expressed as a percentage of the trailer capacity.
  • volume_perc: As a percentage of the trailer volume.
  • weight_perc: As a percentage of the maximum allowed total weight the trailer can carry.

trailer.capacity.volume

The trailer dimensions are described by the fields:

  • depth_m*: Depth.
  • height_m*: Height.
  • width_m*: Width.

The values are expressed in meters.

*: Required parameter

trailer.capacity.volume_cbm

Trailer volume in cubic meters.

If the trailer has both the size and volume specified, then trailer.capacity.volume defines the dimensions and trailer.capacity.volume_cbm defines the carrying capacity.

trailer.max_capacity_difference

The maximum difference in the load of a trailer and a truck is described by the following fields:

  • units: How many more units of goods there can be in the trailer than in the truck.
  • volume_cbm: How many cubic meters the trailer load can be greater than the truck load.
  • weight_kg: How many kilograms the trailer load can be greater than the truck load.

If one of the fields is omitted, there are no restrictions on it.

There are physical limitations to the difference between the trailer load and the truck load. If the truck is empty and the trailer is heavy, the vehicle becomes uncontrollable and can't go.

trailer.max_capacity_difference.custom

The maximum permissible difference in the loaded number of custom units between the trailer and the truck.

The permissible difference in custom units is set in "name": size format where:

  • name: The custom unit name (set instead of the additionalProperties field).
  • size: The vehicle capacity in appropriate units, expressed as a non-negative real number.

If the unit name is present in deliveries or other vehicles, but not in this one, there is no restriction.

trailer.cost

The trailer cost is described by the fields:

  • fixed: The fixed cost-of-use for the trailer.
  • hour: The cost-of-use for the trailer per hour.
  • km: The cost per kilometer (added only for the route sections where a truck is traveling with a trailer).
  • location: The cost-of-use for the trailer per destination.
  • run: The cost per run from the depot to the destination.
  • tonne_km: The cost of transporting one ton per kilometer.

It's added to the truck cost if the trailer is used.

trailer.decoupling_time_s

Time to decouple the trailer in seconds.

Only for orders with the anchor and parking types (see Using a trailer).

trailer.coupling_time_s

Time to couple the trailer in seconds.

Only for orders with the anchor and parking types (see Using a trailer).

trailer.rolling_cost

Penalty for each rolling* of cargo from the trailer to the truck.

* Rolling means moving orders from a trailer to a truck to deliver them to non-accessible locations (See Using a trailer).

trailer.rolling_time

The time to roll cargo from a trailer to a truck is set in seconds. It can be described by the following fields:

  • fixed_time_s: Fixed time.
  • s_per_kg: Unloading time per kilogram of cargo.
  • s_per_m3: Unloading time per cubic meter of cargo.
  • s_per_unit: Unloading time per unit of cargo.

If multiple time types are specified, use the one that spends the most time. If no type is specified, use only the fixed time.

coupling_time_s and decoupling_time_s are added to the rolling time, because you need to first couple the trailer and then decouple it for rolling.

Sample trailer description in a request:

{
    "trailer": {
        "capacity": {
            "weight_kg": 10000,
            "units": 200
        },
        "max_capacity_difference": {
            "units": 10,
            "weight_kg": 0
        },
        "cost": {
            "fixed": 1000,
            "km": 10
        },
        "decoupling_time_s": 300,
        "coupling_time_s": 300,
        "rolling_time": {
            "fixed_time_s": 3000
        }
    }
}

For more information about using a truck with a trailer, see Using a trailer.

Minimum order weight for a vehicle

To prevent a heavy truck from carrying a single small order across town, use the min_stop_weight parameter. Using this parameter, you can specify the minimum total weight that a vehicle will deliver to a single location. This is relevant when planning freight transportation by vehicles of different capacity.

The restriction is soft: the algorithm may violate it. Penalties are set in the fields:

  • penalty.min_stop_weight.fixed: A penalty for violating the minimum total weight for all orders in a single location (1000 by default).

  • penalty.min_stop_weight.kg: A penalty for each kilogram that the total order weight delivered to a single location (50 by default) is short.

For more information about using vehicles with different capacity, see Transport with different load capacities.

Example 1

Two vehicles with capacities of 1500 and 10,000 kg deliver orders weighing 100, 500, 900, and 9000 kg. The large truck gets both light and heavy orders.

API request (JSON)API responseView on map

Example 2

For a vehicle with a capacity of 10,000 kg, the specified value of min_stop_weight is 1000. As a result, the larger vehicle only gets the order weighing 9000 kg.

API request (JSON)API responseView on map

Contact support