8#include <unordered_set>
9#include <unordered_map>
19#include "boost/algorithm/string.hpp"
20#include "boost/preprocessor/seq/for_each.hpp"
21#include "boost/format.hpp"
50 daf::base::PropertySet
const &metadata) {
52 for (
auto const &fullName : paramNames) {
54 auto name = (lastPeriod == std::string::npos) ? fullName : fullName.substr(lastPeriod + 1);
59 if (name.size() > 8) {
62 out = (boost::format(
"%-8s= ") %
name).str();
64 if (type ==
typeid(
bool)) {
65 out += metadata.get<
bool>(
name) ?
"T" :
"F";
67 out += (boost::format(
"%20d") %
static_cast<int>(metadata.get<
std::uint8_t>(
name))).str();
68 }
else if (type ==
typeid(
int)) {
69 out += (boost::format(
"%20d") % metadata.get<
int>(
name)).str();
70 }
else if (type ==
typeid(
double)) {
71 double value = metadata.get<
double>(
name);
74 out += (boost::format(
"%#20.17G") % value).str();
77 boost::format(
"In %s, found NaN in metadata item '%s'") %
78 BOOST_CURRENT_FUNCTION % name);
82 }
else if (type ==
typeid(
float)) {
83 float value = metadata.get<
float>(
name);
85 out += (boost::format(
"%#20.15G") % value).str();
88 boost::format(
"In %s, found NaN in metadata item '%s'") %
89 BOOST_CURRENT_FUNCTION % name);
97 if (out.
size() > 80) {
102 int const len = out.
size();
105 }
else if (len > 80) {
108 "Formatted data too long: " +
std::to_string(len) +
" > 80: \"" + out +
"\"");
125class StringStartSet {
129 for (
auto const &word : input) {
131 if (size < _minSize) {
135 for (
auto const &word : input) {
137 assert(_words.
count(start) == 0);
138 _words[start] = word;
144 auto const iter = _words.
find(startString(key));
145 if (iter == _words.
end()) {
170 "SIMPLE",
"BITPIX",
"NAXIS",
"EXTEND",
"GCOUNT",
"PCOUNT",
"XTENSION",
"TFIELDS",
"BSCALE",
"BZERO",
172 "ZBITPIX",
"ZIMAGE",
"ZCMPTYPE",
"ZSIMPLE",
"ZEXTEND",
"ZBLANK",
"ZDATASUM",
"ZHECKSUM",
"ZQUANTIZ",
174 "DATASUM",
"CHECKSUM"};
181StringStartSet
const ignoreKeyStarts{
182 "NAXIS",
"TZERO",
"TSCAL",
184 "ZNAXIS",
"ZTILE",
"ZNAME",
"ZVAL"};
191StringStartSet
const ignoreKeyStartsWrite{
"TFORM",
"TTYPE"};
195 if (s.empty())
return s;
197 if (i1 == std::string::npos) {
202 return s.substr(i1, 1 + i2 - i1);
207char getFormatCode(
bool *) {
return 'X'; }
216char getFormatCode(
float *) {
return 'E'; }
217char getFormatCode(
double *) {
return 'D'; }
227 return (boost::format(
"%d%c") % size % getFormatCode((T *)
nullptr)).str();
228 }
else if (size < 0) {
230 return (boost::format(
"1Q%c(%d)") % getFormatCode((T *)
nullptr) % (-size)).str();
233 return (boost::format(
"1Q%c") % getFormatCode((T *)
nullptr)).str();
243struct FitsType<bool> {
244 static int const CONSTANT = TLOGICAL;
247struct FitsType<char> {
248 static int const CONSTANT = TSTRING;
251struct FitsType<signed char> {
252 static int const CONSTANT = TSBYTE;
255struct FitsType<unsigned char> {
256 static int const CONSTANT = TBYTE;
259struct FitsType<short> {
260 static int const CONSTANT = TSHORT;
263struct FitsType<unsigned short> {
264 static int const CONSTANT = TUSHORT;
267struct FitsType<
int> {
268 static int const CONSTANT = TINT;
271struct FitsType<unsigned
int> {
272 static int const CONSTANT = TUINT;
275struct FitsType<long> {
276 static int const CONSTANT = TLONG;
279struct FitsType<unsigned long> {
280 static int const CONSTANT = TULONG;
283struct FitsType<long long> {
284 static int const CONSTANT = TLONGLONG;
287struct FitsType<unsigned long long> {
288 static int const CONSTANT = TLONGLONG;
291struct FitsType<
float> {
292 static int const CONSTANT = TFLOAT;
295struct FitsType<double> {
296 static int const CONSTANT = TDOUBLE;
300 static int const CONSTANT = TDOUBLE;
303struct FitsType<
std::complex<float> > {
304 static int const CONSTANT = TCOMPLEX;
307struct FitsType<
std::complex<double> > {
308 static int const CONSTANT = TDBLCOMPLEX;
313struct FitsTableType :
public FitsType<T> {};
315struct FitsTableType<bool> {
316 static int const CONSTANT = TBIT;
323struct FitsBitPix<unsigned char> {
324 static int const CONSTANT = BYTE_IMG;
327struct FitsBitPix<short> {
328 static int const CONSTANT = SHORT_IMG;
331struct FitsBitPix<unsigned short> {
332 static int const CONSTANT = USHORT_IMG;
335struct FitsBitPix<
int> {
336 static int const CONSTANT = LONG_IMG;
339struct FitsBitPix<unsigned
int> {
340 static int const CONSTANT = ULONG_IMG;
343struct FitsBitPix<
std::int64_t> {
344 static int const CONSTANT = LONGLONG_IMG;
347struct FitsBitPix<
std::uint64_t> {
348 static int const CONSTANT = LONGLONG_IMG;
351struct FitsBitPix<
float> {
352 static int const CONSTANT = FLOAT_IMG;
355struct FitsBitPix<double> {
356 static int const CONSTANT = DOUBLE_IMG;
359bool isFitsImageTypeSigned(
int constant) {
361 case BYTE_IMG:
return false;
362 case SHORT_IMG:
return true;
363 case USHORT_IMG:
return false;
364 case LONG_IMG:
return true;
365 case ULONG_IMG:
return false;
366 case LONGLONG_IMG:
return true;
367 case FLOAT_IMG:
return true;
368 case DOUBLE_IMG:
return true;
370 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
"Invalid constant.");
373static bool allowImageCompression =
true;
375int fitsTypeForBitpix(
int bitpix) {
391 os <<
"Invalid bitpix value: " << bitpix;
392 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, os.str());
414ItemInfo isCommentIsValid(daf::base::PropertyList
const &pl,
std::string const &name) {
415 if (!pl.exists(name)) {
416 return ItemInfo(
false,
false);
419 if ((name ==
"COMMENT") || (name ==
"HISTORY")) {
420 return ItemInfo(
true, type ==
typeid(
std::string));
422 return ItemInfo(
false,
true);
433 os <<
"cfitsio error";
434 if (fileName !=
"") {
435 os <<
" (" << fileName <<
")";
438 char fitsErrMsg[FLEN_ERRMSG];
439 fits_get_errstatus(status, fitsErrMsg);
440 os <<
": " << fitsErrMsg <<
" (" << status <<
")";
445 os <<
"\ncfitsio error stack:\n";
446 char cfitsioMsg[FLEN_ERRMSG];
449 while (fits_read_errmsg(cfitsioMsg) != 0) {
450 cfitsioMsg[FLEN_ERRMSG-1] = char(0);
453 if( !isprint(cfitsioMsg[i]) ) cfitsioMsg[i] =
'.';
454 os <<
" " << cfitsioMsg <<
"\n";
461 fitsfile *fd =
reinterpret_cast<fitsfile *
>(fptr);
462 if (fd !=
nullptr && fd->Fptr !=
nullptr && fd->Fptr->filename !=
nullptr) {
463 fileName = fd->Fptr->filename;
478 for (
auto const &name : allParamNames) {
479 if (excludeNames.
count(name) == 0) {
483 return makeLimitedFitsHeaderImpl(desiredParamNames, metadata);
502 return FitsBitPix<T>::CONSTANT;
511 fitsfile *fd =
reinterpret_cast<fitsfile *
>(
fptr);
512 if (fd !=
nullptr && fd->Fptr !=
nullptr && fd->Fptr->filename !=
nullptr) {
513 fileName = fd->Fptr->filename;
520 fits_get_hdu_num(
reinterpret_cast<fitsfile *
>(
fptr), &n);
526 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu,
nullptr, &
status);
532 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu + 1,
nullptr, &
status);
537 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), 1,
nullptr, &tmpStatus);
546 fits_movnam_hdu(
reinterpret_cast<fitsfile *
>(
fptr),
static_cast<int>(hdutype),
547 const_cast<char *
>(name.c_str()), hduver, &
status);
550 static_cast<int>(hdutype) % hduver);
555 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &n, &
status);
592double stringToNonFiniteDouble(
std::string const &value) {
593 if (value ==
"NAN") {
596 if (value ==
"+INFINITY") {
599 if (value ==
"-INFINITY") {
606void updateKeyImpl(Fits &fits,
char const *key, T
const &value,
char const *comment) {
607 fits_update_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
608 const_cast<T *
>(&value),
const_cast<char *
>(comment), &fits.status);
611void updateKeyImpl(Fits &fits,
char const *key,
std::string const &value,
char const *comment) {
612 fits_update_key_longstr(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(key),
613 const_cast<char *
>(value.c_str()),
const_cast<char *
>(comment), &fits.status);
616void updateKeyImpl(Fits &fits,
char const *key,
bool const &value,
char const *comment) {
618 fits_update_key(
reinterpret_cast<fitsfile *
>(fits.fptr), TLOGICAL,
const_cast<char *
>(key), &v,
619 const_cast<char *
>(comment), &fits.status);
622void updateKeyImpl(Fits &fits,
char const *key,
double const &value,
char const *comment) {
623 std::string strValue = nonFiniteDoubleToString(value);
624 if (!strValue.
empty()) {
625 updateKeyImpl(fits, key, strValue, comment);
627 fits_update_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<double>::CONSTANT,
628 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
634void writeKeyImpl(Fits &fits,
char const *key, T
const &value,
char const *comment) {
635 fits_write_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
636 const_cast<T *
>(&value),
const_cast<char *
>(comment), &fits.status);
639void writeKeyImpl(Fits &fits,
char const *key,
char const *comment) {
641 fits_write_key_null(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(key),
642 const_cast<char *
>(comment), &fits.status);
645void writeKeyImpl(Fits &fits,
char const *key,
std::string const &value,
char const *comment) {
646 if (
strncmp(key,
"COMMENT", 7) == 0) {
647 fits_write_comment(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(value.c_str()),
649 }
else if (
strncmp(key,
"HISTORY", 7) == 0) {
650 fits_write_history(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(value.c_str()),
653 fits_write_key_longstr(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(key),
654 const_cast<char *
>(value.c_str()),
const_cast<char *
>(comment), &fits.status);
658void writeKeyImpl(Fits &fits,
char const *key,
bool const &value,
char const *comment) {
660 fits_write_key(
reinterpret_cast<fitsfile *
>(fits.fptr), TLOGICAL,
const_cast<char *
>(key), &v,
661 const_cast<char *
>(comment), &fits.status);
664void writeKeyImpl(Fits &fits,
char const *key,
double const &value,
char const *comment) {
665 std::string strValue = nonFiniteDoubleToString(value);
666 if (!strValue.
empty()) {
667 writeKeyImpl(fits, key, strValue, comment);
669 fits_write_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<double>::CONSTANT,
670 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
679 updateKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
687 writeKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
695 updateKeyImpl(*
this, key.
c_str(), value,
nullptr);
703 writeKeyImpl(*
this, key.
c_str(), value,
nullptr);
711 updateKey((boost::format(
"%s%d") %
prefix % (n + 1)).str(), value, comment);
719 writeKey((boost::format(
"%s%d") %
prefix % (n + 1)).str(), value, comment);
735 writeKey((boost::format(
"%s%d") %
prefix % (n + 1)).str(), value);
746void readKeyImpl(
Fits &fits,
char const *key, T &value) {
747 fits_read_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
748 &value,
nullptr, &fits.status);
751void readKeyImpl(Fits &fits,
char const *key,
std::string &value) {
753 fits_read_key_longstr(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(key), &buf,
nullptr,
761void readKeyImpl(Fits &fits,
char const *key,
double &value) {
765 char buf[FLEN_VALUE];
766 fits_read_keyword(
reinterpret_cast<fitsfile *
>(fits.fptr),
const_cast<char *
>(key), buf,
nullptr, &fits.status);
767 if (fits.status != 0) {
772 readKeyImpl(fits, key, unquoted);
773 if (fits.status != 0) {
776 value = stringToNonFiniteDouble(unquoted);
779 afw::fits::FitsError,
780 (boost::format(
"Unrecognised string value for keyword '%s' when parsing as double: %s") %
785 fits_read_key(
reinterpret_cast<fitsfile *
>(fits.fptr), FitsType<double>::CONSTANT,
786 const_cast<char *
>(key), &value,
nullptr, &fits.status);
794 readKeyImpl(*
this, key.
c_str(), value);
805 fits_get_hdrspace(
reinterpret_cast<fitsfile *
>(
fptr), &nKeys,
nullptr, &
status);
811 fits_read_keyn(
reinterpret_cast<fitsfile *
>(
fptr), i, key, value, comment, &
status);
815 boost::to_upper(upperKey);
816 if (upperKey.
compare(key) != 0){
818 boost::format(
"In %s, standardizing key '%s' to uppercase '%s' on read.") %
819 BOOST_CURRENT_FUNCTION % key % upperKey);
823 commentStr = comment;
825 while (valueStr.
size() > 2 && valueStr[valueStr.
size() - 2] ==
'&' && i <= nKeys) {
827 fits_read_record(
reinterpret_cast<fitsfile *
>(
fptr), i, key, &
status);
828 if (strncmp(key,
"CONTINUE", 8) != 0) {
835 if (firstQuote == std::string::npos) {
840 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
843 if (lastQuote == std::string::npos) {
848 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
850 valueStr += card.
substr(firstQuote + 1, lastQuote - firstQuote);
852 if (slash != std::string::npos) {
860 functor(keyStr, valueStr, commentStr);
868bool isKeyIgnored(
std::string const &key,
bool write =
false) {
869 return ((ignoreKeys.
find(key) != ignoreKeys.
end()) || ignoreKeyStarts.matches(key) ||
870 (write && ignoreKeyStartsWrite.matches(key)));
873class MetadataIterationFunctor :
public HeaderIterationFunctor {
877 template <
typename T>
883 if (list->exists(key) && list->isUndefined(key)) {
885 boost::format(
"In %s, replacing undefined value for key '%s'.") %
886 BOOST_CURRENT_FUNCTION % key);
887 list->set(key, value, comment);
889 list->add(key, value, comment);
892 if (set->exists(key) && set->isUndefined(key)) {
894 boost::format(
"In %s, replacing undefined value for key '%s'.") %
895 BOOST_CURRENT_FUNCTION % key);
896 set->set(key, value);
898 set->add(key, value);
908 if (list->exists(key) && !list->isUndefined(key)) {
912 boost::format(
"In %s, dropping undefined value for key '%s'.") %
913 BOOST_CURRENT_FUNCTION % key);
915 list->add(key,
nullptr, comment);
918 if (set->exists(key) && !set->isUndefined(key)) {
922 boost::format(
"In %s, dropping undefined value for key '%s'.") %
923 BOOST_CURRENT_FUNCTION % key);
925 set->add(key,
nullptr);
931 daf::base::PropertySet *set;
932 daf::base::PropertyList *list;
938 static std::regex const intRegex(
"[+-]?[0-9]+");
939 static std::regex const doubleRegex(
"[+-]?([0-9]*\\.?[0-9]+|[0-9]+\\.?[0-9]*)([eE][+-]?[0-9]+)?");
940 static std::regex const fitsStringRegex(
"'(.*?) *'");
942 static std::regex const fitsDefinitionCommentRegex(
943 " *(FITS \\(Flexible Image Transport System\\)|and Astrophysics', volume 376, page 359).*");
946 if (
strip && isKeyIgnored(key)) {
953 add(key,
bool(value ==
"T" || value ==
"t"), comment);
958 if (
val < (1LL << 31) &&
val > -(1LL << 31)) {
959 add(key,
static_cast<int>(
val), comment);
970 double val = stringToNonFiniteDouble(str);
974 add(key, str, comment);
976 }
else if (key ==
"HISTORY") {
977 add(key, comment,
"");
979 add(key, comment,
"");
980 }
else if (key.
empty() && value.empty()) {
985 add(
"COMMENT", comment,
"");
986 }
else if (value.empty()) {
989 if (key !=
"COMMENT") {
994 afw::fits::FitsError,
995 (boost::format(
"Could not parse header value for key '%s': '%s'") % key % value).str());
999void writeKeyFromProperty(Fits &fits, daf::base::PropertySet
const &metadata,
std::string const &key,
1000 char const *comment =
nullptr) {
1002 boost::to_upper(upperKey);
1003 if (upperKey.compare(key) != 0){
1005 boost::format(
"In %s, key '%s' may be standardized to uppercase '%s' on write.") %
1006 BOOST_CURRENT_FUNCTION % key % upperKey);
1012 if (keyName.
size() > 8 && keyName.
rfind(
"HIERARCH ", 0) != 0) {
1013 keyName =
"HIERARCH " + keyName;
1016 if (valueType ==
typeid(
bool)) {
1017 if (metadata.isArray(key)) {
1021 writeKeyImpl(fits, keyName.
c_str(),
static_cast<bool>(tmp[i]), comment);
1024 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
bool>(key), comment);
1027 if (metadata.isArray(key)) {
1030 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1035 }
else if (valueType ==
typeid(
int)) {
1036 if (metadata.isArray(key)) {
1039 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1042 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
int>(key), comment);
1044 }
else if (valueType ==
typeid(
long)) {
1045 if (metadata.isArray(key)) {
1048 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1051 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
long>(key), comment);
1053 }
else if (valueType ==
typeid(
long long)) {
1054 if (metadata.isArray(key)) {
1057 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1060 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
long long>(key), comment);
1063 if (metadata.isArray(key)) {
1066 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1071 }
else if (valueType ==
typeid(
double)) {
1072 if (metadata.isArray(key)) {
1075 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1078 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
double>(key), comment);
1081 if (metadata.isArray(key)) {
1084 writeKeyImpl(fits, keyName.
c_str(), tmp[i], comment);
1087 writeKeyImpl(fits, keyName.
c_str(), metadata.get<
std::string>(key), comment);
1090 if (metadata.isArray(key)) {
1094 writeKeyImpl(fits, keyName.
c_str(), comment);
1097 writeKeyImpl(fits, keyName.
c_str(), comment);
1101 LOGLS_WARN(
"lsst.afw.fits.writeKeyFromProperty",
1103 boost::format(
"In %s, unknown type '%s' for key '%s'.") %
1104 BOOST_CURRENT_FUNCTION % valueType.
name() % key));
1114 MetadataIterationFunctor f;
1124 NameList paramNames;
1130 for (
auto const ¶mName : paramNames) {
1131 if (!isKeyIgnored(paramName,
true)) {
1133 writeKeyFromProperty(*
this, metadata, paramName, pl->
getComment(paramName).
c_str());
1135 writeKeyFromProperty(*
this, metadata, paramName);
1144 char *ttype =
nullptr;
1145 char *tform =
nullptr;
1146 fits_create_tbl(
reinterpret_cast<fitsfile *
>(fptr), BINARY_TBL, 0, 0, &ttype, &tform,
nullptr,
nullptr, &status);
1147 if (behavior & AUTO_CHECK) {
1152template <
typename T>
1155 fits_get_num_cols(
reinterpret_cast<fitsfile *
>(fptr), &nCols, &status);
1157 fits_insert_col(
reinterpret_cast<fitsfile *
>(fptr), nCols + 1,
const_cast<char *
>(ttype.
c_str()),
1158 const_cast<char *
>(tform.
c_str()), &status);
1159 if (behavior & AUTO_CHECK) {
1165template <
typename T>
1167 int nCols = addColumn<T>(ttype, size);
1168 updateColumnKey(
"TTYPE", nCols, ttype, comment);
1169 if (behavior & AUTO_CHECK) {
1177 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(fptr), &first, &status);
1178 fits_insert_rows(
reinterpret_cast<fitsfile *
>(fptr), first, nRows, &status);
1179 if (behavior & AUTO_CHECK) {
1187 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(fptr), &r, &status);
1188 if (behavior & AUTO_CHECK) {
1194template <
typename T>
1196 fits_write_col(
reinterpret_cast<fitsfile *
>(fptr), FitsTableType<T>::CONSTANT,
col + 1,
row + 1, 1,
1197 nElements,
const_cast<T *
>(value), &status);
1198 if (behavior & AUTO_CHECK) {
1209 char const *tmp = value.c_str();
1210 fits_write_col(
reinterpret_cast<fitsfile *
>(fptr), TSTRING,
col + 1,
row + 1, 1, 1,
1211 const_cast<char const **
>(&tmp), &status);
1212 if (behavior & AUTO_CHECK) {
1217template <
typename T>
1220 fits_read_col(
reinterpret_cast<fitsfile *
>(fptr), FitsTableType<T>::CONSTANT,
col + 1,
row + 1, 1,
1221 nElements,
nullptr, value, &anynul, &status);
1222 if (behavior & AUTO_CHECK) {
1229 long size = isVariableLength ? getTableArraySize(
row,
col) : getTableArraySize(
col);
1234 char *tmp = &buf.
front();
1235 fits_read_col(
reinterpret_cast<fitsfile *
>(fptr), TSTRING,
col + 1,
row + 1, 1, 1,
nullptr, &tmp, &anynul,
1237 if (behavior & AUTO_CHECK) {
1247 fits_get_coltype(
reinterpret_cast<fitsfile *
>(fptr),
col + 1, &typecode, &
result, &width, &status);
1248 if (behavior & AUTO_CHECK) {
1257 fits_read_descript(
reinterpret_cast<fitsfile *
>(fptr),
col + 1,
row + 1, &
result, &offset, &status);
1258 if (behavior & AUTO_CHECK) {
1268 fits_create_img(
reinterpret_cast<fitsfile *
>(fptr), 8, 0, &naxes, &status);
1269 if (behavior & AUTO_CHECK) {
1274void Fits::createImageImpl(
int bitpix,
int naxis,
long const *naxes) {
1275 fits_create_img(
reinterpret_cast<fitsfile *
>(fptr), bitpix, naxis,
const_cast<long *
>(naxes), &status);
1276 if (behavior & AUTO_CHECK) {
1281template <
typename T>
1282void Fits::writeImageImpl(T
const *
data,
int nElements) {
1283 fits_write_img(
reinterpret_cast<fitsfile *
>(fptr), FitsType<T>::CONSTANT, 1, nElements,
1284 const_cast<T *
>(
data), &status);
1285 if (behavior & AUTO_CHECK) {
1296struct ImageCompressionContext {
1298 ImageCompressionContext(Fits &fits_, ImageCompressionOptions
const &useThis)
1299 : fits(fits_), old(fits.getImageCompression()) {
1300 fits.setImageCompression(useThis);
1302 ~ImageCompressionContext() {
1306 fits.setImageCompression(old);
1309 makeErrorMessage(fits.fptr, fits.status,
"Failed to restore compression settings"));
1316 ImageCompressionOptions old;
1321template <
typename T>
1325 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1327 image.getBBox().getArea() > 0
1331 ImageCompressionContext comp(*
this, compression);
1332 if (behavior & AUTO_CHECK) {
1339 ndarray::Vector<long, 2> dims(
image.getArray().getShape().reverse());
1348 fullMetadata->combine(*wcsMetadata);
1350 fullMetadata = wcsMetadata;
1352 writeMetadata(*fullMetadata);
1366 fits_set_bscale(fits, 1.0, 0.0, &status);
1367 if (behavior & AUTO_CHECK) {
1373 int const fitsType = scale.bitpix == 0 ? FitsType<T>::CONSTANT : fitsTypeForBitpix(scale.bitpix);
1374 fits_write_img(fits, fitsType, 1, pixels->getNumElements(),
const_cast<void *
>(pixels->getData()),
1376 if (behavior & AUTO_CHECK) {
1386 if (scale.bzero != 0.0) {
1387 fits_write_key_lng(fits,
"BZERO",
static_cast<long>(scale.bzero),
1388 "Scaling: MEMORY = BZERO + BSCALE * DISK", &status);
1390 if (scale.bscale != 1.0) {
1391 fits_write_key_lng(fits,
"BSCALE",
static_cast<long>(scale.bscale),
1392 "Scaling: MEMORY = BZERO + BSCALE * DISK", &status);
1395 fits_write_key_dbl(fits,
"BZERO", scale.bzero, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1397 fits_write_key_dbl(fits,
"BSCALE", scale.bscale, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1400 if (behavior & AUTO_CHECK) {
1406 fits_write_key_lng(fits,
"BLANK", scale.blank,
"Value for undefined pixels", &status);
1407 fits_write_key_lng(fits,
"ZDITHER0",
options.scaling.seed,
"Dithering seed", &status);
1408 fits_write_key_str(fits,
"ZQUANTIZ",
"SUBTRACTIVE_DITHER_1",
"Dithering algorithm", &status);
1409 if (behavior & AUTO_CHECK) {
1417 fits_set_hdustruc(fits, &status);
1418 if (behavior & AUTO_CHECK) {
1427template <
typename T,
class Enable =
void>
1429 static T
constexpr value = 0;
1433template <
typename T>
1434struct NullValue<T, typename
std::enable_if<std::numeric_limits<T>::has_quiet_NaN>::type> {
1440template <
typename T>
1441void Fits::readImageImpl(
int nAxis, T *
data,
long *begin,
long *
end,
long *increment) {
1442 T null = NullValue<T>::value;
1444 fits_read_subset(
reinterpret_cast<fitsfile *
>(fptr), FitsType<T>::CONSTANT, begin,
end, increment,
1445 reinterpret_cast<void *
>(&null),
data, &anyNulls, &status);
1451 fits_get_img_dim(
reinterpret_cast<fitsfile *
>(fptr), &nAxis, &status);
1456void Fits::getImageShapeImpl(
int maxDim,
long *nAxes) {
1457 fits_get_img_size(
reinterpret_cast<fitsfile *
>(fptr), maxDim, nAxes, &status);
1461template <
typename T>
1464 fits_get_img_equivtype(
reinterpret_cast<fitsfile *
>(fptr), &imageType, &status);
1467 if (imageType < 0) {
1471 if (isFitsImageTypeSigned(imageType)) {
1472 return FitsBitPix<T>::CONSTANT >= imageType;
1475 return FitsBitPix<T>::CONSTANT > imageType;
1478 if (!isFitsImageTypeSigned(imageType)) {
1479 return FitsBitPix<T>::CONSTANT >= imageType;
1480 }
else if (imageType == LONGLONG_IMG) {
1483 return FitsBitPix<T>::CONSTANT >= imageType;
1495 fits_get_img_equivtype(
reinterpret_cast<fitsfile *
>(fptr), &bitpix, &status);
1508 case BYTE_IMG:
return "uint8";
1509 case SBYTE_IMG:
return "int8";
1510 case SHORT_IMG:
return "int16";
1511 case USHORT_IMG:
return "uint16";
1512 case LONG_IMG:
return "int32";
1513 case ULONG_IMG:
return "uint32";
1514 case LONGLONG_IMG:
return "int64";
1518 (boost::format(
"Unrecognized BITPIX value: %d") % bitpix).str()
1523 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1525 fits_get_compression_type(fits, &compType, &status);
1526 if (behavior & AUTO_CHECK) {
1531 fits_get_tile_dim(fits, tiles.getNumElements(), tiles.getData(), &status);
1532 if (behavior & AUTO_CHECK) {
1536 float quantizeLevel;
1537 fits_get_quantize_level(fits, &quantizeLevel, &status);
1538 if (behavior & AUTO_CHECK) {
1546 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1547 fits_unset_compression_request(fits, &status);
1551 if (behavior & AUTO_CHECK) {
1560 fits_set_tile_dim(fits, comp.
tiles.getNumElements(), comp.
tiles.getData(), &status);
1561 if (behavior & AUTO_CHECK) {
1567 if (behavior & AUTO_CHECK) {
1580 : fptr(nullptr), status(0), behavior(behavior_) {
1581 if (mode ==
"r" || mode ==
"rb") {
1582 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READONLY,
1584 }
else if (mode ==
"w" || mode ==
"wb") {
1585 std::filesystem::remove(filename);
1586 fits_create_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), &
status);
1587 }
else if (mode ==
"a" || mode ==
"ab") {
1588 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READWRITE,
1591 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1592 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1597 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1602 (boost::format(
"Invalid mode '%s' given when opening file '%s'") % mode % filename).str());
1610 : fptr(nullptr), status(0), behavior(behavior_) {
1611 using Reallocator =
void *(*)(
void *,
std::size_t);
1614 if (mode ==
"r" || mode ==
"rb") {
1615 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READONLY, &manager._ptr,
1616 &manager._len, 0,
nullptr,
1618 }
else if (mode ==
"w" || mode ==
"wb") {
1619 Reallocator reallocator =
nullptr;
1621 fits_create_memfile(
reinterpret_cast<fitsfile **
>(&
fptr), &manager._ptr, &manager._len, 0,
1624 }
else if (mode ==
"a" || mode ==
"ab") {
1625 Reallocator reallocator =
nullptr;
1627 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READWRITE, &manager._ptr,
1628 &manager._len, 0, reallocator, &
status);
1630 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1631 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1636 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1640 (boost::format(
"Invalid mode '%s' given when opening memory file at '%s'") % mode %
1646 *
this, boost::format(
"Opening memory file at '%s' with mode '%s'") % manager._ptr % mode);
1651 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &
status);
1658 auto combined = std::make_shared<daf::base::PropertyList>();
1659 bool const asScalar =
true;
1660 for (
auto const &name : first.getOrderedNames()) {
1661 auto const iscv = isCommentIsValid(first, name);
1662 if (iscv.isComment) {
1667 combined->copy(name, first, name, asScalar);
1670 for (
auto const &name : second.getOrderedNames()) {
1671 auto const iscv = isCommentIsValid(second, name);
1672 if (iscv.isComment) {
1678 combined->copy(name, second, name, asScalar);
1687template <
typename T,
typename... Args>
1715 auto metadata = std::make_shared<lsst::daf::base::PropertyList>();
1718 int oldHdu = fitsfile.
getHdu();
1719 if (oldHdu != 0 && metadata->exists(
"INHERIT")) {
1720 bool inherit =
false;
1724 }
else if (metadata->typeOf(
"INHERIT") ==
typeid(
std::string)) {
1725 inherit = (metadata->get<
std::string>(
"INHERIT") ==
"T");
1727 inherit = metadata->get<
bool>(
"INHERIT");
1729 if (
strip) metadata->remove(
"INHERIT");
1735 auto primaryHduMetadata = std::make_shared<daf::base::PropertyList>();
1740 auto const emptyMetadata = std::make_shared<lsst::daf::base::PropertyList>();
1750 _oldHdu(_fits.getHdu()),
1753 _fits.
setHdu(hdu, relative);
1775 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1781 fits_get_img_dim(fits, &naxis, &
status);
1790 bool isCompressed = fits_is_compressed_image(fits, &
status);
1797 return isCompressed;
1802 std::vector<long>{config.getAsInt64(
"compression.columns"),
1803 config.getAsInt64(
"compression.rows")},
1804 config.getAsDouble(
"compression.quantizeLevel")),
1806 config.getAsInt(
"scaling.bitpix"),
1807 config.exists(
"scaling.maskPlanes") ? config.getArray<
std::string>(
"scaling.maskPlanes")
1808 :
std::vector<
std::string>{},
1809 config.getAsInt(
"scaling.seed"), config.getAsDouble(
"scaling.quantizeLevel"),
1810 config.getAsDouble(
"scaling.quantizePad"), config.get<
bool>(
"scaling.fuzz"),
1811 config.getAsDouble(
"scaling.bscale"), config.getAsDouble(
"scaling.bzero")) {}
1815template <
typename T>
1818 output.
add(name, input.get<T>(name, defaultValue));
1821template <
typename T>
1822void validateEntry(daf::base::PropertySet &output, daf::base::PropertySet
const &input,
1824 output.add(name, input.exists(name) ? input.getArray<T>(name) : defaultValue);
1830 auto validated = std::make_shared<daf::base::PropertySet>();
1832 validateEntry(*validated, config,
"compression.algorithm",
std::string(
"NONE"));
1833 validateEntry(*validated, config,
"compression.columns", 0);
1834 validateEntry(*validated, config,
"compression.rows", 1);
1835 validateEntry(*validated, config,
"compression.quantizeLevel", 0.0);
1837 validateEntry(*validated, config,
"scaling.algorithm",
std::string(
"NONE"));
1838 validateEntry(*validated, config,
"scaling.bitpix", 0);
1840 validateEntry(*validated, config,
"scaling.seed", 1);
1841 validateEntry(*validated, config,
"scaling.quantizeLevel", 5.0);
1842 validateEntry(*validated, config,
"scaling.quantizePad", 10.0);
1843 validateEntry(*validated, config,
"scaling.fuzz",
true);
1844 validateEntry(*validated, config,
"scaling.bscale", 1.0);
1845 validateEntry(*validated, config,
"scaling.bzero", 0.0);
1848 for (
auto const &name : config.names(
false)) {
1849 if (!validated->exists(name)) {
1851 os <<
"Invalid image write option: " <<
name;
1859#define INSTANTIATE_KEY_OPS(r, data, T) \
1860 template void Fits::updateKey(std::string const &, T const &, std::string const &); \
1861 template void Fits::writeKey(std::string const &, T const &, std::string const &); \
1862 template void Fits::updateKey(std::string const &, T const &); \
1863 template void Fits::writeKey(std::string const &, T const &); \
1864 template void Fits::updateColumnKey(std::string const &, int, T const &, std::string const &); \
1865 template void Fits::writeColumnKey(std::string const &, int, T const &, std::string const &); \
1866 template void Fits::updateColumnKey(std::string const &, int, T const &); \
1867 template void Fits::writeColumnKey(std::string const &, int, T const &); \
1868 template void Fits::readKey(std::string const &, T &);
1870#define INSTANTIATE_IMAGE_OPS(r, data, T) \
1871 template void Fits::writeImageImpl(T const *, int); \
1872 template void Fits::writeImage(image::ImageBase<T> const &, ImageWriteOptions const &, \
1873 daf::base::PropertySet const *, \
1874 image::Mask<image::MaskPixel> const *); \
1875 template void Fits::readImageImpl(int, T *, long *, long *, long *); \
1876 template bool Fits::checkImageType<T>(); \
1877 template int getBitPix<T>();
1879#define INSTANTIATE_TABLE_OPS(r, data, T) \
1880 template int Fits::addColumn<T>(std::string const &ttype, int size); \
1881 template int Fits::addColumn<T>(std::string const &ttype, int size, std::string const &comment);
1882#define INSTANTIATE_TABLE_ARRAY_OPS(r, data, T) \
1883 template void Fits::writeTableArray(std::size_t row, int col, int nElements, T const *value); \
1884 template void Fits::readTableArray(std::size_t row, int col, int nElements, T *value);
1891 (bool)(unsigned char)(short)(unsigned short)(int)(unsigned int)(long)(unsigned long)(LONGLONG)( \
1892 float)(double)(std::complex<float>)(std::complex<double>)(std::string)
1894#define COLUMN_TYPES \
1895 (bool)(std::string)(std::int8_t)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t) \
1896 (std::int64_t)(float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1898#define COLUMN_ARRAY_TYPES \
1899 (bool)(char)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t)(std::int64_t)( \
1900 float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1902#define IMAGE_TYPES \
1903 (unsigned char)(short)(unsigned short)(int)(unsigned int)(std::int64_t)(std::uint64_t)(float)(double)
table::Key< std::string > name
#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.
table::Key< double > scaling
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.
ImageCompressionOptions getImageCompression()
Return the current image compression settings.
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 setImageCompression(ImageCompressionOptions const &options)
Set compression options for writing FITS images.
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.
void add(std::string const &name, T const &value)
Append a single value to the vector of values for a property name (possibly hierarchical).
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.
A class representing an angle.
Reports errors that are due to events beyond the control of the program.
#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.
int getBitPix()
Return the cfitsio integer BITPIX code for the given data type.
ImageScalingOptions::ScalingAlgorithm scalingAlgorithmFromString(std::string const &name)
Interpret scaling algorithm expressed in string.
void setAllowImageCompression(bool allow)
std::string makeErrorMessage(std::string const &fileName="", int status=0, std::string const &msg="")
Return an error message reflecting FITS I/O errors.
bool getAllowImageCompression()
int compressionAlgorithmToCfitsio(ImageCompressionOptions::CompressionAlgorithm algorithm)
Convert ImageCompressionOptions::CompressionAlgorithm to cfitsio.
HduType
an enum representing the various types of FITS HDU that are available in cfitsio library
ImageCompressionOptions::CompressionAlgorithm compressionAlgorithmFromCfitsio(int cfitsio)
Convert compression algorithm from cfitsio to ImageCompressionOptions::CompressionAlgorithm.
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.
ImageCompressionOptions::CompressionAlgorithm compressionAlgorithmFromString(std::string const &name)
Interpret compression algorithm expressed in string.
ndarray::Array< T const, N, N > const makeContiguousArray(ndarray::Array< T, N, C > const &array)
Construct a contiguous ndarray.
std::string const wcsNameForXY0
Options for tile compression of image pixels.
float quantizeLevel
quantization level: 0.0 = none requires use of GZIP or GZIP_SHUFFLE
CompressionAlgorithm algorithm
Compresion algorithm to use.
ndarray::Array< long, 1, 1 > Tiles
Tiles tiles
Tile size; a dimension with 0 means infinite (e.g., to specify one row: 0,1)
CompressionAlgorithm
Compression algorithms.
Options for writing an image to FITS.
static std::shared_ptr< daf::base::PropertySet > validate(daf::base::PropertySet const &config)
Validate a PropertySet.
ImageWriteOptions(image::Image< T > const &image)
Construct with default options for images.
FITS BITPIX header value by C++ type.
py::scoped_interpreter guard