13#ifndef RTCTK_COMPONENTFRAMEWORK_FITSIOFUNCTIONS_HPP
14#define RTCTK_COMPONENTFRAMEWORK_FITSIOFUNCTIONS_HPP
26#include <fmt/format.h>
42void IdentifyCfitsioTypes(
int& bitpix,
int& datatype) {
43 if constexpr (std::is_integral_v<T>) {
44 if constexpr (std::is_same_v<T, bool>) {
47 }
else if constexpr (
sizeof(T) == 1) {
48 if constexpr (std::is_signed_v<T>) {
55 }
else if constexpr (
sizeof(T) == 2) {
56 if constexpr (std::is_signed_v<T>) {
63 }
else if constexpr (
sizeof(T) == 4) {
64 if constexpr (std::is_signed_v<T>) {
66 if constexpr (
sizeof(int) == 4) {
68 }
else if constexpr (
sizeof(long) == 4) {
71 static_assert(
sizeof(int) == 4 or
sizeof(
long) == 4,
72 "Require either int or long type to be 32 bits wide.");
76 if constexpr (
sizeof(
unsigned int) == 4) {
78 }
else if constexpr (
sizeof(
unsigned long) == 4) {
82 sizeof(
unsigned int) == 4 or
sizeof(
unsigned long) == 4,
83 "Require either unsigned int or unsigned long type to be 32 bits wide.");
86 }
else if constexpr (
sizeof(T) == 8) {
87 if constexpr (std::is_signed_v<T>) {
88 bitpix = LONGLONG_IMG;
92 bitpix = ULONGLONG_IMG;
93 datatype = TULONGLONG;
97 bitpix = LONGLONG_IMG;
103 sizeof(T) == 1 or
sizeof(T) == 2 or
sizeof(T) == 4 or
sizeof(T) == 8,
104 "The integer type used as the template parameter must be one of 8, 16, 32, or 64"
108 if constexpr (std::is_floating_point_v<T>) {
109 if constexpr (
sizeof(T) == 4) {
112 }
else if constexpr (
sizeof(T) == 8) {
117 sizeof(T) == 4 or
sizeof(T) == 8,
118 "The floating-point type used as the template parameter must be float or double.");
121 static_assert(std::is_arithmetic_v<T>,
122 "The template parameter used must be an integer or floating-point type.");
165void WriteMatrixToFits(
const std::string& filename,
const T& matrix,
bool boolean_items =
false) {
166 static_assert(IS_MATRIX_BUFFER_TYPE<T> or IS_MATRIX_SPAN_TYPE<T>,
167 "The matrix argument must be of type MatrixBuffer or MatrixSpan.");
168 assert(int64_t(matrix.GetNrows()) <= int64_t(std::numeric_limits<long>::max()));
169 assert(int64_t(matrix.GetNcols()) <= int64_t(std::numeric_limits<long>::max()));
171 using U =
typename T::value_type;
176 IdentifyCfitsioTypes<U>(bitpix, datatype);
178 std::string tmpfilename = filename +
".new-" + std::to_string(getpid());
181 fitsfile* fptr =
nullptr;
183 if (fits_create_file(&fptr, tmpfilename.c_str(), &status) != 0) {
184 std::string msg = fmt::format(
191 long nrows =
static_cast<long>(matrix.GetNrows());
192 long ncols =
static_cast<long>(matrix.GetNcols());
193 long size[2] = {ncols, nrows};
195 if (fits_create_img(fptr, bitpix, 2, size, &status) != 0) {
196 std::string msg = fmt::format(
"Failed to create the image type from FITS file '{}'. {}",
203 char value[80] =
"true";
204 if (fits_write_key(fptr, TSTRING,
"BOOLITEM", value,
nullptr, &status) != 0) {
205 std::string msg = fmt::format(
"Failed to write keyword BOOLITEM to '{}'. {}",
213 long fpixel[2] = {1, 1};
214 long nelements = matrix.size();
217 void* array =
const_cast<void*
>(
reinterpret_cast<const void*
>(matrix.data()));
219 if (fits_write_pix(fptr, datatype, fpixel, nelements, array, &status) != 0) {
220 std::string msg = fmt::format(
"Failed to write the matrix to FITS file '{}'. {}",
230 if (fits_close_file(fptr, &status) != 0) {
232 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
240 if (fits_close_file(fptr, &status) != 0) {
241 std::string msg = fmt::format(
250 std::filesystem::rename(tmpfilename, filename);
251 }
catch (
const std::exception& error) {
252 CII_THROW_WITH_NESTED(
255 fmt::format(
"Failed to rename FITS file '{}' to '{}'.", tmpfilename, filename))
268 static_assert(IS_MATRIX_BUFFER_TYPE<T> or IS_MATRIX_SPAN_TYPE<T>,
269 "The matrix argument must be of type MatrixBuffer or MatrixSpan.");
271 using U =
typename T::value_type;
274 int expected_bitpix = 0;
276 IdentifyCfitsioTypes<U>(expected_bitpix, datatype);
278 fitsfile* fptr =
nullptr;
280 if (fits_open_image(&fptr, filename.c_str(), READONLY, &status) != 0) {
282 fmt::format(
"Failed to open FITS file '{}'. {}", filename,
GetCfitsioErrorMsg(status));
291 if (fits_get_img_equivtype(fptr, &bitpix, &status) != 0) {
292 std::string msg = fmt::format(
"Failed to read the image type from FITS file '{}'. {}",
297 if (bitpix == LONGLONG_IMG) {
302 int result = fits_read_keyword(fptr,
"BZERO", value,
nullptr, &status);
303 if (result != 0 and status != KEY_NO_EXIST) {
304 std::string msg = fmt::format(
"Failed to read keyword BZERO from '{}'. {}",
309 if (std::string(value) ==
310 std::to_string(std::numeric_limits<uint64_t>::max() / 2 + 1)) {
312 bitpix = ULONGLONG_IMG;
316 bitpix = LONGLONG_IMG;
320 if (bitpix != expected_bitpix) {
321 std::string msg = fmt::format(
322 "The FITS file '{}' has the wrong image format of {}. Expected a FITS image of "
333 if (fits_get_img_dim(fptr, &naxis, &status) != 0) {
335 fmt::format(
"Failed to read the number of image axes from FITS file '{}'. {}",
341 std::string msg = fmt::format(
342 "The FITS file '{}' has image dimensions that we cannot handle. Expect a 2D image "
343 "when loading as a matrix.",
348 long size[2] = {-1, -1};
350 if (fits_get_img_size(fptr, 2, size, &status) != 0) {
351 std::string msg = fmt::format(
"Failed to read the image size from FITS file '{}'. {}",
357 using size_type =
typename T::size_type;
358 auto nrows =
static_cast<size_type
>(size[1]);
359 auto ncols =
static_cast<size_type
>(size[0]);
360 long nelements = size[0] * size[1];
362 long fpixel[2] = {1, 1};
364 if constexpr (IS_MATRIX_BUFFER_TYPE<T>) {
365 matrix.resize(nrows, ncols);
367 if (matrix.size() <
static_cast<size_type
>(nrows * ncols)) {
371 void* array = matrix.data();
374 fits_read_pix(fptr, datatype, fpixel, nelements,
nullptr, array, &anynull, &status);
376 std::string msg = fmt::format(
"Failed to read the image from FITS file '{}'. {}",
386 if (fits_close_file(fptr, &status) != 0) {
388 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
396 if (fits_close_file(fptr, &status) != 0) {
397 std::string msg = fmt::format(
412void WriteVectorToFits(
const std::string& filename,
const T& vector,
bool boolean_items =
false) {
413 static_assert(IS_VECTOR_TYPE<T> or IS_SPAN_TYPE<T>,
414 "The vector argument must be of type std::vector or gsl::span.");
415 assert(int64_t(vector.size()) <= int64_t(std::numeric_limits<long>::max()));
417 using U =
typename T::value_type;
422 IdentifyCfitsioTypes<U>(bitpix, datatype);
424 std::string tmpfilename = filename +
".new-" + std::to_string(getpid());
427 fitsfile* fptr =
nullptr;
429 if (fits_create_file(&fptr, tmpfilename.c_str(), &status) != 0) {
430 std::string msg = fmt::format(
437 long size =
static_cast<long>(vector.size());
439 if (fits_create_img(fptr, bitpix, 1, &size, &status) != 0) {
440 std::string msg = fmt::format(
"Failed to create the image type from FITS file '{}'. {}",
447 char value[80] =
"true";
448 if (fits_write_key(fptr, TSTRING,
"BOOLITEM", value,
nullptr, &status) != 0) {
449 std::string msg = fmt::format(
"Failed to write keyword BOOLITEM to '{}'. {}",
458 long nelements = vector.size();
461 void* array =
const_cast<void*
>(
reinterpret_cast<const void*
>(vector.data()));
463 if (fits_write_pix(fptr, datatype, &fpixel, nelements, array, &status) != 0) {
464 std::string msg = fmt::format(
"Failed to write the vector to FITS file '{}'. {}",
474 if (fits_close_file(fptr, &status) != 0) {
476 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
484 if (fits_close_file(fptr, &status) != 0) {
485 std::string msg = fmt::format(
494 std::filesystem::rename(tmpfilename, filename);
495 }
catch (
const std::exception& error) {
496 CII_THROW_WITH_NESTED(
499 fmt::format(
"Failed to rename FITS file '{}' to '{}'.", tmpfilename, filename));
512 static_assert(IS_VECTOR_TYPE<T> or IS_SPAN_TYPE<T>,
513 "The vector argument must be of type std::vector or gsl::span.");
515 using U =
typename T::value_type;
518 int expected_bitpix = 0;
520 IdentifyCfitsioTypes<U>(expected_bitpix, datatype);
522 fitsfile* fptr =
nullptr;
524 if (fits_open_image(&fptr, filename.c_str(), READONLY, &status) != 0) {
526 fmt::format(
"Failed to open FITS file '{}'. {}", filename,
GetCfitsioErrorMsg(status));
535 if (fits_get_img_equivtype(fptr, &bitpix, &status) != 0) {
536 std::string msg = fmt::format(
"Failed to read the image type from FITS file '{}'. {}",
541 if (bitpix == LONGLONG_IMG) {
546 int result = fits_read_keyword(fptr,
"BZERO", value,
nullptr, &status);
547 if (result != 0 and status != KEY_NO_EXIST) {
548 std::string msg = fmt::format(
"Failed to read keyword BZERO from '{}'. {}",
553 if (std::string(value) ==
554 std::to_string(std::numeric_limits<uint64_t>::max() / 2 + 1)) {
556 bitpix = ULONGLONG_IMG;
560 bitpix = LONGLONG_IMG;
564 if (bitpix != expected_bitpix) {
565 std::string msg = fmt::format(
566 "The FITS file '{}' has the wrong image format of {}. Expected a FITS image of "
577 if (fits_get_img_dim(fptr, &naxis, &status) != 0) {
579 fmt::format(
"Failed to read the number of image axes from FITS file '{}'. {}",
585 std::string msg = fmt::format(
586 "The FITS file '{}' has image dimensions that we cannot handle. Expect a 1D image "
587 "when loading as a vector.",
594 if (fits_get_img_size(fptr, 1, &nelements, &status) != 0) {
596 fmt::format(
"Failed to read the 1D image size from FITS file '{}'. {}",
602 using size_type =
typename T::size_type;
605 if constexpr (IS_VECTOR_TYPE<T>) {
606 vector.resize(
static_cast<size_type
>(nelements));
608 if (vector.size() <
static_cast<size_type
>(nelements)) {
612 void* array = vector.data();
615 fits_read_pix(fptr, datatype, &fpixel, nelements,
nullptr, array, &anynull, &status);
617 std::string msg = fmt::format(
"Failed to read the image from FITS file '{}'. {}",
627 if (fits_close_file(fptr, &status) != 0) {
629 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
637 if (fits_close_file(fptr, &status) != 0) {
638 std::string msg = fmt::format(
656 int_matrix(n, m) = matrix(n, m) ? 1 : 0;
673 matrix(n, m) = int_matrix(n, m) == 0 ? false :
true;
684 std::vector<int8_t> int_vector;
685 int_vector.resize(vector.size());
686 for (std::vector<int8_t>::size_type n = 0; n < int_vector.size(); ++n) {
687 int_vector[n] = vector[n] ? 1 : 0;
698 std::vector<int8_t> int_vector;
700 vector.resize(int_vector.size());
701 for (std::vector<int8_t>::size_type n = 0; n < int_vector.size(); ++n) {
702 vector[n] = int_vector[n] == 0 ? false :
true;
The BufferTooSmall is thrown when an API call fails because the provided buffer is not big enough to ...
Definition: exceptions.hpp:290
A buffer class representing 2D matrix data.
Definition: matrixBuffer.hpp:28
size_type GetNcols() const
Definition: matrixBuffer.hpp:101
size_type GetNrows() const
Definition: matrixBuffer.hpp:97
constexpr void resize(size_type n, size_type m)
Definition: matrixBuffer.hpp:72
The RtctkException class is the base class for all Rtctk exceptions.
Definition: exceptions.hpp:237
Provides macros and utilities for exception handling.
std::string GetCfitsioErrorMsg(int status)
Helper function to convert a Cfitsio status code to a human readable message.
Definition: fitsIoFunctions.cpp:20
void ReadVectorFromFits(const std::string &filename, T &vector)
Reads a FITS file containing a 1D image into a buffer object representing a vector.
Definition: fitsIoFunctions.hpp:511
void WriteMatrixToFits(const std::string &filename, const T &matrix, bool boolean_items=false)
Writes data representing a matrix as an image to a FITS file.
Definition: fitsIoFunctions.hpp:165
void ReadMatrixFromFits(const std::string &filename, T &matrix)
Reads a FITS file image into a buffer object representing a matrix.
Definition: fitsIoFunctions.hpp:267
std::string CfitsioDataTypeToString(int datatype)
Returns a string representation of a Cfitsio data type code.
Definition: fitsIoFunctions.cpp:58
std::string CfitsioImageTypeToString(int bitpix)
Returns a string representation of a Cfitsio image type code.
Definition: fitsIoFunctions.cpp:29
void WriteVectorToFits(const std::string &filename, const T &vector, bool boolean_items=false)
Writes data as a 1D image to a FITS file.
Definition: fitsIoFunctions.hpp:412
const std::type_info & GetFitsImageType(const std::string &filename)
Get the C++ type corresponding to a FITS image.
Definition: fitsIoFunctions.cpp:101
std::size_t GetFitsImageSize(const std::string &filename)
Get the number of pixels in a FITS image.
Definition: fitsIoFunctions.cpp:271
log4cplus::Logger & GetLogger(const std::string &name="app")
Get handle to a specific logger.
Definition: logger.cpp:180
Logging Support Library based on log4cplus.
Declaration of the MatrixBuffer template class used in APIs.
Definition: commandReplier.cpp:22
Provides useful mechanisms to test various type traits.