barometer 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.
Files changed (61) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +266 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/barometer +63 -0
  5. data/lib/barometer.rb +52 -0
  6. data/lib/barometer/base.rb +52 -0
  7. data/lib/barometer/data.rb +15 -0
  8. data/lib/barometer/data/current.rb +93 -0
  9. data/lib/barometer/data/distance.rb +131 -0
  10. data/lib/barometer/data/forecast.rb +66 -0
  11. data/lib/barometer/data/geo.rb +98 -0
  12. data/lib/barometer/data/location.rb +20 -0
  13. data/lib/barometer/data/measurement.rb +161 -0
  14. data/lib/barometer/data/pressure.rb +133 -0
  15. data/lib/barometer/data/speed.rb +147 -0
  16. data/lib/barometer/data/sun.rb +35 -0
  17. data/lib/barometer/data/temperature.rb +164 -0
  18. data/lib/barometer/data/units.rb +55 -0
  19. data/lib/barometer/data/zone.rb +124 -0
  20. data/lib/barometer/extensions/graticule.rb +50 -0
  21. data/lib/barometer/extensions/httparty.rb +21 -0
  22. data/lib/barometer/query.rb +228 -0
  23. data/lib/barometer/services.rb +6 -0
  24. data/lib/barometer/services/google.rb +146 -0
  25. data/lib/barometer/services/noaa.rb +6 -0
  26. data/lib/barometer/services/service.rb +324 -0
  27. data/lib/barometer/services/weather_bug.rb +6 -0
  28. data/lib/barometer/services/weather_dot_com.rb +6 -0
  29. data/lib/barometer/services/wunderground.rb +285 -0
  30. data/lib/barometer/services/yahoo.rb +274 -0
  31. data/lib/barometer/weather.rb +187 -0
  32. data/spec/barometer_spec.rb +162 -0
  33. data/spec/data_current_spec.rb +225 -0
  34. data/spec/data_distance_spec.rb +336 -0
  35. data/spec/data_forecast_spec.rb +150 -0
  36. data/spec/data_geo_spec.rb +90 -0
  37. data/spec/data_location_spec.rb +59 -0
  38. data/spec/data_measurement_spec.rb +411 -0
  39. data/spec/data_pressure_spec.rb +336 -0
  40. data/spec/data_speed_spec.rb +374 -0
  41. data/spec/data_sun_spec.rb +76 -0
  42. data/spec/data_temperature_spec.rb +396 -0
  43. data/spec/data_zone_spec.rb +133 -0
  44. data/spec/fixtures/current_calgary_ab.xml +1 -0
  45. data/spec/fixtures/forecast_calgary_ab.xml +1 -0
  46. data/spec/fixtures/geocode_40_73.xml +1 -0
  47. data/spec/fixtures/geocode_90210.xml +1 -0
  48. data/spec/fixtures/geocode_T5B4M9.xml +1 -0
  49. data/spec/fixtures/geocode_calgary_ab.xml +1 -0
  50. data/spec/fixtures/geocode_newyork_ny.xml +1 -0
  51. data/spec/fixtures/google_calgary_ab.xml +1 -0
  52. data/spec/fixtures/yahoo_90210.xml +1 -0
  53. data/spec/query_spec.rb +469 -0
  54. data/spec/service_google_spec.rb +144 -0
  55. data/spec/service_wunderground_spec.rb +330 -0
  56. data/spec/service_yahoo_spec.rb +299 -0
  57. data/spec/services_spec.rb +1106 -0
  58. data/spec/spec_helper.rb +14 -0
  59. data/spec/units_spec.rb +101 -0
  60. data/spec/weather_spec.rb +265 -0
  61. metadata +119 -0
@@ -0,0 +1,55 @@
1
+ module Barometer
2
+ class Units
3
+ include Comparable
4
+
5
+ attr_accessor :metric
6
+
7
+ def initialize(metric=true)
8
+ @metric = metric
9
+ end
10
+
11
+ #
12
+ # HELPERS
13
+ #
14
+
15
+ def metric?; @metric; end
16
+ def metric!; @metric=true; end
17
+ def imperial!; @metric=false; end
18
+
19
+ # assigns a value to the right attribute based on metric setting
20
+ def <<(value)
21
+ return unless value
22
+
23
+ begin
24
+ if value.is_a?(Array)
25
+ value_m = value[0].to_f
26
+ value_i = value[1].to_f
27
+ value_b = nil
28
+ else
29
+ value_m = nil
30
+ value_i = nil
31
+ value_b = value.to_f
32
+ end
33
+ rescue
34
+ # do nothing
35
+ end
36
+
37
+ if self.metric?
38
+ self.metric_default = value_m || value_b
39
+ else
40
+ self.imperial_default = value_i || value_b
41
+ end
42
+ end
43
+
44
+ # STUB: define this method to actually retireve the metric_default
45
+ def metric_default=(value)
46
+ raise NotImplementedError
47
+ end
48
+
49
+ # STUB: define this method to actually retireve the imperial_default
50
+ def imperial_default=(value)
51
+ raise NotImplementedError
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,124 @@
1
+ require 'rubygems'
2
+ require 'tzinfo'
3
+
4
+ module Barometer
5
+ #
6
+ # A simple Zone class
7
+ #
8
+ # Used for building and converting timezone aware date and times
9
+ # Really, these are just wrappers for TZInfo conversions.
10
+ #
11
+ class Zone
12
+
13
+ attr_accessor :timezone, :tz
14
+
15
+ def initialize(timezone)
16
+ @timezone = timezone
17
+ @tz = TZInfo::Timezone.get(timezone)
18
+ end
19
+
20
+ # what is the Timezone Short Code for the current timezone
21
+ def code
22
+ return "" unless @tz
23
+ @tz.period_for_utc(Time.now.utc).zone_identifier.to_s
24
+ end
25
+
26
+ # is the current timezone in daylights savings mode?
27
+ def dst?
28
+ return nil unless @tz
29
+ @tz.period_for_utc(Time.now.utc).dst?
30
+ end
31
+
32
+ # return Time.now.utc for the set timezone
33
+ def now
34
+ Barometer::Zone.now(@timezone)
35
+ end
36
+
37
+ # return Date.today for the set timezone
38
+ def today
39
+ Barometer::Zone.today(@timezone)
40
+ end
41
+
42
+ def local_to_utc(local_time)
43
+ @tz.local_to_utc(local_time)
44
+ end
45
+
46
+ def utc_to_local(utc_time)
47
+ @tz.utc_to_local(utc_time)
48
+ end
49
+
50
+ #
51
+ # Class Methods
52
+ #
53
+
54
+ # return the local current time, providing a timezone
55
+ # (ie 'Europe/Paris') will give the local time for the
56
+ # timezone, otherwise it will be Time.now
57
+ def self.now(timezone=nil)
58
+ if timezone
59
+ utc = Time.now.utc
60
+ tz = TZInfo::Timezone.get(timezone)
61
+ tz.utc_to_local(utc)
62
+ else
63
+ Time.now
64
+ end
65
+ end
66
+
67
+ # return the local current date, providing a timezone
68
+ # (ie 'Europe/Paris') will give the local date for the
69
+ # timezone, otherwise it will be Date.today
70
+ def self.today(timezone=nil)
71
+ if timezone
72
+ utc = Time.now.utc
73
+ tz = TZInfo::Timezone.get(timezone)
74
+ now = tz.utc_to_local(utc)
75
+ Date.new(now.year, now.month, now.day)
76
+ else
77
+ Date.today
78
+ end
79
+ end
80
+
81
+ # takes a time (any timezone), and a TimeZone Short Code (ie PST) and
82
+ # converts the time to UTC accorsing to that time_zone
83
+ # NOTE: No Tests
84
+ def self.code_to_utc(time, timezone_code)
85
+ raise ArgumentError unless time.is_a?(Time)
86
+ offset = Time.zone_offset(timezone_code) || 0
87
+
88
+ Time.utc(
89
+ time.year, time.month, time.day,
90
+ time.hour, time.min, time.sec, time.usec
91
+ ) - offset
92
+ end
93
+
94
+ # takes a string with TIME only information and merges it with a string that
95
+ # has DATE only information and creates a UTC TIME object with time and date
96
+ # info. if you supply the timezone code (ie PST), it will apply the timezone
97
+ # offset to the final time
98
+ def self.merge(time, date, timezone_code=nil)
99
+ raise ArgumentError unless (time.is_a?(Time) || time.is_a?(String))
100
+ raise ArgumentError unless (date.is_a?(Time) || date.is_a?(Date) || date.is_a?(String))
101
+
102
+ if time.is_a?(String)
103
+ reference_time = Time.parse(time)
104
+ elsif time.is_a?(Time)
105
+ reference_time = time
106
+ end
107
+
108
+ if date.is_a?(String)
109
+ reference_date = Date.parse(date)
110
+ elsif date.is_a?(Time)
111
+ reference_date = Date.new(date.year, date.month, date.day)
112
+ elsif date.is_a?(Date)
113
+ reference_date = date
114
+ end
115
+
116
+ new_time = Time.utc(
117
+ reference_date.year, reference_date.month, reference_date.day,
118
+ reference_time.hour, reference_time.min, reference_time.sec
119
+ )
120
+ timezone_code ? Barometer::Zone.code_to_utc(new_time,timezone_code) : new_time
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,50 @@
1
+ module Graticule
2
+ class Location
3
+
4
+ attr_accessor :country_code
5
+
6
+ def attributes
7
+ [:latitude, :longitude, :street, :locality, :region, :postal_code, :country, :precision, :cuntry_code].inject({}) do |result,attr|
8
+ result[attr] = self.send(attr) unless self.send(attr).blank?
9
+ result
10
+ end
11
+ end
12
+
13
+ end
14
+
15
+ module Geocoder
16
+
17
+ class Google < Rest
18
+
19
+ # Locates +address+ returning a Location
20
+ # add ability to bias towards a country
21
+ def locate(address, country_bias=nil)
22
+ get :q => (address.is_a?(String) ? address : location_from_params(address).to_s),
23
+ :gl => country_bias
24
+ end
25
+
26
+ private
27
+
28
+ # Extracts a Location from +xml+.
29
+ def parse_response(xml) #:nodoc:
30
+ longitude, latitude, = xml.elements['/kml/Response/Placemark/Point/coordinates'].text.split(',').map { |v| v.to_f }
31
+ returning Location.new(:latitude => latitude, :longitude => longitude) do |l|
32
+ address = REXML::XPath.first(xml, '//xal:AddressDetails',
33
+ 'xal' => "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0")
34
+
35
+ if address
36
+ l.street = value(address.elements['.//ThoroughfareName/text()'])
37
+ l.locality = value(address.elements['.//LocalityName/text()'])
38
+ l.region = value(address.elements['.//AdministrativeAreaName/text()'])
39
+ l.postal_code = value(address.elements['.//PostalCodeNumber/text()'])
40
+ l.country = value(address.elements['.//CountryName/text()'])
41
+ l.country_code = value(address.elements['.//CountryNameCode/text()'])
42
+ l.precision = PRECISION[address.attribute('Accuracy').value.to_i] || :unknown
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # extends HTTParty by adding configurable timeout support
3
+ #
4
+ module HTTParty
5
+ class Request
6
+
7
+ private
8
+
9
+ def http
10
+ http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport])
11
+ http.use_ssl = (uri.port == 443)
12
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
13
+ if options[:timeout] && options[:timeout].is_a?(Integer)
14
+ http.open_timeout = options[:timeout]
15
+ http.read_timeout = options[:timeout]
16
+ end
17
+ http
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,228 @@
1
+ module Barometer
2
+ #
3
+ # This class represents a query and can answer the
4
+ # questions that a Barometer will need to measure the weather
5
+ #
6
+ # Summary:
7
+ # When you create a new Query, you set the query string
8
+ # ie: "New York, NY" or "90210"
9
+ # The class will then determine the query string format
10
+ # ie: :zipcode, :postalcode, :geocode, :coordinates
11
+ # Now, when a Weather API driver asks for the query, it will prefer
12
+ # certain formats, and only permit certain formats. The Query class
13
+ # will attempt to either return the query string as-is if acceptable,
14
+ # or it will attempt to convert it to a format that is acceptable
15
+ # (most likely this conversion will in Googles geocoding service using
16
+ # the Graticule gem). Worst case scenario is that the Weather API will
17
+ # not accept the query string.
18
+ #
19
+ class Query
20
+
21
+ # OPTIONAL: key required by Google for geocoding
22
+ @@google_geocode_key = nil
23
+ def self.google_geocode_key; @@google_geocode_key || Barometer.google_geocode_key; end;
24
+ def self.google_geocode_key=(key); @@google_geocode_key = key; end;
25
+
26
+ attr_reader :format
27
+ attr_accessor :q, :preferred, :country_code, :geo
28
+
29
+ def initialize(query=nil)
30
+ @q = query
31
+ self.analyze!
32
+ end
33
+
34
+ # analyze the saved query to determine the format. for the format of
35
+ # :zipcode and :postalcode the country_code can also be set
36
+ def analyze!
37
+ return unless @q
38
+ if Barometer::Query.is_us_zipcode?(@q)
39
+ @format = :zipcode
40
+ elsif Barometer::Query.is_canadian_postcode?(@q)
41
+ @format = :postalcode
42
+ elsif Barometer::Query.is_coordinates?(@q)
43
+ @format = :coordinates
44
+ else
45
+ @format = :geocode
46
+ end
47
+ @country_code = Barometer::Query.format_to_country_code(@format)
48
+ end
49
+
50
+ # take a list of acceptable (and ordered by preference) formats and convert
51
+ # the current query (q) into the most preferred and acceptable format. as a
52
+ # side effect of some conversions, the country_code might be known, then save it
53
+ def convert!(preferred_formats=nil)
54
+ raise ArgumentError unless (preferred_formats && preferred_formats.size > 0)
55
+ # reset preferred
56
+ @preferred = nil
57
+
58
+ # go through each acceptable format and try to convert to that
59
+ converted = false
60
+ geocoded = false
61
+ preferred_formats.each do |preferred_format|
62
+ # we are already in this format, return this
63
+ if preferred_format == @format
64
+ converted = true
65
+ @preferred ||= @q
66
+ end
67
+
68
+ unless converted
69
+ case preferred_format
70
+ when :coordinates
71
+ geocoded = true
72
+ @preferred, @country_code, @geo = Barometer::Query.to_coordinates(@q, @format)
73
+ when :geocode
74
+ geocoded = true
75
+ @preferred, @country_code, @geo = Barometer::Query.to_geocode(@q, @format)
76
+ end
77
+ end
78
+ end
79
+
80
+ # if we haven't already geocoded and we are forcing it, do it now
81
+ if !geocoded && Barometer.force_geocode
82
+ not_used_coords, not_used_code, @geo = Barometer::Query.to_coordinates(@q, @format)
83
+ end
84
+
85
+ @preferred
86
+ end
87
+
88
+ #
89
+ # HELPERS
90
+ #
91
+
92
+ def zipcode?; @format == :zipcode; end
93
+ def postalcode?; @format == :postalcode; end
94
+ def coordinates?; @format == :coordinates; end
95
+ def geocode?; @format == :geocode; end
96
+
97
+ def self.is_us_zipcode?(query)
98
+ us_zipcode_regex = /(^[0-9]{5}$)|(^[0-9]{5}-[0-9]{4}$)/
99
+ return !(query =~ us_zipcode_regex).nil?
100
+ end
101
+
102
+ def self.is_canadian_postcode?(query)
103
+ # Rules: no D, F, I, O, Q, or U anywhere
104
+ # Basic validation: ^[ABCEGHJ-NPRSTVXY]{1}[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[ ]?[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[0-9]{1}$
105
+ # Extended validation: ^(A(0[ABCEGHJ-NPR]|1[ABCEGHK-NSV-Y]|2[ABHNV]|5[A]|8[A])|B(0[CEHJ-NPRSTVW]|1[ABCEGHJ-NPRSTV-Y]|2[ABCEGHJNRSTV-Z]|3[ABEGHJ-NPRSTVZ]|4[ABCEGHNPRV]|5[A]|6[L]|9[A])|C(0[AB]|1[ABCEN])|E(1[ABCEGHJNVWX]|2[AEGHJ-NPRSV]|3[ABCELNVYZ]|4[ABCEGHJ-NPRSTV-Z]|5[ABCEGHJ-NPRSTV]|6[ABCEGHJKL]|7[ABCEGHJ-NP]|8[ABCEGJ-NPRST]|9[ABCEGH])|G(0[ACEGHJ-NPRSTV-Z]|1[ABCEGHJ-NPRSTV-Y]|2[ABCEGJ-N]|3[ABCEGHJ-NZ]|4[ARSTVWXZ]|5[ABCHJLMNRTVXYZ]|6[ABCEGHJKLPRSTVWXZ]|7[ABGHJKNPSTXYZ]|8[ABCEGHJ-NPTVWYZ]|9[ABCHNPRTX])|H(0[HM]|1[ABCEGHJ-NPRSTV-Z]|2[ABCEGHJ-NPRSTV-Z]|3[ABCEGHJ-NPRSTV-Z]|4[ABCEGHJ-NPRSTV-Z]|5[AB]|7[ABCEGHJ-NPRSTV-Y]|8[NPRSTYZ]|9[ABCEGHJKPRSWX])|J(0[ABCEGHJ-NPRSTV-Z]|1[ACEGHJ-NRSTXZ]|2[ABCEGHJ-NRSTWXY]|3[ABEGHLMNPRTVXYZ]|4[BGHJ-NPRSTV-Z]|5[ABCJ-MRTV-Z]|6[AEJKNRSTVWYXZ]|7[ABCEGHJ-NPRTV-Z]|8[ABCEGHLMNPRTVXYZ]|9[ABEHJLNTVXYZ])|K(0[ABCEGHJ-M]|1[ABCEGHJ-NPRSTV-Z]|2[ABCEGHJ-MPRSTVW]|4[ABCKMPR]|6[AHJKTV]|7[ACGHK-NPRSV]|8[ABHNPRV]|9[AHJKLV])|L(0[[ABCEGHJ-NPRS]]|1[ABCEGHJ-NPRSTV-Z]|2[AEGHJMNPRSTVW]|3[BCKMPRSTVXYZ]|4[ABCEGHJ-NPRSTV-Z]|5[ABCEGHJ-NPRSTVW]|6[ABCEGHJ-MPRSTV-Z]|7[ABCEGJ-NPRST]|8[EGHJ-NPRSTVW]|9[ABCGHK-NPRSTVWYZ])|M(1[BCEGHJ-NPRSTVWX]|2[HJ-NPR]|3[ABCHJ-N]|4[ABCEGHJ-NPRSTV-Y]|5[ABCEGHJ-NPRSTVWX]|6[ABCEGHJ-NPRS]|7[AY]|8[V-Z]|9[ABCLMNPRVW])|N(0[ABCEGHJ-NPR]|1[ACEGHKLMPRST]|2[ABCEGHJ-NPRTVZ]|3[ABCEHLPRSTVWY]|4[BGKLNSTVWXZ]|5[ACHLPRV-Z]|6[ABCEGHJ-NP]|7[AGLMSTVWX]|8[AHMNPRSTV-Y]|9[ABCEGHJKVY])|P(0[ABCEGHJ-NPRSTV-Y]|1[ABCHLP]|2[ABN]|3[ABCEGLNPY]|4[NPR]|5[AEN]|6[ABC]|7[ABCEGJKL]|8[NT]|9[AN])|R(0[ABCEGHJ-M]|1[ABN]|2[CEGHJ-NPRV-Y]|3[ABCEGHJ-NPRSTV-Y]|4[AHJKL]|5[AGH]|6[MW]|7[ABCN]|8[AN]|9[A])|S(0[ACEGHJ-NP]|2[V]|3[N]|4[AHLNPRSTV-Z]|6[HJKVWX]|7[HJ-NPRSTVW]|9[AHVX])|T(0[ABCEGHJ-MPV]|1[ABCGHJ-MPRSV-Y]|2[ABCEGHJ-NPRSTV-Z]|3[ABCEGHJ-NPRZ]|4[ABCEGHJLNPRSTVX]|5[ABCEGHJ-NPRSTV-Z]|6[ABCEGHJ-NPRSTVWX]|7[AENPSVXYZ]|8[ABCEGHLNRSVWX]|9[ACEGHJKMNSVWX])|V(0[ABCEGHJ-NPRSTVWX]|1[ABCEGHJ-NPRSTV-Z]|2[ABCEGHJ-NPRSTV-Z]|3[ABCEGHJ-NRSTV-Y]|4[ABCEGK-NPRSTVWXZ]|5[ABCEGHJ-NPRSTV-Z]|6[ABCEGHJ-NPRSTV-Z]|7[ABCEGHJ-NPRSTV-Y]|8[ABCGJ-NPRSTV-Z]|9[ABCEGHJ-NPRSTV-Z])|X(0[ABCGX]|1[A])|Y(0[AB]|1[A]))[ ]?[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[0-9]{1}$
106
+ ca_postcode_regex = /^[A-Z]{1}[\d]{1}[A-Z]{1}[ ]?[\d]{1}[A-Z]{1}[\d]{1}$/
107
+ return !(query =~ ca_postcode_regex).nil?
108
+ end
109
+
110
+ def self.is_coordinates?(query)
111
+ coordinates_regex = /^[-]?[0-9\.]+[,]{1}[-]?[0-9\.]+$/
112
+ return !(query =~ coordinates_regex).nil?
113
+ end
114
+
115
+ #
116
+ # CONVERTERS
117
+ #
118
+
119
+ # this will take all query formats and convert them to coordinates
120
+ # accepts- :zipcode, :postalcode, :geocode
121
+ # returns- :coordinates
122
+ # if the conversion fails, return nil
123
+ def self.to_coordinates(query, format)
124
+ country_code = self.format_to_country_code(format)
125
+ geo = self.geocode(query, country_code)
126
+ country_code ||= geo.country_code if geo
127
+ return nil unless geo && geo.longitude && geo.latitude
128
+ ["#{geo.latitude},#{geo.longitude}", country_code, geo]
129
+ end
130
+
131
+ # this will take all query formats and convert them to coorinates
132
+ # accepts- :zipcode, :postalcode, :coordinates
133
+ # returns- :geocode
134
+ def self.to_geocode(query, format)
135
+ perform_geocode = false
136
+ perform_geocode = true if self.has_geocode_key?
137
+
138
+ # some formats can't convert, no need to geocode then
139
+ skip_formats = [:postalcode]
140
+ perform_geocode = false if skip_formats.include?(format)
141
+
142
+ country_code = self.format_to_country_code(format)
143
+ if perform_geocode
144
+ geo = self.geocode(query, country_code)
145
+ country_code ||= geo.country_code if geo
146
+ return nil unless geo && geo.locality && geo.region && geo.country
147
+ return ["#{geo.locality}, #{geo.region}, #{geo.country}", country_code, geo]
148
+ else
149
+ # without geocoding, the best we can do is just make use the given query as
150
+ # the query for the "geocode" format
151
+ return [query, country_code, nil]
152
+ end
153
+ return nil
154
+ end
155
+
156
+ #
157
+ # --- TODO ---
158
+ # The following methods need more coverage tests
159
+ #
160
+
161
+ def self.has_geocode_key?
162
+ # quick check to see that the Google API key exists for geocoding
163
+ self.google_geocode_key && !self.google_geocode_key.nil?
164
+ end
165
+
166
+ # if Graticule exists, use it, otherwise use HTTParty
167
+ def self.geocode(query, country_code=nil)
168
+ use_graticule = false
169
+ unless Barometer::skip_graticule
170
+ begin
171
+ require 'rubygems'
172
+ require 'graticule'
173
+ $:.unshift(File.dirname(__FILE__))
174
+ # load some changes to Graticule
175
+ # TODO: attempt to get changes into Graticule gem
176
+ require 'extensions/graticule'
177
+ use_graticule = true
178
+ rescue LoadError
179
+ # do nothing, we will use HTTParty
180
+ end
181
+ end
182
+
183
+ if use_graticule
184
+ geo = self.geocode_graticule(query, country_code)
185
+ else
186
+ geo = self.geocode_httparty(query, country_code)
187
+ end
188
+ geo
189
+ end
190
+
191
+ def self.geocode_graticule(query, country_code=nil)
192
+ return nil unless self.has_geocode_key?
193
+ geocoder = Graticule.service(:google).new(self.google_geocode_key)
194
+ location = geocoder.locate(query, country_code)
195
+ geo = Barometer::Geo.new(location)
196
+ end
197
+
198
+ def self.geocode_httparty(query, country_code=nil)
199
+ return nil unless self.has_geocode_key?
200
+ location = Barometer::Service.get(
201
+ "http://maps.google.com/maps/geo",
202
+ :query => {
203
+ :gl => country_code,
204
+ :key => self.google_geocode_key,
205
+ :output => "xml",
206
+ :q => query
207
+ },
208
+ :format => :xml
209
+ )['kml']['Response']
210
+ #puts location.inspect
211
+ geo = Barometer::Geo.new(location)
212
+ end
213
+
214
+ def self.format_to_country_code(format)
215
+ return nil unless format
216
+ case format
217
+ when :zipcode
218
+ country_code = "US"
219
+ when :postalcode
220
+ country_code = "CA"
221
+ else
222
+ country_code = nil
223
+ end
224
+ country_code
225
+ end
226
+
227
+ end
228
+ end