1
/*=============================================================================
3
* WCSLIB - an implementation of the FITS WCS proposal.
4
* Copyright (C) 1995, Mark Calabretta
6
* This library is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU Library General Public License as published
8
* by the Free Software Foundation; either version 2 of the License, or (at
9
* your option) any later version.
11
* This library is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
14
* General Public License for more details.
16
* You should have received a copy of the GNU Library General Public License
17
* along with this library; if not, write to the Free Software Foundation,
18
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
* Correspondence concerning WCSLIB may be directed to:
21
* Internet email: mcalabre@atnf.csiro.au
22
* Postal address: Dr. Mark Calabretta,
23
* Australia Telescope National Facility,
30
*=============================================================================
32
* C routines which implement the FITS World Coordinate System (WCS)
37
* These routines are provided as drivers for the lower level spherical
38
* coordinate transformation and projection routines. There are separate
39
* driver routines for the forward, celfwd(), and reverse, celrev(),
42
* An initialization routine, celset(), computes intermediate values from
43
* the transformation parameters but need not be called explicitly - see the
44
* explanation of cel.flag below.
47
* Initialization routine; celset()
48
* --------------------------------
49
* Initializes members of a celprm data structure which hold intermediate
50
* values. Note that this routine need not be called directly; it will be
51
* invoked by celfwd() and celrev() if the "flag" structure member is
52
* anything other than a predefined magic value.
55
* pcode[4] char WCS projection code (see below).
58
* cel celprm* Spherical coordinate transformation parameters
60
* prj prjprm* Projection parameters (usage is described in the
61
* prologue to "proj.c").
63
* Function return value:
66
* 1: Invalid coordinate transformation parameters.
67
* 2: Ill-conditioned coordinate transformation
70
* Forward transformation; celfwd()
71
* --------------------------------
72
* Compute (x,y) coordinates in the plane of projection from celestial
73
* coordinates (lng,lat).
76
* pcode[4] char WCS projection code (see below).
77
* lng,lat double Celestial longitude and latitude of the projected
81
* cel celprm* Spherical coordinate transformation parameters
85
* phi, double* Longitude and latitude in the native coordinate
86
* theta system of the projection, in degrees.
89
* prj prjprm* Projection parameters (usage is described in the
90
* prologue to "proj.c").
93
* x,y double* Projected coordinates, "degrees".
95
* Function return value:
98
* 1: Invalid coordinate transformation parameters.
99
* 2: Invalid projection parameters.
100
* 3: Invalid value of (lng,lat).
102
* Reverse transformation; celrev()
103
* --------------------------------
104
* Compute the celestial coordinates (lng,lat) of the point with projected
108
* pcode[4] char WCS projection code (see below).
109
* x,y double Projected coordinates, "degrees".
111
* Given and returned:
112
* prj prjprm* Projection parameters (usage is described in the
113
* prologue to "proj.c").
116
* phi, double* Longitude and latitude in the native coordinate
117
* theta system of the projection, in degrees.
119
* Given and returned:
120
* cel celprm* Spherical coordinate transformation parameters
124
* lng,lat double* Celestial longitude and latitude of the projected
127
* Function return value:
130
* 1: Invalid coordinate transformation parameters.
131
* 2: Invalid projection parameters.
132
* 3: Invalid value of (x,y).
134
* Coordinate transformation parameters
135
* ------------------------------------
136
* The celprm struct consists of the following:
139
* The celprm struct contains pointers to the forward and reverse
140
* projection routines as well as intermediaries computed from the
141
* reference coordinates (see below). Whenever the projection code
142
* (pcode) or any of ref[4] are set or changed then this flag must be
143
* set to zero to signal the initialization routine, celset(), to
144
* redetermine the function pointers and recompute intermediaries.
145
* Once this has been done pcode itself is ignored.
148
* The first pair of values should be set to the celestial longitude
149
* and latitude (usually right ascension and declination) of the
150
* reference point of the projection.
152
* The second pair of values are the native longitude and latitude of
153
* the pole of the celestial coordinate system and correspond to the
154
* FITS keywords LONGPOLE and LATPOLE.
156
* LONGPOLE defaults to 0 degrees if the celestial latitude of the
157
* reference point of the projection is greater than the native
158
* latitude, otherwise 180 degrees. (This is the condition for the
159
* celestial latitude to increase in the same direction as the native
160
* latitude at the reference point.) ref[2] may be set to 999.0 to
161
* indicate that the correct default should be substituted.
163
* In some circumstances the latitude of the native pole may be
164
* determined by the first three values only to within a sign and
165
* LATPOLE is used to choose between the two solutions. LATPOLE is
166
* set in ref[3] and the solution closest to this value is used to
167
* reset ref[3]. It is therefore legitimate, for example, to set
168
* ref[3] to 999.0 to choose the more northerly solution - the default
169
* if the LATPOLE card is omitted from the FITS header. For the
170
* special case where the reference point of the projection is at
171
* native latitude zero, its celestial latitude is zero, and
172
* LONGPOLE = +/- 90 then the native latitude of the pole is not
173
* determined by the first three reference values and LATPOLE
174
* specifies it completely.
176
* The remaining members of the celprm struct are maintained by the
177
* initialization routines and should not be modified. This is done for the
178
* sake of efficiency and to allow an arbitrary number of contexts to be
179
* maintained simultaneously.
182
* Euler angles and associated intermediaries derived from the
183
* coordinate reference values.
186
* Pointers to the forward and reverse projection routines.
189
* WCS projection codes
190
* --------------------
191
* Zenithals/azimuthals:
192
* AZP: zenithal/azimuthal perspective
194
* SIN: synthesis (generalized orthographic)
196
* ARC: zenithal/azimuthal equidistant
197
* ZPN: zenithal/azimuthal polynomial
198
* ZEA: zenithal/azimuthal equal area
202
* CYP: cylindrical perspective
205
* CEA: cylindrical equal area
208
* COP: conic perspective
209
* COD: conic equidistant
210
* COE: conic equal area
211
* COO: conic orthomorphic
217
* Pseudo-cylindricals:
218
* GLS: Sanson-Flamsteed (global sinusoidal)
226
* CSC: COBE quadrilateralized spherical cube
227
* QSC: quadrilateralized spherical cube
228
* TSC: tangential spherical cube
230
* Author: Mark Calabretta, Australia Telescope National Facility
231
*===========================================================================*/
244
#define signbit(X) ((X) < 0.0 ? 0 : 1)
247
int celset(pcode, cel, prj)
255
const double tol = 1.0e-10;
256
double clat0, cphip, cthe0, theta0, slat0, sphip, sthe0;
257
double latp, latp1, latp2;
258
double u, v, x, y, z;
260
/* Set pointers to the forward and reverse projection routines. */
261
if (strcmp(pcode, "AZP") == 0) {
262
cel->prjfwd = azpfwd;
263
cel->prjrev = azprev;
265
} else if (strcmp(pcode, "TAN") == 0) {
266
cel->prjfwd = tanfwd;
267
cel->prjrev = tanrev;
269
} else if (strcmp(pcode, "SIN") == 0) {
270
cel->prjfwd = sinfwd;
271
cel->prjrev = sinrev;
273
} else if (strcmp(pcode, "STG") == 0) {
274
cel->prjfwd = stgfwd;
275
cel->prjrev = stgrev;
277
} else if (strcmp(pcode, "ARC") == 0) {
278
cel->prjfwd = arcfwd;
279
cel->prjrev = arcrev;
281
} else if (strcmp(pcode, "ZPN") == 0) {
282
cel->prjfwd = zpnfwd;
283
cel->prjrev = zpnrev;
285
} else if (strcmp(pcode, "ZEA") == 0) {
286
cel->prjfwd = zeafwd;
287
cel->prjrev = zearev;
289
} else if (strcmp(pcode, "AIR") == 0) {
290
cel->prjfwd = airfwd;
291
cel->prjrev = airrev;
293
} else if (strcmp(pcode, "CYP") == 0) {
294
cel->prjfwd = cypfwd;
295
cel->prjrev = cyprev;
297
} else if (strcmp(pcode, "CAR") == 0) {
298
cel->prjfwd = carfwd;
299
cel->prjrev = carrev;
301
} else if (strcmp(pcode, "MER") == 0) {
302
cel->prjfwd = merfwd;
303
cel->prjrev = merrev;
305
} else if (strcmp(pcode, "CEA") == 0) {
306
cel->prjfwd = ceafwd;
307
cel->prjrev = cearev;
309
} else if (strcmp(pcode, "COP") == 0) {
310
cel->prjfwd = copfwd;
311
cel->prjrev = coprev;
313
} else if (strcmp(pcode, "COD") == 0) {
314
cel->prjfwd = codfwd;
315
cel->prjrev = codrev;
317
} else if (strcmp(pcode, "COE") == 0) {
318
cel->prjfwd = coefwd;
319
cel->prjrev = coerev;
321
} else if (strcmp(pcode, "COO") == 0) {
322
cel->prjfwd = coofwd;
323
cel->prjrev = coorev;
325
} else if (strcmp(pcode, "BON") == 0) {
326
cel->prjfwd = bonfwd;
327
cel->prjrev = bonrev;
329
} else if (strcmp(pcode, "PCO") == 0) {
330
cel->prjfwd = pcofwd;
331
cel->prjrev = pcorev;
333
} else if (strcmp(pcode, "GLS") == 0) {
334
cel->prjfwd = glsfwd;
335
cel->prjrev = glsrev;
337
} else if (strcmp(pcode, "PAR") == 0) {
338
cel->prjfwd = parfwd;
339
cel->prjrev = parrev;
341
} else if (strcmp(pcode, "AIT") == 0) {
342
cel->prjfwd = aitfwd;
343
cel->prjrev = aitrev;
345
} else if (strcmp(pcode, "MOL") == 0) {
346
cel->prjfwd = molfwd;
347
cel->prjrev = molrev;
349
} else if (strcmp(pcode, "CSC") == 0) {
350
cel->prjfwd = cscfwd;
351
cel->prjrev = cscrev;
353
} else if (strcmp(pcode, "QSC") == 0) {
354
cel->prjfwd = qscfwd;
355
cel->prjrev = qscrev;
357
} else if (strcmp(pcode, "TSC") == 0) {
358
cel->prjfwd = tscfwd;
359
cel->prjrev = tscrev;
362
/* Unrecognized projection code. */
366
/* Set default for native longitude of the celestial pole? */
367
dophip = (cel->ref[2] == 999.0);
369
/* Compute celestial coordinates of the native pole. */
370
if (theta0 == 90.0) {
371
/* Reference point is at the native pole. */
374
/* Set default for longitude of the celestial pole. */
381
cel->euler[0] = cel->ref[0];
382
cel->euler[1] = 90.0 - latp;
384
/* Reference point away from the native pole. */
386
/* Set default for longitude of the celestial pole. */
388
cel->ref[2] = (cel->ref[1] < theta0) ? 180.0 : 0.0;
391
clat0 = wcs_cosd(cel->ref[1]);
392
slat0 = wcs_sind(cel->ref[1]);
393
cphip = wcs_cosd(cel->ref[2]);
394
sphip = wcs_sind(cel->ref[2]);
395
cthe0 = wcs_cosd(theta0);
396
sthe0 = wcs_sind(theta0);
406
/* latp determined by LATPOLE in this case. */
409
if (fabs(slat0/z) > 1.0) {
414
v = wcs_acosd(slat0/z);
419
} else if (latp1 < -180.0) {
426
} else if (latp2 < -180.0) {
430
if (fabs(cel->ref[3]-latp1) < fabs(cel->ref[3]-latp2)) {
431
if (fabs(latp1) < 90.0+tol) {
437
if (fabs(latp2) < 90.0+tol) {
447
cel->euler[1] = 90.0 - latp;
449
z = wcs_cosd(latp)*clat0;
451
if (fabs(clat0) < tol) {
452
/* Celestial pole at the reference point. */
453
cel->euler[0] = cel->ref[0];
454
cel->euler[1] = 90.0 - theta0;
455
} else if (latp > 0.0) {
456
/* Celestial pole at the native north pole.*/
457
cel->euler[0] = cel->ref[0] + cel->ref[2] - 180.0;
459
} else if (latp < 0.0) {
460
/* Celestial pole at the native south pole. */
461
cel->euler[0] = cel->ref[0] - cel->ref[2];
462
cel->euler[1] = 180.0;
465
x = (sthe0 - wcs_sind(latp)*slat0)/z;
466
y = sphip*cthe0/clat0;
467
if (x == 0.0 && y == 0.0) {
470
cel->euler[0] = cel->ref[0] - wcs_atan2d(y,x);
473
/* Make euler[0] the same sign as ref[0]. */
474
if (cel->ref[0] >= 0.0) {
475
if (cel->euler[0] < 0.0) cel->euler[0] += 360.0;
477
if (cel->euler[0] > 0.0) cel->euler[0] -= 360.0;
481
cel->euler[2] = cel->ref[2];
482
cel->euler[3] = wcs_cosd(cel->euler[1]);
483
cel->euler[4] = wcs_sind(cel->euler[1]);
486
/* Check for ill-conditioned parameters. */
487
if (fabs(latp) > 90.0+tol) {
494
/*--------------------------------------------------------------------------*/
496
int celfwd(pcode, lng, lat, cel, phi, theta, prj, x, y)
512
if (cel->flag != CELSET) {
513
if (celset(pcode, cel, prj)) return 1;
516
/* Compute native coordinates. */
517
sphfwd(lng, lat, cel->euler, phi, theta);
519
/* Apply forward projection. */
520
if ((err = cel->prjfwd(*phi, *theta, prj, x, y))) {
521
return err == 1 ? 2 : 3;
527
/*--------------------------------------------------------------------------*/
529
int celrev(pcode, x, y, prj, phi, theta, cel, lng, lat)
545
if (cel->flag != CELSET) {
546
if(celset(pcode, cel, prj)) return 1;
549
/* Apply reverse projection. */
550
if ((err = cel->prjrev(x, y, prj, phi, theta))) {
551
return err == 1 ? 2 : 3;
554
/* Compute native coordinates. */
555
sphrev(*phi, *theta, cel->euler, lng, lat);