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

spedas / pyspedas / 18149032752

01 Oct 2025 02:10AM UTC coverage: 89.455% (+2.0%) from 87.427%
18149032752

push

github

jameswilburlewis
Update lock file after changing pyproject.toml

40549 of 45329 relevant lines covered (89.45%)

0.89 hits per line

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

74.53
/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

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

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

27

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

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

41
    """
42

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

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

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

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

68
    # ylog is now guaranteed to be boolean
69

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

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

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

118

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

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

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

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

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

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

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

168

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

192

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

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

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

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

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

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

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

319

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

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

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

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

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

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

391

392
    axis_font_size = pyspedas.tplot_tools.tplot_opt_glob.get('axis_font_size')
1✔
393

394
    colorbars = {}
1✔
395

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

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

409
        var_data = copy.deepcopy(var_data_org)
1✔
410

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

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

422
        pseudo_var = False
1✔
423
        overplots = None
1✔
424
        spec = False
1✔
425

426
        var_quants = pyspedas.tplot_tools.data_quants[variable]
1✔
427

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

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

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

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

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

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

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

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

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

548
            continue
1✔
549

550

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

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

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

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

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

597
        var_times = var_data_times
1✔
598

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

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

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

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

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

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

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

635

636
        xsubtitle = ''
1✔
637
        if xaxis_options.get('axis_subtitle') is not None:
1✔
638
            xsubtitle = xaxis_options['axis_subtitle']
1✔
639

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

645
        if xaxis_options.get('axis_color') is not None:
1✔
646
            xtitle_color = xaxis_options['axis_color']
1✔
647

648
        ylog = yaxis_options['y_axis_type']
1✔
649

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

655
        ytitle = yaxis_options['axis_label']
1✔
656
        if ytitle == '':
1✔
657
            ytitle = variable
1✔
658

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

663
        # replace some common superscripts
664
        ysubtitle = replace_common_exp(ysubtitle)
1✔
665

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

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

674
        if plot_extras.get('char_size') is not None:
1✔
675
            char_size = plot_extras['char_size']
1✔
676

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

687
        ymajor_ticks = yaxis_options.get('y_major_ticks')
1✔
688
        if ymajor_ticks is not None:
1✔
689
            this_axis.set_yticks(ymajor_ticks)
1✔
690

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

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

700
        if yaxis_options.get('axis_color') is not None:
1✔
701
            ytitle_color = yaxis_options['axis_color']
1✔
702

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

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

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

718
        if border == False:
1✔
719
            this_axis.axis('off')
1✔
720

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

725
        if plot_extras.get('ytickcolor') is not None:
1✔
726
            this_axis.tick_params(axis='y', color=plot_extras.get('ytickcolor'))
1✔
727

728
        if plot_extras.get('xtick_direction') is not None:
1✔
729
            this_axis.tick_params(axis='x', direction=plot_extras.get('xtick_direction'))
1✔
730

731
        if plot_extras.get('ytick_direction') is not None:
1✔
732
            this_axis.tick_params(axis='y', direction=plot_extras.get('ytick_direction'))
1✔
733

734
        if plot_extras.get('xtick_length') is not None:
1✔
735
            this_axis.tick_params(axis='x', length=plot_extras.get('xtick_length'))
1✔
736

737
        if plot_extras.get('ytick_length') is not None:
1✔
738
            this_axis.tick_params(axis='y', length=plot_extras.get('ytick_length'))
1✔
739

740
        if plot_extras.get('xtick_width') is not None:
1✔
741
            this_axis.tick_params(axis='x', width=plot_extras.get('xtick_width'))
1✔
742

743
        if plot_extras.get('ytick_width') is not None:
1✔
744
            this_axis.tick_params(axis='y', width=plot_extras.get('ytick_width'))
1✔
745

746
        if plot_extras.get('xtick_labelcolor') is not None:
1✔
747
            this_axis.tick_params(axis='x', labelcolor=plot_extras.get('xtick_labelcolor'))
1✔
748

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

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

757
        if spec:
1✔
758
            # create spectrogram plots
759
            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✔
760
            if not plot_created:
1✔
761
                continue
×
762
        else:
763
            # create line plots
764
            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✔
765
            if not plot_created:
1✔
766
                continue
×
767

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

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

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

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

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

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

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

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

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

833

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

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

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

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

850
            if zaxis_options is None:
1✔
851
                continue
×
852

853
            if zaxis_options.get('axis_color') is not None:
1✔
854
                ztitle_color = zaxis_options['axis_color']
1✔
855

856
            ztitle_text = colorbars[variable]['ztitle']
1✔
857
            zsubtitle_text = colorbars[variable]['zsubtitle']
1✔
858

859
            # replace some common superscripts
860
            ztitle_text = replace_common_exp(ztitle_text)
1✔
861
            zsubtitle_text = replace_common_exp(zsubtitle_text)
1✔
862

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

870
    # plt.tight_layout()
871
    fig.canvas.draw()
1✔
872

873
    if save_png is not None and save_png != '':
1✔
874
        if not save_png.endswith('.png'):
1✔
875
            save_png += '.png'
1✔
876
        plt.savefig(save_png, dpi=dpi)
1✔
877

878
    if save_eps is not None and save_eps != '':
1✔
879
        if not save_eps.endswith('.eps'):
×
880
            save_eps += '.eps'
×
881
        plt.savefig(save_eps, dpi=dpi)
×
882

883
    if save_svg is not None and save_svg != '':
1✔
884
        if not save_svg.endswith('.svg'):
×
885
            save_svg += '.svg'
×
886
        plt.savefig(save_svg, dpi=dpi)
×
887

888
    if save_pdf is not None and save_pdf != '':
1✔
889
        if not save_pdf.endswith('.pdf'):
×
890
            save_pdf += '.pdf'
×
891
        plt.savefig(save_pdf, dpi=dpi)
×
892

893
    if save_jpeg is not None and save_jpeg != '':
1✔
894
        if not save_jpeg.endswith('.jpeg'):
×
895
            save_jpeg += '.jpeg'
×
896
        plt.savefig(save_jpeg, dpi=dpi)
×
897

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

904
    if display:
1✔
905
        plt.show()
1✔
906

907
    if return_plot_objects:
1✔
908
        return fig, axes
1✔
909

910

911
def varlabels_extra_axes(num_panels, this_axis, var_label):
1✔
912
    # apply any addition x-axes specified by the var_label keyword
913
    if var_label is not None:
1✔
914
        if not isinstance(var_label, list):
1✔
915
            var_label = [var_label]
×
916

917
        axis_delta = 0.0
1✔
918

919
        for label in var_label:
1✔
920
            if isinstance(label, int):
1✔
921
                label = tname_byindex(label)
×
922
            label_data = get_data(label, xarray=True, dt=True)
1✔
923

924
            if label_data is None:
1✔
925
                logging.info('Variable not found: ' + label)
×
926
                continue
×
927

928
            if len(label_data.values.shape) != 1:
1✔
929
                logging.info(
×
930
                    label + ' specified as a vector; var_label only supports scalars. Try splitting the vector into seperate tplot variables.')
931
                continue
×
932

933
            # set up the new x-axis
934
            axis_delta = axis_delta - num_panels * 0.1
1✔
935
            new_xaxis = this_axis.secondary_xaxis(axis_delta)
1✔
936
            xaxis_ticks = this_axis.get_xticks().tolist()
1✔
937
            xaxis_ticks_dt = [np.datetime64(mpl.dates.num2date(tick_val).replace(tzinfo=None).isoformat(), 'ns') for
1✔
938
                              tick_val in xaxis_ticks]
939
            # xaxis_ticks_unix = [tick_val.timestamp() for tick_val in xaxis_ticks_dt]
940
            xaxis_labels = get_var_label_ticks(label_data, xaxis_ticks_dt)
1✔
941
            new_xaxis.set_xticks(xaxis_ticks_dt)
1✔
942
            new_xaxis.set_xticklabels(xaxis_labels)
1✔
943
            ytitle = pyspedas.tplot_tools.data_quants[label].attrs['plot_options']['yaxis_opt']['axis_label']
1✔
944
            new_xaxis.set_xlabel(ytitle)
1✔
945

946
        # fig.subplots_adjust(bottom=0.05+len(var_label)*0.1)
947

948

949
def mouse_move_slice(event, slice_axes, slice_plot):
1✔
950
    """
951
    This function is called when the mouse moves over an axis
952
    and the slice keyword is set to True; for spectra figures, it
953
    updates the slice plot based on the mouse location
954
    """
955
    if event.inaxes is None:
×
956
        return
×
957

958
    # check for a spectrogram
959
    try:
×
960
        data = get_data(event.inaxes.var_name)
×
961
    except AttributeError:
×
962
        return
×
963

964
    if data is None:
×
965
        return
×
966

967
    if len(data) != 3:
×
968
        return
×
969

970
    slice_time = mdates.num2date(event.xdata).timestamp()
×
971
    idx = np.abs(data.times-slice_time).argmin()
×
972

973
    if len(data.v.shape) > 1:
×
974
        # time varying y-axis
975
        vdata = data.v[idx, :]
×
976
    else:
977
        vdata = data.v
×
978

979
    xaxis_options = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options']['xaxis_opt']
×
980
    yaxis_options = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options']['yaxis_opt']
×
981
    zaxis_options = pyspedas.tplot_tools.data_quants[event.inaxes.var_name].attrs['plot_options']['zaxis_opt']
×
982

983
    yrange = yaxis_options.get('y_range')
×
984
    if yrange is None:
×
985
        yrange = [np.nanmin(vdata), np.nanmax(vdata)]
×
986

987
    zrange = zaxis_options.get('z_range')
×
988
    if zrange is None:
×
989
        zrange = [np.nanmin(data.y), np.nanmax(data.y)]
×
990

991
    y_label = zaxis_options.get('axis_label')
×
992
    if y_label is not None:
×
993
        slice_axes.set_ylabel(y_label)
×
994

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

997
    x_label = yaxis_options.get('axis_label')
×
998
    if x_label is not None:
×
999
        title = x_label + ' (' + title + ')'
×
1000

1001
    slice_axes.set_title(title)
×
1002

1003
    x_subtitle = yaxis_options.get('axis_subtitle')
×
1004
    if x_subtitle is not None:
×
1005
        slice_axes.set_xlabel(x_subtitle)
×
1006

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

1009
    xscale = None
×
1010
    yscale = None
×
1011

1012
    if slice_yaxis_opt is not None:
×
1013
        xscale = slice_yaxis_opt.get('xi_axis_type')
×
1014
        yscale = slice_yaxis_opt.get('yi_axis_type')
×
1015

1016
    if yscale is None:
×
1017
        # if the user didn't explicitly set the ylog_slice option,
1018
        # use the option from the plot
1019
        yscale = zaxis_options.get('z_axis_type')
×
1020
        if yscale is None:
×
1021
            yscale = 'linear'
×
1022

1023
    if xscale is None:
×
1024
        # if the user didn't explicitly set the xlog_slice option,
1025
        # use the option from the plot
1026
        xscale = yaxis_options.get('y_axis_type')
×
1027
        if xscale is None:
×
1028
            xscale = 'linear'
×
1029

1030
    if yscale == 'log' and zrange[0] == 0.0:
×
1031
        zrange[0] = np.nanmin(data.y[idx, :])
×
1032

1033
    slice_plot.set_data(vdata, data.y[idx, :])
×
1034
    slice_axes.set_ylim(zrange)
×
1035
    slice_axes.set_xlim(yrange)
×
1036
    slice_axes.set_xscale(xscale)
×
1037
    slice_axes.set_yscale(yscale)
×
1038

1039
    try:
×
1040
        plt.draw()
×
1041
    except ValueError:
×
1042
        return
×
1043
    sleep(0.01)
×
1044

1045
def replace_common_exp(title):
1✔
1046
    if hasattr(title, 'decode'):
1✔
1047
        title = title.decode('utf-8')
1✔
1048
    if '$' in title:
1✔
1049
        return title
1✔
1050
    if '^' not in title:
1✔
1051
        return title
1✔
1052
    exp = False
1✔
1053
    title_out = ''
1✔
1054
    for char in title:
1✔
1055
        if char == '^':
1✔
1056
            exp = True
1✔
1057
            title_out += '$^{'
1✔
1058
            continue
1✔
1059
        else:
1060
            if exp:
1✔
1061
                if not char.isalnum():
1✔
1062
                    title_out += '}$' + char
1✔
1063
                    exp = False
1✔
1064
                    continue
1✔
1065
        title_out += char
1✔
1066
    if exp:
1✔
1067
        title_out += '}$'
×
1068
    return title_out
1✔
1069

1070

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