carray-netcdf 1.0.13 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -0
- data/CONTRIBUTING.md +75 -0
- data/LICENSE +21 -0
- data/README.md +108 -2
- data/bin/ncheader +223 -0
- data/bin/ncvar +160 -0
- data/carray-netcdf.gemspec +37 -19
- data/docs/00_introduction.md +72 -0
- data/docs/01_getting_started.md +114 -0
- data/docs/02_reading.md +216 -0
- data/docs/03_writing.md +418 -0
- data/docs/04_codec.md +373 -0
- data/docs/05_groups.md +200 -0
- data/docs/06_layer1.md +160 -0
- data/docs/07_cookbook.md +345 -0
- data/docs/08_cheatsheet.md +326 -0
- data/docs/09_cli.md +79 -0
- data/lib/carray/netcdf/codec.rb +536 -0
- data/lib/carray/netcdf/dim.rb +67 -0
- data/lib/carray/netcdf/file.rb +354 -0
- data/lib/carray/netcdf/group.rb +517 -0
- data/lib/carray/netcdf/var.rb +539 -0
- data/lib/carray/netcdf/version.rb +5 -0
- data/lib/carray/netcdf.rb +287 -0
- data/lib/carray-netcdf.rb +3 -1
- metadata +80 -25
- data/API.md +0 -270
- data/Rakefile +0 -12
- data/bin/ncdef2yaml +0 -60
- data/examples/test.nc +0 -0
- data/examples/test.rb +0 -41
- data/examples/time.rb +0 -11
- data/ext/extconf.rb +0 -11
- data/ext/rb_netcdflib.c +0 -2674
- data/lib/io/netcdf.rb +0 -848
data/lib/io/netcdf.rb
DELETED
|
@@ -1,848 +0,0 @@
|
|
|
1
|
-
require "carray"
|
|
2
|
-
require "carray/netcdflib.so"
|
|
3
|
-
|
|
4
|
-
module NC
|
|
5
|
-
|
|
6
|
-
module_function
|
|
7
|
-
|
|
8
|
-
def nc_decode (fd, varid, data)
|
|
9
|
-
if fill_value = nc_get_att(fd, varid, "_FillValue")
|
|
10
|
-
data[:eq, fill_value] = UNDEF
|
|
11
|
-
end
|
|
12
|
-
if scale_factor = nc_get_att(fd, varid, "scale_factor")
|
|
13
|
-
data *= scale_factor
|
|
14
|
-
end
|
|
15
|
-
if add_offset = nc_get_att(fd, varid, "add_offset")
|
|
16
|
-
data += add_offset
|
|
17
|
-
end
|
|
18
|
-
return data
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def nc_put_att_simple (fd, varid, name, val)
|
|
22
|
-
case val
|
|
23
|
-
when Float
|
|
24
|
-
nc_put_att(fd, varid, name, CA_DOUBLE(val))
|
|
25
|
-
when Integer
|
|
26
|
-
nc_put_att(fd, varid, name, CA_INT(val))
|
|
27
|
-
else
|
|
28
|
-
nc_put_att(fd, varid, name, val)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def nc_put_var_all (fd, varid, val)
|
|
33
|
-
data_type = NC.ca_type(nc_inq_vartype(fd, varid))
|
|
34
|
-
dim = (0...nc_inq_varndims(fd, varid)).collect do |dimid|
|
|
35
|
-
nc_inq_dimlen(fd, dimid)
|
|
36
|
-
end
|
|
37
|
-
data = CArray.new(data_type, dim) { val }
|
|
38
|
-
nc_put_var(fd, varid, data)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
class NCObject
|
|
44
|
-
|
|
45
|
-
include NC
|
|
46
|
-
|
|
47
|
-
def get_attributes (file_id, var_id)
|
|
48
|
-
attrs = {}
|
|
49
|
-
varnatts = nc_inq_varnatts(file_id, var_id)
|
|
50
|
-
varnatts.times do |i|
|
|
51
|
-
attname = nc_inq_attname(file_id, var_id, i)
|
|
52
|
-
value = nc_get_att(file_id, var_id, attname)
|
|
53
|
-
attrs[attname] = value
|
|
54
|
-
end
|
|
55
|
-
return attrs.freeze
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def get_attributes! (file_id, var_id)
|
|
59
|
-
attrs = {}
|
|
60
|
-
varnatts = nc_inq_varnatts(file_id, var_id)
|
|
61
|
-
varnatts.times do |i|
|
|
62
|
-
attname = nc_inq_attname(file_id, var_id, i)
|
|
63
|
-
type = nc_inq_atttype(file_id, var_id, attname)
|
|
64
|
-
case type
|
|
65
|
-
when NC_CHAR
|
|
66
|
-
value = nc_get_att(file_id, var_id, attname)
|
|
67
|
-
else
|
|
68
|
-
len = nc_inq_attlen(file_id, var_id, attname)
|
|
69
|
-
value = CArray.new(NC.ca_type(type), [len])
|
|
70
|
-
nc_get_att(file_id, var_id, attname, value)
|
|
71
|
-
end
|
|
72
|
-
attrs[attname] = value
|
|
73
|
-
end
|
|
74
|
-
return attrs.freeze
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def attribute (name)
|
|
78
|
-
return @attributes[name]
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
attr_reader :attributes
|
|
82
|
-
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
class NCVar < NCObject
|
|
86
|
-
|
|
87
|
-
include NC
|
|
88
|
-
|
|
89
|
-
TYPENAME = {
|
|
90
|
-
NC_CHAR => "char",
|
|
91
|
-
NC_BYTE => "byte",
|
|
92
|
-
NC_SHORT => "short",
|
|
93
|
-
NC_INT => "int",
|
|
94
|
-
NC_FLOAT => "float",
|
|
95
|
-
NC_DOUBLE => "double",
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
def initialize (ncfile, var_id)
|
|
99
|
-
@ncfile = ncfile
|
|
100
|
-
@file_id = ncfile.file_id
|
|
101
|
-
@var_id = var_id
|
|
102
|
-
@name = nc_inq_varname(@file_id, var_id)
|
|
103
|
-
@vartype = nc_inq_vartype(@file_id, var_id)
|
|
104
|
-
@dims = ncfile.dims.values_at(*nc_inq_vardimid(@file_id, var_id))
|
|
105
|
-
@shape = @dims.map{|d| d.len}
|
|
106
|
-
@attributes = get_attributes(@file_id, var_id)
|
|
107
|
-
@dims.freeze
|
|
108
|
-
@shape.freeze
|
|
109
|
-
@attributes.freeze
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
attr_reader :name, :dims, :attributes
|
|
113
|
-
|
|
114
|
-
def definition
|
|
115
|
-
defs = { "type" => TYPENAME[@vartype] }
|
|
116
|
-
if not dims.empty?
|
|
117
|
-
defs["dim"] = dims.map{|x| x.name }
|
|
118
|
-
end
|
|
119
|
-
atts = {}
|
|
120
|
-
get_attributes!(@file_id, @var_id).each do |k,v|
|
|
121
|
-
atts[k] = NCFile.convert_attribute_value(v)
|
|
122
|
-
end
|
|
123
|
-
defs.update(atts)
|
|
124
|
-
return defs
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def inspect
|
|
128
|
-
return "#{@name}#{@dims}"
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def is_dim?
|
|
132
|
-
begin
|
|
133
|
-
nc_inq_dimlen(@file_id, @var_id)
|
|
134
|
-
return true
|
|
135
|
-
rescue RuntimeError
|
|
136
|
-
return false
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def decode (value)
|
|
141
|
-
if @attributes.has_key?("_FillValue")
|
|
142
|
-
fill_value = @attributes["_FillValue"]
|
|
143
|
-
case value
|
|
144
|
-
when CArray
|
|
145
|
-
value[:eq, fill_value] = UNDEF
|
|
146
|
-
else
|
|
147
|
-
value = UNDEF if value == fill_value
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
if @attributes.has_key?("missing_value")
|
|
151
|
-
missing_values = [@attributes["missing_value"]].flatten
|
|
152
|
-
missing_values.each do |mv|
|
|
153
|
-
case value
|
|
154
|
-
when CArray
|
|
155
|
-
value[:eq, mv] = UNDEF
|
|
156
|
-
else
|
|
157
|
-
if value == mv
|
|
158
|
-
value = UNDEF
|
|
159
|
-
break
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
if @attributes.has_key?("scale_factor")
|
|
165
|
-
value *= @attributes["scale_factor"]
|
|
166
|
-
end
|
|
167
|
-
if @attributes.has_key?("add_offset")
|
|
168
|
-
value += @attributes["add_offset"]
|
|
169
|
-
end
|
|
170
|
-
return value
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def to_ca
|
|
174
|
-
return self[]
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
TIME_UNITS = {
|
|
178
|
-
"day" => 86400,
|
|
179
|
-
"days" => 86400,
|
|
180
|
-
"d" => 86400,
|
|
181
|
-
"hour" => 3600,
|
|
182
|
-
"hours" => 3600,
|
|
183
|
-
"hr" => 3600,
|
|
184
|
-
"h" => 3600,
|
|
185
|
-
"minute" => 60,
|
|
186
|
-
"minutes" => 60,
|
|
187
|
-
"min" => 60,
|
|
188
|
-
"second" => 1,
|
|
189
|
-
"seconds" => 1,
|
|
190
|
-
"sec" => 1,
|
|
191
|
-
"s" => 1,
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
def to_time
|
|
195
|
-
|
|
196
|
-
if not @attributes.has_key?("units")
|
|
197
|
-
raise "variable #{@name} has no attribute 'units'"
|
|
198
|
-
elsif @attributes["units"] =~ /\A(.+?)\s+since\s+(.+)\Z/
|
|
199
|
-
interval = TIME_UNITS[$1]
|
|
200
|
-
basetime = Time.parse($2)
|
|
201
|
-
if interval
|
|
202
|
-
return self[].convert(:object) {|x| basetime + interval*x }
|
|
203
|
-
else
|
|
204
|
-
raise "#{$1} is invalid for time units"
|
|
205
|
-
end
|
|
206
|
-
else
|
|
207
|
-
raise "format of #{@name}:units is not valid for time"
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def [] (*argv)
|
|
212
|
-
return get!(*argv)
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
def get (*argv)
|
|
216
|
-
if argv.size > 0 and argv[0].is_a?(Struct::CAIndexInfo)
|
|
217
|
-
info = argv.shift
|
|
218
|
-
else
|
|
219
|
-
info = CArray.scan_index(@shape, argv)
|
|
220
|
-
end
|
|
221
|
-
out = nil
|
|
222
|
-
case info.type
|
|
223
|
-
when CA_REG_ADDRESS
|
|
224
|
-
addr = info.index[0]
|
|
225
|
-
index = []
|
|
226
|
-
(0..@shape.size-1).reverse_each do |i|
|
|
227
|
-
index[i] = addr % @shape[i]
|
|
228
|
-
addr /= @shape[i]
|
|
229
|
-
end
|
|
230
|
-
out = get_var1(*index)
|
|
231
|
-
when CA_REG_FLATTEN
|
|
232
|
-
out = get_var[nil]
|
|
233
|
-
when CA_REG_POINT
|
|
234
|
-
out = get_var1(*info.index)
|
|
235
|
-
when CA_REG_ALL
|
|
236
|
-
out = get_var()
|
|
237
|
-
when CA_REG_BLOCK
|
|
238
|
-
use_compact = false
|
|
239
|
-
start = []
|
|
240
|
-
count = []
|
|
241
|
-
stride = []
|
|
242
|
-
info.index.each do |idx|
|
|
243
|
-
case idx
|
|
244
|
-
when Array
|
|
245
|
-
start << idx[0]
|
|
246
|
-
count << idx[1]
|
|
247
|
-
stride << idx[2]
|
|
248
|
-
else
|
|
249
|
-
use_compact = true
|
|
250
|
-
start << idx
|
|
251
|
-
count << 1
|
|
252
|
-
stride << 1
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
if stride.all?{|x| x == 1 }
|
|
256
|
-
out = get_vara(start, count)
|
|
257
|
-
else
|
|
258
|
-
out = get_vars(start, count, stride)
|
|
259
|
-
end
|
|
260
|
-
if use_compact and out.is_a?(CArray)
|
|
261
|
-
out = out.compact
|
|
262
|
-
end
|
|
263
|
-
when CA_REG_SELECT, CA_REG_GRID
|
|
264
|
-
out = get_var[*argv]
|
|
265
|
-
else
|
|
266
|
-
raise "invalid index"
|
|
267
|
-
end
|
|
268
|
-
return out
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
def get! (*argv)
|
|
272
|
-
info = CArray.scan_index(@shape, argv)
|
|
273
|
-
case info.type
|
|
274
|
-
when CA_REG_METHOD_CALL
|
|
275
|
-
return decode(get_var)[*argv]
|
|
276
|
-
else
|
|
277
|
-
return decode(get(info, *argv))
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def get_var1 (*index)
|
|
282
|
-
return nc_get_var1(@file_id, @var_id, index)
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
def get_var1! (*index)
|
|
286
|
-
return decode(get_var1(*index))
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def get_var ()
|
|
290
|
-
return nc_get_var(@file_id, @var_id)
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
def get_var! ()
|
|
294
|
-
return decode(nc_get_var(@file_id, @var_id))
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
def get_vara (start, count)
|
|
298
|
-
return nc_get_vara(@file_id, @var_id, start, count)
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
def get_vara! (start, count)
|
|
302
|
-
return decode(nc_get_vara(@file_id, @var_id, start, count))
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
def get_vars (start, count, stride)
|
|
306
|
-
return nc_get_vars(@file_id, @var_id, start, count, stride)
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
def get_vars! (start, count, stride)
|
|
310
|
-
return decode(nc_get_vars(@file_id, @var_id, start, count, stride))
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
def get_varm (start, count, stride, imap)
|
|
314
|
-
return nc_get_varm(@file_id, @var_id, start, count, stride, imap)
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
def get_varm! (start, count, stride, imap)
|
|
318
|
-
return decode(nc_get_varm(@file_id, @var_id, start, count, stride, imap))
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
def method_missing (id, *argv)
|
|
322
|
-
to_ca.send(id, *argv)
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
class NCDim < NCObject
|
|
328
|
-
|
|
329
|
-
include NC
|
|
330
|
-
|
|
331
|
-
def initialize (ncfile, dim_id)
|
|
332
|
-
@ncfile = ncfile
|
|
333
|
-
@file_id = ncfile.file_id
|
|
334
|
-
@dim_id = dim_id
|
|
335
|
-
@name = nc_inq_dimname(@file_id, @dim_id)
|
|
336
|
-
@len = nc_inq_dimlen(@file_id, @dim_id)
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
attr_reader :name, :len
|
|
340
|
-
|
|
341
|
-
def definition
|
|
342
|
-
return @len
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
def inspect
|
|
346
|
-
return "#{@name}=#{@len}"
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
def to_i
|
|
350
|
-
return @dim_id
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
def to_ca
|
|
354
|
-
return self[]
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
def [] (*argv)
|
|
358
|
-
return @ncfile[name][*argv]
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
class NCFile < NCObject
|
|
364
|
-
|
|
365
|
-
include NC
|
|
366
|
-
|
|
367
|
-
def self.open (filename)
|
|
368
|
-
file_id = NC.open(filename)
|
|
369
|
-
if block_given?
|
|
370
|
-
begin
|
|
371
|
-
nc = NCFile.new(file_id)
|
|
372
|
-
yield(nc)
|
|
373
|
-
ensure
|
|
374
|
-
nc.close
|
|
375
|
-
end
|
|
376
|
-
else
|
|
377
|
-
return NCFile.new(file_id)
|
|
378
|
-
end
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
def initialize (file_id)
|
|
382
|
-
@file_id = file_id
|
|
383
|
-
@dims = []
|
|
384
|
-
@vars = []
|
|
385
|
-
@name2dim = {}
|
|
386
|
-
@name2var = {}
|
|
387
|
-
@attributes = get_attributes(@file_id, NC_GLOBAL)
|
|
388
|
-
@attributes.freeze
|
|
389
|
-
parse_metadata()
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
attr_reader :file_id, :dims, :vars, :attributes
|
|
393
|
-
|
|
394
|
-
def close ()
|
|
395
|
-
nc_close(@file_id)
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
def parse_metadata ()
|
|
399
|
-
ndims = nc_inq_ndims(@file_id)
|
|
400
|
-
ndims.times do |i|
|
|
401
|
-
dim = NCDim.new(self, i)
|
|
402
|
-
@dims[i] = dim
|
|
403
|
-
@name2dim[dim.name] = dim
|
|
404
|
-
end
|
|
405
|
-
@dims.freeze
|
|
406
|
-
@name2dim.freeze
|
|
407
|
-
nvars = nc_inq_nvars(@file_id)
|
|
408
|
-
nvars.times do |i|
|
|
409
|
-
var = NCVar.new(self, i)
|
|
410
|
-
@vars[i] = var
|
|
411
|
-
@name2var[var.name] = var
|
|
412
|
-
end
|
|
413
|
-
@vars.freeze
|
|
414
|
-
@name2var.freeze
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
def self.convert_attribute_value (value)
|
|
418
|
-
case value
|
|
419
|
-
when CArray
|
|
420
|
-
if value.length == 1
|
|
421
|
-
case value.data_type
|
|
422
|
-
when CA_UINT8
|
|
423
|
-
value = { "char" => value[0] }
|
|
424
|
-
when CA_INT8
|
|
425
|
-
value = { "byte" => value[0] }
|
|
426
|
-
when CA_SHORT
|
|
427
|
-
value = { "short" => value[0] }
|
|
428
|
-
when CA_INT
|
|
429
|
-
value = { "int" => value[0] }
|
|
430
|
-
when CA_FLOAT
|
|
431
|
-
value = { "float" => value[0] }
|
|
432
|
-
when CA_DOUBLE
|
|
433
|
-
value = { "double" => value[0] }
|
|
434
|
-
end
|
|
435
|
-
else
|
|
436
|
-
case value.data_type
|
|
437
|
-
when CA_UINT8
|
|
438
|
-
value = { "char" => value.to_a }
|
|
439
|
-
when CA_INT8
|
|
440
|
-
value = { "byte" => value.to_a }
|
|
441
|
-
when CA_SHORT
|
|
442
|
-
value = { "short" => value.to_a }
|
|
443
|
-
when CA_INT
|
|
444
|
-
value = { "int" => value.to_a }
|
|
445
|
-
when CA_FLOAT
|
|
446
|
-
value = { "float" => value.to_a }
|
|
447
|
-
when CA_DOUBLE
|
|
448
|
-
value = { "double" => value.to_a }
|
|
449
|
-
end
|
|
450
|
-
end
|
|
451
|
-
end
|
|
452
|
-
return value
|
|
453
|
-
end
|
|
454
|
-
|
|
455
|
-
def definition
|
|
456
|
-
atts = {}
|
|
457
|
-
get_attributes!(@file_id, NC_GLOBAL).each do |k,v|
|
|
458
|
-
atts[k] = NCFile.convert_attribute_value(v)
|
|
459
|
-
end
|
|
460
|
-
{
|
|
461
|
-
"dimensions" => @dims.map{|x| [x.name, x.definition] }.to_h,
|
|
462
|
-
"variables" => @vars.map{|x| [x.name, x.definition] }.to_h,
|
|
463
|
-
"attributes" => atts
|
|
464
|
-
}
|
|
465
|
-
end
|
|
466
|
-
|
|
467
|
-
def [] (name)
|
|
468
|
-
return @name2var[name]
|
|
469
|
-
end
|
|
470
|
-
|
|
471
|
-
def dim (name)
|
|
472
|
-
return @name2dim[name]
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
def has_dim?(name)
|
|
476
|
-
return @name2dim.has_key?(name)
|
|
477
|
-
end
|
|
478
|
-
|
|
479
|
-
def has_var?(name)
|
|
480
|
-
return @name2var.has_key?(name)
|
|
481
|
-
end
|
|
482
|
-
|
|
483
|
-
end
|
|
484
|
-
|
|
485
|
-
class NCFileWriter
|
|
486
|
-
|
|
487
|
-
include NC
|
|
488
|
-
|
|
489
|
-
class Var
|
|
490
|
-
|
|
491
|
-
include NC
|
|
492
|
-
|
|
493
|
-
TYPEMAP = {
|
|
494
|
-
"char" => NC_CHAR,
|
|
495
|
-
"byte" => NC_BYTE,
|
|
496
|
-
"short" => NC_SHORT,
|
|
497
|
-
"int" => NC_INT,
|
|
498
|
-
"float" => NC_FLOAT,
|
|
499
|
-
"double" => NC_DOUBLE,
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
def initialize (ncfile, name, definition, compression = 0, define_mode = true)
|
|
503
|
-
@ncfile = ncfile
|
|
504
|
-
@file_id = ncfile.file_id
|
|
505
|
-
@name = name
|
|
506
|
-
@type = definition["type"] || NC_FLOAT
|
|
507
|
-
if @type.is_a?(String)
|
|
508
|
-
if TYPEMAP.include?(@type)
|
|
509
|
-
@type = TYPEMAP[@type]
|
|
510
|
-
else
|
|
511
|
-
raise "invalid variable data type"
|
|
512
|
-
end
|
|
513
|
-
end
|
|
514
|
-
@dims = definition["dim"]
|
|
515
|
-
if @dims
|
|
516
|
-
@shape = @dims.map{|key| @ncfile.dim(key).len }
|
|
517
|
-
dim_ids = @dims.map{|key| @ncfile.dim(key).to_i }
|
|
518
|
-
else
|
|
519
|
-
dim_ids = []
|
|
520
|
-
end
|
|
521
|
-
if define_mode
|
|
522
|
-
@var_id = nc_def_var(@file_id, @name, @type, dim_ids)
|
|
523
|
-
if compression != 0
|
|
524
|
-
nc_def_var_deflate(@file_id, @var_id, 1, 1, compression)
|
|
525
|
-
end
|
|
526
|
-
else
|
|
527
|
-
@var_id = nc_inq_varid(@file_id, @name)
|
|
528
|
-
end
|
|
529
|
-
@attributes = definition.dup
|
|
530
|
-
@attributes.delete("type")
|
|
531
|
-
@attributes.delete("dim")
|
|
532
|
-
if define_mode
|
|
533
|
-
@attributes.each do |name, value|
|
|
534
|
-
value = NCFileWriter.convert_attribute_value(value)
|
|
535
|
-
begin
|
|
536
|
-
nc_put_att(@file_id, @var_id, name, value)
|
|
537
|
-
rescue => e
|
|
538
|
-
raise(e.class, e.message + " {#{name}: #{value}}")
|
|
539
|
-
end
|
|
540
|
-
end
|
|
541
|
-
end
|
|
542
|
-
@attributes.freeze
|
|
543
|
-
end
|
|
544
|
-
|
|
545
|
-
attr_reader :name, :type, :attributes
|
|
546
|
-
|
|
547
|
-
def to_i
|
|
548
|
-
return @var_id
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
def []= (*argv)
|
|
552
|
-
put(*argv)
|
|
553
|
-
end
|
|
554
|
-
|
|
555
|
-
def put (*argv)
|
|
556
|
-
value = argv.pop
|
|
557
|
-
info = CArray.scan_index(@shape, argv)
|
|
558
|
-
case info.type
|
|
559
|
-
when CA_REG_ADDRESS
|
|
560
|
-
addr = info.index[0]
|
|
561
|
-
index = []
|
|
562
|
-
(0..@shape.size-1).reverse_each do |i|
|
|
563
|
-
index[i] = addr % @shape[i]
|
|
564
|
-
addr /= @shape[i]
|
|
565
|
-
end
|
|
566
|
-
put_var1(index, value)
|
|
567
|
-
when CA_REG_FLATTEN
|
|
568
|
-
put_var(value)
|
|
569
|
-
when CA_REG_POINT
|
|
570
|
-
put_var1(info.index, value)
|
|
571
|
-
when CA_REG_ALL
|
|
572
|
-
put_var(value)
|
|
573
|
-
when CA_REG_BLOCK
|
|
574
|
-
start = []
|
|
575
|
-
count = []
|
|
576
|
-
stride = []
|
|
577
|
-
info.index.each do |idx|
|
|
578
|
-
case idx
|
|
579
|
-
when Array
|
|
580
|
-
start << idx[0]
|
|
581
|
-
count << idx[1]
|
|
582
|
-
stride << idx[2]
|
|
583
|
-
else
|
|
584
|
-
start << idx
|
|
585
|
-
count << 1
|
|
586
|
-
stride << 1
|
|
587
|
-
end
|
|
588
|
-
end
|
|
589
|
-
if stride.all?{|x| x == 1 }
|
|
590
|
-
put_vara(start, count, value)
|
|
591
|
-
else
|
|
592
|
-
put_vars(start, count, stride, value)
|
|
593
|
-
end
|
|
594
|
-
else
|
|
595
|
-
raise "invalid index"
|
|
596
|
-
end
|
|
597
|
-
end
|
|
598
|
-
|
|
599
|
-
def put_var1 (index, value)
|
|
600
|
-
return nc_put_var1(@file_id, @var_id, index, value)
|
|
601
|
-
end
|
|
602
|
-
|
|
603
|
-
def put_var (value)
|
|
604
|
-
return nc_put_var(@file_id, @var_id, value)
|
|
605
|
-
end
|
|
606
|
-
|
|
607
|
-
def put_vara (start, count, value)
|
|
608
|
-
return nc_put_vara(@file_id, @var_id, start, count, value)
|
|
609
|
-
end
|
|
610
|
-
|
|
611
|
-
def put_vars (start, count, stride, value)
|
|
612
|
-
return nc_put_vars(@file_id, @var_id, start, count, stride, value)
|
|
613
|
-
end
|
|
614
|
-
|
|
615
|
-
def get_varm (start, count, stride, imap, value)
|
|
616
|
-
return nc_put_varm(@file_id, @var_id, start, count, stride, imap, value)
|
|
617
|
-
end
|
|
618
|
-
|
|
619
|
-
end
|
|
620
|
-
|
|
621
|
-
class Dim
|
|
622
|
-
|
|
623
|
-
include NC
|
|
624
|
-
|
|
625
|
-
def initialize (ncfile, name, len, define_mode = true)
|
|
626
|
-
@ncfile = ncfile
|
|
627
|
-
@file_id = ncfile.file_id
|
|
628
|
-
@name = name
|
|
629
|
-
@len = len
|
|
630
|
-
if define_mode
|
|
631
|
-
@dim_id = nc_def_dim(@file_id, @name, @len)
|
|
632
|
-
else
|
|
633
|
-
@dim_id = nc_inq_dimid(@file_id, @name)
|
|
634
|
-
end
|
|
635
|
-
end
|
|
636
|
-
|
|
637
|
-
attr_reader :name, :len
|
|
638
|
-
|
|
639
|
-
def to_i
|
|
640
|
-
return @dim_id
|
|
641
|
-
end
|
|
642
|
-
|
|
643
|
-
end
|
|
644
|
-
|
|
645
|
-
TYPEMAP = {
|
|
646
|
-
"char" => NC_CHAR,
|
|
647
|
-
"byte" => NC_BYTE,
|
|
648
|
-
"short" => NC_SHORT,
|
|
649
|
-
"int" => NC_INT,
|
|
650
|
-
"float" => NC_FLOAT,
|
|
651
|
-
"double" => NC_DOUBLE,
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
def initialize (file, mode = 0, template: nil, compression: 0)
|
|
655
|
-
@dims = []
|
|
656
|
-
@name2dim = {}
|
|
657
|
-
@vars = []
|
|
658
|
-
@name2var = {}
|
|
659
|
-
@compression = compression
|
|
660
|
-
@attributes = nil
|
|
661
|
-
if template.nil?
|
|
662
|
-
if compression > 0
|
|
663
|
-
@file_id = nc_create(file, mode|NC_NETCDF4)
|
|
664
|
-
else
|
|
665
|
-
@file_id = nc_create(file, mode)
|
|
666
|
-
end
|
|
667
|
-
else
|
|
668
|
-
IO.popen("ncgen -o #{file}", "w") { |io|
|
|
669
|
-
io.write(template)
|
|
670
|
-
}
|
|
671
|
-
if compression > 0
|
|
672
|
-
@file_id = nc_open(file, mode|NC_WRITE|NC_SHARE|NC_NETCDF4)
|
|
673
|
-
else
|
|
674
|
-
@file_id = nc_open(file, mode|NC_WRITE|NC_SHARE)
|
|
675
|
-
end
|
|
676
|
-
NCFile.open(file) {|nc|
|
|
677
|
-
define nc.definition, define_mode: false
|
|
678
|
-
}
|
|
679
|
-
end
|
|
680
|
-
end
|
|
681
|
-
|
|
682
|
-
attr_reader :file_id
|
|
683
|
-
|
|
684
|
-
# key of Hash in definition should be String
|
|
685
|
-
def normalize_definition (definition)
|
|
686
|
-
out = {}
|
|
687
|
-
definition.each do |key, value|
|
|
688
|
-
if value.is_a?(Hash)
|
|
689
|
-
value = normalize_definition(value)
|
|
690
|
-
end
|
|
691
|
-
out[key.to_s] = value
|
|
692
|
-
end
|
|
693
|
-
return out
|
|
694
|
-
end
|
|
695
|
-
|
|
696
|
-
def check_definition (definition)
|
|
697
|
-
if definition.has_key?("dimensions")
|
|
698
|
-
dim_keys = definition["dimensions"].keys
|
|
699
|
-
else
|
|
700
|
-
raise "NetCDF definition shoud have 'dimensions' entry"
|
|
701
|
-
end
|
|
702
|
-
if definition.has_key?("variables")
|
|
703
|
-
definition["variables"].each do |name, var_def|
|
|
704
|
-
unless var_def.has_key?("type")
|
|
705
|
-
warn "NCFileWriter: Variable '#{name}' does not have 'type' entry in definition"
|
|
706
|
-
else
|
|
707
|
-
unless ["char","byte","short","int","float","double"].include?(var_def["type"])
|
|
708
|
-
raise "invalid type of NetCDF Variable '#{name}'"
|
|
709
|
-
end
|
|
710
|
-
end
|
|
711
|
-
unless var_def.has_key?("dim")
|
|
712
|
-
warn "NCFileWriter: Variable '#{name}' does not have 'dim' entry in definition"
|
|
713
|
-
else
|
|
714
|
-
unless var_def["dim"].is_a?(Array)
|
|
715
|
-
raise "'dim' of NetCDF Variable '#{name}' should be an Array"
|
|
716
|
-
else
|
|
717
|
-
var_def["dim"].each do |dim|
|
|
718
|
-
unless dim_keys.include?(dim)
|
|
719
|
-
raise "invalid dimension '#{dim}' for NetCDF Variable '#{name}'"
|
|
720
|
-
end
|
|
721
|
-
end
|
|
722
|
-
end
|
|
723
|
-
end
|
|
724
|
-
var_def.each do |k, v|
|
|
725
|
-
case v
|
|
726
|
-
when Hash
|
|
727
|
-
if v.size > 1 or not ["byte","short","int","float","double"].include?(v.keys[0])
|
|
728
|
-
raise "invalid specification in attribute '#{k}' for NetCDF Variable '#{name}'"
|
|
729
|
-
end
|
|
730
|
-
end
|
|
731
|
-
end
|
|
732
|
-
end
|
|
733
|
-
end
|
|
734
|
-
end
|
|
735
|
-
|
|
736
|
-
def self.convert_attribute_value (value)
|
|
737
|
-
case value
|
|
738
|
-
when Hash
|
|
739
|
-
type, value = *value.first
|
|
740
|
-
case type
|
|
741
|
-
when "char"
|
|
742
|
-
value = CA_UINT8(value)
|
|
743
|
-
when "byte"
|
|
744
|
-
value = CA_INT8(value)
|
|
745
|
-
when "short"
|
|
746
|
-
value = CA_SHORT(value)
|
|
747
|
-
when "int"
|
|
748
|
-
value = CA_INT(value)
|
|
749
|
-
when "float"
|
|
750
|
-
value = CA_FLOAT(value)
|
|
751
|
-
when "double"
|
|
752
|
-
value = CA_DOUBLE(value)
|
|
753
|
-
end
|
|
754
|
-
end
|
|
755
|
-
return value
|
|
756
|
-
end
|
|
757
|
-
|
|
758
|
-
def define (definition, define_mode: true)
|
|
759
|
-
definition = normalize_definition(definition)
|
|
760
|
-
check_definition(definition)
|
|
761
|
-
if definition.has_key?("dimensions") and definition["dimensions"].is_a?(Hash)
|
|
762
|
-
definition["dimensions"].each do |name, len|
|
|
763
|
-
dim = Dim.new(self, name, len.to_i, define_mode)
|
|
764
|
-
@dims.push dim
|
|
765
|
-
@name2dim[name] = dim
|
|
766
|
-
end
|
|
767
|
-
else
|
|
768
|
-
warn "NCFileWriter: No dimensions in definition"
|
|
769
|
-
end
|
|
770
|
-
if definition.has_key?("variables") and definition["variables"].is_a?(Hash)
|
|
771
|
-
definition["variables"].each do |name, info|
|
|
772
|
-
var = Var.new(self, name, info, @compression, define_mode)
|
|
773
|
-
@vars.push var
|
|
774
|
-
@name2var[name] = var
|
|
775
|
-
end
|
|
776
|
-
else
|
|
777
|
-
warn "NCFileWriter: No variables in definition"
|
|
778
|
-
end
|
|
779
|
-
if definition.has_key?("attributes") and definition["attributes"].is_a?(Hash)
|
|
780
|
-
@attributes = definition["attributes"]
|
|
781
|
-
if define_mode
|
|
782
|
-
@attributes.each do |name, value|
|
|
783
|
-
value = NCFileWriter.convert_attribute_value(value)
|
|
784
|
-
begin
|
|
785
|
-
nc_put_att(@file_id, NC_GLOBAL, name, value)
|
|
786
|
-
rescue => e
|
|
787
|
-
raise(e.class, e.error_message + " {#{name}: #{value}}")
|
|
788
|
-
end
|
|
789
|
-
end
|
|
790
|
-
end
|
|
791
|
-
@attributes.freeze
|
|
792
|
-
else
|
|
793
|
-
@attributes = {}.freeze
|
|
794
|
-
end
|
|
795
|
-
if define_mode
|
|
796
|
-
nc_enddef(@file_id)
|
|
797
|
-
end
|
|
798
|
-
end
|
|
799
|
-
|
|
800
|
-
def dim (name)
|
|
801
|
-
return @name2dim[name]
|
|
802
|
-
end
|
|
803
|
-
|
|
804
|
-
def [] (name)
|
|
805
|
-
return @name2var[name]
|
|
806
|
-
end
|
|
807
|
-
|
|
808
|
-
def []= (name, value)
|
|
809
|
-
return @name2var[name].put(value)
|
|
810
|
-
end
|
|
811
|
-
|
|
812
|
-
def close
|
|
813
|
-
nc_close(@file_id)
|
|
814
|
-
end
|
|
815
|
-
|
|
816
|
-
end
|
|
817
|
-
|
|
818
|
-
class NCFileWriter
|
|
819
|
-
|
|
820
|
-
def self.scaled_integer (type, value)
|
|
821
|
-
min, max = value.min, value.max
|
|
822
|
-
case type
|
|
823
|
-
when :short
|
|
824
|
-
short_max = (0x7fff-1).to_f
|
|
825
|
-
fillvalue = CA_SHORT(0x7fff+1)[0]
|
|
826
|
-
scale_factor = (max - min)/short_max
|
|
827
|
-
add_offset = min + short_max*scale_factor
|
|
828
|
-
packed = ((value - add_offset)/scale_factor).short
|
|
829
|
-
when :int
|
|
830
|
-
int_max = (0x7fffffff-1).to_f
|
|
831
|
-
fillvalue = CA_INT(0x7fffffff+1)[0]
|
|
832
|
-
scale_factor = (max - min)/int_max
|
|
833
|
-
add_offset = min + int_max*scale_factor
|
|
834
|
-
packed = ((value - add_offset)/scale_factor).int
|
|
835
|
-
else
|
|
836
|
-
raise
|
|
837
|
-
end
|
|
838
|
-
packed.attribute["scale_factor"] = scale_factor
|
|
839
|
-
packed.attribute["add_offset"] = add_offset
|
|
840
|
-
if value.has_mask?
|
|
841
|
-
packed[:is_masked] = fillvalue
|
|
842
|
-
packed.attribute["_FillValue"] = fillvalue
|
|
843
|
-
end
|
|
844
|
-
return packed
|
|
845
|
-
end
|
|
846
|
-
|
|
847
|
-
end
|
|
848
|
-
|