knmi 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,181 @@
1
+ ---
2
+ - parameter: YYYYMMDD
3
+ category:
4
+ description: Time Stamp
5
+ validate:
6
+ conversion: |-
7
+ Time.utc(
8
+ year = n.to_s[(0..3)],
9
+ month = n.to_s[(4..5)],
10
+ day = n.to_s[(6..7)]
11
+ )
12
+ units: Time
13
+ period: hourly
14
+ - parameter: HH
15
+ category:
16
+ description: Hour
17
+ validate: (1..24).include?(n)
18
+ conversion:
19
+ units: hour
20
+ period: hourly
21
+ - parameter: DD
22
+ category: WIND
23
+ description: Mean Wind Direction During the 10-Minute Period Preceding the Time of Observation
24
+ validate: (0..360).to_a.push(990).include?(n)
25
+ conversion:
26
+ units: Polar Coordinates (360=north, 90=east, 180=south, 270=west,0=calm, 990=variable)
27
+ period: hourly
28
+ - parameter: FH
29
+ category: WIND
30
+ description: Hourly Mean Wind Speed
31
+ validate: n.integer?
32
+ conversion: n / 10
33
+ units: m/s
34
+ period: hourly
35
+ - parameter: FF
36
+ category: WIND
37
+ description: Mean Wind Speed
38
+ validate: n.integer?
39
+ conversion: n / 10
40
+ units: m/s
41
+ period: hourly
42
+ - parameter: FX
43
+ category: WIND
44
+ description: Maximum Wind Gust
45
+ validate: n.integer?
46
+ conversion: n / 10
47
+ units: m/s
48
+ period: hourly
49
+ - parameter: T
50
+ category: TEMP
51
+ description: Air Temperature at 1.5 m
52
+ validate: n.integer?
53
+ conversion: n / 10
54
+ units: C
55
+ period: hourly
56
+ - parameter: T10N
57
+ category: TEMP
58
+ description: Minimum Air Temperature at 10 cm in the Preceding 6 Hours
59
+ validate: n.integer?
60
+ conversion: n / 10
61
+ units: C
62
+ period: hourly
63
+ - parameter: TD
64
+ category: TEMP
65
+ description: Dew Point Temperature
66
+ validate: n.integer?
67
+ conversion: n / 10
68
+ units: C
69
+ period: hourly
70
+ - parameter: SQ
71
+ category: RDTN
72
+ description: Sunshine Duration
73
+ validate: (-1..240).include?(n)
74
+ conversion: "n == -1 ? 0.05 : (n / 10) * 60"
75
+ units: minutes
76
+ period: hourly
77
+ - parameter: Q
78
+ category: RDTN
79
+ description: Golbal Radiation
80
+ validate: n.integer?
81
+ conversion:
82
+ units: J/cm^2
83
+ period: hourly
84
+ - parameter: DR
85
+ category: PRCP
86
+ description: Precipitation Duration
87
+ validate: (0..240).include?(n)
88
+ conversion: (n / 10) * 60
89
+ units: minutes
90
+ period: hourly
91
+ - parameter: RH
92
+ category: PRCP
93
+ description: Precipitation
94
+ validate: n.integer?
95
+ conversion: n / 10
96
+ units: mm
97
+ period: hourly
98
+ - parameter: P
99
+ category: ATMS
100
+ description: Air Pressure
101
+ validate: n.integer?
102
+ conversion: n / 10
103
+ units: hPa
104
+ period: hourly
105
+ - parameter: VV
106
+ category: VISB
107
+ description: Visibility
108
+ validate:
109
+ conversion: |-
110
+ if n == 0
111
+ '< 100 meters'
112
+ elsif (1..49).include?(n)
113
+ (n * 100).to_s + '-' + ((n+1) * 100).to_s + ' meters'
114
+ elsif n == 50
115
+ '5-6 kilometers'
116
+ elsif (56..79).include?(n)
117
+ (n - 50).to_s + '-' + (n - 49).to_s + ' kilometers'
118
+ elsif (80..88).include?(n)
119
+ (n - 50).to_s + '-' + (n - 45).to_s + ' kilometers'
120
+ elsif n == 89
121
+ '> 70 kilometers'
122
+ end
123
+ units:
124
+ period: hourly
125
+ - parameter: N
126
+ category: VISB
127
+ description: Cloud Cover
128
+ validate: (0..9).include?(n)
129
+ conversion:
130
+ units:
131
+ period: hourly
132
+ - parameter: U
133
+ category: ATMS
134
+ description: Relative Humidity
135
+ validate: (0..100).include?(n)
136
+ conversion:
137
+ units: %
138
+ period: hourly
139
+ - parameter: M
140
+ category: WTHR
141
+ description: Fog
142
+ validate: (0..1).include?(n)
143
+ conversion:
144
+ units: boolean
145
+ period: hourly
146
+ - parameter: R
147
+ category: WTHR
148
+ description: Rainfall
149
+ validate: (0..1).include?(n)
150
+ conversion:
151
+ units: boolean
152
+ period: hourly
153
+ - parameter: S
154
+ category: WTHR
155
+ description: Snow
156
+ validate: (0..1).include?(n)
157
+ conversion:
158
+ units: boolean
159
+ period: hourly
160
+ - parameter: O
161
+ category: WTHR
162
+ description: Thunder
163
+ validate: (0..1).include?(n)
164
+ conversion:
165
+ units: boolean
166
+ period: hourly
167
+ - parameter: Y
168
+ category: WTHR
169
+ description: Ice
170
+ validate: (0..1).include?(n)
171
+ conversion:
172
+ units: boolean
173
+ period: hourly
174
+ - parameter: WW
175
+ category: WTHR
176
+ description: Weather Code
177
+ validate: (0..99).include?(n)
178
+ conversion:
179
+ units: code
180
+ period: hourly
181
+
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{knmi}
8
- s.version = "0.1.4"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["bullfight"]
12
- s.date = %q{2011-05-02}
12
+ s.date = %q{2011-05-17}
13
13
  s.description = %q{A set of methods to query the KNMI HTTP get form for daily climate data and select a variety of measured parameters, from available stations, in a json style array of hashes, and if necessary convert to csv.}
14
14
  s.email = %q{p.schmitz@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
17
17
  "README.rdoc"
18
18
  ]
19
19
  s.files = [
20
+ ".DS_Store",
20
21
  ".document",
21
22
  "Gemfile",
22
23
  "Gemfile.lock",
@@ -24,10 +25,21 @@ Gem::Specification.new do |s|
24
25
  "README.rdoc",
25
26
  "Rakefile",
26
27
  "VERSION",
28
+ "data/current_stations.yml",
29
+ "data/daily_data_key.yml",
30
+ "data/data_key.yml",
31
+ "data/hourly_data_key.yml",
27
32
  "knmi.gemspec",
33
+ "lib/.DS_Store",
28
34
  "lib/knmi.rb",
35
+ "lib/knmi/httpservice.rb",
36
+ "lib/knmi/parameters.rb",
37
+ "lib/knmi/station.rb",
29
38
  "test/helper.rb",
30
- "test/test_knmi.rb"
39
+ "test/test_httpservice.rb",
40
+ "test/test_knmi.rb",
41
+ "test/test_parameters.rb",
42
+ "test/test_station.rb"
31
43
  ]
32
44
  s.homepage = %q{http://github.com/bullfight/knmi}
33
45
  s.licenses = ["MIT"]
@@ -36,30 +48,45 @@ Gem::Specification.new do |s|
36
48
  s.summary = %q{Ruby API to access daily climate data from the Royal Netherlands Meteorological Institute}
37
49
  s.test_files = [
38
50
  "test/helper.rb",
39
- "test/test_knmi.rb"
51
+ "test/test_httpservice.rb",
52
+ "test/test_knmi.rb",
53
+ "test/test_parameters.rb",
54
+ "test/test_station.rb"
40
55
  ]
41
56
 
42
57
  if s.respond_to? :specification_version then
43
58
  s.specification_version = 3
44
59
 
45
60
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
- s.add_development_dependency(%q<shoulda>, [">= 0"])
61
+ s.add_runtime_dependency(%q<httparty>, ["~> 0.7.7"])
62
+ s.add_runtime_dependency(%q<geokit>, ["~> 1.5.0"])
63
+ s.add_development_dependency(%q<shoulda>, ["~> 2.11.3"])
64
+ s.add_development_dependency(%q<shoulda-context>, ["~> 1.0.0.beta1"])
47
65
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
48
66
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
49
67
  s.add_development_dependency(%q<rcov>, [">= 0"])
68
+ s.add_development_dependency(%q<pry>, ["~> 0.8.3"])
50
69
  s.add_runtime_dependency(%q<httparty>, [">= 0.7.4"])
51
70
  else
52
- s.add_dependency(%q<shoulda>, [">= 0"])
71
+ s.add_dependency(%q<httparty>, ["~> 0.7.7"])
72
+ s.add_dependency(%q<geokit>, ["~> 1.5.0"])
73
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
74
+ s.add_dependency(%q<shoulda-context>, ["~> 1.0.0.beta1"])
53
75
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
54
76
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
55
77
  s.add_dependency(%q<rcov>, [">= 0"])
78
+ s.add_dependency(%q<pry>, ["~> 0.8.3"])
56
79
  s.add_dependency(%q<httparty>, [">= 0.7.4"])
57
80
  end
58
81
  else
59
- s.add_dependency(%q<shoulda>, [">= 0"])
82
+ s.add_dependency(%q<httparty>, ["~> 0.7.7"])
83
+ s.add_dependency(%q<geokit>, ["~> 1.5.0"])
84
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
85
+ s.add_dependency(%q<shoulda-context>, ["~> 1.0.0.beta1"])
60
86
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
61
87
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
62
88
  s.add_dependency(%q<rcov>, [">= 0"])
89
+ s.add_dependency(%q<pry>, ["~> 0.8.3"])
63
90
  s.add_dependency(%q<httparty>, [">= 0.7.4"])
64
91
  end
65
92
  end
Binary file
@@ -1,202 +1,83 @@
1
- require 'httparty'
2
- require 'csv'
1
+ begin
2
+ require 'httparty'
3
+ require 'geokit'
4
+ require 'csv'
5
+ rescue LoadError => e
6
+ if require 'rubygems' then retry
7
+ else raise(e)
8
+ end
9
+ end
10
+
11
+ %w(station parameters httpservice).each { |file| require File.join(File.dirname(__FILE__), 'knmi', file) }
12
+
13
+ module KNMI
3
14
 
4
- class KNMI
5
- include HTTParty
6
- base_uri 'http://www.knmi.nl/climatology/daily_data/getdata_day.cgi'
7
-
8
15
  class << self
9
- private :new
10
16
 
11
- # station1:station2:station15 requires station or list of stations NO DEFAULT
12
- def station(station_number)
13
- if station_number.nil? == true
14
- raise "Station Number Required" # doesn't work look into this
15
- elsif station_number.kind_of?(Array)
16
- "stns=#{station_number * ":"}"
17
- else
18
- "stns=#{station_number}"
19
- end
17
+ #
18
+ # Get nearest station by lat lng
19
+ # KNMI.station_by_location(52.165, 4.419) #=> #<KNMI::Station:0x0000010134e7f0>
20
+ def station_by_location(lat, lng)
21
+ Station.closest_to(lat, lng)
20
22
  end
21
23
 
22
- def check_stations
23
- # stubbed out complete later
24
+ #
25
+ # Get station object by station ID
26
+ # KNMI.station_by_id(210) #=> #<KNMI::Station:0x000001016b9b38>
27
+ def station_by_id(station_id)
28
+ Station.find(station_id)
24
29
  end
25
30
 
26
- # YYYYMMDD Default is first day of current month
27
- def start_date(_start)
28
- "start=#{_start}"
29
- end
31
+ #
32
+ # Generate array of unique daily or hourly parameter objects
33
+ # All details in daily_data_key.yml and hourly_data_key.yml
34
+ # inputs
35
+ # period = "daily" or "hourly"
36
+ # params =
37
+ # categories =
30
38
 
31
- # YYYYMMDD Default is current or last recorded day
32
- def end_date(_end)
33
- "end=#{_end}"
34
- end
35
-
36
- def variables(vars)
37
-
38
- if vars.empty? == true
39
- "vars=ALL"
40
- else
41
- vars, nvars = check_variables(vars)
42
- "vars=#{vars * ":"}"
43
- end
39
+ def parameters(period, params = "", categories = "")
40
+ if params.blank? and categories.blank?
41
+ list = Parameters.all(period)
42
+ else
43
+ list = []
44
+
45
+ # Parameters by category
46
+ list << Parameters.category(period, categories)
47
+ list.flatten!
48
+
49
+ # Parameters by name
50
+ params = unique_params(params, list) #ensure params are unique to list
51
+ list << Parameters.find(period, params)
52
+ list.flatten!
53
+ list.compact!
54
+ end
55
+ return list
44
56
  end
45
-
46
- # Delete Invalid Variables and keep those valid, if none are valid replace with all
47
- def check_variables(vars)
48
-
49
- if vars.kind_of?(Array) == false
50
- vars = [vars]
57
+
58
+ #
59
+ # Input a station object from KNMI.station_by_id or KNMI.station_by_location
60
+ # and a parameter hash of objects from # KNMI.Parameters
61
+ # return
62
+ def get_data(station_object, parameter_object, start_at = nil, end_at = nil, seasonal = false)
63
+ if parameter_object[0].period == "daily"
64
+ HttpService.get_daily(station_object, parameter_object, start_at, end_at, seasonal)
65
+ elsif parameter_object[0].period == "hourly"
66
+ HttpService.get_hourly(station_object, parameter_object, start_at, end_at, seasonal)
51
67
  end
52
-
53
- # Collections of variables and all Possible Variables
54
- varOpts = {
55
- "WIND" => ["DDVEC", "FG", "FHX", "FHX", "FX"],
56
- "TEMP" => ["TG", "TN", "TX", "T10N"],
57
- "SUNR" => ["SQ", "SP", "Q"],
58
- "PRCP" => ["DR", "RH", "EV24"],
59
- "PRES" => ["PG", "PGX", "PGN"],
60
- "VICL" => ["VVN", "VVX", "NG"],
61
- "MSTR" => ["VVN", "VVX", "NG"],
62
- "ALL" => ["DDVEC", "FHVEC", "FG", "FHX",
63
- "FHXH", "FHN", "FHNH", "FXX", "FXXH", "TG", "TN",
64
- "TNH", "TX", "TXH", "T10N", "T10NH", "SQ", "SP",
65
- "Q", "DR", "RH", "RHX", "RHXH", "EV24", "PG", "PX",
66
- "PXH", "PN", "PNH", "VVN", "VVNH", "VVX", "VVXH",
67
- "NG", "UG", "UX", "UXH", "UN", "UNH"]
68
- }
69
-
70
- # Drop in invalid vars
71
- vars = ( vars & varOpts.to_a.flatten )
72
-
73
- # Only Allow Variable to be selected once, All, or TEMP, not ALL & TEMP
74
- # And count the number of selected variables
75
- if vars.include?("ALL") == true or vars.empty? == true
76
- vars = "ALL"
77
- nvars = varOpts["ALL"].count
78
- elsif (vars & varOpts.keys) # check if contains keys
79
-
80
- x = []
81
- (vars & varOpts.keys).each do |k|
82
- x << varOpts.values_at(k)
83
- end
84
- vars = (vars - x.flatten)
85
-
86
- nvars = 0
87
- ( vars & varOpts.keys ).each do |v|
88
- nvars += varOpts[v].count
89
- end
90
-
91
- ( vars - varOpts.keys ).each do |v|
92
- nvars += 1
93
- end
94
- end
95
-
96
- return vars, nvars
97
68
  end
98
-
99
- # Parse Response into hashed arrays
100
- #Other elements
101
- #varlist = res[(7+nstn)..(6 + nstn + nvars)]
102
- #colheader = res[(8 + nstn + nvars)]
103
- #header = res[0..(9 + nstn + nvars)]
104
- def parse(response, station_number, vars)
105
- # Line Index Numbers
106
- nstn = [station_number].flatten.length
107
- vars, nvars = check_variables(vars)
108
-
109
- # Split lines into array
110
- response = response.split(/\n/)
111
-
112
- # Get Station Details
113
- stations = response[5..(4+nstn)]
114
- stations = stations.join.tr("\t", "\s")
115
- stations = stations.tr("#", "")
116
- stations = stations.tr(":", "")
117
- stations = CSV.parse( stations, {:col_sep => "\s"} )
118
- stations = stations.map {|row| row.map {|cell| cell.to_s } }
119
- st_header = [:station_code, :lng, :lat, :elev, :name]
120
- stations = stations.map {|row| Hash[*st_header.zip(row).flatten] }
121
69
 
122
- # Get Variable Details
123
- varlist = response[(7+nstn)..(6 + nstn + nvars)]
124
- varlist = varlist.join
125
- varlist = varlist.gsub(/# /, "")
126
- varlist = varlist.gsub(/\s{2,}/, "")
127
- varlist = varlist.gsub(/;/, "\r")
128
- varlist = CSV.parse( varlist, {:col_sep => "= "} )
129
- vr_header = [:var, :description]
130
- varlist = varlist.map {|row| Hash[*vr_header.zip(row).flatten] }
131
-
132
- # Get and clean data
133
- response = response[(8 + nstn + nvars)..response.length]
134
- response = response.join.tr("\s+", "")
135
- response = response.tr("#", "")
136
-
137
- # Parse into array and then hash with var name
138
- response = CSV.parse(response, {:skip_blanks => true})
139
- header = response.shift.map {|i| i.to_s.intern }
140
- string_data = response.map {|row| row.map {|cell| cell.to_s } }
141
- data = string_data.map {|row| Hash[*header.zip(row).flatten] }
142
-
143
- return {:stations => stations, :variables => varlist, :data => data}
144
- end
145
- end # End Private
146
-
147
- # gets variables from station or list of stations
148
- # with all variables if vars is empty or selected variables passed as an array
149
- # data is from begining of current month to current day
150
- # Example
151
- # station_number = [210, 212]
152
- # vars = "TG"
153
- # res = KNMIdaily.get_station( station_number, vars )
154
- # output {:stations => {:}}
155
- def self.get_station(station_number, vars = "")
156
- query = station(station_number) + "&" + variables(vars)
157
- puts query
158
- res = get("", { :query => query } )
159
- res = parse(res, station_number, vars)
160
- end
161
-
162
- # gets variables from station or list of stations
163
- # with all variables if vars is empty or selected variables passed as an array
164
- # data is from start date to current
165
- def self.get_station_start_to_current(station_number, start, vars = "")
166
- query = station(station_number) + "&" + start_date(start) + "&" + variables(vars)
167
- puts query
168
- res = get("", { :query => query } )
169
- res = parse(res, station_number, vars)
170
- end
171
-
172
- # gets variables from station or list of stations
173
- # with all variables if vars is empty or selected variables passed as an array
174
- # data is from start date to end date
175
- def self.get_station_range(station_number, _start, _end, vars = "")
176
- query = station(station_number) + "&" + start_date(_start) + "&" + end_date(_end) + "&" + variables(vars)
177
- puts query
178
- res = get("", { :query => query } )
179
- res = parse(res, station_number, vars)
180
- end
181
-
182
- # gets variables from station or list of stations
183
- # with all variables if vars is empty or selected variables passed as an array
184
- # seasonal data is from start date to end date
185
- # by selected month and day within each year
186
- def self.get_seasonal(station_number, _start, _end, vars = "")
187
- query = station(station_number) + "&" + "inseason=true" + "&" + start_date(_start) + "&" + end_date(_end) + "&" + variables(vars)
188
- puts query
189
- get("", { :query => query } )
190
- end
191
-
192
- def self.to_csv(filename, response)
193
- CSV.open(filename, "wb") do |csv|
194
- response[:data]
195
- csv << response[0].keys
196
- response[(1..(response.length - 1))].each do |line|
197
- csv << line.values
70
+
71
+ private
72
+
73
+ # Identify and eliminate overlap between params and categories
74
+ def unique_params(params, list)
75
+ list.each do |e|
76
+ params = [params].flatten
77
+ params.delete_if {|p| p == e.parameter }
198
78
  end
79
+ return params
199
80
  end
81
+
200
82
  end
201
-
202
83
  end