fix decoding encoreding

This commit is contained in:
Arnaud Delcasse 2023-03-30 00:45:55 +02:00
parent 1442647132
commit 30339c9bdd
4 changed files with 227 additions and 5 deletions

View File

@ -0,0 +1,95 @@
package polylines
import (
"math"
)
type chunks []int32
// Parse splices an integer into chunks
func (c *chunks) Parse(element int32) {
c.slice(element, 5)
}
// ParseLine converts and splices string into integer chunks
func (c *chunks) ParseLine(line string) {
chunkSlice := []int32{}
for index, letter := range line {
elementInt := int32(letter) - 63
if index != len(line)-1 {
elementInt = elementInt & 31
}
chunkSlice = append(chunkSlice, elementInt)
}
*c = chunkSlice
}
// String returns the chunks as polyline in base64
func (c *chunks) String() string {
c.or()
s := ""
for i := range *c {
s += string((*c)[i])
}
return s
}
// Coordinate converts integer chunks into a single coordinate
func (c *chunks) Coordinate(precision uint32) float64 {
resultInt := int32(0)
for i, element := range *c {
resultInt += element << uint32(i*5)
}
if resultInt&1 == 1 {
resultInt = ^resultInt
}
resultInt = resultInt >> 1
return float64(resultInt) / math.Pow10(int(precision))
}
// slice splits elements into group of "length" bits
func (c *chunks) slice(element int32, length int) {
if element == 0 {
*c = []int32{0}
return
}
chunkSlice := []int32{}
bitMask := int32(31)
for i := 0; int32(math.Pow(2, float64(i))) <= element; i += 5 {
group := (element >> uint(i)) & bitMask
chunkSlice = append(chunkSlice, group)
}
*c = chunkSlice
}
// or sets the 6th bit to 1 for every chunk except the last one
// as indicator bit for coordinate boundaries.
// It also adds 63 (decimal) to every group to ensure it is in
// ASCII range
func (c *chunks) or() {
for i := range *c {
if i < len(*c)-1 {
(*c)[i] = (*c)[i] | 0x20
}
(*c)[i] += 63
}
}

View File

@ -6,6 +6,62 @@ import (
"github.com/paulmach/orb" "github.com/paulmach/orb"
) )
// // Decode decodes coordinates to the "Encoded Polyline Algorithm Format"
// // More info: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
// // polyline: polyline string in "Encoded Polyline Algorithm Format"
// // precision: usually 5 or 6; Google's original algorithm uses 5 digits of decimal precision,
// // which is accurate to about a meter. A precision of 6 gives you an accuracy of about 10cm
// // more info: https://mapzen.com/blog/polyline-precision/
// func Decode(polyline string, precision uint32) orb.LineString {
// group := ""
// coordinates := []float64{}
// for _, letter := range polyline {
// group += string(letter)
// if (int32(letter)-63)&0x20 == 0 {
// coordinates = append(coordinates, decodeElement(group, precision))
// group = ""
// }
// }
// points := orb.LineString{}
// for i := 1; i < len(coordinates); i += 2 {
// points = append(points, orb.Point{round(coordinates[i], precision), round(coordinates[i-1], precision)})
// }
// for i := range points {
// if i > 0 {
// points[i][1] = round(points[i-1].Lat()+points[i].Lat(), precision)
// points[i][0] = round(points[i-1].Lon()+points[i].Lon(), precision)
// }
// }
// return points
// }
// // Decode5 is a short call for Decode with precision set to 5
// // Accuracy is about one meter
// func Decode5(polyline string) orb.LineString {
// return Decode(polyline, 5)
// }
// // Decode6 is a short call for Decode with precision set to 6
// // Accuracy is about ten centimeters
// func Decode6(polyline string) orb.LineString {
// return Decode(polyline, 6)
// }
// // decodeElement decodes an coordinate element (i.e. latitude or longitude)
// // from the "Encoded Polyline Algorithm Format"
// func decodeElement(group string, precision uint32) float64 {
// var c chunks
// c.ParseLine(group)
// return c.Coordinate(precision)
// }
func Decode(encoded *string, precisionOptional ...int) orb.LineString { func Decode(encoded *string, precisionOptional ...int) orb.LineString {
// default to 6 digits of precision // default to 6 digits of precision
precision := 6 precision := 6

View File

@ -5,12 +5,79 @@ import (
"github.com/twpayne/go-polyline" "github.com/twpayne/go-polyline"
) )
func Encode(line orb.LineString) string { // // Encode encodes coordinates to the "Encoded Polyline Algorithm Format"
// // More info: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
// // points: points of the polyline
// // precision: usually 5 or 6; Google's original algorithm uses 5 digits of decimal precision,
// // which is accurate to about a meter. A precision of 6 gives you an accuracy of about 10cm
// // more info: https://mapzen.com/blog/polyline-precision/
// func Encode(linestring orb.LineString, precision uint32) string {
// encoded := ""
// latitude := 0.0
// longitude := 0.0
// for _, point := range linestring {
// polyLatitude := encodeElement(point.Lat()-latitude, precision)
// encoded += polyLatitude
// polyLongitude := encodeElement(point.Lon()-longitude, precision)
// encoded += polyLongitude
// // to conserve space, points only include the offset from the previous point
// latitude = point.Lat()
// longitude = point.Lon()
// }
// return encoded
// }
// // Encode5 is a short call for Encode with precision set to 5
// // Accuracy is about one meter
// func Encode5(linestring orb.LineString) string {
// return Encode(linestring, 5)
// }
// // Encode6 is a short call for Encode with precision set to 6
// // Accuracy is about ten centimeters
// func Encode6(linestring orb.LineString) string {
// return Encode(linestring, 6)
// }
// // encodeElement encodes an coordinate element (i.e. latitude or longitude)
// // to the "Encoded Polyline Algorithm Format"
// func encodeElement(element float64, precision uint32) string {
// elementInt := int32(math.Round(element * math.Pow10(int(precision))))
// elementInt = elementInt << 1
// if element < 0 {
// elementInt = ^elementInt
// }
// var c chunks
// c.Parse(elementInt)
// return c.String()
// }
// // round n to precision digits
// func round(n float64, precision uint32) float64 {
// factor := math.Pow10(int(precision))
// return math.Round(n*factor) / factor
// }
func Encode(line orb.LineString, precision uint32) string {
preparedLine := [][]float64{} preparedLine := [][]float64{}
for _, point := range line { for _, point := range line {
preparedLine = append(preparedLine, []float64{point.Lon(), point.Lat()}) preparedLine = append(preparedLine, []float64{point.Lat(), point.Lon()})
} }
return string(polyline.EncodeCoords(preparedLine)) return string(polyline.EncodeCoords(preparedLine))

View File

@ -10,6 +10,7 @@ import (
"git.coopgo.io/coopgo-platform/routing-service/encoding/polylines" "git.coopgo.io/coopgo-platform/routing-service/encoding/polylines"
"git.coopgo.io/coopgo-platform/routing-service/proto/valhalla" "git.coopgo.io/coopgo-platform/routing-service/proto/valhalla"
"github.com/paulmach/orb" "github.com/paulmach/orb"
"github.com/rs/zerolog/log"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
@ -47,11 +48,14 @@ func (v *ValhallaRouting) Route(locations []orb.Point) (route *Route, err error)
resp, err := v.protocolBufferRequest(request, "route") resp, err := v.protocolBufferRequest(request, "route")
if err != nil { if err != nil {
log.Error().Err(err).Msg("pb request to valhalla error")
return nil, err return nil, err
} }
log.Debug().Any("resp", resp).Msg("valhalla response")
if resp.Directions == nil || resp.Directions.Routes == nil || len(resp.Directions.Routes) < 1 { if resp.Directions == nil || resp.Directions.Routes == nil || len(resp.Directions.Routes) < 1 {
return nil, errors.New("no routes returnes by valhalla") return nil, errors.New("no routes returned by valhalla")
} }
decodedLinestring := orb.LineString{} decodedLinestring := orb.LineString{}
@ -66,7 +70,7 @@ func (v *ValhallaRouting) Route(locations []orb.Point) (route *Route, err error)
routeLeg := RouteLeg{ routeLeg := RouteLeg{
Distance: float64(leg.Summary.Length), Distance: float64(leg.Summary.Length),
Duration: time.Duration(leg.Summary.Time) * time.Second, Duration: time.Duration(leg.Summary.Time) * time.Second,
Polyline: polylines.Encode(decodedShape), Polyline: polylines.Encode(decodedShape, 5),
} }
legs = append(legs, routeLeg) legs = append(legs, routeLeg)
@ -74,7 +78,7 @@ func (v *ValhallaRouting) Route(locations []orb.Point) (route *Route, err error)
return &Route{ return &Route{
Summary: RouteSummary{ Summary: RouteSummary{
Polyline: polylines.Encode(decodedLinestring), Polyline: polylines.Encode(decodedLinestring, 5),
}, },
Legs: legs, Legs: legs,
}, nil }, nil