> ## 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: Squash durations and parking
- URL: https://blog.afi.io/blog/gmpro-docs-squash-durations-and-parking/
- Published: 2025-12-21T08:58:39.000Z
- Updated: 2026-08-22T04:51:34.000Z
- Description: How to model "park once, deliver multiple" scenarios using transition attributes and squashed durations with GMPRO.
- Author: Afian Anwar
- Tags: googlemaps

When calculating how many jobs a driver can do in a day, route optimization engines such as the [Google Maps Platform Route Optimization API](https://developers.google.com/maps/documentation/route-optimization?ref=blog.afi.io) (GMPRO) generally consider three key factors: the travel time between stops, the driver’s available working time, and the service time required at each stop. Typically, each stop might have a service time of say 10 minutes to account for parking and finding the entrance. But if a driver has six stops within the same building, it shouldn’t take 6 × 10 minutes (60 minutes) in total. 

The route optimization solver should *squash durations* by adding a one-time parking duration to each group of deliveries at the same building, while applying separate service times for each individual delivery. In this blog post, I’ll show you how to use GMPRO's `transition_attributes` property to do this and accurately model parking time for multiple deliveries within the same building.

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

Part 1: [GMPRO: Google Maps Platform route optimization API](https://blog.afi.io/blog/gmpro-google-maps-platform-route-optimization-api/)  
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: [GMPRO fleet routing app - free route planner for multiple stops](https://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://www.afi.io/blog/gmpro-docs-fixed-vehicle-costs/?ref=blog.afi.io)  
Part 6: [GMPRO docs: Territory optimization and route planning](https://www.afi.io/blog/gmpro-docs-territory-optimization-and-route-planning/?ref=blog.afi.io)  
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 (this article)**  
Part 15: [GMPRO docs: Google Large Vehicle Routing](https://www.afi.io/blog/gmpro-docs-google-large-vehicle-routing/?ref=blog.afi.io)

### Parking and typical driver workflows

It might seem simple, but to understand how GMPRO handles parking, we first need to look at how drivers actually run their routes. At the beginning of a shift, a driver is usually given a list of stops. This list might already be optimized using a tool like GMPRO - or it might be unordered, leaving the driver to plan the route manually.

The driver leaves the depot with a van full of packages and heads to the first stop - an apartment building with three deliveries. He unloads the packages for that building onto a trolley or hand cart, then visits each unit to drop off the packages at the door. Once all deliveries are made, he returns to his vehicle and drives to the next stop.

![Squashed durations in GMPRO applied to stops at the same location](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2025/12/Screenshot-2025-12-21-at-6.53.01---AM-1.png)

Squashed durations in GMPRO applied to stops at the same location

### Modeling the driver's route with transition attributes

Before we look at how to use [transition attributes](https://developers.google.com/maps/documentation/route-optimization/reference/rpc/google.maps.routeoptimization.v1?ref=blog.afi.io#google.maps.routeoptimization.v1.TransitionAttributes) to account for parking in GMPRO, we first need to understand what a *transition* is and the role it plays in breaking a route into its individual components.

The `transitions` field in the GMPRO response output describes the movements between stops on a vehicle's route - essentially, the "legs" of the journey between two consecutive locations, or `visits`. For example, if you have two `visits` `yvr789` and `yvr987` at two different locations:

```
{
    "visits": [
        {
            "shipmentIndex": 1,
            "startTime": "2024-07-08T16:15:06Z",
            "detour": "0s",
            "shipmentLabel": "yvr789",
            "loadDemands": {
                "weight": {
                    "amount": "-1"
                }
            }
        },
        {
            "shipmentIndex": 2,
            "startTime": "2024-07-08T16:34:25Z",
            "detour": "940s",
            "shipmentLabel": "yvr987",
            "loadDemands": {
                "weight": {
                    "amount": "-1"
                }
            }
        }
    ]
}
```

The output will include three `transitions` to represent the full journey: one from the start location to the first `visit`, one between the first (index 0) and second `visits`, and one from the second `visit` (index 1) to the end location (if one is specified). In general, if there are *n* visits, there are always *n+1* transitions.

```
{
    "transitions": [
        {
            "travelDuration": "906s",
            "travelDistanceMeters": 9746,
            "waitDuration": "0s",
            "totalDuration": "906s",
            "startTime": "2024-07-08T16:00:00Z",
            "vehicleLoads": {
                "weight": {
                    "amount": "4"
                }
            }
        },
        {
            "travelDuration": "737s",
            "travelDistanceMeters": 9965,
            "delayDuration": "300s",
            "waitDuration": "0s",
            "totalDuration": "1037s",
            "startTime": "2024-07-08T16:25:06Z",
            "vehicleLoads": {
                "weight": {
                    "amount": "3"
                }
            }
        },
        {
            "travelDuration": "2188s",
            "travelDistanceMeters": 13446,
            "waitDuration": "0s",
            "totalDuration": "2188s",
            "startTime": "2024-07-08T17:12:23Z",
            "vehicleLoads": {
                "weight": {}
            }
        }
    ]
}
```

Viewed as a timeline, you can see that a `visit` is always surrounded by a transition before it (with the same index) and a transition after it (with index + 1).

![Transitions between visits in GMPRO](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2025/12/Screenshot-2025-12-21-at-8.21.14---AM.png)

Transitions between visits in GMPRO

The total time required to complete this two stop route is the sum of all transition travel times and the service times at each stop. In the example above, that’s 3 min (travel) + 10 min (stop) + 4 min (travel) + 10 min (stop) + 5 min (travel) = 32 min.

### Modeling parking time for multi-stop deliveries at the same location 

The 10 minute service time assigned to each stop typically includes both the time spent parking and the time spent making the delivery. However, if a driver has two stops within the same building, it shouldn't take 20 minutes total - parking only needs to happen once. To solve this in GMPRO, we first separate the delivery time from the parking time. For example, we might allocate 6 minutes for parking and 4 minutes for the actual service at each stop.

![Transitions between visits with parking applied (no squashed durations)](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2025/12/Screenshot-2025-12-21-at-2.32.32---PM.png)

Transitions between visits with parking applied (no squashed durations)

Next, for each unique parking spot in the optimization, we assign a distinct tag to the relevant `shipment` objects. This tag should be a unique string not used elsewhere in the model. For example, if both visits are to a building at 1074 Jefferson Avenue, West Vancouver, you could tag each associated shipment with "1074-jefferson-ave" like so:

```
{
    "shipments": [
        {
            "deliveries": [
                {
                    "arrivalLocation": {
                        "latitude": 49.2545595,
                        "longitude": -123.1096174
                    },
                    "duration": "600s",
                    "timeWindows": [
                        {
                            "startTime": "2024-07-08T16:00:00Z",
                            "endTime": "2024-07-08T18:00:00Z"
                        }
                    ]
                }
            ],
            "loadDemands": {
                "weight": {
                    "amount": "1"
                }
            },
            "label": "1074-jefferson-ave"
        }
    ]
}
```

Next, we use the [transition attributes](https://developers.google.com/maps/documentation/route-optimization/reference/rpc/google.maps.routeoptimization.v1?ref=blog.afi.io#google.maps.routeoptimization.v1.TransitionAttributes) object in `model` to add a 360s (6 min) parking delay to any transition that leads to a `visit` tagged with "1074-jefferson-ave-parking", unless the transition originates from another visit with the same tag. In other words, if the driver is already delivering within the same building, the solver should not add additional parking time.

```
{
    "transition_attributes": [
        {
            "excluded_src_tag": "1074-jefferson-ave",
            "dst_tag": "1074-jefferson-ave",
            "delay": "360s"
        }
    ]
}
```

When passed to GMPRO, the additional parking time of 360s (6 min) shows up in the response as a `delayDuration` in the relevant `transitions` object.

```
{
    "transitions": [
        {
            "travelDuration": "808s",
            "travelDistanceMeters": 13112,
            "delayDuration": "360s",
            "waitDuration": "0s",
            "totalDuration": "1168s",
            "startTime": "2024-07-08T16:00:00Z",
            "vehicleLoads": {
                "weight": {
                    "amount": "6"
                }
            }
        }
    ]
}
```

When viewed as a timeline, you’ll notice that any transition leading to `visits` with the same tag do not include additional parking time i.e. "park once, deliver multiple".

![Transitions between visits with squashed durations applied](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2025/12/Screenshot-2025-12-21-at-3.38.56---PM.png)

Transitions between visits with squashed durations applied

### GMPRO squashed durations worked example

Let's put everything together. Here's a sample GMPRO JSON input with squashed durations enabled for all shipments being delivered to 1074 Jefferson Avenue.

**Input**

```
{
    "model": {
        "shipments": [
            {
                "deliveries": [
                    {
                        "arrivalLocation": {
                            "latitude": 49.2545595,
                            "longitude": -123.1096174
                        },
                        "duration": "600s",
                        "timeWindows": [
                            {
                                "startTime": "2024-07-08T16:00:00Z",
                                "endTime": "2024-07-08T18:00:00Z"
                            }
                        ]
                    }
                ],
                "loadDemands": {
                    "weight": {
                        "amount": "1"
                    }
                },
                "label": "yvr123"
            },
            {
                "deliveries": [
                    {
                        "arrivalLocation": {
                            "latitude": 49.313724,
                            "longitude": -123.0514561
                        },
                        "duration": "600s",
                        "timeWindows": [
                            {
                                "startTime": "2024-07-08T16:00:00Z",
                                "endTime": "2024-07-08T18:00:00Z"
                            }
                        ]
                    }
                ],
                "loadDemands": {
                    "weight": {
                        "amount": "1"
                    }
                },
                "label": "yvr789"
            },
            {
                "deliveries": [
                    {
                        "arrivalLocation": {
                            "latitude": 49.343481,
                            "longitude": -123.0863414
                        },
                        "duration": "600s",
                        "timeWindows": [
                            {
                                "startTime": "2024-07-08T16:00:00Z",
                                "endTime": "2024-07-08T18:00:00Z"
                            }
                        ]
                    }
                ],
                "loadDemands": {
                    "weight": {
                        "amount": "1"
                    }
                },
                "label": "yvr987"
            },
            {
                "deliveries": [
                    {
                        "arrivalLocation": {
                            "latitude": 49.3352081,
                            "longitude": -123.1453979
                        },
                        "duration": "240s",
                        "timeWindows": [
                            {
                                "startTime": "2024-07-08T16:00:00Z",
                                "endTime": "2024-07-08T18:00:00Z"
                            }
                        ],
                        "tags": ["1074-jefferson-ave"]
                    }
                ],
                "loadDemands": {
                    "weight": {
                        "amount": "1"
                    }
                },
                "label": "yvr654-a"
            },
            {
                "deliveries": [
                    {
                        "arrivalLocation": {
                            "latitude": 49.3352081,
                            "longitude": -123.1453979
                        },
                        "duration": "240s",
                        "timeWindows": [
                            {
                                "startTime": "2024-07-08T16:00:00Z",
                                "endTime": "2024-07-08T18:00:00Z"
                            }
                        ],
                        "tags": ["1074-jefferson-ave"]
                    }
                ],
                "loadDemands": {
                    "weight": {
                        "amount": "1"
                    }
                },
                "label": "yvr654-b"
            },
            {
                "deliveries": [
                    {
                        "arrivalLocation": {
                            "latitude": 49.3352081,
                            "longitude": -123.1453979
                        },
                        "duration": "240s",
                        "timeWindows": [
                            {
                                "startTime": "2024-07-08T16:00:00Z",
                                "endTime": "2024-07-08T18:00:00Z"
                            }
                        ],
                        "tags": ["1074-jefferson-ave"]
                    }
                ],
                "loadDemands": {
                    "weight": {
                        "amount": "1"
                    }
                },
                "label": "yvr654-c"
            }
        ],
        "vehicles": [
            {
                "startLocation": {
                    "latitude": 49.375757,
                    "longitude": -123.2739673
                },
                "loadLimits": {
                    "weight": {
                        "maxLoad": 10
                    }
                },
                "startTimeWindows": [
                    {
                        "startTime": "2024-07-08T16:00:00Z"
                    }
                ],
                "endTimeWindows": [
                    {
                        "endTime": "2024-07-08T18:00:00Z"
                    }
                ],
                "label": "mark-yvr",
                "costPerKilometer": 1
            }
        ],
        "transition_attributes": [
            {
              "excluded_src_tag": "1074-jefferson-ave",
              "dst_tag": "1074-jefferson-ave",
              "delay": "360s"
            }
        ],
        "globalStartTime": "2024-07-08T07:00:00Z",
        "globalEndTime": "2024-07-09T06:59:00Z"
    },
    "populatePolylines": true
}
```

And here's the response (note the 360s `delayDuration` on the first transition):

**Output**

```
{
    "routes": [
        {
            "vehicleLabel": "mark-yvr",
            "vehicleStartTime": "2024-07-08T16:00:00Z",
            "vehicleEndTime": "2024-07-08T17:45:11Z",
            "visits": [
                {
                    "shipmentIndex": 5,
                    "startTime": "2024-07-08T16:19:28Z",
                    "detour": "0s",
                    "shipmentLabel": "yvr654-c",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 4,
                    "startTime": "2024-07-08T16:23:28Z",
                    "detour": "240s",
                    "shipmentLabel": "yvr654-b",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 3,
                    "startTime": "2024-07-08T16:27:28Z",
                    "detour": "480s",
                    "shipmentLabel": "yvr654-a",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 2,
                    "startTime": "2024-07-08T16:40:33Z",
                    "detour": "1416s",
                    "shipmentLabel": "yvr987",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "shipmentIndex": 1,
                    "startTime": "2024-07-08T17:00:02Z",
                    "detour": "2439s",
                    "shipmentLabel": "yvr789",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                },
                {
                    "startTime": "2024-07-08T17:35:11Z",
                    "detour": "3543s",
                    "shipmentLabel": "yvr123",
                    "loadDemands": {
                        "weight": {
                            "amount": "-1"
                        }
                    }
                }
            ],
            "transitions": [
                {
                    "travelDuration": "808s",
                    "travelDistanceMeters": 13112,
                    "delayDuration": "360s",
                    "waitDuration": "0s",
                    "totalDuration": "1168s",
                    "startTime": "2024-07-08T16:00:00Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "6"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T16:23:28Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "5"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T16:27:28Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "4"
                        }
                    }
                },
                {
                    "travelDuration": "545s",
                    "travelDistanceMeters": 6329,
                    "waitDuration": "0s",
                    "totalDuration": "545s",
                    "startTime": "2024-07-08T16:31:28Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "3"
                        }
                    }
                },
                {
                    "travelDuration": "569s",
                    "travelDistanceMeters": 7066,
                    "waitDuration": "0s",
                    "totalDuration": "569s",
                    "startTime": "2024-07-08T16:50:33Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "2"
                        }
                    }
                },
                {
                    "travelDuration": "1509s",
                    "travelDistanceMeters": 14500,
                    "waitDuration": "0s",
                    "totalDuration": "1509s",
                    "startTime": "2024-07-08T17:10:02Z",
                    "vehicleLoads": {
                        "weight": {
                            "amount": "1"
                        }
                    }
                },
                {
                    "travelDuration": "0s",
                    "waitDuration": "0s",
                    "totalDuration": "0s",
                    "startTime": "2024-07-08T17:45:11Z",
                    "vehicleLoads": {
                        "weight": {}
                    }
                }
            ],
            "routePolyline": {
                "points": "spjlHh{koV^`@HFHBB?D@JBDDB@ZLPaF@e@B[Du@BYBI@ADGDCF?@?L@TD`@@j@AP?H?RUN@J@LBNDLDRJHDTTLLP`"
            },
            "metrics": {
                "performedShipmentCount": 6,
                "travelDuration": "3431s",
                "waitDuration": "0s",
                "delayDuration": "360s",
                "breakDuration": "0s",
                "visitDuration": "2520s",
                "totalDuration": "6311s",
                "travelDistanceMeters": 41007,
                "maxLoads": {
                    "weight": {
                        "amount": "6"
                    }
                },
                "performedMandatoryShipmentCount": 6
            },
            "routeCosts": {
                "model.vehicles.cost_per_kilometer": 41.007
            },
            "routeTotalCost": 41.007,
            "vehicleFullness": {
                "maxFullness": 0.87652777777777779,
                "maxLoad": 0.6,
                "activeSpan": 0.87652777777777779
            }
        }
    ],
    "metrics": {
        "aggregatedRouteMetrics": {
            "performedShipmentCount": 6,
            "travelDuration": "3431s",
            "waitDuration": "0s",
            "delayDuration": "360s",
            "breakDuration": "0s",
            "visitDuration": "2520s",
            "totalDuration": "6311s",
            "travelDistanceMeters": 41007,
            "maxLoads": {
                "weight": {
                    "amount": "6"
                }
            },
            "performedMandatoryShipmentCount": 6
        },
        "usedVehicleCount": 1,
        "earliestVehicleStartTime": "2024-07-08T16:00:00Z",
        "latestVehicleEndTime": "2024-07-08T17:45:11Z",
        "totalCost": 41.007,
        "costs": {
            "model.vehicles.cost_per_kilometer": 41.007
        }
    }
}
```

If you upload these files to my [GMPRO Viewer](https://gmpro-viewer.afi.dev/?ref=blog.afi.io), you'll see the parking time displayed with a small 🅿️ icon.

![](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2025/12/Screenshot-2025-12-21-at-4.43.59---PM.png)

### Is there a better way to handle parking and squashed durations?

There's much to like about how GMPRO handles squashed durations. It works as advertised and offers significant flexibility. For example, you can apply squashed durations selectively to specific locations, or customize the parking time based on each location. However, this approach requires you to preprocess your shipment list by grouping deliveries that belong to the same building or apartment complex and assigning a unique tag to each group. You’ll then need to define transition attributes for each of these tags.

A more elegant way of handling parking is used by [Routific](https://www.routific.com/?ref=blog.afi.io). In their route optimization API, implementing squashed durations is as easy as adding a single line, `"squash_durations": 1`, to the `options` payload attached to each optimization request. This squashes the durations of each subsequent delivery at the same address. 

```
{
    "options": {
        "squash_durations": 1
    }
}
```

Let's say you have 6 stops at the same address and each has a service time of 10 minutes. If you set `squash_durations` to 1, it will squash it to 1 minute, so the total duration for the 6 stops would be 10 min + 1 min+ 1 min + 1 min + 1 min + 1 min = 15 min.

**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 15: GMPRO docs: Google Large Vehicle Routing](https://www.afi.io/blog/gmpro-docs-google-large-vehicle-routing/?ref=blog.afi.io)