Routing Service

Maciej Mionskowski edited this page Jan 27, 2017 · 22 revisions

Routing service is the main service you will use. It provides a wrapper for most of the route4me functionality:

  • Managing(running, updating, fetching) optimizations
  • Managing(updating, fetching) routes

Creating Service

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
}

Addresses

Examples written below assume that you have an []*Address under addresses variable. It's a recurrent theme. You can find the struct definition here. Member names should be self-descriptive. If something's unclear leave an issue or contact support.

Getting address

In order to get an address you have to acquire route-id and route-destination-id. You can extract that data from here.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    address, err = service.GetAddress(&routing.AddressQuery{RouteID: "route-id",
    RouteDestinationID: "route-destination-id",
    Notes: true, //If this is set to true all notes associated with this address will be returned.
    })
    if err != nil {
        //handle errors
        return
    }
}

Updating address

In order to update an address you have to have it's object first. You can get it by calling either GetRoute or GetAddress

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    updated, err := service.UpdateAddress(address)
    if err != nil {
        //handle errors
        return
    }
    //do something with updated address
}

Marking address as detected and visited

The example refers to the process of marking an address as Detected as Visited.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    var address *routing.Address //fetch address
    address.IsVisited = true
    updated, err := service.UpdateAddress(address)
    if err != nil {
        //handle errors
        return
    }
    //do something with updated address
}

Marking address as detected and departed

The example refers to the process of marking an address as Detected as Departed.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    var address *routing.Address //fetch address
    address.IsDeparted = true
    updated, err := service.UpdateAddress(address)
    if err != nil {
        //handle errors
        return
    }
    //do something with updated address
}

Deleting address

Remove a destination (an address) with specified route_destination_id from an optimization problem with specified optimization_problem_id.

Removing an optimization from an optimization problem is different from removing it from a route. The optimization problem is a container for all locations that are being optimized simultaneously When an address is removed from an optimization, the sub-routes get rebalanced as a result of the modification However, when an address is removed from a sub-route, that is the same as a manual override (i.e. order cancellation).

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    deletedRouteDestinationID, err := service.DeleteAddress(optimizationProblemId,routeID)
    if err != nil {
        //handle errors
        return
    }
    //do something with deleted id
}

Routes

  • A Route is a collection of addresses usually sorted in the most optimal sequence
  • One route can have many addresses
  • Each address can have attributes, constraints, and metadata which influence the way the route is sequenced

Search through routes

The example refers to the process of searching for the specified text throughout all routes belonging to the user's account.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    routes, err := service.GetTeamRoutes(&RouteQuery{Query: "Tbilisi"})
    if err != nil {
        //handle errors
        return
    }
    //do something with routes
}

Getting Route

In order to get a route you need its ID (route-id). Not specifying the ID field will result in an error being returned.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    route, err = service.GetRoute(&routing.RouteQuery{ID: route-id})
    if err != nil {
        //handle errors
        return
    }
}

Get path points of the route

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    routes, err := service.GetRoute(&routing.RouteQuery{ID: "routeID", PathOutput: routing.Points})
    if err != nil {
        //handle errors
        return
    }
    //do something with route
}

Get Route Directions

Edge by edge turn-by-turn directions. Note: For round-trip routes (parameters.rt = true), the return to the start address is returned as well.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    routes, err := service.GetRoute(&routing.RouteQuery{ID: "routeID", Directions: true})
    if err != nil {
        //handle errors
        return
    }
    //do something with routes
}

Getting Route ID from Optimization Problem ID (problem-id)

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    routeID, err := service.GetRouteID("problem-id")
    if err != nil {
        t.Error(err)
        return
    }
    //Do something with routeID
}

Deleting Routes

In order to delete routes You need to know their ids (route-id). You can fetch them by calling GetTeamRoutes().

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    routes, err = service.DeleteRoutes([]string{"route-id"})
    if err != nil {
        //handle errors
        return
    }
}

Updating Route

In order to update the Route you need to have it's id(route-id). It's recommended to use GetTeamRoutes to fetch instances.

  • Updating a route supports the Reoptimize: True parameter, which reoptimizes only that route. Also supports the parameters from GET.
  • By sending RecomputeDirections=1 we request that the route directions be recomputed (note that this does happen automatically if certain properties of the route are updated, such as stop sequence_no changes or round-tripness)
  • After updating the route there is no guarantee that the route_destination_id values are preserved! It may create copies resulting in new destination IDs, especially when dealing with multiple depots.
import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    route.Field = "updated value"
    updated, err := service.UpdateRoute(route)
    if err != nil {
        //handle errors
        return
    }
    //do something with updated route
}

Duplicating Route

Duplicates the route by accepting route-id. The duplicated route has all the same metadata and account ownership information as the original route. However, route notes and visited status flags are not copied into the new route. The name of the new route is automatically appended with the string " (Duplicate)" to indicate that the route is a duplicate route. There is currently no special designation inside the database to indicate that a route originated from another route.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    duplicated, err := service.DuplicateRoute(`route-id`)
    if err != nil {
        //handle errors
        return
    }
}

Merging routes

Accepts an array of valid route ID’s which exist in the Route4Me database and merges all the route destinations into a single new route. A new route ID is generated and it is a union of all the routes in the provided list of route IDs. The new route’s destinations are all in the sequence of the route ID’s that are being merged. The merged route has all the same metadata and account ownership information as the original routes, however route notes and visited status flags are not copied into the new route.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    //fetch routes to merge
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    err := service.MergeRoutes(&routing.MergeRequest{
        RouteIDs:       routes[0].ID + "," + routes[1].ID,
        RemoveOrigin:   false,
        DepotAddress:   depot.Addresses[0].AddressString,
        DepotLatitude:  depot.Addresses[0].Latitude,
        DepotLongitude: depot.Addresses[0].Longitude,
    })
    if err != nil {
        //handle errors
        return
    }
}

Sharing a route

Share a route via email.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    err := service.ShareRoute("route-id","email@example.com")
    if err != nil {
        //handle errors
        return
    }
}

Adding destinations to a route

In order to add destinations to a route You need to know its ids (route-id). You can fetch them by calling GetTeamRoutes().

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    addresses := []routing.Address{
        //address to be added
        routing.Address{
            AddressString: "717 5th Ave New York, NY 10021",
            Alias:         "Giorgio Armani",
            Latitude:      40.7669692,
            Longitude:     -73.9693864,
            Time:          0,
        },
    }
    do, err = service.AddRouteDestinations("route-id", addresses)
    if err != nil {
        //handle errors
        return
    }
    // do is DataObject
}

Removing destinations from routes

In order to remove a destination from a route you need to acquire route's route-id and destination's route-destination-id You can do that by calling GetTeamRoutes() and GetRoute() respectively.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    boolean, err := service.RemoveRouteDestination("route-id", "route-destination-id")
    if err != nil {
        // handle error
        return
    }
}

Moving Destination to another route

In order to move a destination between routes you have to acquire their ids.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    query := routing.DestinationMoveRequest {
        ToRouteID: "route-id",
        RouteDestinationID: "route-destination-id",
        AfterDestinationID: "after-destination-id",
    }
    success, err := service.MoveDestinationToRoute(query)
    if err != nil {
        // handle error
        return
    }
    if !success {
        // handle failed attempt
        return
    }
}

Notes

Getting Address Notes

In order to get address notes you have to acquire route_id and address_id. You can do that by calling GetRoute()

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    query := &NoteQuery{
        RouteID:   "route_id",
        AddressID: "address_id",
    }
    notes, err := service.GetAddressNotes(query)
    if err != nil {
        //handle errors
        return
    }
    // do something with address notes
}

Adding a Note to an Address

In order to add a note to an address you have to acquire route_id and address_id. You can do that by calling GetRoute()

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    query := &NoteQuery{
        RouteID:      get.ID,
        AddressID:    get.Addresses[0].RouteDestinationID.String(),
        Latitude:     33.132675170898,
        Longitude:    -83.244743347168,
        ActivityType: DropOff,
    }
    addedNote, err = service.AddAddressNote(query, "Note Contents")
    if err != nil {
        //handle errors
        return
    }
}

Optimizations

  • An Optimization Problem is a collection of addresses that need to be visited.
  • The optimization problem takes into consideration all of the addresses that need to be visited and all the constraints associated with each address and depot.
  • It is preferable to create an optimization problem with as many orders in it as possible, so that the optimization engine is able to consider the entire problem set.
  • This is different from a Route, which is a sequence of addresses that need to be visited by a single vehicle and a single driver in a fixed time period. Solving an Optimization Problem results in a number of routes. Possibly in the future they may also be recurring.

Getting Optimizations

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    optimizations, err := service.GetOptimizations(&RouteQuery{Limit: 5})
    if err != nil {
        //handle errors
        return
    }
    //do something with optimizations
}

Getting a single optimization

In order to get a single optimization you need to obtain the ID. You can do it by calling GetOptimizations

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    optimization, err = service.GetOptimization(&OptimizationParameters{ProblemID: optimizations[0].ProblemID})
    if err != nil {
        //handler errors
        return
    }
    //do something with optimization
}

Deleting a single optimization

This currently is not implemented on the Route4Me side.

In order to delete a single optimization you need to obtain the ID. You can do it by calling GetOptimizations

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    err = service.DeleteOptimization({optimizationId})
    if err != nil {
        //handler errors
        return
    }
}

Updating optimization

Updating optimization allows you to add addresses to the process. You have to acquire problem_id in order to update it. Setting reoptimize to true will reoptimize the whole optimization.

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    addresses := []routing.Address{
        //address to be added
        routing.Address{
            AddressString: "717 5th Ave New York, NY 10021",
            Alias:         "Giorgio Armani",
            Latitude:      40.7669692,
            Longitude:     -73.9693864,
            Time:          0,
        },
    }

    updated, err := service.UpdateOptimization(&OptimizationParameters{ProblemID: "problem_id", Addresses: addresses, Reoptimize: true})
    if err != nil {
        //handle errors
        return
    }
    // do something with updated optimization
}

Running optimization

There are multiple configurations of running the route. Examples from C# SDK might be helpful to understand the whole process.

Let's take a look at AlgorithmType:

//AlgorithmType is a type of algorithm used for optimization
type AlgorithmType uint

const (
    //TSP stands for single depot, single driver route
    TSP AlgorithmType = iota + 1
    //VRP stands for single depot, multiple driver, no constraints, no time windows, no capacities
    VRP
    //CVRP_TW_SD stands for single depot, multiple driver, capacitated, time windows
    CVRP_TW_SD
    //CVRP_TW_MD stands for multiple depot, multiple driver, capacitated, time windows
    CVRP_TW_MD
    //TSP_TW stands for single depot, single driver, time windows
    TSP_TW
    //TSP_TW_CR stands for single depot, single driver, time windows, continuous optimization (minimal location shifting)
    TSP_TW_CR
    //BBCVRP stands for shifts addresses from one route to another over time on a recurring schedule
    BBCVRP
)

These options describe what type of optimization should be created. We need to specify RouteParameters

routeParams := &routing.RouteParameters{
    AlgorithmType:        routing.CVRP_TW_MD,
    Name:                 "Multiple Depot, Multiple Driver",
    RouteDate:            time.Now().Unix(),
    RouteTime:            60 * 60 * 7,
    RouteMaxDuration:     86400,
    VehicleCapacity:      1,
    VehicleMaxDistanceMI: 1000,
    Optimize:             routing.Distance,
    DistanceUnit:         routing.Miles,
    DeviceType:           routing.Web,
    TravelMode:           routing.Driving,
}

Members should be self-descriptive, they correspond to the route4me web UI.

Once route parameters are set up we need to wrap it with addresses and execute the optimization.

Specifying ProblemID in OptimizationParameters will create a route.

optParams := &routing.OptimizationParameters{
    Addresses:  addresses,
    Parameters: routeParams,
}
optimization, err := service.RunOptimization(optParams)
if err != nil {
    //handle errors
    return
}
//do something with optimization

Complete example:

import (
    "github.com/route4me/route4me-go-sdk"
    "github.com/route4me/route4me-go-sdk/routing"
)

func main() {
    client := route4me.NewClient("your-api-key")
    service := &routing.Service{Client: client}
    routeParams := &routing.RouteParameters{
        AlgorithmType:        routing.CVRP_TW_MD,
        Name:                 "Multiple Depot, Multiple Driver",
        RouteDate:            time.Now().Unix(),
        RouteTime:            60 * 60 * 7,
        RouteMaxDuration:     86400,
        VehicleCapacity:      1,
        VehicleMaxDistanceMI: 1000,
        Optimize:             routing.Distance,
        DistanceUnit:         routing.Miles,
        DeviceType:           routing.Web,
        TravelMode:           routing.Driving,
    }
    optParams := &routing.OptimizationParameters{
        Addresses:  addresses,
        Parameters: routeParams,
    }
    optimization, err := service.RunOptimization(optParams)
    if err != nil {
        //handle errors
        return
    }
    //do something with optimization
}

The returned object will not contain the optimized route, you have to keep calling GetOptimization to fetch the current status.

You can look at service's test file for more implementation details.