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-22 09:08 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 09:08 +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/>.
22from __future__ import annotations
24__all__ = ("CoaddDepthPlot",)
26from typing import TYPE_CHECKING, Any, Mapping
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
34from ...interfaces import PlotAction, Vector
35from .plotUtils import addPlotInfo
37if TYPE_CHECKING:
38 from ...interfaces import KeyedData, KeyedDataSchema
40bands_dict = publication_plots.get_band_dicts()
43class CoaddDepthPlot(PlotAction):
44 """Make a plot of pixels per coadd depth."""
46 def setDefaults(self):
47 super().setDefaults()
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
57 def __call__(
58 self,
59 data: KeyedData,
60 tractInfo: ExplicitTractInfo,
61 **kwargs,
62 ) -> Figure:
63 self._validateInput(data, tractInfo)
65 if not isinstance(tractInfo, ExplicitTractInfo):
66 raise TypeError(f"Input `tractInfo` type must be {ExplicitTractInfo} not {type(tractInfo)}.")
68 return self.makePlot(data, tractInfo, **kwargs)
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()}")
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.
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.
97 Returns
98 -------
99 fig : `~matplotlib.figure.Figure`
100 The resulting figure.
102 Examples
103 --------
104 An example coaddDepthPlot may be seen below:
106 .. image:: /_static/analysis_tools/coaddDepthPlotExample.png
107 """
108 set_rubin_plotstyle()
109 fig = plt.figure(dpi=300, figsize=(20, 20))
111 # Get number of vertical and horizontal patches in the tract.
112 num_patches_x, num_patches_y = tractInfo.getNumPatches()
114 max_depth = max(data["depth"])
115 max_pixels = max(data["pixels"])
117 plt.subplots_adjust(hspace=0, wspace=0)
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
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
133 tot_mask = (patch_mask) & (band_mask)
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)
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)
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)
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))
195 if plotInfo is not None:
196 fig = addPlotInfo(fig, plotInfo)
198 return fig