24__all__ = [
"getRefFluxField",
"getRefFluxKeys",
"LoadReferenceObjectsTask",
"LoadReferenceObjectsConfig",
25 "ReferenceObjectLoader",
"ReferenceObjectLoaderBase"]
30from deprecated.sphinx
import deprecated
39import lsst.pipe.base
as pipeBase
40from lsst
import sphgeom
46 """Return True if this name/units combination corresponds to an
47 "old-style" reference catalog flux field.
49 unitsCheck = units != 'nJy'
50 isFlux = name.endswith(
'_flux')
51 isFluxSigma = name.endswith(
'_fluxSigma')
52 isFluxErr = name.endswith(
'_fluxErr')
53 return (isFlux
or isFluxSigma
or isFluxErr)
and unitsCheck
58 """Return True if the units of all flux and fluxErr are correct (nJy).
67 """"Return the format version stored in a reference catalog header.
72 Reference catalog to inspect.
77 Format verison integer. Returns `0` if the catalog has no metadata
78 or the metadata does
not include a
"REFCAT_FORMAT_VERSION" key.
82 deprecation_msg =
"Support for version 0 refcats (pre-nJy fluxes) will be removed after v25."
83 md = refCat.getMetadata()
85 warnings.warn(deprecation_msg)
88 return md.getScalar(
"REFCAT_FORMAT_VERSION")
90 warnings.warn(deprecation_msg)
95@deprecated(reason="Support for version 0 refcats (pre-nJy fluxes) will be removed after v25.
",
96 version="v24.0", category=FutureWarning)
98 """Convert fluxes in a catalog from jansky to nanojansky.
103 The catalog to convert.
105 Log to send messages to.
106 doConvert : `bool`, optional
107 Return a converted catalog,
or just identify the fields that need to be converted?
108 This supports the
"write=False" mode of `bin/convert_to_nJy.py`.
113 The converted catalog,
or None if ``doConvert``
is False.
117 Support
for old units
in reference catalogs will be removed after the
118 release of late calendar year 2019.
119 Use `meas_algorithms/bin/convert_to_nJy.py` to update your reference catalog.
123 mapper = afwTable.SchemaMapper(catalog.schema, shareAliasMap=
False)
124 mapper.addMinimalSchema(afwTable.SimpleTable.makeMinimalSchema())
127 for field
in catalog.schema:
128 oldName = field.field.getName()
129 oldUnits = field.field.getUnits()
133 if oldName.endswith(
'_fluxSigma'):
134 name = oldName.replace(
'_fluxSigma',
'_fluxErr')
137 newField = afwTable.Field[field.dtype](name, field.field.getDoc(), units)
138 mapper.addMapping(field.getKey(), newField)
139 input_fields.append(field.field)
140 output_fields.append(newField)
142 mapper.addMapping(field.getKey())
144 fluxFieldsStr =
'; '.join(
"(%s, '%s')" % (field.getName(), field.getUnits())
for field
in input_fields)
147 newSchema = mapper.getOutputSchema()
148 output = afwTable.SimpleCatalog(newSchema)
149 output.reserve(len(catalog))
150 output.extend(catalog, mapper=mapper)
151 for field
in output_fields:
152 output[field.getName()] *= 1e9
153 log.info(
"Converted refcat flux fields to nJy (name, units): %s", fluxFieldsStr)
156 log.info(
"Found old-style refcat flux fields (name, units): %s", fluxFieldsStr)
161 """This is a private helper class which filters catalogs by
162 row based on the row being inside the region used to initialize
168 The spatial region which all objects should lie within
174 """This call method on an instance of this class takes in a reference
175 catalog, and the region
from which the catalog was generated.
177 If the catalog region
is entirely contained within the region used to
178 initialize this
class, then all the entries
in the catalog must be
179 within the region
and so the whole catalog
is returned.
181 If the catalog region
is not entirely contained, then the location
for
182 each record
is tested against the region used to initialize the
class.
183 Records which fall inside this region are added to a new catalog,
and
184 this catalog
is then returned.
189 SourceCatalog to be filtered.
191 Region
in which the catalog was created
193 if catRegion.isWithin(self.
region):
197 filteredRefCat = type(refCat)(refCat.table)
198 for record
in refCat:
199 if self.
region.contains(record.getCoord().getVector()):
200 filteredRefCat.append(record)
201 return filteredRefCat
205 pixelMargin = pexConfig.RangeField(
206 doc=
"Padding to add to 4 all edges of the bounding box (pixels)",
211 anyFilterMapsToThis = pexConfig.Field(
212 doc=(
"Always use this reference catalog filter, no matter whether or what filter name is "
213 "supplied to the loader. Effectively a trivial filterMap: map all filter names to this filter."
214 " This can be set for purely-astrometric catalogs (e.g. Gaia DR2) where there is only one "
215 "reasonable choice for every camera filter->refcat mapping, but not for refcats used for "
216 "photometry, which need a filterMap and/or colorterms/transmission corrections."),
221 filterMap = pexConfig.DictField(
222 doc=(
"Mapping of camera filter name: reference catalog filter name; "
223 "each reference filter must exist in the refcat."
224 " Note that this does not perform any bandpass corrections: it is just a lookup."),
229 requireProperMotion = pexConfig.Field(
230 doc=
"Require that the fields needed to correct proper motion "
231 "(epoch, pm_ra and pm_dec) are present?",
235 ref_dataset_name = pexConfig.Field(
236 doc=
"Deprecated; do not use. Added for easier transition from LoadIndexedReferenceObjectsConfig to "
237 "LoadReferenceObjectsConfig",
240 deprecated=
'This field is not used. It will be removed after v25.',
246 msg =
"`filterMap` and `anyFilterMapsToThis` are mutually exclusive"
247 raise pexConfig.FieldValidationError(LoadReferenceObjectsConfig.anyFilterMapsToThis,
252 """This class facilitates loading reference catalogs.
254 The QuantumGraph generation will create a list of datasets that may
255 possibly overlap a given region. These datasets are then used to construct
256 an instance of this class. The
class instance should then be passed into
257 a task which needs reference catalogs. These tasks should then determine
258 the exact region of the sky reference catalogs will be loaded
for,
and
259 call a corresponding method to load the reference objects.
263 dataIds : iterable of `lsst.daf.butler.DataCoordinate`
264 An iterable object of data IDs that point to reference catalogs.
265 refCats : iterable of `lsst.daf.butler.DeferredDatasetHandle`
266 Handles to load refCats on demand.
267 name : `str`, optional
268 The name of the refcat that this object will load. This name
is used
269 for applying colorterms,
for example.
270 config : `LoadReferenceObjectsConfig`
271 Configuration of this reference loader.
272 log : `
lsst.log.Log`, `logging.Logger`
or `
None`, optional
273 Logger object used to write out messages. If `
None` a default
276 ConfigClass = LoadReferenceObjectsConfig
278 def __init__(self, dataIds, refCats, name=None, log=None, config=None, **kwargs):
280 warnings.warn(
"Instantiating ReferenceObjectLoader with additional kwargs is deprecated "
281 "and will be removed after v25.0", FutureWarning, stacklevel=2)
289 self.
log = log
or logging.getLogger(__name__).getChild(
"ReferenceObjectLoader")
292 """Apply proper motion correction to a reference catalog.
294 Adjust position and position error
in the ``catalog``
295 for proper motion to the specified ``epoch``,
296 modifying the catalog
in place.
301 Catalog of positions, containing at least these fields:
303 - Coordinates, retrieved by the table
's coordinate key.
304 - ``coord_raErr`` : Error in Right Ascension (rad).
305 - ``coord_decErr`` : Error
in Declination (rad).
306 - ``pm_ra`` : Proper motion
in Right Ascension (rad/yr,
308 - ``pm_raErr`` : Error
in ``pm_ra`` (rad/yr), optional.
309 - ``pm_dec`` : Proper motion
in Declination (rad/yr,
311 - ``pm_decErr`` : Error
in ``pm_dec`` (rad/yr), optional.
312 - ``epoch`` : Mean epoch of object (an astropy.time.Time)
313 epoch : `astropy.time.Time`
314 Epoch to which to correct proper motion.
315 If
None, do
not apply PM corrections
or raise if
316 ``config.requireProperMotion``
is True.
321 Raised
if ``config.requireProperMotion``
is set but we cannot
322 apply the proper motion correction
for some reason.
325 if self.
config.requireProperMotion:
326 raise RuntimeError(
"requireProperMotion=True but epoch not provided to loader.")
328 self.
log.debug(
"No epoch provided: not applying proper motion corrections to refcat.")
332 if (
"pm_ra" in catalog.schema
333 and not isinstance(catalog.schema[
"pm_ra"].asKey(), afwTable.KeyAngle)):
334 if self.
config.requireProperMotion:
335 raise RuntimeError(
"requireProperMotion=True but refcat pm_ra field is not an Angle.")
337 self.
log.warning(
"Reference catalog pm_ra field is not an Angle; cannot apply proper motion.")
340 if (
"epoch" not in catalog.schema
or "pm_ra" not in catalog.schema):
341 if self.
config.requireProperMotion:
342 raise RuntimeError(
"requireProperMotion=True but PM data not available from catalog.")
344 self.
log.warning(
"Proper motion correction not available for this reference catalog.")
351 addIsPhotometric=False, addIsResolved=False,
352 addIsVariable=False, coordErrDim=2,
353 addProperMotion=False, properMotionErrDim=2,
355 """Make a standard schema for reference object catalogs.
359 filterNameList : `list` of `str`
360 List of filter names. Used to create <filterName>_flux fields.
361 addIsPhotometric : `bool`
362 If True then add field
"photometric".
363 addIsResolved : `bool`
364 If
True then add field
"resolved".
365 addIsVariable : `bool`
366 If
True then add field
"variable".
368 Number of coord error fields; must be one of 0, 2, 3:
370 - If 2
or 3: add fields
"coord_raErr" and "coord_decErr".
371 - If 3: also add field
"coord_radecErr".
372 addProperMotion : `bool`
373 If
True add fields
"epoch",
"pm_ra",
"pm_dec" and "pm_flag".
374 properMotionErrDim : `int`
375 Number of proper motion error fields; must be one of 0, 2, 3;
376 ignored
if addProperMotion false:
377 - If 2
or 3: add fields
"pm_raErr" and "pm_decErr".
378 - If 3: also add field
"pm_radecErr".
380 If
True add fields
"epoch",
"parallax",
"parallaxErr"
386 Schema
for reference catalog, an
391 Reference catalogs support additional covariances, such
as
392 covariance between RA
and proper motion
in declination,
393 that are
not supported by this method, but can be added after
396 schema = afwTable.SimpleTable.makeMinimalSchema()
398 afwTable.Point2DKey.addFields(
401 "centroid on an exposure, if relevant",
407 doc=
"is position known?",
409 for filterName
in filterNameList:
411 field=
"%s_flux" % (filterName,),
413 doc=
"flux in filter %s" % (filterName,),
416 for filterName
in filterNameList:
418 field=
"%s_fluxErr" % (filterName,),
420 doc=
"flux uncertainty in filter %s" % (filterName,),
427 doc=
"set if the object can be used for photometric calibration",
433 doc=
"set if the object is spatially resolved",
439 doc=
"set if the object has variable brightness",
441 if coordErrDim
not in (0, 2, 3):
442 raise ValueError(
"coordErrDim={}; must be (0, 2, 3)".format(coordErrDim))
444 afwTable.CovarianceMatrix2fKey.addFields(
448 units=[
"rad",
"rad"],
449 diagonalOnly=(coordErrDim == 2),
452 if addProperMotion
or addParallax:
456 doc=
"date of observation (TAI, MJD)",
464 doc=
"proper motion in the right ascension direction = dra/dt * cos(dec)",
470 doc=
"proper motion in the declination direction",
473 if properMotionErrDim
not in (0, 2, 3):
474 raise ValueError(
"properMotionErrDim={}; must be (0, 2, 3)".format(properMotionErrDim))
475 if properMotionErrDim > 0:
476 afwTable.CovarianceMatrix2fKey.addFields(
480 units=[
"rad/year",
"rad/year"],
481 diagonalOnly=(properMotionErrDim == 2),
486 doc=
"Set if proper motion or proper motion error is bad",
499 doc=
"uncertainty in parallax",
503 field=
"parallax_flag",
505 doc=
"Set if parallax or parallax error is bad",
510 def _remapReferenceCatalogSchema(refCat, *, anyFilterMapsToThis=None,
511 filterMap=None, centroids=False):
512 """This function takes in a reference catalog and returns a new catalog
513 with additional columns defined
from the remaining function arguments.
518 Reference catalog to map to new catalog
519 anyFilterMapsToThis : `str`, optional
520 Always use this reference catalog filter.
521 Mutually exclusive
with `filterMap`
522 filterMap : `dict` [`str`,`str`], optional
523 Mapping of camera filter name: reference catalog filter name.
524 centroids : `bool`, optional
525 Add centroid fields to the loaded Schema. ``loadPixelBox`` expects
526 these fields to exist.
531 Deep copy of input reference catalog
with additional columns added
533 if anyFilterMapsToThis
or filterMap:
534 ReferenceObjectLoader._addFluxAliases(refCat.schema, anyFilterMapsToThis, filterMap)
536 mapper = afwTable.SchemaMapper(refCat.schema,
True)
537 mapper.addMinimalSchema(refCat.schema,
True)
538 mapper.editOutputSchema().disconnectAliases()
545 mapper.editOutputSchema().addField(
"centroid_x", type=float, doReplace=
True)
546 mapper.editOutputSchema().addField(
"centroid_y", type=float, doReplace=
True)
547 mapper.editOutputSchema().addField(
"hasCentroid", type=
"Flag", doReplace=
True)
548 mapper.editOutputSchema().getAliasMap().set(
"slot_Centroid",
"centroid")
550 expandedCat = afwTable.SimpleCatalog(mapper.getOutputSchema())
551 expandedCat.setMetadata(refCat.getMetadata())
552 expandedCat.extend(refCat, mapper=mapper)
557 def _addFluxAliases(schema, anyFilterMapsToThis=None, filterMap=None):
558 """Add aliases for camera filter fluxes to the schema.
560 For each camFilter: refFilter in filterMap, adds these aliases:
561 <camFilter>_camFlux: <refFilter>_flux
562 <camFilter>_camFluxErr: <refFilter>_fluxErr,
if the latter exists
563 or sets `anyFilterMapsToThis`
in the schema.
568 Schema
for reference catalog.
569 anyFilterMapsToThis : `str`, optional
570 Always use this reference catalog filter.
571 Mutually exclusive
with `filterMap`.
572 filterMap : `dict` [`str`,`str`], optional
573 Mapping of camera filter name: reference catalog filter name.
574 Mutually exclusive
with `anyFilterMapsToThis`.
579 Raised
if any required reference flux field
is missing
from the
583 if anyFilterMapsToThis
and filterMap:
584 raise ValueError(
"anyFilterMapsToThis and filterMap are mutually exclusive!")
586 aliasMap = schema.getAliasMap()
588 if anyFilterMapsToThis
is not None:
589 refFluxName = anyFilterMapsToThis +
"_flux"
590 if refFluxName
not in schema:
591 msg = f
"Unknown reference filter for anyFilterMapsToThis='{refFluxName}'"
592 raise RuntimeError(msg)
593 aliasMap.set(
"anyFilterMapsToThis", refFluxName)
596 def addAliasesForOneFilter(filterName, refFilterName):
597 """Add aliases for a single filter
601 filterName : `str` (optional)
602 Camera filter name. The resulting alias name is
604 refFilterName : `str`
605 Reference catalog filter name; the field
606 <refFilterName>_flux must exist.
608 camFluxName = filterName + "_camFlux"
609 refFluxName = refFilterName +
"_flux"
610 if refFluxName
not in schema:
611 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
612 aliasMap.set(camFluxName, refFluxName)
613 refFluxErrName = refFluxName +
"Err"
614 if refFluxErrName
in schema:
615 camFluxErrName = camFluxName +
"Err"
616 aliasMap.set(camFluxErrName, refFluxErrName)
618 if filterMap
is not None:
619 for filterName, refFilterName
in filterMap.items():
620 addAliasesForOneFilter(filterName, refFilterName)
623 def _makeBoxRegion(BBox, wcs, BBoxPadding):
636 outerLocalBBox.grow(BBoxPadding)
637 innerLocalBBox.grow(-1*BBoxPadding)
649 innerBoxCorners = innerLocalBBox.getCorners()
650 innerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in innerBoxCorners]
653 outerBoxCorners = outerLocalBBox.getCorners()
654 outerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in outerBoxCorners]
657 return innerSkyRegion, outerSkyRegion, innerSphCorners, outerSphCorners
660 def _calculateCircle(bbox, wcs, pixelMargin):
661 """Compute on-sky center and radius of search region.
668 WCS; used to convert pixel positions to sky coordinates.
670 Padding to add to 4 all edges of the bounding box (pixels).
674 results : `lsst.pipe.base.Struct`
678 ICRS center of the search region.
680 Radius of the search region.
682 Bounding box used to compute the circle.
685 bbox.grow(pixelMargin)
686 coord = wcs.pixelToSky(bbox.getCenter())
687 radius = max(coord.separation(wcs.pixelToSky(pp))
for pp
in bbox.getCorners())
688 return pipeBase.Struct(coord=coord, radius=radius, bbox=bbox)
692 """Return metadata about the reference catalog being loaded.
694 This metadata is used
for reloading the catalog (e.g.
for
695 reconstituting a normalized match list).
700 ICRS center of the search region.
702 Radius of the search region.
704 Name of the camera filter.
705 epoch : `astropy.time.Time`
or `
None`, optional
706 Epoch to which to correct proper motion
and parallax,
or `
None` to
707 not apply such corrections.
712 Metadata about the catalog.
715 md.add('RA', coord.getRa().asDegrees(),
'field center in degrees')
716 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
717 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
718 md.add(
'SMATCHV', 1,
'SourceMatchVector version number')
719 md.add(
'FILTER', filterName,
'filter name for photometric data')
720 md.add(
'EPOCH',
"NONE" if epoch
is None else epoch.mjd,
'Epoch (TAI MJD) for catalog')
724 bboxToSpherePadding=100):
725 """Return metadata about the load
727 This metadata is used
for reloading the catalog (e.g.,
for
728 reconstituting a normalised match list).
733 Bounding box
for the pixels.
735 The WCS object associated
with ``bbox``.
737 Name of the camera filter.
738 epoch : `astropy.time.Time`
or `
None`, optional
739 Epoch to which to correct proper motion
and parallax,
or `
None` to
740 not apply such corrections.
741 bboxToSpherePadding : `int`, optional
742 Padding
in pixels to account
for translating a set of corners into
743 a spherical (convex) boundary that
is certain to encompass the
744 enitre area covered by the box.
749 The metadata detailing the search parameters used
for this
753 md = self.getMetadataCircle(circle.coord, circle.radius, filterName, epoch=epoch)
755 paddedBbox = circle.bbox
756 _, _, innerCorners, outerCorners = self._makeBoxRegion(paddedBbox, wcs, bboxToSpherePadding)
757 for box, corners
in zip((
"INNER",
"OUTER"), (innerCorners, outerCorners)):
758 for (name, corner)
in zip((
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"),
760 md.add(f
"{box}_{name}_RA",
geom.SpherePoint(corner).getRa().asDegrees(), f
"{box}_corner")
761 md.add(f
"{box}_{name}_DEC",
geom.SpherePoint(corner).getDec().asDegrees(), f
"{box}_corner")
765 """Relink an unpersisted match list to sources and reference objects.
767 A match list is persisted
and unpersisted
as a catalog of IDs
768 produced by afw.table.packMatches(),
with match metadata
769 (
as returned by the astrometry tasks)
in the catalog
's metadata
770 attribute. This method converts such a match catalog into a match
771 list, with links to source records
and reference object records.
776 Unpersisted packed match list.
777 ``matchCat.table.getMetadata()`` must contain match metadata,
778 as returned by the astrometry tasks.
780 Source catalog. As a side effect, the catalog will be sorted
791 bboxToSpherePadding=100):
792 """Load reference objects that are within a pixel-based rectangular
795 This algorithm works by creating a spherical box whose corners
796 correspond to the WCS converted corners of the input bounding box
797 (possibly padded). It then defines a filtering function which looks at
798 the pixel position of the reference objects and accepts only those that
799 lie within the specified bounding box.
801 The spherical box region
and filtering function are passed to the
802 generic loadRegion method which loads
and filters the reference objects
803 from the datastore
and returns a single catalog containing the filtered
804 set of reference objects.
809 Box which bounds a region
in pixel space.
811 Wcs object defining the pixel to sky (
and inverse) transform
for
812 the supplied ``bbox``.
814 Name of camera filter.
815 epoch : `astropy.time.Time`
or `
None`, optional
816 Epoch to which to correct proper motion
and parallax,
or `
None`
817 to
not apply such corrections.
818 bboxToSpherePadding : `int`, optional
819 Padding to account
for translating a set of corners into a
820 spherical (convex) boundary that
is certain to encompase the
821 enitre area covered by the box.
825 output : `lsst.pipe.base.Struct`
826 Results struct
with attributes:
829 Catalog containing reference objects inside the specified
830 bounding box (padded by self.
config.pixelMargin).
832 Name of the field containing the flux associated
with
838 Raised
if no reference catalogs could be found
for the specified
841 Raised
if the loaded reference catalogs do
not have matching
845 paddedBbox.grow(self.config.pixelMargin)
846 innerSkyRegion, outerSkyRegion, _, _ = self._makeBoxRegion(paddedBbox, wcs, bboxToSpherePadding)
848 def _filterFunction(refCat, region):
859 refCat = preFiltFunc(refCat, region)
865 afwTable.updateRefCentroids(wcs, refCat)
868 if innerSkyRegion.contains(region):
872 filteredRefCat = type(refCat)(refCat.table)
873 centroidKey = afwTable.Point2DKey(refCat.schema[
'centroid'])
874 for record
in refCat:
875 pixCoords = record[centroidKey]
877 filteredRefCat.append(record)
878 return filteredRefCat
879 return self.
loadRegion(outerSkyRegion, filterName, filtFunc=_filterFunction, epoch=epoch)
881 def loadRegion(self, region, filterName, filtFunc=None, epoch=None):
882 """Load reference objects within a specified region.
884 This function loads the DataIds used to construct an instance of this
885 class which intersect or are contained within the specified region. The
886 reference catalogs which intersect but are
not fully contained within
887 the input region are further filtered by the specified filter function.
888 This function returns a single source catalog containing all reference
889 objects inside the specified region.
895 should define the spatial region
for which reference objects are to
897 filtFunc : callable
or `
None`, optional
898 This optional parameter should be a callable object that takes a
899 reference catalog
and its corresponding region
as parameters,
900 filters the catalog by some criteria
and returns the filtered
901 reference catalog. If `
None`, an internal filter function
is used
902 which filters according to
if a reference object falls within the
905 Name of camera filter.
906 epoch : `astropy.time.Time`
or `
None`, optional
907 Epoch to which to correct proper motion
and parallax,
or `
None` to
908 not apply such corrections.
912 output : `lsst.pipe.base.Struct`
913 Results struct
with attributes:
916 Catalog containing reference objects which intersect the
917 input region, filtered by the specified filter function.
919 Name of the field containing the flux associated
with
925 Raised
if no reference catalogs could be found
for the specified
928 Raised
if the loaded reference catalogs do
not have matching
931 regionLat = region.getBoundingBox().getLat()
932 regionLon = region.getBoundingBox().getLon()
933 self.log.info("Loading reference objects from %s in region bounded by "
934 "[%.8f, %.8f], [%.8f, %.8f] RA Dec",
936 self.
refCats[0].ref.datasetType.name,
937 regionLon.getA().asDegrees(), regionLon.getB().asDegrees(),
938 regionLat.getA().asDegrees(), regionLat.getB().asDegrees())
947 intersects = dataId.region.intersects(region)
949 intersects = region.intersects(dataId.region)
952 overlapList.append((dataId, refCat))
954 if len(overlapList) == 0:
955 raise RuntimeError(
"No reference tables could be found for input region")
957 firstCat = overlapList[0][1].get()
958 refCat = filtFunc(firstCat, overlapList[0][0].region)
959 trimmedAmount = len(firstCat) - len(refCat)
962 for dataId, inputRefCat
in overlapList[1:]:
963 tmpCat = inputRefCat.get()
965 if tmpCat.schema != firstCat.schema:
966 raise TypeError(
"Reference catalogs have mismatching schemas")
968 filteredCat = filtFunc(tmpCat, dataId.region)
969 refCat.extend(filteredCat)
970 trimmedAmount += len(tmpCat) - len(filteredCat)
972 self.
log.debug(
"Trimmed %d refCat objects lying outside padded region, leaving %d",
973 trimmedAmount, len(refCat))
974 self.
log.info(
"Loaded %d reference objects", len(refCat))
977 if not refCat.isContiguous():
978 refCat = refCat.copy(deep=
True)
986 self.
log.warning(
"Found version 0 reference catalog with old style units in schema.")
987 self.
log.warning(
"run `meas_algorithms/bin/convert_refcat_to_nJy.py` to convert fluxes to nJy.")
988 self.
log.warning(
"See RFC-575 for more details.")
992 anyFilterMapsToThis=self.
config.anyFilterMapsToThis,
993 filterMap=self.
config.filterMap)
996 if not expandedCat.isContiguous():
997 expandedCat = expandedCat.copy(deep=
True)
1000 return pipeBase.Struct(refCat=expandedCat, fluxField=fluxField)
1003 """Load reference objects that lie within a circular region on the sky.
1005 This method constructs a circular region from an input center
and
1006 angular radius, loads reference catalogs which are contained
in or
1007 intersect the circle,
and filters reference catalogs which intersect
1008 down to objects which lie within the defined circle.
1013 Point defining the center of the circular region.
1015 Defines the angular radius of the circular region.
1017 Name of camera filter.
1018 epoch : `astropy.time.Time`
or `
None`, optional
1019 Epoch to which to correct proper motion
and parallax,
or `
None` to
1020 not apply such corrections.
1024 output : `lsst.pipe.base.Struct`
1025 Results struct
with attributes:
1028 Catalog containing reference objects inside the specified
1031 Name of the field containing the flux associated
with
1034 centerVector = ctrCoord.getVector()
1037 return self.
loadRegion(circularRegion, filterName, epoch=epoch)
1041 """Get the name of a flux field from a schema.
1046 Reference catalog schema.
1048 Name of camera filter.
1052 fluxFieldName : `str`
1057 Return the alias of ``anyFilterMapsToThis``, if present
1058 else,
return ``*filterName*_camFlux``
if present,
1059 else,
return ``*filterName*_flux``
if present (camera filter name
1060 matches reference filter name),
else raise an exception.
1065 Raised
if an appropriate field
is not found.
1067 if not isinstance(schema, afwTable.Schema):
1068 raise RuntimeError(
"schema=%s is not a schema" % (schema,))
1070 return schema.getAliasMap().get(
"anyFilterMapsToThis")
1074 fluxFieldList = [filterName +
"_camFlux", filterName +
"_flux"]
1075 for fluxField
in fluxFieldList:
1076 if fluxField
in schema:
1079 raise RuntimeError(
"Could not find flux field(s) %s" % (
", ".join(fluxFieldList)))
1083 """Return keys for flux and flux error.
1088 Reference catalog schema.
1090 Name of camera filter.
1098 - flux error key, if present,
else None
1103 If flux field
not found.
1106 fluxErrField = fluxField + "Err"
1107 fluxKey = schema[fluxField].asKey()
1109 fluxErrKey = schema[fluxErrField].asKey()
1112 return (fluxKey, fluxErrKey)
1115@deprecated(reason=(
"This task is used in gen2 only; it will be removed after v25. "
1116 "See DM-35671 for details on updating code to avoid this warning."),
1117 version=
"v25.0", category=FutureWarning)
1119 """Abstract gen2 base class to load objects from reference catalogs.
1121 _DefaultName = "LoadReferenceObjects"
1124 def loadSkyCircle(self, ctrCoord, radius, filterName, epoch=None, centroids=False):
1125 """Load reference objects that overlap a circular sky region.
1130 ICRS center of search region.
1132 Radius of search region.
1134 Name of filter. This can be used for flux limit comparisons.
1135 epoch : `astropy.time.Time`
or `
None`, optional
1136 Epoch to which to correct proper motion
and parallax,
or `
None` to
1137 not apply such corrections.
1138 centroids : `bool`, optional
1139 Add centroid fields to the loaded Schema. ``loadPixelBox`` expects
1140 these fields to exist.
1144 results : `lsst.pipe.base.Struct`
1145 A `~lsst.pipe.base.Struct` containing the following fields:
1148 A catalog of reference objects
with the standard
1149 schema,
as documented
in the main doc string
for
1150 `LoadReferenceObjects`.
1151 The catalog
is guaranteed to be contiguous.
1152 (`lsst.afw.catalog.SimpleCatalog`)
1154 Name of flux field
for specified `filterName`. (`str`)
1158 Note that subclasses are responsible
for performing the proper motion
1159 correction, since this
is the lowest-level interface
for retrieving
1166 """Relink an unpersisted match list to sources and reference
1169 A match list is persisted
and unpersisted
as a catalog of IDs
1170 produced by afw.table.packMatches(),
with match metadata
1171 (
as returned by the astrometry tasks)
in the catalog
's metadata
1172 attribute. This method converts such a match catalog into a match
1173 list, with links to source records
and reference object records.
1178 Reference object loader to use
in getting reference objects
1180 Unperisted packed match list.
1181 ``matchCat.table.getMetadata()`` must contain match metadata,
1182 as returned by the astrometry tasks.
1184 Source catalog. As a side effect, the catalog will be sorted
1192 matchmeta = matchCat.table.getMetadata()
1193 version = matchmeta.getInt('SMATCHV')
1195 raise ValueError(
'SourceMatchVector version number is %i, not 1.' % version)
1196 filterName = matchmeta.getString(
'FILTER').strip()
1198 epoch = matchmeta.getDouble(
'EPOCH')
1199 except (LookupError, TypeError):
1201 if 'RADIUS' in matchmeta:
1204 matchmeta.getDouble(
'DEC'), geom.degrees)
1205 rad = matchmeta.getDouble(
'RADIUS')*geom.degrees
1206 refCat = refObjLoader.loadSkyCircle(ctrCoord, rad, filterName, epoch=epoch).refCat
1207 elif "INNER_UPPER_LEFT_RA" in matchmeta:
1213 for place
in (
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"):
1215 matchmeta.getDouble(f
"OUTER_{place}_DEC"),
1216 geom.degrees).getVector()
1219 refCat = refObjLoader.loadRegion(outerBox, filterName, epoch=epoch).refCat
1223 return afwTable.unpackMatches(matchCat, refCat, sourceCat)
1226@deprecated(reason=
"Base class only used for gen2 interface, and will be removed after v25.0. "
1227 "Please use ReferenceObjectLoader directly.",
1228 version=
"v25.0", category=FutureWarning)
1230 """Stub of a deprecated class.
1234 config : `lsst.pex.config.Config`
1235 Configuration for the loader.
1242 """Apply proper motion correction to a reference catalog.
1244 Adjust position and position error
in the ``catalog``
1245 for proper motion to the specified ``epoch``,
1246 modifying the catalog
in place.
1251 Log object to write to.
1253 Catalog of positions, containing:
1255 - Coordinates, retrieved by the table
's coordinate key.
1256 - ``coord_raErr`` : Error in Right Ascension (rad).
1257 - ``coord_decErr`` : Error
in Declination (rad).
1258 - ``pm_ra`` : Proper motion
in Right Ascension (rad/yr,
1260 - ``pm_raErr`` : Error
in ``pm_ra`` (rad/yr), optional.
1261 - ``pm_dec`` : Proper motion
in Declination (rad/yr,
1263 - ``pm_decErr`` : Error
in ``pm_dec`` (rad/yr), optional.
1264 - ``epoch`` : Mean epoch of object (an astropy.time.Time)
1265 epoch : `astropy.time.Time`
1266 Epoch to which to correct proper motion.
1268 if "epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema:
1269 log.warning(
"Proper motion correction not available from catalog")
1271 if not catalog.isContiguous():
1272 raise RuntimeError(
"Catalog must be contiguous")
1273 catEpoch = astropy.time.Time(catalog[
"epoch"], scale=
"tai", format=
"mjd")
1274 log.info(
"Correcting reference catalog for proper motion to %r", epoch)
1276 timeDiffsYears = (epoch.tai - catEpoch).to(astropy.units.yr).value
1277 coordKey = catalog.table.getCoordKey()
1280 pmRaRad = catalog[
"pm_ra"]
1281 pmDecRad = catalog[
"pm_dec"]
1282 offsetsRaRad = pmRaRad*timeDiffsYears
1283 offsetsDecRad = pmDecRad*timeDiffsYears
1291 offsetBearingsRad = numpy.arctan2(pmDecRad*1e6, pmRaRad*1e6)
1292 offsetAmountsRad = numpy.hypot(offsetsRaRad, offsetsDecRad)
1293 for record, bearingRad, amountRad
in zip(catalog, offsetBearingsRad, offsetAmountsRad):
1294 record.set(coordKey,
1295 record.get(coordKey).offset(bearing=bearingRad*geom.radians,
1296 amount=amountRad*geom.radians))
1298 if "coord_raErr" in catalog.schema:
1299 catalog[
"coord_raErr"] = numpy.hypot(catalog[
"coord_raErr"],
1300 catalog[
"pm_raErr"]*timeDiffsYears)
1301 if "coord_decErr" in catalog.schema:
1302 catalog[
"coord_decErr"] = numpy.hypot(catalog[
"coord_decErr"],
1303 catalog[
"pm_decErr"]*timeDiffsYears)
def __call__(self, refCat, catRegion)
def __init__(self, region)
def loadSkyCircle(self, ctrCoord, radius, filterName, epoch=None, centroids=False)
def __init__(self, config=None, *args, **kwargs)
def _calculateCircle(bbox, wcs, pixelMargin)
def __init__(self, dataIds, refCats, name=None, log=None, config=None, **kwargs)
def _makeBoxRegion(BBox, wcs, BBoxPadding)
def applyProperMotions(self, catalog, epoch)
def loadSkyCircle(self, ctrCoord, radius, filterName, epoch=None)
def _remapReferenceCatalogSchema(refCat, *anyFilterMapsToThis=None, filterMap=None, centroids=False)
def loadRegion(self, region, filterName, filtFunc=None, epoch=None)
def getMetadataBox(self, bbox, wcs, filterName, epoch=None, bboxToSpherePadding=100)
def joinMatchListWithCatalog(self, matchCat, sourceCat)
def loadPixelBox(self, bbox, wcs, filterName, epoch=None, bboxToSpherePadding=100)
def makeMinimalSchema(filterNameList, *addCentroid=False, addIsPhotometric=False, addIsResolved=False, addIsVariable=False, coordErrDim=2, addProperMotion=False, properMotionErrDim=2, addParallax=False)
def getMetadataCircle(coord, radius, filterName, epoch=None)
def hasNanojanskyFluxUnits(schema)
def getRefFluxKeys(schema, filterName)
def convertToNanojansky(catalog, log, doConvert=True)
def joinMatchListWithCatalogImpl(refObjLoader, matchCat, sourceCat)
def isOldFluxField(name, units)
def getRefFluxField(schema, filterName)
def getFormatVersionFromRefCat(refCat)
def applyProperMotionsImpl(log, catalog, epoch)