weatherbug 0.0.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.
- data/lib/weatherbug.rb +117 -0
- data/lib/weatherbug/forecast.rb +18 -0
- data/lib/weatherbug/hash_methods.rb +40 -0
- data/lib/weatherbug/live_observation.rb +34 -0
- data/lib/weatherbug/station.rb +33 -0
- data/lib/weatherbug/station_hint.rb +14 -0
- data/lib/weatherbug/transformable_data.rb +31 -0
- data/lib/weatherbug/version.rb +9 -0
- metadata +87 -0
data/lib/weatherbug.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'open-uri'
|
5
|
+
|
6
|
+
module WeatherBug
|
7
|
+
|
8
|
+
require 'weatherbug/hash_methods'
|
9
|
+
require 'weatherbug/transformable_data'
|
10
|
+
autoload :Station, 'weatherbug/station'
|
11
|
+
autoload :StationHint, 'weatherbug/station_hint'
|
12
|
+
autoload :LiveObservation, 'weatherbug/live_observation'
|
13
|
+
autoload :Forecast, 'weatherbug/forecast'
|
14
|
+
|
15
|
+
API_URL = 'datafeed.weatherbug.com'
|
16
|
+
|
17
|
+
|
18
|
+
# Set your application's partner ID
|
19
|
+
def self.partner_id=(partner_id)
|
20
|
+
@partner_id = partner_id
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get the 25 closest stations, or less in less dense areas
|
24
|
+
def self.nearby_stations(lookup_options)
|
25
|
+
HashMethods.valid_keys(lookup_options, [:zip_code, :city_code, :postal_code, :include_pws])
|
26
|
+
HashMethods.valid_one_only(lookup_options, [:zip_code, :city_code])
|
27
|
+
HashMethods.valid_one_only(lookup_options, [:postal_code, :city_code])
|
28
|
+
HashMethods.valid_needs(lookup_options, :zip_code, :postal_code) if lookup_options.has_key?(:postal_code)
|
29
|
+
response = make_request('StationList', HashMethods.convert_symbols(lookup_options))
|
30
|
+
|
31
|
+
response.xpath('/aws:weather/aws:station').map { |station| WeatherBug::Station.from_document(station) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.closest_station(lookup_options)
|
35
|
+
HashMethods.valid_keys(lookup_options, [:zip_code, :city_code, :postal_code, :include_pws])
|
36
|
+
HashMethods.valid_one_only(lookup_options, [:zip_code, :city_code])
|
37
|
+
HashMethods.valid_one_only(lookup_options, [:postal_code, :city_code])
|
38
|
+
HashMethods.valid_needs(lookup_options, :zip_code, :postal_code) if lookup_options.has_key?(:postal_code)
|
39
|
+
params = HashMethods.convert_symbols(lookup_options)
|
40
|
+
params['ListType'] = '1'
|
41
|
+
response = make_request('StationList', params)
|
42
|
+
|
43
|
+
WeatherBug::Station.from_document response.xpath('/aws:weather/aws:station').first
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.get_station(station_id)
|
47
|
+
params = {'StationId' => station_id}
|
48
|
+
response = make_request('StationInfo', params)
|
49
|
+
|
50
|
+
WeatherBug::Station.from_document response.xpath('/aws:weather/aws:station').first
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.live_observation(station, unit_type = :f)
|
54
|
+
station_id = station.is_a?(WeatherBug::Station) ? station.station_id : station
|
55
|
+
params = {'StationId' => station_id}
|
56
|
+
params['UnitType'] = '1' if unit_type == :c
|
57
|
+
response = make_request('LiveObservations', params)
|
58
|
+
|
59
|
+
live_observation = WeatherBug::LiveObservation.from_document response.xpath('/aws:weather/aws:ob').first
|
60
|
+
live_observation.send(:station_reference=, station) if station.is_a?(WeatherBug::Station)
|
61
|
+
live_observation
|
62
|
+
end
|
63
|
+
|
64
|
+
# Get the list of stations in a bounding box
|
65
|
+
# Does not return full stations
|
66
|
+
def self.stations_in_box(top_right_lat, top_right_lng, bot_left_lat, bot_left_lng)
|
67
|
+
params = {
|
68
|
+
'LatitudeTopRight' => top_right_lat,
|
69
|
+
'LongitudeTopRight' => top_right_lng,
|
70
|
+
'LatitudeBottomLeft' => bot_left_lat,
|
71
|
+
'LongitudeBottomLeft' => bot_left_lng
|
72
|
+
}
|
73
|
+
response = make_request('StationListByLatLng', params)
|
74
|
+
|
75
|
+
station_data = response.xpath('/aws:weather/aws:stations/aws:station')
|
76
|
+
return nil unless station_data
|
77
|
+
|
78
|
+
station_data.map do |sdata|
|
79
|
+
WeatherBug::StationHint.from_document(sdata)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.forecast(options)
|
84
|
+
self.retrieve_forecast(1, options)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# TODO add validation for fields
|
90
|
+
def self.retrieve_forecast(forecast_type, options)
|
91
|
+
HashMethods.valid_keys(options, [:latitude, :longitude])
|
92
|
+
params = HashMethods.convert_symbols(options)
|
93
|
+
params['ForecastType'] = forecast_type
|
94
|
+
response = make_request(options.has_key?(:longitude) ? 'ClosestForecastByLatLng' : 'Forecast', params)
|
95
|
+
|
96
|
+
forecast_data = response.xpath('/aws:weather/aws:forecasts/aws:forecast')
|
97
|
+
return nil unless forecast_data
|
98
|
+
|
99
|
+
forecast_data.map do |fdata|
|
100
|
+
WeatherBug::Forecast.from_document(fdata)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Make the actual request, unwrap it and send it back
|
105
|
+
# Handles errors that may come up
|
106
|
+
def self.make_request(method, params)
|
107
|
+
params['RequestType'] = method
|
108
|
+
params['PartnerId'] = @partner_id
|
109
|
+
data = open("http://#{API_URL}/GetXml.aspx?#{params.map { |k, v| "#{k}=#{v}" }.join('&')}")
|
110
|
+
raise Exception.new("Unexpected exception - empty response") unless data
|
111
|
+
Nokogiri::XML(data)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
WeatherBug.partner_id = 'f0c313fe-fffe-4f5d-96dc-5a5d6ec9eb30'
|
117
|
+
stations = WeatherBug::nearby_stations(:zip_code => '08005')
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module WeatherBug
|
2
|
+
|
3
|
+
class Forecast < TransformableData
|
4
|
+
|
5
|
+
register_transformer 'aws:title', :required => true
|
6
|
+
register_transformer 'aws:city'
|
7
|
+
register_transformer 'aws:short-title'
|
8
|
+
register_transformer 'aws:icon'
|
9
|
+
register_transformer 'aws:description'
|
10
|
+
register_transformer 'aws:prediction'
|
11
|
+
register_transformer 'aws:lo', :name => :temp_low, :transform => :to_i
|
12
|
+
register_transformer 'aws:hi', :name => :temp_high, :transform => :to_i
|
13
|
+
register_transformer 'aws:hi/@units', :name => :temp_units
|
14
|
+
register_transformer 'aws:prob-of-precip', :name => :probability_of_precipitation
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module WeatherBug
|
2
|
+
|
3
|
+
class HashMethods
|
4
|
+
|
5
|
+
WB_MAPPING = {
|
6
|
+
:zip_code => 'ZipCode',
|
7
|
+
:city_code => 'CityCode',
|
8
|
+
:postal_code => 'PostalCode',
|
9
|
+
:include_pws => 'IncludePWS',
|
10
|
+
:latitude => 'Latitude',
|
11
|
+
:longitude => 'Longitude'
|
12
|
+
}
|
13
|
+
|
14
|
+
# Raise an argument error if we get invalid keys at all
|
15
|
+
def self.valid_keys(hash, allowed)
|
16
|
+
invalids = nil
|
17
|
+
hash.keys.each { |key| (invalids ||= []; invalids << key) unless allowed.include?(key) }
|
18
|
+
raise ArgumentError.new("Invalid options: #{invalids.join(', ')}") unless invalids.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.valid_one_only(hash, conflictions)
|
22
|
+
conflicts = []
|
23
|
+
conflictions.each { |c| conflicts << c if hash.keys.include?(c) }
|
24
|
+
raise ArgumentError.new("Cannot combine options: #{conflicts.join(', ')}") if conflicts.size > 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.valid_needs(hash, key, key_for)
|
28
|
+
raise ArgumentError.new("You must supply option #{key} to use #{key_for}") unless hash.has_key?(key)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.convert_symbols(hash)
|
32
|
+
hash.inject({}) do |rhash, (original_key, value)|
|
33
|
+
rhash[WB_MAPPING.fetch(original_key, original_key)] = value
|
34
|
+
rhash
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module WeatherBug
|
2
|
+
|
3
|
+
class LiveObservation < TransformableData
|
4
|
+
|
5
|
+
register_transformer 'aws:station-id'
|
6
|
+
register_transformer 'aws:station', :name => :station_name
|
7
|
+
register_transformer 'aws:temp-high/@units', :name => :temp_units
|
8
|
+
register_transformer 'aws:temp-high', :name => :temp_high, :transform => :to_i
|
9
|
+
register_transformer 'aws:temp-low', :name => :temp_low, :transform => :to_i
|
10
|
+
register_transformer 'aws:dew-point', :transform => :to_i
|
11
|
+
register_transformer 'aws:elevation', :transform => :to_i
|
12
|
+
register_transformer 'aws:feels-like', :transform => :to_i
|
13
|
+
register_transformer 'aws:humidity', :transform => :to_i
|
14
|
+
register_transformer 'aws:wind-direction'
|
15
|
+
register_transformer 'aws:wind-speed', :transform => :to_i
|
16
|
+
register_transformer 'aws:pressure', :transform => :to_f
|
17
|
+
|
18
|
+
def self.from_document(document)
|
19
|
+
lops = super
|
20
|
+
raise ArgumentError.new('No such station') if lops.station_id.empty?
|
21
|
+
lops
|
22
|
+
end
|
23
|
+
|
24
|
+
def station
|
25
|
+
@station_reference ||= WeatherBug.get_station(self.station_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_writer :station_reference
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module WeatherBug
|
2
|
+
|
3
|
+
class Station < TransformableData
|
4
|
+
|
5
|
+
register_transformer '@id', :name => :station_id
|
6
|
+
register_transformer '@name'
|
7
|
+
register_transformer '@distance', :transform => :to_f
|
8
|
+
register_transformer '@zipcode', :name => :zip_code
|
9
|
+
register_transformer '@city-code', :transform => :to_i
|
10
|
+
register_transformer '@station-type'
|
11
|
+
register_transformer '@city'
|
12
|
+
register_transformer '@state'
|
13
|
+
register_transformer '@country'
|
14
|
+
register_transformer '@latitude', :transform => :to_f
|
15
|
+
register_transformer '@longitude', :transform => :to_f
|
16
|
+
|
17
|
+
def self.from_document(document)
|
18
|
+
station = super
|
19
|
+
raise ArgumentError.new('No such station') if station.station_id.empty?
|
20
|
+
station
|
21
|
+
end
|
22
|
+
|
23
|
+
def live_observation(unit = :f)
|
24
|
+
WeatherBug.live_observation(self, unit)
|
25
|
+
end
|
26
|
+
|
27
|
+
def forecast
|
28
|
+
WeatherBug.forecast(:latitude => latitude, :longitude => longitude)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module WeatherBug
|
2
|
+
|
3
|
+
# This is a super-convenient class for transorming data and requiring things in the
|
4
|
+
# different associated WeatherBug data classes
|
5
|
+
class TransformableData
|
6
|
+
|
7
|
+
def self.register_transformer(name, options = {})
|
8
|
+
@transformers ||= {}
|
9
|
+
options[:name] = name.split(/[:@]/).last.gsub('-', '_').to_sym unless options.has_key?(:name)
|
10
|
+
private; attr_writer options[:name]
|
11
|
+
public; attr_reader options[:name]
|
12
|
+
@transformers[name] = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_document(document)
|
16
|
+
object = self.new
|
17
|
+
@transformers.each do |key, options|
|
18
|
+
value = document.xpath(key).text
|
19
|
+
# If this thing is empty, skip it
|
20
|
+
next if value.nil?
|
21
|
+
# If this thing needs to be transformed, transform it (from a string)
|
22
|
+
value = value.send(options[:transform]) if options.has_key?(:transform)
|
23
|
+
# Assign it on the class
|
24
|
+
object.send("#{options[:name]}=", value)
|
25
|
+
end
|
26
|
+
object
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: weatherbug
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- John Crepezzi
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-19 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Light Wrapper for the WeatherBug partner API
|
36
|
+
email: john.crepezzi@patch.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- lib/weatherbug/forecast.rb
|
45
|
+
- lib/weatherbug/hash_methods.rb
|
46
|
+
- lib/weatherbug/live_observation.rb
|
47
|
+
- lib/weatherbug/station.rb
|
48
|
+
- lib/weatherbug/station_hint.rb
|
49
|
+
- lib/weatherbug/transformable_data.rb
|
50
|
+
- lib/weatherbug/version.rb
|
51
|
+
- lib/weatherbug.rb
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://github.com/seejohnrun/weatherbug
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
hash: 3
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
requirements: []
|
80
|
+
|
81
|
+
rubyforge_project: weatherbug
|
82
|
+
rubygems_version: 1.3.7
|
83
|
+
signing_key:
|
84
|
+
specification_version: 3
|
85
|
+
summary: Ruby WeatherBug partner API
|
86
|
+
test_files: []
|
87
|
+
|