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 getRefFluxKeys
Return flux and flux error keys.
def _trimToBBox
Remove objects outside a given pixel-based bbox and set centroid and hasCentroid fields.
def loadPixelBox
Load reference objects that overlap a pixel-based rectangular region.
def loadSkyCircle
Load reference objects that overlap a circular sky region.
def __init__
Construct a LoadReferenceObjectsTask.
Abstract base class to load objects from reference catalogs.
def _calculateCircle
Compute on-sky center and radius of search region.
def getRefFluxField
Get name of flux field in schema.
def getMetadataCircle
Return metadata about the load.
def getMetadataBox
Return metadata about the load.
def joinMatchListWithCatalog
Relink an unpersisted match list to sources and reference objects.
def makeMinimalSchema
Make the standard schema for reference object catalogs.