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