EleFits  4.0.1
A modern C++ API on top of CFitsIO
EleFitsTutorial.cpp
#include "EleFitsUtils/ProgramOptions.h"
#include "ElementsKernel/ProgramHeaders.h"
#include <boost/program_options.hpp>
#include <map>
#include <string>
#include "EleFitsData/TestColumn.h"
#include "EleFitsData/TestRaster.h"
#include "EleFitsData/TestRecord.h"
#include "EleFits/MefFile.h"
using namespace Euclid;
// EleFits API is in the Euclid::Fits namespace.
// We could have be using namespace Euclid::Fits instead,
// but things would have been less obvious in the snippets.
using boost::program_options::options_description;
using boost::program_options::value;
using boost::program_options::variable_value;
static Elements::Logging logger = Elements::Logging::getLogger("EleFitsTutorial");
// DECLARATIONS //
struct TutoRecords {
Fits::Record<int> intRecord;
Fits::Record<float> floatRecord;
};
struct TutoRasters {
};
struct TutoColumns {
Fits::VecColumn<float> float32Column;
};
TutoRecords createRecords();
TutoRasters createRasters();
TutoColumns createColumns();
void writeMefFile(const std::string& filename);
void readMefFile(const std::string& filename);
void writeRecords(const Fits::Hdu& hdu);
void readRecords(const Fits::Hdu& hdu);
void readRaster(const Fits::ImageHdu& hdu);
void readColumns(const Fits::BintableHdu& hdu);
// DATA CLASSES //
TutoRecords createRecords() {
logger.info(" Creating records...");
/* Create a record with unit and comment */
Fits::Record<std::string> stringRecord("STRING", "VALUE", "unit", "comment");
/* Create a record with keyword and value only */
Fits::Record<int> intRecord("INT", 0);
/* Create a record from an initialization list */
Fits::Record<float> floatRecord { "FLOAT", 3.14F, "", "A piece of Pi" };
// This is often used as a shortcut to create records as function parameters.
/* Generate a random record */
auto complexRecord = Fits::Test::generateRandomRecord<std::complex<double>>("COMPLEX");
return { stringRecord, intRecord, floatRecord, complexRecord };
}
TutoRasters createRasters() {
logger.info(" Creating rasters...");
/* Initialize and later fill a raster */
Fits::VecRaster<std::int16_t, 2> int16Raster2D({ 4, 3 });
for (const auto& position : int16Raster2D.domain()) {
int16Raster2D[position] = position[0] + position[1];
}
// This demonstrates the iteration over positions;
// It is possible to use two nested loops instead.
/* Create a raster from a vector */
std::vector<std::int32_t> int32Vec(16 * 9 * 3, 0);
// ... do what you have to do with the vector, and then move it to the raster ...
Fits::VecRaster<std::int32_t, 3> int32Raster3D({ 16, 9, 3 }, std::move(int32Vec));
// Instead of moving a vector, it's also possible to work with
// a raw pointer with the PtrRaster class.
/* Generate a random raster */
auto int64Raster4D = Fits::Test::RandomRaster<std::int64_t, 4>({ 17, 9, 3, 24 });
return { int16Raster2D, int32Raster3D, int64Raster4D };
}
TutoColumns createColumns() {
logger.info(" Creating columns...");
/* Initialize and later fill a column */
Fits::VecColumn<std::string> stringColumn({ "STRING", "unit", 3 }, 100);
// String columns must be wide-enough to hold each character.
for (long i = 0; i < stringColumn.rowCount(); ++i) {
stringColumn(i) = std::to_string(i);
}
// operator() takes two parameters: the row index, and repeat index (=0 by default)
/* Create a column from a vector */
// ... do what you have to do with the vector, and then move it to the column ...
Fits::VecColumn<std::int32_t> int32Column({ "INT32", "", 1 }, std::move(int32Vec));
// Analogously to rasters, columns can be managed with the lightweight PtrColumn classe.
/* Generate a random column */
auto float32Column = Fits::Test::RandomVectorColumn<float>(8, 100);
return { stringColumn, int32Column, float32Column };
}
// WRITING //
void writeMefFile(const std::string& filename) {
logger.info("Creating a MEF file...");
Fits::MefFile f(filename, Fits::FileMode::Create);
const auto rasters = createRasters();
logger.info(" Writing image HDUs...");
/* Fill the header and data units */
const auto& image1 = f.assignImageExt("IMAGE1", rasters.int32Raster3D);
/* Fill the header only (for now) */
const auto& image2 = f.initImageExt<std::int16_t>("IMAGE2", rasters.int16Raster2D.shape());
image2.raster().write(rasters.int16Raster2D);
const auto columns = createColumns();
logger.info(" Writing binary table HDUs...");
/* Fill the header and data units */
const auto& table1 = f.assignBintableExt("TABLE1", columns.stringColumn, columns.int32Column, columns.float32Column);
/* Fill the header unit only (for now) */
const auto& table2 = f.initBintableExt(
"TABLE2",
columns.stringColumn.info(),
columns.int32Column.info(),
columns.float32Column.info());
/* Write a single column */
table2.columns().write(columns.stringColumn);
/* Write several columns */
table2.columns().writeSeq(columns.int32Column, columns.float32Column);
/* Write records */
writeRecords(f.accessPrimary<>());
/* Mute "unused variable" warnings */
(void)image1;
(void)table1;
// File is closed at destruction of f.
}
void writeRecords(const Fits::Hdu& hdu) {
const auto records = createRecords();
logger.info(" Writing records...");
/* Write a single record */
hdu.header().write(records.stringRecord);
/* Write several records */
hdu.header().writeSeq(records.intRecord, records.floatRecord, records.complexRecord);
/* Update using initialization lists */
Fits::Record<int>("INT", 1),
Fits::Record<float>("FLOAT", 3.14159F, "", "A larger piece of Pi"),
Fits::Record<std::complex<double>>("COMPLEX", { 180., 90. }));
}
// READING //
void readMefFile(const std::string& filename) {
logger.info("Reading the MEF file...");
Fits::MefFile f(filename, Fits::FileMode::Read);
logger.info(" Accessing HDUs...");
/* Access the Primary HDU */
const auto& primary = f.primary();
const auto primaryIndex = primary.index();
// Indices are 0-based.
/* Access an HDU by its index */
const auto& image2 = f.access<Fits::ImageHdu>(2);
const auto imageName = image2.readName();
/* Access an HDU by its name */
const auto& table1 = f.accessFirst<Fits::BintableHdu>("TABLE1");
const auto tableIndex = table1.index();
// If several HDUs have the same name, the first one is returned.
logger.info() << " Primary index: " << primaryIndex;
logger.info() << " Name of the second extension: " << imageName;
logger.info() << " Index of the 'TABLE1' extension: " << tableIndex;
readRecords(primary);
readRaster(image2);
readColumns(table1);
}
void readRecords(const Fits::Hdu& hdu) {
logger.info(" Reading records...");
/* Read a single record */
const auto intRecord = hdu.header().parse<int>("INT");
// Records can be sliced as their value for immediate use:
const int intValue = hdu.header().parse<int>("INT");
/* Read several records */
const auto someRecords = hdu.header().parseSeq(
const auto& thirdRecord = std::get<2>(someRecords);
/* Read as VariantValue */
const auto variantRecords = hdu.header().parseSeq<>({ "INT", "COMPLEX" });
const auto complexRecord = variantRecords.as<std::complex<double>>("COMPLEX");
/* Read as a user-defined structure */
const auto tutoRecords = hdu.header().parseStruct<TutoRecords>(
const auto& stringRecord = tutoRecords.stringRecord;
logger.info() << " " << intRecord.keyword << " = " << intRecord.value << " " << intRecord.unit;
logger.info() << " INT value: " << intValue;
logger.info() << " " << thirdRecord.keyword << " = " << thirdRecord.value << " " << thirdRecord.unit;
logger.info() << " " << complexRecord.keyword << " = " << complexRecord.value.real() << " + "
<< complexRecord.value.imag() << "j " << complexRecord.unit;
logger.info() << " " << stringRecord.keyword << " = " << stringRecord.value << " " << stringRecord.unit;
}
void readRaster(const Fits::ImageHdu& hdu) {
logger.info(" Reading a raster...");
const auto image = hdu.raster().read<std::int16_t, 2>();
const auto& firstPixel = image[{ 0, 0 }];
const auto& lastPixel = image.at({ -1, -1 });
// `operator[]` performs no bound checking, while `at` does and enables backward indexing.
logger.info() << " First pixel: " << firstPixel;
logger.info() << " Last pixel: " << lastPixel;
}
void readColumns(const Fits::BintableHdu& hdu) {
logger.info(" Reading columns...");
/* Read a single column */
const auto vectorColumn = hdu.readColumn<double>("VECTOR");
/* Read several columns by their name */
const auto byName = hdu.columns().readSeq(Fits::Named<std::string>("STRING"), Fits::Named<std::int32_t>("INT32"));
const auto& stringColumn = std::get<0>(byName);
/* Read several columns by their index */
const auto& intColumn = std::get<1>(byIndex);
/* Use values */
const auto& firstString = stringColumn(0);
const auto& firstInt = intColumn(0);
const auto& lastFloat = vectorColumn.at(-1, -1);
// There is no operator[]() for columns, because vector columns require 2 indices (row and repeat).
// operator()() performs no bound checking, while at() does and enables backward indexing.
logger.info() << " First string: " << firstString;
logger.info() << " First int: " << firstInt;
logger.info() << " Last float: " << lastFloat;
}
// PROGRAM //
class EleFitsTutorial : public Elements::Program {
public:
auto options = Fits::ProgramOptions::fromAuxFile("Tutorial.txt");
options.positional("output", value<std::string>()->default_value("/tmp/tuto.fits"), "Output file");
return options.asPair();
}
Elements::ExitCode mainMethod(std::map<std::string, variable_value>& args) override {
const auto filename = args["output"].as<std::string>();
logger.info() << "---";
logger.info() << "Hello, EleFits " << Fits::version() << "!";
logger.info() << "---";
writeMefFile(filename);
logger.info() << "---";
readMefFile(filename);
logger.info() << "---";
logger.info() << "The end!";
logger.info() << "---";
return Elements::ExitCode::OK;
}
};
MAIN_FOR(EleFitsTutorial)
Euclid::Fits::ImageRaster::read
VecRaster< T, n > read() const
Read the whole data unit as a new VecRaster.
Euclid::Fits::ImageHdu
Image HDU reader-writer.
Definition: ImageHdu.h:59
std::string
STL class.
std::move
T move(T... args)
std::pair
Euclid::Cfitsio::BintableIo::readColumns
std::tuple< Fits::VecColumn< Ts >... > readColumns(fitsfile *fptr, const std::vector< long > &indices)
Read several binary table columns with given indices.
std::vector
STL class.
Euclid::Fits::Hdu
Base class for ImageHdu and BintableHdu.
Definition: Hdu.h:47
Euclid::Fits::BintableHdu::columns
const BintableColumns & columns() const
Access the data unit column-wise.
Euclid::Fits::Record< std::string >
Euclid::Fits::Named
A name and type.
Definition: DataUtils.h:38
Euclid::Fits::RecordMode::CreateOrUpdate
@ CreateOrUpdate
Modify a record if keyword already exists, create a record otherwise.
Euclid::Fits::Header::parse
Record< T > parse(const std::string &keyword) const
Parse a record.
Euclid::Fits::ImageHdu::raster
const ImageRaster & raster() const
Access the data unit to read and write the raster.
Euclid::Fits::Header::parseSeq
RecordVec< T > parseSeq(const std::vector< std::string > &keywords) const
Parse a sequence of homogeneous records.
Euclid::Fits::Hdu::index
long index() const
Get the 0-based index of the HDU.
Euclid::Fits::Test::RandomVectorColumn
A small vector column of given type.
Definition: TestColumn.h:187
Euclid::Fits::version
std::string version()
Version number of the EleFits project.
Euclid::Fits::Hdu::readName
std::string readName() const
Read the extension name.
Euclid::Fits::Column::info
const ColumnInfo< std::decay_t< T > > & info() const
Get the column metadata.
std::to_string
T to_string(T... args)
Euclid::Fits::Indexed
An index and type.
Definition: DataUtils.h:61
std::complex::real
T real(T... args)
std::int16_t
std::map
STL class.
Euclid::Fits::ProgramOptions::fromAuxFile
static ProgramOptions fromAuxFile(const std::string &helpFile)
Create option descriptions from help file.
Euclid::Fits::Header::writeSeq
void writeSeq(const Record< Ts > &... records) const
Write a homogeneous sequence of records.
Euclid::Fits::Test::RandomRaster
A random Raster of given type and shape.
Definition: TestRaster.h:68
Euclid::Fits::Header::write
void write(const Record< T > &record) const
Write a record.
Euclid::Fits::VecColumn
Column which stores internally the data.
Definition: Column.h:387
Euclid::Fits::Header::parseStruct
TOut parseStruct(const Named< Ts > &... keywords) const
Parse a sequence of records.
std::complex< double >
Euclid::Fits::VecRaster
Copy constructor.
Definition: Raster.h:429
Euclid::Cfitsio::ImageIo::readRaster
Fits::VecRaster< T, n > readRaster(fitsfile *fptr)
Read the whole raster of the current image HDU.
Euclid::Fits::BintableHdu::readColumn
VecColumn< T > readColumn(long index) const
Read a column with given index.
Euclid::Fits::Hdu::header
const Header & header() const
Access the header unit to read and write records.
Euclid::Fits::MefFile
Multi-Extension Fits file reader-writer.
Definition: MefFile.h:47
Euclid
Euclid SGS namespace.
Definition: cfitsio_toc.dox:1
Euclid::Fits::BintableHdu
Binary table HDU reader-writer.
Definition: BintableHdu.h:36
Euclid::Fits::BintableColumns::readSeq
std::tuple< VecColumn< Ts >... > readSeq(const Named< Ts > &... names) const
Read the columns with given names.
Euclid::Cfitsio::HeaderIo::writeRecords
void writeRecords(fitsfile *fptr, const Fits::Record< Ts > &... records)
Write new records.