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  }