Output marshalling
You will learn how to use the Output option with a practical example.
By default, the router engine marshals the solution state with basic information, similar to this structure:
JSON
1
{
2
"unassigned": [
3
{
4
"id": "...",
5
"position": {...}
6
},
7
{...}
8
],
9
"vehicles": [
10
{
11
"id": "...",
12
"route": [
13
{
14
"id": "...",
15
"position": {...}
16
},
17
{...}
18
],
19
"route_duration": ...
20
},
21
{...}
22
]
23
}
Copied!
In the above snippet, you can observe that only the unassigned stops and the routes assigned to the vehicles are shown. Sometimes it is useful to modify the output to achieve a desired structure or to showcase custom information. The router engine provides the Output option to set up a custom output marshaller, relying on the vehicle and fleet engines. Given this distinction, the Output option takes two arguments: VehicleMarshaller and FleetMarshaller interfaces, part of the router engine. You can specify custom types to implement these interfaces and personalize the state output marshalling in the underlying vehicle and fleet engines.

Example

The aim of this example is to define a custom output when using multiple vehicles. The introductory router example is used as a base, where routes are created to visit seven landmarks in Kyoto using two vehicles.
output-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
}
Copied!

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. A custom type called fleetMarshaller is defined to implement the FleetMarshaller interface. To modify the default output, only the total number of stops are displayed per route, as opposed to listing all stops. The same change is applied to the unassigned stops. Note that the first argument to the Output option is nil, as we are only customizing the output when using multiple vehicles.
Go
1
package main
2
3
import (
4
"encoding/json"
5
6
"github.com/nextmv-io/code/engines/route"
7
fleetEngine "github.com/nextmv-io/code/engines/route/fleet"
8
"github.com/nextmv-io/code/hop/run/cli"
9
"github.com/nextmv-io/code/hop/solve"
10
)
11
12
// Struct to read from JSON in.
13
type input struct {
14
Stops []route.Stop `json:"stops,omitempty"`
15
Vehicles []string `json:"vehicles,omitempty"`
16
}
17
18
// Struct to customize part of the output.
19
type outputRoute struct {
20
VehicleID string `json:"vehicle_id,omitempty"`
21
NumStops int `json:"num_stops,omitempty"`
22
}
23
24
// Custom type to implement the FleetMarshaller interface.
25
type fleetMarshaller struct {
26
Routes []outputRoute `json:"routes"`
27
Unassigned int `json:"unassigned"`
28
}
29
30
// Marshall the number of unassigned stops and the number of stops per route.
31
func (m *fleetMarshaller) Marshal(s fleetEngine.State) ([]byte, error) {
32
vehicles := s.Vehicles()
33
routes := make([]outputRoute, len(vehicles))
34
for v, state := range vehicles {
35
// Ignore vehicle's start and end locations by using the length of
36
// the route and subtracting two stops.
37
routes[v] = outputRoute{
38
VehicleID: state.Input().VehicleID.(string),
39
NumStops: len(state.Route()) - 2,
40
}
41
}
42
43
m.Routes = routes
44
m.Unassigned = s.Unassigned().Len()
45
return json.Marshal(m)
46
}
47
48
// Use the CLI runner to solve a Vehicle Routing Problem.
49
func main() {
50
f := func(i input, opt solve.Options) (solve.Solver, error) {
51
// Declare the router and its solver.
52
m := fleetMarshaller{}
53
router, err := route.NewRouter(i.Stops, i.Vehicles, route.Output(nil, &m))
54
if err != nil {
55
return nil, err
56
}
57
58
return router.Solver(opt)
59
}
60
61
cli.Run(f)
62
}
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
"routes": [
3
{
4
"vehicle_id": "v1",
5
"num_stops": 1
6
},
7
{
8
"vehicle_id": "v2",
9
"num_stops": 6
10
}
11
],
12
"unassigned": 0
13
}
Copied!
You can see that the marshalled solution state is different than the introductory router solution. If a custom output is desired when using a single vehicle, the VehicleMarshaller interface should also be implemented.
Export as PDF
Copy link