geohash.go 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package utils
  2. import (
  3. "bytes"
  4. )
  5. const (
  6. BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"
  7. MAX_LATITUDE float64 = 90
  8. MIN_LATITUDE float64 = -90
  9. MAX_LONGITUDE float64 = 180
  10. MIN_LONGITUDE float64 = -180
  11. )
  12. var (
  13. bits = []int{16, 8, 4, 2, 1}
  14. base32 = []byte(BASE32)
  15. )
  16. // Box geohash的精度与其长度成正比
  17. // 每个点的geohash值实际上代表了一个区域,这个区域的大小与geohash的精度成反比
  18. // 坐标点的格式为(纬度,经度)
  19. // 将这个区域用一个矩形表示
  20. type Box struct {
  21. MinLat, MaxLat float64 // 纬度
  22. MinLng, MaxLng float64 // 经度
  23. }
  24. func (this *Box) Width() float64 {
  25. return this.MaxLng - this.MinLng
  26. }
  27. func (this *Box) Height() float64 {
  28. return this.MaxLat - this.MinLat
  29. }
  30. // GeoHashEncode 输入值:纬度,经度,精度(geohash的长度)
  31. // 返回geohash, 以及该点所在的区域
  32. func GeoHashEncode(latitude, longitude float64, precision int) (string, *Box) {
  33. var geohash bytes.Buffer
  34. var minLat, maxLat float64 = MIN_LATITUDE, MAX_LATITUDE
  35. var minLng, maxLng float64 = MIN_LONGITUDE, MAX_LONGITUDE
  36. var mid float64 = 0
  37. bit, ch, length, isEven := 0, 0, 0, true
  38. for length < precision {
  39. if isEven {
  40. if mid = (minLng + maxLng) / 2; mid < longitude {
  41. ch |= bits[bit]
  42. minLng = mid
  43. } else {
  44. maxLng = mid
  45. }
  46. } else {
  47. if mid = (minLat + maxLat) / 2; mid < latitude {
  48. ch |= bits[bit]
  49. minLat = mid
  50. } else {
  51. maxLat = mid
  52. }
  53. }
  54. isEven = !isEven
  55. if bit < 4 {
  56. bit++
  57. } else {
  58. geohash.WriteByte(base32[ch])
  59. length, bit, ch = length+1, 0, 0
  60. }
  61. }
  62. b := &Box{
  63. MinLat: minLat,
  64. MaxLat: maxLat,
  65. MinLng: minLng,
  66. MaxLng: maxLng,
  67. }
  68. return geohash.String(), b
  69. }