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 log : `
lsst.log.Log`, `logging.Logger`
or `
None`, optional
268 Logger object used to write out messages. If `
None` a default
271 ConfigClass = LoadReferenceObjectsConfig
273 def __init__(self, dataIds, refCats, log=None, config=None, **kwargs):
275 warnings.warn(
"Instantiating ReferenceObjectLoader with additional kwargs is deprecated "
276 "and will be removed after v25.0", FutureWarning, stacklevel=2)
283 self.
log = log
or logging.getLogger(__name__).getChild(
"ReferenceObjectLoader")
286 """Apply proper motion correction to a reference catalog.
288 Adjust position and position error
in the ``catalog``
289 for proper motion to the specified ``epoch``,
290 modifying the catalog
in place.
295 Catalog of positions, containing at least these fields:
297 - Coordinates, retrieved by the table
's coordinate key.
298 - ``coord_raErr`` : Error in Right Ascension (rad).
299 - ``coord_decErr`` : Error
in Declination (rad).
300 - ``pm_ra`` : Proper motion
in Right Ascension (rad/yr,
302 - ``pm_raErr`` : Error
in ``pm_ra`` (rad/yr), optional.
303 - ``pm_dec`` : Proper motion
in Declination (rad/yr,
305 - ``pm_decErr`` : Error
in ``pm_dec`` (rad/yr), optional.
306 - ``epoch`` : Mean epoch of object (an astropy.time.Time)
307 epoch : `astropy.time.Time`
308 Epoch to which to correct proper motion.
309 If
None, do
not apply PM corrections
or raise if
310 ``config.requireProperMotion``
is True.
315 Raised
if ``config.requireProperMotion``
is set but we cannot
316 apply the proper motion correction
for some reason.
319 if self.
config.requireProperMotion:
320 raise RuntimeError(
"requireProperMotion=True but epoch not provided to loader.")
322 self.
log.debug(
"No epoch provided: not applying proper motion corrections to refcat.")
326 if (
"pm_ra" in catalog.schema
327 and not isinstance(catalog.schema[
"pm_ra"].asKey(), afwTable.KeyAngle)):
328 if self.
config.requireProperMotion:
329 raise RuntimeError(
"requireProperMotion=True but refcat pm_ra field is not an Angle.")
331 self.
log.warning(
"Reference catalog pm_ra field is not an Angle; cannot apply proper motion.")
334 if (
"epoch" not in catalog.schema
or "pm_ra" not in catalog.schema):
335 if self.
config.requireProperMotion:
336 raise RuntimeError(
"requireProperMotion=True but PM data not available from catalog.")
338 self.
log.warning(
"Proper motion correction not available for this reference catalog.")
345 addIsPhotometric=False, addIsResolved=False,
346 addIsVariable=False, coordErrDim=2,
347 addProperMotion=False, properMotionErrDim=2,
349 """Make a standard schema for reference object catalogs.
353 filterNameList : `list` of `str`
354 List of filter names. Used to create <filterName>_flux fields.
355 addIsPhotometric : `bool`
356 If True then add field
"photometric".
357 addIsResolved : `bool`
358 If
True then add field
"resolved".
359 addIsVariable : `bool`
360 If
True then add field
"variable".
362 Number of coord error fields; must be one of 0, 2, 3:
364 - If 2
or 3: add fields
"coord_raErr" and "coord_decErr".
365 - If 3: also add field
"coord_radecErr".
366 addProperMotion : `bool`
367 If
True add fields
"epoch",
"pm_ra",
"pm_dec" and "pm_flag".
368 properMotionErrDim : `int`
369 Number of proper motion error fields; must be one of 0, 2, 3;
370 ignored
if addProperMotion false:
371 - If 2
or 3: add fields
"pm_raErr" and "pm_decErr".
372 - If 3: also add field
"pm_radecErr".
374 If
True add fields
"epoch",
"parallax",
"parallaxErr"
380 Schema
for reference catalog, an
385 Reference catalogs support additional covariances, such
as
386 covariance between RA
and proper motion
in declination,
387 that are
not supported by this method, but can be added after
390 schema = afwTable.SimpleTable.makeMinimalSchema()
392 afwTable.Point2DKey.addFields(
395 "centroid on an exposure, if relevant",
401 doc=
"is position known?",
403 for filterName
in filterNameList:
405 field=
"%s_flux" % (filterName,),
407 doc=
"flux in filter %s" % (filterName,),
410 for filterName
in filterNameList:
412 field=
"%s_fluxErr" % (filterName,),
414 doc=
"flux uncertainty in filter %s" % (filterName,),
421 doc=
"set if the object can be used for photometric calibration",
427 doc=
"set if the object is spatially resolved",
433 doc=
"set if the object has variable brightness",
435 if coordErrDim
not in (0, 2, 3):
436 raise ValueError(
"coordErrDim={}; must be (0, 2, 3)".format(coordErrDim))
438 afwTable.CovarianceMatrix2fKey.addFields(
442 units=[
"rad",
"rad"],
443 diagonalOnly=(coordErrDim == 2),
446 if addProperMotion
or addParallax:
450 doc=
"date of observation (TAI, MJD)",
458 doc=
"proper motion in the right ascension direction = dra/dt * cos(dec)",
464 doc=
"proper motion in the declination direction",
467 if properMotionErrDim
not in (0, 2, 3):
468 raise ValueError(
"properMotionErrDim={}; must be (0, 2, 3)".format(properMotionErrDim))
469 if properMotionErrDim > 0:
470 afwTable.CovarianceMatrix2fKey.addFields(
474 units=[
"rad/year",
"rad/year"],
475 diagonalOnly=(properMotionErrDim == 2),
480 doc=
"Set if proper motion or proper motion error is bad",
493 doc=
"uncertainty in parallax",
497 field=
"parallax_flag",
499 doc=
"Set if parallax or parallax error is bad",
504 def _remapReferenceCatalogSchema(refCat, *, anyFilterMapsToThis=None,
505 filterMap=None, centroids=False):
506 """This function takes in a reference catalog and returns a new catalog
507 with additional columns defined
from the remaining function arguments.
512 Reference catalog to map to new catalog
513 anyFilterMapsToThis : `str`, optional
514 Always use this reference catalog filter.
515 Mutually exclusive
with `filterMap`
516 filterMap : `dict` [`str`,`str`], optional
517 Mapping of camera filter name: reference catalog filter name.
518 centroids : `bool`, optional
519 Add centroid fields to the loaded Schema. ``loadPixelBox`` expects
520 these fields to exist.
525 Deep copy of input reference catalog
with additional columns added
527 if anyFilterMapsToThis
or filterMap:
528 ReferenceObjectLoader._addFluxAliases(refCat.schema, anyFilterMapsToThis, filterMap)
530 mapper = afwTable.SchemaMapper(refCat.schema,
True)
531 mapper.addMinimalSchema(refCat.schema,
True)
532 mapper.editOutputSchema().disconnectAliases()
539 mapper.editOutputSchema().addField(
"centroid_x", type=float, doReplace=
True)
540 mapper.editOutputSchema().addField(
"centroid_y", type=float, doReplace=
True)
541 mapper.editOutputSchema().addField(
"hasCentroid", type=
"Flag", doReplace=
True)
542 mapper.editOutputSchema().getAliasMap().set(
"slot_Centroid",
"centroid")
544 expandedCat = afwTable.SimpleCatalog(mapper.getOutputSchema())
545 expandedCat.setMetadata(refCat.getMetadata())
546 expandedCat.extend(refCat, mapper=mapper)
551 def _addFluxAliases(schema, anyFilterMapsToThis=None, filterMap=None):
552 """Add aliases for camera filter fluxes to the schema.
554 For each camFilter: refFilter in filterMap, adds these aliases:
555 <camFilter>_camFlux: <refFilter>_flux
556 <camFilter>_camFluxErr: <refFilter>_fluxErr,
if the latter exists
557 or sets `anyFilterMapsToThis`
in the schema.
562 Schema
for reference catalog.
563 anyFilterMapsToThis : `str`, optional
564 Always use this reference catalog filter.
565 Mutually exclusive
with `filterMap`.
566 filterMap : `dict` [`str`,`str`], optional
567 Mapping of camera filter name: reference catalog filter name.
568 Mutually exclusive
with `anyFilterMapsToThis`.
573 Raised
if any required reference flux field
is missing
from the
577 if anyFilterMapsToThis
and filterMap:
578 raise ValueError(
"anyFilterMapsToThis and filterMap are mutually exclusive!")
580 aliasMap = schema.getAliasMap()
582 if anyFilterMapsToThis
is not None:
583 refFluxName = anyFilterMapsToThis +
"_flux"
584 if refFluxName
not in schema:
585 msg = f
"Unknown reference filter for anyFilterMapsToThis='{refFluxName}'"
586 raise RuntimeError(msg)
587 aliasMap.set(
"anyFilterMapsToThis", refFluxName)
590 def addAliasesForOneFilter(filterName, refFilterName):
591 """Add aliases for a single filter
595 filterName : `str` (optional)
596 Camera filter name. The resulting alias name is
598 refFilterName : `str`
599 Reference catalog filter name; the field
600 <refFilterName>_flux must exist.
602 camFluxName = filterName + "_camFlux"
603 refFluxName = refFilterName +
"_flux"
604 if refFluxName
not in schema:
605 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
606 aliasMap.set(camFluxName, refFluxName)
607 refFluxErrName = refFluxName +
"Err"
608 if refFluxErrName
in schema:
609 camFluxErrName = camFluxName +
"Err"
610 aliasMap.set(camFluxErrName, refFluxErrName)
612 if filterMap
is not None:
613 for filterName, refFilterName
in filterMap.items():
614 addAliasesForOneFilter(filterName, refFilterName)
617 def _makeBoxRegion(BBox, wcs, BBoxPadding):
630 outerLocalBBox.grow(BBoxPadding)
631 innerLocalBBox.grow(-1*BBoxPadding)
643 innerBoxCorners = innerLocalBBox.getCorners()
644 innerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in innerBoxCorners]
647 outerBoxCorners = outerLocalBBox.getCorners()
648 outerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in outerBoxCorners]
651 return innerSkyRegion, outerSkyRegion, innerSphCorners, outerSphCorners
654 def _calculateCircle(bbox, wcs, pixelMargin):
655 """Compute on-sky center and radius of search region.
662 WCS; used to convert pixel positions to sky coordinates.
664 Padding to add to 4 all edges of the bounding box (pixels).
668 results : `lsst.pipe.base.Struct`
672 ICRS center of the search region.
674 Radius of the search region.
676 Bounding box used to compute the circle.
679 bbox.grow(pixelMargin)
680 coord = wcs.pixelToSky(bbox.getCenter())
681 radius = max(coord.separation(wcs.pixelToSky(pp))
for pp
in bbox.getCorners())
682 return pipeBase.Struct(coord=coord, radius=radius, bbox=bbox)
686 """Return metadata about the reference catalog being loaded.
688 This metadata is used
for reloading the catalog (e.g.
for
689 reconstituting a normalized match list).
694 ICRS center of the search region.
696 Radius of the search region.
698 Name of the camera filter.
699 epoch : `astropy.time.Time`
or `
None`, optional
700 Epoch to which to correct proper motion
and parallax,
or `
None` to
701 not apply such corrections.
706 Metadata about the catalog.
709 md.add('RA', coord.getRa().asDegrees(),
'field center in degrees')
710 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
711 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
712 md.add(
'SMATCHV', 1,
'SourceMatchVector version number')
713 md.add(
'FILTER', filterName,
'filter name for photometric data')
714 md.add(
'EPOCH',
"NONE" if epoch
is None else epoch.mjd,
'Epoch (TAI MJD) for catalog')
718 bboxToSpherePadding=100):
719 """Return metadata about the load
721 This metadata is used
for reloading the catalog (e.g.,
for
722 reconstituting a normalised match list).
727 Bounding box
for the pixels.
729 The WCS object associated
with ``bbox``.
731 Name of the camera filter.
732 epoch : `astropy.time.Time`
or `
None`, optional
733 Epoch to which to correct proper motion
and parallax,
or `
None` to
734 not apply such corrections.
735 bboxToSpherePadding : `int`, optional
736 Padding
in pixels to account
for translating a set of corners into
737 a spherical (convex) boundary that
is certain to encompass the
738 enitre area covered by the box.
743 The metadata detailing the search parameters used
for this
747 md = self.getMetadataCircle(circle.coord, circle.radius, filterName, epoch=epoch)
749 paddedBbox = circle.bbox
750 _, _, innerCorners, outerCorners = self._makeBoxRegion(paddedBbox, wcs, bboxToSpherePadding)
751 for box, corners
in zip((
"INNER",
"OUTER"), (innerCorners, outerCorners)):
752 for (name, corner)
in zip((
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"),
754 md.add(f
"{box}_{name}_RA",
geom.SpherePoint(corner).getRa().asDegrees(), f
"{box}_corner")
755 md.add(f
"{box}_{name}_DEC",
geom.SpherePoint(corner).getDec().asDegrees(), f
"{box}_corner")
759 """Relink an unpersisted match list to sources and reference objects.
761 A match list is persisted
and unpersisted
as a catalog of IDs
762 produced by afw.table.packMatches(),
with match metadata
763 (
as returned by the astrometry tasks)
in the catalog
's metadata
764 attribute. This method converts such a match catalog into a match
765 list, with links to source records
and reference object records.
770 Unpersisted packed match list.
771 ``matchCat.table.getMetadata()`` must contain match metadata,
772 as returned by the astrometry tasks.
774 Source catalog. As a side effect, the catalog will be sorted
785 bboxToSpherePadding=100):
786 """Load reference objects that are within a pixel-based rectangular
789 This algorithm works by creating a spherical box whose corners
790 correspond to the WCS converted corners of the input bounding box
791 (possibly padded). It then defines a filtering function which looks at
792 the pixel position of the reference objects and accepts only those that
793 lie within the specified bounding box.
795 The spherical box region
and filtering function are passed to the
796 generic loadRegion method which loads
and filters the reference objects
797 from the datastore
and returns a single catalog containing the filtered
798 set of reference objects.
803 Box which bounds a region
in pixel space.
805 Wcs object defining the pixel to sky (
and inverse) transform
for
806 the supplied ``bbox``.
808 Name of camera filter.
809 epoch : `astropy.time.Time`
or `
None`, optional
810 Epoch to which to correct proper motion
and parallax,
or `
None`
811 to
not apply such corrections.
812 bboxToSpherePadding : `int`, optional
813 Padding to account
for translating a set of corners into a
814 spherical (convex) boundary that
is certain to encompase the
815 enitre area covered by the box.
819 output : `lsst.pipe.base.Struct`
820 Results struct
with attributes:
823 Catalog containing reference objects inside the specified
824 bounding box (padded by self.
config.pixelMargin).
826 Name of the field containing the flux associated
with
832 Raised
if no reference catalogs could be found
for the specified
835 Raised
if the loaded reference catalogs do
not have matching
839 paddedBbox.grow(self.config.pixelMargin)
840 innerSkyRegion, outerSkyRegion, _, _ = self._makeBoxRegion(paddedBbox, wcs, bboxToSpherePadding)
842 def _filterFunction(refCat, region):
853 refCat = preFiltFunc(refCat, region)
859 afwTable.updateRefCentroids(wcs, refCat)
862 if innerSkyRegion.contains(region):
866 filteredRefCat = type(refCat)(refCat.table)
867 centroidKey = afwTable.Point2DKey(refCat.schema[
'centroid'])
868 for record
in refCat:
869 pixCoords = record[centroidKey]
871 filteredRefCat.append(record)
872 return filteredRefCat
873 return self.
loadRegion(outerSkyRegion, filterName, filtFunc=_filterFunction, epoch=epoch)
875 def loadRegion(self, region, filterName, filtFunc=None, epoch=None):
876 """Load reference objects within a specified region.
878 This function loads the DataIds used to construct an instance of this
879 class which intersect or are contained within the specified region. The
880 reference catalogs which intersect but are
not fully contained within
881 the input region are further filtered by the specified filter function.
882 This function returns a single source catalog containing all reference
883 objects inside the specified region.
889 should define the spatial region
for which reference objects are to
891 filtFunc : callable
or `
None`, optional
892 This optional parameter should be a callable object that takes a
893 reference catalog
and its corresponding region
as parameters,
894 filters the catalog by some criteria
and returns the filtered
895 reference catalog. If `
None`, an internal filter function
is used
896 which filters according to
if a reference object falls within the
899 Name of camera filter.
900 epoch : `astropy.time.Time`
or `
None`, optional
901 Epoch to which to correct proper motion
and parallax,
or `
None` to
902 not apply such corrections.
906 output : `lsst.pipe.base.Struct`
907 Results struct
with attributes:
910 Catalog containing reference objects which intersect the
911 input region, filtered by the specified filter function.
913 Name of the field containing the flux associated
with
919 Raised
if no reference catalogs could be found
for the specified
922 Raised
if the loaded reference catalogs do
not have matching
925 regionLat = region.getBoundingBox().getLat()
926 regionLon = region.getBoundingBox().getLon()
927 self.log.info("Loading reference objects from %s in region bounded by "
928 "[%.8f, %.8f], [%.8f, %.8f] RA Dec",
930 self.
refCats[0].ref.datasetType.name,
931 regionLon.getA().asDegrees(), regionLon.getB().asDegrees(),
932 regionLat.getA().asDegrees(), regionLat.getB().asDegrees())
941 intersects = dataId.region.intersects(region)
943 intersects = region.intersects(dataId.region)
946 overlapList.append((dataId, refCat))
948 if len(overlapList) == 0:
949 raise RuntimeError(
"No reference tables could be found for input region")
951 firstCat = overlapList[0][1].get()
952 refCat = filtFunc(firstCat, overlapList[0][0].region)
953 trimmedAmount = len(firstCat) - len(refCat)
956 for dataId, inputRefCat
in overlapList[1:]:
957 tmpCat = inputRefCat.get()
959 if tmpCat.schema != firstCat.schema:
960 raise TypeError(
"Reference catalogs have mismatching schemas")
962 filteredCat = filtFunc(tmpCat, dataId.region)
963 refCat.extend(filteredCat)
964 trimmedAmount += len(tmpCat) - len(filteredCat)
966 self.
log.debug(
"Trimmed %d refCat objects lying outside padded region, leaving %d",
967 trimmedAmount, len(refCat))
968 self.
log.info(
"Loaded %d reference objects", len(refCat))
971 if not refCat.isContiguous():
972 refCat = refCat.copy(deep=
True)
980 self.
log.warning(
"Found version 0 reference catalog with old style units in schema.")
981 self.
log.warning(
"run `meas_algorithms/bin/convert_refcat_to_nJy.py` to convert fluxes to nJy.")
982 self.
log.warning(
"See RFC-575 for more details.")
986 anyFilterMapsToThis=self.
config.anyFilterMapsToThis,
987 filterMap=self.
config.filterMap)
990 if not expandedCat.isContiguous():
991 expandedCat = expandedCat.copy(deep=
True)
994 return pipeBase.Struct(refCat=expandedCat, fluxField=fluxField)
997 """Load reference objects that lie within a circular region on the sky.
999 This method constructs a circular region from an input center
and
1000 angular radius, loads reference catalogs which are contained
in or
1001 intersect the circle,
and filters reference catalogs which intersect
1002 down to objects which lie within the defined circle.
1007 Point defining the center of the circular region.
1009 Defines the angular radius of the circular region.
1011 Name of camera filter.
1012 epoch : `astropy.time.Time`
or `
None`, optional
1013 Epoch to which to correct proper motion
and parallax,
or `
None` to
1014 not apply such corrections.
1018 output : `lsst.pipe.base.Struct`
1019 Results struct
with attributes:
1022 Catalog containing reference objects inside the specified
1025 Name of the field containing the flux associated
with
1028 centerVector = ctrCoord.getVector()
1031 return self.
loadRegion(circularRegion, filterName, epoch=epoch)
1035 """Get the name of a flux field from a schema.
1037 return the alias of
"anyFilterMapsToThis",
if present
1039 return "*filterName*_camFlux" if present
1040 else return "*filterName*_flux" if present (camera filter name
1041 matches reference filter name)
1042 else throw RuntimeError
1047 Reference catalog schema.
1049 Name of camera filter.
1053 fluxFieldName : `str`
1059 If an appropriate field
is not found.
1061 if not isinstance(schema, afwTable.Schema):
1062 raise RuntimeError(
"schema=%s is not a schema" % (schema,))
1064 return schema.getAliasMap().get(
"anyFilterMapsToThis")
1068 fluxFieldList = [filterName +
"_camFlux", filterName +
"_flux"]
1069 for fluxField
in fluxFieldList:
1070 if fluxField
in schema:
1073 raise RuntimeError(
"Could not find flux field(s) %s" % (
", ".join(fluxFieldList)))
1077 """Return keys for flux and flux error.
1082 Reference catalog schema.
1084 Name of camera filter.
1092 - flux error key, if present,
else None
1097 If flux field
not found.
1100 fluxErrField = fluxField + "Err"
1101 fluxKey = schema[fluxField].asKey()
1103 fluxErrKey = schema[fluxErrField].asKey()
1106 return (fluxKey, fluxErrKey)
1109@deprecated(reason=(
"This task is used in gen2 only; it will be removed after v25. "
1110 "See DM-35671 for details on updating code to avoid this warning."),
1111 version=
"v25.0", category=FutureWarning)
1113 """Abstract gen2 base class to load objects from reference catalogs.
1115 _DefaultName = "LoadReferenceObjects"
1118 def loadSkyCircle(self, ctrCoord, radius, filterName, epoch=None, centroids=False):
1119 """Load reference objects that overlap a circular sky region.
1124 ICRS center of search region.
1126 Radius of search region.
1128 Name of filter. This can be used for flux limit comparisons.
1129 epoch : `astropy.time.Time`
or `
None`, optional
1130 Epoch to which to correct proper motion
and parallax,
or `
None` to
1131 not apply such corrections.
1132 centroids : `bool`, optional
1133 Add centroid fields to the loaded Schema. ``loadPixelBox`` expects
1134 these fields to exist.
1138 results : `lsst.pipe.base.Struct`
1139 A Struct containing the following fields:
1140 refCat : `lsst.afw.catalog.SimpleCatalog`
1141 A catalog of reference objects
with the standard
1142 schema,
as documented
in the main doc string
for
1143 `LoadReferenceObjects`.
1144 The catalog
is guaranteed to be contiguous.
1146 Name of flux field
for specified `filterName`.
1150 Note that subclasses are responsible
for performing the proper motion
1151 correction, since this
is the lowest-level interface
for retrieving
1158 """Relink an unpersisted match list to sources and reference
1161 A match list is persisted
and unpersisted
as a catalog of IDs
1162 produced by afw.table.packMatches(),
with match metadata
1163 (
as returned by the astrometry tasks)
in the catalog
's metadata
1164 attribute. This method converts such a match catalog into a match
1165 list, with links to source records
and reference object records.
1170 Reference object loader to use
in getting reference objects
1172 Unperisted packed match list.
1173 ``matchCat.table.getMetadata()`` must contain match metadata,
1174 as returned by the astrometry tasks.
1176 Source catalog. As a side effect, the catalog will be sorted
1184 matchmeta = matchCat.table.getMetadata()
1185 version = matchmeta.getInt('SMATCHV')
1187 raise ValueError(
'SourceMatchVector version number is %i, not 1.' % version)
1188 filterName = matchmeta.getString(
'FILTER').strip()
1190 epoch = matchmeta.getDouble(
'EPOCH')
1191 except (LookupError, TypeError):
1193 if 'RADIUS' in matchmeta:
1196 matchmeta.getDouble(
'DEC'), geom.degrees)
1197 rad = matchmeta.getDouble(
'RADIUS')*geom.degrees
1198 refCat = refObjLoader.loadSkyCircle(ctrCoord, rad, filterName, epoch=epoch).refCat
1199 elif "INNER_UPPER_LEFT_RA" in matchmeta:
1205 for place
in (
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"):
1207 matchmeta.getDouble(f
"OUTER_{place}_DEC"),
1208 geom.degrees).getVector()
1211 refCat = refObjLoader.loadRegion(outerBox, filterName, epoch=epoch).refCat
1215 return afwTable.unpackMatches(matchCat, refCat, sourceCat)
1218@deprecated(reason=
"Base class only used for gen2 interface, and will be removed after v25.0. "
1219 "Please use ReferenceObjectLoader directly.",
1220 version=
"v25.0", category=FutureWarning)
1222 """Stub of a deprecated class.
1226 config : `lsst.pex.config.Config`
1227 Configuration for the loader.
1234 """Apply proper motion correction to a reference catalog.
1236 Adjust position and position error
in the ``catalog``
1237 for proper motion to the specified ``epoch``,
1238 modifying the catalog
in place.
1243 Log object to write to.
1245 Catalog of positions, containing:
1247 - Coordinates, retrieved by the table
's coordinate key.
1248 - ``coord_raErr`` : Error in Right Ascension (rad).
1249 - ``coord_decErr`` : Error
in Declination (rad).
1250 - ``pm_ra`` : Proper motion
in Right Ascension (rad/yr,
1252 - ``pm_raErr`` : Error
in ``pm_ra`` (rad/yr), optional.
1253 - ``pm_dec`` : Proper motion
in Declination (rad/yr,
1255 - ``pm_decErr`` : Error
in ``pm_dec`` (rad/yr), optional.
1256 - ``epoch`` : Mean epoch of object (an astropy.time.Time)
1257 epoch : `astropy.time.Time`
1258 Epoch to which to correct proper motion.
1260 if "epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema:
1261 log.warning(
"Proper motion correction not available from catalog")
1263 if not catalog.isContiguous():
1264 raise RuntimeError(
"Catalog must be contiguous")
1265 catEpoch = astropy.time.Time(catalog[
"epoch"], scale=
"tai", format=
"mjd")
1266 log.info(
"Correcting reference catalog for proper motion to %r", epoch)
1268 timeDiffsYears = (epoch.tai - catEpoch).to(astropy.units.yr).value
1269 coordKey = catalog.table.getCoordKey()
1272 pmRaRad = catalog[
"pm_ra"]
1273 pmDecRad = catalog[
"pm_dec"]
1274 offsetsRaRad = pmRaRad*timeDiffsYears
1275 offsetsDecRad = pmDecRad*timeDiffsYears
1283 offsetBearingsRad = numpy.arctan2(pmDecRad*1e6, pmRaRad*1e6)
1284 offsetAmountsRad = numpy.hypot(offsetsRaRad, offsetsDecRad)
1285 for record, bearingRad, amountRad
in zip(catalog, offsetBearingsRad, offsetAmountsRad):
1286 record.set(coordKey,
1287 record.get(coordKey).offset(bearing=bearingRad*geom.radians,
1288 amount=amountRad*geom.radians))
1290 if "coord_raErr" in catalog.schema:
1291 catalog[
"coord_raErr"] = numpy.hypot(catalog[
"coord_raErr"],
1292 catalog[
"pm_raErr"]*timeDiffsYears)
1293 if "coord_decErr" in catalog.schema:
1294 catalog[
"coord_decErr"] = numpy.hypot(catalog[
"coord_decErr"],
1295 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 _makeBoxRegion(BBox, wcs, BBoxPadding)
def applyProperMotions(self, catalog, epoch)
def loadSkyCircle(self, ctrCoord, radius, filterName, epoch=None)
def __init__(self, dataIds, refCats, log=None, config=None, **kwargs)
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)