Implementations/Libraries/Ruby
From Geohashing
Ruby Geohasher v1.2
This implementation IS FULLY 30W-compliant. |
ScottKuma did this as a first excursion into Ruby. Yes, it's long. Yes, it's kinda clunky. However, IT WORKS!
v1.2 adds the ability to use unix-style commandline options in any order.
Any changes, suggestions, etc. are happily accepted!
#!/usr/bin/ruby # == Synopsis # geohasher: prints geohash coordinates for a given date & graticule # # == Usage # # geohasher [OPTION] ... # -d, --date YYYY-MM-DD: # the date in YYYY-MM-DD or YYYY/MM/DD format), # # -t, --lat x: # the latitude "base coordinate" # # -g, --long x: # the longitude "base coordinate" # # -m, --dow x require 'net/http' require 'date' require 'digest' require 'getoptlong' require 'rdoc/usage' def hexFracToDecFrac(hexFracPart) #I wish I had a neat little algorithm to do this in one line - but this works! #NOTE: do not feed the preceding "0." to this function....only the fractional part (the part after the decimal point) fracLen = hexFracPart.length fracPortion = hexFracPart[0..(fracLen-1)] fracLen = fracPortion.length myLen = (fracLen - 1) sum = 0 for i in (0 .. myLen) numSixteenths = fracPortion[i..i].to_i(16) conversionFactor = (16.**(i+1)).to_f conversionFactor = 1./conversionFactor sum = sum + ((numSixteenths) * conversionFactor) end return sum.to_s[2..8] end # Parse the command line options opts = GetoptLong.new( ['--help','-h',GetoptLong::NO_ARGUMENT ], ['--lat','-t',GetoptLong::REQUIRED_ARGUMENT ], ['--long','-g',GetoptLong::REQUIRED_ARGUMENT ], ['--dow','-m',GetoptLong::REQUIRED_ARGUMENT ], ['--date','-d',GetoptLong::REQUIRED_ARGUMENT ] ) # Set default items (still parsing...) myLat = "39" # Change me to set the desired default graticule's latitude myLong = "-84" # Change me to set the desired default graticule's longitude t=Time.now myDate = t.strftime("%Y-%m-%d") dow = "" begin opts.each do |opt,arg| case opt when '--help' RDoc::usage when '--lat' myLat = arg when '--long' myLong = arg when '--dow' dow = arg when '--date' myDate = arg end end rescue RDoc::usage end dateSplit = '' if myDate.split('/').length == 3 dateSplit = myDate.split('/') else dateSplit = myDate.split('-') end myDate = Date.civil(dateSplit[0].to_i, dateSplit[1].to_i, dateSplit[2].to_i) #fix for the "-30 rule" fixDate = Date.civil(2008,05,25) timeDelta = 0 if myLong.to_i > -30 and (myDate > fixDate) puts "-30 rule in effect!" timeDelta = 1 end algorithmEncodedDate = myDate.to_s urlEncodedDate = (myDate - timeDelta).strftime('%Y/%m/%d') baseURL = 'carabiner.peeron.com' basePath = '/xkcd/map/data/' fullPath = basePath + urlEncodedDate if dow=="" Net::HTTP.start(baseURL,80) do |http| dow = http.get(fullPath).body end end if !dow.include? "404 Not Found" ghash = algorithmEncodedDate+'-'+dow digest = Digest::MD5.hexdigest(ghash) digest1 = digest[0..15] digest2 = digest[16..31] puts "(" + myLat + "." + hexFracToDecFrac(digest1) + ", " + myLong + "." + hexFracToDecFrac(digest2) + ")" else puts "Dow information not available for "+urlEncodedDate end
Bugs
Dan Q used this as inspiration for his own implementation (underway), but notes that return sum.to_s[2..8]
is truncating the resulting coordinates, rather than rounding them, which can result in a small error. This should probably instead be sprintf('%.08f', sum)[2..]
, which rounds to 8 decimal places before cutting off the "0." from the front.