lsst.obs.base  18.1.0-11-g311e899
fitsRawFormatterBase.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__ = ("FitsRawFormatterBase",)
23 
24 from abc import ABCMeta, abstractmethod
25 
26 from astro_metadata_translator import ObservationInfo
27 
28 import lsst.afw.image
29 from lsst.daf.butler.formatters.fitsExposureFormatter import FitsExposureFormatter
30 from lsst.obs.base import MakeRawVisitInfoViaObsInfo
31 
32 
33 class FitsRawFormatterBase(FitsExposureFormatter, metaclass=ABCMeta):
34  """Abstract base class for reading and writing raw data to and from
35  FITS files.
36  """
37 
38  @property
39  @abstractmethod
40  def translatorClass(self):
41  """`~astro_metadata_translator.MetadataTranslator` to translate
42  metadata header to `~astro_metadata_translator.ObservationInfo`.
43  """
44  return None
45 
46  _observationInfo = None
47 
48  def readImage(self):
49  """Read just the image component of the Exposure.
50 
51  Returns
52  -------
53  image : `~lsst.afw.image.Image`
54  In-memory image component.
55  """
56  return lsst.afw.image.ImageU(self.fileDescriptor.location.path)
57 
58  def readMask(self):
59  """Read just the mask component of the Exposure.
60 
61  May return None (as the default implementation does) to indicate that
62  there is no mask information to be extracted (at least not trivially)
63  from the raw data. This will prohibit direct reading of just the mask,
64  and set the mask of the full Exposure to zeros.
65 
66  Returns
67  -------
68  mask : `~lsst.afw.image.Mask`
69  In-memory mask component.
70  """
71  return None
72 
73  def readVariance(self):
74  """Read just the variance component of the Exposure.
75 
76  May return None (as the default implementation does) to indicate that
77  there is no variance information to be extracted (at least not
78  trivially) from the raw data. This will prohibit direct reading of
79  just the variance, and set the variance of the full Exposure to zeros.
80 
81  Returns
82  -------
83  image : `~lsst.afw.image.Image`
84  In-memory variance component.
85  """
86  return None
87 
88  def stripMetadata(self):
89  """Remove metadata entries that are parsed into components.
90  """
91  self.makeVisitInfo()
92  self.makeWcs()
93 
94  def makeVisitInfo(self):
95  """Construct a VisitInfo from ObservationInfo.
96 
97  Returns
98  -------
99  visitInfo : `~lsst.afw.image.VisitInfo`
100  Structured metadata about the observation.
101  """
102  return MakeRawVisitInfoViaObsInfo.observationInfo2visitInfo(self.observationInfo)
103 
104  def makeWcs(self):
105  """Construct a SkyWcs from metadata.
106 
107  Returns
108  -------
109  wcs : `~lsst.afw.geom.SkyWcs`
110  Reversible mapping from pixel coordinates to sky coordinates.
111  """
112  from lsst.afw.geom import makeSkyWcs
113  return makeSkyWcs(self.metadata, strip=True)
114 
115  def makeFilter(self):
116  """Construct a Filter from metadata.
117 
118  Returns
119  -------
120  filter : `~lsst.afw.image.Filter`
121  Object that identifies the filter for this image.
122  """
123  raise NotImplementedError("Must be implemented by subclasses.")
124 
125  def readImageComponent(self, component):
126  """Read the image, mask, or variance component of an Exposure.
127 
128  Parameters
129  ----------
130  component : `str`, optional
131  Component to read from the file. Always one of "image",
132  "variance", or "mask".
133 
134  Returns
135  -------
136  image : `~lsst.afw.image.Image` or `~lsst.afw.image.Mask`
137  In-memory image, variance, or mask component.
138  """
139  if component == "image":
140  return self.readImage()
141  elif component == "mask":
142  return self.readMask()
143  elif component == "variance":
144  return self.readVariance()
145 
146  def readInfoComponent(self, component):
147  """Read a component held by ExposureInfo.
148 
149  The implementation provided by FitsRawFormatter provides only "wcs"
150  and "visitInfo". When adding support for other components, subclasses
151  should delegate to `super()` for those and update `readFull` with
152  similar logic.
153 
154  Parameters
155  ----------
156  component : `str`, optional
157  Component to read from the file.
158 
159  Returns
160  -------
161  obj : component-dependent
162  In-memory component object.
163  """
164  if component == "filter":
165  return self.makeFilter()
166  elif component == "visitInfo":
167  return self.makeVisitInfo()
168  elif component == "wcs":
169  return self.makeWcs()
170  return None
171 
172  def readFull(self, parameters=None):
173  """Read the full Exposure object.
174 
175  Parameters
176  ----------
177  parameters : `dict`, optional
178  If specified a dictionary of slicing parameters that overrides
179  those in ``self.fileDescriptor`.
180 
181  Returns
182  -------
183  exposure : `~lsst.afw.image.Exposure`
184  Complete in-memory exposure.
185  """
186  from lsst.afw.image import makeExposure, makeMaskedImage
187  full = makeExposure(makeMaskedImage(self.readImage()))
188  mask = self.readMask()
189  if mask is not None:
190  full.setMask(mask)
191  variance = self.readVariance()
192  if variance is not None:
193  full.setVariance(variance)
194  info = full.getInfo()
195  info.setWcs(self.makeWcs())
196  info.setFilter(self.makeFilter())
197  info.setVisitInfo(self.makeVisitInfo())
198  # We shouldn't have to call stripMetadata() here because that should
199  # have been done by makeVisitInfo and makeWcs (or by subclasses that
200  # strip metadata for other components when constructing them).
201  full.setMetadata(self.metadata)
202  return full
203 
204  def write(self, inMemoryDataset):
205  """Write a Python object to a file.
206 
207  Parameters
208  ----------
209  inMemoryDataset : `object`
210  The Python object to store.
211 
212  Returns
213  -------
214  path : `str`
215  The `URI` where the primary file is stored.
216  """
217  raise NotImplementedError("Raw data cannot be `put`.")
218 
219  @property
220  def observationInfo(self):
221  """The `~astro_metadata_translator.ObservationInfo` extracted from
222  this file's metadata (`~astro_metadata_translator.ObservationInfo`,
223  read-only).
224  """
225  if self._observationInfo is None:
226  self._observationInfo = ObservationInfo(self.metadata, translator_class=self.translatorClass)
227  return self._observationInfo