2 __all__ = [
"MatchOptimisticBTask", 
"MatchOptimisticBConfig",
 
    7 import lsst.pex.config 
as pexConfig
 
   10 from .setMatchDistance 
import setMatchDistance
 
   11 from .matchOptimisticB 
import matchOptimisticB, MatchOptimisticBControl
 
   15     """Stores match tolerances for use in `lsst.meas.astrom.AstrometryTask` and 
   16     later iterations of the matcher. 
   18     MatchOptimsiticBTask relies on a maximum distance for matching 
   19     set by either the default in MatchOptimisticBConfig or the 2 sigma 
   20     scatter found after AstrometryTask has fit for a wcs. 
   24     maxMatchDist : `lsst.geom.Angle` 
   25         Current maximum distance to consider a match. 
   33     """Configuration for MatchOptimisticBTask 
   35     maxMatchDistArcSec = pexConfig.RangeField(
 
   36         doc=
"Maximum separation between reference objects and sources " 
   37         "beyond which they will not be considered a match (arcsec)",
 
   42     numBrightStars = pexConfig.RangeField(
 
   43         doc=
"Number of bright stars to use",
 
   48     minMatchedPairs = pexConfig.RangeField(
 
   49         doc=
"Minimum number of matched pairs; see also minFracMatchedPairs",
 
   54     minFracMatchedPairs = pexConfig.RangeField(
 
   55         doc=
"Minimum number of matched pairs as a fraction of the smaller of " 
   56         "the number of reference stars or the number of good sources; " 
   57         "the actual minimum is the smaller of this value or minMatchedPairs",
 
   63     maxOffsetPix = pexConfig.RangeField(
 
   64         doc=
"Maximum allowed shift of WCS, due to matching (pixel). " 
   65             "When changing this value, the LoadReferenceObjectsConfig.pixelMargin should also be updated.",
 
   70     maxRotationDeg = pexConfig.RangeField(
 
   71         doc=
"Rotation angle allowed between sources and position reference objects (degrees)",
 
   76     allowedNonperpDeg = pexConfig.RangeField(
 
   77         doc=
"Allowed non-perpendicularity of x and y (degree)",
 
   82     numPointsForShape = pexConfig.Field(
 
   83         doc=
"number of points to define a shape for matching",
 
   87     maxDeterminant = pexConfig.Field(
 
   88         doc=
"maximum determinant of linear transformation matrix for a usable solution",
 
  104     """Match sources to reference objects using the Optimistic Pattern Matcher 
  105     B algorithm of Tabur 2007. 
  107     ConfigClass = MatchOptimisticBConfig
 
  108     _DefaultName = 
"matchObjectsToSources" 
  111         pipeBase.Task.__init__(self, **kwargs)
 
  114         """Extra filtering pass; subclass if desired. 
  118         refCat : `lsst.afw.table.SimpleCatalog` 
  119             Catalog of reference objects. 
  123         trimmedRefCat : `lsst.afw.table.SimpleCatalog` 
  124             Reference catalog with some filtering applied. Currently no 
  125             filtering is applied. 
  131                               match_tolerance=None):
 
  132         """Match sources to position reference stars. 
  136         refCat : `lsst.afw.table.SimpleCatalog` 
  137             Reference catalog to match. 
  138         sourceCat : `lsst.afw.table.SourceCatalog` 
  139             Catalog of sources found on an exposure.  This should already be 
  140             down-selected to "good"/"usable" sources in the calling Task. 
  141         wcs : `lsst.afw.geom.SkyWcs` 
  142             Current WCS of the  exposure containing the sources. 
  143         sourceFluxField : `str` 
  144             Field of the sourceCat to use for flux 
  146             Field of the refCat to use for flux 
  147         match_tolerance : `lsst.meas.astrom.MatchTolerance` 
  148             Object containing information from previous 
  149             `lsst.meas.astrom.AstrometryTask` match/fit cycles for use in 
  150             matching. If `None` is config defaults. 
  154         matchResult : `lsst.pipe.base.Struct` 
  155             Result struct with components 
  157             - ``matches`` : List of matches with distance below the maximum match 
  158               distance (`list` of `lsst.afw.table.ReferenceMatch`). 
  159             - ``useableSourceCat`` : Catalog of sources matched and suited for 
  160               WCS fitting (`lsst.afw.table.SourceCatalog`). 
  161             - ``match_tolerance`` : MatchTolerance object updated from this 
  162               match iteration (`lsst.meas.astrom.MatchTolerance`). 
  167         preNumObj = len(refCat)
 
  169         numRefObj = len(refCat)
 
  172             self.log.
info(
"filterStars purged %d reference stars, leaving %d stars" %
 
  173                           (preNumObj - numRefObj, numRefObj))
 
  175         if match_tolerance 
is None:
 
  180         usableSourceCat = sourceCat
 
  182         numUsableSources = len(usableSourceCat)
 
  184         if len(usableSourceCat) == 0:
 
  185             raise pipeBase.TaskError(
"No sources are usable")
 
  187         minMatchedPairs = 
min(self.config.minMatchedPairs,
 
  188                               int(self.config.minFracMatchedPairs * 
min([len(refCat), len(usableSourceCat)])))
 
  193             sourceCat=usableSourceCat,
 
  195             refFluxField=refFluxField,
 
  196             numUsableSources=numUsableSources,
 
  197             minMatchedPairs=minMatchedPairs,
 
  198             maxMatchDist=match_tolerance.maxMatchDist,
 
  199             sourceFluxField=sourceFluxField,
 
  200             verbose=debug.verbose,
 
  206         for match 
in usableMatches:
 
  209                 matches.append(match)
 
  211         self.log.
debug(
"Found %d usable matches, of which %d had good sources",
 
  212                        len(usableMatches), len(matches))
 
  214         if len(matches) == 0:
 
  215             raise RuntimeError(
"Unable to match sources")
 
  217         self.log.
info(
"Matched %d sources" % len(matches))
 
  218         if len(matches) < minMatchedPairs:
 
  219             self.log.
warn(
"Number of matches is smaller than request")
 
  221         return pipeBase.Struct(
 
  223             usableSourceCat=usableSourceCat,
 
  224             match_tolerance=match_tolerance,
 
  227     def _getIsGoodKeys(self, schema):
 
  228         """Retrieve the keys needed for the isGoodTest from the source catalog 
  233         schema : `lsst.afw.table.Schema` 
  234             Source schema to retrieve `lsst.afw.table.Key` s from. 
  236         self.
edgeKey = schema[
"base_PixelFlags_flag_edge"].asKey()
 
  240     def _isGoodTest(self, source):
 
  241         """Test that an object is good for use in the WCS fitter. 
  243         This is a hard coded version of the isGood flag from the old SourceInfo 
  244         class that used to be part of this class. 
  248         source : `lsst.afw.table.SourceRecord` 
  254             Source passes CCD edge and saturated tests. 
  256         return (
not source.get(self.
edgeKey)
 
  261     def _doMatch(self, refCat, sourceCat, wcs, refFluxField, numUsableSources, minMatchedPairs,
 
  262                  maxMatchDist, sourceFluxField, verbose):
 
  263         """Implementation of matching sources to position reference stars. 
  265         Unlike matchObjectsToSources, this method does not check if the sources 
  270         refCat : `lsst.afw.table.SimpleCatalog` 
  271             Catalog of reference objects. 
  272         sourceCat : `lsst.afw.table.SourceCatalog` 
  273             Catalog of detected sources. 
  274         wcs : `lsst.afw.geom.SkyWcs` 
  275             Current best WCS of the image. 
  276         refFluxFioeld : `str` 
  277             Name of flux field in refCat to use. 
  278         numUsableSources : `int` 
  279             Total number of source usable for matching. 
  280         mintMatchPairs : `int` 
  281             Minimum number of objects to match between the refCat and sourceCat 
  282             to consider a valid match. 
  283         maxMatchDist : `lsst.geom.Angle` 
  284             Maximum separation to considering a reference and a source a match. 
  285         sourceFluxField : `str` 
  286             Name of source catalog flux field. 
  288             Print diagnostic information std::cout 
  292         matches : `list` of `lsst.afw.table.ReferenceMatch` 
  294         numSources = len(sourceCat)
 
  295         posRefBegInd = numUsableSources - numSources
 
  296         if maxMatchDist 
is None:
 
  297             maxMatchDistArcSec = self.config.maxMatchDistArcSec
 
  299             maxMatchDistArcSec = 
min(maxMatchDist.asArcseconds(), self.config.maxMatchDistArcSec)
 
  300         configMatchDistPix = maxMatchDistArcSec/wcs.getPixelScale().asArcseconds()
 
  302         matchControl = MatchOptimisticBControl()
 
  303         matchControl.refFluxField = refFluxField
 
  304         matchControl.sourceFluxField = sourceFluxField
 
  305         matchControl.numBrightStars = self.config.numBrightStars
 
  306         matchControl.minMatchedPairs = self.config.minMatchedPairs
 
  307         matchControl.maxOffsetPix = self.config.maxOffsetPix
 
  308         matchControl.numPointsForShape = self.config.numPointsForShape
 
  309         matchControl.maxDeterminant = self.config.maxDeterminant
 
  311         for maxRotInd 
in range(4):
 
  312             matchControl.maxRotationDeg = self.config.maxRotationDeg * math.pow(2.0, 0.5*maxRotInd)
 
  313             for matchRadInd 
in range(3):
 
  314                 matchControl.matchingAllowancePix = configMatchDistPix * math.pow(1.25, matchRadInd)
 
  316                 for angleDiffInd 
in range(3):
 
  317                     matchControl.allowedNonperpDeg = self.config.allowedNonperpDeg*(angleDiffInd+1)
 
  326                     if matches 
is not None and len(matches) > 0: