File | /wise/base/deliv/dev/lib/perl/WISE/CoUtils.pm | Statements Executed | 31121297 | Total Time | 80.8105209972666 seconds |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine | |
---|---|---|---|---|---|---|
5980967 | 1 | 1 | 61.04364 | 79.71310 | WISE::CoUtils:: | angdist |
55478 | 1 | 1 | 0.76807 | 0.76807 | WISE::CoUtils:: | hdms_dd |
27739 | 3 | 1 | 0.60347 | 4.34133 | WISE::CoUtils:: | cconv |
27739 | 1 | 1 | 0.27110 | 1.03917 | WISE::CoUtils:: | to_dd |
13869 | 1 | 1 | 0.14052 | 0.24279 | WISE::CoUtils:: | rd2radvec |
13869 | 1 | 1 | 0.11891 | 2.49860 | WISE::CoUtils:: | _equgal |
13869 | 1 | 1 | 0.09614 | 2.28587 | WISE::CoUtils:: | _fk5fk4 |
13869 | 1 | 1 | 0.09231 | 0.20001 | WISE::CoUtils:: | _equecl |
1 | 1 | 1 | 3.7e-5 | 3.7e-5 | WISE::CoUtils:: | _fpre |
1 | 1 | 1 | 1.1e-5 | 7.7e-5 | WISE::CoUtils:: | _eclequ |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | BEGIN |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | _eclgal |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | _fk4fk5 |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | _galecl |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | _galequ |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | _trivial |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | _undefize |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | autocorrhist |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | cconvdd |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | dd_hdms |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | fit_3param_simple |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | gcposang |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | gnomfwd_pure |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | gnomrev_pure |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | lonlat2rpa_small |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | platefit_3param_simple |
0 | 0 | 0 | 0 | 0 | WISE::CoUtils:: | to_sex |
1 | 1 | 1 | -152.71548 | 460.76892 | WISE::CoUtils:: | posmatch |
Line | Stmts. | Exclusive Time | Avg. | Code |
---|---|---|---|---|
1 | #! /usr/bin/env perl | |||
2 | 3 | 3.3e-5 | 1.1e-5 | use strict; # spent 13µs making 1 call to strict::import |
3 | 3 | 5.1e-5 | 1.7e-5 | use warnings; # spent 36µs making 1 call to warnings::import |
4 | ||||
5 | package WISE::CoUtils; | |||
6 | ||||
7 | 3 | 2.9e-5 | 9.7e-6 | use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION $R2D $PI $S2); # spent 98µs making 1 call to vars::import |
8 | ||||
9 | # Extension modules | |||
10 | ||||
11 | 3 | 7.7e-5 | 2.6e-5 | use Exporter; # spent 28µs making 1 call to Exporter::import |
12 | 1 | 1.0e-6 | 1.0e-6 | $VERSION = 1.00; |
13 | 1 | 1.0e-5 | 1.0e-5 | @ISA = qw(Exporter); |
14 | ||||
15 | 1 | 1.0e-6 | 1.0e-6 | @EXPORT = (); |
16 | ||||
17 | 1 | 7.0e-6 | 7.0e-6 | @EXPORT_OK = qw(angdist posmatch to_dd to_sex rd2rad |
18 | rd2radvec lonlat2rpa_small gcposang | |||
19 | cconv convdd autocorrhist | |||
20 | gnomrev_pure gnomfwd_pure | |||
21 | $R2D $PI $S2 $sig2fwhm); | |||
22 | ||||
23 | 3 | 0.00059 | 0.00020 | use WISE::CoUtilsXS; # spent 154µs making 1 call to Exporter::import |
24 | ||||
25 | 3 | 3.7e-5 | 1.2e-5 | use POSIX ('tan','acos', 'asin'); # spent 82µs making 1 call to POSIX::import |
26 | ||||
27 | 3 | 0.00054 | 0.00018 | use vars qw /$PI $R2D $S2 $sig2fwhm/; # spent 58µs making 1 call to vars::import |
28 | ||||
29 | 1 | 0 | 0 | $PI = 3.1415926535897931; |
30 | 1 | 2.0e-6 | 2.0e-6 | $R2D = 180.0 / $PI; |
31 | 1 | 1.0e-6 | 1.0e-6 | $S2 = sqrt(2.0); |
32 | 1 | 0 | 0 | $sig2fwhm = 2.354820; |
33 | ||||
34 | 1 | 3.0e-6 | 3.0e-6 | my $iam = "$0/CoUtils"; |
35 | 1 | 1.0e-6 | 1.0e-6 | my $err = "*** $iam"; |
36 | 1 | 1.0e-6 | 1.0e-6 | my $warn= "=== $iam"; |
37 | ||||
38 | # CVS revision ID | |||
39 | 1 | 1.0e-6 | 1.0e-6 | my $version = '$Id: CoUtils.pm 6953 2010-01-14 01:35:33Z tim $ '; |
40 | ||||
41 | # Supplement and/or alias WISE::Pop and Astro::Coord conversions. | |||
42 | # Convert to use in degrees instead of "turns." | |||
43 | sub _fk4fk5 { return map {$_*360} fk4fk5($_[0]/360,$_[1]/360); } | |||
44 | 13869 | 0.09940 | 7.2e-6 | # spent 2.29s (96.1ms+2.19) within WISE::CoUtils::_fk5fk4 which was called 13869 times, avg 165µs/call:
# 13869 times (96.1ms+2.19s) by WISE::CoUtils::_equgal at line 55, avg 165µs/call # spent 2.19s making 13869 calls to Astro::Coord::fk5fk4, avg 158µs/call |
45 | # - We assume the user wants J2000 equatorial coords, except when | |||
46 | # converting explicitly to B1950. | |||
47 | # - We assume the user wants the J2000 ecliptic. | |||
48 | # - The galactic routines take/use b1950 ra,dec, so | |||
49 | # we use b1950 equatorial coord.s as an intermediary. | |||
50 | sub _galequ { my ($bra,$bdec) = galeqd($_[0],$_[1]); | |||
51 | return _fk4fk5($bra,$bdec); } | |||
52 | sub _galecl { my ($bra,$bdec) = galeqd($_[0],$_[1]); | |||
53 | ($bra,$bdec) = _fk4fk5($bra,$bdec); | |||
54 | return equecx($bra,$bdec,2,2000); } | |||
55 | 27738 | 0.19805 | 7.1e-6 | # spent 2.50s (119ms+2.38) within WISE::CoUtils::_equgal which was called 13869 times, avg 180µs/call:
# 13869 times (119ms+2.38s) by WISE::CoUtils::cconv at line 130, avg 180µs/call # spent 2.29s making 13869 calls to WISE::CoUtils::_fk5fk4, avg 165µs/call |
56 | return equgad($bra,$bdec); } # spent 93.8ms making 13869 calls to WISE::CoUtilsXS::equgad, avg 7µs/call | |||
57 | 13869 | 0.14963 | 1.1e-5 | # spent 200ms (92.3+108) within WISE::CoUtils::_equecl which was called 13869 times, avg 14µs/call:
# 13869 times (92.3ms+108ms) by WISE::CoUtils::cconv at line 130, avg 14µs/call # spent 108ms making 13869 calls to WISE::CoUtilsXS::equecx, avg 8µs/call |
58 | 1 | 7.0e-5 | 7.0e-5 | # spent 77µs (11+66) within WISE::CoUtils::_eclequ which was called
# once (11µs+66µs) by WISE::CoUtils::cconv at line 130 # spent 66µs making 1 call to WISE::CoUtilsXS::ecleqx |
59 | sub _eclgal { my ($bra,$bdec) = ecleqx($_[0],$_[1],2,2000); | |||
60 | ($bra,$bdec) = _fk5fk4($bra,$bdec); | |||
61 | return equgad($bra,$bdec); } | |||
62 | # For non-converting conversions | |||
63 | sub _trivial { return @_; } | |||
64 | ||||
65 | # Acceptable coordinate system aliases | |||
66 | 1 | 2.0e-5 | 2.0e-5 | our %coords = (qw/equ equ |
67 | eq equ | |||
68 | 1 equ | |||
69 | q equ | |||
70 | equatorial equ | |||
71 | j2000 equ | |||
72 | fk5 equ | |||
73 | 2000 equ | |||
74 | gal gal | |||
75 | ga gal | |||
76 | g gal | |||
77 | 2 gal | |||
78 | galactic gal | |||
79 | ecl ecl | |||
80 | ec ecl | |||
81 | 3 ecl | |||
82 | ecliptic ecl | |||
83 | b1950 fk4 | |||
84 | fk4 fk4 | |||
85 | 1950 fk4 / | |||
86 | ); | |||
87 | ||||
88 | # Pair conversion functions | |||
89 | 1 | 1.7e-5 | 1.7e-5 | my %conv = ( |
90 | equequ => \&_trivial, | |||
91 | equgal => \&_equgal, | |||
92 | equecl => \&_equecl, | |||
93 | galequ => \&_galequ, | |||
94 | galgal => \&_trivial, | |||
95 | galecl => \&_galecl, | |||
96 | eclequ => \&_eclequ, | |||
97 | eclgal => \&_eclgal, | |||
98 | eclecl => \&_trivial, | |||
99 | fk4equ => \&_fk4fk5, | |||
100 | equfk4 => \&_fk5fk4, | |||
101 | fk4fk4 => \&_trivial, | |||
102 | ); | |||
103 | ||||
104 | 3 | 0.00125 | 0.00042 | use Astro::Coord; # spent 133µs making 1 call to Exporter::import |
105 | 3 | 0.00586 | 0.00195 | use WISE::CoUtilsXS; # spent 144µs making 1 call to Exporter::import |
106 | ||||
107 | # spent 4.34s (603ms+3.74) within WISE::CoUtils::cconv which was called 27739 times, avg 157µs/call:
# 13869 times (271ms+2.98s) at line 838 of /wise/base/deliv/dev/bin/getfix, avg 234µs/call
# 13869 times (333ms+758ms) at line 833 of /wise/base/deliv/dev/bin/getfix, avg 79µs/call
# once (53µs+237µs) at line 485 of /wise/base/deliv/dev/bin/getfix | |||
108 | 388346 | 0.69072 | 1.8e-6 | warn '$0/Cconv: Bad args. cconv($from,$lon,$lat[,$to])' |
109 | if @_ != 3 && @_ != 4; | |||
110 | my $from = shift; | |||
111 | my $lon0 = shift; | |||
112 | my $lat0 = shift; | |||
113 | my $to = shift || 'equ'; | |||
114 | ||||
115 | warn("$0/Cconv: Some required args undefined.\n"), return | |||
116 | if grep {! defined $_} ($from,$lon0,$lat0); | |||
117 | $from = $coords{lc $from} | |||
118 | or warn("$0/Cconv: Coord alias '$from' not known.\n"), return; | |||
119 | $to = $coords{lc $to} | |||
120 | or warn("$0/Cconv: Coord alias '$to' not known.\n"), return; | |||
121 | my $hord = $from eq 'equ' ? 'h' : 'd'; | |||
122 | # Convert from sexigesimal (detected internally in to_dd) | |||
123 | my ($lon0d,$lat0d) = to_dd($lon0,$lat0,$hord); # spent 1.04s making 27739 calls to WISE::CoUtils::to_dd, avg 37µs/call | |||
124 | warn("$0/Cconv: Bad position ($lon0,$lat0) passed.\n"), return | |||
125 | if ! defined $lon0d || ! defined $lat0d || $lat0d < -90 || $lat0d > 90; | |||
126 | warn "$0/Cconv: Unnormalized longitude ($lon0) passed.\n" | |||
127 | if $lon0d < 0 || $lon0d > 360; | |||
128 | my $conv = $conv{$from.$to} | |||
129 | or warn("$0/Cconv: Conversion function '$from$to' not available.\n"), return; | |||
130 | return ($conv->($lon0d,$lat0d),$from,$to); # spent 2.50s making 13869 calls to WISE::CoUtils::_equgal, avg 180µs/call
# spent 200ms making 13869 calls to WISE::CoUtils::_equecl, avg 14µs/call
# spent 77µs making 1 call to WISE::CoUtils::_eclequ | |||
131 | } | |||
132 | ||||
133 | # Version of above where positions are guaranteed to be decimal degrees | |||
134 | # and in range | |||
135 | sub cconvdd { | |||
136 | my $from = shift; | |||
137 | my $lon0 = shift; | |||
138 | my $lat0 = shift; | |||
139 | my $to = shift || 'equ'; | |||
140 | ||||
141 | $from = $coords{lc $from} | |||
142 | or warn("$0/Cconv: Coord alias '$from' not known"), return; | |||
143 | $to = $coords{lc $to} | |||
144 | or warn("$0/Cconv: Coord alias '$to' not known"), return; | |||
145 | my $conv = $conv{$from.$to} | |||
146 | or warn("$0/Cconv: Conversion function '$from$to' not available"), return; | |||
147 | return ($conv->($lon0,$lat0),$from,$to); | |||
148 | } | |||
149 | ||||
150 | # Pure perl gnomonic projections | |||
151 | sub gnomrev_pure { | |||
152 | my ($xi,$eta,$ra0,$dec0,$crota,$cdelt,$cenpix) = @_; | |||
153 | my $x = ($xi - $cenpix)*$cdelt/$R2D; | |||
154 | my $y = ($eta - $cenpix)*$cdelt/$R2D; | |||
155 | my $crotar = $crota/$R2D; | |||
156 | my $xp = $x*cos($crotar) + $y*sin($crotar); | |||
157 | my $yp = -$x*sin($crotar) + $y*cos($crotar); | |||
158 | # delta = atan(sqrt($xp*$xp + $yp*$yp)) | |||
159 | my $rsq = $xp*$xp + $yp*$yp; | |||
160 | my $r = sqrt($rsq); | |||
161 | my $cosdelta = $rsq > 1e-32 ? 1. / sqrt($rsq + 1) : 1.0; | |||
162 | my $sindelta = sqrt(1. - $cosdelta*$cosdelta); | |||
163 | # beta = atan2(-$x,$y) | |||
164 | my $cosbeta = $r>0 ? -$yp/$r : 1; | |||
165 | my $sinbeta = $r>0 ? -$xp/$r : 0; | |||
166 | my $cosdec0 = cos($dec0/$R2D); | |||
167 | my $sindec0 = sin($dec0/$R2D); | |||
168 | my $xx = $sindec0*$sindelta*$cosbeta + $cosdec0*$cosdelta; | |||
169 | my $yy = $sindelta*$sinbeta; | |||
170 | my $dec = asin($sindec0*$cosdelta - $cosdec0*$sindelta*$cosbeta)*$R2D; | |||
171 | my $ra = $ra0 + atan2($yy,$xx)*$R2D; | |||
172 | if($ra >= 360) { $ra -= 360 ; } | |||
173 | if($ra < 0) { $ra += 360 ; } | |||
174 | #printf("--- rev: returning %f,%f.\n",$ra,$dec); | |||
175 | return ($ra,$dec); | |||
176 | } | |||
177 | ||||
178 | sub gnomfwd_pure { | |||
179 | my ($ra, $dec, $ra0, $dec0, $crota, $cdelt, $cenpix) = @_; | |||
180 | my $cosdec0 = cos($dec0/$R2D); | |||
181 | my $sindec0 = sin($dec0/$R2D); | |||
182 | my $drar = ($ra0-$ra)/$R2D; | |||
183 | my $decr = $dec/$R2D; | |||
184 | my $crotar = $crota/$R2D; | |||
185 | my $a = cos($decr) * cos($drar); | |||
186 | my $f = 1.0/($sindec0 * sin($decr) + $a*$cosdec0); | |||
187 | my $xp = $f * cos($decr) * sin($drar); | |||
188 | my $yp = $f * ($cosdec0 * sin($decr) - $a*$sindec0); | |||
189 | my $x = $xp*cos(-$crotar) + $yp*sin(-$crotar); | |||
190 | my $y = -$xp*sin(-$crotar) + $yp*cos(-$crotar); | |||
191 | my $xi = $x*$R2D/$cdelt + $cenpix; | |||
192 | my $eta = $y*$R2D/$cdelt + $cenpix; | |||
193 | return ($xi,$eta); | |||
194 | } | |||
195 | ||||
196 | # Angular distance between two lon-lat pairs. Not great for really small | |||
197 | # differences. | |||
198 | # spent 79.7s (61.0+18.7) within WISE::CoUtils::angdist which was called 5980967 times, avg 13µs/call:
# 5980967 times (61.0s+18.7s) by WISE::CHealPix::pix_withinr_ring at line 89 of /wise/base/deliv/dev/lib/perl/x86_64-linux-thread-multi/WISE/CHealPix.pm, avg 13µs/call | |||
199 | 29904835 | 66.02429 | 2.2e-6 | my ($x1,$y1,$x2,$y2) = map {$_/$R2D} @_; |
200 | my $arg = sin($y1)*sin($y2) + cos($y1)*cos($y2)*cos($x1-$x2); | |||
201 | $arg = 1 if $arg > 1; | |||
202 | $arg = -1 if $arg < -1; | |||
203 | return acos($arg)*$R2D; # spent 18.7s making 5980967 calls to POSIX::acos, avg 3µs/call | |||
204 | } | |||
205 | ||||
206 | # spent 243ms (141+102) within WISE::CoUtils::rd2radvec which was called 13869 times, avg 18µs/call:
# 13869 times (141ms+102ms) by WISE::CoUtils::posmatch at line 767, avg 18µs/call | |||
207 | 69345 | 0.20699 | 3.0e-6 | my ($r,$dx,$dy) = radifs($_[2],$_[3],$_[0],$_[1]); # From CoUtilsXS # spent 102ms making 13869 calls to WISE::CoUtilsXS::radifs, avg 7µs/call |
208 | my $pa = atan2($dy,-$dx)*$R2D-90; | |||
209 | $pa += 360 if $pa < 0; | |||
210 | $pa -= 360 if $pa > 360; | |||
211 | return wantarray ? ($r/3600,$pa,$dx/3600,$dy/3600) : $r/3600; | |||
212 | } | |||
213 | ||||
214 | sub lonlat2rpa_small { | |||
215 | my ($ra,$dec,$ra0,$dec0) = @_; | |||
216 | my ($x,$y) = gnomfwd_pure($ra,$dec,$ra0,$dec0,0,1/3600,0); | |||
217 | my ($r,$pa) = xy2rve(-$x,$y,0,0); | |||
218 | return wantarray ? ($r/3600,$pa) : $r/3600; | |||
219 | } | |||
220 | ||||
221 | # Formal position angle using angle between great circle intersecting | |||
222 | # the two positions and the meridian. | |||
223 | # Cribbed from IDL posang routine. | |||
224 | sub gcposang { | |||
225 | # The order of the args is switched to match the IDL formula | |||
226 | my ($rarad2,$decrad2,$rarad1,$decrad1) = map { $_/$R2D } @_; | |||
227 | my $radif = $rarad2-$rarad1; | |||
228 | my $angle = atan2(sin($radif), | |||
229 | cos($decrad1)*tan($decrad2)-sin($decrad1)*cos($radif)) | |||
230 | * $R2D; | |||
231 | $angle += 360 if $angle < 0; | |||
232 | return $angle; | |||
233 | } | |||
234 | ||||
235 | sub fit_3param_simple { | |||
236 | my ($ra1, $dec1) = (shift, shift); | |||
237 | my ($ra0, $dec0) = (shift, shift); # Truth | |||
238 | my ($racen, $deccen) = (shift, shift); # Proj center | |||
239 | my $n = @$ra1; | |||
240 | ||||
241 | my (@x1, @y1); | |||
242 | for (0..$n-1) { | |||
243 | my ($x,$y) = gnomfwd_pure($ra1->[$_], $dec1->[$_], | |||
244 | $racen, $deccen, 0, 1/3600., 0); | |||
245 | push @x1, $x; | |||
246 | push @y1, $y; | |||
247 | } | |||
248 | ||||
249 | my (@x0, @y0); | |||
250 | for (0..$n-1) { | |||
251 | my ($x,$y) = gnomfwd_pure($ra0->[$_], $dec0->[$_], | |||
252 | $racen, $deccen, 0, 1/3600., 0); | |||
253 | push @x0, $x; | |||
254 | push @y0, $y; | |||
255 | } | |||
256 | ||||
257 | my $res = platefit_3param_simple(\@x1, \@y1, \@x0, \@y0); | |||
258 | ||||
259 | return $res; | |||
260 | } | |||
261 | ||||
262 | sub platefit_3param_simple { | |||
263 | my ($x_in, $y_in) = (shift, shift); | |||
264 | my ($xi, $eta) = (shift, shift); | |||
265 | my $cdelt = shift; | |||
266 | ||||
267 | require PDL::Lite; | |||
268 | ||||
269 | if(ref($x_in) =~ /array/i) { | |||
270 | $x_in = PDL->new($x_in); | |||
271 | $y_in = PDL->new($y_in); | |||
272 | $xi = PDL->new($xi); | |||
273 | $eta = PDL->new($eta); | |||
274 | } | |||
275 | ||||
276 | return if $x_in->nelem < 2; | |||
277 | ||||
278 | my ($x_scale,$y_scale) = (1,1); | |||
279 | if($cdelt) { | |||
280 | $x_scale = abs($cdelt)*3600; | |||
281 | $y_scale = abs($cdelt)*3600; | |||
282 | } | |||
283 | ||||
284 | # Rescale x_in and y_in and populate x and y | |||
285 | my $x = $x_scale*$x_in; | |||
286 | my $y = $y_scale*$y_in; | |||
287 | ||||
288 | # Compute the centroids, which will later be used to compute offsets | |||
289 | my $x0 = $x->avg; | |||
290 | my $y0 = $y->avg; | |||
291 | ||||
292 | my $xi0 = $xi->avg; | |||
293 | my $eta0 = $eta->avg; | |||
294 | ||||
295 | # Compute the sums for the rotation angle. | |||
296 | my $x_xi = ( ($x-$x0) * ($xi -$xi0) )->sum; | |||
297 | my $x_eta = ( ($x-$x0) * ($eta-$eta0) )->sum; | |||
298 | my $y_xi = ( ($y-$y0) * ($xi -$xi0) )->sum; | |||
299 | my $y_eta = ( ($y-$y0) * ($eta-$eta0) )->sum; | |||
300 | ||||
301 | # Determinant | |||
302 | my $det = $x_xi*$y_eta - $x_eta*$y_xi; | |||
303 | ||||
304 | my ($num,$denom); | |||
305 | if($det < 0) { | |||
306 | $num = $y_xi + $x_eta; | |||
307 | $denom = -$x_xi + $y_eta; | |||
308 | } else { | |||
309 | $num = $y_xi - $x_eta; | |||
310 | $denom = $x_xi + $y_eta; | |||
311 | } | |||
312 | ||||
313 | # Rotation angle | |||
314 | my $theta = atan2( $num, $denom ); | |||
315 | $theta += 2*$PI if $theta < 0; | |||
316 | ||||
317 | # Package up the fit parameters | |||
318 | ||||
319 | my $ctheta = cos( $theta ); | |||
320 | my $stheta = sin( $theta ); | |||
321 | ||||
322 | my ($ctheta_x,$stheta_y); | |||
323 | if($det < 0) { | |||
324 | $ctheta_x = -$ctheta; | |||
325 | $stheta_y = -$stheta; | |||
326 | } else { | |||
327 | $ctheta_x = $ctheta; | |||
328 | $stheta_y = $stheta; | |||
329 | } | |||
330 | ||||
331 | my $stheta_x = $stheta; | |||
332 | my $ctheta_y = $ctheta; | |||
333 | ||||
334 | # 3-param solution | |||
335 | my $rot = $theta*$R2D; | |||
336 | my $xoff = $xi0 - ( $ctheta_x*$x0 + $stheta_x*$y0); | |||
337 | my $yoff = $eta0 - (-$stheta_y*$x0 + $ctheta_y*$y0); | |||
338 | ||||
339 | # 6-param solution | |||
340 | my @params; | |||
341 | $params[0] = $xoff; # a | |||
342 | $params[1] = $ctheta_x*$x_scale; # b | |||
343 | $params[2] = $stheta_x*$y_scale; # c | |||
344 | $params[3] = $yoff; # d | |||
345 | $params[4] = -$stheta_y*$x_scale; # e | |||
346 | $params[5] = $ctheta_y*$y_scale; # f | |||
347 | ||||
348 | return wantarray ? ($xoff, $yoff, $rot, @params) : | |||
349 | {xoff=>$xoff, yoff=>$yoff, rot=>$rot, | |||
350 | params=>\@params}; | |||
351 | ||||
352 | } | |||
353 | ||||
354 | sub autocorrhist { | |||
355 | my $rows = shift; # Table to autocorrelate | |||
356 | my $rmax = shift || 10*60; # Search radius | |||
357 | my $opts = shift || {}; # Options my $verbose = $opts->{verbose}; | |||
358 | my $verbose = $opts->{verbose}; | |||
359 | my $xy = $opts->{xy}; | |||
360 | my $debug = $opts->{debug} || ""; | |||
361 | my $loncol = $opts->{loncol} | |||
362 | || $xy ? 'X_IMAGE' : 'X_WORLD'; # Name of the longitude column | |||
363 | my $latcol = $opts->{latcol} | |||
364 | || $xy ? 'Y_IMAGE' : 'Y_WORLD'; # Name of the latitude column | |||
365 | my $magcol = $opts->{magcol} | |||
366 | || 'MAG_AUTO'; # Name of the magnitude column | |||
367 | my $rbin = $opts->{rbinsz} || 5; # Arc-secs | |||
368 | my $pabin = defined $opts->{pabinsz} ? $opts->{pabinsz} : 4; # Degrees | |||
369 | my $xybin = $opts->{xybinsz} || 5; | |||
370 | my $top = $opts->{top} || 0; | |||
371 | my $fits = $opts->{fits}; # FITS input file handle | |||
372 | my $norm = $opts->{norm}; | |||
373 | my $sig = $opts->{sig}; | |||
374 | my $x0 = $opts->{x0}; | |||
375 | my $y0 = $opts->{y0}; | |||
376 | my $fovr = $opts->{fovr}; | |||
377 | my $pixsz = $opts->{pixsz}; | |||
378 | my $asymmetric = $opts->{asymmetric}; # Asymmetric matching | |||
379 | my $hist2d; | |||
380 | my $havecenter; | |||
381 | my $top2 = 0; | |||
382 | ||||
383 | if(ref $top) { | |||
384 | if(@$top == 1) { $top = $top->[0]; } | |||
385 | else { | |||
386 | ($top,$top2) = sort {$a<=>$b} @$top | |||
387 | if @$top > 1 && $top->[0]; | |||
388 | } | |||
389 | } | |||
390 | ||||
391 | $asymmetric = 0 if ! $top || $top < 0; | |||
392 | $asymmetric = 1 if $top2 && $top2 > 0; | |||
393 | ||||
394 | print "Asymmetric matching ($top, $top2) for autocorrelation ". | |||
395 | "will be done.\n" | |||
396 | if $asymmetric && $verbose; | |||
397 | ||||
398 | if(! ref $rows) { | |||
399 | require WISE::FITSIO; | |||
400 | $fits = WISE::FITSIO->new($rows,{mode=>'r'}) | |||
401 | or die; | |||
402 | print "Reading from $rows ...\n" if $verbose; | |||
403 | $rows = $fits->readcols([$loncol,$latcol,($magcol?($magcol):())], | |||
404 | {hdunum=>2}) | |||
405 | or return; | |||
406 | print "... read ".$fits->nread." rows.\n" if $verbose; | |||
407 | } | |||
408 | ||||
409 | if($fits && $fovr && $fovr > 0 && ! defined $x0) { | |||
410 | $pixsz ||= $fits->key("CDELT2", {hdunum=>1})*3600 | |||
411 | || $fits->key("PXSCAL2",{hdunum=>1}); | |||
412 | ($x0,$y0) = $fits->key(["CRVAL1","CRVAL2"],{hdunum=>1}); | |||
413 | if($xy) { | |||
414 | ($x0,$y0) = $fits->cel2pix($x0,$y0); | |||
415 | } | |||
416 | print "Read FOV center as $x0, $y0 .\n" | |||
417 | if $verbose && defined $x0 && defined $y0; | |||
418 | } | |||
419 | ||||
420 | ($x0,$y0) = (undef,undef) if defined $x0 && ($x0 < -990 || $y0 < -990); | |||
421 | ||||
422 | my $nrows = @{$rows->{$loncol}}; | |||
423 | ||||
424 | if(defined $fovr && $fovr>0 && defined $x0 && defined $y0) { | |||
425 | my %newrows; | |||
426 | print "Restricting sources to R(FOV) = $fovr\" ...\n" if $verbose; | |||
427 | for my $i (0..$nrows-1) { | |||
428 | my $d = ($xy | |||
429 | ? sqrt(($rows->{$loncol}[$i]-$x0)**2 + | |||
430 | ($rows->{$latcol}[$i]-$y0)**2)*$pixsz | |||
431 | : angdist($rows->{$loncol}[$i],$rows->{$latcol}[$i], | |||
432 | $x0, $y0) * 3600); | |||
433 | next if $d > $fovr; | |||
434 | push @{$newrows{$_}}, $rows->{$_}[$i] for keys %$rows; | |||
435 | } | |||
436 | print "... found ".@{$newrows{$loncol}}." rows.\n" if $verbose; | |||
437 | $rows = \%newrows; | |||
438 | $havecenter = 1; | |||
439 | } | |||
440 | ||||
441 | my $mchto = $rows; | |||
442 | my $mchfrom = $rows; | |||
443 | ||||
444 | if($magcol && $top && $top <= $#{$rows->{$loncol}}) { | |||
445 | my @order; | |||
446 | @order = (sort {$rows->{$magcol}[$a]<=>$rows->{$magcol}[$b]} | |||
447 | 0..$#{$rows->{$loncol}}); | |||
448 | if($top2 && $top2 <= $#{$rows->{$loncol}}) { | |||
449 | print "Restricting match-to group to brightest $top2 sources ...\n" | |||
450 | if $verbose; | |||
451 | my %newrows; | |||
452 | $newrows{$_} = [@{$rows->{$_}}[@order[0..$top2-1]]] | |||
453 | for keys %$rows; | |||
454 | $mchto = \%newrows; | |||
455 | } | |||
456 | print "Restricting matching to brightest $top sources ...\n" | |||
457 | if $verbose; | |||
458 | my %newrows; | |||
459 | $newrows{$_} = [@{$rows->{$_}}[@order[0..$top-1]]] | |||
460 | for keys %$rows; | |||
461 | $mchfrom = \%newrows; | |||
462 | $mchto = $mchfrom if ! $asymmetric; | |||
463 | } | |||
464 | ||||
465 | # Initialize the histograms. The pre-filled size is important since | |||
466 | # only pre-allocated bins are filled. | |||
467 | ||||
468 | my (@rhist, @rnorm, @pahist, @r, @pa, @errnorm, @errpa, @hist2d, | |||
469 | @xyhist, @errxy, $nrbins, $npabins, $nxybins); | |||
470 | ||||
471 | $nrbins = int($rmax/$rbin) - 1; | |||
472 | @rhist[0.. $nrbins-1] = (0) x $nrbins; | |||
473 | @r = map { $_*$rbin } 0..$#rhist; | |||
474 | ||||
475 | if($pabin > 0) { | |||
476 | $npabins = int(360/$pabin) - 1; | |||
477 | @pahist[0..$npabins-1] = (0) x $npabins; | |||
478 | @pa = map { $_*$pabin } 0..$#pahist; | |||
479 | } else { | |||
480 | $npabins = 0; | |||
481 | } | |||
482 | ||||
483 | if($xybin > 0) { | |||
484 | $nxybins = 2*int($rmax/$xybin) - 1; | |||
485 | @xyhist[0..$nxybins-1] = map { [(0) x $nxybins] } 0..$nxybins-1; | |||
486 | } | |||
487 | ||||
488 | # Do the matching and fill the histograms | |||
489 | ||||
490 | my $state = { | |||
491 | index => 1, | |||
492 | loncol0 => $loncol, | |||
493 | latcol0 => $latcol, | |||
494 | magcol0 => $magcol, | |||
495 | loncol => $loncol, | |||
496 | latcol => $latcol, | |||
497 | magcol => $magcol, | |||
498 | noself => 1, | |||
499 | nearest => 0, | |||
500 | rhist => \@rhist, rbinsz => $rbin, | |||
501 | pahist => \@pahist, pabinsz => $pabin, | |||
502 | xyhist => \@xyhist, xybinsz => $xybin, | |||
503 | histonly => 1, | |||
504 | xy => $xy, | |||
505 | pixsz => $pixsz, | |||
506 | %$opts | |||
507 | }; | |||
508 | ||||
509 | my $res = posmatch($mchfrom,$mchto,$xy?$rmax/$pixsz:$rmax,$state); | |||
510 | my $n = $res->{nmatched}; | |||
511 | ||||
512 | # Fix up the returned histos in various ways | |||
513 | ||||
514 | my $av = $n/($PI*($rmax/3600)**2); | |||
515 | for my $i (0..$#rhist) { | |||
516 | my $r = ($i+1)*$rbin/3600; | |||
517 | # Annular bin area. Corrected for ave. fraction off the fov. | |||
518 | my $a = $PI*($r**2 - ($r-$rbin/3600)**2); | |||
519 | $rnorm[$i] = $rhist[$i] / $a; | |||
520 | $errnorm[$i] = sqrt($rhist[$i])/$a; | |||
521 | $rnorm[$i] = $errnorm[$i]>0 ? ($rnorm[$i]-$av)/$errnorm[$i] : 0 | |||
522 | if $sig; | |||
523 | } | |||
524 | @rhist = @rnorm if $norm; | |||
525 | ||||
526 | for my $i (0..$#pahist) { | |||
527 | $errpa[$i] = sqrt($pahist[$i]); | |||
528 | } | |||
529 | ||||
530 | if($xybin > 0) { | |||
531 | for my $i (0..$nxybins-1) { | |||
532 | for my $j (0..$nxybins-1) { | |||
533 | $errxy[$i][$j] = sqrt($xyhist[$i][$j]); | |||
534 | } | |||
535 | } | |||
536 | } | |||
537 | ||||
538 | print "Found $n matches for autocorrelation.\n" if $verbose; | |||
539 | ||||
540 | return wantarray ? @rhist : { | |||
541 | n=>$n, | |||
542 | nrbins=>$nrbins, npabins=>$npabins, | |||
543 | rbinsz=>$rbin, pabinsz=>$pabin, | |||
544 | ave=>$av, r=>\@r, pa=>\@pa, | |||
545 | rhist=>\@rhist, pahist=>\@pahist, | |||
546 | rnorm=>\@rnorm, errnorm=>\@errnorm, | |||
547 | errpa=>\@errpa, | |||
548 | xybinsz=>$xybin,nxybins=>$nxybins, | |||
549 | xyhist=>\@xyhist,errxy=>\@errxy, | |||
550 | }; | |||
551 | } | |||
552 | ||||
553 | # Do position matching betwee two 'tables' represented in memory as a | |||
554 | # hash of arrays of columns. | |||
555 | # spent 461s (-152715476400ns+613) within WISE::CoUtils::posmatch which was called
# once (-152715476400ns+613s) at line 936 of /wise/base/deliv/dev/bin/getfix | |||
556 | 259420 | 12.47215 | 4.8e-5 | my $rows = shift; # Table to match from. |
557 | my $rows0 = shift; # Table to match to. | |||
558 | my $rad = shift; # Search radius | |||
559 | my $opts = shift || {}; # Options | |||
560 | my ($loncol,$latcol,$loncol0,$latcol0,$magcol,$magcol0); | |||
561 | my ($hdunum,$index,$nsides,$nearest,$xy,$magtol); | |||
562 | my ($nrows,$nrows0,$debug,$verbose,$noself,$pixsz,$histonly); | |||
563 | my ($rhist,$rbin,$rmax,$pahist,$pabin,$pamax,$xyhist,$xybin,$xymax); | |||
564 | ||||
565 | if(! $rows) { | |||
566 | die "*** $0/POSMCHN: No match-from data rows supplied.\n"; | |||
567 | } | |||
568 | ||||
569 | if(! $rows0) { | |||
570 | die "*** $0/POSMCHN: No match-to data rows supplied.\n"; | |||
571 | } | |||
572 | ||||
573 | require WISE::CHealPix; | |||
574 | WISE::CHealPix->import(qw(ang2pix_ring neighbors_ring pix_withinr_ring # spent 172µs making 1 call to Exporter::import | |||
575 | nsides2side_deg side2nsides_deg)); | |||
576 | ||||
577 | $loncol = $opts->{loncol} || 'ra'; # Name of the longitude "column" | |||
578 | $latcol = $opts->{latcol} || 'dec'; # Name of the lat "column" | |||
579 | $magcol = $opts->{magcol} || 'mag'; # Name of the magnitude column | |||
580 | $loncol0 = $opts->{loncol0} || $loncol; | |||
581 | $latcol0 = $opts->{latcol0} || $latcol; | |||
582 | $magcol0 = $opts->{magcol0} || $magcol; | |||
583 | $pixsz = $opts->{pixsz} || 1; # Pixel size for xy mode (for hists) | |||
584 | $debug = $opts->{debug} || 0; | |||
585 | $hdunum = $opts->{hdunum} || 2; | |||
586 | $nsides = $opts->{nsides}; # HealPix nsides param for indexing | |||
587 | $verbose = $opts->{verbose}; | |||
588 | $nearest = $opts->{nearest}; # Return only a single, nearest match | |||
589 | $magtol = $opts->{magtol}; # Mag tolerance | |||
590 | $noself = $opts->{noself}; # Disallow identical position matches | |||
591 | $index = $opts->{index}; # Saved index | |||
592 | $rhist = $opts->{rhist}; | |||
593 | $rmax = $opts->{rmax}; | |||
594 | $rbin = $opts->{rbinsz}; | |||
595 | $pahist = $opts->{pahist}; | |||
596 | $pabin = $opts->{pabinsz}; | |||
597 | $xyhist = $opts->{xyhist}; | |||
598 | $xybin = $opts->{xybinsz}; | |||
599 | $histonly= $opts->{histonly}; | |||
600 | my $hist = (($rhist && ref($rhist) =~/array/i && $rbin ) || | |||
601 | ($pahist && ref($pahist)=~/array/i && $pabin) || | |||
602 | ($xyhist && ref($xyhist)=~/array/i && $xybin)); | |||
603 | my $fovr = $xybin*@$xyhist/2 if $hist && $xyhist; | |||
604 | ||||
605 | ++$verbose if $debug; | |||
606 | ||||
607 | die "*** $0/POSMCHN: Radius '"._undefize($rad)."' illegal.\n" | |||
608 | if ! defined $rad || ref $rad || $rad<0; | |||
609 | ||||
610 | die "*** $0/POSMCHN: Histogram spec. error; /". | |||
611 | join("/", _undefize($rhist,$rbin,$pahist,$pabin,$xyhist,$xybin))."/\n" | |||
612 | if ! $hist && ($rhist || $pahist || $xyhist); | |||
613 | ||||
614 | die "*** $0/POSMCHN: lon/latcol/0 name(s) not specified.\n" | |||
615 | if grep {! $_} ($loncol,$latcol,$loncol0,$latcol0); | |||
616 | ||||
617 | die "*** $0/POSMCHN: Magtol defined but magcol or magcol0 not specified.\n" | |||
618 | if $magtol && (! $magcol || ! $magcol0); | |||
619 | ||||
620 | my %results; | |||
621 | my @matched; | |||
622 | $results{nmatched} = 0; | |||
623 | $results{nmatches} = 0; | |||
624 | $results{matched} = \@matched; | |||
625 | ||||
626 | if(! ref $rows) { | |||
627 | # Filename was passed, so build rows | |||
628 | print "Posmatch: Reading '$rows' ...\n" if $verbose; | |||
629 | my $fits = WISE::FITSIO->new($rows,{verbose=>$verbose>1}) or return; | |||
630 | $rows = $fits->readcols([$loncol, $latcol, $magtol?($magcol):()], | |||
631 | {hdunum=>$hdunum}) or return; | |||
632 | } | |||
633 | ||||
634 | if(! ref $rows0) { | |||
635 | # Filename was passed, so build rows | |||
636 | print "Posmatch: Reading '$rows0' ...\n" if $verbose; | |||
637 | my $fits = WISE::FITSIO->new($rows0,{verbose=>$verbose>1}) or return; | |||
638 | $rows0 = $fits->readcols([$loncol0, $latcol0, $magtol?($magcol0):()], | |||
639 | {hdunum=>$hdunum}) or return; | |||
640 | } | |||
641 | ||||
642 | { my @missing = | |||
643 | (grep {! $rows->{$_} } ($loncol, $latcol, $magtol?($magcol) :()), | |||
644 | grep {! $rows0->{$_}} ($loncol0,$latcol0,$magtol?($magcol0):()) ); | |||
645 | die "*** $0/POSMCHN: Required columns not in data; @missing.\n" | |||
646 | if @missing; | |||
647 | } | |||
648 | ||||
649 | if(ref($rows) eq 'ARRAY') { | |||
650 | # We have a single position as [$ra, $dec]. Turn it into a fake | |||
651 | # row structure of the appropriate type. | |||
652 | $rows = { $loncol=>[$rows->[0]], $latcol=>[$rows->[1]] }; | |||
653 | } | |||
654 | ||||
655 | $nrows0 = @{$rows0->{$latcol0}} ; | |||
656 | $nrows = @{$rows ->{$latcol} } ; | |||
657 | ||||
658 | if($nrows0 < 1) { return 0; } | |||
659 | ||||
660 | $index = 0 if $nrows0 < 10; | |||
661 | ||||
662 | if($index && (! $nsides || $nsides < 0)) { | |||
663 | my $side = $rad*2; | |||
664 | if($side < 10) { $side = 10; } | |||
665 | if($side > 36000) { $side = 36000; } | |||
666 | $nsides = side2nsides_deg($side/3600); # spent 12µs making 1 call to WISE::CHealPix::side2nsides_deg | |||
667 | $nsides = 8192 if $nsides>8192; | |||
668 | } | |||
669 | ||||
670 | if($hist) { | |||
671 | if($rhist) { | |||
672 | if(@$rhist) { # Keep pre-allocated size | |||
673 | $rmax = $rbin*@$rhist; | |||
674 | } else { | |||
675 | $rmax ||= $rad; | |||
676 | @$rhist = (0) x int($rmax/$rbin); | |||
677 | } | |||
678 | } | |||
679 | if($pahist) { | |||
680 | if(@$pahist) { | |||
681 | $pamax = $pabin*@$pahist; | |||
682 | } else { | |||
683 | $pamax ||= 360; | |||
684 | @$pahist = (0) x int($pamax/$pabin); | |||
685 | } | |||
686 | } | |||
687 | if($xyhist) { | |||
688 | if(@$xyhist) { | |||
689 | die "*** $0/POSMCHN: xyhist misconfigured.\n" | |||
690 | if ! ref($xyhist->[0])!~/array/i || | |||
691 | grep {@$_ != @$xyhist} @$xyhist; | |||
692 | $xymax = $xybin*@$xyhist/2; | |||
693 | } else { | |||
694 | $xymax ||= $rad; | |||
695 | my $nxy = 2*int($xymax/$xybin); | |||
696 | $xymax = $xybin*$nxy/2; | |||
697 | @$xyhist = map {[(0) x $nxy]} (1..$nxy); | |||
698 | } | |||
699 | } | |||
700 | } | |||
701 | ||||
702 | my $lons0 = $rows0->{$loncol0}; | |||
703 | my $lats0 = $rows0->{$latcol0}; | |||
704 | my $lons = $rows ->{$loncol}; | |||
705 | my $lats = $rows ->{$latcol}; | |||
706 | ||||
707 | # If index is defined but is not a reference or is an empty reference, | |||
708 | # create a spatial index on the input catalog | |||
709 | if($index && (! ref($index) || ! keys(%$index) )) { | |||
710 | my $side = nsides2side_deg($nsides)*3600; # spent 23µs making 1 call to WISE::CHealPix::nsides2side_deg | |||
711 | print "Posmatch: Indexing with nsides=$nsides, pixel=$side\" ...\n" | |||
712 | if $verbose; | |||
713 | $index = {} if ! ref($index); | |||
714 | %$index = (); | |||
715 | # Index rows0 | |||
716 | for my $ix (0..($nrows0-1)) { | |||
717 | next if ! defined $lons0->[$ix] || ! defined $lats0->[$ix]; | |||
718 | my $pix = ang2pix_ring($nsides,$lons0->[$ix],$lats0->[$ix]); # spent 260ms making 13869 calls to WISE::CHealPix::ang2pix_ring, avg 19µs/call | |||
719 | my @pixs; | |||
720 | if($rad <= $side) { | |||
721 | @pixs= ($pix,neighbors_ring($nsides,$pix)); | |||
722 | } else { | |||
723 | @pixs= pix_withinr_ring($nsides,$pix,$rad); # spent 613s making 13869 calls to WISE::CHealPix::pix_withinr_ring, avg 44.2ms/call | |||
724 | } | |||
725 | print "--- $ix: $lons0->[$ix],$lats0->[$ix] = $pix,@pixs\n" | |||
726 | if $debug > 2; | |||
727 | push @{ $index->{$_} }, $ix for (@pixs); | |||
728 | } | |||
729 | print "Posmatch: ... created ".(keys %$index)." indicies.\n" | |||
730 | if $verbose; | |||
731 | } | |||
732 | ||||
733 | # If the radius is impossible, just index and return | |||
734 | if($rad < 0) { return 0; } | |||
735 | ||||
736 | my @search; | |||
737 | @search = (0..$nrows0-1) if ! $index; | |||
738 | my $nmatches = 0; | |||
739 | my $nmatched = 0; | |||
740 | ||||
741 | print "Posmatch: Comparing $nrows rows to $nrows0 rows ". | |||
742 | "with radius $rad\" ...\n" | |||
743 | if $verbose; | |||
744 | ||||
745 | SEARCH: for my $ix (0..($nrows-1)) { | |||
746 | ||||
747 | my ($lon,$lat) = ($lons->[$ix], $lats->[$ix]); | |||
748 | next if ! defined $lon || ! defined $lat; | |||
749 | my @matches; | |||
750 | print "\t--- Looking for $lon, $lat ...\n" | |||
751 | if $debug > 1; | |||
752 | if($index) { | |||
753 | # If there's an index, use it to get a list of rows to check | |||
754 | my $pix = ang2pix_ring($nsides,$lon,$lat); # spent 21µs making 1 call to WISE::CHealPix::ang2pix_ring | |||
755 | @search = @{ $index->{$pix} || [] }; | |||
756 | print "\t\t--- pix=$pix, search=@search\n" | |||
757 | if $debug > 1; | |||
758 | } | |||
759 | ||||
760 | print "\t\t--- Searching ".@search." rows.\n" | |||
761 | if $debug > 1; | |||
762 | ||||
763 | for my $ix0 (@search) { | |||
764 | my ($lon0,$lat0) = ($lons0->[$ix0], $lats0->[$ix0]); | |||
765 | next if ! defined $lon0 || ! defined $lat0; | |||
766 | next if $noself && $lon==$lon0 && $lat==$lat0; | |||
767 | my($r,$pa) = ( $xy # spent 243ms making 13869 calls to WISE::CoUtils::rd2radvec, avg 18µs/call | |||
768 | ? (sqrt(($lon-$lon0)**2 + ($lat-$lat0)**2), | |||
769 | atan2(($lat-$lat0)/$R2D,($lon-$lon0)/$R2D)*$R2D | |||
770 | ) | |||
771 | : rd2radvec($lon,$lat,$lon0,$lat0) | |||
772 | ) ; | |||
773 | $pa += 360 if $pa < 0; | |||
774 | $r *= 3600 if ! $xy; | |||
775 | print "\t\t\t--- Comparing to $lon0, $lat0 yields r,pa=$r\",$pa.\n" | |||
776 | if $debug > 2; | |||
777 | next if $r > $rad; | |||
778 | # Mag tolerance check. | |||
779 | next if $magtol && | |||
780 | abs($rows0->{$magcol0}[$ix0] - $rows->{$magcol}[$ix]) | |||
781 | > $magtol; | |||
782 | $results{counts2}{$ix0}++; | |||
783 | if(! $nearest) { | |||
784 | push @matches, {ix=>$ix0,r=>$r,pa=>$pa}; | |||
785 | } elsif(! @matches || $r < $matches[0]{r}) { | |||
786 | $matches[0] = {ix=>$ix0,r=>$r,pa=>$pa}; | |||
787 | } | |||
788 | } | |||
789 | $results{counts1}{$ix} = @matches; | |||
790 | print "\t\t--- MATCHED ".@matches." rows to #$ix=$lon,$lat". | |||
791 | (@matches | |||
792 | ?": ".join(";",map {"ix0=$_->{ix},r=$_->{r},". | |||
793 | "pos=$lons0->[$_->{ix}],$lats0->[$_->{ix}]"} | |||
794 | @matches) | |||
795 | :" .")."\n" | |||
796 | if $debug > 1 || ($debug && @matches); | |||
797 | ||||
798 | # Record results | |||
799 | if(@matches) { | |||
800 | ||||
801 | $nmatches += @matches; | |||
802 | push @matched, $ix; | |||
803 | ++$nmatched; | |||
804 | $results{matches}{$ix} = [@matches] if ! $histonly; | |||
805 | ||||
806 | if($hist) { | |||
807 | my ($bin,$xbin,$ybin); | |||
808 | ||||
809 | for my $mch (@matches) { | |||
810 | my ($i,$r,$pa) = @{$mch}{qw/ix r pa/}; | |||
811 | my $bin; | |||
812 | if($rhist && $r < $rmax) { | |||
813 | $bin = int($r/$rbin); | |||
814 | $rhist->[$bin]++; | |||
815 | } | |||
816 | if($pahist && $pa < $pamax) { | |||
817 | $bin = int($pa/$pabin); | |||
818 | $pahist->[$bin]++; | |||
819 | } | |||
820 | if($xyhist) { | |||
821 | my ($x,$y); | |||
822 | if(! $xy) { | |||
823 | $x = $r*cos($pa/$R2D) + $xymax; | |||
824 | $y = -$r*sin($pa/$R2D) + $xymax; | |||
825 | } else { | |||
826 | $x = $pixsz*$x + $xymax; | |||
827 | $y = $pixsz*$y + $xymax; | |||
828 | } | |||
829 | if($x>=0 && $x<2*$xymax && $y>=0 && $y<2*$xymax) { | |||
830 | $xbin = int($x/$xybin); | |||
831 | $ybin = int($y/$xybin); | |||
832 | $xyhist->[$ybin][$xbin]++; | |||
833 | } | |||
834 | } | |||
835 | } # for | |||
836 | ||||
837 | } # $hist | |||
838 | ||||
839 | } # @matches | |||
840 | ||||
841 | } # SEARCH | |||
842 | ||||
843 | $results{nmatches} = $nmatches; | |||
844 | $results{nmatched} = $nmatched; | |||
845 | $results{matched} = \@matched; | |||
846 | ||||
847 | if($hist) { | |||
848 | if($rhist && @$rhist) { $_||=0 for @$rhist; } | |||
849 | if($pahist && @$pahist) { $_||=0 for @$pahist; } | |||
850 | if($xyhist && @$xyhist) { $_||=0 for map { (@$_) } @$xyhist; } | |||
851 | } | |||
852 | ||||
853 | print "Posmatch: Matched $results{nmatched} source rows to ". | |||
854 | "$results{nmatches} target rows.\n" | |||
855 | if $verbose; | |||
856 | ||||
857 | return wantarray ? @matched : \%results; | |||
858 | } | |||
859 | ||||
860 | # Convert a sexagesimal (lon,lat) pair representation to decimal degrees | |||
861 | # spent 1.04s (271ms+768ms) within WISE::CoUtils::to_dd which was called 27739 times, avg 37µs/call:
# 27739 times (271ms+768ms) by WISE::CoUtils::cconv at line 123, avg 37µs/call | |||
862 | # v => is the lon in hours or degrees? | |||
863 | 55478 | 0.31894 | 5.7e-6 | my ($lon,$lat,$hord) = @_; |
864 | ||||
865 | (&hdms_dd($lon,$hord),&hdms_dd($lat,'d')); # spent 768ms making 55478 calls to WISE::CoUtils::hdms_dd, avg 14µs/call | |||
866 | } | |||
867 | ||||
868 | 1 | 1.2e-5 | 1.2e-5 | my $FPre = _fpre(); # spent 37µs making 1 call to WISE::CoUtils::_fpre |
869 | ||||
870 | # Convert a single lon or lat in sexagesimal hours or degrees | |||
871 | # spent 768ms within WISE::CoUtils::hdms_dd which was called 55478 times, avg 14µs/call:
# 55478 times (768ms+0) by WISE::CoUtils::to_dd at line 865, avg 14µs/call | |||
872 | 388346 | 0.64167 | 1.7e-6 | my ($x,$hord) = @_; |
873 | my ($hd,$m,$s,$pord,$hord2) = (); | |||
874 | ||||
875 | $hord = lc ($hord || "d"); | |||
876 | ||||
877 | # Expand compact hhmmss.ss or hh:mm:ss.ss format | |||
878 | $x =~ s/^\s*([-+]?)(\d\d?):(\d\d?):(\d\d?(?:\.\d*)?)/$1$2$hord$3m$4s/; | |||
879 | $x =~ s/^\s*([-+]?)(\d\d)(\d\d)(\d\d(?:\.\d*)?)/$1$2$hord$3m$4s/; | |||
880 | ||||
881 | if ($x =~ /^$FPre$/) { # F.P. dec. deg.; nothing to do | |||
882 | } elsif ($x =~ /[hdms]/) { # Sexigesimal | |||
883 | # Confirm correct hms or dms format | |||
884 | if($x =~ m'^\s* | |||
885 | ([-+])?\s*(?:(\d{1,3}(?:\.\d*)?)([hd]))?\s* | |||
886 | (?:(\d\d?(?:\.\d*)?)m)?\s* | |||
887 | (?:(\d\d?(?:\.\d*)?)s)? | |||
888 | \s*$ 'xi) { | |||
889 | $pord = $1 || "+"; | |||
890 | $hd = $2 || 0; | |||
891 | # detect hord from format or from variable | |||
892 | $hord = ($3 && lc $3) || $hord; | |||
893 | $m = $4 || 0; | |||
894 | $s = $5 || 0; | |||
895 | $x = ($hd + $m/60. + $s/3600.) * | |||
896 | ($hord eq "h" ? 15 : 1) * | |||
897 | ($pord eq "-" ? -1 : 1); | |||
898 | } else { undef $x; } # Bad hms format | |||
899 | } else { # Not recognizeable | |||
900 | undef $x; | |||
901 | } | |||
902 | ||||
903 | return $x; | |||
904 | } | |||
905 | ||||
906 | # Convert a (lon,lat) pair in decimal degrees to sexagesimal representation. | |||
907 | # Three decimal places of hours or two of degrees are kept. | |||
908 | sub to_sex { | |||
909 | # v => hhmmss.ss instead of hhH mmM ss.sssS | |||
910 | my ($lon,$lat,$hord,$brief) = @_; | |||
911 | ||||
912 | if($lon < 0) { $lon += 360.; } | |||
913 | (&dd_hdms($lon,3,$hord,$brief),&dd_hdms($lat,2,'d',$brief)); | |||
914 | } | |||
915 | ||||
916 | # Convert a single lon or lat in decimal hours or degrees to sexagesimal. | |||
917 | # Number of decimal places on sec.s is adjustable. | |||
918 | sub dd_hdms { | |||
919 | my($t,$dp,$hord,$brief) = @_; | |||
920 | my($h,$m,$s) = (0,0,0.0); | |||
921 | my($i,$sign,$str,$l,$cs); | |||
922 | ||||
923 | $dp = defined $dp?$dp:3; $hord = lc ($hord || 'h'); | |||
924 | $l = $dp+($dp?3:2); | |||
925 | ||||
926 | $sign = $t =~ /\s*([+-])/ ? $1 : "" ; | |||
927 | ||||
928 | $t = abs($t)/($hord eq 'h' ? 15 : 1)*3600.0; | |||
929 | $t = sprintf("%". | |||
930 | length(substr($t,0,(($i=index($t,"."))>=0?$i:length($t)))) . | |||
931 | ".${dp}f",$t); | |||
932 | ||||
933 | $h = int $t/3600.0; | |||
934 | $m = int (($t - $h*3600.0)/60.); | |||
935 | $s = $t - ($h*3600.0 + $m*60.0); | |||
936 | $cs = sprintf("%0$l.${dp}f",$s); | |||
937 | ||||
938 | if(! $brief) { | |||
939 | $str = sprintf("%s%02d%s%02dm${cs}s", $sign,$h,$hord,$m,$s); | |||
940 | } else { | |||
941 | $str = sprintf("%s%02d%02d${cs}", $sign,$h,$m,$s); | |||
942 | } | |||
943 | ||||
944 | return $str; | |||
945 | } | |||
946 | ||||
947 | # This is a copy of what's in Utils.pm, copied here to make | |||
948 | # this module smaller if other modules ar enot otherwise needed | |||
949 | ||||
950 | # Make undefined values visible/printable | |||
951 | sub _undefize { | |||
952 | my @x = map {defined $_ ? $_ : "<undef>"} @_; | |||
953 | return wantarray ? @x : $x[0]; | |||
954 | } | |||
955 | ||||
956 | # Regex to match a floating point number | |||
957 | # spent 37µs within WISE::CoUtils::_fpre which was called
# once (37µs+0) at line 868 | |||
958 | 7 | 2.7e-5 | 3.9e-6 | my $d = shift || ""; |
959 | $d = 'dD' if $d; # Is 'd' allowed instead of 'e' in the exponent? | |||
960 | my $re = | |||
961 | "(?:". | |||
962 | "[-+]?". # Optional_sign | |||
963 | "(?:". | |||
964 | "(?:(?:\\d*\\.?\\d+|\\d+\\.)(?:[eE$d][+-]?\\d+)?)|". # Normal_number | |||
965 | "(?:nanq?|inf(?:inity))". # Special_values | |||
966 | ")". | |||
967 | ")"; | |||
968 | $re =~ s/nanq/[Nn][Aa][Nn][Qq]/; # Ensure case insenstivity w/o using (?i) | |||
969 | $re =~ s/inf/[Ii][Nn][Ff]/; | |||
970 | $re =~ s/inity/[Ii][Nn][Ii][Tt][Yy]/; | |||
971 | return $re; | |||
972 | } | |||
973 | ||||
974 | 1 | 3.0e-5 | 3.0e-5 | 1; |