Custom matrices¶
Info
This feature is only available for Platform. You can find a list of all available features.
Using duration matrices (either derived from haversine and speed or a road network) may not always represent the real world drive times good enough, depending on the region where you operate or the time of the day (rush hours).
Following, we will make use of two concepts in our vehicle routing engine that help you mitigate these issues:
- Expression scaling
- Time dependent expressions
Expression scaling can be used to make drive times between stops slower or
faster by a given factor. Whilst time dependent duration expressions introduce
a concept of time to an otherwise fixed drive time. This means that you can
specify multiple duration matrices along with the information at what time of
the day they should be used. In the exmaple below we will combine both concepts.
Example¶
Let's say you are generally happy with the drive times that are calculated through haversine and a constant speed. But you want to adhere to rush hours in the morning and evening.
First, create a haversine travel duration expression which will be used as a
default for the whole day and scale it to represent slower driving during rush
hour:
haversineExpression := nextroute.NewHaversineExpression()
defaultDurationExpression := nextroute.NewTravelDurationExpression(
haversineExpression,
common.NewSpeed(10, common.MetersPerSecond),
)
slowDurationExpression := nextroute.NewScaledDurationExpression(defaultDurationExpression, 2.0)
Of cource, you can use any other travel duration expressions as your default.
You could, for example, use a custom duration matrix or
map data.
Next, create a time dependent duration expression. We will create this
expression using the default expression we created before:
timeDependentExpression, err := nextroute.NewTimeDependentDurationExpression(
model,
defaultDurationExpression,
)
Add the time slots when you expect the rush hour to start and end to
the time dependent duration expression using the slower travel
duration expression:
s1 := time.Date(2023, 1, 1, 6, 30, 0, 0, time.UTC)
e1 := time.Date(2023, 1, 1, 9, 30, 0, 0, time.UTC)
err = timeDependentExpression.SetExpression(
s1,
e1,
slowDurationExpression,
)
if err != nil {
return runSchema.Output{}, err
}
s2 := time.Date(2023, 1, 1, 17, 30, 0, 0, time.UTC)
e2 := time.Date(2023, 1, 1, 19, 30, 0, 0, time.UTC)
err = timeDependentExpression.SetExpression(
s2,
e2,
slowDurationExpression,
)
if err != nil {
return runSchema.Output{}, err
}
And finally, update the model and add the time dependent duration expression to
every vehicle type:
for _, v := range model.Vehicles() {
err := v.VehicleType().SetTravelDurationExpression(timeDependentExpression)
if err != nil {
return runSchema.Output{}, err
}
}
Make sure that the times for the time slots must go hand in hand with the
start and end times of your vehicles. Here is an example input.json that we
created to work with the code snippets above:
{
"defaults": {
"vehicles": {
"speed": 20,
"start_time": "2023-01-01T06:00:00-06:00",
"end_time": "2023-01-01T10:00:00-06:00"
}
},
"stops": [
{
"id": "Fushimi Inari Taisha",
"location": { "lon": 135.772695, "lat": 34.967146 }
},
{
"id": "Kiyomizu-dera",
"location": { "lon": 135.78506, "lat": 34.994857 }
},
{
"id": "Nijō Castle",
"location": { "lon": 135.748134, "lat": 35.014239 }
},
{
"id": "Kyoto Imperial Palace",
"location": { "lon": 135.762057, "lat": 35.025431 }
},
{
"id": "Gionmachi",
"location": { "lon": 135.775682, "lat": 35.002457 }
},
{
"id": "Kinkaku-ji",
"location": { "lon": 135.728898, "lat": 35.039705 }
},
{
"id": "Arashiyama Bamboo Forest",
"location": { "lon": 135.672009, "lat": 35.017209 }
}
],
"vehicles": [
{
"id": "v1",
"start_location": { "lon": 135.672009, "lat": 35.017209 }
},
{
"id": "v2",
"start_location": { "lon": 135.728898, "lat": 35.039705 }
}
]
}
To avoid hard-coding the start and end times of rush hours, you can levarage
custom data in your input.json.