Module: GeoHash

Defined in:
lib/geohash.rb

Constant Summary collapse

VERSION =
'1.0.0'
BASE32 =
'0123456789bcdefghjkmnpqrstuvwxyz'
NEIGHBORS =
{
  right: {
    even: 'bc01fg45238967deuvhjyznpkmstqrwx',
    odd:  'p0r21436x8zb9dcf5h7kjnmqesgutwvy',
  },
  left: {
    even: '238967debc01fg45kmstqrwxuvhjyznp',
    odd:  '14365h7k9dcfesgujnmqp0r2twvyx8zb',
  },
  top: {
    even: 'p0r21436x8zb9dcf5h7kjnmqesgutwvy',
    odd:  'bc01fg45238967deuvhjyznpkmstqrwx',
  },
  bottom: {
    even: '14365h7k9dcfesgujnmqp0r2twvyx8zb',
    odd:  '238967debc01fg45kmstqrwxuvhjyznp',
  }
}
BORDERS =
{
  right:  { even: 'bcfguvyz', odd: 'prxz' },
  left:   { even: '0145hjnp', odd: '028b' },
  top:    { even: 'prxz',     odd: 'bcfguvyz' },
  bottom: { even: '028b',     odd: '0145hjnp' },
}
@@neighbors_cache =
{}
@@adjacent_cache =
{
  right: {}, left: {}, top: {}, bottom: {}
}

Class Method Summary collapse

Class Method Details

.adjacent(geohash, dir) ⇒ String

Calculate adjacent geohashes

Returns:

  • (String)

    adjacent geohash



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/geohash.rb', line 136

def adjacent(geohash, dir)
  return @@adjacent_cache[dir][geohash] if @@adjacent_cache[dir][geohash]

  head, last = geohash[0..-2], geohash[-1]
  type = [:even, :odd][geohash.length % 2]

  head = adjacent(head, dir) if BORDERS[dir][type].include?(last)

  # NOTICE:
  # do not use append `<<` instead of `+`
  # head possibly be cached so that mustn't be mutated,
  # just create new String instance with it
  head = head + BASE32[NEIGHBORS[dir][type].index(last)]

  @@adjacent_cache[dir][geohash] = head
end

.decode(geohash) ⇒ [[Float]]

Decode geohash into bounding box of latitudes and longitudes

Returns:

  • ([[Float]])

    decoded bounding box [

    [north_latitude, west_longitude],
    [south_latitude, east_longitude]
    

    ]



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/geohash.rb', line 53

def decode(geohash)
  bounds = [[-90.0, +90.0], [-180.0, +180.0]]

  geohash.downcase.each_char.with_index do |c, i|
    d = BASE32.index c

    5.times do |j|
      bit = (d & (1 << (4 - j))) >> (4 - j)
      k = (~i & 1) ^ (j & 1)
      bounds[k][bit ^ 1] = (bounds[k][0] + bounds[k][1]) / 2
    end
  end

  bounds.transpose
end

.encode(latitude, longitude, precision = 12) ⇒ String

Encode latitude and longitude into geohash

Returns:

  • (String)

    geohash string



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/geohash.rb', line 78

def encode(latitude, longitude, precision = 12)
  mids = [latitude, longitude]
  bounds = [[-90.0, +90.0], [-180.0, +180.0]]

  geohash = []

  precision.times do |i|
    d = 0

    5.times do |j|
      k = (~i & 1) ^ (j & 1)
      mid = (bounds[k][0] + bounds[k][1]) / 2
      bit = mids[k] > mid ? 1 : 0
      bounds[k][bit ^ 1] = mid
      d |= bit << (4 - j)
    end

    geohash << BASE32[d]
  end

  geohash.join
end

.neighbors(geohash) ⇒ [String]

Calculate neighbors geohash

Returns:

  • ([String])

    neighbors array



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/geohash.rb', line 108

def neighbors(geohash)
  return @@neighbors_cache[geohash] if @@neighbors_cache[geohash]

  # walk path:
  #
  # 8   1 - 2
  # |   |   |
  # 7   B   3
  # |       |
  # 6 - 5 - 4
  #
  @@neighbors_cache[geohash] = walk geohash, [
    :top,
    :right,
    :bottom, :bottom,
    :left, :left,
    :top, :top
  ]
end

.walk(base, path) ⇒ [String]

Walk down

Returns:

  • ([String])

    result geohashes of the walk



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/geohash.rb', line 161

def walk(base, path)
  hashes = []

  h = base

  path.each do |dir|
    hashes << (h = adjacent(h, dir))
  end

  hashes
end