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
data/lib/barometer/data/zone.rb
CHANGED
@@ -6,9 +6,10 @@ module Barometer
|
|
6
6
|
# A simple Zone class
|
7
7
|
#
|
8
8
|
# Used for building and converting timezone aware date and times
|
9
|
-
# Really, these are just wrappers for TZInfo conversions
|
9
|
+
# Really, these are just wrappers for TZInfo conversions plus
|
10
|
+
# some extras.
|
10
11
|
#
|
11
|
-
class Zone
|
12
|
+
class Data::Zone
|
12
13
|
|
13
14
|
attr_accessor :timezone, :tz
|
14
15
|
|
@@ -31,12 +32,12 @@ module Barometer
|
|
31
32
|
|
32
33
|
# return Time.now.utc for the set timezone
|
33
34
|
def now
|
34
|
-
|
35
|
+
Data::Zone.now(@timezone)
|
35
36
|
end
|
36
37
|
|
37
38
|
# return Date.today for the set timezone
|
38
39
|
def today
|
39
|
-
|
40
|
+
Data::Zone.today(@timezone)
|
40
41
|
end
|
41
42
|
|
42
43
|
def local_to_utc(local_time)
|
@@ -78,12 +79,14 @@ module Barometer
|
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
81
|
-
# takes a time (any timezone), and a TimeZone Short Code (ie PST)
|
82
|
-
#
|
82
|
+
# takes a time (any timezone), and a TimeZone Short Code (ie PST) or
|
83
|
+
# the number of hours offset from UTC time and
|
84
|
+
# converts the time to UTC according to that time_zone
|
83
85
|
# NOTE: No Tests
|
84
|
-
def self.code_to_utc(time,
|
86
|
+
def self.code_to_utc(time, timezone)
|
85
87
|
raise ArgumentError unless time.is_a?(Time)
|
86
|
-
|
88
|
+
|
89
|
+
offset = self.zone_to_offset(timezone)
|
87
90
|
|
88
91
|
Time.utc(
|
89
92
|
time.year, time.month, time.day,
|
@@ -117,7 +120,130 @@ module Barometer
|
|
117
120
|
reference_date.year, reference_date.month, reference_date.day,
|
118
121
|
reference_time.hour, reference_time.min, reference_time.sec
|
119
122
|
)
|
120
|
-
timezone_code ?
|
123
|
+
timezone_code ? Data::Zone.code_to_utc(new_time,timezone_code) : new_time
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Known conflicts
|
128
|
+
# IRT (ireland and india)
|
129
|
+
# CST (central standard time, china standard time)
|
130
|
+
#
|
131
|
+
def self.zone_to_offset(timezone)
|
132
|
+
offset = 0
|
133
|
+
seconds_in_hour = 60*60
|
134
|
+
# do we have a short_timezone code, or an offset?
|
135
|
+
if timezone.is_a?(Fixnum) ||
|
136
|
+
(timezone.is_a?(String) && timezone.to_i.to_s == timezone)
|
137
|
+
# we have an offset, convert to second
|
138
|
+
offset = timezone.to_i * seconds_in_hour
|
139
|
+
else
|
140
|
+
# try to use Time
|
141
|
+
unless offset = Time.zone_offset(timezone)
|
142
|
+
# that would have been too easy, do it manually
|
143
|
+
# http://www.timeanddate.com/library/abbreviations/timezones/
|
144
|
+
# http://www.worldtimezone.com/wtz-names/timezonenames.html
|
145
|
+
zone_offsets = {
|
146
|
+
:A => 1, :ACDT => 10.5, :ACST => 9.5, :ADT => -3, :AEDT => 11,
|
147
|
+
:AEST => 10, :AFT => 4.5, :AHDT => -9, :AHST => -10, :AKDT => -8,
|
148
|
+
:AKST => -9, :AMST => 5, :AMT => 4, :ANAST => 13, :ANAT => 12,
|
149
|
+
:ART => -3, :AST => -4, :AT => -1, :AWDT => 9, :AWST => 8,
|
150
|
+
:AZOST => 0, :AZOT => -1, :AZST => 5, :AZT => 4,
|
151
|
+
|
152
|
+
:B => 2, :BADT => 4, :BAT => 6, :BDST => 2, :BDT => 6, :BET => -11,
|
153
|
+
:BNT => 8, :BORT => 8, :BOT => -4, :BRA => -3, :BST => 1, :BT => 6,
|
154
|
+
:BTT => 6,
|
155
|
+
|
156
|
+
:C => 3, :CAT => 2, :CCT => 8, :CEST => 2, :CET => 1, :CHADT => 13.75,
|
157
|
+
:CHAST => 12.75, :CHST => 10, :CKT => -10, :CLST => -3, :CLT => -4,
|
158
|
+
:COT => -5, :CUT => 0, :CVT => -1, :CWT => 8.75, :CXT => 7, :CEDT => 2,
|
159
|
+
|
160
|
+
:D => 4, :DAVT => 7, :DDUT => 10, :DNT => 1, :DST => 2,
|
161
|
+
|
162
|
+
:E => 5, :EASST => -5, :EAST => -6, :EAT => 3, :ECT => -5, :EEST => 3,
|
163
|
+
:EET => 2, :EGST => 0, :EGT => -1, :EMT => 1, :EEDT => 3,
|
164
|
+
|
165
|
+
:F => 6, :FDT => -1, :FJST => 13, :FJT => 12, :FKST => -3, :FKT => -4,
|
166
|
+
:FST => 2, :FWT => 1,
|
167
|
+
|
168
|
+
:G => 7, :GALT => -6, :GAMT => -9, :GEST => 5, :GET => 4, :GFT => -3,
|
169
|
+
:GILT => 12, :GST => 4, :GT => 0, :GYT => -4, :GZ => 0,
|
170
|
+
|
171
|
+
:H => 8, :HAA => -3, :HAC => -5, :HADT => -9, :HAE => -4, :HAP => -7,
|
172
|
+
:HAR => -6, :HAST => -10, :HAT => -2.5, :HAY => -8, :HDT => -9.5,
|
173
|
+
:HFE => 2, :HFH => 1, :HG => 0, :HKT => 8, :HNA => -4, :HNC => -6,
|
174
|
+
:HNE => -5, :HNP => -8, :HNR => -7, :HNT => -3.5, :HNY => -9,
|
175
|
+
:HOE => 1, :HST => -10,
|
176
|
+
|
177
|
+
:I => 9, :ICT => 4, :IDLE => 12, :IDLW => -12, :IDT => 1, :IOT => 5,
|
178
|
+
:IRDT => 4.5, :IRKST => 9, :IRKT => 8, :IRST => 3.5, :IRT => 3.5,
|
179
|
+
:IST => 1, :IT => 3.5, :ITA => 1,
|
180
|
+
|
181
|
+
:JAVT => 7, :JAYT => 9, :JFDT => -3, :JFST => -4, :JST => 9, :JT => 7,
|
182
|
+
|
183
|
+
:K => 10, :KDT => 10, :KGST => 6, :KGT => 5, :KOST => 12, :KOVT => 7,
|
184
|
+
:KOVST => 8, :KRAST => 8, :KRAT => 7, :KST => 9,
|
185
|
+
|
186
|
+
:L => 11, :LHDT => 11, :LHST => 10.5, :LIGT => 10, :LINT => 14, :LKT => 6,
|
187
|
+
:LST => 1,
|
188
|
+
|
189
|
+
:M => 12, :MAGST => 12, :MAGT => 11, :MAL => 8, :MART => -9.5, :MAT => 3,
|
190
|
+
:MAWT => 6, :MBT => 8, :MED => 2, :MEDST => 2, :MEST => 2, :MESZ => 2,
|
191
|
+
:MET => 1, :MEWT => 1, :MEX => -6, :MEZ => 1, :MHT => 12, :MIT => 9.5,
|
192
|
+
:MMT => 6.5, :MNT => 8, :MNST => 9, :MPT => 10, :MSD => 4, :MSK => 3,
|
193
|
+
:MSKS => 4, :MT => 8.5, :MUT => 4, :MUST => 5, :MVT => 5, :MYT => 8,
|
194
|
+
:MFPT => -10,
|
195
|
+
|
196
|
+
:N => -1, :NCT => 11, :NDT => -2.5, :NFT => 11.5, :NOR => 1, :NOVST => 7,
|
197
|
+
:NOVT => 6, :NPT => 5.75, :NRT => 12, :NST => -3.5, :NSUT => 6.5,
|
198
|
+
:NT => -11, :NUT => -11, :NZDT => 13, :NZST => 12, :NZT => 12,
|
199
|
+
|
200
|
+
:O => -2, :OESZ => 3, :OEZ => 2, :OMSK => 7, :OMSST => 7, :OMST => 6,
|
201
|
+
|
202
|
+
:P => -3, :PET => -5, :PETST => 13, :PETT => 12, :PGT => 10, :PHOT => 13,
|
203
|
+
:PHT => 8, :PIT => 8, :PKT => 5, :PKST => 6, :PMDT => -2, :PMST => -3,
|
204
|
+
:PMT => -3, :PNT => -8.5, :PONT => 11, :PYST => -3, :PYT => -4, :PWT => 9,
|
205
|
+
|
206
|
+
:Q => -4,
|
207
|
+
|
208
|
+
:R => -5, :R1T => 2, :R2T => 3, :RET => 4, :ROK => 9, :ROTT => -3,
|
209
|
+
|
210
|
+
:S => -6, :SADT => 10.5, :SAMST => 5, :SAMT => 4, :SAST => 2, :SBT => 11,
|
211
|
+
:SCT => 4, :SCDT => 13, :SCST => 12, :SET => 1, :SGT => 8, :SIT => 8,
|
212
|
+
:SLT => -4, :SLST => -3, :SRT => -3, :SST => -11, :SYST => 3, :SWT => 1,
|
213
|
+
:SYT => 2,
|
214
|
+
|
215
|
+
:T => -7, :TAHT => -10, :TFT => 5, :THA => 7, :THAT => -10, :TJT => 5,
|
216
|
+
:TKT => -10, :TMT => 5, :TOT => 13, :TRUK => 10, :TPT => 9, :TRUT => 10,
|
217
|
+
:TST => 3, :TUC => 0, :TVT => 12, :TWT => 8,
|
218
|
+
|
219
|
+
:U => -8, :ULAST => 9, :ULAT => 8, :USZ1 => 2, :USZ1S => 3, :USZ3 => 4,
|
220
|
+
:USZ3S => 5, :USZ4 => 5, :USZ4S => 6, :USZ5 => 6, :USZ5S => 7, :USZ6 => 7,
|
221
|
+
:USZ6S => 8, :USZ7 => 8, :USZ7S => 9, :USZ8 => 9, :USZ8S => 10, :USZ9 => 10,
|
222
|
+
:USZ9S => 11, :UTZ => -3, :UYT => -3, :UYST => -2, :UZ10 => 11, :UZ10S => 12,
|
223
|
+
:UZ11 => 12, :UZ11S => 13, :UZ12 => 12, :UZ12S => 13, :UZT => 5,
|
224
|
+
|
225
|
+
:V => -9, :VET => -4.5, :VLAST => 11, :VLAT => 10, :VOST => 6, :VST => -4.5,
|
226
|
+
:VTZ => -2, :VUT => 11,
|
227
|
+
|
228
|
+
:W => -10, :WAKT => 12, :WAST => 2, :WAT => 1, :WCT => 8.75, :WEST => 1,
|
229
|
+
:WESZ => 1, :WET => 0, :WEZ => 0, :WFT => 12, :WGST => -2, :WGT => -3,
|
230
|
+
:WIB => 7, :WITA => 8, :WIT => 9, :WST => 8, :WKST => 5, :WTZ => -1,
|
231
|
+
:WUT => 1, :WEDT => 1, :WDT => 9,
|
232
|
+
|
233
|
+
:X => -11,
|
234
|
+
|
235
|
+
:Y => -12, :YAKST => 10, :YAKT => 9, :YAPT => 10, :YDT => -8, :YEKST => 6,
|
236
|
+
:YEKT => 5, :YST => -9,
|
237
|
+
|
238
|
+
:Z => 0
|
239
|
+
}
|
240
|
+
# unknown
|
241
|
+
# :HL => X, :LST => X, :LT => X, :OZ => X :SZ => X :TAI => X :UT => X :WZ => X
|
242
|
+
|
243
|
+
offset = (zone_offsets[timezone.to_s.upcase.to_sym] || 0) * seconds_in_hour
|
244
|
+
end
|
245
|
+
end
|
246
|
+
return offset
|
121
247
|
end
|
122
248
|
|
123
249
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
# query formats
|
4
|
+
#
|
5
|
+
require 'formats/format'
|
6
|
+
require 'formats/short_zipcode'
|
7
|
+
require 'formats/zipcode'
|
8
|
+
require 'formats/postalcode'
|
9
|
+
require 'formats/weather_id'
|
10
|
+
require 'formats/coordinates'
|
11
|
+
require 'formats/icao'
|
12
|
+
require 'formats/geocode'
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Format: Coordinates
|
4
|
+
#
|
5
|
+
# eg. 123.1234,-123.123
|
6
|
+
#
|
7
|
+
# This class is used to determine if a query is a
|
8
|
+
# :coordinates and how to convert to :coordinates.
|
9
|
+
#
|
10
|
+
class Query::Format::Coordinates < Query::Format
|
11
|
+
|
12
|
+
def self.format; :coordinates; end
|
13
|
+
def self.regex; /^[-]?[0-9\.]+[,]{1}[-]?[0-9\.]+$/; end
|
14
|
+
def self.convertable_formats
|
15
|
+
[:short_zipcode, :zipcode, :postalcode, :weather_id, :coordinates, :icao, :geocode]
|
16
|
+
end
|
17
|
+
|
18
|
+
# convert to this format, X -> :coordinates
|
19
|
+
#
|
20
|
+
def self.to(original_query)
|
21
|
+
raise ArgumentError unless is_a_query?(original_query)
|
22
|
+
# return nil unless converts?(original_query)
|
23
|
+
converted_query = Barometer::Query.new
|
24
|
+
|
25
|
+
# pre-convert
|
26
|
+
#
|
27
|
+
pre_query = nil
|
28
|
+
if original_query.format == :weather_id
|
29
|
+
pre_query = Query::Format::WeatherID.reverse(original_query)
|
30
|
+
end
|
31
|
+
|
32
|
+
# convert & adjust
|
33
|
+
#
|
34
|
+
converted_query = Query::Format::Geocode.geocode(pre_query || original_query)
|
35
|
+
converted_query.q = converted_query.geo.coordinates if converted_query.geo
|
36
|
+
converted_query.format = format
|
37
|
+
|
38
|
+
converted_query
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Base Format Class
|
4
|
+
#
|
5
|
+
# Fromats are used to determine if a query is of a certain
|
6
|
+
# format, how to convert to and from that format
|
7
|
+
# and what the country_code is for that format (if possible).
|
8
|
+
# Some formats require external Web Services to help
|
9
|
+
# in the converision. (ie :weather_id -> :geocode)
|
10
|
+
#
|
11
|
+
class Query::Format
|
12
|
+
|
13
|
+
# stubs
|
14
|
+
#
|
15
|
+
def self.regex; raise NotImplementedError; end
|
16
|
+
def self.format; raise NotImplementedError; end
|
17
|
+
|
18
|
+
# defaults
|
19
|
+
#
|
20
|
+
def self.to(query=nil,country=nil); nil; end
|
21
|
+
def self.country_code(query=nil); nil; end
|
22
|
+
def self.convertable_formats; []; end
|
23
|
+
|
24
|
+
# is the query of this format?
|
25
|
+
#
|
26
|
+
def self.is?(query=nil)
|
27
|
+
raise ArgumentError unless query.is_a?(String)
|
28
|
+
return !(query =~ self.regex).nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
# does the format support conversion from the given query?
|
32
|
+
#
|
33
|
+
def self.converts?(query=nil)
|
34
|
+
return false unless is_a_query?(query)
|
35
|
+
self.convertable_formats.include?(query.format)
|
36
|
+
end
|
37
|
+
|
38
|
+
# is the object a Barometer::Query?
|
39
|
+
#
|
40
|
+
def self.is_a_query?(object=nil)
|
41
|
+
return false unless object
|
42
|
+
object.is_a?(Barometer::Query)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Format: Geocode
|
4
|
+
# (not to be confused with the WebService geocode)
|
5
|
+
#
|
6
|
+
# eg. 123 Elm St, Mystery, Alaska, USA
|
7
|
+
#
|
8
|
+
# This class is used to determine if a query is a
|
9
|
+
# :geocode, how to convert to :geocode
|
10
|
+
#
|
11
|
+
class Query::Format::Geocode < Query::Format
|
12
|
+
|
13
|
+
def self.format; :geocode; end
|
14
|
+
def self.is?(query=nil); query.is_a?(String) ? true : false; end
|
15
|
+
def self.convertable_formats
|
16
|
+
[:short_zipcode, :zipcode, :coordinates, :weather_id, :icao]
|
17
|
+
end
|
18
|
+
|
19
|
+
# convert to this format, X -> :geocode
|
20
|
+
#
|
21
|
+
def self.to(original_query)
|
22
|
+
raise ArgumentError unless is_a_query?(original_query)
|
23
|
+
unless converts?(original_query)
|
24
|
+
return (original_query.format == format ? original_query.dup : nil)
|
25
|
+
end
|
26
|
+
converted_query = Barometer::Query.new
|
27
|
+
|
28
|
+
converted_query = (original_query.format == :weather_id ?
|
29
|
+
Query::Format::WeatherID.reverse(original_query) :
|
30
|
+
geocode(original_query))
|
31
|
+
converted_query
|
32
|
+
end
|
33
|
+
|
34
|
+
# geocode the query
|
35
|
+
#
|
36
|
+
def self.geocode(original_query)
|
37
|
+
raise ArgumentError unless is_a_query?(original_query)
|
38
|
+
converted_query = Barometer::Query.new
|
39
|
+
|
40
|
+
#converted_query.geo = _geocode(original_query)
|
41
|
+
converted_query.geo = WebService::Geocode.fetch(original_query)
|
42
|
+
if converted_query.geo
|
43
|
+
converted_query.country_code = converted_query.geo.country_code
|
44
|
+
converted_query.q = converted_query.geo.to_s
|
45
|
+
converted_query.format = format
|
46
|
+
end
|
47
|
+
converted_query
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Format: ICAO (International Civil Aviation Organization)
|
4
|
+
#
|
5
|
+
# eg. KLAX (Los Angeles Airport)
|
6
|
+
#
|
7
|
+
# This class is used to determine if a query is a
|
8
|
+
# :icao and what the country_code is.
|
9
|
+
#
|
10
|
+
class Query::Format::Icao < Query::Format
|
11
|
+
|
12
|
+
@@codes_file = File.expand_path(
|
13
|
+
File.join(File.dirname(__FILE__), '..', 'translations', 'icao_country_codes.yml'))
|
14
|
+
@@codes = nil
|
15
|
+
|
16
|
+
def self.format; :icao; end
|
17
|
+
|
18
|
+
# call any 3-4 letter query, :icao ... obviously this will have a lot
|
19
|
+
# of false positives. So far this isn't an issue as all weather services
|
20
|
+
# that take :icao (which is just one, :wunderground) also take what
|
21
|
+
# this would have been if it was not called :icao.
|
22
|
+
#
|
23
|
+
def self.regex; /^[A-Za-z]{3,4}$/; end
|
24
|
+
|
25
|
+
# # in some cases the first letter can designate the country
|
26
|
+
# #
|
27
|
+
def self.country_code(query=nil)
|
28
|
+
return unless query && query.is_a?(String)
|
29
|
+
$:.unshift(File.dirname(__FILE__))
|
30
|
+
@@codes ||= YAML.load_file(@@codes_file)
|
31
|
+
return unless @@codes && @@codes['one_letter'] && @@codes['two_letter']
|
32
|
+
@@codes['one_letter'][query[0..0].upcase.to_s] ||
|
33
|
+
@@codes['two_letter'][query[0..1].upcase.to_s] || nil
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Format: Postal Code
|
4
|
+
#
|
5
|
+
# eg. H0H 0H0
|
6
|
+
#
|
7
|
+
# This class is used to determine if a query is a
|
8
|
+
# :postalcode and what the country_code is.
|
9
|
+
#
|
10
|
+
class Query::Format::Postalcode < Query::Format
|
11
|
+
|
12
|
+
def self.format; :postalcode; end
|
13
|
+
def self.country_code(query=nil); "CA"; end
|
14
|
+
def self.regex
|
15
|
+
# Rules: no D, F, I, O, Q, or U anywhere
|
16
|
+
# Basic validation: ^[ABCEGHJ-NPRSTVXY]{1}[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}
|
17
|
+
# [ ]?[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[0-9]{1}$
|
18
|
+
/^[A-Z]{1}[\d]{1}[A-Z]{1}[ ]?[\d]{1}[A-Z]{1}[\d]{1}$/
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Format: Zip Code (short)
|
4
|
+
#
|
5
|
+
# eg. 90210
|
6
|
+
#
|
7
|
+
# This class is used to determine if a query is a
|
8
|
+
# :short_zipcode and what the country_code is.
|
9
|
+
#
|
10
|
+
class Query::Format::ShortZipcode < Query::Format
|
11
|
+
|
12
|
+
def self.format; :short_zipcode; end
|
13
|
+
def self.country_code(query=nil); "US"; end
|
14
|
+
def self.regex; /(^[0-9]{5}$)/; end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Barometer
|
2
|
+
#
|
3
|
+
# Format: Weather ID (specific to weather.com)
|
4
|
+
#
|
5
|
+
# eg. USGA0028
|
6
|
+
#
|
7
|
+
# This class is used to determine if a query is a
|
8
|
+
# :weather_id, how to convert to and from :weather_id
|
9
|
+
# and what the country_code is.
|
10
|
+
#
|
11
|
+
class Query::Format::WeatherID < Query::Format
|
12
|
+
|
13
|
+
@@fixes_file = File.expand_path(
|
14
|
+
File.join(File.dirname(__FILE__), '..', 'translations', 'weather_country_codes.yml'))
|
15
|
+
@@fixes = nil
|
16
|
+
|
17
|
+
def self.format; :weather_id; end
|
18
|
+
def self.regex; /(^[A-Za-z]{4}[0-9]{4}$)/; end
|
19
|
+
def self.convertable_formats
|
20
|
+
[:short_zipcode, :zipcode, :coordinates, :geocode]
|
21
|
+
end
|
22
|
+
|
23
|
+
# the first two letters of the :weather_id is the country_code
|
24
|
+
#
|
25
|
+
def self.country_code(query=nil)
|
26
|
+
(query && query.size >= 2) ? _fix_country(query[0..1]) : nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# convert to this format, X -> :weather_id
|
30
|
+
#
|
31
|
+
def self.to(original_query)
|
32
|
+
raise ArgumentError unless is_a_query?(original_query)
|
33
|
+
return nil unless converts?(original_query)
|
34
|
+
converted_query = Barometer::Query.new
|
35
|
+
|
36
|
+
# convert original query to :geocode, as that is the only
|
37
|
+
# format we can convert directly from to weather_id
|
38
|
+
converted_query = Query::Format::Geocode.to(original_query)
|
39
|
+
converted_query.q = _search(converted_query)
|
40
|
+
converted_query.format = format
|
41
|
+
converted_query.country_code = country_code(converted_query.q)
|
42
|
+
converted_query
|
43
|
+
end
|
44
|
+
|
45
|
+
# reverse lookup, :weather_id -> (:geocode || :coordinates)
|
46
|
+
#
|
47
|
+
def self.reverse(original_query)
|
48
|
+
raise ArgumentError unless is_a_query?(original_query)
|
49
|
+
return nil unless original_query.format == format
|
50
|
+
converted_query = Barometer::Query.new
|
51
|
+
converted_query.q = _reverse(original_query)
|
52
|
+
converted_query.format = Query::Format::Geocode.format
|
53
|
+
converted_query
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# :geocode -> :weather_id
|
59
|
+
# search weather.com for the given query
|
60
|
+
#
|
61
|
+
def self._search(query=nil)
|
62
|
+
return nil unless query
|
63
|
+
raise ArgumentError unless is_a_query?(query)
|
64
|
+
response = WebService::WeatherID.fetch(query)
|
65
|
+
_parse_weather_id(response)
|
66
|
+
end
|
67
|
+
|
68
|
+
# :weather_id -> :geocode
|
69
|
+
# query yahoo with :weather_id and parse geo_data
|
70
|
+
#
|
71
|
+
def self._reverse(query=nil)
|
72
|
+
return nil unless query
|
73
|
+
raise ArgumentError unless is_a_query?(query)
|
74
|
+
response = WebService::WeatherID.reverse(query)
|
75
|
+
_parse_geocode(response)
|
76
|
+
end
|
77
|
+
|
78
|
+
# match the first :weather_id (from search results)
|
79
|
+
#
|
80
|
+
def self._parse_weather_id(text)
|
81
|
+
return nil unless text
|
82
|
+
match = text.match(/loc id=[\\]?['|""]([0-9a-zA-Z]*)[\\]?['|""]/)
|
83
|
+
match ? match[1] : nil
|
84
|
+
end
|
85
|
+
|
86
|
+
# parse the geo_data
|
87
|
+
#
|
88
|
+
def self._parse_geocode(text)
|
89
|
+
return nil unless text
|
90
|
+
output = [text["city"], text["region"], _fix_country(text["country"])]
|
91
|
+
output.delete("")
|
92
|
+
output.compact.join(', ')
|
93
|
+
end
|
94
|
+
|
95
|
+
# fix the country code
|
96
|
+
#
|
97
|
+
# weather.com uses non-standard two letter country codes that
|
98
|
+
# hinder the ability to determine the country or fetch geo_data.
|
99
|
+
# correct these "mistakes"
|
100
|
+
#
|
101
|
+
def self._fix_country(country_code)
|
102
|
+
@@fixes ||= YAML.load_file(@@fixes_file)
|
103
|
+
@@fixes[country_code.upcase.to_s] || country_code
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|