routing-service/valhalla.go

115 lines
2.5 KiB
Go

package routing
import (
"bytes"
"errors"
"io/ioutil"
"net/http"
"time"
"git.coopgo.io/coopgo-platform/routing-service/encoding/polylines"
"git.coopgo.io/coopgo-platform/routing-service/proto/valhalla"
"github.com/paulmach/orb"
"google.golang.org/protobuf/proto"
)
type ValhallaRouting struct {
BaseURL string
}
func NewValhallaRouting(baseURL string) (*ValhallaRouting, error) {
return &ValhallaRouting{
BaseURL: baseURL,
}, nil
}
func (v *ValhallaRouting) Route(locations []orb.Point) (route *Route, err error) {
valhalla_locations := []*valhalla.Location{}
for _, loc := range locations {
valhalla_locations = append(valhalla_locations, &valhalla.Location{
Ll: &valhalla.LatLng{
HasLat: &valhalla.LatLng_Lat{Lat: loc.Lat()},
HasLng: &valhalla.LatLng_Lng{Lng: loc.Lon()},
},
})
}
request := &valhalla.Api{
Options: &valhalla.Options{
Action: valhalla.Options_route,
Locations: valhalla_locations,
CostingType: valhalla.Costing_auto_,
Format: valhalla.Options_pbf,
},
}
resp, err := v.protocolBufferRequest(request, "route")
if err != nil {
return nil, err
}
if len(resp.Directions.Routes) < 1 {
return nil, errors.New("no routes returnes by valhalla")
}
decodedLinestring := orb.LineString{}
legs := []RouteLeg{}
for _, leg := range resp.Directions.Routes[0].Legs {
shape := leg.Shape
decodedShape := polylines.Decode(&shape, 6)
decodedLinestring = append(decodedLinestring, decodedShape...)
routeLeg := RouteLeg{
Distance: float64(leg.Summary.Length),
Duration: time.Duration(leg.Summary.Time) * time.Second,
Polyline: polylines.Encode(decodedShape),
}
legs = append(legs, routeLeg)
}
return &Route{
Summary: RouteSummary{
Polyline: polylines.Encode(decodedLinestring),
},
Legs: legs,
}, nil
}
func (v *ValhallaRouting) protocolBufferRequest(api *valhalla.Api, path string) (*valhalla.Api, error) {
data, err := proto.Marshal(api)
if err != nil {
return nil, err
}
req, err := http.NewRequest(http.MethodGet, v.BaseURL+path, bytes.NewBuffer(data))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-protobuf")
client := http.Client{}
resp, err := client.Do(req)
//resp, err := http.Post(v.BaseURL+path, "application/x-protobuf", bytes.NewBuffer(data))
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
response := valhalla.Api{}
err = proto.Unmarshal(body, &response)
if err != nil {
return nil, err
}
return &response, nil
}