Control job task position

The job task position feature allows you to directly set the serving order of jobs inside a tour. When using this feature, you can assign the desired position value to the tasks in the problem, and in this way, influence the order of serving them in the solution.

Consider a scenario where a logistics company needs to manage a fleet of delivery trucks. By determining the position of assignments, the company can ensure that the trucks complete all deliveries before any making any pickups. This prevents the trucks from carrying unnecessary load, improving fuel efficiency and reducing delivery times.

You have the option to specify the position of pickup or delivery assignments within the tour using the position object, which offers the values first, ordered, any, and last. This flexibility enables tailored prioritization based on factors such as urgency, proximity, and resource availability.

📘

Note

The order property is now deprecated in favor of the position property.
For more information, see the API Reference.

The following list provides an overview of how you can control the placement of specific tasks within a tour through the position option:

  • first: Assignments marked as first are addressed at the start of the tour, immediately after departure, and before tackling assignments with a specified position value, those without a defined position, or those with the position value set to any or last. In cases where multiple assignments are positioned as first, the API prioritizes them by determining the most efficient route between such assignments before moving on to subsequent tasks.

  • ordered: Assignments labeled as ordered must include an associated position value, represented by an integer equal to or greater than 1. Within the API, these assignments are prioritized lower than those marked as first, but higher than unordered tasks or those designated as last. When multiple assignments are positioned as 3, the API prioritizes them by determining the most efficient route between such assignments before moving on to subsequent tasks. You can also include the priority attribute to specify that some jobs are more desirable to be included in the tour than others. For more information, see Prioritize jobs.

  • assignments without a position value: Assignments that do not have any position assigned are handled after the assignments labeled as first and ordered and before the assignments labeled as last.

  • any: This designation permits the API to position assignments within the tour anywhere between first and last assignments. This flexibility allows assignments with this position value to be scattered among position.type: "ordered" and unordered assignments, optimizing the route for cost and time efficiency.

  • last: If an assignment is labeled with the last position, the API prioritizes it as the final task to be handled after all other assignments, regardless of their position values. When multiple assignments are positioned as last, the API prioritizes between them by determining the most efficient route.

Based on the previous overview, the following figure provides a visual representation of how the API positions tasks within the tour, depending on the task's position value:

Order of tasks within a tour based on the position value

The following comparison shows how to configure a job task within a problem depending whether the position.type is ordered or first/last/any:

ordered

{
  "id": "Job_X",
  "tasks": {
    "pickups": [
      {
        "places": [
          {
            "location": {
              "lat": 52.457629,
              "lng": 13.323742
            },
            "duration": 600
          }
        ],
        "demand": [
          1
        ],
        "position": {
          "type": "ordered",
          "value": 1
        }
      }
    ]
  }
}

first/last/any

{
  "id": "Job_X",
  "tasks": {
    "pickups": [
      {
        "places": [
          {
            "location": {
              "lat": 52.458232,
              "lng": 13.338698
            },
            "duration": 600
          }
        ],
        "demand": [
          1
        ],
        "position": {
          "type": "first"
        }
      }
    ]
  }
}

To illustrate how the position of tasks within a tour can vary based on different position settings, the following sections present a range of sample problem and solution files. The samples begin with a tour having no positioned tasks, then different position settings are progressively introduced, initially as hard constraints, and finally, the position is configured as a soft constraint. In addition, this tutorial provides additional scenarios that combine task position with other features like time windows and relations to further facilitate your understanding of the feature.

Problem: Tasks without a position

The following problem JSON serves as a starting point and does not contain positioned tasks:

Click to expand/collapse the sample JSON
  {
    "fleet": {
      "types": [
        {
          "id": "small",
          "profile": "car",
          "costs": {
            "fixed": 20,
            "distance": 0,
            "time": 0.005
          },
          "shifts": [
            {
              "start": {
                "time": "2023-05-28T08:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              },
              "end": {
                "time": "2023-05-28T16:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              }
            }
          ],
          "capacity": [
            100
          ],
          "amount": 10
        }
      ],
      "profiles": [
        {
          "type": "car",
          "name": "car"
        }
      ]
    },
    "plan": {
      "jobs": [
        {
          "id": "Job_1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.56182,
                      "lng": 13.497167
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_2",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.534553,
                      "lng": 13.519429
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_3",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.482275,
                      "lng": 13.502456
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_4",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.473537,
                      "lng": 13.505414
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_5",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.463341,
                      "lng": 13.49061
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_6",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.434003,
                      "lng": 13.466142
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_7",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.447476,
                      "lng": 13.433062
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_8",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458414,
                      "lng": 13.392079
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_9",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.446407,
                      "lng": 13.36047
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_10",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.440807,
                      "lng": 13.351399
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_11",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.441913,
                      "lng": 13.339028
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_12",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458232,
                      "lng": 13.338698
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_13",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.457629,
                      "lng": 13.323742
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        }
      ]
    }
  }

Solution

As demonstrated in the following visualization, the optimization algorithm determined the most optimal solution for the tour based on no additional position constraint:

A tour with no position constraint

The following section contains the full solution JSON file:

Click to expand/collapse the sample JSON
  {
    "statistic": {
      "cost": 91.16499999999999,
      "distance": 60330,
      "duration": 14233,
      "times": {
        "driving": 6433,
        "serving": 7800,
        "waiting": 0,
        "stopping": 0,
        "break": 0
      }
    },
    "tours": [
      {
        "vehicleId": "small_8",
        "typeId": "small",
        "stops": [
          {
            "time": {
              "arrival": "2023-05-28T08:00:00Z",
              "departure": "2023-05-28T08:00:00Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "departure",
                "type": "departure",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T08:00:00Z",
                  "end": "2023-05-28T08:00:00Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 0
          },
          {
            "time": {
              "arrival": "2023-05-28T08:19:39Z",
              "departure": "2023-05-28T08:29:39Z"
            },
            "load": [
              1
            ],
            "activities": [
              {
                "jobId": "Job_1",
                "type": "pickup",
                "location": {
                  "lat": 52.56182,
                  "lng": 13.497167
                },
                "time": {
                  "start": "2023-05-28T08:19:39Z",
                  "end": "2023-05-28T08:29:39Z"
                }
              }
            ],
            "location": {
              "lat": 52.56182,
              "lng": 13.497167
            },
            "distance": 9971
          },
          {
            "time": {
              "arrival": "2023-05-28T08:36:16Z",
              "departure": "2023-05-28T08:46:16Z"
            },
            "load": [
              2
            ],
            "activities": [
              {
                "jobId": "Job_2",
                "type": "pickup",
                "location": {
                  "lat": 52.534553,
                  "lng": 13.519429
                },
                "time": {
                  "start": "2023-05-28T08:36:16Z",
                  "end": "2023-05-28T08:46:16Z"
                }
              }
            ],
            "location": {
              "lat": 52.534553,
              "lng": 13.519429
            },
            "distance": 13720
          },
          {
            "time": {
              "arrival": "2023-05-28T09:00:18Z",
              "departure": "2023-05-28T09:10:18Z"
            },
            "load": [
              3
            ],
            "activities": [
              {
                "jobId": "Job_3",
                "type": "pickup",
                "location": {
                  "lat": 52.482275,
                  "lng": 13.502456
                },
                "time": {
                  "start": "2023-05-28T09:00:18Z",
                  "end": "2023-05-28T09:10:18Z"
                }
              }
            ],
            "location": {
              "lat": 52.482275,
              "lng": 13.502456
            },
            "distance": 22500
          },
          {
            "time": {
              "arrival": "2023-05-28T09:12:08Z",
              "departure": "2023-05-28T09:22:08Z"
            },
            "load": [
              4
            ],
            "activities": [
              {
                "jobId": "Job_4",
                "type": "pickup",
                "location": {
                  "lat": 52.473537,
                  "lng": 13.505414
                },
                "time": {
                  "start": "2023-05-28T09:12:08Z",
                  "end": "2023-05-28T09:22:08Z"
                }
              }
            ],
            "location": {
              "lat": 52.473537,
              "lng": 13.505414
            },
            "distance": 23786
          },
          {
            "time": {
              "arrival": "2023-05-28T09:28:08Z",
              "departure": "2023-05-28T09:38:08Z"
            },
            "load": [
              5
            ],
            "activities": [
              {
                "jobId": "Job_5",
                "type": "pickup",
                "location": {
                  "lat": 52.463341,
                  "lng": 13.49061
                },
                "time": {
                  "start": "2023-05-28T09:28:08Z",
                  "end": "2023-05-28T09:38:08Z"
                }
              }
            ],
            "location": {
              "lat": 52.463341,
              "lng": 13.49061
            },
            "distance": 26727
          },
          {
            "time": {
              "arrival": "2023-05-28T09:47:07Z",
              "departure": "2023-05-28T09:57:07Z"
            },
            "load": [
              6
            ],
            "activities": [
              {
                "jobId": "Job_6",
                "type": "pickup",
                "location": {
                  "lat": 52.434003,
                  "lng": 13.466142
                },
                "time": {
                  "start": "2023-05-28T09:47:07Z",
                  "end": "2023-05-28T09:57:07Z"
                }
              }
            ],
            "location": {
              "lat": 52.434003,
              "lng": 13.466142
            },
            "distance": 32034
          },
          {
            "time": {
              "arrival": "2023-05-28T10:02:58Z",
              "departure": "2023-05-28T10:12:58Z"
            },
            "load": [
              7
            ],
            "activities": [
              {
                "jobId": "Job_7",
                "type": "pickup",
                "location": {
                  "lat": 52.447476,
                  "lng": 13.433062
                },
                "time": {
                  "start": "2023-05-28T10:02:58Z",
                  "end": "2023-05-28T10:12:58Z"
                }
              }
            ],
            "location": {
              "lat": 52.447476,
              "lng": 13.433062
            },
            "distance": 35564
          },
          {
            "time": {
              "arrival": "2023-05-28T10:19:25Z",
              "departure": "2023-05-28T10:29:25Z"
            },
            "load": [
              8
            ],
            "activities": [
              {
                "jobId": "Job_8",
                "type": "pickup",
                "location": {
                  "lat": 52.458414,
                  "lng": 13.392079
                },
                "time": {
                  "start": "2023-05-28T10:19:25Z",
                  "end": "2023-05-28T10:29:25Z"
                }
              }
            ],
            "location": {
              "lat": 52.458414,
              "lng": 13.392079
            },
            "distance": 39588
          },
          {
            "time": {
              "arrival": "2023-05-28T10:34:40Z",
              "departure": "2023-05-28T10:44:40Z"
            },
            "load": [
              9
            ],
            "activities": [
              {
                "jobId": "Job_9",
                "type": "pickup",
                "location": {
                  "lat": 52.446407,
                  "lng": 13.36047
                },
                "time": {
                  "start": "2023-05-28T10:34:40Z",
                  "end": "2023-05-28T10:44:40Z"
                }
              }
            ],
            "location": {
              "lat": 52.446407,
              "lng": 13.36047
            },
            "distance": 42402
          },
          {
            "time": {
              "arrival": "2023-05-28T10:48:10Z",
              "departure": "2023-05-28T10:58:10Z"
            },
            "load": [
              10
            ],
            "activities": [
              {
                "jobId": "Job_10",
                "type": "pickup",
                "location": {
                  "lat": 52.440807,
                  "lng": 13.351399
                },
                "time": {
                  "start": "2023-05-28T10:48:10Z",
                  "end": "2023-05-28T10:58:10Z"
                }
              }
            ],
            "location": {
              "lat": 52.440807,
              "lng": 13.351399
            },
            "distance": 43986
          },
          {
            "time": {
              "arrival": "2023-05-28T11:01:31Z",
              "departure": "2023-05-28T11:11:31Z"
            },
            "load": [
              11
            ],
            "activities": [
              {
                "jobId": "Job_11",
                "type": "pickup",
                "location": {
                  "lat": 52.441913,
                  "lng": 13.339028
                },
                "time": {
                  "start": "2023-05-28T11:01:31Z",
                  "end": "2023-05-28T11:11:31Z"
                }
              }
            ],
            "location": {
              "lat": 52.441913,
              "lng": 13.339028
            },
            "distance": 45585
          },
          {
            "time": {
              "arrival": "2023-05-28T11:17:21Z",
              "departure": "2023-05-28T11:27:21Z"
            },
            "load": [
              12
            ],
            "activities": [
              {
                "jobId": "Job_13",
                "type": "pickup",
                "location": {
                  "lat": 52.457629,
                  "lng": 13.323742
                },
                "time": {
                  "start": "2023-05-28T11:17:21Z",
                  "end": "2023-05-28T11:27:21Z"
                }
              }
            ],
            "location": {
              "lat": 52.457629,
              "lng": 13.323742
            },
            "distance": 48285
          },
          {
            "time": {
              "arrival": "2023-05-28T11:29:52Z",
              "departure": "2023-05-28T11:39:52Z"
            },
            "load": [
              13
            ],
            "activities": [
              {
                "jobId": "Job_12",
                "type": "pickup",
                "location": {
                  "lat": 52.458232,
                  "lng": 13.338698
                },
                "time": {
                  "start": "2023-05-28T11:29:52Z",
                  "end": "2023-05-28T11:39:52Z"
                }
              }
            ],
            "location": {
              "lat": 52.458232,
              "lng": 13.338698
            },
            "distance": 49439
          },
          {
            "time": {
              "arrival": "2023-05-28T11:57:13Z",
              "departure": "2023-05-28T11:57:13Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "arrival",
                "type": "arrival",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T11:57:13Z",
                  "end": "2023-05-28T11:57:13Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 60330
          }
        ],
        "statistic": {
          "cost": 91.16499999999999,
          "distance": 60330,
          "duration": 14233,
          "times": {
            "driving": 6433,
            "serving": 7800,
            "waiting": 0,
            "stopping": 0,
            "break": 0
          }
        },
        "shiftIndex": 0
      }
    ]
  }

Problem: Positioned tasks

The following problem involves a tour plan similar to the previous example. However, in this instance, most tasks have a specific position designated by the position.type: "ordered" property and its corresponding value. The exceptions to this are Job_1, Job_2, and Job_13, which have no specified position.

To simplify identifying positioned tasks, the position is also reflected in the ID, such as Job_3_position-10.

Click to expand/collapse the sample JSON
  {
    "fleet": {
      "types": [
        {
          "id": "small",
          "profile": "car",
          "costs": {
            "fixed": 20,
            "distance": 0,
            "time": 0.005
          },
          "shifts": [
            {
              "start": {
                "time": "2023-05-28T08:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              },
              "end": {
                "time": "2023-05-28T16:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              }
            }
          ],
          "capacity": [
            20
          ],
          "amount": 1
        }
      ],
      "profiles": [
        {
          "type": "car",
          "name": "car"
        }
      ]
    },
    "plan": {
      "jobs": [
        {
          "id": "Job_1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.56182,
                      "lng": 13.497167
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_2",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.534553,
                      "lng": 13.519429
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        },
        {
          "id": "Job_3_position-10",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.482275,
                      "lng": 13.502456
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 10
                }
              }
            ]
          }
        },
        {
          "id": "Job_4_position-9",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.473537,
                      "lng": 13.505414
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 9
                }
              }
            ]
          }
        },
        {
          "id": "Job_5_position-8",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.463341,
                      "lng": 13.49061
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 8
                }
              }
            ]
          }
        },
        {
          "id": "Job_6_position-7",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.434003,
                      "lng": 13.466142
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 7
                }
              }
            ]
          }
        },
        {
          "id": "Job_7_position-6",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.447476,
                      "lng": 13.433062
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 6
                }
              }
            ]
          }
        },
        {
          "id": "Job_8_position-5",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458414,
                      "lng": 13.392079
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 5
                }
              }
            ]
          }
        },
        {
          "id": "Job_9_position-4",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.446407,
                      "lng": 13.36047
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 4
                }
              }
            ]
          }
        },
        {
          "id": "Job_10_position-3",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.440807,
                      "lng": 13.351399
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 3
                }
              }
            ]
          }
        },
        {
          "id": "Job_11_position-2",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.441913,
                      "lng": 13.339028
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "Job_12_position-1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458232,
                      "lng": 13.338698
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                }
              }
            ]
          }
        },
        {
          "id": "Job_13",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.457629,
                      "lng": 13.323742
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ]
              }
            ]
          }
        }
      ]
    }
  }

Solution

In this case, the optimization algorithm assigns positioned tasks first, placing tasks without a position at the end of the tour. For positioned tasks, a lower value indicates a higher priority.

A tour with positioned and unpositioned jobs

The following section contains the full solution JSON file:

Click to expand/collapse the sample JSON
  {
    "statistic": {
      "cost": 102.16,
      "distance": 84191,
      "duration": 16432,
      "times": {
        "driving": 8632,
        "serving": 7800,
        "waiting": 0,
        "stopping": 0,
        "break": 0
      }
    },
    "tours": [
      {
        "vehicleId": "small_1",
        "typeId": "small",
        "stops": [
          {
            "time": {
              "arrival": "2023-05-28T08:00:00Z",
              "departure": "2023-05-28T08:00:00Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "departure",
                "type": "departure",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T08:00:00Z",
                  "end": "2023-05-28T08:00:00Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 0
          },
          {
            "time": {
              "arrival": "2023-05-28T08:18:22Z",
              "departure": "2023-05-28T08:28:22Z"
            },
            "load": [
              1
            ],
            "activities": [
              {
                "jobId": "Job_12_position-1",
                "type": "pickup",
                "location": {
                  "lat": 52.458232,
                  "lng": 13.338698
                },
                "time": {
                  "start": "2023-05-28T08:18:22Z",
                  "end": "2023-05-28T08:28:22Z"
                }
              }
            ],
            "location": {
              "lat": 52.458232,
              "lng": 13.338698
            },
            "distance": 11325
          },
          {
            "time": {
              "arrival": "2023-05-28T08:32:24Z",
              "departure": "2023-05-28T08:42:24Z"
            },
            "load": [
              2
            ],
            "activities": [
              {
                "jobId": "Job_11_position-2",
                "type": "pickup",
                "location": {
                  "lat": 52.441913,
                  "lng": 13.339028
                },
                "time": {
                  "start": "2023-05-28T08:32:24Z",
                  "end": "2023-05-28T08:42:24Z"
                }
              }
            ],
            "location": {
              "lat": 52.441913,
              "lng": 13.339028
            },
            "distance": 13451
          },
          {
            "time": {
              "arrival": "2023-05-28T08:45:55Z",
              "departure": "2023-05-28T08:55:55Z"
            },
            "load": [
              3
            ],
            "activities": [
              {
                "jobId": "Job_10_position-3",
                "type": "pickup",
                "location": {
                  "lat": 52.440807,
                  "lng": 13.351399
                },
                "time": {
                  "start": "2023-05-28T08:45:55Z",
                  "end": "2023-05-28T08:55:55Z"
                }
              }
            ],
            "location": {
              "lat": 52.440807,
              "lng": 13.351399
            },
            "distance": 15063
          },
          {
            "time": {
              "arrival": "2023-05-28T08:58:00Z",
              "departure": "2023-05-28T09:08:00Z"
            },
            "load": [
              4
            ],
            "activities": [
              {
                "jobId": "Job_9_position-4",
                "type": "pickup",
                "location": {
                  "lat": 52.446407,
                  "lng": 13.36047
                },
                "time": {
                  "start": "2023-05-28T08:58:00Z",
                  "end": "2023-05-28T09:08:00Z"
                }
              }
            ],
            "location": {
              "lat": 52.446407,
              "lng": 13.36047
            },
            "distance": 16157
          },
          {
            "time": {
              "arrival": "2023-05-28T09:15:14Z",
              "departure": "2023-05-28T09:25:14Z"
            },
            "load": [
              5
            ],
            "activities": [
              {
                "jobId": "Job_8_position-5",
                "type": "pickup",
                "location": {
                  "lat": 52.458414,
                  "lng": 13.392079
                },
                "time": {
                  "start": "2023-05-28T09:15:14Z",
                  "end": "2023-05-28T09:25:14Z"
                }
              }
            ],
            "location": {
              "lat": 52.458414,
              "lng": 13.392079
            },
            "distance": 19734
          },
          {
            "time": {
              "arrival": "2023-05-28T09:31:59Z",
              "departure": "2023-05-28T09:41:59Z"
            },
            "load": [
              6
            ],
            "activities": [
              {
                "jobId": "Job_7_position-6",
                "type": "pickup",
                "location": {
                  "lat": 52.447476,
                  "lng": 13.433062
                },
                "time": {
                  "start": "2023-05-28T09:31:59Z",
                  "end": "2023-05-28T09:41:59Z"
                }
              }
            ],
            "location": {
              "lat": 52.447476,
              "lng": 13.433062
            },
            "distance": 23760
          },
          {
            "time": {
              "arrival": "2023-05-28T09:48:05Z",
              "departure": "2023-05-28T09:58:05Z"
            },
            "load": [
              7
            ],
            "activities": [
              {
                "jobId": "Job_6_position-7",
                "type": "pickup",
                "location": {
                  "lat": 52.434003,
                  "lng": 13.466142
                },
                "time": {
                  "start": "2023-05-28T09:48:05Z",
                  "end": "2023-05-28T09:58:05Z"
                }
              }
            ],
            "location": {
              "lat": 52.434003,
              "lng": 13.466142
            },
            "distance": 27317
          },
          {
            "time": {
              "arrival": "2023-05-28T10:07:13Z",
              "departure": "2023-05-28T10:17:13Z"
            },
            "load": [
              8
            ],
            "activities": [
              {
                "jobId": "Job_5_position-8",
                "type": "pickup",
                "location": {
                  "lat": 52.463341,
                  "lng": 13.49061
                },
                "time": {
                  "start": "2023-05-28T10:07:13Z",
                  "end": "2023-05-28T10:17:13Z"
                }
              }
            ],
            "location": {
              "lat": 52.463341,
              "lng": 13.49061
            },
            "distance": 32635
          },
          {
            "time": {
              "arrival": "2023-05-28T10:23:53Z",
              "departure": "2023-05-28T10:33:53Z"
            },
            "load": [
              9
            ],
            "activities": [
              {
                "jobId": "Job_4_position-9",
                "type": "pickup",
                "location": {
                  "lat": 52.473537,
                  "lng": 13.505414
                },
                "time": {
                  "start": "2023-05-28T10:23:53Z",
                  "end": "2023-05-28T10:33:53Z"
                }
              }
            ],
            "location": {
              "lat": 52.473537,
              "lng": 13.505414
            },
            "distance": 35855
          },
          {
            "time": {
              "arrival": "2023-05-28T10:36:34Z",
              "departure": "2023-05-28T10:46:34Z"
            },
            "load": [
              10
            ],
            "activities": [
              {
                "jobId": "Job_3_position-10",
                "type": "pickup",
                "location": {
                  "lat": 52.482275,
                  "lng": 13.502456
                },
                "time": {
                  "start": "2023-05-28T10:36:34Z",
                  "end": "2023-05-28T10:46:34Z"
                }
              }
            ],
            "location": {
              "lat": 52.482275,
              "lng": 13.502456
            },
            "distance": 37299
          },
          {
            "time": {
              "arrival": "2023-05-28T11:00:08Z",
              "departure": "2023-05-28T11:10:08Z"
            },
            "load": [
              11
            ],
            "activities": [
              {
                "jobId": "Job_2",
                "type": "pickup",
                "location": {
                  "lat": 52.534553,
                  "lng": 13.519429
                },
                "time": {
                  "start": "2023-05-28T11:00:08Z",
                  "end": "2023-05-28T11:10:08Z"
                }
              }
            ],
            "location": {
              "lat": 52.534553,
              "lng": 13.519429
            },
            "distance": 46053
          },
          {
            "time": {
              "arrival": "2023-05-28T11:21:08Z",
              "departure": "2023-05-28T11:31:08Z"
            },
            "load": [
              12
            ],
            "activities": [
              {
                "jobId": "Job_1",
                "type": "pickup",
                "location": {
                  "lat": 52.56182,
                  "lng": 13.497167
                },
                "time": {
                  "start": "2023-05-28T11:21:08Z",
                  "end": "2023-05-28T11:31:08Z"
                }
              }
            ],
            "location": {
              "lat": 52.56182,
              "lng": 13.497167
            },
            "distance": 50696
          },
          {
            "time": {
              "arrival": "2023-05-28T12:05:09Z",
              "departure": "2023-05-28T12:15:09Z"
            },
            "load": [
              13
            ],
            "activities": [
              {
                "jobId": "Job_13",
                "type": "pickup",
                "location": {
                  "lat": 52.457629,
                  "lng": 13.323742
                },
                "time": {
                  "start": "2023-05-28T12:05:09Z",
                  "end": "2023-05-28T12:15:09Z"
                }
              }
            ],
            "location": {
              "lat": 52.457629,
              "lng": 13.323742
            },
            "distance": 71681
          },
          {
            "time": {
              "arrival": "2023-05-28T12:33:52Z",
              "departure": "2023-05-28T12:33:52Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "arrival",
                "type": "arrival",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T12:33:52Z",
                  "end": "2023-05-28T12:33:52Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 84191
          }
        ],
        "statistic": {
          "cost": 102.16,
          "distance": 84191,
          "duration": 16432,
          "times": {
            "driving": 8632,
            "serving": 7800,
            "waiting": 0,
            "stopping": 0,
            "break": 0
          }
        },
        "shiftIndex": 0
      }
    ]
  }

Problem: Ordered tasks with first, last, and any

In this scenario, the previously unpositioned tasks (Job_1, Job_2, and Job_13), are assigned the following positions:

  • Job_1 is now assigned position.type: "any"
  • Job_2 is now assigned position.type: "last"
  • Job_13 is now assigned position.type: "first"
Click to expand/collapse the sample JSON
  {
    "fleet": {
      "types": [
        {
          "id": "small",
          "profile": "car",
          "costs": {
            "fixed": 20,
            "distance": 0,
            "time": 0.005
          },
          "shifts": [
            {
              "start": {
                "time": "2023-05-28T08:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              },
              "end": {
                "time": "2023-05-28T16:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              }
            }
          ],
          "capacity": [
            20
          ],
          "amount": 1
        }
      ],
      "profiles": [
        {
          "type": "car",
          "name": "car"
        }
      ]
    },
    "plan": {
      "jobs": [
        {
          "id": "Job_1_position-any",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.56182,
                      "lng": 13.497167
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "any"
                }
              }
            ]
          }
        },
        {
          "id": "Job_2_position-last",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.534553,
                      "lng": 13.519429
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "last"
                }
              }
            ]
          }
        },
        {
          "id": "Job_3_position-11",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.482275,
                      "lng": 13.502456
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 11
                }
              }
            ]
          }
        },
        {
          "id": "Job_4_position-10",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.473537,
                      "lng": 13.505414
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 10
                }
              }
            ]
          }
        },
        {
          "id": "Job_5_position-9",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.463341,
                      "lng": 13.49061
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 9
                }
              }
            ]
          }
        },
        {
          "id": "Job_6_position-8",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.434003,
                      "lng": 13.466142
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 8
                }
              }
            ]
          }
        },
        {
          "id": "Job_7_position-7",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.447476,
                      "lng": 13.433062
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 7
                }
              }
            ]
          }
        },
        {
          "id": "Job_8_position-6",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458414,
                      "lng": 13.392079
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 6
                }
              }
            ]
          }
        },
        {
          "id": "Job_9_position-5",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.446407,
                      "lng": 13.36047
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 5
                }
              }
            ]
          }
        },
        {
          "id": "Job_10_position-4",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.440807,
                      "lng": 13.351399
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 4
                }
              }
            ]
          }
        },
        {
          "id": "Job_11_position-3",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.441913,
                      "lng": 13.339028
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 3
                }
              }
            ]
          }
        },
        {
          "id": "Job_12_position-2",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458232,
                      "lng": 13.338698
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "Job_13_position-first",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.457629,
                      "lng": 13.323742
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "first"
                }
              }
            ]
          }
        }
      ]
    }
  }

Solution

With specific position type now applied to Job_1, Job_2, and Job_13, the optimization algorithm places these tasks within the tour corresponding to the position type of the task:

  • Job_1 can be served anywhere between tasks whose position.type is first and last.
  • Job_2 is served after tasks whose position.type is first, ordered, or any, as well as unpositioned tasks, and immediately before returning to the depot.
  • Job_13 is served as the first job, immediately after the vehicle departs from the depot, and before any ordered jobs.

The following figure shows the updated tour and highlights the new positions of Job_1, Job_2, and Job_13:

A tour with positioned and first, last, any tasks

The following section contains the full solution JSON file:

Click to expand/collapse the sample JSON
  {
    "statistic": {
      "cost": 92.725,
      "distance": 63720,
      "duration": 14545,
      "times": {
        "driving": 6745,
        "serving": 7800,
        "waiting": 0,
        "stopping": 0,
        "break": 0
      }
    },
    "tours": [
      {
        "vehicleId": "small_1",
        "typeId": "small",
        "stops": [
          {
            "time": {
              "arrival": "2023-05-28T08:00:00Z",
              "departure": "2023-05-28T08:00:00Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "departure",
                "type": "departure",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T08:00:00Z",
                  "end": "2023-05-28T08:00:00Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 0
          },
          {
            "time": {
              "arrival": "2023-05-28T08:19:30Z",
              "departure": "2023-05-28T08:29:30Z"
            },
            "load": [
              1
            ],
            "activities": [
              {
                "jobId": "Job_13_position-first",
                "type": "pickup",
                "location": {
                  "lat": 52.457629,
                  "lng": 13.323742
                },
                "time": {
                  "start": "2023-05-28T08:19:30Z",
                  "end": "2023-05-28T08:29:30Z"
                }
              }
            ],
            "location": {
              "lat": 52.457629,
              "lng": 13.323742
            },
            "distance": 12734
          },
          {
            "time": {
              "arrival": "2023-05-28T08:32:01Z",
              "departure": "2023-05-28T08:42:01Z"
            },
            "load": [
              2
            ],
            "activities": [
              {
                "jobId": "Job_12_position-2",
                "type": "pickup",
                "location": {
                  "lat": 52.458232,
                  "lng": 13.338698
                },
                "time": {
                  "start": "2023-05-28T08:32:01Z",
                  "end": "2023-05-28T08:42:01Z"
                }
              }
            ],
            "location": {
              "lat": 52.458232,
              "lng": 13.338698
            },
            "distance": 13888
          },
          {
            "time": {
              "arrival": "2023-05-28T08:46:03Z",
              "departure": "2023-05-28T08:56:03Z"
            },
            "load": [
              3
            ],
            "activities": [
              {
                "jobId": "Job_11_position-3",
                "type": "pickup",
                "location": {
                  "lat": 52.441913,
                  "lng": 13.339028
                },
                "time": {
                  "start": "2023-05-28T08:46:03Z",
                  "end": "2023-05-28T08:56:03Z"
                }
              }
            ],
            "location": {
              "lat": 52.441913,
              "lng": 13.339028
            },
            "distance": 16014
          },
          {
            "time": {
              "arrival": "2023-05-28T08:59:34Z",
              "departure": "2023-05-28T09:09:34Z"
            },
            "load": [
              4
            ],
            "activities": [
              {
                "jobId": "Job_10_position-4",
                "type": "pickup",
                "location": {
                  "lat": 52.440807,
                  "lng": 13.351399
                },
                "time": {
                  "start": "2023-05-28T08:59:34Z",
                  "end": "2023-05-28T09:09:34Z"
                }
              }
            ],
            "location": {
              "lat": 52.440807,
              "lng": 13.351399
            },
            "distance": 17626
          },
          {
            "time": {
              "arrival": "2023-05-28T09:11:39Z",
              "departure": "2023-05-28T09:21:39Z"
            },
            "load": [
              5
            ],
            "activities": [
              {
                "jobId": "Job_9_position-5",
                "type": "pickup",
                "location": {
                  "lat": 52.446407,
                  "lng": 13.36047
                },
                "time": {
                  "start": "2023-05-28T09:11:39Z",
                  "end": "2023-05-28T09:21:39Z"
                }
              }
            ],
            "location": {
              "lat": 52.446407,
              "lng": 13.36047
            },
            "distance": 18720
          },
          {
            "time": {
              "arrival": "2023-05-28T09:28:53Z",
              "departure": "2023-05-28T09:38:53Z"
            },
            "load": [
              6
            ],
            "activities": [
              {
                "jobId": "Job_8_position-6",
                "type": "pickup",
                "location": {
                  "lat": 52.458414,
                  "lng": 13.392079
                },
                "time": {
                  "start": "2023-05-28T09:28:53Z",
                  "end": "2023-05-28T09:38:53Z"
                }
              }
            ],
            "location": {
              "lat": 52.458414,
              "lng": 13.392079
            },
            "distance": 22297
          },
          {
            "time": {
              "arrival": "2023-05-28T09:45:38Z",
              "departure": "2023-05-28T09:55:38Z"
            },
            "load": [
              7
            ],
            "activities": [
              {
                "jobId": "Job_7_position-7",
                "type": "pickup",
                "location": {
                  "lat": 52.447476,
                  "lng": 13.433062
                },
                "time": {
                  "start": "2023-05-28T09:45:38Z",
                  "end": "2023-05-28T09:55:38Z"
                }
              }
            ],
            "location": {
              "lat": 52.447476,
              "lng": 13.433062
            },
            "distance": 26323
          },
          {
            "time": {
              "arrival": "2023-05-28T10:01:44Z",
              "departure": "2023-05-28T10:11:44Z"
            },
            "load": [
              8
            ],
            "activities": [
              {
                "jobId": "Job_6_position-8",
                "type": "pickup",
                "location": {
                  "lat": 52.434003,
                  "lng": 13.466142
                },
                "time": {
                  "start": "2023-05-28T10:01:44Z",
                  "end": "2023-05-28T10:11:44Z"
                }
              }
            ],
            "location": {
              "lat": 52.434003,
              "lng": 13.466142
            },
            "distance": 29880
          },
          {
            "time": {
              "arrival": "2023-05-28T10:20:52Z",
              "departure": "2023-05-28T10:30:52Z"
            },
            "load": [
              9
            ],
            "activities": [
              {
                "jobId": "Job_5_position-9",
                "type": "pickup",
                "location": {
                  "lat": 52.463341,
                  "lng": 13.49061
                },
                "time": {
                  "start": "2023-05-28T10:20:52Z",
                  "end": "2023-05-28T10:30:52Z"
                }
              }
            ],
            "location": {
              "lat": 52.463341,
              "lng": 13.49061
            },
            "distance": 35198
          },
          {
            "time": {
              "arrival": "2023-05-28T10:37:32Z",
              "departure": "2023-05-28T10:47:32Z"
            },
            "load": [
              10
            ],
            "activities": [
              {
                "jobId": "Job_4_position-10",
                "type": "pickup",
                "location": {
                  "lat": 52.473537,
                  "lng": 13.505414
                },
                "time": {
                  "start": "2023-05-28T10:37:32Z",
                  "end": "2023-05-28T10:47:32Z"
                }
              }
            ],
            "location": {
              "lat": 52.473537,
              "lng": 13.505414
            },
            "distance": 38418
          },
          {
            "time": {
              "arrival": "2023-05-28T10:50:13Z",
              "departure": "2023-05-28T11:00:13Z"
            },
            "load": [
              11
            ],
            "activities": [
              {
                "jobId": "Job_3_position-11",
                "type": "pickup",
                "location": {
                  "lat": 52.482275,
                  "lng": 13.502456
                },
                "time": {
                  "start": "2023-05-28T10:50:13Z",
                  "end": "2023-05-28T11:00:13Z"
                }
              }
            ],
            "location": {
              "lat": 52.482275,
              "lng": 13.502456
            },
            "distance": 39862
          },
          {
            "time": {
              "arrival": "2023-05-28T11:20:47Z",
              "departure": "2023-05-28T11:30:47Z"
            },
            "load": [
              12
            ],
            "activities": [
              {
                "jobId": "Job_1_position-any",
                "type": "pickup",
                "location": {
                  "lat": 52.56182,
                  "lng": 13.497167
                },
                "time": {
                  "start": "2023-05-28T11:20:47Z",
                  "end": "2023-05-28T11:30:47Z"
                }
              }
            ],
            "location": {
              "lat": 52.56182,
              "lng": 13.497167
            },
            "distance": 51041
          },
          {
            "time": {
              "arrival": "2023-05-28T11:37:24Z",
              "departure": "2023-05-28T11:47:24Z"
            },
            "load": [
              13
            ],
            "activities": [
              {
                "jobId": "Job_2_position-last",
                "type": "pickup",
                "location": {
                  "lat": 52.534553,
                  "lng": 13.519429
                },
                "time": {
                  "start": "2023-05-28T11:37:24Z",
                  "end": "2023-05-28T11:47:24Z"
                }
              }
            ],
            "location": {
              "lat": 52.534553,
              "lng": 13.519429
            },
            "distance": 54790
          },
          {
            "time": {
              "arrival": "2023-05-28T12:02:25Z",
              "departure": "2023-05-28T12:02:25Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "arrival",
                "type": "arrival",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T12:02:25Z",
                  "end": "2023-05-28T12:02:25Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 63720
          }
        ],
        "statistic": {
          "cost": 92.725,
          "distance": 63720,
          "duration": 14545,
          "times": {
            "driving": 6745,
            "serving": 7800,
            "waiting": 0,
            "stopping": 0,
            "break": 0
          }
        },
        "shiftIndex": 0
      }
    ]
  }

Problem: Task position as soft constraint

When task positions are treated as hard constraints, the optimization algorithm must strictly follow the specified order, which can sometimes cause inefficient routes. However, by using the optimizeTaskPosition objective within the objectives array in the problem specification, you can allow the algorithm to consider task positions while still focusing on efficiency.

This means the algorithm can prioritize creating an efficient route, even if it needs to adjust the order of tasks. This approach leads to more efficient solutions because it introduces a balance between task positions and overall route optimization.

The following snippet provides a sample objectives array to optimize the efficiency of the tour, containing the optimizeTaskPosition objective at the recommended position:

{
  "objectives": [
    {
      "type": "minimizeUnassigned"
    },
    {
      "type": "optimizeTaskPosition"
    },
    {
      "type": "minimizeCost"
    }
  ]
}
📘

Note

The minimizeUnassigned and minimizeCost objectives are mandatory. For more information, see Use objective functions for specific optimization goals.

Consider the following sample problem that contains job tasks positioned as first, last, any, and ordered:

Click to expand/collapse the sample JSON
  {
    "fleet": {
      "types": [
        {
          "id": "vehicle_1",
          "profile": "vehicle",
          "costs": {
            "fixed": 22,
            "distance": 0.0005,
            "time": 0.0019444444444444444
          },
          "shifts": [
            {
              "start": {
                "time": "2024-06-14T08:00:00+02:00",
                "location": {
                  "lat": 52.531,
                  "lng": 13.38461
                }
              },
              "end": {
                "time": "2024-06-14T18:00:00+02:00",
                "location": {
                  "lat": 52.531,
                  "lng": 13.38461
                }
              }
            }
          ],
          "capacity": [
            30
          ],
          "amount": 2
        }
      ],
      "profiles": [
        {
          "type": "car",
          "name": "vehicle"
        }
      ]
    },
    "plan": {
      "jobs": [
        {
          "id": "job_0_position-first",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.53277,
                      "lng": 13.40199
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "first"
                }
              }
            ]
          }
        },
        {
          "id": "job_1_position-1",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.52777,
                      "lng": 13.40092
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                }
              }
            ]
          }
        },
        {
          "id": "job_2_position-2",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.534,
                      "lng": 13.38847
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "job_3_position-3",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.52588,
                      "lng": 13.3899
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 3
                }
              }
            ]
          }
        },
        {
          "id": "job_4_position-4",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.53261,
                      "lng": 13.38863
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 4
                }
              }
            ]
          }
        },
        {
          "id": "job_5_position-5",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.5325,
                      "lng": 13.38878
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 5
                }
              }
            ]
          }
        },
        {
          "id": "job_6_position-6",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.53201,
                      "lng": 13.37802
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 6
                }
              }
            ]
          }
        },
        {
          "id": "job_7_position-7",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.52608,
                      "lng": 13.3892
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 7
                }
              }
            ]
          }
        },
        {
          "id": "job_8_position-8",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.524,
                      "lng": 13.3875
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 8
                }
              }
            ]
          }
        },
        {
          "id": "job_9_position-9",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.52872,
                      "lng": 13.37579
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 9
                }
              }
            ]
          }
        },
        {
          "id": "job_10_position-10",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.52373,
                      "lng": 13.38084
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 10
                }
              }
            ]
          }
        },
        {
          "id": "job_11_position-11",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.53627,
                      "lng": 13.37686
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 11
                }
              }
            ]
          }
        },
        {
          "id": "job_12_position-last",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.5311,
                      "lng": 13.3999
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "last"
                }
              }
            ]
          }
        },
        {
          "id": "job_13_position-any",
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.53447,
                      "lng": 13.37006
                    },
                    "duration": 300
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "any"
                }
              }
            ]
          }
        }
      ]
    }
  }

Solution

The following sections compare the solutions for the preceding problem, considering both soft and hard constraints on the position.

To clearly illustrate the contrast between these solutions and effectively highlight the differences in tour order, the routes between stops are depicted as straight lines.

Position as hard constraint

The following figure visualizes the solution for the preceding problem, in which the position was configured as a hard constraint (the problem doesn't have the optimizeTaskPosition objective):

A tour with task position as a hard constraint that might lead to inefficient routes

Because position is a hard constraint, the optimization algorithm must adhere to the specified task order rigidly, regardless of potential inefficiencies it might cause. As demonstrated in the previous figure, to maintain the position order, the vehicle is assigned to follow a long and convoluted route, even if better alternatives are available.

Position as soft constraint

By contrast, the following figure visualizes the solution for the preceding problem, but now position is a soft constraint (the problem has the optimizeTaskPosition objective):

A tour with task position as a soft constraint that makes routes more efficient

In this case, the optimization algorithm treated the position order as a preference, rather than a strict constraint. This, for example, allowed it to serve a task with position.type set to last earlier than a task with position.type set to first—an outcome that would have been impossible with hard constraints. By doing so, the algorithm achieved greater tour efficiency by reducing the total tour distance as well as the overall tour cost.

Problem: Task position and multiple vehicles

Consider an example with more than one vehicle to serve jobs with various position constraints within the tour. In the following problem, the fleet consists of two vehicles belonging to a single vehicle type. The fleet is to perform 13 jobs, with varying position.type settings (ordered, first, last, and any).

Click to expand/collapse the sample JSON
  {
    "fleet": {
      "types": [
        {
          "id": "small",
          "profile": "car",
          "costs": {
            "fixed": 20,
            "distance": 0,
            "time": 0.005
          },
          "shifts": [
            {
              "start": {
                "time": "2023-05-28T08:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              },
              "end": {
                "time": "2023-05-28T16:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              }
            }
          ],
          "capacity": [
            7
          ],
          "amount": 2
        }
      ],
      "profiles": [
        {
          "type": "car",
          "name": "car"
        }
      ]
    },
    "plan": {
      "jobs": [
        {
          "id": "Job_1_position-any",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.56182,
                      "lng": 13.497167
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "any"
                }
              }
            ]
          }
        },
        {
          "id": "Job_2_position-last",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.534553,
                      "lng": 13.519429
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "last"
                }
              }
            ]
          }
        },
        {
          "id": "Job_3_position-11",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.482275,
                      "lng": 13.502456
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 11
                }
              }
            ]
          }
        },
        {
          "id": "Job_4_position-10",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.473537,
                      "lng": 13.505414
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 10
                }
              }
            ]
          }
        },
        {
          "id": "Job_5_position-9",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.463341,
                      "lng": 13.49061
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 9
                }
              }
            ]
          }
        },
        {
          "id": "Job_6_position-8",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.434003,
                      "lng": 13.466142
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 8
                }
              }
            ]
          }
        },
        {
          "id": "Job_7_position-7",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.447476,
                      "lng": 13.433062
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 7
                }
              }
            ]
          }
        },
        {
          "id": "Job_8_position-6",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458414,
                      "lng": 13.392079
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 6
                }
              }
            ]
          }
        },
        {
          "id": "Job_9_position-5",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.446407,
                      "lng": 13.36047
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 5
                }
              }
            ]
          }
        },
        {
          "id": "Job_10_position-4",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.440807,
                      "lng": 13.351399
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 4
                }
              }
            ]
          }
        },
        {
          "id": "Job_11_position-3",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.441913,
                      "lng": 13.339028
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 3
                }
              }
            ]
          }
        },
        {
          "id": "Job_12_position-2",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458232,
                      "lng": 13.338698
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "Job_13_position-first",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.457629,
                      "lng": 13.323742
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "first"
                }
              }
            ]
          }
        }
      ]
    }
  }

Solution

The solution demonstrates that each vehicle serves its assigned jobs according to its position: starting with tasks assigned to the first position, then moving on to ordered and any tasks, and finally finishing the tour with tasks assigned to the last position type.

The following figure shows the solution visualization for each vehicle, along with the corresponding tour position:

A tour with task position as soft constraint
📘

Note

For solutions involving multiple vehicles, jobs are positioned relative to each vehicle's route, rather than in absolute order. This means a task with position 7 could be served before a job with position 6 if they are assigned to different vehicles, as demonstrated in the following figure:

Job position in relation to vehicle route

The following section contains the full solution JSON file:

Click to expand/collapse the sample JSON
  {
      "statistic": {
          "cost": 697.76,
          "distance": 241984,
          "duration": 26211,
          "times": {
              "driving": 21211,
              "serving": 5000,
              "waiting": 0,
              "break": 0
          }
      },
      "tours": [
          {
              "vehicleId": "6d1dc02e19d6_1",
              "typeId": "6d1dc02e19d6",
              "stops": [
                  {
                      "location": {
                          "lat": 52.530971,
                          "lng": 13.384915
                      },
                      "time": {
                          "arrival": "2021-08-27T06:03:00Z",
                          "departure": "2021-08-27T06:03:00Z"
                      },
                      "load": [
                          9
                      ],
                      "activities": [
                          {
                              "jobId": "departure",
                              "type": "departure"
                          }
                      ],
                      "distance": 0
                  },
                  {
                      "location": {
                          "lat": 52.90926602381726,
                          "lng": 13.781018586655174
                      },
                      "time": {
                          "arrival": "2021-08-27T06:56:53Z",
                          "departure": "2021-08-27T07:05:13Z"
                      },
                      "load": [
                          6
                      ],
                      "activities": [
                          {
                              "jobId": "job_9",
                              "type": "delivery"
                          }
                      ],
                      "distance": 11909
                  },
                  {
                      "location": {
                          "lat": 52.91900693527653,
                          "lng": 13.92574157985448
                      },
                      "time": {
                          "arrival": "2021-08-27T07:26:14Z",
                          "departure": "2021-08-27T07:34:34Z"
                      },
                      "load": [
                          3
                      ],
                      "activities": [
                          {
                              "jobId": "job_8",
                              "type": "delivery"
                          }
                      ],
                      "distance": 24698
                  },
                  {
                      "location": {
                          "lat": 52.73186486317331,
                          "lng": 13.59405790954721
                      },
                      "time": {
                          "arrival": "2021-08-27T08:19:56Z",
                          "departure": "2021-08-27T08:28:16Z"
                      },
                      "load": [
                          0
                      ],
                      "activities": [
                          {
                              "jobId": "job_7",
                              "type": "delivery"
                          }
                      ],
                      "distance": 35735
                  },
                  {
                      "location": {
                          "lat": 52.529953,
                          "lng": 13.314877
                      },
                      "time": {
                          "arrival": "2021-08-27T09:20:22Z",
                          "departure": "2021-08-27T09:20:22Z"
                      },
                      "load": [
                          0
                      ],
                      "activities": [
                          {
                              "jobId": "arrival",
                              "type": "arrival"
                          }
                      ],
                      "distance": 43438
                  }
              ],
              "statistic": {
                  "cost": 441.36400000000003,
                  "distance": 163393,
                  "duration": 11842,
                  "times": {
                      "driving": 10342,
                      "serving": 1500,
                      "waiting": 0,
                      "break": 0
                  }
              },
              "shiftIndex": 0
          },
          {
              "vehicleId": "b47bb0d26e4c_1",
              "typeId": "b47bb0d26e4c",
              "stops": [
                  {
                      "location": {
                          "lat": 52.530971,
                          "lng": 13.384915
                      },
                      "time": {
                          "arrival": "2021-08-27T14:03:00Z",
                          "departure": "2021-08-27T14:03:00Z"
                      },
                      "load": [
                          6
                      ],
                      "activities": [
                          {
                              "jobId": "departure",
                              "type": "departure"
                          }
                      ],
                      "distance": 0
                  },
                  {
                      "location": {
                          "lat": 52.469144533572845,
                          "lng": 13.497412369472364
                      },
                      "time": {
                          "arrival": "2021-08-27T14:35:54Z",
                          "departure": "2021-08-27T14:44:14Z"
                      },
                      "load": [
                          9
                      ],
                      "activities": [
                          {
                              "jobId": "job_5",
                              "type": "pickup"
                          }
                      ],
                      "distance": 3350
                  },
                  {
                      "location": {
                          "lat": 52.44927857909315,
                          "lng": 13.36502844360698
                      },
                      "time": {
                          "arrival": "2021-08-27T15:07:52Z",
                          "departure": "2021-08-27T15:16:12Z"
                      },
                      "load": [
                          12
                      ],
                      "activities": [
                          {
                              "jobId": "job_3",
                              "type": "pickup"
                          }
                      ],
                      "distance": 12036
                  },
                  {
                      "location": {
                          "lat": 52.48998701990294,
                          "lng": 13.286076137075746
                      },
                      "time": {
                          "arrival": "2021-08-27T15:36:17Z",
                          "departure": "2021-08-27T15:44:37Z"
                      },
                      "load": [
                          15
                      ],
                      "activities": [
                          {
                              "jobId": "job_4",
                              "type": "pickup"
                          }
                      ],
                      "distance": 18658
                  },
                  {
                      "location": {
                          "lat": 52.53315878386378,
                          "lng": 13.352999270430129
                      },
                      "time": {
                          "arrival": "2021-08-27T16:04:46Z",
                          "departure": "2021-08-27T16:13:06Z"
                      },
                      "load": [
                          18
                      ],
                      "activities": [
                          {
                              "jobId": "job_2",
                              "type": "pickup"
                          }
                      ],
                      "distance": 73933
                  },
                  {
                      "location": {
                          "lat": 52.58136440229602,
                          "lng": 13.423236627987324
                      },
                      "time": {
                          "arrival": "2021-08-27T16:37:27Z",
                          "departure": "2021-08-27T16:45:47Z"
                      },
                      "load": [
                          21
                      ],
                      "activities": [
                          {
                              "jobId": "job_1",
                              "type": "pickup"
                          }
                      ],
                      "distance": 91183
                  },
                  {
                      "location": {
                          "lat": 52.57604629377083,
                          "lng": 13.488619313215825
                      },
                      "time": {
                          "arrival": "2021-08-27T17:03:41Z",
                          "departure": "2021-08-27T17:12:01Z"
                      },
                      "load": [
                          18
                      ],
                      "activities": [
                          {
                              "jobId": "job_6",
                              "type": "delivery"
                          }
                      ],
                      "distance": 128474
                  },
                  {
                      "location": {
                          "lat": 52.55137844164478,
                          "lng": 13.509316000155945
                      },
                      "time": {
                          "arrival": "2021-08-27T17:25:49Z",
                          "departure": "2021-08-27T17:34:09Z"
                      },
                      "load": [
                          15
                      ],
                      "activities": [
                          {
                              "jobId": "job_10",
                              "type": "delivery"
                          }
                      ],
                      "distance": 157410
                  },
                  {
                      "location": {
                          "lat": 52.530971,
                          "lng": 13.384915
                      },
                      "time": {
                          "arrival": "2021-08-27T18:02:29Z",
                          "departure": "2021-08-27T18:02:29Z"
                      },
                      "load": [
                          0
                      ],
                      "activities": [
                          {
                              "jobId": "arrival",
                              "type": "arrival"
                          }
                      ],
                      "distance": 168272
                  }
              ],
              "statistic": {
                  "cost": 256.39599999999996,
                  "distance": 78591,
                  "duration": 14369,
                  "times": {
                      "driving": 10869,
                      "serving": 3500,
                      "waiting": 0,
                      "break": 0
                  }
              },
              "shiftIndex": 0
          }
      ]
  }

Problem: Task position and time windows

As the job task position is a hard constraint by default, it can conflict with some other job constraints that are considered as hard in Tour Planning, such as skills, territories, demand, or time windows.

Consider an example in which two jobs are configured with time windows and are assigned with a position:

  • Job_12:
    • Positioned as an ordered task within the tour (position.type: "ordered") with the position value set to 2.
    • Configured with a time window for completion between 08:10:00 and 08:20:00
  • Job_13:
    • Positioned as the first task in the tour (position.type: "first")
    • Configured with a time window for completion between 08:30:00 and 08:40:00

The following section contains this configuration within a context of a full problem JSON:

Click to expand/collapse the sample JSON
  {
    "fleet": {
      "types": [
        {
          "id": "small",
          "profile": "car",
          "costs": {
            "fixed": 20,
            "distance": 0,
            "time": 0.005
          },
          "shifts": [
            {
              "start": {
                "time": "2023-05-28T08:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              },
              "end": {
                "time": "2023-05-28T16:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              }
            }
          ],
          "capacity": [
            20
          ],
          "amount": 1
        }
      ],
      "profiles": [
        {
          "type": "car",
          "name": "car"
        }
      ]
    },
    "plan": {
      "jobs": [
        {
          "id": "Job_1_position-any",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.56182,
                      "lng": 13.497167
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "any"
                }
              }
            ]
          }
        },
        {
          "id": "Job_2_position-last",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.534553,
                      "lng": 13.519429
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "last"
                }
              }
            ]
          }
        },
        {
          "id": "Job_3_position-11",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.482275,
                      "lng": 13.502456
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 11
                }
              }
            ]
          }
        },
        {
          "id": "Job_4_position-10",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.473537,
                      "lng": 13.505414
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 10
                }
              }
            ]
          }
        },
        {
          "id": "Job_5_position-9",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.463341,
                      "lng": 13.49061
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 9
                }
              }
            ]
          }
        },
        {
          "id": "Job_6_position-8",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.434003,
                      "lng": 13.466142
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 8
                }
              }
            ]
          }
        },
        {
          "id": "Job_7_position-7",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.447476,
                      "lng": 13.433062
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 7
                }
              }
            ]
          }
        },
        {
          "id": "Job_8_position-6",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458414,
                      "lng": 13.392079
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 6
                }
              }
            ]
          }
        },
        {
          "id": "Job_9_position-5",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.446407,
                      "lng": 13.36047
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 5
                }
              }
            ]
          }
        },
        {
          "id": "Job_10_position-4",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.440807,
                      "lng": 13.351399
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 4
                }
              }
            ]
          }
        },
        {
          "id": "Job_11_position-3",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.441913,
                      "lng": 13.339028
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 3
                }
              }
            ]
          }
        },
        {
          "id": "Job_12_position-2",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "times": [
                      [
                        "2023-05-28T08:10:00Z",
                        "2023-05-28T08:20:00Z"
                      ]
                    ],
                    "location": {
                      "lat": 52.458232,
                      "lng": 13.338698
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "Job_13_position-first",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "times": [
                      [
                        "2023-05-28T08:30:00Z",
                        "2023-05-28T08:40:00Z"
                      ]
                    ],
                    "location": {
                      "lat": 52.457629,
                      "lng": 13.323742
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "first"
                }
              }
            ]
          }
        }
      ]
    }
  }

Solution

As demonstrated in the full solution JSON, the problem resulted in Job_13 being unassigned due to a violation of the time window constraint whose root cause was a misalignment of the job time windows with their required positions in the tour.

Based on the tour plan in the problem, Job_13 must be completed first, as it is positioned as the first task in the tour. Job_12 must be completed after the task that is in the first position, which, in this case, is Job_13. However, the time window for Job_13 is from 08:30:00 to 08:40:00, while the time window for Job_12 is from 08:10:00 to 08:20:00, which means that the time windows of the jobs do not allow for the required sequencing of the tasks based on their positions.

In this case, to avoid unassigned tasks in the tour, either extend their time windows or change the task positions.

The following section contains the full solution JSON file:

Click to expand/collapse the sample JSON
  {
    "statistic": {
      "cost": 88.63,
      "distance": 61157,
      "duration": 13726,
      "times": {
        "driving": 6526,
        "serving": 7200,
        "waiting": 0,
        "stopping": 0,
        "break": 0
      }
    },
    "tours": [
      {
        "vehicleId": "small_1",
        "typeId": "small",
        "stops": [
          {
            "time": {
              "arrival": "2023-05-28T08:00:00Z",
              "departure": "2023-05-28T08:00:00Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "departure",
                "type": "departure",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T08:00:00Z",
                  "end": "2023-05-28T08:00:00Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 0
          },
          {
            "time": {
              "arrival": "2023-05-28T08:18:22Z",
              "departure": "2023-05-28T08:28:22Z"
            },
            "load": [
              1
            ],
            "activities": [
              {
                "jobId": "Job_12_position-2",
                "type": "pickup",
                "location": {
                  "lat": 52.458232,
                  "lng": 13.338698
                },
                "time": {
                  "start": "2023-05-28T08:18:22Z",
                  "end": "2023-05-28T08:28:22Z"
                }
              }
            ],
            "location": {
              "lat": 52.458232,
              "lng": 13.338698
            },
            "distance": 11325
          },
          {
            "time": {
              "arrival": "2023-05-28T08:32:24Z",
              "departure": "2023-05-28T08:42:24Z"
            },
            "load": [
              2
            ],
            "activities": [
              {
                "jobId": "Job_11_position-3",
                "type": "pickup",
                "location": {
                  "lat": 52.441913,
                  "lng": 13.339028
                },
                "time": {
                  "start": "2023-05-28T08:32:24Z",
                  "end": "2023-05-28T08:42:24Z"
                }
              }
            ],
            "location": {
              "lat": 52.441913,
              "lng": 13.339028
            },
            "distance": 13451
          },
          {
            "time": {
              "arrival": "2023-05-28T08:45:55Z",
              "departure": "2023-05-28T08:55:55Z"
            },
            "load": [
              3
            ],
            "activities": [
              {
                "jobId": "Job_10_position-4",
                "type": "pickup",
                "location": {
                  "lat": 52.440807,
                  "lng": 13.351399
                },
                "time": {
                  "start": "2023-05-28T08:45:55Z",
                  "end": "2023-05-28T08:55:55Z"
                }
              }
            ],
            "location": {
              "lat": 52.440807,
              "lng": 13.351399
            },
            "distance": 15063
          },
          {
            "time": {
              "arrival": "2023-05-28T08:58:00Z",
              "departure": "2023-05-28T09:08:00Z"
            },
            "load": [
              4
            ],
            "activities": [
              {
                "jobId": "Job_9_position-5",
                "type": "pickup",
                "location": {
                  "lat": 52.446407,
                  "lng": 13.36047
                },
                "time": {
                  "start": "2023-05-28T08:58:00Z",
                  "end": "2023-05-28T09:08:00Z"
                }
              }
            ],
            "location": {
              "lat": 52.446407,
              "lng": 13.36047
            },
            "distance": 16157
          },
          {
            "time": {
              "arrival": "2023-05-28T09:15:14Z",
              "departure": "2023-05-28T09:25:14Z"
            },
            "load": [
              5
            ],
            "activities": [
              {
                "jobId": "Job_8_position-6",
                "type": "pickup",
                "location": {
                  "lat": 52.458414,
                  "lng": 13.392079
                },
                "time": {
                  "start": "2023-05-28T09:15:14Z",
                  "end": "2023-05-28T09:25:14Z"
                }
              }
            ],
            "location": {
              "lat": 52.458414,
              "lng": 13.392079
            },
            "distance": 19734
          },
          {
            "time": {
              "arrival": "2023-05-28T09:31:59Z",
              "departure": "2023-05-28T09:41:59Z"
            },
            "load": [
              6
            ],
            "activities": [
              {
                "jobId": "Job_7_position-7",
                "type": "pickup",
                "location": {
                  "lat": 52.447476,
                  "lng": 13.433062
                },
                "time": {
                  "start": "2023-05-28T09:31:59Z",
                  "end": "2023-05-28T09:41:59Z"
                }
              }
            ],
            "location": {
              "lat": 52.447476,
              "lng": 13.433062
            },
            "distance": 23760
          },
          {
            "time": {
              "arrival": "2023-05-28T09:48:05Z",
              "departure": "2023-05-28T09:58:05Z"
            },
            "load": [
              7
            ],
            "activities": [
              {
                "jobId": "Job_6_position-8",
                "type": "pickup",
                "location": {
                  "lat": 52.434003,
                  "lng": 13.466142
                },
                "time": {
                  "start": "2023-05-28T09:48:05Z",
                  "end": "2023-05-28T09:58:05Z"
                }
              }
            ],
            "location": {
              "lat": 52.434003,
              "lng": 13.466142
            },
            "distance": 27317
          },
          {
            "time": {
              "arrival": "2023-05-28T10:07:13Z",
              "departure": "2023-05-28T10:17:13Z"
            },
            "load": [
              8
            ],
            "activities": [
              {
                "jobId": "Job_5_position-9",
                "type": "pickup",
                "location": {
                  "lat": 52.463341,
                  "lng": 13.49061
                },
                "time": {
                  "start": "2023-05-28T10:07:13Z",
                  "end": "2023-05-28T10:17:13Z"
                }
              }
            ],
            "location": {
              "lat": 52.463341,
              "lng": 13.49061
            },
            "distance": 32635
          },
          {
            "time": {
              "arrival": "2023-05-28T10:23:53Z",
              "departure": "2023-05-28T10:33:53Z"
            },
            "load": [
              9
            ],
            "activities": [
              {
                "jobId": "Job_4_position-10",
                "type": "pickup",
                "location": {
                  "lat": 52.473537,
                  "lng": 13.505414
                },
                "time": {
                  "start": "2023-05-28T10:23:53Z",
                  "end": "2023-05-28T10:33:53Z"
                }
              }
            ],
            "location": {
              "lat": 52.473537,
              "lng": 13.505414
            },
            "distance": 35855
          },
          {
            "time": {
              "arrival": "2023-05-28T10:36:34Z",
              "departure": "2023-05-28T10:46:34Z"
            },
            "load": [
              10
            ],
            "activities": [
              {
                "jobId": "Job_3_position-11",
                "type": "pickup",
                "location": {
                  "lat": 52.482275,
                  "lng": 13.502456
                },
                "time": {
                  "start": "2023-05-28T10:36:34Z",
                  "end": "2023-05-28T10:46:34Z"
                }
              }
            ],
            "location": {
              "lat": 52.482275,
              "lng": 13.502456
            },
            "distance": 37299
          },
          {
            "time": {
              "arrival": "2023-05-28T11:07:08Z",
              "departure": "2023-05-28T11:17:08Z"
            },
            "load": [
              11
            ],
            "activities": [
              {
                "jobId": "Job_1_position-any",
                "type": "pickup",
                "location": {
                  "lat": 52.56182,
                  "lng": 13.497167
                },
                "time": {
                  "start": "2023-05-28T11:07:08Z",
                  "end": "2023-05-28T11:17:08Z"
                }
              }
            ],
            "location": {
              "lat": 52.56182,
              "lng": 13.497167
            },
            "distance": 48478
          },
          {
            "time": {
              "arrival": "2023-05-28T11:23:45Z",
              "departure": "2023-05-28T11:33:45Z"
            },
            "load": [
              12
            ],
            "activities": [
              {
                "jobId": "Job_2_position-last",
                "type": "pickup",
                "location": {
                  "lat": 52.534553,
                  "lng": 13.519429
                },
                "time": {
                  "start": "2023-05-28T11:23:45Z",
                  "end": "2023-05-28T11:33:45Z"
                }
              }
            ],
            "location": {
              "lat": 52.534553,
              "lng": 13.519429
            },
            "distance": 52227
          },
          {
            "time": {
              "arrival": "2023-05-28T11:48:46Z",
              "departure": "2023-05-28T11:48:46Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "arrival",
                "type": "arrival",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T11:48:46Z",
                  "end": "2023-05-28T11:48:46Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 61157
          }
        ],
        "statistic": {
          "cost": 88.63,
          "distance": 61157,
          "duration": 13726,
          "times": {
            "driving": 6526,
            "serving": 7200,
            "waiting": 0,
            "stopping": 0,
            "break": 0
          }
        },
        "shiftIndex": 0
      }
    ],
    "unassigned": [
      {
        "jobId": "Job_13_position-first",
        "reasons": [
          {
            "code": "TIME_WINDOW_CONSTRAINT",
            "description": "cannot be assigned due to violation of time window",
            "details": [
              {
                "vehicleId": "small_1",
                "shiftIndex": 0
              }
            ]
          }
        ]
      }
    ]
  }

Problem: Task position and relations

Some use cases require modeling a problem where tasks must be performed in a specific order, known as relations. To ensure these specific rules are followed in Tour Planning, relations always take precedence over the position setting.

For example, consider a sample sequence relation:

"relations": [
  {
    "type": "sequence",
    "jobs": [
      "Job_9_position-5",
      "Job_10_position-4",
      "Job_11_position-3",
      "Job_13_position-first",
      "Job_12_position-2"
    ],
    "vehicleId": "small_1"
  }
]  

This relation is particularly useful during tour replanning. When changes occur on the road, you can use the sequence relation to reinsert all locations as they were originally planned. The remaining part of the journey is then recalculated.

The following problem contains jobs with various position settings and a sequence relation from the previous example that ensures some tasks are performed in a specified order regardless of their position settings:

📘

Note

While this sample problem shows how task position can interact with tour properties or constraints, combining relations and position features is not recommended. When combined, these features might provide overlapping or conflicting information to the optimization algorithm, which can result in unassigned jobs or other unexpected results.

Click to expand/collapse the sample JSON
  {
    "fleet": {
      "types": [
        {
          "id": "small",
          "profile": "car",
          "costs": {
            "fixed": 20,
            "distance": 0,
            "time": 0.005
          },
          "shifts": [
            {
              "start": {
                "time": "2023-05-28T08:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              },
              "end": {
                "time": "2023-05-28T16:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              }
            }
          ],
          "capacity": [
            20
          ],
          "amount": 1
        }
      ],
      "profiles": [
        {
          "type": "car",
          "name": "car"
        }
      ]
    },
    "plan": {
      "jobs": [
        {
          "id": "Job_1_position-any",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.56182,
                      "lng": 13.497167
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "any"
                }
              }
            ]
          }
        },
        {
          "id": "Job_2_position-last",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.534553,
                      "lng": 13.519429
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "last"
                }
              }
            ]
          }
        },
        {
          "id": "Job_3_position-11",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.482275,
                      "lng": 13.502456
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 11
                }
              }
            ]
          }
        },
        {
          "id": "Job_4_position-10",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.473537,
                      "lng": 13.505414
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 10
                }
              }
            ]
          }
        },
        {
          "id": "Job_5_position-9",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.463341,
                      "lng": 13.49061
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 9
                }
              }
            ]
          }
        },
        {
          "id": "Job_6_position-8",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.434003,
                      "lng": 13.466142
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 8
                }
              }
            ]
          }
        },
        {
          "id": "Job_7_position-7",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.447476,
                      "lng": 13.433062
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 7
                }
              }
            ]
          }
        },
        {
          "id": "Job_8_position-6",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458414,
                      "lng": 13.392079
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 6
                }
              }
            ]
          }
        },
        {
          "id": "Job_9_position-5",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.446407,
                      "lng": 13.36047
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 5
                }
              }
            ]
          }
        },
        {
          "id": "Job_10_position-4",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.440807,
                      "lng": 13.351399
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 4
                }
              }
            ]
          }
        },
        {
          "id": "Job_11_position-3",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.441913,
                      "lng": 13.339028
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 3
                }
              }
            ]
          }
        },
        {
          "id": "Job_12_position-2",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458232,
                      "lng": 13.338698
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "Job_13_position-first",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.457629,
                      "lng": 13.323742
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "first"
                }
              }
            ]
          }
        }
      ],
      "relations": [
        {
          "type": "sequence",
          "jobs": [
            "Job_9_position-5",
            "Job_10_position-4",
            "Job_11_position-3",
            "Job_13_position-first",
            "Job_12_position-2"
          ],
          "vehicleId": "small_1"
        }
      ]
    }
  }

Solution

As the solution visualization demonstrates, the sequence relation takes precedence over the task position setting. In this specific solution, the tasks specified in the sequence relation are served at the beginning of the tour, while the remaining tasks are served according to their position type and value:

Relations and task position

The following section provides the full solution JSON file:

Click to expand/collapse the sample JSON
  {
    "statistic": {
      "cost": 93.515,
      "distance": 62385,
      "duration": 14703,
      "times": {
        "driving": 6903,
        "serving": 7800,
        "waiting": 0,
        "stopping": 0,
        "break": 0
      }
    },
    "tours": [
      {
        "vehicleId": "small_1",
        "typeId": "small",
        "stops": [
          {
            "time": {
              "arrival": "2023-05-28T08:00:00Z",
              "departure": "2023-05-28T08:00:00Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "departure",
                "type": "departure",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T08:00:00Z",
                  "end": "2023-05-28T08:00:00Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 0
          },
          {
            "time": {
              "arrival": "2023-05-28T08:19:04Z",
              "departure": "2023-05-28T08:29:04Z"
            },
            "load": [
              1
            ],
            "activities": [
              {
                "jobId": "Job_9_position-5",
                "type": "pickup",
                "location": {
                  "lat": 52.446407,
                  "lng": 13.36047
                },
                "time": {
                  "start": "2023-05-28T08:19:04Z",
                  "end": "2023-05-28T08:29:04Z"
                }
              }
            ],
            "location": {
              "lat": 52.446407,
              "lng": 13.36047
            },
            "distance": 9863
          },
          {
            "time": {
              "arrival": "2023-05-28T08:32:34Z",
              "departure": "2023-05-28T08:42:34Z"
            },
            "load": [
              2
            ],
            "activities": [
              {
                "jobId": "Job_10_position-4",
                "type": "pickup",
                "location": {
                  "lat": 52.440807,
                  "lng": 13.351399
                },
                "time": {
                  "start": "2023-05-28T08:32:34Z",
                  "end": "2023-05-28T08:42:34Z"
                }
              }
            ],
            "location": {
              "lat": 52.440807,
              "lng": 13.351399
            },
            "distance": 11447
          },
          {
            "time": {
              "arrival": "2023-05-28T08:45:55Z",
              "departure": "2023-05-28T08:55:55Z"
            },
            "load": [
              3
            ],
            "activities": [
              {
                "jobId": "Job_11_position-3",
                "type": "pickup",
                "location": {
                  "lat": 52.441913,
                  "lng": 13.339028
                },
                "time": {
                  "start": "2023-05-28T08:45:55Z",
                  "end": "2023-05-28T08:55:55Z"
                }
              }
            ],
            "location": {
              "lat": 52.441913,
              "lng": 13.339028
            },
            "distance": 13046
          },
          {
            "time": {
              "arrival": "2023-05-28T09:01:45Z",
              "departure": "2023-05-28T09:11:45Z"
            },
            "load": [
              4
            ],
            "activities": [
              {
                "jobId": "Job_13_position-first",
                "type": "pickup",
                "location": {
                  "lat": 52.457629,
                  "lng": 13.323742
                },
                "time": {
                  "start": "2023-05-28T09:01:45Z",
                  "end": "2023-05-28T09:11:45Z"
                }
              }
            ],
            "location": {
              "lat": 52.457629,
              "lng": 13.323742
            },
            "distance": 15746
          },
          {
            "time": {
              "arrival": "2023-05-28T09:14:16Z",
              "departure": "2023-05-28T09:24:16Z"
            },
            "load": [
              5
            ],
            "activities": [
              {
                "jobId": "Job_12_position-2",
                "type": "pickup",
                "location": {
                  "lat": 52.458232,
                  "lng": 13.338698
                },
                "time": {
                  "start": "2023-05-28T09:14:16Z",
                  "end": "2023-05-28T09:24:16Z"
                }
              }
            ],
            "location": {
              "lat": 52.458232,
              "lng": 13.338698
            },
            "distance": 16900
          },
          {
            "time": {
              "arrival": "2023-05-28T09:31:31Z",
              "departure": "2023-05-28T09:41:31Z"
            },
            "load": [
              6
            ],
            "activities": [
              {
                "jobId": "Job_8_position-6",
                "type": "pickup",
                "location": {
                  "lat": 52.458414,
                  "lng": 13.392079
                },
                "time": {
                  "start": "2023-05-28T09:31:31Z",
                  "end": "2023-05-28T09:41:31Z"
                }
              }
            ],
            "location": {
              "lat": 52.458414,
              "lng": 13.392079
            },
            "distance": 20962
          },
          {
            "time": {
              "arrival": "2023-05-28T09:48:16Z",
              "departure": "2023-05-28T09:58:16Z"
            },
            "load": [
              7
            ],
            "activities": [
              {
                "jobId": "Job_7_position-7",
                "type": "pickup",
                "location": {
                  "lat": 52.447476,
                  "lng": 13.433062
                },
                "time": {
                  "start": "2023-05-28T09:48:16Z",
                  "end": "2023-05-28T09:58:16Z"
                }
              }
            ],
            "location": {
              "lat": 52.447476,
              "lng": 13.433062
            },
            "distance": 24988
          },
          {
            "time": {
              "arrival": "2023-05-28T10:04:22Z",
              "departure": "2023-05-28T10:14:22Z"
            },
            "load": [
              8
            ],
            "activities": [
              {
                "jobId": "Job_6_position-8",
                "type": "pickup",
                "location": {
                  "lat": 52.434003,
                  "lng": 13.466142
                },
                "time": {
                  "start": "2023-05-28T10:04:22Z",
                  "end": "2023-05-28T10:14:22Z"
                }
              }
            ],
            "location": {
              "lat": 52.434003,
              "lng": 13.466142
            },
            "distance": 28545
          },
          {
            "time": {
              "arrival": "2023-05-28T10:23:30Z",
              "departure": "2023-05-28T10:33:30Z"
            },
            "load": [
              9
            ],
            "activities": [
              {
                "jobId": "Job_5_position-9",
                "type": "pickup",
                "location": {
                  "lat": 52.463341,
                  "lng": 13.49061
                },
                "time": {
                  "start": "2023-05-28T10:23:30Z",
                  "end": "2023-05-28T10:33:30Z"
                }
              }
            ],
            "location": {
              "lat": 52.463341,
              "lng": 13.49061
            },
            "distance": 33863
          },
          {
            "time": {
              "arrival": "2023-05-28T10:40:10Z",
              "departure": "2023-05-28T10:50:10Z"
            },
            "load": [
              10
            ],
            "activities": [
              {
                "jobId": "Job_4_position-10",
                "type": "pickup",
                "location": {
                  "lat": 52.473537,
                  "lng": 13.505414
                },
                "time": {
                  "start": "2023-05-28T10:40:10Z",
                  "end": "2023-05-28T10:50:10Z"
                }
              }
            ],
            "location": {
              "lat": 52.473537,
              "lng": 13.505414
            },
            "distance": 37083
          },
          {
            "time": {
              "arrival": "2023-05-28T10:52:51Z",
              "departure": "2023-05-28T11:02:51Z"
            },
            "load": [
              11
            ],
            "activities": [
              {
                "jobId": "Job_3_position-11",
                "type": "pickup",
                "location": {
                  "lat": 52.482275,
                  "lng": 13.502456
                },
                "time": {
                  "start": "2023-05-28T10:52:51Z",
                  "end": "2023-05-28T11:02:51Z"
                }
              }
            ],
            "location": {
              "lat": 52.482275,
              "lng": 13.502456
            },
            "distance": 38527
          },
          {
            "time": {
              "arrival": "2023-05-28T11:23:25Z",
              "departure": "2023-05-28T11:33:25Z"
            },
            "load": [
              12
            ],
            "activities": [
              {
                "jobId": "Job_1_position-any",
                "type": "pickup",
                "location": {
                  "lat": 52.56182,
                  "lng": 13.497167
                },
                "time": {
                  "start": "2023-05-28T11:23:25Z",
                  "end": "2023-05-28T11:33:25Z"
                }
              }
            ],
            "location": {
              "lat": 52.56182,
              "lng": 13.497167
            },
            "distance": 49706
          },
          {
            "time": {
              "arrival": "2023-05-28T11:40:02Z",
              "departure": "2023-05-28T11:50:02Z"
            },
            "load": [
              13
            ],
            "activities": [
              {
                "jobId": "Job_2_position-last",
                "type": "pickup",
                "location": {
                  "lat": 52.534553,
                  "lng": 13.519429
                },
                "time": {
                  "start": "2023-05-28T11:40:02Z",
                  "end": "2023-05-28T11:50:02Z"
                }
              }
            ],
            "location": {
              "lat": 52.534553,
              "lng": 13.519429
            },
            "distance": 53455
          },
          {
            "time": {
              "arrival": "2023-05-28T12:05:03Z",
              "departure": "2023-05-28T12:05:03Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "arrival",
                "type": "arrival",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T12:05:03Z",
                  "end": "2023-05-28T12:05:03Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 62385
          }
        ],
        "statistic": {
          "cost": 93.515,
          "distance": 62385,
          "duration": 14703,
          "times": {
            "driving": 6903,
            "serving": 7800,
            "waiting": 0,
            "stopping": 0,
            "break": 0
          }
        },
        "shiftIndex": 0
      }
    ]
  }

Problem: Task position and multi-jobs

In tour planning, the concept of a multi-job encompasses scenarios where up to three pickup and delivery tasks are defined within a single job. In Tour Planning, the task position feature cannot be used to change the order of pickups and deliveries in multi-job assignments. The following scenario illustrates a potential conflict arising from differences in position settings between single-job tasks and those within a multi-job.

Consider the following snippet from a sample problem:

[
  {
    "id": "Job_X-delivery-2",
    "tasks": {
      "deliveries": [
        {
          "places": [
            {
              "location": {
                "lat": 52.447476,
                "lng": 13.433062
              },
              "duration": 600
            }
          ],
          "demand": [
            1
          ],
          "position": {
            "type": "ordered",
            "value": 2
          }
        }
      ]
    }
  },
  {
    "id": "Job_Y-pickup-1",
    "tasks": {
      "pickups": [
        {
          "places": [
            {
              "location": {
                "lat": 52.441913,
                "lng": 13.339028
              },
              "duration": 600
            }
          ],
          "demand": [
            1
          ],
          "position": {
            "type": "ordered",
            "value": 1
          }
        }
      ]
    }
  },
  {
    "id": "Job_Z-pickup-2-delivery-1",
    "tasks": {
      "pickups": [
        {
          "places": [
            {
              "location": {
                "lat": 52.458232,
                "lng": 13.338698
              },
              "duration": 600
            }
          ],
          "position": {
            "type": "ordered",
            "value": 2
          },
          "demand": [
            1
          ]
        }
      ],
      "deliveries": [
        {
          "places": [
            {
              "location": {
                "lat": 52.457629,
                "lng": 13.323742
              },
              "duration": 600
            }
          ],
          "position": {
            "type": "ordered",
            "value": 1
          },
          "demand": [
            1
          ]
        }
      ]
    }
  }
]

In the preceding snippet, the position of the delivery/pickup tasks within Job_Z is reversed compared to the remaining jobs. The delivery task has a position of 1, indicating it should be executed before the pickup task, which has a position of 2.

As a result, the optimization algorithm left Job_Z unassigned, with the following message:

{
  "unassigned": [
    {
      "jobId": "Job_Z-pickup-2-delivery-1",
      "reasons": [
        {
          "code": "TOUR_ORDER_CONSTRAINT",
          "description": "cannot be assigned due to tour order constraint"
        }
      ]
    }
  ]
}

The job was not assigned to the tour because of a TOUR_ORDER_CONSTRAINT violation. This means that the order of jobs within the tour does not allow for Job_Z to be assigned due to pickup task needing to occur before the delivery task, which cannot be fulfilled within the existing tour sequence.

To avoid unassigned tours, adjust the order of pickup/delivery tasks in multi-job assignments to facilitate its assignment to tours and prevent unassigned tours.

Problem: Task position and priority

The position of a job defines its possible order but does not affect its priority within the tour, for example, in case of a lack of capacity. The following example demonstrates the difference between position and priority:

Click to expand/collapse the sample JSON
  {
    "fleet": {
      "types": [
        {
          "id": "small",
          "profile": "car",
          "costs": {
            "fixed": 20,
            "distance": 0,
            "time": 0.005
          },
          "shifts": [
            {
              "start": {
                "time": "2023-05-28T08:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              },
              "end": {
                "time": "2023-05-28T16:00:00Z",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                }
              }
            }
          ],
          "capacity": [
            8
          ],
          "amount": 1
        }
      ],
      "profiles": [
        {
          "type": "car",
          "name": "car"
        }
      ]
    },
    "plan": {
      "jobs": [
        {
          "id": "Job_1_D_position-2",
          "priority":1,
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.56182,
                      "lng": 13.497167
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "Job_2_P_position-1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.534553,
                      "lng": 13.519429
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                }
              }
            ]
          }
        },
        {
          "id": "Job_3_P_position-1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.482275,
                      "lng": 13.502456
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                }
              }
            ]
          }
        },
        {
          "id": "Job_4_D_position-2",
          "priority":1,
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.473537,
                      "lng": 13.505414
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "Job_5_P_position-1",
          "priority":2,
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.463341,
                      "lng": 13.49061
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                }
              }
            ]
          }
        },
        {
          "id": "Job_6_D_position-2",
          "priority":1,
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.434003,
                      "lng": 13.466142
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "Job_7_D_position-2",
          "priority":1,
          "tasks": {
            "deliveries": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.447476,
                      "lng": 13.433062
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 2
                }
              }
            ]
          }
        },
        {
          "id": "Job_8_P_position-1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458414,
                      "lng": 13.392079
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                }
              }
            ]
          }
        },
        {
          "id": "Job_9_P_position-1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.446407,
                      "lng": 13.36047
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                }
              }
            ]
          }
        },
        {
          "id": "Job_10_P_position-1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.440807,
                      "lng": 13.351399
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                }
              }
            ]
          }
        },
        {
          "id": "Job_11_P_position-1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.441913,
                      "lng": 13.339028
                    },
                    "duration": 600
                  }
                ],
                "demand": [
                  1
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                }
              }
            ]
          }
        },
        {
          "id": "Job_12_P_position-1",
          "tasks": {
            "pickups": [
              {
                "places": [
                  {
                    "location": {
                      "lat": 52.458232,
                      "lng": 13.338698
                    },
                    "duration": 600
                  }
                ],
                "position": {
                  "type": "ordered",
                  "value": 1
                },
                "demand": [
                  1
                ]
              }
            ]
          }
        }
      ]
    }
  }

In the given scenario, delivery tasks are designated with a position.type: ordered value of 2, whereas pickup tasks hold a corresponding position value of 1. This hierarchy suggests that pickup tasks should be prioritized over deliveries in cases where insufficient capacity prevents the fleet from serving all jobs, as demonstrated in the preceding problem in which the capacity setting is limited to 8 while the number of jobs to serve is 12.

However, the problem JSON reveals that delivery tasks are additionally assigned a priority value of 1, superseding the positional arrangement.

Solution

In the following solution, the optimization algorithm left four jobs unassigned, as the following figure demonstrates:

Priority and task position

All unassigned jobs were configured with a position value of 1. However, the algorithm prioritized the high-priority delivery jobs, despite their lower position value (2). This highlights the precedence of the priority setting over the position in use cases where there is insufficient capacity to serve all jobs.

The following section provides the full solution JSON file:

Click to expand/collapse the sample JSON
  {
    "statistic": {
      "cost": 73.91,
      "distance": 57009,
      "duration": 10782,
      "times": {
        "driving": 5982,
        "serving": 4800,
        "waiting": 0,
        "stopping": 0,
        "break": 0
      }
    },
    "tours": [
      {
        "vehicleId": "small_1",
        "typeId": "small",
        "stops": [
          {
            "time": {
              "arrival": "2023-05-28T08:00:00Z",
              "departure": "2023-05-28T08:00:00Z"
            },
            "load": [
              4
            ],
            "activities": [
              {
                "jobId": "departure",
                "type": "departure",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T08:00:00Z",
                  "end": "2023-05-28T08:00:00Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 0
          },
          {
            "time": {
              "arrival": "2023-05-28T08:18:12Z",
              "departure": "2023-05-28T08:28:12Z"
            },
            "load": [
              5
            ],
            "activities": [
              {
                "jobId": "Job_12_P_position-1",
                "type": "pickup",
                "location": {
                  "lat": 52.458232,
                  "lng": 13.338698
                },
                "time": {
                  "start": "2023-05-28T08:18:12Z",
                  "end": "2023-05-28T08:28:12Z"
                }
              }
            ],
            "location": {
              "lat": 52.458232,
              "lng": 13.338698
            },
            "distance": 11325
          },
          {
            "time": {
              "arrival": "2023-05-28T08:32:14Z",
              "departure": "2023-05-28T08:42:14Z"
            },
            "load": [
              6
            ],
            "activities": [
              {
                "jobId": "Job_11_P_position-1",
                "type": "pickup",
                "location": {
                  "lat": 52.441913,
                  "lng": 13.339028
                },
                "time": {
                  "start": "2023-05-28T08:32:14Z",
                  "end": "2023-05-28T08:42:14Z"
                }
              }
            ],
            "location": {
              "lat": 52.441913,
              "lng": 13.339028
            },
            "distance": 13451
          },
          {
            "time": {
              "arrival": "2023-05-28T08:45:45Z",
              "departure": "2023-05-28T08:55:45Z"
            },
            "load": [
              7
            ],
            "activities": [
              {
                "jobId": "Job_10_P_position-1",
                "type": "pickup",
                "location": {
                  "lat": 52.440807,
                  "lng": 13.351399
                },
                "time": {
                  "start": "2023-05-28T08:45:45Z",
                  "end": "2023-05-28T08:55:45Z"
                }
              }
            ],
            "location": {
              "lat": 52.440807,
              "lng": 13.351399
            },
            "distance": 15063
          },
          {
            "time": {
              "arrival": "2023-05-28T09:02:39Z",
              "departure": "2023-05-28T09:12:39Z"
            },
            "load": [
              8
            ],
            "activities": [
              {
                "jobId": "Job_8_P_position-1",
                "type": "pickup",
                "location": {
                  "lat": 52.458414,
                  "lng": 13.392079
                },
                "time": {
                  "start": "2023-05-28T09:02:39Z",
                  "end": "2023-05-28T09:12:39Z"
                }
              }
            ],
            "location": {
              "lat": 52.458414,
              "lng": 13.392079
            },
            "distance": 18904
          },
          {
            "time": {
              "arrival": "2023-05-28T09:19:24Z",
              "departure": "2023-05-28T09:29:24Z"
            },
            "load": [
              7
            ],
            "activities": [
              {
                "jobId": "Job_7_D_position-2",
                "type": "delivery",
                "location": {
                  "lat": 52.447476,
                  "lng": 13.433062
                },
                "time": {
                  "start": "2023-05-28T09:19:24Z",
                  "end": "2023-05-28T09:29:24Z"
                }
              }
            ],
            "location": {
              "lat": 52.447476,
              "lng": 13.433062
            },
            "distance": 22930
          },
          {
            "time": {
              "arrival": "2023-05-28T09:35:30Z",
              "departure": "2023-05-28T09:45:30Z"
            },
            "load": [
              6
            ],
            "activities": [
              {
                "jobId": "Job_6_D_position-2",
                "type": "delivery",
                "location": {
                  "lat": 52.434003,
                  "lng": 13.466142
                },
                "time": {
                  "start": "2023-05-28T09:35:30Z",
                  "end": "2023-05-28T09:45:30Z"
                }
              }
            ],
            "location": {
              "lat": 52.434003,
              "lng": 13.466142
            },
            "distance": 26487
          },
          {
            "time": {
              "arrival": "2023-05-28T09:59:55Z",
              "departure": "2023-05-28T10:09:55Z"
            },
            "load": [
              5
            ],
            "activities": [
              {
                "jobId": "Job_4_D_position-2",
                "type": "delivery",
                "location": {
                  "lat": 52.473537,
                  "lng": 13.505414
                },
                "time": {
                  "start": "2023-05-28T09:59:55Z",
                  "end": "2023-05-28T10:09:55Z"
                }
              }
            ],
            "location": {
              "lat": 52.473537,
              "lng": 13.505414
            },
            "distance": 34329
          },
          {
            "time": {
              "arrival": "2023-05-28T10:32:43Z",
              "departure": "2023-05-28T10:42:43Z"
            },
            "load": [
              4
            ],
            "activities": [
              {
                "jobId": "Job_1_D_position-2",
                "type": "delivery",
                "location": {
                  "lat": 52.56182,
                  "lng": 13.497167
                },
                "time": {
                  "start": "2023-05-28T10:32:43Z",
                  "end": "2023-05-28T10:42:43Z"
                }
              }
            ],
            "location": {
              "lat": 52.56182,
              "lng": 13.497167
            },
            "distance": 47136
          },
          {
            "time": {
              "arrival": "2023-05-28T10:59:42Z",
              "departure": "2023-05-28T10:59:42Z"
            },
            "load": [
              0
            ],
            "activities": [
              {
                "jobId": "arrival",
                "type": "arrival",
                "location": {
                  "lat": 52.50935,
                  "lng": 13.41997
                },
                "time": {
                  "start": "2023-05-28T10:59:42Z",
                  "end": "2023-05-28T10:59:42Z"
                }
              }
            ],
            "location": {
              "lat": 52.50935,
              "lng": 13.41997
            },
            "distance": 57009
          }
        ],
        "statistic": {
          "cost": 73.91,
          "distance": 57009,
          "duration": 10782,
          "times": {
            "driving": 5982,
            "serving": 4800,
            "waiting": 0,
            "stopping": 0,
            "break": 0
          }
        },
        "shiftIndex": 0
      }
    ],
    "unassigned": [
      {
        "jobId": "Job_2_P_position-1",
        "reasons": [
          {
            "code": "TOUR_ORDER_CONSTRAINT",
            "description": "cannot be assigned due to tour order constraint"
          }
        ]
      },
      {
        "jobId": "Job_5_P_position-1",
        "reasons": [
          {
            "code": "TOUR_ORDER_CONSTRAINT",
            "description": "cannot be assigned due to tour order constraint"
          }
        ]
      },
      {
        "jobId": "Job_9_P_position-1",
        "reasons": [
          {
            "code": "TOUR_ORDER_CONSTRAINT",
            "description": "cannot be assigned due to tour order constraint"
          }
        ]
      },
      {
        "jobId": "Job_3_P_position-1",
        "reasons": [
          {
            "code": "TOUR_ORDER_CONSTRAINT",
            "description": "cannot be assigned due to tour order constraint"
          }
        ]
      }
    ]
  }

Next steps

  • For more information about formulating problems in the HERE Tour Planning API, see Problem.
  • For an in-depth exploration of the HERE Tour Planning API methods, endpoints, and parameters, see the API Reference.