LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
LSST Data Management Base Package
makeDiscreteSkyMap.py
Go to the documentation of this file.
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 #
22 import sys
23 import traceback
24 import lsst.sphgeom
25 
26 import lsst.geom as geom
27 import lsst.pex.config as pexConfig
28 import lsst.pipe.base as pipeBase
29 from lsst.skymap import DiscreteSkyMap, BaseSkyMap
30 from lsst.pipe.base import ArgumentParser
31 
32 
33 class MakeDiscreteSkyMapConfig(pexConfig.Config):
34  """Config for MakeDiscreteSkyMapTask
35  """
36  coaddName = pexConfig.Field(
37  doc="coadd name, e.g. deep, goodSeeing, chiSquared",
38  dtype=str,
39  default="deep",
40  )
41  skyMap = pexConfig.ConfigField(
42  dtype=BaseSkyMap.ConfigClass,
43  doc="SkyMap configuration parameters, excluding position and radius"
44  )
45  borderSize = pexConfig.Field(
46  doc="additional border added to the bounding box of the calexps, in degrees",
47  dtype=float,
48  default=0.0
49  )
50  doAppend = pexConfig.Field(
51  doc="append another tract to an existing DiscreteSkyMap on disk, if present?",
52  dtype=bool,
53  default=False
54  )
55  doWrite = pexConfig.Field(
56  doc="persist the skyMap?",
57  dtype=bool,
58  default=True,
59  )
60 
61  def setDefaults(self):
62  self.skyMapskyMap.tractOverlap = 0.0
63 
64 
65 class MakeDiscreteSkyMapRunner(pipeBase.TaskRunner):
66  """Run a task with all dataRefs at once, rather than one dataRef at a time.
67 
68  Call the run method of the task using two positional arguments:
69  - butler: data butler
70  - dataRefList: list of all dataRefs,
71  """
72  @staticmethod
73  def getTargetList(parsedCmd):
74  return [(parsedCmd.butler, parsedCmd.id.refList)]
75 
76  def __call__(self, args):
77  """
78  @param args Arguments for Task.run()
79 
80  @return:
81  - None if self.doReturnResults false
82  - A pipe_base Struct containing these fields if self.doReturnResults true:
83  - dataRef: the provided data reference
84  - metadata: task metadata after execution of run
85  - result: result returned by task run, or None if the task fails
86  """
87  butler, dataRefList = args
88  task = self.TaskClass(config=self.config, log=self.log)
89  result = None # in case the task fails
90  exitStatus = 0 # exit status for shell
91  if self.doRaise:
92  result = task.runDataRef(butler, dataRefList)
93  else:
94  try:
95  result = task.runDataRef(butler, dataRefList)
96  except Exception as e:
97  task.log.fatal("Failed: %s", e)
98  exitStatus = 1
99  if not isinstance(e, pipeBase.TaskError):
100  traceback.print_exc(file=sys.stderr)
101  for dataRef in dataRefList:
102  task.writeMetadata(dataRef)
103 
104  if self.doReturnResults:
105  return pipeBase.Struct(
106  dataRefList=dataRefList,
107  metadata=task.metadata,
108  result=result,
109  exitStatus=exitStatus,
110  )
111  else:
112  return pipeBase.Struct(
113  exitStatus=exitStatus,
114  )
115 
116 
117 class MakeDiscreteSkyMapTask(pipeBase.CmdLineTask):
118  """!Make a DiscreteSkyMap in a repository, using the bounding box of a set of calexps.
119 
120  The command-line and run signatures and config are sufficiently different from MakeSkyMapTask
121  that we don't inherit from it, but it is a replacement, so we use the same config/metadata names.
122  """
123  ConfigClass = MakeDiscreteSkyMapConfig
124  _DefaultName = "makeDiscreteSkyMap"
125  RunnerClass = MakeDiscreteSkyMapRunner
126 
127  def __init__(self, **kwargs):
128  super().__init__(**kwargs)
129 
130  def runDataRef(self, butler, dataRefList):
131  """Make a skymap from the bounds of the given set of calexps using the butler.
132 
133  Parameters
134  ----------
135  butler : `lsst.daf.persistence.Butler`
136  Gen2 data butler used to save the SkyMap
137  dataRefList : iterable
138  A list of Gen2 data refs of calexps used to determin the size and pointing of the SkyMap
139  Returns
140  -------
141  struct : `lsst.pipe.base.Struct`
142  The returned struct has one attribute, ``skyMap``, which holds the returned SkyMap
143  """
144  wcs_bbox_tuple_list = []
145  oldSkyMap = None
146  datasetName = self.config.coaddName + "Coadd_skyMap"
147  for dataRef in dataRefList:
148  if not dataRef.datasetExists("calexp"):
149  self.log.warning("CalExp for %s does not exist: ignoring", dataRef.dataId)
150  continue
151  wcs_bbox_tuple_list.append((dataRef.get("calexp_wcs", immediate=True),
152  dataRef.get("calexp_bbox", immediate=True)))
153  if self.config.doAppend and butler.datasetExists(datasetName):
154  oldSkyMap = butler.get(datasetName, immediate=True)
155  if not isinstance(oldSkyMap.config, DiscreteSkyMap.ConfigClass):
156  raise TypeError("Cannot append to existing non-discrete skymap")
157  compareLog = []
158  if not self.config.skyMap.compare(oldSkyMap.config, output=compareLog.append):
159  raise ValueError("Cannot append to existing skymap - configurations differ:", *compareLog)
160  result = self.runrun(wcs_bbox_tuple_list, oldSkyMap)
161  if self.config.doWrite:
162  butler.put(result.skyMap, datasetName)
163  return result
164 
165  @pipeBase.timeMethod
166  def run(self, wcs_bbox_tuple_list, oldSkyMap=None):
167  """Make a SkyMap from the bounds of the given set of calexp metadata.
168 
169  Parameters
170  ----------
171  wcs_bbox_tuple_list : iterable
172  A list of tuples with each element expected to be a (Wcs, Box2I) pair
173  oldSkyMap : `lsst.skymap.DiscreteSkyMap`, option
174  The SkyMap to extend if appending
175  Returns
176  -------
177  struct : `lsst.pipe.base.Struct
178  The returned struct has one attribute, ``skyMap``, which holds the returned SkyMap
179  """
180  self.log.info("Extracting bounding boxes of %d images", len(wcs_bbox_tuple_list))
181  points = []
182  for wcs, boxI in wcs_bbox_tuple_list:
183  boxD = geom.Box2D(boxI)
184  points.extend(wcs.pixelToSky(corner).getVector() for corner in boxD.getCorners())
185  if len(points) == 0:
186  raise RuntimeError("No data found from which to compute convex hull")
187  self.log.info("Computing spherical convex hull")
188  polygon = lsst.sphgeom.ConvexPolygon.convexHull(points)
189  if polygon is None:
190  raise RuntimeError(
191  "Failed to compute convex hull of the vertices of all calexp bounding boxes; "
192  "they may not be hemispherical."
193  )
194  circle = polygon.getBoundingCircle()
195 
196  skyMapConfig = DiscreteSkyMap.ConfigClass()
197  if oldSkyMap:
198  skyMapConfig.raList.extend(oldSkyMap.config.raList)
199  skyMapConfig.decList.extend(oldSkyMap.config.decList)
200  skyMapConfig.radiusList.extend(oldSkyMap.config.radiusList)
201  skyMapConfig.update(**self.config.skyMap.toDict())
202  circleCenter = lsst.sphgeom.LonLat(circle.getCenter())
203  skyMapConfig.raList.append(circleCenter[0].asDegrees())
204  skyMapConfig.decList.append(circleCenter[1].asDegrees())
205  circleRadiusDeg = circle.getOpeningAngle().asDegrees()
206  skyMapConfig.radiusList.append(circleRadiusDeg + self.config.borderSize)
207  skyMap = DiscreteSkyMap(skyMapConfig)
208 
209  for tractInfo in skyMap:
210  wcs = tractInfo.getWcs()
211  posBox = geom.Box2D(tractInfo.getBBox())
212  pixelPosList = (
213  posBox.getMin(),
214  geom.Point2D(posBox.getMaxX(), posBox.getMinY()),
215  posBox.getMax(),
216  geom.Point2D(posBox.getMinX(), posBox.getMaxY()),
217  )
218  skyPosList = [wcs.pixelToSky(pos).getPosition(geom.degrees) for pos in pixelPosList]
219  posStrList = ["(%0.3f, %0.3f)" % tuple(skyPos) for skyPos in skyPosList]
220  self.log.info("tract %s has corners %s (RA, Dec deg) and %s x %s patches",
221  tractInfo.getId(), ", ".join(posStrList),
222  tractInfo.getNumPatches()[0], tractInfo.getNumPatches()[1])
223  return pipeBase.Struct(
224  skyMap=skyMap
225  )
226 
227  def _getConfigName(self):
228  """Return None to disable saving config
229 
230  There's only one SkyMap per repository, so the config is redundant, and checking it means we can't
231  easily overwrite or append to an existing repository.
232  """
233  return None
234 
235  def _getMetadataName(self):
236  """Return None to disable saving metadata
237 
238  The metadata is not interesting, and by not saving it we can eliminate a dataset type.
239  """
240  return None
241 
242  @classmethod
243  def _makeArgumentParser(cls):
244  parser = ArgumentParser(name=cls._DefaultName_DefaultName)
245  parser.add_id_argument(name="--id", datasetType="calexp", help="data ID, e.g. --id visit=123 ccd=1,2")
246  return parser
A floating-point coordinate rectangle geometry.
Definition: Box.h:413
Make a DiscreteSkyMap in a repository, using the bounding box of a set of calexps.
def run(self, wcs_bbox_tuple_list, oldSkyMap=None)
static ConvexPolygon convexHull(std::vector< UnitVector3d > const &points)
convexHull returns the convex hull of the given set of points if it exists and throws an exception ot...
Definition: ConvexPolygon.h:65
LonLat represents a spherical coordinate (longitude/latitude angle) pair.
Definition: LonLat.h:48