geo_magic 0.2.3.1 → 0.2.4

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.
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"