Coverage for python / lsst / analysis / tools / actions / plot / coaddDepthPlot.py: 19%

75 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-17 09:36 +0000

1# This file is part of analysis_tools. 

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 

22from __future__ import annotations 

23 

24__all__ = ("CoaddDepthPlot",) 

25 

26from collections.abc import Mapping 

27from typing import TYPE_CHECKING, Any 

28 

29import matplotlib.pyplot as plt 

30from matplotlib.figure import Figure 

31from matplotlib.lines import Line2D 

32 

33from lsst.skymap.tractInfo import ExplicitTractInfo 

34from lsst.utils.plotting import publication_plots, set_rubin_plotstyle 

35 

36from ...interfaces import PlotAction, Vector 

37from .plotUtils import addPlotInfo 

38 

39if TYPE_CHECKING: 

40 from ...interfaces import KeyedData, KeyedDataSchema 

41 

42bands_dict = publication_plots.get_band_dicts() 

43 

44 

45class CoaddDepthPlot(PlotAction): 

46 """Make a plot of pixels per coadd depth.""" 

47 

48 def setDefaults(self): 

49 super().setDefaults() 

50 

51 def getInputSchema(self) -> KeyedDataSchema: 

52 base: list[tuple[str, type[Vector]]] = [] 

53 base.append(("patch", Vector)) 

54 base.append(("band", Vector)) 

55 base.append(("depth", Vector)) 

56 base.append(("pixels", Vector)) 

57 return base 

58 

59 def __call__( 

60 self, 

61 data: KeyedData, 

62 tractInfo: ExplicitTractInfo, 

63 **kwargs, 

64 ) -> Figure: 

65 self._validateInput(data, tractInfo) 

66 

67 if not isinstance(tractInfo, ExplicitTractInfo): 

68 raise TypeError(f"Input `tractInfo` type must be {ExplicitTractInfo} not {type(tractInfo)}.") 

69 

70 return self.makePlot(data, tractInfo, **kwargs) 

71 

72 def _validateInput( 

73 self, 

74 data: KeyedData, 

75 tractInfo: ExplicitTractInfo, 

76 ) -> None: 

77 needed = set(k[0] for k in self.getInputSchema()) 

78 if not needed.issubset(data.keys()): 

79 raise ValueError(f"Input data does not contain all required keys: {self.getInputSchema()}") 

80 

81 def makePlot( 

82 self, 

83 data: KeyedData, 

84 tractInfo: ExplicitTractInfo, 

85 plotInfo: Mapping[str, str] | None = None, 

86 **kwargs: Any, 

87 ) -> Figure: 

88 """Make the plot. 

89 

90 Parameters 

91 ---------- 

92 `KeyedData` 

93 The catalog to plot the points from. 

94 tractInfo : `~lsst.skymap.tractInfo.ExplicitTractInfo` 

95 The tract info object. 

96 plotInfo : `dict` 

97 A dictionary of the plot information. 

98 

99 Returns 

100 ------- 

101 fig : `~matplotlib.figure.Figure` 

102 The resulting figure. 

103 

104 Examples 

105 -------- 

106 An example coaddDepthPlot may be seen below: 

107 

108 .. image:: /_static/analysis_tools/coaddDepthPlotExample.png 

109 """ 

110 set_rubin_plotstyle() 

111 fig = plt.figure(dpi=300, figsize=(20, 20)) 

112 

113 # Get number of vertical and horizontal patches in the tract. 

114 num_patches_x, num_patches_y = tractInfo.getNumPatches() 

115 

116 max_depth = max(data["depth"]) 

117 max_pixels = max(data["pixels"]) 

118 

119 plt.subplots_adjust(hspace=0, wspace=0) 

120 

121 patch_counter = (num_patches_x * num_patches_y) - num_patches_x # The top left corner of a tract 

122 m = 0 # subplot index 

123 while patch_counter >= 0: 

124 for n in range(num_patches_x): # column index 

125 ax = plt.subplot(num_patches_x, num_patches_y, m + 1) 

126 patch_mask = data["patch"] == patch_counter 

127 

128 if patch_counter in data["patch"]: 

129 uniqueBands = set(data["band"][patch_mask]) 

130 for band in uniqueBands: 

131 color = bands_dict["colors"][band] 

132 markerstyle = bands_dict["symbols"][band] 

133 band_mask = data["band"] == band 

134 

135 tot_mask = (patch_mask) & (band_mask) 

136 

137 ax.plot( 

138 data["depth"][tot_mask], 

139 data["pixels"][tot_mask], 

140 color=color, 

141 linewidth=0, 

142 ls=None, 

143 marker=markerstyle, 

144 ms=4, 

145 alpha=0.75, 

146 label=f"{band}", 

147 ) 

148 ax.grid(alpha=0.5) 

149 

150 # Chart formatting 

151 # Need a solution for ax.set_xscale when max_depth is high, 

152 # but not all patches/bands have quality coverage. 

153 ax.set_yscale("log") 

154 ax.set_xlim(0, max_depth + 5) 

155 ax.set_ylim(5, max_pixels) 

156 # Can we somehow generalize ax.set_xticks? 

157 # ax.set_xticks(np.arange(0, max_depth, 20)) 

158 ax.tick_params(axis="both", which="minor") 

159 ax.tick_params(axis="both", which="both", top=False, right=False) 

160 

161 # Only label axes of the farmost left and bottom row of plots 

162 if n != 0: 

163 ax.set_yticklabels([]) 

164 ax.tick_params(axis="y", which="both", length=0) 

165 if patch_counter not in range(num_patches_x): 

166 ax.set_xticklabels([]) 

167 ax.tick_params(axis="x", which="both", length=0) 

168 

169 ax.set_title(f"patch {patch_counter}", y=0.85) 

170 patch_counter += 1 

171 m += 1 

172 patch_counter -= 2 * (n + 1) 

173 fig.supxlabel("Number of input visits (n_image depth)", y=0.075) 

174 fig.supylabel("Count (pixels)", x=0.075) 

175 legend_elements = [ 

176 Line2D( 

177 [0], [0], color=bands_dict["colors"]["u"], lw=0, marker=bands_dict["symbols"]["u"], label="u" 

178 ), 

179 Line2D( 

180 [0], [0], color=bands_dict["colors"]["g"], lw=0, marker=bands_dict["symbols"]["g"], label="g" 

181 ), 

182 Line2D( 

183 [0], [0], color=bands_dict["colors"]["r"], lw=0, marker=bands_dict["symbols"]["r"], label="r" 

184 ), 

185 Line2D( 

186 [0], [0], color=bands_dict["colors"]["i"], lw=0, marker=bands_dict["symbols"]["i"], label="i" 

187 ), 

188 Line2D( 

189 [0], [0], color=bands_dict["colors"]["z"], lw=0, marker=bands_dict["symbols"]["z"], label="z" 

190 ), 

191 Line2D( 

192 [0], [0], color=bands_dict["colors"]["y"], lw=0, marker=bands_dict["symbols"]["y"], label="y" 

193 ), 

194 ] 

195 plt.legend(handles=legend_elements, loc="upper left", bbox_to_anchor=(1.05, 10)) 

196 

197 if plotInfo is not None: 

198 fig = addPlotInfo(fig, plotInfo) 

199 

200 return fig