gnuastro-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnuastro-commits] master 9067a69: Library (wcs.h): access to WCSLIB's w


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 9067a69: Library (wcs.h): access to WCSLIB's wcshdo() only within library
Date: Sun, 11 Jul 2021 19:18:16 -0400 (EDT)

branch: master
commit 9067a691996e605204557f2258960030fdadf7e1
Author: Mohammad Akhlaghi <mohammad@akhlaghi.org>
Commit: Mohammad Akhlaghi <mohammad@akhlaghi.org>

    Library (wcs.h): access to WCSLIB's wcshdo() only within library
    
    Until now, the Crop and MakeProfiles programs directly called the 'wcshdo'
    function of WCSLIB (that is in charge of writing the FITS
    keywords). However, in time we have found some peculiarities with the WCS
    keyword writing, for example if there is a TPV distortion, the CD matrix
    should be used or some cleaning of the written parameters (sometimes
    happening due to floating point errors).
    
    With this commit a new function has been added to the 'wcs.h' library of
    Gnuastro: 'gal_wcs_write_wcsstr' and the only place that we call WCSLIB's
    'wcshdo' within the whole of Gnuastro is within this function. This greatly
    helps in modularity and unifying the output behavior of all programs.
    
    In the same spirit, the final CD/PC matrix correction (after the keywords
    were written into the FITS file) have been moved to
    'gal_fits_key_write_wcsstr' and the check for seeing if a CD matrix should
    be used has been moved into a function and used in the
    'gal_wcs_read_fitsptr' function (which is the only place we read WCS in
    Gnuastro).
    
    The necessity for all this was found after noting that the 'wcstoimg'
    operator in column arithmetic is giving a WCSLIB error when the image has
    TPV distortion (bug #60909). After contacting Mark Calabretta, he kindly
    noted that the problem is in not using the CD matrix with TPV. I then
    discovered that the tests we had added for this were not actually being
    used by the Crop or MakeProfiles programs. So instead of repeating those
    checks in these two programs (which would certainly cause many headaches in
    the future), I made the generic changes above and now we have a single
    place for reading/writing the WCS that will account for all existing/future
    corrections.
    
    This fixes bug #60909.
---
 NEWS                         |  12 +++--
 bin/crop/onecrop.c           |  10 +---
 bin/crop/ui.c                |   8 +---
 bin/mkprof/main.h            |   2 +-
 bin/mkprof/mkprof.c          |   2 +-
 bin/mkprof/ui.c              |  39 +++++++--------
 doc/announce-acknowledge.txt |   1 +
 doc/gnuastro.texi            |   7 +++
 lib/fits.c                   |  34 +++++++++++++
 lib/gnuastro/wcs.h           |   3 ++
 lib/wcs.c                    | 111 ++++++++++++++++++++++---------------------
 11 files changed, 132 insertions(+), 97 deletions(-)

diff --git a/NEWS b/NEWS
index a1475f0..c0998da 100644
--- a/NEWS
+++ b/NEWS
@@ -37,6 +37,8 @@ See the end of the file for license conditions.
   Library:
    - Arithmetic macros:
      - GAL_ARITHMETIC_OP_BOX_AROUND_ELLIPSE
+   - gal_wcs_write_wcsstr: wrapper of wcshdo of WCSLIB to simplify generic
+     access to that low-level operation (has some extra sanity checks).
 
 ** Removed features
 
@@ -56,15 +58,17 @@ See the end of the file for license conditions.
 ** Bugs fixed
   bug #60725: MakeCatalog doesn't put comment on --halfsumsb column.
   bug #60776: Radial profile script not using standard deviation image,
-              this bug was fixed by Zahra Sharbaf.
+              fixed by Zahra Sharbaf.
   bug #60778: Brightness error not NaN when all STD pixels are blank,
-              this bug was reported by Zahra Sharbaf.
+              reported by Zahra Sharbaf.
   bug #60826: Arithmetic won't delete existing file with tofile operators.
   bug #60881: Query segmentation fault when NED is called without a dataset.
   bug #60901: sort-by-night crash during make check due to stdintimeout,
-              this bug was reported by Zahra Sharbaf.
+              reported by Zahra Sharbaf.
   bug #60903: Increasing value of stdintimeout not effective beyond 1000000,
-              this bug was reported by Zahra Sharbaf.
+              reported by Zahra Sharbaf.
+  bug #60909: WCS coordinate conversion not working with TPV distortion,
+              fixed with the help of Mark Calabretta.
 
 
 
diff --git a/bin/crop/onecrop.c b/bin/crop/onecrop.c
index 470e966..f1c8bbd 100644
--- a/bin/crop/onecrop.c
+++ b/bin/crop/onecrop.c
@@ -613,15 +613,7 @@ onecrop_make_array(struct onecropparams *crp, long 
*fpixel_i,
   if(img->wcs)
     {
       /* Write the WCS title and common WCS information. */
-      if(fits_write_record(ofp, blankrec, &status))
-        gal_fits_io_error(status, NULL);
-      sprintf(titlerec, "%sWCS information", GAL_FITS_KEY_TITLE_START);
-      for(i=strlen(titlerec);i<79;++i)
-        titlerec[i]=' ';
-      fits_write_record(ofp, titlerec, &status);
-      for(i=0;i<img->nwcskeys-1;++i)
-        fits_write_record(ofp, &img->wcstxt[i*80], &status);
-      gal_fits_io_error(status, NULL);
+      gal_fits_key_write_wcsstr(ofp, img->wcs, img->wcstxt, img->nwcskeys);
 
       /* Correct the CRPIX keywords. */
       for(i=0;i<ndim;++i)
diff --git a/bin/crop/ui.c b/bin/crop/ui.c
index b22e4c9..892e101 100644
--- a/bin/crop/ui.c
+++ b/bin/crop/ui.c
@@ -1057,13 +1057,7 @@ ui_preparations(struct cropparams *p)
       img->wcs=gal_wcs_read_fitsptr(tmpfits, p->cp.wcslinearmatrix,
                                     p->hstartwcs, p->hendwcs, &img->nwcs);
       if(img->wcs)
-        {
-          gal_wcs_decompose_pc_cdelt(img->wcs);
-          status=wcshdo(0, img->wcs, &img->nwcskeys, &img->wcstxt);
-          if(status)
-            error(EXIT_FAILURE, 0, "wcshdo ERROR %d: %s", status,
-                  wcs_errmsg[status]);
-        }
+        img->wcstxt=gal_wcs_write_wcsstr(img->wcs, &img->nwcskeys);
       else
         if(p->mode==IMGCROP_MODE_WCS)
           error(EXIT_FAILURE, 0, "%s (hdu %s): the WCS structure is "
diff --git a/bin/mkprof/main.h b/bin/mkprof/main.h
index 7440f8c..d794946 100644
--- a/bin/mkprof/main.h
+++ b/bin/mkprof/main.h
@@ -188,7 +188,7 @@ struct mkprofparams
   pthread_cond_t     qready;  /* bq is ready to be written.               */
   pthread_mutex_t     qlock;  /* Mutex lock to change builtq.             */
   double          halfpixel;  /* Half pixel in oversampled image.         */
-  char           *wcsheader;  /* The WCS header information for main img. */
+  char              *wcsstr;  /* The WCS keywords derived from main img.  */
   int            wcsnkeyrec;  /* The number of keywords in the WCS header.*/
   char       *mergedimgname;  /* Name of merged image.                    */
   gal_data_t        *custom;  /* Table containing custom values.          */
diff --git a/bin/mkprof/mkprof.c b/bin/mkprof/mkprof.c
index 13b8cde..afcfaf1 100644
--- a/bin/mkprof/mkprof.c
+++ b/bin/mkprof/mkprof.c
@@ -162,7 +162,7 @@ saveindividual(struct mkonthread *mkp)
         crpix[i] = ((double *)(p->crpix->array))[i] - os*(mkp->fpixel_i[i]-1);
 
       /* Write the image. */
-      gal_fits_img_write_corr_wcs_str(ibq->image, filename, p->wcsheader,
+      gal_fits_img_write_corr_wcs_str(ibq->image, filename, p->wcsstr,
                                       p->wcsnkeyrec, crpix, NULL,
                                       PROGRAM_NAME);
     }
diff --git a/bin/mkprof/ui.c b/bin/mkprof/ui.c
index d12ede4..acbc2d0 100644
--- a/bin/mkprof/ui.c
+++ b/bin/mkprof/ui.c
@@ -1374,9 +1374,9 @@ static void
 ui_prepare_canvas(struct mkprofparams *p)
 {
   float *f, *ff;
+  int setshift=0;
   long width[3]={1,1,1};
   size_t tndim, *tdsize;
-  int status=0, setshift=0;
   double truncr, semiaxes[3], euler_deg[3];
   size_t i, nshift=0, *dsize=NULL, ndim_counter;
 
@@ -1528,19 +1528,6 @@ ui_prepare_canvas(struct mkprofparams *p)
       if(p->out->unit==NULL)
         gal_checkset_allocate_copy("Brightness", &p->out->unit);
     }
-
-
-  /* When individual mode is requested, write the WCS structure to a header
-     string to speed up the process: if we don't do it here, this process
-     will be necessary on every individual profile's output. So it is much
-     more efficient done once here. */
-  if(p->individual && p->wcs)
-    {
-      status=wcshdo(WCSHDO_safe, p->wcs, &p->wcsnkeyrec, &p->wcsheader);
-      if(status)
-        error(EXIT_FAILURE, 0, "wcshdo error %d: %s", status,
-              wcs_errmsg[status]);
-    }
 }
 
 
@@ -1859,11 +1846,22 @@ ui_preparations(struct mkprofparams *p)
   else
     ui_prepare_canvas(p);
 
-  /* Read the (possible) RA/Dec inputs into X and Y for the builder.  NOTE:
-     It may happen that there are no input columns, in that case, just
-     ignore this step.*/
-  if(p->wcs && p->num)
-    ui_finalize_coordinates(p);
+  /* Preparations now that we have WCS (if any was given in any way: either
+     from a background or from options).  */
+  if(p->wcs)
+    {
+      /* Read the (possible) RA/Dec inputs into X and Y for the builder.
+         NOTE: It may happen that there are no input columns, in that case,
+         just ignore this step.*/
+      if(p->num)
+        ui_finalize_coordinates(p);
+
+      /* If individual mode is activated, write the WCS as a string here
+         (earlier than needed). This is because it will be necessary for
+         every individual profile, but it will identical (except for the
+         'CRPIX's that will be changed). */
+      p->wcsstr=gal_wcs_write_wcsstr(p->wcs, &p->wcsnkeyrec);
+    }
 
   /* Prepare the random number generator. */
   p->rng=gal_checkset_gsl_rng(p->envseed, &p->rng_name, &p->rng_seed);
@@ -2064,8 +2062,7 @@ ui_free_report(struct mkprofparams *p, struct timeval *t1)
     }
 
   /* Free the WCS headers string that was defined for individual mode. */
-  if(p->individual)
-    free(p->wcsheader);
+  if(p->wcsstr) free(p->wcsstr);
 
   /* Free the random number generator: */
   gsl_rng_free(p->rng);
diff --git a/doc/announce-acknowledge.txt b/doc/announce-acknowledge.txt
index bf1d900..90c6cdf 100644
--- a/doc/announce-acknowledge.txt
+++ b/doc/announce-acknowledge.txt
@@ -1,5 +1,6 @@
 Alphabetically ordered list to acknowledge in the next release.
 
+Mark Calabretta
 Leslie Hunt
 Juan Molina Tobar
 Zahra Sharbaf
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index 12e0678..157c491 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -26810,6 +26810,13 @@ For example if @code{CTYPE1} is @code{RA---TAN}, the 
string that function return
 Recall that the second component of @code{CTYPEi} contains the type of 
projection.
 @end deftypefun
 
+@deftypefun {char *} gal_wcs_write_wcsstr (struct wcsprm @code{*wcs}, int 
@code{*nkeyrec})
+Return an allocated string which contains the respective FITS keywords for the 
given WCS structure into it.
+The number of keywords is written in the space pointed by @code{nkeyrec}.
+Each FITS keyword is 80 characters wide (according to the FITS standard), and 
the next one is placed immediately after it, so the full string has 
@code{80*nkeyrec} bytes.
+The output of this function can later be written into an opened FITS file 
using @code{gal_fits_key_write_wcsstr} (see @ref{FITS header keywords}).
+@end deftypefun
+
 @deftypefun void gal_wcs_write (struct wcsprm @code{*wcs}, char 
@code{*filename}, char @code{*extname}, gal_fits_list_key_t @code{*headers}, 
char @code{*program_string})
 Write the given WCS structure into the second extension of an empty FITS 
header.
 The first/primary extension will be empty like the default format of all 
Gnuastro outputs.
diff --git a/lib/fits.c b/lib/fits.c
index 140cf09..839671a 100644
--- a/lib/fits.c
+++ b/lib/fits.c
@@ -1895,6 +1895,9 @@ gal_fits_key_write_wcsstr(fitsfile *fptr, struct wcsprm 
*wcs,
   int status=0;
   char *keystart;
 
+  /* If the 'wcsstr' string is empty, don't do anything, simply return. */
+  if(wcsstr==NULL) return;
+
   /* Write the title. */
   gal_fits_key_write_title_in_ptr("World Coordinate System (WCS)", fptr);
 
@@ -1917,6 +1920,37 @@ gal_fits_key_write_wcsstr(fitsfile *fptr, struct wcsprm 
*wcs,
         }
     }
   gal_fits_io_error(status, NULL);
+
+   /* WCSLIB is going to write PC+CDELT keywords in any case. But when we
+      have a TPV, TNX or ZPX distortion, it is cleaner to use a CD matrix
+      (WCSLIB can't convert coordinates properly if the PC matrix is used
+      with the TPV distortion). So to help users avoid the potential
+      problems with WCSLIB, upon reading the WCS structure, in such cases,
+      we set CDELTi=1.0 and 'altlin=2' (signifying that the CD matrix
+      should be used). Therefore, effectively the CD matrix and PC matrix
+      are equivalent, we just need to convert the keyword names and delete
+      the CDELT keywords. Note that zero-valued PC/CD elements may not be
+      present, so we'll manually set 'status' to zero to avoid CFITSIO from
+      crashing. */
+  if(wcs && wcs->altlin==2)
+    {
+      status=0; fits_delete_str(fptr, "CDELT1", &status);
+      status=0; fits_delete_str(fptr, "CDELT2", &status);
+      status=0; fits_modify_name(fptr, "PC1_1", "CD1_1", &status);
+      status=0; fits_modify_name(fptr, "PC1_2", "CD1_2", &status);
+      status=0; fits_modify_name(fptr, "PC2_1", "CD2_1", &status);
+      status=0; fits_modify_name(fptr, "PC2_2", "CD2_2", &status);
+      if(wcs->naxis==3)
+        {
+          status=0; fits_delete_str(fptr, "CDELT3", &status);
+          status=0; fits_modify_name(fptr, "PC1_3", "CD1_3", &status);
+          status=0; fits_modify_name(fptr, "PC2_3", "CD2_3", &status);
+          status=0; fits_modify_name(fptr, "PC3_1", "CD3_1", &status);
+          status=0; fits_modify_name(fptr, "PC3_2", "CD3_2", &status);
+          status=0; fits_modify_name(fptr, "PC3_3", "CD3_3", &status);
+        }
+      status=0;
+    }
 }
 
 
diff --git a/lib/gnuastro/wcs.h b/lib/gnuastro/wcs.h
index dfe430a..4abe42b 100644
--- a/lib/gnuastro/wcs.h
+++ b/lib/gnuastro/wcs.h
@@ -125,6 +125,9 @@ gal_wcs_write(struct wcsprm *wcs, char *filename,
               char *extname, gal_fits_list_key_t *headers,
               char *program_string);
 
+char *
+gal_wcs_write_wcsstr(struct wcsprm *wcs, int *nkeyrec);
+
 void
 gal_wcs_write_in_fitsptr(fitsfile *fptr, struct wcsprm *wcs);
 
diff --git a/lib/wcs.c b/lib/wcs.c
index b29db3d..7a64ca5 100644
--- a/lib/wcs.c
+++ b/lib/wcs.c
@@ -124,6 +124,24 @@ wcs_read_correct_pc_cd(struct wcsprm *wcs)
 
 
 
+/* For the TPV, TNX and ZPX distortions, WCSLIB can't deal with the CDELT
+   keys properly and its better to use the CD matrix instead, this function
+   will check for this and return 1 if a CD matrix should be used
+   (over-riding the user's desired matrix if necessary). */
+static int
+wcs_use_cd_for_distortion(struct wcsprm *wcs)
+{
+  return ( wcs
+           && wcs->lin.disseq
+           && ( !strcmp(   wcs->lin.disseq->dtype[1], "TPV")
+                || !strcmp(wcs->lin.disseq->dtype[1], "TNX")
+                || !strcmp(wcs->lin.disseq->dtype[1], "ZPX") ) );
+}
+
+
+
+
+
 /* Read the WCS information from the header. Unfortunately, WCS lib is
    not thread safe, so it needs a mutex. In case you are not using
    multiple threads, just pass a NULL pointer as the mutex.
@@ -369,10 +387,12 @@ gal_wcs_read_fitsptr(fitsfile *fptr, int linearmatrix, 
size_t hstartwcs,
         }
     }
 
-  /* If the user wants a CD linear matrix, do the conversion here,
-     otherwise, make sure the PC matrix is used. */
-  if(linearmatrix==GAL_WCS_LINEAR_MATRIX_CD) gal_wcs_to_cd(wcs);
-  else                          gal_wcs_decompose_pc_cdelt(wcs);
+  /* If the distortion requires a CD matrix, or if the user wants it, do
+     the conversion here, otherwise, make sure the PC matrix is used. */
+  if(wcs_use_cd_for_distortion(wcs) \
+     || linearmatrix==GAL_WCS_LINEAR_MATRIX_CD)
+    gal_wcs_to_cd(wcs);
+  else gal_wcs_decompose_pc_cdelt(wcs);
 
   /* Clean up and return. */
   status=0;
@@ -512,70 +532,53 @@ gal_wcs_dimension_name(struct wcsprm *wcs, size_t 
dimension)
 /*************************************************************
  ***********               Write WCS               ***********
  *************************************************************/
-void
-gal_wcs_write_in_fitsptr(fitsfile *fptr, struct wcsprm *wcs)
+char *
+gal_wcs_write_wcsstr(struct wcsprm *wcs, int *nkeyrec)
 {
   char *wcsstr;
-  int cdfordist, status=0, nkeyrec;
-
-  /* For the TPV, TNX and ZPX distortions, WCSLIB can't deal with the CDELT
-     keys properly and its better to use the CD matrix instead, so we'll
-     use the 'gal_wcs_to_cd' function. */
-  cdfordist = ( wcs->lin.disseq
-                && ( !strcmp(   wcs->lin.disseq->dtype[1], "TPV")
-                     || !strcmp(wcs->lin.disseq->dtype[1], "TNX")
-                     || !strcmp(wcs->lin.disseq->dtype[1], "ZPX") ) );
+  int status=0;
 
   /* Finalize the linear transformation matrix. Note that some programs may
      have worked on the WCS. So even if 'altlin' is already 2, we'll just
      ensure that the final matrix is CD here. */
-  if(wcs->altlin==2 || cdfordist) gal_wcs_to_cd(wcs);
-  else                            gal_wcs_decompose_pc_cdelt(wcs);
+  if(wcs->altlin==2 || wcs_use_cd_for_distortion(wcs)) gal_wcs_to_cd(wcs);
+  else gal_wcs_decompose_pc_cdelt(wcs);
 
   /* Clean up small errors in the PC matrix and CDELT values. */
   gal_wcs_clean_errors(wcs);
 
-  /* Convert the WCS information to text. */
-  status=wcshdo(WCSHDO_safe, wcs, &nkeyrec, &wcsstr);
+  /* Convert the WCS information to text. If there was an error, then free
+     any allocated space and put zero on 'nkeyrec'. */
+  status=wcshdo(WCSHDO_safe, wcs, nkeyrec, &wcsstr);
   if(status)
-    error(0, 0, "%s: WARNING: WCSLIB error, no WCS in output.\n"
-          "wcshdu ERROR %d: %s", __func__, status, wcs_errmsg[status]);
-  else
     {
-      gal_fits_key_write_wcsstr(fptr, wcs, wcsstr, nkeyrec);
-      free(wcsstr);
+      error(0, 0, "%s: WARNING: WCSLIB error, no WCS in output.\n"
+            "wcshdo ERROR %d: %s", __func__, status, wcs_errmsg[status]);
+      if(wcsstr) free(wcsstr);
+      *nkeyrec=0;
+      wcsstr=NULL;
     }
-  status=0;
 
-   /* WCSLIB is going to write PC+CDELT keywords in any case. But when we
-      have a TPV, TNX or ZPX distortion, it is cleaner to use a CD matrix
-      (WCSLIB can't convert coordinates properly if the PC matrix is used
-      with the TPV distortion). So to help users avoid the potential
-      problems with WCSLIB. 'gal_wcs_to_cd' function already made sure that
-      CDELTi=1.0, so effectively the CD matrix and PC matrix are
-      equivalent, we just need to convert the keyword names and delete the
-      CDELT keywords. Note that zero-valued PC/CD elements may not be
-      present, so we'll manually set 'status' to zero to avoid CFITSIO from
-      crashing. */
-  if(wcs->altlin==2)
-    {
-      status=0; fits_delete_str(fptr, "CDELT1", &status);
-      status=0; fits_delete_str(fptr, "CDELT2", &status);
-      status=0; fits_modify_name(fptr, "PC1_1", "CD1_1", &status);
-      status=0; fits_modify_name(fptr, "PC1_2", "CD1_2", &status);
-      status=0; fits_modify_name(fptr, "PC2_1", "CD2_1", &status);
-      status=0; fits_modify_name(fptr, "PC2_2", "CD2_2", &status);
-      if(wcs->naxis==3)
-        {
-          status=0; fits_delete_str(fptr, "CDELT3", &status);
-          status=0; fits_modify_name(fptr, "PC1_3", "CD1_3", &status);
-          status=0; fits_modify_name(fptr, "PC2_3", "CD2_3", &status);
-          status=0; fits_modify_name(fptr, "PC3_1", "CD3_1", &status);
-          status=0; fits_modify_name(fptr, "PC3_2", "CD3_2", &status);
-          status=0; fits_modify_name(fptr, "PC3_3", "CD3_3", &status);
-        }
-      status=0;
-    }
+  /* Return the string. */
+  return wcsstr;
+}
+
+
+
+
+
+void
+gal_wcs_write_in_fitsptr(fitsfile *fptr, struct wcsprm *wcs)
+{
+  char *wcsstr;
+  int nkeyrec=0;
+
+  /* Convert the WCS structure into FITS keywords as a string. */
+  wcsstr=gal_wcs_write_wcsstr(wcs, &nkeyrec);
+
+  /* Write the keywords into the FITS file. */
+  gal_fits_key_write_wcsstr(fptr, wcs, wcsstr, nkeyrec);
+  free(wcsstr);
 }
 
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]