Files
redis-go/lib/geohash/geohash.go
2021-05-02 14:54:42 +08:00

108 lines
2.2 KiB
Go

package geohash
import (
"bytes"
"encoding/base32"
"encoding/binary"
)
var bits = []uint8{128, 64, 32, 16, 8, 4, 2, 1}
var enc = base32.NewEncoding("0123456789bcdefghjkmnpqrstuvwxyz").WithPadding(base32.NoPadding)
const defaultBitSize = 64 // 32 bits for latitude, another 32 bits for longitude
// return: geohash, box
func encode0(latitude, longitude float64, bitSize uint) ([]byte, [2][2]float64) {
box := [2][2]float64{
{-180, 180}, // lng
{-90, 90}, // lat
}
pos := [2]float64{longitude, latitude}
hash := &bytes.Buffer{}
bit := 0
var precision uint = 0
code := uint8(0)
for precision < bitSize {
for direction, val := range pos {
mid := (box[direction][0] + box[direction][1]) / 2
if val < mid {
box[direction][1] = mid
} else {
box[direction][0] = mid
code |= bits[bit]
}
bit++
if bit == 8 {
hash.WriteByte(code)
bit = 0
code = 0
}
precision++
if precision == bitSize {
break
}
}
}
// precision%8 > 0
if code > 0 {
hash.WriteByte(code)
}
return hash.Bytes(), box
}
func Encode(latitude, longitude float64) uint64 {
buf, _ := encode0(latitude, longitude, defaultBitSize)
return binary.BigEndian.Uint64(buf)
}
func decode0(hash []byte) [][]float64 {
box := [][]float64{
{-180, 180},
{-90, 90},
}
direction := 0
for i := 0; i < len(hash); i++ {
code := hash[i]
for j := 0; j < len(bits); j++ {
mid := (box[direction][0] + box[direction][1]) / 2
mask := bits[j]
if mask&code > 0 {
box[direction][0] = mid
} else {
box[direction][1] = mid
}
direction = (direction + 1) % 2
}
}
return box
}
func Decode(code uint64) (float64, float64) {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, code)
box := decode0(buf)
lng := float64(box[0][0]+box[0][1]) / 2
lat := float64(box[1][0]+box[1][1]) / 2
return lat, lng
}
func ToString(buf []byte) string {
return enc.EncodeToString(buf)
}
func ToInt(buf []byte) uint64 {
// padding
if len(buf) < 8 {
buf2 := make([]byte, 8)
copy(buf2, buf)
return binary.BigEndian.Uint64(buf2)
}
return binary.BigEndian.Uint64(buf)
}
func FromInt(code uint64) []byte {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, code)
return buf
}