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) {
62 if (type ==
typeid(
bool)) {
63 out += metadata.get<
bool>(
name) ?
"T" :
"F";
66 }
else if (type ==
typeid(
int)) {
68 }
else if (type ==
typeid(
double)) {
71 }
else if (type ==
typeid(
float)) {
77 if (out.
size() > 80) {
82 int const len = out.
size();
85 }
else if (len > 80) {
88 "Formatted data too long: " +
std::to_string(len) +
" > 80: \"" + out +
"\"");
105 class StringStartSet {
109 for (
auto const &word : input) {
111 if (size < _minSize) {
115 for (
auto const &word : input) {
117 assert(_words.
count(start) == 0);
118 _words[start] = word;
124 auto const iter = _words.
find(startString(key));
125 if (iter == _words.
end()) {
150 "SIMPLE",
"BITPIX",
"NAXIS",
"EXTEND",
"GCOUNT",
"PCOUNT",
"XTENSION",
"TFIELDS",
"BSCALE",
"BZERO",
152 "ZBITPIX",
"ZIMAGE",
"ZCMPTYPE",
"ZSIMPLE",
"ZEXTEND",
"ZBLANK",
"ZDATASUM",
"ZHECKSUM",
"ZQUANTIZ",
154 "DATASUM",
"CHECKSUM"};
161 StringStartSet
const ignoreKeyStarts{
162 "NAXIS",
"TZERO",
"TSCAL",
164 "ZNAXIS",
"ZTILE",
"ZNAME",
"ZVAL"};
171 StringStartSet
const ignoreKeyStartsWrite{
"TFORM",
"TTYPE"};
175 if (s.
empty())
return s;
178 return s.
substr(i1, (i1 == std::string::npos) ? 0 : 1 + i2 - i1);
183 char getFormatCode(
bool *) {
return 'X'; }
192 char getFormatCode(
float *) {
return 'E'; }
193 char getFormatCode(
double *) {
return 'D'; }
200 template <
typename T>
203 return (
boost::format(
"%d%c") % size % getFormatCode((T *)0)).str();
204 }
else if (size < 0) {
206 return (
boost::format(
"1Q%c(%d)") % getFormatCode((T *)0) % (-size)).str();
209 return (
boost::format(
"1Q%c") % getFormatCode((T *)0)).str();
215 template <
typename T>
219 struct FitsType<bool> {
220 static int const CONSTANT = TLOGICAL;
223 struct FitsType<char> {
224 static int const CONSTANT = TSTRING;
227 struct FitsType<signed char> {
228 static int const CONSTANT = TSBYTE;
231 struct FitsType<unsigned char> {
232 static int const CONSTANT = TBYTE;
235 struct FitsType<short> {
236 static int const CONSTANT = TSHORT;
239 struct FitsType<unsigned short> {
240 static int const CONSTANT = TUSHORT;
243 struct FitsType<
int> {
244 static int const CONSTANT = TINT;
247 struct FitsType<unsigned
int> {
248 static int const CONSTANT = TUINT;
251 struct FitsType<long> {
252 static int const CONSTANT = TLONG;
255 struct FitsType<unsigned long> {
256 static int const CONSTANT = TULONG;
259 struct FitsType<long long> {
260 static int const CONSTANT = TLONGLONG;
263 struct FitsType<unsigned long long> {
264 static int const CONSTANT = TLONGLONG;
267 struct FitsType<
float> {
268 static int const CONSTANT = TFLOAT;
271 struct FitsType<double> {
272 static int const CONSTANT = TDOUBLE;
276 static int const CONSTANT = TDOUBLE;
279 struct FitsType<
std::complex<float> > {
280 static int const CONSTANT = TCOMPLEX;
283 struct FitsType<
std::complex<double> > {
284 static int const CONSTANT = TDBLCOMPLEX;
288 template <
typename T>
289 struct FitsTableType :
public FitsType<T> {};
291 struct FitsTableType<bool> {
292 static int const CONSTANT = TBIT;
295 template <
typename T>
299 struct FitsBitPix<unsigned char> {
300 static int const CONSTANT = BYTE_IMG;
303 struct FitsBitPix<short> {
304 static int const CONSTANT = SHORT_IMG;
307 struct FitsBitPix<unsigned short> {
308 static int const CONSTANT = USHORT_IMG;
311 struct FitsBitPix<
int> {
312 static int const CONSTANT = LONG_IMG;
315 struct FitsBitPix<unsigned
int> {
316 static int const CONSTANT = ULONG_IMG;
319 struct FitsBitPix<
std::int64_t> {
320 static int const CONSTANT = LONGLONG_IMG;
323 struct FitsBitPix<
std::uint64_t> {
324 static int const CONSTANT = LONGLONG_IMG;
327 struct FitsBitPix<
float> {
328 static int const CONSTANT = FLOAT_IMG;
331 struct FitsBitPix<double> {
332 static int const CONSTANT = DOUBLE_IMG;
335 bool isFitsImageTypeSigned(
int constant) {
337 case BYTE_IMG:
return false;
338 case SHORT_IMG:
return true;
339 case USHORT_IMG:
return false;
340 case LONG_IMG:
return true;
341 case ULONG_IMG:
return false;
342 case LONGLONG_IMG:
return true;
343 case FLOAT_IMG:
return true;
344 case DOUBLE_IMG:
return true;
346 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
"Invalid constant.");
349 static bool allowImageCompression =
true;
351 int fitsTypeForBitpix(
int bitpix) {
367 os <<
"Invalid bitpix value: " << bitpix;
368 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, os.
str());
378 ItemInfo(
bool isComment,
bool isValid) : isComment(isComment), isValid(isValid) {}
390 ItemInfo isCommentIsValid(daf::base::PropertyList
const &pl,
std::string const &
name) {
391 if (!pl.exists(name)) {
392 return ItemInfo(
false,
false);
395 if ((name ==
"COMMENT") || (name ==
"HISTORY")) {
396 return ItemInfo(
true, type ==
typeid(
std::string));
398 return ItemInfo(
false,
true);
409 os <<
"cfitsio error";
410 if (fileName !=
"") {
411 os <<
" (" << fileName <<
")";
414 char fitsErrMsg[FLEN_ERRMSG];
415 fits_get_errstatus(status, fitsErrMsg);
416 os <<
": " << fitsErrMsg <<
" (" << status <<
")";
421 os <<
"\ncfitsio error stack:\n";
422 char cfitsioMsg[FLEN_ERRMSG];
423 while (fits_read_errmsg(cfitsioMsg) != 0) {
424 os <<
" " << cfitsioMsg <<
"\n";
431 fitsfile *fd =
reinterpret_cast<fitsfile *
>(fptr);
432 if (fd != 0 && fd->Fptr != 0 && fd->Fptr->filename != 0) {
433 fileName = fd->Fptr->filename;
448 for (
auto const &
name : allParamNames) {
453 return makeLimitedFitsHeaderImpl(desiredParamNames, metadata);
470 template <
typename T>
472 return FitsBitPix<T>::CONSTANT;
481 fitsfile *fd =
reinterpret_cast<fitsfile *
>(fptr);
482 if (fd != 0 && fd->Fptr != 0 && fd->Fptr->filename != 0) {
483 fileName = fd->Fptr->filename;
490 fits_get_hdu_num(reinterpret_cast<fitsfile *>(fptr), &n);
496 fits_movrel_hdu(reinterpret_cast<fitsfile *>(fptr), hdu, 0, &status);
497 if (behavior & AUTO_CHECK) {
502 fits_movabs_hdu(reinterpret_cast<fitsfile *>(fptr), hdu + 1, 0, &status);
504 if (hdu ==
DEFAULT_HDU && getHdu() == 0 && getImageDim() == 0) {
506 int tmpStatus = status;
507 fits_movrel_hdu(reinterpret_cast<fitsfile *>(fptr), 1, 0, &tmpStatus);
509 if (behavior & AUTO_CHECK) {
517 fits_get_num_hdus(reinterpret_cast<fitsfile *>(fptr), &n, &status);
518 if (behavior & AUTO_CHECK) {
536 std::string nonFiniteDoubleToString(
double value) {
554 double stringToNonFiniteDouble(
std::string const &value) {
555 if (value ==
"NAN") {
558 if (value ==
"+INFINITY") {
561 if (value ==
"-INFINITY") {
567 template <
typename T>
568 void updateKeyImpl(
Fits &
fits,
char const *key, T
const &value,
char const *comment) {
569 fits_update_key(reinterpret_cast<fitsfile *>(fits.
fptr), FitsType<T>::CONSTANT, const_cast<char *>(key),
570 const_cast<T *>(&value), const_cast<char *>(comment), &fits.
status);
573 void updateKeyImpl(
Fits &fits,
char const *key,
std::string const &value,
char const *comment) {
574 fits_update_key_longstr(reinterpret_cast<fitsfile *>(fits.
fptr), const_cast<char *>(key),
575 const_cast<char *>(value.
c_str()), const_cast<char *>(comment), &fits.
status);
578 void updateKeyImpl(
Fits &fits,
char const *key,
bool const &value,
char const *comment) {
580 fits_update_key(reinterpret_cast<fitsfile *>(fits.
fptr), TLOGICAL, const_cast<char *>(key), &v,
581 const_cast<char *>(comment), &fits.
status);
584 void updateKeyImpl(
Fits &fits,
char const *key,
double const &value,
char const *comment) {
585 std::string strValue = nonFiniteDoubleToString(value);
586 if (!strValue.
empty()) {
587 updateKeyImpl(fits, key, strValue, comment);
589 fits_update_key(reinterpret_cast<fitsfile *>(fits.
fptr), FitsType<double>::CONSTANT,
590 const_cast<char *>(key), const_cast<double *>(&value), const_cast<char *>(comment),
595 template <
typename T>
596 void writeKeyImpl(
Fits &fits,
char const *key, T
const &value,
char const *comment) {
597 fits_write_key(reinterpret_cast<fitsfile *>(fits.
fptr), FitsType<T>::CONSTANT, const_cast<char *>(key),
598 const_cast<T *>(&value), const_cast<char *>(comment), &fits.
status);
601 void writeKeyImpl(
Fits &fits,
char const *key,
char const *comment) {
603 fits_write_key_null(reinterpret_cast<fitsfile *>(fits.
fptr), const_cast<char *>(key),
604 const_cast<char *>(comment), &fits.
status);
607 void writeKeyImpl(
Fits &fits,
char const *key,
std::string const &value,
char const *comment) {
608 if (strncmp(key,
"COMMENT", 7) == 0) {
609 fits_write_comment(reinterpret_cast<fitsfile *>(fits.
fptr), const_cast<char *>(value.
c_str()),
611 }
else if (strncmp(key,
"HISTORY", 7) == 0) {
612 fits_write_history(reinterpret_cast<fitsfile *>(fits.
fptr), const_cast<char *>(value.
c_str()),
615 fits_write_key_longstr(reinterpret_cast<fitsfile *>(fits.
fptr), const_cast<char *>(key),
616 const_cast<char *>(value.
c_str()), const_cast<char *>(comment), &fits.
status);
620 void writeKeyImpl(
Fits &fits,
char const *key,
bool const &value,
char const *comment) {
622 fits_write_key(reinterpret_cast<fitsfile *>(fits.
fptr), TLOGICAL, const_cast<char *>(key), &v,
623 const_cast<char *>(comment), &fits.
status);
626 void writeKeyImpl(
Fits &fits,
char const *key,
double const &value,
char const *comment) {
627 std::string strValue = nonFiniteDoubleToString(value);
628 if (!strValue.
empty()) {
629 writeKeyImpl(fits, key, strValue, comment);
631 fits_write_key(reinterpret_cast<fitsfile *>(fits.
fptr), FitsType<double>::CONSTANT,
632 const_cast<char *>(key), const_cast<double *>(&value), const_cast<char *>(comment),
639 template <
typename T>
641 updateKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
642 if (behavior & AUTO_CHECK) {
647 template <
typename T>
649 writeKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
650 if (behavior & AUTO_CHECK) {
655 template <
typename T>
657 updateKeyImpl(*
this, key.
c_str(), value, 0);
658 if (behavior & AUTO_CHECK) {
663 template <
typename T>
665 writeKeyImpl(*
this, key.
c_str(), value, 0);
666 if (behavior & AUTO_CHECK) {
671 template <
typename T>
673 updateKey((
boost::format(
"%s%d") % prefix % (n + 1)).
str(), value, comment);
674 if (behavior & AUTO_CHECK) {
679 template <
typename T>
682 if (behavior & AUTO_CHECK) {
687 template <
typename T>
690 if (behavior & AUTO_CHECK) {
695 template <
typename T>
698 if (behavior & AUTO_CHECK) {
707 template <
typename T>
708 void readKeyImpl(
Fits &
fits,
char const *key, T &value) {
709 fits_read_key(reinterpret_cast<fitsfile *>(fits.
fptr), FitsType<T>::CONSTANT, const_cast<char *>(key),
715 fits_read_key_longstr(reinterpret_cast<fitsfile *>(fits.
fptr), const_cast<char *>(key), &buf, 0,
723 void readKeyImpl(
Fits &fits,
char const *key,
double &value) {
727 char buf[FLEN_VALUE];
728 fits_read_keyword(reinterpret_cast<fitsfile *>(fits.
fptr), const_cast<char *>(key), buf, 0, &fits.
status);
732 if (
std::string(buf).find(
'\'') != std::string::npos) {
734 readKeyImpl(fits, key, unquoted);
738 value = stringToNonFiniteDouble(unquoted);
742 (
boost::format(
"Unrecognised string value for keyword '%s' when parsing as double: %s") %
747 fits_read_key(reinterpret_cast<fitsfile *>(fits.
fptr), FitsType<double>::CONSTANT,
748 const_cast<char *>(key), &value, 0, &fits.
status);
754 template <
typename T>
756 readKeyImpl(*
this, key.
c_str(), value);
757 if (behavior & AUTO_CHECK) {
767 fits_get_hdrspace(reinterpret_cast<fitsfile *>(fptr), &nKeys, 0, &status);
773 fits_read_keyn(reinterpret_cast<fitsfile *>(fptr), i, key, value, comment, &status);
776 commentStr = comment;
778 while (valueStr.
size() > 2 && valueStr[valueStr.
size() - 2] ==
'&' && i <= nKeys) {
780 fits_read_record(reinterpret_cast<fitsfile *>(fptr), i, key, &status);
781 if (strncmp(key,
"CONTINUE", 8) != 0) {
788 if (firstQuote == std::string::npos) {
793 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
796 if (lastQuote == std::string::npos) {
801 boost::format(
"Invalid CONTINUE at header key %d: \"%s\".") % i % card));
803 valueStr += card.
substr(firstQuote + 1, lastQuote - firstQuote);
805 if (slash != std::string::npos) {
810 if (behavior & AUTO_CHECK) {
813 functor(keyStr, valueStr, commentStr);
821 bool isKeyIgnored(
std::string const &key,
bool write =
false) {
822 return ((ignoreKeys.
find(key) != ignoreKeys.
end()) || ignoreKeyStarts.matches(key) ||
823 (write && ignoreKeyStartsWrite.matches(key)));
830 template <
typename T>
836 if (
list->exists(key) &&
list->isUndefined(key)) {
838 boost::format(
"In %s, replacing undefined value for key '%s'.") %
839 BOOST_CURRENT_FUNCTION % key);
840 list->set(key, value, comment);
842 list->add(key, value, comment);
845 if (
set->exists(key) &&
set->isUndefined(key)) {
847 boost::format(
"In %s, replacing undefined value for key '%s'.") %
848 BOOST_CURRENT_FUNCTION % key);
849 set->set(key, value);
851 set->add(key, value);
861 if (
list->exists(key) && !
list->isUndefined(key)) {
865 boost::format(
"In %s, dropping undefined value for key '%s'.") %
866 BOOST_CURRENT_FUNCTION % key);
868 list->add(key,
nullptr, comment);
871 if (
set->exists(key) && !
set->isUndefined(key)) {
875 boost::format(
"In %s, dropping undefined value for key '%s'.") %
876 BOOST_CURRENT_FUNCTION % key);
878 set->add(key,
nullptr);
884 daf::base::PropertySet *
set;
890 static boost::regex
const boolRegex(
"[tTfF]");
891 static boost::regex
const intRegex(
"[+-]?[0-9]+");
892 static boost::regex
const doubleRegex(
"[+-]?([0-9]*\\.?[0-9]+|[0-9]+\\.?[0-9]*)([eE][+-]?[0-9]+)?");
893 static boost::regex
const fitsStringRegex(
"'(.*?) *'");
895 static boost::regex
const fitsDefinitionCommentRegex(
896 " *(FITS \\(Flexible Image Transport System\\)|and Astrophysics', volume 376, page 359).*");
897 boost::smatch matchStrings;
899 if (
strip && isKeyIgnored(key)) {
904 if (boost::regex_match(value, boolRegex)) {
906 add(key,
bool(value ==
"T" || value ==
"t"), comment);
907 }
else if (boost::regex_match(value, intRegex)) {
911 if (val < (1
LL << 31) && val > -(1
LL << 31)) {
912 add(key, static_cast<int>(val), comment);
914 add(key, val, comment);
916 }
else if (boost::regex_match(value, doubleRegex)) {
920 add(key, val, comment);
921 }
else if (boost::regex_match(value, matchStrings, fitsStringRegex)) {
923 double val = stringToNonFiniteDouble(str);
925 add(key, val, comment);
927 add(key, str, comment);
929 }
else if (key ==
"HISTORY") {
930 add(key, comment,
"");
931 }
else if (key ==
"COMMENT" && !(
strip && boost::regex_match(comment, fitsDefinitionCommentRegex))) {
932 add(key, comment,
"");
933 }
else if (value.
empty()) {
936 if (key !=
"COMMENT") {
941 afw::fits::FitsError,
942 (
boost::format(
"Could not parse header value for key '%s': '%s'") % key % value).
str());
946 void writeKeyFromProperty(Fits &
fits, daf::base::PropertySet
const &metadata,
std::string const &key,
947 char const *comment = 0) {
949 if (valueType ==
typeid(
bool)) {
950 if (metadata.isArray(key)) {
954 writeKeyImpl(fits, key.
c_str(),
static_cast<bool>(tmp[i]), comment);
957 writeKeyImpl(fits, key.
c_str(), metadata.get<
bool>(
key), comment);
960 if (metadata.isArray(key)) {
963 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
968 }
else if (valueType ==
typeid(
int)) {
969 if (metadata.isArray(key)) {
972 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
975 writeKeyImpl(fits, key.
c_str(), metadata.get<
int>(
key), comment);
977 }
else if (valueType ==
typeid(
long)) {
978 if (metadata.isArray(key)) {
981 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
984 writeKeyImpl(fits, key.
c_str(), metadata.get<
long>(
key), comment);
986 }
else if (valueType ==
typeid(
long long)) {
987 if (metadata.isArray(key)) {
990 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
993 writeKeyImpl(fits, key.
c_str(), metadata.get<
long long>(
key), comment);
996 if (metadata.isArray(key)) {
999 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1004 }
else if (valueType ==
typeid(
double)) {
1005 if (metadata.isArray(key)) {
1008 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1011 writeKeyImpl(fits, key.
c_str(), metadata.get<
double>(
key), comment);
1014 if (metadata.isArray(key)) {
1017 writeKeyImpl(fits, key.
c_str(), tmp[i], comment);
1022 }
else if (valueType ==
typeid(nullptr_t)) {
1023 if (metadata.isArray(key)) {
1027 writeKeyImpl(fits, key.
c_str(), comment);
1030 writeKeyImpl(fits, key.
c_str(), comment);
1037 BOOST_CURRENT_FUNCTION % valueType.
name() %
key));
1047 MetadataIterationFunctor f;
1057 NameList paramNames;
1063 for (NameList::const_iterator i = paramNames.begin(); i != paramNames.end(); ++i) {
1064 if (!isKeyIgnored(*i,
true)) {
1066 writeKeyFromProperty(*
this, metadata, *i, pl->
getComment(*i).
c_str());
1068 writeKeyFromProperty(*
this, metadata, *i);
1079 fits_create_tbl(reinterpret_cast<fitsfile *>(fptr), BINARY_TBL, 0, 0, &ttype, &tform, 0, 0, &status);
1080 if (behavior & AUTO_CHECK) {
1085 template <
typename T>
1088 fits_get_num_cols(reinterpret_cast<fitsfile *>(fptr), &nCols, &status);
1090 fits_insert_col(reinterpret_cast<fitsfile *>(fptr), nCols + 1, const_cast<char *>(ttype.
c_str()),
1091 const_cast<char *>(tform.
c_str()), &status);
1092 if (behavior & AUTO_CHECK) {
1098 template <
typename T>
1100 int nCols = addColumn<T>(ttype, size);
1101 updateColumnKey(
"TTYPE", nCols, ttype, comment);
1102 if (behavior & AUTO_CHECK) {
1110 fits_get_num_rows(reinterpret_cast<fitsfile *>(fptr), &first, &status);
1111 fits_insert_rows(reinterpret_cast<fitsfile *>(fptr), first, nRows, &status);
1112 if (behavior & AUTO_CHECK) {
1120 fits_get_num_rows(reinterpret_cast<fitsfile *>(fptr), &r, &status);
1121 if (behavior & AUTO_CHECK) {
1127 template <
typename T>
1129 fits_write_col(reinterpret_cast<fitsfile *>(fptr), FitsTableType<T>::CONSTANT, col + 1, row + 1, 1,
1130 nElements, const_cast<T *>(value), &status);
1131 if (behavior & AUTO_CHECK) {
1133 nElements % row % col);
1142 char const *tmp = value.
c_str();
1143 fits_write_col(reinterpret_cast<fitsfile *>(fptr), TSTRING, col + 1, row + 1, 1, 1,
1144 const_cast<char const **>(&tmp), &status);
1145 if (behavior & AUTO_CHECK) {
1150 template <
typename T>
1153 fits_read_col(reinterpret_cast<fitsfile *>(fptr), FitsTableType<T>::CONSTANT, col + 1, row + 1, 1,
1154 nElements, 0, value, &anynul, &status);
1155 if (behavior & AUTO_CHECK) {
1162 long size = isVariableLength ? getTableArraySize(row, col) : getTableArraySize(col);
1167 char *tmp = &buf.
front();
1168 fits_read_col(reinterpret_cast<fitsfile *>(fptr), TSTRING, col + 1, row + 1, 1, 1, 0, &tmp, &anynul,
1170 if (behavior & AUTO_CHECK) {
1180 fits_get_coltype(reinterpret_cast<fitsfile *>(fptr), col + 1, &typecode, &result, &width, &status);
1181 if (behavior & AUTO_CHECK) {
1190 fits_read_descript(reinterpret_cast<fitsfile *>(fptr), col + 1, row + 1, &result, &offset, &status);
1191 if (behavior & AUTO_CHECK) {
1201 fits_create_img(reinterpret_cast<fitsfile *>(fptr), 8, 0, &naxes, &status);
1202 if (behavior & AUTO_CHECK) {
1207 void Fits::createImageImpl(
int bitpix,
int naxis,
long const *naxes) {
1208 fits_create_img(reinterpret_cast<fitsfile *>(fptr), bitpix, naxis, const_cast<long *>(naxes), &status);
1209 if (behavior & AUTO_CHECK) {
1214 template <
typename T>
1215 void Fits::writeImageImpl(T
const *
data,
int nElements) {
1216 fits_write_img(reinterpret_cast<fitsfile *>(fptr), FitsType<T>::CONSTANT, 1, nElements,
1217 const_cast<T *>(data), &status);
1218 if (behavior & AUTO_CHECK) {
1229 struct ImageCompressionContext {
1232 :
fits(fits_), old(
fits.getImageCompression()) {
1233 fits.setImageCompression(useThis);
1235 ~ImageCompressionContext() {
1239 fits.setImageCompression(old);
1254 template <
typename T>
1258 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1264 ImageCompressionContext comp(*
this, compression);
1265 if (behavior & AUTO_CHECK) {
1272 ndarray::Vector<long, 2> dims(image.
getArray().getShape().reverse());
1280 copy->combine(wcsMetadata);
1283 header = wcsMetadata;
1285 writeMetadata(*header);
1299 fits_set_bscale(
fits, 1.0, 0.0, &status);
1300 if (behavior & AUTO_CHECK) {
1306 int const fitsType = scale.
bitpix == 0 ? FitsType<T>::CONSTANT : fitsTypeForBitpix(scale.
bitpix);
1307 fits_write_img(
fits, fitsType, 1, pixels->getNumElements(),
const_cast<void *
>(pixels->getData()),
1309 if (behavior & AUTO_CHECK) {
1319 if (scale.
bzero != 0.0) {
1320 fits_write_key_lng(
fits,
"BZERO", static_cast<long>(scale.
bzero),
1321 "Scaling: MEMORY = BZERO + BSCALE * DISK", &status);
1323 if (scale.
bscale != 1.0) {
1324 fits_write_key_lng(
fits,
"BSCALE", static_cast<long>(scale.
bscale),
1325 "Scaling: MEMORY = BZERO + BSCALE * DISK", &status);
1328 fits_write_key_dbl(
fits,
"BZERO", scale.
bzero, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1330 fits_write_key_dbl(
fits,
"BSCALE", scale.
bscale, 12,
"Scaling: MEMORY = BZERO + BSCALE * DISK",
1333 if (behavior & AUTO_CHECK) {
1339 fits_write_key_lng(
fits,
"BLANK", scale.
blank,
"Value for undefined pixels", &status);
1340 fits_write_key_lng(
fits,
"ZDITHER0", options.
scaling.
seed,
"Dithering seed", &status);
1341 fits_write_key_str(
fits,
"ZQUANTIZ",
"SUBTRACTIVE_DITHER_1",
"Dithering algorithm", &status);
1342 if (behavior & AUTO_CHECK) {
1350 fits_set_hdustruc(
fits, &status);
1351 if (behavior & AUTO_CHECK) {
1359 template <
typename T,
class Enable =
void>
1361 static T constexpr value = 0;
1365 template <
typename T>
1366 struct NullValue<T, typename std::enable_if<std::numeric_limits<T>::has_quiet_NaN>
::type> {
1372 template <
typename T>
1373 void Fits::readImageImpl(
int nAxis, T *data,
long *begin,
long *
end,
long *increment) {
1374 T null = NullValue<T>::value;
1376 fits_read_subset(reinterpret_cast<fitsfile *>(fptr), FitsType<T>::CONSTANT, begin, end, increment,
1377 reinterpret_cast<void *>(&null), data, &anyNulls, &status);
1383 fits_get_img_dim(reinterpret_cast<fitsfile *>(fptr), &nAxis, &status);
1388 void Fits::getImageShapeImpl(
int maxDim,
long *nAxes) {
1389 fits_get_img_size(reinterpret_cast<fitsfile *>(fptr), maxDim, nAxes, &status);
1393 template <
typename T>
1396 fits_get_img_equivtype(reinterpret_cast<fitsfile *>(fptr), &imageType, &status);
1399 if (imageType < 0) {
1403 if (isFitsImageTypeSigned(imageType)) {
1404 return FitsBitPix<T>::CONSTANT >= imageType;
1407 return FitsBitPix<T>::CONSTANT > imageType;
1410 if (!isFitsImageTypeSigned(imageType)) {
1411 return FitsBitPix<T>::CONSTANT >= imageType;
1412 }
else if (imageType == LONGLONG_IMG) {
1415 return FitsBitPix<T>::CONSTANT >= imageType;
1427 fits_get_img_equivtype(reinterpret_cast<fitsfile *>(fptr), &bitpix, &status);
1440 case BYTE_IMG:
return "uint8";
1441 case SBYTE_IMG:
return "int8";
1442 case SHORT_IMG:
return "int16";
1443 case USHORT_IMG:
return "uint16";
1444 case LONG_IMG:
return "int32";
1445 case ULONG_IMG:
return "uint32";
1446 case LONGLONG_IMG:
return "int64";
1455 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1457 fits_get_compression_type(
fits, &compType, &status);
1458 if (behavior & AUTO_CHECK) {
1463 fits_get_tile_dim(
fits, tiles.getNumElements(), tiles.getData(), &status);
1464 if (behavior & AUTO_CHECK) {
1468 float quantizeLevel;
1469 fits_get_quantize_level(
fits, &quantizeLevel, &status);
1470 if (behavior & AUTO_CHECK) {
1478 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1479 fits_unset_compression_request(
fits, &status);
1483 if (behavior & AUTO_CHECK) {
1492 fits_set_tile_dim(
fits, comp.
tiles.getNumElements(), comp.
tiles.getData(), &status);
1493 if (behavior & AUTO_CHECK) {
1499 if (behavior & AUTO_CHECK) {
1512 : fptr(0), status(0), behavior(behavior_) {
1513 if (mode ==
"r" || mode ==
"rb") {
1514 fits_open_file(reinterpret_cast<fitsfile **>(&
fptr), const_cast<char *>(filename.
c_str()), READONLY,
1516 }
else if (mode ==
"w" || mode ==
"wb") {
1517 boost::filesystem::remove(filename);
1518 fits_create_file(reinterpret_cast<fitsfile **>(&
fptr), const_cast<char *>(filename.
c_str()), &
status);
1519 }
else if (mode ==
"a" || mode ==
"ab") {
1520 fits_open_file(reinterpret_cast<fitsfile **>(&
fptr), const_cast<char *>(filename.
c_str()), READWRITE,
1523 fits_get_num_hdus(reinterpret_cast<fitsfile *>(
fptr), &nHdu, &
status);
1524 fits_movabs_hdu(reinterpret_cast<fitsfile *>(
fptr), nHdu, NULL, &
status);
1529 fits_close_file(reinterpret_cast<fitsfile *>(
fptr), &tmpStatus);
1534 (
boost::format(
"Invalid mode '%s' given when opening file '%s'") % mode % filename).
str());
1543 typedef void *(*Reallocator)(
void *,
std::size_t);
1546 if (mode ==
"r" || mode ==
"rb") {
1547 fits_open_memfile(reinterpret_cast<fitsfile **>(&
fptr),
"unused", READONLY, &manager._ptr,
1548 &manager._len, 0, 0,
1550 }
else if (mode ==
"w" || mode ==
"wb") {
1551 Reallocator reallocator = 0;
1553 fits_create_memfile(reinterpret_cast<fitsfile **>(&
fptr), &manager._ptr, &manager._len, 0,
1556 }
else if (mode ==
"a" || mode ==
"ab") {
1557 Reallocator reallocator = 0;
1559 fits_open_memfile(reinterpret_cast<fitsfile **>(&
fptr),
"unused", READWRITE, &manager._ptr,
1560 &manager._len, 0, reallocator, &
status);
1562 fits_get_num_hdus(reinterpret_cast<fitsfile *>(
fptr), &nHdu, &
status);
1563 fits_movabs_hdu(reinterpret_cast<fitsfile *>(
fptr), nHdu, NULL, &
status);
1568 fits_close_file(reinterpret_cast<fitsfile *>(
fptr), &tmpStatus);
1572 (
boost::format(
"Invalid mode '%s' given when opening memory file at '%s'") % mode %
1578 *
this,
boost::format(
"Opening memory file at '%s' with mode '%s'") % manager._ptr % mode);
1583 fits_close_file(reinterpret_cast<fitsfile *>(
fptr), &
status);
1590 auto combined = std::make_shared<daf::base::PropertyList>();
1591 bool const asScalar =
true;
1592 for (
auto const &
name : first->getOrderedNames()) {
1593 auto const iscv = isCommentIsValid(*first,
name);
1594 if (iscv.isComment) {
1599 combined->copy(
name, first,
name, asScalar);
1602 for (
auto const &
name : second->getOrderedNames()) {
1603 auto const iscv = isCommentIsValid(*second,
name);
1604 if (iscv.isComment) {
1610 combined->copy(
name, second,
name, asScalar);
1629 auto metadata = std::make_shared<lsst::daf::base::PropertyList>();
1632 int oldHdu = fitsfile.
getHdu();
1633 if (oldHdu != 0 && metadata->exists(
"INHERIT")) {
1634 bool inherit =
false;
1635 if (metadata->typeOf(
"INHERIT") ==
typeid(
std::string)) {
1636 inherit = (metadata->get<
std::string>(
"INHERIT") ==
"T");
1638 inherit = metadata->get<
bool>(
"INHERIT");
1640 if (strip) metadata->remove(
"INHERIT");
1646 auto primaryHduMetadata = std::make_shared<daf::base::PropertyList>();
1651 auto const emptyMetadata = std::make_shared<lsst::daf::base::PropertyList>();
1664 _fits.
setHdu(hdu, relative);
1686 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1687 if (getHdu() != 0 || countHdus() == 1) {
1692 fits_get_img_dim(
fits, &naxis, &status);
1693 if (behavior & AUTO_CHECK) {
1701 bool isCompressed = fits_is_compressed_image(
fits, &status);
1702 if (behavior & AUTO_CHECK) {
1708 return isCompressed;
1715 config.getAsDouble(
"compression.quantizeLevel")),
1717 config.getAsInt(
"scaling.bitpix"),
1718 config.exists(
"scaling.maskPlanes") ? config.getArray<
std::string>(
"scaling.maskPlanes")
1720 config.getAsInt(
"scaling.seed"), config.getAsDouble(
"scaling.quantizeLevel"),
1721 config.getAsDouble(
"scaling.quantizePad"), config.get<
bool>(
"scaling.fuzz"),
1722 config.getAsDouble(
"scaling.bscale"), config.getAsDouble(
"scaling.bzero")) {}
1726 template <
typename T>
1729 output.
add(name, input.
get<T>(name, defaultValue));
1732 template <
typename T>
1741 auto validated = std::make_shared<daf::base::PropertySet>();
1743 validateEntry(*validated, config,
"compression.algorithm",
std::string(
"NONE"));
1744 validateEntry(*validated, config,
"compression.columns", 0);
1745 validateEntry(*validated, config,
"compression.rows", 1);
1746 validateEntry(*validated, config,
"compression.quantizeLevel", 0.0);
1748 validateEntry(*validated, config,
"scaling.algorithm",
std::string(
"NONE"));
1749 validateEntry(*validated, config,
"scaling.bitpix", 0);
1751 validateEntry(*validated, config,
"scaling.seed", 1);
1752 validateEntry(*validated, config,
"scaling.quantizeLevel", 5.0);
1753 validateEntry(*validated, config,
"scaling.quantizePad", 10.0);
1754 validateEntry(*validated, config,
"scaling.fuzz",
true);
1755 validateEntry(*validated, config,
"scaling.bscale", 1.0);
1756 validateEntry(*validated, config,
"scaling.bzero", 0.0);
1759 for (
auto const &
name : config.
names(
false)) {
1760 if (!validated->exists(
name)) {
1762 os <<
"Invalid image write option: " <<
name;
1770 #define INSTANTIATE_KEY_OPS(r, data, T) \ 1771 template void Fits::updateKey(std::string const &, T const &, std::string const &); \ 1772 template void Fits::writeKey(std::string const &, T const &, std::string const &); \ 1773 template void Fits::updateKey(std::string const &, T const &); \ 1774 template void Fits::writeKey(std::string const &, T const &); \ 1775 template void Fits::updateColumnKey(std::string const &, int, T const &, std::string const &); \ 1776 template void Fits::writeColumnKey(std::string const &, int, T const &, std::string const &); \ 1777 template void Fits::updateColumnKey(std::string const &, int, T const &); \ 1778 template void Fits::writeColumnKey(std::string const &, int, T const &); \ 1779 template void Fits::readKey(std::string const &, T &); 1781 #define INSTANTIATE_IMAGE_OPS(r, data, T) \ 1782 template void Fits::writeImageImpl(T const *, int); \ 1783 template void Fits::writeImage(image::ImageBase<T> const &, ImageWriteOptions const &, \ 1784 std::shared_ptr<daf::base::PropertySet const>, \ 1785 std::shared_ptr<image::Mask<image::MaskPixel> const>); \ 1786 template void Fits::readImageImpl(int, T *, long *, long *, long *); \ 1787 template bool Fits::checkImageType<T>(); \ 1788 template int getBitPix<T>(); 1790 #define INSTANTIATE_TABLE_OPS(r, data, T) \ 1791 template int Fits::addColumn<T>(std::string const &ttype, int size); \ 1792 template int Fits::addColumn<T>(std::string const &ttype, int size, std::string const &comment); 1793 #define INSTANTIATE_TABLE_ARRAY_OPS(r, data, T) \ 1794 template void Fits::writeTableArray(std::size_t row, int col, int nElements, T const *value); \ 1795 template void Fits::readTableArray(std::size_t row, int col, int nElements, T *value); 1802 (bool)(unsigned char)(short)(unsigned short)(int)(unsigned int)(long)(unsigned long)(LONGLONG)( \ 1803 float)(double)(std::complex<float>)(std::complex<double>)(std::string) 1805 #define COLUMN_TYPES \ 1806 (bool)(std::string)(std::int8_t)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t) \ 1807 (std::int64_t)(float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>) 1809 #define COLUMN_ARRAY_TYPES \ 1810 (bool)(char)(std::uint8_t)(std::int16_t)(std::uint16_t)(std::int32_t)(std::uint32_t)(std::int64_t)( \ 1811 float)(double)(lsst::geom::Angle)(std::complex<float>)(std::complex<double>) 1813 #define IMAGE_TYPES \ 1814 (unsigned char)(short)(unsigned short)(int)(unsigned int)(std::int64_t)(std::uint64_t)(float)(double) #define LOGLS_WARN(logger, message)
Log a warn-level message using an iostream-based interface.
int getImageDim()
Return the number of dimensions in the current HDU.
std::size_t countRows()
Return the number of row in a table.
#define INSTANTIATE_TABLE_OPS(r, data, T)
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::string getFileName() const
Return the file name associated with the FITS object or "<unknown>" if there is none.
void forEachKey(HeaderIterationFunctor &functor)
Call a polymorphic functor for every key in the header.
ImageCompressionOptions getImageCompression()
Return the current image compression settings.
ImageCompressionOptions compression
Options controlling compression.
T find_first_not_of(T... args)
void writeMetadata(daf::base::PropertySet const &metadata)
Read a FITS header into a PropertySet or PropertyList.
#define LSST_FITS_CHECK_STATUS(fitsObj,...)
Throw a FitsError exception if the status of the given Fits object is nonzero.
std::vector< T > getArray(std::string const &name) const
Get the vector of values for a property name (possibly hierarchical).
int compressionAlgorithmToCfitsio(ImageCompressionOptions::CompressionAlgorithm algorithm)
Convert ImageCompressionOptions::CompressionAlgorithm to cfitsio.
Class for storing ordered metadata with comments.
T get(std::string const &name) const
Get the last value for a property name (possibly hierarchical).
long blank
Value for integer images indicating non-finite values.
void readKey(std::string const &key, T &value)
Read a FITS header key into the given reference.
int bitpix
Bits per pixel; negative means floating-point: 8,16,32,64,-32,-64.
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.
Options for writing an image to FITS.
std::string const & getComment(std::string const &name) const
Get the comment for a string property name (possibly hierarchical).
def scale(algorithm, min, max=None, frame=None)
int getBitPix()
Return the cfitsio integer BITPIX code for the given data type.
void readTableScalar(std::size_t row, int col, T &value)
Read an array scalar from a binary table.
bool checkImageType()
Return true if the current HDU is compatible with the given pixel type.
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.
#define INSTANTIATE_TABLE_ARRAY_OPS(r, data, T)
std::vector< std::string > paramNames(bool topLevelOnly=true) const
A variant of names that excludes the names of subproperties.
CompressionAlgorithm
Compression algorithms.
daf::base::PropertySet * set
A simple struct that combines the two arguments that must be passed to most cfitsio routines and cont...
The base class for all image classed (Image, Mask, MaskedImage, ...)
long getTableArraySize(int col)
Return the size of an array column.
FITS BITPIX header value by C++ type.
void closeFile()
Close a FITS file.
A class representing an angle.
int seed
Seed for random number generator when fuzzing.
void createEmpty()
Create an empty image HDU with NAXIS=0 at the end of the file.
std::string getImageDType()
Return the numpy dtype equivalent of the image pixel type (e.g.
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.
LSST DM logging module built on log4cxx.
ndarray::Array< T const, N, N > const makeContiguousArray(ndarray::Array< T, N, C > const &array)
Construct a contiguous ndarray.
An exception thrown when problems are found when reading or writing FITS files.
Tiles tiles
Tile size; a dimension with 0 means infinite (e.g., to specify one row: 0,1)
Options for tile compression of image pixels.
A base class for image defects.
lsst::geom::Box2I getBBox(ImageOrigin origin=PARENT) const
Represent a 2-dimensional array of bitmask pixels.
int64_t getAsInt64(std::string const &name) const
Get the last value for a bool/char/short/int/int64_t property name (possibly hierarchical).
Lifetime-management for memory that goes into FITS memory files.
double bzero
Zero-point to apply when reading from FITS.
std::size_t addRows(std::size_t nRows)
Append rows to a table, and return the index of the first new row.
std::shared_ptr< daf::base::PropertyList > createTrivialWcsMetadata(std::string const &wcsName, lsst::geom::Point2I const &xy0)
ImageCompressionOptions::CompressionAlgorithm compressionAlgorithmFromString(std::string const &name)
Interpret compression algorithm expressed in string.
Fits()
Default constructor; set all data members to 0.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
ndarray::Array< long, 1, 1 > Tiles
void createTable()
Create a new binary table extension.
#define LOGL_WARN(logger, message...)
Log a warn-level message using a varargs/printf style interface.
double bscale
Scale to apply when reading from FITS.
void setHdu(int hdu, bool relative=false)
Set the current HDU.
T find_last_not_of(T... args)
#define INSTANTIATE_IMAGE_OPS(r, data, T)
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
lsst::geom::Point2I getXY0() const
Return the image's origin.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
#define INSTANTIATE_KEY_OPS(r, data, T)
ImageScalingOptions scaling
Options controlling scaling.
static std::shared_ptr< daf::base::PropertySet > validate(daf::base::PropertySet const &config)
Validate a PropertySet.
void disable()
Disable the guard, leaving the HDU at its current state at destruction.
bool checkCompressedImagePhu()
Go to the first image header in the FITS file.
void setAllowImageCompression(bool allow)
Class for storing generic metadata.
float quantizeLevel
quantization level: 0.0 = none requires use of GZIP or GZIP_SHUFFLE
void writeTableArray(std::size_t row, int col, int nElements, T const *value)
Write an array value to a binary table.
void reset()
Return the manager to the same state it would be if default-constructed.
CompressionAlgorithm algorithm
Compresion algorithm to use.
void readTableArray(std::size_t row, int col, int nElements, T *value)
Read an array value from a binary table.
ImageCompressionOptions::CompressionAlgorithm compressionAlgorithmFromCfitsio(int cfitsio)
Convert compression algorithm from cfitsio to ImageCompressionOptions::CompressionAlgorithm.
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.
void setImageCompression(ImageCompressionOptions const &options)
Set compression options for writing FITS images.
#define COLUMN_ARRAY_TYPES
void writeKey(std::string const &key, T const &value, std::string const &comment)
Add a FITS header key to the bottom of the header.
bool fuzz
Fuzz the values when quantising floating-point values?
std::vector< std::string > names(bool topLevelOnly=true) const
Get the names in the PropertySet, optionally including those in subproperties.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
bool getAllowImageCompression()
int getHdu()
Return the current HDU (0-indexed; 0 is the Primary HDU).
void add(std::string const &name, T const &value)
Append a single value to the vector of values for a property name (possibly hierarchical).
void writeImage(ndarray::Array< T const, N, C > const &array)
Write an ndarray::Array to a FITS image HDU.
std::vector< std::string > getOrderedNames() const
Get the list of property names, in the order they were added.
int addColumn(std::string const &ttype, int size, std::string const &comment)
Add a column to a table.
daf::base::PropertyList * list
std::string makeErrorMessage(std::string const &fileName="", int status=0, std::string const &msg="")
Return an error message reflecting FITS I/O errors.
RAII scoped guard for moving the HDU in a Fits object.
ImageScalingOptions::ScalingAlgorithm scalingAlgorithmFromString(std::string const &name)
Interpret scaling algorithm expressed in string.
std::string const wcsNameForXY0
void writeTableScalar(std::size_t row, int col, T value)
Write a scalar value to a binary table.
void readMetadata(daf::base::PropertySet &metadata, bool strip=false)
Read a FITS header into a PropertySet or PropertyList.
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.
Reports errors that are due to events beyond the control of the program.
const int DEFAULT_HDU
Specify that the default HDU should be read.
std::shared_ptr< detail::PixelArrayBase > toFits(ndarray::Array< T const, 2, 2 > const &image, bool forceNonfiniteRemoval, bool fuzz=true, ndarray::Array< long, 1 > const &tiles=ndarray::Array< long, 1, 1 >(), int seed=1) const
Convert to an array of pixel values to write to FITS.
ImageWriteOptions(image::Image< T > const &image)
Construct with default options for images.
int countHdus()
Return the number of HDUs in the file.