8 #include <unordered_set>
9 #include <unordered_map>
16 #include "boost/algorithm/string.hpp"
17 #include "boost/regex.hpp"
18 #include "boost/filesystem.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;
149 return key.compare(0, word.
size(), word) == 0;
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 *)0)).str();
223 }
else if (size < 0) {
225 return (
boost::format(
"1Q%c(%d)") % getFormatCode((T *)0) % (-size)).str();
228 return (
boost::format(
"1Q%c") % getFormatCode((T *)0)).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 != 0 && fd->Fptr != 0 && fd->Fptr->filename != 0) {
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 != 0 && fd->Fptr != 0 && fd->Fptr->filename != 0) {
502 fileName = fd->Fptr->filename;
509 fits_get_hdu_num(
reinterpret_cast<fitsfile *
>(
fptr), &n);
515 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu, 0, &
status);
521 fits_movabs_hdu(
reinterpret_cast<fitsfile *
>(
fptr), hdu + 1, 0, &
status);
526 fits_movrel_hdu(
reinterpret_cast<fitsfile *
>(
fptr), 1, 0, &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) {
628 fits_write_comment(
reinterpret_cast<fitsfile *
>(
fits.
fptr),
const_cast<char *
>(value.c_str()),
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, 0);
682 template <
typename T>
684 writeKeyImpl(*
this,
key.c_str(), value, 0);
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, 0,
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, 0, &
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,
773 template <
typename T>
775 readKeyImpl(*
this,
key.c_str(), value);
786 fits_get_hdrspace(
reinterpret_cast<fitsfile *
>(
fptr), &nKeys, 0, &
status);
792 fits_read_keyn(
reinterpret_cast<fitsfile *
>(
fptr), i,
key, value, comment, &
status);
796 boost::to_upper(upperKey);
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>
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);
875 boost::format(
"In %s, replacing undefined value for key '%s'.") %
876 BOOST_CURRENT_FUNCTION %
key);
893 boost::format(
"In %s, dropping undefined value for key '%s'.") %
894 BOOST_CURRENT_FUNCTION %
key);
896 list->add(
key,
nullptr, comment);
903 boost::format(
"In %s, dropping undefined value for key '%s'.") %
904 BOOST_CURRENT_FUNCTION %
key);
912 daf::base::PropertySet *
set;
918 static boost::regex
const boolRegex(
"[tTfF]");
919 static boost::regex
const intRegex(
"[+-]?[0-9]+");
920 static boost::regex
const doubleRegex(
"[+-]?([0-9]*\\.?[0-9]+|[0-9]+\\.?[0-9]*)([eE][+-]?[0-9]+)?");
921 static boost::regex
const fitsStringRegex(
"'(.*?) *'");
923 static boost::regex
const fitsDefinitionCommentRegex(
924 " *(FITS \\(Flexible Image Transport System\\)|and Astrophysics', volume 376, page 359).*");
925 boost::smatch matchStrings;
932 if (boost::regex_match(value, boolRegex)) {
934 add(
key,
bool(value ==
"T" || value ==
"t"), comment);
935 }
else if (boost::regex_match(value, intRegex)) {
939 if (
val < (1LL << 31) &&
val > -(1LL << 31)) {
940 add(
key,
static_cast<int>(
val), comment);
944 }
else if (boost::regex_match(value, doubleRegex)) {
949 }
else if (boost::regex_match(value, matchStrings, fitsStringRegex)) {
951 double val = stringToNonFiniteDouble(str);
955 add(
key, str, comment);
957 }
else if (
key ==
"HISTORY") {
958 add(
key, comment,
"");
959 }
else if (
key ==
"COMMENT" && !(
strip && boost::regex_match(comment, fitsDefinitionCommentRegex))) {
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 = 0) {
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);
1063 }
else if (valueType ==
typeid(nullptr_t)) {
1064 if (metadata.isArray(
key)) {
1066 auto tmp = metadata.getArray<nullptr_t>(
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 (NameList::const_iterator i = paramNames.begin(); i != paramNames.end(); ++i) {
1105 if (!isKeyIgnored(*i,
true)) {
1107 writeKeyFromProperty(*
this, metadata, *i, pl->
getComment(*i).
c_str());
1109 writeKeyFromProperty(*
this, metadata, *i);
1120 fits_create_tbl(
reinterpret_cast<fitsfile *
>(
fptr), BINARY_TBL, 0, 0, &ttype, &tform, 0, 0, &
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, 0, value, &anynul, &
status);
1208 char *tmp = &buf.
front();
1209 fits_read_col(
reinterpret_cast<fitsfile *
>(
fptr), TSTRING,
col + 1,
row + 1, 1, 1, 0, &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(0), 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 boost::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, NULL, &
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(0), status(0), behavior(behavior_) {
1584 typedef void *(*Reallocator)(
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, 0,
1591 }
else if (mode ==
"w" || mode ==
"wb") {
1592 Reallocator reallocator = 0;
1594 fits_create_memfile(
reinterpret_cast<fitsfile **
>(&
fptr), &manager._ptr, &manager._len, 0,
1597 }
else if (mode ==
"a" || mode ==
"ab") {
1598 Reallocator reallocator = 0;
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, NULL, &
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)