def run(self, butler, dataRefList): """!Make a skymap from the bounds of the given set of calexps. @param[in] butler data butler used to save the SkyMap @param[in] dataRefList dataRefs of calexps used to determine the size and pointing of the SkyMap @return a pipeBase Struct containing: - skyMap: the constructed SkyMap """ self.log.info("Extracting bounding boxes of %d images" % len(dataRefList)) points = [] for dataRef in dataRefList: if not dataRef.datasetExists("calexp"): self.log.warn("CalExp for %s does not exist: ignoring" % (dataRef.dataId,)) continue md = dataRef.get("calexp_md", immediate=True) wcs = afwGeom.makeSkyWcs(md) # nb: don't need to worry about xy0 because Exposure saves Wcs with CRPIX shifted by (-x0, -y0). boxI = afwImage.bboxFromMetadata(md) boxD = afwGeom.Box2D(boxI) points.extend(wcs.pixelToSky(corner).getVector() for corner in boxD.getCorners()) if len(points) == 0: raise RuntimeError("No data found from which to compute convex hull") self.log.info("Computing spherical convex hull") polygon = lsst.sphgeom.ConvexPolygon.convexHull(points) if polygon is None: raise RuntimeError( "Failed to compute convex hull of the vertices of all calexp bounding boxes; " "they may not be hemispherical." ) circle = polygon.getBoundingCircle() datasetName = self.config.coaddName + "Coadd_skyMap" skyMapConfig = DiscreteSkyMap.ConfigClass() if self.config.doAppend and butler.datasetExists(datasetName): oldSkyMap = butler.get(datasetName, immediate=True) if not isinstance(oldSkyMap.config, DiscreteSkyMap.ConfigClass): raise TypeError("Cannot append to existing non-discrete skymap") compareLog = [] if not self.config.skyMap.compare(oldSkyMap.config, output=compareLog.append): raise ValueError("Cannot append to existing skymap - configurations differ:", *compareLog) skyMapConfig.raList.extend(oldSkyMap.config.raList) skyMapConfig.decList.extend(oldSkyMap.config.decList) skyMapConfig.radiusList.extend(oldSkyMap.config.radiusList) skyMapConfig.update(**self.config.skyMap.toDict()) circleCenter = lsst.sphgeom.LonLat(circle.getCenter()) skyMapConfig.raList.append(circleCenter[0].asDegrees()) skyMapConfig.decList.append(circleCenter[1].asDegrees()) circleRadiusDeg = circle.getOpeningAngle().asDegrees() skyMapConfig.radiusList.append(circleRadiusDeg + self.config.borderSize) skyMap = DiscreteSkyMap(skyMapConfig) for tractInfo in skyMap: wcs = tractInfo.getWcs() posBox = afwGeom.Box2D(tractInfo.getBBox()) pixelPosList = ( posBox.getMin(), afwGeom.Point2D(posBox.getMaxX(), posBox.getMinY()), posBox.getMax(), afwGeom.Point2D(posBox.getMinX(), posBox.getMaxY()), ) skyPosList = [wcs.pixelToSky(pos).getPosition(afwGeom.degrees) for pos in pixelPosList] posStrList = ["(%0.3f, %0.3f)" % tuple(skyPos) for skyPos in skyPosList] self.log.info("tract %s has corners %s (RA, Dec deg) and %s x %s patches" % (tractInfo.getId(), ", ".join(posStrList), tractInfo.getNumPatches()[0], tractInfo.getNumPatches()[1])) if self.config.doWrite: butler.put(skyMap, datasetName) return pipeBase.Struct( skyMap=skyMap )
def run(self, wcs_bbox_tuple_list, oldSkyMap=None): """Make a SkyMap from the bounds of the given set of calexp metadata. Parameters ---------- wcs_bbox_tuple_list : iterable A list of tuples with each element expected to be a (Wcs, Box2I) pair oldSkyMap : `lsst.skymap.DiscreteSkyMap`, option The SkyMap to extend if appending Returns ------- struct : `lsst.pipe.base.Struct The returned struct has one attribute, ``skyMap``, which holds the returned SkyMap """ self.log.info("Extracting bounding boxes of %d images", len(wcs_bbox_tuple_list)) points = [] for wcs, boxI in wcs_bbox_tuple_list: boxD = geom.Box2D(boxI) points.extend( wcs.pixelToSky(corner).getVector() for corner in boxD.getCorners()) if len(points) == 0: raise RuntimeError( "No data found from which to compute convex hull") self.log.info("Computing spherical convex hull") polygon = lsst.sphgeom.ConvexPolygon.convexHull(points) if polygon is None: raise RuntimeError( "Failed to compute convex hull of the vertices of all calexp bounding boxes; " "they may not be hemispherical.") circle = polygon.getBoundingCircle() skyMapConfig = DiscreteSkyMap.ConfigClass() if oldSkyMap: skyMapConfig.raList.extend(oldSkyMap.config.raList) skyMapConfig.decList.extend(oldSkyMap.config.decList) skyMapConfig.radiusList.extend(oldSkyMap.config.radiusList) configIntersection = { k: getattr(self.config.skyMap, k) for k in self.config.skyMap.toDict() if k in skyMapConfig } skyMapConfig.update(**configIntersection) circleCenter = lsst.sphgeom.LonLat(circle.getCenter()) skyMapConfig.raList.append(circleCenter[0].asDegrees()) skyMapConfig.decList.append(circleCenter[1].asDegrees()) circleRadiusDeg = circle.getOpeningAngle().asDegrees() skyMapConfig.radiusList.append(circleRadiusDeg + self.config.borderSize) skyMap = DiscreteSkyMap(skyMapConfig) for tractInfo in skyMap: wcs = tractInfo.getWcs() posBox = geom.Box2D(tractInfo.getBBox()) pixelPosList = ( posBox.getMin(), geom.Point2D(posBox.getMaxX(), posBox.getMinY()), posBox.getMax(), geom.Point2D(posBox.getMinX(), posBox.getMaxY()), ) skyPosList = [ wcs.pixelToSky(pos).getPosition(geom.degrees) for pos in pixelPosList ] posStrList = [ "(%0.3f, %0.3f)" % tuple(skyPos) for skyPos in skyPosList ] self.log.info( "tract %s has corners %s (RA, Dec deg) and %s x %s patches", tractInfo.getId(), ", ".join(posStrList), tractInfo.getNumPatches()[0], tractInfo.getNumPatches()[1]) return pipeBase.Struct(skyMap=skyMap)