1 from __future__
import division
2 from builtins
import range
3 from builtins
import object
27 from .patchInfo
import PatchInfo
29 __all__ = [
"TractInfo"]
33 """Information about a tract in a SkyMap sky pixelization 35 The tract is subdivided into rectangular patches. Each patch has the following properties: 36 - An inner region defined by an inner bounding. The inner regions of the patches exactly tile the tract, 37 and all inner regions have the same dimensions. The tract is made larger as required to make this work. 38 - An outer region defined by an outer bounding box. The outer region extends beyond the inner region 39 by patchBorder pixels in all directions, except there is no border at the edges of the tract. 40 Thus patches overlap each other but never extend off the tract. If you do not want any overlap 41 between adjacent patches then set patchBorder to 0. 42 - An index that consists of a pair of integers: 43 0 <= x index < numPatches[0] 44 0 <= y index < numPatches[1] 45 Patch 0,0 is at the minimum corner of the tract bounding box. 48 def __init__(self, id, patchInnerDimensions, patchBorder, ctrCoord, vertexCoordList, tractOverlap, wcs):
49 """Construct a TractInfo 51 @param[in] id: tract ID 52 @param[in] patchInnerDimensions: dimensions of inner region of patches (x,y pixels) 53 @param[in] patchBorder: overlap between adjacent patches (in pixels, one int) 54 @param[in] ctrCoord: sky coordinate of center of inner region of tract, as an afwCoord.Coord; 55 also used as the CRVAL for the WCS. 56 @param[in] vertexCoordList: list of sky coordinates (afwCoord.Coord) 57 of vertices that define the boundaries of the inner region 58 @param[in] tractOverlap: minimum overlap between adjacent sky tracts; an afwGeom.Angle; 59 this defines the minimum distance the tract extends beyond the inner region in all directions 60 @param[in,out] wcs: an afwImage.Wcs; the reference pixel will be shifted as required 61 so that the lower left-hand pixel (index 0,0) has pixel position 0.0, 0.0 64 - It is not enforced that ctrCoord is the center of vertexCoordList, but SkyMap relies on it 65 - vertexCoordList will likely become a geom SphericalConvexPolygon someday. 69 assert len(patchInnerDimensions) == 2
72 raise TypeError(
"patchInnerDimensions=%s; must be two ints" % (patchInnerDimensions,))
82 def _minimumBoundingBox(self, wcs):
83 """Calculate the minimum bounding box for the tract, given the WCS 85 The bounding box is created in the frame of the supplied WCS, 86 so that it's OK if the coordinates are negative. 88 We compute the bounding box that holds all the vertices and the 91 minBBoxD = afwGeom.Box2D()
95 minBBoxD.include(wcs.skyToPixel(vertexCoord))
98 angleIncr = afwGeom.Angle(360.0, afwGeom.degrees) / float(numAngles)
99 for i
in range(numAngles):
100 offAngle = angleIncr * i
101 offCoord = vertexCoord.clone()
102 offCoord.offset(offAngle, halfOverlap)
103 pixPos = wcs.skyToPixel(offCoord)
104 minBBoxD.include(pixPos)
107 def _setupPatches(self, minBBox, wcs):
108 """Setup for patches of a particular size. 110 We grow the bounding box to hold an exact multiple of 111 the desired size (patchInnerDimensions), while keeping 112 the center roughly the same. We return the final 113 bounding box, and the number of patches in each dimension 116 @param minBBox Minimum bounding box for tract 117 @param wcs Wcs object 118 @return final bounding box, number of patches 120 bbox = afwGeom.Box2I(minBBox)
121 bboxMin = bbox.getMin()
122 bboxDim = bbox.getDimensions()
123 numPatches = afwGeom.Extent2I(0, 0)
125 num = (bboxDim[i] + innerDim - 1) // innerDim
126 deltaDim = (innerDim * num) - bboxDim[i]
128 bboxDim[i] = innerDim * num
129 bboxMin[i] -= deltaDim // 2
131 bbox = afwGeom.Box2I(bboxMin, bboxDim)
132 return bbox, numPatches
134 def _finalOrientation(self, bbox, wcs):
135 """Determine the final orientation 137 We offset everything so the lower-left corner is at 0,0 138 and compute the final Wcs. 140 @param bbox Current bounding box 141 @param wcs Current Wcs 142 @return revised bounding box, revised Wcs 144 finalBBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), bbox.getDimensions())
147 pixPosOffset = afwGeom.Extent2D(finalBBox.getMinX() - bbox.getMinX(),
148 finalBBox.getMinY() - bbox.getMinY())
149 wcs = wcs.copyAtShiftedPixelOrigin(pixPosOffset)
150 return finalBBox, wcs
153 """Find the patch containing the specified coord 155 @param[in] coord: sky coordinate (afwCoord.Coord) 156 @return PatchInfo of patch whose inner bbox contains the specified coord 158 @raise LookupError if coord is not in tract or we cannot determine the 159 pixel coordinate (which likely means the coord is off the tract). 161 @note This routine will be more efficient if coord is ICRS. 163 icrsCoord = coord.toIcrs()
165 pixel = self.
getWcs().skyToPixel(icrsCoord)
168 raise LookupError(
"Unable to determine pixel position for coordinate %s" % (coord,))
169 pixelInd = afwGeom.Point2I(pixel)
171 raise LookupError(
"coord %s is not in tract %s" % (coord, self.
getId()))
176 """Find patches containing the specified list of coords 178 @param[in] coordList: list of sky coordinates (afwCoord.Coord) 179 @return list of PatchInfo for patches that contain, or may contain, the specified region. 180 The list will be empty if there is no overlap. 183 * This may give incorrect answers on regions that are larger than a tract 184 * This uses a naive algorithm that may find some patches that do not overlap the region 185 (especially if the region is not a rectangle aligned along patch x,y). 187 box2D = afwGeom.Box2D()
188 for coord
in coordList:
189 icrsCoord = coord.toIcrs()
191 pixelPos = self.
getWcs().skyToPixel(icrsCoord)
195 box2D.include(pixelPos)
196 bbox = afwGeom.Box2I(box2D)
205 for xInd
in range(llPatchInd[0], urPatchInd[0]+1)
206 for yInd
in range(llPatchInd[1], urPatchInd[1]+1))
209 """Get bounding box of tract (as an afwGeom.Box2I) 211 return afwGeom.Box2I(self._bbox)
214 """Get sky coordinate of center of tract (as an afwCoord.Coord) 224 """Get the number of patches in x, y 226 @return the number of patches in x, y 233 @return patch border (pixels) 238 """Return information for the specified patch 240 @param[in] index: index of patch, as a pair of ints 241 @return patch info, an instance of PatchInfo 243 @raise IndexError if index is out of range 247 raise IndexError(
"Patch index %s is not in range [0-%d, 0-%d]" %
251 if not self._bbox.
contains(innerBBox):
253 "Bug: patch index %s valid but inner bbox=%s not contained in tract bbox=%s" %
254 (index, innerBBox, self._bbox))
255 outerBBox = afwGeom.Box2I(innerBBox)
257 outerBBox.clip(self._bbox)
265 """Get dimensions of inner region of the patches (all are the same) 267 @return dimensions of inner region of the patches (as an afwGeom Extent2I) 272 """Get minimum overlap of adjacent sky tracts 274 @return minimum overlap between adjacent sky tracts, as an afwGeom Angle 279 """Get list of sky coordinates of vertices that define the boundary of the inner region 281 @warning: this is not a deep copy 282 @warning vertexCoordList will likely become a geom SphericalConvexPolygon someday. 289 @warning: this is not a deep copy 294 return "TractInfo(id=%s)" % (self.
_id,)
297 return "TractInfo(id=%s, ctrCoord=%s)" % (self.
_id, self.
_ctrCoord.getVector())
301 for y
in range(yNum):
302 for x
in range(xNum):
313 """Does this tract contain the coordinate?""" 314 icrsCoord = coord.toIcrs()
316 pixels = self.
getWcs().skyToPixel(icrsCoord)
324 """Information for a tract specified explicitly 326 A tract is placed at the explicitly defined coordinates, with the nominated 327 radius. The tracts are square (i.e., the radius is really a half-size). 330 def __init__(self, ident, patchInnerDimensions, patchBorder, ctrCoord, radius, tractOverlap, wcs):
334 super(ExplicitTractInfo, self).
__init__(ident, patchInnerDimensions, patchBorder, ctrCoord,
335 vertexList, tractOverlap, wcs)
337 bboxD = afwGeom.BoxD(self.
getBBox())
342 def _minimumBoundingBox(self, wcs):
343 """The minimum bounding box is calculated using the nominated radius""" 344 bbox = afwGeom.Box2D()
348 pixPos = wcs.skyToPixel(coord)
def getTractOverlap(self)
def _finalOrientation(self, bbox, wcs)
def _setupPatches(self, minBBox, wcs)
def getPatchInfo(self, index)
def contains(self, coord)
def getPatchInnerDimensions(self)
def __getitem__(self, index)
def _minimumBoundingBox(self, wcs)
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)