#! /usr/bin/env ruby # # ruby script to convert from gtool3 to netCDF # # created by Noriyoshi Takahasi # modified by S Takehiro (2006/01/05) # modified by Seiya Nishizawa # last modified on 09 May 2007 by Seiya Nishizawa # # require "narray" require "numru/netcdf" include NumRu class File @endian = :little def fuget(atype, *opts) @no_strip = false if !opts.nil? then opts.each {|opt| @no_strip = true if opt == :no_strip @endian = :big if opt == :big_endian @endian = :little if opt == :little_endian } end ret = Array.new if atype.class != Array then raise TypeError, "an array expected, got #{array.class}" end if (self.read(4).nil?) then return nil end atype.each {|a| if a.class != Array || a.size < 2 then raise TypeError, "[\"type\", size(, byte)] Array expected." end if a[1].nil? || (a[1] == 0) then raise RangeError, "Please set size > 0." else dsize = a[1].to_i end case a[0] when "char", "character" type = "C" byte = a[2].nil? ? 1 : a[2] when "short" type = (@endian == :little) ? "v" : "n" byte = a[2].nil? ? 2 : a[2] when "int", "integer", "long" type = (@endian == :little) ? "V" : "N" byte = a[2].nil? ? 4 : a[2] when "float", "real" type = (@endian == :little) ? "e" : "g" byte = a[2].nil? ? 4 : a[2] when "double" type = (@endian == :little) ? "E" : "G" byte = a[2].nil? ? 8 : a[2] else type = "C" byte = a[2].nil? ? 1 : a[2] end ary = Array.new case type when "C" if @no_strip then ary.push self.read(dsize*byte) else ary.push self.read(dsize*byte).strip end ary = ary[0] else (0...dsize).each do |i| ary.push self.read(byte).unpack(type)[0] end ary = NArray[ary][true,0] end ret.push ary } self.read(4) return (ret.size == 1) ? ret[0] : ret end def set_endian(endian) if endian == :big || endian == :little then @endian = endian else raise NameError, "Unknown endian type : #{endian}" end end end class Gt3nc class Axis attr_accessor :short_name, :long_name, :unit, :cyclic, :dmin, :dmax, :divs, :divl, :positive, :log, :dstr, :dend attr_reader :data_type def initialize(axis, path, *opts) floc = "GTAXLOC." + axis["name"] fwgt = "GTAXWGT." + axis["name"] @min = axis["min"] @max = axis["max"] @floc = File.open(path + "/" + floc, "rb") begin @fwgt = File.open(path + "/" + fwgt, "rb") rescue end if (opts[0].key? :endian) then @floc.set_endian(opts[0][:endian]) @fwgt.set_endian(opts[0][:endian]) if @fwgt end hl = Array.new 64.times do hl.push ["character", 16] end @idfm, @dset, @item, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, title0, title1, @unit, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, @dstr, @dend, tmp, tmp, tmp, tmp, tmp, tmp, dfmt, @miss, @min, @max, @divs, @divl, styp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, tmp, @data_size = @floc.fuget(hl) @fwgt.fuget(hl) if @fwgt case dfmt when "UR4" @data_type = "real" when "UR8" @data_type = "double" end @dend = 1 if !axis["name"].scan(/[A-Z]1$/)[0].nil? @short_name = @item @long_name = (title0.split + title1.split).to_s case styp when 1 @positive = nil @log = nil when -1 @positive = "down" @log = nil when 2 @positive = nil @log = true when -2 @positive = "down" @log = true end @get = false end def get @get = true @floc.fuget([[@data_type, @data_size.to_i]])[(@dend==1 ? 0 : true)] end def get_wgt @fwgt && @fwgt.fuget([[@data_type, @data_size.to_i]])[(@dend==1 ? 0 : true)] end def get? return @get end def exist_wgt? return !@fwgt.nil? end end attr_accessor :data, :short_name, :long_name, :unit, :dmin, :dmax, :divs, :divl, :styp, :format_id, :dataset, :title, :edit, :cdate, :csign, :mdate, :msign, :dims def initialize(infile, outfile, *opts) @infile = File::open(infile, "rb") @outfile = NetCDF.create(outfile) @gt3_header_path = "." if opts[0].class == Hash then @gt3_header_path = opts[0][:axis] if opts[0].key? :axis if (opts[0].key? :endian) then @infile.set_endian(opts[0][:endian]) end end @hl = nil @last_axis = nil @dims = Hash.new @wrote_global_attr = false @time = Array.new @last_time = nil end def read_data_header if @hl.nil? @hl = Array.new @edit_short = Array.new(8) @edit_long = Array.new(8) @title = Array.new(2) @opt = Array.new(3) @memo = Array.new(12) 64.times do @hl.push ["character", 16] end end @axis = Array.new(3) (0...3).each {|i| @axis[i] = {}} @idfm, @dset, @item, @edit_short[0], @edit_short[1], @edit_short[2], @edit_short[3], @edit_short[4], @edit_short[5], @edit_short[6], @edit_short[7], @fnum, @dnum, @title[0], @title[1], @unit, @edit_long[0], @edit_long[1], @edit_long[2], @edit_long[3], @edit_long[4], @edit_long[5], @edit_long[6], @edit_long[7], time, @date, @utime, @tdur, @axis[0]["name"], @axis[0]["min"], @axis[0]["max"], @axis[1]["name"], @axis[1]["min"], @axis[1]["max"], @axis[2]["name"], @axis[2]["min"], @axis[2]["max"], dfmt, @miss, @min, @max, @divs, @divl, @styp, @opt[0], @opt[1], @opt[2], @memo[0], @memo[1], @memo[2], @memo[3], @memo[4], @memo[5], @memo[6], @memo[7], @memo[8], @memo[9], @memo[10], @memo[11], @cdate, @csign, @mdate, @msign, @data_size = @infile.fuget(@hl) @time.push time.to_i if !(@time.include? time.to_i) return nil if @idfm.nil? # EOF @item.gsub!(/ /,'_') @item.gsub!(/,/,'_') case dfmt when "UR4" @data_type = "real" when "UR8" @data_type = "double" end @get = false return true end def check_axis(*opts) if !(@last_axis == @axis) then @current_dims = Array.new @axis.each {|ax| if (ax["name"] != "") && (ax["name"] != @item) then @current_dims.push ax # まだ開いていない軸ファイルを開く if !(@dims.key? ax["name"]) then @dims[ax["name"]] = Axis.new(ax, @gt3_header_path, *opts) end end } end @last_axis = @axis.clone end def get if @get == false then @get = true @dat = @infile.fuget([[@data_type, @data_size]]) else @dat end end def get? return @get end def write_header @outfile.redef # # global attributes # if !@wrote_global_attrs then fattrs = @outfile.att_names attrname = ["dataset_name", "gt3_file_number", "history", "gt3_edit"] history = "created #{cdate} by #{csign}\n" history += "last modified #{mdate} by #{msign}" gt3_edit = "" @edit_short.each_with_index {|e,i| gt3_edit += sprintf("[%s] : %s\n", e, @edit_long[i]) if e != "" } [@dset, @fnum, history, gt3_edit].each_with_index {|e,i| if !(fattrs.include? e) then @outfile.put_att(attrname[i],e) if e != "" end } @wrote_global_attrs = true end # # dimensions/variables # fdims = @outfile.dim_names if !(fdims.include? "time") d = @outfile.def_dim("time", 0) v = @outfile.def_var("time", "float", [d]) end @current_dims.each {|dim| cdim = @dims[dim["name"]] if !(fdims.include? cdim.long_name[0..2]) then d = @outfile.def_dim(cdim.long_name[0..2], dim["max"].to_i-dim["min"].to_i+1) type = {"double"=>"float","real"=>"sfloat"}[cdim.data_type] v = @outfile.def_var(cdim.long_name[0..2], type, [d]) v.put_att("units", cdim.unit) v.put_att("long_name", cdim.long_name) v.put_att("divs", cdim.divs.to_f) v.put_att("divl", cdim.divl.to_f) v.put_att("positive", cdim.positive) if !cdim.positive.nil? v.put_att("log", "yes") if !cdim.log.nil? if cdim.exist_wgt? v = @outfile.def_var(cdim.long_name[0..2]+"_wgt", type, [d]) v.put_att("longname", cdim.long_name + "_wgt") end end } if !(@outfile.var_names.include? @item) then d = Array.new @current_dims.each{|dim| d.push @outfile.dim(@dims[dim["name"]].long_name[0..2]) } d.push @outfile.dim("time") type = {"double"=>"float","real"=>"sfloat"}[@data_type] v = @outfile.def_var(@item, type, d) v.put_att("units", @unit) v.put_att("long_name", (@title[0].split + @title[1].split).to_s) v.put_att("missing_value", @miss.to_f) v.put_att("divs", @divs.to_f) v.put_att("divl", @divl.to_f) v.put_att("gt3_min", @min) v.put_att("gt3_max", @max) end @outfile.enddef end def write_data @outfile.enddef @current_dims.each{|dim| if !((cdim = @dims[dim["name"]]).get?) then loc = cdim.get wgt = cdim.get_wgt if Array===loc || dim["min"].to_i != 1 || dim["max"].to_i != 1 loc = loc[(dim["min"].to_i-1)..(dim["max"].to_i-1)] wgt = wgt[(dim["min"].to_i-1)..(dim["max"].to_i-1)] if wgt end v_loc = @outfile.var(cdim.long_name[0..2]) v_loc.put(loc) v_wgt = @outfile.var(cdim.long_name[0..2]+"_wgt") if wgt v_wgt.put(wgt) if wgt end } if @last_time != @time then @outfile.var("time").put(@time[-1], "index"=>[@time.size-1]) end rstr = Array.new rend = Array.new @current_dims.each {|ax| rstr.push ax["min"].to_i - 1 rend.push ax["max"].to_i - 1 } rstr.push @time.size - 1 rend.push @time.size - 1 print "TIME = " + @time[-1].to_s + "\n" @outfile.var(@item).put(self.get, "start"=>rstr, "end"=>rend) @outfile.sync @last_time = @time.clone end end if $0 == __FILE__ then infile = ARGV.shift if infile.nil? then raise "Usage : #$0 infile [outfile] [axis=path_to_axis] [endian=big_or_little]" end if !ARGV[0].nil? && !ARGV[0].include?("=") then outfile = ARGV.shift else outfile = infile + ".nc" end axis = "." endian = :big opts = ARGV opts.each{|opt| case opt when /^endian=/ case opt[7..-1] when "big" endian = :big when "little" endian = :little else raise("option of endian must be big or little, #{opt[6..-1]}") end when /axis=/ axis = opt[5..-1] end } a = Gt3nc.new(infile, outfile, :axis=>axis, :endian=>endian) while(a.read_data_header) a.check_axis(:endian=>endian) a.write_header a.write_data end end