ruby-grads 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d221bddb4c3b9dbdf46348691daac803a5194520580bb4ee0068aa1ef3aa24d
4
- data.tar.gz: 25d393d1683f63fa60857831f16209dee5105a897ca96fd4dbe86e9fb02450a6
3
+ metadata.gz: 95bceedf1ed3e79fdb745fcc31e9b4153475d69fd72377fed5c3a16384d610cd
4
+ data.tar.gz: 5aa72a291c3e54e6de246215187b2ca9bd2e074dfb1a3c26421a5dd37b47b9de
5
5
  SHA512:
6
- metadata.gz: fc9e107a70200bd51ef5170743fc16f6216af003bfd4f72cfa02b12ff2d1d866365fd86d7c3daeae3cd922d0f882f1cc1cf75b24e4e3dcc8f3a2b63c901d29b8
7
- data.tar.gz: d1a255347549db953a31fa9f8898b1b5551e889599600b3a525376a04a6360cc5dcf5912a3deca77e3a443093a76393d6eab08d4990d8ec870575413ddc4019e
6
+ metadata.gz: 425065b1cc532c8e9934f0e6cf812848edb5b5886f241361daf8252b18d1da8d0985ffaf979f885c47a411d2cf2c5a7ea55bb5694c42d56981979bb3f6a783c9
7
+ data.tar.gz: a189c6e9bc19933042aea13dd223616f252f362540c213a6715c2ec03b027bc00c44ca0bf8be5cc8efb7f3fae90ddc348650a79f317f0f68d642c61a375b9cbb
@@ -0,0 +1,735 @@
1
+ #
2
+ #
3
+ #
4
+ # A library to read GrADS gridded data into CArray.
5
+ #
6
+ # TODO
7
+ # * options
8
+ #
9
+ # NOTE
10
+ # * 'dset' should be a file
11
+ #
12
+ # --- supported -------
13
+ # dset
14
+ # dtype
15
+ # title
16
+ # undef
17
+ # options
18
+ # pdef
19
+ # xdef
20
+ # ydef
21
+ # zdef
22
+ # tdef
23
+ # vectorpairs
24
+ # * comment
25
+ #
26
+ # --- not supported ---
27
+ # chsub
28
+ # fileheader
29
+ # theader
30
+ # tailerbytes
31
+ # xyheader
32
+ #
33
+
34
+ require "carray"
35
+ require "strscan"
36
+
37
+ module GrADS
38
+ end
39
+
40
+ class GrADS::Gridded
41
+ end
42
+
43
+ class GrADS::Gridded::Variable
44
+
45
+ def initialize (name, ctl)
46
+ @name = name
47
+ @dim = ctl.vardims[name]
48
+ @tsize = ctl.tsize
49
+ @dset = ctl.dset
50
+ @chunksize = ctl.chunksize
51
+ @offset = ctl.varoffsets[name]
52
+ @undef = ctl.undef
53
+ end
54
+
55
+ attr_reader :name, :dim, :size, :dset, :offset
56
+
57
+ def each_trange (trng=nil)
58
+ case trng
59
+ when NilClass, FalseClass
60
+ (0...@tsize).each do |t|
61
+ yield(t)
62
+ end
63
+ when Range
64
+ trng.each do |t|
65
+ yield(t)
66
+ end
67
+ else
68
+ t = trng
69
+ yield(t)
70
+ end
71
+ end
72
+
73
+ def tsize (trng=nil)
74
+ case trng
75
+ when NilClass, FalseClass
76
+ return @tsize
77
+ when Range
78
+ return trng.to_a.size
79
+ else
80
+ return 1
81
+ end
82
+ end
83
+
84
+ def [] (*argv)
85
+ trng = argv.shift
86
+ buf = CArray.float(*@dim)
87
+ out = nil
88
+ open(@dset) { |io|
89
+ if tsize(trng) == 1
90
+ each_trange(trng) { |t|
91
+ io.seek(t * @chunksize + offset)
92
+ buf.load_binary(io)
93
+ out = buf[*argv]
94
+ }
95
+ else
96
+ outlist = []
97
+ i = 0
98
+ bind_ok = false
99
+ each_trange(trng) { |t|
100
+ t * @chunksize + offset
101
+ io.seek(t * @chunksize + offset)
102
+ buf.load_binary(io)
103
+ obj = buf[*argv].to_ca
104
+ if obj.is_a?(CArray)
105
+ outlist << obj
106
+ else
107
+ outlist << CA_FLOAT(obj)
108
+ bind_ok = true
109
+ end
110
+ i += 1
111
+ }
112
+ if bind_ok
113
+ out = CArray.bind(:float, outlist, 0)
114
+ else
115
+ out = CArray.merge(:float, outlist, 0)
116
+ end
117
+ end
118
+ }
119
+ out[:eq, @undef] = UNDEF
120
+ return out
121
+ end
122
+
123
+ def to_ca
124
+ return self[]
125
+ end
126
+
127
+ end
128
+
129
+ class GrADS::Gridded
130
+
131
+ def initialize (ctl_file, radius: nil)
132
+ @ctl_file = ctl_file
133
+ @entries = scan_ctl_file(ctl_file)
134
+ @radius = radius
135
+ parse_entries(@entries)
136
+ end
137
+
138
+ attr_reader :ctl_file, :entries
139
+ attr_reader :dset, :title, :undef
140
+ attr_reader :pdef, :proj
141
+ attr_reader :isize, :jsize
142
+ attr_reader :xsize, :ysize, :zsize, :tsize
143
+ attr_reader :xaxis, :yaxis, :zaxis, :taxis
144
+ attr_reader :vectorpair
145
+ attr_reader :varnames, :vars, :vardims, :varoffsets
146
+ attr_reader :chunksize
147
+
148
+ private
149
+
150
+ def scan_ctl_file (ctl_file)
151
+ io = open(ctl_file, "r:ASCII-8BIT")
152
+ edefs = ""
153
+ vdefs = ""
154
+ headers = ""
155
+ while line = io.gets
156
+ case line
157
+ when /\A\s*vars/i
158
+ vdefs << line
159
+ while line = io.gets
160
+ vdefs << line
161
+ if line =~ /\A\s*endvars/i
162
+ break
163
+ end
164
+ end
165
+ when /\A\s*edef/i
166
+ raise "edef entry is not supported, sorry!"
167
+ edefs << line
168
+ while line = io.gets
169
+ ddefs << line
170
+ if line =~ /\A\s*endedef/i
171
+ break
172
+ end
173
+ end
174
+ else
175
+ headers << line
176
+ end
177
+ end
178
+ entries = scan_headers(headers)
179
+ entries["vars"] = scan_vars(vdefs)
180
+ return entries
181
+ end
182
+
183
+ def scan_headers (headers)
184
+ items = {}
185
+ buffer = ""
186
+ headers.each_line do |line|
187
+ case line
188
+ when /\A\s*\*/, /\A\s*\Z/
189
+ next
190
+ when /A\s*(dtype)\s*(.*)\Z/i
191
+ raise "data file is not GrADS gridded binary"
192
+ when /\A\s*(dset|title|undef|options|pdef|xdef|ydef|zdef|tdef|vectorpairs)\s*(.*)\Z/i
193
+ name = $1.downcase
194
+ if items[name]
195
+ items[name] = [items[name]]
196
+ items[name] << $2.rstrip
197
+ buffer = items[name].last
198
+ else
199
+ items[name] = $2.rstrip
200
+ buffer = items[name]
201
+ end
202
+ when /\A\s*(chsub|fileheader|theader|tailerbyts|xyheader)\s*(.*)\Z/i
203
+ raise "#{$1} entry is not supported, sorry"
204
+ else
205
+ line = line.chomp.sub(/\\\z/,'').rstrip
206
+ buffer.replace(buffer + " " + line)
207
+ end
208
+ end
209
+ return items
210
+ end
211
+
212
+ def scan_vars (vars)
213
+ items = {:namelist=>[]}
214
+ nvars = nil
215
+ vars.each_line do |line|
216
+ case line
217
+ when /\A\s*\*/, /\A\s*\Z/
218
+ next
219
+ when /\A\s*vars\s+(\d+)/i
220
+ nvars = $1.to_i
221
+ when /\A\s*endvars/i
222
+ break
223
+ when /\A\s*(\w+)\s*(.*)\Z/
224
+ name = $1
225
+ list = $2
226
+ items[:namelist].push(name)
227
+ items[name] = list
228
+ end
229
+ end
230
+ if items[:namelist].size != nvars
231
+ raise "invalid vars number"
232
+ end
233
+ return items
234
+ end
235
+
236
+ def parse_entries (entries)
237
+ parse_dset(entries["dset"])
238
+ if entries.has_key?("title")
239
+ parse_title(entries["title"])
240
+ end
241
+ parse_undef(entries["undef"])
242
+ parse_options(*entries["options"])
243
+ if entries.has_key?("pdef")
244
+ parse_pdef(entries["pdef"])
245
+ parse_xdef(entries["xdef"], with_pdef = true)
246
+ parse_ydef(entries["ydef"], with_pdef = true)
247
+ else
248
+ parse_xdef(entries["xdef"], with_pdef = false)
249
+ parse_ydef(entries["ydef"], with_pdef = false)
250
+ end
251
+ parse_zdef(entries["zdef"])
252
+ parse_tdef(entries["tdef"])
253
+ if entries.has_key?("vectorpairs")
254
+ parse_vectorpairs(entries["vectorpairs"])
255
+ end
256
+ parse_vars(entries["vars"][:namelist], entries["vars"])
257
+ end
258
+
259
+ def parse_dset (line)
260
+ line.strip!
261
+ if line =~ /\A\^/
262
+ cwd = File.dirname(File.expand_path(@ctl_file))
263
+ @dset = File.join(cwd, line[1..-1])
264
+ else
265
+ @dset = line
266
+ end
267
+ end
268
+
269
+ def parse_title (line)
270
+ @title = line
271
+ end
272
+
273
+ def parse_undef (line)
274
+ @undef = Float(line)
275
+ end
276
+
277
+ def parse_options (*lines)
278
+ # yrev o
279
+ # zrev o
280
+ # template x
281
+ # squential o
282
+ # 365_day_calendar ?
283
+ # byteswapped o
284
+ # big_endian o
285
+ # little_endian o
286
+ # cray_32bit_ieee ?
287
+ end
288
+
289
+ def parse_pdef (line)
290
+ list = line.split(/\s+/)
291
+ @pdef = list
292
+ @isize = list[0].to_i
293
+ @jsize = list[1].to_i
294
+ case list[2].downcase
295
+ when "nps", "sps"
296
+ parse_pdef_xps(list[2..-1])
297
+ when "lcc", "lccr"
298
+ parse_pdef_lcc(list[2..-1])
299
+ when "eta.u"
300
+ parse_pdef_eta_u(list[2..-1])
301
+ when "pse"
302
+ parse_pdef_pse(list[2..-1])
303
+ when "ops"
304
+ parse_pdef_ops(list[2..-1])
305
+ when "rotll", "rotllr"
306
+ parse_pdef_rotll(list[2..-1])
307
+ when "bilin"
308
+ parse_pdef_bilin(list[2..-1])
309
+ when "general"
310
+ parse_pdef_general(list[2..-1])
311
+ when "file"
312
+ parse_pdef_file(list[2..-1])
313
+ else
314
+ raise "pdef #{list[2].downcase} is not supported"
315
+ end
316
+ end
317
+
318
+ def parse_pdef_xps (args)
319
+ raise NotImplementedError, "not implemented"
320
+ end
321
+
322
+ def parse_pdef_lcc (args)
323
+ require "proj4r"
324
+ rlat = args[1].to_f
325
+ rlon = args[2].to_f
326
+ xi = args[3].to_f
327
+ yi = args[4].to_f
328
+ slat1 = args[5].to_f
329
+ slat2 = args[6].to_f
330
+ slon = args[7].to_f
331
+ dx = args[8].to_f
332
+ dy = args[9].to_f
333
+ slat0 = slat1 > 0 ? 90 : -90
334
+ if @radius
335
+ p @radius
336
+ proj = PROJ4::Proj.new %{
337
+ +ellps=sphere +a=#{@radius} +b=#{@radius} +proj=lcc \
338
+ +lat_1=#{slat1} +lat_2=#{slat2} +lon_0=#{slon}
339
+ }
340
+ else
341
+ proj = PROJ4::Proj.new %{
342
+ +ellps=sphere +proj=lcc \
343
+ +lat_1=#{slat1} +lat_2=#{slat2} +lon_0=#{slon}
344
+ }
345
+ end
346
+ rx, ry = proj.forward(rlon, rlat)
347
+ rx0 = rx - (xi-1)*dx
348
+ ry0 = ry - (yi-1)*dy
349
+ @forward = lambda { |lon, lat|
350
+ mx, my = proj.forward(lon, lat)
351
+ [(mx - rx0)/dx, (my - ry0)/dy]
352
+ }
353
+ @inverse = lambda { |fi, fj|
354
+ mx = fi*dx + rx0
355
+ my = fj*dy + ry0
356
+ proj.inverse(mx, my)
357
+ }
358
+ @xy = lambda {
359
+ x = CArray.float(xsize).seq(rx0, dx)
360
+ y = CArray.float(ysize).seq(ry0, dy)
361
+ [x[ysize,:%].to_ca, y[:%,xsize].to_ca]
362
+ }
363
+ end
364
+
365
+ def parse_pdef_eta_u (args)
366
+ raise NotImplementedError, "not implemented"
367
+ end
368
+
369
+ def parse_pdef_pse (args)
370
+ raise NotImplementedError, "not implemented"
371
+ end
372
+
373
+ def parse_pdef_ops (args)
374
+ raise NotImplementedError, "not implemented"
375
+ end
376
+
377
+ def parse_pdef_rotll (args)
378
+ raise NotImplementedError, "not implemented"
379
+ end
380
+
381
+ def parse_pdef_general (args)
382
+ raise NotImplementedError, "not implemented"
383
+ end
384
+
385
+ def parse_pdef_file (args)
386
+ raise NotImplementedError, "not implemented"
387
+ end
388
+
389
+ def parse_xdef (line, with_pdef=false)
390
+ list = line.split(/\s+/)
391
+ if with_pdef
392
+ @xsize = list[0].to_i
393
+ else
394
+ @isize = list[0].to_i
395
+ @xsize = @isize
396
+ end
397
+ case list[1]
398
+ when /linear/i
399
+ @xaxis = []
400
+ @xsize.times do |i|
401
+ @xaxis << list[2].to_f + i*list[3].to_f
402
+ end
403
+ when /levels/i
404
+ @xaxis = list[2..-1].map{|x| x.to_f}
405
+ end
406
+ end
407
+
408
+ def parse_ydef (line, with_pdef=false)
409
+ list = line.split(/\s+/)
410
+ if with_pdef
411
+ @ysize = list[0].to_i
412
+ else
413
+ @jsize = list[0].to_i
414
+ @ysize = @jsize
415
+ end
416
+ case list[1]
417
+ when /linear/i
418
+ @yaxis = []
419
+ @ysize.times do |i|
420
+ @yaxis << list[2].to_f + i*list[3].to_f
421
+ end
422
+ when /levels/i
423
+ @yaxis = list[2..-1].map{|y| y.to_f}
424
+ end
425
+ end
426
+
427
+ def parse_zdef (line)
428
+ list = line.split(/\s+/)
429
+ @zsize = list[0].to_i
430
+ case list[1]
431
+ when /linear/i
432
+ @zaxis = []
433
+ @zsize.times do |i|
434
+ @zaxis << list[2].to_f + i*list[3].to_f
435
+ end
436
+ when /levels/i
437
+ @zaxis = list[2..-1].map{|z| z.to_f}
438
+ end
439
+ end
440
+
441
+ def parse_tdef (line)
442
+ list = line.split(/\s+/)
443
+ @tsize = list[0].to_i
444
+ @inittime = DateTime.parse(list[2].sub(/z/i,' '))
445
+ @taxis = [@inittime]
446
+ case list[3]
447
+ when /\A(\d+)(mn|hr|dy|mo|yr)/
448
+ case $2
449
+ when "mn"
450
+ (1..@tsize-1).each do |i|
451
+ @taxis[i] = @taxis[i-1] + ($1.to_i).quo(1440)
452
+ end
453
+ when "hr"
454
+ (1..@tsize-1).each do |i|
455
+ @taxis[i] = @taxis[i-1] + ($1.to_i).quo(24)
456
+ end
457
+ when "dy"
458
+ (1..@tsize-1).each do |i|
459
+ @taxis[i] = @taxis[i-1] + $1.to_i
460
+ end
461
+ when "mn"
462
+ (1..@tsize-1).each do |i|
463
+ @taxis[i] = @taxis[i-1] >> $1.to_i
464
+ end
465
+ when "yr"
466
+ (1..@tsize-1).each do |i|
467
+ @taxis[i] = @taxis[i-1] >> (12*$1.to_i)
468
+ end
469
+ end
470
+ else
471
+ raise "invalid time increment"
472
+ end
473
+ end
474
+
475
+ def parse_vectorpairs (line)
476
+ list = line.split(/\s+/)
477
+ @vectorpair = list.map{|pair| pair.downcase.split(/\s*,\s*/)}
478
+ end
479
+
480
+ def parse_vars (namelist, hash)
481
+ @varnames = namelist
482
+ @vars = {}
483
+ @vardims = {}
484
+ @varoffsets = {}
485
+ @chunksize = 0
486
+ namelist.each do |name|
487
+ line = hash[name]
488
+ ss = StringScanner.new(line)
489
+ znum = ss.scan(/\A.*?\s+/).rstrip.to_i
490
+ units = ss.scan(/\A.*?\s+/).rstrip
491
+ desc = ss.rest
492
+ @vars[name] = [znum, units, desc]
493
+ if znum == 0
494
+ @vardims[name] = [@jsize,@isize]
495
+ else
496
+ @vardims[name] = [znum, @jsize,@isize]
497
+ end
498
+ @varoffsets[name] = @chunksize
499
+ @chunksize += 4 * @vardims[name].inject(1) {|s,d| s * d}
500
+ end
501
+ end
502
+
503
+ public
504
+
505
+ def inverse (fi, fj)
506
+ return @inverse[fi,fj]
507
+ end
508
+
509
+ def forward (rlon, rlat)
510
+ return @forward[rlon, rlat]
511
+ end
512
+
513
+ def lonlat
514
+ fi = CArray.float(xsize).seq
515
+ fj = CArray.float(ysize).seq
516
+ ii = fi[ysize,:%]
517
+ jj = fj[:%,xsize]
518
+ return @inverse[ii, jj]
519
+ end
520
+
521
+ def xy
522
+ return @xy[]
523
+ end
524
+
525
+ def var (name)
526
+ unless @varnames.include?(name)
527
+ raise "invalid variable name '#{name}'"
528
+ end
529
+ return Variable.new(name, self)
530
+ end
531
+
532
+ def template (ctl_file, &block)
533
+ asm = Writer.new(self)
534
+ asm.define(&block)
535
+ asm.write(ctl_file)
536
+ end
537
+
538
+ end
539
+
540
+ class GrADS::Gridded::Writer
541
+
542
+ def initialize (ctl = nil)
543
+ @dset = nil
544
+ @title = nil
545
+ @options = []
546
+ @vectorpairs = nil
547
+ @vars = []
548
+ @nt = 1
549
+ if ctl
550
+ e = ctl.entries
551
+ @undef = e["undef"]
552
+ @pdef = e["pdef"]
553
+ @xdef = e["xdef"]
554
+ @ydef = e["ydef"]
555
+ @zdef = e["zdef"]
556
+ @tdef = e["tdef"]
557
+ else
558
+ @undef = nil
559
+ @pdef = nil
560
+ @xdef = nil
561
+ @ydef = nil
562
+ @zdef = nil
563
+ @tdef = "1 linear 0z1jan2000"
564
+ end
565
+ end
566
+
567
+ private
568
+
569
+ def dset (arg)
570
+ @dset = arg
571
+ end
572
+
573
+ def title (arg)
574
+ @title = arg
575
+ end
576
+
577
+ def undef! (arg)
578
+ @undef = arg
579
+ end
580
+
581
+ def options (*args)
582
+ @options.push(*args)
583
+ end
584
+
585
+ def pdef (*args)
586
+ @pdef = args.join(" ")
587
+ end
588
+
589
+ def xdef (*args)
590
+ if args.size == 1 and
591
+ ( args.first.is_a?(CArray) or args.first.is_a?(Array) )
592
+ ary = args.first
593
+ @xdef = [ary.length, "levels", ary.to_a.join("\n")].join(" ")
594
+ else
595
+ @xdef = args.join(" ")
596
+ end
597
+ end
598
+
599
+ def ydef (*args)
600
+ if args.size == 1 and
601
+ ( args.first.is_a?(CArray) or args.first.is_a?(Array) )
602
+ ary = args.first
603
+ @ydef = [ary.length, "levels", ary.to_a.join("\n")].join(" ")
604
+ else
605
+ @ydef = args.join(" ")
606
+ end
607
+ end
608
+
609
+ def zdef (*args)
610
+ if args.size == 1 and
611
+ ( args.first.is_a?(CArray) or args.first.is_a?(Array) )
612
+ ary = args.first
613
+ @zdef = [ary.length, "levels", ary.to_a.join("\n")].join(" ")
614
+ else
615
+ @zdef = args.join(" ")
616
+ end
617
+ end
618
+
619
+ def tdef (*args)
620
+ if args.size == 1 and
621
+ ( args.first.is_a?(CArray) or args.first.is_a?(Array) )
622
+ ary = args.first
623
+ @tdef = [ary.length, "levels", ary.to_a.join("\n")].join(" ")
624
+ else
625
+ @tdef = args.join(" ")
626
+ end
627
+ end
628
+
629
+ def vectorpairs (args)
630
+ @vectorpairs = args.join(" ")
631
+ end
632
+
633
+ def var2d (name, obj, desc = "x")
634
+ @vars.push([name, 2, obj, desc])
635
+ if obj.rank == 3 and obj.dim0 > @nt
636
+ @nt = obj.dim0
637
+ end
638
+ end
639
+
640
+ def var3d (name, obj, desc = "x")
641
+ @vars.push([name, 3, obj, desc])
642
+ if obj.rank == 4 and obj.dim0 > @nt
643
+ @nt = obj.dim0
644
+ end
645
+ end
646
+
647
+ public
648
+
649
+ def define (&block)
650
+ instance_eval(&block)
651
+ end
652
+
653
+ def write (ctl_file)
654
+ open(ctl_file, "w") { |io|
655
+ if @dset
656
+ io.puts "dset #{@dset}"
657
+ else
658
+ raise "dset is required"
659
+ end
660
+ if @title
661
+ io.puts "title #{@title}"
662
+ end
663
+ io.puts "undef #{@undef}"
664
+ @options.each do |opt|
665
+ io.puts %{options #{opt}}
666
+ end
667
+ if @pdef
668
+ io.puts %{pdef #{@pdef}}
669
+ end
670
+ io.puts %{xdef #{@xdef}}
671
+ io.puts %{ydef #{@ydef}}
672
+ io.puts %{zdef #{@zdef}}
673
+ io.puts %{tdef #{@tdef}}
674
+ if @vectorpairs
675
+ io.puts %{vectorpairs #{@vectorpairs}}
676
+ end
677
+
678
+ if @dset =~ /\A\^/
679
+ cwd = File.dirname(File.expand_path(ctl_file))
680
+ dset = File.join(cwd, @dset[1..-1])
681
+ else
682
+ dset = @dset
683
+ end
684
+
685
+ io.puts %{vars #{@vars.size}}
686
+ @vars.each do |name, d, obj, desc|
687
+ case d
688
+ when 2
689
+ io.puts %{#{name} 1 99 #{desc}}
690
+ when 3
691
+ case obj.rank
692
+ when 3
693
+ io.puts %{#{name} #{obj.dim0} 99 #{desc} }
694
+ when 4
695
+ io.puts %{#{name} #{obj.dim1} 99 #{desc} }
696
+ else
697
+ raise "invalid size"
698
+ end
699
+ end
700
+ end
701
+ io.puts "endvars"
702
+
703
+ open(dset, "w") { |dat|
704
+ @nt.times do |i|
705
+ @vars.each do |name, d, obj, desc|
706
+ case d
707
+ when 2
708
+ case obj.rank
709
+ when 2
710
+ obj.float.dump_binary(dat)
711
+ when 3
712
+ obj[i,false].float.dump_binary(dat)
713
+ end
714
+ when 3
715
+ case obj.rank
716
+ when 3
717
+ obj.float.dump_binary(dat)
718
+ when 4
719
+ obj[i,false].float.dump_binary(dat)
720
+ end
721
+ end
722
+ end
723
+ end
724
+ }
725
+ }
726
+
727
+
728
+ end
729
+
730
+ def template (ctlfile, &block)
731
+ define(&block)
732
+ write(ctlfile)
733
+ end
734
+
735
+ end
data/lib/grads/gridded.rb CHANGED
@@ -128,9 +128,10 @@ end
128
128
 
129
129
  class GrADS::Gridded
130
130
 
131
- def initialize (ctl_file)
131
+ def initialize (ctl_file, radius: nil)
132
132
  @ctl_file = ctl_file
133
133
  @entries = scan_ctl_file(ctl_file)
134
+ @radius = radius
134
135
  parse_entries(@entries)
135
136
  end
136
137
 
@@ -330,10 +331,17 @@ class GrADS::Gridded
330
331
  dx = args[8].to_f
331
332
  dy = args[9].to_f
332
333
  slat0 = slat1 > 0 ? 90 : -90
333
- proj = PROJ4::Proj.new %{
334
- +ellps=sphere +proj=lcc \
335
- +lat_1=#{slat1} +lat_2=#{slat2} +lat_0=90 +lon_0=#{slon}
336
- }
334
+ if @radius
335
+ proj = PROJ4::Proj.new %{
336
+ +ellps=sphere +a=#{@radius} +b=#{@radius} +proj=lcc \
337
+ +lat_1=#{slat1} +lat_2=#{slat2} +lon_0=#{slon}
338
+ }
339
+ else
340
+ proj = PROJ4::Proj.new %{
341
+ +ellps=sphere +proj=lcc \
342
+ +lat_1=#{slat1} +lat_2=#{slat2} +lon_0=#{slon}
343
+ }
344
+ end
337
345
  rx, ry = proj.forward(rlon, rlat)
338
346
  rx0 = rx - (xi-1)*dx
339
347
  ry0 = ry - (yi-1)*dy
@@ -346,6 +354,11 @@ class GrADS::Gridded
346
354
  my = fj*dy + ry0
347
355
  proj.inverse(mx, my)
348
356
  }
357
+ @xy = lambda {
358
+ x = CArray.float(xsize).seq(rx0, dx)
359
+ y = CArray.float(ysize).seq(ry0, dy)
360
+ [x, y]
361
+ }
349
362
  end
350
363
 
351
364
  def parse_pdef_eta_u (args)
@@ -495,6 +508,18 @@ class GrADS::Gridded
495
508
  def forward (rlon, rlat)
496
509
  return @forward[rlon, rlat]
497
510
  end
511
+
512
+ def lonlat
513
+ fi = CArray.float(xsize).seq
514
+ fj = CArray.float(ysize).seq
515
+ ii = fi[ysize,:%]
516
+ jj = fj[:%,xsize]
517
+ return @inverse[ii, jj]
518
+ end
519
+
520
+ def xy
521
+ return @xy[]
522
+ end
498
523
 
499
524
  def var (name)
500
525
  unless @varnames.include?(name)
data/ruby-grads.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
 
2
2
  Gem::Specification::new do |s|
3
- version = "1.0.0"
3
+ version = "1.0.2"
4
4
 
5
5
  files = Dir.glob("**/*") - [
6
- Dir.glob("carray*.gem"),
6
+ Dir.glob("ruby-grads*.gem"),
7
7
  ].flatten
8
8
 
9
9
  s.platform = Gem::Platform::RUBY
@@ -18,6 +18,5 @@ Gem::Specification::new do |s|
18
18
  s.homepage = 'https://github.com/himotoyoshi/ruby-grads'
19
19
  s.files = files
20
20
  # s.extensions = [ "extconf.rb" ]
21
- s.has_rdoc = false
22
21
  s.required_ruby_version = ">= 1.8.1"
23
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-grads
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroki Motoyoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-27 00:00:00.000000000 Z
11
+ date: 2019-01-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: " Library for driving GrADS from Ruby\n"
14
14
  email: ''
@@ -19,6 +19,7 @@ files:
19
19
  - README.md
20
20
  - extconf.rb
21
21
  - lib/grads.rb
22
+ - lib/grads/binary.rb
22
23
  - lib/grads/command.rb
23
24
  - lib/grads/gridded.rb
24
25
  - lib/grads/lib/colorbar.rb