Coverage for python / lsst / analysis / tools / actions / plot / coaddDepthPlot.py: 19%
75 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 08:55 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 08:55 +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 collections.abc import Mapping
27from typing import TYPE_CHECKING, Any
29import matplotlib.pyplot as plt
30from matplotlib.figure import Figure
31from matplotlib.lines import Line2D
33from lsst.skymap.tractInfo import ExplicitTractInfo
34from lsst.utils.plotting import publication_plots, set_rubin_plotstyle
36from ...interfaces import PlotAction, Vector
37from .plotUtils import addPlotInfo
39if TYPE_CHECKING:
40 from ...interfaces import KeyedData, KeyedDataSchema
42bands_dict = publication_plots.get_band_dicts()
45class CoaddDepthPlot(PlotAction):
46 """Make a plot of pixels per coadd depth."""
48 def setDefaults(self):
49 super().setDefaults()
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
59 def __call__(
60 self,
61 data: KeyedData,
62 tractInfo: ExplicitTractInfo,
63 **kwargs,
64 ) -> Figure:
65 self._validateInput(data, tractInfo)
67 if not isinstance(tractInfo, ExplicitTractInfo):
68 raise TypeError(f"Input `tractInfo` type must be {ExplicitTractInfo} not {type(tractInfo)}.")
70 return self.makePlot(data, tractInfo, **kwargs)
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()}")
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.
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.
99 Returns
100 -------
101 fig : `~matplotlib.figure.Figure`
102 The resulting figure.
104 Examples
105 --------
106 An example coaddDepthPlot may be seen below:
108 .. image:: /_static/analysis_tools/coaddDepthPlotExample.png
109 """
110 set_rubin_plotstyle()
111 fig = plt.figure(dpi=300, figsize=(20, 20))
113 # Get number of vertical and horizontal patches in the tract.
114 num_patches_x, num_patches_y = tractInfo.getNumPatches()
116 max_depth = max(data["depth"])
117 max_pixels = max(data["pixels"])
119 plt.subplots_adjust(hspace=0, wspace=0)
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
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
135 tot_mask = (patch_mask) & (band_mask)
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)
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)
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)
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))
197 if plotInfo is not None:
198 fig = addPlotInfo(fig, plotInfo)
200 return fig