FORS Pipeline Reference Manual  5.0.9
fors_cpl_wcs.c
1 /* $Id: fors_cpl_wcs.c,v 1.10 2010-09-14 07:49:30 cizzo Exp $
2  *
3  * This file is part of the ESO Common Pipeline Library
4  * Copyright (C) 2002-2010 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */
20 
21 /*
22  * $Author: cizzo $
23  * $Date: 2010-09-14 07:49:30 $
24  * $Revision: 1.10 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 // Ouch! WCSLIB exports its config.h which will clash with ours,
29 // so avoid that
30 //#ifdef HAVE_CONFIG_H
31 //#include <config.h>
32 //#endif
33 
34 /*----------------------------------------------------------------------------
35  Includes
36  ----------------------------------------------------------------------------*/
37 
38 #include "fors_cpl_wcs.h"
39 
40 #include "cpl.h"
41 
42 #include <fitsio.h>
43 #include <fitsio2.h>
44 #include <wcslib.h>
45 
46 //#include <cpl_propertylist_impl.h>
47 //Declare internal CPL functions:
48 cpl_propertylist *cpl_propertylist_from_fitsfile(fitsfile *file);
49 
50 // Note! ugly hack here. In CPL 4.0 the prototype was
51 // cpl_error_code cpl_propertylist_to_fitsfile(fitsfile *file, const cpl_propertylist *self);
52 //
53 // Later versions:
54 // cpl_error_code cpl_propertylist_to_fitsfile(fitsfile *file, const cpl_propertylist *, const char *);
55 // We declare this function always as
56 
57 cpl_error_code cpl_propertylist_to_fitsfile(fitsfile *file, const cpl_propertylist *self, const char *);
58 
59 // which accidentally works also with the CPL4.0 prototype because the
60 // 3rd superfluous argument is ignored
61 
62 #include <math.h>
63 #include <string.h>
64 
65 /*---------------------------------------------------------------------------*/
82 /*---------------------------------------------------------------------------*/
85 /*----------------------------------------------------------------------------
86  Type definition
87  ----------------------------------------------------------------------------*/
88 
90  struct wcsprm *wcsptr; /* WCSLIB structure */
91  int naxis; /* Number of dimensions of the image */
92  int *dims; /* Dimensions of image */
93 };
94 
95 /* Static routine declarations */
96 
97 static fors_cpl_wcs *fors_cpl_wcs_init(void);
98 static char *fors_cpl_wcs_plist2fitsstr(const cpl_propertylist *self, int *nkeys);
99 static cpl_propertylist *fors_cpl_wcs_fitsstr2plist(char *fitsstr);
100 
101 /* Useful conversion factors */
102 
103 #define DEGRAD 57.2957795130823229
104 
105 /* WCSLIB error messages */
106 
107 #define WCSLIB_ERRCODE_MAX 9
108 static char *wcslib_errmsgs[WCSLIB_ERRCODE_MAX+1] = {
109  "",
110  "WCSLIB undefined input structure pointer",
111  "WCSLIB unable to allocate required memory",
112  "WCSLIB linear transformation matrix is singular",
113  "WCSLIB invalid coordinate axis types",
114  "WCSLIB invalid parameter value",
115  "WCSLIB invalid coordinate transformation parameters",
116  "WCSLIB Ill-conditioned coordinate transformation parameters",
117  "WCSLIB One or more input coordinates invalid",
118  "WCSLIB One or more input coordinates invalid"};
119 
120 
121 /*-------------------------------------------------------------------------*/
122 /*-------------------------------------------------------------------------*/
123 
124 
125 /*--------------------------------------------------------------------------
126  [jmlarsen] This is a workaround for ffhdr2str() which has a severe memory error in CFITSIO 2.510,
127  fixed somewhere between versions 2.510 and 3.030
128 */
129 
130 static int fors_ffhdr2str(
131  fitsfile *fptr, /* I - FITS file pointer */
132  int exclude_comm, /* I - if TRUE, exclude commentary keywords */
133  char **exclist, /* I - list of excluded keyword names */
134  int nexc, /* I - number of names in exclist */
135  char **header, /* O - returned header string */
136  int *nkeys, /* O - returned number of 80-char keywords */
137  int *status) /* IO - error status */
138 /*
139  read header keywords into a long string of chars. This routine allocates
140  memory for the string, so the calling routine must eventually free the
141  memory when it is not needed any more. If exclude_comm is TRUE, then all
142  the COMMENT, HISTORY, and <blank> keywords will be excluded from the output
143  string of keywords. Any other list of keywords to be excluded may be
144  specified with the exclist parameter.
145 */
146 {
147  {
148  /* [jmlarsen] Use the actual CFITSIO function if the
149  version number is recent enough */
150  float version;
151  fits_get_version(&version);
152 
153  if (version >= 3.030)
154  return ffhdr2str(fptr, exclude_comm, exclist, nexc, header, nkeys, status);
155  }
156 
157  int casesn, match, exact, totkeys;
158  long ii, jj;
159  char keybuf[162], keyname[FLEN_KEYWORD], *headptr;
160 
161  *nkeys = 0;
162 
163  if (*status > 0)
164  return(*status);
165 
166  /* get number of keywords in the header (doesn't include END) */
167  if (ffghsp(fptr, &totkeys, NULL, status) > 0)
168  return(*status);
169 
170  /* allocate memory for all the keywords (multiple of 2880 bytes) */
171  *header = (char *) calloc ( (totkeys + 1) * 80 + 1, 1);
172  if (!(*header))
173  {
174  *status = MEMORY_ALLOCATION;
175  ffpmsg("failed to allocate memory to hold all the header keywords");
176  return(*status);
177  }
178 
179  headptr = *header;
180  casesn = FALSE;
181 
182  /* read every keyword */
183  for (ii = 1; ii <= totkeys; ii++)
184  {
185  ffgrec(fptr, ii, keybuf, status);
186  /* pad record with blanks so that it is at least 80 chars long */
187  strcat(keybuf,
188  " ");
189 
190  keyname[0] = '\0';
191  strncat(keyname, keybuf, 8); /* copy the keyword name */
192 
193  if (exclude_comm)
194  {
195  if (!FSTRCMP("COMMENT ", keyname) ||
196  !FSTRCMP("HISTORY ", keyname) ||
197  !FSTRCMP(" ", keyname) )
198  continue; /* skip this commentary keyword */
199  }
200 
201  /* does keyword match any names in the exclusion list? */
202  for (jj = 0; jj < nexc; jj++ )
203  {
204  ffcmps(exclist[jj], keyname, casesn, &match, &exact);
205  if (match)
206  break;
207  }
208 
209  if (jj == nexc)
210  {
211  /* not in exclusion list, add this keyword to the string */
212  strcpy(headptr, keybuf);
213  headptr += 80;
214  (*nkeys)++;
215  }
216  }
217 
218  /* add the END keyword */
219  strcpy(headptr,
220  "END ");
221  headptr += 80;
222  (*nkeys)++;
223 
224  *headptr = '\0'; /* terminate the header string */
225 
226  *header = realloc(*header, (*nkeys *80) + 1); /* minimize the allocated memory */
227 
228  return(*status);
229 }
230 
231 
232 
233 
234 /*
235  * @brief
236  * Create a wcs structure by parsing a propertylist.
237  *
238  * @param plist The input propertylist
239  *
240  * @return
241  * The newly created and filled fors_cpl_wcs object or NULL if it could not be
242  * created. In the latter case an appropriate error code is set.
243  *
244  * @error
245  * <table class="ec" align="center">
246  * <tr>
247  * <td class="ecl">CPL_ERROR_NULL_INPUT</td>
248  * <td class="ecr">
249  * The parameter <i>plist</i> is a <tt>NULL</tt> pointer.
250  * </td>
251  * </tr>
252  * <tr>
253  * <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
254  * <td class="ecr">
255  * NAXIS information in image propertylist is not an integer
256  * </td>
257  * </tr>
258  * <tr>
259  * <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
260  * <td class="ecr">
261  * Error in getting NAXIS information for image propertylists
262  * </td>
263  * </tr>
264  * </table>
265  * @enderror
266  *
267  * The function allocates memory for a WCS structure. A pointer to the WCSLIB
268  * header information is created by parsing the FITS WCS keywords from the
269  * header of a file. A few ancillary items are also filled in.
270  *
271  * The returned property must be destroyed using the wcs destructor
272  * @b fors_cpl_wcs_delete().
273  *
274  * @see fors_cpl_wcs_delete()
275  */
276 
277 fors_cpl_wcs *fors_cpl_wcs_new_from_propertylist(const cpl_propertylist *plist) {
278  const char *_id = "fors_cpl_wcs_new";
279  char *shdr,nax[9];
280  fors_cpl_wcs *wcs;
281  int retval,nrej,nwcs,np,i;
282  struct wcsprm *wwcs = NULL;
283 
284  /* Check to see if the propertylist exists */
285 
286  if (plist == NULL) {
287  cpl_error_set(_id,CPL_ERROR_NULL_INPUT);
288  return(NULL);
289  }
290 
291  /* Get the size of the propertylist.
292  jmlarsen: No, it will overrun a buffer
293  np = cpl_propertylist_get_size(plist);
294  */
295 
296  /* Get a fors_cpl_wcs structure and initialise it */
297 
298  if ((wcs = fors_cpl_wcs_init()) == NULL)
299  return(NULL);
300 
301  /* Convert the propertylist into a string */
302 
303  shdr = fors_cpl_wcs_plist2fitsstr(plist, &np);
304  if (! shdr) {
305  cpl_error_set_where(_id);
306  fors_cpl_wcs_delete(wcs);
307  return(NULL);
308  }
309 
310  /* Parse the header to get the wcslib structure */
311  retval = wcspih(shdr,np,0,0,&nrej,&nwcs,&wwcs);
312 
313  if (retval != 0) {
314  cpl_msg_warning(_id,"Cannot parse WCS header");
315  free(shdr);
316  wcsvfree(&nwcs,&wwcs);
317  fors_cpl_wcs_delete(wcs);
318  return(NULL);
319  }
320  free(shdr);
321 
322  /* Now extract the one you want and ditch the rest */
323 
324  wcs->wcsptr = (struct wcsprm *)cpl_calloc(1,sizeof(struct wcsprm));
325  (wcs->wcsptr)->flag = -1;
326  wcscopy(1,wwcs,wcs->wcsptr);
327  wcsset(wcs->wcsptr);
328  wcsvfree(&nwcs,&wwcs);
329 
330  /* Fill the rest of the stuff */
331 
332  if (cpl_propertylist_has(plist,"NAXIS")) {
333  wcs->naxis = cpl_propertylist_get_int(plist,"NAXIS");
334  if (cpl_error_get_code() != CPL_ERROR_NONE) {
335  fors_cpl_wcs_delete(wcs);
336  return(NULL);
337  }
338  wcs->dims = cpl_calloc(wcs->naxis,sizeof(int));
339  for (i = 1; i <= wcs->naxis; i++) {
340  (void)snprintf(nax,9,"NAXIS%d",i);
341  wcs->dims[i-1] = cpl_propertylist_get_int(plist,nax);
342  if (cpl_error_get_code() != CPL_ERROR_NONE) {
343  fors_cpl_wcs_delete(wcs);
344  return(NULL);
345  }
346  }
347  }
348 
349  /* Get out of here */
350 
351  return(wcs);
352 }
353 
368 
369  /* Free the workspace */
370 
371  if (wcs == NULL)
372  return;
373  if (wcs->wcsptr != NULL) {
374  (void)wcsfree(wcs->wcsptr);
375  cpl_free(wcs->wcsptr);
376  }
377  if (wcs->dims != NULL)
378  cpl_free(wcs->dims);
379  cpl_free(wcs);
380 }
381 
382 /*
383  * @brief
384  * Convert between physical and world coordiantes.
385  *
386  * @param wcs The input fors_cpl_wcs structure
387  * @param from The input coordinate matrix
388  * @param to The output coordinate matrix
389  * @param status The output status array
390  * @param transform The transformation mode
391  *
392  * @return
393  * An appropriate error code.
394  *
395  * @error
396  * <table class="ec" align="center">
397  * <tr>
398  * <td class="ecl">CPL_ERROR_NULL_INPUT</td>
399  * <td class="ecr">
400  * The parameter <i>wcs</i> is a <tt>NULL</tt> pointer, the parameter
401  * <i>from</i> is a <tt>NULL</tt> pointer or <i>wcs</i> is missing
402  * some of its information.
403  * </td>
404  * </tr>
405  * <tr>
406  * <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
407  * <td class="ecr">
408  * No rows or columns in the input matrix, or an unspecified error
409  * has occurred in the WCSLIB routine
410  * </td>
411  * </tr>
412  * <tr>
413  * <td class="ecl">CPL_ERROR_UNSUPPORTED_MODE</td>
414  * <td class="ecr">
415  * The input conversion mode is not supported
416  * </td>
417  * </tr>
418  * </table>
419  * @enderror
420  *
421  * This function converts between coordinates. The supported modes are:
422  * -- FORS_CPL_WCS_PHYS2WORLD: Converts input physical to world coordinates
423  * -- FORS_CPL_WCS_WORLD2PHYS: Converts input world to physical coordinates
424  * -- FORS_CPL_WCS_WORLD2STD: Converts input world to standard coordinates
425  * -- FORS_CPL_WCS_PHYS2STD: Converts input physical to standard coordinates
426  * The output matrix and status arrays will be allocated here, and thus will
427  * need to be freed by the calling routine. The status array is used to flag
428  * input coordinates where there has been some sort of failure in the
429  * transformation.
430  *
431  */
432 
433 cpl_error_code fors_cpl_wcs_convert(const fors_cpl_wcs *wcs, const cpl_matrix *from,
434  cpl_matrix **to, cpl_array **status,
435  fors_cpl_wcs_trans_mode transform) {
436  int nrows,ncols,*sdata,retval,i;
437  cpl_matrix *x1;
438  cpl_array *x2,*x3;
439  cpl_error_code code;
440  double *fdata,*tdata,*x1data,*x2data,*x3data;
441  char *_id = "fors_cpl_wcs_convert";
442 
443  /* Initialise output */
444 
445  *to = NULL;
446  *status = NULL;
447 
448  /* Basic checks on the input pointers */
449 
450  if (wcs == NULL || wcs->wcsptr == NULL || from == NULL) {
451  cpl_error_set(_id,CPL_ERROR_NULL_INPUT);
452  return(CPL_ERROR_NULL_INPUT);
453  }
454 
455  /* Check the number of rows/columns for the input matrix */
456 
457  nrows = cpl_matrix_get_nrow(from);
458  ncols = cpl_matrix_get_ncol(from);
459  if (nrows == 0 || ncols == 0) {
460  cpl_error_set(_id,CPL_ERROR_UNSPECIFIED);
461  return(CPL_ERROR_UNSPECIFIED);
462  }
463 
464  /* Get the output memory and some memory for wcslib to use */
465 
466  *to = cpl_matrix_new(nrows,ncols);
467  *status = cpl_array_new(nrows,CPL_TYPE_INT);
468  x1 = cpl_matrix_new(nrows,ncols);
469  x2 = cpl_array_new(nrows,CPL_TYPE_DOUBLE);
470  x3 = cpl_array_new(nrows,CPL_TYPE_DOUBLE);
471 
472  /* Now get the pointers for the data arrays */
473 
474  fdata = cpl_matrix_get_data(from);
475  tdata = cpl_matrix_get_data(*to);
476  sdata = cpl_array_get_data_int(*status);
477  x1data = cpl_matrix_get_data(x1);
478  x2data = cpl_array_get_data_double(x2);
479  x3data = cpl_array_get_data_double(x3);
480 
481  /* Right, now switch for the transform type. First physical
482  to world coordinates */
483 
484  switch (transform) {
485  case FORS_CPL_WCS_PHYS2WORLD:
486  retval = wcsp2s(wcs->wcsptr,nrows,wcs->naxis,fdata,x1data,x2data,
487  x3data,tdata,sdata);
488  break;
489  case FORS_CPL_WCS_WORLD2PHYS:
490  retval = wcss2p(wcs->wcsptr,nrows,wcs->naxis,fdata,x2data,x3data,
491  x1data,tdata,sdata);
492  break;
493  case FORS_CPL_WCS_WORLD2STD:
494  retval = wcss2p(wcs->wcsptr,nrows,wcs->naxis,fdata,x2data,x3data,
495  tdata,x1data,sdata);
496  break;
497  case FORS_CPL_WCS_PHYS2STD:
498  retval = wcsp2s(wcs->wcsptr,nrows,wcs->naxis,fdata,tdata,x2data,
499  x3data,x1data,sdata);
500  break;
501  default:
502  cpl_error_set(_id,CPL_ERROR_UNSUPPORTED_MODE);
503  cpl_matrix_delete(*to);
504  *to = NULL;
505  cpl_array_delete(*status);
506  *status = NULL;
507  cpl_matrix_delete(x1);
508  return(CPL_ERROR_UNSUPPORTED_MODE);
509  }
510 
511  /* Ditch the intermediate coordinate results */
512 
513  cpl_matrix_delete(x1);
514  cpl_array_delete(x2);
515  cpl_array_delete(x3);
516 
517  /* Now interpret the return value from wcslib */
518 
519  switch (retval) {
520  case 0:
521  code = CPL_ERROR_NONE;
522  break;
523  case 1:
524  code = CPL_ERROR_NULL_INPUT;
525  cpl_msg_warning(__func__,wcslib_errmsgs[1]);
526  cpl_error_set(__func__,code);
527  break;
528  case 2:
529  case 3:
530  case 4:
531  case 5:
532  case 6:
533  case 7:
534  case 8:
535  case 9:
536  code = CPL_ERROR_UNSPECIFIED;
537  cpl_error_set(__func__,code);
538  cpl_msg_warning(__func__,wcslib_errmsgs[retval]);
539  break;
540  default:
541  code = CPL_ERROR_UNSPECIFIED;
542  cpl_error_set(__func__,code);
543  cpl_msg_warning(__func__,"WCSLIB found an unspecified error: %d",
544  retval);
545  break;
546  }
547  return(code);
548 }
549 
550 
551 
554 /*-------------------------------------------------------------------------*/
555 
570  fors_cpl_wcs *wcs = NULL;
571 
572  /* Get the main structure workspace */
573 
574  wcs = cpl_malloc(sizeof(fors_cpl_wcs));
575  if (wcs == NULL)
576  return(NULL);
577 
578  /* Initialise the output structure */
579 
580  wcs->wcsptr = NULL;
581  wcs->naxis = 0;
582  wcs->dims = NULL;
583 
584  return wcs;
585 }
586 
613 static char *fors_cpl_wcs_plist2fitsstr(const cpl_propertylist *self, int *nkeys) {
614 
615  const char *_id = "fors_cpl_wcs_plist2fitsstr";
616  char *header,*h,cval[2];
617  int n,status;
618  long lval,i;
619  float fval;
620  double dval;
621  fitsfile *fptr;
622  cpl_property *p;
623 
624  /* Sanity testing of input propertylist */
625 
626  if (self == NULL) {
627  cpl_error_set(_id,CPL_ERROR_NULL_INPUT);
628  return(NULL);
629  }
630 
631  /* Find out how big the propertylist is */
632 
633  n = cpl_propertylist_get_size(self);
634 
635  /* Open a memory file with CFITSIO */
636 
637  status = 0;
638  (void)fits_create_file(&fptr,"mem://",&status);
639  (void)fits_create_img(fptr,BYTE_IMG,0,NULL,&status);
640 
641  /* Add the properties into the FITS file */
642 
643  cpl_propertylist_to_fitsfile(fptr,self,NULL);
644 
645  /* Parse the header */
646 
647 // (void)fits_hdr2str(fptr,1,NULL,0,&header,&ival,&status);
648  (void)fors_ffhdr2str(fptr,1,NULL,0,&header,nkeys,&status);
649  (void)fits_close_file(fptr,&status);
650 
651  /* Get out of here */
652 
653  return(header);
654 
655 }
656 
682 static cpl_propertylist *fors_cpl_wcs_fitsstr2plist(char *fitsstr) {
683  int i,status;
684  fitsfile *fptr;
685  char *f,*_id="fors_cpl_wcs_fitsstr2plist";
686  cpl_propertylist *p;
687 
688  /* Check input */
689 
690  if (fitsstr == NULL) {
691  cpl_error_set(_id,CPL_ERROR_NULL_INPUT);
692  return(NULL);
693  }
694 
695  /* Create a memory file using CFITSIO. We can then add individual cards to
696  the header and allow CFITSIO to decide on data types etc. */
697 
698  status = 0;
699  (void)fits_create_file(&fptr,"mem://",&status);
700  (void)fits_create_img(fptr,BYTE_IMG,0,NULL,&status);
701 
702  /* Loop for all the cards in the FITS string and add them to the
703  memory FITS header */
704 
705  f = fitsstr;
706  while (strncmp(f,"END ",8)) {
707  (void)fits_insert_card(fptr,f,&status);
708  f += (FLEN_CARD - 1);
709  }
710  (void)fits_insert_card(fptr,f,&status);
711 
712  /* Now create the propertylist */
713 
714  p = cpl_propertylist_from_fitsfile(fptr);
715 
716  /* Close the memory file and get out of here */
717 
718  (void)fits_close_file(fptr,&status);
719  return(p);
720 }
721 
static char * fors_cpl_wcs_plist2fitsstr(const cpl_propertylist *self, int *nkeys)
Convert a propertylist to a FITS string.
Definition: fors_cpl_wcs.c:613
static cpl_propertylist * fors_cpl_wcs_fitsstr2plist(char *fitsstr)
Convert a FITS string to a propertylist.
Definition: fors_cpl_wcs.c:682
void fors_cpl_wcs_delete(fors_cpl_wcs *wcs)
Destroy a WCS structure.
Definition: fors_cpl_wcs.c:367
static fors_cpl_wcs * fors_cpl_wcs_init(void)
Create an empty wcs structure.
Definition: fors_cpl_wcs.c:569