cdo 1.3.6 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/gemspec +1 -1
- data/lib/cdo.rb +176 -73
- data/test/test_cdo.rb +233 -151
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2085dbda68e564e4449b83e69c840dcfc817b60adfe2010c597eeb795eade50d
|
4
|
+
data.tar.gz: 34c2082c1ed7de54cb889dde3cd8fe3d3e8e9ba7abd6f20f655bd6d5478e0905
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38f8689cf9674697d64c9958df35c85177e86c08d6becaa808cc13a56c460fcbce6a6f57c43ca376c129c531c26a1b09dbae38a07c1557e36ec1746f4365ca50
|
7
|
+
data.tar.gz: 71d527c68c39531e5b3c8007a05fa0b46989efa4b49da2a4e3738ced45d60137d32071bfe77a2da11273a6a716f0c9e17484a4676680825f480633289521f921
|
data/gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.unshift File.join(File.dirname(__FILE__),"..","lib")
|
|
3
3
|
|
4
4
|
spec = Gem::Specification.new do |s|
|
5
5
|
s.name = "cdo"
|
6
|
-
s.version = '1.
|
6
|
+
s.version = '1.4.0'
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.files = ["lib/cdo.rb","gemspec","LICENSE"]
|
9
9
|
s.test_file = "test/test_cdo.rb"
|
data/lib/cdo.rb
CHANGED
@@ -2,6 +2,8 @@ require 'pp'
|
|
2
2
|
require 'open3'
|
3
3
|
require 'logger'
|
4
4
|
require 'stringio'
|
5
|
+
require 'json'
|
6
|
+
require 'tempfile'
|
5
7
|
|
6
8
|
class Hash
|
7
9
|
alias :include? :has_key?
|
@@ -12,17 +14,25 @@ class Cdo
|
|
12
14
|
|
13
15
|
# hardcoded fallback list of output operators - from 1.8.0 there is an
|
14
16
|
# options for this: --operators_no_output
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
# this list works for cdo-1.6.4
|
18
|
+
NoOutputOperators = %w[cdiread cmor codetab conv_cmor_table diff diffc diffn
|
19
|
+
diffp diffv dump_cmor_table dumpmap filedes gmtcells gmtxyz gradsdes griddes
|
20
|
+
griddes2 gridverify info infoc infon infop infos infov map ncode ndate
|
21
|
+
ngridpoints ngrids nlevel nmon npar ntime nvar nyear output outputarr
|
22
|
+
outputbounds outputboundscpt outputcenter outputcenter2 outputcentercpt
|
23
|
+
outputext outputf outputfld outputint outputkey outputsrv outputtab outputtri
|
24
|
+
outputts outputvector outputvrml outputxyz pardes partab partab2 seinfo
|
25
|
+
seinfoc seinfon seinfop showattribute showatts showattsglob showattsvar
|
26
|
+
showcode showdate showformat showgrid showlevel showltype showmon showname
|
27
|
+
showparam showstdname showtime showtimestamp showunit showvar showyear sinfo
|
28
|
+
sinfoc sinfon sinfop sinfov spartab specinfo tinfo vardes vct vct2 verifygrid
|
29
|
+
vlist xinfon zaxisdes]
|
30
|
+
TwoOutputOperators = %w[trend samplegridicon mrotuv eoftime
|
31
|
+
eofspatial eof3dtime eof3dspatial eof3d eof complextorect complextopol]
|
32
|
+
MoreOutputOperators = %w[distgrid eofcoeff eofcoeff3d intyear scatter splitcode
|
33
|
+
splitday splitgrid splithour splitlevel splitmon splitname splitparam splitrec
|
34
|
+
splitseas splitsel splittabnum splitvar splityear splityearmon splitzaxis]
|
35
|
+
|
26
36
|
|
27
37
|
attr_accessor :cdo, :returnCdf, :forceOutput, :env, :debug, :logging, :logFile
|
28
38
|
attr_reader :operators, :filetypes
|
@@ -32,6 +42,7 @@ class Cdo
|
|
32
42
|
returnFalseOnError: false,
|
33
43
|
forceOutput: true,
|
34
44
|
env: {},
|
45
|
+
tempdir: Dir.tmpdir,
|
35
46
|
logging: false,
|
36
47
|
logFile: StringIO.new,
|
37
48
|
debug: false,
|
@@ -41,20 +52,30 @@ class Cdo
|
|
41
52
|
@cdo = ENV.has_key?('CDO') ? ENV['CDO'] : cdo
|
42
53
|
|
43
54
|
@operators = getOperators(@cdo)
|
55
|
+
@noOutputOperators = @operators.select {|op,io| 0 == io}.keys
|
56
|
+
|
44
57
|
@returnCdf = returnCdf
|
45
58
|
@forceOutput = forceOutput
|
46
59
|
@env = env
|
47
60
|
@debug = ENV.has_key?('DEBUG') ? true : debug
|
48
|
-
@noOutputOperators = getNoOuputOperators(@cdo) # operators without output files (write to stdout)
|
49
61
|
@returnNilOnError = returnNilOnError
|
50
62
|
|
51
|
-
@filetypes = getFiletypes
|
52
63
|
@returnFalseOnError = returnFalseOnError
|
53
64
|
|
65
|
+
@tempStore = CdoTempfileStore.new(tempdir)
|
54
66
|
@logging = logging
|
55
67
|
@logFile = logFile
|
56
68
|
@logger = Logger.new(@logFile,'a')
|
57
69
|
@logger.level = Logger::INFO
|
70
|
+
|
71
|
+
@config = getFeatures
|
72
|
+
|
73
|
+
# create methods to descibe what can be done with the binary
|
74
|
+
@config.each {|k,v|
|
75
|
+
self.class.send :define_method, k.tr('-','_') do
|
76
|
+
v
|
77
|
+
end
|
78
|
+
}
|
58
79
|
end
|
59
80
|
|
60
81
|
private # {{{
|
@@ -73,9 +94,13 @@ class Cdo
|
|
73
94
|
end
|
74
95
|
|
75
96
|
# collect the complete list of possible operators
|
76
|
-
def getOperators(path2cdo)
|
97
|
+
def getOperators(path2cdo) #{{{
|
98
|
+
operators = {}
|
99
|
+
|
100
|
+
# little side note: the option --operators_no_output works in 1.8.0 and
|
101
|
+
# 1.8.2, but not in 1.9.0, from 1.9.1 it works again
|
77
102
|
case
|
78
|
-
when version
|
103
|
+
when version < '1.7.2' then
|
79
104
|
cmd = path2cdo + ' 2>&1'
|
80
105
|
help = IO.popen(cmd).readlines.map {|l| l.chomp.lstrip}
|
81
106
|
if 5 >= help.size
|
@@ -84,38 +109,77 @@ class Cdo
|
|
84
109
|
exit
|
85
110
|
end
|
86
111
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
112
|
+
_operators = help[(help.index("Operators:")+1)..help.index(help.find {|v|
|
113
|
+
v =~ /CDO version/
|
114
|
+
}) - 2].join(' ').split
|
115
|
+
|
116
|
+
# build up operator inventory
|
117
|
+
# default is 1 output stream
|
118
|
+
_operators.each {|op| operators[op] = 1 }
|
119
|
+
operators.each {|op,_|
|
120
|
+
operators[op] = 0 if NoOutputOperators.include?(op)
|
121
|
+
operators[op] = 2 if TwoOutputOperators.include?(op)
|
122
|
+
operators[op] = -1 if MoreOutputOperators.include?(op)
|
123
|
+
}
|
124
|
+
|
125
|
+
when (version < '1.8.0' or '1.9.0' == version) then
|
126
|
+
cmd = "#{path2cdo} --operators"
|
127
|
+
_operators = IO.popen(cmd).readlines.map {|l| l.split(' ').first }
|
128
|
+
|
129
|
+
_operators.each {|op| operators[op] = 1 }
|
130
|
+
operators.each {|op,_|
|
131
|
+
operators[op] = 0 if NoOutputOperators.include?(op)
|
132
|
+
operators[op] = 2 if TwoOutputOperators.include?(op)
|
133
|
+
operators[op] = -1 if MoreOutputOperators.include?(op)
|
134
|
+
}
|
135
|
+
|
136
|
+
|
137
|
+
when version < '1.9.3' then
|
138
|
+
|
139
|
+
cmd = "#{path2cdo} --operators"
|
140
|
+
_operators = IO.popen(cmd).readlines.map {|l| l.split(' ').first }
|
141
|
+
cmd = "#{path2cdo} --operators_no_output"
|
142
|
+
_operatorsNoOutput = IO.popen(cmd).readlines.map {|l| l.split(' ').first }
|
143
|
+
|
144
|
+
# build up operator inventory
|
145
|
+
_operators.each {|op| operators[op] = 1 }
|
146
|
+
_operatorsNoOutput.each {|op| operators[op] = 0}
|
147
|
+
operators.each {|op,_|
|
148
|
+
operators[op] = 0 if _operatorsNoOutput.include?(op)
|
149
|
+
operators[op] = 2 if TwoOutputOperators.include?(op)
|
150
|
+
operators[op] = -1 if MoreOutputOperators.include?(op)
|
151
|
+
}
|
152
|
+
|
91
153
|
else
|
92
154
|
cmd = "#{path2cdo} --operators"
|
93
155
|
operators = {}
|
94
156
|
IO.popen(cmd).readlines.map {|line|
|
95
|
-
lineContent
|
96
|
-
name
|
97
|
-
iCounter, oCounter = lineContent[-1].
|
98
|
-
operators[name]
|
157
|
+
lineContent = line.chomp.split(' ')
|
158
|
+
name = lineContent[0]
|
159
|
+
iCounter, oCounter = lineContent[-1][1..-1].split('|')
|
160
|
+
operators[name] = oCounter.to_i
|
99
161
|
}
|
100
162
|
end
|
101
163
|
return operators
|
102
|
-
end
|
164
|
+
end #}}}
|
103
165
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
else
|
109
|
-
NoOutputOperators
|
110
|
-
end
|
111
|
-
end
|
166
|
+
# get meta-data about the CDO installation
|
167
|
+
def getFeatures
|
168
|
+
config = {}
|
169
|
+
config.default(false)
|
112
170
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
171
|
+
if version > '1.9.3' then
|
172
|
+
config.merge!(JSON.parse(IO.popen(@cdo + " --config all").read.chomp))
|
173
|
+
config.each {|k,v| config[k] = ('yes' == v) ? true : false}
|
174
|
+
else
|
175
|
+
_, _, stderr, _ = Open3.popen3(@cdo + " -V")
|
176
|
+
supported = stderr.readlines.map(&:chomp)
|
117
177
|
|
118
|
-
|
178
|
+
supported.grep(/(Filetypes)/)[0].split(':')[1].split.map(&:downcase).each {|ftype|
|
179
|
+
config["has-#{ftype}"] = true
|
180
|
+
}
|
181
|
+
end
|
182
|
+
config
|
119
183
|
end
|
120
184
|
|
121
185
|
# Execute the given cdo call and return all outputs
|
@@ -174,18 +238,25 @@ class Cdo
|
|
174
238
|
autoSplit: nil)
|
175
239
|
options = options.to_s
|
176
240
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
241
|
+
# switch netcdf output if data of filehandles are requested as output
|
242
|
+
options << ' -f nc' if ( \
|
243
|
+
( returnCdf ) or \
|
244
|
+
( not returnArray.nil? ) or \
|
245
|
+
( not returnMaArray.nil?) \
|
246
|
+
)
|
247
|
+
|
183
248
|
# setup basic operator execution command
|
184
249
|
cmd = "#{@cdo} -O #{options} -#{operatorName}#{operatorParameters} #{input} "
|
185
250
|
|
186
251
|
# use an empty hash for non-given environment
|
187
252
|
env = {} if env.nil?
|
188
253
|
|
254
|
+
# list of all output streams
|
255
|
+
outputs = []
|
256
|
+
|
257
|
+
# just collect given output(s)
|
258
|
+
outputs << output unless output.nil?
|
259
|
+
|
189
260
|
case output
|
190
261
|
when $stdout
|
191
262
|
retvals = _call(cmd,env)
|
@@ -204,13 +275,18 @@ class Cdo
|
|
204
275
|
end
|
205
276
|
end
|
206
277
|
else
|
278
|
+
# if operators was not called with output-forcing given, take the global switch
|
207
279
|
force = @forceOutput if force.nil?
|
280
|
+
|
208
281
|
if force or not File.exists?(output.to_s)
|
209
|
-
# create
|
210
|
-
output
|
282
|
+
# create tempfile(s) according to the number of output streams needed
|
283
|
+
# if output argument is missing
|
284
|
+
if output.nil? then
|
285
|
+
operators[operatorName].times { outputs << @tempStore.newFile}
|
286
|
+
end
|
211
287
|
|
212
288
|
#finalize the execution command
|
213
|
-
cmd << "#{
|
289
|
+
cmd << "#{outputs.join(' ')}"
|
214
290
|
|
215
291
|
retvals = _call(cmd,env)
|
216
292
|
|
@@ -222,20 +298,25 @@ class Cdo
|
|
222
298
|
end
|
223
299
|
end
|
224
300
|
else
|
225
|
-
warn "Use existing file '#{
|
301
|
+
warn "Use existing file(s) '#{outputs.join(' ')}'" if @debug
|
226
302
|
end
|
227
303
|
end
|
228
304
|
|
305
|
+
# return data arrays instead - this is for now limitted to fields of the
|
306
|
+
# first output file. number from the second need only one addition line, so
|
307
|
+
# I think this is sufficient
|
229
308
|
if not returnArray.nil?
|
230
|
-
readArray(
|
309
|
+
readArray(outputs[0],returnArray)
|
231
310
|
elsif not returnMaArray.nil?
|
232
|
-
readMaArray(
|
311
|
+
readMaArray(outputs[0],returnMaArray)
|
233
312
|
elsif returnCdf or @returnCdf
|
234
|
-
readCdf(
|
313
|
+
retval = outputs.map{|f| readCdf(f)}
|
314
|
+
return 1 == retval.size ? retval[0] : retval
|
235
315
|
elsif /^split/.match(operatorName)
|
236
316
|
Dir.glob("#{output}*")
|
237
317
|
else
|
238
|
-
|
318
|
+
return outputs[0] if outputs.size == 1
|
319
|
+
return outputs
|
239
320
|
end
|
240
321
|
end
|
241
322
|
|
@@ -295,17 +376,20 @@ class Cdo
|
|
295
376
|
return File.open(@logFile).readlines
|
296
377
|
end
|
297
378
|
end
|
379
|
+
|
298
380
|
# print the loggin messaged
|
299
381
|
def showLog
|
300
382
|
puts collectLogs
|
301
383
|
end
|
302
384
|
|
385
|
+
# check if the CDO binary is present and works
|
303
386
|
def hasCdo(path=@cdo)
|
304
387
|
executable = system("#{path} -V >/dev/null 2>&1")
|
305
388
|
fullpath = File.exists?(path) and File.executable?(path)
|
306
389
|
|
307
390
|
return (executable or fullpath)
|
308
391
|
end
|
392
|
+
|
309
393
|
# check if cdo backend is working
|
310
394
|
def check
|
311
395
|
return false unless hasCdo
|
@@ -316,6 +400,7 @@ class Cdo
|
|
316
400
|
return true
|
317
401
|
end
|
318
402
|
|
403
|
+
# return CDO version string
|
319
404
|
def version(verbose=false)
|
320
405
|
info = IO.popen("#{@cdo} -V 2>&1").readlines
|
321
406
|
if verbose then
|
@@ -359,8 +444,9 @@ class Cdo
|
|
359
444
|
end
|
360
445
|
end
|
361
446
|
|
362
|
-
|
363
|
-
|
447
|
+
# remove tempfiles created from this or previous runs
|
448
|
+
def cleanTempDir
|
449
|
+
@tempStore.cleanTempDir
|
364
450
|
end
|
365
451
|
# }}}
|
366
452
|
|
@@ -392,33 +478,50 @@ class Cdo
|
|
392
478
|
|
393
479
|
end
|
394
480
|
#
|
395
|
-
# Helper module for easy temp file handling
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
481
|
+
# Helper module for easy temp file handling {{{
|
482
|
+
class CdoTempfileStore
|
483
|
+
# base for persitent temp files - just for debugging
|
484
|
+
N = 10000000
|
485
|
+
|
486
|
+
def initialize(dir=Dir.tmpdir)
|
487
|
+
@dir = dir
|
488
|
+
@tag = 'Cdorb'
|
489
|
+
@persistent_tempfiles = false
|
490
|
+
|
491
|
+
# storage for filenames in order to prevent too early removement
|
492
|
+
@_tempfiles = []
|
493
|
+
|
494
|
+
# make sure the tempdir ie really there
|
495
|
+
Dir.mkdir(@dir) unless Dir.exists?(@dir)
|
404
496
|
end
|
405
497
|
|
406
|
-
def
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
498
|
+
def setPersist(value)
|
499
|
+
@persistent_tempfiles = value
|
500
|
+
end
|
501
|
+
|
502
|
+
def newFile
|
503
|
+
unless @persistent_tempfiles
|
504
|
+
t = Tempfile.new(@tag,@dir)
|
505
|
+
@_tempfiles << t
|
506
|
+
@_tempfiles << t.path
|
411
507
|
t.path
|
412
508
|
else
|
413
|
-
t = "_"+rand(
|
414
|
-
|
509
|
+
t = "_"+rand(N).to_s
|
510
|
+
@_tempfiles << t
|
415
511
|
t
|
416
512
|
end
|
417
513
|
end
|
418
514
|
|
419
|
-
def
|
420
|
-
|
515
|
+
def showFiles
|
516
|
+
@_tempfiles.each {|f| print(f+" ") if f.kind_of? String}
|
421
517
|
end
|
422
|
-
end
|
423
518
|
|
424
|
-
|
519
|
+
def cleanTempDir
|
520
|
+
# filter by name, realfile and ownership
|
521
|
+
Dir.entries(@dir).map {|f| "#@dir/#{f}"}.find_all {|file|
|
522
|
+
File.file?(file) and File.owned?(file) and file.include?(@tag)
|
523
|
+
}.each {|f| File.unlink(f)}
|
524
|
+
end
|
525
|
+
end #}}}
|
526
|
+
|
527
|
+
# vim: fdm=marker
|
data/test/test_cdo.rb
CHANGED
@@ -18,6 +18,7 @@ class TestCdo < Minitest::Test
|
|
18
18
|
|
19
19
|
def setup
|
20
20
|
@cdo = Cdo.new
|
21
|
+
@tempStore = CdoTempfileStore.new
|
21
22
|
end
|
22
23
|
|
23
24
|
def test_cdo
|
@@ -82,7 +83,7 @@ class TestCdo < Minitest::Test
|
|
82
83
|
end
|
83
84
|
def test_CDO_version
|
84
85
|
assert("1.4.3.1" < @cdo.version,"Version too low: #{@cdo.version}")
|
85
|
-
assert("1.
|
86
|
+
assert("1.6.3" < @cdo.version,"Version too low: #{@cdo.version}")
|
86
87
|
assert("3.0" > @cdo.version,"Version too high: #{@cdo.version}")
|
87
88
|
end
|
88
89
|
def test_args
|
@@ -264,23 +265,25 @@ class TestCdo < Minitest::Test
|
|
264
265
|
assert_nil(@cdo.diffv(:input => ["-stdatm,0",fileB]).last)
|
265
266
|
end
|
266
267
|
|
267
|
-
def
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
assert_raises ArgumentError do
|
272
|
-
@cdo.filetypes("foo")
|
273
|
-
end
|
268
|
+
def test_features
|
269
|
+
assert_equal(true,@cdo.has_srv)
|
270
|
+
assert_equal(true,@cdo.has_ieg)
|
271
|
+
assert_equal(true,@cdo.has_ext)
|
274
272
|
end
|
275
273
|
|
276
274
|
def test_noOutputOps
|
277
|
-
|
278
|
-
|
275
|
+
operators = @cdo.operators
|
276
|
+
%w[griddes griddes2 gridverify info infoc infon infop infos infov map
|
279
277
|
outputarr outputbounds outputboundscpt outputcenter outputcenter2
|
280
278
|
outputcentercpt outputext outputf outputfld outputint outputkey outputsrv
|
281
279
|
outputtab outputtri outputts outputvector outputvrml outputxyz pardes partab].each {|op|
|
282
|
-
assert(
|
280
|
+
assert(operators.include?(op),"Operator '#{op}' cannot be found!")
|
281
|
+
assert_equal(0,operators[op],"Operator '#{op}' has a non-zero output counter!")
|
283
282
|
}
|
283
|
+
# just a rought estimation
|
284
|
+
opsCounf = @cdo.operators.select {|_,c| 0 == c}.size
|
285
|
+
assert(opsCounf > 50)
|
286
|
+
assert(opsCounf < 200)
|
284
287
|
end
|
285
288
|
|
286
289
|
def test_output_set_to_nil
|
@@ -311,59 +314,226 @@ class TestCdo < Minitest::Test
|
|
311
314
|
}
|
312
315
|
end
|
313
316
|
|
317
|
+
def test_operators_with_multiple_output_files
|
318
|
+
assert_equal(1,@cdo.operators['topo'],'wrong output counter for "topo"')
|
319
|
+
assert_equal(0,@cdo.operators['sinfo'],'wrong output counter for "sinfo"')
|
320
|
+
assert_equal(0,@cdo.operators['ngridpoints'],'wrong output counter for "sinfo"') if @cdo.version > '1.6.4'
|
321
|
+
|
322
|
+
assert_equal(-1,@cdo.operators['splitsel'],'wrong output counter for "splitsel"')
|
323
|
+
assert_equal(2,@cdo.operators['trend'],'wrong output counter for "trend"')
|
324
|
+
# create input for eof
|
325
|
+
#
|
326
|
+
# check automatic generation ot two tempfiles
|
327
|
+
aFile, bFile = @cdo.trend(input: "-addc,7 -mulc,44 -for,1,100")
|
328
|
+
assert_equal(51.0,@cdo.outputkey('value',input: aFile)[-1].to_f)
|
329
|
+
assert_equal(44.0,@cdo.outputkey('value',input: bFile)[-1].to_f)
|
330
|
+
# check usage of 'returnCdf' with these operators
|
331
|
+
aFile, bFile = @cdo.trend(input: "-addc,7 -mulc,44 -for,1,100",returnCdf: true)
|
332
|
+
assert_equal(51.0, aFile.var('for').get.flatten[0],"got wrong value from cdf handle")
|
333
|
+
assert_equal(44.0, bFile.var('for').get.flatten[0],"got wrong value from cdf handle")
|
334
|
+
|
335
|
+
avar = @cdo.trend(input: "-addc,7 -mulc,44 -for,1,100",returnArray: 'for').flatten[0]
|
336
|
+
assert_equal(51.0, avar,"got wrong value from narray")
|
337
|
+
end
|
338
|
+
def test_tempdir
|
339
|
+
# manual set path
|
340
|
+
tag = 'tempRb'
|
341
|
+
tempPath = Dir.pwd+'/'+tag
|
342
|
+
pp Dir.glob("#{tempPath}/*").size
|
343
|
+
assert_equal(0,Dir.glob("#{tempPath}/*").size)
|
344
|
+
cdo = Cdo.new(tempdir: tempPath)
|
345
|
+
cdo.topo('r10x10',options: '-f nc')
|
346
|
+
assert_equal(1,Dir.glob("#{tempPath}/*").size)
|
347
|
+
cdo.topo('r10x10',options: '-f nc')
|
348
|
+
cdo.topo('r10x10',options: '-f nc')
|
349
|
+
assert_equal(3,Dir.glob("#{tempPath}/*").size)
|
350
|
+
cdo.topo('r10x10',options: '-f nc')
|
351
|
+
cdo.topo('r10x10',options: '-f nc')
|
352
|
+
assert_equal(5,Dir.glob("#{tempPath}/*").size)
|
353
|
+
cdo.cleanTempDir
|
354
|
+
assert_equal(0,Dir.glob("#{tempPath}/*").size)
|
355
|
+
end
|
356
|
+
|
357
|
+
def test_returnArray
|
358
|
+
temperature = @cdo.stdatm(0,:returnCdf => true).var('T').get.flatten[0]
|
359
|
+
assert(1.7 < temperature,"Temperature to low!")
|
360
|
+
assert_raises ArgumentError do
|
361
|
+
@cdo.stdatm(0,:returnArray => 'TT')
|
362
|
+
end
|
363
|
+
temperature = @cdo.stdatm(0,:returnArray => 'T')
|
364
|
+
assert_equal(288.0,temperature.flatten[0])
|
365
|
+
pressure = @cdo.stdatm(0,1000,:options => '-b F64',:returnArray => 'P')
|
366
|
+
assert_equal("1013.25 898.543456035875",pressure.flatten.to_a.join(' '))
|
367
|
+
end
|
368
|
+
def test_returnMaArray
|
369
|
+
@cdo.debug = @@debug
|
370
|
+
topo = @cdo.topo(:returnMaArray => 'topo')
|
371
|
+
assert_equal(-1890.0,topo.mean.round)
|
372
|
+
bathy = @cdo.setrtomiss(0,10000,
|
373
|
+
:input => @cdo.topo(:options => '-f nc'),:returnMaArray => 'topo')
|
374
|
+
assert_equal(-3386.0,bathy.mean.round)
|
375
|
+
oro = @cdo.setrtomiss(-10000,0,
|
376
|
+
:input => @cdo.topo(:options => '-f nc'),:returnMaArray => 'topo')
|
377
|
+
assert_equal(1142.0,oro.mean.round)
|
378
|
+
bathy = @cdo.remapnn('r2x2',:input => @cdo.topo(:options => '-f nc'), :returnMaArray => 'topo')
|
379
|
+
assert_equal(-4298.0,bathy[0,0])
|
380
|
+
assert_equal(-2669.0,bathy[1,0])
|
381
|
+
ta = @cdo.remapnn('r2x2',:input => @cdo.topo(:options => '-f nc'))
|
382
|
+
tb = @cdo.subc(-2669.0,:input => ta)
|
383
|
+
withMask = @cdo.div(:input => ta+" "+tb,:returnMaArray => 'topo')
|
384
|
+
assert(-8.0e+33 > withMask[1,0])
|
385
|
+
assert(0 < withMask[0,0])
|
386
|
+
assert(0 < withMask[0,1])
|
387
|
+
assert(0 < withMask[1,1])
|
388
|
+
end
|
389
|
+
def test_combine
|
390
|
+
ofile0, ofile1 = @tempStore.newFile, @tempStore.newFile
|
391
|
+
@cdo.fldsum(:input => @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:options => "-f nc"),:output => ofile0)
|
392
|
+
@cdo.fldsum(:input => "-stdatm,25,100,250,500,875,1400,2100,3000,4000,5000",:options => "-f nc",:output => ofile1)
|
393
|
+
@cdo.returnCdf = true
|
394
|
+
@tempStore.showFiles
|
395
|
+
diff = @cdo.sub(:input => [ofile0,ofile1].join(' ')).var('T').get
|
396
|
+
assert_equal(0.0,diff.min)
|
397
|
+
assert_equal(0.0,diff.max)
|
398
|
+
@cdo.returnCdf = false
|
399
|
+
end
|
400
|
+
|
401
|
+
def test_returnCdf
|
402
|
+
ofile = rand(0xfffff).to_s + '_test_returnCdf.nc'
|
403
|
+
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
404
|
+
assert_equal(ofile,vals)
|
405
|
+
@cdo.returnCdf = true
|
406
|
+
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
407
|
+
assert_equal(["lon","lat","level","P","T"],vals.var_names)
|
408
|
+
assert_equal(276,vals.var("T").get.flatten.mean.floor)
|
409
|
+
@cdo.returnCdf = false
|
410
|
+
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
411
|
+
assert_equal(ofile,vals)
|
412
|
+
FileUtils.rm(ofile)
|
413
|
+
end
|
414
|
+
def test_simple_returnCdf
|
415
|
+
ofile0, ofile1 = @tempStore.newFile, @tempStore.newFile
|
416
|
+
sum = @cdo.fldsum(:input => @cdo.stdatm(0,:options => "-f nc"),
|
417
|
+
:returnCdf => true).var("P").get
|
418
|
+
assert_equal(1013.25,sum.min)
|
419
|
+
sum = @cdo.fldsum(:input => @cdo.stdatm(0,:options => "-f nc"),:output => ofile0)
|
420
|
+
assert_equal(ofile0,sum)
|
421
|
+
test_returnCdf
|
422
|
+
end
|
423
|
+
def test_readCdf
|
424
|
+
input = "-settunits,days -setyear,2000 -for,1,4"
|
425
|
+
cdfFile = @cdo.copy(:options =>"-f nc",:input=>input)
|
426
|
+
cdf = @cdo.readCdf(cdfFile)
|
427
|
+
assert_empty(['lon','lat','for','time'] - cdf.var_names)
|
428
|
+
end
|
429
|
+
def test_combine
|
430
|
+
ofile0, ofile1 = @tempStore.newFile, @tempStore.newFile
|
431
|
+
@cdo.fldsum(:input => @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:options => "-f nc"),:output => ofile0)
|
432
|
+
@cdo.fldsum(:input => "-stdatm,25,100,250,500,875,1400,2100,3000,4000,5000",:options => "-f nc",:output => ofile1)
|
433
|
+
@cdo.returnCdf = true
|
434
|
+
@tempStore.showFiles
|
435
|
+
diff = @cdo.sub(:input => [ofile0,ofile1].join(' ')).var('T').get
|
436
|
+
assert_equal(0.0,diff.min)
|
437
|
+
assert_equal(0.0,diff.max)
|
438
|
+
@cdo.returnCdf = false
|
439
|
+
end
|
440
|
+
def test_readArray
|
441
|
+
@cdo.debug = @@debug
|
442
|
+
assert_equal([40,80],@cdo.readArray(@cdo.sellonlatbox(-10,10,-20,20,:input => '-topo',:options => '-f nc'), 'topo').shape)
|
443
|
+
end
|
444
|
+
def test_env
|
445
|
+
oTag = 'test_env_with_splitlevel_'
|
446
|
+
levels = [0,10,100]
|
447
|
+
expected = levels.map {|l| "test_env_with_splitlevel_000#{l.to_s.rjust(3,'0')}"}
|
448
|
+
# clean up first
|
449
|
+
rm(Dir.glob(oTag+'*'))
|
450
|
+
|
451
|
+
# oType = grb (default)
|
452
|
+
ofiles = expected.map {|f| f += '.grb'}
|
453
|
+
@cdo.splitlevel(input: "-stdatm,0,10,100",output: oTag)
|
454
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
455
|
+
rm(ofiles)
|
456
|
+
|
457
|
+
# oType = nc, from cdo options
|
458
|
+
ofiles = expected.map {|f| f += '.nc'}
|
459
|
+
@cdo.splitlevel(input: "-stdatm,0,10,100",output: oTag,options: '-f nc')
|
460
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
461
|
+
rm(ofiles)
|
462
|
+
|
463
|
+
# oType = nc, from input type
|
464
|
+
ofiles = expected.map {|f| f += '.nc'}
|
465
|
+
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag)
|
466
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
467
|
+
rm(ofiles)
|
468
|
+
|
469
|
+
# oType = nc, from input ENV
|
470
|
+
ofiles = expected.map {|f| f += '.nc2'}
|
471
|
+
@cdo.env = {'CDO_FILE_SUFFIX' => '.nc2'}
|
472
|
+
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag)
|
473
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
474
|
+
rm(ofiles)
|
475
|
+
|
476
|
+
# oType = nc, from input ENV setting for each call
|
477
|
+
ofiles = expected.map {|f| f += '.nc4'}
|
478
|
+
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag,env: {'CDO_FILE_SUFFIX' => '.nc4'})
|
479
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
480
|
+
rm(ofiles)
|
481
|
+
end
|
482
|
+
def test_log
|
483
|
+
cmd = '-fldmean -mul -random,r20x20 -topo,r20x20'
|
484
|
+
# logging without a real file
|
485
|
+
@cdo = Cdo.new( returnNilOnError: true)
|
486
|
+
@cdo.debug = false
|
487
|
+
@cdo.logging = true
|
488
|
+
@cdo.topo
|
489
|
+
@cdo.temp
|
490
|
+
@cdo.sinfov(input: cmd)
|
491
|
+
puts @cdo.showLog
|
492
|
+
@cdo.sinfov(input: '-top')
|
493
|
+
@cdo.topo
|
494
|
+
puts @cdo.showLog
|
495
|
+
# use a use definded file for looging
|
496
|
+
@cdo = Cdo.new(logFile: 'test.log',logging: true, returnNilOnError: true)
|
497
|
+
@cdo.topo
|
498
|
+
@cdo.temp
|
499
|
+
@cdo.sinfov(input: cmd)
|
500
|
+
puts @cdo.showLog
|
501
|
+
end
|
314
502
|
if @@maintainermode then
|
315
503
|
require 'unifiedPlot'
|
316
504
|
|
505
|
+
def test_system_tempdir
|
506
|
+
# automatic path
|
507
|
+
tempPath = Dir.tmpdir
|
508
|
+
tag = 'Cdorb'
|
509
|
+
pattern = "#{tempPath}/#{tag}*"
|
510
|
+
cdo = Cdo.new
|
511
|
+
assert_equal(0,Dir.glob(pattern).size)
|
512
|
+
cdo.topo('r10x10')
|
513
|
+
assert_equal(1,Dir.glob(pattern).size)
|
514
|
+
cdo.topo('r10x10')
|
515
|
+
cdo.topo('r10x10',options: '-f nc')
|
516
|
+
assert_equal(3,Dir.glob(pattern).size)
|
517
|
+
cdo.topo('r10x10',options: '-f nc')
|
518
|
+
cdo.topo('r10x10',options: '-f nc')
|
519
|
+
cdo.topo('r10x10',options: '-f nc')
|
520
|
+
cdo.topo('r10x10')
|
521
|
+
cdo.topo('r10x10',options: '-f nc')
|
522
|
+
cdo.topo('r10x10',options: '-f nc')
|
523
|
+
cdo.topo('r10x10',options: '-f nc')
|
524
|
+
cdo.topo('r10x10')
|
525
|
+
cdo.topo('r10x10',options: '-f nc')
|
526
|
+
assert_equal(12,Dir.glob(pattern).size)
|
527
|
+
cdo.cleanTempDir()
|
528
|
+
assert_equal(0,Dir.glob(pattern).size)
|
529
|
+
end
|
317
530
|
def test_longChain
|
318
531
|
ifile = "-enlarge,global_0.3 -settaxis,2000-01-01 -expr,'t=sin(for*3.141529/180.0)' -for,1,10"
|
319
532
|
t = @cdo.fldmax(input: "-div -sub -timmean -seltimestep,2,3 #{ifile} -seltimestep,1 #{ifile} -gridarea #{ifile}",returnArray: "t")
|
320
533
|
assert_equal(8.981299259858133e-09,t[0])
|
321
534
|
end
|
322
|
-
def test_returnArray
|
323
|
-
temperature = @cdo.stdatm(0,:options => '-f nc',:returnCdf => true).var('T').get.flatten[0]
|
324
|
-
assert_raises ArgumentError do
|
325
|
-
@cdo.stdatm(0,:options => '-f nc',:returnArray => 'TT')
|
326
|
-
end
|
327
|
-
temperature = @cdo.stdatm(0,:options => '-f nc',:returnArray => 'T')
|
328
|
-
assert_equal(288.0,temperature.flatten[0])
|
329
|
-
pressure = @cdo.stdatm(0,1000,:options => '-f nc -b F64',:returnArray => 'P')
|
330
|
-
assert_equal("1013.25 898.543456035875",pressure.flatten.to_a.join(' '))
|
331
|
-
end
|
332
|
-
def test_returnMaArray
|
333
|
-
@cdo.debug = @@debug
|
334
|
-
topo = @cdo.topo(:options => '-f nc',:returnMaArray => 'topo')
|
335
|
-
assert_equal(-1890.0,topo.mean.round)
|
336
|
-
bathy = @cdo.setrtomiss(0,10000,
|
337
|
-
:input => @cdo.topo(:options => '-f nc'),:returnMaArray => 'topo')
|
338
|
-
assert_equal(-3386.0,bathy.mean.round)
|
339
|
-
oro = @cdo.setrtomiss(-10000,0,
|
340
|
-
:input => @cdo.topo(:options => '-f nc'),:returnMaArray => 'topo')
|
341
|
-
assert_equal(1142.0,oro.mean.round)
|
342
|
-
bathy = @cdo.remapnn('r2x2',:input => @cdo.topo(:options => '-f nc'), :returnMaArray => 'topo')
|
343
|
-
assert_equal(-4298.0,bathy[0,0])
|
344
|
-
assert_equal(-2669.0,bathy[1,0])
|
345
|
-
ta = @cdo.remapnn('r2x2',:input => @cdo.topo(:options => '-f nc'))
|
346
|
-
tb = @cdo.subc(-2669.0,:input => ta)
|
347
|
-
withMask = @cdo.div(:input => ta+" "+tb,:returnMaArray => 'topo')
|
348
|
-
assert(-8.0e+33 > withMask[1,0])
|
349
|
-
assert(0 < withMask[0,0])
|
350
|
-
assert(0 < withMask[0,1])
|
351
|
-
assert(0 < withMask[1,1])
|
352
|
-
end
|
353
|
-
def test_combine
|
354
|
-
ofile0, ofile1 = MyTempfile.path, MyTempfile.path
|
355
|
-
@cdo.fldsum(:input => @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:options => "-f nc"),:output => ofile0)
|
356
|
-
@cdo.fldsum(:input => "-stdatm,25,100,250,500,875,1400,2100,3000,4000,5000",:options => "-f nc",:output => ofile1)
|
357
|
-
@cdo.returnCdf = true
|
358
|
-
MyTempfile.showFiles
|
359
|
-
diff = @cdo.sub(:input => [ofile0,ofile1].join(' ')).var('T').get
|
360
|
-
assert_equal(0.0,diff.min)
|
361
|
-
assert_equal(0.0,diff.max)
|
362
|
-
@cdo.returnCdf = false
|
363
|
-
end
|
364
|
-
|
365
535
|
def test_tempfile
|
366
|
-
ofile0, ofile1 =
|
536
|
+
ofile0, ofile1 = @tempStore.newFile, @tempStore.newFile
|
367
537
|
assert(ofile0 != ofile1, "Found equal tempfiles!")
|
368
538
|
# Tempfile should not disappeare even if the GC was started
|
369
539
|
puts ofile0
|
@@ -371,35 +541,6 @@ class TestCdo < Minitest::Test
|
|
371
541
|
GC.start
|
372
542
|
assert(File.exist?(ofile0))
|
373
543
|
end
|
374
|
-
|
375
|
-
def test_returnCdf
|
376
|
-
ofile = rand(0xfffff).to_s + '_test_returnCdf.nc'
|
377
|
-
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
378
|
-
assert_equal(ofile,vals)
|
379
|
-
@cdo.returnCdf = true
|
380
|
-
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
381
|
-
assert_equal(["lon","lat","level","P","T"],vals.var_names)
|
382
|
-
assert_equal(276,vals.var("T").get.flatten.mean.floor)
|
383
|
-
@cdo.returnCdf = false
|
384
|
-
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
385
|
-
assert_equal(ofile,vals)
|
386
|
-
FileUtils.rm(ofile)
|
387
|
-
end
|
388
|
-
def test_simple_returnCdf
|
389
|
-
ofile0, ofile1 = MyTempfile.path, MyTempfile.path
|
390
|
-
sum = @cdo.fldsum(:input => @cdo.stdatm(0,:options => "-f nc"),
|
391
|
-
:returnCdf => true).var("P").get
|
392
|
-
assert_equal(1013.25,sum.min)
|
393
|
-
sum = @cdo.fldsum(:input => @cdo.stdatm(0,:options => "-f nc"),:output => ofile0)
|
394
|
-
assert_equal(ofile0,sum)
|
395
|
-
test_returnCdf
|
396
|
-
end
|
397
|
-
def test_readCdf
|
398
|
-
input = "-settunits,days -setyear,2000 -for,1,4"
|
399
|
-
cdfFile = @cdo.copy(:options =>"-f nc",:input=>input)
|
400
|
-
cdf = @cdo.readCdf(cdfFile)
|
401
|
-
assert_empty(['lon','lat','for','time'] - cdf.var_names)
|
402
|
-
end
|
403
544
|
def test_selIndexListFromIcon
|
404
545
|
input = "~/data/icon/oce.nc"
|
405
546
|
end
|
@@ -456,71 +597,12 @@ class TestCdo < Minitest::Test
|
|
456
597
|
{:y => vFm1s,:style => 'points',:title => 'fillmiss2'}],
|
457
598
|
plotConf: {:yrange => '[0:1]'},title: 'r10x1') if @@show
|
458
599
|
end
|
459
|
-
end
|
460
|
-
|
461
|
-
def test_env
|
462
|
-
oTag = 'test_env_with_splitlevel_'
|
463
|
-
levels = [0,10,100]
|
464
|
-
expected = levels.map {|l| "test_env_with_splitlevel_000#{l.to_s.rjust(3,'0')}"}
|
465
|
-
# clean up first
|
466
|
-
rm(Dir.glob(oTag+'*'))
|
467
|
-
|
468
|
-
# oType = grb (default)
|
469
|
-
ofiles = expected.map {|f| f += '.grb'}
|
470
|
-
@cdo.splitlevel(input: "-stdatm,0,10,100",output: oTag)
|
471
|
-
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
472
|
-
rm(ofiles)
|
473
|
-
|
474
|
-
# oType = nc, from cdo options
|
475
|
-
ofiles = expected.map {|f| f += '.nc'}
|
476
|
-
@cdo.splitlevel(input: "-stdatm,0,10,100",output: oTag,options: '-f nc')
|
477
|
-
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
478
|
-
rm(ofiles)
|
479
|
-
|
480
|
-
# oType = nc, from input type
|
481
|
-
ofiles = expected.map {|f| f += '.nc'}
|
482
|
-
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag)
|
483
|
-
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
484
|
-
rm(ofiles)
|
485
|
-
|
486
|
-
# oType = nc, from input ENV
|
487
|
-
ofiles = expected.map {|f| f += '.nc2'}
|
488
|
-
@cdo.env = {'CDO_FILE_SUFFIX' => '.nc2'}
|
489
|
-
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag)
|
490
|
-
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
491
|
-
rm(ofiles)
|
492
600
|
|
493
|
-
#
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
end
|
499
|
-
def test_log
|
500
|
-
cmd = '-fldmean -mul -random,r20x20 -topo,r20x20'
|
501
|
-
# logging without a real file
|
502
|
-
@cdo = Cdo.new( returnNilOnError: true)
|
503
|
-
@cdo.debug = false
|
504
|
-
@cdo.logging = true
|
505
|
-
@cdo.topo
|
506
|
-
@cdo.temp
|
507
|
-
@cdo.sinfov(input: cmd)
|
508
|
-
puts @cdo.showLog
|
509
|
-
@cdo.sinfov(input: '-top')
|
510
|
-
@cdo.topo
|
511
|
-
puts @cdo.showLog
|
512
|
-
# use a use definded file for looging
|
513
|
-
@cdo = Cdo.new(logFile: 'test.log',logging: true, returnNilOnError: true)
|
514
|
-
@cdo.topo
|
515
|
-
@cdo.temp
|
516
|
-
@cdo.sinfov(input: cmd)
|
517
|
-
puts @cdo.showLog
|
518
|
-
end
|
519
|
-
def test_noOutputOps
|
520
|
-
c = Cdo.new
|
521
|
-
opsCounf = c.noOutputOps.size
|
522
|
-
assert(opsCounf > 50)
|
523
|
-
assert(opsCounf < 200)
|
601
|
+
# opendap test - broken since 1.9.0
|
602
|
+
def test_opendap
|
603
|
+
ifile = 'https://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/cpc_global_precip/precip.1979.nc'
|
604
|
+
@cdo.sinfov(input: ifile)
|
605
|
+
end if @@debug
|
524
606
|
end
|
525
607
|
end
|
526
608
|
|
@@ -584,10 +666,10 @@ end
|
|
584
666
|
# @cdo.expr("'t=#{TEMP_EXPR['geopotheight']}'", :input => ifile, :output => tempFile)
|
585
667
|
#
|
586
668
|
#
|
587
|
-
# # TIPS: I often work with temporary files and for getting rid of handling them manually the
|
669
|
+
# # TIPS: I often work with temporary files and for getting rid of handling them manually the CdoTempfileStore module can be used:
|
588
670
|
# # Simply include the following methods into you scripts and use tfile for any temporary variable
|
589
671
|
# def tfile
|
590
|
-
#
|
672
|
+
# CdoTempfileStore.path
|
591
673
|
# end
|
592
674
|
# # As an example, the computation of simple atmospherric density could look like
|
593
675
|
# presFile, tempFile = tfile, tfile
|
@@ -596,6 +678,6 @@ end
|
|
596
678
|
# @cdo.chainCall("setname,#{rho} -divc,#{C_R} -div",in: [presFile,tempFile].join(' '), out: densityFile)
|
597
679
|
#
|
598
680
|
# # For debugging, it is helpfull, to avoid the automatic cleanup at the end of the scripts:
|
599
|
-
#
|
681
|
+
# CdoTempfileStore.setPersist(true)
|
600
682
|
# # creates randomly names files. Switch on debugging with
|
601
683
|
# @cdo.Debug = true
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cdo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ralf Mueller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: unifiedPlot
|