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 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)
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:
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)
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"]
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
205 _DefaultName =
"forcedPhotCcd" 209 inputData[
'refWcs'] = butler.get(f
"{self.config.refWcs.name}.wcs", inputDataIds[
"refWcs"])
211 inputData[
'refCat'], inputData[
'refWcs'])
213 inputData[
'exposure'],
214 inputData[
'refCat'], inputData[
'refWcs'],
215 "VisitDetector", butler)
217 return self.
run(**inputData)
220 """Filter reference catalog so that all sources are within the 221 boundaries of the exposure. 225 exposure : `lsst.afw.image.exposure.Exposure` 226 Exposure to generate the catalog for. 227 refCat : `lsst.afw.table.SourceCatalog` 228 Catalog of shapes and positions at which to force photometry. 229 refWcs : `lsst.afw.image.SkyWcs` 230 Reference world coordinate system. 234 refSources : `lsst.afw.table.SourceCatalog` 235 Filtered catalog of forced sources to measure. 239 Filtering the reference catalog is currently handled by Gen2 240 specific methods. To function for Gen3, this method copies 241 code segments to do the filtering and transformation. The 242 majority of this code is based on the methods of 243 lsst.meas.algorithms.loadReferenceObjects.ReferenceObjectLoader 249 expWcs = exposure.getWcs()
250 expRegion = exposure.getBBox(lsst.afw.image.PARENT)
252 expBoxCorners = expBBox.getCorners()
253 expSkyCorners = [expWcs.pixelToSky(corner).getVector()
for 254 corner
in expBoxCorners]
259 sources =
type(refCat)(refCat.table)
260 for record
in refCat:
261 if expPolygon.contains(record.getCoord().getVector()):
262 sources.append(record)
263 refCatIdDict = {ref.getId(): ref.getParent()
for ref
in sources}
268 refSources =
type(refCat)(refCat.table)
269 for record
in refCat:
270 if expPolygon.contains(record.getCoord().getVector()):
271 recordId = record.getId()
274 if topId
in refCatIdDict:
275 topId = refCatIdDict[topId]
279 refSources.append(record)
283 for refRecord
in refSources:
284 refRecord.setFootprint(refRecord.getFootprint().
transform(refWcs,
290 """Create an object that generates globally unique source IDs. 292 Source IDs are created based on a per-CCD ID and the ID of the CCD 297 dataRef : `lsst.daf.persistence.ButlerDataRef` 298 Butler data reference. The ``ccdExposureId_bits`` and 299 ``ccdExposureId`` datasets are accessed. The data ID must have the 300 keys that correspond to ``ccdExposureId``, which are generally the 301 same as those that correspond to ``calexp`` (``visit``, ``raft``, 302 ``sensor`` for LSST data). 304 expBits = dataRef.get(
"ccdExposureId_bits")
305 expId =
int(dataRef.get(
"ccdExposureId"))
309 return int(dataRef.get(
"ccdExposureId", immediate=
True))
312 """Get sources that overlap the exposure. 316 dataRef : `lsst.daf.persistence.ButlerDataRef` 317 Butler data reference corresponding to the image to be measured; 318 should have ``tract``, ``patch``, and ``filter`` keys. 319 exposure : `lsst.afw.image.Exposure` 320 The image to be measured (used only to obtain a WCS and bounding 325 referencs : `lsst.afw.table.SourceCatalog` 326 Catalog of sources that overlap the exposure 330 The returned catalog is sorted by ID and guarantees that all included 331 children have their parent included and that all Footprints are valid. 333 All work is delegated to the references subtask; see 334 :lsst-task:`lsst.meas.base.references.CoaddSrcReferencesTask` 335 for information about the default behavior. 339 unfiltered = self.references.fetchInBox(dataRef, exposure.getBBox(), exposure.getWcs())
340 for record
in unfiltered:
341 if record.getFootprint()
is None or record.getFootprint().getArea() == 0:
342 if record.getParent() != 0:
343 self.
log.
warn(
"Skipping reference %s (child of %s) with bad Footprint",
344 record.getId(), record.getParent())
346 self.
log.
warn(
"Skipping reference parent %s with bad Footprint", record.getId())
347 badParents.add(record.getId())
348 elif record.getParent()
not in badParents:
349 references.append(record)
355 """Read input exposure for measurement. 359 dataRef : `lsst.daf.persistence.ButlerDataRef` 360 Butler data reference. Only the ``calexp`` dataset is used, unless 361 ``config.doApplyUberCal`` is `True`, in which case the 362 corresponding meas_mosaic outputs are used as well. 364 exposure = ForcedPhotImageTask.getExposure(self, dataRef)
365 if not self.
config.doApplyUberCal:
367 if applyMosaicResults
is None:
369 "Cannot use improved calibrations for %s because meas_mosaic could not be imported." 375 def _getConfigName(self):
377 return self.
dataPrefix +
"forcedPhotCcd_config" 379 def _getMetadataName(self):
381 return self.
dataPrefix +
"forcedPhotCcd_metadata" 384 def _makeArgumentParser(cls):
386 parser.add_id_argument(
"--id",
"forced_src", help=
"data ID with raw CCD keys [+ tract optionally], " 387 "e.g. --id visit=12345 ccd=1,2 [tract=0]",
388 ContainerClass=PerTractCcdDataIdContainer)
def getExposure(self, dataRef)
A floating-point coordinate rectangle geometry.
def adaptArgsAndRun(self, inputData, inputDataIds, outputDataIds, butler)
def makeDataRefList(self, namespace)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
def formatTemplateNames(self, templateParamsDict)
daf::base::PropertySet * set
def run(self, measCat, exposure, refCat, refWcs, exposureId=None)
def makeIdFactory(self, dataRef)
static std::shared_ptr< IdFactory > makeSource(RecordId expId, int reserved)
Return an IdFactory that includes another, fixed ID in the higher-order bits.
static ConvexPolygon convexHull(std::vector< UnitVector3d > const &points)
convexHull returns the convex hull of the given set of points if it exists and throws an exception ot...
def generateMeasCat(self, exposureDataId, exposure, refCat, refWcs, idPackerName, butler)
def imageOverlapsTract(tract, imageWcs, imageBox)
ConvexPolygon is a closed convex polygon on the unit sphere.
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")
Construct a FITS SkyWcs from camera geometry.
def filterReferences(self, exposure, refCat, refWcs)
static Key< RecordId > getParentKey()
Key for the parent ID.
def fetchReferences(self, dataRef, exposure)
lsst::geom::Box2I bboxFromMetadata(daf::base::PropertySet &metadata)
Determine the image bounding box from its metadata (FITS header)