8 #include <unordered_set>
9 #include <unordered_map>
16 #include "boost/regex.hpp"
17 #include "boost/filesystem.hpp"
18 #include "boost/preprocessor/seq/for_each.hpp"
19 #include "boost/format.hpp"
48 daf::base::PropertySet
const &metadata) {
50 for (
auto const &fullName : paramNames) {
52 auto name = (lastPeriod == std::string::npos) ? fullName : fullName.substr(lastPeriod + 1);
57 if (
name.size() > 8) {
60 out = (boost::format(
"%-8s= ") %
name).str();
62 if (
type ==
typeid(
bool)) {
63 out += metadata.get<
bool>(
name) ?
"T" :
"F";
65 out += (boost::format(
"%20d") % static_cast<int>(metadata.get<
std::uint8_t>(
name))).str();
66 }
else if (
type ==
typeid(
int)) {
67 out += (boost::format(
"%20d") % metadata.get<
int>(
name)).str();
68 }
else if (
type ==
typeid(
double)) {
69 double value = metadata.get<
double>(
name);
72 out += (boost::format(
"%#20.17G") % value).str();
75 boost::format(
"In %s, found NaN in metadata item '%s'") %
76 BOOST_CURRENT_FUNCTION %
name);
80 }
else if (
type ==
typeid(
float)) {
81 float value = metadata.get<
float>(
name);
83 out += (boost::format(
"%#20.15G") % value).str();
86 boost::format(
"In %s, found NaN in metadata item '%s'") %
87 BOOST_CURRENT_FUNCTION %
name);
95 if (out.
size() > 80) {
100 int const len = out.
size();
103 }
else if (len > 80) {
106 "Formatted data too long: " +
std::to_string(len) +
" > 80: \"" + out +
"\"");
123 class StringStartSet {
127 for (
auto const &word : input) {
129 if (size < _minSize) {
133 for (
auto const &word : input) {
135 assert(_words.
count(start) == 0);
136 _words[start] = word;
143 if (iter == _words.
end()) {
148 return key.compare(0, word.
size(), word) == 0;
168 "SIMPLE",
"BITPIX",
"NAXIS",
"EXTEND",
"GCOUNT",
"PCOUNT",
"XTENSION",
"TFIELDS",
"BSCALE",
"BZERO",
170 "ZBITPIX",
"ZIMAGE",
"ZCMPTYPE",
"ZSIMPLE",
"ZEXTEND",
"ZBLANK",
"ZDATASUM",
"ZHECKSUM",
"ZQUANTIZ",
172 "DATASUM",
"CHECKSUM"};
179 StringStartSet
const ignoreKeyStarts{
180 "NAXIS",
"TZERO",
"TSCAL",
182 "ZNAXIS",
"ZTILE",
"ZNAME",
"ZVAL"};
189 StringStartSet
const ignoreKeyStartsWrite{
"TFORM",
"TTYPE"};
193 if (s.
empty())
return s;
196 return s.
substr(i1, (i1 == std::string::npos) ? 0 : 1 + i2 - i1);
201 char getFormatCode(
bool *) {
return 'X'; }
210 char getFormatCode(
float *) {
return 'E'; }
211 char getFormatCode(
double *) {
return 'D'; }
218 template <
typename T>
221 return (boost::format(
"%d%c") % size % getFormatCode((T *)0)).str();
222 }
else if (size < 0) {
224 return (boost::format(
"1Q%c(%d)") % getFormatCode((T *)0) % (-size)).str();
227 return (boost::format(
"1Q%c") % getFormatCode((T *)0)).str();
233 template <
typename T>
237 struct FitsType<bool> {
238 static int const CONSTANT = TLOGICAL;
241 struct FitsType<char> {
242 static int const CONSTANT = TSTRING;
245 struct FitsType<signed char> {
246 static int const CONSTANT = TSBYTE;
249 struct FitsType<unsigned char> {
250 static int const CONSTANT = TBYTE;
253 struct FitsType<short> {
254 static int const CONSTANT = TSHORT;
257 struct FitsType<unsigned short> {
258 static int const CONSTANT = TUSHORT;
261 struct FitsType<int> {
262 static int const CONSTANT = TINT;
265 struct FitsType<unsigned int> {
266 static int const CONSTANT = TUINT;
269 struct FitsType<long> {
270 static int const CONSTANT = TLONG;
273 struct FitsType<unsigned long> {
274 static int const CONSTANT = TULONG;
277 struct FitsType<long long> {
278 static int const CONSTANT = TLONGLONG;
281 struct FitsType<unsigned long long> {
282 static int const CONSTANT = TLONGLONG;
285 struct FitsType<float> {
286 static int const CONSTANT = TFLOAT;
289 struct FitsType<double> {
290 static int const CONSTANT = TDOUBLE;
294 static int const CONSTANT = TDOUBLE;
297 struct FitsType<
std::complex<float> > {
298 static int const CONSTANT = TCOMPLEX;
301 struct FitsType<
std::complex<double> > {
302 static int const CONSTANT = TDBLCOMPLEX;
306 template <
typename T>
307 struct FitsTableType :
public FitsType<T> {};
309 struct FitsTableType<bool> {
310 static int const CONSTANT = TBIT;
313 template <
typename T>
317 struct FitsBitPix<unsigned char> {
318 static int const CONSTANT = BYTE_IMG;
321 struct FitsBitPix<short> {
322 static int const CONSTANT = SHORT_IMG;
325 struct FitsBitPix<unsigned short> {
326 static int const CONSTANT = USHORT_IMG;
329 struct FitsBitPix<int> {
330 static int const CONSTANT = LONG_IMG;
333 struct FitsBitPix<unsigned int> {
334 static int const CONSTANT = ULONG_IMG;
337 struct FitsBitPix<
std::int64_t> {
338 static int const CONSTANT = LONGLONG_IMG;
341 struct FitsBitPix<
std::uint64_t> {
342 static int const CONSTANT = LONGLONG_IMG;
345 struct FitsBitPix<float> {
346 static int const CONSTANT = FLOAT_IMG;
349 struct FitsBitPix<double> {
350 static int const CONSTANT = DOUBLE_IMG;
353 bool isFitsImageTypeSigned(
int constant) {
355 case BYTE_IMG:
return false;
356 case SHORT_IMG:
return true;
357 case USHORT_IMG:
return false;
358 case LONG_IMG:
return true;
359 case ULONG_IMG:
return false;
360 case LONGLONG_IMG:
return true;
361 case FLOAT_IMG:
return true;
362 case DOUBLE_IMG:
return true;
364 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
"Invalid constant.");
367 static bool allowImageCompression =
true;
369 int fitsTypeForBitpix(
int bitpix) {
385 os <<
"Invalid bitpix value: " << bitpix;
386 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
os.str());
408 ItemInfo isCommentIsValid(daf::base::PropertyList
const &pl,
std::string const &
name) {
409 if (!pl.exists(
name)) {
410 return ItemInfo(
false,
false);
413 if ((
name ==
"COMMENT") || (
name ==
"HISTORY")) {
416 return ItemInfo(
false,
true);
427 os <<
"cfitsio error";
428 if (fileName !=
"") {
429 os <<
" (" << fileName <<
")";
432 char fitsErrMsg[FLEN_ERRMSG];
433 fits_get_errstatus(status, fitsErrMsg);
434 os <<
": " << fitsErrMsg <<
" (" << status <<
")";
439 os <<
"\ncfitsio error stack:\n";
440 char cfitsioMsg[FLEN_ERRMSG];
441 while (fits_read_errmsg(cfitsioMsg) != 0) {
442 os <<
" " << cfitsioMsg <<
"\n";
449 fitsfile *fd = reinterpret_cast<fitsfile *>(fptr);
450 if (fd != 0 && fd->Fptr != 0 && fd->Fptr->filename != 0) {
451 fileName = fd->Fptr->filename;
466 for (
auto const &
name : allParamNames) {
471 return makeLimitedFitsHeaderImpl(desiredParamNames, metadata);
488 template <
typename T>
490 return FitsBitPix<T>::CONSTANT;
499 fitsfile *fd = reinterpret_cast<fitsfile *>(
fptr);
500 if (fd != 0 && fd->Fptr != 0 && fd->Fptr->filename != 0) {
501 fileName = fd->Fptr->filename;
508 fits_get_hdu_num(reinterpret_cast<fitsfile *>(
fptr), &n);
514 fits_movrel_hdu(reinterpret_cast<fitsfile *>(
fptr), hdu, 0, &
status);
520 fits_movabs_hdu(reinterpret_cast<fitsfile *>(
fptr), hdu + 1, 0, &
status);
525 fits_movrel_hdu(reinterpret_cast<fitsfile *>(
fptr), 1, 0, &tmpStatus);
535 fits_get_num_hdus(reinterpret_cast<fitsfile *>(
fptr), &n, &
status);
554 std::string nonFiniteDoubleToString(
double value) {
572 double stringToNonFiniteDouble(
std::string const &value) {
573 if (value ==
"NAN") {
576 if (value ==
"+INFINITY") {
579 if (value ==
"-INFINITY") {
585 template <
typename T>
586 void updateKeyImpl(Fits &
fits,
char const *
key, T
const &value,
char const *comment) {
587 fits_update_key(reinterpret_cast<fitsfile *>(
fits.
fptr), FitsType<T>::CONSTANT, const_cast<char *>(
key),
588 const_cast<T *>(&value), const_cast<char *>(comment), &
fits.
status);
591 void updateKeyImpl(Fits &
fits,
char const *
key,
std::string const &value,
char const *comment) {
592 fits_update_key_longstr(reinterpret_cast<fitsfile *>(
fits.
fptr), const_cast<char *>(
key),
593 const_cast<char *>(value.c_str()), const_cast<char *>(comment), &
fits.
status);
596 void updateKeyImpl(Fits &
fits,
char const *
key,
bool const &value,
char const *comment) {
598 fits_update_key(reinterpret_cast<fitsfile *>(
fits.
fptr), TLOGICAL, const_cast<char *>(
key), &v,
602 void updateKeyImpl(Fits &
fits,
char const *
key,
double const &value,
char const *comment) {
603 std::string strValue = nonFiniteDoubleToString(value);
604 if (!strValue.
empty()) {
605 updateKeyImpl(
fits,
key, strValue, comment);
607 fits_update_key(reinterpret_cast<fitsfile *>(
fits.
fptr), FitsType<double>::CONSTANT,
608 const_cast<char *>(
key), const_cast<double *>(&value), const_cast<char *>(comment),
613 template <
typename T>
614 void writeKeyImpl(Fits &
fits,
char const *
key, T
const &value,
char const *comment) {
615 fits_write_key(reinterpret_cast<fitsfile *>(
fits.
fptr), FitsType<T>::CONSTANT, const_cast<char *>(
key),
616 const_cast<T *>(&value), const_cast<char *>(comment), &
fits.
status);
619 void writeKeyImpl(Fits &
fits,
char const *
key,
char const *comment) {
621 fits_write_key_null(reinterpret_cast<fitsfile *>(
fits.
fptr), const_cast<char *>(
key),
625 void writeKeyImpl(Fits &
fits,
char const *
key,
std::string const &value,
char const *comment) {
627 fits_write_comment(reinterpret_cast<fitsfile *>(
fits.
fptr), const_cast<char *>(value.c_str()),
630 fits_write_history(reinterpret_cast<fitsfile *>(
fits.
fptr), const_cast<char *>(value.c_str()),
633 fits_write_key_longstr(reinterpret_cast<fitsfile *>(
fits.
fptr), const_cast<char *>(
key),
634 const_cast<char *>(value.c_str()), const_cast<char *>(comment), &
fits.
status);
638 void writeKeyImpl(Fits &
fits,
char const *
key,
bool const &value,
char const *comment) {
640 fits_write_key(reinterpret_cast<fitsfile *>(
fits.
fptr), TLOGICAL, const_cast<char *>(
key), &v,
644 void writeKeyImpl(Fits &
fits,
char const *
key,
double const &value,
char const *comment) {
645 std::string strValue = nonFiniteDoubleToString(value);
646 if (!strValue.
empty()) {
647 writeKeyImpl(
fits,
key, strValue, comment);
649 fits_write_key(reinterpret_cast<fitsfile *>(
fits.
fptr), FitsType<double>::CONSTANT,
650 const_cast<char *>(
key), const_cast<double *>(&value), const_cast<char *>(comment),
657 template <
typename T>
659 updateKeyImpl(*
this,
key.c_str(), value, comment.
c_str());
665 template <
typename T>
667 writeKeyImpl(*
this,
key.c_str(), value, comment.
c_str());
673 template <
typename T>
675 updateKeyImpl(*
this,
key.c_str(), value, 0);
681 template <
typename T>
683 writeKeyImpl(*
this,
key.c_str(), value, 0);
689 template <
typename T>
691 updateKey((boost::format(
"%s%d") %
prefix % (n + 1)).str(), value, comment);
697 template <
typename T>
699 writeKey((boost::format(
"%s%d") %
prefix % (n + 1)).str(), value, comment);
705 template <
typename T>
713 template <
typename T>
715 writeKey((boost::format(
"%s%d") %
prefix % (n + 1)).str(), value);
725 template <
typename T>
726 void readKeyImpl(
Fits &
fits,
char const *
key, T &value) {
727 fits_read_key(reinterpret_cast<fitsfile *>(
fits.
fptr), FitsType<T>::CONSTANT, const_cast<char *>(
key),
733 fits_read_key_longstr(reinterpret_cast<fitsfile *>(
fits.
fptr), const_cast<char *>(
key), &buf, 0,
741 void readKeyImpl(Fits &
fits,
char const *
key,
double &value) {
745 char buf[FLEN_VALUE];
746 fits_read_keyword(reinterpret_cast<fitsfile *>(
fits.
fptr), const_cast<char *>(
key), buf, 0, &
fits.
status);
752 readKeyImpl(
fits,
key, unquoted);
756 value = stringToNonFiniteDouble(unquoted);
759 afw::fits::FitsError,
760 (boost::format(
"Unrecognised string value for keyword '%s' when parsing as double: %s") %
765 fits_read_key(reinterpret_cast<fitsfile *>(
fits.
fptr), FitsType<double>::CONSTANT,
772 template <
typename T>
774 readKeyImpl(*
this,
key.c_str(), value);
785 fits_get_hdrspace(reinterpret_cast<fitsfile *>(
fptr), &nKeys, 0, &
status);
791 fits_read_keyn(reinterpret_cast<fitsfile *>(
fptr), i,
key, value, comment, &
status);
794 commentStr = comment;
796 while (valueStr.
size() > 2 && valueStr[valueStr.
size() - 2] ==
'&' && i <= nKeys) {
798 fits_read_record(reinterpret_cast<fitsfile *>(
fptr), i,
key, &
status);
799 if (strncmp(
key,
"CONTINUE", 8) != 0) {
806 if (firstQuote == std::string::npos) {
811 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
814 if (lastQuote == std::string::npos) {
819 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
821 valueStr += card.
substr(firstQuote + 1, lastQuote - firstQuote);
823 if (slash != std::string::npos) {
831 functor(keyStr, valueStr, commentStr);
840 return ((ignoreKeys.
find(
key) != ignoreKeys.
end()) || ignoreKeyStarts.matches(
key) ||
841 (write && ignoreKeyStartsWrite.matches(
key)));
844 class MetadataIterationFunctor :
public HeaderIterationFunctor {
848 template <
typename T>
856 boost::format(
"In %s, replacing undefined value for key '%s'.") %
857 BOOST_CURRENT_FUNCTION %
key);
858 list->set(
key, value, comment);
860 list->add(
key, value, comment);
865 boost::format(
"In %s, replacing undefined value for key '%s'.") %
866 BOOST_CURRENT_FUNCTION %
key);
883 boost::format(
"In %s, dropping undefined value for key '%s'.") %
884 BOOST_CURRENT_FUNCTION %
key);
886 list->add(
key,
nullptr, comment);
893 boost::format(
"In %s, dropping undefined value for key '%s'.") %
894 BOOST_CURRENT_FUNCTION %
key);
902 daf::base::PropertySet *
set;
908 static boost::regex
const boolRegex(
"[tTfF]");
909 static boost::regex
const intRegex(
"[+-]?[0-9]+");
910 static boost::regex
const doubleRegex(
"[+-]?([0-9]*\\.?[0-9]+|[0-9]+\\.?[0-9]*)([eE][+-]?[0-9]+)?");
911 static boost::regex
const fitsStringRegex(
"'(.*?) *'");
913 static boost::regex
const fitsDefinitionCommentRegex(
914 " *(FITS \\(Flexible Image Transport System\\)|and Astrophysics', volume 376, page 359).*");
915 boost::smatch matchStrings;
922 if (boost::regex_match(value, boolRegex)) {
924 add(
key,
bool(value ==
"T" || value ==
"t"), comment);
925 }
else if (boost::regex_match(value, intRegex)) {
929 if (val < (1LL << 31) && val > -(1LL << 31)) {
930 add(
key, static_cast<int>(val), comment);
932 add(
key, val, comment);
934 }
else if (boost::regex_match(value, doubleRegex)) {
938 add(
key, val, comment);
939 }
else if (boost::regex_match(value, matchStrings, fitsStringRegex)) {
941 double val = stringToNonFiniteDouble(str);
943 add(
key, val, comment);
945 add(
key, str, comment);
947 }
else if (
key ==
"HISTORY") {
948 add(
key, comment,
"");
949 }
else if (
key ==
"COMMENT" && !(
strip && boost::regex_match(comment, fitsDefinitionCommentRegex))) {
950 add(
key, comment,
"");
951 }
else if (
key.empty() && value.empty()) {
956 add(
"COMMENT", comment,
"");
957 }
else if (value.empty()) {
960 if (
key !=
"COMMENT") {
965 afw::fits::FitsError,
966 (boost::format(
"Could not parse header value for key '%s': '%s'") %
key % value).str());
970 void writeKeyFromProperty(Fits &
fits, daf::base::PropertySet
const &metadata,
std::string const &
key,
971 char const *comment = 0) {
973 if (valueType ==
typeid(
bool)) {
974 if (metadata.isArray(
key)) {
978 writeKeyImpl(
fits,
key.c_str(), static_cast<bool>(tmp[i]), comment);
981 writeKeyImpl(
fits,
key.c_str(), metadata.get<
bool>(
key), comment);
984 if (metadata.isArray(
key)) {
987 writeKeyImpl(
fits,
key.c_str(), tmp[i], comment);
992 }
else if (valueType ==
typeid(
int)) {
993 if (metadata.isArray(
key)) {
996 writeKeyImpl(
fits,
key.c_str(), tmp[i], comment);
999 writeKeyImpl(
fits,
key.c_str(), metadata.get<
int>(
key), comment);
1001 }
else if (valueType ==
typeid(
long)) {
1002 if (metadata.isArray(
key)) {
1005 writeKeyImpl(
fits,
key.c_str(), tmp[i], comment);
1008 writeKeyImpl(
fits,
key.c_str(), metadata.get<
long>(
key), comment);
1010 }
else if (valueType ==
typeid(
long long)) {
1011 if (metadata.isArray(
key)) {
1014 writeKeyImpl(
fits,
key.c_str(), tmp[i], comment);
1017 writeKeyImpl(
fits,
key.c_str(), metadata.get<
long long>(
key), comment);
1020 if (metadata.isArray(
key)) {
1023 writeKeyImpl(
fits,
key.c_str(), tmp[i], comment);
1028 }
else if (valueType ==
typeid(
double)) {
1029 if (metadata.isArray(
key)) {
1032 writeKeyImpl(
fits,
key.c_str(), tmp[i], comment);
1035 writeKeyImpl(
fits,
key.c_str(), metadata.get<
double>(
key), comment);
1038 if (metadata.isArray(
key)) {
1041 writeKeyImpl(
fits,
key.c_str(), tmp[i], comment);
1046 }
else if (valueType ==
typeid(nullptr_t)) {
1047 if (metadata.isArray(
key)) {
1051 writeKeyImpl(
fits,
key.c_str(), comment);
1054 writeKeyImpl(
fits,
key.c_str(), comment);
1060 boost::format(
"In %s, unknown type '%s' for key '%s'.") %
1061 BOOST_CURRENT_FUNCTION % valueType.
name() %
key));
1071 MetadataIterationFunctor f;
1074 f.list = dynamic_cast<daf::base::PropertyList *>(&metadata);
1081 NameList paramNames;
1087 for (NameList::const_iterator i = paramNames.begin(); i != paramNames.end(); ++i) {
1088 if (!isKeyIgnored(*i,
true)) {
1090 writeKeyFromProperty(*
this, metadata, *i, pl->
getComment(*i).
c_str());
1092 writeKeyFromProperty(*
this, metadata, *i);
1103 fits_create_tbl(reinterpret_cast<fitsfile *>(
fptr), BINARY_TBL, 0, 0, &ttype, &tform, 0, 0, &
status);
1109 template <
typename T>
1112 fits_get_num_cols(reinterpret_cast<fitsfile *>(
fptr), &nCols, &
status);
1114 fits_insert_col(reinterpret_cast<fitsfile *>(
fptr), nCols + 1, const_cast<char *>(ttype.
c_str()),
1122 template <
typename T>
1124 int nCols = addColumn<T>(ttype, size);
1134 fits_get_num_rows(reinterpret_cast<fitsfile *>(
fptr), &
first, &
status);
1135 fits_insert_rows(reinterpret_cast<fitsfile *>(
fptr),
first, nRows, &
status);
1144 fits_get_num_rows(reinterpret_cast<fitsfile *>(
fptr), &r, &
status);
1151 template <
typename T>
1153 fits_write_col(reinterpret_cast<fitsfile *>(
fptr), FitsTableType<T>::CONSTANT, col + 1, row + 1, 1,
1154 nElements, const_cast<T *>(value), &
status);
1157 nElements % row % col);
1166 char const *tmp = value.c_str();
1167 fits_write_col(reinterpret_cast<fitsfile *>(
fptr), TSTRING, col + 1, row + 1, 1, 1,
1168 const_cast<char const **>(&tmp), &
status);
1174 template <
typename T>
1177 fits_read_col(reinterpret_cast<fitsfile *>(
fptr), FitsTableType<T>::CONSTANT, col + 1, row + 1, 1,
1178 nElements, 0, value, &anynul, &
status);
1191 char *tmp = &buf.
front();
1192 fits_read_col(reinterpret_cast<fitsfile *>(
fptr), TSTRING, col + 1, row + 1, 1, 1, 0, &tmp, &anynul,
1204 fits_get_coltype(reinterpret_cast<fitsfile *>(
fptr), col + 1, &typecode, &
result, &width, &
status);
1214 fits_read_descript(reinterpret_cast<fitsfile *>(
fptr), col + 1, row + 1, &
result, &offset, &
status);
1225 fits_create_img(reinterpret_cast<fitsfile *>(
fptr), 8, 0, &naxes, &
status);
1231 void Fits::createImageImpl(
int bitpix,
int naxis,
long const *naxes) {
1232 fits_create_img(reinterpret_cast<fitsfile *>(
fptr), bitpix, naxis, const_cast<long *>(naxes), &
status);
1238 template <
typename T>
1239 void Fits::writeImageImpl(T
const *
data,
int nElements) {
1240 fits_write_img(reinterpret_cast<fitsfile *>(
fptr), FitsType<T>::CONSTANT, 1, nElements,
1253 struct ImageCompressionContext {
1255 ImageCompressionContext(Fits &fits_, ImageCompressionOptions
const &useThis)
1256 :
fits(fits_), old(
fits.getImageCompression()) {
1257 fits.setImageCompression(useThis);
1259 ~ImageCompressionContext() {
1263 fits.setImageCompression(old);
1273 ImageCompressionOptions old;
1278 template <
typename T>
1282 auto fits = reinterpret_cast<fitsfile *>(
fptr);
1284 image.getBBox().getArea() > 0
1288 ImageCompressionContext comp(*
this, compression);
1296 ndarray::Vector<long, 2> dims(
image.getArray().getShape().reverse());
1304 copy->combine(wcsMetadata);
1307 header = wcsMetadata;
1330 int const fitsType =
scale.bitpix == 0 ? FitsType<T>::CONSTANT : fitsTypeForBitpix(
scale.bitpix);
1331 fits_write_img(
fits, fitsType, 1, pixels->getNumElements(), const_cast<void *>(pixels->getData()),
1343 if (
scale.bzero != 0.0) {
1344 fits_write_key_lng(
fits,
"BZERO", static_cast<long>(
scale.bzero),
1345 "Scaling: MEMORY = BZERO + BSCALE * DISK", &
status);
1347 if (
scale.bscale != 1.0) {
1348 fits_write_key_lng(
fits,
"BSCALE", static_cast<long>(
scale.bscale),
1349 "Scaling: MEMORY = BZERO + BSCALE * DISK", &
status);
1352 fits_write_key_dbl(
fits,
"BZERO",
scale.bzero, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1354 fits_write_key_dbl(
fits,
"BSCALE",
scale.bscale, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1363 fits_write_key_lng(
fits,
"BLANK",
scale.blank,
"Value for undefined pixels", &
status);
1365 fits_write_key_str(
fits,
"ZQUANTIZ",
"SUBTRACTIVE_DITHER_1",
"Dithering algorithm", &
status);
1383 template <
typename T,
class Enable =
void>
1385 static T constexpr value = 0;
1389 template <
typename T>
1390 struct NullValue<T, typename
std::enable_if<std::numeric_limits<T>::has_quiet_NaN>
::type> {
1396 template <
typename T>
1397 void Fits::readImageImpl(
int nAxis, T *
data,
long *begin,
long *
end,
long *increment) {
1398 T
null = NullValue<T>::value;
1400 fits_read_subset(reinterpret_cast<fitsfile *>(
fptr), FitsType<T>::CONSTANT, begin,
end, increment,
1401 reinterpret_cast<void *>(&
null),
data, &anyNulls, &
status);
1407 fits_get_img_dim(reinterpret_cast<fitsfile *>(
fptr), &nAxis, &
status);
1412 void Fits::getImageShapeImpl(
int maxDim,
long *nAxes) {
1413 fits_get_img_size(reinterpret_cast<fitsfile *>(
fptr), maxDim, nAxes, &
status);
1417 template <
typename T>
1420 fits_get_img_equivtype(reinterpret_cast<fitsfile *>(
fptr), &imageType, &
status);
1423 if (imageType < 0) {
1427 if (isFitsImageTypeSigned(imageType)) {
1428 return FitsBitPix<T>::CONSTANT >= imageType;
1431 return FitsBitPix<T>::CONSTANT > imageType;
1434 if (!isFitsImageTypeSigned(imageType)) {
1435 return FitsBitPix<T>::CONSTANT >= imageType;
1436 }
else if (imageType == LONGLONG_IMG) {
1439 return FitsBitPix<T>::CONSTANT >= imageType;
1451 fits_get_img_equivtype(reinterpret_cast<fitsfile *>(
fptr), &bitpix, &
status);
1464 case BYTE_IMG:
return "uint8";
1465 case SBYTE_IMG:
return "int8";
1466 case SHORT_IMG:
return "int16";
1467 case USHORT_IMG:
return "uint16";
1468 case LONG_IMG:
return "int32";
1469 case ULONG_IMG:
return "uint32";
1470 case LONGLONG_IMG:
return "int64";
1474 (boost::format(
"Unrecognized BITPIX value: %d") % bitpix).str()
1479 auto fits = reinterpret_cast<fitsfile *>(
fptr);
1481 fits_get_compression_type(
fits, &compType, &
status);
1487 fits_get_tile_dim(
fits, tiles.getNumElements(), tiles.getData(), &
status);
1492 float quantizeLevel;
1493 fits_get_quantize_level(
fits, &quantizeLevel, &
status);
1502 auto fits = reinterpret_cast<fitsfile *>(
fptr);
1503 fits_unset_compression_request(
fits, &
status);
1536 : fptr(0), status(0), behavior(behavior_) {
1537 if (mode ==
"r" || mode ==
"rb") {
1538 fits_open_file(reinterpret_cast<fitsfile **>(&
fptr), const_cast<char *>(filename.
c_str()), READONLY,
1540 }
else if (mode ==
"w" || mode ==
"wb") {
1541 boost::filesystem::remove(filename);
1542 fits_create_file(reinterpret_cast<fitsfile **>(&
fptr), const_cast<char *>(filename.
c_str()), &
status);
1543 }
else if (mode ==
"a" || mode ==
"ab") {
1544 fits_open_file(reinterpret_cast<fitsfile **>(&
fptr), const_cast<char *>(filename.
c_str()), READWRITE,
1547 fits_get_num_hdus(reinterpret_cast<fitsfile *>(
fptr), &nHdu, &
status);
1548 fits_movabs_hdu(reinterpret_cast<fitsfile *>(
fptr), nHdu, NULL, &
status);
1553 fits_close_file(reinterpret_cast<fitsfile *>(
fptr), &tmpStatus);
1558 (boost::format(
"Invalid mode '%s' given when opening file '%s'") % mode % filename).str());
1566 : fptr(0), status(0), behavior(behavior_) {
1567 typedef void *(*Reallocator)(
void *,
std::size_t);
1570 if (mode ==
"r" || mode ==
"rb") {
1571 fits_open_memfile(reinterpret_cast<fitsfile **>(&
fptr),
"unused", READONLY, &manager._ptr,
1572 &manager._len, 0, 0,
1574 }
else if (mode ==
"w" || mode ==
"wb") {
1575 Reallocator reallocator = 0;
1577 fits_create_memfile(reinterpret_cast<fitsfile **>(&
fptr), &manager._ptr, &manager._len, 0,
1580 }
else if (mode ==
"a" || mode ==
"ab") {
1581 Reallocator reallocator = 0;
1583 fits_open_memfile(reinterpret_cast<fitsfile **>(&
fptr),
"unused", READWRITE, &manager._ptr,
1584 &manager._len, 0, reallocator, &
status);
1586 fits_get_num_hdus(reinterpret_cast<fitsfile *>(
fptr), &nHdu, &
status);
1587 fits_movabs_hdu(reinterpret_cast<fitsfile *>(
fptr), nHdu, NULL, &
status);
1592 fits_close_file(reinterpret_cast<fitsfile *>(
fptr), &tmpStatus);
1596 (boost::format(
"Invalid mode '%s' given when opening memory file at '%s'") % mode %
1602 *
this, boost::format(
"Opening memory file at '%s' with mode '%s'") % manager._ptr % mode);
1607 fits_close_file(reinterpret_cast<fitsfile *>(
fptr), &
status);
1614 auto combined = std::make_shared<daf::base::PropertyList>();
1615 bool const asScalar =
true;
1616 for (
auto const &
name :
first->getOrderedNames()) {
1617 auto const iscv = isCommentIsValid(*
first,
name);
1618 if (iscv.isComment) {
1626 for (
auto const &
name :
second->getOrderedNames()) {
1627 auto const iscv = isCommentIsValid(*
second,
name);
1628 if (iscv.isComment) {
1653 auto metadata = std::make_shared<lsst::daf::base::PropertyList>();
1656 int oldHdu = fitsfile.
getHdu();
1657 if (oldHdu != 0 && metadata->exists(
"INHERIT")) {
1658 bool inherit =
false;
1659 if (metadata->typeOf(
"INHERIT") ==
typeid(
std::string)) {
1660 inherit = (metadata->get<
std::string>(
"INHERIT") ==
"T");
1662 inherit = metadata->get<
bool>(
"INHERIT");
1664 if (
strip) metadata->remove(
"INHERIT");
1670 auto primaryHduMetadata = std::make_shared<daf::base::PropertyList>();
1675 auto const emptyMetadata = std::make_shared<lsst::daf::base::PropertyList>();
1685 _oldHdu(_fits.getHdu()),
1688 _fits.
setHdu(hdu, relative);
1710 auto fits = reinterpret_cast<fitsfile *>(
fptr);
1725 bool isCompressed = fits_is_compressed_image(
fits, &
status);
1732 return isCompressed;
1739 config.getAsDouble(
"compression.quantizeLevel")),
1741 config.getAsInt(
"scaling.bitpix"),
1742 config.exists(
"scaling.maskPlanes") ? config.getArray<
std::string>(
"scaling.maskPlanes")
1744 config.getAsInt(
"scaling.seed"), config.getAsDouble(
"scaling.quantizeLevel"),
1745 config.getAsDouble(
"scaling.quantizePad"), config.get<
bool>(
"scaling.fuzz"),
1746 config.getAsDouble(
"scaling.bscale"), config.getAsDouble(
"scaling.bzero")) {}
1750 template <
typename T>
1756 template <
typename T>
1757 void validateEntry(daf::base::PropertySet &output, daf::base::PropertySet
const &input,
1759 output.add(
name, input.exists(
name) ? input.getArray<T>(
name) : defaultValue);
1765 auto validated = std::make_shared<daf::base::PropertySet>();
1767 validateEntry(*validated, config,
"compression.algorithm",
std::string(
"NONE"));
1768 validateEntry(*validated, config,
"compression.columns", 0);
1769 validateEntry(*validated, config,
"compression.rows", 1);
1770 validateEntry(*validated, config,
"compression.quantizeLevel", 0.0);
1772 validateEntry(*validated, config,
"scaling.algorithm",
std::string(
"NONE"));
1773 validateEntry(*validated, config,
"scaling.bitpix", 0);
1775 validateEntry(*validated, config,
"scaling.seed", 1);
1776 validateEntry(*validated, config,
"scaling.quantizeLevel", 5.0);
1777 validateEntry(*validated, config,
"scaling.quantizePad", 10.0);
1778 validateEntry(*validated, config,
"scaling.fuzz",
true);
1779 validateEntry(*validated, config,
"scaling.bscale", 1.0);
1780 validateEntry(*validated, config,
"scaling.bzero", 0.0);
1783 for (
auto const &
name : config.
names(
false)) {
1784 if (!validated->exists(
name)) {
1786 os <<
"Invalid image write option: " <<
name;
1794 #define INSTANTIATE_KEY_OPS(r, data, T) \
1795 template void Fits::updateKey(std::string const &, T const &, std::string const &); \
1796 template void Fits::writeKey(std::string const &, T const &, std::string const &); \
1797 template void Fits::updateKey(std::string const &, T const &); \
1798 template void Fits::writeKey(std::string const &, T const &); \
1799 template void Fits::updateColumnKey(std::string const &, int, T const &, std::string const &); \
1800 template void Fits::writeColumnKey(std::string const &, int, T const &, std::string const &); \
1801 template void Fits::updateColumnKey(std::string const &, int, T const &); \
1802 template void Fits::writeColumnKey(std::string const &, int, T const &); \
1803 template void Fits::readKey(std::string const &, T &);
1805 #define INSTANTIATE_IMAGE_OPS(r, data, T) \
1806 template void Fits::writeImageImpl(T const *, int); \
1807 template void Fits::writeImage(image::ImageBase<T> const &, ImageWriteOptions const &, \
1808 std::shared_ptr<daf::base::PropertySet const>, \
1809 std::shared_ptr<image::Mask<image::MaskPixel> const>); \
1810 template void Fits::readImageImpl(int, T *, long *, long *, long *); \
1811 template bool Fits::checkImageType<T>(); \
1812 template int getBitPix<T>();
1814 #define INSTANTIATE_TABLE_OPS(r, data, T) \
1815 template int Fits::addColumn<T>(std::string const &ttype, int size); \
1816 template int Fits::addColumn<T>(std::string const &ttype, int size, std::string const &comment);
1817 #define INSTANTIATE_TABLE_ARRAY_OPS(r, data, T) \
1818 template void Fits::writeTableArray(std::size_t row, int col, int nElements, T const *value); \
1819 template void Fits::readTableArray(std::size_t row, int col, int nElements, T *value);
1826 (bool)(unsigned char)(short)(unsigned short)(int)(unsigned int)(long)(unsigned long)(LONGLONG)( \
1827 float)(double)(std::complex<float>)(std::complex<double>)(std::string)
1829 #define COLUMN_TYPES \
1830 (bool)(std::string)(std::int8_t)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t) \
1831 (std::int64_t)(float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1833 #define COLUMN_ARRAY_TYPES \
1834 (bool)(char)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t)(std::int64_t)( \
1835 float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>)
1837 #define IMAGE_TYPES \
1838 (unsigned char)(short)(unsigned short)(int)(unsigned int)(std::int64_t)(std::uint64_t)(float)(double)