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

spedas / pyspedas / 28726857065

05 Jul 2026 02:22AM UTC coverage: 89.847% (-0.8%) from 90.682%
28726857065

push

github

jameswilburlewis
Temporarily commenting out mica tests due to server outage at unh.edu.

44760 of 49818 relevant lines covered (89.85%)

0.9 hits per line

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

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

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

25
def pseudovar_component_props(varname: str):
1✔
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

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

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

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

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

69
    # ylog is now guaranteed to be boolean
70

71
    if plot_extras.get('spec') is not None:
1✔
72
        spec = plot_extras['spec']
1✔
73
        if spec:
1✔
74
            # Inspect a specplot variable
75
            output_dict['is_spec']=True
1✔
76
            if yrange is not None:
1✔
77
                output_dict['ymin'] = yrange[0]
1✔
78
                output_dict['ymax'] = yrange[1]
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

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

119

120
    else:
121
        # Inspect a line plot variable
122
        output_dict['is_spec']=False
1✔
123
        if yrange is not None:
1✔
124
            output_dict['ymin'] = yrange[0]
1✔
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)
×
129
    return output_dict
1✔
130

131
def gather_pseudovar_props(pseudovars:list[str]):
1✔
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
    """
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

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

193

194
def tplot(variables,
1✔
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)
1✔
316
    if len(variables) == 0:
1✔
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')
1✔
322
    if varlabel_style is None or varlabel_style.lower() == 'extra_axes':
1✔
323
        num_panels = len(variables)
1✔
324
        panel_sizes = [1]*num_panels
1✔
325
    else: # varlabel_style 'extra_panel'
326
        num_panels = len(variables) + 1
1✔
327
        panel_sizes = [1] * len(variables) + [0.1 * (len(var_label) + 2)]
1✔
328

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

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

342
    if ysize is None:
1✔
343
        ysize = pyspedas.tplot_tools.tplot_opt_glob.get('ysize')
1✔
344
        if ysize is None:
1✔
345
            if num_panels > 4:
1✔
346
                ysize = 8
1✔
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]
1✔
350
                ysize = default_y_sizes[num_panels]
1✔
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:
1✔
364
        fig, axes = plt.subplots(nrows=num_panels, sharex=True, gridspec_kw={'height_ratios': panel_sizes}, layout='constrained')
1✔
365
        fig.set_size_inches(xsize, ysize)
1✔
366
        plot_title = pyspedas.tplot_tools.tplot_opt_glob['title_text']
1✔
367
        fig.suptitle(plot_title)
1✔
368
        if (plot_title is not None) and plot_title != '':
1✔
369
            if 'title_size' in pyspedas.tplot_tools.tplot_opt_glob:
1✔
370
                title_size = pyspedas.tplot_tools.tplot_opt_glob['title_size']
1✔
371
                fig.suptitle(plot_title, fontsize=title_size)
1✔
372
            else:
373
                fig.suptitle(plot_title)
×
374
        # support for matplotlib styles
375
        style = pyspedas.tplot_tools.tplot_opt_glob.get('style')
1✔
376
        if style is not None:
1✔
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
381
        if pseudo_idx == 0 or pseudo_right_axis == False:
1✔
382
            # setting up first axis
383
            axes = axis
1✔
384
        elif pseudo_idx > 0 and pseudo_right_axis:
1✔
385
            # generate and use the right axis?  probably still wrong...
386
            axes = axis.twinx()
1✔
387
        # support for matplotlib styles -- shouldn't be needed if processing pseudovar components?
388
        style = pyspedas.tplot_tools.tplot_opt_glob.get('style')
1✔
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')
1✔
394

395
    colorbars = {}
1✔
396

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

401
        # reset all plot options to None for this iteration
402
        xaxis_options = dict()
1✔
403
        yaxis_options = dict()
1✔
404
        zaxis_options = dict()
1✔
405
        line_opts = dict()
1✔
406
        plot_extras = dict()
1✔
407

408
        #Check for a 3d variable, call reduce_spec_dataset
409
        if hasattr(var_data_org, 'v1') and hasattr(var_data_org, 'v2'):
1✔
410
            temp_dq = reduce_spec_dataset(name=variable)
1✔
411
            var_data_org = get_data(variable, dt=True, data_quant_in=temp_dq)
1✔
412
        
413
        if var_data_org is None:
1✔
414
            logging.info('Variable not found: ' + variable)
×
415
            continue
×
416

417
        var_data = copy.deepcopy(var_data_org)
1✔
418

419
        # plt.subplots returns a list of axes for multiple panels
420
        # but only a single axis for a single panel
421
        if num_panels == 1:
1✔
422
            this_axis = axes
1✔
423
        else:
424
            this_axis = axes[idx]
1✔
425

426
        # we need to track the variable name in the axis object
427
        # for spectrogram slices
428
        this_axis.var_name = variable
1✔
429

430
        pseudo_var = False
1✔
431
        overplots = None
1✔
432
        spec = False
1✔
433

434
        var_quants = pyspedas.tplot_tools.data_quants[variable]
1✔
435

436
        if not isinstance(var_quants, dict):
1✔
437
            overplots = var_quants.attrs['plot_options'].get('overplots_mpl')
1✔
438
            if overplots is not None and len(overplots) > 0:
1✔
439
                pseudo_var = True
1✔
440

441
        # deal with pseudo-variables first
442
        if isinstance(var_data, list) or isinstance(var_data, str) or pseudo_var:
1✔
443
            # this is a pseudo variable
444
            if isinstance(var_data, str):
1✔
445
                var_data = var_data.split(' ')
×
446

447
            if pseudo_var:
1✔
448
                pseudo_vars = overplots
1✔
449
            else:
450
                pseudo_vars = var_data
×
451

452
            # pseudo variable metadata should override the metadata
453
            # for individual variables
454
            xaxis_options = None
1✔
455
            yaxis_options = None
1✔
456
            zaxis_options = None
1✔
457
            line_opts = None
1✔
458
            plot_extras = None
1✔
459
            if pseudo_var:
1✔
460
                plot_extras = var_quants.attrs['plot_options']['extras']
1✔
461
                if plot_extras.get('spec') is not None:
1✔
462
                    spec = True
1✔
463

464
                if plot_extras.get('right_axis') is not None:
1✔
465
                    if plot_extras.get('right_axis'):
1✔
466
                        pseudo_right_axis = True
1✔
467

468
                if pseudo_right_axis: # if pseudo_right_axis:?
1✔
469
                    plot_extras = None
1✔
470
                    yaxis_options = var_quants.attrs['plot_options']['yaxis_opt']
1✔
471
                    zaxis_options = var_quants.attrs['plot_options']['zaxis_opt']
1✔
472
                    line_opts = var_quants.attrs['plot_options']['line_opt']
1✔
473

474
                else:
475
                    yaxis_options = var_quants.attrs['plot_options']['yaxis_opt']
1✔
476
                    zaxis_options = var_quants.attrs['plot_options']['zaxis_opt']
1✔
477
                    line_opts = var_quants.attrs['plot_options']['line_opt']
1✔
478

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

515
            # Determine which Z axis options need to be changed to accommodate multiple component variables
516
            #
517
            # We only care about specplots here.
518
            new_zr = [ None, pseudovar_props['spec_zmax']]
1✔
519
            if pseudovar_props['spec_zlog']:
1✔
520
                new_zscale='log'
1✔
521
            else:
522
                new_zscale='linear'
1✔
523
            override_zopts = {}
1✔
524
            if zaxis_options is None:
1✔
525
                override_zopts = {'z_range':new_zr, 'z_axis_style':new_zscale}
×
526
            else:
527
                if zaxis_options.get('z_range') is None:
1✔
528
                    override_zopts['z_range'] = new_zr
1✔
529
                if zaxis_options.get('z_axis_style') is None:
1✔
530
                    override_zopts['z_axis_style'] = new_zscale
1✔
531
            if zaxis_options is not None:
1✔
532
                zaxis_options = zaxis_options | override_zopts
1✔
533
            else:
534
                zaxis_options = override_zopts
×
535

536
            for pseudo_idx, var in enumerate(pseudo_vars):
1✔
537
                # We're plotting a pseudovariable.  Iterate over the sub-variables, keeping track of how many
538
                # traces have been plotted so far, so we can correctly match option values to traces. The pseudovariable
539
                # y_axis, z_axis, line and extra options are passed as parameters so they can be merged with the
540
                # sub-variable options, with any pseudovar options overriding the sub-variable options.
541
                trace_count_thisvar = count_traces(var)
1✔
542
                if var == pseudovar_props['first_spec_var']:
1✔
543
                    pseudo_show_colorbar=True
1✔
544
                else:
545
                    pseudo_show_colorbar=False
1✔
546

547
                var_attrs = pyspedas.tplot_tools.data_quants[var].attrs
1✔
548
                var_is_spec = bool(var_attrs['plot_options']['extras'].get('spec'))
1✔
549
                this_plot_extras = plot_extras
1✔
550
                if not var_is_spec:
1✔
551
                    # Do not propagate spec flag or spec_dims_to_plot for non-spec base variables
552
                    # But we do want to propagate some other options, like line colors
553
                    if this_plot_extras is not None:
1✔
554
                        this_plot_extras["spec"] = False
1✔
555
                        this_plot_extras["spec_dim_to_plot"] = None
1✔
556
                this_zaxis_options = zaxis_options if var_is_spec else None
1✔
557
                
558
                tplot(var,
1✔
559
                      trange=trange,
560
                      return_plot_objects=return_plot_objects,
561
                      xsize=xsize, ysize=ysize,
562
                      fig=fig, axis=this_axis, display=False,
563
                      running_trace_count=traces_processed,
564
                      pseudo_idx=pseudo_idx,
565
                      pseudo_xaxis_options=xaxis_options, pseudo_yaxis_options=yaxis_options, pseudo_zaxis_options=this_zaxis_options,
566
                      pseudo_line_options=line_opts, pseudo_extra_options=this_plot_extras,
567
                      pseudo_right_axis=pseudo_right_axis,
568
                      show_colorbar=pseudo_show_colorbar)
569
                traces_processed += trace_count_thisvar
1✔
570
            
571

572
            continue
1✔
573

574

575
        #if data_gap is an option for this variable, or if it's a add
576
        #gaps here; an individual gap setting should override the
577
        #global setting
578
        plot_extras = var_quants.attrs['plot_options']['extras']
1✔
579
        if plot_extras.get('data_gap') is not None and plot_extras.get('data_gap') > 0:
1✔
580
            var_data = makegap(var_data, dt = plot_extras.get('data_gap'))
1✔
581
        else:
582
            if pyspedas.tplot_tools.tplot_opt_glob['data_gap'] is not None and pyspedas.tplot_tools.tplot_opt_glob['data_gap'] > 0:
1✔
583
                var_data = makegap(var_data, dt = pyspedas.tplot_tools.tplot_opt_glob['data_gap'])
×
584

585
        # set the x-axis range, if it was set with xlim or tlimit or the trange parameter
586
        if trange is None and pyspedas.tplot_tools.tplot_opt_glob.get('x_range') is None:
1✔
587
            var_data_times = var_data.times
1✔
588
            time_idxs = np.arange(len(var_data_times))
1✔
589
        else:
590
            if trange is not None:
1✔
591
                if len(trange) != 2:
1✔
592
                    logging.error('Invalid trange setting: must be a 2-element list or array')
×
593
                    return
×
594
                if isinstance(trange[0], str):
1✔
595
                    x_range = pyspedas.tplot_tools.time_double(trange) # seconds since epoch
1✔
596
                    x_range_start = x_range[0]
1✔
597
                    x_range_stop = x_range[1]
1✔
598
            else:
599
                x_range = pyspedas.tplot_tools.tplot_opt_glob['x_range']  # Seconds since epoch
1✔
600
                x_range_start = x_range[0]
1✔
601
                x_range_stop = x_range[1]
1✔
602

603
            # Check for NaN or inf in x_range
604
            if not np.isfinite(x_range_start):
1✔
605
                logging.warning('tplot: x_range start is not finite, replacing with 0')
×
606
                x_range_start = 0
×
607

608
            if not np.isfinite(x_range_stop):
1✔
609
                logging.warning('tplot: x_range end is not finite, replacing with 0')
×
610
                x_range_stop = 0
×
611

612
            # Convert to np.datetime64 with nanosecond precision
613
            x_range = np.array(np.array([x_range_start*1e9, x_range_stop*1e9]),dtype='datetime64[ns]')
1✔
614
            this_axis.set_xlim(x_range)
1✔
615
            time_idxs = np.argwhere((var_data.times >= x_range[0]) & (var_data.times <= x_range[1])).flatten()
1✔
616
            if len(time_idxs) == 0:
1✔
617
                logging.info('No data found in the time range: ' + variable)
1✔
618
                continue
1✔
619
            var_data_times = var_data.times[time_idxs]
1✔
620

621
        var_times = var_data_times
1✔
622

623
        # set some more plot options
624
        xaxis_options = var_quants.attrs['plot_options']['xaxis_opt']
1✔
625
        if pseudo_xaxis_options is not None and len(pseudo_xaxis_options) > 0:
1✔
626
            merged_xaxis_options = xaxis_options | pseudo_xaxis_options
×
627
            xaxis_options = merged_xaxis_options
×
628

629
        yaxis_options = var_quants.attrs['plot_options']['yaxis_opt']
1✔
630
        if pseudo_yaxis_options is not None and len(pseudo_yaxis_options) > 0:
1✔
631
            merged_yaxis_options = yaxis_options | pseudo_yaxis_options
1✔
632
            yaxis_options = merged_yaxis_options
1✔
633

634
        zaxis_options = var_quants.attrs['plot_options']['zaxis_opt']
1✔
635
        if pseudo_zaxis_options is not None and len(pseudo_zaxis_options) > 0:
1✔
636
            merged_zaxis_options = zaxis_options | pseudo_zaxis_options
1✔
637
            zaxis_options = merged_zaxis_options
1✔
638

639
        line_opts = var_quants.attrs['plot_options']['line_opt']
1✔
640
        if pseudo_line_options is not None and len(pseudo_line_options) > 0:
1✔
641
            merged_line_opts = line_opts | pseudo_line_options
1✔
642
            line_opts = merged_line_opts
1✔
643

644
        if line_opts is not None:
1✔
645
            if 'name' in line_opts:
1✔
646
                this_axis.set_title(line_opts['name'])
×
647
            elif 'title' in line_opts:
1✔
648
                this_axis.set_title(line_opts['title'])
1✔
649

650
        plot_extras = var_quants.attrs['plot_options']['extras']
1✔
651
        if pseudo_extra_options is not None and len(pseudo_extra_options) > 0:
1✔
652
            merged_plot_extras = plot_extras | pseudo_extra_options
1✔
653
            plot_extras = merged_plot_extras
1✔
654

655
        xtitle = None
1✔
656
        if xaxis_options.get('axis_label') is not None:
1✔
657
            xtitle = xaxis_options['axis_label']
1✔
658

659

660
        xsubtitle = ''
1✔
661
        if xaxis_options.get('axis_subtitle') is not None:
1✔
662
            xsubtitle = xaxis_options['axis_subtitle']
1✔
663

664
        if style is None:
1✔
665
            xtitle_color = 'black'
1✔
666
        else:
667
            xtitle_color = None
×
668

669
        if xaxis_options.get('axis_color') is not None:
1✔
670
            xtitle_color = xaxis_options['axis_color']
1✔
671

672
        ylog = yaxis_options['y_axis_type']
1✔
673

674
        if ylog == 'log':
1✔
675
            this_axis.set_yscale('log')
1✔
676
        else:
677
            this_axis.set_yscale('linear')
1✔
678

679
        ytitle = yaxis_options['axis_label']
1✔
680
        if ytitle == '':
1✔
681
            ytitle = variable
1✔
682

683
        ysubtitle = ''
1✔
684
        if yaxis_options.get('axis_subtitle') is not None:
1✔
685
            ysubtitle = yaxis_options['axis_subtitle']
1✔
686

687
        # replace some common superscripts
688
        ysubtitle = replace_common_exp(ysubtitle)
1✔
689

690
        if axis_font_size is not None:
1✔
691
            this_axis.tick_params(axis='x', labelsize=axis_font_size)
1✔
692
            this_axis.tick_params(axis='y', labelsize=axis_font_size)
1✔
693

694
        char_size = pyspedas.tplot_tools.tplot_opt_glob.get('charsize')
1✔
695
        if char_size is None:
1✔
696
            char_size = 12
1✔
697

698
        if plot_extras.get('char_size') is not None:
1✔
699
            char_size = plot_extras['char_size']
1✔
700

701
        user_set_yrange = yaxis_options.get('y_range_user')
1✔
702
        if user_set_yrange is not None:
1✔
703
            # the user has set the yrange manually
704
            yrange = yaxis_options['y_range']
1✔
705
            if not np.isfinite(yrange[0]):
1✔
706
                yrange[0] = None
×
707
            if not np.isfinite(yrange[1]):
1✔
708
                yrange[1] = None
×
709
            this_axis.set_ylim(yrange)
1✔
710

711
        ymajor_ticks = yaxis_options.get('y_major_ticks')
1✔
712
        if ymajor_ticks is not None:
1✔
713
            this_axis.set_yticks(ymajor_ticks)
1✔
714

715
        yminor_tick_interval = yaxis_options.get('y_minor_tick_interval')
1✔
716
        if yminor_tick_interval is not None and ylog != 'log':
1✔
717
            this_axis.yaxis.set_minor_locator(plt.MultipleLocator(yminor_tick_interval))
1✔
718

719
        if style is None:
1✔
720
            ytitle_color = 'black'
1✔
721
        else:
722
            ytitle_color = None
×
723

724
        if yaxis_options.get('axis_color') is not None:
1✔
725
            ytitle_color = yaxis_options['axis_color']
1✔
726

727
        if xtitle is not None and xtitle != '':
1✔
728
            if xtitle_color is not None:
1✔
729
                this_axis.set_xlabel(xtitle + '\n' + xsubtitle, fontsize=char_size, color=xtitle_color)
1✔
730
            else:
731
                this_axis.set_xlabel(xtitle + '\n' + xsubtitle, fontsize=char_size)
×
732

733
        if ytitle_color is not None:
1✔
734
            this_axis.set_ylabel(ytitle + '\n' + ysubtitle, fontsize=char_size, color=ytitle_color)
1✔
735
        else:
736
            this_axis.set_ylabel(ytitle + '\n' + ysubtitle, fontsize=char_size)
×
737

738
        border = True
1✔
739
        if plot_extras.get('border') is not None:
1✔
740
            border = plot_extras['border']
1✔
741

742
        if border == False:
1✔
743
            this_axis.axis('off')
1✔
744

745
        # axis tick options
746
        if plot_extras.get('xtickcolor') is not None:
1✔
747
            this_axis.tick_params(axis='x', color=plot_extras.get('xtickcolor'))
1✔
748

749
        if plot_extras.get('ytickcolor') is not None:
1✔
750
            this_axis.tick_params(axis='y', color=plot_extras.get('ytickcolor'))
1✔
751

752
        if plot_extras.get('xtick_direction') is not None:
1✔
753
            this_axis.tick_params(axis='x', direction=plot_extras.get('xtick_direction'))
1✔
754

755
        if plot_extras.get('ytick_direction') is not None:
1✔
756
            this_axis.tick_params(axis='y', direction=plot_extras.get('ytick_direction'))
1✔
757

758
        if plot_extras.get('xtick_length') is not None:
1✔
759
            this_axis.tick_params(axis='x', length=plot_extras.get('xtick_length'))
1✔
760

761
        if plot_extras.get('ytick_length') is not None:
1✔
762
            this_axis.tick_params(axis='y', length=plot_extras.get('ytick_length'))
1✔
763

764
        if plot_extras.get('xtick_width') is not None:
1✔
765
            this_axis.tick_params(axis='x', width=plot_extras.get('xtick_width'))
1✔
766

767
        if plot_extras.get('ytick_width') is not None:
1✔
768
            this_axis.tick_params(axis='y', width=plot_extras.get('ytick_width'))
1✔
769

770
        if plot_extras.get('xtick_labelcolor') is not None:
1✔
771
            this_axis.tick_params(axis='x', labelcolor=plot_extras.get('xtick_labelcolor'))
1✔
772

773
        if plot_extras.get('ytick_labelcolor') is not None:
1✔
774
            this_axis.tick_params(axis='y', labelcolor=plot_extras.get('ytick_labelcolor'))
1✔
775

776
        # determine if this is a line plot or a spectrogram
777
        spec = False
1✔
778
        if plot_extras.get('spec') is not None:
1✔
779
            spec = plot_extras['spec']
1✔
780

781
        if spec:
1✔
782
            # create spectrogram plots
783
            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)
1✔
784
            if not plot_created:
1✔
785
                continue
×
786
        else:
787
            # create line plots
788
            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)
1✔
789
            if not plot_created:
1✔
790
                continue
×
791

792
        # apply any vertical/horizontal bars
793
        if pyspedas.tplot_tools.data_quants[variable].attrs['plot_options'].get('time_bar') is not None:
1✔
794
            time_bars = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['time_bar']
1✔
795

796
            for time_bar in time_bars:
1✔
797
                # vertical bars
798
                if time_bar['dimension'] == 'height':
1✔
799
                    this_axis.axvline(x=datetime.fromtimestamp(time_bar['location'], tz=timezone.utc),
1✔
800
                        color=np.array(time_bar.get('line_color'))/256.0, lw=time_bar.get('line_width'),
801
                                      linestyle=time_bar.get('line_dash'))
802

803
                # horizontal bars
804
                if time_bar['dimension'] == 'width':
1✔
805
                    this_axis.axhline(y=time_bar['location'], color=np.array(time_bar.get('line_color'))/256.0,
1✔
806
                                      lw=time_bar.get('line_width'),
807
                                      linestyle=time_bar.get('line_dash'))
808

809
        # highlight time intervals
810
        if pyspedas.tplot_tools.data_quants[variable].attrs['plot_options'].get('highlight_intervals') is not None:
1✔
811
            highlight_intervals = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['highlight_intervals']
1✔
812

813
            for highlight_interval in highlight_intervals:
1✔
814
                hightlight_opts = copy.deepcopy(highlight_interval)
1✔
815
                del hightlight_opts['location']
1✔
816
                if highlight_interval['edgecolor'] is not None or highlight_interval['facecolor'] is not None:
1✔
817
                    del hightlight_opts['color']
×
818

819
                this_axis.axvspan(mdates.date2num(datetime.fromtimestamp(highlight_interval['location'][0], timezone.utc)),
1✔
820
                                  mdates.date2num(datetime.fromtimestamp(highlight_interval['location'][1], timezone.utc)),
821
                                  **hightlight_opts)
822

823
        # add annotations
824
        if pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['extras'].get('annotations') is not None:
1✔
825
            annotations = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['extras']['annotations']
1✔
826
            for annotation in annotations:
1✔
827
                this_axis.annotate(annotation['text'], annotation['position'],
1✔
828
                                   xycoords=annotation['xycoords'],
829
                                   fontsize=annotation['fontsize'],
830
                                   alpha=annotation['alpha'],
831
                                   fontfamily=annotation['fontfamily'],
832
                                   fontvariant=annotation['fontvariant'],
833
                                   fontstyle=annotation['fontstyle'],
834
                                   fontstretch=annotation['fontstretch'],
835
                                   fontweight=annotation['fontweight'],
836
                                   rotation=annotation['rotation'],
837
                                   color=annotation['color'])
838

839
    # apply any addition x-axes (or panel) specified by the var_label keyword
840
    if var_label is not None:
1✔
841
        if varlabel_style is None or varlabel_style.lower() == 'extra_axes':
1✔
842
            varlabels_extra_axes(num_panels, this_axis, var_label, axis_font_size, plot_extras=plot_extras)
1✔
843
        else:
844
            var_label_panel(variables, var_label, axes,  axis_font_size)
1✔
845

846
    # add the color bars to any spectra
847
    for idx, variable in enumerate(variables):
1✔
848
        if pyspedas.tplot_tools.data_quants.get(variable) is None:
1✔
849
            continue
×
850
        plot_extras = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['extras']
1✔
851
        # If we're plotting a pseudovariable with a spec component, the pseudovariable might have its
852
        # own zaxis_options that should override the base variable.  Without this, the pseudovar axis color won't
853
        # get propagated to the ztitle and zsubtitle.
854
        zaxis_options = pyspedas.tplot_tools.data_quants[variable].attrs['plot_options']['zaxis_opt'] | zaxis_options
1✔
855
        if plot_extras.get('spec') is not None:
1✔
856
            spec = plot_extras['spec']
1✔
857
        else:
858
            spec = False
1✔
859

860

861
        if spec and show_colorbar:
1✔
862
            if colorbars.get(variable) is None:
1✔
863
                continue
×
864

865
            if num_panels == 1:
1✔
866
                this_axis = axes
1✔
867
            else:
868
                this_axis = axes[idx]
1✔
869

870
            colorbar = fig.colorbar(colorbars[variable]['im'], ax=this_axis)
1✔
871

872
            if style is None:
1✔
873
                ztitle_color = 'black'
1✔
874
            else:
875
                ztitle_color = None
×
876

877
            if zaxis_options is None:
1✔
878
                continue
×
879

880
            if zaxis_options.get('axis_color') is not None:
1✔
881
                ztitle_color = zaxis_options['axis_color']
1✔
882

883
            ztitle_text = colorbars[variable]['ztitle']
1✔
884
            zsubtitle_text = colorbars[variable]['zsubtitle']
1✔
885

886
            # replace some common superscripts
887
            ztitle_text = replace_common_exp(ztitle_text)
1✔
888
            zsubtitle_text = replace_common_exp(zsubtitle_text)
1✔
889

890
            if ztitle_color is not None:
1✔
891
                colorbar.set_label(ztitle_text + '\n ' + zsubtitle_text,
1✔
892
                                   color=ztitle_color, fontsize=char_size)
893
            else:
894
                colorbar.set_label(ztitle_text + '\n ' + zsubtitle_text,
×
895
                                   fontsize=char_size)
896

897
    # plt.tight_layout()
898
    fig.canvas.draw()
1✔
899

900
    save_plot(save_png=save_png, save_eps=save_eps, save_jpeg=save_jpeg, save_pdf=save_pdf, save_svg=save_svg, dpi=dpi)
1✔
901

902
    if slice:
1✔
903
        slice_fig, slice_axes = plt.subplots(nrows=1)
×
904
        slice_plot, = slice_axes.plot([0], [0])
×
905
        mouse_event_func = lambda event: mouse_move_slice(event, slice_axes, slice_plot)
×
906
        cid = fig.canvas.mpl_connect('motion_notify_event', mouse_event_func)
×
907

908
    if display:
1✔
909
        plt.show()
1✔
910

911
    if return_plot_objects:
1✔
912
        return fig, axes
1✔
913

914

915
def varlabels_extra_axes(num_panels, this_axis, var_label, axis_font_size, plot_extras):
1✔
916
    # apply any addition x-axes specified by the var_label keyword
917
    if var_label is not None:
1✔
918
        if not isinstance(var_label, list):
1✔
919
            var_label = [var_label]
×
920

921
        char_size = pyspedas.tplot_tools.tplot_opt_glob.get('charsize')
1✔
922
        if char_size is None:
1✔
923
            char_size = 12
1✔
924

925
        if plot_extras.get('char_size') is not None:
1✔
926
            char_size = plot_extras['char_size']
×
927

928
        axis_delta = 0.0
1✔
929

930
        for label in var_label:
1✔
931
            if isinstance(label, int):
1✔
932
                label = tname_byindex(label)
×
933
            label_data = get_data(label, xarray=True, dt=True)
1✔
934

935
            if label_data is None:
1✔
936
                logging.info('Variable not found: ' + label)
×
937
                continue
×
938

939
            if len(label_data.values.shape) != 1:
1✔
940
                logging.info(
×
941
                    label + ' specified as a vector; var_label only supports scalars. Try splitting the vector into seperate tplot variables.')
942
                continue
×
943

944
            # set up the new x-axis
945
            axis_delta = axis_delta - num_panels * 0.1
1✔
946
            new_xaxis = this_axis.secondary_xaxis(axis_delta)
1✔
947
            if axis_font_size is not None:
1✔
948
                new_xaxis.tick_params(axis='x', labelsize=axis_font_size)
1✔
949
                new_xaxis.tick_params(axis='y', labelsize=axis_font_size)
1✔
950

951
            xaxis_ticks = this_axis.get_xticks().tolist()
1✔
952
            xaxis_ticks_dt = [np.datetime64(mpl.dates.num2date(tick_val).replace(tzinfo=None).isoformat(), 'ns') for
1✔
953
                              tick_val in xaxis_ticks]
954
            # xaxis_ticks_unix = [tick_val.timestamp() for tick_val in xaxis_ticks_dt]
955
            xaxis_labels = get_var_label_ticks(label_data, xaxis_ticks_dt)
1✔
956
            new_xaxis.set_xticks(xaxis_ticks_dt)
1✔
957
            new_xaxis.set_xticklabels(xaxis_labels)
1✔
958
            ytitle = pyspedas.tplot_tools.data_quants[label].attrs['plot_options']['yaxis_opt']['axis_label']
1✔
959
            new_xaxis.set_xlabel(ytitle, fontsize=char_size)
1✔
960

961
        # fig.subplots_adjust(bottom=0.05+len(var_label)*0.1)
962

963

964
def mouse_move_slice(event, slice_axes, slice_plot):
1✔
965
    """
966
    This function is called when the mouse moves over an axis
967
    and the slice keyword is set to True; for spectra figures, it
968
    updates the slice plot based on the mouse location
969
    """
970
    if event.inaxes is None:
×
971
        return
×
972

973
    # check for a spectrogram
974
    try:
×
975
        data = get_data(event.inaxes.var_name)
×
976
    except AttributeError:
×
977
        return
×
978

979
    if data is None:
×
980
        return
×
981

982
    if len(data) != 3:
×
983
        return
×
984

985
    slice_time = mdates.num2date(event.xdata).timestamp()
×
986
    idx = np.abs(data.times-slice_time).argmin()
×
987

988
    if len(data.v.shape) > 1:
×
989
        # time varying y-axis
990
        vdata = data.v[idx, :]
×
991
    else:
992
        vdata = data.v
×
993

994
    xaxis_options = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options']['xaxis_opt']
×
995
    yaxis_options = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options']['yaxis_opt']
×
996
    zaxis_options = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options']['zaxis_opt']
×
997

998
    yrange = yaxis_options.get('y_range')
×
999
    if yrange is None:
×
1000
        yrange = [np.nanmin(vdata), np.nanmax(vdata)]
×
1001

1002
    zrange = zaxis_options.get('z_range')
×
1003
    if zrange is None:
×
1004
        zrange = [np.nanmin(data.y), np.nanmax(data.y)]
×
1005

1006
    y_label = zaxis_options.get('axis_label')
×
1007
    if y_label is not None:
×
1008
        slice_axes.set_ylabel(y_label)
×
1009

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

1012
    x_label = yaxis_options.get('axis_label')
×
1013
    if x_label is not None:
×
1014
        title = x_label + ' (' + title + ')'
×
1015

1016
    slice_axes.set_title(title)
×
1017

1018
    x_subtitle = yaxis_options.get('axis_subtitle')
×
1019
    if x_subtitle is not None:
×
1020
        slice_axes.set_xlabel(x_subtitle)
×
1021

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

1024
    xscale = None
×
1025
    yscale = None
×
1026

1027
    if slice_yaxis_opt is not None:
×
1028
        xscale = slice_yaxis_opt.get('xi_axis_type')
×
1029
        yscale = slice_yaxis_opt.get('yi_axis_type')
×
1030

1031
    if yscale is None:
×
1032
        # if the user didn't explicitly set the ylog_slice option,
1033
        # use the option from the plot
1034
        yscale = zaxis_options.get('z_axis_type')
×
1035
        if yscale is None:
×
1036
            yscale = 'linear'
×
1037

1038
    if xscale is None:
×
1039
        # if the user didn't explicitly set the xlog_slice option,
1040
        # use the option from the plot
1041
        xscale = yaxis_options.get('y_axis_type')
×
1042
        if xscale is None:
×
1043
            xscale = 'linear'
×
1044

1045
    if yscale == 'log' and zrange[0] == 0.0:
×
1046
        zrange[0] = np.nanmin(data.y[idx, :])
×
1047

1048
    slice_plot.set_data(vdata, data.y[idx, :])
×
1049
    slice_axes.set_ylim(zrange)
×
1050
    slice_axes.set_xlim(yrange)
×
1051
    slice_axes.set_xscale(xscale)
×
1052
    slice_axes.set_yscale(yscale)
×
1053

1054
    try:
×
1055
        plt.draw()
×
1056
    except ValueError:
×
1057
        return
×
1058
    sleep(0.01)
×
1059

1060
def replace_common_exp(title):
1✔
1061
    if hasattr(title, 'decode'):
1✔
1062
        title = title.decode('utf-8')
1✔
1063
    if '$' in title:
1✔
1064
        return title
1✔
1065
    if '^' not in title:
1✔
1066
        return title
1✔
1067
    exp = False
1✔
1068
    title_out = ''
1✔
1069
    for char in title:
1✔
1070
        if char == '^':
1✔
1071
            exp = True
1✔
1072
            title_out += '$^{'
1✔
1073
            continue
1✔
1074
        else:
1075
            if exp:
1✔
1076
                if not char.isalnum():
1✔
1077
                    title_out += '}$' + char
1✔
1078
                    exp = False
1✔
1079
                    continue
1✔
1080
        title_out += char
1✔
1081
    if exp:
1✔
1082
        title_out += '}$'
×
1083
    return title_out
1✔
1084

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