barometer 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,7 +8,7 @@ module Barometer
8
8
  class Geo
9
9
 
10
10
  attr_accessor :latitude, :longitude
11
- attr_accessor :locality, :region, :country, :country_code
11
+ attr_accessor :locality, :region, :country, :country_code, :address
12
12
 
13
13
  #
14
14
  # this will take a Location object (either generated by Graticule
@@ -60,6 +60,7 @@ module Barometer
60
60
  @region = location.region
61
61
  @country = location.country
62
62
  @country_code = location.country_code
63
+ @address = location.address_line
63
64
  end
64
65
 
65
66
  def build_from_httparty(location=nil)
@@ -87,6 +88,7 @@ module Barometer
87
88
  end
88
89
  @country = placemark["AddressDetails"]["Country"]["CountryName"]
89
90
  @country_code = placemark["AddressDetails"]["Country"]["CountryNameCode"]
91
+ @address = placemark["AddressDetails"]["Country"]["AddressLine"]
90
92
  end
91
93
  end
92
94
 
@@ -1,10 +1,10 @@
1
1
  module Graticule
2
2
  class Location
3
3
 
4
- attr_accessor :country_code
4
+ attr_accessor :country_code, :address_line
5
5
 
6
6
  def attributes
7
- [:latitude, :longitude, :street, :locality, :region, :postal_code, :country, :precision, :cuntry_code].inject({}) do |result,attr|
7
+ [:latitude, :longitude, :street, :locality, :region, :postal_code, :country, :precision, :cuntry_code, :address_line].inject({}) do |result,attr|
8
8
  result[attr] = self.send(attr) unless self.send(attr).blank?
9
9
  result
10
10
  end
@@ -39,6 +39,7 @@ module Graticule
39
39
  l.postal_code = value(address.elements['.//PostalCodeNumber/text()'])
40
40
  l.country = value(address.elements['.//CountryName/text()'])
41
41
  l.country_code = value(address.elements['.//CountryNameCode/text()'])
42
+ l.address_line = value(address.elements['.//AddressLine/text()'])
42
43
  l.precision = PRECISION[address.attribute('Accuracy').value.to_i] || :unknown
43
44
  end
44
45
  end
@@ -31,20 +31,23 @@ module Barometer
31
31
  self.analyze!
32
32
  end
33
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
34
+ # analyze the saved query to determine the format.
36
35
  def analyze!
37
36
  return unless @q
38
37
  if Barometer::Query.is_us_zipcode?(@q)
39
38
  @format = :zipcode
39
+ @country_code = Barometer::Query.format_to_country_code(@format)
40
40
  elsif Barometer::Query.is_canadian_postcode?(@q)
41
41
  @format = :postalcode
42
+ @country_code = Barometer::Query.format_to_country_code(@format)
42
43
  elsif Barometer::Query.is_coordinates?(@q)
43
44
  @format = :coordinates
45
+ elsif Barometer::Query.is_icao?(@q)
46
+ @format = :icao
47
+ # @country_code = Barometer::Query.icao_to_country_code(@q)
44
48
  else
45
49
  @format = :geocode
46
50
  end
47
- @country_code = Barometer::Query.format_to_country_code(@format)
48
51
  end
49
52
 
50
53
  # take a list of acceptable (and ordered by preference) formats and convert
@@ -93,6 +96,7 @@ module Barometer
93
96
  def postalcode?; @format == :postalcode; end
94
97
  def coordinates?; @format == :coordinates; end
95
98
  def geocode?; @format == :geocode; end
99
+ def icao?; @format == :icao; end
96
100
 
97
101
  def self.is_us_zipcode?(query)
98
102
  us_zipcode_regex = /(^[0-9]{5}$)|(^[0-9]{5}-[0-9]{4}$)/
@@ -112,12 +116,25 @@ module Barometer
112
116
  return !(query =~ coordinates_regex).nil?
113
117
  end
114
118
 
119
+ def self.is_icao?(query)
120
+ # allow any 3 or 4 letter word ... unfortunately this means some locations
121
+ # (ie Utah, Goa, Kiev, etc) will be detected as ICAO. This won't matter for
122
+ # returning weather results ... it will just effect what happens to the query.
123
+ # For example, wunderground will accept :icao above :coordinates and :geocode,
124
+ # which means that a city like Kiev would normally get converted to :coordinates
125
+ # but in this case it will be detected as :icao so it will be passed as is.
126
+ # Currently, only wunderground accepts ICAO, and they process ICAO the same as a
127
+ # city name, so it doesn't matter.
128
+ icao_regex = /^[A-Za-z]{3,4}$/
129
+ return !(query =~ icao_regex).nil?
130
+ end
131
+
115
132
  #
116
133
  # CONVERTERS
117
134
  #
118
135
 
119
136
  # this will take all query formats and convert them to coordinates
120
- # accepts- :zipcode, :postalcode, :geocode
137
+ # accepts- :zipcode, :postalcode, :geocode, :icao
121
138
  # returns- :coordinates
122
139
  # if the conversion fails, return nil
123
140
  def self.to_coordinates(query, format)
@@ -129,7 +146,7 @@ module Barometer
129
146
  end
130
147
 
131
148
  # this will take all query formats and convert them to coorinates
132
- # accepts- :zipcode, :postalcode, :coordinates
149
+ # accepts- :zipcode, :postalcode, :coordinates, :icao
133
150
  # returns- :geocode
134
151
  def self.to_geocode(query, format)
135
152
  perform_geocode = false
@@ -143,8 +160,17 @@ module Barometer
143
160
  if perform_geocode
144
161
  geo = self.geocode(query, country_code)
145
162
  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]
163
+ # different formats have different acceptance criteria
164
+ q = nil
165
+ case format
166
+ when :icao
167
+ return nil unless geo && geo.address && geo.country
168
+ q = "#{geo.address}, #{geo.country}"
169
+ else
170
+ return nil unless geo && geo.locality && geo.region && geo.country
171
+ q = "#{geo.locality}, #{geo.region}, #{geo.country}"
172
+ end
173
+ return [q, country_code, geo]
148
174
  else
149
175
  # without geocoding, the best we can do is just make use the given query as
150
176
  # the query for the "geocode" format
@@ -207,7 +233,6 @@ module Barometer
207
233
  },
208
234
  :format => :xml
209
235
  )['kml']['Response']
210
- #puts location.inspect
211
236
  geo = Barometer::Geo.new(location)
212
237
  end
213
238
 
@@ -223,6 +248,30 @@ module Barometer
223
248
  end
224
249
  country_code
225
250
  end
251
+
252
+ # todo, the fist letter in a 4-letter icao can designate country:
253
+ # c=canada
254
+ # k=usa
255
+ # etc...
256
+ # def self.icao_to_country_code(icao_code)
257
+ # return unless icao_code.is_a?(String)
258
+ # country_code = nil
259
+ # if icao_code.size == 4
260
+ # case icao_code.first_letter
261
+ # when "C"
262
+ # country_code = "CA"
263
+ # when "K"
264
+ # country_code = "US"
265
+ # end
266
+ # if coutry_code.nil?
267
+ # case icao_code.first_two_letters
268
+ # when "ET"
269
+ # country_code = "GERMANY"
270
+ # end
271
+ # end
272
+ # end
273
+ # country_code
274
+ # end
226
275
 
227
276
  end
228
277
  end
@@ -39,7 +39,7 @@ module Barometer
39
39
  class Wunderground < Service
40
40
 
41
41
  def self.accepted_formats
42
- [:zipcode, :postalcode, :coordinates, :geocode]
42
+ [:zipcode, :postalcode, :icao, :coordinates, :geocode]
43
43
  end
44
44
 
45
45
  def self.source_name
@@ -48,8 +48,9 @@ module Barometer
48
48
 
49
49
  # these are the icon codes that indicate "wet", used by wet? function
50
50
  def self.wet_icon_codes
51
- %w(flurries rain sleet snow tstorms nt_flurries nt_rain nt_sleet nt_snow nt_tstorms)
51
+ %w(flurries rain sleet snow tstorms nt_flurries nt_rain nt_sleet nt_snow nt_tstorms chancerain)
52
52
  end
53
+ # these are the icon codes that indicate "sun", used by sunny? function
53
54
  def self.sunny_icon_codes
54
55
  %w(clear mostlysunny partlysunny sunny partlycloudy)
55
56
  end
@@ -34,17 +34,9 @@ module Barometer
34
34
  # Quick access methods
35
35
  #
36
36
 
37
- def current
38
- (default = self.default) ? default.current : nil
39
- end
40
-
41
- def forecast
42
- (default = self.default) ? default.forecast : nil
43
- end
44
-
45
- def now
46
- self.current
47
- end
37
+ def current; (default = self.default) ? default.current : nil; end
38
+ def forecast; (default = self.default) ? default.forecast : nil; end
39
+ def now; self.current; end
48
40
 
49
41
  def today
50
42
  default = self.default
@@ -72,37 +64,48 @@ module Barometer
72
64
  # averages
73
65
  #
74
66
 
75
- # average of all humidity values
76
- # def humidity
77
- # end
78
- #
79
- # # average of all temperature values
80
- # def temperature
81
- # end
82
- #
83
- # # average of all wind speed values
84
- # def wind
85
- # end
86
- #
87
- # # average of all pressure values
88
- # def pressure
89
- # end
90
- #
91
- # # average of all dew_point values
92
- # def dew_point
93
- # end
94
- #
95
- # # average of all heat_index values
96
- # def heat_index
97
- # end
98
- #
99
- # # average of all wind_chill values
100
- # def wind_chill
101
- # end
102
- #
103
- # # average of all visibility values
104
- # def visibility
105
- # end
67
+ # TODO: not tested (except via averages)
68
+ def metric?
69
+ self.default ? self.default.metric? : true
70
+ end
71
+
72
+ # TODO: not tested (except via averages)
73
+ # this assumes calculating for current, and that "to_f" for a value
74
+ # will return the value needed
75
+ # value_name = the name of the value we are averaging
76
+ def current_average(value_name)
77
+ values = []
78
+ @measurements.each do |measurement|
79
+ values << measurement.current.send(value_name).to_f if measurement.success?
80
+ end
81
+ return nil unless values && values.size > 0
82
+ values.inject(0.0) { |sum,v| sum += v } / values.size
83
+ end
84
+
85
+ # TODO: not tested (except via averages)
86
+ def average(value_name, do_average=true, class_name=nil)
87
+ if class_name
88
+ if do_average
89
+ avg = Barometer.const_get(class_name).new(self.metric?)
90
+ avg << self.current_average(value_name)
91
+ else
92
+ avg = self.now.send(value_name)
93
+ end
94
+ else
95
+ avg = (do_average ? self.current_average(value_name) : self.now.send(value_name))
96
+ end
97
+ avg
98
+ end
99
+
100
+ # average of all values
101
+ def humidity(do_average=true); average("humidity",do_average); end
102
+ def temperature(do_average=true); average("temperature",do_average,"Temperature"); end
103
+ def wind(do_average=true); average("wind",do_average,"Speed"); end
104
+ def pressure(do_average=true); average("pressure",do_average,"Pressure"); end
105
+ def dew_point(do_average=true); average("dew_point",do_average,"Temperature"); end
106
+ def heat_index(do_average=true); average("heat_index",do_average,"Temperature"); end
107
+ def wind_chill(do_average=true); average("wind_chill",do_average,"Temperature"); end
108
+ def visibility(do_average=true); average("visibility",do_average,"Distance"); end
106
109
 
107
110
  #
108
111
  # quick access methods
@@ -0,0 +1,254 @@
1
+ body {
2
+ color:#000;
3
+ font-family:'lucida grande', 'lucida sans unicade', sans-serif;
4
+ font-size:100%;
5
+ line-height:1.3;
6
+ background-color:#fff;
7
+ margin:0;
8
+ padding:0;
9
+ }
10
+
11
+ /* HEADER */
12
+ #head {
13
+ text-align:center;
14
+ }
15
+ #head img {
16
+ vertical-align:middle;
17
+ margin:-10px 0 0 -82px;
18
+ padding:0 15px 0 0;
19
+ border:0;
20
+ }
21
+ #head h1 {
22
+ color:#000;
23
+ font-family:'georgia', 'bitstream vera serif', serif;
24
+ font-size:4em;
25
+ font-weight:normal;
26
+ letter-spacing:-3px;
27
+ line-height:1;
28
+ margin:0;
29
+ padding:20px 0 8px 0;
30
+ }
31
+ #head h1 a, #head h1 a:link, #head h1 a:visited, #head h1 a:hover {
32
+ color:#100;
33
+ text-decoration:none;
34
+ }
35
+ #head ul {
36
+ font-size:1.1em;
37
+ font-family:"lucida console", "monaco", "andale mono", "bitstream vera sans mono",
38
+ "consolas", monospace;
39
+ font-weight:normal;
40
+ text-transform:uppercase;
41
+ letter-spacing:2px;
42
+ text-align:center;
43
+ margin:0;
44
+ padding:0 40px 0.25em 40px;
45
+ }
46
+ #head ul li {
47
+ display:inline;
48
+ list-style-type:none;
49
+ padding:0 0.5em 0 0;
50
+ }
51
+ #head ul a, #head ul a:link, #head ul a:visited {
52
+ color:#56534f;
53
+ text-decoration:none;
54
+ }
55
+ #head ul a:hover {
56
+ color:#000;
57
+ text-decoration:underline
58
+ }
59
+
60
+ /* CONTENT */
61
+
62
+ #content {
63
+ color:#333;
64
+ font-size:1.1em;
65
+ max-width:44em;
66
+ min-width:27em;
67
+ margin:7px auto;
68
+ border-top:2px solid #837d7c;
69
+ }
70
+ strong {
71
+ font-weight:bold;
72
+ }
73
+
74
+ /* HEADINGS */
75
+
76
+ h1, h2, h3, h4, h5, h6 {
77
+ color:#000;
78
+ }
79
+ h1, h2 {
80
+ color:#211d1f;
81
+ font-family:'georgia', 'bitstream vera serif', serif;
82
+ font-weight:normal;
83
+ }
84
+ h1 a, h2 a { color:#222 }
85
+ h1 {
86
+ font-size:2.25em;
87
+ letter-spacing:-1px;
88
+ margin-bottom:0;
89
+ }
90
+ h2 {
91
+ font-size:1.5em;
92
+ letter-spacing:-1px;
93
+ margin:1.25em 0 -0.32em 0;
94
+ }
95
+ h3 {
96
+ color:#211d1f;
97
+ font-size:1.02em;
98
+ font-weight:bold;
99
+ margin:1.8em 0 -0.25em 0;
100
+ letter-spacing:-1px;
101
+ }
102
+ h3 a {
103
+ color:#000;
104
+ text-decoration:underline;
105
+ }
106
+
107
+ /* LINKS */
108
+ a {
109
+ color:#000;
110
+ text-decoration:underline;
111
+ }
112
+ a:hover {
113
+ color:#910;
114
+ text-decoration:underline;
115
+ }
116
+ a img {
117
+ border:none;
118
+ }
119
+ a.out {
120
+ background: url('/images/link-out.gif') center right no-repeat;
121
+ padding-right: 12px;
122
+ }
123
+
124
+
125
+ /* CODE */
126
+
127
+ code, pre, textarea, tt {
128
+ font-family:"lucida console", "monaco", "andale mono", "bitstream vera sans mono",
129
+ "consolas", monospace;
130
+ }
131
+ pre {
132
+ font-size:0.85em;
133
+ background:#f4f5f5;
134
+ border:2px solid #d5d0d2;
135
+ padding:0.5em;
136
+ line-height:1.15;
137
+ color:#222;
138
+ }
139
+ code, tt {
140
+ font-size:0.85em;
141
+ color:#444;
142
+ }
143
+ h1 code, h2 code, h3 code, h4 code {
144
+ color:#333;
145
+ }
146
+ pre code {
147
+ font-size:1em;
148
+ color:#222;
149
+ }
150
+
151
+ /* MISC */
152
+
153
+ hr {
154
+ margin:3em 0;
155
+ padding:0;
156
+ color:#eee;
157
+ }
158
+
159
+ /* HOME */
160
+
161
+ body#home {
162
+ background: #fff url(../images/legend.gif) no-repeat fixed right 130px;
163
+ }
164
+ #home #content {
165
+ font-size:1.05em;
166
+ margin:20px 0 0 0;
167
+ padding:0 20px;
168
+ border-width:0;
169
+ }
170
+ #home #head, #home #foot {
171
+ display:none;
172
+ }
173
+ .pipe {
174
+ clear:both;
175
+ padding:50px 0;
176
+ }
177
+ .pipe p {
178
+ color:#000;
179
+ font-size:62px;
180
+ font-family:'georgia', 'bitstream vera serif', serif;
181
+ letter-spacing:-3px;
182
+ line-height:1;
183
+ text-align:right;
184
+ margin:0em 43% 0 0;
185
+ padding:0 60px 0 0;
186
+ }
187
+ .pipe pre {
188
+ float:right;
189
+ background:transparent;
190
+ font-size:28px;
191
+ margin:0;
192
+ padding:5px 0 0 0;
193
+ width:42%;
194
+ border-width:0;
195
+ }
196
+ .pipe pre code {
197
+ color:#666;
198
+ background:transparent;
199
+ }
200
+ .pipe.shell p {
201
+ font-size:56px;
202
+ }
203
+ .pipe.shell pre {
204
+ font-size:18px;
205
+ }
206
+ .pipe.nav p {
207
+ font-size:100px;
208
+ letter-spacing:-6px;
209
+ }
210
+ .pipe.nav pre {
211
+ font-size:36px;
212
+ padding-top:20px;
213
+ text-transform:uppercase;
214
+ letter-spacing:4px;
215
+ line-height:1.25;
216
+ }
217
+ .pipe.nav {
218
+ padding:25px 0 50px 0;
219
+ }
220
+ .pipe.nav a, .pipe.nav a:link, .pipe.nav a:visited {
221
+ color:#444;
222
+ text-decoration:none;
223
+ }
224
+ .pipe.nav a:hover {
225
+ color:#000;
226
+ text-decoration:underline;
227
+ }
228
+
229
+ form {
230
+ text-align: center;
231
+ }
232
+
233
+ form .text-input input {
234
+ font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Helvetica, Arial, sans-serif;
235
+ margin: 10px 0 5px 10px;
236
+ color: #686868;
237
+ font-size: 40px;
238
+ text-align: left;
239
+ text-transform: lowercase;
240
+ }
241
+
242
+ form .submit-input input {
243
+ position: relative;
244
+ margin: 0 7% 0 0;
245
+ float: right;
246
+ }
247
+
248
+ form .options-input {
249
+ position: relative;
250
+ margin: 0 7% 0 0;
251
+ float: left;
252
+ text-align: left;
253
+ }
254
+