/*============================================================================ WCSLIB 4.3 - an implementation of the FITS WCS standard. Copyright (C) 1995-2007, Mark Calabretta This file is part of WCSLIB. WCSLIB is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with WCSLIB. If not, see . Correspondence concerning WCSLIB may be directed to: Internet email: mcalabre@atnf.csiro.au Postal address: Dr. Mark Calabretta Australia Telescope National Facility, CSIRO PO Box 76 Epping NSW 1710 AUSTRALIA Author: Mark Calabretta, Australia Telescope National Facility http://www.atnf.csiro.au/~mcalabre/index.html $Id: wcsfix.c,v 4.3 2007/12/27 05:41:36 cal103 Exp $ *===========================================================================*/ #include #include #include #include #include "wcsmath.h" #include "sph.h" #include "wcs.h" #include "wcsunits.h" #include "wcsfix.h" extern const int WCSSET; /* Maximum number of coordinate axes that can be handled. */ #define NMAX 16 /* Map status return value to message. */ const char *wcsfix_errmsg[] = { "Success", "Null wcsprm pointer passed", "Memory allocation failed", "Linear transformation matrix is singular", "Inconsistent or unrecognized coordinate axis types", "Invalid parameter value", "Invalid coordinate transformation parameters", "Ill-conditioned coordinate transformation parameters", "All of the corner pixel coordinates are invalid", "Could not determine reference pixel coordinate", "Could not determine reference pixel value"}; /*--------------------------------------------------------------------------*/ int wcsfix(int ctrl, const int naxis[], struct wcsprm *wcs, int stat[]) { int status = 0; if ((stat[CDFIX] = cdfix(wcs)) > 0) { status = 1; } if ((stat[DATFIX] = datfix(wcs)) > 0) { status = 1; } if ((stat[UNITFIX] = unitfix(ctrl, wcs)) > 0) { status = 1; } if ((stat[CELFIX] = celfix(wcs)) > 0) { status = 1; } if ((stat[SPCFIX] = spcfix(wcs)) > 0) { status = 1; } if (naxis) { if ((stat[CYLFIX] = cylfix(naxis, wcs)) > 0) { status = 1; } } else { stat[CYLFIX] = -2; } return status; } /*--------------------------------------------------------------------------*/ int cdfix(struct wcsprm *wcs) { int i, k, naxis, status = -1; double *cd; if (wcs == 0x0) return 1; if ((wcs->altlin & 1) || !(wcs->altlin & 2)) { /* Either we have PCi_ja or there are no CDi_ja. */ return -1; } naxis = wcs->naxis; status = -1; for (i = 0; i < naxis; i++) { /* Row of zeros? */ cd = wcs->cd + i * naxis; for (k = 0; k < naxis; k++, cd++) { if (*cd != 0.0) goto next; } /* Column of zeros? */ cd = wcs->cd + i; for (k = 0; k < naxis; k++, cd += naxis) { if (*cd != 0.0) goto next; } cd = wcs->cd + i * (naxis + 1); *cd = 1.0; status = 0; next: ; } return status; } /*--------------------------------------------------------------------------*/ int datfix(struct wcsprm *wcs) { char *dateobs; int day, dd, hour = 0, jd, minute = 0, month, msec, n4, year; double mjdobs, sec = 0.0, t; if (wcs == 0x0) return 1; dateobs = wcs->dateobs; if (dateobs[0] == '\0') { if (undefined(wcs->mjdobs)) { /* No date information was provided. */ return -1; } else { /* Calendar date from MJD. */ jd = 2400001 + (int)wcs->mjdobs; n4 = 4*(jd + ((2*((4*jd - 17918)/146097)*3)/4 + 1)/2 - 37); dd = 10*(((n4-237)%1461)/4) + 5; year = n4/1461 - 4712; month = (2 + dd/306)%12 + 1; day = (dd%306)/10 + 1; sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day); /* Write time part only if non-zero. */ if ((t = wcs->mjdobs - (int)wcs->mjdobs) > 0.0) { t *= 24.0; hour = (int)t; t = 60.0 * (t - hour); minute = (int)t; sec = 60.0 * (t - minute); /* Round to 1ms. */ dd = 60000*(60*hour + minute) + (int)(1000*(sec+0.0005)); hour = dd / 3600000; dd -= 3600000 * hour; minute = dd / 60000; msec = dd - 60000 * minute; sprintf(dateobs+10, "T%.2d:%.2d:%.2d", hour, minute, msec/1000); /* Write fractions of a second only if non-zero. */ if (msec%1000) { sprintf(dateobs+19, ".%.3d", msec%1000); } } return 0; } } else { if (strlen(dateobs) < 8) { /* Can't be a valid date. */ return 5; } /* Identify the date format. */ if (dateobs[4] == '-' && dateobs[7] == '-') { /* Standard year-2000 form: CCYY-MM-DD[Thh:mm:ss[.sss...]] */ if (sscanf(dateobs, "%4d-%2d-%2d", &year, &month, &day) < 3) { return 5; } if (dateobs[10] == 'T') { if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) < 3) { return 5; } } else if (dateobs[10] == ' ') { if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) == 3) { dateobs[10] = 'T'; } else { hour = 0; minute = 0; sec = 0.0; } } } else if (dateobs[4] == '/' && dateobs[7] == '/') { /* Also allow CCYY/MM/DD[Thh:mm:ss[.sss...]] */ if (sscanf(dateobs, "%4d/%2d/%2d", &year, &month, &day) < 3) { return 5; } if (dateobs[10] == 'T') { if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) < 3) { return 5; } } else if (dateobs[10] == ' ') { if (sscanf(dateobs+11, "%2d:%2d:%lf", &hour, &minute, &sec) == 3) { dateobs[10] = 'T'; } else { hour = 0; minute = 0; sec = 0.0; } } /* Looks ok, fix it up. */ dateobs[4] = '-'; dateobs[7] = '-'; } else { if (dateobs[2] == '/' && dateobs[5] == '/') { /* Old format date: DD/MM/YY, also allowing DD/MM/CCYY. */ if (sscanf(dateobs, "%2d/%2d/%4d", &day, &month, &year) < 3) { return 5; } } else if (dateobs[2] == '-' && dateobs[5] == '-') { /* Also recognize DD-MM-YY and DD-MM-CCYY */ if (sscanf(dateobs, "%2d-%2d-%4d", &day, &month, &year) < 3) { return 5; } } else { /* Not a valid date format. */ return 5; } if (year < 100) year += 1900; /* Doesn't have a time. */ sprintf(dateobs, "%.4d-%.2d-%.2d", year, month, day); } /* Compute MJD. */ mjdobs = (double)((1461*(year - (12-month)/10 + 4712))/4 + (306*((month+9)%12) + 5)/10 - (3*((year - (12-month)/10 + 4900)/100))/4 + day - 2399904) + (hour + (minute + sec/60.0)/60.0)/24.0; if (undefined(wcs->mjdobs)) { wcs->mjdobs = mjdobs; } else { /* Check for consistency. */ if (fabs(mjdobs - wcs->mjdobs) > 0.5) { return 5; } } } return 0; } /*--------------------------------------------------------------------------*/ int unitfix(int ctrl, struct wcsprm *wcs) { int i, status = -1; if (wcs == 0x0) return 1; for (i = 0; i < wcs->naxis; i++) { if (wcsutrn(ctrl, wcs->cunit[i]) == 0) status = 0; } return status; } /*--------------------------------------------------------------------------*/ int celfix(struct wcsprm *wcs) { int k, status; struct celprm *wcscel = &(wcs->cel); struct prjprm *wcsprj = &(wcscel->prj); /* Initialize if required. */ if (wcs == 0x0) return 1; if (wcs->flag != WCSSET) { if ((status = wcsset(wcs))) return status; } /* Was an NCP or GLS projection code translated? */ if (wcs->lat >= 0) { /* Check ctype. */ if (strcmp(wcs->ctype[wcs->lat]+5, "NCP") == 0) { strcpy(wcs->ctype[wcs->lng]+5, "SIN"); strcpy(wcs->ctype[wcs->lat]+5, "SIN"); if (wcs->npvmax < wcs->npv + 2) { /* Allocate space for two more PVi_ja keyvalues. */ if (wcs->m_flag == WCSSET && wcs->pv == wcs->m_pv) { if (!(wcs->pv = calloc(wcs->npv+2, sizeof(struct pvcard)))) { wcs->pv = wcs->m_pv; return 2; } wcs->npvmax = wcs->npv + 2; wcs->m_flag = WCSSET; for (k = 0; k < wcs->npv; k++) { wcs->pv[k] = wcs->m_pv[k]; } if (wcs->m_pv) free(wcs->m_pv); wcs->m_pv = wcs->pv; } else { return 2; } } wcs->pv[wcs->npv].i = wcs->lat + 1; wcs->pv[wcs->npv].m = 1; wcs->pv[wcs->npv].value = wcsprj->pv[1]; (wcs->npv)++; wcs->pv[wcs->npv].i = wcs->lat + 1; wcs->pv[wcs->npv].m = 2; wcs->pv[wcs->npv].value = wcsprj->pv[2]; (wcs->npv)++; return 0; } else if (strcmp(wcs->ctype[wcs->lat]+5, "GLS") == 0) { strcpy(wcs->ctype[wcs->lng]+5, "SFL"); strcpy(wcs->ctype[wcs->lat]+5, "SFL"); return 0; } } return -1; } /*--------------------------------------------------------------------------*/ int spcfix(struct wcsprm *wcs) { char *scode; int i, status; /* Initialize if required. */ if (wcs == 0x0) return 1; if (wcs->flag != WCSSET) { if ((status = wcsset(wcs))) return status; } if ((i = wcs->spec) < 0) { /* Look for a linear spectral axis. */ for (i = 0; i < wcs->naxis; i++) { if (wcs->types[i]/100 == 30) { break; } } if (i >= wcs->naxis) { /* No spectral axis. */ return -1; } } /* Was an AIPS-convention spectral type translated? */ scode = wcs->ctype[i] + 4; if (strcmp(scode, "-LSR") == 0) { strcpy(wcs->specsys, "LSRK"); } else if (strcmp(scode, "-HEL") == 0) { strcpy(wcs->specsys, "BARYCENT"); } else if (strcmp(scode, "-OBS") == 0) { strcpy(wcs->specsys, "TOPOCENT"); } else { return -1; } strcpy(scode, "\0\0\0\0"); if (strcmp(wcs->ctype[i], "FELO") == 0) { strcpy(wcs->ctype[i], "VOPT-F2W"); } return 0; } /*--------------------------------------------------------------------------*/ int cylfix(const int naxis[], struct wcsprm *wcs) { unsigned short icnr, indx[NMAX], ncnr; int j, k, stat[4], status; double img[4][NMAX], lat, lng, phi[4], phi0, phimax, phimin, pix[4][NMAX], *pixj, theta[4], theta0, world[4][NMAX], x, y; /* Initialize if required. */ if (wcs == 0x0) return 1; if (wcs->flag != WCSSET) { if ((status = wcsset(wcs))) return status; } /* Check that we have a cylindrical projection. */ if (wcs->cel.prj.category != CYLINDRICAL) return -1; if (wcs->naxis < 2) return -1; /* Compute the native longitude in each corner of the image. */ ncnr = 1 << wcs->naxis; for (k = 0; k < NMAX; k++) { indx[k] = 1 << k; } status = 0; phimin = 1.0e99; phimax = -1.0e99; for (icnr = 0; icnr < ncnr;) { /* Do four corners at a time. */ for (j = 0; j < 4; j++, icnr++) { pixj = pix[j]; for (k = 0; k < wcs->naxis; k++) { if (icnr & indx[k]) { *(pixj++) = naxis[k] + 0.5; } else { *(pixj++) = 0.5; } } } if (!(status = wcsp2s(wcs, 4, NMAX, pix[0], img[0], phi, theta, world[0], stat))) { for (j = 0; j < 4; j++) { if (phi[j] < phimin) phimin = phi[j]; if (phi[j] > phimax) phimax = phi[j]; } } } if (phimin > phimax) return status; /* Any changes needed? */ if (phimin >= -180.0 && phimax <= 180.0) return -1; /* Compute the new reference pixel coordinates. */ phi0 = (phimin + phimax) / 2.0; theta0 = 0.0; if ((status = prjs2x(&(wcs->cel.prj), 1, 1, 1, 1, &phi0, &theta0, &x, &y, stat))) { return (status == 2) ? 5 : 9; } for (k = 0; k < wcs->naxis; k++) { img[0][k] = 0.0; } img[0][wcs->lng] = x; img[0][wcs->lat] = y; if ((status = linx2p(&(wcs->lin), 1, 0, img[0], pix[0]))) { return status; } /* Compute celestial coordinates at the new reference pixel. */ if ((status = wcsp2s(wcs, 1, 0, pix[0], img[0], phi, theta, world[0], stat))) { return status == 8 ? 10 : status; } /* Compute native coordinates of the celestial pole. */ lng = 0.0; lat = 90.0; (void)sphs2x(wcs->cel.euler, 1, 1, 1, 1, &lng, &lat, phi, theta); wcs->crpix[wcs->lng] = pix[0][wcs->lng]; wcs->crpix[wcs->lat] = pix[0][wcs->lat]; wcs->crval[wcs->lng] = world[0][wcs->lng]; wcs->crval[wcs->lat] = world[0][wcs->lat]; wcs->lonpole = phi[0] - phi0; return wcsset(wcs); }