barometer 0.3.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +78 -70
- data/VERSION.yml +2 -2
- data/bin/barometer +100 -37
- data/lib/barometer.rb +12 -8
- data/lib/barometer/base.rb +48 -20
- data/lib/barometer/data.rb +5 -1
- data/lib/barometer/data/current.rb +23 -15
- data/lib/barometer/data/distance.rb +15 -5
- data/lib/barometer/data/forecast.rb +23 -5
- data/lib/barometer/data/geo.rb +16 -54
- data/lib/barometer/data/local_datetime.rb +137 -0
- data/lib/barometer/data/local_time.rb +134 -0
- data/lib/barometer/data/location.rb +6 -1
- data/lib/barometer/data/measurement.rb +71 -42
- data/lib/barometer/data/night.rb +69 -0
- data/lib/barometer/data/pressure.rb +15 -5
- data/lib/barometer/data/speed.rb +16 -5
- data/lib/barometer/data/sun.rb +8 -20
- data/lib/barometer/data/temperature.rb +22 -9
- data/lib/barometer/data/units.rb +10 -19
- data/lib/barometer/data/zone.rb +135 -9
- data/lib/barometer/formats.rb +12 -0
- data/lib/barometer/formats/coordinates.rb +42 -0
- data/lib/barometer/formats/format.rb +46 -0
- data/lib/barometer/formats/geocode.rb +51 -0
- data/lib/barometer/formats/icao.rb +37 -0
- data/lib/barometer/formats/postalcode.rb +22 -0
- data/lib/barometer/formats/short_zipcode.rb +17 -0
- data/lib/barometer/formats/weather_id.rb +107 -0
- data/lib/barometer/formats/zipcode.rb +31 -0
- data/lib/barometer/query.rb +61 -232
- data/lib/barometer/services.rb +14 -4
- data/lib/barometer/translations/icao_country_codes.yml +9 -0
- data/lib/barometer/translations/weather_country_codes.yml +17 -0
- data/lib/barometer/weather.rb +51 -30
- data/lib/barometer/{services → weather_services}/google.rb +23 -26
- data/lib/barometer/weather_services/noaa.rb +6 -0
- data/lib/barometer/{services → weather_services}/service.rb +101 -92
- data/lib/barometer/weather_services/weather_bug.rb +6 -0
- data/lib/barometer/weather_services/weather_dot_com.rb +261 -0
- data/lib/barometer/{services → weather_services}/wunderground.rb +58 -76
- data/lib/barometer/{services → weather_services}/yahoo.rb +91 -121
- data/lib/barometer/web_services/geocode.rb +33 -0
- data/lib/barometer/web_services/weather_id.rb +37 -0
- data/lib/barometer/web_services/web_service.rb +32 -0
- data/lib/demometer/demometer.rb +31 -4
- data/lib/demometer/views/forecast.erb +20 -0
- data/lib/demometer/views/index.erb +10 -3
- data/lib/demometer/views/measurement.erb +8 -3
- data/lib/demometer/views/readme.erb +63 -24
- data/spec/barometer_spec.rb +18 -36
- data/spec/{data_current_spec.rb → data/current_spec.rb} +73 -49
- data/spec/{data_distance_spec.rb → data/distance_spec.rb} +30 -30
- data/spec/{data_forecast_spec.rb → data/forecast_spec.rb} +57 -15
- data/spec/data/geo_spec.rb +91 -0
- data/spec/data/local_datetime_spec.rb +269 -0
- data/spec/data/local_time_spec.rb +239 -0
- data/spec/{data_location_spec.rb → data/location_spec.rb} +12 -1
- data/spec/{data_measurement_spec.rb → data/measurement_spec.rb} +135 -66
- data/spec/data/night_measurement_spec.rb +136 -0
- data/spec/{data_pressure_spec.rb → data/pressure_spec.rb} +29 -29
- data/spec/{data_speed_spec.rb → data/speed_spec.rb} +30 -30
- data/spec/data/sun_spec.rb +49 -0
- data/spec/{data_temperature_spec.rb → data/temperature_spec.rb} +44 -44
- data/spec/{units_spec.rb → data/units_spec.rb} +6 -6
- data/spec/{data_zone_spec.rb → data/zone_spec.rb} +15 -15
- data/spec/fixtures/formats/weather_id/90210.xml +1 -0
- data/spec/fixtures/formats/weather_id/atlanta.xml +1 -0
- data/spec/fixtures/formats/weather_id/from_USGA0028.xml +1 -0
- data/spec/fixtures/formats/weather_id/new_york.xml +1 -0
- data/spec/fixtures/{geocode_40_73.xml → geocode/40_73.xml} +0 -0
- data/spec/fixtures/{geocode_90210.xml → geocode/90210.xml} +0 -0
- data/spec/fixtures/{geocode_T5B4M9.xml → geocode/T5B4M9.xml} +0 -0
- data/spec/fixtures/geocode/atlanta.xml +1 -0
- data/spec/fixtures/{geocode_calgary_ab.xml → geocode/calgary_ab.xml} +0 -0
- data/spec/fixtures/{geocode_ksfo.xml → geocode/ksfo.xml} +0 -0
- data/spec/fixtures/{geocode_newyork_ny.xml → geocode/newyork_ny.xml} +0 -0
- data/spec/fixtures/{google_calgary_ab.xml → services/google/calgary_ab.xml} +0 -0
- data/spec/fixtures/services/weather_dot_com/90210.xml +1 -0
- data/spec/fixtures/{current_calgary_ab.xml → services/wunderground/current_calgary_ab.xml} +0 -0
- data/spec/fixtures/{forecast_calgary_ab.xml → services/wunderground/forecast_calgary_ab.xml} +0 -0
- data/spec/fixtures/{yahoo_90210.xml → services/yahoo/90210.xml} +0 -0
- data/spec/formats/coordinates_spec.rb +158 -0
- data/spec/formats/format_spec.rb +73 -0
- data/spec/formats/geocode_spec.rb +179 -0
- data/spec/formats/icao_spec.rb +61 -0
- data/spec/formats/postalcode_spec.rb +59 -0
- data/spec/formats/short_zipcode_spec.rb +53 -0
- data/spec/formats/weather_id_spec.rb +191 -0
- data/spec/formats/zipcode_spec.rb +111 -0
- data/spec/query_spec.rb +261 -288
- data/spec/spec_helper.rb +128 -4
- data/spec/{service_google_spec.rb → weather_services/google_spec.rb} +46 -46
- data/spec/weather_services/services_spec.rb +1118 -0
- data/spec/weather_services/weather_dot_com_spec.rb +327 -0
- data/spec/weather_services/wunderground_spec.rb +332 -0
- data/spec/{service_yahoo_spec.rb → weather_services/yahoo_spec.rb} +65 -81
- data/spec/weather_spec.rb +73 -61
- data/spec/web_services/geocode_spec.rb +45 -0
- data/spec/web_services/web_services_spec.rb +26 -0
- metadata +88 -36
- data/lib/barometer/services/noaa.rb +0 -6
- data/lib/barometer/services/weather_bug.rb +0 -6
- data/lib/barometer/services/weather_dot_com.rb +0 -6
- data/spec/data_geo_spec.rb +0 -94
- data/spec/data_sun_spec.rb +0 -76
- data/spec/service_wunderground_spec.rb +0 -330
- data/spec/services_spec.rb +0 -1106
@@ -0,0 +1,261 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# = Weather.com
|
4
|
+
# www.weather.com
|
5
|
+
#
|
6
|
+
# - key required: YES (partnerid & licensekey)
|
7
|
+
# - registration required: YES
|
8
|
+
# - supported countries: US (by zipcode), International (by Weather Location ID)
|
9
|
+
#
|
10
|
+
# === performs geo coding
|
11
|
+
# - city: PARTIAL (just a name)
|
12
|
+
# - coordinates: YES
|
13
|
+
#
|
14
|
+
# === time info
|
15
|
+
# - sun rise/set: YES
|
16
|
+
# - provides timezone: NO, but provides a utc offset
|
17
|
+
# - requires TZInfo: NO
|
18
|
+
#
|
19
|
+
# == resources
|
20
|
+
# - API: ?
|
21
|
+
#
|
22
|
+
# === Possible queries:
|
23
|
+
# - http://xoap.weather.com/weather/local/30339?cc=*&dayf=5&link=xoap&prod=xoap&par=[PartnerID]&key=[LicenseKey]
|
24
|
+
#
|
25
|
+
# where query can be:
|
26
|
+
# - zipcode (US) [5 digits only]
|
27
|
+
# - Weather Location ID (International)
|
28
|
+
#
|
29
|
+
# = Weather.com terms of use
|
30
|
+
# ???
|
31
|
+
#
|
32
|
+
# == notes
|
33
|
+
# - the Weather Location ID is a propreitary number (possibly shared with yahoo.com)
|
34
|
+
#
|
35
|
+
# == TODO
|
36
|
+
# - improve "forecasted_wet_by_icon?" to determine if day or night and use right code
|
37
|
+
# - improve "forecasted_sunny_by_icon?" to determine if day or night and use right code
|
38
|
+
# - improve "forcasted_wet_by_humidity?" to use forecasted values
|
39
|
+
# - improve "forcasted_windy?" to use forecasted values
|
40
|
+
#
|
41
|
+
class WeatherService::WeatherDotCom < WeatherService
|
42
|
+
|
43
|
+
@@partner_key = nil
|
44
|
+
@@license_key = nil
|
45
|
+
|
46
|
+
def self.source_name; :weather_dot_com; end
|
47
|
+
def self.accepted_formats; [:short_zipcode, :weather_id]; end
|
48
|
+
|
49
|
+
def self.keys=(keys)
|
50
|
+
raise ArgumentError unless keys.is_a?(Hash)
|
51
|
+
keys.each do |key, value|
|
52
|
+
@@partner_key = value.to_s if key.to_s.downcase == "partner"
|
53
|
+
@@license_key = value.to_s if key.to_s.downcase == "license"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.has_keys?; !@@partner_key.nil? && !@@license_key.nil?; end
|
58
|
+
def self.requires_keys?; true; end
|
59
|
+
|
60
|
+
def self.wet_icon_codes
|
61
|
+
codes = (0..18).to_a + [35] + (37..43).to_a + (45..47).to_a
|
62
|
+
codes.collect {|c| c.to_s}
|
63
|
+
end
|
64
|
+
def self.sunny_icon_codes
|
65
|
+
codes = [19, 22, 28, 30, 32, 34, 36]
|
66
|
+
codes.collect {|c| c.to_s}
|
67
|
+
end
|
68
|
+
|
69
|
+
def self._measure(measurement, query, metric=true)
|
70
|
+
raise ArgumentError unless measurement.is_a?(Data::Measurement)
|
71
|
+
raise ArgumentError unless query.is_a?(Barometer::Query)
|
72
|
+
measurement.source = self.source_name
|
73
|
+
|
74
|
+
begin
|
75
|
+
result = self.fetch(query.q, metric)
|
76
|
+
rescue Timeout::Error => e
|
77
|
+
return measurement
|
78
|
+
end
|
79
|
+
|
80
|
+
measurement.current = self.build_current(result, metric)
|
81
|
+
measurement.forecast = self.build_forecast(result, metric)
|
82
|
+
measurement.location = self.build_location(result, query.geo)
|
83
|
+
measurement.current.sun = self.build_sun(result)
|
84
|
+
|
85
|
+
# add links
|
86
|
+
if result && result['lnks'] && result['lnks']['link']
|
87
|
+
result['lnks']['link'].each do |link_hash|
|
88
|
+
measurement.links[link_hash['t']] = link_hash['l']
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# set local time of measurement
|
93
|
+
local_time = self.build_local_time(result)
|
94
|
+
measurement.measured_at = local_time
|
95
|
+
measurement.current.current_at = local_time
|
96
|
+
|
97
|
+
measurement
|
98
|
+
end
|
99
|
+
|
100
|
+
# WARNING
|
101
|
+
# this is a best guess method. the data provided for time conversions
|
102
|
+
# leaves a lot to be desired. some time zones, offsets, local times are
|
103
|
+
# out to lunch. eg. Tahiti (all times seem to be 30 min off), but there
|
104
|
+
# is no way to determine this
|
105
|
+
#
|
106
|
+
# regardless of the above, this method will trust the data given to it
|
107
|
+
#
|
108
|
+
def self.build_local_time(data)
|
109
|
+
(data && data['loc']) ? Data::LocalTime.parse(data['loc']['tm']) : nil
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.build_current(data, metric=true)
|
113
|
+
raise ArgumentError unless data.is_a?(Hash)
|
114
|
+
current = Data::CurrentMeasurement.new
|
115
|
+
if data
|
116
|
+
if data['cc']
|
117
|
+
current.updated_at = Data::LocalDateTime.parse(data['cc']['lsup'])
|
118
|
+
current.icon = data['cc']['icon']
|
119
|
+
current.condition = data['cc']['t']
|
120
|
+
current.humidity = data['cc']['hmid'].to_i
|
121
|
+
current.temperature = Data::Temperature.new(metric)
|
122
|
+
current.temperature << data['cc']['tmp']
|
123
|
+
current.dew_point = Data::Temperature.new(metric)
|
124
|
+
current.dew_point << data['cc']['dewp']
|
125
|
+
current.wind_chill = Data::Temperature.new(metric)
|
126
|
+
current.wind_chill << data['cc']['flik']
|
127
|
+
current.visibility = Data::Distance.new(metric)
|
128
|
+
current.visibility << data['cc']['vis']
|
129
|
+
if data['cc']['wind']
|
130
|
+
current.wind = Data::Speed.new(metric)
|
131
|
+
current.wind << data['cc']['wind']['s']
|
132
|
+
current.wind.degrees = data['cc']['wind']['d'].to_f
|
133
|
+
current.wind.direction = data['cc']['wind']['t']
|
134
|
+
end
|
135
|
+
if data['cc']['bar']
|
136
|
+
current.pressure = Data::Pressure.new(metric)
|
137
|
+
current.pressure << data['cc']['bar']['r']
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
current
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.build_forecast(data, metric=true)
|
145
|
+
raise ArgumentError unless data.is_a?(Hash)
|
146
|
+
forecasts = []
|
147
|
+
|
148
|
+
if data && data['dayf'] && data['dayf']['day']
|
149
|
+
local_date = data['dayf']['lsup']
|
150
|
+
data['dayf']['day'].each do |forecast|
|
151
|
+
forecast_measurement = Data::ForecastMeasurement.new
|
152
|
+
forecast_measurement.date = Date.parse(forecast['dt'])
|
153
|
+
|
154
|
+
forecast_measurement.high = Data::Temperature.new(metric)
|
155
|
+
forecast_measurement.high << forecast['hi']
|
156
|
+
forecast_measurement.low = Data::Temperature.new(metric)
|
157
|
+
forecast_measurement.low << forecast['low']
|
158
|
+
|
159
|
+
# build sun
|
160
|
+
rise_local_time = Data::LocalTime.parse(forecast['sunr'])
|
161
|
+
set_local_time = Data::LocalTime.parse(forecast['suns'])
|
162
|
+
sun = Data::Sun.new(rise_local_time, set_local_time)
|
163
|
+
forecast_measurement.sun = sun
|
164
|
+
|
165
|
+
if forecast['part']
|
166
|
+
forecast['part'].each do |part|
|
167
|
+
if part['p'] == 'd'
|
168
|
+
# add this to the ForecastMeasurement
|
169
|
+
forecast_measurement.condition = part['t']
|
170
|
+
forecast_measurement.icon = part['icon']
|
171
|
+
forecast_measurement.pop = part['ppcp'].to_i
|
172
|
+
forecast_measurement.humidity = part['hmid'].to_i
|
173
|
+
|
174
|
+
if part['wind']
|
175
|
+
forecast_measurement.wind = Data::Speed.new(metric)
|
176
|
+
forecast_measurement.wind << part['wind']['s']
|
177
|
+
forecast_measurement.wind.degrees = part['wind']['d'].to_i
|
178
|
+
forecast_measurement.wind.direction = part['wind']['t']
|
179
|
+
end
|
180
|
+
|
181
|
+
elsif part['p'] == 'n'
|
182
|
+
# add this to the NightMeasurement
|
183
|
+
forecast_measurement.night = Data::NightMeasurement.new
|
184
|
+
forecast_measurement.night.condition = part['t']
|
185
|
+
forecast_measurement.night.icon = part['icon']
|
186
|
+
forecast_measurement.night.pop = part['ppcp'].to_i
|
187
|
+
forecast_measurement.night.humidity = part['hmid'].to_i
|
188
|
+
|
189
|
+
if part['wind']
|
190
|
+
forecast_measurement.night.wind = Data::Speed.new(metric)
|
191
|
+
forecast_measurement.night.wind << part['wind']['s']
|
192
|
+
forecast_measurement.night.wind.degrees = part['wind']['d'].to_i
|
193
|
+
forecast_measurement.night.wind.direction = part['wind']['t']
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
forecasts << forecast_measurement
|
200
|
+
end
|
201
|
+
end
|
202
|
+
forecasts
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.build_location(data, geo=nil)
|
206
|
+
raise ArgumentError unless data.is_a?(Hash)
|
207
|
+
raise ArgumentError unless (geo.nil? || geo.is_a?(Data::Geo))
|
208
|
+
location = Data::Location.new
|
209
|
+
# use the geocoded data if available, otherwise get data from result
|
210
|
+
if geo
|
211
|
+
location.city = geo.locality
|
212
|
+
location.state_code = geo.region
|
213
|
+
location.country = geo.country
|
214
|
+
location.country_code = geo.country_code
|
215
|
+
location.latitude = geo.latitude
|
216
|
+
location.longitude = geo.longitude
|
217
|
+
else
|
218
|
+
if data && data['loc']
|
219
|
+
location.name = data['loc']['dnam']
|
220
|
+
location.latitude = data['loc']['lat']
|
221
|
+
location.longitude = data['loc']['lon']
|
222
|
+
end
|
223
|
+
end
|
224
|
+
location
|
225
|
+
end
|
226
|
+
|
227
|
+
def self.build_sun(data)
|
228
|
+
raise ArgumentError unless data.is_a?(Hash)
|
229
|
+
sun = nil
|
230
|
+
if data
|
231
|
+
if data['loc']
|
232
|
+
rise_local_time = Data::LocalTime.parse(data['loc']['sunr'])
|
233
|
+
set_local_time = Data::LocalTime.parse(data['loc']['suns'])
|
234
|
+
end
|
235
|
+
sun = Data::Sun.new(rise_local_time, set_local_time)
|
236
|
+
end
|
237
|
+
sun || Data::Sun.new
|
238
|
+
end
|
239
|
+
|
240
|
+
# use HTTParty to get the current weather
|
241
|
+
#
|
242
|
+
def self.fetch(query, metric=true)
|
243
|
+
self.get(
|
244
|
+
"http://xoap.weather.com/weather/local/#{query}",
|
245
|
+
:query => { :par => @@partner_key, :key => @@license_key,
|
246
|
+
:prod => "xoap", :link => "xoap", :cc => "*",
|
247
|
+
:dayf => "5", :unit => (metric ? 'm' : 's')
|
248
|
+
},
|
249
|
+
:format => :xml,
|
250
|
+
:timeout => Barometer.timeout
|
251
|
+
)['weather']
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# FUTURE DATA TO SUPPORT?
|
258
|
+
# "cc"=>
|
259
|
+
# {"obst"=>"Santa Monica, CA",
|
260
|
+
# "uv"=>{"i"=>"0", "t"=>"Low"},
|
261
|
+
# "moon"=>{"icon"=>"9", "t"=>"Waxing Gibbous"}
|
@@ -1,4 +1,6 @@
|
|
1
1
|
module Barometer
|
2
|
+
#
|
3
|
+
# [DEFAULT PROVIDER]
|
2
4
|
#
|
3
5
|
# = Weather Underground
|
4
6
|
# www.wunderground.com
|
@@ -16,6 +18,9 @@ module Barometer
|
|
16
18
|
# - sun rise/set: YES (today only)
|
17
19
|
# - provides timezone: YES
|
18
20
|
# - requires TZInfo: YES
|
21
|
+
# *NOTE: If accuarcy of times and converting, this service is the top choice.
|
22
|
+
# They provide the full timezone name that is needed for the most
|
23
|
+
# accurate time conversions.
|
19
24
|
#
|
20
25
|
# == resources
|
21
26
|
# - API: http://wiki.wunderground.com/index.php/API_-_XML
|
@@ -36,19 +41,19 @@ module Barometer
|
|
36
41
|
# - airport code (3-letter or 4-letter)
|
37
42
|
# - lat,lon
|
38
43
|
#
|
39
|
-
|
44
|
+
# = Wunderground terms of use
|
45
|
+
# Unable to locate.
|
46
|
+
#
|
47
|
+
class WeatherService::Wunderground < WeatherService
|
40
48
|
|
49
|
+
def self.source_name; :wunderground; end
|
41
50
|
def self.accepted_formats
|
42
51
|
[:zipcode, :postalcode, :icao, :coordinates, :geocode]
|
43
52
|
end
|
44
53
|
|
45
|
-
def self.source_name
|
46
|
-
:wunderground
|
47
|
-
end
|
48
|
-
|
49
54
|
# these are the icon codes that indicate "wet", used by wet? function
|
50
55
|
def self.wet_icon_codes
|
51
|
-
%w(flurries rain sleet snow tstorms nt_flurries nt_rain nt_sleet nt_snow nt_tstorms chancerain)
|
56
|
+
%w(flurries rain sleet snow tstorms nt_flurries nt_rain nt_sleet nt_snow nt_tstorms chancerain chancetstorms)
|
52
57
|
end
|
53
58
|
# these are the icon codes that indicate "sun", used by sunny? function
|
54
59
|
def self.sunny_icon_codes
|
@@ -56,21 +61,19 @@ module Barometer
|
|
56
61
|
end
|
57
62
|
|
58
63
|
def self._measure(measurement, query, metric=true)
|
59
|
-
raise ArgumentError unless measurement.is_a?(
|
64
|
+
raise ArgumentError unless measurement.is_a?(Data::Measurement)
|
60
65
|
raise ArgumentError unless query.is_a?(Barometer::Query)
|
61
66
|
measurement.source = self.source_name
|
62
67
|
|
63
|
-
# get current measurement
|
64
68
|
begin
|
65
|
-
current_result = self.get_current(query.
|
69
|
+
current_result = self.get_current(query.q)
|
66
70
|
measurement.current = self.build_current(current_result, metric)
|
67
71
|
rescue Timeout::Error => e
|
68
72
|
return measurement
|
69
73
|
end
|
70
74
|
|
71
|
-
# get forecast measurement
|
72
75
|
begin
|
73
|
-
forecast_result = self.get_forecast(query.
|
76
|
+
forecast_result = self.get_forecast(query.q)
|
74
77
|
measurement.forecast = self.build_forecast(forecast_result, metric)
|
75
78
|
rescue Timeout::Error => e
|
76
79
|
return measurement
|
@@ -80,7 +83,10 @@ module Barometer
|
|
80
83
|
measurement.station = self.build_station(current_result)
|
81
84
|
measurement.timezone = self.build_timezone(forecast_result)
|
82
85
|
|
83
|
-
|
86
|
+
if current_result["credit"] && current_result["credit_URL"]
|
87
|
+
measurement.links[current_result["credit"]] = current_result["credit_URL"]
|
88
|
+
end
|
89
|
+
|
84
90
|
sun = nil
|
85
91
|
if measurement.current
|
86
92
|
sun = self.build_sun(forecast_result, measurement.timezone)
|
@@ -88,48 +94,49 @@ module Barometer
|
|
88
94
|
end
|
89
95
|
# use todays sun data for all future days
|
90
96
|
if measurement.forecast && sun
|
91
|
-
start_date = Date.parse(measurement.current.time)
|
92
97
|
measurement.forecast.each do |forecast|
|
93
|
-
|
94
|
-
forecast.sun = Barometer::Sun.add_days!(sun,days_in_future.to_i)
|
98
|
+
forecast.sun = sun
|
95
99
|
end
|
96
100
|
end
|
97
101
|
|
102
|
+
local_time = measurement.timezone ? Data::LocalTime.parse(
|
103
|
+
measurement.timezone.utc_to_local(Time.now.utc)
|
104
|
+
) : nil
|
105
|
+
measurement.measured_at = local_time
|
106
|
+
measurement.current.current_at = local_time
|
107
|
+
|
98
108
|
measurement
|
99
109
|
end
|
100
|
-
|
110
|
+
|
101
111
|
def self.build_current(data, metric=true)
|
102
112
|
raise ArgumentError unless data.is_a?(Hash)
|
103
113
|
|
104
|
-
current = CurrentMeasurement.new
|
105
|
-
|
106
|
-
# current_result['observation_time_rfc822'].blank?
|
107
|
-
current.time = data['observation_time_rfc822']
|
108
|
-
current.local_time = data['observation_time']
|
114
|
+
current = Data::CurrentMeasurement.new
|
115
|
+
current.updated_at = Data::LocalDateTime.parse(data['observation_time']) if data['observation_time']
|
109
116
|
current.humidity = data['relative_humidity'].to_i
|
110
117
|
current.icon = data['icon'] if data['icon']
|
111
118
|
|
112
|
-
current.temperature = Temperature.new(metric)
|
119
|
+
current.temperature = Data::Temperature.new(metric)
|
113
120
|
current.temperature << [data['temp_c'], data['temp_f']]
|
114
121
|
|
115
|
-
current.wind = Speed.new(metric)
|
122
|
+
current.wind = Data::Speed.new(metric)
|
116
123
|
current.wind.mph = data['wind_mph'].to_f
|
117
124
|
current.wind.degrees = data['wind_degrees'].to_i
|
118
125
|
current.wind.direction = data['wind_dir']
|
119
126
|
|
120
|
-
current.pressure = Pressure.new(metric)
|
127
|
+
current.pressure = Data::Pressure.new(metric)
|
121
128
|
current.pressure << [data['pressure_mb'], data['pressure_in']]
|
122
129
|
|
123
|
-
current.dew_point = Temperature.new(metric)
|
130
|
+
current.dew_point = Data::Temperature.new(metric)
|
124
131
|
current.dew_point << [data['dewpoint_c'], data['dewpoint_f']]
|
125
132
|
|
126
|
-
current.heat_index = Temperature.new(metric)
|
133
|
+
current.heat_index = Data::Temperature.new(metric)
|
127
134
|
current.heat_index << [data['heat_index_c'], data['heat_index_f']]
|
128
135
|
|
129
|
-
current.wind_chill = Temperature.new(metric)
|
136
|
+
current.wind_chill = Data::Temperature.new(metric)
|
130
137
|
current.wind_chill << [data['windchill_c'], data['windchill_f']]
|
131
138
|
|
132
|
-
current.visibility = Distance.new(metric)
|
139
|
+
current.visibility = Data::Distance.new(metric)
|
133
140
|
current.visibility << [data['visibility_km'], data['visibility_mi']]
|
134
141
|
|
135
142
|
current
|
@@ -143,15 +150,15 @@ module Barometer
|
|
143
150
|
data['simpleforecast']['forecastday']
|
144
151
|
|
145
152
|
data['simpleforecast']['forecastday'].each do |forecast|
|
146
|
-
forecast_measurement = ForecastMeasurement.new
|
153
|
+
forecast_measurement = Data::ForecastMeasurement.new
|
147
154
|
forecast_measurement.icon = forecast['icon']
|
148
155
|
forecast_measurement.date = Date.parse(forecast['date']['pretty'])
|
149
156
|
forecast_measurement.pop = forecast['pop'].to_i
|
150
157
|
|
151
|
-
forecast_measurement.high = Temperature.new(metric)
|
158
|
+
forecast_measurement.high = Data::Temperature.new(metric)
|
152
159
|
forecast_measurement.high << [forecast['high']['celsius'],forecast['high']['fahrenheit']]
|
153
160
|
|
154
|
-
forecast_measurement.low = Temperature.new(metric)
|
161
|
+
forecast_measurement.low = Data::Temperature.new(metric)
|
155
162
|
forecast_measurement.low << [forecast['low']['celsius'],forecast['low']['fahrenheit']]
|
156
163
|
|
157
164
|
forecasts << forecast_measurement
|
@@ -162,7 +169,7 @@ module Barometer
|
|
162
169
|
|
163
170
|
def self.build_location(data)
|
164
171
|
raise ArgumentError unless data.is_a?(Hash)
|
165
|
-
location = Location.new
|
172
|
+
location = Data::Location.new
|
166
173
|
if data['display_location']
|
167
174
|
location.name = data['display_location']['full']
|
168
175
|
location.city = data['display_location']['city']
|
@@ -178,7 +185,7 @@ module Barometer
|
|
178
185
|
|
179
186
|
def self.build_station(data)
|
180
187
|
raise ArgumentError unless data.is_a?(Hash)
|
181
|
-
station = Location.new
|
188
|
+
station = Data::Location.new
|
182
189
|
station.id = data['station_id']
|
183
190
|
if data['observation_location']
|
184
191
|
station.name = data['observation_location']['full']
|
@@ -193,15 +200,6 @@ module Barometer
|
|
193
200
|
station
|
194
201
|
end
|
195
202
|
|
196
|
-
# <forecastday>
|
197
|
-
# <date>
|
198
|
-
# <pretty_short>9:00 PM CST</pretty_short>
|
199
|
-
# <pretty>9:00 PM CST on January 15, 2008</pretty>
|
200
|
-
# <isdst>0</isdst>
|
201
|
-
# <tz_short>CST</tz_short>
|
202
|
-
# <tz_long>America/Chicago</tz_long>
|
203
|
-
# </date>
|
204
|
-
# </forecastday>
|
205
203
|
def self.build_timezone(data)
|
206
204
|
raise ArgumentError unless data.is_a?(Hash)
|
207
205
|
timezone = nil
|
@@ -209,62 +207,45 @@ module Barometer
|
|
209
207
|
data['simpleforecast']['forecastday'] &&
|
210
208
|
data['simpleforecast']['forecastday'].first &&
|
211
209
|
data['simpleforecast']['forecastday'].first['date']
|
212
|
-
timezone =
|
210
|
+
timezone = Data::Zone.new(
|
213
211
|
data['simpleforecast']['forecastday'].first['date']['tz_long']
|
214
212
|
)
|
215
213
|
end
|
216
|
-
timezone
|
214
|
+
timezone || Data::Zone.new(nil)
|
217
215
|
end
|
218
216
|
|
219
217
|
def self.build_sun(data, timezone)
|
220
218
|
raise ArgumentError unless data.is_a?(Hash)
|
221
|
-
raise ArgumentError unless timezone.is_a?(
|
222
|
-
|
219
|
+
raise ArgumentError unless timezone.is_a?(Data::Zone)
|
223
220
|
sun = nil
|
224
221
|
if data
|
225
|
-
|
226
|
-
if data['simpleforecast'] &&
|
227
|
-
data['simpleforecast']['forecastday'] &&
|
228
|
-
data['simpleforecast']['forecastday'].first &&
|
229
|
-
data['simpleforecast']['forecastday'].first['date']
|
230
|
-
|
231
|
-
# construct current date
|
232
|
-
date_data = data['simpleforecast']['forecastday'].first['date']
|
233
|
-
time = Time.local(
|
234
|
-
date_data['year'], date_data['month'], date_data['day'],
|
235
|
-
date_data['hour'], date_data['min'], date_data['sec']
|
236
|
-
)
|
237
|
-
end
|
238
|
-
if time && data['moon_phase']
|
239
|
-
# get the sun rise and set times (ie "6:32 am")
|
222
|
+
if data['moon_phase']
|
240
223
|
if data['moon_phase']['sunrise']
|
241
|
-
rise =
|
242
|
-
|
243
|
-
data['moon_phase']['sunrise']['
|
244
|
-
data['moon_phase']['sunrise']['minute']
|
224
|
+
rise = Data::LocalTime.new(
|
225
|
+
data['moon_phase']['sunrise']['hour'].to_i,
|
226
|
+
data['moon_phase']['sunrise']['minute'].to_i
|
245
227
|
)
|
246
228
|
end
|
247
229
|
if data['moon_phase']['sunset']
|
248
|
-
set =
|
249
|
-
|
250
|
-
data['moon_phase']['sunset']['
|
251
|
-
data['moon_phase']['sunset']['minute']
|
230
|
+
set = Data::LocalTime.new(
|
231
|
+
data['moon_phase']['sunset']['hour'].to_i,
|
232
|
+
data['moon_phase']['sunset']['minute'].to_i
|
252
233
|
)
|
253
234
|
end
|
254
235
|
|
255
|
-
sun = Sun.new(
|
256
|
-
|
257
|
-
|
236
|
+
sun = Data::Sun.new(
|
237
|
+
rise,
|
238
|
+
set
|
258
239
|
)
|
259
240
|
end
|
260
241
|
end
|
261
|
-
|
262
|
-
sun || Sun.new
|
242
|
+
sun || Data::Sun.new
|
263
243
|
end
|
264
244
|
|
265
245
|
# use HTTParty to get the current weather
|
266
246
|
def self.get_current(query)
|
267
|
-
|
247
|
+
return unless query
|
248
|
+
self.get(
|
268
249
|
"http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml",
|
269
250
|
:query => {:query => query},
|
270
251
|
:format => :xml,
|
@@ -274,7 +255,8 @@ module Barometer
|
|
274
255
|
|
275
256
|
# use HTTParty to get the forecasted weather
|
276
257
|
def self.get_forecast(query)
|
277
|
-
|
258
|
+
return unless query
|
259
|
+
self.get(
|
278
260
|
"http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml",
|
279
261
|
:query => {:query => query},
|
280
262
|
:format => :xml,
|