If you have to work with Geographic data in your Rails applications the Geocoder gem makes this much simpler. It can convert place names to coordinates and vice-versa and even convert IP addresses to street addresses. It can also find nearby locations with their distance and bearing and has many other useful features.

In this episode we’ll create an application called Siteseer which will allow people to recommend tourist locations. Once we’ve created the application the next thing to do is to create a Location scaffold with address, latitude and longitude fields.

The naming of the latitude and longitude fields is important as Geocoder will use these to store the location coordinates, although the default field names can be overridden. Both fields need to be floats. We’ll need to migrate the database to create the new locations table.

terminal

$ rake db:migrate

Now we have the scaffolding set up we can create new locations but we have to enter the latitude and longitude manually. We’ll update our application now so that these fields are filled in automatically by Geocoder.

Installing and Using Geocoder

Geocoder is installed in the usual way. First we add a reference to the gem in the Gemfile and then run bundle to install it.

The geocode method sends a request to an external API, by default the Google Maps API. As it calls an external service it would be better to move this call into a background process. We won’t do that here but to find out how it’s done take a look at the recent episode on Resque [watch, read].

We can start up our application now and try Geocoder out. When we enter a new location, say “St Pancras Station, London”, without a latitude or longitude, Geocoder will fetch that data from the external API and add it to the location.

The external API will be called every time we update a location but this should only happen when the address field changes. We can make a simple change to the after_validation callback to implement this.

This will use dirty tracking to determine if the address has changed and only call the API if it has.

We can also use a reverse_geocoded_by method to convert a latitude and longitude into an address. This works in a similar way to the geocoded_by method and we won’t be using it in our application.

Getting nearby locations

It would be useful when viewing a location to be able to see a list of nearby locations. We have a number of locations in our database now so we’ll add this feature to our location page.

We want to list each location within a given distance and Geocoder makes getting nearby locations easy with its nearbys method. By default this will return locations within a twenty-mile radius, but we’ll restrict it to ten. To list the nearby locations we just loop through each location in @location.nearbys and display a link to it along with its distance in miles.

The form uses GET and will call the the index page with a search parameter when submitted. In the LocationsController’s index action we’ll check for that parameter and if it exists we’ll search for nearby locations.

If we reload the page and search for “Manchester” we’ll see the one matching location 30 miles away in Liverpool.

This is all we need to do to find locations that are near a given city.

Adding Maps

As we’re working with geographic locations it would be useful to have a map to display for each location. We’ll add one on the location page to show exactly where the location is. The Google Maps API provides a variety of ways to add maps to a web page but for simplicity we’ll use the Static Maps API. Maps are added with a simple image tag whose URL has parameters to specify various aspects of the map, including the latitude and longitude, size, zoom and so on. We’ll add the map directly above the list of nearby locations.

All of the parameters we pass are static with the exception of the latitude and longitude which come from our @location object. When we view the page for a location now we’ll see the map for that location.

If you want something a little fancier than a static image it’s worth taking a look at the Google Maps For Rails gem which makes interacting with the JavaScript API easy. This gives you a simple way to add interactive maps to your Rails applications.