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.");
81 _mapper = SchemaMapper(t->getSchema(),
true);
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();
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);
115 footprint.
reset(
new afw::detection::Footprint(*footprint));
117 int footprintArchiveId =
_archive.put(footprint);
118 _outRecord->
set(_footprintKey, footprintArchiveId);
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");
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,
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()) {
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 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,
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()) {
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;
377 _footprint = s._footprint;
386 "Schema for Source must contain at least the keys defined by getMinimalSchema().");
399 if (alias.
compare(0, 4,
"slot") != 0) {
405SourceTable::MinimalSchema::MinimalSchema() {
407 parent =
schema.addField<
RecordId>(
"parent",
"unique ID of parent source");
410SourceTable::MinimalSchema &SourceTable::getMinimalSchema() {
411 static MinimalSchema it;
416 return std::make_shared<SourceFitsWriter>(fitsfile, flags);
424 auto record = constructRecord<SourceRecord>();
table::Key< std::string > name
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
table::Key< table::Array< std::uint8_t > > wcs
afw::table::Key< int > footprintKey
Base class for all records.
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
void assign(BaseRecord const &other)
Copy all field values from other to this, requiring that they have equal schemas.
void set(Key< T > const &key, U const &value)
Set value of a field for the given key.
Schema getSchema() const
Return the table's schema.
static std::shared_ptr< BaseTable > make(Schema const &schema)
Construct a new table.
A custom container class for records, based on std::vector.
A FunctorKey used to get or set a lsst::geom::Point from an (x,y) pair of int or double Keys.
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.
Schema const getOutputSchema() const
Return the output schema (copy-on-write).
Schema & editOutputSchema()
Return a reference to the output schema that allows it to be modified in place.
void addMinimalSchema(Schema const &minimal, bool doMap=true)
Add the given minimal schema to the output schema.
void setCoord(lsst::geom::SpherePoint const &coord)
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).
Custom catalog class for record/table subclasses that are guaranteed to have an ID,...
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)
Update the coord field using the given Wcs and the field in the centroid slot.
CentroidSlotDefinition::MeasValue getCentroid() const
Get the value of the Centroid slot measurement.
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< BaseTable > _clone() const override
Clone implementation with noncovariant return types.
void handleAliasChange(std::string const &alias) override
static bool checkSchema(Schema const &other)
Return true if the given schema is a valid SourceTable schema.
virtual void _writeRecord(BaseRecord const &source)
Write an individual record.
virtual void _writeTable(std::shared_ptr< BaseTable const > const &table, std::size_t nRows)
Write a table and its schema.
Reports invalid arguments.
Reports errors in the logical structure of the program.
T emplace_back(T... args)
std::string makeErrorMessage(std::string const &fileName="", int status=0, std::string const &msg="")
Return an error message reflecting FITS I/O errors.
lsst::afw::detection::Footprint Footprint
@ SOURCE_IO_NO_FOOTPRINTS
Do not read/write footprints at all.
@ SOURCE_IO_NO_HEAVY_FOOTPRINTS
Read/write heavy footprints as non-heavy footprints.
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