SnotelAPI 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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/SnotelAPI.gemspec +23 -0
- data/lib/SnotelAPI/version.rb +3 -0
- data/lib/SnotelAPI.rb +156 -0
- data/test/snotel_api_spec.rb +55 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 22059a277b15f4e5727a9cc2eff6817b2007ab7c
|
4
|
+
data.tar.gz: 28d778dff8b79421a6c237c0d4d00ebeef206b52
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ec469a149922e7dd5567b2953286e2c78fca44560eac471a14131d0058ea8ac3757578a3fafd8693202e2157a8d02dd9b5c5386b75d2efb5673a92be3df08e3e
|
7
|
+
data.tar.gz: b2ae3c5d9978ad9256742520322c6c737fbfc7e14748b49124afd9090690ba971a942bba66cdcb1e1345e95897de17362fe6946cce80e490d06cac2a44e58063
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Pedro Rodriguez
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# SnotelAPI
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'SnotelAPI'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install SnotelAPI
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/SnotelAPI.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'SnotelAPI/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'SnotelAPI'
|
8
|
+
spec.version = SnotelAPI::VERSION
|
9
|
+
spec.authors = ['Pedro Rodriguez']
|
10
|
+
spec.email = ['ski.rodriguez@gmail.com']
|
11
|
+
spec.description = 'Ruby gem that interfaces with the NOAA Snotel API'
|
12
|
+
spec.summary = 'Ruby gem that interfaces with the NOAA Snotel API'
|
13
|
+
spec.homepage = 'http://snowgeek.org'
|
14
|
+
spec.license = 'GNU V2'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
end
|
data/lib/SnotelAPI.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require "SnotelAPI/version"
|
2
|
+
require 'thread_safe'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'savon'
|
5
|
+
require 'active_support/core_ext/string/strip'
|
6
|
+
|
7
|
+
module SnotelAPI
|
8
|
+
WSDL = 'http://www.wcc.nrcs.usda.gov/awdbWebService/services?WSDL'
|
9
|
+
CSV_API_URL = 'http://www.wcc.nrcs.usda.gov/reportGenerator/view_csv/customMultipleStationReport%2Cmetric/hourly/'
|
10
|
+
CLIENT = Savon::Client.new do
|
11
|
+
wsdl 'lib/snotel-services.xml'
|
12
|
+
log false #Rails.env.development?
|
13
|
+
end
|
14
|
+
#Code docs here: http://www.wcc.nrcs.usda.gov/web_service/AWDB_Web_Service_Reference.htm#elementCodes
|
15
|
+
#Element Codes
|
16
|
+
#TOBS PREC SNWD WTEQ PRES DPTP RHUM SNOW WDIRV WDIR WSPD WSPV
|
17
|
+
|
18
|
+
def make_wsdl_request(method, message, client=nil)
|
19
|
+
if client.nil?
|
20
|
+
return CLIENT.call(method, message: message)
|
21
|
+
end
|
22
|
+
return client.call(method, message: message)
|
23
|
+
end
|
24
|
+
|
25
|
+
def make_client(num=-1)
|
26
|
+
if num == -1
|
27
|
+
return Savon::Client.new do
|
28
|
+
wsdl 'lib/data/snotel-services.xml'
|
29
|
+
log Rails.env.development?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
clients = ThreadSafe::Array.new(num)
|
33
|
+
clients.map { make_client }
|
34
|
+
return clients
|
35
|
+
end
|
36
|
+
|
37
|
+
# Gets the stations metadata for the station_triplet
|
38
|
+
# The result is a hash of the values for the metadata
|
39
|
+
# Hash Values
|
40
|
+
# action_id, begin_date, county_name, elevation, end_date, fips_county_cd, fips_country_cd, fips_state_number
|
41
|
+
# huc, hud, latitude, longitude, name, shefid, station_data_time_zone, station_time_zone, station_triplet
|
42
|
+
# http://www.wcc.nrcs.usda.gov/web_service/AWDB_Web_Service_Reference.htm#StationMetadata
|
43
|
+
def get_station_metadata(station_triplet)
|
44
|
+
xml = make_wsdl_request(:get_station_metadata, get_station_metadata_xml(station_triplet))
|
45
|
+
xml.body[:get_station_metadata_response][:return]
|
46
|
+
end
|
47
|
+
#Returns the xml for getting station metadata based on the station_triplet
|
48
|
+
def get_station_metadata_xml(station_triplet)
|
49
|
+
"<stationTriplet>#{station_triplet}</stationTriplet>"
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_all_stations_xml
|
53
|
+
<<-eos.strip_heredoc
|
54
|
+
<networkCds>SNTL</networkCds>
|
55
|
+
<logicalAnd>true</logicalAnd>
|
56
|
+
eos
|
57
|
+
end
|
58
|
+
def get_all_stations
|
59
|
+
make_wsdl_request(:get_stations, get_all_stations_xml)
|
60
|
+
end
|
61
|
+
def get_bounded_stations_xml(min_lat, max_lat, min_lon, max_lon)
|
62
|
+
<<-eos.strip_heredoc
|
63
|
+
<networkCds>SNTL</networkCds>
|
64
|
+
<minLatitude>#{min_lat}</minLatitude>
|
65
|
+
<maxLatitude>#{max_lat}</maxLatitude>
|
66
|
+
<minLongitude>#{min_lon}</minLongitude>
|
67
|
+
<maxLongitude>#{max_lon}</maxLongitude>
|
68
|
+
<logicalAnd>true</logicalAnd>
|
69
|
+
eos
|
70
|
+
end
|
71
|
+
# Queries Snotel for any stations which are within min_lat, max_lat, min_lon, and max_lon
|
72
|
+
# This list is returned as an array of strings of the stations striplets
|
73
|
+
def get_bounded_stations(min_lat, max_lat, min_lon, max_lon)
|
74
|
+
xml = make_wsdl_request(:get_stations, get_bounded_stations_xml(min_lat, max_lat, min_lon, max_lon))
|
75
|
+
stations = xml.body[:get_stations_response][:return]
|
76
|
+
unless stations.kind_of?(Array)
|
77
|
+
stations = [stations]
|
78
|
+
end
|
79
|
+
return stations
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_hourly_data(station_triplets, hours_back)
|
83
|
+
csv = make_csv_request(station_triplets, hours_back)
|
84
|
+
return parse_hourly_data_csv(csv)
|
85
|
+
end
|
86
|
+
|
87
|
+
def parse_hourly_data_csv(csv)
|
88
|
+
clean_csv = remove_csv_comments(csv)
|
89
|
+
data = []
|
90
|
+
clean_csv.each do |r|
|
91
|
+
r = CSV.parse(r)[0]
|
92
|
+
data.append({ datetime: r[0], snow_water_equivalent: r[3], snow_depth: r[4], temp: r[5] })
|
93
|
+
end
|
94
|
+
return data
|
95
|
+
end
|
96
|
+
|
97
|
+
# Takes in csv as a string. Parses it via newlines checking to see if any line is empty
|
98
|
+
# or contains a comment beggining with a #. Then shifts the csv to remove the header line of the file
|
99
|
+
def remove_csv_comments(csv)
|
100
|
+
csv = csv.split(/\n/).reject { |s| csv_requirement(s) }
|
101
|
+
csv.shift()
|
102
|
+
return csv
|
103
|
+
end
|
104
|
+
|
105
|
+
def csv_requirement(s)
|
106
|
+
comment = !/^#/.match(s).nil?
|
107
|
+
comment || s.empty?
|
108
|
+
end
|
109
|
+
|
110
|
+
def make_csv_request(station_triplets, days_back)
|
111
|
+
url = URI.parse(build_csv_api_url(station_triplets, days_back))
|
112
|
+
request = Net::HTTP::Get.new(url.to_s)
|
113
|
+
response = Net::HTTP.start(url.host, url.port) do |http|
|
114
|
+
http.request(request)
|
115
|
+
end
|
116
|
+
response.body
|
117
|
+
end
|
118
|
+
|
119
|
+
def build_csv_api_url(station_triplets, hours_back)
|
120
|
+
stations = station_triplets.join('|')
|
121
|
+
url = "#{CSV_API_URL}#{CGI.escape(stations)}%7Cid%3D%22%22%7Cname~0/-#{hours_back}%2C0/stationId%2Cname%2CWTEQ%3A%3Avalue%2CSNWD%3A%3Avalue%2CTOBS%3A%3Avalue"
|
122
|
+
return url
|
123
|
+
end
|
124
|
+
|
125
|
+
def convert_raw_data_to_display_formats(data, hours_back)
|
126
|
+
result = {}
|
127
|
+
depth_data = []
|
128
|
+
temperature_data = []
|
129
|
+
sw_equivalent_data = []
|
130
|
+
raw_data = []
|
131
|
+
depth_counter = -hours_back
|
132
|
+
temperature_counter = -hours_back
|
133
|
+
sw_equivalent_counter = -hours_back
|
134
|
+
data.each do |r|
|
135
|
+
unless r[:snow_depth].nil?
|
136
|
+
depth_data.append([depth_counter, r[:snow_depth].to_f])
|
137
|
+
end
|
138
|
+
depth_counter += 1
|
139
|
+
unless r[:temp].nil?
|
140
|
+
temperature_data.append([temperature_counter, r[:temp].to_f])
|
141
|
+
end
|
142
|
+
temperature_counter += 1
|
143
|
+
unless r[:snow_water_equivalent].nil?
|
144
|
+
sw_equivalent_data.append([sw_equivalent_counter, r[:snow_water_equivalent].to_f])
|
145
|
+
end
|
146
|
+
sw_equivalent_counter += 1
|
147
|
+
raw_data.append([r[:datetime], r[:snow_water_equivalent].to_f, r[:snow_depth].to_f, r[:temp].to_f])
|
148
|
+
end
|
149
|
+
result[:depth_data] = depth_data
|
150
|
+
result[:temperature_data] = temperature_data
|
151
|
+
result[:sw_equivalent_data] = sw_equivalent_data
|
152
|
+
result[:raw_data] = raw_data
|
153
|
+
return result
|
154
|
+
end
|
155
|
+
extend self
|
156
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'data/snotel_api'
|
3
|
+
|
4
|
+
describe SnotelAPI do
|
5
|
+
describe 'xml methods' do
|
6
|
+
it 'has correct get_all_stations xml' do
|
7
|
+
expected_xml = <<-eos.strip_heredoc
|
8
|
+
<networkCds>SNTL</networkCds>
|
9
|
+
<logicalAnd>true</logicalAnd>
|
10
|
+
eos
|
11
|
+
SnotelAPI.get_all_stations_xml.should == expected_xml
|
12
|
+
end
|
13
|
+
it 'has correct get_station_metadata xml' do
|
14
|
+
station_triplet = '302:OR:SNTL'
|
15
|
+
expected_xml = "<stationTriplet>#{station_triplet}</stationTriplet>"
|
16
|
+
SnotelAPI.get_station_metadata_xml(station_triplet).should == expected_xml
|
17
|
+
end
|
18
|
+
it 'has correct get_bounded_stations xml' do
|
19
|
+
min_lat = 0
|
20
|
+
max_lat = 1
|
21
|
+
min_lon = 2
|
22
|
+
max_lon = 3
|
23
|
+
expected_xml = <<-eos.strip_heredoc
|
24
|
+
<networkCds>SNTL</networkCds>
|
25
|
+
<minLatitude>#{min_lat}</minLatitude>
|
26
|
+
<maxLatitude>#{max_lat}</maxLatitude>
|
27
|
+
<minLongitude>#{min_lon}</minLongitude>
|
28
|
+
<maxLongitude>#{max_lon}</maxLongitude>
|
29
|
+
<logicalAnd>true</logicalAnd>
|
30
|
+
eos
|
31
|
+
SnotelAPI.get_bounded_stations_xml(min_lat, max_lat, min_lon, max_lon).should == expected_xml
|
32
|
+
end
|
33
|
+
end
|
34
|
+
describe 'xml API methods' do
|
35
|
+
it 'has working get_station_metadata' do
|
36
|
+
VCR.use_cassette('data#get_station_metadata') do
|
37
|
+
result = SnotelAPI.get_station_metadata('302:OR:SNTL')
|
38
|
+
result[:county_name].should == 'WALLOWA'
|
39
|
+
result[:fips_county_cd].should == '063'
|
40
|
+
result[:station_triplet].should == '302:OR:SNTL'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
it 'has working get_all_stations' do
|
44
|
+
VCR.use_cassette('data#get_all_stations') do
|
45
|
+
SnotelAPI.get_all_stations()
|
46
|
+
end
|
47
|
+
end
|
48
|
+
it 'has working get_bounded_stations' do
|
49
|
+
VCR.use_cassette('data#get_bounded_stations') do
|
50
|
+
stations = SnotelAPI.get_bounded_stations(41.234, 41.236, -120.792, -120.790)
|
51
|
+
stations.should == ['301:CA:SNTL']
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: SnotelAPI
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pedro Rodriguez
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Ruby gem that interfaces with the NOAA Snotel API
|
42
|
+
email:
|
43
|
+
- ski.rodriguez@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- Gemfile
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- SnotelAPI.gemspec
|
54
|
+
- lib/SnotelAPI.rb
|
55
|
+
- lib/SnotelAPI/version.rb
|
56
|
+
- test/snotel_api_spec.rb
|
57
|
+
homepage: http://snowgeek.org
|
58
|
+
licenses:
|
59
|
+
- GNU V2
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.1.11
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Ruby gem that interfaces with the NOAA Snotel API
|
81
|
+
test_files:
|
82
|
+
- test/snotel_api_spec.rb
|