tephpy.plotting.isopleths ========================= .. py:module:: tephpy.plotting.isopleths .. autoapi-nested-parse:: 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 :class:`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. .. !! processed by numpydoc !! Attributes ---------- .. autoapisummary:: tephpy.plotting.isopleths.EDGES Classes ------- .. autoapisummary:: tephpy.plotting.isopleths.Member tephpy.plotting.isopleths.ResolvedOptions tephpy.plotting.isopleths.FamilySpec tephpy.plotting.isopleths.IsoplethFamily Functions --------- .. autoapisummary:: tephpy.plotting.isopleths.edge_crossings tephpy.plotting.isopleths.isotherm_members tephpy.plotting.isopleths.dry_adiabat_members tephpy.plotting.isopleths.isobar_members tephpy.plotting.isopleths.moist_adiabat_members tephpy.plotting.isopleths.mixing_ratio_members Module Contents --------------- .. py:data:: EDGES :type: Final[tuple[str, Ellipsis]] :value: ('bottom', 'top', 'left', 'right') .. py:function:: edge_crossings(xy: numpy.typing.NDArray[numpy.float64], edge: str, view: matplotlib.transforms.Bbox) -> numpy.typing.NDArray[numpy.float64] 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: **xy** : :obj:`numpy.ndarray` The member polyline, shape ``(n, 2)`` in tephigram data space. **edge** : :class:`python:str` The edge to intersect, one of :data:`EDGES`. **view** : :obj:`matplotlib.transforms.Bbox` The current data-space view rectangle. :Returns: :obj:`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: :obj:`TypeError` If `edge` is not one of :data:`EDGES`. .. !! processed by numpydoc !! .. py:class:: Member 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. .. !! processed by numpydoc !! .. py:attribute:: value :type: float .. py:attribute:: xy :type: numpy.typing.NDArray[numpy.float64] .. py:function:: isotherm_members(values: numpy.typing.ArrayLike) -> list[Member] 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: **values** : :obj:`ArrayLike ` Member temperatures in degrees Celsius. :Returns: :class:`python:list` of :obj:`Member ` One member per value, in input order. .. !! processed by numpydoc !! .. py:function:: dry_adiabat_members(values: numpy.typing.ArrayLike) -> list[Member] 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: **values** : :obj:`ArrayLike ` Member potential temperatures in degrees Celsius. :Returns: :class:`python:list` of :obj:`Member ` One member per value, in input order. .. !! processed by numpydoc !! .. py:function:: isobar_members(values: numpy.typing.ArrayLike) -> list[Member] 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: **values** : :obj:`ArrayLike ` Member pressures in hPa. :Returns: :class:`python:list` of :obj:`Member ` One member per value, in input order. .. !! processed by numpydoc !! .. py:function:: moist_adiabat_members(values: numpy.typing.ArrayLike, truncation: float = MOIST_ADIABAT_TRUNCATION) -> list[Member] 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 :func:`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: **values** : :obj:`ArrayLike ` Member wet-bulb potential temperatures in degrees Celsius. **truncation** : :class:`python:float`, default: :obj:`MOIST_ADIABAT_TRUNCATION` Temperature (°C) below which the curves are truncated. :Returns: :class:`python:list` of :obj:`Member ` One member per surviving value, in input order. .. !! processed by numpydoc !! .. py:function:: mixing_ratio_members(values: numpy.typing.ArrayLike) -> list[Member] 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: **values** : :obj:`ArrayLike ` Member humidity mixing ratios in g/kg. :Returns: :class:`python:list` of :obj:`Member ` One member per value, in input order. .. !! processed by numpydoc !! .. py:class:: ResolvedOptions 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. .. !! processed by numpydoc !! .. py:attribute:: values :type: tuple[float, Ellipsis] | None .. py:attribute:: interval :type: float | None .. py:attribute:: truncation :type: float | None .. py:attribute:: color :type: str .. py:attribute:: linewidth :type: float .. py:attribute:: alpha :type: float .. py:attribute:: labels :type: bool .. py:attribute:: label_edges :type: tuple[str, Ellipsis] .. py:attribute:: visible :type: bool .. py:attribute:: emphasis :type: collections.abc.Mapping[float, collections.abc.Mapping[str, object]] .. py:class:: FamilySpec 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. .. !! processed by numpydoc !! .. py:attribute:: name :type: str .. py:attribute:: builder :type: collections.abc.Callable[[numpy.typing.NDArray[numpy.float64], float | None], list[Member]] .. py:attribute:: allowed :type: frozenset[str] .. py:attribute:: color :type: str .. py:attribute:: zorder :type: float .. py:attribute:: domain :type: tuple[float, float] | None :value: None .. py:attribute:: steps :type: tuple[tuple[float, float], Ellipsis] | None :value: None .. py:attribute:: strides :type: tuple[tuple[float, int], Ellipsis] | None :value: None .. py:attribute:: values :type: tuple[float, Ellipsis] | None :value: None .. py:attribute:: truncation :type: float | None :value: None .. py:class:: IsoplethFamily(spec: FamilySpec, section: object, validate: collections.abc.Callable[[str, ResolvedOptions], None] | None = None, on_change: collections.abc.Callable[[], None] | None = None) Bases: :py:obj:`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: **spec** : :obj:`FamilySpec ` The family's static wiring (builder plus convention defaults). **section** : :obj:`object` The family's ``tephpy.config`` section, read at creation and on :meth:`configure`. **validate** : :func:`python:callable`, 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_change** : :func:`python:callable`, 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 :meth:`configure` or :meth:`set_visible` reaches it too (spec §3.2). .. !! processed by numpydoc !! .. py:property:: options :type: ResolvedOptions The resolved settings snapshot currently in force. :Returns: :obj:`ResolvedOptions` The snapshot (accessor kwargs > ``tephpy.config`` > ``_constants``) taken at creation or the last :meth:`configure`. .. !! processed by numpydoc !! .. py:method:: configure(**kwargs: object) -> None 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: **\*\*kwargs** : :obj:`object` Options to override; the family's accessor documents the accepted names. :Raises: :obj:`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. :obj:`ValueError` If an option value is invalid, e.g. a non-positive ``interval``. .. !! processed by numpydoc !! .. py:method:: set_visible(b: bool) -> None 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: **b** : :ref:`bool ` Whether the family is drawn. :Raises: :obj:`TypeError` If showing the family would reclaim an edge another family took while it was hidden; the family stays hidden (see :meth:`configure`). .. !! processed by numpydoc !! .. py:method:: set_figure(fig: matplotlib.figure.Figure | matplotlib.figure.SubFigure) -> None Propagate the owning figure to the managed child artists. :Parameters: **fig** : :obj:`matplotlib.figure.Figure` or :obj:`matplotlib.figure.SubFigure` The figure the family belongs to. .. !! processed by numpydoc !! .. py:method:: draw(renderer: matplotlib.backend_bases.RendererBase) -> None Draw the members visible in the current view. :Parameters: **renderer** : :obj:`matplotlib.backend_bases.RendererBase` The active renderer. .. !! processed by numpydoc !!