tephpy.plotting.axes#

The tephigram matplotlib projection.

TephigramAxes (registered as the "tephigram" projection) uses the native rotated x-y plane as its data space, with the temperature/theta mapping exposed as an invertible matplotlib transform and the five background isopleth families drawn by default as zoom-aware artists (spec §3.2).

Side-of-axes layout contract (spec §10 item 7): panels beside the diagram are appended with mpl_toolkits.axes_grid1’s axes divider, which tracks the equal-aspect box height — right side, inside-out: the wind-barb gutter, then the indices panel. One divider is created per axes, cached, and shared by every side-panel method; _relayout_side_panels rebuilds the divider’s horizontal stack and reassigns every locator whenever a panel appears, so the inside-out order holds regardless of the order the panel methods are called in (spec §3.2).

Classes#

TephigramTransform

Map (temperature, theta) pairs to tephigram (x, y) pairs.

TephigramInvertedTransform

Map tephigram (x, y) pairs back to (temperature, theta).

TephigramAxes

Matplotlib axes for the "tephigram" projection.

Module Contents#

class tephpy.plotting.axes.TephigramTransform(shorthand_name=None)[source]#

Bases: matplotlib.transforms.Transform

Map (temperature, theta) pairs to tephigram (x, y) pairs.

A thin, invertible matplotlib wrapper over tephpy.transforms.xy_from_temperature_theta(); operates on (N, 2) arrays in diagram-native units (degrees Celsius).

input_dims = 2#

The number of input dimensions of this transform. Must be overridden (with integers) in the subclass.

output_dims = 2#

The number of output dimensions of this transform. Must be overridden (with integers) in the subclass.

is_separable = False#

True if this transform is separable in the x- and y- dimensions.

has_inverse = True#

True if this transform has a corresponding inverse transform.

transform_non_affine(values: numpy.typing.ArrayLike) numpy.typing.NDArray[numpy.float64][source]#

Transform (temperature, theta) pairs to (x, y).

Parameters:
valuesArrayLike

Array-like of shape (N, 2) or length 2: temperature, theta in degrees Celsius.

Returns:
numpy.ndarray

The tephigram x, y coordinates (the axes’ data space), with the input’s dimensionality preserved: shape (N, 2) for (N, 2) input, shape (2,) for length-2 input.

inverted() TephigramInvertedTransform[source]#

Return the inverse (x, y) -> (temperature, theta) transform.

Returns:
TephigramInvertedTransform

The inverse transform.

class tephpy.plotting.axes.TephigramInvertedTransform(shorthand_name=None)[source]#

Bases: matplotlib.transforms.Transform

Map tephigram (x, y) pairs back to (temperature, theta).

input_dims = 2#

The number of input dimensions of this transform. Must be overridden (with integers) in the subclass.

output_dims = 2#

The number of output dimensions of this transform. Must be overridden (with integers) in the subclass.

is_separable = False#

True if this transform is separable in the x- and y- dimensions.

has_inverse = True#

True if this transform has a corresponding inverse transform.

transform_non_affine(values: numpy.typing.ArrayLike) numpy.typing.NDArray[numpy.float64][source]#

Transform (x, y) pairs to (temperature, theta).

Parameters:
valuesArrayLike

Array-like of shape (N, 2) or length 2: tephigram x, y coordinates (the axes’ data space).

Returns:
numpy.ndarray

Temperature, theta in degrees Celsius, with the input’s dimensionality preserved: shape (N, 2) for (N, 2) input, shape (2,) for length-2 input.

inverted() TephigramTransform[source]#

Return the forward (temperature, theta) -> (x, y) transform.

Returns:
TephigramTransform

The forward transform.

class tephpy.plotting.axes.TephigramAxes(fig, *args, facecolor=None, frameon=True, sharex=None, sharey=None, label='', xscale=None, yscale=None, box_aspect=None, forward_navigation_events='auto', **kwargs)[source]#

Bases: matplotlib.axes.Axes

Matplotlib axes for the "tephigram" projection.

The data space is the native rotated x-y plane (dimensionless), with equal aspect so the isotherm/dry-adiabat grid stays exactly perpendicular on screen. The five background isopleth families are drawn by default as zoom-aware artists and reconfigured through the accessor methods (isotherms(), isobars(), dry_adiabats(), moist_adiabats(), mixing_ratios()). The temperature/theta mapping is exposed as tephigram_transform; artists plot in (temperature, theta) space via transform=ax.tephigram_transform + ax.transData. Native x/y ticks carry no meteorological meaning and are hidden until a family claims an edge for its labels — labels=("bottom", "left") turns them into that family’s scale (spec §3.2).

name = 'tephigram'#
tephigram_transform: TephigramTransform#
clear() None[source]#

Reset the axes to the tephigram projection defaults.

Matplotlib calls this during Axes.__init__ and on user ax.clear(); both paths recreate the projection-owned state: the tephigram transform, equal aspect, hidden native axes, the five background isopleth families, any edges they claim for their labels, and the default extent (tephpy.config diagram extent, else DEFAULT_EXTENT). Side panels — the barb gutter and the indices panel — are removed with the diagram they annotated, except when the figure is clearing itself: it removes every axes anyway, and it is iterating a snapshot this one must not delete from.

Raises:
TypeError

If tephpy.config gives one diagram edge to two families, or names an unknown label placement, or carries a malformed family emphasis — a non-mapping, a member value that will not convert to float, a style that is not a mapping, or an unknown style key (spec §3.2).

ValueError

If a tephpy.config family emphasis keys a member value that is not finite, or gives a linewidth that is not positive and finite, or an alpha outside [0, 1], or a family interval is not positive and finite.

set_extent(extent: tuple[tuple[float, float], tuple[float, float]]) None[source]#

Fix the view from ((pressure, temperature), …) corners.

The cartopy-style idiom for directly comparable figures (spec §3.2): the two corners are mapped through the tephigram transforms to x/y limits, and autoscaling is disabled so later overlays never drift the window.

Parameters:
extenttuple

((pressure, temperature), (pressure, temperature)) bottom-left and top-right corners in hPa / degrees Celsius.

Raises:
ValueError

If a corner is unphysical (non-positive pressure) or the corners are degenerate.

format_coord(x: float, y: float) str[source]#

Report diagram-meaningful values for the cursor position (spec §3.2).

The navigation toolbar’s readout: the data-space cursor position inverts to (temperature, theta), pressure derives via Poisson’s equation, and the configured fields render in listed order, e.g. 850 hPa, -4.2 °C, θ 8.6 °C. Fields resolve as instance assignment > tephpy.config > _constants: assigning ax.format_coord = fn (stock matplotlib) shadows this method entirely, and config.cursor.fields is read live on every call, so a config.context(cursor={"fields": ...}) override applies to existing axes for its duration (spec §3.5). Fields whose value is undefined at the point are omitted from the readout.

Parameters:
xfloat

Cursor x in tephigram data space.

yfloat

Cursor y in tephigram data space.

Returns:
str

The formatted readout, or "" when the position is unphysical (e.g. left of the -273.15 °C isotherm).

Raises:
TypeError

If config.cursor.fields is a bare string rather than a tuple of field names, or names an unknown field.

plot_profile(pressure: tephpy.calc.Profile, *, label: str | None = None, **kwargs: Any) matplotlib.lines.Line2D[source]#
plot_profile(pressure: object, temperature: object, *, units: collections.abc.Mapping[str, str] | None = None, label: str | None = None, **kwargs: Any) matplotlib.lines.Line2D

Plot one profile of temperature against pressure (spec §3.2).

Both arrays are pint quantities — or bare arrays with the units= mapping (spec §5) — converted to diagram-native units and plotted through the tephigram transform machinery. Matplotlib keywords pass through untouched, and out-of-domain values (pressure <= 0 hPa) propagate NaN, breaking the line (spec §3.1).

The same signature also accepts a calc.Profile (e.g. the return of calc.parcel_path) as its only positional argument; dispatch is duck-typed on the Profile shape — temperature omitted and pressure/temperature/lcl_pressure attributes present. Label precedence in that form: label argument > profile.label > no entry. In both forms no style defaults are set — this is the low-level primitive (spec §4 styles parcel paths explicitly at the call site).

Parameters:
pressurepint.Quantity, ArrayLike, or Profile

Level pressures, or the profile to plot.

temperaturepint.Quantity or ArrayLike, optional

Level temperatures; omitted in the Profile form.

unitsmapping of str to str, optional

Unit strings for bare arrays, keyed by argument name, e.g. units={"pressure": "hPa", "temperature": "degC"}; not accepted in the Profile form.

labelstr, optional

Legend label for the line.

**kwargsAny

Passed through to matplotlib.axes.Axes.plot().

Returns:
matplotlib.lines.Line2D

The profile line.

Raises:
TephpyUnitsError

For unit-less bare arrays, ambiguous or unparsable units, or the wrong dimensionality.

TypeError

For wrong argument combinations: a Profile together with temperature or units=, or temperature omitted when the sole argument is not Profile-shaped (a bare pressure array, or a Sounding passed by mistake).

plot_sounding(snd: tephpy.sounding.Sounding, *, label: str | None = None, **kwargs: Any) tuple[matplotlib.lines.Line2D, matplotlib.lines.Line2D | None][source]#

Plot a sounding’s temperature and dewpoint profiles (spec §3.2).

Temperature and dewpoint-when-present draw as two profile lines in the conventional colours (temperature red, dewpoint green), with one legend entry per sounding attached to the temperature line. Label precedence: label argument > snd.label > no entry. Matplotlib keywords pass through to both lines, overriding the convention defaults; legends stay stock matplotlib — call ax.legend().

Parameters:
sndSounding

The sounding to plot.

labelstr, optional

Legend label override.

**kwargsAny

Passed through to matplotlib.axes.Axes.plot() for both lines.

Returns:
tuple of matplotlib.lines.Line2D

(temperature_line, dewpoint_line); the dewpoint line is None when the sounding has no dewpoint.

shade_cape(snd: tephpy.sounding.Sounding, parcel: tephpy.calc.Profile, **kwargs: Any) matplotlib.patches.PathPatch | None[source]#

Shade the CAPE area between the sounding and a parcel path.

The positive-buoyancy region between the environment temperature and the parcel path, bounded as metpy.calc.cape_cin() integrates — from the LFC to the EL, to the profile top when the parcel is still buoyant there — so the shading matches the annotated numbers (spec §3.2). Drawn as one compound-path patch; interrupted regions become multiple polygons in the same patch.

Parameters:
sndSounding

The environment sounding.

parcelProfile

The parcel path, e.g. from calc.parcel_path.

**kwargsAny

Passed through to matplotlib.patches.PathPatch, overriding the _constants conventions.

Returns:
matplotlib.patches.PathPatch or None

The shaded patch, or None for zero area — 0 is an answer, not an error (spec §6).

shade_cin(snd: tephpy.sounding.Sounding, parcel: tephpy.calc.Profile, **kwargs: Any) matplotlib.patches.PathPatch | None[source]#

Shade the CIN area between the sounding and a parcel path.

The negative-buoyancy region between the environment temperature and the parcel path, bounded as metpy.calc.cape_cin() integrates — from the parcel start to the LFC — so the shading matches the annotated numbers (spec §3.2). Drawn as one compound-path patch; with no LFC there is no CIN region.

Parameters:
sndSounding

The environment sounding.

parcelProfile

The parcel path, e.g. from calc.parcel_path.

**kwargsAny

Passed through to matplotlib.patches.PathPatch, overriding the _constants conventions.

Returns:
matplotlib.patches.PathPatch or None

The shaded patch, or None for zero area — 0 is an answer, not an error (spec §6).

edge_axis(edge: str) matplotlib.axis.Axis[source]#

Return the matplotlib axis drawing one diagram edge’s ticks.

The uniform handle on all four edges (spec §3.2), keyed by the same names the labels option takes. Bottom and left are the axes’ own xaxis/yaxis; top and right belong to a secondary axes that has no other public handle. tephpy stamps its tick conventions on an edge axis once, when that axis is created, so everything stock matplotlib offers is the caller’s from the claim onwards — e.g. ax.edge_axis("top").set_tick_params(labelsize=12), or set_label_text("") to keep the ticks and drop the axis title. The only thing tephpy changes afterwards is the tick colour, and only when the owning family’s own colour or alpha changes, or another family takes the edge.

Parameters:
edgestr

The edge, one of EDGES.

Returns:
matplotlib.axis.Axis

The axis drawing that edge’s ticks.

Raises:
TypeError

If edge is not one of EDGES.

ValueError

If no family labels that edge. An unclaimed edge renders nothing to style — bottom and left are hidden, and top and right have no axis yet — and probing one must not build a secondary axes nothing is using.

plot_barbs(snd: tephpy.sounding.Sounding, *, x: float | None = None, minimum_separation: float | None = None, **kwargs: Any) tephpy.plotting.barbs.BarbStaff[source]#

Plot the sounding’s wind barbs on the gutter staff (spec §3.2).

The barbs draw on a right-hand gutter appended with the shared divider, each level at the y where its isobar meets the diagram’s right edge (the printed-form staff convention), thinned per draw to a minimum vertical separation — zooming in reveals more levels. Met Office symbology: flag 50 kt, full barb 10 kt, half barb 5 kt, speeds rounded to 5 kt bins; calm levels render as matplotlib’s small circle. Each call draws one staff: overlay soundings by calling again with another x, minimum_separation, and colour.

Parameters:
sndSounding

The sounding to plot; must carry wind.

xfloat, optional

The staff position as a fraction across the gutter (default BARB_STAFF_POSITION).

minimum_separationfloat, optional

The minimum vertical separation between drawn barbs, in points (default BARB_MIN_SEPARATION) — a longer length= glyph wants a wider separation.

**kwargsAny

Passed through to matplotlib.quiver.Barbs, over the _constants conventions (increments, rounding, length).

Returns:
BarbStaff

The zoom-aware staff artist; its barbs property is the underlying matplotlib collection.

Raises:
MissingDataError

If the sounding has no wind (spec §6).

annotate_indices(indices: tephpy.calc.SoundingIndices) matplotlib.axes.Axes[source]#

Display derived parameters in a panel beside the diagram.

The first consumer of the side-of-axes contract (spec §3.2): the panel is appended with the axes_grid1 divider, one formatted line per SoundingIndices field, NaN rendered as an em dash. Calling it again updates the panel in place rather than stacking a second one, and the side-panel layout is rebuilt inside-out (barb gutter, then this panel) whichever order the panel methods are called in (spec §3.2).

Parameters:
indicesSoundingIndices

The derived parameters, e.g. from calc.indices.

Returns:
matplotlib.axes.Axes

The panel axes, for restyling.

isotherms(*, values: collections.abc.Iterable[float] | None = None, interval: float | None = None, color: str | None = None, linewidth: float | None = None, alpha: float | None = None, labels: bool | str | tuple[str, Ellipsis] | None = None, emphasis: collections.abc.Mapping[float, collections.abc.Mapping[str, object]] | None = None, visible: bool | None = None) tephpy.plotting.isopleths.IsoplethFamily[source]#

Return (and optionally reconfigure) the isotherm family.

With no arguments this returns the family artist unchanged; any keyword given reconfigures it first (spec §3.2). Values are in degrees Celsius.

Parameters:
valuesiterable of float, optional

Explicit member temperatures; disables the zoom ladder.

intervalfloat, optional

Member interval; disables the zoom ladder.

colorstr, optional

Line and label colour.

linewidthfloat, optional

Line width in points.

alphafloat, optional

Line and label alpha.

labelsbool or str or tuple of str, optional

Where member values are labelled: True (every member inline — the default), False (none), or the diagram edge names "bottom", "top", "left" and "right", singly or as a tuple. Listed edges label the members that reach them; every member left over is labelled inline. One family per edge. An edge crowded by closely spaced members is thinned with interval=; edge labelling never drops a member’s label itself.

emphasismapping of float to mapping, optional

Members to distinguish, keyed by member value in degrees Celsius. Each value is a mapping of style overrides — color, linewidth, linestyle, alpha — and an omitted key falls back to the family’s own style, so {0.0: {}} draws that member at EMPHASIS_LINEWIDTH in the family’s own colour. An emphasised member is always drawn, whatever the zoom ladder would select, so a value the interval never lands on still appears. An empty mapping emphasises nothing.

visiblebool, optional

Whether the family is drawn.

Returns:
IsoplethFamily

The isotherm family artist.

Raises:
TypeError

If labels names an unknown placement, emphasis is malformed, or an edge another family already claims.

ValueError

If an emphasis member value is not finite, a linewidth is not positive and finite, an alpha falls outside [0, 1], or interval is not positive and finite.

isobars(*, values: collections.abc.Iterable[float] | None = None, interval: float | None = None, color: str | None = None, linewidth: float | None = None, alpha: float | None = None, labels: bool | str | tuple[str, Ellipsis] | None = None, emphasis: collections.abc.Mapping[float, collections.abc.Mapping[str, object]] | None = None, visible: bool | None = None) tephpy.plotting.isopleths.IsoplethFamily[source]#

Return (and optionally reconfigure) the isobar family.

With no arguments this returns the family artist unchanged; any keyword given reconfigures it first (spec §3.2). Values are in hPa.

Parameters:
valuesiterable of float, optional

Explicit member pressures; disables the zoom ladder.

intervalfloat, optional

Member interval; disables the zoom ladder.

colorstr, optional

Line and label colour.

linewidthfloat, optional

Line width in points.

alphafloat, optional

Line and label alpha.

labelsbool or str or tuple of str, optional

Where member values are labelled: True (every member inline — the default), False (none), or the diagram edge names "bottom", "top", "left" and "right", singly or as a tuple. Listed edges label the members that reach them; every member left over is labelled inline. One family per edge. An edge crowded by closely spaced members is thinned with interval=; edge labelling never drops a member’s label itself.

emphasismapping of float to mapping, optional

Members to distinguish, keyed by member value in hPa. Each value is a mapping of style overrides — color, linewidth, linestyle, alpha — and an omitted key falls back to the family’s own style, so {500.0: {}} draws that member at EMPHASIS_LINEWIDTH in the family’s own colour. An emphasised member is always drawn, whatever the zoom ladder would select, so a value the interval never lands on still appears. An empty mapping emphasises nothing.

visiblebool, optional

Whether the family is drawn.

Returns:
IsoplethFamily

The isobar family artist.

Raises:
TypeError

If labels names an unknown placement, emphasis is malformed, or an edge another family already claims.

ValueError

If an emphasis member value is not finite, a linewidth is not positive and finite, an alpha falls outside [0, 1], or interval is not positive and finite.

dry_adiabats(*, values: collections.abc.Iterable[float] | None = None, interval: float | None = None, color: str | None = None, linewidth: float | None = None, alpha: float | None = None, labels: bool | str | tuple[str, Ellipsis] | None = None, emphasis: collections.abc.Mapping[float, collections.abc.Mapping[str, object]] | None = None, visible: bool | None = None) tephpy.plotting.isopleths.IsoplethFamily[source]#

Return (and optionally reconfigure) the dry-adiabat family.

With no arguments this returns the family artist unchanged; any keyword given reconfigures it first (spec §3.2). Values are potential temperatures in degrees Celsius.

Parameters:
valuesiterable of float, optional

Explicit member potential temperatures; disables the zoom ladder.

intervalfloat, optional

Member interval; disables the zoom ladder.

colorstr, optional

Line and label colour.

linewidthfloat, optional

Line width in points.

alphafloat, optional

Line and label alpha.

labelsbool or str or tuple of str, optional

Where member values are labelled: True (every member inline — the default), False (none), or the diagram edge names "bottom", "top", "left" and "right", singly or as a tuple. Listed edges label the members that reach them; every member left over is labelled inline. One family per edge. An edge crowded by closely spaced members is thinned with interval=; edge labelling never drops a member’s label itself.

emphasismapping of float to mapping, optional

Members to distinguish, keyed by member value in degrees Celsius. Each value is a mapping of style overrides — color, linewidth, linestyle, alpha — and an omitted key falls back to the family’s own style, so {0.0: {}} draws that member at EMPHASIS_LINEWIDTH in the family’s own colour. An emphasised member is always drawn, whatever the zoom ladder would select, so a value the interval never lands on still appears. An empty mapping emphasises nothing.

visiblebool, optional

Whether the family is drawn.

Returns:
IsoplethFamily

The dry-adiabat family artist.

Raises:
TypeError

If labels names an unknown placement, emphasis is malformed, or an edge another family already claims.

ValueError

If an emphasis member value is not finite, a linewidth is not positive and finite, an alpha falls outside [0, 1], or interval is not positive and finite.

moist_adiabats(*, values: collections.abc.Iterable[float] | None = None, interval: float | None = None, truncation: float | None = None, color: str | None = None, linewidth: float | None = None, alpha: float | None = None, labels: bool | str | tuple[str, Ellipsis] | None = None, emphasis: collections.abc.Mapping[float, collections.abc.Mapping[str, object]] | None = None, visible: bool | None = None) tephpy.plotting.isopleths.IsoplethFamily[source]#

Return (and optionally reconfigure) the moist-adiabat family.

With no arguments this returns the family artist unchanged; any keyword given reconfigures it first (spec §3.2). Values are wet-bulb potential temperatures in degrees Celsius.

Parameters:
valuesiterable of float, optional

Explicit member wet-bulb potential temperatures; disables the zoom ladder.

intervalfloat, optional

Member interval; disables the zoom ladder.

truncationfloat, optional

Temperature (°C) below which the curves are truncated.

colorstr, optional

Line and label colour.

linewidthfloat, optional

Line width in points.

alphafloat, optional

Line and label alpha.

labelsbool or str or tuple of str, optional

Where member values are labelled: True (every member inline — the default), False (none), or the diagram edge names "bottom", "top", "left" and "right", singly or as a tuple. Listed edges label the members that reach them; every member left over is labelled inline. One family per edge. An edge crowded by closely spaced members is thinned with interval=; edge labelling never drops a member’s label itself.

emphasismapping of float to mapping, optional

Members to distinguish, keyed by member value in degrees Celsius. Each value is a mapping of style overrides — color, linewidth, linestyle, alpha — and an omitted key falls back to the family’s own style, so {0.0: {}} draws that member at EMPHASIS_LINEWIDTH in the family’s own colour. An emphasised member is always drawn, whatever the zoom ladder would select, so a value the interval never lands on still appears. An empty mapping emphasises nothing.

visiblebool, optional

Whether the family is drawn.

Returns:
IsoplethFamily

The moist-adiabat family artist.

Raises:
TypeError

If labels names an unknown placement, emphasis is malformed, or an edge another family already claims.

ValueError

If an emphasis member value is not finite, a linewidth is not positive and finite, an alpha falls outside [0, 1], or interval is not positive and finite.

mixing_ratios(*, values: collections.abc.Iterable[float] | None = None, color: str | None = None, linewidth: float | None = None, alpha: float | None = None, labels: bool | str | tuple[str, Ellipsis] | None = None, emphasis: collections.abc.Mapping[float, collections.abc.Mapping[str, object]] | None = None, visible: bool | None = None) tephpy.plotting.isopleths.IsoplethFamily[source]#

Return (and optionally reconfigure) the mixing-ratio family.

With no arguments this returns the family artist unchanged; any keyword given reconfigures it first (spec §3.2). Values are humidity mixing ratios in g/kg; this family has no interval (its members come from the MIXING_RATIO_VALUES ladder).

Parameters:
valuesiterable of float, optional

Explicit member mixing ratios; disables the zoom ladder.

colorstr, optional

Line and label colour.

linewidthfloat, optional

Line width in points.

alphafloat, optional

Line and label alpha.

labelsbool or str or tuple of str, optional

Where member values are labelled: True (every member inline — the default), False (none), or the diagram edge names "bottom", "top", "left" and "right", singly or as a tuple. Listed edges label the members that reach them; every member left over is labelled inline. One family per edge. An edge crowded by a large member set is thinned with values=; edge labelling never drops a member’s label itself.

emphasismapping of float to mapping, optional

Members to distinguish, keyed by member value in g/kg. Each value is a mapping of style overrides — color, linewidth, linestyle, alpha — and an omitted key falls back to the family’s own style, so {5.0: {}} draws that member at EMPHASIS_LINEWIDTH in the family’s own colour. An emphasised member is always drawn, whatever the zoom ladder would select, so a value the ladder never selects still appears. An empty mapping emphasises nothing.

visiblebool, optional

Whether the family is drawn.

Returns:
IsoplethFamily

The mixing-ratio family artist.

Raises:
TypeError

If labels names an unknown placement, emphasis is malformed, or an edge another family already claims.

ValueError

If an emphasis member value is not finite, a linewidth is not positive and finite, or an alpha falls outside [0, 1].