-
Notifications
You must be signed in to change notification settings - Fork 553
/
neighbor.go
118 lines (105 loc) · 3.09 KB
/
neighbor.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package geohash
import "math"
const (
dr = math.Pi / 180.0
earthRadius = 6372797.560856
mercatorMax = 20037726.37 // pi * earthRadius
mercatorMin = -20037726.37
)
func degRad(ang float64) float64 {
return ang * dr
}
func radDeg(ang float64) float64 {
return ang / dr
}
func estimatePrecisionByRadius(radiusMeters float64, latitude float64) uint {
if radiusMeters == 0 {
return defaultBitSize - 1
}
var precision uint = 1
for radiusMeters < mercatorMax {
radiusMeters *= 2
precision++
}
/* Make sure range is included in most of the base cases. */
precision -= 2
if latitude > 66 || latitude < -66 {
precision--
if latitude > 80 || latitude < -80 {
precision--
}
}
if precision < 1 {
precision = 1
}
if precision > 32 {
precision = 32
}
return precision*2 - 1
}
// Distance computes the distance between two given coordinates in meter
func Distance(latitude1, longitude1, latitude2, longitude2 float64) float64 {
radLat1 := degRad(latitude1)
radLat2 := degRad(latitude2)
a := radLat1 - radLat2
b := degRad(longitude1) - degRad(longitude2)
return 2 * earthRadius * math.Asin(math.Sqrt(math.Pow(math.Sin(a/2), 2)+
math.Cos(radLat1)*math.Cos(radLat2)*math.Pow(math.Sin(b/2), 2)))
}
// toRange covert geohash prefix to uint64 range
func toRange(scope []byte, precision uint) [2]uint64 {
lower := ToInt(scope)
radius := uint64(1 << (64 - precision))
upper := lower + radius
return [2]uint64{lower, upper}
}
func ensureValidLat(lat float64) float64 {
if lat > 90 {
return 90
}
if lat < -90 {
return -90
}
return lat
}
func ensureValidLng(lng float64) float64 {
if lng > 180 {
return -360 + lng
}
if lng < -180 {
return 360 + lng
}
return lng
}
// GetNeighbours returns geohash code of blocks within radiusMeters to the given coordinate
func GetNeighbours(latitude, longitude, radiusMeters float64) [][2]uint64 {
precision := estimatePrecisionByRadius(radiusMeters, latitude)
center, box := encode0(latitude, longitude, precision)
height := box[0][1] - box[0][0]
width := box[1][1] - box[1][0]
centerLng := (box[0][1] + box[0][0]) / 2
centerLat := (box[1][1] + box[1][0]) / 2
maxLat := ensureValidLat(centerLat + height)
minLat := ensureValidLat(centerLat - height)
maxLng := ensureValidLng(centerLng + width)
minLng := ensureValidLng(centerLng - width)
var result [10][2]uint64
leftUpper, _ := encode0(maxLat, minLng, precision)
result[1] = toRange(leftUpper, precision)
upper, _ := encode0(maxLat, centerLng, precision)
result[2] = toRange(upper, precision)
rightUpper, _ := encode0(maxLat, maxLng, precision)
result[3] = toRange(rightUpper, precision)
left, _ := encode0(centerLat, minLng, precision)
result[4] = toRange(left, precision)
result[5] = toRange(center, precision)
right, _ := encode0(centerLat, maxLng, precision)
result[6] = toRange(right, precision)
leftDown, _ := encode0(minLat, minLng, precision)
result[7] = toRange(leftDown, precision)
down, _ := encode0(minLat, centerLng, precision)
result[8] = toRange(down, precision)
rightDown, _ := encode0(minLat, maxLng, precision)
result[9] = toRange(rightDown, precision)
return result[1:]
}