LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
patchInfo.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010 LSST Corporation.
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 <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 __all__ = ["PatchInfo"]
24 
25 import numbers
26 from collections.abc import Iterable
27 
28 from lsst.geom import Extent2I, Point2I, Box2I
29 from .detail import makeSkyPolygonFromBBox, Index2D
30 from .cellInfo import CellInfo
31 
32 
33 class PatchInfo:
34  """Information about a patch within a tract of a sky map.
35 
36  If cellInnerDimensions and cellBorder are set then the patch
37  will be gridded with cells.
38 
39  See `TractInfo` for more information.
40 
41  Parameters
42  ----------
43  index : `lsst.skymap.Index2D`
44  x,y index of patch (a pair of ints)
45  innerBBox : `lsst.geom.Box2I`
46  inner bounding box
47  outerBBox : `lsst.geom.Box2I`
48  inner bounding box
49  sequentialIndex : `int`
50  Patch sequential index
51  tractWcs : `lsst.afw.geom.SkyWcs`
52  Tract WCS object.
53  cellInnerDimensions : `Iterable` [`int`, `int`] or `lsst.geom.Extent2I`, optional
54  Inner dimensions of each cell (x,y pixels).
55  cellBorder : `int`, optional
56  Cell border size (pixels).
57  numCellsPerPatchInner : `int`, optional
58  Number of cells per inner patch region.
59  numCellsInPatchBorder : `int`, optional
60  Number of cells in the patch border.
61  """
62 
63  def __init__(self, index, innerBBox, outerBBox, sequentialIndex,
64  tractWcs,
65  cellInnerDimensions=(0, 0), cellBorder=0,
66  numCellsPerPatchInner=0, numCellsInPatchBorder=0):
67  self._index_index = index
68  self._sequentialIndex_sequentialIndex = sequentialIndex
69  self._innerBBox_innerBBox = innerBBox
70  self._outerBBox_outerBBox = outerBBox
71  self._wcs_wcs = tractWcs
72  if not outerBBox.contains(innerBBox):
73  raise RuntimeError("outerBBox=%s does not contain innerBBox=%s" % (outerBBox, innerBBox))
74  if not isinstance(cellInnerDimensions, (Iterable, Extent2I)):
75  raise ValueError("Input cellInnerDimensions is not an iterable.")
76  if len(cellInnerDimensions) != 2:
77  raise ValueError("Input cellInnerDimensions does not have two values.")
78  self._cellInnerDimensions_cellInnerDimensions = Extent2I(*cellInnerDimensions)
79  self._cellBorder_cellBorder = cellBorder
80  self._numCellsInPatchBorder_numCellsInPatchBorder = numCellsInPatchBorder
81  if numCellsPerPatchInner == 0:
82  self._numCells_numCells = Index2D(x=0, y=0)
83  else:
84  # There are numCellsInPatchBorder extra boundary cell on each side
85  self._numCells_numCells = Index2D(x=numCellsPerPatchInner + 2*numCellsInPatchBorder,
86  y=numCellsPerPatchInner + 2*numCellsInPatchBorder)
87 
88  def getIndex(self):
89  """Return patch index: a tuple of (x, y)
90 
91  Returns
92  -------
93  result : `lsst.skymap.Index2D`
94  Patch index (x, y).
95  """
96  return self._index_index
97 
98  index = property(getIndex)
99 
101  """Return patch sequential index.
102 
103  Returns
104  -------
105  result : `int`
106  Sequential patch index.
107  """
108  return self._sequentialIndex_sequentialIndex
109 
110  sequential_index = property(getSequentialIndex)
111 
112  def getWcs(self):
113  """Return the associated tract wcs
114 
115  Returns
116  -------
117  wcs : `lsst.afw.geom.SkyWcs`
118  Tract WCS.
119  """
120  return self._wcs_wcs
121 
122  wcs = property(getWcs)
123 
124  def getInnerBBox(self):
125  """Get inner bounding box.
126 
127  Returns
128  -------
129  bbox : `lsst.geom.Box2I`
130  The inner bounding Box.
131  """
132  return self._innerBBox_innerBBox
133 
134  inner_bbox = property(getInnerBBox)
135 
136  def getOuterBBox(self):
137  """Get outer bounding box.
138 
139  Returns
140  -------
141  bbox : `lsst.geom.Box2I`
142  The outer bounding Box.
143  """
144  return self._outerBBox_outerBBox
145 
146  outer_bbox = property(getOuterBBox)
147 
148  def getInnerSkyPolygon(self, tractWcs=None):
149  """Get the inner on-sky region.
150 
151  Parameters
152  ----------
153  tractWcs : `lsst.afw.image.SkyWcs`, optional
154  WCS for the associated tract.
155 
156  Returns
157  -------
158  result : `lsst.sphgeom.ConvexPolygon`
159  The inner sky region.
160  """
161  _tractWcs = tractWcs if tractWcs is not None else self._wcs_wcs
162  return makeSkyPolygonFromBBox(bbox=self.getInnerBBoxgetInnerBBox(), wcs=_tractWcs)
163 
164  @property
165  def inner_sky_polygon(self):
166  return self.getInnerSkyPolygongetInnerSkyPolygon()
167 
168  def getOuterSkyPolygon(self, tractWcs=None):
169  """Get the outer on-sky region.
170 
171  Parameters
172  ----------
173  tractWcs : `lsst.afw.image.SkyWcs`, optional
174  WCS for the associated tract.
175 
176  Returns
177  -------
178  result : `lsst.sphgeom.ConvexPolygon`
179  The outer sky region.
180  """
181  _tractWcs = tractWcs if tractWcs is not None else self._wcs_wcs
182  return makeSkyPolygonFromBBox(bbox=self.getOuterBBoxgetOuterBBox(), wcs=_tractWcs)
183 
184  @property
185  def outer_sky_polygon(self):
186  return self.getOuterSkyPolygongetOuterSkyPolygon()
187 
188  def getNumCells(self):
189  """Get the number of cells in x, y.
190 
191  May return (0, 0) if no cells are defined.
192 
193  Returns
194  -------
195  result : `lsst.skymap.Index2D`
196  The number of cells in x, y.
197  """
198  return self._numCells_numCells
199 
200  num_cells = property(getNumCells)
201 
202  def getCellBorder(self):
203  return self._cellBorder_cellBorder
204 
205  cell_border = property(getCellBorder)
206 
207  def getCellInfo(self, index):
208  """Return information for the specified cell.
209 
210  Parameters
211  ----------
212  index : `lsst.skymap.Index2D` or `int`
213  Index of cell, as `Index2D`, or `Iterable` [`int`, `int`];
214  or a sequential index as returned by getSequentialCellIndex;
215  negative values are not supported.
216 
217  Returns
218  -------
219  result : `lsst.skymap.CellInfo`
220  The cell info for that index.
221 
222  Raises
223  ------
224  IndexError
225  If index is out of range.
226  """
227  if self._numCells_numCells.x == 0 or self._numCells_numCells.y == 0:
228  raise IndexError("Patch does not contain cells.")
229  if isinstance(index, Index2D):
230  _index = index
231  else:
232  if isinstance(index, numbers.Number):
233  _index = self.getCellIndexPairgetCellIndexPair(index)
234  else:
235  _index = Index2D(*index)
236  if (not 0 <= _index.x < self._numCells_numCells.x) \
237  or (not 0 <= _index.y < self._numCells_numCells.y):
238  raise IndexError("Cell index %s is not in range [0-%d, 0-%d]" %
239  (_index, self._numCells_numCells.x - 1, self._numCells_numCells.y - 1))
240  # We offset the index by numCellsInPatchBorder because the cells
241  # start outside the inner dimensions.
242  # The cells are defined relative to the patch bounding box (within the tract).
243  patchInnerBBox = self.getInnerBBoxgetInnerBBox()
244  innerMin = Point2I(*[(_index[i] - self._numCellsInPatchBorder_numCellsInPatchBorder)*self._cellInnerDimensions_cellInnerDimensions[i]
245  + patchInnerBBox.getBegin()[i]
246  for i in range(2)])
247 
248  innerBBox = Box2I(innerMin, self._cellInnerDimensions_cellInnerDimensions)
249  outerBBox = Box2I(innerBBox)
250  outerBBox.grow(self._cellBorder_cellBorder)
251 
252  return CellInfo(
253  index=_index,
254  innerBBox=innerBBox,
255  outerBBox=outerBBox,
256  sequentialIndex=self.getSequentialCellIndexFromPairgetSequentialCellIndexFromPair(_index),
257  tractWcs=self._wcs_wcs
258  )
259 
261  """Get dimensions of inner region of the cells (all are the same)
262  """
263  return self._cellInnerDimensions_cellInnerDimensions
264 
265  cell_inner_dimensions = property(getCellInnerDimensions)
266 
267  def getSequentialCellIndex(self, cellInfo):
268  """Return a single integer that uniquely identifies
269  the given cell within this patch.
270 
271  Parameters
272  ----------
273  cellInfo : `lsst.skymap.CellInfo`
274 
275  Returns
276  -------
277  sequentialIndex : `int`
278 
279  Raises
280  ------
281  IndexError
282  If index is out of range.
283  """
284  index = cellInfo.getIndex()
285  return self.getSequentialCellIndexFromPairgetSequentialCellIndexFromPair(index)
286 
288  """Return a single integer that uniquely identifies
289  the given cell within this patch.
290 
291  Parameters
292  ----------
293  index : `lsst.skymap.Index2D`
294 
295  Returns
296  -------
297  sequentialIndex : `int`
298 
299  Raises
300  ------
301  IndexError
302  If index is out of range.
303  """
304  if isinstance(index, Index2D):
305  _index = index
306  else:
307  _index = Index2D(*index)
308  nx, ny = self.getNumCellsgetNumCells()
309  return nx*_index.y + _index.x
310 
311  def getCellIndexPair(self, sequentialIndex):
312  """Convert a sequential index into an index pair.
313 
314  Parameters
315  ----------
316  sequentialIndex : `int`
317 
318  Returns
319  -------
320  x, y : `lsst.skymap.Index2D`
321 
322  Raises
323  ------
324  IndexError
325  If index is out of range.
326  """
327  if self._numCells_numCells.x == 0 or self._numCells_numCells.y == 0:
328  raise IndexError("Patch does not contain cells.")
329 
330  nx, ny = self.getNumCellsgetNumCells()
331  x = sequentialIndex % nx
332  y = sequentialIndex // nx
333  return Index2D(x=x, y=y)
334 
335  def __iter__(self):
336  xNum, yNum = self.getNumCellsgetNumCells()
337  for y in range(yNum):
338  for x in range(xNum):
339  yield self.getCellInfogetCellInfo(Index2D(x=x, y=y))
340 
341  def __len__(self):
342  xNum, yNum = self.getNumCellsgetNumCells()
343  return xNum*yNum
344 
345  def __getitem__(self, index):
346  return self.getCellInfogetCellInfo(index)
347 
348  def __eq__(self, rhs):
349  return (self.getIndexgetIndex() == rhs.getIndex()) \
350  and (self.getInnerBBoxgetInnerBBox() == rhs.getInnerBBox()) \
351  and (self.getOuterBBoxgetOuterBBox() == rhs.getOuterBBox()) \
352  and (self.getNumCellsgetNumCells() == rhs.getNumCells()) \
353  and (self.getCellBordergetCellBorder() == rhs.getCellBorder())
354 
355  def __ne__(self, rhs):
356  return not self.__eq____eq__(rhs)
357 
358  def __str__(self):
359  return "PatchInfo(index=%s)" % (self.getIndexgetIndex(),)
360 
361  def __repr__(self):
362  if self.getNumCellsgetNumCells()[0] > 0:
363  return ("PatchInfo(index=%s, innerBBox=%s, outerBBox=%s, cellInnerDimensions=%s, "
364  "cellBorder=%s, numCellsPerPatchInner=%s)") % \
365  (self.getIndexgetIndex(), self.getInnerBBoxgetInnerBBox(), self.getOuterBBoxgetOuterBBox(),
366  self.getCellInnerDimensionsgetCellInnerDimensions(), self.getCellBordergetCellBorder(),
367  self.getNumCellsgetNumCells()[0])
368  else:
369  return "PatchInfo(index=%s, innerBBox=%s, outerBBox=%s)" % \
370  (self.getIndexgetIndex(), self.getInnerBBoxgetInnerBBox(), self.getOuterBBoxgetOuterBBox())
An integer coordinate rectangle.
Definition: Box.h:55
def getOuterSkyPolygon(self, tractWcs=None)
Definition: patchInfo.py:168
def getCellInfo(self, index)
Definition: patchInfo.py:207
def __init__(self, index, innerBBox, outerBBox, sequentialIndex, tractWcs, cellInnerDimensions=(0, 0), cellBorder=0, numCellsPerPatchInner=0, numCellsInPatchBorder=0)
Definition: patchInfo.py:66
def getSequentialCellIndex(self, cellInfo)
Definition: patchInfo.py:267
def getCellIndexPair(self, sequentialIndex)
Definition: patchInfo.py:311
def getSequentialCellIndexFromPair(self, index)
Definition: patchInfo.py:287
def getInnerSkyPolygon(self, tractWcs=None)
Definition: patchInfo.py:148
def __getitem__(self, index)
Definition: patchInfo.py:345
Extent< int, 2 > Extent2I
Definition: Extent.h:397
Point< int, 2 > Point2I
Definition: Point.h:321
def makeSkyPolygonFromBBox(bbox, wcs)
Definition: utils.py:61