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";
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;
198 return s.
substr(i1, (i1 == std::string::npos) ? 0 : 1 + i2 - i1);
203char getFormatCode(
bool *) {
return 'X'; }
212char getFormatCode(
float *) {
return 'E'; }
213char getFormatCode(
double *) {
return 'D'; }
223 return (boost::format(
"%d%c") % size % getFormatCode((T *)
nullptr)).str();
224 }
else if (size < 0) {
226 return (boost::format(
"1Q%c(%d)") % getFormatCode((T *)
nullptr) % (-size)).str();
229 return (boost::format(
"1Q%c") % getFormatCode((T *)
nullptr)).str();
239struct FitsType<bool> {
240 static int const CONSTANT = TLOGICAL;
243struct FitsType<char> {
244 static int const CONSTANT = TSTRING;
247struct FitsType<signed char> {
248 static int const CONSTANT = TSBYTE;
251struct FitsType<unsigned char> {
252 static int const CONSTANT = TBYTE;
255struct FitsType<short> {
256 static int const CONSTANT = TSHORT;
259struct FitsType<unsigned short> {
260 static int const CONSTANT = TUSHORT;
263struct FitsType<int> {
264 static int const CONSTANT = TINT;
267struct FitsType<unsigned int> {
268 static int const CONSTANT = TUINT;
271struct FitsType<long> {
272 static int const CONSTANT = TLONG;
275struct FitsType<unsigned long> {
276 static int const CONSTANT = TULONG;
279struct FitsType<long long> {
280 static int const CONSTANT = TLONGLONG;
283struct FitsType<unsigned long long> {
284 static int const CONSTANT = TLONGLONG;
287struct FitsType<float> {
288 static int const CONSTANT = TFLOAT;
291struct FitsType<double> {
292 static int const CONSTANT = TDOUBLE;
296 static int const CONSTANT = TDOUBLE;
299struct FitsType<
std::complex<float> > {
300 static int const CONSTANT = TCOMPLEX;
303struct FitsType<
std::complex<double> > {
304 static int const CONSTANT = TDBLCOMPLEX;
309struct FitsTableType :
public FitsType<T> {};
311struct FitsTableType<bool> {
312 static int const CONSTANT = TBIT;
319struct FitsBitPix<unsigned char> {
320 static int const CONSTANT = BYTE_IMG;
323struct FitsBitPix<short> {
324 static int const CONSTANT = SHORT_IMG;
327struct FitsBitPix<unsigned short> {
328 static int const CONSTANT = USHORT_IMG;
331struct FitsBitPix<int> {
332 static int const CONSTANT = LONG_IMG;
335struct FitsBitPix<unsigned int> {
336 static int const CONSTANT = ULONG_IMG;
339struct FitsBitPix<
std::int64_t> {
340 static int const CONSTANT = LONGLONG_IMG;
343struct FitsBitPix<
std::uint64_t> {
344 static int const CONSTANT = LONGLONG_IMG;
347struct FitsBitPix<float> {
348 static int const CONSTANT = FLOAT_IMG;
351struct FitsBitPix<double> {
352 static int const CONSTANT = DOUBLE_IMG;
355bool isFitsImageTypeSigned(
int constant) {
357 case BYTE_IMG:
return false;
358 case SHORT_IMG:
return true;
359 case USHORT_IMG:
return false;
360 case LONG_IMG:
return true;
361 case ULONG_IMG:
return false;
362 case LONGLONG_IMG:
return true;
363 case FLOAT_IMG:
return true;
364 case DOUBLE_IMG:
return true;
366 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
"Invalid constant.");
369static bool allowImageCompression =
true;
371int fitsTypeForBitpix(
int bitpix) {
387 os <<
"Invalid bitpix value: " << bitpix;
388 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
os.str());
410ItemInfo isCommentIsValid(daf::base::PropertyList
const &pl,
std::string const &
name) {
411 if (!pl.exists(
name)) {
412 return ItemInfo(
false,
false);
415 if ((
name ==
"COMMENT") || (
name ==
"HISTORY")) {
418 return ItemInfo(
false,
true);
429 os <<
"cfitsio error";
430 if (fileName !=
"") {
431 os <<
" (" << fileName <<
")";
434 char fitsErrMsg[FLEN_ERRMSG];
435 fits_get_errstatus(status, fitsErrMsg);
436 os <<
": " << fitsErrMsg <<
" (" << status <<
")";
441 os <<
"\ncfitsio error stack:\n";
442 char cfitsioMsg[FLEN_ERRMSG];
445 while (fits_read_errmsg(cfitsioMsg) != 0) {
446 cfitsioMsg[FLEN_ERRMSG-1] = char(0);
449 if( !isprint(cfitsioMsg[i]) ) cfitsioMsg[i] =
'.';
450 os <<
" " << cfitsioMsg <<
"\n";
457 fitsfile *fd =
reinterpret_cast<fitsfile *
>(fptr);
458 if (fd !=
nullptr && fd->Fptr !=
nullptr && fd->Fptr->filename !=
nullptr) {
459 fileName = fd->Fptr->filename;
474 for (
auto const &
name : allParamNames) {
479 return makeLimitedFitsHeaderImpl(desiredParamNames, metadata);
498 return FitsBitPix<T>::CONSTANT;
507 fitsfile *fd =
reinterpret_cast<fitsfile *
>(
fptr);
508 if (fd !=
nullptr && fd->Fptr !=
nullptr && fd->Fptr->filename !=
nullptr) {
509 fileName = fd->Fptr->filename;
516 fits_get_hdu_num(
reinterpret_cast<fitsfile *
>(
fptr), &n);
522 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu,
nullptr, &
status);
528 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu + 1,
nullptr, &
status);
533 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), 1,
nullptr, &tmpStatus);
543 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &n, &
status);
580double stringToNonFiniteDouble(
std::string const &value) {
581 if (value ==
"NAN") {
584 if (value ==
"+INFINITY") {
587 if (value ==
"-INFINITY") {
594void updateKeyImpl(Fits &
fits,
char const *key, T
const &value,
char const *comment) {
595 fits_update_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
596 const_cast<T *
>(&value),
const_cast<char *
>(comment), &
fits.
status);
599void updateKeyImpl(Fits &
fits,
char const *key,
std::string const &value,
char const *comment) {
600 fits_update_key_longstr(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key),
601 const_cast<char *
>(value.c_str()),
const_cast<char *
>(comment), &
fits.
status);
604void updateKeyImpl(Fits &
fits,
char const *key,
bool const &value,
char const *comment) {
606 fits_update_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), TLOGICAL,
const_cast<char *
>(key), &v,
610void updateKeyImpl(Fits &
fits,
char const *key,
double const &value,
char const *comment) {
611 std::string strValue = nonFiniteDoubleToString(value);
612 if (!strValue.
empty()) {
613 updateKeyImpl(
fits, key, strValue, comment);
615 fits_update_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<double>::CONSTANT,
616 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
622void writeKeyImpl(Fits &
fits,
char const *key, T
const &value,
char const *comment) {
623 fits_write_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
624 const_cast<T *
>(&value),
const_cast<char *
>(comment), &
fits.
status);
627void writeKeyImpl(Fits &
fits,
char const *key,
char const *comment) {
629 fits_write_key_null(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key),
633void writeKeyImpl(Fits &
fits,
char const *key,
std::string const &value,
char const *comment) {
634 if (
strncmp(key,
"COMMENT", 7) == 0) {
635 fits_write_comment(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(value.c_str()),
637 }
else if (
strncmp(key,
"HISTORY", 7) == 0) {
638 fits_write_history(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(value.c_str()),
641 fits_write_key_longstr(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key),
642 const_cast<char *
>(value.c_str()),
const_cast<char *
>(comment), &
fits.
status);
646void writeKeyImpl(Fits &
fits,
char const *key,
bool const &value,
char const *comment) {
648 fits_write_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), TLOGICAL,
const_cast<char *
>(key), &v,
652void writeKeyImpl(Fits &
fits,
char const *key,
double const &value,
char const *comment) {
653 std::string strValue = nonFiniteDoubleToString(value);
654 if (!strValue.
empty()) {
655 writeKeyImpl(
fits, key, strValue, comment);
657 fits_write_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<double>::CONSTANT,
658 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
667 updateKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
675 writeKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
683 updateKeyImpl(*
this, key.
c_str(), value,
nullptr);
691 writeKeyImpl(*
this, key.
c_str(), value,
nullptr);
734void readKeyImpl(
Fits &
fits,
char const *key, T &value) {
735 fits_read_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
741 fits_read_key_longstr(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key), &buf,
nullptr,
749void readKeyImpl(Fits &
fits,
char const *key,
double &value) {
753 char buf[FLEN_VALUE];
754 fits_read_keyword(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key), buf,
nullptr, &
fits.
status);
760 readKeyImpl(
fits, key, unquoted);
764 value = stringToNonFiniteDouble(unquoted);
767 afw::fits::FitsError,
768 (boost::format(
"Unrecognised string value for keyword '%s' when parsing as double: %s") %
773 fits_read_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<double>::CONSTANT,
774 const_cast<char *
>(key), &value,
nullptr, &
fits.
status);
782 readKeyImpl(*
this, key.
c_str(), value);
793 fits_get_hdrspace(
reinterpret_cast<fitsfile *
>(
fptr), &nKeys,
nullptr, &
status);
799 fits_read_keyn(
reinterpret_cast<fitsfile *
>(
fptr), i, key, value, comment, &
status);
803 boost::to_upper(upperKey);
804 if (upperKey.
compare(key) != 0){
806 boost::format(
"In %s, standardizing key '%s' to uppercase '%s' on read.") %
807 BOOST_CURRENT_FUNCTION % key % upperKey);
811 commentStr = comment;
813 while (valueStr.
size() > 2 && valueStr[valueStr.
size() - 2] ==
'&' && i <= nKeys) {
815 fits_read_record(
reinterpret_cast<fitsfile *
>(
fptr), i, key, &
status);
816 if (strncmp(key,
"CONTINUE", 8) != 0) {
823 if (firstQuote == std::string::npos) {
828 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
831 if (lastQuote == std::string::npos) {
836 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
838 valueStr += card.
substr(firstQuote + 1, lastQuote - firstQuote);
840 if (slash != std::string::npos) {
848 functor(keyStr, valueStr, commentStr);
856bool isKeyIgnored(
std::string const &key,
bool write =
false) {
857 return ((ignoreKeys.
find(key) != ignoreKeys.
end()) || ignoreKeyStarts.matches(key) ||
858 (write && ignoreKeyStartsWrite.matches(key)));
861class MetadataIterationFunctor :
public HeaderIterationFunctor {
865 template <
typename T>
871 if (
list->exists(key) &&
list->isUndefined(key)) {
873 boost::format(
"In %s, replacing undefined value for key '%s'.") %
874 BOOST_CURRENT_FUNCTION % key);
875 list->set(key, value, comment);
877 list->add(key, value, comment);
880 if (
set->exists(key) &&
set->isUndefined(key)) {
882 boost::format(
"In %s, replacing undefined value for key '%s'.") %
883 BOOST_CURRENT_FUNCTION % key);
884 set->set(key, value);
886 set->add(key, value);
896 if (
list->exists(key) && !
list->isUndefined(key)) {
900 boost::format(
"In %s, dropping undefined value for key '%s'.") %
901 BOOST_CURRENT_FUNCTION % key);
903 list->add(key,
nullptr, comment);
906 if (
set->exists(key) && !
set->isUndefined(key)) {
910 boost::format(
"In %s, dropping undefined value for key '%s'.") %
911 BOOST_CURRENT_FUNCTION % key);
913 set->add(key,
nullptr);
919 daf::base::PropertySet *
set;
926 static std::regex const intRegex(
"[+-]?[0-9]+");
927 static std::regex const doubleRegex(
"[+-]?([0-9]*\\.?[0-9]+|[0-9]+\\.?[0-9]*)([eE][+-]?[0-9]+)?");
928 static std::regex const fitsStringRegex(
"'(.*?) *'");
930 static std::regex const fitsDefinitionCommentRegex(
931 " *(FITS \\(Flexible Image Transport System\\)|and Astrophysics', volume 376, page 359).*");
934 if (
strip && isKeyIgnored(key)) {
941 add(key,
bool(value ==
"T" || value ==
"t"), comment);
946 if (val < (1LL << 31) && val > -(1LL << 31)) {
947 add(key,
static_cast<int>(val), comment);
949 add(key, val, comment);
955 add(key, val, comment);
958 double val = stringToNonFiniteDouble(
str);
960 add(key, val, comment);
962 add(key,
str, comment);
964 }
else if (key ==
"HISTORY") {
965 add(key, comment,
"");
967 add(key, comment,
"");
968 }
else if (key.
empty() && value.empty()) {
973 add(
"COMMENT", comment,
"");
974 }
else if (value.empty()) {
977 if (key !=
"COMMENT") {
982 afw::fits::FitsError,
983 (boost::format(
"Could not parse header value for key '%s': '%s'") % key % value).
str());
987void writeKeyFromProperty(Fits &
fits, daf::base::PropertySet
const &metadata,
std::string const &key,
988 char const *comment =
nullptr) {
990 boost::to_upper(upperKey);
991 if (upperKey.compare(key) != 0){
993 boost::format(
"In %s, key '%s' may be standardized to uppercase '%s' on write.") %
994 BOOST_CURRENT_FUNCTION % key % upperKey);
997 if (valueType ==
typeid(
bool)) {
998 if (metadata.isArray(key)) {
1002 writeKeyImpl(
fits, key.
c_str(),
static_cast<bool>(tmp[i]), comment);
1005 writeKeyImpl(
fits, key.
c_str(), metadata.get<
bool>(key), comment);
1008 if (metadata.isArray(key)) {
1011 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1016 }
else if (valueType ==
typeid(
int)) {
1017 if (metadata.isArray(key)) {
1020 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1023 writeKeyImpl(
fits, key.
c_str(), metadata.get<
int>(key), comment);
1025 }
else if (valueType ==
typeid(
long)) {
1026 if (metadata.isArray(key)) {
1029 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1032 writeKeyImpl(
fits, key.
c_str(), metadata.get<
long>(key), comment);
1034 }
else if (valueType ==
typeid(
long long)) {
1035 if (metadata.isArray(key)) {
1038 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1041 writeKeyImpl(
fits, key.
c_str(), metadata.get<
long long>(key), comment);
1044 if (metadata.isArray(key)) {
1047 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1052 }
else if (valueType ==
typeid(
double)) {
1053 if (metadata.isArray(key)) {
1056 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1059 writeKeyImpl(
fits, key.
c_str(), metadata.get<
double>(key), comment);
1062 if (metadata.isArray(key)) {
1065 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1071 if (metadata.isArray(key)) {
1075 writeKeyImpl(
fits, key.
c_str(), comment);
1078 writeKeyImpl(
fits, key.
c_str(), comment);
1082 LOGLS_WARN(
"lsst.afw.fits.writeKeyFromProperty",
1084 boost::format(
"In %s, unknown type '%s' for key '%s'.") %
1085 BOOST_CURRENT_FUNCTION % valueType.
name() % key));
1095 MetadataIterationFunctor f;
1105 NameList paramNames;
1111 for (
auto const ¶mName : paramNames) {
1112 if (!isKeyIgnored(paramName,
true)) {
1114 writeKeyFromProperty(*
this, metadata, paramName, pl->
getComment(paramName).
c_str());
1116 writeKeyFromProperty(*
this, metadata, paramName);
1125 char *ttype =
nullptr;
1126 char *tform =
nullptr;
1127 fits_create_tbl(
reinterpret_cast<fitsfile *
>(
fptr), BINARY_TBL, 0, 0, &ttype, &tform,
nullptr,
nullptr, &
status);
1133template <
typename T>
1136 fits_get_num_cols(
reinterpret_cast<fitsfile *
>(
fptr), &nCols, &
status);
1138 fits_insert_col(
reinterpret_cast<fitsfile *
>(
fptr), nCols + 1,
const_cast<char *
>(ttype.
c_str()),
1146template <
typename T>
1148 int nCols = addColumn<T>(ttype, size);
1158 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(
fptr), &
first, &
status);
1159 fits_insert_rows(
reinterpret_cast<fitsfile *
>(
fptr),
first, nRows, &
status);
1168 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(
fptr), &r, &
status);
1175template <
typename T>
1177 fits_write_col(
reinterpret_cast<fitsfile *
>(
fptr), FitsTableType<T>::CONSTANT, col + 1, row + 1, 1,
1178 nElements,
const_cast<T *
>(value), &
status);
1181 nElements % row % col);
1190 char const *tmp = value.c_str();
1191 fits_write_col(
reinterpret_cast<fitsfile *
>(
fptr), TSTRING, col + 1, row + 1, 1, 1,
1192 const_cast<char const **
>(&tmp), &
status);
1198template <
typename T>
1201 fits_read_col(
reinterpret_cast<fitsfile *
>(
fptr), FitsTableType<T>::CONSTANT, col + 1, row + 1, 1,
1202 nElements,
nullptr, value, &anynul, &
status);
1215 char *tmp = &buf.
front();
1216 fits_read_col(
reinterpret_cast<fitsfile *
>(
fptr), TSTRING, col + 1, row + 1, 1, 1,
nullptr, &tmp, &anynul,
1228 fits_get_coltype(
reinterpret_cast<fitsfile *
>(
fptr), col + 1, &typecode, &result, &width, &
status);
1238 fits_read_descript(
reinterpret_cast<fitsfile *
>(
fptr), col + 1, row + 1, &result, &offset, &
status);
1249 fits_create_img(
reinterpret_cast<fitsfile *
>(
fptr), 8, 0, &naxes, &
status);
1255void Fits::createImageImpl(
int bitpix,
int naxis,
long const *naxes) {
1256 fits_create_img(
reinterpret_cast<fitsfile *
>(
fptr), bitpix, naxis,
const_cast<long *
>(naxes), &
status);
1262template <
typename T>
1263void Fits::writeImageImpl(T
const *
data,
int nElements) {
1264 fits_write_img(
reinterpret_cast<fitsfile *
>(
fptr), FitsType<T>::CONSTANT, 1, nElements,
1277struct ImageCompressionContext {
1279 ImageCompressionContext(Fits &fits_, ImageCompressionOptions
const &useThis)
1280 :
fits(fits_), old(
fits.getImageCompression()) {
1281 fits.setImageCompression(useThis);
1283 ~ImageCompressionContext() {
1287 fits.setImageCompression(old);
1297 ImageCompressionOptions old;
1302template <
typename T>
1306 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1308 image.getBBox().getArea() > 0
1312 ImageCompressionContext comp(*
this, compression);
1320 ndarray::Vector<long, 2> dims(
image.getArray().getShape().reverse());
1329 fullMetadata->combine(*wcsMetadata);
1331 fullMetadata = wcsMetadata;
1354 int const fitsType =
scale.bitpix == 0 ? FitsType<T>::CONSTANT : fitsTypeForBitpix(
scale.bitpix);
1355 fits_write_img(
fits, fitsType, 1, pixels->getNumElements(),
const_cast<void *
>(pixels->getData()),
1367 if (
scale.bzero != 0.0) {
1368 fits_write_key_lng(
fits,
"BZERO",
static_cast<long>(
scale.bzero),
1369 "Scaling: MEMORY = BZERO + BSCALE * DISK", &
status);
1371 if (
scale.bscale != 1.0) {
1372 fits_write_key_lng(
fits,
"BSCALE",
static_cast<long>(
scale.bscale),
1373 "Scaling: MEMORY = BZERO + BSCALE * DISK", &
status);
1376 fits_write_key_dbl(
fits,
"BZERO",
scale.bzero, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1378 fits_write_key_dbl(
fits,
"BSCALE",
scale.bscale, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1387 fits_write_key_lng(
fits,
"BLANK",
scale.blank,
"Value for undefined pixels", &
status);
1389 fits_write_key_str(
fits,
"ZQUANTIZ",
"SUBTRACTIVE_DITHER_1",
"Dithering algorithm", &
status);
1404template <
typename T>
1414template <
typename T,
class Enable =
void>
1416 static T
constexpr value = 0;
1420template <
typename T>
1421struct NullValue<T, typename
std::enable_if<std::numeric_limits<T>::has_quiet_NaN>
::type> {
1427template <
typename T>
1428void Fits::readImageImpl(
int nAxis, T *
data,
long *begin,
long *
end,
long *increment) {
1429 T null = NullValue<T>::value;
1431 fits_read_subset(
reinterpret_cast<fitsfile *
>(
fptr), FitsType<T>::CONSTANT, begin,
end, increment,
1432 reinterpret_cast<void *
>(&null),
data, &anyNulls, &
status);
1438 fits_get_img_dim(
reinterpret_cast<fitsfile *
>(
fptr), &nAxis, &
status);
1443void Fits::getImageShapeImpl(
int maxDim,
long *nAxes) {
1444 fits_get_img_size(
reinterpret_cast<fitsfile *
>(
fptr), maxDim, nAxes, &
status);
1448template <
typename T>
1451 fits_get_img_equivtype(
reinterpret_cast<fitsfile *
>(
fptr), &imageType, &
status);
1454 if (imageType < 0) {
1458 if (isFitsImageTypeSigned(imageType)) {
1459 return FitsBitPix<T>::CONSTANT >= imageType;
1462 return FitsBitPix<T>::CONSTANT > imageType;
1465 if (!isFitsImageTypeSigned(imageType)) {
1466 return FitsBitPix<T>::CONSTANT >= imageType;
1467 }
else if (imageType == LONGLONG_IMG) {
1470 return FitsBitPix<T>::CONSTANT >= imageType;
1482 fits_get_img_equivtype(
reinterpret_cast<fitsfile *
>(
fptr), &bitpix, &
status);
1495 case BYTE_IMG:
return "uint8";
1496 case SBYTE_IMG:
return "int8";
1497 case SHORT_IMG:
return "int16";
1498 case USHORT_IMG:
return "uint16";
1499 case LONG_IMG:
return "int32";
1500 case ULONG_IMG:
return "uint32";
1501 case LONGLONG_IMG:
return "int64";
1505 (boost::format(
"Unrecognized BITPIX value: %d") % bitpix).
str()
1510 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1512 fits_get_compression_type(
fits, &compType, &
status);
1518 fits_get_tile_dim(
fits, tiles.getNumElements(), tiles.getData(), &
status);
1523 float quantizeLevel;
1524 fits_get_quantize_level(
fits, &quantizeLevel, &
status);
1533 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1534 fits_unset_compression_request(
fits, &
status);
1567 : fptr(nullptr), status(0), behavior(behavior_) {
1568 if (mode ==
"r" || mode ==
"rb") {
1569 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READONLY,
1571 }
else if (mode ==
"w" || mode ==
"wb") {
1572 std::filesystem::remove(filename);
1573 fits_create_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), &
status);
1574 }
else if (mode ==
"a" || mode ==
"ab") {
1575 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READWRITE,
1578 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1579 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1584 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1589 (boost::format(
"Invalid mode '%s' given when opening file '%s'") % mode % filename).
str());
1597 : fptr(nullptr), status(0), behavior(behavior_) {
1598 using Reallocator =
void *(*)(
void *,
std::size_t);
1601 if (mode ==
"r" || mode ==
"rb") {
1602 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READONLY, &manager._ptr,
1603 &manager._len, 0,
nullptr,
1605 }
else if (mode ==
"w" || mode ==
"wb") {
1606 Reallocator reallocator =
nullptr;
1608 fits_create_memfile(
reinterpret_cast<fitsfile **
>(&
fptr), &manager._ptr, &manager._len, 0,
1611 }
else if (mode ==
"a" || mode ==
"ab") {
1612 Reallocator reallocator =
nullptr;
1614 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READWRITE, &manager._ptr,
1615 &manager._len, 0, reallocator, &
status);
1617 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1618 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1623 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1627 (boost::format(
"Invalid mode '%s' given when opening memory file at '%s'") % mode %
1633 *
this, boost::format(
"Opening memory file at '%s' with mode '%s'") % manager._ptr % mode);
1638 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &
status);
1645 auto combined = std::make_shared<daf::base::PropertyList>();
1646 bool const asScalar =
true;
1647 for (
auto const &
name :
first.getOrderedNames()) {
1648 auto const iscv = isCommentIsValid(
first,
name);
1649 if (iscv.isComment) {
1657 for (
auto const &
name :
second.getOrderedNames()) {
1658 auto const iscv = isCommentIsValid(
second,
name);
1659 if (iscv.isComment) {
1696 auto metadata = std::make_shared<lsst::daf::base::PropertyList>();
1699 int oldHdu = fitsfile.
getHdu();
1700 if (oldHdu != 0 && metadata->exists(
"INHERIT")) {
1701 bool inherit =
false;
1702 if (metadata->typeOf(
"INHERIT") ==
typeid(
std::string)) {
1703 inherit = (metadata->get<
std::string>(
"INHERIT") ==
"T");
1705 inherit = metadata->get<
bool>(
"INHERIT");
1707 if (
strip) metadata->remove(
"INHERIT");
1713 auto primaryHduMetadata = std::make_shared<daf::base::PropertyList>();
1718 auto const emptyMetadata = std::make_shared<lsst::daf::base::PropertyList>();
1728 _oldHdu(_fits.getHdu()),
1731 _fits.
setHdu(hdu, relative);
1753 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1768 bool isCompressed = fits_is_compressed_image(
fits, &
status);
1775 return isCompressed;
1782 config.getAsDouble(
"compression.quantizeLevel")),
1784 config.getAsInt(
"scaling.bitpix"),
1785 config.exists(
"scaling.maskPlanes") ? config.getArray<
std::string>(
"scaling.maskPlanes")
1787 config.getAsInt(
"scaling.seed"), config.getAsDouble(
"scaling.quantizeLevel"),
1788 config.getAsDouble(
"scaling.quantizePad"), config.get<
bool>(
"scaling.fuzz"),
1789 config.getAsDouble(
"scaling.bscale"), config.getAsDouble(
"scaling.bzero")) {}
1793template <
typename T>
1799template <
typename T>
1800void validateEntry(daf::base::PropertySet &output, daf::base::PropertySet
const &input,
1802 output.add(
name, input.exists(
name) ? input.getArray<T>(
name) : defaultValue);
1808 auto validated = std::make_shared<daf::base::PropertySet>();
1810 validateEntry(*validated, config,
"compression.algorithm",
std::string(
"NONE"));
1811 validateEntry(*validated, config,
"compression.columns", 0);
1812 validateEntry(*validated, config,
"compression.rows", 1);
1813 validateEntry(*validated, config,
"compression.quantizeLevel", 0.0);
1815 validateEntry(*validated, config,
"scaling.algorithm",
std::string(
"NONE"));
1816 validateEntry(*validated, config,
"scaling.bitpix", 0);
1818 validateEntry(*validated, config,
"scaling.seed", 1);
1819 validateEntry(*validated, config,
"scaling.quantizeLevel", 5.0);
1820 validateEntry(*validated, config,
"scaling.quantizePad", 10.0);
1821 validateEntry(*validated, config,
"scaling.fuzz",
true);
1822 validateEntry(*validated, config,
"scaling.bscale", 1.0);
1823 validateEntry(*validated, config,
"scaling.bzero", 0.0);
1826 for (
auto const &
name : config.
names(
false)) {
1827 if (!validated->exists(
name)) {
1829 os <<
"Invalid image write option: " <<
name;
1837#define INSTANTIATE_KEY_OPS(r, data, T) \
1838 template void Fits::updateKey(std::string const &, T const &, std::string const &); \
1839 template void Fits::writeKey(std::string const &, T const &, std::string const &); \
1840 template void Fits::updateKey(std::string const &, T const &); \
1841 template void Fits::writeKey(std::string const &, T const &); \
1842 template void Fits::updateColumnKey(std::string const &, int, T const &, std::string const &); \
1843 template void Fits::writeColumnKey(std::string const &, int, T const &, std::string const &); \
1844 template void Fits::updateColumnKey(std::string const &, int, T const &); \
1845 template void Fits::writeColumnKey(std::string const &, int, T const &); \
1846 template void Fits::readKey(std::string const &, T &);
1848#define INSTANTIATE_IMAGE_OPS(r, data, T) \
1849 template void Fits::writeImageImpl(T const *, int); \
1850 template void Fits::writeImage(image::ImageBase<T> const &, ImageWriteOptions const &, \
1851 daf::base::PropertySet const *, \
1852 image::Mask<image::MaskPixel> const *); \
1853 template void Fits::writeImage(image::ImageBase<T> const &, ImageWriteOptions const &, \
1854 std::shared_ptr<daf::base::PropertySet const>, \
1855 std::shared_ptr<image::Mask<image::MaskPixel> const>); \
1856 template void Fits::readImageImpl(int, T *, long *, long *, long *); \
1857 template bool Fits::checkImageType<T>(); \
1858 template int getBitPix<T>();
1860#define INSTANTIATE_TABLE_OPS(r, data, T) \
1861 template int Fits::addColumn<T>(std::string const &ttype, int size); \
1862 template int Fits::addColumn<T>(std::string const &ttype, int size, std::string const &comment);
1863#define INSTANTIATE_TABLE_ARRAY_OPS(r, data, T) \
1864 template void Fits::writeTableArray(std::size_t row, int col, int nElements, T const *value); \
1865 template void Fits::readTableArray(std::size_t row, int col, int nElements, T *value);
1872 (bool)(unsigned char)(short)(unsigned short)(int)(unsigned int)(long)(unsigned long)(LONGLONG)( \
1873 float)(double)(std::complex<float>)(std::complex<double>)(std::string)
1875#define COLUMN_TYPES \
1876 (bool)(std::string)(std::int8_t)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t) \
1877 (std::int64_t)(float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1879#define COLUMN_ARRAY_TYPES \
1880 (bool)(char)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t)(std::int64_t)( \
1881 float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1883#define IMAGE_TYPES \
1884 (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,...)
#define LOGLS_WARN(logger, message)
#define LOGL_WARN(logger, message...)
#define LOGLS_DEBUG(logger, message)
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.
bool fuzz
Fuzz the values when quantising floating-point values?
ImageScale determine(image::ImageBase< T > const &image, image::Mask< image::MaskPixel > const *mask=nullptr) const
Determine the scaling for a particular image.
int seed
Seed for random number generator when fuzzing.
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.
std::string const & getComment(std::string const &name) const
std::vector< std::string > getOrderedNames() const
int64_t getAsInt64(std::string const &name) const
std::vector< std::string > names(bool topLevelOnly=true) const
T get(std::string const &name) const
void add(std::string const &name, T const &value)
virtual std::shared_ptr< PropertySet > deepCopy() const
std::vector< std::string > paramNames(bool topLevelOnly=true) const
T find_first_not_of(T... args)
T find_last_not_of(T... args)
#define INSTANTIATE_TABLE_OPS(r, data, T)
daf::base::PropertyList * list
#define INSTANTIATE_IMAGE_OPS(r, data, T)
#define COLUMN_ARRAY_TYPES
daf::base::PropertySet * set
#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.
def scale(algorithm, min, max=None, frame=None)
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.
int getBitPix()
Return the cfitsio integer BITPIX code for the given data type.
std::shared_ptr< daf::base::PropertyList > readMetadata(std::string const &fileName, int hdu=DEFAULT_HDU, bool strip=false)
Read FITS header.
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.
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::shared_ptr< daf::base::PropertyList > createTrivialWcsMetadata(std::string const &wcsName, lsst::geom::Point2I const &xy0)
std::string const wcsNameForXY0
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_COLUMNVIEW_SCALAR, _, BOOST_PP_TUPLE_TO_SEQ(AFW_TABLE_SCALAR_FIELD_TYPE_N, AFW_TABLE_SCALAR_FIELD_TYPE_TUPLE)) BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_COLUMNVIEW_ARRAY
A base class for image defects.
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.
ImageCompressionOptions compression
Options controlling compression.
ImageScalingOptions scaling
Options controlling scaling.
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.