Geokit
DESCRIPTION
The Geokit gem provides:
- Distance calculations between two points on the earth. Calculate the distance in miles, kilometers, meters, or nautical miles, with all the trigonometry abstracted away by Geokit.
- Geocoding from multiple providers. It supports Google, Yahoo, Geocoder.us, and Geocoder.ca geocoders, and others. It provides a uniform response structure from all of them.
It also provides a fail-over mechanism, in case your input fails to geocode in one service.
- Rectangular bounds calculations: is a point within a given rectangular bounds?
- Heading and midpoint calculations
Combine this gem with the geokit-rails to get location-based finders for your Rails app.
COMMUNICATION
- If you need help, use Stack Overflow. (Tag 'geokit' and we'll be alerted)
- If you found a bug, use GitHub issues.
- If you have an idea, use GitHub issues.
- If you'd like to ask a general question, use GitHub issues.
- If you want to contribute, submit a pull request.
INSTALL
gem install geokit
SUPPORTED GEOCODERS
"regular" address geocoders
- Yahoo BOSS - requires an API key.
- Geocoder.us - may require authentication if performing more than the free request limit.
- Geocoder.ca - for Canada; may require authentication as well.
- Geonames - a free geocoder
- Bing
- Yandex
- MapQuest
- Geocod.io
- OpenStreetMap (Nominatim)
- Mapbox - requires an access token
- OpenCage - requires an API key
address geocoders that also provide reverse geocoding
- Google - Supports multiple results and bounding box/country code biasing. Also supports Maps API for Business keys; see the configuration section below.
- FCC
- OpenStreetMap (Nominatim)
- Mapbox
- OpenCage
IP address geocoders
- IP - geocodes an IP address using hostip.info's web service.
- Geoplugin.net -- another IP address geocoder
- Geobytes
- RIPE
- MaxMind
- Ipstack
- IP-API.com
HTTPS-supporting geocoders
- Google
- Yahoo
- Bing
- FCC
- MapQuest
- Mapbox
- OpenCage
Options to control the use of HTTPS are described below in the Configuration section.
QUICK START
irb> require 'rubygems'
irb> require 'geokit'
irb> a=Geokit::Geocoders::GoogleGeocoder.geocode '140 Market St, San Francisco, CA'
irb> a.ll
=> 37.79363,-122.396116
irb> b=Geokit::Geocoders::GoogleGeocoder.geocode '789 Geary St, San Francisco, CA'
irb> b.ll
=> 37.786217,-122.41619
irb> a.distance_to(b)
=> 1.21120007413626
irb> a.heading_to(b)
=> 244.959832435678
irb(main):006:0> c=a.midpoint_to(b)
irb> c.ll
=> "37.7899239257175,-122.406153503469"
irb(main):008:0> d=c.endpoint(90,10)
irb> d.ll
=> "37.7897825005142,-122.223214776155"
FYI, that .ll
method means "latitude longitude".
See the RDOC more more ... there are also operations on rectangular bounds (e.g., determining if a point is within bounds, find the center, etc).
CONFIGURATION
If you're using this gem by itself, here are the configuration options:
Geokit::default_units = :miles
Geokit::default_formula = :sphere
Geokit::Geocoders::request_timeout = 3
Geokit::Geocoders::proxy = 'https://user:password@host:port'
Geokit::Geocoders::useragent = 'my agent string'
Geokit::Geocoders::YahooGeocoder.key = 'REPLACE_WITH_YOUR_YAHOO_KEY'
Geokit::Geocoders::YahooGeocoder.secret = 'REPLACE_WITH_YOUR_YAHOO_SECRET'
Geokit::Geocoders::GoogleGeocoder.client_id = ''
Geokit::Geocoders::GoogleGeocoder.cryptographic_key = ''
Geokit::Geocoders::GoogleGeocoder.channel = ''
Geokit::Geocoders::GoogleGeocoder.api_key = ''
Geokit::Geocoders::UsGeocoder.key = 'username:password'
Geokit::Geocoders::CaGeocoder.key = 'KEY'
Geokit::Geocoders::GeonamesGeocoder.key = 'KEY'
Geokit::Geocoders::IpstackGeocoder.api_key = 'API_KEY'
Geokit::Geocoders::BingGeocoder.key = ''
Geokit::Geocoders::MapQuestGeocoder.key = ''
Geokit::Geocoders::YandexGeocoder.key = ''
Geokit::Geocoders::MapboxGeocoder.key = 'ACCESS_TOKEN'
Geokit::Geocoders::OpencageGeocoder.key = 'some_api_key'
Geokit::Geocoders::GeonamesGeocoder.premium = false
Geokit::Geocoders::provider_order = [:google,:us]
Geokit::Geocoders::secure = false
Geokit::Geocoders::ssl_verify_mode = OpenSSL::SSL::VERIFY_(PEER/NONE)
Google Geocoder Tricks
The Google Geocoder sports a number of useful tricks that elevate it a little bit above the rest of the currently supported geocoders. For starters, it returns a suggested_bounds
property for all your geocoded results, so you can more easily decide where and how to center a map on the places you geocode. Here's a quick example:
irb> res = Geokit::Geocoders::GoogleGeocoder.geocode('140 Market St, San Francisco, CA')
irb> pp res.suggested_bounds
#<Geokit::Bounds:0x53b36c
@ne=#<Geokit::LatLng:0x53b204 @lat=37.7968528, @lng=-122.3926933>,
@sw=#<Geokit::LatLng:0x53b2b8 @lat=37.7905576, @lng=-122.3989885>>
In addition, you can use viewport or country code biasing to make sure the geocoders prefers results within a specific area. Say we wanted to geocode the city of Toledo in Spain. A normal geocoding query would look like this:
irb> res = Geokit::Geocoders::GoogleGeocoder.geocode('Toledo')
irb> res.full_address
=> "Toledo, OH, USA"
Not exactly what we were looking for. We know that Toledo is in Spain, so we can tell the Google Geocoder to prefer results from Spain first, and then wander the Toledos of the world. To do that, we have to pass Spain's ccTLD (country code top-level domain) to the :bias
option of the geocode
method. You can find a comprehensive list of all ccTLDs here: http://en.wikipedia.org/wiki/CcTLD.
irb> res = Geokit::Geocoders::GoogleGeocoder.geocode('Toledo', :bias => 'es')
irb> res.full_address
=> "Toledo, Toledo, Spain"
Alternatively, we can specify the geocoding bias as a bounding box object. Say we wanted to geocode the Winnetka district in Los Angeles.
irb> res = Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka')
irb> res.full_address
=> "Winnetka, IL, USA"
Not it. What we can do is tell the geocoder to return results only from in and around LA.
irb> la_bounds = Geokit::Geocoders::GoogleGeocoder.geocode('Los Angeles').suggested_bounds
irb> res = Geokit::Geocoders::GoogleGeocoder.geocode('Winnetka', :bias => la_bounds)
irb> res.full_address
=> "Winnetka, California, USA"
Another option is to use Component Filtering as described at https://developers.google.com/maps/documentation/geocoding/intro#ComponentFiltering. To do that supply the :components
option to the geocode
method. This option should be a hash with keys corresponding to desired component names.
Suppose we'd like to geocode string 'Austin'. Regularly, Google would return 'Austin, TX, USA' for such a query. Not with component filtering:
irb>res = Geokit::Geocoders::GoogleGeocoder.geocode("austin", components: { administrative_area: 'IL', country: 'US' })
irb>res.full_address
=> "Austin, Chicago, IL, USA"
The Multigeocoder
Multi Geocoder - provides failover for the physical location geocoders, and also IP address geocoders. Its configured by setting Geokit::Geocoders::provider_order, and Geokit::Geocoders::ip_provider_order. You should call the Multi-Geocoder with its :geocode method, supplying one address parameter which is either a real street address, or an ip address. For example:
Geokit::Geocoders::MultiGeocoder.geocode("900 Sycamore Drive")
Geokit::Geocoders::MultiGeocoder.geocode("12.12.12.12")
Geokit::Geocoders::MultiGeocoder.geocode("Hamburg, Germany", :provider_order => [:osm, :mapbox, :google])
MULTIPLE RESULTS
Some geocoding services will return multiple results if the there isn't one clear result.
Geoloc can capture multiple results through its "all" method.
irb> geo=Geokit::Geocoders::GoogleGeocoder.geocode("900 Sycamore Drive")
irb> geo.full_address
=> "900 Sycamore Dr, Arkadelphia, AR 71923, USA"
irb> geo.all.size
irb> geo.all.each { |e| puts e.full_address }
900 Sycamore Dr, Arkadelphia, AR 71923, USA
900 Sycamore Dr, Burkburnett, TX 76354, USA
900 Sycamore Dr, TN 38361, USA
....
geo.all is just an array of additional Geolocs, so do what you want with it. If you call .all on a
geoloc that doesn't have any additional results, you will get an array of one.
NOTES ON WHAT'S WHERE
mappable.rb contains the Mappable module, which provides basic
distance calculation methods, i.e., calculating the distance
between two points.
LatLng is a simple container for latitude and longitude, but
it's made more powerful by mixing in the above-mentioned Mappable
module -- therefore, you can calculate easily the distance between two
LatLng objects with distance = first.distance_to(other)
GeoLoc represents an address or location which
has been geocoded. You can get the city, zipcode, street address, etc.
from a GeoLoc object. GeoLoc extends LatLng, so you also get lat/lng
AND the Mappable module goodness for free.
geocoders.rb contains all the geocoder implementations. All the geocoders
inherit from a common base (class Geocoder) and implement the private method
do_geocode.
WRITING YOUR OWN GEOCODERS
If you would like to write your own geocoders, you can do so by requiring 'geokit' or 'geokit/geocoders.rb' in a new file and subclassing the base class (which is class "Geocoder").
You must then also require such external file back in your main geokit configuration.
require "geokit"
module Geokit
module Geocoders
class MyGeocoder < Geocoder
config :key
private
def self.do_geocode(address, options = {})
end
def self.parse_json(json)
end
end
end
end