Single vehicle routing

Last updatedAugust 12, 2021

The following is an example for how to use the Nextmv vehicle engine to route a single driver from Mvrs-N-Shkrs Coffee Distributor to a set of coffee shops that ordered beans while minimizing distance traveled.

Our input for the model is pretty straightforward, with a set of locations and a single vehicle with a starting location.

{
  "vehicles":
    {
      "vehicle_id": "Mvrs-N-Shkrs-1",
      "start": [33.122746, -96.659222],
      "end": [33.122746, -96.659222]
    },
  "requests": [
    {
      "location_id": "Coffee Labs Cafe",
      "position": [33.004745, -96.827094]
    },
    {
      "location_id": "The Caffeinated Cat",
      "position": [33.005741, -96.86074]
    },
    {
      "location_id": "Barks and Brew",
      "position": [32.969456, -96.820915]
    },
    {
      "location_id": "Study Loft",
      "position": [32.921629, -96.695259]
    },
    {
      "location_id": "Java the Hutt",
      "position": [32.875507, -96.673973]
    }
  ]
}

Since the value we are minimizing is the same as the vehicle engine, but the input schema is different, we are going to use the DefaultModel to customize for our data.

package route

import (
  "github.com/nextmv-io/code/hop/model"
  "github.com/nextmv-io/code/hop/solve"
  "github.com/nextmv-io/code/engines/route/vehicle"

  "github.com/mooney-corp/coffee-router/schema"
  "github.com/nextmv-io/code/engines/measure"
  vehicleschema "github.com/nextmv-io/code/engines/route/vehicle/schema"
)

// Solver constructs root state from inputs and options and returns a Minimizer.
func Solver(input schema.Input, opt solve.Options) (solve.Solver, error) {

  // Extract geo points from requests to use in vehicle.
  // i.e. [location 1, location 2, ...., location n]
  points := []measure.Point{}

  for _, request := range input.Requests {
    points = append(points, request.Position)
  }

  // Create an intDomain for efficient indexing points.
  requests := model.Domain(model.Range(0, len(points)-1))

  // Add the vehicle start and end to points
  input.Vehicle.StartIndex = len(points)
  points = append(points, input.Vehicle.Start)
  input.Vehicle.EndIndex = input.Vehicle.StartIndex

  // measure the cost between location in haversine meters
  measure := measure.HaversineByIndex(points)

  // Build the vehicle model input
  vehicleInput := vehicleschema.Vehicle{
    VehicleID: input.Vehicle.ID,
    Locations: requests,
    Start:     input.Vehicle.StartIndex,
    End:       input.Vehicle.EndIndex,
  }

  // Use the default model
  root, err := vehicle.DefaultModel(vehicleInput, measure)

  if err != nil {
    return nil, err
  }
  opt.Tags["measure"] = measure

  return solve.Minimizer(root, opt), nil
}

This is all we need to do! go build and we now have a vehicle router for our coffee business.

Was this helpful?