23 from __future__
import absolute_import, division, print_function
25 __all__ = [
"getRefFluxField",
"getRefFluxKeys",
"LoadReferenceObjectsTask",
"LoadReferenceObjectsConfig"]
31 import lsst.afw.coord
as afwCoord
33 import lsst.afw.table
as afwTable
34 import lsst.pex.config
as pexConfig
35 import lsst.pipe.base
as pipeBase
36 from lsst.daf.base
import PropertyList
37 from future.utils
import with_metaclass
41 """!Get name of flux field in schema 43 if filterName is specified: 44 return *filterName*_camFlux if present 45 else return *filterName*_flux if present (camera filter name matches reference filter name) 46 else throw RuntimeError 48 return camFlux, if present, 49 else throw RuntimeError 51 @param[in] schema reference catalog schema 52 @param[in] filterName name of camera filter 53 @return flux field name 54 @throw RuntimeError if appropriate field is not found 56 if not isinstance(schema, afwTable.Schema):
57 raise RuntimeError(
"schema=%s is not a schema" % (schema,))
59 fluxFieldList = [filterName +
"_camFlux", filterName +
"_flux"]
61 fluxFieldList = [
"camFlux"]
62 for fluxField
in fluxFieldList:
63 if fluxField
in schema:
66 raise RuntimeError(
"Could not find flux field(s) %s" % (
", ".join(fluxFieldList)))
70 """!Return flux and flux error keys 72 @param[in] schema reference catalog schema 73 @param[in] filterName name of camera filter 74 @return a pair of keys: 76 flux error key, if present, else None 77 @throw RuntimeError if flux field not found 80 fluxErrField = fluxField +
"Sigma" 81 fluxKey = schema[fluxField].asKey()
83 fluxErrKey = schema[fluxErrField].asKey()
86 return (fluxKey, fluxErrKey)
90 pixelMargin = pexConfig.RangeField(
91 doc=
"Padding to add to 4 all edges of the bounding box (pixels)",
96 defaultFilter = pexConfig.Field(
97 doc=
"Default reference catalog filter to use if filter not specified in exposure; " +
98 "if blank then filter must be specified in exposure",
102 filterMap = pexConfig.DictField(
103 doc=
"Mapping of camera filter name: reference catalog filter name; " +
104 "each reference filter must exist",
120 """!Abstract base class to load objects from reference catalogs 122 @anchor LoadReferenceObjectsTask_ 124 @section meas_algorithms_loadReferenceObjects_Contents Contents 126 - @ref meas_algorithms_loadReferenceObjects_Purpose 127 - @ref meas_algorithms_loadReferenceObjects_Initialize 128 - @ref meas_algorithms_loadReferenceObjects_IO 129 - @ref meas_algorithms_loadReferenceObjects_Schema 130 - @ref meas_algorithms_loadReferenceObjects_Config 132 @section meas_algorithms_loadReferenceObjects_Purpose Description 134 Abstract base class for tasks that load objects from a reference catalog 135 in a particular region of the sky. 137 Implementations must subclass this class, override the loadSkyCircle method, 138 and will typically override the value of ConfigClass with a task-specific config class. 140 @section meas_algorithms_loadReferenceObjects_Initialize Task initialisation 142 @copydoc \_\_init\_\_ 144 @section meas_algorithms_loadReferenceObjects_IO Invoking the Task 146 @copydoc loadObjectsInBBox 148 @section meas_algorithms_loadReferenceObjects_Schema Schema of the reference object catalog 150 Reference object catalogs are instances of lsst.afw.table.SimpleCatalog with the following schema 151 (other fields may also be present): 152 - coord: position of star on sky (an lsst.afw.coord.IcrsCoord) 153 - centroid: position of star on an exposure, if relevant (an lsst.afw.Point2D) 154 - hasCentroid: is centroid usable? 155 - *referenceFilterName*_flux: brightness in the specified reference catalog filter (Jy) 156 Note: the function lsst.afw.image.abMagFromFlux will convert flux in Jy to AB Magnitude. 157 - *referenceFilterName*_fluxSigma (optional): brightness standard deviation (Jy); 158 omitted if no data is available; possibly nan if data is available for some objects but not others 159 - camFlux: brightness in default camera filter (Jy); omitted if defaultFilter not specified 160 - camFluxSigma: brightness standard deviation for default camera filter; 161 omitted if defaultFilter not specified or standard deviation not available that filter 162 - *cameraFilterName*_camFlux: brightness in specified camera filter (Jy) 163 - *cameraFilterName*_camFluxSigma (optional): brightness standard deviation 164 in specified camera filter (Jy); omitted if no data is available; 165 possibly nan if data is available for some objects but not others 166 - photometric (optional): is the object usable for photometric calibration? 167 - resolved (optional): is the object spatially resolved? 168 - variable (optional): does the object have variable brightness? 170 @section meas_algorithms_loadReferenceObjects_Config Configuration parameters 172 See @ref LoadReferenceObjectsConfig for a base set of configuration parameters. 173 Most subclasses will add configuration variables. 175 ConfigClass = LoadReferenceObjectsConfig
176 _DefaultName =
"LoadReferenceObjects" 179 """!Construct a LoadReferenceObjectsTask 181 @param[in] butler A daf.persistence.Butler object. This allows subclasses to use the butler to 182 access reference catalog files using the stack I/O abstraction scheme. 184 pipeBase.Task.__init__(self, *args, **kwargs)
189 """!Load reference objects that overlap a pixel-based rectangular region 191 The search algorithm works by searching in a region in sky coordinates whose center is the center 192 of the bbox and radius is large enough to just include all 4 corners of the bbox. 193 Stars that lie outside the bbox are then trimmed from the list. 195 @param[in] bbox bounding box for pixels (an lsst.afw.geom.Box2I or Box2D) 196 @param[in] wcs WCS (an lsst.afw.image.Wcs) 197 @param[in] filterName name of camera filter, or None or blank for the default filter 198 @param[in] calib calibration, or None if unknown 200 @return an lsst.pipe.base.Struct containing: 201 - refCat a catalog of reference objects with the 202 \link meas_algorithms_loadReferenceObjects_Schema standard schema \endlink 203 as documented in LoadReferenceObjects, including photometric, resolved and variable; 204 hasCentroid is False for all objects. 205 - fluxField = name of flux field for specified filterName 210 self.log.info(
"Loading reference objects using center %s and radius %s deg" %
211 (circle.coord, circle.radius.asDegrees()))
212 loadRes = self.
loadSkyCircle(circle.coord, circle.radius, filterName)
213 refCat = loadRes.refCat
214 numFound = len(refCat)
217 refCat = self.
_trimToBBox(refCat=refCat, bbox=circle.bbox, wcs=wcs)
218 numTrimmed = numFound - len(refCat)
219 self.log.debug(
"trimmed %d out-of-bbox objects, leaving %d", numTrimmed, len(refCat))
220 self.log.info(
"Loaded %d reference objects", len(refCat))
222 loadRes.refCat = refCat
227 """!Load reference objects that overlap a circular sky region 229 @param[in] ctrCoord center of search region (an lsst.afw.geom.Coord) 230 @param[in] radius radius of search region (an lsst.afw.geom.Angle) 231 @param[in] filterName name of filter, or None for the default filter; 232 used for flux values in case we have flux limits (which are not yet implemented) 234 @return an lsst.pipe.base.Struct containing: 235 - refCat a catalog of reference objects with the 236 \link meas_algorithms_loadReferenceObjects_Schema standard schema \endlink 237 as documented in LoadReferenceObjects, including photometric, resolved and variable; 238 hasCentroid is False for all objects. 239 - fluxField = name of flux field for specified filterName 244 def _trimToBBox(refCat, bbox, wcs):
245 """!Remove objects outside a given pixel-based bbox and set centroid and hasCentroid fields 247 @param[in] refCat a catalog of objects (an lsst.afw.table.SimpleCatalog, 248 or other table type that supports getCoord() on records) 249 @param[in] bbox pixel region (an afwImage.Box2D) 250 @param[in] wcs WCS used to convert sky position to pixel position (an lsst.afw.math.WCS) 252 @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set 254 centroidKey = afwTable.Point2DKey(refCat.schema[
"centroid"])
255 hasCentroidKey = refCat.schema[
"hasCentroid"].asKey()
256 retStarCat = type(refCat)(refCat.table)
258 point = wcs.skyToPixel(star.getCoord())
259 if bbox.contains(point):
260 star.set(centroidKey, point)
261 star.set(hasCentroidKey,
True)
262 retStarCat.append(star)
265 def _addFluxAliases(self, schema):
266 """Add aliases for camera filter fluxes to the schema 268 If self.config.defaultFilter then adds these aliases: 269 camFlux: <defaultFilter>_flux 270 camFluxSigma: <defaultFilter>_fluxSigma, if the latter exists 272 For each camFilter: refFilter in self.config.filterMap adds these aliases: 273 <camFilter>_camFlux: <refFilter>_flux 274 <camFilter>_camFluxSigma: <refFilter>_fluxSigma, if the latter exists 276 @throw RuntimeError if any reference flux field is missing from the schema 278 aliasMap = schema.getAliasMap()
280 def addAliasesForOneFilter(filterName, refFilterName):
281 """Add aliases for a single filter 283 @param[in] filterName camera filter name, or "" 284 the name is <filterName>_camFlux or camFlux if filterName is None 285 @param[in] refFilterName reference filter name; <refFilterName>_flux must exist 287 camFluxName = filterName +
"_camFlux" if filterName
is not None else "camFlux" 288 refFluxName = refFilterName +
"_flux" 289 if refFluxName
not in schema:
290 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
291 aliasMap.set(camFluxName, refFluxName)
292 refFluxErrName = refFluxName +
"Sigma" 293 if refFluxErrName
in schema:
294 camFluxErrName = camFluxName +
"Sigma" 295 aliasMap.set(camFluxErrName, refFluxErrName)
297 if self.config.defaultFilter:
298 addAliasesForOneFilter(
None, self.config.defaultFilter)
300 for filterName, refFilterName
in self.config.filterMap.items():
301 addAliasesForOneFilter(filterName, refFilterName)
305 addIsPhotometric=False, addIsResolved=False, addIsVariable=False):
306 """!Make the standard schema for reference object catalogs 308 @param[in] filterNameList list of filter names; used to create *filterName*_flux fields 309 @param[in] addFluxSigma if True then include flux sigma fields 310 @param[in] addIsPhotometric if True add field "photometric" 311 @param[in] addIsResolved if True add field "resolved" 312 @param[in] addIsVariable if True add field "variable" 314 schema = afwTable.SimpleTable.makeMinimalSchema()
315 afwTable.Point2DKey.addFields(
318 "centroid on an exposure, if relevant",
324 doc=
"is position known?",
326 for filterName
in filterNameList:
328 field=
"%s_flux" % (filterName,),
330 doc=
"flux in filter %s" % (filterName,),
334 for filterName
in filterNameList:
336 field=
"%s_fluxSigma" % (filterName,),
338 doc=
"flux uncertainty in filter %s" % (filterName,),
345 doc=
"set if the object can be used for photometric calibration",
351 doc=
"set if the object is spatially resolved",
357 doc=
"set if the object has variable brightness",
361 def _calculateCircle(self, bbox, wcs):
362 """!Compute on-sky center and radius of search region 364 @param[in] bbox bounding box for pixels (an lsst.afw.geom.Box2I or Box2D) 365 @param[in] wcs WCS (an lsst.afw.image.Wcs) 366 @return an lsst.pipe.base.Struct containing: 367 - coord: the central coordinate of the search region (lsst.afw.coord.Coord) 368 - radius: the radius of the search region (lsst.afw.geom.Angle) 369 - bbox: the bounding box used to compute the circle (lsst.afw.geom.Box2D) 371 bbox = afwGeom.Box2D(bbox)
372 bbox.grow(self.config.pixelMargin)
373 coord = wcs.pixelToSky(bbox.getCenter())
374 radius = max(coord.angularSeparation(wcs.pixelToSky(pp))
for pp
in bbox.getCorners())
375 return pipeBase.Struct(coord=coord, radius=radius, bbox=bbox)
378 """!Return metadata about the load 380 This metadata is used for reloading the catalog (e.g., for 381 reconstituting a normalised match list. 383 @param[in] bbox bounding box for pixels (an lsst.afw.geom.Box2I or Box2D) 384 @param[in] wcs WCS (an lsst.afw.image.Wcs) 385 @param[in] filterName name of camera filter, or None or blank for the default filter 386 @param[in] calib calibration, or None if unknown 387 @return metadata (lsst.daf.base.PropertyList) 393 """!Return metadata about the load 395 This metadata is used for reloading the catalog (e.g., for 396 reconstituting a normalised match list. 398 @param[in] coord central coordinate of circle (lsst.afw.coord.Coord) 399 @param[in] radius radius of circle (lsst.afw.geom.Angle) 400 @param[in] filterName name of camera filter, or None or blank for the default filter 401 @param[in] calib calibration, or None if unknown 402 @return metadata (lsst.daf.base.PropertyList) 405 md.add(
'RA', coord.getRa().asDegrees(),
'field center in degrees')
406 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
407 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
408 md.add(
'SMATCHV', 1,
'SourceMatchVector version number')
409 filterName =
"UNKNOWN" if filterName
is None else str(filterName)
410 md.add(
'FILTER', filterName,
'filter name for photometric data')
414 """!Relink an unpersisted match list to sources and reference objects 416 A match list is persisted and unpersisted as a catalog of IDs produced by 417 afw.table.packMatches(), with match metadata (as returned by the astrometry tasks) 418 in the catalog's metadata attribute. This method converts such a match catalog 419 into a match list (an lsst.afw.table.ReferenceMatchVector) with links to source 420 records and reference object records. 422 @param[in] matchCat Unperisted packed match list (an lsst.afw.table.BaseCatalog). 423 matchCat.table.getMetadata() must contain match metadata, 424 as returned by the astrometry tasks. 425 @param[in,out] sourceCat Source catalog (an lsst.afw.table.SourceCatalog). 426 As a side effect, the catalog will be sorted by ID. 428 @return the match list (an lsst.afw.table.ReferenceMatchVector) 430 matchmeta = matchCat.table.getMetadata()
431 version = matchmeta.getInt(
'SMATCHV')
433 raise ValueError(
'SourceMatchVector version number is %i, not 1.' % version)
434 filterName = matchmeta.getString(
'FILTER').strip()
435 ctrCoord = afwCoord.IcrsCoord(
436 matchmeta.getDouble(
'RA') * afwGeom.degrees,
437 matchmeta.getDouble(
'DEC') * afwGeom.degrees,
439 rad = matchmeta.getDouble(
'RADIUS') * afwGeom.degrees
440 refCat = self.
loadSkyCircle(ctrCoord, rad, filterName).refCat
443 return afwTable.unpackMatches(matchCat, refCat, sourceCat)
def joinMatchListWithCatalog(self, matchCat, sourceCat)
Relink an unpersisted match list to sources and reference objects.
def __init__(self, butler=None, args, kwargs)
Construct a LoadReferenceObjectsTask.
def _trimToBBox(refCat, bbox, wcs)
Remove objects outside a given pixel-based bbox and set centroid and hasCentroid fields.
def loadPixelBox(self, bbox, wcs, filterName=None, calib=None)
Load reference objects that overlap a pixel-based rectangular region.
def getRefFluxField(schema, filterName=None)
Get name of flux field in schema.
def getMetadataCircle(self, coord, radius, filterName, calib=None)
Return metadata about the load.
def getRefFluxKeys(schema, filterName=None)
Return flux and flux error keys.
def makeMinimalSchema(filterNameList, addFluxSigma=False, addIsPhotometric=False, addIsResolved=False, addIsVariable=False)
Make the standard schema for reference object catalogs.
Abstract base class to load objects from reference catalogs.
def loadSkyCircle(self, ctrCoord, radius, filterName=None)
Load reference objects that overlap a circular sky region.
def _calculateCircle(self, bbox, wcs)
Compute on-sky center and radius of search region.
def getMetadataBox(self, bbox, wcs, filterName=None, calib=None)
Return metadata about the load.