gnuastro-commits
[Top][All Lists]
Advanced

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

[gnuastro-commits] master 2eeb21d9: ConvertType: now possible to draw ve


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 2eeb21d9: ConvertType: now possible to draw vector graphics marks on EPS/PDF
Date: Fri, 19 Aug 2022 08:20:56 -0400 (EDT)

branch: master
commit 2eeb21d91f9ec07e5c7b4674e16fb554f3eb390a
Author: Mohammad Akhlaghi <mohammad@akhlaghi.org>
Commit: Mohammad Akhlaghi <mohammad@akhlaghi.org>

    ConvertType: now possible to draw vector graphics marks on EPS/PDF
    
    Until now, there was no easy interface to mark certain objects or add text
    under the marks in the publication-ready outputs (PDF or EPS). The only way
    to have high quality (vector graphics) marks and text over the image in
    your report was to use PGFPlots to draw them within the actual
    paper. However, within LaTeX the units are in centimeters or page width,
    not pixels and converting RA/DEC to the LaTeX coordinates for each image
    was very annoying.
    
    With this commit, it is now possible to give a "Marks" catalog to
    ConvertType. Using that catalog, ConvertType will draw shapes from the
    given coordinates in the table (and optionally add text under the
    mark). The user also has freedom to select 140 colors from the extended Web
    color formats, set the line width, rotation and many other ways to
    customize the marks.
    
    In order to do this, many options have been added to ConvertType and to
    easily adopt/learn these new features, two new sections have been added to
    the general tutorial.
    
    In the process, several other things have been done with this commit:
    
     - Based on feedback from many users, the general tutorial is now the first
       section of the tutorials chapter. The "Sufi simulates a detection"
       tutorial has been moved to the fourth section (because it is about
       simulating data, while the first three are about using existing data).
    
     - Mathematical (like 'pi') and Physical constants (like the speed of
       light) are now available in the Arithmetic library and usable in Table's
       column arithmetic and also the Arithmetic program.
    
     - Four new fuctions have been added to the Arithmetic library to convert
       surface brightness to and from magnitudes and counts.
    
     - The Fits program now has a new option called '--pixelareaarcsec2' which
       will return the pixel area in units of arc-seconds squared.
    
     - Many new library functions and macros have been added to enable easy
       addition of marks.
---
 NEWS                            |   84 +
 bin/arithmetic/arithmetic.c     |   23 +-
 bin/convertt/args.h             |  282 ++-
 bin/convertt/astconvertt.conf   |    6 +-
 bin/convertt/color.h            |    3 +
 bin/convertt/convertt.c         |    4 +-
 bin/convertt/main.h             |   20 +
 bin/convertt/ui.c               |  780 +++++++-
 bin/convertt/ui.h               |   24 +-
 bin/fits/args.h                 |   13 +
 bin/fits/fits.c                 |   83 +-
 bin/fits/main.h                 |    1 +
 bin/fits/ui.c                   |   17 +-
 bin/fits/ui.h                   |    1 +
 bin/table/arithmetic.c          |   19 +-
 bin/table/main.h                |    1 +
 bootstrap.conf                  |    1 +
 doc/gnuastro.texi               | 4213 ++++++++++++++++++++++++---------------
 doc/plotsrc/Makefile            |    1 +
 doc/plotsrc/all.tex             |    2 +
 doc/plotsrc/tex/color-names.tex |  491 +++++
 lib/Makefile.am                 |  169 +-
 lib/arithmetic.c                |  170 +-
 lib/box.c                       |   89 +
 lib/color.c                     |  531 +++++
 lib/data.c                      |   33 +-
 lib/eps.c                       |  682 ++++++-
 lib/gnuastro/arithmetic.h       |   17 +-
 lib/gnuastro/box.h              |    4 +
 lib/gnuastro/color.h            |  240 +++
 lib/gnuastro/eps.h              |   62 +-
 lib/gnuastro/list.h             |    3 +
 lib/gnuastro/pdf.h              |    3 +-
 lib/gnuastro/units.h            |   14 +
 lib/list.c                      |   21 +
 lib/pdf.c                       |   34 +-
 lib/units.c                     |   45 +
 37 files changed, 6463 insertions(+), 1723 deletions(-)

diff --git a/NEWS b/NEWS
index d112b490..b7f7b22b 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,66 @@ See the end of the file for license conditions.
 
 ** New features
 
+  Book:
+  - Two new sections added to the "General program usage tutorial" for
+    describing how to prepare a FITS image for high quality publication (in
+    PDF), and drawing vector graphics marks from a catalog over it (for
+    example to show your selected galaxies in the field of view). The use
+    the newly added features of ConvertType.
+
+  Arithmetic:
+  - Added new type operators and physical constants. All are also available
+    in Table's column arithmetic also).
+    - e: Base of the natural logarithm (no units).
+    - pi: Fraction of Circle cirumference to diameter (no units).
+    - c: speed of light in vaccume (in units of m/s).
+    - G: Gravitational constant (in units of m^3/kg/s^2).
+    - h: Plank's constant (in units of J/Hz).
+    - au: Astronomical Units (in units of meters).
+    - ly: Light years (in units of meters).
+    - avogadro: Avogadro's constant (in units of 1/mol).
+    - fine-structure: Fine structure constant (no units).
+    - counts-to-sb: convert counts to surface brightness (mag/arcsec^2).
+    - sb-to-counts: convert surface brightness (mag/arcsec^2) to counts.
+    - mag-to-sb: convert magnitudes to surface brightness over a certain area.
+    - sb-to-mag: convert surface brightness to magnitudes over a certain area.
+
+  ConvertType:
+  - It is now possible to draw vector graphics marks from a catalog over
+    the output PDF images. The following options have been added to
+    ConvertType for doing this. See the "General program usage tutorial"
+    for a fully working example.
+    --marks: name of table containing mark information.
+    --markshdu: HDU of table if file given to '--marks' is FITS.
+    --markcoords: name or number of two columns containing coordinates.
+    --mode: if the coordinates are in 'img' (image) or 'wcs' (RA/Dec).
+    --markshape: name or number of column containing the shape of each mark.
+    --markrotate: name or number of column containing rotation of each mark.
+    --marksize: name or number of column containing the size of the mark.
+    --sizeinpix: interpret the values in the size column as pixels.
+    --sizeinarcsec: interpret the values in the size column as arc-seconds.
+    --sizeinarcmin: interpret the values in the size column as arc-minutes.
+    --marklinewidth: name or number of column containing mark's line width.
+    --markcolor: name or number of column containing mark's color.
+    --listcolors: List all the 140 available colors, and show the colors on
+                  24-bit (true color) terminal.
+    --marktext: name or number of column containing text under each mark.
+    --marktextprecision: number of decimals to print as text when the text
+                         column (given to '--marktext') is floating point.
+    --markfont: name or number of column containing the font to use for the
+                the mark text (given to '--marktext').
+    --markfontsize: name or number of column containing the size of the
+                    font to use for the mark text (given to '--marktext').
+    --showfonts: build a demo PDF with one page per font to show the
+                 various available fonts on the system.
+    --listfonts: List the names of the available fonts on the terminal.
+
+  Fits:
+  --pixelareaarcsec2: print the image pixel area in units of arcsec^2 to
+    standard output. Among other things, this is useful in creating a
+    surface brightness image using the new 'counts-to-sb' operator of
+    Arithmetic.
+
   astscript-psf-stamp:
   - sub-pixel warping is applied to ensure that your coordinate is at the
     center of the central pixel of the output image. This results in a
@@ -18,10 +78,34 @@ See the end of the file for license conditions.
     to avoid slowing down you pipeline, you can disable sub-pixel warping
     with this option.
 
+  Library functions:
+  - gal_box_border_rotate_around_center: width of box after rotation.
+  - gal_color_name_to_id: return the ID of a color from its name.
+  - gal_color_id_to_name: return the name of a color from its ID.
+  - gal_color_in_rgb: return the fraction of red-green-blue in given color.
+  - gal_eps_shape_name_to_id: return the ID of a shape from its name.
+  - gal_eps_shape_id_to_name: return the name of a shape from its ID.
+  - gal_list_data_select_by_name: select a dataset from a list by its name.
+  - gal_units_mag_to_sb: surface brightness (SB) from magnitude and area.
+  - gal_units_sb_to_mag: magnitude from SB and area.
+  - gal_units_counts_to_sb: SB from counts, zeropoint and area.
+  - gal_units_sb_to_counts: counts from SB, zeropoint and area.
+
+
 ** Removed features
 
 ** Changed features
 
+  Book:
+  - The "General program usage tutorial" section is now the first section
+    of the Tutorial chapter, since it introduces the tools at a more basic
+    level. The "Sufi simulates a detection" (which was previously first)
+    has been moved to the fourth section.
+
+  Library
+  - gal_eps_write: an extra argument has been added to draw marks.
+  - gal_pdf_write: similar to 'gal_eps_write'.
+
 ** Bugs fixed
   bug #62861: '--printkeynames' of Fits program gets caught in an infinite
               loop on FITS files that have empty keywords before
diff --git a/bin/arithmetic/arithmetic.c b/bin/arithmetic/arithmetic.c
index 5722633d..3551c4bb 100644
--- a/bin/arithmetic/arithmetic.c
+++ b/bin/arithmetic/arithmetic.c
@@ -1221,15 +1221,18 @@ arithmetic_repeat(struct arithmeticparams *p, char 
*token, int operator)
 /*************      Reverse Polish algorithm       *************/
 /***************************************************************/
 static int
-arithmetic_set_operator(char *string, size_t *num_operands)
+arithmetic_set_operator(char *string, size_t *num_operands, int *inlib)
 {
   /* Use the library's main function for its own operators. */
   int op = gal_arithmetic_set_operator(string, num_operands);
 
+  /* Mark if this operator is in the library or not. */
+  *inlib = op==GAL_ARITHMETIC_OP_INVALID ? 0 : 1;
+
   /* If its not a library operator, check if its an internal operator. */
   if(op==GAL_ARITHMETIC_OP_INVALID)
     {
-      /* Non-library operators. */
+      /* See if its a custom operator to the Arithmetic program. */
       if      (!strcmp(string, "filter-mean"))
         { op=ARITHMETIC_OP_FILTER_MEAN;           *num_operands=0; }
       else if (!strcmp(string, "filter-median"))
@@ -1290,7 +1293,8 @@ arithmetic_set_operator(char *string, size_t 
*num_operands)
 
 static void
 arithmetic_operator_run(struct arithmeticparams *p, int operator,
-                        char *operator_string, size_t num_operands)
+                        char *operator_string, size_t num_operands,
+                        int inlib)
 {
   size_t i;
   unsigned int numop;
@@ -1301,8 +1305,8 @@ arithmetic_operator_run(struct arithmeticparams *p, int 
operator,
   if(p->cp.quiet) flags |= GAL_ARITHMETIC_FLAG_QUIET;
   if(p->envseed)  flags |= GAL_ARITHMETIC_FLAG_ENVSEED;
 
-  /* When 'num_operands!=0', the operator is in the library. */
-  if(num_operands)
+  /* If this operator is in the library, we should pop everything here.  */
+  if(inlib)
     {
       /* Pop the necessary number of operators. Note that the
          operators are poped from a linked list (which is
@@ -1311,6 +1315,9 @@ arithmetic_operator_run(struct arithmeticparams *p, int 
operator,
          last (right most, in in-fix notation) input operand.*/
       switch(num_operands)
         {
+        case 0:
+          break;
+
         case 1:
           d1=operands_pop(p, operator_string);
           break;
@@ -1459,8 +1466,8 @@ reversepolish(struct arithmeticparams *p)
   gal_list_str_t *token;
   gal_data_t *data, *col;
   char *hdu, *filename, *printnum;
-  int operator=GAL_ARITHMETIC_OP_INVALID;
   struct gal_options_common_params *cp=&p->cp;
+  int inlib, operator=GAL_ARITHMETIC_OP_INVALID;
 
   /* Prepare the processing: */
   p->popcounter=0;
@@ -1511,8 +1518,8 @@ reversepolish(struct arithmeticparams *p)
          isn't an operator. */
       else
         {
-          operator=arithmetic_set_operator(token->v, &num_operands);
-          arithmetic_operator_run(p, operator, token->v, num_operands);
+          operator=arithmetic_set_operator(token->v, &num_operands, &inlib);
+          arithmetic_operator_run(p, operator, token->v, num_operands, inlib);
         }
 
       /* Increment the token counter. */
diff --git a/bin/convertt/args.h b/bin/convertt/args.h
index b6cc9639..7728d85e 100644
--- a/bin/convertt/args.h
+++ b/bin/convertt/args.h
@@ -76,19 +76,6 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
-    {
-      "borderwidth",
-      UI_KEY_BORDERWIDTH,
-      "INT",
-      0,
-      "EPS/PDF border width in units of 1/72 inch.",
-      GAL_OPTIONS_GROUP_OUTPUT,
-      &p->borderwidth,
-      GAL_TYPE_UINT32,
-      GAL_OPTIONS_RANGE_GE_0,
-      GAL_OPTIONS_NOT_MANDATORY,
-      GAL_OPTIONS_NOT_SET
-    },
     {
       "hex",
       UI_KEY_HEX,
@@ -247,6 +234,275 @@ struct argp_option program_options[] =
 
 
 
+
+    {
+      0, 0, 0, 0,
+      "Vector graphics (only for EPS or PDF outputs)",
+      UI_GROUP_MARKS
+    },
+    {
+      "borderwidth",
+      UI_KEY_BORDERWIDTH,
+      "INT",
+      0,
+      "Border width in units of points (1/72 inch).",
+      GAL_OPTIONS_GROUP_OUTPUT,
+      &p->borderwidth,
+      GAL_TYPE_UINT32,
+      GAL_OPTIONS_RANGE_GE_0,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "marks",
+      UI_KEY_MARKS,
+      "STR",
+      0,
+      "Name of mark information table.",
+      UI_GROUP_MARKS,
+      &p->marksname,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "markshdu",
+      UI_KEY_MARKSHDU,
+      "STR",
+      0,
+      "HDU in '--marks' (if its a FITS file).",
+      UI_GROUP_MARKS,
+      &p->markshdu,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "markcoords",
+      UI_KEY_MARKCOORDS,
+      "STR,STR",
+      0,
+      "Name or Number of columns with coordinates.",
+      UI_GROUP_MARKS,
+      &p->markcoords,
+      GAL_TYPE_STRLL,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+    },
+    {
+      "mode",
+      UI_KEY_MODE,
+      "STR",
+      0,
+      "Coordinate mode for marks ('wcs' or 'img').",
+      UI_GROUP_MARKS,
+      &p->mode,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "markshape",
+      UI_KEY_MARKSHAPE,
+      "STR",
+      0,
+      "Name or Number of col. with mark shapes: circle (1), "
+      "plus (2), cross (3), ellipse (4), point(5), square (6) "
+      "rectangle (7) and line (8).",
+      UI_GROUP_MARKS,
+      &p->markshape,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "markrotate",
+      UI_KEY_MARKROTATE,
+      "STR",
+      0,
+      "Name or Num. of col. with mark rotation.",
+      UI_GROUP_MARKS,
+      &p->markrotate,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "marksize",
+      UI_KEY_MARKSIZE,
+      "STR[,STR]",
+      0,
+      "Name or Number of cols. with mark size(s).",
+      UI_GROUP_MARKS,
+      &p->marksize,
+      GAL_TYPE_STRLL,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "sizeinpix",
+      UI_KEY_SIZEINPIX,
+      0,
+      0,
+      "Size col. values are in pixels (in WCS-mode).",
+      UI_GROUP_MARKS,
+      &p->sizeinpix,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "sizeinarcsec",
+      UI_KEY_SIZEINARCSEC,
+      0,
+      0,
+      "Size col. values are in arcsec (in WCS-mode).",
+      UI_GROUP_MARKS,
+      &p->sizeinarcsec,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "sizeinarcmin",
+      UI_KEY_SIZEINARCMIN,
+      0,
+      0,
+      "Size col. values are in arcmin (in WCS-mode).",
+      UI_GROUP_MARKS,
+      &p->sizeinarcmin,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "marklinewidth",
+      UI_KEY_MARKLINEWIDTH,
+      "STR",
+      0,
+      "Name or Number of col. with line width.",
+      UI_GROUP_MARKS,
+      &p->marklinewidth,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "markcolor",
+      UI_KEY_MARKCOLOR,
+      "STR",
+      0,
+      "Name or Number of col. with mark color. ",
+      UI_GROUP_MARKS,
+      &p->markcolor,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "listcolors",
+      UI_KEY_LISTCOLORS,
+      0,
+      0,
+      "List names and RGB info of all colors.",
+      UI_GROUP_MARKS,
+      &p->listcolors,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "marktext",
+      UI_KEY_MARKTEXT,
+      "STR",
+      0,
+      "Name or Num. of col. with mark text.",
+      UI_GROUP_MARKS,
+      &p->marktext,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "marktextprecision",
+      UI_KEY_MARKTEXTPRECISION,
+      "INT",
+      0,
+      "Number decimals when text is float column.",
+      UI_GROUP_MARKS,
+      &p->marktextprecision,
+      GAL_TYPE_UINT8,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "markfont",
+      UI_KEY_MARKFONT,
+      "STR",
+      0,
+      "Name or Num. of col. with mark font name.",
+      UI_GROUP_MARKS,
+      &p->markfont,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "markfontsize",
+      UI_KEY_MARKFONTSIZE,
+      "STR",
+      0,
+      "Name or Num. of col. with mark font size.",
+      UI_GROUP_MARKS,
+      &p->markfontsize,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "showfonts",
+      UI_KEY_SHOWFONTS,
+      0,
+      0,
+      "Show all fonts in a PDF.",
+      UI_GROUP_MARKS,
+      &p->showfonts,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+    {
+      "listfonts",
+      UI_KEY_LISTFONTS,
+      0,
+      0,
+      "List names of available fonts.",
+      UI_GROUP_MARKS,
+      &p->listfonts,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
+
     {0}
   };
 
diff --git a/bin/convertt/astconvertt.conf b/bin/convertt/astconvertt.conf
index dae9eba5..d41036a1 100644
--- a/bin/convertt/astconvertt.conf
+++ b/bin/convertt/astconvertt.conf
@@ -25,12 +25,16 @@
  quality              100
  widthincm            10.0
  borderwidth          1
- output               jpg
+ output               output.jpg
  colormap             gray
 
 # Flux:
  invert               0
 
+# Marks:
+ markshdu             1
+ marktextprecision    0
+
 # Common options
 #
 # --stdintimeout: If a standard input is given, ConvertType will use it as
diff --git a/bin/convertt/color.h b/bin/convertt/color.h
index 780c4f78..10c3b2f9 100644
--- a/bin/convertt/color.h
+++ b/bin/convertt/color.h
@@ -23,6 +23,9 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #ifndef COLOR_H
 #define COLOR_H
 
+void
+color_name_to_rgb(char *n, float *f);
+
 void
 color_map_prepare(struct converttparams *p);
 
diff --git a/bin/convertt/convertt.c b/bin/convertt/convertt.c
index 0adf4f5f..2aa0f284 100644
--- a/bin/convertt/convertt.c
+++ b/bin/convertt/convertt.c
@@ -350,14 +350,14 @@ convertt(struct converttparams *p)
     case OUT_FORMAT_EPS:
       if(p->colormap) color_map_prepare(p); else convertt_scale_to_uchar(p);
       gal_eps_write(p->chll, p->cp.output, p->widthincm, p->borderwidth,
-                    p->hex, p->forcemin || p->forcemax, 0);
+                    p->hex, p->forcemin || p->forcemax, 0, p->marks);
       break;
 
     /* PDF */
     case OUT_FORMAT_PDF:
       if(p->colormap) color_map_prepare(p); else convertt_scale_to_uchar(p);
       gal_pdf_write(p->chll, p->cp.output, p->widthincm, p->borderwidth,
-                    p->forcemin || p->forcemax);
+                    p->forcemin || p->forcemax, p->marks);
       break;
 
     /* Not recognized. */
diff --git a/bin/convertt/main.h b/bin/convertt/main.h
index b32840e6..1cc439e3 100644
--- a/bin/convertt/main.h
+++ b/bin/convertt/main.h
@@ -98,6 +98,25 @@ struct converttparams
   char           *changestr;  /* String of change values.              */
   uint8_t  changeaftertrunc;  /* First convert, then truncate.         */
   uint8_t            invert;  /* ==1: invert the output image.         */
+  char           *marksname;  /* Filename with table with mark info.   */
+  char            *markshdu;  /* HDU of table with mark info.          */
+  char                *mode;  /* Mode of the coordinates for marks.    */
+  gal_list_str_t *markcoords; /* Coordinates of the marks.             */
+  gal_list_str_t  *marksize;  /* Columns containing mark size(s).      */
+  char       *marklinewidth;  /* Column containing mark line width.    */
+  char           *markcolor;  /* Column containing mark color.         */
+  char           *markshape;  /* Column containing mark shape.         */
+  char          *markrotate;  /* Column containing mark rotation.      */
+  char            *marktext;  /* Column containing mark text.          */
+  char            *markfont;  /* Column containing mark font name.     */
+  char        *markfontsize;  /* Column containing mark font size.     */
+  uint8_t        listcolors;  /* List available colors                 */
+  uint8_t         listfonts;  /* List available fonts.                 */
+  uint8_t         showfonts;  /* Show available fonts.                 */
+  uint8_t         sizeinpix;  /* Sizes are in pixels (in WCS-mode).    */
+  uint8_t      sizeinarcsec;  /* Sizes are in arcseconds (in WCS-mode).*/
+  uint8_t      sizeinarcmin;  /* Sizes are in arcminutes (in WCS-mode).*/
+  uint8_t marktextprecision;  /* Precision to convert floats.          */
 
   /* Internal */
   struct change     *change;  /* The value conversion string.          */
@@ -107,6 +126,7 @@ struct converttparams
   int             outformat;  /* The format of the output file.        */
   size_t              numch;  /* Current Channel.                      */
   gal_data_t          *chll;  /* Linked list of color channels.        */
+  gal_data_t         *marks;  /* Information of objects to show.       */
 };
 
 #endif
diff --git a/bin/convertt/ui.c b/bin/convertt/ui.c
index f8889d1e..9b02c505 100644
--- a/bin/convertt/ui.c
+++ b/bin/convertt/ui.c
@@ -26,6 +26,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <errno.h>
 #include <error.h>
 #include <stdio.h>
+#include <unistd.h>
 #include <string.h>
 
 #include <gnuastro/eps.h>
@@ -38,6 +39,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gnuastro/tiff.h>
 #include <gnuastro/table.h>
 #include <gnuastro/blank.h>
+#include <gnuastro/color.h>
 #include <gnuastro/arithmetic.h>
 
 #include <gnuastro-internal/timing.h>
@@ -144,8 +146,6 @@ ui_initialize_options(struct converttparams *p,
           break;
 
         case GAL_OPTIONS_KEY_TYPE:
-        case GAL_OPTIONS_KEY_SEARCHIN:
-        case GAL_OPTIONS_KEY_IGNORECASE:
         case GAL_OPTIONS_KEY_TABLEFORMAT:
           cp->coptions[i].flags=OPTION_HIDDEN;
           break;
@@ -241,9 +241,9 @@ ui_colormap_sanity_check(struct converttparams *p)
         1. Keep 'gray' and 'grey' in the same line.
         2. Keep a space after the ',' before the strings.   */
   strarr=p->colormap->array;
-  if     ( !strcmp(strarr[0], "hsv"))     { ccode=COLOR_HSV;     nparams=2; }
-  else if( !strcmp(strarr[0], "sls"))     { ccode=COLOR_SLS;     nparams=0; }
-  else if( !strcmp(strarr[0], "viridis")) { ccode=COLOR_VIRIDIS; nparams=0; }
+  if     ( !strcmp(strarr[0], "hsv"))    {ccode=COLOR_HSV;     nparams=2;}
+  else if( !strcmp(strarr[0], "sls"))    {ccode=COLOR_SLS;     nparams=0;}
+  else if( !strcmp(strarr[0], "viridis")){ccode=COLOR_VIRIDIS; nparams=0;}
   else if( !strcmp(strarr[0], "gray") || !strcmp(strarr[0], "grey"))
                                          { ccode=COLOR_GRAY; nparams=0; }
   else if( !strcmp(strarr[0], "sls-inverse"))
@@ -270,7 +270,8 @@ ui_colormap_sanity_check(struct converttparams *p)
         {
           p->colormap->next=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, 1,
                                            &nparams, NULL, 0,
-                                           p->cp.minmapsize, p->cp.quietmmap,
+                                           p->cp.minmapsize,
+                                           p->cp.quietmmap,
                                            NULL,NULL,NULL);
           farray=p->colormap->next->array;
           switch(p->colormap->status)
@@ -314,6 +315,159 @@ ui_colormap_sanity_check(struct converttparams *p)
 
 
 
+/* List the acceptable colors with a demo of what they look like (by
+   setting the background color of an "EXAMPLE" string to the desired color
+   following the ANSI escape sequence standard:
+   https://en.wikipedia.org/wiki/ANSI_escape_code */
+static void
+ui_list_colors(struct converttparams *p)
+{
+  size_t i;
+  int rgbi[3];
+  float rgbf[3];
+
+  /* Print the metadata lightgoldenrodyellow */
+  printf("# Column 1: Color-ID   [counter, u8] Color's numerical 
identifier.\n");
+  printf("# Column 2: Color-Name [name, str20] Extended Web color name.\n");
+  printf("# Column 3: FRAC-R     [frac,   f32] Fraction of Red.\n");
+  printf("# Column 4: FRAC-G     [frac,   f32] Fraction of Green.\n");
+  printf("# Column 5: FRAC-B     [frac,   f32] Fraction of Blue.\n");
+  printf("# Column 6: HEX        [hex,   str6] Color code in hexadecimal.\n");
+  printf("# Column 7: EXAMPLE    [n/a,  str35] Example of color in 24-bit "
+         "terminals\n");
+
+  /* Print each color's information. */
+  for(i=1;i<GAL_COLOR_NUMBER;++i)
+    {
+      gal_color_in_rgb(i, rgbf);
+      rgbi[0]=rgbf[0]*255; rgbi[1]=rgbf[1]*255; rgbi[2]=rgbf[2]*255;
+      printf("%-3zu %-20s %-5.2f %-5.2f %-5.2f %02X%02X%02X  "
+             "\x1b[48;2;%d;%d;%dm EXAMPLE \x1b[0m\n",
+             i, gal_color_id_to_name(i),
+             rgbf[0], rgbf[1], rgbf[2],
+             rgbi[0], rgbi[1], rgbi[2],
+             rgbi[0], rgbi[1], rgbi[2]);
+    }
+
+  /* Print information about colors. */
+  if( !p->cp.quiet )
+    {
+      printf("#\n");
+      printf("# When viewed within a 24-bit or \"true color\" terminal, "
+             "the demonstration ('EXAMPLE') column will show the desired "
+             "color as the background of the text 'EXAMPLE'. If your "
+             "terminal doesn't support 24-bit true color or the ANSI "
+             "escape sequence standard "
+             "(https://en.wikipedia.org/wiki/ANSI_escape_code), the "
+             "last column's color will either be rounded to the "
+             "nearest supported color, or that column may be displayed "
+             "as a long string of numbers and brackets (which are the "
+             "raw source behind the color-coding). On macOS, the default "
+             "terminal emulator (iTerm) doesn't support 24-bit colors, "
+             "so it is recommended to install and use iTerm2 "
+             "(https://iterm2.com: it is free software and available "
+             "in Homebrew). This message can be removed with the "
+             "'--quiet' (or '-q') option.\n");
+    }
+
+  /* There is nothing else for the program to do, simply return
+     successfully. */
+  exit(EXIT_SUCCESS);
+}
+
+
+
+
+
+/* List the available fonts for the user to select from. Ghostscript
+   command was inspired from
+   
https://superuser.com/questions/379384/ghostscript-how-do-i-find-out-what-fonts-are-available*/
+static void
+ui_list_fonts(struct converttparams *p)
+{
+  char command[]="gs -q -dNODISPLAY -dBATCH "
+    "-c '(*) {cvn ==} 256 string /Font resourceforall' "
+    "| sed -e's|^/||'";
+
+  if(system(command))
+    error(EXIT_FAILURE, 0, "the Ghostscript command (printed after "
+          "this message) to list the available fonts was not "
+          "successful! The Ghostscript command was: %s", command);
+
+  /* Let the users know about '--showfonts'. */
+  if(!p->cp.quiet)
+    printf("#\n# NOTICE: with '--showfonts' you can see all the "
+           "fonts in a PDF file. This can help if you aren't "
+           "already familiar with the shapes of each font. You "
+           "can remove this notice with the '--quiet' option\n");
+
+  /* Abort the program. */
+  exit(EXIT_SUCCESS);
+}
+
+
+
+
+
+/* List the available fonts for the user to select from */
+static void
+ui_show_fonts(struct converttparams *p)
+{
+  FILE *fp;
+  char *outps, *outpdf, *command;
+
+  /* Set the PDF and PS file names. */
+  outpdf = gal_checkset_automatic_output(&p->cp, p->cp.output, "-fonts.pdf");
+  gal_checkset_writable_remove(outpdf, 0, p->cp.dontdelete);
+  outps=gal_checkset_automatic_output(&p->cp, outpdf, ".ps");
+
+  /* This command was taken from the 'ps2pdfwr' installed script that comes
+     with Ghostscript by default ('ps2pdfwr' is ultimately what 'ps2pdf'
+     calls, after some checks on the PDF version). */
+  if( asprintf(&command, "gs -P- -dSAFER -q -P- -dNOPAUSE -dBATCH "
+               "-sDEVICE=pdfwrite -sOutputFile=%s %s", outpdf, outps)<0 )
+    error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__);
+
+  /* Write the contents of the PostScript file. Inspired from:
+     
https://superuser.com/questions/379384/ghostscript-how-do-i-find-out-what-fonts-are-available*/
+  errno=0;
+  fp=fopen(outps, "w");
+  if(fp==NULL)
+    error(EXIT_FAILURE, errno, "%s", outps);
+  fprintf(fp, "%%!\n");
+  fprintf(fp, "<< /PageSize [500 80] >> setpagedevice\n");
+  fprintf(fp, "(*) {dup cvn findfont 20 scalefont setfont\n");
+  fprintf(fp, "10 50 moveto show\n");
+  fprintf(fp, "10 10 moveto (ABCDEFGHIJKLMNOPQRSTUVWXYZ) show showpage}\n");
+  fprintf(fp, "256 string /Font resourceforall\n");
+  fprintf(fp, "%%%%EOF");
+  fclose(fp);
+
+  /* Convert this to PDF. */
+  if(system(command))
+    error(EXIT_FAILURE, 0, "the Ghostscript command (printed after "
+          "this message) to list the available fonts was not "
+          "successful! The Ghostscript command was: %s", command);
+
+  /* Delete the PostScript file. */
+  errno=0;
+  if(unlink(outps))
+    error(EXIT_FAILURE, errno, "%s", outps);
+
+  /* Let the user know that the printed fonts are now available. */
+  if(!p->cp.quiet)
+    printf("Fonts shown in (one page per font): %s\n", outpdf);
+
+  /* Cleanup and finish. */
+  free(outps);
+  free(outpdf);
+  exit(EXIT_SUCCESS);
+}
+
+
+
+
+
 /* Read and check ONLY the options. When arguments are involved, do the
    check in 'ui_check_options_and_arguments'. */
 static void
@@ -321,6 +475,19 @@ ui_read_check_only_options(struct converttparams *p)
 {
   gal_data_t *cond;
 
+  /* If the user has asked to list colors or fonts,, that is the only thing
+     the program should do. */
+  if(p->listcolors || p->listfonts || p->showfonts)
+    {
+      if(p->listcolors + p->listfonts + p->showfonts > 1)
+        error(EXIT_FAILURE, 0, "only one of the '--listcolors', "
+              "'--listfonts' or '--showfonts' should be called in "
+              "one command");
+      if(p->listfonts)  ui_list_fonts(p);
+      if(p->showfonts)  ui_show_fonts(p);
+      if(p->listcolors) ui_list_colors(p);
+    }
+
   /* Read the truncation values into a data structure and see if flux low
      is indeed smaller than fluxhigh. */
   if(p->fluxlowstr)
@@ -355,6 +522,50 @@ ui_read_check_only_options(struct converttparams *p)
   /* Check the colormap. */
   if(p->colormap)
     ui_colormap_sanity_check(p);
+
+  /* Check the marks information (the minimum required parameters are the X
+     and Y positions). */
+  if(p->marksname)
+    {
+      /* If the mark coordinates are given, blend them into one list  */
+      if(p->markcoords)
+        {
+          gal_options_merge_list_of_csv(&p->markcoords);
+          if(gal_list_str_number(p->markcoords)!=2)
+            error(EXIT_FAILURE, 0, "two values should be give to the "
+                  "'--markcoords' (or '-r') option, while you have given "
+                  "%zu", gal_list_str_number(p->markcoords));
+        }
+      else
+        error(EXIT_FAILURE, 0, "the '--markcoords' (or '-r') is necessary "
+              "to define the positions of the marks over the output (recall "
+              "that marks are only supported in EPS or PDF formats)");
+
+      /* It is mandatory to define a mode (of 'wcs' or 'img). */
+      if( p->mode )
+        {
+          if( strcmp(p->mode, "wcs") && strcmp(p->mode, "img") )
+            error(EXIT_FAILURE, 0, "'%s' is not recognized for the "
+                  "'--mode' (or '-O') option. The recognized values "
+                  "are 'img' or 'wcs'", p->mode);
+        }
+      else
+        error(EXIT_FAILURE, 0, "the '--mode' (or '-O') is necessary "
+              "to define how the mark coordinates should be interpreted "
+              "(recall that marks are only supported in EPS or PDF "
+              "formats)");
+
+      /* Make sure the size column(s) are in one list. */
+      if(p->marksize)
+        {
+          gal_options_merge_list_of_csv(&p->marksize);
+          if(gal_list_str_number(p->marksize)>2)
+            error(EXIT_FAILURE, 0, "the '--marksize' option takes two "
+                  "values (column names or numbers) at most, but you "
+                  "have given %zu values",
+                  gal_list_str_number(p->marksize));
+        }
+    }
 }
 
 
@@ -429,16 +640,16 @@ ui_make_change_struct(char *arg)
           /* Go to the end of this number (until you reach a '\0'). */
           while(*p!='\0') {++p; continue;}
 
-          /* Put the data structure in the correct place. When the counter is
-             an odd number, we have just started a new set of changes.*/
+          /* Put the data structure in the correct place. When the counter
+             is an odd number, we have just started a new set of changes.*/
           if(counter%2)             /* Odd. */
             {
               /* Allocate space for the new structure. */
               errno=0;
               ch=malloc(sizeof *ch);
               if(ch==NULL)
-                error(EXIT_FAILURE, errno, "%s: allocating %zu bytes for 'ch'",
-                      __func__, sizeof *ch);
+                error(EXIT_FAILURE, errno, "%s: allocating %zu bytes "
+                      "for 'ch'", __func__, sizeof *ch);
 
               /* If the last structure has already been defined (!=NULL)
                  then we should set its next element to 'ch' and change it
@@ -499,7 +710,8 @@ ui_make_channels_ll(struct converttparams *p)
   lines=gal_txt_stdin_read(p->cp.stdintimeout);
   if(lines)
     {
-      data=gal_txt_image_read(NULL, lines, p->cp.minmapsize, p->cp.quietmmap);
+      data=gal_txt_image_read(NULL, lines, p->cp.minmapsize,
+                              p->cp.quietmmap);
       gal_list_data_add(&p->chll, data);
       gal_list_str_free(lines, 1);
       ++p->numch;
@@ -533,10 +745,10 @@ ui_make_channels_ll(struct converttparams *p)
                 error(EXIT_FAILURE, 0, "not enough HDUs. Every input FITS "
                       "image needs a HDU (identified by name or number, "
                       "counting from zero). You can use multiple calls to "
-                      "the '--hdu' ('-h') option for each input FITS image "
-                      "(in the same order as the input FITS files), or use "
-                      "'--globalhdu' ('-g') once when the same HDU should "
-                      "be used for all of them");
+                      "the '--hdu' ('-h') option for each input FITS "
+                      "image (in the same order as the input FITS files), "
+                      "or use '--globalhdu' ('-g') once when the same "
+                      "HDU should be used for all of them");
             }
 
           /* Read in the array and its WCS information. */
@@ -794,8 +1006,8 @@ ui_prepare_input_channels(struct converttparams *p)
                     ndim, tmp->ndim);
             for(i=0;i<ndim;++i)
               if(dsize[i]!=tmp->dsize[i])
-                error(EXIT_FAILURE, 0, "The length along each dimension of "
-                      "the channels must be the same");
+                error(EXIT_FAILURE, 0, "The length along each dimension "
+                      "of the channels must be the same");
           }
 
         /* Incase there is WCS information, also keep a pointer to the
@@ -907,17 +1119,46 @@ ui_set_output(struct converttparams *p)
   /* JPEG */
   else if(gal_jpeg_name_is_jpeg(cp->output))
     {
-      /* Small sanity checks. */
-      if(p->quality == GAL_BLANK_UINT8)
-        error(EXIT_FAILURE, 0, "the '--quality' ('-u') option is necessary "
-              "for jpeg outputs, but it has not been given");
-      if(p->quality > 100)
-        error(EXIT_FAILURE, 0, "'%u' is larger than 100. The value to the "
-              "'--quality' ('-u') option must be between 1 and 100 "
-              "(inclusive)", p->quality);
-
-      /* Preparations. */
-      p->outformat=OUT_FORMAT_JPEG;
+      /* If marks are necessary, then we need to use Ghostscript to put the
+         marks over the image. */
+      if(p->marksname)
+        {
+          p->outformat=OUT_FORMAT_PDF;
+          if(!p->cp.quiet)
+            error(EXIT_SUCCESS, 0, "WARNING: output format is JPEG (a "
+                  "raster graphics format), but you have requested "
+                  "vector graphics marks (which are native to formats "
+                  "like PDF or EPS). The marks will therefore become "
+                  "pixelated. If the pixelation over the marks is too "
+                  "strong (the quality is too low!), you need to "
+                  "increase the resolution. You can do this by "
+                  "increasing the centimeter-width of the output by "
+                  "giving a larger number to '--widthincm' (or '-w'; "
+                  "currently it is %g cm). Just don't increase it too "
+                  "much, otherwise your output file size will become "
+                  "very large (in bytes). Vector formats are optimal "
+                  "for marks (PDF or EPS) and will become much smaller "
+                  "(in bytes) while having infinite resolution. Also, "
+                  "Ghostscript, and its 'jpeg' output device will be "
+                  "used, in case you don't have Ghostscript or this "
+                  "device isn't activated, the program will crash. "
+                  "This warning can be suppressed with '--quiet' "
+                  "(or '-q')", p->widthincm);
+        }
+      else
+        {
+          /* Small sanity checks. */
+          if(p->quality == GAL_BLANK_UINT8)
+            error(EXIT_FAILURE, 0, "the '--quality' ('-u') option is "
+                  "necessary for jpeg outputs, but it has not been given");
+          if(p->quality > 100)
+            error(EXIT_FAILURE, 0, "'%u' is larger than 100. The value to "
+                  "the '--quality' ('-u') option must be between 1 and 100 "
+                  "(inclusive)", p->quality);
+
+          /* Preparations. */
+          p->outformat=OUT_FORMAT_JPEG;
+        }
       if( gal_jpeg_suffix_is_jpeg(cp->output) )
         ui_add_dot_use_automatic_output(p);
     }
@@ -979,12 +1220,12 @@ ui_set_output(struct converttparams *p)
           /* If output type is not an image, there should only be one color
              channel: */
           if(p->numch>1)
-            error(EXIT_FAILURE, 0, "text output ('--output=%s') can only be "
-                  "completed with one input color channel. You have given "
-                  "%zu. Note that some formats (for example JPEG) can have "
-                  "more than one color channel in each file. You can first "
-                  "convert the file to FITS, then convert the desired "
-                  "channel to text by specifying the HDU",
+            error(EXIT_FAILURE, 0, "text output ('--output=%s') can only "
+                  "be completed with one input color channel. You have "
+                  "given %zu. Note that some formats (for example JPEG) "
+                  "can have more than one color channel in each file. "
+                  "You can first convert the file to FITS, then convert "
+                  "the desired channel to text by specifying the HDU",
                   cp->output, p->numch);
         }
     }
@@ -997,6 +1238,474 @@ ui_set_output(struct converttparams *p)
 
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/***********************************************************************/
+/****************    Marks for EPS or PDF outputs   ********************/
+/***********************************************************************/
+void
+ui_marks_read_raw(struct converttparams *p, gal_data_t **coord1,
+                  gal_data_t **coord2, gal_data_t **size1,
+                  gal_data_t **size2, gal_data_t **linewidth,
+                  gal_data_t **color, gal_data_t **shape,
+                  gal_data_t **rotate, gal_data_t **text,
+                  gal_data_t **font, gal_data_t **fontsize)
+{
+  size_t colnum=0;
+  gal_list_str_t *cols=NULL;
+  gal_data_t *table, *latest;
+
+  /* Set the requested columns. */
+  gal_list_str_add(&cols, p->markcoords->v, 1);         ++colnum;
+  gal_list_str_add(&cols, p->markcoords->next->v,1);    ++colnum;
+  if(p->marksize)
+    { gal_list_str_add(&cols, p->marksize->v, 1);       ++colnum; }
+  if(p->marksize && p->marksize->next)
+    { gal_list_str_add(&cols, p->marksize->next->v, 1); ++colnum; }
+  if(p->marklinewidth)
+    { gal_list_str_add(&cols, p->marklinewidth, 1);     ++colnum; }
+  if(p->markcolor)
+    { gal_list_str_add(&cols, p->markcolor, 1);         ++colnum; }
+  if(p->markshape)
+    { gal_list_str_add(&cols, p->markshape, 1);         ++colnum; }
+  if(p->markrotate)
+    { gal_list_str_add(&cols, p->markrotate, 1);        ++colnum; }
+  if(p->marktext)
+    { gal_list_str_add(&cols, p->marktext, 1);          ++colnum; }
+  if(p->markfont)
+    { gal_list_str_add(&cols, p->markfont, 1);          ++colnum; }
+  if(p->markfontsize)
+    { gal_list_str_add(&cols, p->markfontsize, 1);      ++colnum; }
+
+  /* Put the columns in the same order defined above (recall that its a
+     last-in-first-out list). */
+  gal_list_str_reverse(&cols);
+
+  /* Read the table */
+  table=gal_table_read(p->marksname, p->markshdu, NULL, cols,
+                       p->cp.searchin, p->cp.ignorecase,
+                       p->cp.numthreads, p->cp.minmapsize,
+                       p->cp.quietmmap, NULL);
+
+  /* Make sure that we have only one column for each entry (it may happen
+     that a table has two columns with the same name!). */
+  if(gal_list_data_number(table)!=colnum)
+    error(EXIT_FAILURE, 0, "%s: more than one column was found for "
+          "one of your '--mark*' columns. This usually happens when "
+          "more than one column has the same name",
+          gal_fits_name_save_as_string(p->marksname, p->markshdu));
+
+  /* Put each of the columns in their proper pointer.
+     IMPORTANT: KEEP THE ORDER THE SAME AS THE 'cols' DEFINITION.*/
+  latest=*coord1=table;
+  latest=*coord2=latest->next;
+  if(p->marksize)
+    {                       latest=*size1=latest->next;
+      if(p->marksize->next) latest=*size2=latest->next;
+    }
+  if(p->marklinewidth)      latest=*linewidth=latest->next;
+  if(p->markcolor)          latest=*color=latest->next;
+  if(p->markshape)          latest=*shape=latest->next;
+  if(p->markrotate)         latest=*rotate=latest->next;
+  if(p->marktext)           latest=*text=latest->next;
+  if(p->markfont)           latest=*font=latest->next;
+  if(p->markfontsize)       latest=*fontsize=latest->next;
+
+  /* Now that they are read, un-list each column sot they can be treated
+     independently (we don't want their 'next' pointer to interfere with
+     future steps). */
+  (*coord1)->next=(*coord2)->next=NULL;
+  if(p->marktext)       (*text)->next=NULL;
+  if(p->markfont)       (*font)->next=NULL;
+  if(p->marksize)       (*size1)->next=NULL;
+  if(p->markcolor)      (*color)->next=NULL;
+  if(p->markshape)      (*shape)->next=NULL;
+  if(p->markrotate)     (*rotate)->next=NULL;
+  if(p->markfontsize)   (*fontsize)->next=NULL;
+  if(p->marklinewidth)  (*linewidth)->next=NULL;
+  if(p->marksize && p->marksize->next) (*size2)->next=NULL;
+}
+
+
+
+
+
+/* Error message for cases that WCS mode has been requested by the
+   inputs don't have WCS. */
+static void
+ui_marks_error_no_wcs(void)
+{
+  error(EXIT_FAILURE, 0, "none of the input channel(s) have "
+        "WCS while you had defined your coordinates and sizes "
+        "to be in WCS mode (with '--mode=wcs'). If your "
+        "coordinates and sizes are in image coordinates "
+        "(in units of pixels), please use '--mode=img'");
+}
+
+
+
+
+
+static void
+ui_marks_read_coords(struct converttparams *p, gal_data_t **coord1,
+                     gal_data_t **coord2)
+{
+  int wcsfound=0;
+  gal_data_t *c1=*coord1, *c2=*coord2, *tmp;
+
+  /* If the coordinates are in WCS mode, convert them. */
+  if( !strcmp(p->mode, "wcs") )
+    {
+      /* Find the first channel with WCS and use it to convert the
+         coordinates to pixel coordinates. */
+      for(tmp=p->chll; tmp!=NULL; tmp=tmp->next)
+        if(tmp->wcs)
+          {
+            /* The coordinates need to have 64-bit floating point type for
+               the WCS conversion. */
+            c1=gal_data_copy_to_new_type_free(c1, GAL_TYPE_FLOAT64);
+            c2=gal_data_copy_to_new_type_free(c2, GAL_TYPE_FLOAT64);
+
+            /* Set the second one as the 'next' of the first and do the
+               conversion. */
+            c1->next=c2;
+            gal_wcs_world_to_img(c1, tmp->wcs, 1);
+            wcsfound=1;
+            c1->next=NULL;
+            break;
+          }
+
+      /* If no WCS could be found, abort. */
+      if(wcsfound==0) ui_marks_error_no_wcs();
+    }
+
+  /* The columns should have specific names. */
+  if(c1->name) free(c1->name);
+  if(c2->name) free(c2->name);
+  gal_checkset_allocate_copy(GAL_EPS_MARK_COLNAME_XPIX, &c1->name);
+  gal_checkset_allocate_copy(GAL_EPS_MARK_COLNAME_YPIX, &c2->name);
+
+  /* The columns should have specific types. */
+  *coord1=gal_data_copy_to_new_type_free(c1, GAL_TYPE_FLOAT32);
+  *coord2=gal_data_copy_to_new_type_free(c2, GAL_TYPE_FLOAT32);
+}
+
+
+
+
+
+/* In WCS-mode, the user has given sizes in WCS units (usually degrees). We
+   need to convert them to image coordinates for the EPS library. */
+static void
+ui_marks_size_to_image(struct converttparams *p, gal_data_t *size1,
+                       gal_data_t *size2, gal_data_t *shape)
+{
+  uint8_t *u;
+  float *f, *ff;
+  gal_data_t *tmp;
+  double *ps=NULL;
+
+  /* Multiplication factor based on size. */
+  double m = ( p->sizeinarcsec
+               ? 3600
+               : (p->sizeinarcmin ? 60 : 1.0 ) );
+
+  /* If both arrays to convert are NULL, then don't bother with the rest of
+     this function. */
+  if(size1==NULL && size2==NULL) return;
+
+  /* Find the first channel with WCS and use it to convert the
+     coordinates to pixel coordinates. */
+  for(tmp=p->chll; tmp!=NULL; tmp=tmp->next)
+    if(tmp->wcs)
+      ps=gal_wcs_pixel_scale(tmp->wcs);
+
+  /* If no WCS could be found, print an error. */
+  if(ps==NULL) ui_marks_error_no_wcs();
+
+  /* Use the first dimension's pixel scale for 'size1', and second
+     dimension's pixel scale for 'size2'. */
+  if(size1)
+    { ff=(f=size1->array)+size1->size; do *f /= m*ps[0]; while(++f<ff); }
+  if(size2)
+    {
+      /* The second size column is only relevant if a shape is defined.
+         Also, for the ellipse rows, we shouldn't multiply by the pixel
+         scale. */
+      if(shape)
+        {
+          u=shape->array;
+          ff=(f=size2->array)+size1->size;
+          do if(*u++!=GAL_EPS_MARK_SHAPE_ELLIPSE) *f /= m*ps[1];
+          while(++f<ff);
+        }
+    }
+}
+
+
+
+
+
+/* Make sure named-columns (like shape or color) are in the code-format. */
+static gal_data_t *
+ui_marks_read_named_cols(gal_data_t *in, int shape1_color2)
+{
+  size_t i;
+  uint8_t n, *u;
+  gal_data_t *out=NULL;
+  char *name=NULL, **strarr, *modestr;
+
+  /* Prepare the output based on type. */
+  if(in->type==GAL_TYPE_STRING)
+    {
+      /* Allocate the output dataset. */
+      out=gal_data_alloc(NULL, GAL_TYPE_UINT8, 1, in->dsize, NULL,
+                         0, in->minmapsize, in->quietmmap, NULL,
+                         NULL, NULL);
+
+      /* Go over the array and convert the names to codes. If the name is
+         not correct the functions will abort with an informative error.*/
+      u=out->array;
+      strarr=in->array;
+      switch(shape1_color2)
+        {
+        case 1: /* Shape */
+          for(i=0;i<in->size;++i) u[i]=gal_eps_shape_name_to_id(strarr[i]);
+          name="SHAPE";
+          break;
+        case 2: /* Color */
+          for(i=0;i<in->size;++i) u[i]=gal_color_name_to_id(strarr[i]);
+          name="COLOR";
+          break;
+        default:
+          error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' "
+                "to find and fix the problem. The operation code '%d' "
+                "isn't recognized", __func__, PACKAGE_BUGREPORT,
+                shape1_color2);
+        }
+
+      /* Clean the input column. */
+      gal_data_free(in);
+    }
+  else
+    {
+      /* Set the constants. */
+      name = shape1_color2==1 ? "SHAPE" : "COLOR";
+      modestr = shape1_color2==1 ? "shape" : "color";
+      n=shape1_color2==1 ? GAL_EPS_MARK_SHAPE_NUMBER : GAL_COLOR_NUMBER;
+
+      /* Convert the type and do a sanity check. */
+      out=gal_data_copy_to_new_type_free(in, GAL_TYPE_UINT8);
+      u=out->array;
+      for(i=0;i<in->size;++i)
+        {
+        if( u[i]==0 || u[i]>n )
+          error(EXIT_FAILURE, 0, "the %s numerical identifier '%u' "
+                "(in row %zu) is not recognized! The largest "
+                "numerical identifier for %ss is %u",
+                modestr, u[i], i, modestr, n);
+        }
+
+      /* Set the dataset name. */
+    }
+
+  /* Set the specific name of the output. */
+  if(out->name) free(out->name);
+  gal_checkset_allocate_copy(name, &out->name);
+
+  /* Return the output dataset. */
+  return out;
+}
+
+
+
+
+
+/* All numbered columns should have a float32 type with a specific name. */
+static gal_data_t *
+ui_marks_read_fixedtype_col(struct converttparams *p, gal_data_t *in,
+                            uint8_t type, char *name, int onlypositive,
+                            char *colname)
+{
+  float *f;
+  size_t i;
+  gal_data_t *out=gal_data_copy_to_new_type_free(in, type);
+
+  /* Small sanity check. */
+  if(type!=GAL_TYPE_FLOAT32 && type!=GAL_TYPE_STRING)
+    error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' "
+          "to fix the problem. The input's type should either be "
+          "float32 or string, but it is '%s'", __func__,
+          PACKAGE_BUGREPORT, gal_type_name(type, 1));
+
+  /* If it is important that this column is positive, do the check. */
+  if(onlypositive)
+    {
+      /* Small sanity check. */
+      if(type!=GAL_TYPE_FLOAT32)
+        error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' "
+              "to fix the problem. When 'onlypositive' is set, the "
+              "type should be float32, but it is '%s'",  __func__,
+              PACKAGE_BUGREPORT, gal_type_name(type, 1));
+
+      /* Make sure all the elements are positive. */
+      f=out->array;
+      for(i=0;i<out->size;++i)
+        if(f[i]<0.0)
+          error(EXIT_FAILURE, 0, "%s: column '%s', row %zu has a "
+                "negative value (%g)! This column's values should "
+                "be positive",
+                gal_fits_name_save_as_string(p->marksname, p->markshdu),
+                colname, i+1, f[i]);
+    }
+
+  /* Free the possibly existing current name. */
+  if(out->name) free(out->name);
+  gal_checkset_allocate_copy(name, &out->name);
+
+  /* Return the proper dataset. */
+  return out;
+}
+
+
+
+
+
+void
+ui_marks_read(struct converttparams *p)
+{
+  size_t i;
+  float *s2arr;
+  uint8_t *sharr;
+  gal_data_t *font=NULL, *fontsize=NULL;
+  gal_data_t *size2=NULL, *rotate=NULL, *text=NULL;
+  gal_data_t *color=NULL, *shape=NULL, *lwidth=NULL;
+  gal_data_t *coord1=NULL, *coord2=NULL, *size1=NULL;
+
+  /* Read the columns. */
+  ui_marks_read_raw(p, &coord1, &coord2, &size1, &size2,
+                    &lwidth, &color, &shape, &rotate, &text,
+                    &font, &fontsize);
+
+  /* Prepare the coordinates. */
+  ui_marks_read_coords(p, &coord1, &coord2);
+
+  /* Prepare the shape and color. */
+  if(shape) shape=ui_marks_read_named_cols(shape, 1);
+  if(color) color=ui_marks_read_named_cols(color, 2);
+
+  /* Set the precision to print floating point numbers as strings (if the
+     user has provided one. */
+  if(p->marktextprecision>0)
+    text->disp_precision=p->marktextprecision;
+
+  /* Prepare the size (the EPS library needs each dataset in the input list
+     for marks to have specific names and specific formats). */
+  if(size1)
+    size1 = ui_marks_read_fixedtype_col(p, size1, GAL_TYPE_FLOAT32,
+                                        GAL_EPS_MARK_COLNAME_SIZE1, 1,
+                                        p->marksize->v);
+  if(size2)
+    size2 = ui_marks_read_fixedtype_col(p, size2, GAL_TYPE_FLOAT32,
+                                        GAL_EPS_MARK_COLNAME_SIZE2, 1,
+                                        p->marksize->next->v);
+  if(rotate)
+    rotate = ui_marks_read_fixedtype_col(p, rotate, GAL_TYPE_FLOAT32,
+                                         GAL_EPS_MARK_COLNAME_ROTATE, 0,
+                                         p->markrotate);
+  if(lwidth)
+    lwidth = ui_marks_read_fixedtype_col(p, lwidth, GAL_TYPE_FLOAT32,
+                                         GAL_EPS_MARK_COLNAME_LINEWIDTH, 1,
+                                         p->marklinewidth);
+  if(text)
+    text = ui_marks_read_fixedtype_col(p, text, GAL_TYPE_STRING,
+                                       GAL_EPS_MARK_COLNAME_TEXT, 0,
+                                       p->marktext);
+  if(font)
+    font = ui_marks_read_fixedtype_col(p, font, GAL_TYPE_STRING,
+                                       GAL_EPS_MARK_COLNAME_FONT, 0,
+                                       p->markfont);
+  if(fontsize)
+    fontsize = ui_marks_read_fixedtype_col(p, fontsize, GAL_TYPE_FLOAT32,
+                                           GAL_EPS_MARK_COLNAME_FONTSIZE, 1,
+                                           p->markfontsize);
+
+  /* Convert the sizes to pixel if necessary. */
+  if( !strcmp(p->mode,"wcs") && p->sizeinpix==0 )
+    ui_marks_size_to_image(p, size1, size2, shape);
+
+  /* Put the columns in the list to pass to the EPS library. */
+  gal_list_data_add(&p->marks, coord1);
+  gal_list_data_add(&p->marks, coord2);
+  if(text)     gal_list_data_add(&p->marks, text);
+  if(font)     gal_list_data_add(&p->marks, font);
+  if(shape)    gal_list_data_add(&p->marks, shape);
+  if(color)    gal_list_data_add(&p->marks, color);
+  if(size1)    gal_list_data_add(&p->marks, size1);
+  if(size2)    gal_list_data_add(&p->marks, size2);
+  if(lwidth)   gal_list_data_add(&p->marks, lwidth);
+  if(rotate)   gal_list_data_add(&p->marks, rotate);
+  if(fontsize) gal_list_data_add(&p->marks, fontsize);
+
+  /* Some sanity checks. */
+  if(shape && size2)
+    {
+      sharr=shape->array;
+      s2arr=size2->array;
+      for(i=0;i<p->marks->size;++i)
+        if( sharr[i]==GAL_EPS_MARK_SHAPE_ELLIPSE
+            && ( s2arr[i]<=0 || s2arr[i]>1 ) )
+          error(EXIT_FAILURE, 0, "%g is not a valid 'size2' column "
+                "for an ellipse shape (from row number %zu of the "
+                "marks table). For an ellipse, the 'size2' column is "
+                "the axis ratio, so it should always be larger than "
+                "0 and smaller or equal to 1", s2arr[i], i+1);
+    }
+
+  /* For a check:
+  {
+    gal_data_t *tmp;
+    for(tmp=p->marks;tmp!=NULL;tmp=tmp->next)
+      printf("%s: %s\n", __func__, tmp->name);
+    exit(0);
+  } */
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/***********************************************************************/
+/****************       High-level preparations     ********************/
+/***********************************************************************/
 void
 ui_preparations(struct converttparams *p)
 {
@@ -1007,6 +1716,9 @@ ui_preparations(struct converttparams *p)
   /* Read the input channels. */
   ui_prepare_input_channels(p);
 
+  /* Read the marks info. */
+  if(p->marksname) ui_marks_read(p);
+
   /* Set the output name. */
   ui_set_output(p);
 }
diff --git a/bin/convertt/ui.h b/bin/convertt/ui.h
index 4f9c9521..19532115 100644
--- a/bin/convertt/ui.h
+++ b/bin/convertt/ui.h
@@ -34,6 +34,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 enum program_args_groups
 {
   UI_GROUP_FLUX = GAL_OPTIONS_GROUP_AFTER_COMMON,
+  UI_GROUP_MARKS
 };
 
 
@@ -42,8 +43,8 @@ enum program_args_groups
 
 /* Available letters for short options:
 
-   a d e f j k l n p r s t v y z
-   E G J O Q R W X Y
+   a d e f j k l n p s t v y z
+   E G J Q R W X Y
 */
 enum option_keys_enum
 {
@@ -61,11 +62,30 @@ enum option_keys_enum
   UI_KEY_CHANGE              = 'c',
   UI_KEY_CHANGEAFTERTRUNC    = 'C',
   UI_KEY_INVERT              = 'i',
+  UI_KEY_MODE                = 'O',
+  UI_KEY_MARKCOORDS          = 'r',
 
   /* Only with long version (start with a value 1000, the rest will be set
      automatically). */
   UI_KEY_COLORMAP            = 1000,
   UI_KEY_RGBTOHSV,
+  UI_KEY_MARKS,
+  UI_KEY_MARKSHDU,
+  UI_KEY_MARKSIZE,
+  UI_KEY_MARKLINEWIDTH,
+  UI_KEY_MARKCOLOR,
+  UI_KEY_MARKSHAPE,
+  UI_KEY_MARKROTATE,
+  UI_KEY_MARKTEXT,
+  UI_KEY_MARKFONT,
+  UI_KEY_MARKFONTSIZE,
+  UI_KEY_LISTCOLORS,
+  UI_KEY_LISTFONTS,
+  UI_KEY_SHOWFONTS,
+  UI_KEY_SIZEINPIX,
+  UI_KEY_SIZEINARCSEC,
+  UI_KEY_SIZEINARCMIN,
+  UI_KEY_MARKTEXTPRECISION,
 };
 
 
diff --git a/bin/fits/args.h b/bin/fits/args.h
index d4e81eae..2da61f2b 100644
--- a/bin/fits/args.h
+++ b/bin/fits/args.h
@@ -75,6 +75,19 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "pixelareaarcsec2",
+      UI_KEY_PIXELAREAARCSEC2,
+      0,
+      0,
+      "Pixel area in arc-seconds squared.",
+      UI_GROUP_EXTENSION_INFORMATION,
+      &p->pixelareaarcsec2,
+      GAL_OPTIONS_NO_ARG_TYPE,
+      GAL_OPTIONS_RANGE_0_OR_1,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET
+    },
     {
       "skycoverage",
       UI_KEY_SKYCOVERAGE,
diff --git a/bin/fits/fits.c b/bin/fits/fits.c
index e292c8f3..05de9dff 100644
--- a/bin/fits/fits.c
+++ b/bin/fits/fits.c
@@ -352,13 +352,12 @@ fits_datasum(struct fitsparams *p)
 
 
 
-static void
-fits_pixelscale(struct fitsparams *p)
+struct wcsprm *
+fits_read_check_wcs(struct fitsparams *p, size_t *ndim,
+                    char *operation)
 {
   int nwcs=0;
-  size_t i, ndim=0;
   struct wcsprm *wcs;
-  double multip, *pixelscale;
 
   /* Read the desired WCS. */
   wcs=gal_wcs_read(p->input->v, p->cp.hdu, p->cp.wcslinearmatrix,
@@ -366,11 +365,26 @@ fits_pixelscale(struct fitsparams *p)
 
   /* If a WCS doesn't exist, let the user know and return. */
   if(wcs)
-    ndim=wcs->naxis;
+    *ndim=wcs->naxis;
   else
     error(EXIT_FAILURE, 0, "%s (hdu %s): no WCS could be read by WCSLIB, "
-          "hence the pixel-scale cannot be determined", p->input->v,
-          p->cp.hdu);
+          "hence the %s cannot be determined", p->input->v,
+          p->cp.hdu, operation);
+
+  /* Return the WCS. */
+  return wcs;
+}
+
+
+
+
+
+static void
+fits_pixelscale(struct fitsparams *p)
+{
+  size_t i, ndim=0;
+  double multip, *pixelscale;
+  struct wcsprm *wcs=fits_read_check_wcs(p, &ndim, "pixel scale");
 
   /* Calculate the pixel-scale in each dimension. */
   pixelscale=gal_wcs_pixel_scale(wcs);
@@ -473,6 +487,42 @@ fits_pixelscale(struct fitsparams *p)
 
 
 
+static void
+fits_pixelarea(struct fitsparams *p)
+{
+  double area;
+  size_t i, ndim;
+  double *pixelscale;
+  struct wcsprm *wcs=fits_read_check_wcs(p, &ndim, "pixel area");
+
+  /* Basic sanity checks. */
+  if(ndim!=2)
+    error(EXIT_FAILURE, 0, "the pixel area can only be calculated "
+          "for two dimensional data (images). The given input has "
+          "%zu dimensions", ndim);
+  for(i=0;i<ndim;++i)
+    if( strcmp(wcs->cunit[i], "deg") )
+      error(EXIT_FAILURE, 0, "%s (hdu: %s): dimension %zu doesn't "
+            "have a unit of degrees (value to 'CUNIT%zu'), but "
+            "'%s'! In order to use '--pixelareaarcsec2', the units "
+            "of the WCS have to be in degrees", p->input->v,
+            p->cp.hdu, i+1, i+1, wcs->cunit[i]);
+
+  /* Calculate the pixel-scale in each dimension. */
+  pixelscale=gal_wcs_pixel_scale(wcs);
+
+  /* Calcluate the pixel area (currently only in arcsec^N) */
+  area=1;
+  for(i=0;i<ndim;++i) area *= pixelscale[i]*3600;
+  printf("%g\n", area);
+
+  /* Clean up. */
+  wcsfree(wcs);
+  free(pixelscale);
+}
+
+
+
 static void
 fits_skycoverage(struct fitsparams *p)
 {
@@ -822,15 +872,16 @@ fits(struct fitsparams *p)
     case FITS_MODE_HDU:
 
       /* Options that must be called alone. */
-      if(p->numhdus) fits_hdu_number(p);
-      else if(p->datasum)       fits_datasum(p);
-      else if(p->pixelscale)    fits_pixelscale(p);
-      else if(p->skycoverage)   fits_skycoverage(p);
-      else if(p->hasimagehdu)   fits_certain_hdu(p, 0, 0);
-      else if(p->hastablehdu)   fits_certain_hdu(p, 0, 1);
-      else if(p->listimagehdus) fits_certain_hdu(p, 1, 0);
-      else if(p->listtablehdus) fits_certain_hdu(p, 1, 1);
-      else if(p->listallhdus)   fits_list_all_hdus(p);
+      if(p->numhdus)               fits_hdu_number(p);
+      else if(p->datasum)          fits_datasum(p);
+      else if(p->pixelscale)       fits_pixelscale(p);
+      else if(p->pixelareaarcsec2) fits_pixelarea(p);
+      else if(p->skycoverage)      fits_skycoverage(p);
+      else if(p->hasimagehdu)      fits_certain_hdu(p, 0, 0);
+      else if(p->hastablehdu)      fits_certain_hdu(p, 0, 1);
+      else if(p->listimagehdus)    fits_certain_hdu(p, 1, 0);
+      else if(p->listtablehdus)    fits_certain_hdu(p, 1, 1);
+      else if(p->listallhdus)      fits_list_all_hdus(p);
 
       /* Options that can be called together. */
       else
diff --git a/bin/fits/main.h b/bin/fits/main.h
index 7bfd2af8..0cd60c29 100644
--- a/bin/fits/main.h
+++ b/bin/fits/main.h
@@ -63,6 +63,7 @@ struct fitsparams
   uint8_t           numhdus;   /* Print number of HDUs in FITS file.    */
   uint8_t           datasum;   /* Calculate and print HDU's datasum.    */
   uint8_t        pixelscale;   /* Calculate and print HDU's pixelscale. */
+  uint8_t  pixelareaarcsec2;   /* Return pixel area in arcsec^2.        */
   uint8_t       skycoverage;   /* Calculate and image coverage in WCS.  */
   uint8_t       hastablehdu;   /* File has atleast one table HDU.       */
   uint8_t       hasimagehdu;   /* File has atleast one image HDU.       */
diff --git a/bin/fits/ui.c b/bin/fits/ui.c
index 4a8be610..20007689 100644
--- a/bin/fits/ui.c
+++ b/bin/fits/ui.c
@@ -421,8 +421,9 @@ ui_read_check_only_options(struct fitsparams *p)
 
   /* Same for the extension-related options */
   if( p->remove || p->copy || p->cut || p->numhdus || p->datasum
-      || p->pixelscale || p->skycoverage || p->hastablehdu
-      || p->hasimagehdu || p->listtablehdus || p->listimagehdus )
+      || p->pixelscale || p->pixelareaarcsec2 || p->skycoverage
+      || p->hastablehdu || p->hasimagehdu || p->listtablehdus
+      || p->listimagehdus )
     {
       /* A small sanity check. */
       if(p->mode!=FITS_MODE_INVALID)
@@ -431,8 +432,9 @@ ui_read_check_only_options(struct fitsparams *p)
 
       /* Some HDU options cannot be called with other options. */
       stdoutcheck = ( p->numhdus + p->datasum + p->pixelscale
-                      + p->skycoverage + p->hastablehdu + p->hasimagehdu
-                      + p->listtablehdus + p->listimagehdus );
+                      + p->pixelareaarcsec2 + p->skycoverage
+                      + p->hastablehdu + p->hasimagehdu + p->listtablehdus
+                      + p->listimagehdus );
 
       /* Make sure if an output file is needed. */
       if(stdoutcheck)
@@ -456,9 +458,10 @@ ui_read_check_only_options(struct fitsparams *p)
           if( ( p->numhdus || p->hastablehdu || p->hasimagehdu
                 || p->listtablehdus || p->listimagehdus)
               && p->cp.hdu==NULL )
-            error(EXIT_FAILURE, 0, "a HDU (extension) is necessary for the "
-                  "'--datasum', '--pixelscale' or '--skycoverage' options. "
-                  "Please use the '--hdu' (or '-h') option to select one");
+            error(EXIT_FAILURE, 0, "a HDU (extension) is necessary "
+                  "for the options that return information on one HDU "
+                  "(like ""'--pixelscale' or '--skycoverage'. Please "
+                  "use the '--hdu' (or '-h') option to select one");
         }
       else
         {
diff --git a/bin/fits/ui.h b/bin/fits/ui.h
index 71020c4b..f74237d5 100644
--- a/bin/fits/ui.h
+++ b/bin/fits/ui.h
@@ -74,6 +74,7 @@ enum option_keys_enum
   UI_KEY_TITLE        = 1000,
   UI_KEY_DATASUM,
   UI_KEY_PIXELSCALE,
+  UI_KEY_PIXELAREAARCSEC2,
   UI_KEY_SKYCOVERAGE,
   UI_KEY_HASTABLEHDU,
   UI_KEY_HASIMAGEHDU,
diff --git a/bin/table/arithmetic.c b/bin/table/arithmetic.c
index bac5f669..901c774d 100644
--- a/bin/table/arithmetic.c
+++ b/bin/table/arithmetic.c
@@ -178,9 +178,13 @@ arithmetic_init_wcs(struct tableparams *p, char *operator)
 /* Set the operator code from the given string. */
 static int
 arithmetic_set_operator(struct tableparams *p, char *string,
-                        size_t *num_operands)
+                        size_t *num_operands, int *inlib)
 {
-  int op=gal_arithmetic_set_operator(string, num_operands);;
+  /* Use the library's main function for its own operators. */
+  int op=gal_arithmetic_set_operator(string, num_operands);
+
+  /* Mark if this operator is in the library or not. */
+  *inlib = op==GAL_ARITHMETIC_OP_INVALID ? 0 : 1;
 
   /* Set the operator and number of operands. */
   if( op==GAL_ARITHMETIC_OP_INVALID )
@@ -299,7 +303,9 @@ arithmetic_init(struct tableparams *p, struct 
arithmetic_token **arith,
       node=arithmetic_add_new_to_end(arith);
 
       /* See if the token is an operator, if not check other cases.  */
-      node->operator=arithmetic_set_operator(p, token, &node->num_operands);
+      node->operator=arithmetic_set_operator(p, token,
+                                             &node->num_operands,
+                                             &node->inlib);
       if(node->operator==GAL_ARITHMETIC_OP_INVALID)
         {
           /* Token is a single number.*/
@@ -852,8 +858,8 @@ arithmetic_operator_run(struct tableparams *p,
   if(p->cp.quiet) flags |= GAL_ARITHMETIC_FLAG_QUIET;
   if(p->envseed)  flags |= GAL_ARITHMETIC_FLAG_ENVSEED;
 
-  /* When 'num_operands!=0', the operator is in the library. */
-  if(token->num_operands)
+  /* If this operator is in the library, we should pop everything here. */
+  if(token->inlib)
     {
       /* Pop the necessary number of operators. Note that the
          operators are poped from a linked list (which is
@@ -862,6 +868,9 @@ arithmetic_operator_run(struct tableparams *p,
          last (right most, in in-fix notation) input operand.*/
       switch(token->num_operands)
         {
+        case 0:
+          break;
+
         case 1:
           d1=arithmetic_stack_pop(stack, token->operator, NULL);
           break;
diff --git a/bin/table/main.h b/bin/table/main.h
index 73519fe0..73578e51 100644
--- a/bin/table/main.h
+++ b/bin/table/main.h
@@ -64,6 +64,7 @@ struct arithmetic_token
 {
   /* First layer elements. */
   int            operator;  /* OPERATOR: Code of operator.                */
+  int               inlib;  /* OPERATOR: if operator is in library.       */
   size_t     num_operands;  /* OPERATOR: Number of required operands.     */
   size_t            index;  /* OPERAND: Index in requested columns.       */
   char       *id_at_usage;  /* OPERAND: col identifier at usage time.     */
diff --git a/bootstrap.conf b/bootstrap.conf
index 891f077b..f65c499d 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -220,6 +220,7 @@ gnulib_modules="
     sys_time
     strptime
     faccessat
+    strcasecmp
     system-posix
     secure_getenv
     git-version-gen
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index d9d67d54..be7244b3 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -267,14 +267,10 @@ New to GNU/Linux?
 
 Tutorials
 
-* Sufi simulates a detection::  Simulating a detection.
 * General program usage tutorial::  Tutorial on many programs in generic 
scenario.
 * Detecting large extended targets::  NoiseChisel for huge extended targets.
 * Building the extended PSF::   How to extract an extended PSF from science 
data.
-
-Sufi simulates a detection
-
-* General program usage tutorial::
+* Sufi simulates a detection::  Simulating a detection.
 
 General program usage tutorial
 
@@ -283,7 +279,7 @@ General program usage tutorial
 * Setup and data download::     Setup this template and download datasets.
 * Dataset inspection and cropping::  Crop the flat region to use in next steps.
 * Angular coverage on the sky::  Measure the field size on the sky.
-* Cosmological coverage::       Measure the field size at different redshifts.
+* Cosmological coverage and visualizing tables::  Size in Mpc2, and plotting 
its change.
 * Building custom programs with the library::  Easy way to build new programs.
 * Option management and configuration files::  Dealing with options and 
configuring them.
 * Warping to a new pixel grid::  Transforming/warping the dataset.
@@ -296,7 +292,9 @@ General program usage tutorial
 * Column statistics color-magnitude diagram::  Visualizing column correlations.
 * Aperture photometry::         Doing photometry on a fixed aperture.
 * Matching catalogs::           Easily find corresponding rows from two 
catalogs.
-* Finding reddest clumps and visual inspection::  Selecting some targets and 
inspecting them.
+* Reddest clumps cutouts and parallelization::
+* FITS images in a publication::
+* Marking objects for publication::
 * Writing scripts to automate the steps::  Scripts will greatly help in 
re-doing things fast.
 * Citing and acknowledging Gnuastro::  How to cite and acknowledge Gnuastro in 
your papers.
 
@@ -443,16 +441,29 @@ Invoking Fits
 
 ConvertType
 
+* Raster and Vector graphics::  Images coming from nature, and the abstract.
 * Recognized file formats::     Recognized file formats
 * Color::                       Some explanations on color.
 * Aligning images with small WCS offsets::  When the WCS slightly differs.
 * Annotations for figure in paper::  Adding coordinates or physical scale.
 * Invoking astconvertt::        Options and arguments to ConvertType.
 
+Color
+
+* Pixel colors::                Multiple filters in each pixel.
+* Colormaps for single-channel pixels::  Better display of single-filter 
images.
+* Vector graphics colors::
+
 Annotations for figure in paper
 
 * Full script of annotations on figure::  All the steps in one script
 
+Invoking ConvertType
+
+* ConvertType input and output::  Input/output file names and formats.
+* Pixel visualization::         Visualizing the pixels in the output.
+* Drawing with vector graphics::  Adding marks in many shapes and colors over 
the pixels.
+
 Table
 
 * Column arithmetic::           How to do operations on table columns.
@@ -495,6 +506,7 @@ Arithmetic operators
 
 * Basic mathematical operators::  For example +, -, /, log, pow, and etc.
 * Trigonometric and hyperbolic operators::  sin, cos, atan, asinh, and etc
+* Constants::
 * Unit conversion operators::   magnitudes to counts, or parsecs to AUs, and 
etc.
 * Statistical operators::       Statistics of a single dataset (for example 
mean).
 * Stacking operators::          Coadding or combining multiple datasets into 
one.
@@ -757,6 +769,7 @@ Gnuastro library
 * Labeled datasets::            Working with Segmented/labeled datasets.
 * Convolution functions::       Library functions to do convolution.
 * Interpolation::               Interpolate (over blank values possibly).
+* Color functions::             Definitions and operations related to colors.
 * Git wrappers::                Wrappers for functions in libgit2.
 * Unit conversion library::     Converting between recognized units.
 * Spectral lines library::      Functions for operating on Spectral lines.
@@ -1710,7 +1723,7 @@ University of Salento, Lecce, Italy.@*
 Centre de Recherche Astrophysique de Lyon (CRAL), Lyon, France.@*
 Instituto de Astrofisica de Canarias (IAC), Tenerife, Spain.@*
 Centro de Estudios de F@'isica del Cosmos de Arag@'on (CEFCA), Teruel, Spain.@*
-Google Summer of Code 2020 and 2021
+Google Summer of Code 2020, 2021 and 2022
 @end quotation
 
 
@@ -1734,19 +1747,11 @@ To help new users have a smooth and easy start with 
Gnuastro, in this chapter se
 These tutorials demonstrate the capabilities of different Gnuastro programs 
and libraries, along with tips and guidelines for the best practices of using 
them in various realistic situations.
 
 We strongly recommend going through these tutorials to get a good feeling of 
how the programs are related (built in a modular design to be used together in 
a pipeline), very similar to the core Unix-based programs that they were 
modeled on.
-Therefore these tutorials will greatly help in optimally using Gnuastro's 
programs (and generally, the Unix-like command-line environment) effectively 
for your research.
+Therefore these tutorials will help in optimally using Gnuastro's programs 
(and generally, the Unix-like command-line environment) effectively for your 
research.
 
-In @ref{Sufi simulates a detection}, we'll start with a fictional@footnote{The 
two historically motivated tutorials (@ref{Sufi simulates a detection} is not 
intended to be a historical reference (the historical facts of this fictional 
tutorial used Wikipedia as a reference).
-This form of presenting a tutorial was influenced by the PGF/TikZ and Beamer 
manuals.
-They are both packages in @TeX{} and @LaTeX{}, the first is a high-level 
vector graphic programming environment, while with the second you can make 
presentation slides.
-On a similar topic, there are also some nice words of wisdom for Unix-like 
systems called @url{http://catb.org/esr/writings/unix-koans, Rootless Root}.
-These also have a similar style but they use a mythical figure named Master 
Foo.
-If you already have some experience in Unix-like systems, you will definitely 
find these Unix Koans entertaining/educative.} tutorial explaining how Abd 
al-rahman Sufi (903 -- 986 A.D., the first recorded description of ``nebulous'' 
objects in the heavens is attributed to him) could have used some of Gnuastro's 
programs for a realistic simulation of his observations and see if his 
detection of nebulous objects was trust-able.
-Because all conditions are under control in a simulated/mock 
environment/dataset, mock datasets can be a valuable tool to inspect the 
limitations of your data analysis and processing.
-But they need to be as realistic as possible, so the first tutorial is 
dedicated to this important step of an analysis.
-
-The next two tutorials (@ref{General program usage tutorial} and 
@ref{Detecting large extended targets}) use real input datasets from some of 
the deep Hubble Space Telescope (HST) images and the Sloan Digital Sky Survey 
(SDSS) respectively.
+The first three tutorials (@ref{General program usage tutorial} and 
@ref{Detecting large extended targets} and @ref{Building the extended PSF}) use 
real input datasets from some of the deep Hubble Space Telescope (HST) images, 
the Sloan Digital Sky Survey (SDSS) and the Javalambre Photometric Local 
Universe Survey (J-PLUS) respectively.
 Their aim is to demonstrate some real-world problems that many astronomers 
often face and how they can be solved with Gnuastro's programs.
+The fourth tutorial (@ref{Sufi simulates a detection}) focuses on simulating 
astronomical images, which is another critical aspect of any analysis!
 
 The ultimate aim of @ref{General program usage tutorial} is to detect galaxies 
in a deep HST image, measure their positions and brightness and select those 
with the strongest colors.
 In the process, it takes many detours to introduce you to the useful 
capabilities of many of the programs.
@@ -1765,6 +1770,16 @@ In Gnuastro we have multiple installed scripts for this 
job.
 Their usage and logic behind best tuning them for the particular step, is 
fully described in this tutorial, on a real dataset.
 The tutorial concludes with subtracting that extended PSF from the science 
image; thus giving you a cleaner image (with no scattered light of the brighter 
stars) for your higher-level analysis.
 
+@ref{Sufi simulates a detection} has a fictional@footnote{The two historically 
motivated tutorials (@ref{Sufi simulates a detection} is not intended to be a 
historical reference (the historical facts of this fictional tutorial used 
Wikipedia as a reference).)
+This form of presenting a tutorial was influenced by the PGF/TikZ and Beamer 
manuals.
+They are both packages in @TeX{} and @LaTeX{}, the first is a high-level 
vector graphic programming environment, while with the second you can make 
presentation slides.
+On a similar topic, there are also some nice words of wisdom for Unix-like 
systems called @url{http://catb.org/esr/writings/unix-koans, Rootless Root}.
+These also have a similar style but they use a mythical figure named Master 
Foo.
+If you already have some experience in Unix-like systems, you will definitely 
find these Unix Koans entertaining/educative.} setting!
+Showing how Abd al-rahman Sufi (903 -- 986 A.D., the first recorded 
description of ``nebulous'' objects in the heavens is attributed to him) could 
have used some of Gnuastro's programs for a realistic simulation of his 
observations and see if his detection of nebulous objects was trust-able.
+Because all conditions are under control in a simulated/mock 
environment/dataset, mock datasets can be a valuable tool to inspect the 
limitations of your data analysis and processing.
+But they need to be as realistic as possible, so this tutorial is dedicated to 
this important step of an analysis (simulations).
+
 In these tutorials, we have intentionally avoided too many cross references to 
make it more easy to read.
 For more information about a particular program, you can visit the section 
with the same name as the program in this book.
 Each program section in the subsequent chapters starts by explaining the 
general concepts behind what it does, for example see @ref{Convolve}.
@@ -1772,875 +1787,329 @@ If you only want practical information on running a 
program, for example its opt
 For an explanation of the conventions we use in the example codes through the 
book, please see @ref{Conventions}.
 
 @menu
-* Sufi simulates a detection::  Simulating a detection.
 * General program usage tutorial::  Tutorial on many programs in generic 
scenario.
 * Detecting large extended targets::  NoiseChisel for huge extended targets.
 * Building the extended PSF::   How to extract an extended PSF from science 
data.
+* Sufi simulates a detection::  Simulating a detection.
 @end menu
 
 
-@node Sufi simulates a detection, General program usage tutorial, Tutorials, 
Tutorials
-@section Sufi simulates a detection
-
-@cindex Azophi
-@cindex Abd al-rahman Sufi
-@cindex Sufi, Abd al-rahman
-It is the year 953 A.D. and Abd al-rahman Sufi (903 -- 986 A.D.)@footnote{In 
Latin Sufi is known as Azophi.
-He was an Iranian astronomer.
-His manuscript ``Book of fixed stars'' contains the first recorded 
observations of the Andromeda galaxy, the Large Magellanic Cloud and seven 
other non-stellar or `nebulous' objects.}  is in Shiraz as a guest astronomer.
-He had come there to use the advanced 123 centimeter astrolabe for his studies 
on the Ecliptic.
-However, something was bothering him for a long time.
-While mapping the constellations, there were several non-stellar objects that 
he had detected in the sky, one of them was in the Andromeda constellation.
-During a trip he had to Yemen, Sufi had seen another such object in the 
southern skies looking over the Indian ocean.
-He wasn't sure if such cloud-like non-stellar objects (which he was the first 
to call `Sah@={a}bi' in Arabic or `nebulous') were real astronomical objects or 
if they were only the result of some bias in his observations.
-Could such diffuse objects actually be detected at all with his detection 
technique?
 
-@cindex Almagest
-@cindex Claudius Ptolemy
-@cindex Ptolemy, Claudius
-He still had a few hours left until nightfall (when he would continue his 
studies on the ecliptic) so he decided to find an answer to this question.
-He had thoroughly studied Claudius Ptolemy's (90 -- 168 A.D) Almagest and had 
made lots of corrections to it, in particular in measuring the brightness.
-Using his same experience, he was able to measure a magnitude for the objects 
and wanted to simulate his observation to see if a simulated object with the 
same brightness and size could be detected in a simulated noise with the same 
detection technique.
-The general outline of the steps he wants to take are:
+@node General program usage tutorial, Detecting large extended targets, 
Tutorials, Tutorials
+@section General program usage tutorial
 
-@enumerate
+@cindex Hubble Space Telescope (HST)
+@cindex Colors, broad-band photometry
+Measuring colors of astronomical objects in broad-band or narrow-band images 
is one of the most basic and common steps in astronomical analysis.
+Here, we will use Gnuastro's programs to get a physical scale (area at certain 
redshifts) of the field we are studying, detect objects in a Hubble Space 
Telescope (HST) image, measure their colors and identify the ones with the 
strongest colors, do a visual inspection of these objects and inspect spatial 
position in the image.
+After this tutorial, you can also try the @ref{Detecting large extended 
targets} tutorial which goes into a little more detail on detecting very low 
surface brightness signal.
 
-@item
-Make some mock profiles in an over-sampled image.
-The initial mock image has to be over-sampled prior to convolution or other 
forms of transformation in the image.
-Through his experiences, Sufi knew that this is because the image of heavenly 
bodies is actually transformed by the atmosphere or other sources outside the 
atmosphere (for example gravitational lenses) prior to being sampled on an 
image.
-Since that transformation occurs on a continuous grid, to best approximate it, 
he should do all the work on a finer pixel grid.
-In the end he can re-sample the result to the initially desired grid size.
+During the tutorial, we will take many detours to explain, and practically 
demonstrate, the many capabilities of Gnuastro's programs.
+In the end you will see that the things you learned during this tutorial are 
much more generic than this particular problem and can be used in solving a 
wide variety of problems involving the analysis of data (images or tables).
+So please don't rush, and go through the steps patiently to optimally master 
Gnuastro.
 
-@item
-@cindex PSF
-Convolve the image with a point spread function (PSF, see @ref{PSF}) that is 
over-sampled to the same resolution as the mock image.
-Since he wants to finish in a reasonable time and the PSF kernel will be very 
large due to oversampling, he has to use frequency domain convolution which has 
the side effect of dimming the edges of the image.
-So in the first step above he also has to build the image to be larger by at 
least half the width of the PSF convolution kernel on each edge.
+@cindex XDF survey
+@cindex eXtreme Deep Field (XDF) survey
+In this tutorial, we'll use the HST@url{https://archive.stsci.edu/prepds/xdf, 
eXtreme Deep Field} dataset.
+Like almost all astronomical surveys, this dataset is free for download and 
usable by the public.
+You will need the following tools in this tutorial: Gnuastro, SAO DS9 
@footnote{See @ref{SAO DS9}, available at 
@url{http://ds9.si.edu/site/Home.html}}, GNU 
Wget@footnote{@url{https://www.gnu.org/software/wget}}, and AWK (most common 
implementation is GNU AWK@footnote{@url{https://www.gnu.org/software/gawk}}).
 
-@item
-With all the transformations complete, the image should be re-sampled to the 
same size of the pixels in his detector.
+This tutorial was first prepared for the ``Exploring the Ultra-Low Surface 
Brightness Universe'' workshop (November 2017) at the ISSI in Bern, Switzerland.
+It was further extended in the ``4th Indo-French Astronomy School'' (July 
2018) organized by LIO, CRAL CNRS UMR5574, UCBL, and IUCAA in Lyon, France.
+We are very grateful to the organizers of these workshops and the attendees 
for the very fruitful discussions and suggestions that made this tutorial 
possible.
 
-@item
-He should remove those extra pixels on all edges to remove frequency domain 
convolution artifacts in the final product.
+@cartouche
+@noindent
+@strong{Write the example commands manually:} Try to type the example commands 
on your terminal manually and use the history feature of your command-line (by 
pressing the ``up'' button to retrieve previous commands).
+Don't simply copy and paste the commands shown here.
+This will help simulate future situations when you are processing your own 
datasets.
+@end cartouche
 
-@item
-He should add noise to the (until now, noise-less) mock image.
-After all, all observations have noise associated with them.
 
-@end enumerate
+@menu
+* Calling Gnuastro's programs::  Easy way to find Gnuastro's programs.
+* Accessing documentation::     Access to manual of programs you are running.
+* Setup and data download::     Setup this template and download datasets.
+* Dataset inspection and cropping::  Crop the flat region to use in next steps.
+* Angular coverage on the sky::  Measure the field size on the sky.
+* Cosmological coverage and visualizing tables::  Size in Mpc2, and plotting 
its change.
+* Building custom programs with the library::  Easy way to build new programs.
+* Option management and configuration files::  Dealing with options and 
configuring them.
+* Warping to a new pixel grid::  Transforming/warping the dataset.
+* NoiseChisel and Multiextension FITS files::  Running NoiseChisel and having 
multiple HDUs.
+* NoiseChisel optimization for detection::  Check NoiseChisel's operation and 
improve it.
+* NoiseChisel optimization for storage::  Dramatically decrease output's 
volume.
+* Segmentation and making a catalog::  Finding true peaks and creating a 
catalog.
+* Measuring the dataset limits::  One way to measure the ``depth'' of your 
data.
+* Working with catalogs estimating colors::  Estimating colors using the 
catalogs.
+* Column statistics color-magnitude diagram::  Visualizing column correlations.
+* Aperture photometry::         Doing photometry on a fixed aperture.
+* Matching catalogs::           Easily find corresponding rows from two 
catalogs.
+* Reddest clumps cutouts and parallelization::
+* FITS images in a publication::
+* Marking objects for publication::
+* Writing scripts to automate the steps::  Scripts will greatly help in 
re-doing things fast.
+* Citing and acknowledging Gnuastro::  How to cite and acknowledge Gnuastro in 
your papers.
+@end menu
 
-Fortunately Sufi had heard of GNU Astronomy Utilities from a colleague in 
Isfahan (where he worked) and had installed it on his computer a year before.
-It had tools to do all the steps above.
-He had used MakeProfiles before, but wasn't sure which columns he had chosen 
in his user or system wide configuration files for which parameters, see 
@ref{Configuration files}.
-So to start his simulation, Sufi runs MakeProfiles with the @option{-P} option 
to make sure what columns in a catalog MakeProfiles currently recognizes and 
the output image parameters.
-In particular, Sufi is interested in the recognized columns (shown below).
+@node Calling Gnuastro's programs, Accessing documentation, General program 
usage tutorial, General program usage tutorial
+@subsection Calling Gnuastro's programs
+A handy feature of Gnuastro is that all program names start with @code{ast}.
+This will allow your command-line processor to easily list and auto-complete 
Gnuastro's programs for you.
+Try typing the following command (press @key{TAB} key when you see 
@code{<TAB>}) to see the list:
 
 @example
-$ astmkprof -P
+$ ast<TAB><TAB>
+@end example
 
-[[[ ... Truncated lines ... ]]]
+@noindent
+Any program that starts with @code{ast} (including all Gnuastro programs) will 
be shown.
+By choosing the subsequent characters of your desired program and pressing 
@key{<TAB><TAB>} again, the list will narrow down and the program name will 
auto-complete once your input characters are unambiguous.
+In short, you often don't need to type the full name of the program you want 
to run.
 
-# Output:
- type         float32     # Type of output: e.g., int16, float32, etc...
- mergedsize   1000,1000   # Number of pixels along first FITS axis.
- oversample   5           # Scale of oversampling (>0 and odd).
+@node Accessing documentation, Setup and data download, Calling Gnuastro's 
programs, General program usage tutorial
+@subsection Accessing documentation
 
-[[[ ... Truncated lines ... ]]]
+Gnuastro contains a large number of programs and it is natural to forget the 
details of each program's options or inputs and outputs.
+Therefore, before starting the analysis steps of this tutorial, let's review 
how you can access this book to refresh your memory any time you want, without 
having to take your hands off the keyboard.
 
-# Columns, by info (see `--searchin'), or number (starting from 1):
- ccol         2           # Coord. columns (one call for each dim.).
- ccol         3           # Coord. columns (one call for each dim.).
- fcol         4           # sersic (1), moffat (2), gaussian (3), point
-                          # (4), flat (5), circumference (6), distance
-                          # (7), custom-prof (8), azimuth (9),
-                          # custom-img (10).
- rcol         5           # Effective radius or FWHM in pixels.
- ncol         6           # Sersic index or Moffat beta.
- pcol         7           # Position angle.
- qcol         8           # Axis ratio.
- mcol         9           # Magnitude.
- tcol         10          # Truncation in units of radius or pixels.
+When you install Gnuastro, this book is also installed on your system along 
with all the programs and libraries, so you don't need an internet connection 
to access/read it.
+Also, by accessing this book as described below, you can be sure that it 
corresponds to your installed version of Gnuastro.
 
-[[[ ... Truncated lines ... ]]]
+@cindex GNU Info
+GNU Info@footnote{GNU Info is already available on almost all Unix-like 
operating systems.} is the program in charge of displaying the manual on the 
command-line (for more, see @ref{Info}).
+To see this whole book on your command-line, please run the following command 
and press subsequent keys.
+Info has its own mini-environment, therefore we'll show the keys that must be 
pressed in the mini-environment after a @code{->} sign.
+You can also ignore anything after the @code{#} sign in the middle of the 
line, they are only for your information.
 
+@example
+$ info gnuastro                # Open the top of the manual.
+-> <SPACE>                     # All the book chapters.
+-> <SPACE>                     # Continue down: show sections.
+-> <SPACE> ...                 # Keep pressing space to go down.
+-> q                           # Quit Info, return to the command-line.
 @end example
 
-@noindent
-In Gnuastro, column counting starts from 1, so the columns are ordered such 
that the first column (number 1) can be an ID he specifies for each object (and 
MakeProfiles ignores), each subsequent column is used for another property of 
the profile.
-It is also possible to use column names for the values of these options and 
change these defaults, but Sufi preferred to stick to the defaults.
-Fortunately MakeProfiles has the capability to also make the PSF which is to 
be used on the mock image and using the @option{--prepforconv} option, he can 
also make the mock image to be larger by the correct amount and all the sources 
to be shifted by the correct amount.
-
-For his initial check he decides to simulate the nebula in the Andromeda 
constellation.
-The night he was observing, the PSF had roughly a FWHM of about 5 pixels, so 
as the first row (profile) in the table below, he defines the PSF parameters.
-Sufi sets the radius column (@code{rcol} above, fifth column) to @code{5.000}, 
he also chooses a Moffat function for its functional form.
-Remembering how diffuse the nebula in the Andromeda constellation was, he 
decides to simulate it with a mock S@'{e}rsic index 1.0 profile.
-He wants the output to be 499 pixels by 499 pixels, so he can put the center 
of the mock profile in the central pixel of the image which is the 250th pixel 
along both dimensions (note that an even number doesn't have a ``central'' 
pixel).
-
-Looking at his drawings of it, he decides a reasonable effective radius for it 
would be 40 pixels on this image pixel scale (second row, 5th column below).
-He also sets the axis ratio (0.4) and position angle (-25 degrees) to 
approximately correct values too, and finally he sets the total magnitude of 
the profile to 3.44 which he had measured.
-Sufi also decides to truncate both the mock profile and PSF at 5 times the 
respective radius parameters.
-In the end he decides to put four stars on the four corners of the image at 
very low magnitudes as a visual scale.
-While he was preparing the catalog, one of his students approached him and was 
also following the steps.
+The thing that greatly simplifies navigation in Info is the links (regions 
with an underline).
+You can immediately go to the next link in the page with the @key{<TAB>} key 
and press @key{<ENTER>} on it to go into that part of the manual.
+Try the commands above again, but this time also use @key{<TAB>} to go to the 
links and press @key{<ENTER>} on them to go to the respective section of the 
book.
+Then follow a few more links and go deeper into the book.
+To return to the previous page, press @key{l} (small L).
+If you are searching for a specific phrase in the whole book (for example an 
option name), press @key{s} and type your search phrase and end it with an 
@key{<ENTER>}.
 
-@noindent
-As described above, the catalog of profiles to build will be a table (multiple 
columns of numbers) like below:
+You don't need to start from the top of the manual every time.
+For example, to get to @ref{Invoking astnoisechisel}, run the following 
command.
+In general, all programs have such an ``Invoking ProgramName'' section in this 
book.
+These sections are specifically for the description of inputs, outputs and 
configuration options of each program.
+You can access them directly for each program by giving its executable name to 
Info.
 
 @example
-0  0.000   0.000  2  5   4.7  0.0  1.0  30.0  5.0
-1  250.0   250.0  1  40  1.0  -25  0.4  3.44  5.0
-2  50.00   50.00  4  0   0.0  0.0  0.0  6.00  0.0
-3  450.0   50.00  4  0   0.0  0.0  0.0  6.50  0.0
-4  50.00   450.0  4  0   0.0  0.0  0.0  7.00  0.0
-5  450.0   450.0  4  0   0.0  0.0  0.0  7.50  0.0
+$ info astnoisechisel
 @end example
 
-This contains all the ``data'' to build the profile, and you can easily pass 
it to Gnuastro's MakeProfiles: since Sufi already knows the columns and 
expected values very good, he has placed the information in the proper columns.
-However, when the student sees this, he just sees a mumble-jumble of numbers!
-Generally, Sufi explains to the student that even if you know the number 
positions and values very nicely today, in a couple of months you will forget!
-It will then be very hard for you to interpret the numbers properly.
-So you should never use naked data (or data without any extra information).
-
-@cindex Metadata
-Data (or information) that describes other data is called ``metadata''!
-One common example is column names (the name of a column is itself a data 
element, but data that describes the lower-level data within that column: how 
to interpret the numbers within it).
-Sufi explains to his student that Gnuastro has a convention for adding 
metadata within a plain-text file; and guides him to @ref{Gnuastro text table 
format}.
-Because we don't want metadata to be confused with the actual data, in a 
plain-text file, we start lines containing metadata with a `@code{#}'.
-For example, see the same data above, but this time with metadata for every 
column:
+The other sections don't have such shortcuts.
+To directly access them from the command-line, you need to tell Info to look 
into Gnuastro's manual, then look for the specific section (an unambiguous 
title is necessary).
+For example, if you only want to review/remember NoiseChisel's @ref{Detection 
options}), just run the following command.
+Note how case is irrelevant for Info when calling a title in this manner.
 
 @example
-# Column 1:  ID      [counter, u8] Identifier
-# Column 2:  X       [pix,    f32] Horizontal position
-# Column 3:  Y       [pix,    f32] Vertical position
-# Column 4:  PROFILE [name,    u8] Radial profile function
-# Column 5:  R       [pix,    f32] Effective radius
-# Column 6:  N       [n/a,    f32] Sersic index
-# Column 7:  PA      [deg,    f32] Position angle
-# Column 8:  Q       [n/a,    f32] Axis ratio
-# Column 9:  MAG     [log,    f32] Magnitude
-# Column 10: TRUNC   [n/a,    f32] Truncation (multiple of R)
-0  0.000   0.000  2  5   4.7  0.0  1.0  30.0  5.0
-1  250.0   250.0  1  40  1.0  -25  0.4  3.44  5.0
-2  50.00   50.00  4  0   0.0  0.0  0.0  6.00  0.0
-3  450.0   50.00  4  0   0.0  0.0  0.0  6.50  0.0
-4  50.00   450.0  4  0   0.0  0.0  0.0  7.00  0.0
-5  450.0   450.0  4  0   0.0  0.0  0.0  7.50  0.0
+$ info gnuastro "Detection options"
 @end example
 
-@noindent
-The numbers now make much more sense to for the student!
-Before continuing, Sufi reminded the student that even though metadata may not 
be strictly/technically necessary (for the computer programs), metadata are 
critical for human readers!
-Therefore, a good scientist should never forget to keep metadata with any data 
that they create, use or archive.
-
-To start simulating the nebula, Sufi creates a directory named 
@file{simulationtest} in his home directory.
-Note that the @command{pwd} command will print the ``parent working 
directory'' of the current directory (its a good way to confirm/check your 
current location in the full file system: it always starts from the root, or 
`@code{/}').
+In general, Info is a powerful and convenient way to access this whole book 
with detailed information about the programs you are running.
+If you are not already familiar with it, please run the following command and 
just read along and do what it says to learn it.
+Do not stop until you feel sufficiently fluent in it.
+Please invest the half an hour's time necessary to start using Info 
comfortably.
+It will greatly improve your productivity and you will start reaping the 
rewards of this investment very soon.
 
 @example
-$ mkdir ~/simulationtest
-$ cd ~/simulationtest
-$ pwd
-/home/rahman/simulationtest
+$ info info
 @end example
 
-@cindex Redirection
-@cindex Standard output
-It is possible to use a plain-text editor to manually put the catalog contents 
above into a plain-text file.
-But to easily automate catalog production (in later trials), Sufi decides to 
fill the input catalog with the redirection features of the command-line (or 
shell).
-Sufi's student wasn't familiar with this feature of the shell!
-So Sufi decided to do a fast demo; giving the following explanations while 
running the commands:
+As a good scientist you need to feel comfortable to play with the 
features/options and avoid (be critical to) using default values as much as 
possible.
+On the other hand, our human memory is limited, so it is important to be able 
to easily access any part of this book fast and remember the option names, what 
they do and their acceptable values.
 
-Shell redirection allows you to ``re-direct'' the ``standard output'' of a 
program (which is usually printed by the program on the command-line during its 
execution; like the output of @command{pwd} above) into a file.
-For example, let's simply ``echo'' (or print to standard output) the line 
``This is a test.'':
+If you just want the option names and a short description, calling the program 
with the @option{--help} option might also be a good solution like the first 
example below.
+If you know a few characters of the option name, you can feed the output to 
@command{grep} like the second or third example commands.
 
 @example
-$ echo "This is a test."
-This is a test.
+$ astnoisechisel --help
+$ astnoisechisel --help | grep quant
+$ astnoisechisel --help | grep check
 @end example
 
-@noindent
-As you see, our statement was simply ``echo''-ed to the standard output!
-To redirect this sentence into a file (instead of simply printing it on the 
standard output), we can simply use the @code{>} character, followed by the 
name of the file we want it to be dumped in.
+@node Setup and data download, Dataset inspection and cropping, Accessing 
documentation, General program usage tutorial
+@subsection Setup and data download
+
+The first step in the analysis of the tutorial is to download the necessary 
input datasets.
+First, to keep things clean, let's create a @file{gnuastro-tutorial} directory 
and continue all future steps in it:
 
 @example
-$ echo "This is a test." > test.txt
+$ mkdir gnuastro-tutorial
+$ cd gnuastro-tutorial
 @end example
 
-This time, the @command{echo} command didn't print anything in the terminal.
-Instead, the shell (command-line environment) took the output, and 
``re-directed'' it into a file called @file{test.txt}.
-Let's confirm this with the @command{ls} command (@command{ls} is short for 
``list'' and will list all the files in the current directory):
+We will be using the near infra-red @url{http://www.stsci.edu/hst/wfc3, Wide 
Field Camera} dataset.
+If you already have them in another directory (for example @file{XDFDIR}, with 
the same FITS file names), you can set the @file{download} directory to be a 
symbolic link to @file{XDFDIR} with a command like this:
 
 @example
-$ ls
-test.txt
+$ ln -s XDFDIR download
 @end example
 
 @noindent
-Now that you confirm the existence of @file{test.txt}, you can see its 
contents with the @command{cat} command (short for ``concatenation''; because 
it can also merge multiple files together):
+Otherwise, when the following images aren't already present on your system, 
you can make a @file{download} directory and download them there.
 
 @example
-$ cat test.txt
-This is a test.
+$ mkdir download
+$ cd download
+$ xdfurl=http://archive.stsci.edu/pub/hlsp/xdf
+$ wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_f105w_v1_sci.fits
+$ wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_f125w_v1_sci.fits
+$ wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_f160w_v1_sci.fits
+$ cd ..
 @end example
 
 @noindent
-Now that we have written our first line in @file{test.txt}, let's try adding a 
second line (don't forget that our final catalog of objects to simulate will 
have multiple lines):
+In this tutorial, we'll just use these three filters.
+Later, you may need to download more filters.
+To do that, you can use the shell's @code{for} loop to download them all in 
series (one after the other@footnote{Note that you only have one port to the 
internet, so downloading in parallel will actually be slower than downloading 
in series.}) with one command like the one below for the WFC3 filters.
+Put this command instead of the three @code{wget} commands above.
+Recall that all the extra spaces, back-slashes (@code{\}), and new lines can 
be ignored if you are typing on the lines on the terminal.
 
 @example
-$ echo "This is my second line." > test.txt
-$ cat test.txt
-This is my second line.
+$ for f in f105w f125w f140w f160w; do \
+    wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_"$f"_v1_sci.fits; \
+  done
 @end example
 
-As you see, the first line that you put in the file is no longer present!
-This happens because `@code{>}' always starts dumping content to a file from 
the start of the file.
-In effect, this means that any possibly pre-existing content is over-written 
by the new content!
-To append new lines (or dumping new content at the end of existing content), 
you can use `@code{>>}'.
-For example with the commands below, first we'll write the first sentence 
(using `@code{>}'), then use `@code{>>}' to add the second and third sentences.
-Finally, we'll print the contents of @file{test.txt} to confirm that all three 
lines are preserved.
 
-@example
-$ echo "My first sentence."   > test.txt
-$ echo "My second sentence." >> test.txt
-$ echo "My third sentence."  >> test.txt
-$ cat test.txt
-My first sentence.
-My second sentence.
-My third sentence.
-@end example
+@node Dataset inspection and cropping, Angular coverage on the sky, Setup and 
data download, General program usage tutorial
+@subsection Dataset inspection and cropping
 
-The student thanked Sufi for this explanation and now feels more comfortable 
with redirection.
-Therefore Sufi continues with the main project.
-But before that, he deletes the temporary test file:
+First, let's visually inspect the datasets we downloaded in @ref{Setup and 
data download}.
+Let's take F160W image as an example.
+One of the most common programs for viewing FITS images is SAO DS9, which is 
usually called through the @command{ds9} command-line program, like the command 
below.
+If you don't already have DS9 on your computer and the command below fails, 
please see @ref{SAO DS9}.
 
 @example
-$ rm test.txt
+$ ds9 download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f160w_v1_sci.fits
 @end example
 
-To put the catalog of profile data and their metadata (that was described 
above) into a file, Sufi uses the commands below.
-While Sufi was writing these commands, the student complained that ``I could 
have done in this in a text editor''.
-Sufi reminded the student that it is indeed possible; but it requires manual 
intervention.
-The advantage of a solution like below is that it can be automated (for 
example adding more rows; for more profiles in the final image).
+By default, DS9 open a relatively small window (for modern browsers) and its 
default scale and color bar make it very hard to see any structure in the 
image: everything will look black.
+Also, by default, it zooms into the center of the image and you need to scroll 
to zoom-out and see the whole thing.
+To avoid these problems, Gnuastro has the @command{astscript-fits-view} script:
 
 @example
-$ echo "# Column 1:  ID    [counter, u8] Identifier" > cat.txt
-$ echo "# Column 2:  X     [pix,    f32] Horizontal position" >> cat.txt
-$ echo "# Column 3:  Y     [pix,    f32] Vertical position" >> cat.txt
-$ echo "# Column 4:  PROF  [name,    u8] Radial profile function" \
-       >> cat.txt
-$ echo "# Column 5:  R     [pix,    f32] Effective radius" >> cat.txt
-$ echo "# Column 6:  N     [n/a,    f32] Sersic index" >> cat.txt
-$ echo "# Column 7:  PA    [deg,    f32] Position angle" >> cat.txt
-$ echo "# Column 8:  Q     [n/a,    f32] Axis ratio" >> cat.txt
-$ echo "# Column 9:  MAG   [log,    f32] Magnitude" >> cat.txt
-$ echo "# Column 10: TRUNC [n/a,    f32] Truncation (multiple of R)" \
-       >> cat.txt
-$ echo "0  0.000   0.000  2  5   4.7  0.0  1.0  30.0  5.0" >> cat.txt
-$ echo "1  250.0   250.0  1  40  1.0  -25  0.4  3.44  5.0" >> cat.txt
-$ echo "2  50.00   50.00  4  0   0.0  0.0  0.0  6.00  0.0" >> cat.txt
-$ echo "3  450.0   50.00  4  0   0.0  0.0  0.0  6.50  0.0" >> cat.txt
-$ echo "4  50.00   450.0  4  0   0.0  0.0  0.0  7.00  0.0" >> cat.txt
-$ echo "5  450.0   450.0  4  0   0.0  0.0  0.0  7.50  0.0" >> cat.txt
+$ astscript-fits-view \
+           download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f160w_v1_sci.fits
 @end example
 
-@noindent
-To make sure if the catalog's content is correct (and there was no typo for 
example!), Sufi runs `@command{cat cat.txt}', and confirms that it is correct.
+After running this command, you will see that the DS9 window fully covers the 
height of your monitor, it is showing the whole image, using a more clear 
color-map, and many more useful things.
+Infact, you see the DS9 command it used in your terminal@footnote{When 
comparing DS9's command-line options to Gnuastro's, you will notice how SAO DS9 
doesn't follow the GNU style of options where ``long'' and ``short'' options 
are preceded by @option{--} and @option{-} respectively (for example 
@option{--width} and @option{-w}, see @ref{Options}).}.
+On GNU/Linux operating systems (like Ubuntu, Fedora and etc), you can also set 
your graphics user interface to use this script for opening FITS files when you 
click on them.
+For more, see the instructions in the checklist at the start of @ref{Invoking 
astscript-fits-view}.
 
-@cindex Zero point
-Now that the catalog is created, Sufi is ready to call MakeProfiles to build 
the image containing these objects.
-He looks into his records and finds that the zero point magnitude for that 
night and that detector 18.
-The student was a little confused on the concept of zero point, so Sufi 
pointed him to @ref{Brightness flux magnitude}, which the student can study in 
detail later.
-Sufi therefore runs MakeProfiles with the command below:
+As you hover your mouse over the image, notice how the ``Value'' and 
positional fields on the top of the ds9 window get updated.
+The first thing you might notice is that when you hover the mouse over the 
regions with no data, they have a value of zero.
+The next thing might be that the dataset actually has two ``depth''s (see 
@ref{Quantifying measurement limits}).
+Recall that this is a combined/reduced image of many exposures, and the parts 
that have more exposures are deeper.
+In particular, the exposure time of the deep inner region is more than 4 times 
the exposure time of the outer (more shallower) parts.
+
+To simplify the analysis in this tutorial, we'll only be working on the deep 
field, so let's crop it out of the full dataset.
+Fortunately the XDF survey web page (above) contains the vertices of the deep 
flat WFC3-IR 
field@footnote{@url{https://archive.stsci.edu/prepds/xdf/#dataproducts}}.
+With Gnuastro's Crop program@footnote{To learn more about the crop program see 
@ref{Crop}.}, you can use those vertices to cutout this deep region from the 
larger image.
+But before that, to keep things organized, let's make a directory called 
@file{flat-ir} and keep the flat (single-depth) regions in that directory (with 
a `@file{xdf-}' suffix for a shorter and easier filename).
 
 @example
-$ astmkprof --prepforconv --mergedsize=499,499 --zeropoint=18.0 cat.txt
-MakeProfiles @value{VERSION} started on Sat Oct  6 16:26:56 953
-  - 6 profiles read from cat.txt
-  - Random number generator (RNG) type: ranlxs1
-  - Basic RNG seed: 1652884540
-  - Using 12 threads.
-  ---- row 3 complete, 5 left to go
-  ---- row 4 complete, 4 left to go
-  ---- row 6 complete, 3 left to go
-  ---- row 5 complete, 2 left to go
-  ---- ./0_cat_profiles.fits created.
-  ---- row 1 complete, 1 left to go
-  ---- row 2 complete, 0 left to go
-  - ./cat_profiles.fits created.                       0.092573 seconds
-  -- Output: ./cat_profiles.fits
-MakeProfiles finished in 0.293644 seconds
-@end example
+$ mkdir flat-ir
+$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f105w.fits \
+          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
+                     53.134517,-27.787144 : 53.161906,-27.807208" \
+          download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f105w_v1_sci.fits
 
-Sufi encourages the student to read through the printed output.
-As the statements say, two FITS files should have been created in the running 
directory.
-So Sufi ran the command below to confirm:
+$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f125w.fits \
+          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
+                     53.134517,-27.787144 : 53.161906,-27.807208" \
+          download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f125w_v1_sci.fits
 
-@example
-$ ls
-0_cat_profiles.fits  cat_profiles.fits  cat.txt
+$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f160w.fits \
+          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
+                     53.134517,-27.787144 : 53.161906,-27.807208" \
+          download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f160w_v1_sci.fits
 @end example
 
-@cindex Oversample
 @noindent
-The file @file{0_cat_profiles.fits} is the PSF Sufi had asked for, and 
@file{cat_profiles.fits} is the image containing the main objects in the 
catalog.
-Sufi opened the main image with the command below (using SAO DS9):
+Run the command below to have a look at the cropped images:
 
 @example
-$ astscript-fits-view cat_profiles.fits --ds9scale=95
+$ astscript-fits-view flat-ir/xdf-f160w.fits
 @end example
 
-The student could clearly see the main elliptical structure in the center.
-However, the size of @file{cat_profiles.fits} was surprising for the student, 
instead of 499 by 499 (as we had requested), it was 2615 by 2615 pixels (from 
the command below):
+You only see the deep region now, doesn't the noise look much cleaner?
+An important result of this crop is that regions with no data now have a NaN 
(Not-a-Number, or a blank value) value.
+Any self-respecting statistical program will ignore NaN values, so they will 
not affect your outputs.
+For example, notice how changing the DS9 color bar will not affect the NaN 
pixels (their colors won't change).
 
-@example
-$ astfits cat_profiles.fits
-Fits (GNU Astronomy Utilities) @value{VERSION}
-Run on Sat Oct  6 16:26:58 953
------
-HDU (extension) information: 'cat_profiles.fits'.
- Column 1: Index (counting from 0, usable with '--hdu').
- Column 2: Name ('EXTNAME' in FITS standard, usable with '--hdu').
- Column 3: Image data type or 'table' format (ASCII or binary).
- Column 4: Size of data in HDU.
------
-0      MKPROF-CONFIG   no-data         0
-1      Mock profiles   float32         2615x2615
-@end example
+However, do you remember that in the downloaded files, such regions had a 
value of zero?
+That is a big problem!
+Because zero is a number, and is thus meaningful, especially when you later 
want to NoiseChisel to detect@footnote{As you will see below, unlike most other 
detection algorithms, NoiseChisel detects the objects from their faintest 
parts, it doesn't start with their high signal-to-noise ratio peaks.
+Since the Sky is already subtracted in many images and noise fluctuates around 
zero, zero is commonly higher than the initial threshold applied.
+Therefore not ignoring zero-valued pixels in this image, will cause them to 
part of the detections!} all the signal from the deep universe in this image.
+Generally, when you want to ignore some pixels in a dataset, and avoid 
higher-level ambiguities or complications, it is always best to give them blank 
values (not zero, or some other absurdly large or small number).
+Gnuastro has the Arithmetic program for such cases, and we'll introduce it 
later in this tutorial.
 
-@noindent
-So Sufi explained why oversampling is important in modeling, especially for 
parts of the image where the flux change is significant over a pixel.
-Recall that when you oversample the model (for example by 5 times), for every 
desired pixel, you get 25 pixels (@mymath{5\times5}).
-Sufi then explained that after convolving (next step below) we will 
down-sample the image to get our originally desired size/resolution.
+In the example above, the polygon vertices are in degrees, but you can also 
replace them with sexagesimal coordinates (for example using 
@code{03h32m44.9794} or @code{03:32:44.9794} instead of @code{53.187414} 
instead of the first RA and @code{-27d46m44.9472} or @code{-27:46:44.9472} 
instead of the first Dec).
+To further simplify things, you can even define your polygon visually as a DS9 
``region'', save it as a ``region file'' and give that file to crop.
+But we need to continue, so if you are interested to learn more, see 
@ref{Crop}.
 
-After seeing the image, the student complained that only the large elliptical 
model for the Andromeda nebula can be seen in the center.
-He couldn't see the four stars that we had also requested in the catalog.
-So Sufi had to explain that the stars are there in the image, but the reason 
that they aren't visible when looking at the whole image at once, is that they 
only cover a single pixel!
-To prove it, he centered the image around the coordinates 2308 and 2308, where 
one of the stars is located in the over-sampled image [you can do this in 
@command{ds9} by selecting ``Pan'' in the ``Edit'' menu, then clicking around 
that position].
-Sufi then zoomed in to that region and soon, the star's non-zero pixel could 
be clearly seen.
+Before closing this section, let's just take a look at the three cropping 
commands we ran above.
+The only thing varying in the three commands the filter name!
+Note how everything else is the same!
+In such cases, you should generally avoid repeating a command manually, it is 
prone to @emph{many} bugs, and as you see, it is very hard to read (didn't you 
suddenly write a @code{7} as an @code{8}?).
 
-Sufi explained that the stars will take the shape of the PSF (cover an area of 
more than one pixel) after convolution.
-If we didn't have an atmosphere and we didn't need an aperture, then stars 
would only cover a single pixel with normal CCD resolutions.
-So Sufi convolved the image with this command:
+To simplify the command, and allow you to work on more filters, we can use the 
shell's @code{for} loop as shown below.
+Notice how the place where the filter names (@file{f105w}, @file{f125w} and 
@file{f160w}) are used above, have been replaced with @file{$f} (the shell 
variable that @code{for} will update in every loop) below.
 
 @example
-$ astconvolve --kernel=0_cat_profiles.fits cat_profiles.fits \
-              --output=cat_convolved.fits
-Convolve started on Sat Oct  6 16:35:32 953
-  - Using 8 CPU threads.
-  - Input: cat_profiles.fits (hdu: 1)
-  - Kernel: 0_cat_profiles.fits (hdu: 1)
-  - Input and Kernel images padded.                    0.075541 seconds
-  - Images converted to frequency domain.              6.728407 seconds
-  - Multiplied in the frequency domain.                0.040659 seconds
-  - Converted back to the spatial domain.              3.465344 seconds
-  - Padded parts removed.                              0.016767 seconds
-  - Output: cat_convolved.fits
-Convolve finished in:  10.422161 seconds
+$ rm flat-ir/*.fits
+$ for f in f105w f125w f160w; do \
+    astcrop --mode=wcs -h0 --output=flat-ir/xdf-$f.fits \
+            --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
+                       53.134517,-27.787144 : 53.161906,-27.807208" \
+            download/hlsp_xdf_hst_wfc3ir-60mas_hudf_"$f"_v1_sci.fits; \
+  done
 @end example
 
-@noindent
-When convolution finished, Sufi opened @file{cat_convolved.fits} and the four 
stars could be easily seen now:
+@node Angular coverage on the sky, Cosmological coverage and visualizing 
tables, Dataset inspection and cropping, General program usage tutorial
+@subsection Angular coverage on the sky
+
+@cindex @code{CDELT}
+@cindex Coordinate scales
+@cindex Scales, coordinate
+This is the deepest image we currently have of the sky.
+The first thing that comes to mind may be this: ``How large is this field on 
the sky?''.
+You can get a fast and crude answer with Gnuastro's Fits program using this 
command:
 
 @example
-$ astscript-fits-view cat_convolved.fits --ds9scale=95
+astfits flat-ir/xdf-f160w.fits --skycoverage
 @end example
 
-It was interesting for the student that all the flux in that single pixel is 
now distributed over so many pixels (the sum of all the pixels in each 
convolved star is actually equal to the value of the single pixel before 
convolution).
-Sufi explained how a PSF with a larger FWHM would make the points even wider 
than this (distributing their flux in a larger area).
-With the convolved image ready, they were prepared to re-sample it to the 
original pixel scale Sufi had planned [from the @command{$ astmkprof -P} 
command above, recall that MakeProfiles had over-sampled the image by 5 times].
-Sufi explained the basic concepts of warping the image to his student and ran 
Warp with the following command:
+It will print the sky coverage in two formats (all numbers are in units of 
degrees for this image): 1) the image's central RA and Dec and full width 
around that center, 2) the range of RA and Dec covered by this image.
+You can use these values in various online query systems.
+You can also use this option to automatically calculate the area covered by 
this image.
+With the @option{--quiet} option, the printed output of @option{--skycoverage} 
will not contain human-readable text, making it easier for further processing:
 
 @example
-$ astwarp --scale=1/5 --centeroncorner cat_convolved.fits
-Warp started on Sat Oct  6 16:51:59 953
- Using 8 CPU threads.
- Input: cat_convolved.fits (hdu: 1)
- matrix:
-        0.2000   0.0000   0.4000
-        0.0000   0.2000   0.4000
-        0.0000   0.0000   1.0000
-
-$ astfits cat_convolved_scaled.fits --quiet
-0      WARP-CONFIG     no-data         0
-1      Warped          float32         523x523
+astfits flat-ir/xdf-f160w.fits --skycoverage --quiet
 @end example
 
-@noindent
-@file{cat_convolved_scaled.fits} now has the correct pixel scale.
-However, the image is still larger than what we had wanted, it is 
@mymath{523\times523} pixels (not our desired @mymath{499\times499}).
-The student is slightly confused, so Sufi also re-samples the PSF with the 
same scale by running
+The second row is the coverage range along RA and Dec (compare with the 
outputs before using @option{--quiet}).
+We can thus simply subtract the second from the first column and multiply it 
with the difference of the fourth and third columns to calculate the image area.
+We'll also multiply each by 60 to have the area in arc-minutes squared.
 
 @example
-$ astwarp --scale=1/5 --centeroncorner 0_cat_profiles.fits
-$ astfits 0_cat_profiles_scaled.fits --quiet
-0      WARP-CONFIG     no-data         0
-1      Warped          float32         25x25
-@end example
-
-@noindent
-Sufi notes that @mymath{25=12+12+1} and that @mymath{523=499+12+12}.
-He goes on to explain that frequency space convolution will dim the edges and 
that is why he added the @option{--prepforconv} option to MakeProfiles above.
-Now that convolution is done, Sufi can remove those extra pixels using Crop 
with the command below.
-Crop's @option{--section} option accepts coordinates inclusively and counting 
from 1 (according to the FITS standard), so the crop region's first pixel has 
to be 13, not 12.
-
-@example
-$ astcrop cat_convolved_scaled.fits --section=13:*-12,13:*-12    \
-          --mode=img --zeroisnotblank
-Crop started on Sat Oct  6 17:03:24 953
-  - Read metadata of 1 image.                          0.001304 seconds
-  ---- ...nvolved_scaled_cropped.fits created: 1 input.
-Crop finished in:  0.027204 seconds
-@end example
-
-@noindent
-To fully convince the student, Sufi checks the size of the output of the crop 
command above:
-
-@example
-$ astfits cat_convolved_scaled_cropped.fits --quiet
-0      n/a             no-data         0
-1      n/a             float32         499x499
-@end example
-
-@noindent
-Finally, @file{cat_convolved_scaled_cropped.fits} is @mymath{499\times499} 
pixels and the mock Andromeda galaxy is centered on the central pixel.
-This is the same dimensions as Sufi had desired in the beginning.
-All this trouble was certainly worth it because now there is no dimming on the 
edges of the image and the profile centers are more accurately sampled.
-
-The final step to simulate a real observation would be to add noise to the 
image.
-Sufi set the zero point magnitude to the same value that he set when making 
the mock profiles and looking again at his observation log, he had measured the 
background flux near the nebula had a @emph{per-pixel} magnitude of 7 that 
night.
-For more on how the background value determines the noise, see @ref{Noise 
basics}.
-So using these values he ran MakeNoise, and with the second command, he 
visually inspected the image.
-
-@example
-$ astmknoise --zeropoint=18 --background=7 --output=out.fits    \
-             cat_convolved_scaled_cropped.fits
-MakeNoise @value{VERSION} started on Sat Oct  6 17:05:06 953
-  - Generator type: ranlxs1
-  - Generator seed: 1428318100
-MakeNoise finished in:  0.033491 (seconds)
-
-$ astscript-fits-view out.fits
-@end example
-
-@noindent
-The @file{out.fits} file now contains the noised image of the mock catalog 
Sufi had asked for.
-The student hadn't observed the nebula in the sky, so when he saw the mock 
image in SAO DS9 (with the second command above), he understood why Sufi was 
dubious: it was very diffuse!
-
-Seeing how the @option{--output} option allows the user to specify the name of 
the output file, the student was confused and wanted to know why Sufi hadn't 
used it more regularly before?
-Sufi explained that for intermediate steps, you can rely on the automatic 
output of the programs (see @ref{Automatic output}).
-Doing so will give all the intermediate files a similar basic name structure, 
so in the end you can simply remove them all with the Shell's capabilities, and 
it will be familiar for other users.
-So Sufi decided to show this to the student by making a shell script from the 
commands he had used before.
-
-The command-line shell has the capability to read all the separate input 
commands from a file.
-This is useful when you want to do the same thing multiple times, with only 
the names of the files or minor parameters changing between the different 
instances.
-Using the shell's history (by pressing the up keyboard key) Sufi reviewed all 
the commands and then he retrieved the last 5 commands with the @command{$ 
history 5} command.
-He selected all those lines he had input and put them in a text file named 
@file{mymock.sh}.
-Then he defined the @code{edge} and @code{base} shell variables for easier 
customization later.
-Finally, before every command, he added some comments (lines starting with 
@key{#}) for future readability.
-
-@example
-edge=12
-base=cat
-
-# Stop running next commands if one fails.
-set -e
-
-# Remove any (possibly) existing output (from previous runs)
-# before starting.
-rm -f out.fits
-
-# Run MakeProfiles to create an oversampled FITS image.
-astmkprof --prepforconv --mergedsize=499,499 --zeropoint=18.0 \
-          "$base".txt
-
-# Convolve the created image with the kernel.
-astconvolve "$base"_profiles.fits \
-            --kernel=0_"$base"_profiles.fits \
-            --output="$base"_convolved.fits
-
-# Scale the image back to the intended resolution.
-astwarp --scale=1/5 --centeroncorner "$base"_convolved.fits
-
-# Crop the edges out (dimmed during convolution). '--section'
-# accepts inclusive coordinates, so the start of the section
-# must be one pixel larger than its end.
-st_edge=$(( edge + 1 ))
-astcrop "$base"_convolved_scaled.fits --zeroisnotblank \
-        --mode=img --section=$st_edge:*-$edge,$st_edge:*-$edge
-
-# Add noise to the image.
-astmknoise --zeropoint=18 --background=7 --output=out.fits \
-           "$base"_convolved_scaled_cropped.fits
-
-# Remove all the temporary files.
-rm 0*.fits "$base"*.fits
-@end example
-
-@cindex Comments
-He used this chance to remind the student of the importance of comments in 
code or shell scripts!
-Just like metadata in a dataset, when writing the code, you have a good mental 
picture of what you are doing, so writing comments might seem superfluous and 
excessive.
-However, in one month when you want to re-use the script, you have lost that 
mental picture and remembering it can be time-consuming and frustrating.
-The importance of comments is further amplified when you want to share the 
script with a friend/colleague.
-So it is good to accompany any step of a script, or code, with useful comments 
while you are writing it (create a good mental picture of why you are doing 
something: don't just describe the command, but its purpose).
-
-@cindex Gedit
-@cindex GNU Emacs
-Sufi then explained to the eager student that you define a variable by giving 
it a name, followed by an @code{=} sign and the value you want.
-Then you can reference that variable from anywhere in the script by calling 
its name with a @code{$} prefix.
-So in the script whenever you see @code{$base}, the value we defined for it 
above is used.
-If you use advanced editors like GNU Emacs or even simpler ones like Gedit 
(part of the GNOME graphical user interface) the variables will become a 
different color which can really help in understanding the script.
-We have put all the @code{$base} variables in double quotation marks 
(@code{"}) so the variable name and the following text do not get mixed, the 
shell is going to ignore the @code{"} after replacing the variable value.
-To make the script executable, Sufi ran the following command:
-
-@example
-$ chmod +x mymock.sh
-@end example
-
-@noindent
-Then finally, Sufi ran the script, simply by calling its file name:
-
-@example
-$ ./mymock.sh
-@end example
-
-After the script finished, the only file remaining is the @file{out.fits} file 
that Sufi had wanted in the beginning.
-Sufi then explained to the student how he could run this script anywhere that 
he has a catalog if the script is in the same directory.
-The only thing the student had to modify in the script was the name of the 
catalog (the value of the @code{base} variable in the start of the script) and 
the value to the @code{edge} variable if he changed the PSF size.
-The student was also happy to hear that he won't need to make it executable 
again when he makes changes later, it will remain executable unless he 
explicitly changes the executable flag with @command{chmod}.
-
-The student was really excited, since now, through simple shell scripting, he 
could really speed up his work and run any command in any fashion he likes 
allowing him to be much more creative in his works.
-Until now he was using the graphical user interface which doesn't have such a 
facility and doing repetitive things on it was really frustrating and some 
times he would make mistakes.
-So he left to go and try scripting on his own computer.
-He later reminded Sufi that the second tutorial in the Gnuastro book as more 
complex commands in data analysis, and a more advanced introduction to 
scripting (see @ref{General program usage tutorial}).
-
-Sufi could now get back to his own work and see if the simulated nebula which 
resembled the one in the Andromeda constellation could be detected or not.
-Although it was extremely faint@footnote{The brightness of a diffuse object is 
added over all its pixels to give its final magnitude, see @ref{Brightness flux 
magnitude}.
-So although the magnitude 3.44 (of the mock nebula) is orders of magnitude 
brighter than 6 (of the stars), the central galaxy is much fainter.
-Put another way, the brightness is distributed over a large area in the case 
of a nebula.}.
-Therefore, Sufi ran Gnuastro's detection software (@ref{NoiseChisel}) to see 
if this object is detectable or not.
-NoiseChisel's output (@file{out_detected.fits}) is a multi-extension FITS 
file, so he used Gnuastro's @command{astscript-fits-view} program in the second 
command to see the output:
-
-@example
-$ astnoisechisel out.fits
-
-$ astscript-fits-view out_detected.fits
-@end example
-
-In the ``Cube'' window (that was opened with DS9), if Sufi clicked on the 
``Next'' button to see the pixels that were detected to contain significant 
signal.
-Fortunately the nebula's shape was detectable and he could finally confirm 
that the nebula he kept in his notebook was actually observable.
-He wrote this result in the draft manuscript that would later become ``Book of 
fixed stars''@footnote{@url{https://en.wikipedia.org/wiki/Book_of_Fixed_Stars}}.
-
-He still had to check the other nebula he saw from Yemen and several other 
such objects, but they could wait until tomorrow (thanks to the shell script, 
he only has to define a new catalog).
-It was nearly sunset and they had to begin preparing for the night's 
measurements on the ecliptic.
-
-
-@menu
-* General program usage tutorial::
-@end menu
-
-@node General program usage tutorial, Detecting large extended targets, Sufi 
simulates a detection, Tutorials
-@section General program usage tutorial
-
-@cindex Hubble Space Telescope (HST)
-@cindex Colors, broad-band photometry
-Measuring colors of astronomical objects in broad-band or narrow-band images 
is one of the most basic and common steps in astronomical analysis.
-Here, we will use Gnuastro's programs to get a physical scale (area at certain 
redshifts) of the field we are studying, detect objects in a Hubble Space 
Telescope (HST) image, measure their colors and identify the ones with the 
strongest colors, do a visual inspection of these objects and inspect spatial 
position in the image.
-After this tutorial, you can also try the @ref{Detecting large extended 
targets} tutorial which goes into a little more detail on detecting very low 
surface brightness signal.
-
-During the tutorial, we will take many detours to explain, and practically 
demonstrate, the many capabilities of Gnuastro's programs.
-In the end you will see that the things you learned during this tutorial are 
much more generic than this particular problem and can be used in solving a 
wide variety of problems involving the analysis of data (images or tables).
-So please don't rush, and go through the steps patiently to optimally master 
Gnuastro.
-
-@cindex XDF survey
-@cindex eXtreme Deep Field (XDF) survey
-In this tutorial, we'll use the HST@url{https://archive.stsci.edu/prepds/xdf, 
eXtreme Deep Field} dataset.
-Like almost all astronomical surveys, this dataset is free for download and 
usable by the public.
-You will need the following tools in this tutorial: Gnuastro, SAO DS9 
@footnote{See @ref{SAO DS9}, available at 
@url{http://ds9.si.edu/site/Home.html}}, GNU 
Wget@footnote{@url{https://www.gnu.org/software/wget}}, and AWK (most common 
implementation is GNU AWK@footnote{@url{https://www.gnu.org/software/gawk}}).
-
-This tutorial was first prepared for the ``Exploring the Ultra-Low Surface 
Brightness Universe'' workshop (November 2017) at the ISSI in Bern, Switzerland.
-It was further extended in the ``4th Indo-French Astronomy School'' (July 
2018) organized by LIO, CRAL CNRS UMR5574, UCBL, and IUCAA in Lyon, France.
-We are very grateful to the organizers of these workshops and the attendees 
for the very fruitful discussions and suggestions that made this tutorial 
possible.
-
-@cartouche
-@noindent
-@strong{Write the example commands manually:} Try to type the example commands 
on your terminal manually and use the history feature of your command-line (by 
pressing the ``up'' button to retrieve previous commands).
-Don't simply copy and paste the commands shown here.
-This will help simulate future situations when you are processing your own 
datasets.
-@end cartouche
-
-
-@menu
-* Calling Gnuastro's programs::  Easy way to find Gnuastro's programs.
-* Accessing documentation::     Access to manual of programs you are running.
-* Setup and data download::     Setup this template and download datasets.
-* Dataset inspection and cropping::  Crop the flat region to use in next steps.
-* Angular coverage on the sky::  Measure the field size on the sky.
-* Cosmological coverage::       Measure the field size at different redshifts.
-* Building custom programs with the library::  Easy way to build new programs.
-* Option management and configuration files::  Dealing with options and 
configuring them.
-* Warping to a new pixel grid::  Transforming/warping the dataset.
-* NoiseChisel and Multiextension FITS files::  Running NoiseChisel and having 
multiple HDUs.
-* NoiseChisel optimization for detection::  Check NoiseChisel's operation and 
improve it.
-* NoiseChisel optimization for storage::  Dramatically decrease output's 
volume.
-* Segmentation and making a catalog::  Finding true peaks and creating a 
catalog.
-* Measuring the dataset limits::  One way to measure the ``depth'' of your 
data.
-* Working with catalogs estimating colors::  Estimating colors using the 
catalogs.
-* Column statistics color-magnitude diagram::  Visualizing column correlations.
-* Aperture photometry::         Doing photometry on a fixed aperture.
-* Matching catalogs::           Easily find corresponding rows from two 
catalogs.
-* Finding reddest clumps and visual inspection::  Selecting some targets and 
inspecting them.
-* Writing scripts to automate the steps::  Scripts will greatly help in 
re-doing things fast.
-* Citing and acknowledging Gnuastro::  How to cite and acknowledge Gnuastro in 
your papers.
-@end menu
-
-@node Calling Gnuastro's programs, Accessing documentation, General program 
usage tutorial, General program usage tutorial
-@subsection Calling Gnuastro's programs
-A handy feature of Gnuastro is that all program names start with @code{ast}.
-This will allow your command-line processor to easily list and auto-complete 
Gnuastro's programs for you.
-Try typing the following command (press @key{TAB} key when you see 
@code{<TAB>}) to see the list:
-
-@example
-$ ast<TAB><TAB>
-@end example
-
-@noindent
-Any program that starts with @code{ast} (including all Gnuastro programs) will 
be shown.
-By choosing the subsequent characters of your desired program and pressing 
@key{<TAB><TAB>} again, the list will narrow down and the program name will 
auto-complete once your input characters are unambiguous.
-In short, you often don't need to type the full name of the program you want 
to run.
-
-@node Accessing documentation, Setup and data download, Calling Gnuastro's 
programs, General program usage tutorial
-@subsection Accessing documentation
-
-Gnuastro contains a large number of programs and it is natural to forget the 
details of each program's options or inputs and outputs.
-Therefore, before starting the analysis steps of this tutorial, let's review 
how you can access this book to refresh your memory any time you want, without 
having to take your hands off the keyboard.
-
-When you install Gnuastro, this book is also installed on your system along 
with all the programs and libraries, so you don't need an internet connection 
to access/read it.
-Also, by accessing this book as described below, you can be sure that it 
corresponds to your installed version of Gnuastro.
-
-@cindex GNU Info
-GNU Info@footnote{GNU Info is already available on almost all Unix-like 
operating systems.} is the program in charge of displaying the manual on the 
command-line (for more, see @ref{Info}).
-To see this whole book on your command-line, please run the following command 
and press subsequent keys.
-Info has its own mini-environment, therefore we'll show the keys that must be 
pressed in the mini-environment after a @code{->} sign.
-You can also ignore anything after the @code{#} sign in the middle of the 
line, they are only for your information.
-
-@example
-$ info gnuastro                # Open the top of the manual.
--> <SPACE>                     # All the book chapters.
--> <SPACE>                     # Continue down: show sections.
--> <SPACE> ...                 # Keep pressing space to go down.
--> q                           # Quit Info, return to the command-line.
-@end example
-
-The thing that greatly simplifies navigation in Info is the links (regions 
with an underline).
-You can immediately go to the next link in the page with the @key{<TAB>} key 
and press @key{<ENTER>} on it to go into that part of the manual.
-Try the commands above again, but this time also use @key{<TAB>} to go to the 
links and press @key{<ENTER>} on them to go to the respective section of the 
book.
-Then follow a few more links and go deeper into the book.
-To return to the previous page, press @key{l} (small L).
-If you are searching for a specific phrase in the whole book (for example an 
option name), press @key{s} and type your search phrase and end it with an 
@key{<ENTER>}.
-
-You don't need to start from the top of the manual every time.
-For example, to get to @ref{Invoking astnoisechisel}, run the following 
command.
-In general, all programs have such an ``Invoking ProgramName'' section in this 
book.
-These sections are specifically for the description of inputs, outputs and 
configuration options of each program.
-You can access them directly for each program by giving its executable name to 
Info.
-
-@example
-$ info astnoisechisel
-@end example
-
-The other sections don't have such shortcuts.
-To directly access them from the command-line, you need to tell Info to look 
into Gnuastro's manual, then look for the specific section (an unambiguous 
title is necessary).
-For example, if you only want to review/remember NoiseChisel's @ref{Detection 
options}), just run the following command.
-Note how case is irrelevant for Info when calling a title in this manner.
-
-@example
-$ info gnuastro "Detection options"
-@end example
-
-In general, Info is a powerful and convenient way to access this whole book 
with detailed information about the programs you are running.
-If you are not already familiar with it, please run the following command and 
just read along and do what it says to learn it.
-Do not stop until you feel sufficiently fluent in it.
-Please invest the half an hour's time necessary to start using Info 
comfortably.
-It will greatly improve your productivity and you will start reaping the 
rewards of this investment very soon.
-
-@example
-$ info info
-@end example
-
-As a good scientist you need to feel comfortable to play with the 
features/options and avoid (be critical to) using default values as much as 
possible.
-On the other hand, our human memory is limited, so it is important to be able 
to easily access any part of this book fast and remember the option names, what 
they do and their acceptable values.
-
-If you just want the option names and a short description, calling the program 
with the @option{--help} option might also be a good solution like the first 
example below.
-If you know a few characters of the option name, you can feed the output to 
@command{grep} like the second or third example commands.
-
-@example
-$ astnoisechisel --help
-$ astnoisechisel --help | grep quant
-$ astnoisechisel --help | grep check
-@end example
-
-@node Setup and data download, Dataset inspection and cropping, Accessing 
documentation, General program usage tutorial
-@subsection Setup and data download
-
-The first step in the analysis of the tutorial is to download the necessary 
input datasets.
-First, to keep things clean, let's create a @file{gnuastro-tutorial} directory 
and continue all future steps in it:
-
-@example
-$ mkdir gnuastro-tutorial
-$ cd gnuastro-tutorial
-@end example
-
-We will be using the near infra-red @url{http://www.stsci.edu/hst/wfc3, Wide 
Field Camera} dataset.
-If you already have them in another directory (for example @file{XDFDIR}, with 
the same FITS file names), you can set the @file{download} directory to be a 
symbolic link to @file{XDFDIR} with a command like this:
-
-@example
-$ ln -s XDFDIR download
-@end example
-
-@noindent
-Otherwise, when the following images aren't already present on your system, 
you can make a @file{download} directory and download them there.
-
-@example
-$ mkdir download
-$ cd download
-$ xdfurl=http://archive.stsci.edu/pub/hlsp/xdf
-$ wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_f105w_v1_sci.fits
-$ wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_f125w_v1_sci.fits
-$ wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_f160w_v1_sci.fits
-$ cd ..
-@end example
-
-@noindent
-In this tutorial, we'll just use these three filters.
-Later, you may need to download more filters.
-To do that, you can use the shell's @code{for} loop to download them all in 
series (one after the other@footnote{Note that you only have one port to the 
internet, so downloading in parallel will actually be slower than downloading 
in series.}) with one command like the one below for the WFC3 filters.
-Put this command instead of the three @code{wget} commands above.
-Recall that all the extra spaces, back-slashes (@code{\}), and new lines can 
be ignored if you are typing on the lines on the terminal.
-
-@example
-$ for f in f105w f125w f140w f160w; do \
-    wget $xdfurl/hlsp_xdf_hst_wfc3ir-60mas_hudf_"$f"_v1_sci.fits; \
-  done
-@end example
-
-
-@node Dataset inspection and cropping, Angular coverage on the sky, Setup and 
data download, General program usage tutorial
-@subsection Dataset inspection and cropping
-
-First, let's visually inspect the datasets we downloaded in @ref{Setup and 
data download}.
-Let's take F160W image as an example.
-Do the steps below with the other image(s) too (and later with any dataset 
that you want to work on).
-It is very important to get a good visual feeling of the dataset you intend to 
use.
-Also, note how SAO DS9 (used here for visual inspection of FITS images) 
doesn't follow the GNU style of options where ``long'' and ``short'' options 
are preceded by @option{--} and @option{-} respectively (for example 
@option{--width} and @option{-w}, see @ref{Options}).
-
-Run the command below to see the F160W image with DS9.
-Ds9's @option{-zscale} scaling is good to visually highlight the low surface 
brightness regions, and as the name suggests, @option{-zoom to fit} will fit 
the whole dataset in the window.
-If the window is too small, expand it with your mouse, then press the ``zoom'' 
button on the top row of buttons above the image.
-Afterwards, in the bottom row of buttons, press ``zoom fit''.
-You can also zoom in and out by scrolling your mouse or the respective 
operation on your touch-pad when your cursor/pointer is over the image.
-
-@example
-$ ds9 download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f160w_v1_sci.fits     \
-      -zscale -zoom to fit
-@end example
-
-As you hover your mouse over the image, notice how the ``Value'' and 
positional fields on the top of the ds9 window get updated.
-The first thing you might notice is that when you hover the mouse over the 
regions with no data, they have a value of zero.
-The next thing might be that the dataset actually has two ``depth''s (see 
@ref{Quantifying measurement limits}).
-Recall that this is a combined/reduced image of many exposures, and the parts 
that have more exposures are deeper.
-In particular, the exposure time of the deep inner region is larger than 4 
times of the outer (more shallower) parts.
-
-To simplify the analysis in this tutorial, we'll only be working on the deep 
field, so let's crop it out of the full dataset.
-Fortunately the XDF survey web page (above) contains the vertices of the deep 
flat WFC3-IR field.
-With Gnuastro's Crop program@footnote{To learn more about the crop program see 
@ref{Crop}.}, you can use those vertices to cutout this deep region from the 
larger image.
-But before that, to keep things organized, let's make a directory called 
@file{flat-ir} and keep the flat (single-depth) regions in that directory (with 
a `@file{xdf-}' suffix for a shorter and easier filename).
-
-@example
-$ mkdir flat-ir
-$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f105w.fits \
-          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
-                     53.134517,-27.787144 : 53.161906,-27.807208" \
-          download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f105w_v1_sci.fits
-
-$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f125w.fits \
-          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
-                     53.134517,-27.787144 : 53.161906,-27.807208" \
-          download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f125w_v1_sci.fits
-
-$ astcrop --mode=wcs -h0 --output=flat-ir/xdf-f160w.fits \
-          --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
-                     53.134517,-27.787144 : 53.161906,-27.807208" \
-          download/hlsp_xdf_hst_wfc3ir-60mas_hudf_f160w_v1_sci.fits
-@end example
-
-The only thing varying in the three calls to Gnuastro's Crop program is the 
filter name!
-Note how everything else is the same.
-In such cases, you should generally avoid repeating a command manually, it is 
prone to many bugs, and as you see, it is very hard to read (didn't you 
suddenly write a @code{7} as an @code{8}?).
-To simplify the command, and later allow work on more filters, we can use the 
shell's @code{for} loop as shown below.
-Notice how the place where the filter names (@file{f105w}, @file{f125w} and 
@file{f160w}) are used above, have been replaced with @file{$f} (the shell 
variable that @code{for} will update in every loop) below.
-
-@example
-$ rm flat-ir/*.fits
-$ for f in f105w f125w f160w; do \
-    astcrop --mode=wcs -h0 --output=flat-ir/xdf-$f.fits \
-            --polygon="53.187414,-27.779152 : 53.159507,-27.759633 : \
-                       53.134517,-27.787144 : 53.161906,-27.807208" \
-            download/hlsp_xdf_hst_wfc3ir-60mas_hudf_"$f"_v1_sci.fits; \
-  done
-@end example
-
-Please open these images and inspect them with the same @command{ds9} command 
you used above.
-You will see how it is nicely flat now and doesn't have varying depths.
-In the example above, the polygon vertices are in degrees, but you can also 
replace them with sexagesimal coordinates (for example using 
@code{03h32m44.9794} or @code{03:32:44.9794} instead of @code{53.187414} 
instead of the first RA and @code{-27d46m44.9472} or @code{-27:46:44.9472} 
instead of the first Dec).
-To further simplify things, you can even define your polygon visually as a DS9 
``region'', save it as a ``region file'' and give that file to crop.
-But we need to continue, so if you are interested to learn more, see 
@ref{Crop}.
-
-Another important result of this crop is that regions with no data now have a 
NaN (Not-a-Number, or a blank value) value.
-In the downloaded files, such regions had a value of zero.
-However, zero is a number, and is thus meaningful, especially when you later 
want to NoiseChisel@footnote{As you will see below, unlike most other detection 
algorithms, NoiseChisel detects the objects from their faintest parts, it 
doesn't start with their high signal-to-noise ratio peaks.
-Since the Sky is already subtracted in many images and noise fluctuates around 
zero, zero is commonly higher than the initial threshold applied.
-Therefore not ignoring zero-valued pixels in this image, will cause them to 
part of the detections!}.
-Generally, when you want to ignore some pixels in a dataset, and avoid 
higher-level ambiguities or complications, it is always best to give them blank 
values (not zero, or some other absurdly large or small number).
-Gnuastro has the Arithmetic program for such cases, and we'll introduce it 
later in this tutorial.
-
-@node Angular coverage on the sky, Cosmological coverage, Dataset inspection 
and cropping, General program usage tutorial
-@subsection Angular coverage on the sky
-
-@cindex @code{CDELT}
-@cindex Coordinate scales
-@cindex Scales, coordinate
-This is the deepest image we currently have of the sky.
-The first thing that comes to mind may be this: ``How large is this field on 
the sky?''.
-You can get a fast and crude answer with Gnuastro's Fits program using this 
command:
-
-@example
-astfits flat-ir/xdf-f160w.fits --skycoverage
-@end example
-
-It will print the sky coverage in two formats (all numbers are in units of 
degrees for this image): 1) the image's central RA and Dec and full width 
around that center, 2) the range of RA and Dec covered by this image.
-You can use these values in various online query systems.
-You can also use this option to automatically calculate the area covered by 
this image.
-With the @option{--quiet} option, the printed output of @option{--skycoverage} 
will not contain human-readable text, making it easier for further processing:
-
-@example
-astfits flat-ir/xdf-f160w.fits --skycoverage --quiet
-@end example
-
-The second row is the coverage range along RA and Dec (compare with the 
outputs before using @option{--quiet}).
-We can thus simply subtract the second from the first column and multiply it 
with the difference of the fourth and third columns to calculate the image area.
-We'll also multiply each by 60 to have the area in arc-minutes squared.
-
-@iftex
-@cartouche
-@noindent
-@strong{Single quotes in PDF format:} in the PDF version of this manual, a 
single quote (or apostrophe) character in the commands or codes is shown like 
this: @code{'}.
-Single quotes are sometimes necessary in combination with commands like 
@code{awk} or @code{sed} (like the command below), or when using Column 
arithmetic in Gnuastro's own Table (see @ref{Column arithmetic}).
-Therefore when typing (recommended) or copy-pasting (not recommended) the 
commands that have a @code{'}, please correct it to the single-quote (or 
apostrophe) character, otherwise the command will fail.
-@end cartouche
-@end iftex
-
-@example
-astfits flat-ir/xdf-f160w.fits --skycoverage --quiet \
-        | awk 'NR==2@{print ($2-$1)*60*($4-$3)*60@}'
+astfits flat-ir/xdf-f160w.fits --skycoverage --quiet \
+        | awk 'NR==2@{print ($2-$1)*60*($4-$3)*60@}'
 @end example
 
 The returned value is @mymath{9.06711} arcmin@mymath{^2}.
@@ -2670,32 +2139,33 @@ However, all Gnuastro programs (which use the default 
FITS keyword writing forma
 If your dataset doesn't use the @code{CDELT} convention, you can feed it to 
any (simple) Gnuastro program (for example Arithmetic) and the output will have 
the @code{CDELT} keyword.
 See Section 8 of the 
@url{https://fits.gsfc.nasa.gov/standard40/fits_standard40aa-le.pdf, FITS 
standard} for more}.
 
-With the commands below, we'll use @code{CDELT} (along with the image size) to 
find the answer of our initial question: ``how much of the sky does this image 
cover?''.
+With the commands below, we'll use @code{CDELT} (along with the number of 
non-blank pixels) to find the answer of our initial question: ``how much of the 
sky does this image cover?''.
 The lines starting with @code{##} are just comments for you to read and 
understand each command.
-Don't type them on the terminal.
-The commands are intentionally repetitive in some places to better understand 
each step and also to demonstrate the beauty of command-line features like 
history, variables, pipes and loops (which you will commonly use as you master 
the command-line).
+Don't type them on the terminal (no problem if you do, they will just not have 
any effect).
+The commands are intentionally repetitive in some places to better understand 
each step and also to demonstrate the beauty of command-line features like 
history, variables, pipes and loops (which you will commonly use as you become 
more proficient on the command-line).
 
 @cartouche
 @noindent
-@strong{Use shell history:} Don't forget to make effective use of your shell's 
history: you don't have to re-type previous command to add something to them.
+@strong{Use shell history:} Don't forget to make effective use of your shell's 
history: you don't have to re-type previous command to add something to them 
(like the examples below).
 This is especially convenient when you just want to make a small change to 
your previous command.
 Press the ``up'' key on your keyboard (possibly multiple times) to see your 
previous command(s) and modify them accordingly.
 @end cartouche
 
 @cartouche
-@strong{Your locale doesn't use `.' as decimal separator:} on systems that 
don't use an English language environment, dates, numbers and etc can be 
printed in different formats (for example `0.5' can be written as `0,5').
-With the @code{LC_NUMERIC} line at the start of the script below, we are 
ensuring a unified format in the outupt of @command{seq}.
+@noindent
+@strong{Your locale doesn't use `.' as decimal separator:} on systems that 
don't use an English language environment, dates, numbers and etc can be 
printed in different formats (for example `0.5' can be written as `0,5': with a 
comma).
+With the @code{LC_NUMERIC} line at the start of the script below, we are 
ensuring a unified format in the output of @command{seq}.
 For more, please see @ref{Numeric locale}.
 @end cartouche
 
 @example
-## If your system language uses ',' (not '.') as decimal separator.
+## Make sure that the decimal separator is a point in any environment.
 $ export LC_NUMERIC=C
 
 ## See the general statistics of non-blank pixel values.
 $ aststatistics flat-ir/xdf-f160w.fits
 
-## We only want the number of non-blank pixels.
+## We only want the number of non-blank pixels (add '--number').
 $ aststatistics flat-ir/xdf-f160w.fits --number
 
 ## Keep the result of the command above in the shell variable `n'.
@@ -2713,17 +2183,14 @@ $ astfits flat-ir/xdf-f160w.fits -h1
 $ astfits flat-ir/xdf-f160w.fits -h1 | grep CDELT
 
 ## Since the resolution of both dimensions is (approximately) equal,
-## we'll only use one of them (CDELT1).
-$ astfits flat-ir/xdf-f160w.fits -h1 | grep CDELT1
+## we'll only read the value of one (CDELT1) with '--keyvalue'.
+$ astfits flat-ir/xdf-f160w.fits -h1 --keyvalue=CDELT1
 
-## To extract the value (third token in the line above), we'll
-## feed the output to AWK. Note that the first two tokens are
-## `CDELT1' and `='.
-$ astfits flat-ir/xdf-f160w.fits -h1 | grep CDELT1 | awk '@{print $3@}'
+## We don't need the file name in the output (add '--quiet').
+$ astfits flat-ir/xdf-f160w.fits -h1 --keyvalue=CDELT1 --quiet
 
 ## Save it as the shell variable `r'.
-$ r=$(astfits flat-ir/xdf-f160w.fits -h1 | grep CDELT1   \
-              | awk '@{print $3@}')
+$ r=$(astfits flat-ir/xdf-f160w.fits -h1 --keyvalue=CDELT1 --quiet)
 
 ## Print the values of `n' and `r'.
 $ echo $n $r
@@ -2735,11 +2202,11 @@ $ echo $n $r | awk '@{print $1 * ($2*60)^2@}'
 @end example
 
 The output of the last command (area of this field) is 4.03817 (or 
approximately 4.04) arc-minutes squared.
-Just for comparison, this is roughly 175 times smaller than the average moon's 
angular area (with a diameter of 30arc-minutes or half a degree).
+Just for comparison, this is roughly 175 times smaller than the average moon's 
angular area (with a diameter of 30 arc-minutes or half a degree).
 
 Some FITS writers don't use the @code{CDELT} convention, making it hard to use 
the steps above.
 In such cases, you can extract the pixel scale with the @option{--pixelscale} 
option of Gnuastro's Fits program like the command below.
-Like the @option{--skycoverage} option above, you can also use the 
@option{--quiet} option to allow easy usage of the values in scripts.
+Similar to the @option{--skycoverage} option above, you can also use the 
@option{--quiet} option to allow easy usage of the values in scripts.
 
 @example
 $ astfits flat-ir/xdf-f160w.fits --pixelscale
@@ -2755,8 +2222,8 @@ Just like this manual, you can also access GNU AWK's 
manual on the command-line
 Just run @code{info awk}.
 @end cartouche
 
-@node Cosmological coverage, Building custom programs with the library, 
Angular coverage on the sky, General program usage tutorial
-@subsection Cosmological coverage
+@node Cosmological coverage and visualizing tables, Building custom programs 
with the library, Angular coverage on the sky, General program usage tutorial
+@subsection Cosmological coverage and visualizing tables
 Having found the angular coverage of the dataset in @ref{Angular coverage on 
the sky}, we can now use Gnuastro to answer a more physically motivated 
question: ``How large is this area at different redshifts?''.
 To get a feeling of the tangential area that this field covers at redshift 2, 
you can use Gnuastro's CosmicCalcular program (@ref{CosmicCalculator}).
 In particular, you need the tangential distance covered by 1 arc-second as raw 
output.
@@ -2781,17 +2248,20 @@ $ astcosmiccal -z2 --help
 ## in units of kpc/arc-seconds.
 $ astcosmiccal -z2 --arcsectandist
 
-## But its easier to use the short version of this option (which
-## can be appended to other short options.
+## Its easier to use the short (single character) version of
+## this option when typing (but this is hard to read, so use
+## the long version in scripts or notes you plan to archive).
+$ astcosmiccal -z2 -s
+
+## Short options can be merged (they are only a single character!)
 $ astcosmiccal -sz2
 
 ## Convert this distance to kpc^2/arcmin^2 and save in `k'.
 $ k=$(astcosmiccal -sz2 | awk '@{print ($1*60)^2@}')
 
-## Re-calculate the area of the dataset in arcmin^2.
+## Calculate the area of the dataset in arcmin^2.
 $ n=$(aststatistics flat-ir/xdf-f160w.fits --number)
-$ r=$(astfits flat-ir/xdf-f160w.fits -h1 | grep CDELT1   \
-              | awk '@{print $3@}')
+$ r=$(astfits flat-ir/xdf-f160w.fits -h1 --keyvalue=CDELT1 -q)
 $ a=$(echo $n $r | awk '@{print $1 * ($2*60)^2 @}')
 
 ## Multiply `k' and `a' and divide by 10^6 for value in Mpc^2.
@@ -2810,8 +2280,8 @@ $ for z in 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0; do    
    \
 @end example
 
 @noindent
-Fortunately, the shell has a useful tool/program to print a sequence of 
numbers that is nicely called @code{seq}.
-You can use it instead of typing all the different redshifts in this example.
+Fortunately, the shell has a useful tool/program to print a sequence of 
numbers that is nicely called @code{seq} (short for ``sequence'').
+You can use it instead of typing all the different redshifts in the loop above.
 For example the loop below will calculate and print the tangential coverage of 
this field across a larger range of redshifts (0.1 to 5) and with finer 
increments of 0.1.
 For more on the @code{LC_NUMERIC} command, see @ref{Numeric locale}.
 
@@ -2826,15 +2296,59 @@ $ for z in $(seq 0.1 0.1 5); do                         
         \
   done
 @end example
 
+Have a look at the two printed columns.
+The first is the redshift, and the second is the area of this image at that 
redshift (in Mega Parsecs squared).
+@url{https://en.wikipedia.org/wiki/Redshift, Redshift} (@mymath{z}) is a 
measure of distance in galaxy evolution and cosmology: a higher redshift 
corresponds to larger distance.
 
-@node Building custom programs with the library, Option management and 
configuration files, Cosmological coverage, General program usage tutorial
-@subsection Building custom programs with the library
-In @ref{Cosmological coverage}, we repeated a certain calculation/output of a 
program multiple times using the shell's @code{for} loop.
-This simple way repeating a calculation is great when it is only necessary 
once.
-However, if you commonly need this calculation and possibly for a larger 
number of redshifts at higher precision, the command above can be slow (try it 
out to see).
+@cindex Turn over point (angular diameter distance)
+Now, have a look at the first few values.
+At @mymath{z=0.1} and @mymath{z=0.5}, this image covers @mymath{0.05 Mpc^2} 
and @mymath{0.57 Mpc^2} respectively.
+This increase of coverage with redshift is expected because a fixed angle will 
cover a larger tangential area at larger distances.
+However, as you come down the list (to higher redshifts) you will notice that 
this relation doesn't hold!
+The largest coverage is at @mymath{z=1.6}: at higher redshifts, the area 
decreases, and continues decreasing!!!
+In @mymath{\Lambda{}CDM} cosmology, this happens because of the finite speed 
of light and the expansion of the universe, see 
@url{https://en.wikipedia.org/wiki/Angular_diameter_distance#Angular_diameter_turnover_point,
 the Wikipedia page}.
+
+In case you have TOPCAT, you can visualize this as a plot (if you don't have 
TOPCAT, see @ref{TOPCAT}).
+To do so, first you need to save the output of the loop above into a FITS 
table by piping the output to Gnuastro's Table program and giving an output 
name:
+
+@example
+$ for z in $(seq 0.1 0.1 5); do                                  \
+    k=$(astcosmiccal -z$z --arcsectandist);                      \
+    echo $z $k $a | awk '@{print $1, ($2*60)^2 * $3 / 1e6@}';   \
+  done | asttable --output=z-vs-tandist.fits
+@end example
+
+You can now use Gnuastro's @command{astscript-fits-view} to open this table in 
TOPCAT with the command below.
+Do you remember this script from @ref{Dataset inspection and cropping}?
+There, we used it to view a FITS image with DS9!
+This script will see if the first dataset in the image is a table or an image 
and will call TOPCAT or DS9 accordingly: making it a very convenient tool to 
inspect the contents of all types of FITS data.
 
-This slowness of the repeated calls to a generic program (like 
CosmicCalculator), is because it can have a lot of overhead on each call.
-To be generic and easy to operate, it has to parse the command-line and all 
configuration files (see @ref{Option management and configuration files}) which 
contain human-readable characters and need a lot of pre-processing to be ready 
for processing by the computer.
+@example
+$ astscript-fits-view z-vs-tandist.fits
+@end example
+
+After TOPCAT opens, you will see the name of the table 
@file{z-vs-tandist.fits} in the left panel.
+On the top menu bar, select the ``Graphics'' menu, then select ``Plain plot'' 
to visualize the two columns printed above as a plot and get a better 
impression of the turn over point of the image cosmological coverage.
+
+
+
+
+
+@node Building custom programs with the library, Option management and 
configuration files, Cosmological coverage and visualizing tables, General 
program usage tutorial
+@subsection Building custom programs with the library
+In @ref{Cosmological coverage and visualizing tables}, we repeated a certain 
calculation/output of a program multiple times using the shell's @code{for} 
loop.
+This simple way of repeating a calculation is great when it is only necessary 
once.
+However, if you commonly need this calculation and possibly for a larger 
number of redshifts at higher precision, the command above can be slow.
+Please try it out by changing the sequence command in the previous section to 
`@code{seq 0.1 0.01 10}'.
+It will take about 11 seconds@footnote{To measure how much time the loop of 
@ref{Cosmological coverage and visualizing tables} takes on your system, you 
can use the @command{time} command.
+First put the whole loop (and pipe) into a plain-text file (to be loaded as a 
shell script) called @file{z-vs-tandist.sh}.
+Then run this command: @command{time -p bash z-vs-tandist.sh}.
+The relevant time (in seconds) is shown after @code{real}.}!
+This can be improved by @emph{hundreds} of times!
+This section will show you how.
+
+Generally, repeated calls to a generic program (like CosmicCalculator) are 
slow, because a generic program can have a lot of overhead on each call:
+To be generic and easy to operate, CosmicCalculator has to parse the 
command-line and all configuration files (see @ref{Option management and 
configuration files}) which contain human-readable characters and need a lot of 
pre-processing to be ready for processing by the computer.
 Afterwards, CosmicCalculator has to check the sanity of its inputs and check 
which of its many options you have asked for.
 All the this pre-processing takes as much time as the high-level calculation 
you are requesting, and it has to re-do all of these for every redshift in your 
loop.
 
@@ -2858,7 +2372,7 @@ main(void)
   double H0=67.66, olambda=0.6889, omatter=0.3111, oradiation=0;
 
   /* Do the same thing for all redshifts (z) between 0.1 and 5. */
-  for(z=0.1; z<5; z+=0.1)
+  for(z=0.1; z<10; z+=0.01)
     @{
       /* Calculate the angular diameter distance. */
       adist=gal_cosmology_angular_distance(z, H0, olambda,
@@ -2885,12 +2399,17 @@ $ astbuildprog myprogram.c
 
 @noindent
 In the command above, you used Gnuastro's BuildProgram program.
-Its job is to greatly simplify the compilation, linking and running of simple 
C programs that use Gnuastro's library (like this one).
+Its job is to simplify the compilation, linking and running of simple C 
programs that use Gnuastro's library (like this one).
 BuildProgram is designed to manage Gnuastro's dependencies, compile and link 
your custom program and then run it.
 
-Did you notice how your custom program was much faster than the repeated calls 
to CosmicCalculator in the previous section? You might have noticed that a new 
file called @file{myprogram} is also created in the directory.
+Did you notice how your custom program created the table almost 
instantaneously?
+Technically, it only took about 0.03 seconds!
+Recall that the @code{for} loop of @ref{Cosmological coverage and visualizing 
tables} took more than 11 seconds (or @mymath{\sim367} times slower!).
+
+Please run the @command{ls} command to see a listing of the files in the 
current directory.
+You will notice that a new file called @file{myprogram} has been created.
 This is the compiled program that was created and run by the command above 
(its in binary machine code format, not human-readable any more).
-You can run it again to get the same results with a command like this:
+You can run it again to get the same results by executing it:
 
 @example
 $ ./myprogram
@@ -2911,21 +2430,25 @@ But for this tutorial, let's stop discussing the 
libraries at this point in and
 But before continuing, let's clean up the files we don't need any more:
 
 @example
-$ rm myprogram*
+$ rm myprogram* z-vs-tandist*
 @end example
 
 
 @node Option management and configuration files, Warping to a new pixel grid, 
Building custom programs with the library, General program usage tutorial
 @subsection Option management and configuration files
-None of Gnuastro's programs keep a default value internally within their code.
-However, when you ran CosmicCalculator only with the @option{-z2} option (not 
specifying the cosmological parameters) in @ref{Cosmological coverage}, it 
completed its processing and printed results.
-Where did the necessary cosmological parameters (like the matter density, etc) 
that are necessary for its calculations come from? Fast reply: the values come 
from a configuration file (see @ref{Configuration file precedence}).
-
-CosmicCalculator is a small program with a limited set of parameters/options.
-Therefore, let's use it to discuss configuration files in Gnuastro (for more, 
you can always see @ref{Configuration files}).
-Configuration files are an important part of all Gnuastro's programs, 
especially the ones with a large number of options, so its important to 
understand this part well .
-
-Once you get comfortable with configuration files here, you can make good use 
of them in all Gnuastro programs (for example, NoiseChisel).
+In the previous section (@ref{Cosmological coverage and visualizing tables}), 
when you ran CosmicCalculator, you only specified the redshfit with 
@option{-z2} option.
+You didn't specifying the cosmological parameters that are necessary for the 
calculations!
+Parameters like the Hubble constant (@mymath{H_0}), the matter density, etc.
+In spite of this, CosmicCalculator done its processing and printed results.
+
+None of Gnuastro's programs keep a default value internally within their code 
(they are all set by the user)!
+So where did the necessary cosmological parameters that are necessary for its 
calculations come from?
+What were the values to those parameters?
+In short, they come from a configuration file (see @ref{Configuration file 
precedence}), and the final used values can be checked/edited on the 
command-line.
+In this section we'll review this important aspect of all the programs in 
Gnuastro.
+
+Configuration files are an important part of all Gnuastro's programs, 
especially the ones with a large number of options, so its important to 
understand this part well.
+Once you get comfortable with configuration files, you can make good use of 
them in all Gnuastro programs (for example, NoiseChisel).
 For example, to do optimal detection on various datasets, you can have 
configuration files for different noise properties.
 The configuration of each program (besides its version) is vital for the 
reproducibility of your results, so it is important to manage them properly.
 
@@ -2938,22 +2461,28 @@ However, in each group, options are ordered 
alphabetically.
 $ astcosmiccal --help
 @end example
 
-@noindent
-The options that need a value have an @key{=} sign after their long version 
and @code{FLT}, @code{INT} or @code{STR} for floating point numbers, integer 
numbers, and strings (filenames for example) respectively.
-All options have a long format and some have a short format (a single 
character), for more see @ref{Options}.
+After running the command above, please scroll to the line that you ran this 
command and read through the output (its the same format for all the programs).
+All options have a long format (starting with @code{--} and a multi-character 
name) and some have a short format (starting with @code{-} and a single 
character), for more see @ref{Options}.
+The options that expect a value have an @key{=} sign after their long version.
+The format of their expected value is also shown as @code{FLT}, @code{INT} or 
@code{STR} for floating point numbers, integer numbers, and strings (filenames 
for example) respectively.
 
-When you are using a program, it is often necessary to check the value the 
option has just before the program starts its processing.
-In other words, after it has parsed the command-line options and all 
configuration files.
-You can see the values of all options that need one with the 
@option{--printparams} or @code{-P} option.
+You can see the values of all options that need one with the 
@option{--printparams} option (or its short format: @code{-P}).
 @option{--printparams} is common to all programs (see @ref{Common options}).
-In the command below, try replacing @code{-P} with @option{--printparams} to 
see how both do the same operation.
+You can see the default cosmological parameters (from the 
@url{https://arxiv.org/abs/1807.06209, Plank 2018 results}) under the @code{# 
Input:} title:
 
 @example
 $ astcosmiccal -P
+
+# Input:
+ H0          67.66    # Current expansion rate (Hubble constant).
+ olambda     0.6889   # Current cosmological cst. dens. per crit. dens.
+ omatter     0.3111   # Current matter density per critical density.
+ oradiation  0        # Current radiation density per critical density.
 @end example
 
-Let's say you want a different Hubble constant.
-Try running the following command (just adding @option{--H0=70} after the 
command above) to see how the Hubble constant in the output of the command 
above has changed.
+Let's say you want to do the calculation in the previous section using 
@mymath{H_0=70} km/s/Mpc.
+To do this, just add @option{--H0=70} after the command above (while keeping 
the @option{-P}).
+In the output, you can see that the used Hubble constant has also changed.
 
 @example
 $ astcosmiccal -P --H0=70
@@ -2984,7 +2513,7 @@ So the last command above can also be written as:
 $ astcosmiccal --H0 70 -sz2
 @end example
 
-Let's assume that in one project, you want to only use rounded cosmological 
parameters (H0 of 70km/s/Mpc and matter density of 0.3).
+Let's assume that in one project, you want to only use rounded cosmological 
parameters (@mymath{H_0} of 70km/s/Mpc and matter density of 0.3).
 You should therefore run CosmicCalculator like this:
 
 @example
@@ -2995,10 +2524,10 @@ But having to type these extra options every time you 
run CosmicCalculator will
 Therefore in Gnuastro, you can put all the options and their values in a 
``Configuration file'' and tell the programs to read the option values from 
there.
 
 Let's create a configuration file...
-With your favorite text editor, make a file named @file{my-cosmology.conf} (or 
@file{my-cosmology.txt}, the suffix doesn't matter, but a more descriptive 
suffix like @file{.conf} is recommended).
-Then put the following lines inside of it.
+With your favorite text editor, make a file named @file{my-cosmology.conf} (or 
@file{my-cosmology.txt}, the suffix doesn't matter for Gnuastro, but a more 
descriptive suffix like @file{.conf} is recommended for humans reading your 
code and seeing your files: this includes you, looking into your own project, 
in a couple of months that you have forgot the details!).
+Then put the following lines inside of the plain-text file.
 One space between the option value and name is enough, the values are just 
under each other to help in readability.
-Also note that you can only use long option names in configuration files.
+Also note that you should only use @emph{long option names} in configuration 
files.
 
 @example
 H0       70
@@ -3035,8 +2564,9 @@ Once you run CosmicCalculator within @file{my-cosmology} 
(as shown below), you w
 
 @example
 $ cd my-cosmology
-$ astcosmiccal -P
+$ astcosmiccal -P       # Your custom cosmology is printed.
 $ cd ..
+$ astcosmiccal -P       # The default cosmology is printed.
 @end example
 
 To further simplify the process, you can use the @option{--setdirconf} option.
@@ -3127,7 +2657,10 @@ $ rm *.fits
 
 @node NoiseChisel and Multiextension FITS files, NoiseChisel optimization for 
detection, Warping to a new pixel grid, General program usage tutorial
 @subsection NoiseChisel and Multiextension FITS files
-Having completed a review of the basics in the previous sections, we are now 
ready to separate the signal (galaxies or stars) from the background noise in 
the image.
+In the previous sections, we commpleted a review of the basics of Gnuastro's 
programs.
+We are now ready to do some more serious analysis on the downloaded images: 
extract the pixels containing signal from the image, find sub-structure of the 
extracted signal, do measurements over the extracted objects and analyze them 
(finding certain objects of interest in the image).
+
+The first step  is to separate the signal (galaxies or stars) from the 
background noise in the image.
 We will be using the results of @ref{Dataset inspection and cropping}, so be 
sure you already have them.
 Gnuastro has NoiseChisel for this job.
 But NoiseChisel's output is a multi-extension FITS file, therefore to better 
understand how to use NoiseChisel, let's take a look at multi-extension FITS 
files and how you can interact with them.
@@ -3141,60 +2674,65 @@ $ astnoisechisel flat-ir/xdf-f160w.fits
 $ astfits xdf-f160w_detected.fits
 @end example
 
-From the output list, we see that NoiseChisel's output contains 5 extensions 
and the first (counting from zero, with name @code{NOISECHISEL-CONFIG}) is 
empty: it has value of @code{0} in the last column (which shows its size).
-The first extension in all the outputs of Gnuastro's programs only contains 
meta-data: data about/describing the datasets within (all) the output's 
extensions.
+From the output list, we see that NoiseChisel's output contains 5 extensions.
+The zero-th (counting from zero, with name @code{NOISECHISEL-CONFIG}) is 
empty: it has value of @code{0} in the fourth column (which shows its size in 
pixels).
+Like NoiseChisel, in all of Gnuastro's programs, the first (or zero-th) 
extension of the output only contains meta-data: data about/describing the 
datasets within (all) the output's extensions.
 This is recommended by the FITS standard, see @ref{Fits} for more.
 In the case of Gnuastro's programs, this generic zero-th/meta-data extension 
(for the whole file) contains all the configuration options of the program that 
created the file.
 
-The second extension of NoiseChisel's output (numbered 1, named 
@code{INPUT-NO-SKY}) is the Sky-subtracted input that you provided.
-The third (@code{DETECTIONS}) is NoiseChisel's main output which is a binary 
image with only two possible values for all pixels: 0 for noise and 1 for 
signal.
-Since it only has two values, to avoid taking too much space on your computer, 
its numeric datatype an unsigned 8-bit integer (or @code{uint8})@footnote{To 
learn more about numeric data types see @ref{Numeric data types}.}.
-The fourth and fifth (@code{SKY} and @code{SKY_STD}) extensions, have the Sky 
and its standard deviation values for the input on a tile grid and were 
calculated over the undetected regions (for more on the importance of the Sky 
value, see @ref{Sky value}).
-
 Metadata regarding how the analysis was done (or a dataset was created) is 
very important for higher-level analysis and reproducibility.
 Therefore, Let's first take a closer look at the @code{NOISECHISEL-CONFIG} 
extension.
 If you specify a special header in the FITS file, Gnuastro's Fits program will 
print the header keywords (metadata) of that extension.
 You can either specify the HDU/extension counter (starting from 0), or name.
-Therefore, the two commands below are identical for this file:
+Therefore, the two commands below are identical for this file.
+We are usually tempted to use the first (shorter format), but when putting 
your commands into a script, please use the second format which is more 
human-friendly and understandable for readers of your code who may not know 
what is in the 0-th extension (this includes your self in a few months!):
 
 @example
 $ astfits xdf-f160w_detected.fits -h0
 $ astfits xdf-f160w_detected.fits -hNOISECHISEL-CONFIG
 @end example
 
-The first group of FITS header keywords are standard keywords (containing the 
@code{SIMPLE} and @code{BITPIX} keywords the first empty line).
+The first group of FITS header keywords you see (containing the @code{SIMPLE} 
and @code{BITPIX} keywords; before the first empty line) are standard keywords.
 They are required by the FITS standard and must be present in any FITS 
extension.
-The second group contains the input file and all the options with their values 
in that run of NoiseChisel.
-Finally, the last group contains the date and version information of Gnuastro 
and its dependencies.
+The second group starts with the input file name (value to the @code{INPUT} 
keyword).
+The rest of the keywords you see afterwards have the same name as 
NoiseChisel's options, and the value used by NoiseChisel in this run is shown 
after the @code{=} sign.
+Finally, the last group (starting with @code{DATE}) contains the date and 
version information of Gnuastro and its dependencies that were used to generate 
this file.
+Besides the option values, these are also critical for future reproducibility 
of the result (you may update Gnuastro or its dependencies, and they may behave 
differently afterwards).
 The ``versions and date'' group of keywords are present in all Gnuastro's FITS 
extension outputs, for more see @ref{Output FITS files}.
 
 Note that if a keyword name is larger than 8 characters, it is preceded by a 
@code{HIERARCH} keyword and that all keyword names are in capital letters.
-Therefore, if you want to see only one keyword's value by feeding the output 
to Grep, you should ask Grep to ignore case with its @option{-i} option (short 
name for @option{--ignore-case}).
-For example, below we'll check the value to the @option{--snminarea} option, 
note how we don't need Grep's @option{-i} option when it is fed with 
@command{astnoisechisel -P} since it is already in small-caps there.
-The extra white spaces in the first command are only to help in readability, 
you can ignore them when typing.
+These are all part of the FITS standard and originate from its history.
+But in short, both can be ignored!
+For example, with the command below, let's see what the default value of the 
@option{--detgrowquant} option is (using the @option{-P} option described in 
@ref{Option management and configuration files}).
 
 @example
-$ astnoisechisel -P                   | grep    snminarea
-$ astfits xdf-f160w_detected.fits -h0 | grep -i snminarea
+$ astnoisechisel -P | grep detgrowquant
 @end example
 
-@noindent
-The metadata (that is stored in the output) can later be used to exactly 
reproduce/understand your result, even if you have lost/forgot the command you 
used to create the file.
-This feature is present in all of Gnuastro's programs, not just NoiseChisel.
+To confirm that NoiseChisel used this value when we ran it above, let's use 
@code{grep} to extract the keyword line with @code{detgrowquant} from the 
metadata extension.
+However, as you saw above, keyword names in the header is in all caps.
+So we need to ask @code{grep} to ignore case with the @option{-i} option.
 
-@cindex DS9
-@cindex GNOME
-@cindex SAO DS9
-Let's continue with the extensions in NoiseChisel's output that contain a 
dataset by visually inspecting them (here, we'll use SAO DS9).
-Since the file contains multiple related extensions, the easiest way to view 
all of them in DS9 is to open the file as a ``Multi-extension data cube'' with 
the @option{-mecube} option as shown below@footnote{You can configure your 
graphic user interface to open DS9 in multi-extension cube mode by default when 
using the GUI (double clicking on the file)}.
+@example
+$ astfits xdf-f160w_detected.fits -h0 | grep -i detgrowquant
+@end example
+
+In the output of the above command, you see @code{HIERARCH} at the start of 
the line.
+According to the FITS standard, @code{HIERARCH} is placed at the start of all 
keywords that have a name that is more than 8 characters long.
+Both the all-caps and the @code{HIERARCH} keyword can be annoying when you 
want to read/check the value.
+Therefore, the best solution is to use the @option{--keyvalue} option of 
Gnuastro's @command{astfits} program as shown below.
+With it, you don't have to worry about @code{HIERARCH} or the case of the name 
(FITS keyword names are not case-sensitive).
 
 @example
-$ ds9 -mecube xdf-f160w_detected.fits -zscale -zoom to fit
+$ astfits xdf-f160w_detected.fits -h0 --keyvalue=detgrowquant -q
 @end example
 
-To simplify viewing multi-extension FITS images in SAO DS9 (or tables in 
TOPCAT) and avoid such long commands, Gnuastro has the 
@code{astscript-fits-view}.
-It is just a simple wrapper to call them with some useful customizations 
depending on your input (see @ref{Viewing FITS file contents with DS9 or 
TOPCAT}).
-It also comes with a @file{.desktop} file (usable in GNOME or KDE graphic user 
interfaces or GUIs) to enable easy double-clicking on any number of FITS files 
to open the respective program (very useful when navigating in your graphic 
environment):
+@noindent
+The metadata (that is stored in the output) can later be used to exactly 
reproduce/understand your result, even if you have lost/forgot the command you 
used to create the file.
+This feature is present in all of Gnuastro's programs, not just NoiseChisel.
+
+The rest of the HDUs in NoiseChisel have data.
+So let's open them in a DS9 window and then describe each:
 
 @example
 $ astscript-fits-view xdf-f160w_detected.fits
@@ -3206,6 +2744,11 @@ In this mode, all DS9's settings (for example zoom or 
color-bar) will be identic
 Try zooming into one part and flipping through the extensions to see how the 
galaxies were detected along with the Sky and Sky standard deviation values for 
that region.
 Just have in mind that NoiseChisel's job is @emph{only} detection (separating 
signal from noise), We'll do segmentation on this result later to find the 
individual galaxies/peaks over the detected pixels.
 
+The second extension of NoiseChisel's output (numbered 1, named 
@code{INPUT-NO-SKY}) is the Sky-subtracted input that you provided.
+The third (@code{DETECTIONS}) is NoiseChisel's main output which is a binary 
image with only two possible values for all pixels: 0 for noise and 1 for 
signal.
+Since it only has two values, to avoid taking too much space on your computer, 
its numeric datatype an unsigned 8-bit integer (or @code{uint8})@footnote{To 
learn more about numeric data types see @ref{Numeric data types}.}.
+The fourth and fifth (@code{SKY} and @code{SKY_STD}) extensions, have the Sky 
and its standard deviation values for the input on a tile grid and were 
calculated over the undetected regions (for more on the importance of the Sky 
value, see @ref{Sky value}).
+
 Each HDU/extension in a FITS file is an independent dataset (image or table) 
which you can delete from the FITS file, or copy/cut to another file.
 For example, with the command below, you can copy NoiseChisel's 
@code{DETECTIONS} HDU/extension to another file:
 
@@ -3539,11 +3082,12 @@ $ astsegment nc/xdf-f105w.fits -oseg/xdf-f105w.fits
 @end example
 
 Segment's operation is very much like NoiseChisel (in fact, prior to version 
0.6, it was part of NoiseChisel).
-For example the output is a multi-extension FITS file, it has check images and 
uses the undetected regions as a reference.
-Please have a look at Segment's multi-extension output with @command{ds9} to 
get a good feeling of what it has done.
+For example the output is a multi-extension FITS file (previously discussed in 
@ref{NoiseChisel and Multiextension FITS files}), it has check images and uses 
the undetected regions as a reference (previously discussed in @ref{NoiseChisel 
optimization for detection}).
+Please have a look at Segment's multi-extension output to get a good feeling 
of what it has done.
+Don't forget to flip through the extensions in the ``Cube'' window.
 
 @example
-$ ds9 -mecube seg/xdf-f160w.fits -zscale -zoom to fit
+$ astscript-fits-view seg/xdf-f160w.fits
 @end example
 
 Like NoiseChisel, the first extension is the input.
@@ -3642,11 +3186,7 @@ $ asttable cat/xdf-f160w.fits -hCLUMPS -i
 
 Finally, MakeCatalog also does basic calculations on the full dataset 
(independent of each labeled region but related to whole data), for example 
pixel area or per-pixel surface brightness limit.
 They are stored as keywords in the FITS headers (or lines starting with 
@code{#} in plain text).
-You can see them with this command (for more, see @ref{Image surface 
brightness limit} in the next tutorial):
-
-@example
-$ astfits cat/xdf-f160w.fits -h1
-@end example
+This (and other ways to measure the limits of your dataset) are discussed in 
the next section: @ref{Measuring the dataset limits}.
 
 @node Measuring the dataset limits, Working with catalogs estimating colors, 
Segmentation and making a catalog, General program usage tutorial
 @subsection Measuring the dataset limits
@@ -3656,7 +3196,7 @@ Before measuring colors, or doing any other kind of 
analysis on the catalogs (an
 Without understanding the limitations of your dataset, you cannot make any 
physical interpretation of your results.
 The theory behind the calculations discussed here is thoroughly introduced in 
@ref{Quantifying measurement limits}.
 
-For example, with the command below, let's sort all the detected clumps in the 
image by magnitude, and have a look at the RA, Declination, magnitude and S/N 
columns after sorting:
+For example, with the command below, let's sort all the detected clumps in the 
image by magnitude and signal-to-noise ratio (S/N) columns after sorting:
 
 @example
 $ asttable cat/xdf-f160w.fits -hclumps -cmagnitude,sn \
@@ -3664,7 +3204,10 @@ $ asttable cat/xdf-f160w.fits -hclumps -cmagnitude,sn \
 @end example
 
 As you see, we have clumps with a total magnitude of almost 32!
-Let's have a look at all of those with a magnitude between 31 and 32:
+This is @emph{extremely faint}!
+Are these things trustable?
+Let's have a look at all of those with a magnitude between 31 and 32 with the 
command below.
+We are first using Table to only keep the relevant columns rows, and using 
Gnuastro's DS9 region file creation script (@code{astscript-ds9-region}) to 
generate DS9 region files, and open DS9:
 
 @example
 $ asttable cat/xdf-f160w.fits -hclumps -cra,dec \
@@ -3673,9 +3216,15 @@ $ asttable cat/xdf-f160w.fits -hclumps -cra,dec \
            --command="ds9 -mecube seg/xdf-f160w.fits -zscale"
 @end example
 
+Zoom-out a little and you will see some green circles (DS9 region files) in 
some regions of the image.
 There actually does seem to be a true peak under the selected regions, but as 
you see, they are very small, diffuse and noisy.
 How reliable are the measured magnitudes?
-Using the S/N column from the first command above, you can see that such 
objects only have a signal to noise of about 1.5.
+Using the S/N column from the first command above, you can see that such 
objects only have a signal to noise of about 2.6 (which is indeed too low for 
most analysis purposes)
+
+@example
+$ asttable cat/xdf-f160w.fits -hclumps -csn \
+           --range=magnitude,31:32 | aststatistics
+@end example
 
 This brings us to the first method of quantifying your dataset's 
@emph{magnitude limit}, which is also sometimes called @emph{detection limit} 
(see @ref{Magnitude limit of image}).
 To estimate the @mymath{5\sigma} detection limit of your dataset, you simply 
report the median magnitude of the objects that have a signal to noise of 
(approximately) five.
@@ -3684,11 +3233,10 @@ This is very easy to calculate with the command below:
 @example
 $ asttable cat/xdf-f160w.fits -hclumps --range=sn,4.8:5.2 -cmagnitude \
            | aststatistics --median
-29.9627
+29.9949
 @end example
 
-But this is unreasonably faint!
-So let's have a look at these objects, to get a feeling of what these clump 
looks like:
+Let's have a look at these objects, to get a feeling of what these clump looks 
like:
 
 @example
 $ asttable cat/xdf-f160w.fits -hclumps --range=sn,4.8:5.2 \
@@ -3702,9 +3250,13 @@ The number you see on top of each region is the clump's 
magnitude.
 Please go over the objects and have a close look at them!
 It is very important to have a feeling of what your dataset looks like, and 
how to interpret the numbers to associate an image with them.
 
-Generally, they look very small and seem to be much fainter than what you 
would expect to be @mymath{5\sigma}.
-The main issue is that MakeCatalog uses individual pixel measurements of 
signal and noise to estimate this.
-However, during the reduction many exposures are co-added and stacked, mixing 
the pixels like a small convolution.
+@cindex Correlated noise
+@cindex Noise (correlated)
+Generally, they look very small with different levels of diffuse-ness!
+Those that are sharper make more visual sense (to be @mymath{5\sigma} 
detections), but the more diffuse ones extend over a larger area.
+Furthermore, the noise is measured on individual pixel measurements.
+However, during the reduction many exposures are co-added and stacked, mixing 
the pixels like a small convolution (creating ``correlated noise'').
+Therefore you clearly see two main issues with the detection limit as defined 
above: it depends on the morphology, and it doesn't take into account the 
correlated noise.
 
 @cindex Upper-limit
 A more realistic way to estimate the significance of the detection is to take 
its footprint, randomly place it in thousands of undetected regions of the 
image and use that distribution as a reference.
@@ -3721,6 +3273,7 @@ $ astmkcatalog seg/xdf-f160w.fits --ids --ra --dec 
--magnitude --sn \
                --output=xdf-f160w.fits
 @end example
 
+@noindent
 Let's compare the upper-limit magnitude with the measured magnitude of each 
clump:
 
 @example
@@ -3728,22 +3281,29 @@ $ asttable xdf-f160w.fits -hclumps 
-cmagnitude,upperlimit_mag
 @end example
 
 As you see, in almost all of the cases, the measured magnitude is sufficiently 
higher than the upper-limit magnitude.
-For these objects, the clump seems to have sufficiently higher brightness than 
the noisy background.
-Let's use Table's @ref{Column arithmetic} to find only those that have a 
positive difference:
+Let's subtract the latter from the former to better see this difference in a 
third column:
+
+@example
+asttable xdf-f160w.fits -hclumps -cmagnitude,upperlimit_mag \
+         -c'arith upperlimit_mag magnitude -'
+@end example
+
+The ones with a positive third column (difference) we can say that the clump 
seems to has sufficiently higher brightness than the noisy background to be 
usable.
+Let's use Table's @ref{Column arithmetic} to find only those that have a 
negative difference:
 
 @example
 $ asttable xdf-f160w.fits -hclumps -cra,dec --noblankend=3 \
-      -c'arith magnitude upperlimit_mag - set-d d d 0 lt nan where'
+      -c'arith upperlimit_mag magnitude - set-d d d 0 gt nan where'
 @end example
 
 @noindent
-From more than 3500 clumps, this command only gave 177 rows!
+From more than 3500 clumps, this command only gave @mymath{\sim150} rows (this 
number may slightly change on different runs due to the random nature of the 
upper-limit sampling@footnote{You can fix the random number generator seed, so 
you always get the same sampling, see @ref{Generating random numbers}.})!
 Let's have a look at them:
 
 @example
 $ asttable xdf-f160w.fits -hclumps -cra,dec --noblankend=3 \
-       -c'arith magnitude upperlimit_mag - set-d d d 0 lt nan where' \
-       | astscript-ds9-region -c1,2 --namecol=3 --width=2 \
+      -c'arith upperlimit_mag magnitude - set-d d d 0 gt nan where' \
+      | astscript-ds9-region -c1,2 --namecol=3 --width=2 \
                   --radius=0.5 \
                   --command="ds9 -mecube seg/xdf-f160w.fits -zscale"
 @end example
@@ -3774,12 +3334,15 @@ $ asttable xdf-f160w.fits -hclumps 
--range=upperlimit_sigma,4.8:5.2 \
 
 We see that the @mymath{5\sigma} detection limit is @mymath{\sim29.6}!
 This is extremely deep!
-For example in the Legacy 
Survey@footnote{@url{https://www.legacysurvey.org/dr9/description}}, the 
@mymath{5\sigma} detection limit for @emph{point sources} is approximately 24.5 
(5 magnitudes shallower than this image).
+For example in the Legacy 
Survey@footnote{@url{https://www.legacysurvey.org/dr9/description}}, the 
@mymath{5\sigma} detection limit for @emph{point sources} is approximately 24.5 
(5 magnitudes, or 100 times, shallower than this image).
 
-An important caveat in this simple calculation is that we should only be 
looking at point-like objects, not simply everything.
+As mentioned above, an important caveat in this simple calculation is that we 
should only be looking at point-like objects, not simply everything.
 This is because the shape or radial slope of the profile has an important 
effect on this measurement: at the same total magnitude, a sharper object will 
have a higher S/N.
 To be more precise, we should first perform star-galaxy separation, then do 
this only the objects classified as stars.
-A crude, first-order, method is to use the @option{--axisratio} option so 
MakeCatalog also measures the axis ratio, then call Table with 
@option{--range=sn,4.8:5.2} and @option{--range=axis_ratio,0.95:1} (in one 
command).
+A crude, first-order, method is to use the @option{--axisratio} option so 
MakeCatalog also measures the axis ratio, then call Table with 
@option{--range=upperlimit_sigma,,4.8:5.2} and 
@option{--range=axis_ratio,0.95:1} (in one command).
+Please do this for your self as an exercise to see the difference with the 
result above.
+
+@noindent
 Before continuing, let's remove this temporarily produced catalog:
 
 @example
@@ -3826,11 +3389,12 @@ $ aststatistics cat/xdf-f160w.fits -hclumps -cmagnitude 
--histogram \
                 --greaterequal=20 --lessthan=32 --numbins=24 \
                 --output=f160w-hist.txt
 $ asttable f160w-hist.txt \
-           | awk '$2>50 && $2<prev@{print $1; exit@} @{prev=$2@}'
-29.10874136289
+           | awk '$2>50 && $2<prev@{print prevbin; exit@} \
+                  @{prev=$2; prevbin=$1@}'
+28.932122667631
 @end example
 
-Therefore, to first order (and very crudely!) we can say that if an object is 
in our field of view and has a magnitude of 29.1 or brighter, we can be highly 
confident that we have detected it.
+Therefore, to first order (and very crudely!) we can say that if an object is 
in our field of view and has a magnitude of @mymath{\sim29} or brighter, we can 
be highly confident that we have detected it.
 But before continuing, let's clean up behind ourselves:
 
 @example
@@ -3999,9 +3563,9 @@ $ asttable cat/xdf-f160w.fits -hCLUMPS 
--output=three-in-one-3.fits \
         --catcolumnfile=cat/xdf-f105w-on-f160w-lab.fits \
         --catcolumnhdu=CLUMPS --catcolumnhdu=CLUMPS \
         --catcolumns=MAGNITUDE \
-        --colmetadata=MAGNITUDE,MAG-F160w,log,"Magnitude in F160W." \
-        --colmetadata=MAGNITUDE-1,MAG-F125w,log,"Magnitude in F125W." \
-        --colmetadata=MAGNITUDE-2,MAG-F105w,log,"Magnitude in F105W."
+        --colmetadata=MAGNITUDE,MAG-F160W,log,"Magnitude in F160W." \
+        --colmetadata=MAGNITUDE-1,MAG-F125W,log,"Magnitude in F125W." \
+        --colmetadata=MAGNITUDE-2,MAG-F105W,log,"Magnitude in F105W."
 $ asttable three-in-one-3.fits -i
 @end example
 
@@ -4064,9 +3628,9 @@ $ asttable three-in-one-3.fits --range=SN,5,inf 
-c1,2,RA,DEC,SN \
          -c'arith MAG-F105W MAG-F125W -' \
          -c'arith MAG-F105W MAG-F160W -' \
          --colmetadata=SN,SN-F160W,ratio,"F160W signal to noise ratio" \
-         --colmetadata=ARITH_1,F125W-F160W,log,"Color F125W and F160W" \
-         --colmetadata=ARITH_2,F105W-F125W,log,"Color F105W and F125W" \
-         --colmetadata=ARITH_3,F105W-F160W,log,"Color F105W and F160W" \
+         --colmetadata=ARITH_1,F125W-F160W,log,"Color F125W-F160W." \
+         --colmetadata=ARITH_2,F105W-F125W,log,"Color F105W-F125W." \
+         --colmetadata=ARITH_3,F105W-F160W,log,"Color F105W-F160W." \
          --output=cat/mags-with-color.fits
 $ asttable cat/mags-with-color.fits -i
 @end example
@@ -4162,10 +3726,10 @@ $ aststatistics cat/mags-with-color.fits 
-cMAG-F160W,F105W-F160W \
 
 @noindent
 You can now open this FITS file as a normal FITS image, for example with the 
command below.
-Try hovering/zooming over the pixels: not only will you see the number of 
objects in the UVUDF catalog that fall in each bin/pixel, but you also see the 
@code{F160W} magnitude and color of that pixel also (in the same place you 
usually see RA and Dec when hovering over an astronomical image).
+Try hovering/zooming over the pixels: not only will you see the number of 
objects in catalog that fall in each bin/pixel, but you also see the 
@code{F160W} magnitude and color of that pixel also (in the same place you 
usually see RA and Dec when hovering over an astronomical image).
 
 @example
-$ ds9 cmd.fits -cmap sls -zoom to fit
+$ astscript-fits-view cmd.fits --ds9scale=minmax
 @end example
 
 Having a 2D histogram as a FITS image with WCS has many great advantages.
@@ -4177,15 +3741,17 @@ With the first command below, you can activate the grid 
feature of DS9 to actual
 With the second command, DS9 will even read the labels of the axes and use 
them to generate an almost publication-ready plot.
 
 @example
-$ ds9 cmd.fits -cmap sls -zoom to fit -grid yes
-$ ds9 cmd.fits -cmap sls -zoom to fit -grid yes -grid type publication
+$ astscript-fits-view cmd.fits --ds9scale=minmax --ds9extra="-grid yes"
+$ astscript-fits-view cmd.fits --ds9scale=minmax \
+           --ds9extra="-grid yes -grid type publication"
 @end example
 
 If you are happy with the grid and coloring and etc, you can also use ds9 to 
save this as a JPEG image to directly use in your documents/slides with these 
extra DS9 options (DS9 will write the image to @file{cmd-2d.jpeg} and quit 
immediately afterwards):
 
 @example
-$ ds9 cmd.fits -cmap sls -zoom 4 -grid yes -grid type publication \
-      -saveimage cmd-2d.jpeg -quit
+$ astscript-fits-view cmd.fits --ds9scale=minmax \
+           --ds9extra="-grid yes -grid type publication" \
+           --ds9extra="-saveimage cmd-2d.jpeg -quit"
 @end example
 
 @cindex PGFPlots (@LaTeX{} package)
@@ -4199,15 +3765,15 @@ We'll use Gnuastro's @ref{ConvertType} for this, and 
use the @code{sls-inverse}
 $ astconvertt cmd.fits --colormap=sls-inverse --borderwidth=0 -ocmd.pdf
 @end example
 
-@noindent
+Open the resulting @file{cmd.pdf} and see the PDF.
 Below you can see a minimally working example of how to add axis numbers, 
labels and a grid to the PDF generated above.
 First, let's create a new @file{report} directory to keep the @LaTeX{} 
outputs, then put the minimal report's source in a file called 
@file{report.tex}.
 Notice the @code{xmin}, @code{xmax}, @code{ymin}, @code{ymax} values and how 
they are the same as the range specified above.
 
 @example
-$ mkdir report
-$ mv cmd.pdf report/
-$ cat report/report.tex
+$ mkdir report-cmd
+$ mv cmd.pdf report-cmd/
+$ cat report-cmd/report.tex
 \documentclass@{article@}
 \usepackage@{pgfplots@}
 \dimendef\prevdepth=0
@@ -4235,7 +3801,7 @@ You can write all you want here...\par
 Run this command to build your PDF (assuming you have @LaTeX{} and PGFPlots).
 
 @example
-$ cd report
+$ cd report-cmd
 $ pdflatex report.tex
 @end example
 
@@ -4305,7 +3871,7 @@ You can also change the filter name and zero point 
magnitudes and run this comma
 
 
 
-@node Matching catalogs, Finding reddest clumps and visual inspection, 
Aperture photometry, General program usage tutorial
+@node Matching catalogs, Reddest clumps cutouts and parallelization, Aperture 
photometry, General program usage tutorial
 @subsection Matching catalogs
 
 In the example above, we had the luxury to generate the catalogs ourselves, 
and where thus able to generate them in a way that the rows match.
@@ -4368,8 +3934,8 @@ $ astfits matched.fits
 
 
 
-@node Finding reddest clumps and visual inspection, Writing scripts to 
automate the steps, Matching catalogs, General program usage tutorial
-@subsection Finding reddest clumps and visual inspection
+@node Reddest clumps cutouts and parallelization, FITS images in a 
publication, Matching catalogs, General program usage tutorial
+@subsection Reddest clumps, cutouts and parallelization
 @cindex GNU AWK
 As a final step, let's go back to the original clumps-based color measurement 
we generated in @ref{Working with catalogs estimating colors}.
 We'll find the objects with the strongest color and make a cutout to inspect 
them visually and finally, we'll see how they are located on the image.
@@ -4391,13 +3957,24 @@ We'll define this identifier using the object and clump 
labels (with an undersco
 Note that since we are making a plain text table, we'll define the necessary 
(for the string-type first column) metadata manually (see @ref{Gnuastro text 
table format}).
 
 @example
-$ echo "# Column 1: ID [name, str10] Object ID" > reddest.txt
+$ echo "# Column 1: ID [name, str10] Object ID" > cat/reddest.txt
 $ asttable cat/mags-with-color.fits --range=F105W-F160W,1.5,inf \
            | awk '@{printf("%d_%-10d %f %f\n", $1, $2, $3, $4)@}' \
-           >> reddest.txt
+           >> cat/reddest.txt
+@end example
+
+@cindex DS9
+@cindex SAO DS9
+Let's see how these objects are positioned over the dataset.
+DS9 has the ``Region''s concept for this purpose.
+And you build such regions easily from a table using Gnuastro's 
@command{astscript-ds9-region} installed script, using the command below:
+
+@example
+$ astscript-ds9-region cat/reddest.txt -c2,3 --mode=wcs \
+           --command="ds9 flat-ir/xdf-f160w.fits -zscale"
 @end example
 
-We can now feed @file{reddest.txt} into Gnuastro's Crop program to see what 
these objects look like.
+We can now feed @file{cat/reddest.txt} into Gnuastro's Crop program to get 
separate postage stamps for each object.
 To keep things clean, we'll make a directory called @file{crop-red} and ask 
Crop to save the crops in this directory.
 We'll also add a @file{-f160w.fits} suffix to the crops (to remind us which 
filter they came from).
 The width of the crops will be 15 arc-seconds (or 15/3600 degrees, which is 
the units of the WCS).
@@ -4405,21 +3982,26 @@ The width of the crops will be 15 arc-seconds (or 
15/3600 degrees, which is the
 @example
 $ mkdir crop-red
 $ astcrop flat-ir/xdf-f160w.fits --mode=wcs --namecol=ID \
-          --catalog=reddest.txt --width=15/3600,15/3600  \
+          --catalog=cat/reddest.txt --width=15/3600,15/3600  \
           --suffix=-f160w.fits --output=crop-red
 @end example
 
-You can see all the cropped FITS files in the @file{crop-red} directory.
-Like the MakeProfiles command in @ref{Aperture photometry}, you might notice 
that the crops aren't made in order.
+Like the MakeProfiles command in @ref{Aperture photometry}, if you look at the 
order of the crops, you will notice that the crops aren't made in order!
 This is because each crop is independent of the rest, therefore crops are done 
in parallel, and parallel operations are asynchronous.
+So the order can differ in each run, but the final output is the same!
 In the command above, you can change @file{f160w} to @file{f105w} to make the 
crops in both filters.
+You can see all the cropped FITS files in the @file{crop-red} directory with 
this command:
+
+@example
+$ astscript-fits-view crop-red/*.fits
+@end example
 
 To view the crops more easily (not having to open ds9 for each image), you can 
convert the FITS crops into the JPEG format with a shell loop like below.
 
 @example
 $ cd crop-red
-$ for f in *.fits; do                                                  \
-    astconvertt $f --fluxlow=-0.001 --fluxhigh=0.005 --invert -ojpg;   \
+$ for f in *.fits; do \
+    astconvertt $f --fluxlow=-0.001 --fluxhigh=0.005 --invert -ojpg; \
   done
 $ cd ..
 $ ls crop-red/
@@ -4427,53 +4009,443 @@ $ ls crop-red/
 
 You can now use your general graphic user interface image viewer to flip 
through the images more easily, or import them into your papers/reports.
 
-@cindex GNU Parallel
+@cindex GNU Make
+@cindex @file{Makefile}
 The @code{for} loop above to convert the images will do the job in series: 
each file is converted only after the previous one is complete.
-If you have @url{https://www.gnu.org/s/parallel, GNU Parallel}, you can 
greatly speed up this conversion.
-GNU Parallel will run the separate commands simultaneously on different CPU 
threads in parallel.
-For more information on efficiently using your threads, see 
@ref{Multi-threaded operations}.
-Here is a replacement for the shell @code{for} loop above using GNU Parallel.
+But like the crops, each JPEG image is independent, so let's parallelize it.
+In other words, we want to write run more than one instance of the command at 
any moment.
+To do that, we will use @url{https://en.wikipedia.org/wiki/Make_(software), 
Make}.
+Make is a very wonderful pipeline management system, and the most common and 
powerful implementation is @url{https://www.gnu.org/software/make, GNU Make}, 
which has a complete manual just like this one.
+We can't go into the details of Make here, for a hands-on video tutorial, see 
this @url{https://peertube.stream/w/iJitjS3r232Z8UPMxKo6jq, video introduction}.
+To do the process above in Make, please copy the contents below into a 
plain-text file called @file{Makefile}.
+Just replace the @code{__[TAB]__} part at the start of the line with a single 
`@key{TAB}' button on your keyboard.
 
 @example
-$ cd crop-red
-$ parallel astconvertt --fluxlow=-0.001 --fluxhigh=0.005 --invert   \
-           -ojpg ::: *.fits
-$ cd ..
+jpgs=$(subst .fits,.jpg,$(wildcard *.fits))
+all: $(jpgs)
+$(jpgs): %.jpg: %.fits
+__[TAB]__astconvertt $< --fluxlow=-0.001 --fluxhigh=0.005 \
+__[TAB]__            --invert -o$@
+@end example
+
+Now that the @file{Makefile} is ready, you can run Make on 12 threads using 
the commands below.
+Feel free to replace the 12 with any number of threads you have on your system 
(you can find out by running the @command{nproc} command on GNU/Linux operating 
systems):
+
+@example
+$ make -j12
 @end example
 
 @noindent
-Did you notice how much faster this one was? When possible, its always very 
helpful to do your analysis in parallel.
-But the problem is that many operations are not as simple as this.
-For such cases, you can use 
@url{https://en.wikipedia.org/wiki/Make_(software), Make} which will greatly 
help designing workflows.
-But that is beyond the topic here.
+Did you notice how much faster this one was?
+When possible, its always very helpful to do your analysis in parallel.
+You can build very complex workflows with Make, for example see 
@url{https://arxiv.org/abs/2006.03018, Akhlaghi et al. (2021)} so its worth 
spending some time to master.
 
-@cindex DS9
-@cindex SAO DS9
-As the final action, let's see how these objects are positioned over the 
dataset.
-DS9 has the ``Region''s concept for this purpose.
-You just have to convert your catalog into a ``region file'' to feed into DS9.
-To do that, you can use AWK again as shown below.
+@node FITS images in a publication, Marking objects for publication, Reddest 
clumps cutouts and parallelization, General program usage tutorial
+@subsection FITS images in a publication
+
+In the previous section (@ref{Reddest clumps cutouts and parallelization}), we 
visually inspected the positions of the reddest objects using DS9.
+That is very good for an interactive inspection of the objects: you can 
zoom-in and out, you can do measurements and etc.
+Once the experimentation phase of your project is complete, you want to show 
these objects over the whole image in a report, paper or slides.
+
+One solution is to use DS9 itself!
+For example, run the @command{astscript-fits-view} command of the previous 
section to open DS9 with the regions overplotted.
+Click on the ``File'' menu and select ``Save Image''.
+In the side-menu that opens, you have multiple formats to select from.
+Usually for publications, we want to show the regions and text (in the 
colorbar) in vector graphics, so it is best to export to EPS.
+Once you have made the EPS, you can then convert it to PDF with the 
@command{epspdf} command.
+
+Another solution is to use Gnuastro's ConvertType progarm.
+The main difference is that DS9 is a Graphic User Interfac (GUI) program, so 
it takes relatively long (about a second) to load, and it requires many 
dependencies.
+This will slow-down automatic conversion of many files, and will make your 
code hard to more to another operating system.
+DS9 does have a command-line interface that you can use to automate the 
creation of each file, however, it has a very peculiar command-line interface 
and formats (like the ``region'' files).
+However, in ConvertType, there is no graphic interface, so it has very few 
dependencies, it is fast, and finally, it takes normal tables (in plain-text or 
FITS) as input.
+So in this concluding step of the analysis, let's build a nice 
publication-ready plot, showing the positions of the reddest objects in the 
image for our paper.
+
+In @ref{Reddest clumps cutouts and parallelization}, we already used 
ConvertType to make JPEG postage stamps.
+Here, we'll use it to make a PDF image of the whole deep region.
+To start, let's simply run ConvertType on the F160W image:
+
+@example
+$ astconvertt flat-ir/xdf-f160w.fits -oxdf.pdf
+@end example
+
+Open the output in a PDF viewer. You see that it is almost fully black!
+Let's see why this happens!
+First, with the two commands below, let's calculate the maximum value, and the 
standard deviation of the sky in this image (using NoiseChisel's output, which 
we found at the end of @ref{NoiseChisel optimization for detection}).
+Note that NoiseChisel writes the median sky standard deviation @emph{before} 
interpolation in the @code{MEDSTD} keyword of the @code{SKY_STD} HDU.
+This is more robust than the median of the Sky standard deviation image (which 
has gone through interpolation).
+
+@example
+$ max=$(aststatistics nc/xdf-f160w.fits -hINPUT-NO-SKY --maximum)
+$ skystd=$(astfits nc/xdf-f160w.fits -hSKY_STD --keyvalue=MEDSTD -q)
+
+$ echo $max $skystd
+58.8292 0.000410282
+
+$ echo $max $skystd | awk '@{print $1/$2@}'
+143387
+@end example
+
+@noindent
+In the last command above, we divided the maximum by the sky standard 
deviation.
+You see that the maximum value is more than @mymath{140000} times larger than 
the noise level!
+On the other hand common monitors or printers, usually have a maximum dynamic 
range of 8-bits, only allowing for @mymath{2^8=256} layers.
+This is therefore the maximum number of ``layers'' you can have in a common 
display formats like JPEG, PDF or PNG!
+Dividing the result above by 256, we get a layer spacing of
+
+@example
+$ echo $max $skystd | awk '@{print $1/$2/256@}'
+560.106
+@end example
+
+In other words, the first layer (which is black) will contain all the pixel 
values below @mymath{\sim560}!
+So all pixels with a signal-to-noise ratio lower than @mymath{\sim560} will 
have a black color since they fall in the first layer of an 8-bit PDF (or JPEG) 
image.
+This happens because by default we are assuming a linear mapping from floating 
point to 8-bit integers.
+
+@cindex Surface Brightness
+To fix this, we should move to a different mapping.
+A good, physically motivated, mapping is Surface Brightness (which is in 
log-scale, see @ref{Brightness flux magnitude}).
+Fortunately this is very easy to do with Gnuastro's Arithmetic program, as 
shown in the commands below (using the known 
zeropoint@footnote{@url{https://archive.stsci.edu/prepds/xdf/#science-images}}, 
and after calculating the pixel area in units of arcsec@mymath{^2}):
+
+@example
+$ zeropoint=25.94
+$ pixarcsec2=$(astfits nc/xdf-f160w.fits --pixelareaarcsec2)
+$ astarithmetic nc/xdf-f160w.fits $zeropoint $pixarcsec2 counts-to-sb \
+                --output=xdf-f160w-sb.fits
+@end example
+
+@noindent
+With the two commands below, first, let's look at the dynamic range of the 
image now (dividing the maximum by the minimum), and then let's open the image 
and have a look at it:
+
+@example
+$ aststatistics xdf-f160w-sb.fits --minimum --maximum
+$ astscript-fits-view xdf-f160w-sb.fits
+@end example
+
+@noindent
+The good news is that the dynamic range has now decreased to about 2!
+In other words, we can distribute the 256 layers of an 8-bit display over a 
much smaller range of values, and therefore better visualize the data.
+However, there are two important points to consider from the output of the 
first command and a visual inspection of the second.
+@itemize
+@item
+The largest pixel value (faintest surface brightness level) in the image is 
@mymath{\sim43}!
+This is far too low to be realistic, and is just due to noise.
+As discussed in @ref{Measuring the dataset limits}, the @mymath{3\sigma} 
surface brightness limit of this image, over 100 arcsec@mymath{^2} is roughly 
32.66 mag/arcsec@mymath{^2}.
+@item
+You see many NaN pixels in between the galaxies!
+These are due to the fact that the magnitude is defined on a logarithmic scale 
and the logarithm of a negative number is not defined.
+@end itemize
+
+In other words, we should replace all NaN pixels, and pixels with a surface 
brightness value fainter than the image surface brightness limit to this limit.
+With the first command below, we'll first extract the surface brightness limit 
from the catalog headers that we calculated before, and then call Arithmetic to 
use this limit.
+
+@example
+$ sblimit=$(astfits cat/xdf-f160w.fits --keyvalue=SBLMAG -q)
+$ astarithmetic nc/xdf-f160w.fits $zeropoint $pixarcsec2 \
+                counts-to-sb set-sb \
+                sb sb $sblimit gt sb isblank or $sblimit where \
+                --output=xdf-f160w-sb.fits
+@end example
+
+@noindent
+Let's convert this image into a PDF with the command below:
+
+@example
+$ astconvertt xdf-f160w-sb.fits --output=xdf-f160w-sb.pdf
+@end example
+
+It is much better now and we can visualize many features of the FITS file 
(from the central structures of the galaxies and stars, to a little into the 
noise and their low surface brightness features.
+However, the image generally looks a little too gray!
+This is because of that bright star in the bottom half of the image!
+Stars are very sharp!
+So let's manually tell ConvertType to set any pixel with a value less than 
(brighter than) 20 to black (and not use the minimum).
+We do this with the @option{--fluxlow} option:
+
+@example
+$ astconvertt xdf-f160w-sb.fits --output=xdf-f160w-sb.pdf --fluxlow=20
+@end example
+
+We are still missing some of the diffuse flux in this PDF.
+This is because of those negative pixels that were set to NaN.
+To better show these structures, we should warp the image to larger pixels.
+So let's warp it to a pixel grid where the new pixels are @mymath{4\times4} 
larger than the original pixels.
+But be careful that warping should be done on the original image, not on the 
surface brightness image.
+We should recalculate the surface brightness image after the warping is one.
+This is because @mymath{log(a+b)\ne log(a)+log(b)}.
+Recall that surface brightness calculation involves a logarithm, and warping 
involves addition of pixel values.
+
+@example
+$ astwarp nc/xdf-f160w.fits --scale=1/4 --centeroncorner \
+          --output=xdf-f160w-warped.fits
+
+$ pixarcsec2=$(astfits xdf-f160w-warped.fits --pixelareaarcsec2)
+
+$ astarithmetic xdf-f160w-warped.fits $zeropoint $pixarcsec2 \
+                counts-to-sb set-sb \
+                sb sb $sblimit gt sb isblank or $sblimit where \
+                --output=xdf-f160w-sb.fits
+
+$ astconvertt xdf-f160w-sb.fits --output=xdf-f160w-sb.pdf --fluxlow=20
+@end example
+
+Above, we needed to re-calculate the pixel area of the warpped image, but we 
didn't need to re-calculate the surface brightness limit!
+The reason is that the surface brightness limit is independent of the pixel 
area (in its derivation, the pixel area has been accounted for).
+As a side-effect of the warping, the number of pixels in the image also 
dramatically decreased, therefore the volumn of the output PDF (in bytes) is 
also smaller, making your paper/report easier to upload/download or send by 
email.
+This visual resolution is still more than enough for including on top of a 
column in your paper!
+
+@cartouche
+@noindent
+@strong{I don't have the zeropoint of my image:} The absolute value of the 
zeropoint is irrelevant for the finally produced PDF.
+We used it here because it was available and makes the numbers physically 
understandable.
+If you don't have the zeropoint, just set it to zero (which is also the 
default zeropoint used by MakeCatalog when it estimates the surface brightness 
limit).
+For the value to @option{--fluxlow} above, you can simply subtract 
@mymath{\sim10} from the surface brightness limit.
+@end cartouche
+
+@noindent
+To summarize, and to keep the image for the next section in a separate 
directory, here are the necessary commands:
+
+@example
+$ zeropoint=25.94
+$ mkdir report-image
+$ sblimit=$(astfits cat/xdf-f160w.fits --keyvalue=SBLMAG -q)
+$ astwarp nc/xdf-f160w.fits --scale=1/4 --centeroncorner \
+          --output=report-image/warped.fits
+$ pixarcsec2=$(astfits report-image/warped.fits --pixelareaarcsec2)
+$ astarithmetic report-image/warped.fits $zeropoint $pixarcsec2 \
+                counts-to-sb set-sb \
+                sb sb $sblimit gt sb isblank or $sblimit where \
+                --output=report-image/sb.fits
+$ astconvertt report-image/sb.fits --output=report-image/sb.pdf \
+              --fluxlow=20
+@end example
+
+@noindent
+Finally, let's remove all the temporary files we built in the top-level 
tutorial directory:
+
+@example
+$ rm *.fits *.pdf
+@end example
+
+@node Marking objects for publication, Writing scripts to automate the steps, 
FITS images in a publication, General program usage tutorial
+@subsection Marking objects for publication
+
+In @ref{FITS images in a publication} we created a ready-to-print 
visualization of the FITS image used in this tutorial.
+However, you rarely want to show a naked image like that!
+You usually want to highlight some objects (that are the target of your 
science) over the image and show different marks for the various types of 
objects you are studying.
+In this tutorial, we'll do just that: select a sub-set of the full catalog of 
clumps, and show them with different marks shapes and colors, while also adding 
some text under each mark.
+To add coordiantes on the edges of the figure in your paper, see 
@ref{Annotations for figure in paper}.
+
+To start with, let's put a red plus sign over the sub-sample of reddest clumps 
similar to @ref{Reddest clumps cutouts and parallelization}.
+First, we'll need to make the table of marks.
+We'll choose those with a color stronger than 1.5 magnitudes and a 
signal-to-noise ratio (in F160W) larger than 5.
+We also only need the RA, Dec, color and magnitude (in F160W) columns.
+
+@example
+$ asttable cat/mags-with-color.fits --range=F105W-F160W,1.5:inf \
+           --range=sn-f160w,5:inf -cRA,DEC,MAG-F160w,F105W-F160W \
+           -oreport-image/reddest-cat.fits
+@end example
+
+@noindent
+To keep the rest of the code easier to read, let's move to the 
@file{report-image} directory:
+
+@example
+$ cd report-image
+@end example
+
+Gnuastro's ConvertType program also has features to add marks over the finally 
produced PDF.
+Below, we'll start with the same @command{astconvertt} command of the previous 
section.
+The positions of the marks should be given as a table to the @option{--marks} 
option.
+Two other options are also mandatory: @option{--markcoords} identifies the 
columns that contain the coordinates of each mark and @option{--mode} specifies 
if the coordinates are in image or WCS coordinates.
+
+@example
+$ astconvertt sb.fits --output=reddest.pdf --fluxlow=20 \
+              --marks=reddest-cat.fits --mode=wcs \
+              --markcoords=RA,DEC
+@end example
+
+Open the output @file{reddest.pdf} and see the result.
+You will see relatively thick red circles placed over the given coordinates.
+In your PDF browser, zoom-in to one of the regions, you will see that while 
the pixels of the background image become larger, the lines of these regions 
don't degrade!
+This is the concept/power of Vector Graphics: ideal for publication!
+For more on raster (pixelated) and vector (infinite-resolution) graphics, see 
@ref{Raster and Vector graphics}.
+
+We had planned to put a plus-sign on each object.
+However, because we didn't explicitly ask for a certain shape, ConvertType put 
a circle.
+Each mark can have its own separate shape.
+Shapes can be given by a name or a code.
+The full list of available shapes names and codes is given in the description 
of @option{--markshape} option of @ref{Drawing with vector graphics}.
+
+To use a different shape, we need to add a new column to the base table, 
containing the identifier of the desired shape for each mark.
+For example, the code for the plus sign is @code{2}.
+With the commands below, we'll add a new column with this fixed value.
+With the first AWK command we'll make a single-column file, where all the rows 
have the same value.
+We pipe our base table into AWK, so it has the same number of rows.
+With the second command, we concatenate (or append) the new column with Table, 
and give this new column the name @code{SHAPE} (to easily refer to it later and 
not have to count).
+With the third command, we clean-up behind our selves (deleting the extra 
@file{params.txt} file).
+Finally, we use the @option{--markshape} option to tell ConvertType which 
column to use for the shape identifier.
+
+@example
+$ asttable reddest-cat.fits | awk '@{print 2@}' > params.txt
+
+$ asttable reddest-cat.fits --catcolumnfile=params.txt \
+           --colmetadata=5,SHAPE,id,"Shape of mark" \
+           --output=reddest-marks.fits
+$ rm params.txt
+
+$ astconvertt sb.fits --output=reddest.pdf --fluxlow=20 \
+              --marks=reddest-marks.fits --mode=wcs \
+              --markcoords=RA,DEC --markshape=SHAPE
+@end example
+
+Open the PDF and have a look!
+You do see red signs over the coordinates, but the thick plus-signs only 
become visible after you zoom-in multiple times!
+To make them larger, you can give another column to specify the size of each 
mark.
+Let's set the full width of the plus sign to extend 3 arcseconds.
+The commands are similar to above, try to follow the difference (in 
particular, how we use @option{--sizeinarcsec}).
+
+@example
+$ asttable reddest-cat.fits | awk '@{print 2, 3@}' > params.txt
+
+$ asttable reddest-cat.fits --catcolumnfile=params.txt \
+           --colmetadata=5,SHAPE,id,"Shape of mark" \
+           --colmetadata=6,SIZE,arcsec,"Size in arcseconds" \
+           --output=reddest-marks.fits
+$ rm params.txt
+
+$ astconvertt sb.fits --output=reddest.pdf --fluxlow=20 \
+              --marks=reddest-marks.fits --mode=wcs \
+              --markcoords=RA,DEC --markshape=SHAPE \
+              --marksize=SIZE --sizeinarcsec
+@end example
+
+The power of this methodology is that each mark can be completely different!
+For example, let's show the objects with a color less than 2 magnitudes with a 
circle, and those with a stronger color with a plus (recall that the code for a 
circle was @code{1} and that of a plus was @code{2}).
+You only need to replace the first command above with the one below.
+After wards, run the rest of the commands in the last code-block.
+
+@example
+$ asttable reddest-cat.fits -cF105W-F160W \
+           | awk '@{if($1<2) shape=1; else shape=2; print shape, 3@}' \
+           > params.txt
+@end example
+
+Have a look at the resulting @file{reddest.pdf}.
+You see that the circles are much larger than the plus signs.
+This is because the ``size'' of a cross is defined to be its ful width, but 
for a circle, the value in the size column is the radius.
+The way each shape interprets the value of the size column is fully described 
under @option{--markshape} of @ref{Drawing with vector graphics}.
+To make them more comparable, let's set the circle sizes to be half of the 
cross sizes.
+
+@example
+$ asttable reddest-cat.fits -cF105W-F160W \
+           | awk '@{if($1<2) @{shape=1; size=1.5@} \
+                   else     @{shape=2; size=3@} \
+                   print shape, size@}' \
+           > params.txt
+@end example
+
+Let's make things a little more complex (and show more information in the 
visualization) by using color.
+Gnuastro recognizes the full 
@url{https://en.wikipedia.org/wiki/Web_colors#Extended_colors, extended web 
colors}, for their full list (containing names and codes) see @ref{Vector 
graphics colors}.
+But like everything else, an even easier way to view and select the color for 
your figure is on the command-line!
+If your terminal supports 24-bit true-color, you can see all the colors by 
running this command (supported on modern GNU/Linux distributions):
+
+@example
+$ astconvertt --listcolors
+@end example
+
+we'll give a ``Sienna'' color for the objects that are fainter than 29th 
magnitude and a ``deeppink'' color to the brighter ones (while keeping the same 
shapes definition as before)
+Since there are many colors, using their codes can make the table hard to read 
by a human!
+So let's use the color names instead of the color codes in the example below 
(this is useful in other columns require strings-only, like the font name).
+
+The only intricacy is in the making of @file{params.txt}.
+Recall that string columns need column metadata (@ref{Gnuastro text table 
format}).
+In this particular case, since the string column is the last one, we can 
safely use AWK's @code{print} command.
+But if you have multiple string columns, to be safe it is better to use AWK's 
@code{printf} and explicitly specify the number of characters in the string 
columns.
 
 @example
-$ awk 'BEGIN@{print "# Region file format: DS9 version 4.1";      \
-             print "global color=green width=2";                 \
-             print "fk5";@}                                       \
-       !/^#/@{printf "circle(%s,%s,1\") # text=@{%s@}\n",$2,$3,$1;@}'\
-      reddest.txt > reddest.reg
+$ asttable reddest-cat.fits -cF105W-F160W,MAG-F160W \
+           | awk 'BEGIN@{print "# Column 3: COLOR [name, str8]"@}\
+                  @{if($1<2)  @{shape=1; size=1.5@} \
+                   else      @{shape=2; size=3@} \
+                   if($2>29) @{color="sienna"@} \
+                   else      @{color="deeppink"@} \
+                   print shape, size, color@}' \
+           > params.txt
+
+$ asttable reddest-cat.fits --catcolumnfile=params.txt \
+           --colmetadata=5,SHAPE,id,"Shape of mark" \
+           --colmetadata=6,SIZE,arcsec,"Size in arcseconds" \
+           --output=reddest-marks.fits
+$ rm params.txt
+
+$ astconvertt sb.fits --output=reddest.pdf --fluxlow=20 \
+              --marks=reddest-marks.fits --mode=wcs \
+              --markcoords=RA,DEC --markshape=SHAPE \
+              --marksize=SIZE --sizeinarcsec --markcolor=COLOR
 @end example
 
-This region file can be loaded into DS9 with its @option{-regions} option to 
display over any image (that has world coordinate system).
-In the example below, we'll open Segment's output and load the regions over 
all the extensions (to see the image and the respective clump):
+As one final example, let's write the magnitude of each object under it.
+Since the magnitude is already in the @file{marks.fits} that we produced 
above, adding it is very easy (just add @option{--marktext} option to 
ConvertType):
+
+@example
+$ astconvertt sb.fits --output=reddest.pdf --fluxlow=20 \
+              --marks=reddest-marks.fits --mode=wcs \
+              --markcoords=RA,DEC --markshape=SHAPE \
+              --marksize=SIZE --sizeinarcsec \
+              --markcolor=COLOR --marktext=MAG-F160W
+@end example
+
+Open the final PDF (@file{reddest.pdf}) and you will see the magnitudes 
written under each mark in the same color.
+In the case of magnitudes (where the magnitude error is usually much larger 
than 0.01 magnitudes, four decimals is not too meaningful.
+By default, for printing floating point columns, we use the compiler's default 
precision (which is about 4 digits for 32-bit floating point numbers).
+But you can over-write this (to only show two digits after the decimal point) 
with the @option{--marktextprecision=2} option.
+
+You can customize the written text by specifying a different line-width (for 
the text, different from the main mark), or even specifying a different font 
for each mark!
+You can see the full list of available fonts for the text under a mark with 
the first command below and with the second, you can actually see them in a 
custom PDF (to only show the fonts).
 
 @example
-$ ds9 -mecube seg/xdf-f160w.fits -zscale -zoom to fit    \
-      -regions load all reddest.reg
+$ astconvertt --listfonts
+$ astconvertt --showfonts
 @end example
 
-@node Writing scripts to automate the steps, Citing and acknowledging 
Gnuastro, Finding reddest clumps and visual inspection, General program usage 
tutorial
+As you see, there are many ways you can customize each mark!
+The above examples were just the tip of the iceburg!
+But this section has already become long so we'll stop it here (see the box at 
the end of this section for yet another useful example).
+Like above, each feature of a mark can be controlled with a column in the 
table of mark information.
+Please see in @ref{Drawing with vector graphics} for the full list of 
columns/features that you can use.
+
+@cartouche
+@noindent
+@strong{Drawing ellipses:} With the commands below, you can measure the 
elliptical properties of the objects and visualized them in a ready-to-publish 
PDF (we'll only show the ellipses of the largest clumps):
+@example
+$ astmkcatalog ../seg/xdf-f160w.fits --ra --dec --semimajor \
+               --axisratio --positionangle --clumpscat \
+               --output=ellipseinfo.fits
+$ asttable ellipseinfo.fits -hCLUMPS | awk '@{print 4@}' > params.txt
+$ asttable ellipseinfo.fits -hCLUMPS --catcolumnfile=params.txt \
+           --range=SEMI_MAJOR,10,inf -oellipse-marks.fits \
+           --colmetadata=6,SHAPE,id,"Shape of mark"
+$ astconvertt sb.fits --output=ellipse.pdf --fluxlow=20 \
+              --marks=ellipse-marks.fits --mode=wcs \
+              --markcoords=RA,DEC --markshape=SHAPE \
+              --marksize=SEMI_MAJOR,AXIS_RATIO --sizeinpix \
+              --markrotate=POSITION_ANGLE
+@end example
+@end cartouche
+
+@cindex Point (Vector graphics; PostScript)
+To conclude this section, let us highlight an important factor to consider in 
vector graphics.
+In ConvertType, things like line width or font size are defined in units of 
``point''s.
+In vector graphics standards, 72 ``point''s correspond to one 1 inch.
+Therefore, one way you can change these factors for all the objects is to 
assign a larger or smaller print size to the image.
+The print size is just a meta-data entry, and won't affect the file's volume 
in bytes!
+You can do this with the @option{--widthincm} option.
+Try adding this option and giving it very different values like @code{5} or 
@code{30}.
+
+@node Writing scripts to automate the steps, Citing and acknowledging 
Gnuastro, Marking objects for publication, General program usage tutorial
 @subsection Writing scripts to automate the steps
 
-In the previous sub-sections, we went through a series of steps like 
downloading the necessary datasets (in @ref{Setup and data download}), 
detecting the objects in the image, and finally selecting a particular subset 
of them to inspect visually (in @ref{Finding reddest clumps and visual 
inspection}).
+In the previous sub-sections, we went through a series of steps like 
downloading the necessary datasets (in @ref{Setup and data download}), 
detecting the objects in the image, and finally selecting a particular subset 
of them to inspect visually (in @ref{Reddest clumps cutouts and 
parallelization}).
 To benefit most effectively from this subsection, please go through the 
previous sub-sections, and if you haven't actually done them, we recommended to 
do/run them before continuing here.
 
 @cindex @command{history}
@@ -5852,7 +5824,7 @@ See @ref{Segmentation and making a catalog} and 
@ref{Segment} for more on using
 
 
 
-@node Building the extended PSF,  , Detecting large extended targets, Tutorials
+@node Building the extended PSF, Sufi simulates a detection, Detecting large 
extended targets, Tutorials
 @section Building the extended PSF
 
 Deriving the extended PSF of an image is very important in many aspects of the 
analysis of the objects within it.
@@ -6659,322 +6631,894 @@ $ astscript-psf-unite outer/stack.fits \
 @end example
 
 @noindent
-Let's have a look at the outer stack and the final PSF with the command below.
-Since we want several other DS9 settings to help you directly see the main 
point, we are using @option{--ds9extra}.
-After DS9 is opened, you can see that the center of the PSF has now been 
nicely filled.
-You can click on the ``Edit'' button and then the ``Colorbar'' and hold your 
cursor over the image and move it.
-You can see that besides filling the inner regions nicely, there is also no 
major discontinuity in the 2D image around the union radius of 12 pixels around 
the center.
+Let's have a look at the outer stack and the final PSF with the command below.
+Since we want several other DS9 settings to help you directly see the main 
point, we are using @option{--ds9extra}.
+After DS9 is opened, you can see that the center of the PSF has now been 
nicely filled.
+You can click on the ``Edit'' button and then the ``Colorbar'' and hold your 
cursor over the image and move it.
+You can see that besides filling the inner regions nicely, there is also no 
major discontinuity in the 2D image around the union radius of 12 pixels around 
the center.
+
+@example
+$ astscript-fits-view outer/stack.fits psf.fits --ds9scale=minmax \
+           --ds9extra="-scale limits 0 22000 -match scale" \
+           --ds9extra="-lock scale yes -zoom 4 -scale log"
+@end example
+
+Nothing demonstrates the effect of a bad analysis than actually seeing a bad 
result!
+So let's choose a bad normalization radial range (50 to 60 pixels) and unite 
the inner and outer parts based on that.
+The last command will open the two PSFs together in DS9, you should be able to 
immediately see the discontinuity in the union radius.
+
+@example
+$ scale=$(astscript-psf-scale-factor outer/stack.fits \
+                   --psf=inner/stack.fits --center=501,501 \
+                   --mode=img --normradii=50,60 --quiet)
+
+$ astscript-psf-unite outer/stack.fits \
+           --inner=inner/stack.fits --radius=55 \
+           --scale=$scale --output=psf-bad.fits
+
+$ astscript-fits-view psf-bad.fits psf.fits --ds9scale=minmax \
+           --ds9extra="-scale limits 0 50 -match scale" \
+           --ds9extra="-lock scale yes -zoom 4 -scale log"
+@end example
+
+As you see, the selection of the normalization radii and the unite radius are 
very important.
+The first time you are trying to build the PSF of a new dataset, it has to be 
explored with a visual inspection of the images and radial profiles.
+Once you have found a good normalization radius for a certain part of the PSF 
in a survey, you can generally use it comfortably without change.
+But for a new survey, or a different part of the PSF, be sure to repeat the 
visual checks above to choose the best radii.
+As a summary, a good junction radius is one that:
+
+@itemize
+@item
+Is large enough to not let saturation and non-linearity (from the outer 
profile) into the inner region.
+@item
+Is small enough to have a sufficiently high signal to noise ratio (from the 
inner profile) to avoid adding noise in the union radius.
+@end itemize
+
+Now that the complete PSF has been obtained, let's remove that bad-looking 
PSF, and stick with the nice and clean PSF for the next step in 
@ref{Subtracting the PSF}.
+
+@example
+$ rm -rf psf-bad.fits
+@end example
+
+
+
+
+
+
+
+
+
+
+@node Subtracting the PSF,  , Uniting the different PSF components, Building 
the extended PSF
+@subsection Subtracting the PSF
+Previously (in @ref{Uniting the different PSF components}) we constructed a 
full PSF, from the central pixel to a radius of 500 pixels.
+Now, let's use the PSF to subtract the scattered light from each individual 
star in the image.
+
+By construction, the pixel values of the PSF came from the normalization of 
the individual stamps (that were created for stars of different magnitudes).
+As a consequence, it is necessary to compute a scale factor to fit that PSF 
image to each star.
+This is done with the same @file{astscript-psf-scale-factor} command that we 
used previously in @ref{Uniting the different PSF components}.
+The difference is that now we are not aiming to join two different PSF parts 
but looking for the necessary scale factor to match the star with the PSF.
+Afterwards, we will use @file{astscript-psf-subtract} for placing the PSF 
image at the desired coordinates within the same pixel grid as the image.
+Finally, once the stars have been modeled by the PSF, we will subtract it.
+
+First, let's start with a single star.
+Later, when the basic idea has been explained, we will generalize the method 
for any number of stars.
+With the following command we obtain the coordinates (RA and DEC) and 
magnitude of the brightest star in the image (which is on the top edge of the 
image):
+
+@example
+$ mkdir single-star
+$ center=$(asttable flat/67510-bright.fits --sort phot_g_mean_mag \
+                    --column=ra,dec --head 1 \
+                    | awk '@{printf "%s,%s", $1, $2@}')
+$ echo $center
+@end example
+
+With the center position of that star, let's obtain the flux factor using the 
same normalization ring we used for the creation of the outer part of the PSF:
+
+@example
+$ scale=$(astscript-psf-scale-factor label/67510-seg.fits \
+                   --mode=wcs --quiet \
+                   --psf=psf.fits \
+                   --center=$center \
+                   --normradii=10,15 \
+                   --segment=label/67510-seg.fits)
+@end example
+
+Now we have all the information necessary to model the star using the PSF: the 
position on the sky and the flux factor.
+Let's use this data with the script @file{astscript-psf-subtract} for modeling 
this star and have a look with DS9.
+
+@example
+$ astscript-psf-subtract label/67510-seg.fits \
+           --mode=wcs \
+           --psf=psf.fits \
+           --scale=$scale \
+           --center=$center \
+           --output=single-star/subtracted.fits
+
+$ astscript-fits-view label/67510-seg.fits single-star/subtracted.fits \
+           --ds9center=$center --ds9mode=wcs --ds9extra="-zoom 4"
+@end example
+
+You will notice that there is something wrong with this ``subtraction''!
+The box of the extended PSF is clearly visible!
+The sky noise under the box is clearly larger than the rest of the noise in 
the image.
+Before reading on, please try to think about the cause of this yourself.
+
+To understand the cause, let's look at the scale factor, the number of stamps 
used to build the outer part (and its square root):
+
+@example
+$ echo $scale
+$ ls outer/stamps/*.fits | wc -l
+$ ls outer/stamps/*.fits | wc -l | awk '@{print sqrt($1)@}'
+@end example
+
+You see that the scale is almost 19!
+As a result, the PSF has been multiplied by 19 before being subtracted.
+However, the outer part of the PSF was created with only a handful of star 
stamps.
+When you stack @mymath{N} images, the stack's signal-to-noise ratio (S/N) 
improves by @mymath{\sqrt{N}}.
+We had 8 images for the outer part, so the S/N has only improved by a factor 
of just under 3!
+When we multiply the final stacked PSF with 19, we are also scaling up the 
noise by that same factor (most importantly: in the outer most regions where 
there is almost no signal).
+So the stacked image's noise-level is @mymath{19/3=6.3} times larger than the 
noise of the input image.
+This terrible noise-level is what you clearly see as the footprint of the PSF.
+
+To confirm this, let's use the commands below to subtract the faintest of the 
bright-stars catalog (note the use of @option{--tail} when finding the central 
position).
+You will notice that the scale factor (@mymath{\sim1.3}) is now smaller than 3.
+So when we multiply the PSF with this factor, the PSF's noise level is lower 
than our input image and we shouldn't see any footprint like before.
+Note also that we are using a larger zoom factor, because this star is smaller 
in the image.
+
+@example
+$ center=$(asttable flat/67510-bright.fits --sort phot_g_mean_mag \
+                    --column=ra,dec --tail 1 \
+                    | awk '@{printf "%s,%s", $1, $2@}')
+
+$ scale=$(astscript-psf-scale-factor label/67510-seg.fits \
+                   --mode=wcs --quiet \
+                   --psf=psf.fits \
+                   --center=$center \
+                   --normradii=10,15 \
+                   --segment=label/67510-seg.fits)
+$ echo $scale
+
+$ astscript-psf-subtract label/67510-seg.fits \
+           --mode=wcs \
+           --psf=psf.fits \
+           --scale=$scale \
+           --center=$center \
+           --output=single-star/subtracted.fits
+
+$ astscript-fits-view label/67510-seg.fits single-star/subtracted.fits \
+           --ds9center=$center --ds9mode=wcs --ds9extra="-zoom 10"
+@end example
+
+In a large survey like J-PLUS, its easy to use more and more bright stars from 
different pointings (ideally with similar FWHM and similar telescope 
properties@footnote{For example in J-PLUS, the baffle of the secondary mirror 
was adjusted in 2017 because it produced extra spikes in the PSF. So all images 
after that date have a PSF with 4 spikes (like this one), while those before it 
have many more spikes.}) to improve the S/N of the PSF.
+As explained before, we designed the output files of this tutorial with the 
@code{67510} (which is this image's pointing label in J-PLUS) where necessary 
so you see how easy it is to add more pointings to use in the creation of the 
PSF.
+
+Let's consider now more than one single star.
+We should have two things in mind:
+
+@itemize
+@item
+The brightest (subtract-able, see the point below) star should be the first 
star to be subtracted.
+This is because of its extended wings which may affect the scale factor of 
nearby stars.
+So we should sort the catalog by brightness and come down from the brightest.
+@item
+We should only subtract stars where the scale factor is less than the S/N of 
the PSF (in relation to the data).
+@end itemize
+
+Since it can get a little complex, its easier to implement this step as a 
script (that is heavily commented for you to easily understand every step; 
especially if you put it in a good text editor with color-coding!).
+You will notice that script also creates a @file{.log} file, which shows which 
star was subtracted and which one wasn't (this is important, and will be used 
below!).
+
+@example
+#!/bin/bash
+
+# Abort the script on first error.
+set -e
+
+# ID of image to subtract stars from.
+imageid=67510
+
+# Get S/N level of the final PSF in relation to the actual data:
+snlevel=$(ls outer/stamps/*.fits | wc -l | awk '@{print sqrt($1)@}')
+
+# Put a copy of the image we want to subtract the PSF from in the
+# final file (this will be over-written after each subtraction).
+subtracted=subtracted/$imageid.fits
+cp label/$imageid-seg.fits $subtracted
+
+# Name of log-file to keep status of the subtraction of each star.
+logname=subtracted/$imageid.log
+echo "# Column 1: RA   [deg, f64] Right ascension of star." >  $logname
+echo "# Column 2: Dec  [deg, f64] Declination of star."     >> $logname
+echo "# Column 3: Stat [deg, f64] Status (1: subtracted)"   >> $logname
+
+# Go over each item in the bright star catalog:
+asttable flat/67510-bright.fits -cra,dec --sort phot_g_mean_mag  \
+    | while read -r ra dec; do
+
+    # Put a comma between the RA/Dec to pass to options.
+    center=$(echo $ra $dec | awk '@{printf "%s,%s", $1, $2@}')
+
+    # Calculate the scale value
+    scale=$(astscript-psf-scale-factor label/67510-seg.fits \
+                   --mode=wcs --quiet\
+                   --psf=psf.fits \
+                   --center=$center \
+                   --normradii=10,15 \
+                   --segment=label/67510-seg.fits)
+
+    # Subtract this star if the scale factor is less than the S/N
+    # level calculated above.
+    check=$(echo $snlevel $scale \
+                | awk '@{if($1>$2) c="good"; else c="bad"; print c@}')
+    if [ $check = good ]; then
+
+        # A temporary file to subtract this star.
+        subtmp=subtracted/$imageid-tmp.fits
+
+        # Subtract this star from the image where all previous stars
+        # were subtracted.
+        astscript-psf-subtract $subtracted \
+                 --mode=wcs \
+                 --psf=psf.fits \
+                 --scale=$scale \
+                 --center=$center \
+                 --output=$subtmp
+
+        # Rename the temporary subtracted file to the final one:
+        mv $subtmp $subtracted
+
+        # Keep the status for this star.
+        status=1
+    else
+        # Let the user know this star didn't work, and keep the status
+        # for this star.
+        echo "$center: $scale is larger than $snlevel"
+        status=0
+    fi
+
+    # Keep the status in a log file.
+    echo "$ra $dec $status" >> $logname
+done
+@end example
+
+Copy the contents above into a file called @file{subtract-psf-from-cat.sh} and 
run the following commands.
+Just note that in the script above, we assumed the output is written in the 
@file{subtracted/}, directory, so we'll first make that.
+
+@example
+$ mkdir subtracted
+$ chmod +x subtract-psf-from-cat.sh
+$ ./subtract-psf-from-cat.sh
+
+$ astscript-fits-view label/67510-seg.fits subtracted/67510.fits
+@end example
+
+Can you visually find the stars that have been subtracted?
+Its a little hard, isn't it?
+This shows that you done a good job this time (the sky-noise is not 
significantly affected)!
+So let's subtract the actual image from the PSF-subtracted image to see the 
scattered light field of the subtracted stars.
+With the second command below we'll zoom into the brightest subtracted star, 
but of course feel free to zoom-out and inspect the others also.
+
+@example
+$ astarithmetic label/67510-seg.fits subtracted/67510.fits - \
+                --output=scattered-light.fits -g1
+
+$ center=$(asttable subtracted/67510.log --equal=Stat,1 --head=1 \
+                    -cra,dec | awk '@{printf "%s,%s", $1, $2@}')
+
+$ astscript-fits-view label/67510-seg.fits subtracted/67510.fits \
+                      scattered-light.fits \
+                      --ds9center=$center --ds9mode=wcs \
+                      --ds9extra="-scale limits -0.5 1.5 -match scale" \
+                      --ds9extra="-lock scale yes -zoom 10" \
+                      --ds9extra="-tile mode column"
+
+## We can always make it easily, so let's remove this.
+$ rm scattered-light.fits
+@end example
+
+You will probably have noticed that in the scattered light field there are 
some patches that correspond to the saturation of the stars.
+Since we obtained the scattered light field by subtracting PSF-subtracted 
image from the original image, it is natural that we have such saturated 
regions.
+To solve such inconvenience, this script also has an option to not make the 
subtraction of the PSF but to give as output the modeled star.
+For doing that, it is necessary to run the script with the option 
@option{--modelonly}.
+We encourage the reader to obtain such scattered light field model.
+In some scenarios it could be interesting having such way of correcting the 
PSF.
+For example, if there are many faint stars that can be modeled at the same 
time because their flux don't affect each other.
+In such situation, the task could be easily parallelized without having to 
wait to model the brighter stars before the fainter ones.
+At the end, once all stars have been modeled, a simple Arithmetic command 
could be used to sum the different modeled-PSF stamps to obtain the entire 
scattered light field.
+
+In general you see that the subtraction has been done nicely and almost all 
the extended wings of the PSF have been subtracted.
+The central regions of the stars aren't perfectly subtracted:
+
+@itemize
+@item
+Some may get too dark at the center.
+This may be due to the non-linearity of the CCD counting (as discussed 
previously in @ref{Uniting the different PSF components}).
+
+@item
+Others may have a strong gradient: one side is too positive and one side is 
too negative (only in the very central few pixels).
+This is due to the non-accurate positioning: most probably this happens 
because of imperfect astrometry.
+@end itemize
+
+Note also that during this process we assumed that the PSF doesn't vary with 
the CCD position or any other parameter.
+In other words, we are obtaining an averaged PSF model from a few star stamps 
that are naturally different, and this also explains the residuals on each 
subtracted star.
+
+We let as an interesting exercise the modeling and subtraction of other stars, 
for example, the non saturated stars of the image.
+By doing this, you will notice that in the core region the residuals are 
different compared to the residuals of brighter stars that we have obtained.
+
+In general, in this tutorial we have showed how to deal with the most 
important challenges for constructing an extended PSF.
+Each image or dataset will have its own particularities that you will have to 
take into account when constructing the PSF.
+
+
+
+
+
+
+
+
+
+
+@node Sufi simulates a detection,  , Building the extended PSF, Tutorials
+@section Sufi simulates a detection
+
+@cindex Azophi
+@cindex Abd al-rahman Sufi
+@cindex Sufi, Abd al-rahman
+It is the year 953 A.D. and Abd al-rahman Sufi (903 -- 986 A.D.)@footnote{In 
Latin Sufi is known as Azophi.
+He was an Iranian astronomer.
+His manuscript ``Book of fixed stars'' contains the first recorded 
observations of the Andromeda galaxy, the Large Magellanic Cloud and seven 
other non-stellar or `nebulous' objects.}  is in Shiraz as a guest astronomer.
+He had come there to use the advanced 123 centimeter astrolabe for his studies 
on the Ecliptic.
+However, something was bothering him for a long time.
+While mapping the constellations, there were several non-stellar objects that 
he had detected in the sky, one of them was in the Andromeda constellation.
+During a trip he had to Yemen, Sufi had seen another such object in the 
southern skies looking over the Indian ocean.
+He wasn't sure if such cloud-like non-stellar objects (which he was the first 
to call `Sah@={a}bi' in Arabic or `nebulous') were real astronomical objects or 
if they were only the result of some bias in his observations.
+Could such diffuse objects actually be detected at all with his detection 
technique?
+
+@cindex Almagest
+@cindex Claudius Ptolemy
+@cindex Ptolemy, Claudius
+He still had a few hours left until nightfall (when he would continue his 
studies on the ecliptic) so he decided to find an answer to this question.
+He had thoroughly studied Claudius Ptolemy's (90 -- 168 A.D) Almagest and had 
made lots of corrections to it, in particular in measuring the brightness.
+Using his same experience, he was able to measure a magnitude for the objects 
and wanted to simulate his observation to see if a simulated object with the 
same brightness and size could be detected in a simulated noise with the same 
detection technique.
+The general outline of the steps he wants to take are:
+
+@enumerate
+
+@item
+Make some mock profiles in an over-sampled image.
+The initial mock image has to be over-sampled prior to convolution or other 
forms of transformation in the image.
+Through his experiences, Sufi knew that this is because the image of heavenly 
bodies is actually transformed by the atmosphere or other sources outside the 
atmosphere (for example gravitational lenses) prior to being sampled on an 
image.
+Since that transformation occurs on a continuous grid, to best approximate it, 
he should do all the work on a finer pixel grid.
+In the end he can re-sample the result to the initially desired grid size.
+
+@item
+@cindex PSF
+Convolve the image with a point spread function (PSF, see @ref{PSF}) that is 
over-sampled to the same resolution as the mock image.
+Since he wants to finish in a reasonable time and the PSF kernel will be very 
large due to oversampling, he has to use frequency domain convolution which has 
the side effect of dimming the edges of the image.
+So in the first step above he also has to build the image to be larger by at 
least half the width of the PSF convolution kernel on each edge.
+
+@item
+With all the transformations complete, the image should be re-sampled to the 
same size of the pixels in his detector.
+
+@item
+He should remove those extra pixels on all edges to remove frequency domain 
convolution artifacts in the final product.
+
+@item
+He should add noise to the (until now, noise-less) mock image.
+After all, all observations have noise associated with them.
+
+@end enumerate
+
+Fortunately Sufi had heard of GNU Astronomy Utilities from a colleague in 
Isfahan (where he worked) and had installed it on his computer a year before.
+It had tools to do all the steps above.
+He had used MakeProfiles before, but wasn't sure which columns he had chosen 
in his user or system wide configuration files for which parameters, see 
@ref{Configuration files}.
+So to start his simulation, Sufi runs MakeProfiles with the @option{-P} option 
to make sure what columns in a catalog MakeProfiles currently recognizes and 
the output image parameters.
+In particular, Sufi is interested in the recognized columns (shown below).
+
+@example
+$ astmkprof -P
+
+[[[ ... Truncated lines ... ]]]
+
+# Output:
+ type         float32     # Type of output: e.g., int16, float32, etc...
+ mergedsize   1000,1000   # Number of pixels along first FITS axis.
+ oversample   5           # Scale of oversampling (>0 and odd).
+
+[[[ ... Truncated lines ... ]]]
+
+# Columns, by info (see `--searchin'), or number (starting from 1):
+ ccol         2           # Coord. columns (one call for each dim.).
+ ccol         3           # Coord. columns (one call for each dim.).
+ fcol         4           # sersic (1), moffat (2), gaussian (3), point
+                          # (4), flat (5), circumference (6), distance
+                          # (7), custom-prof (8), azimuth (9),
+                          # custom-img (10).
+ rcol         5           # Effective radius or FWHM in pixels.
+ ncol         6           # Sersic index or Moffat beta.
+ pcol         7           # Position angle.
+ qcol         8           # Axis ratio.
+ mcol         9           # Magnitude.
+ tcol         10          # Truncation in units of radius or pixels.
+
+[[[ ... Truncated lines ... ]]]
+
+@end example
+
+@noindent
+In Gnuastro, column counting starts from 1, so the columns are ordered such 
that the first column (number 1) can be an ID he specifies for each object (and 
MakeProfiles ignores), each subsequent column is used for another property of 
the profile.
+It is also possible to use column names for the values of these options and 
change these defaults, but Sufi preferred to stick to the defaults.
+Fortunately MakeProfiles has the capability to also make the PSF which is to 
be used on the mock image and using the @option{--prepforconv} option, he can 
also make the mock image to be larger by the correct amount and all the sources 
to be shifted by the correct amount.
+
+For his initial check he decides to simulate the nebula in the Andromeda 
constellation.
+The night he was observing, the PSF had roughly a FWHM of about 5 pixels, so 
as the first row (profile) in the table below, he defines the PSF parameters.
+Sufi sets the radius column (@code{rcol} above, fifth column) to @code{5.000}, 
he also chooses a Moffat function for its functional form.
+Remembering how diffuse the nebula in the Andromeda constellation was, he 
decides to simulate it with a mock S@'{e}rsic index 1.0 profile.
+He wants the output to be 499 pixels by 499 pixels, so he can put the center 
of the mock profile in the central pixel of the image which is the 250th pixel 
along both dimensions (note that an even number doesn't have a ``central'' 
pixel).
+
+Looking at his drawings of it, he decides a reasonable effective radius for it 
would be 40 pixels on this image pixel scale (second row, 5th column below).
+He also sets the axis ratio (0.4) and position angle (-25 degrees) to 
approximately correct values too, and finally he sets the total magnitude of 
the profile to 3.44 which he had measured.
+Sufi also decides to truncate both the mock profile and PSF at 5 times the 
respective radius parameters.
+In the end he decides to put four stars on the four corners of the image at 
very low magnitudes as a visual scale.
+While he was preparing the catalog, one of his students approached him and was 
also following the steps.
+
+@noindent
+As described above, the catalog of profiles to build will be a table (multiple 
columns of numbers) like below:
+
+@example
+0  0.000   0.000  2  5   4.7  0.0  1.0  30.0  5.0
+1  250.0   250.0  1  40  1.0  -25  0.4  3.44  5.0
+2  50.00   50.00  4  0   0.0  0.0  0.0  6.00  0.0
+3  450.0   50.00  4  0   0.0  0.0  0.0  6.50  0.0
+4  50.00   450.0  4  0   0.0  0.0  0.0  7.00  0.0
+5  450.0   450.0  4  0   0.0  0.0  0.0  7.50  0.0
+@end example
+
+This contains all the ``data'' to build the profile, and you can easily pass 
it to Gnuastro's MakeProfiles: since Sufi already knows the columns and 
expected values very good, he has placed the information in the proper columns.
+However, when the student sees this, he just sees a mumble-jumble of numbers!
+Generally, Sufi explains to the student that even if you know the number 
positions and values very nicely today, in a couple of months you will forget!
+It will then be very hard for you to interpret the numbers properly.
+So you should never use naked data (or data without any extra information).
+
+@cindex Metadata
+Data (or information) that describes other data is called ``metadata''!
+One common example is column names (the name of a column is itself a data 
element, but data that describes the lower-level data within that column: how 
to interpret the numbers within it).
+Sufi explains to his student that Gnuastro has a convention for adding 
metadata within a plain-text file; and guides him to @ref{Gnuastro text table 
format}.
+Because we don't want metadata to be confused with the actual data, in a 
plain-text file, we start lines containing metadata with a `@code{#}'.
+For example, see the same data above, but this time with metadata for every 
column:
+
+@example
+# Column 1:  ID      [counter, u8] Identifier
+# Column 2:  X       [pix,    f32] Horizontal position
+# Column 3:  Y       [pix,    f32] Vertical position
+# Column 4:  PROFILE [name,    u8] Radial profile function
+# Column 5:  R       [pix,    f32] Effective radius
+# Column 6:  N       [n/a,    f32] Sersic index
+# Column 7:  PA      [deg,    f32] Position angle
+# Column 8:  Q       [n/a,    f32] Axis ratio
+# Column 9:  MAG     [log,    f32] Magnitude
+# Column 10: TRUNC   [n/a,    f32] Truncation (multiple of R)
+0  0.000   0.000  2  5   4.7  0.0  1.0  30.0  5.0
+1  250.0   250.0  1  40  1.0  -25  0.4  3.44  5.0
+2  50.00   50.00  4  0   0.0  0.0  0.0  6.00  0.0
+3  450.0   50.00  4  0   0.0  0.0  0.0  6.50  0.0
+4  50.00   450.0  4  0   0.0  0.0  0.0  7.00  0.0
+5  450.0   450.0  4  0   0.0  0.0  0.0  7.50  0.0
+@end example
+
+@noindent
+The numbers now make much more sense to for the student!
+Before continuing, Sufi reminded the student that even though metadata may not 
be strictly/technically necessary (for the computer programs), metadata are 
critical for human readers!
+Therefore, a good scientist should never forget to keep metadata with any data 
that they create, use or archive.
+
+To start simulating the nebula, Sufi creates a directory named 
@file{simulationtest} in his home directory.
+Note that the @command{pwd} command will print the ``parent working 
directory'' of the current directory (its a good way to confirm/check your 
current location in the full file system: it always starts from the root, or 
`@code{/}').
+
+@example
+$ mkdir ~/simulationtest
+$ cd ~/simulationtest
+$ pwd
+/home/rahman/simulationtest
+@end example
+
+@cindex Redirection
+@cindex Standard output
+It is possible to use a plain-text editor to manually put the catalog contents 
above into a plain-text file.
+But to easily automate catalog production (in later trials), Sufi decides to 
fill the input catalog with the redirection features of the command-line (or 
shell).
+Sufi's student wasn't familiar with this feature of the shell!
+So Sufi decided to do a fast demo; giving the following explanations while 
running the commands:
+
+Shell redirection allows you to ``re-direct'' the ``standard output'' of a 
program (which is usually printed by the program on the command-line during its 
execution; like the output of @command{pwd} above) into a file.
+For example, let's simply ``echo'' (or print to standard output) the line 
``This is a test.'':
+
+@example
+$ echo "This is a test."
+This is a test.
+@end example
+
+@noindent
+As you see, our statement was simply ``echo''-ed to the standard output!
+To redirect this sentence into a file (instead of simply printing it on the 
standard output), we can simply use the @code{>} character, followed by the 
name of the file we want it to be dumped in.
+
+@example
+$ echo "This is a test." > test.txt
+@end example
+
+This time, the @command{echo} command didn't print anything in the terminal.
+Instead, the shell (command-line environment) took the output, and 
``re-directed'' it into a file called @file{test.txt}.
+Let's confirm this with the @command{ls} command (@command{ls} is short for 
``list'' and will list all the files in the current directory):
+
+@example
+$ ls
+test.txt
+@end example
+
+@noindent
+Now that you confirm the existence of @file{test.txt}, you can see its 
contents with the @command{cat} command (short for ``concatenation''; because 
it can also merge multiple files together):
+
+@example
+$ cat test.txt
+This is a test.
+@end example
+
+@noindent
+Now that we have written our first line in @file{test.txt}, let's try adding a 
second line (don't forget that our final catalog of objects to simulate will 
have multiple lines):
 
 @example
-$ astscript-fits-view outer/stack.fits psf.fits --ds9scale=minmax \
-           --ds9extra="-scale limits 0 22000 -match scale" \
-           --ds9extra="-lock scale yes -zoom 4 -scale log"
+$ echo "This is my second line." > test.txt
+$ cat test.txt
+This is my second line.
 @end example
 
-Nothing demonstrates the effect of a bad analysis than actually seeing a bad 
result!
-So let's choose a bad normalization radial range (50 to 60 pixels) and unite 
the inner and outer parts based on that.
-The last command will open the two PSFs together in DS9, you should be able to 
immediately see the discontinuity in the union radius.
+As you see, the first line that you put in the file is no longer present!
+This happens because `@code{>}' always starts dumping content to a file from 
the start of the file.
+In effect, this means that any possibly pre-existing content is over-written 
by the new content!
+To append new lines (or dumping new content at the end of existing content), 
you can use `@code{>>}'.
+For example with the commands below, first we'll write the first sentence 
(using `@code{>}'), then use `@code{>>}' to add the second and third sentences.
+Finally, we'll print the contents of @file{test.txt} to confirm that all three 
lines are preserved.
 
 @example
-$ scale=$(astscript-psf-scale-factor outer/stack.fits \
-                   --psf=inner/stack.fits --center=501,501 \
-                   --mode=img --normradii=50,60 --quiet)
-
-$ astscript-psf-unite outer/stack.fits \
-           --inner=inner/stack.fits --radius=55 \
-           --scale=$scale --output=psf-bad.fits
-
-$ astscript-fits-view psf-bad.fits psf.fits --ds9scale=minmax \
-           --ds9extra="-scale limits 0 50 -match scale" \
-           --ds9extra="-lock scale yes -zoom 4 -scale log"
+$ echo "My first sentence."   > test.txt
+$ echo "My second sentence." >> test.txt
+$ echo "My third sentence."  >> test.txt
+$ cat test.txt
+My first sentence.
+My second sentence.
+My third sentence.
 @end example
 
-As you see, the selection of the normalization radii and the unite radius are 
very important.
-The first time you are trying to build the PSF of a new dataset, it has to be 
explored with a visual inspection of the images and radial profiles.
-Once you have found a good normalization radius for a certain part of the PSF 
in a survey, you can generally use it comfortably without change.
-But for a new survey, or a different part of the PSF, be sure to repeat the 
visual checks above to choose the best radii.
-As a summary, a good junction radius is one that:
+The student thanked Sufi for this explanation and now feels more comfortable 
with redirection.
+Therefore Sufi continues with the main project.
+But before that, he deletes the temporary test file:
 
-@itemize
-@item
-Is large enough to not let saturation and non-linearity (from the outer 
profile) into the inner region.
-@item
-Is small enough to have a sufficiently high signal to noise ratio (from the 
inner profile) to avoid adding noise in the union radius.
-@end itemize
+@example
+$ rm test.txt
+@end example
 
-Now that the complete PSF has been obtained, let's remove that bad-looking 
PSF, and stick with the nice and clean PSF for the next step in 
@ref{Subtracting the PSF}.
+To put the catalog of profile data and their metadata (that was described 
above) into a file, Sufi uses the commands below.
+While Sufi was writing these commands, the student complained that ``I could 
have done in this in a text editor''.
+Sufi reminded the student that it is indeed possible; but it requires manual 
intervention.
+The advantage of a solution like below is that it can be automated (for 
example adding more rows; for more profiles in the final image).
 
 @example
-$ rm -rf psf-bad.fits
+$ echo "# Column 1:  ID    [counter, u8] Identifier" > cat.txt
+$ echo "# Column 2:  X     [pix,    f32] Horizontal position" >> cat.txt
+$ echo "# Column 3:  Y     [pix,    f32] Vertical position" >> cat.txt
+$ echo "# Column 4:  PROF  [name,    u8] Radial profile function" \
+       >> cat.txt
+$ echo "# Column 5:  R     [pix,    f32] Effective radius" >> cat.txt
+$ echo "# Column 6:  N     [n/a,    f32] Sersic index" >> cat.txt
+$ echo "# Column 7:  PA    [deg,    f32] Position angle" >> cat.txt
+$ echo "# Column 8:  Q     [n/a,    f32] Axis ratio" >> cat.txt
+$ echo "# Column 9:  MAG   [log,    f32] Magnitude" >> cat.txt
+$ echo "# Column 10: TRUNC [n/a,    f32] Truncation (multiple of R)" \
+       >> cat.txt
+$ echo "0  0.000   0.000  2  5   4.7  0.0  1.0  30.0  5.0" >> cat.txt
+$ echo "1  250.0   250.0  1  40  1.0  -25  0.4  3.44  5.0" >> cat.txt
+$ echo "2  50.00   50.00  4  0   0.0  0.0  0.0  6.00  0.0" >> cat.txt
+$ echo "3  450.0   50.00  4  0   0.0  0.0  0.0  6.50  0.0" >> cat.txt
+$ echo "4  50.00   450.0  4  0   0.0  0.0  0.0  7.00  0.0" >> cat.txt
+$ echo "5  450.0   450.0  4  0   0.0  0.0  0.0  7.50  0.0" >> cat.txt
 @end example
 
+@noindent
+To make sure if the catalog's content is correct (and there was no typo for 
example!), Sufi runs `@command{cat cat.txt}', and confirms that it is correct.
 
+@cindex Zero point
+Now that the catalog is created, Sufi is ready to call MakeProfiles to build 
the image containing these objects.
+He looks into his records and finds that the zero point magnitude for that 
night and that detector 18.
+The student was a little confused on the concept of zero point, so Sufi 
pointed him to @ref{Brightness flux magnitude}, which the student can study in 
detail later.
+Sufi therefore runs MakeProfiles with the command below:
 
+@example
+$ astmkprof --prepforconv --mergedsize=499,499 --zeropoint=18.0 cat.txt
+MakeProfiles @value{VERSION} started on Sat Oct  6 16:26:56 953
+  - 6 profiles read from cat.txt
+  - Random number generator (RNG) type: ranlxs1
+  - Basic RNG seed: 1652884540
+  - Using 12 threads.
+  ---- row 3 complete, 5 left to go
+  ---- row 4 complete, 4 left to go
+  ---- row 6 complete, 3 left to go
+  ---- row 5 complete, 2 left to go
+  ---- ./0_cat_profiles.fits created.
+  ---- row 1 complete, 1 left to go
+  ---- row 2 complete, 0 left to go
+  - ./cat_profiles.fits created.                       0.092573 seconds
+  -- Output: ./cat_profiles.fits
+MakeProfiles finished in 0.293644 seconds
+@end example
 
+Sufi encourages the student to read through the printed output.
+As the statements say, two FITS files should have been created in the running 
directory.
+So Sufi ran the command below to confirm:
 
+@example
+$ ls
+0_cat_profiles.fits  cat_profiles.fits  cat.txt
+@end example
 
+@cindex Oversample
+@noindent
+The file @file{0_cat_profiles.fits} is the PSF Sufi had asked for, and 
@file{cat_profiles.fits} is the image containing the main objects in the 
catalog.
+Sufi opened the main image with the command below (using SAO DS9):
 
+@example
+$ astscript-fits-view cat_profiles.fits --ds9scale=95
+@end example
 
+The student could clearly see the main elliptical structure in the center.
+However, the size of @file{cat_profiles.fits} was surprising for the student, 
instead of 499 by 499 (as we had requested), it was 2615 by 2615 pixels (from 
the command below):
 
+@example
+$ astfits cat_profiles.fits
+Fits (GNU Astronomy Utilities) @value{VERSION}
+Run on Sat Oct  6 16:26:58 953
+-----
+HDU (extension) information: 'cat_profiles.fits'.
+ Column 1: Index (counting from 0, usable with '--hdu').
+ Column 2: Name ('EXTNAME' in FITS standard, usable with '--hdu').
+ Column 3: Image data type or 'table' format (ASCII or binary).
+ Column 4: Size of data in HDU.
+-----
+0      MKPROF-CONFIG   no-data         0
+1      Mock profiles   float32         2615x2615
+@end example
 
-@node Subtracting the PSF,  , Uniting the different PSF components, Building 
the extended PSF
-@subsection Subtracting the PSF
-Previously (in @ref{Uniting the different PSF components}) we constructed a 
full PSF, from the central pixel to a radius of 500 pixels.
-Now, let's use the PSF to subtract the scattered light from each individual 
star in the image.
+@noindent
+So Sufi explained why oversampling is important in modeling, especially for 
parts of the image where the flux change is significant over a pixel.
+Recall that when you oversample the model (for example by 5 times), for every 
desired pixel, you get 25 pixels (@mymath{5\times5}).
+Sufi then explained that after convolving (next step below) we will 
down-sample the image to get our originally desired size/resolution.
 
-By construction, the pixel values of the PSF came from the normalization of 
the individual stamps (that were created for stars of different magnitudes).
-As a consequence, it is necessary to compute a scale factor to fit that PSF 
image to each star.
-This is done with the same @file{astscript-psf-scale-factor} command that we 
used previously in @ref{Uniting the different PSF components}.
-The difference is that now we are not aiming to join two different PSF parts 
but looking for the necessary scale factor to match the star with the PSF.
-Afterwards, we will use @file{astscript-psf-subtract} for placing the PSF 
image at the desired coordinates within the same pixel grid as the image.
-Finally, once the stars have been modeled by the PSF, we will subtract it.
+After seeing the image, the student complained that only the large elliptical 
model for the Andromeda nebula can be seen in the center.
+He couldn't see the four stars that we had also requested in the catalog.
+So Sufi had to explain that the stars are there in the image, but the reason 
that they aren't visible when looking at the whole image at once, is that they 
only cover a single pixel!
+To prove it, he centered the image around the coordinates 2308 and 2308, where 
one of the stars is located in the over-sampled image [you can do this in 
@command{ds9} by selecting ``Pan'' in the ``Edit'' menu, then clicking around 
that position].
+Sufi then zoomed in to that region and soon, the star's non-zero pixel could 
be clearly seen.
 
-First, let's start with a single star.
-Later, when the basic idea has been explained, we will generalize the method 
for any number of stars.
-With the following command we obtain the coordinates (RA and DEC) and 
magnitude of the brightest star in the image (which is on the top edge of the 
image):
+Sufi explained that the stars will take the shape of the PSF (cover an area of 
more than one pixel) after convolution.
+If we didn't have an atmosphere and we didn't need an aperture, then stars 
would only cover a single pixel with normal CCD resolutions.
+So Sufi convolved the image with this command:
 
 @example
-$ mkdir single-star
-$ center=$(asttable flat/67510-bright.fits --sort phot_g_mean_mag \
-                    --column=ra,dec --head 1 \
-                    | awk '@{printf "%s,%s", $1, $2@}')
-$ echo $center
+$ astconvolve --kernel=0_cat_profiles.fits cat_profiles.fits \
+              --output=cat_convolved.fits
+Convolve started on Sat Oct  6 16:35:32 953
+  - Using 8 CPU threads.
+  - Input: cat_profiles.fits (hdu: 1)
+  - Kernel: 0_cat_profiles.fits (hdu: 1)
+  - Input and Kernel images padded.                    0.075541 seconds
+  - Images converted to frequency domain.              6.728407 seconds
+  - Multiplied in the frequency domain.                0.040659 seconds
+  - Converted back to the spatial domain.              3.465344 seconds
+  - Padded parts removed.                              0.016767 seconds
+  - Output: cat_convolved.fits
+Convolve finished in:  10.422161 seconds
 @end example
 
-With the center position of that star, let's obtain the flux factor using the 
same normalization ring we used for the creation of the outer part of the PSF:
+@noindent
+When convolution finished, Sufi opened @file{cat_convolved.fits} and the four 
stars could be easily seen now:
 
 @example
-$ scale=$(astscript-psf-scale-factor label/67510-seg.fits \
-                   --mode=wcs --quiet \
-                   --psf=psf.fits \
-                   --center=$center \
-                   --normradii=10,15 \
-                   --segment=label/67510-seg.fits)
+$ astscript-fits-view cat_convolved.fits --ds9scale=95
 @end example
 
-Now we have all the information necessary to model the star using the PSF: the 
position on the sky and the flux factor.
-Let's use this data with the script @file{astscript-psf-subtract} for modeling 
this star and have a look with DS9.
+It was interesting for the student that all the flux in that single pixel is 
now distributed over so many pixels (the sum of all the pixels in each 
convolved star is actually equal to the value of the single pixel before 
convolution).
+Sufi explained how a PSF with a larger FWHM would make the points even wider 
than this (distributing their flux in a larger area).
+With the convolved image ready, they were prepared to re-sample it to the 
original pixel scale Sufi had planned [from the @command{$ astmkprof -P} 
command above, recall that MakeProfiles had over-sampled the image by 5 times].
+Sufi explained the basic concepts of warping the image to his student and ran 
Warp with the following command:
 
 @example
-$ astscript-psf-subtract label/67510-seg.fits \
-           --mode=wcs \
-           --psf=psf.fits \
-           --scale=$scale \
-           --center=$center \
-           --output=single-star/subtracted.fits
+$ astwarp --scale=1/5 --centeroncorner cat_convolved.fits
+Warp started on Sat Oct  6 16:51:59 953
+ Using 8 CPU threads.
+ Input: cat_convolved.fits (hdu: 1)
+ matrix:
+        0.2000   0.0000   0.4000
+        0.0000   0.2000   0.4000
+        0.0000   0.0000   1.0000
 
-$ astscript-fits-view label/67510-seg.fits single-star/subtracted.fits \
-           --ds9center=$center --ds9mode=wcs --ds9extra="-zoom 4"
+$ astfits cat_convolved_scaled.fits --quiet
+0      WARP-CONFIG     no-data         0
+1      Warped          float32         523x523
 @end example
 
-You will notice that there is something wrong with this ``subtraction''!
-The box of the extended PSF is clearly visible!
-The sky noise under the box is clearly larger than the rest of the noise in 
the image.
-Before reading on, please try to think about the cause of this yourself.
-
-To understand the cause, let's look at the scale factor, the number of stamps 
used to build the outer part (and its square root):
+@noindent
+@file{cat_convolved_scaled.fits} now has the correct pixel scale.
+However, the image is still larger than what we had wanted, it is 
@mymath{523\times523} pixels (not our desired @mymath{499\times499}).
+The student is slightly confused, so Sufi also re-samples the PSF with the 
same scale by running
 
 @example
-$ echo $scale
-$ ls outer/stamps/*.fits | wc -l
-$ ls outer/stamps/*.fits | wc -l | awk '@{print sqrt($1)@}'
+$ astwarp --scale=1/5 --centeroncorner 0_cat_profiles.fits
+$ astfits 0_cat_profiles_scaled.fits --quiet
+0      WARP-CONFIG     no-data         0
+1      Warped          float32         25x25
 @end example
 
-You see that the scale is almost 19!
-As a result, the PSF has been multiplied by 19 before being subtracted.
-However, the outer part of the PSF was created with only a handful of star 
stamps.
-When you stack @mymath{N} images, the stack's signal-to-noise ratio (S/N) 
improves by @mymath{\sqrt{N}}.
-We had 8 images for the outer part, so the S/N has only improved by a factor 
of just under 3!
-When we multiply the final stacked PSF with 19, we are also scaling up the 
noise by that same factor (most importantly: in the outer most regions where 
there is almost no signal).
-So the stacked image's noise-level is @mymath{19/3=6.3} times larger than the 
noise of the input image.
-This terrible noise-level is what you clearly see as the footprint of the PSF.
+@noindent
+Sufi notes that @mymath{25=12+12+1} and that @mymath{523=499+12+12}.
+He goes on to explain that frequency space convolution will dim the edges and 
that is why he added the @option{--prepforconv} option to MakeProfiles above.
+Now that convolution is done, Sufi can remove those extra pixels using Crop 
with the command below.
+Crop's @option{--section} option accepts coordinates inclusively and counting 
from 1 (according to the FITS standard), so the crop region's first pixel has 
to be 13, not 12.
 
-To confirm this, let's use the commands below to subtract the faintest of the 
bright-stars catalog (note the use of @option{--tail} when finding the central 
position).
-You will notice that the scale factor (@mymath{\sim1.3}) is now smaller than 3.
-So when we multiply the PSF with this factor, the PSF's noise level is lower 
than our input image and we shouldn't see any footprint like before.
-Note also that we are using a larger zoom factor, because this star is smaller 
in the image.
+@example
+$ astcrop cat_convolved_scaled.fits --section=13:*-12,13:*-12    \
+          --mode=img --zeroisnotblank
+Crop started on Sat Oct  6 17:03:24 953
+  - Read metadata of 1 image.                          0.001304 seconds
+  ---- ...nvolved_scaled_cropped.fits created: 1 input.
+Crop finished in:  0.027204 seconds
+@end example
+
+@noindent
+To fully convince the student, Sufi checks the size of the output of the crop 
command above:
 
 @example
-$ center=$(asttable flat/67510-bright.fits --sort phot_g_mean_mag \
-                    --column=ra,dec --tail 1 \
-                    | awk '@{printf "%s,%s", $1, $2@}')
+$ astfits cat_convolved_scaled_cropped.fits --quiet
+0      n/a             no-data         0
+1      n/a             float32         499x499
+@end example
 
-$ scale=$(astscript-psf-scale-factor label/67510-seg.fits \
-                   --mode=wcs --quiet \
-                   --psf=psf.fits \
-                   --center=$center \
-                   --normradii=10,15 \
-                   --segment=label/67510-seg.fits)
-$ echo $scale
+@noindent
+Finally, @file{cat_convolved_scaled_cropped.fits} is @mymath{499\times499} 
pixels and the mock Andromeda galaxy is centered on the central pixel.
+This is the same dimensions as Sufi had desired in the beginning.
+All this trouble was certainly worth it because now there is no dimming on the 
edges of the image and the profile centers are more accurately sampled.
 
-$ astscript-psf-subtract label/67510-seg.fits \
-           --mode=wcs \
-           --psf=psf.fits \
-           --scale=$scale \
-           --center=$center \
-           --output=single-star/subtracted.fits
+The final step to simulate a real observation would be to add noise to the 
image.
+Sufi set the zero point magnitude to the same value that he set when making 
the mock profiles and looking again at his observation log, he had measured the 
background flux near the nebula had a @emph{per-pixel} magnitude of 7 that 
night.
+For more on how the background value determines the noise, see @ref{Noise 
basics}.
+So using these values he ran MakeNoise, and with the second command, he 
visually inspected the image.
 
-$ astscript-fits-view label/67510-seg.fits single-star/subtracted.fits \
-           --ds9center=$center --ds9mode=wcs --ds9extra="-zoom 10"
-@end example
+@example
+$ astmknoise --zeropoint=18 --background=7 --output=out.fits    \
+             cat_convolved_scaled_cropped.fits
+MakeNoise @value{VERSION} started on Sat Oct  6 17:05:06 953
+  - Generator type: ranlxs1
+  - Generator seed: 1428318100
+MakeNoise finished in:  0.033491 (seconds)
 
-In a large survey like J-PLUS, its easy to use more and more bright stars from 
different pointings (ideally with similar FWHM and similar telescope 
properties@footnote{For example in J-PLUS, the baffle of the secondary mirror 
was adjusted in 2017 because it produced extra spikes in the PSF. So all images 
after that date have a PSF with 4 spikes (like this one), while those before it 
have many more spikes.}) to improve the S/N of the PSF.
-As explained before, we designed the output files of this tutorial with the 
@code{67510} (which is this image's pointing label in J-PLUS) where necessary 
so you see how easy it is to add more pointings to use in the creation of the 
PSF.
+$ astscript-fits-view out.fits
+@end example
 
-Let's consider now more than one single star.
-We should have two things in mind:
+@noindent
+The @file{out.fits} file now contains the noised image of the mock catalog 
Sufi had asked for.
+The student hadn't observed the nebula in the sky, so when he saw the mock 
image in SAO DS9 (with the second command above), he understood why Sufi was 
dubious: it was very diffuse!
 
-@itemize
-@item
-The brightest (subtract-able, see the point below) star should be the first 
star to be subtracted.
-This is because of its extended wings which may affect the scale factor of 
nearby stars.
-So we should sort the catalog by brightness and come down from the brightest.
-@item
-We should only subtract stars where the scale factor is less than the S/N of 
the PSF (in relation to the data).
-@end itemize
+Seeing how the @option{--output} option allows the user to specify the name of 
the output file, the student was confused and wanted to know why Sufi hadn't 
used it more regularly before?
+Sufi explained that for intermediate steps, you can rely on the automatic 
output of the programs (see @ref{Automatic output}).
+Doing so will give all the intermediate files a similar basic name structure, 
so in the end you can simply remove them all with the Shell's capabilities, and 
it will be familiar for other users.
+So Sufi decided to show this to the student by making a shell script from the 
commands he had used before.
 
-Since it can get a little complex, its easier to implement this step as a 
script (that is heavily commented for you to easily understand every step; 
especially if you put it in a good text editor with color-coding!).
-You will notice that script also creates a @file{.log} file, which shows which 
star was subtracted and which one wasn't (this is important, and will be used 
below!).
+The command-line shell has the capability to read all the separate input 
commands from a file.
+This is useful when you want to do the same thing multiple times, with only 
the names of the files or minor parameters changing between the different 
instances.
+Using the shell's history (by pressing the up keyboard key) Sufi reviewed all 
the commands and then he retrieved the last 5 commands with the @command{$ 
history 5} command.
+He selected all those lines he had input and put them in a text file named 
@file{mymock.sh}.
+Then he defined the @code{edge} and @code{base} shell variables for easier 
customization later.
+Finally, before every command, he added some comments (lines starting with 
@key{#}) for future readability.
 
 @example
-#!/bin/bash
+edge=12
+base=cat
 
-# Abort the script on first error.
+# Stop running next commands if one fails.
 set -e
 
-# ID of image to subtract stars from.
-imageid=67510
-
-# Get S/N level of the final PSF in relation to the actual data:
-snlevel=$(ls outer/stamps/*.fits | wc -l | awk '@{print sqrt($1)@}')
+# Remove any (possibly) existing output (from previous runs)
+# before starting.
+rm -f out.fits
 
-# Put a copy of the image we want to subtract the PSF from in the
-# final file (this will be over-written after each subtraction).
-subtracted=subtracted/$imageid.fits
-cp label/$imageid-seg.fits $subtracted
+# Run MakeProfiles to create an oversampled FITS image.
+astmkprof --prepforconv --mergedsize=499,499 --zeropoint=18.0 \
+          "$base".txt
 
-# Name of log-file to keep status of the subtraction of each star.
-logname=subtracted/$imageid.log
-echo "# Column 1: RA   [deg, f64] Right ascension of star." >  $logname
-echo "# Column 2: Dec  [deg, f64] Declination of star."     >> $logname
-echo "# Column 3: Stat [deg, f64] Status (1: subtracted)"   >> $logname
+# Convolve the created image with the kernel.
+astconvolve "$base"_profiles.fits \
+            --kernel=0_"$base"_profiles.fits \
+            --output="$base"_convolved.fits
 
-# Go over each item in the bright star catalog:
-asttable flat/67510-bright.fits -cra,dec --sort phot_g_mean_mag  \
-    | while read -r ra dec; do
+# Scale the image back to the intended resolution.
+astwarp --scale=1/5 --centeroncorner "$base"_convolved.fits
 
-    # Put a comma between the RA/Dec to pass to options.
-    center=$(echo $ra $dec | awk '@{printf "%s,%s", $1, $2@}')
+# Crop the edges out (dimmed during convolution). '--section'
+# accepts inclusive coordinates, so the start of the section
+# must be one pixel larger than its end.
+st_edge=$(( edge + 1 ))
+astcrop "$base"_convolved_scaled.fits --zeroisnotblank \
+        --mode=img --section=$st_edge:*-$edge,$st_edge:*-$edge
 
-    # Calculate the scale value
-    scale=$(astscript-psf-scale-factor label/67510-seg.fits \
-                   --mode=wcs --quiet\
-                   --psf=psf.fits \
-                   --center=$center \
-                   --normradii=10,15 \
-                   --segment=label/67510-seg.fits)
+# Add noise to the image.
+astmknoise --zeropoint=18 --background=7 --output=out.fits \
+           "$base"_convolved_scaled_cropped.fits
 
-    # Subtract this star if the scale factor is less than the S/N
-    # level calculated above.
-    check=$(echo $snlevel $scale \
-                | awk '@{if($1>$2) c="good"; else c="bad"; print c@}')
-    if [ $check = good ]; then
+# Remove all the temporary files.
+rm 0*.fits "$base"*.fits
+@end example
 
-        # A temporary file to subtract this star.
-        subtmp=subtracted/$imageid-tmp.fits
+@cindex Comments
+He used this chance to remind the student of the importance of comments in 
code or shell scripts!
+Just like metadata in a dataset, when writing the code, you have a good mental 
picture of what you are doing, so writing comments might seem superfluous and 
excessive.
+However, in one month when you want to re-use the script, you have lost that 
mental picture and remembering it can be time-consuming and frustrating.
+The importance of comments is further amplified when you want to share the 
script with a friend/colleague.
+So it is good to accompany any step of a script, or code, with useful comments 
while you are writing it (create a good mental picture of why you are doing 
something: don't just describe the command, but its purpose).
 
-        # Subtract this star from the image where all previous stars
-        # were subtracted.
-        astscript-psf-subtract $subtracted \
-                 --mode=wcs \
-                 --psf=psf.fits \
-                 --scale=$scale \
-                 --center=$center \
-                 --output=$subtmp
+@cindex Gedit
+@cindex GNU Emacs
+Sufi then explained to the eager student that you define a variable by giving 
it a name, followed by an @code{=} sign and the value you want.
+Then you can reference that variable from anywhere in the script by calling 
its name with a @code{$} prefix.
+So in the script whenever you see @code{$base}, the value we defined for it 
above is used.
+If you use advanced editors like GNU Emacs or even simpler ones like Gedit 
(part of the GNOME graphical user interface) the variables will become a 
different color which can really help in understanding the script.
+We have put all the @code{$base} variables in double quotation marks 
(@code{"}) so the variable name and the following text do not get mixed, the 
shell is going to ignore the @code{"} after replacing the variable value.
+To make the script executable, Sufi ran the following command:
 
-        # Rename the temporary subtracted file to the final one:
-        mv $subtmp $subtracted
+@example
+$ chmod +x mymock.sh
+@end example
 
-        # Keep the status for this star.
-        status=1
-    else
-        # Let the user know this star didn't work, and keep the status
-        # for this star.
-        echo "$center: $scale is larger than $snlevel"
-        status=0
-    fi
+@noindent
+Then finally, Sufi ran the script, simply by calling its file name:
 
-    # Keep the status in a log file.
-    echo "$ra $dec $status" >> $logname
-done
+@example
+$ ./mymock.sh
 @end example
 
-Copy the contents above into a file called @file{subtract-psf-from-cat.sh} and 
run the following commands.
-Just note that in the script above, we assumed the output is written in the 
@file{subtracted/}, directory, so we'll first make that.
+After the script finished, the only file remaining is the @file{out.fits} file 
that Sufi had wanted in the beginning.
+Sufi then explained to the student how he could run this script anywhere that 
he has a catalog if the script is in the same directory.
+The only thing the student had to modify in the script was the name of the 
catalog (the value of the @code{base} variable in the start of the script) and 
the value to the @code{edge} variable if he changed the PSF size.
+The student was also happy to hear that he won't need to make it executable 
again when he makes changes later, it will remain executable unless he 
explicitly changes the executable flag with @command{chmod}.
+
+The student was really excited, since now, through simple shell scripting, he 
could really speed up his work and run any command in any fashion he likes 
allowing him to be much more creative in his works.
+Until now he was using the graphical user interface which doesn't have such a 
facility and doing repetitive things on it was really frustrating and some 
times he would make mistakes.
+So he left to go and try scripting on his own computer.
+He later reminded Sufi that the second tutorial in the Gnuastro book as more 
complex commands in data analysis, and a more advanced introduction to 
scripting (see @ref{General program usage tutorial}).
+
+Sufi could now get back to his own work and see if the simulated nebula which 
resembled the one in the Andromeda constellation could be detected or not.
+Although it was extremely faint@footnote{The brightness of a diffuse object is 
added over all its pixels to give its final magnitude, see @ref{Brightness flux 
magnitude}.
+So although the magnitude 3.44 (of the mock nebula) is orders of magnitude 
brighter than 6 (of the stars), the central galaxy is much fainter.
+Put another way, the brightness is distributed over a large area in the case 
of a nebula.}.
+Therefore, Sufi ran Gnuastro's detection software (@ref{NoiseChisel}) to see 
if this object is detectable or not.
+NoiseChisel's output (@file{out_detected.fits}) is a multi-extension FITS 
file, so he used Gnuastro's @command{astscript-fits-view} program in the second 
command to see the output:
 
 @example
-$ mkdir subtracted
-$ chmod +x subtract-psf-from-cat.sh
-$ ./subtract-psf-from-cat.sh
+$ astnoisechisel out.fits
 
-$ astscript-fits-view label/67510-seg.fits subtracted/67510.fits
+$ astscript-fits-view out_detected.fits
 @end example
 
-Can you visually find the stars that have been subtracted?
-Its a little hard, isn't it?
-This shows that you done a good job this time (the sky-noise is not 
significantly affected)!
-So let's subtract the actual image from the PSF-subtracted image to see the 
scattered light field of the subtracted stars.
-With the second command below we'll zoom into the brightest subtracted star, 
but of course feel free to zoom-out and inspect the others also.
+In the ``Cube'' window (that was opened with DS9), if Sufi clicked on the 
``Next'' button to see the pixels that were detected to contain significant 
signal.
+Fortunately the nebula's shape was detectable and he could finally confirm 
that the nebula he kept in his notebook was actually observable.
+He wrote this result in the draft manuscript that would later become ``Book of 
fixed stars''@footnote{@url{https://en.wikipedia.org/wiki/Book_of_Fixed_Stars}}.
 
-@example
-$ astarithmetic label/67510-seg.fits subtracted/67510.fits - \
-                --output=scattered-light.fits -g1
+He still had to check the other nebula he saw from Yemen and several other 
such objects, but they could wait until tomorrow (thanks to the shell script, 
he only has to define a new catalog).
+It was nearly sunset and they had to begin preparing for the night's 
measurements on the ecliptic.
 
-$ center=$(asttable subtracted/67510.log --equal=Stat,1 --head=1 \
-                    -cra,dec | awk '@{printf "%s,%s", $1, $2@}')
 
-$ astscript-fits-view label/67510-seg.fits subtracted/67510.fits \
-                      scattered-light.fits \
-                      --ds9center=$center --ds9mode=wcs \
-                      --ds9extra="-scale limits -0.5 1.5 -match scale" \
-                      --ds9extra="-lock scale yes -zoom 10" \
-                      --ds9extra="-tile mode column"
 
-## We can always make it easily, so let's remove this.
-$ rm scattered-light.fits
-@end example
 
-You will probably have noticed that in the scattered light field there are 
some patches that correspond to the saturation of the stars.
-Since we obtained the scattered light field by subtracting PSF-subtracted 
image from the original image, it is natural that we have such saturated 
regions.
-To solve such inconvenience, this script also has an option to not make the 
subtraction of the PSF but to give as output the modeled star.
-For doing that, it is necessary to run the script with the option 
@option{--modelonly}.
-We encourage the reader to obtain such scattered light field model.
-In some scenarios it could be interesting having such way of correcting the 
PSF.
-For example, if there are many faint stars that can be modeled at the same 
time because their flux don't affect each other.
-In such situation, the task could be easily parallelized without having to 
wait to model the brighter stars before the fainter ones.
-At the end, once all stars have been modeled, a simple Arithmetic command 
could be used to sum the different modeled-PSF stamps to obtain the entire 
scattered light field.
 
-In general you see that the subtraction has been done nicely and almost all 
the extended wings of the PSF have been subtracted.
-The central regions of the stars aren't perfectly subtracted:
 
-@itemize
-@item
-Some may get too dark at the center.
-This may be due to the non-linearity of the CCD counting (as discussed 
previously in @ref{Uniting the different PSF components}).
 
-@item
-Others may have a strong gradient: one side is too positive and one side is 
too negative (only in the very central few pixels).
-This is due to the non-accurate positioning: most probably this happens 
because of imperfect astrometry.
-@end itemize
 
-Note also that during this process we assumed that the PSF doesn't vary with 
the CCD position or any other parameter.
-In other words, we are obtaining an averaged PSF model from a few star stamps 
that are naturally different, and this also explains the residuals on each 
subtracted star.
 
-We let as an interesting exercise the modeling and subtraction of other stars, 
for example, the non saturated stars of the image.
-By doing this, you will notice that in the core region the residuals are 
different compared to the residuals of brighter stars that we have obtained.
 
-In general, in this tutorial we have showed how to deal with the most 
important challenges for constructing an extended PSF.
-Each image or dataset will have its own particularities that you will have to 
take into account when constructing the PSF.
 
 @node Installation, Common program behavior, Tutorials, Top
 @chapter Installation
@@ -10445,7 +10989,7 @@ As a summary, for each labeled region (or, galaxy) we 
have one @emph{row} and fo
 This high-level structure is usually the first step for higher-level analysis, 
for example finding the stellar mass or photometric redshift from magnitudes in 
multiple colors.
 Thus, tables are not just outputs of programs, in fact it is much more common 
for tables to be inputs of programs.
 For example, to make a mock galaxy image, you need to feed in the properties 
of each galaxy into @ref{MakeProfiles} for it do the inverse of the process 
above and make a simulated image from a catalog, see @ref{Sufi simulates a 
detection}.
-In other cases, you can feed a table into @ref{Crop} and it will crop out 
regions centered on the positions within the table, see @ref{Finding reddest 
clumps and visual inspection}.
+In other cases, you can feed a table into @ref{Crop} and it will crop out 
regions centered on the positions within the table, see @ref{Reddest clumps 
cutouts and parallelization}.
 So to end this relatively long introduction, tables play a very important role 
in astronomy, or generally all branches of data analysis.
 
 In @ref{Recognized table formats} the currently recognized table formats in 
Gnuastro are discussed.
@@ -11204,6 +11748,7 @@ Without the @option{--quiet} option, the output of 
@option{--pixelscale} has mul
 It prints the file/HDU name, number of dimensions, and the units along with 
the actual pixel scales.
 Also, when any of the units are in degrees, the pixel scales and area/volume 
are also printed in units of arc-seconds.
 For 3D datasets, the pixel area (on each 2D slice of the 3D cube) is printed 
as well as the voxel volume.
+If you only want the pixel area of a 2D image in units of arcsec@mymath{^2} 
you can use @option{--pixelareaarcsec2} described below.
 
 However, in scripts (that are to be run automatically), this human-friendly 
format is annoying, so when called with the @option{--quiet} option, only the 
pixel-scale value(s) along each dimension is(are) printed in one line.
 These numbers are followed by the pixel area (in the raw WCS units).
@@ -11212,6 +11757,11 @@ Finally, for 3D datasets, a final number (the voxel 
volume) is printed.
 As a summary, in @option{--quiet} mode, for 2D datasets three numbers are 
printed and for 3D datasets, 5 numbers are printed.
 If the dataset has more than 3 dimensions, only the pixel-scale values are 
printed (no area or volume will be printed).
 
+@item --pixelareaarcsec2
+Print the HDU's pixel area in units of arcsec@mymath{^2}.
+This option only works on 2D images, that have WCS coordiantes in units of 
degrees.
+For lower-level information about the pixel scale in each dimension, see 
@option{--pixelscale} (described above).
+
 @item --skycoverage
 @cindex Image's sky coverage
 @cindex Coverage of image over sky
@@ -11837,9 +12387,14 @@ The various types will increase with future updates 
and based on need.
 The conversion is not only one way (from FITS to other formats), but two ways 
(except the EPS and PDF formats@footnote{Because EPS and PDF are vector, not 
raster/pixelated formats}).
 So you can also convert a JPEG image or text file into a FITS image.
 Basically, other than EPS/PDF, you can use any of the recognized formats as 
different color channel inputs to get any of the recognized outputs.
-So before explaining the options and arguments (in @ref{Invoking 
astconvertt}), we'll start with a short description of the recognized files 
types in @ref{Recognized file formats}, followed a short introduction to 
digital color in @ref{Color}.
+
+Before explaining the options and arguments (in @ref{Invoking astconvertt}), 
we'll start with a short discussion on the difference between raster and vector 
graphics in @ref{Raster and Vector graphics}.
+In ConvertType, vector graphics are used to add markers over your originally 
rasterized data, producing high quality images, ready to be used in your 
exciting papers.
+We'll continue with a description of the recognized files types in 
@ref{Recognized file formats}, followed a short introduction to digital color 
in @ref{Color}.
+A tutorial on how to add markers over an image is then given in @ref{Marking 
objects for publication} and we conclude with a @LaTeX{} based solution to add 
coordinates over an image.
 
 @menu
+* Raster and Vector graphics::  Images coming from nature, and the abstract.
 * Recognized file formats::     Recognized file formats
 * Color::                       Some explanations on color.
 * Aligning images with small WCS offsets::  When the WCS slightly differs.
@@ -11847,17 +12402,48 @@ So before explaining the options and arguments (in 
@ref{Invoking astconvertt}),
 * Invoking astconvertt::        Options and arguments to ConvertType.
 @end menu
 
-@node Recognized file formats, Color, ConvertType, ConvertType
+@node Raster and Vector graphics, Recognized file formats, ConvertType, 
ConvertType
+@subsection Raster and Vector graphics
+
+@cindex Raster graphics
+@cindex Graphics (raster)
+Images that are produced by a hardware (for example the camera in your phone, 
or the camera connected to a telescope) provide pixelated data.
+Such data are therefore stored in a 
@url{https://en.wikipedia.org/wiki/Raster_graphics, Raster graphics} format 
which has descrete, independent, equally spaced data elements.
+For example this is the format used FITS (see @ref{Fits}), JPEG, TIFF, PNG and 
other image formats.
+
+@cindex Vector graphics
+@cindex Graphics (vector)
+On the other hand, when something is generated by the computer (for example a 
diagram, plot or even adding a cross over a camera image to highlight something 
there), there is no ``observation'' or connection with nature!
+Everything is abstract!
+For such things, it is much easier to draw a mathematical line (with infinite 
resolution).
+Therefore, no matter how much you zoom-in, it will never get pixelated.
+This is the realm of @url{https://en.wikipedia.org/wiki/Vector_graphics, 
Vector graphics}.
+If you open the Gnuastro manual in 
@url{https://www.gnu.org/software/gnuastro/manual/gnuastro.pdf, PDF format} You 
can see such graphics in the Gnuastro manual,
+For example in @ref{Circles and the complex plane} or @ref{Distance on a 2D 
curved space}.
+The most common vector graphics format is PDF for document sharing or SVG for 
web-based applications.
+
+The pixels of a raster image can be shown as vector-based squares with 
different shades, so vector graphics can generally also support raster graphics.
+This is very useful when you want to add some graphics over an image to help 
your discussion (for examle a @mymath{+} over your object of interest).
+However, vector graphics is not optimized for rasterized data (which are 
usually also noisy!), and can ither not display nicely, or result in much 
larger file volumns (in bytes).
+Therefore, if its not necessary to add any marks over a FITS image, for 
example, it may be better to store it in a rasterized format.
+
+The distinction between the vector and raster graphics is also the primary 
theme behind Gnuastro's logo, see @ref{Logo of Gnuastro}.
+
+
+@node Recognized file formats, Color, Raster and Vector graphics, ConvertType
 @subsection Recognized file formats
 
 The various standards and the file name extensions recognized by ConvertType 
are listed below.
-Currently Gnuastro uses the file name's suffix to identify the format.
+For a review on the difference between Raster and Vector graphics, see 
@ref{Raster and Vector graphics}.
+For a review on the concept of color and channels, see @ref{Color}.
+Currently, except for the FITS format, Gnuastro uses the file name's suffix to 
identify the format, so if the file's name doesn't end with one of the suffixes 
mentioned below, it won't be recognized.
 
 @table @asis
 @item FITS or IMH
 @cindex IRAF
 @cindex Astronomical data format
 Astronomical data are commonly stored in the FITS format (or the older data 
IRAF @file{.imh} format), a list of file name suffixes which indicate that the 
file is in this format is given in @ref{Arguments}.
+FITS is a raster graphics format.
 
 Each image extension of a FITS file only has one value per pixel/element.
 Therefore, when used as input, each input FITS image contributes as one color 
channel.
@@ -11883,8 +12469,9 @@ The file name endings that are recognized as a JPEG 
file for input are:
 @item TIFF
 @cindex TIFF format
 TIFF (or Tagged Image File Format) was originally designed as a common format 
for scanners in the early 90s and since then it has grown to become very 
general.
-In many aspects, the TIFF standard is similar to the FITS image standard: it 
can allow data of many types (see @ref{Numeric data types}), and also allows 
multiple images to be stored in a single file (each image in the file is called 
a `directory' in the TIFF standard).
+In many aspects, the TIFF standard is similar to the FITS image standard: it 
can allow data of many types (see @ref{Numeric data types}), and also allows 
multiple images to be stored in a single file (like a FITS extension: each 
image in the file is called a `directory' in the TIFF standard).
 However, unlike FITS, it can only store images, it has no constructs for 
tables.
+Also unlike FITS, each `directory' of a TIFF file can have a multi-channel 
(e.g., RGB) image.
 Another (inconvenient) difference with the FITS standard is that keyword names 
are stored as numbers, not human-readable text.
 
 However, outside of astronomy, because of its support of different numeric
@@ -11900,30 +12487,19 @@ writing TIFF images, please get in touch with us.
 @cindex Vector graphics
 @cindex Encapsulated PostScript
 The Encapsulated PostScript (EPS) format is essentially a one page PostScript 
file which has a specified size.
-PostScript also includes non-image data, for example lines and texts.
+Postscript is used to store a full document like this whole Gnuastro book.
+PostScript therefore also includes non-image data, for example lines and texts.
 It is a fully functional programming language to describe a document.
+A PostScript file is a plain text file that can be edited like any program 
source with any plain-text editor.
 Therefore in ConvertType, EPS is only an output format and cannot be used as 
input.
 Contrary to the FITS or JPEG formats, PostScript is not a raster format, but 
is categorized as vector graphics.
 
-@cindex PDF
-@cindex Adobe systems
-@cindex PostScript vs. PDF
-@cindex Compiled PostScript
-@cindex Portable Document format
-@cindex Static document description format
-The Portable Document Format (PDF) is currently the most common format for 
documents.
-Some believe that PDF has replaced PostScript and that PostScript is now 
obsolete.
-This view is wrong, a PostScript file is an actual plain text file that can be 
edited like any program source with any text editor.
-To be able to display its programmed content or print, it needs to pass 
through a processor or compiler.
-A PDF file can be thought of as the processed output of the compiler on an 
input PostScript file.
-PostScript, EPS and PDF were created and are registered by Adobe Systems.
-
 @cindex @TeX{}
 @cindex @LaTeX{}
 With these features in mind, you can see that when you are compiling a 
document with @TeX{} or @LaTeX{}, using an EPS file is much more low level than 
a JPEG and thus you have much greater control and therefore quality.
 Since it also includes vector graphic lines we also use such lines to make a 
thin border around the image to make its appearance in the document much better.
+Furthermore, through EPS, you can add marks over the image in many shapes and 
colors.
 No matter the resolution of the display or printer, these lines will always be 
clear and not pixelated.
-In the future, addition of text might be included (for example labels or 
object IDs) on the EPS output.
 However, this can be done better with tools within @TeX{} or @LaTeX{} such as 
PGF/Tikz@footnote{@url{http://sourceforge.net/projects/pgf/}}.
 
 @cindex Binary image
@@ -11938,12 +12514,26 @@ The standard formats for an EPS file are @file{.eps}, 
@file{.EPS}, @file{.epsf}
 The EPS outputs of ConvertType have the @file{.eps} suffix.
 
 @item PDF
+@cindex PDF
+@cindex Adobe systems
+@cindex PostScript vs. PDF
+@cindex Compiled PostScript
+@cindex Portable Document format
+@cindex Static document description format
+The Portable Document Format (PDF) is currently the most common format for 
documents.
+It is a vector graphics format, allowing abstract constructs like marks or 
borders.
+
+The PDF format is based on Postscript, so it shares all the features mentioned 
above for EPS.
+To be able to display its programmed content or print, a Postscript file needs 
to pass through a processor or compiler.
+A PDF file can be thought of as the processed output of the PostScript 
compiler.
+PostScript, EPS and PDF were created and are registered by Adobe Systems.
+
 @cindex Suffixes, PDF format
 @cindex GPL Ghostscript
-As explained above, a PDF document is a static document description format, 
viewing its result is therefore much faster and more efficient than PostScript.
-To create a PDF output, ConvertType will make a PostScript page description 
and convert that to PDF using GPL Ghostscript.
+As explained under EPS above, a PDF document is a static document description 
format, viewing its result is therefore much faster and more efficient than 
PostScript.
+To create a PDF output, ConvertType will make an EPS file and convert that to 
PDF using GPL Ghostscript.
 The suffixes recognized for a PDF file are: @file{.pdf}, @file{.PDF}.
-If GPL Ghostscript cannot be run on the PostScript file, it will remain and a 
warning will be printed.
+If GPL Ghostscript cannot be run on the PostScript file, The EPS will remain 
and a warning will be printed (see @ref{Optional dependencies}).
 
 @item @option{blank}
 @cindex @file{blank} color channel
@@ -11953,6 +12543,9 @@ If this argument is given for any color channel, that 
channel will not be used i
 @item Plain text
 @cindex Plain text
 @cindex Suffixes, plain text
+The value of each pixel in a 2D image can be written as a 2D matrix in a 
plain-text file.
+Therefore, for the purpose of ConvertType, plain-text files are a 
single-channel raster graphics file format.
+
 Plain text files have the advantage that they can be viewed with any text 
editor or on the command-line.
 Most programs also support input as plain text files.
 As input, each plain text file is considered to contain one color channel.
@@ -11982,6 +12575,25 @@ To print to the standard output, set the output name 
to `@file{stdout}'.
 @node Color, Aligning images with small WCS offsets, Recognized file formats, 
ConvertType
 @subsection Color
 
+@cindex RGB
+@cindex Filter
+@cindex Color channel
+@cindex Channel (color)
+Color is generally defined after mixing various data ``channels''.
+The values for each channel usually come a filter that is placed in the 
optical path.
+Filters, only allow a certrain window of the spectrum to pass (for example the 
SDSS @emph{r} filter only alows light from about 5500 to 7000 Angstroms).
+In digital monitors or common digital cameras, a different set of filters are 
used: Red, Green and Blue (commonly known as RGB) that are more optimized to 
the eye's perception.
+On the other hand, when printing on paper, standard printers use the cyan, 
magenta, yellow and key (CMYK, key=black) color space.
+
+@menu
+* Pixel colors::                Multiple filters in each pixel.
+* Colormaps for single-channel pixels::  Better display of single-filter 
images.
+* Vector graphics colors::
+@end menu
+
+
+@node Pixel colors, Colormaps for single-channel pixels, Color, Color
+@subsubsection Pixel colors
 @cindex RGB
 @cindex CMYK
 @cindex Image
@@ -11989,35 +12601,60 @@ To print to the standard output, set the output name 
to `@file{stdout}'.
 @cindex Pixels
 @cindex Colormap
 @cindex Primary colors
-Color is defined by mixing various measurements/filters.
-In digital monitors or common digital cameras, colors are displayed/stored by 
mixing the three basic colors of red, green and blue (RGB) with various 
proportions.
-When printing on paper, standard printers use the cyan, magenta, yellow and 
key (CMYK, key=black) color space.
-In other words, for each displayed/printed pixel of a color image, the 
dataset/image has three or four values.
 
 @cindex Color channel
 @cindex Channel, color
+As discussed in @ref{Color}, for each displayed/printed pixel of a color 
image, the dataset/image has three or four values.
 To store/show the three values for each pixel, cameras and monitors allocate a 
certain fraction of each pixel's area to red, green and blue filters.
 These three filters are thus built into the hardware at the pixel level.
+
 However, because measurement accuracy is very important in scientific 
instruments, and we want to do measurements (take images) with various/custom 
filters (without having to order a new expensive detector!), scientific 
detectors use the full area of the pixel to store one value for it in a 
single/mono channel dataset.
 To make measurements in different filters, we just place a filter in the light 
path before the detector.
 Therefore, the FITS format that is used to store astronomical datasets is 
inherently a mono-channel format (see @ref{Recognized file formats} or 
@ref{Fits}).
 
-When a subject has been imaged in multiple filters, you can feed each 
different filter into the red, green and blue channels and obtain a colored 
visualization.
-In ConvertType, you can do this by giving each separate single-channel dataset 
(for example in the FITS image format) as an argument (in the proper order), 
then asking for the output in a format that supports multi-channel datasets 
(for example JPEG or PDF, see the examples in @ref{Invoking astconvertt}).
+@cindex False color
+@cindex Pseudo color
+When a subject has been imaged in multiple filters, you can feed each 
different filter into the red, green and blue channels of your monitor and 
obtain a false-colored visualization.
+The reason we say ``false-color'' (or pseudo color) is that generally, the 
three data channels you provide are not from the same Red, Green and Blue 
filters of your monitor!
+So the observed color on your monitor doesn't correspond the physical 
``color'' that you would have seen if you looked at the object by eye.
+Nevertheless, it is good (and sometimes necessary) for visualization (of 
special features).
+
+In ConvertType, you can do this by giving each separate single-channel dataset 
(for example in the FITS image format) as an argument (in the proper order), 
then asking for the output in a format that supports multi-channel datasets 
(for example see the command below, or @ref{ConvertType input and output}).
+
+@example
+$ astconvertt r.fits g.fits b.fits --output=color.jpg
+@end example
+
+
+@node Colormaps for single-channel pixels, Vector graphics colors, Pixel 
colors, Color
+@subsubsection Colormaps for single-channel pixels
 
-@cindex Grayscale
 @cindex Visualization
 @cindex Colormap, HSV
-@cindex Colormap, gray-scale
 @cindex HSV: Hue Saturation Value
-As discussed above, color is not defined when a dataset/image contains a 
single value for each pixel.
-However, we interact with scientific datasets through monitors or printers 
(which allow multiple values per pixel and produce color with them).
+As discussed in @ref{Pixel colors}, color is not defined when a dataset/image 
contains a single value for each pixel.
+However, we interact with scientific datasets through monitors or printers.
+They allow multiple channels (independent values)  per pixel and produce color 
with them (on monitors, this is usually with three channels: Red, Green and 
Blue).
 As a result, there is a lot of freedom in visualizing a single-channel dataset.
-The most basic is to use shades of black (because of its strong contrast with 
white).
-This scheme is called grayscale.
-To help in visualization, more complex mappings can be defined.
-For example, the values can be scaled to a range of 0 to 360 and used as the 
``Hue'' term of the @url{https://en.wikipedia.org/wiki/HSL_and_HSV, 
Hue-Saturation-Value} (HSV) color space (while fixing the ``Saturation'' and 
``Value'' terms).
+
+The mapping of single-channel values to multi-channel colors is called called 
a ``color map''.
+Since more information can be put in multiple channels, this usually results 
in better visualizing the dynamic range of your single-channel data.
 In ConvertType, you can use the @option{--colormap} option to choose between 
different mappings of mono-channel inputs, see @ref{Invoking astconvertt}.
+Below, we'll review two of the basic color maps, please see the description of 
@option{--colormap} in @ref{Invoking astconvertt} for the full list.
+
+@itemize
+@item
+@cindex Grayscale
+@cindex Colormap, gray-scale
+The most basic colormap is shades of black (because of its strong contrast 
with white).
+This scheme is called @url{https://en.wikipedia.org/wiki/Grayscale, Grayscale}.
+But ultimately, the black is just one color, so with Grayscale, you aren't 
using the full dynamic range of the three-channel monitor effectively.
+To help in visualization, more complex mappings can be defined.
+
+@item
+A slightly more complex color map can be defined when you scale the values to 
a range of 0 to 360, and use as it as the ``Hue'' term of the 
@url{https://en.wikipedia.org/wiki/HSL_and_HSV, Hue-Saturation-Value} (HSV) 
color space (while fixing the ``Saturation'' and ``Value'' terms).
+The increased usage of the monitor's 3-channel color space is indeed better, 
but the resulting images can be un-''natural'' to the eye.
+@end itemize
 
 Since grayscale is a commonly used mapping of single-valued datasets, we'll 
continue with a closer look at how it is stored.
 One way to represent a gray-scale image in different color spaces is to use 
the same proportions of the primary colors in each pixel.
@@ -12046,6 +12683,31 @@ From this we see how each pixel in a gray-scale image 
is one byte (8 bits) long,
 But thanks to the JPEG compression algorithms, when all the pixels of one 
channel have the same value, that channel is compressed to one pixel.
 Therefore a Grayscale image and a CMYK image that has only the K-channel 
filled are approximately the same file size.
 
+@node Vector graphics colors,  , Colormaps for single-channel pixels, Color
+@subsubsection Vector graphics colors
+@cindex Web colors
+@cindex Colors (web)
+When creating vector graphics, ConvertType recognizes the 
@url{https://en.wikipedia.org/wiki/Web_colors#Extended_colors, extended web 
colors} that are the result of merging the colors in the HTML 4.01, CSS 2.0, 
SVG 1.0 and CSS3 standards.
+They are all shown with their standard name in @ref{colornames}.
+The names are not case sensitive so you can use them in any form (for example 
@code{turquoise} is the same as @code{Turquoise} or @code{TURQUOISE}).
+
+@cindex 24-bit terminal
+@cindex True color terminal
+@cindex Terminal (true color, 24 bit)
+On the command line, you can also get the list of colors with the 
@option{--listcolors} option to CovertType, like below.
+In particular, if your terminal is 24-bit or "true color", in the last column, 
you will see each color.
+This greatly helps in selecting the best color for our purpose easily on the 
command-line (without taking your hands off the keyboard and getting 
distracted).
+
+@example
+$ astconvertt --listcolors
+@end example
+
+@float Figure,colornames
+@center@image{gnuastro-figures/color-names, 15cm, , }
+
+@caption{Recognized color names in Gnuastro, shown with their numerical 
identifiers.}
+@end float
+
 
 @node Aligning images with small WCS offsets, Annotations for figure in paper, 
Color, ConvertType
 @subsection Aligning images with small WCS offsets
@@ -12161,16 +12823,15 @@ $ astcrop ah_f160w.fits 
--center=53.1616278,-27.7802446 --mode=wcs \
 
 To better show the low surface brightness (LSB) outskirts, we'll warp the 
image, then convert the pixel units to surface brightness with the commands 
below.
 It is very important that the warping is done @emph{before} the conversion to 
surface brightness (in units of mag/arcsec@mymath{^2}), because the definition 
of surface brightness is non-linear.
-For more, see the Surface brightness topic of @ref{Brightness flux magnitude}.
+For more, see the Surface brightness topic of @ref{Brightness flux magnitude}, 
and for a more complete tutorial, see @ref{FITS images in a publication}.
 
 @example
 $ zeropoint=25.94
 $ astwarp build/crop.fits --centeroncorner --scale=1/3 \
           --output=build/scaled.fits
-$ pixarea=$(astfits build/scaled.fits --pixelscale --quiet \
-                    | awk '@{print $1*3600*$2*3600@}')
-$ astarithmetic build/scaled.fits abs $zeropoint counts-to-mag \
-                $pixarea log10 2.5 x + --output=build/sb.fits
+$ pixarea=$(astfits build/scaled.fits --pixelareaarcsec2)
+$ astarithmetic build/scaled.fits $zeropoint $pixarea counts-to-sb \
+                --output=build/sb.fits
 @end example
 
 We are now ready to convert the surface brightness image into a PDF.
@@ -12460,10 +13121,9 @@ astwarp $crop --centeroncorner --scale=1/3 
--output=$scaled
 
 # Calculate the pixel area and convert image to Surface brightness.
 sb=$bdir/sb.fits
-pixarea=$(astfits $scaled --pixelscale --quiet \
-              | awk '{print $1*3600*$2*3600}')
-astarithmetic $scaled abs $zeropoint counts-to-mag \
-              $pixarea log10 2.5 x + --output=$sb
+pixarea=$(astfits $scaled --pixelareaarcsec2)
+astarithmetic $scaled $zeropoint $pixarea counts-to-sb \
+              --output=$sb
 
 # Convert the Surface brightness image into PDF.
 sbpdf=$bdir/sb.pdf
@@ -12536,6 +13196,10 @@ $ astconvertt image.fits --output=pdf
 ## Similar to before, but use the Viridis color map:
 $ astconvertt image.fits --colormap=viridis --output=pdf
 
+## Add markers to to highlight parts of the image
+## ('marks.fits' is a table containing coordinates)
+$ astconvertt image.fits --marks=marks.fits --output=pdf
+
 ## Convert an image in JPEG to FITS (with multiple extensions
 ## if its color):
 $ astconvertt image.jpg -oimage.fits
@@ -12551,33 +13215,75 @@ $ astconvertt M31_r.fits M31_g.fits blank -oeps
 $ cat 2darray.txt | astconvertt -oimg.fits
 @end example
 
-@noindent
-The output's file format will be interpreted from the value given to the 
@option{--output} option.
-It can either be given on the command-line or in any of the configuration 
files (see @ref{Configuration files}).
-Note that if the output suffix is not recognized, it will default to plain 
text format, see @ref{Recognized file formats}.
+In the sub-sections below various options that are specific to ConvertType are 
grouped in different categories.
+Please see those sections for a detailed discussion on each group and its 
options.
+Besides those, ConvertType also shares the @ref{Common options} with other 
Gnuastro programs.
+The common options are not repeated here.
+
+
+@menu
+* ConvertType input and output::  Input/output file names and formats.
+* Pixel visualization::         Visualizing the pixels in the output.
+* Drawing with vector graphics::  Adding marks in many shapes and colors over 
the pixels.
+@end menu
+
+@node ConvertType input and output, Pixel visualization, Invoking astconvertt, 
Invoking astconvertt
+@subsubsection ConvertType input and output
 
 @cindex Standard input
 At most four input files (one for each color channel for formats that allow 
it) are allowed in ConvertType.
-The first input dataset can either be a file or come from Standard input (see 
@ref{Standard input}).
+The first input dataset can either be a file, or come from Standard input (see 
@ref{Standard input} and @ref{Recognized file formats}).
+
 The order of multiple input files is important.
-After reading the input file(s) the number of color channels in all the inputs 
will be used to define which color space to use for the outputs and how each 
color channel is interpreted.
+After reading the input file(s) the number of color channels in all the inputs 
will be used to define which color space to use for the outputs and how each 
color channel is interpreted: 1 (for grayscale), 3 (for RGB) and 4 (for CMYK) 
input channels.
+For more on pixel color channels, see @ref{Pixel colors}.
+Depending on the format of the input(s), the number of input files can differ.
+
+For example if you plan to build an RGB PDF and your three channels are in the 
first HDU of @file{r.fits}, @file{g.fits} and @file{b.fits}, then you can 
simply call MakeProfiles like this:
 
-Some formats can allow more than one color channel (for example in the JPEG 
format, see @ref{Recognized file formats}).
-If there is one input dataset (color channel) the output will be gray-scale, 
if three input datasets (color channels) are given, they are respectively 
considered to be the red, green and blue color channels.
+@example
+$ astconvertt r.fits g.fits b.fits -g1 --output=rgb.pdf
+@end example
+
+@noindent
+However, if the three color channels are in three extensions (assuming the 
HDUs are respectively named @code{R}, @code{G} and @code{B}) of a single file 
(assuming @file{channels.fits}), you should run it like this:
+
+@example
+$ astconvertt channels.fits -hR -hG -hB --output=rgb.pdf
+@end example
+
+@noindent
+On the other hand, if the channels are already in a multi-channel format (like 
JPEG), you can simply provide that file:
+
+@example
+$ astconvertt image.jpg --output=rgb.pdf
+@end example
+
+@noindent
+If multiple channels are given as input, and the output format doesn't support 
multiple color channels (for example FITS), ConvertType will put the channels 
in different HDUs, like the example below.
+After running the @command{astfits} command, if your JPEG file wasn't 
grayscale (single channel), you will see multiple HDUs in @file{channels.fits}.
+
+@example
+$ astconvertt image.jpg --output=channels.fits
+$ astfits channels.fits
+@end example
+
+As shown above, the output's file format will be interpreted from the name 
given to the @option{--output} option (as a common option to all Gnuastro 
programs, for the description of @option{--output}, see @ref{Input output 
options}).
+It can either be given on the command-line or in any of the configuration 
files (see @ref{Configuration files}).
+When the output suffix is not recognized, it will default to plain text 
format, see @ref{Recognized file formats}.
+
+If there is one input dataset (color channel) the output will be gray-scale.
+When three input datasets (color channels) are given, they are respectively 
considered to be the red, green and blue color channels.
 Finally, if there are four color channels they will be cyan, magenta, yellow 
and black (CMYK colors).
 
 The value to @option{--output} (or @option{-o}) can be either a full file name 
or just the suffix of the desired output format.
-In the former case, it will used for the output.
+In the former case (full name), it will be directly used for the output's file 
name.
 In the latter case, the name of the output file will be set based on the 
automatic output guidelines, see @ref{Automatic output}.
-Note that the suffix name can optionally start a @file{.} (dot), so for 
example @option{--output=.jpg} and @option{--output=jpg} are equivalent.
+Note that the suffix name can optionally start with a @file{.} (dot), so for 
example @option{--output=.jpg} and @option{--output=jpg} are equivalent.
 See @ref{Recognized file formats}.
 
-Besides the common set of options explained in @ref{Common options}, the 
options to ConvertType can be classified into input, output and flux related 
options.
-The majority of the options are to do with the flux range.
-Astronomical data usually have a very large dynamic range (difference between 
maximum and minimum value) and different subjects might be better demonstrated 
with a limited flux range.
+The relevant options for input/output formats are described below:
 
-@noindent
-Input:
 @table @option
 @item -h STR/INT
 @itemx --hdu=STR/INT
@@ -12596,32 +13302,13 @@ Hence the concept of names is not defined for the 
directories and the values to
 @itemx --globalhdu=STR/INT
 Use the value given to this option (a HDU name or a counter, starting from 0) 
for the HDU identifier of all the input FITS files.
 This is useful when all the inputs are distributed in different files, but 
have the same HDU in those files.
-@end table
-
-@noindent
-Output:
-@table @option
 
 @item -w FLT
 @itemx --widthincm=FLT
 The width of the output in centimeters.
-This is only relevant for those formats that accept such a width (not plain 
text for example).
+This is only relevant for those formats that accept such a width as metadata 
(not FITS or plain-text for example), see @ref{Recognized file formats}.
 For most digital purposes, the number of pixels is far more important than the 
value to this parameter because you can adjust the absolute width (in inches or 
centimeters) in your document preparation program.
 
-@item -b INT
-@itemx --borderwidth=INT
-@cindex Border on an image
-The width of the border to be put around the EPS and PDF outputs in units of 
PostScript points.
-There are 72 or 28.35 PostScript points in an inch or centimeter respectively.
-In other words, there are roughly 3 PostScript points in every millimeter.
-If you are planning on adding a border, its significance is highly correlated 
with the value you give to the @option{--widthincm} parameter.
-
-Unfortunately in the document structuring convention of the PostScript 
language, the ``bounding box'' has to be in units of PostScript points with no 
fractions allowed.
-So the border values only have to be specified in integers.
-To have a final border that is thinner than one PostScript point in your 
document, you can ask for a larger width in ConvertType and then scale down the 
output EPS or PDF file in your document preparation program.
-For example by setting @command{width} in your @command{includegraphics} 
command in @TeX{} or @LaTeX{}.
-Since it is vector graphics, the changes of size have no effect on the quality 
of your output quality (pixels don't get different values).
-
 @item -x
 @itemx --hex
 @cindex ASCII85 encoding
@@ -12644,7 +13331,16 @@ For other formats the value to this option is ignored.
 Note that only in gray-scale (when one input color channel is given) will this 
actually be the exact quality (each pixel will correspond to one input value).
 If it is in color mode, some degradation will occur.
 While the JPEG standard does support loss-less graphics, it is not commonly 
supported.
+@end table
+
+@node Pixel visualization, Drawing with vector graphics, ConvertType input and 
output, Invoking astconvertt
+@subsubsection Pixel visualization
+
+The main goal of ConvertType is to visualize pixels to/from print or web 
friendly formats.
+
+Astronomical data usually have a very large dynamic range (difference between 
maximum and minimum value) and different subjects might be better demonstrated 
with a limited flux range.
 
+@table @option
 @item --colormap=STR[,FLT,...]
 The color map to visualize a single channel.
 The first value given to this option is the name of the color map, which is 
shown below.
@@ -12698,13 +13394,6 @@ When there are three input channels and the output is 
in the FITS format, interp
 The currently supported output formats of ConvertType don't have native 
support for HSV.
 Therefore this option is only supported when the output is in FITS format and 
each of the hue, saturation and value arrays can be saved as one FITS extension 
in the output for further analysis (for example to select a certain color).
 
-@end table
-
-@noindent
-Flux range:
-
-@table @option
-
 @item -c STR
 @itemx --change=STR
 @cindex Change converted pixel values
@@ -12768,16 +13457,277 @@ However, when @option{--fluxlow} or 
@option{--fluxhigh} are called, this optimiz
 @itemx --forcemax
 Similar to @option{--forcemin}, but for the maximum.
 
-
 @item -i
 @itemx --invert
 For 8-bit output types (JPEG, EPS, and PDF for example) the final value that 
is stored is inverted so white becomes black and vice versa.
 The reason for this is that astronomical images usually have a very large area 
of blank sky in them.
 The result will be that a large are of the image will be black.
 Note that this behavior is ideal for gray-scale images, if you want a color 
image, the colors are going to be mixed up.
+@end table
+
+@node Drawing with vector graphics,  , Pixel visualization, Invoking 
astconvertt
+@subsubsection Drawing with vector graphics
+
+With the options described in this section, you can draw marks over your 
to-be-published images (for example in PDF).
+Each mark can be highly customized so they can have different shapes, colors, 
linewidths, text, text size and etc.
+The properties of the marks should be stored in a table that is given to the 
@option{--marks} option described below.
+A fully working demo on adding marks is provided in @ref{Marking objects for 
publication}.
+
+@cindex PostScript point
+@cindex Vector graphics point
+@cindex Point (Vector graphics; PostScript)
+An important factor to consider when drawing vector graphics is that vector 
graphics standards (the PostScript standard in this case) use a ``point'' as 
the primary unit of line thickness or font size.
+Such that 72 points correspond to 1 inch (or 2.54 centimeters).
+In other words, there are roughly 3 PostScript points in every millimeter.
+On the other hand, the pixels of the images you plan to show as the background 
don't have any real size!
+Pixels are abstract and can be associated with any print-size.
+
+In ConvertType, the print-size of your final image is set with the 
@option{--widthincm} option (see @ref{ConvertType input and output}).
+The value to @option{--widthincm} is the to-be width of the image in 
centimeters.
+It therefore defines the thickness of lines or font sizes for your vector 
graphics features (like the image border or marks).
+Just recall that we are not talking about resolution!
+Vector graphics have infinite resolution!
+We are talking about the relative thickness of the lines (or font sizes) in 
releation to the pixels in your background image.
+
+@table @option
+@item -b INT
+@itemx --borderwidth=INT
+@cindex Border on an image
+The width of the border to be put around the EPS and PDF outputs in units of 
PostScript points.
+If you are planning on adding a border, its thickness in relation to your 
image pixel sizes is highly correlated with the value you give to the 
@option{--widthincm} parameter.
+See the description at the start of this section for more.
+
+Unfortunately in the document structuring convention of the PostScript 
language, the ``bounding box'' has to be in units of PostScript points with no 
fractions allowed.
+So the border values only have to be specified in integers.
+To have a final border that is thinner than one PostScript point in your 
document, you can ask for a larger width in ConvertType and then scale down the 
output EPS or PDF file in your document preparation program.
+For example by setting @command{width} in your @command{includegraphics} 
command in @TeX{} or @LaTeX{} to be larger than the value to 
@option{--widthincm}.
+Since it is vector graphics, the changes of size have no effect on the quality 
of your output (pixels don't get different values).
+
+@item --marks=STR
+Draw vector graphics (infinite resolution) marks over the image.
+The value to this option should be the file name of a table containing the 
mark information.
+The table given to this option can have various properties for each mark in 
each column.
+You can specify which column contains which property of the marks using the 
options below that start with @option{--mark}.
+Only two property columns are mandatory (@option{--markcoords}), the rest are 
optional.
+
+The table can be in any of the Gnuastro's @ref{Recognized table formats}.
+For more on the difference between vector and raster graphics, see @ref{Raster 
and Vector graphics}.
+For example, if your table with mark information is called 
@file{my-marks.fits}, you can use the command below to draw red circles of 
radius 5 pixels over the coordinates.
+
+@example
+$ astconvertt image.fits --output=image.pdf \
+              --marks=marks.fits --mode=wcs \
+              --markcoords=RA,DEC
+@end example
+
+You can highly customize each mark with different columns in @file{marks.fits} 
using the @option{--mark*} options below (for example using different colors, 
different shapes, different sizes, text and etc on each mark).
+
+@item --markshdu=STR/INT
+The HDU (or extension) name or number of the table containing mark properties 
(file given to @option{--marks}).
+This is only relevant if the table is in the FITS format and there is more 
than one HDU in the FITS file.
+
+@item -r STR,STR
+@itemx --markcoords=STR,STR
+The column names (or numbers) containing the coordinates of each mark (in 
table given to @option{--marks}).
+Only two values should be given to this option (one for each coordinate).
+They can either be given to one call (@option{--markcoords=RA,DEC}) or in 
separate calls (@option{--markcoords=RA --markcoords=DEC}).
+
+When @option{--mode=image} the columns will be associated to the 
horizonal/vertical coordinates of the image, and interpretted in units of 
pixels.
+In @option{--mode=wcs}, the columns will be assocated to the WCS coordinates 
(typically Right Ascension and Declination, in units of degrees).
+
+@item -O STR
+@itemx --mode=STR
+The coordinate mode for interpretting the values in the columns given to the 
@option{--markcoord1} and @option{--markcoord2} options.
+The acceptable values are either @code{img} (for image or pixel coordinates), 
and @code{wcs} for World Coordinate System (typically RA and Dec).
+For the WCS-mode, the input image should have the necessary WCS keywords, 
otherwize ConvertType will crash.
+
+@item --markshape=STR/INT
+@cindex Shapes for marks (vector graphics)
+The column name(s), or number(s), containing the shapes of each mark (in table 
given to @option{--marks}).
+The shapes can either be identified by their name, or their numerical 
identifier.
+If identifying them by name in a plain-text table, you need to define a string 
column (see @ref{Gnuastro text table format}).
+The full list of names is shown below, with their numerical identifier in 
parenthesis afterwards.
+For each shape, you can also specify properties like the size, line width, 
rotation, color and etc.
+See the description of the relevant @option{--mark*} option below.
+
+@table @code
+
+@item circle (1)
+A circular circumference.
+It's @emph{radius} is defined by a single size element (the first column given 
to @option{--marksize}).
+Any value in the second size column (if given for other shapes in the same 
call) are ignored by this shape.
+
+@item plus (2)
+The plus sign (@mymath{+}).
+The @emph{length of its lines} is defined by a single size element (the first 
column given to @option{--marksize}).
+Such that the intersection of its lines is on the central coordinate of the 
mark.
+Any value in the second size column (if given for other shapes in the same 
call) are ignored by this shape.
+
+@item cross (3)
+A multiplication sign (@mymath{\times}).
+The @emph{length of its lines} is defined by a single size element (the first 
column given to @option{--marksize}).
+Such that the intersection of its lines is on the central coordinate of the 
mark.
+Any value in the second size column (if given for other shapes in the same 
call) are ignored by this shape.
+
+@item ellipse (4)
+An elliptical circumference.
+Its major axis radius is defined by the first size element (first column given 
to @option{--marksize}), and its axis ratio is defined through the second size 
element (second column given to @option{--marksize}).
+
+@item point (5)
+A point (or a filled circle).
+It's @emph{radius} is defined by a single size element (the first column given 
to @option{--marksize}).
+Any value in the second size column (if given for other shapes in the same 
call) are ignored by this shape.
+
+This filled circle mark is defined as a ``point'' because it is usually 
relevant as a small size (or point in the whole image).
+But there is no limit on its size, so it can be arbitrarily large.
+
+@item square (6)
+A square circumference.
+Its @emph{edge length} is defined by a single size element (the first column 
given to @option{--marksize}).
+Any value in the second size column (if given for other shapes in the same 
call) are ignored by this shape.
+
+@item rectangle (7)
+A rectangular circumference.
+Its length along the horizontal image axis is defined by first size element 
(first column given to @option{--marksize}), and its length along the vertical 
image axis is defined through the second size element (second column given to 
@option{--marksize}).
+
+@item line (8)
+A line.
+The line's @emph{length} is defined by a single size element (the first column 
given to @option{--marksize}.
+The line will be centered on the given coordinate.
+Like all shapes, you can rotate the line about its center using the 
@option{--markrotate} column.
+Any value in the second size column (if given for other shapes in the same 
call) are ignored by this shape.
 
 @end table
 
+@item --markrotate=STR/INT
+Column name or number that contains the mark's rotation angle.
+The rotation angle should be in degrees and be relative to the horizontal axis 
of the image.
+
+@item --marksize=STR[,STR]
+The column name(s), or number(s), containing the size(s) of each mark (in 
table given to @option{--marks}).
+All shapes need at least one ``size'' parameter and some need two.
+For the interpretation of the size column(s) for each shape, see the 
@option{--markshape} option's description.
+Since the size column(s) is (are) optional, when not specified, default values 
will be used (which may be too small in larger images, so you need to change 
them).
+
+By default, the values in the size column are assumed to be in the same units 
as the coordinates (defined by the @option{--mode} option, described above).
+However, when the coordinates are in WCS-mode, some special cases may occur 
for the size.
+@itemize
+@item
+The native WCS units (usually degrees) can be too large, and it may be more 
convenient for the values in the size column(s) to be in arc-seconds.
+In this case, you can use the @option{--sizeinarcsec} option.
+@item
+Similar to above, but in units of arc-minutes.
+In this case, you can use the @option{--sizeinarcmin} option.
+@item
+Your sizes may be in units of pixels, not the WCS units.
+In this case, you can use the @option{--sizeinpix} option.
+@end itemize
+
+@item --sizeinpix
+In WCS-mode, assume that the sizes are in units of pixels.
+By default, when in WCS-mode, the sizes are assumed to be in the units of the 
WCS coordinates (usually degrees).
+
+@item --sizeinarcsec
+In WCS-mode, assume that the sizes are in units of arc-seconds.
+By default, when in WCS-mode, the sizes are assumed to be in the units of the 
WCS coordinates (usually degrees).
+
+@item --sizeinarcmin
+In WCS-mode, assume that the sizes are in units of arc-seconds.
+By default, when in WCS-mode, the sizes are assumed to be in the units of the 
WCS coordinates (usually degrees).
+
+@item --marklinewidth=STR/INT
+Column containg the width (thickness) of the line to draw each mark.
+The linewidth is measured in units of ``points'' (where 72 points is one 
inch), and it can be any positive floating point number.
+Therefore, the thickness (in relation to the pixels of your image) depends on 
@option{--widthincm} option.
+For more, see the description at the start of this section.
+
+@item --markcolor=STR/INT
+Column containing the color of the mark.
+This column can be either a string or an integer.
+As a string, the color name can be written directly in your table (this grealy 
helps in human readability).
+For more on string columns see @ref{Gnuastro text table format}.
+As an integer, you can simply use the numerical identifier of the column.
+You can see the list of colors with their names and numerical identifiers in 
Gnuastro by running ConvertType with @option{--listcolors}, or see @ref{Vector 
graphics colors}.
+
+@item --listcolors
+The list of acceptable color names, their codes and their representation can 
be seen with the @option{--listcolors} option.
+By ``representation'' we mean that the color will be shown on the terminal as 
the background in that column.
+But this will only be properly visible with ``true color'' or 24-bit 
terminals, see @url{https://en.wikipedia.org/wiki/ANSI_escape_code,ANSI escape 
sequence standard}.
+Most modern GNU/Linux terminals support 24-bit colors natively, and no 
modification is necessary.
+For macOS, see the box below.
+
+The printed text in standard output is in the @ref{Gnuastro text table 
format}, so if you want to store this table, you can simply pipe the output to 
Gnuastro's Table program and store it as a FITS table:
+
+@example
+$ astconvertt --listcolors | astttable -ocolors.fits
+@end example
+
+@cindex iTerm
+@cindex macOS terminal 24-bit color
+@cindex Color in macOS terminals
+@cartouche
+@noindent
+@strong{macOS terminal colors}: as of August 2022, the default macOS terminal 
(iTerm) does not support 24-bit colors!
+The output of @option{--listlines} therefore doesn't display the actual colors 
(you can only use the color names).
+One tested solution is to install and use @url{https://iterm2.com, iTerm2}, 
which is free software and available in 
@url{https://formulae.brew.sh/cask/iterm2, Homebrew}.
+iTerm2 is described as a successor for iTerm and works on macOS 10.14 
(released in September 2018) or newer.
+@end cartouche
+
+@item --marktext=STR/INT
+Column name or number that contains the text that should be printed under the 
mark.
+If the column is numeric, the number will be printed under the mark (for 
example if you want to write teh magnitude or redshift of the object under the 
mark showing it).
+For the precision of writing floating point columns, see 
@option{--marktextprecision}.
+But if the column has a string format (for example the name of the object like 
an NGC), you need to define the column as a string column (see @ref{Gnuastro 
text table format}).
+
+@item --marktextprecision=INT
+The number of decimal digits to print after the floating point.
+This is only relevant when @option{--marktext} is given, and the selected 
column has a floating point format.
+
+@item --markfont=STR/INT
+@cindex Fonts
+@cindex Ghostscript fonts
+Column name or number that contains the font for the displayed text under the 
mark.
+This is only relevant if @option{--marktext} is called.
+The font should be accessible by Ghostscript.
+
+If you aren't familiar with the available fonts on your system's Ghostscript, 
you can use the @option{--showfonts} option to see all the fonts in a custom 
PDF file (one page per font).
+If you are already familiar with the font you want, but just want to make sure 
about its presence (or spelling!), you can get a list (on standard output) of 
all the available fonts with the @option{--listfonts} option.
+Both are described below.
+
+@cindex Adding Ghostscript fonts
+It is possible to add custom fonts to Ghostscript as described in the 
@url{https://ghostscript.com/doc/current/Fonts.htm, Fonts section} of the 
Ghostscrit manual.
+
+@item --markfontsize=STR/INT
+Column name or number that contains the font size to use.
+This is only relevant if a text column has been defined (with 
@option{--marktext}, described above).
+The font size is in units of ``point''s, see description at the start of this 
section for more.
+
+@item --showfonts
+Create a special PDF file that shows the name and shape of all available fonts 
in your system's Ghostscript.
+You can use this for selecting the best font to put in the 
@option{--markfonts} column.
+The available fonts can differ from one system to another (depending on how 
Ghostscript was configured in that system).
+The PDF file's name is constructed by appending a @file{-fonts.pdf} to the 
file name given to the  @option{--output} option.
+
+The PDF file will have one page for each font, and the sizes of the pages are 
customized for showing the fonts (each page is horizontally elongated).
+This helps to better check the files by disable ``continuous'' mode in your 
PDF viewer, and setting the zoom such that the width of the page corresponds to 
the width of your PDF viewer.
+Simply pressing the left/right keys will then nicely show each fonts 
separately.
+
+@item --listfonts
+Print (to standard output) the names of all available fonts in Ghostscript 
that you can use for the @option{--markfonts} column.
+The available fonts can differ from one system to another (depending on how 
Ghostscript was configured in that system).
+If you aren't already familiar with the shape of each font, please use 
@option{--showfonts} (described above).
+
+@end table
+
+
+
+
+
+
+
+
+
+
 @node Table, Query, ConvertType, Data containers
 @section Table
 
@@ -14258,7 +15208,7 @@ The center of (possibly multiple) crops are read from a 
text file.
 In this mode, the columns identified with the @option{--coordcol} option are 
interpreted as the center of a crop with a width of @option{--width} pixels 
along each dimension.
 The columns can contain any floating point value.
 The value to @option{--output} option is seen as a directory which will host 
(the possibly multiple) separate crop files, see @ref{Crop output} for more.
-For a tutorial using this feature, please see @ref{Finding reddest clumps and 
visual inspection}.
+For a tutorial using this feature, please see @ref{Reddest clumps cutouts and 
parallelization}.
 
 @item Center of a single crop (on the command-line)
 The center of the crop is given on the command-line with the @option{--center} 
option.
@@ -14443,7 +15393,7 @@ In the former case, Crop can check if the central 
region of the cropped image is
 When in catalog mode, Crop will run in parallel unless you set 
@option{--numthreads=1}, see @ref{Multi-threaded operations}.
 Note that when multiple outputs are created with threads, the outputs will not 
be created in the same order.
 This is because the threads are asynchronous and thus not started in order.
-This has no effect on each output, see @ref{Finding reddest clumps and visual 
inspection} for a tutorial on effectively using this feature.
+This has no effect on each output, see @ref{Reddest clumps cutouts and 
parallelization} for a tutorial on effectively using this feature.
 
 @menu
 * Crop options::                A list of all the options with explanation.
@@ -15054,6 +16004,7 @@ Reading NaN as a floating point number in Gnuastro 
isn't case-sensitive.
 @menu
 * Basic mathematical operators::  For example +, -, /, log, pow, and etc.
 * Trigonometric and hyperbolic operators::  sin, cos, atan, asinh, and etc
+* Constants::
 * Unit conversion operators::   magnitudes to counts, or parsecs to AUs, and 
etc.
 * Statistical operators::       Statistics of a single dataset (for example 
mean).
 * Stacking operators::          Coadding or combining multiple datasets into 
one.
@@ -15185,7 +16136,7 @@ For example the command below will take the natural 
logarithm of every pixel in
 $ astarithmetic image.fits log --output=log.fits
 @end example
 
-@item log10
+Basic mathematical operatorsTrigonometric and hyperbolic operators@item log10
 Base-10 logarithm of first popped operand, so ``@command{4 log}'' is 
equivalent to @mymath{log_{10}(4)}.
 Negative pixels will become NaN, and the output type is determined from the 
input, see the explanation under @command{sqrt} for more on these features.
 For example the command below will take the base-10 logarithm of every pixel 
in the input.
@@ -15194,7 +16145,7 @@ $ astarithmetic image.fits log10
 @end example
 @end table
 
-@node Trigonometric and hyperbolic operators, Unit conversion operators, Basic 
mathematical operators, Arithmetic operators
+@node Trigonometric and hyperbolic operators, Constants, Basic mathematical 
operators, Arithmetic operators
 @subsubsection Trigonometric and hyperbolic operators
 
 All the trigonometric and hyperbolic functions are described here.
@@ -15255,8 +16206,67 @@ Inverse Hyperbolic sine, cosine, and tangent.
 These operators take a single operand.
 @end table
 
+@node Constants, Unit conversion operators, Trigonometric and hyperbolic 
operators, Arithmetic operators
+@subsubsection Constants
+@cindex @mymath{\pi}
+During your analysis it is often necessary to have certain constants like the 
number @mymath{\pi}.
+The ``operators'' in this section don't actually take any operand, they just 
replace the desired constant into the stack.
+So in effect, these are actually operands.
+But since their value is not inserted by the user, we have placed them in the 
list of operators.
+
+@table @code
+@item e
+@cindex @mymath{e} (base of natural logarithm)
+@cindex Euler's number (@mymath{e})
+@cindex Base of natural logarithm (@mymath{e})
+Euler’s number, or the base of the natural logarithm (no units).
+See @url{https://en.wikipedia.org/wiki/E_(mathematical_constant), Wikipedia}.
+
+@item pi
+@cindex @mymath{\pi}
+Ratio of circle’s circumference to its diameter (no units).
+See @url{https://en.wikipedia.org/wiki/Pi, Wikipedia}.
+
+@item c
+@cindex Speed of light
+The speed of light in vaccum, in units of @mymath{m/s}.
+see @url{https://en.wikipedia.org/wiki/Speed_of_light, Wikipedia}.
+
+@item G
+@cindex @mymath{g} (gravitational constant)
+@cindex Gravitational constant (@mymath{g})
+The gravitational constant, in units of @mymath{m^3/kg/s^2}.
+See @url{https://en.wikipedia.org/wiki/Gravitational_constant, Wikipedia}.
+
+@item h
+@cindex @mymath{h} (Plank's constant)
+@cindex Plank's constant (@mymath{h})
+Plank's constant, in units of @mymath{J/Hz} or @mymath{kg\times m^2/s}.
+See @url{https://en.wikipedia.org/wiki/Planck_constant, Wikipedia}.
+
+@item au
+@cindex Astronomical Unit (AU)
+@cindex AU (Astronomical Unit)
+Astronomical Unit, in units of meters.
+See @url{https://en.wikipedia.org/wiki/Astronomical_unit, Wikipedia}.
+
+@item ly
+@cindex Light year
+Distance covered by light in vacuum in one year, in units of meters.
+See @url{https://en.wikipedia.org/wiki/Light-year, Wikipedia}.
+
+@item avogadro
+@cindex Avogradro's number
+Avogadro's constant, in units of @mymath{1/mol}.
+See @url{https://en.wikipedia.org/wiki/Avogadro_constant, Wikipedia}.
+
+@item fine-structure
+@cindex Fine structure constant
+The fine-structure constant (no units).
+See @url{https://en.wikipedia.org/wiki/Fine-structure_constant, Wikipedia}.
+@end table
 
-@node Unit conversion operators, Statistical operators, Trigonometric and 
hyperbolic operators, Arithmetic operators
+@node Unit conversion operators, Statistical operators, Constants, Arithmetic 
operators
 @subsubsection Unit conversion operators
 
 It often happens that you have data in one unit (for example magnitudes to 
measure the brightness of a galaxy), but would like to convert it into another 
(for example electron counts on your CCD).
@@ -15289,6 +16299,42 @@ Note that because the output is a single number, we 
are using @option{--quiet} t
 $ astarithmetic 20 24.8 mag-to-counts --quiet
 @end example
 
+@item counts-to-sb
+Convert counts to surface brightness using the zeropoint and area (in units of 
arcsec@mymath{^2}).
+The first popped operand is the area (in arcsec@mymath{^2}), the second popped 
operand is the zeropoint and the third are the count values.
+Estimating the surface brightness involves taking the logarithm.
+Therefore this operator will produce NaN for counts with a negative value.
+
+For example with the commands below, we read the zeropoint from the image 
headers (assuming it is in the @code{ZPOINT} keyword), we calculate the pixel 
area from the image itself, and we call this operator to convert the image 
pixels (in counts) to surface brightness (mag/arcsec@mymath{^2}).
+
+@example
+$ zeropoint=$(astfits image.fits --keyvalue=ZPOINT -q)
+$ pixarea=$(astfits image.fits --pixelareaarcsec2)
+$ astarithmetic image.fits $zeropoint $pixarea counts-to-sb \
+                --output=image-sb.fits
+
+@end example
+For more on the definition of surface brightness see @ref{Brightness flux 
magnitude}, and for a fully tutorial on optimal usage of this, see @ref{FITS 
images in a publication}.
+
+@item sb-to-counts
+Convert surface brightness using the zeropoint and area (in units of 
arcsec@mymath{^2}) to counts.
+The first popped operand is the area (in arcsec@mymath{^2}), the second popped 
operand is the zeropoint and the third are the surface brightness values.
+See the description of @command{counts-to-sb} for more.
+
+@item mag-to-sb
+Convert magnitudes to surface brightness over a certain area (in units of 
arcsec@mymath{^2}).
+The first popped operand is the area and the second is the magnitude.
+For example, let's assume you have a table with the two columns of magnitude 
(called @code{MAG}) and area (called @code{AREAARCSEC2}).
+In the command below, we'll use @ref{Column arithmetic} to return the surface 
brightness.
+@example
+$ asttable table.fits -c'arith MAG AREAARCSEC2 mag-to-sb'
+@end example
+
+@item sb-to-mag
+Convert surface brightness to magnitudes over a certain area (in units of 
arcsec@mymath{^2}).
+The first popped operand is the area and the second is the magnitude.
+See the description of @code{mag-to-sb} for more.
+
 @item counts-to-jy
 @cindex AB magnitude
 @cindex Magnitude, AB
@@ -21155,24 +22201,19 @@ But this is wrong because magnitude is a logarithmic 
scale while area is linear.
 It is the brightness that should be divided by the solid angle because both 
have linear scales.
 The magnitude of that ratio is then defined to be the surface brightness.
 
-A common scenario is to convert an image's pixel values to surface brightness, 
when you know its zeropoint.
+One usual application of this is to convert an image's pixel values to surface 
brightness, when you know its zeropoint.
 This can be done with the two simple commands below.
 First, we derive the pixel area (in arcsec@mymath{^2}) then we use Arithmetic 
to convert the pixels into surface brightness, see below for the details.
 
 @example
-$ pixarea=$(astfits image.fits --pixelscale --quiet \
-                    | awk '@{print $1*3600*$2*3600@}')
-$ astarithmetic image.fits abs 26.23 counts-to-mag \
-                $pixarea log10 2.5 x + \
+$ zeropoint=22.5
+$ pixarea=$(astfits image.fits --pixelareaarcsec2)
+$ astarithmetic image.fits $zeropoint $pixarea counts-to-sb \
                 --output=image-sb.fits
 @end example
 
-Within the Arithmetic call, first we apply the @code{abs} operator to convert 
all negative pixels to positive.
-This is done because magnitudes are defined in log scale, which is not defined 
in the negative.
-In a properly reduced image, negative pixels will be only due to noise and 
their fluctuations will be similar to positive noise pixels, so this is no 
problem.
-The result is then fed into Arithmetic's @code{counts-to-mag} operator with 
the zeropoint which will convert all pixels to magnitudes.
-Finally we calculate the extra @mymath{2.5\log_{10}(A)} term (with the 
`@code{$pixarea log10 2.5 x}' term) and add it (with `@code{+}') to each pixel 
of the magnitude image to have the pixel's surface brightness.
 See @ref{Reverse polish notation} for more on Arithmetic's notation and 
@ref{Arithmetic operators} for a description of each operator.
+And see @ref{FITS images in a publication} for a fully working tutorial on how 
to optimally convert a FITS image to a PDF image for usage in a publication 
using the surface brightness conversion shown above.
 
 @cartouche
 @noindent
@@ -24513,8 +25554,7 @@ In @ref{flatplane} the infinitesimal changes for each 
polar coordinate are plott
 @float Figure,flatplane
 @center@image{gnuastro-figures/flatplane, 10cm, , }
 
-@caption{Two dimensional Cartesian and polar coordinates on a flat
-plane.}
+@caption{Two dimensional Cartesian and polar coordinates on a flat plane.}
 @end float
 
 Assuming an object is placed at a certain position, which can be parameterized 
as @mymath{(x,y)}, or @mymath{(r,\phi)}, a general infinitesimal change in its 
position will place it in the coordinates @mymath{(x+dx,y+dy)}, or 
@mymath{(r+dr,\phi+d\phi)}.
@@ -27430,6 +28470,7 @@ If you use the Info version of this manual (see 
@ref{Info}), you don't have to w
 * Labeled datasets::            Working with Segmented/labeled datasets.
 * Convolution functions::       Library functions to do convolution.
 * Interpolation::               Interpolate (over blank values possibly).
+* Color functions::             Definitions and operations related to colors.
 * Git wrappers::                Wrappers for functions in libgit2.
 * Unit conversion library::     Converting between recognized units.
 * Spectral lines library::      Functions for operating on Spectral lines.
@@ -29860,6 +30901,11 @@ this is the first node to be added to the list, 
@code{list} must be
 Pop the top node from @code{list} and return it.
 @end deftypefun
 
+@deftypefun {gal_data_t *} gal_list_data_select_by_name (gal_data_t 
@code{*list}, char @code{*name})
+Select the dataset within the list, that has a @code{name} element that is 
identical (case-sensitive) to the given @code{name}.
+Note that this dataset will not be popped from the list, only a pointer to it 
will be returned and if you free it or change its @code{next} element, it may 
harm your original list.
+@end deftypefun
+
 @deftypefun void gal_list_data_reverse (gal_data_t @code{**list})
 Reverse the order of the list such that the top node in the list before
 calling this function becomes the bottom node after it.
@@ -31153,72 +32199,98 @@ The display width of the JPEG file in units of 
centimeters (to suggest to viewer
 @node EPS files, PDF files, JPEG files, File input output
 @subsubsection EPS files (@file{eps.h})
 
-The Encapsulated PostScript (EPS) format is commonly used to store images
-(or individual/single-page parts of a document) in the PostScript
-documents. For a more complete introduction, please see @ref{Recognized
-file formats}. To provide high quality graphics, the Postscript language is
-a vectorized format, therefore pixels (elements of a ``rasterized'' format)
-aren't defined in their context.
+The Encapsulated PostScript (EPS) format is commonly used to store images (or 
individual/single-page parts of a document) in the PostScript documents.
+For a more complete introduction, please see @ref{Recognized file formats}.
+To provide high quality graphics, the Postscript language is a vectorized 
format, therefore pixels (elements of a ``rasterized'' format) aren't defined 
in their context.
+
+To display rasterized images, PostScript does allow arrays of pixels.
+However, since the over-all EPS file may contain many vectorized elements (for 
example borders, text, or other lines over the text) and interpreting them is 
not trivial or necessary within Gnuastro's scope, Gnuastro only provides some 
functions to write a dataset (in the @code{gal_data_t} format, see @ref{Generic 
data container}) into EPS.
+
+@deffn  Macro GAL_EPS_MARK_COLNAME_TEXT
+@deffnx Macro GAL_EPS_MARK_COLNAME_FONT
+@deffnx Macro GAL_EPS_MARK_COLNAME_XPIX
+@deffnx Macro GAL_EPS_MARK_COLNAME_YPIX
+@deffnx Macro GAL_EPS_MARK_COLNAME_SHAPE
+@deffnx Macro GAL_EPS_MARK_COLNAME_COLOR
+@deffnx Macro GAL_EPS_MARK_COLNAME_SIZE1
+@deffnx Macro GAL_EPS_MARK_COLNAME_SIZE2
+@deffnx Macro GAL_EPS_MARK_COLNAME_ROTATE
+@deffnx Macro GAL_EPS_MARK_COLNAME_FONTSIZE
+@deffnx Macro GAL_EPS_MARK_COLNAME_LINEWIDTH
+Name of column that the required property will be read from.
+@end deffn
 
-To display rasterized images, PostScript does allow arrays of
-pixels. However, since the over-all EPS file may contain many vectorized
-elements (for example borders, text, or other lines over the text) and
-interpreting them is not trivial or necessary within Gnuastro's scope,
-Gnuastro only provides some functions to write a dataset (in the
-@code{gal_data_t} format, see @ref{Generic data container}) into EPS.
+@deffn  Macro GAL_EPS_MARK_DEFAULT_SHAPE
+@deffnx Macro GAL_EPS_MARK_DEFAULT_COLOR
+@deffnx Macro GAL_EPS_MARK_DEFAULT_SIZE1
+@deffnx Macro GAL_EPS_MARK_DEFAULT_SIZE2
+@deffnx Macro GAL_EPS_MARK_DEFAULT_SIZE2_ELLIPSE
+@deffnx Macro GAL_EPS_MARK_DEFAULT_ROTATE
+@deffnx Macro GAL_EPS_MARK_DEFAULT_LINEWIDTH
+@deffnx Macro GAL_EPS_MARK_DEFAULT_FONT
+@deffnx Macro GAL_EPS_MARK_DEFAULT_FONTSIZE
+Default values for the various mark properties.
+These constants will be used if the caller hasn't provided any of the given 
property.
+@end deffn
 
 @deftypefun {int} gal_eps_name_is_eps (char @code{*name})
-Return @code{1} if @code{name} has an EPS suffix. This can be used to make
-sure that a given input file is EPS. See @code{gal_eps_suffix_is_eps} for a
-list of recognized suffixes.
+Return @code{1} if @code{name} has an EPS suffix.
+This can be used to make sure that a given input file is EPS.
+See @code{gal_eps_suffix_is_eps} for a list of recognized suffixes.
 @end deftypefun
 
 @deftypefun {int} gal_eps_suffix_is_eps (char @code{*name})
-Return @code{1} if @code{suffix} is a recognized EPS suffix. The recognized
-suffixes are @code{.eps}, @code{.EPS}, @code{.epsf}, @code{.epsi}.
+Return @code{1} if @code{suffix} is a recognized EPS suffix.
+The recognized suffixes are @code{.eps}, @code{.EPS}, @code{.epsf}, 
@code{.epsi}.
 @end deftypefun
 
 @deftypefun {void} gal_eps_to_pt (float @code{widthincm}, size_t 
@code{*dsize}, size_t @code{*w_h_in_pt})
-Given a specific width in centimeters (@code{widthincm} and the number of
-the dataset's pixels in each dimension (@code{dsize}) calculate the size of
-the output in PostScript points. The output values are written in the
-@code{w_h_in_pt} array (which has to be allocated before calling this
-function). The first element in @code{w_h_in_pt} is the width and the
-second is the height of the image.
-@end deftypefun
-
-@deftypefun {void} gal_eps_write (gal_data_t @code{*in}, char 
@code{*filename}, float @code{widthincm}, uint32_t @code{borderwidth}, int 
@code{hex}, int @code{dontoptimize}, int @code{forpdf})
-Write the @code{in} dataset into an EPS file called
-@code{filename}. @code{in} has to be an unsigned 8-bit character type
-(@code{GAL_TYPE_UINT8}, see @ref{Numeric data types}). The desired width of
-the image in human/non-pixel units (to help the displayer) can be set with
-the @code{widthincm} argument. If @code{borderwidth} is non-zero, it is
-interpreted as the width (in points) of a solid black border around the
-image. A border can helpful when importing the EPS file into a document.
+Given a specific width in centimeters (@code{widthincm} and the number of he 
dataset's pixels in each dimension (@code{dsize}) calculate the size of he 
output in PostScript points.
+The output values are written in the @code{w_h_in_pt} array (which has to be 
allocated before calling this unction).
+The first element in @code{w_h_in_pt} is the width and the second is the 
height of the image.
+@end deftypefun
+
+@deftypefun uint8_t gal_eps_shape_name_to_id (char *name)
+Return the shape ID of a mark from its name (which is not case-sensitive).
+@end deftypefun
+
+@deftypefun uint8_t gal_eps_shape_id_to_name (uint8_t id)
+Return the shape name from its ID.
+@end deftypefun
+
+@deftypefun {void} gal_eps_write (gal_data_t @code{*in}, char 
@code{*filename}, float @code{widthincm}, uint32_t @code{borderwidth}, int 
@code{hex}, int @code{dontoptimize}, int @code{forps}, gal_data_t @code{*marks})
+Write the @code{in} dataset into an EPS file called @code{filename}.
+@code{in} has to be an unsigned 8-bit character type @code{GAL_TYPE_UINT8}, 
see @ref{Numeric data types}).
+The desired width of he image in human/non-pixel units (to help the displayer) 
can be set with he @code{widthincm} argument.
+If @code{borderwidth} is non-zero, it is nterpreted as the width (in points) 
of a solid black border around the mage.
+A border can helpful when importing the EPS file into a document.
+If @code{forpdf} is not zero, the output can be imported into a Postscript 
file directly (not as an ``encapsulated'' postscript, which is the default).
 
 @cindex ASCII85 encoding
 @cindex Hexadecimal encoding
-EPS files are plain-text (can be opened/edited in a text editor), therefore
-there are different encodings to store the data (pixel values) within
-them. Gnuastro supports the Hexadecimal and ASCII85 encoding. ASCII85 is
-more efficient (producing small file sizes), so it is the default
-encoding. To use Hexadecimal encoding, set @code{hex} to a non-zero
-value. Currently If you don't directly want to import the EPS file into a
-PostScript document but want to later compile it into a PDF file, set the
-@code{forpdf} argument to @code{1}.
+EPS files are plain-text (can be opened/edited in a text editor), therefore 
there are different encodings to store the data (pixel values) within them.
+Gnuastro supports the Hexadecimal and ASCII85 encoding.
+ASCII85 is more efficient (producing small file sizes), so it is the default 
encoding.
+To use Hexadecimal encoding, set @code{hex} to a non-zero value.
 
 @cindex PDF
 @cindex EPS
 @cindex PostScript
-By default, when the dataset only has two values, this function will use
-the PostScript optimization that allows setting the pixel values per bit,
-not byte (@ref{Recognized file formats}). This can greatly help reduce the
-file size. However, when @option{dontoptimize!=0}, this optimization is
-disabled: even though there are only two values (is binary), the
-difference between them does not correspond to the full contrast of black
-and white.
-@end deftypefun
+By default, when the dataset only has two values, this function will use the 
PostScript optimization that allows setting the pixel values per bit, not byte 
(@ref{Recognized file formats}).
+This can greatly help reduce the file size.
+However, when @option{dontoptimize!=0}, this optimization is disabled: even 
though there are only two values (is binary), the difference between them does 
not correspond to the full contrast of black and white.
 
+If @code{marks!=NULL}, it is assumed to contain multiple columns of 
information to draw marks over the background image.
+The multiple columns are a linked list of 1D @code{gal_data_t} of the same 
size (number of rows) that are connected to each other through the @code{next} 
element (this is the same format that Gnuastro's library uses for tables, see 
@ref{Table input output} or @ref{Library demo - reading and writing table 
columns}).
+
+The macros defined above that have the format of @code{GAL_EPS_MARK_COLNAME_*} 
show all the possible columns that you can provide in this linked list.
+Only the two coordinate columns are mandatory 
(@code{GAL_EPS_MARK_COLNAME_XPIX} and @code{GAL_EPS_MARK_COLNAME_YPIX}.
+If any of the other properties is not in the linked list, then the default 
properties of the @code{GAL_EPS_MARK_DEFAULT_*} macros will be used (also 
defined above.
+
+The columns are identified based on the @code{name} element of Gnuastro's 
generic data structure (see @ref{Generic data container}).
+The names must have the pre-defined names of the @code{GAL_EPS_MARK_COLNAME_*} 
macros (case sensitive).
+Therefore, the order of columns in the list is irrelevant!
+@end deftypefun
 
 
 
@@ -31226,54 +32298,45 @@ and white.
 @node PDF files,  , EPS files, File input output
 @subsubsection PDF files (@file{pdf.h})
 
-The portable document format (PDF) has arguably become the most common
-format used for distribution of documents. In practice, a PDF file is just
-a compiled PostScript file. For a more complete introduction, please see
-@ref{Recognized file formats}. To provide high quality graphics, the PDF is
-a vectorized format, therefore pixels (elements of a ``rasterized'' format)
-aren't defined in their context. As a result, similar to @ref{EPS files},
-Gnuastro only writes datasets to a PDF file, not vice-versa.
+The portable document format (PDF) has arguably become the most common format 
used for distribution of documents.
+In practice, a PDF file is just a compiled PostScript file.
+For a more complete introduction, please see @ref{Recognized file formats}.
+To provide high quality graphics, the PDF is a vectorized format, therefore 
pixels (elements of a ``rasterized'' format) aren't defined in their context.
+As a result, similar to @ref{EPS files}, Gnuastro only writes datasets to a 
PDF file, not vice-versa.
 
 @deftypefun {int} gal_pdf_name_is_pdf (char @code{*name})
-Return @code{1} if @code{name} has an PDF suffix. This can be used to make
-sure that a given input file is PDF. See @code{gal_pdf_suffix_is_pdf} for a
-list of recognized suffixes.
+Return @code{1} if @code{name} has an PDF suffix.
+This can be used to make sure that a given input file is PDF.
+See @code{gal_pdf_suffix_is_pdf} for a list of recognized suffixes.
 @end deftypefun
 
 @deftypefun {int} gal_pdf_suffix_is_pdf (char @code{*name})
-Return @code{1} if @code{suffix} is a recognized PDF suffix. The recognized
-suffixes are @code{.pdf} and @code{.PDF}.
-@end deftypefun
-
-@deftypefun {void} gal_pdf_write (gal_data_t @code{*in}, char 
@code{*filename}, float @code{widthincm}, uint32_t @code{borderwidth}, int 
@code{dontoptimize})
-Write the @code{in} dataset into an EPS file called
-@code{filename}. @code{in} has to be an unsigned 8-bit character type
-(@code{GAL_TYPE_UINT8}, see @ref{Numeric data types}). The desired width of
-the image in human/non-pixel units (to help the displayer) can be set with
-the @code{widthincm} argument. If @code{borderwidth} is non-zero, it is
-interpreted as the width (in points) of a solid black border around the
-image. A border can helpful when importing the PDF file into a document.
-
-This function is just a wrapper for the @code{gal_eps_write} function in
-@ref{EPS files}. After making the EPS file, Ghostscript (with a version of
-9.10 or above, see @ref{Optional dependencies}) will be used to compile the
-EPS file to a PDF file. Therefore if GhostScript doesn't exist, doesn't have
-the proper version, or fails for any other reason, the EPS file will
-remain. It can be used to find the cause, or use another converter or
-PostScript compiler.
+Return @code{1} if @code{suffix} is a recognized PDF suffix.
+The recognized suffixes are @code{.pdf} and @code{.PDF}.
+@end deftypefun
+
+@deftypefun {void} gal_pdf_write (gal_data_t @code{*in}, char 
@code{*filename}, float @code{widthincm}, uint32_t @code{borderwidth}, int 
@code{dontoptimize}, gal_data_t @code{*marks})
+Write the @code{in} dataset into an EPS file called @code{filename}.
+@code{in} has to be an unsigned 8-bit character type (@code{GAL_TYPE_UINT8}, 
see @ref{Numeric data types}).
+The desired width of the image in human/non-pixel units (to help the 
displayer) can be set with the @code{widthincm} argument.
+If @code{borderwidth} is non-zero, it is interpreted as the width (in points) 
of a solid black border around the image.
+A border can helpful when importing the PDF file into a document.
+
+This function is just a wrapper for the @code{gal_eps_write} function in 
@ref{EPS files}.
+After making the EPS file, Ghostscript (with a version of 9.10 or above, see 
@ref{Optional dependencies}) will be used to compile the EPS file to a PDF file.
+Therefore if GhostScript doesn't exist, doesn't have the proper version, or 
fails for any other reason, the EPS file will remain.
+It can be used to find the cause, or use another converter or PostScript 
compiler.
 
 @cindex PDF
 @cindex EPS
 @cindex PostScript
-By default, when the dataset only has two values, this function will use
-the PostScript optimization that allows setting the pixel values per bit,
-not byte (@ref{Recognized file formats}). This can greatly help reduce the
-file size. However, when @option{dontoptimize!=0}, this optimization is
-disabled: even though there are only two values (is binary), the
-difference between them does not correspond to the full contrast of black
-and white.
-@end deftypefun
+By default, when the dataset only has two values, this function will use the 
PostScript optimization that allows setting the pixel values per bit,not byte 
(@ref{Recognized file formats}).
+This can greatly help reduce the file size.
+However, when @option{dontoptimize!=0}, this optimization is disabled: even 
though there are only two values (is binary), the difference between them does 
not correspond to the full contrast of black and white.
 
+If @code{marks!=NULL}, it is assumed to contain information on how to draw 
marks over the image.
+This is directly fed to the @code{gal_eps_write} function, so for more on how 
to provide the mark information, see the description of @code{gal_eps_write} in 
@ref{EPS files}.
+@end deftypefun
 
 
 
@@ -31735,6 +32798,36 @@ The first two operators expect a string operand (in 
the sexagesimal formats ment
 The latter two are the opposite.
 @end deffn
 
+@deffn  Macro GAL_ARITHMETIC_OP_COUNTS_TO_MAG
+@deffnx Macro GAL_ARITHMETIC_OP_MAG_TO_COUNTS
+@deffnx Macro GAL_ARITHMETIC_OP_MAG_TO_SB
+@deffnx Macro GAL_ARITHMETIC_OP_SB_TO_MAG
+@deffnx Macro GAL_ARITHMETIC_OP_COUNTS_TO_JY
+@deffnx Macro GAL_ARITHMETIC_OP_JY_TO_COUNTS
+@deffnx Macro GAL_ARITHMETIC_OP_MAG_TO_JY
+@deffnx Macro GAL_ARITHMETIC_OP_JY_TO_MAG
+@cindex Surface Brightness
+Binary operators for converting brightness and surface brightness units to and 
from each other.
+The first operand to all of them are the values in the respective input unit.
+The second poppped operand is the zeropoint, except for the operators that 
involve surface brightness (those with @code{SB}).
+For the surface brightness related operators, the second popped operand is the 
area in units of arcsec@mymath{^2}.
+@end deffn
+
+@deffn  Macro GAL_ARITHMETIC_OP_COUNTS_TO_SB
+@deffnx Macro GAL_ARITHMETIC_OP_SB_TO_COUNTS
+Operators for converting counts to surface brightness and vice-versa.
+These operators take three operands: 1) the input dataset in units of counts 
or surface brightness (depending on the operator), 2) the zeropoint, 3) the 
area in units of arcsec@mymath{^2}.
+@end deffn
+
+@deffn  Macro GAL_ARITHMETIC_OP_AU_TO_PC
+@deffnx Macro GAL_ARITHMETIC_OP_PC_TO_AU
+@deffnx Macro GAL_ARITHMETIC_OP_LY_TO_PC
+@deffnx Macro GAL_ARITHMETIC_OP_PC_TO_LY
+@deffnx Macro GAL_ARITHMETIC_OP_LY_TO_AU
+@deffnx Macro GAL_ARITHMETIC_OP_AU_TO_LY
+Unary operators to convert various distance units to and from each other: 
Astronomical Units (AU), Parsecs (PC) and Light years (LY).
+@end deffn
+
 @deffn  Macro GAL_ARITHMETIC_OP_MINVAL
 @deffnx Macro GAL_ARITHMETIC_OP_MAXVAL
 @deffnx Macro GAL_ARITHMETIC_OP_NUMBERVAL
@@ -31870,6 +32963,21 @@ It accepts these operators only for completeness and 
easy usage in @ref{Arithmet
 So in your programs, it might be preferable to directly use those functions.
 @end deffn
 
+@deffn  Macro GAL_ARITHMETIC_OP_E
+@deffnx Macro GAL_ARITHMETIC_OP_C
+@deffnx Macro GAL_ARITHMETIC_OP_G
+@deffnx Macro GAL_ARITHMETIC_OP_H
+@deffnx Macro GAL_ARITHMETIC_OP_AU
+@deffnx Macro GAL_ARITHMETIC_OP_LY
+@deffnx Macro GAL_ARITHMETIC_OP_PI
+@deffnx Macro GAL_ARITHMETIC_OP_AVOGADRO
+@deffnx Macro GAL_ARITHMETIC_OP_FINESTRUCTURE
+
+Return the respective mathematical constant.
+For their description please see @ref{Constants}.
+The constant values are taken from the GNU Scientific Library's headers 
(defined in @file{gsl/gsl_math.h}).
+@end deffn
+
 @deffn Macro GAL_ARITHMETIC_OP_BOX_AROUND_ELLIPSE
 Return the width (along horizontal) and height (along vertical) of a box that 
encompasses an ellipse with the same center point.
 For more on the three input operands to this operator see the description of 
@code{box-around-ellipse}.
@@ -32351,23 +33459,15 @@ gal_data_free(faketile);
 @node Tile grid,  , Independent tiles, Tessellation library
 @subsubsection Tile grid
 
-One very useful application of tiles is to completely cover an input
-dataset with tiles. Such that you know every pixel/data-element of the
-input image is covered by only one tile. The constructs in this section
-allow easy definition of such a tile structure. They will create lists of
-tiles that are also usable by the general tools discussed in
-@ref{Independent tiles}.
+One very useful application of tiles is to completely cover an input dataset 
with tiles.
+Such that you know every pixel/data-element of the input image is covered by 
only one tile.
+The constructs in this section allow easy definition of such a tile structure.
+They will create lists of tiles that are also usable by the general tools 
discussed in @ref{Independent tiles}.
 
-As discussed in @ref{Tessellation}, (mainly raw) astronomical images will
-mostly require two layers of tessellation, one for amplifier channels which
-all have the same size and another (smaller tile-size) tessellation over
-each channel. Hence, in this section we define a general structure to keep
-the main parameters of this two-layer tessellation and help in benefiting
-from it.
+As discussed in @ref{Tessellation}, (mainly raw) astronomical images will 
mostly require two layers of tessellation, one for amplifier channels which all 
have the same size and another (smaller tile-size) tessellation over each 
channel.
+Hence, in this section we define a general structure to keep the main 
parameters of this two-layer tessellation and help in benefiting from it.
 
-@deftp {Type (C @code{struct})} gal_tile_two_layer_params
-The general structure to keep all the necessary parameters for a two-layer
-tessellation.
+@deftp {Type (C @code{struct})} gal_tile_two_layer_params The general 
structure to keep all the necessary parameters for a two-layer tessellation.
 
 @example
 struct gal_tile_two_layer_params
@@ -32400,78 +33500,59 @@ struct gal_tile_two_layer_params
 @end deftp
 
 @deftypefun {size_t *} gal_tile_full (gal_data_t @code{*input}, size_t 
@code{*regular}, float @code{remainderfrac}, gal_data_t @code{**out}, size_t 
@code{multiple}, size_t @code{**firsttsize})
-Cover the full dataset with (mostly) identical tiles and return the number
-of tiles created along each dimension. The regular tile size (along each
-dimension) is determined from the @code{regular} array. If @code{input}'s
-size is not an exact multiple of @code{regular} for each dimension, then
-the tiles touching the edges in that dimension will have a different size
-to fully cover every element of the input (depending on
-@code{remainderfrac}).
-
-The output is an array with the same dimensions as @code{input} which
-contains the number of tiles along each dimension.  See @ref{Tessellation}
-for a description of its application in Gnuastro's programs and
-@code{remainderfrac}, just note that this function defines only one layer of
-tiles.
-
-This is a low-level function (independent of the
-@code{gal_tile_two_layer_params} structure defined above). If you want a
-two-layer tessellation, directly call @code{gal_tile_full_two_layers} that
-is described below. The input arguments to this function are:
+Cover the full dataset with (mostly) identical tiles and return the number of 
tiles created along each dimension.
+The regular tile size (along each dimension) is determined from the 
@code{regular} array.
+If @code{input}'s size is not an exact multiple of @code{regular} for each 
dimension, then the tiles touching the edges in that dimension will have a 
different size to fully cover every element of the input (depending on 
@code{remainderfrac}).
+
+The output is an array with the same dimensions as @code{input} which contains 
the number of tiles along each dimension.
+See @ref{Tessellation} for a description of its application in Gnuastro's 
programs and @code{remainderfrac}, just note that this function defines only 
one layer of tiles.
+
+This is a low-level function (independent of the 
@code{gal_tile_two_layer_params} structure defined above).
+If you want a two-layer tessellation, directly call 
@code{gal_tile_full_two_layers} that is described below.
+The input arguments to this function are:
 
 @table @code
 @item input
-The main dataset (allocated block) which you want to create a tessellation
-over (only used for its sizes). So @code{input} may be a tile also.
+The main dataset (allocated block) which you want to create a tessellation 
over (only used for its sizes).
+So @code{input} may be a tile also.
 
 @item regular
-The size of the regular tiles along each of the input's dimensions. So
-it must have the same number of elements as the dimensions of @code{input}
-(or @code{input->ndim}).
+The size of the regular tiles along each of the input's dimensions.
+So it must have the same number of elements as the dimensions of @code{input} 
(or @code{input->ndim}).
 
 @item remainderfrac
-The significant fraction of the remainder space to see if it should be
-split into two and put on both sides of a dimension or not. This is thus
-only relevant @code{input} length along a dimension isn't an exact multiple
-of the regular tile size along that dimension. See @ref{Tessellation} for a
-more thorough discussion.
+The significant fraction of the remainder space to see if it should be split 
into two and put on both sides of a dimension or not.
+This is thus only relevant @code{input} length along a dimension isn't an 
exact multiple of the regular tile size along that dimension.
+See @ref{Tessellation} for a more thorough discussion.
 
 @item out
-Pointer to the array of data structures that will keep all the tiles (see
-@ref{Arrays of datasets}). If @code{*out==NULL}, then the necessary space
-to keep all the tiles will be allocated. If not, then all the tile
-information will be filled from the dataset that @code{*out} points to, see
+Pointer to the array of data structures that will keep all the tiles (see 
@ref{Arrays of datasets}).
+If @code{*out==NULL}, then the necessary space to keep all the tiles will be 
allocated.
+If not, then all the tile information will be filled from the dataset that 
@code{*out} points to, see
 @code{multiple} for more.
 
 @item multiple
-When @code{*out==NULL} (and thus will be allocated by this function),
-allocate space for @code{multiple} times the number of tiles needed. This
-can be very useful when you have several more identically sized
-@code{inputs}, and you want all their tiles to be allocated (and thus
-indexed) together, even though they have different @code{block} datasets
-(that then link to one allocated space). See the definition of channels in
+When @code{*out==NULL} (and thus will be allocated by this function), allocate 
space for @code{multiple} times the number of tiles needed.
+This can be very useful when you have several more identically sized 
@code{inputs}, and you want all their tiles to be allocated (and thus indexed) 
together, even though they have different @code{block} datasets (that then link 
to one allocated space).
+See the definition of channels in
 @ref{Tessellation} and @code{gal_tile_full_two_layers} below.
 
 @item firsttsize
-The size of the first tile along every dimension. This is only different
-from the regular tile size when @code{regular} is not an exact multiple of
-@code{input}'s length along every dimension. This array is allocated
-internally by this function.
+The size of the first tile along every dimension.
+This is only different from the regular tile size when @code{regular} is not 
an exact multiple of @code{input}'s length along every dimension.
+This array is allocated internally by this function.
 @end table
 @end deftypefun
 
 
 @deftypefun void gal_tile_full_sanity_check (char @code{*filename}, char 
@code{*hdu}, gal_data_t @code{*input}, struct gal_tile_two_layer_params 
@code{*tl})
-Make sure that the input parameters (in @code{tl}, short for two-layer)
-correspond to the input dataset. @code{filename} and @code{hdu} are only
-required for error messages. Also, allocate and fill the
-@code{tl->channelsize} array.
+Make sure that the input parameters (in @code{tl}, short for two-layer) 
correspond to the input dataset.
+@code{filename} and @code{hdu} are only required for error messages. Also, 
allocate and fill the @code{tl->channelsize} array.
 @end deftypefun
 
 @deftypefun void gal_tile_full_two_layers (gal_data_t @code{*input}, struct 
gal_tile_two_layer_params @code{*tl})
-Create the two layered tessellation in @code{tl}.  The general set of steps
-you need to take to define the two-layered tessellation over an image can
-be seen in the example code below.
+Create the two layered tessellation in @code{tl}.
+The general set of steps you need to take to define the two-layered 
tessellation over an image can be seen in the example code below.
 
 @example
 gal_data_t *input;
@@ -32496,27 +33577,18 @@ gal_tile_full_two_layers(input, &tl);
 
 
 @deftypefun void gal_tile_full_permutation (struct gal_tile_two_layer_params 
@code{*tl})
-Make a permutation to allow the conversion of tile location in memory to
-its location in the full input dataset and put it in
-@code{tl->permutation}. If a permutation has already been defined for the
-tessellation, this function will not do anything. If permutation won't be
-necessary (there is only one channel or one dimension), then this function
-will not do anything (@code{tl->permutation} must have been initialized to
-@code{NULL}).
-
-When there is only one channel OR one dimension, the tiles are allocated in
-memory in the same order that they represent the input data. However, to
-make channel-independent processing possible in a generic way, the tiles of
-each channel are allocated contiguously. So, when there is more than one
-channel AND more than one dimension, the index of the tile does not
-correspond to its position in the grid covering the input dataset.
-
-The example below may help clarify: assume you have a 6x6 tessellation with
-two channels in the horizontal and one in the vertical. On the left you can
-see how the tile IDs correspond to the input dataset. NOTE how `03' is on
-the second row, not on the first after `02'. On the right, you can see how
-the tiles are stored in memory (and shown if you simply write the array
-into a FITS file for example).
+Make a permutation to allow the conversion of tile location in memory to its 
location in the full input dataset and put it in @code{tl->permutation}.
+If a permutation has already been defined for the tessellation, this function 
will not do anything.
+If permutation won't be necessary (there is only one channel or one 
dimension), then this function will not do anything (@code{tl->permutation} 
must have been initialized to @code{NULL}).
+
+When there is only one channel OR one dimension, the tiles are allocated in 
memory in the same order that they represent the input data.
+However, to make channel-independent processing possible in a generic way, the 
tiles of each channel are allocated contiguously.
+So, when there is more than one channel AND more than one dimension, the index 
of the tile does not correspond to its position in the grid covering the input 
dataset.
+
+The example below may help clarify: assume you have a 6x6 tessellation with 
two channels in the horizontal and one in the vertical.
+On the left you can see how the tile IDs correspond to the input dataset.
+NOTE how `03' is on the second row, not on the first after `02'.
+On the right, you can see how the tiles are stored in memory (and shown if you 
simply write the array into a FITS file for example).
 
 @example
    Corresponding to input               In memory
@@ -32553,31 +33625,22 @@ inverse:    IN_ALL[ perm[i] ]   =   IN_MEMORY[ i      
 ]
 @end deftypefun
 
 @deftypefun void gal_tile_full_values_write (gal_data_t @code{*tilevalues}, 
struct gal_tile_two_layer_params @code{*tl}, int @code{withblank}, char 
@code{*filename}, gal_fits_list_key_t @code{*keys}, char @code{*program_string})
-Write one value for each tile into a file. It is important to note that the
-values in @code{tilevalues} must be ordered in the same manner as the
-tiles, so @code{tilevalues->array[i]} is the value that should be given to
-@code{tl->tiles[i]}. The @code{tl->permutation} array must have been
-initialized before calling this function with
-@code{gal_tile_full_permutation}.
+Write one value for each tile into a file.
+It is important to note that the values in @code{tilevalues} must be ordered 
in the same manner as the tiles, so @code{tilevalues->array[i]} is the value 
that should be given to @code{tl->tiles[i]}.
+The @code{tl->permutation} array must have been initialized before calling 
this function with @code{gal_tile_full_permutation}.
 
-If @code{withblank} is non-zero, then block structure of the tiles will be
-checked and all blank pixels in the block will be blank in the final output
-file also.
+If @code{withblank} is non-zero, then block structure of the tiles will be 
checked and all blank pixels in the block will be blank in the final output 
file also.
 @end deftypefun
 
 @deftypefun {gal_data_t *} gal_tile_full_values_smooth (gal_data_t 
@code{*tilevalues}, struct gal_tile_two_layer_params @code{*tl}, size_t 
@code{width}, size_t @code{numthreads})
-Smooth the given values with a flat kernel of the given @code{width}. This
-cannot be done manually because if @code{tl->workoverch==0}, tiles in
-different channels must not be mixed/smoothed. Also the tiles are
-contiguous within the channel, not within the image, see the description
-under @code{gal_tile_full_permutation}.
+Smooth the given values with a flat kernel of the given @code{width}.
+This cannot be done manually because if @code{tl->workoverch==0}, tiles in 
different channels must not be mixed/smoothed.
+Also the tiles are contiguous within the channel, not within the image, see 
the description under @code{gal_tile_full_permutation}.
 @end deftypefun
 
 @deftypefun size_t gal_tile_full_id_from_coord (struct 
gal_tile_two_layer_params @code{*tl}, size_t @code{*coord})
-Return the ID of the tile that corresponds to the coordinates
-@code{coord}. Having this ID, you can use the @code{tl->tiles} array to get
-to the proper tile or read/write a value into an array that has one value
-per tile.
+Return the ID of the tile that corresponds to the coordinates @code{coord}.
+Having this ID, you can use the @code{tl->tiles} array to get to the proper 
tile or read/write a value into an array that has one value per tile.
 @end deftypefun
 
 @deftypefun void gal_tile_full_free_contents (struct gal_tile_two_layer_params 
@code{*tl})
@@ -32589,85 +33652,67 @@ Free all the allocated arrays within @code{tl}.
 @node Bounding box, Polygons, Tessellation library, Gnuastro library
 @subsection Bounding box (@file{box.h})
 
-Functions related to reporting the bounding box of certain inputs are
-declared in @file{gnuastro/box.h}. All coordinates in this header are in
-the FITS format (first axis is the horizontal and the second axis is
-vertical).
+Functions related to reporting the bounding box of certain inputs are declared 
in @file{gnuastro/box.h}.
+All coordinates in this header are in the FITS format (first axis is the 
horizontal and the second axis is vertical).
 
 @deftypefun void gal_box_bound_ellipse_extent (double @code{a}, double 
@code{b}, double @code{theta_deg}, double @code{*extent})
-Return the maximum extent along each dimension of the given ellipse from
-the center of the ellipse. Therefore this is half the extent of the box in
-each dimension. @code{a} is the ellipse semi-major axis, @code{b} is the
-semi-minor axis, @code{theta_deg} is the position angle in degrees. The
-extent in each dimension is in floating point format and stored in
-@code{extent} which must already be allocated before this function.
+Return the maximum extent along each dimension of the given ellipse from the 
center of the ellipse.
+Therefore this is half the extent of the box in each dimension.
+@code{a} is the ellipse semi-major axis, @code{b} is the semi-minor axis, 
@code{theta_deg} is the position angle in degrees.
+The extent in each dimension is in floating point format and stored in 
@code{extent} which must already be allocated before this function.
 @end deftypefun
 
 @deftypefun void gal_box_bound_ellipse (double @code{a}, double @code{b}, 
double @code{theta_deg}, long @code{*width})
-Any ellipse can be enclosed into a rectangular box. This function will
-write the height and width of that box where @code{width} points to. It
-assumes the center of the ellipse is located within the central pixel of
-the box. @code{a} is the ellipse semi-major axis length, @code{b} is the
-semi-minor axis, @code{theta_deg} is the position angle in degrees. The
-@code{width} array will contain the output size in long integer
-type. @code{width[0]}, and @code{width[1]} are the number of pixels along
-the first and second FITS axis. Since the ellipse center is assumed to be
-in the center of the box, all the values in @code{width} will be an odd
-integer.
+Any ellipse can be enclosed into a rectangular box.
+This function will write the height and width of that box where @code{width} 
points to.
+It assumes the center of the ellipse is located within the central pixel of 
the box.
+@code{a} is the ellipse semi-major axis length, @code{b} is the semi-minor 
axis, @code{theta_deg} is the position angle in degrees.
+The @code{width} array will contain the output size in long integer type.
+@code{width[0]}, and @code{width[1]} are the number of pixels along the first 
and second FITS axis.
+Since the ellipse center is assumed to be in the center of the box, all the 
values in @code{width} will be an odd integer.
 @end deftypefun
 
 @deftypefun void gal_box_bound_ellipsoid_extent (double @code{*semiaxes}, 
double @code{*euler_deg}, double @code{*extent})
-Return the maximum extent along each dimension of the given ellipsoid from
-its center. Therefore this is half the extent of the box in each
-dimension. The semi-axis lengths of the ellipsoid must be present in the 3
-element @code{semiaxis} array. The @code{euler_deg} array contains the
-three ellipsoid Euler angles in degrees. For a description of the Euler
-angles, see description of @code{gal_box_bound_ellipsoid} below. The extent
-in each dimension is in floating point format and stored in @code{extent}
-which must already be allocated before this function.
+Return the maximum extent along each dimension of the given ellipsoid from its 
center.
+Therefore this is half the extent of the box in each dimension.
+The semi-axis lengths of the ellipsoid must be present in the 3 element 
@code{semiaxis} array.
+The @code{euler_deg} array contains the three ellipsoid Euler angles in 
degrees.
+For a description of the Euler angles, see description of 
@code{gal_box_bound_ellipsoid} below.
+The extent in each dimension is in floating point format and stored in 
@code{extent} which must already be allocated before this function.
 @end deftypefun
 
 @deftypefun void gal_box_bound_ellipsoid (double @code{*semiaxes}, double 
@code{*euler_deg}, long @code{*width})
-Any ellipsoid can be enclosed into a rectangular volume/box. The purpose of
-this function is to give the integer size/width of that box. The semi-axes
-lengths of the ellipse must be in the @code{semiaxes} array (with three
-elements). The major axis length must be the first element of
-@code{semiaxes}. The only other condition is that the next two semi-axes
-must both be smaller than the first. The orientation of the major axis is
-defined through three proper Euler angles (ZXZ order in degrees) that are
-given in the @code{euler_deg} array. The @code{width} array will contain
-the output size in long integer type (in FITS axis order). Since the
-ellipsoid center is assumed to be in the center of the box, all the values
-in @code{width} will be an odd integer.
+Any ellipsoid can be enclosed into a rectangular volume/box.
+The purpose of this function is to give the integer size/width of that box.
+The semi-axes lengths of the ellipse must be in the @code{semiaxes} array 
(with three elements).
+The major axis length must be the first element of @code{semiaxes}.
+The only other condition is that the next two semi-axes must both be smaller 
than the first.
+The orientation of the major axis is defined through three proper Euler angles 
(ZXZ order in degrees) that are given in the @code{euler_deg} array.
+The @code{width} array will contain the output size in long integer type (in 
FITS axis order).
+Since the ellipsoid center is assumed to be in the center of the box, all the 
values in @code{width} will be an odd integer.
 
 @cindex Euler angles
-The proper Euler angles can be defined in many ways (which axes to rotate
-about). For a full description of the Euler angles, please see
-@url{https://en.wikipedia.org/wiki/Euler_angles, Wikipedia}. Here we adopt
-the ZXZ (or @mymath{Z_1X_2Z_3}) proper Euler angles were the first rotation
-is done around the Z axis, the second one about the (rotated) X axis and
-the third about the (rotated) Z axis.
+The proper Euler angles can be defined in many ways (which axes to rotate 
about).
+For a full description of the Euler angles, please see 
@url{https://en.wikipedia.org/wiki/Euler_angles, Wikipedia}.
+Here we adopt the ZXZ (or @mymath{Z_1X_2Z_3}) proper Euler angles were the 
first rotation is done around the Z axis, the second one about the (rotated) X 
axis and the third about the (rotated) Z axis.
 @end deftypefun
 
 @deftypefun void gal_box_border_from_center (double @code{center}, size_t 
@code{ndim}, long @code{*width}, long @code{*fpixel}, long @code{*lpixel})
-Given the center coordinates in @code{center} and the @code{width} (along
-each dimension) of a box, return the coordinates of the first
-(@code{fpixel}) and last (@code{lpixel}) pixels. All arrays must have
-@code{ndim} elements (one for each dimension).
+Given the center coordinates in @code{center} and the @code{width} (along each 
dimension) of a box, return the coordinates of the first (@code{fpixel}) and 
last (@code{lpixel}) pixels.
+All arrays must have @code{ndim} elements (one for each dimension).
+@end deftypefun
+
+@deftypefun void gal_box_border_rotate_around_center (long @code{*fpixel}, 
long @code{*lpixel}, size_t @code{ndim}, float @code{rotate_deg})
+Modify the input first and last pixels (@code{fpixel} and @code{lpixel}, that 
you can estimate with @code{gal_box_border_from_center}) to account for the 
given rotation (in units of degrees) in 2D (currently @code{ndim} can only have 
a value of @code{2}).
 @end deftypefun
 
 @deftypefun int gal_box_overlap (long @code{*naxes}, long @code{*fpixel_i}, 
long @code{*lpixel_i}, long @code{*fpixel_o}, long @code{*lpixel_o}, size_t 
@code{ndim})
-An @code{ndim}-dimensional dataset of size @code{naxes} (along each
-dimension, in FITS order) and a box with first and last (inclusive)
-coordinate of @code{fpixel_i} and @code{lpixel_i} is given. This box
-doesn't necessarily have to lie within the dataset, it can be outside of
-it, or only partially overlap. This function will change the values of
-@code{fpixel_i} and @code{lpixel_i} to exactly cover the overlap in the
-input dataset's coordinates.
+An @code{ndim}-dimensional dataset of size @code{naxes} (along each dimension, 
in FITS order) and a box with first and last (inclusive) coordinate of 
@code{fpixel_i} and @code{lpixel_i} is given.
+This box doesn't necessarily have to lie within the dataset, it can be outside 
of it, or only partially overlap.
+This function will change the values of @code{fpixel_i} and @code{lpixel_i} to 
exactly cover the overlap in the input dataset's coordinates.
 
-This function will return 1 if there is an overlap and 0 if there
-isn't. When there is an overlap, the coordinates of the first and last
-pixels of the overlap will be put in @code{fpixel_o} and @code{lpixel_o}.
+This function will return 1 if there is an overlap and 0 if there isn't.
+When there is an overlap, the coordinates of the first and last pixels of the 
overlap will be put in @code{fpixel_o} and @code{lpixel_o}.
 @end deftypefun
 
 
@@ -34263,7 +35308,7 @@ channel borders. Other pixels in the image will not be 
touched. Hence, it
 is much faster.
 @end deftypefun
 
-@node Interpolation, Git wrappers, Convolution functions, Gnuastro library
+@node Interpolation, Color functions, Convolution functions, Gnuastro library
 @subsection Interpolation (@file{interpolate.h})
 
 @cindex Sky line
@@ -34504,8 +35549,48 @@ blank. To see if any blank (non-interpolated) elements 
remain, you can use
 @code{gal_blank_present} on @code{in} after this function is finished.
 @end deftypefun
 
+@node Color functions, Git wrappers, Interpolation, Gnuastro library
+@subsection Color functions (@file{color.h})
+
+@cindex Colors
+The available pre-defined colors in Gnuastro are shown and discussed in 
@ref{Vector graphics colors}.
+This part of Gnuastro is currently in charge of mapping the color names to the 
color IDs and to return the red-green-blue fractions of each color.
+On a terminal that supports 24-bit (true color), you can see the full list of 
color names and a demo of each color with this commadn:
+
+@example
+$ astconvertt --listcolors
+@end example
+
+@noindent
+For each color we have a separate macro that starts with @code{GAL_COLOR_}, 
and ends with the color name in all-caps.
+
+@deffn  Macro GAL_COLOR_INVALID
+@deffnx Macro GAL_COLOR_MEDIUMVIOLETRED
+@deffnx Macro GAL_COLOR_DEEPPINK
+@deffnx Macro GAL_COLOR_*
+The integer identifiers for each of the named colors in Gnuastro.
+Except for the first one (@code{GAL_COLOR_INVALID}), we currently have 140 
colors from the @url{https://en.wikipedia.org/wiki/Web_colors#Extended_colors, 
extended web colors}.
+The full list of colors and a demo is shown in @ref{Vector graphics colors}.
+@end deffn
+
+@noindent
+The functions below can be used to interact with the pre-defined colors:
+
+@deftypefun uint8_t gal_color_name_to_id (char @code{*name})
+Given the name of a color, return the identifier.
+The name matching is not case-sensitive.
+@end deftypefun
+
+@deftypefun {char *} gal_color_id_to_name (uint8_t @code{color})
+Given the ID of a color, return its name.
+@end deftypefun
+
+@deftypefun void gal_color_in_rgb (uint8_t @code{color}, float @code{*f})
+Given the identifier of a color, write the color's red-green-blue fractions in 
the space that @code{f} points to.
+It is upto the caller to have the space for three 32-bit floating point 
numbers to be already allocated before calling this function.
+@end deftypefun
 
-@node Git wrappers, Unit conversion library, Interpolation, Gnuastro library
+@node Git wrappers, Unit conversion library, Color functions, Gnuastro library
 @subsection Git wrappers (@file{git.h})
 
 @cindex Git
@@ -34590,6 +35675,28 @@ Convert magnitudes to counts through the given zero 
point.
 For more on the equation, see @ref{Brightness flux magnitude}.
 @end deftypefun
 
+@deftypefun double gal_units_mag_to_sb (double @code{mag}, double 
@code{area_arcsec2})
+@cindex Magnitude
+@cindex Surface Brightness
+Calculate the surface brightness of a given magnitude, over a certain area in 
units of arcsec@mymath{^2}.
+For more on the equation, see @ref{Brightness flux magnitude}.
+@end deftypefun
+
+@deftypefun double gal_units_sb_to_mag (double @code{sb}, double 
@code{area_arcsec2})
+Calculate the magnitude of a given surface brightness, over a certain area in 
units of arcsec@mymath{^2}.
+For more on the equation, see @ref{Brightness flux magnitude}.
+@end deftypefun
+
+@deftypefun double gal_units_counts_to_sb (double @code{counts}, double 
@code{zeropoint_ab}, double @code{area_arcsec2})
+Calculate the surface brightness of a given count level, over a certain area 
in units of arcsec@mymath{^2}, assuming a certain AB zeropoint.
+For more on the equation, see @ref{Brightness flux magnitude}.
+@end deftypefun
+
+@deftypefun double gal_units_sb_to_counts (double @code{sb}, double 
@code{zeropoint_ab}, double @code{area_arcsec2})
+Calculate the counts corresponding to a given surface brightness, over a 
certain area in units of arcsec@mymath{^2}.
+For more on the equation, see @ref{Brightness flux magnitude}.
+@end deftypefun
+
 @deftypefun double gal_units_counts_to_jy (double @code{counts}, double 
@code{zeropoint_ab})
 @cindex Jansky (Jy)
 @cindex AB Magnitude
diff --git a/doc/plotsrc/Makefile b/doc/plotsrc/Makefile
index 950a2876..1e7b6d02 100644
--- a/doc/plotsrc/Makefile
+++ b/doc/plotsrc/Makefile
@@ -55,6 +55,7 @@ $(finaltarget): all.tex ./tex/*.tex ./conversions.sh | tikz
        && cp tikz/all-figure1.eps ../gnuastro-figures/samplingfreq.eps \
        && cp tikz/all-figure2.eps ../gnuastro-figures/flatplane.eps \
        && cp tikz/all-figure3.eps ../gnuastro-figures/sphereandplane.eps \
+       && cp tikz/all-figure4.eps ../gnuastro-figures/color-names.eps \
        && ./conversions.sh ../gnuastro-figures/ \
        && echo "All necessary images created." > $(finaltarget)
 
diff --git a/doc/plotsrc/all.tex b/doc/plotsrc/all.tex
index a0474a38..9c771805 100644
--- a/doc/plotsrc/all.tex
+++ b/doc/plotsrc/all.tex
@@ -150,4 +150,6 @@ appropriate directory.
 
 \input{tex/sphereandplane.tex}
 
+\input{tex/color-names.tex}
+
 \end{document}
diff --git a/doc/plotsrc/tex/color-names.tex b/doc/plotsrc/tex/color-names.tex
new file mode 100644
index 00000000..fecf82d3
--- /dev/null
+++ b/doc/plotsrc/tex/color-names.tex
@@ -0,0 +1,491 @@
+% This TeX document is part of the manual of the GNU Astronomy
+% Utilities (Gnuastro). A Makefile is also distributed which allows
+% you to compile this TeX file in the desired manner.
+%
+% Original author:
+%     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+% Contributing author(s):
+% Copyright (C) 2022 Free Software Foundation, Inc.
+%
+% Gnuastro is free software: you can redistribute it and/or modify it
+% under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Gnuastro is distributed in the hope that it will be useful, but
+% WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+% General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+
+
+%% To generate the repetative parts of this file, we first wrote the output
+%% of the '--listcolors' option of ConvertType into a table:
+%%
+%%     astconvertt --listcolors | asttable -ocolors.fits
+
+
+
+
+
+%% Special color names to avoid confusing with standard color names. To
+%% generate these, we used the 'colors.fits' above with the following
+%% command:
+%%
+%%    asttable colors.fits -cColor-Name,FRAC-R,FRAC-G,Frac-B \
+%%             | awk '{printf 
"\\definecolor{gnuastro%s}{rgb}{%.2f,%.2f,%.2f}\n", \
+%%                            $1, $2, $3, $4}'
+\definecolor{gnuastromediumvioletred}{rgb}{0.78,0.08,0.52}
+\definecolor{gnuastrodeeppink}{rgb}{1.00,0.08,0.58}
+\definecolor{gnuastropalevioletred}{rgb}{0.86,0.44,0.58}
+\definecolor{gnuastrohotpink}{rgb}{1.00,0.41,0.71}
+\definecolor{gnuastrolightpink}{rgb}{1.00,0.71,0.76}
+\definecolor{gnuastropink}{rgb}{1.00,0.75,0.80}
+\definecolor{gnuastrodarkred}{rgb}{0.55,0.00,0.00}
+\definecolor{gnuastrored}{rgb}{1.00,0.00,0.00}
+\definecolor{gnuastrofirebrick}{rgb}{0.70,0.13,0.13}
+\definecolor{gnuastrocrimson}{rgb}{0.86,0.08,0.24}
+\definecolor{gnuastroindianred}{rgb}{0.80,0.36,0.36}
+\definecolor{gnuastrolightcoral}{rgb}{0.94,0.50,0.50}
+\definecolor{gnuastrosalmon}{rgb}{0.98,0.50,0.45}
+\definecolor{gnuastrodarksalmon}{rgb}{0.91,0.59,0.48}
+\definecolor{gnuastrolightsalmon}{rgb}{1.00,0.63,0.48}
+\definecolor{gnuastroorangered}{rgb}{1.00,0.27,0.00}
+\definecolor{gnuastrotomato}{rgb}{1.00,0.39,0.28}
+\definecolor{gnuastrodarkorange}{rgb}{1.00,0.55,0.00}
+\definecolor{gnuastrocoral}{rgb}{1.00,0.50,0.31}
+\definecolor{gnuastroorange}{rgb}{1.00,0.65,0.00}
+\definecolor{gnuastrodarkkhaki}{rgb}{0.74,0.72,0.42}
+\definecolor{gnuastrogold}{rgb}{1.00,0.84,0.00}
+\definecolor{gnuastrokhaki}{rgb}{0.94,0.90,0.55}
+\definecolor{gnuastropeachpuff}{rgb}{1.00,0.85,0.73}
+\definecolor{gnuastroyellow}{rgb}{1.00,1.00,0.00}
+\definecolor{gnuastropalegoldenrod}{rgb}{0.93,0.91,0.67}
+\definecolor{gnuastromoccasin}{rgb}{1.00,0.89,0.71}
+\definecolor{gnuastropapayawhip}{rgb}{1.00,0.94,0.84}
+\definecolor{gnuastrolightgoldenrodyellow}{rgb}{0.98,0.98,0.82}
+\definecolor{gnuastrolemonchiffon}{rgb}{1.00,0.98,0.80}
+\definecolor{gnuastrolightyellow}{rgb}{1.00,1.00,0.88}
+\definecolor{gnuastromaroon}{rgb}{0.50,0.00,0.00}
+\definecolor{gnuastrobrown}{rgb}{0.65,0.16,0.16}
+\definecolor{gnuastrosaddlebrown}{rgb}{0.55,0.27,0.07}
+\definecolor{gnuastrosienna}{rgb}{0.63,0.32,0.18}
+\definecolor{gnuastrochocolate}{rgb}{0.82,0.41,0.12}
+\definecolor{gnuastrodarkgoldenrod}{rgb}{0.72,0.53,0.04}
+\definecolor{gnuastroperu}{rgb}{0.80,0.52,0.25}
+\definecolor{gnuastrorosybrown}{rgb}{0.74,0.56,0.56}
+\definecolor{gnuastrogoldenrod}{rgb}{0.85,0.65,0.13}
+\definecolor{gnuastrosandybrown}{rgb}{0.96,0.64,0.38}
+\definecolor{gnuastrotan}{rgb}{0.82,0.71,0.55}
+\definecolor{gnuastroburlywood}{rgb}{0.87,0.72,0.53}
+\definecolor{gnuastrowheat}{rgb}{0.96,0.87,0.70}
+\definecolor{gnuastronavajowhite}{rgb}{1.00,0.87,0.68}
+\definecolor{gnuastrobisque}{rgb}{1.00,0.89,0.77}
+\definecolor{gnuastroblanchedalmond}{rgb}{1.00,0.92,0.80}
+\definecolor{gnuastrocornsilk}{rgb}{1.00,0.97,0.86}
+\definecolor{gnuastrodarkgreen}{rgb}{0.00,0.39,0.00}
+\definecolor{gnuastrogreen}{rgb}{0.00,0.50,0.00}
+\definecolor{gnuastrodarkolivegreen}{rgb}{0.33,0.42,0.18}
+\definecolor{gnuastroforestgreen}{rgb}{0.13,0.55,0.13}
+\definecolor{gnuastroseagreen}{rgb}{0.18,0.55,0.34}
+\definecolor{gnuastroolive}{rgb}{0.50,0.50,0.00}
+\definecolor{gnuastroolivedrab}{rgb}{0.42,0.56,0.14}
+\definecolor{gnuastromediumseagreen}{rgb}{0.24,0.70,0.44}
+\definecolor{gnuastrolimegreen}{rgb}{0.20,0.80,0.20}
+\definecolor{gnuastrolime}{rgb}{0.00,1.00,0.00}
+\definecolor{gnuastrospringgreen}{rgb}{0.00,1.00,0.50}
+\definecolor{gnuastromediumspringgreen}{rgb}{0.00,0.98,0.60}
+\definecolor{gnuastrodarkseagreen}{rgb}{0.56,0.74,0.56}
+\definecolor{gnuastromediumaquamarine}{rgb}{0.40,0.80,0.67}
+\definecolor{gnuastroyellowgreen}{rgb}{0.60,0.80,0.20}
+\definecolor{gnuastrolawngreen}{rgb}{0.49,0.99,0.00}
+\definecolor{gnuastrochartreuse}{rgb}{0.50,1.00,0.00}
+\definecolor{gnuastrolightgreen}{rgb}{0.56,0.93,0.56}
+\definecolor{gnuastrogreenyellow}{rgb}{0.68,1.00,0.18}
+\definecolor{gnuastropalegreen}{rgb}{0.60,0.98,0.60}
+\definecolor{gnuastroteal}{rgb}{0.00,0.50,0.50}
+\definecolor{gnuastrodarkcyan}{rgb}{0.00,0.55,0.55}
+\definecolor{gnuastrolightseagreen}{rgb}{0.13,0.70,0.67}
+\definecolor{gnuastrocadetblue}{rgb}{0.37,0.62,0.63}
+\definecolor{gnuastrodarkturquoise}{rgb}{0.00,0.81,0.82}
+\definecolor{gnuastromediumturquoise}{rgb}{0.28,0.82,0.80}
+\definecolor{gnuastroturquoise}{rgb}{0.25,0.88,0.82}
+\definecolor{gnuastroaqua}{rgb}{0.00,1.00,1.00}
+\definecolor{gnuastrocyan}{rgb}{0.00,1.00,1.00}
+\definecolor{gnuastroaquamarine}{rgb}{0.50,1.00,0.83}
+\definecolor{gnuastropaleturquoise}{rgb}{0.69,0.93,0.93}
+\definecolor{gnuastrolightcyan}{rgb}{0.88,1.00,1.00}
+\definecolor{gnuastromidnightblue}{rgb}{0.10,0.10,0.44}
+\definecolor{gnuastronavy}{rgb}{0.00,0.00,0.50}
+\definecolor{gnuastrodarkblue}{rgb}{0.00,0.00,0.55}
+\definecolor{gnuastromediumblue}{rgb}{0.00,0.00,0.80}
+\definecolor{gnuastroblue}{rgb}{0.00,0.00,1.00}
+\definecolor{gnuastroroyalblue}{rgb}{0.25,0.41,0.88}
+\definecolor{gnuastrosteelblue}{rgb}{0.27,0.51,0.71}
+\definecolor{gnuastrododgerblue}{rgb}{0.12,0.56,1.00}
+\definecolor{gnuastrodeepskyblue}{rgb}{0.00,0.75,1.00}
+\definecolor{gnuastrocornflowerblue}{rgb}{0.39,0.58,0.93}
+\definecolor{gnuastroskyblue}{rgb}{0.53,0.81,0.92}
+\definecolor{gnuastrolightskyblue}{rgb}{0.53,0.81,0.98}
+\definecolor{gnuastrolightsteelblue}{rgb}{0.69,0.77,0.87}
+\definecolor{gnuastrolightblue}{rgb}{0.68,0.85,0.90}
+\definecolor{gnuastropowderblue}{rgb}{0.69,0.88,0.90}
+\definecolor{gnuastroindigo}{rgb}{0.29,0.00,0.51}
+\definecolor{gnuastropurple}{rgb}{0.50,0.00,0.50}
+\definecolor{gnuastrodarkmagenta}{rgb}{0.55,0.00,0.55}
+\definecolor{gnuastrodarkviolet}{rgb}{0.58,0.00,0.83}
+\definecolor{gnuastrodarkslateblue}{rgb}{0.28,0.24,0.55}
+\definecolor{gnuastroblueviolet}{rgb}{0.54,0.17,0.89}
+\definecolor{gnuastrodarkorchid}{rgb}{0.60,0.20,0.80}
+\definecolor{gnuastrofuchsia}{rgb}{1.00,0.00,1.00}
+\definecolor{gnuastromagenta}{rgb}{1.00,0.00,1.00}
+\definecolor{gnuastroslateblue}{rgb}{0.42,0.35,0.80}
+\definecolor{gnuastromediumslateblue}{rgb}{0.48,0.41,0.93}
+\definecolor{gnuastromediumorchid}{rgb}{0.73,0.33,0.83}
+\definecolor{gnuastromediumpurple}{rgb}{0.58,0.44,0.86}
+\definecolor{gnuastroorchid}{rgb}{0.85,0.44,0.84}
+\definecolor{gnuastroviolet}{rgb}{0.93,0.51,0.93}
+\definecolor{gnuastroplum}{rgb}{0.87,0.63,0.87}
+\definecolor{gnuastrothistle}{rgb}{0.85,0.75,0.85}
+\definecolor{gnuastrolavender}{rgb}{0.90,0.90,0.98}
+\definecolor{gnuastromistyrose}{rgb}{1.00,0.89,0.88}
+\definecolor{gnuastroantiquewhite}{rgb}{0.98,0.92,0.84}
+\definecolor{gnuastrolinen}{rgb}{0.98,0.94,0.90}
+\definecolor{gnuastrobeige}{rgb}{0.96,0.96,0.86}
+\definecolor{gnuastrowhitesmoke}{rgb}{0.96,0.96,0.96}
+\definecolor{gnuastrolavenderblush}{rgb}{1.00,0.94,0.96}
+\definecolor{gnuastrooldlace}{rgb}{0.99,0.96,0.90}
+\definecolor{gnuastroaliceblue}{rgb}{0.94,0.97,1.00}
+\definecolor{gnuastroseashell}{rgb}{1.00,0.96,0.93}
+\definecolor{gnuastroghostwhite}{rgb}{0.97,0.97,1.00}
+\definecolor{gnuastrohoneydew}{rgb}{0.94,1.00,0.94}
+\definecolor{gnuastrofloralwhite}{rgb}{1.00,0.98,0.94}
+\definecolor{gnuastroazure}{rgb}{0.94,1.00,1.00}
+\definecolor{gnuastromintcream}{rgb}{0.96,1.00,0.98}
+\definecolor{gnuastrosnow}{rgb}{1.00,0.98,0.98}
+\definecolor{gnuastroivory}{rgb}{1.00,1.00,0.94}
+\definecolor{gnuastrowhite}{rgb}{1.00,1.00,1.00}
+\definecolor{gnuastroblack}{rgb}{0.00,0.00,0.00}
+\definecolor{gnuastrodarkslategray}{rgb}{0.18,0.31,0.31}
+\definecolor{gnuastrodimgray}{rgb}{0.41,0.41,0.41}
+\definecolor{gnuastroslategray}{rgb}{0.44,0.50,0.56}
+\definecolor{gnuastrogray}{rgb}{0.50,0.50,0.50}
+\definecolor{gnuastrolightslategray}{rgb}{0.47,0.53,0.60}
+\definecolor{gnuastrodarkgray}{rgb}{0.66,0.66,0.66}
+\definecolor{gnuastrosilver}{rgb}{0.75,0.75,0.75}
+\definecolor{gnuastrolightgray}{rgb}{0.83,0.83,0.83}
+\definecolor{gnuastrogainsboro}{rgb}{0.86,0.86,0.86}
+
+
+
+%% Using the 'colors.fits' file above, the repetative parts below were
+%% constructed with this command. Colors with long names will be treated
+%% specially: their code will be printed in the next line.
+%%
+%% asttable colors.fits -cColor-ID,Color-Name \
+%%     | awk -vspacing=0.008 -vboxw=0.02 -vnrows=20 -vcolwidth=0.18 \
+%%           '{ \
+%%              if( $2=="lightgoldenrodyellow" \
+%%                  || $2=="mediumspringgreen" ) { \
+%%            special=1;
+%%              } \
+%%          else special=0; \
+%%              printf("\\draw [fill=gnuastro%s] (%f\\linewidth,%f\\linewidth) 
rectangle (%f\\linewidth,%f\\linewidth);\n", \
+%%                     $2, x, y, x+boxw, y+boxw); \
+%%              printf("\\draw [anchor=south west, inner sep=1, outer sep=0] 
(%f\\linewidth,%f\\linewidth) node {\\texttt{%s%s}};\n", \
+%%                         x+boxw, y, $2, special==0?" ("$1")":""); \
+%%              if(special==1) \
+%%                printf("\\draw [anchor=south west, inner sep=0, outer sep=0] 
(%f\\linewidth,%f\\linewidth) node {\\texttt{(%d)}};\n", \
+%%                         x+boxw, y-boxw/2, NR); \
+%%              y-=boxw+spacing; \
+%%              if(NR%nrows==0) {x+=colwidth; y=0} }' > colors.tex
+\begin{tikzpicture}
+\scriptsize
+\draw [fill=gnuastromediumvioletred] (0.000000\linewidth,0.000000\linewidth) 
rectangle (0.020000\linewidth,0.020000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,0.000000\linewidth) node {\texttt{mediumvioletred (1)}};
+\draw [fill=gnuastrodeeppink] (0.000000\linewidth,-0.028000\linewidth) 
rectangle (0.020000\linewidth,-0.008000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.028000\linewidth) node {\texttt{deeppink (2)}};
+\draw [fill=gnuastropalevioletred] (0.000000\linewidth,-0.056000\linewidth) 
rectangle (0.020000\linewidth,-0.036000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.056000\linewidth) node {\texttt{palevioletred (3)}};
+\draw [fill=gnuastrohotpink] (0.000000\linewidth,-0.084000\linewidth) 
rectangle (0.020000\linewidth,-0.064000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.084000\linewidth) node {\texttt{hotpink (4)}};
+\draw [fill=gnuastrolightpink] (0.000000\linewidth,-0.112000\linewidth) 
rectangle (0.020000\linewidth,-0.092000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.112000\linewidth) node {\texttt{lightpink (5)}};
+\draw [fill=gnuastropink] (0.000000\linewidth,-0.140000\linewidth) rectangle 
(0.020000\linewidth,-0.120000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.140000\linewidth) node {\texttt{pink (6)}};
+\draw [fill=gnuastrodarkred] (0.000000\linewidth,-0.168000\linewidth) 
rectangle (0.020000\linewidth,-0.148000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.168000\linewidth) node {\texttt{darkred (7)}};
+\draw [fill=gnuastrored] (0.000000\linewidth,-0.196000\linewidth) rectangle 
(0.020000\linewidth,-0.176000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.196000\linewidth) node {\texttt{red (8)}};
+\draw [fill=gnuastrofirebrick] (0.000000\linewidth,-0.224000\linewidth) 
rectangle (0.020000\linewidth,-0.204000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.224000\linewidth) node {\texttt{firebrick (9)}};
+\draw [fill=gnuastrocrimson] (0.000000\linewidth,-0.252000\linewidth) 
rectangle (0.020000\linewidth,-0.232000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.252000\linewidth) node {\texttt{crimson (10)}};
+\draw [fill=gnuastroindianred] (0.000000\linewidth,-0.280000\linewidth) 
rectangle (0.020000\linewidth,-0.260000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.280000\linewidth) node {\texttt{indianred (11)}};
+\draw [fill=gnuastrolightcoral] (0.000000\linewidth,-0.308000\linewidth) 
rectangle (0.020000\linewidth,-0.288000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.308000\linewidth) node {\texttt{lightcoral (12)}};
+\draw [fill=gnuastrosalmon] (0.000000\linewidth,-0.336000\linewidth) rectangle 
(0.020000\linewidth,-0.316000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.336000\linewidth) node {\texttt{salmon (13)}};
+\draw [fill=gnuastrodarksalmon] (0.000000\linewidth,-0.364000\linewidth) 
rectangle (0.020000\linewidth,-0.344000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.364000\linewidth) node {\texttt{darksalmon (14)}};
+\draw [fill=gnuastrolightsalmon] (0.000000\linewidth,-0.392000\linewidth) 
rectangle (0.020000\linewidth,-0.372000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.392000\linewidth) node {\texttt{lightsalmon (15)}};
+\draw [fill=gnuastroorangered] (0.000000\linewidth,-0.420000\linewidth) 
rectangle (0.020000\linewidth,-0.400000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.420000\linewidth) node {\texttt{orangered (16)}};
+\draw [fill=gnuastrotomato] (0.000000\linewidth,-0.448000\linewidth) rectangle 
(0.020000\linewidth,-0.428000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.448000\linewidth) node {\texttt{tomato (17)}};
+\draw [fill=gnuastrodarkorange] (0.000000\linewidth,-0.476000\linewidth) 
rectangle (0.020000\linewidth,-0.456000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.476000\linewidth) node {\texttt{darkorange (18)}};
+\draw [fill=gnuastrocoral] (0.000000\linewidth,-0.504000\linewidth) rectangle 
(0.020000\linewidth,-0.484000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.504000\linewidth) node {\texttt{coral (19)}};
+\draw [fill=gnuastroorange] (0.000000\linewidth,-0.532000\linewidth) rectangle 
(0.020000\linewidth,-0.512000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.020000\linewidth,-0.532000\linewidth) node {\texttt{orange (20)}};
+\draw [fill=gnuastrodarkkhaki] (0.180000\linewidth,0.000000\linewidth) 
rectangle (0.200000\linewidth,0.020000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,0.000000\linewidth) node {\texttt{darkkhaki (21)}};
+\draw [fill=gnuastrogold] (0.180000\linewidth,-0.028000\linewidth) rectangle 
(0.200000\linewidth,-0.008000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.028000\linewidth) node {\texttt{gold (22)}};
+\draw [fill=gnuastrokhaki] (0.180000\linewidth,-0.056000\linewidth) rectangle 
(0.200000\linewidth,-0.036000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.056000\linewidth) node {\texttt{khaki (23)}};
+\draw [fill=gnuastropeachpuff] (0.180000\linewidth,-0.084000\linewidth) 
rectangle (0.200000\linewidth,-0.064000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.084000\linewidth) node {\texttt{peachpuff (24)}};
+\draw [fill=gnuastroyellow] (0.180000\linewidth,-0.112000\linewidth) rectangle 
(0.200000\linewidth,-0.092000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.112000\linewidth) node {\texttt{yellow (25)}};
+\draw [fill=gnuastropalegoldenrod] (0.180000\linewidth,-0.140000\linewidth) 
rectangle (0.200000\linewidth,-0.120000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.140000\linewidth) node {\texttt{palegoldenrod (26)}};
+\draw [fill=gnuastromoccasin] (0.180000\linewidth,-0.168000\linewidth) 
rectangle (0.200000\linewidth,-0.148000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.168000\linewidth) node {\texttt{moccasin (27)}};
+\draw [fill=gnuastropapayawhip] (0.180000\linewidth,-0.196000\linewidth) 
rectangle (0.200000\linewidth,-0.176000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.196000\linewidth) node {\texttt{papayawhip (28)}};
+\draw [fill=gnuastrolightgoldenrodyellow] 
(0.180000\linewidth,-0.224000\linewidth) rectangle 
(0.200000\linewidth,-0.204000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.224000\linewidth) node {\texttt{lightgoldenrodyellow}};
+\draw [anchor=south west, inner sep=0, outer sep=0] 
(0.200000\linewidth,-0.234000\linewidth) node {\texttt{(29)}};
+\draw [fill=gnuastrolemonchiffon] (0.180000\linewidth,-0.252000\linewidth) 
rectangle (0.200000\linewidth,-0.232000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.252000\linewidth) node {\texttt{lemonchiffon (30)}};
+\draw [fill=gnuastrolightyellow] (0.180000\linewidth,-0.280000\linewidth) 
rectangle (0.200000\linewidth,-0.260000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.280000\linewidth) node {\texttt{lightyellow (31)}};
+\draw [fill=gnuastromaroon] (0.180000\linewidth,-0.308000\linewidth) rectangle 
(0.200000\linewidth,-0.288000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.308000\linewidth) node {\texttt{maroon (32)}};
+\draw [fill=gnuastrobrown] (0.180000\linewidth,-0.336000\linewidth) rectangle 
(0.200000\linewidth,-0.316000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.336000\linewidth) node {\texttt{brown (33)}};
+\draw [fill=gnuastrosaddlebrown] (0.180000\linewidth,-0.364000\linewidth) 
rectangle (0.200000\linewidth,-0.344000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.364000\linewidth) node {\texttt{saddlebrown (34)}};
+\draw [fill=gnuastrosienna] (0.180000\linewidth,-0.392000\linewidth) rectangle 
(0.200000\linewidth,-0.372000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.392000\linewidth) node {\texttt{sienna (35)}};
+\draw [fill=gnuastrochocolate] (0.180000\linewidth,-0.420000\linewidth) 
rectangle (0.200000\linewidth,-0.400000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.420000\linewidth) node {\texttt{chocolate (36)}};
+\draw [fill=gnuastrodarkgoldenrod] (0.180000\linewidth,-0.448000\linewidth) 
rectangle (0.200000\linewidth,-0.428000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.448000\linewidth) node {\texttt{darkgoldenrod (37)}};
+\draw [fill=gnuastroperu] (0.180000\linewidth,-0.476000\linewidth) rectangle 
(0.200000\linewidth,-0.456000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.476000\linewidth) node {\texttt{peru (38)}};
+\draw [fill=gnuastrorosybrown] (0.180000\linewidth,-0.504000\linewidth) 
rectangle (0.200000\linewidth,-0.484000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.504000\linewidth) node {\texttt{rosybrown (39)}};
+\draw [fill=gnuastrogoldenrod] (0.180000\linewidth,-0.532000\linewidth) 
rectangle (0.200000\linewidth,-0.512000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.200000\linewidth,-0.532000\linewidth) node {\texttt{goldenrod (40)}};
+\draw [fill=gnuastrosandybrown] (0.360000\linewidth,0.000000\linewidth) 
rectangle (0.380000\linewidth,0.020000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,0.000000\linewidth) node {\texttt{sandybrown (41)}};
+\draw [fill=gnuastrotan] (0.360000\linewidth,-0.028000\linewidth) rectangle 
(0.380000\linewidth,-0.008000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.028000\linewidth) node {\texttt{tan (42)}};
+\draw [fill=gnuastroburlywood] (0.360000\linewidth,-0.056000\linewidth) 
rectangle (0.380000\linewidth,-0.036000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.056000\linewidth) node {\texttt{burlywood (43)}};
+\draw [fill=gnuastrowheat] (0.360000\linewidth,-0.084000\linewidth) rectangle 
(0.380000\linewidth,-0.064000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.084000\linewidth) node {\texttt{wheat (44)}};
+\draw [fill=gnuastronavajowhite] (0.360000\linewidth,-0.112000\linewidth) 
rectangle (0.380000\linewidth,-0.092000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.112000\linewidth) node {\texttt{navajowhite (45)}};
+\draw [fill=gnuastrobisque] (0.360000\linewidth,-0.140000\linewidth) rectangle 
(0.380000\linewidth,-0.120000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.140000\linewidth) node {\texttt{bisque (46)}};
+\draw [fill=gnuastroblanchedalmond] (0.360000\linewidth,-0.168000\linewidth) 
rectangle (0.380000\linewidth,-0.148000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.168000\linewidth) node {\texttt{blanchedalmond (47)}};
+\draw [fill=gnuastrocornsilk] (0.360000\linewidth,-0.196000\linewidth) 
rectangle (0.380000\linewidth,-0.176000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.196000\linewidth) node {\texttt{cornsilk (48)}};
+\draw [fill=gnuastrodarkgreen] (0.360000\linewidth,-0.224000\linewidth) 
rectangle (0.380000\linewidth,-0.204000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.224000\linewidth) node {\texttt{darkgreen (49)}};
+\draw [fill=gnuastrogreen] (0.360000\linewidth,-0.252000\linewidth) rectangle 
(0.380000\linewidth,-0.232000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.252000\linewidth) node {\texttt{green (50)}};
+\draw [fill=gnuastrodarkolivegreen] (0.360000\linewidth,-0.280000\linewidth) 
rectangle (0.380000\linewidth,-0.260000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.280000\linewidth) node {\texttt{darkolivegreen (51)}};
+\draw [fill=gnuastroforestgreen] (0.360000\linewidth,-0.308000\linewidth) 
rectangle (0.380000\linewidth,-0.288000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.308000\linewidth) node {\texttt{forestgreen (52)}};
+\draw [fill=gnuastroseagreen] (0.360000\linewidth,-0.336000\linewidth) 
rectangle (0.380000\linewidth,-0.316000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.336000\linewidth) node {\texttt{seagreen (53)}};
+\draw [fill=gnuastroolive] (0.360000\linewidth,-0.364000\linewidth) rectangle 
(0.380000\linewidth,-0.344000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.364000\linewidth) node {\texttt{olive (54)}};
+\draw [fill=gnuastroolivedrab] (0.360000\linewidth,-0.392000\linewidth) 
rectangle (0.380000\linewidth,-0.372000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.392000\linewidth) node {\texttt{olivedrab (55)}};
+\draw [fill=gnuastromediumseagreen] (0.360000\linewidth,-0.420000\linewidth) 
rectangle (0.380000\linewidth,-0.400000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.420000\linewidth) node {\texttt{mediumseagreen (56)}};
+\draw [fill=gnuastrolimegreen] (0.360000\linewidth,-0.448000\linewidth) 
rectangle (0.380000\linewidth,-0.428000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.448000\linewidth) node {\texttt{limegreen (57)}};
+\draw [fill=gnuastrolime] (0.360000\linewidth,-0.476000\linewidth) rectangle 
(0.380000\linewidth,-0.456000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.476000\linewidth) node {\texttt{lime (58)}};
+\draw [fill=gnuastrospringgreen] (0.360000\linewidth,-0.504000\linewidth) 
rectangle (0.380000\linewidth,-0.484000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.504000\linewidth) node {\texttt{springgreen (59)}};
+\draw [fill=gnuastromediumspringgreen] 
(0.360000\linewidth,-0.532000\linewidth) rectangle 
(0.380000\linewidth,-0.512000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.380000\linewidth,-0.532000\linewidth) node {\texttt{mediumspringgreen}};
+\draw [anchor=south west, inner sep=0, outer sep=0] 
(0.380000\linewidth,-0.542000\linewidth) node {\texttt{(60)}};
+\draw [fill=gnuastrodarkseagreen] (0.540000\linewidth,0.000000\linewidth) 
rectangle (0.560000\linewidth,0.020000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,0.000000\linewidth) node {\texttt{darkseagreen (61)}};
+\draw [fill=gnuastromediumaquamarine] (0.540000\linewidth,-0.028000\linewidth) 
rectangle (0.560000\linewidth,-0.008000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.028000\linewidth) node {\texttt{mediumaquamarine (62)}};
+\draw [fill=gnuastroyellowgreen] (0.540000\linewidth,-0.056000\linewidth) 
rectangle (0.560000\linewidth,-0.036000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.056000\linewidth) node {\texttt{yellowgreen (63)}};
+\draw [fill=gnuastrolawngreen] (0.540000\linewidth,-0.084000\linewidth) 
rectangle (0.560000\linewidth,-0.064000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.084000\linewidth) node {\texttt{lawngreen (64)}};
+\draw [fill=gnuastrochartreuse] (0.540000\linewidth,-0.112000\linewidth) 
rectangle (0.560000\linewidth,-0.092000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.112000\linewidth) node {\texttt{chartreuse (65)}};
+\draw [fill=gnuastrolightgreen] (0.540000\linewidth,-0.140000\linewidth) 
rectangle (0.560000\linewidth,-0.120000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.140000\linewidth) node {\texttt{lightgreen (66)}};
+\draw [fill=gnuastrogreenyellow] (0.540000\linewidth,-0.168000\linewidth) 
rectangle (0.560000\linewidth,-0.148000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.168000\linewidth) node {\texttt{greenyellow (67)}};
+\draw [fill=gnuastropalegreen] (0.540000\linewidth,-0.196000\linewidth) 
rectangle (0.560000\linewidth,-0.176000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.196000\linewidth) node {\texttt{palegreen (68)}};
+\draw [fill=gnuastroteal] (0.540000\linewidth,-0.224000\linewidth) rectangle 
(0.560000\linewidth,-0.204000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.224000\linewidth) node {\texttt{teal (69)}};
+\draw [fill=gnuastrodarkcyan] (0.540000\linewidth,-0.252000\linewidth) 
rectangle (0.560000\linewidth,-0.232000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.252000\linewidth) node {\texttt{darkcyan (70)}};
+\draw [fill=gnuastrolightseagreen] (0.540000\linewidth,-0.280000\linewidth) 
rectangle (0.560000\linewidth,-0.260000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.280000\linewidth) node {\texttt{lightseagreen (71)}};
+\draw [fill=gnuastrocadetblue] (0.540000\linewidth,-0.308000\linewidth) 
rectangle (0.560000\linewidth,-0.288000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.308000\linewidth) node {\texttt{cadetblue (72)}};
+\draw [fill=gnuastrodarkturquoise] (0.540000\linewidth,-0.336000\linewidth) 
rectangle (0.560000\linewidth,-0.316000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.336000\linewidth) node {\texttt{darkturquoise (73)}};
+\draw [fill=gnuastromediumturquoise] (0.540000\linewidth,-0.364000\linewidth) 
rectangle (0.560000\linewidth,-0.344000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.364000\linewidth) node {\texttt{mediumturquoise (74)}};
+\draw [fill=gnuastroturquoise] (0.540000\linewidth,-0.392000\linewidth) 
rectangle (0.560000\linewidth,-0.372000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.392000\linewidth) node {\texttt{turquoise (75)}};
+\draw [fill=gnuastroaqua] (0.540000\linewidth,-0.420000\linewidth) rectangle 
(0.560000\linewidth,-0.400000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.420000\linewidth) node {\texttt{aqua (76)}};
+\draw [fill=gnuastrocyan] (0.540000\linewidth,-0.448000\linewidth) rectangle 
(0.560000\linewidth,-0.428000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.448000\linewidth) node {\texttt{cyan (77)}};
+\draw [fill=gnuastroaquamarine] (0.540000\linewidth,-0.476000\linewidth) 
rectangle (0.560000\linewidth,-0.456000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.476000\linewidth) node {\texttt{aquamarine (78)}};
+\draw [fill=gnuastropaleturquoise] (0.540000\linewidth,-0.504000\linewidth) 
rectangle (0.560000\linewidth,-0.484000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.504000\linewidth) node {\texttt{paleturquoise (79)}};
+\draw [fill=gnuastrolightcyan] (0.540000\linewidth,-0.532000\linewidth) 
rectangle (0.560000\linewidth,-0.512000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.560000\linewidth,-0.532000\linewidth) node {\texttt{lightcyan (80)}};
+\draw [fill=gnuastromidnightblue] (0.720000\linewidth,0.000000\linewidth) 
rectangle (0.740000\linewidth,0.020000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,0.000000\linewidth) node {\texttt{midnightblue (81)}};
+\draw [fill=gnuastronavy] (0.720000\linewidth,-0.028000\linewidth) rectangle 
(0.740000\linewidth,-0.008000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.028000\linewidth) node {\texttt{navy (82)}};
+\draw [fill=gnuastrodarkblue] (0.720000\linewidth,-0.056000\linewidth) 
rectangle (0.740000\linewidth,-0.036000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.056000\linewidth) node {\texttt{darkblue (83)}};
+\draw [fill=gnuastromediumblue] (0.720000\linewidth,-0.084000\linewidth) 
rectangle (0.740000\linewidth,-0.064000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.084000\linewidth) node {\texttt{mediumblue (84)}};
+\draw [fill=gnuastroblue] (0.720000\linewidth,-0.112000\linewidth) rectangle 
(0.740000\linewidth,-0.092000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.112000\linewidth) node {\texttt{blue (85)}};
+\draw [fill=gnuastroroyalblue] (0.720000\linewidth,-0.140000\linewidth) 
rectangle (0.740000\linewidth,-0.120000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.140000\linewidth) node {\texttt{royalblue (86)}};
+\draw [fill=gnuastrosteelblue] (0.720000\linewidth,-0.168000\linewidth) 
rectangle (0.740000\linewidth,-0.148000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.168000\linewidth) node {\texttt{steelblue (87)}};
+\draw [fill=gnuastrododgerblue] (0.720000\linewidth,-0.196000\linewidth) 
rectangle (0.740000\linewidth,-0.176000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.196000\linewidth) node {\texttt{dodgerblue (88)}};
+\draw [fill=gnuastrodeepskyblue] (0.720000\linewidth,-0.224000\linewidth) 
rectangle (0.740000\linewidth,-0.204000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.224000\linewidth) node {\texttt{deepskyblue (89)}};
+\draw [fill=gnuastrocornflowerblue] (0.720000\linewidth,-0.252000\linewidth) 
rectangle (0.740000\linewidth,-0.232000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.252000\linewidth) node {\texttt{cornflowerblue (90)}};
+\draw [fill=gnuastroskyblue] (0.720000\linewidth,-0.280000\linewidth) 
rectangle (0.740000\linewidth,-0.260000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.280000\linewidth) node {\texttt{skyblue (91)}};
+\draw [fill=gnuastrolightskyblue] (0.720000\linewidth,-0.308000\linewidth) 
rectangle (0.740000\linewidth,-0.288000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.308000\linewidth) node {\texttt{lightskyblue (92)}};
+\draw [fill=gnuastrolightsteelblue] (0.720000\linewidth,-0.336000\linewidth) 
rectangle (0.740000\linewidth,-0.316000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.336000\linewidth) node {\texttt{lightsteelblue (93)}};
+\draw [fill=gnuastrolightblue] (0.720000\linewidth,-0.364000\linewidth) 
rectangle (0.740000\linewidth,-0.344000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.364000\linewidth) node {\texttt{lightblue (94)}};
+\draw [fill=gnuastropowderblue] (0.720000\linewidth,-0.392000\linewidth) 
rectangle (0.740000\linewidth,-0.372000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.392000\linewidth) node {\texttt{powderblue (95)}};
+\draw [fill=gnuastroindigo] (0.720000\linewidth,-0.420000\linewidth) rectangle 
(0.740000\linewidth,-0.400000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.420000\linewidth) node {\texttt{indigo (96)}};
+\draw [fill=gnuastropurple] (0.720000\linewidth,-0.448000\linewidth) rectangle 
(0.740000\linewidth,-0.428000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.448000\linewidth) node {\texttt{purple (97)}};
+\draw [fill=gnuastrodarkmagenta] (0.720000\linewidth,-0.476000\linewidth) 
rectangle (0.740000\linewidth,-0.456000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.476000\linewidth) node {\texttt{darkmagenta (98)}};
+\draw [fill=gnuastrodarkviolet] (0.720000\linewidth,-0.504000\linewidth) 
rectangle (0.740000\linewidth,-0.484000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.504000\linewidth) node {\texttt{darkviolet (99)}};
+\draw [fill=gnuastrodarkslateblue] (0.720000\linewidth,-0.532000\linewidth) 
rectangle (0.740000\linewidth,-0.512000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.740000\linewidth,-0.532000\linewidth) node {\texttt{darkslateblue (100)}};
+\draw [fill=gnuastroblueviolet] (0.900000\linewidth,0.000000\linewidth) 
rectangle (0.920000\linewidth,0.020000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,0.000000\linewidth) node {\texttt{blueviolet (101)}};
+\draw [fill=gnuastrodarkorchid] (0.900000\linewidth,-0.028000\linewidth) 
rectangle (0.920000\linewidth,-0.008000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.028000\linewidth) node {\texttt{darkorchid (102)}};
+\draw [fill=gnuastrofuchsia] (0.900000\linewidth,-0.056000\linewidth) 
rectangle (0.920000\linewidth,-0.036000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.056000\linewidth) node {\texttt{fuchsia (103)}};
+\draw [fill=gnuastromagenta] (0.900000\linewidth,-0.084000\linewidth) 
rectangle (0.920000\linewidth,-0.064000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.084000\linewidth) node {\texttt{magenta (104)}};
+\draw [fill=gnuastroslateblue] (0.900000\linewidth,-0.112000\linewidth) 
rectangle (0.920000\linewidth,-0.092000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.112000\linewidth) node {\texttt{slateblue (105)}};
+\draw [fill=gnuastromediumslateblue] (0.900000\linewidth,-0.140000\linewidth) 
rectangle (0.920000\linewidth,-0.120000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.140000\linewidth) node {\texttt{mediumslateblue (106)}};
+\draw [fill=gnuastromediumorchid] (0.900000\linewidth,-0.168000\linewidth) 
rectangle (0.920000\linewidth,-0.148000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.168000\linewidth) node {\texttt{mediumorchid (107)}};
+\draw [fill=gnuastromediumpurple] (0.900000\linewidth,-0.196000\linewidth) 
rectangle (0.920000\linewidth,-0.176000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.196000\linewidth) node {\texttt{mediumpurple (108)}};
+\draw [fill=gnuastroorchid] (0.900000\linewidth,-0.224000\linewidth) rectangle 
(0.920000\linewidth,-0.204000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.224000\linewidth) node {\texttt{orchid (109)}};
+\draw [fill=gnuastroviolet] (0.900000\linewidth,-0.252000\linewidth) rectangle 
(0.920000\linewidth,-0.232000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.252000\linewidth) node {\texttt{violet (110)}};
+\draw [fill=gnuastroplum] (0.900000\linewidth,-0.280000\linewidth) rectangle 
(0.920000\linewidth,-0.260000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.280000\linewidth) node {\texttt{plum (111)}};
+\draw [fill=gnuastrothistle] (0.900000\linewidth,-0.308000\linewidth) 
rectangle (0.920000\linewidth,-0.288000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.308000\linewidth) node {\texttt{thistle (112)}};
+\draw [fill=gnuastrolavender] (0.900000\linewidth,-0.336000\linewidth) 
rectangle (0.920000\linewidth,-0.316000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.336000\linewidth) node {\texttt{lavender (113)}};
+\draw [fill=gnuastromistyrose] (0.900000\linewidth,-0.364000\linewidth) 
rectangle (0.920000\linewidth,-0.344000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.364000\linewidth) node {\texttt{mistyrose (114)}};
+\draw [fill=gnuastroantiquewhite] (0.900000\linewidth,-0.392000\linewidth) 
rectangle (0.920000\linewidth,-0.372000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.392000\linewidth) node {\texttt{antiquewhite (115)}};
+\draw [fill=gnuastrolinen] (0.900000\linewidth,-0.420000\linewidth) rectangle 
(0.920000\linewidth,-0.400000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.420000\linewidth) node {\texttt{linen (116)}};
+\draw [fill=gnuastrobeige] (0.900000\linewidth,-0.448000\linewidth) rectangle 
(0.920000\linewidth,-0.428000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.448000\linewidth) node {\texttt{beige (117)}};
+\draw [fill=gnuastrowhitesmoke] (0.900000\linewidth,-0.476000\linewidth) 
rectangle (0.920000\linewidth,-0.456000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.476000\linewidth) node {\texttt{whitesmoke (118)}};
+\draw [fill=gnuastrolavenderblush] (0.900000\linewidth,-0.504000\linewidth) 
rectangle (0.920000\linewidth,-0.484000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.504000\linewidth) node {\texttt{lavenderblush (119)}};
+\draw [fill=gnuastrooldlace] (0.900000\linewidth,-0.532000\linewidth) 
rectangle (0.920000\linewidth,-0.512000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(0.920000\linewidth,-0.532000\linewidth) node {\texttt{oldlace (120)}};
+\draw [fill=gnuastroaliceblue] (1.080000\linewidth,0.000000\linewidth) 
rectangle (1.100000\linewidth,0.020000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,0.000000\linewidth) node {\texttt{aliceblue (121)}};
+\draw [fill=gnuastroseashell] (1.080000\linewidth,-0.028000\linewidth) 
rectangle (1.100000\linewidth,-0.008000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.028000\linewidth) node {\texttt{seashell (122)}};
+\draw [fill=gnuastroghostwhite] (1.080000\linewidth,-0.056000\linewidth) 
rectangle (1.100000\linewidth,-0.036000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.056000\linewidth) node {\texttt{ghostwhite (123)}};
+\draw [fill=gnuastrohoneydew] (1.080000\linewidth,-0.084000\linewidth) 
rectangle (1.100000\linewidth,-0.064000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.084000\linewidth) node {\texttt{honeydew (124)}};
+\draw [fill=gnuastrofloralwhite] (1.080000\linewidth,-0.112000\linewidth) 
rectangle (1.100000\linewidth,-0.092000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.112000\linewidth) node {\texttt{floralwhite (125)}};
+\draw [fill=gnuastroazure] (1.080000\linewidth,-0.140000\linewidth) rectangle 
(1.100000\linewidth,-0.120000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.140000\linewidth) node {\texttt{azure (126)}};
+\draw [fill=gnuastromintcream] (1.080000\linewidth,-0.168000\linewidth) 
rectangle (1.100000\linewidth,-0.148000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.168000\linewidth) node {\texttt{mintcream (127)}};
+\draw [fill=gnuastrosnow] (1.080000\linewidth,-0.196000\linewidth) rectangle 
(1.100000\linewidth,-0.176000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.196000\linewidth) node {\texttt{snow (128)}};
+\draw [fill=gnuastroivory] (1.080000\linewidth,-0.224000\linewidth) rectangle 
(1.100000\linewidth,-0.204000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.224000\linewidth) node {\texttt{ivory (129)}};
+\draw [fill=gnuastrowhite] (1.080000\linewidth,-0.252000\linewidth) rectangle 
(1.100000\linewidth,-0.232000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.252000\linewidth) node {\texttt{white (130)}};
+\draw [fill=gnuastroblack] (1.080000\linewidth,-0.280000\linewidth) rectangle 
(1.100000\linewidth,-0.260000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.280000\linewidth) node {\texttt{black (131)}};
+\draw [fill=gnuastrodarkslategray] (1.080000\linewidth,-0.308000\linewidth) 
rectangle (1.100000\linewidth,-0.288000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.308000\linewidth) node {\texttt{darkslategray (132)}};
+\draw [fill=gnuastrodimgray] (1.080000\linewidth,-0.336000\linewidth) 
rectangle (1.100000\linewidth,-0.316000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.336000\linewidth) node {\texttt{dimgray (133)}};
+\draw [fill=gnuastroslategray] (1.080000\linewidth,-0.364000\linewidth) 
rectangle (1.100000\linewidth,-0.344000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.364000\linewidth) node {\texttt{slategray (134)}};
+\draw [fill=gnuastrogray] (1.080000\linewidth,-0.392000\linewidth) rectangle 
(1.100000\linewidth,-0.372000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.392000\linewidth) node {\texttt{gray (135)}};
+\draw [fill=gnuastrolightslategray] (1.080000\linewidth,-0.420000\linewidth) 
rectangle (1.100000\linewidth,-0.400000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.420000\linewidth) node {\texttt{lightslategray (136)}};
+\draw [fill=gnuastrodarkgray] (1.080000\linewidth,-0.448000\linewidth) 
rectangle (1.100000\linewidth,-0.428000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.448000\linewidth) node {\texttt{darkgray (137)}};
+\draw [fill=gnuastrosilver] (1.080000\linewidth,-0.476000\linewidth) rectangle 
(1.100000\linewidth,-0.456000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.476000\linewidth) node {\texttt{silver (138)}};
+\draw [fill=gnuastrolightgray] (1.080000\linewidth,-0.504000\linewidth) 
rectangle (1.100000\linewidth,-0.484000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.504000\linewidth) node {\texttt{lightgray (139)}};
+\draw [fill=gnuastrogainsboro] (1.080000\linewidth,-0.532000\linewidth) 
rectangle (1.100000\linewidth,-0.512000\linewidth);
+\draw [anchor=south west, inner sep=1, outer sep=0] 
(1.100000\linewidth,-0.532000\linewidth) node {\texttt{gainsboro (140)}};
+\end{tikzpicture}
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e438a909..f11dcee7 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -63,18 +63,66 @@ endif
 
 
 # Specify the library .c files
-libgnuastro_la_SOURCES = $(MAYBE_WCSDISTORTION) arithmetic.c \
-  arithmetic-and.c arithmetic-bitand.c arithmetic-bitlsh.c \
-  arithmetic-bitor.c arithmetic-bitrsh.c arithmetic-bitxor.c \
-  arithmetic-divide.c arithmetic-eq.c arithmetic-ge.c arithmetic-gt.c \
-  arithmetic-le.c arithmetic-lt.c arithmetic-minus.c arithmetic-modulo.c \
-  arithmetic-multiply.c arithmetic-ne.c arithmetic-or.c \
-  arithmetic-plus.c arithmetic-set.c array.c binary.c blank.c box.c \
-  checkset.c convolve.c cosmology.c data.c ds9.c eps.c fits.c git.c \
-  interpolate.c jpeg.c kdtree.c label.c list.c match.c options.c pdf.c \
-  permutation.c pointer.c polygon.c qsort.c dimension.c speclines.c \
-  statistics.c table.c tableintern.c threads.c tiff.c tile.c \
-  tile-internal.c timing.c txt.c type.c units.c wcs.c
+libgnuastro_la_SOURCES = $(MAYBE_WCSDISTORTION) \
+  arithmetic.c \
+  arithmetic-and.c \
+  arithmetic-bitand.c \
+  arithmetic-bitlsh.c \
+  arithmetic-bitor.c \
+  arithmetic-bitrsh.c \
+  arithmetic-bitxor.c \
+  arithmetic-divide.c \
+  arithmetic-eq.c\
+  arithmetic-ge.c \
+  arithmetic-gt.c \
+  arithmetic-le.c \
+  arithmetic-lt.c \
+  arithmetic-minus.c \
+  arithmetic-modulo.c \
+  arithmetic-multiply.c \
+  arithmetic-ne.c \
+  arithmetic-or.c \
+  arithmetic-plus.c \
+  arithmetic-set.c \
+  array.c \
+  binary.c \
+  blank.c \
+  box.c \
+  checkset.c \
+  color.c \
+  convolve.c \
+  cosmology.c \
+  data.c \
+  ds9.c \
+  eps.c \
+  fits.c \
+  git.c \
+  interpolate.c \
+  jpeg.c \
+  kdtree.c \
+  label.c \
+  list.c \
+  match.c \
+  options.c \
+  pdf.c \
+  permutation.c \
+  pointer.c \
+  polygon.c \
+  qsort.c \
+  dimension.c \
+  speclines.c \
+  statistics.c \
+  table.c \
+  tableintern.c \
+  threads.c \
+  tiff.c \
+  tile.c \
+  tile-internal.c \
+  timing.c \
+  txt.c \
+  type.c \
+  units.c \
+  wcs.c
 
 
 
@@ -84,18 +132,42 @@ libgnuastro_la_SOURCES = $(MAYBE_WCSDISTORTION) 
arithmetic.c \
 # in the $(headersdir) directory. Some of the header files don't need to be
 # installed.
 headersdir=$(top_srcdir)/lib/gnuastro
-pkginclude_HEADERS = gnuastro/config.h $(headersdir)/arithmetic.h \
-  $(headersdir)/array.h $(headersdir)/binary.h $(headersdir)/blank.h \
-  $(headersdir)/box.h $(headersdir)/convolve.h $(headersdir)/cosmology.h \
-  $(headersdir)/data.h $(headersdir)/dimension.h $(headersdir)/ds9.h \
-  $(headersdir)/eps.h $(headersdir)/fits.h $(headersdir)/git.h \
-  $(headersdir)/interpolate.h $(headersdir)/jpeg.h $(headersdir)/kdtree.h \
-  $(headersdir)/label.h $(headersdir)/list.h $(headersdir)/match.h \
-  $(headersdir)/pdf.h $(headersdir)/permutation.h $(headersdir)/pointer.h \
-  $(headersdir)/polygon.h $(headersdir)/qsort.h $(headersdir)/speclines.h \
-  $(headersdir)/statistics.h $(headersdir)/table.h $(headersdir)/threads.h \
-  $(headersdir)/tiff.h $(headersdir)/tile.h $(headersdir)/txt.h \
-  $(headersdir)/type.h $(headersdir)/units.h $(headersdir)/wcs.h
+pkginclude_HEADERS = gnuastro/config.h \
+  $(headersdir)/arithmetic.h \
+  $(headersdir)/array.h \
+  $(headersdir)/binary.h \
+  $(headersdir)/blank.h \
+  $(headersdir)/box.h \
+  $(headersdir)/color.h \
+  $(headersdir)/convolve.h \
+  $(headersdir)/cosmology.h \
+  $(headersdir)/data.h \
+  $(headersdir)/dimension.h \
+  $(headersdir)/ds9.h \
+  $(headersdir)/eps.h \
+  $(headersdir)/fits.h \
+  $(headersdir)/git.h \
+  $(headersdir)/interpolate.h \
+  $(headersdir)/jpeg.h \
+  $(headersdir)/kdtree.h \
+  $(headersdir)/label.h \
+  $(headersdir)/list.h \
+  $(headersdir)/match.h \
+  $(headersdir)/pdf.h \
+  $(headersdir)/permutation.h \
+  $(headersdir)/pointer.h \
+  $(headersdir)/polygon.h \
+  $(headersdir)/qsort.h \
+  $(headersdir)/speclines.h \
+  $(headersdir)/statistics.h \
+  $(headersdir)/table.h \
+  $(headersdir)/threads.h \
+  $(headersdir)/tiff.h \
+  $(headersdir)/tile.h \
+  $(headersdir)/txt.h \
+  $(headersdir)/type.h \
+  $(headersdir)/units.h \
+  $(headersdir)/wcs.h
 
 
 
@@ -107,22 +179,39 @@ pkginclude_HEADERS = gnuastro/config.h 
$(headersdir)/arithmetic.h \
 # will not distributed, so we need to explicitly tell Automake to
 # distribute them here.
 internaldir=$(top_srcdir)/lib/gnuastro-internal
-EXTRA_DIST = gnuastro.pc.in $(headersdir)/README $(internaldir)/README \
-  $(internaldir)/arithmetic-and.h $(internaldir)/arithmetic-binary.h \
-  $(internaldir)/arithmetic-bitand.h $(internaldir)/arithmetic-bitlsh.h \
-  $(internaldir)/arithmetic-bitor.h $(internaldir)/arithmetic-bitrsh.h \
-  $(internaldir)/arithmetic-bitxor.h $(internaldir)/arithmetic-divide.h \
-  $(internaldir)/arithmetic-eq.h $(internaldir)/arithmetic-ge.h \
-  $(internaldir)/arithmetic-gt.h $(internaldir)/arithmetic-internal.h \
-  $(internaldir)/arithmetic-le.h $(internaldir)/arithmetic-lt.h \
-  $(internaldir)/arithmetic-minus.h $(internaldir)/arithmetic-modulo.h \
-  $(internaldir)/arithmetic-multiply.h $(internaldir)/arithmetic-ne.h \
-  $(internaldir)/arithmetic-or.h $(internaldir)/arithmetic-plus.h \
-  $(internaldir)/arithmetic-set.h $(internaldir)/checkset.h \
-  $(internaldir)/commonopts.h $(internaldir)/config.h.in \
-  $(internaldir)/fixedstringmacros.h $(internaldir)/options.h \
-  $(internaldir)/tableintern.h $(internaldir)/tile-internal.h \
-  $(internaldir)/timing.h $(internaldir)/wcsdistortion.h
+EXTRA_DIST = gnuastro.pc.in \
+  $(headersdir)/README \
+  $(internaldir)/README \
+  $(internaldir)/arithmetic-and.h \
+  $(internaldir)/arithmetic-binary.h \
+  $(internaldir)/arithmetic-bitand.h  \
+  $(internaldir)/arithmetic-bitlsh.h \
+  $(internaldir)/arithmetic-bitor.h  \
+  $(internaldir)/arithmetic-bitrsh.h \
+  $(internaldir)/arithmetic-bitxor.h \
+  $(internaldir)/arithmetic-divide.h \
+  $(internaldir)/arithmetic-eq.h  \
+  $(internaldir)/arithmetic-ge.h \
+  $(internaldir)/arithmetic-gt.h  \
+  $(internaldir)/arithmetic-internal.h \
+  $(internaldir)/arithmetic-le.h  \
+  $(internaldir)/arithmetic-lt.h \
+  $(internaldir)/arithmetic-minus.h  \
+  $(internaldir)/arithmetic-modulo.h \
+  $(internaldir)/arithmetic-multiply.h  \
+  $(internaldir)/arithmetic-ne.h \
+  $(internaldir)/arithmetic-or.h  \
+  $(internaldir)/arithmetic-plus.h \
+  $(internaldir)/arithmetic-set.h  \
+  $(internaldir)/checkset.h \
+  $(internaldir)/commonopts.h  \
+  $(internaldir)/config.h.in \
+  $(internaldir)/fixedstringmacros.h  \
+  $(internaldir)/options.h \
+  $(internaldir)/tableintern.h  \
+  $(internaldir)/tile-internal.h \
+  $(internaldir)/timing.h  \
+  $(internaldir)/wcsdistortion.h
 
 
 
diff --git a/lib/arithmetic.c b/lib/arithmetic.c
index 58f88298..2bec4b75 100644
--- a/lib/arithmetic.c
+++ b/lib/arithmetic.c
@@ -33,6 +33,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <gsl/gsl_rng.h>
 #include <gsl/gsl_math.h>
 #include <gsl/gsl_randist.h>
+#include <gsl/gsl_const_num.h>
+#include <gsl/gsl_const_mks.h>
 
 #include <gnuastro/box.h>
 #include <gnuastro/list.h>
@@ -1852,7 +1854,7 @@ struct multioperandparams
 
 
 /* Worker function on each thread. */
-void *
+static void *
 multioperand_on_thread(void *in_prm)
 {
   /* Low-level definitions to be done first. */
@@ -2432,6 +2434,10 @@ arithmetic_function_binary_flt(int operator, int flags, 
gal_data_t *il,
       BINFUNC_F_OPERATOR_SET( pow,   +0 );         break;
     case GAL_ARITHMETIC_OP_ATAN2:
       BINFUNC_F_OPERATOR_SET( atan2, *180.0f/M_PI ); break;
+    case GAL_ARITHMETIC_OP_SB_TO_MAG:
+      BINFUNC_F_OPERATOR_SET( gal_units_sb_to_mag, +0 ); break;
+    case GAL_ARITHMETIC_OP_MAG_TO_SB:
+      BINFUNC_F_OPERATOR_SET( gal_units_mag_to_sb, +0 ); break;
     case GAL_ARITHMETIC_OP_COUNTS_TO_MAG:
       BINFUNC_F_OPERATOR_SET( gal_units_counts_to_mag, +0 ); break;
     case GAL_ARITHMETIC_OP_MAG_TO_COUNTS:
@@ -2484,7 +2490,47 @@ arithmetic_function_binary_flt(int operator, int flags, 
gal_data_t *il,
 
 
 
-gal_data_t *
+/* The list of arguments are:
+     d1: Input data (counts or SB).
+     d2: Zeropoint.
+     d3: Area.      */
+static gal_data_t *
+arithmetic_counts_to_from_sb(int operator, int flags, gal_data_t *d1,
+                             gal_data_t *d2, gal_data_t *d3)
+{
+  gal_data_t *tmp, *out=NULL;
+
+  switch(operator)
+    {
+    case GAL_ARITHMETIC_OP_COUNTS_TO_SB:
+      tmp=arithmetic_function_binary_flt(GAL_ARITHMETIC_OP_COUNTS_TO_MAG,
+                                         flags, d1, d2); /* d2=zeropoint */
+      out=arithmetic_function_binary_flt(GAL_ARITHMETIC_OP_MAG_TO_SB,
+                                         flags, tmp, d3); /* d3=area */
+      break;
+
+    case GAL_ARITHMETIC_OP_SB_TO_COUNTS:
+      tmp=arithmetic_function_binary_flt(GAL_ARITHMETIC_OP_SB_TO_MAG,
+                                         flags, d1, d3); /* d3-->area */
+      out=arithmetic_function_binary_flt(GAL_ARITHMETIC_OP_MAG_TO_COUNTS,
+                                         flags, tmp, d2); /* d2=zeropoint */
+      break;
+
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' "
+            "to find and fix the problem. The value of '%d' isn't "
+            "recognized for the 'operator' variable", __func__,
+            PACKAGE_BUGREPORT, operator);
+    }
+
+  return out;
+}
+
+
+
+
+
+static gal_data_t *
 arithmetic_box_around_ellipse(gal_data_t *d1, gal_data_t *d2,
                               gal_data_t *d3, int operator, int flags)
 {
@@ -2568,6 +2614,54 @@ arithmetic_box_around_ellipse(gal_data_t *d1, gal_data_t 
*d2,
 
 
 
+static gal_data_t *
+arithmetic_constant(int operator)
+{
+  size_t one=1;
+
+  /* Allocate the output dataset. */
+  gal_data_t *out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &one,
+                                 NULL, 0, -1, 0, NULL, NULL, NULL);
+
+  /* Set the pointer to the value. */
+  double *value=out->array;
+
+  /* Write the desired constant (from GSL). */
+  switch(operator)
+    {
+    case GAL_ARITHMETIC_OP_E:            /* Base of natural logarithm. */
+      value[0]=M_E; break;
+    case GAL_ARITHMETIC_OP_PI:    /* Circle circumfernece to diameter. */
+      value[0]=M_PI; break;
+    case GAL_ARITHMETIC_OP_C:                   /* The speed of light. */
+      value[0]=GSL_CONST_MKS_SPEED_OF_LIGHT;
+    case GAL_ARITHMETIC_OP_G:           /* The gravitational constant. */
+      value[0]=GSL_CONST_MKS_GRAVITATIONAL_CONSTANT;
+    case GAL_ARITHMETIC_OP_H:                     /* Plank's constant. */
+      value[0]=GSL_CONST_MKS_PLANCKS_CONSTANT_H;
+    case GAL_ARITHMETIC_OP_AU:       /* Astronomical Unit (in meters). */
+      value[0]=GSL_CONST_MKS_ASTRONOMICAL_UNIT;
+    case GAL_ARITHMETIC_OP_LY:             /* Light years (in meters). */
+      value[0]=GSL_CONST_MKS_LIGHT_YEAR;
+    case GAL_ARITHMETIC_OP_AVOGADRO:             /* Avogadro's number. */
+      value[0]=GSL_CONST_NUM_AVOGADRO;
+    case GAL_ARITHMETIC_OP_FINESTRUCTURE:  /* Fine-structure constant. */
+      value[0]=GSL_CONST_NUM_FINE_STRUCTURE;
+
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' to "
+            "fix the problem. The value '%d' isn't recognized for the "
+            "variable 'operator'", __func__, PACKAGE_BUGREPORT, operator);
+    }
+
+  /* Return the allocated dataset. */
+  return out;
+}
+
+
+
+
+
 
 
 
@@ -2586,7 +2680,7 @@ arithmetic_box_around_ellipse(gal_data_t *d1, gal_data_t 
*d2,
 /**********************************************************************/
 /****************             New datasets            *****************/
 /**********************************************************************/
-gal_data_t *
+static gal_data_t *
 arithmetic_makenew(gal_data_t *sizes)
 {
   gal_data_t *out, *tmp, *ttmp;
@@ -2760,6 +2854,7 @@ gal_arithmetic_load_col(char *str, int searchin, int 
ignorecase,
 
 
 
+
 /**********************************************************************/
 /****************         High-level functions        *****************/
 /**********************************************************************/
@@ -2834,6 +2929,14 @@ gal_arithmetic_set_operator(char *string, size_t 
*num_operands)
     { op=GAL_ARITHMETIC_OP_COUNTS_TO_MAG;     *num_operands=2;  }
   else if (!strcmp(string, "mag-to-counts"))
     { op=GAL_ARITHMETIC_OP_MAG_TO_COUNTS;     *num_operands=2;  }
+  else if (!strcmp(string, "sb-to-mag"))
+    { op=GAL_ARITHMETIC_OP_SB_TO_MAG;         *num_operands=2;  }
+  else if (!strcmp(string, "mag-to-sb"))
+    { op=GAL_ARITHMETIC_OP_MAG_TO_SB;         *num_operands=2;  }
+  else if (!strcmp(string, "counts-to-sb"))
+    { op=GAL_ARITHMETIC_OP_COUNTS_TO_SB;      *num_operands=3;  }
+  else if (!strcmp(string, "sb-to-counts"))
+    { op=GAL_ARITHMETIC_OP_SB_TO_COUNTS;      *num_operands=3;  }
   else if (!strcmp(string, "counts-to-jy"))
     { op=GAL_ARITHMETIC_OP_COUNTS_TO_JY;      *num_operands=2;  }
   else if (!strcmp(string, "jy-to-counts"))
@@ -2981,6 +3084,26 @@ gal_arithmetic_set_operator(char *string, size_t 
*num_operands)
   else if (!strcmp(string, "float64"))
     { op=GAL_ARITHMETIC_OP_TO_FLOAT64;        *num_operands=1;  }
 
+  /* Constants. */
+  else if (!strcmp(string, "e"))
+    { op=GAL_ARITHMETIC_OP_E;                 *num_operands=0;  }
+  else if (!strcmp(string, "pi"))
+    { op=GAL_ARITHMETIC_OP_PI;                *num_operands=0;  }
+  else if (!strcmp(string, "c"))
+    { op=GAL_ARITHMETIC_OP_C;                 *num_operands=0;  }
+  else if (!strcmp(string, "G"))
+    { op=GAL_ARITHMETIC_OP_G;                 *num_operands=0;  }
+  else if (!strcmp(string, "h"))
+    { op=GAL_ARITHMETIC_OP_H;                 *num_operands=0;  }
+  else if (!strcmp(string, "AU"))
+    { op=GAL_ARITHMETIC_OP_AU;                *num_operands=0;  }
+  else if (!strcmp(string, "ly"))
+    { op=GAL_ARITHMETIC_OP_LY;                *num_operands=0;  }
+  else if (!strcmp(string, "avogadro"))
+    { op=GAL_ARITHMETIC_OP_AVOGADRO;          *num_operands=0;  }
+  else if (!strcmp(string, "fine-structure"))
+    { op=GAL_ARITHMETIC_OP_FINESTRUCTURE;     *num_operands=0;  }
+
   /* Surrounding box. */
   else if (!strcmp(string, "box-around-ellipse"))
     { op=GAL_ARITHMETIC_OP_BOX_AROUND_ELLIPSE;*num_operands=3;  }
@@ -3055,6 +3178,10 @@ gal_arithmetic_operator_string(int operator)
     case GAL_ARITHMETIC_OP_DEGREE_TO_DEC:   return "degree-to-dec";
     case GAL_ARITHMETIC_OP_COUNTS_TO_MAG:   return "counts-to-mag";
     case GAL_ARITHMETIC_OP_MAG_TO_COUNTS:   return "mag-to-counts";
+    case GAL_ARITHMETIC_OP_SB_TO_MAG:       return "sb-to-mag";
+    case GAL_ARITHMETIC_OP_MAG_TO_SB:       return "mag-to-sb";
+    case GAL_ARITHMETIC_OP_COUNTS_TO_SB:    return "counts-to-sb";
+    case GAL_ARITHMETIC_OP_SB_TO_COUNTS:    return "sb-to-counts";
     case GAL_ARITHMETIC_OP_COUNTS_TO_JY:    return "counts-to-jy";
     case GAL_ARITHMETIC_OP_JY_TO_COUNTS:    return "jy-to-counts";
     case GAL_ARITHMETIC_OP_MAG_TO_JY:       return "mag-to-jy";
@@ -3110,6 +3237,16 @@ gal_arithmetic_operator_string(int operator)
     case GAL_ARITHMETIC_OP_TO_FLOAT32:      return "float32";
     case GAL_ARITHMETIC_OP_TO_FLOAT64:      return "float64";
 
+    case GAL_ARITHMETIC_OP_E:               return "e";
+    case GAL_ARITHMETIC_OP_PI:              return "pi";
+    case GAL_ARITHMETIC_OP_C:               return "c";
+    case GAL_ARITHMETIC_OP_G:               return "G";
+    case GAL_ARITHMETIC_OP_H:               return "h";
+    case GAL_ARITHMETIC_OP_AU:              return "au";
+    case GAL_ARITHMETIC_OP_LY:              return "ly";
+    case GAL_ARITHMETIC_OP_AVOGADRO:        return "avogadro";
+    case GAL_ARITHMETIC_OP_FINESTRUCTURE:   return "fine-structure";
+
     case GAL_ARITHMETIC_OP_BOX_AROUND_ELLIPSE: return "box-around-ellipse";
 
     case GAL_ARITHMETIC_OP_MAKENEW:         return "makenew";
@@ -3207,8 +3344,10 @@ gal_arithmetic(int operator, size_t numthreads, int 
flags, ...)
     /* Binary function operators. */
     case GAL_ARITHMETIC_OP_POW:
     case GAL_ARITHMETIC_OP_ATAN2:
-    case GAL_ARITHMETIC_OP_COUNTS_TO_JY:
+    case GAL_ARITHMETIC_OP_MAG_TO_SB:
+    case GAL_ARITHMETIC_OP_SB_TO_MAG:
     case GAL_ARITHMETIC_OP_JY_TO_COUNTS:
+    case GAL_ARITHMETIC_OP_COUNTS_TO_JY:
     case GAL_ARITHMETIC_OP_COUNTS_TO_MAG:
     case GAL_ARITHMETIC_OP_MAG_TO_COUNTS:
       d1 = va_arg(va, gal_data_t *);
@@ -3216,6 +3355,16 @@ gal_arithmetic(int operator, size_t numthreads, int 
flags, ...)
       out=arithmetic_function_binary_flt(operator, flags, d1, d2);
       break;
 
+    /* More complex operators. */
+    case GAL_ARITHMETIC_OP_COUNTS_TO_SB:
+    case GAL_ARITHMETIC_OP_SB_TO_COUNTS:
+      d1 = va_arg(va, gal_data_t *);
+      d2 = va_arg(va, gal_data_t *);
+      d3 = va_arg(va, gal_data_t *);
+      out=arithmetic_counts_to_from_sb(operator, flags, d1, d2, d3);
+
+      break;
+
     /* Statistical operators that return one value. */
     case GAL_ARITHMETIC_OP_MINVAL:
     case GAL_ARITHMETIC_OP_MAXVAL:
@@ -3322,6 +3471,19 @@ gal_arithmetic(int operator, size_t numthreads, int 
flags, ...)
       out=arithmetic_change_type(d1, operator, flags);
       break;
 
+    /* Constants. */
+    case GAL_ARITHMETIC_OP_E:
+    case GAL_ARITHMETIC_OP_C:
+    case GAL_ARITHMETIC_OP_G:
+    case GAL_ARITHMETIC_OP_H:
+    case GAL_ARITHMETIC_OP_AU:
+    case GAL_ARITHMETIC_OP_LY:
+    case GAL_ARITHMETIC_OP_PI:
+    case GAL_ARITHMETIC_OP_AVOGADRO:
+    case GAL_ARITHMETIC_OP_FINESTRUCTURE:
+      out=arithmetic_constant(operator);
+      break;
+
     /* Calculate the width and height of a box surrounding an ellipse with
        a certain major axis, minor axis and position angle. */
     case GAL_ARITHMETIC_OP_BOX_AROUND_ELLIPSE:
diff --git a/lib/box.c b/lib/box.c
index 97212971..2ce5cccb 100644
--- a/lib/box.c
+++ b/lib/box.c
@@ -28,6 +28,8 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <error.h>
 #include <stdlib.h>
 
+#include <gsl/gsl_math.h>
+
 #include <gnuastro/box.h>
 
 
@@ -324,6 +326,93 @@ gal_box_border_from_center(double *center, size_t ndim, 
long *width,
 
 
 
+/* Rotate one point */
+static void
+box_border_rotate_point(double *point, double rad)
+{
+  double x = point[0]*cos(rad) - point[1]*sin(rad);
+  double y = point[0]*sin(rad) + point[1]*cos(rad);
+  point[0]=x;
+  point[1]=y;
+}
+
+
+
+
+/* Given a certain 'fpixel' and 'lpixel' of a box, update their values if
+   there is rotation (in relation to the first FITS/horizontal axis). */
+void
+gal_box_border_rotate_around_center(long *fpixel, long *lpixel,
+                                    size_t ndim, float rotate_deg)
+{
+  double minx, miny, maxx, maxy;
+  double rad = rotate_deg * M_PI / 180.0f;
+  double center[2]={ (lpixel[0]+fpixel[0])/2.0f,
+                     (lpixel[1]+fpixel[1])/2.0f };
+
+  /* The four corners in relation to the box's center: 'bl' for
+     bottom-left, 'br' for bottom-right, 'tl' for top-left and 'tr' for
+     top-right. */
+  double bl[2]={fpixel[0]-center[0], fpixel[1]-center[1]};
+  double br[2]={lpixel[0]-center[0], fpixel[1]-center[1]};
+  double tl[2]={fpixel[0]-center[0], lpixel[1]-center[1]};
+  double tr[2]={lpixel[0]-center[0], lpixel[1]-center[1]};
+
+  /* In case we don't have any rotation, there is no purpose for
+     continuing. */
+  if(rotate_deg==0) return;
+
+  /* This function currently only works on 2D inputs. */
+  if(ndim!=2)
+    error(EXIT_FAILURE, 0, "%s: currently only 2D datasets are "
+          "allowed, please contact us at '%s' to generalize",
+          __func__, PACKAGE_BUGREPORT);
+
+  /* For a check
+  printf("bottom-left:  %g, %g\n",   bl[0], bl[1]);
+  printf("bottom-right: %g, %g\n",   br[0], br[1]);
+  printf("top-left:     %g, %g\n",   tl[0], tl[1]);
+  printf("top-right:    %g, %g\n\n", tr[0], tr[1]);
+  */
+
+  /* Rotate the four points. */
+  box_border_rotate_point(bl, rad);
+  box_border_rotate_point(br, rad);
+  box_border_rotate_point(tl, rad);
+  box_border_rotate_point(tr, rad);
+
+  /* For a check
+  printf("bottom-left:  %g, %g\n", bl[0], bl[1]);
+  printf("bottom-right: %g, %g\n", br[0], br[1]);
+  printf("top-left:     %g, %g\n", tl[0], tl[1]);
+  printf("top-right:    %g, %g\n", tr[0], tr[1]);
+  */
+
+  /* Update the first and last points. */
+  minx=bl[0];                    maxx=bl[0];
+  if(br[0]<minx) {minx=br[0];}     if(br[0]>maxx) {maxx=br[0];}
+  if(tl[0]<minx) {minx=tl[0];}     if(tl[0]>maxx) {maxx=tl[0];}
+  if(tr[0]<minx) {minx=tr[0];}     if(tr[0]>maxx) {maxx=tr[0];}
+  miny=bl[1];                    maxy=bl[1];
+  if(br[1]<miny) {miny=br[1];}     if(br[1]>maxy) {maxy=br[1];}
+  if(tl[1]<miny) {miny=tl[1];}     if(tl[1]>maxy) {maxy=tl[1];}
+  if(tr[1]<miny) {miny=tr[1];}     if(tr[1]>maxy) {maxy=tr[1];}
+
+  /* Put the minima and maxima into the first and last pixel
+     coordinates. */
+  fpixel[0]=center[0]+minx;   fpixel[1]=center[1]+miny;
+  lpixel[0]=center[0]+maxx;   lpixel[1]=center[1]+maxy;
+
+  /* For a check:
+  printf("fpixel: %ld, %ld\n", fpixel[0], fpixel[1]);
+  printf("lpixel: %ld, %ld\n", lpixel[0], lpixel[1]);
+  */
+}
+
+
+
+
+
 
 /* Problem to solve: We have set the first and last pixels of a box in an
    input image (fpixel_i[2] and lpixel_i[2]). But those first and last
diff --git a/lib/color.c b/lib/color.c
new file mode 100644
index 00000000..6a27e102
--- /dev/null
+++ b/lib/color.c
@@ -0,0 +1,531 @@
+/*********************************************************************
+Functions for working with colors.
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2022 Free Software Foundation, Inc.
+
+Gnuastro is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+#include <config.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <error.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gnuastro/color.h>
+#include <gnuastro/blank.h>
+
+
+
+
+
+/***********************************************************************/
+/**************             Names and values           *****************/
+/***********************************************************************/
+/* Return the macros for the extended HTML/CSS colors based on their names:
+   'https://en.wikipedia.org/wiki/Web_colors#HTML_color_names'.*/
+uint8_t
+gal_color_name_to_id(char *n)
+{
+  if(      !strcasecmp("aliceblue",  n)) return GAL_COLOR_ALICEBLUE;
+  else if( !strcasecmp("antiquewhite", n)) return GAL_COLOR_ANTIQUEWHITE;
+  else if( !strcasecmp("aqua",       n)) return GAL_COLOR_AQUA;
+  else if( !strcasecmp("aquamarine", n)) return GAL_COLOR_AQUAMARINE;
+  else if( !strcasecmp("azure",      n)) return GAL_COLOR_AZURE;
+  else if( !strcasecmp("beige",      n)) return GAL_COLOR_BEIGE;
+  else if( !strcasecmp("bisque",     n)) return GAL_COLOR_BISQUE;
+  else if( !strcasecmp("black",      n)) return GAL_COLOR_BLACK;
+  else if( !strcasecmp("blanchedalmond", n)) return GAL_COLOR_BLANCHEDALMOND;
+  else if( !strcasecmp("blue",       n)) return GAL_COLOR_BLUE;
+  else if( !strcasecmp("blueviolet", n)) return GAL_COLOR_BLUEVIOLET;
+  else if( !strcasecmp("brown",      n)) return GAL_COLOR_BROWN;
+  else if( !strcasecmp("burlywood",  n)) return GAL_COLOR_BURLYWOOD;
+  else if( !strcasecmp("cadetblue",  n)) return GAL_COLOR_CADETBLUE;
+  else if( !strcasecmp("chartreuse", n)) return GAL_COLOR_CHARTREUSE;
+  else if( !strcasecmp("chocolate",  n)) return GAL_COLOR_CHOCOLATE;
+  else if( !strcasecmp("coral",      n)) return GAL_COLOR_CORAL;
+  else if( !strcasecmp("cornflowerblue", n)) return GAL_COLOR_CORNFLOWERBLUE;
+  else if( !strcasecmp("cornsilk",   n)) return GAL_COLOR_CORNSILK;
+  else if( !strcasecmp("crimson",    n)) return GAL_COLOR_CRIMSON;
+  else if( !strcasecmp("cyan",       n)) return GAL_COLOR_CYAN;
+  else if( !strcasecmp("darkblue",   n)) return GAL_COLOR_DARKBLUE;
+  else if( !strcasecmp("darkcyan",   n)) return GAL_COLOR_DARKCYAN;
+  else if( !strcasecmp("darkgoldenrod", n)) return GAL_COLOR_DARKGOLDENROD;
+  else if( !strcasecmp("darkgray",   n)) return GAL_COLOR_DARKGRAY;
+  else if( !strcasecmp("darkgreen",  n)) return GAL_COLOR_DARKGREEN;
+  else if( !strcasecmp("darkkhaki",  n)) return GAL_COLOR_DARKKHAKI;
+  else if( !strcasecmp("darkmagenta", n)) return GAL_COLOR_DARKMAGENTA;
+  else if( !strcasecmp("darkolivegreen", n)) return GAL_COLOR_DARKOLIVEGREEN;
+  else if( !strcasecmp("darkorange", n)) return GAL_COLOR_DARKORANGE;
+  else if( !strcasecmp("darkorchid", n)) return GAL_COLOR_DARKORCHID;
+  else if( !strcasecmp("darkred",    n)) return GAL_COLOR_DARKRED;
+  else if( !strcasecmp("darksalmon", n)) return GAL_COLOR_DARKSALMON;
+  else if( !strcasecmp("darkseagreen", n)) return GAL_COLOR_DARKSEAGREEN;
+  else if( !strcasecmp("darkslateblue", n)) return GAL_COLOR_DARKSLATEBLUE;
+  else if( !strcasecmp("darkslategray", n)) return GAL_COLOR_DARKSLATEGRAY;
+  else if( !strcasecmp("darkturquoise", n)) return GAL_COLOR_DARKTURQUOISE;
+  else if( !strcasecmp("darkviolet", n)) return GAL_COLOR_DARKVIOLET;
+  else if( !strcasecmp("deeppink",   n)) return GAL_COLOR_DEEPPINK;
+  else if( !strcasecmp("deepskyblue", n)) return GAL_COLOR_DEEPSKYBLUE;
+  else if( !strcasecmp("dimgray",    n)) return GAL_COLOR_DIMGRAY;
+  else if( !strcasecmp("dodgerblue", n)) return GAL_COLOR_DODGERBLUE;
+  else if( !strcasecmp("firebrick",  n)) return GAL_COLOR_FIREBRICK;
+  else if( !strcasecmp("floralwhite", n)) return GAL_COLOR_FLORALWHITE;
+  else if( !strcasecmp("forestgreen", n)) return GAL_COLOR_FORESTGREEN;
+  else if( !strcasecmp("fuchsia",    n)) return GAL_COLOR_FUCHSIA;
+  else if( !strcasecmp("gainsboro",  n)) return GAL_COLOR_GAINSBORO;
+  else if( !strcasecmp("ghostwhite", n)) return GAL_COLOR_GHOSTWHITE;
+  else if( !strcasecmp("gold",       n)) return GAL_COLOR_GOLD;
+  else if( !strcasecmp("goldenrod",  n)) return GAL_COLOR_GOLDENROD;
+  else if( !strcasecmp("gray",       n)) return GAL_COLOR_GRAY;
+  else if( !strcasecmp("green",      n)) return GAL_COLOR_GREEN;
+  else if( !strcasecmp("greenyellow", n)) return GAL_COLOR_GREENYELLOW;
+  else if( !strcasecmp("honeydew",   n)) return GAL_COLOR_HONEYDEW;
+  else if( !strcasecmp("hotpink",    n)) return GAL_COLOR_HOTPINK;
+  else if( !strcasecmp("indianred",  n)) return GAL_COLOR_INDIANRED;
+  else if( !strcasecmp("indigo",     n)) return GAL_COLOR_INDIGO;
+  else if( !strcasecmp("ivory",      n)) return GAL_COLOR_IVORY;
+  else if( !strcasecmp("khaki",      n)) return GAL_COLOR_KHAKI;
+  else if( !strcasecmp("lavender",   n)) return GAL_COLOR_LAVENDER;
+  else if( !strcasecmp("lavenderblush", n)) return GAL_COLOR_LAVENDERBLUSH;
+  else if( !strcasecmp("lawngreen",  n)) return GAL_COLOR_LAWNGREEN;
+  else if( !strcasecmp("lemonchiffon", n)) return GAL_COLOR_LEMONCHIFFON;
+  else if( !strcasecmp("lightblue",  n)) return GAL_COLOR_LIGHTBLUE;
+  else if( !strcasecmp("lightcoral", n)) return GAL_COLOR_LIGHTCORAL;
+  else if( !strcasecmp("lightcyan",  n)) return GAL_COLOR_LIGHTCYAN;
+  else if( !strcasecmp("lightgoldenrodyellow", n)) return 
GAL_COLOR_LIGHTGOLDENRODYELLOW;
+  else if( !strcasecmp("lightgray",  n)) return GAL_COLOR_LIGHTGRAY;
+  else if( !strcasecmp("lightgreen", n)) return GAL_COLOR_LIGHTGREEN;
+  else if( !strcasecmp("lightpink",  n)) return GAL_COLOR_LIGHTPINK;
+  else if( !strcasecmp("lightsalmon", n)) return GAL_COLOR_LIGHTSALMON;
+  else if( !strcasecmp("lightseagreen", n)) return GAL_COLOR_LIGHTSEAGREEN;
+  else if( !strcasecmp("lightskyblue", n)) return GAL_COLOR_LIGHTSKYBLUE;
+  else if( !strcasecmp("lightslategray", n)) return GAL_COLOR_LIGHTSLATEGRAY;
+  else if( !strcasecmp("lightsteelblue", n)) return GAL_COLOR_LIGHTSTEELBLUE;
+  else if( !strcasecmp("lightyellow", n)) return GAL_COLOR_LIGHTYELLOW;
+  else if( !strcasecmp("lime",       n)) return GAL_COLOR_LIME;
+  else if( !strcasecmp("limegreen",  n)) return GAL_COLOR_LIMEGREEN;
+  else if( !strcasecmp("linen",      n)) return GAL_COLOR_LINEN;
+  else if( !strcasecmp("magenta",    n)) return GAL_COLOR_MAGENTA;
+  else if( !strcasecmp("maroon",     n)) return GAL_COLOR_MAROON;
+  else if( !strcasecmp("mediumaquamarine", n)) return 
GAL_COLOR_MEDIUMAQUAMARINE;
+  else if( !strcasecmp("mediumblue", n)) return GAL_COLOR_MEDIUMBLUE;
+  else if( !strcasecmp("mediumorchid", n)) return GAL_COLOR_MEDIUMORCHID;
+  else if( !strcasecmp("mediumpurple", n)) return GAL_COLOR_MEDIUMPURPLE;
+  else if( !strcasecmp("mediumseagreen", n)) return GAL_COLOR_MEDIUMSEAGREEN;
+  else if( !strcasecmp("mediumslateblue", n)) return GAL_COLOR_MEDIUMSLATEBLUE;
+  else if( !strcasecmp("mediumspringgreen", n)) return 
GAL_COLOR_MEDIUMSPRINGGREEN;
+  else if( !strcasecmp("mediumturquoise", n)) return GAL_COLOR_MEDIUMTURQUOISE;
+  else if( !strcasecmp("mediumvioletred", n)) return GAL_COLOR_MEDIUMVIOLETRED;
+  else if( !strcasecmp("midnightblue", n)) return GAL_COLOR_MIDNIGHTBLUE;
+  else if( !strcasecmp("mintcream",  n)) return GAL_COLOR_MINTCREAM;
+  else if( !strcasecmp("mistyrose",  n)) return GAL_COLOR_MISTYROSE;
+  else if( !strcasecmp("moccasin",   n)) return GAL_COLOR_MOCCASIN;
+  else if( !strcasecmp("navajowhite", n)) return GAL_COLOR_NAVAJOWHITE;
+  else if( !strcasecmp("navy",       n)) return GAL_COLOR_NAVY;
+  else if( !strcasecmp("oldlace",    n)) return GAL_COLOR_OLDLACE;
+  else if( !strcasecmp("olive",      n)) return GAL_COLOR_OLIVE;
+  else if( !strcasecmp("olivedrab",  n)) return GAL_COLOR_OLIVEDRAB;
+  else if( !strcasecmp("orange",     n)) return GAL_COLOR_ORANGE;
+  else if( !strcasecmp("orangered",  n)) return GAL_COLOR_ORANGERED;
+  else if( !strcasecmp("orchid",     n)) return GAL_COLOR_ORCHID;
+  else if( !strcasecmp("palegoldenrod", n)) return GAL_COLOR_PALEGOLDENROD;
+  else if( !strcasecmp("palegreen",  n)) return GAL_COLOR_PALEGREEN;
+  else if( !strcasecmp("paleturquoise", n)) return GAL_COLOR_PALETURQUOISE;
+  else if( !strcasecmp("palevioletred", n)) return GAL_COLOR_PALEVIOLETRED;
+  else if( !strcasecmp("papayawhip", n)) return GAL_COLOR_PAPAYAWHIP;
+  else if( !strcasecmp("peachpuff",  n)) return GAL_COLOR_PEACHPUFF;
+  else if( !strcasecmp("peru",       n)) return GAL_COLOR_PERU;
+  else if( !strcasecmp("pink",       n)) return GAL_COLOR_PINK;
+  else if( !strcasecmp("plum",       n)) return GAL_COLOR_PLUM;
+  else if( !strcasecmp("powderblue", n)) return GAL_COLOR_POWDERBLUE;
+  else if( !strcasecmp("purple",     n)) return GAL_COLOR_PURPLE;
+  else if( !strcasecmp("red",        n)) return GAL_COLOR_RED;
+  else if( !strcasecmp("rosybrown",  n)) return GAL_COLOR_ROSYBROWN;
+  else if( !strcasecmp("royalblue",  n)) return GAL_COLOR_ROYALBLUE;
+  else if( !strcasecmp("saddlebrown", n)) return GAL_COLOR_SADDLEBROWN;
+  else if( !strcasecmp("salmon",     n)) return GAL_COLOR_SALMON;
+  else if( !strcasecmp("sandybrown", n)) return GAL_COLOR_SANDYBROWN;
+  else if( !strcasecmp("seagreen",   n)) return GAL_COLOR_SEAGREEN;
+  else if( !strcasecmp("seashell",   n)) return GAL_COLOR_SEASHELL;
+  else if( !strcasecmp("sienna",     n)) return GAL_COLOR_SIENNA;
+  else if( !strcasecmp("silver",     n)) return GAL_COLOR_SILVER;
+  else if( !strcasecmp("skyblue",    n)) return GAL_COLOR_SKYBLUE;
+  else if( !strcasecmp("slateblue",  n)) return GAL_COLOR_SLATEBLUE;
+  else if( !strcasecmp("slategray",  n)) return GAL_COLOR_SLATEGRAY;
+  else if( !strcasecmp("snow",       n)) return GAL_COLOR_SNOW;
+  else if( !strcasecmp("springgreen", n)) return GAL_COLOR_SPRINGGREEN;
+  else if( !strcasecmp("steelblue",  n)) return GAL_COLOR_STEELBLUE;
+  else if( !strcasecmp("tan",        n)) return GAL_COLOR_TAN;
+  else if( !strcasecmp("teal",       n)) return GAL_COLOR_TEAL;
+  else if( !strcasecmp("thistle",    n)) return GAL_COLOR_THISTLE;
+  else if( !strcasecmp("tomato",     n)) return GAL_COLOR_TOMATO;
+  else if( !strcasecmp("turquoise",  n)) return GAL_COLOR_TURQUOISE;
+  else if( !strcasecmp("violet",     n)) return GAL_COLOR_VIOLET;
+  else if( !strcasecmp("wheat",      n)) return GAL_COLOR_WHEAT;
+  else if( !strcasecmp("white",      n)) return GAL_COLOR_WHITE;
+  else if( !strcasecmp("whitesmoke", n)) return GAL_COLOR_WHITESMOKE;
+  else if( !strcasecmp("yellow",     n)) return GAL_COLOR_YELLOW;
+  else if( !strcasecmp("yellowgreen", n)) return GAL_COLOR_YELLOWGREEN;
+  else
+    printf("%s: name '%s' is not recognized. Gnuastro uses the "
+           "extended HTML color names described in the following "
+           "link (the names are not case sensitive in Gnuastro):"
+           "https://en.wikipedia.org/wiki/Web_colors#Extended_colors\n";,
+           __func__, n);
+
+  /* Control should not reach here. */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to find "
+        "and solve the problem. Control should not reach this part "
+        "of the function", __func__, PACKAGE_BUGREPORT);
+  return GAL_COLOR_INVALID;
+}
+
+
+
+
+
+/* Return the name of each color. */
+char *
+gal_color_id_to_name(uint8_t color)
+{
+  switch(color)
+    {
+    case GAL_COLOR_ALICEBLUE:            return "aliceblue";
+    case GAL_COLOR_ANTIQUEWHITE:         return "antiquewhite";
+    case GAL_COLOR_AQUA:                 return "aqua";
+    case GAL_COLOR_AQUAMARINE:           return "aquamarine";
+    case GAL_COLOR_AZURE:                return "azure";
+    case GAL_COLOR_BEIGE:                return "beige";
+    case GAL_COLOR_BISQUE:               return "bisque";
+    case GAL_COLOR_BLACK:                return "black";
+    case GAL_COLOR_BLANCHEDALMOND:       return "blanchedalmond";
+    case GAL_COLOR_BLUE:                 return "blue";
+    case GAL_COLOR_BLUEVIOLET:           return "blueviolet";
+    case GAL_COLOR_BROWN:                return "brown";
+    case GAL_COLOR_BURLYWOOD:            return "burlywood";
+    case GAL_COLOR_CADETBLUE:            return "cadetblue";
+    case GAL_COLOR_CHARTREUSE:           return "chartreuse";
+    case GAL_COLOR_CHOCOLATE:            return "chocolate";
+    case GAL_COLOR_CORAL:                return "coral";
+    case GAL_COLOR_CORNFLOWERBLUE:       return "cornflowerblue";
+    case GAL_COLOR_CORNSILK:             return "cornsilk";
+    case GAL_COLOR_CRIMSON:              return "crimson";
+    case GAL_COLOR_CYAN:                 return "cyan";
+    case GAL_COLOR_DARKBLUE:             return "darkblue";
+    case GAL_COLOR_DARKCYAN:             return "darkcyan";
+    case GAL_COLOR_DARKGOLDENROD:        return "darkgoldenrod";
+    case GAL_COLOR_DARKGRAY:             return "darkgray";
+    case GAL_COLOR_DARKGREEN:            return "darkgreen";
+    case GAL_COLOR_DARKKHAKI:            return "darkkhaki";
+    case GAL_COLOR_DARKMAGENTA:          return "darkmagenta";
+    case GAL_COLOR_DARKOLIVEGREEN:       return "darkolivegreen";
+    case GAL_COLOR_DARKORANGE:           return "darkorange";
+    case GAL_COLOR_DARKORCHID:           return "darkorchid";
+    case GAL_COLOR_DARKRED:              return "darkred";
+    case GAL_COLOR_DARKSALMON:           return "darksalmon";
+    case GAL_COLOR_DARKSEAGREEN:         return "darkseagreen";
+    case GAL_COLOR_DARKSLATEBLUE:        return "darkslateblue";
+    case GAL_COLOR_DARKSLATEGRAY:        return "darkslategray";
+    case GAL_COLOR_DARKTURQUOISE:        return "darkturquoise";
+    case GAL_COLOR_DARKVIOLET:           return "darkviolet";
+    case GAL_COLOR_DEEPPINK:             return "deeppink";
+    case GAL_COLOR_DEEPSKYBLUE:          return "deepskyblue";
+    case GAL_COLOR_DIMGRAY:              return "dimgray";
+    case GAL_COLOR_DODGERBLUE:           return "dodgerblue";
+    case GAL_COLOR_FIREBRICK:            return "firebrick";
+    case GAL_COLOR_FLORALWHITE:          return "floralwhite";
+    case GAL_COLOR_FORESTGREEN:          return "forestgreen";
+    case GAL_COLOR_FUCHSIA:              return "fuchsia";
+    case GAL_COLOR_GAINSBORO:            return "gainsboro";
+    case GAL_COLOR_GHOSTWHITE:           return "ghostwhite";
+    case GAL_COLOR_GOLD:                 return "gold";
+    case GAL_COLOR_GOLDENROD:            return "goldenrod";
+    case GAL_COLOR_GRAY:                 return "gray";
+    case GAL_COLOR_GREEN:                return "green";
+    case GAL_COLOR_GREENYELLOW:          return "greenyellow";
+    case GAL_COLOR_HONEYDEW:             return "honeydew";
+    case GAL_COLOR_HOTPINK:              return "hotpink";
+    case GAL_COLOR_INDIANRED:            return "indianred";
+    case GAL_COLOR_INDIGO:               return "indigo";
+    case GAL_COLOR_IVORY:                return "ivory";
+    case GAL_COLOR_KHAKI:                return "khaki";
+    case GAL_COLOR_LAVENDER:             return "lavender";
+    case GAL_COLOR_LAVENDERBLUSH:        return "lavenderblush";
+    case GAL_COLOR_LAWNGREEN:            return "lawngreen";
+    case GAL_COLOR_LEMONCHIFFON:         return "lemonchiffon";
+    case GAL_COLOR_LIGHTBLUE:            return "lightblue";
+    case GAL_COLOR_LIGHTCORAL:           return "lightcoral";
+    case GAL_COLOR_LIGHTCYAN:            return "lightcyan";
+    case GAL_COLOR_LIGHTGOLDENRODYELLOW: return "lightgoldenrodyellow";
+    case GAL_COLOR_LIGHTGRAY:            return "lightgray";
+    case GAL_COLOR_LIGHTGREEN:           return "lightgreen";
+    case GAL_COLOR_LIGHTPINK:            return "lightpink";
+    case GAL_COLOR_LIGHTSALMON:          return "lightsalmon";
+    case GAL_COLOR_LIGHTSEAGREEN:        return "lightseagreen";
+    case GAL_COLOR_LIGHTSKYBLUE:         return "lightskyblue";
+    case GAL_COLOR_LIGHTSLATEGRAY:       return "lightslategray";
+    case GAL_COLOR_LIGHTSTEELBLUE:       return "lightsteelblue";
+    case GAL_COLOR_LIGHTYELLOW:          return "lightyellow";
+    case GAL_COLOR_LIME:                 return "lime";
+    case GAL_COLOR_LIMEGREEN:            return "limegreen";
+    case GAL_COLOR_LINEN:                return "linen";
+    case GAL_COLOR_MAGENTA:              return "magenta";
+    case GAL_COLOR_MAROON:               return "maroon";
+    case GAL_COLOR_MEDIUMAQUAMARINE:     return "mediumaquamarine";
+    case GAL_COLOR_MEDIUMBLUE:           return "mediumblue";
+    case GAL_COLOR_MEDIUMORCHID:         return "mediumorchid";
+    case GAL_COLOR_MEDIUMPURPLE:         return "mediumpurple";
+    case GAL_COLOR_MEDIUMSEAGREEN:       return "mediumseagreen";
+    case GAL_COLOR_MEDIUMSLATEBLUE:      return "mediumslateblue";
+    case GAL_COLOR_MEDIUMSPRINGGREEN:    return "mediumspringgreen";
+    case GAL_COLOR_MEDIUMTURQUOISE:      return "mediumturquoise";
+    case GAL_COLOR_MEDIUMVIOLETRED:      return "mediumvioletred";
+    case GAL_COLOR_MIDNIGHTBLUE:         return "midnightblue";
+    case GAL_COLOR_MINTCREAM:            return "mintcream";
+    case GAL_COLOR_MISTYROSE:            return "mistyrose";
+    case GAL_COLOR_MOCCASIN:             return "moccasin";
+    case GAL_COLOR_NAVAJOWHITE:          return "navajowhite";
+    case GAL_COLOR_NAVY:                 return "navy";
+    case GAL_COLOR_OLDLACE:              return "oldlace";
+    case GAL_COLOR_OLIVE:                return "olive";
+    case GAL_COLOR_OLIVEDRAB:            return "olivedrab";
+    case GAL_COLOR_ORANGE:               return "orange";
+    case GAL_COLOR_ORANGERED:            return "orangered";
+    case GAL_COLOR_ORCHID:               return "orchid";
+    case GAL_COLOR_PALEGOLDENROD:        return "palegoldenrod";
+    case GAL_COLOR_PALEGREEN:            return "palegreen";
+    case GAL_COLOR_PALETURQUOISE:        return "paleturquoise";
+    case GAL_COLOR_PALEVIOLETRED:        return "palevioletred";
+    case GAL_COLOR_PAPAYAWHIP:           return "papayawhip";
+    case GAL_COLOR_PEACHPUFF:            return "peachpuff";
+    case GAL_COLOR_PERU:                 return "peru";
+    case GAL_COLOR_PINK:                 return "pink";
+    case GAL_COLOR_PLUM:                 return "plum";
+    case GAL_COLOR_POWDERBLUE:           return "powderblue";
+    case GAL_COLOR_PURPLE:               return "purple";
+    case GAL_COLOR_RED:                  return "red";
+    case GAL_COLOR_ROSYBROWN:            return "rosybrown";
+    case GAL_COLOR_ROYALBLUE:            return "royalblue";
+    case GAL_COLOR_SADDLEBROWN:          return "saddlebrown";
+    case GAL_COLOR_SALMON:               return "salmon";
+    case GAL_COLOR_SANDYBROWN:           return "sandybrown";
+    case GAL_COLOR_SEAGREEN:             return "seagreen";
+    case GAL_COLOR_SEASHELL:             return "seashell";
+    case GAL_COLOR_SIENNA:               return "sienna";
+    case GAL_COLOR_SILVER:               return "silver";
+    case GAL_COLOR_SKYBLUE:              return "skyblue";
+    case GAL_COLOR_SLATEBLUE:            return "slateblue";
+    case GAL_COLOR_SLATEGRAY:            return "slategray";
+    case GAL_COLOR_SNOW:                 return "snow";
+    case GAL_COLOR_SPRINGGREEN:          return "springgreen";
+    case GAL_COLOR_STEELBLUE:            return "steelblue";
+    case GAL_COLOR_TAN:                  return "tan";
+    case GAL_COLOR_TEAL:                 return "teal";
+    case GAL_COLOR_THISTLE:              return "thistle";
+    case GAL_COLOR_TOMATO:               return "tomato";
+    case GAL_COLOR_TURQUOISE:            return "turquoise";
+    case GAL_COLOR_VIOLET:               return "violet";
+    case GAL_COLOR_WHEAT:                return "wheat";
+    case GAL_COLOR_WHITE:                return "white";
+    case GAL_COLOR_WHITESMOKE:           return "whitesmoke";
+    case GAL_COLOR_YELLOW:               return "yellow";
+    case GAL_COLOR_YELLOWGREEN:          return "yellowgreen";
+    default:
+      error(EXIT_FAILURE, 0, "%s: color id '%u' is not recognized",
+            __func__, color);
+    }
+
+  /* Control should not reach here. */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to find "
+        "and solve the problem. Control should not reach this part "
+        "of the function", __func__, PACKAGE_BUGREPORT);
+  return GAL_BLANK_STRING;
+}
+
+
+
+
+
+/* Return the fractional RGB color space of pre-defined colors. We are
+   using 'n' for "name" and 'f' for RGB-Fractional value to help in
+   readability. The names and values are taken from the extended HTML/CSS
+   colors: https://en.wikipedia.org/wiki/Web_colors#HTML_color_names.
+
+   After copying the names and values into a plain-text file (by mouse,
+   group by group, 'color.txt'), I converted it to the format used in this
+   function with the following AWK command:
+
+      awk '{printf "case GAL_COLOR_%s: ", toupper($1); \
+            printf "f[0]=%.2f; f[1]=%.2f; f[2]=%.2f; break;\n", \
+                   $5/255, $6/255, $7/255}' colors.txt \
+                   | sort \
+                   | awk '{printf "%s %-25s %s %s %s %s\n", \
+                                  $1, $2, $3, $4, $5, $6}'
+*/
+void
+gal_color_in_rgb(uint8_t color, float *f)
+{
+  switch(color)
+    {
+    case GAL_COLOR_ALICEBLUE:      f[0]=0.94; f[1]=0.97; f[2]=1.00; break;
+    case GAL_COLOR_ANTIQUEWHITE:   f[0]=0.98; f[1]=0.92; f[2]=0.84; break;
+    case GAL_COLOR_AQUA:           f[0]=0.00; f[1]=1.00; f[2]=1.00; break;
+    case GAL_COLOR_AQUAMARINE:     f[0]=0.50; f[1]=1.00; f[2]=0.83; break;
+    case GAL_COLOR_AZURE:          f[0]=0.94; f[1]=1.00; f[2]=1.00; break;
+    case GAL_COLOR_BEIGE:          f[0]=0.96; f[1]=0.96; f[2]=0.86; break;
+    case GAL_COLOR_BISQUE:         f[0]=1.00; f[1]=0.89; f[2]=0.77; break;
+    case GAL_COLOR_BLACK:          f[0]=0.00; f[1]=0.00; f[2]=0.00; break;
+    case GAL_COLOR_BLANCHEDALMOND: f[0]=1.00; f[1]=0.92; f[2]=0.80; break;
+    case GAL_COLOR_BLUE:           f[0]=0.00; f[1]=0.00; f[2]=1.00; break;
+    case GAL_COLOR_BLUEVIOLET:     f[0]=0.54; f[1]=0.17; f[2]=0.89; break;
+    case GAL_COLOR_BROWN:          f[0]=0.65; f[1]=0.16; f[2]=0.16; break;
+    case GAL_COLOR_BURLYWOOD:      f[0]=0.87; f[1]=0.72; f[2]=0.53; break;
+    case GAL_COLOR_CADETBLUE:      f[0]=0.37; f[1]=0.62; f[2]=0.63; break;
+    case GAL_COLOR_CHARTREUSE:     f[0]=0.50; f[1]=1.00; f[2]=0.00; break;
+    case GAL_COLOR_CHOCOLATE:      f[0]=0.82; f[1]=0.41; f[2]=0.12; break;
+    case GAL_COLOR_CORAL:          f[0]=1.00; f[1]=0.50; f[2]=0.31; break;
+    case GAL_COLOR_CORNFLOWERBLUE: f[0]=0.39; f[1]=0.58; f[2]=0.93; break;
+    case GAL_COLOR_CORNSILK:       f[0]=1.00; f[1]=0.97; f[2]=0.86; break;
+    case GAL_COLOR_CRIMSON:        f[0]=0.86; f[1]=0.08; f[2]=0.24; break;
+    case GAL_COLOR_CYAN:           f[0]=0.00; f[1]=1.00; f[2]=1.00; break;
+    case GAL_COLOR_DARKBLUE:       f[0]=0.00; f[1]=0.00; f[2]=0.55; break;
+    case GAL_COLOR_DARKCYAN:       f[0]=0.00; f[1]=0.55; f[2]=0.55; break;
+    case GAL_COLOR_DARKGOLDENROD:  f[0]=0.72; f[1]=0.53; f[2]=0.04; break;
+    case GAL_COLOR_DARKGRAY:       f[0]=0.66; f[1]=0.66; f[2]=0.66; break;
+    case GAL_COLOR_DARKGREEN:      f[0]=0.00; f[1]=0.39; f[2]=0.00; break;
+    case GAL_COLOR_DARKKHAKI:      f[0]=0.74; f[1]=0.72; f[2]=0.42; break;
+    case GAL_COLOR_DARKMAGENTA:    f[0]=0.55; f[1]=0.00; f[2]=0.55; break;
+    case GAL_COLOR_DARKOLIVEGREEN: f[0]=0.33; f[1]=0.42; f[2]=0.18; break;
+    case GAL_COLOR_DARKORANGE:     f[0]=1.00; f[1]=0.55; f[2]=0.00; break;
+    case GAL_COLOR_DARKORCHID:     f[0]=0.60; f[1]=0.20; f[2]=0.80; break;
+    case GAL_COLOR_DARKRED:        f[0]=0.55; f[1]=0.00; f[2]=0.00; break;
+    case GAL_COLOR_DARKSALMON:     f[0]=0.91; f[1]=0.59; f[2]=0.48; break;
+    case GAL_COLOR_DARKSEAGREEN:   f[0]=0.56; f[1]=0.74; f[2]=0.56; break;
+    case GAL_COLOR_DARKSLATEBLUE:  f[0]=0.28; f[1]=0.24; f[2]=0.55; break;
+    case GAL_COLOR_DARKSLATEGRAY:  f[0]=0.18; f[1]=0.31; f[2]=0.31; break;
+    case GAL_COLOR_DARKTURQUOISE:  f[0]=0.00; f[1]=0.81; f[2]=0.82; break;
+    case GAL_COLOR_DARKVIOLET:     f[0]=0.58; f[1]=0.00; f[2]=0.83; break;
+    case GAL_COLOR_DEEPPINK:       f[0]=1.00; f[1]=0.08; f[2]=0.58; break;
+    case GAL_COLOR_DEEPSKYBLUE:    f[0]=0.00; f[1]=0.75; f[2]=1.00; break;
+    case GAL_COLOR_DIMGRAY:        f[0]=0.41; f[1]=0.41; f[2]=0.41; break;
+    case GAL_COLOR_DODGERBLUE:     f[0]=0.12; f[1]=0.56; f[2]=1.00; break;
+    case GAL_COLOR_FIREBRICK:      f[0]=0.70; f[1]=0.13; f[2]=0.13; break;
+    case GAL_COLOR_FLORALWHITE:    f[0]=1.00; f[1]=0.98; f[2]=0.94; break;
+    case GAL_COLOR_FORESTGREEN:    f[0]=0.13; f[1]=0.55; f[2]=0.13; break;
+    case GAL_COLOR_FUCHSIA:        f[0]=1.00; f[1]=0.00; f[2]=1.00; break;
+    case GAL_COLOR_GAINSBORO:      f[0]=0.86; f[1]=0.86; f[2]=0.86; break;
+    case GAL_COLOR_GHOSTWHITE:     f[0]=0.97; f[1]=0.97; f[2]=1.00; break;
+    case GAL_COLOR_GOLDENROD:      f[0]=0.85; f[1]=0.65; f[2]=0.13; break;
+    case GAL_COLOR_GOLD:           f[0]=1.00; f[1]=0.84; f[2]=0.00; break;
+    case GAL_COLOR_GRAY:           f[0]=0.50; f[1]=0.50; f[2]=0.50; break;
+    case GAL_COLOR_GREEN:          f[0]=0.00; f[1]=0.50; f[2]=0.00; break;
+    case GAL_COLOR_GREENYELLOW:    f[0]=0.68; f[1]=1.00; f[2]=0.18; break;
+    case GAL_COLOR_HONEYDEW:       f[0]=0.94; f[1]=1.00; f[2]=0.94; break;
+    case GAL_COLOR_HOTPINK:        f[0]=1.00; f[1]=0.41; f[2]=0.71; break;
+    case GAL_COLOR_INDIANRED:      f[0]=0.80; f[1]=0.36; f[2]=0.36; break;
+    case GAL_COLOR_INDIGO:         f[0]=0.29; f[1]=0.00; f[2]=0.51; break;
+    case GAL_COLOR_IVORY:          f[0]=1.00; f[1]=1.00; f[2]=0.94; break;
+    case GAL_COLOR_KHAKI:          f[0]=0.94; f[1]=0.90; f[2]=0.55; break;
+    case GAL_COLOR_LAVENDERBLUSH:  f[0]=1.00; f[1]=0.94; f[2]=0.96; break;
+    case GAL_COLOR_LAVENDER:       f[0]=0.90; f[1]=0.90; f[2]=0.98; break;
+    case GAL_COLOR_LAWNGREEN:      f[0]=0.49; f[1]=0.99; f[2]=0.00; break;
+    case GAL_COLOR_LEMONCHIFFON:   f[0]=1.00; f[1]=0.98; f[2]=0.80; break;
+    case GAL_COLOR_LIGHTBLUE:      f[0]=0.68; f[1]=0.85; f[2]=0.90; break;
+    case GAL_COLOR_LIGHTCORAL:     f[0]=0.94; f[1]=0.50; f[2]=0.50; break;
+    case GAL_COLOR_LIGHTCYAN:      f[0]=0.88; f[1]=1.00; f[2]=1.00; break;
+    case GAL_COLOR_LIGHTGOLDENRODYELLOW: f[0]=0.98; f[1]=0.98; f[2]=0.82; 
break;
+    case GAL_COLOR_LIGHTGRAY:      f[0]=0.83; f[1]=0.83; f[2]=0.83; break;
+    case GAL_COLOR_LIGHTGREEN:     f[0]=0.56; f[1]=0.93; f[2]=0.56; break;
+    case GAL_COLOR_LIGHTPINK:      f[0]=1.00; f[1]=0.71; f[2]=0.76; break;
+    case GAL_COLOR_LIGHTSALMON:    f[0]=1.00; f[1]=0.63; f[2]=0.48; break;
+    case GAL_COLOR_LIGHTSEAGREEN:  f[0]=0.13; f[1]=0.70; f[2]=0.67; break;
+    case GAL_COLOR_LIGHTSKYBLUE:   f[0]=0.53; f[1]=0.81; f[2]=0.98; break;
+    case GAL_COLOR_LIGHTSLATEGRAY: f[0]=0.47; f[1]=0.53; f[2]=0.60; break;
+    case GAL_COLOR_LIGHTSTEELBLUE: f[0]=0.69; f[1]=0.77; f[2]=0.87; break;
+    case GAL_COLOR_LIGHTYELLOW:    f[0]=1.00; f[1]=1.00; f[2]=0.88; break;
+    case GAL_COLOR_LIME:           f[0]=0.00; f[1]=1.00; f[2]=0.00; break;
+    case GAL_COLOR_LIMEGREEN:      f[0]=0.20; f[1]=0.80; f[2]=0.20; break;
+    case GAL_COLOR_LINEN:          f[0]=0.98; f[1]=0.94; f[2]=0.90; break;
+    case GAL_COLOR_MAGENTA:        f[0]=1.00; f[1]=0.00; f[2]=1.00; break;
+    case GAL_COLOR_MAROON:         f[0]=0.50; f[1]=0.00; f[2]=0.00; break;
+    case GAL_COLOR_MEDIUMAQUAMARINE: f[0]=0.40; f[1]=0.80; f[2]=0.67; break;
+    case GAL_COLOR_MEDIUMBLUE:     f[0]=0.00; f[1]=0.00; f[2]=0.80; break;
+    case GAL_COLOR_MEDIUMORCHID:   f[0]=0.73; f[1]=0.33; f[2]=0.83; break;
+    case GAL_COLOR_MEDIUMPURPLE:   f[0]=0.58; f[1]=0.44; f[2]=0.86; break;
+    case GAL_COLOR_MEDIUMSEAGREEN: f[0]=0.24; f[1]=0.70; f[2]=0.44; break;
+    case GAL_COLOR_MEDIUMSLATEBLUE: f[0]=0.48; f[1]=0.41; f[2]=0.93; break;
+    case GAL_COLOR_MEDIUMSPRINGGREEN: f[0]=0.00; f[1]=0.98; f[2]=0.60; break;
+    case GAL_COLOR_MEDIUMTURQUOISE: f[0]=0.28; f[1]=0.82; f[2]=0.80; break;
+    case GAL_COLOR_MEDIUMVIOLETRED: f[0]=0.78; f[1]=0.08; f[2]=0.52; break;
+    case GAL_COLOR_MIDNIGHTBLUE:   f[0]=0.10; f[1]=0.10; f[2]=0.44; break;
+    case GAL_COLOR_MINTCREAM:      f[0]=0.96; f[1]=1.00; f[2]=0.98; break;
+    case GAL_COLOR_MISTYROSE:      f[0]=1.00; f[1]=0.89; f[2]=0.88; break;
+    case GAL_COLOR_MOCCASIN:       f[0]=1.00; f[1]=0.89; f[2]=0.71; break;
+    case GAL_COLOR_NAVAJOWHITE:    f[0]=1.00; f[1]=0.87; f[2]=0.68; break;
+    case GAL_COLOR_NAVY:           f[0]=0.00; f[1]=0.00; f[2]=0.50; break;
+    case GAL_COLOR_OLDLACE:        f[0]=0.99; f[1]=0.96; f[2]=0.90; break;
+    case GAL_COLOR_OLIVEDRAB:      f[0]=0.42; f[1]=0.56; f[2]=0.14; break;
+    case GAL_COLOR_OLIVE:          f[0]=0.50; f[1]=0.50; f[2]=0.00; break;
+    case GAL_COLOR_ORANGE:         f[0]=1.00; f[1]=0.65; f[2]=0.00; break;
+    case GAL_COLOR_ORANGERED:      f[0]=1.00; f[1]=0.27; f[2]=0.00; break;
+    case GAL_COLOR_ORCHID:         f[0]=0.85; f[1]=0.44; f[2]=0.84; break;
+    case GAL_COLOR_PALEGOLDENROD:  f[0]=0.93; f[1]=0.91; f[2]=0.67; break;
+    case GAL_COLOR_PALEGREEN:      f[0]=0.60; f[1]=0.98; f[2]=0.60; break;
+    case GAL_COLOR_PALETURQUOISE:  f[0]=0.69; f[1]=0.93; f[2]=0.93; break;
+    case GAL_COLOR_PALEVIOLETRED:  f[0]=0.86; f[1]=0.44; f[2]=0.58; break;
+    case GAL_COLOR_PAPAYAWHIP:     f[0]=1.00; f[1]=0.94; f[2]=0.84; break;
+    case GAL_COLOR_PEACHPUFF:      f[0]=1.00; f[1]=0.85; f[2]=0.73; break;
+    case GAL_COLOR_PERU:           f[0]=0.80; f[1]=0.52; f[2]=0.25; break;
+    case GAL_COLOR_PINK:           f[0]=1.00; f[1]=0.75; f[2]=0.80; break;
+    case GAL_COLOR_PLUM:           f[0]=0.87; f[1]=0.63; f[2]=0.87; break;
+    case GAL_COLOR_POWDERBLUE:     f[0]=0.69; f[1]=0.88; f[2]=0.90; break;
+    case GAL_COLOR_PURPLE:         f[0]=0.50; f[1]=0.00; f[2]=0.50; break;
+    case GAL_COLOR_RED:            f[0]=1.00; f[1]=0.00; f[2]=0.00; break;
+    case GAL_COLOR_ROSYBROWN:      f[0]=0.74; f[1]=0.56; f[2]=0.56; break;
+    case GAL_COLOR_ROYALBLUE:      f[0]=0.25; f[1]=0.41; f[2]=0.88; break;
+    case GAL_COLOR_SADDLEBROWN:    f[0]=0.55; f[1]=0.27; f[2]=0.07; break;
+    case GAL_COLOR_SALMON:         f[0]=0.98; f[1]=0.50; f[2]=0.45; break;
+    case GAL_COLOR_SANDYBROWN:     f[0]=0.96; f[1]=0.64; f[2]=0.38; break;
+    case GAL_COLOR_SEAGREEN:       f[0]=0.18; f[1]=0.55; f[2]=0.34; break;
+    case GAL_COLOR_SEASHELL:       f[0]=1.00; f[1]=0.96; f[2]=0.93; break;
+    case GAL_COLOR_SIENNA:         f[0]=0.63; f[1]=0.32; f[2]=0.18; break;
+    case GAL_COLOR_SILVER:         f[0]=0.75; f[1]=0.75; f[2]=0.75; break;
+    case GAL_COLOR_SKYBLUE:        f[0]=0.53; f[1]=0.81; f[2]=0.92; break;
+    case GAL_COLOR_SLATEBLUE:      f[0]=0.42; f[1]=0.35; f[2]=0.80; break;
+    case GAL_COLOR_SLATEGRAY:      f[0]=0.44; f[1]=0.50; f[2]=0.56; break;
+    case GAL_COLOR_SNOW:           f[0]=1.00; f[1]=0.98; f[2]=0.98; break;
+    case GAL_COLOR_SPRINGGREEN:    f[0]=0.00; f[1]=1.00; f[2]=0.50; break;
+    case GAL_COLOR_STEELBLUE:      f[0]=0.27; f[1]=0.51; f[2]=0.71; break;
+    case GAL_COLOR_TAN:            f[0]=0.82; f[1]=0.71; f[2]=0.55; break;
+    case GAL_COLOR_TEAL:           f[0]=0.00; f[1]=0.50; f[2]=0.50; break;
+    case GAL_COLOR_THISTLE:        f[0]=0.85; f[1]=0.75; f[2]=0.85; break;
+    case GAL_COLOR_TOMATO:         f[0]=1.00; f[1]=0.39; f[2]=0.28; break;
+    case GAL_COLOR_TURQUOISE:      f[0]=0.25; f[1]=0.88; f[2]=0.82; break;
+    case GAL_COLOR_VIOLET:         f[0]=0.93; f[1]=0.51; f[2]=0.93; break;
+    case GAL_COLOR_WHEAT:          f[0]=0.96; f[1]=0.87; f[2]=0.70; break;
+    case GAL_COLOR_WHITE:          f[0]=1.00; f[1]=1.00; f[2]=1.00; break;
+    case GAL_COLOR_WHITESMOKE:     f[0]=0.96; f[1]=0.96; f[2]=0.96; break;
+    case GAL_COLOR_YELLOW:         f[0]=1.00; f[1]=1.00; f[2]=0.00; break;
+    case GAL_COLOR_YELLOWGREEN:    f[0]=0.60; f[1]=0.80; f[2]=0.20; break;
+    default:
+      error(EXIT_FAILURE, 0, "%s: code '%u' doesn't correspond to "
+            "any recognized color", __func__, color);
+    }
+}
diff --git a/lib/data.c b/lib/data.c
index ce4700c5..caa997a7 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -536,7 +536,7 @@ data_copy_from_string(gal_data_t *from, gal_data_t *to)
       }                                                                 \
   }
 
-#define COPY_TO_STR_FLT(CTYPE, BLANK) {                                 \
+#define COPY_TO_STR_FLT(CTYPE, BLANK, FMT) {                            \
     CTYPE *a=from->array;                                               \
     for(i=0;i<from->size;++i)                                           \
       {                                                                 \
@@ -544,7 +544,7 @@ data_copy_from_string(gal_data_t *from, gal_data_t *to)
         else             isblank = a[i]==BLANK ? 1 : 0;                 \
         if(isblank==0)                                                  \
           {                                                             \
-            if( asprintf(&strarr[i], "%f", a[i])<0 )                    \
+            if( asprintf(&strarr[i], FMT, a[i])<0 )                    \
               error(EXIT_FAILURE, 0, "%s: asprintf allocation", __func__); \
           }                                                             \
         else gal_checkset_allocate_copy(GAL_BLANK_STRING, &strarr[i]);  \
@@ -557,7 +557,8 @@ static void
 data_copy_to_string(gal_data_t *from, gal_data_t *to)
 {
   size_t i;
-  int isblank;
+  char *fltfmt;
+  int isblank, leftadjust=1;
   char **strarr=to->array, **instrarr=from->array;
 
   /* Sanity check */
@@ -568,6 +569,28 @@ data_copy_to_string(gal_data_t *from, gal_data_t *to)
           "('block' element must be NULL). Please contact us at %s so we "
           "can implement this feature", __func__, PACKAGE_BUGREPORT);
 
+  /* For the two floating point formats, the user may have given a certain
+     width and precision. */
+  if(from->disp_width>0 || from->disp_precision>0)
+    {
+      if(from->disp_width>0)    /* Both width and precision are given. */
+        {
+          if( asprintf(&fltfmt, "%%%s%d.%df",
+                       leftadjust ? "-" : "", from->disp_width,
+                       from->disp_precision)<0 )
+            error(EXIT_FAILURE, 0, "%s: asprintf allocation error "
+                  "(with width)", __func__);
+        }
+      else                      /* Only precision is given. */
+        {
+          if( asprintf(&fltfmt, "%%.%df", from->disp_precision)<0 )
+            error(EXIT_FAILURE, 0, "%s: asprintf allocation error "
+                  "(without width)", __func__);
+        }
+    }
+  else
+    gal_checkset_allocate_copy("%g", &fltfmt);
+
   /* Do the copying */
   switch(from->type)
     {
@@ -596,10 +619,10 @@ data_copy_to_string(gal_data_t *from, gal_data_t *to)
       COPY_TO_STR_INT(int64_t,  GAL_BLANK_INT64,  "%"PRId64); break;
 
     case GAL_TYPE_FLOAT32:
-      COPY_TO_STR_FLT(float, GAL_BLANK_FLOAT32);              break;
+      COPY_TO_STR_FLT(float, GAL_BLANK_FLOAT32, fltfmt);      break;
 
     case GAL_TYPE_FLOAT64:
-      COPY_TO_STR_FLT(double, GAL_BLANK_FLOAT32);             break;
+      COPY_TO_STR_FLT(double, GAL_BLANK_FLOAT64, fltfmt);     break;
 
     case GAL_TYPE_STRING:
       for(i=0;i<from->size;++i)
diff --git a/lib/eps.c b/lib/eps.c
index 37006284..494ee7a3 100644
--- a/lib/eps.c
+++ b/lib/eps.c
@@ -28,8 +28,11 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <stdlib.h>
 #include <string.h>
 
+#include <gnuastro/box.h>
 #include <gnuastro/eps.h>
 #include <gnuastro/list.h>
+#include <gnuastro/color.h>
+#include <gnuastro/blank.h>
 
 #include <gnuastro-internal/timing.h>
 #include <gnuastro-internal/checkset.h>
@@ -49,7 +52,7 @@ gal_eps_name_is_eps(char *name)
   if(name)
     {
       len=strlen(name);
-      if ( ( len>=3 && strcmp(&name[len-3], "eps") == 0 )
+      if ( (    len>=3 && strcmp(&name[len-3], "eps") == 0 )
            || ( len>=3 && strcmp(&name[len-3], "EPS") == 0 )
            || ( len>=4 && strcmp(&name[len-4], "epsf") == 0 )
            || ( len>=4 && strcmp(&name[len-4], "epsi") == 0 ) )
@@ -172,7 +175,7 @@ eps_convert_to_bitstream(gal_data_t *in, size_t *numbytes, 
uint8_t bitone)
 
       /* Put the values in. */
       arr=channel->array;
-      for(i=0;i<s0;++i)           /* i*s0+j is the byte, not bit position. */
+      for(i=0;i<s0;++i)       /* i*s0+j is the byte, not bit position. */
         for(j=0;j<bytesinrow;++j)
           {
             /* Set the 8 bits to zero. */
@@ -298,7 +301,8 @@ eps_write_ascii85(gal_data_t *write, FILE *fp, size_t 
numbytes)
 
 
 static void
-eps_write_image(gal_data_t *in, FILE *fp, int hex, int dontoptimize)
+eps_write_image(gal_data_t *in, FILE *fp, int hex, int dontoptimize,
+                int forps)
 {
   int bpc=8;
   uint8_t bitone;
@@ -352,6 +356,12 @@ eps_write_image(gal_data_t *in, FILE *fp, int hex, int 
dontoptimize)
   fprintf(fp, ">>\n");
   fprintf(fp, "image\n\n");
 
+  /* Finish the image part based on the final output. Note that 'grestore'
+     will also fix the translation we done on top so the point moves to
+     (0,0) before making the next path. */
+  if(forps) fprintf(fp, "showpage\n\n");
+  else      fprintf(fp, "grestore\n\n");
+
   /* Clean up. */
   if(write!=in)
     gal_list_data_free(write);
@@ -360,7 +370,9 @@ eps_write_image(gal_data_t *in, FILE *fp, int hex, int 
dontoptimize)
 
 
 
-
+/* The "point" is the smallest unit of measure in Postscript (and
+   typesetting in general). It is defined like this: 72 points is 1
+   international inch. So 72 points is 2.54 cm. */
 void
 gal_eps_to_pt(float widthincm, size_t *dsize, size_t *w_h_in_pt)
 {
@@ -372,9 +384,639 @@ gal_eps_to_pt(float widthincm, size_t *dsize, size_t 
*w_h_in_pt)
 
 
 
+/* Return the macro corresponding the to the given shape. */
+uint8_t
+gal_eps_shape_name_to_id(char *n)
+{
+  if(      !strcasecmp(n, "line")      ) return GAL_EPS_MARK_SHAPE_LINE;
+  else if( !strcasecmp(n, "plus")      ) return GAL_EPS_MARK_SHAPE_PLUS;
+  else if( !strcasecmp(n, "cross")     ) return GAL_EPS_MARK_SHAPE_CROSS;
+  else if( !strcasecmp(n, "point")     ) return GAL_EPS_MARK_SHAPE_POINT;
+  else if( !strcasecmp(n, "circle")    ) return GAL_EPS_MARK_SHAPE_CIRCLE;
+  else if( !strcasecmp(n, "square")    ) return GAL_EPS_MARK_SHAPE_SQUARE;
+  else if( !strcasecmp(n, "ellipse")   ) return GAL_EPS_MARK_SHAPE_ELLIPSE;
+  else if( !strcasecmp(n, "rectangle") ) return GAL_EPS_MARK_SHAPE_RECTANGLE;
+  else
+    error(EXIT_FAILURE, 0, "%s: the shape name '%s' is not recognized. "
+          "The currently recognized shapes are: 'point', 'circle', "
+          "'square', 'ellipse' or 'rectangle'", __func__, n);
+
+  /* Control should not reach here. */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to find "
+        "and solve the problem. Control should not reach this part "
+        "of the function", __func__, PACKAGE_BUGREPORT);
+  return GAL_EPS_MARK_SHAPE_INVALID;
+}
+
+
+
+
+
+/* Convert a shape ID to a name */
+char *
+gal_eps_shape_id_to_name(uint8_t id)
+{
+  /* Return the proper name. */
+  switch(id)
+    {
+    case GAL_EPS_MARK_SHAPE_LINE:      return "line";
+    case GAL_EPS_MARK_SHAPE_PLUS:      return "plus";
+    case GAL_EPS_MARK_SHAPE_CROSS:     return "cross";
+    case GAL_EPS_MARK_SHAPE_POINT:     return "point";
+    case GAL_EPS_MARK_SHAPE_CIRCLE:    return "circle";
+    case GAL_EPS_MARK_SHAPE_SQUARE:    return "square";
+    case GAL_EPS_MARK_SHAPE_ELLIPSE:   return "ellipse";
+    case GAL_EPS_MARK_SHAPE_RECTANGLE: return "rectangle";
+    default:
+      error(EXIT_FAILURE, 0, "%s: the shape id '%u' is not recognized. "
+            "Please see the 'GAL_EPS_MARK_SHAPE_*' macros in "
+            "'gnuastro/eps.h' for the acceptable ids", __func__, id);
+    }
+
+  /* Control should not reach here. */
+  error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to find "
+        "and solve the problem. Control should not reach this part "
+        "of the function", __func__, PACKAGE_BUGREPORT);
+  return GAL_BLANK_STRING;
+}
+
+
+
+
+
+/* Find the center coordinate */
+static void
+eps_mark_to_pt(float x_pix, float y_pix, float size1_pix,
+               float size2_pix, double pix_in_pt,
+               uint32_t borderwidth, long ymin, uint8_t shapev,
+               float *x_pt, float *y_pt,
+               float *size1_pt, float *size2_pt, float *ymin_pt)
+{
+  /* Necessary shift (in points) because of the coordiante system that has
+     been edited due to the image and border width */
+  float shiftpt=borderwidth;
+
+  /* Do the conversion: 1) Subtract by half so the bottom-left corners of
+     the point and FITS X axis match. 2) Multiply by the pixel-to-point
+     conversion scale. 3) account for the shift. */
+  *x_pt = (x_pix-0.5) * pix_in_pt + shiftpt;
+  *y_pt = (y_pix-0.5) * pix_in_pt + shiftpt;
+
+  /* Other parameters. Note that for an ellipse, the second size paramter
+     is the axis ratio. */
+  *ymin_pt  = ymin      * pix_in_pt;
+  *size1_pt = size1_pix * pix_in_pt;
+  *size2_pt = ( shapev==GAL_EPS_MARK_SHAPE_ELLIPSE
+                ? size2_pix
+                : size2_pix * pix_in_pt );
+}
+
+
+
+
+
+/* Low-level function to draw the shape in the corrected coordinate system
+   (after translation and rotation). */
+static void
+eps_mark_draw_shape(FILE *fp, uint8_t shape, float s1, float s2)
+{
+  /* Draw the shape. */
+  switch(shape)
+    {
+    case GAL_EPS_MARK_SHAPE_LINE:
+      fprintf(fp, "%f 0  moveto %f 0 lineto \n", -s1/2, s1/2);
+      break;
+
+    case GAL_EPS_MARK_SHAPE_PLUS:
+      fprintf(fp, "%f 0  moveto %f 0 lineto \n", -s1/2, s1/2);
+      fprintf(fp, "0  %f moveto 0 %f lineto closepath\n", -s1/2, s1/2);
+      break;
+
+    case GAL_EPS_MARK_SHAPE_CROSS:
+      fprintf(fp, "%f %f moveto %f %f lineto \n", -s1/M_SQRT2/2,
+              -s1/M_SQRT2/2, s1/M_SQRT2/2, s1/M_SQRT2/2);
+      fprintf(fp, "%f %f moveto %f %f lineto \n", -s1/M_SQRT2/2,
+              s1/M_SQRT2/2, s1/M_SQRT2/2, -s1/M_SQRT2/2);
+      break;
+
+    case GAL_EPS_MARK_SHAPE_POINT:
+      fprintf(fp, "newpath 0 0 %f 0 360 arc fill closepath\n", s1);
+      break;
+
+    case GAL_EPS_MARK_SHAPE_CIRCLE:
+      fprintf(fp, "newpath 0 0 %f 0 360 arc closepath\n", s1);
+      break;
+
+    case GAL_EPS_MARK_SHAPE_ELLIPSE:
+      fprintf(fp, "newpath 0 0 %f %f 0 360 ellipse\n", s1, s1*s2);
+      break;
+
+    case GAL_EPS_MARK_SHAPE_SQUARE:
+      fprintf(fp, "newpath -%.2f -%.2f %.2f %.2f rectstroke\n",
+              s1/2, s1/2, s1, s1);
+      break;
+
+    case GAL_EPS_MARK_SHAPE_RECTANGLE:
+      fprintf(fp, "newpath -%.2f -%.2f %.2f %.2f rectstroke\n",
+              s1/2, s2/2, s1, s2);
+      break;
+
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' to "
+            "fix the problem. The code '%d' is not recognized for the "
+            "'shape' variable", __func__, PACKAGE_BUGREPORT, shape);
+    }
+}
+
+
+
+
+
+/* Insert the part about the mark shape. */
+static void
+eps_mark_add_shape(FILE *fp, size_t index, float x, float y, float s1,
+                   float s2, float linewidth, uint8_t color, uint8_t shape,
+                   float rotate, char *text, float textsize, char *textfont,
+                   float yminpt)
+{
+  float rgb[3];
+
+  /* If the rotation angle is negative, add it with 360 so it becomes
+     positive. */
+  if(rotate<0.0) rotate+=360.0;
+
+  /* Print a comment, identifying that this is a mark. */
+  fprintf(fp, "%% Mark %zu (a %s):\n", index+1,
+          gal_eps_shape_id_to_name(shape));
+
+  /* If the shape is a circle, or the user gave a rotation angle of zero,
+     then rotation is irrelevant, so set it to NaN, so we don't even write
+     it into the file. */
+  if(shape==GAL_EPS_MARK_SHAPE_CIRCLE || rotate==0.0) rotate=NAN;
+
+  /* If 'linewidth' isn't blank, then we need to set it for each mark. */
+  if( !isnan(linewidth) )
+    fprintf(fp, "%f setlinewidth\n", linewidth);
+
+  /* Set the color (if it hasn't already been set. */
+  if( color != GAL_BLANK_UINT8 )
+    {
+      gal_color_in_rgb(color, rgb);
+      fprintf(fp, "%.2f %.2f %.2f setrgbcolor\n", rgb[0], rgb[1], rgb[2]);
+    }
+
+  /* Move the coordinate to the desired central coordinate and do the
+     rotation if requested. */
+  fprintf(fp, "%f %f translate\n", x, y);
+  if( !isnan(rotate) ) fprintf(fp, "%f rotate\n", rotate);
+
+  /* Draw the shape. */
+  eps_mark_draw_shape(fp, shape, s1, s2);
+
+  /* Correct the rotation. */
+  if( !isnan(rotate) )
+    fprintf(fp, "-%f rotate\n", rotate);
+  fprintf(fp, "-%f -%f translate\n", x, y);
+
+  /* Finish the stroke. */
+  fprintf(fp, "stroke\n\n");
+
+  /* Write the text (if 'text' isn't NULL, and it isn't a blank string. */
+  if( text && strcmp(text, GAL_BLANK_STRING) )
+    {
+      fprintf(fp, "%% Text of the mark above\n");
+      fprintf(fp, "(%s) %g /%s %g %g centertoptext\n\n", text,
+              textsize, textfont, x, yminpt);
+    }
+}
+
+
+
+
+/* Simplify the checking of various columns. */
+static void *
+eps_mark_prepare_col(gal_data_t *marks, char *name, uint8_t type,
+                     int abort, char *extrainfo)
+{
+  gal_data_t *tmp=gal_list_data_select_by_name(marks, name);
+
+  /* If a list element was found, check if it has the correct type. */
+  if(tmp)
+    {
+      if(tmp->type!=type)
+        error(EXIT_FAILURE, 0, "%s: the '%s' column should have "
+              "a %s numeric data type%s", __func__, name,
+              gal_type_name(type, 1), extrainfo);
+      return tmp->array;
+    }
+  else
+    {
+      if(abort)
+        error(EXIT_FAILURE, 0, "%s: no column with name '%s' was found "
+              "in the list of mark metadata", __func__, name);
+    }
+
+  /* No list element with the desired name was found. */
+  return NULL;
+}
+
+
+
+
+
+/* Make sure the inputs are in the desired format and return the usable
+   arrays for the main function to add marks. */
+static void
+eps_mark_prepare(gal_data_t *marks, float **x, float **y, uint8_t **shape,
+                 uint8_t **color, float **size1, float **size2,
+                 float **lwidth, float **rotate, char ***text,
+                 char ***font, float **fontsize)
+{
+  size_t i;
+  gal_data_t *tmp;
+
+  /* Make sure all the columns given to 'marks' have the same number
+     elements. */
+  for(tmp=marks;tmp!=NULL;tmp=tmp->next)
+    {
+      if(tmp->size!=marks->size)
+        error(EXIT_FAILURE, 0, "%s: the mark column '%s' has "
+              "a different number of rows, or elements (%zu), than "
+              "the first (named '%s' with %zu rows)", __func__,
+              tmp->name, tmp->size, marks->name, marks->size);
+    }
+
+  /* Check each column. */
+  *x=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_XPIX,
+                          GAL_TYPE_FLOAT32, 1, "");
+  *y=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_YPIX,
+                          GAL_TYPE_FLOAT32, 1, "");
+  *text=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_TEXT,
+                             GAL_TYPE_STRING, 0, "");
+  *font=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_FONT,
+                             GAL_TYPE_STRING, 0, "");
+  *size1=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_SIZE1,
+                             GAL_TYPE_FLOAT32, 0, "");
+  *size2=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_SIZE2,
+                             GAL_TYPE_FLOAT32, 0, "");
+  *rotate=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_ROTATE,
+                               GAL_TYPE_FLOAT32, 0, "");
+  *lwidth=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_LINEWIDTH,
+                               GAL_TYPE_FLOAT32, 0, "");
+  *fontsize=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_FONTSIZE,
+                                 GAL_TYPE_FLOAT32, 0, "");
+  *shape=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_SHAPE,
+                              GAL_TYPE_UINT8, 0, ". Note that the macros "
+                              "containing shape identifiers have the "
+                              "'GAL_EPS_MARK_SHAPE-' prefix and are "
+                              "defined in 'gnuastro/eps.h'");
+  *color=eps_mark_prepare_col(marks, GAL_EPS_MARK_COLNAME_COLOR,
+                              GAL_TYPE_UINT8, 0, ". Note that the macros "
+                              "containing color identifiers have the "
+                              "'GAL_COLOR_' prefix and are "
+                              "defined in 'gnuastro/color.h'");
+
+  /* Some sanity checks for the columns that need one. */
+  if( *rotate || *shape )
+    for(i=0;i<marks->size;++i)
+      {
+        /* Rotation angle should be between 0 to 360. */
+        if( *rotate && ((*rotate)[i]<-360 || (*rotate)[i]>360 ) )
+          error(EXIT_FAILURE, 0, "%s: %g is not a valid rotation angle "
+                "(in degrees). It belongs to mark number %zu (counting "
+                "from 1)", __func__, (*rotate)[i], i+1);
+
+        /* For an ellipse, the 'size2' column should be positive and
+           smaller than one. */
+        if( (*shape)[i]==GAL_EPS_MARK_SHAPE_ELLIPSE
+            && *size2
+            && ( (*size2)[i]<0 || (*size2)[i]>1 ) )
+          error(EXIT_FAILURE, 0, "%s: %g is not a valid 'size2' column "
+                "for an ellipse shape (from mark number %zu, counting "
+                "from 1). For an ellipse, the 'size2' column is the"
+                "axis ratio, so it should always be between 0 and 1",
+                __func__, (*size2)[i], i+1);
+      }
+}
+
+
+
+
+
+/* Write the default mark properties (like linewidth, color and etc). This
+   can happen if they aren't given by the user, or that all the values
+   given by the user are the same. */
+static void
+eps_mark_add_defaults(FILE *fp, gal_data_t *marks, uint8_t **color_o,
+                      float **lwidth_o, uint8_t *shape, char **text)
+{
+  size_t i;
+  float rgb[3];
+  uint8_t *color=*color_o;
+  float *lwidth=*lwidth_o;
+  int samelwidth=1, samecolor=1;
+
+  /* Parse all the information */
+  for(i=1;i<marks->size;++i)
+    {
+      if(samecolor==1  && color  && color[i]!=color[0]  ) samecolor=0;
+      if(samelwidth==1 && lwidth && lwidth[i]!=lwidth[0]) samelwidth=0;
+    }
+
+  /* Default color */
+  if(samecolor)
+    {
+      fprintf(fp, "%% Same color for all marks:\n");
+      gal_color_in_rgb(color ? color[0] : GAL_EPS_MARK_DEFAULT_COLOR, rgb);
+      fprintf(fp, "%.2f %.2f %.2f setrgbcolor\n\n", rgb[0], rgb[1], rgb[2]);
+    }
+
+  /* Default line-width. */
+  if(samelwidth)
+    {
+      fprintf(fp, "%% Same line width for all marks:\n");
+      fprintf(fp, "%f setlinewidth\n\n",
+              lwidth ? lwidth[0] : GAL_EPS_MARK_DEFAULT_LINEWIDTH);
+      *lwidth_o=NULL;
+    }
+
+  /* If an ellipse has been requested, define the function (this function
+     has been inspired from http://www.redgrittybrick.org/ellipse.html */
+  if(shape)
+    {
+      for(i=0;i<marks->size;++i)
+        if(shape[i]==GAL_EPS_MARK_SHAPE_ELLIPSE)
+          {
+            fprintf(fp, "%% Function for ellipse shape:\n");
+            fprintf(fp, "/ellipse {\n");
+            fprintf(fp, "    /endangle exch def\n");
+            fprintf(fp, "    /startangle exch def\n");
+            fprintf(fp, "    /yrad exch def\n");
+            fprintf(fp, "    /xrad exch def\n");
+            fprintf(fp, "    /y exch def\n");
+            fprintf(fp, "    /x exch def\n");
+            fprintf(fp, "    /savematrix matrix currentmatrix def\n");
+            fprintf(fp, "    x y translate\n");
+            fprintf(fp, "    xrad yrad scale\n");
+            fprintf(fp, "    0 0 1 startangle endangle arc\n");
+            fprintf(fp, "    savematrix setmatrix\n");
+            fprintf(fp, "} def\n\n");
+            break;
+          }
+    }
+
+  /* In case we have text, use this function to put them such that they the
+     given coordinate is at the top of the string. This was inspired from
+     [1] (the main change is that '-2 div' (for 'dy') has been set to '-1
+     mul' so the given coordinate is at the top. It should be called with
+     the following format (just set the upper-case words).
+
+         (STRING) FONTSIZE FONTNAME X Y centertoptext
+
+     [1] 
https://stackoverflow.com/questions/3618194/how-to-determine-string-height-in-postscript*/
+  if(text)
+    {
+      fprintf(fp, "%% Print text with coordinate at center-top:\n");
+      fprintf(fp, "/centertoptext {\n");
+
+      /* Save the current state. */
+      fprintf(fp, "  gsave\n");
+
+      /* Move to the top two popped operands (X and Y), and set the fonts. */
+      fprintf(fp, "   moveto findfont exch scalefont setfont\n");
+
+      /* Save the state. */
+      fprintf(fp, "   gsave\n");
+
+      /* 1. dup: Duplicate the string.
+         2. charpath: move to where the string finishes.
+         3. flattenpath: account for curves.
+         4. pathbox: put four numbers on the stack:
+             x0: bottom-left point's X axis position.
+             y0: bottom-left point's Y axis position.
+             x1: top-right point's X axis position.
+             y1: top-right point's Y axis position. */
+      fprintf(fp, "    dup false charpath flattenpath pathbbox\n");
+
+      /* Go back to the previous settings (before charpath changed it). */
+      fprintf(fp, "   grestore\n");
+
+      /* 3 -1 roll: permutes the top three: y0 x1 y1 ---> x1 y1 y0 */
+      fprintf(fp, "   3 -1 roll\n");
+
+      /* Subtract the second popped operand from the first (at the top, we
+         now have the height of the text's box or 'dy'. Then multiply this
+         by -1, giving us '-dy' (which is the distance we want to shift
+         vertically/downwards before "showing" the string. */
+      fprintf(fp, "   sub -1 mul\n");
+
+      /* 3 1 roll: permute the top three points: x0 x1 dy --> dy x0 x1 */
+      fprintf(fp, "   3 1 roll\n");
+
+      /* Subtract x0 from x1 get the negative 'dx' on top of the stack.
+         Then divide it by two, so we have '-dx/2' (we need to shift the
+         word by the amount along the horizintal/left-ward before "show"ing
+         it).*/
+      fprintf(fp, "   sub 2 div\n");
+
+      /* Exchange the top operands so they are in this order: '-dx -dy'. */
+      fprintf(fp, "   exch \n");
+
+      /* Move (relatively) by the given displacement and print the string
+         (which has stayed at the bottom of the stack until now) and print
+         the string.. */
+      fprintf(fp, "   rmoveto show\n");
+
+      /* Restore the graphical environment for next paths. */
+      fprintf(fp, "  grestore\n");
+      fprintf(fp, "} bind def\n\n");
+    }
+}
+
+
+
+
+
+/* Set the second size parameter (depending on a shape being defined or
+   not). */
+static float
+eps_mark_size2(uint8_t *shape, float *size2arr, size_t i)
+{
+  if(size2arr) return size2arr[i];
+  else
+    {
+      if(shape)
+        switch(shape[i])
+          {
+          case GAL_EPS_MARK_SHAPE_ELLIPSE:
+            return GAL_EPS_MARK_DEFAULT_SIZE2_ELLIPSE;
+          default: return GAL_EPS_MARK_DEFAULT_SIZE2;
+          }
+      else
+        return GAL_EPS_MARK_DEFAULT_SIZE2;
+    }
+  return NAN;
+}
+
+
+
+
+
+/* Fill the 'fpixel' and 'lpixel' arrays for the shapes that may be
+   affected by rotation. */
+static void
+eps_mark_in_img_fl_pixel(float x, float y, float s1, float s2,
+                         uint8_t shape, long *fpixel, long *lpixel)
+{
+  long width[2];
+  double center[2]={x, y};
+
+  /* Depending the shape, set the four corners in relation to the shape's
+     center (these will later be added to the X,Y of the center). */
+  switch(shape)
+    {
+    case GAL_EPS_MARK_SHAPE_LINE:
+      width[0]=s1; width[1]=0.1; break; /* We will add line-width later, */
+                                        /* And width[1] shouldn't be 0.  */
+    case GAL_EPS_MARK_SHAPE_PLUS:
+    case GAL_EPS_MARK_SHAPE_SQUARE:
+      width[0]=width[1]=s1; break;
+
+    case GAL_EPS_MARK_SHAPE_POINT:
+    case GAL_EPS_MARK_SHAPE_CIRCLE:
+      width[0]=width[1]=s1*2; break; /* s1 is radius: half of width */
+
+    case GAL_EPS_MARK_SHAPE_CROSS:
+      width[0]=width[1]=M_SQRT2*s1; break;
+
+    case GAL_EPS_MARK_SHAPE_RECTANGLE:
+      width[0]=s1; width[1]=s2;
+      break;
+
+    case GAL_EPS_MARK_SHAPE_ELLIPSE:
+      width[0]=s1*2; width[1]=s1*s2*2; /* s1: half major axis. */
+      break;                           /* s2: axis ratio. */
+
+    default:
+      error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at '%s' to "
+            "find and fix the problem. The code '%u' isn't recognized "
+            "for the 'shape' variable", __func__, PACKAGE_BUGREPORT,
+            shape);
+    }
+
+  /* Find the before-rotation fpixel and lpixel */
+  gal_box_border_from_center(center, 2, width, fpixel, lpixel);
+
+  /* For a check:
+  printf("%s:center: %g, %g\n",   __func__, center[0], center[1]);
+  printf("%s:width:  %ld, %ld\n", __func__, width[0],  width[1]);
+  printf("%s:fpixel: %ld, %ld\n", __func__, fpixel[0], fpixel[1]);
+  printf("%s:lpixel: %ld, %ld\n", __func__, lpixel[0], lpixel[1]);
+  */
+}
+
+
+
+
+
+/* See if the given shape falls within the image or not. Currently, this
+   doesn't account for rotation due to time limits at development time. */
+static int
+eps_mark_in_img(size_t *dsize, size_t i, float x, float y,
+                float *size1arr, float *size2arr,
+                uint8_t *shapearr, float *rotarr, long *ymin)
+{
+  long naxes[2]={dsize[1], dsize[0]};
+  float s2 = eps_mark_size2(shapearr, size2arr, i);
+  long fpixel_i[2], lpixel_i[2], fpixel_o[2], lpixel_o[2];
+  uint8_t shape = shapearr ? shapearr[i] : GAL_EPS_MARK_DEFAULT_SHAPE;
+  float   s1    = size1arr ? size1arr[i] : GAL_EPS_MARK_DEFAULT_SIZE1;
+  float   rot   = rotarr   ? rotarr[i]   : GAL_EPS_MARK_DEFAULT_ROTATE;
+
+  /* Find the first and last pixels of the marks based on its shape. For a
+     point, things are easy, but for others, we have a separate
+     function. */
+  eps_mark_in_img_fl_pixel(x, y, s1, s2, shape, fpixel_i, lpixel_i);
+
+  /* Update the box's first and last pixels based on the rotation. This is
+     not necessary if we don't have any rotation or we are dealing with a
+     circle shape. */
+  if( rot!=0.0
+      && shape!=GAL_EPS_MARK_SHAPE_POINT
+      && shape!=GAL_EPS_MARK_SHAPE_CIRCLE )
+    gal_box_border_rotate_around_center(fpixel_i, lpixel_i, 2, rot);
+
+  /* Set the smallest vertical position of the box. */
+  *ymin=fpixel_i[1];
+
+  /* See if there is any overlap */
+  return gal_box_overlap(naxes, fpixel_i, lpixel_i,
+                         fpixel_o, lpixel_o, 2);
+}
+
+
+
+
+
+/* Add markers on each desired coordinate. */
+static void
+eps_mark_add(gal_data_t *in, gal_data_t *marks, FILE *fp,
+             size_t *w_h_in_pt, uint32_t borderwidth)
+{
+  size_t i;
+  long ymin;
+  float lwidthv;
+  char **text=NULL, **font=NULL;
+  uint8_t shapev, *shape=NULL, *color=NULL;
+  float s1, s2, *size1=NULL, *size2=NULL;
+  float yminpt, *rotate=NULL, *fontsize=NULL;
+  float x, y, *xarr=NULL, *yarr=NULL, *lwidth=NULL;
+
+  /* The size of one pixel in "points". */
+  double pix_in_pt=(double)(w_h_in_pt[0])/(double)(in->dsize[1]);
+
+  /* Load the pointers to the various metdata. */
+  eps_mark_prepare(marks, &xarr, &yarr, &shape, &color,
+                   &size1, &size2, &lwidth, &rotate, &text,
+                   &font, &fontsize);
+
+  /* If the mark parameters are similar, just write them once (instead of
+     repeating for each mark, and set their pointers to 'NULL' so the rest
+     of the function doesn't even bother with them). */
+  eps_mark_add_defaults(fp, marks, &color, &lwidth, shape, text);
+
+  /* Write the mark into the EPS file. But only when the requested region
+     falls within the image (we don't want to waste storage) */
+  for(i=0;i<marks->size;++i)
+    if( eps_mark_in_img(in->dsize, i, xarr[i], yarr[i], size1, size2,
+                        shape, rotate, &ymin) )
+      {
+        lwidthv=lwidth ? lwidth[i] : GAL_EPS_MARK_DEFAULT_LINEWIDTH;
+        shapev=shape ? shape[i] : GAL_EPS_MARK_DEFAULT_SHAPE,
+        eps_mark_to_pt(xarr[i], yarr[i],
+                       size1 ? size1[i] : GAL_EPS_MARK_DEFAULT_SIZE1,
+                       eps_mark_size2(shape, size2, i),
+                       pix_in_pt, borderwidth, ymin, shapev,
+                       &x, &y, &s1, &s2, &yminpt);
+        eps_mark_add_shape(fp, i, x, y, s1, s2, lwidthv,
+                           color    ? color[i]    : GAL_BLANK_UINT8,
+                           shapev,
+                           rotate   ? rotate[i]   : GAL_BLANK_FLOAT32,
+                           text     ? text[i]     : NULL,
+                           fontsize ? fontsize[i] : 
GAL_EPS_MARK_DEFAULT_FONTSIZE,
+                           font     ? font[i]     : GAL_EPS_MARK_DEFAULT_FONT,
+                           yminpt - lwidthv/2);
+      }
+}
+
+
+
+
+
 void
 gal_eps_write(gal_data_t *in, char *filename, float widthincm,
-              uint32_t borderwidth, int hex, int dontoptimize, int forpdf)
+              uint32_t borderwidth, int hex, int dontoptimize,
+              int forps, gal_data_t *marks)
 {
   FILE *fp;
   float hbw;
@@ -387,8 +1029,9 @@ gal_eps_write(gal_data_t *in, char *filename, float 
widthincm,
     error(EXIT_FAILURE, 0, "%s: only 1, 3, and 4 color channels are "
           "acceptable, input is a list of %zu data sets", __func__, numch);
   if(in->type!=GAL_TYPE_UINT8)
-    error(EXIT_FAILURE, 0, "%s: input has a '%s' type, but JPEG images can "
-          "only have a 'uint8' type", __func__, gal_type_name(in->type, 1));
+    error(EXIT_FAILURE, 0, "%s: input has a '%s' type, but JPEG images "
+          "can only have a 'uint8' type", __func__,
+          gal_type_name(in->type, 1));
 
 
   /* Read the time to write in the output. */
@@ -412,11 +1055,23 @@ gal_eps_write(gal_data_t *in, char *filename, float 
widthincm,
   fprintf(fp, "%%%%CreationDate: %s", ctime(&rawtime));
   fprintf(fp, "%%%%LanuageLevel: 3\n");
   fprintf(fp, "%%%%EndComments\n\n");
-  if(forpdf==0)
+  if(forps==0)
     fprintf(fp, "gsave\n\n");
 
 
-  /* Commands to draw the border: */
+  /* Write the image: */
+  fprintf(fp, "%% Draw the image:\n");
+  fprintf(fp, "%d %d translate\n", borderwidth, borderwidth);
+  fprintf(fp, "%zu %zu scale\n", w_h_in_pt[0], w_h_in_pt[1]);
+  eps_write_image(in, fp, hex, dontoptimize, forps);
+
+
+  /* Mark parts of the image if necessary. */
+  if(marks)
+    eps_mark_add(in, marks, fp, w_h_in_pt, borderwidth);
+
+
+    /* Commands to draw the border: */
   if(borderwidth)
     {
       fprintf(fp, "%% Draw the border:\n");
@@ -431,16 +1086,7 @@ gal_eps_write(gal_data_t *in, char *filename, float 
widthincm,
     }
 
 
-  /* Write the image: */
-  fprintf(fp, "%% Draw the image:\n");
-  fprintf(fp, "%d %d translate\n", borderwidth, borderwidth);
-  fprintf(fp, "%zu %zu scale\n", w_h_in_pt[0], w_h_in_pt[1]);
-  eps_write_image(in, fp, hex, dontoptimize);
-
-
   /* Ending of the EPS file: */
-  if(forpdf) fprintf(fp, "showpage\n");
-  else       fprintf(fp, "grestore\n");
   fprintf(fp, "%%%%EOF");
   fclose(fp);
 }
diff --git a/lib/gnuastro/arithmetic.h b/lib/gnuastro/arithmetic.h
index a82dd750..47fbfcdc 100644
--- a/lib/gnuastro/arithmetic.h
+++ b/lib/gnuastro/arithmetic.h
@@ -123,7 +123,7 @@ enum gal_arithmetic_operators
   GAL_ARITHMETIC_OP_ASIN,         /* Inverse sine (output in deg). */
   GAL_ARITHMETIC_OP_ACOS,         /* Inverse cosine (output in deg). */
   GAL_ARITHMETIC_OP_ATAN,         /* Inverse tangent (output in deg). */
-  GAL_ARITHMETIC_OP_ATAN2,        /* Inv. atan (preserves quad, out in deg). */
+  GAL_ARITHMETIC_OP_ATAN2,        /* Inv. atan (preserves quad, in deg). */
   GAL_ARITHMETIC_OP_SINH,         /* Hyperbolic sine. */
   GAL_ARITHMETIC_OP_COSH,         /* Hyperbolic cosine. */
   GAL_ARITHMETIC_OP_TANH,         /* Hyperbolic tangent. */
@@ -131,12 +131,27 @@ enum gal_arithmetic_operators
   GAL_ARITHMETIC_OP_ACOSH,        /* Inverse hyperbolic cosine. */
   GAL_ARITHMETIC_OP_ATANH,        /* Inverse hyperbolic tangent. */
 
+  GAL_ARITHMETIC_OP_E,            /* The base of natural logirithm. */
+  GAL_ARITHMETIC_OP_PI,           /* Circle circumference by diameter. */
+
+  GAL_ARITHMETIC_OP_C,            /* The speed of light. */
+  GAL_ARITHMETIC_OP_G,            /* The gravitational constant. */
+  GAL_ARITHMETIC_OP_H,            /* Plank's constant. */
+  GAL_ARITHMETIC_OP_AU,           /* Astronomical Unit (in meters). */
+  GAL_ARITHMETIC_OP_LY,           /* Light years (in meters). */
+  GAL_ARITHMETIC_OP_AVOGADRO,     /* Avogadro's number. */
+  GAL_ARITHMETIC_OP_FINESTRUCTURE,/* Fine-structure constant. */
+
   GAL_ARITHMETIC_OP_RA_TO_DEGREE, /* right ascension to decimal. */
   GAL_ARITHMETIC_OP_DEC_TO_DEGREE,/* declination to decimal. */
   GAL_ARITHMETIC_OP_DEGREE_TO_RA, /* right ascension to decimal. */
   GAL_ARITHMETIC_OP_DEGREE_TO_DEC,/* declination to decimal. */
   GAL_ARITHMETIC_OP_COUNTS_TO_MAG,/* Counts to magnitude. */
   GAL_ARITHMETIC_OP_MAG_TO_COUNTS,/* Magnitude to counts. */
+  GAL_ARITHMETIC_OP_MAG_TO_SB,    /* Magnitude to Surface Brightness. */
+  GAL_ARITHMETIC_OP_SB_TO_MAG,    /* Surface Brightness to Magnitude. */
+  GAL_ARITHMETIC_OP_COUNTS_TO_SB, /* Counts to Surface Brightness. */
+  GAL_ARITHMETIC_OP_SB_TO_COUNTS, /* Surface Brightness to counts. */
   GAL_ARITHMETIC_OP_COUNTS_TO_JY, /* Counts to Janskys with AB-mag zeropoint. 
*/
   GAL_ARITHMETIC_OP_JY_TO_COUNTS, /* Janskys to Counts with AB-mag zeropoint. 
*/
   GAL_ARITHMETIC_OP_MAG_TO_JY,    /* Magnitude to Janskys. */
diff --git a/lib/gnuastro/box.h b/lib/gnuastro/box.h
index ec45b99b..6cf1350a 100644
--- a/lib/gnuastro/box.h
+++ b/lib/gnuastro/box.h
@@ -69,6 +69,10 @@ void
 gal_box_border_from_center(double *center, size_t ndim, long *width,
                            long *fpixel, long *lpixel);
 
+void
+gal_box_border_rotate_around_center(long *fpixel, long *lpixel,
+                                    size_t ndim, float rotate_deg);
+
 int
 gal_box_overlap(long *naxes, long *fpixel_i, long *lpixel_i,
                 long *fpixel_o, long *lpixel_o, size_t ndim);
diff --git a/lib/gnuastro/color.h b/lib/gnuastro/color.h
new file mode 100644
index 00000000..a4a473bc
--- /dev/null
+++ b/lib/gnuastro/color.h
@@ -0,0 +1,240 @@
+/*********************************************************************
+Functions for colors
+This is part of GNU Astronomy Utilities (Gnuastro) package.
+
+Original author:
+     Mohammad Akhlaghi <mohammad@akhlaghi.org>
+Contributing author(s):
+Copyright (C) 2022 Free Software Foundation, Inc.
+
+Gnuastro is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Gnuastro is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
+**********************************************************************/
+#ifndef __GAL_COLOR_H__
+#define __GAL_COLOR_H__
+
+/* Include other headers if necessary here. Note that other header files
+   must be included before the C++ preparations below */
+#include <gnuastro/data.h>
+
+
+/* C++ Preparations */
+#undef __BEGIN_C_DECLS
+#undef __END_C_DECLS
+#ifdef __cplusplus
+# define __BEGIN_C_DECLS extern "C" {
+# define __END_C_DECLS }
+#else
+# define __BEGIN_C_DECLS                /* empty */
+# define __END_C_DECLS                  /* empty */
+#endif
+/* End of C++ preparations */
+
+
+
+/* Actual header contants (the above were for the Pre-processor). */
+__BEGIN_C_DECLS  /* From C++ preparations */
+
+
+
+/* From: https://en.wikipedia.org/wiki/Web_colors#Extended_colors */
+enum gal_color_all
+{
+  GAL_COLOR_INVALID,            /* ==0 (in C standard). */
+
+  /* Pink colors. */
+  GAL_COLOR_MEDIUMVIOLETRED,
+  GAL_COLOR_DEEPPINK,
+  GAL_COLOR_PALEVIOLETRED,
+  GAL_COLOR_HOTPINK,
+  GAL_COLOR_LIGHTPINK,
+  GAL_COLOR_PINK,
+
+  /* Red colors. */
+  GAL_COLOR_DARKRED,
+  GAL_COLOR_RED,
+  GAL_COLOR_FIREBRICK,
+  GAL_COLOR_CRIMSON,
+  GAL_COLOR_INDIANRED,
+  GAL_COLOR_LIGHTCORAL,
+  GAL_COLOR_SALMON,
+  GAL_COLOR_DARKSALMON,
+  GAL_COLOR_LIGHTSALMON,
+
+  /* Orange colors */
+  GAL_COLOR_ORANGERED,
+  GAL_COLOR_TOMATO,
+  GAL_COLOR_DARKORANGE,
+  GAL_COLOR_CORAL,
+  GAL_COLOR_ORANGE,
+
+  /* Yellow colors. */
+  GAL_COLOR_DARKKHAKI,
+  GAL_COLOR_GOLD,
+  GAL_COLOR_KHAKI,
+  GAL_COLOR_PEACHPUFF,
+  GAL_COLOR_YELLOW,
+  GAL_COLOR_PALEGOLDENROD,
+  GAL_COLOR_MOCCASIN,
+  GAL_COLOR_PAPAYAWHIP,
+  GAL_COLOR_LIGHTGOLDENRODYELLOW,
+  GAL_COLOR_LEMONCHIFFON,
+  GAL_COLOR_LIGHTYELLOW,
+
+  /* Brown colors. */
+  GAL_COLOR_MAROON,
+  GAL_COLOR_BROWN,
+  GAL_COLOR_SADDLEBROWN,
+  GAL_COLOR_SIENNA,
+  GAL_COLOR_CHOCOLATE,
+  GAL_COLOR_DARKGOLDENROD,
+  GAL_COLOR_PERU,
+  GAL_COLOR_ROSYBROWN,
+  GAL_COLOR_GOLDENROD,
+  GAL_COLOR_SANDYBROWN,
+  GAL_COLOR_TAN,
+  GAL_COLOR_BURLYWOOD,
+  GAL_COLOR_WHEAT,
+  GAL_COLOR_NAVAJOWHITE,
+  GAL_COLOR_BISQUE,
+  GAL_COLOR_BLANCHEDALMOND,
+  GAL_COLOR_CORNSILK,
+
+  /* Green colors. */
+  GAL_COLOR_DARKGREEN,
+  GAL_COLOR_GREEN,
+  GAL_COLOR_DARKOLIVEGREEN,
+  GAL_COLOR_FORESTGREEN,
+  GAL_COLOR_SEAGREEN,
+  GAL_COLOR_OLIVE,
+  GAL_COLOR_OLIVEDRAB,
+  GAL_COLOR_MEDIUMSEAGREEN,
+  GAL_COLOR_LIMEGREEN,
+  GAL_COLOR_LIME,
+  GAL_COLOR_SPRINGGREEN,
+  GAL_COLOR_MEDIUMSPRINGGREEN,
+  GAL_COLOR_DARKSEAGREEN,
+  GAL_COLOR_MEDIUMAQUAMARINE,
+  GAL_COLOR_YELLOWGREEN,
+  GAL_COLOR_LAWNGREEN,
+  GAL_COLOR_CHARTREUSE,
+  GAL_COLOR_LIGHTGREEN,
+  GAL_COLOR_GREENYELLOW,
+  GAL_COLOR_PALEGREEN,
+
+  /* Cyan colors. */
+  GAL_COLOR_TEAL,
+  GAL_COLOR_DARKCYAN,
+  GAL_COLOR_LIGHTSEAGREEN,
+  GAL_COLOR_CADETBLUE,
+  GAL_COLOR_DARKTURQUOISE,
+  GAL_COLOR_MEDIUMTURQUOISE,
+  GAL_COLOR_TURQUOISE,
+  GAL_COLOR_AQUA,
+  GAL_COLOR_CYAN,
+  GAL_COLOR_AQUAMARINE,
+  GAL_COLOR_PALETURQUOISE,
+  GAL_COLOR_LIGHTCYAN,
+
+  /* Blue colors. */
+  GAL_COLOR_MIDNIGHTBLUE,
+  GAL_COLOR_NAVY,
+  GAL_COLOR_DARKBLUE,
+  GAL_COLOR_MEDIUMBLUE,
+  GAL_COLOR_BLUE,
+  GAL_COLOR_ROYALBLUE,
+  GAL_COLOR_STEELBLUE,
+  GAL_COLOR_DODGERBLUE,
+  GAL_COLOR_DEEPSKYBLUE,
+  GAL_COLOR_CORNFLOWERBLUE,
+  GAL_COLOR_SKYBLUE,
+  GAL_COLOR_LIGHTSKYBLUE,
+  GAL_COLOR_LIGHTSTEELBLUE,
+  GAL_COLOR_LIGHTBLUE,
+  GAL_COLOR_POWDERBLUE,
+
+  /* Purple colors. */
+  GAL_COLOR_INDIGO,
+  GAL_COLOR_PURPLE,
+  GAL_COLOR_DARKMAGENTA,
+  GAL_COLOR_DARKVIOLET,
+  GAL_COLOR_DARKSLATEBLUE,
+  GAL_COLOR_BLUEVIOLET,
+  GAL_COLOR_DARKORCHID,
+  GAL_COLOR_FUCHSIA,
+  GAL_COLOR_MAGENTA,
+  GAL_COLOR_SLATEBLUE,
+  GAL_COLOR_MEDIUMSLATEBLUE,
+  GAL_COLOR_MEDIUMORCHID,
+  GAL_COLOR_MEDIUMPURPLE,
+  GAL_COLOR_ORCHID,
+  GAL_COLOR_VIOLET,
+  GAL_COLOR_PLUM,
+  GAL_COLOR_THISTLE,
+  GAL_COLOR_LAVENDER,
+
+  /* White colors */
+  GAL_COLOR_MISTYROSE,
+  GAL_COLOR_ANTIQUEWHITE,
+  GAL_COLOR_LINEN,
+  GAL_COLOR_BEIGE,
+  GAL_COLOR_WHITESMOKE,
+  GAL_COLOR_LAVENDERBLUSH,
+  GAL_COLOR_OLDLACE,
+  GAL_COLOR_ALICEBLUE,
+  GAL_COLOR_SEASHELL,
+  GAL_COLOR_GHOSTWHITE,
+  GAL_COLOR_HONEYDEW,
+  GAL_COLOR_FLORALWHITE,
+  GAL_COLOR_AZURE,
+  GAL_COLOR_MINTCREAM,
+  GAL_COLOR_SNOW,
+  GAL_COLOR_IVORY,
+  GAL_COLOR_WHITE,
+
+  /* Gray and black colors. */
+  GAL_COLOR_BLACK,
+  GAL_COLOR_DARKSLATEGRAY,
+  GAL_COLOR_DIMGRAY,
+  GAL_COLOR_SLATEGRAY,
+  GAL_COLOR_GRAY,
+  GAL_COLOR_LIGHTSLATEGRAY,
+  GAL_COLOR_DARKGRAY,
+  GAL_COLOR_SILVER,
+  GAL_COLOR_LIGHTGRAY,
+  GAL_COLOR_GAINSBORO,
+
+  /* Final number of pre-defined colors. */
+  GAL_COLOR_NUMBER,
+};
+
+
+
+
+
+/* Functions */
+uint8_t
+gal_color_name_to_id(char *n);
+
+char *
+gal_color_id_to_name(uint8_t color);
+
+void
+gal_color_in_rgb(uint8_t color, float *f);
+
+
+
+
+__END_C_DECLS    /* From C++ preparations */
+
+#endif           /* __GAL_COLOR_H__ */
diff --git a/lib/gnuastro/eps.h b/lib/gnuastro/eps.h
index e73265a7..e6cb7223 100644
--- a/lib/gnuastro/eps.h
+++ b/lib/gnuastro/eps.h
@@ -27,6 +27,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /* Include other headers if necessary here. Note that other header files
    must be included before the C++ preparations below */
 #include <gnuastro/data.h>
+#include <gnuastro/color.h>
 
 
 
@@ -53,6 +54,57 @@ __BEGIN_C_DECLS  /* From C++ preparations */
 
 
 
+/* Definitions */
+enum gal_eps_shapes
+{
+  GAL_EPS_MARK_SHAPE_INVALID,   /* Invalid (=0 by C standard).  */
+  GAL_EPS_MARK_SHAPE_CIRCLE,
+  GAL_EPS_MARK_SHAPE_PLUS,
+  GAL_EPS_MARK_SHAPE_CROSS,     /* The lines are not based on length */
+  GAL_EPS_MARK_SHAPE_ELLIPSE,
+  GAL_EPS_MARK_SHAPE_POINT,
+  GAL_EPS_MARK_SHAPE_SQUARE,
+  GAL_EPS_MARK_SHAPE_RECTANGLE,
+  GAL_EPS_MARK_SHAPE_LINE,
+
+  /* This will be the total number of shapes (good for scripts). */
+  GAL_EPS_MARK_SHAPE_NUMBER
+};
+
+
+
+
+
+/* Expected names of columns */
+#define  GAL_EPS_MARK_COLNAME_TEXT       "TEXT"
+#define  GAL_EPS_MARK_COLNAME_FONT       "FONT"
+#define  GAL_EPS_MARK_COLNAME_XPIX       "X-PIX"
+#define  GAL_EPS_MARK_COLNAME_YPIX       "Y-PIX"
+#define  GAL_EPS_MARK_COLNAME_SHAPE      "SHAPE"
+#define  GAL_EPS_MARK_COLNAME_COLOR      "COLOR"
+#define  GAL_EPS_MARK_COLNAME_SIZE1      "SIZE1"
+#define  GAL_EPS_MARK_COLNAME_SIZE2      "SIZE2"
+#define  GAL_EPS_MARK_COLNAME_ROTATE     "ROTATE"
+#define  GAL_EPS_MARK_COLNAME_FONTSIZE   "FONTSIZE"
+#define  GAL_EPS_MARK_COLNAME_LINEWIDTH  "LINEWIDTH"
+
+
+
+
+/* Default mark properties. */
+#define GAL_EPS_MARK_DEFAULT_SHAPE         GAL_EPS_MARK_SHAPE_CIRCLE
+#define GAL_EPS_MARK_DEFAULT_COLOR         GAL_COLOR_RED
+#define GAL_EPS_MARK_DEFAULT_SIZE1         5
+#define GAL_EPS_MARK_DEFAULT_SIZE2         3
+#define GAL_EPS_MARK_DEFAULT_SIZE2_ELLIPSE 0.5
+#define GAL_EPS_MARK_DEFAULT_ROTATE        0
+#define GAL_EPS_MARK_DEFAULT_LINEWIDTH     1
+#define GAL_EPS_MARK_DEFAULT_FONT         "Arial"
+#define GAL_EPS_MARK_DEFAULT_FONTSIZE      4
+
+
+
+
 /* Functions */
 int
 gal_eps_name_is_eps(char *name);
@@ -63,10 +115,16 @@ gal_eps_suffix_is_eps(char *name);
 void
 gal_eps_to_pt(float widthincm, size_t *dsize, size_t *w_h_in_pt);
 
+uint8_t
+gal_eps_shape_name_to_id(char *name);
+
+char *
+gal_eps_shape_id_to_name(uint8_t id);
+
 void
 gal_eps_write(gal_data_t *in, char *filename, float widthincm,
-              uint32_t borderwidth, int hex, int dontoptimize, int forpdf);
-
+              uint32_t borderwidth, int hex, int dontoptimize,
+              int forpdf, gal_data_t *marks);
 
 
 
diff --git a/lib/gnuastro/list.h b/lib/gnuastro/list.h
index 5637672f..2561050f 100644
--- a/lib/gnuastro/list.h
+++ b/lib/gnuastro/list.h
@@ -337,6 +337,9 @@ gal_list_data_add_alloc(gal_data_t **list, void *array, 
uint8_t type,
 gal_data_t *
 gal_list_data_pop(gal_data_t **list);
 
+gal_data_t *
+gal_list_data_select_by_name(gal_data_t *list, char *name);
+
 void
 gal_list_data_reverse(gal_data_t **list);
 
diff --git a/lib/gnuastro/pdf.h b/lib/gnuastro/pdf.h
index 62bf0f69..8afbec01 100644
--- a/lib/gnuastro/pdf.h
+++ b/lib/gnuastro/pdf.h
@@ -62,7 +62,8 @@ gal_pdf_suffix_is_pdf(char *name);
 
 void
 gal_pdf_write(gal_data_t *in, char *filename, float widthincm,
-              uint32_t borderwidth, int dontoptimize);
+              uint32_t borderwidth, int dontoptimize,
+              gal_data_t *marks);
 
 
 
diff --git a/lib/gnuastro/units.h b/lib/gnuastro/units.h
index e7636d2f..4e3304b5 100644
--- a/lib/gnuastro/units.h
+++ b/lib/gnuastro/units.h
@@ -79,6 +79,20 @@ gal_units_counts_to_mag(double counts, double zeropoint);
 double
 gal_units_mag_to_counts(double mag, double zeropoint);
 
+double
+gal_units_mag_to_sb(double mag, double area_arcsec2);
+
+double
+gal_units_sb_to_mag(double sb, double area_arcsec2);
+
+double
+gal_units_counts_to_sb(double counts, double zeropoint,
+                       double area_arcsec2);
+
+double
+gal_units_sb_to_counts(double sb, double zeropoint,
+                       double area_arcsec2);
+
 double
 gal_units_counts_to_jy(double counts, double zeropoint_ab);
 
diff --git a/lib/list.c b/lib/list.c
index 2c32bc8c..21e9dcd7 100644
--- a/lib/list.c
+++ b/lib/list.c
@@ -26,6 +26,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 #include <errno.h>
 #include <error.h>
 #include <stdlib.h>
+#include <string.h>
 #include <assert.h>
 #include <inttypes.h>
 
@@ -1347,6 +1348,26 @@ gal_list_data_pop(gal_data_t **list)
 
 
 
+/* From the input list of datasets, return the first one that has a name
+   equal to the input string 'name'. */
+gal_data_t *
+gal_list_data_select_by_name(gal_data_t *list, char *name)
+{
+  gal_data_t *tmp;
+
+  /* Parse the list and return the desired element. */
+  for(tmp=list;tmp!=NULL;tmp=tmp->next)
+    if(tmp->name && strcmp(tmp->name,name)==0 )
+      return tmp;
+
+  /* If control reaches here, no such name could be found. */
+  return NULL;
+}
+
+
+
+
+
 void
 gal_list_data_reverse(gal_data_t **list)
 {
diff --git a/lib/pdf.c b/lib/pdf.c
index 7aa473a4..c55b2de2 100644
--- a/lib/pdf.c
+++ b/lib/pdf.c
@@ -31,6 +31,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 
 #include <gnuastro/eps.h>
 #include <gnuastro/pdf.h>
+#include <gnuastro/jpeg.h>
 
 #include <gnuastro-internal/checkset.h>
 
@@ -99,30 +100,40 @@ gal_pdf_suffix_is_pdf(char *name)
  *************************************************************/
 void
 gal_pdf_write(gal_data_t *in, char *filename, float widthincm,
-              uint32_t borderwidth, int dontoptimize)
+              uint32_t borderwidth, int dontoptimize,
+              gal_data_t *marks)
 {
-  char command[20000];
   size_t w_h_in_pt[2];
+  char *command, *device;
   char *epsname=gal_checkset_malloc_cat(filename, ".ps");
 
   /* Write the EPS file. */
-  gal_eps_write(in, epsname, widthincm, borderwidth, 0, dontoptimize, 1);
+  gal_eps_write(in, epsname, widthincm, borderwidth, 0,
+                dontoptimize, 0, marks);
 
   /* Get the size of the image in 'pt' units. */
   gal_eps_to_pt(widthincm, in->dsize, w_h_in_pt);
 
+  /* Set the device from the file name */
+  if(gal_jpeg_name_is_jpeg(filename)) device="jpeg";
+  else                                device="pdfwrite";
+
   /* Write the ghostscript command to compile the EPS file to PDF. */
-  sprintf(command, "gs -q -o %s -sDEVICE=pdfwrite -dDEVICEWIDTHPOINTS=%zu"
-          " -dDEVICEHEIGHTPOINTS=%zu -dPDFFitPage %s", filename,
-          w_h_in_pt[0]+2*borderwidth, w_h_in_pt[1]+2*borderwidth,
-          epsname);
+  if( asprintf(&command, "gs -q -o %s -sDEVICE=%s "
+               "-dDEVICEWIDTHPOINTS=%zu -dDEVICEHEIGHTPOINTS=%zu "
+               "-dPDFFitPage %s", filename, device,
+               w_h_in_pt[0]+2*borderwidth, w_h_in_pt[1]+2*borderwidth,
+               epsname)<0 )
+    error(EXIT_FAILURE, 0, "%s: asprintf allocation error", __func__);
 
   /* Run Ghostscript. */
   if(system(command))
-    error(EXIT_FAILURE, 0, "the command to convert a PostScript file to "
-          "PDF ('%s') was not successful! The PostScript file (%s) is "
-          "left if you want to convert or use it through any other "
-          "means", command, epsname);
+    error(EXIT_FAILURE, 0, "the Ghostscript command (printed after "
+          "this message) to convert the EPS file to PDF was not "
+          "successful! The EPS file ('%s') is left if you want to "
+          "convert it through any other means (for example the "
+          "'epspdf' program). The Ghostscript command was: %s",
+          epsname, command);
 
   /* Delete the EPS file. */
   errno=0;
@@ -130,5 +141,6 @@ gal_pdf_write(gal_data_t *in, char *filename, float 
widthincm,
     error(EXIT_FAILURE, errno, "%s", epsname);
 
   /* Clean up. */
+  free(command);
   free(epsname);
 }
diff --git a/lib/units.c b/lib/units.c
index 0ee082b6..a280506a 100644
--- a/lib/units.c
+++ b/lib/units.c
@@ -394,6 +394,51 @@ gal_units_mag_to_counts(double mag, double zeropoint)
 
 
 
+double
+gal_units_mag_to_sb(double mag, double area_arcsec2)
+{
+  return mag+2.5*log10(area_arcsec2);
+}
+
+
+
+
+
+double
+gal_units_sb_to_mag(double sb, double area_arcsec2)
+{
+  return sb-2.5*log10(area_arcsec2);
+}
+
+
+
+
+
+double
+gal_units_counts_to_sb(double counts, double zeropoint,
+                       double area_arcsec2)
+{
+  return gal_units_mag_to_sb(
+                   gal_units_counts_to_mag(counts, zeropoint),
+                   area_arcsec2);
+}
+
+
+
+
+
+double
+gal_units_sb_to_counts(double sb, double zeropoint,
+                       double area_arcsec2)
+{
+  return gal_units_mag_to_counts(
+                   gal_units_sb_to_mag(sb, area_arcsec2),
+                   zeropoint);
+}
+
+
+
+
 /* Convert Pixel values to Janskys with an AB-magnitude based
    zero-point. See the "Brightness, Flux, Magnitude and Surface
    brightness". */



reply via email to

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