GNU Astronomy Utilities



5.2.4 Annotations for figure in paper

To make a nice figure from your FITS images, it is important to show more than merely the raw image (converted to a printer friendly format like PDF or JPEG; see FITS images in a publication and Marking objects for publication).

Annotations (or visual metadata) over the raw image greatly help the readers clearly see your argument and put the image/result in a larger context. Examples include:

Because of the modular philosophy of Gnuastro, ConvertType is only focused on converting your FITS images to printer friendly formats like JPEG or PDF. But to present your results in a slide or paper, you will often need to annotate the raw JPEG or PDF with some of the features above. The good news is that there are many powerful plotting programs that you can use to add such annotations. As a result, there is no point in making a new one, specific to Gnuastro. Instead, Gnuastro hopes to provide the necessary interface to communicate easily with those tools. In this section, we will demonstrate this using the very powerful PGFPlots142 package of LaTeX.

Single script for easy running: In this section we are reviewing the reason and details of every step which is good for educational purposes. But when you know the steps already, these separate code blocks can be annoying. Therefore the full script (except for the data download step) is available in Full script of annotations on figure.

PGFPlots uses the same LaTeX graphic engine that typesets your paper/slide. Therefore when you build your plots and figures using PGFPlots (and its underlying package PGF/TiKZ143) your plots will blend beautifully within your text: same fonts, same colors, same line properties, etc. Since most papers (and presentation slides144) are made with LaTeX, PGFPlots is therefore the best tool for those who use LaTeX to create documents. PGFPlots also does not need any extra dependencies beyond a basic/minimal TeX-live installation, so it is much more reliable than tools like Matplotlib in Python that have hundreds of fast-evolving dependencies145.

To demonstrate this, we will create a surface brightness image of a galaxy in the F160W filter of the ABYSS survey146. In the code-block below, let’s make a “build” directory to keep intermediate files and avoid populating the top-level source directory (it is always good to keep your data separate from your source). Afterwards, we will download the full image and crop out a 20 arcmin wide image around the galaxy with the commands below. You can run these commands in an empty directory.

$ mkdir build
$ wget http://cdsarc.u-strasbg.fr/ftp/J/A+A/621/A133/fits/ah_f160w.fits
$ astcrop ah_f160w.fits --center=53.1616278,-27.7802446 --mode=wcs \
          --width=20/3600 --output=build/crop.fits

To better show the low surface brightness (LSB) outskirts, we will warp the image, then convert the pixel units to surface brightness with the commands below with Gnuastro’s Arithmetic program. An important point to remember here is that the magnitudes (and thus the surface brightness) come from a logarithm; therefore if a pixel is negative, its log will be NaN (Not-a-Number). Therefore after counts-to-sb, we convert all the NaN pixels with the faintest (highest) surface brightness limit possible in this image: 30 mag/arcsec\(^2\) (which we define as a variable because it is also necessary later). In case you don’t know your image’s surface brightness limit, see FITS images in a publication. For more, see the surface brightness topic of Brightness, Flux, Magnitude and Surface brightness, and for a more complete tutorial, see FITS images in a publication.

$ sbhigh=30
$ zeropoint=25.94
$ astwarp build/crop.fits --centeroncorner --scale=1/3 \
          --output=build/scaled.fits
$ pixarea=$(astfits build/scaled.fits --pixelareaarcsec2)
$ astarithmetic build/scaled.fits $zeropoint $pixarea counts-to-sb \
                set-sb sb sb isblank sb $sbhigh gt or $sbhigh where \
                --output=build/sb.fits

We are now ready to convert the surface brightness image into a PDF. To better show the LSB features, we will limit the brighter range of values (lower numerics in the Magnitude) with the --fluxlow option. All pixels with a surface brightness brighter than 22 mag/arcsec\(^2\) will be shown at a similar color. This threshold is also being defined as a variable, because we will also need it later below (to pass into PGFPlots). Finally, in the call to convert the FITS to PDF, we also set --borderwidth=0, because the coordinate system we will be added over the edges of the image which effectively becomes a border for the image (separating it from the background). The --cmappgfplots option of ConvertType is recommended when you want to add a color bar in PGFPlots because it will create the necessary PGFPlots command to show the exact color map that ConvertType used in the colorbar.

$ sblow=22
$ colormap=sls
$ astconvertt build/sb.fits --colormap=$colormap --borderwidth=0 \
              --fluxhigh=$sbhigh --fluxlow=$sblow \
              --output=build/sb.pdf --cmappgfplots

Please open build/sb.pdf and have a look. Try changing the surface brightness variables above or the colormap to make it more pleasing to your eye (you will need such experimentation when you later do this on your own images). We now have the printable PDF representation of the image, but as discussed above, it is not enough for a paper. We will add 1) a thick line showing the size of 20 kpc (kilo parsecs) at the redshift of the central galaxy to help the readers of our report create a mental image of its physical size, 2) coordinates on the edge so the reader can easily identify the location on the sky and observed size, and 3) a color bar, showing the colormap of the surface brightness level for readers to be able to interpret the pixel values.

To get the first job done, we first need to know the redshift of the central galaxy. To do this, we can use Gnuastro’s Query program to look into all the objects in NED within this image (only asking for the RA, Dec and redshift columns). We will then use the Match program to find the NED entry that corresponds to our galaxy.

$ astquery ned --dataset=objdir --overlapwith=build/sb.fits \
           --column=ra,dec,z --output=build/ned.fits
$ astmatch build/ned.fits -h1 --coord=53.1616278,-27.7802446 \
           --ccol1=RA,Dec --aperture=1/3600 \
           --output=build/ned-matched.fits
$ redshift=$(asttable build/ned-matched.fits -cz)
$ echo $redshift

Now that we know the redshift of the central object, we can define the coordinates of the thick line that will show the length of 20 kpc at that redshift. It will be a horizontal line (fixed Declination) across a range of RA. The start of this thick line will be located at the top edge of the image (at the 95-percent of the width and height of the image). With the commands below we will find the three necessary parameters (one declination and two Right Ascensions). Just note that in astronomical images, RA increases to the left/east, which is the reason we are using the minimum and + to find the RA starting point.

$ scalelineinkpc=20
$ coverage=$(astfits build/sb.fits --skycoverage --quiet | awk 'NR==2')
$ scalelinedec=$(echo      $coverage | awk '{print $4-($4-$3)*0.05}')
$ scalelinerastart=$(echo  $coverage | awk '{print $1+($2-$1)*0.05}')
$ scalelineraend=$(astcosmiccal --redshift=$redshift --arcsectandist \
                      | awk '{start='$scalelinerastart'; \
                             width='$scalelineinkpc'/$1/3600; \
                             print start+width}')

To draw coordinates over the image, we need to feed these values into PGFPlots. But manually entering numbers into the PGFPlots source will be very frustrating, prone to many errors and will be hard to change in the future (for example after the referee report comes)! Fortunately there is an easy way to do this: LaTeX macros. New macros are defined by this LaTeX command:

\newcommand{\macroname}{value}

Anywhere that LaTeX confronts \macroname, it will replace value when building the output. We will have one file called macros.tex in the build directory and define macros based on those values. We will use the shell’s printf command to write these macro definition lines into the macro file. Double backslashes are used in the printf command, because backslash is a meaningful character for it. Also, we put a \n at the end of each line, otherwise, all the commands will go into a single line of the macro file. We will also place the random ‘ma’ string at the start of all our LaTeX macros to help identify the macros for this plot throughout your report’s source. In the case of calculated numbers (like the redshift, we will use AWK to print to two decimal digits) to simplify the LaTeX commands.

$ macros=build/macros.tex
$ printf '\\newcommand{\\maScaleDec}'"{$scalelinedec}\n" > $macros
$ printf '\\newcommand{\\maScaleRAa}'"{$scalelinerastart}\n" >> $macros
$ printf '\\newcommand{\\maScaleRAb}'"{$scalelineraend}\n" >> $macros
$ printf '\\newcommand{\\maScaleKpc}'"{$scalelineinkpc}\n" >> $macros
$ v=$(echo $redshift | awk '{printf "%.2f", $1'})
$ printf '\\newcommand{\\maCenterZ}'"{$v}\n" >> $macros

Please open the macros file after these commands and have a look to see if they do conform to the expected format above. Another set of macros we will need to feed into PGFPlots is the coordinates of the image corners. Fortunately the coverage variable found above is also useful here. We just need to extract each item before feeding it into the macros. To do this, we will use AWK and keep each value with the temporary shell variable ‘v’.

$ v=$(echo $coverage | awk '{print $1}')
$ printf '\\newcommand{\\maCropRAMin}'"{$v}\n" >> $macros
$ v=$(echo $coverage | awk '{print $2}')
$ printf '\\newcommand{\\maCropRAMax}'"{$v}\n" >> $macros
$ v=$(echo $coverage | awk '{print $3}')
$ printf '\\newcommand{\\maCropDecMin}'"{$v}\n" >> $macros
$ v=$(echo $coverage | awk '{print $4}')
$ printf '\\newcommand{\\maCropDecMax}'"{$v}\n" >> $macros

Finally, we also need to pass some other numbers to PGFPlots: 1) the major tick distance (in the coordinate axes that will be printed on the edge of the image). We will assume 7 ticks for this image. 2) The minimum and maximum surface brightness values that we gave to ConvertType when making the PDF; PGFPlots will define its color-bar based on these two values. 3) The name of the Gnuastro colormap (you can manually change this in the produced colormap file by ConvertType: it is plain-text). Also, note that if all your figures are generated with the same colormap you only need to ask ConvertType to generate it once.

$ v=$(echo $coverage | awk '{print ($2-$1)/7}')
$ printf '\\newcommand{\\maTickDist}'"{$v}\n" >> $macros
$ printf '\\newcommand{\\maSBlow}'"{$sblow}\n" >> $macros
$ printf '\\newcommand{\\maSBhigh}'"{$sbhigh}\n" >> $macros
$ printf '\\newcommand{\maColormap}'"{gnuastro$colormap}\n" >> $macros

All the necessary numbers we need to pass to LaTeX are now ready. Please copy the contents below into a file called my-figure.tex. This is the PGFPlots source for this particular plot. Besides the coordinates and scale-line, we will also add some text over the image and an orange arrow pointing to the central object with its redshift printed over it. The parameters are generally human-readable, so you should be able to get a good feeling of every line by simply reading it. There are also comments which will show up as a different color when you copy this into a plain-text editor that recognizes LaTeX.

\begin{tikzpicture}

  %% Define the coordinates and colorbar
  \begin{axis}[
      at={(0,0)},
      axis on top,
      x dir=reverse,
      scale only axis,
      width=\linewidth,
      height=\linewidth,
      minor tick num=10,
      xmin=\maCropRAMin,
      xmax=\maCropRAMax,
      ymin=\maCropDecMin,
      ymax=\maCropDecMax,
      enlargelimits=false,
      every tick/.style={black},
      xtick distance=\maTickDist,
      ytick distance=\maTickDist,
      yticklabel style={rotate=90},
      ylabel={Declination (degrees)},
      xlabel={Right Ascension (degrees)},
      ticklabel style={font=\small,
        /pgf/number format/.cd, precision=4,/tikz/.cd},
      x label style={at={(axis description cs:0.5,0.02)},
        anchor=north,font=\small},
      y label style={at={(axis description cs:0.07,0.5)},
        anchor=south,font=\small},
      colorbar,
      colormap name=\maColormap,
      point meta min=\maSBlow,
      point meta max=\maSBhigh,
      colorbar style={
        at={(1.01,1)},
        ylabel={Surface brightness (mag/arcsec$^2$)},
        yticklabel style={
          /pgf/number format/.cd, precision=1, /tikz/.cd},
        y label style={at={(axis description cs:5.3,0.5)},
          anchor=south,font=\small},
      },
    ]

    %% Put the image in the proper positions of the plot.
    \addplot graphics[ xmin=\maCropRAMin,  xmax=\maCropRAMax,
                       ymin=\maCropDecMin, ymax=\maCropDecMax]
             {sb.pdf};

    %% Draw the scale factor.
    \addplot[black, line width=5, name=scaleline] coordinates
            {(\maScaleRAa,\maScaleDec) (\maScaleRAb,\maScaleDec)}
            node [anchor=north west] {\large $\maScaleKpc$ kpc};
  \end{axis}

  %% Add some text anywhere over the plot. The text is added two
  %% times: the first time with a white background (that with a
  %% certain opacity), the second time just the text with opacity.
  \node[anchor=south west, fill=white, opacity=0.5]
       at (0.01\linewidth,0.01\linewidth)
       {(a) Text can be added here};
  \node[anchor=south west]
       at (0.01\linewidth,0.01\linewidth)
       {(a) Text can be added here};

  %% Add an arrow to highlight certain structures.
  \draw [->, black, line width=5]
  (0.35\linewidth,0.35\linewidth)
  -- node [anchor=south, rotate=45]{$z=\maCenterZ$}
  (0.45\linewidth,0.45\linewidth);
\end{tikzpicture}

Finally, we need another simple LaTeX source for the main text of your report that will host the figure (it helps the readability of your source for yourself to keep the code of your figures in a separate file to be easily included in the proper place you want). Simply copy the minimal working example below in a file called report.tex.

\documentclass{article}

%% Import the TiKZ package and activate its "external" feature.
\usepackage{tikz}
\usetikzlibrary{external}
\tikzexternalize

%% PGFPlots (which uses TiKZ) and all the colormaps you need.
\usepackage{pgfplots}
\pgfplotsset{axis line style={thick}}
\input{sb-colormap.tex}

%% Import the macros.
\input{macros.tex}

%% Start document.
\begin{document}
You can write anything here.

%% Add the figure and its caption.
\begin{figure}
  \input{my-figure.tex}
  \caption{A demo image.}
\end{figure}

%% Finish the document.
\end{document}

You are now ready to create the PDF. But LaTeX creates many temporary files, so to avoid populating our top-level (source code and input data) directory, we will copy the two .tex files into the build directory, go there and run LaTeX. Before running it, we will first delete all the files that have the name pattern *-figure0*, these are “external” files created by TiKZ+PGFPlots, including the actual PDF of the figure. PGFPlots has nice ways to set a name for each “external” figure it generates (and not depend on a counter), but that is beyond the scope here, see its manual for details.

$ cp report.tex my-figure.tex build
$ cd build
$ rm -f *-figure0*
$ pdflatex -shell-escape -halt-on-error report.tex

You now have the full “report” in report.pdf. The good news is that in case you need the high-quality figure for other purposes (like showing in slides), you don’t have to take a screenshot! You also have the raw PDF of the figure in report-figure0.pdf. Try adding some extra text in report.tex, or in the caption of the figure and re-running the last four commands so it becomes more like an actual

You can also try changing the 20kpc scale line length to 50kpc, or try changing the redshift, to see how the length and text of the thick scale-line will automatically change. But these kinds of changes will be hard in the manual steps above and due to the number of steps, human error can easily cause a crash. Therefore it is best to put such numerous steps in a script as we have done in Full script of annotations on figure below. So it may be easier to take the script from there and do the changes suggested here.

In a larger paper, you can add multiple such figures (with different .tex files that are placed in different figure environments with different captions throughout your text). Each figure will get a number in the build directory. TiKZ also allows setting a file name for each “external” figure (to avoid such numbers that can be annoying if the image orders are changed). PGFPlots is also highly customizable, you can make a lot of changes and customizations. Both TiKZ147 and PGFPLots148 have wonderful manuals, so have a look trough them and you will enjoy the power that comes under your fingers afterwards.


Footnotes

(142)

http://mirrors.ctan.org/graphics/pgf/contrib/pgfplots/doc/pgfplots.pdf

(143)

http://mirrors.ctan.org/graphics/pgf/base/doc/pgfmanual.pdf

(144)

To build slides, LaTeX has packages like Beamer, see http://mirrors.ctan.org/macros/latex/contrib/beamer/doc/beameruserguide.pdf

(145)

See Figure 1 of Alliez et al. 2019.

(146)

http://research.iac.es/proyecto/abyss

(147)

http://mirrors.ctan.org/graphics/pgf/base/doc/pgfmanual.pdf

(148)

http://mirrors.ctan.org/graphics/pgf/contrib/pgfplots/doc/pgfplots.pdf