Coverage for python / lsst / afw / fits / _fitsContinued.py: 26%

54 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-05-14 00:45 -0700

1# This file is part of afw. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://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 <https://www.gnu.org/licenses/>. 

21 

22__all__ = ["Fits"] 

23 

24from collections.abc import Mapping 

25 

26from lsst.utils import continueClass 

27from ._fits import ( 

28 CompressionAlgorithm, 

29 CompressionOptions, 

30 DitherAlgorithm, 

31 Fits, 

32 QuantizationOptions, 

33 ScalingAlgorithm, 

34) 

35 

36 

37@continueClass 

38class Fits: # noqa: F811 

39 def __enter__(self): 

40 return self 

41 

42 def __exit__(self, cls, exc, traceback): 

43 self.closeFile() 

44 

45 

46@continueClass 

47class QuantizationOptions: # noqa: F811 

48 @classmethod 

49 def from_mapping(cls, mapping: Mapping[str, object]) -> QuantizationOptions: 

50 """Construct from a dictionary with keys matching this struct's fields. 

51 

52 Parameters 

53 ---------- 

54 mapping : `~collections.abc.Mapping` 

55 Mapping from string to value. Enumeration values may be passed as 

56 string value names. 

57 

58 Returns 

59 ------- 

60 options : `QuantizationOptions` 

61 An instance of this options class. 

62 

63 Notes 

64 ----- 

65 Allowed keys are: 

66 

67 - ``dither``: `str`, one of ``NO_DITHER``, ``SUBTRACTIVE_DITHER_1``, or 

68 ``SUBTRACTIVE_DITHER_2``. Defaults to ``NO_DITHER``. 

69 - ``scaling``: `str`, one of ``STDEV_CFITSIO``, ``STDEV_MASKED``, 

70 ``RANGE``, or ``MANUAL`` (see C++ docs for definitions). 

71 - ``mask_planes`` : `list` of `str`, names of mask planes to reject in 

72 ``STDEV_MASKED`` and ``RANGE`` (but only when writing objects with 

73 mask planes) 

74 - ``level``: `float`, target compression level (see C++ docs). 

75 - ``seed``: random number seed for dithering. Default is zero, which 

76 uses a timestamp-based seed when used directly, but a data ID-based 

77 seed when used via `lsst.obs.base.FitsExposureFormatter`. 

78 """ 

79 copy = dict(mapping) 

80 result = QuantizationOptions() 

81 if "dither" in copy: 

82 result.dither = DitherAlgorithm[copy.pop("dither")] 

83 if "scaling" in copy: 

84 result.scaling = ScalingAlgorithm[copy.pop("scaling")] 

85 if "mask_planes" in copy: 

86 result.mask_planes = copy.pop("mask_planes") 

87 if "level" in copy: 

88 result.level = copy.pop("level") 

89 if "seed" in copy: 

90 result.seed = copy.pop("seed") 

91 if copy: 

92 raise ValueError(f"Unrecognized quantization options: {list(copy.keys())}.") 

93 return result 

94 

95 def to_dict(self) -> dict[str, object]: 

96 """"Return the mapping representation of these options. 

97 

98 Returns 

99 ------- 

100 mapping : `dict` 

101 See `from_mapping`. 

102 """ 

103 return { 

104 "dither": self.dither.name, 

105 "scaling": self.scaling.name, 

106 "mask_planes": list(self.mask_planes), 

107 "level": self.level, 

108 "seed": self.seed, 

109 } 

110 

111 def __repr__(self): 

112 return ( 

113 f"{self.__class__.__name__}(dither={self.dither!r}, scaling={self.scaling!r}, " 

114 f"mask_planes={self.mask_planes!r}, level={self.level!r}, seed={self.seed!r})" 

115 ) 

116 

117 

118@continueClass 

119class CompressionOptions: # noqa: F811 

120 @classmethod 

121 def from_mapping(cls, mapping: Mapping[str, object]) -> CompressionOptions: 

122 """Construct from a dictionary with keys matching this struct's fields. 

123 

124 Parameters 

125 ---------- 

126 mapping : `~collections.abc.Mapping` 

127 Mapping from string to value. Enumeration values may be passed as 

128 string value names. Missing keys are mapped to default values. 

129 

130 Returns 

131 ------- 

132 options : `CompressionOptions` 

133 An instance of this options class. 

134 

135 Notes 

136 ----- 

137 Allowed keys are: 

138 

139 - ``algorithm``: `str`, one of ``GZIP_1``, ``GZIP_2``, or ``RICE_1``. 

140 - ``tile_width``: `int`, zero to use entire rows. 

141 - ``tile_height``: `int`, zero to use entire columns. 

142 - ``quantization``: `dict` or `None` (see 

143 `QuantizationOptions.from_mapping`). 

144 

145 Missing keys are replaced by defaults that reflect lossless compression 

146 (``GZIP_2``) with single rows as tiles. 

147 """ 

148 copy = dict(mapping) 

149 result = CompressionOptions() 

150 if "algorithm" in copy: 

151 result.algorithm = CompressionAlgorithm[copy.pop("algorithm")] 

152 if "tile_width" in copy: 

153 result.tile_width = copy.pop("tile_width") 

154 if "tile_height" in copy: 

155 result.tile_height = copy.pop("tile_height") 

156 if (quantization := copy.pop("quantization", None)) is not None: 

157 result.quantization = QuantizationOptions.from_mapping(quantization) 

158 if copy: 

159 raise ValueError(f"Unrecognized compression options: {list(copy.keys())}.") 

160 return result 

161 

162 def to_dict(self) -> dict[str, object]: 

163 """"Return the mapping representation of these options. 

164 

165 Returns 

166 ------- 

167 mapping : `dict` 

168 See `from_mapping`. 

169 """ 

170 return { 

171 "algorithm": self.algorithm.name, 

172 "tile_width": self.tile_width, 

173 "tile_height": self.tile_height, 

174 "quantization": self.quantization.to_dict() if self.quantization is not None else None, 

175 } 

176 

177 def __repr__(self): 

178 return ( 

179 f"{self.__class__.__name__}(algorithm={self.algorithm!r}, " 

180 f"tile_width={self.tile_width!r}, tile_height={self.tile_height!r}, " 

181 f"quantization={self.quantization!r})" 

182 )