22__all__ = [
"FitAffineWcsTask",
"FitAffineWcsConfig",
"TransformedSkyWcsMaker"]
27from scipy.linalg
import lstsq
34from lsst.utils.timer
import timeMethod
36from ._measAstromLib
import makeMatchStatisticsInRadians
37from .setMatchDistance
import setMatchDistance
44 """Config for FitTanSipWcsTask."""
49 """Fit a TAN-SIP WCS given a list of reference object/source matches.
51 This WCS fitter should be used on top of a cameraGeom distortion model as
52 the model assumes that only a shift the WCS center position
and a small
53 affine transform are required.
55 ConfigClass = FitAffineWcsConfig
56 _DefaultName = "fitAffineWcs"
66 """Fit a simple Affine transform with a shift to the matches and update
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.
76 The following fields are read:
78 - match.first (reference object) coord
79 - match.second (source) centroid
81 The following fields are written:
83 - match.first (reference object) centroid,
84 - match.second (source) centroid
85 - match.distance (on sky separation,
in radians)
90 Ignored; present
for consistency
with FitSipDistortionTask.
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".
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".
104 Ignored; present
for consistency
with FitSipDistortionTask.
108 result : `lsst.pipe.base.Struct`
109 with the following fields:
112 - ``scatterOnSky`` : median on-sky separation between reference
113 objects
and sources
in "matches" (`lsst.afw.geom.Angle`)
121 back = wcsMaker.frameDict.getMapping(wcsMaker.frameMax, wcsMaker.frameMax-1)
122 forward = wcsMaker.lastMapBeforeSky
146 A = np.zeros((len(matches)*2, 6), dtype=float)
147 b = np.empty(len(matches)*2, dtype=float)
154 for i, match
in enumerate(matches):
155 refCoord = match.first.getCoord()
156 b[i*2:i*2+2] = back.applyForward(refCoord)
158 srcCentroid = match.second.getCentroid()
159 val = forward.applyForward(srcCentroid)
166 fit = lstsq(A, b, lapack_driver=
'gelsy')[0]
168 self.log.debug(
"Linear shift in x: %.3f, y: %.3f, "
169 "Affine matrix: [[%.6f, %.6f], [%.6f, %.6f]]...",
171 fit[0], fit[1], fit[2], fit[3])
174 wcs = wcsMaker.makeWcs(fit[4:], fit[:4].reshape((2, 2)))
177 if refCat
is not None:
178 self.log.debug(
"Updating centroids in refCat")
181 self.log.warning(
"Updating reference object centroids in match list; refCat is None")
184 refList=[match.first
for match
in matches])
186 if sourceCat
is not None:
187 self.log.debug(
"Updating coords in sourceCat")
190 self.log.warning(
"Updating source coords in match list; sourceCat is None")
193 sourceList=[match.second
for match
in matches])
194 setMatchDistance(matches)
196 stats = makeMatchStatisticsInRadians(wcs,
198 lsst.afw.math.MEDIAN)
199 scatterOnSky = stats.getValue() * radians
201 self.log.debug(
"In fitter scatter %.4f", scatterOnSky.asArcseconds())
203 return lsst.pipe.base.Struct(
205 scatterOnSky=scatterOnSky,
210 """Convenience class for appending a shifting an input SkyWcs on sky and
211 appending an affine transform.
213 The class assumes that all frames are sequential and are mapped one
to the
219 WCS to decompose
and append affine matrix
and shift
in on sky
233 for domain
in domains])
252 def makeWcs(self, linearShift, affMatrix):
253 """Apply a shift and affine transform to the WCS internal to this
256 A new SkyWcs
with these transforms applied
is returns.
260 linearShift : `numpy.ndarray`, (2,)
261 A linear shift to apply at the same time
as applying the affine
263 aff_matrix :
'numpy.ndarray', (3, 3)
264 Affine matrix to apply to the mapping/transform to add to the
270 Wcs
with a final shift
and affine transform applied.
276 iwcsToSkyWcs = makeSkyWcs(
279 np.array([[1., 0.], [0., 1.]]))
280 iwcToSkyMap = iwcsToSkyWcs.getFrameDict().getMapping(
"PIXELS",
"SKY")
285 newMapping = newMapping.then(astshim.ShiftMap(linearShift))
290 outputFrameDict = astshim.FrameDict(
294 outputFrameDict.addFrame(
298 elif frameIdx >= self.
mapTo:
301 outputFrameDict.addFrame(
303 self.
frameDict.getMapping(frameIdx, frameIdx + 1),
306 outputFrameDict.addFrame(
309 iwcsToSkyWcs.getFrameDict().getFrame(
"SKY"))
311 return SkyWcs(outputFrameDict)
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Custom catalog class for record/table subclasses that are guaranteed to have an ID,...
An integer coordinate rectangle.
fitWcs(self, matches, initWcs, bbox=None, refCat=None, sourceCat=None, exposure=None)
void updateRefCentroids(geom::SkyWcs const &wcs, ReferenceCollection &refList)
Update centroids in a collection of reference objects.
void updateSourceCoords(geom::SkyWcs const &wcs, SourceCollection &sourceList)
Update sky coordinates in a collection of source objects.
Lightweight representation of a geometric match between two records.