65 exposure=None):
66 """Fit a simple Affine transform with a shift to the matches and update
67 the WCS.
68
69 This method assumes that the distortion model of the telescope is
70 applied correctly and is accurate with only a slight rotation,
71 rotation, and "squish" required to fit to the reference locations.
72
73 Parameters
74 ----------
75 matches : `list` of `lsst.afw.table.ReferenceMatch`
76 The following fields are read:
77
78 - match.first (reference object) coord
79 - match.second (source) centroid
80
81 The following fields are written:
82
83 - match.first (reference object) centroid,
84 - match.second (source) centroid
85 - match.distance (on sky separation, in radians)
86
87 initWcs : `lsst.afw.geom.SkyWcs`
88 initial WCS
89 bbox : `lsst.geom.Box2I`
90 Ignored; present for consistency with FitSipDistortionTask.
91 refCat : `lsst.afw.table.SimpleCatalog`
92 reference object catalog, or None.
93 If provided then all centroids are updated with the new WCS,
94 otherwise only the centroids for ref objects in matches are
95 updated. Required fields are "centroid_x", "centroid_y",
96 "coord_ra", and "coord_dec".
97 sourceCat : `lsst.afw.table.SourceCatalog`
98 source catalog, or None.
99 If provided then coords are updated with the new WCS;
100 otherwise only the coords for sources in matches are updated.
101 Required fields are "slot_Centroid_x", "slot_Centroid_y", and
102 "coord_ra", and "coord_dec".
103 exposure : `lsst.afw.image.Exposure`
104 Ignored; present for consistency with FitSipDistortionTask.
105
106 Returns
107 -------
108 result : `lsst.pipe.base.Struct`
109 with the following fields:
110
111 - ``wcs`` : the fit WCS (`lsst.afw.geom.SkyWcs`)
112 - ``scatterOnSky`` : median on-sky separation between reference
113 objects and sources in "matches" (`lsst.afw.geom.Angle`)
114 """
115
116
117 wcsMaker = TransformedSkyWcsMaker(initWcs)
118
119
120
121 back = wcsMaker.frameDict.getMapping(wcsMaker.frameMax, wcsMaker.frameMax-1)
122 forward = wcsMaker.lastMapBeforeSky
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146 A = np.zeros((len(matches)*2, 6), dtype=float)
147 b = np.empty(len(matches)*2, dtype=float)
148
149
150 A[::2, 4] = 1
151 A[1::2, 5] = 1
152
153
154 for i, match in enumerate(matches):
155 refCoord = match.first.getCoord()
156 b[i*2:i*2+2] = back.applyForward(refCoord)
157
158 srcCentroid = match.second.getCentroid()
159 val = forward.applyForward(srcCentroid)
160 A[i*2, :2] = val
161 A[i*2+1, 2:4] = val
162
163
164
165
166 fit = lstsq(A, b, lapack_driver='gelsy')[0]
167
168 self.log.debug("Linear shift in x: %.3f, y: %.3f, "
169 "Affine matrix: [[%.6f, %.6f], [%.6f, %.6f]]...",
170 fit[4], fit[5],
171 fit[0], fit[1], fit[2], fit[3])
172
173
174 wcs = wcsMaker.makeWcs(fit[4:], fit[:4].reshape((2, 2)))
175
176
177 if refCat is not None:
178 self.log.debug("Updating centroids in refCat")
180 else:
181 self.log.warning("Updating reference object centroids in match list; refCat is None")
183 wcs,
184 refList=[match.first for match in matches])
185
186 if sourceCat is not None:
187 self.log.debug("Updating coords in sourceCat")
189 else:
190 self.log.warning("Updating source coords in match list; sourceCat is None")
192 wcs,
193 sourceList=[match.second for match in matches])
194 setMatchDistance(matches)
195
196 stats = makeMatchStatisticsInRadians(wcs,
197 matches,
198 lsst.afw.math.MEDIAN)
199 scatterOnSky = stats.getValue() * radians
200
201 self.log.debug("In fitter scatter %.4f", scatterOnSky.asArcseconds())
202
203 return lsst.pipe.base.Struct(
204 wcs=wcs,
205 scatterOnSky=scatterOnSky,
206 )
207
208
void updateRefCentroids(geom::SkyWcs const &wcs, ReferenceCollection &refList)
Update centroids in a collection of reference objects.
void updateSourceCoords(geom::SkyWcs const &wcs, SourceCollection &sourceList, bool include_covariance=true)
Update sky coordinates in a collection of source objects.