rb-grib 0.1.4 → 0.2.0
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/BSDL +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -12
- data/README.rdoc +23 -0
- data/Rakefile +5 -0
- data/data/tp_ecmwf.grib +0 -0
- data/ext/grib.c +376 -68
- data/lib/numru/grib.rb +5 -1
- data/lib/numru/grib/definitions/grib1/localConcepts/kwbc/name.def +125 -0
- data/lib/numru/grib/definitions/grib1/localConcepts/kwbc/paramId.def +126 -0
- data/lib/numru/grib/definitions/grib1/localConcepts/kwbc/shortName.def +125 -0
- data/lib/numru/grib/definitions/grib1/localConcepts/kwbc/units.def +126 -0
- data/lib/numru/grib/definitions/grib2/localConcepts/rjtd/name.def +30 -0
- data/lib/numru/grib/definitions/grib2/localConcepts/rjtd/paramId.def +30 -0
- data/lib/numru/grib/definitions/grib2/localConcepts/rjtd/shortName.def +30 -0
- data/lib/numru/grib/definitions/grib2/localConcepts/rjtd/units.def +30 -0
- data/lib/numru/grib/grib.rb +132 -182
- data/lib/numru/grib/setenv.rb +12 -0
- data/lib/numru/grib/version.rb +1 -1
- data/rb-grib.gemspec +2 -1
- data/spec/grib_read_spec.rb +105 -76
- metadata +48 -7
@@ -0,0 +1,30 @@
|
|
1
|
+
#Total precipitation
|
2
|
+
'Total precipitation' = {
|
3
|
+
discipline = 0 ;
|
4
|
+
parameterCategory = 1 ;
|
5
|
+
parameterNumber = 8 ;
|
6
|
+
}
|
7
|
+
#Total cloud cover
|
8
|
+
'Total cloud cover' = {
|
9
|
+
discipline = 0 ;
|
10
|
+
parameterCategory = 6 ;
|
11
|
+
parameterNumber = 1 ;
|
12
|
+
}
|
13
|
+
#Low cloud cover
|
14
|
+
'Low cloud cover' = {
|
15
|
+
discipline = 0 ;
|
16
|
+
parameterCategory = 6 ;
|
17
|
+
parameterNumber = 3 ;
|
18
|
+
}
|
19
|
+
#Medium cloud cover
|
20
|
+
'Medium cloud cover' = {
|
21
|
+
discipline = 0 ;
|
22
|
+
parameterCategory = 6 ;
|
23
|
+
parameterNumber = 4 ;
|
24
|
+
}
|
25
|
+
#High cloud cover
|
26
|
+
'High cloud cover' = {
|
27
|
+
discipline = 0 ;
|
28
|
+
parameterCategory = 6 ;
|
29
|
+
parameterNumber = 5 ;
|
30
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#Total precipitation
|
2
|
+
'228' = {
|
3
|
+
discipline = 0 ;
|
4
|
+
parameterCategory = 1 ;
|
5
|
+
parameterNumber = 8 ;
|
6
|
+
}
|
7
|
+
#Total cloud cover
|
8
|
+
'164' = {
|
9
|
+
discipline = 0 ;
|
10
|
+
parameterCategory = 6 ;
|
11
|
+
parameterNumber = 1 ;
|
12
|
+
}
|
13
|
+
#Low cloud cover
|
14
|
+
'186' = {
|
15
|
+
discipline = 0 ;
|
16
|
+
parameterCategory = 6 ;
|
17
|
+
parameterNumber = 3 ;
|
18
|
+
}
|
19
|
+
#Medium cloud cover
|
20
|
+
'187' = {
|
21
|
+
discipline = 0 ;
|
22
|
+
parameterCategory = 6 ;
|
23
|
+
parameterNumber = 4 ;
|
24
|
+
}
|
25
|
+
#High cloud cover
|
26
|
+
'188' = {
|
27
|
+
discipline = 0 ;
|
28
|
+
parameterCategory = 6 ;
|
29
|
+
parameterNumber = 5 ;
|
30
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#Total precipitation
|
2
|
+
'tp' = {
|
3
|
+
discipline = 0 ;
|
4
|
+
parameterCategory = 1 ;
|
5
|
+
parameterNumber = 8 ;
|
6
|
+
}
|
7
|
+
#Total cloud cover
|
8
|
+
'tcc' = {
|
9
|
+
discipline = 0 ;
|
10
|
+
parameterCategory = 6 ;
|
11
|
+
parameterNumber = 1 ;
|
12
|
+
}
|
13
|
+
#Low cloud cover
|
14
|
+
'lcc' = {
|
15
|
+
discipline = 0 ;
|
16
|
+
parameterCategory = 6 ;
|
17
|
+
parameterNumber = 3 ;
|
18
|
+
}
|
19
|
+
#Medium cloud cover
|
20
|
+
'mcc' = {
|
21
|
+
discipline = 0 ;
|
22
|
+
parameterCategory = 6 ;
|
23
|
+
parameterNumber = 4 ;
|
24
|
+
}
|
25
|
+
#High cloud cover
|
26
|
+
'hcc' = {
|
27
|
+
discipline = 0 ;
|
28
|
+
parameterCategory = 6 ;
|
29
|
+
parameterNumber = 5 ;
|
30
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#Total precipitation
|
2
|
+
'kg m**-2' = {
|
3
|
+
discipline = 0 ;
|
4
|
+
parameterCategory = 1 ;
|
5
|
+
parameterNumber = 8 ;
|
6
|
+
}
|
7
|
+
#Total cloud cover
|
8
|
+
'%' = {
|
9
|
+
discipline = 0 ;
|
10
|
+
parameterCategory = 6 ;
|
11
|
+
parameterNumber = 1 ;
|
12
|
+
}
|
13
|
+
#Low cloud cover
|
14
|
+
'%' = {
|
15
|
+
discipline = 0 ;
|
16
|
+
parameterCategory = 6 ;
|
17
|
+
parameterNumber = 4 ;
|
18
|
+
}
|
19
|
+
#Medium cloud cover
|
20
|
+
'%' = {
|
21
|
+
discipline = 0 ;
|
22
|
+
parameterCategory = 6 ;
|
23
|
+
parameterNumber = 4 ;
|
24
|
+
}
|
25
|
+
#High cloud cover
|
26
|
+
'%' = {
|
27
|
+
discipline = 0 ;
|
28
|
+
parameterCategory = 6 ;
|
29
|
+
parameterNumber = 5 ;
|
30
|
+
}
|
data/lib/numru/grib/grib.rb
CHANGED
@@ -1,8 +1,3 @@
|
|
1
|
-
require "narray_miss"
|
2
|
-
require "date"
|
3
|
-
require "numru/grib.so"
|
4
|
-
|
5
|
-
|
6
1
|
module NumRu
|
7
2
|
class Grib
|
8
3
|
class << self
|
@@ -45,45 +40,9 @@ module NumRu
|
|
45
40
|
return false
|
46
41
|
end
|
47
42
|
|
48
|
-
def var_names
|
49
|
-
parse_vars unless @vars
|
50
|
-
return @vars.keys
|
51
|
-
end
|
52
|
-
def var(name)
|
53
|
-
parse_vars unless @vars
|
54
|
-
var = @vars[name]
|
55
|
-
return nil if var.nil?
|
56
|
-
return ::NumRu::GribVar.parse(self, var, name)
|
57
|
-
end
|
58
43
|
def inspect
|
59
44
|
"Grib: #{path}"
|
60
45
|
end
|
61
|
-
private
|
62
|
-
def parse_vars
|
63
|
-
vars = Hash.new
|
64
|
-
get_messages.each do |msg|
|
65
|
-
name = msg.sname
|
66
|
-
# zn = msg.z_sname
|
67
|
-
# name << "_" << zn unless /\A(pl|isobaricIn)/ =~ zn
|
68
|
-
hash = (vars[name] ||= Hash.new)
|
69
|
-
nij = [msg.get_value("Ni"), msg.get_value("Nj"), msg.gtype, msg.z_type]
|
70
|
-
if hash[nij]
|
71
|
-
hash[nij][1].push msg
|
72
|
-
else
|
73
|
-
hash[nij] = [hash.length, [msg]]
|
74
|
-
end
|
75
|
-
end
|
76
|
-
@vars = Hash.new
|
77
|
-
vars.each do |name,hash|
|
78
|
-
if hash.length == 1
|
79
|
-
@vars[name] = hash.values[0][1]
|
80
|
-
else
|
81
|
-
hash.each do |k,ary|
|
82
|
-
@vars[name+"_"+ary[0].to_s] = ary[1]
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
46
|
end # class Grib
|
88
47
|
|
89
48
|
class GribMessage
|
@@ -110,7 +69,7 @@ module NumRu
|
|
110
69
|
get_value("indicatorOfTypeOfLevel") || get_value("typeOfLevel") || "level"
|
111
70
|
end
|
112
71
|
def z_value
|
113
|
-
get_value("level")
|
72
|
+
get_value("levels") || get_value("level")
|
114
73
|
end
|
115
74
|
def z_units
|
116
75
|
get_value("pressureUnits")
|
@@ -120,8 +79,8 @@ module NumRu
|
|
120
79
|
h = get_value("dataTime").to_s.rjust(4,"0")
|
121
80
|
return DateTime.parse(d << h)
|
122
81
|
end
|
123
|
-
def
|
124
|
-
get_value("
|
82
|
+
def step
|
83
|
+
get_value("step")
|
125
84
|
end
|
126
85
|
def time_interval
|
127
86
|
get_value("lengthOfTimeRange")
|
@@ -180,136 +139,7 @@ module NumRu
|
|
180
139
|
|
181
140
|
class GribVar
|
182
141
|
Day0 = DateTime.new(1900)
|
183
|
-
MAXNDIM = 7 # lon, lat, lev, t,
|
184
|
-
class << self
|
185
|
-
def parse(file,msgs,name)
|
186
|
-
msg = msgs[0]
|
187
|
-
va = ::NumRu::GribVar.new(file,name)
|
188
|
-
va.put_att("long_name", msg.name)
|
189
|
-
# va.put_att("standard_name",std_name)
|
190
|
-
va.put_att("units",msg.units) if msg.units
|
191
|
-
va.put_att("grid_type", msg.gtype)
|
192
|
-
va.put_att("missing_value", [msg.missing_value]) if msg.missing_value
|
193
|
-
vdim = Array.new
|
194
|
-
xy_dims = 0
|
195
|
-
msg.get_xy.sort{|x,y| x["ij"]<=>y["ij"] }.each do |xy|
|
196
|
-
val = xy.delete("value")
|
197
|
-
sname = xy.delete("short_name")
|
198
|
-
ij = xy.delete("ij")
|
199
|
-
if val.length > 1
|
200
|
-
d = va.def_dim(sname, -1)
|
201
|
-
d.put(val)
|
202
|
-
xy.each{|k,v| d.put_att(k,v)}
|
203
|
-
xy_dims += 1
|
204
|
-
else
|
205
|
-
#xy.each{|k,v| va.put_att(k,v)}
|
206
|
-
end
|
207
|
-
end
|
208
|
-
z = Array.new
|
209
|
-
t = Array.new
|
210
|
-
ft = Array.new
|
211
|
-
ti = Array.new
|
212
|
-
en = Array.new
|
213
|
-
hash = Hash.new
|
214
|
-
msgs.each_with_index do |msg,i|
|
215
|
-
zv = msg.z_value
|
216
|
-
tv = get_time(msg.date)
|
217
|
-
ftv = msg.forecast_time
|
218
|
-
tiv = msg.time_interval
|
219
|
-
env = msg.ensemble_member
|
220
|
-
z.push zv
|
221
|
-
t.push tv
|
222
|
-
ft.push ftv
|
223
|
-
ti.push tiv
|
224
|
-
en.push env
|
225
|
-
ary = [zv,tv,ftv,tiv,env]
|
226
|
-
if hash[ary] # error
|
227
|
-
m1 = msgs[hash[ary]]
|
228
|
-
m2 = msgs[i]
|
229
|
-
a1 = m1.get_keys.map{|k| [k,m1.get_value(k)]}
|
230
|
-
a2 = m2.get_keys.map{|k| [k,m2.get_value(k)]}
|
231
|
-
a = Array.new
|
232
|
-
a1.length.times{|j| a.push [a1[j],a2[j]] if a1[j]!=a2[j]}
|
233
|
-
warn "BUG: send the following message to the developers"
|
234
|
-
p ary
|
235
|
-
p a
|
236
|
-
raise("error")
|
237
|
-
end
|
238
|
-
hash[ary] = i
|
239
|
-
end
|
240
|
-
hash = hash.invert
|
241
|
-
[z, t, ft, ti, en].each{|a| a.uniq!}
|
242
|
-
# [z, t, ft, ti, en].each{|a| a.uniq!; a.sort!}
|
243
|
-
idx = Array.new(msgs.length) do |i|
|
244
|
-
zv, tv, ftv, tiv, env = hash[i]
|
245
|
-
[z.index(zv), t.index(tv), ft.index(ftv), ti.index(tiv), en.index(env)]
|
246
|
-
end
|
247
|
-
del_dims = Array.new
|
248
|
-
if z.length == 1
|
249
|
-
va.put_att("level_type", msg.z_type)
|
250
|
-
va.put_att("level_value", [z[0]])
|
251
|
-
va.put_att("level_units", msg.z_units) if msg.z_units
|
252
|
-
del_dims.push xy_dims
|
253
|
-
else
|
254
|
-
d = va.def_dim(msg.z_sname, -1)
|
255
|
-
d.put_att("long_name", msg.z_type)
|
256
|
-
d.put_att("units", msg.z_units) if msg.z_units
|
257
|
-
d.put(NArray.to_na(z))
|
258
|
-
end
|
259
|
-
if t.length == 1
|
260
|
-
va.put_att("time", msg.date.to_s)
|
261
|
-
del_dims.push xy_dims + 1
|
262
|
-
else
|
263
|
-
d = va.def_dim("time", -1)
|
264
|
-
d.put_att("long_name","time")
|
265
|
-
d.put_att("units","hours since #{Day0.strftime('%Y-%m-%d %H:%M:%S')}")
|
266
|
-
d.put(NArray.to_na(t))
|
267
|
-
end
|
268
|
-
if ft.length == 1
|
269
|
-
va.put_att("forecast_time", [msg.forecast_time]) if msg.forecast_time
|
270
|
-
va.put_att("forcast_time_units", msg.step_units) if msg.step_units
|
271
|
-
del_dims.push xy_dims + 2
|
272
|
-
else
|
273
|
-
d = va.def_dim("forecast_time", -1)
|
274
|
-
d.put_att("long_name", "forecast_time")
|
275
|
-
d.put_att("units", msg.step_units) if msg.step_units
|
276
|
-
d.put(NArray.to_na(ft))
|
277
|
-
end
|
278
|
-
if ti.length == 1
|
279
|
-
del_dims.push xy_dims + 3
|
280
|
-
else
|
281
|
-
d = va.def_dim("time_interval", -1)
|
282
|
-
d.put_att("long_name", "time_interval")
|
283
|
-
d.put_att("units", msg.step_units) if msg.step_units
|
284
|
-
d.put(NArray.to_na(ti))
|
285
|
-
end
|
286
|
-
if en.length == 1
|
287
|
-
del_dims.push xy_dims + 4
|
288
|
-
else
|
289
|
-
d = va.def_dim("member", -1)
|
290
|
-
d.put_att("long_name", "ensemble_member")
|
291
|
-
d.put(NArray.to_na(en))
|
292
|
-
end
|
293
|
-
va.set_msgs(msgs, idx, xy_dims, del_dims)
|
294
|
-
return va
|
295
|
-
end
|
296
|
-
def get_time(date)
|
297
|
-
((date - Day0)*24).to_f
|
298
|
-
end
|
299
|
-
end # class << self
|
300
|
-
attr_reader :file, :name
|
301
|
-
def initialize(file,name)
|
302
|
-
@file = file
|
303
|
-
@name = name
|
304
|
-
@attr = Hash.new
|
305
|
-
@dims = Array.new
|
306
|
-
end
|
307
|
-
def set_msgs(msgs, idx, xy_dims, del_dims)
|
308
|
-
@msgs = msgs
|
309
|
-
@idx = idx
|
310
|
-
@xy_dims = xy_dims
|
311
|
-
@del_dims = del_dims
|
312
|
-
end
|
142
|
+
MAXNDIM = 7 # lon, lat, lev, t, step, timeInterval, ensemble
|
313
143
|
def rank
|
314
144
|
@dims.length
|
315
145
|
end
|
@@ -362,13 +192,14 @@ module NumRu
|
|
362
192
|
mask = nil
|
363
193
|
first = Array.new(rank-@xy_dims,0)
|
364
194
|
if indices.length != 0
|
365
|
-
if indices
|
366
|
-
|
367
|
-
|
368
|
-
indices
|
369
|
-
|
370
|
-
|
195
|
+
if indices.include?(false)
|
196
|
+
sha2 = sha.dup
|
197
|
+
sha2.delete(false)
|
198
|
+
raise ArgumentError, 'multiple "false" in indices' if sha.length - sha2.length > 1
|
199
|
+
indices[indices.index(false)] = [true]*(sha.length-indices.length+1)
|
200
|
+
indices.flatten!
|
371
201
|
end
|
202
|
+
|
372
203
|
rank.times{|n|
|
373
204
|
ind = indices[n]
|
374
205
|
case ind
|
@@ -422,7 +253,7 @@ module NumRu
|
|
422
253
|
val.reshape!(*shape[0...@xy_dims])
|
423
254
|
# val = msg.get_data[2].reshape!(*shape[0...@xy_dims])
|
424
255
|
unless indices.length==0 || indices[0...@xy_dims].inject(true){|t,v| t &&= v==true}
|
425
|
-
val = val
|
256
|
+
val = val.slice(*indices[0...@xy_dims])
|
426
257
|
end
|
427
258
|
end
|
428
259
|
(MAXNDIM-2).times do |i| index[i] = idx[i]-first[i] end
|
@@ -442,7 +273,126 @@ module NumRu
|
|
442
273
|
alias :[] :get
|
443
274
|
alias :val :get
|
444
275
|
def inspect
|
445
|
-
"GribVar: #{name} in #{
|
276
|
+
"GribVar: #{name} in #{file.path}, [#{shape.join(",")}]"
|
277
|
+
end
|
278
|
+
private
|
279
|
+
def get_time(date)
|
280
|
+
(date - Day0).to_f*24
|
281
|
+
end
|
282
|
+
def init
|
283
|
+
@attr = Hash.new
|
284
|
+
@dims = Array.new
|
285
|
+
msgs = get_messages
|
286
|
+
msg = msgs[0]
|
287
|
+
put_att("long_name", msg.name)
|
288
|
+
# put_att("standard_name",std_name)
|
289
|
+
put_att("units",msg.units) if msg.units
|
290
|
+
put_att("grid_type", msg.gtype)
|
291
|
+
put_att("missing_value", [msg.missing_value]) if msg.missing_value
|
292
|
+
vdim = Array.new
|
293
|
+
xy_dims = 0
|
294
|
+
msg.get_xy.sort{|x,y| x["ij"]<=>y["ij"] }.each do |xy|
|
295
|
+
val = xy.delete("value")
|
296
|
+
sname = xy.delete("short_name")
|
297
|
+
ij = xy.delete("ij")
|
298
|
+
if val.length > 1
|
299
|
+
d = def_dim(sname, -1)
|
300
|
+
d.put(val)
|
301
|
+
xy.each{|k,v| d.put_att(k,v)}
|
302
|
+
xy_dims += 1
|
303
|
+
else
|
304
|
+
#xy.each{|k,v| va.put_att(k,v)}
|
305
|
+
end
|
306
|
+
end
|
307
|
+
z = Array.new
|
308
|
+
t = Array.new
|
309
|
+
st = Array.new
|
310
|
+
ti = Array.new
|
311
|
+
en = Array.new
|
312
|
+
hash = Hash.new
|
313
|
+
msgs.each_with_index do |msg,i|
|
314
|
+
zv = msg.z_value
|
315
|
+
tv = get_time(msg.date)
|
316
|
+
stv = msg.step
|
317
|
+
tiv = msg.time_interval
|
318
|
+
env = msg.ensemble_member
|
319
|
+
z.push zv
|
320
|
+
t.push tv
|
321
|
+
st.push stv
|
322
|
+
ti.push tiv
|
323
|
+
en.push env
|
324
|
+
ary = [zv,tv,stv,tiv,env]
|
325
|
+
if hash[ary] # error
|
326
|
+
m1 = msgs[hash[ary]]
|
327
|
+
m2 = msgs[i]
|
328
|
+
a1 = m1.get_keys.map{|k| [k,m1.get_value(k)]}
|
329
|
+
a2 = m2.get_keys.map{|k| [k,m2.get_value(k)]}
|
330
|
+
a = Array.new
|
331
|
+
a1.length.times{|j| a.push [a1[j],a2[j]] if a1[j]!=a2[j]}
|
332
|
+
warn "BUG: send the following message to the developers"
|
333
|
+
p self
|
334
|
+
p ary
|
335
|
+
p a
|
336
|
+
raise("error")
|
337
|
+
end
|
338
|
+
hash[ary] = i
|
339
|
+
end
|
340
|
+
[z, t, st, ti, en].each{|a| a.uniq!}
|
341
|
+
# [z, t, st, ti, en].each{|a| a.uniq!; a.sort!}
|
342
|
+
@idx = Array.new(msgs.length)
|
343
|
+
hash.each do |ary, i|
|
344
|
+
zv, tv, stv, tiv, env = ary
|
345
|
+
@idx[i] = [z.index(zv), t.index(tv), st.index(stv), ti.index(tiv), en.index(env)]
|
346
|
+
end
|
347
|
+
del_dims = Array.new
|
348
|
+
if z.length == 1
|
349
|
+
put_att("level_type", msg.z_type)
|
350
|
+
put_att("level_value", z[0].kind_of?(Numeric) ? [z[0]] : z[0])
|
351
|
+
put_att("level_units", msg.z_units) if msg.z_units
|
352
|
+
del_dims.push xy_dims
|
353
|
+
else
|
354
|
+
d = def_dim(msg.z_sname, -1)
|
355
|
+
d.put_att("long_name", msg.z_type)
|
356
|
+
d.put_att("units", msg.z_units) if msg.z_units
|
357
|
+
d.put(NArray.to_na(z))
|
358
|
+
end
|
359
|
+
if t.length == 1
|
360
|
+
put_att("time", msg.date.to_s)
|
361
|
+
del_dims.push xy_dims + 1
|
362
|
+
else
|
363
|
+
d = def_dim("time", -1)
|
364
|
+
d.put_att("long_name","time")
|
365
|
+
d.put_att("units","hours since #{Day0.strftime('%Y-%m-%d %H:%M:%S')}")
|
366
|
+
d.put(NArray.to_na(t))
|
367
|
+
end
|
368
|
+
if st.length == 1
|
369
|
+
put_att("step", [msg.step]) if msg.step
|
370
|
+
put_att("step_units", msg.step_units) if msg.step_units
|
371
|
+
del_dims.push xy_dims + 2
|
372
|
+
else
|
373
|
+
d = def_dim("step", -1)
|
374
|
+
d.put_att("long_name", "step")
|
375
|
+
d.put_att("units", msg.step_units) if msg.step_units
|
376
|
+
d.put(NArray.to_na(st))
|
377
|
+
end
|
378
|
+
if ti.length == 1
|
379
|
+
del_dims.push xy_dims + 3
|
380
|
+
else
|
381
|
+
d = def_dim("time_interval", -1)
|
382
|
+
d.put_att("long_name", "time_interval")
|
383
|
+
d.put_att("units", msg.step_units) if msg.step_units
|
384
|
+
d.put(NArray.to_na(ti))
|
385
|
+
end
|
386
|
+
if en.length == 1
|
387
|
+
del_dims.push xy_dims + 4
|
388
|
+
else
|
389
|
+
d = def_dim("member", -1)
|
390
|
+
d.put_att("long_name", "ensemble_member")
|
391
|
+
d.put(NArray.to_na(en))
|
392
|
+
end
|
393
|
+
@xy_dims = xy_dims
|
394
|
+
@del_dims = del_dims
|
395
|
+
@msgs = msgs
|
446
396
|
end
|
447
397
|
end # class GribVar
|
448
398
|
|