4 #include "boost/iterator/transform_iterator.hpp" 25 struct PersistenceHelper {
51 class 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.");
81 _mapper = SchemaMapper(t->getSchema(),
true);
82 _mapper.addMinimalSchema(t->getSchema(),
true);
83 _footprintKey =
_mapper.editOutputSchema().addField<
int>(
"footprint",
"archive ID for Footprint");
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();
105 _fits->writeKey(
"AFW_TYPE",
"SOURCE",
"Tells lsst::afw to load this as a Source table.");
108 void SourceFitsWriter::_writeRecord(BaseRecord
const &r) {
109 SourceRecord
const &record =
static_cast<SourceRecord
const &
>(r);
111 _outRecord->assign(record,
_mapper);
117 int footprintArchiveId =
_archive.put(footprint);
118 _outRecord->set(_footprintKey, footprintArchiveId);
151 class 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");
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.");
208 SourceRecord &record =
static_cast<SourceRecord &
>(baseRecord);
212 int spanElementCount = fits.getTableArraySize(row, _spanCol);
213 int peakElementCount = fits.getTableArraySize(row, _peakCol);
214 if (spanElementCount) {
215 if (spanElementCount % 3) {
217 afw::fits::FitsError,
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());
226 while (j != spanElements.
end()) {
230 spansVector.
push_back(geom::Span(y, x0, x1));
234 std::make_shared<geom::SpanSet>(
std::move(spansVector)));
235 if (peakElementCount) {
236 if (peakElementCount % 3) {
238 afw::fits::FitsError,
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());
247 while (j != peakElements.
end()) {
251 fp->addPeak(x, y, value);
254 record.setFootprint(fp);
257 if (_heavyPixCol < 0) {
260 int heavyPixElementCount = fits.getTableArraySize(row, _heavyPixCol);
261 int heavyMaskElementCount = fits.getTableArraySize(row, _heavyMaskCol);
262 int heavyVarElementCount = fits.getTableArraySize(row, _heavyVarCol);
263 if (heavyPixElementCount > 0) {
264 int N = fp->getArea();
265 if ((heavyPixElementCount != N) || (heavyMaskElementCount != N) || (heavyVarElementCount != N)) {
267 afw::fits::FitsError,
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 typedef detection::HeavyFootprint<float, image::MaskPixel, image::VariancePixel> HeavyFootprint;
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);
294 class SourceFootprintReader :
public io::FitsColumnReader {
296 static void setup(io::FitsSchemaInputMapper &
mapper,
int ioFlags) {
297 auto item = mapper.find(
"footprint");
299 if (mapper.hasArchive()) {
308 SourceFootprintReader(
bool noHeavy,
int column) : _noHeavy(noHeavy), _column(column) {}
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);
335 class 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);
358 static SourceFitsReader
const sourceFitsReader;
377 _footprint = s._footprint;
384 if (!checkSchema(schema)) {
386 "Schema for Source must contain at least the keys defined by getMinimalSchema().");
392 :
SimpleTable(schema, idFactory), _slots(schema) {}
399 if (alias.
compare(0, 4,
"slot") != 0) {
405 SourceTable::MinimalSchema::MinimalSchema() {
407 parent =
schema.addField<
RecordId>(
"parent",
"unique ID of parent source");
408 schema.getCitizen().markPersistent();
411 SourceTable::MinimalSchema &SourceTable::getMinimalSchema() {
412 static MinimalSchema it;
417 return std::make_shared<SourceFitsWriter>(fitsfile, flags);
425 auto record = constructRecord<SourceRecord>();
void handleAliasChange(std::string const &alias) override
Defines the fields and offsets for a table.
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
static Schema makeMinimalSchema()
Return a minimal schema for Simple tables and records.
void updateCoord(geom::SkyWcs const &wcs)
Update the coord field using the given Wcs and the field in the centroid slot.
A custom container class for records, based on std::vector.
lsst::geom::SpherePoint pixelToSky(lsst::geom::Point2D const &pixel) const
Compute sky position(s) from pixel position(s)
Schema getSchema() const
Return the table's schema.
A simple struct that combines the two arguments that must be passed to most cfitsio routines and cont...
virtual void _assign(BaseRecord const &other)
Called by assign() after transferring fields to allow subclass data members to be copied...
table::Key< table::Array< std::uint8_t > > wcs
std::shared_ptr< BaseRecord > _makeRecord() override
Default-construct an associated record (protected implementation).
Table class that must contain a unique ID field and a celestial coordinate field. ...
A base class for image defects.
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
T dynamic_pointer_cast(T... args)
Reports errors in the logical structure of the program.
virtual void _writeTable(std::shared_ptr< BaseTable const > const &table, std::size_t nRows)
Write a table and its schema.
std::shared_ptr< BaseTable > _clone() const override
Clone implementation with noncovariant return types.
std::shared_ptr< io::OutputArchive > _archive
static std::shared_ptr< SourceTable > make(Schema const &schema, std::shared_ptr< IdFactory > const &idFactory)
Construct a new table.
Table class that contains measurements made on a single exposure.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Base class for all records.
afw::table::Key< int > footprintKey
static std::shared_ptr< BaseTable > make(Schema const &schema)
Construct a new table.
Read/write heavy footprints as non-heavy footprints.
lsst::afw::detection::Footprint Footprint
virtual void _writeRecord(BaseRecord const &source)
Write an individual record.
Reports invalid arguments.
ItemVariant const * other
Do not read/write footprints at all.
Record class that contains measurements made on a single exposure.
std::shared_ptr< IdFactory > getIdFactory()
Return the object that generates IDs for the table (may be null).
std::string makeErrorMessage(std::string const &fileName="", int status=0, std::string const &msg="")
Return an error message reflecting FITS I/O errors.
SourceTable(Schema const &schema, std::shared_ptr< IdFactory > const &idFactory)
void handleAliasChange(std::string const &alias, Schema const &schema)
Handle a callback from an AliasMap informing the table that an alias has changed. ...