23 __all__ = [
"RingsSkyMapConfig", 
"RingsSkyMap"]
 
   28 from lsst.pex.config 
import Field
 
   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 = math.pi / (config.numRings + 1)  
 
   83         for i 
in range(config.numRings):
 
   84             startDec = self.
_ringSize*(i + 0.5) - 0.5*math.pi
 
   86             dec = 
min(math.fabs(startDec), math.fabs(stopDec))  
 
   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.
config.numRings, 0
 
  107             raise IndexError(
"Tract index %d is out of range [0, %d]" % (index, len(self) - 1))
 
  118             while ring < self.
config.numRings 
and tractNum >= self.
_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.
config.numRings:  
 
  129             ra, dec = 0, 0.5*math.pi
 
  131             dec = self.
_ringSize*(ringNum + 1) - 0.5*math.pi
 
  132             ra = ((2*math.pi*tractNum/self.
_ringNums[ringNum])*geom.radians
 
  138                                  0.5*self.
_ringSize*geom.radians, self.
config.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*0.5 - 0.5*math.pi
 
  156         if dec < firstRingStart:
 
  159         elif dec > firstRingStart*-1:
 
  161             return self.
config.numRings
 
  162         return int((dec.asRadians() - firstRingStart)/self.
_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.
config.numRings):
 
  181         assert ringNum 
in range(self.
config.numRings)
 
  183                        / (2*math.pi/self.
_ringNums[ringNum]) + 0.5)
 
  184         return 0 
if tractNum == self.
_ringNums[ringNum] 
else tractNum  
 
  191         if ringNum == self.
config.numRings:
 
  196         if self.
_version == 0 
and tractNum == 0 
and ringNum != 0:
 
  201         index = sum(self.
_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. 
  222         for r 
in [ringNum - 1, ringNum, ringNum + 1]:
 
  223             if r < 0 
or r >= self.
config.numRings:
 
  228             for t 
in [tractNum - 1, tractNum, tractNum + 1]:
 
  236                 if self.
_version == 0 
and t == 0 
and r != 0:
 
  241                 index = sum(self.
_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.
config.numRings, self.
config.raStart))