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

75 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-07-15 02:30 -0700

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 Box, Circle, ConvexPolygon, Ellipse, RangeSet, Region, UnitVector3d 

26from .pixelization_abc import PixelizationABC 

27 

28 

29class HealpixPixelization(PixelizationABC): 

30 """HEALPix pixelization class. 

31 

32 Parameters 

33 ---------- 

34 level : `int` 

35 Pixelization level. HEALPix nside = 2**level 

36 """ 

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 

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

79 

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

81 return str(i) 

82 

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

84 import healpy as hp 

85 

86 if maxRanges > 0: 

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

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

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

90 

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

92 # region is completely covered at high resolution. 

93 neighbors = hp.get_all_neighbours(self._nside_highres, pixels_highres, nest=True) 

94 # Shift back to the original resolution and uniquify 

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

96 

97 return RangeSet(pixels) 

98 

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

100 import healpy as hp 

101 

102 if maxRanges > 0: 

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

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

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

106 

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

108 # the region 

109 

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

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

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

113 

114 corners_int = region.contains( 

115 corners[:, 0, :].ravel(), corners[:, 1, :].ravel(), corners[:, 2, :].ravel() 

116 ).reshape((len(pixels), 4)) 

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

118 pixels = pixels[interior] 

119 

120 return RangeSet(pixels) 

121 

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

123 """Get interior pixels from a region. 

124 

125 Parameters 

126 ---------- 

127 nside : `int` 

128 Healpix nside to retrieve interior pixels. 

129 region : `lsst.sphgeom.Region` 

130 Sphgeom region to find interior pixels. 

131 

132 Returns 

133 ------- 

134 pixels : `np.ndarray` 

135 Array of pixels at resolution nside, nest ordering. 

136 """ 

137 import healpy as hp 

138 

139 _circle = None 

140 _poly = None 

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

142 _circle = region.getBoundingCircle() 

143 elif isinstance(region, Circle): 

144 _circle = region 

145 elif isinstance(region, ConvexPolygon): 

146 _poly = region 

147 else: 

148 raise ValueError("Invalid region.") 

149 

150 # Find all pixels at an arbitrarily higher resolution 

151 if _circle is not None: 

152 center = _circle.getCenter() 

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

154 pixels = hp.query_disc( 

155 nside, vec, _circle.getOpeningAngle().asRadians(), inclusive=False, nest=True 

156 ) 

157 else: 

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

159 pixels = hp.query_polygon(nside, vertices, inclusive=False, nest=True) 

160 

161 return pixels 

162 

163 def __eq__(self, other): 

164 if isinstance(other, HealpixPixelization): 

165 return self._level == other._level 

166 

167 def __repr__(self): 

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

169 

170 def __reduce__(self): 

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