23__all__ = [
'RefMatchConfig',
'RefMatchTask']
32from lsst.meas.algorithms
import ReferenceSourceSelectorTask
33from lsst.meas.algorithms.sourceSelector
import sourceSelectorRegistry
34from lsst.utils.timer
import timeMethod
35from .matchPessimisticB
import MatchPessimisticBTask
36from .display
import displayAstrometry
37from .
import makeMatchStatistics
41 matcher = pexConfig.ConfigurableField(
42 target=MatchPessimisticBTask,
43 doc=
"reference object/source matcher",
45 matchDistanceSigma = pexConfig.RangeField(
46 doc=
"the maximum match distance is set to "
47 " mean_match_distance + matchDistanceSigma*std_dev_match_distance; "
48 "ignored if not fitting a WCS",
53 sourceSelector = sourceSelectorRegistry.makeField(
54 doc=
"How to select sources for cross-matching.",
57 referenceSelector = pexConfig.ConfigurableField(
58 target=ReferenceSourceSelectorTask,
59 doc=
"How to select reference objects for cross-matching."
61 sourceFluxType = pexConfig.Field(
63 doc=
"Source flux type to use in source selection.",
69 self.
sourceSelectorsourceSelector[
'science'].fluxLimit.fluxField = \
71 self.
sourceSelectorsourceSelector[
'science'].signalToNoise.fluxField = \
73 self.
sourceSelectorsourceSelector[
'science'].signalToNoise.errField = \
78 """Match an input source catalog with objects from a reference catalog.
82 refObjLoader : `lsst.meas.algorithms.ReferenceLoader`
83 A reference object loader object
85 additional keyword arguments for pipe_base `lsst.pipe.base.Task`
87 ConfigClass = RefMatchConfig
88 _DefaultName = "calibrationBaseClass"
91 pipeBase.Task.__init__(self, **kwargs)
97 if self.config.sourceSelector.name ==
'matcher':
98 if self.config.sourceSelector[
'matcher'].sourceFluxType != self.config.sourceFluxType:
99 raise RuntimeError(
"The sourceFluxType in the sourceSelector['matcher'] must match "
100 "the configured sourceFluxType")
102 self.makeSubtask(
"matcher")
103 self.makeSubtask(
"sourceSelector")
104 self.makeSubtask(
"referenceSelector")
107 """Sets the reference object loader for the task
112 An instance of a reference object loader task or class
118 """Load reference objects overlapping an exposure and match to sources
119 detected on that exposure.
124 exposure that the sources overlap
126 catalog of sources detected on the exposure
130 result : `lsst.pipe.base.Struct`
131 Result struct with Components:
133 - ``refCat`` : reference object catalog of objects that overlap the
135 - ``matches`` : Matched sources
and references
137 - ``matchMeta`` : metadata needed to unpersist matches
142 ignores config.matchDistanceSigma
145 raise RuntimeError(
"Running matcher task with no refObjLoader set in __ini__ or setRefObjLoader")
151 sourceSelection = self.sourceSelector.run(sourceCat)
153 sourceFluxField =
"slot_%sFlux_instFlux" % (self.config.sourceFluxType)
158 filterName=expMd.filterName,
159 photoCalib=expMd.photoCalib,
163 refSelection = self.referenceSelector.run(loadRes.refCat)
165 matchMeta = self.
refObjLoaderrefObjLoader.getMetadataBox(
168 filterName=expMd.filterName,
169 photoCalib=expMd.photoCalib,
173 matchRes = self.matcher.matchObjectsToSources(
174 refCat=refSelection.sourceCat,
175 sourceCat=sourceSelection.sourceCat,
177 sourceFluxField=sourceFluxField,
178 refFluxField=loadRes.fluxField,
179 match_tolerance=
None,
184 "Found %d matches with scatter = %0.3f +- %0.3f arcsec; ",
185 len(matchRes.matches), distStats.distMean.asArcseconds(), distStats.distStdDev.asArcseconds()
189 frame = int(debug.frame)
191 refCat=refSelection.sourceCat,
192 sourceCat=sourceSelection.sourceCat,
193 matches=matchRes.matches,
200 return pipeBase.Struct(
201 refCat=loadRes.refCat,
202 refSelection=refSelection,
203 sourceSelection=sourceSelection,
204 matches=matchRes.matches,
208 def _computeMatchStatsOnSky(self, matchList):
209 """Compute on-sky radial distance statistics for a match list
214 list of matches between reference object and sources;
215 the distance field
is the only field read
and it must be set to distance
in radians
219 result : `lsst.pipe.base.Struct`
220 Result struct
with components:
222 - ``distMean`` : clipped mean of on-sky radial separation (`float`)
223 - ``distStdDev`` : clipped standard deviation of on-sky radial
225 - ``maxMatchDist`` : distMean + self.config.matchDistanceSigma *
229 distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*lsst.geom.radians
230 distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*lsst.geom.radians
231 return pipeBase.Struct(
233 distStdDev=distStdDev,
234 maxMatchDist=distMean + self.config.matchDistanceSigma * distStdDev,
237 def _getExposureMetadata(self, exposure):
238 """Extract metadata from an exposure.
246 result : `lsst.pipe.base.Struct`
247 Result struct with components:
252 - ``filterName`` : name of filter band (`str`)
253 - ``epoch`` : date of exposure (`astropy.time.Time`)
256 exposureInfo = exposure.getInfo()
257 filterLabel = exposureInfo.getFilterLabel()
258 filterName = filterLabel.bandLabel if filterLabel
is not None else None
260 if exposure.getInfo().hasVisitInfo():
261 epochTaiMjd = exposure.getInfo().getVisitInfo().getDate().get(system=DateTime.MJD,
263 epoch = astropy.time.Time(epochTaiMjd, scale=
"tai", format=
"mjd")
265 return pipeBase.Struct(
266 bbox=exposure.getBBox(),
267 wcs=exposureInfo.getWcs(),
268 photoCalib=exposureInfo.getPhotoCalib(),
269 filterName=filterName,
def _computeMatchStatsOnSky(self, matchList)
def loadAndMatch(self, exposure, sourceCat)
def __init__(self, refObjLoader, **kwargs)
def _getExposureMetadata(self, exposure)
def setRefObjLoader(self, refObjLoader)
def displayAstrometry(refCat=None, sourceCat=None, distortedCentroidKey=None, bbox=None, exposure=None, matches=None, frame=1, title="", pause=True)