LSSTApplications  20.0.0
LSSTDataManagementBasePackage
fitsExposureFormatter.py
Go to the documentation of this file.
1 # This file is part of obs_base.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (http://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 
22 __all__ = ("FitsExposureFormatter", )
23 
24 from astro_metadata_translator import fix_header
25 from lsst.daf.butler import Formatter
26 from lsst.afw.image import ExposureFitsReader
27 
28 
29 class FitsExposureFormatter(Formatter):
30  """Interface for reading and writing Exposures to and from FITS files.
31  """
32  extension = ".fits"
33  _metadata = None
34 
35  @property
36  def metadata(self):
37  """The metadata read from this file. It will be stripped as
38  components are extracted from it
39  (`lsst.daf.base.PropertyList`).
40  """
41  if self._metadata is None:
42  self._metadata = self.readMetadata()
43  return self._metadata
44 
45  def readMetadata(self):
46  """Read all header metadata directly into a PropertyList.
47 
48  Returns
49  -------
50  metadata : `~lsst.daf.base.PropertyList`
51  Header metadata.
52  """
53  from lsst.afw.image import readMetadata
54  md = readMetadata(self.fileDescriptor.location.path)
55  fix_header(md)
56  return md
57 
58  def stripMetadata(self):
59  """Remove metadata entries that are parsed into components.
60 
61  This is only called when just the metadata is requested; stripping
62  entries there forces code that wants other components to ask for those
63  components directly rather than trying to extract them from the
64  metadata manually, which is fragile. This behavior is an intentional
65  change from Gen2.
66 
67  Parameters
68  ----------
69  metadata : `~lsst.daf.base.PropertyList`
70  Header metadata, to be modified in-place.
71  """
72  # TODO: make sure this covers everything, by delegating to something
73  # that doesn't yet exist in afw.image.ExposureInfo.
74  from lsst.afw.image import bboxFromMetadata
75  from lsst.afw.geom import makeSkyWcs
76  bboxFromMetadata(self.metadata) # always strips
77  makeSkyWcs(self.metadata, strip=True)
78 
79  def readComponent(self, component, parameters=None):
80  """Read a component held by the Exposure.
81 
82  Parameters
83  ----------
84  component : `str`, optional
85  Component to read from the file.
86  parameters : `dict`, optional
87  If specified, a dictionary of slicing parameters that
88  overrides those in ``fileDescriptor``.
89 
90  Returns
91  -------
92  obj : component-dependent
93  In-memory component object.
94 
95  Raises
96  ------
97  KeyError
98  Raised if the requested component cannot be handled.
99  """
100  componentMap = {'wcs': ('readWcs', False),
101  'coaddInputs': ('readCoaddInputs', False),
102  'psf': ('readPsf', False),
103  'image': ('readImage', True),
104  'mask': ('readMask', True),
105  'variance': ('readVariance', True),
106  'photoCalib': ('readPhotoCalib', False),
107  'bbox': ('readBBox', True),
108  'xy0': ('readXY0', True),
109  'metadata': ('readMetadata', False),
110  'filter': ('readFilter', False),
111  'polygon': ('readValidPolygon', False),
112  'apCorrMap': ('readApCorrMap', False),
113  'visitInfo': ('readVisitInfo', False),
114  'transmissionCurve': ('readTransmissionCurve', False),
115  'detector': ('readDetector', False),
116  'extras': ('readExtraComponents', False),
117  'exposureInfo': ('readExposureInfo', False),
118  }
119  method, hasParams = componentMap.get(component, None)
120 
121  if method:
122  reader = ExposureFitsReader(self.fileDescriptor.location.path)
123  caller = getattr(reader, method, None)
124 
125  if caller:
126  if parameters is None:
127  parameters = self.fileDescriptor.parameters
128  if parameters is None:
129  parameters = {}
130  self.fileDescriptor.storageClass.validateParameters(parameters)
131 
132  if hasParams and parameters:
133  return caller(**parameters)
134  else:
135  return caller()
136  else:
137  raise KeyError(f"Unknown component requested: {component}")
138 
139  def readFull(self, parameters=None):
140  """Read the full Exposure object.
141 
142  Parameters
143  ----------
144  parameters : `dict`, optional
145  If specified a dictionary of slicing parameters that overrides
146  those in ``fileDescriptor`.
147 
148  Returns
149  -------
150  exposure : `~lsst.afw.image.Exposure`
151  Complete in-memory exposure.
152  """
153  fileDescriptor = self.fileDescriptor
154  if parameters is None:
155  parameters = fileDescriptor.parameters
156  if parameters is None:
157  parameters = {}
158  fileDescriptor.storageClass.validateParameters(parameters)
159  try:
160  output = fileDescriptor.storageClass.pytype(fileDescriptor.location.path, **parameters)
161  except TypeError:
162  reader = ExposureFitsReader(fileDescriptor.location.path)
163  output = reader.read(**parameters)
164  return output
165 
166  def read(self, component=None, parameters=None):
167  """Read data from a file.
168 
169  Parameters
170  ----------
171  component : `str`, optional
172  Component to read from the file. Only used if the `StorageClass`
173  for reading differed from the `StorageClass` used to write the
174  file.
175  parameters : `dict`, optional
176  If specified, a dictionary of slicing parameters that
177  overrides those in ``fileDescriptor``.
178 
179  Returns
180  -------
181  inMemoryDataset : `object`
182  The requested data as a Python object. The type of object
183  is controlled by the specific formatter.
184 
185  Raises
186  ------
187  ValueError
188  Component requested but this file does not seem to be a concrete
189  composite.
190  KeyError
191  Raised when parameters passed with fileDescriptor are not
192  supported.
193  """
194  fileDescriptor = self.fileDescriptor
195  if fileDescriptor.readStorageClass != fileDescriptor.storageClass:
196  if component == "metadata":
197  self.stripMetadata()
198  return self.metadata
199  elif component is not None:
200  return self.readComponent(component, parameters)
201  else:
202  raise ValueError("Storage class inconsistency ({} vs {}) but no"
203  " component requested".format(fileDescriptor.readStorageClass.name,
204  fileDescriptor.storageClass.name))
205  return self.readFull()
206 
207  def write(self, inMemoryDataset):
208  """Write a Python object to a file.
209 
210  Parameters
211  ----------
212  inMemoryDataset : `object`
213  The Python object to store.
214 
215  Returns
216  -------
217  path : `str`
218  The `URI` where the primary file is stored.
219  """
220  # Update the location with the formatter-preferred file extension
221  self.fileDescriptor.location.updateExtension(self.extension)
222  inMemoryDataset.writeFits(self.fileDescriptor.location.path)
223  return self.fileDescriptor.location.pathInStore
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter.readMetadata
def readMetadata(self)
Definition: fitsExposureFormatter.py:45
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter.metadata
def metadata(self)
Definition: fitsExposureFormatter.py:36
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter._metadata
_metadata
Definition: fitsExposureFormatter.py:33
lsst::afw::image
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Definition: imageAlgorithm.dox:1
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter.write
def write(self, inMemoryDataset)
Definition: fitsExposureFormatter.py:207
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter.readFull
def readFull(self, parameters=None)
Definition: fitsExposureFormatter.py:139
pex.config.history.format
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter.read
def read(self, component=None, parameters=None)
Definition: fitsExposureFormatter.py:166
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter.readComponent
def readComponent(self, component, parameters=None)
Definition: fitsExposureFormatter.py:79
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter.stripMetadata
def stripMetadata(self)
Definition: fitsExposureFormatter.py:58
lsst::afw::image::bboxFromMetadata
lsst::geom::Box2I bboxFromMetadata(daf::base::PropertySet &metadata)
Determine the image bounding box from its metadata (FITS header)
Definition: Image.cc:694
lsst::afw::image::ExposureFitsReader
A FITS reader class for Exposures and their components.
Definition: ExposureFitsReader.h:42
lsst::afw::geom::makeSkyWcs
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition: SkyWcs.cc:526
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter.extension
string extension
Definition: fitsExposureFormatter.py:32
lsst.obs.base.fitsExposureFormatter.FitsExposureFormatter
Definition: fitsExposureFormatter.py:29
lsst::afw::geom
Definition: frameSetUtils.h:40