Skip to content

Render options (grouped parameters)#

Glyph plot() / animate() calls take these typed objects in place of loose keyword arguments. Each bundles a family of related options and exposes to_options(), which the glyph flattens into its render settings — only the fields you set are applied, so a group never clobbers a glyph's own defaults. (The ArrayGlyph-specific input objects — RgbBands, PointOverlay, FrameLabel, PanelLabels — are documented on the ArrayGlyph page.)

ColorScaling#

The colour-scale (norm) selector: plot(color=ColorScaling.power(gamma=0.5)), ColorScaling.sym_log(...), ColorScaling.boundary(bounds=[...]), ColorScaling.midpoint(at=0), ColorScaling.linear().

cleopatra.styling.scaling.ColorScaling dataclass #

The colour-scale group: a scale kind plus its scale-specific knobs.

Prefer the variant constructors (linear, power, sym_log, boundary, midpoint) over the raw dataclass -- each exposes only the fields its scale uses, so nonsensical combinations (e.g. a midpoint on a linear scale) cannot be built.

Attributes:

Name Type Description
kind ColorScale

The scale kind (cleopatra.styling.styles.ColorScale).

gamma float

Exponent for the power scale. Ignored by other kinds.

line_threshold float

Linear-region threshold for sym-lognorm.

line_scale float

Linear-region scale factor for sym-lognorm.

bounds list[float] | None

Explicit bin edges for boundary-norm.

center float

Centre value for the midpoint scale (the value pinned to the colormap centre). Named center rather than midpoint so the field does not shadow the midpoint() variant constructor.

Source code in src/cleopatra/styling/scaling.py
@dataclass(frozen=True)
class ColorScaling:
    """The colour-scale group: a scale kind plus its scale-specific knobs.

    Prefer the variant constructors (`linear`, `power`, `sym_log`,
    `boundary`, `midpoint`) over the raw dataclass -- each exposes only
    the fields its scale uses, so nonsensical combinations (e.g. a
    `midpoint` on a `linear` scale) cannot be built.

    Attributes:
        kind: The scale kind (`cleopatra.styling.styles.ColorScale`).
        gamma: Exponent for the `power` scale. Ignored by other kinds.
        line_threshold: Linear-region threshold for `sym-lognorm`.
        line_scale: Linear-region scale factor for `sym-lognorm`.
        bounds: Explicit bin edges for `boundary-norm`.
        center: Centre value for the `midpoint` scale (the value pinned to
            the colormap centre). Named `center` rather than `midpoint` so
            the field does not shadow the `midpoint()` variant constructor.
    """

    kind: ColorScale = ColorScale.LINEAR
    gamma: float = 0.5
    line_threshold: float = 0.0001
    line_scale: float = 0.001
    bounds: list[float] | None = None
    center: float = 0

    @classmethod
    def linear(cls) -> ColorScaling:
        """A plain linear colour scale (matplotlib's default norm).

        Examples:
            - The linear scale carries no extra knobs:
                ```python
                >>> from cleopatra.styling.scaling import ColorScaling
                >>> ColorScaling.linear().kind.value
                'linear'

                ```
        """
        return cls(kind=ColorScale.LINEAR)

    @classmethod
    def power(cls, gamma: float = 0.5) -> ColorScaling:
        """A power-law (`PowerNorm`) colour scale.

        Args:
            gamma: The power exponent. Defaults to `0.5`.

        Examples:
            - Only `gamma` is exposed:
                ```python
                >>> from cleopatra.styling.scaling import ColorScaling
                >>> ColorScaling.power(gamma=2.0).gamma
                2.0

                ```
        """
        return cls(kind=ColorScale.POWER, gamma=gamma)

    @classmethod
    def sym_log(cls, threshold: float = 0.0001, scale: float = 0.001) -> ColorScaling:
        """A symmetric-log (`SymLogNorm`) colour scale.

        Args:
            threshold: The linear-region half-width (`linthresh`).
                Defaults to `0.0001`.
            scale: The linear-region scale factor (`linscale`). Defaults
                to `0.001`.

        Examples:
            - Exposes the two `sym-lognorm` knobs:
                ```python
                >>> from cleopatra.styling.scaling import ColorScaling
                >>> s = ColorScaling.sym_log(threshold=0.01, scale=0.1)
                >>> (s.line_threshold, s.line_scale)
                (0.01, 0.1)

                ```
        """
        return cls(kind=ColorScale.SYM_LOGNORM, line_threshold=threshold, line_scale=scale)

    @classmethod
    def boundary(cls, bounds: list[float] | None = None) -> ColorScaling:
        """A discrete (`BoundaryNorm`) colour scale.

        Args:
            bounds: Explicit bin edges. When `None`, the edges are derived
                from `levels` (if set) or the tick positions at render
                time.

        Examples:
            - Explicit edges are carried through:
                ```python
                >>> from cleopatra.styling.scaling import ColorScaling
                >>> ColorScaling.boundary([0, 1, 5, 10]).bounds
                [0, 1, 5, 10]

                ```
        """
        return cls(kind=ColorScale.BOUNDARY_NORM, bounds=bounds)

    @classmethod
    def midpoint(cls, at: float = 0) -> ColorScaling:
        """A midpoint-anchored diverging colour scale.

        Args:
            at: The value pinned to the colormap centre. Defaults to `0`.

        Examples:
            - Anchor the colormap centre at a chosen value:
                ```python
                >>> from cleopatra.styling.scaling import ColorScaling
                >>> ColorScaling.midpoint(at=100).center
                100

                ```
        """
        return cls(kind=ColorScale.MIDPOINT, center=at)

    @classmethod
    def from_options(cls, options: dict[str, Any]) -> ColorScaling:
        """Build a `ColorScaling` from a flat `default_options` dict.

        The bridge between the legacy flat-key storage every glyph still
        uses internally and this object's behaviour. Reads the six
        colour-scale keys, validating `color_scale` with the same
        actionable error the flat path raised.

        Args:
            options: A glyph's `default_options` (or any mapping carrying
                the colour-scale keys).

        Returns:
            ColorScaling: The reconstructed scale object.

        Raises:
            ValueError: If `options["color_scale"]` is not a recognised
                `cleopatra.styling.styles.ColorScale` value.

        Examples:
            - Round-trips the flat keys back into an object:
                ```python
                >>> from cleopatra.styling.scaling import ColorScaling
                >>> ColorScaling.from_options({"color_scale": "power", "gamma": 0.7}).gamma
                0.7

                ```
        """
        raw_scale = options.get("color_scale", _SCALE_DEFAULTS["color_scale"])
        try:
            kind = ColorScale(raw_scale)
        except ValueError as e:
            valid = ", ".join(repr(m.value) for m in ColorScale)
            raise ValueError(
                f"Invalid color_scale {raw_scale!r}. Expected one of "
                f"{valid} (or a cleopatra.styling.styles.ColorScale member)."
            ) from e
        return cls(
            kind=kind,
            gamma=options.get("gamma", _SCALE_DEFAULTS["gamma"]),
            line_threshold=options.get("line_threshold", _SCALE_DEFAULTS["line_threshold"]),
            line_scale=options.get("line_scale", _SCALE_DEFAULTS["line_scale"]),
            bounds=options.get("bounds", _SCALE_DEFAULTS["bounds"]),
            center=options.get("midpoint", _SCALE_DEFAULTS["midpoint"]),
        )

    def to_options(self) -> dict[str, Any]:
        """Flatten back to the `default_options` keys the engine reads.

        Returns:
            dict: The six colour-scale keys, with `color_scale` as the
                plain string value.

        Examples:
            - Emits the flat keys a glyph merges into `default_options`:
                ```python
                >>> from cleopatra.styling.scaling import ColorScaling
                >>> ColorScaling.power(gamma=0.7).to_options()["color_scale"]
                'power'

                ```
        """
        return {
            "color_scale": self.kind.value,
            "gamma": self.gamma,
            "line_threshold": self.line_threshold,
            "line_scale": self.line_scale,
            "bounds": self.bounds,
            "midpoint": self.center,
        }

    def build_norm(
        self,
        ticks: np.ndarray,
        levels: int | list[float] | np.ndarray | None = None,
        extend: str | None = None,
    ) -> tuple[colors.Normalize | None, dict[str, Any]]:
        """Build the matplotlib norm and colorbar keyword arguments.

        The colour-scale logic that used to live in
        `Glyph._create_norm_and_cbar_kw`. `vmin`/`vmax` are read from the
        first and last tick; `levels` and `extend` are cross-group inputs
        (contour discretisation and colorbar arrow extension) passed in by
        the caller.

        Args:
            ticks: Tick positions for the colorbar; `ticks[0]`/`ticks[-1]`
                supply `vmin`/`vmax`.
            levels: Optional discretisation for the `linear`/`boundary`
                kinds (int count or explicit edges).
            extend: Colorbar arrow extension. When `None`, auto-resolves to
                `"both"` if `levels` is set, else `"neither"`.

        Returns:
            tuple[Normalize or None, dict]: The norm (`None` for a plain
                linear scale) and the colorbar keyword arguments.

        Examples:
            - A linear scale with no levels yields no norm and passes the
                ticks straight through:
                ```python
                >>> import numpy as np
                >>> from cleopatra.styling.scaling import ColorScaling
                >>> norm, cbar_kw = ColorScaling.linear().build_norm(
                ...     np.array([0.0, 5.0, 10.0])
                ... )
                >>> norm is None
                True
                >>> cbar_kw["extend"]
                'neither'

                ```
            - `levels` on the linear scale builds a `BoundaryNorm` and
                defaults `extend` to `"both"`:
                ```python
                >>> import numpy as np
                >>> from cleopatra.styling.scaling import ColorScaling
                >>> norm, cbar_kw = ColorScaling.linear().build_norm(
                ...     np.array([0.0, 5.0, 10.0]), levels=5
                ... )
                >>> norm is None
                False
                >>> cbar_kw["extend"]
                'both'

                ```
        """
        vmin = ticks[0]
        vmax = ticks[-1]
        bounds_from_levels = levels_to_bounds(levels, vmin, vmax)

        norm: colors.Normalize | None
        cbar_kw: dict[str, Any]
        if self.kind == ColorScale.LINEAR:
            norm, cbar_kw = self._linear_norm(ticks, bounds_from_levels)
        elif self.kind == ColorScale.POWER:
            norm = colors.PowerNorm(gamma=self.gamma, vmin=vmin, vmax=vmax)
            cbar_kw = {"ticks": ticks}
        elif self.kind == ColorScale.SYM_LOGNORM:
            norm = colors.SymLogNorm(
                linthresh=self.line_threshold,
                linscale=self.line_scale,
                base=np.e,
                vmin=vmin,
                vmax=vmax,
            )
            cbar_kw = {"ticks": ticks, "format": LogFormatter(10, labelOnlyBase=False)}
        elif self.kind == ColorScale.BOUNDARY_NORM:
            norm, cbar_kw = self._boundary_norm(ticks, bounds_from_levels)
        elif self.kind == ColorScale.MIDPOINT:
            norm = MidpointNormalize(midpoint=self.center, vmin=vmin, vmax=vmax)
            cbar_kw = {"ticks": ticks}
        else:  # pragma: no cover - a ColorScale member without a branch
            raise ValueError(
                f"No norm branch implemented for color_scale={self.kind!r}."
            )

        if extend is None:
            extend = "both" if levels is not None else "neither"
        cbar_kw["extend"] = extend
        return norm, cbar_kw

    def _linear_norm(
        self, ticks: np.ndarray, bounds_from_levels: np.ndarray | None
    ) -> tuple[colors.Normalize | None, dict[str, Any]]:
        """Linear-scale norm: a `BoundaryNorm` when `levels` are given, else no norm."""
        if bounds_from_levels is not None:
            norm = colors.BoundaryNorm(boundaries=bounds_from_levels, ncolors=256)
            return norm, {"ticks": bounds_from_levels}
        return None, {"ticks": ticks}

    def _boundary_norm(
        self, ticks: np.ndarray, bounds_from_levels: np.ndarray | None
    ) -> tuple[colors.Normalize, dict[str, Any]]:
        """Explicit-bounds norm: own `bounds` win, then `levels`, then the ticks."""
        if self.bounds:
            bounds = self.bounds
        elif bounds_from_levels is not None:
            bounds = bounds_from_levels
        else:
            bounds = ticks
        return colors.BoundaryNorm(boundaries=bounds, ncolors=256), {"ticks": bounds}

boundary(bounds=None) classmethod #

A discrete (BoundaryNorm) colour scale.

Parameters:

Name Type Description Default
bounds list[float] | None

Explicit bin edges. When None, the edges are derived from levels (if set) or the tick positions at render time.

None

Examples:

  • Explicit edges are carried through:
    >>> from cleopatra.styling.scaling import ColorScaling
    >>> ColorScaling.boundary([0, 1, 5, 10]).bounds
    [0, 1, 5, 10]
    
Source code in src/cleopatra/styling/scaling.py
@classmethod
def boundary(cls, bounds: list[float] | None = None) -> ColorScaling:
    """A discrete (`BoundaryNorm`) colour scale.

    Args:
        bounds: Explicit bin edges. When `None`, the edges are derived
            from `levels` (if set) or the tick positions at render
            time.

    Examples:
        - Explicit edges are carried through:
            ```python
            >>> from cleopatra.styling.scaling import ColorScaling
            >>> ColorScaling.boundary([0, 1, 5, 10]).bounds
            [0, 1, 5, 10]

            ```
    """
    return cls(kind=ColorScale.BOUNDARY_NORM, bounds=bounds)

build_norm(ticks, levels=None, extend=None) #

Build the matplotlib norm and colorbar keyword arguments.

The colour-scale logic that used to live in Glyph._create_norm_and_cbar_kw. vmin/vmax are read from the first and last tick; levels and extend are cross-group inputs (contour discretisation and colorbar arrow extension) passed in by the caller.

Parameters:

Name Type Description Default
ticks ndarray

Tick positions for the colorbar; ticks[0]/ticks[-1] supply vmin/vmax.

required
levels int | list[float] | ndarray | None

Optional discretisation for the linear/boundary kinds (int count or explicit edges).

None
extend str | None

Colorbar arrow extension. When None, auto-resolves to "both" if levels is set, else "neither".

None

Returns:

Type Description
tuple[Normalize | None, dict[str, Any]]

tuple[Normalize or None, dict]: The norm (None for a plain linear scale) and the colorbar keyword arguments.

Examples:

  • A linear scale with no levels yields no norm and passes the ticks straight through:
    >>> import numpy as np
    >>> from cleopatra.styling.scaling import ColorScaling
    >>> norm, cbar_kw = ColorScaling.linear().build_norm(
    ...     np.array([0.0, 5.0, 10.0])
    ... )
    >>> norm is None
    True
    >>> cbar_kw["extend"]
    'neither'
    
  • levels on the linear scale builds a BoundaryNorm and defaults extend to "both":
    >>> import numpy as np
    >>> from cleopatra.styling.scaling import ColorScaling
    >>> norm, cbar_kw = ColorScaling.linear().build_norm(
    ...     np.array([0.0, 5.0, 10.0]), levels=5
    ... )
    >>> norm is None
    False
    >>> cbar_kw["extend"]
    'both'
    
Source code in src/cleopatra/styling/scaling.py
def build_norm(
    self,
    ticks: np.ndarray,
    levels: int | list[float] | np.ndarray | None = None,
    extend: str | None = None,
) -> tuple[colors.Normalize | None, dict[str, Any]]:
    """Build the matplotlib norm and colorbar keyword arguments.

    The colour-scale logic that used to live in
    `Glyph._create_norm_and_cbar_kw`. `vmin`/`vmax` are read from the
    first and last tick; `levels` and `extend` are cross-group inputs
    (contour discretisation and colorbar arrow extension) passed in by
    the caller.

    Args:
        ticks: Tick positions for the colorbar; `ticks[0]`/`ticks[-1]`
            supply `vmin`/`vmax`.
        levels: Optional discretisation for the `linear`/`boundary`
            kinds (int count or explicit edges).
        extend: Colorbar arrow extension. When `None`, auto-resolves to
            `"both"` if `levels` is set, else `"neither"`.

    Returns:
        tuple[Normalize or None, dict]: The norm (`None` for a plain
            linear scale) and the colorbar keyword arguments.

    Examples:
        - A linear scale with no levels yields no norm and passes the
            ticks straight through:
            ```python
            >>> import numpy as np
            >>> from cleopatra.styling.scaling import ColorScaling
            >>> norm, cbar_kw = ColorScaling.linear().build_norm(
            ...     np.array([0.0, 5.0, 10.0])
            ... )
            >>> norm is None
            True
            >>> cbar_kw["extend"]
            'neither'

            ```
        - `levels` on the linear scale builds a `BoundaryNorm` and
            defaults `extend` to `"both"`:
            ```python
            >>> import numpy as np
            >>> from cleopatra.styling.scaling import ColorScaling
            >>> norm, cbar_kw = ColorScaling.linear().build_norm(
            ...     np.array([0.0, 5.0, 10.0]), levels=5
            ... )
            >>> norm is None
            False
            >>> cbar_kw["extend"]
            'both'

            ```
    """
    vmin = ticks[0]
    vmax = ticks[-1]
    bounds_from_levels = levels_to_bounds(levels, vmin, vmax)

    norm: colors.Normalize | None
    cbar_kw: dict[str, Any]
    if self.kind == ColorScale.LINEAR:
        norm, cbar_kw = self._linear_norm(ticks, bounds_from_levels)
    elif self.kind == ColorScale.POWER:
        norm = colors.PowerNorm(gamma=self.gamma, vmin=vmin, vmax=vmax)
        cbar_kw = {"ticks": ticks}
    elif self.kind == ColorScale.SYM_LOGNORM:
        norm = colors.SymLogNorm(
            linthresh=self.line_threshold,
            linscale=self.line_scale,
            base=np.e,
            vmin=vmin,
            vmax=vmax,
        )
        cbar_kw = {"ticks": ticks, "format": LogFormatter(10, labelOnlyBase=False)}
    elif self.kind == ColorScale.BOUNDARY_NORM:
        norm, cbar_kw = self._boundary_norm(ticks, bounds_from_levels)
    elif self.kind == ColorScale.MIDPOINT:
        norm = MidpointNormalize(midpoint=self.center, vmin=vmin, vmax=vmax)
        cbar_kw = {"ticks": ticks}
    else:  # pragma: no cover - a ColorScale member without a branch
        raise ValueError(
            f"No norm branch implemented for color_scale={self.kind!r}."
        )

    if extend is None:
        extend = "both" if levels is not None else "neither"
    cbar_kw["extend"] = extend
    return norm, cbar_kw

from_options(options) classmethod #

Build a ColorScaling from a flat default_options dict.

The bridge between the legacy flat-key storage every glyph still uses internally and this object's behaviour. Reads the six colour-scale keys, validating color_scale with the same actionable error the flat path raised.

Parameters:

Name Type Description Default
options dict[str, Any]

A glyph's default_options (or any mapping carrying the colour-scale keys).

required

Returns:

Name Type Description
ColorScaling ColorScaling

The reconstructed scale object.

Raises:

Type Description
ValueError

If options["color_scale"] is not a recognised cleopatra.styling.styles.ColorScale value.

Examples:

  • Round-trips the flat keys back into an object:
    >>> from cleopatra.styling.scaling import ColorScaling
    >>> ColorScaling.from_options({"color_scale": "power", "gamma": 0.7}).gamma
    0.7
    
Source code in src/cleopatra/styling/scaling.py
@classmethod
def from_options(cls, options: dict[str, Any]) -> ColorScaling:
    """Build a `ColorScaling` from a flat `default_options` dict.

    The bridge between the legacy flat-key storage every glyph still
    uses internally and this object's behaviour. Reads the six
    colour-scale keys, validating `color_scale` with the same
    actionable error the flat path raised.

    Args:
        options: A glyph's `default_options` (or any mapping carrying
            the colour-scale keys).

    Returns:
        ColorScaling: The reconstructed scale object.

    Raises:
        ValueError: If `options["color_scale"]` is not a recognised
            `cleopatra.styling.styles.ColorScale` value.

    Examples:
        - Round-trips the flat keys back into an object:
            ```python
            >>> from cleopatra.styling.scaling import ColorScaling
            >>> ColorScaling.from_options({"color_scale": "power", "gamma": 0.7}).gamma
            0.7

            ```
    """
    raw_scale = options.get("color_scale", _SCALE_DEFAULTS["color_scale"])
    try:
        kind = ColorScale(raw_scale)
    except ValueError as e:
        valid = ", ".join(repr(m.value) for m in ColorScale)
        raise ValueError(
            f"Invalid color_scale {raw_scale!r}. Expected one of "
            f"{valid} (or a cleopatra.styling.styles.ColorScale member)."
        ) from e
    return cls(
        kind=kind,
        gamma=options.get("gamma", _SCALE_DEFAULTS["gamma"]),
        line_threshold=options.get("line_threshold", _SCALE_DEFAULTS["line_threshold"]),
        line_scale=options.get("line_scale", _SCALE_DEFAULTS["line_scale"]),
        bounds=options.get("bounds", _SCALE_DEFAULTS["bounds"]),
        center=options.get("midpoint", _SCALE_DEFAULTS["midpoint"]),
    )

linear() classmethod #

A plain linear colour scale (matplotlib's default norm).

Examples:

  • The linear scale carries no extra knobs:
    >>> from cleopatra.styling.scaling import ColorScaling
    >>> ColorScaling.linear().kind.value
    'linear'
    
Source code in src/cleopatra/styling/scaling.py
@classmethod
def linear(cls) -> ColorScaling:
    """A plain linear colour scale (matplotlib's default norm).

    Examples:
        - The linear scale carries no extra knobs:
            ```python
            >>> from cleopatra.styling.scaling import ColorScaling
            >>> ColorScaling.linear().kind.value
            'linear'

            ```
    """
    return cls(kind=ColorScale.LINEAR)

midpoint(at=0) classmethod #

A midpoint-anchored diverging colour scale.

Parameters:

Name Type Description Default
at float

The value pinned to the colormap centre. Defaults to 0.

0

Examples:

  • Anchor the colormap centre at a chosen value:
    >>> from cleopatra.styling.scaling import ColorScaling
    >>> ColorScaling.midpoint(at=100).center
    100
    
Source code in src/cleopatra/styling/scaling.py
@classmethod
def midpoint(cls, at: float = 0) -> ColorScaling:
    """A midpoint-anchored diverging colour scale.

    Args:
        at: The value pinned to the colormap centre. Defaults to `0`.

    Examples:
        - Anchor the colormap centre at a chosen value:
            ```python
            >>> from cleopatra.styling.scaling import ColorScaling
            >>> ColorScaling.midpoint(at=100).center
            100

            ```
    """
    return cls(kind=ColorScale.MIDPOINT, center=at)

power(gamma=0.5) classmethod #

A power-law (PowerNorm) colour scale.

Parameters:

Name Type Description Default
gamma float

The power exponent. Defaults to 0.5.

0.5

Examples:

  • Only gamma is exposed:
    >>> from cleopatra.styling.scaling import ColorScaling
    >>> ColorScaling.power(gamma=2.0).gamma
    2.0
    
Source code in src/cleopatra/styling/scaling.py
@classmethod
def power(cls, gamma: float = 0.5) -> ColorScaling:
    """A power-law (`PowerNorm`) colour scale.

    Args:
        gamma: The power exponent. Defaults to `0.5`.

    Examples:
        - Only `gamma` is exposed:
            ```python
            >>> from cleopatra.styling.scaling import ColorScaling
            >>> ColorScaling.power(gamma=2.0).gamma
            2.0

            ```
    """
    return cls(kind=ColorScale.POWER, gamma=gamma)

sym_log(threshold=0.0001, scale=0.001) classmethod #

A symmetric-log (SymLogNorm) colour scale.

Parameters:

Name Type Description Default
threshold float

The linear-region half-width (linthresh). Defaults to 0.0001.

0.0001
scale float

The linear-region scale factor (linscale). Defaults to 0.001.

0.001

Examples:

  • Exposes the two sym-lognorm knobs:
    >>> from cleopatra.styling.scaling import ColorScaling
    >>> s = ColorScaling.sym_log(threshold=0.01, scale=0.1)
    >>> (s.line_threshold, s.line_scale)
    (0.01, 0.1)
    
Source code in src/cleopatra/styling/scaling.py
@classmethod
def sym_log(cls, threshold: float = 0.0001, scale: float = 0.001) -> ColorScaling:
    """A symmetric-log (`SymLogNorm`) colour scale.

    Args:
        threshold: The linear-region half-width (`linthresh`).
            Defaults to `0.0001`.
        scale: The linear-region scale factor (`linscale`). Defaults
            to `0.001`.

    Examples:
        - Exposes the two `sym-lognorm` knobs:
            ```python
            >>> from cleopatra.styling.scaling import ColorScaling
            >>> s = ColorScaling.sym_log(threshold=0.01, scale=0.1)
            >>> (s.line_threshold, s.line_scale)
            (0.01, 0.1)

            ```
    """
    return cls(kind=ColorScale.SYM_LOGNORM, line_threshold=threshold, line_scale=scale)

to_options() #

Flatten back to the default_options keys the engine reads.

Returns:

Name Type Description
dict dict[str, Any]

The six colour-scale keys, with color_scale as the plain string value.

Examples:

  • Emits the flat keys a glyph merges into default_options:
    >>> from cleopatra.styling.scaling import ColorScaling
    >>> ColorScaling.power(gamma=0.7).to_options()["color_scale"]
    'power'
    
Source code in src/cleopatra/styling/scaling.py
def to_options(self) -> dict[str, Any]:
    """Flatten back to the `default_options` keys the engine reads.

    Returns:
        dict: The six colour-scale keys, with `color_scale` as the
            plain string value.

    Examples:
        - Emits the flat keys a glyph merges into `default_options`:
            ```python
            >>> from cleopatra.styling.scaling import ColorScaling
            >>> ColorScaling.power(gamma=0.7).to_options()["color_scale"]
            'power'

            ```
    """
    return {
        "color_scale": self.kind.value,
        "gamma": self.gamma,
        "line_threshold": self.line_threshold,
        "line_scale": self.line_scale,
        "bounds": self.bounds,
        "midpoint": self.center,
    }

Contour#

Discrete colour levels and inline contour labels: plot(contour=Contour(levels=6, labels=True)).

cleopatra.styling.params.Contour dataclass #

Contour discretisation and inline-label options.

Groups the levels / labels / label_kw options. levels applies to every colour-mapped glyph that discretises a scale (array, vector, flow, polygon, scatter, kde); labels / label_kw draw inline numeric labels on isolines and are honoured only by the glyphs that render contour lines (ArrayGlyph with kind="contour", MeshGlyph node contours).

Attributes:

Name Type Description
levels int | Sequence[float] | None

Discrete colour levels -- an int count or an explicit sequence of edges. None leaves the scale continuous.

labels bool | None

Draw inline numeric labels on isolines. None leaves the glyph default (False).

label_kw dict[str, Any] | None

Extra keyword arguments forwarded to ax.clabel when labels is true.

Examples:

  • Only the set fields are emitted:
    >>> from cleopatra.styling.params import Contour
    >>> Contour(levels=5).to_options()
    {'levels': 5}
    >>> Contour(labels=True, label_kw={"fontsize": 8}).to_options()
    {'labels': True, 'label_kw': {'fontsize': 8}}
    
Source code in src/cleopatra/styling/params.py
@dataclass(frozen=True)
class Contour:
    """Contour discretisation and inline-label options.

    Groups the `levels` / `labels` / `label_kw` options. `levels` applies
    to every colour-mapped glyph that discretises a scale (array, vector,
    flow, polygon, scatter, kde); `labels` / `label_kw` draw inline numeric
    labels on isolines and are honoured only by the glyphs that render
    contour lines (`ArrayGlyph` with `kind="contour"`, `MeshGlyph` node
    contours).

    Attributes:
        levels: Discrete colour levels -- an int count or an explicit
            sequence of edges. `None` leaves the scale continuous.
        labels: Draw inline numeric labels on isolines. `None` leaves the
            glyph default (`False`).
        label_kw: Extra keyword arguments forwarded to `ax.clabel` when
            `labels` is true.

    Examples:
        - Only the set fields are emitted:
            ```python
            >>> from cleopatra.styling.params import Contour
            >>> Contour(levels=5).to_options()
            {'levels': 5}
            >>> Contour(labels=True, label_kw={"fontsize": 8}).to_options()
            {'labels': True, 'label_kw': {'fontsize': 8}}

            ```
    """

    levels: int | Sequence[float] | None = None
    labels: bool | None = None
    label_kw: dict[str, Any] | None = None

    def to_options(self) -> dict[str, Any]:
        """Flatten the explicitly-set fields into `default_options` keys.

        Returns:
            dict: `levels` / `labels` / `label_kw` for the fields that were
                set (non-`None`); an empty dict when nothing was set.
        """
        options: dict[str, Any] = {}
        if self.levels is not None:
            options["levels"] = self.levels
        if self.labels is not None:
            options["labels"] = self.labels
        if self.label_kw is not None:
            options["label_kw"] = self.label_kw
        return options

to_options() #

Flatten the explicitly-set fields into default_options keys.

Returns:

Name Type Description
dict dict[str, Any]

levels / labels / label_kw for the fields that were set (non-None); an empty dict when nothing was set.

Source code in src/cleopatra/styling/params.py
def to_options(self) -> dict[str, Any]:
    """Flatten the explicitly-set fields into `default_options` keys.

    Returns:
        dict: `levels` / `labels` / `label_kw` for the fields that were
            set (non-`None`); an empty dict when nothing was set.
    """
    options: dict[str, Any] = {}
    if self.levels is not None:
        options["levels"] = self.levels
    if self.labels is not None:
        options["labels"] = self.labels
    if self.label_kw is not None:
        options["label_kw"] = self.label_kw
    return options

CellValues#

Per-cell value-text overlay (ArrayGlyph): plot(cells=CellValues(show=True, size=10)).

cleopatra.styling.params.CellValues dataclass #

Per-cell value-text display options (ArrayGlyph only).

Groups the display_cell_value / num_size / background_color_threshold options that overlay each cell's numeric value on an imshow / pcolormesh render.

Attributes:

Name Type Description
show bool | None

Draw each cell's value as text. None leaves the glyph default (False).

size int | None

Font size of the cell-value text. None leaves the default.

background_threshold float | None

Value above which the text switches to the light colour (for contrast against a dark cell). None leaves the default (max(array) / 2).

Examples:

  • Enable the overlay with a custom font size:
    >>> from cleopatra.styling.params import CellValues
    >>> CellValues(show=True, size=10).to_options()
    {'display_cell_value': True, 'num_size': 10}
    
Source code in src/cleopatra/styling/params.py
@dataclass(frozen=True)
class CellValues:
    """Per-cell value-text display options (`ArrayGlyph` only).

    Groups the `display_cell_value` / `num_size` /
    `background_color_threshold` options that overlay each cell's numeric
    value on an `imshow` / `pcolormesh` render.

    Attributes:
        show: Draw each cell's value as text. `None` leaves the glyph
            default (`False`).
        size: Font size of the cell-value text. `None` leaves the default.
        background_threshold: Value above which the text switches to the
            light colour (for contrast against a dark cell). `None` leaves
            the default (`max(array) / 2`).

    Examples:
        - Enable the overlay with a custom font size:
            ```python
            >>> from cleopatra.styling.params import CellValues
            >>> CellValues(show=True, size=10).to_options()
            {'display_cell_value': True, 'num_size': 10}

            ```
    """

    show: bool | None = None
    size: int | None = None
    background_threshold: float | None = None

    def to_options(self) -> dict[str, Any]:
        """Flatten the explicitly-set fields into `default_options` keys.

        Returns:
            dict: `display_cell_value` / `num_size` /
                `background_color_threshold` for the fields that were set.
        """
        options: dict[str, Any] = {}
        if self.show is not None:
            options["display_cell_value"] = self.show
        if self.size is not None:
            options["num_size"] = self.size
        if self.background_threshold is not None:
            options["background_color_threshold"] = self.background_threshold
        return options

to_options() #

Flatten the explicitly-set fields into default_options keys.

Returns:

Name Type Description
dict dict[str, Any]

display_cell_value / num_size / background_color_threshold for the fields that were set.

Source code in src/cleopatra/styling/params.py
def to_options(self) -> dict[str, Any]:
    """Flatten the explicitly-set fields into `default_options` keys.

    Returns:
        dict: `display_cell_value` / `num_size` /
            `background_color_threshold` for the fields that were set.
    """
    options: dict[str, Any] = {}
    if self.show is not None:
        options["display_cell_value"] = self.show
    if self.size is not None:
        options["num_size"] = self.size
    if self.background_threshold is not None:
        options["background_color_threshold"] = self.background_threshold
    return options

DataStyle#

Named preset, relief shading, and per-call preset overrides: plot(data_style=DataStyle(style="topography", hillshade=True)).

cleopatra.styling.params.DataStyle dataclass #

Named data-style preset, relief-shading, and per-call preset overrides.

Groups the style / hillshade options honoured by ArrayGlyph, MeshGlyph, and KDEGlyph, plus the bands / alpha / alpha_range per-call overrides of an active ArrayGlyph preset. Each field has three states: left unset (keep the glyph's current value -- these options are sticky), set to a value (apply it), or set explicitly to None (clear the preset / disable hillshade / drop the override). to_options() emits a key only for a field that was given (set or explicit None), never for an unset one.

The bands / alpha / alpha_range fields override just one aspect of a styled render while keeping the rest of the preset; they are only meaningful alongside a style (they replace one field of the active DATA_STYLES preset). bands rebands the scale (replacing the preset's levels); alpha sets a constant opacity and alpha_range a value-linked one -- the two are mutually exclusive and resolved downstream (a constant alpha wins). They apply to a continuous/levelled preset only; a categorical (class-colour) preset renders opaque with its fixed class colours and ignores these overrides.

Attributes:

Name Type Description
style str | None | _Unset

Name of a cleopatra.styling.colors.DATA_STYLES preset, or None to clear a sticky preset back to plain colouring.

hillshade bool | dict[str, Any] | None | _Unset

Relief-shade a regular-grid DEM -- True for defaults, a dict tuning vert_exag / azimuth / altitude / blend_mode / multidirectional, or None/False to disable.

bands int | None | _Unset

Discrete band count partitioning the preset's value range, replacing the preset's own levels/bands. Rebands a plain linear scale only -- it is ignored (with a warning) on a diverging (center) or log/symlog preset, whose own scale is kept. None clears a sticky override, keeping the preset's own scale.

alpha float | None | _Unset

Constant layer opacity in [0, 1] overriding the preset's opacity. None clears a sticky override.

alpha_range tuple[float, float] | None | _Unset

(vmin, vmax) mapping data values to opacity (a value-linked alpha) overriding the preset's opacity. None clears a sticky override.

Examples:

  • Select a preset and turn on relief shading:
    >>> from cleopatra.styling.params import DataStyle
    >>> DataStyle(style="dem", hillshade=True).to_options()
    {'style': 'dem', 'hillshade': True}
    
  • Override a styled preset's banding and opacity per call:
    >>> from cleopatra.styling.params import DataStyle
    >>> DataStyle(style="temperature_2m", bands=6, alpha=0.5).to_options()
    {'style': 'temperature_2m', 'bands': 6, 'alpha': 0.5, 'alpha_range': None}
    >>> DataStyle(alpha_range=(0.0, 40.0)).to_options()
    {'alpha_range': (0.0, 40.0), 'alpha': None}
    
  • An unset field is omitted (keeping the sticky value); an explicit None is emitted (clearing it):
    >>> from cleopatra.styling.params import DataStyle
    >>> DataStyle().to_options()
    {}
    >>> DataStyle(style=None).to_options()
    {'style': None}
    
Source code in src/cleopatra/styling/params.py
@dataclass(frozen=True)
class DataStyle:
    """Named data-style preset, relief-shading, and per-call preset overrides.

    Groups the `style` / `hillshade` options honoured by `ArrayGlyph`,
    `MeshGlyph`, and `KDEGlyph`, plus the `bands` / `alpha` / `alpha_range`
    per-call overrides of an active `ArrayGlyph` preset. Each field has three
    states: left unset (keep the glyph's current value -- these options are
    sticky), set to a value (apply it), or set explicitly to `None` (clear the
    preset / disable hillshade / drop the override). `to_options()` emits a key
    only for a field that was given (set or explicit `None`), never for an
    unset one.

    The `bands` / `alpha` / `alpha_range` fields override just one aspect of a
    styled render while keeping the rest of the preset; they are only
    meaningful alongside a `style` (they replace one field of the active
    `DATA_STYLES` preset). `bands` rebands the scale (replacing the preset's
    `levels`); `alpha` sets a constant opacity and `alpha_range` a value-linked
    one -- the two are mutually exclusive and resolved downstream (a constant
    `alpha` wins). They apply to a continuous/levelled preset only; a
    categorical (class-colour) preset renders opaque with its fixed class
    colours and ignores these overrides.

    Attributes:
        style: Name of a `cleopatra.styling.colors.DATA_STYLES` preset, or
            `None` to clear a sticky preset back to plain colouring.
        hillshade: Relief-shade a regular-grid DEM -- `True` for defaults,
            a dict tuning `vert_exag` / `azimuth` / `altitude` /
            `blend_mode` / `multidirectional`, or `None`/`False` to
            disable.
        bands: Discrete band count partitioning the preset's value range,
            replacing the preset's own `levels`/`bands`. Rebands a plain
            linear scale only -- it is ignored (with a warning) on a diverging
            (`center`) or `log`/`symlog` preset, whose own scale is kept.
            `None` clears a sticky override, keeping the preset's own scale.
        alpha: Constant layer opacity in `[0, 1]` overriding the preset's
            opacity. `None` clears a sticky override.
        alpha_range: `(vmin, vmax)` mapping data values to opacity (a
            value-linked alpha) overriding the preset's opacity. `None`
            clears a sticky override.

    Examples:
        - Select a preset and turn on relief shading:
            ```python
            >>> from cleopatra.styling.params import DataStyle
            >>> DataStyle(style="dem", hillshade=True).to_options()
            {'style': 'dem', 'hillshade': True}

            ```
        - Override a styled preset's banding and opacity per call:
            ```python
            >>> from cleopatra.styling.params import DataStyle
            >>> DataStyle(style="temperature_2m", bands=6, alpha=0.5).to_options()
            {'style': 'temperature_2m', 'bands': 6, 'alpha': 0.5, 'alpha_range': None}
            >>> DataStyle(alpha_range=(0.0, 40.0)).to_options()
            {'alpha_range': (0.0, 40.0), 'alpha': None}

            ```
        - An unset field is omitted (keeping the sticky value); an
            explicit `None` is emitted (clearing it):
            ```python
            >>> from cleopatra.styling.params import DataStyle
            >>> DataStyle().to_options()
            {}
            >>> DataStyle(style=None).to_options()
            {'style': None}

            ```
    """

    style: str | None | _Unset = _UNSET
    hillshade: bool | dict[str, Any] | None | _Unset = _UNSET
    bands: int | None | _Unset = _UNSET
    alpha: float | None | _Unset = _UNSET
    alpha_range: tuple[float, float] | None | _Unset = _UNSET

    def __post_init__(self) -> None:
        """Validate `alpha_range` is a `(vmin, vmax)` numeric pair when given.

        Raises:
            TypeError: If `alpha_range` is set to something that is not a
                length-2 sequence of numbers, so the error surfaces at the
                `DataStyle` boundary rather than deep inside the render.
        """
        ar = self.alpha_range
        if isinstance(ar, _Unset) or ar is None:
            return
        try:
            lo, hi = ar
            float(lo), float(hi)
        except (TypeError, ValueError) as exc:
            raise TypeError(
                "DataStyle(alpha_range=...) must be a (vmin, vmax) pair of "
                f"numbers, got {ar!r}"
            ) from exc

    @classmethod
    def for_apply_style(
        cls,
        style: str | None,
        hillshade: bool | dict[str, Any] | None | _Unset = _UNSET,
    ) -> DataStyle:
        """Build the `DataStyle` an `apply_style(...)` call forwards to `plot`.

        Folds a preset `style` and an optionally-forwarded `hillshade` into one
        object: when `hillshade` is left unset (the default sentinel) it is
        omitted so any sticky relief shading is kept; an explicit value (a dict,
        `True`/`False`, or `None` to clear) flows through to
        `DataStyle(hillshade=...)`. Centralises the sentinel-gated construction
        that the `apply_style` helpers of `ArrayGlyph`, `MeshGlyph`, and
        `KDEGlyph` previously each hand-rolled with their own sentinels.

        Args:
            style: The `DATA_STYLES` preset name to apply (or `None` to clear).
            hillshade: Relief-shading override, or the `_UNSET` sentinel
                (default) to leave it unset.

        Returns:
            DataStyle: `DataStyle(style=style)` when `hillshade` is unset, else
                `DataStyle(style=style, hillshade=hillshade)`.

        Examples:
            ```python
            >>> from cleopatra.styling.params import DataStyle
            >>> DataStyle.for_apply_style("dem").to_options()
            {'style': 'dem'}
            >>> DataStyle.for_apply_style("dem", hillshade=True).to_options()
            {'style': 'dem', 'hillshade': True}

            ```
        """
        if isinstance(hillshade, _Unset):
            return cls(style=style)
        return cls(style=style, hillshade=hillshade)

    def to_options(self) -> dict[str, Any]:
        """Flatten the explicitly-given fields into `default_options` keys.

        Returns:
            dict: `style` / `hillshade` / `bands` / `alpha` / `alpha_range`
                for the fields the caller gave (a value or an explicit
                `None`); unset fields are omitted. Setting one of the two
                mutually-exclusive opacity fields to a real value also emits an
                explicit `None` for the other, so a mode switch clears the
                sticky opposite field.
        """
        options: dict[str, Any] = {}
        if not isinstance(self.style, _Unset):
            options["style"] = self.style
        if not isinstance(self.hillshade, _Unset):
            options["hillshade"] = self.hillshade
        if not isinstance(self.bands, _Unset):
            options["bands"] = self.bands
        alpha_set = not isinstance(self.alpha, _Unset)
        range_set = not isinstance(self.alpha_range, _Unset)
        if alpha_set:
            options["alpha"] = self.alpha
        if range_set:
            options["alpha_range"] = self.alpha_range
        # `alpha` (constant) and `alpha_range` (value-linked) are mutually
        # exclusive opacity modes. Setting one to a real value emits an explicit
        # `None` for the other so switching modes on the same (sticky) glyph is
        # not defeated by the stale field -- a leftover constant `alpha` would
        # otherwise win the tie-break in `resolve_style_overrides`. Clearing a
        # field (`=None`) leaves the other untouched.
        if alpha_set and self.alpha is not None and not range_set:
            options["alpha_range"] = None
        elif range_set and self.alpha_range is not None and not alpha_set:
            options["alpha"] = None
        return options

__post_init__() #

Validate alpha_range is a (vmin, vmax) numeric pair when given.

Raises:

Type Description
TypeError

If alpha_range is set to something that is not a length-2 sequence of numbers, so the error surfaces at the DataStyle boundary rather than deep inside the render.

Source code in src/cleopatra/styling/params.py
def __post_init__(self) -> None:
    """Validate `alpha_range` is a `(vmin, vmax)` numeric pair when given.

    Raises:
        TypeError: If `alpha_range` is set to something that is not a
            length-2 sequence of numbers, so the error surfaces at the
            `DataStyle` boundary rather than deep inside the render.
    """
    ar = self.alpha_range
    if isinstance(ar, _Unset) or ar is None:
        return
    try:
        lo, hi = ar
        float(lo), float(hi)
    except (TypeError, ValueError) as exc:
        raise TypeError(
            "DataStyle(alpha_range=...) must be a (vmin, vmax) pair of "
            f"numbers, got {ar!r}"
        ) from exc

for_apply_style(style, hillshade=_UNSET) classmethod #

Build the DataStyle an apply_style(...) call forwards to plot.

Folds a preset style and an optionally-forwarded hillshade into one object: when hillshade is left unset (the default sentinel) it is omitted so any sticky relief shading is kept; an explicit value (a dict, True/False, or None to clear) flows through to DataStyle(hillshade=...). Centralises the sentinel-gated construction that the apply_style helpers of ArrayGlyph, MeshGlyph, and KDEGlyph previously each hand-rolled with their own sentinels.

Parameters:

Name Type Description Default
style str | None

The DATA_STYLES preset name to apply (or None to clear).

required
hillshade bool | dict[str, Any] | None | _Unset

Relief-shading override, or the _UNSET sentinel (default) to leave it unset.

_UNSET

Returns:

Name Type Description
DataStyle DataStyle

DataStyle(style=style) when hillshade is unset, else DataStyle(style=style, hillshade=hillshade).

Examples:

>>> from cleopatra.styling.params import DataStyle
>>> DataStyle.for_apply_style("dem").to_options()
{'style': 'dem'}
>>> DataStyle.for_apply_style("dem", hillshade=True).to_options()
{'style': 'dem', 'hillshade': True}
Source code in src/cleopatra/styling/params.py
@classmethod
def for_apply_style(
    cls,
    style: str | None,
    hillshade: bool | dict[str, Any] | None | _Unset = _UNSET,
) -> DataStyle:
    """Build the `DataStyle` an `apply_style(...)` call forwards to `plot`.

    Folds a preset `style` and an optionally-forwarded `hillshade` into one
    object: when `hillshade` is left unset (the default sentinel) it is
    omitted so any sticky relief shading is kept; an explicit value (a dict,
    `True`/`False`, or `None` to clear) flows through to
    `DataStyle(hillshade=...)`. Centralises the sentinel-gated construction
    that the `apply_style` helpers of `ArrayGlyph`, `MeshGlyph`, and
    `KDEGlyph` previously each hand-rolled with their own sentinels.

    Args:
        style: The `DATA_STYLES` preset name to apply (or `None` to clear).
        hillshade: Relief-shading override, or the `_UNSET` sentinel
            (default) to leave it unset.

    Returns:
        DataStyle: `DataStyle(style=style)` when `hillshade` is unset, else
            `DataStyle(style=style, hillshade=hillshade)`.

    Examples:
        ```python
        >>> from cleopatra.styling.params import DataStyle
        >>> DataStyle.for_apply_style("dem").to_options()
        {'style': 'dem'}
        >>> DataStyle.for_apply_style("dem", hillshade=True).to_options()
        {'style': 'dem', 'hillshade': True}

        ```
    """
    if isinstance(hillshade, _Unset):
        return cls(style=style)
    return cls(style=style, hillshade=hillshade)

to_options() #

Flatten the explicitly-given fields into default_options keys.

Returns:

Name Type Description
dict dict[str, Any]

style / hillshade / bands / alpha / alpha_range for the fields the caller gave (a value or an explicit None); unset fields are omitted. Setting one of the two mutually-exclusive opacity fields to a real value also emits an explicit None for the other, so a mode switch clears the sticky opposite field.

Source code in src/cleopatra/styling/params.py
def to_options(self) -> dict[str, Any]:
    """Flatten the explicitly-given fields into `default_options` keys.

    Returns:
        dict: `style` / `hillshade` / `bands` / `alpha` / `alpha_range`
            for the fields the caller gave (a value or an explicit
            `None`); unset fields are omitted. Setting one of the two
            mutually-exclusive opacity fields to a real value also emits an
            explicit `None` for the other, so a mode switch clears the
            sticky opposite field.
    """
    options: dict[str, Any] = {}
    if not isinstance(self.style, _Unset):
        options["style"] = self.style
    if not isinstance(self.hillshade, _Unset):
        options["hillshade"] = self.hillshade
    if not isinstance(self.bands, _Unset):
        options["bands"] = self.bands
    alpha_set = not isinstance(self.alpha, _Unset)
    range_set = not isinstance(self.alpha_range, _Unset)
    if alpha_set:
        options["alpha"] = self.alpha
    if range_set:
        options["alpha_range"] = self.alpha_range
    # `alpha` (constant) and `alpha_range` (value-linked) are mutually
    # exclusive opacity modes. Setting one to a real value emits an explicit
    # `None` for the other so switching modes on the same (sticky) glyph is
    # not defeated by the stale field -- a leftover constant `alpha` would
    # otherwise win the tie-break in `resolve_style_overrides`. Clearing a
    # field (`=None`) leaves the other untouched.
    if alpha_set and self.alpha is not None and not range_set:
        options["alpha_range"] = None
    elif range_set and self.alpha_range is not None and not alpha_set:
        options["alpha"] = None
    return options

Classify#

Categorical / classed colour schemes on the scatter / vector / flow / polygon glyphs: plot(classify=Classify(scheme="categorical", k=5)).

cleopatra.styling.params.Classify dataclass #

Value-classification (choropleth) options.

Groups the scheme / k / category_legend_kwargs options honoured by the glyphs whose colour mapping routes through Glyph._prepare_scalar_mapping -- VectorGlyph, FlowGlyph, PolygonGlyph, ScatterGlyph.

Attributes:

Name Type Description
scheme str | Sequence[float] | None

A cleopatra.styling.styles.classify scheme name (e.g. "quantiles", "equal_interval"), an explicit sequence of bin edges, or the literal "categorical" for a distinct-value mapping. None leaves the default (no classification).

k int | None

The class count for count/width schemes. None leaves the default (5).

category_legend_kwargs dict[str, Any] | None

Extra keyword arguments forwarded to the legend a "categorical" scheme draws (e.g. loc, ncol).

Examples:

  • A quantile scheme with four classes:
    >>> from cleopatra.styling.params import Classify
    >>> Classify(scheme="quantiles", k=4).to_options()
    {'scheme': 'quantiles', 'k': 4}
    
Source code in src/cleopatra/styling/params.py
@dataclass(frozen=True)
class Classify:
    """Value-classification (choropleth) options.

    Groups the `scheme` / `k` / `category_legend_kwargs` options honoured
    by the glyphs whose colour mapping routes through
    `Glyph._prepare_scalar_mapping` -- `VectorGlyph`, `FlowGlyph`,
    `PolygonGlyph`, `ScatterGlyph`.

    Attributes:
        scheme: A `cleopatra.styling.styles.classify` scheme name (e.g.
            `"quantiles"`, `"equal_interval"`), an explicit sequence of bin
            edges, or the literal `"categorical"` for a distinct-value
            mapping. `None` leaves the default (no classification).
        k: The class count for count/width schemes. `None` leaves the
            default (`5`).
        category_legend_kwargs: Extra keyword arguments forwarded to the
            legend a `"categorical"` scheme draws (e.g. `loc`, `ncol`).

    Examples:
        - A quantile scheme with four classes:
            ```python
            >>> from cleopatra.styling.params import Classify
            >>> Classify(scheme="quantiles", k=4).to_options()
            {'scheme': 'quantiles', 'k': 4}

            ```
    """

    scheme: str | Sequence[float] | None = None
    k: int | None = None
    category_legend_kwargs: dict[str, Any] | None = None

    def to_options(self) -> dict[str, Any]:
        """Flatten the explicitly-set fields into `default_options` keys.

        Returns:
            dict: `scheme` / `k` / `category_legend_kwargs` for the fields
                that were set.
        """
        options: dict[str, Any] = {}
        if self.scheme is not None:
            options["scheme"] = self.scheme
        if self.k is not None:
            options["k"] = self.k
        if self.category_legend_kwargs is not None:
            options["category_legend_kwargs"] = self.category_legend_kwargs
        return options

to_options() #

Flatten the explicitly-set fields into default_options keys.

Returns:

Name Type Description
dict dict[str, Any]

scheme / k / category_legend_kwargs for the fields that were set.

Source code in src/cleopatra/styling/params.py
def to_options(self) -> dict[str, Any]:
    """Flatten the explicitly-set fields into `default_options` keys.

    Returns:
        dict: `scheme` / `k` / `category_legend_kwargs` for the fields
            that were set.
    """
    options: dict[str, Any] = {}
    if self.scheme is not None:
        options["scheme"] = self.scheme
    if self.k is not None:
        options["k"] = self.k
    if self.category_legend_kwargs is not None:
        options["category_legend_kwargs"] = self.category_legend_kwargs
    return options

ColorBar#

Colorbar placement, caption, and sizing: plot(colorbar=ColorBar(location="bottom", label="mm/day")). Pass colorbar=True/False for the simple cases.

cleopatra.styling.colorbar.ColorBar #

Placement (and backing box) for the colorbar plot / animate draws.

Bundles the colorbar-layout choices -- which edge it sits on, whether it is inset inside the frame, and its backing box -- into one value passed as plot(colorbar=...) / animate(colorbar=...), mirroring FrameLabel. Pass colorbar=True / False / None for the simple cases and a ColorBar for placement control.

Attributes:

Name Type Description
location

Edge the colorbar sits on -- "left", "right", "top", or "bottom". None (default) keeps matplotlib's placement (right of a vertical bar). Left/right force a vertical bar, top/bottom a horizontal one.

orientation

Bar orientation -- "vertical" or "horizontal". None (default) lets location decide, and yields a vertical bar when location is None too. Because a set location fixes the orientation, an orientation that disagrees with it is ignored (with a UserWarning) -- set only one. The resolved orientation is sticky on a reused glyph: a later ColorBar() with orientation unset does not reset a previously applied one.

inside

When True, the colorbar is inset inside the frame at location (overlaying the data) rather than in an outside gutter, by default False. An inset is a child of the data axes, so it tracks the axes through full_bleed.

box

Backing panel behind the scale, so the data does not show through its labels. False draws none; True an opaque white panel; a colour string a panel of that colour; a dict of matplotlib.patches.Rectangle kwargs for full control. Defaults to None, which becomes True when inside is set (an inset over moving data almost always wants a panel) and stays off otherwise. For a real colorbar the panel backs an inside colorbar only (it is ignored when inside=False, which sits in its own gutter); for a style preset's swatch legend it backs the swatch regardless of placement, and the swatch title/values then default to a colour that contrasts with the panel (an explicit label_color/tick_color still wins).

label_color

Colour of the scale's title text -- the colorbar's axis label and, for a style preset, the swatch legend's title (the endpoint values take tick_color, not this). None (default) keeps the default: matplotlib's for a colorbar label; for the swatch, a colour that contrasts with box, else white.

tick_color

Colour of the tick labels (the numbers) of a real colorbar and, for a style preset, the swatch legend's endpoint values. None (default) keeps matplotlib's default for a colorbar; for the swatch it defaults to a colour that contrasts with box, else white.

label

Caption text for the scale (the colorbar's title). None (default) keeps the current default caption.

length

Bar length as a fraction of the axis (e.g. 0.8). None (default) keeps the default length.

label_size

Font size of the caption. None (default) keeps the default.

label_rotation

Rotation of the caption in degrees. None (default) leaves matplotlib's own label orientation; pass a value to rotate the caption (e.g. 0 for a horizontal caption).

label_location

Where the caption sits along the bar (e.g. "center"). Distinct from location, which is the bar's edge. Valid values depend on orientation (vertical bar: "top"/"center"/"bottom"; horizontal bar: "left"/"center"/"right"). None (default) keeps the default.

ticks_spacing

Spacing between the colorbar's ticks. None (default) keeps the default.

Examples:

  • An inside colorbar on the right -- its box defaults on:
    >>> from cleopatra.styling.colorbar import ColorBar
    >>> spec = ColorBar(location="right", inside=True)
    >>> spec.inside, spec.box
    (True, True)
    
  • Black title + tick numbers, outside on the bottom (no box):
    >>> from cleopatra.styling.colorbar import ColorBar
    >>> spec = ColorBar(location="bottom", label_color="black", tick_color="black")
    >>> (spec.box, spec.label_color, spec.tick_color)
    (None, 'black', 'black')
    
  • A captioned bar, fully specified through the spec (no loose kwargs):
    >>> from cleopatra.styling.colorbar import ColorBar
    >>> spec = ColorBar(location="bottom", label="Rainfall mm/day", length=0.8)
    >>> (spec.label, spec.length)
    ('Rainfall mm/day', 0.8)
    
Source code in src/cleopatra/styling/colorbar.py
class ColorBar:
    """Placement (and backing box) for the colorbar `plot` / `animate` draws.

    Bundles the colorbar-layout choices -- which edge it sits on, whether it
    is inset *inside* the frame, and its backing box -- into one value passed
    as `plot(colorbar=...)` / `animate(colorbar=...)`, mirroring `FrameLabel`.
    Pass `colorbar=True` / `False` / `None` for the simple cases and a
    `ColorBar` for placement control.

    Attributes:
        location: Edge the colorbar sits on -- `"left"`, `"right"`, `"top"`,
            or `"bottom"`. `None` (default) keeps matplotlib's placement
            (right of a vertical bar). Left/right force a vertical bar,
            top/bottom a horizontal one.
        orientation: Bar orientation -- `"vertical"` or `"horizontal"`. `None`
            (default) lets `location` decide, and yields a vertical bar when
            `location` is `None` too. Because a set `location` fixes the
            orientation, an `orientation` that disagrees with it is ignored
            (with a `UserWarning`) -- set only one. The resolved orientation is
            sticky on a reused glyph: a later `ColorBar()` with `orientation`
            unset does not reset a previously applied one.
        inside: When `True`, the colorbar is inset *inside* the frame at
            `location` (overlaying the data) rather than in an outside gutter,
            by default `False`. An inset is a child of the data axes, so it
            tracks the axes through `full_bleed`.
        box: Backing panel behind the scale, so the data does not show through
            its labels. `False` draws none; `True` an opaque white panel; a
            colour string a panel of that colour; a dict of
            `matplotlib.patches.Rectangle` kwargs for full control. Defaults to
            `None`, which becomes `True` when `inside` is set (an inset over
            moving data almost always wants a panel) and stays off otherwise.
            For a real colorbar the panel backs an *inside* colorbar only (it is
            ignored when `inside=False`, which sits in its own gutter); for a
            `style` preset's swatch legend it backs the swatch regardless of
            placement, and the swatch title/values then default to a colour that
            contrasts with the panel (an explicit `label_color`/`tick_color`
            still wins).
        label_color: Colour of the scale's title text -- the colorbar's axis
            label and, for a `style` preset, the swatch legend's title (the
            endpoint values take `tick_color`, not this). `None` (default) keeps
            the default: matplotlib's for a colorbar label; for the swatch, a
            colour that contrasts with `box`, else white.
        tick_color: Colour of the tick labels (the numbers) of a real colorbar
            and, for a `style` preset, the swatch legend's endpoint values.
            `None` (default) keeps matplotlib's default for a colorbar; for the
            swatch it defaults to a colour that contrasts with `box`, else white.
        label: Caption text for the scale (the colorbar's title). `None`
            (default) keeps the current default caption.
        length: Bar length as a fraction of the axis (e.g. `0.8`). `None`
            (default) keeps the default length.
        label_size: Font size of the caption. `None` (default) keeps the
            default.
        label_rotation: Rotation of the caption in degrees. `None` (default)
            leaves matplotlib's own label orientation; pass a value to rotate
            the caption (e.g. `0` for a horizontal caption).
        label_location: Where the caption sits along the bar (e.g. `"center"`).
            Distinct from `location`, which is the bar's *edge*. Valid values
            depend on orientation (vertical bar: `"top"`/`"center"`/`"bottom"`;
            horizontal bar: `"left"`/`"center"`/`"right"`). `None` (default)
            keeps the default.
        ticks_spacing: Spacing between the colorbar's ticks. `None` (default)
            keeps the default.

    Examples:
        - An inside colorbar on the right -- its box defaults on:
            ```python
            >>> from cleopatra.styling.colorbar import ColorBar
            >>> spec = ColorBar(location="right", inside=True)
            >>> spec.inside, spec.box
            (True, True)

            ```
        - Black title + tick numbers, outside on the bottom (no box):
            ```python
            >>> from cleopatra.styling.colorbar import ColorBar
            >>> spec = ColorBar(location="bottom", label_color="black", tick_color="black")
            >>> (spec.box, spec.label_color, spec.tick_color)
            (None, 'black', 'black')

            ```
        - A captioned bar, fully specified through the spec (no loose kwargs):
            ```python
            >>> from cleopatra.styling.colorbar import ColorBar
            >>> spec = ColorBar(location="bottom", label="Rainfall mm/day", length=0.8)
            >>> (spec.label, spec.length)
            ('Rainfall mm/day', 0.8)

            ```
    """

    def __init__(
        self,
        *,
        location: Literal["left", "right", "top", "bottom"] | None = None,
        orientation: Literal["vertical", "horizontal"] | None = None,
        inside: bool = False,
        box: bool | str | dict | None = None,
        label_color: str | None = None,
        tick_color: str | None = None,
        label: str | None = None,
        length: float | None = None,
        label_size: float | None = None,
        label_rotation: float | None = None,
        label_location: str | None = None,
        ticks_spacing: float | None = None,
    ) -> None:
        """Initialise a `ColorBar`.

        Args:
            location: Edge to sit on (`"left"`/`"right"`/`"top"`/`"bottom"`),
                or `None` for matplotlib's default placement.
            orientation: Bar orientation (`"vertical"`/`"horizontal"`), or
                `None` to let `location` decide (a vertical bar when neither is
                set). Ignored (with a `UserWarning`) when it disagrees with the
                orientation `location` implies.
            inside: Inset the colorbar inside the frame, by default `False`.
            box: Backing panel for an inside colorbar (`True` / colour / dict),
                or `None` to default it on when `inside` is set.
            label_color: Colour of the scale title / colorbar label (and the
                swatch title for a `style` preset); `None` keeps the default.
            tick_color: Colour of the colorbar's tick numbers; `None` keeps
                matplotlib's default.
            label: Caption text (scale title); `None` keeps the default.
            length: Bar length as a fraction of the axis; `None` keeps the
                default.
            label_size: Caption font size; `None` keeps the default.
            label_rotation: Caption rotation in degrees; `None` leaves
                matplotlib's own label orientation.
            label_location: Caption placement along the bar (distinct from
                `location`, the bar's edge); valid values depend on orientation
                (vertical: top/center/bottom, horizontal: left/center/right);
                `None` keeps the default.
            ticks_spacing: Spacing between the colorbar's ticks; `None` keeps
                the default.
        """
        _validate_orientation(orientation)
        _warn_orientation_conflict(location, orientation)
        _validate_label_location(location, orientation, label_location)
        self.location = location
        self.orientation = orientation
        self.inside = inside
        self.box = True if (inside and box is None) else box
        self.label_color = label_color
        self.tick_color = tick_color
        self.label = label
        self.length = length
        self.label_size = label_size
        self.label_rotation = label_rotation
        self.label_location = label_location
        self.ticks_spacing = ticks_spacing

    def to_options(self) -> dict:
        """Map this spec's fields onto the `cbar_*` `default_options` keys.

        Mirrors the other grouped styling objects' `to_options`: the object
        owns the translation from its own fields to the flat render options
        `create_color_bar` reads. Placement fields are always emitted (so a
        reused glyph's prior placement is overwritten); the caption / sizing /
        orientation / tick-spacing fields are emitted only when set, leaving an
        unset field at the existing default.

        Returns:
            dict: `default_options` updates for this spec, always including
                `add_colorbar=True`.

        Examples:
            - Placement maps onto `cbar_*`; unset caption fields are omitted:
                ```python
                >>> from cleopatra.styling.colorbar import ColorBar
                >>> ColorBar(location="left", inside=True).to_options()["cbar_location"]
                'left'
                >>> "cbar_label" in ColorBar(location="right").to_options()
                False

                ```
        """
        updates = {
            "add_colorbar": True,
            "cbar_location": self.location,
            "cbar_inside": self.inside,
            "cbar_box": self.box,
            "cbar_label_color": self.label_color,
            "cbar_tick_color": self.tick_color,
        }
        optional = {
            "cbar_label": self.label,
            "cbar_length": self.length,
            "cbar_label_size": self.label_size,
            "cbar_label_rotation": self.label_rotation,
            "cbar_label_location": self.label_location,
            "cbar_orientation": self.orientation,
            "ticks_spacing": self.ticks_spacing,
        }
        updates.update({k: v for k, v in optional.items() if v is not None})
        return updates

    def specifies_placement(self) -> bool:
        """Whether this spec explicitly requests a placement or orientation.

        `True` when any of `location`, `inside`, or `orientation` is set -- the
        spec asks for a specific colorbar rather than leaving the default. Used
        to decide whether a styled (preset) render should still draw a colorbar.

        Returns:
            bool: `True` if `location`, `inside`, or `orientation` is set.

        Examples:
            - A placement edge counts as specified; a bare spec does not:
                ```python
                >>> from cleopatra.styling.colorbar import ColorBar
                >>> ColorBar(location="bottom").specifies_placement()
                True
                >>> ColorBar().specifies_placement()
                False

                ```
        """
        return (
            self.location is not None
            or self.inside
            or self.orientation is not None
        )

    @classmethod
    def reset_options(cls) -> dict:
        """`default_options` updates for a default, sticky-clearing colorbar.

        The dict `colorbar=True` applies: it draws a default bar and resets the
        resettable `cbar_*` family to `STYLE_DEFAULTS`, so a reused glyph does
        not inherit a prior sticky spec's placement or caption. Distinct from
        `to_options`, which maps a *specific* spec's fields and omits unset
        ones; this resets the whole `cbar_*` family to the defaults.
        `ticks_spacing` is deliberately excluded: it is glyph-specific
        (`KDEGlyph`, for one, auto-derives it from the data range when unset),
        so a single shared reset value could not restore each glyph's own
        default -- it is therefore left untouched by `colorbar=True`.

        Returns:
            dict: `default_options` updates for a default colorbar.

        Examples:
            - The reset always enables the bar and clears the placement:
                ```python
                >>> from cleopatra.styling.colorbar import ColorBar
                >>> opts = ColorBar.reset_options()
                >>> opts["add_colorbar"]
                True
                >>> opts["cbar_location"] is None
                True

                ```
        """
        return {
            "add_colorbar": True,
            "cbar_location": None,
            "cbar_inside": False,
            "cbar_box": None,
            "cbar_label_color": None,
            "cbar_tick_color": None,
            "cbar_orientation": STYLE_DEFAULTS["cbar_orientation"],
            "cbar_label": STYLE_DEFAULTS["cbar_label"],
            "cbar_length": STYLE_DEFAULTS["cbar_length"],
            "cbar_label_size": STYLE_DEFAULTS["cbar_label_size"],
            "cbar_label_rotation": STYLE_DEFAULTS["cbar_label_rotation"],
            "cbar_label_location": STYLE_DEFAULTS["cbar_label_location"],
        }

    @classmethod
    def resolve(cls, colorbar: "bool | ColorBar | None") -> dict:
        """Translate a `colorbar=` argument into `default_options` updates.

        Owns the full `None` / `False` / `True` / `ColorBar` dispatch: `None`
        leaves the colorbar options untouched; `False` suppresses the bar;
        `True` resets to a default bar via `reset_options`; a `ColorBar`
        instance maps its fields via `to_options`.

        Args:
            colorbar: `None`, `False`, `True`, or a `ColorBar` instance.

        Returns:
            dict: Updates to merge into `default_options` (empty for `None`).

        Raises:
            TypeError: If `colorbar` is not a bool, `ColorBar`, or `None`.

        Examples:
            ```python
            >>> from cleopatra.styling.colorbar import ColorBar
            >>> ColorBar.resolve(False)
            {'add_colorbar': False}
            >>> ColorBar.resolve(ColorBar(location="left", inside=True))["cbar_location"]
            'left'
            >>> "cbar_label" in ColorBar.resolve(ColorBar(location="right"))
            False

            ```
        """
        if colorbar is None:
            return {}
        if colorbar is False:
            return {"add_colorbar": False}
        if colorbar is True:
            return cls.reset_options()
        if isinstance(colorbar, cls):
            return colorbar.to_options()
        raise TypeError(
            f"colorbar must be a bool, ColorBar, or None, got {type(colorbar).__name__}."
        )

__init__(*, location=None, orientation=None, inside=False, box=None, label_color=None, tick_color=None, label=None, length=None, label_size=None, label_rotation=None, label_location=None, ticks_spacing=None) #

Initialise a ColorBar.

Parameters:

Name Type Description Default
location Literal['left', 'right', 'top', 'bottom'] | None

Edge to sit on ("left"/"right"/"top"/"bottom"), or None for matplotlib's default placement.

None
orientation Literal['vertical', 'horizontal'] | None

Bar orientation ("vertical"/"horizontal"), or None to let location decide (a vertical bar when neither is set). Ignored (with a UserWarning) when it disagrees with the orientation location implies.

None
inside bool

Inset the colorbar inside the frame, by default False.

False
box bool | str | dict | None

Backing panel for an inside colorbar (True / colour / dict), or None to default it on when inside is set.

None
label_color str | None

Colour of the scale title / colorbar label (and the swatch title for a style preset); None keeps the default.

None
tick_color str | None

Colour of the colorbar's tick numbers; None keeps matplotlib's default.

None
label str | None

Caption text (scale title); None keeps the default.

None
length float | None

Bar length as a fraction of the axis; None keeps the default.

None
label_size float | None

Caption font size; None keeps the default.

None
label_rotation float | None

Caption rotation in degrees; None leaves matplotlib's own label orientation.

None
label_location str | None

Caption placement along the bar (distinct from location, the bar's edge); valid values depend on orientation (vertical: top/center/bottom, horizontal: left/center/right); None keeps the default.

None
ticks_spacing float | None

Spacing between the colorbar's ticks; None keeps the default.

None
Source code in src/cleopatra/styling/colorbar.py
def __init__(
    self,
    *,
    location: Literal["left", "right", "top", "bottom"] | None = None,
    orientation: Literal["vertical", "horizontal"] | None = None,
    inside: bool = False,
    box: bool | str | dict | None = None,
    label_color: str | None = None,
    tick_color: str | None = None,
    label: str | None = None,
    length: float | None = None,
    label_size: float | None = None,
    label_rotation: float | None = None,
    label_location: str | None = None,
    ticks_spacing: float | None = None,
) -> None:
    """Initialise a `ColorBar`.

    Args:
        location: Edge to sit on (`"left"`/`"right"`/`"top"`/`"bottom"`),
            or `None` for matplotlib's default placement.
        orientation: Bar orientation (`"vertical"`/`"horizontal"`), or
            `None` to let `location` decide (a vertical bar when neither is
            set). Ignored (with a `UserWarning`) when it disagrees with the
            orientation `location` implies.
        inside: Inset the colorbar inside the frame, by default `False`.
        box: Backing panel for an inside colorbar (`True` / colour / dict),
            or `None` to default it on when `inside` is set.
        label_color: Colour of the scale title / colorbar label (and the
            swatch title for a `style` preset); `None` keeps the default.
        tick_color: Colour of the colorbar's tick numbers; `None` keeps
            matplotlib's default.
        label: Caption text (scale title); `None` keeps the default.
        length: Bar length as a fraction of the axis; `None` keeps the
            default.
        label_size: Caption font size; `None` keeps the default.
        label_rotation: Caption rotation in degrees; `None` leaves
            matplotlib's own label orientation.
        label_location: Caption placement along the bar (distinct from
            `location`, the bar's edge); valid values depend on orientation
            (vertical: top/center/bottom, horizontal: left/center/right);
            `None` keeps the default.
        ticks_spacing: Spacing between the colorbar's ticks; `None` keeps
            the default.
    """
    _validate_orientation(orientation)
    _warn_orientation_conflict(location, orientation)
    _validate_label_location(location, orientation, label_location)
    self.location = location
    self.orientation = orientation
    self.inside = inside
    self.box = True if (inside and box is None) else box
    self.label_color = label_color
    self.tick_color = tick_color
    self.label = label
    self.length = length
    self.label_size = label_size
    self.label_rotation = label_rotation
    self.label_location = label_location
    self.ticks_spacing = ticks_spacing

reset_options() classmethod #

default_options updates for a default, sticky-clearing colorbar.

The dict colorbar=True applies: it draws a default bar and resets the resettable cbar_* family to STYLE_DEFAULTS, so a reused glyph does not inherit a prior sticky spec's placement or caption. Distinct from to_options, which maps a specific spec's fields and omits unset ones; this resets the whole cbar_* family to the defaults. ticks_spacing is deliberately excluded: it is glyph-specific (KDEGlyph, for one, auto-derives it from the data range when unset), so a single shared reset value could not restore each glyph's own default -- it is therefore left untouched by colorbar=True.

Returns:

Name Type Description
dict dict

default_options updates for a default colorbar.

Examples:

  • The reset always enables the bar and clears the placement:
    >>> from cleopatra.styling.colorbar import ColorBar
    >>> opts = ColorBar.reset_options()
    >>> opts["add_colorbar"]
    True
    >>> opts["cbar_location"] is None
    True
    
Source code in src/cleopatra/styling/colorbar.py
@classmethod
def reset_options(cls) -> dict:
    """`default_options` updates for a default, sticky-clearing colorbar.

    The dict `colorbar=True` applies: it draws a default bar and resets the
    resettable `cbar_*` family to `STYLE_DEFAULTS`, so a reused glyph does
    not inherit a prior sticky spec's placement or caption. Distinct from
    `to_options`, which maps a *specific* spec's fields and omits unset
    ones; this resets the whole `cbar_*` family to the defaults.
    `ticks_spacing` is deliberately excluded: it is glyph-specific
    (`KDEGlyph`, for one, auto-derives it from the data range when unset),
    so a single shared reset value could not restore each glyph's own
    default -- it is therefore left untouched by `colorbar=True`.

    Returns:
        dict: `default_options` updates for a default colorbar.

    Examples:
        - The reset always enables the bar and clears the placement:
            ```python
            >>> from cleopatra.styling.colorbar import ColorBar
            >>> opts = ColorBar.reset_options()
            >>> opts["add_colorbar"]
            True
            >>> opts["cbar_location"] is None
            True

            ```
    """
    return {
        "add_colorbar": True,
        "cbar_location": None,
        "cbar_inside": False,
        "cbar_box": None,
        "cbar_label_color": None,
        "cbar_tick_color": None,
        "cbar_orientation": STYLE_DEFAULTS["cbar_orientation"],
        "cbar_label": STYLE_DEFAULTS["cbar_label"],
        "cbar_length": STYLE_DEFAULTS["cbar_length"],
        "cbar_label_size": STYLE_DEFAULTS["cbar_label_size"],
        "cbar_label_rotation": STYLE_DEFAULTS["cbar_label_rotation"],
        "cbar_label_location": STYLE_DEFAULTS["cbar_label_location"],
    }

resolve(colorbar) classmethod #

Translate a colorbar= argument into default_options updates.

Owns the full None / False / True / ColorBar dispatch: None leaves the colorbar options untouched; False suppresses the bar; True resets to a default bar via reset_options; a ColorBar instance maps its fields via to_options.

Parameters:

Name Type Description Default
colorbar bool | ColorBar | None

None, False, True, or a ColorBar instance.

required

Returns:

Name Type Description
dict dict

Updates to merge into default_options (empty for None).

Raises:

Type Description
TypeError

If colorbar is not a bool, ColorBar, or None.

Examples:

>>> from cleopatra.styling.colorbar import ColorBar
>>> ColorBar.resolve(False)
{'add_colorbar': False}
>>> ColorBar.resolve(ColorBar(location="left", inside=True))["cbar_location"]
'left'
>>> "cbar_label" in ColorBar.resolve(ColorBar(location="right"))
False
Source code in src/cleopatra/styling/colorbar.py
@classmethod
def resolve(cls, colorbar: "bool | ColorBar | None") -> dict:
    """Translate a `colorbar=` argument into `default_options` updates.

    Owns the full `None` / `False` / `True` / `ColorBar` dispatch: `None`
    leaves the colorbar options untouched; `False` suppresses the bar;
    `True` resets to a default bar via `reset_options`; a `ColorBar`
    instance maps its fields via `to_options`.

    Args:
        colorbar: `None`, `False`, `True`, or a `ColorBar` instance.

    Returns:
        dict: Updates to merge into `default_options` (empty for `None`).

    Raises:
        TypeError: If `colorbar` is not a bool, `ColorBar`, or `None`.

    Examples:
        ```python
        >>> from cleopatra.styling.colorbar import ColorBar
        >>> ColorBar.resolve(False)
        {'add_colorbar': False}
        >>> ColorBar.resolve(ColorBar(location="left", inside=True))["cbar_location"]
        'left'
        >>> "cbar_label" in ColorBar.resolve(ColorBar(location="right"))
        False

        ```
    """
    if colorbar is None:
        return {}
    if colorbar is False:
        return {"add_colorbar": False}
    if colorbar is True:
        return cls.reset_options()
    if isinstance(colorbar, cls):
        return colorbar.to_options()
    raise TypeError(
        f"colorbar must be a bool, ColorBar, or None, got {type(colorbar).__name__}."
    )

specifies_placement() #

Whether this spec explicitly requests a placement or orientation.

True when any of location, inside, or orientation is set -- the spec asks for a specific colorbar rather than leaving the default. Used to decide whether a styled (preset) render should still draw a colorbar.

Returns:

Name Type Description
bool bool

True if location, inside, or orientation is set.

Examples:

  • A placement edge counts as specified; a bare spec does not:
    >>> from cleopatra.styling.colorbar import ColorBar
    >>> ColorBar(location="bottom").specifies_placement()
    True
    >>> ColorBar().specifies_placement()
    False
    
Source code in src/cleopatra/styling/colorbar.py
def specifies_placement(self) -> bool:
    """Whether this spec explicitly requests a placement or orientation.

    `True` when any of `location`, `inside`, or `orientation` is set -- the
    spec asks for a specific colorbar rather than leaving the default. Used
    to decide whether a styled (preset) render should still draw a colorbar.

    Returns:
        bool: `True` if `location`, `inside`, or `orientation` is set.

    Examples:
        - A placement edge counts as specified; a bare spec does not:
            ```python
            >>> from cleopatra.styling.colorbar import ColorBar
            >>> ColorBar(location="bottom").specifies_placement()
            True
            >>> ColorBar().specifies_placement()
            False

            ```
    """
    return (
        self.location is not None
        or self.inside
        or self.orientation is not None
    )

to_options() #

Map this spec's fields onto the cbar_* default_options keys.

Mirrors the other grouped styling objects' to_options: the object owns the translation from its own fields to the flat render options create_color_bar reads. Placement fields are always emitted (so a reused glyph's prior placement is overwritten); the caption / sizing / orientation / tick-spacing fields are emitted only when set, leaving an unset field at the existing default.

Returns:

Name Type Description
dict dict

default_options updates for this spec, always including add_colorbar=True.

Examples:

  • Placement maps onto cbar_*; unset caption fields are omitted:
    >>> from cleopatra.styling.colorbar import ColorBar
    >>> ColorBar(location="left", inside=True).to_options()["cbar_location"]
    'left'
    >>> "cbar_label" in ColorBar(location="right").to_options()
    False
    
Source code in src/cleopatra/styling/colorbar.py
def to_options(self) -> dict:
    """Map this spec's fields onto the `cbar_*` `default_options` keys.

    Mirrors the other grouped styling objects' `to_options`: the object
    owns the translation from its own fields to the flat render options
    `create_color_bar` reads. Placement fields are always emitted (so a
    reused glyph's prior placement is overwritten); the caption / sizing /
    orientation / tick-spacing fields are emitted only when set, leaving an
    unset field at the existing default.

    Returns:
        dict: `default_options` updates for this spec, always including
            `add_colorbar=True`.

    Examples:
        - Placement maps onto `cbar_*`; unset caption fields are omitted:
            ```python
            >>> from cleopatra.styling.colorbar import ColorBar
            >>> ColorBar(location="left", inside=True).to_options()["cbar_location"]
            'left'
            >>> "cbar_label" in ColorBar(location="right").to_options()
            False

            ```
    """
    updates = {
        "add_colorbar": True,
        "cbar_location": self.location,
        "cbar_inside": self.inside,
        "cbar_box": self.box,
        "cbar_label_color": self.label_color,
        "cbar_tick_color": self.tick_color,
    }
    optional = {
        "cbar_label": self.label,
        "cbar_length": self.length,
        "cbar_label_size": self.label_size,
        "cbar_label_rotation": self.label_rotation,
        "cbar_label_location": self.label_location,
        "cbar_orientation": self.orientation,
        "ticks_spacing": self.ticks_spacing,
    }
    updates.update({k: v for k, v in optional.items() if v is not None})
    return updates