Skip to content

Commit

Permalink
Merge branch 'tickets/DM-27169' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
kfindeisen committed Dec 9, 2020
2 parents 3aeab58 + e3d7fdd commit d10a12c
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 30 deletions.
6 changes: 5 additions & 1 deletion python/lsst/obs/base/butler_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,11 @@ def _test_exposure(self, name):
self.assertEqual(exp.getDetector().getId(), self.butler_get_data.detectorIds[name])
self.assertEqual(exp.getDetector().getName(), self.butler_get_data.detector_names[name])
self.assertEqual(exp.getDetector().getSerial(), self.butler_get_data.detector_serials[name])
self.assertEqual(exp.getFilter().getName(), self.butler_get_data.filters[name])
# obs_test does not have physical filters, so include a fallback
exposureFilter = exp.getFilterLabel()
filterName = exposureFilter.physicalLabel if exposureFilter.hasPhysicalLabel() \
else exposureFilter.bandLabel
self.assertEqual(filterName, self.butler_get_data.filters[name])
exposureId = self.butler.get('ccdExposureId', dataId=self.dataIds[name])
self.assertEqual(exposureId, self.butler_get_data.exposureIds[name])
self.assertEqual(exp.getInfo().getVisitInfo().getExposureTime(), self.butler_get_data.exptimes[name])
Expand Down
8 changes: 8 additions & 0 deletions python/lsst/obs/base/cameraMapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,20 @@ def getVisitInfo(datasetType, pythonType, location, dataId):

setMethods("visitInfo", bypassImpl=getVisitInfo)

# TODO: deprecate in DM-27170, remove in DM-27177
def getFilter(datasetType, pythonType, location, dataId):
fitsReader = afwImage.ExposureFitsReader(location.getLocationsWithRoot()[0])
return fitsReader.readFilter()

setMethods("filter", bypassImpl=getFilter)

# TODO: deprecate in DM-27177, remove in DM-27811
def getFilterLabel(datasetType, pythonType, location, dataId):
fitsReader = afwImage.ExposureFitsReader(location.getLocationsWithRoot()[0])
return fitsReader.readFilterLabel()

setMethods("filterLabel", bypassImpl=getFilterLabel)

setMethods("detector",
mapImpl=lambda dataId, write=False:
dafPersist.ButlerLocation(
Expand Down
35 changes: 9 additions & 26 deletions python/lsst/obs/base/exposureAssembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

# Need to enable PSFs to be instantiated
import lsst.afw.detection # noqa: F401
from lsst.afw.image import makeExposure, makeMaskedImage, Filter, stripFilterKeywords
from lsst.afw.image import makeExposure, makeMaskedImage

from lsst.daf.butler import StorageClassDelegate

Expand All @@ -32,9 +32,9 @@ class ExposureAssembler(StorageClassDelegate):

EXPOSURE_COMPONENTS = set(("image", "variance", "mask", "wcs", "psf"))
EXPOSURE_INFO_COMPONENTS = set(("apCorrMap", "coaddInputs", "photoCalib", "metadata",
"filter", "transmissionCurve", "visitInfo",
"filterLabel", "transmissionCurve", "visitInfo",
"detector", "validPolygon"))
EXPOSURE_READ_COMPONENTS = {"bbox", "dimensions", "xy0"}
EXPOSURE_READ_COMPONENTS = {"bbox", "dimensions", "xy0", "filter"}

COMPONENT_MAP = {"bbox": "BBox", "xy0": "XY0"}
"""Map component name to actual getter name."""
Expand All @@ -44,9 +44,9 @@ def _groupRequestedComponents(self):
Returns
-------
expComps : `dict`
expComps : `set` [`str`]
Components associated with the top level Exposure.
expInfoComps : `dict`
expInfoComps : `set` [`str`]
Components associated with the ExposureInfo
Raises
Expand Down Expand Up @@ -95,7 +95,7 @@ def getComponent(self, composite, componentName):
# an ExposureInfo composite so trap for that and only get
# the ExposureInfo if the method is supported
composite = composite.getInfo()
return super().getComponent(composite, componentName)
return super().getComponent(composite, self.COMPONENT_MAP.get(componentName, componentName))
else:
raise AttributeError("Do not know how to retrieve component {} from {}".format(componentName,
type(composite)))
Expand Down Expand Up @@ -166,14 +166,6 @@ def disassemble(self, composite):
subset=expInfoItems, override=composite.getInfo())
components.update(fromExposureInfo)

# We must reproduce some of the metadata manipulation that occurs
# in ExposureInfo::FitsWriteData.

# Force the FILTER header to be overwritten
if "filter" in components and "metadata" in components:
md = components["metadata"].component
md["FILTER"] = components["filter"].component.getName()

return components

def assemble(self, components):
Expand Down Expand Up @@ -237,18 +229,8 @@ def assemble(self, components):
info.setDetector(components.pop("detector", None))
info.setTransmissionCurve(components.pop("transmissionCurve", None))

# Filter needs to be updated specially to match Exposure behavior
# from ExposureFitsReader::MetadataReader

# Override the serialized filter knowing that we are using FILTER
md = info.getMetadata()
if "filter" in components and "FILTER" in md:
filter = Filter(md, True)
stripFilterKeywords(md)
if filter.getName() != components["filter"].getName():
components["filter"] = filter

info.setFilter(components.pop("filter", None))
# TODO: switch back to "filter" as primary component in DM-27177
info.setFilterLabel(components.pop("filterLabel", None))

# If we have some components left over that is a problem
if components:
Expand Down Expand Up @@ -292,6 +274,7 @@ def selectResponsibleComponent(cls, readComponent: str, fromComponents) -> str:
"bbox": imageComponents,
"dimensions": imageComponents,
"xy0": imageComponents,
"filter": ["filterLabel"],
}
forwarder = forwarderMap.get(readComponent)
if forwarder is not None:
Expand Down
57 changes: 56 additions & 1 deletion python/lsst/obs/base/formatters/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

# TODO: remove this entire file in DM-27177

from __future__ import annotations

__all__ = ("FilterFormatter",)
__all__ = ("FilterFormatter", "FilterTranslator",)

import yaml
from lsst.afw.image import Filter
Expand All @@ -32,7 +34,9 @@
Type,
)

from lsst.afw.image import FilterLabel
from lsst.daf.butler.formatters.file import FileFormatter
from lsst.daf.butler import StorageClassDelegate


class FilterFormatter(FileFormatter):
Expand Down Expand Up @@ -139,3 +143,54 @@ def _toBytes(self, inMemoryDataset: Any) -> bytes:
filter["aliases"] = inMemoryDataset.getAliases()

return yaml.dump(filter).encode()


class FilterTranslator(StorageClassDelegate):
"""Derived-component converter for a Filter that has been stored as
a FilterLabel.
"""
# More complex than a Formatter that can read both Filter and FilterLabel,
# but can be phased out once Filter is gone without breaking compatibility
# with old FilterLabels.

def getComponent(self, label, derivedName):
"""Derive a Filter from a FilterLabel.
Parameters
----------
label : `~lsst.afw.image.FilterLabel`
The object to convert.
derivedName : `str`
Name of type to convert to. Only "filter" is supported.
Returns
-------
derived : `object`
The converted type. Can be `None`.
Raises
------
AttributeError
An unknown component was requested.
"""
if derivedName == "filter":
# Port of backwards-compatibility code in afw; don't want to
# expose it as API.

# Filters still have standard aliases, so can use almost any name
# to define them. Prefer afw_name or band because that's what most
# code assumes is Filter.getName().
if label == FilterLabel(band="r", physical="HSC-R2"):
return Filter("r2", force=True)
elif label == FilterLabel(band="i", physical="HSC-I2"):
return Filter("i2", force=True)
elif label == FilterLabel(physical="solid plate 0.0 0.0"):
return Filter("SOLID", force=True)
elif label.hasBandLabel():
return Filter(label.bandLabel, force=True)
else:
# FilterLabel guarantees at least one of band or physical
# is defined.
return Filter(label.physicalLabel, force=True)
else:
raise AttributeError(f"Do not know how to convert {type(label)} to {derivedName}")
3 changes: 3 additions & 0 deletions python/lsst/obs/base/formatters/fitsExposure.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ def readComponent(self, component, parameters=None):
'bbox': ('readBBox', True),
'dimensions': ('readBBox', True),
'xy0': ('readXY0', True),
# TODO: deprecate in DM-27170, remove in DM-27177
'filter': ('readFilter', False),
# TODO: deprecate in DM-27177, remove in DM-27811
'filterLabel': ('readFilterLabel', False),
'validPolygon': ('readValidPolygon', False),
'apCorrMap': ('readApCorrMap', False),
'visitInfo': ('readVisitInfo', False),
Expand Down
Binary file modified tests/data/calexp.fits
Binary file not shown.
6 changes: 4 additions & 2 deletions tests/test_butlerFits.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@

# Components present in the test file
COMPONENTS = {"wcs", "image", "mask", "coaddInputs", "psf", "visitInfo", "variance", "metadata", "photoCalib",
"filter", "validPolygon", "transmissionCurve", "detector", "apCorrMap"}
READ_COMPONENTS = {"bbox", "xy0", "dimensions"}
"filterLabel", "validPolygon", "transmissionCurve", "detector", "apCorrMap"}
READ_COMPONENTS = {"bbox", "xy0", "dimensions", "filter"}


class SimpleConfig(lsst.pex.config.Config):
Expand Down Expand Up @@ -279,6 +279,8 @@ def runExposureCompositePutGetTest(self, datasetTypeName: str) -> DatasetRef:
pass
elif compName == "filter":
self.assertEqual(component.getCanonicalName(), reference.getCanonicalName())
elif compName == "filterLabel":
self.assertEqual(component, reference)
elif compName == "visitInfo":
self.assertEqual(component.getExposureId(), reference.getExposureId(),
"VisitInfo comparison")
Expand Down
3 changes: 3 additions & 0 deletions tests/test_cameraMapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ def testImage(self):
butler = dafPersist.ButlerFactory(mapper=mapper).create()
image = butler.get("some", ccd=35)
self.assertEqual(image.getFilter().getName(), "r")
self.assertEqual(image.getFilterLabel().bandLabel, "r")

self.assertEqual(butler.get("some_bbox", ccd=35), image.getBBox())

Expand All @@ -306,6 +307,7 @@ def testGzImage(self):
butler = dafPersist.ButlerFactory(mapper=mapper).create()
image = butler.get("someGz", ccd=35)
self.assertEqual(image.getFilter().getName(), "r")
self.assertEqual(image.getFilterLabel().bandLabel, "r")

bbox = geom.BoxI(geom.Point2I(200, 100),
geom.Extent2I(300, 400))
Expand All @@ -324,6 +326,7 @@ def testFzImage(self):
butler = dafPersist.ButlerFactory(mapper=mapper).create()
image = butler.get("someFz", ccd=35)
self.assertEqual(image.getFilter().getName(), "r")
self.assertEqual(image.getFilterLabel().bandLabel, "r")

bbox = geom.BoxI(geom.Point2I(200, 100),
geom.Extent2I(300, 400))
Expand Down

0 comments on commit d10a12c

Please sign in to comment.