/*============================================================================ * * WCSLIB - an implementation of the FITS WCS proposal. * Copyright (C) 1995-2002, Mark Calabretta * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Library 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 Library * General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street,Fifth Floor, Boston, MA 02110-1301, USA * * Correspondence concerning WCSLIB may be directed to: * Internet email: mcalabre@atnf.csiro.au * Postal address: Dr. Mark Calabretta, * Australia Telescope National Facility, * P.O. Box 76, * Epping, NSW, 2121, * AUSTRALIA * * *============================================================================= * * This version of proj.c is based on the version in wcslib-2.9, but has * been modified in the following ways by the Starlink project (e-mail: * ussc@star.rl.ac.uk): * - The copysign macro is now always defined within this file * instead of only being defined if the COPYSIGN macro has previously * been defined. * - Sine values which are slightly larger than 1.0 are now treated * as 1.0 in function astCYPrev. * - The maximum number of projection parameters has been changed from * 10 to 100. * - The maximum number of projection parameters is given by the * WCSLIB_MXPAR macro (defined in proj.h) instead of being hard-wired. * - The names of all functions and structures have been chanegd to avoid * clashes with wcslib. This involves adding "Ast" or "ast" at the * front and changing the capitalisation. * - Include string.h (for strcpy and strcmp prototypes). * - Include stdlib.h (for abs prototype). * - Comment out declarations of npcode and pcodes variables (they * are not needed by AST) in order to avoid clash with similar names * in other modules imported as part of other software systems (e.g. * SkyCat). * - astZPNfwd: Loop from prj->n to zero, not from MAXPAR to zero. * - astZPNfwd: Only return "2" if prj->n is larger than 2. * - Lots of variables are initialised to null values in order to * avoid "use of uninitialised variable" messages from compilers which * are not clever enough to work out that the uninitialised variable is * not in fact ever used. * - Use dynamic rather than static memory for the parameter arrays in * the AstPrjPrm structure.Override astGetObjSize. This is to * reduce the in-memory size of a WcsMap. * - HPX and XPH projections included from a more recent version of WCSLIB, * and modified to use scalar instead of vector positions * - The expressions for xc in astHPXrev and phic in astHPXfwd have * been conditioned differently to the WCSLIB code in order to improve * accuracy of the floor function for arguments very slightly below an * integer value. *============================================================================= * * C implementation of the spherical map projections recognized by the FITS * "World Coordinate System" (WCS) convention. * * Summary of routines * ------------------- * Each projection is implemented via separate functions for the forward, * *fwd(), and reverse, *rev(), transformation. * * Initialization routines, *set(), compute intermediate values from the * projection parameters but need not be called explicitly - see the * explanation of prj.flag below. * * astPRJset astPRJfwd astPRJrev Driver routines (see below). * * astAZPset astAZPfwd astAZPrev AZP: zenithal/azimuthal perspective * astSZPset astSZPfwd astSZPrev SZP: slant zenithal perspective * astTANset astTANfwd astTANrev TAN: gnomonic * astSTGset astSTGfwd astSTGrev STG: stereographic * astSINset astSINfwd astSINrev SIN: orthographic/synthesis * astARCset astARCfwd astARCrev ARC: zenithal/azimuthal equidistant * astZPNset astZPNfwd astZPNrev ZPN: zenithal/azimuthal polynomial * astZEAset astZEAfwd astZEArev ZEA: zenithal/azimuthal equal area * astAIRset astAIRfwd astAIRrev AIR: Airy * astCYPset astCYPfwd astCYPrev CYP: cylindrical perspective * astCEAset astCEAfwd astCEArev CEA: cylindrical equal area * astCARset astCARfwd astCARrev CAR: Cartesian * astMERset astMERfwd astMERrev MER: Mercator * astSFLset astSFLfwd astSFLrev SFL: Sanson-Flamsteed * astPARset astPARfwd astPARrev PAR: parabolic * astMOLset astMOLfwd astMOLrev MOL: Mollweide * astAITset astAITfwd astAITrev AIT: Hammer-Aitoff * astCOPset astCOPfwd astCOPrev COP: conic perspective * astCOEset astCOEfwd astCOErev COE: conic equal area * astCODset astCODfwd astCODrev COD: conic equidistant * astCOOset astCOOfwd astCOOrev COO: conic orthomorphic * astBONset astBONfwd astBONrev BON: Bonne * astPCOset astPCOfwd astPCOrev PCO: polyconic * astTSCset astTSCfwd astTSCrev TSC: tangential spherical cube * astCSCset astCSCfwd astCSCrev CSC: COBE quadrilateralized spherical cube * astQSCset astQSCfwd astQSCrev QSC: quadrilateralized spherical cube * astHPXset astHPXfwd astHPXrev HPX: HEALPix projection * astXPHset astXPHfwd astXPHrev XPH: HEALPix polar, aka "butterfly" * * * Driver routines; astPRJset(), astPRJfwd() & astPRJrev() * ---------------------------------------------- * A set of driver routines are available for use as a generic interface to * the specific projection routines. The interfaces to astPRJfwd() and astPRJrev() * are the same as those of the forward and reverse transformation routines * for the specific projections (see below). * * The interface to astPRJset() differs slightly from that of the initialization * routines for the specific projections and unlike them it must be invoked * explicitly to use astPRJfwd() and astPRJrev(). * * Given: * pcode[4] const char * WCS projection code. * * Given and/or returned: * prj AstPrjPrm* Projection parameters (see below). * * Function return value: * int Error status * 0: Success. * * * Initialization routine; *set() * ------------------------------ * Initializes members of a AstPrjPrm data structure which hold intermediate * values. Note that this routine need not be called directly; it will be * invoked by astPRJfwd() and astPRJrev() if the "flag" structure member is * anything other than a predefined magic value. * * Given and/or returned: * prj AstPrjPrm* Projection parameters (see below). * * Function return value: * int Error status * 0: Success. * 1: Invalid projection parameters. * * Forward transformation; *fwd() * ----------------------------- * Compute (x,y) coordinates in the plane of projection from native spherical * coordinates (phi,theta). * * Given: * phi, const double * theta Longitude and latitude of the projected point in * native spherical coordinates, in degrees. * * Given and returned: * prj AstPrjPrm* Projection parameters (see below). * * Returned: * x,y double* Projected coordinates. * * Function return value: * int Error status * 0: Success. * 1: Invalid projection parameters. * 2: Invalid value of (phi,theta). * * Reverse transformation; *rev() * ----------------------------- * Compute native spherical coordinates (phi,theta) from (x,y) coordinates in * the plane of projection. * * Given: * x,y const double * Projected coordinates. * * Given and returned: * prj AstPrjPrm* Projection parameters (see below). * * Returned: * phi, double* Longitude and latitude of the projected point in * theta native spherical coordinates, in degrees. * * Function return value: * int Error status * 0: Success. * 1: Invalid projection parameters. * 2: Invalid value of (x,y). * 1: Invalid projection parameters. * * Projection parameters * --------------------- * The AstPrjPrm struct consists of the following: * * int flag * This flag must be set to zero whenever any of p[] or r0 are set * or changed. This signals the initialization routine to recompute * intermediaries. flag may also be set to -1 to disable strict bounds * checking for the AZP, SZP, TAN, SIN, ZPN, and COP projections. * * double r0 * r0; The radius of the generating sphere for the projection, a linear * scaling parameter. If this is zero, it will be reset to the default * value of 180/pi (the value for FITS WCS). * * double p[] * Contains the projection parameters associated with the * longitude axis. * * The remaining members of the AstPrjPrm struct are maintained by the * initialization routines and should not be modified. This is done for the * sake of efficiency and to allow an arbitrary number of contexts to be * maintained simultaneously. * * char code[4] * Three-letter projection code. * * double phi0, theta0 * Native longitude and latitude of the reference point, in degrees. * * double w[10] * int n * Intermediate values derived from the projection parameters. * * int (*astPRJfwd)() * int (*astPRJrev)() * Pointers to the forward and reverse projection routines. * * Usage of the p[] array as it applies to each projection is described in * the prologue to each trio of projection routines. * * Argument checking * ----------------- * Forward routines: * * The values of phi and theta (the native longitude and latitude) * normally lie in the range [-180,180] for phi, and [-90,90] for theta. * However, all forward projections will accept any value of phi and will * not normalize it. * * The forward projection routines do not explicitly check that theta lies * within the range [-90,90]. They do check for any value of theta which * produces an invalid argument to the projection equations (e.g. leading * to division by zero). The forward routines for AZP, SZP, TAN, SIN, * ZPN, and COP also return error 2 if (phi,theta) corresponds to the * overlapped (far) side of the projection but also return the * corresponding value of (x,y). This strict bounds checking may be * relaxed by setting prj->flag to -1 (rather than 0) when these * projections are initialized. * * Reverse routines: * * Error checking on the projected coordinates (x,y) is limited to that * required to ascertain whether a solution exists. Where a solution does * exist no check is made that the value of phi and theta obtained lie * within the ranges [-180,180] for phi, and [-90,90] for theta. * * Accuracy * -------- * Closure to a precision of at least 1E-10 degree of longitude and latitude * has been verified for typical projection parameters on the 1 degree grid * of native longitude and latitude (to within 5 degrees of any latitude * where the projection may diverge). * * Author: Mark Calabretta, Australia Telescope National Facility * $Id$ *===========================================================================*/ /* Set the name of the module we are implementing. This indicates to the header files that define class interfaces that they should make "protected" symbols available. NB, this module is not a proper AST class, but it defines this macro sanyway in order to get the protected symbols defined in memory.h */ #include #include #include #include "wcsmath.h" #include "wcstrig.h" #include "memory.h" #include "proj.h" /* Following variables are not needed in AST and are commented out to avoid name clashes with other software systems (e.g. SkyCat) which defines them. int npcode = 28; char pcodes[28][4] = {"AZP", "SZP", "TAN", "STG", "SIN", "ARC", "ZPN", "ZEA", "AIR", "CYP", "CEA", "CAR", "MER", "COP", "COE", "COD", "COO", "SFL", "PAR", "MOL", "AIT", "BON", "PCO", "TSC", "CSC", "QSC", "HPX", "XPH"}; */ const int WCS__AZP = 101; const int WCS__SZP = 102; const int WCS__TAN = 103; const int WCS__STG = 104; const int WCS__SIN = 105; const int WCS__ARC = 106; const int WCS__ZPN = 107; const int WCS__ZEA = 108; const int WCS__AIR = 109; const int WCS__CYP = 201; const int WCS__CEA = 202; const int WCS__CAR = 203; const int WCS__MER = 204; const int WCS__SFL = 301; const int WCS__PAR = 302; const int WCS__MOL = 303; const int WCS__AIT = 401; const int WCS__COP = 501; const int WCS__COE = 502; const int WCS__COD = 503; const int WCS__COO = 504; const int WCS__BON = 601; const int WCS__PCO = 602; const int WCS__TSC = 701; const int WCS__CSC = 702; const int WCS__QSC = 703; const int WCS__HPX = 801; const int WCS__XPH = 802; /* Map error number to error message for each function. */ const char *astPRJset_errmsg[] = { 0, "Invalid projection parameters"}; const char *astPRJfwd_errmsg[] = { 0, "Invalid projection parameters", "Invalid value of (phi,theta)"}; const char *astPRJrev_errmsg[] = { 0, "Invalid projection parameters", "Invalid value of (x,y)"}; #define copysign(X, Y) ((Y) < 0.0 ? -fabs(X) : fabs(X)) #define icopysign(X, Y) ((Y) < 0.0 ? -abs(X) : abs(X)) /*==========================================================================*/ int astPRJset(pcode, prj) const char pcode[4]; struct AstPrjPrm *prj; { /* Set pointers to the forward and reverse projection routines. */ if (strcmp(pcode, "AZP") == 0) { astAZPset(prj); } else if (strcmp(pcode, "SZP") == 0) { astSZPset(prj); } else if (strcmp(pcode, "TAN") == 0) { astTANset(prj); } else if (strcmp(pcode, "STG") == 0) { astSTGset(prj); } else if (strcmp(pcode, "SIN") == 0) { astSINset(prj); } else if (strcmp(pcode, "ARC") == 0) { astARCset(prj); } else if (strcmp(pcode, "ZPN") == 0) { astZPNset(prj); } else if (strcmp(pcode, "ZEA") == 0) { astZEAset(prj); } else if (strcmp(pcode, "AIR") == 0) { astAIRset(prj); } else if (strcmp(pcode, "CYP") == 0) { astCYPset(prj); } else if (strcmp(pcode, "CEA") == 0) { astCEAset(prj); } else if (strcmp(pcode, "CAR") == 0) { astCARset(prj); } else if (strcmp(pcode, "MER") == 0) { astMERset(prj); } else if (strcmp(pcode, "SFL") == 0) { astSFLset(prj); } else if (strcmp(pcode, "PAR") == 0) { astPARset(prj); } else if (strcmp(pcode, "MOL") == 0) { astMOLset(prj); } else if (strcmp(pcode, "AIT") == 0) { astAITset(prj); } else if (strcmp(pcode, "COP") == 0) { astCOPset(prj); } else if (strcmp(pcode, "COE") == 0) { astCOEset(prj); } else if (strcmp(pcode, "COD") == 0) { astCODset(prj); } else if (strcmp(pcode, "COO") == 0) { astCOOset(prj); } else if (strcmp(pcode, "BON") == 0) { astBONset(prj); } else if (strcmp(pcode, "PCO") == 0) { astPCOset(prj); } else if (strcmp(pcode, "TSC") == 0) { astTSCset(prj); } else if (strcmp(pcode, "CSC") == 0) { astCSCset(prj); } else if (strcmp(pcode, "QSC") == 0) { astQSCset(prj); } else if (strcmp(pcode, "HPX") == 0) { astHPXset(prj); } else if (strcmp(pcode, "XPH") == 0) { astXPHset(prj); } else { /* Unrecognized projection code. */ return 1; } return 0; } /*--------------------------------------------------------------------------*/ int astPRJfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { return prj->astPRJfwd(phi, theta, prj, x, y); } /*--------------------------------------------------------------------------*/ int astPRJrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { return prj->astPRJrev(x, y, prj, phi, theta); } /*============================================================================ * AZP: zenithal/azimuthal perspective projection. * * Given: * prj->p[1] Distance parameter, mu in units of r0. * prj->p[2] Tilt angle, gamma in degrees. * * Given and/or returned: * prj->flag AZP, or -AZP if prj->flag is given < 0. * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "AZP" * prj->phi0 0.0 * prj->theta0 90.0 * prj->w[0] r0*(mu+1) * prj->w[1] tan(gamma) * prj->w[2] sec(gamma) * prj->w[3] cos(gamma) * prj->w[4] sin(gamma) * prj->w[5] asin(-1/mu) for |mu| >= 1, -90 otherwise * prj->w[6] mu*cos(gamma) * prj->w[7] 1 if |mu*cos(gamma)| < 1, 0 otherwise * prj->astPRJfwd Pointer to astAZPfwd(). * prj->astPRJrev Pointer to astAZPrev(). *===========================================================================*/ int astAZPset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "AZP"); prj->flag = icopysign(WCS__AZP, prj->flag); prj->phi0 = 0.0; prj->theta0 = 90.0; if (prj->r0 == 0.0) prj->r0 = R2D; prj->w[0] = prj->r0*(prj->p[1] + 1.0); if (prj->w[0] == 0.0) { return 1; } prj->w[3] = astCosd(prj->p[2]); if (prj->w[3] == 0.0) { return 1; } prj->w[2] = 1.0/prj->w[3]; prj->w[4] = astSind(prj->p[2]); prj->w[1] = prj->w[4] / prj->w[3]; if (fabs(prj->p[1]) > 1.0) { prj->w[5] = astASind(-1.0/prj->p[1]); } else { prj->w[5] = -90.0; } prj->w[6] = prj->p[1] * prj->w[3]; prj->w[7] = (fabs(prj->w[6]) < 1.0) ? 1.0 : 0.0; prj->astPRJfwd = astAZPfwd; prj->astPRJrev = astAZPrev; return 0; } /*--------------------------------------------------------------------------*/ int astAZPfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double a, b, cphi, cthe, r, s, t; if (abs(prj->flag) != WCS__AZP) { if (astAZPset(prj)) return 1; } cphi = astCosd(phi); cthe = astCosd(theta); s = prj->w[1]*cphi; t = (prj->p[1] + astSind(theta)) + cthe*s; if (t == 0.0) { return 2; } r = prj->w[0]*cthe/t; *x = r*astSind(phi); *y = -r*cphi*prj->w[2]; /* Bounds checking. */ if (prj->flag > 0) { /* Overlap. */ if (theta < prj->w[5]) { return 2; } /* Divergence. */ if (prj->w[7] > 0.0) { t = prj->p[1] / sqrt(1.0 + s*s); if (fabs(t) <= 1.0) { s = astATand(-s); t = astASind(t); a = s - t; b = s + t + 180.0; if (a > 90.0) a -= 360.0; if (b > 90.0) b -= 360.0; if (theta < ((a > b) ? a : b)) { return 2; } } } } return 0; } /*--------------------------------------------------------------------------*/ int astAZPrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double a, b, r, s, t, ycosg; const double tol = 1.0e-13; if (abs(prj->flag) != WCS__AZP) { if (astAZPset(prj)) return 1; } ycosg = y*prj->w[3]; r = sqrt(x*x + ycosg*ycosg); if (r == 0.0) { *phi = 0.0; *theta = 90.0; } else { *phi = astATan2d(x, -ycosg); s = r / (prj->w[0] + y*prj->w[4]); t = s*prj->p[1]/sqrt(s*s + 1.0); s = astATan2d(1.0, s); if (fabs(t) > 1.0) { t = copysign(90.0,t); if (fabs(t) > 1.0+tol) { return 2; } } else { t = astASind(t); } a = s - t; b = s + t + 180.0; if (a > 90.0) a -= 360.0; if (b > 90.0) b -= 360.0; *theta = (a > b) ? a : b; } return 0; } /*============================================================================ * SZP: slant zenithal perspective projection. * * Given: * prj->p[1] Distance of the point of projection from the centre of the * generating sphere, mu in units of r0. * prj->p[2] Native longitude, phi_c, and ... * prj->p[3] Native latitude, theta_c, on the planewards side of the * intersection of the line through the point of projection * and the centre of the generating sphere, phi_c in degrees. * * Given and/or returned: * prj->flag SZP, or -SZP if prj->flag is given < 0. * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "SZP" * prj->phi0 0.0 * prj->theta0 90.0 * prj->w[0] 1/r0 * prj->w[1] xp = -mu*cos(theta_c)*sin(phi_c) * prj->w[2] yp = mu*cos(theta_c)*cos(phi_c) * prj->w[3] zp = mu*sin(theta_c) + 1 * prj->w[4] r0*xp * prj->w[5] r0*yp * prj->w[6] r0*zp * prj->w[7] (zp - 1)^2 * prj->w[8] asin(1-zp) if |1 - zp| < 1, -90 otherwise * prj->astPRJfwd Pointer to astSZPfwd(). * prj->astPRJrev Pointer to astSZPrev(). *===========================================================================*/ int astSZPset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "SZP"); prj->flag = icopysign(WCS__SZP, prj->flag); prj->phi0 = 0.0; prj->theta0 = 90.0; if (prj->r0 == 0.0) prj->r0 = R2D; prj->w[0] = 1.0/prj->r0; prj->w[3] = prj->p[1] * astSind(prj->p[3]) + 1.0; if (prj->w[3] == 0.0) { return 1; } prj->w[1] = -prj->p[1] * astCosd(prj->p[3]) * astSind(prj->p[2]); prj->w[2] = prj->p[1] * astCosd(prj->p[3]) * astCosd(prj->p[2]); prj->w[4] = prj->r0 * prj->w[1]; prj->w[5] = prj->r0 * prj->w[2]; prj->w[6] = prj->r0 * prj->w[3]; prj->w[7] = (prj->w[3] - 1.0) * prj->w[3] - 1.0; if (fabs(prj->w[3] - 1.0) < 1.0) { prj->w[8] = astASind(1.0 - prj->w[3]); } else { prj->w[8] = -90.0; } prj->astPRJfwd = astSZPfwd; prj->astPRJrev = astSZPrev; return 0; } /*--------------------------------------------------------------------------*/ int astSZPfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double a, b, cphi, cthe, s, sphi, t; if (abs(prj->flag) != WCS__SZP) { if (astSZPset(prj)) return 1; } cphi = astCosd(phi); sphi = astSind(phi); cthe = astCosd(theta); s = 1.0 - astSind(theta); t = prj->w[3] - s; if (t == 0.0) { return 2; } *x = (prj->w[6]*cthe*sphi - prj->w[4]*s)/t; *y = -(prj->w[6]*cthe*cphi + prj->w[5]*s)/t; /* Bounds checking. */ if (prj->flag > 0) { /* Divergence. */ if (theta < prj->w[8]) { return 2; } /* Overlap. */ if (fabs(prj->p[1]) > 1.0) { s = prj->w[1]*sphi - prj->w[2]*cphi; t = 1.0/sqrt(prj->w[7] + s*s); if (fabs(t) <= 1.0) { s = astATan2d(s, prj->w[3] - 1.0); t = astASind(t); a = s - t; b = s + t + 180.0; if (a > 90.0) a -= 360.0; if (b > 90.0) b -= 360.0; if (theta < ((a > b) ? a : b)) { return 2; } } } } return 0; } /*--------------------------------------------------------------------------*/ int astSZPrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double a, b, c, d, r2, sth1, sth2, sthe, sxy, t, x1, xp, y1, yp, z; const double tol = 1.0e-13; if (abs(prj->flag) != WCS__SZP) { if (astSZPset(prj)) return 1; } xp = x*prj->w[0]; yp = y*prj->w[0]; r2 = xp*xp + yp*yp; x1 = (xp - prj->w[1])/prj->w[3]; y1 = (yp - prj->w[2])/prj->w[3]; sxy = xp*x1 + yp*y1; if (r2 < 1.0e-10) { /* Use small angle formula. */ z = r2/2.0; *theta = 90.0 - R2D*sqrt(r2/(1.0 + sxy)); } else { t = x1*x1 + y1*y1; a = t + 1.0; b = sxy - t; c = r2 - sxy - sxy + t - 1.0; d = b*b - a*c; /* Check for a solution. */ if (d < 0.0) { return 2; } d = sqrt(d); /* Choose solution closest to pole. */ sth1 = (-b + d)/a; sth2 = (-b - d)/a; sthe = (sth1 > sth2) ? sth1 : sth2; if (sthe > 1.0) { if (sthe-1.0 < tol) { sthe = 1.0; } else { sthe = (sth1 < sth2) ? sth1 : sth2; } } if (sthe < -1.0) { if (sthe+1.0 > -tol) { sthe = -1.0; } } if (sthe > 1.0 || sthe < -1.0) { return 2; } *theta = astASind(sthe); z = 1.0 - sthe; } *phi = astATan2d(xp - x1*z, -(yp - y1*z)); return 0; } /*============================================================================ * TAN: gnomonic projection. * * Given and/or returned: * prj->flag TAN, or -TAN if prj->flag is given < 0. * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "TAN" * prj->phi0 0.0 * prj->theta0 90.0 * prj->astPRJfwd Pointer to astTANfwd(). * prj->astPRJrev Pointer to astTANrev(). *===========================================================================*/ int astTANset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "TAN"); prj->flag = icopysign(WCS__TAN, prj->flag); prj->phi0 = 0.0; prj->theta0 = 90.0; if (prj->r0 == 0.0) prj->r0 = R2D; prj->astPRJfwd = astTANfwd; prj->astPRJrev = astTANrev; return 0; } /*--------------------------------------------------------------------------*/ int astTANfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double r, s; if (abs(prj->flag) != WCS__TAN) { if(astTANset(prj)) return 1; } s = astSind(theta); if (s == 0.0) { return 2; } r = prj->r0*astCosd(theta)/s; *x = r*astSind(phi); *y = -r*astCosd(phi); if (prj->flag > 0 && s < 0.0) { return 2; } return 0; } /*--------------------------------------------------------------------------*/ int astTANrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double r; if (abs(prj->flag) != WCS__TAN) { if (astTANset(prj)) return 1; } r = sqrt(x*x + y*y); if (r == 0.0) { *phi = 0.0; } else { *phi = astATan2d(x, -y); } *theta = astATan2d(prj->r0, r); return 0; } /*============================================================================ * STG: stereographic projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "STG" * prj->flag STG * prj->phi0 0.0 * prj->theta0 90.0 * prj->w[0] 2*r0 * prj->w[1] 1/(2*r0) * prj->astPRJfwd Pointer to astSTGfwd(). * prj->astPRJrev Pointer to astSTGrev(). *===========================================================================*/ int astSTGset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "STG"); prj->flag = WCS__STG; prj->phi0 = 0.0; prj->theta0 = 90.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 360.0/PI; prj->w[1] = PI/360.0; } else { prj->w[0] = 2.0*prj->r0; prj->w[1] = 1.0/prj->w[0]; } prj->astPRJfwd = astSTGfwd; prj->astPRJrev = astSTGrev; return 0; } /*--------------------------------------------------------------------------*/ int astSTGfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double r, s; if (prj->flag != WCS__STG) { if (astSTGset(prj)) return 1; } s = 1.0 + astSind(theta); if (s == 0.0) { return 2; } r = prj->w[0]*astCosd(theta)/s; *x = r*astSind(phi); *y = -r*astCosd(phi); return 0; } /*--------------------------------------------------------------------------*/ int astSTGrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double r; if (prj->flag != WCS__STG) { if (astSTGset(prj)) return 1; } r = sqrt(x*x + y*y); if (r == 0.0) { *phi = 0.0; } else { *phi = astATan2d(x, -y); } *theta = 90.0 - 2.0*astATand(r*prj->w[1]); return 0; } /*============================================================================ * SIN: orthographic/synthesis projection. * * Given: * prj->p[1:2] Obliqueness parameters, xi and eta. * * Given and/or returned: * prj->flag SIN, or -SIN if prj->flag is given < 0. * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "SIN" * prj->phi0 0.0 * prj->theta0 90.0 * prj->w[0] 1/r0 * prj->w[1] xi**2 + eta**2 * prj->w[2] xi**2 + eta**2 + 1 * prj->w[3] xi**2 + eta**2 - 1 * prj->astPRJfwd Pointer to astSINfwd(). * prj->astPRJrev Pointer to astSINrev(). *===========================================================================*/ int astSINset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "SIN"); prj->flag = icopysign(WCS__SIN, prj->flag); prj->phi0 = 0.0; prj->theta0 = 90.0; if (prj->r0 == 0.0) prj->r0 = R2D; prj->w[0] = 1.0/prj->r0; prj->w[1] = prj->p[1]*prj->p[1] + prj->p[2]*prj->p[2]; prj->w[2] = prj->w[1] + 1.0; prj->w[3] = prj->w[1] - 1.0; prj->astPRJfwd = astSINfwd; prj->astPRJrev = astSINrev; return 0; } /*--------------------------------------------------------------------------*/ int astSINfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double cphi, cthe, sphi, t, z; if (abs(prj->flag) != WCS__SIN) { if (astSINset(prj)) return 1; } t = (90.0 - fabs(theta))*D2R; if (t < 1.0e-5) { if (theta > 0.0) { z = t*t/2.0; } else { z = 2.0 - t*t/2.0; } cthe = t; } else { z = 1.0 - astSind(theta); cthe = astCosd(theta); } cphi = astCosd(phi); sphi = astSind(phi); *x = prj->r0*(cthe*sphi + prj->p[1]*z); *y = -prj->r0*(cthe*cphi - prj->p[2]*z); /* Validate this solution. */ if (prj->flag > 0) { if (prj->w[1] == 0.0) { /* Orthographic projection. */ if (theta < 0.0) { return 2; } } else { /* "Synthesis" projection. */ t = -astATand(prj->p[1]*sphi - prj->p[2]*cphi); if (theta < t) { return 2; } } } return 0; } /*--------------------------------------------------------------------------*/ int astSINrev (x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { const double tol = 1.0e-13; double a, b, c, d, r2, sth1, sth2, sthe, sxy, x0, x1, xp, y0, y1, yp, z; if (abs(prj->flag) != WCS__SIN) { if (astSINset(prj)) return 1; } /* Compute intermediaries. */ x0 = x*prj->w[0]; y0 = y*prj->w[0]; r2 = x0*x0 + y0*y0; if (prj->w[1] == 0.0) { /* Orthographic projection. */ if (r2 != 0.0) { *phi = astATan2d(x0, -y0); } else { *phi = 0.0; } if (r2 < 0.5) { *theta = astACosd(sqrt(r2)); } else if (r2 <= 1.0) { *theta = astASind(sqrt(1.0 - r2)); } else { return 2; } } else { /* "Synthesis" projection. */ x1 = prj->p[1]; y1 = prj->p[2]; sxy = x0*x1 + y0*y1; if (r2 < 1.0e-10) { /* Use small angle formula. */ z = r2/2.0; *theta = 90.0 - R2D*sqrt(r2/(1.0 + sxy)); } else { a = prj->w[2]; b = sxy - prj->w[1]; c = r2 - sxy - sxy + prj->w[3]; d = b*b - a*c; /* Check for a solution. */ if (d < 0.0) { return 2; } d = sqrt(d); /* Choose solution closest to pole. */ sth1 = (-b + d)/a; sth2 = (-b - d)/a; sthe = (sth1 > sth2) ? sth1 : sth2; if (sthe > 1.0) { if (sthe-1.0 < tol) { sthe = 1.0; } else { sthe = (sth1 < sth2) ? sth1 : sth2; } } if (sthe < -1.0) { if (sthe+1.0 > -tol) { sthe = -1.0; } } if (sthe > 1.0 || sthe < -1.0) { return 2; } *theta = astASind(sthe); z = 1.0 - sthe; } xp = -y0 + prj->p[2]*z; yp = x0 - prj->p[1]*z; if (xp == 0.0 && yp == 0.0) { *phi = 0.0; } else { *phi = astATan2d(yp,xp); } } return 0; } /*============================================================================ * ARC: zenithal/azimuthal equidistant projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "ARC" * prj->flag ARC * prj->phi0 0.0 * prj->theta0 90.0 * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->astPRJfwd Pointer to astARCfwd(). * prj->astPRJrev Pointer to astARCrev(). *===========================================================================*/ int astARCset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "ARC"); prj->flag = WCS__ARC; prj->phi0 = 0.0; prj->theta0 = 90.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 1.0; prj->w[1] = 1.0; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = 1.0/prj->w[0]; } prj->astPRJfwd = astARCfwd; prj->astPRJrev = astARCrev; return 0; } /*--------------------------------------------------------------------------*/ int astARCfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double r; if (prj->flag != WCS__ARC) { if (astARCset(prj)) return 1; } r = prj->w[0]*(90.0 - theta); *x = r*astSind(phi); *y = -r*astCosd(phi); return 0; } /*--------------------------------------------------------------------------*/ int astARCrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double r; if (prj->flag != WCS__ARC) { if (astARCset(prj)) return 1; } r = sqrt(x*x + y*y); if (r == 0.0) { *phi = 0.0; } else { *phi = astATan2d(x, -y); } *theta = 90.0 - r*prj->w[1]; return 0; } /*============================================================================ * ZPN: zenithal/azimuthal polynomial projection. * * Given: * prj->p[0:WCSLIB_MXPAR-1] Polynomial coefficients. * * Given and/or returned: * prj->flag ZPN, or -ZPN if prj->flag is given < 0. * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "ZPN" * prj->phi0 0.0 * prj->theta0 90.0 * prj->n Degree of the polynomial, N. * prj->w[0] Co-latitude of the first point of inflection (N > 2). * prj->w[1] Radius of the first point of inflection (N > 2). * prj->astPRJfwd Pointer to astZPNfwd(). * prj->astPRJrev Pointer to astZPNrev(). *===========================================================================*/ int astZPNset(prj) struct AstPrjPrm *prj; { int i, j, k, plen; double d, d1, d2, r, zd, zd1, zd2; const double tol = 1.0e-13; strcpy(prj->code, "ZPN"); prj->flag = icopysign(WCS__ZPN, prj->flag); prj->phi0 = 0.0; prj->theta0 = 90.0; if (prj->r0 == 0.0) prj->r0 = R2D; /* Find the highest non-zero coefficient. */ plen = astSizeOf( prj->p )/sizeof( double ); for (k = plen-1; k >= 0 && prj->p[k] == 0.0; k--); if (k < 0) return 1; prj->n = k; if (k >= 3) { /* Find the point of inflection closest to the pole. */ zd1 = 0.0; d1 = prj->p[1]; if (d1 <= 0.0) { return 1; } /* Find the point where the derivative first goes negative. */ for (i = 0; i < 180; i++) { zd2 = i*D2R; d2 = 0.0; for (j = k; j > 0; j--) { d2 = d2*zd2 + j*prj->p[j]; } if (d2 <= 0.0) break; zd1 = zd2; d1 = d2; } if (i == 180) { /* No negative derivative -> no point of inflection. */ zd = PI; } else { /* Find where the derivative is zero. */ for (i = 1; i <= 10; i++) { zd = zd1 - d1*(zd2-zd1)/(d2-d1); d = 0.0; for (j = k; j > 0; j--) { d = d*zd + j*prj->p[j]; } if (fabs(d) < tol) break; if (d < 0.0) { zd2 = zd; d2 = d; } else { zd1 = zd; d1 = d; } } } r = 0.0; for (j = k; j >= 0; j--) { r = r*zd + prj->p[j]; } prj->w[0] = zd; prj->w[1] = r; } prj->astPRJfwd = astZPNfwd; prj->astPRJrev = astZPNrev; return 0; } /*--------------------------------------------------------------------------*/ int astZPNfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { int j; double r, s; if (abs(prj->flag) != WCS__ZPN) { if (astZPNset(prj)) return 1; } s = (90.0 - theta)*D2R; r = 0.0; for (j = prj->n; j >= 0; j--) { r = r*s + prj->p[j]; } r = prj->r0*r; *x = r*astSind(phi); *y = -r*astCosd(phi); if (prj->flag > 0 && s > prj->w[0] && prj->n > 2 ) { return 2; } return 0; } /*--------------------------------------------------------------------------*/ int astZPNrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { int i, j, k; double a, b, c, d, lambda, r, r1, r2, rt, zd, zd1, zd2; const double tol = 1.0e-13; if (abs(prj->flag) != WCS__ZPN) { if (astZPNset(prj)) return 1; } k = prj->n; r = sqrt(x*x + y*y)/prj->r0; if (k < 1) { /* Constant - no solution. */ return 1; } else if (k == 1) { /* Linear. */ zd = (r - prj->p[0])/prj->p[1]; } else if (k == 2) { /* Quadratic. */ a = prj->p[2]; b = prj->p[1]; c = prj->p[0] - r; d = b*b - 4.0*a*c; if (d < 0.0) { return 2; } d = sqrt(d); /* Choose solution closest to pole. */ zd1 = (-b + d)/(2.0*a); zd2 = (-b - d)/(2.0*a); zd = (zd1zd2) ? zd1 : zd2; if (zd < 0.0) { if (zd < -tol) { return 2; } zd = 0.0; } else if (zd > PI) { if (zd > PI+tol) { return 2; } zd = PI; } } else { /* Higher order - solve iteratively. */ zd1 = 0.0; r1 = prj->p[0]; zd2 = prj->w[0]; r2 = prj->w[1]; if (r < r1) { if (r < r1-tol) { return 2; } zd = zd1; } else if (r > r2) { if (r > r2+tol) { return 2; } zd = zd2; } else { /* Disect the interval. */ for (j = 0; j < 100; j++) { lambda = (r2 - r)/(r2 - r1); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { lambda = 0.9; } zd = zd2 - lambda*(zd2 - zd1); rt = 0.0; for (i = k; i >= 0; i--) { rt = (rt * zd) + prj->p[i]; } if (rt < r) { if (r-rt < tol) break; r1 = rt; zd1 = zd; } else { if (rt-r < tol) break; r2 = rt; zd2 = zd; } if (fabs(zd2-zd1) < tol) break; } } } if (r == 0.0) { *phi = 0.0; } else { *phi = astATan2d(x, -y); } *theta = 90.0 - zd*R2D; return 0; } /*============================================================================ * ZEA: zenithal/azimuthal equal area projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "ZEA" * prj->flag ZEA * prj->phi0 0.0 * prj->theta0 90.0 * prj->w[0] 2*r0 * prj->w[1] 1/(2*r0) * prj->astPRJfwd Pointer to astZEAfwd(). * prj->astPRJrev Pointer to astZEArev(). *===========================================================================*/ int astZEAset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "ZEA"); prj->flag = WCS__ZEA; prj->phi0 = 0.0; prj->theta0 = 90.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 360.0/PI; prj->w[1] = PI/360.0; } else { prj->w[0] = 2.0*prj->r0; prj->w[1] = 1.0/prj->w[0]; } prj->astPRJfwd = astZEAfwd; prj->astPRJrev = astZEArev; return 0; } /*--------------------------------------------------------------------------*/ int astZEAfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double r; if (prj->flag != WCS__ZEA) { if (astZEAset(prj)) return 1; } r = prj->w[0]*astSind((90.0 - theta)/2.0); *x = r*astSind(phi); *y = -r*astCosd(phi); return 0; } /*--------------------------------------------------------------------------*/ int astZEArev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double r, s; const double tol = 1.0e-12; if (prj->flag != WCS__ZEA) { if (astZEAset(prj)) return 1; } r = sqrt(x*x + y*y); if (r == 0.0) { *phi = 0.0; } else { *phi = astATan2d(x, -y); } s = r*prj->w[1]; if (fabs(s) > 1.0) { if (fabs(r - prj->w[0]) < tol) { *theta = -90.0; } else { return 2; } } else { *theta = 90.0 - 2.0*astASind(s); } return 0; } /*============================================================================ * AIR: Airy's projection. * * Given: * prj->p[1] Latitude theta_b within which the error is minimized, in * degrees. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "AIR" * prj->flag AIR * prj->phi0 0.0 * prj->theta0 90.0 * prj->w[0] 2*r0 * prj->w[1] ln(cos(xi_b))/tan(xi_b)**2, where xi_b = (90-theta_b)/2 * prj->w[2] 1/2 - prj->w[1] * prj->w[3] 2*r0*prj->w[2] * prj->w[4] tol, cutoff for using small angle approximation, in * radians. * prj->w[5] prj->w[2]*tol * prj->w[6] (180/pi)/prj->w[2] * prj->astPRJfwd Pointer to astAIRfwd(). * prj->astPRJrev Pointer to astAIRrev(). *===========================================================================*/ int astAIRset(prj) struct AstPrjPrm *prj; { const double tol = 1.0e-4; double cxi; strcpy(prj->code, "AIR"); prj->flag = WCS__AIR; prj->phi0 = 0.0; prj->theta0 = 90.0; if (prj->r0 == 0.0) prj->r0 = R2D; prj->w[0] = 2.0*prj->r0; if (prj->p[1] == 90.0) { prj->w[1] = -0.5; prj->w[2] = 1.0; } else if (prj->p[1] > -90.0) { cxi = astCosd((90.0 - prj->p[1])/2.0); prj->w[1] = log(cxi)*(cxi*cxi)/(1.0-cxi*cxi); prj->w[2] = 0.5 - prj->w[1]; } else { return 1; } prj->w[3] = prj->w[0] * prj->w[2]; prj->w[4] = tol; prj->w[5] = prj->w[2]*tol; prj->w[6] = R2D/prj->w[2]; prj->astPRJfwd = astAIRfwd; prj->astPRJrev = astAIRrev; return 0; } /*--------------------------------------------------------------------------*/ int astAIRfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double cxi, r, txi, xi; if (prj->flag != WCS__AIR) { if (astAIRset(prj)) return 1; } if (theta == 90.0) { r = 0.0; } else if (theta > -90.0) { xi = D2R*(90.0 - theta)/2.0; if (xi < prj->w[4]) { r = xi*prj->w[3]; } else { cxi = astCosd((90.0 - theta)/2.0); txi = sqrt(1.0-cxi*cxi)/cxi; r = -prj->w[0]*(log(cxi)/txi + prj->w[1]*txi); } } else { return 2; } *x = r*astSind(phi); *y = -r*astCosd(phi); return 0; } /*--------------------------------------------------------------------------*/ int astAIRrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { int j; double cxi, lambda, r, r1, r2, rt, txi, x1, x2, xi; const double tol = 1.0e-12; if (prj->flag != WCS__AIR) { if (astAIRset(prj)) return 1; } r = sqrt(x*x + y*y)/prj->w[0]; if (r == 0.0) { xi = 0.0; } else if (r < prj->w[5]) { xi = r*prj->w[6]; } else { /* Find a solution interval. */ x1 = 1.0; r1 = 0.0; for (j = 0; j < 30; j++) { x2 = x1/2.0; txi = sqrt(1.0-x2*x2)/x2; r2 = -(log(x2)/txi + prj->w[1]*txi); if (r2 >= r) break; x1 = x2; r1 = r2; } if (j == 30) return 2; for (j = 0; j < 100; j++) { /* Weighted division of the interval. */ lambda = (r2-r)/(r2-r1); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { lambda = 0.9; } cxi = x2 - lambda*(x2-x1); txi = sqrt(1.0-cxi*cxi)/cxi; rt = -(log(cxi)/txi + prj->w[1]*txi); if (rt < r) { if (r-rt < tol) break; r1 = rt; x1 = cxi; } else { if (rt-r < tol) break; r2 = rt; x2 = cxi; } } if (j == 100) return 2; xi = astACosd(cxi); } if (r == 0.0) { *phi = 0.0; } else { *phi = astATan2d(x, -y); } *theta = 90.0 - 2.0*xi; return 0; } /*============================================================================ * CYP: cylindrical perspective projection. * * Given: * prj->p[1] Distance of point of projection from the centre of the * generating sphere, mu, in units of r0. * prj->p[2] Radius of the cylinder of projection, lambda, in units of * r0. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "CYP" * prj->flag CYP * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*lambda*(pi/180) * prj->w[1] (180/pi)/(r0*lambda) * prj->w[2] r0*(mu + lambda) * prj->w[3] 1/(r0*(mu + lambda)) * prj->astPRJfwd Pointer to astCYPfwd(). * prj->astPRJrev Pointer to astCYPrev(). *===========================================================================*/ int astCYPset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "CYP"); prj->flag = WCS__CYP; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = prj->p[2]; if (prj->w[0] == 0.0) { return 1; } prj->w[1] = 1.0/prj->w[0]; prj->w[2] = R2D*(prj->p[1] + prj->p[2]); if (prj->w[2] == 0.0) { return 1; } prj->w[3] = 1.0/prj->w[2]; } else { prj->w[0] = prj->r0*prj->p[2]*D2R; if (prj->w[0] == 0.0) { return 1; } prj->w[1] = 1.0/prj->w[0]; prj->w[2] = prj->r0*(prj->p[1] + prj->p[2]); if (prj->w[2] == 0.0) { return 1; } prj->w[3] = 1.0/prj->w[2]; } prj->astPRJfwd = astCYPfwd; prj->astPRJrev = astCYPrev; return 0; } /*--------------------------------------------------------------------------*/ int astCYPfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double s; if (prj->flag != WCS__CYP) { if (astCYPset(prj)) return 1; } s = prj->p[1] + astCosd(theta); if (s == 0.0) { return 2; } *x = prj->w[0]*phi; *y = prj->w[2]*astSind(theta)/s; return 0; } /*--------------------------------------------------------------------------*/ int astCYPrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double eta; double a; const double tol = 1.0e-13; if (prj->flag != WCS__CYP) { if (astCYPset(prj)) return 1; } *phi = x*prj->w[1]; eta = y*prj->w[3]; a = eta*prj->p[1]/sqrt(eta*eta+1.0); if( fabs( a ) < 1.0 ) { *theta = astATan2d(eta,1.0) + astASind( a ); } else if( fabs( a ) < 1.0 + tol ) { if( a > 0.0 ){ *theta = astATan2d(eta,1.0) + 90.0; } else { *theta = astATan2d(eta,1.0) - 90.0; } } else { return 2; } return 0; } /*============================================================================ * CEA: cylindrical equal area projection. * * Given: * prj->p[1] Square of the cosine of the latitude at which the * projection is conformal, lambda. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "CEA" * prj->flag CEA * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->w[2] r0/lambda * prj->w[3] lambda/r0 * prj->astPRJfwd Pointer to astCEAfwd(). * prj->astPRJrev Pointer to astCEArev(). *===========================================================================*/ int astCEAset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "CEA"); prj->flag = WCS__CEA; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 1.0; prj->w[1] = 1.0; if (prj->p[1] <= 0.0 || prj->p[1] > 1.0) { return 1; } prj->w[2] = prj->r0/prj->p[1]; prj->w[3] = prj->p[1]/prj->r0; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = R2D/prj->r0; if (prj->p[1] <= 0.0 || prj->p[1] > 1.0) { return 1; } prj->w[2] = prj->r0/prj->p[1]; prj->w[3] = prj->p[1]/prj->r0; } prj->astPRJfwd = astCEAfwd; prj->astPRJrev = astCEArev; return 0; } /*--------------------------------------------------------------------------*/ int astCEAfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { if (prj->flag != WCS__CEA) { if (astCEAset(prj)) return 1; } *x = prj->w[0]*phi; *y = prj->w[2]*astSind(theta); return 0; } /*--------------------------------------------------------------------------*/ int astCEArev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double s; const double tol = 1.0e-13; if (prj->flag != WCS__CEA) { if (astCEAset(prj)) return 1; } s = y*prj->w[3]; if (fabs(s) > 1.0) { if (fabs(s) > 1.0+tol) { return 2; } s = copysign(1.0,s); } *phi = x*prj->w[1]; *theta = astASind(s); return 0; } /*============================================================================ * CAR: Cartesian projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "CAR" * prj->flag CAR * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->astPRJfwd Pointer to astCARfwd(). * prj->astPRJrev Pointer to astCARrev(). *===========================================================================*/ int astCARset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "CAR"); prj->flag = WCS__CAR; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 1.0; prj->w[1] = 1.0; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = 1.0/prj->w[0]; } prj->astPRJfwd = astCARfwd; prj->astPRJrev = astCARrev; return 0; } /*--------------------------------------------------------------------------*/ int astCARfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { if (prj->flag != WCS__CAR) { if (astCARset(prj)) return 1; } *x = prj->w[0]*phi; *y = prj->w[0]*theta; return 0; } /*--------------------------------------------------------------------------*/ int astCARrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { if (prj->flag != WCS__CAR) { if (astCARset(prj)) return 1; } *phi = prj->w[1]*x; *theta = prj->w[1]*y; return 0; } /*============================================================================ * MER: Mercator's projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "MER" * prj->flag MER * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->astPRJfwd Pointer to astMERfwd(). * prj->astPRJrev Pointer to astMERrev(). *===========================================================================*/ int astMERset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "MER"); prj->flag = WCS__MER; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 1.0; prj->w[1] = 1.0; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = 1.0/prj->w[0]; } prj->astPRJfwd = astMERfwd; prj->astPRJrev = astMERrev; return 0; } /*--------------------------------------------------------------------------*/ int astMERfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { if (prj->flag != WCS__MER) { if (astMERset(prj)) return 1; } if (theta <= -90.0 || theta >= 90.0) { return 2; } *x = prj->w[0]*phi; *y = prj->r0*log(astTand((90.0+theta)/2.0)); return 0; } /*--------------------------------------------------------------------------*/ int astMERrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { if (prj->flag != WCS__MER) { if (astMERset(prj)) return 1; } *phi = x*prj->w[1]; *theta = 2.0*astATand(exp(y/prj->r0)) - 90.0; return 0; } /*============================================================================ * SFL: Sanson-Flamsteed ("global sinusoid") projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "SFL" * prj->flag SFL * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->astPRJfwd Pointer to astSFLfwd(). * prj->astPRJrev Pointer to astSFLrev(). *===========================================================================*/ int astSFLset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "SFL"); prj->flag = WCS__SFL; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 1.0; prj->w[1] = 1.0; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = 1.0/prj->w[0]; } prj->astPRJfwd = astSFLfwd; prj->astPRJrev = astSFLrev; return 0; } /*--------------------------------------------------------------------------*/ int astSFLfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { if (prj->flag != WCS__SFL) { if (astSFLset(prj)) return 1; } *x = prj->w[0]*phi*astCosd(theta); *y = prj->w[0]*theta; return 0; } /*--------------------------------------------------------------------------*/ int astSFLrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double w; if (prj->flag != WCS__SFL) { if (astSFLset(prj)) return 1; } w = cos(y/prj->r0); if (w == 0.0) { *phi = 0.0; } else { *phi = x*prj->w[1]/cos(y/prj->r0); } *theta = y*prj->w[1]; return 0; } /*============================================================================ * PAR: parabolic projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "PAR" * prj->flag PAR * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->w[2] pi*r0 * prj->w[3] 1/(pi*r0) * prj->astPRJfwd Pointer to astPARfwd(). * prj->astPRJrev Pointer to astPARrev(). *===========================================================================*/ int astPARset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "PAR"); prj->flag = WCS__PAR; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 1.0; prj->w[1] = 1.0; prj->w[2] = 180.0; prj->w[3] = 1.0/prj->w[2]; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = 1.0/prj->w[0]; prj->w[2] = PI*prj->r0; prj->w[3] = 1.0/prj->w[2]; } prj->astPRJfwd = astPARfwd; prj->astPRJrev = astPARrev; return 0; } /*--------------------------------------------------------------------------*/ int astPARfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double s; if (prj->flag != WCS__PAR) { if (astPARset(prj)) return 1; } s = astSind(theta/3.0); *x = prj->w[0]*phi*(1.0 - 4.0*s*s); *y = prj->w[2]*s; return 0; } /*--------------------------------------------------------------------------*/ int astPARrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double s, t; if (prj->flag != WCS__PAR) { if (astPARset(prj)) return 1; } s = y*prj->w[3]; if (s > 1.0 || s < -1.0) { return 2; } t = 1.0 - 4.0*s*s; if (t == 0.0) { if (x == 0.0) { *phi = 0.0; } else { return 2; } } else { *phi = prj->w[1]*x/t; } *theta = 3.0*astASind(s); return 0; } /*============================================================================ * MOL: Mollweide's projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "MOL" * prj->flag MOL * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] sqrt(2)*r0 * prj->w[1] sqrt(2)*r0/90 * prj->w[2] 1/(sqrt(2)*r0) * prj->w[3] 90/r0 * prj->astPRJfwd Pointer to astMOLfwd(). * prj->astPRJrev Pointer to astMOLrev(). *===========================================================================*/ int astMOLset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "MOL"); prj->flag = WCS__MOL; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) prj->r0 = R2D; prj->w[0] = SQRT2*prj->r0; prj->w[1] = prj->w[0]/90.0; prj->w[2] = 1.0/prj->w[0]; prj->w[3] = 90.0/prj->r0; prj->w[4] = 2.0/PI; prj->astPRJfwd = astMOLfwd; prj->astPRJrev = astMOLrev; return 0; } /*--------------------------------------------------------------------------*/ int astMOLfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { int j; double gamma, resid, u, v, v0, v1; const double tol = 1.0e-13; if (prj->flag != WCS__MOL) { if (astMOLset(prj)) return 1; } if (fabs(theta) == 90.0) { *x = 0.0; *y = copysign(prj->w[0],theta); } else if (theta == 0.0) { *x = prj->w[1]*phi; *y = 0.0; } else { u = PI*astSind(theta); v0 = -PI; v1 = PI; v = u; for (j = 0; j < 100; j++) { resid = (v - u) + sin(v); if (resid < 0.0) { if (resid > -tol) break; v0 = v; } else { if (resid < tol) break; v1 = v; } v = (v0 + v1)/2.0; } gamma = v/2.0; *x = prj->w[1]*phi*cos(gamma); *y = prj->w[0]*sin(gamma); } return 0; } /*--------------------------------------------------------------------------*/ int astMOLrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double s, y0, z; const double tol = 1.0e-12; if (prj->flag != WCS__MOL) { if (astMOLset(prj)) return 1; } y0 = y/prj->r0; s = 2.0 - y0*y0; if (s <= tol) { if (s < -tol) { return 2; } s = 0.0; if (fabs(x) > tol) { return 2; } *phi = 0.0; } else { s = sqrt(s); *phi = prj->w[3]*x/s; } z = y*prj->w[2]; if (fabs(z) > 1.0) { if (fabs(z) > 1.0+tol) { return 2; } z = copysign(1.0,z) + y0*s/PI; } else { z = asin(z)*prj->w[4] + y0*s/PI; } if (fabs(z) > 1.0) { if (fabs(z) > 1.0+tol) { return 2; } z = copysign(1.0,z); } *theta = astASind(z); return 0; } /*============================================================================ * AIT: Hammer-Aitoff projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "AIT" * prj->flag AIT * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] 2*r0**2 * prj->w[1] 1/(2*r0)**2 * prj->w[2] 1/(4*r0)**2 * prj->w[3] 1/(2*r0) * prj->astPRJfwd Pointer to astAITfwd(). * prj->astPRJrev Pointer to astAITrev(). *===========================================================================*/ int astAITset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "AIT"); prj->flag = WCS__AIT; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) prj->r0 = R2D; prj->w[0] = 2.0*prj->r0*prj->r0; prj->w[1] = 1.0/(2.0*prj->w[0]); prj->w[2] = prj->w[1]/4.0; prj->w[3] = 1.0/(2.0*prj->r0); prj->astPRJfwd = astAITfwd; prj->astPRJrev = astAITrev; return 0; } /*--------------------------------------------------------------------------*/ int astAITfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double cthe, w; if (prj->flag != WCS__AIT) { if (astAITset(prj)) return 1; } cthe = astCosd(theta); w = sqrt(prj->w[0]/(1.0 + cthe*astCosd(phi/2.0))); *x = 2.0*w*cthe*astSind(phi/2.0); *y = w*astSind(theta); return 0; } /*--------------------------------------------------------------------------*/ int astAITrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double s, u, xp, yp, z; const double tol = 1.0e-13; if (prj->flag != WCS__AIT) { if (astAITset(prj)) return 1; } u = 1.0 - x*x*prj->w[2] - y*y*prj->w[1]; if (u < 0.0) { if (u < -tol) { return 2; } u = 0.0; } z = sqrt(u); s = z*y/prj->r0; if (fabs(s) > 1.0) { if (fabs(s) > 1.0+tol) { return 2; } s = copysign(1.0,s); } xp = 2.0*z*z - 1.0; yp = z*x*prj->w[3]; if (xp == 0.0 && yp == 0.0) { *phi = 0.0; } else { *phi = 2.0*astATan2d(yp, xp); } *theta = astASind(s); return 0; } /*============================================================================ * COP: conic perspective projection. * * Given: * prj->p[1] sigma = (theta2+theta1)/2 * prj->p[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the * latitudes of the standard parallels, in degrees. * * Given and/or returned: * prj->flag COP, or -COP if prj->flag is given < 0. * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "COP" * prj->phi0 0.0 * prj->theta0 sigma * prj->w[0] C = sin(sigma) * prj->w[1] 1/C * prj->w[2] Y0 = r0*cos(delta)*cot(sigma) * prj->w[3] r0*cos(delta) * prj->w[4] 1/(r0*cos(delta) * prj->w[5] cot(sigma) * prj->astPRJfwd Pointer to astCOPfwd(). * prj->astPRJrev Pointer to astCOPrev(). *===========================================================================*/ int astCOPset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "COP"); prj->flag = icopysign(WCS__COP, prj->flag); prj->phi0 = 0.0; prj->theta0 = prj->p[1]; if (prj->r0 == 0.0) prj->r0 = R2D; prj->w[0] = astSind(prj->p[1]); if (prj->w[0] == 0.0) { return 1; } prj->w[1] = 1.0/prj->w[0]; prj->w[3] = prj->r0*astCosd(prj->p[2]); if (prj->w[3] == 0.0) { return 1; } prj->w[4] = 1.0/prj->w[3]; prj->w[5] = 1.0/astTand(prj->p[1]); prj->w[2] = prj->w[3]*prj->w[5]; prj->astPRJfwd = astCOPfwd; prj->astPRJrev = astCOPrev; return 0; } /*--------------------------------------------------------------------------*/ int astCOPfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double a, r, s, t; if (abs(prj->flag) != WCS__COP) { if (astCOPset(prj)) return 1; } t = theta - prj->p[1]; s = astCosd(t); if (s == 0.0) { return 2; } a = prj->w[0]*phi; r = prj->w[2] - prj->w[3]*astSind(t)/s; *x = r*astSind(a); *y = prj->w[2] - r*astCosd(a); if (prj->flag > 0 && r*prj->w[0] < 0.0) { return 2; } return 0; } /*--------------------------------------------------------------------------*/ int astCOPrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double a, dy, r; if (abs(prj->flag) != WCS__COP) { if (astCOPset(prj)) return 1; } dy = prj->w[2] - y; r = sqrt(x*x + dy*dy); if (prj->p[1] < 0.0) r = -r; if (r == 0.0) { a = 0.0; } else { a = astATan2d(x/r, dy/r); } *phi = a*prj->w[1]; *theta = prj->p[1] + astATand(prj->w[5] - r*prj->w[4]); return 0; } /*============================================================================ * COE: conic equal area projection. * * Given: * prj->p[1] sigma = (theta2+theta1)/2 * prj->p[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the * latitudes of the standard parallels, in degrees. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "COE" * prj->flag COE * prj->phi0 0.0 * prj->theta0 sigma * prj->w[0] C = (sin(theta1) + sin(theta2))/2 * prj->w[1] 1/C * prj->w[2] Y0 = chi*sqrt(psi - 2C*astSind(sigma)) * prj->w[3] chi = r0/C * prj->w[4] psi = 1 + sin(theta1)*sin(theta2) * prj->w[5] 2C * prj->w[6] (1 + sin(theta1)*sin(theta2))*(r0/C)**2 * prj->w[7] C/(2*r0**2) * prj->w[8] chi*sqrt(psi + 2C) * prj->astPRJfwd Pointer to astCOEfwd(). * prj->astPRJrev Pointer to astCOErev(). *===========================================================================*/ int astCOEset(prj) struct AstPrjPrm *prj; { double theta1, theta2; strcpy(prj->code, "COE"); prj->flag = WCS__COE; prj->phi0 = 0.0; prj->theta0 = prj->p[1]; if (prj->r0 == 0.0) prj->r0 = R2D; theta1 = prj->p[1] - prj->p[2]; theta2 = prj->p[1] + prj->p[2]; prj->w[0] = (astSind(theta1) + astSind(theta2))/2.0; if (prj->w[0] == 0.0) { return 1; } prj->w[1] = 1.0/prj->w[0]; prj->w[3] = prj->r0/prj->w[0]; prj->w[4] = 1.0 + astSind(theta1)*astSind(theta2); prj->w[5] = 2.0*prj->w[0]; prj->w[6] = prj->w[3]*prj->w[3]*prj->w[4]; prj->w[7] = 1.0/(2.0*prj->r0*prj->w[3]); prj->w[8] = prj->w[3]*sqrt(prj->w[4] + prj->w[5]); prj->w[2] = prj->w[3]*sqrt(prj->w[4] - prj->w[5]*astSind(prj->p[1])); prj->astPRJfwd = astCOEfwd; prj->astPRJrev = astCOErev; return 0; } /*--------------------------------------------------------------------------*/ int astCOEfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double a, r; if (prj->flag != WCS__COE) { if (astCOEset(prj)) return 1; } a = phi*prj->w[0]; if (theta == -90.0) { r = prj->w[8]; } else { r = prj->w[3]*sqrt(prj->w[4] - prj->w[5]*astSind(theta)); } *x = r*astSind(a); *y = prj->w[2] - r*astCosd(a); return 0; } /*--------------------------------------------------------------------------*/ int astCOErev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double a, dy, r, w; const double tol = 1.0e-12; if (prj->flag != WCS__COE) { if (astCOEset(prj)) return 1; } dy = prj->w[2] - y; r = sqrt(x*x + dy*dy); if (prj->p[1] < 0.0) r = -r; if (r == 0.0) { a = 0.0; } else { a = astATan2d(x/r, dy/r); } *phi = a*prj->w[1]; if (fabs(r - prj->w[8]) < tol) { *theta = -90.0; } else { w = (prj->w[6] - r*r)*prj->w[7]; if (fabs(w) > 1.0) { if (fabs(w-1.0) < tol) { *theta = 90.0; } else if (fabs(w+1.0) < tol) { *theta = -90.0; } else { return 2; } } else { *theta = astASind(w); } } return 0; } /*============================================================================ * COD: conic equidistant projection. * * Given: * prj->p[1] sigma = (theta2+theta1)/2 * prj->p[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the * latitudes of the standard parallels, in degrees. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "COD" * prj->flag COD * prj->phi0 0.0 * prj->theta0 sigma * prj->w[0] C = r0*sin(sigma)*sin(delta)/delta * prj->w[1] 1/C * prj->w[2] Y0 = delta*cot(delta)*cot(sigma) * prj->w[3] Y0 + sigma * prj->astPRJfwd Pointer to astCODfwd(). * prj->astPRJrev Pointer to astCODrev(). *===========================================================================*/ int astCODset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "COD"); prj->flag = WCS__COD; prj->phi0 = 0.0; prj->theta0 = prj->p[1]; if (prj->r0 == 0.0) prj->r0 = R2D; if (prj->p[2] == 0.0) { prj->w[0] = prj->r0*astSind(prj->p[1])*D2R; } else { prj->w[0] = prj->r0*astSind(prj->p[1])*astSind(prj->p[2])/prj->p[2]; } if (prj->w[0] == 0.0) { return 1; } prj->w[1] = 1.0/prj->w[0]; prj->w[2] = prj->r0*astCosd(prj->p[2])*astCosd(prj->p[1])/prj->w[0]; prj->w[3] = prj->w[2] + prj->p[1]; prj->astPRJfwd = astCODfwd; prj->astPRJrev = astCODrev; return 0; } /*--------------------------------------------------------------------------*/ int astCODfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double a, r; if (prj->flag != WCS__COD) { if (astCODset(prj)) return 1; } a = prj->w[0]*phi; r = prj->w[3] - theta; *x = r*astSind(a); *y = prj->w[2] - r*astCosd(a); return 0; } /*--------------------------------------------------------------------------*/ int astCODrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double a, dy, r; if (prj->flag != WCS__COD) { if (astCODset(prj)) return 1; } dy = prj->w[2] - y; r = sqrt(x*x + dy*dy); if (prj->p[1] < 0.0) r = -r; if (r == 0.0) { a = 0.0; } else { a = astATan2d(x/r, dy/r); } *phi = a*prj->w[1]; *theta = prj->w[3] - r; return 0; } /*============================================================================ * COO: conic orthomorphic projection. * * Given: * prj->p[1] sigma = (theta2+theta1)/2 * prj->p[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the * latitudes of the standard parallels, in degrees. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "COO" * prj->flag COO * prj->phi0 0.0 * prj->theta0 sigma * prj->w[0] C = ln(cos(theta2)/cos(theta1))/ln(tan(tau2)/tan(tau1)) * where tau1 = (90 - theta1)/2 * tau2 = (90 - theta2)/2 * prj->w[1] 1/C * prj->w[2] Y0 = psi*tan((90-sigma)/2)**C * prj->w[3] psi = (r0*cos(theta1)/C)/tan(tau1)**C * prj->w[4] 1/psi * prj->astPRJfwd Pointer to astCOOfwd(). * prj->astPRJrev Pointer to astCOOrev(). *===========================================================================*/ int astCOOset(prj) struct AstPrjPrm *prj; { double cos1, cos2, tan1, tan2, theta1, theta2; strcpy(prj->code, "COO"); prj->flag = WCS__COO; prj->phi0 = 0.0; prj->theta0 = prj->p[1]; if (prj->r0 == 0.0) prj->r0 = R2D; theta1 = prj->p[1] - prj->p[2]; theta2 = prj->p[1] + prj->p[2]; tan1 = astTand((90.0 - theta1)/2.0); cos1 = astCosd(theta1); if (theta1 == theta2) { prj->w[0] = astSind(theta1); } else { tan2 = astTand((90.0 - theta2)/2.0); cos2 = astCosd(theta2); prj->w[0] = log(cos2/cos1)/log(tan2/tan1); } if (prj->w[0] == 0.0) { return 1; } prj->w[1] = 1.0/prj->w[0]; prj->w[3] = prj->r0*(cos1/prj->w[0])/pow(tan1,prj->w[0]); if (prj->w[3] == 0.0) { return 1; } prj->w[2] = prj->w[3]*pow(astTand((90.0 - prj->p[1])/2.0),prj->w[0]); prj->w[4] = 1.0/prj->w[3]; prj->astPRJfwd = astCOOfwd; prj->astPRJrev = astCOOrev; return 0; } /*--------------------------------------------------------------------------*/ int astCOOfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double a, r; if (prj->flag != WCS__COO) { if (astCOOset(prj)) return 1; } a = prj->w[0]*phi; if (theta == -90.0) { if (prj->w[0] < 0.0) { r = 0.0; } else { return 2; } } else { r = prj->w[3]*pow(astTand((90.0 - theta)/2.0),prj->w[0]); } *x = r*astSind(a); *y = prj->w[2] - r*astCosd(a); return 0; } /*--------------------------------------------------------------------------*/ int astCOOrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double a, dy, r; if (prj->flag != WCS__COO) { if (astCOOset(prj)) return 1; } dy = prj->w[2] - y; r = sqrt(x*x + dy*dy); if (prj->p[1] < 0.0) r = -r; if (r == 0.0) { a = 0.0; } else { a = astATan2d(x/r, dy/r); } *phi = a*prj->w[1]; if (r == 0.0) { if (prj->w[0] < 0.0) { *theta = -90.0; } else { return 2; } } else { *theta = 90.0 - 2.0*astATand(pow(r*prj->w[4],prj->w[1])); } return 0; } /*============================================================================ * BON: Bonne's projection. * * Given: * prj->p[1] Bonne conformal latitude, theta1, in degrees. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "BON" * prj->flag BON * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[1] r0*pi/180 * prj->w[2] Y0 = r0*(cot(theta1) + theta1*pi/180) * prj->astPRJfwd Pointer to astBONfwd(). * prj->astPRJrev Pointer to astBONrev(). *===========================================================================*/ int astBONset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "BON"); prj->flag = WCS__BON; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[1] = 1.0; prj->w[2] = prj->r0*astCosd(prj->p[1])/astSind(prj->p[1]) + prj->p[1]; } else { prj->w[1] = prj->r0*D2R; prj->w[2] = prj->r0*(astCosd(prj->p[1])/astSind(prj->p[1]) + prj->p[1]*D2R); } prj->astPRJfwd = astBONfwd; prj->astPRJrev = astBONrev; return 0; } /*--------------------------------------------------------------------------*/ int astBONfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double a, r; if (prj->p[1] == 0.0) { /* Sanson-Flamsteed. */ return astSFLfwd(phi, theta, prj, x, y); } if (prj->flag != WCS__BON) { if (astBONset(prj)) return 1; } r = prj->w[2] - theta*prj->w[1]; a = prj->r0*phi*astCosd(theta)/r; *x = r*astSind(a); *y = prj->w[2] - r*astCosd(a); return 0; } /*--------------------------------------------------------------------------*/ int astBONrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double a, cthe, dy, r; if (prj->p[1] == 0.0) { /* Sanson-Flamsteed. */ return astSFLrev(x, y, prj, phi, theta); } if (prj->flag != WCS__BON) { if (astBONset(prj)) return 1; } dy = prj->w[2] - y; r = sqrt(x*x + dy*dy); if (prj->p[1] < 0.0) r = -r; if (r == 0.0) { a = 0.0; } else { a = astATan2d(x/r, dy/r); } *theta = (prj->w[2] - r)/prj->w[1]; cthe = astCosd(*theta); if (cthe == 0.0) { *phi = 0.0; } else { *phi = a*(r/prj->r0)/cthe; } return 0; } /*============================================================================ * PCO: polyconic projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "PCO" * prj->flag PCO * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*(pi/180) * prj->w[1] 1/r0 * prj->w[2] 2*r0 * prj->astPRJfwd Pointer to astPCOfwd(). * prj->astPRJrev Pointer to astPCOrev(). *===========================================================================*/ int astPCOset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "PCO"); prj->flag = WCS__PCO; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 1.0; prj->w[1] = 1.0; prj->w[2] = 360.0/PI; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = 1.0/prj->w[0]; prj->w[2] = 2.0*prj->r0; } prj->astPRJfwd = astPCOfwd; prj->astPRJrev = astPCOrev; return 0; } /*--------------------------------------------------------------------------*/ int astPCOfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double a, cthe, cotthe, sthe; if (prj->flag != WCS__PCO) { if (astPCOset(prj)) return 1; } cthe = astCosd(theta); sthe = astSind(theta); a = phi*sthe; if (sthe == 0.0) { *x = prj->w[0]*phi; *y = 0.0; } else { cotthe = cthe/sthe; *x = prj->r0*cotthe*astSind(a); *y = prj->r0*(cotthe*(1.0 - astCosd(a)) + theta*D2R); } return 0; } /*--------------------------------------------------------------------------*/ int astPCOrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { int j; double f, fneg, fpos, lambda, tanthe, theneg, thepos, w, xp, xx, ymthe, yp; const double tol = 1.0e-12; if (prj->flag != WCS__PCO) { if (astPCOset(prj)) return 1; } w = fabs(y*prj->w[1]); if (w < tol) { *phi = x*prj->w[1]; *theta = 0.0; } else if (fabs(w-90.0) < tol) { *phi = 0.0; *theta = copysign(90.0,y); } else { /* Iterative solution using weighted division of the interval. */ if (y > 0.0) { thepos = 90.0; } else { thepos = -90.0; } theneg = 0.0; xx = x*x; ymthe = y - prj->w[0]*thepos; fpos = xx + ymthe*ymthe; fneg = -999.0; for (j = 0; j < 64; j++) { if (fneg < -100.0) { /* Equal division of the interval. */ *theta = (thepos+theneg)/2.0; } else { /* Weighted division of the interval. */ lambda = fpos/(fpos-fneg); if (lambda < 0.1) { lambda = 0.1; } else if (lambda > 0.9) { lambda = 0.9; } *theta = thepos - lambda*(thepos-theneg); } /* Compute the residue. */ ymthe = y - prj->w[0]*(*theta); tanthe = astTand(*theta); f = xx + ymthe*(ymthe - prj->w[2]/tanthe); /* Check for convergence. */ if (fabs(f) < tol) break; if (fabs(thepos-theneg) < tol) break; /* Redefine the interval. */ if (f > 0.0) { thepos = *theta; fpos = f; } else { theneg = *theta; fneg = f; } } xp = prj->r0 - ymthe*tanthe; yp = x*tanthe; if (xp == 0.0 && yp == 0.0) { *phi = 0.0; } else { *phi = astATan2d(yp, xp)/astSind(*theta); } } return 0; } /*============================================================================ * TSC: tangential spherical cube projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "TSC" * prj->flag TSC * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*(pi/4) * prj->w[1] (4/pi)/r0 * prj->astPRJfwd Pointer to astTSCfwd(). * prj->astPRJrev Pointer to astTSCrev(). *===========================================================================*/ int astTSCset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "TSC"); prj->flag = WCS__TSC; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 45.0; prj->w[1] = 1.0/45.0; } else { prj->w[0] = prj->r0*PI/4.0; prj->w[1] = 1.0/prj->w[0]; } prj->astPRJfwd = astTSCfwd; prj->astPRJrev = astTSCrev; return 0; } /*--------------------------------------------------------------------------*/ int astTSCfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { int face; double cthe, l, m, n, rho, x0, xf, y0, yf; const double tol = 1.0e-12; x0 = 0.0; xf = 0.0; y0 = 0.0; yf = 0.0; if (prj->flag != WCS__TSC) { if (astTSCset(prj)) return 1; } cthe = astCosd(theta); l = cthe*astCosd(phi); m = cthe*astSind(phi); n = astSind(theta); face = 0; rho = n; if (l > rho) { face = 1; rho = l; } if (m > rho) { face = 2; rho = m; } if (-l > rho) { face = 3; rho = -l; } if (-m > rho) { face = 4; rho = -m; } if (-n > rho) { face = 5; rho = -n; } if (face == 0) { xf = m/rho; yf = -l/rho; x0 = 0.0; y0 = 2.0; } else if (face == 1) { xf = m/rho; yf = n/rho; x0 = 0.0; y0 = 0.0; } else if (face == 2) { xf = -l/rho; yf = n/rho; x0 = 2.0; y0 = 0.0; } else if (face == 3) { xf = -m/rho; yf = n/rho; x0 = 4.0; y0 = 0.0; } else if (face == 4) { xf = l/rho; yf = n/rho; x0 = 6.0; y0 = 0.0; } else if (face == 5) { xf = m/rho; yf = l/rho; x0 = 0.0; y0 = -2.0; } if (fabs(xf) > 1.0) { if (fabs(xf) > 1.0+tol) { return 2; } xf = copysign(1.0,xf); } if (fabs(yf) > 1.0) { if (fabs(yf) > 1.0+tol) { return 2; } yf = copysign(1.0,yf); } *x = prj->w[0]*(xf + x0); *y = prj->w[0]*(yf + y0); return 0; } /*--------------------------------------------------------------------------*/ int astTSCrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double l, m, n, xf, yf; if (prj->flag != WCS__TSC) { if (astTSCset(prj)) return 1; } xf = x*prj->w[1]; yf = y*prj->w[1]; /* Check bounds. */ if (fabs(xf) <= 1.0) { if (fabs(yf) > 3.0) return 2; } else { if (fabs(xf) > 7.0) return 2; if (fabs(yf) > 1.0) return 2; } /* Map negative faces to the other side. */ if (xf < -1.0) xf += 8.0; /* Determine the face. */ if (xf > 5.0) { /* face = 4 */ xf = xf - 6.0; m = -1.0/sqrt(1.0 + xf*xf + yf*yf); l = -m*xf; n = -m*yf; } else if (xf > 3.0) { /* face = 3 */ xf = xf - 4.0; l = -1.0/sqrt(1.0 + xf*xf + yf*yf); m = l*xf; n = -l*yf; } else if (xf > 1.0) { /* face = 2 */ xf = xf - 2.0; m = 1.0/sqrt(1.0 + xf*xf + yf*yf); l = -m*xf; n = m*yf; } else if (yf > 1.0) { /* face = 0 */ yf = yf - 2.0; n = 1.0/sqrt(1.0 + xf*xf + yf*yf); l = -n*yf; m = n*xf; } else if (yf < -1.0) { /* face = 5 */ yf = yf + 2.0; n = -1.0/sqrt(1.0 + xf*xf + yf*yf); l = -n*yf; m = -n*xf; } else { /* face = 1 */ l = 1.0/sqrt(1.0 + xf*xf + yf*yf); m = l*xf; n = l*yf; } if (l == 0.0 && m == 0.0) { *phi = 0.0; } else { *phi = astATan2d(m, l); } *theta = astASind(n); return 0; } /*============================================================================ * CSC: COBE quadrilateralized spherical cube projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "CSC" * prj->flag CSC * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*(pi/4) * prj->w[1] (4/pi)/r0 * prj->astPRJfwd Pointer to astCSCfwd(). * prj->astPRJrev Pointer to astCSCrev(). *===========================================================================*/ int astCSCset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "CSC"); prj->flag = WCS__CSC; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 45.0; prj->w[1] = 1.0/45.0; } else { prj->w[0] = prj->r0*PI/4.0; prj->w[1] = 1.0/prj->w[0]; } prj->astPRJfwd = astCSCfwd; prj->astPRJrev = astCSCrev; return 0; } /*--------------------------------------------------------------------------*/ int astCSCfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { int face; float cthe, eta, l, m, n, rho, xi; const float tol = 1.0e-7; float a, a2, a2b2, a4, ab, b, b2, b4, ca2, cb2, x0, xf, y0, yf; const float gstar = 1.37484847732; const float mm = 0.004869491981; const float gamma = -0.13161671474; const float omega1 = -0.159596235474; const float d0 = 0.0759196200467; const float d1 = -0.0217762490699; const float c00 = 0.141189631152; const float c10 = 0.0809701286525; const float c01 = -0.281528535557; const float c11 = 0.15384112876; const float c20 = -0.178251207466; const float c02 = 0.106959469314; eta = 0.0; xi = 0.0; x0 = 0.0; y0 = 0.0; if (prj->flag != WCS__CSC) { if (astCSCset(prj)) return 1; } cthe = astCosd(theta); l = cthe*astCosd(phi); m = cthe*astSind(phi); n = astSind(theta); face = 0; rho = n; if (l > rho) { face = 1; rho = l; } if (m > rho) { face = 2; rho = m; } if (-l > rho) { face = 3; rho = -l; } if (-m > rho) { face = 4; rho = -m; } if (-n > rho) { face = 5; rho = -n; } if (face == 0) { xi = m; eta = -l; x0 = 0.0; y0 = 2.0; } else if (face == 1) { xi = m; eta = n; x0 = 0.0; y0 = 0.0; } else if (face == 2) { xi = -l; eta = n; x0 = 2.0; y0 = 0.0; } else if (face == 3) { xi = -m; eta = n; x0 = 4.0; y0 = 0.0; } else if (face == 4) { xi = l; eta = n; x0 = 6.0; y0 = 0.0; } else if (face == 5) { xi = m; eta = l; x0 = 0.0; y0 = -2.0; } a = xi/rho; b = eta/rho; a2 = a*a; b2 = b*b; ca2 = 1.0 - a2; cb2 = 1.0 - b2; /* Avoid floating underflows. */ ab = fabs(a*b); a4 = (a2 > 1.0e-16) ? a2*a2 : 0.0; b4 = (b2 > 1.0e-16) ? b2*b2 : 0.0; a2b2 = (ab > 1.0e-16) ? a2*b2 : 0.0; xf = a*(a2 + ca2*(gstar + b2*(gamma*ca2 + mm*a2 + cb2*(c00 + c10*a2 + c01*b2 + c11*a2b2 + c20*a4 + c02*b4)) + a2*(omega1 - ca2*(d0 + d1*a2)))); yf = b*(b2 + cb2*(gstar + a2*(gamma*cb2 + mm*b2 + ca2*(c00 + c10*b2 + c01*a2 + c11*a2b2 + c20*b4 + c02*a4)) + b2*(omega1 - cb2*(d0 + d1*b2)))); if (fabs(xf) > 1.0) { if (fabs(xf) > 1.0+tol) { return 2; } xf = copysign(1.0,xf); } if (fabs(yf) > 1.0) { if (fabs(yf) > 1.0+tol) { return 2; } yf = copysign(1.0,yf); } *x = prj->w[0]*(x0 + xf); *y = prj->w[0]*(y0 + yf); return 0; } /*--------------------------------------------------------------------------*/ int astCSCrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { int face; float l, m, n; float a, b, xf, xx, yf, yy, z0, z1, z2, z3, z4, z5, z6; const float p00 = -0.27292696; const float p10 = -0.07629969; const float p20 = -0.22797056; const float p30 = 0.54852384; const float p40 = -0.62930065; const float p50 = 0.25795794; const float p60 = 0.02584375; const float p01 = -0.02819452; const float p11 = -0.01471565; const float p21 = 0.48051509; const float p31 = -1.74114454; const float p41 = 1.71547508; const float p51 = -0.53022337; const float p02 = 0.27058160; const float p12 = -0.56800938; const float p22 = 0.30803317; const float p32 = 0.98938102; const float p42 = -0.83180469; const float p03 = -0.60441560; const float p13 = 1.50880086; const float p23 = -0.93678576; const float p33 = 0.08693841; const float p04 = 0.93412077; const float p14 = -1.41601920; const float p24 = 0.33887446; const float p05 = -0.63915306; const float p15 = 0.52032238; const float p06 = 0.14381585; l = 0.0; m = 0.0; n = 0.0; if (prj->flag != WCS__CSC) { if (astCSCset(prj)) return 1; } xf = x*prj->w[1]; yf = y*prj->w[1]; /* Check bounds. */ if (fabs(xf) <= 1.0) { if (fabs(yf) > 3.0) return 2; } else { if (fabs(xf) > 7.0) return 2; if (fabs(yf) > 1.0) return 2; } /* Map negative faces to the other side. */ if (xf < -1.0) xf += 8.0; /* Determine the face. */ if (xf > 5.0) { face = 4; xf = xf - 6.0; } else if (xf > 3.0) { face = 3; xf = xf - 4.0; } else if (xf > 1.0) { face = 2; xf = xf - 2.0; } else if (yf > 1.0) { face = 0; yf = yf - 2.0; } else if (yf < -1.0) { face = 5; yf = yf + 2.0; } else { face = 1; } xx = xf*xf; yy = yf*yf; z0 = p00 + xx*(p10 + xx*(p20 + xx*(p30 + xx*(p40 + xx*(p50 + xx*(p60)))))); z1 = p01 + xx*(p11 + xx*(p21 + xx*(p31 + xx*(p41 + xx*(p51))))); z2 = p02 + xx*(p12 + xx*(p22 + xx*(p32 + xx*(p42)))); z3 = p03 + xx*(p13 + xx*(p23 + xx*(p33))); z4 = p04 + xx*(p14 + xx*(p24)); z5 = p05 + xx*(p15); z6 = p06; a = z0 + yy*(z1 + yy*(z2 + yy*(z3 + yy*(z4 + yy*(z5 + yy*z6))))); a = xf + xf*(1.0 - xx)*a; z0 = p00 + yy*(p10 + yy*(p20 + yy*(p30 + yy*(p40 + yy*(p50 + yy*(p60)))))); z1 = p01 + yy*(p11 + yy*(p21 + yy*(p31 + yy*(p41 + yy*(p51))))); z2 = p02 + yy*(p12 + yy*(p22 + yy*(p32 + yy*(p42)))); z3 = p03 + yy*(p13 + yy*(p23 + yy*(p33))); z4 = p04 + yy*(p14 + yy*(p24)); z5 = p05 + yy*(p15); z6 = p06; b = z0 + xx*(z1 + xx*(z2 + xx*(z3 + xx*(z4 + xx*(z5 + xx*z6))))); b = yf + yf*(1.0 - yy)*b; if (face == 0) { n = 1.0/sqrt(a*a + b*b + 1.0); l = -b*n; m = a*n; } else if (face == 1) { l = 1.0/sqrt(a*a + b*b + 1.0); m = a*l; n = b*l; } else if (face == 2) { m = 1.0/sqrt(a*a + b*b + 1.0); l = -a*m; n = b*m; } else if (face == 3) { l = -1.0/sqrt(a*a + b*b + 1.0); m = a*l; n = -b*l; } else if (face == 4) { m = -1.0/sqrt(a*a + b*b + 1.0); l = -a*m; n = -b*m; } else if (face == 5) { n = -1.0/sqrt(a*a + b*b + 1.0); l = -b*n; m = -a*n; } if (l == 0.0 && m == 0.0) { *phi = 0.0; } else { *phi = astATan2d(m, l); } *theta = astASind(n); return 0; } /*============================================================================ * QSC: quadrilaterilized spherical cube projection. * * Given and/or returned: * prj->r0 r0; reset to 180/pi if 0. * * Returned: * prj->code "QSC" * prj->flag QSC * prj->phi0 0.0 * prj->theta0 0.0 * prj->w[0] r0*(pi/4) * prj->w[1] (4/pi)/r0 * prj->astPRJfwd Pointer to astQSCfwd(). * prj->astPRJrev Pointer to astQSCrev(). *===========================================================================*/ int astQSCset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "QSC"); prj->flag = WCS__QSC; prj->phi0 = 0.0; prj->theta0 = 0.0; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 45.0; prj->w[1] = 1.0/45.0; } else { prj->w[0] = prj->r0*PI/4.0; prj->w[1] = 1.0/prj->w[0]; } prj->astPRJfwd = astQSCfwd; prj->astPRJrev = astQSCrev; return 0; } /*--------------------------------------------------------------------------*/ int astQSCfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { int face; double cthe, eta, l, m, n, omega, p, rho, rhu, t, tau, x0, xf, xi, y0, yf; const double tol = 1.0e-12; eta = 0.0; x0 = 0.0; xf = 0.0; xi = 0.0; y0 = 0.0; yf = 0.0; if (prj->flag != WCS__QSC) { if (astQSCset(prj)) return 1; } if (fabs(theta) == 90.0) { *x = 0.0; *y = copysign(2.0*prj->w[0],theta); return 0; } cthe = astCosd(theta); l = cthe*astCosd(phi); m = cthe*astSind(phi); n = astSind(theta); face = 0; rho = n; if (l > rho) { face = 1; rho = l; } if (m > rho) { face = 2; rho = m; } if (-l > rho) { face = 3; rho = -l; } if (-m > rho) { face = 4; rho = -m; } if (-n > rho) { face = 5; rho = -n; } rhu = 1.0 - rho; if (face == 0) { xi = m; eta = -l; if (rhu < 1.0e-8) { /* Small angle formula. */ t = (90.0 - theta)*D2R; rhu = t*t/2.0; } x0 = 0.0; y0 = 2.0; } else if (face == 1) { xi = m; eta = n; if (rhu < 1.0e-8) { /* Small angle formula. */ t = theta*D2R; p = fmod(phi,360.0); if (p < -180.0) p += 360.0; if (p > 180.0) p -= 360.0; p *= D2R; rhu = (p*p + t*t)/2.0; } x0 = 0.0; y0 = 0.0; } else if (face == 2) { xi = -l; eta = n; if (rhu < 1.0e-8) { /* Small angle formula. */ t = theta*D2R; p = fmod(phi,360.0); if (p < -180.0) p += 360.0; p = (90.0 - p)*D2R; rhu = (p*p + t*t)/2.0; } x0 = 2.0; y0 = 0.0; } else if (face == 3) { xi = -m; eta = n; if (rhu < 1.0e-8) { /* Small angle formula. */ t = theta*D2R; p = fmod(phi,360.0); if (p < 0.0) p += 360.0; p = (180.0 - p)*D2R; rhu = (p*p + t*t)/2.0; } x0 = 4.0; y0 = 0.0; } else if (face == 4) { xi = l; eta = n; if (rhu < 1.0e-8) { /* Small angle formula. */ t = theta*D2R; p = fmod(phi,360.0); if (p > 180.0) p -= 360.0; p *= (90.0 + p)*D2R; rhu = (p*p + t*t)/2.0; } x0 = 6; y0 = 0.0; } else if (face == 5) { xi = m; eta = l; if (rhu < 1.0e-8) { /* Small angle formula. */ t = (90.0 + theta)*D2R; rhu = t*t/2.0; } x0 = 0.0; y0 = -2; } if (xi == 0.0 && eta == 0.0) { xf = 0.0; yf = 0.0; } else if (-xi >= fabs(eta)) { omega = eta/xi; tau = 1.0 + omega*omega; xf = -sqrt(rhu/(1.0-1.0/sqrt(1.0+tau))); yf = (xf/15.0)*(astATand(omega) - astASind(omega/sqrt(tau+tau))); } else if (xi >= fabs(eta)) { omega = eta/xi; tau = 1.0 + omega*omega; xf = sqrt(rhu/(1.0-1.0/sqrt(1.0+tau))); yf = (xf/15.0)*(astATand(omega) - astASind(omega/sqrt(tau+tau))); } else if (-eta > fabs(xi)) { omega = xi/eta; tau = 1.0 + omega*omega; yf = -sqrt(rhu/(1.0-1.0/sqrt(1.0+tau))); xf = (yf/15.0)*(astATand(omega) - astASind(omega/sqrt(tau+tau))); } else if (eta > fabs(xi)) { omega = xi/eta; tau = 1.0 + omega*omega; yf = sqrt(rhu/(1.0-1.0/sqrt(1.0+tau))); xf = (yf/15.0)*(astATand(omega) - astASind(omega/sqrt(tau+tau))); } if (fabs(xf) > 1.0) { if (fabs(xf) > 1.0+tol) { return 2; } xf = copysign(1.0,xf); } if (fabs(yf) > 1.0) { if (fabs(yf) > 1.0+tol) { return 2; } yf = copysign(1.0,yf); } *x = prj->w[0]*(xf + x0); *y = prj->w[0]*(yf + y0); return 0; } /*--------------------------------------------------------------------------*/ int astQSCrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { int direct, face; double l, m, n, omega, rho, rhu, tau, xf, yf, w; const double tol = 1.0e-12; l = 0.0; m = 0.0; n = 0.0; if (prj->flag != WCS__QSC) { if (astQSCset(prj)) return 1; } xf = x*prj->w[1]; yf = y*prj->w[1]; /* Check bounds. */ if (fabs(xf) <= 1.0) { if (fabs(yf) > 3.0) return 2; } else { if (fabs(xf) > 7.0) return 2; if (fabs(yf) > 1.0) return 2; } /* Map negative faces to the other side. */ if (xf < -1.0) xf += 8.0; /* Determine the face. */ if (xf > 5.0) { face = 4; xf = xf - 6.0; } else if (xf > 3.0) { face = 3; xf = xf - 4.0; } else if (xf > 1.0) { face = 2; xf = xf - 2.0; } else if (yf > 1.0) { face = 0; yf = yf - 2.0; } else if (yf < -1.0) { face = 5; yf = yf + 2.0; } else { face = 1; } direct = (fabs(xf) > fabs(yf)); if (direct) { if (xf == 0.0) { omega = 0.0; tau = 1.0; rho = 1.0; rhu = 0.0; } else { w = 15.0*yf/xf; omega = astSind(w)/(astCosd(w) - SQRT2INV); tau = 1.0 + omega*omega; rhu = xf*xf*(1.0 - 1.0/sqrt(1.0 + tau)); rho = 1.0 - rhu; } } else { if (yf == 0.0) { omega = 0.0; tau = 1.0; rho = 1.0; rhu = 0.0; } else { w = 15.0*xf/yf; omega = astSind(w)/(astCosd(w) - SQRT2INV); tau = 1.0 + omega*omega; rhu = yf*yf*(1.0 - 1.0/sqrt(1.0 + tau)); rho = 1.0 - rhu; } } if (rho < -1.0) { if (rho < -1.0-tol) { return 2; } rho = -1.0; rhu = 2.0; w = 0.0; } else { w = sqrt(rhu*(2.0-rhu)/tau); } if (face == 0) { n = rho; if (direct) { m = w; if (xf < 0.0) m = -m; l = -m*omega; } else { l = w; if (yf > 0.0) l = -l; m = -l*omega; } } else if (face == 1) { l = rho; if (direct) { m = w; if (xf < 0.0) m = -m; n = m*omega; } else { n = w; if (yf < 0.0) n = -n; m = n*omega; } } else if (face == 2) { m = rho; if (direct) { l = w; if (xf > 0.0) l = -l; n = -l*omega; } else { n = w; if (yf < 0.0) n = -n; l = -n*omega; } } else if (face == 3) { l = -rho; if (direct) { m = w; if (xf > 0.0) m = -m; n = -m*omega; } else { n = w; if (yf < 0.0) n = -n; m = -n*omega; } } else if (face == 4) { m = -rho; if (direct) { l = w; if (xf < 0.0) l = -l; n = l*omega; } else { n = w; if (yf < 0.0) n = -n; l = n*omega; } } else if (face == 5) { n = -rho; if (direct) { m = w; if (xf < 0.0) m = -m; l = m*omega; } else { l = w; if (yf < 0.0) l = -l; m = l*omega; } } if (l == 0.0 && m == 0.0) { *phi = 0.0; } else { *phi = astATan2d(m, l); } *theta = astASind(n); return 0; } /*============================================================================ * HPX: HEALPix projection. * * Given: * prj->p[1] H - the number of facets in longitude. * prj->p[2] K - the number of facets in latitude * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to 0.0 * prj->theta0 Reset to 0.0 * * Returned: * prj->flag HPX * prj->code "HPX" * prj->n True if K is odd. * prj->w[0] r0*(pi/180) * prj->w[1] (180/pi)/r0 * prj->w[2] (K-1)/K * prj->w[3] 90*K/H * prj->w[4] (K+1)/2 * prj->w[5] 90*(K-1)/H * prj->w[6] 180/H * prj->w[7] H/360 * prj->w[8] (90*K/H)*r0*(pi/180) * prj->w[9] (180/H)*r0*(pi/180) * prj->astPRJfwd Pointer to astHPXfwd(). * prj->astPRJrev Pointer to astHPXrev(). *===========================================================================*/ int astHPXset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "HPX"); prj->flag = WCS__HPX; prj->phi0 = 0.0; prj->theta0 = 0.0; prj->n = ((int)prj->p[2])%2; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 1.0; prj->w[1] = 1.0; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = R2D/prj->r0; } prj->w[2] = (prj->p[2] - 1.0) / prj->p[2]; prj->w[3] = 90.0 * prj->p[2] / prj->p[1]; prj->w[4] = (prj->p[2] + 1.0) / 2.0; prj->w[5] = 90.0 * (prj->p[2] - 1.0) / prj->p[1]; prj->w[6] = 180.0 / prj->p[1]; prj->w[7] = prj->p[1] / 360.0; prj->w[8] = prj->w[3] * prj->w[0]; prj->w[9] = prj->w[6] * prj->w[0]; prj->astPRJfwd = astHPXfwd; prj->astPRJrev = astHPXrev; return 0; } /*--------------------------------------------------------------------------*/ int astHPXfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double abssin, sigma, sinthe, phic; int hodd; if( prj->flag != WCS__HPX ) { if( astHPXset( prj ) ) return 1; } sinthe = astSind( theta ); abssin = fabs( sinthe ); /* Equatorial zone */ if( abssin <= prj->w[2] ) { *x = prj->w[0] * phi; *y = prj->w[8] * sinthe; /* Polar zone */ } else { /* DSB - The expression for phic is conditioned differently to the WCSLIB code in order to improve accuracy of the floor function for arguments very slightly below an integer value. */ hodd = ((int)prj->p[1]) % 2; if( !prj->n && theta <= 0.0 ) hodd = 1 - hodd; if( hodd ) { phic = -180.0 + (2.0*floor( prj->w[7] * phi + 1/2 ) + prj->p[1] ) * prj->w[6]; } else { phic = -180.0 + (2.0*floor( prj->w[7] * phi ) + prj->p[1] + 1 ) * prj->w[6]; } sigma = sqrt( prj->p[2]*( 1.0 - abssin )); *x = prj->w[0] *( phic + ( phi - phic )*sigma ); *y = prj->w[9] * ( prj->w[4] - sigma ); if( theta < 0 ) *y = -*y; } return 0; } /*--------------------------------------------------------------------------*/ int astHPXrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double absy, sigma, t, yr, xc; int hodd; if (prj->flag != WCS__HPX) { if (astHPXset(prj)) return 1; } yr = prj->w[1]*y; absy = fabs( yr ); /* Equatorial zone */ if( absy <= prj->w[5] ) { *phi = prj->w[1] * x; t = yr/prj->w[3]; if( t < -1.0 || t > 1.0 ) { return 2; } else { *theta = astASind( t ); } /* Polar zone */ } else if( absy <= 90 ){ /* DSB - The expression for xc is conditioned differently to the WCSLIB code in order to improve accuracy of the floor function for arguments very slightly below an integer value. */ hodd = ((int)prj->p[1]) % 2; if( !prj->n && yr <= 0.0 ) hodd = 1 - hodd; if( hodd ) { xc = -180.0 + (2.0*floor( prj->w[7] * x + 1/2 ) + prj->p[1] ) * prj->w[6]; } else { xc = -180.0 + (2.0*floor( prj->w[7] * x ) + prj->p[1] + 1 ) * prj->w[6]; } sigma = prj->w[4] - absy / prj->w[6]; if( sigma == 0.0 ) { return 2; } else { t = ( x - xc )/sigma; if( fabs( t ) <= prj->w[6] ) { *phi = prj->w[1] *( xc + t ); } else { return 2; } } t = 1.0 - sigma*sigma/prj->p[2]; if( t < -1.0 || t > 1.0 ) { return 2; } else { *theta = astASind( t ); if( y < 0 ) *theta = -*theta; } } else { return 2; } return 0; } /*============================================================================ * XPH: HEALPix polar, aka "butterfly" projection. * * Given and/or returned: * prj->r0 Reset to 180/pi if 0. * prj->phi0 Reset to 0.0 if undefined. * prj->theta0 Reset to 0.0 if undefined. * * Returned: * prj->flag XPH * prj->code "XPH" * prj->w[0] r0*(pi/180)/sqrt(2) * prj->w[1] (180/pi)/r0/sqrt(2) * prj->w[2] 2/3 * prj->w[3] tol (= 1e-4) * prj->w[4] sqrt(2/3)*(180/pi) * prj->w[5] 90 - tol*sqrt(2/3)*(180/pi) * prj->w[6] sqrt(3/2)*(pi/180) * prj->astPRJfwd Pointer to astXPHfwd(). * prj->astPRJrev Pointer to astXPHrev(). *===========================================================================*/ int astXPHset(prj) struct AstPrjPrm *prj; { strcpy(prj->code, "XPH"); prj->flag = WCS__XPH; if (prj->r0 == 0.0) { prj->r0 = R2D; prj->w[0] = 1.0; prj->w[1] = 1.0; } else { prj->w[0] = prj->r0*D2R; prj->w[1] = R2D/prj->r0; } prj->w[0] /= sqrt(2.0); prj->w[1] /= sqrt(2.0); prj->w[2] = 2.0/3.0; prj->w[3] = 1e-4; prj->w[4] = sqrt(prj->w[2])*R2D; prj->w[5] = 90.0 - prj->w[3]*prj->w[4]; prj->w[6] = sqrt(1.5)*D2R; prj->astPRJfwd = astXPHfwd; prj->astPRJrev = astXPHrev; return 0; } /*--------------------------------------------------------------------------*/ int astXPHfwd(phi, theta, prj, x, y) const double phi, theta; struct AstPrjPrm *prj; double *x, *y; { double abssin, chi, eta, psi, sigma, sinthe, xi; if (prj->flag != WCS__XPH) { if (astXPHset(prj)) return 1; } /* Do phi dependence. */ chi = phi; if (180.0 <= fabs(chi)) { chi = fmod(chi, 360.0); if (chi < -180.0) { chi += 360.0; } else if (180.0 <= chi) { chi -= 360.0; } } /* phi is also recomputed from chi to avoid rounding problems. */ chi += 180.0; psi = fmod(chi, 90.0); /* y is used to hold phi (rounded). */ *x = psi; *y = chi - 180.0; /* Do theta dependence. */ sinthe = astSind(theta); abssin = fabs(sinthe); if (abssin <= prj->w[2]) { /* Equatorial regime. */ xi = *x; eta = 67.5 * sinthe; } else { /* Polar regime. */ if (theta < prj->w[5]) { sigma = sqrt(3.0*(1.0 - abssin)); } else { sigma = (90.0 - theta)*prj->w[6]; } xi = 45.0 + (*x - 45.0)*sigma; eta = 45.0 * (2.0 - sigma); if (theta < 0.0) eta = -eta; } xi -= 45.0; eta -= 90.0; /* Recall that y holds phi. */ if (*y < -90.0) { *x = prj->w[0]*(-xi + eta); *y = prj->w[0]*(-xi - eta); } else if (*y < 0.0) { *x = prj->w[0]*(+xi + eta); *y = prj->w[0]*(-xi + eta); } else if (*y < 90.0) { *x = prj->w[0]*( xi - eta); *y = prj->w[0]*( xi + eta); } else { *x = prj->w[0]*(-xi - eta); *y = prj->w[0]*( xi - eta); } return 0; } /*--------------------------------------------------------------------------*/ int astXPHrev(x, y, prj, phi, theta) const double x, y; struct AstPrjPrm *prj; double *phi, *theta; { double abseta, eta, eta1, sigma, xi, xi1, xr, yr; const double tol = 1.0e-12; if (prj->flag != WCS__XPH) { if (astXPHset(prj)) return 1; } xr = x*prj->w[1]; yr = y*prj->w[1]; if (xr <= 0.0 && 0.0 < yr) { xi1 = -xr - yr; eta1 = xr - yr; *phi = -180.0; } else if (xr < 0.0 && yr <= 0.0) { xi1 = xr - yr; eta1 = xr + yr; *phi = -90.0; } else if (0.0 <= xr && yr < 0.0) { xi1 = xr + yr; eta1 = -xr + yr; *phi = 0.0; } else { xi1 = -xr + yr; eta1 = -xr - yr; *phi = 90.0; } xi = xi1 + 45.0; eta = eta1 + 90.0; abseta = fabs(eta); if (abseta <= 90.0) { if (abseta <= 45.0) { /* Equatorial regime. */ *phi += xi; *theta = astASind(eta/67.5); /* Bounds checking. */ if (45.0+tol < fabs(xi1)) return 2; } else { /* Polar regime. */ sigma = (90.0 - abseta) / 45.0; /* Ensure an exact result for points on the boundary. */ if (xr == 0.0) { if (yr <= 0.0) { *phi = 0.0; } else { *phi = 180.0; } } else if (yr == 0.0) { if (xr < 0.0) { *phi = -90.0; } else { *phi = 90.0; } } else { *phi += 45.0 + xi1/sigma; } if (sigma < prj->w[3]) { *theta = 90.0 - sigma*prj->w[4]; } else { *theta = astASind(1.0 - sigma*sigma/3.0); } if (eta < 0.0) *theta = -(*theta); /* Bounds checking. */ if (eta < -45.0 && eta+90.0+tol < fabs(xi1)) return 2; } } else { /* Beyond latitude range. */ *phi = 0.0; *theta = 0.0; return 2; } return 0; }