drifter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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