Coverage for python/lsst/skymap/patchInfo.py: 31%
113 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-28 03:05 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-28 03:05 -0700
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#
23__all__ = ["PatchInfo"]
25import numbers
26from collections.abc import Iterable
28from lsst.geom import Extent2I, Point2I, Box2I
29from .detail import makeSkyPolygonFromBBox, Index2D
30from .cellInfo import CellInfo
33class PatchInfo:
34 """Information about a patch within a tract of a sky map.
36 If cellInnerDimensions and cellBorder are set then the patch
37 will be gridded with cells.
39 See `TractInfo` for more information.
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 : `~collections.abc.Iterable` of 2 `int` or \
54 `lsst.geom.Extent2I`, optional
55 Inner dimensions of each cell (x,y pixels).
56 cellBorder : `int`, optional
57 Cell border size (pixels).
58 numCellsPerPatchInner : `int`, optional
59 Number of cells per inner patch region.
60 numCellsInPatchBorder : `int`, optional
61 Number of cells in the patch border.
62 """
64 def __init__(self, index, innerBBox, outerBBox, sequentialIndex,
65 tractWcs,
66 cellInnerDimensions=(0, 0), cellBorder=0,
67 numCellsPerPatchInner=0, numCellsInPatchBorder=0):
68 self._index = index
69 self._sequentialIndex = sequentialIndex
70 self._innerBBox = innerBBox
71 self._outerBBox = outerBBox
72 self._wcs = tractWcs
73 if not outerBBox.contains(innerBBox):
74 raise RuntimeError("outerBBox=%s does not contain innerBBox=%s" % (outerBBox, innerBBox))
75 if not isinstance(cellInnerDimensions, (Iterable, Extent2I)):
76 raise ValueError("Input cellInnerDimensions is not an iterable.")
77 if len(cellInnerDimensions) != 2:
78 raise ValueError("Input cellInnerDimensions does not have two values.")
79 self._cellInnerDimensions = Extent2I(*cellInnerDimensions)
80 self._cellBorder = cellBorder
81 self._numCellsInPatchBorder = numCellsInPatchBorder
82 if numCellsPerPatchInner == 0:
83 self._numCells = Index2D(x=0, y=0)
84 else:
85 # There are numCellsInPatchBorder extra boundary cell on each side
86 self._numCells = Index2D(x=numCellsPerPatchInner + 2*numCellsInPatchBorder,
87 y=numCellsPerPatchInner + 2*numCellsInPatchBorder)
89 def getIndex(self):
90 """Return patch index: a tuple of (x, y)
92 Returns
93 -------
94 result : `lsst.skymap.Index2D`
95 Patch index (x, y).
96 """
97 return self._index
99 index = property(getIndex)
101 def getSequentialIndex(self):
102 """Return patch sequential index.
104 Returns
105 -------
106 result : `int`
107 Sequential patch index.
108 """
109 return self._sequentialIndex
111 sequential_index = property(getSequentialIndex)
113 def getWcs(self):
114 """Return the associated tract wcs
116 Returns
117 -------
118 wcs : `lsst.afw.geom.SkyWcs`
119 Tract WCS.
120 """
121 return self._wcs
123 wcs = property(getWcs)
125 def getInnerBBox(self):
126 """Get inner bounding box.
128 Returns
129 -------
130 bbox : `lsst.geom.Box2I`
131 The inner bounding Box.
132 """
133 return self._innerBBox
135 inner_bbox = property(getInnerBBox)
137 def getOuterBBox(self):
138 """Get outer bounding box.
140 Returns
141 -------
142 bbox : `lsst.geom.Box2I`
143 The outer bounding Box.
144 """
145 return self._outerBBox
147 outer_bbox = property(getOuterBBox)
149 def getInnerSkyPolygon(self, tractWcs=None):
150 """Get the inner on-sky region.
152 Parameters
153 ----------
154 tractWcs : `lsst.afw.image.SkyWcs`, optional
155 WCS for the associated tract.
157 Returns
158 -------
159 result : `lsst.sphgeom.ConvexPolygon`
160 The inner sky region.
161 """
162 _tractWcs = tractWcs if tractWcs is not None else self._wcs
163 return makeSkyPolygonFromBBox(bbox=self.getInnerBBox(), wcs=_tractWcs)
165 @property
166 def inner_sky_polygon(self):
167 return self.getInnerSkyPolygon()
169 def getOuterSkyPolygon(self, tractWcs=None):
170 """Get the outer on-sky region.
172 Parameters
173 ----------
174 tractWcs : `lsst.afw.image.SkyWcs`, optional
175 WCS for the associated tract.
177 Returns
178 -------
179 result : `lsst.sphgeom.ConvexPolygon`
180 The outer sky region.
181 """
182 _tractWcs = tractWcs if tractWcs is not None else self._wcs
183 return makeSkyPolygonFromBBox(bbox=self.getOuterBBox(), wcs=_tractWcs)
185 @property
186 def outer_sky_polygon(self):
187 return self.getOuterSkyPolygon()
189 def getNumCells(self):
190 """Get the number of cells in x, y.
192 May return (0, 0) if no cells are defined.
194 Returns
195 -------
196 result : `lsst.skymap.Index2D`
197 The number of cells in x, y.
198 """
199 return self._numCells
201 num_cells = property(getNumCells)
203 def getCellBorder(self):
204 return self._cellBorder
206 cell_border = property(getCellBorder)
208 def getCellInfo(self, index):
209 """Return information for the specified cell.
211 Parameters
212 ----------
213 index : `lsst.skymap.Index2D` or `~collections.abc.Iterable` of 2 `int`
214 Index of cell, as `Index2D` ,or two integers,
215 or a sequential index as returned by getSequentialCellIndex;
216 negative values are not supported.
218 Returns
219 -------
220 result : `lsst.skymap.CellInfo`
221 The cell info for that index.
223 Raises
224 ------
225 IndexError
226 Raised if index is out of range.
227 """
228 if self._numCells.x == 0 or self._numCells.y == 0:
229 raise IndexError("Patch does not contain cells.")
230 if isinstance(index, Index2D):
231 _index = index
232 else:
233 if isinstance(index, numbers.Number):
234 _index = self.getCellIndexPair(index)
235 else:
236 _index = Index2D(*index)
237 if (not 0 <= _index.x < self._numCells.x) \
238 or (not 0 <= _index.y < self._numCells.y):
239 raise IndexError("Cell index %s is not in range [0-%d, 0-%d]" %
240 (_index, self._numCells.x - 1, self._numCells.y - 1))
241 # We offset the index by numCellsInPatchBorder because the cells
242 # start outside the inner dimensions.
243 # The cells are defined relative to the patch bounding box (within the
244 # tract).
245 patchInnerBBox = self.getInnerBBox()
246 innerMin = Point2I(*[(_index[i] - self._numCellsInPatchBorder)*self._cellInnerDimensions[i]
247 + patchInnerBBox.getBegin()[i]
248 for i in range(2)])
250 innerBBox = Box2I(innerMin, self._cellInnerDimensions)
251 outerBBox = Box2I(innerBBox)
252 outerBBox.grow(self._cellBorder)
254 return CellInfo(
255 index=_index,
256 innerBBox=innerBBox,
257 outerBBox=outerBBox,
258 sequentialIndex=self.getSequentialCellIndexFromPair(_index),
259 tractWcs=self._wcs
260 )
262 def getCellInnerDimensions(self):
263 """Get dimensions of inner region of the cells (all are the same)
264 """
265 return self._cellInnerDimensions
267 cell_inner_dimensions = property(getCellInnerDimensions)
269 def getSequentialCellIndex(self, cellInfo):
270 """Return a single integer that uniquely identifies
271 the given cell within this patch.
273 Parameters
274 ----------
275 cellInfo : `lsst.skymap.CellInfo`
277 Returns
278 -------
279 sequentialIndex : `int`
281 Raises
282 ------
283 IndexError
284 Raised if index is out of range.
285 """
286 index = cellInfo.getIndex()
287 return self.getSequentialCellIndexFromPair(index)
289 def getSequentialCellIndexFromPair(self, index):
290 """Return a single integer that uniquely identifies
291 the given cell within this patch.
293 Parameters
294 ----------
295 index : `lsst.skymap.Index2D`
297 Returns
298 -------
299 sequentialIndex : `int`
301 Raises
302 ------
303 IndexError
304 Raised if index is out of range.
305 """
306 if isinstance(index, Index2D):
307 _index = index
308 else:
309 _index = Index2D(*index)
310 nx, ny = self.getNumCells()
311 return nx*_index.y + _index.x
313 def getCellIndexPair(self, sequentialIndex):
314 """Convert a sequential index into an index pair.
316 Parameters
317 ----------
318 sequentialIndex : `int`
320 Returns
321 -------
322 x, y : `lsst.skymap.Index2D`
324 Raises
325 ------
326 IndexError
327 Raised if index is out of range.
328 """
329 if self._numCells.x == 0 or self._numCells.y == 0:
330 raise IndexError("Patch does not contain cells.")
332 nx, ny = self.getNumCells()
333 x = sequentialIndex % nx
334 y = sequentialIndex // nx
335 return Index2D(x=x, y=y)
337 def __iter__(self):
338 xNum, yNum = self.getNumCells()
339 for y in range(yNum):
340 for x in range(xNum):
341 yield self.getCellInfo(Index2D(x=x, y=y))
343 def __len__(self):
344 xNum, yNum = self.getNumCells()
345 return xNum*yNum
347 def __getitem__(self, index):
348 return self.getCellInfo(index)
350 def __eq__(self, rhs):
351 return (self.getIndex() == rhs.getIndex()) \
352 and (self.getInnerBBox() == rhs.getInnerBBox()) \
353 and (self.getOuterBBox() == rhs.getOuterBBox()) \
354 and (self.getNumCells() == rhs.getNumCells()) \
355 and (self.getCellBorder() == rhs.getCellBorder())
357 def __ne__(self, rhs):
358 return not self.__eq__(rhs)
360 def __str__(self):
361 return "PatchInfo(index=%s)" % (self.getIndex(),)
363 def __repr__(self):
364 if self.getNumCells()[0] > 0:
365 return ("PatchInfo(index=%s, innerBBox=%s, outerBBox=%s, cellInnerDimensions=%s, "
366 "cellBorder=%s, numCellsPerPatchInner=%s)") % \
367 (self.getIndex(), self.getInnerBBox(), self.getOuterBBox(),
368 self.getCellInnerDimensions(), self.getCellBorder(),
369 self.getNumCells()[0])
370 else:
371 return "PatchInfo(index=%s, innerBBox=%s, outerBBox=%s)" % \
372 (self.getIndex(), self.getInnerBBox(), self.getOuterBBox())