2023-03-27 18:57:28 +00:00
|
|
|
package routing
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
2023-03-29 10:51:09 +00:00
|
|
|
"time"
|
2023-03-27 18:57:28 +00:00
|
|
|
|
|
|
|
"git.coopgo.io/coopgo-platform/routing-service/encoding/polylines"
|
|
|
|
"git.coopgo.io/coopgo-platform/routing-service/proto/valhalla"
|
|
|
|
"github.com/paulmach/orb"
|
2023-03-29 22:45:55 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2023-03-27 18:57:28 +00:00
|
|
|
"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 {
|
2023-03-29 22:45:55 +00:00
|
|
|
log.Error().Err(err).Msg("pb request to valhalla error")
|
2023-03-27 18:57:28 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-03-29 16:31:29 +00:00
|
|
|
if resp.Directions == nil || resp.Directions.Routes == nil || len(resp.Directions.Routes) < 1 {
|
2023-03-29 22:45:55 +00:00
|
|
|
return nil, errors.New("no routes returned by valhalla")
|
2023-03-27 18:57:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
decodedLinestring := orb.LineString{}
|
|
|
|
|
2023-03-29 10:51:09 +00:00
|
|
|
legs := []RouteLeg{}
|
|
|
|
|
2023-03-27 18:57:28 +00:00
|
|
|
for _, leg := range resp.Directions.Routes[0].Legs {
|
|
|
|
shape := leg.Shape
|
|
|
|
decodedShape := polylines.Decode(&shape, 6)
|
|
|
|
decodedLinestring = append(decodedLinestring, decodedShape...)
|
2023-03-29 10:51:09 +00:00
|
|
|
|
|
|
|
routeLeg := RouteLeg{
|
|
|
|
Distance: float64(leg.Summary.Length),
|
|
|
|
Duration: time.Duration(leg.Summary.Time) * time.Second,
|
2023-03-29 22:45:55 +00:00
|
|
|
Polyline: polylines.Encode(decodedShape, 5),
|
2023-03-29 10:51:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
legs = append(legs, routeLeg)
|
2023-03-27 18:57:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &Route{
|
|
|
|
Summary: RouteSummary{
|
2023-03-29 22:45:55 +00:00
|
|
|
Polyline: polylines.Encode(decodedLinestring, 5),
|
2023-03-27 18:57:28 +00:00
|
|
|
},
|
2023-03-29 10:51:09 +00:00
|
|
|
Legs: legs,
|
2023-03-27 18:57:28 +00:00
|
|
|
}, 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
|
|
|
|
}
|