• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

spedas / pyspedas / 25194239086

26 Apr 2026 08:07PM UTC coverage: 61.697% (-28.8%) from 90.54%
25194239086

push

github

jameswilburlewis
Added test for loading Cluster CODIF differential energy flux

0 of 7 new or added lines in 1 file covered. (0.0%)

19460 existing lines in 418 files now uncovered.

30204 of 48955 relevant lines covered (61.7%)

1.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.35
/pyspedas/tplot_tools/MPLPlotter/tplot.py
1
import copy
3✔
2
import logging
3✔
3
import numpy as np
3✔
4
import matplotlib as mpl
3✔
5
from datetime import date, datetime, timezone
3✔
6
from matplotlib import pyplot as plt
3✔
7
from mpl_toolkits.axes_grid1 import make_axes_locatable
3✔
8
import pyspedas
3✔
9
from fnmatch import filter as tname_filter
3✔
10
from time import sleep
3✔
11
from pyspedas.tplot_tools import tplot_wildcard_expand, tname_byindex, get_data, var_label_panel
3✔
12
from pyspedas.tplot_tools import lineplot, count_traces, makegap
3✔
13
from pyspedas.tplot_tools import specplot, specplot_make_1d_ybins, reduce_spec_dataset
3✔
14
from pyspedas.tplot_tools import get_var_label_ticks
3✔
15
from .save_plot import save_plot
3✔
16

17
# the following improves the x-axis ticks labels
18
import matplotlib.units as munits
3✔
19
import matplotlib.dates as mdates
3✔
20
converter = mdates.ConciseDateConverter()
3✔
21
munits.registry[np.datetime64] = converter
3✔
22
munits.registry[date] = converter
3✔
23
munits.registry[datetime] = converter
3✔
24

25
def pseudovar_component_props(varname: str):
3✔
26
    """ Return or calculate the plot properties for a single normal tplot variable
27

28

29
    Parameters
30
    ----------
31
    varname: str
32
        Name of the tplot variable to inspect
33

34
    Returns
35
    -------
36
    dict
37
        Dictionary of plot properties for this variable::
38
            is_spec: True if variable is a spectrogram
39
            ymin, ymax, ylog: Y axis limits
40
            zmin, zmax, zlog: Z axis limits (if variable is a spectrogram)
41

42
    """
43

UNCOV
44
    output_dict = {'ymin':np.nan, 'ymax':np.nan, 'ylog':False,
1✔
45
                   'zmin':np.nan, 'zmax': np.nan, 'zlog':False,
46
                   'is_spec': False}
47

UNCOV
48
    attrs = pyspedas.tplot_tools.data_quants[varname].attrs
1✔
UNCOV
49
    plot_opts = attrs.get('plot_options')
1✔
UNCOV
50
    if plot_opts is not None:
1✔
UNCOV
51
        yaxis_opt = plot_opts.get('yaxis_opt')
1✔
UNCOV
52
        if yaxis_opt is not None:
1✔
UNCOV
53
            ylog = yaxis_opt['y_axis_type']
1✔
UNCOV
54
            yrange = yaxis_opt.get('y_range')
1✔
55
    else:
56
        ylog = False
×
57
        yrange=[None, None]
×
58

UNCOV
59
    plot_extras = attrs['plot_options']['extras']
1✔
UNCOV
60
    dat = get_data(varname)
1✔
61

UNCOV
62
    if ylog is None or ylog is False or ylog == '' or ylog.lower() == 'linear':
1✔
UNCOV
63
        output_dict['ylog'] = False
1✔
UNCOV
64
        ylog = False
1✔
65
    else:
UNCOV
66
        output_dict['ylog'] = True
×
UNCOV
67
        ylog = True
×
68

69
    # ylog is now guaranteed to be boolean
70

UNCOV
71
    if plot_extras.get('spec') is not None:
1✔
UNCOV
72
        spec = plot_extras['spec']
×
UNCOV
73
        if spec:
×
74
            # Inspect a specplot variable
UNCOV
75
            output_dict['is_spec']=True
×
UNCOV
76
            if yrange is not None:
×
UNCOV
77
                output_dict['ymin'] = yrange[0]
×
UNCOV
78
                output_dict['ymax'] = yrange[1]
×
79
            else:
80
                # Figure out which attribute to use for bin centers (copied from specplot, maybe
81
                # should be pulled out into a separate routine?)
82
                var_data=dat
×
83
                if len(var_data) == 3:
×
84
                    if hasattr(var_data, 'v'):
×
85
                        input_bin_centers = var_data.v
×
86
                    elif hasattr(var_data, 'v1'):
×
87
                        input_bin_centers = var_data.v1
×
88
                    else:
89
                        logging.warning("Multidimensional variable %s has no v or v1 attribute", varname)
×
90
                elif len(var_data) == 4:
×
91
                    if hasattr(var_data, 'v1'):
×
92
                        if 'spec_dim_to_plot' in plot_extras:
×
93
                            if plot_extras['spec_dim_to_plot'] == 'v1':
×
94
                                input_bin_centers = var_data.v1
×
95
                    if hasattr(var_data, 'v2'):
×
96
                        if 'spec_dim_to_plot' in plot_extras:
×
97
                            if plot_extras['spec_dim_to_plot'] == 'v2':
×
98
                                input_bin_centers = var_data.v2
×
99

100
                output_bin_boundaries = specplot_make_1d_ybins(None, input_bin_centers, ylog, no_regrid=True)
×
101
                output_dict['ymin']=np.nanmin(output_bin_boundaries)
×
102
                output_dict['ymax']=np.nanmax(output_bin_boundaries)
×
103

UNCOV
104
            zaxis_options = attrs['plot_options']['zaxis_opt']
×
UNCOV
105
            zrange = zaxis_options.get('zrange')
×
UNCOV
106
            if zrange is not None:
×
107
                output_dict['zmin'] = zrange[0]
×
108
                output_dict['zmax'] = zrange[1]
×
109
            else:
110
                # Determine Z range from data array
UNCOV
111
                output_dict['zmin'] = np.nanmin(dat.y)
×
UNCOV
112
                output_dict['zmax'] = np.nanmax(dat.y)
×
UNCOV
113
            zlog = zaxis_options.get('z_axis_type')
×
UNCOV
114
            if zlog is None or zlog.lower() == 'linear':
×
115
                output_dict['zlog'] = False
×
116
            else:
UNCOV
117
                output_dict['zlog'] = True
×
118

119

120
    else:
121
        # Inspect a line plot variable
UNCOV
122
        output_dict['is_spec']=False
1✔
UNCOV
123
        if yrange is not None:
1✔
UNCOV
124
            output_dict['ymin'] = yrange[0]
1✔
UNCOV
125
            output_dict['ymax'] = yrange[1]
1✔
126
        else:
127
            output_dict['ymin'] = np.nanmin(dat.y)
×
128
            output_dict['ymax'] = np.nanmax(dat.y)
×
UNCOV
129
    return output_dict
1✔
130

131
def gather_pseudovar_props(pseudovars:list[str]):
3✔
132
    """ Inspect the components of a pseudovariable to determine plot limits and other settings
133

134
    If any component has y or z ranges/scales set, they will be used, otherwise limits will be determined from
135
    the data values.
136

137
    If any component specifies log scaling, it will be applied to all components, otherwise linear scaling will
138
    be used for all
139

140
    Parameters
141
    ----------
142
    pseudovars: list[str]
143
        List of tplot composite variable components to inspect
144

145
    Returns
146
    -------
147
    dict
148
        A dictionary containing the plot properties to apply to each component variable::
149
            line_ymin, line_ymax, line_ylog : Y axis limits for line plots
150
            spec_ymin, spec_ymax, spec_ylog : Y axis limits for spectrograms
151
            spec_zmin, spec_zmax, spec_zlog : Z axis limits for spectrograms
152
            has_line_plots: True if any component is a line plot
153
            has_spec_plots: True if any component is a spectrogram plot
154

155
    """
UNCOV
156
    output_dict = { 'has_line_plots': False,
1✔
157
                    'has_spec_plots': False,
158
                    'line_ymin': np.nan,
159
                    'line_ymax': np.nan,
160
                    'line_ylog': False,
161
                    'spec_ymin': np.nan,
162
                    'spec_ymax': np.nan,
163
                    'spec_ylog': False,
164
                    'spec_zmin': np.nan,
165
                    'spec_zmax': np.nan,
166
                    'spec_zlog': False,
167
                    'first_spec_var': None}
168

169

UNCOV
170
    for var in pseudovars:
1✔
UNCOV
171
        props = pseudovar_component_props(var)
1✔
172
        #print(f"{var}: is_spec: {props['is_spec']} ymin: {props['ymin']} ymax: {props['ymax']} ylog: {props['ylog']} zmin: {props['zmin']} zmax: {props['zmax']} zlog: {props['zlog']}")
UNCOV
173
        if props['is_spec']:
1✔
UNCOV
174
            output_dict['has_spec_plots'] = True
×
UNCOV
175
            if output_dict['first_spec_var'] is None:
×
UNCOV
176
                output_dict['first_spec_var'] = var
×
UNCOV
177
            if props['zlog']:
×
UNCOV
178
                output_dict['spec_zlog'] = True
×
UNCOV
179
            if props['ylog']:
×
UNCOV
180
                output_dict['spec_ylog'] = True
×
UNCOV
181
            output_dict['spec_zmin']= np.nanmin([output_dict['spec_zmin'], props['zmin']])
×
UNCOV
182
            output_dict['spec_zmax']= np.nanmax([output_dict['spec_zmax'], props['zmax']])
×
UNCOV
183
            output_dict['spec_ymin']= np.nanmin([output_dict['spec_ymin'], props['ymin']])
×
UNCOV
184
            output_dict['spec_ymax']= np.nanmax([output_dict['spec_ymax'], props['ymax']])
×
185
        else:
UNCOV
186
            output_dict['has_line_plots'] = True
1✔
UNCOV
187
            if props['ylog']:
1✔
UNCOV
188
                output_dict['line_ylog'] = True
×
UNCOV
189
            output_dict['line_ymin'] = np.nanmin([output_dict['line_ymin'], props['ymin']])
1✔
UNCOV
190
            output_dict['line_ymax'] = np.nanmax([output_dict['line_ymax'], props['ymax']])
1✔
UNCOV
191
    return output_dict
1✔
192

193

194
def tplot(variables,
3✔
195
          trange=None,
196
          var_label=None,
197
          xsize=None,
198
          ysize=None,
199
          save_png='',
200
          save_eps='',
201
          save_svg='',
202
          save_pdf='',
203
          save_jpeg='',
204
          dpi=None,
205
          display=True,
206
          fig=None,
207
          axis=None,
208
          running_trace_count=None,
209
          pseudo_idx=None,
210
          pseudo_right_axis=False,
211
          pseudo_xaxis_options=None,
212
          pseudo_yaxis_options=None,
213
          pseudo_zaxis_options=None,
214
          pseudo_line_options=None,
215
          pseudo_extra_options=None,
216
          show_colorbar=True,
217
          slice=False,
218
          return_plot_objects=False):
219
    """
220
    Plot tplot variables to the display, or as saved files, using Matplotlib
221

222
    Parameters
223
    ----------
224
        variables: str or list of str, required
225
            List of tplot variables to be plotted.  Space-delimited strings may be used instead f
226
            lists.  Wildcards will be expanded.
227
        trange: list of string or float, optional
228
            If set, this time range will be used, temporarily overriding any previous xlim or timespan calls
229
        var_label : str or list of str, optional
230
            A list of variables to be displayed as values underneath the X axis major tick marks
231
        xsize: float, optional
232
            Plot size in the horizontal direction (in inches)
233
        ysize: float, optional
234
            Plot size in the vertical direction (in inches)
235
        dpi: float, optional
236
            The resolution of the plot in dots per inch
237
        save_png : str, optional
238
            A full file name and path.
239
            If this option is set, the plot will be automatically saved to the file name provided in PNG format.
240
        save_eps : str, optional
241
            A full file name and path.
242
            If this option is set, the plot will be automatically saved to the file name provided in EPS format.
243
        save_jpeg : str, optional
244
            A full file name and path.
245
            If this option is set, the plot will be automatically saved to the file name provided in JPEG format.
246
        save_pdf : str, optional
247
            A full file name and path.
248
            If this option is set, the plot will be automatically saved to the file name provided in PDF format.
249
        save_svg : str, optional
250
            A full file name and path.
251
            If this option is set, the plot will be automatically saved to the file name provided in SVG format.
252
        dpi: float, optional
253
            The resolution of the plot in dots per inch
254
        display: bool, optional
255
            If True, then this function will display the plotted tplot variables. Use False to suppress display (for example, if
256
            saving to a file, or returning plot objects to be displayed later). Default: True
257
        fig: Matplotlib figure object
258
            Use an existing figure to plot in (mainly for recursive calls to render composite variables)
259
        axis: Matplotlib axes object
260
            Use an existing set of axes to plot on (mainly for recursive calls to render composite variables)
261
        running_trace_count: int, optional
262
            In recursive calls for rendering composite variables, the index of the trace currently being rendered.
263
        pseudo_idx: int, optional
264
            In recursive calls for rendering composite variables, the index of the variable component currently being rendered.
265
        pseudo_right_axis: bool, optional
266
            In recursive calls for rendering composite variables, a flag to indicate the Y scale should be placed on
267
            the right Y axis.
268
        pseudo_xaxis_options: dict, optional
269
            In recursice calls for rendering composite variables, X axis options inherited from the parent variable
270
        pseudo_yaxis_options: dict, optional
271
            In recursive calls for rendering composite variables, Y axis options inherited from the parent variable
272
        pseudo_zaxis_options: dict, optional
273
            In recursive calls for rendering composite variables, Z axis options inherited from the parent variable
274
        pseudo_line_options: dict, optional
275
            In recursive calls for rendering composite variables, line options inherited from the parent variable
276
        pseudo_extra_options: dict, optional
277
            In recursive calls for rendering composite variables, extra options inherited from the parent variable
278
        show_colorbar: bool, optional
279
            Show a colorbar showing the Z scale for spectrogram plots.
280
        slice: bool, optional
281
            If True, show an interactive window with a plot of Z versus Y values for the X axis (time) value under the cursor. Default: False
282
        return_plot_objects: bool, optional
283
            If true, returns the matplotlib fig and axes objects for further manipulation. Default: False
284

285
    Returns
286
    -------
287
        Any
288
            Returns matplotlib fig and axes objects, if return_plot_objects==True
289

290
    Examples
291
    --------
292
        >>> # Plot a single line plot
293
        >>> import pyspedas
294
        >>> x_data = [2,3,4,5,6]
295
        >>> y_data = [1,2,3,4,5]
296
        >>> pyspedas.store_data("Variable1", data={'x':x_data, 'y':y_data})
297
        >>> pyspedas.tplot("Variable1")
298

299
        >>> # Plot two variables
300
        >>> x_data = [1,2,3,4,5]
301
        >>> y_data = [[1,5],[2,4],[3,3],[4,2],[5,1]]
302
        >>> pyspedas.store_data("Variable2", data={'x':x_data, 'y':y_data})
303
        >>> pyspedas.tplot(["Variable1", "Variable2"])
304

305
        >>> # Plot two plots, adding a third variable's values as annotations at X axis major tick marks
306
        >>> x_data = [1,2,3]
307
        >>> y_data = [ [1,2,3] , [4,5,6], [7,8,9] ]
308
        >>> v_data = [1,2,3]
309
        >>> pyspedas.store_data("Variable3", data={'x':x_data, 'y':y_data, 'v':v_data})
310
        >>> pyspedas.options("Variable3", 'spec', 1)
311
        >>> pyspedas.tplot(["Variable2", "Variable3"], var_label='Variable1')
312

313
    """
314
    # This call resolves wildcard patterns and converts integers to variable names
315
    variables = tplot_wildcard_expand(variables)
3✔
316
    if len(variables) == 0:
3✔
317
        logging.warning("tplot: No matching tplot names were found")
×
318
        return
×
319

320

321
    varlabel_style = pyspedas.tplot_tools.tplot_opt_glob.get('varlabel_style')
3✔
322
    if varlabel_style is None or varlabel_style.lower() == 'extra_axes':
3✔
323
        num_panels = len(variables)
3✔
324
        panel_sizes = [1]*num_panels
3✔
325
    else: # varlabel_style 'extra_panel'
UNCOV
326
        num_panels = len(variables) + 1
×
UNCOV
327
        panel_sizes = [1] * len(variables) + [0.1 * (len(var_label) + 2)]
×
328

329
    # support for the panel_size option
330
    for var_idx, variable in enumerate(variables):
3✔
331
        if pyspedas.tplot_tools.data_quants.get(variable) is None:
3✔
332
            continue
×
333
        panel_size = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['extras'].get('panel_size')
3✔
334
        if panel_size is not None:
3✔
335
            panel_sizes[var_idx] = panel_size
3✔
336

337
    if xsize is None:
3✔
338
        xsize = pyspedas.tplot_tools.tplot_opt_glob.get('xsize')
3✔
339
        if xsize is None:
3✔
340
            xsize = 12
3✔
341

342
    if ysize is None:
3✔
343
        ysize = pyspedas.tplot_tools.tplot_opt_glob.get('ysize')
3✔
344
        if ysize is None:
3✔
345
            if num_panels > 4:
3✔
346
                ysize = 8
3✔
347
            else:
348
                # This was previously set to 5, which resulted in a non-monotonic progression of panel heights
349
                default_y_sizes = [5, 5, 6, 7, 8]
3✔
350
                ysize = default_y_sizes[num_panels]
3✔
351

352
    # The logic here for handling the 'right_axis' option is pretty convoluted, and makes a number of assumptions
353
    # that may not be warranted: mainly that right_axis will only be set on pseudovariables, and that if
354
    # set, the first sub-variable gets the left axis and all other sub-variables get a newly twinx-ed right axis.
355
    # "right axis" implies there will only be at most two Y axes. What if more scales are needed?
356
    # There also seems to be some conflation of the set of axes for the whole stack of plots, versus
357
    # the single (left or possibly right) axis for the variable currently being rendered.
358
    #
359
    # This whole concept is kind of a mess at the moment.  For now, we'll make it work for the
360
    # most likely use case, plotting a single spectrum variable followed by a single line variable
361
    # (for example, an energy spectrum plus spacecraft potential).  JWL 2024-03-26
362

363
    if fig is None and axis is None:
3✔
364
        fig, axes = plt.subplots(nrows=num_panels, sharex=True, gridspec_kw={'height_ratios': panel_sizes}, layout='constrained')
3✔
365
        fig.set_size_inches(xsize, ysize)
3✔
366
        plot_title = pyspedas.tplot_tools.tplot_opt_glob['title_text']
3✔
367
        fig.suptitle(plot_title)
3✔
368
        if (plot_title is not None) and plot_title != '':
3✔
UNCOV
369
            if 'title_size' in pyspedas.tplot_tools.tplot_opt_glob:
2✔
UNCOV
370
                title_size = pyspedas.tplot_tools.tplot_opt_glob['title_size']
2✔
UNCOV
371
                fig.suptitle(plot_title, fontsize=title_size)
2✔
372
            else:
373
                fig.suptitle(plot_title)
×
374
        # support for matplotlib styles
375
        style = pyspedas.tplot_tools.tplot_opt_glob.get('style')
3✔
376
        if style is not None:
3✔
377
            plt.style.use(style)
×
378
    else:
379
        # fig and axis have been passed as parameters, most likely a recursive tplot call to render
380
        # a pseudovariable
UNCOV
381
        if pseudo_idx == 0 or pseudo_right_axis == False:
1✔
382
            # setting up first axis
UNCOV
383
            axes = axis
1✔
UNCOV
384
        elif pseudo_idx > 0 and pseudo_right_axis:
×
385
            # generate and use the right axis?  probably still wrong...
UNCOV
386
            axes = axis.twinx()
×
387
        # support for matplotlib styles -- shouldn't be needed if processing pseudovar components?
UNCOV
388
        style = pyspedas.tplot_tools.tplot_opt_glob.get('style')
1✔
UNCOV
389
        if style is not None:
1✔
390
            plt.style.use(style)
×
391

392

393
    axis_font_size = pyspedas.tplot_tools.tplot_opt_glob.get('axis_font_size')
3✔
394

395
    colorbars = {}
3✔
396

397
    for idx, variable in enumerate(variables):
3✔
398
        var_data_org = get_data(variable, dt=True)
3✔
399
        var_metadata = get_data(variable, metadata=True)
3✔
400

401
        #Check for a 3d variable, call reduce_spec_dataset
402
        if hasattr(var_data_org, 'v1') and hasattr(var_data_org, 'v2'):
3✔
UNCOV
403
            temp_dq = reduce_spec_dataset(name=variable)
×
UNCOV
404
            var_data_org = get_data(variable, dt=True, data_quant_in=temp_dq)
×
405
        
406
        if var_data_org is None:
3✔
407
            logging.info('Variable not found: ' + variable)
×
408
            continue
×
409

410
        var_data = copy.deepcopy(var_data_org)
3✔
411

412
        # plt.subplots returns a list of axes for multiple panels
413
        # but only a single axis for a single panel
414
        if num_panels == 1:
3✔
415
            this_axis = axes
3✔
416
        else:
417
            this_axis = axes[idx]
3✔
418

419
        # we need to track the variable name in the axis object
420
        # for spectrogram slices
421
        this_axis.var_name = variable
3✔
422

423
        pseudo_var = False
3✔
424
        overplots = None
3✔
425
        spec = False
3✔
426

427
        var_quants = pyspedas.tplot_tools.data_quants[variable]
3✔
428

429
        if not isinstance(var_quants, dict):
3✔
430
            overplots = var_quants.attrs['plot_options'].get('overplots_mpl')
3✔
431
            if overplots is not None and len(overplots) > 0:
3✔
UNCOV
432
                pseudo_var = True
1✔
433

434
        # deal with pseudo-variables first
435
        if isinstance(var_data, list) or isinstance(var_data, str) or pseudo_var:
3✔
436
            # this is a pseudo variable
UNCOV
437
            if isinstance(var_data, str):
1✔
438
                var_data = var_data.split(' ')
×
439

UNCOV
440
            if pseudo_var:
1✔
UNCOV
441
                pseudo_vars = overplots
1✔
442
            else:
443
                pseudo_vars = var_data
×
444

445
            # pseudo variable metadata should override the metadata
446
            # for individual variables
UNCOV
447
            xaxis_options = None
1✔
UNCOV
448
            yaxis_options = None
1✔
UNCOV
449
            zaxis_options = None
1✔
UNCOV
450
            line_opts = None
1✔
UNCOV
451
            plot_extras = None
1✔
UNCOV
452
            if pseudo_var:
1✔
UNCOV
453
                plot_extras = var_quants.attrs['plot_options']['extras']
1✔
UNCOV
454
                if plot_extras.get('spec') is not None:
1✔
455
                    spec = True
×
456

UNCOV
457
                if plot_extras.get('right_axis') is not None:
1✔
UNCOV
458
                    if plot_extras.get('right_axis'):
×
UNCOV
459
                        pseudo_right_axis = True
×
460

UNCOV
461
                if pseudo_right_axis or spec:
1✔
UNCOV
462
                    plot_extras = None
×
463
                else:
UNCOV
464
                    yaxis_options = var_quants.attrs['plot_options']['yaxis_opt']
1✔
UNCOV
465
                    zaxis_options = var_quants.attrs['plot_options']['zaxis_opt']
1✔
UNCOV
466
                    line_opts = var_quants.attrs['plot_options']['line_opt']
1✔
467

UNCOV
468
            traces_processed = 0
1✔
UNCOV
469
            pseudovar_props = gather_pseudovar_props(pseudo_vars)
1✔
470
            #
471
            # Determine which Y axis options need to be changed to accommodate multiple component variables
472
            # There might be line variables and spectra, each with their own overall yscale and yrange.
473
            # If both are present, the spectra will take priority.
474
            # If explicit y or z scales or ranges are set on the pseudovariable, do not override.
UNCOV
475
            if pseudovar_props['has_spec_plots']:
1✔
UNCOV
476
                if pseudovar_props['spec_ylog']:
×
UNCOV
477
                    new_yscale = 'log'
×
478
                else:
479
                    new_yscale = 'linear'
×
UNCOV
480
                new_yr = [pseudovar_props['spec_ymin'], pseudovar_props['spec_ymax']]
×
481
            else:
UNCOV
482
                if pseudovar_props['line_ylog']:
1✔
UNCOV
483
                    new_yscale = 'log'
×
484
                else:
UNCOV
485
                    new_yscale = 'linear'
1✔
UNCOV
486
                new_yr = [pseudovar_props['line_ymin'], pseudovar_props['line_ymax']]
1✔
UNCOV
487
            override_yopts = {}
1✔
UNCOV
488
            if pseudo_right_axis:
1✔
489
                # Who knows?  Let the component variable Y-axis fight it out...
UNCOV
490
                override_yopts = {}
×
UNCOV
491
            elif yaxis_options is None:
1✔
492
                override_yopts = {'y_range':new_yr, 'y_range_user':True,'y_axis_style':new_yscale}
×
493
            else:
UNCOV
494
                if yaxis_options.get('y_range') is None:
1✔
UNCOV
495
                    override_yopts['y_range'] = new_yr
1✔
UNCOV
496
                    override_yopts['y_range_user'] = True
1✔
UNCOV
497
                if yaxis_options.get('y_axis_style') is None:
1✔
UNCOV
498
                    override_yopts['y_axis_style'] = new_yscale
1✔
UNCOV
499
            if yaxis_options is not None:
1✔
UNCOV
500
                yaxis_options = yaxis_options | override_yopts
1✔
501
            else:
UNCOV
502
                yaxis_options = override_yopts
×
503

504
            # Determine which Z axis options need to be changed to accommodate multiple component variables
505
            #
506
            # We only care about specplots here.
UNCOV
507
            new_zr = [ None, pseudovar_props['spec_zmax']]
1✔
UNCOV
508
            if pseudovar_props['spec_zlog']:
1✔
UNCOV
509
                new_zscale='log'
×
510
            else:
UNCOV
511
                new_zscale='linear'
1✔
UNCOV
512
            override_zopts = {}
1✔
UNCOV
513
            if zaxis_options is None:
1✔
UNCOV
514
                override_zopts = {'z_range':new_zr, 'z_axis_style':new_zscale}
×
515
            else:
UNCOV
516
                if zaxis_options.get('z_range') is None:
1✔
UNCOV
517
                    override_zopts['z_range'] = new_zr
1✔
UNCOV
518
                if zaxis_options.get('z_axis_style') is None:
1✔
UNCOV
519
                    override_zopts['z_axis_style'] = new_zscale
1✔
UNCOV
520
            if zaxis_options is not None:
1✔
UNCOV
521
                zaxis_options = zaxis_options | override_zopts
1✔
522
            else:
UNCOV
523
                zaxis_options = override_zopts
×
524

UNCOV
525
            for pseudo_idx, var in enumerate(pseudo_vars):
1✔
526
                # We're plotting a pseudovariable.  Iterate over the sub-variables, keeping track of how many
527
                # traces have been plotted so far, so we can correctly match option values to traces. The pseudovariable
528
                # y_axis, z_axis, line and extra options are passed as parameters so they can be merged with the
529
                # sub-variable options, with any pseudovar options overriding the sub-variable options.
UNCOV
530
                trace_count_thisvar = count_traces(var)
1✔
UNCOV
531
                if var == pseudovar_props['first_spec_var']:
1✔
UNCOV
532
                    pseudo_show_colorbar=True
×
533
                else:
UNCOV
534
                    pseudo_show_colorbar=False
1✔
UNCOV
535
                tplot(var,
1✔
536
                      trange=trange,
537
                      return_plot_objects=return_plot_objects,
538
                      xsize=xsize, ysize=ysize,
539
                      fig=fig, axis=this_axis, display=False,
540
                      running_trace_count=traces_processed,
541
                      pseudo_idx=pseudo_idx,
542
                      pseudo_xaxis_options=xaxis_options, pseudo_yaxis_options=yaxis_options, pseudo_zaxis_options=zaxis_options,
543
                      pseudo_line_options=line_opts, pseudo_extra_options=plot_extras,
544
                      pseudo_right_axis=pseudo_right_axis,
545
                      show_colorbar=pseudo_show_colorbar)
UNCOV
546
                traces_processed += trace_count_thisvar
1✔
547
            
548

UNCOV
549
            continue
1✔
550

551

552
        #if data_gap is an option for this variable, or if it's a add
553
        #gaps here; an individual gap setting should override the
554
        #global setting
555
        plot_extras = var_quants.attrs['plot_options']['extras']
3✔
556
        if plot_extras.get('data_gap') is not None and plot_extras.get('data_gap') > 0:
3✔
UNCOV
557
            var_data = makegap(var_data, dt = plot_extras.get('data_gap'))
1✔
558
        else:
559
            if pyspedas.tplot_tools.tplot_opt_glob['data_gap'] is not None and pyspedas.tplot_tools.tplot_opt_glob['data_gap'] > 0:
3✔
560
                var_data = makegap(var_data, dt = pyspedas.tplot_tools.tplot_opt_glob['data_gap'])
×
561

562
        # set the x-axis range, if it was set with xlim or tlimit or the trange parameter
563
        if trange is None and pyspedas.tplot_tools.tplot_opt_glob.get('x_range') is None:
3✔
UNCOV
564
            var_data_times = var_data.times
2✔
UNCOV
565
            time_idxs = np.arange(len(var_data_times))
2✔
566
        else:
567
            if trange is not None:
3✔
UNCOV
568
                if len(trange) != 2:
×
569
                    logging.error('Invalid trange setting: must be a 2-element list or array')
×
570
                    return
×
UNCOV
571
                if isinstance(trange[0], str):
×
UNCOV
572
                    x_range = pyspedas.tplot_tools.time_double(trange) # seconds since epoch
×
UNCOV
573
                    x_range_start = x_range[0]
×
UNCOV
574
                    x_range_stop = x_range[1]
×
575
            else:
576
                x_range = pyspedas.tplot_tools.tplot_opt_glob['x_range']  # Seconds since epoch
3✔
577
                x_range_start = x_range[0]
3✔
578
                x_range_stop = x_range[1]
3✔
579

580
            # Check for NaN or inf in x_range
581
            if not np.isfinite(x_range_start):
3✔
582
                logging.warning('tplot: x_range start is not finite, replacing with 0')
×
583
                x_range_start = 0
×
584

585
            if not np.isfinite(x_range_stop):
3✔
586
                logging.warning('tplot: x_range end is not finite, replacing with 0')
×
587
                x_range_stop = 0
×
588

589
            # Convert to np.datetime64 with nanosecond precision
590
            x_range = np.array(np.array([x_range_start*1e9, x_range_stop*1e9]),dtype='datetime64[ns]')
3✔
591
            this_axis.set_xlim(x_range)
3✔
592
            time_idxs = np.argwhere((var_data.times >= x_range[0]) & (var_data.times <= x_range[1])).flatten()
3✔
593
            if len(time_idxs) == 0:
3✔
UNCOV
594
                logging.info('No data found in the time range: ' + variable)
×
UNCOV
595
                continue
×
596
            var_data_times = var_data.times[time_idxs]
3✔
597

598
        var_times = var_data_times
3✔
599

600
        # set some more plot options
601
        xaxis_options = var_quants.attrs['plot_options']['xaxis_opt']
3✔
602
        if pseudo_xaxis_options is not None and len(pseudo_xaxis_options) > 0:
3✔
603
            merged_xaxis_options = xaxis_options | pseudo_xaxis_options
×
604
            xaxis_options = merged_xaxis_options
×
605

606
        yaxis_options = var_quants.attrs['plot_options']['yaxis_opt']
3✔
607
        if pseudo_yaxis_options is not None and len(pseudo_yaxis_options) > 0:
3✔
UNCOV
608
            merged_yaxis_options = yaxis_options | pseudo_yaxis_options
1✔
UNCOV
609
            yaxis_options = merged_yaxis_options
1✔
610

611
        zaxis_options = var_quants.attrs['plot_options']['zaxis_opt']
3✔
612
        if pseudo_zaxis_options is not None and len(pseudo_zaxis_options) > 0:
3✔
UNCOV
613
            merged_zaxis_options = zaxis_options | pseudo_zaxis_options
1✔
UNCOV
614
            zaxis_options = merged_zaxis_options
1✔
615

616
        line_opts = var_quants.attrs['plot_options']['line_opt']
3✔
617
        if pseudo_line_options is not None and len(pseudo_line_options) > 0:
3✔
UNCOV
618
            merged_line_opts = line_opts | pseudo_line_options
1✔
UNCOV
619
            line_opts = merged_line_opts
1✔
620

621
        if line_opts is not None:
3✔
622
            if 'name' in line_opts:
3✔
623
                this_axis.set_title(line_opts['name'])
×
624
            elif 'title' in line_opts:
3✔
625
                this_axis.set_title(line_opts['title'])
1✔
626

627
        plot_extras = var_quants.attrs['plot_options']['extras']
3✔
628
        if pseudo_extra_options is not None and len(pseudo_extra_options) > 0:
3✔
UNCOV
629
            merged_plot_extras = plot_extras | pseudo_extra_options
1✔
UNCOV
630
            plot_extras = merged_plot_extras
1✔
631

632
        xtitle = None
3✔
633
        if xaxis_options.get('axis_label') is not None:
3✔
634
            xtitle = xaxis_options['axis_label']
3✔
635

636

637
        xsubtitle = ''
3✔
638
        if xaxis_options.get('axis_subtitle') is not None:
3✔
UNCOV
639
            xsubtitle = xaxis_options['axis_subtitle']
×
640

641
        if style is None:
3✔
642
            xtitle_color = 'black'
3✔
643
        else:
644
            xtitle_color = None
×
645

646
        if xaxis_options.get('axis_color') is not None:
3✔
UNCOV
647
            xtitle_color = xaxis_options['axis_color']
×
648

649
        ylog = yaxis_options['y_axis_type']
3✔
650

651
        if ylog == 'log':
3✔
652
            this_axis.set_yscale('log')
3✔
653
        else:
654
            this_axis.set_yscale('linear')
3✔
655

656
        ytitle = yaxis_options['axis_label']
3✔
657
        if ytitle == '':
3✔
UNCOV
658
            ytitle = variable
2✔
659

660
        ysubtitle = ''
3✔
661
        if yaxis_options.get('axis_subtitle') is not None:
3✔
662
            ysubtitle = yaxis_options['axis_subtitle']
3✔
663

664
        # replace some common superscripts
665
        ysubtitle = replace_common_exp(ysubtitle)
3✔
666

667
        if axis_font_size is not None:
3✔
668
            this_axis.tick_params(axis='x', labelsize=axis_font_size)
3✔
669
            this_axis.tick_params(axis='y', labelsize=axis_font_size)
3✔
670

671
        char_size = pyspedas.tplot_tools.tplot_opt_glob.get('charsize')
3✔
672
        if char_size is None:
3✔
673
            char_size = 12
3✔
674

675
        if plot_extras.get('char_size') is not None:
3✔
UNCOV
676
            char_size = plot_extras['char_size']
×
677

678
        user_set_yrange = yaxis_options.get('y_range_user')
3✔
679
        if user_set_yrange is not None:
3✔
680
            # the user has set the yrange manually
681
            yrange = yaxis_options['y_range']
3✔
682
            if not np.isfinite(yrange[0]):
3✔
683
                yrange[0] = None
×
684
            if not np.isfinite(yrange[1]):
3✔
685
                yrange[1] = None
×
686
            this_axis.set_ylim(yrange)
3✔
687

688
        ymajor_ticks = yaxis_options.get('y_major_ticks')
3✔
689
        if ymajor_ticks is not None:
3✔
UNCOV
690
            this_axis.set_yticks(ymajor_ticks)
×
691

692
        yminor_tick_interval = yaxis_options.get('y_minor_tick_interval')
3✔
693
        if yminor_tick_interval is not None and ylog != 'log':
3✔
UNCOV
694
            this_axis.yaxis.set_minor_locator(plt.MultipleLocator(yminor_tick_interval))
×
695

696
        if style is None:
3✔
697
            ytitle_color = 'black'
3✔
698
        else:
699
            ytitle_color = None
×
700

701
        if yaxis_options.get('axis_color') is not None:
3✔
UNCOV
702
            ytitle_color = yaxis_options['axis_color']
×
703

704
        if xtitle is not None and xtitle != '':
3✔
UNCOV
705
            if xtitle_color is not None:
×
UNCOV
706
                this_axis.set_xlabel(xtitle + '\n' + xsubtitle, fontsize=char_size, color=xtitle_color)
×
707
            else:
708
                this_axis.set_xlabel(xtitle + '\n' + xsubtitle, fontsize=char_size)
×
709

710
        if ytitle_color is not None:
3✔
711
            this_axis.set_ylabel(ytitle + '\n' + ysubtitle, fontsize=char_size, color=ytitle_color)
3✔
712
        else:
713
            this_axis.set_ylabel(ytitle + '\n' + ysubtitle, fontsize=char_size)
×
714

715
        border = True
3✔
716
        if plot_extras.get('border') is not None:
3✔
717
            border = plot_extras['border']
3✔
718

719
        if border == False:
3✔
720
            this_axis.axis('off')
×
721

722
        # axis tick options
723
        if plot_extras.get('xtickcolor') is not None:
3✔
UNCOV
724
            this_axis.tick_params(axis='x', color=plot_extras.get('xtickcolor'))
×
725

726
        if plot_extras.get('ytickcolor') is not None:
3✔
UNCOV
727
            this_axis.tick_params(axis='y', color=plot_extras.get('ytickcolor'))
×
728

729
        if plot_extras.get('xtick_direction') is not None:
3✔
UNCOV
730
            this_axis.tick_params(axis='x', direction=plot_extras.get('xtick_direction'))
×
731

732
        if plot_extras.get('ytick_direction') is not None:
3✔
UNCOV
733
            this_axis.tick_params(axis='y', direction=plot_extras.get('ytick_direction'))
×
734

735
        if plot_extras.get('xtick_length') is not None:
3✔
UNCOV
736
            this_axis.tick_params(axis='x', length=plot_extras.get('xtick_length'))
×
737

738
        if plot_extras.get('ytick_length') is not None:
3✔
UNCOV
739
            this_axis.tick_params(axis='y', length=plot_extras.get('ytick_length'))
×
740

741
        if plot_extras.get('xtick_width') is not None:
3✔
UNCOV
742
            this_axis.tick_params(axis='x', width=plot_extras.get('xtick_width'))
×
743

744
        if plot_extras.get('ytick_width') is not None:
3✔
UNCOV
745
            this_axis.tick_params(axis='y', width=plot_extras.get('ytick_width'))
×
746

747
        if plot_extras.get('xtick_labelcolor') is not None:
3✔
UNCOV
748
            this_axis.tick_params(axis='x', labelcolor=plot_extras.get('xtick_labelcolor'))
×
749

750
        if plot_extras.get('ytick_labelcolor') is not None:
3✔
UNCOV
751
            this_axis.tick_params(axis='y', labelcolor=plot_extras.get('ytick_labelcolor'))
×
752

753
        # determine if this is a line plot or a spectrogram
754
        spec = False
3✔
755
        if plot_extras.get('spec') is not None:
3✔
756
            spec = plot_extras['spec']
3✔
757

758
        if spec:
3✔
759
            # create spectrogram plots
760
            plot_created = specplot(var_data, var_times, this_axis, yaxis_options, zaxis_options, plot_extras, colorbars, axis_font_size, fig, variable, time_idxs=time_idxs, style=style)
3✔
761
            if not plot_created:
3✔
762
                continue
×
763
        else:
764
            # create line plots
765
            plot_created = lineplot(var_data, var_times, this_axis, line_opts, yaxis_options, plot_extras, running_trace_count=running_trace_count, time_idxs=time_idxs, style=style, var_metadata=var_metadata)
3✔
766
            if not plot_created:
3✔
767
                continue
×
768

769
        # apply any vertical/horizontal bars
770
        if pyspedas.tplot_tools.data_quants[variable].attrs['plot_options'].get('time_bar') is not None:
3✔
771
            time_bars = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['time_bar']
3✔
772

773
            for time_bar in time_bars:
3✔
774
                # vertical bars
UNCOV
775
                if time_bar['dimension'] == 'height':
×
UNCOV
776
                    this_axis.axvline(x=datetime.fromtimestamp(time_bar['location'], tz=timezone.utc),
×
777
                        color=np.array(time_bar.get('line_color'))/256.0, lw=time_bar.get('line_width'),
778
                                      linestyle=time_bar.get('line_dash'))
779

780
                # horizontal bars
UNCOV
781
                if time_bar['dimension'] == 'width':
×
UNCOV
782
                    this_axis.axhline(y=time_bar['location'], color=np.array(time_bar.get('line_color'))/256.0,
×
783
                                      lw=time_bar.get('line_width'),
784
                                      linestyle=time_bar.get('line_dash'))
785

786
        # highlight time intervals
787
        if pyspedas.tplot_tools.data_quants[variable].attrs['plot_options'].get('highlight_intervals') is not None:
3✔
UNCOV
788
            highlight_intervals = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['highlight_intervals']
×
789

UNCOV
790
            for highlight_interval in highlight_intervals:
×
UNCOV
791
                hightlight_opts = copy.deepcopy(highlight_interval)
×
UNCOV
792
                del hightlight_opts['location']
×
UNCOV
793
                if highlight_interval['edgecolor'] is not None or highlight_interval['facecolor'] is not None:
×
794
                    del hightlight_opts['color']
×
795

UNCOV
796
                this_axis.axvspan(mdates.date2num(datetime.fromtimestamp(highlight_interval['location'][0], timezone.utc)),
×
797
                                  mdates.date2num(datetime.fromtimestamp(highlight_interval['location'][1], timezone.utc)),
798
                                  **hightlight_opts)
799

800
        # add annotations
801
        if pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['extras'].get('annotations') is not None:
3✔
UNCOV
802
            annotations = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['extras']['annotations']
×
UNCOV
803
            for annotation in annotations:
×
UNCOV
804
                this_axis.annotate(annotation['text'], annotation['position'],
×
805
                                   xycoords=annotation['xycoords'],
806
                                   fontsize=annotation['fontsize'],
807
                                   alpha=annotation['alpha'],
808
                                   fontfamily=annotation['fontfamily'],
809
                                   fontvariant=annotation['fontvariant'],
810
                                   fontstyle=annotation['fontstyle'],
811
                                   fontstretch=annotation['fontstretch'],
812
                                   fontweight=annotation['fontweight'],
813
                                   rotation=annotation['rotation'],
814
                                   color=annotation['color'])
815

816
    # apply any addition x-axes (or panel) specified by the var_label keyword
817
    if var_label is not None:
3✔
UNCOV
818
        if varlabel_style is None or varlabel_style.lower() == 'extra_axes':
×
UNCOV
819
            varlabels_extra_axes(num_panels, this_axis, var_label, axis_font_size, plot_extras=plot_extras)
×
820
        else:
UNCOV
821
            var_label_panel(variables, var_label, axes,  axis_font_size)
×
822

823
    # add the color bars to any spectra
824
    for idx, variable in enumerate(variables):
3✔
825
        if pyspedas.tplot_tools.data_quants.get(variable) is None:
3✔
826
            continue
×
827
        plot_extras = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['extras']
3✔
828
        zaxis_options = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['zaxis_opt']
3✔
829
        if plot_extras.get('spec') is not None:
3✔
830
            spec = plot_extras['spec']
3✔
831
        else:
832
            spec = False
3✔
833

834

835
        if spec and show_colorbar:
3✔
836
            if colorbars.get(variable) is None:
3✔
837
                continue
×
838

839
            if num_panels == 1:
3✔
840
                this_axis = axes
3✔
841
            else:
842
                this_axis = axes[idx]
3✔
843

844
            colorbar = fig.colorbar(colorbars[variable]['im'], ax=this_axis)
3✔
845

846
            if style is None:
3✔
847
                ztitle_color = 'black'
3✔
848
            else:
849
                ztitle_color = None
×
850

851
            if zaxis_options is None:
3✔
852
                continue
×
853

854
            if zaxis_options.get('axis_color') is not None:
3✔
UNCOV
855
                ztitle_color = zaxis_options['axis_color']
×
856

857
            ztitle_text = colorbars[variable]['ztitle']
3✔
858
            zsubtitle_text = colorbars[variable]['zsubtitle']
3✔
859

860
            # replace some common superscripts
861
            ztitle_text = replace_common_exp(ztitle_text)
3✔
862
            zsubtitle_text = replace_common_exp(zsubtitle_text)
3✔
863

864
            if ztitle_color is not None:
3✔
865
                colorbar.set_label(ztitle_text + '\n ' + zsubtitle_text,
3✔
866
                                   color=ztitle_color, fontsize=char_size)
867
            else:
868
                colorbar.set_label(ztitle_text + '\n ' + zsubtitle_text,
×
869
                                   fontsize=char_size)
870

871
    # plt.tight_layout()
872
    fig.canvas.draw()
3✔
873

874
    save_plot(save_png=save_png, save_eps=save_eps, save_jpeg=save_jpeg, save_pdf=save_pdf, save_svg=save_svg, dpi=dpi)
3✔
875

876
    if slice:
3✔
877
        slice_fig, slice_axes = plt.subplots(nrows=1)
×
878
        slice_plot, = slice_axes.plot([0], [0])
×
879
        mouse_event_func = lambda event: mouse_move_slice(event, slice_axes, slice_plot)
×
880
        cid = fig.canvas.mpl_connect('motion_notify_event', mouse_event_func)
×
881

882
    if display:
3✔
883
        plt.show()
1✔
884

885
    if return_plot_objects:
3✔
UNCOV
886
        return fig, axes
×
887

888

889
def varlabels_extra_axes(num_panels, this_axis, var_label, axis_font_size, plot_extras):
3✔
890
    # apply any addition x-axes specified by the var_label keyword
UNCOV
891
    if var_label is not None:
×
UNCOV
892
        if not isinstance(var_label, list):
×
893
            var_label = [var_label]
×
894

UNCOV
895
        char_size = pyspedas.tplot_tools.tplot_opt_glob.get('charsize')
×
UNCOV
896
        if char_size is None:
×
UNCOV
897
            char_size = 12
×
898

UNCOV
899
        if plot_extras.get('char_size') is not None:
×
900
            char_size = plot_extras['char_size']
×
901

UNCOV
902
        axis_delta = 0.0
×
903

UNCOV
904
        for label in var_label:
×
UNCOV
905
            if isinstance(label, int):
×
906
                label = tname_byindex(label)
×
UNCOV
907
            label_data = get_data(label, xarray=True, dt=True)
×
908

UNCOV
909
            if label_data is None:
×
910
                logging.info('Variable not found: ' + label)
×
911
                continue
×
912

UNCOV
913
            if len(label_data.values.shape) != 1:
×
914
                logging.info(
×
915
                    label + ' specified as a vector; var_label only supports scalars. Try splitting the vector into seperate tplot variables.')
916
                continue
×
917

918
            # set up the new x-axis
UNCOV
919
            axis_delta = axis_delta - num_panels * 0.1
×
UNCOV
920
            new_xaxis = this_axis.secondary_xaxis(axis_delta)
×
UNCOV
921
            if axis_font_size is not None:
×
UNCOV
922
                new_xaxis.tick_params(axis='x', labelsize=axis_font_size)
×
UNCOV
923
                new_xaxis.tick_params(axis='y', labelsize=axis_font_size)
×
924

UNCOV
925
            xaxis_ticks = this_axis.get_xticks().tolist()
×
UNCOV
926
            xaxis_ticks_dt = [np.datetime64(mpl.dates.num2date(tick_val).replace(tzinfo=None).isoformat(), 'ns') for
×
927
                              tick_val in xaxis_ticks]
928
            # xaxis_ticks_unix = [tick_val.timestamp() for tick_val in xaxis_ticks_dt]
UNCOV
929
            xaxis_labels = get_var_label_ticks(label_data, xaxis_ticks_dt)
×
UNCOV
930
            new_xaxis.set_xticks(xaxis_ticks_dt)
×
UNCOV
931
            new_xaxis.set_xticklabels(xaxis_labels)
×
UNCOV
932
            ytitle = pyspedas.tplot_tools.data_quants[label].attrs['plot_options']['yaxis_opt']['axis_label']
×
UNCOV
933
            new_xaxis.set_xlabel(ytitle, fontsize=char_size)
×
934

935
        # fig.subplots_adjust(bottom=0.05+len(var_label)*0.1)
936

937

938
def mouse_move_slice(event, slice_axes, slice_plot):
3✔
939
    """
940
    This function is called when the mouse moves over an axis
941
    and the slice keyword is set to True; for spectra figures, it
942
    updates the slice plot based on the mouse location
943
    """
944
    if event.inaxes is None:
×
945
        return
×
946

947
    # check for a spectrogram
948
    try:
×
949
        data = get_data(event.inaxes.var_name)
×
950
    except AttributeError:
×
951
        return
×
952

953
    if data is None:
×
954
        return
×
955

956
    if len(data) != 3:
×
957
        return
×
958

959
    slice_time = mdates.num2date(event.xdata).timestamp()
×
960
    idx = np.abs(data.times-slice_time).argmin()
×
961

962
    if len(data.v.shape) > 1:
×
963
        # time varying y-axis
964
        vdata = data.v[idx, :]
×
965
    else:
966
        vdata = data.v
×
967

968
    xaxis_options = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options']['xaxis_opt']
×
969
    yaxis_options = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options']['yaxis_opt']
×
970
    zaxis_options = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options']['zaxis_opt']
×
971

972
    yrange = yaxis_options.get('y_range')
×
973
    if yrange is None:
×
974
        yrange = [np.nanmin(vdata), np.nanmax(vdata)]
×
975

976
    zrange = zaxis_options.get('z_range')
×
977
    if zrange is None:
×
978
        zrange = [np.nanmin(data.y), np.nanmax(data.y)]
×
979

980
    y_label = zaxis_options.get('axis_label')
×
981
    if y_label is not None:
×
982
        slice_axes.set_ylabel(y_label)
×
983

984
    title = datetime.fromtimestamp(data.times[idx], timezone.utc).strftime('%Y-%m-%d %H:%M:%S.%f')
×
985

986
    x_label = yaxis_options.get('axis_label')
×
987
    if x_label is not None:
×
988
        title = x_label + ' (' + title + ')'
×
989

990
    slice_axes.set_title(title)
×
991

992
    x_subtitle = yaxis_options.get('axis_subtitle')
×
993
    if x_subtitle is not None:
×
994
        slice_axes.set_xlabel(x_subtitle)
×
995

996
    slice_yaxis_opt = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options'].get('slice_yaxis_opt')
×
997

998
    xscale = None
×
999
    yscale = None
×
1000

1001
    if slice_yaxis_opt is not None:
×
1002
        xscale = slice_yaxis_opt.get('xi_axis_type')
×
1003
        yscale = slice_yaxis_opt.get('yi_axis_type')
×
1004

1005
    if yscale is None:
×
1006
        # if the user didn't explicitly set the ylog_slice option,
1007
        # use the option from the plot
1008
        yscale = zaxis_options.get('z_axis_type')
×
1009
        if yscale is None:
×
1010
            yscale = 'linear'
×
1011

1012
    if xscale is None:
×
1013
        # if the user didn't explicitly set the xlog_slice option,
1014
        # use the option from the plot
1015
        xscale = yaxis_options.get('y_axis_type')
×
1016
        if xscale is None:
×
1017
            xscale = 'linear'
×
1018

1019
    if yscale == 'log' and zrange[0] == 0.0:
×
1020
        zrange[0] = np.nanmin(data.y[idx, :])
×
1021

1022
    slice_plot.set_data(vdata, data.y[idx, :])
×
1023
    slice_axes.set_ylim(zrange)
×
1024
    slice_axes.set_xlim(yrange)
×
1025
    slice_axes.set_xscale(xscale)
×
1026
    slice_axes.set_yscale(yscale)
×
1027

1028
    try:
×
1029
        plt.draw()
×
1030
    except ValueError:
×
1031
        return
×
1032
    sleep(0.01)
×
1033

1034
def replace_common_exp(title):
3✔
1035
    if hasattr(title, 'decode'):
3✔
UNCOV
1036
        title = title.decode('utf-8')
2✔
1037
    if '$' in title:
3✔
1038
        return title
3✔
1039
    if '^' not in title:
3✔
1040
        return title
3✔
1041
    exp = False
3✔
1042
    title_out = ''
3✔
1043
    for char in title:
3✔
1044
        if char == '^':
3✔
1045
            exp = True
3✔
1046
            title_out += '$^{'
3✔
1047
            continue
3✔
1048
        else:
1049
            if exp:
3✔
1050
                if not char.isalnum():
3✔
1051
                    title_out += '}$' + char
3✔
1052
                    exp = False
3✔
1053
                    continue
3✔
1054
        title_out += char
3✔
1055
    if exp:
3✔
1056
        title_out += '}$'
×
1057
    return title_out
3✔
1058

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc