Coverage for python/lsst/pipe/tasks/makeDiscreteSkyMap.py: 30%
52 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-08-20 09:51 +0000
« prev ^ index » next coverage.py v6.4.4, created at 2022-08-20 09:51 +0000
1#
2# LSST Data Management System
3# Copyright 2008-2015 AURA/LSST.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <https://www.lsstcorp.org/LegalNotices/>.
21#
22import lsst.sphgeom
24import lsst.geom as geom
25import lsst.pex.config as pexConfig
26import lsst.pipe.base as pipeBase
27from lsst.skymap import DiscreteSkyMap, BaseSkyMap
28from lsst.utils.timer import timeMethod
31class MakeDiscreteSkyMapConfig(pexConfig.Config):
32 """Config for MakeDiscreteSkyMapTask
33 """
34 coaddName = pexConfig.Field(
35 doc="coadd name, e.g. deep, goodSeeing, chiSquared",
36 dtype=str,
37 default="deep",
38 )
39 skyMap = pexConfig.ConfigField(
40 dtype=BaseSkyMap.ConfigClass,
41 doc="SkyMap configuration parameters, excluding position and radius"
42 )
43 borderSize = pexConfig.Field(
44 doc="additional border added to the bounding box of the calexps, in degrees",
45 dtype=float,
46 default=0.0
47 )
48 doAppend = pexConfig.Field(
49 doc="append another tract to an existing DiscreteSkyMap on disk, if present?",
50 dtype=bool,
51 default=False
52 )
53 doWrite = pexConfig.Field(
54 doc="persist the skyMap?",
55 dtype=bool,
56 default=True,
57 )
59 def setDefaults(self):
60 self.skyMap.tractOverlap = 0.0
63class MakeDiscreteSkyMapTask(pipeBase.Task):
64 """!Make a DiscreteSkyMap in a repository, using the bounding box of a set of calexps.
66 The command-line and run signatures and config are sufficiently different from MakeSkyMapTask
67 that we don't inherit from it, but it is a replacement, so we use the same config/metadata names.
68 """
69 ConfigClass = MakeDiscreteSkyMapConfig
70 _DefaultName = "makeDiscreteSkyMap"
72 @timeMethod
73 def run(self, wcs_bbox_tuple_list, oldSkyMap=None):
74 """Make a SkyMap from the bounds of the given set of calexp metadata.
76 Parameters
77 ----------
78 wcs_bbox_tuple_list : iterable
79 A list of tuples with each element expected to be a (Wcs, Box2I) pair
80 oldSkyMap : `lsst.skymap.DiscreteSkyMap`, option
81 The SkyMap to extend if appending
82 Returns
83 -------
84 struct : `lsst.pipe.base.Struct
85 The returned struct has one attribute, ``skyMap``, which holds the returned SkyMap
86 """
87 self.log.info("Extracting bounding boxes of %d images", len(wcs_bbox_tuple_list))
88 points = []
89 for wcs, boxI in wcs_bbox_tuple_list:
90 boxD = geom.Box2D(boxI)
91 points.extend(wcs.pixelToSky(corner).getVector() for corner in boxD.getCorners())
92 if len(points) == 0:
93 raise RuntimeError("No data found from which to compute convex hull")
94 self.log.info("Computing spherical convex hull")
95 polygon = lsst.sphgeom.ConvexPolygon.convexHull(points)
96 if polygon is None:
97 raise RuntimeError(
98 "Failed to compute convex hull of the vertices of all calexp bounding boxes; "
99 "they may not be hemispherical."
100 )
101 circle = polygon.getBoundingCircle()
103 skyMapConfig = DiscreteSkyMap.ConfigClass()
104 if oldSkyMap:
105 skyMapConfig.raList.extend(oldSkyMap.config.raList)
106 skyMapConfig.decList.extend(oldSkyMap.config.decList)
107 skyMapConfig.radiusList.extend(oldSkyMap.config.radiusList)
108 configIntersection = {k: getattr(self.config.skyMap, k)
109 for k in self.config.skyMap.toDict()
110 if k in skyMapConfig}
111 skyMapConfig.update(**configIntersection)
112 circleCenter = lsst.sphgeom.LonLat(circle.getCenter())
113 skyMapConfig.raList.append(circleCenter[0].asDegrees())
114 skyMapConfig.decList.append(circleCenter[1].asDegrees())
115 circleRadiusDeg = circle.getOpeningAngle().asDegrees()
116 skyMapConfig.radiusList.append(circleRadiusDeg + self.config.borderSize)
117 skyMap = DiscreteSkyMap(skyMapConfig)
119 for tractInfo in skyMap:
120 wcs = tractInfo.getWcs()
121 posBox = geom.Box2D(tractInfo.getBBox())
122 pixelPosList = (
123 posBox.getMin(),
124 geom.Point2D(posBox.getMaxX(), posBox.getMinY()),
125 posBox.getMax(),
126 geom.Point2D(posBox.getMinX(), posBox.getMaxY()),
127 )
128 skyPosList = [wcs.pixelToSky(pos).getPosition(geom.degrees) for pos in pixelPosList]
129 posStrList = ["(%0.3f, %0.3f)" % tuple(skyPos) for skyPos in skyPosList]
130 self.log.info("tract %s has corners %s (RA, Dec deg) and %s x %s patches",
131 tractInfo.getId(), ", ".join(posStrList),
132 tractInfo.getNumPatches()[0], tractInfo.getNumPatches()[1])
133 return pipeBase.Struct(
134 skyMap=skyMap
135 )