[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
ncdigest V1 #724 (fwd)
- Subject: ncdigest V1 #724 (fwd)
- Date: Sat, 27 Sep 2003 10:40:54 -0600 (MDT)
===============================================================================
Robb Kambic Unidata Program Center
Software Engineer III Univ. Corp for Atmospheric Research
address@hidden WWW: http://www.unidata.ucar.edu/
===============================================================================
---------- Forwarded message ----------
Date: Fri, 26 Sep 2003 21:25:00 -0600 (MDT)
From: ncdigest <address@hidden>
To: address@hidden
Subject: ncdigest V1 #724
ncdigest Friday, September 26 2003 Volume 01 : Number 724
Today's Topics:
Re: perl interface problem, plus ncdump oddity
----------------------------------------------------------------------
Date: Fri, 26 Sep 2003 15:54:55 -0400
From: Jason Thaxter <address@hidden>
Subject: Re: perl interface problem, plus ncdump oddity
- --SLDf9lqlvOQaIe6s
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
On Tue, Sep 16, 2003 at 11:20:13AM -0400, Jason Thaxter wrote:
> I've found two odd problems with NetCDF tools recently. I suspect they are
> related to compiler and perl versions, but I don't really know.
>
> 1) ----------------------------------------------------
>
> The Perl interface issue involves values retrieved by NetCDF::recget. The
> variable is a scalar of type BYTE. Perl always thinks it has a zero (0), even
I figured out this particular problem; the perl-netcdf library does not appear
to be misbehaving at all, though it does things that might trap an unwary -
and at least in this case, fairly wary - perl programmer.
Variables of the NetCDF::BYTE type cannot be immediately used as perl
variables. They will end up looking like "^A" or "^@" or something else when
you try to use them as strings; worse, if you look at them as numbers, they
will always appear to be zero.
Suppose that $value is an actual single value retrieved from
attget/varget/recget. To use a NetCDF::BYTE properly, do this
$value = unpack('C',$value);
I put this trick into a wrapper module I've written around NetCDF, called
GoMOOS::NetCDF. There are a number of other tricks there, too - initializing
arrays before passing them to NetCDF functions, retrieving variables by names
instead of index number, etc. I wouldn't say it's complete or flawless; it's
a little inconsistent about return values, and it's probably more useful for
taking a record-oriented view than a variable-oriented view, and it's totally
useless for writing NetCDF files.
Nevertheless, anyone using perl-netcdf may find it useful and so I've
attached it to this message. If the attachment doesn't make it through
majordomo onto the list, feel free to e-mail me for a copy.
Thanks,
Jason
- --
- ----------------------------------------------
Jason Thaxter
GoMOOS, P.O. Box 4919, Portland, ME 04112-4919
Office Location: 1 Canal Plaza, 7th Floor
Office: 207.773.0423
Fax: 207.773.8672
Email: address@hidden
- ------------www.gomoos.org--------------------
- --SLDf9lqlvOQaIe6s
Content-Type: application/x-perl
Content-Disposition: attachment; filename="NetCDF.pm"
Content-Transfer-Encoding: quoted-printable
package GoMOOS::NetCDF;=0A=0A=3Dhead1 NAME=0A=0AGoMOOS::NetCDF - a library =
of routines for handling NetCDF files=0A=0A=3Dhead1 SYNOPSIS=0A=0AWraps bas=
ic NetCDF functions for use raw by scripts, or by other classes=0A(e.g. GoM=
OOS::Buoys::NetCDF). Note that many of the functions in this library=0Amay=
have a tendency to die suddenly. We try to check them, and we mark them=
=0Aif possible.=0A=0ANote that this is primarily a record-oriented library.=
=0A=0A=3Dcut=0Amy $version =3D '$Revision: 1.29 $';=0A=0Ause strict;=0Ause =
Carp 'cluck';=0A=0Ause NetCDF;=0A=0Amy @types;=0A$types[NetCDF::FLOAT] =3D =
'float';=0A$types[NetCDF::DOUBLE] =3D 'double';=0A$types[NetCDF::LONG] =3D =
'long';=0A$types[NetCDF::SHORT] =3D 'short';=0A$types[NetCDF::BYTE] =3D 'by=
te';=0A$types[NetCDF::CHAR] =3D 'char';=0Amy $ncGLOBAL =3D NetCDF::GLOBAL;=
=0A=0Amy $_MODE_RO =3D NetCDF::NOWRITE;=0A=0Asub new {=0A my $proto =3D
shi=
ft;=0A my $class =3D ref($proto) || $proto;=0A my $self =3D {};=0A=0A if (=
my $file =3D shift){=0A $self->{_FILE} =3D $file;=0A }=0A=0A
bless($self,$=
class);=0A return $self;=0A}=0A=0Asub open {=0A my ($self,$file) =3D
@_;=0A=
=0A # may pass argument to open=0A if ($file){ $self->{_FILE} =3D $file; }=
=0A=0A unless(-e $self->{_FILE}){=0A cluck "No such file: " .
$self->{_FIL=
E};=0A return; # NetCDF::open will terminate!! if file can't be
opened=0A =
}=0A=0A # TODO: check to see if it looks like a netcdf file...=0A # it
shou=
ld be a binary file:=0A # file tmp/test.nc =3D tmp/test.nc: data=0A
# =
and the first three chars are 'CDF'=0A=0A # this next line will crash on
fa=
il, can't eval!!=0A my $ncid =3D NetCDF::open($self->{_FILE},$_MODE_RO); # =
read-only mode should be default=0A $self->{_NCID} =3D $ncid;=0A=0A # Store=
basic info=0A my ($ndims,$nvars,$ngatts,$recdim);=0A NetCDF::inquire($ncid=
,$ndims,$nvars,$ngatts,$recdim);=0A #warn "ndims=3D$ndims,nvars=3D$nvars,na=
tt=3D$ngatts,recdim=3D$recdim\n";=0A $self->{_NDIMS} =3D $ndims; #=0A
$se=
lf->{_NVARS} =3D $nvars;=0A $self->{_NGATTS} =3D $ngatts;=0A
$self->{_RECD=
IM} =3D $recdim;=0A=0A my ($numrecs,$dim);=0A=0A # STORE OUR DIMENSION
SIZ=
ES=0A for (my $i=3D0;$i<$ndims;$i++){=0A my ($dname,$dsize);=0A
if (NetCD=
F::diminq($ncid,$i,\$dname,\$dsize) =3D=3D 0){=0A # STORE
FOR REFERENCE=
=0A $self->{_DIM}{$dname} =3D $dsize;=0A
# STORE OUR RECORD DIMENSION: =
TELLS HOW MANY RECORDS WE HAVE=0A if ($i =3D=3D
$recdim){=0A $numrecs =
=3D $dsize;=0A $self->{_NUMRECS} =3D $numrecs;=0A
}=0A }=0A else {=
=0A cluck "NetCDF::diminq returned non-zero";=0A
}=0A }=0A # TODO: this =
could cause other problems... maybe we should yell=0A return $self->{_NCID}=
unless ($self->{_NUMRECS});=0A=0A # CREATE LIST OF ALL VARIABLES=0A
my @al=
l_vars =3D ();=0A for my $i (0 ... ($self->{_NVARS} - 1)){=0A
my ($var,$ty=
pe,$ndims,@dimids,$natts);=0A
NetCDF::varinq($ncid,$i,\$var,\$type,\$ndims=
,\@dimids,\$natts);=0A push @all_vars,$var;=0A # also remember
the type: =
used by get_rec_val=0A $self->{_VAR_TYPES}{$var} =3D $type;=0A }=0A
$self-=
>{_ALLVARS} =3D \@all_vars;=0A=0A # CREATE LIST OF RECORD VARIABLES=0A
># cu=
rrently only works if =0A if
(NetCDF::diminq($ncid,$self->{_RECDIM},\$dim,\=
$numrecs) =3D=3D 0){=0A # Get record zero and store var names=0A
my @recv=
arids =3D ();=0A my @recsizes =3D ();=0A my
$nrecvars;=0A NetCDF::recinq=
($self->{_NCID}, $nrecvars, \@recvarids, \@recsizes) =3D=3D 0 ||=0A
cluck=
"Couldn't inquire about record variables\n";=0A=0A my @varnames
=3D ();=
=0A if ($numrecs > 0){=0A my @record =3D ();=0A
NetCDF::recget($self->=
{_NCID}, 0, \@record) =3D=3D 0 || cluck "Couldn't get record zero\n";=0A
=
# So we can immediately call get_next_rec()=0A
$self->{_CURRENT_RECORD_NU=
M} =3D -1;=0A for my $k (0 ... $#record){=0A
my $varid =3D $recvarids[=
$k];=0A my ($var,$type,$ndims,@dimids,$natts);=0A
NetCDF::varinq($nci=
d,$varid,\$var,\$type,\$ndims,\@dimids,\$natts);=0A
push @varnames, $var=
;=0A }=0A }=0A $self->{_RECVARS} =3D
\@varnames;=0A }=0A=0A=0A return =
$self->{_NCID}; # return 'true' (!=3D0) value if succeeded=0A}=0A=0Asub clo=
se {=0A my $self =3D shift;=0A=0A unless($self->{_NCID}){=0A
cluck "don't =
know how we can close NetCDF with NCID=3D" . $self->{_NCID};=0A
return;=0A=
}=0A=0A my $rs =3D NetCDF::close($self->{_NCID});=0A=0A return
!$rs;=0A}=
=0A=0Asub numrecs { # return number of records=0A my $self =3D shift;=0A
r=
eturn $self->{_NUMRECS};=0A}=0A=0A=3Dhead2 get_rec_vars=0A=0AGet the list o=
f record variables for this file.=0A=0A=3Dcut=0Asub get_rec_vars {=0A my $s=
elf =3D shift;=0A my @vars =3D @{$self->{_RECVARS}};=0A=0A return
@vars;=0A=
}=0A=0A=3Dhead2 get_all_vars=0A=0AGet the list of all variables for this fi=
le.=0A=0A=3Dcut=0A=0Asub get_all_vars {=0A my $self =3D shift;=0A my
@vars =
=3D @{$self->{_ALLVARS}};=0A=0A return @vars;=0A}=0A=0A=0A=3Dhead2 get_gatt=
=0A=0AGet the values of a global attribute and return as a list. This is u=
seful=0Awhen you just want to extract the value of a given attribute. NOTE=
: it returns=0Aa list, even if there is only one value.=0A=0A=3Dcut=0Asub g=
et_gatt {=0A my ($self,$attr) =3D @_;=0A my ($type,$len);=0A=0A # First
get=
the type of the global attribute so we know how to handle the result=0A
my=
$ret =3D NetCDF::attinq($self->{_NCID}, NetCDF::GLOBAL, $attr, $type, $len=
);=0A=0A my @values =3D (); # NetCDF::attget will core if this is an
undef=
=0A NetCDF::attget($self->{_NCID},NetCDF::GLOBAL,$attr,\@values);=0A
@value=
s =3D perlify_values($type,\@values);=0A=0A #print "gvalues(" . scalar @val=
ues . ")=3D(" . join(",",@values) . ")\n";=0A return @values;=0A}=0A=0A=3Dh=
ead2 get_all_gatts=0A=0AGet all global attributes and return as a hash.=0A=
=0ASample:=0A my %gatts =3D $nc->get_all_gatts();=0A=0A foreach my
$gatt ( =
sort keys %gatts){=0A my @vals =3D @{$gatts{$gatt}};=0A
print "global att=
$gatt =3D " . join(",",@vals) . "\n";=0A }=0A=0A=3Dcut=0Asub
get_all_gatts=
{=0A my $self =3D shift;=0A=0A my %gatts =3D ();=0A=0A for my $i (0
... ($=
self->{_NGATTS} - 1)){=0A=0A my ($name,$value,$type,$len);=0A
NetCDF::att=
name($self->{_NCID}, NetCDF::GLOBAL, $i, \$name);=0A=0A # First get the
ty=
pe of the global attribute so we know how to present=0A # the result=0A
m=
y $ret =3D NetCDF::attinq($self->{_NCID}, NetCDF::GLOBAL, $name, $type, $le=
n);=0A=0A my @values =3D (); # NetCDF::attget will core if this
is an unde=
f=0A
NetCDF::attget($self->{_NCID},NetCDF::GLOBAL,$name,\@values);=0A
@va=
lues =3D perlify_values($type,\@values);=0A $gatts{$name} =3D
\@values;=0A=
}=0A=0A return %gatts;=0A}=0A=0A=3Dhead2 get_var_att=0A=0APass a name
of a=
variable and the name of its attribute, and get back the value=0Aof the at=
tribute. This also checks to see if there is such an attribute, since=0Ath=
e charming NetCDF library will DIE if you try to look at a non-existent=0Aa=
ttribute. NOTE: it returns a list, even if there is only one value.=0A=0A=
=3Dcut=0Asub get_var_att {=0A=0A my ($self,$var,$attr) =3D @_;=0A
my @value=
s =3D (); # NetCDF::attget will core if this is an undef=0A=0A # GET VARIAB=
LE ID=0A # which will kill you dead unless you have a valid variable=0A
if =
( !( grep { $_ eq $var; } @{$self->{_ALLVARS}}) ){=0A return
undef;=0A }=
=0A my $varid =3D NetCDF::varid($self->{_NCID}, $var);=0A=0A # Now
get the =
number of attributes it has=0A my ($var,$vtype,$ndims,@dimids,$natts);=0A
N=
etCDF::varinq($self->{_NCID},$varid,\$var,\$vtype,\$ndims,\@dimids,\$natts)=
;=0A #warn "varinq =3D $var,$vtype,$ndims,(" . join(",",@dimids) . "),$natt=
s" if $GoMOOS::DEBUG;=0A=0A # Find the correct attribute id... you'll see w=
hy in a minute=0A my $attid =3D -1;=0A for my $j (0 ... ($natts -
1)){=0A =
my $name;=0A NetCDF::attname($self->{_NCID}, $varid, $j, \$name);=0A
if (=
$name eq $attr){=0A $attid =3D $j;=0A
#warn "$j:name =3D $name\n";=0A =
last;=0A }=0A }=0A # Return an empty list if there
is no such attribute.=
=0A if ($attid =3D=3D -1){ return (); }=0A #warn "attid =3D $attr\n" if
$Go=
MOOS::DEBUG;=0A=0A # Now go after the attribute itself.=0A my
($type,$len);=
=0A # This one dies with an untrappable error if $attr is bogus... (eval ju=
st dies)=0A # That's what all the above work is for!=0A
NetCDF::attinq($sel=
f->{_NCID}, $varid, $attr, $type, $len);=0A # Now get the actual values=0A
=
NetCDF::attget($self->{_NCID},$varid,$attr,\@values);=0A @values =3D
perlif=
y_values($type,\@values);=0A=0A # Return single value as scalar for conveni=
ence=0A if (@values =3D=3D 1){=0A my $retval =3D $values[0];=0A
return $r=
etval;=0A }=0A=0A #warn "attr values(" . scalar @values . ")=3D" .
join(","=
,@values) if $GoMOOS::DEBUG;=0A return @values;=0A}=0A=0A=3Dhead2 get_rec=
=0A=0APassed a record number: 0 to numrecs - 1 get the record and store it =
in _CURRENT_RECORD_NUM.=0ASee get_rec_val().=0A=0A=3Dcut=0A=0A=0Asub get_re=
c {=0A my ($self,$recnum, %opts) =3D @_;=0A=0A if( $self->{_NUMRECS} =3D=3D=
0){=0A cluck "NO RECORDS";=0A return 0;=0A }=0A=0A if
($recnum < 0 || $r=
ecnum >=3D $self->{_NUMRECS}){=0A if
(defined($self->{_NCID})){=0A unles=
s( $opts{INTERNAL}){=0A cluck "Invalid record number:
$recnum";=0A }=
=0A cluck "Invalid record number: $recnum\n";=0A
$self->{_CURRENT_RECOR=
D_NUM} =3D -1;=0A }=0A else {=0A
#warn "No NCID: file not open!! (recnu=
m =3D $recnum)\n";=0A cluck "No NCID: file not open!! (recnum
=3D $recnum=
)\n";=0A $self->{_CURRENT_RECORD_NUM} =3D -1;=0A
}=0A return 0;=0A }=0A=
=0A # GET RECORD=0A my @current_record =3D ();=0A my $status =3D
NetCDF::re=
cget($self->{_NCID}, $recnum, \@current_record);=0A if($status){=0A
cluck =
"Could not get record $recnum\n";=0A $self->{_CURRENT_RECORD_NUM}
=3D -1;=
=0A return 0;=0A }=0A=0A $self->{_CURRENT_RECORD_NUM} =3D
$recnum;=0A $sel=
f->{_CURRENT_RECORD} =3D \@current_record;=0A=0A return
1;=0A}=0A=0A=3Dhead=
2 get_rec_val=0A=0AReturn the values from current record returned by the la=
st call to get_rec().=0APass a variable name and get a scalar if the variab=
le reference is a scalar or=0Aif it is an array with only one value, also r=
eturn a scalar. If it is a reference to an=0Aarray just return the array re=
ference.=0A Sample:=0A my $sclr =3D $nc->get_rec_val('temperature_qc');=0A =
# time is an array but with only one value.=0A my $sclr_time =3D $nc->get_r=
ec_val('time');=0A Note: @arry =3D $nc->get_rec_val('time') will also work,=
arry will have one value.=0A my @arry =3D @{$nc->get_rec_val('current_u')}=
;=0A=0A=3Dcut=0A=0Asub get_rec_val {=0A my ($self,$var) =3D @_;=0A my
@recv=
ars =3D @{$self->{_RECVARS}};=0A my @current_record =3D
@{$self->{_CURRENT_=
RECORD}};=0A my $type =3D $self->{_VAR_TYPES}{$var};=0A die "WHAT? NO
RECOR=
D VARIABLES in $self->{_FILE}: (@recvars)" unless @recvars;=0A=0A my
$retva=
l =3D undef;=0A for my $k (0 ... $#recvars){=0A if ($recvars[$k] eq
$var){=
=0A $retval =3D
perlify_values($type,$current_record[$k]);=0A last;=0A
=
}=0A }=0A #if (!defined($retval)){ cluck "did not find $var\n";
}=0A=0A re=
turn $retval;=0A}=0A=0Asub get_next_rec {=0A my $self =3D shift;=0A=0A
if( =
$self->{_NUMRECS} =3D=3D 0){ return 0; }=0A=0A $self->{_CURRENT_RECORD_NUM}=
++;=0A=0A if ($self->{_CURRENT_RECORD_NUM} >=3D $self->{_NUMRECS}){
return =
0; }=0A=0A return $self->get_rec($self->{_CURRENT_RECORD_NUM},
INTERNAL=3D>=
1);=0A}=0A=0Asub get_prev_rec { =0A my $self =3D shift;=0A=0A if(
$self->{_=
NUMRECS} =3D=3D 0){ return 0; }=0A=0A $self->{_CURRENT_RECORD_NUM}--;=0A=0A=
if ($self->{_CURRENT_RECORD_NUM} < 0){ return 0; }=0A=0A return
$self->get=
_rec($self->{_CURRENT_RECORD_NUM}, INTERNAL=3D>1);=0A}=0A=0Asub get_rewind =
{ # reset get next record pointer=0A my ($self,%opts) =3D @_;=0A=0A if (def=
ined($opts{REVERSE})){=0A $self->{_CURRENT_RECORD_NUM} =3D
$self->{_NUMREC=
S};=0A }=0A else {=0A $self->{_CURRENT_RECORD_NUM} =3D 0;=0A
}=0A=0A retur=
n;=0A=0A}=0A=0A=3Dhead2 get_var ($var,[START=3D>$start,END=3D>$end,COUNT=3D=
>])=0A=0AReturn value(s) corresponding to a NetCDF variable. This accesses=
variables in=0Aan array format, as opposed to the record format.=0A=0AThe =
parameters are: START, which tells which value (record) to start at (0 is t=
he=0Afirst); TO, which gives the index of the last value to return; and COU=
NT, which=0Atells a number of records to return. If none are specified, th=
e first record is=0Areturned. If both COUNT and TO are specified, COUNT ta=
kes precedence. If TO=0Ais -1, then values up to the last one are returned=
.=0A=0AIf this is called in a scalar context, then the first value is retur=
ned.=0AOtherwise, an array is returned. Caller beware.=0A=0A$start and $co=
unt are naturally an offset and a number; they default to 0 and 1,=0Awhich =
gives the first value only, which is useful when there is only one, which=
=0Ais the only time you wouldn't want to speficy it anyway. If $count is -=
1 then it=0Aretrieves the entire array.=0A=0A=3Dcut=0Asub get_var {=0A my (=
$self,$var,%range) =3D @_;=0A my @values =3D (); # NetCDF::varget might cor=
e if this is an undef=0A=0A # careful... NetCDF::varget will CORE if start =
or count are undef...=0A my ($start,$count);=0A $start =3D 0 if not
defined=
($range{START});=0A if ($range{COUNT}){=0A $count =3D
$range{COUNT};=0A }=
=0A elsif ($range{TO}){=0A $count =3D $range{TO} -
$range{START};=0A }=0A =
$count =3D 1 if not $count;=0A=0A # GET VARIABLE ID=0A # which will
kill yo=
u dead unless you have a valid variable, so we check that=0A # it's valid b=
efore we do anything with it=0A if ( !( grep { $_ eq $var; } @{$self->{_ALL=
VARS}}) ){=0A return undef;=0A }=0A my $varid =3D
NetCDF::varid($self->{_N=
CID}, $var);=0A=0A # GET NUMBER OF RECORDS IF POSSIBLE AND ADJUST COUNT IF
=
NECESSARY=0A if ($self->{_DIM}{$var}){=0A if ($self->{_DIM}{$var}
< ($star=
t + $count) or=0A ($range{TO} and not
$range{COUNT})){=0A $count =3D $=
self->{_DIM}{$var} - $start;=0A }=0A }=0A=0A # NOW CHECK START,
COUNT, ETC=
.=0A # NOW GET THE VALUES=0A NetCDF::varget($self->{_NCID},$varid,\$start,\=
$count,\@values);=0A # And don't forget to perlify them=0A @values =3D
perl=
ify_values($self->{_VAR_TYPES}{$var},\@values);=0A=0A # Return single value=
as scalar for convenience=0A if (not wantarray){=0A my $retval =3D
$value=
s[0];=0A # see get_rec_val: here it could also be a string, so
we vary the=
test...=0A if (not $retval =3D~ /\w/ and $retval =3D=3D 0){ return
0; }=
=0A return $retval;=0A }=0A=0A return
@values;=0A}=0A=0A=3Dhead2 perlify_v=
alues=0A=0AThe purpose of this function is to prettify the values extracted=
from NetCDF=0Afiles into something more like what a Perl programmer expect=
s - strings or=0Anumbers, not arrays of numbers representing char values or=
bytes that don't look=0Alike numbers.=0A=0AThis expects two arguments: the=
value's NetCDF type, and a reference to the=0Avalues. The values can be e=
ither an array OR scalar, depending on whether we've=0Ajust gotten the cont=
ents of a NetCDF variable - which itself can be either - or=0Athe content o=
f a variable's attribute.=0A=0AThis function returns either an array or a r=
eference to an array, e.g.=0A=0A @get_array =3D
perlify_values($type,\@valu=
es);=0A $get_ref =3D perlify_values($type,\@values);=0A @use_array =3D
@$=
get_ref;=0A=0A=3Dcut=0Asub perlify_values {=0A my ($type,$input) =3D @_;=0A=
my @vals;=0A my $result;=0A=0A # the values can be an array
reference, a s=
calar reference, or a scalar.=0A # just don't pass arrays.=0A if
(ref($i=
nput) eq 'ARRAY') { @vals =3D @$input }=0A elsif (ref($input) eq 'SCALAR'=
){ @vals =3D ($$input) }=0A else { @vals =3D ($input) }=0A=0A if
($type =
=3D=3D NetCDF::CHAR){=0A # weed out nulls, which can sneak in
because NetC=
DF stores attribute strings=0A # as arrays of chars, not as
null-terminate=
d strings=0A @vals =3D grep { $_ !=3D 0 } @vals;=0A # we
also split it ba=
ck into an array of lines, which is the perl-ish way to=0A # look
at strin=
gs=0A $result =3D [split "\n",(pack( 'c' x @vals, @vals))];=0A
}=0A # the =
numeric types are easy... the input is the same as the output=0A elsif
($t=
ype =3D=3D NetCDF::FLOAT){ $result =3D $input }=0A elsif ($type =3D=3D
NetC=
DF::DOUBLE){ $result =3D $input }=0A elsif ($type =3D=3D NetCDF::SHORT){ $r=
esult =3D $input }=0A elsif ($type =3D=3D NetCDF::LONG){ $result =3D $inpu=
t }=0A # except this one doesn't do what you'd expect in perl=0A elsif
($ty=
pe =3D=3D NetCDF::BYTE){=0A $result =3D [map { unpack('C',$_) }
@vals];=0A=
}=0A else {=0A die "UNKNOWN NetCDF type=3D'$type'";=0A
}=0A=0A return (wa=
ntarray ? @$result : (@$result =3D=3D 1 ? $result->[0] : $result));=0A}=0A=
=0A=3Dhead1 VERSION=0A=0ARevsion: $Revision: 1.29 $=0A=0A=3Dcut=0A=0A1;=0A=
=0A
- --SLDf9lqlvOQaIe6s--
------------------------------
End of ncdigest V1 #724
***********************