#!/usr/bin/env ruby
=begin
= rgtview.rb -- netCDF クイックビューア

= VER
  rgtview.rb ver.2.1

= SYNOPSTIC
  rgtview ((options)) <file <file1, file2...>> <var>

= DESCRIPTION
rgtview は引数に netCDF 形式のファイルに格納された変数から適切な図形表現を表示する
簡易可視化ソフトウェアです. 以下のファイル群によって構成 or 依存しています.

* rgtview.rb
* libdraw.rb
* libgphys-e.rb

上記ファイルの簡単な解説を以下でします.

== rgtview.rb
このファイル. rgtview のメインプログラム.

== libdraw.rb
主に, 描画に関する設定やメソッドの定義を行っている Draw モジュールを定義しているファイル.
特に描画する変数に合わせた描画パラメータの設定はこのファイル内で行っている.

== libgphys-e.rb
GPhys クラスに対する拡張メソッドを定義しているファイル.

= OPTIONS

== 全体オプション

=== いろいろ
+ -h, --help
ヘルプを表示します.

+ -f, --file
生成した画像を PostScript 形式のファイルに保存します. 保存するファイル名は dcl.ps です.
なお, アニメーションモード(--animate, --smooth) で --file を指定した場合, 各々の画像は
dcl_001.ps, dcl_002.ps というように適宜番号が振られ名前で保存されます. 

=== 次元操作に関するオプション
+ --mean dim<,dim2,dim3>
指定した次元 (dim) に沿って平均操作を施します. 複数の次元について平均をとりたい場合は
次元同士を","でつなぎます. このとき空白を挿入してはいけません. 操作を行った次元は縮約され,
メモリ上で消滅します. もちろん, 元のファイルの次元は変更されません. ご安心を. 

+ --range dim=(from)..(to)
指定した次元(dim)を開始点(from)と終点(to)で切り取って処理します. 


=== 描画に関するオプション
+ --line
線グラフを描画します. デフォルトでは指定した操作を行った後の多次元配列の次元から適切な
絵を描画しますが, このオプションを指定すると適当な次元を切り出して, 強制的に線グラフを
描画します.

+ --bs
表示される絵をあらかじめ画面の裏で描画します. これにより画像の切り替わりが滑らかになります.
X のバッキングストア機能を有効にしていなければなりません.

+ --animate dim=num
アニメーションを作成します. 今のところ, アニメーションする軸(dim)および開始する点(num)を指定しなければ
なりません. また, num は軸の値ではなく, 配列の添字でなければなりません. 画像の切替えはマウスで絵をクリックするか
Enter キー, Space キーで行います.

+ --datime
(ERA40 データのみ有効なオプション) 時間軸が "time" という次元変数で指定されており, かつ
4 つの値(ex. 0, 0600, 1200, 1800) で構成されている場合に, アニメーションを作成すると
--animate, --smooth で指定した次元+"time"軸方向にスライドしつつアニメーションを行います.


+ --nowait
アニメーションモードで, マウスやキーボードからの入力を待たずに自動的に切替えを行います.
--animate が指定されていないと有効になりません.

+ --smooth dim=num
--animate, --bs, --nowait が同時に指定されているのと等しいです. 引数の与え方は --animate と同じです.

+ --exch
軸をひっくり返します. 

+ --title title
絵のタイトルを陽に指定します. なおデフォルトのタイトルは, 描画する変数の long_name です.

== 等値線図に関するオプション
+ --noshade
トーンを抑制し, 等値線(コンター)のみ描画します.

+ --nocont
コンターを抑制し, 色(もしくはトーンパターン)の塗わけのみ描画します.

+ --map
地図を重ねます. 今のところメルカトル図法しか対応していません.


== 線グラフに対するオプション

+ 現在なし.

=end


require "getopts"        # for option_parse
require "numru/netcdf"

class NArray # for deep_copy
  def self._load(o); to_na(*Marshal::load(o)).ntoh; end
  def _dump(limit); Marshal::dump([hton.to_s, typecode, *shape]); end
end

module NumRu
  class NetCDFVar
    def scaled_get(hash=nil)
      sf = att('scale_factor')
      ao = att('add_offset')
      if ( sf == nil && ao == nil ) then
	# no scaling --> just call get
	simple_get(hash)
      else
	if (sf != nil) 
	  csf = sf.get
	       if csf.is_a?(NArray) then  # --> should be a numeric
		 csf = csf[0]
	       elsif csf.is_a?(String)
		 raise NetcdfError, "scale_factor is not a numeric"
	       end
	  if(csf == 0) then; raise NetcdfError, "zero scale_factor"; end
	else
	  csf = 1.0      # assume 1 if not defined
	end
	if (ao != nil) 
	  cao = ao.get
	  if cao.is_a?(NArray) then  # --> should be a numeric
	    cao = cao[0]
	  elsif csf.is_a?(String)
	    raise NetcdfError, "add_offset is not a numeric"
	  end
	    else
	  cao = 0.0      # assume 0 if not defined
	end
	var = simple_get(hash)
	var = var.to_type( NArray::FLOAT )
	csf*var+cao
      end
    end  
    alias put scaled_put
    alias get scaled_get
  end
end

require "libgphys-e" # 内部で numru/ggraph を呼び出している
require "libdraw-e"
include Draw

DCL.glcset('DUPATH','/home/daktu32/.dcldir/')     # set User Path for dcldatabase

##########################################
#
#    オプション解析部


unless getopts("hHfnc", "help", "file", "native", "mean:", "itr:", "title:", "range:", "nocont", "noshade", "line", "map", "noannote", "animate:", "smooth:", "bs", "nowait", "exch", "datime", "daytime", "min:", "max:", "default", "mvunit")
  print "#{$0}:illegal options. please exec this program with -h/--help. \n"
  exit 1
end

if ($OPT_animate) && ($OPT_smooth)
  print "please set options only animate or smooth\n"
  exit 1
end

file = Array.new         # 引数中の nc ファイルを配列に格納
ARGV.each do |i|         
  if i =~ /.nc$/ then
    p i
    file << i
  end
end

if ARGV[ARGV.size-1] =~ /.nc$/ then
  var = NetCDF.open(file[0], "r").var_names[-1]
else 
  var = ARGV[ARGV.size-1]
end


p var

if file.size == 1 then # 単一ファイルが指定された場合
  gphys = GPhys::NetCDF_IO.open(file[0].to_s, var)
 p  $title = File::basename(file[0].to_s, ".nc")
else 
  if ($OPT_c)          # コンポジット. 引数に与えられたファイルが領域分割されたファイルの場合, それらを仮想的に"くっつけて扱う."
    gphys = GPhys::NetCDF_IO.open(file, var)
  else
    gphys = mean_gphys(file, var)
  end
end


if ($OPT_range)
  range = ($OPT_range).to_s.split(/\s*,\s*/)
  range.each{|rg|
    ran = rg.to_s.split(/\s*=\s*/)
    if ran[1] =~ /\s*\.\.\s*/
      rg1=ran[1].split(/\s*\.\.\s*/)[0].to_f
      rg2=ran[1].split(/\s*\.\.\s*/)[1].to_f
      gphys = gphys.cut(ran[0] => rg1..rg2)
    else
      gphys = gphys.cut(ran[0] => ran[1].to_f)
    end
  }
end



$title = ($OPT_title) if ($OPT_title)

if ($OPT_mean)
  mean = ($OPT_mean).to_s.split(/\s*,\s*/)
  mean.each{|mn|
    gphys = gphys.mean(gphys.axnames.index(mn))
  }
end

if ($OPT_daytime)
# time-date composit only for ERA40

lonax = gphys.axis(0).pos.val
timeax = gphys.axis(1).pos.val
dateax = gphys.axis(2).pos.val
lnum = gphys.shape[0]
tnum = gphys.shape[1]
dnum = gphys.shape[2]

dataname = gphys.data.name
timeunits = 'days since 2001-1-1'

atna = NArray.float(tnum*dnum).fill!(0)

for i in 0..(dnum-1) 
  for j in 0..(tnum-1) 
    atna[tnum*i+j] = i+0.25*j
  end
end

atna = atna+dateax[0]
atx = VArray.new(atna).rename("date")

data = gphys.data.val
datana = NArray.sfloat(lnum,tnum*dnum).fill!(0.0)

for i in 0..(dnum-1) 
  for j in 0..(tnum-1) 
    datana[true,4*i+j] = data[true, j, i]
  end
end
datana

data = VArray.new(datana).rename(dataname)

atx = Axis.new(true).set_cell_guess_bounds(atx).set_pos_to_center

grid = Grid.new(gphys.axis(0),atx)

gphys = GPhys.new(grid, data)

gphys.coord(1).set_att('units',timeunits)

# end time-date
end



##########################################
#
#    描画オプションの初期値

  draw_opts = {                  # default
    "levels"     => $OPT_levels,   # false
    "patterns"   => $OPT_patterns, # false
    "line"       => $OPT_line,     # false
    "cont"       => !$OPT_nocont,  # true
    "tone"       => !$OPT_noshade, # true
    "colorbar"   => !$OPT_colorbar,# true
    "colorbar_opts"   => nil,# nil
    "annot"     => !$OPT_noannote,  # true
    "map"        => $OPT_map,       # false
    "exchange"        => $OPT_exch,       # false
    "min"        => $OPT_min,       # false
    "max"        => $OPT_max       # false
   }


##########################################
#
#    変数毎に設定読み込み

if  var == "t" then
  Draw::settei_T
elsif var == "u" || var == "uwnd" then 
  Draw::settei_U(draw_opts)
elsif var == "v" then 
  Draw::settei_V(draw_opts)
elsif var == "w" then 
  Draw::settei_W(draw_opts)
#elsif var == "cp" then
#  Draw::settei_CP
elsif var == "tsrc" then
  Draw::settei_tsrc(draw_opts)
elsif var == "ttr" then
  Draw::settei_ttr(draw_opts)
elsif var == "ttrc" then
  Draw::settei_ttrc(draw_opts)
#elsif var == "lsp" then  
#  Draw::settei_LSP
elsif var == "tp" || var == "lsp" || var == "cp" then  
  Draw::settei_TP(draw_opts)
elsif ($OPT_native) || ($OPT_n) then
  Draw::settei_native(draw_opts)
elsif var == "psi" then
  Draw::settei_strm(gphys)
elsif var == "theta" then
  Draw::settei_Theta
elsif var == "pbar" then
  Draw::settei_Available
elsif var == "pws" then
  Draw::settei_PWS(draw_opts)
else
  Draw::settei_default(draw_opts)
end

Draw::settei_default(draw_opts) if $OPT_default
Draw::settei_native(draw_opts)  if $OPT_native


Draw_opts_main = draw_opts.clone #Marshal.load(Marshal.dump(draw_opts)).clone

##########################################
#
#    実際に描画部分

DCL.swlset('lalt', true)   if ($OPT_bs) || ($OPT_smooth)
DCL.uscset('cyspos', 'B' ) if ($OPT_mvunit)

if ($OPT_animate) || ($OPT_smooth)
  DCL.swlset('lwait', false) if ($OPT_nowait) || ($OPT_smooth) # auto animation
  DCL::swlset('lsep',true) if ($OPT_f) || ($OPT_file)

  anirange = ($OPT_animate).to_s.split(/\s*,\s*/) if ($OPT_animate)
  anirange = ($OPT_smooth).to_s.split(/\s*,\s*/) if ($OPT_smooth)

  anirange.each{|rg|
    ran = rg.to_s.split(/\s*=\s*/)
    dimindex = gphys.dim_index(ran[0])
    if ($OPT_datime)
      if ran[1] =~ /\s*\.\.\s*/                    # 開始点と終点が指定された場合
	arg1 = ran[1].split(/\s*\.\.\s*/)[0].to_i      # 
	arg2 = ran[1].split(/\s*\.\.\s*/)[1].to_i      #
	val = gphys.coord(dimindex).val[ran[1].to_i]
	Draw::mkwin
	for time in 0..3
	  Draw::draw(gphys.cut(ran[0]=>val, "time"=>600*time), true, Draw_opts_main)
	end
	arg1+1.upto(arg2) do |i|
	  val = gphys.coord(dimindex).val[i]
	  for time in 0..3
	    val = gphys.coord(dimindex).val[i]
	    Draw::draw(gphys.cut(ran[0]=>val, "time"=>600*time), true, Draw_opts_main)
	  end
	end
      else                                         # 開始点のみ指定された場合
	val = gphys.coord(dimindex).val[ran[1].to_i]
	Draw::mkwin
	for time in 0..3
	  Draw::draw(gphys.cut(ran[0]=>val, "time"=>600*time), true, Draw_opts_main)
	end
	ept = gphys.coord(dimindex).shape_current[0].to_i
	(ran[1].to_i+1).upto(ept-1) { |i|
	  val = gphys.coord(dimindex).val[i]
	  for time in 0..3
	    Draw::draw(gphys.cut(ran[0]=>val, "time"=>600*time), true, Draw_opts_main)
	  end
	}
      end
    else
      if ran[1] =~ /\s*\.\.\s*/                    # 開始点と終点が指定された場合
	#      arg1=ran[1].split(/\s*\.\.\s*/)[0].to_f       # 軸の値(緯度経度で指定するばあいのルーチン)
	#      arg2=ran[1].split(/\s*\.\.\s*/)[1].to_f       #
	#      grid1 = gphys.coord(dimindex).val.index(arg1) #
	#      grid2 = gphys.coord(dimindex).val.index(arg2) #
	arg1 = ran[1].split(/\s*\.\.\s*/)[0].to_i      # 
	arg2 = ran[1].split(/\s*\.\.\s*/)[1].to_i      #
	val = gphys.coord(dimindex).val[ran[1].to_i]
	Draw::mkwin
	Draw::draw(gphys.cut(ran[0]=>val), true, Draw_opts_main)
	arg1+1.upto(arg2) do |i|
	  val = gphys.coord(dimindex).val[i]
	  Draw::draw(gphys.cut(ran[0]=>val), true, Draw_opts_main)
	end
      else                                         # 開始点のみ指定された場合
	val = gphys.coord(dimindex).val[ran[1].to_i]
	Draw::mkwin
	Draw::draw(gphys.cut(ran[0]=>val), true, Draw_opts_main)
	ept = gphys.coord(dimindex).shape_current[0].to_i
	(ran[1].to_i+1).upto(ept-1) { |i|
	  val = gphys.coord(dimindex).val[i]
	  Draw::draw(gphys.cut(ran[0]=>val), true, Draw_opts_main)
	}
      end
    end
    }
else
  Draw::mkwin
#  gphys = gphys.stspct_fft("rain")
  Draw::draw(gphys, true, Draw_opts_main) 
end

Draw::clwin

exit
