nationalweather 0.1.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/lib/nationalweather/conditions.rb +34 -0
- data/lib/nationalweather/current.rb +105 -0
- data/lib/nationalweather/day.rb +10 -0
- data/lib/nationalweather/forecast.rb +161 -0
- data/lib/nationalweather/hazard.rb +22 -0
- data/lib/nationalweather.rb +60 -0
- data/test/test_current.rb +44 -0
- data/test/test_forecast.rb +152 -0
- metadata +57 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
module NationalWeather
|
|
3
|
+
|
|
4
|
+
class Conditions
|
|
5
|
+
|
|
6
|
+
attr_reader :summary, :values;
|
|
7
|
+
|
|
8
|
+
def initialize(summary, values)
|
|
9
|
+
@summary = summary
|
|
10
|
+
@values = values
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_s
|
|
14
|
+
s = @summary
|
|
15
|
+
vals = Array.new
|
|
16
|
+
if @values != nil
|
|
17
|
+
@values.each do |v|
|
|
18
|
+
# TODO: handle "none" for intensity, ex: "patchy none fog"
|
|
19
|
+
# TODO: handle "qualifier"
|
|
20
|
+
if v.has_key?('additive')
|
|
21
|
+
vals.push("#{v['additive']} #{v['coverage']} #{v['intensity']} #{v['weather-type']}")
|
|
22
|
+
else
|
|
23
|
+
vals.push("#{v['coverage']} #{v['intensity']} #{v['weather-type']}")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
s +' (' + vals.join(' ') + ')'
|
|
27
|
+
else
|
|
28
|
+
s
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
|
|
2
|
+
module NationalWeather
|
|
3
|
+
|
|
4
|
+
class Current
|
|
5
|
+
|
|
6
|
+
# if the xml is invalid, subsequent calls to retrieve values will return nil
|
|
7
|
+
def initialize(xml_string)
|
|
8
|
+
@xml = REXML::Document.new xml_string
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def temperature_f
|
|
12
|
+
value("/current_observation/temp_f").text.to_f
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def temperature_c
|
|
16
|
+
value("/current_observation/temp_c").text.to_f
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def dewpoint_f
|
|
20
|
+
value("/current_observation/dewpoint_f").text.to_f
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def dewpoint_c
|
|
24
|
+
value("/current_observation/dewpoint_c").text.to_f
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def pressure_mb
|
|
28
|
+
value("/current_observation/pressure_mb").text.to_f
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def pressure_inhg
|
|
32
|
+
value("/current_observation/pressure_in").text.to_f
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def relative_humidity
|
|
36
|
+
value("/current_observation/relative_humidity").text.to_i
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def visibility_miles
|
|
40
|
+
value("/current_observation/visibility_mi").text.to_f
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def wind_speed_mph
|
|
44
|
+
value("/current_observation/wind_mph").text.to_f
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def wind_speed_knots
|
|
48
|
+
value("/current_observation/wind_kt").text.to_f
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def wind_degrees
|
|
52
|
+
value("/current_observation/wind_degrees").text.to_i
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def wind_direction
|
|
56
|
+
value("/current_observation/wind_dir").text
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def wind_string
|
|
60
|
+
value("/current_observation/wind_string").text
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def weather
|
|
64
|
+
value("/current_observation/weather").text
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def icon
|
|
68
|
+
base = value("/current_observation/icon_url_base").text
|
|
69
|
+
name = value("/current_observation/icon_url_name").text
|
|
70
|
+
base + name
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def icon_name
|
|
74
|
+
value("/current_observation/icon_url_name").text
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def station_id
|
|
78
|
+
value("/current_observation/station_id").text
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def location
|
|
82
|
+
value("/current_observation/location").text
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def latitude
|
|
86
|
+
value("/current_observation/latitude").text.to_f
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def longitude
|
|
90
|
+
value("/current_observation/longitude").text.to_f
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def time
|
|
94
|
+
value("/current_observation/observation_time_rfc822").text
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def value(xpath_string)
|
|
100
|
+
REXML::XPath.first(@xml, xpath_string)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
|
|
2
|
+
require 'time'
|
|
3
|
+
|
|
4
|
+
module NationalWeather
|
|
5
|
+
|
|
6
|
+
class Forecast
|
|
7
|
+
|
|
8
|
+
def initialize(xml_string)
|
|
9
|
+
@xml = REXML::Document.new xml_string
|
|
10
|
+
@values = Hash.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def day(index)
|
|
14
|
+
days[index]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def days
|
|
18
|
+
if !@values.has_key?('days')
|
|
19
|
+
days = Array.new
|
|
20
|
+
length.times do |i|
|
|
21
|
+
d = NationalWeather::Day.new
|
|
22
|
+
d.high = high_temperatures[i]
|
|
23
|
+
d.low = low_temperatures[i]
|
|
24
|
+
d.start_time = start_times[i]
|
|
25
|
+
d.end_time = end_times[i]
|
|
26
|
+
d.conditions = conditions[i]
|
|
27
|
+
d.icon = icons[i]
|
|
28
|
+
d.precipitation_probability_day = precipitation_probabilities[i*2]
|
|
29
|
+
d.precipitation_probability_night = precipitation_probabilities[i*2+1]
|
|
30
|
+
days.push(d)
|
|
31
|
+
end
|
|
32
|
+
@values['days'] = days
|
|
33
|
+
end
|
|
34
|
+
@values['days']
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def length
|
|
38
|
+
start_times.length
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def high_temperatures
|
|
42
|
+
values('/dwml/data[1]/parameters[1]/temperature[@type="maximum"][1]/value', 'high_temperatures') {|node| node.to_i }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def low_temperatures
|
|
46
|
+
values('/dwml/data[1]/parameters[1]/temperature[@type="minimum"][1]/value', 'low_temperatures') {|node| node.to_i }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def precipitation_probabilities
|
|
50
|
+
values('/dwml/data[1]/parameters[1]/probability-of-precipitation[1]/value', 'precipitation_probabilities') {|node| node.to_i }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def start_times
|
|
54
|
+
values('/dwml/data[1]/time-layout[@summarization="24hourly"][1]/start-valid-time', 'start_times') {|node| Time.parse(node) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def end_times
|
|
58
|
+
values('/dwml/data[1]/time-layout[@summarization="24hourly"][1]/end-valid-time', 'end_times') {|node| Time.parse(node) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def icons
|
|
62
|
+
values('/dwml/data[1]/parameters[1]/conditions-icon[1]/icon-link', 'icons')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns any Hazards (Watches, Warnings, and Advisories) for the forecast time period.
|
|
66
|
+
#
|
|
67
|
+
# SINGLE:
|
|
68
|
+
# <hazard hazardCode="LW.Y" phenomena="Lake Wind" significance="Advisory" hazardType="long duration">
|
|
69
|
+
# <hazardTextURL>http://forecast.weather.gov/wwamap/wwatxtget.php?cwa=usa&wwa=Lake%20Wind%20Advisory</hazardTextURL>
|
|
70
|
+
# </hazard>
|
|
71
|
+
#
|
|
72
|
+
# EMPTY:
|
|
73
|
+
# <hazard-conditions xsi:nil="true"/>
|
|
74
|
+
def hazards
|
|
75
|
+
if !@values.has_key?('hazards')
|
|
76
|
+
@values['hazards'] = REXML::XPath.match(@xml, '/dwml/data[1]/parameters[1]/hazards[1]/hazard-conditions[1]/hazard').map {|node|
|
|
77
|
+
# handle empty nodes like <hazards-conditions xsi:nil="true" />
|
|
78
|
+
if node.has_elements?
|
|
79
|
+
code = node.attributes["hazardCode"]
|
|
80
|
+
phenomena = node.attributes["phenomena"]
|
|
81
|
+
significance = node.attributes["significance"]
|
|
82
|
+
type = node.attributes["hazardType"]
|
|
83
|
+
url = node.get_elements("hazardTextURL")[0].text
|
|
84
|
+
NationalWeather::Hazard.new(code, phenomena, significance, type, url)
|
|
85
|
+
else
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
@values['hazards']
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Returns all Conditions objects for this forecast
|
|
94
|
+
#
|
|
95
|
+
# MULTIPLE:
|
|
96
|
+
# <weather-conditions weather-summary="Chance Rain Showers">
|
|
97
|
+
# <value coverage="chance" intensity="light" weather-type="rain showers" qualifier="none"/>
|
|
98
|
+
# <value coverage="patchy" intensity="none" additive="and" weather-type="fog" qualifier="none"/>
|
|
99
|
+
# </weather-conditions>
|
|
100
|
+
#
|
|
101
|
+
# EMPTY:
|
|
102
|
+
# <weather-conditions weather-summary="Partly Sunny"/>
|
|
103
|
+
#
|
|
104
|
+
# SINGLE:
|
|
105
|
+
# <weather-conditions weather-summary="Chance Rain Showers">
|
|
106
|
+
# <value coverage="chance" intensity="light" weather-type="rain showers" qualifier="none"/>
|
|
107
|
+
# </weather-conditions>
|
|
108
|
+
#
|
|
109
|
+
def conditions
|
|
110
|
+
if !@values.has_key?('conditions')
|
|
111
|
+
allConditions = Array.new
|
|
112
|
+
@values['conditions'] = REXML::XPath.match(@xml, '/dwml/data[1]/parameters[1]/weather[1]/weather-conditions').map {|node|
|
|
113
|
+
# handle weather-conditions with child values
|
|
114
|
+
if node.has_elements?
|
|
115
|
+
# Array to hold <value> attributes
|
|
116
|
+
cValues = Array.new
|
|
117
|
+
# gather the attributes of each <value> into a Hash
|
|
118
|
+
node.get_elements("value").each do |v|
|
|
119
|
+
atts = Hash.new
|
|
120
|
+
v.attributes.each do |k, v|
|
|
121
|
+
atts[k] = v.to_s
|
|
122
|
+
end
|
|
123
|
+
cValues.push(atts)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
allConditions.push(NationalWeather::Conditions.new(node.attributes["weather-summary"], cValues))
|
|
127
|
+
}
|
|
128
|
+
@values['conditions'] = allConditions
|
|
129
|
+
end
|
|
130
|
+
@values['conditions']
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
# Returns an Array of values for the given XPath query.
|
|
136
|
+
# A block to format each value String can be included with the call to this method.
|
|
137
|
+
# If no block is given the values in the Array will be Strings.
|
|
138
|
+
# This method cached the result so it doesn't need to query the XML and format the nodes each time it's called
|
|
139
|
+
def values(xpath_string, key)
|
|
140
|
+
if !@values.has_key?(key)
|
|
141
|
+
@values[key] = REXML::XPath.match(@xml, xpath_string).map {|node|
|
|
142
|
+
# handle empty nodes like <value xsi:nil="true" />
|
|
143
|
+
if node.has_text?
|
|
144
|
+
if block_given?
|
|
145
|
+
# format the String with the supplied block
|
|
146
|
+
yield(node.text)
|
|
147
|
+
else
|
|
148
|
+
# default: return a String
|
|
149
|
+
node.text
|
|
150
|
+
end
|
|
151
|
+
else
|
|
152
|
+
nil
|
|
153
|
+
end
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
@values[key]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
module NationalWeather
|
|
3
|
+
|
|
4
|
+
class Hazard
|
|
5
|
+
|
|
6
|
+
attr_reader :code, :phenomena, :significance, :type, :url;
|
|
7
|
+
|
|
8
|
+
def initialize(code, phenomena, significance, type, url)
|
|
9
|
+
@code = code
|
|
10
|
+
@phenomena = phenomena
|
|
11
|
+
@significance = significance
|
|
12
|
+
@type = type
|
|
13
|
+
@url = url
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_s
|
|
17
|
+
"#{@type} #{@phenomena} #{@significance}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'rexml/document'
|
|
5
|
+
require 'nationalweather/current'
|
|
6
|
+
require 'nationalweather/forecast'
|
|
7
|
+
require 'nationalweather/hazard'
|
|
8
|
+
require 'nationalweather/conditions'
|
|
9
|
+
require 'nationalweather/day'
|
|
10
|
+
|
|
11
|
+
module NationalWeather
|
|
12
|
+
|
|
13
|
+
VERSION = '0.1.0'
|
|
14
|
+
|
|
15
|
+
# Returns the current weather conditions at the station id specified, or nil if there was an error.
|
|
16
|
+
# For the station ID see: http://www.weather.gov/xml/current_obs/
|
|
17
|
+
# XML list of stations: http://www.weather.gov/xml/current_obs/index.xml
|
|
18
|
+
def NationalWeather::current(station_id)
|
|
19
|
+
xml = fetch("http://www.weather.gov/xml/current_obs/#{station_id}.xml")
|
|
20
|
+
NationalWeather::Current.new(xml)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns the forecast for the given location, or nil if there was an error.
|
|
24
|
+
# start_date expected in YYYY-MM-DD format
|
|
25
|
+
def NationalWeather::forecast(lat, lng, start_date, days)
|
|
26
|
+
xml = fetch("http://graphical.weather.gov/xml/sample_products/browser_interface/ndfdBrowserClientByDay.php?lat=#{lat.to_s}&lon=#{lng.to_s}&format=24+hourly&numDays=#{days.to_s}&startDate=#{start_date}")
|
|
27
|
+
NationalWeather::Forecast.new(xml)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# raised if there are too many redirects while fetching the weather response
|
|
31
|
+
class TooManyRedirectsError < StandardError
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# raised if the API server does not return an HTTP success code
|
|
35
|
+
class BadHTTPResponseError < StandardError
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Returns the XML response string for the given URL, following redirects along the way
|
|
41
|
+
def NationalWeather::fetch(uri_str, limit = 10)
|
|
42
|
+
|
|
43
|
+
# TODO: optional file cache
|
|
44
|
+
|
|
45
|
+
raise NationalWeather::TooManyRedirectsError, 'Too many HTTP redirects.' if limit == 0
|
|
46
|
+
|
|
47
|
+
response = Net::HTTP.get_response(URI(uri_str))
|
|
48
|
+
|
|
49
|
+
case response
|
|
50
|
+
when Net::HTTPSuccess then
|
|
51
|
+
response.body.to_s
|
|
52
|
+
when Net::HTTPRedirection then
|
|
53
|
+
location = response['location']
|
|
54
|
+
fetch(location, limit - 1)
|
|
55
|
+
else
|
|
56
|
+
raise NationalWeather::BadHTTPResponseError, "Bad return value: #{response.value}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require File.dirname(__FILE__) + '/../lib/nationalweather'
|
|
3
|
+
|
|
4
|
+
class TestCurrent < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_values
|
|
7
|
+
cw = NationalWeather::Current.new(File.new(File.dirname(__FILE__) + "/xml/current_KBAF.xml"))
|
|
8
|
+
|
|
9
|
+
assert_equal(62.0, cw.temperature_f)
|
|
10
|
+
assert_equal(16.7, cw.temperature_c)
|
|
11
|
+
|
|
12
|
+
assert_equal(50.0, cw.dewpoint_f)
|
|
13
|
+
assert_equal(10.0, cw.dewpoint_c)
|
|
14
|
+
|
|
15
|
+
assert_equal(1007.2, cw.pressure_mb)
|
|
16
|
+
assert_equal(29.74, cw.pressure_inhg)
|
|
17
|
+
|
|
18
|
+
assert_equal(65, cw.relative_humidity)
|
|
19
|
+
|
|
20
|
+
assert_equal(10.0, cw.visibility_miles);
|
|
21
|
+
|
|
22
|
+
assert_equal(0.0, cw.wind_speed_mph)
|
|
23
|
+
assert_equal(0.0, cw.wind_speed_knots)
|
|
24
|
+
assert_equal(0.0, cw.wind_degrees)
|
|
25
|
+
assert_equal("North", cw.wind_direction)
|
|
26
|
+
assert_equal("Calm", cw.wind_string)
|
|
27
|
+
|
|
28
|
+
assert_equal("Overcast", cw.weather)
|
|
29
|
+
|
|
30
|
+
assert_equal("http://w1.weather.gov/images/fcicons/ovc.jpg", cw.icon)
|
|
31
|
+
assert_equal("ovc.jpg", cw.icon_name)
|
|
32
|
+
|
|
33
|
+
assert_equal("KBAF", cw.station_id)
|
|
34
|
+
|
|
35
|
+
assert_equal("Westfield, Barnes Municipal Airport, MA", cw.location)
|
|
36
|
+
|
|
37
|
+
assert_equal(42.16, cw.latitude)
|
|
38
|
+
|
|
39
|
+
assert_equal(-72.72, cw.longitude)
|
|
40
|
+
|
|
41
|
+
assert_equal("Sun, 30 Sep 2012 14:53:00 -0400", cw.time)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require File.dirname(__FILE__) + '/../lib/nationalweather'
|
|
3
|
+
|
|
4
|
+
class TestForecast < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_7day
|
|
7
|
+
f = NationalWeather::Forecast.new(File.new(File.dirname(__FILE__) + "/xml/forecast_7day.xml"))
|
|
8
|
+
|
|
9
|
+
# length of forecast
|
|
10
|
+
assert_equal(7, f.length)
|
|
11
|
+
|
|
12
|
+
# high temps
|
|
13
|
+
expectedHighs = [38, 29, 33, 20, 37, 18, 14]
|
|
14
|
+
assert_equal(expectedHighs, f.high_temperatures)
|
|
15
|
+
|
|
16
|
+
# low temps
|
|
17
|
+
expectedLows = [17, 19, 20, 14, 14, 2, nil]
|
|
18
|
+
assert_equal(expectedLows, f.low_temperatures)
|
|
19
|
+
|
|
20
|
+
# precipitation
|
|
21
|
+
expectedPrecipitation = [27, 19, 56, 33, 30, 27, 20, 7, 10, 16, 26, 12, 11, nil]
|
|
22
|
+
assert_equal(expectedPrecipitation, f.precipitation_probabilities)
|
|
23
|
+
|
|
24
|
+
# icons
|
|
25
|
+
expectedIcons = [
|
|
26
|
+
'http://www.nws.noaa.gov/weather/images/fcicons/sn30.jpg',
|
|
27
|
+
'http://www.nws.noaa.gov/weather/images/fcicons/sn60.jpg',
|
|
28
|
+
'http://www.nws.noaa.gov/weather/images/fcicons/sn30.jpg',
|
|
29
|
+
'http://www.nws.noaa.gov/weather/images/fcicons/sn20.jpg',
|
|
30
|
+
'http://www.nws.noaa.gov/weather/images/fcicons/bkn.jpg',
|
|
31
|
+
'http://www.nws.noaa.gov/weather/images/fcicons/sn30.jpg',
|
|
32
|
+
'http://www.nws.noaa.gov/weather/images/fcicons/bkn.jpg'
|
|
33
|
+
]
|
|
34
|
+
assert_equal(expectedIcons, f.icons)
|
|
35
|
+
|
|
36
|
+
# start times
|
|
37
|
+
expectedStartTimes = [
|
|
38
|
+
'2008-12-05T06:00:00-07:00',
|
|
39
|
+
'2008-12-06T06:00:00-07:00',
|
|
40
|
+
'2008-12-07T06:00:00-07:00',
|
|
41
|
+
'2008-12-08T06:00:00-07:00',
|
|
42
|
+
'2008-12-09T06:00:00-07:00',
|
|
43
|
+
'2008-12-10T06:00:00-07:00',
|
|
44
|
+
'2008-12-11T06:00:00-07:00'
|
|
45
|
+
].map {|t| Time.parse(t) }
|
|
46
|
+
assert_equal(expectedStartTimes, f.start_times)
|
|
47
|
+
|
|
48
|
+
# end times
|
|
49
|
+
expectedEndTimes = [
|
|
50
|
+
'2008-12-06T06:00:00-07:00',
|
|
51
|
+
'2008-12-07T06:00:00-07:00',
|
|
52
|
+
'2008-12-08T06:00:00-07:00',
|
|
53
|
+
'2008-12-09T06:00:00-07:00',
|
|
54
|
+
'2008-12-10T06:00:00-07:00',
|
|
55
|
+
'2008-12-11T06:00:00-07:00',
|
|
56
|
+
'2008-12-12T06:00:00-07:00'
|
|
57
|
+
].map {|t| Time.parse(t) }
|
|
58
|
+
assert_equal(expectedEndTimes, f.end_times)
|
|
59
|
+
|
|
60
|
+
# hazards
|
|
61
|
+
expectedHazard = NationalWeather::Hazard.new(
|
|
62
|
+
"LW.Y",
|
|
63
|
+
"Lake Wind",
|
|
64
|
+
"Advisory",
|
|
65
|
+
"long duration",
|
|
66
|
+
"http://forecast.weather.gov/wwamap/wwatxtget.php?cwa=usa&wwa=Lake%20Wind%20Advisory"
|
|
67
|
+
)
|
|
68
|
+
# returns an array, but there's only one in this test
|
|
69
|
+
hazard = f.hazards[0]
|
|
70
|
+
assert_equal(expectedHazard.code, hazard.code)
|
|
71
|
+
assert_equal(expectedHazard.phenomena, hazard.phenomena)
|
|
72
|
+
assert_equal(expectedHazard.significance, hazard.significance)
|
|
73
|
+
assert_equal(expectedHazard.type, hazard.type)
|
|
74
|
+
assert_equal(expectedHazard.url, hazard.url)
|
|
75
|
+
assert_equal("long duration Lake Wind Advisory", hazard.to_s)
|
|
76
|
+
|
|
77
|
+
# conditions
|
|
78
|
+
expectedConditionsSummaries = [
|
|
79
|
+
'Snow Likely',
|
|
80
|
+
'Snow Likely',
|
|
81
|
+
'Chance Snow',
|
|
82
|
+
'Slight Chance Snow',
|
|
83
|
+
'Mostly Cloudy',
|
|
84
|
+
'Chance Snow',
|
|
85
|
+
'Mostly Cloudy'
|
|
86
|
+
]
|
|
87
|
+
expectedConditionsStrings = [
|
|
88
|
+
'Snow Likely (likely light snow)',
|
|
89
|
+
'Snow Likely (likely light snow)',
|
|
90
|
+
'Chance Snow (chance light snow)',
|
|
91
|
+
'Slight Chance Snow (slight chance light snow)',
|
|
92
|
+
'Mostly Cloudy',
|
|
93
|
+
'Chance Snow (chance light snow)',
|
|
94
|
+
'Mostly Cloudy'
|
|
95
|
+
]
|
|
96
|
+
i = 0
|
|
97
|
+
f.conditions.each do |c|
|
|
98
|
+
assert_equal(expectedConditionsSummaries[i], c.summary)
|
|
99
|
+
assert_equal(expectedConditionsStrings[i], c.to_s)
|
|
100
|
+
i += 1
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# days
|
|
104
|
+
7.times do |i|
|
|
105
|
+
day = f.day(i)
|
|
106
|
+
assert_not_nil(day)
|
|
107
|
+
assert_equal(expectedHighs[i], day.high)
|
|
108
|
+
assert_equal(expectedLows[i], day.low)
|
|
109
|
+
assert_equal(expectedIcons[i], day.icon)
|
|
110
|
+
assert_equal(expectedStartTimes[i], day.start_time)
|
|
111
|
+
assert_equal(expectedEndTimes[i], day.end_time)
|
|
112
|
+
assert_equal(expectedConditionsSummaries[i], day.conditions.summary)
|
|
113
|
+
assert_equal(expectedConditionsStrings[i], day.conditions.to_s)
|
|
114
|
+
assert_equal(expectedPrecipitation[i*2], day.precipitation_probability_day)
|
|
115
|
+
assert_equal(expectedPrecipitation[i*2+1], day.precipitation_probability_night)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def test_conditions
|
|
121
|
+
|
|
122
|
+
f = NationalWeather::Forecast.new(File.new(File.dirname(__FILE__) + "/xml/forecast_conditions.xml"))
|
|
123
|
+
|
|
124
|
+
# conditions
|
|
125
|
+
expectedConditionsSummaries = [
|
|
126
|
+
'Chance Rain Showers',
|
|
127
|
+
'Partly Sunny',
|
|
128
|
+
'Chance Rain Showers',
|
|
129
|
+
'Chance Rain Showers',
|
|
130
|
+
'Slight Chance Rain Showers',
|
|
131
|
+
'Slight Chance Rain Showers',
|
|
132
|
+
'Slight Chance Rain Showers'
|
|
133
|
+
]
|
|
134
|
+
expectedConditionsStrings = [
|
|
135
|
+
'Chance Rain Showers (chance light rain showers and patchy none fog)',
|
|
136
|
+
'Partly Sunny',
|
|
137
|
+
'Chance Rain Showers (chance light rain showers)',
|
|
138
|
+
'Chance Rain Showers (chance light rain showers)',
|
|
139
|
+
'Slight Chance Rain Showers (slight chance light rain showers)',
|
|
140
|
+
'Slight Chance Rain Showers (slight chance light rain showers)',
|
|
141
|
+
'Slight Chance Rain Showers (slight chance light rain showers)'
|
|
142
|
+
]
|
|
143
|
+
i = 0
|
|
144
|
+
f.conditions.each do |c|
|
|
145
|
+
assert_equal(expectedConditionsSummaries[i], c.summary)
|
|
146
|
+
assert_equal(expectedConditionsStrings[i], c.to_s)
|
|
147
|
+
i += 1
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: nationalweather
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Andrew M. Whalen
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-01-04 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: NationalWeather is a Ruby client library for the National Oceanic and
|
|
15
|
+
Atmospheric Administration's (NOAA) National Weather Service (NWS) forecast and
|
|
16
|
+
current weather REST web services.
|
|
17
|
+
email: nationalweather-ruby@amwhalen.com
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- lib/nationalweather.rb
|
|
23
|
+
- lib/nationalweather/conditions.rb
|
|
24
|
+
- lib/nationalweather/current.rb
|
|
25
|
+
- lib/nationalweather/day.rb
|
|
26
|
+
- lib/nationalweather/forecast.rb
|
|
27
|
+
- lib/nationalweather/hazard.rb
|
|
28
|
+
- test/test_current.rb
|
|
29
|
+
- test/test_forecast.rb
|
|
30
|
+
homepage: http://rubygems.org/gems/nationalweather
|
|
31
|
+
licenses:
|
|
32
|
+
- MIT
|
|
33
|
+
post_install_message:
|
|
34
|
+
rdoc_options: []
|
|
35
|
+
require_paths:
|
|
36
|
+
- lib
|
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
38
|
+
none: false
|
|
39
|
+
requirements:
|
|
40
|
+
- - ! '>='
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: 1.8.6
|
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
|
+
none: false
|
|
45
|
+
requirements:
|
|
46
|
+
- - ! '>='
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '0'
|
|
49
|
+
requirements: []
|
|
50
|
+
rubyforge_project:
|
|
51
|
+
rubygems_version: 1.8.24
|
|
52
|
+
signing_key:
|
|
53
|
+
specification_version: 3
|
|
54
|
+
summary: Client library for NOAA's forecast and current weather services.
|
|
55
|
+
test_files:
|
|
56
|
+
- test/test_current.rb
|
|
57
|
+
- test/test_forecast.rb
|