github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/geo/polyline.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package geo 12 13 import ( 14 "math" 15 "strings" 16 ) 17 18 // decodePolylinePoints decodes encoded Polyline according to the polyline algorithm: https://developers.google.com/maps/documentation/utilities/polylinealgorithm 19 func decodePolylinePoints(encoded string, precision int) []float64 { 20 idx := 0 21 latitude := float64(0) 22 longitude := float64(0) 23 bytes := []byte(encoded) 24 results := []float64{} 25 for idx < len(bytes) { 26 var deltaLat float64 27 idx, deltaLat = decodePointValue(idx, bytes) 28 latitude += deltaLat 29 30 var deltaLng float64 31 idx, deltaLng = decodePointValue(idx, bytes) 32 longitude += deltaLng 33 results = append(results, 34 longitude/math.Pow10(precision), 35 latitude/math.Pow10(precision)) 36 } 37 return results 38 } 39 40 func decodePointValue(idx int, bytes []byte) (int, float64) { 41 res := int32(0) 42 shift := 0 43 for byte := byte(0x20); byte >= 0x20; { 44 if idx > len(bytes)-1 { 45 return idx, 0 46 } 47 byte = bytes[idx] - 63 48 idx++ 49 res |= int32(byte&0x1F) << shift 50 shift += 5 51 } 52 var pointValue float64 53 if (res & 1) == 1 { 54 pointValue = float64(^(res >> 1)) 55 } else { 56 pointValue = float64(res >> 1) 57 } 58 return idx, pointValue 59 } 60 61 // encodePolylinePoints encodes provided points using the algorithm: https://developers.google.com/maps/documentation/utilities/polylinealgorithm 62 // Assumes there are no malformed points - length of the input slice should be even. 63 func encodePolylinePoints(points []float64, precision int) string { 64 lastLat := 0 65 lastLng := 0 66 var res strings.Builder 67 for i := 1; i < len(points); i += 2 { 68 lat := int(math.Round(points[i-1] * math.Pow10(precision))) 69 lng := int(math.Round(points[i] * math.Pow10(precision))) 70 res = encodePointValue(lng-lastLng, res) 71 res = encodePointValue(lat-lastLat, res) 72 lastLat = lat 73 lastLng = lng 74 } 75 76 return res.String() 77 } 78 79 func encodePointValue(diff int, b strings.Builder) strings.Builder { 80 var shifted int 81 shifted = diff << 1 82 if diff < 0 { 83 shifted = ^shifted 84 } 85 rem := shifted 86 for rem >= 0x20 { 87 b.WriteRune(rune(0x20 | (rem & 0x1f) + 63)) 88 89 rem = rem >> 5 90 } 91 92 b.WriteRune(rune(rem + 63)) 93 return b 94 }