24 __all__ = [
"getRefFluxField", 
"getRefFluxKeys", 
"LoadReferenceObjectsTask", 
"LoadReferenceObjectsConfig",
 
   25            "ReferenceObjectLoader"]
 
   36 import lsst.pex.config 
as pexConfig
 
   42 from lsst 
import sphgeom
 
   47     """Return True if this name/units combination corresponds to an 
   48     "old-style" reference catalog flux field. 
   50     unitsCheck = units != 
'nJy'   
   51     isFlux = name.endswith(
'_flux')
 
   52     isFluxSigma = name.endswith(
'_fluxSigma')
 
   53     isFluxErr = name.endswith(
'_fluxErr')
 
   54     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. 
   71     refCat : `lsst.afw.table.SimpleCatalog` 
   72         Reference catalog to inspect. 
   76     version : `int` or `None` 
   77         Format version integer, or `None` if the catalog has no metadata 
   78         or the metadata does not include a "REFCAT_FORMAT_VERSION" key. 
   80     md = refCat.getMetadata()
 
   84         return md.getScalar(
"REFCAT_FORMAT_VERSION")
 
   90     """Convert fluxes in a catalog from jansky to nanojansky. 
   94     catalog : `lsst.afw.table.SimpleCatalog` 
   95         The catalog to convert. 
   97         Log to send messages to. 
   98     doConvert : `bool`, optional 
   99         Return a converted catalog, or just identify the fields that need to be converted? 
  100         This supports the "write=False" mode of `bin/convert_to_nJy.py`. 
  104     catalog : `lsst.afw.table.SimpleCatalog` or None 
  105         The converted catalog, or None if ``doConvert`` is False. 
  109     Support for old units in reference catalogs will be removed after the 
  110     release of late calendar year 2019. 
  111     Use `meas_algorithms/bin/convert_to_nJy.py` to update your reference catalog. 
  119     for field 
in catalog.schema:
 
  120         oldName = field.field.getName()
 
  121         oldUnits = field.field.getUnits()
 
  125             if oldName.endswith(
'_fluxSigma'):
 
  126                 name = oldName.replace(
'_fluxSigma', 
'_fluxErr')
 
  130             mapper.addMapping(field.getKey(), newField)
 
  131             input_fields.append(field.field)
 
  132             output_fields.append(newField)
 
  134             mapper.addMapping(field.getKey())
 
  136     fluxFieldsStr = 
'; '.join(
"(%s, '%s')" % (field.getName(), field.getUnits()) 
for field 
in input_fields)
 
  139         newSchema = mapper.getOutputSchema()
 
  141         output.extend(catalog, mapper=mapper)
 
  142         for field 
in output_fields:
 
  143             output[field.getName()] *= 1e9
 
  144         log.info(f
"Converted refcat flux fields to nJy (name, units): {fluxFieldsStr}")
 
  147         log.info(f
"Found old-style refcat flux fields (name, units): {fluxFieldsStr}")
 
  152     """This is a private helper class which filters catalogs by 
  153     row based on the row being inside the region used to initialize 
  158     region : `lsst.sphgeom.Region` 
  159         The spatial region which all objects should lie within 
  165         """This call method on an instance of this class takes in a reference 
  166         catalog, and the region from which the catalog was generated. 
  168         If the catalog region is entirely contained within the region used to 
  169         initialize this class, then all the entries in the catalog must be 
  170         within the region and so the whole catalog is returned. 
  172         If the catalog region is not entirely contained, then the location for 
  173         each record is tested against the region used to initialize the class. 
  174         Records which fall inside this region are added to a new catalog, and 
  175         this catalog is then returned. 
  179         refCat : `lsst.afw.table.SourceCatalog` 
  180             SourceCatalog to be filtered. 
  181         catRegion : `lsst.sphgeom.Region` 
  182             Region in which the catalog was created 
  184         if catRegion.isWithin(self.
region):
 
  188         filteredRefCat = 
type(refCat)(refCat.table)
 
  189         for record 
in refCat:
 
  191                 filteredRefCat.append(record)
 
  192         return filteredRefCat
 
  196     """ This class facilitates loading reference catalogs with gen 3 middleware 
  198     The middleware preflight solver will create a list of datarefs that may 
  199     possibly overlap a given region. These datarefs are then used to construct 
  200     and instance of this class. The class instance should then be passed into 
  201     a task which needs reference catalogs. These tasks should then determine 
  202     the exact region of the sky reference catalogs will be loaded for, and 
  203     call a corresponding method to load the reference objects. 
  205     def __init__(self, dataIds, refCats, config, log=None):
 
  206         """ Constructs an instance of ReferenceObjectLoader 
  210         dataIds : iterable of `lsst.daf.butler.DataIds` 
  211             An iterable object of DataSetRefs which point to reference catalogs 
  212             in a gen 3 repository 
  213         refCats : Iterable of `lsst.daf.butler.DeferedDatasetHandle` 
  214             Handles to load refCats on demand 
  216             Logger object used to write out messages. If `None` (default) the default 
  217             lsst logger will be used 
  226     def _makeBoxRegion(BBox, wcs, BBoxPadding):
 
  233         outerLocalBBox.grow(BBoxPadding)
 
  234         innerLocalBBox.grow(-1*BBoxPadding)
 
  242             innerBoxCorners = innerLocalBBox.getCorners()
 
  243             innerSphCorners = [wcs.pixelToSky(corner).getVector() 
for corner 
in innerBoxCorners]
 
  248         outerBoxCorners = outerLocalBBox.getCorners()
 
  249         outerSphCorners = [wcs.pixelToSky(corner).getVector() 
for corner 
in outerBoxCorners]
 
  253         return innerSkyRegion, outerSkyRegion, innerSphCorners, outerSphCorners
 
  255     def loadPixelBox(self, bbox, wcs, filterName=None, epoch=None, photoCalib=None, bboxPadding=100):
 
  256         """Load reference objects that are within a pixel-based rectangular region 
  258         This algorithm works by creating a spherical box whose corners correspond 
  259         to the WCS converted corners of the input bounding box (possibly padded). 
  260         It then defines a filtering function which will look at a reference 
  261         objects pixel position and accept objects that lie within the specified 
  264         The spherical box region and filtering function are passed to the generic 
  265         loadRegion method which will load and filter the reference objects from 
  266         the datastore and return a single catalog containing all reference objects 
  270         bbox : `lsst.geom.box2I` 
  271             Box which bounds a region in pixel space 
  272         wcs : `lsst.afw.geom.SkyWcs` 
  273             Wcs object defining the pixel to sky (and inverse) transform for the space 
  274             of pixels of the supplied bbox 
  276             Name of camera filter, or None or blank for the default filter 
  277         epoch : `astropy.time.Time` (optional) 
  278             Epoch to which to correct proper motion and parallax, 
  279             or None to not apply such corrections. 
  281             Deprecated and ignored, only included for api compatibility 
  283             Number describing how much to pad the input bbox by (in pixels), defaults 
  284             to 100. This parameter is necessary because optical distortions in telescopes 
  285             can cause a rectangular pixel grid to map into a non "rectangular" spherical 
  286             region in sky coordinates. This padding is used to create a spherical 
  287             "rectangle", which will for sure enclose the input box.  This padding is only 
  288             used to determine if the reference catalog for a sky patch will be loaded from 
  289             the data store, this function will filter out objects which lie within the 
  290             padded region but fall outside the input bounding box region. 
  294         referenceCatalog : `lsst.afw.table.SimpleCatalog` 
  295             Catalog containing reference objects inside the specified bounding box 
  299         `lsst.pex.exception.RuntimeError` 
  300             Raised if no reference catalogs could be found for the specified region 
  302         `lsst.pex.exception.TypeError` 
  303             Raised if the loaded reference catalogs do not have matching schemas 
  305         innerSkyRegion, outerSkyRegion, _, _ = self.
_makeBoxRegion(bbox, wcs, bboxPadding)
 
  307         def _filterFunction(refCat, region):
 
  314             if innerSkyRegion.contains(region):
 
  317             filteredRefCat = 
type(refCat)(refCat.table)
 
  319             for record 
in refCat:
 
  320                 pixCoords = record[centroidKey]
 
  322                     filteredRefCat.append(record)
 
  323             return filteredRefCat
 
  324         return self.
loadRegion(outerSkyRegion, filtFunc=_filterFunction, epoch=epoch, filterName=filterName)
 
  326     def loadRegion(self, region, filtFunc=None, filterName=None, epoch=None):
 
  327         """ Load reference objects within a specified region 
  329         This function loads the DataIds used to construct an instance of this class 
  330         which intersect or are contained within the specified region. The reference 
  331         catalogs which intersect but are not fully contained within the input region are 
  332         further filtered by the specified filter function. This function will return a 
  333         single source catalog containing all reference objects inside the specified region. 
  337         region : `lsst.sphgeom.Region` 
  338             This can be any type that is derived from `lsst.sphgeom.region` and should 
  339             define the spatial region for which reference objects are to be loaded. 
  341             This optional parameter should be a callable object that takes a reference 
  342             catalog and its corresponding region as parameters, filters the catalog by 
  343             some criteria and returns the filtered reference catalog. If the value is 
  344             left as the default (None) than an internal filter function is used which 
  345             filters according to if a reference object falls within the input region. 
  347             Name of camera filter, or None or blank for the default filter 
  348         epoch : `astropy.time.Time` (optional) 
  349             Epoch to which to correct proper motion and parallax, 
  350             or None to not apply such corrections. 
  354         referenceCatalog : `lsst.afw.table.SourceCatalog` 
  355             Catalog containing reference objects which intersect the input region, 
  356             filtered by the specified filter function 
  360         `lsst.pex.exception.RuntimeError` 
  361             Raised if no reference catalogs could be found for the specified region 
  363         `lsst.pex.exception.TypeError` 
  364             Raised if the loaded reference catalogs do not have matching schemas 
  367         regionBounding = region.getBoundingBox()
 
  368         self.
log.
info(
"Loading reference objects from region bounded by {}, {} lat lon".
format(
 
  369             regionBounding.getLat(), regionBounding.getLon()))
 
  378                 intersects = dataId.region.intersects(region)
 
  380                 intersects = region.intersects(dataId.region)
 
  383                 overlapList.append((dataId, refCat))
 
  385         if len(overlapList) == 0:
 
  388         firstCat = overlapList[0][1].get()
 
  389         refCat = filtFunc(firstCat, overlapList[0][0].region)
 
  390         trimmedAmount = len(firstCat) - len(refCat)
 
  393         for dataId, inputRefCat 
in overlapList[1:]:
 
  394             tmpCat = inputRefCat.get()
 
  396             if tmpCat.schema != firstCat.schema:
 
  399             filteredCat = filtFunc(tmpCat, dataId.region)
 
  400             refCat.extend(filteredCat)
 
  401             trimmedAmount += len(tmpCat) - len(filteredCat)
 
  403         self.
log.
debug(f
"Trimmed {trimmedAmount} out of region objects, leaving {len(refCat)}")
 
  404         self.
log.
info(f
"Loaded {len(refCat)} reference objects")
 
  406         if epoch 
is not None and "pm_ra" in refCat.schema:
 
  408             if isinstance(refCat.schema[
"pm_ra"].asKey(), lsst.afw.table.KeyAngle):
 
  411                 self.
log.
warn(
"Catalog pm_ra field is not an Angle; not applying proper motion")
 
  416             self.
log.
warn(
"Found version 0 reference catalog with old style units in schema.")
 
  417             self.
log.
warn(
"run `meas_algorithms/bin/convert_refcat_to_nJy.py` to convert fluxes to nJy.")
 
  418             self.
log.
warn(
"See RFC-575 for more details.")
 
  427         if not expandedCat.isContiguous():
 
  428             expandedCat = refCat.copy(deep=
True)
 
  430         fluxField = 
getRefFluxField(schema=expandedCat.schema, filterName=filterName)
 
  431         return pipeBase.Struct(refCat=expandedCat, fluxField=fluxField)
 
  434         """Load reference objects that lie within a circular region on the sky 
  436         This method constructs a circular region from an input center and angular radius, 
  437         loads reference catalogs which are contained in or intersect the circle, and 
  438         filters reference catalogs which intersect down to objects which lie within 
  443         ctrCoord : `lsst.geom.SpherePoint` 
  444             Point defining the center of the circular region 
  445         radius : `lsst.geom.Angle` 
  446             Defines the angular radius of the circular region 
  448             Name of camera filter, or None or blank for the default filter 
  449         epoch : `astropy.time.Time` (optional) 
  450             Epoch to which to correct proper motion and parallax, 
  451             or None to not apply such corrections. 
  455         referenceCatalog : `lsst.afw.table.SourceCatalog` 
  456             Catalog containing reference objects inside the specified bounding box 
  460         `lsst.pex.exception.RuntimeError` 
  461             Raised if no reference catalogs could be found for the specified region 
  463         `lsst.pex.exception.TypeError` 
  464             Raised if the loaded reference catalogs do not have matching schemas 
  467         centerVector = ctrCoord.getVector()
 
  470         return self.
loadRegion(circularRegion, filterName=filterName, epoch=
None)
 
  473         """Relink an unpersisted match list to sources and reference 
  476         A match list is persisted and unpersisted as a catalog of IDs 
  477         produced by afw.table.packMatches(), with match metadata 
  478         (as returned by the astrometry tasks) in the catalog's metadata 
  479         attribute. This method converts such a match catalog into a match 
  480         list, with links to source records and reference object records. 
  484         matchCat : `lsst.afw.table.BaseCatalog` 
  485             Unpersisted packed match list. 
  486             ``matchCat.table.getMetadata()`` must contain match metadata, 
  487             as returned by the astrometry tasks. 
  488         sourceCat : `lsst.afw.table.SourceCatalog` 
  489             Source catalog. As a side effect, the catalog will be sorted 
  494         matchList : `lsst.afw.table.ReferenceMatchVector` 
  500     def getMetadataBox(cls, bbox, wcs, filterName=None, photoCalib=None, epoch=None, bboxPadding=100):
 
  501         """Return metadata about the load 
  503         This metadata is used for reloading the catalog (e.g., for 
  504         reconstituting a normalised match list.) 
  508         bbox : `lsst.geom.Box2I` 
  509             Bounding bos for the pixels 
  510         wcs : `lsst.afw.geom.SkyWcs 
  512         filterName : `str` or None 
  513             filterName of the camera filter, or None or blank for the default filter 
  515             Deprecated, only included for api compatibility 
  516         epoch : `astropy.time.Time` (optional) 
  517             Epoch to which to correct proper motion and parallax, 
  518             or None to not apply such corrections. 
  520             Number describing how much to pad the input bbox by (in pixels), defaults 
  521             to 100. This parameter is necessary because optical distortions in telescopes 
  522             can cause a rectangular pixel grid to map into a non "rectangular" spherical 
  523             region in sky coordinates. This padding is used to create a spherical 
  524             "rectangle", which will for sure enclose the input box.  This padding is only 
  525             used to determine if the reference catalog for a sky patch will be loaded from 
  526             the data store, this function will filter out objects which lie within the 
  527             padded region but fall outside the input bounding box region. 
  530         md : `lsst.daf.base.PropertyList` 
  532         _, _, innerCorners, outerCorners = cls.
_makeBoxRegion(bbox, wcs, bboxPadding)
 
  534         for box, corners 
in zip((
"INNER", 
"OUTER"), (innerCorners, outerCorners)):
 
  535             for (name, corner) 
in zip((
"UPPER_LEFT", 
"UPPER_RIGHT", 
"LOWER_LEFT", 
"LOWER_RIGHT"),
 
  537                 md.add(f
"{box}_{name}_RA", 
geom.SpherePoint(corner).getRa().asDegrees(), f
"{box}_corner")
 
  538                 md.add(f
"{box}_{name}_DEC", 
geom.SpherePoint(corner).getDec().asDegrees(), f
"{box}_corner")
 
  539         md.add(
"SMATCHV", 1, 
'SourceMatchVector version number')
 
  540         filterName = 
"UNKNOWN" if filterName 
is None else str(filterName)
 
  541         md.add(
'FILTER', filterName, 
'filter name for photometric data')
 
  544         md.add(
'EPOCH', 
"NONE" if epoch 
is None else str(epoch), 
'Epoch (TAI MJD) for catalog')
 
  549         """Return metadata about the load 
  551         This metadata is used for reloading the catalog (e.g. for reconstituting 
  552         a normalized match list.) 
  556         coord : `lsst.geom.SpherePoint` 
  557             ICRS center of a circle 
  558         radius : `lsst.geom.angle` 
  560         filterName : `str` or None 
  561             filterName of the camera filter, or None or blank for the default filter 
  563             Deprecated, only included for api compatibility 
  564         epoch : `astropy.time.Time` (optional) 
  565             Epoch to which to correct proper motion and parallax, 
  566             or None to not apply such corrections. 
  570         md : `lsst.daf.base.PropertyList` 
  573         md.add(
'RA', coord.getRa().asDegrees(), 
'field center in degrees')
 
  574         md.add(
'DEC', coord.getDec().asDegrees(), 
'field center in degrees')
 
  575         md.add(
'RADIUS', radius.asDegrees(), 
'field radius in degrees, minimum')
 
  576         md.add(
'SMATCHV', 1, 
'SourceMatchVector version number')
 
  577         filterName = 
"UNKNOWN" if filterName 
is None else str(filterName)
 
  578         md.add(
'FILTER', filterName, 
'filter name for photometric data')
 
  579         md.add(
'EPOCH', 
"NONE" if epoch 
is None else epoch, 
'Epoch (TAI MJD) for catalog')
 
  584         """This function creates a new catalog containing the information of the input refCat 
  585         as well as added flux columns and aliases between camera and reference flux. 
  589         refCat : `lsst.afw.table.SimpleCatalog` 
  590             Catalog of reference objects 
  591         defaultFilter : `str` 
  592             Name of the default reference filter 
  593         filterReferenceMap : `dict` of `str` 
  594             Dictionary with keys corresponding to a filter name, and values which 
  595             correspond to the name of the reference filter. 
  599         refCat : `lsst.afw.table.SimpleCatalog` 
  600             Reference catalog with columns added to track reference filters 
  605             If specified reference filter name is not a filter specifed as a key in the 
  606             reference filter map. 
  608         refCat = ReferenceObjectLoader.remapReferenceCatalogSchema(refCat,
 
  609                                                                    filterNameList=filterReferenceMap.keys())
 
  610         aliasMap = refCat.schema.getAliasMap()
 
  611         if filterReferenceMap 
is None:
 
  612             filterReferenceMap = {}
 
  613         for filterName, refFilterName 
in itertools.chain([(
None, defaultFilter)],
 
  614                                                          filterReferenceMap.items()):
 
  616                 camFluxName = filterName + 
"_camFlux" if filterName 
is not None else "camFlux" 
  617                 refFluxName = refFilterName + 
"_flux" 
  618                 if refFluxName 
not in refCat.schema:
 
  619                     raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
 
  620                 aliasMap.set(camFluxName, refFluxName)
 
  622                 refFluxErrName = refFluxName + 
"Err" 
  623                 camFluxErrName = camFluxName + 
"Err" 
  624                 aliasMap.set(camFluxErrName, refFluxErrName)
 
  630         """This function takes in a reference catalog and creates a new catalog with additional 
  631         columns defined the remaining function arguments. 
  635         refCat : `lsst.afw.table.SimpleCatalog` 
  636             Reference catalog to map to new catalog 
  640         expandedCat : `lsst.afw.table.SimpleCatalog` 
  641             Deep copy of input reference catalog with additional columns added 
  644         mapper.addMinimalSchema(refCat.schema, 
True)
 
  645         mapper.editOutputSchema().disconnectAliases()
 
  647             for filterName 
in filterNameList:
 
  648                 mapper.editOutputSchema().addField(f
"{filterName}_flux",
 
  650                                                    doc=f
"flux in filter {filterName}",
 
  653                 mapper.editOutputSchema().addField(f
"{filterName}_fluxErr",
 
  655                                                    doc=f
"flux uncertanty in filter {filterName}",
 
  660             mapper.editOutputSchema().addField(
"centroid_x", type=float, doReplace=
True)
 
  661             mapper.editOutputSchema().addField(
"centroid_y", type=float, doReplace=
True)
 
  662             mapper.editOutputSchema().addField(
"hasCentroid", type=
"Flag", doReplace=
True)
 
  663             mapper.editOutputSchema().getAliasMap().
set(
"slot_Centroid", 
"centroid")
 
  666             mapper.editOutputSchema().addField(
"photometric",
 
  668                                                doc=
"set if the object can be used for photometric" 
  671             mapper.editOutputSchema().addField(
"resolved",
 
  673                                                doc=
"set if the object is spatially resolved" 
  675             mapper.editOutputSchema().addField(
"variable",
 
  677                                                doc=
"set if the object has variable brightness" 
  681         expandedCat.setMetadata(refCat.getMetadata())
 
  682         expandedCat.extend(refCat, mapper=mapper)
 
  688     """Get the name of a flux field from a schema. 
  690     if filterName is specified: 
  691         return *filterName*_camFlux if present 
  692         else return *filterName*_flux if present (camera filter name matches reference filter name) 
  693         else throw RuntimeError 
  695         return camFlux, if present, 
  696         else throw RuntimeError 
  700     schema : `lsst.afw.table.Schema` 
  701         Reference catalog schema. 
  703         Name of camera filter. 
  707     fluxFieldName : `str` 
  713         If an appropriate field is not found. 
  716         raise RuntimeError(
"schema=%s is not a schema" % (schema,))
 
  718         fluxFieldList = [filterName + 
"_camFlux", filterName + 
"_flux"]
 
  720         fluxFieldList = [
"camFlux"]
 
  721     for fluxField 
in fluxFieldList:
 
  722         if fluxField 
in schema:
 
  725     raise RuntimeError(
"Could not find flux field(s) %s" % (
", ".join(fluxFieldList)))
 
  729     """Return keys for flux and flux error. 
  733     schema : `lsst.afw.table.Schema` 
  734         Reference catalog schema. 
  736         Name of camera filter. 
  740     keys : `tuple` of (`lsst.afw.table.Key`, `lsst.afw.table.Key`) 
  744         - flux error key, if present, else None 
  749         If flux field not found. 
  752     fluxErrField = fluxField + 
"Err" 
  753     fluxKey = schema[fluxField].asKey()
 
  755         fluxErrKey = schema[fluxErrField].asKey()
 
  758     return (fluxKey, fluxErrKey)
 
  762     pixelMargin = pexConfig.RangeField(
 
  763         doc=
"Padding to add to 4 all edges of the bounding box (pixels)",
 
  768     defaultFilter = pexConfig.Field(
 
  769         doc=
"Default reference catalog filter to use if filter not specified in exposure; " 
  770         "if blank then filter must be specified in exposure",
 
  774     filterMap = pexConfig.DictField(
 
  775         doc=
"Mapping of camera filter name: reference catalog filter name; " 
  776         "each reference filter must exist",
 
  781     requireProperMotion = pexConfig.Field(
 
  782         doc=
"Require that the fields needed to correct proper motion " 
  783             "(epoch, pm_ra and pm_dec) are present?",
 
  798     r"""!Abstract base class to load objects from reference catalogs 
  800     @anchor LoadReferenceObjectsTask_ 
  802     @section meas_algorithms_loadReferenceObjects_Contents Contents 
  804      - @ref meas_algorithms_loadReferenceObjects_Purpose 
  805      - @ref meas_algorithms_loadReferenceObjects_Initialize 
  806      - @ref meas_algorithms_loadReferenceObjects_IO 
  807      - @ref meas_algorithms_loadReferenceObjects_Schema 
  808      - @ref meas_algorithms_loadReferenceObjects_Config 
  810     @section meas_algorithms_loadReferenceObjects_Purpose  Description 
  812     Abstract base class for tasks that load objects from a reference catalog 
  813     in a particular region of the sky. 
  815     Implementations must subclass this class, override the loadSkyCircle method, 
  816     and will typically override the value of ConfigClass with a task-specific config class. 
  818     @section meas_algorithms_loadReferenceObjects_Initialize   Task initialisation 
  820     @copydoc \_\_init\_\_ 
  822     @section meas_algorithms_loadReferenceObjects_IO       Invoking the Task 
  824     @copydoc loadPixelBox 
  826     @section meas_algorithms_loadReferenceObjects_Schema       Schema of the reference object catalog 
  828     Reference object catalogs are instances of lsst.afw.table.SimpleCatalog with the following schema 
  829     (other fields may also be present). 
  830     The units use astropy quantity conventions, so a 2 suffix means squared. 
  831     See also makeMinimalSchema. 
  832     - coord: ICRS position of star on sky (an lsst.geom.SpherePoint) 
  833     - centroid: position of star on an exposure, if relevant (an lsst.afw.Point2D) 
  834     - hasCentroid: is centroid usable? (a Flag) 
  835     - *referenceFilterName*_flux: brightness in the specified reference catalog filter (nJy) 
  836         Note: you can use astropy.units to convert from AB Magnitude to nJy: 
  837             `u.Magnitude(value, u.ABmag).to_value(u.nJy)` 
  838     - *referenceFilterName*_fluxErr (optional): brightness standard deviation (nJy); 
  839         omitted if no data is available; possibly nan if data is available for some objects but not others 
  840     - camFlux: brightness in default camera filter (nJy); omitted if defaultFilter not specified 
  841     - camFluxErr: brightness standard deviation for default camera filter; 
  842         omitted if defaultFilter not specified or standard deviation not available that filter 
  843     - *cameraFilterName*_camFlux: brightness in specified camera filter (nJy) 
  844     - *cameraFilterName*_camFluxErr (optional): brightness standard deviation 
  845         in specified camera filter (nJy); omitted if no data is available; 
  846         possibly nan if data is available for some objects but not others 
  847     - photometric (optional): is the object usable for photometric calibration? (a Flag) 
  848     - resolved (optional): is the object spatially resolved? (a Flag) 
  849     - variable (optional): does the object have variable brightness? (a Flag) 
  850     - coord_raErr: uncertainty in `coord` along the direction of right ascension (radian, an Angle) 
  851                     = uncertainty in ra * cos(dec); nan if unknown 
  852     - coord_decErr: uncertainty in `coord` along the direction of declination (radian, an Angle); 
  855     The following are optional; fields should only be present if the 
  856     information is available for at least some objects. 
  857     Numeric values are `nan` if unknown: 
  858     - epoch: date of observation as TAI MJD (day) 
  860     - pm_ra: proper motion along the direction of right ascension (rad/year, an Angle) = dra/dt * cos(dec) 
  861     - pm_dec: proper motion along the direction of declination (rad/year, and Angle) 
  862     - pm_raErr: uncertainty in `pm_ra` (rad/year) 
  863     - pm_decErr: uncertainty in `pm_dec` (rad/year) 
  864     - pm_ra_dec_Cov: covariance between pm_ra and pm_dec (rad2/year2) 
  865     - pm_flag: set if proper motion, error or covariance is bad 
  867     - parallax: parallax (rad, an Angle) 
  868     - parallaxErr: uncertainty in `parallax` (rad) 
  869     - parallax_flag: set if parallax value or parallaxErr is bad 
  871     - coord_ra_pm_ra_Cov: covariance between coord_ra and pm_ra (rad2/year) 
  872     - coord_ra_pm_dec_Cov: covariance between coord_ra and pm_dec (rad2/year) 
  873     - coord_ra_parallax_Cov: covariance between coord_ra and parallax (rad2/year) 
  874     - coord_dec_pm_ra_Cov: covariance between coord_dec and pm_ra (rad2/year) 
  875     - coord_dec_pm_dec_Cov: covariance between coord_dec and pm_dec (rad2/year) 
  876     - coord_dec_parallax_Cov: covariance between coord_dec and parallax (rad2/year) 
  877     - pm_ra_parallax_Cov: covariance between pm_ra and parallax (rad2/year) 
  878     - pm_dec_parallax_Cov: covariance between pm_dec and parallax (rad2/year) 
  880     @section meas_algorithms_loadReferenceObjects_Config       Configuration parameters 
  882     See @ref LoadReferenceObjectsConfig for a base set of configuration parameters. 
  883     Most subclasses will add configuration variables. 
  885     ConfigClass = LoadReferenceObjectsConfig
 
  886     _DefaultName = 
"LoadReferenceObjects" 
  889         """Construct a LoadReferenceObjectsTask 
  893         butler : `lsst.daf.persistence.Butler` 
  894             Data butler, for access reference catalogs. 
  896         pipeBase.Task.__init__(self, *args, **kwargs)
 
  900     def loadPixelBox(self, bbox, wcs, filterName=None, photoCalib=None, epoch=None):
 
  901         """Load reference objects that overlap a rectangular pixel region. 
  905         bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 
  906             Bounding box for pixels. 
  907         wcs : `lsst.afw.geom.SkyWcs` 
  908             WCS; used to convert pixel positions to sky coordinates 
  911             Name of filter, or `None` or `""` for the default filter. 
  912             This is used for flux values in case we have flux limits 
  913             (which are not yet implemented). 
  914         photoCalib : `lsst.afw.image.PhotoCalib` (optional) 
  915             Calibration, or `None` if unknown. 
  916         epoch : `astropy.time.Time` (optional) 
  917             Epoch to which to correct proper motion and parallax, 
  918             or None to not apply such corrections. 
  922         results : `lsst.pipe.base.Struct` 
  923             A Struct containing the following fields: 
  924             refCat : `lsst.afw.catalog.SimpleCatalog` 
  925                 A catalog of reference objects with the standard 
  926                 schema, as documented in the main doc string for 
  927                 `LoadReferenceObjects`. 
  928                 The catalog is guaranteed to be contiguous. 
  930                 Name of flux field for specified `filterName`. 
  934         The search algorithm works by searching in a region in sky 
  935         coordinates whose center is the center of the bbox and radius 
  936         is large enough to just include all 4 corners of the bbox. 
  937         Stars that lie outside the bbox are then trimmed from the list. 
  942         self.log.
info(
"Loading reference objects using center %s and radius %s deg" %
 
  943                       (circle.coord, circle.radius.asDegrees()))
 
  944         loadRes = self.
loadSkyCircle(circle.coord, circle.radius, filterName, centroids=
True)
 
  945         refCat = loadRes.refCat
 
  946         numFound = len(refCat)
 
  949         refCat = self.
_trimToBBox(refCat=refCat, bbox=circle.bbox, wcs=wcs)
 
  950         numTrimmed = numFound - len(refCat)
 
  951         self.log.
debug(
"trimmed %d out-of-bbox objects, leaving %d", numTrimmed, len(refCat))
 
  952         self.log.
info(
"Loaded %d reference objects", len(refCat))
 
  955         if not refCat.isContiguous():
 
  956             loadRes.refCat = refCat.copy(deep=
True)
 
  961     def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None, centroids=False):
 
  962         """Load reference objects that overlap a circular sky region. 
  966         ctrCoord : `lsst.geom.SpherePoint` 
  967             ICRS center of search region. 
  968         radius : `lsst.geom.Angle` 
  969             Radius of search region. 
  970         filterName : `str` (optional) 
  971             Name of filter, or `None` or `""` for the default filter. 
  972             This is used for flux values in case we have flux limits 
  973             (which are not yet implemented). 
  974         epoch : `astropy.time.Time` (optional) 
  975             Epoch to which to correct proper motion and parallax, 
  976             or None to not apply such corrections. 
  977         centroids : `bool` (optional) 
  978             Add centroid fields to the loaded Schema. ``loadPixelBox`` expects 
  979             these fields to exist. 
  983         results : `lsst.pipe.base.Struct` 
  984             A Struct containing the following fields: 
  985             refCat : `lsst.afw.catalog.SimpleCatalog` 
  986                 A catalog of reference objects with the standard 
  987                 schema, as documented in the main doc string for 
  988                 `LoadReferenceObjects`. 
  989                 The catalog is guaranteed to be contiguous. 
  991                 Name of flux field for specified `filterName`. 
  995         Note that subclasses are responsible for performing the proper motion 
  996         correction, since this is the lowest-level interface for retrieving 
 1002     def _trimToBBox(refCat, bbox, wcs):
 
 1003         """Remove objects outside a given pixel bounding box and set 
 1004         centroid and hasCentroid fields. 
 1008         refCat : `lsst.afw.table.SimpleCatalog` 
 1009             A catalog of objects. The schema must include fields 
 1010             "coord", "centroid" and "hasCentroid". 
 1011             The "coord" field is read. 
 1012             The "centroid" and "hasCentroid" fields are set. 
 1013         bbox : `lsst.geom.Box2D` 
 1015         wcs : `lsst.afw.geom.SkyWcs` 
 1016             WCS; used to convert sky coordinates to pixel positions. 
 1018         @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set 
 1022         retStarCat = 
type(refCat)(refCat.table)
 
 1024             point = star.get(centroidKey)
 
 1025             if bbox.contains(point):
 
 1026                 retStarCat.append(star)
 
 1029     def _addFluxAliases(self, schema):
 
 1030         """Add aliases for camera filter fluxes to the schema. 
 1032         If self.config.defaultFilter then adds these aliases: 
 1033             camFlux:      <defaultFilter>_flux 
 1034             camFluxErr: <defaultFilter>_fluxErr, if the latter exists 
 1036         For each camFilter: refFilter in self.config.filterMap adds these aliases: 
 1037             <camFilter>_camFlux:      <refFilter>_flux 
 1038             <camFilter>_camFluxErr: <refFilter>_fluxErr, if the latter exists 
 1042         schema : `lsst.afw.table.Schema` 
 1043             Schema for reference catalog. 
 1048             If any reference flux field is missing from the schema. 
 1050         aliasMap = schema.getAliasMap()
 
 1052         def addAliasesForOneFilter(filterName, refFilterName):
 
 1053             """Add aliases for a single filter 
 1057             filterName : `str` (optional) 
 1058                 Camera filter name. The resulting alias name is 
 1059                 <filterName>_camFlux, or simply "camFlux" if `filterName` 
 1061             refFilterName : `str` 
 1062                 Reference catalog filter name; the field 
 1063                 <refFilterName>_flux must exist. 
 1065             camFluxName = filterName + 
"_camFlux" if filterName 
is not None else "camFlux" 
 1066             refFluxName = refFilterName + 
"_flux" 
 1067             if refFluxName 
not in schema:
 
 1068                 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
 
 1069             aliasMap.set(camFluxName, refFluxName)
 
 1070             refFluxErrName = refFluxName + 
"Err" 
 1071             if refFluxErrName 
in schema:
 
 1072                 camFluxErrName = camFluxName + 
"Err" 
 1073                 aliasMap.set(camFluxErrName, refFluxErrName)
 
 1075         if self.config.defaultFilter:
 
 1076             addAliasesForOneFilter(
None, self.config.defaultFilter)
 
 1078         for filterName, refFilterName 
in self.config.filterMap.items():
 
 1079             addAliasesForOneFilter(filterName, refFilterName)
 
 1083                           addIsPhotometric=False, addIsResolved=False,
 
 1084                           addIsVariable=False, coordErrDim=2,
 
 1085                           addProperMotion=False, properMotionErrDim=2,
 
 1087         """Make a standard schema for reference object catalogs. 
 1091         filterNameList : `list` of `str` 
 1092             List of filter names. Used to create <filterName>_flux fields. 
 1093         addIsPhotometric : `bool` 
 1094             If True then add field "photometric". 
 1095         addIsResolved : `bool` 
 1096             If True then add field "resolved". 
 1097         addIsVariable : `bool` 
 1098             If True then add field "variable". 
 1100             Number of coord error fields; must be one of 0, 2, 3: 
 1102             - If 2 or 3: add fields "coord_raErr" and "coord_decErr". 
 1103             - If 3: also add field "coord_radecErr". 
 1104         addProperMotion : `bool` 
 1105             If True add fields "epoch", "pm_ra", "pm_dec" and "pm_flag". 
 1106         properMotionErrDim : `int` 
 1107             Number of proper motion error fields; must be one of 0, 2, 3; 
 1108             ignored if addProperMotion false: 
 1109             - If 2 or 3: add fields "pm_raErr" and "pm_decErr". 
 1110             - If 3: also add field "pm_radecErr". 
 1111         addParallax : `bool` 
 1112             If True add fields "epoch", "parallax", "parallaxErr" 
 1113             and "parallax_flag". 
 1117         schema : `lsst.afw.table.Schema` 
 1118             Schema for reference catalog, an 
 1119             `lsst.afw.table.SimpleCatalog`. 
 1123         Reference catalogs support additional covariances, such as 
 1124         covariance between RA and proper motion in declination, 
 1125         that are not supported by this method, but can be added after 
 1126         calling this method. 
 1128         schema = afwTable.SimpleTable.makeMinimalSchema()
 
 1130             afwTable.Point2DKey.addFields(
 
 1133                 "centroid on an exposure, if relevant",
 
 1137                 field=
"hasCentroid",
 
 1139                 doc=
"is position known?",
 
 1141         for filterName 
in filterNameList:
 
 1143                 field=
"%s_flux" % (filterName,),
 
 1145                 doc=
"flux in filter %s" % (filterName,),
 
 1148         for filterName 
in filterNameList:
 
 1150                 field=
"%s_fluxErr" % (filterName,),
 
 1152                 doc=
"flux uncertainty in filter %s" % (filterName,),
 
 1155         if addIsPhotometric:
 
 1157                 field=
"photometric",
 
 1159                 doc=
"set if the object can be used for photometric calibration",
 
 1165                 doc=
"set if the object is spatially resolved",
 
 1171                 doc=
"set if the object has variable brightness",
 
 1173         if coordErrDim 
not in (0, 2, 3):
 
 1174             raise ValueError(
"coordErrDim={}; must be (0, 2, 3)".
format(coordErrDim))
 
 1176             afwTable.CovarianceMatrix2fKey.addFields(
 
 1179                 names=[
"ra", 
"dec"],
 
 1180                 units=[
"rad", 
"rad"],
 
 1181                 diagonalOnly=(coordErrDim == 2),
 
 1184         if addProperMotion 
or addParallax:
 
 1188                 doc=
"date of observation (TAI, MJD)",
 
 1196                 doc=
"proper motion in the right ascension direction = dra/dt * cos(dec)",
 
 1202                 doc=
"proper motion in the declination direction",
 
 1205             if properMotionErrDim 
not in (0, 2, 3):
 
 1206                 raise ValueError(
"properMotionErrDim={}; must be (0, 2, 3)".
format(properMotionErrDim))
 
 1207             if properMotionErrDim > 0:
 
 1208                 afwTable.CovarianceMatrix2fKey.addFields(
 
 1211                     names=[
"ra", 
"dec"],
 
 1212                     units=[
"rad/year", 
"rad/year"],
 
 1213                     diagonalOnly=(properMotionErrDim == 2),
 
 1218                 doc=
"Set if proper motion or proper motion error is bad",
 
 1229                 field=
"parallaxErr",
 
 1231                 doc=
"uncertainty in parallax",
 
 1235                 field=
"parallax_flag",
 
 1237                 doc=
"Set if parallax or parallax error is bad",
 
 1241     def _calculateCircle(self, bbox, wcs):
 
 1242         """Compute on-sky center and radius of search region. 
 1246         bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 
 1248         wcs : `lsst.afw.geom.SkyWcs` 
 1249             WCS; used to convert pixel positions to sky coordinates. 
 1253         results : `lsst.pipe.base.Struct` 
 1254             A Struct containing: 
 1256             - coord : `lsst.geom.SpherePoint` 
 1257                 ICRS center of the search region. 
 1258             - radius : `lsst.geom.Angle` 
 1259                 Radius of the search region. 
 1260             - bbox : `lsst.geom.Box2D` 
 1261                 Bounding box used to compute the circle. 
 1264         bbox.grow(self.config.pixelMargin)
 
 1265         coord = wcs.pixelToSky(bbox.getCenter())
 
 1266         radius = 
max(coord.separation(wcs.pixelToSky(pp)) 
for pp 
in bbox.getCorners())
 
 1267         return pipeBase.Struct(coord=coord, radius=radius, bbox=bbox)
 
 1270         """Return metadata about the load. 
 1272         This metadata is used for reloading the catalog (e.g., for 
 1273         reconstituting a normalised match list. 
 1277         bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 
 1279         wcs : `lsst.afw.geom.SkyWcs` 
 1280             WCS; used to convert pixel positions to sky coordinates. 
 1282             Name of camera filter, or `None` or `""` for the default 
 1284         photoCalib : `lsst.afw.image.PhotoCalib` (optional) 
 1285             Calibration, or `None` if unknown. 
 1286         epoch : `astropy.time.Time` (optional) 
 1287             Epoch to which to correct proper motion and parallax, 
 1288             or None to not apply such corrections. 
 1292         metadata : lsst.daf.base.PropertyList 
 1293             Metadata about the load. 
 1296         return self.
getMetadataCircle(circle.coord, circle.radius, filterName, photoCalib)
 
 1299         """Return metadata about the load. 
 1301         This metadata is used for reloading the catalog (e.g., for 
 1302         reconstituting a normalised match list. 
 1306         coord : `lsst.geom.SpherePoint` 
 1307             ICRS center of the search region. 
 1308         radius : `lsst.geom.Angle` 
 1309             Radius of the search region. 
 1311             Name of camera filter, or `None` or `""` for the default 
 1313         photoCalib : `lsst.afw.image.PhotoCalib` (optional) 
 1314             Calibration, or `None` if unknown. 
 1315         epoch : `astropy.time.Time` (optional) 
 1316             Epoch to which to correct proper motion and parallax, 
 1317             or None to not apply such corrections. 
 1321         metadata : lsst.daf.base.PropertyList 
 1322             Metadata about the load 
 1325         md.add(
'RA', coord.getRa().asDegrees(), 
'field center in degrees')
 
 1326         md.add(
'DEC', coord.getDec().asDegrees(), 
'field center in degrees')
 
 1327         md.add(
'RADIUS', radius.asDegrees(), 
'field radius in degrees, minimum')
 
 1328         md.add(
'SMATCHV', 1, 
'SourceMatchVector version number')
 
 1329         filterName = 
"UNKNOWN" if filterName 
is None else str(filterName)
 
 1330         md.add(
'FILTER', filterName, 
'filter name for photometric data')
 
 1331         md.add(
'EPOCH', 
"NONE" if epoch 
is None else epoch, 
'Epoch (TAI MJD) for catalog')
 
 1335         """Relink an unpersisted match list to sources and reference 
 1338         A match list is persisted and unpersisted as a catalog of IDs 
 1339         produced by afw.table.packMatches(), with match metadata 
 1340         (as returned by the astrometry tasks) in the catalog's metadata 
 1341         attribute. This method converts such a match catalog into a match 
 1342         list, with links to source records and reference object records. 
 1346         matchCat : `lsst.afw.table.BaseCatalog` 
 1347             Unperisted packed match list. 
 1348             ``matchCat.table.getMetadata()`` must contain match metadata, 
 1349             as returned by the astrometry tasks. 
 1350         sourceCat : `lsst.afw.table.SourceCatalog` 
 1351             Source catalog. As a side effect, the catalog will be sorted 
 1356         matchList : `lsst.afw.table.ReferenceMatchVector` 
 1362         """Apply proper motion correction to a reference catalog. 
 1364         Adjust position and position error in the ``catalog`` 
 1365         for proper motion to the specified ``epoch``, 
 1366         modifying the catalong in place. 
 1370         catalog : `lsst.afw.table.SimpleCatalog` 
 1371             Catalog of positions, containing: 
 1373             - Coordinates, retrieved by the table's coordinate key. 
 1374             - ``coord_raErr`` : Error in Right Ascension (rad). 
 1375             - ``coord_decErr`` : Error in Declination (rad). 
 1376             - ``pm_ra`` : Proper motion in Right Ascension (rad/yr, 
 1378             - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional. 
 1379             - ``pm_dec`` : Proper motion in Declination (rad/yr, 
 1381             - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional. 
 1382             - ``epoch`` : Mean epoch of object (an astropy.time.Time) 
 1383         epoch : `astropy.time.Time` (optional) 
 1384             Epoch to which to correct proper motion and parallax, 
 1385             or None to not apply such corrections. 
 1387         if (
"epoch" not in catalog.schema 
or "pm_ra" not in catalog.schema 
or "pm_dec" not in catalog.schema):
 
 1388             if self.config.requireProperMotion:
 
 1389                 raise RuntimeError(
"Proper motion correction required but not available from catalog")
 
 1390             self.log.
warn(
"Proper motion correction not available from catalog")
 
 1396     """Relink an unpersisted match list to sources and reference 
 1399     A match list is persisted and unpersisted as a catalog of IDs 
 1400     produced by afw.table.packMatches(), with match metadata 
 1401     (as returned by the astrometry tasks) in the catalog's metadata 
 1402     attribute. This method converts such a match catalog into a match 
 1403     list, with links to source records and reference object records. 
 1408         Reference object loader to use in getting reference objects 
 1409     matchCat : `lsst.afw.table.BaseCatalog` 
 1410         Unperisted packed match list. 
 1411         ``matchCat.table.getMetadata()`` must contain match metadata, 
 1412         as returned by the astrometry tasks. 
 1413     sourceCat : `lsst.afw.table.SourceCatalog` 
 1414         Source catalog. As a side effect, the catalog will be sorted 
 1419     matchList : `lsst.afw.table.ReferenceMatchVector` 
 1422     matchmeta = matchCat.table.getMetadata()
 
 1423     version = matchmeta.getInt(
'SMATCHV')
 
 1425         raise ValueError(
'SourceMatchVector version number is %i, not 1.' % version)
 
 1426     filterName = matchmeta.getString(
'FILTER').
strip()
 
 1428         epoch = matchmeta.getDouble(
'EPOCH')
 
 1431     if 'RADIUS' in matchmeta:
 
 1434                                          matchmeta.getDouble(
'DEC'), lsst.geom.degrees)
 
 1435         rad = matchmeta.getDouble(
'RADIUS') * lsst.geom.degrees
 
 1436         refCat = refObjLoader.loadSkyCircle(ctrCoord, rad, filterName, epoch=epoch).refCat
 
 1437     elif "INNER_UPPER_LEFT_RA" in matchmeta:
 
 1443         for place 
in (
"UPPER_LEFT", 
"UPPER_RIGHT", 
"LOWER_LEFT", 
"LOWER_RIGHT"):
 
 1445                                           matchmeta.getDouble(f
"OUTER_{place}_DEC"),
 
 1446                                           lsst.geom.degrees).getVector()
 
 1449         refCat = refObjLoader.loadRegion(outerBox, filterName=filterName, epoch=epoch).refCat
 
 1457     """Apply proper motion correction to a reference catalog. 
 1459     Adjust position and position error in the ``catalog`` 
 1460     for proper motion to the specified ``epoch``, 
 1461     modifying the catalong in place. 
 1465     log : `lsst.log.log` 
 1466         log object to write to 
 1467     catalog : `lsst.afw.table.SimpleCatalog` 
 1468         Catalog of positions, containing: 
 1470         - Coordinates, retrieved by the table's coordinate key. 
 1471         - ``coord_raErr`` : Error in Right Ascension (rad). 
 1472         - ``coord_decErr`` : Error in Declination (rad). 
 1473         - ``pm_ra`` : Proper motion in Right Ascension (rad/yr, 
 1475         - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional. 
 1476         - ``pm_dec`` : Proper motion in Declination (rad/yr, 
 1478         - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional. 
 1479         - ``epoch`` : Mean epoch of object (an astropy.time.Time) 
 1480     epoch : `astropy.time.Time` (optional) 
 1481         Epoch to which to correct proper motion and parallax, 
 1482         or None to not apply such corrections. 
 1484     if "epoch" not in catalog.schema 
or "pm_ra" not in catalog.schema 
or "pm_dec" not in catalog.schema:
 
 1485         log.warn(
"Proper motion correction not available from catalog")
 
 1487     if not catalog.isContiguous():
 
 1488         raise RuntimeError(
"Catalog must be contiguous")
 
 1489     catEpoch = astropy.time.Time(catalog[
"epoch"], scale=
"tai", format=
"mjd")
 
 1490     log.debug(
"Correcting reference catalog for proper motion to %r", epoch)
 
 1492     timeDiffsYears = (epoch.tai - catEpoch).
to(astropy.units.yr).value
 
 1493     coordKey = catalog.table.getCoordKey()
 
 1496     pmRaRad = catalog[
"pm_ra"]
 
 1497     pmDecRad = catalog[
"pm_dec"]
 
 1498     offsetsRaRad = pmRaRad*timeDiffsYears
 
 1499     offsetsDecRad = pmDecRad*timeDiffsYears
 
 1507     offsetBearingsRad = numpy.arctan2(pmDecRad*1e6, pmRaRad*1e6)
 
 1508     offsetAmountsRad = numpy.hypot(offsetsRaRad, offsetsDecRad)
 
 1509     for record, bearingRad, amountRad 
in zip(catalog, offsetBearingsRad, offsetAmountsRad):
 
 1510         record.set(coordKey,
 
 1511                    record.get(coordKey).offset(bearing=bearingRad*lsst.geom.radians,
 
 1512                                                amount=amountRad*lsst.geom.radians))
 
 1514     if "coord_raErr" in catalog.schema:
 
 1515         catalog[
"coord_raErr"] = numpy.hypot(catalog[
"coord_raErr"],
 
 1516                                              catalog[
"pm_raErr"]*timeDiffsYears)
 
 1517     if "coord_decErr" in catalog.schema:
 
 1518         catalog[
"coord_decErr"] = numpy.hypot(catalog[
"coord_decErr"],
 
 1519                                               catalog[
"pm_decErr"]*timeDiffsYears)