23 __all__ = [
"TractInfo"]
31 from .patchInfo
import PatchInfo, makeSkyPolygonFromBBox
35 """Information about a tract in a SkyMap sky pixelization 41 patchInnerDimensions : `tuple` of `int` 42 Dimensions of inner region of patches (x,y pixels). 44 Overlap between adjacent patches (in pixels) 45 ctrCoord : `lsst.geom.SpherePoint` 46 ICRS sky coordinate of center of inner region of tract; also used as 47 the CRVAL for the WCS. 48 vertexCoordList : `list` of `lsst.geom.SpherePoint` 49 Vertices that define the boundaries of the inner region. 50 tractOverlap : `lsst.geom.Angle` 51 Minimum overlap between adjacent sky tracts; this defines the minimum 52 distance the tract extends beyond the inner region in all directions. 53 wcs : `lsst.afw.image.SkyWcs` 54 WCS for tract. The reference pixel will be shifted as required so that 55 the lower left-hand pixel (index 0,0) has pixel position 0.0, 0.0. 59 The tract is subdivided into rectangular patches. Each patch has the 62 - An inner region defined by an inner bounding box. The inner regions of 63 the patches exactly tile the tract, and all inner regions have the same 64 dimensions. The tract is made larger as required to make this work. 66 - An outer region defined by an outer bounding box. The outer region 67 extends beyond the inner region by patchBorder pixels in all directions, 68 except there is no border at the edges of the tract. 69 Thus patches overlap each other but never extend off the tract. 70 If you do not want any overlap between adjacent patches then set 73 - An index that consists of a pair of integers: 75 * 0 <= x index < numPatches[0] 77 * 0 <= y index < numPatches[1] 79 Patch 0,0 is at the minimum corner of the tract bounding box. 81 - It is not enforced that ctrCoord is the center of vertexCoordList, but 85 def __init__(self, id, patchInnerDimensions, patchBorder, ctrCoord, vertexCoordList, tractOverlap, wcs):
88 assert len(patchInnerDimensions) == 2
91 raise TypeError(
"patchInnerDimensions=%s; must be two ints" % (patchInnerDimensions,))
101 def _minimumBoundingBox(self, wcs):
102 """Calculate the minimum bounding box for the tract, given the WCS. 104 The bounding box is created in the frame of the supplied WCS, 105 so that it's OK if the coordinates are negative. 107 We compute the bounding box that holds all the vertices and the 114 minBBoxD.include(wcs.skyToPixel(vertexCoord))
118 for i
in range(numAngles):
119 offAngle = angleIncr * i
120 offCoord = vertexCoord.offset(offAngle, halfOverlap)
121 pixPos = wcs.skyToPixel(offCoord)
122 minBBoxD.include(pixPos)
125 def _setupPatches(self, minBBox, wcs):
126 """Setup for patches of a particular size. 128 We grow the bounding box to hold an exact multiple of 129 the desired size (patchInnerDimensions), while keeping 130 the center roughly the same. We return the final 131 bounding box, and the number of patches in each dimension 136 minBBox : `lsst.geom.Box2I` 137 Minimum bounding box for tract 138 wcs : `lsst.afw.geom.SkyWcs` 143 bbox : `lsst.geom.Box2I 144 final bounding box, number of patches 148 bboxMin = bbox.getMin()
149 bboxDim = bbox.getDimensions()
152 num = (bboxDim[i] + innerDim - 1) // innerDim
153 deltaDim = (innerDim * num) - bboxDim[i]
155 bboxDim[i] = innerDim * num
156 bboxMin[i] -= deltaDim // 2
159 return bbox, numPatches
161 def _finalOrientation(self, bbox, wcs):
162 """Determine the final orientation 164 We offset everything so the lower-left corner is at 0,0 165 and compute the final Wcs. 169 bbox : `lsst.geom.Box2I` 170 Current bounding box. 171 wcs : `lsst.afw.geom.SkyWcs 176 finalBBox : `lsst.geom.Box2I` 177 Revised bounding box. 178 wcs : `lsst.afw.geom.SkyWcs` 184 pixPosOffset =
geom.Extent2D(finalBBox.getMinX() - bbox.getMinX(),
185 finalBBox.getMinY() - bbox.getMinY())
186 wcs = wcs.copyAtShiftedPixelOrigin(pixPosOffset)
187 return finalBBox, wcs
190 """Return a single integer that uniquely identifies the given patch 193 x, y = patchInfo.getIndex()
199 x = sequentialIndex % nx
200 y = (sequentialIndex - x) / nx
204 """Find the patch containing the specified coord. 208 coord : `lsst.geom.SpherePoint` 209 ICRS sky coordinate to search for. 213 result : `lsst.skymap.PatchInfo` 214 PatchInfo of patch whose inner bbox contains the specified coord 219 If coord is not in tract or we cannot determine the 220 pixel coordinate (which likely means the coord is off the tract). 223 pixel = self.
getWcs().skyToPixel(coord)
226 raise LookupError(
"Unable to determine pixel position for coordinate %s" % (coord,))
229 raise LookupError(
"coord %s is not in tract %s" % (coord, self.
getId()))
234 """Find patches containing the specified list of coords. 238 coordList : `list` of `lsst.geom.SpherePoint` 239 ICRS sky coordinates to search for. 243 result : `list` of `lsst.skymap.PatchInfo` 244 List of PatchInfo for patches that contain, or may contain, the 245 specified region. The list will be empty if there is no overlap. 251 - This may give incorrect answers on regions that are larger than a 254 - This uses a naive algorithm that may find some patches that do not 255 overlap the region (especially if the region is not a rectangle 256 aligned along patch x,y). 259 for coord
in coordList:
261 pixelPos = self.
getWcs().skyToPixel(coord)
265 box2D.include(pixelPos)
275 for xInd
in range(llPatchInd[0], urPatchInd[0]+1)
276 for yInd
in range(llPatchInd[1], urPatchInd[1]+1))
279 """Get bounding box of tract (as an geom.Box2I) 284 """Get ICRS sky coordinate of center of tract 285 (as an lsst.geom.SpherePoint) 295 """Get the number of patches in x, y. 299 result : `tuple` of `int` 300 The number of patches in x, y 308 """Return information for the specified patch. 312 index : `tuple` of `int` 313 Index of patch, as a pair of ints; 314 or a sequential index as returned by getSequentialPatchIndex; 315 negative values are not supported. 319 result : `lsst.skymap.PatchInfo` 320 The patch info for that index. 325 If index is out of range. 327 if isinstance(index, numbers.Number):
331 raise IndexError(
"Patch index %s is not in range [0-%d, 0-%d]" %
335 if not self._bbox.
contains(innerBBox):
337 "Bug: patch index %s valid but inner bbox=%s not contained in tract bbox=%s" %
338 (index, innerBBox, self._bbox))
341 outerBBox.clip(self._bbox)
349 """Get dimensions of inner region of the patches (all are the same) 354 """Get minimum overlap of adjacent sky tracts. 359 """Get list of ICRS sky coordinates of vertices that define the 360 boundary of the inner region. 364 **warning:** this is not a deep copy. 369 """Get inner on-sky region as a sphgeom.ConvexPolygon. 371 skyUnitVectors = [sp.getVector()
for sp
in self.
getVertexList()]
372 return ConvexPolygon.convexHull(skyUnitVectors)
375 """Get outer on-sky region as a sphgeom.ConvexPolygon 384 wcs : `lsst.afw.geom.SkyWcs` 385 The WCS of this tract 390 return "TractInfo(id=%s)" % (self.
_id,)
393 return "TractInfo(id=%s, ctrCoord=%s)" % (self.
_id, self.
_ctrCoord.getVector())
397 for y
in range(yNum):
398 for x
in range(xNum):
409 """Does this tract contain the coordinate?""" 411 pixels = self.
getWcs().skyToPixel(coord)
419 """Information for a tract specified explicitly. 421 A tract is placed at the explicitly defined coordinates, with the nominated 422 radius. The tracts are square (i.e., the radius is really a half-size). 425 def __init__(self, ident, patchInnerDimensions, patchBorder, ctrCoord, radius, tractOverlap, wcs):
429 super(ExplicitTractInfo, self).
__init__(ident, patchInnerDimensions, patchBorder, ctrCoord,
430 vertexList, tractOverlap, wcs)
437 def _minimumBoundingBox(self, wcs):
438 """Calculate the minimum bounding box for the tract, given the WCS, and 439 the nominated radius. 444 pixPos = wcs.skyToPixel(cornerCoord)
def getTractOverlap(self)
A floating-point coordinate rectangle geometry.
def _finalOrientation(self, bbox, wcs)
def _setupPatches(self, minBBox, wcs)
Reports arguments outside the domain of an operation.
A class representing an angle.
def makeSkyPolygonFromBBox(bbox, wcs)
def getInnerSkyPolygon(self)
def getPatchIndexPair(self, sequentialIndex)
def getPatchInfo(self, index)
def contains(self, coord)
def getPatchInnerDimensions(self)
def __getitem__(self, index)
def _minimumBoundingBox(self, wcs)
def getSequentialPatchIndex(self, patchInfo)
def findPatch(self, coord)
def findPatchList(self, coordList)
def __init__(self, ident, patchInnerDimensions, patchBorder, ctrCoord, radius, tractOverlap, wcs)
def __init__(self, id, patchInnerDimensions, patchBorder, ctrCoord, vertexCoordList, tractOverlap, wcs)
An integer coordinate rectangle.
def getOuterSkyPolygon(self)
Reports errors that are due to events beyond the control of the program.