24 __all__ = [
"getRefFluxField",
"getRefFluxKeys",
"LoadReferenceObjectsTask",
"LoadReferenceObjectsConfig",
25 "ReferenceObjectLoader"]
42 from lsst
import sphgeom
47 """This is a private helper class which filters catalogs by 48 row based on the row being inside the region used to initialize 53 region : `lsst.sphgeom.Region` 54 The spatial region which all objects should lie within 60 """This call method on an instance of this class takes in a reference 61 catalog, and the region from which the catalog was generated. 63 If the catalog region is entirely contained within the region used to 64 initialize this class, then all the entries in the catalog must be 65 within the region and so the whole catalog is returned. 67 If the catalog region is not entirely contained, then the location for 68 each record is tested against the region used to initialize the class. 69 Records which fall inside this region are added to a new catalog, and 70 this catalog is then returned. 74 refCat : `lsst.afw.table.SourceCatalog` 75 SourceCatalog to be filtered. 76 catRegion : `lsst.sphgeom.Region` 77 Region in which the catalog was created 79 if catRegion.isWithin(self.
region):
83 filteredRefCat = type(refCat)(refCat.table)
85 if self.
region.contains(record.getCoord().getVector()):
86 filteredRefCat.append(record)
91 """ This class facilitates loading reference catalogs with gen 3 middleware 93 The middleware preflight solver will create a list of datarefs that may 94 possibly overlap a given region. These datarefs are then used to construct 95 and instance of this class. The class instance should then be passed into 96 a task which needs reference catalogs. These tasks should then determine 97 the exact region of the sky reference catalogs will be loaded for, and 98 call a corresponding method to load the reference objects. 100 def __init__(self, dataIds, butler, config, log=None):
101 """ Constructs an instance of ReferenceObjectLoader 105 dataIds : iterable of `lsst.daf.butler.DataIds` 106 An iterable object of DataSetRefs which point to reference catalogs 107 in a gen 3 repository 108 bulter : `lsst.daf.bulter.Butler` 109 A gen 3 butler instance 111 Logger object used to write out messages. If `None` (default) the default 112 lsst logger will be used 121 def _makeBoxRegion(BBox, wcs, BBoxPadding):
128 outerLocalBBox.grow(BBoxPadding)
129 innerLocalBBox.shrink(BBoxPadding)
137 innerBoxCorners = innerLocalBBox.getCorners()
138 innerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in innerBoxCorners]
143 outerBoxCorners = outerLocalBBox.getCorners()
144 outerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in outerBoxCorners]
148 return innerSkyRegion, outerSkyRegion, innerSphCorners, outerSphCorners
150 def loadPixelBox(self, bbox, wcs, filterName=None, epoch=None, calib=None, bboxPadding=100):
151 """Load reference objects that are within a pixel-based rectangular region 153 This algorithm works by creating a spherical box whose corners correspond 154 to the WCS converted corners of the input bounding box (possibly padded). 155 It then defines a filtering function which will look at a reference 156 objects pixel position and accept objects that lie within the specified 159 The spherical box region and filtering function are passed to the generic 160 loadRegion method which will load and filter the reference objects from 161 the datastore and return a single catalog containing all reference objects 165 bbox : `lsst.geom.box2I` 166 Box which bounds a region in pixel space 167 wcs : `lsst.afw.geom.SkyWcs` 168 Wcs object defining the pixel to sky (and inverse) transform for the space 169 of pixels of the supplied bbox 171 Name of camera filter, or None or blank for the default filter 172 epoch : `astropy.time.Time` (optional) 173 Epoch to which to correct proper motion and parallax, 174 or None to not apply such corrections. 176 Deprecated and ignored, only included for api compatibility 178 Number describing how much to pad the input bbox by (in pixels), defaults 179 to 100. This parameter is necessary because optical distortions in telescopes 180 can cause a rectangular pixel grid to map into a non "rectangular" spherical 181 region in sky coordinates. This padding is used to create a spherical 182 "rectangle", which will for sure enclose the input box. This padding is only 183 used to determine if the reference catalog for a sky patch will be loaded from 184 the data store, this function will filter out objects which lie within the 185 padded region but fall outside the input bounding box region. 189 referenceCatalog : `lsst.afw.table.SimpleCatalog` 190 Catalog containing reference objects inside the specified bounding box 194 `lsst.pex.exception.RuntimeError` 195 Raised if no reference catalogs could be found for the specified region 197 `lsst.pex.exception.TypeError` 198 Raised if the loaded reference catalogs do not have matching schemas 200 innerSkyRegion, outerSkyRegion, _, _ = self.
_makeBoxRegion(bbox, wcs, bboxPadding)
202 def _filterFunction(refCat, region):
206 afwTable.updateRefCentroids(wcs, refCat)
209 if innerSkyRegion.contains(region):
212 filteredRefCat = type(refCat)(refCat.table)
213 for record
in refCat:
214 pixCoords = record.getCentroid()
215 if bbox.contains(pixCoords):
216 filteredRefCat.append(record)
217 return filteredRefCat
218 return self.
loadRegion(outerSkyRegion, filtFunc=_filterFunction, epoch=epoch, filterName=filterName)
220 def loadRegion(self, region, filtFunc=None, filterName=None, epoch=None):
221 """ Load reference objects within a specified region 223 This function loads the DataIds used to construct an instance of this class 224 which intersect or are contained within the specified region. The reference 225 catalogs which intersect but are not fully contained within the input region are 226 further filtered by the specified filter function. This function will return a 227 single source catalog containing all reference objects inside the specified region. 231 region : `lsst.sphgeom.Region` 232 This can be any type that is derived from `lsst.sphgeom.region` and should 233 define the spatial region for which reference objects are to be loaded. 235 This optional parameter should be a callable object that takes a reference 236 catalog and its corresponding region as parameters, filters the catalog by 237 some criteria and returns the filtered reference catalog. If the value is 238 left as the default (None) than an internal filter function is used which 239 filters according to if a reference object falls within the input region. 241 Name of camera filter, or None or blank for the default filter 242 epoch : `astropy.time.Time` (optional) 243 Epoch to which to correct proper motion and parallax, 244 or None to not apply such corrections. 248 referenceCatalog : `lsst.afw.table.SourceCatalog` 249 Catalog containing reference objects which intersect the input region, 250 filtered by the specified filter function 254 `lsst.pex.exception.RuntimeError` 255 Raised if no reference catalogs could be found for the specified region 257 `lsst.pex.exception.TypeError` 258 Raised if the loaded reference catalogs do not have matching schemas 261 regionBounding = region.getBoundingBox()
262 self.
log.info(
"Loading reference objects from region bounded by {}, {} lat lon".format(
263 regionBounding.getLat(), regionBounding.getLon()))
272 intersects = dataId.region.intersects(region)
274 intersects = region.intersects(dataId.region)
277 overlapList.append(dataId)
279 if len(overlapList) == 0:
280 raise pexExceptions.RuntimeError(
"No reference tables could be found for input region")
282 firstCat = self.
butler.get(
'ref_cat', overlapList[0])
283 refCat = filtFunc(firstCat, overlapList[0].region)
284 trimmedAmount = len(refCat) - len(firstCat)
287 for dataId
in overlapList[1:]:
288 tmpCat = self.
butler.get(
'ref_cat', dataId)
289 if tmpCat.schema != refCat.schema:
290 raise pexExceptions.TypeError(
"Reference catalogs have mismatching schemas")
292 filteredCat = filtFunc(tmpCat, dataId.region)
293 refCat.extend(filteredCat)
294 trimmedAmount += len(tmpCat) - len(filteredCat)
296 self.
log.debug(f
"Trimmed {trimmedAmount} out of region objects, leaving {len(refCat)}")
297 self.
log.info(f
"Loaded {len(refCat)} reference objects")
299 if epoch
is not None and "pm_ra" in refCat.schema:
301 if isinstance(refCat.schema[
"pm_ra"].asKey(), lsst.afw.table.KeyAngle):
304 self.
log.warn(
"Catalog pm_ra field is not an Angle; not applying proper motion")
312 if not expandedCat.isContiguous():
313 expandedCat = refCat.copy(deep=
True)
315 fluxField =
getRefFluxField(schema=expandedCat.schema, filterName=filterName)
316 return pipeBase.Struct(refCat=expandedCat, fluxField=fluxField)
319 """Load reference objects that lie within a circular region on the sky 321 This method constructs a circular region from an input center and angular radius, 322 loads reference catalogs which are contained in or intersect the circle, and 323 filters reference catalogs which intersect down to objects which lie within 328 ctrCoord : `lsst.geom.SpherePoint` 329 Point defining the center of the circular region 330 radius : `lsst.geom.Angle` 331 Defines the angular radius of the circular region 333 Name of camera filter, or None or blank for the default filter 334 epoch : `astropy.time.Time` (optional) 335 Epoch to which to correct proper motion and parallax, 336 or None to not apply such corrections. 340 referenceCatalog : `lsst.afw.table.SourceCatalog` 341 Catalog containing reference objects inside the specified bounding box 345 `lsst.pex.exception.RuntimeError` 346 Raised if no reference catalogs could be found for the specified region 348 `lsst.pex.exception.TypeError` 349 Raised if the loaded reference catalogs do not have matching schemas 352 centerVector = ctrCoord.getVector()
355 return self.
loadRegion(circularRegion, filterName=filterName, epoch=
None)
358 """Relink an unpersisted match list to sources and reference 361 A match list is persisted and unpersisted as a catalog of IDs 362 produced by afw.table.packMatches(), with match metadata 363 (as returned by the astrometry tasks) in the catalog's metadata 364 attribute. This method converts such a match catalog into a match 365 list, with links to source records and reference object records. 369 matchCat : `lsst.afw.table.BaseCatalog` 370 Unpersisted packed match list. 371 ``matchCat.table.getMetadata()`` must contain match metadata, 372 as returned by the astrometry tasks. 373 sourceCat : `lsst.afw.table.SourceCatalog` 374 Source catalog. As a side effect, the catalog will be sorted 379 matchList : `lsst.afw.table.ReferenceMatchVector` 385 def getMetadataBox(cls, bbox, wcs, filterName=None, calib=None, epoch=None, bboxPadding=100):
386 """Return metadata about the load 388 This metadata is used for reloading the catalog (e.g., for 389 reconstituting a normalised match list.) 393 bbox : `lsst.geom.Box2I` 394 Bounding bos for the pixels 395 wcs : `lsst.afw.geom.SkyWcs 397 filterName : `str` or None 398 filterName of the camera filter, or None or blank for the default filter 400 Deprecated, only included for api compatibility 401 epoch : `astropy.time.Time` (optional) 402 Epoch to which to correct proper motion and parallax, 403 or None to not apply such corrections. 405 Number describing how much to pad the input bbox by (in pixels), defaults 406 to 100. This parameter is necessary because optical distortions in telescopes 407 can cause a rectangular pixel grid to map into a non "rectangular" spherical 408 region in sky coordinates. This padding is used to create a spherical 409 "rectangle", which will for sure enclose the input box. This padding is only 410 used to determine if the reference catalog for a sky patch will be loaded from 411 the data store, this function will filter out objects which lie within the 412 padded region but fall outside the input bounding box region. 415 md : `lsst.daf.base.PropertyList` 417 _, _, innerCorners, outerCorners = cls.
_makeBoxRegion(bbox, wcs, bboxPadding)
419 for box, corners
in zip((
"INNER",
"OUTER"), (innerCorners, outerCorners)):
420 for (name, corner)
in zip((
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"),
422 md.add(f
"{box}_{name}_RA", corner.getRa().asDegrees(), f
"{box}_corner")
423 md.add(f
"{box}_{name}_DEC", corner.getDec().asDegrees(), f
"{box}_corner")
424 md.add(
"SMATCHV", 1,
'SourceMatchVector version number')
425 filterName =
"UNKNOWN" if filterName
is None else str(filterName)
426 md.add(
'FILTER', filterName,
'filter name for photometric data')
427 md.add(
'EPOCH',
"NONE" if epoch
is None else epoch,
'Epoch (TAI MJD) for catalog')
432 """Return metadata about the load 434 This metadata is used for reloading the catalog (e.g. for reconstituting 435 a normalized match list.) 439 coord : `lsst.geom.SpherePoint` 440 ICRS center of a circle 441 radius : `lsst.geom.angle` 443 filterName : `str` or None 444 filterName of the camera filter, or None or blank for the default filter 446 Deprecated, only included for api compatibility 447 epoch : `astropy.time.Time` (optional) 448 Epoch to which to correct proper motion and parallax, 449 or None to not apply such corrections. 453 md : `lsst.daf.base.PropertyList` 456 md.add(
'RA', coord.getRa().asDegrees(),
'field center in degrees')
457 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
458 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
459 md.add(
'SMATCHV', 1,
'SourceMatchVector version number')
460 filterName =
"UNKNOWN" if filterName
is None else str(filterName)
461 md.add(
'FILTER', filterName,
'filter name for photometric data')
462 md.add(
'EPOCH',
"NONE" if epoch
is None else epoch,
'Epoch (TAI MJD) for catalog')
467 """This function creates a new catalog containing the information of the input refCat 468 as well as added flux columns and aliases between camera and reference flux. 472 refCat : `lsst.afw.table.SimpleCatalog` 473 Catalog of reference objects 474 defaultFilter : `str` 475 Name of the default reference filter 476 filterReferenceMap : `dict` of `str` 477 Dictionary with keys corresponding to a filter name, and values which 478 correspond to the name of the reference filter. 482 refCat : `lsst.afw.table.SimpleCatalog` 483 Reference catalog with columns added to track reference filters 488 If specified reference filter name is not a filter specifed as a key in the 489 reference filter map. 491 refCat = ReferenceObjectLoader.remapReferenceCatalogSchema(refCat,
492 filterNameList=filterReferenceMap.keys())
493 aliasMap = refCat.schema.getAliasMap()
495 if filterReferenceMap
is None:
496 filterReferenceMap = {}
497 for filterName, refFilterName
in itertools.chain([(
None, defaultFilter)],
498 filterReferenceMap.items()):
500 camFluxName = filterName +
"_camFlux" if filterName
is not None else "camFlux" 501 refFluxName = refFilterName +
"_flux" 502 if refFluxName
not in refCat.schema:
503 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
504 aliasMap.set(camFluxName, refFluxName)
506 refFluxErrName = refFluxName +
"Err" 507 camFluxErrName = camFluxName +
"Err" 508 aliasMap.set(camFluxErrName, refFluxErrName)
514 """This function takes in a reference catalog and creates a new catalog with additional 515 columns defined the remaining function arguments. 519 refCat : `lsst.afw.table.SimpleCatalog` 520 Reference catalog to map to new catalog 524 expandedCat : `lsst.afw.table.SimpleCatalog` 525 Deep copy of input reference catalog with additional columns added 527 mapper = afwTable.SchemaMapper(refCat.schema,
True)
528 mapper.addMinimalSchema(refCat.schema,
True)
529 mapper.editOutputSchema().disconnectAliases()
531 for filterName
in filterNameList:
532 mapper.editOutputSchema().addField(f
"{filterName}_flux",
534 doc=f
"flux in filter {filterName}",
537 mapper.editOutputSchema().addField(f
"{filterName}_fluxErr",
539 doc=f
"flux uncertanty in filter {filterName}",
544 mapper.editOutputSchema().addField(
"centroid_x", type=float, doReplace=
True)
545 mapper.editOutputSchema().addField(
"centroid_y", type=float, doReplace=
True)
546 mapper.editOutputSchema().addField(
"hasCentroid", type=
"Flag", doReplace=
True)
547 mapper.editOutputSchema().getAliasMap().set(
"slot_Centroid",
"centroid")
550 mapper.editOutputSchema().addField(
"photometric",
552 doc=
"set if the object can be used for photometric" +
555 mapper.editOutputSchema().addField(
"resolved",
557 doc=
"set if the object is spatially resolved" 559 mapper.editOutputSchema().addField(
"variable",
561 doc=
"set if the object has variable brightness" 564 expandedCat = afwTable.SimpleCatalog(mapper.getOutputSchema())
565 expandedCat.extend(refCat, mapper=mapper)
571 """Get the name of a flux field from a schema. 573 if filterName is specified: 574 return *filterName*_camFlux if present 575 else return *filterName*_flux if present (camera filter name matches reference filter name) 576 else throw RuntimeError 578 return camFlux, if present, 579 else throw RuntimeError 583 schema : `lsst.afw.table.Schema` 584 Reference catalog schema. 586 Name of camera filter. 590 fluxFieldName : `str` 596 If an appropriate field is not found. 598 if not isinstance(schema, afwTable.Schema):
599 raise RuntimeError(
"schema=%s is not a schema" % (schema,))
601 fluxFieldList = [filterName +
"_camFlux", filterName +
"_flux"]
603 fluxFieldList = [
"camFlux"]
604 for fluxField
in fluxFieldList:
605 if fluxField
in schema:
608 raise RuntimeError(
"Could not find flux field(s) %s" % (
", ".join(fluxFieldList)))
612 """Return keys for flux and flux error. 616 schema : `lsst.afw.table.Schema` 617 Reference catalog schema. 619 Name of camera filter. 623 keys : `tuple` of (`lsst.afw.table.Key`, `lsst.afw.table.Key`) 627 - flux error key, if present, else None 632 If flux field not found. 635 fluxErrField = fluxField +
"Err" 636 fluxKey = schema[fluxField].asKey()
638 fluxErrKey = schema[fluxErrField].asKey()
641 return (fluxKey, fluxErrKey)
645 pixelMargin = pexConfig.RangeField(
646 doc=
"Padding to add to 4 all edges of the bounding box (pixels)",
651 defaultFilter = pexConfig.Field(
652 doc=
"Default reference catalog filter to use if filter not specified in exposure; " 653 "if blank then filter must be specified in exposure",
657 filterMap = pexConfig.DictField(
658 doc=
"Mapping of camera filter name: reference catalog filter name; " 659 "each reference filter must exist",
664 requireProperMotion = pexConfig.Field(
665 doc=
"Require that the fields needed to correct proper motion " 666 "(epoch, pm_ra and pm_dec) are present?",
681 r"""!Abstract base class to load objects from reference catalogs 683 @anchor LoadReferenceObjectsTask_ 685 @section meas_algorithms_loadReferenceObjects_Contents Contents 687 - @ref meas_algorithms_loadReferenceObjects_Purpose 688 - @ref meas_algorithms_loadReferenceObjects_Initialize 689 - @ref meas_algorithms_loadReferenceObjects_IO 690 - @ref meas_algorithms_loadReferenceObjects_Schema 691 - @ref meas_algorithms_loadReferenceObjects_Config 693 @section meas_algorithms_loadReferenceObjects_Purpose Description 695 Abstract base class for tasks that load objects from a reference catalog 696 in a particular region of the sky. 698 Implementations must subclass this class, override the loadSkyCircle method, 699 and will typically override the value of ConfigClass with a task-specific config class. 701 @section meas_algorithms_loadReferenceObjects_Initialize Task initialisation 703 @copydoc \_\_init\_\_ 705 @section meas_algorithms_loadReferenceObjects_IO Invoking the Task 707 @copydoc loadPixelBox 709 @section meas_algorithms_loadReferenceObjects_Schema Schema of the reference object catalog 711 Reference object catalogs are instances of lsst.afw.table.SimpleCatalog with the following schema 712 (other fields may also be present). 713 The units use astropy quantity conventions, so a 2 suffix means squared. 714 See also makeMinimalSchema. 715 - coord: ICRS position of star on sky (an lsst.geom.SpherePoint) 716 - centroid: position of star on an exposure, if relevant (an lsst.afw.Point2D) 717 - hasCentroid: is centroid usable? (a Flag) 718 - *referenceFilterName*_flux: brightness in the specified reference catalog filter (Jy) 719 Note: the function lsst.afw.image.abMagFromFlux will convert flux in Jy to AB Magnitude. 720 - *referenceFilterName*_fluxErr (optional): brightness standard deviation (Jy); 721 omitted if no data is available; possibly nan if data is available for some objects but not others 722 - camFlux: brightness in default camera filter (Jy); omitted if defaultFilter not specified 723 - camFluxErr: brightness standard deviation for default camera filter; 724 omitted if defaultFilter not specified or standard deviation not available that filter 725 - *cameraFilterName*_camFlux: brightness in specified camera filter (Jy) 726 - *cameraFilterName*_camFluxErr (optional): brightness standard deviation 727 in specified camera filter (Jy); omitted if no data is available; 728 possibly nan if data is available for some objects but not others 729 - photometric (optional): is the object usable for photometric calibration? (a Flag) 730 - resolved (optional): is the object spatially resolved? (a Flag) 731 - variable (optional): does the object have variable brightness? (a Flag) 732 - coord_raErr: uncertainty in `coord` along the direction of right ascension (radian, an Angle) 733 = uncertainty in ra * cos(dec); nan if unknown 734 - coord_decErr: uncertainty in `coord` along the direction of declination (radian, an Angle); 737 The following are optional; fields should only be present if the 738 information is available for at least some objects. 739 Numeric values are `nan` if unknown: 740 - epoch: date of observation as TAI MJD (day) 742 - pm_ra: proper motion along the direction of right ascension (rad/year, an Angle) = dra/dt * cos(dec) 743 - pm_dec: proper motion along the direction of declination (rad/year, and Angle) 744 - pm_raErr: uncertainty in `pm_ra` (rad/year) 745 - pm_decErr: uncertainty in `pm_dec` (rad/year) 746 - pm_ra_dec_Cov: covariance between pm_ra and pm_dec (rad2/year2) 747 - pm_flag: set if proper motion, error or covariance is bad 749 - parallax: parallax (rad, an Angle) 750 - parallaxErr: uncertainty in `parallax` (rad) 751 - parallax_flag: set if parallax value or parallaxErr is bad 753 - coord_ra_pm_ra_Cov: covariance between coord_ra and pm_ra (rad2/year) 754 - coord_ra_pm_dec_Cov: covariance between coord_ra and pm_dec (rad2/year) 755 - coord_ra_parallax_Cov: covariance between coord_ra and parallax (rad2/year) 756 - coord_dec_pm_ra_Cov: covariance between coord_dec and pm_ra (rad2/year) 757 - coord_dec_pm_dec_Cov: covariance between coord_dec and pm_dec (rad2/year) 758 - coord_dec_parallax_Cov: covariance between coord_dec and parallax (rad2/year) 759 - pm_ra_parallax_Cov: covariance between pm_ra and parallax (rad2/year) 760 - pm_dec_parallax_Cov: covariance between pm_dec and parallax (rad2/year) 762 @section meas_algorithms_loadReferenceObjects_Config Configuration parameters 764 See @ref LoadReferenceObjectsConfig for a base set of configuration parameters. 765 Most subclasses will add configuration variables. 767 ConfigClass = LoadReferenceObjectsConfig
768 _DefaultName =
"LoadReferenceObjects" 771 """Construct a LoadReferenceObjectsTask 775 butler : `lsst.daf.persistence.Butler` 776 Data butler, for access reference catalogs. 778 pipeBase.Task.__init__(self, *args, **kwargs)
782 def loadPixelBox(self, bbox, wcs, filterName=None, calib=None, epoch=None):
783 """Load reference objects that overlap a rectangular pixel region. 787 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 788 Bounding box for pixels. 789 wcs : `lsst.afw.geom.SkyWcs` 790 WCS; used to convert pixel positions to sky coordinates 793 Name of filter, or `None` or `""` for the default filter. 794 This is used for flux values in case we have flux limits 795 (which are not yet implemented). 796 calib : `lsst.afw.image.Calib` (optional) 797 Calibration, or `None` if unknown. 798 epoch : `astropy.time.Time` (optional) 799 Epoch to which to correct proper motion and parallax, 800 or None to not apply such corrections. 804 results : `lsst.pipe.base.Struct` 805 A Struct containing the following fields: 806 refCat : `lsst.afw.catalog.SimpleCatalog` 807 A catalog of reference objects with the standard 808 schema, as documented in the main doc string for 809 `LoadReferenceObjects`. 810 The catalog is guaranteed to be contiguous. 812 Name of flux field for specified `filterName`. 816 The search algorithm works by searching in a region in sky 817 coordinates whose center is the center of the bbox and radius 818 is large enough to just include all 4 corners of the bbox. 819 Stars that lie outside the bbox are then trimmed from the list. 824 self.log.info(
"Loading reference objects using center %s and radius %s deg" %
825 (circle.coord, circle.radius.asDegrees()))
826 loadRes = self.
loadSkyCircle(circle.coord, circle.radius, filterName)
827 refCat = loadRes.refCat
828 numFound = len(refCat)
831 refCat = self.
_trimToBBox(refCat=refCat, bbox=circle.bbox, wcs=wcs)
832 numTrimmed = numFound - len(refCat)
833 self.log.debug(
"trimmed %d out-of-bbox objects, leaving %d", numTrimmed, len(refCat))
834 self.log.info(
"Loaded %d reference objects", len(refCat))
837 if not refCat.isContiguous():
838 loadRes.refCat = refCat.copy(deep=
True)
844 """Load reference objects that overlap a circular sky region. 848 ctrCoord : `lsst.geom.SpherePoint` 849 ICRS center of search region. 850 radius : `lsst.geom.Angle` 851 Radius of search region. 852 filterName : `str` (optional) 853 Name of filter, or `None` or `""` for the default filter. 854 This is used for flux values in case we have flux limits 855 (which are not yet implemented). 856 epoch : `astropy.time.Time` (optional) 857 Epoch to which to correct proper motion and parallax, 858 or None to not apply such corrections. 862 results : `lsst.pipe.base.Struct` 863 A Struct containing the following fields: 864 refCat : `lsst.afw.catalog.SimpleCatalog` 865 A catalog of reference objects with the standard 866 schema, as documented in the main doc string for 867 `LoadReferenceObjects`. 868 The catalog is guaranteed to be contiguous. 870 Name of flux field for specified `filterName`. 874 Note that subclasses are responsible for performing the proper motion 875 correction, since this is the lowest-level interface for retrieving 881 def _trimToBBox(refCat, bbox, wcs):
882 """Remove objects outside a given pixel bounding box and set 883 centroid and hasCentroid fields. 887 refCat : `lsst.afw.table.SimpleCatalog` 888 A catalog of objects. The schema must include fields 889 "coord", "centroid" and "hasCentroid". 890 The "coord" field is read. 891 The "centroid" and "hasCentroid" fields are set. 892 bbox : `lsst.geom.Box2D` 894 wcs : `lsst.afw.geom.SkyWcs` 895 WCS; used to convert sky coordinates to pixel positions. 897 @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set 899 afwTable.updateRefCentroids(wcs, refCat)
900 centroidKey = afwTable.Point2DKey(refCat.schema[
"centroid"])
901 retStarCat = type(refCat)(refCat.table)
903 point = star.get(centroidKey)
904 if bbox.contains(point):
905 retStarCat.append(star)
908 def _addFluxAliases(self, schema):
909 """Add aliases for camera filter fluxes to the schema. 911 If self.config.defaultFilter then adds these aliases: 912 camFlux: <defaultFilter>_flux 913 camFluxErr: <defaultFilter>_fluxErr, if the latter exists 915 For each camFilter: refFilter in self.config.filterMap adds these aliases: 916 <camFilter>_camFlux: <refFilter>_flux 917 <camFilter>_camFluxErr: <refFilter>_fluxErr, if the latter exists 921 schema : `lsst.afw.table.Schema` 922 Schema for reference catalog. 927 If any reference flux field is missing from the schema. 929 aliasMap = schema.getAliasMap()
931 def addAliasesForOneFilter(filterName, refFilterName):
932 """Add aliases for a single filter 936 filterName : `str` (optional) 937 Camera filter name. The resulting alias name is 938 <filterName>_camFlux, or simply "camFlux" if `filterName` 940 refFilterName : `str` 941 Reference catalog filter name; the field 942 <refFilterName>_flux must exist. 944 camFluxName = filterName +
"_camFlux" if filterName
is not None else "camFlux" 945 refFluxName = refFilterName +
"_flux" 946 if refFluxName
not in schema:
947 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
948 aliasMap.set(camFluxName, refFluxName)
949 refFluxErrName = refFluxName +
"Err" 950 if refFluxErrName
in schema:
951 camFluxErrName = camFluxName +
"Err" 952 aliasMap.set(camFluxErrName, refFluxErrName)
954 if self.config.defaultFilter:
955 addAliasesForOneFilter(
None, self.config.defaultFilter)
957 for filterName, refFilterName
in self.config.filterMap.items():
958 addAliasesForOneFilter(filterName, refFilterName)
962 addIsPhotometric=False, addIsResolved=False,
963 addIsVariable=False, coordErrDim=2,
964 addProperMotion=False, properMotionErrDim=2,
965 addParallax=False, addParallaxErr=True):
966 """Make a standard schema for reference object catalogs. 970 filterNameList : `list` of `str` 971 List of filter names. Used to create <filterName>_flux fields. 972 addIsPhotometric : `bool` 973 If True then add field "photometric". 974 addIsResolved : `bool` 975 If True then add field "resolved". 976 addIsVariable : `bool` 977 If True then add field "variable". 979 Number of coord error fields; must be one of 0, 2, 3: 981 - If 2 or 3: add fields "coord_raErr" and "coord_decErr". 982 - If 3: also add field "coord_radecErr". 983 addProperMotion : `bool` 984 If True add fields "epoch", "pm_ra", "pm_dec" and "pm_flag". 985 properMotionErrDim : `int` 986 Number of proper motion error fields; must be one of 0, 2, 3; 987 ignored if addProperMotion false: 988 - If 2 or 3: add fields "pm_raErr" and "pm_decErr". 989 - If 3: also add field "pm_radecErr". 991 If True add fields "epoch", "parallax", "parallaxErr" 993 addParallaxErr : `bool` 994 If True add field "parallaxErr"; ignored if addParallax false. 998 schema : `lsst.afw.table.Schema` 999 Schema for reference catalog, an 1000 `lsst.afw.table.SimpleCatalog`. 1004 Reference catalogs support additional covariances, such as 1005 covariance between RA and proper motion in declination, 1006 that are not supported by this method, but can be added after 1007 calling this method. 1009 schema = afwTable.SimpleTable.makeMinimalSchema()
1011 afwTable.Point2DKey.addFields(
1014 "centroid on an exposure, if relevant",
1018 field=
"hasCentroid",
1020 doc=
"is position known?",
1022 for filterName
in filterNameList:
1024 field=
"%s_flux" % (filterName,),
1026 doc=
"flux in filter %s" % (filterName,),
1029 for filterName
in filterNameList:
1031 field=
"%s_fluxErr" % (filterName,),
1033 doc=
"flux uncertainty in filter %s" % (filterName,),
1036 if addIsPhotometric:
1038 field=
"photometric",
1040 doc=
"set if the object can be used for photometric calibration",
1046 doc=
"set if the object is spatially resolved",
1052 doc=
"set if the object has variable brightness",
1054 if coordErrDim
not in (0, 2, 3):
1055 raise ValueError(
"coordErrDim={}; must be (0, 2, 3)".format(coordErrDim))
1057 afwTable.CovarianceMatrix2fKey.addFields(
1060 names=[
"ra",
"dec"],
1061 units=[
"rad",
"rad"],
1062 diagonalOnly=(coordErrDim == 2),
1065 if addProperMotion
or addParallax:
1069 doc=
"date of observation (TAI, MJD)",
1077 doc=
"proper motion in the right ascension direction = dra/dt * cos(dec)",
1083 doc=
"proper motion in the declination direction",
1086 if properMotionErrDim
not in (0, 2, 3):
1087 raise ValueError(
"properMotionErrDim={}; must be (0, 2, 3)".format(properMotionErrDim))
1088 if properMotionErrDim > 0:
1089 afwTable.CovarianceMatrix2fKey.addFields(
1092 names=[
"ra",
"dec"],
1093 units=[
"rad/year",
"rad/year"],
1094 diagonalOnly=(properMotionErrDim == 2),
1099 doc=
"Set if proper motion or proper motion error is bad",
1111 field=
"parallaxErr",
1113 doc=
"uncertainty in parallax",
1117 field=
"parallax_flag",
1119 doc=
"Set if parallax or parallax error is bad",
1123 def _calculateCircle(self, bbox, wcs):
1124 """Compute on-sky center and radius of search region. 1128 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 1130 wcs : `lsst.afw.geom.SkyWcs` 1131 WCS; used to convert pixel positions to sky coordinates. 1135 results : `lsst.pipe.base.Struct` 1136 A Struct containing: 1138 - coord : `lsst.geom.SpherePoint` 1139 ICRS center of the search region. 1140 - radius : `lsst.geom.Angle` 1141 Radius of the search region. 1142 - bbox : `lsst.afw.geom.Box2D` 1143 Bounding box used to compute the circle. 1146 bbox.grow(self.config.pixelMargin)
1147 coord = wcs.pixelToSky(bbox.getCenter())
1148 radius = max(coord.separation(wcs.pixelToSky(pp))
for pp
in bbox.getCorners())
1149 return pipeBase.Struct(coord=coord, radius=radius, bbox=bbox)
1152 """Return metadata about the load. 1154 This metadata is used for reloading the catalog (e.g., for 1155 reconstituting a normalised match list. 1159 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 1161 wcs : `lsst.afw.geom.SkyWcs` 1162 WCS; used to convert pixel positions to sky coordinates. 1164 Name of camera filter, or `None` or `""` for the default 1166 calib : `lsst.afw.image.Calib` (optional) 1167 Calibration, or `None` if unknown. 1168 epoch : `astropy.time.Time` (optional) 1169 Epoch to which to correct proper motion and parallax, 1170 or None to not apply such corrections. 1174 metadata : lsst.daf.base.PropertyList 1175 Metadata about the load. 1181 """Return metadata about the load. 1183 This metadata is used for reloading the catalog (e.g., for 1184 reconstituting a normalised match list. 1188 coord : `lsst.geom.SpherePoint` 1189 ICRS center of the search region. 1190 radius : `lsst.geom.Angle` 1191 Radius of the search region. 1193 Name of camera filter, or `None` or `""` for the default 1195 calib : `lsst.afw.image.Calib` (optional) 1196 Calibration, or `None` if unknown. 1197 epoch : `astropy.time.Time` (optional) 1198 Epoch to which to correct proper motion and parallax, 1199 or None to not apply such corrections. 1203 metadata : lsst.daf.base.PropertyList 1204 Metadata about the load 1207 md.add(
'RA', coord.getRa().asDegrees(),
'field center in degrees')
1208 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
1209 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
1210 md.add(
'SMATCHV', 1,
'SourceMatchVector version number')
1211 filterName =
"UNKNOWN" if filterName
is None else str(filterName)
1212 md.add(
'FILTER', filterName,
'filter name for photometric data')
1213 md.add(
'EPOCH',
"NONE" if epoch
is None else epoch,
'Epoch (TAI MJD) for catalog')
1217 """Relink an unpersisted match list to sources and reference 1220 A match list is persisted and unpersisted as a catalog of IDs 1221 produced by afw.table.packMatches(), with match metadata 1222 (as returned by the astrometry tasks) in the catalog's metadata 1223 attribute. This method converts such a match catalog into a match 1224 list, with links to source records and reference object records. 1228 matchCat : `lsst.afw.table.BaseCatalog` 1229 Unperisted packed match list. 1230 ``matchCat.table.getMetadata()`` must contain match metadata, 1231 as returned by the astrometry tasks. 1232 sourceCat : `lsst.afw.table.SourceCatalog` 1233 Source catalog. As a side effect, the catalog will be sorted 1238 matchList : `lsst.afw.table.ReferenceMatchVector` 1244 """Apply proper motion correction to a reference catalog. 1246 Adjust position and position error in the ``catalog`` 1247 for proper motion to the specified ``epoch``, 1248 modifying the catalong in place. 1252 catalog : `lsst.afw.table.SimpleCatalog` 1253 Catalog of positions, containing: 1255 - Coordinates, retrieved by the table's coordinate key. 1256 - ``coord_raErr`` : Error in Right Ascension (rad). 1257 - ``coord_decErr`` : Error in Declination (rad). 1258 - ``pm_ra`` : Proper motion in Right Ascension (rad/yr, 1260 - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional. 1261 - ``pm_dec`` : Proper motion in Declination (rad/yr, 1263 - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional. 1264 - ``epoch`` : Mean epoch of object (an astropy.time.Time) 1265 epoch : `astropy.time.Time` (optional) 1266 Epoch to which to correct proper motion and parallax, 1267 or None to not apply such corrections. 1269 if (
"epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema):
1270 if self.config.requireProperMotion:
1271 raise RuntimeError(
"Proper motion correction required but not available from catalog")
1272 self.log.warn(
"Proper motion correction not available from catalog")
1278 """Relink an unpersisted match list to sources and reference 1281 A match list is persisted and unpersisted as a catalog of IDs 1282 produced by afw.table.packMatches(), with match metadata 1283 (as returned by the astrometry tasks) in the catalog's metadata 1284 attribute. This method converts such a match catalog into a match 1285 list, with links to source records and reference object records. 1290 Reference object loader to use in getting reference objects 1291 matchCat : `lsst.afw.table.BaseCatalog` 1292 Unperisted packed match list. 1293 ``matchCat.table.getMetadata()`` must contain match metadata, 1294 as returned by the astrometry tasks. 1295 sourceCat : `lsst.afw.table.SourceCatalog` 1296 Source catalog. As a side effect, the catalog will be sorted 1301 matchList : `lsst.afw.table.ReferenceMatchVector` 1304 matchmeta = matchCat.table.getMetadata()
1305 version = matchmeta.getInt(
'SMATCHV')
1307 raise ValueError(
'SourceMatchVector version number is %i, not 1.' % version)
1308 filterName = matchmeta.getString(
'FILTER').strip()
1310 epoch = matchmeta.getDouble(
'EPOCH')
1311 except (pexExcept.NotFoundError, pexExcept.TypeError):
1313 if 'RADIUS' in matchmeta:
1316 matchmeta.getDouble(
'DEC'), lsst.geom.degrees)
1317 rad = matchmeta.getDouble(
'RADIUS') * lsst.geom.degrees
1318 refCat = refObjLoader.loadSkyCircle(ctrCoord, rad, filterName, epoch=epoch).refCat
1319 elif "INNER_UPPER_LEFT_RA" in matchmeta:
1325 for place
in (
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"):
1327 matchmeta.getDouble(f
"OUTER_{place}_DEC"),
1328 lsst.geom.degrees).getVector()
1331 refCat = refObjLoader.loadRegion(outerBox, filterName=filterName, epoch=epoch).refCat
1335 return afwTable.unpackMatches(matchCat, refCat, sourceCat)
1339 """Apply proper motion correction to a reference catalog. 1341 Adjust position and position error in the ``catalog`` 1342 for proper motion to the specified ``epoch``, 1343 modifying the catalong in place. 1347 log : `lsst.log.log` 1348 log object to write to 1349 catalog : `lsst.afw.table.SimpleCatalog` 1350 Catalog of positions, containing: 1352 - Coordinates, retrieved by the table's coordinate key. 1353 - ``coord_raErr`` : Error in Right Ascension (rad). 1354 - ``coord_decErr`` : Error in Declination (rad). 1355 - ``pm_ra`` : Proper motion in Right Ascension (rad/yr, 1357 - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional. 1358 - ``pm_dec`` : Proper motion in Declination (rad/yr, 1360 - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional. 1361 - ``epoch`` : Mean epoch of object (an astropy.time.Time) 1362 epoch : `astropy.time.Time` (optional) 1363 Epoch to which to correct proper motion and parallax, 1364 or None to not apply such corrections. 1366 if "epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema:
1367 log.warn(
"Proper motion correction not available from catalog")
1369 if not catalog.isContiguous():
1370 raise RuntimeError(
"Catalog must be contiguous")
1371 catEpoch = astropy.time.Time(catalog[
"epoch"], scale=
"tai", format=
"mjd")
1372 log.debug(
"Correcting reference catalog for proper motion to %r", epoch)
1374 timeDiffsYears = (epoch.tai - catEpoch).to(astropy.units.yr).value
1375 coordKey = catalog.table.getCoordKey()
1378 pmRaRad = catalog[
"pm_ra"]
1379 pmDecRad = catalog[
"pm_dec"]
1380 offsetsRaRad = pmRaRad*timeDiffsYears
1381 offsetsDecRad = pmDecRad*timeDiffsYears
1389 offsetBearingsRad = numpy.arctan2(pmDecRad*1e6, pmRaRad*1e6)
1390 offsetAmountsRad = numpy.hypot(offsetsRaRad, offsetsDecRad)
1391 for record, bearingRad, amountRad
in zip(catalog, offsetBearingsRad, offsetAmountsRad):
1392 record.set(coordKey,
1393 record.get(coordKey).offset(bearing=bearingRad*lsst.geom.radians,
1394 amount=amountRad*lsst.geom.radians))
1396 if "coord_raErr" in catalog.schema:
1397 catalog[
"coord_raErr"] = numpy.hypot(catalog[
"coord_raErr"],
1398 catalog[
"pm_raErr"]*timeDiffsYears)
1399 if "coord_decErr" in catalog.schema:
1400 catalog[
"coord_decErr"] = numpy.hypot(catalog[
"coord_decErr"],
1401 catalog[
"pm_decErr"]*timeDiffsYears)
def loadPixelBox(self, bbox, wcs, filterName=None, calib=None, epoch=None)
def joinMatchListWithCatalog(self, matchCat, sourceCat)
def __call__(self, refCat, catRegion)
def addFluxAliases(refCat, defaultFilter, filterReferenceMap)
def getMetadataCircle(coord, radius, filterName, calib=None, epoch=None)
def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None)
def __init__(self, butler=None, args, kwargs)
def applyProperMotions(self, catalog, epoch)
def _trimToBBox(refCat, bbox, wcs)
def __init__(self, region)
def getMetadataBox(self, bbox, wcs, filterName=None, calib=None, epoch=None)
def joinMatchListWithCatalog(self, matchCat, sourceCat)
static Log getDefaultLogger()
def getRefFluxField(schema, filterName=None)
def loadRegion(self, region, filtFunc=None, filterName=None, epoch=None)
def getRefFluxKeys(schema, filterName=None)
Abstract base class to load objects from reference catalogs.
def applyProperMotionsImpl(log, catalog, epoch)
def makeMinimalSchema(filterNameList, addCentroid=True, addIsPhotometric=False, addIsResolved=False, addIsVariable=False, coordErrDim=2, addProperMotion=False, properMotionErrDim=2, addParallax=False, addParallaxErr=True)
def loadPixelBox(self, bbox, wcs, filterName=None, epoch=None, calib=None, bboxPadding=100)
def getMetadataCircle(self, coord, radius, filterName, calib=None, epoch=None)
def _makeBoxRegion(BBox, wcs, BBoxPadding)
def joinMatchListWithCatalogImpl(refObjLoader, matchCat, sourceCat)
def getMetadataBox(cls, bbox, wcs, filterName=None, calib=None, epoch=None, bboxPadding=100)
def __init__(self, dataIds, butler, config, log=None)
def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None)
def _calculateCircle(self, bbox, wcs)
def remapReferenceCatalogSchema(refCat, filterNameList=None, position=False, photometric=False)