GMPRO docs: Fixed vehicle costs
How to minimize the number of vehicles needed by adjusting fixed and variable costs in GMPRO.
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.
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
Part 8: GMPRO docs: Driver load balancing with soft constraints
Part 9: GMPRO docs: Driver breaks
Part 10: GMPRO docs: Complete deliveries before pickups in cargo bike logistics
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.
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.
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
).
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.
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).
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.
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:
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.
👋 As always, if you have any questions or suggestions for me, please reach out or say hello on LinkedIn.
Next: Part 6: GMPRO docs: Territory optimization and route planning