mirror of
https://github.com/HDT3213/godis.git
synced 2025-10-05 08:46:56 +08:00
108 lines
2.2 KiB
Go
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
|
|
}
|