LSSTApplications  10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
loadReferenceObjects.py
Go to the documentation of this file.
1 from __future__ import absolute_import, division, print_function
2 
3 import abc
4 
5 import numpy
6 
7 import lsst.afw.table as afwTable
8 import lsst.afw.geom as afwGeom
9 import lsst.pex.config as pexConfig
10 import lsst.pipe.base as pipeBase
11 
12 __all__ = ["getRefFluxField", "getRefFluxKeys", "LoadReferenceObjectsTask", "LoadReferenceObjectsConfig"]
13 
14 def getRefFluxField(schema, filterName=None):
15  """!Get name of flux field in schema
16 
17  if filterName is specified:
18  return *filterName*_camFlux if present
19  else return *filterName*_flux if present (camera filter name matches reference filter name)
20  else throw RuntimeError
21  else:
22  return camFlux, if present,
23  else throw RuntimeError
24 
25  @param[in] schema reference catalog schema
26  @param[in] filterName name of camera filter
27  @return flux field name
28  @throw RuntimeError if appropriate field is not found
29  """
30  if not isinstance(schema, afwTable.Schema):
31  raise RuntimeError("schema=%s is not a schema" % (schema,))
32  if filterName:
33  fluxFieldList = [filterName + "_camFlux", filterName + "_flux"]
34  else:
35  fluxFieldList = ["camFlux"]
36  for fluxField in fluxFieldList:
37  if fluxField in schema:
38  return fluxField
39 
40  raise RuntimeError("Could not find flux field(s) %s" % (", ".join(fluxFieldList)))
41 
42 def getRefFluxKeys(schema, filterName=None):
43  """!Return flux and flux error keys
44 
45  @param[in] schema reference catalog schema
46  @param[in] filterName name of camera filter
47  @return a pair of keys:
48  flux key
49  flux error key, if present, else None
50  @throw RuntimeError if flux field not found
51  """
52  fluxField = getRefFluxField(schema, filterName)
53  fluxErrField = fluxField + "Sigma"
54  fluxKey = schema[fluxField].asKey()
55  try:
56  fluxErrKey = schema[fluxErrField].asKey()
57  except Exception:
58  fluxErrKey = None
59  return (fluxKey, fluxErrKey)
60 
61 class LoadReferenceObjectsConfig(pexConfig.Config):
62  pixelMargin = pexConfig.RangeField(
63  doc = "Padding to add to 4 all edges of the bounding box (pixels)",
64  dtype = int,
65  default = 50,
66  min = 0,
67  )
68  defaultFilter = pexConfig.Field(
69  doc = "Default reference catalog filter to use if filter not specified in exposure; " + \
70  "if blank then filter must be specified in exposure",
71  dtype = str,
72  default = "",
73  )
74  filterMap = pexConfig.DictField(
75  doc = "Mapping of camera filter name: reference catalog filter name; " + \
76  "each reference filter must exist",
77  keytype = str,
78  itemtype = str,
79  default = {},
80  )
81 
82 class LoadReferenceObjectsTask(pipeBase.Task):
83  """!Abstract base class to load objects from reference catalogs
84 
85  @anchor LoadReferenceObjectsTask_
86 
87  @section meas_algorithms_loadReferenceObjects_Contents Contents
88 
89  - @ref meas_algorithms_loadReferenceObjects_Purpose
90  - @ref meas_algorithms_loadReferenceObjects_Initialize
91  - @ref meas_algorithms_loadReferenceObjects_IO
92  - @ref meas_algorithms_loadReferenceObjects_Schema
93  - @ref meas_algorithms_loadReferenceObjects_Config
94 
95  @section meas_algorithms_loadReferenceObjects_Purpose Description
96 
97  Abstract base class for tasks that load objects from a reference catalog
98  in a particular region of the sky.
99 
100  Implementations must subclass this class, override the loadSkyCircle method,
101  and will typically override the value of ConfigClass with a task-specific config class.
102 
103  @section meas_algorithms_loadReferenceObjects_Initialize Task initialisation
104 
105  @copydoc \_\_init\_\_
106 
107  @section meas_algorithms_loadReferenceObjects_IO Invoking the Task
108 
109  @copydoc loadObjectsInBBox
110 
111  @section meas_algorithms_loadReferenceObjects_Schema Schema of the reference object catalog
112 
113  Reference object catalogs are instances of lsst.afw.table.SimpleCatalog with the following schema
114  (other fields may also be present):
115  - coord: position of star on sky (an lsst.afw.coord.IcrsCoord)
116  - centroid: position of star on an exposure, if relevant (an lsst.afw.Point2D)
117  - hasCentroid: is centroid usable?
118  - *referenceFilterName*_flux: brightness in the specified reference catalog filter (Jy)
119  Note: the function lsst.afw.image.abMagFromFlux will convert flux in Jy to AB Magnitude.
120  - *referenceFilterName*_fluxSigma (optional): brightness standard deviation (Jy);
121  omitted if no data is available; possibly nan if data is available for some objects but not others
122  - *cameraFilterName*_camera_flux: brightness in specified camera filter (Jy)
123  - *cameraFilterName*_camera_fluxSigma (optional): brightness standard deviation
124  in specified camera filter (Jy); omitted if no data is available;
125  possibly nan if data is available for some objects but not others
126  - default_flux (optional): brightness to use if no camera filter is available (Jy);
127  omitted unless defaultFilter is specified in the config
128  - default_fluxSigma (optional): brightness standard deviation to use if no camera filter is available (Jy);
129  (Jy); omitted unless defaultFilter is specified in the config and the corresponding
130  fluxSigma field exists
131  - photometric (optional): is the object usable for photometric calibration?
132  - resolved (optional): is the object spatially resolved?
133  - variable (optional): does the object have variable brightness?
134 
135  @section meas_algorithms_loadReferenceObjects_Config Configuration parameters
136 
137  See @ref LoadReferenceObjectsConfig for a base set of configuration parameters.
138  Most subclasses will add configuration variables.
139  """
140  __metaclass__ = abc.ABCMeta
141  ConfigClass = LoadReferenceObjectsConfig
142  _DefaultName = "LoadReferenceObjects"
143 
144  @pipeBase.timeMethod
145  def loadPixelBox(self, bbox, wcs, filterName=None, calib=None):
146  """!Load reference objects that overlap a pixel-based rectangular region
147 
148  The search algorith works by searching in a region in sky coordinates whose center is the center
149  of the bbox and radius is large enough to just include all 4 corners of the bbox.
150  Stars that lie outside the bbox are then trimmed from the list.
151 
152  @param[in] bbox bounding box for pixels (an lsst.afw.geom.Box2I or Box2D)
153  @param[in] wcs WCS (an lsst.afw.image.Wcs)
154  @param[in] filterName name of camera filter, or None or blank for the default filter
155  @param[in] calib calibration, or None if unknown
156 
157  @return a catalog of reference objects using the standard schema (see the class doc string)
158  """
159  # compute on-sky center and radius of search region, for loadSkyCircle
160  bbox = afwGeom.Box2D(bbox) # make sure bbox is double and that we have a copy
161  bbox.grow(self.config.pixelMargin)
162  ctrCoord = wcs.pixelToSky(bbox.getCenter())
163  maxRadius = afwGeom.Angle(0)
164  for pixPt in bbox.getCorners():
165  coord = wcs.pixelToSky(pixPt)
166  rad = ctrCoord.angularSeparation(coord)
167  maxRadius = max(rad, maxRadius)
168  del rad
169 
170  # find objects in circle
171  self.log.info("Loading reference objects using center %s pix = %s sky and radius %s deg" %
172  (bbox.getCenter(), ctrCoord, maxRadius.asDegrees()))
173  loadRes = self.loadSkyCircle(ctrCoord, maxRadius, filterName)
174  refCat = loadRes.refCat
175  numFound = len(refCat)
176 
177  # trim objects outside bbox
178  refCat = self._trimToBBox(refCat=refCat, bbox=bbox, wcs=wcs)
179  numTrimmed = numFound - len(refCat)
180  self.log.logdebug("trimmed %d out-of-bbox objects, leaving %d" % (numTrimmed, len(refCat)))
181  self.log.info("Loaded %d reference objects" % (len(refCat),))
182 
183  loadRes.refCat = refCat # should be a no-op, but just in case
184  return loadRes
185 
186  @abc.abstractmethod
187  def loadSkyCircle(self, ctrCoord, radius, filterName=None):
188  """!Load reference objects that overlap a circular sky region
189 
190  @param[in] ctrCoord center of search region (an lsst.afw.geom.Coord)
191  @param[in] radius radius of search region (an lsst.afw.geom.Angle)
192  @param[in] filterName name of filter, or None for the default filter;
193  used for flux values in case we have flux limits (which are not yet implemented)
194 
195  @return an lsst.pipe.base.Struct containing:
196  - refCat a catalog of reference objects with the
197  \link meas_algorithms_loadReferenceObjects_Schema standard schema \endlink
198  as documented in LoadReferenceObjects, including photometric, resolved and variable;
199  hasCentroid is False for all objects.
200  - fluxField = name of flux field for specified filterName
201  """
202  return
203 
204  @staticmethod
205  def _trimToBBox(refCat, bbox, wcs):
206  """!Remove objects outside a given pixel-based bbox and set centroid and hasCentroid fields
207 
208  @param[in] refCat a catalog of objects (an lsst.afw.table.SimpleCatalog,
209  or other table type that supports getCoord() on records)
210  @param[in] bbox pixel region (an afwImage.Box2D)
211  @param[in] wcs WCS used to convert sky position to pixel position (an lsst.afw.math.WCS)
212 
213  @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set
214  """
215  centroidKey = afwTable.Point2DKey(refCat.schema["centroid"])
216  hasCentroidKey = refCat.schema["hasCentroid"].asKey()
217  retStarCat = type(refCat)(refCat.table)
218  for star in refCat:
219  point = wcs.skyToPixel(star.getCoord())
220  if bbox.contains(point):
221  star.set(centroidKey, point)
222  star.set(hasCentroidKey, True)
223  retStarCat.append(star)
224  return retStarCat
225 
226  def _addFluxAliases(self, schema):
227  """Add aliases for camera filter fluxes to the schema
228 
229  If self.config.defaultFilter then adds these aliases:
230  camFlux: <defaultFilter>_flux
231  camFluxSigma: <defaultFilter>_fluxSigma, if the latter exists
232 
233  For each camFilter: refFilter in self.config.filterMap adds these aliases:
234  <camFilter>_camFlux: <refFilter>_flux
235  <camFilter>_camFluxSigma: <refFilter>_fluxSigma, if the latter exists
236 
237  @throw RuntimeError if any reference flux field is missing from the schema
238  """
239  aliasMap = schema.getAliasMap()
240 
241  def addAliasesForOneFilter(filterName, refFilterName):
242  """Add aliases for a single filter
243 
244  @param[in] filterName camera filter name, or ""
245  the name is <filterName>_camFlux or camFlux if filterName is None
246  @param[in] refFilterName reference filter name; <refFilterName>_flux must exist
247  """
248  camFluxName = filterName + "_camFlux" if filterName is not None else "camFlux"
249  refFluxName = refFilterName + "_flux"
250  if refFluxName not in schema:
251  raise RuntimeError("Unknown reference filter %s" % (refFluxName,))
252  aliasMap.set(camFluxName, refFluxName)
253  refFluxErrName = refFluxName + "Sigma"
254  if refFluxErrName in schema:
255  camFluxErrName = camFluxName + "Sigma"
256  aliasMap.set(camFluxErrName, refFluxErrName)
257 
258  if self.config.defaultFilter:
259  addAliasesForOneFilter(None, self.config.defaultFilter)
260 
261  for filterName, refFilterName in self.config.filterMap.iteritems():
262  addAliasesForOneFilter(filterName, refFilterName)
263 
264  @staticmethod
265  def makeMinimalSchema(filterNameList, addFluxSigma=False,
266  addIsPhotometric=False, addIsResolved=False, addIsVariable=False):
267  """!Make the standard schema for reference object catalogs
268 
269  @param[in] filterNameList list of filter names; used to create *filterName*_flux fields
270  @param[in] addFluxSigma if True then include flux sigma fields
271  @param[in] addIsPhotometric if True add field "photometric"
272  @param[in] addIsResolved if True add field "resolved"
273  @param[in] addIsVariable if True add field "variable"
274  """
275  schema = afwTable.SimpleTable.makeMinimalSchema()
276  afwTable.Point2DKey.addFields(
277  schema,
278  "centroid",
279  "centroid on an exposure, if relevant",
280  "pixels",
281  )
282  schema.addField(
283  field = "hasCentroid",
284  type = "Flag",
285  doc = "is position known?",
286  )
287  for filterName in filterNameList:
288  schema.addField(
289  field = "%s_flux" % (filterName,),
290  type = numpy.float64,
291  doc = "flux in filter %s" % (filterName,),
292  units = "?",
293  )
294  if addFluxSigma:
295  for filterName in filterNameList:
296  schema.addField(
297  field = "%s_fluxSigma" % (filterName,),
298  type = numpy.float64,
299  doc = "flux uncertainty in filter %s" % (filterName,),
300  units = "?",
301  )
302  if addIsPhotometric:
303  schema.addField(
304  field = "photometric",
305  type = "Flag",
306  doc = "set if the object can be used for photometric calibration",
307  )
308  if addIsResolved:
309  schema.addField(
310  field = "resolved",
311  type = "Flag",
312  doc = "set if the object is spatially resolved",
313  )
314  if addIsVariable:
315  schema.addField(
316  field = "variable",
317  type = "Flag",
318  doc = "set if the object has variable brightness",
319  )
320  return schema
Defines the fields and offsets for a table.
Definition: Schema.h:46
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.
Abstract base class to load objects from reference catalogs.
def getRefFluxField
Get name of flux field in schema.
A floating-point coordinate rectangle geometry.
Definition: Box.h:271
def makeMinimalSchema
Make the standard schema for reference object catalogs.