-
Notifications
You must be signed in to change notification settings - Fork 554
/
geohash.go
104 lines (93 loc) · 2.37 KB
/
geohash.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package geohash
import (
"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}
hashLen := bitSize >> 3
if bitSize&7 > 0 {
hashLen++
}
hash := make([]byte, hashLen)
var precision uint = 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
hash[precision>>3] |= 1 << (7 - precision&7)
}
precision++
if precision == bitSize {
break
}
}
}
return hash, box
}
// Encode converts latitude and longitude to uint64 geohash code
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
}
// Decode converts uint64 geohash code to latitude and longitude
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
}
// ToString converts bytes geohash code to base32 string
func ToString(buf []byte) string {
return enc.EncodeToString(buf)
}
// ToInt converts bytes geohash code to uint64 code
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)
}
// FromInt converts uint64 geohash code to bytes
func FromInt(code uint64) []byte {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, code)
return buf
}