summaryrefslogtreecommitdiffstats
path: root/funtools/wcs/wcsinit.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-10-25 20:57:49 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-10-25 20:57:49 (GMT)
commitd1c4bf158203c4e8ec29fdeb83fd311e36320885 (patch)
tree15874534e282f67505ce4af5ba805a1ff70ec43e /funtools/wcs/wcsinit.c
parente19a18e035dc4d0e8e215f9b452bb9ef6f58b9d7 (diff)
parent339420dd5dd874c41f6bab5808291fb4036dd022 (diff)
downloadblt-d1c4bf158203c4e8ec29fdeb83fd311e36320885.zip
blt-d1c4bf158203c4e8ec29fdeb83fd311e36320885.tar.gz
blt-d1c4bf158203c4e8ec29fdeb83fd311e36320885.tar.bz2
Merge commit '339420dd5dd874c41f6bab5808291fb4036dd022' as 'funtools'
Diffstat (limited to 'funtools/wcs/wcsinit.c')
-rw-r--r--funtools/wcs/wcsinit.c1611
1 files changed, 1611 insertions, 0 deletions
diff --git a/funtools/wcs/wcsinit.c b/funtools/wcs/wcsinit.c
new file mode 100644
index 0000000..134c1bf
--- /dev/null
+++ b/funtools/wcs/wcsinit.c
@@ -0,0 +1,1611 @@
+/*** File libwcs/wcsinit.c
+ *** October 19, 2012
+ *** By Jessica Mink, jmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1998-2012
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+ This library 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 2 of the License, or (at your option) any later version.
+
+ This library 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 this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Correspondence concerning WCSTools should be addressed as follows:
+ Internet email: jmink@cfa.harvard.edu
+ Postal address: Jessica Mink
+ Smithsonian Astrophysical Observatory
+ 60 Garden St.
+ Cambridge, MA 02138 USA
+
+ * Module: wcsinit.c (World Coordinate Systems)
+ * Purpose: Convert FITS WCS to pixels and vice versa:
+ * Subroutine: wcsinit (hstring) sets a WCS structure from an image header
+ * Subroutine: wcsninit (hstring,lh) sets a WCS structure from fixed-length header
+ * Subroutine: wcsinitn (hstring, name) sets a WCS structure for specified WCS
+ * Subroutine: wcsninitn (hstring,lh, name) sets a WCS structure for specified WCS
+ * Subroutine: wcsinitc (hstring, mchar) sets a WCS structure if multiple
+ * Subroutine: wcsninitc (hstring,lh,mchar) sets a WCS structure if multiple
+ * Subroutine: wcschar (hstring, name) returns suffix for specifed WCS
+ * Subroutine: wcseq (hstring, wcs) set radecsys and equinox from image header
+ * Subroutine: wcseqm (hstring, wcs, mchar) set radecsys and equinox if multiple
+ */
+
+#include <string.h> /* strstr, NULL */
+#include <stdio.h> /* stderr */
+#include <math.h>
+#include "wcs.h"
+#ifndef VMS
+#include <stdlib.h>
+#endif
+
+static void wcseq();
+static void wcseqm();
+static void wcsioset();
+void wcsrotset();
+char wcschar();
+
+/* set up a WCS structure from a FITS image header lhstring bytes long
+ * for a specified WCS name */
+
+struct WorldCoor *
+wcsninitn (hstring, lhstring, name)
+
+const char *hstring; /* character string containing FITS header information
+ in the format <keyword>= <value> [/ <comment>] */
+int lhstring; /* Length of FITS header in bytes */
+const char *name; /* character string with identifying name of WCS */
+{
+ hlength (hstring, lhstring);
+ return (wcsinitn (hstring, name));
+}
+
+
+/* set up a WCS structure from a FITS image header for specified WCSNAME */
+
+struct WorldCoor *
+wcsinitn (hstring, name)
+
+const char *hstring; /* character string containing FITS header information
+ in the format <keyword>= <value> [/ <comment>] */
+const char *name; /* character string with identifying name of WCS */
+{
+ char mchar; /* Suffix character for one of multiple WCS */
+
+ mchar = wcschar (hstring, name);
+ if (mchar == '_') {
+ fprintf (stderr, "WCSINITN: WCS name %s not matched in FITS header\n",
+ name);
+ return (NULL);
+ }
+ return (wcsinitc (hstring, &mchar));
+}
+
+
+/* WCSCHAR -- Find the letter for a specific WCS conversion */
+
+char
+wcschar (hstring, name)
+
+const char *hstring; /* character string containing FITS header information
+ in the format <keyword>= <value> [/ <comment>] */
+const char *name; /* Name of WCS conversion to be matched
+ (case-independent) */
+{
+ char *upname, *uppercase();
+ char cwcs, charwcs;
+ int iwcs;
+ char keyword[12];
+ char *upval, value[72];
+
+ /* If no WCS character, return 0 */
+ if (name == NULL)
+ return ((char) 0);
+
+ /* Convert input name to upper case */
+ upname = uppercase (name);
+
+ /* If single character name, return that character */
+ if (strlen (upname) == 1)
+ return (upname[0]);
+
+ /* Try to match input name to available WCSNAME names in header */
+ strcpy (keyword, "WCSNAME");
+ keyword[8] = (char) 0;
+ charwcs = '_';
+ for (iwcs = 0; iwcs < 27; iwcs++) {
+ if (iwcs > 0)
+ cwcs = (char) (64 + iwcs);
+ else
+ cwcs = (char) 0;
+ keyword[7] = cwcs;
+ if (hgets (hstring, keyword, 72, value)) {
+ upval = uppercase (value);
+ if (!strcmp (upval, upname))
+ charwcs = cwcs;
+ free (upval);
+ }
+ }
+ free (upname);
+ return (charwcs);
+}
+
+
+/* Make string of arbitrary case all uppercase */
+
+char *
+uppercase (string)
+char *string;
+{
+ int lstring, i;
+ char *upstring;
+
+ lstring = strlen (string);
+ upstring = (char *) calloc (1,lstring+1);
+ for (i = 0; i < lstring; i++) {
+ if (string[i] > 96 && string[i] < 123)
+ upstring[i] = string[i] - 32;
+ else
+ upstring[i] = string[i];
+ }
+ upstring[lstring] = (char) 0;
+ return (upstring);
+}
+
+
+/* set up a WCS structure from a FITS image header lhstring bytes long */
+
+struct WorldCoor *
+wcsninit (hstring, lhstring)
+
+const char *hstring; /* character string containing FITS header information
+ in the format <keyword>= <value> [/ <comment>] */
+int lhstring; /* Length of FITS header in bytes */
+{
+ char mchar; /* Suffix character for one of multiple WCS */
+ mchar = (char) 0;
+ hlength (hstring, lhstring);
+ return (wcsinitc (hstring, &mchar));
+}
+
+
+/* set up a WCS structure from a FITS image header lhstring bytes long */
+
+struct WorldCoor *
+wcsninitc (hstring, lhstring, mchar)
+
+const char *hstring; /* character string containing FITS header information
+ in the format <keyword>= <value> [/ <comment>] */
+int lhstring; /* Length of FITS header in bytes */
+char *mchar; /* Suffix character for one of multiple WCS */
+{
+ hlength (hstring, lhstring);
+ if (mchar[0] == ' ')
+ mchar[0] = (char) 0;
+ return (wcsinitc (hstring, mchar));
+}
+
+
+/* set up a WCS structure from a FITS image header */
+
+struct WorldCoor *
+wcsinit (hstring)
+
+const char *hstring; /* character string containing FITS header information
+ in the format <keyword>= <value> [/ <comment>] */
+{
+ char mchar; /* Suffix character for one of multiple WCS */
+ mchar = (char) 0;
+ return (wcsinitc (hstring, &mchar));
+}
+
+
+/* set up a WCS structure from a FITS image header for specified suffix */
+
+struct WorldCoor *
+wcsinitc (hstring, wchar)
+
+const char *hstring; /* character string containing FITS header information
+ in the format <keyword>= <value> [/ <comment>] */
+char *wchar; /* Suffix character for one of multiple WCS */
+{
+ struct WorldCoor *wcs, *depwcs;
+ char ctype1[32], ctype2[32], tstring[32];
+ char pvkey1[8],pvkey2[8],pvkey3[8];
+ char *hcoeff; /* pointer to first coeff's in header */
+ char decsign;
+ double rah,ram,ras, dsign,decd,decm,decs;
+ double dec_deg,ra_hours, secpix, ra0, ra1, dec0, dec1, cvel;
+ double cdelt1, cdelt2, cd[4], pc[81];
+ char keyword[16];
+ int ieq, i, j, k, naxes, cd11p, cd12p, cd21p, cd22p;
+ int ilat; /* coordinate for latitude or declination */
+ /*
+ int ix1, ix2, iy1, iy2, idx1, idx2, idy1, idy2;
+ double dxrefpix, dyrefpix;
+ */
+ char temp[80];
+ char wcsname[64]; /* Name of WCS depended on by current WCS */
+ char mchar;
+ char cspace = (char) ' ';
+ char cnull = (char) 0;
+ double mjd;
+ double rot;
+ double ut;
+ int nax;
+ int twod;
+ extern int tnxinit();
+ extern int zpxinit();
+ extern int platepos();
+ extern int dsspos();
+ void invert_wcs();
+
+ wcs = (struct WorldCoor *) calloc (1, sizeof(struct WorldCoor));
+
+ /* Set WCS character and name in structure */
+ mchar = wchar[0];
+ if (mchar == ' ')
+ mchar = cnull;
+ wcs->wcschar = mchar;
+ if (hgetsc (hstring, "WCSNAME", &mchar, 63, wcsname)) {
+ wcs->wcsname = (char *) calloc (strlen (wcsname)+2, 1);
+ strcpy (wcs->wcsname, wcsname);
+ }
+
+
+ /* Set WCSLIB flags so that structures will be reinitialized */
+ wcs->cel.flag = 0;
+ wcs->lin.flag = 0;
+ wcs->wcsl.flag = 0;
+ wcs->wcsl.cubeface = -1;
+
+ /* Initialize to no plate fit */
+ wcs->ncoeff1 = 0;
+ wcs->ncoeff2 = 0;
+
+ /* Initialize to no CD matrix */
+ cdelt1 = 0.0;
+ cdelt2 = 0.0;
+ cd[0] = 0.0;
+ cd[1] = 0.0;
+ cd[2] = 0.0;
+ cd[3] = 0.0;
+ pc[0] = 0.0;
+ wcs->rotmat = 0;
+ wcs->rot = 0.0;
+
+ /* Header parameters independent of projection */
+ naxes = 0;
+ hgeti4c (hstring, "WCSAXES", &mchar, &naxes);
+ if (naxes == 0)
+ hgeti4 (hstring, "WCSAXES", &naxes);
+ if (naxes == 0)
+ hgeti4 (hstring, "NAXIS", &naxes);
+ if (naxes == 0)
+ hgeti4 (hstring, "WCSDIM", &naxes);
+ if (naxes < 1) {
+ setwcserr ("WCSINIT: No WCSAXES, NAXIS, or WCSDIM keyword");
+ wcsfree (wcs);
+ return (NULL);
+ }
+ if (naxes > 2)
+ naxes = 2;
+ wcs->naxis = naxes;
+ wcs->naxes = naxes;
+ wcs->lin.naxis = naxes;
+ wcs->nxpix = 0;
+ hgetr8 (hstring, "NAXIS1", &wcs->nxpix);
+ if (wcs->nxpix < 1)
+ hgetr8 (hstring, "IMAGEW", &wcs->nxpix);
+ if (wcs->nxpix < 1) {
+ setwcserr ("WCSINIT: No NAXIS1 or IMAGEW keyword");
+ wcsfree (wcs);
+ return (NULL);
+ }
+ wcs->nypix = 0;
+ hgetr8 (hstring, "NAXIS2", &wcs->nypix);
+ if (wcs->nypix < 1)
+ hgetr8 (hstring, "IMAGEH", &wcs->nypix);
+ if (naxes > 1 && wcs->nypix < 1) {
+ setwcserr ("WCSINIT: No NAXIS2 or IMAGEH keyword");
+ wcsfree (wcs);
+ return (NULL);
+ }
+
+ /* Reset number of axes to only those with dimension greater than one */
+ nax = 0;
+ for (i = 0; i < naxes; i++) {
+
+ /* Check for number of pixels in axis more than one */
+ strcpy (keyword, "NAXIS");
+ sprintf (temp, "%d", i+1);
+ strcat (keyword, temp);
+ if (!hgeti4 (hstring, keyword, &j)) {
+ if (i == 0 && wcs->nxpix > 1) {
+ /* fprintf (stderr,"WCSINIT: Missing keyword %s set to %.0f from IMAGEW\n",
+ keyword, wcs->nxpix); */
+ j = wcs->nxpix;
+ }
+ else if (i == 1 && wcs->nypix > 1) {
+ /* fprintf (stderr,"WCSINIT: Missing keyword %s set to %.0f from IMAGEH\n",
+ keyword, wcs->nypix); */
+ j = wcs->nypix;
+ }
+ else
+ fprintf (stderr,"WCSINIT: Missing keyword %s assumed 1\n",keyword);
+ }
+
+ /* Check for TAB WCS in axis */
+ strcpy (keyword, "CTYPE");
+ strcat (keyword, temp);
+ if (hgets (hstring, keyword, 16, temp)) {
+ if (strsrch (temp, "-TAB"))
+ j = 0;
+ }
+ if (j > 1) nax = nax + 1;
+ }
+ naxes = nax;
+ wcs->naxes = nax;
+ wcs->naxis = nax;
+
+ hgets (hstring, "INSTRUME", 16, wcs->instrument);
+ hgeti4 (hstring, "DETECTOR", &wcs->detector);
+ wcs->wcsproj = getdefwcs();
+ wcs->logwcs = 0;
+ hgeti4 (hstring, "DC-FLAG", &wcs->logwcs);
+
+ /* Initialize rotation matrices */
+ for (i = 0; i < 81; i++) wcs->pc[i] = 0.0;
+ for (i = 0; i < 81; i++) pc[i] = 0.0;
+ for (i = 0; i < naxes; i++) wcs->pc[(i*naxes)+i] = 1.0;
+ for (i = 0; i < naxes; i++) pc[(i*naxes)+i] = 1.0;
+ for (i = 0; i < 9; i++) wcs->cdelt[i] = 0.0;
+ for (i = 0; i < naxes; i++) wcs->cdelt[i] = 1.0;
+
+ /* If the current world coordinate system depends on another, set it now */
+ if (hgetsc (hstring, "WCSDEP",&mchar, 63, wcsname)) {
+ if ((wcs->wcs = wcsinitn (hstring, wcsname)) == NULL) {
+ setwcserr ("WCSINIT: depended on WCS could not be set");
+ wcsfree (wcs);
+ return (NULL);
+ }
+ depwcs = wcs->wcs;
+ depwcs->wcsdep = wcs;
+ }
+ else
+ wcs->wcs = NULL;
+
+ /* Read radial velocity from image header */
+ wcs->radvel = 0.0;
+ wcs->zvel = 0.0;
+ cvel = 299792.5;
+ if (hgetr8c (hstring, "VSOURCE", &mchar, &wcs->radvel))
+ wcs->zvel = wcs->radvel / cvel;
+ else if (hgetr8c (hstring, "ZSOURCE", &mchar, &wcs->zvel))
+ wcs->radvel = wcs->zvel * cvel;
+ else if (hgetr8 (hstring, "VELOCITY", &wcs->radvel))
+ wcs->zvel = wcs->radvel / cvel;
+
+ for (i = 0; i < 10; i++) {
+ wcs->prj.p[i] = 0.0;
+ }
+
+ /* World coordinate system reference coordinate information */
+ if (hgetsc (hstring, "CTYPE1", &mchar, 16, ctype1)) {
+
+ /* Read second coordinate type */
+ strcpy (ctype2, ctype1);
+ if (!hgetsc (hstring, "CTYPE2", &mchar, 16, ctype2))
+ twod = 0;
+ else
+ twod = 1;
+ strcpy (wcs->ctype[0], ctype1);
+ strcpy (wcs->ctype[1], ctype2);
+ if (strsrch (ctype2, "LAT") || strsrch (ctype2, "DEC"))
+ ilat = 2;
+ else
+ ilat = 1;
+
+ /* Read third and fourth coordinate types, if present */
+ strcpy (wcs->ctype[2], "");
+ hgetsc (hstring, "CTYPE3", &mchar, 9, wcs->ctype[2]);
+ strcpy (wcs->ctype[3], "");
+ hgetsc (hstring, "CTYPE4", &mchar, 9, wcs->ctype[3]);
+
+ /* Set projection type in WCS data structure */
+ if (wcstype (wcs, ctype1, ctype2)) {
+ wcsfree (wcs);
+ return (NULL);
+ }
+
+ /* Get units, if present, for linear coordinates */
+ if (wcs->prjcode == WCS_LIN) {
+ if (!hgetsc (hstring, "CUNIT1", &mchar, 16, wcs->units[0])) {
+ if (!mgetstr (hstring, "WAT1", "units", 16, wcs->units[0])) {
+ wcs->units[0][0] = 0;
+ }
+ }
+ if (!strcmp (wcs->units[0], "pixel"))
+ wcs->prjcode = WCS_PIX;
+ if (twod) {
+ if (!hgetsc (hstring, "CUNIT2", &mchar, 16, wcs->units[1])) {
+ if (!mgetstr (hstring, "WAT2", "units", 16, wcs->units[1])) {
+ wcs->units[1][0] = 0;
+ }
+ }
+ if (!strcmp (wcs->units[0], "pixel"))
+ wcs->prjcode = WCS_PIX;
+ }
+ }
+
+ /* Reference pixel coordinates and WCS value */
+ wcs->crpix[0] = 1.0;
+ hgetr8c (hstring, "CRPIX1", &mchar, &wcs->crpix[0]);
+ wcs->crpix[1] = 1.0;
+ hgetr8c (hstring, "CRPIX2", &mchar, &wcs->crpix[1]);
+ wcs->xrefpix = wcs->crpix[0];
+ wcs->yrefpix = wcs->crpix[1];
+ wcs->crval[0] = 0.0;
+ hgetr8c (hstring, "CRVAL1", &mchar, &wcs->crval[0]);
+ wcs->crval[1] = 0.0;
+ hgetr8c (hstring, "CRVAL2", &mchar, &wcs->crval[1]);
+ if (wcs->syswcs == WCS_NPOLE)
+ wcs->crval[1] = 90.0 - wcs->crval[1];
+ if (wcs->syswcs == WCS_SPA)
+ wcs->crval[1] = wcs->crval[1] - 90.0;
+ wcs->xref = wcs->crval[0];
+ wcs->yref = wcs->crval[1];
+ if (wcs->coorflip) {
+ wcs->cel.ref[0] = wcs->crval[1];
+ wcs->cel.ref[1] = wcs->crval[0];
+ }
+ else {
+ wcs->cel.ref[0] = wcs->crval[0];
+ wcs->cel.ref[1] = wcs->crval[1];
+ }
+ wcs->longpole = 999.0;
+ hgetr8c (hstring, "LONPOLE", &mchar, &wcs->longpole);
+ wcs->cel.ref[2] = wcs->longpole;
+ wcs->latpole = 999.0;
+ hgetr8c (hstring, "LATPOLE", &mchar, &wcs->latpole);
+ wcs->cel.ref[3] = wcs->latpole;
+ wcs->lin.crpix = wcs->crpix;
+ wcs->lin.cdelt = wcs->cdelt;
+ wcs->lin.pc = wcs->pc;
+
+ /* Projection constants (this should be projection-dependent */
+ wcs->prj.r0 = 0.0;
+ hgetr8c (hstring, "PROJR0", &mchar, &wcs->prj.r0);
+
+ /* FITS WCS interim proposal projection constants */
+ for (i = 0; i < 10; i++) {
+ sprintf (keyword,"PROJP%d",i);
+ hgetr8c (hstring, keyword, &mchar, &wcs->prj.p[i]);
+ }
+
+ sprintf (pvkey1, "PV%d_1", ilat);
+ sprintf (pvkey2, "PV%d_2", ilat);
+ sprintf (pvkey3, "PV%d_3", ilat);
+
+ /* FITS WCS standard projection constants (projection-dependent) */
+ if (wcs->prjcode == WCS_AZP || wcs->prjcode == WCS_SIN ||
+ wcs->prjcode == WCS_COP || wcs->prjcode == WCS_COE ||
+ wcs->prjcode == WCS_COD || wcs->prjcode == WCS_COO) {
+ hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+ hgetr8c (hstring, pvkey2, &mchar, &wcs->prj.p[2]);
+ }
+ else if (wcs->prjcode == WCS_SZP) {
+ hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+ hgetr8c (hstring, pvkey2, &mchar, &wcs->prj.p[2]);
+ if (wcs->prj.p[3] == 0.0)
+ wcs->prj.p[3] = 90.0;
+ hgetr8c (hstring, pvkey3, &mchar, &wcs->prj.p[3]);
+ }
+ else if (wcs->prjcode == WCS_CEA) {
+ if (wcs->prj.p[1] == 0.0)
+ wcs->prj.p[1] = 1.0;
+ hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+ }
+ else if (wcs->prjcode == WCS_CYP) {
+ if (wcs->prj.p[1] == 0.0)
+ wcs->prj.p[1] = 1.0;
+ hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+ if (wcs->prj.p[2] == 0.0)
+ wcs->prj.p[2] = 1.0;
+ hgetr8c (hstring, pvkey2, &mchar, &wcs->prj.p[2]);
+ }
+ else if (wcs->prjcode == WCS_AIR) {
+ if (wcs->prj.p[1] == 0.0)
+ wcs->prj.p[1] = 90.0;
+ hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+ }
+ else if (wcs->prjcode == WCS_BON) {
+ hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+ }
+ else if (wcs->prjcode == WCS_ZPN) {
+ for (i = 0; i < 10; i++) {
+ sprintf (keyword,"PV%d_%d", ilat, i);
+ hgetr8c (hstring, keyword, &mchar, &wcs->prj.p[i]);
+ }
+ }
+
+ /* Initialize TNX, defaulting to TAN if there is a problem */
+ if (wcs->prjcode == WCS_TNX) {
+ if (tnxinit (hstring, wcs)) {
+ wcs->ctype[0][6] = 'A';
+ wcs->ctype[0][7] = 'N';
+ wcs->ctype[1][6] = 'A';
+ wcs->ctype[1][7] = 'N';
+ wcs->prjcode = WCS_TAN;
+ }
+ }
+
+ /* Initialize ZPX, defaulting to ZPN if there is a problem */
+ if (wcs->prjcode == WCS_ZPX) {
+ if (zpxinit (hstring, wcs)) {
+ wcs->ctype[0][7] = 'N';
+ wcs->ctype[1][7] = 'N';
+ wcs->prjcode = WCS_ZPN;
+ }
+ }
+
+ /* Set TPV to TAN as SCAMP coefficients will be added below */
+ if (wcs->prjcode == WCS_TPV) {
+ wcs->ctype[0][6] = 'A';
+ wcs->ctype[0][7] = 'N';
+ wcs->ctype[1][6] = 'A';
+ wcs->ctype[1][7] = 'N';
+ wcs->prjcode = WCS_TAN;
+ }
+
+ /* Coordinate reference frame, equinox, and epoch */
+ if (wcs->wcsproj > 0)
+ wcseqm (hstring, wcs, &mchar);
+ wcsioset (wcs);
+
+ /* Read distortion coefficients, if present */
+ distortinit (wcs, hstring);
+
+ /* Use polynomial fit instead of projection, if present */
+ wcs->ncoeff1 = 0;
+ wcs->ncoeff2 = 0;
+ cd11p = hgetr8c (hstring, "CD1_1", &mchar, &cd[0]);
+ cd12p = hgetr8c (hstring, "CD1_2", &mchar, &cd[1]);
+ cd21p = hgetr8c (hstring, "CD2_1", &mchar, &cd[2]);
+ cd22p = hgetr8c (hstring, "CD2_2", &mchar, &cd[3]);
+ if (wcs->wcsproj != WCS_OLD &&
+ (hcoeff = ksearch (hstring,"CO1_1")) != NULL) {
+ wcs->prjcode = WCS_PLT;
+ (void)strcpy (wcs->ptype, "PLATE");
+ for (i = 0; i < 20; i++) {
+ sprintf (keyword,"CO1_%d", i+1);
+ wcs->x_coeff[i] = 0.0;
+ if (hgetr8 (hcoeff, keyword, &wcs->x_coeff[i]))
+ wcs->ncoeff1 = i + 1;
+ }
+ hcoeff = ksearch (hstring,"CO2_1");
+ for (i = 0; i < 20; i++) {
+ sprintf (keyword,"CO2_%d",i+1);
+ wcs->y_coeff[i] = 0.0;
+ if (hgetr8 (hcoeff, keyword, &wcs->y_coeff[i]))
+ wcs->ncoeff2 = i + 1;
+ }
+
+ /* Compute a nominal scale factor */
+ platepos (wcs->crpix[0], wcs->crpix[1], wcs, &ra0, &dec0);
+ platepos (wcs->crpix[0], wcs->crpix[1]+1.0, wcs, &ra1, &dec1);
+ wcs->yinc = dec1 - dec0;
+ wcs->xinc = -wcs->yinc;
+
+ /* Compute image rotation angle */
+ wcs->wcson = 1;
+ wcsrotset (wcs);
+ rot = degrad (wcs->rot);
+
+ /* Compute scale at reference pixel */
+ platepos (wcs->crpix[0], wcs->crpix[1], wcs, &ra0, &dec0);
+ platepos (wcs->crpix[0]+cos(rot),
+ wcs->crpix[1]+sin(rot), wcs, &ra1, &dec1);
+ wcs->cdelt[0] = -wcsdist (ra0, dec0, ra1, dec1);
+ wcs->xinc = wcs->cdelt[0];
+ platepos (wcs->crpix[0]+sin(rot),
+ wcs->crpix[1]+cos(rot), wcs, &ra1, &dec1);
+ wcs->cdelt[1] = wcsdist (ra0, dec0, ra1, dec1);
+ wcs->yinc = wcs->cdelt[1];
+
+ /* Set CD matrix from header */
+ wcs->cd[0] = cd[0];
+ wcs->cd[1] = cd[1];
+ wcs->cd[2] = cd[2];
+ wcs->cd[3] = cd[3];
+ (void) matinv (2, wcs->cd, wcs->dc);
+ }
+
+ /* Else use CD matrix, if present */
+ else if (cd11p || cd12p || cd21p || cd22p) {
+ wcs->rotmat = 1;
+ wcscdset (wcs, cd);
+ }
+
+ /* Else get scaling from CDELT1 and CDELT2 */
+ else if (hgetr8c (hstring, "CDELT1", &mchar, &cdelt1) != 0) {
+ hgetr8c (hstring, "CDELT2", &mchar, &cdelt2);
+
+ /* If CDELT1 or CDELT2 is 0 or missing */
+ if (cdelt1 == 0.0 || (wcs->nypix > 1 && cdelt2 == 0.0)) {
+ if (ksearch (hstring,"SECPIX") != NULL ||
+ ksearch (hstring,"PIXSCALE") != NULL ||
+ ksearch (hstring,"PIXSCAL1") != NULL ||
+ ksearch (hstring,"XPIXSIZE") != NULL ||
+ ksearch (hstring,"SECPIX1") != NULL) {
+ secpix = 0.0;
+ hgetr8 (hstring,"SECPIX",&secpix);
+ if (secpix == 0.0)
+ hgetr8 (hstring,"PIXSCALE",&secpix);
+ if (secpix == 0.0) {
+ hgetr8 (hstring,"SECPIX1",&secpix);
+ if (secpix != 0.0) {
+ if (cdelt1 == 0.0)
+ cdelt1 = -secpix / 3600.0;
+ if (cdelt2 == 0.0) {
+ hgetr8 (hstring,"SECPIX2",&secpix);
+ cdelt2 = secpix / 3600.0;
+ }
+ }
+ else {
+ hgetr8 (hstring,"XPIXSIZE",&secpix);
+ if (secpix != 0.0) {
+ if (cdelt1 == 0.0)
+ cdelt1 = -secpix / 3600.0;
+ if (cdelt2 == 0.0) {
+ hgetr8 (hstring,"YPIXSIZE",&secpix);
+ cdelt2 = secpix / 3600.0;
+ }
+ }
+ else {
+ hgetr8 (hstring,"PIXSCAL1",&secpix);
+ if (secpix != 0.0 && cdelt1 == 0.0)
+ cdelt1 = -secpix / 3600.0;
+ if (cdelt2 == 0.0) {
+ hgetr8 (hstring,"PIXSCAL2",&secpix);
+ cdelt2 = secpix / 3600.0;
+ }
+ }
+ }
+ }
+ else {
+ if (cdelt1 == 0.0)
+ cdelt1 = -secpix / 3600.0;
+ if (cdelt2 == 0.0)
+ cdelt2 = secpix / 3600.0;
+ }
+ }
+ }
+ if (cdelt2 == 0.0 && wcs->nypix > 1)
+ cdelt2 = -cdelt1;
+ wcs->cdelt[2] = 1.0;
+ wcs->cdelt[3] = 1.0;
+
+ /* Initialize rotation matrix */
+ for (i = 0; i < 81; i++) {
+ pc[i] = 0.0;
+ wcs->pc[i] = 0.0;
+ }
+ for (i = 0; i < naxes; i++)
+ pc[(i*naxes)+i] = 1.0;
+
+ /* Read FITS WCS interim rotation matrix */
+ if (!mchar && hgetr8 (hstring,"PC001001",&pc[0]) != 0) {
+ k = 0;
+ for (i = 0; i < naxes; i++) {
+ for (j = 0; j < naxes; j++) {
+ if (i == j)
+ pc[k] = 1.0;
+ else
+ pc[k] = 0.0;
+ sprintf (keyword, "PC00%1d00%1d", i+1, j+1);
+ hgetr8 (hstring, keyword, &pc[k++]);
+ }
+ }
+ wcspcset (wcs, cdelt1, cdelt2, pc);
+ }
+
+ /* Read FITS WCS standard rotation matrix */
+ else if (hgetr8c (hstring, "PC1_1", &mchar, &pc[0]) != 0) {
+ k = 0;
+ for (i = 0; i < naxes; i++) {
+ for (j = 0; j < naxes; j++) {
+ if (i == j)
+ pc[k] = 1.0;
+ else
+ pc[k] = 0.0;
+ sprintf (keyword, "PC%1d_%1d", i+1, j+1);
+ hgetr8c (hstring, keyword, &mchar, &pc[k++]);
+ }
+ }
+ wcspcset (wcs, cdelt1, cdelt2, pc);
+ }
+
+ /* Otherwise, use CROTAn */
+ else {
+ rot = 0.0;
+ if (ilat == 2)
+ hgetr8c (hstring, "CROTA2", &mchar, &rot);
+ else
+ hgetr8c (hstring,"CROTA1", &mchar, &rot);
+ wcsdeltset (wcs, cdelt1, cdelt2, rot);
+ }
+ }
+
+ /* If no scaling is present, set to 1 per pixel, no rotation */
+ else {
+ wcs->xinc = 1.0;
+ wcs->yinc = 1.0;
+ wcs->cdelt[0] = 1.0;
+ wcs->cdelt[1] = 1.0;
+ wcs->rot = 0.0;
+ wcs->rotmat = 0;
+ setwcserr ("WCSINIT: setting CDELT to 1");
+ }
+
+ /* SCAMP convention */
+ if (wcs->prjcode == WCS_TAN && wcs->naxis == 2) {
+ int n = 0;
+ if (wcs->inv_x) {
+ poly_end(wcs->inv_x);
+ wcs->inv_x = NULL;
+ }
+ if (wcs->inv_y) {
+ poly_end(wcs->inv_y);
+ wcs->inv_y = NULL;
+ }
+ wcs->pvfail = 0;
+ for (i = 0; i < (2*MAXPV); i++) {
+ wcs->projppv[i] = 0.0;
+ wcs->prj.ppv[i] = 0.0;
+ }
+ for (k = 0; k < 2; k++) {
+ for (j = 0; j < MAXPV; j++) {
+ sprintf(keyword, "PV%d_%d", k+1, j);
+ if (hgetr8c(hstring, keyword,&mchar, &wcs->projppv[j+k*MAXPV]) == 0) {
+ wcs->projppv[j+k*MAXPV] = 0.0;
+ }
+ else
+ n++;
+ }
+ }
+
+ /* If any PVi_j are set, add them to the structure */
+ if (n > 0) {
+ n = 0;
+
+ for (k = MAXPV; k >= 0; k--) {
+ /* lat comes first for compatibility reasons */
+ wcs->prj.ppv[k] = wcs->projppv[k+wcs->wcsl.lat*MAXPV];
+ wcs->prj.ppv[k+MAXPV] = wcs->projppv[k+wcs->wcsl.lng*MAXPV];
+ if (!n && (wcs->prj.ppv[k] || wcs->prj.ppv[k+MAXPV])) {
+ n = k+1;
+ }
+ }
+ invert_wcs(wcs);
+
+ /* Need to call tanset again */
+ wcs->cel.flag = 0;
+ }
+ }
+
+ /* If linear or pixel WCS, print "degrees" */
+ if (!strncmp (wcs->ptype,"LINEAR",6) ||
+ !strncmp (wcs->ptype,"PIXEL",5)) {
+ wcs->degout = -1;
+ wcs->ndec = 5;
+ }
+
+ /* Epoch of image (from observation date, if possible) */
+ if (hgetr8 (hstring, "MJD-OBS", &mjd))
+ wcs->epoch = 1900.0 + (mjd - 15019.81352) / 365.242198781;
+ else if (!hgetdate (hstring,"DATE-OBS",&wcs->epoch)) {
+ if (!hgetdate (hstring,"DATE",&wcs->epoch)) {
+ if (!hgetr8 (hstring,"EPOCH",&wcs->epoch))
+ wcs->epoch = wcs->equinox;
+ }
+ }
+
+ /* Add time of day if not part of DATE-OBS string */
+ else {
+ hgets (hstring,"DATE-OBS",32,tstring);
+ if (!strchr (tstring,'T')) {
+ if (hgetr8 (hstring, "UT",&ut))
+ wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+ else if (hgetr8 (hstring, "UTMID",&ut))
+ wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+ }
+ }
+
+ wcs->wcson = 1;
+ }
+
+ else if (mchar != cnull && mchar != cspace) {
+ (void) sprintf (temp, "WCSINITC: No image scale for WCS %c", mchar);
+ setwcserr (temp);
+ wcsfree (wcs);
+ return (NULL);
+ }
+
+ /* Plate solution coefficients */
+ else if (ksearch (hstring,"PLTRAH") != NULL) {
+ wcs->prjcode = WCS_DSS;
+ hcoeff = ksearch (hstring,"PLTRAH");
+ hgetr8 (hcoeff,"PLTRAH",&rah);
+ hgetr8 (hcoeff,"PLTRAM",&ram);
+ hgetr8 (hcoeff,"PLTRAS",&ras);
+ ra_hours = rah + (ram / (double)60.0) + (ras / (double)3600.0);
+ wcs->plate_ra = hrrad (ra_hours);
+ decsign = '+';
+ hgets (hcoeff,"PLTDECSN", 1, &decsign);
+ if (decsign == '-')
+ dsign = -1.;
+ else
+ dsign = 1.;
+ hgetr8 (hcoeff,"PLTDECD",&decd);
+ hgetr8 (hcoeff,"PLTDECM",&decm);
+ hgetr8 (hcoeff,"PLTDECS",&decs);
+ dec_deg = dsign * (decd+(decm/(double)60.0)+(decs/(double)3600.0));
+ wcs->plate_dec = degrad (dec_deg);
+ hgetr8 (hstring,"EQUINOX",&wcs->equinox);
+ hgeti4 (hstring,"EQUINOX",&ieq);
+ if (ieq == 1950)
+ strcpy (wcs->radecsys,"FK4");
+ else
+ strcpy (wcs->radecsys,"FK5");
+ wcs->epoch = wcs->equinox;
+ hgetr8 (hstring,"EPOCH",&wcs->epoch);
+ (void)sprintf (wcs->center,"%2.0f:%2.0f:%5.3f %c%2.0f:%2.0f:%5.3f %s",
+ rah,ram,ras,decsign,decd,decm,decs,wcs->radecsys);
+ hgetr8 (hstring,"PLTSCALE",&wcs->plate_scale);
+ hgetr8 (hstring,"XPIXELSZ",&wcs->x_pixel_size);
+ hgetr8 (hstring,"YPIXELSZ",&wcs->y_pixel_size);
+ hgetr8 (hstring,"CNPIX1",&wcs->x_pixel_offset);
+ hgetr8 (hstring,"CNPIX2",&wcs->y_pixel_offset);
+ hcoeff = ksearch (hstring,"PPO1");
+ for (i = 0; i < 6; i++) {
+ sprintf (keyword,"PPO%d", i+1);
+ wcs->ppo_coeff[i] = 0.0;
+ hgetr8 (hcoeff,keyword,&wcs->ppo_coeff[i]);
+ }
+ hcoeff = ksearch (hstring,"AMDX1");
+ for (i = 0; i < 20; i++) {
+ sprintf (keyword,"AMDX%d", i+1);
+ wcs->x_coeff[i] = 0.0;
+ hgetr8 (hcoeff, keyword, &wcs->x_coeff[i]);
+ }
+ hcoeff = ksearch (hstring,"AMDY1");
+ for (i = 0; i < 20; i++) {
+ sprintf (keyword,"AMDY%d",i+1);
+ wcs->y_coeff[i] = 0.0;
+ hgetr8 (hcoeff, keyword, &wcs->y_coeff[i]);
+ }
+ wcs->wcson = 1;
+ (void)strcpy (wcs->c1type, "RA");
+ (void)strcpy (wcs->c2type, "DEC");
+ (void)strcpy (wcs->ptype, "DSS");
+ wcs->degout = 0;
+ wcs->ndec = 3;
+
+ /* Compute a nominal reference pixel at the image center */
+ strcpy (wcs->ctype[0], "RA---DSS");
+ strcpy (wcs->ctype[1], "DEC--DSS");
+ wcs->crpix[0] = 0.5 * wcs->nxpix;
+ wcs->crpix[1] = 0.5 * wcs->nypix;
+ wcs->xrefpix = wcs->crpix[0];
+ wcs->yrefpix = wcs->crpix[1];
+ dsspos (wcs->crpix[0], wcs->crpix[1], wcs, &ra0, &dec0);
+ wcs->crval[0] = ra0;
+ wcs->crval[1] = dec0;
+ wcs->xref = wcs->crval[0];
+ wcs->yref = wcs->crval[1];
+
+ /* Compute a nominal scale factor */
+ dsspos (wcs->crpix[0], wcs->crpix[1]+1.0, wcs, &ra1, &dec1);
+ wcs->yinc = dec1 - dec0;
+ wcs->xinc = -wcs->yinc;
+ wcsioset (wcs);
+
+ /* Compute image rotation angle */
+ wcs->wcson = 1;
+ wcsrotset (wcs);
+ rot = degrad (wcs->rot);
+
+ /* Compute image scale at center */
+ dsspos (wcs->crpix[0]+cos(rot),
+ wcs->crpix[1]+sin(rot), wcs, &ra1, &dec1);
+ wcs->cdelt[0] = -wcsdist (ra0, dec0, ra1, dec1);
+ dsspos (wcs->crpix[0]+sin(rot),
+ wcs->crpix[1]+cos(rot), wcs, &ra1, &dec1);
+ wcs->cdelt[1] = wcsdist (ra0, dec0, ra1, dec1);
+
+ /* Set all other image scale parameters */
+ wcsdeltset (wcs, wcs->cdelt[0], wcs->cdelt[1], wcs->rot);
+ }
+
+ /* Approximate world coordinate system if plate scale is known */
+ else if ((ksearch (hstring,"SECPIX") != NULL ||
+ ksearch (hstring,"PIXSCALE") != NULL ||
+ ksearch (hstring,"PIXSCAL1") != NULL ||
+ ksearch (hstring,"XPIXSIZE") != NULL ||
+ ksearch (hstring,"SECPIX1") != NULL)) {
+ secpix = 0.0;
+ hgetr8 (hstring,"SECPIX",&secpix);
+ if (secpix == 0.0)
+ hgetr8 (hstring,"PIXSCALE",&secpix);
+ if (secpix == 0.0) {
+ hgetr8 (hstring,"SECPIX1",&secpix);
+ if (secpix != 0.0) {
+ cdelt1 = -secpix / 3600.0;
+ hgetr8 (hstring,"SECPIX2",&secpix);
+ cdelt2 = secpix / 3600.0;
+ }
+ else {
+ hgetr8 (hstring,"XPIXSIZE",&secpix);
+ if (secpix != 0.0) {
+ cdelt1 = -secpix / 3600.0;
+ hgetr8 (hstring,"YPIXSIZE",&secpix);
+ cdelt2 = secpix / 3600.0;
+ }
+ else {
+ hgetr8 (hstring,"PIXSCAL1",&secpix);
+ cdelt1 = -secpix / 3600.0;
+ hgetr8 (hstring,"PIXSCAL2",&secpix);
+ cdelt2 = secpix / 3600.0;
+ }
+ }
+ }
+ else {
+ cdelt2 = secpix / 3600.0;
+ cdelt1 = -cdelt2;
+ }
+
+ /* Get rotation angle from the header, if it's there */
+ rot = 0.0;
+ hgetr8 (hstring,"CROTA1", &rot);
+ if (wcs->rot == 0.)
+ hgetr8 (hstring,"CROTA2", &rot);
+
+ /* Set CD and PC matrices */
+ wcsdeltset (wcs, cdelt1, cdelt2, rot);
+
+ /* By default, set reference pixel to center of image */
+ wcs->crpix[0] = 0.5 + (wcs->nxpix * 0.5);
+ wcs->crpix[1] = 0.5 + (wcs->nypix * 0.5);
+
+ /* Get reference pixel from the header, if it's there */
+ if (ksearch (hstring,"CRPIX1") != NULL) {
+ hgetr8 (hstring,"CRPIX1",&wcs->crpix[0]);
+ hgetr8 (hstring,"CRPIX2",&wcs->crpix[1]);
+ }
+
+ /* Use center of detector array as reference pixel
+ else if (ksearch (hstring,"DETSIZE") != NULL ||
+ ksearch (hstring,"DETSEC") != NULL) {
+ char *ic;
+ hgets (hstring, "DETSIZE", 32, temp);
+ ic = strchr (temp, ':');
+ if (ic != NULL)
+ *ic = ' ';
+ ic = strchr (temp, ',');
+ if (ic != NULL)
+ *ic = ' ';
+ ic = strchr (temp, ':');
+ if (ic != NULL)
+ *ic = ' ';
+ ic = strchr (temp, ']');
+ if (ic != NULL)
+ *ic = cnull;
+ sscanf (temp, "%d %d %d %d", &idx1, &idx2, &idy1, &idy2);
+ dxrefpix = 0.5 * (double) (idx1 + idx2 - 1);
+ dyrefpix = 0.5 * (double) (idy1 + idy2 - 1);
+ hgets (hstring, "DETSEC", 32, temp);
+ ic = strchr (temp, ':');
+ if (ic != NULL)
+ *ic = ' ';
+ ic = strchr (temp, ',');
+ if (ic != NULL)
+ *ic = ' ';
+ ic = strchr (temp, ':');
+ if (ic != NULL)
+ *ic = ' ';
+ ic = strchr (temp, ']');
+ if (ic != NULL)
+ *ic = cnull;
+ sscanf (temp, "%d %d %d %d", &ix1, &ix2, &iy1, &iy2);
+ wcs->crpix[0] = dxrefpix - (double) (ix1 - 1);
+ wcs->crpix[1] = dyrefpix - (double) (iy1 - 1);
+ } */
+ wcs->xrefpix = wcs->crpix[0];
+ wcs->yrefpix = wcs->crpix[1];
+
+ wcs->crval[0] = -999.0;
+ if (!hgetra (hstring,"RA",&wcs->crval[0])) {
+ setwcserr ("WCSINIT: No RA with SECPIX, no WCS");
+ wcsfree (wcs);
+ return (NULL);
+ }
+ wcs->crval[1] = -999.0;
+ if (!hgetdec (hstring,"DEC",&wcs->crval[1])) {
+ setwcserr ("WCSINIT No DEC with SECPIX, no WCS");
+ wcsfree (wcs);
+ return (NULL);
+ }
+ wcs->xref = wcs->crval[0];
+ wcs->yref = wcs->crval[1];
+ wcs->coorflip = 0;
+
+ wcs->cel.ref[0] = wcs->crval[0];
+ wcs->cel.ref[1] = wcs->crval[1];
+ wcs->cel.ref[2] = 999.0;
+ if (!hgetr8 (hstring,"LONPOLE",&wcs->cel.ref[2]))
+ hgetr8 (hstring,"LONGPOLE",&wcs->cel.ref[2]);
+ wcs->cel.ref[3] = 999.0;
+ hgetr8 (hstring,"LATPOLE",&wcs->cel.ref[3]);
+
+ /* Epoch of image (from observation date, if possible) */
+ if (hgetr8 (hstring, "MJD-OBS", &mjd))
+ wcs->epoch = 1900.0 + (mjd - 15019.81352) / 365.242198781;
+ else if (!hgetdate (hstring,"DATE-OBS",&wcs->epoch)) {
+ if (!hgetdate (hstring,"DATE",&wcs->epoch)) {
+ if (!hgetr8 (hstring,"EPOCH",&wcs->epoch))
+ wcs->epoch = wcs->equinox;
+ }
+ }
+
+ /* Add time of day if not part of DATE-OBS string */
+ else {
+ hgets (hstring,"DATE-OBS",32,tstring);
+ if (!strchr (tstring,'T')) {
+ if (hgetr8 (hstring, "UT",&ut))
+ wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+ else if (hgetr8 (hstring, "UTMID",&ut))
+ wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+ }
+ }
+
+ /* Coordinate reference frame and equinox */
+ (void) wcstype (wcs, "RA---TAN", "DEC--TAN");
+ wcs->coorflip = 0;
+ wcseq (hstring,wcs);
+ wcsioset (wcs);
+ wcs->degout = 0;
+ wcs->ndec = 3;
+ wcs->wcson = 1;
+ }
+
+ else {
+ setwcserr ("WCSINIT: No image scale");
+ wcsfree (wcs);
+ return (NULL);
+ }
+
+ wcs->lin.crpix = wcs->crpix;
+ wcs->lin.cdelt = wcs->cdelt;
+ wcs->lin.pc = wcs->pc;
+
+ wcs->printsys = 1;
+ wcs->tabsys = 0;
+ wcs->linmode = 0;
+
+ /* Initialize special WCS commands */
+ setwcscom (wcs);
+
+ return (wcs);
+}
+
+
+/******* invert_wcs ***********************************************************
+PROTO void invert_wcs(wcsstruct *wcs)
+PURPOSE Invert WCS projection mapping (using a polynomial).
+INPUT WCS structure.
+OUTPUT -.
+NOTES .
+AUTHOR E. Bertin (IAP)
+VERSION 06/11/2003
+ ***/
+
+void
+invert_wcs( struct WorldCoor *wcs)
+
+{
+ polystruct *poly;
+ double pixin[NAXISPV],raw[NAXISPV],rawmin[NAXISPV];
+ double *outpos,*outpost, *lngpos,*lngpost;
+ double *latpos,*latpost,lngstep,latstep, rawsize, epsilon;
+ int group[] = {1,1};
+ /* Don't ask, this is needed by poly_init()! */
+ int i,j,lng,lat,deg, maxflag;
+ char errstr[80];
+ double xmin;
+ double ymin;
+ double xmax;
+ double ymax;
+ double lngmin;
+ double latmin;
+
+ /* Check first that inversion is not straightforward */
+ lng = wcs->wcsl.lng;
+ lat = wcs->wcsl.lat;
+
+ if (wcs->naxis != NAXISPV) {
+ return;
+ }
+
+ if (strcmp(wcs->wcsl.pcode, "TAN") != 0) {
+ return;
+ }
+
+ if ((wcs->projppv[1+lng*MAXPV] == 0) &&
+ (wcs->projppv[1+lat*MAXPV] == 0)) {
+ return;
+ }
+
+ if (wcs->wcs != NULL) {
+ pix2wcs(wcs->wcs,0,0,&xmin,&ymin);
+ pix2wcs(wcs->wcs,wcs->nxpix,wcs->nypix,&xmax,&ymax);
+ }
+ else {
+ xmin = 0;
+ ymin = 0;
+ xmax = wcs->nxpix;
+ ymax = wcs->nypix;
+ }
+
+ /* We define x as "longitude" and y as "latitude" projections */
+ /* We assume that PCxx cross-terms with additional dimensions are small */
+ /* Sample the whole image with a regular grid */
+ if (lng == 0) {
+ lngstep = (xmax-xmin)/(WCS_NGRIDPOINTS-1.0);
+ lngmin = xmin;
+ latstep = (ymax-ymin)/(WCS_NGRIDPOINTS-1.0);
+ latmin = ymin;
+ }
+ else {
+ lngstep = (ymax-ymin)/(WCS_NGRIDPOINTS-1.0);
+ lngmin = ymin;
+ latstep = (xmax-xmin)/(WCS_NGRIDPOINTS-1.0);
+ latmin = xmin;
+ }
+
+ outpos = (double *)calloc(2*WCS_NGRIDPOINTS2,sizeof(double));
+ lngpos = (double *)calloc(WCS_NGRIDPOINTS2,sizeof(double));
+ latpos = (double *)calloc(WCS_NGRIDPOINTS2,sizeof(double));
+ raw[lat] = rawmin[lat] = 0.5+latmin;
+ raw[lng] = rawmin[lng] = 0.5+lngmin;
+ outpost = outpos;
+ lngpost = lngpos;
+ latpost = latpos;
+ for (j=WCS_NGRIDPOINTS; j--; raw[lat]+=latstep) {
+ raw[lng] = rawmin[lng];
+ for (i=WCS_NGRIDPOINTS; i--; raw[lng]+=lngstep) {
+ if (linrev(raw, &wcs->lin, pixin)) {
+ sprintf (errstr,"*Error*: incorrect linear conversion in %s",
+ wcs->wcsl.pcode);
+ setwcserr (errstr);
+ }
+ *(lngpost++) = pixin[lng];
+ *(latpost++) = pixin[lat];
+ raw_to_pv (&wcs->prj,pixin[lng],pixin[lat], outpost, outpost+1);
+ outpost += 2;
+ }
+ }
+
+ /* Invert "longitude" */
+ /* Compute the extent of the pixel in reduced projected coordinates */
+ linrev(rawmin, &wcs->lin, pixin);
+ pixin[lng] += S2D;
+ linfwd(pixin, &wcs->lin, raw);
+ rawsize = sqrt((raw[lng]-rawmin[lng])*(raw[lng]-rawmin[lng])
+ +(raw[lat]-rawmin[lat])*(raw[lat]-rawmin[lat]))*D2S;
+ if (!rawsize) {
+ sprintf (errstr,"*Error*: incorrect linear conversion in %s",
+ wcs->wcsl.pcode);
+ setwcserr (errstr);
+ }
+ epsilon = WCS_INVACCURACY/rawsize;
+
+ /* Find the lowest degree polynom */
+ poly = NULL; /* to avoid gcc -Wall warnings */
+ maxflag = 1;
+ for (deg=1; deg<=WCS_INVMAXDEG && maxflag; deg++) {
+ if (deg>1) {
+ poly_end(poly);
+ }
+ poly = poly_init(group, 2, &deg, 1);
+ poly_fit(poly, outpos, lngpos, NULL, WCS_NGRIDPOINTS2, NULL);
+ maxflag = 0;
+ outpost = outpos;
+ lngpost = lngpos;
+ for (i=WCS_NGRIDPOINTS2; i--; outpost+=2) {
+ if (fabs(poly_func(poly, outpost)-*(lngpost++))>epsilon) {
+ maxflag = 1;
+ break;
+ }
+ }
+ }
+ if (maxflag) {
+ setwcserr ("WARNING: Significant inaccuracy likely to occur in projection");
+ wcs->pvfail = 1;
+ }
+
+ /* Now link the created structure */
+ wcs->prj.inv_x = wcs->inv_x = poly;
+
+ /* Invert "latitude" */
+ /* Compute the extent of the pixel in reduced projected coordinates */
+ linrev(rawmin, &wcs->lin, pixin);
+ pixin[lat] += S2D;
+ linfwd(pixin, &wcs->lin, raw);
+ rawsize = sqrt((raw[lng]-rawmin[lng])*(raw[lng]-rawmin[lng])
+ +(raw[lat]-rawmin[lat])*(raw[lat]-rawmin[lat]))*D2S;
+ if (!rawsize) {
+ sprintf (errstr,"*Error*: incorrect linear conversion in %s",
+ wcs->wcsl.pcode);
+ setwcserr (errstr);
+ }
+ epsilon = WCS_INVACCURACY/rawsize;
+
+ /* Find the lowest degree polynom */
+ maxflag = 1;
+ for (deg=1; deg<=WCS_INVMAXDEG && maxflag; deg++) {
+ if (deg>1)
+ poly_end(poly);
+ poly = poly_init(group, 2, &deg, 1);
+ poly_fit(poly, outpos, latpos, NULL, WCS_NGRIDPOINTS2, NULL);
+ maxflag = 0;
+ outpost = outpos;
+ latpost = latpos;
+ for (i=WCS_NGRIDPOINTS2; i--; outpost+=2) {
+ if (fabs(poly_func(poly, outpost)-*(latpost++))>epsilon) {
+ maxflag = 1;
+ break;
+ }
+ }
+ }
+ if (maxflag) {
+ setwcserr ("WARNING: Significant inaccuracy likely to occur in projection");
+ wcs->pvfail = 1;
+ }
+
+ /* Now link the created structure */
+ wcs->prj.inv_y = wcs->inv_y = poly;
+
+ /* Free memory */
+ free(outpos);
+ free(lngpos);
+ free(latpos);
+
+ return;
+}
+
+
+/* Set coordinate system of image, input, and output */
+
+static void
+wcsioset (wcs)
+
+struct WorldCoor *wcs;
+{
+ if (strlen (wcs->radecsys) == 0 || wcs->prjcode == WCS_LIN)
+ strcpy (wcs->radecsys, "LINEAR");
+ if (wcs->prjcode == WCS_PIX)
+ strcpy (wcs->radecsys, "PIXEL");
+ wcs->syswcs = wcscsys (wcs->radecsys);
+
+ if (wcs->syswcs == WCS_B1950)
+ strcpy (wcs->radecout, "FK4");
+ else if (wcs->syswcs == WCS_J2000)
+ strcpy (wcs->radecout, "FK5");
+ else
+ strcpy (wcs->radecout, wcs->radecsys);
+ wcs->sysout = wcscsys (wcs->radecout);
+ wcs->eqout = wcs->equinox;
+ strcpy (wcs->radecin, wcs->radecsys);
+ wcs->sysin = wcscsys (wcs->radecin);
+ wcs->eqin = wcs->equinox;
+ return;
+}
+
+
+static void
+wcseq (hstring, wcs)
+
+char *hstring; /* character string containing FITS header information
+ in the format <keyword>= <value> [/ <comment>] */
+struct WorldCoor *wcs; /* World coordinate system data structure */
+{
+ char mchar; /* Suffix character for one of multiple WCS */
+ mchar = (char) 0;
+ wcseqm (hstring, wcs, &mchar);
+ return;
+}
+
+
+static void
+wcseqm (hstring, wcs, mchar)
+
+char *hstring; /* character string containing FITS header information
+ in the format <keyword>= <value> [/ <comment>] */
+struct WorldCoor *wcs; /* World coordinate system data structure */
+char *mchar; /* Suffix character for one of multiple WCS */
+{
+ int ieq = 0;
+ int eqhead = 0;
+ char systring[32], eqstring[32];
+ char radeckey[16], eqkey[16];
+ char tstring[32];
+ double ut;
+
+ /* Set equinox from EQUINOX, EPOCH, or RADECSYS; default to 2000 */
+ systring[0] = 0;
+ eqstring[0] = 0;
+ if (mchar[0]) {
+ sprintf (eqkey, "EQUINOX%c", mchar[0]);
+ sprintf (radeckey, "RADECSYS%c", mchar[0]);
+ }
+ else {
+ strcpy (eqkey, "EQUINOX");
+ sprintf (radeckey, "RADECSYS");
+ }
+ if (!hgets (hstring, eqkey, 31, eqstring)) {
+ if (hgets (hstring, "EQUINOX", 31, eqstring))
+ strcpy (eqkey, "EQUINOX");
+ }
+ if (!hgets (hstring, radeckey, 31, systring)) {
+ if (hgets (hstring, "RADECSYS", 31, systring))
+ sprintf (radeckey, "RADECSYS");
+ }
+
+ if (eqstring[0] == 'J') {
+ wcs->equinox = atof (eqstring+1);
+ ieq = atoi (eqstring+1);
+ strcpy (systring, "FK5");
+ }
+ else if (eqstring[0] == 'B') {
+ wcs->equinox = atof (eqstring+1);
+ ieq = (int) atof (eqstring+1);
+ strcpy (systring, "FK4");
+ }
+ else if (hgeti4 (hstring, eqkey, &ieq)) {
+ hgetr8 (hstring, eqkey, &wcs->equinox);
+ eqhead = 1;
+ }
+
+ else if (hgeti4 (hstring,"EPOCH",&ieq)) {
+ if (ieq == 0) {
+ ieq = 1950;
+ wcs->equinox = 1950.0;
+ }
+ else {
+ hgetr8 (hstring,"EPOCH",&wcs->equinox);
+ eqhead = 1;
+ }
+ }
+
+ else if (systring[0] != (char)0) {
+ if (!strncmp (systring,"FK4",3)) {
+ wcs->equinox = 1950.0;
+ ieq = 1950;
+ }
+ else if (!strncmp (systring,"ICRS",4)) {
+ wcs->equinox = 2000.0;
+ ieq = 2000;
+ }
+ else if (!strncmp (systring,"FK5",3)) {
+ wcs->equinox = 2000.0;
+ ieq = 2000;
+ }
+ else if (!strncmp (systring,"GAL",3)) {
+ wcs->equinox = 2000.0;
+ ieq = 2000;
+ }
+ else if (!strncmp (systring,"ECL",3)) {
+ wcs->equinox = 2000.0;
+ ieq = 2000;
+ }
+ }
+
+ if (ieq == 0) {
+ wcs->equinox = 2000.0;
+ ieq = 2000;
+ if (!strncmp (wcs->c1type, "RA",2) || !strncmp (wcs->c1type,"DEC",3))
+ strcpy (systring,"FK5");
+ }
+
+ /* Epoch of image (from observation date, if possible) */
+ if (!hgetdate (hstring,"DATE-OBS",&wcs->epoch)) {
+ if (!hgetdate (hstring,"DATE",&wcs->epoch)) {
+ if (!hgetr8 (hstring,"EPOCH",&wcs->epoch))
+ wcs->epoch = wcs->equinox;
+ }
+ }
+
+ /* Add time of day if not part of DATE-OBS string */
+ else {
+ hgets (hstring,"DATE-OBS",32,tstring);
+ if (!strchr (tstring,'T')) {
+ if (hgetr8 (hstring, "UT",&ut))
+ wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+ else if (hgetr8 (hstring, "UTMID",&ut))
+ wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+ }
+ }
+ if (wcs->epoch == 0.0)
+ wcs->epoch = wcs->equinox;
+
+ /* Set coordinate system from keyword, if it is present */
+ if (systring[0] == (char) 0)
+ hgets (hstring, radeckey, 31, systring);
+ if (systring[0] != (char) 0) {
+ strcpy (wcs->radecsys,systring);
+ if (!eqhead) {
+ if (!strncmp (wcs->radecsys,"FK4",3))
+ wcs->equinox = 1950.0;
+ else if (!strncmp (wcs->radecsys,"FK5",3))
+ wcs->equinox = 2000.0;
+ else if (!strncmp (wcs->radecsys,"ICRS",4))
+ wcs->equinox = 2000.0;
+ else if (!strncmp (wcs->radecsys,"GAL",3) && ieq == 0)
+ wcs->equinox = 2000.0;
+ }
+ }
+
+ /* Otherwise set coordinate system from equinox */
+ /* Systemless coordinates cannot be translated using b, j, or g commands */
+ else if (wcs->syswcs != WCS_NPOLE) {
+ if (ieq > 1980)
+ strcpy (wcs->radecsys,"FK5");
+ else
+ strcpy (wcs->radecsys,"FK4");
+ }
+
+ /* Set galactic coordinates if GLON or GLAT are in C1TYPE */
+ if (wcs->c1type[0] == 'G')
+ strcpy (wcs->radecsys,"GALACTIC");
+ else if (wcs->c1type[0] == 'E')
+ strcpy (wcs->radecsys,"ECLIPTIC");
+ else if (wcs->c1type[0] == 'S')
+ strcpy (wcs->radecsys,"SGALACTC");
+ else if (wcs->c1type[0] == 'H')
+ strcpy (wcs->radecsys,"HELIOECL");
+ else if (wcs->c1type[0] == 'A')
+ strcpy (wcs->radecsys,"ALTAZ");
+ else if (wcs->c1type[0] == 'L')
+ strcpy (wcs->radecsys,"LINEAR");
+
+ wcs->syswcs = wcscsys (wcs->radecsys);
+
+ return;
+}
+
+/* Jun 11 1998 Split off header-dependent WCS initialization from other subs
+ * Jun 15 1998 Fix major bug in wcsinit() when synthesizing WCS from header
+ * Jun 18 1998 Fix bug in CD initialization; split PC initialization off
+ * Jun 18 1998 Split PC initialization off into subroutine wcspcset()
+ * Jun 24 1998 Set equinox from RADECSYS only if EQUINOX and EPOCH not present
+ * Jul 6 1998 Read third and fourth axis CTYPEs
+ * Jul 7 1998 Initialize eqin and eqout to equinox,
+ * Jul 9 1998 Initialize rotation matrices correctly
+ * Jul 13 1998 Initialize rotation, scale for polynomial and DSS projections
+ * Aug 6 1998 Fix CROTA computation for DSS projection
+ * Sep 4 1998 Fix CROTA, CDELT computation for DSS and polynomial projections
+ * Sep 14 1998 If DATE-OBS not found, check for DATE
+ * Sep 14 1998 If B or J present in EQUINOX, use that info to set system
+ * Sep 29 1998 Initialize additional WCS commands from the environment
+ * Sep 29 1998 Fix bug which read DATE as number rather than formatted date
+ * Dec 2 1998 Read projection constants from header (bug fix)
+ *
+ * Feb 9 1999 Set rotation angle correctly when using DSS projection
+ * Feb 19 1999 Fill in CDELTs from scale keyword if absent or zero
+ * Feb 19 1999 Add PIXSCALE as possible default arcseconds per pixel
+ * Apr 7 1999 Add error checking for NAXIS and NAXIS1 keywords
+ * Apr 7 1999 Do not set systring if epoch is 0 and not RA/Dec
+ * Jul 8 1999 In RADECSYS, use FK5 and FK4 instead of J2000 and B1950
+ * Oct 15 1999 Free wcs using wcsfree()
+ * Oct 20 1999 Add multiple WCS support using new subroutine names
+ * Oct 21 1999 Delete unused variables after lint; declare dsspos()
+ * Nov 9 1999 Add wcschar() to check WCSNAME keywords for desired WCS
+ * Nov 9 1999 Check WCSPREx keyword to find out if chained WCS's
+ *
+ * Jan 6 1999 Add wcsinitn() to initialize from specific WCSNAME
+ * Jan 24 2000 Set CD matrix from header even if using polynomial
+ * Jan 27 2000 Fix MJD to epoch conversion for when MJD-OBS is the only date
+ * Jan 28 2000 Set CD matrix for DSS projection, too
+ * Jan 28 2000 Use wcsproj instead of oldwcs
+ * Dec 18 2000 Fix error in hgets() call in wcschar()
+ * Dec 29 2000 Compute inverse CD matrix even if polynomial solution
+ * Dec 29 2000 Add PROJR0 keyword for WCSLIB projections
+ * Dec 29 2000 Use CDi_j matrix if any elements are present
+ *
+ * Jan 31 2001 Fix to allow 1D WCS
+ * Jan 31 2001 Treat single character WCS name as WCS character
+ * Feb 20 2001 Implement WCSDEPx nested WCS's
+ * Feb 23 2001 Initialize all 4 terms of CD matrix
+ * Feb 28 2001 Fix bug which read CRPIX1 into CRPIX2
+ * Mar 20 2001 Compare mchar to (char)0, not null
+ * Mar 21 2001 Move ic declaration into commented out code
+ * Jul 12 2001 Read PROJPn constants into proj.p array instead of PVn
+ * Sep 7 2001 Set system to galactic or ecliptic based on CTYPE, not RADECSYS
+ * Oct 11 2001 Set ctype[0] as well as ctype[1] to TAN for TNX projections
+ * Oct 19 2001 WCSDIM keyword overrides zero value of NAXIS
+ *
+ * Feb 19 2002 Add XPIXSIZE/YPIXSIZE (KPNO) as default image scale keywords
+ * Mar 12 2002 Add LONPOLE as well as LONGPOLE for WCSLIB 2.8
+ * Apr 3 2002 Implement hget8c() and hgetsc() to simplify code
+ * Apr 3 2002 Add PVj_n projection constants in addition to PROJPn
+ * Apr 19 2002 Increase numeric keyword value length from 16 to 31
+ * Apr 19 2002 Fix bug which didn't set radecsys keyword name
+ * Apr 24 2002 If no WCS present for specified letter, return null
+ * Apr 26 2002 Implement WCSAXESa keyword as first choice for number of axes
+ * Apr 26 2002 Add wcschar and wcsname to WCS structure
+ * May 9 2002 Add radvel and zvel to WCS structure
+ * May 13 2002 Free everything which is allocated
+ * May 28 2002 Read 10 prj.p instead of maximum of 100
+ * May 31 2002 Fix bugs with PV reading
+ * May 31 2002 Initialize syswcs, sysin, sysout in wcsioset()
+ * Sep 25 2002 Fix subroutine calls for radvel and latpole
+ * Dec 6 2002 Correctly compute pixel at center of image for default CRPIX
+ *
+ * Jan 2 2002 Do not reinitialize projection vector for PV input
+ * Jan 3 2002 For ZPN, read PVi_0 to PVi_9, not PVi_1 to PVi_10
+ * Mar 27 2003 Clean up default center computation
+ * Apr 3 2003 Add input for SIRTF distortion coefficients
+ * May 8 2003 Change PROJP reading to start with 0 instead of 1
+ * May 22 2003 Add ZPX approximation, reading projpn from WATi
+ * May 28 2003 Avoid reinitializing coefficients set by PROJP
+ * Jun 26 2003 Initialize xref and yref to -999.0
+ * Sep 23 2003 Change mgets() to mgetstr() to avoid name collision at UCO Lick
+ * Oct 1 2003 Rename wcs->naxes to wcs->naxis to match WCSLIB 3.2
+ * Nov 3 2003 Initialize distortion coefficients in distortinit() in distort.c
+ * Dec 1 2003 Change p[0,1,2] initializations to p[1,2,3]
+ * Dec 3 2003 Add back wcs->naxes for backward compatibility
+ * Dec 3 2003 Remove unused variables j,m in wcsinitc()
+ * Dec 12 2003 Fix call to setwcserr() with format in it
+ *
+ * Feb 26 2004 Add parameters for ZPX projection
+ *
+ * Jun 22 2005 Drop declaration of variable wcserrmsg which is not used
+ * Nov 9 2005 Use CROTA1 if CTYPE1 is LAT/DEC, CROTA2 if CTYPE2 is LAT/DEC
+ *
+ * Mar 9 2006 Get Epoch of observation from MJD-OBS or DATE-OBS/UT unless DSS
+ * Apr 24 2006 Initialize rotation matrices
+ * Apr 25 2006 Ignore axes with dimension of one
+ * May 19 2006 Initialize all of 9x9 PC matrix; read in loops
+ * Aug 21 2006 Limit naxes to 2 everywhere; RA and DEC should always be 1st
+ * Oct 6 2006 If units are pixels, projection type is PIXEL
+ * Oct 30 2006 Initialize cube face to -1, not a cube projection
+ *
+ * Jan 4 2007 Drop declarations of wcsinitc() and wcsinitn() already in wcs.h
+ * Jan 8 2007 Change WCS letter from char to char*
+ * Feb 1 2007 Read IRAF log wavelength flag DC-FLAG to wcs.logwcs
+ * Feb 15 2007 Check for wcs->wcsproj > 0 instead of CTYPEi != LINEAR or PIXEL
+ * Mar 13 2007 Try for RA, DEC, SECPIX if WCS character is space or null
+ * Apr 27 2007 Ignore axes with TAB WCS for now
+ * Oct 17 2007 Fix bug testing &mchar instead of mchar in if statement
+ *
+ * May 9 2008 Initialize TNX projection when projection types first set
+ * Jun 27 2008 If NAXIS1 and NAXIS2 not present, check for IMAGEW and IMAGEH
+ *
+ * Mar 24 2009 Fix dimension bug if NAXISi not present (fix from John Burns)
+ *
+ * Mar 11 2011 Add NOAO ZPX projection (Frank Valdes)
+ * Mar 18 2011 Add invert_wcs() by Emmanuel Bertin for SCAMP
+ * Mar 18 2011 Change Bertin's ARCSEC/DEG to S2D and DEG/ARCSEC to D2S
+ * Sep 1 2011 Add TPV as TAN with SCAMP PVs
+ *
+ * Oct 19 2012 Drop unused variable iszpx; fix bug in latmin assignment
+ */