noaaer 0.0.4
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/.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
|
+
|