> ## Content Index
> Fetch the complete content index at: https://blog.afi.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# GMPRO docs: Territory optimization and route planning
- URL: https://blog.afi.io/blog/gmpro-docs-territory-optimization-and-route-planning/
- Published: 2024-09-09T04:33:29.000Z
- Updated: 2026-08-21T05:42:17.000Z
- Description: How to use the costsPerVehicle parameter to build a territory optimization system with GMPRO.
- Author: Afian Anwar
- Tags: googlemaps

In this short tutorial, I'm going to show you how to use the [Google Maps Platform Route Optimization API](https://developers.google.com/maps/documentation/route-optimization?ref=blog.afi.io) (GMPRO) `costsPerVehicle` parameter to build a territory optimization and route planning system for your logistics operation.

![](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/09/image.png)

Part 1: [GMPRO: Google Maps Platform route optimization API](https://afi.io/blog/gmpro-google-maps-platform-route-optimization-api/?ref=blog.afi.io)  
Part 2: [GMPRO TSP solver: Google Maps with more than 25 waypoints](https://afi.io/blog/gmpro-tsp-solver-google-maps-with-more-than-25-waypoints/?ref=blog.afi.io)  
Part 3: [Google Maps route optimization: multi vehicle](https://afi.io/blog/google-maps-route-optimization-multi-vehicle/?ref=blog.afi.io)  
Part 4: [Fleet routing app - free Google Maps route planner for multiple stops](https://www.afi.io/blog/fleet-routing-app-free-google-maps-route-planner-for-multiple-stops/?ref=blog.afi.io)  
Part 5: [GMPRO docs: Fixed vehicle costs](https://afi.io/blog/gmpro-docs-fixed-vehicle-costs/?ref=blog.afi.io)  
**Part 6: GMPRO docs: Territory optimization and route planning (this article)**  
Part 7: [GMPRO docs: Solving the VRP with route clustering and soft constraints](https://afi.io/blog/gmpro-docs-solving-the-vrp-with-route-clustering-and-soft-constraints/?ref=blog.afi.io)  
Part 8: [GMPRO docs: Driver load balancing with soft constraints](https://www.afi.io/blog/gmpro-docs-driver-load-balancing-with-soft-constraints/?ref=blog.afi.io)  
Part 9: [GMPRO docs: Driver breaks](https://www.afi.io/blog/gmpro-docs-driver-breaks/?ref=blog.afi.io)  
Part 10: [GMPRO docs: Complete deliveries before pickups in cargo bike logistics](https://www.afi.io/blog/gmpro-docs-complete-all-deliveries-before-pickups-in-cargo-bike-logistics/?ref=blog.afi.io)  
Part 11: [GMPRO docs: Force stop sequences using precedence rules](https://blog.afi.io/blog/gmpro-docs-force-stop-sequences-using-precedence-rules/)  
Part 12: [GMPRO docs: Cut cost / raise care with smart NEMT routing](https://blog.afi.io/blog/gmpro-docs-cut-cost-raise-care-with-smart-nemt-routing/)  
Part 13: [GMPRO docs: Routing for Demand Responsive Transport](https://blog.afi.io/blog/gmpro-docs-routing-for-demand-responsive-transport/)  
Part 14: [GMPRO docs: Squash durations and parking](https://blog.afi.io/blog/gmpro-docs-squash-durations-and-parking/)

### What is territory optimization?

Territory optimization is the strategic process of dividing a geographic area or market into specific regions, or "territories". It is commonly used by sales teams, logistics companies and field service operators (e.g. telecommunications, HVAC, or utility companies) to balance workloads, maximize coverage and improve geographic efficiency so that each territory is geographically compact and easily accessible, without overlap.

![Territory optimization involves fairly dividing a work area into distinct regions](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/09/Screen-Shot-2024-09-08-at-7.36.07-AM.png)

Territory optimization involves fairly dividing a work area into distinct regions

Territory optimization works by assigning each stop to a specific region. To determine the most efficient sequence of stops for each vehicle, you'll still need a route optimization API, like [GMPRO](https://developers.google.com/maps/documentation/route-optimization?ref=blog.afi.io)

### How territory optimization works in GMPRO

There are two main ways to incorporate territory optimization into a route optimization system. The first is by applying **hard constraints**. For instance, if you want to designate a `downtown` territory that only cargo bikes in your fleet can service and a `suburbs` territory for vans, trucks and other vehicle types, you should add a `downtown` constraint to the `loadDemands` field of `shipments` located downtown:

```
{
    "loadDemands": {
        "downtown": {
            "amount": "1"
        }
    }
}
```

In the corresponding `vehicles` object, you would add that same constraint to the `loadLimits` field like so:

```JSON
{
    "loadLimits": {
        "downtown": {
            "maxLoad": 5
        },
        "suburbs": {
            "maxLoad": 0
        }
    }
}
```

You also need to add a `"maxLoad": 0` for any other territories to ensure that only `vehicles` with a `downtown` `loadLimit` can handle `downtown` deliveries.

Alternatively, you can use use the `allowedVehicleIndices` constraint to achieve the same thing. For example, if the vehicles that can serve `downtown` are in position 1, 3 and 4 (the second, fourth and fifth elements) in the `vehicles` array, you can add:

```
{
    "allowedVehicleIndices": [
        1,
        3,
        4
    ]
}
```

to the `downtown` shipments. For a full list allowed parameters in the `shipment` object, see [this link](https://developers.google.com/maps/documentation/route-optimization/reference/rest/v1/ShipmentModel?ref=blog.afi.io#shipment).

The second way to approach territory optimization is to use **soft constraints**, which is the method I prefer.

💡

[GMPRO](https://developers.google.com/maps/documentation/route-optimization?ref=blog.afi.io) is only responsible for assigning vehicles to stops within their designated territories. If you want to define and visualize territories on a map, refer to this [Mapbox case study](https://www.mapbox.com/showcase/sotargruppen?ref=blog.afi.io) to see [how territories are mapped and assigned at Sotargruppen](https://www.mapbox.com/showcase/sotargruppen?ref=blog.afi.io). 

### Applying soft constraints to territory optimization in GMPRO

Unlike a hard constraint which will leave a `shipment` unserved if there are no suitable `vehicles` to deliver it, a soft constraint will try to find alternative `vehicles` to do the job, even if the resulting route solution isn't optimal. It's like saying, "Well, this isn't perfect, but this will just have to do." Applied to territory optimization, this means using the `costsPerVehicle` and `costsPerVehicleIndices` fields to selectively apply costs to `vehicles` that are unsuitable to handle the `shipment`.

👨‍💻

Screenshots in this blog post were taken with [GMPRO-viewer](https://gmpro-viewer.afi.dev/?ref=blog.afi.io) , using data imported via [GMPRO-json-converter](https://gmpro-json-converter.afi.dev/?ref=blog.afi.io). Both tools are free to use.

For example, let's say you have two regions, downtown Vancouver (`downtown`) and North Vancouver (`north-vancouver`) as shown in the map below.

![](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/09/Screenshot-2024-09-12-at-8.25.23-AM.png)

You have a single `shipment` to deliver downtown, and two `vehicles`, `veh-cargobike` which is a cargo bike and `veh-sprintervan` which is a large [Mercedes Sprinter Van](https://www.mbvans.com/en/sprinter?ref=blog.afi.io). The package is quite small, so either vehicle would work but the main problem is parking - it is difficult and time consuming for the sprinter van to find curbside parking downtown, so you would rather the sprinter van not do the delivery. To model this, first attach both `vehicles` to the GMPRO request like so:

```JSON
{
    "vehicles": [
        {
            "travelMode": "DRIVING",
            "startLocation": {
                "latitude": 49.2808422,
                "longitude": -123.0827831
            },
            "startTimeWindows": [
                {
                    "startTime": "2024-07-08T16:00:00Z"
                }
            ],
            "endTimeWindows": [
                {
                    "endTime": "2024-07-08T18:00:00Z"
                }
            ],
            "loadLimits": {
                "weight": {
                    "maxLoad": 5
                }
            },
            "label": "veh-cargobike",
            "costPerKilometer": 1
        },
        {
            "travelMode": "DRIVING",
            "startLocation": {
                "latitude": 49.2658769,
                "longitude": -123.0815619
            },
            "startTimeWindows": [
                {
                    "startTime": "2024-07-08T16:00:00Z"
                }
            ],
            "endTimeWindows": [
                {
                    "endTime": "2024-07-08T18:00:00Z"
                }
            ],
            "loadLimits": {
                "weight": {
                    "maxLoad": 5
                }
            },
            "label": "veh-sprintervan",
            "costPerKilometer": 1
        }
    ]
}
```

Note that `veh-cargobike` is in position 0 of the `vehicles` array and `veh-sprintervan` is in position 1\. 

Similarly, on the `shipments` array, we need to set up the `costsPerVehicles` and `costsPerVehicleIndices` array like this:

```JSON
{
    "costsPerVehicle": [
        0,
        50
    ],
    "costsPerVehicleIndices": [
        0,
        1
    ]
}
```

Doing so attaches an additional cost of 50 to the `vehicle` in position 1 of the `vehicles` array (`veh-sprintervan`), making it less likely that GMPRO will assign `veh-sprintervan` to do the delivery. This is exactly what we want.

### `costPerVehicle` / `costPerVehicleIndices` worked example

Putting everything together, here's the full GMPRO request for our 1 `shipment` / 2 `vehicle` example.

![Territory optimization applied to downtown Vancouver for a single vehicle](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/09/image-1.png)

Territory optimization applied to downtown Vancouver for a single vehicle

**Input**

```JSON
{
    "model": {
        "shipments": [
            {
                "deliveries": [
                    {
                        "arrivalLocation": {
                            "latitude": 49.2912037,
                            "longitude": -123.1362474
                        },
                        "timeWindows": [
                            {
                                "startTime": "2024-07-08T16:00:00Z",
                                "endTime": "2024-07-08T18:00:00Z"
                            }
                        ],
                        "duration": "600s"
                    }
                ],
                "loadDemands": {
                    "weight": {
                        "amount": 1
                    }
                },
                "costsPerVehicle": [
                    0,
                    50
                ],
                "costsPerVehicleIndices": [
                    0,
                    1
                ],
                "label": "dt123"
            }
        ],
        "vehicles": [
            {
                "travelMode": "DRIVING",
                "startLocation": {
                    "latitude": 49.2668756,
                    "longitude": -123.0800413
                },
                "startTimeWindows": [
                    {
                        "startTime": "2024-07-08T16:00:00Z"
                    }
                ],
                "endTimeWindows": [
                    {
                        "endTime": "2024-07-08T18:00:00Z"
                    }
                ],
                "loadLimits": {
                    "weight": {
                        "maxLoad": 5
                    }
                },
                "label": "veh-cargobike",
                "costPerKilometer": 1
            },
            {
                "travelMode": "DRIVING",
                "startLocation": {
                    "latitude": 49.2808422,
                    "longitude": -123.0802082
                },
                "startTimeWindows": [
                    {
                        "startTime": "2024-07-08T16:00:00Z"
                    }
                ],
                "endTimeWindows": [
                    {
                        "endTime": "2024-07-08T18:00:00Z"
                    }
                ],
                "loadLimits": {
                    "weight": {
                        "maxLoad": 5
                    }
                },
                "label": "veh-sprintervan",
                "costPerKilometer": 1
            }
        ],
        "globalStartTime": "2024-07-08T16:00:00Z",
        "globalEndTime": "2024-07-09T02:59:59Z"
    },
    "populatePolylines": true
}

```

**Output**

```JSON
{
    "routes": [
        {
            "vehicleLabel": "veh-cargobike",
            "vehicleStartTime": "2024-07-08T16:00:00Z",
            "vehicleEndTime": "2024-07-08T16:27:46Z",
            "visits": [
                {
                    "startTime": "2024-07-08T16:17:46Z",
                    "detour": "0s",
                    "shipmentLabel": "dt123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                }
            ],
            "transitions": [
                {
                    "travelDuration": "1066s",
                    "travelDistanceMeters": 6141,
                    "waitDuration": "0s",
                    "totalDuration": "1066s",
                    "startTime": "2024-07-08T16:00:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "1"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T16:27:46Z",
                    "vehicleLoads": {
                        "weight": {}
                    }
                }
            ],
            "routePolyline": {
                "points": "ijukHdbfnV[pDG|@Z?J?FA|@EHAV?J?xA@`@@?TAVCXCVUfBOxAQtAOvAALOpACN?@CNQ`B]~CK|@OpAIv@QbBEXEXIt@?DCNShBQtA?BABK~@EXCXA`@A\\ATAV?@?T?N?tC?v@At@?bB?b@A\\?@?f@?J?lAAb@?`B?z@?nAA\\?xC?t@?f@A`@AVAPE`@CNCPAHI^ADEVI\\KZKZKXCBKRyA|CcAtBm@t@}@lBa@z@MTIRITKX?@ERK`@qBEw@EM?QAQ?s@Ca@?WCEAOAOEKAGCKE[Oo@YOG[OQGMCKCKAWCM?eBE]Ai@AM?m@AG?cACE?iCEkAAUAsFIsBEY?m@Aa@Au@A@b@BlB?R@~@BbB?l@@t@@v@@\\?Z?@@t@@J@t@?N@X?@?Z?X?t@?H?XAv@Al@Cv@Ad@Eh@GdAC`@OdBCLGb@GXWtAS~@Qn@IVGRA@CFWt@a@~@IV]l@[f@c@|@[h@IPMRUb@INIRIJGHIJc@t@CFA@AH_@p@i@|@s@nA_BjCINS^OXKPININMPCFMTINKPWd@_A`Bg@z@Wd@}@dBIJa@v@GJGLcAbBCFa@t@MRKRw@tACDUb@m@`Aa@t@]j@m@bAOXUb@i@|@IP{@zAc@z@M\\GREPCLAJEZGp@CXKrACTETERA@GVEJEHMVYl@_@n@INOVILYd@q@nAYd@?@Wd@gAhBIRCFyAfCaAfBCDQXGHCNAL?F@D?B@B@@@DBBBDh@~@r@jADHRZFNHNDF?@v@rAdAhBl@bA@@LRJPYd@q@lAgB~CiBbDCDc@t@EFsCbFGJ[l@yAjCa@r@o@jAiB~Cs@lAo@lAb@r@b@t@Wf@"
            },
            "metrics": {
                "performedShipmentCount": 1,
                "travelDuration": "1066s",
                "waitDuration": "0s",
                "delayDuration": "0s",
                "breakDuration": "0s",
                "visitDuration": "600s",
                "totalDuration": "1666s",
                "travelDistanceMeters": 6141,
                "maxLoads": {
                    "weight": {
                        "amount": "1"
                    }
                }
            },
            "routeCosts": {
                "model.vehicles.cost_per_kilometer": 6.141
            },
            "routeTotalCost": 6.141
        },
        {
            "vehicleIndex": 1,
            "vehicleLabel": "veh-sprintervan"
        }
    ],
    "metrics": {
        "aggregatedRouteMetrics": {
            "performedShipmentCount": 1,
            "travelDuration": "1066s",
            "waitDuration": "0s",
            "delayDuration": "0s",
            "breakDuration": "0s",
            "visitDuration": "600s",
            "totalDuration": "1666s",
            "travelDistanceMeters": 6141,
            "maxLoads": {
                "weight": {
                    "amount": "1"
                }
            }
        },
        "usedVehicleCount": 1,
        "earliestVehicleStartTime": "2024-07-08T16:00:00Z",
        "latestVehicleEndTime": "2024-07-08T16:27:46Z",
        "totalCost": 6.141,
        "costs": {
            "model.vehicles.cost_per_kilometer": 6.141
        }
    }
}

```

As expected, GMPRO assigns `veh-cargovan` for the delivery. Now, let's explore a more practical example of how territory optimization works in a real-world scenario.

### GMPRO territory optimization worked example

Just as in the earlier example, we'll be using our 2 `vehicles`, `veh-cargobike` and `veh-sprintervan`. But this time, we are going to give them 8 `shipments` to deliver. 3 in downtown Vancouver, 3 in North Vancouver and 2 "on the way". The `shipments` in downtown Vancouver and North Vancouver have had their `costPerVehicle` array set up to prefer `veh-cargobike` and `veh-sprintervan` respectively, while the remaining 2 `shipments` are "open" (they do not contain a `costPerVehicle` array), and can be delivered by either vehicle.

![Territory optimization applied to downtown and North Vancouver for two vehicles](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/09/Screenshot-2024-09-11-at-8.29.25-PM.png)

Territory optimization applied to downtown and North Vancouver for two vehicles

**Input**

```JSON
{
  "model": {
    "shipments": [
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.31374169999999,
              "longitude": -123.0514387
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            50,
            0
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "nv789"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.343481,
              "longitude": -123.0863414
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            50,
            0
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "nv987"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.309186,
              "longitude": -123.033168
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            50,
            0
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "nv123"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2912037,
              "longitude": -123.1362474
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            0,
            50
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "dt123"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2786079,
              "longitude": -123.1141059
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            0,
            50
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "dt456"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2771031,
              "longitude": -123.1088116
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            0,
            50
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "dt789"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2812657,
              "longitude": -123.0280715
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "label": "ev123"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2580983,
              "longitude": -123.1146481
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "label": "ev456"
      }
    ],
    "vehicles": [
      {
        "travelMode": "DRIVING",
        "startLocation": {
          "latitude": 49.2668756,
          "longitude": -123.0800413
        },
        "startTimeWindows": [
          {
            "startTime": "2024-07-08T16:00:00Z"
          }
        ],
        "endTimeWindows": [
          {
            "endTime": "2024-07-08T18:00:00Z"
          }
        ],
        "loadLimits": {
          "weight": {
            "maxLoad": 5
          }
        },
        "label": "veh-cargobike",
        "costPerKilometer": 1
      },
      {
        "travelMode": "DRIVING",
        "startLocation": {
          "latitude": 49.2808422,
          "longitude": -123.0802082
        },
        "startTimeWindows": [
          {
            "startTime": "2024-07-08T16:00:00Z"
          }
        ],
        "endTimeWindows": [
          {
            "endTime": "2024-07-08T18:00:00Z"
          }
        ],
        "loadLimits": {
          "weight": {
            "maxLoad": 5
          }
        },
        "label": "veh-sprintervan",
        "costPerKilometer": 1
      }
    ],
    "globalStartTime": "2024-07-08T16:00:00Z",
    "globalEndTime": "2024-07-09T02:59:59Z"
  },
  "populatePolylines": true
}
```

**Output**

```JSON
{
    "routes": [
        {
            "vehicleLabel": "veh-cargobike",
            "vehicleStartTime": "2024-07-08T16:00:00Z",
            "vehicleEndTime": "2024-07-08T17:10:29Z",
            "visits": [
                {
                    "shipmentIndex": 7,
                    "startTime": "2024-07-08T16:10:08Z",
                    "detour": "0s",
                    "shipmentLabel": "ev456",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 5,
                    "startTime": "2024-07-08T16:25:58Z",
                    "detour": "1010s",
                    "shipmentLabel": "dt789",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 4,
                    "startTime": "2024-07-08T16:40:24Z",
                    "detour": "1835s",
                    "shipmentLabel": "dt456",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 3,
                    "startTime": "2024-07-08T17:00:29Z",
                    "detour": "2563s",
                    "shipmentLabel": "dt123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                }
            ],
            "transitions": [
                {
                    "travelDuration": "608s",
                    "travelDistanceMeters": 3818,
                    "waitDuration": "0s",
                    "totalDuration": "608s",
                    "startTime": "2024-07-08T16:00:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "4"
                        }
                    }
                },
                {
                    "travelDuration": "350s",
                    "travelDistanceMeters": 2313,
                    "waitDuration": "0s",
                    "totalDuration": "350s",
                    "startTime": "2024-07-08T16:20:08Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "3"
                        }
                    }
                },
                {
                    "travelDuration": "266s",
                    "travelDistanceMeters": 1355,
                    "waitDuration": "0s",
                    "totalDuration": "266s",
                    "startTime": "2024-07-08T16:35:58Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "2"
                        }
                    }
                },
                {
                    "travelDuration": "605s",
                    "travelDistanceMeters": 2737,
                    "waitDuration": "0s",
                    "totalDuration": "605s",
                    "startTime": "2024-07-08T16:50:24Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "1"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T17:10:29Z",
                    "vehicleLoads": {
                        "weight": {}
                    }
                }
            ],
            "routePolyline": {
                "points": "ijukHdbfnV[pDG|@Z?J?FA|@EHAV?J?xA@`@@nDI?@?@?@?@@??@?@?@@??@?@@??@@??@@?@??@@?@??A@?@??A@??A@A?A@??A?A?A?A?Ab@?h@@l@?n@@xA@z@@@?`@?AdK?r@CnI?`@?J?f@?fAA^?l@@hAKZ?@?@Af@?vA?tB?jB?jAP@zA@JADA@?bA@zAAP@x@?H@zA@|A?xA@@?vA@zA@rA@VDR@Z@@@DBFBBBNHDBNHTJb@`@f@b@VPVBXD?j@CzA?bAAv@?JA`BAnAAlC?n@AtBC`D?x@CxAAjA?dAAxA?xB?bB?dDClB?zAAbC?lB?h@@hAApA?fA?LCfFAzDAvCAl@CxCEtE?V?RGvFGrFC`EAfBAbCAbCCfCEpEA^APAr@?t@{A?}AAs@@c@@@kAAjAQ?g@Ac@?qCGQCA?g@CkB@KAQ@uAAgAAaACQ?M?y@GQA[Ac@Au@AIAc@Am@CUAo@?eB?aBAsAEkAAq@DU@c@?S?m@@e@@G?I?K?cA?}@B_AB]B]@o@@o@B_BBG@q@@kA@G?e@GsDGeAA_@?C?E?E?QAQ?C?yAEqACYAW?i@Ag@E]?]AWMMEMAIAMAA?OACAKAIAEAEAKESKMGKKKKEEACACIKMWGMEKEOGQCGq@_CKWEOEMCICKGMGMGEMKq@mBIUIUACGQGM?AIQi@wAEIKU_@aAEMGOUi@O_@k@wAMa@Si@M[IQIWGQIi@I_@E[C]CYCe@[AM?E?oAC_@A[?MAU?c@Ac@CE?EREPARCRAP?N?P?N@P@NBNBT@DBNDR@@DPbB`EPb@BHBH@PFTBH?@BL@J?H@B?H@F?P@R?JAJAVCZCZAV?X?VBXBXFVNp@FVHVLXLZN\\HRJRNTBFBDHJHF@BNJBBHDPDPDr@PHBFFHHb@l@PVRXFJJPNTUJSJKHSRKLQNKPIPGJCBEJCHMTg@|@[l@k@`Ak@_AgBaD_A_BYe@_@q@e@y@d@x@^p@Xd@~@~AfB`Dj@~@MRQXa@p@Wf@[n@w@pAMX_AbB}@|Ag@|@[h@a@r@EHQXm@fAQX}@~A_A`BMVEFo@nA{@|AOVQX[f@Yd@S\\INEFEF}@~AeAnBMROREHGHEFA@GFGHIFGFIDIFIDGDE@UH_@JQHQFQHOLMJA@KLC@MRORQZdAjB\\h@Zj@NPw@pAm@hAs@lAS`@iB`Ds@lA?@Yd@_CbEGNo@jAoBhDAAAACAA??@C?AB?BAB?B?B?B@BQZWf@s@lAYf@Wf@mArB?@w@tAUZy@|Ao@jAa@r@u@rAEHm@dA}CrFeAeBgAkBk@aAYg@Wf@"
            },
            "metrics": {
                "performedShipmentCount": 4,
                "travelDuration": "1829s",
                "waitDuration": "0s",
                "delayDuration": "0s",
                "breakDuration": "0s",
                "visitDuration": "2400s",
                "totalDuration": "4229s",
                "travelDistanceMeters": 10223,
                "maxLoads": {
                    "weight": {
                        "amount": "4"
                    }
                }
            },
            "routeCosts": {
                "model.vehicles.cost_per_kilometer": 10.223
            },
            "routeTotalCost": 10.223
        },
        {
            "vehicleIndex": 1,
            "vehicleLabel": "veh-sprintervan",
            "vehicleStartTime": "2024-07-08T16:00:00Z",
            "vehicleEndTime": "2024-07-08T17:09:55Z",
            "visits": [
                {
                    "shipmentIndex": 6,
                    "startTime": "2024-07-08T16:09:00Z",
                    "detour": "0s",
                    "shipmentLabel": "ev123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 2,
                    "startTime": "2024-07-08T16:25:32Z",
                    "detour": "741s",
                    "shipmentLabel": "nv123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "startTime": "2024-07-08T16:40:02Z",
                    "detour": "1578s",
                    "shipmentLabel": "nv789",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 1,
                    "startTime": "2024-07-08T16:59:55Z",
                    "detour": "2555s",
                    "shipmentLabel": "nv987",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                }
            ],
            "transitions": [
                {
                    "travelDuration": "540s",
                    "travelDistanceMeters": 3810,
                    "waitDuration": "0s",
                    "totalDuration": "540s",
                    "startTime": "2024-07-08T16:00:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "4"
                        }
                    }
                },
                {
                    "travelDuration": "392s",
                    "travelDistanceMeters": 5041,
                    "waitDuration": "0s",
                    "totalDuration": "392s",
                    "startTime": "2024-07-08T16:19:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "3"
                        }
                    }
                },
                {
                    "travelDuration": "270s",
                    "travelDistanceMeters": 1902,
                    "waitDuration": "0s",
                    "totalDuration": "270s",
                    "startTime": "2024-07-08T16:35:32Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "2"
                        }
                    }
                },
                {
                    "travelDuration": "593s",
                    "travelDistanceMeters": 7110,
                    "waitDuration": "0s",
                    "totalDuration": "593s",
                    "startTime": "2024-07-08T16:50:02Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "1"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T17:09:55Z",
                    "vehicleLoads": {
                        "weight": {}
                    }
                }
            ],
            "routePolyline": {
                "points": "kdxkH|afnVDCDG@K?Ai@S?SAICWCa@Gc@B_@EWQ}@CMAKAME[AS?IAQAc@?g@?kA?_@@_@?[BsF?C?w@@w@?iEBc@D_GBiE@eD?u@?G?_@?y@@w@?U?S?W?S?m@?O?uB?[@mC@wC?e@@eB@mBA_FA_DA_@Ki@?g@@sG?u@@sG?w@@_K@yD?s@?_@?uE@}@?w@?q@@G@K?iF?kA?aE?w@?y@?mH?_@?sA?cA?oA?G?A?w@?{EAm@DKB_@@eB@kE?qE?mB?e@?U?U@oBAqA?AIi@AcB?}A?Y?i@?Y?i@?K?_@?W?]?W?_@?oA?_A?kB?eE?w@?mB?w@?]?uE?q@?K?mBFg@@_@?E?}@?_A@}A?cB?yA?Y?[AgA@s@?sE?c@Ae@Gi@@qA?qA?]?O?N?\\?pAApAIl@?x@A~A?`BAp@Ol@IVINMP[?i@?oB@mB?}A?k@?O?U?k@?wD@i@?g@?a@@Y?A?_@C]CI?UG[ISIMGMGOGOIOKIGIGKIWSWW[][]QSQAY_@a@i@OSA?QUQUSWQUCEKMSUQUOUQUQSU[OQIMGGOSUYMOQUMOU]QSQUe@m@QUQU_@e@CCOQUUWWe@[ECOGYKUEUEk@Ee@AS?C?u@@e@?a@@_@?E?m@@{@@o@?S@[?W?Q?e@@e@?_@?E?c@@]?C?e@@g@?Y@E?a@?s@@e@?c@@a@?O?_A@eA@k@@{@?{@@u@@_@?{@@m@?W@S?M?U?W@S?cA?cEDI?c@?[@i@?W@_@?[@Q?o@?a@@Q?e@@Y@M@M@YBQBa@FKBKBODMBKD[Hc@Re@Pc@XOJYPQJSLc@XWLOHQFOFIBYJSHMDMBQDSBUBWBk@@[?Y?O?C?K?Y?G?o@AUAS?_BCO?o@AY?k@AW?U?SAi@?]Ai@AW?k@AW?S?UA[[C?EAUCEAOCSCQEMCKEOGSMSUCEACCCCGEIAG?AAAAKAA?EACAM?KAAEMEOi@sBe@eAY]QUMEECQIUQ[K?`@BjA@RFtADv@BhAD`@B\\DTNv@Lj@DNFVFVLd@Nn@FZFT?BDTFf@?p@?ZALAJCTEPITELGLEFEFGHEFABABABAD?De@TMFMFWNKLGLKPEJGPM\\IRQn@Md@CNITA@ETEJAHANAZA^^CR@^B\\@d@B`@?N?L?J@F?|A?pAEtA?`Cm@t@QPE`AU?fD?b@?c@?gDaATQDu@PaCl@uA?qAD}A?G?KAM?O?a@?e@C]A_@CSA_@BIh@El@?X?B?hA?D?X@|@A^?b@@lBC`F?X?JCbKAzAAPEr@?BAr@?@?J?j@?h@?bA?d@Ab@ArBAvA?v@?t@?~@?p@?lECbA?lA?N@HBJ?v@?X?^Av@AfBAbCAjDA`ACtD?nBAhB?ZApAAnB?v@A~@MWKSAA@@JRLV@_A?w@@oB@qA?[@iB?oBBuD@aA@kD@cC@gB@w@?_@?Y?w@BG@E@EBQ?eA?i@BeD?M?aB@mABcDAcB?K?a@@a@QCi@IWICC_@OAAGEQKEE[Wg@q@c@w@Wg@ACIOOYO[q@mAYg@_@q@k@iAi@gASVi@dACF]h@A@CFKLABY^i@p@_@d@i@d@ABABQh@_BjAoA|@aAr@}@n@}@n@{@l@e@\\UNiAx@[TWP{@l@q@f@{@l@w@j@UP}@l@i@^e@\\oA|@YREB]V[Ty@j@c@\\[Ri@`@w@h@IHOLc@^e@f@]`@UZEFYd@QZMT_@x@Sf@ELGNQd@K\\IZGRI`@A@Kh@I`@IZG\\Qz@Kh@Or@Mx@UdAI`@SdAKb@ERGXEPI^GTGTWz@KVQf@KXMZO\\[p@S`@INOXILOTKLMPORILOPQRSVQPQPWTYXA@KHSP]XQNURA@URQNONURQP_@b@Y^SVCBOROVQTEHGL]j@O\\INWh@ABM\\M\\O`@ELITGTGNQl@K`@K`@K`@G^I`@EPCPO~@CTGb@ANCL?@Gj@C`@CPAPEh@A\\ATAZ?^?DA\\?`@@d@?b@@d@?^@L?X@~@BlA@bA@R@v@@jABhA?N?T@^@h@?`@@d@@`@?x@?d@?b@?r@?~@?^?n@AD?v@?l@?d@?zAAvA?F?z@@fA?j@?dA@bBAbA?lAAzA?r@?n@?R?H?h@?@A`A?l@?bA?j@A\\?V?n@?|@?l@AjA?j@?\\ArD?fA?p@?TAfA?hBCnBAlAAnAAz@AdAApAAp@?B?l@?^?fA?h@@X@tC@t@?P?P?H@t@@fA?x@@f@?p@OZA@?BExB?DCtACx@?@?n@A\\?B?X?nA?B@l@?p@?j@?XATA|@Ox@CLCDCDEHQTi@?cCDQPkA@wBCeAAA?iACO?w@?iB?_BCeACA?cBCS?A?wC@U?wAAq@Be@@]AMAIAICGCIEICGEGEIGiCcBuDcCcBkAkAu@gAo@qAy@On@It@Iz@INCH"
            },
            "metrics": {
                "performedShipmentCount": 4,
                "travelDuration": "1795s",
                "waitDuration": "0s",
                "delayDuration": "0s",
                "breakDuration": "0s",
                "visitDuration": "2400s",
                "totalDuration": "4195s",
                "travelDistanceMeters": 17863,
                "maxLoads": {
                    "weight": {
                        "amount": "4"
                    }
                }
            },
            "routeCosts": {
                "model.vehicles.cost_per_kilometer": 17.863
            },
            "routeTotalCost": 17.863
        }
    ],
    "metrics": {
        "aggregatedRouteMetrics": {
            "performedShipmentCount": 8,
            "travelDuration": "3624s",
            "waitDuration": "0s",
            "delayDuration": "0s",
            "breakDuration": "0s",
            "visitDuration": "4800s",
            "totalDuration": "8424s",
            "travelDistanceMeters": 28086,
            "maxLoads": {
                "weight": {
                    "amount": "4"
                }
            }
        },
        "usedVehicleCount": 2,
        "earliestVehicleStartTime": "2024-07-08T16:00:00Z",
        "latestVehicleEndTime": "2024-07-08T17:10:29Z",
        "totalCost": 28.086,
        "costs": {
            "model.vehicles.cost_per_kilometer": 28.086
        }
    }
}

```

As we hoped, both the `downtown` and `north-vancouver` regions are served by `veh-cargobike` and `veh-sprintervan` respectively. Of the remaining 2 deliveries in East Vancouver, one was assigned to `veh-cargobike` and the other to `veh-sprintervan`, depending on which was passing through.

### Testing territory optimization soft constraints in GMPRO

At the beginning of this post, I mentioned that I prefer territory optimization with soft constraints because it allows drivers to assist in other territories when needed. Here's the same 8 `shipment` / 2 `vehicle` example but with a capacity constraint on `veh-cargobike` so that it can only do 2 deliveries instead of the original 5.

```JSON
{
    "loadLimits": {
        "weight": {
            "maxLoad": 2
        }
    },
    "label": "veh-cargobike"
}
```

**Input**

```
{
  "model": {
    "shipments": [
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.31374169999999,
              "longitude": -123.0514387
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            50,
            0
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "nv789"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.343481,
              "longitude": -123.0863414
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            50,
            0
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "nv987"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.309186,
              "longitude": -123.033168
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            50,
            0
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "nv123"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2912037,
              "longitude": -123.1362474
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            0,
            50
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "dt123"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2786079,
              "longitude": -123.1141059
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            0,
            50
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "dt456"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2771031,
              "longitude": -123.1088116
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "costsPerVehicle": [
            0,
            50
        ],
        "costsPerVehicleIndices": [
            0,
            1
        ],
        "label": "dt789"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2812657,
              "longitude": -123.0280715
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "label": "ev123"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2580983,
              "longitude": -123.1146481
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "label": "ev456"
      }
    ],
    "vehicles": [
      {
        "travelMode": "DRIVING",
        "startLocation": {
          "latitude": 49.2668756,
          "longitude": -123.0800413
        },
        "startTimeWindows": [
          {
            "startTime": "2024-07-08T16:00:00Z"
          }
        ],
        "endTimeWindows": [
          {
            "endTime": "2024-07-08T18:00:00Z"
          }
        ],
        "loadLimits": {
          "weight": {
            "maxLoad": 2
          }
        },
        "label": "veh-cargobike",
        "costPerKilometer": 1
      },
      {
        "travelMode": "DRIVING",
        "startLocation": {
          "latitude": 49.2808422,
          "longitude": -123.0802082
        },
        "startTimeWindows": [
          {
            "startTime": "2024-07-08T16:00:00Z"
          }
        ],
        "endTimeWindows": [
          {
            "endTime": "2024-07-08T18:00:00Z"
          }
        ],
        "loadLimits": {
          "weight": {
            "maxLoad": 6
          }
        },
        "label": "veh-sprintervan",
        "costPerKilometer": 1
      }
    ],
    "globalStartTime": "2024-07-08T16:00:00Z",
    "globalEndTime": "2024-07-09T02:59:59Z"
  },
  "populatePolylines": true
}
```

**Output**

```JSON
{
    "routes": [
        {
            "vehicleLabel": "veh-cargobike",
            "vehicleStartTime": "2024-07-08T16:00:00Z",
            "vehicleEndTime": "2024-07-08T16:39:54Z",
            "visits": [
                {
                    "shipmentIndex": 4,
                    "startTime": "2024-07-08T16:09:49Z",
                    "detour": "0s",
                    "shipmentLabel": "dt456",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 3,
                    "startTime": "2024-07-08T16:29:54Z",
                    "detour": "728s",
                    "shipmentLabel": "dt123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                }
            ],
            "transitions": [
                {
                    "travelDuration": "589s",
                    "travelDistanceMeters": 3955,
                    "waitDuration": "0s",
                    "totalDuration": "589s",
                    "startTime": "2024-07-08T16:00:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "2"
                        }
                    }
                },
                {
                    "travelDuration": "605s",
                    "travelDistanceMeters": 2737,
                    "waitDuration": "0s",
                    "totalDuration": "605s",
                    "startTime": "2024-07-08T16:19:49Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "1"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T16:39:54Z",
                    "vehicleLoads": {
                        "weight": {}
                    }
                }
            ],
            "routePolyline": {
                "points": "ijukHdbfnV[pDG|@Z?J?FA|@EHAV?J?xA@`@@?TAVCXCVUfBOxAQtAOvAALOpACN?@CNQ`B]~CK|@OpAIv@QbBEXEXIt@?DCNShBQtA?BABK~@EXCXA`@A\\ATAV?@?T?N?tC?v@At@?bB?b@A\\?@?f@?J?lAAb@?`B?z@?nAA\\?xC?t@?f@A`@AVAPE`@CNCPAHI^ADEVI\\KZKZKXCBKRyA|CcAtBm@t@}@lBa@z@MTIRITKX?@ERK`@qBEw@EM?QAQ?s@Ca@?WCEAOAOEKAGCKE[Oo@YOG[OQGMCKCKAWCM?eBE]Ai@AM?m@AG?cACE?iCEkAAUAsFIsBEY?m@Aa@Au@A@b@BlB?R@~@BbB?l@@t@@v@@\\?Z?@@t@@J@t@?N@X?@?Z?X?t@?H?XAv@Al@Cv@Ad@Eh@GdAC`@OdBCLGb@GXWtAS~@Qn@IVGRA@CFWt@a@~@IV]l@[f@c@|@[h@IPMRUb@INIRIJGHIJc@t@CFA@AH_@p@i@|@s@nAfAnB~AnCZf@\\h@d@x@DHXf@x@tAd@x@^p@Xd@~@~AfB`Dj@~@MRQXa@p@Wf@[n@w@pAMX_AbB}@|Ag@|@[h@a@r@EHQXm@fAQX}@~A_A`BMVEFo@nA{@|AOVQX[f@Yd@S\\INEFEF}@~AeAnBMROREHGHEFA@GFGHIFGFIDIFIDGDE@UH_@JQHQFQHOLMJA@KLC@MRORQZdAjB\\h@Zj@NPw@pAm@hAs@lAS`@iB`Ds@lA?@Yd@_CbEGNo@jAoBhDAAAACAA??@C?AB?BAB?B?B?B@BQZWf@s@lAYf@Wf@mArB?@w@tAUZy@|Ao@jAa@r@u@rAEHm@dA}CrFeAeBgAkBk@aAYg@Wf@"
            },
            "metrics": {
                "performedShipmentCount": 2,
                "travelDuration": "1194s",
                "waitDuration": "0s",
                "delayDuration": "0s",
                "breakDuration": "0s",
                "visitDuration": "1200s",
                "totalDuration": "2394s",
                "travelDistanceMeters": 6692,
                "maxLoads": {
                    "weight": {
                        "amount": "2"
                    }
                }
            },
            "routeCosts": {
                "model.vehicles.cost_per_kilometer": 6.692
            },
            "routeTotalCost": 6.692
        },
        {
            "vehicleIndex": 1,
            "vehicleLabel": "veh-sprintervan",
            "vehicleStartTime": "2024-07-08T16:00:00Z",
            "vehicleEndTime": "2024-07-08T17:53:47Z",
            "visits": [
                {
                    "shipmentIndex": 7,
                    "startTime": "2024-07-08T16:14:02Z",
                    "detour": "0s",
                    "shipmentLabel": "ev456",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 5,
                    "startTime": "2024-07-08T16:29:52Z",
                    "detour": "1339s",
                    "shipmentLabel": "dt789",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 6,
                    "startTime": "2024-07-08T16:52:52Z",
                    "detour": "2632s",
                    "shipmentLabel": "ev123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 2,
                    "startTime": "2024-07-08T17:09:24Z",
                    "detour": "3373s",
                    "shipmentLabel": "nv123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "startTime": "2024-07-08T17:23:54Z",
                    "detour": "4210s",
                    "shipmentLabel": "nv789",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 1,
                    "startTime": "2024-07-08T17:43:47Z",
                    "detour": "5187s",
                    "shipmentLabel": "nv987",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                }
            ],
            "transitions": [
                {
                    "travelDuration": "842s",
                    "travelDistanceMeters": 5856,
                    "waitDuration": "0s",
                    "totalDuration": "842s",
                    "startTime": "2024-07-08T16:00:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "6"
                        }
                    }
                },
                {
                    "travelDuration": "350s",
                    "travelDistanceMeters": 2313,
                    "waitDuration": "0s",
                    "totalDuration": "350s",
                    "startTime": "2024-07-08T16:24:02Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "5"
                        }
                    }
                },
                {
                    "travelDuration": "780s",
                    "travelDistanceMeters": 6366,
                    "waitDuration": "0s",
                    "totalDuration": "780s",
                    "startTime": "2024-07-08T16:39:52Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "4"
                        }
                    }
                },
                {
                    "travelDuration": "392s",
                    "travelDistanceMeters": 5041,
                    "waitDuration": "0s",
                    "totalDuration": "392s",
                    "startTime": "2024-07-08T17:02:52Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "3"
                        }
                    }
                },
                {
                    "travelDuration": "270s",
                    "travelDistanceMeters": 1902,
                    "waitDuration": "0s",
                    "totalDuration": "270s",
                    "startTime": "2024-07-08T17:19:24Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "2"
                        }
                    }
                },
                {
                    "travelDuration": "593s",
                    "travelDistanceMeters": 7110,
                    "waitDuration": "0s",
                    "totalDuration": "593s",
                    "startTime": "2024-07-08T17:33:54Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "1"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T17:53:47Z",
                    "vehicleLoads": {
                        "weight": {}
                    }
                }
            ],
            "routePolyline": {
                "points": "kdxkH|afnVDCDG@K?A?G@GDIBAJ?b@A?eA@eABCBC@C@G@E@E@_B@w@BeD?u@vA@|@?ZBvA@vAB|A@|A?vA?vAD|A@rA@xDBd@?t@@tA?jB?lA@tEBjB@R@fA@f@BRFNBV@h@Bh@B|@@^@vA@vA?r@ERALCPI`@?j@?xD@`@@r@@f@@Z?^@R?T?Z?J@N?V?n@@v@BpABr@?\\?H?PHn@BfA@zADBAX@x@??nAAtB?`D?n@Fh@Ar@?r@C`EAdK?r@CnI?`@?J?f@?fAA^?l@@hAKZ?@?@Af@?vA?tB?jB?jAP@zA@JADA@?bA@zAAP@x@?H@zA@|A?xA@@?vA@zA@rA@VDR@Z@@@DBFBBBNHDBNHTJb@`@f@b@VPVBXD?j@CzA?bAAv@?JA`BAnAAlC?n@AtBC`D?x@CxAAjA?dAAxA?xB?bB?dDClB?zAAbC?lB?h@@hAApA?fA?LCfFAzDAvCAl@CxCEtE?V?RGvFGrFC`EAfBAbCAbCCfCEpEA^APAr@?t@{A?}AAs@@c@@@kAAjAQ?g@Ac@?qCGQCA?g@CkB@KAQ@uAAgAAaACQ?M?y@GQA[Ac@Au@AIAc@Am@CUAo@?eB?aBAsAEkAAq@DU@c@?S?m@@e@@G?I?K?cA?}@B_AB]B]@o@@o@B_BBG@q@@kA@G?e@GsDGeAA_@?C?E?E?QAQ?C?yAEqACYAW?i@Ag@E]?]AWMMEMAIAMAA?OACAKAIAEAEAKESKMGKKKKEEACACIKMWGMEKEOGQCGq@_CKWEOEMCICKGMGMGEMKq@mBIUIUACGQGM?AIQi@wAEIKU_@aAEMGOUi@O_@k@wAMa@Si@M[IQIWGQkAN?IBe@?]@O?O?W@w@DyF?I?K?_@@_@?u@@mB?e@@O@yD@w@?K@i@?Y?{@?i@?e@?kA@k@?Y@gA@oA?o@?I@I@cD?a@?c@@S?_@?q@@]@]?A@[@]B]D[D]D[ZsBD_@D]D_@D]@eB?_@A]@qADmK@mB@w@B{E@mB@mB@w@@sB?u@B}C?G@mB@qB?Y@YD_M?Q?M?K?M?I?IAK?EAI?OCSCSCS?AEQESEQCMESCOAGCOAEAMAGAI?GAI?IAK?G?KAK?K?K?M?QBwD?_ABaC?kC@kC@gC@{B?u@?cB@mBBkDByG?g@?k@@_C@wB?y@@kE@eEByD?U?u@?w@@cB?_@@gA?}@?qG?]?W?[?YuAAA?yA?wA?oA@G?]?}@Ay@Ae@?eACO?gA?S@_ACW?wACA_FA_DA_@Ki@?g@@sG?u@@sG?w@@_K@yD?s@?_@?uE@}@?w@?q@@G@K?iF?kA?aE?w@?y@?mH?_@?sA?cA?oA?G?A?w@?{EAm@DKB_@@eB@kE?qE?mB?e@?U?U@oBAqA?AIi@AcB?}A?Y?i@?Y?i@?K?_@?W?]?W?_@?oA?_A?kB?eE?w@?mB?w@?]?uE?q@?K?mBFg@@_@?E?}@?_A@}A?cB?yA?Y?[AgA@s@?sE?c@Ae@Gi@@qA?qA?]?O?N?\\?pAApAIl@?x@A~A?`BAp@Ol@IVINMP[?i@?oB@mB?}A?k@?O?U?k@?wD@i@?g@?a@@Y?A?_@C]CI?UG[ISIMGMGOGOIOKIGIGKIWSWW[][]QSQAY_@a@i@OSA?QUQUSWQUCEKMSUQUOUQUQSU[OQIMGGOSUYMOQUMOU]QSQUe@m@QUQU_@e@CCOQUUWWe@[ECOGYKUEUEk@Ee@AS?C?u@@e@?a@@_@?E?m@@{@@o@?S@[?W?Q?e@@e@?_@?E?c@@]?C?e@@g@?Y@E?a@?s@@e@?c@@a@?O?_A@eA@k@@{@?{@@u@@_@?{@@m@?W@S?M?U?W@S?cA?cEDI?c@?[@i@?W@_@?[@Q?o@?a@@Q?e@@Y@M@M@YBQBa@FKBKBODMBKD[Hc@Re@Pc@XOJYPQJSLc@XWLOHQFOFIBYJSHMDMBQDSBUBWBk@@[?Y?O?C?K?Y?G?o@AUAS?_BCO?o@AY?k@AW?U?SAi@?]Ai@AW?k@AW?S?UA[[C?EAUCEAOCSCQEMCKEOGSMSUCEACCCCGEIAG?AAAAKAA?EACAM?KAAEMEOi@sBe@eAY]QUMEECQIUQ[K?`@BjA@RFtADv@BhAD`@B\\DTNv@Lj@DNFVFVLd@Nn@FZFT?BDTFf@?p@?ZALAJCTEPITELGLEFEFGHEFABABABAD?De@TMFMFWNKLGLKPEJGPM\\IRQn@Md@CNITA@ETEJAHANAZA^^CR@^B\\@d@B`@?N?L?J@F?|A?pAEtA?`Cm@t@QPE`AU?fD?b@?c@?gDaATQDu@PaCl@uA?qAD}A?G?KAM?O?a@?e@C]A_@CSA_@BIh@El@?X?B?hA?D?X@|@A^?b@@lBC`F?X?JCbKAzAAPEr@?BAr@?@?J?j@?h@?bA?d@Ab@ArBAvA?v@?t@?~@?p@?lECbA?lA?N@HBJ?v@?X?^Av@AfBAbCAjDA`ACtD?nBAhB?ZApAAnB?v@A~@MWKSAA@@JRLV@_A?w@@oB@qA?[@iB?oBBuD@aA@kD@cC@gB@w@?_@?Y?w@BG@E@EBQ?eA?i@BeD?M?aB@mABcDAcB?K?a@@a@QCi@IWICC_@OAAGEQKEE[Wg@q@c@w@Wg@ACIOOYO[q@mAYg@_@q@k@iAi@gASVi@dACF]h@A@CFKLABY^i@p@_@d@i@d@ABABQh@_BjAoA|@aAr@}@n@}@n@{@l@e@\\UNiAx@[TWP{@l@q@f@{@l@w@j@UP}@l@i@^e@\\oA|@YREB]V[Ty@j@c@\\[Ri@`@w@h@IHOLc@^e@f@]`@UZEFYd@QZMT_@x@Sf@ELGNQd@K\\IZGRI`@A@Kh@I`@IZG\\Qz@Kh@Or@Mx@UdAI`@SdAKb@ERGXEPI^GTGTWz@KVQf@KXMZO\\[p@S`@INOXILOTKLMPORILOPQRSVQPQPWTYXA@KHSP]XQNURA@URQNONURQP_@b@Y^SVCBOROVQTEHGL]j@O\\INWh@ABM\\M\\O`@ELITGTGNQl@K`@K`@K`@G^I`@EPCPO~@CTGb@ANCL?@Gj@C`@CPAPEh@A\\ATAZ?^?DA\\?`@@d@?b@@d@?^@L?X@~@BlA@bA@R@v@@jABhA?N?T@^@h@?`@@d@@`@?x@?d@?b@?r@?~@?^?n@AD?v@?l@?d@?zAAvA?F?z@@fA?j@?dA@bBAbA?lAAzA?r@?n@?R?H?h@?@A`A?l@?bA?j@A\\?V?n@?|@?l@AjA?j@?\\ArD?fA?p@?TAfA?hBCnBAlAAnAAz@AdAApAAp@?B?l@?^?fA?h@@X@tC@t@?P?P?H@t@@fA?x@@f@?p@OZA@?BExB?DCtACx@?@?n@A\\?B?X?nA?B@l@?p@?j@?XATA|@Ox@CLCDCDEHQTi@?cCDQPkA@wBCeAAA?iACO?w@?iB?_BCeACA?cBCS?A?wC@U?wAAq@Be@@]AMAIAICGCIEICGEGEIGiCcBuDcCcBkAkAu@gAo@qAy@On@It@Iz@INCH"
            },
            "metrics": {
                "performedShipmentCount": 6,
                "travelDuration": "3227s",
                "waitDuration": "0s",
                "delayDuration": "0s",
                "breakDuration": "0s",
                "visitDuration": "3600s",
                "totalDuration": "6827s",
                "travelDistanceMeters": 28588,
                "maxLoads": {
                    "weight": {
                        "amount": "6"
                    }
                }
            },
            "routeCosts": {
                "model.shipments.costs_per_vehicle": 50,
                "model.vehicles.cost_per_kilometer": 28.588
            },
            "routeTotalCost": 78.588
        }
    ],
    "metrics": {
        "aggregatedRouteMetrics": {
            "performedShipmentCount": 8,
            "travelDuration": "4421s",
            "waitDuration": "0s",
            "delayDuration": "0s",
            "breakDuration": "0s",
            "visitDuration": "4800s",
            "totalDuration": "9221s",
            "travelDistanceMeters": 35280,
            "maxLoads": {
                "weight": {
                    "amount": "6"
                }
            }
        },
        "usedVehicleCount": 2,
        "earliestVehicleStartTime": "2024-07-08T16:00:00Z",
        "latestVehicleEndTime": "2024-07-08T17:53:47Z",
        "totalCost": 85.28,
        "costs": {
            "model.vehicles.cost_per_kilometer": 35.28,
            "model.shipments.costs_per_vehicle": 50
        }
    }
}
```

Due to the capacity constraint, `veh-sprintervan` (orange) is assigned to make its first two stops in the `downtown` area to assist with deliveries that `veh-cargobike` cannot handle.

![Soft constraints allow vehicles from different territories to assist, if needed](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2024/09/Screenshot-2024-09-12-at-6.22.19-AM.png)

Soft constraints allow vehicles from different territories to assist, if needed

### Conclusion

So that's how you build a territory optimization system in GMPRO. The same soft constraint approach can be used to preferentially assign bookings to drivers with high ratings (ride share), prioritize high margin deliveries (third party logistics) as well as match jobs to technicians optimally (field services). Please contact [afian.anwar@hkmci.com](mailto:afian.anwar@hkmci.com) if you need specific help modelling your business operations with GMPRO.

**This article was written by** [**Afi Labs**](https://afi.io/?ref=blog.afi.io)**, a** [**Google Maps Premier Partner**](https://www.afi.io/?ref=blog.afi.io) **and reseller. We build route optimization, navigation, and fleet tracking software on Google Maps, and offer volume pricing on GMP licensing.** [**Talk to an engineer**](https://www.afi.io/contact%5Fus?ref=blog.afi.io) **or** [**follow Afian on LinkedIn**](https://www.linkedin.com/in/afian-anwar/?ref=blog.afi.io)**.**  
  
Next: [Part 7: GMPRO docs: Solving the VRP with route clustering and soft constraints](https://afi.io/blog/gmpro-docs-solving-the-vrp-with-route-clustering-and-soft-constraints/?ref=blog.afi.io)