Tom Insam

Decoding Geohashes in pure Ruby

Wrote this for work, threw it away again in favour of using an actual gem that someone else will maintain,
but I thought I'd put it here anyway, because it might be useful. Also, the gem is written in C and therefore
hard to deploy sometimes.

#!/usr/bin/env ruby
# pure-ruby geohash decoding function

# default is the example from http://en.wikipedia.org/wiki/Geohash
geohash = ARGV[0] || "ezs42"

# convert geohash into a bit sequence
map = "0123456789bcdefghjkmnpqrstuvwxyz" # silly custom base32 mapping
bits = geohash.split("").map{|c|
    i = map.index(c) or raise("bad geohash (#{c} not permitted)")
    sprintf("%05s", i.to_s(2)).gsub(" ","0").split("")
}.flatten

# even bits are longitude, odd bits are latitude.
# probably a better way of doing this part, feels non-ruby-like..
lat_bits = []
lng_bits = []
bits.each_with_index{|b,i|
    if i % 2 == 1
        lat_bits << b
    else
        lng_bits << b
    end
}

# subdivide the world according to the bit sequences
def decode(bits, range)
    range = [ range.to_f * -1, range.to_f ]
    for b in bits
        if b == "1"
            range[0] = (range[0] + range[1])/2
        else
            range[1] = (range[0] + range[1])/2
        end
    end
    return range
end

lat_range = decode( lat_bits, 90 )
lng_range = decode( lng_bits, 180 )
puts "lat is range #{ lat_range.inspect }"
puts "lng is range #{ lng_range.inspect }"