8 #include <unordered_set>
9 #include <unordered_map>
18 #include "boost/algorithm/string.hpp"
19 #include "boost/preprocessor/seq/for_each.hpp"
20 #include "boost/format.hpp"
49 daf::base::PropertySet
const &metadata) {
51 for (
auto const &fullName : paramNames) {
53 auto name = (lastPeriod == std::string::npos) ? fullName : fullName.substr(lastPeriod + 1);
58 if (
name.size() > 8) {
63 if (
type ==
typeid(
bool)) {
64 out += metadata.get<
bool>(
name) ?
"T" :
"F";
67 }
else if (
type ==
typeid(
int)) {
69 }
else if (
type ==
typeid(
double)) {
70 double value = metadata.get<
double>(
name);
77 BOOST_CURRENT_FUNCTION %
name);
81 }
else if (
type ==
typeid(
float)) {
82 float value = metadata.get<
float>(
name);
88 BOOST_CURRENT_FUNCTION %
name);
96 if (out.
size() > 80) {
101 int const len = out.
size();
104 }
else if (len > 80) {
107 "Formatted data too long: " +
std::to_string(len) +
" > 80: \"" + out +
"\"");
124 class StringStartSet {
128 for (
auto const &word : input) {
130 if (size < _minSize) {
134 for (
auto const &word : input) {
136 assert(_words.
count(start) == 0);
137 _words[start] = word;
143 auto const iter = _words.
find(startString(key));
169 "SIMPLE",
"BITPIX",
"NAXIS",
"EXTEND",
"GCOUNT",
"PCOUNT",
"XTENSION",
"TFIELDS",
"BSCALE",
"BZERO",
171 "ZBITPIX",
"ZIMAGE",
"ZCMPTYPE",
"ZSIMPLE",
"ZEXTEND",
"ZBLANK",
"ZDATASUM",
"ZHECKSUM",
"ZQUANTIZ",
173 "DATASUM",
"CHECKSUM"};
180 StringStartSet
const ignoreKeyStarts{
181 "NAXIS",
"TZERO",
"TSCAL",
183 "ZNAXIS",
"ZTILE",
"ZNAME",
"ZVAL"};
190 StringStartSet
const ignoreKeyStartsWrite{
"TFORM",
"TTYPE"};
194 if (s.
empty())
return s;
197 return s.
substr(i1, (i1 == std::string::npos) ? 0 : 1 + i2 - i1);
202 char getFormatCode(
bool *) {
return 'X'; }
211 char getFormatCode(
float *) {
return 'E'; }
212 char getFormatCode(
double *) {
return 'D'; }
219 template <
typename T>
222 return (
boost::format(
"%d%c") % size % getFormatCode((
T *)
nullptr)).str();
223 }
else if (size < 0) {
225 return (
boost::format(
"1Q%c(%d)") % getFormatCode((
T *)
nullptr) % (-size)).str();
228 return (
boost::format(
"1Q%c") % getFormatCode((
T *)
nullptr)).str();
234 template <
typename T>
238 struct FitsType<bool> {
239 static int const CONSTANT = TLOGICAL;
242 struct FitsType<char> {
243 static int const CONSTANT = TSTRING;
246 struct FitsType<signed char> {
247 static int const CONSTANT = TSBYTE;
250 struct FitsType<unsigned char> {
251 static int const CONSTANT = TBYTE;
254 struct FitsType<short> {
255 static int const CONSTANT = TSHORT;
258 struct FitsType<unsigned short> {
259 static int const CONSTANT = TUSHORT;
262 struct FitsType<int> {
263 static int const CONSTANT = TINT;
266 struct FitsType<unsigned int> {
267 static int const CONSTANT = TUINT;
270 struct FitsType<long> {
271 static int const CONSTANT = TLONG;
274 struct FitsType<unsigned long> {
275 static int const CONSTANT = TULONG;
278 struct FitsType<long long> {
279 static int const CONSTANT = TLONGLONG;
282 struct FitsType<unsigned long long> {
283 static int const CONSTANT = TLONGLONG;
286 struct FitsType<float> {
287 static int const CONSTANT = TFLOAT;
290 struct FitsType<double> {
291 static int const CONSTANT = TDOUBLE;
295 static int const CONSTANT = TDOUBLE;
298 struct FitsType<
std::complex<float> > {
299 static int const CONSTANT = TCOMPLEX;
302 struct FitsType<
std::complex<double> > {
303 static int const CONSTANT = TDBLCOMPLEX;
307 template <
typename T>
308 struct FitsTableType :
public FitsType<T> {};
310 struct FitsTableType<bool> {
311 static int const CONSTANT = TBIT;
314 template <
typename T>
318 struct FitsBitPix<unsigned char> {
319 static int const CONSTANT = BYTE_IMG;
322 struct FitsBitPix<short> {
323 static int const CONSTANT = SHORT_IMG;
326 struct FitsBitPix<unsigned short> {
327 static int const CONSTANT = USHORT_IMG;
330 struct FitsBitPix<int> {
331 static int const CONSTANT = LONG_IMG;
334 struct FitsBitPix<unsigned int> {
335 static int const CONSTANT = ULONG_IMG;
338 struct FitsBitPix<
std::int64_t> {
339 static int const CONSTANT = LONGLONG_IMG;
342 struct FitsBitPix<
std::uint64_t> {
343 static int const CONSTANT = LONGLONG_IMG;
346 struct FitsBitPix<float> {
347 static int const CONSTANT = FLOAT_IMG;
350 struct FitsBitPix<double> {
351 static int const CONSTANT = DOUBLE_IMG;
354 bool isFitsImageTypeSigned(
int constant) {
356 case BYTE_IMG:
return false;
357 case SHORT_IMG:
return true;
358 case USHORT_IMG:
return false;
359 case LONG_IMG:
return true;
360 case ULONG_IMG:
return false;
361 case LONGLONG_IMG:
return true;
362 case FLOAT_IMG:
return true;
363 case DOUBLE_IMG:
return true;
365 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
"Invalid constant.");
368 static bool allowImageCompression =
true;
370 int fitsTypeForBitpix(
int bitpix) {
386 os <<
"Invalid bitpix value: " << bitpix;
387 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
os.str());
409 ItemInfo isCommentIsValid(daf::base::PropertyList
const &pl,
std::string const &
name) {
410 if (!pl.exists(
name)) {
411 return ItemInfo(
false,
false);
414 if ((
name ==
"COMMENT") || (
name ==
"HISTORY")) {
417 return ItemInfo(
false,
true);
428 os <<
"cfitsio error";
429 if (fileName !=
"") {
430 os <<
" (" << fileName <<
")";
433 char fitsErrMsg[FLEN_ERRMSG];
434 fits_get_errstatus(status, fitsErrMsg);
435 os <<
": " << fitsErrMsg <<
" (" << status <<
")";
440 os <<
"\ncfitsio error stack:\n";
441 char cfitsioMsg[FLEN_ERRMSG];
442 while (fits_read_errmsg(cfitsioMsg) != 0) {
443 os <<
" " << cfitsioMsg <<
"\n";
450 fitsfile *fd =
reinterpret_cast<fitsfile *
>(fptr);
451 if (fd !=
nullptr && fd->Fptr !=
nullptr && fd->Fptr->filename !=
nullptr) {
452 fileName = fd->Fptr->filename;
467 for (
auto const &
name : allParamNames) {
472 return makeLimitedFitsHeaderImpl(desiredParamNames, metadata);
489 template <
typename T>
491 return FitsBitPix<T>::CONSTANT;
500 fitsfile *fd =
reinterpret_cast<fitsfile *
>(
fptr);
501 if (fd !=
nullptr && fd->Fptr !=
nullptr && fd->Fptr->filename !=
nullptr) {
502 fileName = fd->Fptr->filename;
509 fits_get_hdu_num(
reinterpret_cast<fitsfile *
>(
fptr), &n);
515 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu,
nullptr, &
status);
521 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu + 1,
nullptr, &
status);
526 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), 1,
nullptr, &tmpStatus);
536 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &n, &
status);
555 std::string nonFiniteDoubleToString(
double value) {
573 double stringToNonFiniteDouble(
std::string const &value) {
574 if (value ==
"NAN") {
577 if (value ==
"+INFINITY") {
580 if (value ==
"-INFINITY") {
586 template <
typename T>
587 void updateKeyImpl(Fits &
fits,
char const *key,
T const &value,
char const *comment) {
588 fits_update_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
589 const_cast<T *
>(&value),
const_cast<char *
>(comment), &
fits.
status);
592 void updateKeyImpl(Fits &
fits,
char const *key,
std::string const &value,
char const *comment) {
593 fits_update_key_longstr(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key),
594 const_cast<char *
>(value.c_str()),
const_cast<char *
>(comment), &
fits.
status);
597 void updateKeyImpl(Fits &
fits,
char const *key,
bool const &value,
char const *comment) {
599 fits_update_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), TLOGICAL,
const_cast<char *
>(key), &v,
603 void updateKeyImpl(Fits &
fits,
char const *key,
double const &value,
char const *comment) {
604 std::string strValue = nonFiniteDoubleToString(value);
605 if (!strValue.
empty()) {
606 updateKeyImpl(
fits, key, strValue, comment);
608 fits_update_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<double>::CONSTANT,
609 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
614 template <
typename T>
615 void writeKeyImpl(Fits &
fits,
char const *key,
T const &value,
char const *comment) {
616 fits_write_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
617 const_cast<T *
>(&value),
const_cast<char *
>(comment), &
fits.
status);
620 void writeKeyImpl(Fits &
fits,
char const *key,
char const *comment) {
622 fits_write_key_null(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key),
626 void writeKeyImpl(Fits &
fits,
char const *key,
std::string const &value,
char const *comment) {
627 if (
strncmp(key,
"COMMENT", 7) == 0) {
628 fits_write_comment(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(value.c_str()),
630 }
else if (
strncmp(key,
"HISTORY", 7) == 0) {
631 fits_write_history(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(value.c_str()),
634 fits_write_key_longstr(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key),
635 const_cast<char *
>(value.c_str()),
const_cast<char *
>(comment), &
fits.
status);
639 void writeKeyImpl(Fits &
fits,
char const *key,
bool const &value,
char const *comment) {
641 fits_write_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), TLOGICAL,
const_cast<char *
>(key), &v,
645 void writeKeyImpl(Fits &
fits,
char const *key,
double const &value,
char const *comment) {
646 std::string strValue = nonFiniteDoubleToString(value);
647 if (!strValue.
empty()) {
648 writeKeyImpl(
fits, key, strValue, comment);
650 fits_write_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<double>::CONSTANT,
651 const_cast<char *
>(key),
const_cast<double *
>(&value),
const_cast<char *
>(comment),
658 template <
typename T>
660 updateKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
666 template <
typename T>
668 writeKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
674 template <
typename T>
676 updateKeyImpl(*
this, key.
c_str(), value,
nullptr);
682 template <
typename T>
684 writeKeyImpl(*
this, key.
c_str(), value,
nullptr);
690 template <
typename T>
698 template <
typename T>
706 template <
typename T>
714 template <
typename T>
726 template <
typename T>
727 void readKeyImpl(
Fits &
fits,
char const *key,
T &value) {
728 fits_read_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<T>::CONSTANT,
const_cast<char *
>(key),
734 fits_read_key_longstr(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key), &buf,
nullptr,
742 void readKeyImpl(Fits &
fits,
char const *key,
double &value) {
746 char buf[FLEN_VALUE];
747 fits_read_keyword(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(key), buf,
nullptr, &
fits.
status);
753 readKeyImpl(
fits, key, unquoted);
757 value = stringToNonFiniteDouble(unquoted);
760 afw::fits::FitsError,
761 (
boost::format(
"Unrecognised string value for keyword '%s' when parsing as double: %s") %
766 fits_read_key(
reinterpret_cast<fitsfile *
>(
fits.
fptr), FitsType<double>::CONSTANT,
767 const_cast<char *
>(key), &value,
nullptr, &
fits.
status);
773 template <
typename T>
775 readKeyImpl(*
this, key.
c_str(), value);
786 fits_get_hdrspace(
reinterpret_cast<fitsfile *
>(
fptr), &nKeys,
nullptr, &
status);
792 fits_read_keyn(
reinterpret_cast<fitsfile *
>(
fptr), i, key, value, comment, &
status);
796 boost::to_upper(upperKey);
797 if (upperKey.
compare(key) != 0){
799 boost::format(
"In %s, standardizing key '%s' to uppercase '%s' on read.") %
800 BOOST_CURRENT_FUNCTION % key % upperKey);
804 commentStr = comment;
806 while (valueStr.
size() > 2 && valueStr[valueStr.
size() - 2] ==
'&' && i <= nKeys) {
808 fits_read_record(
reinterpret_cast<fitsfile *
>(
fptr), i, key, &
status);
809 if (strncmp(key,
"CONTINUE", 8) != 0) {
816 if (firstQuote == std::string::npos) {
821 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
824 if (lastQuote == std::string::npos) {
829 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
831 valueStr += card.
substr(firstQuote + 1, lastQuote - firstQuote);
833 if (slash != std::string::npos) {
841 functor(keyStr, valueStr, commentStr);
850 return ((ignoreKeys.
find(key) != ignoreKeys.
end()) || ignoreKeyStarts.matches(key) ||
851 (
write && ignoreKeyStartsWrite.matches(key)));
854 class MetadataIterationFunctor :
public HeaderIterationFunctor {
858 template <
typename T>
864 if (
list->exists(key) &&
list->isUndefined(key)) {
866 boost::format(
"In %s, replacing undefined value for key '%s'.") %
867 BOOST_CURRENT_FUNCTION % key);
868 list->set(key, value, comment);
870 list->add(key, value, comment);
873 if (
set->exists(key) &&
set->isUndefined(key)) {
875 boost::format(
"In %s, replacing undefined value for key '%s'.") %
876 BOOST_CURRENT_FUNCTION % key);
877 set->set(key, value);
879 set->add(key, value);
889 if (
list->exists(key) && !
list->isUndefined(key)) {
893 boost::format(
"In %s, dropping undefined value for key '%s'.") %
894 BOOST_CURRENT_FUNCTION % key);
896 list->add(key,
nullptr, comment);
899 if (
set->exists(key) && !
set->isUndefined(key)) {
903 boost::format(
"In %s, dropping undefined value for key '%s'.") %
904 BOOST_CURRENT_FUNCTION % key);
906 set->add(key,
nullptr);
912 daf::base::PropertySet *
set;
919 static std::regex const intRegex(
"[+-]?[0-9]+");
920 static std::regex const doubleRegex(
"[+-]?([0-9]*\\.?[0-9]+|[0-9]+\\.?[0-9]*)([eE][+-]?[0-9]+)?");
921 static std::regex const fitsStringRegex(
"'(.*?) *'");
923 static std::regex const fitsDefinitionCommentRegex(
924 " *(FITS \\(Flexible Image Transport System\\)|and Astrophysics', volume 376, page 359).*");
927 if (
strip && isKeyIgnored(key)) {
934 add(key,
bool(value ==
"T" || value ==
"t"), comment);
939 if (
val < (1LL << 31) &&
val > -(1LL << 31)) {
940 add(key,
static_cast<int>(
val), comment);
942 add(key,
val, comment);
948 add(key,
val, comment);
951 double val = stringToNonFiniteDouble(str);
953 add(key,
val, comment);
955 add(key, str, comment);
957 }
else if (key ==
"HISTORY") {
958 add(key, comment,
"");
960 add(key, comment,
"");
961 }
else if (key.
empty() && value.empty()) {
966 add(
"COMMENT", comment,
"");
967 }
else if (value.empty()) {
970 if (key !=
"COMMENT") {
975 afw::fits::FitsError,
976 (
boost::format(
"Could not parse header value for key '%s': '%s'") % key % value).str());
980 void writeKeyFromProperty(Fits &
fits, daf::base::PropertySet
const &metadata,
std::string const &key,
981 char const *comment =
nullptr) {
983 boost::to_upper(upperKey);
984 if (upperKey.compare(key) != 0){
986 boost::format(
"In %s, key '%s' may be standardized to uppercase '%s' on write.") %
987 BOOST_CURRENT_FUNCTION % key % upperKey);
990 if (valueType ==
typeid(
bool)) {
991 if (metadata.isArray(key)) {
995 writeKeyImpl(
fits, key.
c_str(),
static_cast<bool>(tmp[i]), comment);
998 writeKeyImpl(
fits, key.
c_str(), metadata.get<
bool>(key), comment);
1001 if (metadata.isArray(key)) {
1004 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1009 }
else if (valueType ==
typeid(
int)) {
1010 if (metadata.isArray(key)) {
1013 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1016 writeKeyImpl(
fits, key.
c_str(), metadata.get<
int>(key), comment);
1018 }
else if (valueType ==
typeid(
long)) {
1019 if (metadata.isArray(key)) {
1022 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1025 writeKeyImpl(
fits, key.
c_str(), metadata.get<
long>(key), comment);
1027 }
else if (valueType ==
typeid(
long long)) {
1028 if (metadata.isArray(key)) {
1031 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1034 writeKeyImpl(
fits, key.
c_str(), metadata.get<
long long>(key), comment);
1037 if (metadata.isArray(key)) {
1040 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1045 }
else if (valueType ==
typeid(
double)) {
1046 if (metadata.isArray(key)) {
1049 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1052 writeKeyImpl(
fits, key.
c_str(), metadata.get<
double>(key), comment);
1055 if (metadata.isArray(key)) {
1058 writeKeyImpl(
fits, key.
c_str(), tmp[i], comment);
1064 if (metadata.isArray(key)) {
1068 writeKeyImpl(
fits, key.
c_str(), comment);
1071 writeKeyImpl(
fits, key.
c_str(), comment);
1078 BOOST_CURRENT_FUNCTION % valueType.
name() % key));
1088 MetadataIterationFunctor f;
1098 NameList paramNames;
1104 for (
auto const ¶mName : paramNames) {
1105 if (!isKeyIgnored(paramName,
true)) {
1107 writeKeyFromProperty(*
this, metadata, paramName, pl->
getComment(paramName).
c_str());
1109 writeKeyFromProperty(*
this, metadata, paramName);
1118 char *ttype =
nullptr;
1119 char *tform =
nullptr;
1120 fits_create_tbl(
reinterpret_cast<fitsfile *
>(
fptr), BINARY_TBL, 0, 0, &ttype, &tform,
nullptr,
nullptr, &
status);
1126 template <
typename T>
1129 fits_get_num_cols(
reinterpret_cast<fitsfile *
>(
fptr), &nCols, &
status);
1131 fits_insert_col(
reinterpret_cast<fitsfile *
>(
fptr), nCols + 1,
const_cast<char *
>(ttype.
c_str()),
1139 template <
typename T>
1141 int nCols = addColumn<T>(ttype, size);
1151 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(
fptr), &
first, &
status);
1152 fits_insert_rows(
reinterpret_cast<fitsfile *
>(
fptr),
first, nRows, &
status);
1161 fits_get_num_rows(
reinterpret_cast<fitsfile *
>(
fptr), &r, &
status);
1168 template <
typename T>
1170 fits_write_col(
reinterpret_cast<fitsfile *
>(
fptr), FitsTableType<T>::CONSTANT,
col + 1,
row + 1, 1,
1171 nElements,
const_cast<T *
>(value), &
status);
1183 char const *tmp = value.c_str();
1184 fits_write_col(
reinterpret_cast<fitsfile *
>(
fptr), TSTRING,
col + 1,
row + 1, 1, 1,
1185 const_cast<char const **
>(&tmp), &
status);
1191 template <
typename T>
1194 fits_read_col(
reinterpret_cast<fitsfile *
>(
fptr), FitsTableType<T>::CONSTANT,
col + 1,
row + 1, 1,
1195 nElements,
nullptr, value, &anynul, &
status);
1208 char *tmp = &buf.
front();
1209 fits_read_col(
reinterpret_cast<fitsfile *
>(
fptr), TSTRING,
col + 1,
row + 1, 1, 1,
nullptr, &tmp, &anynul,
1221 fits_get_coltype(
reinterpret_cast<fitsfile *
>(
fptr),
col + 1, &typecode, &
result, &width, &
status);
1242 fits_create_img(
reinterpret_cast<fitsfile *
>(
fptr), 8, 0, &naxes, &
status);
1248 void Fits::createImageImpl(
int bitpix,
int naxis,
long const *naxes) {
1249 fits_create_img(
reinterpret_cast<fitsfile *
>(
fptr), bitpix, naxis,
const_cast<long *
>(naxes), &
status);
1255 template <
typename T>
1256 void Fits::writeImageImpl(
T const *
data,
int nElements) {
1257 fits_write_img(
reinterpret_cast<fitsfile *
>(
fptr), FitsType<T>::CONSTANT, 1, nElements,
1270 struct ImageCompressionContext {
1272 ImageCompressionContext(Fits &fits_, ImageCompressionOptions
const &useThis)
1273 :
fits(fits_), old(
fits.getImageCompression()) {
1274 fits.setImageCompression(useThis);
1276 ~ImageCompressionContext() {
1280 fits.setImageCompression(old);
1290 ImageCompressionOptions old;
1295 template <
typename T>
1299 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1301 image.getBBox().getArea() > 0
1305 ImageCompressionContext comp(*
this, compression);
1313 ndarray::Vector<long, 2> dims(
image.getArray().getShape().reverse());
1321 copy->combine(wcsMetadata);
1324 header = wcsMetadata;
1347 int const fitsType =
scale.bitpix == 0 ? FitsType<T>::CONSTANT : fitsTypeForBitpix(
scale.bitpix);
1348 fits_write_img(
fits, fitsType, 1, pixels->getNumElements(),
const_cast<void *
>(pixels->getData()),
1360 if (
scale.bzero != 0.0) {
1361 fits_write_key_lng(
fits,
"BZERO",
static_cast<long>(
scale.bzero),
1362 "Scaling: MEMORY = BZERO + BSCALE * DISK", &
status);
1364 if (
scale.bscale != 1.0) {
1365 fits_write_key_lng(
fits,
"BSCALE",
static_cast<long>(
scale.bscale),
1366 "Scaling: MEMORY = BZERO + BSCALE * DISK", &
status);
1369 fits_write_key_dbl(
fits,
"BZERO",
scale.bzero, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1371 fits_write_key_dbl(
fits,
"BSCALE",
scale.bscale, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1380 fits_write_key_lng(
fits,
"BLANK",
scale.blank,
"Value for undefined pixels", &
status);
1382 fits_write_key_str(
fits,
"ZQUANTIZ",
"SUBTRACTIVE_DITHER_1",
"Dithering algorithm", &
status);
1400 template <
typename T,
class Enable =
void>
1402 static T constexpr value = 0;
1406 template <
typename T>
1407 struct NullValue<
T, typename
std::enable_if<std::numeric_limits<T>::has_quiet_NaN>
::type> {
1413 template <
typename T>
1414 void Fits::readImageImpl(
int nAxis,
T *
data,
long *begin,
long *
end,
long *increment) {
1415 T null = NullValue<T>::value;
1417 fits_read_subset(
reinterpret_cast<fitsfile *
>(
fptr), FitsType<T>::CONSTANT, begin,
end, increment,
1418 reinterpret_cast<void *
>(&
null),
data, &anyNulls, &
status);
1424 fits_get_img_dim(
reinterpret_cast<fitsfile *
>(
fptr), &nAxis, &
status);
1429 void Fits::getImageShapeImpl(
int maxDim,
long *nAxes) {
1430 fits_get_img_size(
reinterpret_cast<fitsfile *
>(
fptr), maxDim, nAxes, &
status);
1434 template <
typename T>
1437 fits_get_img_equivtype(
reinterpret_cast<fitsfile *
>(
fptr), &imageType, &
status);
1440 if (imageType < 0) {
1444 if (isFitsImageTypeSigned(imageType)) {
1445 return FitsBitPix<T>::CONSTANT >= imageType;
1448 return FitsBitPix<T>::CONSTANT > imageType;
1451 if (!isFitsImageTypeSigned(imageType)) {
1452 return FitsBitPix<T>::CONSTANT >= imageType;
1453 }
else if (imageType == LONGLONG_IMG) {
1456 return FitsBitPix<T>::CONSTANT >= imageType;
1468 fits_get_img_equivtype(
reinterpret_cast<fitsfile *
>(
fptr), &bitpix, &
status);
1481 case BYTE_IMG:
return "uint8";
1482 case SBYTE_IMG:
return "int8";
1483 case SHORT_IMG:
return "int16";
1484 case USHORT_IMG:
return "uint16";
1485 case LONG_IMG:
return "int32";
1486 case ULONG_IMG:
return "uint32";
1487 case LONGLONG_IMG:
return "int64";
1491 (
boost::format(
"Unrecognized BITPIX value: %d") % bitpix).str()
1496 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1498 fits_get_compression_type(
fits, &compType, &
status);
1504 fits_get_tile_dim(
fits, tiles.getNumElements(), tiles.getData(), &
status);
1509 float quantizeLevel;
1510 fits_get_quantize_level(
fits, &quantizeLevel, &
status);
1519 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1520 fits_unset_compression_request(
fits, &
status);
1553 : fptr(nullptr), status(0), behavior(behavior_) {
1554 if (mode ==
"r" || mode ==
"rb") {
1555 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READONLY,
1557 }
else if (mode ==
"w" || mode ==
"wb") {
1558 std::filesystem::remove(filename);
1559 fits_create_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), &
status);
1560 }
else if (mode ==
"a" || mode ==
"ab") {
1561 fits_open_file(
reinterpret_cast<fitsfile **
>(&
fptr),
const_cast<char *
>(filename.
c_str()), READWRITE,
1564 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1565 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1570 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1575 (
boost::format(
"Invalid mode '%s' given when opening file '%s'") % mode % filename).str());
1583 : fptr(nullptr), status(0), behavior(behavior_) {
1584 using Reallocator =
void *(*)(
void *,
std::size_t);
1587 if (mode ==
"r" || mode ==
"rb") {
1588 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READONLY, &manager._ptr,
1589 &manager._len, 0,
nullptr,
1591 }
else if (mode ==
"w" || mode ==
"wb") {
1592 Reallocator reallocator =
nullptr;
1594 fits_create_memfile(
reinterpret_cast<fitsfile **
>(&
fptr), &manager._ptr, &manager._len, 0,
1597 }
else if (mode ==
"a" || mode ==
"ab") {
1598 Reallocator reallocator =
nullptr;
1600 fits_open_memfile(
reinterpret_cast<fitsfile **
>(&
fptr),
"unused", READWRITE, &manager._ptr,
1601 &manager._len, 0, reallocator, &
status);
1603 fits_get_num_hdus(
reinterpret_cast<fitsfile *
>(
fptr), &nHdu, &
status);
1604 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), nHdu,
nullptr, &
status);
1609 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &tmpStatus);
1613 (
boost::format(
"Invalid mode '%s' given when opening memory file at '%s'") % mode %
1619 *
this,
boost::format(
"Opening memory file at '%s' with mode '%s'") % manager._ptr % mode);
1624 fits_close_file(
reinterpret_cast<fitsfile *
>(
fptr), &
status);
1631 auto combined = std::make_shared<daf::base::PropertyList>();
1632 bool const asScalar =
true;
1633 for (
auto const &
name :
first->getOrderedNames()) {
1634 auto const iscv = isCommentIsValid(*
first,
name);
1635 if (iscv.isComment) {
1643 for (
auto const &
name :
second->getOrderedNames()) {
1644 auto const iscv = isCommentIsValid(*
second,
name);
1645 if (iscv.isComment) {
1670 auto metadata = std::make_shared<lsst::daf::base::PropertyList>();
1673 int oldHdu = fitsfile.
getHdu();
1674 if (oldHdu != 0 && metadata->exists(
"INHERIT")) {
1675 bool inherit =
false;
1676 if (metadata->typeOf(
"INHERIT") ==
typeid(
std::string)) {
1677 inherit = (metadata->get<
std::string>(
"INHERIT") ==
"T");
1679 inherit = metadata->get<
bool>(
"INHERIT");
1681 if (
strip) metadata->remove(
"INHERIT");
1687 auto primaryHduMetadata = std::make_shared<daf::base::PropertyList>();
1692 auto const emptyMetadata = std::make_shared<lsst::daf::base::PropertyList>();
1702 _oldHdu(_fits.getHdu()),
1705 _fits.
setHdu(hdu, relative);
1727 auto fits =
reinterpret_cast<fitsfile *
>(
fptr);
1742 bool isCompressed = fits_is_compressed_image(
fits, &
status);
1749 return isCompressed;
1756 config.getAsDouble(
"compression.quantizeLevel")),
1758 config.getAsInt(
"scaling.bitpix"),
1759 config.exists(
"scaling.maskPlanes") ? config.getArray<
std::string>(
"scaling.maskPlanes")
1761 config.getAsInt(
"scaling.seed"), config.getAsDouble(
"scaling.quantizeLevel"),
1762 config.getAsDouble(
"scaling.quantizePad"), config.get<
bool>(
"scaling.fuzz"),
1763 config.getAsDouble(
"scaling.bscale"), config.getAsDouble(
"scaling.bzero")) {}
1767 template <
typename T>
1773 template <
typename T>
1774 void validateEntry(daf::base::PropertySet &output, daf::base::PropertySet
const &input,
1776 output.add(
name, input.exists(
name) ? input.getArray<
T>(
name) : defaultValue);
1782 auto validated = std::make_shared<daf::base::PropertySet>();
1784 validateEntry(*validated, config,
"compression.algorithm",
std::string(
"NONE"));
1785 validateEntry(*validated, config,
"compression.columns", 0);
1786 validateEntry(*validated, config,
"compression.rows", 1);
1787 validateEntry(*validated, config,
"compression.quantizeLevel", 0.0);
1789 validateEntry(*validated, config,
"scaling.algorithm",
std::string(
"NONE"));
1790 validateEntry(*validated, config,
"scaling.bitpix", 0);
1792 validateEntry(*validated, config,
"scaling.seed", 1);
1793 validateEntry(*validated, config,
"scaling.quantizeLevel", 5.0);
1794 validateEntry(*validated, config,
"scaling.quantizePad", 10.0);
1795 validateEntry(*validated, config,
"scaling.fuzz",
true);
1796 validateEntry(*validated, config,
"scaling.bscale", 1.0);
1797 validateEntry(*validated, config,
"scaling.bzero", 0.0);
1800 for (
auto const &
name : config.
names(
false)) {
1801 if (!validated->exists(
name)) {
1803 os <<
"Invalid image write option: " <<
name;
1811 #define INSTANTIATE_KEY_OPS(r, data, T) \
1812 template void Fits::updateKey(std::string const &, T const &, std::string const &); \
1813 template void Fits::writeKey(std::string const &, T const &, std::string const &); \
1814 template void Fits::updateKey(std::string const &, T const &); \
1815 template void Fits::writeKey(std::string const &, T const &); \
1816 template void Fits::updateColumnKey(std::string const &, int, T const &, std::string const &); \
1817 template void Fits::writeColumnKey(std::string const &, int, T const &, std::string const &); \
1818 template void Fits::updateColumnKey(std::string const &, int, T const &); \
1819 template void Fits::writeColumnKey(std::string const &, int, T const &); \
1820 template void Fits::readKey(std::string const &, T &);
1822 #define INSTANTIATE_IMAGE_OPS(r, data, T) \
1823 template void Fits::writeImageImpl(T const *, int); \
1824 template void Fits::writeImage(image::ImageBase<T> const &, ImageWriteOptions const &, \
1825 std::shared_ptr<daf::base::PropertySet const>, \
1826 std::shared_ptr<image::Mask<image::MaskPixel> const>); \
1827 template void Fits::readImageImpl(int, T *, long *, long *, long *); \
1828 template bool Fits::checkImageType<T>(); \
1829 template int getBitPix<T>();
1831 #define INSTANTIATE_TABLE_OPS(r, data, T) \
1832 template int Fits::addColumn<T>(std::string const &ttype, int size); \
1833 template int Fits::addColumn<T>(std::string const &ttype, int size, std::string const &comment);
1834 #define INSTANTIATE_TABLE_ARRAY_OPS(r, data, T) \
1835 template void Fits::writeTableArray(std::size_t row, int col, int nElements, T const *value); \
1836 template void Fits::readTableArray(std::size_t row, int col, int nElements, T *value);
1843 (bool)(unsigned char)(short)(unsigned short)(int)(unsigned int)(long)(unsigned long)(LONGLONG)( \
1844 float)(double)(std::complex<float>)(std::complex<double>)(std::string)
1846 #define COLUMN_TYPES \
1847 (bool)(std::string)(std::int8_t)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t) \
1848 (std::int64_t)(float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1850 #define COLUMN_ARRAY_TYPES \
1851 (bool)(char)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t)(std::int64_t)( \
1852 float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1854 #define IMAGE_TYPES \
1855 (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, std::shared_ptr< 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.
std::vector< std::string > names(bool topLevelOnly=true) const
Get the names in the PropertySet, optionally including those in subproperties.
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 > paramNames(bool topLevelOnly=true) const
A variant of names that excludes the names of 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).
A class representing an angle.
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.
def scale(algorithm, min, max=None, frame=None)
const int DEFAULT_HDU
Specify that the default HDU should be read.
ndarray::Array< T const, N, N > const makeContiguousArray(ndarray::Array< T, N, C > const &array)
Construct a contiguous ndarray.
int getBitPix()
Return the cfitsio integer BITPIX code for the given data type.
std::shared_ptr< daf::base::PropertyList > combineMetadata(std::shared_ptr< const daf::base::PropertyList > first, std::shared_ptr< const daf::base::PropertyList > second)
Combine two sets of metadata in a FITS-appropriate fashion.
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.
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.
void write(OutputArchiveHandle &handle) const override
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
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
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.