Coverage for python/lsst/obs/base/utils.py: 29%

54 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-06-23 02:50 -0700

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/>. 

21 

22__all__ = ("InitialSkyWcsError", "createInitialSkyWcs", "createInitialSkyWcsFromBoresight", "bboxFromIraf") 

23 

24import re 

25 

26import lsst.geom as geom 

27import lsst.pex.exceptions 

28from deprecated.sphinx import deprecated 

29from lsst.afw.cameraGeom import FIELD_ANGLE, PIXELS 

30from lsst.afw.geom.skyWcs import makeSkyWcs 

31from lsst.afw.image import RotType 

32from lsst.pipe.base import Instrument 

33 

34 

35class InitialSkyWcsError(Exception): 

36 """For handling failures when creating a SkyWcs from a camera geometry and 

37 boresight. 

38 

39 Typically used as a chained exception from a lower level exception. 

40 """ 

41 

42 pass 

43 

44 

45def createInitialSkyWcs(visitInfo, detector, flipX=False): 

46 """Create a SkyWcs from the visit information and detector geometry. 

47 

48 A typical usecase for this is to create the initial WCS for a newly-read 

49 raw exposure. 

50 

51 

52 Parameters 

53 ---------- 

54 visitInfo : `lsst.afw.image.VisitInfo` 

55 Where to get the telescope boresight and rotator angle from. 

56 detector : `lsst.afw.cameraGeom.Detector` 

57 Where to get the camera geomtry from. 

58 flipX : `bool`, optional 

59 If False, +X is along W, if True +X is along E. 

60 

61 Returns 

62 ------- 

63 skyWcs : `lsst.afw.geom.SkyWcs` 

64 The new composed WCS. 

65 

66 Raises 

67 ------ 

68 InitialSkyWcsError 

69 Raised if there is an error generating the SkyWcs, chained from the 

70 lower-level exception if available. 

71 """ 

72 if visitInfo.getRotType() != RotType.SKY: 

73 msg = ( 

74 f"Cannot create SkyWcs from camera geometry: rotator angle defined using " 

75 f"RotType={visitInfo.getRotType()} instead of SKY." 

76 ) 

77 raise InitialSkyWcsError(msg) 

78 orientation = visitInfo.getBoresightRotAngle() 

79 boresight = visitInfo.getBoresightRaDec() 

80 return createInitialSkyWcsFromBoresight(boresight, orientation, detector, flipX) 

81 

82 

83def createInitialSkyWcsFromBoresight(boresight, orientation, detector, flipX=False): 

84 """Create a SkyWcs from the telescope boresight and detector geometry. 

85 

86 A typical usecase for this is to create the initial WCS for a newly-read 

87 raw exposure. 

88 

89 Parameters 

90 ---------- 

91 boresight : `lsst.geom.SpherePoint` 

92 The ICRS boresight RA/Dec 

93 orientation : `lsst.geom.Angle` 

94 The rotation angle of the focal plane on the sky. 

95 detector : `lsst.afw.cameraGeom.Detector` 

96 Where to get the camera geomtry from. 

97 flipX : `bool`, optional 

98 If False, +X is along W, if True +X is along E. 

99 

100 Returns 

101 ------- 

102 skyWcs : `lsst.afw.geom.SkyWcs` 

103 The new composed WCS. 

104 

105 Raises 

106 ------ 

107 InitialSkyWcsError 

108 Raised if there is an error generating the SkyWcs, chained from the 

109 lower-level exception if available. 

110 """ 

111 try: 

112 pixelsToFieldAngle = detector.getTransform( 

113 detector.makeCameraSys(PIXELS), detector.makeCameraSys(FIELD_ANGLE) 

114 ) 

115 except lsst.pex.exceptions.InvalidParameterError as e: 

116 raise InitialSkyWcsError("Cannot compute PIXELS to FIELD_ANGLE Transform.") from e 

117 return makeSkyWcs(pixelsToFieldAngle, orientation, flipX, boresight) 

118 

119 

120def bboxFromIraf(irafBBoxStr): 

121 """Return a Box2I corresponding to an IRAF-style BBOX 

122 

123 [x0:x1,y0:y1] where x0 and x1 are the one-indexed start and end columns, 

124 and correspondingly y0 and y1 are the start and end rows. 

125 """ 

126 

127 mat = re.search(r"^\[([-\d]+):([-\d]+),([-\d]+):([-\d]+)\]$", irafBBoxStr) 

128 if not mat: 

129 raise RuntimeError('Unable to parse IRAF-style bbox "%s"' % irafBBoxStr) 

130 x0, x1, y0, y1 = [int(_) for _ in mat.groups()] 

131 

132 return geom.BoxI(geom.PointI(x0 - 1, y0 - 1), geom.PointI(x1 - 1, y1 - 1)) 

133 

134 

135@deprecated( 

136 reason="Replaced with lsst.pipe.base.Instrument.from_string. Will be removed after v25.", 

137 version="v24", 

138 category=FutureWarning, 

139) 

140def getInstrument(instrumentName, registry=None, collection_prefix=None): 

141 """Return an instance of a named instrument. 

142 

143 If the instrument name not is qualified (does not contain a '.') and a 

144 butler registry is provided, this will attempt to load the instrument using 

145 Instrument.fromName. Otherwise the instrument will be imported and 

146 instantiated. 

147 

148 Parameters 

149 ---------- 

150 instrumentName : string 

151 The name or fully-qualified class name of an instrument. 

152 registry : `lsst.daf.butler.Registry`, optional 

153 Butler registry to query to find information about the instrument, by 

154 default None 

155 collection_prefix : `str`, optional 

156 Prefix for collection names to use instead of the intrument's own name. 

157 This is primarily for use in simulated-data repositories, where the 

158 instrument name may not be necessary and/or sufficient to distinguish 

159 between collections. 

160 

161 Returns 

162 ------- 

163 Instrument subclass instance 

164 The instantiated instrument. 

165 

166 Raises 

167 ------ 

168 RuntimeError 

169 If the instrument can not be imported, instantiated, or obtained from 

170 the registry. 

171 TypeError 

172 If the instrument is not a subclass of lsst.obs.base.Instrument. 

173 """ 

174 return Instrument.from_string(instrumentName, registry, collection_prefix) 

175 

176 

177# TODO remove the impl in pipe_base? (NB this combines setDottedAtr AND the 

178# handling in ConfigValueAction.__call__) 

179def setDottedAttr(item, name, value): 

180 """Set an instance attribute (like `setattr` but accepting hierarchical 

181 names such as ``foo.bar.baz``) If the attribute can not be set as a string, 

182 will attempt to set the attribute with the result of eval'ing the value. 

183 

184 Parameters 

185 ---------- 

186 item : obj 

187 Object whose attribute is to be set. 

188 name : `str` 

189 Name of attribute to set. 

190 value : obj 

191 New value for the attribute. 

192 

193 Notes 

194 ----- 

195 For example if name is ``foo.bar.baz`` then ``item.foo.bar.baz`` 

196 is set to the specified value. 

197 

198 Raises 

199 ------ 

200 AttributeError 

201 If the item does not have a field specified by name that can be set. 

202 RuntimeError 

203 If the value can not be set as a string or rendered by eval, or if 

204 there is an error setting the attribute with the rendered value. 

205 """ 

206 subitem = item 

207 subnameList = name.split(".") 

208 for subname in subnameList[:-1]: 

209 subitem = getattr(subitem, subname) 

210 try: 

211 setattr(subitem, subnameList[-1], value) 

212 except AttributeError: 

213 raise AttributeError(f"No field: {name!r}") 

214 except Exception: 

215 try: 

216 v = eval(value, {}) 

217 except Exception: 

218 raise RuntimeError(f"Cannot render {value!r} as a value for {name!r}") 

219 try: 

220 setattr(subitem, subnameList[-1], v) 

221 except Exception as e: 

222 raise RuntimeError(f"Cannot set config. {name}={value!r}: {e}") 

223 

224 

225def setDottedAttrs(item, attrs): 

226 for name, value in attrs: 

227 setDottedAttr(item, name, value)