File Coverage

blib/lib/Astro/Correlate/Method/RITMatch.pm
Criterion Covered Total %
statement 24 133 18.0
branch 0 42 0.0
condition 0 15 0.0
subroutine 8 10 80.0
pod 1 1 100.0
total 33 201 16.4


line stmt bran cond sub pod time code
1             package Astro::Correlate::Method::RITMatch;
2              
3             =head1 NAME
4              
5             Astro::Correlate::Method::RITMatch - Correlation using RIT Match.
6              
7             =head1 SYNOPSIS
8              
9             ( $corrcat1, $corrcat2 ) = Astro::Correlate::Match::RITMatch->correlate( catalog1 => $cat1, catalog2 => $cat2 );
10              
11             =head1 DESCRIPTION
12              
13             This class implements catalogue cross-correlation using the RIT Match
14             application.
15              
16             =cut
17              
18 1     1   1799 use 5.006;
  1         4  
  1         49  
19 1     1   6 use strict;
  1         3  
  1         37  
20 1     1   4 use warnings;
  1         2  
  1         42  
21 1     1   5 use warnings::register;
  1         3  
  1         131  
22              
23 1     1   6 use Carp;
  1         2  
  1         250  
24 1     1   6 use File::Temp qw/ tempfile /;
  1         2  
  1         52  
25 1     1   992 use File::SearchPath qw/ searchpath /;
  1         1775  
  1         71  
26 1     1   1279 use Storable qw/ dclone /;
  1         4490  
  1         1798  
27              
28             our $VERSION = '0.02';
29             our $DEBUG = 0;
30              
31             =head1 METHODS
32              
33             =head2 General Methods
34              
35             =over 4
36              
37             =item B
38              
39             Cross-correlates two catalogues.
40              
41             ( $corrcat1, $corrcat2 ) = Astro::Correlate::Method::RITMatch->correlate( catalog1 => $cat1,
42             catalog2 => $cat2 );
43              
44             This method takes two mandatory arguments, both C objects.
45             It returns two C objects containing C
46             objects that matched spatially between the two input catalogues. The
47             first returned catalogue contains matched objects from the first input
48             catalogue, and ditto for the second. The C objects
49             in the returned catalogues are not in the original order, nor do they have
50             the same IDs as in the input catalogues. A matched object has the same ID
51             in the two returned catalogues, allowing for further comparisons between
52             matched objects.
53              
54             This method takes the following optional named arguments:
55              
56             =item cat1magtype - The magnitude type to use for the first supplied
57             catalogue. If not defined, will default to 'mag'. This is used for
58             Astro::Catalog::Item objects that have fluxes that are not standard
59             magnitudes (for example, one might set this to 'mag_iso' for
60             magnitudes that come from the MAG_ISO column of a SExtractor
61             catalogue).
62              
63             =item cat2magtype - As for cat1magtype, but for the second supplied
64             catalogue.
65              
66             =item keeptemps - If this argument is set to true (1), then this
67             method will keep temporary files used in processing. Defaults to
68             false.
69              
70             =item messages - no effect.
71              
72             =item temp - Set the directory to hold temporary files. If not set,
73             then a new temporary directory will be created using File::Temp.
74              
75             =item timeout - Set the time in seconds to wait for the CCDPACK
76             monolith to time out. Defaults to 60 seconds.
77              
78             =item verbose - If this argument is set to true (1), then this method will
79             print progress statements. Defaults to false.
80              
81             This method usees the RIT Match application. In order for this method
82             to work it must be able to find the match binary. It looks in the
83             directory pointed to by the MATCH_DIR environment variable, and if
84             that fails, looks through your $PATH. If it cannot be found, this
85             method will croak.
86              
87             =cut
88              
89             sub correlate {
90 0     0 1   my $class = shift;
91              
92             # Grab the arguments, and make sure they're defined and are
93             # Astro::Catalog objects (the catalogues, at least).
94 0           my %args = @_;
95 0           my $cat1 = dclone( $args{'catalog1'} );
96 0           my $cat2 = dclone( $args{'catalog2'} );
97              
98 0 0 0       if( ! defined( $cat1 ) ||
99             ! UNIVERSAL::isa( $cat1, "Astro::Catalog" ) ) {
100 0           croak "catalog1 parameter to Astro::Correlate::Method::RITMatch->correlate "
101             . "method must be defined and must be an Astro::Catalog object"
102             ;
103             }
104 0 0 0       if( ! defined( $cat2 ) ||
105             ! UNIVERSAL::isa( $cat2, "Astro::Catalog" ) ) {
106 0           croak "catalog2 parameter to Astro::Correlate::Method::RITMatch->correlate "
107             . "method must be defined and must be an Astro::Catalog object"
108             ;
109             }
110              
111             # Retrieve the scaling factor.
112 0           my $scale = _determine_scaling_factor( $cat1, $cat2 );
113              
114 0 0         my $keeptemps = defined( $args{'keeptemps'} ) ? $args{'keeptemps'} : 0;
115 0           my $temp;
116 0 0 0       if( exists( $args{'temp'} ) && defined( $args{'temp'} ) ) {
117 0           $temp = $args{'temp'};
118             } else {
119 0           $temp = tempdir ( UNLINK => ! $keeptemps );
120             }
121 0 0         my $verbose = defined( $args{'verbose'} ) ? $args{'verbose'} : 0;
122 0 0         my $cat1magtype = defined( $args{'cat1magtype'} ) ? $args{'cat1magtype'} : 'mag';
123 0 0         my $cat2magtype = defined( $args{'cat2magtype'} ) ? $args{'cat2magtype'} : 'mag';
124              
125             # Try to find the match binary in the directory pointed to by the
126             # MATCH_DIR environment variable. If that doesn't work, check the
127             # user's $PATH. If that doesn't work, croak.
128 0           my $match_bin;
129 0 0 0       if( defined( $ENV{'MATCH_DIR'} ) &&
      0        
130             -d $ENV{'MATCH_DIR'} &&
131             -e File::Spec->catfile( $ENV{'MATCH_DIR'}, "match" ) ) {
132 0           $match_bin = File::Spec->catfile( $ENV{'MATCH_DIR'}, "match" );
133             } else {
134 0           $match_bin = searchpath( "match" );
135 0 0         if( ! defined( $match_bin ) ) {
136 0           croak "Could not find match binary. Ensure MATCH_DIR environment variable is set";
137             }
138             }
139              
140 0 0         print "match binary is in $match_bin\n" if $DEBUG;
141              
142             # Get two temporary filenames for catalog files.
143 0           ( undef, my $catfile1 ) = tempfile( DIR => $temp );
144 0           ( undef, my $catfile2 ) = tempfile( DIR => $temp );
145              
146             # Match is sensitive to the scaling of the problem, requiring that the
147             # error is of order 1, and certainly less than 50. To meet this requirement
148             # scale the X and Y coordinates to have a range of 200 (XXX make this some
149             # multiple of the image scale).
150 0           my $cat1stars = $cat1->stars;
151 0           foreach my $cat1star ( @$cat1stars ) {
152 0           $cat1star->x( $cat1star->x() / $scale );
153 0           $cat1star->y( $cat1star->y() / $scale );
154             }
155 0           my $cat2stars = $cat2->stars;
156 0           foreach my $cat2star ( @$cat2stars ) {
157 0           $cat2star->x( $cat2star->x() / $scale );
158 0           $cat2star->y( $cat2star->y() / $scale );
159             }
160              
161             # Write the two input catalogues for match.
162 0 0         print "Writing catalog 1 to $catfile1 using $cat1magtype magnitude.\n" if $DEBUG;
163 0           $cat1->write_catalog( Format => 'RITMatch',
164             File => $catfile1,
165             mag_type => $cat1magtype );
166 0 0         print "Input catalog 1 written to $catfile1.\n" if $DEBUG;
167 0 0         print "Writing catalog 2 to $catfile2 using $cat2magtype magnitude.\n" if $DEBUG;
168 0           $cat2->write_catalog( Format => 'RITMatch',
169             File => $catfile2,
170             mag_type => $cat2magtype );
171 0 0         print "Input catalog 2 written to $catfile2.\n" if $DEBUG;
172              
173             # Create two hash lookup tables. Key will be the "match-ed" ID, which
174             # is the original ID with all non-numeric characters removed, and
175             # value will be the original ID. This will allow us to match up stars
176             # after the correlation has happened.
177 0           my %lookup_cat1;
178             my %lookup_cat2;
179              
180 0           $cat1stars = $cat1->stars;
181 0           my $newid = 1;
182 0           foreach my $cat1star ( @$cat1stars ) {
183 0           $lookup_cat1{$newid} = $cat1star->id;
184 0           $newid++;
185             }
186              
187 0           $cat2stars = $cat2->stars;
188 0           $newid = 1;
189 0           foreach my $cat2star ( @$cat2stars ) {
190 0           $lookup_cat2{$newid} = $cat2star->id;
191 0           $newid++;
192             }
193              
194             # Create a base filename for the output catalogues. Put it in the
195             # temporary directory previously set up.
196 0           my $outfilebase = File::Spec->catfile( $temp, "outfile$$" );
197              
198             # Set up the parameter list for match.
199 0           my @matchargs = ( "$catfile1",
200             "1",
201             "2",
202             "3",
203             "$catfile2",
204             "1",
205             "2",
206             "3",
207             "outfile=$outfilebase",
208             "nobj=30",
209             "id1=0",
210             "id2=0",
211             );
212              
213             # Run match.
214 0 0         my $pid = open my $stdout, "$match_bin " . ( join ' ', @matchargs ) . "|" or croak "Could not execute match: $!";
215 0           close $stdout;
216              
217             # Read in the first output catalogue of matching objects. The old ID
218             # will be in the comment field.
219 0           my $tempcat = new Astro::Catalog( Format => 'RITMatch',
220             File => $outfilebase . ".mtA" );
221              
222             # Loop through the stars, making a new catalogue with new stars using
223             # a combination of the new ID and the old information.
224 0           my $corrcat1 = new Astro::Catalog();
225 0           my @stars = $tempcat->stars;
226 0           $newid = 1;
227 0           foreach my $star ( @stars ) {
228              
229             # The old ID is found in the first column of the star's comment.
230             # However, this old ID has been "match-ed", i.e. all non-numeric
231             # characters have been stripped from it. Use the lookup table we
232             # generated earlier to find the proper old ID.
233 0           $star->id =~ /^(\w+)/;
234 0           my $oldmatchid = $1;
235 0           my $oldid = $lookup_cat1{$oldmatchid};
236              
237             # Get the star's information.
238 0           my $oldstar = $cat1->popstarbyid( $oldid );
239 0           $oldstar = $oldstar->[0];
240 0 0         next if ! defined( $oldstar );
241              
242             # Set the ID to the new star's ID.
243 0           $oldstar->id( $newid );
244              
245             # Restore X and Y.
246 0           $oldstar->x( $oldstar->x() * $scale );
247 0           $oldstar->y( $oldstar->y() * $scale );
248              
249             # Set the comment denoting the old ID.
250 0           $oldstar->comment( "Old ID: " . $oldid );
251              
252             # And push this star onto the output catalogue.
253 0           $corrcat1->pushstar( $oldstar );
254              
255 0           $newid++;
256             }
257              
258             # And do the same for the second catalogue.
259 0           $tempcat = new Astro::Catalog( Format => 'RITMatch',
260             File => $outfilebase . ".mtB" );
261              
262 0           my $corrcat2 = new Astro::Catalog();
263 0           @stars = $tempcat->stars;
264 0           $newid = 1;
265 0           foreach my $star ( @stars ) {
266              
267             # The old ID is found in the first column of the star's comment.
268             # However, this old ID has been "match-ed", i.e. all non-numeric
269             # characters have been stripped from it. Use the lookup table we
270             # generated earlier to find the proper old ID.
271 0           $star->id =~ /^(\w+)/;
272 0           my $oldmatchid = $1;
273 0           my $oldid = $lookup_cat2{$oldmatchid};
274              
275             # Get the star's information.
276 0           my $oldstar = $cat2->popstarbyid( $oldid );
277 0           $oldstar = $oldstar->[0];
278 0 0         next if ! defined( $oldstar );
279              
280             # Set the ID to the new star's ID.
281 0           $oldstar->id( $newid );
282              
283             # Restore X and Y.
284 0           $oldstar->x( $oldstar->x() * $scale );
285 0           $oldstar->y( $oldstar->y() * $scale );
286              
287             # Set the comment denoting the old ID.
288 0           $oldstar->comment( "Old ID: " . $oldid );
289              
290             # And push this star onto the output catalogue.
291 0           $corrcat2->pushstar( $oldstar );
292              
293 0           $newid++;
294             }
295              
296             # Delete the temporary files if we're not in debug mode.
297 0 0         if( ! $DEBUG ) {
298 0           unlink $catfile1;
299 0           unlink $catfile2;
300 0           unlink $outfilebase . ".mtA";
301 0           unlink $outfilebase . ".mtB";
302 0           unlink $outfilebase . ".unA";
303 0           unlink $outfilebase . ".unB";
304             }
305              
306 0           return ( $corrcat1, $corrcat2 );
307             }
308              
309             =back
310              
311             =head2 Private Methods
312              
313             =over 4
314              
315             =item B<_determine_scaling_factor>
316              
317             match v0.09 and above had a requirement (or strong suggestion) that
318             coordinate values be less than about 5000. Testing has shown that this
319             limit is closer to about 1000, so this method looks at all of the
320             coordinate values in the two catalogues and determines a scaling
321             factor to bring those coordinate values under 1000.
322              
323             my $factor = _determine_scaling_factor( $cat1, $cat2 );
324              
325             =cut
326              
327             sub _determine_scaling_factor {
328 0     0     my $cat1 = shift;
329 0           my $cat2 = shift;
330              
331 0           my $max = 0;
332 0           my $max_pos = 500;
333              
334 0           foreach my $cat ( ( $cat1, $cat2 ) ) {
335 0           foreach my $item ( $cat->stars ) {
336 0 0         if( $item->x > $max ) {
337 0           $max = $item->x;
338             }
339 0 0         if( $item->y > $max ) {
340 0           $max = $item->y;
341             }
342             }
343             }
344              
345 0 0         my $scale = ( $max > $max_pos ? $max / $max_pos : 1 );
346              
347 0           return $scale;
348              
349             }
350              
351             =head1 SEE ALSO
352              
353             C
354              
355             http://spiff.rit.edu/match/
356              
357             =head1 REVISION
358              
359             $Id$
360              
361             =head1 AUTHORS
362              
363             Brad Cavanagh
364              
365             =head1 COPYRIGHT
366              
367             Copyright (C) 2006 Particle Physics and Astronomy Research Council.
368             All Rights Reserved.
369              
370             This program is free software; you can redistribute it and/or modify it under
371             the terms of the GNU General Public License as published by the Free Software
372             Foundation; either version 2 of the License, or (at your option) any later
373             version.
374              
375             This program is distributed in the hope that it will be useful,but WITHOUT ANY
376             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
377             PARTICULAR PURPOSE. See the GNU General Public License for more details.
378              
379             You should have received a copy of the GNU General Public License along with
380             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
381             Place,Suite 330, Boston, MA 02111-1307, USA
382              
383             =cut
384              
385             1;