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

Shortcuts 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

71 statements  

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 

24import healpy as hp 

25 

26from ._sphgeom import RangeSet, Region, UnitVector3d 

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

28from .pixelization_abc import PixelizationABC 

29 

30 

31class HealpixPixelization(PixelizationABC): 

32 """HEALPix pixelization class. 

33 

34 Parameters 

35 ---------- 

36 level : `int` 

37 Pixelization level. HEALPix nside = 2**level 

38 """ 

39 MAX_LEVEL = 17 

40 

41 def __init__(self, level: int): 

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 # This is arbitrarily returning 4 points on a side 

69 # to approximate the pixel shape. 

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

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

72 

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

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

75 

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

77 return str(i) 

78 

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

80 if maxRanges > 0: 

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

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

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

84 

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

86 # region is completely covered at high resolution. 

87 neighbors = hp.get_all_neighbours(self._nside_highres, 

88 pixels_highres, 

89 nest=True) 

90 # Shift back to the original resolution and uniquify 

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

92 

93 return RangeSet(pixels) 

94 

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

96 if maxRanges > 0: 

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

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

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

100 

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

102 # the region 

103 

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

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

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

107 

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

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

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

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

112 pixels = pixels[interior] 

113 

114 return RangeSet(pixels) 

115 

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

117 """Get interior pixels from a region. 

118 

119 Parameters 

120 ---------- 

121 nside : `int` 

122 Healpix nside to retrieve interior pixels. 

123 region : `lsst.sphgeom.Region` 

124 Sphgeom region to find interior pixels. 

125 

126 Returns 

127 ------- 

128 pixels : `np.ndarray` 

129 Array of pixels at resolution nside, nest ordering. 

130 """ 

131 _circle = None 

132 _poly = None 

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

134 _circle = region.getBoundingCircle() 

135 elif isinstance(region, Circle): 

136 _circle = region 

137 elif isinstance(region, ConvexPolygon): 

138 _poly = region 

139 else: 

140 raise ValueError("Invalid region.") 

141 

142 # Find all pixels at an arbitrarily higher resolution 

143 if _circle is not None: 

144 center = _circle.getCenter() 

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

146 pixels = hp.query_disc(nside, 

147 vec, 

148 _circle.getOpeningAngle().asRadians(), 

149 inclusive=False, 

150 nest=True) 

151 else: 

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

153 pixels = hp.query_polygon(nside, 

154 vertices, 

155 inclusive=False, 

156 nest=True) 

157 

158 return pixels 

159 

160 def __eq__(self, other): 

161 if isinstance(other, HealpixPixelization): 

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

163 

164 def __repr__(self): 

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

166 

167 def __reduce__(self): 

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