File Coverage

blib/lib/Astro/FITS/CFITSIO/Utils.pm
Criterion Covered Total %
statement 19 38 50.0
branch 0 4 0.0
condition 0 3 0.0
subroutine 7 9 77.7
pod n/a
total 26 54 48.1


line stmt bran cond sub pod time code
1             # --8<--8<--8<--8<--
2             #
3             # Copyright (C) 2008 Smithsonian Astrophysical Observatory
4             #
5             # This file is part of Astro::FITS::CFITSIO::Utils
6             #
7             # Astro::FITS::CFITSIO::Utils is free software: you can redistribute
8             # it and/or modify it under the terms of the GNU General Public
9             # License as published by the Free Software Foundation, either version
10             # 3 of the License, or (at your option) any later version.
11             #
12             # This program is distributed in the hope that it will be useful,
13             # but WITHOUT ANY WARRANTY; without even the implied warranty of
14             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15             # GNU General Public License for more details.
16             #
17             # You should have received a copy of the GNU General Public License
18             # along with this program. If not, see .
19             #
20             # -->8-->8-->8-->8--
21              
22             package Astro::FITS::CFITSIO::Utils;
23              
24 4     4   828839 use 5.006;
  4         21  
  4         257  
25 4     4   32 use strict;
  4         24  
  4         205  
26 4     4   27 use warnings;
  4         16  
  4         168  
27              
28 4     4   25 use Carp;
  4         7  
  4         661  
29              
30             our $VERSION = '0.13';
31              
32 4     4   28 use Carp;
  4         11  
  4         300  
33              
34 4     4   6012 use Astro::FITS::Header::Item;
  4         405827  
  4         1109  
35              
36             {
37             package Astro::FITS::CFITSIO::Utils::Item;
38              
39             our @ISA = qw( Astro::FITS::Header::Item );
40              
41             sub new
42             {
43 0     0     my $class = shift;
44 0   0       $class = ref $class || $class;
45              
46 0           my ($keyw, $value );
47             # clean up input list, removing things that the superclass won't
48             # understand. must be a better way to do this.
49 0           my %args;
50 0           my @o_args = @_;
51 0           my @args;
52              
53 0           while( ($keyw, $value ) = splice(@o_args, 0, 2 ) )
54             {
55 0 0         if ( $keyw =~ /^(?:hdu_num|)$/i )
56             {
57 0           $args{lc $keyw} = $value;
58             }
59             else
60             {
61 0           push @args, $keyw, $value;
62             }
63             }
64              
65 0           my $self = $class->SUPER::new( @args );
66              
67             # handle the attributes that we know about
68 0           $self->$keyw( $value )
69             while( ( $keyw, $value ) = each %args );
70              
71 0           return $self;
72             }
73              
74             sub hdu_num
75             {
76 0     0     my $self = shift;
77 0 0         if (@_) {
78 0           $self->{hdu_num} = uc(shift);
79             }
80 0           return $self->{hdu_num};
81             }
82             }
83              
84 4     4   4385 use Astro::FITS::Header::CFITSIO;
  0            
  0            
85             use Astro::FITS::CFITSIO
86             qw[ READONLY CASEINSEN
87             ANY_HDU ASCII_TBL BINARY_TBL
88             BAD_HDU_NUM END_OF_FILE
89             ];
90             use Astro::FITS::CFITSIO::CheckStatus;
91             use Params::Validate qw( validate_with :types );
92              
93             require Exporter;
94              
95             our @ISA = qw(Exporter);
96              
97             our %EXPORT_TAGS = ( 'all' => [ qw(
98             keypar
99             keyval
100             colkeys
101             croak_status
102             ) ] );
103              
104             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
105              
106              
107             # Preloaded methods go here.
108              
109             # this is just a convenience wrapper around keypar
110             sub keyval
111             {
112             my $opts = (@_ && 'HASH' eq ref $_[-1]) ? pop @_ : undef;
113              
114             keypar( @_, { defined $opts ? %$opts : () , Value => 1 } );
115             }
116              
117              
118              
119             # $k1 = keypar( $file, $kw1 );
120             # implicit OnePerHDU = 1, Accumulate = 0
121             # $k1 = first matching card
122              
123             # @k = keypar( $file, $kw1 );
124             # implicit OnePerHDU = 0, Accumulate = 1
125             # @k = all matching cards
126              
127             # ( $k1, $k2 ) = keypar( $file, [ $kw1, $kw2 ] );
128             # implicit OnePerHDU = 1, Accumulate = 0
129             # $k1 = first matching card
130             # $k2 = first matching card
131              
132             # $k = keypar( $file, [$kw1, $kw2] )
133             # illegal
134              
135              
136              
137             sub keypar
138             {
139             my $file = shift;
140             my $opts = (@_ && 'HASH' eq ref $_[-1]) ? pop @_ : undef;
141              
142             @_ == 1
143             or croak( __PACKAGE__, "::keypar: incorrect number of arguments\n" );
144              
145             # set up defaults. they change to make things DWIM
146             my %opt = ( Accumulate => ref $_[0] || ! wantarray() ? 0 : 1,
147             OnePerHDU => ref $_[0] || ! wantarray() ? 1 : 0,
148             Value => 0,
149             $opts ? %$opts : ()
150             );
151              
152             $opt{CurrentHDU} = 0
153             unless defined $opt{CurrentHDU} || $file =~ /\[/;
154              
155             my $keyword;
156              
157             if ( 'ARRAY' eq ref $_[0] )
158             {
159             $keyword = [ map { uc($_) } @{$_[0]} ];
160             }
161              
162             elsif ( ! ref $keyword )
163             {
164             # don't do more work than the caller requests
165             unless ( wantarray() )
166             {
167             $opt{Accumulate} = 0;
168             $opt{OnePerHDU} = 1;
169             }
170             $keyword = [ uc $_[0] ];
171             }
172             else
173             {
174             croak( __PACKAGE__, "::keypar: illegal type for keyword\n" );
175             }
176              
177              
178             my %keywords = map { $_ => [] } @$keyword;
179              
180             # are we passed a pointer to an open file?
181             my $file_is_open = eval { $file->isa( 'fitsfilePtr' ) };
182             my $fptr;
183              
184             tie my $status, 'Astro::FITS::CFITSIO::CheckStatus';
185              
186             if ( $file_is_open )
187             {
188             $fptr = $file;
189             }
190              
191             else
192             {
193             $fptr = Astro::FITS::CFITSIO::open_file
194             ( $file, READONLY,
195             $status = __PACKAGE__ . "::keypar: error reading $file: " );
196             }
197              
198             $fptr->get_hdu_num( my $init_hdu_num );
199             $fptr->movabs_hdu( 1, undef, $status );
200             $fptr->get_hdu_num( my $ext );
201              
202             # number of keywords found. used to short circuit search if
203             # Accumulate == 0
204             my $nfound = 0;
205              
206             for ( ;; $ext++ )
207             {
208             my $hdr = Astro::FITS::Header::CFITSIO->new( fitsID => $fptr,
209             ReadOnly => 1 );
210              
211             # loop over keywords
212             while( my ( $keyw, $found ) = each %keywords )
213             {
214             # ignore this keyword if we've found a match and Accumulate
215             # hasn't been set.
216             next if @$found && ! $opt{Accumulate};
217              
218             my @newfound = $hdr->itembyname( $keyw );
219             if ( @newfound )
220             {
221             $#newfound = 0 if $opt{OnePerHDU};
222             foreach ( @newfound )
223             {
224             my $item = Astro::FITS::CFITSIO::Utils::Item->new( Card => $_->card, HDU_NUM => $ext);
225             push @$found,
226             $opt{Value} && defined $item ? $item->value : $item;
227             }
228             $nfound ++;
229             }
230             }
231              
232             last if $opt{CurrentHDU} ||
233             ! $opt{Accumulate} && $nfound == @$keyword;
234              
235             $fptr->movrel_hdu( 1, undef, my $lstatus = 0);
236              
237             last if $lstatus == BAD_HDU_NUM || $lstatus == END_OF_FILE;
238             croak_status( $lstatus );
239             }
240              
241             # done mucking about in the file; if it was an existing opened file
242             # return to the initial HDU
243             $fptr->movabs_hdu( $init_hdu_num, my $dummy, $status )
244             if $file_is_open;
245              
246             # if passed an array ref for $keyword, prepare to handle multiple
247             # keywords
248             if ( ref $_[0] )
249             {
250             my @found;
251              
252             # a single value per keyword. return list of scalars
253             if ( $opt{OnePerHDU} && !$opt{Accumulate} )
254             {
255             @found = map { @{$_}[0] } @keywords{@$keyword};
256             }
257              
258             # multiple values per keyword. return list of arrayrefs.
259             else
260             {
261             @found = @keywords{@$keyword};
262             }
263             return wantarray ? @found : \@found ;
264             }
265              
266             else
267             {
268             my $found = $keywords{@{$keyword}[0]};
269              
270             return wantarray ? @$found : @$found ? @{$found}[0] : undef;
271             }
272              
273             # NOT REACHED
274              
275             }
276              
277             sub colkeys {
278              
279             my $file = shift;
280              
281             my %opt = validate_with ( params => \@_,
282             spec => {
283             extname => { type => SCALAR,
284             optional => 1 },
285             extver => { type => SCALAR,
286             regex => qr/^\d+$/,
287             optional => 1,
288             depends => [ 'extname' ]
289             }
290             },
291             normalize_keys => sub { lc $_[0] },
292             );
293              
294             # are we passed a pointer to an open file?
295             my $file_is_open = eval { $file->isa( 'fitsfilePtr' ) };
296             my $fptr;
297             my $init_hdu_num;
298              
299             tie my $error, 'Astro::FITS::CFITSIO::CheckStatus';
300              
301             if ( $file_is_open )
302             {
303             $fptr = $file;
304             $fptr->get_hdu_num( $init_hdu_num );
305             }
306             else
307             {
308             $error = "Error reading $file: ";
309             $fptr = Astro::FITS::CFITSIO::open_file( $file, READONLY, $error );
310             }
311              
312             # move to specified HDU
313             if ( $opt{extname} )
314             {
315             $opt{extver} ||= 0;
316             my $extname = $opt{extname} . ($opt{extver} ? $opt{extver} : '');
317              
318             $error = "$file does not contain an extension of $extname";
319             $fptr->movnam_hdu( ANY_HDU, $opt{extname}, $opt{extver}, $error );
320              
321             $fptr->get_hdu_type( my $hdutype, $error );
322              
323             croak( "$file\[$extname] is not a table\n")
324             if $hdutype != ASCII_TBL and $hdutype != BINARY_TBL;
325             }
326              
327             # find the first Table HDU
328             else
329             {
330             my $status;
331              
332             while( 1 )
333             {
334             $error = "$file has no table extension";
335             $fptr->movrel_hdu( 1, my $hdutype, $error );
336             last if $hdutype == ASCII_TBL || $hdutype == BINARY_TBL;
337             }
338             }
339              
340             my $hdr = new Astro::FITS::Header::CFITSIO( fitsID => $fptr );
341              
342             $fptr->get_num_cols( my $ncols, $error );
343              
344             my %colkeys;
345             for my $coln (1..$ncols) {
346              
347             my %info;
348              
349             $fptr->get_colname( CASEINSEN, $coln, my $oname, undef, $error );
350             my $name = lc $oname;
351              
352             # blank name! can't have that. just # number 'em after the
353             # actual column position.
354             $name = "col_$coln" if '' eq $name;
355              
356             if ( exists $colkeys{$name} )
357             {
358             my $idx = 1;
359              
360             $idx++ while exists $colkeys{ "${name}_${idx}" };
361             $name = "${name}_${idx}";
362             }
363              
364             for my $item ( grep { $_ !~ /^NAXIS/ }
365             $hdr->itembyname( qr/\D+$coln([a-z])?$/i ) )
366             {
367             my $key = lc join('',
368             grep { defined }
369             $item->keyword =~ /(.*)$coln(.*)$/ );
370             $info{$key} = $item->value;
371             }
372              
373             $colkeys{$name} = { hdr => \%info,
374             idx => $coln };
375             }
376              
377             # done mucking about in the file; if it was an existing opened file
378             # return to the initial HDU
379             $fptr->movabs_hdu( $init_hdu_num, undef, $error )
380             if $file_is_open;
381              
382             return %colkeys;
383             }
384              
385             sub croak_status {
386             my $s = shift;
387              
388             if ($s)
389             {
390             Astro::FITS::CFITSIO::fits_get_errstatus($s, my $txt);
391              
392             local $Carp::CarpLevel = $Carp::CarpLevel + 1;
393             croak @_, "CFITSIO Error: $txt\n";
394             }
395             }
396              
397              
398              
399             1;
400             __END__