Coverage for python/lsst/analysis/tools/actions/plot/diaSkyPlot.py: 29%
60 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-18 03:12 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-18 03:12 -0800
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/>.
22__all__ = ("DiaSkyPanel", "DiaSkyPlot")
24from typing import Mapping, cast
26import matplotlib.pyplot as plt
27from lsst.pex.config import ConfigDictField, Field
28from matplotlib.figure import Figure
30from ...interfaces import KeyedData, KeyedDataSchema, PlotAction, Vector
31from .plotUtils import PanelConfig
34class DiaSkyPanel(PanelConfig):
35 """Configuration options for DiaSkyPlot panels."""
37 xlabel = Field[str](
38 doc="Panel x-axis label.",
39 default="RA (deg)",
40 )
41 ylabel = Field[str](
42 doc="Panel y-axis label.",
43 default="Dec (deg)",
44 )
45 invertXAxis = Field[bool](
46 doc="Invert x-axis?",
47 default=True,
48 )
49 color = Field[str](
50 doc="Point color",
51 default="C0",
52 )
53 size = Field[float](
54 doc="Point size",
55 default=5,
56 )
57 alpha = Field[float](
58 doc="Point transparency",
59 default=0.5,
60 )
61 # Eventually we might retrieve data from more columns to make the plot
62 # prettier/more information rich
63 ra = Field[str](
64 doc="Name of RA column",
65 optional=False,
66 )
67 dec = Field[str](
68 doc="Name of Dec column",
69 optional=False,
70 )
73class DiaSkyPlot(PlotAction):
74 """Generic pseudo base class for plotting DiaSources
75 (or DiaObjects) on the sky.
76 """
78 panels = ConfigDictField(
79 doc="A configurable dict describing the panels to be plotted (both data columns and layouts).",
80 keytype=str,
81 itemtype=DiaSkyPanel,
82 default={},
83 )
85 def getInputSchema(self) -> KeyedDataSchema:
86 """Defines the schema this plot action expects (the keys it looks
87 for and what type they should be). In other words, verifies that
88 the input data has the columns we are expecting with the right dtypes.
89 """
90 for panel in self.panels.values():
91 yield (panel.ra, Vector)
92 yield (panel.dec, Vector)
94 def __call__(self, data: KeyedData, **kwargs) -> Mapping[str, Figure] | Figure:
95 return self.makePlot(data, **kwargs)
97 def makePlot(self, data: KeyedData, **kwargs) -> Figure:
98 """Make an N-panel plot with locations of DiaSources or
99 DiaObjects displayed in each panel.
101 Parameters
102 ----------
103 data : `lsst.analysis.tools.interfaces.KeyedData`
105 Returns
106 -------
107 fig : `matplotlib.figure.Figure`
108 """
109 if "figsize" in kwargs:
110 figsize = kwargs.pop("figsize", "")
111 fig = plt.figure(figsize=figsize, dpi=600)
112 else:
113 fig = plt.figure(figsize=(8, 6), dpi=600)
114 axs = self._makeAxes(fig)
115 for panel, ax in zip(self.panels.values(), axs):
116 self._makePanel(data, panel, ax, **kwargs)
117 plt.draw()
118 return fig
120 def _makeAxes(self, fig):
121 """Determine axes layout for main figure.
123 Use matplotlib's subplot2grid to determine the panel geometry,
124 which calls gridspec.
126 Parameters
127 ----------
128 fig : `matplotlib.figure.Figure`
130 Returns
131 -------
132 axs : `list` containing one or more matplotlib axes, one for each panel
133 """
134 axs = []
135 for count, panel in enumerate(self.panels.values()):
136 subplot2gridShape = (panel.subplot2gridShapeRow, panel.subplot2gridShapeColumn)
137 subplot2gridLoc = (panel.subplot2gridLocRow, panel.subplot2gridLocColumn)
138 axs.append(
139 plt.subplot2grid(
140 subplot2gridShape,
141 subplot2gridLoc,
142 rowspan=panel.subplot2gridRowspan,
143 colspan=panel.subplot2gridColspan,
144 )
145 )
146 return axs
148 def _makePanel(self, data, panel, ax, **kwargs):
149 """Plot a single panel.
151 Parameters
152 ----------
153 data : `lsst.analysis.tools.interfaces.KeyedData`
154 panel : `DiaSkyPanel`
155 ax : matplotlib axis
156 color : `str`
157 """
158 ras = cast(Vector, data[panel.ra])
159 decs = cast(Vector, data[panel.dec])
161 ax.scatter(ras, decs, c=panel.color, s=panel.size, alpha=panel.alpha, marker=".", linewidths=0)
162 ax.set_xlabel(panel.xlabel)
163 ax.set_ylabel(panel.ylabel)
165 if panel.invertXAxis:
166 ax.invert_xaxis()
167 if panel.topSpinesVisible:
168 ax.spines["top"].set_visible(True)
169 else:
170 ax.spines["top"].set_visible(False)
171 if not panel.bottomSpinesVisible: # default is True
172 ax.spines["bottom"].set_visible(False)
173 if not panel.leftSpinesVisible:
174 # Default is True; if False, also put ticks and labels on the right
175 ax.spines["left"].set_visible(False)
176 ax.yaxis.tick_right()
177 ax.yaxis.set_label_position("right")
178 if not panel.rightSpinesVisible: # default is True
179 ax.spines["right"].set_visible(False)