geo_magic 0.2.3.1 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -5,8 +5,14 @@ Many of the projects did not return quality data (fx when requesting IP or locat
5
5
 
6
6
  This little gem should get you going...
7
7
 
8
- * Get IP and location data using freegeoip.net
9
- * Calculate of distance between map points using haversine (supports multiple distance units)
8
+ Some of the main features:
9
+
10
+ * Get IP and location data using freegeoip.net
11
+ * Calculate distance between map points using various algorithms (haversine, spherical, vincenty, straight line)
12
+ * Geocoding and Reverse Geocoding using adapters (currently: Graticule and Geocode gems)
13
+ * Calculate nearest points from a point
14
+ * Find points within a radius (circle or square from center)
15
+ * Calculate points within a bounding box
10
16
 
11
17
  You can either include the complete library like this:
12
18
 
@@ -73,19 +79,52 @@ it "calculates distance using Hash args (short)" do
73
79
  end
74
80
  </pre>
75
81
 
76
- h2. Extra features
82
+ h2. Geocoder adapters
83
+
84
+ The following Geocode adapters are supported
85
+
86
+ * Geocode
87
+ * Graticule
88
+ * GraticuleMulti
89
+
90
+ Single Geocode Geocoder:
91
+
92
+ <pre>
93
+ # Use Graticule Multi geocoder, which tries multiple geocode services in succession!
94
+ @geocoder = GeoMagic.geo_coder(:type => :geocode)
95
+ @geocoder.configure File.expand_path('../fixtures/map_api_keys.yaml', File.dirname(__FILE__)), :development
96
+ location = @geocoder.instance.geocode "Mullerstrasse 9, Munich"
97
+ location.city
98
+ </pre>
99
+
100
+ Single Graticule Geocoder:
77
101
 
78
- Lots of extra features has been added recently. For now, please check out the specs to get an idea of the features included.
79
- Most recently I have added support to filter a list of points for those who are within a radius of another point (see _select_neares_spec.rb_).
102
+ <pre>
103
+ # Use Graticule Multi geocoder, which tries multiple geocode services in succession!
104
+ @geocoder = GeoMagic.geo_coder(:type => graticule)
105
+ @geocoder.configure File.expand_path('../fixtures/map_api_keys.yaml', File.dirname(__FILE__)), :development
106
+ location = @geocoder.instance.geocode "Mullerstrasse 9, Munich"
107
+ location.city
108
+ </pre>
80
109
 
81
- h3. Geocoder adapters
110
+ Multi Graticule Geocoder:
82
111
 
83
- I have also just created a Geocode adapter for _Geocode_ and _Graticule_ (see: _geocoder_spec.rb_) - This can also easily be configured for Rails.
112
+ <pre>
113
+ # Use Graticule Multi geocoder, which tries multiple geocode services in succession!
114
+ @geocoder = GeoMagic.geo_coder(:type => graticule_multi)
115
+ ..
116
+ </pre>
84
117
 
85
- I recently discovered that the Geocode adapter didn't work for all addresses, so I made some improvements to make it more "stable".
86
- I'm sure there are still corner cases it wont handle, so please join in the effort to fix this!
118
+ Multi Graticule Customization:
87
119
 
88
- Also note that the API has changed a little bit, so that it uses the same GeoMagic namespace as the rest of _geo_magic_.
120
+ <pre>
121
+ # Use Graticule Multi geocoder, which tries multiple geocode services in succession!
122
+ @geocoder = GeoMagic.geo_coder(:type => graticule_multi)
123
+ location = @geocoder.instance(:timeout => 3) do |result|
124
+ [:address, :street].include?(result.precision)]
125
+ end
126
+ ..
127
+ </pre>
89
128
 
90
129
  h3. Create random points in Radius
91
130
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.3.1
1
+ 0.2.4
data/geo_magic.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{geo_magic}
8
- s.version = "0.2.3.1"
8
+ s.version = "0.2.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Kristian Mandrup"]
@@ -30,7 +30,10 @@ Gem::Specification.new do |s|
30
30
  "lib/geo_magic/calculate.rb",
31
31
  "lib/geo_magic/distance.rb",
32
32
  "lib/geo_magic/geocode/config.rb",
33
+ "lib/geo_magic/geocode/geo_adapter.rb",
34
+ "lib/geo_magic/geocode/geocode_adapter.rb",
33
35
  "lib/geo_magic/geocode/geocoder.rb",
36
+ "lib/geo_magic/geocode/graticule_adapter.rb",
34
37
  "lib/geo_magic/location.rb",
35
38
  "lib/geo_magic/map_point.rb",
36
39
  "lib/geo_magic/meta.rb",
@@ -10,14 +10,22 @@ module GeoMagic
10
10
  raise 'method #reverse_geocode should be implemented by adapter subclass'
11
11
  end
12
12
 
13
- def configure path, env = :development
13
+ def configure path, env = :development
14
14
  @config ||= ::YAML.load_file(path)[env.to_s]
15
15
  end
16
16
 
17
+ def self.services_available
18
+ [:google, :yahoo]
19
+ end
20
+
17
21
  protected
18
-
19
- def google_key
20
- config['google_key']
21
- end
22
+
23
+ services_available.each do |api|
24
+ class_eval %{
25
+ def #{api}_key
26
+ config['#{api}_key']
27
+ end
28
+ }
29
+ end
22
30
  end
23
31
  end
@@ -0,0 +1,38 @@
1
+ module GeoMagic
2
+ class GeoAdapter
3
+ attr_accessor :service_name, :environment
4
+
5
+ def initialize services = :google, env = :default
6
+ setup(env)
7
+ @service_name = get_service(services)
8
+ @environment = env
9
+ end
10
+
11
+ def get_service services
12
+ case services
13
+ when String, Symbol
14
+ services
15
+ when Array
16
+ services.first
17
+ else
18
+ raise ArgumentError, "service argument is not valid: #{services.inspect}"
19
+ end
20
+ end
21
+
22
+ def get_key service_name
23
+ method = "#{service_name}_key"
24
+ raise ArgumentError, "Invalid map service: #{service_name}, must be one of #{GeoMagic::ServiceAdapter.services_available}" if !respond_to? method
25
+ send method
26
+ end
27
+
28
+ def setup env
29
+ case env
30
+ when :rails
31
+ require 'rails/config'
32
+ self.class.send(:include, RailsServiceAdapter)
33
+ else
34
+ self.class.send(:include, ServiceAdapter)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,124 @@
1
+ require 'geocode'
2
+
3
+ module GeoMagic
4
+ class GeocodeAdapter < GeoAdapter
5
+ def initialize service_name = :google, env = :default
6
+ super
7
+ end
8
+
9
+ def instance
10
+ @geo_coder ||= ::Geocode.new_geocoder service_name, {:google_api_key => google_key}
11
+ self
12
+ end
13
+
14
+ def geocode location_str
15
+ geo_coder.geocode(location_str).extend GeocodeAPI
16
+ end
17
+
18
+ module GeocodeAPI
19
+ # Address
20
+
21
+ def street
22
+ thoroughfare["ThoroughfareName"] ? thoroughfare["ThoroughfareName"] : ""
23
+ end
24
+
25
+ def postal_code
26
+ locality["PostalCode"] ? locality["PostalCode"]["PostalCodeNumber"] : ""
27
+ end
28
+ alias_method :zip, :postal_code
29
+
30
+ def city
31
+ subadm_api["SubAdministrativeAreaName"] ? subadm_api["SubAdministrativeAreaName"] : ""
32
+ end
33
+
34
+ def state
35
+ adm_api["AdministrativeAreaName"] ? adm_api["AdministrativeAreaName"] : ""
36
+ end
37
+
38
+ def country_code
39
+ country_api["CountryNameCode"] ? country_api["CountryNameCode"] : ""
40
+ end
41
+
42
+ def country_name
43
+ country_api["CountryName"] ? country_api["CountryName"] : ""
44
+ end
45
+ alias_method :country, :country_name
46
+
47
+ def address_hash
48
+ {:street => street, :postal_code => postal_code, :city => city, :state => state, :country => country, :country_code => country_code}
49
+ end
50
+
51
+ # Location
52
+
53
+ def latitude
54
+ coords[1]
55
+ end
56
+
57
+ def longitude
58
+ coords[0]
59
+ end
60
+
61
+ def location_hash
62
+ {:longitude => longitude, :latitude => latitude}
63
+ end
64
+
65
+ protected
66
+
67
+ def thoroughfare
68
+ @thoroughfare ||= begin
69
+ thorough = [:subadm_api, :locality_api, :dependent_locality_api].select do |api|
70
+ x = send(api)
71
+ x ? x["Thoroughfare"] : nil
72
+ end
73
+ thorough.empty? ? {} : send(thorough.first)["Thoroughfare"]
74
+ end
75
+ end
76
+
77
+ def locality
78
+ @locality ||= begin
79
+ loc = [:locality_api, :dependent_locality_api].select do |api|
80
+ x = send(api)
81
+ x ? x["PostalCode"] || x["LocalityName"] : nil
82
+ end
83
+ loc.empty? ? {} : send(loc.first)
84
+ end
85
+ end
86
+
87
+ def api
88
+ data["Placemark"].first
89
+ end
90
+
91
+ def coords
92
+ api["Point"]["coordinates"]
93
+ end
94
+
95
+ def adr_api
96
+ api["AddressDetails"]
97
+ end
98
+
99
+ def country_api
100
+ adr_api["Country"]
101
+ end
102
+
103
+ def adm_api
104
+ country_api["AdministrativeArea"]
105
+ end
106
+
107
+ def subadm_api
108
+ adm_api["SubAdministrativeArea"]
109
+ end
110
+
111
+ def locality_api
112
+ subadm_api["Locality"]
113
+ end
114
+
115
+ def dependent_locality_api
116
+ locality_api["DependentLocality"]
117
+ end
118
+ end
119
+
120
+ def reverse_geocode latitude, longitude
121
+ geo_coder.reverse_geocode "#{latitude}, #{longitude}"
122
+ end
123
+ end
124
+ end
@@ -1,172 +1,22 @@
1
1
  require 'geo_magic/geocode/config'
2
2
  require 'active_support/inflector'
3
- require 'geocode'
4
- require 'graticule'
5
-
6
- module GeoMagic
7
- class GeoAdapter
8
- attr_accessor :service_name, :environment
9
-
10
- def initialize service_name = :google, env = :default
11
- setup(env)
12
- @service_name = service_name
13
- @environment = env
14
- end
15
-
16
- def setup env
17
- case env
18
- when :rails
19
- require 'rails/config'
20
- self.class.send(:include, RailsServiceAdapter)
21
- else
22
- self.class.send(:include, ServiceAdapter)
23
- end
24
- end
25
- end
26
-
27
- class GraticuleAdapter < GeoAdapter
28
- def initialize service_name = :google, env = :default
29
- super
30
- end
31
-
32
- def instance
33
- @geo_coder ||= ::Graticule.service(service_name).new google_key
34
- self
35
- end
36
-
37
- def geocode location_str
38
- geo_coder.locate location_str
39
- end
40
- end
41
-
42
- class GeocodeAdapter < GeoAdapter
43
- def initialize service_name = :google, env = :default
44
- super
45
- end
46
-
47
- def instance
48
- @geo_coder ||= ::Geocode.new_geocoder service_name, {:google_api_key => google_key}
49
- self
50
- end
51
-
52
- def geocode location_str
53
- geo_coder.geocode(location_str).extend GeocodeAPI
54
- end
55
-
56
- module GeocodeAPI
57
- # Address
58
-
59
- def street
60
- thoroughfare["ThoroughfareName"] ? thoroughfare["ThoroughfareName"] : ""
61
- end
62
-
63
- def postal_code
64
- locality["PostalCode"] ? locality["PostalCode"]["PostalCodeNumber"] : ""
65
- end
66
- alias_method :zip, :postal_code
67
-
68
- def city
69
- subadm_api["SubAdministrativeAreaName"] ? subadm_api["SubAdministrativeAreaName"] : ""
70
- end
71
-
72
- def state
73
- adm_api["AdministrativeAreaName"] ? adm_api["AdministrativeAreaName"] : ""
74
- end
75
-
76
- def country_code
77
- country_api["CountryNameCode"] ? country_api["CountryNameCode"] : ""
78
- end
79
-
80
- def country_name
81
- country_api["CountryName"] ? country_api["CountryName"] : ""
82
- end
83
- alias_method :country, :country_name
84
-
85
- def address_hash
86
- {:street => street, :postal_code => postal_code, :city => city, :state => state, :country => country, :country_code => country_code}
87
- end
88
-
89
- # Location
90
-
91
- def latitude
92
- coords[1]
93
- end
94
-
95
- def longitude
96
- coords[0]
97
- end
98
-
99
- def location_hash
100
- {:longitude => longitude, :latitude => latitude}
101
- end
102
-
103
- protected
104
-
105
- def thoroughfare
106
- @thoroughfare ||= begin
107
- thorough = [:subadm_api, :locality_api, :dependent_locality_api].select do |api|
108
- x = send(api)
109
- x ? x["Thoroughfare"] : nil
110
- end
111
- thorough.empty? ? {} : send(thorough.first)["Thoroughfare"]
112
- end
113
- end
114
-
115
- def locality
116
- @locality ||= begin
117
- loc = [:locality_api, :dependent_locality_api].select do |api|
118
- x = send(api)
119
- x ? x["PostalCode"] || x["LocalityName"] : nil
120
- end
121
- loc.empty? ? {} : send(loc.first)
122
- end
123
- end
124
-
125
- def api
126
- data["Placemark"].first
127
- end
128
-
129
- def coords
130
- api["Point"]["coordinates"]
131
- end
132
-
133
- def adr_api
134
- api["AddressDetails"]
135
- end
136
-
137
- def country_api
138
- adr_api["Country"]
139
- end
140
-
141
- def adm_api
142
- country_api["AdministrativeArea"]
143
- end
144
-
145
- def subadm_api
146
- adm_api["SubAdministrativeArea"]
147
- end
148
-
149
- def locality_api
150
- subadm_api["Locality"]
151
- end
152
-
153
- def dependent_locality_api
154
- locality_api["DependentLocality"]
155
- end
156
- end
157
-
158
- def reverse_geocode latitude, longitude
159
- geo_coder.reverse_geocode "#{latitude}, #{longitude}"
160
- end
161
- end
162
3
 
4
+ require 'geo_magic/geocode/geo_adapter'
5
+ require 'geo_magic/geocode/graticule_adapter'
6
+ require 'geo_magic/geocode/geocode_adapter'
163
7
 
8
+ module GeoMagic
164
9
  class << self
165
- def geo_coder options = {:type => :geocode, :service_name => :google}
10
+ def geo_coder options = {:type => :geocode, :service => :google}
166
11
  service_name = options[:service_name] || :google
167
12
  type = options[:type] || :geocode
168
13
  env = options[:env]
169
- "GeoMagic::#{type.to_s.classify}Adapter".constantize.new service_name, env
14
+
15
+ services = options[:services] || options[:service] || :google
16
+
17
+ clazz = "GeoMagic::#{type.to_s.classify}Adapter".constantize
18
+
19
+ clazz.new services, env
170
20
  end
171
21
 
172
22
  def geocode location_str
@@ -0,0 +1,79 @@
1
+ require 'graticule'
2
+
3
+ module GeoMagic
4
+ class GraticuleAdapter < GeoAdapter
5
+ def initialize services = :google, env = :default
6
+ super
7
+ end
8
+
9
+ def instance
10
+ @geo_coder ||= create_graticule_service
11
+ self
12
+ end
13
+
14
+ def create_graticule_service key_name = nil
15
+ api_key = get_key(key_name || service_name)
16
+ gs_service.new api_key
17
+ end
18
+
19
+ def geocode location_str
20
+ geo_coder.locate location_str
21
+ end
22
+
23
+ def gs_service
24
+ ::Graticule.service(service_name)
25
+ end
26
+ end
27
+
28
+ # The Multi geocoder will try the geocoders in order if a Graticule::AddressError
29
+ # is raised. You can customize this behavior by passing in a block to the Multi
30
+ # geocoder. For example, to try the geocoders until one returns a result with a
31
+ # high enough precision:
32
+ #
33
+ # geocoder = Graticule.service(:multi).new(geocoders) do |result|
34
+ # [:address, :street].include?(result.precision)
35
+ # end
36
+ #
37
+ # Geocoders will be tried in order until the block returned true for one of the results
38
+ #
39
+ # Use the :timeout option to specify the number of seconds to allow for each
40
+ # geocoder before raising a Timout::Error (defaults to 10 seconds).
41
+ #
42
+ # Graticule.service(:multi).new(geocoders, :timeout => 3)
43
+
44
+ class GraticuleMultiAdapter < GraticuleAdapter
45
+ attr_accessor :map_services, :geo_coder
46
+
47
+ def initialize services, env = :default
48
+ super(:multi, env)
49
+ @map_services = [services].compact.uniq.flatten
50
+ end
51
+
52
+ def instance options = {}, &block
53
+ @geo_coder ||= begin
54
+ if block
55
+ gs_service.new multi_services, options do |result|
56
+ yield
57
+ end
58
+ else
59
+ gs_service.new multi_services, options do |result|
60
+ true
61
+ end
62
+ end
63
+ end
64
+ self
65
+ end
66
+
67
+ def geocode location_str
68
+ geo_coder.locate location_str
69
+ end
70
+
71
+ def multi_services
72
+ map_services.map do |service_name|
73
+ api_key = send :"#{service_name}_key"
74
+ raise ArgumentError, "API key for service #{service_name} has not been set. Please insert key in your api keys configuration file" if api_key.blank?
75
+ Graticule.service(service_name.to_sym).new(api_key)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -24,6 +24,23 @@ describe "GeoMagic Geocoder" do
24
24
  p location.address_hash
25
25
  end
26
26
 
27
+ it "Graticule adapter" do
28
+ @geocoder = GeoMagic.geo_coder :type => :graticule, :services => :google
29
+ @geocoder.configure File.expand_path('../fixtures/map_api_keys.yaml', File.dirname(__FILE__)), :development
30
+ location = @geocoder.instance.geocode "Pilotystrasse 11, munich, Germany"
31
+ p location
32
+ p location.city
33
+ end
34
+
35
+ it "Graticule Multi adapter" do
36
+ @geocoder = GeoMagic.geo_coder :type => :graticule_multi, :services => :google
37
+ @geocoder.configure File.expand_path('../fixtures/map_api_keys.yaml', File.dirname(__FILE__)), :development
38
+ location = @geocoder.instance.geocode "Pilotystrasse 11, munich, Germany"
39
+ p location
40
+ p location.city
41
+ end
42
+
43
+
27
44
  # Geocoder with Rails 3
28
45
 
29
46
  # Expects map api keys (fx for google maps) to be defined in ROOT/config/map_api_keys.yml
metadata CHANGED
@@ -5,9 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 3
9
- - 1
10
- version: 0.2.3.1
8
+ - 4
9
+ version: 0.2.4
11
10
  platform: ruby
12
11
  authors:
13
12
  - Kristian Mandrup
@@ -159,7 +158,10 @@ files:
159
158
  - lib/geo_magic/calculate.rb
160
159
  - lib/geo_magic/distance.rb
161
160
  - lib/geo_magic/geocode/config.rb
161
+ - lib/geo_magic/geocode/geo_adapter.rb
162
+ - lib/geo_magic/geocode/geocode_adapter.rb
162
163
  - lib/geo_magic/geocode/geocoder.rb
164
+ - lib/geo_magic/geocode/graticule_adapter.rb
163
165
  - lib/geo_magic/location.rb
164
166
  - lib/geo_magic/map_point.rb
165
167
  - lib/geo_magic/meta.rb
@@ -193,7 +195,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
193
195
  requirements:
194
196
  - - ">="
195
197
  - !ruby/object:Gem::Version
196
- hash: 3152749513202391524
198
+ hash: 1749247139327050469
197
199
  segments:
198
200
  - 0
199
201
  version: "0"