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 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
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in SnotelAPI.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec'
7
+ gem 'savon'
8
+ gem 'activesupport'
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
@@ -0,0 +1,3 @@
1
+ module SnotelAPI
2
+ VERSION = "0.0.1"
3
+ 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