lsst.meas.astrom  16.0-18-g51a54b3+2
directMatch.py
Go to the documentation of this file.
1 
2 __all__ = ["DirectMatchConfig", "DirectMatchTask", "DirectMatchConfigWithoutLoader"]
3 
4 from lsst.pex.config import Config, Field, ConfigurableField
5 from lsst.pipe.base import Task, Struct
6 from lsst.meas.algorithms import (LoadIndexedReferenceObjectsTask, ScienceSourceSelectorTask,
7  ReferenceSourceSelectorTask)
8 import lsst.afw.table as afwTable
9 from lsst.afw.geom import arcseconds, averageSpherePoint
10 
11 
13  """Configuration for DirectMatchTask when an already-initialized
14  refObjLoader will be passed to this task."""
15  matchRadius = Field(dtype=float, default=0.25, doc="Matching radius, arcsec")
16  sourceSelection = ConfigurableField(target=ScienceSourceSelectorTask,
17  doc="Selection of science sources")
18  referenceSelection = ConfigurableField(target=ReferenceSourceSelectorTask,
19  doc="Selection of reference sources")
20 
21 
23  """Configuration for DirectMatchTask"""
24  refObjLoader = ConfigurableField(target=LoadIndexedReferenceObjectsTask, doc="Load reference objects")
25 
26 
27 class DirectMatchTask(Task):
28  """Simple, brute force matching of a source catalog to a reference catalog.
29 
30  Parameters
31  ----------
32  butler : `lsst.daf.persistence.Butler`
33  Data butler containing the relevant reference catalog data.
34  refObjLoader : `lsst.meas.algorithms.LoadReferenceObjectsTask` or `None`
35  For loading reference objects
36  **kwargs :
37  Other keyword arguments required for instantiating a Task (e.g., 'config')
38  """
39  ConfigClass = DirectMatchConfig
40  _DefaultName = "directMatch"
41 
42  def __init__(self, butler=None, refObjLoader=None, **kwargs):
43  Task.__init__(self, **kwargs)
44  if not refObjLoader:
45  if not isinstance(self.config, DirectMatchConfig):
46  raise RuntimeError("DirectMatchTask must be initialized with DirectMatchConfig "
47  "if a refObjLoader is not supplied at initialization")
48  self.makeSubtask("refObjLoader", butler=butler)
49  else:
50  self.refObjLoader = refObjLoader
51  self.makeSubtask("sourceSelection")
52  self.makeSubtask("referenceSelection")
53 
54  def run(self, catalog, filterName=None, epoch=None):
55  """Load reference objects and match to them.
56 
57  Parameters
58  ----------
59  catalog : `lsst.afw.table.SourceCatalog`
60  Catalog to match.
61  filterName : `str`
62  Name of filter loading fluxes
63  epoch : `astropy.time.Time` or `None`
64  Epoch to which to correct proper motion and parallax,
65  or `None` to not apply such corrections.
66 
67  Returns
68  -------
69  result : `lsst.pipe.base.Struct`
70  Result struct with components:
71 
72  - matches : Matched sources with associated reference
73  (`lsst.afw.table.SourceMatchVector`)
74  - matchMeta : Match metadata (`lsst.meas.astrom.MatchMetadata`)
75  """
76  circle = self.calculateCircle(catalog)
77  matchMeta = self.refObjLoader.getMetadataCircle(circle.center, circle.radius, filterName, epoch=epoch)
78  emptyResult = Struct(matches=[], matchMeta=matchMeta)
79  sourceSelection = self.sourceSelection.run(catalog)
80  if len(sourceSelection.sourceCat) == 0:
81  self.log.warn("No objects selected from %d objects in source catalog", len(catalog))
82  return emptyResult
83  refData = self.refObjLoader.loadSkyCircle(circle.center, circle.radius, filterName, epoch=epoch)
84  refCat = refData.refCat
85  refSelection = self.referenceSelection.run(refCat)
86  if len(refSelection.sourceCat) == 0:
87  self.log.warn("No objects selected from %d objects in reference catalog", len(refCat))
88  return emptyResult
89  matches = afwTable.matchRaDec(refSelection.sourceCat, sourceSelection.sourceCat,
90  self.config.matchRadius*arcseconds)
91  self.log.info("Matched %d from %d/%d input and %d/%d reference sources" %
92  (len(matches), len(sourceSelection.sourceCat), len(catalog),
93  len(refSelection.sourceCat), len(refCat)))
94  return Struct(matches=matches, matchMeta=matchMeta, refCat=refCat, sourceSelection=sourceSelection,
95  refSelection=refSelection)
96 
97  def calculateCircle(self, catalog):
98  """Calculate a circle enclosing the catalog
99 
100  Parameters
101  ----------
102  catalog : `lsst.afw.table.SourceCatalog`
103  Catalog to encircle
104 
105  Returns
106  -------
107  result : `lsst.pipe.base.Struct`
108  Result struct with components:
109 
110  - center : ICRS center coordinate (`lsst.afw.geom.SpherePoint`)
111  - radius : Radius of the circle (`lsst.geom.Angle`)
112  """
113  coordList = [src.getCoord() for src in catalog]
114  center = averageSpherePoint(coordList)
115  radius = max(center.separation(coord) for coord in coordList)
116  return Struct(center=center, radius=radius + self.config.matchRadius*arcseconds)
def run(self, catalog, filterName=None, epoch=None)
Definition: directMatch.py:54
template SourceMatchVector matchRaDec(SourceCatalog const &, lsst::geom::Angle, MatchControl const &)
def __init__(self, butler=None, refObjLoader=None, kwargs)
Definition: directMatch.py:42