Coverage for python/lsst/meas/extensions/piff/wcs_wrapper.py: 32%

72 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-22 11:51 +0000

1from lsst.geom import Point2D 

2 

3import galsim 

4import numpy as np 

5 

6 

7class CelestialWcsWrapper(galsim.wcs.CelestialWCS): 

8 """Wrap a `lsst.afw.geom.SkyWcs` in a GalSim WCS. 

9 

10 Parameters 

11 ---------- 

12 pix_to_sky : `lsst.afw.geom.SkyWcs` 

13 WCS to wrap. 

14 origin : `galsim.PositionD`, optional 

15 Origin in image coordinates. 

16 """ 

17 def __init__(self, pix_to_sky, origin=None): 

18 if origin is None: 

19 # Use galsim._PositionD as it's faster than galsim.PositionD 

20 origin = galsim._PositionD(0.0, 0.0) 

21 self._pix_to_sky = pix_to_sky 

22 self._origin = origin 

23 self._color = None 

24 

25 @property 

26 def origin(self): 

27 """The image coordinate position to use as the origin. 

28 """ 

29 return self._origin 

30 

31 def _radec(self, x, y, color=None): 

32 x1 = np.atleast_1d(x) 

33 y1 = np.atleast_1d(y) 

34 

35 ra, dec = self._pix_to_sky.pixelToSkyArray(x1, y1) 

36 

37 if np.ndim(x) == np.ndim(y) == 0: 

38 return ra[0], dec[0] 

39 else: 

40 # Sanity checks that the inputs are the same shape. 

41 assert np.ndim(x) == np.ndim(y) 

42 assert x.shape == y.shape 

43 return ra, dec 

44 

45 def _xy(self, ra, dec, color=None): 

46 r1 = np.atleast_1d(ra) 

47 d1 = np.atleast_1d(dec) 

48 

49 x, y = self._pix_to_sky.skyToPixelArray(r1, d1) 

50 

51 if np.ndim(ra) == np.ndim(dec) == 0: 

52 return x[0], y[0] 

53 else: 

54 # Sanity checks that the inputs are the same shape. 

55 assert np.ndim(ra) == np.ndim(dec) 

56 assert ra.shape == dec.shape 

57 return x, y 

58 

59 def _newOrigin(self, origin): 

60 """Return a new CelestialWcsWrapper with new origin. 

61 

62 Parameters 

63 ---------- 

64 origin : `galsim.PositionD`, optional 

65 Origin in image coordinates. 

66 

67 Returns 

68 ------- 

69 ret : `CelestialWcsWrapper` 

70 Transformed WCS. 

71 """ 

72 return CelestialWcsWrapper(self._pix_to_sky, origin=origin) 

73 

74 

75class UVWcsWrapper(galsim.wcs.EuclideanWCS): 

76 """Wrap a `lsst.afw.geom.TransformPoint2ToPoint2[2->2]` in a GalSim WCS. 

77 

78 Parameters 

79 ---------- 

80 pix_to_field : `lsst.afw.geom.TransformPoint2ToPoint2[2->2]` 

81 Transform to wrap. Most likely PIXELS -> FIELD_ANGLE, but other 2D -> 

82 2D transforms should be possible. 

83 origin : `galsim.PositionD`, optional 

84 Origin in image coordinates. 

85 world_origin : `galsim.PositionD`, optional 

86 Origin in world coordinates. 

87 

88 Notes 

89 ----- 

90 

91 GalSim EuclideanWCS assumes 

92 u = ufunc(x-x0, y-y0) + u0 

93 v = vfunc(x-x0, y-y0) + v0 

94 where u,v are world (likely field angles), and (x, y) are pixels. 

95 GalSim also assumes that origin = x0, y0 and world_origin = u0, v0. 

96 I might have naively thought that uv(origin) == world_origin, but 

97 that appears to not be required. So we're just going to leave both 

98 free. 

99 """ 

100 _rad_to_arcsec = 206264.80624709636 

101 

102 def __init__(self, pix_to_field, origin=None, world_origin=None): 

103 if origin is None: 

104 # Use galsim._PositionD as it's faster than galsim.PositionD 

105 origin = galsim._PositionD(0.0, 0.0) 

106 if world_origin is None: 

107 world_origin = galsim._PositionD(0.0, 0.0) 

108 self._pix_to_field = pix_to_field 

109 self._origin = origin 

110 self._world_origin = world_origin 

111 self._mapping = self._pix_to_field.getMapping() 

112 self._color = None 

113 

114 @property 

115 def origin(self): 

116 """The image coordinate position to use as the origin. 

117 """ 

118 return self._origin 

119 

120 @property 

121 def world_origin(self): 

122 """The world coordinate position to use as the origin. 

123 """ 

124 return self._world_origin 

125 

126 def _xyTouv(self, x, y, color=None): 

127 """Convert image coordinates to world coordinates. 

128 

129 Parameters 

130 ---------- 

131 x, y : ndarray 

132 Image coordinates. 

133 color : ndarray 

134 Color to use in transformation. Unused currently. 

135 

136 Returns 

137 ------- 

138 u, v : ndarray 

139 World coordinates. 

140 """ 

141 x = x - self.x0 

142 y = y - self.y0 

143 u, v = self._mapping.applyForward(np.vstack((x, y))) 

144 u *= self._rad_to_arcsec 

145 v *= self._rad_to_arcsec 

146 return u + self.u0, v + self.v0 

147 

148 def _uvToxy(self, u, v, color): 

149 """Convert world coordinates to image coordinates. 

150 

151 Parameters 

152 ---------- 

153 u, v : ndarray 

154 World coordinates. 

155 color : ndarray 

156 Color to use in transformation. Unused currently. 

157 

158 Returns 

159 ------- 

160 x, y : ndarray 

161 Image coordinates. 

162 """ 

163 u = (u - self.u0)/self._rad_to_arcsec 

164 v = (v - self.v0)/self._rad_to_arcsec 

165 x, y = self._mapping.applyInverse(np.vstack((u, v))) 

166 return x + self.x0, y + self.y0 

167 

168 def _posToWorld(self, image_pos, color=None): 

169 """Convert image coordinate to world coordinate. 

170 

171 Parameters 

172 ---------- 

173 image_pos : galsim.PositionD 

174 Image coordinate. 

175 color : ndarray 

176 Color to use in transformation. Unused currently. 

177 

178 Returns 

179 ------- 

180 world_pos : galsim.PositionD 

181 World coordinate. 

182 """ 

183 # Use galsim._PositionD as it's faster than galsim.PositionD 

184 return galsim._PositionD(*self._xyTouv(image_pos.x, image_pos.y, color)) 

185 

186 def _local(self, image_pos, color=None): 

187 """Compute local Jacobian WCS. 

188 

189 Parameters 

190 ---------- 

191 image_pos : galsim.PositionD 

192 Image position at which to compute local WCS. 

193 color : ndarray 

194 Color to use in transformation. Unused currently. 

195 

196 Returns 

197 ------- 

198 local : galsim.JacobianWCS 

199 Local linear approximation to WCS. 

200 """ 

201 x0 = image_pos.x - self.x0 

202 y0 = image_pos.y - self.y0 

203 

204 duvdxy = self._pix_to_field.getJacobian(Point2D(x0, y0)) 

205 return galsim.JacobianWCS( 

206 *(duvdxy.ravel()*self._rad_to_arcsec) 

207 ) 

208 

209 def _newOrigin(self, origin, world_origin): 

210 """Return a new UVWcsWrapper with new origins. 

211 

212 Parameters 

213 ---------- 

214 origin : `galsim.PositionD`, optional 

215 Origin in image coordinates. 

216 world_origin : `galsim.PositionD`, optional 

217 Origin in world coordinates. 

218 

219 Returns 

220 ------- 

221 ret : `UVWcsWrapper` 

222 Transformed WCS. 

223 """ 

224 return UVWcsWrapper(self._pix_to_field, origin, world_origin)