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