barometer 0.3.2 → 0.5.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.
- 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
|