11 #include "boost/multi_index_container.hpp"
12 #include "boost/multi_index/sequenced_index.hpp"
13 #include "boost/multi_index/ordered_index.hpp"
14 #include "boost/multi_index/hashed_index.hpp"
15 #include "boost/multi_index/member.hpp"
34 template <std::
string FitsSchemaItem::*Member>
35 struct SetFitsSchemaString {
36 void operator()(FitsSchemaItem &item) { item.*Member = _v; }
37 explicit SetFitsSchemaString(
std::string const &v) : _v(v) {}
51 using InputContainer = boost::multi_index_container<FitsSchemaItem, boost::multi_index::indexed_by<boost::multi_index::ordered_non_unique<boost::multi_index::member<FitsSchemaItem, int, &FitsSchemaItem::column>>, boost::multi_index::ordered_non_unique<boost::multi_index::member<FitsSchemaItem, int, &FitsSchemaItem::bit>>, boost::multi_index::hashed_unique<boost::multi_index::member<FitsSchemaItem, std::string, &FitsSchemaItem::ttype>>, boost::multi_index::sequenced<>>>;
54 using SetTTYPE = SetFitsSchemaString<&FitsSchemaItem::ttype>;
55 using SetTFORM = SetFitsSchemaString<&FitsSchemaItem::tform>;
56 using SetTCCLS = SetFitsSchemaString<&FitsSchemaItem::tccls>;
57 using SetTUNIT = SetFitsSchemaString<&FitsSchemaItem::tunit>;
58 using SetDoc = SetFitsSchemaString<&FitsSchemaItem::doc>;
90 : _impl(
std::make_shared<
Impl>()) {
93 if (!metadata.
exists(
"AFW_TYPE")) {
97 _impl->
type = metadata.
get(
"AFW_TYPE", _impl->
type);
99 metadata.
remove(
"AFW_TABLE_VERSION");
102 metadata.
remove(
"AFW_TYPE");
110 metadata.
remove(
"AR_HDU");
117 for (
auto const &rawAliase : rawAliases) {
119 if (pos == std::string::npos) {
121 (
boost::format(
"Malformed alias definition: '%s'") % rawAliase).str());
123 _impl->
schema.
getAliasMap()->set(rawAliase.substr(0, pos), rawAliase.substr(pos + 1, std::string::npos));
141 for (
auto const &oldSlotKey : oldSlotKeys) {
146 metadata.
remove(oldSlotKey.first);
147 metadata.
remove(oldSlotKey.first +
"_ERR_SLOT");
148 metadata.
remove(oldSlotKey.first +
"_FLAG_SLOT");
156 for (
auto const &key : keyList) {
157 if (key.compare(0, 5,
"TTYPE") == 0) {
158 int column =
std::stoi(key.substr(5)) - 1;
165 if (
iter->doc.empty()) {
171 }
else if (key.compare(0, 5,
"TFLAG") == 0) {
173 auto iter = _impl->
byBit().lower_bound(bit);
179 if (
iter->doc.empty()) {
185 }
else if (key.compare(0, 4,
"TDOC") == 0) {
186 int column =
std::stoi(key.substr(4)) - 1;
195 }
else if (key.compare(0, 5,
"TFDOC") == 0) {
197 auto iter = _impl->
byBit().lower_bound(bit);
205 }
else if (key.compare(0, 5,
"TUNIT") == 0) {
206 int column =
std::stoi(key.substr(5)) - 1;
215 }
else if (key.compare(0, 5,
"TCCLS") == 0) {
216 int column =
std::stoi(key.substr(5)) - 1;
225 }
else if (key.compare(0, 5,
"TFORM") == 0) {
226 int column =
std::stoi(key.substr(5)) - 1;
235 }
else if (key.compare(0, 5,
"TZERO") == 0) {
239 }
else if (key.compare(0, 5,
"TSCAL") == 0) {
243 }
else if (key.compare(0, 5,
"TNULL") == 0) {
247 }
else if (key.compare(0, 5,
"TDISP") == 0) {
258 metadata.
remove(
"FLAGCOL");
270 static std::regex const regex(
"(\\d+)?X\\(?(\\d)*\\)?");
275 (
boost::format(
"Invalid TFORM key for flags column: '%s'") %
iter->tform).str());
298 int oldHdu =
fits.getHdu();
359 template <
typename T>
368 : _column(item.column), _key(
schema.addField<
T>(item.ttype, item.doc, item.tunit, base)),
369 _cache(), _cacheFirstRow(0)
376 if (_key.getElementCount() == 1u) {
377 std::size_t nElements = nRows*_key.getElementCount();
378 _cache.resize(nElements);
379 _cacheFirstRow = firstRow;
380 fits.readTableArray(firstRow, _column, nElements, &_cache.front());
386 if (_cache.empty()) {
387 fits.readTableArray(
row, _column, _key.getElementCount(), record.getElement(_key));
389 assert(
row >= _cacheFirstRow);
391 assert(offset < _cache.size());
392 std::copy_n(_cache.begin() + offset, _key.getElementCount(), record.getElement(_key));
404 class AngleReader :
public FitsColumnReader {
407 Schema &
schema, FitsSchemaItem
const &item,
408 FieldBase<lsst::geom::Angle>
const &base = FieldBase<lsst::geom::Angle>()) {
412 AngleReader(Schema &
schema, FitsSchemaItem
const &item, FieldBase<lsst::geom::Angle>
const &base)
413 : _column(item.column), _key(
schema.addField<
lsst::
geom::
Angle>(item.ttype, item.doc,
"", base)) {
418 if (!item.tunit.empty() && item.tunit !=
"rad") {
420 "Angle fields must be persisted in radians (TUNIT='rad').");
425 assert(_key.getElementCount() == 1u);
426 _cache.resize(nRows);
427 _cacheFirstRow = firstRow;
428 fits.readTableArray(firstRow, _column, nRows, &_cache.front());
433 if (_cache.empty()) {
435 fits.readTableScalar(
row, _column, tmp);
438 assert(
row >= _cacheFirstRow);
440 assert(offset < _cache.size());
447 Key<lsst::geom::Angle> _key;
452 class StringReader :
public FitsColumnReader {
458 StringReader(Schema &
schema, FitsSchemaItem
const &item,
int size)
459 : _column(item.column),
460 _key(
schema.addField<
std::string>(item.ttype, item.doc, item.tunit, size)),
461 _isVariableLength(size == 0) {}
466 fits.readTableScalar(
row, _column, s, _isVariableLength);
472 Key<std::string> _key;
473 bool _isVariableLength;
476 template <
typename T>
477 class VariableLengthArrayReader :
public FitsColumnReader {
483 VariableLengthArrayReader(Schema &
schema, FitsSchemaItem
const &item)
484 : _column(item.column), _key(
schema.addField<Array<
T>>(item.ttype, item.doc, item.tunit, 0)) {}
488 int size =
fits.getTableArraySize(
row, _column);
489 ndarray::Array<T, 1, 1> array = ndarray::allocate(size);
490 fits.readTableArray(
row, _column, size, array.getData());
491 record.set(_key, array);
501 template <
typename T>
502 class PointConversionReader :
public FitsColumnReader {
508 PointConversionReader(Schema &
schema, FitsSchemaItem
const &item)
509 : _column(item.column), _key(PointKey<
T>::addFields(
schema, item.ttype, item.doc, item.tunit)) {}
514 fits.readTableArray(
row, _column, 2, buffer.
data());
525 class CoordConversionReader :
public FitsColumnReader {
531 CoordConversionReader(Schema &
schema, FitsSchemaItem
const &item)
532 : _column(item.column), _key(CoordKey::addFields(
schema, item.ttype, item.doc)) {}
537 fits.readTableArray(
row, _column, 2, buffer.
data());
548 class MomentsConversionReader :
public FitsColumnReader {
554 MomentsConversionReader(Schema &
schema, FitsSchemaItem
const &item)
555 : _column(item.column),
561 fits.readTableArray(
row, _column, 3, buffer.
data());
562 record.set(_key, geom::ellipses::Quadrupole(buffer[0], buffer[1], buffer[2],
false));
573 template <
typename T,
int N>
574 class CovarianceConversionReader :
public FitsColumnReader {
577 static std::regex const regex(
"(.*)(\\^(\\d+))?");
592 CovarianceConversionReader(Schema &
schema, FitsSchemaItem
const &item,
594 : _column(item.column),
596 _key(CovarianceMatrixKey<
T, N>::addFields(
schema, item.ttype, names, guessUnits(item.tunit))),
602 for (
int i = 0; i <
_size; ++i) {
603 for (
int j = i; j <
_size; ++j) {
612 CovarianceMatrixKey<T, N> _key;
620 static std::regex const regex(
"(\\d+)?([PQ])?([A-Z])\\(?(\\d)*\\)?");
630 char code =
m[3].str()[0];
640 if (item.tccls ==
"Array") {
641 return StandardReader<Array<std::uint8_t>>::make(
schema, item, size);
643 return StandardReader<std::uint8_t>::make(
schema, item);
646 return VariableLengthArrayReader<std::uint8_t>::make(
schema, item);
648 return StandardReader<Array<std::uint8_t>>::make(
schema, item, size);
653 if (item.tccls ==
"Array") {
654 return StandardReader<Array<std::uint16_t>>::make(
schema, item, size);
656 return StandardReader<std::uint16_t>::make(
schema, item);
659 return VariableLengthArrayReader<std::uint16_t>::make(
schema, item);
661 return StandardReader<Array<std::uint16_t>>::make(
schema, item, size);
664 return VariableLengthArrayReader<std::int32_t>::make(
schema, item);
666 if (item.tccls ==
"Point") {
667 return PointConversionReader<std::int32_t>::make(
schema, item);
669 if (size > 1 || item.tccls ==
"Array") {
670 return StandardReader<Array<std::int32_t>>::make(
schema, item, size);
672 return StandardReader<std::int32_t>::make(
schema, item);
675 return StandardReader<std::int64_t>::make(
schema, item);
679 return VariableLengthArrayReader<float>::make(
schema, item);
682 if (item.tccls ==
"Array") {
683 return StandardReader<Array<float>>::make(
schema, item, 1);
687 return StandardReader<float>::make(
schema, item);
689 if (size == 3 && item.tccls ==
"Covariance(Point)") {
691 return CovarianceConversionReader<float, 2>::make(
schema, item, names);
693 if (size == 6 && item.tccls ==
"Covariance(Moments)") {
695 return CovarianceConversionReader<float, 3>::make(
schema, item, names);
697 if (item.tccls ==
"Covariance") {
698 double v = 0.5 * (
std::sqrt(1 + 8 * size) - 1);
700 if (n * (n + 1) != size * 2) {
701 throw LSST_EXCEPT(afw::fits::FitsError,
"Covariance field has invalid size.");
707 return CovarianceConversionReader<float, Eigen::Dynamic>::make(
schema, item, names);
709 return StandardReader<Array<float>>::make(
schema, item, size);
712 return VariableLengthArrayReader<double>::make(
schema, item);
715 if (item.tccls ==
"Angle") {
716 return AngleReader::make(
schema, item);
718 if (item.tccls ==
"Array") {
719 return StandardReader<Array<double>>::make(
schema, item, 1);
721 return StandardReader<double>::make(
schema, item);
724 if (item.tccls ==
"Point") {
725 return PointConversionReader<double>::make(
schema, item);
727 if (item.tccls ==
"Coord") {
728 return CoordConversionReader::make(
schema, item);
731 if (size == 3 && item.tccls ==
"Moments") {
732 return MomentsConversionReader::make(
schema, item);
734 return StandardReader<Array<double>>::make(
schema, item, size);
737 return StringReader::make(
schema, item, size);
747 bool isInstFlux(FitsSchemaItem
const & item) {
752 if (!
includes(item.ttype,
"flux"))
return false;
753 if (
includes(item.ttype,
"modelfit_CModel") && item.tunit.empty()) {
760 std::transform(units.begin(), units.end(), units.begin(), [](
char c) { return std::tolower(c); } );
776 if (flagPos != std::string::npos) {
785 ttype.
replace(flagPos, 5,
"flag");
792 }
else if (isInstFlux(*
iter)) {
794 if (endswith(
iter->ttype,
"_err")) {
795 aliases.
set(replace(
iter->ttype,
"_err",
"_instFluxErr"),
iter->ttype);
797 aliases.
set(
iter->ttype +
"_instFlux",
iter->ttype);
799 }
else if (endswith(
iter->ttype,
"_err")) {
805 if (
iter->tccls ==
"Covariance(Point)") {
806 aliases.
set(replace(
iter->ttype,
"_err",
"_yErr"),
iter->ttype +
"_yErr");
807 aliases.
set(replace(
iter->ttype,
"_err",
"_xErr"),
iter->ttype +
"_xErr");
808 aliases.
set(replace(
iter->ttype,
"_err",
"_x_y_Cov"),
iter->ttype +
"_x_y_Cov");
809 }
else if (
iter->tccls ==
"Covariance(Moments)") {
810 aliases.
set(replace(
iter->ttype,
"_err",
"_xxErr"),
iter->ttype +
"_xxErr");
811 aliases.
set(replace(
iter->ttype,
"_err",
"_yyErr"),
iter->ttype +
"_yyErr");
812 aliases.
set(replace(
iter->ttype,
"_err",
"_xyErr"),
iter->ttype +
"_xyErr");
813 aliases.
set(replace(
iter->ttype,
"_err",
"_xx_yy_Cov"),
iter->ttype +
"_xx_yy_Cov");
814 aliases.
set(replace(
iter->ttype,
"_err",
"_xx_xy_Cov"),
iter->ttype +
"_xx_xy_Cov");
815 aliases.
set(replace(
iter->ttype,
"_err",
"_yy_xy_Cov"),
iter->ttype +
"_yy_xy_Cov");
819 }
else if (_impl->
version < 3) {
826 if (_impl->
version < 2 && endswith(
name,
"Sigma")) {
843 LOGLS_WARN(
"lsst.afw.FitsSchemaInputMapper",
"Format " <<
iter->tform <<
" for column "
845 <<
" not supported; skipping.");
850 (
boost::format(
"Flag field '%s' is is in bit %d (0-indexed) of only %d") %
861 (
boost::format(
"Non-positive record size: %d; file is corrupt or invalid.") %
880 for (
auto const &reader : _impl->
readers) {
881 reader->prepRead(
row, size,
fits);
884 for (
auto const & reader : _impl->
readers) {
table::Key< std::string > name
Key< Flag > const & target
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
LSST DM logging module built on log4cxx.
#define LOGLS_WARN(logger, message)
Log a warn-level message using an iostream-based interface.
An exception thrown when problems are found when reading or writing FITS files.
A simple struct that combines the two arguments that must be passed to most cfitsio routines and cont...
Mapping class that holds aliases for a Schema.
void set(std::string const &alias, std::string const &target)
Add an alias to the schema or replace an existing one.
Base class for all records.
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
Defines the fields and offsets for a table.
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
std::size_t getRecordSize() const
Return the raw size of a record in bytes.
std::shared_ptr< AliasMap > getAliasMap() const
Return the map of aliases.
Polymorphic reader interface used to read different kinds of objects from one or more FITS binary tab...
Class for storing ordered metadata with comments.
T get(std::string const &name) const
Get the last value for a property name (possibly hierarchical).
std::string const & getComment(std::string const &name) const
Get the comment for a string property name (possibly hierarchical).
std::vector< T > getArray(std::string const &name) const
Get the vector of values for a property name (possibly hierarchical).
std::vector< std::string > getOrderedNames() const
Get the list of property names, in the order they were added.
virtual void remove(std::string const &name)
Remove all values for a property name (possibly hierarchical).
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
A coordinate class intended to represent absolute positions (2-d specialization).
Point in an unspecified spherical coordinate system.
Reports attempts to exceed implementation-defined length limits for some classes.
Reports attempts to access elements using an invalid key.
std::size_t computeCovariancePackedSize(std::size_t size)
Defines the packed size of a covariance matrices.
std::size_t indexCovariance(std::size_t i, std::size_t j)
Defines the ordering of packed covariance matrices.
CoordinateType
Enum used to set units for geometric FunctorKeys.
constexpr AngleUnit radians
constant with units of radians
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
A base class for image defects.
table::Key< table::Array< int > > _size
Field base class default implementation (used for numeric scalars and lsst::geom::Angle).
A structure that describes a field as a collection of related strings read from the FITS header.