barometer 0.1.0 → 0.2.1

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.
@@ -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
+