8#include <unordered_set>
9#include <unordered_map>
20#include "boost/algorithm/string.hpp"
21#include "boost/preprocessor/seq/for_each.hpp"
22#include "boost/format.hpp"
47constexpr int header_bitpix =
static_cast<int>(
sizeof(
T)) * 8 * (std::is_floating_point_v<T> ? -1 : 1);
49template <
typename T,
bool is_
signed = std::is_
signed_v<T>,
int size = sizeof(T)>
50constexpr int cfitsio_bitpix = header_bitpix<T>;
53constexpr int cfitsio_bitpix<T, true, 1> = SBYTE_IMG;
56constexpr int cfitsio_bitpix<T, false, 2> = USHORT_IMG;
59constexpr int cfitsio_bitpix<T, false, 4> = ULONG_IMG;
62constexpr int cfitsio_bitpix<T, false, 8> = ULONGLONG_IMG;
75std::string makeLimitedFitsHeaderImpl(std::vector<std::string>
const ¶mNames,
76 daf::base::PropertySet
const &metadata) {
78 for (
auto const &fullName : paramNames) {
80 auto name = (lastPeriod == std::string::npos) ? fullName : fullName.substr(lastPeriod + 1);
81 std::type_info
const &
type = metadata.typeOf(name);
85 if (
name.size() > 8) {
88 out = (boost::format(
"%-8s= ") % name).str();
90 if (type ==
typeid(
bool)) {
91 out += metadata.get<
bool>(
name) ?
"T" :
"F";
92 }
else if (type ==
typeid(std::uint8_t)) {
93 out += (boost::format(
"%20d") %
static_cast<int>(metadata.get<std::uint8_t>(
name))).str();
94 }
else if (type ==
typeid(
int)) {
95 out += (boost::format(
"%20d") % metadata.get<
int>(
name)).str();
96 }
else if (type ==
typeid(
double)) {
97 double value = metadata.get<
double>(
name);
100 out += (boost::format(
"%#20.17G") %
value).
str();
103 boost::format(
"In %s, found NaN in metadata item '%s'") %
104 BOOST_CURRENT_FUNCTION % name);
108 }
else if (type ==
typeid(
float)) {
111 out += (boost::format(
"%#20.15G") %
value).
str();
114 boost::format(
"In %s, found NaN in metadata item '%s'") %
115 BOOST_CURRENT_FUNCTION % name);
121 }
else if (type ==
typeid(std::string)) {
122 out +=
"'" + metadata.get<std::string>(
name) +
"'";
123 if (out.
size() > 80) {
128 int const len = out.
size();
130 out += std::string(80 - len,
' ');
131 }
else if (len > 80) {
134 "Formatted data too long: " +
std::to_string(len) +
" > 80: \"" + out +
"\"");
151class StringStartSet {
154 StringStartSet(std::initializer_list<std::string>
const &input) : _minSize(-1) {
155 for (
auto const &word : input) {
157 if (size < _minSize) {
161 for (
auto const &word : input) {
162 std::string
const start = startString(word);
163 assert(_words.count(start) == 0);
164 _words[start] = word;
169 bool matches(std::string
const &key)
const {
170 auto const iter = _words.find(startString(key));
171 if (iter == _words.end()) {
175 std::string
const &word =
iter->second;
180 using Map = std::unordered_map<std::string, std::string>;
183 std::string startString(std::string
const &word)
const {
return word.
substr(0, _minSize); }
194static std::unordered_set<std::string>
const ignoreKeys = {
196 "SIMPLE",
"BITPIX",
"NAXIS",
"EXTEND",
"GCOUNT",
"PCOUNT",
"XTENSION",
"TFIELDS",
"BSCALE",
"BZERO",
198 "ZBITPIX",
"ZIMAGE",
"ZCMPTYPE",
"ZSIMPLE",
"ZEXTEND",
"ZBLANK",
"ZDATASUM",
"ZHECKSUM",
"ZQUANTIZ",
201 "DATASUM",
"CHECKSUM"};
208StringStartSet
const ignoreKeyStarts{
209 "NAXIS",
"TZERO",
"TSCAL",
211 "ZNAXIS",
"ZTILE",
"ZNAME",
"ZVAL"};
218StringStartSet
const ignoreKeyStartsWrite{
"TFORM",
"TTYPE"};
221std::string strip(std::string
const &s) {
222 if (s.
empty())
return s;
224 if (i1 == std::string::npos) {
225 return std::string();
229 return s.
substr(i1, 1 + i2 - i1);
234char getFormatCode(
bool *) {
return 'X'; }
235char getFormatCode(std::string *) {
return 'A'; }
236char getFormatCode(std::int8_t *) {
return 'S'; }
237char getFormatCode(std::uint8_t *) {
return 'B'; }
238char getFormatCode(std::int16_t *) {
return 'I'; }
239char getFormatCode(std::uint16_t *) {
return 'U'; }
240char getFormatCode(std::int32_t *) {
return 'J'; }
241char getFormatCode(std::uint32_t *) {
return 'V'; }
242char getFormatCode(std::int64_t *) {
return 'K'; }
243char getFormatCode(
float *) {
return 'E'; }
244char getFormatCode(
double *) {
return 'D'; }
245char getFormatCode(std::complex<float> *) {
return 'C'; }
246char getFormatCode(std::complex<double> *) {
return 'M'; }
247char getFormatCode(lsst::geom::Angle *) {
return 'D'; }
252std::string makeColumnFormat(
int size = 1) {
254 return (boost::format(
"%d%c") % size % getFormatCode((T *)
nullptr)).str();
255 }
else if (size < 0) {
257 return (boost::format(
"1Q%c(%d)") % getFormatCode((T *)
nullptr) % (-size)).str();
260 return (boost::format(
"1Q%c") % getFormatCode((T *)
nullptr)).str();
270struct FitsType<bool> {
271 static int const CONSTANT = TLOGICAL;
274struct FitsType<char> {
275 static int const CONSTANT = TSTRING;
278struct FitsType<signed char> {
279 static int const CONSTANT = TSBYTE;
282struct FitsType<unsigned char> {
283 static int const CONSTANT = TBYTE;
286struct FitsType<short> {
287 static int const CONSTANT = TSHORT;
290struct FitsType<unsigned short> {
291 static int const CONSTANT = TUSHORT;
294struct FitsType<
int> {
295 static int const CONSTANT = TINT;
298struct FitsType<unsigned
int> {
299 static int const CONSTANT = TUINT;
302struct FitsType<long> {
303 static int const CONSTANT = TLONG;
306struct FitsType<unsigned long> {
307 static int const CONSTANT = TULONG;
310struct FitsType<long long> {
311 static int const CONSTANT = TLONGLONG;
314struct FitsType<unsigned long long> {
315 static int const CONSTANT = TULONGLONG;
318struct FitsType<
float> {
319 static int const CONSTANT = TFLOAT;
322struct FitsType<double> {
323 static int const CONSTANT = TDOUBLE;
326struct FitsType<lsst::geom::Angle> {
327 static int const CONSTANT = TDOUBLE;
330struct FitsType<std::complex<float> > {
331 static int const CONSTANT = TCOMPLEX;
334struct FitsType<std::complex<double> > {
335 static int const CONSTANT = TDBLCOMPLEX;
340struct FitsTableType :
public FitsType<T> {};
342struct FitsTableType<bool> {
343 static int const CONSTANT = TBIT;
346bool isFitsImageTypeSigned(
int constant) {
348 case BYTE_IMG:
return false;
349 case SHORT_IMG:
return true;
350 case USHORT_IMG:
return false;
351 case LONG_IMG:
return true;
352 case ULONG_IMG:
return false;
353 case LONGLONG_IMG:
return true;
354 case ULONGLONG_IMG:
return false;
355 case FLOAT_IMG:
return true;
356 case DOUBLE_IMG:
return true;
358 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
"Invalid constant.");
367 ItemInfo(
bool isComment,
bool isValid) : isComment(isComment), isValid(isValid) {}
379ItemInfo isCommentIsValid(daf::base::PropertyList
const &pl, std::string
const &name) {
380 if (!pl.exists(name)) {
381 return ItemInfo(
false,
false);
383 std::type_info
const &
type = pl.typeOf(name);
384 if ((name ==
"COMMENT") || (name ==
"HISTORY")) {
385 return ItemInfo(
true, type ==
typeid(std::string));
387 return ItemInfo(
false,
true);
398 os <<
"cfitsio error";
399 if (fileName !=
"") {
400 os <<
" (" << fileName <<
")";
403 char fitsErrMsg[FLEN_ERRMSG];
404 fits_get_errstatus(status, fitsErrMsg);
405 os <<
": " << fitsErrMsg <<
" (" << status <<
")";
410 os <<
"\ncfitsio error stack:\n";
411 char cfitsioMsg[FLEN_ERRMSG];
414 while (fits_read_errmsg(cfitsioMsg) != 0) {
415 cfitsioMsg[FLEN_ERRMSG-1] = char(0);
418 if( !isprint(cfitsioMsg[i]) ) cfitsioMsg[i] =
'.';
419 os <<
" " << cfitsioMsg <<
"\n";
426 fitsfile *fd =
reinterpret_cast<fitsfile *
>(fptr);
427 if (fd !=
nullptr && fd->Fptr !=
nullptr && fd->Fptr->filename !=
nullptr) {
428 fileName = fd->Fptr->filename;
443 for (
auto const &name : allParamNames) {
444 if (excludeNames.
count(name) == 0) {
448 return makeLimitedFitsHeaderImpl(desiredParamNames, metadata);
467 return cfitsio_bitpix<T>;
476 fitsfile *fd =
reinterpret_cast<fitsfile *
>(
fptr);
477 if (fd !=
nullptr && fd->Fptr !=
nullptr && fd->Fptr->filename !=
nullptr) {
478 fileName = fd->Fptr->filename;
485 fits_get_hdu_num(
reinterpret_cast<fitsfile *
>(
fptr), &n);
491 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu,
nullptr, &
status);
497 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu + 1,
nullptr, &
status);
502 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), 1,
nullptr, &tmpStatus);
511 fits_movnam_hdu(
reinterpret_cast<fitsfile *
>(
fptr),
static_cast<int>(hdutype),
512 const_cast<char *
>(name.c_str()), hduver, &
status);
515 static_cast<int>(hdutype) % hduver);
520 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &n, &
status);
557double stringToNonFiniteDouble(std::string
const &value) {
558 if (value ==
"NAN") {
561 if (value ==
"+INFINITY") {
564 if (value ==
"-INFINITY") {
571void updateKeyImpl(
Fits &fits,
char const *key, T
const &value,
char const *comment) {
572 fits_update_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
573 const_cast<T *
>(&value),
const_cast<char *
>(comment), &fits.status);
576void updateKeyImpl(
Fits &fits,
char const *key, std::string
const &value,
char const *comment) {
577 fits_update_key_longstr(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(key),
578 const_cast<char *
>(
value.c_str()),
const_cast<char *
>(comment), &fits.status);
581void updateKeyImpl(
Fits &fits,
char const *key,
bool const &value,
char const *comment) {
583 fits_update_key(
reinterpret_cast<fitsfile *
>(fits.fptr), TLOGICAL,
const_cast<char *
>(key), &v,
584 const_cast<char *
>(comment), &fits.status);
587void updateKeyImpl(
Fits &fits,
char const *key,
double const &value,
char const *comment) {
588 std::string strValue = nonFiniteDoubleToString(value);
589 if (!strValue.
empty()) {
590 updateKeyImpl(fits, key, strValue, comment);
592 fits_update_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<double>::CONSTANT,
593 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
599void writeKeyImpl(
Fits &fits,
char const *key, T
const &value,
char const *comment) {
600 fits_write_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
601 const_cast<T *
>(&value),
const_cast<char *
>(comment), &fits.status);
604void writeKeyImpl(
Fits &fits,
char const *key,
char const *comment) {
606 fits_write_key_null(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(key),
607 const_cast<char *
>(comment), &fits.status);
610void writeKeyImpl(
Fits &fits,
char const *key, std::string
const &value,
char const *comment) {
611 if (
strncmp(key,
"COMMENT", 7) == 0) {
612 fits_write_comment(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(
value.c_str()),
614 }
else if (
strncmp(key,
"HISTORY", 7) == 0) {
615 fits_write_history(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(
value.c_str()),
618 fits_write_key_longstr(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(key),
619 const_cast<char *
>(
value.c_str()),
const_cast<char *
>(comment), &fits.status);
623void writeKeyImpl(
Fits &fits,
char const *key,
bool const &value,
char const *comment) {
625 fits_write_key(
reinterpret_cast<fitsfile *
>(fits.fptr), TLOGICAL,
const_cast<char *
>(key), &v,
626 const_cast<char *
>(comment), &fits.status);
629void writeKeyImpl(
Fits &fits,
char const *key,
double const &value,
char const *comment) {
630 std::string strValue = nonFiniteDoubleToString(value);
631 if (!strValue.
empty()) {
632 writeKeyImpl(fits, key, strValue, comment);
634 fits_write_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<double>::CONSTANT,
635 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
644 updateKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
652 writeKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
660 updateKeyImpl(*
this, key.
c_str(), value,
nullptr);
668 writeKeyImpl(*
this, key.
c_str(), value,
nullptr);
676 updateKey((boost::format(
"%s%d") % prefix % (n + 1)).str(), value, comment);
684 writeKey((boost::format(
"%s%d") % prefix % (n + 1)).str(), value, comment);
692 updateKey((boost::format(
"%s%d") % prefix % (n + 1)).str(), value);
700 writeKey((boost::format(
"%s%d") % prefix % (n + 1)).str(), value);
711void readKeyImpl(
Fits &
fits,
char const *key, T &value) {
712 fits_read_key(
reinterpret_cast<fitsfile *
>(
fits.fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
713 &value,
nullptr, &
fits.status);
718 fits_read_key_longstr(
reinterpret_cast<fitsfile *
>(
fits.fptr),
const_cast<char *
>(key), &buf,
nullptr,
726void readKeyImpl(Fits &fits,
char const *key,
double &value) {
730 char buf[FLEN_VALUE];
731 fits_read_keyword(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(key), buf,
nullptr, &fits.status);
732 if (fits.status != 0) {
735 if (std::string(buf).
find(
'\'') != std::string::npos) {
736 std::string unquoted;
737 readKeyImpl(fits, key, unquoted);
738 if (fits.status != 0) {
741 value = stringToNonFiniteDouble(unquoted);
744 afw::fits::FitsError,
745 (boost::format(
"Unrecognised string value for keyword '%s' when parsing as double: %s") %
750 fits_read_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<double>::CONSTANT,
751 const_cast<char *
>(key), &value,
nullptr, &fits.status);
759 readKeyImpl(*
this, key.
c_str(), value);
770 fits_get_hdrspace(
reinterpret_cast<fitsfile *
>(
fptr), &nKeys,
nullptr, &
status);
776 fits_read_keyn(
reinterpret_cast<fitsfile *
>(
fptr), i, key, value, comment, &
status);
780 boost::to_upper(upperKey);
781 if (upperKey.
compare(key) != 0){
783 boost::format(
"In %s, standardizing key '%s' to uppercase '%s' on read.") %
784 BOOST_CURRENT_FUNCTION % key % upperKey);
788 commentStr = comment;
790 while (valueStr.
size() > 2 && valueStr[valueStr.
size() - 2] ==
'&' && i <= nKeys) {
792 fits_read_record(
reinterpret_cast<fitsfile *
>(
fptr), i, key, &
status);
793 if (strncmp(key,
"CONTINUE", 8) != 0) {
800 if (firstQuote == std::string::npos) {
805 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
808 if (lastQuote == std::string::npos) {
813 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
815 valueStr += card.
substr(firstQuote + 1, lastQuote - firstQuote);
817 if (slash != std::string::npos) {
818 commentStr += strip(card.
substr(slash + 1));
825 functor(keyStr, valueStr, commentStr);
833bool isKeyIgnored(
std::string const &key,
bool write =
false) {
834 return ((ignoreKeys.
find(key) != ignoreKeys.
end()) || ignoreKeyStarts.matches(key) ||
835 (write && ignoreKeyStartsWrite.matches(key)));
838class MetadataIterationFunctor :
public HeaderIterationFunctor {
842 template <
typename T>
848 if (list->exists(key) && list->isUndefined(key)) {
850 boost::format(
"In %s, replacing undefined value for key '%s'.") %
851 BOOST_CURRENT_FUNCTION % key);
852 list->set(key, value, comment);
854 list->add(key, value, comment);
857 if (set->exists(key) && set->isUndefined(key)) {
859 boost::format(
"In %s, replacing undefined value for key '%s'.") %
860 BOOST_CURRENT_FUNCTION % key);
861 set->set(key, value);
863 set->add(key, value);
868 void add(std::string
const &key, std::string
const &comment) {
873 if (list->exists(key) && !list->isUndefined(key)) {
877 boost::format(
"In %s, dropping undefined value for key '%s'.") %
878 BOOST_CURRENT_FUNCTION % key);
880 list->add(key,
nullptr, comment);
883 if (set->exists(key) && !set->isUndefined(key)) {
887 boost::format(
"In %s, dropping undefined value for key '%s'.") %
888 BOOST_CURRENT_FUNCTION % key);
890 set->add(key,
nullptr);
896 daf::base::PropertySet *set;
897 daf::base::PropertyList *list;
900void MetadataIterationFunctor::operator()(std::string
const &key, std::string
const &value,
901 std::string
const &comment) {
902 static std::regex
const boolRegex(
"[tTfF]");
903 static std::regex
const intRegex(
"[+-]?[0-9]+");
904 static std::regex
const doubleRegex(
"[+-]?([0-9]*\\.?[0-9]+|[0-9]+\\.?[0-9]*)([eE][+-]?[0-9]+)?");
905 static std::regex
const fitsStringRegex(
"'(.*?) *'");
907 static std::regex
const fitsDefinitionCommentRegex(
908 " *(FITS \\(Flexible Image Transport System\\)|and Astrophysics', volume 376, page 359).*");
909 std::smatch matchStrings;
911 if (strip && isKeyIgnored(key)) {
915 std::istringstream converter(value);
918 add(key,
bool(value ==
"T" || value ==
"t"), comment);
923 if (val < (1LL << 31) && val > -(1LL << 31)) {
924 add(key,
static_cast<int>(val), comment);
926 add(key, val, comment);
932 add(key, val, comment);
934 std::string
const str = matchStrings[1].
str();
935 double val = stringToNonFiniteDouble(str);
937 add(key, val, comment);
939 add(key, str, comment);
941 }
else if (key ==
"HISTORY") {
942 add(key, comment,
"");
943 }
else if (key ==
"COMMENT" && !(strip &&
std::regex_match(comment, fitsDefinitionCommentRegex))) {
944 add(key, comment,
"");
950 add(
"COMMENT", comment,
"");
951 }
else if (
value.empty()) {
954 if (key !=
"COMMENT") {
959 afw::fits::FitsError,
960 (boost::format(
"Could not parse header value for key '%s': '%s'") % key % value).
str());
964void writeKeyFromProperty(Fits &fits, daf::base::PropertySet
const &metadata, std::string
const &key,
965 char const *comment =
nullptr) {
966 std::string upperKey(key);
967 boost::to_upper(upperKey);
968 if (upperKey.compare(key) != 0){
970 boost::format(
"In %s, key '%s' may be standardized to uppercase '%s' on write.") %
971 BOOST_CURRENT_FUNCTION % key % upperKey);
973 std::type_info
const &valueType = metadata.typeOf(key);
976 std::string keyName = key;
977 if (keyName.
size() > 8 && keyName.
rfind(
"HIERARCH ", 0) != 0) {
978 keyName =
"HIERARCH " + keyName;
981 if (valueType ==
typeid(
bool)) {
982 if (metadata.isArray(key)) {
983 std::vector<bool> tmp = metadata.getArray<
bool>(key);
986 writeKeyImpl(fits, keyName.
c_str(),
static_cast<bool>(tmp[i]), comment);
989 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
bool>(key), comment);
991 }
else if (valueType ==
typeid(std::uint8_t)) {
992 if (metadata.isArray(key)) {
993 std::vector<std::uint8_t> tmp = metadata.getArray<std::uint8_t>(key);
995 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
998 writeKeyImpl(fits, keyName.
c_str(), metadata.get<std::uint8_t>(key), comment);
1000 }
else if (valueType ==
typeid(
int)) {
1001 if (metadata.isArray(key)) {
1002 std::vector<int> tmp = metadata.getArray<
int>(key);
1004 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1007 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
int>(key), comment);
1009 }
else if (valueType ==
typeid(
long)) {
1010 if (metadata.isArray(key)) {
1011 std::vector<long> tmp = metadata.getArray<
long>(key);
1013 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1016 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
long>(key), comment);
1018 }
else if (valueType ==
typeid(
long long)) {
1019 if (metadata.isArray(key)) {
1020 std::vector<long long> tmp = metadata.getArray<
long long>(key);
1022 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1025 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
long long>(key), comment);
1027 }
else if (valueType ==
typeid(std::int64_t)) {
1028 if (metadata.isArray(key)) {
1029 std::vector<std::int64_t> tmp = metadata.getArray<std::int64_t>(key);
1031 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1034 writeKeyImpl(fits, keyName.
c_str(), metadata.get<std::int64_t>(key), comment);
1036 }
else if (valueType ==
typeid(
double)) {
1037 if (metadata.isArray(key)) {
1038 std::vector<double> tmp = metadata.getArray<
double>(key);
1040 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1043 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
double>(key), comment);
1045 }
else if (valueType ==
typeid(std::string)) {
1046 if (metadata.isArray(key)) {
1047 std::vector<std::string> tmp = metadata.getArray<std::string>(key);
1049 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1052 writeKeyImpl(fits, keyName.
c_str(), metadata.get<std::string>(key), comment);
1055 if (metadata.isArray(key)) {
1059 writeKeyImpl(fits, keyName.
c_str(), comment);
1062 writeKeyImpl(fits, keyName.
c_str(), comment);
1066 LOGLS_WARN(
"lsst.afw.fits.writeKeyFromProperty",
1068 boost::format(
"In %s, unknown type '%s' for key '%s'.") %
1069 BOOST_CURRENT_FUNCTION % valueType.
name() % key));
1079 MetadataIterationFunctor f;
1089 NameList paramNames;
1095 for (
auto const ¶mName : paramNames) {
1096 if (!isKeyIgnored(paramName,
true)) {
1098 writeKeyFromProperty(*
this, metadata, paramName, pl->
getComment(paramName).
c_str());
1100 writeKeyFromProperty(*
this, metadata, paramName);
1109 char *ttype =
nullptr;
1110 char *tform =
nullptr;
1111 fits_create_tbl(
reinterpret_cast<fitsfile *
>(
fptr), BINARY_TBL, 0, 0, &ttype, &tform,
nullptr,
nullptr, &
status);
1117template <
typename T>
1120 fits_get_num_cols(
reinterpret_cast<fitsfile *
>(
fptr), &nCols, &
status);
1122 fits_insert_col(
reinterpret_cast<fitsfile *
>(
fptr), nCols + 1,
const_cast<char *
>(ttype.
c_str()),
1130template <
typename T>
1142 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(
fptr), &first, &
status);
1143 fits_insert_rows(
reinterpret_cast<fitsfile *
>(
fptr), first, nRows, &
status);
1152 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(
fptr), &r, &
status);
1159template <
typename T>
1161 fits_write_col(
reinterpret_cast<fitsfile *
>(
fptr), FitsTableType<T>::CONSTANT, col + 1, row + 1, 1,
1162 nElements,
const_cast<T *
>(value), &
status);
1165 nElements % row % col);
1174 char const *tmp = value.c_str();
1175 fits_write_col(
reinterpret_cast<fitsfile *
>(
fptr), TSTRING, col + 1, row + 1, 1, 1,
1176 const_cast<char const **
>(&tmp), &
status);
1182template <
typename T>
1185 fits_read_col(
reinterpret_cast<fitsfile *
>(
fptr), FitsTableType<T>::CONSTANT, col + 1, row + 1, 1,
1186 nElements,
nullptr, value, &anynul, &
status);
1199 char *tmp = &buf.
front();
1200 fits_read_col(
reinterpret_cast<fitsfile *
>(
fptr), TSTRING, col + 1, row + 1, 1, 1,
nullptr, &tmp, &anynul,
1212 fits_get_coltype(
reinterpret_cast<fitsfile *
>(
fptr), col + 1, &typecode, &result, &width, &
status);
1222 fits_read_descript(
reinterpret_cast<fitsfile *
>(
fptr), col + 1, row + 1, &result, &offset, &
status);
1233int get_actual_cfitsio_bitpix(
Fits &
fits) {
1235 fits_get_img_equivtype(
reinterpret_cast<fitsfile *
>(
fits.fptr), &result, &
fits.status);
1237 if (result == cfitsio_bitpix<std::int64_t>) {
1243 fits_read_key(
reinterpret_cast<fitsfile *
>(
fits.fptr), FitsType<std::uint64_t>::CONSTANT,
"BZERO",
1244 &bzero,
nullptr, &tmp_status);
1245 if (tmp_status == 0 && bzero == 9223372036854775808u) {
1246 result = cfitsio_bitpix<std::uint64_t>;
1253template <
typename T>
1255 ndarray::Array<bool, 1, 1>
const& mask) {
1257 auto mm = mask.begin();
1258 for (
auto ii =
image.begin(); ii !=
image.end(); ++ii, ++mm) {
1261 if (*ii > max) max = *ii;
1262 if (*ii < min) min = *ii;
1269constexpr std::uint64_t quantized_range() {
1271 int constexpr N_RESERVED_VALUES = 10;
1272 std::uint64_t range = (
static_cast<std::uint64_t
>(1) << 32) - 1;
1273 range -= N_RESERVED_VALUES;
1278template <
typename T>
1279float measure_range_scaling(ndarray::Array<T const, 1, 1>
const& image,
1280 ndarray::Array<bool, 1, 1>
const& mask) {
1281 auto minMax = calculate_min_max(image, mask);
1282 T const min = minMax.first;
1283 T const max = minMax.second;
1284 if (min == max)
return -1.0;
1285 auto range = quantized_range();
1286 return (max - min) / range;
1289template <
typename T>
1290float measure_stdev_scaling(ndarray::Array<T const, 1, 1>
const& image,
1291 ndarray::Array<bool, 1, 1>
const& mask,
float level) {
1292 std::vector<T> array;
1294 auto mm =
mask.begin();
1295 for (
auto ii =
image.begin(); ii !=
image.end(); ++ii, ++mm) {
1301 auto const q1 = array.
size() / 4;
1302 auto const q2 = array.
size() / 2;
1303 auto const q3 = q1 + q2;
1309 double const lq = array[q1];
1310 double const uq = array[q3];
1311 double const stdev = 0.741 * (uq - lq);
1315template <
typename T>
1316float measure_qlevel(
1317 QuantizationOptions
const & options,
1318 ndarray::Array<T const, 1, 1>
const& image,
1319 ndarray::Array<bool, 1, 1>
const& mask
1321 switch (options.scaling) {
1326 return -measure_range_scaling(image, mask);
1328 return -measure_stdev_scaling(image, mask, options.level);
1330 return options.level;
1332 return -options.level;
1334 throw LSST_EXCEPT(pex::exceptions::LogicError,
"Invalid scaling algorithm.");
1338 switch (algorithm) {
1346 throw LSST_EXCEPT(pex::exceptions::LogicError,
"Invalid compression algorithm.");
1350 switch (algorithm) {
1354 return SUBTRACTIVE_DITHER_1;
1356 return SUBTRACTIVE_DITHER_2;
1358 throw LSST_EXCEPT(pex::exceptions::LogicError,
"Invalid dither algorithm.");
1361class CompressionContext {
1364 template <
typename T>
1367 CompressionOptions
const * options,
1368 afw::image::ImageBase<T>
const& image,
1369 afw::image::Mask<>
const * mask
1370 ) : CompressionContext(fits) {
1375 ndarray::Array<T, 1, 1> image_flat_mutable;
1376 ndarray::Array<T const, 1, 1> image_flat;
1377 ndarray::Array<T const, 2, 2> image_array = ndarray::dynamic_dimension_cast<2>(
image.getArray());
1378 if (image_array.isEmpty()) {
1379 ndarray::Array<T, 2, 2> image_array_copy = ndarray::copy(
image.getArray());
1380 image_flat_mutable = ndarray::flatten<1>(image_array_copy);
1381 image_flat = image_flat_mutable;
1383 image_flat = ndarray::flatten<1>(image_array);
1385 _pixel_data = image_flat.begin();
1386 _pixel_data_mgr = image_flat.getManager();
1387 _n_pixels = image_flat.size();
1390 if constexpr(std::is_floating_point_v<T>) {
1391 if (options->quantization) {
1392 if (mask &&
image.getDimensions() !=
mask->getDimensions()) {
1393 std::ostringstream os;
1394 os <<
"Size mismatch between image and mask: ";
1395 os <<
image.getWidth() <<
"x" <<
image.getHeight();
1397 os <<
mask->getWidth() <<
"x" <<
mask->getHeight();
1398 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, os.
str());
1400 ndarray::Array<bool, 2, 2> mask_array = ndarray::allocate(image_array.getShape());
1401 ndarray::Array<bool, 1, 1> mask_flat = ndarray::flatten<1>(mask_array);
1402 if (mask && options->uses_mask()) {
1403 mask_array.deep() = (
1404 mask->getArray() &
mask->getPlaneBitMask(options->quantization.value().mask_planes)
1408 image_flat.begin(), image_flat.end(), mask_flat.begin(), mask_flat.begin(),
1409 [](T
const & img,
bool const & msk) { return msk || !std::isfinite(img); }
1411 qlevel = measure_qlevel(options->quantization.value(), image_flat, mask_flat);
1414 if (image_flat_mutable.isEmpty()) {
1416 image_flat_mutable = ndarray::copy(image_flat);
1419 image_flat_mutable.begin(), image_flat_mutable.end(), image_flat_mutable.begin(),
1420 [](T
const & value) {
1421 return (std::isnan(value)) ? -std::numeric_limits<T>::infinity() : value;
1424 _pixel_data = image_flat_mutable.begin();
1425 _pixel_data_mgr = image_flat_mutable.getManager();
1428 _apply(*options, qlevel, std::is_floating_point_v<T>,
image.getDimensions());
1432 template <
typename T>
1433 T const * get_pixel_data()
const {
return static_cast<T const*
>(_pixel_data); }
1435 std::size_t get_n_pixels()
const {
return _n_pixels; }
1438 CompressionContext(CompressionContext
const&) =
delete;
1439 CompressionContext& operator=(CompressionContext
const&) =
delete;
1441 ~CompressionContext(){
1442 auto fptr =
reinterpret_cast<fitsfile *
>(_fits->
fptr);
1443 fits_set_compression_type(fptr, _algorithm, &_fits->
status);
1444 fits_set_tile_dim(fptr, _tile_dim.size(), _tile_dim.data(), &_fits->
status);
1445 fits_set_quantize_level(fptr, _qlevel, &_fits->
status);
1446 fits_set_dither_seed(fptr, _dither_seed, &_fits->
status);
1451 explicit CompressionContext(Fits & fits)
1452 : _fits(&fits), _algorithm(0), _dither_seed(0), _qlevel(0.0), _n_pixels(0),
1453 _pixel_data(nullptr), _pixel_data_mgr()
1455 auto fptr =
reinterpret_cast<fitsfile *
>(fits.fptr);
1456 fits_get_compression_type(fptr, &_algorithm, &fits.status);
1457 fits_get_tile_dim(fptr, _tile_dim.size(), _tile_dim.data(), &fits.status);
1458 fits_get_quantize_level(fptr, &_qlevel, &fits.status);
1463 fits_get_dither_seed(fptr, &_dither_seed, &fits.status);
1467 CompressionOptions options,
1472 if (is_float && !options.quantization && options.algorithm == CompressionAlgorithm::RICE_1_) {
1474 pex::exceptions::InvalidParameterError,
1475 "RICE_1 compression of floating point images requires quantization."
1478 if (is_float && qlevel == 0.0 && options.algorithm == CompressionAlgorithm::RICE_1_) {
1485 options = CompressionOptions();
1487 if (!is_float && options.quantization) {
1489 pex::exceptions::InvalidParameterError,
1490 "Quantization cannot be used on integer images."
1493 auto fptr =
reinterpret_cast<fitsfile *
>(_fits->
fptr);
1495 fits_set_compression_type(fptr, 0, &_fits->
status);
1498 fits_set_compression_type(fptr, compression_algorithm_to_cfitsio(options.algorithm), &_fits->
status);
1501 std::array<long, 2> tile_dim = {
1502 (options.tile_width == 0) ?
dimensions.getX() :
static_cast<long>(options.tile_width),
1503 (options.tile_height == 0) ?
dimensions.getY() :
static_cast<long>(options.tile_height)
1505 fits_set_tile_dim(fptr, tile_dim.
size(), tile_dim.
data(), &_fits->
status);
1506 if (options.quantization) {
1507 auto q = options.quantization.value();
1508 fits_set_quantize_method(fptr, dither_algorithm_to_cfitsio(q.dither), &_fits->
status);
1509 fits_set_dither_seed(fptr, q.seed, &_fits->
status);
1510 fits_set_quantize_level(fptr, qlevel, &_fits->
status);
1512 fits_set_quantize_level(fptr, 0.0, &_fits->
status);
1523 std::array<long, 2> _tile_dim;
1528 void const * _pixel_data;
1529 ndarray::Manager::Ptr _pixel_data_mgr;
1536 fits_create_img(
reinterpret_cast<fitsfile *
>(
fptr), 8, 0, &naxes, &
status);
1542void Fits::createImageImpl(
int bitpix,
int naxis,
long const *naxes) {
1543 fits_create_img(
reinterpret_cast<fitsfile *
>(fptr), bitpix, naxis,
const_cast<long *
>(naxes), &status);
1544 if (behavior & AUTO_CHECK) {
1549template <
typename T>
1550void Fits::writeImageImpl(T
const *data,
int nElements, std::optional<T> explicit_null) {
1551 if (explicit_null) {
1552 fits_write_imgnull(
reinterpret_cast<fitsfile *
>(fptr), FitsType<T>::CONSTANT, 1, nElements,
1553 const_cast<T *
>(data),
const_cast<T*
>(&explicit_null.value()), &status);
1555 fits_write_img(
reinterpret_cast<fitsfile *
>(fptr), FitsType<T>::CONSTANT, 1, nElements,
1556 const_cast<T *
>(data), &status);
1558 if (behavior & AUTO_CHECK) {
1563template <
typename T>
1568 CompressionContext context(*
this, compression,
image, mask);
1574 ndarray::Vector<long, 2> dims(
image.getArray().getShape().reverse());
1575 createImageImpl(cfitsio_bitpix<T>, 2, dims.elems);
1582 fullMetadata->combine(*wcsMetadata);
1584 fullMetadata = wcsMetadata;
1587 std::optional<T> explicit_null = std::nullopt;
1588 if constexpr(std::is_floating_point_v<T>) {
1594 writeImageImpl(context.get_pixel_data<T>(), context.get_n_pixels(), explicit_null);
1601template <
typename T,
class Enable =
void>
1603 static T
constexpr value = 0;
1607template <
typename T>
1608struct NullValue<T, typename
std::enable_if<std::numeric_limits<T>::has_quiet_NaN>::type> {
1614template <
typename T>
1615void Fits::readImageImpl(
int nAxis, T *data,
long *begin,
long *end,
long *increment) {
1616 fitsfile * fits =
reinterpret_cast<fitsfile *
>(fptr);
1617 int is_compressed = fits_is_compressed_image(fits, &status);
1620 is_compressed && fits->Fptr->quantize_level == 9999
1621 && (fits->Fptr->cn_zscale != 0 || fits->Fptr->cn_zzero != 0)
1630 fits->Fptr->quantize_level = 0;
1632 T null = NullValue<T>::value;
1634 fits_read_subset(fits, FitsType<T>::CONSTANT, begin, end, increment,
1635 reinterpret_cast<void *
>(&null), data, &anyNulls, &status);
1641 fits_get_img_dim(
reinterpret_cast<fitsfile *
>(
fptr), &nAxis, &
status);
1646void Fits::getImageShapeImpl(
int maxDim,
long *nAxes) {
1647 fits_get_img_size(
reinterpret_cast<fitsfile *
>(fptr), maxDim, nAxes, &status);
1651template <
typename T>
1653 int imageType = get_actual_cfitsio_bitpix(*
this);
1655 if (imageType < 0) {
1658 bool is_compressed = fits_is_compressed_image(
reinterpret_cast<fitsfile*
>(
fptr), &
status);
1660 if (is_compressed &&
sizeof(T) == 8) {
1666 if (isFitsImageTypeSigned(imageType)) {
1667 return cfitsio_bitpix<T> >= imageType;
1670 return cfitsio_bitpix<T> > imageType;
1673 if (!isFitsImageTypeSigned(imageType)) {
1674 return cfitsio_bitpix<T> >= imageType;
1685 int bitpix = get_actual_cfitsio_bitpix(*
this);
1690 case BYTE_IMG:
return "uint8";
1691 case SBYTE_IMG:
return "int8";
1692 case SHORT_IMG:
return "int16";
1693 case USHORT_IMG:
return "uint16";
1694 case LONG_IMG:
return "int32";
1695 case ULONG_IMG:
return "uint32";
1696 case LONGLONG_IMG:
return "int64";
1697 case ULONGLONG_IMG:
return "uint64";
1701 (boost::format(
"Unrecognized BITPIX value: %d") % bitpix).str()
1709 if (mode ==
"r" || mode ==
"rb") {
1710 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READONLY,
1712 }
else if (mode ==
"w" || mode ==
"wb") {
1713 std::filesystem::remove(filename);
1714 fits_create_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), &
status);
1715 }
else if (mode ==
"a" || mode ==
"ab") {
1716 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READWRITE,
1719 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1720 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1725 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1730 (boost::format(
"Invalid mode '%s' given when opening file '%s'") % mode % filename).str());
1739 using Reallocator =
void *(*)(
void *,
std::size_t);
1742 if (mode ==
"r" || mode ==
"rb") {
1743 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READONLY, &manager._ptr,
1744 &manager._len, 0,
nullptr,
1746 }
else if (mode ==
"w" || mode ==
"wb") {
1747 Reallocator reallocator =
nullptr;
1749 fits_create_memfile(
reinterpret_cast<fitsfile **
>(&
fptr), &manager._ptr, &manager._len, 0,
1752 }
else if (mode ==
"a" || mode ==
"ab") {
1753 Reallocator reallocator =
nullptr;
1755 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READWRITE, &manager._ptr,
1756 &manager._len, 0, reallocator, &
status);
1758 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1759 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1764 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1768 (boost::format(
"Invalid mode '%s' given when opening memory file at '%s'") % mode %
1774 *
this, boost::format(
"Opening memory file at '%s' with mode '%s'") % manager._ptr % mode);
1779 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &
status);
1787 bool const asScalar =
true;
1788 for (
auto const &name : first.getOrderedNames()) {
1789 auto const iscv = isCommentIsValid(first, name);
1790 if (iscv.isComment) {
1795 combined->copy(name, first, name, asScalar);
1798 for (
auto const &name : second.getOrderedNames()) {
1799 auto const iscv = isCommentIsValid(second, name);
1800 if (iscv.isComment) {
1806 combined->copy(name, second, name, asScalar);
1815template <
typename T,
typename... Args>
1846 int oldHdu = fitsfile.
getHdu();
1847 if (oldHdu != 0 && metadata->exists(
"INHERIT")) {
1848 bool inherit =
false;
1852 }
else if (metadata->typeOf(
"INHERIT") ==
typeid(
std::string)) {
1853 inherit = (metadata->get<
std::string>(
"INHERIT") ==
"T");
1855 inherit = metadata->get<
bool>(
"INHERIT");
1857 if (strip) metadata->remove(
"INHERIT");
1878 _oldHdu(_fits.getHdu()),
1881 _fits.setHdu(hdu, relative);
1891 _fits.setHdu(_oldHdu);
1903 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1918 bool isCompressed = fits_is_compressed_image(
fits, &
status);
1925 return isCompressed;
1928#define INSTANTIATE_KEY_OPS(r, data, T) \
1929 template void Fits::updateKey(std::string const &, T const &, std::string const &); \
1930 template void Fits::writeKey(std::string const &, T const &, std::string const &); \
1931 template void Fits::updateKey(std::string const &, T const &); \
1932 template void Fits::writeKey(std::string const &, T const &); \
1933 template void Fits::updateColumnKey(std::string const &, int, T const &, std::string const &); \
1934 template void Fits::writeColumnKey(std::string const &, int, T const &, std::string const &); \
1935 template void Fits::updateColumnKey(std::string const &, int, T const &); \
1936 template void Fits::writeColumnKey(std::string const &, int, T const &); \
1937 template void Fits::readKey(std::string const &, T &);
1939#define INSTANTIATE_IMAGE_OPS(r, data, T) \
1940 template void Fits::writeImageImpl(T const *, int, std::optional<T>); \
1941 template void Fits::writeImage(image::ImageBase<T> const &, CompressionOptions const *, \
1942 daf::base::PropertySet const *, \
1943 image::Mask<image::MaskPixel> const *); \
1944 template void Fits::readImageImpl(int, T *, long *, long *, long *); \
1945 template bool Fits::checkImageType<T>(); \
1946 template int getBitPix<T>();
1948#define INSTANTIATE_TABLE_OPS(r, data, T) \
1949 template int Fits::addColumn<T>(std::string const &ttype, int size); \
1950 template int Fits::addColumn<T>(std::string const &ttype, int size, std::string const &comment);
1951#define INSTANTIATE_TABLE_ARRAY_OPS(r, data, T) \
1952 template void Fits::writeTableArray(std::size_t row, int col, int nElements, T const *value); \
1953 template void Fits::readTableArray(std::size_t row, int col, int nElements, T *value);
1960 (bool)(unsigned char)(short)(unsigned short)(int)(unsigned int)(long)(unsigned long)(LONGLONG)( \
1961 float)(double)(std::complex<float>)(std::complex<double>)(std::string)
1963#define COLUMN_TYPES \
1964 (bool)(std::string)(std::int8_t)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t) \
1965 (std::int64_t)(float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1967#define COLUMN_ARRAY_TYPES \
1968 (bool)(char)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t)(std::int64_t)( \
1969 float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1971#define IMAGE_TYPES \
1972 (unsigned char)(short)(unsigned short)(int)(unsigned int)(std::int64_t)(std::uint64_t)(float)(double)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
LSST DM logging module built on log4cxx.
#define LOGLS_WARN(logger, message)
Log a warn-level message using an iostream-based interface.
#define LOGL_WARN(logger, message...)
Log a warn-level message using a varargs/printf style interface.
#define LOGLS_DEBUG(logger, message)
Log a debug-level message using an iostream-based interface.
An exception thrown when problems are found when reading or writing FITS files.
A simple struct that combines the two arguments that must be passed to most cfitsio routines and cont...
void writeKey(std::string const &key, T const &value, std::string const &comment)
Add a FITS header key to the bottom of the header.
void writeColumnKey(std::string const &prefix, int n, T const &value, std::string const &comment)
Write a key of the form XXXXXnnn, where XXXXX is the prefix and nnn is a column number.
void closeFile()
Close a FITS file.
void writeImage(ndarray::Array< T const, N, C > const &array)
Write an ndarray::Array to a FITS image HDU.
int getImageDim()
Return the number of dimensions in the current HDU.
std::size_t countRows()
Return the number of row in a table.
void createEmpty()
Create an empty image HDU with NAXIS=0 at the end of the file.
void readTableArray(std::size_t row, int col, int nElements, T *value)
Read an array value from a binary table.
void updateColumnKey(std::string const &prefix, int n, T const &value, std::string const &comment)
Update a key of the form XXXXXnnn, where XXXXX is the prefix and nnn is a column number.
void setHdu(int hdu, bool relative=false)
Set the current HDU.
void createTable()
Create a new binary table extension.
Fits()
Default constructor; set all data members to 0.
void readTableScalar(std::size_t row, int col, T &value)
Read an array scalar from a binary table.
bool checkCompressedImagePhu()
Go to the first image header in the FITS file.
int countHdus()
Return the number of HDUs in the file.
void writeTableScalar(std::size_t row, int col, T value)
Write a scalar value to a binary table.
void forEachKey(HeaderIterationFunctor &functor)
Call a polymorphic functor for every key in the header.
std::string getFileName() const
Return the file name associated with the FITS object or "<unknown>" if there is none.
int addColumn(std::string const &ttype, int size, std::string const &comment)
Add a column to a table.
void updateKey(std::string const &key, T const &value, std::string const &comment)
Set a FITS header key, editing if it already exists and appending it if not.
void readMetadata(daf::base::PropertySet &metadata, bool strip=false)
Read a FITS header into a PropertySet or PropertyList.
void writeTableArray(std::size_t row, int col, int nElements, T const *value)
Write an array value to a binary table.
void readKey(std::string const &key, T &value)
Read a FITS header key into the given reference.
std::string getImageDType()
Return the numpy dtype equivalent of the image pixel type (e.g.
int getHdu()
Return the current HDU (0-indexed; 0 is the Primary HDU).
void writeMetadata(daf::base::PropertySet const &metadata)
Read a FITS header into a PropertySet or PropertyList.
long getTableArraySize(int col)
Return the size of an array column.
std::size_t addRows(std::size_t nRows)
Append rows to a table, and return the index of the first new row.
bool checkImageType()
Return true if the current HDU is compatible with the given pixel type.
RAII scoped guard for moving the HDU in a Fits object.
Lifetime-management for memory that goes into FITS memory files.
void reset()
Return the manager to the same state it would be if default-constructed.
The base class for all image classed (Image, Mask, MaskedImage, ...)
Represent a 2-dimensional array of bitmask pixels.
Class for storing ordered metadata with comments.
std::string const & getComment(std::string const &name) const
Get the comment for a string property name (possibly hierarchical).
std::vector< std::string > getOrderedNames() const
Get the list of property names, in the order they were added.
Class for storing generic metadata.
virtual std::shared_ptr< PropertySet > deepCopy() const
Make a deep copy of the PropertySet and all of its contents.
std::vector< std::string > paramNames(bool topLevelOnly=true) const
A variant of names that excludes the names of subproperties.
T find_first_not_of(T... args)
T find_last_not_of(T... args)
#define INSTANTIATE_TABLE_OPS(r, data, T)
#define INSTANTIATE_IMAGE_OPS(r, data, T)
#define COLUMN_ARRAY_TYPES
#define INSTANTIATE_KEY_OPS(r, data, T)
#define INSTANTIATE_TABLE_ARRAY_OPS(r, data, T)
#define LSST_FITS_CHECK_STATUS(fitsObj,...)
Throw a FitsError exception if the status of the given Fits object is nonzero.
dafPlistPtr _readMetadata(T &&fitsparm, bool strip, Args... args)
std::shared_ptr< daf::base::PropertyList > combineMetadata(daf::base::PropertyList const &first, daf::base::PropertyList const &second)
Combine two sets of metadata in a FITS-appropriate fashion.
const int DEFAULT_HDU
Specify that the default HDU should be read.
std::shared_ptr< daf::base::PropertyList > readMetadata(std::string const &fileName, int hdu=DEFAULT_HDU, bool strip=false)
Read FITS header.
@ RANGE
Scale to preserve dynamic range with bad pixels msasked out.
@ STDEV_MASKED
Scale based on the standard deviation with bad pixels masked out.
@ STDEV_CFITSIO
Let CFITSIO work out the scaling (per-tile; does not respect mask planes)
@ MANUAL
Scale set manually.
CompressionAlgorithm
FITS compression algorithms.
int getBitPix()
Return the cfitsio integer BITPIX code for the given data type.
std::string makeErrorMessage(std::string const &fileName="", int status=0, std::string const &msg="")
Return an error message reflecting FITS I/O errors.
HduType
an enum representing the various types of FITS HDU that are available in cfitsio library
std::string makeLimitedFitsHeader(lsst::daf::base::PropertySet const &metadata, std::set< std::string > const &excludeNames={})
Format a PropertySet into an FITS header string in a simplistic fashion.
std::shared_ptr< daf::base::PropertyList > dafPlistPtr
DitherAlgorithm
FITS quantization algorithms.
std::string const wcsNameForXY0
Extent< int, 2 > Extent2I
decltype(nullptr) nullptr_t
decltype(sizeof(void *)) size_t
Options controlling image compression with FITS.
std::optional< QuantizationOptions > quantization
Options for quantizing a floating point image (i.e. lossy compression).
py::scoped_interpreter guard