tephpy.plotting.isopleths#

Isopleth families for the tephigram projection (spec §3.2).

Each of the five background families — isotherms, isobars, dry adiabats, moist adiabats, and humidity mixing-ratio lines — is drawn by one zoom-aware IsoplethFamily artist. Member polylines are precomputed as bare numpy arrays over a generous physical domain (the _constants domains), mapped once into the tephigram x-y data space, and cached on the artist; every draw selects the members appropriate to the current view and zoom ladder and re-places the family’s labels. The curved families delegate their moist thermodynamics to MetPy behind function-local imports so that import tephpy stays light (spec §10 item 10). The design is derived from the published tephigram construction with tephi as a corroborating oracle, not ported from tephi (spec §3.1/§10 item 5).

Units are diagram-native (spec §5 exemption): pressure in hPa, temperatures and potential temperatures in degrees Celsius, mixing ratios in g/kg.

Attributes#

Classes#

Member

One isopleth polyline in tephigram x-y data space.

ResolvedOptions

A family's fully resolved settings snapshot.

FamilySpec

Static wiring of one isopleth family: builder plus conventions.

IsoplethFamily

One zoom-aware background isopleth family (spec §3.2).

Functions#

edge_crossings(→ numpy.typing.NDArray[numpy.float64])

Return where a member polyline meets one edge of the view.

isotherm_members(→ list[Member])

Build isotherm polylines (lines of constant temperature).

dry_adiabat_members(→ list[Member])

Build dry-adiabat polylines (lines of constant potential temperature).

isobar_members(→ list[Member])

Build isobar polylines (lines of constant pressure).

moist_adiabat_members(→ list[Member])

Build moist-adiabat (pseudoadiabat) polylines.

mixing_ratio_members(→ list[Member])

Build humidity mixing-ratio polylines (isohumes).

Module Contents#

tephpy.plotting.isopleths.EDGES: Final[tuple[str, Ellipsis]] = ('bottom', 'top', 'left', 'right')#
tephpy.plotting.isopleths.edge_crossings(xy: numpy.typing.NDArray[numpy.float64], edge: str, view: matplotlib.transforms.Bbox) numpy.typing.NDArray[numpy.float64][source]#

Return where a member polyline meets one edge of the view.

Pure numpy over the cached member geometry: each segment that straddles the edge’s level contributes one linearly interpolated crossing, kept only when it falls within the edge’s own span. A vertex lying exactly on the edge counts once — attributed to the segment it starts, or to the segment it ends when it is the polyline’s last vertex; a segment with a non-finite endpoint never counts. A member may cross the same edge more than once — a curved isobar leaving and re-entering the view — and every crossing is returned (spec §3.2).

Parameters:
xynumpy.ndarray

The member polyline, shape (n, 2) in tephigram data space.

edgestr

The edge to intersect, one of EDGES.

viewmatplotlib.transforms.Bbox

The current data-space view rectangle.

Returns:
numpy.ndarray

The along-edge coordinates of the crossings — x for "bottom" and "top", y for "left" and "right" — in polyline order; empty when the member does not reach the edge.

Raises:
TypeError

If edge is not one of EDGES.

class tephpy.plotting.isopleths.Member[source]#

One isopleth polyline in tephigram x-y data space.

value is the member’s isopleth value in the family’s native units (°C, hPa, or g/kg); xy is the (N, 2) float64 polyline.

value: float#
xy: numpy.typing.NDArray[numpy.float64]#
tephpy.plotting.isopleths.isotherm_members(values: numpy.typing.ArrayLike) list[Member][source]#

Build isotherm polylines (lines of constant temperature).

Isotherms are exactly straight in the tephigram plane; each member spans THETA_DOMAIN at its constant temperature.

Parameters:
valuesArrayLike

Member temperatures in degrees Celsius.

Returns:
list of Member

One member per value, in input order.

tephpy.plotting.isopleths.dry_adiabat_members(values: numpy.typing.ArrayLike) list[Member][source]#

Build dry-adiabat polylines (lines of constant potential temperature).

Dry adiabats are exactly straight in the tephigram plane, perpendicular to the isotherms; each member spans TEMPERATURE_DOMAIN at its constant potential temperature.

Parameters:
valuesArrayLike

Member potential temperatures in degrees Celsius.

Returns:
list of Member

One member per value, in input order.

tephpy.plotting.isopleths.isobar_members(values: numpy.typing.ArrayLike) list[Member][source]#

Build isobar polylines (lines of constant pressure).

Pressure is a derived curve on the tephigram, not an axis: each member traces Poisson’s equation across TEMPERATURE_DOMAIN at its constant pressure.

Parameters:
valuesArrayLike

Member pressures in hPa.

Returns:
list of Member

One member per value, in input order.

tephpy.plotting.isopleths.moist_adiabat_members(values: numpy.typing.ArrayLike, truncation: float = MOIST_ADIABAT_TRUNCATION) list[Member][source]#

Build moist-adiabat (pseudoadiabat) polylines.

Each member is labelled by its wet-bulb potential temperature — the temperature where the curve crosses P_REF — and is integrated over PRESSURE_DOMAIN with metpy.calc.moist_lapse() in a single vectorized call, then truncated where the temperature falls below truncation (the curves converge onto the dry adiabats; Met Office Factsheet 13 convention). Members with fewer than two remaining vertices are dropped.

Parameters:
valuesArrayLike

Member wet-bulb potential temperatures in degrees Celsius.

truncationfloat, default: MOIST_ADIABAT_TRUNCATION

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

Returns:
list of Member

One member per surviving value, in input order.

tephpy.plotting.isopleths.mixing_ratio_members(values: numpy.typing.ArrayLike) list[Member][source]#

Build humidity mixing-ratio polylines (isohumes).

For a mixing ratio w the member traces the dew-point temperature at which the saturation mixing ratio equals w, sampled across PRESSURE_DOMAIN: Td = dewpoint(vapor_pressure(p, w)) via MetPy.

Parameters:
valuesArrayLike

Member humidity mixing ratios in g/kg.

Returns:
list of Member

One member per value, in input order.

class tephpy.plotting.isopleths.ResolvedOptions[source]#

A family’s fully resolved settings snapshot.

Resolution precedence: accessor kwargs > tephpy.config > _constants (spec §3.5). values/interval of None mean the zoom-adaptive default ladder is in force. An empty label_edges means the family labels inline only, and an empty emphasis means no member is distinguished. The snapshot is immutable throughout: the class is frozen against rebinding, and emphasis – its one field with any container depth – is a read-only proxy at both levels over dicts the family copied for itself when it resolved.

values: tuple[float, Ellipsis] | None#
interval: float | None#
truncation: float | None#
color: str#
linewidth: float#
alpha: float#
labels: bool#
label_edges: tuple[str, Ellipsis]#
visible: bool#
emphasis: collections.abc.Mapping[float, collections.abc.Mapping[str, object]]#
class tephpy.plotting.isopleths.FamilySpec[source]#

Static wiring of one isopleth family: builder plus conventions.

Exactly one of (domain + steps) for interval families or (values + strides) for list families is set.

name: str#
builder: collections.abc.Callable[[numpy.typing.NDArray[numpy.float64], float | None], list[Member]]#
allowed: frozenset[str]#
color: str#
zorder: float#
domain: tuple[float, float] | None = None#
steps: tuple[tuple[float, float], Ellipsis] | None = None#
strides: tuple[tuple[float, int], Ellipsis] | None = None#
values: tuple[float, Ellipsis] | None = None#
truncation: float | None = None#
class tephpy.plotting.isopleths.IsoplethFamily(spec: FamilySpec, section: object, validate: collections.abc.Callable[[str, ResolvedOptions], None] | None = None, on_change: collections.abc.Callable[[], None] | None = None)[source]#

Bases: matplotlib.artist.Artist

One zoom-aware background isopleth family (spec §3.2).

Member polylines are built lazily on first draw and cached; each draw clips the cache to the current view rectangle, selects the members appropriate to the zoom level via the family’s convention ladder, and re-places the member labels. Settings resolve as accessor kwargs > tephpy.config > _constants, read when the family is created or reconfigured (spec §3.5); explicit values or interval fixes the member set and disables the zoom ladder.

Parameters:
specFamilySpec

The family’s static wiring (builder plus convention defaults).

sectionobject

The family’s tephpy.config section, read at creation and on configure().

validatecallable(), optional

Called with (family name, candidate options) whenever the options resolve; raising rejects the change. The owning axes passes its one-family-per-edge check here so the rejection lands inside this class’s rollback (spec §3.2).

on_changecallable(), optional

Called with no arguments after the options resolve successfully, whichever entry point resolved them. The owning axes passes its edge-ownership sync here so a direct configure() or set_visible() reaches it too (spec §3.2).

property options: ResolvedOptions#

The resolved settings snapshot currently in force.

Returns:
ResolvedOptions

The snapshot (accessor kwargs > tephpy.config > _constants) taken at creation or the last configure().

configure(**kwargs: object) None[source]#

Reconfigure the family (the accessor-kwargs precedence tier).

Re-reads tephpy.config now (spec §3.5 semantics), so any tier may move — a geometry option changed there takes effect on the next call whether or not that call mentions it, and the cached members are rebuilt whenever the resolved geometry differs. Passing None for an option removes any prior override so the value falls back to tephpy.config and then _constants. A call that raises leaves the family unchanged, and only a call that succeeds notifies the owner’s on_change — which is how an edge claimed or released here reaches the diagram (spec §3.2).

Parameters:
**kwargsobject

Options to override; the family’s accessor documents the accepted names.

Raises:
TypeError

If an option name is unknown for this family, if labels names an unknown placement, or if the owning axes rejects an edge claim already held by another family, or if emphasis is malformed.

ValueError

If an option value is invalid, e.g. a non-positive interval.

set_visible(b: bool) None[source]#

Show or hide the family, resolving its options as it goes.

The inherited Artist.set_visible only flips a flag; an isopleth family’s visibility is one of its resolved options, and an invisible family draws nothing so it holds no edge (spec §3.2). Hiding is therefore configure(visible=False), which releases any claimed edge, and showing is configure(visible=True), which reclaims it. Setting the value the family already has changes nothing, exactly as the base class does.

Parameters:
bbool

Whether the family is drawn.

Raises:
TypeError

If showing the family would reclaim an edge another family took while it was hidden; the family stays hidden (see configure()).

set_figure(fig: matplotlib.figure.Figure | matplotlib.figure.SubFigure) None[source]#

Propagate the owning figure to the managed child artists.

Parameters:
figmatplotlib.figure.Figure or matplotlib.figure.SubFigure

The figure the family belongs to.

draw(renderer: matplotlib.backend_bases.RendererBase) None[source]#

Draw the members visible in the current view.

Parameters:
renderermatplotlib.backend_bases.RendererBase

The active renderer.