Coverage for python/lsst/sphgeom/_healpixPixelization.py: 23%

76 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-06 20:22 +0000

1# This file is part of sphgeom. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

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

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

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

12# (at your option) any later version. 

13# 

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

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

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21__all__ = ["HealpixPixelization"] 

22 

23import numpy as np 

24 

25from ._sphgeom import RangeSet, Region, UnitVector3d 

26from ._sphgeom import Box, Circle, ConvexPolygon, Ellipse 

27from .pixelization_abc import PixelizationABC 

28 

29 

30class HealpixPixelization(PixelizationABC): 

31 """HEALPix pixelization class. 

32 

33 Parameters 

34 ---------- 

35 level : `int` 

36 Pixelization level. HEALPix nside = 2**level 

37 """ 

38 MAX_LEVEL = 17 

39 

40 def __init__(self, level: int): 

41 import healpy as hp 

42 

43 if level < 0: 

44 raise ValueError("HealPix level must be >= 0.") 

45 

46 self._level = level 

47 self._nside = 2**level 

48 

49 self._npix = hp.nside2npix(self._nside) 

50 

51 # Values used to do pixel/region intersections 

52 self._bit_shift = 8 

53 self._nside_highres = self._nside*(2**(self._bit_shift//2)) 

54 

55 @property 

56 def nside(self): 

57 return self._nside 

58 

59 def getLevel(self): 

60 return self._level 

61 

62 level = property(getLevel) 

63 

64 def universe(self) -> RangeSet: 

65 return RangeSet(0, self._npix) 

66 

67 def pixel(self, i) -> Region: 

68 import healpy as hp 

69 

70 # This is arbitrarily returning 4 points on a side 

71 # to approximate the pixel shape. 

72 varr = hp.boundaries(self._nside, i, step=4, nest=True).T 

73 return ConvexPolygon([UnitVector3d(*c) for c in varr]) 

74 

75 def index(self, v: UnitVector3d) -> int: 

76 import healpy as hp 

77 return hp.vec2pix(self._nside, v.x(), v.y(), v.z(), nest=True) 

78 

79 def toString(self, i: int) -> str: 

80 return str(i) 

81 

82 def envelope(self, region: Region, maxRanges: int = 0): 

83 import healpy as hp 

84 

85 if maxRanges > 0: 

86 # If this is important, the rangeset can be consolidated. 

87 raise NotImplementedError("HealpixPixelization: maxRanges not implemented") 

88 pixels_highres = self._interior_pixels_from_region(self._nside_highres, region) 

89 

90 # Dilate the high resolution pixels by one to ensure that the full 

91 # region is completely covered at high resolution. 

92 neighbors = hp.get_all_neighbours(self._nside_highres, 

93 pixels_highres, 

94 nest=True) 

95 # Shift back to the original resolution and uniquify 

96 pixels = np.unique(np.right_shift(neighbors, self._bit_shift)) 

97 

98 return RangeSet(pixels) 

99 

100 def interior(self, region: Region, maxRanges: int = 0): 

101 import healpy as hp 

102 

103 if maxRanges > 0: 

104 # If this is important, the rangeset can be consolidated. 

105 raise NotImplementedError("HealpixPixelization: maxRanges not implemented") 

106 pixels = self._interior_pixels_from_region(self._nside, region) 

107 

108 # Check that the corners of the pixels are entirely enclosed in 

109 # the region 

110 

111 # Returns array [npixels, 3, ncorners], where ncorners is 4, and 

112 # the center index points to x, y, z. 

113 corners = hp.boundaries(self._nside, pixels, step=1, nest=True) 

114 

115 corners_int = region.contains(corners[:, 0, :].ravel(), 

116 corners[:, 1, :].ravel(), 

117 corners[:, 2, :].ravel()).reshape((len(pixels), 4)) 

118 interior = (np.sum(corners_int, axis=1) == 4) 

119 pixels = pixels[interior] 

120 

121 return RangeSet(pixels) 

122 

123 def _interior_pixels_from_region(self, nside: int, region: Region): 

124 """Get interior pixels from a region. 

125 

126 Parameters 

127 ---------- 

128 nside : `int` 

129 Healpix nside to retrieve interior pixels. 

130 region : `lsst.sphgeom.Region` 

131 Sphgeom region to find interior pixels. 

132 

133 Returns 

134 ------- 

135 pixels : `np.ndarray` 

136 Array of pixels at resolution nside, nest ordering. 

137 """ 

138 import healpy as hp 

139 

140 _circle = None 

141 _poly = None 

142 if isinstance(region, (Box, Ellipse)): 

143 _circle = region.getBoundingCircle() 

144 elif isinstance(region, Circle): 

145 _circle = region 

146 elif isinstance(region, ConvexPolygon): 

147 _poly = region 

148 else: 

149 raise ValueError("Invalid region.") 

150 

151 # Find all pixels at an arbitrarily higher resolution 

152 if _circle is not None: 

153 center = _circle.getCenter() 

154 vec = np.array([center.x(), center.y(), center.z()]).T 

155 pixels = hp.query_disc(nside, 

156 vec, 

157 _circle.getOpeningAngle().asRadians(), 

158 inclusive=False, 

159 nest=True) 

160 else: 

161 vertices = np.array([[v.x(), v.y(), v.z()] for v in _poly.getVertices()]) 

162 pixels = hp.query_polygon(nside, 

163 vertices, 

164 inclusive=False, 

165 nest=True) 

166 

167 return pixels 

168 

169 def __eq__(self, other): 

170 if isinstance(other, HealpixPixelization): 

171 return (self._level == other._level) 

172 

173 def __repr__(self): 

174 return f"HealpixPixelization({self._level})" 

175 

176 def __reduce__(self): 

177 return (self.__class__, (self._level, ))