LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
Public Member Functions | Static Public Attributes | List of all members
lsst.meas.astrom.fitTanSipWcs.FitTanSipWcsTask Class Reference
Inheritance diagram for lsst.meas.astrom.fitTanSipWcs.FitTanSipWcsTask:

Public Member Functions

def fitWcs (self, matches, initWcs, bbox=None, refCat=None, sourceCat=None, exposure=None)
 
def initialWcs (self, matches, wcs)
 
def rejectMatches (self, matches, wcs, rejected)
 
def plotFit (self, matches, wcs, rejected)
 

Static Public Attributes

 ConfigClass = FitTanSipWcsConfig
 

Detailed Description

Fit a TAN-SIP WCS given a list of reference object/source matches.

Definition at line 74 of file fitTanSipWcs.py.

Member Function Documentation

◆ fitWcs()

def lsst.meas.astrom.fitTanSipWcs.FitTanSipWcsTask.fitWcs (   self,
  matches,
  initWcs,
  bbox = None,
  refCat = None,
  sourceCat = None,
  exposure = None 
)
Fit a TAN-SIP WCS from a list of reference object/source matches

Parameters
----------
matches : `list` of `lsst.afw.table.ReferenceMatch`
    The following fields are read:

    - match.first (reference object) coord
    - match.second (source) centroid

    The following fields are written:

    - match.first (reference object) centroid,
    - match.second (source) centroid
    - match.distance (on sky separation, in radians)

initWcs : `lsst.afw.geom.SkyWcs`
    initial WCS
bbox : `lsst.geom.Box2I`
    the region over which the WCS will be valid (an lsst:afw::geom::Box2I);
    if None or an empty box then computed from matches
refCat : `lsst.afw.table.SimpleCatalog`
    reference object catalog, or None.
    If provided then all centroids are updated with the new WCS,
    otherwise only the centroids for ref objects in matches are updated.
    Required fields are "centroid_x", "centroid_y", "coord_ra", and "coord_dec".
sourceCat : `lsst.afw.table.SourceCatalog`
    source catalog, or None.
    If provided then coords are updated with the new WCS;
    otherwise only the coords for sources in matches are updated.
    Required fields are "slot_Centroid_x", "slot_Centroid_y", and "coord_ra", and "coord_dec".
exposure : `lsst.afw.image.Exposure`
    Ignored; present for consistency with FitSipDistortionTask.

Returns
-------
result : `lsst.pipe.base.Struct`
    with the following fields:

    - ``wcs`` :  the fit WCS (`lsst.afw.geom.SkyWcs`)
    - ``scatterOnSky`` :  median on-sky separation between reference
      objects and sources in "matches" (`lsst.afw.geom.Angle`)

Definition at line 81 of file fitTanSipWcs.py.

81  def fitWcs(self, matches, initWcs, bbox=None, refCat=None, sourceCat=None, exposure=None):
82  """Fit a TAN-SIP WCS from a list of reference object/source matches
83 
84  Parameters
85  ----------
86  matches : `list` of `lsst.afw.table.ReferenceMatch`
87  The following fields are read:
88 
89  - match.first (reference object) coord
90  - match.second (source) centroid
91 
92  The following fields are written:
93 
94  - match.first (reference object) centroid,
95  - match.second (source) centroid
96  - match.distance (on sky separation, in radians)
97 
98  initWcs : `lsst.afw.geom.SkyWcs`
99  initial WCS
100  bbox : `lsst.geom.Box2I`
101  the region over which the WCS will be valid (an lsst:afw::geom::Box2I);
102  if None or an empty box then computed from matches
103  refCat : `lsst.afw.table.SimpleCatalog`
104  reference object catalog, or None.
105  If provided then all centroids are updated with the new WCS,
106  otherwise only the centroids for ref objects in matches are updated.
107  Required fields are "centroid_x", "centroid_y", "coord_ra", and "coord_dec".
108  sourceCat : `lsst.afw.table.SourceCatalog`
109  source catalog, or None.
110  If provided then coords are updated with the new WCS;
111  otherwise only the coords for sources in matches are updated.
112  Required fields are "slot_Centroid_x", "slot_Centroid_y", and "coord_ra", and "coord_dec".
113  exposure : `lsst.afw.image.Exposure`
114  Ignored; present for consistency with FitSipDistortionTask.
115 
116  Returns
117  -------
118  result : `lsst.pipe.base.Struct`
119  with the following fields:
120 
121  - ``wcs`` : the fit WCS (`lsst.afw.geom.SkyWcs`)
122  - ``scatterOnSky`` : median on-sky separation between reference
123  objects and sources in "matches" (`lsst.afw.geom.Angle`)
124  """
125  if bbox is None:
126  bbox = lsst.geom.Box2I()
127 
128  import lsstDebug
129  debug = lsstDebug.Info(__name__)
130 
131  wcs = self.initialWcs(matches, initWcs)
132  rejected = np.zeros(len(matches), dtype=bool)
133  for rej in range(self.config.numRejIter):
134  sipObject = self._fitWcs([mm for i, mm in enumerate(matches) if not rejected[i]], wcs)
135  wcs = sipObject.getNewWcs()
136  rejected = self.rejectMatches(matches, wcs, rejected)
137  if rejected.sum() == len(rejected):
138  raise RuntimeError("All matches rejected in iteration %d" % (rej + 1,))
139  self.log.debug(
140  "Iteration %d of astrometry fitting: rejected %f outliers, out of %d total matches.",
141  rej, rejected.sum(), len(rejected)
142  )
143  if debug.plot:
144  print("Plotting fit after rejection iteration %d/%d" % (rej + 1, self.config.numRejIter))
145  self.plotFit(matches, wcs, rejected)
146  # Final fit after rejection
147  sipObject = self._fitWcs([mm for i, mm in enumerate(matches) if not rejected[i]], wcs)
148  wcs = sipObject.getNewWcs()
149  if debug.plot:
150  print("Plotting final fit")
151  self.plotFit(matches, wcs, rejected)
152 
153  if refCat is not None:
154  self.log.debug("Updating centroids in refCat")
155  afwTable.updateRefCentroids(wcs, refList=refCat)
156  else:
157  self.log.warning("Updating reference object centroids in match list; refCat is None")
158  afwTable.updateRefCentroids(wcs, refList=[match.first for match in matches])
159 
160  if sourceCat is not None:
161  self.log.debug("Updating coords in sourceCat")
162  afwTable.updateSourceCoords(wcs, sourceList=sourceCat)
163  else:
164  self.log.warning("Updating source coords in match list; sourceCat is None")
165  afwTable.updateSourceCoords(wcs, sourceList=[match.second for match in matches])
166 
167  self.log.debug("Updating distance in match list")
168  setMatchDistance(matches)
169 
170  scatterOnSky = sipObject.getScatterOnSky()
171 
172  if scatterOnSky.asArcseconds() > self.config.maxScatterArcsec:
173  raise pipeBase.TaskError(
174  "Fit failed: median scatter on sky = %0.3f arcsec > %0.3f config.maxScatterArcsec" %
175  (scatterOnSky.asArcseconds(), self.config.maxScatterArcsec))
176 
177  return pipeBase.Struct(
178  wcs=wcs,
179  scatterOnSky=scatterOnSky,
180  )
181 
An integer coordinate rectangle.
Definition: Box.h:55
void updateRefCentroids(geom::SkyWcs const &wcs, ReferenceCollection &refList)
Update centroids in a collection of reference objects.
Definition: wcsUtils.cc:72
void updateSourceCoords(geom::SkyWcs const &wcs, SourceCollection &sourceList)
Update sky coordinates in a collection of source objects.
Definition: wcsUtils.cc:95

◆ initialWcs()

def lsst.meas.astrom.fitTanSipWcs.FitTanSipWcsTask.initialWcs (   self,
  matches,
  wcs 
)
Generate a guess Wcs from the astrometric matches

We create a Wcs anchored at the center of the matches, with the scale
of the input Wcs.  This is necessary because matching returns only
matches with no estimated Wcs, and the input Wcs is a wild guess.
We're using the best of each: positions from the matches, and scale
from the input Wcs.

Parameters
----------
matches : `list` of `lsst.afw.table.ReferenceMatch`
    List of sources matched to references.
wcs : `lsst.afw.geom.SkyWcs`
    Current WCS.

Returns
-------
newWcs : `lsst.afw.geom.SkyWcs`
    Initial WCS guess from estimated crpix and crval.

Definition at line 182 of file fitTanSipWcs.py.

182  def initialWcs(self, matches, wcs):
183  """Generate a guess Wcs from the astrometric matches
184 
185  We create a Wcs anchored at the center of the matches, with the scale
186  of the input Wcs. This is necessary because matching returns only
187  matches with no estimated Wcs, and the input Wcs is a wild guess.
188  We're using the best of each: positions from the matches, and scale
189  from the input Wcs.
190 
191  Parameters
192  ----------
193  matches : `list` of `lsst.afw.table.ReferenceMatch`
194  List of sources matched to references.
195  wcs : `lsst.afw.geom.SkyWcs`
196  Current WCS.
197 
198  Returns
199  -------
200  newWcs : `lsst.afw.geom.SkyWcs`
201  Initial WCS guess from estimated crpix and crval.
202  """
203  crpix = lsst.geom.Extent2D(0, 0)
204  crval = lsst.sphgeom.Vector3d(0, 0, 0)
205  for mm in matches:
206  crpix += lsst.geom.Extent2D(mm.second.getCentroid())
207  crval += mm.first.getCoord().getVector()
208  crpix /= len(matches)
209  crval /= len(matches)
210  newWcs = afwGeom.makeSkyWcs(crpix=lsst.geom.Point2D(crpix),
211  crval=lsst.geom.SpherePoint(crval),
212  cdMatrix=wcs.getCdMatrix())
213  return newWcs
214 
Point in an unspecified spherical coordinate system.
Definition: SpherePoint.h:57
Vector3d is a vector in ℝ³ with components stored in double precision.
Definition: Vector3d.h:44
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition: SkyWcs.cc:521

◆ plotFit()

def lsst.meas.astrom.fitTanSipWcs.FitTanSipWcsTask.plotFit (   self,
  matches,
  wcs,
  rejected 
)
Plot the fit

We create four plots, for all combinations of (dx, dy) against
(x, y).  Good points are black, while rejected points are red.

Parameters
----------
matches : `list` of `lsst.afw.table.ReferenceMatch`
    List of sources matched to references.
wcs : `lsst.afw.geom.SkyWcs`
    Fitted WCS.
rejected : array-like of `bool`
    Array of matches rejected from the fit.

Definition at line 262 of file fitTanSipWcs.py.

262  def plotFit(self, matches, wcs, rejected):
263  """Plot the fit
264 
265  We create four plots, for all combinations of (dx, dy) against
266  (x, y). Good points are black, while rejected points are red.
267 
268  Parameters
269  ----------
270  matches : `list` of `lsst.afw.table.ReferenceMatch`
271  List of sources matched to references.
272  wcs : `lsst.afw.geom.SkyWcs`
273  Fitted WCS.
274  rejected : array-like of `bool`
275  Array of matches rejected from the fit.
276  """
277  try:
278  import matplotlib.pyplot as plt
279  except ImportError as e:
280  self.log.warning("Unable to import matplotlib: %s", e)
281  return
282 
283  fit = [wcs.skyToPixel(m.first.getCoord()) for m in matches]
284  x1 = np.array([ff.getX() for ff in fit])
285  y1 = np.array([ff.getY() for ff in fit])
286  x2 = np.array([m.second.getCentroid().getX() for m in matches])
287  y2 = np.array([m.second.getCentroid().getY() for m in matches])
288 
289  dx = x1 - x2
290  dy = y1 - y2
291 
292  good = np.logical_not(rejected)
293 
294  figure = plt.figure()
295  axes = figure.add_subplot(2, 2, 1)
296  axes.plot(x2[good], dx[good], 'ko')
297  axes.plot(x2[rejected], dx[rejected], 'ro')
298  axes.set_xlabel("x")
299  axes.set_ylabel("dx")
300 
301  axes = figure.add_subplot(2, 2, 2)
302  axes.plot(x2[good], dy[good], 'ko')
303  axes.plot(x2[rejected], dy[rejected], 'ro')
304  axes.set_xlabel("x")
305  axes.set_ylabel("dy")
306 
307  axes = figure.add_subplot(2, 2, 3)
308  axes.plot(y2[good], dx[good], 'ko')
309  axes.plot(y2[rejected], dx[rejected], 'ro')
310  axes.set_xlabel("y")
311  axes.set_ylabel("dx")
312 
313  axes = figure.add_subplot(2, 2, 4)
314  axes.plot(y2[good], dy[good], 'ko')
315  axes.plot(y2[rejected], dy[rejected], 'ro')
316  axes.set_xlabel("y")
317  axes.set_ylabel("dy")
318 
319  plt.show()

◆ rejectMatches()

def lsst.meas.astrom.fitTanSipWcs.FitTanSipWcsTask.rejectMatches (   self,
  matches,
  wcs,
  rejected 
)
Flag deviant matches

We return a boolean numpy array indicating whether the corresponding
match should be rejected.  The previous list of rejections is used
so we can calculate uncontaminated statistics.

Parameters
----------
matches : `list` of `lsst.afw.table.ReferenceMatch`
    List of sources matched to references.
wcs : `lsst.afw.geom.SkyWcs`
    Fitted WCS.
rejected : array-like of `bool`
    Array of matches rejected from the fit. Unused.

Returns
-------
rejectedMatches : `ndarray` of type `bool`
    Matched objects found to be outside of tolerance.

Definition at line 235 of file fitTanSipWcs.py.

235  def rejectMatches(self, matches, wcs, rejected):
236  """Flag deviant matches
237 
238  We return a boolean numpy array indicating whether the corresponding
239  match should be rejected. The previous list of rejections is used
240  so we can calculate uncontaminated statistics.
241 
242  Parameters
243  ----------
244  matches : `list` of `lsst.afw.table.ReferenceMatch`
245  List of sources matched to references.
246  wcs : `lsst.afw.geom.SkyWcs`
247  Fitted WCS.
248  rejected : array-like of `bool`
249  Array of matches rejected from the fit. Unused.
250 
251  Returns
252  -------
253  rejectedMatches : `ndarray` of type `bool`
254  Matched objects found to be outside of tolerance.
255  """
256  fit = [wcs.skyToPixel(m.first.getCoord()) for m in matches]
257  dx = np.array([ff.getX() - mm.second.getCentroid().getX() for ff, mm in zip(fit, matches)])
258  dy = np.array([ff.getY() - mm.second.getCentroid().getY() for ff, mm in zip(fit, matches)])
259  good = np.logical_not(rejected)
260  return (dx > self.config.rejSigma*dx[good].std()) | (dy > self.config.rejSigma*dy[good].std())
261 
STL namespace.

Member Data Documentation

◆ ConfigClass

lsst.meas.astrom.fitTanSipWcs.FitTanSipWcsTask.ConfigClass = FitTanSipWcsConfig
static

Definition at line 77 of file fitTanSipWcs.py.


The documentation for this class was generated from the following file: