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

74 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-15 00:23 +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 typing import TYPE_CHECKING, Any, Mapping 

27 

28import matplotlib.pyplot as plt 

29from lsst.skymap.tractInfo import ExplicitTractInfo 

30from lsst.utils.plotting import publication_plots, set_rubin_plotstyle 

31from matplotlib.figure import Figure 

32from matplotlib.lines import Line2D 

33 

34from ...interfaces import PlotAction, Vector 

35from .plotUtils import addPlotInfo 

36 

37if TYPE_CHECKING: 

38 from ...interfaces import KeyedData, KeyedDataSchema 

39 

40bands_dict = publication_plots.get_band_dicts() 

41 

42 

43class CoaddDepthPlot(PlotAction): 

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

45 

46 def setDefaults(self): 

47 super().setDefaults() 

48 

49 def getInputSchema(self) -> KeyedDataSchema: 

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

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

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

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

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

55 return base 

56 

57 def __call__( 

58 self, 

59 data: KeyedData, 

60 tractInfo: ExplicitTractInfo, 

61 **kwargs, 

62 ) -> Figure: 

63 self._validateInput(data, tractInfo) 

64 

65 if not isinstance(tractInfo, ExplicitTractInfo): 

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

67 

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

69 

70 def _validateInput( 

71 self, 

72 data: KeyedData, 

73 tractInfo: ExplicitTractInfo, 

74 ) -> None: 

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

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

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

78 

79 def makePlot( 

80 self, 

81 data: KeyedData, 

82 tractInfo: ExplicitTractInfo, 

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

84 **kwargs: Any, 

85 ) -> Figure: 

86 """Make the plot. 

87 

88 Parameters 

89 ---------- 

90 `KeyedData` 

91 The catalog to plot the points from. 

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

93 The tract info object. 

94 plotInfo : `dict` 

95 A dictionary of the plot information. 

96 

97 Returns 

98 ------- 

99 fig : `~matplotlib.figure.Figure` 

100 The resulting figure. 

101 

102 Examples 

103 -------- 

104 An example coaddDepthPlot may be seen below: 

105 

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

107 """ 

108 set_rubin_plotstyle() 

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

110 

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

112 num_patches_x, num_patches_y = tractInfo.getNumPatches() 

113 

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

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

116 

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

118 

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

120 m = 0 # subplot index 

121 while patch_counter >= 0: 

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

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

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

125 

126 if patch_counter in data["patch"]: 

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

128 for band in uniqueBands: 

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

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

131 band_mask = data["band"] == band 

132 

133 tot_mask = (patch_mask) & (band_mask) 

134 

135 ax.plot( 

136 data["depth"][tot_mask], 

137 data["pixels"][tot_mask], 

138 color=color, 

139 linewidth=0, 

140 ls=None, 

141 marker=markerstyle, 

142 ms=4, 

143 alpha=0.75, 

144 label=f"{band}", 

145 ) 

146 ax.grid(alpha=0.5) 

147 

148 # Chart formatting 

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

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

151 ax.set_yscale("log") 

152 ax.set_xlim(0, max_depth + 5) 

153 ax.set_ylim(5, max_pixels) 

154 # Can we somehow generalize ax.set_xticks? 

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

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

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

158 

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

160 if n != 0: 

161 ax.set_yticklabels([]) 

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

163 if patch_counter not in range(num_patches_x): 

164 ax.set_xticklabels([]) 

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

166 

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

168 patch_counter += 1 

169 m += 1 

170 patch_counter -= 2 * (n + 1) 

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

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

173 legend_elements = [ 

174 Line2D( 

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

176 ), 

177 Line2D( 

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

179 ), 

180 Line2D( 

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

182 ), 

183 Line2D( 

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

185 ), 

186 Line2D( 

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

188 ), 

189 Line2D( 

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

191 ), 

192 ] 

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

194 

195 if plotInfo is not None: 

196 fig = addPlotInfo(fig, plotInfo) 

197 

198 return fig