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

spedas / pyspedas / 25194239086

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

push

github

jameswilburlewis
Added test for loading Cluster CODIF differential energy flux

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

19460 existing lines in 418 files now uncovered.

30204 of 48955 relevant lines covered (61.7%)

1.44 hits per line

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

64.74
/pyspedas/tplot_tools/MPLPlotter/lineplot.py
1
import numpy as np
3✔
2
import pyspedas
3✔
3
import logging
3✔
4

5

6
def lineplot(var_data,
3✔
7
             var_times,
8
             this_axis,
9
             line_opts,
10
             yaxis_options,
11
             plot_extras,
12
             running_trace_count=None,
13
             time_idxs=None,
14
             style=None,
15
             var_metadata=None):
16
    """
17
    Generate a matplotlib line plot from a tplot variable
18

19
    Parameters
20
    ----------
21
        var_data: dict
22
            The data to be plotted (may have multiple traces)
23
        var_times:
24
            Array of datetime objects to use for x axis
25
        this_axis:
26
            The current axis (plot panel) we're working with
27
        line_opts: dict
28
            A dictionary of line options
29
        yaxis_options: dict
30
            A dictionary of y axis options
31
        plot_extras: dict
32
            A dictionary of 'extra' options (colors, etc)
33
        running_trace_count:
34
            If not Null, an integer representing the number of traces already processed in this pseudovariable. Defaults to None.
35
        time_idxs: np.ndarray
36
            If provided, an integer array specifying the subset of time indices to be plotted. Defaults to None.
37
        style:
38
            A matplotlib style to be used in the plot. Defaults to None.
39
        var_metadata: dict
40
            The metadata dictionary associated with this tplot variable (used as a fallback for trace labels). Defaults to None.
41

42
    Returns
43
    -------
44
        True
45

46
    """
47
    alpha = plot_extras.get('alpha')
3✔
48

49
    if len(var_data.y.shape) == 1:
3✔
50
        num_lines = 1
3✔
51
    else:
52
        num_lines = var_data.y.shape[1]
3✔
53

54
    is_errorbar_plot = False
3✔
55
    if 'dy' in var_data._fields:
3✔
UNCOV
56
        is_errorbar_plot = True
×
57

58
    if yaxis_options.get('legend_names') is not None:
3✔
59
        labels = yaxis_options['legend_names']
3✔
60
        labels = get_trace_options(labels, running_trace_count, num_lines)
3✔
61

62
        if labels[0] is None:
3✔
63
            labels = None
×
64
    else:
65
        labels = None
3✔
66
        if var_metadata.get('CDF') is not None:
3✔
UNCOV
67
            labels = var_metadata['CDF'].get('LABELS')
1✔
68

69
    legend_location = yaxis_options.get('legend_location')
3✔
70

71
    bbox_to_anchor = None
3✔
72
    if legend_location is not None:
3✔
UNCOV
73
        if legend_location == 'spedas':
1✔
74
            # the spedas legend puts the legend on the outside of the panel
75
            # to the right of the panel (just like in IDL)
UNCOV
76
            legend_location = 'center left'
1✔
UNCOV
77
            bbox_to_anchor = (1.04, 0.5)
1✔
78
    else:
79
        legend_location = 'upper right'
3✔
80

81
    legend_size = yaxis_options.get('legend_size')
3✔
82
    legend_shadow = yaxis_options.get('legend_shadow')
3✔
83
    legend_title = yaxis_options.get('legend_title')
3✔
84
    legend_titlesize = yaxis_options.get('legend_titlesize')
3✔
85
    legend_color = yaxis_options.get('legend_color')
3✔
86
    legend_markerfirst = yaxis_options.get('legend_markerfirst')
3✔
87
    legend_markerscale = yaxis_options.get('legend_markerscale')
3✔
88
    legend_linewidth = yaxis_options.get('legend_linewidth')
3✔
89
    legend_edgecolor = yaxis_options.get('legend_edgecolor')
3✔
90
    legend_facecolor = yaxis_options.get('legend_facecolor')
3✔
91
    legend_frameon = yaxis_options.get('legend_frameon')
3✔
92
    legend_ncols = yaxis_options.get('legend_ncols')
3✔
93
    if legend_ncols is None:
3✔
94
        legend_ncols = 1
3✔
95

96
    if legend_linewidth is None:
3✔
97
        legend_linewidth = 4
3✔
98

99
    if legend_size is None:
3✔
100
        legend_size = pyspedas.tplot_tools.tplot_opt_glob.get('charsize')
3✔
101

102
    markers = None
3✔
103
    if line_opts.get('marker') is not None:
3✔
UNCOV
104
        markers = line_opts['marker']
×
UNCOV
105
        markers = get_trace_options(markers, running_trace_count, num_lines, repeat=True)
×
106

107
    colors = None
3✔
108
    if plot_extras.get('line_color') is not None:
3✔
109
        colors = plot_extras['line_color']
3✔
110
    else:
111
        if style is None:
3✔
112
            if num_lines == 1:
3✔
113
                colors = ['k']
3✔
114
            elif num_lines == 2:
3✔
UNCOV
115
                colors = ['r', 'g']
1✔
116
            elif num_lines == 3:
3✔
117
                colors = ['b', 'g', 'r']
3✔
UNCOV
118
            elif num_lines == 4:
2✔
UNCOV
119
                colors = ['b', 'g', 'r', 'k']
2✔
120
            else:
UNCOV
121
                colors = ['k', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9']
2✔
122
    colors = get_trace_options(colors, running_trace_count, num_lines, repeat=True)
3✔
123

124
    # line thickness
125
    if line_opts.get('line_width') is not None:
3✔
126
        thick = line_opts['line_width']
×
127
    else:
128
        thick = [0.5]
3✔
129
    thick = get_trace_options(thick, running_trace_count, num_lines, repeat=True)
3✔
130

131
    # line style
132
    if line_opts.get('line_style_name') is not None:
3✔
UNCOV
133
        line_style_user = line_opts['line_style_name']
×
134

135
        # line_style_user should already be a list
136
        # handle legacy values
UNCOV
137
        line_style = []
×
UNCOV
138
        for linestyle in line_style_user:
×
UNCOV
139
            if linestyle == 'solid_line':
×
140
                line_style.append('solid')
×
UNCOV
141
            elif linestyle == 'dot':
×
142
                line_style.append('dotted')
×
UNCOV
143
            elif linestyle == 'dash':
×
144
                line_style.append('dashed')
×
UNCOV
145
            elif linestyle == 'dash_dot':
×
146
                line_style.append('dashdot')
×
147
            else:
UNCOV
148
                line_style.append(linestyle)
×
149
    else:
150
        line_style = ['solid']
3✔
151
    line_style = get_trace_options(line_style, running_trace_count, num_lines, repeat=True)
3✔
152

153
    symbols = False
3✔
154
    if line_opts.get('symbols') is not None:
3✔
UNCOV
155
        if line_opts['symbols']:
×
UNCOV
156
            symbols = True
×
157

158
    # create the plot
159
    line_options = {'alpha': alpha}
3✔
160

161
    marker_every = None
3✔
162
    if line_opts.get('markevery') is not None:
3✔
UNCOV
163
        marker_every = line_opts['markevery']
×
UNCOV
164
        marker_every = get_trace_options(marker_every, running_trace_count, num_lines, repeat=True)
×
165

166
    marker_sizes = None
3✔
167
    if line_opts.get('marker_size') is not None:
3✔
UNCOV
168
        marker_sizes = line_opts['marker_size']
×
UNCOV
169
        marker_sizes = get_trace_options(marker_sizes, running_trace_count, num_lines, repeat=True)
×
170

171
    # check for error data first
172
    if is_errorbar_plot:
3✔
173
        # error data provided
UNCOV
174
        line_options['yerr'] = var_data.dy[time_idxs]
×
UNCOV
175
        plotter = this_axis.errorbar
×
UNCOV
176
        if line_opts.get('ecolor') is not None:
×
UNCOV
177
            line_options['ecolor'] = line_opts['ecolor']
×
UNCOV
178
        if line_opts.get('elinewidth') is not None:
×
UNCOV
179
            line_options['elinewidth'] = line_opts['elinewidth']
×
UNCOV
180
        if line_opts.get('errorevery') is not None:
×
UNCOV
181
            line_options['errorevery'] = line_opts['errorevery']
×
UNCOV
182
        if line_opts.get('capsize') is not None:
×
UNCOV
183
            line_options['capsize'] = line_opts['capsize']
×
184
    else:
185
        # no error data provided
186
        plotter = this_axis.plot
3✔
187
        # Note: to turn off connecting lines in an error bar plot, do not use the
188
        # 'symbols' option.  Instead, set the line_options metadata to 'None' (as a string).
189
        if symbols:
3✔
UNCOV
190
            plotter = this_axis.scatter
×
191

192
    for line in range(0, num_lines):
3✔
193
        if colors is not None:
3✔
194
            color = colors[line]
3✔
195
        else:
196
            color = None
×
197

198
        if markers is not None:
3✔
UNCOV
199
            marker = markers[line]
×
200
        else:
201
            marker = None
3✔
202

203
        if marker_sizes is not None:
3✔
204
            # Note: scaling of marker sizes in scatter plots and line plots is different!
205
            # For line plot and scatter plot marker sizes to match, the line plot
206
            # marker size should be the square root of the scatter plot marker size.
207
            # Maybe that should be enforced here....???
208

UNCOV
209
            if symbols:
×
UNCOV
210
                line_options['s'] = marker_sizes[line]
×
211
            else:
UNCOV
212
                line_options['markersize'] = marker_sizes[line]
×
213

214
        if symbols:
3✔
UNCOV
215
            this_line_style='None'
×
216
        else:
217
            this_line_style=line_style[line]
3✔
218

219
        if marker_every is not None:
3✔
UNCOV
220
            line_options['markevery'] = marker_every[line]
×
221

222
        this_line = plotter(var_times, var_data.y[time_idxs] if num_lines == 1 else var_data.y[time_idxs, line], color=color,
3✔
223
                            linestyle=this_line_style, linewidth=thick[line], marker=marker, **line_options)
224

225
        if labels is not None:
3✔
226
            try:
3✔
227
                if isinstance(this_line, list):
3✔
228
                    this_line[0].set_label(labels[line])
3✔
229
                else:
UNCOV
230
                    this_line.set_label(labels[line])
×
UNCOV
231
            except IndexError:
×
UNCOV
232
                continue
×
233

234
    if labels is not None:
3✔
235
        legend = this_axis.legend(loc=legend_location, fontsize=legend_size, shadow=legend_shadow, title=legend_title,
3✔
236
                         labelcolor=legend_color, markerfirst=legend_markerfirst, markerscale=legend_markerscale,
237
                         facecolor=legend_facecolor, edgecolor=legend_edgecolor, frameon=legend_frameon, ncols=legend_ncols,
238
                         title_fontsize=legend_titlesize, bbox_to_anchor=bbox_to_anchor)
239
        try:
3✔
240
            handles = legend.legend_handles
3✔
241
        except AttributeError:
×
242
            handles = legend.legendHandles
×
243
        for legobj in handles:
3✔
244
            legobj.set_linewidth(legend_linewidth)
3✔
245

246
    return True
3✔
247

248
def get_trace_options(parent_array, start_trace=None, num_traces=1, repeat=False, fill=False, fillval=None):
3✔
249
    """ Get options for a set of traces from a parent array, extending or slicing as necessary to handle pseudovariable options
250

251
    Parameters
252
    -----------
253
        parent_array: str or list of str
254
            An array of option values to select from
255
        start_trace: int
256
            If set, we are processing a pseuodovariable, and this is the count of line traces processed so far for
257
            previous sub-variables in the current pseuodovariable.
258

259
            If parent_array is long enough (e.g. if set on the pseudovariable and intended to cover the complete
260
            set of traces), we take a slice from start_trace with num_traces entries.  If it matches
261
            the number of traces requested (e.g. parent array comes from this subvariable), we take it as-is.
262
            If there are fewer entries than num_traces (e.g. a single value intended to apply to all traces),
263
            we repeat parent_array or add fill until there are enough values to take a slice from start_trace.
264

265
            If None, we're just processing a regular variable, so we take values starting at zero (extending or filling
266
            as necessary)
267
            Default: None
268
        num_traces: int
269
            The number of traces in the current (sub-)variable.
270
        repeat: bool
271
            If True, extend the parent array by repetition if necessary. Defaults to False.
272
        fill: bool
273
            If True, extend the parent array by adding fill values. Defaults to False.
274
        fillval: Any
275
            If fill=True, values to append to parent_array to make enough entries. Defaults to None.
276

277
    Returns:
278
    --------
279
        list of option values with num_traces entries
280

281
    """
282
    if not isinstance(parent_array,list):
3✔
UNCOV
283
        parent_array=[parent_array]
×
284
    parent_length = len(parent_array)
3✔
285
    output_array = parent_array
3✔
286
    if start_trace is not None:
3✔
UNCOV
287
        end_trace = start_trace + num_traces
1✔
UNCOV
288
        if parent_length >= start_trace+num_traces:
1✔
UNCOV
289
            output_array = parent_array[start_trace:end_trace]
1✔
UNCOV
290
        elif parent_length == num_traces:
1✔
UNCOV
291
            output_array = parent_array
1✔
292
        else:
UNCOV
293
            if repeat:
×
UNCOV
294
                expansion_factor = int((end_trace/parent_length + 1))
×
UNCOV
295
                expanded_array = np.tile(parent_array,expansion_factor)
×
UNCOV
296
                output_array = expanded_array[start_trace:end_trace]
×
297
            elif fill:
×
298
                output_array = parent_array
×
299
                missing=num_traces-parent_length
×
300
                output_array.extend(np.tile([fillval],missing))
×
301
            else:
302
                logging.warning("Length of trace options (%d) smaller than number of traces (%d)",parent_length, num_traces)
×
303
    else:
304
        if len(parent_array) >= num_traces:
3✔
305
            output_array = parent_array[0:num_traces]
3✔
306
        else:
307
            if repeat:
3✔
308
                expansion_factor = int(num_traces/parent_length + 1)
3✔
309
                expanded_array = np.tile(parent_array,expansion_factor)
3✔
310
                output_array = expanded_array[0:num_traces]
3✔
311
            elif fill:
×
312
                output_array = parent_array
×
313
                missing = num_traces - parent_length
×
314
                output_array.extend(np.tile([fillval], missing))
×
315
            else:
316
                logging.warning("Length of trace options (%d) smaller than number of traces (%d)",parent_length, num_traces)
×
317
    return output_array
3✔
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