geohash.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // Copyright (c) 2019 Couchbase, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // This implementation is inspired from the geohash-js
  15. // ref: https://github.com/davetroy/geohash-js
  16. package geo
  17. // encoding encapsulates an encoding defined by a given base32 alphabet.
  18. type encoding struct {
  19. enc string
  20. dec [256]byte
  21. }
  22. // newEncoding constructs a new encoding defined by the given alphabet,
  23. // which must be a 32-byte string.
  24. func newEncoding(encoder string) *encoding {
  25. e := new(encoding)
  26. e.enc = encoder
  27. for i := 0; i < len(e.dec); i++ {
  28. e.dec[i] = 0xff
  29. }
  30. for i := 0; i < len(encoder); i++ {
  31. e.dec[encoder[i]] = byte(i)
  32. }
  33. return e
  34. }
  35. // base32encoding with the Geohash alphabet.
  36. var base32encoding = newEncoding("0123456789bcdefghjkmnpqrstuvwxyz")
  37. var masks = []uint64{16, 8, 4, 2, 1}
  38. // DecodeGeoHash decodes the string geohash faster with
  39. // higher precision. This api is in experimental phase.
  40. func DecodeGeoHash(geoHash string) (float64, float64) {
  41. even := true
  42. lat := []float64{-90.0, 90.0}
  43. lon := []float64{-180.0, 180.0}
  44. for i := 0; i < len(geoHash); i++ {
  45. cd := uint64(base32encoding.dec[geoHash[i]])
  46. for j := 0; j < 5; j++ {
  47. if even {
  48. if cd&masks[j] > 0 {
  49. lon[0] = (lon[0] + lon[1]) / 2
  50. } else {
  51. lon[1] = (lon[0] + lon[1]) / 2
  52. }
  53. } else {
  54. if cd&masks[j] > 0 {
  55. lat[0] = (lat[0] + lat[1]) / 2
  56. } else {
  57. lat[1] = (lat[0] + lat[1]) / 2
  58. }
  59. }
  60. even = !even
  61. }
  62. }
  63. return (lat[0] + lat[1]) / 2, (lon[0] + lon[1]) / 2
  64. }
  65. func EncodeGeoHash(lat, lon float64) string {
  66. even := true
  67. lats := []float64{-90.0, 90.0}
  68. lons := []float64{-180.0, 180.0}
  69. precision := 12
  70. var ch, bit uint64
  71. var geoHash string
  72. for len(geoHash) < precision {
  73. if even {
  74. mid := (lons[0] + lons[1]) / 2
  75. if lon > mid {
  76. ch |= masks[bit]
  77. lons[0] = mid
  78. } else {
  79. lons[1] = mid
  80. }
  81. } else {
  82. mid := (lats[0] + lats[1]) / 2
  83. if lat > mid {
  84. ch |= masks[bit]
  85. lats[0] = mid
  86. } else {
  87. lats[1] = mid
  88. }
  89. }
  90. even = !even
  91. if bit < 4 {
  92. bit++
  93. } else {
  94. geoHash += string(base32encoding.enc[ch])
  95. ch = 0
  96. bit = 0
  97. }
  98. }
  99. return geoHash
  100. }