22__all__ = [
"getRefFluxField",
"getRefFluxKeys",
"LoadReferenceObjectsConfig",
23 "ReferenceObjectLoader"]
35from lsst
import sphgeom
38from .convertReferenceCatalog
import LATEST_FORMAT_VERSION
42 """"Return the format version stored in a reference catalog header.
46 refCat : `lsst.afw.table.SimpleCatalog`
47 Reference catalog to inspect.
52 Format version integer.
57 Raised if the catalog is version 0, has no metadata, or does not
58 include a "REFCAT_FORMAT_VERSION" key.
60 errMsg =
"Version 0 refcats are no longer supported: refcat fluxes must have nJy units."
61 md = refCat.getMetadata()
63 raise ValueError(f
"No metadata found in refcat header. {errMsg}")
66 version = md.getScalar(
"REFCAT_FORMAT_VERSION")
68 raise ValueError(errMsg)
72 raise ValueError(f
"No version number found in refcat header metadata. {errMsg}")
76 """This is a private helper class which filters catalogs by
77 row based on the row being inside the region used to initialize
82 region : `lsst.sphgeom.Region`
83 The spatial region which all objects should lie within
89 """This call method on an instance of this class takes in a reference
90 catalog, and the region from which the catalog was generated.
92 If the catalog region is entirely contained within the region used to
93 initialize this class, then all the entries in the catalog must be
94 within the region and so the whole catalog is returned.
96 If the catalog region is not entirely contained, then the location for
97 each record is tested against the region used to initialize the class.
98 Records which fall inside this region are added to a new catalog, and
99 this catalog is then returned.
103 refCat : `lsst.afw.table.SourceCatalog`
104 SourceCatalog to be filtered.
105 catRegion : `lsst.sphgeom.Region`
106 Region in which the catalog was created
108 if catRegion.isWithin(self.
region):
112 coordKey = refCat.getCoordKey()
113 inside = self.
region.contains(lon=refCat[coordKey.getRa()], lat=refCat[coordKey.getDec()])
114 filteredRefCat = refCat[inside]
116 return filteredRefCat
120 pixelMargin = pexConfig.RangeField(
121 doc=
"Padding to add to 4 all edges of the bounding box (pixels)",
126 anyFilterMapsToThis = pexConfig.Field(
127 doc=(
"Always use this reference catalog filter, no matter whether or what filter name is "
128 "supplied to the loader. Effectively a trivial filterMap: map all filter names to this filter."
129 " This can be set for purely-astrometric catalogs (e.g. Gaia DR2) where there is only one "
130 "reasonable choice for every camera filter->refcat mapping, but not for refcats used for "
131 "photometry, which need a filterMap and/or colorterms/transmission corrections."),
136 filterMap = pexConfig.DictField(
137 doc=(
"Mapping of camera filter name: reference catalog filter name; "
138 "each reference filter must exist in the refcat."
139 " Note that this does not perform any bandpass corrections: it is just a lookup."),
144 requireProperMotion = pexConfig.Field(
145 doc=
"Require that the fields needed to correct proper motion "
146 "(epoch, pm_ra and pm_dec) are present?",
154 msg =
"`filterMap` and `anyFilterMapsToThis` are mutually exclusive"
155 raise pexConfig.FieldValidationError(LoadReferenceObjectsConfig.anyFilterMapsToThis,
160 """This class facilitates loading reference catalogs.
162 The QuantumGraph generation will create a list of datasets that may
163 possibly overlap a given region. These datasets are then used to construct
164 an instance of this class. The class instance should then be passed into
165 a task which needs reference catalogs. These tasks should then determine
166 the exact region of the sky reference catalogs will be loaded for, and
167 call a corresponding method to load the reference objects.
171 dataIds : iterable of `lsst.daf.butler.DataCoordinate`
172 An iterable object of data IDs that point to reference catalogs.
173 refCats : iterable of `lsst.daf.butler.DeferredDatasetHandle`
174 Handles to load refCats on demand.
175 name : `str`, optional
176 The name of the refcat that this object will load. This name is used
177 for applying colorterms, for example.
178 config : `LoadReferenceObjectsConfig`
179 Configuration of this reference loader.
180 log : `lsst.log.Log`, `logging.Logger` or `None`, optional
181 Logger object used to write out messages. If `None` a default
184 ConfigClass = LoadReferenceObjectsConfig
186 def __init__(self, dataIds, refCats, name=None, log=None, config=None):
193 self.
log = log
or logging.getLogger(__name__).getChild(
"ReferenceObjectLoader")
196 """Apply proper motion correction to a reference catalog.
198 Adjust position and position error in the ``catalog``
199 for proper motion to the specified ``epoch``,
200 modifying the catalog in place.
204 catalog : `lsst.afw.table.SimpleCatalog`
205 Catalog of positions, containing at least these fields:
207 - Coordinates, retrieved by the table's coordinate key.
208 - ``coord_raErr`` : Error in Right Ascension (rad).
209 - ``coord_decErr`` : Error in Declination (rad).
210 - ``pm_ra`` : Proper motion in Right Ascension (rad/yr,
212 - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional.
213 - ``pm_dec`` : Proper motion in Declination (rad/yr,
215 - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional.
216 - ``epoch`` : Mean epoch of object (an astropy.time.Time)
217 epoch : `astropy.time.Time`
218 Epoch to which to correct proper motion.
219 If None, do not apply PM corrections or raise if
220 ``config.requireProperMotion`` is True.
225 Raised if ``config.requireProperMotion`` is set but we cannot
226 apply the proper motion correction for some reason.
229 if self.
config.requireProperMotion:
230 raise RuntimeError(
"requireProperMotion=True but epoch not provided to loader.")
232 self.
log.debug(
"No epoch provided: not applying proper motion corrections to refcat.")
236 if "pm_ra" in catalog.schema:
237 pm_ra_radians =
False
238 field = catalog.schema[
"pm_ra"].asField()
239 if field.getTypeString() ==
"Angle" or field.getUnits() ==
"rad":
242 if self.
config.requireProperMotion
and not pm_ra_radians:
244 "requireProperMotion=True but refcat pm_ra field is not an Angle or radians.",
246 elif not pm_ra_radians:
248 "Reference catalog pm_ra field is not an Angle or radians; cannot apply proper motion.",
252 if (
"epoch" not in catalog.schema
or "pm_ra" not in catalog.schema):
253 if self.
config.requireProperMotion:
254 raise RuntimeError(
"requireProperMotion=True but PM data not available from catalog.")
256 self.
log.warning(
"Proper motion correction not available for this reference catalog.")
263 filterMap=None, centroids=False):
264 """This function takes in a reference catalog and returns a new catalog
265 with additional columns defined from the remaining function arguments.
269 refCat : `lsst.afw.table.SimpleCatalog`
270 Reference catalog to map to new catalog
271 anyFilterMapsToThis : `str`, optional
272 Always use this reference catalog filter.
273 Mutually exclusive with `filterMap`
274 filterMap : `dict` [`str`,`str`], optional
275 Mapping of camera filter name: reference catalog filter name.
276 centroids : `bool`, optional
277 Add centroid fields to the loaded Schema. ``loadPixelBox`` expects
278 these fields to exist.
282 expandedCat : `lsst.afw.table.SimpleCatalog`
283 Deep copy of input reference catalog with additional columns added
285 if anyFilterMapsToThis
or filterMap:
286 ReferenceObjectLoader._addFluxAliases(refCat.schema, anyFilterMapsToThis, filterMap)
288 mapper = afwTable.SchemaMapper(refCat.schema,
True)
289 mapper.addMinimalSchema(refCat.schema,
True)
290 mapper.editOutputSchema().disconnectAliases()
297 mapper.editOutputSchema().addField(
"centroid_x", type=float, doReplace=
True)
298 mapper.editOutputSchema().addField(
"centroid_y", type=float, doReplace=
True)
299 mapper.editOutputSchema().addField(
"hasCentroid", type=
"Flag", doReplace=
True)
300 mapper.editOutputSchema().getAliasMap().set(
"slot_Centroid",
"centroid")
302 expandedCat = afwTable.SimpleCatalog(mapper.getOutputSchema())
303 expandedCat.setMetadata(refCat.getMetadata())
304 expandedCat.extend(refCat, mapper=mapper)
310 """Add aliases for camera filter fluxes to the schema.
312 For each camFilter: refFilter in filterMap, adds these aliases:
313 <camFilter>_camFlux: <refFilter>_flux
314 <camFilter>_camFluxErr: <refFilter>_fluxErr, if the latter exists
315 or sets `anyFilterMapsToThis` in the schema.
319 schema : `lsst.afw.table.Schema`
320 Schema for reference catalog.
321 anyFilterMapsToThis : `str`, optional
322 Always use this reference catalog filter.
323 Mutually exclusive with `filterMap`.
324 filterMap : `dict` [`str`,`str`], optional
325 Mapping of camera filter name: reference catalog filter name.
326 Mutually exclusive with `anyFilterMapsToThis`.
331 Raised if any required reference flux field is missing from the
335 if anyFilterMapsToThis
and filterMap:
336 raise ValueError(
"anyFilterMapsToThis and filterMap are mutually exclusive!")
338 aliasMap = schema.getAliasMap()
340 if anyFilterMapsToThis
is not None:
341 refFluxName = anyFilterMapsToThis +
"_flux"
342 if refFluxName
not in schema:
343 msg = f
"Unknown reference filter for anyFilterMapsToThis='{refFluxName}'"
344 raise RuntimeError(msg)
345 aliasMap.set(
"anyFilterMapsToThis", refFluxName)
348 def addAliasesForOneFilter(filterName, refFilterName):
349 """Add aliases for a single filter
353 filterName : `str` (optional)
354 Camera filter name. The resulting alias name is
356 refFilterName : `str`
357 Reference catalog filter name; the field
358 <refFilterName>_flux must exist.
360 camFluxName = filterName +
"_camFlux"
361 refFluxName = refFilterName +
"_flux"
362 if refFluxName
not in schema:
363 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
364 aliasMap.set(camFluxName, refFluxName)
365 refFluxErrName = refFluxName +
"Err"
366 if refFluxErrName
in schema:
367 camFluxErrName = camFluxName +
"Err"
368 aliasMap.set(camFluxErrName, refFluxErrName)
370 if filterMap
is not None:
371 for filterName, refFilterName
in filterMap.items():
372 addAliasesForOneFilter(filterName, refFilterName)
388 outerLocalBBox.grow(BBoxPadding)
389 innerLocalBBox.grow(-1*BBoxPadding)
401 innerBoxCorners = innerLocalBBox.getCorners()
402 innerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in innerBoxCorners]
405 outerBoxCorners = outerLocalBBox.getCorners()
406 outerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in outerBoxCorners]
409 return innerSkyRegion, outerSkyRegion, innerSphCorners, outerSphCorners
413 """Compute on-sky center and radius of search region.
417 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
419 wcs : `lsst.afw.geom.SkyWcs`
420 WCS; used to convert pixel positions to sky coordinates.
422 Padding to add to 4 all edges of the bounding box (pixels).
426 results : `lsst.pipe.base.Struct`
429 - coord : `lsst.geom.SpherePoint`
430 ICRS center of the search region.
431 - radius : `lsst.geom.Angle`
432 Radius of the search region.
433 - bbox : `lsst.geom.Box2D`
434 Bounding box used to compute the circle.
437 bbox.grow(pixelMargin)
438 coord = wcs.pixelToSky(bbox.getCenter())
439 radius = max(coord.separation(wcs.pixelToSky(pp))
for pp
in bbox.getCorners())
440 return pipeBase.Struct(coord=coord, radius=radius, bbox=bbox)
444 """Return metadata about the loaded reference catalog, in an on-sky
447 This metadata is used for reloading the catalog (e.g. for
448 reconstituting a normalized match list).
452 coord : `lsst.geom.SpherePoint`
453 ICRS center of the search region.
454 radius : `lsst.geom.Angle`
455 Radius of the search region.
457 Name of the camera filter.
458 epoch : `astropy.time.Time` or `None`, optional
459 Epoch that proper motion and parallax were corrected to, or `None`
460 if no such corrections were applied.
464 md : `lsst.daf.base.PropertyList`
465 Metadata about the catalog.
468 md.add(
'RA', coord.getRa().asDegrees(),
'field center in degrees')
469 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
470 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
473 md.add(
'SMATCHV', 2,
'SourceMatchVector version number')
474 md.add(
'FILTER', filterName,
'camera filter name for photometric data')
475 md.add(
'TIMESYS',
"TAI",
"time scale of time keywords")
476 md.add(
'JEPOCH',
None if epoch
is None else epoch.tai.jyear,
477 'Julian epoch (TAI Julian Epoch year) for catalog')
481 bboxToSpherePadding=100):
482 """Return metadata about the loaded reference catalog, in an
485 This metadata is used for reloading the catalog (e.g., for
486 reconstituting a normalised match list).
490 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
491 Bounding box for the pixels.
492 wcs : `lsst.afw.geom.SkyWcs`
493 The WCS object associated with ``bbox``.
495 Name of the camera filter.
496 epoch : `astropy.time.Time` or `None`, optional
497 Epoch that proper motion and parallax were corrected to, or `None`
498 if no such corrections were applied.
499 bboxToSpherePadding : `int`, optional
500 Padding in pixels to account for translating a set of corners into
501 a spherical (convex) boundary that is certain to encompass the
502 enitre area covered by the box.
506 md : `lsst.daf.base.PropertyList`
507 The metadata detailing the search parameters used for this
511 md = self.
getMetadataCircle(circle.coord, circle.radius, filterName, epoch=epoch)
513 paddedBbox = circle.bbox
514 _, _, innerCorners, outerCorners = self.
_makeBoxRegion(paddedBbox, wcs, bboxToSpherePadding)
515 for box, corners
in zip((
"INNER",
"OUTER"), (innerCorners, outerCorners)):
516 for (name, corner)
in zip((
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"),
518 md.add(f
"{box}_{name}_RA",
geom.SpherePoint(corner).getRa().asDegrees(), f
"{box}_corner")
519 md.add(f
"{box}_{name}_DEC",
geom.SpherePoint(corner).getDec().asDegrees(), f
"{box}_corner")
523 bboxToSpherePadding=100):
524 """Load reference objects that are within a pixel-based rectangular
527 This algorithm works by creating a spherical box whose corners
528 correspond to the WCS converted corners of the input bounding box
529 (possibly padded). It then defines a filtering function which looks at
530 the pixel position of the reference objects and accepts only those that
531 lie within the specified bounding box.
533 The spherical box region and filtering function are passed to the
534 generic loadRegion method which loads and filters the reference objects
535 from the datastore and returns a single catalog containing the filtered
536 set of reference objects.
540 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
541 Box which bounds a region in pixel space.
542 wcs : `lsst.afw.geom.SkyWcs`
543 Wcs object defining the pixel to sky (and inverse) transform for
544 the supplied ``bbox``.
546 Name of camera filter.
547 epoch : `astropy.time.Time` or `None`, optional
548 Epoch to which to correct proper motion and parallax, or `None`
549 to not apply such corrections.
550 bboxToSpherePadding : `int`, optional
551 Padding to account for translating a set of corners into a
552 spherical (convex) boundary that is certain to encompase the
553 enitre area covered by the box.
557 output : `lsst.pipe.base.Struct`
558 Results struct with attributes:
561 Catalog containing reference objects inside the specified
562 bounding box (padded by self.config.pixelMargin).
564 Name of the field containing the flux associated with
570 Raised if no reference catalogs could be found for the specified
573 Raised if the loaded reference catalogs do not have matching
577 paddedBbox.grow(self.
config.pixelMargin)
578 innerSkyRegion, outerSkyRegion, _, _ = self.
_makeBoxRegion(paddedBbox, wcs, bboxToSpherePadding)
580 def _filterFunction(refCat, region):
591 refCat = preFiltFunc(refCat, region)
597 afwTable.updateRefCentroids(wcs, refCat)
600 if innerSkyRegion.contains(region):
603 inside = paddedBbox.contains(x=refCat[
"slot_Centroid_x"], y=refCat[
"slot_Centroid_y"])
604 filteredRefCat = refCat[inside]
606 return filteredRefCat
607 return self.
loadRegion(outerSkyRegion, filterName, filtFunc=_filterFunction, epoch=epoch)
609 def loadRegion(self, region, filterName, filtFunc=None, epoch=None):
610 """Load reference objects within a specified region.
612 This function loads the DataIds used to construct an instance of this
613 class which intersect or are contained within the specified region. The
614 reference catalogs which intersect but are not fully contained within
615 the input region are further filtered by the specified filter function.
616 This function returns a single source catalog containing all reference
617 objects inside the specified region.
621 region : `lsst.sphgeom.Region`
622 This can be any type that is derived from `lsst.sphgeom.Region` and
623 should define the spatial region for which reference objects are to
625 filtFunc : callable or `None`, optional
626 This optional parameter should be a callable object that takes a
627 reference catalog and its corresponding region as parameters,
628 filters the catalog by some criteria and returns the filtered
629 reference catalog. If `None`, an internal filter function is used
630 which filters according to if a reference object falls within the
633 Name of camera filter.
634 epoch : `astropy.time.Time` or `None`, optional
635 Epoch to which to correct proper motion and parallax, or `None` to
636 not apply such corrections.
640 output : `lsst.pipe.base.Struct`
641 Results struct with attributes:
644 Catalog containing reference objects which intersect the
645 input region, filtered by the specified filter function.
647 Name of the field containing the flux associated with
653 Raised if no reference catalogs could be found for the specified
656 Raised if the loaded reference catalogs do not have matching
659 regionLat = region.getBoundingBox().getLat()
660 regionLon = region.getBoundingBox().getLon()
661 self.
log.info(
"Loading reference objects from %s in region bounded by "
662 "[%.8f, %.8f], [%.8f, %.8f] RA Dec",
664 regionLon.getA().asDegrees(), regionLon.getB().asDegrees(),
665 regionLat.getA().asDegrees(), regionLat.getB().asDegrees())
674 intersects = dataId.region.intersects(region)
676 intersects = region.intersects(dataId.region)
679 overlapList.append((dataId, refCat))
681 if len(overlapList) == 0:
682 raise RuntimeError(
"No reference tables could be found for input region")
684 firstCat = overlapList[0][1].get()
685 refCat = filtFunc(firstCat, overlapList[0][0].region)
686 trimmedAmount = len(firstCat) - len(refCat)
689 for dataId, inputRefCat
in overlapList[1:]:
690 tmpCat = inputRefCat.get()
692 if tmpCat.schema != firstCat.schema:
693 raise TypeError(
"Reference catalogs have mismatching schemas")
695 filteredCat = filtFunc(tmpCat, dataId.region)
696 refCat.extend(filteredCat)
697 trimmedAmount += len(tmpCat) - len(filteredCat)
699 self.
log.debug(
"Trimmed %d refCat objects lying outside padded region, leaving %d",
700 trimmedAmount, len(refCat))
701 self.
log.info(
"Loaded %d reference objects", len(refCat))
704 if not refCat.isContiguous():
705 refCat = refCat.copy(deep=
True)
710 anyFilterMapsToThis=self.
config.anyFilterMapsToThis,
711 filterMap=self.
config.filterMap)
714 if not expandedCat.isContiguous():
715 expandedCat = expandedCat.copy(deep=
True)
719 if expandedCat.schema[fluxField].asField().getUnits() !=
"nJy":
722 if version > LATEST_FORMAT_VERSION:
723 raise ValueError(f
"Unsupported refcat format version: {version} > {LATEST_FORMAT_VERSION}.")
725 return pipeBase.Struct(refCat=expandedCat, fluxField=fluxField)
728 """Load reference objects that lie within a circular region on the sky.
730 This method constructs a circular region from an input center and
731 angular radius, loads reference catalogs which are contained in or
732 intersect the circle, and filters reference catalogs which intersect
733 down to objects which lie within the defined circle.
737 ctrCoord : `lsst.geom.SpherePoint`
738 Point defining the center of the circular region.
739 radius : `lsst.geom.Angle`
740 Defines the angular radius of the circular region.
742 Name of camera filter.
743 epoch : `astropy.time.Time` or `None`, optional
744 Epoch to which to correct proper motion and parallax, or `None` to
745 not apply such corrections.
749 output : `lsst.pipe.base.Struct`
750 Results struct with attributes:
753 Catalog containing reference objects inside the specified
756 Name of the field containing the flux associated with
759 centerVector = ctrCoord.getVector()
762 return self.
loadRegion(circularRegion, filterName, epoch=epoch)
765 """Load the schema for the reference catalog.
770 Name of camera filter.
774 output : `lsst.pipe.base.Struct`
775 Results struct with attributes:
778 Schema of the reference catalogs returned by other 'load'
781 Name of the field containing the flux associated with
785 raise RuntimeError(
"No reference tables could be found.")
791 self.
refCats[0] = pipeBase.InMemoryDatasetHandle(cat, dataId=self.
refCats[0].dataId, copy=
False)
792 emptyCat = type(cat)(cat.table.clone())
795 anyFilterMapsToThis=self.
config.anyFilterMapsToThis,
796 filterMap=self.
config.filterMap,
799 if expandedEmptyCat.schema[fluxField].asField().getUnits() !=
"nJy":
802 if version > LATEST_FORMAT_VERSION:
803 raise ValueError(f
"Unsupported refcat format version: {version} > {LATEST_FORMAT_VERSION}.")
804 return pipeBase.Struct(schema=expandedEmptyCat.schema, fluxField=fluxField)
808 """Get the name of a flux field from a schema.
812 schema : `lsst.afw.table.Schema`
813 Reference catalog schema.
815 Name of camera filter.
819 fluxFieldName : `str`
824 Return the alias of ``anyFilterMapsToThis``, if present
825 else, return ``*filterName*_camFlux`` if present,
826 else, return ``*filterName*_flux`` if present (camera filter name
827 matches reference filter name), else raise an exception.
832 Raised if an appropriate field is not found.
834 if not isinstance(schema, afwTable.Schema):
835 raise RuntimeError(
"schema=%s is not a schema" % (schema,))
837 return schema.getAliasMap().get(
"anyFilterMapsToThis")
841 fluxFieldList = [filterName +
"_camFlux", filterName +
"_flux"]
842 for fluxField
in fluxFieldList:
843 if fluxField
in schema:
846 raise RuntimeError(
"Could not find flux field(s) %s" % (
", ".join(fluxFieldList)))
850 """Return keys for flux and flux error.
854 schema : `lsst.afw.table.Schema`
855 Reference catalog schema.
857 Name of camera filter.
861 keys : `tuple` of (`lsst.afw.table.Key`, `lsst.afw.table.Key`)
865 - flux error key, if present, else None
870 If flux field not found.
873 fluxErrField = fluxField +
"Err"
874 fluxKey = schema[fluxField].asKey()
876 fluxErrKey = schema[fluxErrField].asKey()
879 return (fluxKey, fluxErrKey)
883 """Apply proper motion correction to a reference catalog.
885 Adjust position and position error in the ``catalog``
886 for proper motion to the specified ``epoch``,
887 modifying the catalog in place.
891 log : `lsst.log.Log` or `logging.Logger`
892 Log object to write to.
893 catalog : `lsst.afw.table.SimpleCatalog`
894 Catalog of positions, containing:
896 - Coordinates, retrieved by the table's coordinate key.
897 - ``coord_raErr`` : Error in Right Ascension (rad).
898 - ``coord_decErr`` : Error in Declination (rad).
899 - ``pm_ra`` : Proper motion in Right Ascension (rad/yr,
901 - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional.
902 - ``pm_dec`` : Proper motion in Declination (rad/yr,
904 - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional.
905 - ``epoch`` : Mean epoch of object (an astropy.time.Time)
906 epoch : `astropy.time.Time`
907 Epoch to which to correct proper motion.
909 if "epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema:
910 log.warning(
"Proper motion correction not available from catalog")
912 if not catalog.isContiguous():
913 raise RuntimeError(
"Catalog must be contiguous")
914 catEpoch = astropy.time.Time(catalog[
"epoch"], scale=
"tai", format=
"mjd")
915 log.info(
"Correcting reference catalog for proper motion to %r", epoch)
917 timeDiffsYears = (epoch.tai - catEpoch).to(astropy.units.yr).value
918 coordKey = catalog.table.getCoordKey()
921 pmRaRad = catalog[
"pm_ra"]
922 pmDecRad = catalog[
"pm_dec"]
923 offsetsRaRad = pmRaRad*timeDiffsYears
924 offsetsDecRad = pmDecRad*timeDiffsYears
932 offsetBearingsRad = numpy.arctan2(offsetsDecRad*1e6, offsetsRaRad*1e6)
933 offsetAmountsRad = numpy.hypot(offsetsRaRad, offsetsDecRad)
934 for record, bearingRad, amountRad
in zip(catalog, offsetBearingsRad, offsetAmountsRad):
936 record.get(coordKey).offset(bearing=bearingRad*geom.radians,
937 amount=amountRad*geom.radians))
940 if "coord_raErr" in catalog.schema:
941 catalog[
"coord_raErr"] = numpy.hypot(catalog[
"coord_raErr"],
942 catalog[
"pm_raErr"]*timeDiffsYears)
943 if "coord_decErr" in catalog.schema:
944 catalog[
"coord_decErr"] = numpy.hypot(catalog[
"coord_decErr"],
945 catalog[
"pm_decErr"]*timeDiffsYears)
__call__(self, refCat, catRegion)
loadRegion(self, region, filterName, filtFunc=None, epoch=None)
_makeBoxRegion(BBox, wcs, BBoxPadding)
_addFluxAliases(schema, anyFilterMapsToThis=None, filterMap=None)
getMetadataBox(self, bbox, wcs, filterName, epoch=None, bboxToSpherePadding=100)
applyProperMotions(self, catalog, epoch)
__init__(self, dataIds, refCats, name=None, log=None, config=None)
getMetadataCircle(coord, radius, filterName, epoch=None)
loadSchema(self, filterName)
loadPixelBox(self, bbox, wcs, filterName, epoch=None, bboxToSpherePadding=100)
_calculateCircle(bbox, wcs, pixelMargin)
loadSkyCircle(self, ctrCoord, radius, filterName, epoch=None)
_remapReferenceCatalogSchema(refCat, *, anyFilterMapsToThis=None, filterMap=None, centroids=False)
getFormatVersionFromRefCat(refCat)
getRefFluxKeys(schema, filterName)
applyProperMotionsImpl(log, catalog, epoch)
getRefFluxField(schema, filterName)