4#include "boost/format.hpp"
25struct PersistenceHelper {
51class SourceFitsWriter :
public io::FitsWriter {
53 explicit SourceFitsWriter(Fits *fits,
int flags) : io::FitsWriter(fits, flags) {}
58 void _writeRecord(BaseRecord
const &record)
override;
60 void _finish()
override {
70 Key<int> _footprintKey;
78 "Cannot use a SourceFitsWriter on a non-Source table.");
80 if (!(_flags & SOURCE_IO_NO_FOOTPRINTS)) {
81 _mapper = SchemaMapper(t->getSchema(),
true);
82 _mapper.addMinimalSchema(t->getSchema(),
true);
83 _footprintKey =
_mapper.editOutputSchema().addField<
int>(
"footprint",
"archive ID for Footprint");
84 _outTable = BaseTable::make(
_mapper.getOutputSchema());
87 metadata = std::static_pointer_cast<daf::base::PropertyList>(metadata->deepCopy());
89 metadata.
reset(
new daf::base::PropertyList());
97 metadata->set(
"AR_HDU", 3,
98 "HDU (1-indexed) containing the archive index for non-record data (e.g. Footprints)");
99 _outTable->setMetadata(metadata);
100 _outRecord = _outTable->makeRecord();
101 io::FitsWriter::_writeTable(_outTable, nRows);
103 io::FitsWriter::_writeTable(table, nRows);
105 _fits->writeKey(
"AFW_TYPE",
"SOURCE",
"Tells lsst::afw to load this as a Source table.");
108void SourceFitsWriter::_writeRecord(BaseRecord
const &r) {
109 SourceRecord
const &record =
static_cast<SourceRecord
const &
>(r);
110 if (!(_flags & SOURCE_IO_NO_FOOTPRINTS)) {
111 _outRecord->assign(record,
_mapper);
114 if ((_flags & SOURCE_IO_NO_HEAVY_FOOTPRINTS) && footprint->isHeavy()) {
115 footprint.
reset(
new afw::detection::Footprint(*footprint));
117 int footprintArchiveId =
_archive.put(footprint);
118 _outRecord->set(_footprintKey, footprintArchiveId);
120 io::FitsWriter::_writeRecord(*_outRecord);
122 io::FitsWriter::_writeRecord(record);
151class OldSourceFootprintReader :
public io::FitsColumnReader {
153 static int readSpecialColumn(io::FitsSchemaInputMapper &
mapper, daf::base::PropertyList &metadata,
155 int column = metadata.get(name, 0);
159 metadata.remove(name);
166 static void setup(io::FitsSchemaInputMapper &
mapper, daf::base::PropertyList &metadata,
int ioFlags,
167 bool stripMetadata) {
169 reader->_spanCol = readSpecialColumn(
mapper, metadata, stripMetadata,
"SPANCOL");
170 reader->_peakCol = readSpecialColumn(
mapper, metadata, stripMetadata,
"PEAKCOL");
171 reader->_heavyPixCol = readSpecialColumn(
mapper, metadata, stripMetadata,
"HVYPIXCO");
172 reader->_heavyMaskCol = readSpecialColumn(
mapper, metadata, stripMetadata,
"HVYMSKCO");
173 reader->_heavyVarCol = readSpecialColumn(
mapper, metadata, stripMetadata,
"HVYVARCO");
174 if ((ioFlags & SOURCE_IO_NO_FOOTPRINTS) ||
mapper.hasArchive()) {
177 if (ioFlags & SOURCE_IO_NO_HEAVY_FOOTPRINTS) {
178 reader->_heavyPixCol = -1;
179 reader->_heavyMaskCol = -1;
180 reader->_heavyVarCol = -1;
184 if ((reader->_spanCol >= 0) != (reader->_peakCol >= 0)) {
186 "Corrupted catalog: either both or none of the Footprint Span/Peak columns "
189 if (reader->_spanCol < 0) {
192 if ((reader->_heavyPixCol >= 0) != (reader->_heavyMaskCol >= 0) ||
193 (reader->_heavyPixCol >= 0) != (reader->_heavyVarCol >= 0)) {
195 afw::fits::FitsError,
196 "Corrupted catalog: either all or none of the HeavyFootprint columns must be present.");
198 if (reader->_heavyPixCol >= 0 && reader->_spanCol < 0) {
200 "Corrupted catalog: HeavyFootprint columns with no Span/Peak columns.");
206 void readCell(BaseRecord &baseRecord,
std::size_t row, fits::Fits &fits,
208 SourceRecord &record =
static_cast<SourceRecord &
>(baseRecord);
212 std::size_t spanElementCount = fits.getTableArraySize(
row, _spanCol);
213 std::size_t peakElementCount = fits.getTableArraySize(
row, _peakCol);
214 if (spanElementCount) {
215 if (spanElementCount % 3) {
217 afw::fits::FitsError,
218 afw::fits::makeErrorMessage(
219 fits.fptr, fits.status,
220 boost::format(
"Number of span elements (%d) must divisible by 3 (row %d)") %
221 spanElementCount %
row));
224 fits.readTableArray(
row, _spanCol, spanElementCount, &spanElements.front());
225 std::vector<int>::iterator j = spanElements.begin();
226 while (j != spanElements.end()) {
234 std::make_shared<geom::SpanSet>(
std::move(spansVector)));
235 if (peakElementCount) {
236 if (peakElementCount % 3) {
238 afw::fits::FitsError,
239 afw::fits::makeErrorMessage(
240 fits.fptr, fits.status,
241 boost::format(
"Number of peak elements (%d) must divisible by 3 (row %d)") %
242 peakElementCount %
row));
245 fits.readTableArray(
row, _peakCol, peakElementCount, &peakElements.front());
246 std::vector<float>::iterator j = peakElements.begin();
247 while (j != peakElements.end()) {
251 fp->addPeak(
x,
y, value);
254 record.setFootprint(fp);
257 if (_heavyPixCol < 0) {
260 std::size_t heavyPixElementCount = fits.getTableArraySize(
row, _heavyPixCol);
261 std::size_t heavyMaskElementCount = fits.getTableArraySize(
row, _heavyMaskCol);
262 std::size_t heavyVarElementCount = fits.getTableArraySize(
row, _heavyVarCol);
263 if (heavyPixElementCount > 0) {
265 if ((heavyPixElementCount != N) || (heavyMaskElementCount != N) || (heavyVarElementCount != N)) {
267 afw::fits::FitsError,
268 afw::fits::makeErrorMessage(
269 fits.fptr, fits.status,
270 boost::format(
"Number of HeavyFootprint elements (pix %d, mask %d, var %d) "
271 "must all be equal to footprint area (%d)") %
272 heavyPixElementCount % heavyMaskElementCount % heavyVarElementCount %
276 using HeavyFootprint = detection::HeavyFootprint<float>;
278 fits.readTableArray(
row, _heavyPixCol, N, heavy->getImageArray().getData());
279 fits.readTableArray(
row, _heavyMaskCol, N, heavy->getMaskArray().getData());
280 fits.readTableArray(
row, _heavyVarCol, N, heavy->getVarianceArray().getData());
281 record.setFootprint(heavy);
294class SourceFootprintReader :
public io::FitsColumnReader {
296 static void setup(io::FitsSchemaInputMapper &
mapper,
int ioFlags) {
297 auto item =
mapper.find(
"footprint");
299 if (
mapper.hasArchive()) {
301 new SourceFootprintReader(ioFlags & SOURCE_IO_NO_HEAVY_FOOTPRINTS, item->column));
308 SourceFootprintReader(
bool noHeavy,
int column) : _noHeavy(noHeavy), _column(column) {}
310 void readCell(BaseRecord &record,
std::size_t row, fits::Fits &fits,
313 fits.readTableScalar<
int>(
row, _column,
id);
315 if (_noHeavy && footprint->isHeavy()) {
325 footprint.reset(
new Footprint(*footprint));
327 static_cast<SourceRecord &
>(record).setFootprint(footprint);
335class SourceFitsReader :
public io::FitsReader {
337 SourceFitsReader() :
afw::table::io::FitsReader(
"SOURCE") {}
341 bool stripMetadata)
const override {
345 OldSourceFootprintReader::setup(
mapper, *metadata, ioFlags, stripMetadata);
348 SourceFootprintReader::setup(
mapper, ioFlags);
350 table->setMetadata(metadata);
358static SourceFitsReader
const sourceFitsReader;
370 setCoord(wcs.pixelToSky(center));
373 auto err = getCentroidErr();
382 setCoord(wcs.pixelToSky(center));
385 auto err = getCentroidErr();
395 _footprint = s._footprint;
402 if (!checkSchema(schema)) {
404 "Schema for Source must contain at least the keys defined by getMinimalSchema().");
410 :
SimpleTable(schema, idFactory), _slots(schema) {}
417 if (
alias.compare(0, 4,
"slot") != 0) {
423SourceTable::MinimalSchema::MinimalSchema() {
425 parent = schema.addField<
RecordId>(
"parent",
"unique ID of parent source");
428SourceTable::MinimalSchema &SourceTable::getMinimalSchema() {
429 static MinimalSchema it;
434 return std::make_shared<SourceFitsWriter>(
fitsfile, flags);
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
afw::table::Key< int > footprintKey
A simple struct that combines the two arguments that must be passed to most cfitsio routines and cont...
Tag types used to declare specialized field types.
Base class for all records.
Schema getSchema() const
Return the table's schema.
static ErrorKey getErrorKey(Schema const &schema)
Defines the fields and offsets for a table.
Table class that must contain a unique ID field and a celestial coordinate field.
static Schema makeMinimalSchema()
Return a minimal schema for Simple tables and records.
std::shared_ptr< IdFactory > getIdFactory()
Return the object that generates IDs for the table (may be null).
Record class that contains measurements made on a single exposure.
virtual void _assign(BaseRecord const &other)
Called by assign() after transferring fields to allow subclass data members to be copied.
void updateCoord(geom::SkyWcs const &wcs, bool include_covariance=true)
Update the coord field using the given Wcs and the field in the centroid slot.
Table class that contains measurements made on a single exposure.
static std::shared_ptr< SourceTable > make(Schema const &schema, std::shared_ptr< IdFactory > const &idFactory)
Construct a new table.
SourceTable(Schema const &schema, std::shared_ptr< IdFactory > const &idFactory)
std::shared_ptr< BaseRecord > _makeRecord() override
Default-construct an associated record (protected implementation).
std::shared_ptr< io::FitsWriter > makeFitsWriter(fits::Fits *fitsfile, int flags) const override
std::shared_ptr< BaseTable > _clone() const override
Clone implementation with noncovariant return types.
void handleAliasChange(std::string const &alias) override
Reports invalid arguments.
Reports errors in the logical structure of the program.
T emplace_back(T... args)
daf::base::PropertySet * set
Eigen::Matrix2f calculateCoordCovariance(geom::SkyWcs const &wcs, lsst::geom::Point2D center, Eigen::Matrix2f err)
Calculate covariance for sky coordinates.
@ SOURCE_IO_NO_FOOTPRINTS
Do not read/write footprints at all.
void handleAliasChange(std::string const &alias, Schema const &schema)
Handle a callback from an AliasMap informing the table that an alias has changed.
std::shared_ptr< io::OutputArchive > _archive