Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

# 

# LSST Data Management System 

# Copyright 2008, 2009, 2010, 2012 LSST Corporation. 

# 

# This product includes software developed by the 

# LSST Project (http://www.lsst.org/). 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 3 of the License, or 

# (at your option) any later version. 

# 

# This program is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the LSST License Statement and 

# the GNU General Public License along with this program. If not, 

# see <http://www.lsstcorp.org/LegalNotices/>. 

# 

 

import struct 

import math 

 

from lsst.pex.config import Field 

import lsst.afw.geom as afwGeom 

from .cachingSkyMap import CachingSkyMap 

from .tractInfo import ExplicitTractInfo 

 

__all__ = ["RingsSkyMapConfig", "RingsSkyMap"] 

 

 

class RingsSkyMapConfig(CachingSkyMap.ConfigClass): 

"""Configuration for the RingsSkyMap""" 

numRings = Field(dtype=int, doc="Number of rings", check=lambda x: x > 0) 

raStart = Field(dtype=float, default=0.0, doc="Starting center RA for each ring (degrees)", 

check=lambda x: x >= 0.0 and x < 360.0) 

 

 

class RingsSkyMap(CachingSkyMap): 

"""Rings sky map pixelization. 

 

We divide the sphere into N rings of Declination, plus the two polar 

caps, which sets the size of the individual tracts. The rings are 

divided in RA into an integral number of tracts of this size; this 

division is made at the Declination closest to zero so as to ensure 

full overlap. 

""" 

ConfigClass = RingsSkyMapConfig 

_version = (1, 0) # for pickle 

 

def __init__(self, config, version=0): 

"""Constructor 

 

@param[in] config: an instance of self.ConfigClass; if None the default config is used 

@param[in] version: software version of this class, to retain compatibility with old instances 

""" 

# We count rings from south to north 

# Note: pole caps together count for one additional ring 

self._ringSize = math.pi / (config.numRings + 1) # Size of a ring in Declination (radians) 

self._ringNums = [] # Number of tracts for each ring 

for i in range(config.numRings): 

startDec = self._ringSize*(i + 0.5) - 0.5*math.pi 

stopDec = startDec + self._ringSize 

dec = min(math.fabs(startDec), math.fabs(stopDec)) # Declination for determining division in RA 

self._ringNums.append(int(2*math.pi*math.cos(dec)/self._ringSize) + 1) 

numTracts = sum(self._ringNums) + 2 

super(RingsSkyMap, self).__init__(numTracts, config, version) 

 

def getRingIndices(self, index): 

"""Calculate ring indices given a numerical index of a tract 

 

The ring indices are the ring number and the tract number within 

the ring. 

 

The ring number is -1 for the south polar cap and increases to the 

north. The north polar cap has ring number = numRings. The tract 

number is zero for either of the polar caps. 

""" 

if index == 0: # South polar cap 

return -1, 0 

if index == self._numTracts - 1: # North polar cap 

return self.config.numRings, 0 

index -= 1 

ring = 0 

while ring < self.config.numRings and index > self._ringNums[ring]: 

index -= self._ringNums[ring] 

ring += 1 

return ring, index 

 

def generateTract(self, index): 

"""Generate the TractInfo for this index""" 

ringNum, tractNum = self.getRingIndices(index) 

if ringNum == -1: # South polar cap 

ra, dec = 0, -0.5*math.pi 

elif ringNum == self.config.numRings: # North polar cap 

ra, dec = 0, 0.5*math.pi 

else: 

dec = self._ringSize*(ringNum + 1) - 0.5*math.pi 

ra = math.fmod(self.config.raStart + 2*math.pi*tractNum/self._ringNums[ringNum], 2*math.pi) 

 

center = afwGeom.SpherePoint(ra, dec, afwGeom.radians) 

wcs = self._wcsFactory.makeWcs(crPixPos=afwGeom.Point2D(0, 0), crValCoord=center) 

return ExplicitTractInfo(index, self.config.patchInnerDimensions, self.config.patchBorder, center, 

0.5*self._ringSize*afwGeom.radians, self.config.tractOverlap*afwGeom.degrees, 

wcs) 

 

def findTract(self, coord): 

"""Find the tract whose center is nearest the specified coord. 

 

@param[in] coord: sky coordinate (afwCoord.Coord) 

@return TractInfo of tract whose center is nearest the specified coord 

 

@warning: 

- if tracts do not cover the whole sky then the returned tract may not include the coord 

 

@note 

- This routine will be more efficient if coord is ICRS. 

- If coord is equidistant between multiple sky tract centers then one is arbitrarily chosen. 

- The default implementation is not very efficient; subclasses may wish to override. 

""" 

ra = coord.getLongitude().asRadians() 

dec = coord.getLatitude().asRadians() 

 

firstRingStart = self._ringSize*0.5 - 0.5*math.pi 

127 ↛ 129line 127 didn't jump to line 129, because the condition on line 127 was never true if dec < firstRingStart: 

# Southern cap 

return self[0] 

130 ↛ 132line 130 didn't jump to line 132, because the condition on line 130 was never true elif dec > firstRingStart*-1: 

# Northern cap 

return self[-1] 

 

ringNum = int((dec - firstRingStart)/self._ringSize) 

tractNum = int(math.fmod(ra - self.config.raStart, 2*math.pi) / 

(2*math.pi/self._ringNums[ringNum]) + 0.5) 

 

index = tractNum + 1 # Allow 1 for south pole 

139 ↛ 140line 139 didn't jump to line 140, because the loop on line 139 never started for i in range(ringNum): 

index += self._ringNums[i] 

 

return self[index] 

 

def findAllTracts(self, coord): 

"""Find all tracts which include the specified coord. 

 

@param[in] coord: sky coordinate (afwCoord.Coord) 

@return List of TractInfo of tracts which include the specified coord 

 

@note 

- This routine will be more efficient if coord is ICRS. 

""" 

ra = coord.getLongitude().asRadians() 

dec = coord.getLatitude().asRadians() 

 

firstRingStart = self._ringSize*0.5 - 0.5*math.pi 

 

ringNum = int((dec - firstRingStart)/self._ringSize) 

 

tractList = list() 

# ringNum denotes the closest ring to the specified coord 

# I will check adjacent rings which may include the specified coord 

for r in [ringNum - 1, ringNum, ringNum + 1]: 

if r < 0 or r > self.config.numRings - 1: 

continue 

tractNum = int(math.fmod(ra - self.config.raStart, 2*math.pi) / 

(2*math.pi/self._ringNums[r]) + 0.5) 

# Adjacent tracts will also be checked. 

for t in [tractNum - 1, tractNum, tractNum + 1]: 

# Wrap over raStart 

if t < 0: 

t = t + self._ringNums[r] 

elif t > self._ringNums[r] - 1: 

t = t - self._ringNums[r] 

 

index = t + 1 # Allow 1 for south pole 

for i in range(r): 

index += self._ringNums[i] 

 

tract = self[index] 

if tract.contains(coord): 

tractList.append(tract) 

 

# Always check tracts at poles 

# Southern cap is 0, Northern cap is the last entry in self 

for entry in [0, len(self)-1]: 

tract = self[entry] 

if tract.contains(coord): 

tractList.append(tract) 

 

return tractList 

 

def findTractPatchList(self, coordList): 

"""Find tracts and patches that overlap a region 

 

@param[in] coordList: list of sky coordinates (afwCoord.Coord) 

@return list of (TractInfo, list of PatchInfo) for tracts and patches that contain, 

or may contain, the specified region. The list will be empty if there is no overlap. 

 

@warning this uses a naive algorithm that may find some tracts and patches that do not overlap 

the region (especially if the region is not a rectangle aligned along patch x,y). 

""" 

retList = [] 

for coord in coordList: 

for tractInfo in self.findAllTracts(coord): 

patchList = tractInfo.findPatchList(coordList) 

207 ↛ 205line 207 didn't jump to line 205, because the condition on line 207 was never false if patchList and not (tractInfo, patchList) in retList: 

retList.append((tractInfo, patchList)) 

return retList 

 

def updateSha1(self, sha1): 

"""Add subclass-specific state or configuration options to the SHA1.""" 

sha1.update(struct.pack("<id", self.config.numRings, self.config.raStart))