drifter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in drifter.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,102 @@
1
+ == drifter
2
+
3
+ drifter is a simple geocoding library with support for the Google Geocoder API and
4
+ the Yahoo Placefinder API. It also supports IP address geocoding using the hostip.info API
5
+
6
+ === Installation
7
+
8
+ gem install drifter
9
+ require 'rubygems'
10
+ require 'drifter'
11
+
12
+ # if you're using rails:
13
+ gem install drifter
14
+ gem 'drifter' # in Gemfile
15
+
16
+
17
+ === Usage
18
+
19
+ Drifter.geocode() takes a string representing an address or location and returns
20
+ an array of Drifter::Location objects
21
+
22
+ >> london = Drifter.geocode("London, UK").first
23
+ => <#Drifter::Location>
24
+
25
+ Drifter::Location objects hold common address attributes like city, state, post_code
26
+ country_code, lat and lng:
27
+
28
+ >> [london.country_code, london.lat, london.lng]
29
+ => ['GB', 51.5001524, -0.1262362]
30
+
31
+ Reverse geocoding is also supported. Instead of passing a string to geocode(), you can
32
+ pass a two item array or an object that responds to lat() and lng()
33
+
34
+ >> loc = Drifter.geocode( [53.4807125, -2.2343765] ).first
35
+ => [loc.city, loc.state].join(', ')
36
+ => "Manchester, England"
37
+
38
+ IP address gecoding is supported using the hostip.info api. Just pass the IP as the
39
+ location parameter
40
+
41
+ >> loc = Drifter.geocode('1.2.3.4').first
42
+ => <#Drifter::Location>
43
+
44
+ hostip.info only provides the city, country, lat and lng. If you need more info, you
45
+ can reverse geocode the result:
46
+
47
+ >> loc = Drifter.geocode('1.2.3.4').first
48
+ >> loc = Drifter.geocode(loc).first
49
+ >> loc.state_code
50
+ => 'CA'
51
+
52
+ Google is the default geocoding provider and works out of the box. Yahoo's placefinder
53
+ is also supported but you'll need an api key (they call it an appid)
54
+
55
+ >> Drifter.default_geocoder = :yahoo
56
+ >> Drifter::Geocoders::Yahoo.api_key = 'my_key'
57
+
58
+ >> bh = Drifter.geocode("90210").first
59
+ => <#Drifter::Location>
60
+
61
+ You can change the geocoder per request:
62
+
63
+ >> Drifter.geocode("springfield", :geocoder => :yahoo)
64
+ >> Drifter.geocode("springfield", :geocoder => :google)
65
+
66
+ Both Yahoo and Google return a lot more info than is held in Drifter::Location's standard
67
+ attributes. You can access the extra data using the data() method which returns a Hash
68
+
69
+ # using google as the provider:
70
+ >> london.data["geometry"]["location_type"]
71
+ => "APPROXIMATE"
72
+
73
+ The key => value pairs in the data Hash are specific to each provider, so you'll have to
74
+ check their docs to see what's available. You can also modify the query sent to the
75
+ geocoder to customise the results. Any option other than :geocoder will be URL encoded
76
+ and sent as a query string parameter e.g. Yahoo's service returns a timezone if you pass a
77
+ 'flags' parameter containing a 'T':
78
+
79
+ >> Drifter.default_geocoder = :yahoo
80
+ >> paris = Drifter.geocode("Paris", :flags => 'T').first
81
+ >> paris.data["timezone"]
82
+ => "Europe/paris"
83
+
84
+ Finally, Drifter::Location objects have a distance_to() method
85
+
86
+ >> london.distance_to(bh)
87
+ => 5438.60013996461
88
+
89
+ Distances are returned in miles by default. You can change this per request or change the default
90
+
91
+ >> Drifter.default_units = :km
92
+ >> london.distance_to(bh, :units => :miles)
93
+
94
+ Drifter.geocode() always returns an array if the request was processed successfully by the
95
+ geocoding service. An empty array indicates that the service returned no results.
96
+
97
+ If the geocoding service returns an error, Drifter.geocode() returns nil and Drifter.last_error()
98
+ returns a hash with the error :code and :message
99
+
100
+ === License
101
+
102
+ MIT License. Copyright 2011 Ahmed Adam (http://github.com/ahmedrb)
@@ -0,0 +1,102 @@
1
+ == drifter
2
+
3
+ drifter is a simple geocoding library with support for the Google Geocoder API and
4
+ the Yahoo Placefinder API. It also supports IP address geocoding using the hostip.info API
5
+
6
+ === Installation
7
+
8
+ gem install drifter
9
+ require 'rubygems'
10
+ require 'drifter'
11
+
12
+ # if you're using rails:
13
+ gem install drifter
14
+ gem 'drifter' # in Gemfile
15
+
16
+
17
+ === Usage
18
+
19
+ Drifter.geocode() takes a string representing an address or location and returns
20
+ an array of Drifter::Location objects
21
+
22
+ >> london = Drifter.geocode("London, UK").first
23
+ => <#Drifter::Location>
24
+
25
+ Drifter::Location objects hold common address attributes like city, state, post_code
26
+ country_code, lat and lng:
27
+
28
+ >> [london.country_code, london.lat, london.lng]
29
+ => ['GB', 51.5001524, -0.1262362]
30
+
31
+ Reverse geocoding is also supported. Instead of passing a string to geocode(), you can
32
+ pass a two item array or an object that responds to lat() and lng()
33
+
34
+ >> loc = Drifter.geocode( [53.4807125, -2.2343765] ).first
35
+ => [loc.city, loc.state].join(', ')
36
+ => "Manchester, England"
37
+
38
+ IP address gecoding is supported using the hostip.info api. Just pass the IP as the
39
+ location parameter
40
+
41
+ >> loc = Drifter.geocode('1.2.3.4').first
42
+ => <#Drifter::Location>
43
+
44
+ hostip.info only provides the city, country, lat and lng. If you need more info, you
45
+ can reverse geocode the result:
46
+
47
+ >> loc = Drifter.geocode('1.2.3.4').first
48
+ >> loc = Drifter.geocode(loc).first
49
+ >> loc.state_code
50
+ => 'CA'
51
+
52
+ Google is the default geocoding provider and works out of the box. Yahoo's placefinder
53
+ is also supported but you'll need an api key (they call it an appid)
54
+
55
+ >> Drifter.default_geocoder = :yahoo
56
+ >> Drifter::Geocoders::Yahoo.api_key = 'my_key'
57
+
58
+ >> bh = Drifter.geocode("90210").first
59
+ => <#Drifter::Location>
60
+
61
+ You can change the geocoder per request:
62
+
63
+ >> Drifter.geocode("springfield", :geocoder => :yahoo)
64
+ >> Drifter.geocode("springfield", :geocoder => :google)
65
+
66
+ Both Yahoo and Google return a lot more info than is held in Drifter::Location's standard
67
+ attributes. You can access the extra data using the data() method which returns a Hash
68
+
69
+ # using google as the provider:
70
+ >> london.data["geometry"]["location_type"]
71
+ => "APPROXIMATE"
72
+
73
+ The key => value pairs in the data Hash are specific to each provider, so you'll have to
74
+ check their docs to see what's available. You can also modify the query sent to the
75
+ geocoder to customise the results. Any option other than :geocoder will be URL encoded
76
+ and sent as a query string parameter e.g. Yahoo's service returns a timezone if you pass a
77
+ 'flags' parameter containing a 'T':
78
+
79
+ >> Drifter.default_geocoder = :yahoo
80
+ >> paris = Drifter.geocode("Paris", :flags => 'T').first
81
+ >> paris.data["timezone"]
82
+ => "Europe/paris"
83
+
84
+ Finally, Drifter::Location objects have a distance_to() method
85
+
86
+ >> london.distance_to(bh)
87
+ => 5438.60013996461
88
+
89
+ Distances are returned in miles by default. You can change this per request or change the default
90
+
91
+ >> Drifter.default_units = :km
92
+ >> london.distance_to(bh, :units => :miles)
93
+
94
+ Drifter.geocode() always returns an array if the request was processed successfully by the
95
+ geocoding service. An empty array indicates that the service returned no results.
96
+
97
+ If the geocoding service returns an error, Drifter.geocode() returns nil and Drifter.last_error()
98
+ returns a hash with the error :code and :message
99
+
100
+ === License
101
+
102
+ MIT License. Copyright 2011 Ahmed Adam (http://github.com/ahmedrb)
@@ -0,0 +1,8 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ desc "Run tests"
5
+ task :test do
6
+ path = File.dirname(__FILE__) + '/test/*_test.rb'
7
+ Dir[path].each { |f| require f }
8
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "drifter/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "drifter"
7
+ s.version = Drifter::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ahmed Adam"]
10
+ s.homepage = "http://github.com/ahmedrb/drifter"
11
+ s.summary = %q{Simple geocoding library for ruby}
12
+
13
+ s.rubyforge_project = "drifter"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency 'json', '~> 1.4.6'
21
+ end
@@ -0,0 +1,92 @@
1
+ require 'rubygems'
2
+ require 'drifter/distance/haversine'
3
+ require 'drifter/geocoders'
4
+ require 'drifter/location'
5
+
6
+ module Drifter
7
+
8
+ @@default_geocoder = :google
9
+ @@default_units = :miles
10
+ @@last_error = nil
11
+
12
+
13
+ # returns the default geocoder
14
+ def self.default_geocoder
15
+ @@default_geocoder
16
+ end
17
+
18
+
19
+ # Sets the default geocoder. Supported values are :google or :yahoo
20
+ # If using :yahoo, you will also need to set your yahoo appid using
21
+ # Drifter::Geocoders::Yahoo.app_id=()
22
+ def self.default_geocoder=(value)
23
+ @@default_geocoder = value
24
+ end
25
+
26
+
27
+ # Returns the default units for distance calculations
28
+ def self.default_units
29
+ @@default_units
30
+ end
31
+
32
+
33
+ # Sets the default units for distance calculations.
34
+ # Supported values are :miles and :kms
35
+ def self.default_units=(value)
36
+ @@default_units=value
37
+ end
38
+
39
+
40
+ # Helper method to extract the lat and lng from an array or from any object
41
+ # that responds to lat() and lng(). Returns nil if neither of those apply
42
+ def self.extract_latlng(loc)
43
+ return loc.first, loc.last if loc.is_a?(Array) && loc.size == 2
44
+ return loc.lat, loc.lng if loc.respond_to?(:lat) && loc.respond_to?(:lng)
45
+ return nil
46
+ end
47
+
48
+
49
+ # Same as Drifter,extract_latlng() but raises ArgumentError on failure
50
+ def self.extract_latlng!(loc)
51
+ lat, lng = extract_latlng(loc)
52
+ return lat, lng if lat && lng
53
+ raise ArgumentError, "Could not extract lat and lng from #{loc.class.name} object"
54
+ end
55
+
56
+
57
+ # Accepts a string or a set of coordinates and returns an Array of Drifter::Location
58
+ # objects with the results of the geocoding request. If there is an error, this
59
+ # method returns nil and the error can be accessed via Drifter.last_error().
60
+ #
61
+ # You can over-ride the default geocoder using the params[:geocoder] option:
62
+ # Drifter.geocode("somewhere", :geocoder => :yahoo)
63
+ #
64
+ # You can perform reverse geocoding by passing a [lat, lng] array, or an object that
65
+ # responds to lat() and lng(). Any params besides :geocoder are url encoded
66
+ # and sent to the geocoder as query string parameters. This can be used to modify
67
+ # the results of the query. See the README for an example.
68
+ #
69
+ # if location is a string containing an IP address, the :geocoder value is ignored
70
+ # and the ip is geocoded using the hostip.info web service. This only returns a
71
+ # country, city, lat and lng so you could reverse geocode the result to get more info
72
+ def self.geocode(location, params={})
73
+ geocoder = params.delete(:geocoder) || default_geocoder
74
+ geocoder = :hostip if location.to_s =~ Drifter::Geocoders::HostIP::IP_PATTERN
75
+ geocoder = case geocoder
76
+ when :google then Drifter::Geocoders::Google
77
+ when :yahoo then Drifter::Geocoders::Yahoo
78
+ when :hostip then Drifter::Geocoders::HostIP
79
+ else raise ArgumentError, "Geocoder #{geocoder} not recognised"
80
+ end
81
+ results = geocoder.geocode(location, params)
82
+ @@last_error = geocoder.last_error
83
+ return results
84
+ end
85
+
86
+
87
+ # Returns a Hash containing error code and status from a failed geocoding request
88
+ def self.last_error
89
+ @@last_error
90
+ end
91
+
92
+ end
@@ -0,0 +1,69 @@
1
+ module Drifter
2
+ module Distance
3
+ class Haversine
4
+
5
+ EarthRadiusInMiles = 3956
6
+ EarthRadiusInKms = 6371
7
+ RAD_PER_DEG = 0.017453293 # PI/180
8
+
9
+
10
+ # this method is from Landon Cox's haversine.rb (GNU Affero GPL v3):
11
+ # http://www.esawdust.com/blog/gps/files/HaversineFormulaInRuby.html:
12
+ # http://www.esawdust.com (Landon Cox)
13
+ # http://www.esawdust.com/blog/businesscard/businesscard.html
14
+ def self.between(point1, point2, options={})
15
+ lat1, lon1 = Drifter.extract_latlng!(point1)
16
+ lat2, lon2 = Drifter.extract_latlng!(point2)
17
+
18
+ dlon = lon2 - lon1
19
+ dlat = lat2 - lat1
20
+
21
+ dlon_rad = dlon * RAD_PER_DEG
22
+ dlat_rad = dlat * RAD_PER_DEG
23
+
24
+ lat1_rad = lat1 * RAD_PER_DEG
25
+ lon1_rad = lon1 * RAD_PER_DEG
26
+
27
+ lat2_rad = lat2 * RAD_PER_DEG
28
+ lon2_rad = lon2 * RAD_PER_DEG
29
+
30
+ a = (Math.sin(dlat_rad/2))**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * (Math.sin(dlon_rad/2))**2
31
+ c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a))
32
+
33
+ units = options.delete(:units) || Drifter.default_units
34
+ return EarthRadiusInKms * c if units == :km
35
+ return EarthRadiusInMiles * c
36
+ end
37
+
38
+
39
+ # haversine sql based on http://code.google.com/apis/maps/articles/phpsqlsearch.html
40
+ # you will need to add 'select' and 'AS distance' if required
41
+ def self.to_postgresql(options)
42
+ origin = options[:origin]
43
+ lat, lng = Drifter.extract_latlng!(origin)
44
+ lat_column = options[:lat_column] || :lat
45
+ lng_column = options[:lng_column] || :lng
46
+ units = options[:units] || Drifter.default_units
47
+ multiplier = EarthRadiusInMiles
48
+ multiplier = EarthRadiusInKms if units == :km
49
+
50
+ postgres = <<-EOS
51
+ #{multiplier} * ACOS(
52
+ COS( RADIANS(#{lat}) ) *
53
+ COS( RADIANS( #{lat_column} ) ) *
54
+ COS( RADIANS( #{lng_column} ) -
55
+ RADIANS(#{lng}) ) +
56
+ SIN( RADIANS(#{lat}) ) *
57
+ SIN( RADIANS( #{lat_column} ) )
58
+ )
59
+ EOS
60
+ end
61
+
62
+ # postgresql code seems to work fine fo sql
63
+ def self.to_mysql(options)
64
+ to_postgresql(options)
65
+ end
66
+
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ require 'drifter/geocoders/hostip'
2
+ require 'drifter/geocoders/yahoo'
3
+ require 'drifter/geocoders/google'
@@ -0,0 +1,57 @@
1
+ require 'cgi'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ module Drifter
7
+ module Geocoders
8
+
9
+ # nodoc
10
+ class Base
11
+
12
+ @@last_error = nil # used by geocode()
13
+
14
+
15
+ # takes a location parameter and returns an Array of Drifter::Location objects
16
+ # with the results of the geocoding request. If the geocoder returns an error,
17
+ # this method should store the error in @@last_error and return nil.
18
+ def self.geocode(*args)
19
+ raise "Not implemented"
20
+ end
21
+
22
+
23
+ # Returns a Hash containing error information from the last geocoding request
24
+ # or nil if there was no error. Subclasses should set @@last_error to nil
25
+ # after every successful request
26
+ def self.last_error
27
+ @@last_error
28
+ end
29
+
30
+
31
+ # Returns a URI object used to make the call to the geocoding service.
32
+ # Subclasses should implement checks for required parameters. See geocoder/yahoo.rb
33
+ # for an example
34
+ def self.query_uri(options={})
35
+ raise "Not implemented"
36
+ end
37
+
38
+
39
+ private
40
+
41
+
42
+ # wrapper for Net::HTTP.get
43
+ def self.fetch(uri)
44
+ Net::HTTP.get(uri.host, uri.request_uri)
45
+ end
46
+
47
+
48
+ # Converts hash to a url enoded query string
49
+ def self.hash_to_query_string(hash)
50
+ qs = hash.collect{ |k,v| k.to_s + '=' + CGI::escape(v.to_s) }
51
+ qs.join('&')
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+ end