Hank Stoever
part time nerd, part time gnar.

Pro Tips for Using Geocoder with Rails

posted about 4 years ago - 2 min read

If you like this article, you'll like my free collection of Ruby on Rails tutorials.

I've now used the awesome geocoder gem in a few Ruby on Rails projects to implement features like geolocation and plotting things on a map. While the gem does a lot of the work for you, there are some extra tips that can be helpful to make the most of the gem.

Use Bing for Geocoding

One great part of the Geocoder gem is how easy it is to swap geocoding providers. The default provider is Google Maps, which is a great option. They don't have the best query limits, however. For example, they limit you to 5 requests per second and 2,500 requests per day. Bing has no per-second limit and allows 50,000 requests per day. It only take a minute or two to obtain an API key from bingmapsportal.com. The github documentation has a helpful section of comparisons between the available geocoding providers.

Once you've obtained your API key, configure geocoder in config/initializers/geocoder.rb:

Geocoder.configure {
  lookup: :bing,
  key: ENV['BING_GEOCODE_ID'],
}

Cache your Geocoding Calls

If you've set up basic caching with Rails (why wouldn't you?), you're one line of code away from integrating cacheing into Geocoder. Simply configure the cache store in your initializer:

Geocoder.configure({
  lookup: :bing,
  key: ENV['BING_GEOCODE_ID'],
  cache: Rails.cache,
})

Now, your geocoding calls will be cached. This is crucial for reducing page load times, as Geocoder's rack middleware will only have to geocode your user's I.P. address once.

Use a helper method for request.location

One of the trickier parts of using Geocoder is making sure things are working in development mode. Since you're probably accessing your Rails app from localhost, geocoded is unable to geocode your location from an I.P. address. To overcome this, I use a helper method located in application_controller.rb to provide a fake location:

def location
  if params[:location].blank?
    if Rails.env.test? || Rails.env.development?
      @location ||= Geocoder.search("50.78.167.161").first
    else
      @location ||= request.location
    end
  else
    params[:location].each {|l| l = l.to_i } if params[:location].is_a? Array
    @location ||= Geocoder.search(params[:location]).first
    @location
  end
end

Let's walk through what's going on here. The first condition checks for a :location parameter. If the parameter is present, we'll return a geolocation based on the value of the parameter. This allows us to append ?location=seattle or ?location=421+fairview+ave+n+seattle,+wa or even ?location[]=127&location[]=46 to provide latitude and longitude parameters. This is handy when testing what a page would look like when requested from a specific location.

If there is no :location parameter, and we're in development mode, it returns a default location. In this case, I chose a generic IP address that geocodes to Bellevue, Washington, USA. In production mode, it returns request.location, which is automatically provided by the gem.

An example for how to use this in a geoquery:

Model.near([location.latitude, location.longitude])

Real World Examples

All of these features were implemented in a recent Startup Weekend event for our team's project, 404kids. You can view our source on github, but here are the most relevant parts:

Using Ruby on Rails with Geocoder is, from my experience, the best way to build a web application with robust geolocation features. I hope you find this useful!

comments powered by Disqus