23 __all__ = [
"RingsSkyMapConfig",
"RingsSkyMap"]
30 from .cachingSkyMap
import CachingSkyMap
31 from .tractInfo
import ExplicitTractInfo
35 """Configuration for the RingsSkyMap"""
36 numRings =
Field(dtype=int, doc=
"Number of rings", check=
lambda x: x > 0)
37 raStart =
Field(dtype=float, default=0.0, doc=
"Starting center RA for each ring (degrees)",
38 check=
lambda x: x >= 0.0
and x < 360.0)
42 """Rings sky map pixelization.
44 We divide the sphere into N rings of Declination, plus the two polar
45 caps, which sets the size of the individual tracts. The rings are
46 divided in RA into an integral number of tracts of this size; this
47 division is made at the Declination closest to zero so as to ensure
50 Rings are numbered in the rings from south to north. The south pole cap is
51 ``tract=0``, then the tract at ``raStart`` in the southernmost ring is
52 ``tract=1``. Numbering continues (in the positive RA direction) around that
53 ring and then continues in the same fashion with the next ring north, and
54 so on until all reaching the north pole cap, which is
55 ``tract=len(skymap) - 1``.
57 However, ``version=0`` had a bug in the numbering of the tracts: the first
58 and last tracts in the first (southernmost) ring were identical, and the
59 first tract in the last (northernmost) ring was missing. When using
60 ``version=0``, these tracts remain missing in order to preserve the
65 config : `lsst.skymap.RingsSkyMapConfig`
66 The configuration for this SkyMap.
67 version : `int`, optional
68 Software version of this class, to retain compatibility with old
69 verisons. ``version=0`` covers the period from first implementation
70 until DM-14809, at which point bugs were identified in the numbering
71 of tracts (affecting only tracts at RA=0). ``version=1`` uses the
72 post-DM-14809 tract numbering.
74 ConfigClass = RingsSkyMapConfig
78 assert version
in (0, 1),
"Unrecognised version: %s" % (version,)
81 self.
_ringSize_ringSize = math.pi / (config.numRings + 1)
83 for i
in range(config.numRings):
84 startDec = self.
_ringSize_ringSize*(i + 0.5) - 0.5*math.pi
85 stopDec = startDec + self.
_ringSize_ringSize
86 dec =
min(math.fabs(startDec), math.fabs(stopDec))
88 numTracts = sum(self.
_ringNums_ringNums) + 2
89 super(RingsSkyMap, self).
__init__(numTracts, config, version)
93 """Calculate ring indices given a numerical index of a tract.
95 The ring indices are the ring number and the tract number within
98 The ring number is -1 for the south polar cap and increases to the
99 north. The north polar cap has ring number = numRings. The tract
100 number is zero for either of the polar caps.
105 return self.
configconfig.numRings, 0
106 if index < 0
or index >= self.
_numTracts_numTracts:
107 raise IndexError(
"Tract index %d is out of range [0, %d]" % (index, len(self) - 1))
114 while ring < self.
configconfig.numRings
and tractNum > self.
_ringNums_ringNums[ring]:
115 tractNum -= self.
_ringNums_ringNums[ring]
118 while ring < self.
configconfig.numRings
and tractNum >= self.
_ringNums_ringNums[ring]:
119 tractNum -= self.
_ringNums_ringNums[ring]
121 return ring, tractNum
124 """Generate TractInfo for the specified tract index."""
127 ra, dec = 0, -0.5*math.pi
128 elif ringNum == self.
configconfig.numRings:
129 ra, dec = 0, 0.5*math.pi
131 dec = self.
_ringSize_ringSize*(ringNum + 1) - 0.5*math.pi
132 ra = ((2*math.pi*tractNum/self.
_ringNums_ringNums[ringNum])*geom.radians
138 0.5*self.
_ringSize_ringSize*geom.radians, self.
configconfig.tractOverlap*geom.degrees,
141 def _decToRingNum(self, dec):
142 """Calculate ring number from Declination.
146 dec : `lsst.geom.Angle`
152 Ring number: -1 for the south polar cap, and increasing to the
153 north, ending with ``numRings`` for the north polar cap.
155 firstRingStart = self.
_ringSize_ringSize*0.5 - 0.5*math.pi
156 if dec < firstRingStart:
159 elif dec > firstRingStart*-1:
161 return self.
configconfig.numRings
162 return int((dec.asRadians() - firstRingStart)/self.
_ringSize_ringSize)
164 def _raToTractNum(self, ra, ringNum):
165 """Calculate tract number from the Right Ascension.
169 ra : `lsst.geom.Angle`
172 Ring number (from ``_decToRingNum``).
177 Tract number within the ring (starts at 0 for the tract at raStart).
179 if ringNum
in (-1, self.
configconfig.numRings):
181 assert ringNum
in range(self.
configconfig.numRings)
182 tractNum = int((ra - self.
_raStart_raStart).
wrap().asRadians()
183 / (2*math.pi/self.
_ringNums_ringNums[ringNum]) + 0.5)
184 return 0
if tractNum == self.
_ringNums_ringNums[ringNum]
else tractNum
187 ringNum = self.
_decToRingNum_decToRingNum(coord.getLatitude())
191 if ringNum == self.
configconfig.numRings:
194 tractNum = self.
_raToTractNum_raToTractNum(coord.getLongitude(), ringNum)
201 index = sum(self.
_ringNums_ringNums[:ringNum], tractNum + 1)
205 """Find all tracts which include the specified coord.
209 coord : `lsst.geom.SpherePoint`
210 ICRS sky coordinate to search for.
214 tractList : `list` of `TractInfo`
215 The tracts which include the specified coord.
217 ringNum = self.
_decToRingNum_decToRingNum(coord.getLatitude())
222 for r
in [ringNum - 1, ringNum, ringNum + 1]:
223 if r < 0
or r >= self.
configconfig.numRings:
226 tractNum = self.
_raToTractNum_raToTractNum(coord.getLongitude(), r)
228 for t
in [tractNum - 1, tractNum, tractNum + 1]:
241 index = sum(self.
_ringNums_ringNums[:r + extra], t + 1)
243 if tract.contains(coord):
244 tractList.append(tract)
248 for entry
in [0, len(self)-1]:
250 if tract.contains(coord):
251 tractList.append(tract)
257 for coord
in coordList:
259 patchList = tractInfo.findPatchList(coordList)
260 if patchList
and not (tractInfo, patchList)
in retList:
261 retList.append((tractInfo, patchList))
265 """Add subclass-specific state or configuration options to the SHA1."""
266 sha1.update(struct.pack(
"<id", self.
configconfig.numRings, self.
configconfig.raStart))
Point in an unspecified spherical coordinate system.
def getRingIndices(self, index)
def _raToTractNum(self, ra, ringNum)
def _decToRingNum(self, dec)
def __init__(self, config, version=1)
def updateSha1(self, sha1)
def findTract(self, coord)
def findTractPatchList(self, coordList)
def generateTract(self, index)
def findAllTracts(self, coord)
daf::base::PropertyList * list
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
std::shared_ptr< afw::geom::SkyWcs > makeWcs(SipForwardTransform const &sipForward, SipReverseTransform const &sipReverse, geom::SpherePoint const &skyOrigin)
Create a new TAN SIP Wcs from a pair of SIP transforms and the sky origin.