In this tutorial, we will show how to read and write multi-extension FITS (MEF) files. This means understanding the usage of the following service classes:
MefFile at file level,ImageHdu and BintableHdu at HDU level,ImageRaster and BintableColumns at data unit level;as well as Record, Raster and Column data classes.
We strongly recommend reading first What's a FITS file?, even if you're familiar with the FITS format, because it introduces EleFits-specific wording.
At the end of the tutorial, you will be able to create a MEF file from scratch with unlimited number of various extensions, and to read the metadata and data back!
The tutorial is built together with an example program: EleFitsTutorial.cpp. We've embedded the calls to the logger in the code snippets below, so that you can easily map the execution log to the following explanations. You can already run the program and watch the resulting file:
We'll first discover the data classes, then use them to create a MEF file, and finally read the file and values back.
First things first, we have to declare the dependency to EleFits and to use the right headers and namespaces. For the first part, head to the Installation and configuration guide. For the headers and namespace, here's the only thing you'll have to do:
Data classes are the classes which hold the pieces of data read from and written to a FITS file. There are three main classes:
Record for header units,Raster for data units of image HDUs,Column for data units of binary table HDUs.A keyword record as defined in the FITS standard is a triplet of keyword, value and optional comment rendered in the file as:
A unit can be included in the comment as follows:
The value can be a Boolean, an integer, real or complex number, or a string.
Record is a simple template class which merely stores those fields. For the purpose of the tutorial, we define a structure to hold our records:
Here's a how they can be built:
Images in FITS are n-dimensional arrays. Raster is a class which represents this kind of data and provides constant time pixel accessors. Template arguments are the pixel type and number of axes. There are two kinds of rasters:
VecRaster owns the data as an std::vector;PtrRaster points to the data owned by another class, as a raw pointer (be careful not to destroy the data while the raster is alive).The rasters of this tutorial are stored in a dedicated structure:
Again, let's show an example of how to create rasters:
A binary table is made of columns which can be either scalar (each cell contains a value) or vector (each cell contains several values). In the latter case, the number of values per cell is named repeat count.
For string columns, the repeat count must be greater than the longest string value.
A Column is a simple structure which holds the column name, repeat count (=1 for scalar columns, >1 for vector columns), unit, and owns or references some data, respectively as a VecColumn or as a PtrColumn.
The following structure stores the tutorial columns:
Here's a set of examples on how create them:
Now that we have learned about the data classes, we can move to the service classes, or handlers. The first thing we can do is to create a new file.
The MefFile class represents multi-extension FITS (MEF) files. It extends the FitsFile class, which is also the base class of SifFile for single-image FITS (SIF) files.
Creating (or opening) a file is simply done with the constructor of MefFile (or SifFile for SIF files!):
A newly created FITS file consists in an empty Primary, which can then be accessed and modified, but is never created by hand.
The mode parameter controls the access rights on the file. For example, an existing file can be opened with read-only mode:
The file is closed when the destructor of MefFile is called (although a FitsFile::close() method is provided for convenience).
MefFile provides services to create and access extensions. Conceptually, MefFile is a FitsFile with a vector of HDUs (in contrast, SifFile is a FitsFile with a single HDU).
HDUs are represented by two classes:
ImageHdu to access image HDUs,BintableHdu to access binary table HDUs.Both derive from parent class Hdu.
To sum up, a MefFile is a vector of Hdus, which can be a mix of ImageHdus and BintableHdus.
There are two kinds of services in MefFile for creating extensions: They can be either initialized with header only or assigned directly with data. Here's an example of creating image HDUs:
And here's one of creating binary table HDUs:
Records are read and written through a Header object, which is accessible from an ImageHdu or a BintableHdu as header(). They can be written and updated one-by-one or by sequences.
An optional template parameter controls the write policy, e.g. what to do if the given keyword already exists. By default, a record is created if the keyword doesn't exists, or updated if the keyword already exists.
Record writing functions have different overloards to allow calling them in different ways. Here are a few examples:
Remember that we have left IMAGE2 extension without data? Filling it is done through the ImageRaster class, accessed as ImageHdu::raster(). For example, here is how to write all the pixels at once:
Many more options are available to write data region-wise. They are described in details in Image data unit handlers.
Analogously to ImageRaster for image HDUs, BintableColumns provides read/write services for the data unit of the binary table extension.
As the name implies, data is stored, read and written column-wise, as opposed to the row-major ordering of the FITS file. This is to avoid working with very complex structures like tuples with holes, and rather rely on Column, std::vector, arrays... Yet, this also means that I/Os could be very inefficient! To workaround this, several columns can be written (and read) at a time. In this case, I/Os are internally performed chunk-wise, using some buffer, and performances drammatically improve.
Like for image HDUs, it is possible to write the data unit only partially, e.g. to append rows, using richer methods of BintableColumns.
HDUs can be accessed with a set of methods, templated with the type of HDU: BintableHdu, ImageHdu, or Hdu. For metadata work, we don't need to know the type of HDU: whether this is an image or binary table HDU has no impact, and a Hdu will be returned by default.
HDUs are accessed either by their name (first HDU whose name matches argument is returned) or by their index, and a shortcut is provided for the primary HDU (which has index 0):
You've probably noticed that we use references to constant HDU handlers. Indeed, HDU handlers are not modified by reading and writing services, only the MefFile is. Actually, HDU-level and data unit-level handlers are very light classes which aim at providing a clear interface.
Records are parsed using Header services. Like for writing, you can parse several records at once:
Like for writing, reading images is ensured by ImageRaster. A VecRaster is generally instantiated, and pixel values can be accessed with the subscript operator []:
Again, regions can be read.
Column reading is provided by BintableColumns. Columns are generally read as VecColumns, values of which are accessed with the call operator ():