Coverage for python/lsst/skymap/patchInfo.py: 31%

113 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-03-23 10:57 +0000

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 

25import numbers 

26from collections.abc import Iterable 

27 

28from lsst.geom import Extent2I, Point2I, Box2I 

29from .detail import makeSkyPolygonFromBBox, Index2D 

30from .cellInfo import CellInfo 

31 

32 

33class 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 : `~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 """ 

63 

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) 

88 

89 def getIndex(self): 

90 """Return patch index: a tuple of (x, y) 

91 

92 Returns 

93 ------- 

94 result : `lsst.skymap.Index2D` 

95 Patch index (x, y). 

96 """ 

97 return self._index 

98 

99 index = property(getIndex) 

100 

101 def getSequentialIndex(self): 

102 """Return patch sequential index. 

103 

104 Returns 

105 ------- 

106 result : `int` 

107 Sequential patch index. 

108 """ 

109 return self._sequentialIndex 

110 

111 sequential_index = property(getSequentialIndex) 

112 

113 def getWcs(self): 

114 """Return the associated tract wcs 

115 

116 Returns 

117 ------- 

118 wcs : `lsst.afw.geom.SkyWcs` 

119 Tract WCS. 

120 """ 

121 return self._wcs 

122 

123 wcs = property(getWcs) 

124 

125 def getInnerBBox(self): 

126 """Get inner bounding box. 

127 

128 Returns 

129 ------- 

130 bbox : `lsst.geom.Box2I` 

131 The inner bounding Box. 

132 """ 

133 return self._innerBBox 

134 

135 inner_bbox = property(getInnerBBox) 

136 

137 def getOuterBBox(self): 

138 """Get outer bounding box. 

139 

140 Returns 

141 ------- 

142 bbox : `lsst.geom.Box2I` 

143 The outer bounding Box. 

144 """ 

145 return self._outerBBox 

146 

147 outer_bbox = property(getOuterBBox) 

148 

149 def getInnerSkyPolygon(self, tractWcs=None): 

150 """Get the inner on-sky region. 

151 

152 Parameters 

153 ---------- 

154 tractWcs : `lsst.afw.image.SkyWcs`, optional 

155 WCS for the associated tract. 

156 

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) 

164 

165 @property 

166 def inner_sky_polygon(self): 

167 return self.getInnerSkyPolygon() 

168 

169 def getOuterSkyPolygon(self, tractWcs=None): 

170 """Get the outer on-sky region. 

171 

172 Parameters 

173 ---------- 

174 tractWcs : `lsst.afw.image.SkyWcs`, optional 

175 WCS for the associated tract. 

176 

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) 

184 

185 @property 

186 def outer_sky_polygon(self): 

187 return self.getOuterSkyPolygon() 

188 

189 def getNumCells(self): 

190 """Get the number of cells in x, y. 

191 

192 May return (0, 0) if no cells are defined. 

193 

194 Returns 

195 ------- 

196 result : `lsst.skymap.Index2D` 

197 The number of cells in x, y. 

198 """ 

199 return self._numCells 

200 

201 num_cells = property(getNumCells) 

202 

203 def getCellBorder(self): 

204 return self._cellBorder 

205 

206 cell_border = property(getCellBorder) 

207 

208 def getCellInfo(self, index): 

209 """Return information for the specified cell. 

210 

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. 

217 

218 Returns 

219 ------- 

220 result : `lsst.skymap.CellInfo` 

221 The cell info for that index. 

222 

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)]) 

249 

250 innerBBox = Box2I(innerMin, self._cellInnerDimensions) 

251 outerBBox = Box2I(innerBBox) 

252 outerBBox.grow(self._cellBorder) 

253 

254 return CellInfo( 

255 index=_index, 

256 innerBBox=innerBBox, 

257 outerBBox=outerBBox, 

258 sequentialIndex=self.getSequentialCellIndexFromPair(_index), 

259 tractWcs=self._wcs 

260 ) 

261 

262 def getCellInnerDimensions(self): 

263 """Get dimensions of inner region of the cells (all are the same) 

264 """ 

265 return self._cellInnerDimensions 

266 

267 cell_inner_dimensions = property(getCellInnerDimensions) 

268 

269 def getSequentialCellIndex(self, cellInfo): 

270 """Return a single integer that uniquely identifies 

271 the given cell within this patch. 

272 

273 Parameters 

274 ---------- 

275 cellInfo : `lsst.skymap.CellInfo` 

276 

277 Returns 

278 ------- 

279 sequentialIndex : `int` 

280 

281 Raises 

282 ------ 

283 IndexError 

284 Raised if index is out of range. 

285 """ 

286 index = cellInfo.getIndex() 

287 return self.getSequentialCellIndexFromPair(index) 

288 

289 def getSequentialCellIndexFromPair(self, index): 

290 """Return a single integer that uniquely identifies 

291 the given cell within this patch. 

292 

293 Parameters 

294 ---------- 

295 index : `lsst.skymap.Index2D` 

296 

297 Returns 

298 ------- 

299 sequentialIndex : `int` 

300 

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 

312 

313 def getCellIndexPair(self, sequentialIndex): 

314 """Convert a sequential index into an index pair. 

315 

316 Parameters 

317 ---------- 

318 sequentialIndex : `int` 

319 

320 Returns 

321 ------- 

322 x, y : `lsst.skymap.Index2D` 

323 

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.") 

331 

332 nx, ny = self.getNumCells() 

333 x = sequentialIndex % nx 

334 y = sequentialIndex // nx 

335 return Index2D(x=x, y=y) 

336 

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)) 

342 

343 def __len__(self): 

344 xNum, yNum = self.getNumCells() 

345 return xNum*yNum 

346 

347 def __getitem__(self, index): 

348 return self.getCellInfo(index) 

349 

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()) 

356 

357 def __ne__(self, rhs): 

358 return not self.__eq__(rhs) 

359 

360 def __str__(self): 

361 return "PatchInfo(index=%s)" % (self.getIndex(),) 

362 

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())