You are viewing Nextmv legacy docs. ⚡️ Go to latest docs ⚡️

Router

Output

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:

{
  "unassigned": [
    {
      "id": "...",
      "position": {...}
    },
    {...}
  ],
  "vehicles": [
    {
      "id": "...",
      "route": [
        {
          "id": "...",
          "position": {...}
        },
        {...}
      ],
      "route_duration": ...
    },
    {...}
  ]
}
Copy

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 marshaler, relying on the fleet engine. Given this, the Output option takes one argument: a Marshaler interface, part of the route package. You can specify a custom type to implement this interface and personalize the state output marshaling in the underlying fleet engine.

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).

{
  "stops": [
    {
      "id": "Fushimi Inari Taisha",
      "position": { "lon": 135.772695, "lat": 34.967146 }
    },
    {
      "id": "Kiyomizu-dera",
      "position": { "lon": 135.78506, "lat": 34.994857 }
    },
    {
      "id": "Nijō Castle",
      "position": { "lon": 135.748134, "lat": 35.014239 }
    },
    {
      "id": "Kyoto Imperial Palace",
      "position": { "lon": 135.762057, "lat": 35.025431 }
    },
    {
      "id": "Gionmachi",
      "position": { "lon": 135.775682, "lat": 35.002457 }
    },
    {
      "id": "Kinkaku-ji",
      "position": { "lon": 135.728898, "lat": 35.039705 }
    },
    {
      "id": "Arashiyama Bamboo Forest",
      "position": { "lon": 135.672009, "lat": 35.017209 }
    }
  ],
  "vehicles": ["v1", "v2"]
}
Copy

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 support@nextmv.io.

To proceed with running the example, create a main.go file and use the code snippet below. A custom type called marshaler is defined to implement the Marshaler 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.

package main

import (
    "encoding/json"

    "github.com/nextmv-io/code/engines/route"
    fleetEngine "github.com/nextmv-io/code/engines/route/fleet"
    "github.com/nextmv-io/code/hop/run/cli"
    "github.com/nextmv-io/code/hop/solve"
)

// Struct to read from JSON in.
type input struct {
    Stops    []route.Stop `json:"stops,omitempty"`
    Vehicles []string     `json:"vehicles,omitempty"`
}

// Struct to customize part of the output.
type outputRoute struct {
    VehicleID string `json:"vehicle_id,omitempty"`
    NumStops  int    `json:"num_stops,omitempty"`
}

// Custom type to implement the Marshaler interface.
type marshaler struct {
    Routes     []outputRoute `json:"routes"`
    Unassigned int           `json:"unassigned"`
}

// Marshal the number of unassigned stops and the number of stops per route.
func (m *marshaler) Marshal(s fleetEngine.State) ([]byte, error) {
    vehicles := s.Vehicles()
    routes := make([]outputRoute, len(vehicles))
    for v, state := range vehicles {
        // Ignore vehicle's start and end locations by using the length of
        // the route and subtracting two stops.
        routes[v] = outputRoute{
            VehicleID: state.Input().VehicleID.(string),
            NumStops:  len(state.Route()) - 2,
        }
    }

    m.Routes = routes
    m.Unassigned = s.Unassigned().Len()
    return json.Marshal(m)
}

// Use the CLI runner to solve a Vehicle Routing Problem.
func main() {
    f := func(i input, opt solve.Options) (solve.Solver, error) {
        // Declare the router and its solver.
        m := marshaler{}
        router, err := route.NewRouter(i.Stops, i.Vehicles, route.Output(&m))
        if err != nil {
            return nil, err
        }

        return router.Solver(opt)
    }

    cli.Run(f)
}
Copy

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).

go run main.go -hop.runner.input.path input.json | jq .state
Copy

Solution

The solution should look similar to this one:

{
  "routes": [
    {
      "vehicle_id": "v1",
      "num_stops": 1
    },
    {
      "vehicle_id": "v2",
      "num_stops": 6
    }
  ],
  "unassigned": 0
}
Copy

You can see that the marshalled solution state is different than the introductory router solution.

Page last updated

Go to on-page nav menu