Coverage for python/lsst/obs/base/utils.py: 21%
65 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-12 01:53 -0800
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-12 01:53 -0800
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# (https://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 <https://www.gnu.org/licenses/>.
22__all__ = ('InitialSkyWcsError', 'createInitialSkyWcs', 'createInitialSkyWcsFromBoresight', 'bboxFromIraf')
24import re
25import lsst.geom as geom
27from . import Instrument
28from lsst.afw.cameraGeom import PIXELS, FIELD_ANGLE
29from lsst.afw.image import RotType
30from lsst.afw.geom.skyWcs import makeSkyWcs
31import lsst.pex.exceptions
32from lsst.utils import doImport
35class InitialSkyWcsError(Exception):
36 """For handling failures when creating a SkyWcs from a camera geometry and
37 boresight.
39 Typically used as a chained exception from a lower level exception.
40 """
41 pass
44def createInitialSkyWcs(visitInfo, detector, flipX=False):
45 """Create a SkyWcs from the visit information and detector geometry.
47 A typical usecase for this is to create the initial WCS for a newly-read
48 raw exposure.
51 Parameters
52 ----------
53 visitInfo : `lsst.afw.image.VisitInfo`
54 Where to get the telescope boresight and rotator angle from.
55 detector : `lsst.afw.cameraGeom.Detector`
56 Where to get the camera geomtry from.
57 flipX : `bool`, optional
58 If False, +X is along W, if True +X is along E.
60 Returns
61 -------
62 skyWcs : `lsst.afw.geom.SkyWcs`
63 The new composed WCS.
65 Raises
66 ------
67 InitialSkyWcsError
68 Raised if there is an error generating the SkyWcs, chained from the
69 lower-level exception if available.
70 """
71 if visitInfo.getRotType() != RotType.SKY:
72 msg = (f"Cannot create SkyWcs from camera geometry: rotator angle defined using "
73 f"RotType={visitInfo.getRotType()} instead of SKY.")
74 raise InitialSkyWcsError(msg)
75 orientation = visitInfo.getBoresightRotAngle()
76 boresight = visitInfo.getBoresightRaDec()
77 return createInitialSkyWcsFromBoresight(boresight, orientation, detector, flipX)
80def createInitialSkyWcsFromBoresight(boresight, orientation, detector, flipX=False):
81 """Create a SkyWcs from the telescope boresight and detector geometry.
83 A typical usecase for this is to create the initial WCS for a newly-read
84 raw exposure.
86 Parameters
87 ----------
88 boresight : `lsst.geom.SpherePoint`
89 The ICRS boresight RA/Dec
90 orientation : `lsst.geom.Angle`
91 The rotation angle of the focal plane on the sky.
92 detector : `lsst.afw.cameraGeom.Detector`
93 Where to get the camera geomtry from.
94 flipX : `bool`, optional
95 If False, +X is along W, if True +X is along E.
97 Returns
98 -------
99 skyWcs : `lsst.afw.geom.SkyWcs`
100 The new composed WCS.
102 Raises
103 ------
104 InitialSkyWcsError
105 Raised if there is an error generating the SkyWcs, chained from the
106 lower-level exception if available.
107 """
108 try:
109 pixelsToFieldAngle = detector.getTransform(detector.makeCameraSys(PIXELS),
110 detector.makeCameraSys(FIELD_ANGLE))
111 except lsst.pex.exceptions.InvalidParameterError as e:
112 raise InitialSkyWcsError("Cannot compute PIXELS to FIELD_ANGLE Transform.") from e
113 return makeSkyWcs(pixelsToFieldAngle, orientation, flipX, boresight)
116def bboxFromIraf(irafBBoxStr):
117 """Return a Box2I corresponding to an IRAF-style BBOX
119 [x0:x1,y0:y1] where x0 and x1 are the one-indexed start and end columns,
120 and correspondingly y0 and y1 are the start and end rows.
121 """
123 mat = re.search(r"^\[([-\d]+):([-\d]+),([-\d]+):([-\d]+)\]$", irafBBoxStr)
124 if not mat:
125 raise RuntimeError("Unable to parse IRAF-style bbox \"%s\"" % irafBBoxStr)
126 x0, x1, y0, y1 = [int(_) for _ in mat.groups()]
128 return geom.BoxI(geom.PointI(x0 - 1, y0 - 1), geom.PointI(x1 - 1, y1 - 1))
131def getInstrument(instrumentName, registry=None, collection_prefix=None):
132 """Return an instance of a named instrument.
134 If the instrument name not is qualified (does not contain a '.') and a
135 butler registry is provided, this will attempt to load the instrument using
136 Instrument.fromName. Otherwise the instrument will be imported and
137 instantiated.
139 Parameters
140 ----------
141 instrumentName : string
142 The name or fully-qualified class name of an instrument.
143 registry : `lsst.daf.butler.Registry`, optional
144 Butler registry to query to find information about the instrument, by
145 default None
146 collection_prefix : `str`, optional
147 Prefix for collection names to use instead of the intrument's own name.
148 This is primarily for use in simulated-data repositories, where the
149 instrument name may not be necessary and/or sufficient to distinguish
150 between collections.
152 Returns
153 -------
154 Instrument subclass instance
155 The instantiated instrument.
157 Raises
158 ------
159 RuntimeError
160 If the instrument can not be imported, instantiated, or obtained from
161 the registry.
162 TypeError
163 If the instrument is not a subclass of lsst.obs.base.Instrument.
164 """
165 if "." not in instrumentName and registry is not None:
166 try:
167 instr = Instrument.fromName(instrumentName, registry, collection_prefix=collection_prefix)
168 except Exception as err:
169 raise RuntimeError(
170 f"Could not get instrument from name: {instrumentName}. Failed with exception: {err}")
171 else:
172 try:
173 instr = doImport(instrumentName)
174 except Exception as err:
175 raise RuntimeError(f"Could not import instrument: {instrumentName}. Failed with exception: {err}")
176 instr = instr(collection_prefix=collection_prefix)
177 if not isinstance(instr, Instrument):
178 raise TypeError(f"{instrumentName} is not an Instrument subclass.")
179 return instr
182# TODO remove the impl in pipe_base? (NB this combines setDottedAtr AND the
183# handling in ConfigValueAction.__call__)
184def setDottedAttr(item, name, value):
185 """Set an instance attribute (like `setattr` but accepting hierarchical
186 names such as ``foo.bar.baz``) If the attribute can not be set as a string,
187 will attempt to set the attribute with the result of eval'ing the value.
189 Parameters
190 ----------
191 item : obj
192 Object whose attribute is to be set.
193 name : `str`
194 Name of attribute to set.
195 value : obj
196 New value for the attribute.
198 Notes
199 -----
200 For example if name is ``foo.bar.baz`` then ``item.foo.bar.baz``
201 is set to the specified value.
203 Raises
204 ------
205 AttributeError
206 If the item does not have a field specified by name that can be set.
207 RuntimeError
208 If the value can not be set as a string or rendered by eval, or if
209 there is an error setting the attribute with the rendered value.
210 """
211 subitem = item
212 subnameList = name.split(".")
213 for subname in subnameList[:-1]:
214 subitem = getattr(subitem, subname)
215 try:
216 setattr(subitem, subnameList[-1], value)
217 except AttributeError:
218 raise AttributeError(f"No field: {name!r}")
219 except Exception:
220 try:
221 v = eval(value, {})
222 except Exception:
223 raise RuntimeError(f"Cannot render {value!r} as a value for {name!r}")
224 try:
225 setattr(subitem, subnameList[-1], v)
226 except Exception as e:
227 raise RuntimeError(f"Cannot set config. {name}={value!r}: {e}")
230def setDottedAttrs(item, attrs):
231 for name, value in attrs:
232 setDottedAttr(item, name, value)