LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
Public Member Functions | Public Attributes | Static Public Attributes | Private Member Functions | Static Private Member Functions | Static Private Attributes | List of all members
lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask Class Reference

Basic implemeentation of the astrometry.net astrometrical fitter. More...

Inheritance diagram for lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask:

Public Member Functions

def __init__
 Construct an ANetBasicAstrometryTask. More...
 
def memusage
 
def useKnownWcs
 Return an InitialAstrometry object, just like determineWcs, but assuming the given input WCS is correct. More...
 
def determineWcs
 
def determineWcs2
 
def getBlindWcsSolution
 
def getSipWcsFromWcs
 Get a TAN-SIP WCS, starting from an existing WCS. More...
 
def getSipWcsFromCorrespondences
 
def plotSolution
 
def getColumnName
 
def joinMatchListWithCatalog
 

Public Attributes

 config
 
 refObjLoader
 

Static Public Attributes

 ConfigClass = ANetBasicAstrometryConfig
 

Private Member Functions

def _getImageParams
 
def _calculateSipTerms
 Iteratively calculate SIP distortions and regenerate matches based on improved WCS. More...
 
def _computeMatchStatsOnSky
 
def _getMatchList
 
def _solve
 
def _isGoodSource
 

Static Private Member Functions

def _trimBadPoints
 

Static Private Attributes

string _DefaultName = "aNetBasicAstrometry"
 

Detailed Description

Basic implemeentation of the astrometry.net astrometrical fitter.

A higher-level class ANetAstrometryTask takes care of dealing with the fact that the initial WCS is probably only a pure TAN SIP, yet we may have significant distortion and a good estimate for that distortion.

About Astrometry.net index files (astrometry_net_data):

There are three components of an index file: a list of stars (stored as a star kd-tree), a list of quadrangles of stars ("quad file") and a list of the shapes ("codes") of those quadrangles, stored as a code kd-tree.

Each index covers a region of the sky, defined by healpix nside and number, and a range of angular scales. In LSST, we share the list of stars in a part of the sky between multiple indexes. That is, the star kd-tree is shared between multiple indices (quads and code kd-trees). In the astrometry.net code, this is called a "multiindex".

It is possible to "unload" and "reload" multiindex (and index) objects. When "unloaded", they consume no FILE or mmap resources.

The multiindex object holds the star kd-tree and gives each index object it holds a pointer to it, so it is necessary to multiindex_reload_starkd() before reloading the indices it holds. The multiindex_unload() method, on the other hand, unloads its starkd and unloads each index it holds.

Definition at line 191 of file anetBasicAstrometry.py.

Constructor & Destructor Documentation

def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.__init__ (   self,
  config,
  andConfig = None,
  kwargs 
)

Construct an ANetBasicAstrometryTask.

Parameters
[in]configconfiguration (an instance of self.ConfigClass)
[in]andConfigastrometry.net data config (an instance of AstromNetDataConfig, or None); if None then use andConfig.py in the astrometry_net_data product (which must be setup)
[in]kwargsadditional keyword arguments for pipe_base Task.__init__
Exceptions
RuntimeErrorif andConfig is None and the configuration cannot be found, either because astrometry_net_data is not setup in eups or because the setup version does not include the file "andConfig.py"

Definition at line 227 of file anetBasicAstrometry.py.

228  **kwargs):
229  """!Construct an ANetBasicAstrometryTask
230 
231  @param[in] config configuration (an instance of self.ConfigClass)
232  @param[in] andConfig astrometry.net data config (an instance of AstromNetDataConfig, or None);
233  if None then use andConfig.py in the astrometry_net_data product (which must be setup)
234  @param[in] kwargs additional keyword arguments for pipe_base Task.\_\_init\_\_
235 
236  @throw RuntimeError if andConfig is None and the configuration cannot be found,
237  either because astrometry_net_data is not setup in eups
238  or because the setup version does not include the file "andConfig.py"
239  """
240  pipeBase.Task.__init__(self, config=config, **kwargs)
241  self.config = config
242  # this is not a subtask because it cannot safely be retargeted
243  self.refObjLoader = LoadAstrometryNetObjectsTask(config=self.config, andConfig=andConfig, log=self.log,
244  name="loadAN")
245  self.refObjLoader._readIndexFiles()

Member Function Documentation

def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask._calculateSipTerms (   self,
  origWcs,
  refCat,
  sourceCat,
  matches,
  bbox 
)
private

Iteratively calculate SIP distortions and regenerate matches based on improved WCS.

Parameters
[in]origWcsoriginal WCS object, probably (but not necessarily) a TAN WCS; this is used to set the baseline when determining whether a SIP solution is any better; it will be returned if no better SIP solution can be found.
[in]refCatreference source catalog
[in]sourceCatsources in the image to be solved
[in]matcheslist of supposedly matched sources, using the "origWcs".
[in]bboxbounding box of image, which is used when finding reverse SIP coefficients.

Definition at line 626 of file anetBasicAstrometry.py.

627  def _calculateSipTerms(self, origWcs, refCat, sourceCat, matches, bbox):
628  """!Iteratively calculate SIP distortions and regenerate matches based on improved WCS.
629 
630  @param[in] origWcs original WCS object, probably (but not necessarily) a TAN WCS;
631  this is used to set the baseline when determining whether a SIP
632  solution is any better; it will be returned if no better SIP solution
633  can be found.
634  @param[in] refCat reference source catalog
635  @param[in] sourceCat sources in the image to be solved
636  @param[in] matches list of supposedly matched sources, using the "origWcs".
637  @param[in] bbox bounding box of image, which is used when finding reverse SIP coefficients.
638  """
639  sipOrder = self.config.sipOrder
640  wcs = origWcs
641 
642  lastMatchSize = len(matches)
643  lastMatchStats = self._computeMatchStatsOnSky(wcs=wcs, matchList=matches)
644  for i in range(self.config.maxIter):
645  # fit SIP terms
646  try:
647  sipObject = astromSip.makeCreateWcsWithSip(matches, wcs, sipOrder, bbox)
648  proposedWcs = sipObject.getNewWcs()
649  self.plotSolution(matches, proposedWcs, bbox.getDimensions())
650  except pexExceptions.Exception as e:
651  self.log.warn('Failed to calculate distortion terms. Error: ' + str(e))
652  break
653 
654  # use new WCS to get new matchlist.
655  proposedMatchlist = self._getMatchList(sourceCat, refCat, proposedWcs)
656  proposedMatchSize = len(proposedMatchlist)
657  proposedMatchStats = self._computeMatchStatsOnSky(wcs=proposedWcs, matchList=proposedMatchlist)
658 
659  self.log.logdebug(
660  "SIP iteration %i: %i objects match, previous = %i;" %
661  (i, proposedMatchSize, lastMatchSize) +
662  " clipped mean scatter = %s arcsec, previous = %s; " %
663  (proposedMatchStats.distMean.asArcseconds(), lastMatchStats.distMean.asArcseconds()) +
664  " max match dist = %s arcsec, previous = %s" %
665  (proposedMatchStats.maxMatchDist.asArcseconds(),
666  lastMatchStats.maxMatchDist.asArcseconds())
667  )
668 
669  if lastMatchStats.maxMatchDist <= proposedMatchStats.maxMatchDist:
670  self.log.logdebug(
671  "Fit WCS: use iter %s because max match distance no better in next iter: " % (i-1,) +
672  " %g < %g arcsec" % (lastMatchStats.maxMatchDist.asArcseconds(),
673  proposedMatchStats.maxMatchDist.asArcseconds()))
674  break
675 
676 
677  wcs = proposedWcs
678  matches = proposedMatchlist
679  lastMatchSize = proposedMatchSize
680  lastMatchStats = proposedMatchStats
681 
682  return wcs, matches
def _calculateSipTerms
Iteratively calculate SIP distortions and regenerate matches based on improved WCS.
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask._computeMatchStatsOnSky (   self,
  wcs,
  matchList 
)
private
Compute on-sky radial distance statistics for a match list

@param[in] wcs  WCS for match list; an lsst.afw.image.Wcs
@param[in] matchList  list of matches between reference object and sources;
    a list of lsst.afw.table.ReferenceMatch;
    the source centroid and reference object coord are read

@return a pipe_base Struct containing these fields:
- distMean  clipped mean of on-sky radial separation
- distStdDev  clipped standard deviation of on-sky radial separation
- maxMatchDist  distMean + self.config.matchDistanceSigma*distStdDev

Definition at line 761 of file anetBasicAstrometry.py.

762  def _computeMatchStatsOnSky(self, wcs, matchList):
763  """Compute on-sky radial distance statistics for a match list
764 
765  @param[in] wcs WCS for match list; an lsst.afw.image.Wcs
766  @param[in] matchList list of matches between reference object and sources;
767  a list of lsst.afw.table.ReferenceMatch;
768  the source centroid and reference object coord are read
769 
770  @return a pipe_base Struct containing these fields:
771  - distMean clipped mean of on-sky radial separation
772  - distStdDev clipped standard deviation of on-sky radial separation
773  - maxMatchDist distMean + self.config.matchDistanceSigma*distStdDev
774  """
775  distStatsInRadians = makeMatchStatisticsInRadians(wcs, matchList, afwMath.MEANCLIP | afwMath.STDEVCLIP)
776  distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*afwGeom.radians
777  distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*afwGeom.radians
778  return pipeBase.Struct(
779  distMean = distMean,
780  distStdDev = distStdDev,
781  maxMatchDist = distMean + self.config.matchDistanceSigma*distStdDev,
782  )
afw::math::Statistics makeMatchStatisticsInRadians(afw::image::Wcs const &wcs, std::vector< MatchT > const &matchList, int const flags, afw::math::StatisticsControl const &sctrl=afw::math::StatisticsControl())
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask._getImageParams (   self,
  exposure = None,
  bbox = None,
  wcs = None,
  filterName = None,
  wcsRequired = True 
)
private
Get image parameters

@param[in] exposure  exposure (an afwImage.Exposure) or None
@param[in] bbox  bounding box (an afwGeom.Box2I) or None; if None then bbox must be specified
@param[in] wcs  WCS (an afwImage.Wcs) or None; if None then exposure must be specified
@param[in] filterName  filter name, a string, or None; if None exposure must be specified
@param[in] wcsRequired  if True then either wcs must be specified or exposure must contain a wcs;
    if False then the returned wcs may be None
@return these items:
- bbox  bounding box; guaranteed to be set
- wcs  WCS if known, else None
- filterName filter name if known, else None
@throw RuntimeError if bbox cannot be determined, or wcs cannot be determined and wcsRequired True

Definition at line 262 of file anetBasicAstrometry.py.

263  def _getImageParams(self, exposure=None, bbox=None, wcs=None, filterName=None, wcsRequired=True):
264  """Get image parameters
265 
266  @param[in] exposure exposure (an afwImage.Exposure) or None
267  @param[in] bbox bounding box (an afwGeom.Box2I) or None; if None then bbox must be specified
268  @param[in] wcs WCS (an afwImage.Wcs) or None; if None then exposure must be specified
269  @param[in] filterName filter name, a string, or None; if None exposure must be specified
270  @param[in] wcsRequired if True then either wcs must be specified or exposure must contain a wcs;
271  if False then the returned wcs may be None
272  @return these items:
273  - bbox bounding box; guaranteed to be set
274  - wcs WCS if known, else None
275  - filterName filter name if known, else None
276  @throw RuntimeError if bbox cannot be determined, or wcs cannot be determined and wcsRequired True
277  """
278  if exposure is not None:
279  if bbox is None:
280  bbox = exposure.getBBox()
281  self.log.logdebug("Setting bbox = %s from exposure metadata" % (bbox,))
282  if wcs is None:
283  self.log.logdebug("Setting wcs from exposure metadata")
284  wcs = exposure.getWcs()
285  if filterName is None:
286  filterName = exposure.getFilter().getName()
287  self.log.logdebug("Setting filterName = %r from exposure metadata" % (filterName,))
288  if bbox is None:
289  raise RuntimeError("bbox or exposure must be specified")
290  if wcs is None and wcsRequired:
291  raise RuntimeError("wcs or exposure (with a WCS) must be specified")
292  return bbox, wcs, filterName
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask._getMatchList (   self,
  sourceCat,
  refCat,
  wcs 
)
private

Definition at line 783 of file anetBasicAstrometry.py.

784  def _getMatchList(self, sourceCat, refCat, wcs):
785  dist = self.config.catalogMatchDist * afwGeom.arcseconds
786  clean = self.config.cleaningParameter
787  matcher = astromSip.MatchSrcToCatalogue(refCat, sourceCat, wcs, dist)
788  matches = matcher.getMatches()
789  if matches is None:
790  # Produce debugging stats...
791  X = [src.getX() for src in sourceCat]
792  Y = [src.getY() for src in sourceCat]
793  R1 = [src.getRa().asDegrees() for src in sourceCat]
794  D1 = [src.getDec().asDegrees() for src in sourceCat]
795  R2 = [src.getRa().asDegrees() for src in refCat]
796  D2 = [src.getDec().asDegrees() for src in refCat]
797  # for src in sourceCat:
798  #self.log.logdebug("source: x,y (%.1f, %.1f), RA,Dec (%.3f, %.3f)" %
799  #(src.getX(), src.getY(), src.getRa().asDegrees(), src.getDec().asDegrees()))
800  #for src in refCat:
801  #self.log.logdebug("ref: RA,Dec (%.3f, %.3f)" %
802  #(src.getRa().asDegrees(), src.getDec().asDegrees()))
803  self.loginfo('_getMatchList: %i sources, %i reference sources' % (len(sourceCat), len(refCat)))
804  if len(sourceCat):
805  self.loginfo(
806  'Source range: x [%.1f, %.1f], y [%.1f, %.1f], RA [%.3f, %.3f], Dec [%.3f, %.3f]' %
807  (min(X), max(X), min(Y), max(Y), min(R1), max(R1), min(D1), max(D1)))
808  if len(refCat):
809  self.loginfo('Reference range: RA [%.3f, %.3f], Dec [%.3f, %.3f]' %
810  (min(R2), max(R2), min(D2), max(D2)))
811  raise RuntimeError('No matches found between image and catalogue')
812  matches = astromSip.cleanBadPoints.clean(matches, wcs, nsigma=clean)
813  return matches
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask._isGoodSource (   self,
  candsource,
  keys 
)
private

Definition at line 942 of file anetBasicAstrometry.py.

943  def _isGoodSource(self, candsource, keys):
944  for k in keys:
945  if candsource.get(k):
946  return False
947  return True
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask._solve (   self,
  sourceCat,
  wcs,
  bbox,
  pixelScale,
  radecCenter,
  searchRadius,
  parity,
  filterName = None 
)
private

Definition at line 832 of file anetBasicAstrometry.py.

833  searchRadius, parity, filterName=None):
834  solver = self.refObjLoader._getSolver()
835 
836  imageSize = bbox.getDimensions()
837  x0, y0 = bbox.getMin()
838 
839  # select sources with valid x,y, flux
840  xybb = afwGeom.Box2D()
841  goodsources = afwTable.SourceCatalog(sourceCat.table)
842  badkeys = [goodsources.getSchema().find(name).key for name in self.config.badFlags]
843 
844  for s in sourceCat:
845  if np.isfinite(s.getX()) and np.isfinite(s.getY()) and np.isfinite(s.getPsfFlux()) \
846  and self._isGoodSource(s, badkeys):
847  goodsources.append(s)
848  xybb.include(afwGeom.Point2D(s.getX() - x0, s.getY() - y0))
849  self.log.info("Number of selected sources for astrometry : %d" %(len(goodsources)))
850  if len(goodsources) < len(sourceCat):
851  self.log.logdebug('Keeping %i of %i sources with finite X,Y positions and PSF flux' %
852  (len(goodsources), len(sourceCat)))
853  self.log.logdebug(('Feeding sources in range x=[%.1f, %.1f], y=[%.1f, %.1f] ' +
854  '(after subtracting x0,y0 = %.1f,%.1f) to Astrometry.net') %
855  (xybb.getMinX(), xybb.getMaxX(), xybb.getMinY(), xybb.getMaxY(), x0, y0))
856  # setStars sorts them by PSF flux.
857  solver.setStars(goodsources, x0, y0)
858  solver.setMaxStars(self.config.maxStars)
859  solver.setImageSize(*imageSize)
860  solver.setMatchThreshold(self.config.matchThreshold)
861  raDecRadius = None
862  if radecCenter is not None:
863  raDecRadius = (radecCenter.getLongitude().asDegrees(),
864  radecCenter.getLatitude().asDegrees(),
865  searchRadius.asDegrees())
866  solver.setRaDecRadius(*raDecRadius)
867  self.log.logdebug('Searching for match around RA,Dec = (%g, %g) with radius %g deg' %
868  raDecRadius)
869 
870  if pixelScale is not None:
871  dscale = self.config.pixelScaleUncertainty
872  scale = pixelScale.asArcseconds()
873  lo = scale / dscale
874  hi = scale * dscale
875  solver.setPixelScaleRange(lo, hi)
876  self.log.logdebug(
877  'Searching for matches with pixel scale = %g +- %g %% -> range [%g, %g] arcsec/pix' %
878  (scale, 100.*(dscale-1.), lo, hi))
879 
880  if parity is not None:
881  solver.setParity(parity)
882  self.log.logdebug('Searching for match with parity = ' + str(parity))
883 
884  # Find and load index files within RA,Dec range and scale range.
885  if radecCenter is not None:
886  multiInds = self.refObjLoader._getMIndexesWithinRange(radecCenter, searchRadius)
887  else:
888  multiInds = self.refObjLoader.multiInds
889  qlo,qhi = solver.getQuadSizeLow(), solver.getQuadSizeHigh()
890 
891  toload_multiInds = set()
892  toload_inds = []
893  for mi in multiInds:
894  for i in range(len(mi)):
895  ind = mi[i]
896  if not ind.overlapsScaleRange(qlo, qhi):
897  continue
898  toload_multiInds.add(mi)
899  toload_inds.append(ind)
900 
901  import lsstDebug
902  if lsstDebug.Info(__name__).display:
903  # Use separate context for display, since astrometry.net can segfault if we don't...
904  with LoadMultiIndexes(toload_multiInds):
905  displayAstrometry(refCat=self.refObjLoader.loadPixelBox(bbox, wcs, filterName).refCat,
906  frame=lsstDebug.Info(__name__).frame, pause=lsstDebug.Info(__name__).pause)
907 
908  with LoadMultiIndexes(toload_multiInds):
909  solver.addIndices(toload_inds)
910  self.memusage('Index files loaded: ')
911 
912  cpulimit = self.config.maxCpuTime
913  solver.run(cpulimit)
914 
915  self.memusage('Solving finished: ')
916 
917  self.memusage('Index files unloaded: ')
918 
919  if solver.didSolve():
920  self.log.logdebug('Solved!')
921  wcs = solver.getWcs()
922  self.log.logdebug('WCS: %s' % wcs.getFitsMetadata().toString())
923 
924  if x0 != 0 or y0 != 0:
925  wcs.shiftReferencePixel(x0, y0)
926  self.log.logdebug('After shifting reference pixel by x0,y0 = (%i,%i), WCS is: %s' %
927  (x0, y0, wcs.getFitsMetadata().toString()))
928 
929  else:
930  self.log.warn('Did not get an astrometric solution from Astrometry.net')
931  wcs = None
932  # Gather debugging info...
933 
934  # -are there any reference stars in the proposed search area?
935  # log the number found and discard the results
936  if radecCenter is not None:
937  self.refObjLoader.loadSkyCircle(radecCenter, searchRadius, filterName)
938 
939  qa = solver.getSolveStats()
940  self.log.logdebug('qa: %s' % qa.toString())
941  return wcs, qa
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Definition: fwd.h:55
A floating-point coordinate rectangle geometry.
Definition: Box.h:271
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask._trimBadPoints (   sourceCat,
  bbox,
  wcs = None 
)
staticprivate
Remove elements from catalog whose xy positions are not within the given bbox.

sourceCat:  a Catalog of SimpleRecord or SourceRecord objects
bbox: an afwImage.Box2D
wcs:  if not None, will be used to compute the xy positions on-the-fly;
      this is required when sources actually contains SimpleRecords.

Returns:
a list of Source objects with xAstrom,yAstrom within the bbox.

Definition at line 949 of file anetBasicAstrometry.py.

950  def _trimBadPoints(sourceCat, bbox, wcs=None):
951  """Remove elements from catalog whose xy positions are not within the given bbox.
952 
953  sourceCat: a Catalog of SimpleRecord or SourceRecord objects
954  bbox: an afwImage.Box2D
955  wcs: if not None, will be used to compute the xy positions on-the-fly;
956  this is required when sources actually contains SimpleRecords.
957 
958  Returns:
959  a list of Source objects with xAstrom,yAstrom within the bbox.
960  """
961  keep = type(sourceCat)(sourceCat.table)
962  for s in sourceCat:
963  point = s.getCentroid() if wcs is None else wcs.skyToPixel(s.getCoord())
964  if bbox.contains(point):
965  keep.append(s)
966  return keep
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.determineWcs (   self,
  sourceCat,
  exposure,
  kwargs 
)
Find a WCS solution for the given 'sourceCat' in the given
'exposure', getting other parameters from config.

Valid kwargs include:

'radecCenter', an afw.coord.Coord giving the RA,Dec position
   of the center of the field.  This is used to limit the
   search done by Astrometry.net (to make it faster and load
   fewer index files, thereby using less memory).  Defaults to
   the RA,Dec center from the exposure's WCS; turn that off
   with the boolean kwarg 'useRaDecCenter' or config option
   'useWcsRaDecCenter'

'useRaDecCenter', a boolean.  Don't use the RA,Dec center from
   the exposure's initial WCS.

'searchRadius', in degrees, to search for a solution around
   the given 'radecCenter'; default from config option
   'raDecSearchRadius'.

'useParity': parity is the 'flip' of the image.  Knowing it
   reduces the search space (hence time) for Astrometry.net.
   The parity can be computed from the exposure's WCS (the
   sign of the determinant of the CD matrix); this option
   controls whether we do that or force Astrometry.net to
   search both parities.  Default from config.useWcsParity.

'pixelScale': afwGeom.Angle, estimate of the angle-per-pixel
   (ie, arcseconds per pixel).  Defaults to a value derived
   from the exposure's WCS.  If enabled, this value, plus or
   minus config.pixelScaleUncertainty, will be used to limit
   Astrometry.net's search.

'usePixelScale': boolean.  Use the pixel scale to limit
   Astrometry.net's search?  Defaults to config.useWcsPixelScale.

'filterName', a string, the filter name of this image.  Will
   be mapped through the 'filterMap' config dictionary to a
   column name in the astrometry_net_data index FITS files.
   Defaults to the exposure.getFilter() value.

'bbox', bounding box of exposure; defaults to exposure.getBBox()

Definition at line 376 of file anetBasicAstrometry.py.

377  **kwargs):
378  """Find a WCS solution for the given 'sourceCat' in the given
379  'exposure', getting other parameters from config.
380 
381  Valid kwargs include:
382 
383  'radecCenter', an afw.coord.Coord giving the RA,Dec position
384  of the center of the field. This is used to limit the
385  search done by Astrometry.net (to make it faster and load
386  fewer index files, thereby using less memory). Defaults to
387  the RA,Dec center from the exposure's WCS; turn that off
388  with the boolean kwarg 'useRaDecCenter' or config option
389  'useWcsRaDecCenter'
390 
391  'useRaDecCenter', a boolean. Don't use the RA,Dec center from
392  the exposure's initial WCS.
393 
394  'searchRadius', in degrees, to search for a solution around
395  the given 'radecCenter'; default from config option
396  'raDecSearchRadius'.
397 
398  'useParity': parity is the 'flip' of the image. Knowing it
399  reduces the search space (hence time) for Astrometry.net.
400  The parity can be computed from the exposure's WCS (the
401  sign of the determinant of the CD matrix); this option
402  controls whether we do that or force Astrometry.net to
403  search both parities. Default from config.useWcsParity.
404 
405  'pixelScale': afwGeom.Angle, estimate of the angle-per-pixel
406  (ie, arcseconds per pixel). Defaults to a value derived
407  from the exposure's WCS. If enabled, this value, plus or
408  minus config.pixelScaleUncertainty, will be used to limit
409  Astrometry.net's search.
410 
411  'usePixelScale': boolean. Use the pixel scale to limit
412  Astrometry.net's search? Defaults to config.useWcsPixelScale.
413 
414  'filterName', a string, the filter name of this image. Will
415  be mapped through the 'filterMap' config dictionary to a
416  column name in the astrometry_net_data index FITS files.
417  Defaults to the exposure.getFilter() value.
418 
419  'bbox', bounding box of exposure; defaults to exposure.getBBox()
420 
421  """
422  assert(exposure is not None)
423 
424  margs = kwargs.copy()
425  if not 'searchRadius' in margs:
426  margs.update(searchRadius = self.config.raDecSearchRadius * afwGeom.degrees)
427  if not 'usePixelScale' in margs:
428  margs.update(usePixelScale = self.config.useWcsPixelScale)
429  if not 'useRaDecCenter' in margs:
430  margs.update(useRaDecCenter = self.config.useWcsRaDecCenter)
431  if not 'useParity' in margs:
432  margs.update(useParity = self.config.useWcsParity)
433  margs.update(exposure=exposure)
434  return self.determineWcs2(sourceCat=sourceCat, **margs)
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.determineWcs2 (   self,
  sourceCat,
  kwargs 
)
Get a blind astrometric solution for the given catalog of sources.

We need:
  -the image size;
  -the filter

And if available, we can use:
  -an initial Wcs estimate;
     --> RA,Dec center
     --> pixel scale
     --> "parity"
     
(all of which are metadata of Exposure).

filterName: string
imageSize: (W,H) integer tuple/iterable
pixelScale: afwGeom::Angle per pixel.
radecCenter: afwCoord::Coord

Definition at line 435 of file anetBasicAstrometry.py.

436  def determineWcs2(self, sourceCat, **kwargs):
437  """Get a blind astrometric solution for the given catalog of sources.
438 
439  We need:
440  -the image size;
441  -the filter
442 
443  And if available, we can use:
444  -an initial Wcs estimate;
445  --> RA,Dec center
446  --> pixel scale
447  --> "parity"
448 
449  (all of which are metadata of Exposure).
450 
451  filterName: string
452  imageSize: (W,H) integer tuple/iterable
453  pixelScale: afwGeom::Angle per pixel.
454  radecCenter: afwCoord::Coord
455  """
456  wcs,qa = self.getBlindWcsSolution(sourceCat, **kwargs)
457  kw = {}
458  # Keys passed to useKnownWcs
459  for key in ['exposure', 'bbox', 'filterName']:
460  if key in kwargs:
461  kw[key] = kwargs[key]
462  astrom = self.useKnownWcs(sourceCat, wcs=wcs, **kw)
463  astrom.solveQa = qa
464  astrom.tanWcs = wcs
465  return astrom
def useKnownWcs
Return an InitialAstrometry object, just like determineWcs, but assuming the given input WCS is corre...
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.getBlindWcsSolution (   self,
  sourceCat,
  exposure = None,
  wcs = None,
  bbox = None,
  radecCenter = None,
  searchRadius = None,
  pixelScale = None,
  filterName = None,
  doTrim = False,
  usePixelScale = True,
  useRaDecCenter = True,
  useParity = True,
  searchRadiusScale = 2. 
)

Definition at line 478 of file anetBasicAstrometry.py.

479  searchRadiusScale=2.):
480  if not useRaDecCenter and radecCenter is not None:
481  raise RuntimeError('radecCenter is set, but useRaDecCenter is False. Make up your mind!')
482  if not usePixelScale and pixelScale is not None:
483  raise RuntimeError('pixelScale is set, but usePixelScale is False. Make up your mind!')
484 
485  bbox, wcs, filterName = self._getImageParams(
486  exposure = exposure,
487  bbox = bbox,
488  wcs = wcs,
489  filterName = filterName,
490  wcsRequired = False,
491  )
492 
493  bboxD = afwGeom.Box2D(bbox)
494  xc, yc = bboxD.getCenter()
495  parity = None
496 
497  if wcs is not None:
498  if pixelScale is None:
499  if usePixelScale:
500  pixelScale = wcs.pixelScale()
501  self.log.logdebug('Setting pixel scale estimate = %.3f from given WCS estimate' %
502  (pixelScale.asArcseconds()))
503 
504  if radecCenter is None:
505  if useRaDecCenter:
506  radecCenter = wcs.pixelToSky(xc, yc)
507  self.log.logdebug(('Setting RA,Dec center estimate = (%.3f, %.3f) from given WCS '
508  + 'estimate, using pixel center = (%.1f, %.1f)') %
509  (radecCenter.getLongitude().asDegrees(),
510  radecCenter.getLatitude().asDegrees(), xc, yc))
511 
512  if searchRadius is None:
513  if useRaDecCenter:
514  assert(pixelScale is not None)
515  pixRadius = math.hypot(*bboxD.getDimensions()) / 2
516  searchRadius = (pixelScale * pixRadius * searchRadiusScale)
517  self.log.logdebug(('Using RA,Dec search radius = %.3f deg, from pixel scale, '
518  + 'image size, and searchRadiusScale = %g') %
519  (searchRadius, searchRadiusScale))
520  if useParity:
521  parity = wcs.isFlipped()
522  self.log.logdebug('Using parity = %s' % (parity and 'True' or 'False'))
523 
524  if doTrim:
525  n = len(sourceCat)
526  if exposure is not None:
527  exposureBBoxD = afwGeom.Box2D(exposure.getMaskedImage().getBBox())
528  else:
529  exposureBBoxD = bboxD
530  sourceCat = self._trimBadPoints(sourceCat, exposureBBoxD)
531  self.log.logdebug("Trimming: kept %i of %i sources" % (n, len(sourceCat)))
532 
533  wcs,qa = self._solve(
534  sourceCat = sourceCat,
535  wcs = wcs,
536  bbox = bbox,
537  pixelScale = pixelScale,
538  radecCenter = radecCenter,
539  searchRadius = searchRadius,
540  parity = parity,
541  filterName = filterName,
542  )
543  if wcs is None:
544  raise RuntimeError("Unable to match sources with catalog.")
545  self.log.info('Got astrometric solution from Astrometry.net')
546 
547  rdc = wcs.pixelToSky(xc, yc)
548  self.log.logdebug('New WCS says image center pixel (%.1f, %.1f) -> RA,Dec (%.3f, %.3f)' %
549  (xc, yc, rdc.getLongitude().asDegrees(), rdc.getLatitude().asDegrees()))
550  return wcs,qa
A floating-point coordinate rectangle geometry.
Definition: Box.h:271
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.getColumnName (   self,
  filterName,
  columnMap,
  default = None 
)
Returns the column name in the astrometry_net_data index file that will be used
for the given filter name.

@param filterName   Name of filter used in exposure
@param columnMap    Dict that maps filter names to column names
@param default      Default column name

Definition at line 814 of file anetBasicAstrometry.py.

815  def getColumnName(self, filterName, columnMap, default=None):
816  """
817  Returns the column name in the astrometry_net_data index file that will be used
818  for the given filter name.
819 
820  @param filterName Name of filter used in exposure
821  @param columnMap Dict that maps filter names to column names
822  @param default Default column name
823  """
824  filterName = self.config.filterMap.get(filterName, filterName) # Exposure filter --> desired filter
825  try:
826  return columnMap[filterName] # Desired filter --> a_n_d column name
827  except KeyError:
828  self.log.warn("No column in configuration for filter '%s'; using default '%s'" %
829  (filterName, default))
830  return default
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.getSipWcsFromCorrespondences (   self,
  origWcs,
  refCat,
  sourceCat,
  bbox 
)
Produce a SIP solution given a list of known correspondences.

Unlike _calculateSipTerms, this does not iterate the solution;
it assumes you have given it a good sets of corresponding stars.

NOTE that "refCat" and "sourceCat" are assumed to be the same length;
entries "refCat[i]" and "sourceCat[i]" are assumed to be correspondences.

@param[in] origWcs  the WCS to linearize in order to get the TAN part of the TAN-SIP WCS.
@param[in] refCat  reference source catalog
@param[in] sourceCat  source catalog
@param[in] bbox  bounding box of image

Definition at line 604 of file anetBasicAstrometry.py.

605  def getSipWcsFromCorrespondences(self, origWcs, refCat, sourceCat, bbox):
606  """Produce a SIP solution given a list of known correspondences.
607 
608  Unlike _calculateSipTerms, this does not iterate the solution;
609  it assumes you have given it a good sets of corresponding stars.
610 
611  NOTE that "refCat" and "sourceCat" are assumed to be the same length;
612  entries "refCat[i]" and "sourceCat[i]" are assumed to be correspondences.
613 
614  @param[in] origWcs the WCS to linearize in order to get the TAN part of the TAN-SIP WCS.
615  @param[in] refCat reference source catalog
616  @param[in] sourceCat source catalog
617  @param[in] bbox bounding box of image
618  """
619  sipOrder = self.config.sipOrder
620  matches = []
621  for ci,si in zip(refCat, sourceCat):
622  matches.append(afwTable.ReferenceMatch(ci, si, 0.))
623 
624  sipObject = astromSip.makeCreateWcsWithSip(matches, origWcs, sipOrder, bbox)
625  return sipObject.getNewWcs()
Lightweight representation of a geometric match between two records.
Definition: fwd.h:90
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.getSipWcsFromWcs (   self,
  wcs,
  bbox,
  ngrid = 20,
  linearizeAtCenter = True 
)

Get a TAN-SIP WCS, starting from an existing WCS.

It uses your WCS to compute a fake grid of corresponding "stars" in pixel and sky coords, and feeds that to the regular SIP code.

Parameters
[in]wcsinitial WCS
[in]bboxbounding box of image
[in]ngridnumber of grid points along x and y for fitting (fit at ngrid^2 points)
[in]linearizeAtCenterif True, get a linear approximation of the input WCS at the image center and use that as the TAN initialization for the TAN-SIP solution. You probably want this if your WCS has its CRPIX outside the image bounding box.

Definition at line 551 of file anetBasicAstrometry.py.

552  def getSipWcsFromWcs(self, wcs, bbox, ngrid=20, linearizeAtCenter=True):
553  """!Get a TAN-SIP WCS, starting from an existing WCS.
554 
555  It uses your WCS to compute a fake grid of corresponding "stars" in pixel and sky coords,
556  and feeds that to the regular SIP code.
557 
558  @param[in] wcs initial WCS
559  @param[in] bbox bounding box of image
560  @param[in] ngrid number of grid points along x and y for fitting (fit at ngrid^2 points)
561  @param[in] linearizeAtCenter if True, get a linear approximation of the input
562  WCS at the image center and use that as the TAN initialization for
563  the TAN-SIP solution. You probably want this if your WCS has its
564  CRPIX outside the image bounding box.
565  """
566  # Ugh, build src and ref tables
567  srcSchema = afwTable.SourceTable.makeMinimalSchema()
568  key = srcSchema.addField("centroid", type="PointD")
569  srcTable = afwTable.SourceTable.make(srcSchema)
570  srcTable.defineCentroid("centroid")
571  srcs = srcTable
572  refs = afwTable.SimpleTable.make(afwTable.SimpleTable.makeMinimalSchema())
573  cref = []
574  csrc = []
575  (W,H) = bbox.getDimensions()
576  x0, y0 = bbox.getMin()
577  for xx in np.linspace(0., W, ngrid):
578  for yy in np.linspace(0, H, ngrid):
579  src = srcs.makeRecord()
580  src.set(key.getX(), x0 + xx)
581  src.set(key.getY(), y0 + yy)
582  csrc.append(src)
583  rd = wcs.pixelToSky(afwGeom.Point2D(xx + x0, yy + y0))
584  ref = refs.makeRecord()
585  ref.setCoord(rd)
586  cref.append(ref)
587 
588  if linearizeAtCenter:
589  # Linearize the original WCS around the image center to create a
590  # TAN WCS.
591  # Reference pixel in LSST coords
592  crpix = afwGeom.Box2D(bbox).getCenter()
593  crval = wcs.pixelToSky(crpix)
594  crval = crval.getPosition(afwGeom.degrees)
595  # Linearize *AT* crval to get effective CD at crval.
596  # (we use the default skyUnit of degrees as per WCS standard)
597  aff = wcs.linearizePixelToSky(crval)
598  cd = aff.getLinear().getMatrix()
599  wcs = afwImage.Wcs(crval, crpix, cd)
600 
601  return self.getSipWcsFromCorrespondences(wcs, cref, csrc, (W,H),
602  x0=x0, y0=y0)
603 
Implementation of the WCS standard for a any projection.
Definition: Wcs.h:107
def getSipWcsFromWcs
Get a TAN-SIP WCS, starting from an existing WCS.
A floating-point coordinate rectangle geometry.
Definition: Box.h:271
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.joinMatchListWithCatalog (   self,
  packedMatches,
  sourceCat 
)
This function is required to reconstitute a ReferenceMatchVector after being
unpersisted.  The persisted form of a ReferenceMatchVector is the 
normalized Catalog of IDs produced by afw.table.packMatches(), with the result of 
InitialAstrometry.getMatchMetadata() in the associated tables\' metadata.

The "live" form of a matchlist has links to
the real record objects that are matched; it is "denormalized".
This function takes a normalized match catalog, along with the catalog of
sources to which the match catalog refers.  It fetches the reference
sources that are within range, and then denormalizes the matches
-- sets the "matches[*].first" and "matches[*].second" entries
to point to the sources in the "sourceCat" argument, and to the
reference sources fetched from the astrometry_net_data files.
    
@param[in] packedMatches  Unpersisted match list (an lsst.afw.table.BaseCatalog).
                  packedMatches.table.getMetadata() must contain the
                  values from InitialAstrometry.getMatchMetadata()
@param[in,out] sourceCat  Source catalog used for the 'second' side of the matches
                  (an lsst.afw.table.SourceCatalog).  As a side effect,
                  the catalog will be sorted by ID.

@return An lsst.afw.table.ReferenceMatchVector of denormalized matches.

Definition at line 967 of file anetBasicAstrometry.py.

968  def joinMatchListWithCatalog(self, packedMatches, sourceCat):
969  """
970  This function is required to reconstitute a ReferenceMatchVector after being
971  unpersisted. The persisted form of a ReferenceMatchVector is the
972  normalized Catalog of IDs produced by afw.table.packMatches(), with the result of
973  InitialAstrometry.getMatchMetadata() in the associated tables\' metadata.
974 
975  The "live" form of a matchlist has links to
976  the real record objects that are matched; it is "denormalized".
977  This function takes a normalized match catalog, along with the catalog of
978  sources to which the match catalog refers. It fetches the reference
979  sources that are within range, and then denormalizes the matches
980  -- sets the "matches[*].first" and "matches[*].second" entries
981  to point to the sources in the "sourceCat" argument, and to the
982  reference sources fetched from the astrometry_net_data files.
983 
984  @param[in] packedMatches Unpersisted match list (an lsst.afw.table.BaseCatalog).
985  packedMatches.table.getMetadata() must contain the
986  values from InitialAstrometry.getMatchMetadata()
987  @param[in,out] sourceCat Source catalog used for the 'second' side of the matches
988  (an lsst.afw.table.SourceCatalog). As a side effect,
989  the catalog will be sorted by ID.
990 
991  @return An lsst.afw.table.ReferenceMatchVector of denormalized matches.
992  """
993  matchmeta = packedMatches.table.getMetadata()
994  version = matchmeta.getInt('SMATCHV')
995  if version != 1:
996  raise ValueError('SourceMatchVector version number is %i, not 1.' % version)
997  filterName = matchmeta.getString('FILTER').strip()
998  ctrCoord = afwCoord.IcrsCoord(
999  matchmeta.getDouble('RA') * afwGeom.degrees,
1000  matchmeta.getDouble('DEC') * afwGeom.degrees,
1001  )
1002  rad = matchmeta.getDouble('RADIUS') * afwGeom.degrees
1003  refCat = self.refObjLoader.loadSkyCircle(ctrCoord, rad, filterName).refCat
1004  refCat.sort()
1005  sourceCat.sort()
1006  return afwTable.unpackMatches(packedMatches, refCat, sourceCat)
1007 
A class to handle Icrs coordinates (inherits from Coord)
Definition: Coord.h:157
std::vector< Match< typename Cat1::Record, typename Cat2::Record > > unpackMatches(BaseCatalog const &matches, Cat1 const &cat1, Cat2 const &cat2)
Reconstruct a MatchVector from a BaseCatalog representation of the matches and a pair of catalogs...
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.memusage (   self,
  prefix = '' 
)

Definition at line 246 of file anetBasicAstrometry.py.

247  def memusage(self, prefix=''):
248  # Not logging at DEBUG: do nothing
249  if self.log.getThreshold() > pexLog.Log.DEBUG:
250  return
251  from astrometry.util.ttime import get_memusage
252  mu = get_memusage()
253  ss = []
254  for key in ['VmPeak', 'VmSize', 'VmRSS', 'VmData']:
255  if key in mu:
256  ss.append(key + ': ' + ' '.join(mu[key]))
257  if 'mmaps' in mu:
258  ss.append('Mmaps: %i' % len(mu['mmaps']))
259  if 'mmaps_total' in mu:
260  ss.append('Mmaps: %i kB' % (mu['mmaps_total'] / 1024))
261  self.log.logdebug(prefix + 'Memory: ' + ', '.join(ss))
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.plotSolution (   self,
  matches,
  wcs,
  imageSize 
)
Plot the solution, when debugging is turned on.

@param matches   The list of matches
@param wcs         The Wcs
@param imageSize   2-tuple with the image size (W,H)

Definition at line 683 of file anetBasicAstrometry.py.

684  def plotSolution(self, matches, wcs, imageSize):
685  """Plot the solution, when debugging is turned on.
686 
687  @param matches The list of matches
688  @param wcs The Wcs
689  @param imageSize 2-tuple with the image size (W,H)
690  """
691  import lsstDebug
692  display = lsstDebug.Info(__name__).display
693  if not display:
694  return
695 
696  try:
697  import matplotlib.pyplot as plt
698  import numpy
699  except ImportError:
700  print >> sys.stderr, "Unable to import matplotlib"
701  return
702 
703  fig = plt.figure(1)
704  fig.clf()
705  try:
706  fig.canvas._tkcanvas._root().lift() # == Tk's raise, but raise is a python reserved word
707  except: # protect against API changes
708  pass
709 
710  num = len(matches)
711  x = numpy.zeros(num)
712  y = numpy.zeros(num)
713  dx = numpy.zeros(num)
714  dy = numpy.zeros(num)
715  for i, m in enumerate(matches):
716  x[i] = m.second.getX()
717  y[i] = m.second.getY()
718  pixel = wcs.skyToPixel(m.first.getCoord())
719  dx[i] = x[i] - pixel.getX()
720  dy[i] = y[i] - pixel.getY()
721 
722  subplots = maUtils.makeSubplots(fig, 2, 2, xgutter=0.1, ygutter=0.1, pygutter=0.04)
723 
724  def plotNext(x, y, xLabel, yLabel, xMax):
725  ax = subplots.next()
726  ax.set_autoscalex_on(False)
727  ax.set_xbound(lower=0, upper=xMax)
728  ax.scatter(x, y)
729  ax.set_xlabel(xLabel)
730  ax.set_ylabel(yLabel)
731  ax.axhline(0.0)
732 
733  plotNext(x, dx, "x", "dx", imageSize[0])
734  plotNext(x, dy, "x", "dy", imageSize[0])
735  plotNext(y, dx, "y", "dx", imageSize[1])
736  plotNext(y, dy, "y", "dy", imageSize[1])
737 
738  fig.show()
739 
740  while True:
741  try:
742  reply = raw_input("Pausing for inspection, enter to continue... [hpQ] ").strip()
743  except EOFError:
744  reply = "n"
745 
746  reply = reply.split()
747  if reply:
748  reply = reply[0]
749  else:
750  reply = ""
751 
752  if reply in ("", "h", "p", "Q"):
753  if reply == "h":
754  print "h[elp] p[db] Q[uit]"
755  continue
756  elif reply == "p":
757  import pdb; pdb.set_trace()
758  elif reply == "Q":
759  sys.exit(1)
760  break
def lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.useKnownWcs (   self,
  sourceCat,
  wcs = None,
  exposure = None,
  filterName = None,
  bbox = None,
  calculateSip = None 
)

Return an InitialAstrometry object, just like determineWcs, but assuming the given input WCS is correct.

This involves searching for reference sources within the WCS area, and matching them to the given 'sourceCat'. If 'calculateSip' is set, we will try to compute a TAN-SIP distortion correction.

Parameters
[in]sourceCatlist of detected sources in this image.
[in]wcsyour known WCS, or None to get from exposure
[in]exposurethe exposure holding metadata for this image; if None then you must specify wcs, filterName and bbox
[in]filterNamestring, filter name, eg "i", or None to get from exposure`
[in]bboxbounding box of image, or None to get from exposure
[in]calculateSipcalculate SIP distortion terms for the WCS? If None then use self.config.calculateSip. To disable WCS fitting set calculateSip=False
Note
this function is also called by 'determineWcs' (via 'determineWcs2'), since the steps are all the same.

Definition at line 293 of file anetBasicAstrometry.py.

294  def useKnownWcs(self, sourceCat, wcs=None, exposure=None, filterName=None, bbox=None, calculateSip=None):
295  """!Return an InitialAstrometry object, just like determineWcs,
296  but assuming the given input WCS is correct.
297 
298  This involves searching for reference sources within the WCS
299  area, and matching them to the given 'sourceCat'. If
300  'calculateSip' is set, we will try to compute a TAN-SIP
301  distortion correction.
302 
303  @param[in] sourceCat list of detected sources in this image.
304  @param[in] wcs your known WCS, or None to get from exposure
305  @param[in] exposure the exposure holding metadata for this image;
306  if None then you must specify wcs, filterName and bbox
307  @param[in] filterName string, filter name, eg "i", or None to get from exposure`
308  @param[in] bbox bounding box of image, or None to get from exposure
309  @param[in] calculateSip calculate SIP distortion terms for the WCS? If None
310  then use self.config.calculateSip. To disable WCS fitting set calculateSip=False
311 
312  @note this function is also called by 'determineWcs' (via 'determineWcs2'), since the steps are all
313  the same.
314  """
315  # return value:
316  astrom = InitialAstrometry()
317 
318  if calculateSip is None:
319  calculateSip = self.config.calculateSip
320 
321  bbox, wcs, filterName = self._getImageParams(
322  exposure = exposure,
323  bbox = bbox,
324  wcs = wcs,
325  filterName = filterName,
326  wcsRequired = True,
327  )
328  refCat = self.refObjLoader.loadPixelBox(
329  bbox = bbox,
330  wcs = wcs,
331  filterName = filterName,
332  calib = None,
333  ).refCat
334  astrom.refCat = refCat
335  catids = [src.getId() for src in refCat]
336  uids = set(catids)
337  self.log.logdebug('%i reference sources; %i unique IDs' % (len(catids), len(uids)))
338  matches = self._getMatchList(sourceCat, refCat, wcs)
339  uniq = set([sm.second.getId() for sm in matches])
340  if len(matches) != len(uniq):
341  self.log.warn(('The list of matched stars contains duplicate reference source IDs ' +
342  '(%i sources, %i unique ids)') % (len(matches), len(uniq)))
343  if len(matches) == 0:
344  self.log.warn('No matches found between input sources and reference catalogue.')
345  return astrom
346 
347  self.log.logdebug('%i reference objects match input sources using input WCS' % (len(matches)))
348  astrom.tanMatches = matches
349  astrom.tanWcs = wcs
350 
351  srcids = [s.getId() for s in sourceCat]
352  for m in matches:
353  assert(m.second.getId() in srcids)
354  assert(m.second in sourceCat)
355 
356  if calculateSip:
357  sipwcs,matches = self._calculateSipTerms(wcs, refCat, sourceCat, matches, bbox=bbox)
358  if sipwcs == wcs:
359  self.log.logdebug('Failed to find a SIP WCS better than the initial one.')
360  else:
361  self.log.logdebug('%i reference objects match input sources using SIP WCS' %
362  (len(matches),))
363  astrom.sipWcs = sipwcs
364  astrom.sipMatches = matches
365 
366  wcs = astrom.getWcs()
367  # _getMatchList() modifies the source list RA,Dec coordinates.
368  # Here, we make them consistent with the WCS we are returning.
369  for src in sourceCat:
370  src.updateCoord(wcs)
371  astrom.matchMeta = _createMetadata(bbox, wcs, filterName)
372  return astrom
def useKnownWcs
Return an InitialAstrometry object, just like determineWcs, but assuming the given input WCS is corre...
def _calculateSipTerms
Iteratively calculate SIP distortions and regenerate matches based on improved WCS.

Member Data Documentation

string lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask._DefaultName = "aNetBasicAstrometry"
staticprivate

Definition at line 223 of file anetBasicAstrometry.py.

lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.config

Definition at line 240 of file anetBasicAstrometry.py.

lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.ConfigClass = ANetBasicAstrometryConfig
static

Definition at line 222 of file anetBasicAstrometry.py.

lsst.meas.astrom.anetBasicAstrometry.ANetBasicAstrometryTask.refObjLoader

Definition at line 242 of file anetBasicAstrometry.py.


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