Want to use GMPRO in your route optimization system? 👋 Say Hello! to start working with Afi Labs.

GMPRO docs: Fixed vehicle costs

How to minimize the number of vehicles needed by adjusting fixed and variable costs in GMPRO.

GMPRO docs: Fixed vehicle costs

In this blog post, I’ll show you how to optimize variable and fixed costs in GMPRO to minimize the number of vehicles used. This approach ensures that all deliveries are made on time while keeping the vehicle count as low as possible.

A route solution that uses fixed vehicle costs to minimize the number of drivers needed
A route solution that uses fixed vehicle costs to minimize the number of drivers needed

Part 1: GMPRO: Google Maps Platform route optimization API
Part 2: GMPRO TSP solver: Google Maps with more than 25 waypoints
Part 3: Google Maps route optimization: multi vehicle
Part 4: Fleet routing app - free Google Maps route planner for multiple stops
Part 5: GMPRO docs: Fixed vehicle costs (this article)
Part 6: GMPRO docs: Territory optimization and route planning
Part 7: GMPRO docs: Solving the VRP with route clustering and soft constraints

How does a route optimization algorithm work?

In a previous blog post, I explained that route optimization algorithms, such as the one used by GMPRO, aim to assign vehicles to stops (shipments in GMPRO) in a way that minimizes costs while ensuring all stops are served. The simplest way to understand these costs is by considering them as the distance each vehicle travels, as distance directly correlates with fuel costs.

🌎
If you want to view route solutions produced by GMPRO on a map like we did here, use our GMPRO Viewer

Worked GMPRO example with variable costs (costPerKilometer)

Here’s a simple example with 4 shipments and 2 vehicles on a unit (1 km x 1 km) grid.

4 shipment / 2 vehicle worked example without fixed costs
4 shipment / 2 vehicle worked example without fixed costs

A quick visual inspection shows that the most efficient way to solve this route plan would be for the blue driver to do stops A, B and C and the orange driver to do D for a total cost of 4 i.e. 1+1+1 (blue) and 1 (orange).

4 shipment / 2 vehicle worked example without fixed costs
4 shipment / 2 vehicle worked example without fixed costs

In GMPRO, we use the costPerKilometer field in the vehicle object to indicate variable costs like so:

{
    "vehicles": [
        {
            "label": "will-yvr",
            "costPerKilometer": 1
        }
    ]
}

Putting everything together, here’s the JSON for a 4 shipment / 2 vehicle worked example with a "costPerKilometer": 1 variable cost:

Input

{
  "model": {
    "shipments": [
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2545595,
              "longitude": -123.1096174
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "label": "yvr123"
      },
      {
        "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
          }
        },
        "label": "yvr789"
      },
      {
        "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
          }
        },
        "label": "yvr987"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.3352081,
              "longitude": -123.1453979
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "label": "yvr654"
      }
    ],
    "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": "mark-yvr",
        "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": "will-yvr",
        "costPerKilometer": 1
      }
    ],
    "globalStartTime": "2024-07-08T16:00:00Z",
    "globalEndTime": "2024-07-09T02:59:59Z"
  },
  "populatePolylines": true
}

Output

{
    "routes": [
        {
            "vehicleLabel": "mark-yvr",
            "vehicleStartTime": "2024-07-08T16:00:00Z",
            "vehicleEndTime": "2024-07-08T17:03:40Z",
            "visits": [
                {
                    "shipmentIndex": 1,
                    "startTime": "2024-07-08T16:14:35Z",
                    "detour": "0s",
                    "shipmentLabel": "yvr789",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 2,
                    "startTime": "2024-07-08T16:34:03Z",
                    "detour": "948s",
                    "shipmentLabel": "yvr987",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 3,
                    "startTime": "2024-07-08T16:53:40Z",
                    "detour": "1934s",
                    "shipmentLabel": "yvr654",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                }
            ],
            "transitions": [
                {
                    "travelDuration": "875s",
                    "travelDistanceMeters": 9486,
                    "waitDuration": "0s",
                    "totalDuration": "875s",
                    "startTime": "2024-07-08T16:00:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "3"
                        }
                    }
                },
                {
                    "travelDuration": "568s",
                    "travelDistanceMeters": 7110,
                    "waitDuration": "0s",
                    "totalDuration": "568s",
                    "startTime": "2024-07-08T16:24:35Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "2"
                        }
                    }
                },
                {
                    "travelDuration": "577s",
                    "travelDistanceMeters": 6406,
                    "waitDuration": "0s",
                    "totalDuration": "577s",
                    "startTime": "2024-07-08T16:44:03Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "1"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T17:03:40Z",
                    "vehicleLoads": {
                        "weight": {}
                    }
                }
            ],
            "routePolyline": {
                "points": "_dxkHnrfnV?`@HAN??f@Ar@@^@TDPFRBP@F?@@P?^?^?X?BAp@eBC@sFBeB@}@@mB?C@oB?O?g@?O?k@@}@?Y?S?m@@O?]A[?SAICWCa@Gc@B_@EWQ}@CMAKAME[AS?IAQAc@?g@?kA?_@@_@U?}@AW?SAgACgAA}@?WCK_FEwAAa@EmBEaCCOCUI}@AKOwAKaAC[SsB_@}DUiCUaC[aDWiCE[CME[CUM{@CQESM_AM}@O_AOeACWCUCUASAS?MAg@?C?c@?gB@{@?s@?w@?cD@kC?uA@eK@qE?]?qC?wA?mBHk@Q?qA@yA?{A?aA?U?[A}@Aa@?y@Ag@?m@@W?OGOC[?EAK?ICECGCEGIUCI?GAEASAQAI?GACACAC@yA?e@?G?w@@u@?yD?uI?iH?{E?kO@aBDg@BqD?oE?u@?A?iD?cC?mB?AAQC_ACm@IkBC{@?W?UB]@UB_@DYHm@^wBH]F]PaAd@{C?AJo@Hq@@CHs@P}ALwAJcA?A`@iEHaAB[JmAr@uIDc@@SDg@De@@]?A@Y?[?O?QAUCQE_@GSK_@GQGMIOQUUUWS_Ai@A?{@e@i@YaAq@QKAAAASOo@m@c@c@m@o@SUY]SUGIA?QCQU_@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?UAq@?M?M?K?G@O@WBYDa@E_@FIDYHQFm@Zi@^SL[X}@nASh@q@pBEPWdASx@Mr@ENU|@Ot@ELKf@CHOp@CLKb@Qn@Ql@On@ABETIVERAFMf@ABK^GXK^CNK^GZELMp@CFQn@IZGPKXGRSl@EJg@hAUf@e@dACFGLQ\\[h@ABEHA@EHSVR\\@Bj@hABFTb@Vd@r@fA^t@`@t@j@nALT`@p@JLRT\\VDBHDJDJD^Lz@PArBAvA?v@?t@?~@?p@?lECbA?lA?N@HBJ?v@?X?^Av@AfBAbCAjD?`AEhE?zAAhBAZ?pAAlB?v@A`AMWOWNVLV?hB?lAAdD?v@?~@AlB?vAClCAt@Av@?t@Af@M@c@AoCC]?a@AkBCG?s@C]?oC@c@?kBCc@AA?}@AoC@aA@{CGq@AoE?qBE}AEoCCc@A[?eCAO@K?O?OAMCoEEeCAkAA]C[E_@ESGOC]Im@U{@g@][GCe@i@IKOQAIAGMSS]q@oAe@iAa@}@Wo@M[GMSc@U?OLYTY^[`@MNm@x@i@|@EFKPWh@KTGJCDKTUb@Yj@A@C^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@INCHBIHOH{@Hu@No@pAx@fAn@jAt@bBjAtDbChCbBHFFDFDHBHDFBHBH@L@\\@d@Ap@CvA@T?vCA@?R?bBB@?dAB~ABhB?v@?N?hAB@?dA@vBBjAAPFjBB\\@lA@@l@?N?f@?F?F@d@?h@@T@\\@`@Bh@?BD|@@j@Bf@HzC?B?@HPAxA?vAAt@?x@A~A?TA`@?J?`@AR?p@ChBA^CpBAf@C~@An@A~@?XAd@?f@A^?p@A~@Al@Ab@?ZAv@?LAf@?@Ab@Ad@Aj@Al@?PChACnAAX?`@Aj@A@A~@Ab@Ab@Af@?h@CdAA`AAp@A`@Ad@?^Af@C`BAh@AxAAvB?`A?r@?f@A|A?rA?\\?R?|E?f@?L?d@?z@?b@?j@?h@?|@?l@?Z?lA?~@?`AAlC?N?dC?rB?fB?nA?`@?hA?v@?hA?z@?~A?pA?|@?\\?h@?|@?j@?Z?^?d@?N?T?b@A`@?h@AZAFA`@Eb@?FEXG`@GZADI^K\\M\\EHIPM\\MTOZa@t@k@hACBOVQ\\]n@S`@U`@GL]n@QZOXQZOXOXQ\\O\\MVOXIPEJOZEFIP]r@MVMVKTEHWh@EFMZOVUNA@EHINa@r@i@`AQZKROZ]v@Qb@CFEJUp@Od@[dAM`@Mj@Mv@Gj@SxACZA^Cj@Ch@ClAChA?JCz@A|@AZ?`@AN?`AChBAr@CzCA`A^?r@@T@f@@pAEfAEb@Ah@?LA@?XIvB@H?|@@l@@z@@@x@?z@Az@?NAf@?J?h@A`@?FAD?DCDADE@C@C@Q?S@O?_@@I?[@S?O@G@S@G@UFSHURA?EHEDOPEHIJGLENG^I\\CTCRCTAXA^Af@?@?P?hE?j@@H?J?H@HB^Fh@?F?F?`A?dD?lA?lB?lB?lBAdD?t@A|ExA?zA??u@?o@"
            },
            "metrics": {
                "performedShipmentCount": 3,
                "travelDuration": "2020s",
                "waitDuration": "0s",
                "delayDuration": "0s",
                "breakDuration": "0s",
                "visitDuration": "1800s",
                "totalDuration": "3820s",
                "travelDistanceMeters": 23002,
                "maxLoads": {
                    "weight": {
                        "amount": "3"
                    }
                }
            },
            "routeCosts": {
                "model.vehicles.cost_per_kilometer": 23.002
            },
            "routeTotalCost": 23.002
        },
        {
            "vehicleIndex": 1,
            "vehicleLabel": "will-yvr",
            "vehicleStartTime": "2024-07-08T16:00:00Z",
            "vehicleEndTime": "2024-07-08T16:19:02Z",
            "visits": [
                {
                    "startTime": "2024-07-08T16:09:02Z",
                    "detour": "0s",
                    "shipmentLabel": "yvr123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                }
            ],
            "transitions": [
                {
                    "travelDuration": "542s",
                    "travelDistanceMeters": 3321,
                    "waitDuration": "0s",
                    "totalDuration": "542s",
                    "startTime": "2024-07-08T16:00:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "1"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T16:19:02Z",
                    "vehicleLoads": {
                        "weight": {}
                    }
                }
            ],
            "routePolyline": {
                "points": "qfukHjifnVxA@`@@nDI?@?@?@?@@??@?@?@@??@?@@??@@??@@?@??@@?@??A@?@??A@??A@A?A@??A?A?A?A?Ab@?h@@l@?n@@xA@z@@@?`@?AbK?t@ClI?t@A~FKhA?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?RGvFGrFdBFxADD?|ABF@bBBh@@"
            },
            "metrics": {
                "performedShipmentCount": 1,
                "travelDuration": "542s",
                "waitDuration": "0s",
                "delayDuration": "0s",
                "breakDuration": "0s",
                "visitDuration": "600s",
                "totalDuration": "1142s",
                "travelDistanceMeters": 3321,
                "maxLoads": {
                    "weight": {
                        "amount": "1"
                    }
                }
            },
            "routeCosts": {
                "model.vehicles.cost_per_kilometer": 3.321
            },
            "routeTotalCost": 3.321
        }
    ],
    "metrics": {
        "aggregatedRouteMetrics": {
            "performedShipmentCount": 4,
            "travelDuration": "2562s",
            "waitDuration": "0s",
            "delayDuration": "0s",
            "breakDuration": "0s",
            "visitDuration": "2400s",
            "totalDuration": "4962s",
            "travelDistanceMeters": 26323,
            "maxLoads": {
                "weight": {
                    "amount": "3"
                }
            }
        },
        "usedVehicleCount": 2,
        "earliestVehicleStartTime": "2024-07-08T16:00:00Z",
        "latestVehicleEndTime": "2024-07-08T17:03:40Z",
        "totalCost": 26.323,
        "costs": {
            "model.vehicles.cost_per_kilometer": 26.323
        }
    }
}

Worked GMPRO example with fixed costs (fixedCost)

In a logistics company, costs go beyond just distance or fuel expenses. You must also consider fixed costs, such as the investment in purchasing a new delivery van or the minimum daily salary required for a driver, even if they only complete a single delivery. So when you use a route optimization algorithm, it might be helpful to use fewer vehicles to achieve the same result. We can do this by assigning fixed costs to vehicles so that the route optimization algorithm is "discouraged" from adding a new vehicle to the route solution.

4 shipment / 2 vehicle worked example with fixed costs
4 shipment / 2 vehicle worked example with fixed costs

Let’s define fixed costs as 5 if a vehicle is included in the route solution and 0 otherwise. In the same example from earlier, the new optimal route solution is for blue to do A, B, C and D for a total cost of 11 i.e. 5 (fixed cost) + 1+1+1+3 (variable costs). The alternative would be for blue to do A, B and C and orange, D, for a total cost of 14 i.e. 5 + 5 (fixed cost) + 1+1+1+1 (variable costs).

4 shipment / 2 vehicle worked example with fixed costs
4 shipment / 2 vehicle worked example with fixed costs

We can combine fixed costs with variable costs to the vehicle object like this:

{
    "vehicles": [
        {
            "label": "will-yvr",
            "costPerKilometer": 1
            "fixedCost": 5
        }
    ]
}

The quality of the route solution depends largely on the balance between fixed and variable costs. If fixed costs are relatively low compared to variable costs, such as in long-distance trucking, the resulting route solution may be the same as one that doesn't account for fixed costs at all.

👨‍💻
Screenshots in this blog post were taken with GMPRO-viewer , using data imported via GMPRO-json-converter. Both tools are free to use.

And here’s the full JSON for the same 4 shipment / 2 vehicle worked example, but now with fixed costs "fixedCosts": 50 added to the mix:

Real world 4 shipment / 2 vehicle worked example with fixed costs
Real world 4 shipment / 2 vehicle worked example with fixed costs

Input

{
  "model": {
    "shipments": [
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.2545595,
              "longitude": -123.1096174
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "label": "yvr123"
      },
      {
        "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
          }
        },
        "label": "yvr789"
      },
      {
        "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
          }
        },
        "label": "yvr987"
      },
      {
        "deliveries": [
          {
            "arrivalLocation": {
              "latitude": 49.3352081,
              "longitude": -123.1453979
            },
            "timeWindows": [
              {
                "startTime": "2024-07-08T16:00:00Z",
                "endTime": "2024-07-08T18:00:00Z"
              }
            ],
            "duration": "600s"
          }
        ],
        "loadDemands": {
          "weight": {
            "amount": 1
          }
        },
        "label": "yvr654"
      }
    ],
    "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": "mark-yvr",
        "costPerKilometer": 1,
	"fixedCost": 50
      },
      {
        "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": "will-yvr",
        "costPerKilometer": 1,
        "fixedCost": 50
      }
    ],
    "globalStartTime": "2024-07-08T16:00:00Z",
    "globalEndTime": "2024-07-09T02:59:59Z"
  },
  "populatePolylines": true
}

Output

{
    "routes": [
        {
            "vehicleLabel": "mark-yvr",
            "vehicleStartTime": "2024-07-08T16:00:00Z",
            "vehicleEndTime": "2024-07-08T17:38:48Z",
            "visits": [
                {
                    "startTime": "2024-07-08T16:14:49Z",
                    "detour": "0s",
                    "shipmentLabel": "yvr123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 3,
                    "startTime": "2024-07-08T16:49:55Z",
                    "detour": "1702s",
                    "shipmentLabel": "yvr654",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 2,
                    "startTime": "2024-07-08T17:09:30Z",
                    "detour": "3070s",
                    "shipmentLabel": "yvr987",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 1,
                    "startTime": "2024-07-08T17:28:48Z",
                    "detour": "4444s",
                    "shipmentLabel": "yvr789",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                }
            ],
            "transitions": [
                {
                    "travelDuration": "889s",
                    "travelDistanceMeters": 6170,
                    "waitDuration": "0s",
                    "totalDuration": "889s",
                    "startTime": "2024-07-08T16:00:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "4"
                        }
                    }
                },
                {
                    "travelDuration": "1506s",
                    "travelDistanceMeters": 12372,
                    "waitDuration": "0s",
                    "totalDuration": "1506s",
                    "startTime": "2024-07-08T16:24:49Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "3"
                        }
                    }
                },
                {
                    "travelDuration": "575s",
                    "travelDistanceMeters": 6353,
                    "waitDuration": "0s",
                    "totalDuration": "575s",
                    "startTime": "2024-07-08T16:59:55Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "2"
                        }
                    }
                },
                {
                    "travelDuration": "558s",
                    "travelDistanceMeters": 7092,
                    "waitDuration": "0s",
                    "totalDuration": "558s",
                    "startTime": "2024-07-08T17:19:30Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "1"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T17:38:48Z",
                    "vehicleLoads": {
                        "weight": {}
                    }
                }
            ],
            "routePolyline": {
                "points": "_dxkHnrfnV?`@HAN??f@Ar@@^@TDPFRBP@F?@@P?^?^?X?BAp@eBC@sFBeB@}@@mB?C@oB?O?g@?O?k@@}@?Y?S?m@@O?]A[?SAICWCa@Gc@B_@EWQ}@CMAKAME[AS?IAQAc@?g@?kA?_@RALOn@@H?|ABvA@|@?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@zADBArA@RBtA@B?`@@R?\\?B@DAF?hAAX?B?p@AL?NAhACpA@?|@?z@?r@@l@A|BDV?^CfJA`K?~A?JAh@?v@?fB?jAAhI?x@ClC?^AdC?h@?^?v@?LAd@?X?~C?F?F?F?H?J?P?b@AV?D?F?F?D?nAAvF?v@CnI?~A?d@Ax@AVCPCr@Ar@_@|BIz@OdACXADGTER@x@AtC?h@?l@?`C?j@?`A?b@L?jDF\\AP?R?H?N@d@?B?V@T?NKxABr@@D?L?rCDb@@ApA?fA?LCfFAzDAvCAl@CxCEtE?V?RGvFGrFdBFxADD?|ABF@bBBh@@i@AcBCGA}ACE?yAEeBGC`EAfBAbCAbCCfCEpEA^APAr@?t@{A?}AAs@@c@@Q?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]?]AW@Y?o@Co@Co@COCSCQ?m@CG?C?u@Ae@CO?G@SBKBI@G@C@IDMDKDIHUJSJKHSRKLQNKPIPGJCBEJCHMTg@|@[l@k@`AMRQXa@p@Wf@[n@w@pAMX_AbBOUKQe@y@mCwEGGYo@oC{EEEk@cA_A~AOVa@p@QXOXi@`A_A~A_AbB}@bBo@dAw@vA[h@S`@i@~@GHe@z@SZ{@~AeAlBaGpKoDlGYd@kAtBi@`AaDxFYf@qA|B{@|AKNYf@a@t@g@|@QXGJYf@MXq@fAKRMRe@r@ELINIPmAzBe@x@e@x@q@lAILaCfEU`@g@`AcBvCU`@ADYd@Wf@EHS\\KPc@x@IDEBS\\a@n@MRQXEFABQVQNGDIFQFKBK@I@I?A?GAI?EAIAKESKSKOGMG]QUMUIa@MQAQ?Q@QBMBQDKBKBKDKDKDGDIDKF]VA@GDGDWX]`@CBABABMZCDCFGHGLCFCDEJA?INCFCFKVCFELK\\a@dBEJADADGVCLMd@Oh@GVABOd@IZGROd@M\\GPITELQb@EJA@ELCFMZS`@KTKPS`@CDINEJGLW`@Q\\e@t@QXORORQTa@f@c@f@cAjAo@l@C@_@ZGFURIFSPQJOJo@`@q@^a@RGB_@P[JC@]J_@JC@c@HIBIBG@C?[FI@I@WBG@A?YBE?[@A?I@W?A?qA?W?i@A_@AGA[AQA[C[CEAYCE?a@Gg@Iq@K_@GQEKC_@IYGCA]I]K[IA?IESGA?MEME[M]M[MAAWMIEQIYQc@S[S]QOIIGg@YoAcAe@a@UUq@o@QOQQCCIG}@y@y@q@ECWWk@i@MKAAOMgA_AGGIGgAaASQGEOM][_@[_@[SQII_@[}@w@SQII_@[QOAAII_@[][SQKIOOMKSQKISQII][_@[KKQO_@]][[WCC}@w@SQII]]gA_AMKMMOM[Y_@]][][_@[UUg@c@oAiAi@g@_@[IIIIWUGEc@c@yFgFEEGEIIUUCCEC][e@c@k@g@gAcA][y@u@eAaAEEGEIGCEGECCCCCEMKCCCEIGAA][MKWUEEGEGGECAAQKc@[UQEM?AAAKKq@k@_Aw@IGUQEE]W]U]SOGIGKGKE[QIEMIIGKGCACCGGAACEEGEGEIEKCKAICQAI?I?A?G?K?M@G@MBKBMDKBIFGDIFEFEFEFCFAHAJ@J@JDHFHHHLBBDHBJBHBNBR@RAR?RARK|AGVSbAGZOv@Or@CXEZKr@WbBCTGb@YlBm@pEQrACVANCX?B?f@?h@?vAKd@CLAFCFCDCDEBCBE@KDKJ]?e@@A?C@CBEJ]A[AqAGeA@U?S@U?c@@]?K@[@a@?aA?aA@KAKA?dA?V?@AD?LAL?@?J?T?^?J?f@?L@PAB?@A??@?@?@?@?@?@?@?@?@?@?@@@?@?@@@?@?B?D?D?D@HAzHAt@?v@@PDn@@NDp@@D@HLlA@JHt@Fj@F`@BZN|@BX@D?@BRBL?BDX@D@`@?n@?r@?B?r@?@?v@?t@?B?v@?r@CjIAv@?t@iB?_B?kBAyAAwBA}A?mB?cB?{AAyA??u@?o@?n@?t@{A?yA?@}E?u@@eD?mB?mB?mB?kA?mB?y@?}@?E?GAEC_@Ei@AI?I?KAI?k@?iE?Q?A@g@@_@@YBUBSBUH]F_@DOFMHKDINQDEDI@?TSRITGFARAFANAR?ZAH?^AN?RAP?BABADA@EBE?E@E?G@a@?i@?K@g@?O@{@?{@Ay@{@Am@A}@AI?wBA[Kc@AuAGq@?QYIEIGEECGCCACCGCIIWCuACw@Am@Ao@AU?CCq@Ag@As@Aa@AS?SAU?K?e@A{@@qA@_@@C@YBi@Ba@Bc@DYDc@Fc@Hg@Jk@Hc@Jc@H]DM?E@C@CA[@AHWJ[JWDIFQL[LY@A\\u@JUJUFMLUL[JQBINYLYP]Zo@\\s@P]LYZq@P]Te@DIBEJUP]LW@ALWLUJQDIHODI`@u@LWNYLUP[HQNWNYP]LUFKFMR_@HODKNWN[NY?AJWLYBEFODMH[FS?ADMBQ@GF]@GDW@IB]B]@O@U@_@?e@@a@?_@?g@?iB?mB?i@?_@?cA?cA?_A?y@?}@?c@?_@?y@?aA?_@?[?e@?g@?]@e@Ag@?I@}@?a@?s@?Y?g@?w@?a@?g@?W?e@?s@?c@?y@?W?c@?Y?m@?w@?K?]?c@?a@?c@?yA?[?g@?cA?a@?q@?c@?C?a@?c@?g@@{@?g@?[?iB@eB@uADeDBkBDiCBiA@gA@m@B_A@u@@u@@c@BiA?S@c@@a@?SBw@@_A@m@@S@m@?[@a@?c@@a@?g@@{@BiA@aA?M@_@@_@BeADuABw@BiB?M@S?a@@]?E?k@@Q?G@m@@oA?c@?a@?a@@c@?]?{@H[@EByADkABq@F}A@OF}ABw@?EBw@?i@?c@?wBBc@I?c@AM?UAc@Ao@?i@?cCDQPkA@wBCeAAA?iACO?w@?iB?_BCeACA?cBCS?A?wC@U?wAAq@Be@@]AMAIAICGCIEICGEGEIGiCcBuDcCcBkAkAu@gAo@qAy@On@It@Iz@INCHBIHOH{@Hu@No@pAx@fAn@jAt@bBjAtDbChCbBHFFDFDHBHDFBHBH@L@\\@d@Ap@CvA@T?vCA@?R?bBB@?dAB~ABhB?v@?N?hAB@?dA@vBBjAAPFjBB\\@lA@R?N@H?H?N?D?H?R?J?Bc@?iAC{AEoAAo@?ECc@?K?ECw@Cg@?MCs@ACAa@Co@?EQi@?eB?Y?q@A}@?u@AkEAI?O?SA]CkDAgA?e@?c@?w@?oA@gA?M@w@?O@q@@gA@sA@}@@yA@_B?m@?e@@{C?eB@kB?iA@eA?oC?}@@M?W?i@?]?m@?O?a@?W@cA?S?sA@gA?oB@}@?iA?Y?I?_@?c@@g@?gA?[@a@?y@@m@?g@@gA?a@@y@@mA?g@@}@?_@?M?c@?Y?g@@i@?c@?a@?}@?Q?{@?a@?g@?a@Aa@Ac@Ae@Aa@C_@CiACa@Ak@Aa@?Y?e@?c@@i@?[Ba@@WBk@Ba@BU@OBU@IDc@Fa@D_@Fc@@IDSFa@Hc@Ha@H[?AH]J_@H_@Ng@BENi@Na@Na@Vo@P_@N[HODGN[NWPYNWPUPWPSBERUPSPSRSHGHITSPORQXWRQJKXWVUJKFGXY\\c@PULQHKHMTYJQJQHMNYP[N[JWN_@Rc@Tq@Na@J_@La@HYJ_@Ha@H]Jc@F_@Ha@H]Hc@Ny@FWF_@@Ed@_CBMHc@H]F_@FYLi@H[La@J]DMFOL_@L[@CJWNYN[NYb@q@NUBCNSNQRUTUTSPOPOVQXUv@i@j@a@h@_@ROTQPMVO?AROJGHGRQRMTOb@]HGt@i@TOROTOPMj@a@ROTOd@]RMRO@AtAaAb@]RM`@YPB@?NKl@]DCl@]r@e@f@YVOrAy@VQ^UhBmAv@i@LIv@i@FGBAz@o@BAFG|AkAHEFCD?B?BAD@ND@@r@fA^t@`@t@j@nALT`@p@JLRT\\VDBHDJDJD^Lz@PArBAvA?v@?t@?~@?p@?lECbA?lA?N@HBJ?v@?X?^Av@AfBAbCAjDA`ACdF?TArC?ZApAAnB?v@A~@MWKSAA"
            },
            "metrics": {
                "performedShipmentCount": 4,
                "travelDuration": "3528s",
                "waitDuration": "0s",
                "delayDuration": "0s",
                "breakDuration": "0s",
                "visitDuration": "2400s",
                "totalDuration": "5928s",
                "travelDistanceMeters": 31987,
                "maxLoads": {
                    "weight": {
                        "amount": "4"
                    }
                }
            },
            "routeCosts": {
                "model.vehicles.cost_per_kilometer": 31.987
            },
            "routeTotalCost": 31.987
        },
        {
            "vehicleIndex": 1,
            "vehicleLabel": "will-yvr"
        }
    ],
    "metrics": {
        "aggregatedRouteMetrics": {
            "performedShipmentCount": 4,
            "travelDuration": "3528s",
            "waitDuration": "0s",
            "delayDuration": "0s",
            "breakDuration": "0s",
            "visitDuration": "2400s",
            "totalDuration": "5928s",
            "travelDistanceMeters": 31987,
            "maxLoads": {
                "weight": {
                    "amount": "4"
                }
            }
        },
        "usedVehicleCount": 1,
        "earliestVehicleStartTime": "2024-07-08T16:00:00Z",
        "latestVehicleEndTime": "2024-07-08T17:38:48Z",
        "totalCost": 31.987,
        "costs": {
            "model.vehicles.cost_per_kilometer": 31.987
        }
    }
}

With these fixed costs added to the vehicle object, it now is optimal to only send out driver mark-yvr (blue) or will-yvr (orange), but not both.

What next?

This brief (but hopefully helpful) blog post explained how to incorporate fixed costs into a GMPRO routing problem. By understanding both fixed and variable costs, you can model complex real-world scenarios, such as optimizing the balance between employees (who have high fixed costs but low variable costs) and contractors (who have low fixed costs but high variable costs). This approach can also be applied to managing mixed fleets with vehicles of varying fuel efficiencies or selecting drivers with different hourly wage rates.

Next: Part 6: GMPRO docs: Territory optimization and route planning