The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/local/perl5/bin/perl
=head1 NAME

GribView.pl - script to view Grib Record Headers

=head1 DESCRIPTION

GribView.pl currently allows the user to browse Grib record headers and 
view the fields as interpreted by the PDL::IO::Grib package.  Maybe one day 
it could also edit the headers, view the data, and allow exports to other formats.

=cut
use blib;
use strict;
use warnings;
use Tk;
use Tk::FileDialog;
use PDL::IO::Grib;

$PDL::IO::Grib::debug=1;

my $pgplot;
my $trid;
BEGIN{
  $ENV{PGPLOT_DEV}=$^O =~ /MSWin32/ ? '/GW' : "/XSERVE";
  $ENV{PGPLOT_DIR}="/usr/local/pgplot/" if(!defined($ENV{PGPLOT_DIR}) and -e "/usr/local/pgplot");
  eval "use PGPLOT";
  if($@ ne ""){
    print "Could not load PGPLOT\n";
  }else{
    eval "use PDL::Graphics::PGPLOT";
    if($@ ne ""){
      print "PDL::Graphics::PGPLOT not found \n";
    }else{
      $pgplot=1;
    }
  }
  eval "use PDL::Graphics::TriD";
  if($@ ne ""){
    print "Could not load TriD\n";
  }else{
	 $trid=1;
  } 
}

my $TkObjects;
my $GribFile;
my $Field;
my $section;
my $debug;

$TkObjects->{MainWindow} = MainWindow->new( );

$TkObjects->{TLFrame1} = $TkObjects->{MainWindow}->Frame()->pack(-side=>'top',-fill=>'x');
$TkObjects->{TLFrame2} = $TkObjects->{MainWindow}->Frame(-width=>100,
																			-height=>300,
																		  -label=>'Field Identifiers')->pack(-side=>'left',-fill=>'both');
$TkObjects->{TLFrame3} = $TkObjects->{MainWindow}->Frame()->pack(-side=>'top',-fill=>'both');
$TkObjects->{TLFrame4} = $TkObjects->{MainWindow}->Frame()->pack(-side=>'top',-fill=>'both');


$TkObjects->{PDSFrame} = $TkObjects->{TLFrame3}->Frame(-label=>'PDS Section'
																		  )->pack(-side=>'left',
																					 -fill=>'both');
$TkObjects->{GDSFrame} = $TkObjects->{TLFrame3}->Frame(
																			-label=>'GDS Section'
																		  )->pack(-side=>'left',
																					 -fill=>'both');
$TkObjects->{BMSFrame} = $TkObjects->{TLFrame4}->Frame(-label=>'BMS Section'
																		  )->pack(-side=>'left',
																					 -fill=>'both');
$TkObjects->{BDSFrame} = $TkObjects->{TLFrame4}->Frame(
																			-label=>'BDS Section'
																		  )->pack(-side=>'left',
																					 -fill=>'both');

use constant PDS_DEFAULTS => {1=> {name=> 'PDS Length',
											  type=> 'uint3'},
										4=> {name=> 'Parameter Table Version'},
										5=> {name=> 'Center ID'},
										6=> {name=> 'Generating Process ID'},
										7=> {name=> 'Grid ID'},
										8=> {name=> 'GDS/BMS Flag',
											  type=>'bits'},
										9=> {name=> 'Parameter and units ID'},
										10=> {name=> 'Type of level or layer'},
										11=> {name=> 'Level top'},
										12=> {name=> 'Level bottom'},
										13=> {name=> 'Year'},
										14=> {name=> 'Month'},
										15=> {name=> 'Day'},
										16=> {name=> 'Hour'},
										17=> {name=> 'Minute'},
										18=> {name=> 'Forecast time unit'},
										19=> {name=> 'Forecast Time'},
										20=> {name=> 'Time Step'},
										21=> {name=> 'Time Range'},
										22=> {name=> 'Number in Average',
												type=> 'uint2'},
										24=> {name=> 'Number missing from Average'},
										25=> {name=> 'Century of initial time'},
										26=> {name=> 'Sub-center ID'},
										27=> {name=> 'Decimal Scale Factor',
												type=> 'int2'}};



use constant GDS_DEFAULTS => {1=> {name=>'GDS Length',
											  type=>'uint3'},
										4=> {name=>'Number of vertical coordinate parameters'},
										5=> {name=>'Location of vertical coordinate parameters'}, 	  
										6=> {name=>'Data representation type'},
										7=> {name=>'Ni Number of points on a Latitude',
											  type=>'uint2'},
										9=>  {name=>'Nj Number of points on a Longitude',
												type => 'uint2'},
										11=> {name=>'latitude of first point',
												type => 'int3'},
										14=> {name=>'longitude of first point',
												type => 'int3'},
										17=> {name=>'Resolution and component flag',
												type=>'bits'},
										18=> {name=>'latitude of last gridpoint',
												type => 'int3'},
										21=> {name=>'longitude of last gridpoint',
												type => 'int3'},
										24=> {name=>'Longitude increment',
												type => 'int2'},
										26=> {name=>'Latitude direction increment',
												type => 'int2'},
										28=> {name=>'Scanning mode flags',
												type => 'bits'}};

use constant BMS_DEFAULTS => {1=> {name=>'BMS Length',
											  type=>'uint3'},
										4=> {name=>'unused bit count'},
										5=> {name=>'bms usage flag',
											  type=>'uint2'}};


use constant BDS_DEFAULTS => {1=> {name=>'BDS Length',
											  type=>'uint3'},
										4=> {name=>'flags',
											  type=>'bits'},
										5=> {name=>'binary scale factor',
											  type=>'int2'},
										7=> {name=>'reference value',
											  type=>'float'},
										11=>{name=>'bits per value'}};

my $types={PDS=>PDS_DEFAULTS,
			  GDS=>GDS_DEFAULTS,
			  BMS=>BMS_DEFAULTS,
			  BDS=>BDS_DEFAULTS};

						  

$TkObjects->{Open} =  $TkObjects->{TLFrame1}->Menubutton(-text=>'FILE ',
							 -relief=>'raised',
							 -menuitems => [[ command => "Open",
									  -command=>[\&opengrib]],
									[ command => "Exit",-command=> sub {exit}]]);
$TkObjects->{Open}->pack(-side=>'left',-anchor=>'nw',-fill=>'y');
if($pgplot or $trid){
  $TkObjects->{View} =  $TkObjects->{TLFrame1}->Menubutton(-text=>'VIEW DATA',
																			-relief=>'raised')->pack(-side=>'left',-anchor=>'nw',-fill=>'y');
  if($pgplot){																									
	 $TkObjects->{View}->command(-label=>'PGPLOT Contour', -command=>[\&pgplot_contour]);
  }
  if($trid){
	 $TkObjects->{View}->command(-label=>'TriD Contour', -command=>[\&trid_contour]);
  }
#  $TkObjects->{View_mb}->pack(-side=>'left',-anchor=>'nw',-fill=>'y');
}


$TkObjects->{ListBox} =  $TkObjects->{TLFrame2}->Scrolled("Listbox",
																			 -scrollbars=>"oe",
																			 -height=>22,
																			 -width=>26);
$TkObjects->{ListBox}->pack(-fill=>'both');

$TkObjects->{ListBox}->bind('<Button-1>', 
									 [ \&FieldSelect, Ev('y') ]);

foreach my $section (qw(PDS GDS BMS BDS)){
  
  $TkObjects->{$section."Scroll"} = $TkObjects->{$section."Frame"}->Scrollbar();
  $TkObjects->{$section."Listboxes"} = [ $TkObjects->{$section."Frame"}->Listbox(-width=>30),
													  $TkObjects->{$section."Frame"}->Listbox() ];
  
  foreach my $list (@{$TkObjects->{$section."Listboxes"}}){
	 $list->configure(-yscrollcommand => [ \&scroll_listboxes, 
														$TkObjects->{$section."Scroll"},
														$list, $TkObjects->{$section."Listboxes"}]);
  }
  $TkObjects->{$section."Scroll"}->configure(-command=> sub { 
															  foreach my $list (@{$TkObjects->{$section."Listboxes"}}){
																 $list->yview(@_);}});

  foreach my $list (@{$TkObjects->{$section."Listboxes"}}){
	 $list->pack(-side=>'left'); 
  }
  $TkObjects->{$section."Scroll"}->pack(-side=>'left',-fill=>'y');

}


MainLoop;



sub opengrib {
  my $path = '.';
  
  my($LoadDialog) = $TkObjects->{MainWindow}->FileDialog(-Title =>'Choose an input file',
												-Create => 0);

  my $file = $LoadDialog->Show(-Path=>$path);
  return unless(defined $file);

  
  print "HERE $path $file\n" if($debug);

  if($file =~ /([^\/]+).gz$/){
	 my $nfile="/tmp/$1";
	 system("gzip -cd $file > $nfile");
	 if(-e $nfile){
		$file = $nfile;
	 }else{
		print "Unable to decompress $file at ",__FILE__," ",__LINE__,"\n";
		return;
	 }
  }

    
  $GribFile = new PDL::IO::Grib($file);

  $TkObjects->{MainWindow}->title("GribView: $file");  
  
  
  $TkObjects->{ListBox}->delete(0,"end");
  foreach(sort idsort keys %$GribFile){
	 next if(/^_/);
    my $name = $GribFile->{$_}->name();
	 if(defined $name){
		$TkObjects->{ListBox}->insert("end",sprintf("%-16.16s %s",$_,$name));
	 }else{
		$TkObjects->{ListBox}->insert("end",$_);
	 }
  }

}

sub FieldSelect{
  my($lb,$y) = @_;

  my $val = $lb->get($lb->nearest($y));
  $val =~ s/\s+.*$//;

  $Field=$GribFile->{$val};

  foreach (qw(PDS GDS BMS BDS)){
#  foreach (qw(PDS )){
	 $section = $_;
	 ShowSection();
  }
  my $data = $Field->read_data($GribFile->{_FILEHANDLE});
  print join(' ',$data->stats),"\n";

}

sub ShowSection{
  
  my $f = $Field;
  return unless defined $f->{$section};
  
  my $wcnt = $f->{$section}->nelem;

  my $shown=0;

  $types->{GDS} = lookup_gds_types($f->gds_attribute(4),
											  $f->gds_attribute(5),
											  $f->gds_attribute(6)) if($section eq 'GDS');

  foreach(@ { $TkObjects->{$section."Listboxes"}}){
	 # empty before refilling
	 $_->delete(0,'end');
  }

  foreach my $num (1..$wcnt){
	 my $val;

    my $dtype = $types->{$section}{$num}{type};

 	 if($shown-- <= 0){
		if(defined $dtype){
		  my($o,$l);
		  $o=$num-1;
		  if($dtype eq 'uint3'){
			 $l=$o+2;
			 $val = $f->{$section}->slice("$o:$l")->unpackint3();
			 $shown=2;
		  }elsif($dtype eq 'int3'){
			 $l=$o+2;
			 $val = $f->{$section}->slice("$o:$l")->unpackint3('signed');
			 $shown=2;
		  }elsif($dtype eq 'uint2'){
			 $l=$o+1;
			 $val = $f->{$section}->slice("$o:$l")->unpackint2();
			 $shown=1;
		  }elsif($dtype eq 'int2'){
			 $l=$o+1;
			 $val = $f->{$section}->slice("$o:$l")->unpackint2('signed');
			 $shown=1;
		  }elsif($dtype eq 'float'){
			 $l=$o+3;
			 $val = sprintf("%f",PDL::IO::Grib::Wgrib::decode_ref_val($f->{$section}->slice("$o:$l")));
			 $shown=3;
		  }elsif($dtype eq 'char'){
			 $val = unpack "C",$f->{$section}->slice("($o)");
		  }elsif($dtype eq 'bits'){
			 $val = '';
			 my $tval = $f->{$section}->slice("($o)");
			 my @twos = (128,64,32,16,8,4,2,1);
          for(my $i=0;$i<8;$i++){
				if($tval & $twos[$i]){
				  $val .= '1';
				}else{
				  $val .= '0';
				}
			 }
		  }
		}else{
		  $val = $f->{$section}->at($num-1);
		}
	 }
    next unless (defined $val);
	 my $name = "$num ";
	 if(defined $types->{$section}{$num}{name}){
		$name .= $types->{$section}{$num}{name};
	 }

	 $TkObjects->{$section."Listboxes"}[0]->insert('end',$name);
	 $TkObjects->{$section."Listboxes"}[1]->insert('end',$val);

  }
  

}
			
	
sub idsort{

  my(@a) = split(/:/,$a);
  my(@b) = split(/:/,$b);

  $#a<=1
	 or
  $#b<=1
	 or
  $a[0] <=> $b[0]
    or
  $a[1] <=> $b[1]
    or
  $a[2] <=> $b[2]
    or
  $a[3] <=> $b[3]
    or
  $a[4] <=> $b[4]
    or
  $a cmp $b;
}

sub scroll_listboxes {
  my ($sb,$scrolled,$lbs,@args) = @_;
  $sb->set(@args);
  my($top,$bottom) = $scrolled->yview();
  foreach my $list (@$lbs){
	 $list->yviewMoveto($top);
  }
}

sub lookup_gds_types{
  my($gds4, $gds5, $gds6) = @_;
  
  my $types;

  $types = GDS_DEFAULTS;

  if($gds6 == 5){
    for(7..27){
		undef $types->{$_};
	 }
	 $types->{7}{name}='NX ';
	 $types->{7}{type}='uint2';
	 $types->{9}{name}='NY ';
	 $types->{9}{type}='uint2';
	 $types->{11}{name}='Lat of first grid point';
	 $types->{11}{type}='int3';
	 $types->{14}{name}='Lon of first grid point';
	 $types->{14}{type}='int3';
	 $types->{17}{name}='Resolution and componet flags';
	 $types->{17}{type}='bits';
	 $types->{18}{name}='grid orientation';
	 $types->{18}{type}='int3';
	 $types->{21}{name}='X-direction grid length';
	 $types->{21}{type}='int3';
	 $types->{23}{name}='Y-direction grid length';
	 $types->{23}{type}='int3';
	 $types->{27}{name}='Projection Center flag';
	 $types->{27}{type}='bits';
	 $types->{28}{name}='Scanning Mode';
	 $types->{28}{type}='bits';
  }elsif($gds6 == 192){
    for(7..27){
		undef $types->{$_};
	 }
	 $types->{11}{type}='int3';
	 $types->{11}{name}='ND: number of diamonds';
	 $types->{14}{type}='int3';
	 $types->{14}{name}='NI: intervals on a main side';
	 $types->{17}{name}='Resolution and componet flags';
	 $types->{17}{type}='bits';
	 $types->{18}{name}='LAPP: Location of Isocondrahedral';
	 $types->{18}{type}='int3';
	 $types->{21}{name}='LOPP: pole point';
	 $types->{21}{type}='int3';
	 $types->{24}{name}='LAMPL';
	 $types->{24}{type}='int3';

	 $types->{28}{name}='Scanning Mode';
	 $types->{28}{type}='bits';
  }	 

  for(my $i=0; $i<=$gds4; $i++){
	 $types->{4*$i+$gds5}{name}="vertical parm $i";
	 $types->{4*$i+$gds5}{type}='float';
  }
  return $types;
}

sub pgplot_contour{
  my(@args) = @_;

  return unless defined $Field;

  my $data = $Field->read_data($GribFile->{_FILEHANDLE});
  
  cont $data;
  
}

sub trid_contour{
  my(@args) = @_;

  return unless defined $Field;

  my $data = $Field->read_data($GribFile->{_FILEHANDLE});
  
  
  my @stats = $data->stats;
  print $Field->name," @stats\n";
 
  
  PDL::Graphics::TriD::contour3d($data,undef,undef, {Labels=>[]});
  
}