File Coverage

blib/lib/Astro/FITS/CFITSIO/Simple.pm
Criterion Covered Total %
statement 91 105 86.6
branch 38 52 73.0
condition 8 12 66.6
subroutine 18 20 90.0
pod 3 6 50.0
total 158 195 81.0


line stmt bran cond sub pod time code
1             package Astro::FITS::CFITSIO::Simple;
2              
3             # ABSTRACT: read and write FITS tables
4              
5 11     11   3622927 use 5.008002;
  11         130  
6 11     11   64 use strict;
  11         30  
  11         222  
7 11     11   52 use warnings;
  11         19  
  11         468  
8              
9             require Exporter;
10              
11 11     11   5175 use Params::Validate qw/ :all /;
  11         93570  
  11         1887  
12              
13 11     11   89 use Carp;
  11         23  
  11         607  
14              
15 11     11   71 use PDL;
  11         32  
  11         98  
16              
17 11     11   261645 use Astro::FITS::CFITSIO qw/ :constants /;
  11         67130  
  11         15965  
18 11     11   4686 use Astro::FITS::CFITSIO::CheckStatus;
  11         9009  
  11         463  
19 11     11   5483 use Astro::FITS::CFITSIO::Simple::Table qw/ :all /;
  11         38  
  11         1304  
20 11     11   5216 use Astro::FITS::CFITSIO::Simple::Image qw/ :all /;
  11         36  
  11         15653  
21              
22              
23             our @ISA = qw(Exporter);
24              
25             # Items to export into callers namespace by default. Note: do not export
26             # names by default without a very good reason. Use EXPORT_OK instead.
27             # Do not simply export all your public functions/methods/constants.
28              
29             # This allows declaration use Astro::FITS::CFITSIO::Table ':all';
30             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
31             # will save memory.
32             our %EXPORT_TAGS = (
33             'all' => [ qw(
34             rdfits
35             ) ] );
36              
37             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
38              
39             our @EXPORT = qw(
40              
41             );
42              
43             our $VERSION = '0.20';
44              
45             # cheap and dirty clean up object so that we can maintain
46             # return contexts in rdfits and its delegates by having
47             # cleanup done during object destruction
48             {
49             package Astro::FITS::CFITSIO::Simple::Cleanup;
50              
51 2     2   7 sub new { my $class = shift; bless {@_}, $class }
  2         10  
52 1     1   7 sub set { $_[0]->{ $_[1] } = $_[2] }
53             sub DESTROY {
54 2     2   7 my $s = shift;
55 2         10 tie my $status, 'Astro::FITS::CFITSIO::CheckStatus';
56             $s->{fptr}->perlyunpacking( $s->{packing} )
57 2 50       106 if defined $s->{packing};
58             $s->{fptr}->movabs_hdu( $s->{hdunum}, undef, $status )
59 2 100       19 if defined $s->{hdunum};
60             }
61             }
62              
63              
64              
65             # HDU types we recognize
66             our %HDUType = (
67             img => IMAGE_HDU,
68             image => IMAGE_HDU,
69             binary => BINARY_TBL,
70             bintbl => BINARY_TBL,
71             ascii => ASCII_TBL,
72             any => ANY_HDU,
73             table => undef, # the CFITSIO flags aren't really bits
74             );
75              
76 17     17 0 259 sub validHDUTYPE { exists $HDUType{ lc $_[0] } }
77 3 50   3 0 57 sub validHDUNUM { $_[0] =~ /^\d+$/ && $_[0] > 0 }
78              
79              
80              
81             # these are the Params::Validate specifications for rdfits
82             # they are specified separately here, so that parameters
83             # for _rdfitsTable and _rdfitsImage can be split out
84             # from the main option hash
85              
86             our %rdfits_spec = (
87             extname => { type => SCALAR, optional => 1 },
88             extver => {
89             type => SCALAR,
90             depends => 'extname',
91             default => 0
92             },
93             hdunum => {
94             type => SCALAR,
95             callbacks => { 'illegal HDUNUM' => \&validHDUNUM, },
96             optional => 1
97             },
98             hdutype => {
99             type => SCALAR,
100             callbacks => { 'illegal HDU type' => \&validHDUTYPE, },
101             default => 'any',
102             optional => 1
103             },
104             resethdu => { type => SCALAR, default => 0 },
105             );
106              
107             sub rdfits {
108              
109             # strip off the options hash
110 39 100   39 1 90550 my $opts = 'HASH' eq ref $_[-1] ? pop : {};
111              
112             # first arg is fitsfilePtr or filename
113 39         92 my $input = shift;
114              
115 39 50 66     418 croak( "input must be a fitsfilePtr or a file name\n" )
      66        
116             unless defined $input
117             && ( UNIVERSAL::isa( $input, 'fitsfilePtr' ) || !ref $input );
118              
119              
120             # rdfits is a dispatch routine; we need to filter out the options
121             # for the delegates (and vice versa). final argument validation
122             # is done by the the delegates
123              
124             # shallow copy, then delete non-rdfits options.
125 39         86 my %rdfits_opts = %{$opts};
  39         164  
126             delete @rdfits_opts{
127 39         129 grep { !exists $rdfits_spec{ lc( $_ ) } }
  49         202  
128             keys %rdfits_opts
129             };
130              
131             # shallow copy, then delete rdfits options
132 39         86 my %delegate_opts = %{$opts};
  39         103  
133 39         86 delete @delegate_opts{ keys %rdfits_opts };
134              
135             # if there are additional arguments, guess that we're being
136             # asked for some columns, and set the requested HDUTYPE to table
137 39 100       130 $rdfits_opts{hdutype} = 'table' if @_;
138              
139             # validate arguments
140             my %opt = validate_with(
141             params => [ \%rdfits_opts ],
142 217     217   1027 normalize_keys => sub { lc $_[0] },
143 39         864 spec => \%rdfits_spec
144             );
145              
146              
147              
148             # CFITSIO file pointer
149 39         314 my $fptr;
150              
151 39         291 tie my $status, 'Astro::FITS::CFITSIO::CheckStatus';
152              
153 39         2072 my $cleanup;
154              
155             # get CFITSIO file pointer
156 39 100       182 if ( UNIVERSAL::isa( $input, 'fitsfilePtr' ) ) {
157              
158 2         5 $fptr = $input;
159              
160 2         28 $cleanup = Astro::FITS::CFITSIO::Simple::Cleanup->new(
161             fptr => $fptr,
162             packing => $fptr->perlyunpacking
163             );
164              
165 2 100       9 if ( $opt{resethdu} ) {
166 1         8 $fptr->get_hdu_num( my $hdunum );
167 1         3 $cleanup->set( hdunum => $hdunum );
168             }
169              
170             }
171             else {
172 37         169 $fptr = Astro::FITS::CFITSIO::open_file( $input, READONLY,
173             $status = "could not open FITS file '$input'" );
174             }
175              
176             # we're not unpacking;
177 39         23246 $fptr->perlyunpacking( 0 );
178              
179             # read in all of the extensions
180             croak( "slurp not yet implemented!\n" )
181 39 50       146 if $opt{slurp};
182              
183             # read in just one
184 39         67 my $hdutype;
185              
186             # HDU specified by name
187 39 100       150 if ( exists $opt{extname} ) {
    100          
188             $fptr->movnam_hdu( ANY_HDU, $opt{extname}, $opt{extver},
189 1         6 $status = "could not move to HDU '$opt{extname}:$opt{extver}'" );
190              
191 1         119 $fptr->get_hdu_type( $hdutype, $status );
192              
193             croak( "requested extension does not match requested HDU type\n" )
194 1 50       19 unless match_hdutype( $opt{hdutype}, $hdutype );
195             }
196              
197             # HDU specified by number?
198             elsif ( exists $opt{hdunum} ) {
199 3         17 $fptr->movabs_hdu( $opt{hdunum}, $hdutype, $status );
200              
201             croak( "requested extension does not match requested HDU type\n" )
202 2 100       74 unless match_hdutype( $opt{hdutype}, $hdutype );
203             }
204              
205             # first recognizable one
206             else {
207             # lazy; let CheckStatus do the work.
208 35         79 eval {
209 35         154 until ( $status ) {
210 64         2809 $fptr->get_hdu_type( $hdutype, $status );
211              
212             # check that we're in an actual image, i.e. NAXIS != 0
213 64 100       1082 if ( IMAGE_HDU == $hdutype ) {
214 37         289 $fptr->get_img_dim( my $naxis, $status );
215 37 100       635 next unless $naxis;
216             }
217 37 100       218 last if match_hdutype( $opt{hdutype}, $hdutype );
218              
219             }
220             continue {
221 29         124 $fptr->movrel_hdu( 1, $hdutype, $status );
222             }
223             };
224              
225             # ran off end of file
226 35 50       315 croak( "unable to find a matching HDU to read\n" )
227             if BAD_HDU_NUM == $status;
228              
229             # all other errors
230 35 50       557 croak $@ if $@;
231             }
232              
233             # update args.
234 37         112 unshift @_, $fptr;
235              
236             # add the options for the delegate
237 37         98 push @_, \%delegate_opts;
238              
239             # dispatch. we use the dispatch goto here to keep croak's etc. at the
240             # correct level and to maintain the calling context.
241              
242 37 100 66     123 if ( BINARY_TBL == $hdutype || ASCII_TBL == $hdutype ) {
    50          
243 27         209 _rdfitsTable( @_ );
244             }
245             elsif ( IMAGE_HDU == $hdutype ) {
246 10         148 _rdfitsImage( @_ );
247             }
248             else {
249 0         0 croak( "internal error. bizarre hdutype = $hdutype\n" );
250             }
251              
252             }
253              
254             # a thin front end for reading in a table
255              
256             sub rdfitstbl {
257             # make shallow copy of passed options hash (or create one)
258 0 0   0 1 0 my %opt = 'HASH' eq ref $_[-1] ? %{ pop @_ } : ();
  0         0  
259              
260             # force the HDU to match a table
261 0         0 $opt{hdutype} = 'table';
262              
263             # read only one HDU
264 0         0 delete $opt{slurp};
265              
266             # make sure only the input file is in there.
267 0 0       0 croak( "too many arguments to rdfitstbl\n" )
268             if @_ > 1;
269              
270             # attach our new options hash
271 0         0 push @_, \%opt;
272              
273             # do the whole shebang; pretend we were never here.
274 0         0 goto &rdfits;
275             }
276              
277             # a thin front end for reading in an image
278              
279             sub rdfitsimg {
280             # make shallow copy of passed options hash (or create one)
281 0 0   0 1 0 my %opt = 'HASH' eq ref $_[-1] ? %{ pop @_ } : ();
  0         0  
282              
283             # force the HDU to match a table
284 0         0 $opt{hdutype} = 'image';
285              
286             # read only one HDU
287 0         0 delete $opt{slurp};
288              
289             # attach our new options hash
290 0         0 push @_, \%opt;
291              
292             # do the whole shebang; pretend we were never here.
293 0         0 goto &rdfits;
294             }
295              
296             sub match_hdutype {
297 40     40 0 127 my ( $req, $actual ) = @_;
298              
299 40 100 66     154 return ( BINARY_TBL == $actual || ASCII_TBL == $actual )
300             if 'table' eq $req;
301              
302 26         71 my $reqtype = $HDUType{$req};
303              
304 26 100       86 return 1 if ANY_HDU == $reqtype;
305              
306 4 100       30 return 1 if $reqtype == $actual;
307              
308              
309 2         8 0;
310             }
311              
312             #
313             # This file is part of Astro-FITS-CFITSIO-Simple
314             #
315             # This software is Copyright (c) 2008 by Smithsonian Astrophysical Observatory.
316             #
317             # This is free software, licensed under:
318             #
319             # The GNU General Public License, Version 3, June 2007
320             #
321              
322             1;
323              
324             __END__