Vehicle start & end locations
You will learn how to use the Starts and Ends options with a practical example.
Traditional vehicle routing problems (VRP) require a depot where a route starts and ends. On the other hand, open vehicle routing problems (OVRP) do not specify an ending location for a vehicle. The router engine provides the Starts and Ends options to configure starting and ending locations for the vehicles, respectively. These options are independent of each other, so you could solve VRPs by configuring the same starts and ends or OVRPs by configuring either of those.

Example

The router example is used as a base, where routes are created to visit seven landmarks in Kyoto using two vehicles. This time, vehicle v1 has a starting location (but no end) and vehicle v2 has the same starting and ending locations.
starts-ends-input
Save the following information in an input.json file (see input and output for more information on working with input files).
JSON
1
{
2
"stops": [
3
{
4
"id": "Fushimi Inari Taisha",
5
"position": { "lon": 135.772695, "lat": 34.967146 }
6
},
7
{
8
"id": "Kiyomizu-dera",
9
"position": { "lon": 135.78506, "lat": 34.994857 }
10
},
11
{
12
"id": "Nijō Castle",
13
"position": { "lon": 135.748134, "lat": 35.014239 }
14
},
15
{
16
"id": "Kyoto Imperial Palace",
17
"position": { "lon": 135.762057, "lat": 35.025431 }
18
},
19
{
20
"id": "Gionmachi",
21
"position": { "lon": 135.775682, "lat": 35.002457 }
22
},
23
{
24
"id": "Kinkaku-ji",
25
"position": { "lon": 135.728898, "lat": 35.039705 }
26
},
27
{
28
"id": "Arashiyama Bamboo Forest",
29
"position": { "lon": 135.672009, "lat": 35.017209 }
30
}
31
],
32
"vehicles": ["v1", "v2"],
33
"starts": [
34
{ "lon": 135.73723, "lat": 35.04381 },
35
{ "lon": 135.758794, "lat": 34.98608 }
36
],
37
"ends": [{}, { "lon": 135.758794, "lat": 34.98608 }]
38
}
Copied!
Note that the starts and ends indices map to the indices of the vehicles. This means that the starting location of vehicle v1 is {"lon": 135.73723, "lat": 35.04381} and it has an empty ending location.

Code

The following program uses the CLI Runner to obtain a solution and requires access to the Nextmv code repository on GitHub. To request access, please contact [email protected].
To proceed with running the example, create a main.go file and use the code snippet below.
Go
1
package main
2
3
import (
4
"github.com/nextmv-io/code/engines/route"
5
"github.com/nextmv-io/code/hop/run/cli"
6
"github.com/nextmv-io/code/hop/solve"
7
)
8
9
// Struct to read from JSON in.
10
type input struct {
11
Stops []route.Stop `json:"stops,omitempty"`
12
Vehicles []string `json:"vehicles,omitempty"`
13
Starts []route.Position `json:"starts,omitempty"`
14
Ends []route.Position `json:"ends,omitempty"`
15
}
16
17
// Use the CLI runner to solve a Vehicle Routing Problem.
18
func main() {
19
f := func(i input, opt solve.Options) (solve.Solver, error) {
20
router, err := route.NewRouter(
21
i.Stops,
22
i.Vehicles,
23
route.Starts(i.Starts),
24
route.Ends(i.Ends),
25
)
26
if err != nil {
27
return nil, err
28
}
29
30
return router.Solver(opt)
31
}
32
33
cli.Run(f)
34
}
Copied!
To execute the example, specify the path to the input.json file using command-line flags and use jq to extract the solution state (see runners for more information on building and running programs).
Bash
1
go run main.go -hop.runner.input.path input.json | jq .state
Copied!

Solution

The solution should look similar to this one:
JSON
1
{
2
"unassigned": [],
3
"vehicles": [
4
{
5
"id": "v1",
6
"route": [
7
{
8
"id": "v1-start",
9
"position": {
10
"lon": 135.73723,
11
"lat": 35.04381
12
}
13
},
14
{
15
"id": "Kinkaku-ji",
16
"position": {
17
"lon": 135.728898,
18
"lat": 35.039705
19
}
20
},
21
{
22
"id": "Arashiyama Bamboo Forest",
23
"position": {
24
"lon": 135.672009,
25
"lat": 35.017209
26
}
27
}
28
],
29
"route_duration": 663
30
},
31
{
32
"id": "v2",
33
"route": [
34
{
35
"id": "v2-start",
36
"position": {
37
"lon": 135.758794,
38
"lat": 34.98608
39
}
40
},
41
{
42
"id": "Fushimi Inari Taisha",
43
"position": {
44
"lon": 135.772695,
45
"lat": 34.967146
46
}
47
},
48
{
49
"id": "Kiyomizu-dera",
50
"position": {
51
"lon": 135.78506,
52
"lat": 34.994857
53
}
54
},
55
{
56
"id": "Gionmachi",
57
"position": {
58
"lon": 135.775682,
59
"lat": 35.002457
60
}
61
},
62
{
63
"id": "Kyoto Imperial Palace",
64
"position": {
65
"lon": 135.762057,
66
"lat": 35.025431
67
}
68
},
69
{
70
"id": "Nijō Castle",
71
"position": {
72
"lon": 135.748134,
73
"lat": 35.014239
74
}
75
},
76
{
77
"id": "v2-end",
78
"position": {
79
"lon": 135.758794,
80
"lat": 34.98608
81
}
82
}
83
],
84
"route_duration": 1483
85
}
86
]
87
}
Copied!
You can see that v1 has two stops assigned and is an open route, whereas v2 has five stops assigned in a closed route.
starts-ends-output
Export as PDF
Copy link