34 from .forcedPhotImage
import ForcedPhotImageTask, ForcedPhotImageConfig
37 from lsst.meas.mosaic
import applyMosaicResults
39 applyMosaicResults =
None 41 __all__ = (
"PerTractCcdDataIdContainer",
"ForcedPhotCcdConfig",
"ForcedPhotCcdTask",
"imageOverlapsTract")
45 """A data ID container which combines raw data IDs with a tract. 49 Required because we need to add "tract" to the raw data ID keys (defined as 50 whatever we use for ``src``) when no tract is provided (so that the user is 51 not required to know which tracts are spanned by the raw data ID). 53 This subclass of `~lsst.pipe.base.DataIdContainer` assumes that a calexp is 54 being measured using the detection information, a set of reference 55 catalogs, from the set of coadds which intersect with the calexp. It needs 56 the calexp id (e.g. visit, raft, sensor), but is also uses the tract to 57 decide what set of coadds to use. The references from the tract whose 58 patches intersect with the calexp are used. 62 """Make self.refList from self.idList 64 if self.datasetType
is None:
65 raise RuntimeError(
"Must call setDatasetType first")
66 log = Log.getLogger(
"meas.base.forcedPhotCcd.PerTractCcdDataIdContainer")
68 visitTract = collections.defaultdict(set)
69 visitRefs = collections.defaultdict(list)
70 for dataId
in self.idList:
71 if "tract" not in dataId:
73 log.info(
"Reading WCS for components of dataId=%s to determine tracts", dict(dataId))
75 skymap = namespace.butler.get(namespace.config.coaddName +
"Coadd_skyMap")
77 for ref
in namespace.butler.subset(
"calexp", dataId=dataId):
78 if not ref.datasetExists(
"calexp"):
81 visit = ref.dataId[
"visit"]
82 visitRefs[visit].append(ref)
84 md = ref.get(
"calexp_md", immediate=
True)
89 tract = skymap.findTract(wcs.pixelToSky(box.getCenter()))
91 visitTract[visit].add(tract.getId())
93 self.refList.extend(ref
for ref
in namespace.butler.subset(self.datasetType, dataId=dataId))
96 for visit, tractSet
in visitTract.items():
97 for ref
in visitRefs[visit]:
98 for tract
in tractSet:
99 self.refList.append(namespace.butler.dataRef(datasetType=self.datasetType,
100 dataId=ref.dataId, tract=tract))
102 tractCounter = collections.Counter()
103 for tractSet
in visitTract.values():
104 tractCounter.update(tractSet)
105 log.info(
"Number of visits for each tract: %s", dict(tractCounter))
109 """Return whether the given bounding box overlaps the tract given a WCS. 113 tract : `lsst.skymap.TractInfo` 114 TractInfo specifying a tract. 115 imageWcs : `lsst.afw.geom.SkyWcs` 116 World coordinate system for the image. 117 imageBox : `lsst.geom.Box2I` 118 Bounding box for the image. 123 `True` if the bounding box overlaps the tract; `False` otherwise. 125 tractPoly = tract.getOuterSkyPolygon()
129 imageSkyCorners = imageWcs.pixelToSky(imagePixelCorners)
130 except lsst.pex.exceptions.LsstCppException
as e:
132 if (
not isinstance(e.message, lsst.pex.exceptions.DomainErrorException)
and 133 not isinstance(e.message, lsst.pex.exceptions.RuntimeErrorException)):
138 return tractPoly.intersects(imagePoly)
142 doApplyUberCal = lsst.pex.config.Field(
144 doc=
"Apply meas_mosaic ubercal results to input calexps?",
158 self.
exposure.nameTemplate =
"{inputName}" 159 self.
exposure.dimensions = [
"Instrument",
"Visit",
"Detector"]
160 self.
measCat.nameTemplate =
"forced_src" 161 self.
measCat.dimensions = [
"Instrument",
"Visit",
"Detector",
"SkyMap",
"Tract"]
163 self.formatTemplateNames({
"inputName":
"calexp",
164 "inputCoaddName":
"deep"})
165 self.quantum.dimensions = (
"Instrument",
"Visit",
"Detector",
"SkyMap",
"Tract")
169 """A command-line driver for performing forced measurement on CCD images. 173 This task is a subclass of 174 :lsst-task:`lsst.meas.base.forcedPhotImage.ForcedPhotImageTask` which is 175 specifically for doing forced measurement on a single CCD exposure, using 176 as a reference catalog the detections which were made on overlapping 179 The `run` method (inherited from `ForcedPhotImageTask`) takes a 180 `~lsst.daf.persistence.ButlerDataRef` argument that corresponds to a single 181 CCD. This should contain the data ID keys that correspond to the 182 ``forced_src`` dataset (the output dataset for this task), which are 183 typically all those used to specify the ``calexp`` dataset (``visit``, 184 ``raft``, ``sensor`` for LSST data) as well as a coadd tract. The tract is 185 used to look up the appropriate coadd measurement catalogs to use as 186 references (e.g. ``deepCoadd_src``; see 187 :lsst-task:`lsst.meas.base.references.CoaddSrcReferencesTask` for more 188 information). While the tract must be given as part of the dataRef, the 189 patches are determined automatically from the bounding box and WCS of the 190 calexp to be measured, and the filter used to fetch references is set via 191 the ``filter`` option in the configuration of 192 :lsst-task:`lsst.meas.base.references.BaseReferencesTask`). 194 In addition to the `run` method, `ForcedPhotCcdTask` overrides several 195 methods of `ForcedPhotImageTask` to specialize it for single-CCD 196 processing, including `~ForcedPhotImageTask.makeIdFactory`, 197 `~ForcedPhotImageTask.fetchReferences`, and 198 `~ForcedPhotImageTask.getExposure`. None of these should be called 199 directly by the user, though it may be useful to override them further in 203 ConfigClass = ForcedPhotCcdConfig
204 RunnerClass = lsst.pipe.base.ButlerInitializedTaskRunner
205 _DefaultName =
"forcedPhotCcd" 210 inputData[
'refCat'], inputData[
'refWcs'])
212 inputData[
'exposure'],
213 inputData[
'refCat'], inputData[
'refWcs'],
214 "VisitDetector", butler)
216 return self.
run(**inputData)
219 """Filter reference catalog so that all sources are within the 220 boundaries of the exposure. 224 exposure : `lsst.afw.image.exposure.Exposure` 225 Exposure to generate the catalog for. 226 refCat : `lsst.afw.table.SourceCatalog` 227 Catalog of shapes and positions at which to force photometry. 228 refWcs : `lsst.afw.image.SkyWcs` 229 Reference world coordinate system. 233 refSources : `lsst.afw.table.SourceCatalog` 234 Filtered catalog of forced sources to measure. 238 Filtering the reference catalog is currently handled by Gen2 239 specific methods. To function for Gen3, this method copies 240 code segments to do the filtering and transformation. The 241 majority of this code is based on the methods of 242 lsst.meas.algorithms.loadReferenceObjects.ReferenceObjectLoader 248 expWcs = exposure.getWcs()
249 expRegion = exposure.getBBox(lsst.afw.image.PARENT)
251 expBoxCorners = expBBox.getCorners()
252 expSkyCorners = [expWcs.pixelToSky(corner).getVector()
for 253 corner
in expBoxCorners]
258 sources = type(refCat)(refCat.table)
259 for record
in refCat:
260 if expPolygon.contains(record.getCoord().getVector()):
261 sources.append(record)
262 refCatIdDict = {ref.getId(): ref.getParent()
for ref
in sources}
267 refSources = type(refCat)(refCat.table)
268 for record
in refCat:
269 if expPolygon.contains(record.getCoord().getVector()):
270 recordId = record.getId()
273 if topId
in refCatIdDict:
274 topId = refCatIdDict[topId]
278 refSources.append(record)
282 for refRecord
in refSources:
283 refRecord.setFootprint(refRecord.getFootprint().
transform(refWcs,
289 """Create an object that generates globally unique source IDs. 291 Source IDs are created based on a per-CCD ID and the ID of the CCD 296 dataRef : `lsst.daf.persistence.ButlerDataRef` 297 Butler data reference. The ``ccdExposureId_bits`` and 298 ``ccdExposureId`` datasets are accessed. The data ID must have the 299 keys that correspond to ``ccdExposureId``, which are generally the 300 same as those that correspond to ``calexp`` (``visit``, ``raft``, 301 ``sensor`` for LSST data). 303 expBits = dataRef.get(
"ccdExposureId_bits")
304 expId = int(dataRef.get(
"ccdExposureId"))
308 return int(dataRef.get(
"ccdExposureId", immediate=
True))
311 """Get sources that overlap the exposure. 315 dataRef : `lsst.daf.persistence.ButlerDataRef` 316 Butler data reference corresponding to the image to be measured; 317 should have ``tract``, ``patch``, and ``filter`` keys. 318 exposure : `lsst.afw.image.Exposure` 319 The image to be measured (used only to obtain a WCS and bounding 324 referencs : `lsst.afw.table.SourceCatalog` 325 Catalog of sources that overlap the exposure 329 The returned catalog is sorted by ID and guarantees that all included 330 children have their parent included and that all Footprints are valid. 332 All work is delegated to the references subtask; see 333 :lsst-task:`lsst.meas.base.references.CoaddSrcReferencesTask` 334 for information about the default behavior. 338 unfiltered = self.references.fetchInBox(dataRef, exposure.getBBox(), exposure.getWcs())
339 for record
in unfiltered:
340 if record.getFootprint()
is None or record.getFootprint().getArea() == 0:
341 if record.getParent() != 0:
342 self.log.warn(
"Skipping reference %s (child of %s) with bad Footprint",
343 record.getId(), record.getParent())
345 self.log.warn(
"Skipping reference parent %s with bad Footprint", record.getId())
346 badParents.add(record.getId())
347 elif record.getParent()
not in badParents:
348 references.append(record)
354 """Read input exposure for measurement. 358 dataRef : `lsst.daf.persistence.ButlerDataRef` 359 Butler data reference. Only the ``calexp`` dataset is used, unless 360 ``config.doApplyUberCal`` is `True`, in which case the 361 corresponding meas_mosaic outputs are used as well. 363 exposure = ForcedPhotImageTask.getExposure(self, dataRef)
364 if not self.config.doApplyUberCal:
366 if applyMosaicResults
is None:
368 "Cannot use improved calibrations for %s because meas_mosaic could not be imported." 374 def _getConfigName(self):
376 return self.
dataPrefix +
"forcedPhotCcd_config" 378 def _getMetadataName(self):
380 return self.
dataPrefix +
"forcedPhotCcd_metadata" 383 def _makeArgumentParser(cls):
384 parser = lsst.pipe.base.ArgumentParser(name=cls.
_DefaultName)
385 parser.add_id_argument(
"--id",
"forced_src", help=
"data ID with raw CCD keys [+ tract optionally], " 386 "e.g. --id visit=12345 ccd=1,2 [tract=0]",
387 ContainerClass=PerTractCcdDataIdContainer)
def getExposure(self, dataRef)
def adaptArgsAndRun(self, inputData, inputDataIds, outputDataIds, butler)
def makeDataRefList(self, namespace)
def run(self, measCat, exposure, refCat, refWcs, exposureId=None)
def makeIdFactory(self, dataRef)
static std::shared_ptr< IdFactory > makeSource(RecordId expId, int reserved)
def generateMeasCat(self, exposureDataId, exposure, refCat, refWcs, idPackerName, butler)
def imageOverlapsTract(tract, imageWcs, imageBox)
def getExposureId(self, dataRef)
std::shared_ptr< SkyWcs > makeSkyWcs(TransformPoint2ToPoint2 const &pixelsToFieldAngle, lsst::geom::Angle const &orientation, bool flipX, lsst::geom::SpherePoint const &boresight, std::string const &projection="TAN")
def filterReferences(self, exposure, refCat, refWcs)
static Key< RecordId > getParentKey()
def fetchReferences(self, dataRef, exposure)
lsst::geom::Box2I bboxFromMetadata(daf::base::PropertySet &metadata)
static ConvexPolygon convexHull(std::vector< UnitVector3d > const &points)