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)) {
69 double value = metadata.get<
double>(
name);
76 BOOST_CURRENT_FUNCTION %
name);
80 }
else if (type ==
typeid(
float)) {
81 float value = metadata.get<
float>(
name);
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;
142 auto const iter = _words.
find(startString(key));
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();
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());
396 ItemInfo(
bool isComment,
bool isValid) : isComment(isComment), isValid(isValid) {}
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")) {
414 return ItemInfo(
true, type ==
typeid(
std::string));
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);
515 if (behavior & AUTO_CHECK) {
520 fits_movabs_hdu(reinterpret_cast<fitsfile *>(fptr), hdu + 1, 0, &status);
522 if (hdu ==
DEFAULT_HDU && getHdu() == 0 && getImageDim() == 0) {
524 int tmpStatus = status;
525 fits_movrel_hdu(reinterpret_cast<fitsfile *>(fptr), 1, 0, &tmpStatus);
527 if (behavior & AUTO_CHECK) {
535 fits_get_num_hdus(reinterpret_cast<fitsfile *>(fptr), &n, &status);
536 if (behavior & AUTO_CHECK) {
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,
599 const_cast<char *>(comment), &fits.
status);
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),
622 const_cast<char *>(comment), &fits.
status);
625 void writeKeyImpl(
Fits &fits,
char const *key,
std::string const &value,
char const *comment) {
626 if (strncmp(key,
"COMMENT", 7) == 0) {
627 fits_write_comment(reinterpret_cast<fitsfile *>(fits.
fptr), const_cast<char *>(value.
c_str()),
629 }
else if (strncmp(key,
"HISTORY", 7) == 0) {
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,
641 const_cast<char *>(comment), &fits.
status);
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());
660 if (behavior & AUTO_CHECK) {
665 template <
typename T>
667 writeKeyImpl(*
this, key.
c_str(), value, comment.
c_str());
668 if (behavior & AUTO_CHECK) {
673 template <
typename T>
675 updateKeyImpl(*
this, key.
c_str(), value, 0);
676 if (behavior & AUTO_CHECK) {
681 template <
typename T>
683 writeKeyImpl(*
this, key.
c_str(), value, 0);
684 if (behavior & AUTO_CHECK) {
689 template <
typename T>
691 updateKey((
boost::format(
"%s%d") % prefix % (n + 1)).str(), value, comment);
692 if (behavior & AUTO_CHECK) {
697 template <
typename T>
699 writeKey((
boost::format(
"%s%d") % prefix % (n + 1)).str(), value, comment);
700 if (behavior & AUTO_CHECK) {
705 template <
typename T>
707 updateKey((
boost::format(
"%s%d") % prefix % (n + 1)).str(), value);
708 if (behavior & AUTO_CHECK) {
713 template <
typename T>
715 writeKey((
boost::format(
"%s%d") % prefix % (n + 1)).str(), value);
716 if (behavior & AUTO_CHECK) {
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);
750 if (
std::string(buf).find(
'\'') != std::string::npos) {
752 readKeyImpl(fits, key, unquoted);
756 value = stringToNonFiniteDouble(unquoted);
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,
766 const_cast<char *>(key), &value, 0, &fits.
status);
772 template <
typename T>
774 readKeyImpl(*
this, key.
c_str(), value);
775 if (behavior & AUTO_CHECK) {
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) {
828 if (behavior & AUTO_CHECK) {
831 functor(keyStr, valueStr, commentStr);
840 return ((ignoreKeys.
find(key) != ignoreKeys.
end()) || ignoreKeyStarts.matches(key) ||
841 (
write && ignoreKeyStartsWrite.matches(key)));
848 template <
typename T>
854 if (
list->exists(key) &&
list->isUndefined(key)) {
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);
863 if (
set->exists(key) &&
set->isUndefined(key)) {
865 boost::format(
"In %s, replacing undefined value for key '%s'.") %
866 BOOST_CURRENT_FUNCTION % key);
867 set->set(key, value);
869 set->add(key, value);
879 if (
list->exists(key) && !
list->isUndefined(key)) {
883 boost::format(
"In %s, dropping undefined value for key '%s'.") %
884 BOOST_CURRENT_FUNCTION % key);
886 list->add(key,
nullptr, comment);
889 if (
set->exists(key) && !
set->isUndefined(key)) {
893 boost::format(
"In %s, dropping undefined value for key '%s'.") %
894 BOOST_CURRENT_FUNCTION % key);
896 set->add(key,
nullptr);
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;
917 if (
strip && isKeyIgnored(key)) {
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,
"");
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);
1061 BOOST_CURRENT_FUNCTION % valueType.
name() %
key));
1071 MetadataIterationFunctor f;
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);
1104 if (behavior & AUTO_CHECK) {
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()),
1115 const_cast<char *>(tform.
c_str()), &status);
1116 if (behavior & AUTO_CHECK) {
1122 template <
typename T>
1124 int nCols = addColumn<T>(ttype, size);
1125 updateColumnKey(
"TTYPE", nCols, ttype, comment);
1126 if (behavior & AUTO_CHECK) {
1134 fits_get_num_rows(reinterpret_cast<fitsfile *>(fptr), &first, &status);
1135 fits_insert_rows(reinterpret_cast<fitsfile *>(fptr), first, nRows, &status);
1136 if (behavior & AUTO_CHECK) {
1144 fits_get_num_rows(reinterpret_cast<fitsfile *>(fptr), &r, &status);
1145 if (behavior & AUTO_CHECK) {
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);
1155 if (behavior & AUTO_CHECK) {
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);
1169 if (behavior & AUTO_CHECK) {
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);
1179 if (behavior & AUTO_CHECK) {
1186 long size = isVariableLength ? getTableArraySize(row, col) : getTableArraySize(col);
1191 char *tmp = &buf.
front();
1192 fits_read_col(reinterpret_cast<fitsfile *>(fptr), TSTRING, col + 1, row + 1, 1, 1, 0, &tmp, &anynul,
1194 if (behavior & AUTO_CHECK) {
1204 fits_get_coltype(reinterpret_cast<fitsfile *>(fptr), col + 1, &typecode, &result, &width, &status);
1205 if (behavior & AUTO_CHECK) {
1214 fits_read_descript(reinterpret_cast<fitsfile *>(fptr), col + 1, row + 1, &result, &offset, &status);
1215 if (behavior & AUTO_CHECK) {
1225 fits_create_img(reinterpret_cast<fitsfile *>(fptr), 8, 0, &naxes, &status);
1226 if (behavior & AUTO_CHECK) {
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);
1233 if (behavior & AUTO_CHECK) {
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,
1241 const_cast<T *>(data), &status);
1242 if (behavior & AUTO_CHECK) {
1253 struct ImageCompressionContext {
1256 :
fits(fits_), old(
fits.getImageCompression()) {
1257 fits.setImageCompression(useThis);
1259 ~ImageCompressionContext() {
1263 fits.setImageCompression(old);
1278 template <
typename T>
1282 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1288 ImageCompressionContext comp(*
this, compression);
1289 if (behavior & AUTO_CHECK) {
1296 ndarray::Vector<long, 2> dims(image.
getArray().getShape().reverse());
1304 copy->combine(wcsMetadata);
1307 header = wcsMetadata;
1323 fits_set_bscale(
fits, 1.0, 0.0, &status);
1324 if (behavior & AUTO_CHECK) {
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()),
1333 if (behavior & AUTO_CHECK) {
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",
1357 if (behavior & AUTO_CHECK) {
1363 fits_write_key_lng(
fits,
"BLANK", scale.
blank,
"Value for undefined pixels", &status);
1364 fits_write_key_lng(
fits,
"ZDITHER0", options.
scaling.
seed,
"Dithering seed", &status);
1365 fits_write_key_str(
fits,
"ZQUANTIZ",
"SUBTRACTIVE_DITHER_1",
"Dithering algorithm", &status);
1366 if (behavior & AUTO_CHECK) {
1374 fits_set_hdustruc(
fits, &status);
1375 if (behavior & AUTO_CHECK) {
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);
1482 if (behavior & AUTO_CHECK) {
1487 fits_get_tile_dim(
fits, tiles.getNumElements(), tiles.getData(), &status);
1488 if (behavior & AUTO_CHECK) {
1492 float quantizeLevel;
1493 fits_get_quantize_level(
fits, &quantizeLevel, &status);
1494 if (behavior & AUTO_CHECK) {
1502 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1503 fits_unset_compression_request(
fits, &status);
1507 if (behavior & AUTO_CHECK) {
1516 fits_set_tile_dim(
fits, comp.
tiles.getNumElements(), comp.
tiles.getData(), &status);
1517 if (behavior & AUTO_CHECK) {
1523 if (behavior & AUTO_CHECK) {
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());
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) {
1623 combined->copy(
name, first,
name, asScalar);
1626 for (
auto const &
name : second->getOrderedNames()) {
1627 auto const iscv = isCommentIsValid(*second,
name);
1628 if (iscv.isComment) {
1634 combined->copy(
name, second,
name, asScalar);
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>();
1688 _fits.
setHdu(hdu, relative);
1710 auto fits =
reinterpret_cast<fitsfile *
>(fptr);
1711 if (getHdu() != 0 || countHdus() == 1) {
1716 fits_get_img_dim(
fits, &naxis, &status);
1717 if (behavior & AUTO_CHECK) {
1725 bool isCompressed = fits_is_compressed_image(
fits, &status);
1726 if (behavior & AUTO_CHECK) {
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>
1753 output.
add(name, input.
get<
T>(name, defaultValue));
1756 template <
typename T>
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) #define LOGLS_WARN(logger, message)
Log a warn-level message using an iostream-based interface.
def write(self, patchRef, catalog)
Write the output.
int getImageDim()
Return the number of dimensions in the current HDU.
std::size_t countRows()
Return the number of row in a table.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
#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.
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.
def writeMetadata(self, dataRefList)
No metadata to write, and not sure how to write it for a list of dataRefs.
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.