lsst-utils 25.2023.2800__py3-none-any.whl → 29.2025.4800__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,159 @@
1
+ # This file is part of utils.
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
+ # Use of this source code is governed by a 3-clause BSD-style
10
+ # license that can be found in the LICENSE file.
11
+ """Utilities related to making matplotlib figures."""
12
+
13
+ from __future__ import annotations
14
+
15
+ __all__ = [
16
+ "get_multiband_plot_colors",
17
+ "get_multiband_plot_linestyles",
18
+ "get_multiband_plot_symbols",
19
+ "make_figure",
20
+ ]
21
+
22
+ from typing import TYPE_CHECKING, Any
23
+
24
+ if TYPE_CHECKING:
25
+ from matplotlib.figure import Figure
26
+
27
+
28
+ def make_figure(**kwargs: Any) -> Figure:
29
+ """Make a matplotlib Figure with an Agg-backend canvas.
30
+
31
+ This routine creates a matplotlib figure without using
32
+ ``matplotlib.pyplot``, and instead uses a fixed non-interactive
33
+ backend. The advantage is that these figures are not cached and
34
+ therefore do not need to be explicitly closed -- they
35
+ are completely self-contained and ephemeral unlike figures
36
+ created with `matplotlib.pyplot.figure()`.
37
+
38
+ Parameters
39
+ ----------
40
+ **kwargs : `dict`
41
+ Keyword arguments to be passed to `matplotlib.figure.Figure()`.
42
+
43
+ Returns
44
+ -------
45
+ figure : `matplotlib.figure.Figure`
46
+ Figure with a fixed Agg backend, and no caching.
47
+
48
+ Notes
49
+ -----
50
+ The code here is based on
51
+ https://matplotlib.org/stable/gallery/user_interfaces/canvasagg.html
52
+ """
53
+ try:
54
+ from matplotlib.backends.backend_agg import FigureCanvasAgg
55
+ from matplotlib.figure import Figure
56
+ except ImportError as e:
57
+ raise RuntimeError("Cannot use make_figure without matplotlib.") from e
58
+
59
+ fig = Figure(**kwargs)
60
+ FigureCanvasAgg(fig)
61
+
62
+ return fig
63
+
64
+
65
+ def get_multiband_plot_colors(dark_background: bool = False) -> dict:
66
+ """Get color mappings for multiband plots using SDSS filter names.
67
+
68
+ Notes
69
+ -----
70
+ From https://rtn-045.lsst.io/#colorblind-friendly-plots
71
+
72
+ Parameters
73
+ ----------
74
+ dark_background : `bool`, optional
75
+ Use colors intended for a dark background.
76
+ Default colors are intended for a light background.
77
+
78
+ Returns
79
+ -------
80
+ plot_colors : `dict` of `str`
81
+ Mapping of the LSST bands to colors.
82
+ """
83
+ plot_filter_colors_white_background = {
84
+ "u": "#1600EA",
85
+ "g": "#31DE1F",
86
+ "r": "#B52626",
87
+ "i": "#370201",
88
+ "z": "#BA52FF",
89
+ "y": "#61A2B3",
90
+ }
91
+ plot_filter_colors_black_background = {
92
+ "u": "#3eb7ff",
93
+ "g": "#30c39f",
94
+ "r": "#ff7e00",
95
+ "i": "#2af5ff",
96
+ "z": "#a7f9c1",
97
+ "y": "#fdc900",
98
+ }
99
+ if dark_background:
100
+ return plot_filter_colors_black_background
101
+ else:
102
+ return plot_filter_colors_white_background
103
+
104
+
105
+ def get_multiband_plot_symbols() -> dict:
106
+ """Get symbol mappings for multiband plots using SDSS filter names.
107
+
108
+ Notes
109
+ -----
110
+ From https://rtn-045.lsst.io/#colorblind-friendly-plots
111
+
112
+ Returns
113
+ -------
114
+ plot_symbols : `dict` of `str`
115
+ Mapping of the LSST bands to symbols.
116
+ """
117
+ plot_symbols = {
118
+ "u": "o",
119
+ "g": "^",
120
+ "r": "v",
121
+ "i": "s",
122
+ "z": "*",
123
+ "y": "p",
124
+ }
125
+ return plot_symbols
126
+
127
+
128
+ def get_multiband_plot_linestyles() -> dict:
129
+ """Get line style mappings for multiband plots using SDSS filter names.
130
+
131
+ Notes
132
+ -----
133
+ From https://rtn-045.lsst.io/#colorblind-friendly-plots
134
+
135
+ Returns
136
+ -------
137
+ plot_linestyles : `dict` of `str`
138
+ Mapping of the LSST bands to line styles.
139
+ """
140
+ plot_line_styles = {
141
+ "u": "--",
142
+ "g": (0, (3, 1, 1, 1)),
143
+ "r": "-.",
144
+ "i": "-",
145
+ "z": (0, (3, 1, 1, 1, 1, 1)),
146
+ "y": ":",
147
+ }
148
+
149
+ # [SP-2200]: Restored to using parametric values.
150
+ # To avoid matplotlib v3.10 bug (see DM-49724),
151
+ # manually iterate over `patches` object returned
152
+ # by `plt.hist` when using histtype='step':
153
+ # _, _, patches = plt.hist()
154
+ # linestyle = plot_line_styles[band]
155
+ # for patch in patches:
156
+ # patch.set_linestyle(linestyle)
157
+ # It seems the bug will be fixed in matplotlib v.3.10.2,
158
+ # see DM-49724[TODO]
159
+ return plot_line_styles
@@ -11,6 +11,8 @@
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
+ __all__ = ["calculate_safe_plotting_limits", "make_calculate_safe_plotting_limits"]
15
+
14
16
  from collections.abc import Callable, Iterable, Sequence
15
17
 
16
18
  import numpy as np
@@ -34,6 +36,15 @@ def calculate_safe_plotting_limits(
34
36
  data_series : `iterable` or `iterable` of `iterable`
35
37
  One or more data series which will be going on the same axis, and
36
38
  therefore want to have their common plotting limits calculated.
39
+ percentile : `float`, optional
40
+ The percentile used to clip the outliers from the data.
41
+ constant_extra : `float` or `None`, optional
42
+ The amount that's added on each side of the range so that data does not
43
+ quite touch the axes. If the default ``None`` is left then 5% of the
44
+ data range is added for cosmetics, but if zero is set this will
45
+ overrides this behaviour and zero you will get.
46
+ symmetric_around_zero : `bool`, optional
47
+ Whether to make the limits symmetric around zero.
37
48
 
38
49
  Returns
39
50
  -------
@@ -64,7 +75,7 @@ def make_calculate_safe_plotting_limits(
64
75
  data range is added for cosmetics, but if zero is set this will
65
76
  overrides this behaviour and zero you will get.
66
77
  symmetric_around_zero : `bool`, optional
67
- Make the limits symmetric around zero?
78
+ Whether to make the limits symmetric around zero.
68
79
 
69
80
  Returns
70
81
  -------
@@ -0,0 +1,184 @@
1
+ # This file is part of utils.
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
+ # Use of this source code is governed by a 3-clause BSD-style
10
+ # license that can be found in the LICENSE file.
11
+ """Utilities for making publication-quality figures."""
12
+
13
+ __all__ = [
14
+ "accent_color",
15
+ "divergent_cmap",
16
+ "galaxies_cmap",
17
+ "galaxies_color",
18
+ "get_band_dicts",
19
+ "mk_colormap",
20
+ "set_rubin_plotstyle",
21
+ "sso_cmap",
22
+ "sso_color",
23
+ "stars_cmap",
24
+ "stars_color",
25
+ ]
26
+
27
+ import numpy as np
28
+
29
+ from . import (
30
+ get_multiband_plot_colors,
31
+ get_multiband_plot_linestyles,
32
+ get_multiband_plot_symbols,
33
+ )
34
+
35
+
36
+ def set_rubin_plotstyle() -> None:
37
+ """
38
+ Set the matplotlib style for Rubin publications
39
+ """
40
+ from matplotlib import style
41
+
42
+ style.use("lsst.utils.plotting.rubin")
43
+
44
+
45
+ def mk_colormap(colorNames): # type: ignore
46
+ """Make a colormap from the list of color names.
47
+
48
+ Parameters
49
+ ----------
50
+ colorNames : `list`
51
+ A list of strings that correspond to matplotlib named colors.
52
+
53
+ Returns
54
+ -------
55
+ cmap : `matplotlib.colors.LinearSegmentedColormap`
56
+ A colormap stepping through the supplied list of names.
57
+ """
58
+ from matplotlib import colors
59
+
60
+ blues = []
61
+ greens = []
62
+ reds = []
63
+ alphas = []
64
+
65
+ if len(colorNames) == 1:
66
+ # Alpha is between 0 and 1 really but
67
+ # using 1.5 saturates out the top of the
68
+ # colorscale, this looks good for ComCam data
69
+ # but might want to be changed in the future.
70
+ alphaRange = [0.2, 1.0]
71
+ nums = np.linspace(0, 1, len(alphaRange))
72
+ r, g, b = colors.colorConverter.to_rgb(colorNames[0])
73
+ for num, alpha in zip(nums, alphaRange):
74
+ blues.append((num, b, b))
75
+ greens.append((num, g, g))
76
+ reds.append((num, r, r))
77
+ alphas.append((num, alpha, alpha))
78
+
79
+ else:
80
+ nums = np.linspace(0, 1, len(colorNames))
81
+ if len(colorNames) == 3:
82
+ alphaRange = [1.0, 1.0, 1.0]
83
+ elif len(colorNames) == 5:
84
+ alphaRange = [1.0, 0.7, 0.3, 0.7, 1.0]
85
+ else:
86
+ alphaRange = np.ones(len(colorNames))
87
+
88
+ for num, color, alpha in zip(nums, colorNames, alphaRange):
89
+ r, g, b = colors.colorConverter.to_rgb(color)
90
+ blues.append((num, b, b))
91
+ greens.append((num, g, g))
92
+ reds.append((num, r, r))
93
+ alphas.append((num, alpha, alpha))
94
+
95
+ colorDict = {"blue": blues, "red": reds, "green": greens, "alpha": alphas}
96
+ cmap = colors.LinearSegmentedColormap("newCmap", colorDict)
97
+ return cmap
98
+
99
+
100
+ def divergent_cmap(): # type: ignore
101
+ """
102
+ Make a divergent color map.
103
+ """
104
+ cmap = mk_colormap([stars_color(), "#D9DCDE", accent_color()])
105
+
106
+ return cmap
107
+
108
+
109
+ def stars_cmap(single_color=False): # type: ignore
110
+ """Make a color map for stars."""
111
+ import seaborn as sns
112
+ from matplotlib.colors import ListedColormap
113
+
114
+ if single_color:
115
+ cmap = mk_colormap([stars_color()])
116
+ else:
117
+ cmap = ListedColormap(sns.color_palette("mako", 256))
118
+ return cmap
119
+
120
+
121
+ def stars_color() -> str:
122
+ """Return the star color string for lines"""
123
+ return "#084d96"
124
+
125
+
126
+ def accent_color() -> str:
127
+ """Return a contrasting color for overplotting,
128
+ black is the best for this but if you need two colors
129
+ this works well on blue.
130
+ """
131
+ return "#DE8F05"
132
+
133
+
134
+ def galaxies_cmap(single_color=False): # type: ignore
135
+ """Make a color map for galaxies."""
136
+ if single_color:
137
+ cmap = mk_colormap([galaxies_color()])
138
+ else:
139
+ cmap = "inferno"
140
+ return cmap
141
+
142
+
143
+ def galaxies_color() -> str:
144
+ """Return the galaxy color string for lines"""
145
+ return "#961A45"
146
+
147
+
148
+ def sso_color() -> str:
149
+ """Return the SSO color string for lines"""
150
+ return "#01694c"
151
+
152
+
153
+ def sso_cmap(single_color=False): # type: ignore
154
+ """Make a color map for solar system objects."""
155
+ if single_color:
156
+ cmap = mk_colormap([sso_color()])
157
+ else:
158
+ cmap = "viridis"
159
+ return cmap
160
+
161
+
162
+ def get_band_dicts() -> dict:
163
+ """
164
+ Define palettes, from RTN-045. This includes dicts for colors (bandpass
165
+ colors for white background), colors_black (bandpass colors for
166
+ black background), plot symbols, and line_styles, keyed on band (ugrizy).
167
+
168
+ Returns
169
+ -------
170
+ band_dict : `dict` of `dict`
171
+ Dicts of colors, colors_black, symbols, and line_styles,
172
+ keyed on bands 'u', 'g', 'r', 'i', 'z', and 'y'.
173
+ """
174
+ colors = get_multiband_plot_colors()
175
+ colors_black = get_multiband_plot_colors(dark_background=True)
176
+ symbols = get_multiband_plot_symbols()
177
+ line_styles = get_multiband_plot_linestyles()
178
+
179
+ return {
180
+ "colors": colors,
181
+ "colors_black": colors_black,
182
+ "symbols": symbols,
183
+ "line_styles": line_styles,
184
+ }
@@ -0,0 +1,46 @@
1
+ axes.labelweight: normal
2
+ figure.titleweight : normal
3
+ axes.labelsize : large
4
+ axes.linewidth: 1
5
+ axes.titleweight: normal
6
+ axes.titlesize : small
7
+ figure.titlesize: small
8
+ errorbar.capsize: 3.0
9
+
10
+ lines.linewidth : 2
11
+ lines.markersize : 10
12
+
13
+ xtick.labelsize : small
14
+ ytick.labelsize : small
15
+
16
+ figure.dpi : 300.0
17
+ figure.facecolor: White
18
+ figure.figsize : 6.4, 4.8
19
+
20
+ # From seaborn-v0_8-colorblind colormap: https://seaborn.pydata.org/tutorial/color_palettes.html
21
+ axes.prop_cycle: cycler('color', ['0173B2', 'DE8F05', '029E73', 'D55E00', 'CC78BC', 'CA9161', 'FBAFE4', '949494', 'ECE133', '56B4E9'])
22
+ patch.facecolor: 006BA4
23
+
24
+ font.size : 14
25
+ legend.fontsize: x-small
26
+
27
+ xtick.major.width: 1.0
28
+ xtick.minor.width: 0.5
29
+ xtick.major.size: 7
30
+ xtick.minor.size: 4
31
+ xtick.minor.visible: True
32
+ xtick.direction: in
33
+ xtick.top: True
34
+ xtick.bottom: True
35
+ ytick.major.width: 1.0
36
+ ytick.minor.width: 0.5
37
+ ytick.major.size: 7
38
+ ytick.minor.size: 4
39
+ ytick.minor.visible: True
40
+ ytick.direction: in
41
+ ytick.left: True
42
+ ytick.right: True
43
+ xtick.major.pad : 6
44
+ xtick.minor.pad : 6
45
+ ytick.major.pad : 6
46
+ ytick.minor.pad : 6