In today’s technological world, we rarely find ourselves relying on printed maps (unless you are my great uncle). On the contrary, we live in a world where practically all of our devices are location detecting, from our cellphones to our computers and even the smart devices inside our homes.

Geocoding Uses

Location detection and location tracking has become an integral part of many modern applications, and it is easy to understand why. The ease of navigating an unfamiliar neighborhood or of finding a restaurant within a five block radius of your current location simply can’t be beat. Workout apps can track and map our runs, search apps like Yelp use location data to suggest nearby businesses, even dating apps rely on our location and distance settings to present us with nearby matches. And I can personally vouch for their effectiveness! I met my husband via a combination of Tinder and Facebook (if you’re wondering what that means, you can read about it here 😉 ).

I again discovered the importance of geocoding in programming while working on my very first project (a simple CLI application) at Flatiron School NYC. My partner and I were building an application that would return real-time price estimates for a ride-share app and we found that their API required a start and end latitude and longitude to run the calculations.

Implementing the Geocoder Gem

A simple Google search immediately led me to the Ruby Geocoder gem, and with over 13 million downloads on rubygems.org I felt fairly confident that it would solve my problem. The gem is simple to install and use, and within about 2 minutes we were calculating real-time ride estimates.

To install the gem, simply add gem 'geocoder', '~> 1.3', '>= 1.3.7' to your Gemfile and then run gem install geocoder -v 1.3.7 in your terminal.

API’s

Geocoder uses external API’s such as Google, Bing, Baidu and more and we can configure which we’d like to use as well as other settings by running rails generate geocoder:config in our terminal. This will create the Config Initializer file geocoder.rb where we can edit our settings. It is important to note that each API will have its own limitations and possibly costs, so you should consider the amount of traffic you may expect to have once your program is deployed and adjust your settings as needed. My project partner and I actually hit an API query limit while we were building and testing out our model, and we ended up having to wait an hour or so before we could resume our testing. There are number of ways that we can deal with API limits, but we will only discuss one or two in this blog post. You can find more information in the Geocoder documentation.

Basic Usage

For the purposes of our project, our needs were minimal and we could use Geocoder in its simplest form without any customizations. The basic functionality will run an address string through Geocoder and return the location’s latitude and longitude. To run a basic geocoder search, you would write something like this:

The return value is an array of latitude and longitude. We were then able to grab these at location.first.coordinates[0] and location.first.coordinates[1] to interpolate into our ride-share API query. You can also run the reverse and enter the latitude and logitude into Geocoder and return an address string.

Geocoding Objects

We didn’t utilize Geocoder to its full extent in our basic CLI application; however, there are many additional functionalities to Geocoder. Let’s start with the ability to geocode objects. Let’s say we have a list of addresses, for example a list of store locations that we’d like to geocode for various uses. We can organize these into a CSV file with columns for street, city, zip and state and then create a table in our database that has matching columns, plus two additional latitude and longitude columns. We can then seed our database with the CSV file and use Geocoder to collect the latitude and longitude for each address and add this to our empty lat/long database columns.

To geocode our objects, we will need to add geocoded_by :address to our model.

This tells Geocoder that we will be using a method called #address to run the geocode, so the next thing we need to do is set up our method. We learned above that Geocoder accepts a single address string to geocode, so let's create our #address method to pass through our street, city, zip and state and convert this into a string.

We can use .compact to leave out any columns that may be nil and then we will convert our array to a string by joining each item at the comma with .join(", ").

To add a geocode method that we can invoke via callback, we add After_validation :geocode.

This is the actual method that we will call on our location in order to geocode it. For example, if we had location1, we could simply call location1.geocode and it would return our latitude/longitude array. One thing to note, however, is that this will geocode our location every time you call the method, and this could cause problems if there are any API limits. To help avoid this problem, you can update the validation code to:

Now we can define a method to check if the address has changed to help reduce our API requests.

The #address_changed? method will check to see if either the street, or the city, or the zip, or the state has changed. If so, then our address has changed and it will run geocode, if not we can avoid running the geocode again and avoid unnecessary API requests.

#near and #within_bounding_box

Another really cool feature of Geocoder is the #near method. We can simply invoke this method on our class to find the nearest location in our database to our request. For example, Location.near("Midtown NYC") would return our Empire State Building object (since that's the only thing we have in our database at the moment, and it's actually in Midtown!). We can either pass in an adress string or an array of latitude and longitude to #near. We see these types of queries often with apps that grab our coordinates through our phone's GPS or our computer's IP address to search nearby our, or a specific, location.

Geocoder can also search within a bounding box. We’ve seen this on Yelp or Google when we move the map around on our screen and are presented new results based on the portion of map that we are viewing. With Geocoder we can enter the latitudes and longitudes of the bounding box corners and see all the results within that range.

Bulk Geocoding

Rather than geocoding one object at a a time, we can also bulk geocode using a rake task rake geocode:all CLASS=Location SLEEP=0.25 BATCH=100. We can add the SLEEP and BATCH to tell Geocoder to wait 0.25 seconds between geocoding and to run batches of 100 at a time. These two parameters also help reduce our API hits.

Observations

An interesting thing that we noticed during our project was that we sometimes had a slight discrepancy on the latitude and longitude coordinates that Geocoder returned if the addresses were entered slightly differently each time (i.e. 24th St. vs. 24th Street). This could be because it may not be finding the exact same spot of the building, etc. each time, but the results were always in the general vicinity of our submitted address, which was perfect for our needs.

Sources

Written by

Full-stack web developer with a background in marketing and graphic design. A coding journey: From nil to skill

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store