13#ifndef RTCTK_COMPONENTFRAMEWORK_FITSIOFUNCTIONS_HPP
14#define RTCTK_COMPONENTFRAMEWORK_FITSIOFUNCTIONS_HPP
23#include <fmt/format.h>
45void IdentifyCfitsioTypes(
int& bitpix,
int& datatype) {
46 if constexpr (std::is_integral_v<T>) {
47 if constexpr (std::is_same_v<T, bool>) {
50 }
else if constexpr (
sizeof(T) == 1) {
51 if constexpr (std::is_signed_v<T>) {
58 }
else if constexpr (
sizeof(T) == 2) {
59 if constexpr (std::is_signed_v<T>) {
66 }
else if constexpr (
sizeof(T) == 4) {
67 if constexpr (std::is_signed_v<T>) {
69 if constexpr (
sizeof(int) == 4) {
71 }
else if constexpr (
sizeof(long) == 4) {
74 static_assert(
sizeof(int) == 4 or
sizeof(long) == 4,
75 "Require either int or long type to be 32 bits wide.");
79 if constexpr (
sizeof(
unsigned int) == 4) {
81 }
else if constexpr (
sizeof(
unsigned long) == 4) {
85 sizeof(
unsigned int) == 4 or
sizeof(
unsigned long) == 4,
86 "Require either unsigned int or unsigned long type to be 32 bits wide.");
89 }
else if constexpr (
sizeof(T) == 8) {
90 if constexpr (std::is_signed_v<T>) {
91 bitpix = LONGLONG_IMG;
95 bitpix = ULONGLONG_IMG;
96 datatype = TULONGLONG;
100 bitpix = LONGLONG_IMG;
101 datatype = TLONGLONG;
106 sizeof(T) == 1 or
sizeof(T) == 2 or
sizeof(T) == 4 or
sizeof(T) == 8,
107 "The integer type used as the template parameter must be one of 8, 16, 32, or 64"
111 if constexpr (std::is_floating_point_v<T>) {
112 if constexpr (
sizeof(T) == 4) {
115 }
else if constexpr (
sizeof(T) == 8) {
120 sizeof(T) == 4 or
sizeof(T) == 8,
121 "The floating-point type used as the template parameter must be float or double.");
124 static_assert(std::is_arithmetic_v<T>,
125 "The template parameter used must be an integer or floating-point type.");
162#pragma GCC diagnostic push
163#pragma GCC diagnostic ignored "-Wdangling-reference"
181#pragma GCC diagnostic pop
228 "The buffer argument must be a matrix or a vector (or a corresponding span).");
231 std::vector<long> dims;
232 long n_elements = buffer.size();
235 dims.push_back(
static_cast<long>(buffer.GetNcols()));
236 dims.push_back(
static_cast<long>(buffer.GetNrows()));
238 dims.push_back(
static_cast<long>(buffer.size()));
241 using U =
typename T::value_type;
244 int bitpix = 0, datatype = 0;
245 IdentifyCfitsioTypes<U>(bitpix, datatype);
247 std::string tmpfilename = filename +
".new-" + std::to_string(getpid());
250 fitsfile* fptr =
nullptr;
252 if (fits_create_file(&fptr, tmpfilename.c_str(), &status) != 0) {
253 std::string msg = fmt::format(
261 if (fits_create_img(fptr, bitpix, dims.size(), dims.data(), &status) != 0) {
262 std::string msg = fmt::format(
"Failed to create image in FITS file '{}'. {}",
269 std::vector<long> fpixel(dims.size(), 1);
272 if constexpr (std::is_same_v<U, bool>) {
276 value.reserve(FLEN_CARD);
278 assert(value.capacity() == FLEN_CARD);
279 if (fits_write_key(fptr, TSTRING,
"BOOLITEM", value.data(),
nullptr, &status) != 0) {
280 std::string msg = fmt::format(
"Failed to write keyword BOOLITEM to '{}'. {}",
287 std::vector<int8_t> tmp_buffer;
288 tmp_buffer.resize(buffer.size());
290 buffer.begin(), buffer.end(), tmp_buffer.begin(), [](
bool element) -> int8_t {
291 return element == true ? 1 : 0;
296 void* array_ptr =
const_cast<void*
>(
reinterpret_cast<const void*
>(tmp_buffer.data()));
298 if (fits_write_pix(fptr, datatype, fpixel.data(), n_elements, array_ptr, &status) !=
300 std::string msg = fmt::format(
"Failed to write buffer to FITS file '{}'. {}",
307 void* array_ptr =
const_cast<void*
>(
reinterpret_cast<const void*
>(buffer.data()));
309 if (fits_write_pix(fptr, datatype, fpixel.data(), n_elements, array_ptr, &status) !=
311 std::string msg = fmt::format(
"Failed to write buffer to FITS file '{}'. {}",
322 if (fits_close_file(fptr, &status) != 0) {
324 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
332 if (fits_close_file(fptr, &status) != 0) {
333 std::string msg = fmt::format(
342 std::filesystem::rename(tmpfilename, filename);
343 }
catch (
const std::exception& error) {
346 fmt::format(
"Failed to rename '{}' to '{}'.", tmpfilename, filename));
369 "The buffer argument must be a matrix or a vector (or a corresponding span).");
371 using U =
typename T::value_type;
374 int expected_bitpix = 0;
376 IdentifyCfitsioTypes<U>(expected_bitpix, datatype);
378 fitsfile* fptr =
nullptr;
380 if (fits_open_image(&fptr, filename.c_str(), READONLY, &status) != 0) {
382 fmt::format(
"Failed to open FITS file '{}'. {}", filename,
GetCfitsioErrorMsg(status));
391 if (fits_get_img_equivtype(fptr, &bitpix, &status) != 0) {
392 std::string msg = fmt::format(
"Failed to read the image type from FITS file '{}'. {}",
398 if (bitpix == LONGLONG_IMG) {
401 std::array<char, FLEN_CARD> value;
403 int result = fits_read_keyword(fptr,
"BZERO", value.data(),
nullptr, &status);
404 if (result != 0 && status != KEY_NO_EXIST) {
405 std::string msg = fmt::format(
"Failed to read keyword BZERO from '{}'. {}",
410 if (std::string(value.data()) ==
411 std::to_string(std::numeric_limits<uint64_t>::max() / 2 + 1)) {
413 bitpix = ULONGLONG_IMG;
417 bitpix = LONGLONG_IMG;
421 if (bitpix != expected_bitpix) {
422 std::string msg = fmt::format(
423 "The FITS file '{}' has the wrong image format of {}. "
424 "Expected a FITS image of type {}.",
434 if (fits_get_img_dim(fptr, &naxis, &status) != 0) {
436 fmt::format(
"Failed to read the number of image axes from FITS file '{}'. {}",
443 const int expected_naxis = is_matrix ? 2 : 1;
445 if (naxis != expected_naxis) {
446 std::string msg = fmt::format(
447 "The FITS file '{}' has image dimensions that we cannot handle. "
448 "Expected a {}D image.",
454 using size_type =
typename T::size_type;
457 std::vector<long> dims;
458 dims.resize(
static_cast<size_type
>(naxis));
460 if (fits_get_img_size(fptr, naxis, dims.data(), &status) != 0) {
461 std::string msg = fmt::format(
"Failed to read the image size from FITS file '{}'. {}",
468 for (
auto dim : dims) {
473 buffer.resize(
static_cast<size_type
>(dims[0]));
475 auto nrows =
static_cast<size_type
>(dims[1]);
476 auto ncols =
static_cast<size_type
>(dims[0]);
477 buffer.resize(nrows, ncols);
479 if (buffer.size() <
static_cast<size_type
>(n_elements)) {
485 std::vector<long> fpixel(dims.size(), 1);
488 if constexpr (std::is_same_v<U, bool>) {
491 std::vector<int8_t> tmp_buffer;
492 tmp_buffer.resize(buffer.size());
495 if (fits_read_pix(fptr,
503 std::string msg = fmt::format(
"Failed to read the image from FITS file '{}'. {}",
510 tmp_buffer.begin(), tmp_buffer.end(), buffer.begin(), [](int8_t element) {
511 return element == 0 ? false : true;
516 if (fits_read_pix(fptr,
524 std::string msg = fmt::format(
"Failed to read the image from FITS file '{}'. {}",
535 if (fits_close_file(fptr, &status) != 0) {
537 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
545 if (fits_close_file(fptr, &status) != 0) {
546 std::string msg = fmt::format(
553void WriteDataToFits(
const std::string& filename,
const std::any& buffer);
567[[deprecated(
"Replaced by WriteDataToFits")]]
568void WriteMatrixToFits(
const std::string& filename,
const T& matrix,
bool boolean_items =
false) {
570 "The matrix argument must be of type MatrixBuffer or MatrixSpan.");
571 assert(uint64_t(matrix.GetNrows()) <= uint64_t(std::numeric_limits<long>::max()));
572 assert(uint64_t(matrix.GetNcols()) <= uint64_t(std::numeric_limits<long>::max()));
574 using U =
typename T::value_type;
579 IdentifyCfitsioTypes<U>(bitpix, datatype);
581 std::string tmpfilename = filename +
".new-" + std::to_string(getpid());
584 fitsfile* fptr =
nullptr;
586 if (fits_create_file(&fptr, tmpfilename.c_str(), &status) != 0) {
587 std::string msg = fmt::format(
594 long nrows =
static_cast<long>(matrix.GetNrows());
595 long ncols =
static_cast<long>(matrix.GetNcols());
596 std::array<long, 2> shape = {ncols, nrows};
598 if (fits_create_img(fptr, bitpix, shape.size(), shape.data(), &status) != 0) {
599 std::string msg = fmt::format(
"Failed to create the image type from FITS file '{}'. {}",
607 value.reserve(FLEN_CARD);
609 assert(value.capacity() == FLEN_CARD);
610 if (fits_write_key(fptr, TSTRING,
"BOOLITEM", value.data(),
nullptr, &status) != 0) {
611 std::string msg = fmt::format(
"Failed to write keyword BOOLITEM to '{}'. {}",
619 std::array<long, 2> fpixel = {1, 1};
620 long nelements = matrix.size();
623 void* array =
const_cast<void*
>(
reinterpret_cast<const void*
>(matrix.data()));
625 if (fits_write_pix(fptr, datatype, fpixel.data(), nelements, array, &status) != 0) {
626 std::string msg = fmt::format(
"Failed to write the matrix to FITS file '{}'. {}",
636 if (fits_close_file(fptr, &status) != 0) {
638 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
646 if (fits_close_file(fptr, &status) != 0) {
647 std::string msg = fmt::format(
656 std::filesystem::rename(tmpfilename, filename);
657 }
catch (
const std::exception& error) {
658 CII_THROW_WITH_NESTED(
661 fmt::format(
"Failed to rename FITS file '{}' to '{}'.", tmpfilename, filename))
673[[deprecated(
"Replaced by ReadDataFromFits")]]
676 "The matrix argument must be of type MatrixBuffer or MatrixSpan.");
678 using U =
typename T::value_type;
681 int expected_bitpix = 0;
683 IdentifyCfitsioTypes<U>(expected_bitpix, datatype);
685 fitsfile* fptr =
nullptr;
687 if (fits_open_image(&fptr, filename.c_str(), READONLY, &status) != 0) {
689 fmt::format(
"Failed to open FITS file '{}'. {}", filename,
GetCfitsioErrorMsg(status));
698 if (fits_get_img_equivtype(fptr, &bitpix, &status) != 0) {
699 std::string msg = fmt::format(
"Failed to read the image type from FITS file '{}'. {}",
704 if (bitpix == LONGLONG_IMG) {
709 int result = fits_read_keyword(fptr,
"BZERO", value,
nullptr, &status);
710 if (result != 0 and status != KEY_NO_EXIST) {
711 std::string msg = fmt::format(
"Failed to read keyword BZERO from '{}'. {}",
716 if (std::string(value) ==
717 std::to_string(std::numeric_limits<uint64_t>::max() / 2 + 1)) {
719 bitpix = ULONGLONG_IMG;
723 bitpix = LONGLONG_IMG;
727 if (bitpix != expected_bitpix) {
728 std::string msg = fmt::format(
729 "The FITS file '{}' has the wrong image format of {}. Expected a FITS image of "
740 if (fits_get_img_dim(fptr, &naxis, &status) != 0) {
742 fmt::format(
"Failed to read the number of image axes from FITS file '{}'. {}",
748 std::string msg = fmt::format(
749 "The FITS file '{}' has image dimensions that we cannot handle. Expect a 2D image "
750 "when loading as a matrix.",
755 long size[2] = {-1, -1};
757 if (fits_get_img_size(fptr, 2, size, &status) != 0) {
758 std::string msg = fmt::format(
"Failed to read the image size from FITS file '{}'. {}",
764 using size_type =
typename T::size_type;
765 auto nrows =
static_cast<size_type
>(size[1]);
766 auto ncols =
static_cast<size_type
>(size[0]);
767 long nelements = size[0] * size[1];
769 long fpixel[2] = {1, 1};
772 matrix.resize(nrows, ncols);
774 if (matrix.size() <
static_cast<size_type
>(nrows * ncols)) {
778 void* array = matrix.data();
781 fits_read_pix(fptr, datatype, fpixel, nelements,
nullptr, array, &anynull, &status);
783 std::string msg = fmt::format(
"Failed to read the image from FITS file '{}'. {}",
793 if (fits_close_file(fptr, &status) != 0) {
795 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
803 if (fits_close_file(fptr, &status) != 0) {
804 std::string msg = fmt::format(
819[[deprecated(
"Replaced by WriteDataToFits")]]
820void WriteVectorToFits(
const std::string& filename,
const T& vector,
bool boolean_items =
false) {
822 "The vector argument must be of type std::vector or gsl::span.");
823 assert(uint64_t(vector.size()) <= uint64_t(std::numeric_limits<long>::max()));
825 using U =
typename T::value_type;
830 IdentifyCfitsioTypes<U>(bitpix, datatype);
832 std::string tmpfilename = filename +
".new-" + std::to_string(getpid());
835 fitsfile* fptr =
nullptr;
837 if (fits_create_file(&fptr, tmpfilename.c_str(), &status) != 0) {
838 std::string msg = fmt::format(
845 long size =
static_cast<long>(vector.size());
847 if (fits_create_img(fptr, bitpix, 1, &size, &status) != 0) {
848 std::string msg = fmt::format(
"Failed to create the image type from FITS file '{}'. {}",
856 value.reserve(FLEN_CARD);
858 assert(value.capacity() == FLEN_CARD);
859 if (fits_write_key(fptr, TSTRING,
"BOOLITEM", value.data(),
nullptr, &status) != 0) {
860 std::string msg = fmt::format(
"Failed to write keyword BOOLITEM to '{}'. {}",
869 long nelements = vector.size();
872 void* array =
const_cast<void*
>(
reinterpret_cast<const void*
>(vector.data()));
874 if (fits_write_pix(fptr, datatype, &fpixel, nelements, array, &status) != 0) {
875 std::string msg = fmt::format(
"Failed to write the vector to FITS file '{}'. {}",
885 if (fits_close_file(fptr, &status) != 0) {
887 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
895 if (fits_close_file(fptr, &status) != 0) {
896 std::string msg = fmt::format(
905 std::filesystem::rename(tmpfilename, filename);
906 }
catch (
const std::exception& error) {
907 CII_THROW_WITH_NESTED(
910 fmt::format(
"Failed to rename FITS file '{}' to '{}'.", tmpfilename, filename));
922[[deprecated(
"Replaced by ReadDataFromFits")]]
925 "The vector argument must be of type std::vector or gsl::span.");
927 using U =
typename T::value_type;
930 int expected_bitpix = 0;
932 IdentifyCfitsioTypes<U>(expected_bitpix, datatype);
934 fitsfile* fptr =
nullptr;
936 if (fits_open_image(&fptr, filename.c_str(), READONLY, &status) != 0) {
938 fmt::format(
"Failed to open FITS file '{}'. {}", filename,
GetCfitsioErrorMsg(status));
947 if (fits_get_img_equivtype(fptr, &bitpix, &status) != 0) {
948 std::string msg = fmt::format(
"Failed to read the image type from FITS file '{}'. {}",
953 if (bitpix == LONGLONG_IMG) {
958 int result = fits_read_keyword(fptr,
"BZERO", value,
nullptr, &status);
959 if (result != 0 and status != KEY_NO_EXIST) {
960 std::string msg = fmt::format(
"Failed to read keyword BZERO from '{}'. {}",
965 if (std::string(value) ==
966 std::to_string(std::numeric_limits<uint64_t>::max() / 2 + 1)) {
968 bitpix = ULONGLONG_IMG;
972 bitpix = LONGLONG_IMG;
976 if (bitpix != expected_bitpix) {
977 std::string msg = fmt::format(
978 "The FITS file '{}' has the wrong image format of {}. Expected a FITS image of "
989 if (fits_get_img_dim(fptr, &naxis, &status) != 0) {
991 fmt::format(
"Failed to read the number of image axes from FITS file '{}'. {}",
997 std::string msg = fmt::format(
998 "The FITS file '{}' has image dimensions that we cannot handle. Expect a 1D image "
999 "when loading as a vector.",
1004 long nelements = -1;
1006 if (fits_get_img_size(fptr, 1, &nelements, &status) != 0) {
1008 fmt::format(
"Failed to read the 1D image size from FITS file '{}'. {}",
1014 using size_type =
typename T::size_type;
1018 vector.resize(
static_cast<size_type
>(nelements));
1020 if (vector.size() <
static_cast<size_type
>(nelements)) {
1024 void* array = vector.data();
1027 fits_read_pix(fptr, datatype, &fpixel, nelements,
nullptr, array, &anynull, &status);
1029 std::string msg = fmt::format(
"Failed to read the image from FITS file '{}'. {}",
1039 if (fits_close_file(fptr, &status) != 0) {
1041 fmt::format(
"Failed to close file '{}' while handling an exception. {}",
1049 if (fits_close_file(fptr, &status) != 0) {
1050 std::string msg = fmt::format(
1061template <
typename A>
1062[[deprecated(
"Replaced by WriteDataToFits")]]
1064#pragma GCC diagnostic push
1065#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1071 int_matrix(n, m) = matrix(n, m) ? 1 : 0;
1075#pragma GCC diagnostic pop
1081template <
typename A>
1082[[deprecated(
"Replaced by ReadDataFromFits")]]
1084#pragma GCC diagnostic push
1085#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1092 matrix(n, m) = int_matrix(n, m) == 0 ? false :
true;
1095#pragma GCC diagnostic pop
1101template <
typename A>
1102[[deprecated(
"Replaced by WriteDataToFits")]]
1104#pragma GCC diagnostic push
1105#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1107 std::vector<int8_t> int_vector;
1108 int_vector.resize(vector.size());
1109 for (std::vector<int8_t>::size_type n = 0; n < int_vector.size(); ++n) {
1110 int_vector[n] = vector[n] ? 1 : 0;
1113#pragma GCC diagnostic pop
1119template <
typename A>
1120[[deprecated(
"Replaced by ReadDataFromFits")]]
1122#pragma GCC diagnostic push
1123#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1125 std::vector<int8_t> int_vector;
1127 vector.resize(int_vector.size());
1128 for (std::vector<int8_t>::size_type n = 0; n < int_vector.size(); ++n) {
1129 vector[n] = int_vector[n] == 0 ? false :
true;
1131#pragma GCC diagnostic pop
The BufferTooSmall is thrown when an API call fails because the provided buffer is not big enough to ...
Definition exceptions.hpp:266
A buffer class representing 2D matrix data.
Definition matrixBuffer.hpp:28
size_type GetNcols() const
Definition matrixBuffer.hpp:89
size_type GetNrows() const
Definition matrixBuffer.hpp:85
constexpr void resize(size_type n, size_type m)
Definition matrixBuffer.hpp:60
The RtctkException class is the base class for all Rtctk exceptions.
Definition exceptions.hpp:213
Provides macros and utilities for exception handling.
Logging Support Library based on log4cplus.
Declaration of the MatrixBuffer template class used in APIs.
Definition commandReplier.cpp:22
std::string GetCfitsioErrorMsg(int status)
Helper function to convert a Cfitsio status code to a human readable message.
Definition fitsIoFunctions.cpp:23
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:923
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:568
void ReadMatrixFromFits(const std::string &filename, T &matrix)
Reads a FITS file image into a buffer object representing a matrix.
Definition fitsIoFunctions.hpp:674
constexpr bool IS_MATRIX_SPAN_TYPE
Is true if the type is a MatrixSpan<U> of some type U.
Definition typeTraits.hpp:128
void ReadDataFromFits(const std::string &filename, std::any &buffer)
Definition fitsIoFunctions.cpp:406
constexpr bool IS_SPAN_TYPE
Is true if the type is a gsl::span<U> of some type U.
Definition typeTraits.hpp:107
void WriteDataToFits(const std::string &filename, const std::any &buffer)
Definition fitsIoFunctions.cpp:395
log4cplus::Logger & GetLogger(const std::string &name="app")
Get handle to a specific logger.
Definition logger.cpp:192
std::string CfitsioDataTypeToString(int datatype)
Returns a string representation of a Cfitsio data type code.
Definition fitsIoFunctions.cpp:61
std::vector< uint64_t > GetFitsImageShape(const std::string &filename)
Get the shape of a FITS image.
Definition fitsIoFunctions.cpp:274
std::string CfitsioImageTypeToString(int bitpix)
Returns a string representation of a Cfitsio image type code.
Definition fitsIoFunctions.cpp:32
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:820
constexpr bool IS_VECTOR_TYPE
Is true if the type is a std::vector<U> of some type U.
Definition typeTraits.hpp:47
constexpr bool IS_MATRIX_BUFFER_TYPE
Is true if the type is a MatrixBuffer<U> of some type U.
Definition typeTraits.hpp:77
const std::type_info & GetFitsImageType(const std::string &filename)
Get the C++ type corresponding to a FITS image.
Definition fitsIoFunctions.cpp:104
std::size_t GetFitsImageSize(const std::string &filename)
Get the number of pixels in a FITS image.
Definition fitsIoFunctions.cpp:336
Provides useful mechanisms to test various type traits.