/usr/share/gnudatalanguage/astrolib/extast.pro is in gdl-astrolib 2018.02.16+dfsg-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 | pro extast,hdr,astr,noparams, alt=alt, Has_CPDIS = has_cpdis, HAS_D2IMDIS= has_d2imdis
;+
; NAME:
; EXTAST
; PURPOSE:
; Extract ASTrometry parameters from a FITS image header.
; EXPLANATION:
; Extract World Coordinate System information
; ( http://fits.gsfc.nasa.gov/fits_wcs.html ) from a FITS header and
; place it into an IDL structure.
;
; CALLING SEQUENCE:
; EXTAST, hdr, astr, [ noparams, ALT= ]
;
; INPUT:
; HDR - variable containing the FITS header (string array)
;
; OUTPUTS:
; In the following, index 1 & 2 refer to the first and second astrometry
; axes respectively. The actual axis numbers are stored in .AXIS
;
; ASTR - Anonymous structure containing astrometry info from the FITS
; header ASTR always contains the following tags (even though
; some projections do not require all the parameters)
; .NAXIS - 2 element array giving image size
; .CD - 2 x 2 array containing the astrometry parameters CD1_1 CD1_2
; in DEGREES/PIXEL CD2_1 CD2_2
; .CDELT - 2 element double vector giving physical increment at the
; reference pixel
; .CRPIX - 2 element double vector giving X and Y coordinates of reference
; pixel (def = NAXIS/2) in FITS convention (first pixel is 1,1)
; .CRVAL - 2 element double precision vector giving R.A. and DEC of
; reference pixel in DEGREES
; .CTYPE - 2 element string vector giving projection types, default
; ['RA---TAN','DEC--TAN']
; .LONGPOLE - scalar giving native longitude of the celestial pole
; (default = 180 for zenithal projections)
; .LATPOLE - scalar giving native latitude of the celestial pole default=0)
; .PV2 - Vector of projection parameters associated with latitude axis
; PV2 will have up to 21 elements for the ZPN projection, up to 3
; for the SIN projection and no more than 2 for any other
; projection
;
; Fields added for version 2:
; .PV1 - Vector of projection parameters associated with longitude axis
; .AXES - 2 element integer vector giving the FITS-convention axis
; numbers associated with astrometry, in ascending order.
; Default [1,2].
; .REVERSE - byte, true if first astrometry axis is Dec/latitude
; .COORD_SYS - 1 or 2 character code giving coordinate system, including
; 'C' = RA/Dec, 'G' = Galactic, 'E' = Ecliptic, 'X' = unknown.
; .PROJECTION - 3-letter WCS projection code
; .KNOWN - true if IDL WCS routines recognise this projection
; .RADECSYS - String giving RA/Dec system e.g. 'FK4', 'ICRS' etc.
; .EQUINOX - Double giving the epoch of the mean equator and equinox
; .DATEOBS - Text string giving (start) date/time of observations
; .MJDOBS - Modified julian date of start of observations.
; .X0Y0 - Implied offset in intermediate world coordinates (x,y)
; if a non-standard fiducial point is set via PV1 and also
; PV1_0a =/ 0, indicating that an offset should be
; applied to place CRVAL at the (x,y) origin.
; Should be *added* to the IWC derived from application of
; CRPIX, CDELT, CD to the pixel coordinates.
;
; .DISTORT - optional substructure specifying any distortion parameters
; currently implement only for "SIP" (Spitzer Imaging
; Polynomial), "TPV" (tangent PV* polynomial) distortion
; parameters, and "TNX" (tangent plus iraf distortion)
;
; NOPARAMS - Scalar indicating the results of EXTAST
; -1 = Failure - Header missing astrometry parameters
; 1 = Success - Header contains CROTA + CDELT (AIPS-type) astrometry
; 2 = Success - Header contains CDn_m astrometry, rec.
; 3 = Success - Header contains PCn_m + CDELT astrometry.
; 4 = Success - Header contains ST Guide Star Survey astrometry
; (see gsssextast.pro )
; OPTIONAL INPUT/OUTPUT KEYWORDS:
; ALT - single character 'A' through 'Z' or ' ' specifying an alternate
; astrometry system present in the FITS header. The default is
; to use the primary astrometry or ALT = ' '. If /ALT is set,
; then this is equivalent to ALT = 'A'. See Section 3.3 of
; Greisen & Calabretta (2002, A&A, 395, 1061) for information about
; alternate astrometry keywords. If not set on input, then
; ALT is set to ' ' on output.
; OPTIONAL OUTPUT KEYWORD:
; HAS_D2IMDIS = set to 1 if the FITS header includes a D2IMDISi keyword indicating that
; there is an array of (detector to image) pixel distortion corrections located in
; another extension. This use of a distortion lookup table is used by a couple of
; Hubble instruments. If this keyword is not supplied, but the header
; contains D2IMDISi keywords then a warning message will be supplied, since
; this distortion information will be missing from the astrometry structure.
; HAS_CPDIS = set to 1 if the FITS header includes a CPDISi keyword indicating that
; there is an array of pixel distortion corrections located in another extension.
; This use of a distortion lookup table is based on the draft proposal
; https://fits.gsfc.nasa.gov/wcs/dcs_20040422.pdf and is used by a couple of
; Hubble instruments. If this keyword is not supplied, but the header
; contains CPDISi keywords then a warning message will be supplied, since
; this distortion information will be missing from the astrometry structure.
; PROCEDURE:
; EXTAST checks for astrometry parameters in the following order:
;
; (1) the CD matrix PC1_1,PC1_2...plus CDELT*, CRPIX and CRVAL
; (2) the CD matrix CD1_1,CD1_2... plus CRPIX and CRVAL.
; (3) CROTA2 (or CROTA1) and CDELT plus CRPIX and CRVAL.
;
; All three forms are valid FITS according to the paper "Representations
; of World Coordinates in FITS by Greisen and Calabretta (2002, A&A, 395,
; 1061 http://fits.gsfc.nasa.gov/fits_wcs.html ) although form (1) is
; preferred.
;
; NOTES:
; 1. An anonymous structure is created to avoid structure definition
; conflicts. This is needed because some projection systems
; require additional dimensions (i.e. spherical cube
; projections require a specification of the cube face).
;
; 2, FITS headers created by SCAMP
; (http://www.astromatic.net/software/scamp) contain a tangent
; projection with distortion polynomial coefficients in the PV[1|2]_?
; keywords. These will be flagged as being a TPV projection
; (http://fits.gsfc.nasa.gov/registry/tpvwcs.html) in the
; astr.projection keyword.
;
; PROCEDURES CALLED:
; GSSSEXTAST, ZPARCHECK
; REVISION HISTORY
; Written by B. Boothman 4/15/86
; Accept CD001001 keywords 1-3-88
; Accept CD1_1, CD2_1... keywords W. Landsman Nov. 92
; Recognize GSSS FITS header W. Landsman June 94
; Get correct sign, when converting CDELT* to CD matrix for right-handed
; coordinate system W. Landsman November 1998
; Consistent conversion between CROTA and CD matrix October 2000
; CTYPE = 'PIXEL' means no astrometry params W. Landsman January 2001
; Don't choke if only 1 CTYPE value given W. Landsman August 2001
; Recognize PC00n00m keywords again (sigh...) W. Landsman December 2001
; Recognize GSSS in ctype also D. Finkbeiner Jan 2002
; Introduce ALT keyword W. Landsman June 2003
; Fix error introduced June 2003 where free-format values would be
; truncated if more than 20 characters. W. Landsman Aug 2003
; Further fix to free-format values -- slash need not be present Sep 2003
; Default value of LATPOLE is 90.0 W. Landsman February 2004
; Allow for distortion substructure, currently implemented only for
; SIP (Spitzer Imaging Polynomial) W. Landsman February 2004
; Correct LONGPOLE computation if CTYPE = ['*DEC','*RA'] W. L. Feb. 2004
; Assume since V5.3 (vector STRMID) W. Landsman Feb 2004
; Yet another fix to free-format values W. Landsman April 2004
; Introduce PV2 tag to replace PROJP1, PROJP2.. etc. W. Landsman May 2004
; Convert NCP projection to generalized SIN W. Landsman Aug 2004
; Add NAXIS tag to output structure W. Landsman Jan 2007
; .CRPIX tag now Double instead of Float W. Landsman Apr 2007
; If duplicate keywords use the *last* value W. Landsman Aug 2008
; Fix typo for AZP projection, nonzero longpole N. Cunningham Feb 2009
; Give warning if reverse SIP coefficient not present W. Landsman Nov 2011
; Allow obsolete CD matrix representations W. Landsman May 2012
; Work for Paritel headers with extra quotes R. Gutermuth/WL April 2013
;
; Version 2: J. P. Leahy, July 2013
; - Support long & lat axes not being the first 2.
; - Implemented PV1 and hence non-default phi0 and theta0
; - Added AXES, REVERSE, COORD_SYS, PROJECTION, RADECSYS, EQUINOX,
; DATEOBS, MJDOBS, PV1, and X0Y0 tags to the structure.
; - More checks for inconsistencies in the keywords.
; v2.1 21/7/13 Missing mjdobs & equinox changed to NaN (was -1 & 0);
; Converts GLS to SFL if possible; added KNOWN tag.
; v2.2 21/9/13 GLS conversion fixed.
; v2.3 1 Dec 13 Add warning if distortions from SCAMP astrometry present
; v2.4. Extract SCAMP or TPV distortion astrometry, if present Jan 2014
; v2.5 Fix bug when SIP parameters not recognized when NAXIS=0 May 2014
; v2.5.1 Make sure CROTA defined for GLS projection WL Sep 2015
; v2.5.2 Like V2.5.1 but also when CD matrix suppied WL May 2016
; v2.5.3 Add warning if CPDIS1 keyword present WL Nov 2016
; v2.5.4 Add HAS_CPDIS and HAS_D2IMDIS keywords WL Nov 2017
;-
compile_opt idl2
;
; List of known map types copied from wcsxy2sph. Needs to be kept up
; to date!
;
map_types=['DEF','AZP','TAN','SIN','STG','ARC','ZPN','ZEA','AIR','CYP',$
'CAR','MER','CEA','COP','COD','COE','COO','BON','PCO','SFL',$
'PAR','AIT','MOL','CSC','QSC','TSC','SZP','HPX','HCT','XPH']
if ( N_params() LT 2 ) then begin
print,'Syntax - EXTAST, hdr, astr, [ noparams, ALT =, HAS_CPDIS= ]'
return
endif
Catch, theError
IF theError NE 0 then begin
Catch,/Cancel
void = cgErrorMsg(/quiet)
RETURN
ENDIF
radeg = 180.0D0/!DPI
keyword = STRUPCASE(strtrim(strmid( hdr, 0, 8), 2))
; Extract values from the FITS header. This is either up to the first slash
; (free format) or first space
space = strpos( hdr, ' ', 10) + 1
slash = strpos( hdr, '/', 10) > space
N = N_elements(hdr)
len = (slash -10) > 20
len = reform(len,1,N)
lvalue = strtrim(strmid(hdr, 10, len),2)
remchar,lvalue,"'"
zparcheck,'EXTAST',hdr,1,7,1,'FITS image header' ;Make sure valid header
noparams = -1 ;Assume no astrometry to start
if N_elements(alt) EQ 0 then begin
alt = '' & altstr = ''
endif else BEGIN
if (alt EQ '1') then alt = 'A' else alt = strupcase(alt)
altstr = ' for alternate system '+alt
ENDELSE
; Search for astrometric axes:
test = STREGEX(keyword,'^CTYPE[1-9][0-9]{0,2}'+alt+'$', LENGTH = ctlen)
typ = WHERE(test GE 0, ntyp)
lon = -1 & lat = -1
lon_form = -1 & lat_form = -1
IF ntyp GT 0 THEN BEGIN
ctlen = ctlen[typ] - STRLEN('CTYPE'+alt) ; gives # digits in axis number
lon0 = WHERE(STRMID(lvalue[typ],0,5) EQ 'RA---')
lon1 = WHERE(STRMID(lvalue[typ],1,4) EQ 'LON-')
lon2 = WHERE(STRMID(lvalue[typ],2,4) EQ 'LN-')
lon = [lon0, lon1, lon2]
form = [REPLICATE(0,N_ELEMENTS(lon0)),REPLICATE(1,N_ELEMENTS(lon1)), $
REPLICATE(2,N_ELEMENTS(lon2))]
good = WHERE(lon GT 0, ngood)
IF ngood GT 1 THEN MESSAGE, /INFORMATIONAL, $
'Multiple longitude axes'+altstr+': Using last.'
lon = MAX(lon, subs)
lon_form = lon GE 0 ? form[subs] : -1
lat0 = WHERE(STRMID(lvalue[typ],0,5) EQ 'DEC--')
lat1 = WHERE(STRMID(lvalue[typ],1,4) EQ 'LAT-')
lat2 = WHERE(STRMID(lvalue[typ],2,4) EQ 'LT-')
lat = [lat0, lat1, lat2]
form = [REPLICATE(0,N_ELEMENTS(lat0)),REPLICATE(1,N_ELEMENTS(lat1)), $
REPLICATE(2,N_ELEMENTS(lat2))]
good = WHERE(lat GT 0, ngood)
IF ngood GT 1 THEN MESSAGE, /INFORMATIONAL, $
'Multiple latitude axes'+altstr+': Using last.'
lat = MAX(lat,subs)
lat_form = lat GE 0 ? form[subs] : -1
ENDIF
;
; Longitude axis data is initially stored in element 0 and latitude
; axis data in element 1 of the various arrays. For backwards compatibility,
; if latitude has a lower axis number in the FITS header, they will be swapped
; into the (latitude, longitude) order in the final structure, and the .REVERSE
; field will be set to true (ie. 1B).
;
lonc = lon GE 0 ? STRMID(keyword[typ[lon]],5,ctlen[lon]) : '1'
latc = lat GE 0 ? STRMID(keyword[typ[lat]],5,ctlen[lat]) : '2'
ctype = ['','']
l = where(keyword EQ 'CTYPE'+lonc+alt, N_ctype1)
if N_ctype1 GT 0 then ctype[0] = lvalue[l[N_ctype1-1]]
l = where(keyword EQ 'CTYPE'+latc+alt, N_ctype2)
if N_ctype2 GT 0 then ctype[1] = lvalue[l[N_ctype2-1]]
ctype = strtrim(ctype,2)
badco = lon_form NE lat_form
CASE lon_form OF
-1: coord = 'X' ; unknown type of coordinate
0: coord = 'C' ; celestial coords, i.e. RA/Dec
1: BEGIN ; longitude format is xLON where x = G, E, etc.
coord = STRMID(ctype[0],0,1)
badco = badco || coord NE STRMID(ctype[1],0,1)
END
2: BEGIN ; longitude format is yzLN
coord = STRMID(ctype[0],0,2)
badco = badco || coord NE STRMID(ctype[2],0,2)
END
ELSE: MESSAGE, 'Internal error: unexpected lon_form'
ENDCASE
naxis = lonarr(2)
l = where(keyword EQ 'NAXIS'+lonc, N_axis1)
if N_axis1 GT 0 then naxis[0] = lvalue[l[N_axis1-1]]
l = where(keyword EQ 'NAXIS'+latc, N_axis2)
if N_axis2 GT 0 then naxis[1] = lvalue[l[N_axis2-1]]
tpv = strmid(ctype[0],2,3,/reverse) EQ 'TPV'
tnx = strmid(ctype[0],2,3,/reverse) EQ 'TNX'
IF (TPV || tnx) THEN BEGIN
proj = 'TAN'
ENDIF ELSE BEGIN
proj = STRMID(ctype[0], 5, 3)
badco = badco || proj NE STRMID(ctype[1], 5, 3)
IF badco THEN BEGIN
MESSAGE, 'ERROR' + altstr + $
': longitude and latitude coordinate types must match:', /CONTINUE
MESSAGE, 'Coords were CTYPE'+lonc+alt+': ' + ctype[0] + $
'; CTYPE'+latc+alt+': ' + ctype[1]
ENDIF
; If the standard CTYPE* astrometry keywords not found, then check if the
; ST guidestar astrometry is present
check_gsss = (N_ctype1 EQ 0)
if N_ctype1 GE 1 then check_gsss = (strmid(ctype[0], 5, 3) EQ 'GSS')
if check_gsss then begin
l = where(keyword EQ 'PPO1'+alt, N_ppo1)
if N_ppo1 EQ 1 then begin
gsssextast, hdr, astr, gsssparams
if gsssparams EQ 0 then noparams = 4
return
endif
endif
if (ctype[0] EQ 'PIXEL') then return
if N_ctype2 EQ 1 then if (ctype[1] EQ 'PIXEL') then return
ENDELSE
crval = dblarr(2)
l = where(keyword EQ 'CRVAL'+lonc+alt, N_crval1)
if N_crval1 GT 0 then crval[0] = lvalue[l[N_crval1-1]]
l = where(keyword EQ 'CRVAL'+latc+alt, N_crval2)
if N_crval2 GT 0 then crval[1] = lvalue[l[N_crval2-1]]
if (N_crval1 EQ 0) || (N_crval2 EQ 0) then return
crpix = dblarr(2)
l = where(keyword EQ 'CRPIX'+lonc+alt, N_crpix1)
if N_crpix1 GT 0 then crpix[0] = lvalue[l[N_crpix1-1]]
l = where(keyword EQ 'CRPIX'+latc+alt, N_crpix2)
if N_crpix2 GT 0 then crpix[1] = lvalue[l[N_crpix2-1]]
if (N_crpix1 EQ 0) || (N_crpix2 EQ 0) then return
cd = dblarr(2,2)
cdelt = [1.0d,1.0d]
; Look for distortion keywords pointing to another FITS extension
cpdis1 = ''
l = where(keyword EQ 'CPDIS1', N_cpdis1)
if N_cpdis1 GT 0 then cpdis1 = strtrim(lvalue[l[N_cpdis1-1]],2)
haslookup = strupcase(cpdis1) EQ 'LOOKUP'
if arg_present(has_cpdis) then has_cpdis = haslookup else begin
if haslookup then begin
message, /inf, $
'Warning - FITS header may point to table lookup distortions (CPDIS1)'
message,/inf, 'Use FITS_XYAD or FITS_ADXY to include distortion lookup table'
endif
endelse
d2imdis1 = ''
l = where(keyword EQ 'D2IMDIS1', N_d2imdis1)
if N_d2imdis1 GT 0 then d2imdis1 = strtrim(lvalue[l[N_cpdis1-1]],2)
haslookup = strupcase(d2imdis1) EQ 'LOOKUP'
if arg_present(has_d2imdis) then has_d2imdis =haslookup else begin
if haslookup then begin
message, /inf, $
'Warning - FITS header may point to table lookup distortions (D2IMDIS1)'
message,/inf, 'Use FITS_XYAD or FITS_ADXY to include distortion lookup table'
endif
endelse
GET_CD_MATRIX:
l = where(keyword EQ 'PC'+lonc+'_'+lonc + alt, N_pc11)
if N_PC11 GT 0 then begin
cd[0,0] = lvalue[l]
l = where(keyword EQ 'PC'+lonc+'_'+latc + alt, N_pc12)
if N_pc12 GT 0 then cd[0,1] = lvalue[l[N_pc12-1]]
l = where(keyword EQ 'PC'+latc+'_'+lonc + alt, N_pc21)
if N_pc21 GT 0 then cd[1,0] = lvalue[l[N_pc21-1]]
l = where(keyword EQ 'PC'+latc+'_'+latc + alt, N_pc22)
if N_pc22 GT 0 then cd[1,1] = lvalue[l[N_pc22-1]]
l = where(keyword EQ 'CDELT'+lonc+ alt, N_cdelt1)
if N_cdelt1 GT 0 then cdelt[0] = lvalue[l[N_cdelt1-1]]
l = where(keyword EQ 'CDELT'+latc+ alt, N_cdelt2)
if N_cdelt2 GT 0 then cdelt[1] = lvalue[l[N_cdelt2-1]]
det = cd[0,0]*cd[1,1] - cd[0,1]*cd[1,0]
if det LT 0 then sgn = -1 else sgn = 1
crota = atan( sgn*cd[0,1], sgn*cd[0,0] )
noparams = 3
endif else begin
l = where(keyword EQ 'CD'+lonc+'_'+lonc + alt, N_cd11)
if N_CD11 GT 0 then begin ;If CD parameters don't exist, try CROTA
cd[0,0] = strtrim(lvalue[l[N_cd11-1]],2)
l = where(keyword EQ 'CD'+lonc+'_'+latc + alt, N_cd12)
if N_cd12 GT 0 then cd[0,1] = lvalue[l[N_cd12-1]]
l = where(keyword EQ 'CD'+latc+'_'+lonc + alt, N_cd21)
if N_cd21 GT 0 then cd[1,0] = lvalue[l[N_cd21-1]]
l = where(keyword EQ 'CD'+latc+'_'+latc + alt, N_cd22)
if N_cd22 GT 0 then cd[1,1] = lvalue[l[N_cd22-1]]
det = cd[0,0]*cd[1,1] - cd[0,1]*cd[1,0]
if det LT 0 then sgn = -1 else sgn = 1
crota = atan( sgn*cd[0,1], sgn*cd[0,0] )
noparams = 2
endif else begin
; Now get rotation, first try CROTA2, if not found try CROTA1, if that
; not found assume North-up. Then convert to CD matrix - see Section 5 in
; Greisen and Calabretta
l = where(keyword EQ 'CDELT'+lonc + alt, N_cdelt1)
if N_cdelt1 GT 0 then cdelt[0] = lvalue[l[N_cdelt1-1]]
l = where(keyword EQ 'CDELT'+latc + alt, N_cdelt2)
if N_cdelt2 GT 0 then cdelt[1] = lvalue[l[N_cdelt2-1]]
if (N_cdelt1 EQ 0) || (N_Cdelt2 EQ 0) then return
;Must have CDELT1 and CDELT2
l = where(keyword EQ 'CROTA'+latc + alt, N_crota)
if N_Crota EQ 0 then $
l = where(keyword EQ 'CROTA'+lonc + alt, N_crota)
if N_crota EQ 0 then begin
l = where(keyword EQ 'PC001001', N_PC00)
l = where(keyword EQ 'CD001001', N_CD00)
if (N_PC00 GT 0) || (N_CD00 GT 0) then begin
message,'Updating obsolete CD matrix representation',/INF
FITS_CD_FIX, hdr
keyword = strtrim(strmid(hdr,0,8),2)
goto, GET_CD_MATRIX
endif else crota = 0.0d
endif else crota = double(lvalue[l[N_crota-1]])/RADEG
cd = [ [cos(crota), -sin(crota)],[sin(crota), cos(crota)] ]
noparams = 1 ;Signal AIPS-type astrometry found
endelse
endelse
; Kluge to test for non-standard PVi_j distortion terms used by SCAMP
scamp_distort = 0b
if ~tpv && (proj EQ 'TAN') then $
tpv = ~array_equal(strmatch(keyword,'PV1_[5-9]'),0) && $ ;Updated 1-8-14
~array_equal(strmatch(keyword,'PV2_[3-9]'),0)
;Extract PV_* keywords. Special case for TPV distortion
if tpv then begin
g= where(strmatch(keyword,'PV1_*'), Ng)
key_pv1 = keyword[g]
temp = gettok(key_pv1,'_')
key_pv1 = fix(key_pv1)
pv1 = dblarr(max(key_pv1)+1)
pv1[key_pv1] = lvalue[g]
g= where(strmatch(keyword,'PV2_*'), Ng)
key_pv2 = keyword[g]
temp = gettok(key_pv2,'_')
key_pv2 = fix(key_pv2)
pv2 = dblarr(max(key_pv2)+1) ;Corrected 13-Jan-2014
pv2[key_pv2] = lvalue[g]
latpole = 90.0D
longpole = 180.0D
known = 1.0
x0y0 = [0d0, 0d0]
distort_flag = 'TPV'
ENDIF ELSE BEGIN
;; extract the tnx coefficients from the WAT keywords
IF(tnx)THEN BEGIN
g=where(strmatch(keyword,'WAT1_*'),Ng)
key_wat1=keyword[g]
val_wat1=STRTRIM(strmid(hdr[g], 10),2)
remchar,val_wat1,"'"
remchar,val_wat1,'"'
remchar,val_wat1,'/'
temp=STRMID(key_wat1,0,3,/REVERSE)
s=SORT(temp)
val_wat1=val_wat1[s]
val_wat1=STRJOIN(val_wat1)
val_wat1=STRSPLIT(val_wat1,/EXTRACT)
g=where(strmatch(keyword,'WAT2_*'),Ng)
key_wat2=keyword[g]
val_wat2=STRTRIM(strmid(hdr[g], 10),2)
remchar,val_wat2,"'"
remchar,val_wat2,'"'
remchar,val_wat2,'/'
temp=STRMID(key_wat2,0,3,/REVERSE)
s=SORT(temp)
val_wat2=val_wat2[s]
val_wat2=STRJOIN(val_wat2)
val_wat2=STRSPLIT(val_wat2,/EXTRACT)
IF(val_wat1[2] NE 'lngcor' || val_wat2[2] NE 'latcor')THEN BEGIN
MESSAGE,'WARNING: TNX projection parameters not parsed correctly',/CON
ctype = ['RA---TAN','DEC--TAN']
tnx=0
ENDIF
IF(val_wat1[4] NE 3 || val_wat2[4] NE 3)THEN BEGIN
MESSAGE,'WARNING - only polynomials supported for TNX projection.',/CON
ctype = ['RA---TAN','DEC--TAN']
tnx=0
ENDIF
IF(tnx)THEN BEGIN
;; tnx coefficients get stored in two structures
ncoeff=N_ELEMENTS(val_wat1)-12
lngcor={functype:0,xiorder:0,etaorder:0,xterms:0,ximin:0d0,ximax:0d0,etamin:0d0,etamax:0d0,coeff:DBLARR(ncoeff)}
lngcor.functype=FIX(val_wat1[4])
lngcor.xiorder=FIX(val_wat1[5])
lngcor.etaorder=FIX(val_wat1[6])
lngcor.xterms=FIX(val_wat1[7])
lngcor.ximin=DOUBLE(val_wat1[8])
lngcor.ximax=DOUBLE(val_wat1[9])
lngcor.etamin=DOUBLE(val_wat1[10])
lngcor.etamax=DOUBLE(val_wat1[11])
lngcor.coeff=DOUBLE(val_wat1[12:*])
ncoeff=N_ELEMENTS(val_wat2)-12
latcor={functype:0,xiorder:0,etaorder:0,xterms:0,ximin:0d0,ximax:0d0,etamin:0d0,etamax:0d0,coeff:DBLARR(ncoeff)}
latcor.functype=FIX(val_wat2[4])
latcor.xiorder=FIX(val_wat2[5])
latcor.etaorder=FIX(val_wat2[6])
latcor.xterms=FIX(val_wat2[7])
latcor.ximin=DOUBLE(val_wat2[8])
latcor.ximax=DOUBLE(val_wat2[9])
latcor.etamin=DOUBLE(val_wat2[10])
latcor.etamax=DOUBLE(val_wat2[11])
latcor.coeff=DOUBLE(val_wat2[12:*])
distort_flag = 'TNX'
ENDIF ELSE distort_flag=''
ENDIF ELSE BEGIN
distort_flag = strlen(ctype[0]) GE 12 ? strmid(ctype[0],9,3) : ''
ENDELSE
case proj of
'ZPN': npv = 21
'SZP': npv = 3
else: npv = 2
endcase
index = proj EQ 'ZPN' ? strtrim(indgen(npv),2) : strtrim(indgen(npv)+1,2)
pv2 = dblarr(npv)
if proj EQ 'HPX' then pv2[0] = [4.d,3.d] ;Default for Healpix
for i=0,npv-1 do begin
l = where(keyword EQ 'PV'+latc+ '_' + index[i] + alt, N_pv2)
if N_pv2 GT 0 then pv2[i] = lvalue[l[N_pv2-1]]
endfor
pv1 = DBLARR(5)
pv1_set = BYTARR(5)
FOR i=0,4 DO BEGIN
l = WHERE(keyword EQ 'PV'+lonc+'_' + STRTRIM(i,2) + alt, N_pv1)
pv1_set[i] = N_pv1 GT 0
IF pv1_set[i] THEN pv1[i] = DOUBLE(lvalue[l[N_pv1-1]])
ENDFOR
xyoff = pv1[0] NE 0d0
phi0 = pv1[1]
if pv1_set[2] THEN theta0 = pv1[2]
if pv1_set[3] then longpole = pv1[3] else begin
l = where(keyword EQ 'LONPOLE' + alt, N_lonpole)
if N_lonpole GT 0 then longpole = double(lvalue[l[N_lonpole-1]])
endelse
if pv1_set[4] then latpole = pv1[4] else begin
l = where(keyword EQ 'LATPOLE' + alt, N_latpole)
latpole = N_latpole GT 0 ? double(lvalue[l[N_latpole-1]]) : 90d0
endelse
; Convert NCP projection to generalized SIN projection (see Section 6.1.2 of
; Calabretta and Greisen (2002)
if proj EQ 'NCP' then begin
ctype = repstr(ctype,'NCP','SIN')
proj = 'SIN'
PV2 = [0d0, 1d0/tan(crval[1]/radeg) ]
longpole = 180d0
endif
; Convert GLS projection (Sect 6.1.4, ibid), but per e-mail from Mark
; Calabretta the correction to CRPIX and CRVAL should only be applied
; to the second axis.
IF proj EQ 'GLS' THEN BEGIN
IF crota EQ 0d0 THEN BEGIN
crpix[1] -= crval[1]/cdelt[1] ; Shift reference point to dec = 0
crval[1] = 0d0
ctype = repstr(ctype,'GLS','SFL')
proj = 'SFL'
ENDIF
ENDIF
test = WHERE(proj EQ map_types)
known = test GE 0
; If LONPOLE (or PV1_3) is not defined in the header, then we must determine
; its default value. This depends on the value of theta0 (the native
; longitude of the fiducial point) of the particular projection)
conic = (proj EQ 'COP') || (proj EQ 'COE') || (proj EQ 'COD') || $
(proj EQ 'COO')
IF conic THEN BEGIN
IF N_pv2 EQ 0 THEN message, $
'ERROR -- Conic projections require a PV2_1 keyword in FITS header'
theta_a = pv2[0]
ENDIF ELSE BEGIN ; Is it a zenithal projection?
if (proj EQ 'AZP') || (proj EQ 'SZP') || (proj EQ 'TAN') || $
(proj EQ 'STG') || (proj EQ 'SIN') || (proj EQ 'ARC') || $
(proj EQ 'ZPN') || (proj EQ 'ZEA') || (proj EQ 'AIR') || $
(proj EQ 'XPH') then begin
theta_a = 90d0
endif else theta_a = 0d0
ENDELSE
IF ~pv1_set[2] THEN BEGIN
theta0 = theta_a
pv1[2] = theta_a
ENDIF
if N_elements(longpole) EQ 0 then begin
if crval[1] GE theta0 then longpole = 0d0 else longpole = 180d0
if pv1_set[1] THEN longpole += phi0
endif
pv1[3] = longpole
pv1[4] = latpole
IF xyoff && (phi0 NE 0d0 || theta0 NE theta_a) THEN BEGIN
; calculate IWC offsets x_0, y_0
WCSSPH2XY, phi0, theta0, x0, y0, CTYPE = ctype, PV2 = pv2
x0y0 = [x0, y0]
ENDIF ELSE x0y0 = [0d0, 0d0]
ENDELSE
axes = FIX([lonc,latc])
flip = axes[0] GT axes[1]
IF flip THEN BEGIN
naxis = REVERSE(naxis)
axes = REVERSE(axes)
cdelt = REVERSE(cdelt)
crpix = REVERSE(crpix)
crval = REVERSE(crval)
ctype = REVERSE(ctype)
cd = ROTATE(cd,2)
x0y0 = REVERSE(x0y0)
ENDIF
equinox = GET_EQUINOX( hdr,eq_code, ALT = alt)
IF equinox EQ 0 THEN equinox = !values.D_NAN
radecsys = ''
mjdobs = !values.D_NAN
dateobs = 'UNKNOWN'
l = WHERE(keyword EQ 'RADESYS' + alt, N_rdsys)
IF N_rdsys GT 0 THEN radecsys = lvalue[l[N_rdsys-1]] ELSE BEGIN
l = WHERE(keyword EQ 'RADECSYS', N_rdsys)
IF N_rdsys GT 0 THEN radecsys = lvalue[l[N_rdsys-1]]
ENDELSE
IF N_rdsys GT 0 THEN radecsys = STRUPCASE(STRTRIM(radecsys,2))
l = WHERE(keyword EQ 'MJD-OBS', N_mjd)
IF N_mjd GT 0 THEN mjdobs = DOUBLE(lvalue[l[N_mjd-1]])
l = WHERE(keyword EQ 'DATE-OBS', N_date)
IF N_date GT 0 THEN dateobs = STRUPCASE(lvalue[l[N_date-1]])
IF N_mjd GT 0 && N_date EQ 0 THEN dateobs = date_conv(mjdobs+2400000.5d0,'FITS')
IF N_date GT 0 THEN BEGIN
; try to convert to standard format:
dateobs = date_conv(dateobs,'FITS', BAD_DATE=bad_date)
IF ~bad_date THEN BEGIN
mjdtest = date_conv(dateobs,'MODIFIED')
IF N_mjd EQ 0 THEN mjdobs = mjdtest ELSE $
IF ABS(mjdtest - mjdobs) GT 1 THEN MESSAGE, $
'DATE-OBS and MJD-OBS are inconsistent'
ENDIF ELSE dateobs = 'UNKNOWN'
ENDIF
IF (coord EQ 'C' || coord EQ 'E' || coord EQ 'H') THEN BEGIN
IF N_rdsys EQ 0 THEN CASE eq_code OF
-1: radecsys = 'ICRS' ; default if no header info.
0: radecsys = equinox GE 1984d0 ? 'FK5' : 'FK4'
1: radecsys = equinox GE 1984d0 ? 'FK5' : 'FK4'
2: radecsys = 'FK4'
3: radecsys = 'FK5'
4: ; shouldn't get here as implies radecsys exists.
else: MESSAGE, 'Internal error: unrecognised eq_code'
ENDCASE
ENDIF
; Note that the dimensions and datatype of each tag must be explicit, so that
; there is no conflict with structure definitions from different FITS headers
ASTR = {NAXIS:naxis, CD: cd, CDELT: cdelt, CRPIX: crpix, CRVAL: crval, $
CTYPE: string(ctype), $
LONGPOLE: double( longpole[0]), LATPOLE: double(latpole[0]), $
PV2: pv2, PV1: pv1, $
AXES: axes, REVERSE: flip, $
COORD_SYS: coord, PROJECTION: proj, KNOWN: known, $
RADECSYS: radecsys, EQUINOX: DOUBLE(equinox), $
DATEOBS: dateobs, MJDOBS: DOUBLE(mjdobs), X0Y0: x0y0}
; Check for any distortion keywords
case distort_flag of
'SIP': begin
l = where(keyword EQ 'A_ORDER', N)
if N GT 0 then a_order = lvalue[l[N-1]] else a_order = 0
l = where(keyword EQ 'B_ORDER', N)
if N GT 0 then b_order = lvalue[l[N-1]] else b_order = 0
l = where(keyword EQ 'AP_ORDER', N)
if N GT 0 then ap_order = lvalue[l[N-1]] else ap_order = 0
l = where(keyword EQ 'BP_ORDER', N)
if N GT 0 then bp_order = lvalue[l[N-1]] else bp_order = 0
a = dblarr(a_order+1,a_order+1)
b = dblarr(b_order+1,b_order+1)
ap = dblarr(ap_order+1,ap_order+1)
bp = dblarr(bp_order+1,bp_order+1)
for i=0, a_order do begin
for j=0, a_order do begin
l = where(keyword EQ 'A_' + strtrim(i,2) + '_' + strtrim(j,2), N)
if N GT 0 then a[i,j] = lvalue[l[N-1]]
endfor
endfor
for i=0, b_order do begin
for j=0, b_order do begin
l = where(keyword EQ 'B_' + strtrim(i,2) + '_' + strtrim(j,2), N)
if N GT 0 then b[i,j] = lvalue[l[N-1]]
endfor
endfor
for i=0, bp_order do begin
for j=0, bp_order do begin
l = where(keyword EQ 'BP_' + strtrim(i,2) + '_' + strtrim(j,2), N)
if N GT 0 then bp[i,j] = lvalue[l[N-1]]
endfor
endfor
for i=0, ap_order do begin
for j=0, ap_order do begin
l = where(keyword EQ 'AP_' + strtrim(i,2) + '_' + strtrim(j,2), N)
if N GT 0 then ap[i,j] = lvalue[l[N-1]]
endfor
endfor
distort = {name:distort_flag, a:a, b:b, ap:ap, bp:bp}
astr = create_struct(temporary(astr), 'distort', distort)
end
'TPV': begin
distort = {name:'TPV', a:0.0d, b:0.0d, ap:0.0d, bp:0.0d}
astr = create_struct(temporary(astr), 'distort', distort)
end
'TNX' : begin
distort = {name:'TNX', lngcor:lngcor, latcor:latcor}
astr = create_struct(temporary(astr), 'distort', distort)
end
'':
else: message,/con,'Unrecognized distortion acronym: ' + distort_flag
endcase
return
end
|