noaaer 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/bin/noaa-update-stations +13 -0
- data/lib/noaaer/current_conditions.rb +185 -0
- data/lib/noaaer/forecast.rb +108 -0
- data/lib/noaaer/forecast_day.rb +38 -0
- data/lib/noaaer/http_service.rb +21 -0
- data/lib/noaaer/station.rb +111 -0
- data/lib/noaaer/station_writer.rb +26 -0
- data/lib/noaaer/version.rb +3 -0
- data/lib/noaaer.rb +57 -0
- data/noaaer.gemspec +21 -0
- metadata +69 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
raise 'hi'
|
4
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'noaaer')
|
5
|
+
|
6
|
+
begin
|
7
|
+
file = File.open(File.join(File.dirname(__FILE__), '..', 'data', 'stations.yml'), 'w')
|
8
|
+
puts 'Downloading station list from NOAA...'
|
9
|
+
doc = Noaaer::HttpService.new.get_station_list
|
10
|
+
puts 'Converting station list to YAML...'
|
11
|
+
Noaaer::StationWriter.new(doc).write(file)
|
12
|
+
puts "Done."
|
13
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module Noaaer
|
2
|
+
#
|
3
|
+
# Representation of the current conditions for a given observation point.
|
4
|
+
#
|
5
|
+
class CurrentConditions
|
6
|
+
|
7
|
+
class <<self
|
8
|
+
private :new
|
9
|
+
|
10
|
+
def from_xml(doc) #:nodoc:
|
11
|
+
new(doc)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(doc) #:notnew:
|
16
|
+
@doc = doc
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Time object containing the time at which these conditions were observed at the NOAA station
|
21
|
+
#
|
22
|
+
def observed_at
|
23
|
+
@observed_at ||= Time.parse(text_from_node('observation_time_rfc822'))
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Text description of the current weather conditions, e.g. "Fair"
|
28
|
+
#
|
29
|
+
def weather_description
|
30
|
+
@weather_description ||= text_from_node('weather')
|
31
|
+
end
|
32
|
+
alias_method :weather_summary, :weather_description
|
33
|
+
|
34
|
+
#
|
35
|
+
# NWS code representing weather type. This distills the #weather_description
|
36
|
+
# into one of a much more manageable set of possibilities. Possible values are:
|
37
|
+
#
|
38
|
+
# - <code>:skc</code> - Clear
|
39
|
+
# - <code>:wind</code> - Windy
|
40
|
+
# - <code>:few</code> - A Few Clouds
|
41
|
+
# - <code>:sct</code> - Partly Cloudy
|
42
|
+
# - <code>:bkn</code> - Mostly Cloudy
|
43
|
+
# - <code>:ovc</code> - Overcast
|
44
|
+
# - <code>:ra1</code> - Light Rain
|
45
|
+
# - <code>:ra</code> - Rain
|
46
|
+
# - <code>:shra</code> - Rain Showers
|
47
|
+
# - <code>:tsra</code> - Thunderstorms
|
48
|
+
# - <code>:ip</code> - Hail
|
49
|
+
# - <code>:fzra</code> - Freezing Rain
|
50
|
+
# - <code>:mix</code> - Wintry Mix
|
51
|
+
# - <code>:sn</code> - Snow
|
52
|
+
# - <code>:fg</code> - Fog
|
53
|
+
# - <code>:smoke</code> - Smoke
|
54
|
+
# - <code>:dust</code> - Dust/Sand
|
55
|
+
# - <code>:mist</code> - Haze
|
56
|
+
# - <code>:svrtsra</code> - Tornado
|
57
|
+
# - <code>:fzrara</code> - Freezing Rain/Rain
|
58
|
+
# - <code>:raip</code> - Rain/Hail
|
59
|
+
# - <code>:rasn</code> - Rain/Snow
|
60
|
+
# - <code>:hi_shwrs</code> - Showers in Vicinity
|
61
|
+
# - <code>:hi_tsra</code> - Thunderstorm in Vicinity
|
62
|
+
#
|
63
|
+
# See http://www.weather.gov/xml/current_obs/weather.php for the NWS's list of possible
|
64
|
+
# descriptions and their type codes.
|
65
|
+
#
|
66
|
+
def weather_type_code
|
67
|
+
@weather_type_code ||= text_from_node('icon_url_name').gsub(/^n|\.jpg$/, '').to_sym
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Return the NWS image URL for the current weather as string
|
72
|
+
#
|
73
|
+
def image_url
|
74
|
+
@image_url ||= "#{text_from_node('icon_url_base')}#{text_from_node('icon_url_name')}"
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# The current temperature in the requested units.
|
79
|
+
#
|
80
|
+
# conditions.temperature #=> temperature in fahrenheit
|
81
|
+
# conditions.temperature(:c) #=> temperature in celsius
|
82
|
+
# conditions.temperature(:kelvin) #=> anything else raises an exception
|
83
|
+
#
|
84
|
+
def temperature(unit = :f)
|
85
|
+
text_from_node_with_unit('temp', unit, :f, :c).to_i
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# The current relative humidity percentage (0-100)
|
90
|
+
#
|
91
|
+
def relative_humidity
|
92
|
+
text_from_node('relative_humidity').to_i
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# The current cardinal or ordinal direction that the wind is coming from (e.g., "Northwest")
|
97
|
+
#
|
98
|
+
def wind_direction
|
99
|
+
text_from_node('wind_dir')
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# The current direction that the wind is coming from degrees (e.g. 330)
|
104
|
+
#
|
105
|
+
def wind_degrees
|
106
|
+
text_from_node('wind_degrees').to_i
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# The current wind speed in miles per hour as a float (e.g., 3.45)
|
111
|
+
#
|
112
|
+
def wind_speed
|
113
|
+
text_from_node('wind_mph').to_f
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# The current wind gust in miles per hour as a float, or nil if none
|
118
|
+
#
|
119
|
+
def wind_gust
|
120
|
+
text_from_node('wind_gust_mph').to_f
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# The current barometric pressure
|
125
|
+
#
|
126
|
+
# conditions.pressure #=> pressure in inches
|
127
|
+
# conditions.pressure(:mb) #=> pressure in millibars
|
128
|
+
# conditions.pressure(:psi) #=> anything else raises an exception
|
129
|
+
#
|
130
|
+
def pressure(unit = :in)
|
131
|
+
text_from_node_with_unit('pressure', unit, :in, :mb).to_f
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# The current dew point.
|
136
|
+
#
|
137
|
+
# conditions.dew_point #=> dew point in fahrenheit
|
138
|
+
# conditions.dew_point(:c) #=> dew point in celsius
|
139
|
+
# conditions.dew_point(:kelvin) #=> anything else raises an exception
|
140
|
+
#
|
141
|
+
def dew_point(unit = :f)
|
142
|
+
text_from_node_with_unit('dewpoint', unit, :f, :c).to_i
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# The current heat index
|
147
|
+
#
|
148
|
+
# conditions.heat_index #=> heat index in fahrenheit
|
149
|
+
# conditions.heat_index(:c) #=> heat index in celsius
|
150
|
+
# conditions.heat_index(:kelvin) #=> anything else raises an exception
|
151
|
+
#
|
152
|
+
def heat_index(unit = :f)
|
153
|
+
text_from_node_with_unit('heat_index', unit, :f, :c).to_i
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# The current wind chill
|
158
|
+
#
|
159
|
+
# conditions.wind_chill #=> wind chill in fahrenheit
|
160
|
+
# conditions.wind_chill(:c) #=> wind chill in celsius
|
161
|
+
# conditions.wind_chill(:kelvin) #=> anything else raises an exception
|
162
|
+
#
|
163
|
+
def wind_chill(unit = :f)
|
164
|
+
text_from_node_with_unit('windchill', unit, :f, :c).to_i
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# The current visibility in miles
|
169
|
+
#
|
170
|
+
def visibility
|
171
|
+
text_from_node('visibility_mi').to_f
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def text_from_node(element_name)
|
177
|
+
@doc.xpath("/current_observation/#{element_name}[1]/child::text()").first.to_s
|
178
|
+
end
|
179
|
+
|
180
|
+
def text_from_node_with_unit(element_name, unit, *allowed_units)
|
181
|
+
raise ArgumentError, "Unknown unit #{unit.inspect} - allowed units are #{allowed_units.inspect}" unless allowed_units.include?(unit)
|
182
|
+
text_from_node("#{element_name}_#{unit}")
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Noaaer
|
2
|
+
#
|
3
|
+
# A Forecast object represents a multi-day forecast for a particular place. The forecast for a given day can
|
4
|
+
# be accessed using the [] method; e.g. (assuming +forecast+ is a forecast for 12/20/2008 - 12/24/2008):
|
5
|
+
#
|
6
|
+
# forecast[1] #=> ForecastDay for 12/21/2008
|
7
|
+
# forecast.length #=> 4
|
8
|
+
#
|
9
|
+
class Forecast
|
10
|
+
|
11
|
+
class <<self
|
12
|
+
private :new
|
13
|
+
|
14
|
+
def from_xml(doc) #:nodoc:
|
15
|
+
new(doc)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(doc) #:noinit:
|
20
|
+
@doc = doc
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# The number of days provided by the forecast
|
25
|
+
#
|
26
|
+
def length
|
27
|
+
@length ||= @doc.find(%q{/dwml/data/time-layout[@summarization='24hourly'][1]/start-valid-time}).length
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Get the ForecastDay for day i
|
32
|
+
#
|
33
|
+
# forecast[1] #=> returns the ForecastDay for the second day
|
34
|
+
#
|
35
|
+
def [](i)
|
36
|
+
forecast_days[i]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def forecast_days
|
42
|
+
@forecast_days ||= begin
|
43
|
+
days = []
|
44
|
+
length.times do |i|
|
45
|
+
days << day = NOAA::ForecastDay.new
|
46
|
+
day.starts_at = starts[i]
|
47
|
+
day.ends_at = ends[i]
|
48
|
+
day.high = maxima[i]
|
49
|
+
day.low = minima[i]
|
50
|
+
day.weather_summary = weather_summaries[i]
|
51
|
+
day.weather_type_code = weather_type_codes[i]
|
52
|
+
day.image_url = image_urls[i]
|
53
|
+
day.daytime_precipitation_probability = precipitation_probabilities[i*2]
|
54
|
+
day.evening_precipitation_probability = precipitation_probabilities[i*2+1]
|
55
|
+
end
|
56
|
+
days
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def starts
|
61
|
+
@starts ||= @doc.find(%q{/dwml/data/time-layout[@summarization='24hourly'][1]/start-valid-time/text()}).map do |node|
|
62
|
+
Time.parse(node.to_s)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def ends
|
67
|
+
@ends ||= @doc.find(%q{/dwml/data/time-layout[@summarization='24hourly'][1]/end-valid-time/text()}).map do |node|
|
68
|
+
Time.parse(node.to_s)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def maxima
|
73
|
+
@maxima ||= @doc.find(%q{/dwml/data/parameters[1]/temperature[@type='maximum'][@units='Fahrenheit'][1]/value/text()}).map do |node|
|
74
|
+
node.to_s.to_i
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def minima
|
79
|
+
@minima ||= @doc.find(%q{/dwml/data/parameters[1]/temperature[@type='minimum'][@units='Fahrenheit'][1]/value/text()}).map do |node|
|
80
|
+
node.to_s.to_i
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def weather_summaries
|
85
|
+
@weather_summaries ||= @doc.find(%q{/dwml/data/parameters[1]/weather[1]/weather-conditions}).map do |node|
|
86
|
+
node['weather-summary'].to_s
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def image_urls
|
91
|
+
@image_urls ||= @doc.find(%q{/dwml/data/parameters[1]/conditions-icon/icon-link/text()}).map do |node|
|
92
|
+
node.to_s
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def weather_type_codes
|
97
|
+
@weather_type_codes ||= image_urls.map do |url|
|
98
|
+
url.match(/n?([a-z_]+)\d*\.jpg$/)[1].to_sym
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def precipitation_probabilities
|
103
|
+
@precipitation_probabilities ||= @doc.find(%q{/dwml/data/parameters[1]/probability-of-precipitation[1]/value/text()}).map do |node|
|
104
|
+
node.to_s.to_i
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Noaaer
|
2
|
+
#
|
3
|
+
# A ForecastDay contains forecast data for a single day. Each day should start at 6am and
|
4
|
+
# end at 6am the following day (assuming that is invariant on the part of the NOAA's data
|
5
|
+
# feed). ForecastDay objects are accessed using NOAA::Forecast#[]
|
6
|
+
class ForecastDay
|
7
|
+
# Time when this forecast's valid time span begins
|
8
|
+
attr_reader :starts_at
|
9
|
+
|
10
|
+
# Time when this forecast's valid time span ends
|
11
|
+
attr_reader :ends_at
|
12
|
+
|
13
|
+
# High temperature for the day in Fahrenheit
|
14
|
+
attr_reader :high
|
15
|
+
|
16
|
+
# Low temperature for the day in Fahrenheit
|
17
|
+
attr_reader :low
|
18
|
+
|
19
|
+
# String summary of weather (e.g., 'Fair')
|
20
|
+
attr_reader :weather_summary
|
21
|
+
alias_method :weather_description, :weather_summary
|
22
|
+
|
23
|
+
# Symbol representing NOAA weather type. See NOAA::CurrentConditions#weather_type_code
|
24
|
+
attr_reader :weather_type_code
|
25
|
+
|
26
|
+
# URL string for NOAA weather image
|
27
|
+
attr_reader :image_url
|
28
|
+
|
29
|
+
# Percentage probability of precipitation during the day, between 6am and 6pm, as an integer (0-100)
|
30
|
+
attr_reader :daytime_precipitation_probability
|
31
|
+
|
32
|
+
# Percentage probability of precipitation during the evening/night, between 6pm and 6am, as an integer (0-100)
|
33
|
+
attr_reader :evening_precipitation_probability
|
34
|
+
|
35
|
+
attr_writer :starts_at, :ends_at, :high, :low, :weather_summary, :weather_type_code, :image_url, #:nodoc:
|
36
|
+
:daytime_precipitation_probability, :evening_precipitation_probability #:nodoc:
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# require 'libxml'
|
2
|
+
|
3
|
+
module Noaaer
|
4
|
+
class HttpService #:nodoc:
|
5
|
+
def initialize(http = Net::HTTP)
|
6
|
+
@HTTP = http
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_current_conditions(station_id)
|
10
|
+
@HTTP.get(URI.parse("http://www.weather.gov/xml/current_obs/#{station_id}.xml"))
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_forecast(num_days, lat, lng)
|
14
|
+
@HTTP.get(URI.parse("http://www.weather.gov/forecasts/xml/sample_products/browser_interface/ndfdBrowserClientByDay.php?lat=#{lat}&lon=#{lng}&format=24+hourly&numDays=#{num_days}"))
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_station_list
|
18
|
+
@HTTP.get(URI.parse("http://www.weather.gov/xml/current_obs/index.xml"))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Noaaer
|
2
|
+
#
|
3
|
+
# Data about an NOAA observation station. When accessing current conditions, the NOAA XML API
|
4
|
+
# takes a station ID as input; thus, to find the current conditions for an arbitrary location, one
|
5
|
+
# must first determine the closest weather station to that location. The NOAA.current_conditions
|
6
|
+
# method performs this task implicitly; however, for applications that need to find conditions
|
7
|
+
# for the same location(s) repeatedly, it is far more efficient to find the closest weather station
|
8
|
+
# once, store that information, and then query directly against the weather station when updated
|
9
|
+
# conditions are needed.
|
10
|
+
#
|
11
|
+
# Station data is stored in a YAML file that is created using the <tt>noaa-update-stations</tt> executable.
|
12
|
+
# Be sure to run this command at least once when you first install the NOAA library, and any time
|
13
|
+
# thereafter that you would like to get the latest list of stations. I don't imagine the list
|
14
|
+
# changes very often but I don't really have any idea.
|
15
|
+
#
|
16
|
+
class Station
|
17
|
+
class <<self
|
18
|
+
attr_writer :stations_file #:nodoc:
|
19
|
+
|
20
|
+
#
|
21
|
+
# Retrieve information about a station given a station ID
|
22
|
+
#
|
23
|
+
# NOAA::Station.find('KNYC') #=> NOAA::Station object for the Central Park station
|
24
|
+
def find(id)
|
25
|
+
stations.find { |station| station.id == id }
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Find the station closest to a given location. Can accept arguments in any of the following
|
30
|
+
# three forms (all are equivalent):
|
31
|
+
#
|
32
|
+
# NOAA::Station.closest_to(37.989, -77.507)
|
33
|
+
# NOAA::Station.closest_to([37.989, -77.507])
|
34
|
+
# NOAA::Station.closest_to(GeoKit::LatLng.new(37.989, -77.507))
|
35
|
+
def closest_to(*args)
|
36
|
+
if args.length == 1
|
37
|
+
if args.first.respond_to?(:distance_to)
|
38
|
+
closest_to_coordinates(args.first)
|
39
|
+
elsif %w(first last).all? { |m| args.first.respond_to?(m) }
|
40
|
+
closest_to_lat_lng(args.first)
|
41
|
+
else
|
42
|
+
raise ArgumentError, "expected two-element Array or GeoKit::LatLng"
|
43
|
+
end
|
44
|
+
elsif args.length == 2
|
45
|
+
closest_to_lat_lng(args)
|
46
|
+
else
|
47
|
+
raise ArgumentError, "closest_to() will accept one Array argument, one GeoKit::LatLng argument, or two FixNum arguments"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def closest_to_lat_lng(pair)
|
54
|
+
closest_to_coordinates(GeoKit::LatLng.new(pair.first, pair.last))
|
55
|
+
end
|
56
|
+
|
57
|
+
def closest_to_coordinates(coordinates)
|
58
|
+
stations.map do |station|
|
59
|
+
[coordinates.distance_to(station.coordinates), station]
|
60
|
+
end.min do |p1, p2|
|
61
|
+
p1.first <=> p2.first # compare distance
|
62
|
+
end[1]
|
63
|
+
end
|
64
|
+
|
65
|
+
def stations
|
66
|
+
File.open(stations_file) do |file|
|
67
|
+
yaml = YAML.load(file) || raise("Can't parse #{file.path} - be sure to run noaa-update-stations")
|
68
|
+
yaml.map { |station_hash| new(station_hash) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def stations_file
|
73
|
+
@stations_file ||= File.join(File.dirname(__FILE__), '..', '..', 'data', 'stations.yml')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# GeoKit::LatLng containing the station's coordinates
|
78
|
+
attr_reader :coordinates
|
79
|
+
|
80
|
+
# Station ID (e.g., "KNYC")
|
81
|
+
attr_reader :id
|
82
|
+
|
83
|
+
# Station name (e.g., "New York City, Central Park")
|
84
|
+
attr_reader :name
|
85
|
+
|
86
|
+
# Two-digit abbreviation for state in which station resides (e.g., "NY")
|
87
|
+
attr_reader :state
|
88
|
+
|
89
|
+
attr_reader :xml_url #:nodoc:
|
90
|
+
|
91
|
+
def initialize(properties)
|
92
|
+
@id, @name, @state, @xml_url = %w(id name state xml_url).map do |p|
|
93
|
+
properties[p]
|
94
|
+
end
|
95
|
+
@coordinates = GeoKit::LatLng.new(properties['latitude'], properties['longitude'])
|
96
|
+
end
|
97
|
+
|
98
|
+
# Latitude of station
|
99
|
+
def latitude
|
100
|
+
@coordinates.lat
|
101
|
+
end
|
102
|
+
alias_method :lat, :latitude
|
103
|
+
|
104
|
+
# Longitude of station
|
105
|
+
def longitude
|
106
|
+
@coordinates.lng
|
107
|
+
end
|
108
|
+
alias_method :lng, :longitude
|
109
|
+
alias_method :lon, :longitude
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Noaeer
|
2
|
+
class StationWriter
|
3
|
+
def initialize(doc)
|
4
|
+
@doc = doc
|
5
|
+
end
|
6
|
+
|
7
|
+
def write(io)
|
8
|
+
YAML.dump(@doc.find('/wx_station_index/station').map { |node| node_to_hash(node) }, io)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def node_to_hash(node)
|
14
|
+
{ 'latitude' => node_value(node, 'latitude').to_f,
|
15
|
+
'longitude' => node_value(node, 'longitude').to_f,
|
16
|
+
'id' => node_value(node, 'station_id'),
|
17
|
+
'name' => node_value(node, 'station_name'),
|
18
|
+
'state' => node_value(node, 'state'),
|
19
|
+
'xml_url' => node_value(node, 'xml_url') }
|
20
|
+
end
|
21
|
+
|
22
|
+
def node_value(node, element)
|
23
|
+
node.find("./#{element}/text()").first.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/noaaer.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'geokit'
|
4
|
+
|
5
|
+
%w(current_conditions forecast forecast_day http_service station station_writer).each { |file| require File.join(File.dirname(__FILE__), 'noaaer', file) }
|
6
|
+
|
7
|
+
#
|
8
|
+
# The NOAA singleton provides methods to conveniently access information from the NOAA weather feed.
|
9
|
+
# For the most part, NOAA.current_conditions and NOAA.forecast will be the only entry point into the
|
10
|
+
# NOAA API you will need; one exception is discussed below.
|
11
|
+
#
|
12
|
+
module Noaaer
|
13
|
+
autoload :VERSION, File.join(File.dirname(__FILE__), 'noaaer', 'version')
|
14
|
+
|
15
|
+
class << self
|
16
|
+
#
|
17
|
+
# Retrieve the current weather conditions for a given latitude and longitude. Returns an
|
18
|
+
# instance of NOAA::CurrentConditions.
|
19
|
+
#
|
20
|
+
# NOAA.current_conditions(37.989, -77.507) #=> NOAA::CurrentConditions encapsulating current conditions at this point
|
21
|
+
#
|
22
|
+
# <b>Note:</b> This method parses the stored list of all weather stations in the US and then finds the closest one to
|
23
|
+
# the given coordinates, as the NOAA XML API only takes a weather station ID as input. Clearly, this is an expensive
|
24
|
+
# operation; if your application needs to repeatedly request conditions for the same point, you will be much better off
|
25
|
+
# determining the current station once using NOAA::Station.closest_to, storing the station ID, and then passing it into
|
26
|
+
# NOAA.current_conditions_at_station when you need to get the latest conditions.
|
27
|
+
#
|
28
|
+
def current_conditions(lat, lng)
|
29
|
+
current_conditions_at_station(Station.closest_to(lat, lng).id)
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Retrieve the current weather conditions for a given weather station ID. Returns an
|
34
|
+
# instance of NOAA::CurrentConditions.
|
35
|
+
#
|
36
|
+
# NOAA.current_conditions_at_station('KNYC') #=> NOAA::CurrentConditions encapsulating current conditions in Central Park
|
37
|
+
#
|
38
|
+
# See discussion above regarding why this method is often preferable to simply calling #current_conditions.
|
39
|
+
#
|
40
|
+
def current_conditions_at_station(station_id)
|
41
|
+
CurrentConditions.from_xml(HttpService.new.get_current_conditions(station_id))
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Retrieve daily forecast information for a given latitude and longitude. Returns
|
46
|
+
# an instance of NOAA::Forecast.
|
47
|
+
#
|
48
|
+
# NOAA.forecast(4, 37.989, -77.507) #=> NOAA::Forecast containing next four days of forecast data for given coordinates
|
49
|
+
#
|
50
|
+
# <b>Note:</b> The NOAA updates this data no more than once an hour, and asks that users of the API not request the forecast
|
51
|
+
# for a given location more frequently than that. For more information, please see http://www.nws.noaa.gov/xml/#frequency
|
52
|
+
#
|
53
|
+
def forecast(num_days, lat, lng)
|
54
|
+
Forecast.from_xml(HttpService.new.get_forecast(num_days, lat, lng))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/noaaer.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "noaaer/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "noaaer"
|
7
|
+
s.version = Noaaer::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Cody Swann, Gunner Technology"]
|
10
|
+
s.email = ["developers@gunnertech.com"]
|
11
|
+
s.homepage = "http://gunnertech.com"
|
12
|
+
s.summary = %q{Wrapper for NOAA Weather Service API}
|
13
|
+
s.description = %q{This gem will help you utilize the NOAA API for weather data}
|
14
|
+
|
15
|
+
s.rubyforge_project = "noaaer"
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: noaaer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.4
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Cody Swann, Gunner Technology
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-04-14 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: This gem will help you utilize the NOAA API for weather data
|
18
|
+
email:
|
19
|
+
- developers@gunnertech.com
|
20
|
+
executables:
|
21
|
+
- noaa-update-stations
|
22
|
+
extensions: []
|
23
|
+
|
24
|
+
extra_rdoc_files: []
|
25
|
+
|
26
|
+
files:
|
27
|
+
- .gitignore
|
28
|
+
- Gemfile
|
29
|
+
- Rakefile
|
30
|
+
- bin/noaa-update-stations
|
31
|
+
- lib/noaaer.rb
|
32
|
+
- lib/noaaer/current_conditions.rb
|
33
|
+
- lib/noaaer/forecast.rb
|
34
|
+
- lib/noaaer/forecast_day.rb
|
35
|
+
- lib/noaaer/http_service.rb
|
36
|
+
- lib/noaaer/station.rb
|
37
|
+
- lib/noaaer/station_writer.rb
|
38
|
+
- lib/noaaer/version.rb
|
39
|
+
- noaaer.gemspec
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://gunnertech.com
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project: noaaer
|
64
|
+
rubygems_version: 1.6.0
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Wrapper for NOAA Weather Service API
|
68
|
+
test_files: []
|
69
|
+
|