Repository navigation
Expand file tree
/
Copy pathsingle_plot_FAST_spectrograms.py
More file actions
178 lines (156 loc) · 6.07 KB
/
Copy pathsingle_plot_FAST_spectrograms.py
File metadata and controls
178 lines (156 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""CLI to render a single FAST ESA spectrogram figure (one CDF file, or one orbit).
Companion to ``batch_multi_plot_FAST_spectrograms.py``: where that script
(via ``configurable_spectrograms.fast.batch_directory``) processes every
orbit in a directory in parallel, this script renders exactly one figure
and exits. All rendering logic lives in
``configurable_spectrograms.fast.plotting``; this script only parses
arguments and calls it -- the same function the GUI's Single Plot page
calls.
"""
__authors__: list[str] = ["Ev Hansen"]
__contact__: str = "ephansen+gh@terpmail.umd.edu"
__credits__: list[list[str]] = [
["Ev Hansen", "Python code"],
["Emma Mirizio", "Co-Mentor"],
["Marilia Samara", "Co-Mentor"],
]
__date__: str = "2026-07-30"
__status__: str = "Development"
__version__: str = "0.0.1"
__license__: str = "GPL-3.0"
import argparse
import sys
from configurable_spectrograms.cdf_utils import load_filtered_orbits
from configurable_spectrograms.fast.orbit_discovery import discover_orbit_files, extract_orbit_and_instrument
from configurable_spectrograms.fast.plotting import FAST_plot_instrument_grid, FAST_plot_pitch_angle_grid
from configurable_spectrograms.styling import configure_matplotlib, quiet_cdflib_noise
def render_single_pitch_angle_grid(
cdf_file_path: str,
output_path: str,
y_scale: str = "linear",
z_scale: str = "linear",
colormap: str = "viridis",
cusp_marker_style: str = "both",
) -> bool:
"""Render one CDF file's pitch-angle grid and save it to *output_path*.
The orbit number (used to look up the cusp boundary) is parsed
automatically from the filename.
Parameters
----------
cdf_file_path : str
Path to the instrument CDF file.
output_path : str
Destination PNG path.
y_scale, z_scale : {'linear', 'log'}, default 'linear'
Axis scaling.
colormap : str, default 'viridis'
Matplotlib colormap name.
cusp_marker_style : {'line', 'bracket', 'both'}, default 'both'
Cusp-boundary marker style.
Returns
-------
bool
``True`` if a figure was produced and saved, ``False`` otherwise.
"""
filtered_orbits_df = load_filtered_orbits()
parsed = extract_orbit_and_instrument(cdf_file_path)
orbit_number = parsed[0] if parsed is not None else None
fig, _canvas = FAST_plot_pitch_angle_grid(
cdf_file_path,
filtered_orbits_df=filtered_orbits_df,
orbit_number=orbit_number,
scale_function_y=y_scale,
scale_function_z=z_scale,
show=False,
colormap=colormap,
cusp_marker_style=cusp_marker_style,
)
if fig is None:
return False
fig.savefig(output_path, dpi=200)
return True
def render_single_instrument_grid(
data_folder: str,
orbit_number: int,
output_path: str,
y_scale: str = "linear",
z_scale: str = "linear",
colormap: str = "viridis",
cusp_marker_style: str = "both",
) -> bool:
"""Render one orbit's multi-instrument grid resolved from a data folder.
Parameters
----------
data_folder : str
Root folder to search for the orbit's instrument CDF files.
orbit_number : int
Orbit number to resolve within *data_folder*.
output_path : str
Destination PNG path.
y_scale, z_scale : {'linear', 'log'}, default 'linear'
Axis scaling.
colormap : str, default 'viridis'
Matplotlib colormap name.
cusp_marker_style : {'line', 'bracket', 'both'}, default 'both'
Cusp-boundary marker style.
Returns
-------
bool
``True`` if a figure was produced and saved, ``False`` otherwise.
"""
filtered_orbits_df = load_filtered_orbits()
instrument_files = discover_orbit_files(data_folder).get(orbit_number, {})
if not instrument_files:
return False
fig, _canvas = FAST_plot_instrument_grid(
instrument_files,
filtered_orbits_df=filtered_orbits_df,
orbit_number=orbit_number,
scale_function_y=y_scale,
scale_function_z=z_scale,
show=False,
colormap=colormap,
cusp_marker_style=cusp_marker_style,
)
if fig is None:
return False
fig.savefig(output_path, dpi=200)
return True
def main() -> int:
"""Parse CLI arguments and render a single FAST ESA spectrogram figure."""
quiet_cdflib_noise()
configure_matplotlib()
parser = argparse.ArgumentParser(description="Render a single FAST ESA spectrogram figure.")
mode_group = parser.add_mutually_exclusive_group(required=True)
mode_group.add_argument("--cdf-file", help="Single CDF file to render as a pitch-angle grid.")
mode_group.add_argument("--data-folder", help="Data folder to search for one orbit's instrument grid.")
parser.add_argument("--orbit", type=int, help="Orbit number (required with --data-folder).")
parser.add_argument("--output", required=True, help="Destination PNG file path.")
parser.add_argument("--y-scale", choices=("linear", "log"), default="linear")
parser.add_argument("--z-scale", choices=("linear", "log"), default="linear")
parser.add_argument("--colormap", default="viridis")
parser.add_argument("--cusp-style", choices=("line", "bracket", "both"), default="both")
args = parser.parse_args()
if args.data_folder is not None and args.orbit is None:
parser.error("--orbit is required when using --data-folder")
if args.cdf_file is not None:
produced = render_single_pitch_angle_grid(
args.cdf_file, args.output, args.y_scale, args.z_scale, args.colormap, args.cusp_style
)
else:
produced = render_single_instrument_grid(
args.data_folder, args.orbit, args.output, args.y_scale, args.z_scale, args.colormap, args.cusp_style
)
if not produced:
print("[WARNING] No data available to plot for the given input.")
return 1
print(f"[SAVED] {args.output}")
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
print("\n[INTERRUPT] Aborted by user.")
sys.exit(130)