Replan routes dynamically
Tour Planning provides significant advantages for planning tours, whether for single vehicles or multiple vehicles. A particularly valuable feature is dynamic tour replanning, which enables drivers to make necessary adjustments (including adding stops, rescheduling, or updating arrival times) while executing a tour.
This capability, achieved through live optimization of the entire tour at the moment of change, is essential for handling unexpected obstacles like drivers running late, changing traffic conditions, or job cancellations during the tour. To implement replanning, the customer submits a new problem request to the API with updated parameters reflecting the current tour state.
Terminology
In this document, "customer" refers to the tour operator or system user who manages the tour planning operations. The customer is responsible for triggering replanning by submitting updated problem requests to the API.
The replanning process works as follows when the customer chooses to replan:
- Initial planning: A tour is optimized and planned, and the driver begins completing it.
- Driver progress: After completing the first job, the driver moves to the next location, according to the tour plan.
- Replanning trigger: When conditions change (for example, due to traffic delays, cancellations, and so on), the customer submits a new problem request to the API.
- Updated parameters: The new request uses the same problem structure, but with the following updates:
- The shift start location is set to the vehicle's current position
- The shift start time reflects the current time
- Already-completed jobs are removed from the job list
- The vehicle capacity reflects the remaining load
- New solution: The API returns an optimized solution for the remaining jobs based on the current conditions.
This approach ensures the driver always has an up-to-date, optimized route that is based on real-world conditions.
The following example demonstrates how replanning adapts to changing conditions during tour completion.
This example models a simple situation in which a vehicle with a capacity of 10 items needs to complete six jobs in different locations, one of which (Job_6) has high priority ("priority": 1), as visualized in the following figure:
The following section shows the full problem JSON that serves as the starting point:
{
"fleet": {
"types": [
{
"id": "Vehicle_1",
"profile": "car_1",
"costs": {
"fixed": 9.0,
"distance": 0.004,
"time": 0.005
},
"shifts": [
{
"start": {
"time": "2021-08-27T08:03:00Z",
"location": {
"lat": 52.530971,
"lng": 13.384915
}
},
"end": {
"time": "2021-08-27T18:03:00Z",
"location": {
"lat": 52.530971,
"lng": 13.384915
}
}
}
],
"capacity": [
10
],
"amount": 1
}
],
"profiles": [
{
"type": "car",
"name": "car_1"
}
]
},
"plan": {
"jobs": [
{
"id": "job_1",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T09:03:00Z",
"2021-08-27T18:03:00Z"
]
],
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372257
},
"duration": 360
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_2",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T11:03:00Z",
"2021-08-27T20:03:00Z"
]
],
"location": {
"lat": 52.43363386232821,
"lng": 13.403232562191313
},
"duration": 540
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_3",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T10:03:00Z",
"2021-08-27T16:03:00Z"
]
],
"location": {
"lat": 52.473321658918245,
"lng": 13.28972099097991
},
"duration": 660
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_4",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T10:03:00Z",
"2021-08-27T16:03:00Z"
]
],
"location": {
"lat": 52.503321,
"lng": 13.299720
},
"duration": 660
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_5",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T10:03:00Z",
"2021-08-27T16:03:00Z"
]
],
"location": {
"lat": 52.403321658918245,
"lng": 13.19972099097991
},
"duration": 660
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_6",
"priority": 1,
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T14:03:00Z",
"2021-08-27T17:03:00Z"
]
],
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"duration": 1140
}
],
"demand": [
1
]
}
]
}
}
]
}
}Initial optimization solution
After optimizing, the driver begins executing the tour. The initial solution specifies this job sequence:
flowchart LR
D0("Depot")
J2(("1<br/>Job_2"))
J5(("2<br/>Job_3"))
J3(("3<br/>Job_5"))
J4(("4<br/>Job_4"))
J1(("5<br/>Job_1"))
J6(("6<br/>Job_6"))
D0 ==> J2 ==> J5 ==> J3 ==> J4 ==> J1 ==> J6 ==> D0
classDef depot fill:#2adace,stroke:#2da0a6,stroke-width:5px,color:#000,font-weight:bold;
classDef stop fill:#2e9e58,stroke:#1f7a43,stroke-width:5px,color:#fff;
classDef priority fill:#f4c430,stroke:#1f7a43,stroke-width:5px,color:#111,font-weight:bold;
class D0 depot;
class J2,J5,J3,J4,J1 stop;
class J6 priority;
Note
Although
job_6has the highest priority, the driver completes it last because they have enough time to finish all jobs on schedule. Job priority doesn't dictate the order of the tour plan. Instead, it ensures that the most important jobs are included in the tour in the most optimized way. For more information see Prioritize jobs.
See the following section for the full solution JSON:
{
"statistic": {
"cost": 385.917,
"distance": 80348,
"duration": 11105,
"times": {
"driving": 7085,
"serving": 4020,
"waiting": 0,
"stopping": 0,
"break": 0,
"intraStop": 0
},
"intraStopDistance": 0
},
"tours": [
{
"vehicleId": "Vehicle_1_1",
"typeId": "Vehicle_1",
"stops": [
{
"time": {
"arrival": "2021-08-27T08:03:00Z",
"departure": "2021-08-27T11:21:07Z"
},
"load": [
6
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"start": "2021-08-27T08:03:00Z",
"end": "2021-08-27T11:21:07Z",
"arrival": "2021-08-27T08:03:00Z"
}
}
],
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"distance": 0
},
{
"time": {
"arrival": "2021-08-27T11:45:13Z",
"departure": "2021-08-27T11:54:13Z"
},
"load": [
5
],
"activities": [
{
"jobId": "job_2",
"type": "delivery",
"location": {
"lat": 52.43363386232821,
"lng": 13.403232562191311
},
"time": {
"start": "2021-08-27T11:45:13Z",
"end": "2021-08-27T11:54:13Z",
"arrival": "2021-08-27T11:45:13Z"
}
}
],
"location": {
"lat": 52.43363386232821,
"lng": 13.403232562191311
},
"distance": 13567
},
{
"time": {
"arrival": "2021-08-27T12:12:30Z",
"departure": "2021-08-27T12:23:30Z"
},
"load": [
4
],
"activities": [
{
"jobId": "job_3",
"type": "delivery",
"location": {
"lat": 52.473321658918245,
"lng": 13.28972099097991
},
"time": {
"start": "2021-08-27T12:12:30Z",
"end": "2021-08-27T12:23:30Z",
"arrival": "2021-08-27T12:12:30Z"
}
}
],
"location": {
"lat": 52.473321658918245,
"lng": 13.28972099097991
},
"distance": 27630
},
{
"time": {
"arrival": "2021-08-27T12:43:00Z",
"departure": "2021-08-27T12:54:00Z"
},
"load": [
3
],
"activities": [
{
"jobId": "job_5",
"type": "delivery",
"location": {
"lat": 52.40332165891824,
"lng": 13.19972099097991
},
"time": {
"start": "2021-08-27T12:43:00Z",
"end": "2021-08-27T12:54:00Z",
"arrival": "2021-08-27T12:43:00Z"
}
}
],
"location": {
"lat": 52.40332165891824,
"lng": 13.19972099097991
},
"distance": 40010
},
{
"time": {
"arrival": "2021-08-27T13:12:44Z",
"departure": "2021-08-27T13:23:44Z"
},
"load": [
2
],
"activities": [
{
"jobId": "job_4",
"type": "delivery",
"location": {
"lat": 52.503321,
"lng": 13.29972
},
"time": {
"start": "2021-08-27T13:12:44Z",
"end": "2021-08-27T13:23:44Z",
"arrival": "2021-08-27T13:12:44Z"
}
}
],
"location": {
"lat": 52.503321,
"lng": 13.29972
},
"distance": 57475
},
{
"time": {
"arrival": "2021-08-27T13:43:39Z",
"departure": "2021-08-27T13:49:39Z"
},
"load": [
1
],
"activities": [
{
"jobId": "job_1",
"type": "delivery",
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372255
},
"time": {
"start": "2021-08-27T13:43:39Z",
"end": "2021-08-27T13:49:39Z",
"arrival": "2021-08-27T13:43:39Z"
}
}
],
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372255
},
"distance": 70991
},
{
"time": {
"arrival": "2021-08-27T14:03:00Z",
"departure": "2021-08-27T14:22:00Z"
},
"load": [
0
],
"activities": [
{
"jobId": "job_6",
"type": "delivery",
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"time": {
"start": "2021-08-27T14:03:00Z",
"end": "2021-08-27T14:22:00Z",
"arrival": "2021-08-27T14:03:00Z"
}
}
],
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"distance": 78381
},
{
"time": {
"arrival": "2021-08-27T14:26:12Z",
"departure": "2021-08-27T14:26:12Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"start": "2021-08-27T14:26:12Z",
"end": "2021-08-27T14:26:12Z",
"arrival": "2021-08-27T14:26:12Z"
}
}
],
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"distance": 80348
}
],
"statistic": {
"cost": 385.917,
"distance": 80348,
"duration": 11105,
"times": {
"driving": 7085,
"serving": 4020,
"waiting": 0,
"stopping": 0,
"break": 0,
"intraStop": 0
},
"intraStopDistance": 0
},
"shiftIndex": 0
}
]
}After completing job_2, the customer must replan the route to account for current traffic conditions (see visualization). To start replanning, the customer submits a new problem request to the Tour Planning API with the following updates to the fleet configuration:
- Shift start location: Updated from the original depot to the driver's current location (the location of
job_2:52.43363386232821, 13.403232562191311) - Shift start time: Updated from the original start time (
08:03:00Z) to the current time when the driver departedjob_2(11:54:13Z). - Job list: Excludes
job_2, which has already been completed. - Vehicle capacity: Remains at [10] since
job_2delivered 1 item, but 5 items remain for delivery.
The following section shows the updated problem configuration:
{
"fleet": {
"types": [
{
"id": "Vehicle_1",
"profile": "car_1",
"costs": {
"fixed": 9.0,
"distance": 0.004,
"time": 0.005
},
"shifts": [
{
"start": {
"time": "2021-08-27T11:54:13Z",
"location": {
"lat": 52.43363386232821,
"lng": 13.403232562191311
}
},
"end": {
"time": "2021-08-27T18:03:00Z",
"location": {
"lat": 52.530971,
"lng": 13.384915
}
}
}
],
"capacity": [
10
],
"amount": 1
}
],
"profiles": [
{
"type": "car",
"name": "car_1"
}
]
},
"plan": {
"jobs": [
{
"id": "job_1",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T09:03:00Z",
"2021-08-27T18:03:00Z"
]
],
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372257
},
"duration": 360
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_3",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T10:03:00Z",
"2021-08-27T16:03:00Z"
]
],
"location": {
"lat": 52.473321658918245,
"lng": 13.28972099097991
},
"duration": 660
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_4",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T10:03:00Z",
"2021-08-27T16:03:00Z"
]
],
"location": {
"lat": 52.503321,
"lng": 13.299720
},
"duration": 660
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_5",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T10:03:00Z",
"2021-08-27T16:03:00Z"
]
],
"location": {
"lat": 52.403321658918245,
"lng": 13.19972099097991
},
"duration": 660
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_6",
"priority": 1,
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T14:03:00Z",
"2021-08-27T17:03:00Z"
]
],
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"duration": 1140
}
],
"demand": [
1
]
}
]
}
}
]
}
}The API returns a new optimized solution that sequences the remaining jobs as: job_3, job_5, job_4, job_1, and job_6, as visualized in the following chart:
flowchart LR
J2completed(("-<br>Job_2"))
D0("Depot")
J2("Job_2 lat/lon")
J3(("1<br/>Job_3"))
J5(("2<br/>Job_5"))
J4(("3<br/>Job_4"))
J1(("4<br/>Job_1"))
J6(("5<br/>Job_6"))
J2completed -. Completed<br>+ heavy traffic .-> J2 ==> J3 ==> J5 ==> J4 ==> J1 ==> J6 ==> D0
classDef depot fill:#2adace,stroke:#2da0a6,stroke-width:5px,color:#000,font-weight:bold;
classDef stop fill:#2e9e58,stroke:#1f7a43,stroke-width:5px,color:#fff;
classDef priority fill:#f4c430,stroke:#1f7a43,stroke-width:5px,color:#111,font-weight:bold;
classDef completed fill:#eeeeee,stroke:#cccccc,stroke-width:2px,color:#999;
class J2completed completed;
class D0,J2 depot;
class J5,J3,J4,J1 stop;
class J6 priority
The chart is showing the already-completed Job_2, which is not included in the tour plan.
See the following section for the full solution JSON:
{
"statistic": {
"cost": 321.71900000000005,
"distance": 66781,
"duration": 9119,
"times": {
"driving": 5639,
"serving": 3480,
"waiting": 0,
"stopping": 0,
"break": 0,
"intraStop": 0
},
"intraStopDistance": 0
},
"tours": [
{
"vehicleId": "Vehicle_1_1",
"typeId": "Vehicle_1",
"stops": [
{
"time": {
"arrival": "2021-08-27T11:54:13Z",
"departure": "2021-08-27T11:54:13Z"
},
"load": [
5
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.43363386232821,
"lng": 13.403232562191311
},
"time": {
"start": "2021-08-27T11:54:13Z",
"end": "2021-08-27T11:54:13Z",
"arrival": "2021-08-27T11:54:13Z"
}
}
],
"location": {
"lat": 52.43363386232821,
"lng": 13.403232562191311
},
"distance": 0
},
{
"time": {
"arrival": "2021-08-27T12:12:30Z",
"departure": "2021-08-27T12:23:30Z"
},
"load": [
4
],
"activities": [
{
"jobId": "job_3",
"type": "delivery",
"location": {
"lat": 52.473321658918245,
"lng": 13.28972099097991
},
"time": {
"start": "2021-08-27T12:12:30Z",
"end": "2021-08-27T12:23:30Z",
"arrival": "2021-08-27T12:12:30Z"
}
}
],
"location": {
"lat": 52.473321658918245,
"lng": 13.28972099097991
},
"distance": 14063
},
{
"time": {
"arrival": "2021-08-27T12:43:00Z",
"departure": "2021-08-27T12:54:00Z"
},
"load": [
3
],
"activities": [
{
"jobId": "job_5",
"type": "delivery",
"location": {
"lat": 52.40332165891824,
"lng": 13.19972099097991
},
"time": {
"start": "2021-08-27T12:43:00Z",
"end": "2021-08-27T12:54:00Z",
"arrival": "2021-08-27T12:43:00Z"
}
}
],
"location": {
"lat": 52.40332165891824,
"lng": 13.19972099097991
},
"distance": 26443
},
{
"time": {
"arrival": "2021-08-27T13:12:44Z",
"departure": "2021-08-27T13:23:44Z"
},
"load": [
2
],
"activities": [
{
"jobId": "job_4",
"type": "delivery",
"location": {
"lat": 52.503321,
"lng": 13.29972
},
"time": {
"start": "2021-08-27T13:12:44Z",
"end": "2021-08-27T13:23:44Z",
"arrival": "2021-08-27T13:12:44Z"
}
}
],
"location": {
"lat": 52.503321,
"lng": 13.29972
},
"distance": 43908
},
{
"time": {
"arrival": "2021-08-27T13:43:39Z",
"departure": "2021-08-27T13:49:39Z"
},
"load": [
1
],
"activities": [
{
"jobId": "job_1",
"type": "delivery",
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372255
},
"time": {
"start": "2021-08-27T13:43:39Z",
"end": "2021-08-27T13:49:39Z",
"arrival": "2021-08-27T13:43:39Z"
}
}
],
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372255
},
"distance": 57424
},
{
"time": {
"arrival": "2021-08-27T14:03:00Z",
"departure": "2021-08-27T14:22:00Z"
},
"load": [
0
],
"activities": [
{
"jobId": "job_6",
"type": "delivery",
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"time": {
"start": "2021-08-27T14:03:00Z",
"end": "2021-08-27T14:22:00Z",
"arrival": "2021-08-27T14:03:00Z"
}
}
],
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"distance": 64814
},
{
"time": {
"arrival": "2021-08-27T14:26:12Z",
"departure": "2021-08-27T14:26:12Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"start": "2021-08-27T14:26:12Z",
"end": "2021-08-27T14:26:12Z",
"arrival": "2021-08-27T14:26:12Z"
}
}
],
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"distance": 66781
}
],
"statistic": {
"cost": 321.71900000000005,
"distance": 66781,
"duration": 9119,
"times": {
"driving": 5639,
"serving": 3480,
"waiting": 0,
"stopping": 0,
"break": 0,
"intraStop": 0
},
"intraStopDistance": 0
},
"shiftIndex": 0
}
]
}Because the delivery recipient was unavailable when completing job_3, direct package delivery could not be completed at the specified address (see visualization). Instead, the driver delivered the items to a nearby pick-up location, which necessitated an extension of the operational time. The customer must therefore submit a new problem request to recalculate the tour while accounting for these new constraints: a modification in drop-off location and an expected delay in departure time.
Based on that, the current problem constraints are defined as follows:
- Shift start location: Updated from the original job coordinates to the temporary pick-up location for
Job_3(52.4696,13.30432). - Shift start time: The departure time is adjusted to reflect the actual completion of the previous task and the subsequent delay (updated to
T12:38:38Z). - Remaining jobs: The optimization engine must now re-sequence all remaining undelivered jobs (
job_5,job_4,job_1, andjob_6) based on this new temporal and spatial starting point.
See the following section for the full updated problem file:
{
"fleet": {
"types": [
{
"id": "Vehicle_1",
"profile": "car_1",
"costs": {
"fixed": 9,
"distance": 0.004,
"time": 0.005
},
"shifts": [
{
"start": {
"time": "2021-08-27T12:38:38Z",
"location": {
"lat": 52.4696,
"lng": 13.30432
}
},
"end": {
"time": "2021-08-27T18:03:00Z",
"location": {
"lat": 52.530971,
"lng": 13.384915
}
}
}
],
"capacity": [
10
],
"amount": 1
}
],
"profiles": [
{
"type": "car",
"name": "car_1"
}
]
},
"plan": {
"jobs": [
{
"id": "job_1",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T09:03:00Z",
"2021-08-27T18:03:00Z"
]
],
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372257
},
"duration": 360
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_4",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T10:03:00Z",
"2021-08-27T16:03:00Z"
]
],
"location": {
"lat": 52.503321,
"lng": 13.29972
},
"duration": 660
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_5",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T10:03:00Z",
"2021-08-27T16:03:00Z"
]
],
"location": {
"lat": 52.403321658918244,
"lng": 13.19972099097991
},
"duration": 660
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_6",
"priority": 1,
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T14:03:00Z",
"2021-08-27T17:03:00Z"
]
],
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"duration": 1140
}
],
"demand": [
1
]
}
]
}
}
]
}
}The updated solution sequences the remaining jobs as: job_5, job_4, job_1, job_6, as visualized in the following chart in addition to the already completed Job_2 and Job_3:
flowchart LR
J2completed(("-<br>Job_2"))
J3completed(("-<br>Job_3"))
D0("Depot")
J3("Backup location for Job_3")
J5(("1<br/>Job_5"))
J4(("2<br/>Job_4"))
J1(("3<br/>Job_1"))
J6(("4<br/>Job_6"))
J2completed -. Completed<br>+ heavy traffic .-> J3completed -. Completed<br> at backup location .-> J3 ==> J5 ==> J4 ==> J1 ==> J6 ==> D0
classDef depot fill:#2adace,stroke:#2da0a6,stroke-width:5px,color:#000,font-weight:bold;
classDef stop fill:#2e9e58,stroke:#1f7a43,stroke-width:5px,color:#fff;
classDef priority fill:#f4c430,stroke:#1f7a43,stroke-width:5px,color:#111,font-weight:bold;
classDef completed fill:#eeeeee,stroke:#cccccc,stroke-width:2px,color:#999;
class J3completed,J2completed completed;
class D0,J3 depot;
class J5,J4,J1 stop;
class J6 priority
To view the full solution, see the following section:
{
"statistic": {
"cost": 258.379,
"distance": 53066,
"duration": 7423,
"times": {
"driving": 4603,
"serving": 2820,
"waiting": 0,
"stopping": 0,
"break": 0,
"intraStop": 0
},
"intraStopDistance": 0
},
"tours": [
{
"vehicleId": "Vehicle_1_1",
"typeId": "Vehicle_1",
"stops": [
{
"time": {
"arrival": "2021-08-27T12:38:38Z",
"departure": "2021-08-27T12:38:38Z"
},
"load": [
4
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.4696,
"lng": 13.30432
},
"time": {
"start": "2021-08-27T12:38:38Z",
"end": "2021-08-27T12:38:38Z",
"arrival": "2021-08-27T12:38:38Z"
}
}
],
"location": {
"lat": 52.4696,
"lng": 13.30432
},
"distance": 0
},
{
"time": {
"arrival": "2021-08-27T12:59:09Z",
"departure": "2021-08-27T13:10:09Z"
},
"load": [
3
],
"activities": [
{
"jobId": "job_5",
"type": "delivery",
"location": {
"lat": 52.40332165891824,
"lng": 13.19972099097991
},
"time": {
"start": "2021-08-27T12:59:09Z",
"end": "2021-08-27T13:10:09Z",
"arrival": "2021-08-27T12:59:09Z"
}
}
],
"location": {
"lat": 52.40332165891824,
"lng": 13.19972099097991
},
"distance": 12728
},
{
"time": {
"arrival": "2021-08-27T13:28:53Z",
"departure": "2021-08-27T13:39:53Z"
},
"load": [
2
],
"activities": [
{
"jobId": "job_4",
"type": "delivery",
"location": {
"lat": 52.503321,
"lng": 13.29972
},
"time": {
"start": "2021-08-27T13:28:53Z",
"end": "2021-08-27T13:39:53Z",
"arrival": "2021-08-27T13:28:53Z"
}
}
],
"location": {
"lat": 52.503321,
"lng": 13.29972
},
"distance": 30193
},
{
"time": {
"arrival": "2021-08-27T13:59:48Z",
"departure": "2021-08-27T14:05:48Z"
},
"load": [
1
],
"activities": [
{
"jobId": "job_1",
"type": "delivery",
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372255
},
"time": {
"start": "2021-08-27T13:59:48Z",
"end": "2021-08-27T14:05:48Z",
"arrival": "2021-08-27T13:59:48Z"
}
}
],
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372255
},
"distance": 43709
},
{
"time": {
"arrival": "2021-08-27T14:19:09Z",
"departure": "2021-08-27T14:38:09Z"
},
"load": [
0
],
"activities": [
{
"jobId": "job_6",
"type": "delivery",
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"time": {
"start": "2021-08-27T14:19:09Z",
"end": "2021-08-27T14:38:09Z",
"arrival": "2021-08-27T14:19:09Z"
}
}
],
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"distance": 51099
},
{
"time": {
"arrival": "2021-08-27T14:42:21Z",
"departure": "2021-08-27T14:42:21Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"start": "2021-08-27T14:42:21Z",
"end": "2021-08-27T14:42:21Z",
"arrival": "2021-08-27T14:42:21Z"
}
}
],
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"distance": 53066
}
],
"statistic": {
"cost": 258.379,
"distance": 53066,
"duration": 7423,
"times": {
"driving": 4603,
"serving": 2820,
"waiting": 0,
"stopping": 0,
"break": 0,
"intraStop": 0
},
"intraStopDistance": 0
},
"shiftIndex": 0
}
]
}Assume traffic changes or vehicle issues delay completion of the remaining jobs (see visualization). As a result, the driver arrives significantly late to complete job_5. Although the expected completion time was 13:10, the driver completes it at T16:15:00Z.
Based on the scenario described in this step, the customer submits a new problem request with the following changes to the problem configuration:
- Shift start time update: The shift start time was updated to reflect the actual completion time of
job_3(T16:15:00Z). - New shift start location: As
job_5was successfully completed despite the delay, its location constraints were incorporated into the updated problem as the new starting point for replanning. - Remaining jobs: The optimization engine must now re-sequence all remaining undelivered jobs (
job_4,job_1, andjob_6) based on this new temporal and spatial starting point.
For the full updated problem JSON, see the following section:
{
"fleet": {
"types": [
{
"id": "Vehicle_1",
"profile": "car_1",
"costs": {
"fixed": 9.0,
"distance": 0.004,
"time": 0.005
},
"shifts": [
{
"start": {
"time": "2021-08-27T16:15:00Z",
"location": {
"lat": 52.403321658918245,
"lng": 13.19972099097991
}
},
"end": {
"time": "2021-08-27T18:03:00Z",
"location": {
"lat": 52.530971,
"lng": 13.384915
}
}
}
],
"capacity": [
10
],
"amount": 1
}
],
"profiles": [
{
"type": "car",
"name": "car_1"
}
]
},
"plan": {
"jobs": [
{
"id": "job_1",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T09:03:00Z",
"2021-08-27T18:03:00Z"
]
],
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372257
},
"duration": 360
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_4",
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T10:03:00Z",
"2021-08-27T16:03:00Z"
]
],
"location": {
"lat": 52.503321,
"lng": 13.299720
},
"duration": 660
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_6",
"priority": 1,
"tasks": {
"deliveries": [
{
"places": [
{
"times": [
[
"2021-08-27T14:03:00Z",
"2021-08-27T17:03:00Z"
]
],
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"duration": 1140
}
],
"demand": [
1
]
}
]
}
}
]
}
}This replanning demonstrates the power of dynamic optimization. Because the delay makes it impossible to complete both remaining low-priority jobs within their time windows, the API prioritizes job_6 (priority 1) over job_4 (no priority).
The following chart shows the updated tour sequence, with the already completed jobs and Job_4 marked as unassigned:
flowchart LR
J2completed(("-<br>Job_2"))
J3completed(("-<br>Job_3"))
J5completed(("-<br>Job_5"))
D0("Depot")
J5("Job_5 lat/lon")
J4(("!<br/>Job_4"))
J1(("2<br/>Job_1"))
J6(("1<br/>Job_6"))
J2completed -. Completed<br>+ heavy traffic .-> J3completed -. Completed<br> at backup location .-> J5completed -. Completed<br>+ severe delay .-> J5 ==> J6 ==> J1 ==> D0
classDef depot fill:#2adace,stroke:#2da0a6,stroke-width:5px,color:#000,font-weight:bold;
classDef stop fill:#2e9e58,stroke:#1f7a43,stroke-width:5px,color:#fff;
classDef priority fill:#f4c430,stroke:#1f7a43,stroke-width:5px,color:#111,font-weight:bold;
classDef completed fill:#eeeeee,stroke:#cccccc,stroke-width:2px,color:#999;
classDef unassigned fill:#a52520,stroke:#9298b3,stroke-width:5px,color:#111,font-weight:bold;
class J2completed,J5completed,J3completed completed;
class D0,J5 depot;
class J1 stop;
class J6 priority;
class J4 unassigned;
The following section provides the full solution JSON:
{
"statistic": {
"cost": 201.655,
"distance": 42000,
"duration": 4931,
"times": {
"driving": 3431,
"serving": 1500,
"waiting": 0,
"stopping": 0,
"break": 0,
"intraStop": 0
},
"intraStopDistance": 0
},
"tours": [
{
"vehicleId": "Vehicle_1_1",
"typeId": "Vehicle_1",
"stops": [
{
"time": {
"arrival": "2021-08-27T16:15:00Z",
"departure": "2021-08-27T16:15:00Z"
},
"load": [
2
],
"activities": [
{
"jobId": "departure",
"type": "departure",
"location": {
"lat": 52.40332165891824,
"lng": 13.19972099097991
},
"time": {
"start": "2021-08-27T16:15:00Z",
"end": "2021-08-27T16:15:00Z",
"arrival": "2021-08-27T16:15:00Z"
}
}
],
"location": {
"lat": 52.40332165891824,
"lng": 13.19972099097991
},
"distance": 0
},
{
"time": {
"arrival": "2021-08-27T16:42:28Z",
"departure": "2021-08-27T17:01:28Z"
},
"load": [
1
],
"activities": [
{
"jobId": "job_6",
"type": "delivery",
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"time": {
"start": "2021-08-27T16:42:28Z",
"end": "2021-08-27T17:01:28Z",
"arrival": "2021-08-27T16:42:28Z"
}
}
],
"location": {
"lat": 52.54165532725351,
"lng": 13.365047170290309
},
"distance": 25315
},
{
"time": {
"arrival": "2021-08-27T17:14:23Z",
"departure": "2021-08-27T17:20:23Z"
},
"load": [
0
],
"activities": [
{
"jobId": "job_1",
"type": "delivery",
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372255
},
"time": {
"start": "2021-08-27T17:14:23Z",
"end": "2021-08-27T17:20:23Z",
"arrival": "2021-08-27T17:14:23Z"
}
}
],
"location": {
"lat": 52.59175589353722,
"lng": 13.350747750372255
},
"distance": 32982
},
{
"time": {
"arrival": "2021-08-27T17:37:11Z",
"departure": "2021-08-27T17:37:11Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival",
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"start": "2021-08-27T17:37:11Z",
"end": "2021-08-27T17:37:11Z",
"arrival": "2021-08-27T17:37:11Z"
}
}
],
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"distance": 42000
}
],
"statistic": {
"cost": 201.655,
"distance": 42000,
"duration": 4931,
"times": {
"driving": 3431,
"serving": 1500,
"waiting": 0,
"stopping": 0,
"break": 0,
"intraStop": 0
},
"intraStopDistance": 0
},
"shiftIndex": 0
}
],
"unassigned": [
{
"jobId": "job_4",
"reasons": [
{
"code": "TIME_WINDOW_CONSTRAINT",
"description": "cannot be assigned due to violation of time window"
}
]
}
],
"notices": [
{
"code": "jobHasIncorrectTimeWindows",
"title": "Job job_4 has time window(s) not supported by any vehicle",
"action": "Check whether the job or vehicles have correct time windows"
}
]
}The solution contains the information about the infraction that caused the Job_4 to get unassigned as well as the suggested troubleshooting steps:
"unassigned": [
{
"jobId": "job_4",
"reasons": [
{
"code": "TIME_WINDOW_CONSTRAINT",
"description": "cannot be assigned due to violation of time window"
}
]
}
],
"notices": [
{
"code": "jobHasIncorrectTimeWindows",
"title": "Job job_4 has time window(s) not supported by any vehicle",
"action": "Check whether the job or vehicles have correct time windows"
}
]Summary
The following steps illustrate the dynamic replanning process, based on the presented use case:
Initial replanning phases
Replanning under constraints
This example demonstrates how dynamic replanning works with the Tour Planning API:
- Replanning is triggered when the customer submits a new problem request to the API. Replanning does not happen through automatic background processes.
- Each replanning request requires the customer to update key parameters:
- Shift start location: Set to the vehicle's current position
- Shift start time: Set to the current time
- Job list: Excludes already-completed jobs
- Vehicle capacity: Reflects remaining load after completed deliveries
- The API returns a fresh optimization based on the updated constraints, enabling the customer to adapt to:
- Traffic changes
- Delivery issues
- Significant delays
- Job cancellations
Dynamic replanning empowers customers to ensure drivers always have an optimal route for their remaining jobs, adapting to real-world conditions as they arise.
Next steps
- To learn more about how to influence the optimization algorithm during replanning, see Use relations to replan tours.
- For in-depth analysis of job unassignment scenarios and constraint violations, see Troubleshoot unassigned jobs.



