1 /*============================================================================
2   WCSLIB 7.7 - an implementation of the FITS WCS standard.
3   Copyright (C) 1995-2021, Mark Calabretta
4 
5   This file is part of WCSLIB.
6 
7   WCSLIB is free software: you can redistribute it and/or modify it under the
8   terms of the GNU Lesser General Public License as published by the Free
9   Software Foundation, either version 3 of the License, or (at your option)
10   any later version.
11 
12   WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
15   more details.
16 
17   You should have received a copy of the GNU Lesser General Public License
18   along with WCSLIB.  If not, see http://www.gnu.org/licenses.
19 
20   Author: Mark Calabretta, Australia Telescope National Facility, CSIRO.
21   http://www.atnf.csiro.au/people/Mark.Calabretta
22   $Id: wcs.c,v 7.7 2021/07/12 06:36:49 mcalabre Exp $
23 *===========================================================================*/
24 
25 #include <math.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "wcserr.h"
31 #include "wcsmath.h"
32 #include "wcsprintf.h"
33 #include "wcstrig.h"
34 #include "wcsunits.h"
35 #include "wcsutil.h"
36 #include "wtbarr.h"
37 #include "lin.h"
38 #include "dis.h"
39 #include "log.h"
40 #include "spc.h"
41 #include "prj.h"
42 #include "sph.h"
43 #include "cel.h"
44 #include "tab.h"
45 #include "wcs.h"
46 
47 const int WCSSET = 137;
48 
49 // Maximum number of PVi_ma and PSi_ma keywords.
50 int NPVMAX = 64;
51 int NPSMAX =  8;
52 
53 // Map status return value to message.
54 const char *wcs_errmsg[] = {
55   "Success",
56   "Null wcsprm pointer passed",
57   "Memory allocation failed",
58   "Linear transformation matrix is singular",
59   "Inconsistent or unrecognized coordinate axis type",
60   "Invalid parameter value",
61   "Unrecognized coordinate transformation parameter",
62   "Ill-conditioned coordinate transformation parameter",
63   "One or more of the pixel coordinates were invalid",
64   "One or more of the world coordinates were invalid",
65   "Invalid world coordinate",
66   "No solution found in the specified interval",
67   "Invalid subimage specification",
68   "Non-separable subimage coordinate system",
69   "wcsprm struct is unset, use wcsset()"};
70 
71 // Map error returns for lower-level routines.
72 const int wcs_linerr[] = {
73   WCSERR_SUCCESS,		//  0: LINERR_SUCCESS
74   WCSERR_NULL_POINTER,		//  1: LINERR_NULL_POINTER
75   WCSERR_MEMORY,		//  2: LINERR_MEMORY
76   WCSERR_SINGULAR_MTX,		//  3: LINERR_SINGULAR_MTX
77   WCSERR_BAD_PARAM,		//  4: LINERR_DISTORT_INIT
78   WCSERR_BAD_PIX,		//  5: LINERR_DISTORT
79   WCSERR_BAD_WORLD		//  6: LINERR_DEDISTORT
80 };
81 
82 const int wcs_logerr[] = {
83   WCSERR_SUCCESS,		//  0: LOGERR_SUCCESS
84   WCSERR_NULL_POINTER,		//  1: LOGERR_NULL_POINTER
85   WCSERR_BAD_PARAM,		//  2: LOGERR_BAD_LOG_REF_VAL
86   WCSERR_BAD_PIX,		//  3: LOGERR_BAD_X
87   WCSERR_BAD_WORLD		//  4: LOGERR_BAD_WORLD
88 };
89 
90 const int wcs_spcerr[] = {
91 				// -1: SPCERR_NO_CHANGE
92   WCSERR_SUCCESS,		//  0: SPCERR_SUCCESS
93   WCSERR_NULL_POINTER,		//  1: SPCERR_NULL_POINTER
94   WCSERR_BAD_PARAM,		//  2: SPCERR_BAD_SPEC_PARAMS
95   WCSERR_BAD_PIX,		//  3: SPCERR_BAD_X
96   WCSERR_BAD_WORLD		//  4: SPCERR_BAD_SPEC
97 };
98 
99 const int wcs_celerr[] = {
100   WCSERR_SUCCESS,		//  0: CELERR_SUCCESS
101   WCSERR_NULL_POINTER,		//  1: CELERR_NULL_POINTER
102   WCSERR_BAD_PARAM,		//  2: CELERR_BAD_PARAM
103   WCSERR_BAD_COORD_TRANS,	//  3: CELERR_BAD_COORD_TRANS
104   WCSERR_ILL_COORD_TRANS,	//  4: CELERR_ILL_COORD_TRANS
105   WCSERR_BAD_PIX,		//  5: CELERR_BAD_PIX
106   WCSERR_BAD_WORLD		//  6: CELERR_BAD_WORLD
107 };
108 
109 const int wcs_taberr[] = {
110   WCSERR_SUCCESS,		//  0: TABERR_SUCCESS
111   WCSERR_NULL_POINTER,		//  1: TABERR_NULL_POINTER
112   WCSERR_MEMORY,		//  2: TABERR_MEMORY
113   WCSERR_BAD_PARAM,		//  3: TABERR_BAD_PARAMS
114   WCSERR_BAD_PIX,		//  4: TABERR_BAD_X
115   WCSERR_BAD_WORLD		//  5: TABERR_BAD_WORLD
116 };
117 
118 // Convenience macro for invoking wcserr_set().
119 #define WCS_ERRMSG(status) WCSERR_SET(status), wcs_errmsg[status]
120 
121 #ifndef signbit
122 #define signbit(X) ((X) < 0.0 ? 1 : 0)
123 #endif
124 
125 // Internal helper functions, not for general use.
126 static int wcs_types(struct wcsprm *);
127 static int wcs_units(struct wcsprm *);
128 
129 //----------------------------------------------------------------------------
130 
wcsnpv(int npvmax)131 int wcsnpv(int npvmax) { if (npvmax >= 0) NPVMAX = npvmax; return NPVMAX; }
wcsnps(int npsmax)132 int wcsnps(int npsmax) { if (npsmax >= 0) NPSMAX = npsmax; return NPSMAX; }
133 
134 //----------------------------------------------------------------------------
135 
wcsini(int alloc,int naxis,struct wcsprm * wcs)136 int wcsini(int alloc, int naxis, struct wcsprm *wcs)
137 
138 {
139   return wcsinit(alloc, naxis, wcs, -1, -1, -1);
140 }
141 
142 //----------------------------------------------------------------------------
143 
wcsinit(int alloc,int naxis,struct wcsprm * wcs,int npvmax,int npsmax,int ndpmax)144 int wcsinit(
145   int alloc,
146   int naxis,
147   struct wcsprm *wcs,
148   int npvmax,
149   int npsmax,
150   int ndpmax)
151 
152 {
153   static const char *function = "wcsinit";
154 
155   int i, j, k, status;
156   double *cd;
157   struct wcserr **err;
158 
159   // Check inputs.
160   if (wcs == 0x0) return WCSERR_NULL_POINTER;
161 
162   if (npvmax < 0) npvmax = wcsnpv(-1);
163   if (npsmax < 0) npsmax = wcsnps(-1);
164 
165 
166   // Initialize error message handling...
167   if (wcs->flag == -1) {
168     wcs->err = 0x0;
169   }
170   err = &(wcs->err);
171   wcserr_clear(err);
172 
173   // ...and also in the contained structs in case we have to return due to
174   // an error before they can be initialized by their specialized routines,
175   // since wcsperr() assumes their wcserr pointers are valid.
176   if (wcs->flag == -1) {
177     wcs->lin.err = 0x0;
178     wcs->cel.err = 0x0;
179     wcs->spc.err = 0x0;
180   }
181   wcserr_clear(&(wcs->lin.err));
182   wcserr_clear(&(wcs->cel.err));
183   wcserr_clear(&(wcs->spc.err));
184 
185 
186   // Initialize pointers.
187   if (wcs->flag == -1 || wcs->m_flag != WCSSET) {
188     if (wcs->flag == -1) {
189       wcs->tab   = 0x0;
190       wcs->types = 0x0;
191       wcs->lin.flag = -1;
192     }
193 
194     // Initialize memory management.
195     wcs->m_flag  = 0;
196     wcs->m_naxis = 0;
197     wcs->m_crpix = 0x0;
198     wcs->m_pc    = 0x0;
199     wcs->m_cdelt = 0x0;
200     wcs->m_crval = 0x0;
201     wcs->m_cunit = 0x0;
202     wcs->m_ctype = 0x0;
203     wcs->m_pv    = 0x0;
204     wcs->m_ps    = 0x0;
205     wcs->m_cd    = 0x0;
206     wcs->m_crota = 0x0;
207     wcs->m_colax = 0x0;
208     wcs->m_cname = 0x0;
209     wcs->m_crder = 0x0;
210     wcs->m_csyer = 0x0;
211     wcs->m_czphs = 0x0;
212     wcs->m_cperi = 0x0;
213     wcs->m_aux   = 0x0;
214     wcs->m_tab   = 0x0;
215     wcs->m_wtb   = 0x0;
216   }
217 
218   if (naxis < 0) {
219     return wcserr_set(WCSERR_SET(WCSERR_MEMORY),
220       "naxis must not be negative (got %d)", naxis);
221   }
222 
223 
224   // Allocate memory for arrays if required.
225   if (alloc ||
226      wcs->crpix == 0x0 ||
227      wcs->pc    == 0x0 ||
228      wcs->cdelt == 0x0 ||
229      wcs->crval == 0x0 ||
230      wcs->cunit == 0x0 ||
231      wcs->ctype == 0x0 ||
232      (npvmax && wcs->pv == 0x0) ||
233      (npsmax && wcs->ps == 0x0) ||
234      wcs->cd    == 0x0 ||
235      wcs->crota == 0x0 ||
236      wcs->colax == 0x0 ||
237      wcs->cname == 0x0 ||
238      wcs->crder == 0x0 ||
239      wcs->csyer == 0x0 ||
240      wcs->czphs == 0x0 ||
241      wcs->cperi == 0x0) {
242 
243     // Was sufficient allocated previously?
244     if (wcs->m_flag == WCSSET &&
245        (wcs->m_naxis < naxis  ||
246         wcs->npvmax  < npvmax ||
247         wcs->npsmax  < npsmax)) {
248       // No, free it.
249       wcsfree(wcs);
250     }
251 
252     if (alloc || wcs->crpix == 0x0) {
253       if (wcs->m_crpix) {
254         // In case the caller fiddled with it.
255         wcs->crpix = wcs->m_crpix;
256 
257       } else {
258         if ((wcs->crpix = calloc(naxis, sizeof(double))) == 0x0) {
259           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
260         }
261 
262         wcs->m_flag  = WCSSET;
263         wcs->m_naxis = naxis;
264         wcs->m_crpix = wcs->crpix;
265       }
266     }
267 
268     if (alloc || wcs->pc == 0x0) {
269       if (wcs->m_pc) {
270         // In case the caller fiddled with it.
271         wcs->pc = wcs->m_pc;
272 
273       } else {
274         if ((wcs->pc = calloc(naxis*naxis, sizeof(double))) == 0x0) {
275           wcsfree(wcs);
276           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
277         }
278 
279         wcs->m_flag  = WCSSET;
280         wcs->m_naxis = naxis;
281         wcs->m_pc    = wcs->pc;
282       }
283     }
284 
285     if (alloc || wcs->cdelt == 0x0) {
286       if (wcs->m_cdelt) {
287         // In case the caller fiddled with it.
288         wcs->cdelt = wcs->m_cdelt;
289 
290       } else {
291         if ((wcs->cdelt = calloc(naxis, sizeof(double))) == 0x0) {
292           wcsfree(wcs);
293           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
294         }
295 
296         wcs->m_flag  = WCSSET;
297         wcs->m_naxis = naxis;
298         wcs->m_cdelt = wcs->cdelt;
299       }
300     }
301 
302     if (alloc || wcs->crval == 0x0) {
303       if (wcs->m_crval) {
304         // In case the caller fiddled with it.
305         wcs->crval = wcs->m_crval;
306 
307       } else {
308         if ((wcs->crval = calloc(naxis, sizeof(double))) == 0x0) {
309           wcsfree(wcs);
310           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
311         }
312 
313         wcs->m_flag  = WCSSET;
314         wcs->m_naxis = naxis;
315         wcs->m_crval = wcs->crval;
316       }
317     }
318 
319     if (alloc || wcs->cunit == 0x0) {
320       if (wcs->m_cunit) {
321         // In case the caller fiddled with it.
322         wcs->cunit = wcs->m_cunit;
323 
324       } else {
325         if ((wcs->cunit = calloc(naxis, sizeof(char [72]))) == 0x0) {
326           wcsfree(wcs);
327           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
328         }
329 
330         wcs->m_flag  = WCSSET;
331         wcs->m_naxis = naxis;
332         wcs->m_cunit = wcs->cunit;
333       }
334     }
335 
336     if (alloc || wcs->ctype == 0x0) {
337       if (wcs->m_ctype) {
338         // In case the caller fiddled with it.
339         wcs->ctype = wcs->m_ctype;
340 
341       } else {
342         if ((wcs->ctype = calloc(naxis, sizeof(char [72]))) == 0x0) {
343           wcsfree(wcs);
344           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
345         }
346 
347         wcs->m_flag  = WCSSET;
348         wcs->m_naxis = naxis;
349         wcs->m_ctype = wcs->ctype;
350       }
351     }
352 
353     if (alloc || wcs->pv == 0x0) {
354       if (wcs->m_pv) {
355         // In case the caller fiddled with it.
356         wcs->pv = wcs->m_pv;
357 
358       } else {
359         if (npvmax) {
360           if ((wcs->pv = calloc(npvmax, sizeof(struct pvcard))) == 0x0) {
361             wcsfree(wcs);
362             return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
363           }
364         } else {
365           wcs->pv = 0x0;
366         }
367 
368         wcs->npvmax  = npvmax;
369 
370         wcs->m_flag  = WCSSET;
371         wcs->m_naxis = naxis;
372         wcs->m_pv    = wcs->pv;
373       }
374     }
375 
376     if (alloc || wcs->ps == 0x0) {
377       if (wcs->m_ps) {
378         // In case the caller fiddled with it.
379         wcs->ps = wcs->m_ps;
380 
381       } else {
382         if (npsmax) {
383           if ((wcs->ps = calloc(npsmax, sizeof(struct pscard))) == 0x0) {
384             wcsfree(wcs);
385             return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
386           }
387         } else {
388           wcs->ps = 0x0;
389         }
390 
391         wcs->npsmax  = npsmax;
392 
393         wcs->m_flag  = WCSSET;
394         wcs->m_naxis = naxis;
395         wcs->m_ps    = wcs->ps;
396       }
397     }
398 
399     if (alloc || wcs->cd == 0x0) {
400       if (wcs->m_cd) {
401         // In case the caller fiddled with it.
402         wcs->cd = wcs->m_cd;
403 
404       } else {
405         if ((wcs->cd = calloc(naxis*naxis, sizeof(double))) == 0x0) {
406           wcsfree(wcs);
407           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
408         }
409 
410         wcs->m_flag  = WCSSET;
411         wcs->m_naxis = naxis;
412         wcs->m_cd    = wcs->cd;
413       }
414     }
415 
416     if (alloc || wcs->crota == 0x0) {
417       if (wcs->m_crota) {
418         // In case the caller fiddled with it.
419         wcs->crota = wcs->m_crota;
420 
421       } else {
422         if ((wcs->crota = calloc(naxis, sizeof(double))) == 0x0) {
423           wcsfree(wcs);
424           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
425         }
426 
427         wcs->m_flag  = WCSSET;
428         wcs->m_naxis = naxis;
429         wcs->m_crota = wcs->crota;
430       }
431     }
432 
433     if (alloc || wcs->colax == 0x0) {
434       if (wcs->m_colax) {
435         // In case the caller fiddled with it.
436         wcs->colax = wcs->m_colax;
437 
438       } else {
439         if ((wcs->colax = calloc(naxis, sizeof(int))) == 0x0) {
440           wcsfree(wcs);
441           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
442         }
443 
444         wcs->m_flag  = WCSSET;
445         wcs->m_naxis = naxis;
446         wcs->m_colax = wcs->colax;
447       }
448     }
449 
450     if (alloc || wcs->cname == 0x0) {
451       if (wcs->m_cname) {
452         // In case the caller fiddled with it.
453         wcs->cname = wcs->m_cname;
454 
455       } else {
456         if ((wcs->cname = calloc(naxis, sizeof(char [72]))) == 0x0) {
457           wcsfree(wcs);
458           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
459         }
460 
461         wcs->m_flag  = WCSSET;
462         wcs->m_naxis = naxis;
463         wcs->m_cname = wcs->cname;
464       }
465     }
466 
467     if (alloc || wcs->crder == 0x0) {
468       if (wcs->m_crder) {
469         // In case the caller fiddled with it.
470         wcs->crder = wcs->m_crder;
471 
472       } else {
473         if ((wcs->crder = calloc(naxis, sizeof(double))) == 0x0) {
474           wcsfree(wcs);
475           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
476         }
477 
478         wcs->m_flag  = WCSSET;
479         wcs->m_naxis = naxis;
480         wcs->m_crder = wcs->crder;
481       }
482     }
483 
484     if (alloc || wcs->csyer == 0x0) {
485       if (wcs->m_csyer) {
486         // In case the caller fiddled with it.
487         wcs->csyer = wcs->m_csyer;
488 
489       } else {
490         if ((wcs->csyer = calloc(naxis, sizeof(double))) == 0x0) {
491           wcsfree(wcs);
492           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
493         }
494 
495         wcs->m_flag  = WCSSET;
496         wcs->m_naxis = naxis;
497         wcs->m_csyer = wcs->csyer;
498       }
499     }
500 
501     if (alloc || wcs->czphs == 0x0) {
502       if (wcs->m_czphs) {
503         // In case the caller fiddled with it.
504         wcs->czphs = wcs->m_czphs;
505 
506       } else {
507         if ((wcs->czphs = calloc(naxis, sizeof(double))) == 0x0) {
508           wcsfree(wcs);
509           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
510         }
511 
512         wcs->m_flag  = WCSSET;
513         wcs->m_naxis = naxis;
514         wcs->m_czphs = wcs->czphs;
515       }
516     }
517 
518     if (alloc || wcs->cperi == 0x0) {
519       if (wcs->m_cperi) {
520         // In case the caller fiddled with it.
521         wcs->cperi = wcs->m_cperi;
522 
523       } else {
524         if ((wcs->cperi = calloc(naxis, sizeof(double))) == 0x0) {
525           wcsfree(wcs);
526           return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
527         }
528 
529         wcs->m_flag  = WCSSET;
530         wcs->m_naxis = naxis;
531         wcs->m_cperi = wcs->cperi;
532       }
533     }
534   }
535 
536 
537   wcs->flag  = 0;
538   wcs->naxis = naxis;
539 
540 
541   // Set defaults for the linear transformation.
542   wcs->lin.crpix  = wcs->crpix;
543   wcs->lin.pc     = wcs->pc;
544   wcs->lin.cdelt  = wcs->cdelt;
545   if ((status = lininit(0, naxis, &(wcs->lin), ndpmax))) {
546     return wcserr_set(WCS_ERRMSG(wcs_linerr[status]));
547   }
548 
549 
550   // CRVALia defaults to 0.0.
551   for (i = 0; i < naxis; i++) {
552     wcs->crval[i] = 0.0;
553   }
554 
555 
556   // CUNITia and CTYPEia are blank by default.
557   for (i = 0; i < naxis; i++) {
558     memset(wcs->cunit[i], 0, 72);
559     memset(wcs->ctype[i], 0, 72);
560   }
561 
562 
563   // Set defaults for the celestial transformation parameters.
564   wcs->lonpole = UNDEFINED;
565   wcs->latpole = +90.0;
566 
567   // Set defaults for the spectral transformation parameters.
568   wcs->restfrq = 0.0;
569   wcs->restwav = 0.0;
570 
571   // Default parameter values.
572   wcs->npv = 0;
573   for (k = 0; k < wcs->npvmax; k++) {
574     wcs->pv[k].i = 0;
575     wcs->pv[k].m = 0;
576     wcs->pv[k].value = 0.0;
577   }
578 
579   wcs->nps = 0;
580   for (k = 0; k < wcs->npsmax; k++) {
581     wcs->ps[k].i = 0;
582     wcs->ps[k].m = 0;
583     memset(wcs->ps[k].value, 0, 72);
584   }
585 
586   // Defaults for alternate linear transformations.
587   cd = wcs->cd;
588   for (i = 0; i < naxis; i++) {
589     for (j = 0; j < naxis; j++) {
590       *(cd++) = 0.0;
591     }
592   }
593   for (i = 0; i < naxis; i++) {
594     wcs->crota[i] = 0.0;
595   }
596   wcs->altlin = 0;
597   wcs->velref = 0;
598 
599   // Defaults for auxiliary coordinate system information.
600   memset(wcs->alt, 0, 4);
601   wcs->alt[0] = ' ';
602   wcs->colnum = 0;
603 
604   for (i = 0; i < naxis; i++) {
605     wcs->colax[i] = 0;
606     memset(wcs->cname[i], 0, 72);
607     wcs->crder[i] = UNDEFINED;
608     wcs->csyer[i] = UNDEFINED;
609     wcs->czphs[i] = UNDEFINED;
610     wcs->cperi[i] = UNDEFINED;
611   }
612 
613   memset(wcs->wcsname, 0, 72);
614 
615   memset(wcs->timesys,  0, 72);
616   memset(wcs->trefpos,  0, 72);
617   memset(wcs->trefdir,  0, 72);
618   memset(wcs->plephem,  0, 72);
619 
620   memset(wcs->timeunit, 0, 72);
621   memset(wcs->dateref,  0, 72);
622   wcs->mjdref[0]  = UNDEFINED;
623   wcs->mjdref[1]  = UNDEFINED;
624   wcs->timeoffs   = UNDEFINED;
625 
626   memset(wcs->dateobs, 0, 72);
627   memset(wcs->datebeg, 0, 72);
628   memset(wcs->dateavg, 0, 72);
629   memset(wcs->dateend, 0, 72);
630   wcs->mjdobs     = UNDEFINED;
631   wcs->mjdbeg     = UNDEFINED;
632   wcs->mjdavg     = UNDEFINED;
633   wcs->mjdend     = UNDEFINED;
634   wcs->jepoch     = UNDEFINED;
635   wcs->bepoch     = UNDEFINED;
636   wcs->tstart     = UNDEFINED;
637   wcs->tstop      = UNDEFINED;
638   wcs->xposure    = UNDEFINED;
639   wcs->telapse    = UNDEFINED;
640 
641   wcs->timsyer    = UNDEFINED;
642   wcs->timrder    = UNDEFINED;
643   wcs->timedel    = UNDEFINED;
644   wcs->timepixr   = UNDEFINED;
645 
646   wcs->obsgeo[0]  = UNDEFINED;
647   wcs->obsgeo[1]  = UNDEFINED;
648   wcs->obsgeo[2]  = UNDEFINED;
649   wcs->obsgeo[3]  = UNDEFINED;
650   wcs->obsgeo[4]  = UNDEFINED;
651   wcs->obsgeo[5]  = UNDEFINED;
652   memset(wcs->obsorbit, 0, 72);
653   memset(wcs->radesys,  0, 72);
654   wcs->equinox    = UNDEFINED;
655   memset(wcs->specsys,  0, 72);
656   memset(wcs->ssysobs,  0, 72);
657   wcs->velosys    = UNDEFINED;
658   wcs->zsource    = UNDEFINED;
659   memset(wcs->ssyssrc,  0, 72);
660   wcs->velangl    = UNDEFINED;
661 
662   // No additional auxiliary coordinate system information.
663   wcs->aux  = 0x0;
664 
665   // Tabular parameters.
666   wcs->ntab = 0;
667   wcs->tab  = 0x0;
668   wcs->nwtb = 0;
669   wcs->wtb  = 0x0;
670 
671   // Reset derived values.
672   strcpy(wcs->lngtyp, "    ");
673   strcpy(wcs->lattyp, "    ");
674   wcs->lng  = -1;
675   wcs->lat  = -1;
676   wcs->spec = -1;
677   wcs->cubeface = -1;
678 
679   celini(&(wcs->cel));
680   spcini(&(wcs->spc));
681 
682   return WCSERR_SUCCESS;
683 }
684 
685 //----------------------------------------------------------------------------
686 
wcsauxi(int alloc,struct wcsprm * wcs)687 int wcsauxi(
688   int alloc,
689   struct wcsprm *wcs)
690 
691 {
692   static const char *function = "wcsauxi";
693 
694   struct auxprm *aux;
695   struct wcserr **err;
696 
697   // Check inputs.
698   if (wcs == 0x0) return WCSERR_NULL_POINTER;
699   err = &(wcs->err);
700 
701   // Allocate memory if required.
702   if (alloc || wcs->aux == 0x0) {
703     if (wcs->m_aux) {
704       // In case the caller fiddled with it.
705       wcs->aux = wcs->m_aux;
706 
707     } else {
708       if ((wcs->aux = malloc(sizeof(struct auxprm))) == 0x0) {
709         return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
710       }
711 
712       wcs->m_aux = wcs->aux;
713     }
714   }
715 
716   aux = wcs->aux;
717   aux->rsun_ref = UNDEFINED;
718   aux->dsun_obs = UNDEFINED;
719   aux->crln_obs = UNDEFINED;
720   aux->hgln_obs = UNDEFINED;
721   aux->hglt_obs = UNDEFINED;
722 
723   return WCSERR_SUCCESS;
724 }
725 
726 //----------------------------------------------------------------------------
727 
wcssub(int alloc,const struct wcsprm * wcssrc,int * nsub,int axes[],struct wcsprm * wcsdst)728 int wcssub(
729   int alloc,
730   const struct wcsprm *wcssrc,
731   int *nsub,
732   int axes[],
733   struct wcsprm *wcsdst)
734 
735 {
736   static const char *function = "wcssub";
737 
738   const char *pq = "PQ";
739   char *c, ctypei[16], ctmp[16], *fp;
740   int  axis, axmap[32], cubeface, dealloc, dummy, i, idp, itab, *itmp = 0x0,
741        j, jhat, k, latitude, longitude, m, *map, msub, naxis, ndp, ndpmax,
742        Nhat, npv, npvmax, nps, npsmax, ntmp, other, spectral, status, stokes;
743   const double *srcp;
744   double *dstp;
745   struct tabprm *tab;
746   struct disprm *dissrc, *disdst;
747   struct dpkey  *dpsrc,  *dpdst;
748   struct wcserr **err;
749 
750   if (wcssrc == 0x0) return WCSERR_NULL_POINTER;
751   if (wcsdst == 0x0) return WCSERR_NULL_POINTER;
752   err = &(wcsdst->err);
753 
754   // N.B. we do not rely on the wcsprm struct having been set up.
755   if ((naxis = wcssrc->naxis) <= 0) {
756     return wcserr_set(WCSERR_SET(WCSERR_MEMORY),
757       "naxis must be positive (got %d)", naxis);
758   }
759 
760   if (nsub == 0x0) {
761     nsub = &dummy;
762     *nsub = naxis;
763   } else if (*nsub == 0) {
764     *nsub = naxis;
765   }
766 
767   // Allocate enough temporary storage to hold either axes[] xor map[].
768   ntmp = (*nsub <= naxis) ? naxis : *nsub;
769   if ((itmp = calloc(ntmp, sizeof(int))) == 0x0) {
770     return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
771   }
772 
773   if ((dealloc = (axes == 0x0))) {
774     // Construct an index array.
775     if ((axes = calloc(naxis, sizeof(int))) == 0x0) {
776       free(itmp);
777       return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
778     }
779 
780     for (i = 0; i < naxis; i++) {
781       axes[i] = i+1;
782     }
783   }
784 
785   // So that we don't try to free uninitialized pointers on cleanup.
786   wcsdst->m_aux = 0x0;
787   wcsdst->m_tab = 0x0;
788 
789 
790   msub = 0;
791   for (j = 0; j < *nsub; j++) {
792     axis = axes[j];
793 
794     if (abs(axis) > 0x1000) {
795       // Subimage extraction by type.
796       k = abs(axis) & 0xFF;
797 
798       longitude = k & WCSSUB_LONGITUDE;
799       latitude  = k & WCSSUB_LATITUDE;
800       cubeface  = k & WCSSUB_CUBEFACE;
801       spectral  = k & WCSSUB_SPECTRAL;
802       stokes    = k & WCSSUB_STOKES;
803 
804       if ((other = (axis < 0))) {
805         longitude = !longitude;
806         latitude  = !latitude;
807         cubeface  = !cubeface;
808         spectral  = !spectral;
809         stokes    = !stokes;
810       }
811 
812       for (i = 0; i < naxis; i++) {
813         strncpy (ctypei, (char *)(wcssrc->ctype + i), 8);
814         ctypei[8] = '\0';
815 
816         // Find the last non-blank character.
817         c = ctypei + 8;
818         while (c-- > ctypei) {
819           if (*c == ' ') *c = '\0';
820           if (*c != '\0') break;
821         }
822 
823         if (
824           strcmp(ctypei,   "RA")  == 0 ||
825           strcmp(ctypei+1, "LON") == 0 ||
826           strcmp(ctypei+2, "LN")  == 0 ||
827           strncmp(ctypei,   "RA---", 5) == 0 ||
828           strncmp(ctypei+1, "LON-", 4) == 0 ||
829           strncmp(ctypei+2, "LN-", 3) == 0) {
830           if (!longitude) {
831             continue;
832           }
833 
834         } else if (
835           strcmp(ctypei,   "DEC") == 0 ||
836           strcmp(ctypei+1, "LAT") == 0 ||
837           strcmp(ctypei+2, "LT")  == 0 ||
838           strncmp(ctypei,   "DEC--", 5) == 0 ||
839           strncmp(ctypei+1, "LAT-", 4) == 0 ||
840           strncmp(ctypei+2, "LT-", 3) == 0) {
841           if (!latitude) {
842             continue;
843           }
844 
845         } else if (strcmp(ctypei, "CUBEFACE") == 0) {
846           if (!cubeface) {
847             continue;
848           }
849 
850         } else if ((
851           strncmp(ctypei, "FREQ", 4) == 0 ||
852           strncmp(ctypei, "ENER", 4) == 0 ||
853           strncmp(ctypei, "WAVN", 4) == 0 ||
854           strncmp(ctypei, "VRAD", 4) == 0 ||
855           strncmp(ctypei, "WAVE", 4) == 0 ||
856           strncmp(ctypei, "VOPT", 4) == 0 ||
857           strncmp(ctypei, "ZOPT", 4) == 0 ||
858           strncmp(ctypei, "AWAV", 4) == 0 ||
859           strncmp(ctypei, "VELO", 4) == 0 ||
860           strncmp(ctypei, "BETA", 4) == 0) &&
861           (ctypei[4] == '\0' || ctypei[4] == '-')) {
862           if (!spectral) {
863             continue;
864           }
865 
866         } else if (strcmp(ctypei, "STOKES") == 0) {
867           if (!stokes) {
868             continue;
869           }
870 
871         } else if (!other) {
872           continue;
873         }
874 
875         // This axis is wanted, but has it already been added?
876         for (k = 0; k < msub; k++) {
877           if (itmp[k] == i+1) {
878             break;
879           }
880         }
881         if (k == msub) itmp[msub++] = i+1;
882       }
883 
884     } else if (0 < axis && axis <= naxis) {
885       // Check that the requested axis has not already been added.
886       for (k = 0; k < msub; k++) {
887         if (itmp[k] == axis) {
888           break;
889         }
890       }
891       if (k == msub) itmp[msub++] = axis;
892 
893     } else if (axis == 0) {
894       // Graft on a new axis.
895       itmp[msub++] = 0;
896 
897     } else {
898       status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_SUBIMAGE));
899       goto cleanup;
900     }
901   }
902 
903   if ((*nsub = msub) == 0) {
904     // Zero out this struct.
905     status = wcsinit(alloc, 0, wcsdst, 0, 0, 0);
906     goto cleanup;
907   }
908 
909   for (i = 0; i < *nsub; i++) {
910     axes[i] = itmp[i];
911   }
912 
913 
914   // Construct the inverse axis map (i is 0-relative, j is 1-relative):
915   // axes[i] == j means that output axis i+1 comes from input axis j,
916   // axes[i] == 0 means to create a new axis,
917   //  map[i] == j means that input axis i+1 goes to output axis j,
918   //  map[i] == 0 means that input axis i+1 is not used.
919   map = itmp;
920   for (i = 0; i < naxis; i++) {
921     map[i] = 0;
922   }
923 
924   for (i = 0; i < *nsub; i++) {
925     if (axes[i] > 0) {
926       map[axes[i]-1] = i+1;
927     }
928   }
929 
930 
931   // Check that the subimage coordinate system is separable.  First check
932   // non-zero, off-diagonal elements of the linear transformation matrix.
933   srcp = wcssrc->pc;
934   for (i = 0; i < naxis; i++) {
935     for (j = 0; j < naxis; j++) {
936       if (*(srcp++) == 0.0 || j == i) continue;
937 
938       if ((map[i] == 0) != (map[j] == 0)) {
939         status = wcserr_set(WCS_ERRMSG(WCSERR_NON_SEPARABLE));
940         goto cleanup;
941       }
942     }
943   }
944 
945   // Now check for distortions that depend on other axes.  As the disprm
946   // struct may not have been initialized, we must parse the dpkey entries.
947   ndpmax = 0;
948   for (m = 0; m < 2; m++) {
949     if (m == 0) {
950       dissrc = wcssrc->lin.dispre;
951     } else {
952       dissrc = wcssrc->lin.disseq;
953     }
954 
955     ndp = 0;
956     if (dissrc != 0x0) {
957       for (j = 0; j < naxis; j++) {
958         if (map[j] == 0) continue;
959 
960         // Axis numbers in axmap[] are 0-relative.
961         for (jhat = 0; jhat < 32; jhat++) {
962           axmap[jhat] = -1;
963         }
964 
965         Nhat = 0;
966         dpsrc = dissrc->dp;
967         for (idp = 0; idp < dissrc->ndp; idp++, dpsrc++) {
968           // Thorough error checking will be done later by disset().
969           if (dpsrc->j != j+1) continue;
970           if (dpsrc->field[1] != pq[m]) continue;
971           if ((fp = strchr(dpsrc->field, '.')) == 0x0) continue;
972           fp++;
973 
974           ndp++;
975 
976           if (strncmp(fp, "NAXES", 6) == 0) {
977             Nhat = dpkeyi(dpsrc);
978           } else if (strncmp(fp, "AXIS.", 5) == 0) {
979             sscanf(fp+5, "%d", &jhat);
980             axmap[jhat-1] = dpkeyi(dpsrc) - 1;
981           }
982         }
983 
984         if (Nhat < 0 || (Nhat == 0 && 1 < ndp) || naxis < Nhat || 32 < Nhat) {
985           status = wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM),
986             "NAXES was not set (or bad) for %s distortion on axis %d",
987             dissrc->dtype[j], j+1);
988           goto cleanup;
989         }
990 
991         for (jhat = 0; jhat < Nhat; jhat++) {
992           if (axmap[jhat] < 0) {
993             axmap[jhat] = jhat;
994 
995             // Make room for an additional DPja.AXIS.j record.
996             ndp++;
997           }
998 
999           if (map[axmap[jhat]] == 0) {
1000             // Distortion depends on an axis excluded from the subimage.
1001             status = wcserr_set(WCS_ERRMSG(WCSERR_NON_SEPARABLE));
1002             goto cleanup;
1003           }
1004         }
1005       }
1006     }
1007 
1008     if (ndpmax < ndp) ndpmax = ndp;
1009   }
1010 
1011 
1012   // Number of PVi_ma records in the subimage.
1013   npvmax = 0;
1014   for (m = 0; m < wcssrc->npv; m++) {
1015     i = wcssrc->pv[m].i;
1016     if (i == 0 || (i > 0 && map[i-1])) {
1017       npvmax++;
1018     }
1019   }
1020 
1021   // Number of PSi_ma records in the subimage.
1022   npsmax = 0;
1023   for (m = 0; m < wcssrc->nps; m++) {
1024     i = wcssrc->ps[m].i;
1025     if (i > 0 && map[i-1]) {
1026       npsmax++;
1027     }
1028   }
1029 
1030   // Initialize the destination.
1031   status = wcsinit(alloc, *nsub, wcsdst, npvmax, npsmax, ndpmax);
1032 
1033   for (m = 0; m < 2; m++) {
1034     if (m == 0) {
1035       dissrc = wcssrc->lin.dispre;
1036       disdst = wcsdst->lin.dispre;
1037     } else {
1038       dissrc = wcssrc->lin.disseq;
1039       disdst = wcsdst->lin.disseq;
1040     }
1041 
1042     if (dissrc && !disdst) {
1043       if ((disdst = calloc(1, sizeof(struct disprm))) == 0x0) {
1044         return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
1045       }
1046 
1047       // Also inits disdst.
1048       disdst->flag = -1;
1049       lindist(m+1, &(wcsdst->lin), disdst, ndpmax);
1050     }
1051   }
1052 
1053   if (status) {
1054     goto cleanup;
1055   }
1056 
1057 
1058   // Linear transformation.
1059   srcp = wcssrc->crpix;
1060   dstp = wcsdst->crpix;
1061   for (j = 0; j < *nsub; j++, dstp++) {
1062     if (axes[j] > 0) {
1063       k = axes[j] - 1;
1064       *dstp = *(srcp+k);
1065     }
1066   }
1067 
1068   srcp = wcssrc->pc;
1069   dstp = wcsdst->pc;
1070   for (i = 0; i < *nsub; i++) {
1071     for (j = 0; j < *nsub; j++, dstp++) {
1072       if (axes[i] > 0 && axes[j] > 0) {
1073         k = (axes[i]-1)*naxis + (axes[j]-1);
1074         *dstp = *(srcp+k);
1075       }
1076     }
1077   }
1078 
1079   srcp = wcssrc->cdelt;
1080   dstp = wcsdst->cdelt;
1081   for (i = 0; i < *nsub; i++, dstp++) {
1082     if (axes[i] > 0) {
1083       k = axes[i] - 1;
1084       *dstp = *(srcp+k);
1085     }
1086   }
1087 
1088   // Coordinate reference value.
1089   srcp = wcssrc->crval;
1090   dstp = wcsdst->crval;
1091   for (i = 0; i < *nsub; i++, dstp++) {
1092     if (axes[i] > 0) {
1093       k = axes[i] - 1;
1094       *dstp = *(srcp+k);
1095     }
1096   }
1097 
1098   // Coordinate units and type.
1099   for (i = 0; i < *nsub; i++) {
1100     if (axes[i] > 0) {
1101       k = axes[i] - 1;
1102       strncpy(wcsdst->cunit[i], wcssrc->cunit[k], 72);
1103       strncpy(wcsdst->ctype[i], wcssrc->ctype[k], 72);
1104     }
1105   }
1106 
1107   // Celestial and spectral transformation parameters.
1108   wcsdst->lonpole = wcssrc->lonpole;
1109   wcsdst->latpole = wcssrc->latpole;
1110   wcsdst->restfrq = wcssrc->restfrq;
1111   wcsdst->restwav = wcssrc->restwav;
1112 
1113   // Parameter values.
1114   npv = 0;
1115   for (m = 0; m < wcssrc->npv; m++) {
1116     i = wcssrc->pv[m].i;
1117     if (i == 0) {
1118       // i == 0 is a special code that means "the latitude axis".
1119       wcsdst->pv[npv] = wcssrc->pv[m];
1120       wcsdst->pv[npv].i = 0;
1121       npv++;
1122     } else if (i > 0 && map[i-1]) {
1123       wcsdst->pv[npv] = wcssrc->pv[m];
1124       wcsdst->pv[npv].i = map[i-1];
1125       npv++;
1126     }
1127   }
1128   wcsdst->npv = npv;
1129 
1130   nps = 0;
1131   for (m = 0; m < wcssrc->nps; m++) {
1132     i = wcssrc->ps[m].i;
1133     if (i > 0 && map[i-1]) {
1134       wcsdst->ps[nps] = wcssrc->ps[m];
1135       wcsdst->ps[nps].i = map[i-1];
1136       nps++;
1137     }
1138   }
1139   wcsdst->nps = nps;
1140 
1141   // Alternate linear transformations.
1142   if (wcssrc->cd) {
1143     srcp = wcssrc->cd;
1144     dstp = wcsdst->cd;
1145     for (i = 0; i < *nsub; i++) {
1146       for (j = 0; j < *nsub; j++, dstp++) {
1147         if (axes[i] > 0 && axes[j] > 0) {
1148           k = (axes[i]-1)*naxis + (axes[j]-1);
1149           *dstp = *(srcp+k);
1150         } else if (i == j && wcssrc->altlin & 2) {
1151           // A new axis is being created where CDi_ja was present in the input
1152           // header, so override the default value of 0 set by wcsinit().
1153           *dstp = 1.0;
1154         }
1155       }
1156     }
1157   }
1158 
1159   if (wcssrc->crota) {
1160     srcp = wcssrc->crota;
1161     dstp = wcsdst->crota;
1162     for (i = 0; i < *nsub; i++, dstp++) {
1163       if (axes[i] > 0) {
1164         k = axes[i] - 1;
1165         *dstp = *(srcp+k);
1166       }
1167     }
1168   }
1169 
1170   wcsdst->altlin = wcssrc->altlin;
1171   wcsdst->velref = wcssrc->velref;
1172 
1173   // Auxiliary coordinate system information.
1174   strncpy(wcsdst->alt, wcssrc->alt, 4);
1175   wcsdst->colnum = wcssrc->colnum;
1176 
1177   for (i = 0; i < *nsub; i++) {
1178     if (axes[i] > 0) {
1179       k = axes[i] - 1;
1180       if (wcssrc->colax) wcsdst->colax[i] = wcssrc->colax[k];
1181       if (wcssrc->cname) strncpy(wcsdst->cname[i], wcssrc->cname[k], 72);
1182       if (wcssrc->crder) wcsdst->crder[i] = wcssrc->crder[k];
1183       if (wcssrc->csyer) wcsdst->csyer[i] = wcssrc->csyer[k];
1184       if (wcssrc->czphs) wcsdst->czphs[i] = wcssrc->czphs[k];
1185       if (wcssrc->cperi) wcsdst->cperi[i] = wcssrc->cperi[k];
1186     }
1187   }
1188 
1189   strncpy(wcsdst->wcsname, wcssrc->wcsname, 72);
1190 
1191   strncpy(wcsdst->timesys, wcssrc->timesys, 72);
1192   strncpy(wcsdst->trefpos, wcssrc->trefpos, 72);
1193   strncpy(wcsdst->trefdir, wcssrc->trefdir, 72);
1194   strncpy(wcsdst->plephem, wcssrc->plephem, 72);
1195 
1196   strncpy(wcsdst->timeunit, wcssrc->timeunit, 72);
1197   strncpy(wcsdst->dateref,  wcssrc->dateref, 72);
1198   wcsdst->mjdref[0] = wcssrc->mjdref[0];
1199   wcsdst->mjdref[1] = wcssrc->mjdref[1];
1200   wcsdst->timeoffs  = wcssrc->timeoffs;
1201 
1202   strncpy(wcsdst->dateobs, wcssrc->dateobs, 72);
1203   strncpy(wcsdst->datebeg, wcssrc->datebeg, 72);
1204   strncpy(wcsdst->dateavg, wcssrc->dateavg, 72);
1205   strncpy(wcsdst->dateend, wcssrc->dateend, 72);
1206 
1207   wcsdst->mjdobs  = wcssrc->mjdobs;
1208   wcsdst->mjdbeg  = wcssrc->mjdbeg;
1209   wcsdst->mjdavg  = wcssrc->mjdavg;
1210   wcsdst->mjdend  = wcssrc->mjdend;
1211   wcsdst->jepoch  = wcssrc->jepoch;
1212   wcsdst->bepoch  = wcssrc->bepoch;
1213   wcsdst->tstart  = wcssrc->tstart;
1214   wcsdst->tstop   = wcssrc->tstop;
1215   wcsdst->xposure = wcssrc->xposure;
1216   wcsdst->telapse = wcssrc->telapse;
1217 
1218   wcsdst->timsyer  = wcssrc->timsyer;
1219   wcsdst->timrder  = wcssrc->timrder;
1220   wcsdst->timedel  = wcssrc->timedel;
1221   wcsdst->timepixr = wcssrc->timepixr;
1222 
1223   wcsdst->obsgeo[0] = wcssrc->obsgeo[0];
1224   wcsdst->obsgeo[1] = wcssrc->obsgeo[1];
1225   wcsdst->obsgeo[2] = wcssrc->obsgeo[2];
1226   wcsdst->obsgeo[3] = wcssrc->obsgeo[3];
1227   wcsdst->obsgeo[4] = wcssrc->obsgeo[4];
1228   wcsdst->obsgeo[5] = wcssrc->obsgeo[5];
1229 
1230   strncpy(wcsdst->obsorbit, wcssrc->obsorbit, 72);
1231   strncpy(wcsdst->radesys,  wcssrc->radesys, 72);
1232   wcsdst->equinox = wcssrc->equinox;
1233   strncpy(wcsdst->specsys,  wcssrc->specsys, 72);
1234   strncpy(wcsdst->ssysobs,  wcssrc->ssysobs, 72);
1235   wcsdst->velosys = wcssrc->velosys;
1236   wcsdst->zsource = wcssrc->zsource;
1237   strncpy(wcsdst->ssyssrc,  wcssrc->ssyssrc, 72);
1238   wcsdst->velangl = wcssrc->velangl;
1239 
1240 
1241   // Additional auxiliary coordinate system information.
1242   if (wcssrc->aux && !wcsdst->aux) {
1243     if ((wcsdst->aux = calloc(1, sizeof(struct auxprm))) == 0x0) {
1244       status = wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
1245       goto cleanup;
1246     }
1247 
1248     wcsdst->m_aux = wcsdst->aux;
1249 
1250     wcsdst->aux->rsun_ref = wcssrc->aux->rsun_ref;
1251     wcsdst->aux->dsun_obs = wcssrc->aux->dsun_obs;
1252     wcsdst->aux->crln_obs = wcssrc->aux->crln_obs;
1253     wcsdst->aux->hgln_obs = wcssrc->aux->hgln_obs;
1254     wcsdst->aux->hglt_obs = wcssrc->aux->hglt_obs;
1255   }
1256 
1257 
1258   // Coordinate lookup tables; only copy what's needed.
1259   wcsdst->ntab = 0;
1260   for (itab = 0; itab < wcssrc->ntab; itab++) {
1261     // Is this table wanted?
1262     for (m = 0; m < wcssrc->tab[itab].M; m++) {
1263       i = wcssrc->tab[itab].map[m];
1264 
1265       if (map[i]) {
1266         wcsdst->ntab++;
1267         break;
1268       }
1269     }
1270   }
1271 
1272   if (wcsdst->ntab) {
1273     // Allocate memory for tabprm structs.
1274     if ((wcsdst->tab = calloc(wcsdst->ntab, sizeof(struct tabprm))) == 0x0) {
1275       wcsdst->ntab = 0;
1276 
1277       status = wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
1278       goto cleanup;
1279     }
1280 
1281     wcsdst->m_tab = wcsdst->tab;
1282   }
1283 
1284   tab = wcsdst->tab;
1285   for (itab = 0; itab < wcssrc->ntab; itab++) {
1286     for (m = 0; m < wcssrc->tab[itab].M; m++) {
1287       i = wcssrc->tab[itab].map[m];
1288 
1289       if (map[i]) {
1290         if ((status = tabcpy(1, wcssrc->tab + itab, tab))) {
1291           wcserr_set(WCS_ERRMSG(wcs_taberr[status]));
1292           goto cleanup;
1293         }
1294 
1295         tab++;
1296         break;
1297       }
1298     }
1299   }
1300 
1301 
1302   // Distortion parameters (in linprm).
1303   for (m = 0; m < 2; m++) {
1304     if (m == 0) {
1305       dissrc = wcssrc->lin.dispre;
1306       disdst = wcsdst->lin.dispre;
1307     } else {
1308       dissrc = wcssrc->lin.disseq;
1309       disdst = wcsdst->lin.disseq;
1310     }
1311 
1312     if (dissrc) {
1313       disdst->naxis = *nsub;
1314 
1315       // Distortion type and maximum distortion (but not total distortion).
1316       for (j = 0; j < *nsub; j++) {
1317         if (axes[j] > 0) {
1318           k = axes[j] - 1;
1319           strncpy(disdst->dtype[j], dissrc->dtype[k], 72);
1320           disdst->maxdis[j] = dissrc->maxdis[k];
1321         }
1322       }
1323 
1324       // DPja or DQia keyvalues.
1325       ndp = 0;
1326       dpdst = disdst->dp;
1327       for (j = 0; j < *nsub; j++) {
1328         if (axes[j] == 0) continue;
1329 
1330         // Determine the axis mapping.
1331         for (jhat = 0; jhat < 32; jhat++) {
1332           axmap[jhat] = -1;
1333         }
1334 
1335         Nhat = 0;
1336         dpsrc = dissrc->dp;
1337         for (idp = 0; idp < dissrc->ndp; idp++, dpsrc++) {
1338           if (dpsrc->j != axes[j]) continue;
1339           if (dpsrc->field[1] != pq[m]) continue;
1340           if ((fp = strchr(dpsrc->field, '.')) == 0x0) continue;
1341           fp++;
1342 
1343           if (strncmp(fp, "NAXES", 6) == 0) {
1344             Nhat = dpkeyi(dpsrc);
1345           } else if (strncmp(fp, "AXIS.", 5) == 0) {
1346             sscanf(fp+5, "%d", &jhat);
1347             axmap[jhat-1] = dpkeyi(dpsrc) - 1;
1348           }
1349         }
1350 
1351         for (jhat = 0; jhat < Nhat; jhat++) {
1352           if (axmap[jhat] < 0) {
1353             axmap[jhat] = jhat;
1354           }
1355         }
1356 
1357         // Copy the DPja or DQia keyvalues.
1358         dpsrc = dissrc->dp;
1359         for (idp = 0; idp < dissrc->ndp; idp++, dpsrc++) {
1360           if (dpsrc->j != axes[j]) continue;
1361           if (dpsrc->field[1] != pq[m]) continue;
1362           if ((fp = strchr(dpsrc->field, '.')) == 0x0) continue;
1363           fp++;
1364 
1365           if (strncmp(fp, "AXIS.", 5) == 0) {
1366             // Skip it, we will create our own later.
1367             continue;
1368           }
1369 
1370           *dpdst = *dpsrc;
1371           sprintf(ctmp, "%d", j+1);
1372           dpdst->field[2] = ctmp[0];
1373           dpdst->j = j+1;
1374 
1375           ndp++;
1376           dpdst++;
1377 
1378           if (strncmp(fp, "NAXES", 6) == 0) {
1379             for (jhat = 0; jhat < Nhat; jhat++) {
1380               strcpy(dpdst->field, dpsrc->field);
1381               dpdst->field[2] = ctmp[0];
1382               fp = strchr(dpdst->field, '.') + 1;
1383               sprintf(fp, "AXIS.%d", jhat+1);
1384               dpdst->j = j+1;
1385               dpdst->type = 0;
1386               dpdst->value.i = map[axmap[jhat]];
1387 
1388               ndp++;
1389               dpdst++;
1390             }
1391           }
1392         }
1393       }
1394 
1395       disdst->ndp = ndp;
1396     }
1397   }
1398 
1399 
1400 cleanup:
1401   if (itmp) free(itmp);
1402   if (dealloc) {
1403     free(axes);
1404   }
1405 
1406   if (status && wcsdst->m_aux) {
1407     free(wcsdst->m_aux);
1408     wcsdst->aux   = 0x0;
1409     wcsdst->m_aux = 0x0;
1410   }
1411 
1412   if (status && wcsdst->m_tab) {
1413     free(wcsdst->m_tab);
1414     wcsdst->tab   = 0x0;
1415     wcsdst->m_tab = 0x0;
1416   }
1417 
1418   return status;
1419 }
1420 
1421 //----------------------------------------------------------------------------
1422 
wcscompare(int cmp,double tol,const struct wcsprm * wcs1,const struct wcsprm * wcs2,int * equal)1423 int wcscompare(
1424   int cmp,
1425   double tol,
1426   const struct wcsprm *wcs1,
1427   const struct wcsprm *wcs2,
1428   int *equal)
1429 
1430 {
1431   int i, j, naxis, naxis2;
1432   double diff;
1433   int tab_equal;
1434   int status;
1435 
1436   if (wcs1  == 0x0) return WCSERR_NULL_POINTER;
1437   if (wcs2  == 0x0) return WCSERR_NULL_POINTER;
1438   if (equal == 0x0) return WCSERR_NULL_POINTER;
1439 
1440   *equal = 0;
1441 
1442   if (wcs1->naxis != wcs2->naxis) {
1443     return 0;
1444   }
1445 
1446   naxis = wcs1->naxis;
1447   naxis2 = wcs1->naxis*wcs1->naxis;
1448 
1449   if (cmp & WCSCOMPARE_CRPIX) {
1450     // Don't compare crpix.
1451   } else if (cmp & WCSCOMPARE_TILING) {
1452     for (i = 0; i < naxis; ++i) {
1453       diff = wcs1->crpix[i] - wcs2->crpix[i];
1454       if ((double)(int)(diff) != diff) {
1455         return 0;
1456       }
1457     }
1458   } else {
1459     if (!wcsutil_dblEq(naxis, tol, wcs1->crpix, wcs2->crpix)) {
1460       return 0;
1461     }
1462   }
1463 
1464   if (!wcsutil_dblEq(naxis2, tol, wcs1->pc, wcs2->pc) ||
1465       !wcsutil_dblEq(naxis, tol, wcs1->cdelt, wcs2->cdelt) ||
1466       !wcsutil_dblEq(naxis, tol, wcs1->crval, wcs2->crval) ||
1467       !wcsutil_strEq(naxis, wcs1->cunit, wcs2->cunit) ||
1468       !wcsutil_strEq(naxis, wcs1->ctype, wcs2->ctype) ||
1469       !wcsutil_dblEq(1, tol, &wcs1->lonpole, &wcs2->lonpole) ||
1470       !wcsutil_dblEq(1, tol, &wcs1->latpole, &wcs2->latpole) ||
1471       !wcsutil_dblEq(1, tol, &wcs1->restfrq, &wcs2->restfrq) ||
1472       !wcsutil_dblEq(1, tol, &wcs1->restwav, &wcs2->restwav) ||
1473       wcs1->npv != wcs2->npv ||
1474       wcs1->nps != wcs2->nps) {
1475     return 0;
1476   }
1477 
1478   // Compare pv cards, which may not be in the same order
1479   for (i = 0; i < wcs1->npv; ++i) {
1480     for (j = 0; j < wcs2->npv; ++j) {
1481       if (wcs1->pv[i].i == wcs2->pv[j].i &&
1482           wcs1->pv[i].m == wcs2->pv[j].m) {
1483         if (!wcsutil_dblEq(1, tol, &wcs1->pv[i].value, &wcs2->pv[j].value)) {
1484           return 0;
1485         }
1486         break;
1487       }
1488     }
1489     // We didn't find a match, so they are not equal
1490     if (j == wcs2->npv) {
1491       return 0;
1492     }
1493   }
1494 
1495   // Compare ps cards, which may not be in the same order
1496   for (i = 0; i < wcs1->nps; ++i) {
1497     for (j = 0; j < wcs2->nps; ++j) {
1498       if (wcs1->ps[i].i == wcs2->ps[j].i &&
1499           wcs1->ps[i].m == wcs2->ps[j].m) {
1500         if (strncmp(wcs1->ps[i].value, wcs2->ps[j].value, 72)) {
1501           return 0;
1502         }
1503         break;
1504       }
1505     }
1506     // We didn't find a match, so they are not equal
1507     if (j == wcs2->nps) {
1508       return 0;
1509     }
1510   }
1511 
1512   if (wcs1->flag != WCSSET || wcs2->flag != WCSSET) {
1513     if (!wcsutil_dblEq(naxis2, tol, wcs1->cd, wcs2->cd) ||
1514         !wcsutil_dblEq(naxis, tol, wcs1->crota, wcs2->crota) ||
1515         wcs1->altlin != wcs2->altlin ||
1516         wcs1->velref != wcs2->velref) {
1517       return 0;
1518     }
1519   }
1520 
1521   if (!(cmp & WCSCOMPARE_ANCILLARY)) {
1522     if (strncmp(wcs1->alt, wcs2->alt, 4) ||
1523         wcs1->colnum != wcs2->colnum ||
1524         !wcsutil_intEq(naxis, wcs1->colax, wcs2->colax) ||
1525         !wcsutil_strEq(naxis, wcs1->cname, wcs2->cname) ||
1526         !wcsutil_dblEq(naxis, tol, wcs1->crder, wcs2->crder) ||
1527         !wcsutil_dblEq(naxis, tol, wcs1->csyer, wcs2->csyer) ||
1528         !wcsutil_dblEq(naxis, tol, wcs1->czphs, wcs2->czphs) ||
1529         !wcsutil_dblEq(naxis, tol, wcs1->cperi, wcs2->cperi) ||
1530         strncmp(wcs1->wcsname,  wcs2->wcsname,  72) ||
1531         strncmp(wcs1->timesys,  wcs2->timesys,  72) ||
1532         strncmp(wcs1->trefpos,  wcs2->trefpos,  72) ||
1533         strncmp(wcs1->trefdir,  wcs2->trefdir,  72) ||
1534         strncmp(wcs1->plephem,  wcs2->plephem,  72) ||
1535         strncmp(wcs1->timeunit, wcs2->timeunit, 72) ||
1536         strncmp(wcs1->dateref,  wcs2->dateref,  72) ||
1537         !wcsutil_dblEq(2, tol,  wcs1->mjdref,    wcs2->mjdref)   ||
1538         !wcsutil_dblEq(1, tol, &wcs1->timeoffs, &wcs2->timeoffs) ||
1539         strncmp(wcs1->dateobs,  wcs2->dateobs, 72) ||
1540         strncmp(wcs1->datebeg,  wcs2->datebeg, 72) ||
1541         strncmp(wcs1->dateavg,  wcs2->dateavg, 72) ||
1542         strncmp(wcs1->dateend,  wcs2->dateend, 72) ||
1543         !wcsutil_dblEq(1, tol, &wcs1->mjdobs,   &wcs2->mjdobs)   ||
1544         !wcsutil_dblEq(1, tol, &wcs1->mjdbeg,   &wcs2->mjdbeg)   ||
1545         !wcsutil_dblEq(1, tol, &wcs1->mjdavg,   &wcs2->mjdavg)   ||
1546         !wcsutil_dblEq(1, tol, &wcs1->mjdend,   &wcs2->mjdend)   ||
1547         !wcsutil_dblEq(1, tol, &wcs1->jepoch,   &wcs2->jepoch)   ||
1548         !wcsutil_dblEq(1, tol, &wcs1->bepoch,   &wcs2->bepoch)   ||
1549         !wcsutil_dblEq(1, tol, &wcs1->tstart,   &wcs2->tstart)   ||
1550         !wcsutil_dblEq(1, tol, &wcs1->tstop,    &wcs2->tstop)    ||
1551         !wcsutil_dblEq(1, tol, &wcs1->xposure,  &wcs2->xposure)  ||
1552         !wcsutil_dblEq(1, tol, &wcs1->telapse,  &wcs2->telapse)  ||
1553         !wcsutil_dblEq(1, tol, &wcs1->timsyer,  &wcs2->timsyer)  ||
1554         !wcsutil_dblEq(1, tol, &wcs1->timrder,  &wcs2->timrder)  ||
1555         !wcsutil_dblEq(1, tol, &wcs1->timedel,  &wcs2->timedel)  ||
1556         !wcsutil_dblEq(1, tol, &wcs1->timepixr, &wcs2->timepixr) ||
1557         !wcsutil_dblEq(6, tol,  wcs1->obsgeo,    wcs2->obsgeo)   ||
1558         strncmp(wcs1->obsorbit, wcs2->obsorbit, 72) ||
1559         strncmp(wcs1->radesys,  wcs2->radesys,  72) ||
1560         !wcsutil_dblEq(1, tol, &wcs1->equinox,  &wcs2->equinox)  ||
1561         strncmp(wcs1->specsys,  wcs2->specsys,  72) ||
1562         strncmp(wcs1->ssysobs,  wcs2->ssysobs,  72) ||
1563         !wcsutil_dblEq(1, tol, &wcs1->velosys,  &wcs2->velosys)  ||
1564         !wcsutil_dblEq(1, tol, &wcs1->zsource,  &wcs2->zsource)  ||
1565         strncmp(wcs1->ssyssrc,  wcs2->ssyssrc,  72) ||
1566         !wcsutil_dblEq(1, tol, &wcs1->velangl,  &wcs2->velangl)) {
1567       return 0;
1568     }
1569 
1570     // Compare additional auxiliary parameters.
1571     if (wcs1->aux && wcs2->aux) {
1572       if (!wcsutil_dblEq(1, tol, &wcs1->aux->rsun_ref, &wcs2->aux->rsun_ref) ||
1573           !wcsutil_dblEq(1, tol, &wcs1->aux->dsun_obs, &wcs2->aux->dsun_obs) ||
1574           !wcsutil_dblEq(1, tol, &wcs1->aux->crln_obs, &wcs2->aux->crln_obs) ||
1575           !wcsutil_dblEq(1, tol, &wcs1->aux->hgln_obs, &wcs2->aux->hgln_obs) ||
1576           !wcsutil_dblEq(1, tol, &wcs1->aux->hglt_obs, &wcs2->aux->hglt_obs)) {
1577         return 0;
1578       }
1579     } else if (wcs1->aux || wcs2->aux) {
1580       return 0;
1581     }
1582   }
1583 
1584   // Compare tabular parameters
1585   if (wcs1->ntab != wcs2->ntab) {
1586     return 0;
1587   }
1588 
1589   for (i = 0; i < wcs1->ntab; ++i) {
1590     if ((status = tabcmp(0, tol, &wcs1->tab[i], &wcs2->tab[i], &tab_equal))) {
1591       return status;
1592     }
1593     if (!tab_equal) {
1594       return 0;
1595     }
1596   }
1597 
1598   *equal = 1;
1599   return 0;
1600 }
1601 
1602 //----------------------------------------------------------------------------
1603 
wcsfree(struct wcsprm * wcs)1604 int wcsfree(struct wcsprm *wcs)
1605 
1606 {
1607   if (wcs == 0x0) return WCSERR_NULL_POINTER;
1608 
1609   if (wcs->flag == -1) {
1610     wcs->lin.flag = -1;
1611 
1612   } else {
1613     // Optionally allocated by wcsinit() for given parameters.
1614     if (wcs->m_flag == WCSSET) {
1615       // Start by cleaning the slate.
1616       if (wcs->crpix == wcs->m_crpix) wcs->crpix = 0x0;
1617       if (wcs->pc    == wcs->m_pc)    wcs->pc    = 0x0;
1618       if (wcs->cdelt == wcs->m_cdelt) wcs->cdelt = 0x0;
1619       if (wcs->crval == wcs->m_crval) wcs->crval = 0x0;
1620       if (wcs->cunit == wcs->m_cunit) wcs->cunit = 0x0;
1621       if (wcs->ctype == wcs->m_ctype) wcs->ctype = 0x0;
1622       if (wcs->pv    == wcs->m_pv)    wcs->pv    = 0x0;
1623       if (wcs->ps    == wcs->m_ps)    wcs->ps    = 0x0;
1624       if (wcs->cd    == wcs->m_cd)    wcs->cd    = 0x0;
1625       if (wcs->crota == wcs->m_crota) wcs->crota = 0x0;
1626       if (wcs->colax == wcs->m_colax) wcs->colax = 0x0;
1627       if (wcs->cname == wcs->m_cname) wcs->cname = 0x0;
1628       if (wcs->crder == wcs->m_crder) wcs->crder = 0x0;
1629       if (wcs->csyer == wcs->m_csyer) wcs->csyer = 0x0;
1630       if (wcs->czphs == wcs->m_czphs) wcs->czphs = 0x0;
1631       if (wcs->cperi == wcs->m_cperi) wcs->cperi = 0x0;
1632 
1633       if (wcs->aux   == wcs->m_aux)   wcs->aux   = 0x0;
1634       if (wcs->tab   == wcs->m_tab)   wcs->tab   = 0x0;
1635       if (wcs->wtb   == wcs->m_wtb)   wcs->wtb   = 0x0;
1636 
1637       // Now release the memory.
1638       if (wcs->m_crpix)  free(wcs->m_crpix);
1639       if (wcs->m_pc)     free(wcs->m_pc);
1640       if (wcs->m_cdelt)  free(wcs->m_cdelt);
1641       if (wcs->m_crval)  free(wcs->m_crval);
1642       if (wcs->m_cunit)  free(wcs->m_cunit);
1643       if (wcs->m_ctype)  free(wcs->m_ctype);
1644       if (wcs->m_pv)     free(wcs->m_pv);
1645       if (wcs->m_ps)     free(wcs->m_ps);
1646       if (wcs->m_cd)     free(wcs->m_cd);
1647       if (wcs->m_crota)  free(wcs->m_crota);
1648       if (wcs->m_colax)  free(wcs->m_colax);
1649       if (wcs->m_cname)  free(wcs->m_cname);
1650       if (wcs->m_crder)  free(wcs->m_crder);
1651       if (wcs->m_csyer)  free(wcs->m_csyer);
1652       if (wcs->m_czphs)  free(wcs->m_czphs);
1653       if (wcs->m_cperi)  free(wcs->m_cperi);
1654 
1655       // May have been allocated by wcspih() or wcssub().
1656       if (wcs->m_aux) free(wcs->m_aux);
1657 
1658       // Allocated unconditionally by wcstab().
1659       if (wcs->m_tab) {
1660         for (int itab = 0; itab < wcs->ntab; itab++) {
1661           tabfree(wcs->m_tab + itab);
1662         }
1663 
1664         free(wcs->m_tab);
1665       }
1666       if (wcs->m_wtb) free(wcs->m_wtb);
1667     }
1668 
1669     // Allocated unconditionally by wcsset().
1670     if (wcs->types) free(wcs->types);
1671 
1672     if (wcs->lin.crpix == wcs->m_crpix) wcs->lin.crpix = 0x0;
1673     if (wcs->lin.pc    == wcs->m_pc)    wcs->lin.pc    = 0x0;
1674     if (wcs->lin.cdelt == wcs->m_cdelt) wcs->lin.cdelt = 0x0;
1675   }
1676 
1677   wcs->m_flag   = 0;
1678   wcs->m_naxis  = 0x0;
1679   wcs->m_crpix  = 0x0;
1680   wcs->m_pc     = 0x0;
1681   wcs->m_cdelt  = 0x0;
1682   wcs->m_crval  = 0x0;
1683   wcs->m_cunit  = 0x0;
1684   wcs->m_ctype  = 0x0;
1685   wcs->m_pv     = 0x0;
1686   wcs->m_ps     = 0x0;
1687   wcs->m_cd     = 0x0;
1688   wcs->m_crota  = 0x0;
1689   wcs->m_colax  = 0x0;
1690   wcs->m_cname  = 0x0;
1691   wcs->m_crder  = 0x0;
1692   wcs->m_csyer  = 0x0;
1693   wcs->m_czphs  = 0x0;
1694   wcs->m_cperi  = 0x0;
1695 
1696   wcs->m_aux    = 0x0;
1697 
1698   wcs->ntab  = 0;
1699   wcs->m_tab = 0x0;
1700   wcs->nwtb  = 0;
1701   wcs->m_wtb = 0x0;
1702 
1703   wcs->types = 0x0;
1704 
1705   wcserr_clear(&(wcs->err));
1706 
1707   wcs->flag = 0;
1708 
1709   linfree(&(wcs->lin));
1710   celfree(&(wcs->cel));
1711   spcfree(&(wcs->spc));
1712 
1713   return WCSERR_SUCCESS;
1714 }
1715 
1716 //----------------------------------------------------------------------------
1717 
wcstrim(struct wcsprm * wcs)1718 int wcstrim(struct wcsprm *wcs)
1719 
1720 {
1721   if (wcs == 0x0) return WCSERR_NULL_POINTER;
1722 
1723   if (wcs->m_flag != WCSSET) {
1724     // Nothing to do.
1725     return WCSERR_SUCCESS;
1726   }
1727 
1728   if (wcs->flag != WCSSET) {
1729     return WCSERR_UNSET;
1730   }
1731 
1732   if (wcs->npv < wcs->npvmax) {
1733     if (wcs->m_pv) {
1734       if (wcs->npv == 0) {
1735         free(wcs->m_pv);
1736         wcs->pv = wcs->m_pv = 0x0;
1737       } else {
1738         size_t size = wcs->npv * sizeof(struct pvcard);
1739         // No error if realloc() fails, it will leave the array untouched.
1740         if ((wcs->pv = wcs->m_pv = realloc(wcs->m_pv, size))) {
1741           wcs->npvmax = wcs->npv;
1742         }
1743       }
1744     }
1745   }
1746 
1747   if (wcs->nps < wcs->npsmax) {
1748     if (wcs->m_ps) {
1749       if (wcs->nps == 0) {
1750         free(wcs->m_ps);
1751         wcs->ps = wcs->m_ps = 0x0;
1752       } else {
1753         size_t size = wcs->nps * sizeof(struct pscard);
1754         // No error if realloc() fails, it will leave the array untouched.
1755         if ((wcs->ps = wcs->m_ps = realloc(wcs->m_ps, size))) {
1756           wcs->npsmax = wcs->nps;
1757         }
1758       }
1759     }
1760   }
1761 
1762   if (!(wcs->altlin & 2)) {
1763     if (wcs->m_cd) {
1764       free(wcs->m_cd);
1765       wcs->cd = wcs->m_cd = 0x0;
1766     }
1767   }
1768 
1769   if (!(wcs->altlin & 4)) {
1770     if (wcs->m_crota) {
1771       free(wcs->m_crota);
1772       wcs->crota = wcs->m_crota = 0x0;
1773     }
1774   }
1775 
1776   if (wcs->colax) {
1777     if (wcsutil_all_ival(wcs->naxis, 0, wcs->colax)) {
1778       free(wcs->m_colax);
1779       wcs->colax = wcs->m_colax = 0x0;
1780     }
1781   }
1782 
1783   if (wcs->cname) {
1784     if (wcsutil_all_sval(wcs->naxis, "", (const char (*)[72])wcs->cname)) {
1785       free(wcs->m_cname);
1786       wcs->cname = wcs->m_cname = 0x0;
1787     }
1788   }
1789 
1790   if (wcs->crder) {
1791     if (wcsutil_all_dval(wcs->naxis, UNDEFINED, wcs->crder)) {
1792       free(wcs->m_crder);
1793       wcs->crder = wcs->m_crder = 0x0;
1794     }
1795   }
1796 
1797   if (wcs->csyer) {
1798     if (wcsutil_all_dval(wcs->naxis, UNDEFINED, wcs->csyer)) {
1799       free(wcs->m_csyer);
1800       wcs->csyer = wcs->m_csyer = 0x0;
1801     }
1802   }
1803 
1804   if (wcs->czphs) {
1805     if (wcsutil_all_dval(wcs->naxis, UNDEFINED, wcs->czphs)) {
1806       free(wcs->m_czphs);
1807       wcs->czphs = wcs->m_czphs = 0x0;
1808     }
1809   }
1810 
1811   if (wcs->cperi) {
1812     if (wcsutil_all_dval(wcs->naxis, UNDEFINED, wcs->cperi)) {
1813       free(wcs->m_cperi);
1814       wcs->cperi = wcs->m_cperi = 0x0;
1815     }
1816   }
1817 
1818   return WCSERR_SUCCESS;
1819 }
1820 
1821 //----------------------------------------------------------------------------
1822 
wcssize(const struct wcsprm * wcs,int sizes[2])1823 int wcssize(const struct wcsprm *wcs, int sizes[2])
1824 
1825 {
1826   if (wcs == 0x0) {
1827     sizes[0] = sizes[1] = 0;
1828     return WCSERR_SUCCESS;
1829   }
1830 
1831   // Base size, in bytes.
1832   sizes[0] = sizeof(struct wcsprm);
1833 
1834   // Total size of allocated memory, in bytes.
1835   sizes[1] = 0;
1836 
1837   int exsizes[2];
1838   int naxis = wcs->naxis;
1839 
1840   // wcsprm::crpix[].
1841   sizes[1] += naxis * sizeof(double);
1842 
1843   // wcsprm::pc[].
1844   sizes[1] += naxis*naxis * sizeof(double);
1845 
1846   // wcsprm::cdelt[].
1847   sizes[1] += naxis * sizeof(double);
1848 
1849   // wcsprm::crval[].
1850   sizes[1] += naxis * sizeof(double);
1851 
1852   // wcsprm::cunit[].
1853   if (wcs->cunit) {
1854     sizes[1] += naxis * sizeof(char [72]);
1855   }
1856 
1857   // wcsprm::ctype[].
1858   sizes[1] += naxis * sizeof(char [72]);
1859 
1860   // wcsprm::pv[].
1861   if (wcs->pv) {
1862     sizes[1] += wcs->npvmax * sizeof(struct pvcard);
1863   }
1864 
1865   // wcsprm::ps[].
1866   if (wcs->ps) {
1867     sizes[1] += wcs->npsmax * sizeof(struct pscard);
1868   }
1869 
1870   // wcsprm::cd[].
1871   if (wcs->cd) {
1872     sizes[1] += naxis*naxis * sizeof(double);
1873   }
1874 
1875   // wcsprm::crota[].
1876   if (wcs->crota) {
1877     sizes[1] += naxis * sizeof(double);
1878   }
1879 
1880   // wcsprm::colax[].
1881   if (wcs->colax) {
1882     sizes[1] += naxis * sizeof(int);
1883   }
1884 
1885   // wcsprm::cname[].
1886   if (wcs->cname) {
1887     sizes[1] += naxis * sizeof(char [72]);
1888   }
1889 
1890   // wcsprm::crder[].
1891   if (wcs->crder) {
1892     sizes[1] += naxis * sizeof(double);
1893   }
1894 
1895   // wcsprm::csyer[].
1896   if (wcs->csyer) {
1897     sizes[1] += naxis * sizeof(double);
1898   }
1899 
1900   // wcsprm::czphs[].
1901   if (wcs->czphs) {
1902     sizes[1] += naxis * sizeof(double);
1903   }
1904 
1905   // wcsprm::cperi[].
1906   if (wcs->cperi) {
1907     sizes[1] += naxis * sizeof(double);
1908   }
1909 
1910   // wcsprm::aux.
1911   if (wcs->aux) {
1912     sizes[1] += sizeof(struct auxprm);
1913   }
1914 
1915   // wcsprm::tab.
1916   for (int itab = 0; itab < wcs->ntab; itab++) {
1917     tabsize(wcs->tab + itab, exsizes);
1918     sizes[1] += exsizes[0] + exsizes[1];
1919   }
1920 
1921   // wcsprm::wtb.
1922   if (wcs->wtb) {
1923     sizes[1] += wcs->nwtb * sizeof(struct wtbarr);
1924   }
1925 
1926   // wcsprm::lin.
1927   linsize(&(wcs->lin), exsizes);
1928   sizes[1] += exsizes[1];
1929 
1930   // wcsprm::err.
1931   wcserr_size(wcs->err, exsizes);
1932   sizes[1] += exsizes[0] + exsizes[1];
1933 
1934   return WCSERR_SUCCESS;
1935 }
1936 
1937 //----------------------------------------------------------------------------
1938 
auxsize(const struct auxprm * aux,int sizes[2])1939 int auxsize(const struct auxprm *aux, int sizes[2])
1940 
1941 {
1942   if (aux == 0x0) {
1943     sizes[0] = sizes[1] = 0;
1944     return WCSERR_SUCCESS;
1945   }
1946 
1947   // Base size, in bytes.
1948   sizes[0] = sizeof(struct auxprm);
1949 
1950   // Total size of allocated memory, in bytes.
1951   sizes[1] = 0;
1952 
1953   return WCSERR_SUCCESS;
1954 }
1955 
1956 
1957 //----------------------------------------------------------------------------
1958 
wcsprt_auxc(const char * name,const char * value)1959 static void wcsprt_auxc(const char *name, const char *value)
1960 {
1961   if (value[0] == '\0') {
1962     wcsprintf("   %s: UNDEFINED\n", name);
1963   } else {
1964     wcsprintf("   %s: \"%s\"\n", name, value);
1965   }
1966 }
1967 
wcsprt_auxd(const char * name,double value)1968 static void wcsprt_auxd(const char *name, double value)
1969 {
1970   if (undefined(value)) {
1971     wcsprintf("   %s: UNDEFINED\n", name);
1972   } else {
1973     wcsprintf("   %s:  %15.9f\n", name, value);
1974   }
1975 }
1976 
wcsprt(const struct wcsprm * wcs)1977 int wcsprt(const struct wcsprm *wcs)
1978 
1979 {
1980   if (wcs == 0x0) return WCSERR_NULL_POINTER;
1981 
1982   if (wcs->flag != WCSSET) {
1983     wcsprintf("The wcsprm struct is UNINITIALIZED.\n");
1984     return WCSERR_SUCCESS;
1985   }
1986 
1987   wcsprintf("       flag: %d\n", wcs->flag);
1988   wcsprintf("      naxis: %d\n", wcs->naxis);
1989   WCSPRINTF_PTR("      crpix: ", wcs->crpix, "\n");
1990   wcsprintf("            ");
1991   for (int i = 0; i < wcs->naxis; i++) {
1992     wcsprintf("  %#- 11.5g", wcs->crpix[i]);
1993   }
1994   wcsprintf("\n");
1995 
1996   // Linear transformation.
1997   int k = 0;
1998   WCSPRINTF_PTR("         pc: ", wcs->pc, "\n");
1999   for (int i = 0; i < wcs->naxis; i++) {
2000     wcsprintf("    pc[%d][]:", i);
2001     for (int j = 0; j < wcs->naxis; j++) {
2002       wcsprintf("  %#- 11.5g", wcs->pc[k++]);
2003     }
2004     wcsprintf("\n");
2005   }
2006 
2007   // Coordinate increment at reference point.
2008   WCSPRINTF_PTR("      cdelt: ", wcs->cdelt, "\n");
2009   wcsprintf("            ");
2010   for (int i = 0; i < wcs->naxis; i++) {
2011     wcsprintf("  %#- 11.5g", wcs->cdelt[i]);
2012   }
2013   wcsprintf("\n");
2014 
2015   // Coordinate value at reference point.
2016   WCSPRINTF_PTR("      crval: ", wcs->crval, "\n");
2017   wcsprintf("            ");
2018   for (int i = 0; i < wcs->naxis; i++) {
2019     wcsprintf("  %#- 11.5g", wcs->crval[i]);
2020   }
2021   wcsprintf("\n");
2022 
2023   // Coordinate units and type.
2024   WCSPRINTF_PTR("      cunit: ", wcs->cunit, "\n");
2025   for (int i = 0; i < wcs->naxis; i++) {
2026     wcsprintf("             \"%s\"\n", wcs->cunit[i]);
2027   }
2028 
2029   WCSPRINTF_PTR("      ctype: ", wcs->ctype, "\n");
2030   for (int i = 0; i < wcs->naxis; i++) {
2031     wcsprintf("             \"%s\"\n", wcs->ctype[i]);
2032   }
2033 
2034   // Celestial and spectral transformation parameters.
2035   if (undefined(wcs->lonpole)) {
2036     wcsprintf("    lonpole: UNDEFINED\n");
2037   } else {
2038     wcsprintf("    lonpole: %9f\n", wcs->lonpole);
2039   }
2040   wcsprintf("    latpole: %9f\n", wcs->latpole);
2041   wcsprintf("    restfrq: %f\n", wcs->restfrq);
2042   wcsprintf("    restwav: %f\n", wcs->restwav);
2043 
2044   // Parameter values.
2045   wcsprintf("        npv: %d\n", wcs->npv);
2046   wcsprintf("     npvmax: %d\n", wcs->npvmax);
2047   WCSPRINTF_PTR("         pv: ", wcs->pv, "\n");
2048   for (int k = 0; k < wcs->npv; k++) {
2049     wcsprintf("             %3d%4d  %#- 11.5g\n", (wcs->pv[k]).i,
2050       (wcs->pv[k]).m, (wcs->pv[k]).value);
2051   }
2052   wcsprintf("        nps: %d\n", wcs->nps);
2053   wcsprintf("     npsmax: %d\n", wcs->npsmax);
2054   WCSPRINTF_PTR("         ps: ", wcs->ps, "\n");
2055   for (int k = 0; k < wcs->nps; k++) {
2056     wcsprintf("             %3d%4d  %s\n", (wcs->ps[k]).i,
2057       (wcs->ps[k]).m, (wcs->ps[k]).value);
2058   }
2059 
2060   // Alternate linear transformations.
2061   k = 0;
2062   WCSPRINTF_PTR("         cd: ", wcs->cd, "\n");
2063   if (wcs->cd) {
2064     for (int i = 0; i < wcs->naxis; i++) {
2065       wcsprintf("    cd[%d][]:", i);
2066       for (int j = 0; j < wcs->naxis; j++) {
2067         wcsprintf("  %#- 11.5g", wcs->cd[k++]);
2068       }
2069       wcsprintf("\n");
2070     }
2071   }
2072 
2073   WCSPRINTF_PTR("      crota: ", wcs->crota, "\n");
2074   if (wcs->crota) {
2075     wcsprintf("            ");
2076     for (int i = 0; i < wcs->naxis; i++) {
2077       wcsprintf("  %#- 11.5g", wcs->crota[i]);
2078     }
2079     wcsprintf("\n");
2080   }
2081 
2082   wcsprintf("     altlin: %d\n", wcs->altlin);
2083   wcsprintf("     velref: %d\n", wcs->velref);
2084 
2085 
2086 
2087   // Auxiliary coordinate system information.
2088   wcsprintf("        alt: '%c'\n", wcs->alt[0]);
2089   wcsprintf("     colnum: %d\n", wcs->colnum);
2090 
2091   WCSPRINTF_PTR("      colax: ", wcs->colax, "\n");
2092   if (wcs->colax) {
2093     wcsprintf("           ");
2094     for (int i = 0; i < wcs->naxis; i++) {
2095       wcsprintf("  %5d", wcs->colax[i]);
2096     }
2097     wcsprintf("\n");
2098   }
2099 
2100   WCSPRINTF_PTR("      cname: ", wcs->cname, "\n");
2101   if (wcs->cname) {
2102     for (int i = 0; i < wcs->naxis; i++) {
2103       if (wcs->cname[i][0] == '\0') {
2104         wcsprintf("             UNDEFINED\n");
2105       } else {
2106         wcsprintf("             \"%s\"\n", wcs->cname[i]);
2107       }
2108     }
2109   }
2110 
2111   WCSPRINTF_PTR("      crder: ", wcs->crder, "\n");
2112   if (wcs->crder) {
2113     wcsprintf("           ");
2114     for (int i = 0; i < wcs->naxis; i++) {
2115       if (undefined(wcs->crder[i])) {
2116         wcsprintf("    UNDEFINED");
2117       } else {
2118         wcsprintf("  %#- 11.5g", wcs->crder[i]);
2119       }
2120     }
2121     wcsprintf("\n");
2122   }
2123 
2124   WCSPRINTF_PTR("      csyer: ", wcs->csyer, "\n");
2125   if (wcs->csyer) {
2126     wcsprintf("           ");
2127     for (int i = 0; i < wcs->naxis; i++) {
2128       if (undefined(wcs->csyer[i])) {
2129         wcsprintf("    UNDEFINED");
2130       } else {
2131         wcsprintf("  %#- 11.5g", wcs->csyer[i]);
2132       }
2133     }
2134     wcsprintf("\n");
2135   }
2136 
2137   WCSPRINTF_PTR("      czphs: ", wcs->czphs, "\n");
2138   if (wcs->czphs) {
2139     wcsprintf("           ");
2140     for (int i = 0; i < wcs->naxis; i++) {
2141       if (undefined(wcs->czphs[i])) {
2142         wcsprintf("    UNDEFINED");
2143       } else {
2144         wcsprintf("  %#- 11.5g", wcs->czphs[i]);
2145       }
2146     }
2147     wcsprintf("\n");
2148   }
2149 
2150   WCSPRINTF_PTR("      cperi: ", wcs->cperi, "\n");
2151   if (wcs->cperi) {
2152     wcsprintf("           ");
2153     for (int i = 0; i < wcs->naxis; i++) {
2154       if (undefined(wcs->cperi[i])) {
2155         wcsprintf("    UNDEFINED");
2156       } else {
2157         wcsprintf("  %#- 11.5g", wcs->cperi[i]);
2158       }
2159     }
2160     wcsprintf("\n");
2161   }
2162 
2163   wcsprt_auxc(" wcsname", wcs->wcsname);
2164 
2165   wcsprt_auxc(" timesys", wcs->timesys);
2166   wcsprt_auxc(" trefpos", wcs->trefpos);
2167   wcsprt_auxc(" trefdir", wcs->trefdir);
2168   wcsprt_auxc(" plephem", wcs->plephem);
2169   wcsprt_auxc("timeunit", wcs->timeunit);
2170   wcsprt_auxc(" dateref", wcs->dateref);
2171   wcsprintf("     mjdref: ");
2172   for (int k = 0; k < 2; k++) {
2173     if (undefined(wcs->mjdref[k])) {
2174       wcsprintf("       UNDEFINED");
2175     } else {
2176       wcsprintf(" %15.9f", wcs->mjdref[k]);
2177     }
2178   }
2179   wcsprintf("\n");
2180   wcsprt_auxd("timeoffs", wcs->timeoffs);
2181 
2182   wcsprt_auxc(" dateobs", wcs->dateobs);
2183   wcsprt_auxc(" datebeg", wcs->datebeg);
2184   wcsprt_auxc(" dateavg", wcs->dateavg);
2185   wcsprt_auxc(" dateend", wcs->dateend);
2186   wcsprt_auxd("  mjdobs", wcs->mjdobs);
2187   wcsprt_auxd("  mjdbeg", wcs->mjdbeg);
2188   wcsprt_auxd("  mjdavg", wcs->mjdavg);
2189   wcsprt_auxd("  mjdend", wcs->mjdend);
2190   wcsprt_auxd("  jepoch", wcs->jepoch);
2191   wcsprt_auxd("  bepoch", wcs->bepoch);
2192   wcsprt_auxd("  tstart", wcs->tstart);
2193   wcsprt_auxd("   tstop", wcs->tstop);
2194   wcsprt_auxd(" xposure", wcs->xposure);
2195   wcsprt_auxd(" telapse", wcs->telapse);
2196 
2197 
2198   wcsprt_auxd(" timsyer", wcs->timsyer);
2199   wcsprt_auxd(" timrder", wcs->timrder);
2200   wcsprt_auxd(" timedel", wcs->timedel);
2201   wcsprt_auxd("timepixr", wcs->timepixr);
2202 
2203   wcsprintf("     obsgeo: ");
2204   for (int k = 0; k < 3; k++) {
2205     if (undefined(wcs->obsgeo[k])) {
2206       wcsprintf("       UNDEFINED");
2207     } else {
2208       wcsprintf(" %15.6f", wcs->obsgeo[k]);
2209     }
2210   }
2211   wcsprintf("\n             ");
2212   for (int k = 3; k < 6; k++) {
2213     if (undefined(wcs->obsgeo[k])) {
2214       wcsprintf("       UNDEFINED");
2215     } else {
2216       wcsprintf(" %15.6f", wcs->obsgeo[k]);
2217     }
2218   }
2219   wcsprintf("\n");
2220 
2221   wcsprt_auxc("obsorbit", wcs->obsorbit);
2222   wcsprt_auxc(" radesys", wcs->radesys);
2223   wcsprt_auxd(" equinox", wcs->equinox);
2224   wcsprt_auxc(" specsys", wcs->specsys);
2225   wcsprt_auxc(" ssysobs", wcs->ssysobs);
2226   wcsprt_auxd(" velosys", wcs->velosys);
2227   wcsprt_auxd(" zsource", wcs->zsource);
2228   wcsprt_auxc(" ssyssrc", wcs->ssyssrc);
2229   wcsprt_auxd(" velangl", wcs->velangl);
2230 
2231   // Additional auxiliary coordinate system information.
2232   WCSPRINTF_PTR("        aux: ", wcs->aux, "\n");
2233   if (wcs->aux) {
2234     wcsprt_auxd("rsun_ref", wcs->aux->rsun_ref);
2235     wcsprt_auxd("dsun_obs", wcs->aux->dsun_obs);
2236     wcsprt_auxd("crln_obs", wcs->aux->crln_obs);
2237     wcsprt_auxd("hgln_obs", wcs->aux->hgln_obs);
2238     wcsprt_auxd("hglt_obs", wcs->aux->hglt_obs);
2239   }
2240 
2241   wcsprintf("       ntab: %d\n", wcs->ntab);
2242   WCSPRINTF_PTR("        tab: ", wcs->tab, "");
2243   if (wcs->tab != 0x0) wcsprintf("  (see below)");
2244   wcsprintf("\n");
2245   wcsprintf("       nwtb: %d\n", wcs->nwtb);
2246   WCSPRINTF_PTR("        wtb: ", wcs->wtb, "");
2247   if (wcs->wtb != 0x0) wcsprintf("  (see below)");
2248   wcsprintf("\n");
2249 
2250   // Derived values.
2251   WCSPRINTF_PTR("      types: ", wcs->types, "\n           ");
2252   for (int i = 0; i < wcs->naxis; i++) {
2253     wcsprintf("%5d", wcs->types[i]);
2254   }
2255   wcsprintf("\n");
2256 
2257   wcsprintf("     lngtyp: \"%s\"\n", wcs->lngtyp);
2258   wcsprintf("     lattyp: \"%s\"\n", wcs->lattyp);
2259   wcsprintf("        lng: %d\n", wcs->lng);
2260   wcsprintf("        lat: %d\n", wcs->lat);
2261   wcsprintf("       spec: %d\n", wcs->spec);
2262   wcsprintf("   cubeface: %d\n", wcs->cubeface);
2263 
2264   WCSPRINTF_PTR("        err: ", wcs->err, "\n");
2265   if (wcs->err) {
2266     wcserr_prt(wcs->err, "             ");
2267   }
2268 
2269   wcsprintf("        lin: (see below)\n");
2270   wcsprintf("        cel: (see below)\n");
2271   wcsprintf("        spc: (see below)\n");
2272 
2273   // Memory management.
2274   wcsprintf("     m_flag: %d\n", wcs->m_flag);
2275   wcsprintf("    m_naxis: %d\n", wcs->m_naxis);
2276   WCSPRINTF_PTR("    m_crpix: ", wcs->m_crpix, "");
2277   if (wcs->m_crpix == wcs->crpix) wcsprintf("  (= crpix)");
2278   wcsprintf("\n");
2279   WCSPRINTF_PTR("       m_pc: ", wcs->m_pc, "");
2280   if (wcs->m_pc == wcs->pc) wcsprintf("  (= pc)");
2281   wcsprintf("\n");
2282   WCSPRINTF_PTR("    m_cdelt: ", wcs->m_cdelt, "");
2283   if (wcs->m_cdelt == wcs->cdelt) wcsprintf("  (= cdelt)");
2284   wcsprintf("\n");
2285   WCSPRINTF_PTR("    m_crval: ", wcs->m_crval, "");
2286   if (wcs->m_crval == wcs->crval) wcsprintf("  (= crval)");
2287   wcsprintf("\n");
2288   WCSPRINTF_PTR("    m_cunit: ", wcs->m_cunit, "");
2289   if (wcs->m_cunit == wcs->cunit) wcsprintf("  (= cunit)");
2290   wcsprintf("\n");
2291   WCSPRINTF_PTR("    m_ctype: ", wcs->m_ctype, "");
2292   if (wcs->m_ctype == wcs->ctype) wcsprintf("  (= ctype)");
2293   wcsprintf("\n");
2294   WCSPRINTF_PTR("       m_pv: ", wcs->m_pv, "");
2295   if (wcs->m_pv == wcs->pv) wcsprintf("  (= pv)");
2296   wcsprintf("\n");
2297   WCSPRINTF_PTR("       m_ps: ", wcs->m_ps, "");
2298   if (wcs->m_ps == wcs->ps) wcsprintf("  (= ps)");
2299   wcsprintf("\n");
2300   WCSPRINTF_PTR("       m_cd: ", wcs->m_cd, "");
2301   if (wcs->m_cd == wcs->cd) wcsprintf("  (= cd)");
2302   wcsprintf("\n");
2303   WCSPRINTF_PTR("    m_crota: ", wcs->m_crota, "");
2304   if (wcs->m_crota == wcs->crota) wcsprintf("  (= crota)");
2305   wcsprintf("\n");
2306   wcsprintf("\n");
2307   WCSPRINTF_PTR("    m_colax: ", wcs->m_colax, "");
2308   if (wcs->m_colax == wcs->colax) wcsprintf("  (= colax)");
2309   wcsprintf("\n");
2310   WCSPRINTF_PTR("    m_cname: ", wcs->m_cname, "");
2311   if (wcs->m_cname == wcs->cname) wcsprintf("  (= cname)");
2312   wcsprintf("\n");
2313   WCSPRINTF_PTR("    m_crder: ", wcs->m_crder, "");
2314   if (wcs->m_crder == wcs->crder) wcsprintf("  (= crder)");
2315   wcsprintf("\n");
2316   WCSPRINTF_PTR("    m_csyer: ", wcs->m_csyer, "");
2317   if (wcs->m_csyer == wcs->csyer) wcsprintf("  (= csyer)");
2318   wcsprintf("\n");
2319   WCSPRINTF_PTR("    m_czphs: ", wcs->m_czphs, "");
2320   if (wcs->m_czphs == wcs->czphs) wcsprintf("  (= czphs)");
2321   wcsprintf("\n");
2322   WCSPRINTF_PTR("    m_cperi: ", wcs->m_cperi, "");
2323   if (wcs->m_cperi == wcs->cperi) wcsprintf("  (= cperi)");
2324   wcsprintf("\n");
2325   WCSPRINTF_PTR("      m_aux: ", wcs->m_aux, "");
2326   if (wcs->m_aux == wcs->aux) wcsprintf("  (= aux)");
2327   wcsprintf("\n");
2328   WCSPRINTF_PTR("      m_tab: ", wcs->m_tab, "");
2329   if (wcs->m_tab == wcs->tab) wcsprintf("  (= tab)");
2330   wcsprintf("\n");
2331   WCSPRINTF_PTR("      m_wtb: ", wcs->m_wtb, "");
2332   if (wcs->m_wtb == wcs->wtb) wcsprintf("  (= wtb)");
2333   wcsprintf("\n");
2334 
2335   // Tabular transformation parameters.
2336   struct wtbarr *wtbp = wcs->wtb;
2337   if (wtbp) {
2338     for (int iwtb = 0; iwtb < wcs->nwtb; iwtb++, wtbp++) {
2339       wcsprintf("\n");
2340       wcsprintf("wtb[%d].*\n", iwtb);
2341       wcsprintf("          i: %d\n", wtbp->i);
2342       wcsprintf("          m: %d\n", wtbp->m);
2343       wcsprintf("       kind: %c\n", wtbp->kind);
2344       wcsprintf("     extnam: %s\n", wtbp->extnam);
2345       wcsprintf("     extver: %d\n", wtbp->extver);
2346       wcsprintf("     extlev: %d\n", wtbp->extlev);
2347       wcsprintf("      ttype: %s\n", wtbp->ttype);
2348       wcsprintf("        row: %ld\n", wtbp->row);
2349       wcsprintf("       ndim: %d\n", wtbp->ndim);
2350       WCSPRINTF_PTR("     dimlen: ", wtbp->dimlen, "\n");
2351       WCSPRINTF_PTR("     arrayp: ", wtbp->arrayp, " -> ");
2352       WCSPRINTF_PTR("", *(wtbp->arrayp), "\n");
2353     }
2354   }
2355 
2356   if (wcs->tab) {
2357     for (int itab = 0; itab < wcs->ntab; itab++) {
2358       wcsprintf("\n");
2359       wcsprintf("tab[%d].*\n", itab);
2360       tabprt(wcs->tab + itab);
2361     }
2362   }
2363 
2364   // Linear transformation parameters.
2365   wcsprintf("\n");
2366   wcsprintf("   lin.*\n");
2367   linprt(&(wcs->lin));
2368 
2369   // Celestial transformation parameters.
2370   wcsprintf("\n");
2371   wcsprintf("   cel.*\n");
2372   celprt(&(wcs->cel));
2373 
2374   // Spectral transformation parameters.
2375   wcsprintf("\n");
2376   wcsprintf("   spc.*\n");
2377   spcprt(&(wcs->spc));
2378 
2379   return WCSERR_SUCCESS;
2380 }
2381 
2382 //----------------------------------------------------------------------------
2383 
wcsperr(const struct wcsprm * wcs,const char * prefix)2384 int wcsperr(const struct wcsprm *wcs, const char *prefix)
2385 
2386 {
2387   if (wcs == 0x0) return WCSERR_NULL_POINTER;
2388 
2389   if (wcs->err && wcserr_prt(wcs->err, prefix) == 0) {
2390     linperr(&(wcs->lin), prefix);
2391     celperr(&(wcs->cel), prefix);
2392     wcserr_prt(wcs->spc.err, prefix);
2393     if (wcs->tab) {
2394       for (int itab = 0; itab < wcs->ntab; itab++) {
2395         wcserr_prt((wcs->tab + itab)->err, prefix);
2396       }
2397     }
2398   }
2399 
2400   return WCSERR_SUCCESS;
2401 }
2402 
2403 //----------------------------------------------------------------------------
2404 
wcsbchk(struct wcsprm * wcs,int bounds)2405 int wcsbchk(struct wcsprm *wcs, int bounds)
2406 
2407 {
2408   if (wcs == 0x0) return WCSERR_NULL_POINTER;
2409 
2410   if (wcs->flag != WCSSET) {
2411     int status;
2412     if ((status = wcsset(wcs))) return status;
2413   }
2414 
2415   wcs->cel.prj.bounds = bounds;
2416 
2417   return WCSERR_SUCCESS;
2418 }
2419 
2420 //----------------------------------------------------------------------------
2421 
wcsset(struct wcsprm * wcs)2422 int wcsset(struct wcsprm *wcs)
2423 
2424 {
2425   static const char *function = "wcsset";
2426 
2427   if (wcs == 0x0) return WCSERR_NULL_POINTER;
2428   struct wcserr **err = &(wcs->err);
2429 
2430   // Determine axis types from CTYPEia.
2431   int status;
2432   if ((status = wcs_types(wcs))) {
2433     return status;
2434   }
2435 
2436   // Convert to canonical units.
2437   if ((status = wcs_units(wcs))) {
2438     return status;
2439   }
2440 
2441   int naxis = wcs->naxis;
2442   if (32 < naxis) {
2443     return wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM),
2444       "naxis must not exceed 32 (got %d)", naxis);
2445   }
2446 
2447 
2448   // Non-linear celestial axes present?
2449   if (wcs->lng >= 0 && wcs->types[wcs->lng] == 2200) {
2450     struct celprm *wcscel = &(wcs->cel);
2451     celini(wcscel);
2452 
2453     // CRVALia, LONPOLEa, and LATPOLEa keyvalues.
2454     wcscel->ref[0] = wcs->crval[wcs->lng];
2455     wcscel->ref[1] = wcs->crval[wcs->lat];
2456     wcscel->ref[2] = wcs->lonpole;
2457     wcscel->ref[3] = wcs->latpole;
2458 
2459     // Do alias translation for TPU/TPV before dealing with PVi_ma.
2460     struct prjprm *wcsprj = &(wcscel->prj);
2461     strncpy(wcsprj->code, wcs->ctype[wcs->lng]+5, 3);
2462     wcsprj->code[3] = '\0';
2463     if (strncmp(wcsprj->code, "TPU", 3) == 0 ||
2464         strncmp(wcsprj->code, "TPV", 3) == 0) {
2465       // Translate the PV parameters.
2466       struct disprm *dis;
2467       if ((dis = calloc(1, sizeof(struct disprm))) == 0x0) {
2468         return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
2469       }
2470 
2471       int ndpmax = 6 + wcs->npv;
2472 
2473       // Attach it to linprm.  Also inits it.
2474       char dpq[16];
2475       struct linprm *wcslin = &(wcs->lin);
2476       dis->flag = -1;
2477       if (strncmp(wcsprj->code, "TPU", 3) == 0) {
2478         // Prior distortion.
2479         lindist(1, wcslin, dis, ndpmax);
2480         strcpy(dpq, "DP");
2481       } else {
2482         // Sequent distortion.
2483         lindist(2, wcslin, dis, ndpmax);
2484         strcpy(dpq, "DQ");
2485       }
2486 
2487       // Yes, the distortion type is "TPV" even for TPU.
2488       strcpy(dis->dtype[wcs->lng], "TPV");
2489       strcpy(dis->dtype[wcs->lat], "TPV");
2490 
2491       // Keep the keywords in axis-order to aid debugging.
2492       struct dpkey *keyp = dis->dp;
2493       dis->ndp = 0;
2494 
2495       sprintf(dpq+2, "%d", wcs->lng+1);
2496       dpfill(keyp++, dpq, "NAXES",  0, 0, 2, 0.0);
2497       dpfill(keyp++, dpq, "AXIS.1", 0, 0, 1, 0.0);
2498       dpfill(keyp++, dpq, "AXIS.2", 0, 0, 2, 0.0);
2499       dis->ndp += 3;
2500 
2501       // Copy distortion parameters for the longitude axis.
2502       for (int k = 0; k < wcs->npv; k++) {
2503         if (wcs->pv[k].i != wcs->lng+1) continue;
2504         sprintf(keyp->field, "%s.TPV.%d", dpq, wcs->pv[k].m);
2505         dpfill(keyp++, 0x0, 0x0, 0, 1, 0, wcs->pv[k].value);
2506         dis->ndp++;
2507       }
2508 
2509       // Now the latitude axis.
2510       sprintf(dpq+2, "%d", wcs->lat+1);
2511       dpfill(keyp++, dpq, "NAXES",  0, 0, 2, 0.0);
2512       dpfill(keyp++, dpq, "AXIS.1", 0, 0, 2, 0.0);
2513       dpfill(keyp++, dpq, "AXIS.2", 0, 0, 1, 0.0);
2514       dis->ndp += 3;
2515 
2516       for (int k = 0; k < wcs->npv; k++) {
2517         if (wcs->pv[k].i != wcs->lat+1) continue;
2518         sprintf(keyp->field, "%s.TPV.%d", dpq, wcs->pv[k].m);
2519         dpfill(keyp++, 0x0, 0x0, 0, 1, 0, wcs->pv[k].value);
2520         dis->ndp++;
2521       }
2522 
2523       // Erase PVi_ma associated with the celestial axes.
2524       int n = 0;
2525       for (int k = 0; k < wcs->npv; k++) {
2526         int i = wcs->pv[k].i - 1;
2527         if (i == wcs->lng || i == wcs->lat) continue;
2528 
2529         wcs->pv[n].i = wcs->pv[k].i;
2530         wcs->pv[n].m = wcs->pv[k].m;
2531         wcs->pv[n].value = wcs->pv[k].value;
2532 
2533         n++;
2534       }
2535 
2536       wcs->npv = n;
2537       strcpy(wcsprj->code, "TAN");
2538 
2539       // As the PVi_ma have now been erased, ctype must be reset to prevent
2540       // this translation from re-occurring if wcsset() is called again.
2541       strcpy(wcs->ctype[wcs->lng]+5, "TAN");
2542       strcpy(wcs->ctype[wcs->lat]+5, "TAN");
2543 
2544     } else if (strncmp(wcsprj->code, "TNX", 3) == 0) {
2545       // The WAT distortion should already have been encoded in disseq.
2546       strcpy(wcsprj->code, "TAN");
2547       strcpy(wcs->ctype[wcs->lng]+5, "TAN");
2548       strcpy(wcs->ctype[wcs->lat]+5, "TAN");
2549 
2550     } else if (strncmp(wcsprj->code, "ZPX", 3) == 0) {
2551       // The WAT distortion should already have been encoded in disseq.
2552       strcpy(wcsprj->code, "ZPN");
2553       strcpy(wcs->ctype[wcs->lng]+5, "ZPN");
2554       strcpy(wcs->ctype[wcs->lat]+5, "ZPN");
2555     }
2556 
2557     // PVi_ma keyvalues.
2558     for (int k = 0; k < wcs->npv; k++) {
2559       if (wcs->pv[k].i == 0) {
2560         // From a PROJPn keyword.
2561         wcs->pv[k].i = wcs->lat + 1;
2562       }
2563 
2564       int i = wcs->pv[k].i - 1;
2565       int m = wcs->pv[k].m;
2566 
2567       if (i == wcs->lat) {
2568         // PVi_ma associated with latitude axis.
2569         if (m < 30) {
2570           wcsprj->pv[m] = wcs->pv[k].value;
2571         }
2572 
2573       } else if (i == wcs->lng) {
2574         // PVi_ma associated with longitude axis.
2575         switch (m) {
2576         case 0:
2577           wcscel->offset = (wcs->pv[k].value != 0.0);
2578           break;
2579         case 1:
2580           wcscel->phi0   = wcs->pv[k].value;
2581           break;
2582         case 2:
2583           wcscel->theta0 = wcs->pv[k].value;
2584           break;
2585         case 3:
2586           // If present, overrides LONPOLEa.
2587           wcscel->ref[2] = wcs->pv[k].value;
2588           break;
2589         case 4:
2590           // If present, overrides LATPOLEa.
2591           wcscel->ref[3] = wcs->pv[k].value;
2592           break;
2593         default:
2594           return wcserr_set(WCSERR_SET(WCSERR_BAD_COORD_TRANS),
2595             "PV%i_%i%s: Unrecognized coordinate transformation parameter",
2596             i+1, m, wcs->alt);
2597           break;
2598         }
2599       }
2600     }
2601 
2602     // Do simple alias translations.
2603     if (strncmp(wcs->ctype[wcs->lng]+5, "GLS", 3) == 0) {
2604       wcscel->offset = 1;
2605       wcscel->phi0   = 0.0;
2606       wcscel->theta0 = wcs->crval[wcs->lat];
2607       strcpy(wcsprj->code, "SFL");
2608 
2609     } else if (strncmp(wcs->ctype[wcs->lng]+5, "NCP", 3) == 0) {
2610       // Convert NCP to SIN.
2611       if (wcscel->ref[1] == 0.0) {
2612         return wcserr_set(WCSERR_SET(WCSERR_BAD_PARAM),
2613           "Invalid projection: NCP blows up on the equator");
2614       }
2615 
2616       strcpy(wcsprj->code, "SIN");
2617       wcsprj->pv[1] = 0.0;
2618       wcsprj->pv[2] = cosd(wcscel->ref[1])/sind(wcscel->ref[1]);
2619     }
2620 
2621     // Initialize the celestial transformation routines.
2622     wcsprj->r0 = 0.0;
2623     if ((status = celset(wcscel))) {
2624       return wcserr_set(WCS_ERRMSG(wcs_celerr[status]));
2625     }
2626 
2627     // Update LONPOLE, LATPOLE, and PVi_ma keyvalues.
2628     wcs->lonpole = wcscel->ref[2];
2629     wcs->latpole = wcscel->ref[3];
2630 
2631     for (int k = 0; k < wcs->npv; k++) {
2632       int i = wcs->pv[k].i - 1;
2633       int m = wcs->pv[k].m;
2634 
2635       if (i == wcs->lng) {
2636         switch (m) {
2637         case 1:
2638           wcs->pv[k].value = wcscel->phi0;
2639           break;
2640         case 2:
2641           wcs->pv[k].value = wcscel->theta0;
2642           break;
2643         case 3:
2644           wcs->pv[k].value = wcscel->ref[2];
2645           break;
2646         case 4:
2647           wcs->pv[k].value = wcscel->ref[3];
2648           break;
2649         }
2650       }
2651     }
2652   }
2653 
2654 
2655   // Non-linear spectral axis present?
2656   if (wcs->spec >= 0 && wcs->types[wcs->spec] == 3300) {
2657     char scode[4], stype[5];
2658     struct spcprm *wcsspc = &(wcs->spc);
2659     spcini(wcsspc);
2660     if ((status = spctype(wcs->ctype[wcs->spec], stype, scode, 0x0, 0x0, 0x0,
2661                           0x0, 0x0, err))) {
2662       return status;
2663     }
2664     strcpy(wcsspc->type, stype);
2665     strcpy(wcsspc->code, scode);
2666 
2667     // CRVALia, RESTFRQa, and RESTWAVa keyvalues.
2668     wcsspc->crval = wcs->crval[wcs->spec];
2669     wcsspc->restfrq = wcs->restfrq;
2670     wcsspc->restwav = wcs->restwav;
2671 
2672     // PVi_ma keyvalues.
2673     for (int k = 0; k < wcs->npv; k++) {
2674       int i = wcs->pv[k].i - 1;
2675       int m = wcs->pv[k].m;
2676 
2677       if (i == wcs->spec) {
2678         // PVi_ma associated with grism axis.
2679         if (m < 7) {
2680           wcsspc->pv[m] = wcs->pv[k].value;
2681         }
2682       }
2683     }
2684 
2685     // Initialize the spectral transformation routines.
2686     if ((status = spcset(wcsspc))) {
2687       return wcserr_set(WCS_ERRMSG(wcs_spcerr[status]));
2688     }
2689   }
2690 
2691 
2692   // Tabular axes present?
2693   for (int itab = 0; itab < wcs->ntab; itab++) {
2694     if ((status = tabset(wcs->tab + itab))) {
2695       return wcserr_set(WCS_ERRMSG(wcs_taberr[status]));
2696     }
2697   }
2698 
2699 
2700   // Initialize the linear transformation.
2701   wcs->altlin &= 15;
2702   if (wcs->altlin > 1 && !(wcs->altlin & 1)) {
2703     double *pc = wcs->pc;
2704 
2705     if ((wcs->altlin & 2) && !(wcs->altlin & 8)) {
2706       // Copy CDi_ja to PCi_ja and reset CDELTia.
2707       double *cd = wcs->cd;
2708       for (int i = 0; i < naxis; i++) {
2709         for (int j = 0; j < naxis; j++) {
2710           *(pc++) = *(cd++);
2711         }
2712         wcs->cdelt[i] = 1.0;
2713       }
2714 
2715     } else if (wcs->altlin & 4) {
2716       // Construct PCi_ja from CROTAia.
2717       int i, j;
2718       if ((i = wcs->lng) >= 0 && (j = wcs->lat) >= 0) {
2719         double rho = wcs->crota[j];
2720 
2721         if (wcs->cdelt[i] == 0.0) {
2722           return wcserr_set(WCSERR_SET(WCSERR_SINGULAR_MTX),
2723             "Singular transformation matrix, CDELT%d is zero", i+1);
2724         }
2725         double lambda = wcs->cdelt[j]/wcs->cdelt[i];
2726 
2727         *(pc + i*naxis + i) = *(pc + j*naxis + j) = cosd(rho);
2728         *(pc + i*naxis + j) = *(pc + j*naxis + i) = sind(rho);
2729         *(pc + i*naxis + j) *= -lambda;
2730         *(pc + j*naxis + i) /=  lambda;
2731       }
2732     }
2733   }
2734 
2735   wcs->lin.crpix  = wcs->crpix;
2736   wcs->lin.pc     = wcs->pc;
2737   wcs->lin.cdelt  = wcs->cdelt;
2738   if ((status = linset(&(wcs->lin)))) {
2739     return wcserr_set(WCS_ERRMSG(wcs_linerr[status]));
2740   }
2741 
2742 
2743   // Set defaults for radesys and equinox for equatorial or ecliptic.
2744   if (strcmp(wcs->lngtyp, "RA")   == 0 ||
2745       strcmp(wcs->lngtyp, "ELON") == 0 ||
2746       strcmp(wcs->lngtyp, "HLON") == 0) {
2747     if (wcs->radesys[0] == '\0') {
2748       if (undefined(wcs->equinox)) {
2749         strcpy(wcs->radesys, "ICRS");
2750       } else if (wcs->equinox < 1984.0) {
2751         strcpy(wcs->radesys, "FK4");
2752       } else {
2753         strcpy(wcs->radesys, "FK5");
2754       }
2755 
2756     } else if (strcmp(wcs->radesys, "ICRS")  == 0 ||
2757                strcmp(wcs->radesys, "GAPPT") == 0) {
2758       // Equinox is not applicable for these coordinate systems.
2759       wcs->equinox = UNDEFINED;
2760 
2761     } else if (undefined(wcs->equinox)) {
2762       if (strcmp(wcs->radesys, "FK5") == 0) {
2763         wcs->equinox = 2000.0;
2764       } else if (strcmp(wcs->radesys, "FK4") == 0 ||
2765                  strcmp(wcs->radesys, "FK4-NO-E") == 0) {
2766         wcs->equinox = 1950.0;
2767       }
2768     }
2769 
2770   } else {
2771     // No celestial axes, ensure that radesys and equinox are unset.
2772     memset(wcs->radesys, 0, 72);
2773     wcs->equinox = UNDEFINED;
2774   }
2775 
2776 
2777   // Strip off trailing blanks and null-fill auxiliary string members.
2778   if (wcs->alt[0] == '\0') wcs->alt[0] = ' ';
2779   memset(wcs->alt+1, '\0', 3);
2780 
2781   for (int i = 0; i < naxis; i++) {
2782     wcsutil_null_fill(72, wcs->cname[i]);
2783   }
2784   wcsutil_null_fill(72, wcs->wcsname);
2785   wcsutil_null_fill(72, wcs->timesys);
2786   wcsutil_null_fill(72, wcs->trefpos);
2787   wcsutil_null_fill(72, wcs->trefdir);
2788   wcsutil_null_fill(72, wcs->plephem);
2789   wcsutil_null_fill(72, wcs->timeunit);
2790   wcsutil_null_fill(72, wcs->dateref);
2791   wcsutil_null_fill(72, wcs->dateobs);
2792   wcsutil_null_fill(72, wcs->datebeg);
2793   wcsutil_null_fill(72, wcs->dateavg);
2794   wcsutil_null_fill(72, wcs->dateend);
2795   wcsutil_null_fill(72, wcs->obsorbit);
2796   wcsutil_null_fill(72, wcs->radesys);
2797   wcsutil_null_fill(72, wcs->specsys);
2798   wcsutil_null_fill(72, wcs->ssysobs);
2799   wcsutil_null_fill(72, wcs->ssyssrc);
2800 
2801   // MJDREF defaults to zero if no reference date keywords were defined.
2802   if (wcs->dateref[0] == '\0') {
2803     if (undefined(wcs->mjdref[0])) {
2804       wcs->mjdref[0] = 0.0;
2805     }
2806     if (undefined(wcs->mjdref[1])) {
2807       wcs->mjdref[1] = 0.0;
2808     }
2809   }
2810 
2811   wcs->flag = WCSSET;
2812 
2813   return WCSERR_SUCCESS;
2814 }
2815 
2816 // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :
2817 
wcs_types(struct wcsprm * wcs)2818 int wcs_types(struct wcsprm *wcs)
2819 
2820 {
2821   static const char *function = "wcs_types";
2822 
2823   const int  nalias = 6;
2824   const char aliases [6][4] = {"NCP", "GLS", "TPU", "TPV", "TNX", "ZPX"};
2825 
2826   const char *alt = "";
2827   char ctypei[16], pcode[4], requir[16], scode[4], specsys[9];
2828   int i, j, m, naxis, *ndx = 0x0, type;
2829 
2830   if (wcs == 0x0) return WCSERR_NULL_POINTER;
2831   struct wcserr **err = &(wcs->err);
2832 
2833   // Parse the CTYPEia keyvalues.
2834   pcode[0]  = '\0';
2835   requir[0] = '\0';
2836   wcs->lng  = -1;
2837   wcs->lat  = -1;
2838   wcs->spec = -1;
2839   wcs->cubeface = -1;
2840 
2841   if (*(wcs->alt) != ' ') alt = wcs->alt;
2842 
2843 
2844   naxis = wcs->naxis;
2845   if (wcs->types) free(wcs->types);
2846   if ((wcs->types = calloc(naxis, sizeof(int))) == 0x0) {
2847     return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
2848   }
2849 
2850   for (i = 0; i < naxis; i++) {
2851     // Null fill.
2852     wcsutil_null_fill(72, wcs->ctype[i]);
2853 
2854     strncpy(ctypei, wcs->ctype[i], 15);
2855     ctypei[15] = '\0';
2856 
2857     // Check for early Paper IV syntax (e.g. '-SIP' used by Spitzer).
2858     if (strlen(ctypei) == 12 && ctypei[8] == '-') {
2859       // Excise the "4-3-3" or "8-3"-form distortion code.
2860       ctypei[8] = '\0';
2861 
2862       // Remove trailing dashes from "8-3"-form codes.
2863       for (j = 7; j > 0; j--) {
2864         if (ctypei[j] != '-') break;
2865         ctypei[j] = '\0';
2866       }
2867     }
2868 
2869     // Logarithmic or tabular axis?
2870     wcs->types[i] = 0;
2871     if (strcmp(ctypei+4, "-LOG") == 0) {
2872       // Logarithmic axis.
2873       wcs->types[i] = 400;
2874 
2875     } else if (strcmp(ctypei+4, "-TAB") == 0) {
2876       // Tabular axis.
2877       wcs->types[i] = 500;
2878     }
2879 
2880     if (wcs->types[i]) {
2881       // Could have -LOG or -TAB with celestial or spectral types.
2882       ctypei[4] = '\0';
2883 
2884       // Take care of things like 'FREQ-LOG' or 'RA---TAB'.
2885       for (j = 3; j >= 0; j--) {
2886         if (ctypei[j] != '-') break;
2887         ctypei[j] = '\0';
2888       }
2889     }
2890 
2891     // Translate AIPS spectral types for spctyp().
2892     if (spcaips(ctypei, wcs->velref, ctypei, specsys) == 0) {
2893       strcpy(wcs->ctype[i], ctypei);
2894       if (wcs->specsys[0] == '\0') strcpy(wcs->specsys, specsys);
2895     }
2896 
2897     // Process linear axes.
2898     if (!(strlen(ctypei) == 8 && ctypei[4] == '-')) {
2899       // Identify Stokes, celestial and spectral types.
2900       if (strcmp(ctypei, "STOKES") == 0) {
2901         // STOKES axis.
2902         wcs->types[i] = 1100;
2903 
2904       } else if (strcmp(ctypei, "RA")  == 0 ||
2905         strcmp(ctypei+1, "LON") == 0 ||
2906         strcmp(ctypei+2, "LN")  == 0) {
2907         // Longitude axis.
2908         wcs->types[i] += 2000;
2909         if (wcs->lng < 0) {
2910           wcs->lng = i;
2911           strcpy(wcs->lngtyp, ctypei);
2912         }
2913 
2914       } else if (strcmp(ctypei,   "DEC") == 0 ||
2915                  strcmp(ctypei+1, "LAT") == 0 ||
2916                  strcmp(ctypei+2, "LT")  == 0) {
2917         // Latitude axis.
2918         wcs->types[i] += 2001;
2919         if (wcs->lat < 0) {
2920           wcs->lat = i;
2921           strcpy(wcs->lattyp, ctypei);
2922         }
2923 
2924       } else if (strcmp(ctypei, "CUBEFACE") == 0) {
2925         // CUBEFACE axis.
2926         if (wcs->cubeface == -1) {
2927           wcs->types[i] = 2102;
2928           wcs->cubeface = i;
2929         } else {
2930           // Multiple CUBEFACE axes!
2931           return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE),
2932             "Multiple CUBEFACE axes (in CTYPE%d%.1s and CTYPE%d%.1s)",
2933             wcs->cubeface+1, alt, i+1, alt);
2934         }
2935 
2936       } else if (spctyp(ctypei, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) == 0) {
2937         // Spectral axis.
2938         if (wcs->spec < 0) wcs->spec = i;
2939         wcs->types[i] += 3000;
2940       }
2941 
2942       continue;
2943     }
2944 
2945 
2946     // CTYPEia is in "4-3" form; is it a recognized spectral type?
2947     if (spctyp(ctypei, 0x0, scode, 0x0, 0x0, 0x0, 0x0, 0x0) == 0) {
2948       // Non-linear spectral axis found.
2949       wcs->types[i] = 3300;
2950 
2951       // Check uniqueness.
2952       if (wcs->spec >= 0) {
2953         return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE),
2954           "Multiple spectral axes (in CTYPE%d%.1s and CTYPE%d%.1s)",
2955           wcs->spec+1, alt, i+1, alt);
2956       }
2957 
2958       wcs->spec = i;
2959 
2960       continue;
2961     }
2962 
2963 
2964     // Is it a recognized celestial projection?
2965     for (j = 0; j < prj_ncode; j++) {
2966       if (strncmp(ctypei+5, prj_codes[j], 3) == 0) break;
2967     }
2968 
2969     if (j == prj_ncode) {
2970       // Not a standard projection code, maybe it's an alias.
2971       for (j = 0; j < nalias; j++) {
2972         if (strncmp(ctypei+5, aliases[j], 3) == 0) break;
2973       }
2974 
2975       if (j == nalias) {
2976         // Not a recognized algorithm code of any type.
2977         wcs->types[i] = -1;
2978         return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE),
2979           "Unrecognized projection code (%s in CTYPE%d%.1s)",
2980           ctypei+5, i+1, alt);
2981       }
2982     }
2983 
2984     // Parse the celestial axis type.
2985     wcs->types[i] = 2200;
2986     if (*pcode == '\0') {
2987       // The first of the two celestial axes.
2988       sprintf(pcode, "%.3s", ctypei+5);
2989 
2990       if (strncmp(ctypei, "RA--", 4) == 0) {
2991         wcs->lng = i;
2992         strcpy(wcs->lngtyp, "RA");
2993         strcpy(wcs->lattyp, "DEC");
2994         ndx = &wcs->lat;
2995         sprintf(requir, "DEC--%s", pcode);
2996       } else if (strncmp(ctypei, "DEC-", 4) == 0) {
2997         wcs->lat = i;
2998         strcpy(wcs->lngtyp, "RA");
2999         strcpy(wcs->lattyp, "DEC");
3000         ndx = &wcs->lng;
3001         sprintf(requir, "RA---%s", pcode);
3002       } else if (strncmp(ctypei+1, "LON", 3) == 0) {
3003         wcs->lng = i;
3004         sprintf(wcs->lngtyp, "%cLON", ctypei[0]);
3005         sprintf(wcs->lattyp, "%cLAT", ctypei[0]);
3006         ndx = &wcs->lat;
3007         sprintf(requir, "%s-%s", wcs->lattyp, pcode);
3008       } else if (strncmp(ctypei+1, "LAT", 3) == 0) {
3009         wcs->lat = i;
3010         sprintf(wcs->lngtyp, "%cLON", ctypei[0]);
3011         sprintf(wcs->lattyp, "%cLAT", ctypei[0]);
3012         ndx = &wcs->lng;
3013         sprintf(requir, "%s-%s", wcs->lngtyp, pcode);
3014       } else if (strncmp(ctypei+2, "LN", 2) == 0) {
3015         wcs->lng = i;
3016         sprintf(wcs->lngtyp, "%c%cLN", ctypei[0], ctypei[1]);
3017         sprintf(wcs->lattyp, "%c%cLT", ctypei[0], ctypei[1]);
3018         ndx = &wcs->lat;
3019         sprintf(requir, "%s-%s", wcs->lattyp, pcode);
3020       } else if (strncmp(ctypei+2, "LT", 2) == 0) {
3021         wcs->lat = i;
3022         sprintf(wcs->lngtyp, "%c%cLN", ctypei[0], ctypei[1]);
3023         sprintf(wcs->lattyp, "%c%cLT", ctypei[0], ctypei[1]);
3024         ndx = &wcs->lng;
3025         sprintf(requir, "%s-%s", wcs->lngtyp, pcode);
3026       } else {
3027         // Unrecognized celestial type.
3028         wcs->types[i] = -1;
3029 
3030         wcs->lng = -1;
3031         wcs->lat = -1;
3032         return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE),
3033           "Unrecognized celestial type (%5s in CTYPE%d%.1s)",
3034           ctypei, i+1, alt);
3035       }
3036 
3037       if (wcs->lat >= 0) wcs->types[i]++;
3038 
3039     } else {
3040       // Looking for the complementary celestial axis.
3041       if (wcs->lat < 0) wcs->types[i]++;
3042 
3043       if (strncmp(ctypei, requir, 8) != 0) {
3044         // Inconsistent projection types.
3045         wcs->lng = -1;
3046         wcs->lat = -1;
3047         return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE), "Inconsistent "
3048           "projection types (expected %s, got %s in CTYPE%d%.1s)", requir,
3049           ctypei, i+1, alt);
3050       }
3051 
3052       *ndx = i;
3053       requir[0] = '\0';
3054     }
3055   }
3056 
3057   // Do we have a complementary pair of celestial axes?
3058   if (strcmp(requir, "")) {
3059     // Unmatched celestial axis.
3060     wcs->lng = -1;
3061     wcs->lat = -1;
3062     return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE),
3063       "Unmatched celestial axes");
3064   }
3065 
3066   // Table group numbers.
3067   for (j = 0; j < wcs->ntab; j++) {
3068     for (m = 0; m < wcs->tab[j].M; m++) {
3069       // Get image axis number.
3070       i = wcs->tab[j].map[m];
3071 
3072       type = (wcs->types[i] / 100) % 10;
3073       if (type != 5) {
3074         return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE),
3075           "Table parameters set for non-table axis type");
3076       }
3077       wcs->types[i] += 10 * j;
3078     }
3079   }
3080 
3081   return WCSERR_SUCCESS;
3082 }
3083 
3084 // : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :
3085 
wcs_units(struct wcsprm * wcs)3086 int wcs_units(struct wcsprm *wcs)
3087 
3088 {
3089   static const char *function = "wcs_units";
3090 
3091   char ctype[9], units[16];
3092   int  i, j, naxis;
3093   double scale, offset, power;
3094   struct wcserr *uniterr = 0x0, **err;
3095 
3096   if (wcs == 0x0) return WCSERR_NULL_POINTER;
3097   err = &(wcs->err);
3098 
3099   naxis = wcs->naxis;
3100   for (i = 0; i < naxis; i++) {
3101     // Squeeze out trailing blanks.
3102     wcsutil_null_fill(72, wcs->cunit[i]);
3103 
3104     // Use types set by wcs_types().
3105     switch (wcs->types[i]/1000) {
3106     case 2:
3107       // Celestial axis.
3108       strcpy(units, "deg");
3109       break;
3110 
3111     case 3:
3112       // Spectral axis.
3113       strncpy(ctype, wcs->ctype[i], 8);
3114       ctype[8] = '\0';
3115       spctyp(ctype, 0x0, 0x0, 0x0, units, 0x0, 0x0, 0x0);
3116       break;
3117 
3118     default:
3119       continue;
3120     }
3121 
3122     // Tabular axis, CDELTia and CRVALia relate to indices.
3123     if ((wcs->types[i]/100)%10 == 5) {
3124       continue;
3125     }
3126 
3127     if (wcs->cunit[i][0]) {
3128       if (wcsunitse(wcs->cunit[i], units, &scale, &offset, &power,
3129                     &uniterr)) {
3130         if (uniterr) {
3131           // uniterr will not be set if wcserr is not enabled.
3132           wcserr_set(WCSERR_SET(WCSERR_BAD_COORD_TRANS),
3133             "In CUNIT%d%.1s: %s", i+1, (*wcs->alt)?wcs->alt:"", uniterr->msg);
3134           free(uniterr);
3135         }
3136         return WCSERR_BAD_COORD_TRANS;
3137       }
3138 
3139       if (scale != 1.0) {
3140         wcs->cdelt[i] *= scale;
3141         wcs->crval[i] *= scale;
3142 
3143         for (j = 0; j < naxis; j++) {
3144           *(wcs->cd + i*naxis + j) *= scale;
3145         }
3146 
3147         strcpy(wcs->cunit[i], units);
3148       }
3149 
3150     } else {
3151       strcpy(wcs->cunit[i], units);
3152     }
3153   }
3154 
3155   return WCSERR_SUCCESS;
3156 }
3157 
3158 //----------------------------------------------------------------------------
3159 
wcsp2s(struct wcsprm * wcs,int ncoord,int nelem,const double pixcrd[],double imgcrd[],double phi[],double theta[],double world[],int stat[])3160 int wcsp2s(
3161   struct wcsprm *wcs,
3162   int ncoord,
3163   int nelem,
3164   const double pixcrd[],
3165   double imgcrd[],
3166   double phi[],
3167   double theta[],
3168   double world[],
3169   int stat[])
3170 
3171 {
3172   static const char *function = "wcsp2s";
3173 
3174   int    bits, face, i, iso_x, iso_y, istat, *istatp, itab, k, m, nx, ny,
3175         *statp, status, type;
3176   double crvali, offset;
3177   register double *img, *wrl;
3178   struct celprm *wcscel = &(wcs->cel);
3179   struct prjprm *wcsprj = &(wcscel->prj);
3180   struct wcserr **err;
3181 
3182   // Initialize if required.
3183   if (wcs == 0x0) return WCSERR_NULL_POINTER;
3184   err = &(wcs->err);
3185 
3186   if (wcs->flag != WCSSET) {
3187     if ((status = wcsset(wcs))) return status;
3188   }
3189 
3190   // Sanity check.
3191   if (ncoord < 1 || (ncoord > 1 && nelem < wcs->naxis)) {
3192     return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE),
3193       "ncoord and/or nelem inconsistent with the wcsprm");
3194   }
3195 
3196 
3197   // Apply pixel-to-world linear transformation.
3198   if ((status = linp2x(&(wcs->lin), ncoord, nelem, pixcrd, imgcrd))) {
3199     return wcserr_set(WCS_ERRMSG(wcs_linerr[status]));
3200   }
3201 
3202   // Initialize status vectors.
3203   if ((istatp = calloc(ncoord, sizeof(int))) == 0x0) {
3204     return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
3205   }
3206 
3207   stat[0] = 0;
3208   wcsutil_setAli(ncoord, 1, stat);
3209 
3210 
3211   // Convert intermediate world coordinates to world coordinates.
3212   for (i = 0; i < wcs->naxis; i++) {
3213     // Extract the second digit of the axis type code.
3214     type = (wcs->types[i] / 100) % 10;
3215 
3216     if (type <= 1) {
3217       // Linear or quantized coordinate axis.
3218       img = imgcrd + i;
3219       wrl = world  + i;
3220       crvali = wcs->crval[i];
3221       for (k = 0; k < ncoord; k++) {
3222         *wrl = *img + crvali;
3223         img += nelem;
3224         wrl += nelem;
3225       }
3226 
3227     } else if (wcs->types[i] == 2200) {
3228       // Convert celestial coordinates; do we have a CUBEFACE axis?
3229       if (wcs->cubeface != -1) {
3230         // Separation between faces.
3231         if (wcsprj->r0 == 0.0) {
3232           offset = 90.0;
3233         } else {
3234           offset = wcsprj->r0*PI/2.0;
3235         }
3236 
3237         // Lay out faces in a plane.
3238         img = imgcrd;
3239         statp = stat;
3240         bits = (1 << i) | (1 << wcs->lat);
3241         for (k = 0; k < ncoord; k++, statp++) {
3242           face = (int)(*(img+wcs->cubeface) + 0.5);
3243           if (fabs(*(img+wcs->cubeface) - face) > 1e-10) {
3244             *statp |= bits;
3245             status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_PIX));
3246 
3247           } else {
3248             *statp = 0;
3249 
3250             switch (face) {
3251             case 0:
3252               *(img+wcs->lat) += offset;
3253               break;
3254             case 1:
3255               break;
3256             case 2:
3257               *(img+i) += offset;
3258               break;
3259             case 3:
3260               *(img+i) += offset*2;
3261               break;
3262             case 4:
3263               *(img+i) += offset*3;
3264               break;
3265             case 5:
3266               *(img+wcs->lat) -= offset;
3267               break;
3268             default:
3269               *statp |= bits;
3270               status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_PIX));
3271             }
3272           }
3273 
3274           img += nelem;
3275         }
3276       }
3277 
3278       // Check for constant x and/or y.
3279       nx = ncoord;
3280       ny = 0;
3281 
3282       if ((iso_x = wcsutil_allEq(ncoord, nelem, imgcrd+i))) {
3283         nx = 1;
3284         ny = ncoord;
3285       }
3286       if ((iso_y = wcsutil_allEq(ncoord, nelem, imgcrd+wcs->lat))) {
3287         ny = 1;
3288       }
3289 
3290       // Transform projection plane coordinates to celestial coordinates.
3291       if ((istat = celx2s(wcscel, nx, ny, nelem, nelem, imgcrd+i,
3292                           imgcrd+wcs->lat, phi, theta, world+i,
3293                           world+wcs->lat, istatp))) {
3294         if (istat) {
3295           status = wcserr_set(WCS_ERRMSG(wcs_celerr[istat]));
3296           if (status != WCSERR_BAD_PIX) {
3297             goto cleanup;
3298           }
3299         }
3300       }
3301 
3302       // If x and y were both constant, replicate values.
3303       if (iso_x && iso_y) {
3304         wcsutil_setAll(ncoord, nelem, world+i);
3305         wcsutil_setAll(ncoord, nelem, world+wcs->lat);
3306         wcsutil_setAll(ncoord, 1, phi);
3307         wcsutil_setAll(ncoord, 1, theta);
3308         wcsutil_setAli(ncoord, 1, istatp);
3309       }
3310 
3311       if (istat == 5) {
3312         bits = (1 << i) | (1 << wcs->lat);
3313         wcsutil_setBit(ncoord, istatp, bits, stat);
3314       }
3315 
3316     } else if (type == 3 || type == 4) {
3317       // Check for constant x.
3318       nx = ncoord;
3319       if ((iso_x = wcsutil_allEq(ncoord, nelem, imgcrd+i))) {
3320         nx = 1;
3321       }
3322 
3323       istat = 0;
3324       if (wcs->types[i] == 3300) {
3325         // Spectral coordinates.
3326         istat = spcx2s(&(wcs->spc), nx, nelem, nelem, imgcrd+i, world+i,
3327                        istatp);
3328         if (istat) {
3329           status = wcserr_set(WCS_ERRMSG(wcs_spcerr[istat]));
3330           if (status != WCSERR_BAD_PIX) {
3331             goto cleanup;
3332           }
3333         }
3334       } else if (type == 4) {
3335         // Logarithmic coordinates.
3336         istat = logx2s(wcs->crval[i], nx, nelem, nelem, imgcrd+i, world+i,
3337                        istatp);
3338         if (istat) {
3339           status = wcserr_set(WCS_ERRMSG(wcs_logerr[istat]));
3340           if (status != WCSERR_BAD_PIX) {
3341             goto cleanup;
3342           }
3343         }
3344       }
3345 
3346       // If x was constant, replicate values.
3347       if (iso_x) {
3348         wcsutil_setAll(ncoord, nelem, world+i);
3349         wcsutil_setAli(ncoord, 1, istatp);
3350       }
3351 
3352       if (istat == 3) {
3353         wcsutil_setBit(ncoord, istatp, 1 << i, stat);
3354       }
3355     }
3356   }
3357 
3358 
3359   // Do tabular coordinates.
3360   for (itab = 0; itab < wcs->ntab; itab++) {
3361     istat = tabx2s(wcs->tab + itab, ncoord, nelem, imgcrd, world, istatp);
3362 
3363     if (istat) {
3364       status = wcserr_set(WCS_ERRMSG(wcs_taberr[istat]));
3365 
3366       if (status != WCSERR_BAD_PIX) {
3367         goto cleanup;
3368 
3369       } else {
3370         bits = 0;
3371         for (m = 0; m < wcs->tab[itab].M; m++) {
3372           bits |= 1 << wcs->tab[itab].map[m];
3373         }
3374         wcsutil_setBit(ncoord, istatp, bits, stat);
3375       }
3376     }
3377   }
3378 
3379 
3380   // Zero the unused world coordinate elements.
3381   for (i = wcs->naxis; i < nelem; i++) {
3382     world[i] = 0.0;
3383     wcsutil_setAll(ncoord, nelem, world+i);
3384   }
3385 
3386 cleanup:
3387   free(istatp);
3388   return status;
3389 }
3390 
3391 //----------------------------------------------------------------------------
3392 
wcss2p(struct wcsprm * wcs,int ncoord,int nelem,const double world[],double phi[],double theta[],double imgcrd[],double pixcrd[],int stat[])3393 int wcss2p(
3394   struct wcsprm* wcs,
3395   int ncoord,
3396   int nelem,
3397   const double world[],
3398   double phi[],
3399   double theta[],
3400   double imgcrd[],
3401   double pixcrd[],
3402   int stat[])
3403 
3404 {
3405   static const char *function = "wcss2p";
3406 
3407   int    bits, i, isolat, isolng, isospec, istat, *istatp, itab, k, m, nlat,
3408          nlng, nwrld, status, type;
3409   double crvali, offset;
3410   register const double *wrl;
3411   register double *img;
3412   struct celprm *wcscel = &(wcs->cel);
3413   struct prjprm *wcsprj = &(wcscel->prj);
3414   struct wcserr **err;
3415 
3416 
3417   // Initialize if required.
3418   if (wcs == 0x0) return WCSERR_NULL_POINTER;
3419   err = &(wcs->err);
3420 
3421   if (wcs->flag != WCSSET) {
3422     if ((status = wcsset(wcs))) return status;
3423   }
3424 
3425   // Sanity check.
3426   if (ncoord < 1 || (ncoord > 1 && nelem < wcs->naxis)) {
3427     return wcserr_set(WCSERR_SET(WCSERR_BAD_CTYPE),
3428       "ncoord and/or nelem inconsistent with the wcsprm");
3429   }
3430 
3431   // Initialize status vectors.
3432   if ((istatp = calloc(ncoord, sizeof(int))) == 0x0) {
3433     return wcserr_set(WCS_ERRMSG(WCSERR_MEMORY));
3434   }
3435 
3436   status = 0;
3437   stat[0] = 0;
3438   wcsutil_setAli(ncoord, 1, stat);
3439 
3440 
3441   // Convert world coordinates to intermediate world coordinates.
3442   for (i = 0; i < wcs->naxis; i++) {
3443     // Extract the second digit of the axis type code.
3444     type = (wcs->types[i] / 100) % 10;
3445 
3446     if (type <= 1) {
3447       // Linear or quantized coordinate axis.
3448       wrl = world  + i;
3449       img = imgcrd + i;
3450       crvali = wcs->crval[i];
3451       for (k = 0; k < ncoord; k++) {
3452         *img = *wrl - crvali;
3453         wrl += nelem;
3454         img += nelem;
3455       }
3456 
3457     } else if (wcs->types[i] == 2200) {
3458       // Celestial coordinates; check for constant lng and/or lat.
3459       nlng = ncoord;
3460       nlat = 0;
3461 
3462       if ((isolng = wcsutil_allEq(ncoord, nelem, world+i))) {
3463         nlng = 1;
3464         nlat = ncoord;
3465       }
3466       if ((isolat = wcsutil_allEq(ncoord, nelem, world+wcs->lat))) {
3467         nlat = 1;
3468       }
3469 
3470       // Transform celestial coordinates to projection plane coordinates.
3471       if ((istat = cels2x(wcscel, nlng, nlat, nelem, nelem, world+i,
3472                           world+wcs->lat, phi, theta, imgcrd+i,
3473                           imgcrd+wcs->lat, istatp))) {
3474         if (istat) {
3475           status = wcserr_set(WCS_ERRMSG(wcs_celerr[istat]));
3476           if (status != WCSERR_BAD_WORLD) {
3477             goto cleanup;
3478           }
3479         }
3480       }
3481 
3482       // If lng and lat were both constant, replicate values.
3483       if (isolng && isolat) {
3484         wcsutil_setAll(ncoord, nelem, imgcrd+i);
3485         wcsutil_setAll(ncoord, nelem, imgcrd+wcs->lat);
3486         wcsutil_setAll(ncoord, 1, phi);
3487         wcsutil_setAll(ncoord, 1, theta);
3488         wcsutil_setAli(ncoord, 1, istatp);
3489       }
3490 
3491       if (istat == CELERR_BAD_WORLD) {
3492         bits = (1 << i) | (1 << wcs->lat);
3493         wcsutil_setBit(ncoord, istatp, bits, stat);
3494       }
3495 
3496       // Do we have a CUBEFACE axis?
3497       if (wcs->cubeface != -1) {
3498         // Separation between faces.
3499         if (wcsprj->r0 == 0.0) {
3500           offset = 90.0;
3501         } else {
3502           offset = wcsprj->r0*PI/2.0;
3503         }
3504 
3505         // Stack faces in a cube.
3506         img = imgcrd;
3507         for (k = 0; k < ncoord; k++) {
3508           if (*(img+wcs->lat) < -0.5*offset) {
3509             *(img+wcs->lat) += offset;
3510             *(img+wcs->cubeface) = 5.0;
3511           } else if (*(img+wcs->lat) > 0.5*offset) {
3512             *(img+wcs->lat) -= offset;
3513             *(img+wcs->cubeface) = 0.0;
3514           } else if (*(img+i) > 2.5*offset) {
3515             *(img+i) -= 3.0*offset;
3516             *(img+wcs->cubeface) = 4.0;
3517           } else if (*(img+i) > 1.5*offset) {
3518             *(img+i) -= 2.0*offset;
3519             *(img+wcs->cubeface) = 3.0;
3520           } else if (*(img+i) > 0.5*offset) {
3521             *(img+i) -= offset;
3522             *(img+wcs->cubeface) = 2.0;
3523           } else {
3524             *(img+wcs->cubeface) = 1.0;
3525           }
3526 
3527           img += nelem;
3528         }
3529       }
3530 
3531     } else if (type == 3 || type == 4) {
3532       // Check for constancy.
3533       nwrld = ncoord;
3534       if ((isospec = wcsutil_allEq(ncoord, nelem, world+i))) {
3535         nwrld = 1;
3536       }
3537 
3538       istat = 0;
3539       if (wcs->types[i] == 3300) {
3540         // Spectral coordinates.
3541         istat = spcs2x(&(wcs->spc), nwrld, nelem, nelem, world+i,
3542                        imgcrd+i, istatp);
3543         if (istat) {
3544           status = wcserr_set(WCS_ERRMSG(wcs_spcerr[istat]));
3545           if (status != WCSERR_BAD_WORLD) {
3546             goto cleanup;
3547           }
3548         }
3549       } else if (type == 4) {
3550         // Logarithmic coordinates.
3551         istat = logs2x(wcs->crval[i], nwrld, nelem, nelem, world+i,
3552                        imgcrd+i, istatp);
3553         if (istat) {
3554           status = wcserr_set(WCS_ERRMSG(wcs_logerr[istat]));
3555           if (status != WCSERR_BAD_WORLD) {
3556             goto cleanup;
3557           }
3558         }
3559       }
3560 
3561       // If constant, replicate values.
3562       if (isospec) {
3563         wcsutil_setAll(ncoord, nelem, imgcrd+i);
3564         wcsutil_setAli(ncoord, 1, istatp);
3565       }
3566 
3567       if (istat == 4) {
3568         wcsutil_setBit(ncoord, istatp, 1 << i, stat);
3569       }
3570     }
3571   }
3572 
3573 
3574   // Do tabular coordinates.
3575   for (itab = 0; itab < wcs->ntab; itab++) {
3576     istat = tabs2x(wcs->tab + itab, ncoord, nelem, world, imgcrd, istatp);
3577 
3578     if (istat) {
3579       status = wcserr_set(WCS_ERRMSG(wcs_taberr[istat]));
3580 
3581       if (status == WCSERR_BAD_WORLD) {
3582         bits = 0;
3583         for (m = 0; m < wcs->tab[itab].M; m++) {
3584           bits |= 1 << wcs->tab[itab].map[m];
3585         }
3586         wcsutil_setBit(ncoord, istatp, bits, stat);
3587 
3588       } else {
3589         goto cleanup;
3590       }
3591     }
3592   }
3593 
3594 
3595   // Zero the unused intermediate world coordinate elements.
3596   for (i = wcs->naxis; i < nelem; i++) {
3597     imgcrd[i] = 0.0;
3598     wcsutil_setAll(ncoord, nelem, imgcrd+i);
3599   }
3600 
3601 
3602   // Apply world-to-pixel linear transformation.
3603   if ((istat = linx2p(&(wcs->lin), ncoord, nelem, imgcrd, pixcrd))) {
3604     status = wcserr_set(WCS_ERRMSG(wcs_linerr[istat]));
3605     goto cleanup;
3606   }
3607 
3608 cleanup:
3609   free(istatp);
3610   return status;
3611 }
3612 
3613 //----------------------------------------------------------------------------
3614 
wcsmix(struct wcsprm * wcs,int mixpix,int mixcel,const double vspan[2],double vstep,int viter,double world[],double phi[],double theta[],double imgcrd[],double pixcrd[])3615 int wcsmix(
3616   struct wcsprm *wcs,
3617   int mixpix,
3618   int mixcel,
3619   const double vspan[2],
3620   double vstep,
3621   int viter,
3622   double world[],
3623   double phi[],
3624   double theta[],
3625   double imgcrd[],
3626   double pixcrd[])
3627 
3628 {
3629   static const char *function = "wcsmix";
3630 
3631   const int niter = 60;
3632   int    crossed, istep, iter, j, k, nstep, retry, stat[1], status;
3633   const double tol  = 1.0e-10;
3634   const double tol2 = 100.0*tol;
3635   double *worldlat, *worldlng;
3636   double lambda, span[2], step;
3637   double pixmix;
3638   double dlng, lng, lng0, lng0m, lng1, lng1m;
3639   double dlat, lat, lat0, lat0m, lat1, lat1m;
3640   double d, d0, d0m, d1, d1m, dx = 0.0;
3641   double dabs, dmin, lmin;
3642   double dphi, phi0, phi1;
3643   struct celprm *wcscel = &(wcs->cel);
3644   struct wcsprm wcs0;
3645   struct wcserr **err;
3646 
3647   // Initialize if required.
3648   if (wcs == 0x0) return WCSERR_NULL_POINTER;
3649   err = &(wcs->err);
3650 
3651   if (wcs->flag != WCSSET) {
3652     if ((status = wcsset(wcs))) return status;
3653   }
3654 
3655   if (wcs->lng < 0 || wcs->lat < 0) {
3656     return wcserr_set(WCSERR_SET(WCSERR_BAD_SUBIMAGE),
3657       "Image does not have celestial axes");
3658   }
3659 
3660   worldlng = world + wcs->lng;
3661   worldlat = world + wcs->lat;
3662 
3663 
3664   // Check vspan.
3665   if (vspan[0] <= vspan[1]) {
3666     span[0] = vspan[0];
3667     span[1] = vspan[1];
3668   } else {
3669     // Swap them.
3670     span[0] = vspan[1];
3671     span[1] = vspan[0];
3672   }
3673 
3674   // Check vstep.
3675   step = fabs(vstep);
3676   if (step == 0.0) {
3677     step = (span[1] - span[0])/10.0;
3678     if (step > 1.0 || step == 0.0) step = 1.0;
3679   }
3680 
3681   // Check viter.
3682   nstep = viter;
3683   if (nstep < 5) {
3684     nstep = 5;
3685   } else if (nstep > 10) {
3686     nstep = 10;
3687   }
3688 
3689   // Given pixel element.
3690   pixmix = pixcrd[mixpix];
3691 
3692   // Iterate on the step size.
3693   for (istep = 0; istep <= nstep; istep++) {
3694     if (istep) step /= 2.0;
3695 
3696     // Iterate on the sky coordinate between the specified range.
3697     if (mixcel == 1) {
3698       // Celestial longitude is given.
3699 
3700       // Check whether the solution interval is a crossing interval.
3701       lat0 = span[0];
3702       *worldlat = lat0;
3703       if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3704                            stat))) {
3705         if (status == WCSERR_BAD_WORLD) {
3706           status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3707         }
3708         return status;
3709       }
3710       d0 = pixcrd[mixpix] - pixmix;
3711 
3712       dabs = fabs(d0);
3713       if (dabs < tol) return WCSERR_SUCCESS;
3714 
3715       lat1 = span[1];
3716       *worldlat = lat1;
3717       if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3718                            stat))) {
3719         if (status == WCSERR_BAD_WORLD) {
3720           status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3721         }
3722         return status;
3723       }
3724       d1 = pixcrd[mixpix] - pixmix;
3725 
3726       dabs = fabs(d1);
3727       if (dabs < tol) return WCSERR_SUCCESS;
3728 
3729       lmin = lat1;
3730       dmin = dabs;
3731 
3732       // Check for a crossing point.
3733       if (signbit(d0) != signbit(d1)) {
3734         crossed = 1;
3735         dx = d1;
3736       } else {
3737         crossed = 0;
3738         lat0 = span[1];
3739       }
3740 
3741       for (retry = 0; retry < 4; retry++) {
3742         // Refine the solution interval.
3743         while (lat0 > span[0]) {
3744           lat0 -= step;
3745           if (lat0 < span[0]) lat0 = span[0];
3746           *worldlat = lat0;
3747           if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3748                                stat))) {
3749             if (status == WCSERR_BAD_WORLD) {
3750               status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3751             }
3752             return status;
3753           }
3754           d0 = pixcrd[mixpix] - pixmix;
3755 
3756           // Check for a solution.
3757           dabs = fabs(d0);
3758           if (dabs < tol) return WCSERR_SUCCESS;
3759 
3760           // Record the point of closest approach.
3761           if (dabs < dmin) {
3762             lmin = lat0;
3763             dmin = dabs;
3764           }
3765 
3766           // Check for a crossing point.
3767           if (signbit(d0) != signbit(d1)) {
3768             crossed = 2;
3769             dx = d0;
3770             break;
3771           }
3772 
3773           // Advance to the next subinterval.
3774           lat1 = lat0;
3775           d1 = d0;
3776         }
3777 
3778         if (crossed) {
3779           // A crossing point was found.
3780           for (iter = 0; iter < niter; iter++) {
3781             // Use regula falsi division of the interval.
3782             lambda = d0/(d0-d1);
3783             if (lambda < 0.1) {
3784               lambda = 0.1;
3785             } else if (lambda > 0.9) {
3786               lambda = 0.9;
3787             }
3788 
3789             dlat = lat1 - lat0;
3790             lat = lat0 + lambda*dlat;
3791             *worldlat = lat;
3792             if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3793                                  stat))) {
3794               if (status == WCSERR_BAD_WORLD) {
3795                 status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3796               }
3797               return status;
3798             }
3799 
3800             // Check for a solution.
3801             d = pixcrd[mixpix] - pixmix;
3802             dabs = fabs(d);
3803             if (dabs < tol) return WCSERR_SUCCESS;
3804 
3805             if (dlat < tol) {
3806               // An artifact of numerical imprecision.
3807               if (dabs < tol2) return WCSERR_SUCCESS;
3808 
3809               // Must be a discontinuity.
3810               break;
3811             }
3812 
3813             // Record the point of closest approach.
3814             if (dabs < dmin) {
3815               lmin = lat;
3816               dmin = dabs;
3817             }
3818 
3819             if (signbit(d0) == signbit(d)) {
3820               lat0 = lat;
3821               d0 = d;
3822             } else {
3823               lat1 = lat;
3824               d1 = d;
3825             }
3826           }
3827 
3828           // No convergence, must have been a discontinuity.
3829           if (crossed == 1) lat0 = span[1];
3830           lat1 = lat0;
3831           d1 = dx;
3832           crossed = 0;
3833 
3834         } else {
3835           // No crossing point; look for a tangent point.
3836           if (lmin == span[0]) break;
3837           if (lmin == span[1]) break;
3838 
3839           lat = lmin;
3840           lat0 = lat - step;
3841           if (lat0 < span[0]) lat0 = span[0];
3842           lat1 = lat + step;
3843           if (lat1 > span[1]) lat1 = span[1];
3844 
3845           *worldlat = lat0;
3846           if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3847                                stat))) {
3848             if (status == WCSERR_BAD_WORLD) {
3849               status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3850             }
3851             return status;
3852           }
3853           d0 = fabs(pixcrd[mixpix] - pixmix);
3854 
3855           d  = dmin;
3856 
3857           *worldlat = lat1;
3858           if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3859                                stat))) {
3860             if (status == WCSERR_BAD_WORLD) {
3861               status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3862             }
3863             return status;
3864           }
3865           d1 = fabs(pixcrd[mixpix] - pixmix);
3866 
3867           for (iter = 0; iter < niter; iter++) {
3868             lat0m = (lat0 + lat)/2.0;
3869             *worldlat = lat0m;
3870             if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3871                                  stat))) {
3872               if (status == WCSERR_BAD_WORLD) {
3873                 status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3874               }
3875               return status;
3876             }
3877             d0m = fabs(pixcrd[mixpix] - pixmix);
3878 
3879             if (d0m < tol) return WCSERR_SUCCESS;
3880 
3881             lat1m = (lat1 + lat)/2.0;
3882             *worldlat = lat1m;
3883             if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3884                                  stat))) {
3885               if (status == WCSERR_BAD_WORLD) {
3886                 status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3887               }
3888               return status;
3889             }
3890             d1m = fabs(pixcrd[mixpix] - pixmix);
3891 
3892             if (d1m < tol) return WCSERR_SUCCESS;
3893 
3894             if (d0m < d && d0m <= d1m) {
3895               lat1 = lat;
3896               d1   = d;
3897               lat  = lat0m;
3898               d    = d0m;
3899             } else if (d1m < d) {
3900               lat0 = lat;
3901               d0   = d;
3902               lat  = lat1m;
3903               d    = d1m;
3904             } else {
3905               lat0 = lat0m;
3906               d0   = d0m;
3907               lat1 = lat1m;
3908               d1   = d1m;
3909             }
3910           }
3911         }
3912       }
3913 
3914     } else {
3915       // Celestial latitude is given.
3916 
3917       // Check whether the solution interval is a crossing interval.
3918       lng0 = span[0];
3919       *worldlng = lng0;
3920       if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3921                            stat))) {
3922         if (status == WCSERR_BAD_WORLD) {
3923           status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3924         }
3925         return status;
3926       }
3927       d0 = pixcrd[mixpix] - pixmix;
3928 
3929       dabs = fabs(d0);
3930       if (dabs < tol) return WCSERR_SUCCESS;
3931 
3932       lng1 = span[1];
3933       *worldlng = lng1;
3934       if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3935                            stat))) {
3936         if (status == WCSERR_BAD_WORLD) {
3937           status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3938         }
3939         return status;
3940       }
3941       d1 = pixcrd[mixpix] - pixmix;
3942 
3943       dabs = fabs(d1);
3944       if (dabs < tol) return WCSERR_SUCCESS;
3945       lmin = lng1;
3946       dmin = dabs;
3947 
3948       // Check for a crossing point.
3949       if (signbit(d0) != signbit(d1)) {
3950         crossed = 1;
3951         dx = d1;
3952       } else {
3953         crossed = 0;
3954         lng0 = span[1];
3955       }
3956 
3957       for (retry = 0; retry < 4; retry++) {
3958         // Refine the solution interval.
3959         while (lng0 > span[0]) {
3960           lng0 -= step;
3961           if (lng0 < span[0]) lng0 = span[0];
3962           *worldlng = lng0;
3963           if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
3964                                stat))) {
3965             if (status == WCSERR_BAD_WORLD) {
3966               status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
3967             }
3968             return status;
3969           }
3970           d0 = pixcrd[mixpix] - pixmix;
3971 
3972           // Check for a solution.
3973           dabs = fabs(d0);
3974           if (dabs < tol) return WCSERR_SUCCESS;
3975 
3976           // Record the point of closest approach.
3977           if (dabs < dmin) {
3978             lmin = lng0;
3979             dmin = dabs;
3980           }
3981 
3982           // Check for a crossing point.
3983           if (signbit(d0) != signbit(d1)) {
3984             crossed = 2;
3985             dx = d0;
3986             break;
3987           }
3988 
3989           // Advance to the next subinterval.
3990           lng1 = lng0;
3991           d1 = d0;
3992         }
3993 
3994         if (crossed) {
3995           // A crossing point was found.
3996           for (iter = 0; iter < niter; iter++) {
3997             // Use regula falsi division of the interval.
3998             lambda = d0/(d0-d1);
3999             if (lambda < 0.1) {
4000               lambda = 0.1;
4001             } else if (lambda > 0.9) {
4002               lambda = 0.9;
4003             }
4004 
4005             dlng = lng1 - lng0;
4006             lng = lng0 + lambda*dlng;
4007             *worldlng = lng;
4008             if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
4009                                  stat))) {
4010               if (status == WCSERR_BAD_WORLD) {
4011                 status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
4012               }
4013               return status;
4014             }
4015 
4016             // Check for a solution.
4017             d = pixcrd[mixpix] - pixmix;
4018             dabs = fabs(d);
4019             if (dabs < tol) return WCSERR_SUCCESS;
4020 
4021             if (dlng < tol) {
4022               // An artifact of numerical imprecision.
4023               if (dabs < tol2) return WCSERR_SUCCESS;
4024 
4025               // Must be a discontinuity.
4026               break;
4027             }
4028 
4029             // Record the point of closest approach.
4030             if (dabs < dmin) {
4031               lmin = lng;
4032               dmin = dabs;
4033             }
4034 
4035             if (signbit(d0) == signbit(d)) {
4036               lng0 = lng;
4037               d0 = d;
4038             } else {
4039               lng1 = lng;
4040               d1 = d;
4041             }
4042           }
4043 
4044           // No convergence, must have been a discontinuity.
4045           if (crossed == 1) lng0 = span[1];
4046           lng1 = lng0;
4047           d1 = dx;
4048           crossed = 0;
4049 
4050         } else {
4051           // No crossing point; look for a tangent point.
4052           if (lmin == span[0]) break;
4053           if (lmin == span[1]) break;
4054 
4055           lng = lmin;
4056           lng0 = lng - step;
4057           if (lng0 < span[0]) lng0 = span[0];
4058           lng1 = lng + step;
4059           if (lng1 > span[1]) lng1 = span[1];
4060 
4061           *worldlng = lng0;
4062           if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
4063                                stat))) {
4064             if (status == WCSERR_BAD_WORLD) {
4065               status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
4066             }
4067             return status;
4068           }
4069           d0 = fabs(pixcrd[mixpix] - pixmix);
4070 
4071           d  = dmin;
4072 
4073           *worldlng = lng1;
4074           if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
4075                                stat))) {
4076             if (status == WCSERR_BAD_WORLD) {
4077               status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
4078             }
4079             return status;
4080           }
4081           d1 = fabs(pixcrd[mixpix] - pixmix);
4082 
4083           for (iter = 0; iter < niter; iter++) {
4084             lng0m = (lng0 + lng)/2.0;
4085             *worldlng = lng0m;
4086             if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
4087                                  stat))) {
4088               if (status == WCSERR_BAD_WORLD) {
4089                 status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
4090               }
4091               return status;
4092             }
4093             d0m = fabs(pixcrd[mixpix] - pixmix);
4094 
4095             if (d0m < tol) return WCSERR_SUCCESS;
4096 
4097             lng1m = (lng1 + lng)/2.0;
4098             *worldlng = lng1m;
4099             if ((status = wcss2p(wcs, 1, 0, world, phi, theta, imgcrd, pixcrd,
4100                                  stat))) {
4101               if (status == WCSERR_BAD_WORLD) {
4102                 status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
4103               }
4104               return status;
4105             }
4106             d1m = fabs(pixcrd[mixpix] - pixmix);
4107 
4108             if (d1m < tol) return WCSERR_SUCCESS;
4109 
4110             if (d0m < d && d0m <= d1m) {
4111               lng1 = lng;
4112               d1   = d;
4113               lng  = lng0m;
4114               d    = d0m;
4115             } else if (d1m < d) {
4116               lng0 = lng;
4117               d0   = d;
4118               lng  = lng1m;
4119               d    = d1m;
4120             } else {
4121               lng0 = lng0m;
4122               d0   = d0m;
4123               lng1 = lng1m;
4124               d1   = d1m;
4125             }
4126           }
4127         }
4128       }
4129     }
4130   }
4131 
4132 
4133   // Set cel0 to the unity transformation.
4134   wcs0 = *wcs;
4135   wcs0.cel.euler[0] = -90.0;
4136   wcs0.cel.euler[1] =   0.0;
4137   wcs0.cel.euler[2] =  90.0;
4138   wcs0.cel.euler[3] =   1.0;
4139   wcs0.cel.euler[4] =   0.0;
4140 
4141   // No convergence, check for aberrant behaviour at a native pole.
4142   *theta = -90.0;
4143   for (j = 1; j <= 2; j++) {
4144     // Could the celestial coordinate element map to a native pole?
4145     *phi = 0.0;
4146     *theta = -*theta;
4147     sphx2s(wcscel->euler, 1, 1, 1, 1, phi, theta, &lng, &lat);
4148 
4149     if (mixcel == 1) {
4150       if (fabs(fmod(*worldlng-lng, 360.0)) > tol) continue;
4151       if (lat < span[0]) continue;
4152       if (lat > span[1]) continue;
4153       *worldlat = lat;
4154     } else {
4155       if (fabs(*worldlat-lat) > tol) continue;
4156       if (lng < span[0]) lng += 360.0;
4157       if (lng > span[1]) lng -= 360.0;
4158       if (lng < span[0]) continue;
4159       if (lng > span[1]) continue;
4160       *worldlng = lng;
4161     }
4162 
4163     // Is there a solution for the given pixel coordinate element?
4164     lng = *worldlng;
4165     lat = *worldlat;
4166 
4167     // Feed native coordinates to wcss2p() with cel0 set to unity.
4168     *worldlng = -180.0;
4169     *worldlat = *theta;
4170     if ((status = wcss2p(&wcs0, 1, 0, world, phi, theta, imgcrd, pixcrd,
4171                          stat))) {
4172       wcserr_clear(err);
4173       wcs->err = wcs0.err;
4174       if (status == WCSERR_BAD_WORLD) {
4175         status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
4176       }
4177       return status;
4178     }
4179     d0 = pixcrd[mixpix] - pixmix;
4180 
4181     // Check for a solution.
4182     if (fabs(d0) < tol) {
4183       // Recall saved world coordinates.
4184       *worldlng = lng;
4185       *worldlat = lat;
4186       return WCSERR_SUCCESS;
4187     }
4188 
4189     // Search for a crossing interval.
4190     phi0 = -180.0;
4191     for (k = -179; k <= 180; k++) {
4192       phi1 = (double) k;
4193       *worldlng = phi1;
4194       if ((status = wcss2p(&wcs0, 1, 0, world, phi, theta, imgcrd, pixcrd,
4195                            stat))) {
4196         wcserr_clear(err);
4197         wcs->err = wcs0.err;
4198         if (status == WCSERR_BAD_WORLD) {
4199           status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
4200         }
4201         return status;
4202       }
4203       d1 = pixcrd[mixpix] - pixmix;
4204 
4205       // Check for a solution.
4206       dabs = fabs(d1);
4207       if (dabs < tol) {
4208         // Recall saved world coordinates.
4209         *worldlng = lng;
4210         *worldlat = lat;
4211         return WCSERR_SUCCESS;
4212       }
4213 
4214       // Is it a crossing interval?
4215       if (signbit(d0) != signbit(d1)) break;
4216 
4217       phi0 = phi1;
4218       d0 = d1;
4219     }
4220 
4221     for (iter = 1; iter <= niter; iter++) {
4222       // Use regula falsi division of the interval.
4223       lambda = d0/(d0-d1);
4224       if (lambda < 0.1) {
4225         lambda = 0.1;
4226       } else if (lambda > 0.9) {
4227         lambda = 0.9;
4228       }
4229 
4230       dphi = phi1 - phi0;
4231       *worldlng = phi0 + lambda*dphi;
4232       if ((status = wcss2p(&wcs0, 1, 0, world, phi, theta, imgcrd, pixcrd,
4233                            stat))) {
4234         wcserr_clear(err);
4235         wcs->err = wcs0.err;
4236         if (status == WCSERR_BAD_WORLD) {
4237           status = wcserr_set(WCS_ERRMSG(WCSERR_BAD_WORLD_COORD));
4238         }
4239         return status;
4240       }
4241 
4242       // Check for a solution.
4243       d = pixcrd[mixpix] - pixmix;
4244       dabs = fabs(d);
4245       if (dabs < tol || (dphi < tol && dabs < tol2)) {
4246         // Recall saved world coordinates.
4247         *worldlng = lng;
4248         *worldlat = lat;
4249         return WCSERR_SUCCESS;
4250       }
4251 
4252       if (signbit(d0) == signbit(d)) {
4253         phi0 = *worldlng;
4254         d0 = d;
4255       } else {
4256         phi1 = *worldlng;
4257         d1 = d;
4258       }
4259     }
4260   }
4261 
4262 
4263   // No solution.
4264   return wcserr_set(WCS_ERRMSG(WCSERR_NO_SOLUTION));
4265 }
4266 
4267 //----------------------------------------------------------------------------
4268 
wcsccs(struct wcsprm * wcs,double lng2P1,double lat2P1,double lng1P2,const char * clng,const char * clat,const char * radesys,double equinox,const char * alt)4269 int wcsccs(
4270   struct wcsprm *wcs,
4271   double lng2P1,
4272   double lat2P1,
4273   double lng1P2,
4274   const char *clng,
4275   const char *clat,
4276   const char *radesys,
4277   double equinox,
4278   const char *alt)
4279 
4280 {
4281   static const char *function = "wcsccs";
4282 
4283   int status;
4284 
4285   // Initialize if required.
4286   if (wcs == 0x0) return WCSERR_NULL_POINTER;
4287   struct wcserr **err = &(wcs->err);
4288 
4289   if (wcs->flag != WCSSET) {
4290     if ((status = wcsset(wcs))) return status;
4291   }
4292 
4293   if (wcs->lng < 0 || wcs->lat < 0) {
4294     return wcserr_set(WCSERR_SET(WCSERR_BAD_SUBIMAGE),
4295       "Image does not have celestial axes");
4296   }
4297 
4298   // (lng1XX,lat1XX)  ...longitude and latitude of XX in the old system.
4299   // (lng2XX,lat2XX)  ...longitude and latitude of XX in the new system.
4300   // XX = NP          ...natuve pole,
4301   //      P1          ...pole of the old system,
4302   //      P2          ...pole of the new system,
4303   //      FP          ...fiducial point.
4304 
4305   // Set up the transformation from the old to the new system.
4306   double euler12[5];
4307   euler12[0] = lng2P1;
4308   euler12[1] = 90.0 - lat2P1;
4309   euler12[2] = lng1P2;
4310   euler12[3] = cosd(euler12[1]);
4311   euler12[4] = sind(euler12[1]);
4312 
4313   // Transform coordinates of the fiducial point (FP) to the new system.
4314   double lng1FP = wcs->crval[wcs->lng];
4315   double lat1FP = wcs->crval[wcs->lat];
4316   double lng2FP, lat2FP;
4317   (void)sphx2s(euler12, 1, 1, 1, 1, &lng1FP, &lat1FP, &lng2FP, &lat2FP);
4318 
4319   // Compute native coordinates of the new pole (noting lat1P2 == lat2P1).
4320   double phiP2, thetaP2;
4321   (void)sphs2x(wcs->cel.euler, 1, 1, 1, 1, &lng1P2, &lat2P1,
4322                &phiP2, &thetaP2);
4323 
4324   if (fabs(lat2FP) == 90.0 || fabs(thetaP2) == 90.0) {
4325     // If one of the poles of the new system is at the fiducial point, then
4326     // lng2FP is indeterminate, and if one of them is at the native pole, then
4327     // phiP2 is indeterminate.  We have to work harder to obtain these values.
4328 
4329     // Compute coordinates of the native pole (NP) in the old and new systems.
4330     double phiNP = 0.0, thetaNP = 90.0;
4331     double lng1NP, lat1NP;
4332     (void)sphx2s(wcs->cel.euler, 1, 1, 1, 1, &phiNP, &thetaNP,
4333                  &lng1NP, &lat1NP);
4334 
4335     double lng2NP, lat2NP;
4336     (void)sphx2s(euler12, 1, 1, 1, 1, &lng1NP, &lat1NP, &lng2NP, &lat2NP);
4337 
4338     // Native latitude and longitude of the fiducial point, (phi0,theta0).
4339     double phiFP   = wcs->cel.prj.phi0;
4340     double thetaFP = wcs->cel.prj.theta0;
4341 
4342     if (fabs(lat2NP) == 90.0) {
4343       // Following WCS Paper II equations (3) and (4), we are free to choose
4344       // phiP2 and set lng2NP accordingly.  So set phiP2 to its default value
4345       // for the projection.
4346       if (thetaFP < lat2FP) {
4347         phiP2 = 0.0;
4348       } else {
4349         phiP2 = 180.0;
4350       }
4351 
4352       // Compute coordinates in the old system of test point X.
4353       double phiX = 0.0, thetaX = 0.0;
4354       double lng1X, lat1X;
4355       (void)sphx2s(wcs->cel.euler, 1, 1, 1, 1, &phiX, &thetaX,
4356                    &lng1X, &lat1X);
4357 
4358       // Ensure that lng1X is not indeterminate.
4359       if (fabs(lat1X) == 90.0) {
4360         phiX = 90.0;
4361         (void)sphx2s(wcs->cel.euler, 1, 1, 1, 1, &phiX, &thetaX,
4362                      &lng1X, &lat1X);
4363       }
4364 
4365       // Compute coordinates in the new system of test point X.
4366       double lng2X, lat2X;
4367       (void)sphx2s(euler12, 1, 1, 1, 1, &lng1X, &lat1X, &lng2X, &lat2X);
4368 
4369       // Apply WCS Paper II equations (3) and (4).
4370       if (lat2NP == +90.0) {
4371         lng2NP = lng2X + (phiP2 - phiX) + 180.0;
4372       } else {
4373         lng2NP = lng2X - (phiP2 - phiX);
4374       }
4375 
4376     } else {
4377       // For (lng2NP + 90, 0), WCS Paper II equation (5) reduces to
4378       // phi = phiP2 - 90.
4379       double lng2X = lng2NP + 90.0;
4380       double lat2X = 0.0;
4381       double lng1X, lat1X;
4382       (void)sphs2x(euler12, 1, 1, 1, 1, &lng2X, &lat2X, &lng1X, &lat1X);
4383 
4384       double phiX, thetaX;
4385       (void)sphs2x(wcs->cel.euler, 1, 1, 1, 1, &lng1X, &lat1X,
4386                    &phiX, &thetaX);
4387 
4388       phiP2 = phiX + 90.0;
4389     }
4390 
4391     // Compute the longitude of the fiducial point in the new system.
4392     double eulerN2[5];
4393     eulerN2[0] = lng2NP;
4394     eulerN2[1] = 90.0 - lat2NP;
4395     eulerN2[2] = phiP2;
4396     eulerN2[3] = cosd(eulerN2[1]);
4397     eulerN2[4] = sind(eulerN2[1]);
4398 
4399     (void)sphx2s(eulerN2, 1, 1, 1, 1, &phiFP, &thetaFP, &lng2FP, &lat2FP);
4400   }
4401 
4402   // Update reference values in wcsprm.
4403   wcs->flag = 0;
4404   wcs->crval[wcs->lng] = lng2FP;
4405   wcs->crval[wcs->lat] = lat2FP;
4406   wcs->lonpole = phiP2;
4407   wcs->latpole = thetaP2;
4408 
4409   // Update wcsprm::ctype.
4410   if (clng) {
4411     strncpy(wcs->ctype[wcs->lng], clng, 4);
4412     for (int i = 0; i < 4; i++) {
4413       if (wcs->ctype[wcs->lng][i] == '\0') {
4414         wcs->ctype[wcs->lng][i] = '-';
4415       }
4416     }
4417   }
4418 
4419   if (clat) {
4420     strncpy(wcs->ctype[wcs->lat], clat, 4);
4421     for (int i = 0; i < 4; i++) {
4422       if (wcs->ctype[wcs->lat][i] == '\0') {
4423         wcs->ctype[wcs->lat][i] = '-';
4424       }
4425     }
4426   }
4427 
4428   // Update auxiliary values.
4429   if (strncmp(wcs->ctype[wcs->lng], "RA--", 4) == 0 &&
4430       strncmp(wcs->ctype[wcs->lat], "DEC-", 4) == 0) {
4431     // Transforming to equatorial coordinates.
4432     if (radesys) {
4433       strncpy(wcs->radesys, radesys, 71);
4434     }
4435 
4436     if (equinox != 0.0) {
4437       wcs->equinox = equinox;
4438     }
4439   } else {
4440     // Meaningless for other than equatorial coordinates.
4441     memset(wcs->radesys, 0, 72);
4442     wcs->equinox = UNDEFINED;
4443   }
4444 
4445   if (alt && *alt) {
4446     wcs->alt[0] = *alt;
4447   }
4448 
4449   // Reset the struct.
4450   if ((status = wcsset(wcs))) return status;
4451 
4452   return WCSERR_SUCCESS;
4453 }
4454 
4455 //----------------------------------------------------------------------------
4456 
wcssptr(struct wcsprm * wcs,int * i,char ctype[9])4457 int wcssptr(
4458   struct wcsprm *wcs,
4459   int  *i,
4460   char ctype[9])
4461 
4462 {
4463   static const char *function = "wcssptr";
4464 
4465   int status;
4466 
4467   // Initialize if required.
4468   if (wcs == 0x0) return WCSERR_NULL_POINTER;
4469   struct wcserr **err = &(wcs->err);
4470 
4471   if (wcs->flag != WCSSET) {
4472     if ((status = wcsset(wcs))) return status;
4473   }
4474 
4475   int j;
4476   if ((j = *i) < 0) {
4477     if ((j = wcs->spec) < 0) {
4478       // Look for a linear spectral axis.
4479       for (j = 0; j < wcs->naxis; j++) {
4480         if (wcs->types[j]/100 == 30) {
4481           break;
4482         }
4483       }
4484 
4485       if (j >= wcs->naxis) {
4486         // No spectral axis.
4487         return wcserr_set(WCSERR_SET(WCSERR_BAD_SUBIMAGE),
4488           "No spectral axis found");
4489       }
4490     }
4491 
4492     *i = j;
4493   }
4494 
4495   // Translate the spectral axis.
4496   double cdelt, crval;
4497   if ((status = spctrne(wcs->ctype[j], wcs->crval[j], wcs->cdelt[j],
4498                         wcs->restfrq, wcs->restwav, ctype, &crval, &cdelt,
4499                         &(wcs->spc.err)))) {
4500     return wcserr_set(WCS_ERRMSG(wcs_spcerr[status]));
4501   }
4502 
4503 
4504   // Translate keyvalues.
4505   wcs->flag = 0;
4506   wcs->cdelt[j] = cdelt;
4507   wcs->crval[j] = crval;
4508   spctyp(ctype, 0x0, 0x0, 0x0, wcs->cunit[j], 0x0, 0x0, 0x0);
4509   strcpy(wcs->ctype[j], ctype);
4510 
4511   // This keeps things tidy if the spectral axis is linear.
4512   spcfree(&(wcs->spc));
4513   spcini(&(wcs->spc));
4514 
4515   // Reset the struct.
4516   if ((status = wcsset(wcs))) return status;
4517 
4518   return WCSERR_SUCCESS;
4519 }
4520 
4521 //----------------------------------------------------------------------------
4522 
4523 #define STRINGIZE(s) STRINGIFY(s)
4524 #define STRINGIFY(s) #s
4525 
wcslib_version(int vers[3])4526 const char *wcslib_version(
4527   int  vers[3])
4528 
4529 {
4530   static const char *wcsver = STRINGIZE(WCSLIB_VERSION);
4531 
4532   if (vers != 0x0) {
4533     vers[2] = 0;
4534     sscanf(wcsver, "%d.%d.%d", vers, vers+1, vers+2);
4535   }
4536 
4537   return wcsver;
4538 }
4539