mirror of
https://github.com/HDT3213/godis.git
synced 2025-10-05 08:46:56 +08:00
137 lines
3.4 KiB
Go
137 lines
3.4 KiB
Go
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 getBoundingBox(latitude float64, longitude float64, radiusMeters float64) (
|
|
minLat, maxLat, minLng, maxLng float64) {
|
|
minLng = longitude - radDeg(radiusMeters/EarthRadius/math.Cos(degRad(latitude)))
|
|
if minLng < -180 {
|
|
minLng = -180
|
|
}
|
|
maxLng = longitude + radDeg(radiusMeters/EarthRadius/math.Cos(degRad(latitude)))
|
|
if maxLng > 180 {
|
|
maxLng = 180
|
|
}
|
|
minLat = latitude - radDeg(radiusMeters/EarthRadius)
|
|
if minLat < -90 {
|
|
minLat = -90
|
|
}
|
|
maxLat = latitude + radDeg(radiusMeters/EarthRadius)
|
|
if maxLat > 90 {
|
|
maxLat = 90
|
|
}
|
|
return
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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)))
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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:]
|
|
}
|