LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
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 73 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 80 of file fitTanSipWcs.py.

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

◆ 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 183 of file fitTanSipWcs.py.

183  def initialWcs(self, matches, wcs):
184  """Generate a guess Wcs from the astrometric matches
185 
186  We create a Wcs anchored at the center of the matches, with the scale
187  of the input Wcs. This is necessary because matching returns only
188  matches with no estimated Wcs, and the input Wcs is a wild guess.
189  We're using the best of each: positions from the matches, and scale
190  from the input Wcs.
191 
192  Parameters
193  ----------
194  matches : `list` of `lsst.afw.table.ReferenceMatch`
195  List of sources matched to references.
196  wcs : `lsst.afw.geom.SkyWcs`
197  Current WCS.
198 
199  Returns
200  -------
201  newWcs : `lsst.afw.geom.SkyWcs`
202  Initial WCS guess from estimated crpix and crval.
203  """
204  crpix = lsst.geom.Extent2D(0, 0)
205  crval = lsst.sphgeom.Vector3d(0, 0, 0)
206  for mm in matches:
207  crpix += lsst.geom.Extent2D(mm.second.getCentroid())
208  crval += mm.first.getCoord().getVector()
209  crpix /= len(matches)
210  crval /= len(matches)
211  newWcs = afwGeom.makeSkyWcs(crpix=lsst.geom.Point2D(crpix),
212  crval=lsst.geom.SpherePoint(crval),
213  cdMatrix=wcs.getCdMatrix())
214  return newWcs
215 
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 263 of file fitTanSipWcs.py.

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

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

Member Data Documentation

◆ ConfigClass

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

Definition at line 76 of file fitTanSipWcs.py.


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