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 GemA 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.
7' to your Gemfile and then run gem install geocoder -v 1.
7 in your terminal.
API’sGeocoder 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 UsageFor 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:location = Geocoder.
search("Empire State Building NYC") location.
coordinates => [40.
9857]The return value is an array of latitude and longitude.
We were then able to grab these at location.
coordinates and location.
coordinates 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 ObjectsWe 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.
Class Location < ActiveRecord::Base geocoded_by :address endThis 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.
Class Location < ActiveRecord::Base geocoded_by :address def address [street, city, state, country].
join(", ") end endWe 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 .
To add a geocode method that we can invoke via callback, we add After_validation :geocode.
Class Location < ActiveRecord::Base geocoded_by :address After_validation :geocode def address [street, city, state, country].
join(", ") end endThis 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:Class Location < ActiveRecord::Base geocoded_by :address After_validation :geocode if: address_changed def address [street, city, state, country].
join(", ") end endNow we can define a method to check if the address has changed to help reduce our API requests.
Class Location < ActiveRecord::Base geocoded_by :address After_validation :geocode if: address_changed?.def address [street, city, state, country].
join(", ") end def address_changed?.street_changed?||city_changed?||zip_changed?||state_changed?.end endThe #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_boxAnother 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.
sw_corner = [40.
23] ne_corner = [36.
within_bounding_box(sw_corner, ne_corner)Bulk GeocodingRather than geocoding one object at a a time, we can also bulk geocode using a rake task rake geocode:all CLASS=Location SLEEP=0.
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.
ObservationsAn 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.
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.
com: Helpful video on implementation and usageDownload the gemGithub DocumentationGeocoder DocumentationOriginally published at http://thatgalcodes.
com on March 10, 2019.