routing-service/encoding/polylines/decoding.go

120 lines
3.3 KiB
Go

package polylines
import (
"math"
"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 {
// default to 6 digits of precision
precision := 6
if len(precisionOptional) > 0 {
precision = precisionOptional[0]
}
factor := math.Pow10(precision)
// Coordinates have variable length when encoded, so just keep
// track of whether we've hit the end of the string. In each
// loop iteration, a single coordinate is decoded.
lat, lng := 0, 0
var coordinates orb.LineString
index := 0
for index < len(*encoded) {
// Consume varint bits for lat until we run out
var byte int = 0x20
shift, result := 0, 0
for byte >= 0x20 {
byte = int((*encoded)[index]) - 63
result |= (byte & 0x1f) << shift
shift += 5
index++
}
// check if we need to go negative or not
if (result & 1) > 0 {
lat += ^(result >> 1)
} else {
lat += result >> 1
}
// Consume varint bits for lng until we run out
byte = 0x20
shift, result = 0, 0
for byte >= 0x20 {
byte = int((*encoded)[index]) - 63
result |= (byte & 0x1f) << shift
shift += 5
index++
}
// check if we need to go negative or not
if (result & 1) > 0 {
lng += ^(result >> 1)
} else {
lng += result >> 1
}
// scale the int back to floating point and storeLineString it
coordinates = append(coordinates, orb.Point{float64(lng) / factor, float64(lat) / factor})
}
return coordinates
}