I recently merged some changes to how afw.image.ExposureInfo
stores its info. The goal is to make ExposureInfo
more flexible in what it can do, as discussed by @jbosch last year.
First, the good news for long-time Stack users is that the changes are backwards-compatible – all existing methods of ExposureInfo
behave the same as before, old files can be read by the new code, and new files can be read by old code. This means that Exposure
's FITS persistence is still on version 1 (which was introduced to accommodate PhotoCalib
).
In addition to its previous capabilities, ExposureInfo
now has a set of methods (setComponent
, getComponent
, hasComponent
, removeComponent
) that let you attach almost arbitrary objects to an ExposureInfo
. The objects can be of any class that inherits from the afw.typehandling.Storable
mixin; these classes do not need to be in afw
itself. Storable
inherits from afw.table.io.Persistable
, and you must provide the usual persistence code if you want a component to get saved to FITS files (consistent with ExposureInfo
's previous behavior, any component whose isPersistable
method returns false
will be silently ignored).
Most of the data previously associated with an ExposureInfo
can be manipulated with the new interface. The exceptions are the metadata, filter, and visit info, which could not be migrated without breaking backwards compatibility.
Python API
In Python, generic components are associated with string keys:
info.setComponent("ExposureFlags", myFlags)
if info.hasComponent("ExposureFlags"):
flags = info.getComponent("ExposureFlags")
The ExposureInfo
class exposes string constants for all the standard components that have been migrated to the new system. These are called KEY_
followed by the uppercased name used in the old-style get
/set
methods:
>>> ExposureInfo.KEY_WCS
'SKYWCS'
>>> info.getWcs() == info.getComponent(ExposureInfo.KEY_WCS)
True
The values of these strings are set by the need for backward compatibility with the old persistence format, so unfortunately we can’t change them. Be careful to use 'SKYWCS'
and not 'wcs'
!
C++ API
In C++, generic components are associated with afw::typehandling::Key
objects that provide type information. Due to table::io
limitations, components must be passed by shared pointer for now:
info.setComponent(typehandling::makeKey<shared_ptr<FlagInfo>>("ExposureFlags"s),
myFlags);
auto key = typehandling::makeKey<shared_ptr<FlagInfo>>("ExposureFlags"s);
if info.hasComponent(key) {
shared_ptr<FlagInfo> flags = info.getComponent(key);
}
Calls to setComponent
with a key that doesn’t match the value will not compile. Like the old-style getters, getComponent
will return a null pointer if there’s no object of the requested type (the type need not match the original key exactly).
ExposureInfo
exposes the same KEY_
constants in C++ as in Python, but as Key
objects:
>>> cout << ExposureInfo::KEY_WCS;
SKYWCS<std::shared_ptr<lsst::afw::geom::SkyWcs>>
Future Plans
The other half of Jim’s discussion focused on replacing table.io
persistence with a more flexible framework. The changes to ExposureInfo
so far make only minor changes to how persistence is handled, but give us more room to maneuver in how we deal with the bigger issue (mostly because ExposureInfo
components no longer need to be in a dependency of afw.image
).
Moving away from table.io
will break the persistence format (one reason I was so careful about backward-compatibility for now), but will free us from having to implement ExposureInfo
components in C++, from arbitrary type restrictions like shared_ptr
, or from needing special treatment for classes that are lower-level than afw
(in particular, daf.base.PropertySet
).