Completed
Pull Request — master (#378)
by
unknown
01:29
created

SkewT.fill_betweenx()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
1
# Copyright (c) 2008-2015 MetPy Developers.
2
# Distributed under the terms of the BSD 3-Clause License.
3
# SPDX-License-Identifier: BSD-3-Clause
4
"""Make Skew-T Log-P based plots.
5
6
Contain tools for making Skew-T Log-P plots, including the base plotting class,
7
`SkewT`, as well as a class for making a `Hodograph`.
8
"""
9
10
from matplotlib.axes import Axes
11
import matplotlib.axis as maxis
12
from matplotlib.collections import LineCollection
13
from matplotlib.patches import Circle
14
from matplotlib.projections import register_projection
15
import matplotlib.spines as mspines
16
from matplotlib.ticker import MultipleLocator, NullFormatter, ScalarFormatter
17
import matplotlib.transforms as transforms
18
import numpy as np
19
20
from ._util import colored_line, delete_masked_points
21
from ..calc import dewpoint, dry_lapse, moist_lapse, vapor_pressure
22
from ..package_tools import Exporter
23
from ..units import units
24
25
exporter = Exporter(globals())
26
27
28
class SkewXTick(maxis.XTick):
29
    r"""Make x-axis ticks for Skew-T plots.
30
31
    This class adds to the standard :class:`matplotlib.axis.XTick` dynamic checking
32
    for whether a top or bottom tick is actually within the data limits at that part
33
    and draw as appropriate. It also performs similar checking for gridlines.
34
    """
35
36
    def update_position(self, loc):
37
        """Set the location of tick in data coords with scalar *loc*."""
38
        # This ensures that the new value of the location is set before
39
        # any other updates take place.
40
        self._loc = loc
41
        super(SkewXTick, self).update_position(loc)
42
43
    def _has_default_loc(self):
44
        return self.get_loc() is None
45
46
    def _need_lower(self):
47
        return (self._has_default_loc() or
48
                transforms.interval_contains(self.axes.lower_xlim,
49
                                             self.get_loc()))
50
51
    def _need_upper(self):
52
        return (self._has_default_loc() or
53
                transforms.interval_contains(self.axes.upper_xlim,
54
                                             self.get_loc()))
55
56
    @property
57
    def gridOn(self):  # noqa: N802
58
        """Control whether the gridline is drawn for this tick."""
59
        return (self._gridOn and (self._has_default_loc() or
60
                transforms.interval_contains(self.get_view_interval(),
61
                                             self.get_loc())))
62
63
    @gridOn.setter
64
    def gridOn(self, value):  # noqa: N802
65
        self._gridOn = value
66
67
    @property
68
    def tick1On(self):  # noqa: N802
69
        """Control whether the lower tick mark is drawn for this tick."""
70
        return self._tick1On and self._need_lower()
71
72
    @tick1On.setter
73
    def tick1On(self, value):  # noqa: N802
74
        self._tick1On = value
75
76
    @property
77
    def label1On(self):  # noqa: N802
78
        """Control whether the lower tick label is drawn for this tick."""
79
        return self._label1On and self._need_lower()
80
81
    @label1On.setter
82
    def label1On(self, value):  # noqa: N802
83
        self._label1On = value
84
85
    @property
86
    def tick2On(self):  # noqa: N802
87
        """Control whether the upper tick mark is drawn for this tick."""
88
        return self._tick2On and self._need_upper()
89
90
    @tick2On.setter
91
    def tick2On(self, value):  # noqa: N802
92
        self._tick2On = value
93
94
    @property
95
    def label2On(self):  # noqa: N802
96
        """Control whether the upper tick label is drawn for this tick."""
97
        return self._label2On and self._need_upper()
98
99
    @label2On.setter
100
    def label2On(self, value):  # noqa: N802
101
        self._label2On = value
102
103
    def get_view_interval(self):
104
        """Get the view interval."""
105
        return self.axes.xaxis.get_view_interval()
106
107
108
class SkewXAxis(maxis.XAxis):
109
    r"""Make an x-axis that works properly for Skew-T plots.
110
111
    This class exists to force the use of our custom :class:`SkewXTick` as well
112
    as provide a custom value for interview that combines the extents of the
113
    upper and lower x-limits from the axes.
114
    """
115
116
    def _get_tick(self, major):
117
        return SkewXTick(self.axes, None, '', major=major)
118
119
    def get_view_interval(self):
120
        """Get the view interval."""
121
        return self.axes.upper_xlim[0], self.axes.lower_xlim[1]
122
123
124
class SkewSpine(mspines.Spine):
125
    r"""Make an x-axis spine that works properly for Skew-T plots.
126
127
    This class exists to use the separate x-limits from the axes to properly
128
    locate the spine.
129
    """
130
131
    def _adjust_location(self):
132
        pts = self._path.vertices
133
        if self.spine_type == 'top':
134
            pts[:, 0] = self.axes.upper_xlim
135
        else:
136
            pts[:, 0] = self.axes.lower_xlim
137
138
139
class SkewXAxes(Axes):
140
    r"""Make a set of axes for Skew-T plots.
141
142
    This class handles registration of the skew-xaxes as a projection as well as setting up
143
    the appropriate transformations. It also makes sure we use our instances for spines
144
    and x-axis: :class:`SkewSpine` and :class:`SkewXAxis`. It provides properties to
145
    facilitate finding the x-limits for the bottom and top of the plot as well.
146
    """
147
148
    # The projection must specify a name.  This will be used be the
149
    # user to select the projection, i.e. ``subplot(111,
150
    # projection='skewx')``.
151
    name = 'skewx'
152
153
    def __init__(self, *args, **kwargs):
154
        r"""Initialize `SkewXAxes`.
155
156
        Parameters
157
        ----------
158
        args : Arbitrary positional arguments
159
            Passed to :class:`matplotlib.axes.Axes`
160
161
        position: int, optional
162
            The rotation of the x-axis against the y-axis, in degrees.
163
164
        kwargs : Arbitrary keyword arguments
165
            Passed to :class:`matplotlib.axes.Axes`
166
        """
167
        # This needs to be popped and set before moving on
168
        self.rot = kwargs.pop('rotation', 30)
169
        Axes.__init__(self, *args, **kwargs)
170
171
    def _init_axis(self):
172
        # Taken from Axes and modified to use our modified X-axis
173
        self.xaxis = SkewXAxis(self)
174
        self.spines['top'].register_axis(self.xaxis)
175
        self.spines['bottom'].register_axis(self.xaxis)
176
        self.yaxis = maxis.YAxis(self)
177
        self.spines['left'].register_axis(self.yaxis)
178
        self.spines['right'].register_axis(self.yaxis)
179
180
    def _gen_axes_spines(self, locations=None, offset=0.0, units='inches'):
181
        # pylint: disable=unused-argument
182
        spines = {'top': SkewSpine.linear_spine(self, 'top'),
183
                  'bottom': mspines.Spine.linear_spine(self, 'bottom'),
184
                  'left': mspines.Spine.linear_spine(self, 'left'),
185
                  'right': mspines.Spine.linear_spine(self, 'right')}
186
        return spines
187
188
    def _set_lim_and_transforms(self):
189
        """Set limits and transforms.
190
191
        This is called once when the plot is created to set up all the
192
        transforms for the data, text and grids.
193
        """
194
        # Get the standard transform setup from the Axes base class
195
        Axes._set_lim_and_transforms(self)
196
197
        # Need to put the skew in the middle, after the scale and limits,
198
        # but before the transAxes. This way, the skew is done in Axes
199
        # coordinates thus performing the transform around the proper origin
200
        # We keep the pre-transAxes transform around for other users, like the
201
        # spines for finding bounds
202
        self.transDataToAxes = (self.transScale +
203
                                (self.transLimits +
204
                                 transforms.Affine2D().skew_deg(self.rot, 0)))
205
206
        # Create the full transform from Data to Pixels
207
        self.transData = self.transDataToAxes + self.transAxes
208
209
        # Blended transforms like this need to have the skewing applied using
210
        # both axes, in axes coords like before.
211
        self._xaxis_transform = (transforms.blended_transform_factory(
212
            self.transScale + self.transLimits,
213
            transforms.IdentityTransform()) +
214
            transforms.Affine2D().skew_deg(self.rot, 0)) + self.transAxes
215
216
    @property
217
    def lower_xlim(self):
218
        """Get the data limits for the x-axis along the bottom of the axes."""
219
        return self.axes.viewLim.intervalx
220
221
    @property
222
    def upper_xlim(self):
223
        """Get the data limits for the x-axis along the top of the axes."""
224
        return self.transDataToAxes.inverted().transform([[0., 1.], [1., 1.]])[:, 0]
225
226
227
# Now register the projection with matplotlib so the user can select
228
# it.
229
register_projection(SkewXAxes)
230
231
232
@exporter.export
233
class SkewT(object):
234
    r"""Make Skew-T log-P plots of data.
235
236
    This class simplifies the process of creating Skew-T log-P plots in
237
    using matplotlib. It handles requesting the appropriate skewed projection,
238
    and provides simplified wrappers to make it easy to plot data, add wind
239
    barbs, and add other lines to the plots (e.g. dry adiabats)
240
241
    Attributes
242
    ----------
243
    ax : `matplotlib.axes.Axes`
244
        The underlying Axes instance, which can be used for calling additional
245
        plot functions (e.g. `axvline`)
246
    """
247
248
    def __init__(self, fig=None, rotation=30, subplot=(1, 1, 1)):
249
        r"""Create SkewT - logP plots.
250
251
        Parameters
252
        ----------
253
        fig : matplotlib.figure.Figure, optional
254
            Source figure to use for plotting. If none is given, a new
255
            :class:`matplotlib.figure.Figure` instance will be created.
256
        rotation : float or int, optional
257
            Controls the rotation of temperature relative to horizontal. Given
258
            in degrees counterclockwise from x-axis. Defaults to 30 degrees.
259
        subplot : tuple[int, int, int] or `matplotlib.gridspec.SubplotSpec` instance, optional
260
            Controls the size/position of the created subplot. This allows creating
261
            the skewT as part of a collection of subplots. If subplot is a tuple, it
262
            should conform to the specification used for
263
            :meth:`matplotlib.figure.Figure.add_subplot`. The
264
            :class:`matplotlib.gridspec.SubplotSpec`
265
            can be created by using :class:`matplotlib.gridspec.GridSpec`.
266
        """
267
        if fig is None:
268
            import matplotlib.pyplot as plt
269
            figsize = plt.rcParams.get('figure.figsize', (7, 7))
270
            fig = plt.figure(figsize=figsize)
271
        self._fig = fig
272
273
        # Handle being passed a tuple for the subplot, or a GridSpec instance
274
        try:
275
            len(subplot)
276
        except TypeError:
277
            subplot = (subplot,)
278
        self.ax = fig.add_subplot(*subplot, projection='skewx', rotation=rotation)
279
        self.ax.grid(True)
280
281
    def fill_betweenx(self, y, x1, x2=0, **kwargs):
282
        y, x1, x2, kwargs['where'] = delete_masked_points(y, x1, x2, kwargs['where'])
283
        self.ax.fill_betweenx(y, x1, x2, **kwargs)
284
285
    def plot(self, p, t, *args, **kwargs):
286
        r"""Plot data.
287
288
        Simple wrapper around plot so that pressure is the first (independent)
289
        input. This is essentially a wrapper around `semilogy`. It also
290
        sets some appropriate ticking and plot ranges.
291
292
        Parameters
293
        ----------
294
        p : array_like
295
            pressure values
296
        t : array_like
297
            temperature values, can also be used for things like dew point
298
        args
299 View Code Duplication
            Other positional arguments to pass to :func:`~matplotlib.pyplot.semilogy`
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
300
        kwargs
301
            Other keyword arguments to pass to :func:`~matplotlib.pyplot.semilogy`
302
303
        Returns
304
        -------
305
        list[matplotlib.lines.Line2D]
306
            lines plotted
307
308
        See Also
309
        --------
310
        :func:`matplotlib.pyplot.semilogy`
311
        """
312
        # Skew-T logP plotting
313
        t, p = delete_masked_points(t, p)
314
        l = self.ax.semilogy(t, p, *args, **kwargs)
315
316
        # Disables the log-formatting that comes with semilogy
317
        self.ax.yaxis.set_major_formatter(ScalarFormatter())
318
        self.ax.yaxis.set_major_locator(MultipleLocator(100))
319
        self.ax.yaxis.set_minor_formatter(NullFormatter())
320
        if not self.ax.yaxis_inverted():
321
            self.ax.invert_yaxis()
322
323
        # Try to make sane default temperature plotting
324
        self.ax.xaxis.set_major_locator(MultipleLocator(10))
325
        self.ax.set_xlim(-50, 50)
326
327
        return l
328
329
    def plot_barbs(self, p, u, v, xloc=1.0, x_clip_radius=0.08, y_clip_radius=0.08, **kwargs):
330
        r"""Plot wind barbs.
331
332
        Adds wind barbs to the skew-T plot. This is a wrapper around the
333
        `barbs` command that adds to appropriate transform to place the
334
        barbs in a vertical line, located as a function of pressure.
335
336
        Parameters
337
        ----------
338
        p : array_like
339
            pressure values
340
        u : array_like
341
            U (East-West) component of wind
342
        v : array_like
343
            V (North-South) component of wind
344
        xloc : float, optional
345
            Position for the barbs, in normalized axes coordinates, where 0.0
346
            denotes far left and 1.0 denotes far right. Defaults to far right.
347
        x_clip_radius : float, optional
348
            Space, in normalized axes coordinates, to leave before clipping
349
            wind barbs in the x-direction. Defaults to 0.08.
350 View Code Duplication
        y_clip_radius : float, optional
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
351
            Space, in normalized axes coordinates, to leave above/below plot
352
            before clipping wind barbs in the y-direction. Defaults to 0.08.
353
        kwargs
354
            Other keyword arguments to pass to :func:`~matplotlib.pyplot.barbs`
355
356
        Returns
357
        -------
358
        matplotlib.quiver.Barbs
359
            instance created
360
361
        See Also
362
        --------
363
        :func:`matplotlib.pyplot.barbs`
364
        """
365
        # Assemble array of x-locations in axes space
366
        x = np.empty_like(p)
367
        x.fill(xloc)
368
369
        # Do barbs plot at this location
370
        b = self.ax.barbs(x, p, u, v,
371
                          transform=self.ax.get_yaxis_transform(which='tick2'),
372
                          clip_on=True, **kwargs)
373
374
        # Override the default clip box, which is the axes rectangle, so we can have
375
        # barbs that extend outside.
376
        ax_bbox = transforms.Bbox([[xloc - x_clip_radius, -y_clip_radius],
377
                                   [xloc + x_clip_radius, 1.0 + y_clip_radius]])
378
        b.set_clip_box(transforms.TransformedBbox(ax_bbox, self.ax.transAxes))
379
        return b
380
381
    def plot_dry_adiabats(self, t0=None, p=None, **kwargs):
382
        r"""Plot dry adiabats.
383
384
        Adds dry adiabats (lines of constant potential temperature) to the
385
        plot. The default style of these lines is dashed red lines with an alpha
386
        value of 0.5. These can be overridden using keyword arguments.
387
388
        Parameters
389
        ----------
390
        t0 : array_like, optional
391
            Starting temperature values in Kelvin. If none are given, they will be
392
            generated using the current temperature range at the bottom of
393
            the plot.
394
        p : array_like, optional
395
            Pressure values to be included in the dry adiabats. If not
396
            specified, they will be linearly distributed across the current
397
            plotted pressure range.
398
        kwargs
399
            Other keyword arguments to pass to :class:`matplotlib.collections.LineCollection`
400
401
        Returns
402
        -------
403
        matplotlib.collections.LineCollection
404
            instance created
405
406
        See Also
407
        --------
408
        :func:`~metpy.calc.thermo.dry_lapse`
409
        :meth:`plot_moist_adiabats`
410
        :class:`matplotlib.collections.LineCollection`
411
        """
412
        # Determine set of starting temps if necessary
413
        if t0 is None:
414
            xmin, xmax = self.ax.get_xlim()
415
            t0 = np.arange(xmin, xmax + 1, 10) * units.degC
416
417
        # Get pressure levels based on ylims if necessary
418
        if p is None:
419
            p = np.linspace(*self.ax.get_ylim()) * units.mbar
420
421
        # Assemble into data for plotting
422
        t = dry_lapse(p, t0[:, np.newaxis]).to(units.degC)
423
        linedata = [np.vstack((ti, p)).T for ti in t]
424
425
        # Add to plot
426
        kwargs.setdefault('colors', 'r')
427
        kwargs.setdefault('linestyles', 'dashed')
428
        kwargs.setdefault('alpha', 0.5)
429
        return self.ax.add_collection(LineCollection(linedata, **kwargs))
430
431
    def plot_moist_adiabats(self, t0=None, p=None, **kwargs):
432
        r"""Plot moist adiabats.
433
434
        Adds saturated pseudo-adiabats (lines of constant equivalent potential
435
        temperature) to the plot. The default style of these lines is dashed
436
        blue lines with an alpha value of 0.5. These can be overridden using
437
        keyword arguments.
438
439
        Parameters
440
        ----------
441
        t0 : array_like, optional
442
            Starting temperature values in Kelvin. If none are given, they will be
443
            generated using the current temperature range at the bottom of
444
            the plot.
445
        p : array_like, optional
446
            Pressure values to be included in the moist adiabats. If not
447
            specified, they will be linearly distributed across the current
448
            plotted pressure range.
449
        kwargs
450
            Other keyword arguments to pass to :class:`matplotlib.collections.LineCollection`
451
452
        Returns
453
        -------
454
        matplotlib.collections.LineCollection
455
            instance created
456
457
        See Also
458
        --------
459
        :func:`~metpy.calc.thermo.moist_lapse`
460
        :meth:`plot_dry_adiabats`
461
        :class:`matplotlib.collections.LineCollection`
462
        """
463
        # Determine set of starting temps if necessary
464
        if t0 is None:
465
            xmin, xmax = self.ax.get_xlim()
466
            t0 = np.concatenate((np.arange(xmin, 0, 10),
467
                                 np.arange(0, xmax + 1, 5))) * units.degC
468
469
        # Get pressure levels based on ylims if necessary
470
        if p is None:
471
            p = np.linspace(*self.ax.get_ylim()) * units.mbar
472
473
        # Assemble into data for plotting
474
        t = moist_lapse(p, t0[:, np.newaxis]).to(units.degC)
475
        linedata = [np.vstack((ti, p)).T for ti in t]
476
477
        # Add to plot
478
        kwargs.setdefault('colors', 'b')
479
        kwargs.setdefault('linestyles', 'dashed')
480
        kwargs.setdefault('alpha', 0.5)
481
        return self.ax.add_collection(LineCollection(linedata, **kwargs))
482
483
    def plot_mixing_lines(self, w=None, p=None, **kwargs):
484
        r"""Plot lines of constant mixing ratio.
485
486
        Adds lines of constant mixing ratio (isohumes) to the
487
        plot. The default style of these lines is dashed green lines with an
488
        alpha value of 0.8. These can be overridden using keyword arguments.
489
490
        Parameters
491
        ----------
492
        w : array_like, optional
493
            Unitless mixing ratio values to plot. If none are given, default
494
            values are used.
495
        p : array_like, optional
496
            Pressure values to be included in the isohumes. If not
497
            specified, they will be linearly distributed across the current
498
            plotted pressure range up to 600 mb.
499
        kwargs
500
            Other keyword arguments to pass to :class:`matplotlib.collections.LineCollection`
501
502
        Returns
503
        -------
504
        matplotlib.collections.LineCollection
505
            instance created
506
507
        See Also
508
        --------
509
        :class:`matplotlib.collections.LineCollection`
510
        """
511
        # Default mixing level values if necessary
512
        if w is None:
513
            w = np.array([0.0004, 0.001, 0.002, 0.004, 0.007, 0.01,
514
                          0.016, 0.024, 0.032]).reshape(-1, 1)
515
516
        # Set pressure range if necessary
517
        if p is None:
518
            p = np.linspace(600, max(self.ax.get_ylim())) * units.mbar
519
520
        # Assemble data for plotting
521
        td = dewpoint(vapor_pressure(p, w))
522
        linedata = [np.vstack((t, p)).T for t in td]
523
524
        # Add to plot
525
        kwargs.setdefault('colors', 'g')
526
        kwargs.setdefault('linestyles', 'dashed')
527
        kwargs.setdefault('alpha', 0.8)
528
        return self.ax.add_collection(LineCollection(linedata, **kwargs))
529
530
531
@exporter.export
532
class Hodograph(object):
533
    r"""Make a hodograph of wind data.
534
535
    Plots the u and v components of the wind along the x and y axes, respectively.
536
537
    This class simplifies the process of creating a hodograph using matplotlib.
538
    It provides helpers for creating a circular grid and for plotting the wind as a line
539
    colored by another value (such as wind speed).
540
541
    Attributes
542
    ----------
543
    ax : `matplotlib.axes.Axes`
544
        The underlying Axes instance used for all plotting
545
    """
546
547
    def __init__(self, ax=None, component_range=80):
548
        r"""Create a Hodograph instance.
549
550
        Parameters
551
        ----------
552
        ax : `matplotlib.axes.Axes`, optional
553
            The `Axes` instance used for plotting
554
        component_range : value
555
            The maximum range of the plot. Used to set plot bounds and control the maximum
556
            number of grid rings needed.
557
        """
558
        if ax is None:
559
            import matplotlib.pyplot as plt
560
            self.ax = plt.figure().add_subplot(1, 1, 1)
561
        else:
562
            self.ax = ax
563
        self.ax.set_aspect('equal', 'box')
564
        self.ax.set_xlim(-component_range, component_range)
565
        self.ax.set_ylim(-component_range, component_range)
566
567
        # == sqrt(2) * max_range, which is the distance at the corner
568
        self.max_range = 1.4142135 * component_range
569
570
    def add_grid(self, increment=10., **kwargs):
571
        r"""Add grid lines to hodograph.
572
573
        Creates lines for the x- and y-axes, as well as circles denoting wind speed values.
574
575
        Parameters
576
        ----------
577
        increment : value, optional
578
            The value increment between rings
579
        kwargs
580
            Other kwargs to control appearance of lines
581
582
        See Also
583
        --------
584
        :class:`matplotlib.patches.Circle`
585
        :meth:`matplotlib.axes.Axes.axhline`
586
        :meth:`matplotlib.axes.Axes.axvline`
587
        """
588
        # Some default arguments. Take those, and update with any
589
        # arguments passed in
590
        grid_args = dict(color='grey', linestyle='dashed')
591
        if kwargs:
592
            grid_args.update(kwargs)
593
594
        # Take those args and make appropriate for a Circle
595
        circle_args = grid_args.copy()
596
        color = circle_args.pop('color', None)
597
        circle_args['edgecolor'] = color
598
        circle_args['fill'] = False
599
600
        self.rings = []
601
        for r in np.arange(increment, self.max_range, increment):
602
            c = Circle((0, 0), radius=r, **circle_args)
603
            self.ax.add_patch(c)
604
            self.rings.append(c)
605
606
        # Add lines for x=0 and y=0
607
        self.yaxis = self.ax.axvline(0, **grid_args)
608
        self.xaxis = self.ax.axhline(0, **grid_args)
609
610
    @staticmethod
611
    def _form_line_args(kwargs):
612
        """Simplify taking the default line style and extending with kwargs."""
613
        def_args = dict(linewidth=3)
614
        def_args.update(kwargs)
615
        return def_args
616
617
    def plot(self, u, v, **kwargs):
618
        r"""Plot u, v data.
619
620
        Plots the wind data on the hodograph.
621
622
        Parameters
623
        ----------
624
        u : array_like
625
            u-component of wind
626
        v : array_like
627
            v-component of wind
628
        kwargs
629
            Other keyword arguments to pass to :meth:`matplotlib.axes.Axes.plot`
630
631
        Returns
632
        -------
633
        list[matplotlib.lines.Line2D]
634
            lines plotted
635
636
        See Also
637
        --------
638
        :meth:`Hodograph.plot_colormapped`
639
        """
640
        line_args = self._form_line_args(kwargs)
641
        u, v = delete_masked_points(u, v)
642
        return self.ax.plot(u, v, **line_args)
643
644
    def plot_colormapped(self, u, v, c, **kwargs):
645
        r"""Plot u, v data, with line colored based on a third set of data.
646
647
        Plots the wind data on the hodograph, but with a colormapped line.
648
649
        Simple wrapper around plot so that pressure is the first (independent)
650
        input. This is essentially a wrapper around `semilogy`. It also
651
        sets some appropriate ticking and plot ranges.
652
653
        Parameters
654
        ----------
655
        u : array_like
656
            u-component of wind
657
        v : array_like
658
            v-component of wind
659
        c : array_like
660
            data to use for colormapping
661
        kwargs
662
            Other keyword arguments to pass to :class:`matplotlib.collections.LineCollection`
663
664
        Returns
665
        -------
666
        matplotlib.collections.LineCollection
667
            instance created
668
669
        See Also
670
        --------
671
        :meth:`Hodograph.plot`
672
        """
673
        line_args = self._form_line_args(kwargs)
674
        u, v, c = delete_masked_points(u, v, c)
675
        lc = colored_line(u, v, c, **line_args)
676
        self.ax.add_collection(lc)
677
        return lc
678