lsst.meas.astrom  14.0-5-ge815445
directMatch.py
Go to the documentation of this file.
1 from __future__ import absolute_import, division, print_function
2 
3 __all__ = ["DirectMatchConfig", "DirectMatchTask"]
4 
5 from lsst.pex.config import Config, Field, ConfigurableField
6 from lsst.pipe.base import Task, Struct
7 from lsst.meas.algorithms import (LoadIndexedReferenceObjectsTask, ScienceSourceSelectorTask,
8  ReferenceSourceSelectorTask)
9 import lsst.afw.table as afwTable
10 import lsst.afw.coord as afwCoord
11 from lsst.afw.geom import arcseconds
12 
13 
14 class DirectMatchConfig(Config):
15  """Configuration for DirectMatchTask"""
16  refObjLoader = ConfigurableField(target=LoadIndexedReferenceObjectsTask, doc="Load reference objects")
17  matchRadius = Field(dtype=float, default=0.25, doc="Matching radius, arcsec")
18  sourceSelection = ConfigurableField(target=ScienceSourceSelectorTask,
19  doc="Selection of science sources")
20  referenceSelection = ConfigurableField(target=ReferenceSourceSelectorTask,
21  doc="Selection of reference sources")
22 
23 
24 class DirectMatchTask(Task):
25  """!Simple matching of a source catalog to a reference catalog
26 
27  @anchor DirectMatchTask_
28 
29  @section meas_astrom_match_Contents Contents
30 
31  - @ref meas_astrom_match_Purpose
32  - @ref meas_astrom_match_Initialize
33  - @ref meas_astrom_match_IO
34  - @ref meas_astrom_match_Config
35  - @ref meas_astrom_match_Example
36 
37  @section meas_astrom_match_Purpose Description
38 
39  Match sources to reference objects. The matching permits no rotation or scaling,
40  but uses the existing sky positions in the source catalog. This is often useful
41  for QA, as it allows validating the pipeline astrometry and photometry against
42  the reference catalog.
43 
44  Note that this DirectMatchTask is not currently suitable for use within the
45  AstrometryTask, as it has a different interface and serves a different purpose.
46 
47  @section meas_astrom_match_Initialize Task initialisation
48 
49  @copydoc \_\_init\_\_
50 
51  @section meas_astrom_match_IO Invoking the Task
52 
53  @copydoc run
54 
55  @section meas_astrom_match_Config Configuration parameters
56 
57  See @ref DirectMatchConfig
58 
59  @section meas_astrom_match_Example A complete example of using DirectMatchTask
60 
61  config = DirectMatchConfig()
62  task = DirectMatchTask(butler=butler, config=config)
63  matchResults = task.run(catalog)
64 
65  """
66 
67  ConfigClass = DirectMatchConfig
68  _DefaultName = "directMatch"
69 
70  def __init__(self, butler=None, refObjLoader=None, **kwargs):
71  """!Ctor
72 
73  Either a 'butler' or 'refObjLoader' is required.
74 
75  @param butler Data butler, or None
76  @param refObjLoader For loading reference objects (lsst.meas.algorithms.LoadReferenceObjectsTask), or
77  None
78  @param kwargs Other keyword arguments required for instantiating a Task (e.g., 'config')
79  """
80  Task.__init__(self, **kwargs)
81  if not refObjLoader:
82  self.makeSubtask("refObjLoader", butler=butler)
83  else:
84  self.refObjLoader = refObjLoader
85  self.makeSubtask("sourceSelection")
86  self.makeSubtask("referenceSelection")
87 
88  def run(self, catalog, filterName=None):
89  """!Load reference objects and match to them
90 
91  @param[in] catalog Catalog to match to (lsst.afw.table.SourceCatalog)
92  @param[in] filterName Name of filter, for loading fluxes (str)
93  @return Struct with matches (lsst.afw.table.SourceMatchVector) and
94  matchMeta (lsst.meas.astrom.MatchMetadata)
95  """
96  circle = self.calculateCircle(catalog)
97  matchMeta = self.refObjLoader.getMetadataCircle(circle.center, circle.radius, filterName)
98  emptyResult = Struct(matches=[], matchMeta=matchMeta)
99  sourceSelection = self.sourceSelection.selectSources(catalog)
100  if len(sourceSelection.sourceCat) == 0:
101  self.log.warn("No objects selected from %d objects in source catalog", len(catalog))
102  return emptyResult
103  refData = self.refObjLoader.loadSkyCircle(circle.center, circle.radius, filterName)
104  refCat = refData.refCat
105  refSelection = self.referenceSelection.selectSources(refCat)
106  if len(refSelection.sourceCat) == 0:
107  self.log.warn("No objects selected from %d objects in reference catalog", len(refCat))
108  return emptyResult
109  matches = afwTable.matchRaDec(refSelection.sourceCat, sourceSelection.sourceCat,
110  self.config.matchRadius*arcseconds)
111  self.log.info("Matched %d from %d/%d input and %d/%d reference sources" %
112  (len(matches), len(sourceSelection.sourceCat), len(catalog),
113  len(refSelection.sourceCat), len(refCat)))
114  return Struct(matches=matches, matchMeta=matchMeta, refCat=refCat, sourceSelection=sourceSelection,
115  refSelection=refSelection)
116 
117  def calculateCircle(self, catalog):
118  """!Calculate a circle enclosing the catalog
119 
120  @param[in] catalog Catalog we will encircle (lsst.afw.table.SourceCatalog)
121  @return Struct with center (lsst.afw.coord.Coord) and radius (lsst.afw.geom.Angle)
122  """
123  coordList = [src.getCoord() for src in catalog]
124  center = afwCoord.averageCoord(coordList)
125  radius = max(center.angularSeparation(coord) for coord in coordList)
126  return Struct(center=center, radius=radius + self.config.matchRadius*arcseconds)
Simple matching of a source catalog to a reference catalog.
Definition: directMatch.py:24
std::vector< Match< typename Cat::Record, typename Cat::Record > > matchRaDec(Cat const &cat, Angle radius, bool symmetric)
def run(self, catalog, filterName=None)
Load reference objects and match to them.
Definition: directMatch.py:88
def calculateCircle(self, catalog)
Calculate a circle enclosing the catalog.
Definition: directMatch.py:117
std::shared_ptr< Coord > averageCoord(std::vector< std::shared_ptr< Coord const >> const coords, CoordSystem system=UNKNOWN)
def __init__(self, butler=None, refObjLoader=None, kwargs)
Ctor.
Definition: directMatch.py:70