mathause / mplotutils

helper functions for cartopy and matplotlib
MIT License
18 stars 3 forks source link

add dedicated function for hatching? #62

Open mathause opened 1 year ago

mathause commented 1 year ago

As for example in https://github.com/IPCC-WG1/Chapter-11/blob/376ab9a8e9ef6e788118a446f2e4b36d41163b2a/code/utils/plot.py#L167

mathause commented 1 year ago

That could look something like:

import numpy as np

import mplotutils as mpu
import matplotlib as mpl

import cartopy.crs as ccrs

def hatch_map(ax, da, hatch, label, linewidth=0.25, color="0.1"):
    """add hatch pattern to a cartopy map

    Parameters
    ----------
    ax : matplotlib.axes
        Axes to draw the hatch on.
    da : xr.DataArray
        DataArray with the hatch information. Data of value `True` is hatched.
    hatch : str
        Hatch pattern.
    label : str
        label for a legend entry
    linewidth : float, default: 0.25
        Default thickness of the hatching.
    color : matplotlib color, default: "0.1"
        Color of the hatch lines.

    Returns
    -------
    legend_handle : handle for the legend entry
    """

    if not isinstance(da, xr.DataArray):
        raise TypeError(f"Expected a xr.DataArray, got {type(da)}.")

    if not np.issubdtype(da.dtype, bool):
        raise TypeError(f"Expected a boolean array, got {da.dtype}")

    if not ds.ndim == 2:
        raise TypeError(f"Expected a 2D array, got {da.ndim=}")

    # dummy patch for the legend entry
    # TODO
    # legend_handle = mpl.patches.Patch(
    #     facecolor="none",
    #     ec=color,
    #     lw=linewidth,
    #     hatch=hatch,
    #     label=label,
    # )

    # contourf has trouble if no gridcell is True
    # if da.sum() == 0:
    #     return legend_handle

    da = mpu.cyclic_dataarray(da)

    # plot "True"
    levels = [0.95, 1.05]
    hatches = [hatch, ""]

    mpl.rcParams["hatch.linewidth"] = linewidth
    mpl.rcParams["hatch.color"] = color

    # unfortunately cannot set options via context manager
    # with mpl.rc_context({"hatch.linewidth": linewidth, "hatch.color": color}):
    da.plot.contourf(
        ax=ax,
        levels=levels,
        hatches=hatches,
        colors="none",
        extend="neither",
        transform=ccrs.PlateCarree(),
        add_colorbar=False,
    )

    # return legend_handle
mathause commented 8 months ago

should also add a hatch for non maps.

mathause commented 7 months ago

Might need three functions

mathause commented 2 months ago

We can add an empty patch for the legend...

xy = np.full((0, 2), fill_value=np.nan)
empty_patch_legend = mpl.patches.Polygon(
   xy,
   facecolor="none",
   ec="0.1",
   lw=1,
   hatch="///",
   label="label",
)
ax.add_patch(empty_patch_legend)

This avoids the manual creation of the legend...