-
Notifications
You must be signed in to change notification settings - Fork 0
/
geohash.go
63 lines (48 loc) · 1.41 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
package geohash
import "strings"
const (
base32Codes = "0123456789bcdefghjkmnpqrstuvwxyz"
// Each base32 character represents exactly 5 bits of a sequence.
bitsPerChar = 5
// Base32 mask 0x1F, equivalent to 00011111 in binary.
bitsMask = (1 << bitsPerChar) - 1
coordBits = 30
totalBits = 60
)
func GenerateGeohash(x, y float64) string {
var xBits, yBits uint32
xs := []float64{-180.0, 0.0, 180.0}
ys := []float64{-90.0, 0.0, 90.0}
for i := coordBits - 1; i >= 0; i-- {
processCoordinate(x >= xs[1], &xBits, xs, i)
processCoordinate(y >= ys[1], &yBits, ys, i)
}
resultBits := interleaveBits(xBits, yBits)
return encodeGeohash(resultBits)
}
func processCoordinate(rightOfMedian bool, bits *uint32, coords []float64, bitPosition int) {
if rightOfMedian {
*bits |= 1 << bitPosition
coords[0] = coords[1]
} else {
coords[2] = coords[1]
}
coords[1] = (coords[0] + coords[2]) / 2.0
}
func interleaveBits(xBits, yBits uint32) uint64 {
var resultBits uint64
for i := coordBits - 1; i >= 0; i-- {
resultBits |= uint64((xBits>>i)&1) << (2*i + 1)
resultBits |= uint64((yBits>>i)&1) << (2 * i)
}
return resultBits
}
func encodeGeohash(resultBits uint64) string {
var geohash strings.Builder
geohash.Grow(totalBits / bitsPerChar)
for i := bitsPerChar; i <= totalBits; i += bitsPerChar {
pos := (resultBits >> (totalBits - i)) & bitsMask
geohash.WriteByte(base32Codes[pos])
}
return geohash.String()
}