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);
542 fits_movnam_hdu(
reinterpret_cast<fitsfile *
>(
fptr),
static_cast<int>(hdutype),
543 const_cast<char *
>(
name.c_str()), hduver, &
status);
546 static_cast<int>(hdutype) % hduver);
551 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &n, &
status);
588double stringToNonFiniteDouble(
std::string const &value) {
589 if (value ==
"NAN") {
592 if (value ==
"+INFINITY") {
595 if (value ==
"-INFINITY") {
602void updateKeyImpl(Fits &fits,
char const *key, T
const &value,
char const *comment) {
603 fits_update_key(
reinterpret_cast<fitsfile *
>(fits.
fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
604 const_cast<T *
>(&value),
const_cast<char *
>(comment), &fits.
status);
607void updateKeyImpl(Fits &fits,
char const *key,
std::string const &value,
char const *comment) {
608 fits_update_key_longstr(
reinterpret_cast<fitsfile *
>(fits.
fptr),
const_cast<char *
>(key),
609 const_cast<char *
>(value.c_str()),
const_cast<char *
>(comment), &fits.
status);
612void updateKeyImpl(Fits &fits,
char const *key,
bool const &value,
char const *comment) {
614 fits_update_key(
reinterpret_cast<fitsfile *
>(fits.
fptr), TLOGICAL,
const_cast<char *
>(key), &v,
615 const_cast<char *
>(comment), &fits.
status);
618void updateKeyImpl(Fits &fits,
char const *key,
double const &value,
char const *comment) {
619 std::string strValue = nonFiniteDoubleToString(value);
620 if (!strValue.
empty()) {
621 updateKeyImpl(fits, key, strValue, comment);
623 fits_update_key(
reinterpret_cast<fitsfile *
>(fits.
fptr), FitsType<double>::CONSTANT,
624 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
630void writeKeyImpl(Fits &fits,
char const *key, T
const &value,
char const *comment) {
631 fits_write_key(
reinterpret_cast<fitsfile *
>(fits.
fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
632 const_cast<T *
>(&value),
const_cast<char *
>(comment), &fits.
status);
635void writeKeyImpl(Fits &fits,
char const *key,
char const *comment) {
637 fits_write_key_null(
reinterpret_cast<fitsfile *
>(fits.
fptr),
const_cast<char *
>(key),
638 const_cast<char *
>(comment), &fits.
status);
641void writeKeyImpl(Fits &fits,
char const *key,
std::string const &value,
char const *comment) {
642 if (
strncmp(key,
"COMMENT", 7) == 0) {
643 fits_write_comment(
reinterpret_cast<fitsfile *
>(fits.
fptr),
const_cast<char *
>(value.c_str()),
645 }
else if (
strncmp(key,
"HISTORY", 7) == 0) {
646 fits_write_history(
reinterpret_cast<fitsfile *
>(fits.
fptr),
const_cast<char *
>(value.c_str()),
649 fits_write_key_longstr(
reinterpret_cast<fitsfile *
>(fits.
fptr),
const_cast<char *
>(key),
650 const_cast<char *
>(value.c_str()),
const_cast<char *
>(comment), &fits.
status);
654void writeKeyImpl(Fits &fits,
char const *key,
bool const &value,
char const *comment) {
656 fits_write_key(
reinterpret_cast<fitsfile *
>(fits.
fptr), TLOGICAL,
const_cast<char *
>(key), &v,
657 const_cast<char *
>(comment), &fits.
status);
660void writeKeyImpl(Fits &fits,
char const *key,
double const &value,
char const *comment) {
661 std::string strValue = nonFiniteDoubleToString(value);
662 if (!strValue.
empty()) {
663 writeKeyImpl(fits, key, strValue, comment);
665 fits_write_key(
reinterpret_cast<fitsfile *
>(fits.
fptr), FitsType<double>::CONSTANT,
666 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
675 updateKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
683 writeKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
691 updateKeyImpl(*
this, key.
c_str(), value,
nullptr);
699 writeKeyImpl(*
this, key.
c_str(), value,
nullptr);
742void readKeyImpl(
Fits &fits,
char const *key, T &value) {
743 fits_read_key(
reinterpret_cast<fitsfile *
>(fits.
fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
744 &value,
nullptr, &fits.
status);
747void readKeyImpl(Fits &fits,
char const *key,
std::string &value) {
749 fits_read_key_longstr(
reinterpret_cast<fitsfile *
>(fits.
fptr),
const_cast<char *
>(key), &buf,
nullptr,
757void readKeyImpl(Fits &fits,
char const *key,
double &value) {
761 char buf[FLEN_VALUE];
762 fits_read_keyword(
reinterpret_cast<fitsfile *
>(fits.
fptr),
const_cast<char *
>(key), buf,
nullptr, &fits.
status);
768 readKeyImpl(fits, key, unquoted);
772 value = stringToNonFiniteDouble(unquoted);
775 afw::fits::FitsError,
776 (boost::format(
"Unrecognised string value for keyword '%s' when parsing as double: %s") %
781 fits_read_key(
reinterpret_cast<fitsfile *
>(fits.
fptr), FitsType<double>::CONSTANT,
782 const_cast<char *
>(key), &value,
nullptr, &fits.
status);
790 readKeyImpl(*
this, key.
c_str(), value);
801 fits_get_hdrspace(
reinterpret_cast<fitsfile *
>(
fptr), &nKeys,
nullptr, &
status);
807 fits_read_keyn(
reinterpret_cast<fitsfile *
>(
fptr), i, key, value, comment, &
status);
811 boost::to_upper(upperKey);
812 if (upperKey.
compare(key) != 0){
814 boost::format(
"In %s, standardizing key '%s' to uppercase '%s' on read.") %
815 BOOST_CURRENT_FUNCTION % key % upperKey);
819 commentStr = comment;
821 while (valueStr.
size() > 2 && valueStr[valueStr.
size() - 2] ==
'&' && i <= nKeys) {
823 fits_read_record(
reinterpret_cast<fitsfile *
>(
fptr), i, key, &
status);
824 if (strncmp(key,
"CONTINUE", 8) != 0) {
831 if (firstQuote == std::string::npos) {
836 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
839 if (lastQuote == std::string::npos) {
844 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
846 valueStr += card.
substr(firstQuote + 1, lastQuote - firstQuote);
848 if (slash != std::string::npos) {
856 functor(keyStr, valueStr, commentStr);
864bool isKeyIgnored(
std::string const &key,
bool write =
false) {
865 return ((ignoreKeys.
find(key) != ignoreKeys.
end()) || ignoreKeyStarts.matches(key) ||
866 (write && ignoreKeyStartsWrite.matches(key)));
869class MetadataIterationFunctor :
public HeaderIterationFunctor {
873 template <
typename T>
879 if (
list->exists(key) &&
list->isUndefined(key)) {
881 boost::format(
"In %s, replacing undefined value for key '%s'.") %
882 BOOST_CURRENT_FUNCTION % key);
883 list->set(key, value, comment);
885 list->add(key, value, comment);
888 if (
set->exists(key) &&
set->isUndefined(key)) {
890 boost::format(
"In %s, replacing undefined value for key '%s'.") %
891 BOOST_CURRENT_FUNCTION % key);
892 set->set(key, value);
894 set->add(key, value);
904 if (
list->exists(key) && !
list->isUndefined(key)) {
908 boost::format(
"In %s, dropping undefined value for key '%s'.") %
909 BOOST_CURRENT_FUNCTION % key);
911 list->add(key,
nullptr, comment);
914 if (
set->exists(key) && !
set->isUndefined(key)) {
918 boost::format(
"In %s, dropping undefined value for key '%s'.") %
919 BOOST_CURRENT_FUNCTION % key);
921 set->add(key,
nullptr);
927 daf::base::PropertySet *
set;
934 static std::regex const intRegex(
"[+-]?[0-9]+");
935 static std::regex const doubleRegex(
"[+-]?([0-9]*\\.?[0-9]+|[0-9]+\\.?[0-9]*)([eE][+-]?[0-9]+)?");
936 static std::regex const fitsStringRegex(
"'(.*?) *'");
938 static std::regex const fitsDefinitionCommentRegex(
939 " *(FITS \\(Flexible Image Transport System\\)|and Astrophysics', volume 376, page 359).*");
942 if (
strip && isKeyIgnored(key)) {
949 add(key,
bool(value ==
"T" || value ==
"t"), comment);
954 if (
val < (1LL << 31) &&
val > -(1LL << 31)) {
955 add(key,
static_cast<int>(
val), comment);
957 add(key,
val, comment);
963 add(key,
val, comment);
966 double val = stringToNonFiniteDouble(
str);
968 add(key,
val, comment);
970 add(key,
str, comment);
972 }
else if (key ==
"HISTORY") {
973 add(key, comment,
"");
975 add(key, comment,
"");
976 }
else if (key.
empty() && value.empty()) {
981 add(
"COMMENT", comment,
"");
982 }
else if (value.empty()) {
985 if (key !=
"COMMENT") {
990 afw::fits::FitsError,
991 (boost::format(
"Could not parse header value for key '%s': '%s'") % key % value).
str());
995void writeKeyFromProperty(Fits &fits, daf::base::PropertySet
const &metadata,
std::string const &key,
996 char const *comment =
nullptr) {
998 boost::to_upper(upperKey);
999 if (upperKey.compare(key) != 0){
1001 boost::format(
"In %s, key '%s' may be standardized to uppercase '%s' on write.") %
1002 BOOST_CURRENT_FUNCTION % key % upperKey);
1005 if (valueType ==
typeid(
bool)) {
1006 if (metadata.isArray(key)) {
1010 writeKeyImpl(fits, key.
c_str(),
static_cast<bool>(tmp[i]), comment);
1013 writeKeyImpl(fits, key.
c_str(), metadata.get<
bool>(key), comment);
1016 if (metadata.isArray(key)) {
1019 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1024 }
else if (valueType ==
typeid(
int)) {
1025 if (metadata.isArray(key)) {
1028 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1031 writeKeyImpl(fits, key.
c_str(), metadata.get<
int>(key), comment);
1033 }
else if (valueType ==
typeid(
long)) {
1034 if (metadata.isArray(key)) {
1037 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1040 writeKeyImpl(fits, key.
c_str(), metadata.get<
long>(key), comment);
1042 }
else if (valueType ==
typeid(
long long)) {
1043 if (metadata.isArray(key)) {
1046 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1049 writeKeyImpl(fits, key.
c_str(), metadata.get<
long long>(key), comment);
1052 if (metadata.isArray(key)) {
1055 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1060 }
else if (valueType ==
typeid(
double)) {
1061 if (metadata.isArray(key)) {
1064 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1067 writeKeyImpl(fits, key.
c_str(), metadata.get<
double>(key), comment);
1070 if (metadata.isArray(key)) {
1073 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1079 if (metadata.isArray(key)) {
1083 writeKeyImpl(fits, key.
c_str(), comment);
1086 writeKeyImpl(fits, key.
c_str(), comment);
1090 LOGLS_WARN(
"lsst.afw.fits.writeKeyFromProperty",
1092 boost::format(
"In %s, unknown type '%s' for key '%s'.") %
1093 BOOST_CURRENT_FUNCTION % valueType.
name() % key));
1103 MetadataIterationFunctor f;
1113 NameList paramNames;
1119 for (
auto const ¶mName : paramNames) {
1120 if (!isKeyIgnored(paramName,
true)) {
1122 writeKeyFromProperty(*
this, metadata, paramName, pl->
getComment(paramName).
c_str());
1124 writeKeyFromProperty(*
this, metadata, paramName);
1133 char *ttype =
nullptr;
1134 char *tform =
nullptr;
1135 fits_create_tbl(
reinterpret_cast<fitsfile *
>(
fptr), BINARY_TBL, 0, 0, &ttype, &tform,
nullptr,
nullptr, &
status);
1141template <
typename T>
1144 fits_get_num_cols(
reinterpret_cast<fitsfile *
>(
fptr), &nCols, &
status);
1146 fits_insert_col(
reinterpret_cast<fitsfile *
>(
fptr), nCols + 1,
const_cast<char *
>(ttype.
c_str()),
1154template <
typename T>
1156 int nCols = addColumn<T>(ttype, size);
1166 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(
fptr), &first, &
status);
1167 fits_insert_rows(
reinterpret_cast<fitsfile *
>(
fptr), first, nRows, &
status);
1176 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(
fptr), &r, &
status);
1183template <
typename T>
1185 fits_write_col(
reinterpret_cast<fitsfile *
>(
fptr), FitsTableType<T>::CONSTANT,
col + 1,
row + 1, 1,
1186 nElements,
const_cast<T *
>(value), &
status);
1198 char const *tmp = value.c_str();
1199 fits_write_col(
reinterpret_cast<fitsfile *
>(
fptr), TSTRING,
col + 1,
row + 1, 1, 1,
1200 const_cast<char const **
>(&tmp), &
status);
1206template <
typename T>
1209 fits_read_col(
reinterpret_cast<fitsfile *
>(
fptr), FitsTableType<T>::CONSTANT,
col + 1,
row + 1, 1,
1210 nElements,
nullptr, value, &anynul, &
status);
1223 char *tmp = &buf.
front();
1224 fits_read_col(
reinterpret_cast<fitsfile *
>(
fptr), TSTRING,
col + 1,
row + 1, 1, 1,
nullptr, &tmp, &anynul,
1236 fits_get_coltype(
reinterpret_cast<fitsfile *
>(
fptr),
col + 1, &typecode, &
result, &width, &
status);
1257 fits_create_img(
reinterpret_cast<fitsfile *
>(
fptr), 8, 0, &naxes, &
status);
1263void Fits::createImageImpl(
int bitpix,
int naxis,
long const *naxes) {
1264 fits_create_img(
reinterpret_cast<fitsfile *
>(
fptr), bitpix, naxis,
const_cast<long *
>(naxes), &
status);
1270template <
typename T>
1271void Fits::writeImageImpl(T
const *
data,
int nElements) {
1272 fits_write_img(
reinterpret_cast<fitsfile *
>(
fptr), FitsType<T>::CONSTANT, 1, nElements,
1285struct ImageCompressionContext {
1287 ImageCompressionContext(Fits &fits_, ImageCompressionOptions
const &useThis)
1288 : fits(fits_), old(fits.getImageCompression()) {
1289 fits.setImageCompression(useThis);
1291 ~ImageCompressionContext() {
1295 fits.setImageCompression(old);
1298 makeErrorMessage(fits.fptr, fits.status,
"Failed to restore compression settings"));
1305 ImageCompressionOptions old;
1310template <
typename T>
1314 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1316 image.getBBox().getArea() > 0
1320 ImageCompressionContext comp(*
this, compression);
1328 ndarray::Vector<long, 2> dims(
image.getArray().getShape().reverse());
1337 fullMetadata->combine(*wcsMetadata);
1339 fullMetadata = wcsMetadata;
1355 fits_set_bscale(fits, 1.0, 0.0, &
status);
1362 int const fitsType = scale.bitpix == 0 ? FitsType<T>::CONSTANT : fitsTypeForBitpix(scale.bitpix);
1363 fits_write_img(fits, fitsType, 1, pixels->getNumElements(),
const_cast<void *
>(pixels->getData()),
1375 if (scale.bzero != 0.0) {
1376 fits_write_key_lng(fits,
"BZERO",
static_cast<long>(scale.bzero),
1377 "Scaling: MEMORY = BZERO + BSCALE * DISK", &
status);
1379 if (scale.bscale != 1.0) {
1380 fits_write_key_lng(fits,
"BSCALE",
static_cast<long>(scale.bscale),
1381 "Scaling: MEMORY = BZERO + BSCALE * DISK", &
status);
1384 fits_write_key_dbl(fits,
"BZERO", scale.bzero, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1386 fits_write_key_dbl(fits,
"BSCALE", scale.bscale, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1395 fits_write_key_lng(fits,
"BLANK", scale.blank,
"Value for undefined pixels", &
status);
1396 fits_write_key_lng(fits,
"ZDITHER0", options.
scaling.
seed,
"Dithering seed", &
status);
1397 fits_write_key_str(fits,
"ZQUANTIZ",
"SUBTRACTIVE_DITHER_1",
"Dithering algorithm", &
status);
1406 fits_set_hdustruc(fits, &
status);
1412template <
typename T>
1422template <
typename T,
class Enable =
void>
1424 static T
constexpr value = 0;
1428template <
typename T>
1429struct NullValue<T, typename
std::enable_if<std::numeric_limits<T>::has_quiet_NaN>
::type> {
1435template <
typename T>
1436void Fits::readImageImpl(
int nAxis, T *
data,
long *begin,
long *
end,
long *increment) {
1437 T null = NullValue<T>::value;
1439 fits_read_subset(
reinterpret_cast<fitsfile *
>(
fptr), FitsType<T>::CONSTANT, begin,
end, increment,
1440 reinterpret_cast<void *
>(&null),
data, &anyNulls, &
status);
1446 fits_get_img_dim(
reinterpret_cast<fitsfile *
>(
fptr), &nAxis, &
status);
1451void Fits::getImageShapeImpl(
int maxDim,
long *nAxes) {
1452 fits_get_img_size(
reinterpret_cast<fitsfile *
>(
fptr), maxDim, nAxes, &
status);
1456template <
typename T>
1459 fits_get_img_equivtype(
reinterpret_cast<fitsfile *
>(
fptr), &imageType, &
status);
1462 if (imageType < 0) {
1466 if (isFitsImageTypeSigned(imageType)) {
1467 return FitsBitPix<T>::CONSTANT >= imageType;
1470 return FitsBitPix<T>::CONSTANT > imageType;
1473 if (!isFitsImageTypeSigned(imageType)) {
1474 return FitsBitPix<T>::CONSTANT >= imageType;
1475 }
else if (imageType == LONGLONG_IMG) {
1478 return FitsBitPix<T>::CONSTANT >= imageType;
1490 fits_get_img_equivtype(
reinterpret_cast<fitsfile *
>(
fptr), &bitpix, &
status);
1503 case BYTE_IMG:
return "uint8";
1504 case SBYTE_IMG:
return "int8";
1505 case SHORT_IMG:
return "int16";
1506 case USHORT_IMG:
return "uint16";
1507 case LONG_IMG:
return "int32";
1508 case ULONG_IMG:
return "uint32";
1509 case LONGLONG_IMG:
return "int64";
1513 (boost::format(
"Unrecognized BITPIX value: %d") % bitpix).
str()
1518 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1520 fits_get_compression_type(fits, &compType, &
status);
1526 fits_get_tile_dim(fits, tiles.getNumElements(), tiles.getData(), &
status);
1531 float quantizeLevel;
1532 fits_get_quantize_level(fits, &quantizeLevel, &
status);
1541 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1542 fits_unset_compression_request(fits, &
status);
1555 fits_set_tile_dim(fits, comp.
tiles.getNumElements(), comp.
tiles.getData(), &
status);
1575 : fptr(nullptr), status(0), behavior(behavior_) {
1576 if (mode ==
"r" || mode ==
"rb") {
1577 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READONLY,
1579 }
else if (mode ==
"w" || mode ==
"wb") {
1580 std::filesystem::remove(filename);
1581 fits_create_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), &
status);
1582 }
else if (mode ==
"a" || mode ==
"ab") {
1583 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READWRITE,
1586 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1587 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1592 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1597 (boost::format(
"Invalid mode '%s' given when opening file '%s'") % mode % filename).
str());
1605 : fptr(nullptr), status(0), behavior(behavior_) {
1606 using Reallocator =
void *(*)(
void *,
std::size_t);
1609 if (mode ==
"r" || mode ==
"rb") {
1610 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READONLY, &manager._ptr,
1611 &manager._len, 0,
nullptr,
1613 }
else if (mode ==
"w" || mode ==
"wb") {
1614 Reallocator reallocator =
nullptr;
1616 fits_create_memfile(
reinterpret_cast<fitsfile **
>(&
fptr), &manager._ptr, &manager._len, 0,
1619 }
else if (mode ==
"a" || mode ==
"ab") {
1620 Reallocator reallocator =
nullptr;
1622 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READWRITE, &manager._ptr,
1623 &manager._len, 0, reallocator, &
status);
1625 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1626 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1631 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1635 (boost::format(
"Invalid mode '%s' given when opening memory file at '%s'") % mode %
1641 *
this, boost::format(
"Opening memory file at '%s' with mode '%s'") % manager._ptr % mode);
1646 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &
status);
1653 auto combined = std::make_shared<daf::base::PropertyList>();
1654 bool const asScalar =
true;
1655 for (
auto const &
name : first.getOrderedNames()) {
1656 auto const iscv = isCommentIsValid(first,
name);
1657 if (iscv.isComment) {
1662 combined->copy(
name, first,
name, asScalar);
1665 for (
auto const &
name : second.getOrderedNames()) {
1666 auto const iscv = isCommentIsValid(second,
name);
1667 if (iscv.isComment) {
1673 combined->copy(
name, second,
name, asScalar);
1694template <
typename T,
typename... Args>
1722 auto metadata = std::make_shared<lsst::daf::base::PropertyList>();
1725 int oldHdu = fitsfile.
getHdu();
1726 if (oldHdu != 0 && metadata->exists(
"INHERIT")) {
1727 bool inherit =
false;
1728 if (metadata->typeOf(
"INHERIT") ==
typeid(
std::string)) {
1729 inherit = (metadata->get<
std::string>(
"INHERIT") ==
"T");
1731 inherit = metadata->get<
bool>(
"INHERIT");
1733 if (
strip) metadata->remove(
"INHERIT");
1739 auto primaryHduMetadata = std::make_shared<daf::base::PropertyList>();
1744 auto const emptyMetadata = std::make_shared<lsst::daf::base::PropertyList>();
1754 _oldHdu(_fits.getHdu()),
1757 _fits.
setHdu(hdu, relative);
1779 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1785 fits_get_img_dim(fits, &naxis, &
status);
1794 bool isCompressed = fits_is_compressed_image(fits, &
status);
1801 return isCompressed;
1808 config.getAsDouble(
"compression.quantizeLevel")),
1810 config.getAsInt(
"scaling.bitpix"),
1811 config.exists(
"scaling.maskPlanes") ? config.getArray<
std::string>(
"scaling.maskPlanes")
1813 config.getAsInt(
"scaling.seed"), config.getAsDouble(
"scaling.quantizeLevel"),
1814 config.getAsDouble(
"scaling.quantizePad"), config.get<
bool>(
"scaling.fuzz"),
1815 config.getAsDouble(
"scaling.bscale"), config.getAsDouble(
"scaling.bzero")) {}
1819template <
typename T>
1825template <
typename T>
1826void validateEntry(daf::base::PropertySet &output, daf::base::PropertySet
const &input,
1828 output.add(
name, input.exists(
name) ? input.getArray<T>(
name) : defaultValue);
1834 auto validated = std::make_shared<daf::base::PropertySet>();
1836 validateEntry(*validated, config,
"compression.algorithm",
std::string(
"NONE"));
1837 validateEntry(*validated, config,
"compression.columns", 0);
1838 validateEntry(*validated, config,
"compression.rows", 1);
1839 validateEntry(*validated, config,
"compression.quantizeLevel", 0.0);
1841 validateEntry(*validated, config,
"scaling.algorithm",
std::string(
"NONE"));
1842 validateEntry(*validated, config,
"scaling.bitpix", 0);
1844 validateEntry(*validated, config,
"scaling.seed", 1);
1845 validateEntry(*validated, config,
"scaling.quantizeLevel", 5.0);
1846 validateEntry(*validated, config,
"scaling.quantizePad", 10.0);
1847 validateEntry(*validated, config,
"scaling.fuzz",
true);
1848 validateEntry(*validated, config,
"scaling.bscale", 1.0);
1849 validateEntry(*validated, config,
"scaling.bzero", 0.0);
1852 for (
auto const &
name : config.
names(
false)) {
1853 if (!validated->exists(
name)) {
1855 os <<
"Invalid image write option: " <<
name;
1863#define INSTANTIATE_KEY_OPS(r, data, T) \
1864 template void Fits::updateKey(std::string const &, T const &, std::string const &); \
1865 template void Fits::writeKey(std::string const &, T const &, std::string const &); \
1866 template void Fits::updateKey(std::string const &, T const &); \
1867 template void Fits::writeKey(std::string const &, T const &); \
1868 template void Fits::updateColumnKey(std::string const &, int, T const &, std::string const &); \
1869 template void Fits::writeColumnKey(std::string const &, int, T const &, std::string const &); \
1870 template void Fits::updateColumnKey(std::string const &, int, T const &); \
1871 template void Fits::writeColumnKey(std::string const &, int, T const &); \
1872 template void Fits::readKey(std::string const &, T &);
1874#define INSTANTIATE_IMAGE_OPS(r, data, T) \
1875 template void Fits::writeImageImpl(T const *, int); \
1876 template void Fits::writeImage(image::ImageBase<T> const &, ImageWriteOptions const &, \
1877 daf::base::PropertySet const *, \
1878 image::Mask<image::MaskPixel> const *); \
1879 template void Fits::writeImage(image::ImageBase<T> const &, ImageWriteOptions const &, \
1880 std::shared_ptr<daf::base::PropertySet const>, \
1881 std::shared_ptr<image::Mask<image::MaskPixel> const>); \
1882 template void Fits::readImageImpl(int, T *, long *, long *, long *); \
1883 template bool Fits::checkImageType<T>(); \
1884 template int getBitPix<T>();
1886#define INSTANTIATE_TABLE_OPS(r, data, T) \
1887 template int Fits::addColumn<T>(std::string const &ttype, int size); \
1888 template int Fits::addColumn<T>(std::string const &ttype, int size, std::string const &comment);
1889#define INSTANTIATE_TABLE_ARRAY_OPS(r, data, T) \
1890 template void Fits::writeTableArray(std::size_t row, int col, int nElements, T const *value); \
1891 template void Fits::readTableArray(std::size_t row, int col, int nElements, T *value);
1898 (bool)(unsigned char)(short)(unsigned short)(int)(unsigned int)(long)(unsigned long)(LONGLONG)( \
1899 float)(double)(std::complex<float>)(std::complex<double>)(std::string)
1901#define COLUMN_TYPES \
1902 (bool)(std::string)(std::int8_t)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t) \
1903 (std::int64_t)(float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1905#define COLUMN_ARRAY_TYPES \
1906 (bool)(char)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t)(std::int64_t)( \
1907 float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1909#define IMAGE_TYPES \
1910 (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.
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.
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.
int64_t getAsInt64(std::string const &name) const
Get the last value for a bool/char/short/int/int64_t property name (possibly hierarchical).
std::vector< std::string > names(bool topLevelOnly=true) const
Get the names in the PropertySet, optionally including those in subproperties.
T get(std::string const &name) const
Get the last value for a property name (possibly hierarchical).
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 invalid arguments.
Reports errors that are due to events beyond the control of the program.
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.
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.
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.