fix decoding encoreding
This commit is contained in:
parent
1442647132
commit
30339c9bdd
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
10
valhalla.go
10
valhalla.go
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue