Introduction
Geocoding typically refers to the transformation process of addresses and places to coordinates.
# Example
Geocoder.coordinates("25 Main St, Cooperstown, NY")
=> [42.700149, -74.922767]
It is sometimes called forward geocoding whereas Reverse geocoding uses geographic coordinates to find a description of the location, most typically a postal address or place name.
In Rails we are going to use Geocoder
gem.
Geocoder [Reference Inspired from Original Doc]
Geocoder is a complete geocoding solution for Ruby. With Rails it adds geocoding (by street or IP address), reverse geocoding (finding street address based on given coordinates), and distance queries. It’s as simple as calling geocode
on your objects, and then using a scope like Venue.near("Billings, MT")
.
For geocoding your model must provide a method that returns an address. This can be a single attribute, but it can also be a method that returns a string assembled from different attributes (eg: city
, state
, and country
).
Next, your model must tell Geocoder which method returns your object’s geocodable address:
geocoded_by :full_street_address # can also be an IP address
after_validation :geocode # auto-fetch coordinates
For reverse geocoding, tell Geocoder which attributes store latitude and longitude:
reverse_geocoded_by :latitude, :longitude
after_validation :reverse_geocode # auto-fetch address
Request Geocoding by Zip-Code
geocoded_by :zip_code
reverse_geocoded_by :latitude, :longitude do |obj, results|
if geo = results.first
obj.state = geo.state
obj.city = geo.city
end
end
Request Geocoding by IP Address
Geocoder adds location
and safe_location
methods to the standard Rack::Request
object so you can easily look up the location of any HTTP request by IP address. For example, in a Rails controller or a Sinatra app:
# returns Geocoder::Result object
result = request.location
The location
method is vulnerable to trivial IP address spoofing via HTTP headers. If that’s a problem for your application, use safe_location
instead.
Location-Aware Database Queries
For geocoded objects you can do things like this:
if obj.geocoded?
obj.nearbys(30) # other objects within 30 miles
obj.distance_from([40.714,-100.234]) # distance from arbitrary point to object
obj.bearing_to("Paris, France") # direction from object to arbitrary point
end
At class level
Venue.near('Omaha, NE, US', 20) # venues within 20 miles of Omaha
Venue.near([40.71, -100.23], 20) # venues within 20 miles of a point
Venue.near([40.71, -100.23], 20, :units => :km)
# venues within 20 kilometres of a point
Venue.geocoded # venues with coordinates
Venue.not_geocoded # venues without coordinates
Here Venue
is a model in which geocoder
is initialized.
Distance and Bearing
When you run a location-aware query the returned objects have two attributes added to them (only w/ ActiveRecord):
obj.distance
– number of miles from the search point to this object
obj.bearing
– direction from the search point to this object
Bearing is given as a number of clockwise degrees from due north, for example:
0
– due north
180
– due south
90
– due east
270
– due west

230.1
– southwest
359.9
– almost due north
You can convert these numbers to compass point names by using the utility method provided:
Geocoder::Calculations.compass_point(355) # => "N"
Geocoder::Calculations.compass_point(45) # => "NE"
Geocoder::Calculations.compass_point(208) # => "SW"
when using SQLite distance
and bearing
values are provided for interface consistency only. They are not very accurate.
To calculate accurate distance and bearing with SQLite or MongoDB:
obj.distance_to([43.9,-98.6]) # distance from obj to point
obj.bearing_to([43.9,-98.6]) # bearing from obj to point
obj.bearing_from(obj2) # bearing from obj2 to obj
The bearing_from/to
methods take a single argument which can be: a [lat,lon]
array, a geocoded object, or a geocodable address (string). The distance_from/to
methods also take a units argument (:mi
, :km
, or :nm
for nautical miles).
Search in Box rather than Circle
You can also look within a square rather than a radius (circle) by using the within_bounding_box
scope:
distance = 20
center_point = [40.71, 100.23]
box = Geocoder::Calculations.bounding_box(center_point, distance)
Venue.within_bounding_box(box)
- Note, however, that returned results do not include
distance
and bearing
attributes.
- Note that
#near
performs both bounding box and radius queries for speed.
Search in Donut or Ring
You can also specify a minimum radius (if you’re using ActiveRecord and not Sqlite) to constrain the lower bound (ie. think of a donut, or ring) by using the :min_radius
option:
box = Geocoder::Calculations.bounding_box(center_point, distance, :min_radius => 10.5)
Auto Assignment of decoded info
So far we have looked at shortcuts for assigning geocoding results to object attributes.
However, if you need to do something fancy you can skip the auto-assignment by providing a block (takes the object to be geocoded and an array ofGeocoder::Result
objects) in which you handle the parsed geocoding result any way you like, for example:
reverse_geocoded_by :latitude, :longitude do |obj,results|
if geo = results.first
obj.city = geo.city
obj.zipcode = geo.postal_code
obj.country = geo.country_code
end
end
after_validation :reverse_geocode
Every Geocoder::Result
object, result
, provides the following data:
result.latitude
– float
result.longitude
– float
result.coordinates
– array of the above two in the form of [lat,lon]
result.address
– string
result.city
– string
result.state
– string
result.state_code
– string
result.postal_code
– string
result.country
– string
result.country_code
– string
Sources
https://github.com/alexreisner/geocoder
http://www.rubygeocoder.com/