Source code for pycolorbar.univariate.colorbar_circular

# -----------------------------------------------------------------------------.
# MIT License

# Copyright (c) 2024 pycolorbar developers
#
# This file is part of pycolorbar.

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# -----------------------------------------------------------------------------.
"""Module with functions to plot circular univariate colorbars."""
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.collections import PatchCollection
from matplotlib.patches import Circle, Wedge
from mpl_toolkits.axes_grid1 import make_axes_locatable

from pycolorbar.utils.mpl_legend import add_colorbar_inset

# Alternative solution:
# - Draw pies and mask with inner disk: https://stackoverflow.com/questions/59877425/python-matplotlib-donut-chart-with-smaller-width-on-one-wedge
# - However does not allow for full transparency in the inner circle ...


def _create_wedges(r_min, r_max, n, center, theta_offset, direction):
    """
    Create a list of wedges (annular sectors) for a circular colorbar.

    Parameters
    ----------
    r_min : float
        Inner radius of the wedges.
    r_max : float
        Outer radius of the wedges.
    center: tuple, optional
        The coordinate center (x,y) around which to draw the circular colorbar.
        The default is (0.5, 0.5)
    n : int
        Number of discrete wedges to create.
    theta_offset : float
        Offset angle (in degrees) for the starting point of the wedges.
    direction : int
        Rotation direction (-1 for clockwise, 1 for counterclockwise).

    Returns
    -------
    list of Wedge
        List of matplotlib Wedge objects representing the colorbar sectors.
    """
    wedges = []
    width = r_max - r_min
    for i in range(n):
        # Define start angle
        theta1 = (direction * (360 * i / n) - theta_offset) % 360
        # Define end angle
        theta2 = (direction * (360 * (i + 1) / n) - theta_offset) % 360
        if direction == 1:
            wedge = Wedge(center, r_max, theta1, theta2, width=width)
        else:
            wedge = Wedge(center[0], r_max, theta2, theta1, width=width)
        wedges.append(wedge)
    return wedges


def _get_ticklabels_alignment(tick):
    """
    Determine horizontal and vertical alignment for tick labels based on their position.

    Parameters
    ----------
    tick : float
        The angle (in degrees) of the current tick.

    Returns
    -------
    tuple of (str, str)
        Horizontal and vertical alignment for the tick label. Possible values for horizontal alignment are
        'left', 'right', and 'center'. For vertical alignment, 'top', 'bottom', or 'center' are returned.
    """
    # Normalize the angle to [0, 360)
    tick = tick % 360

    # Quadrant-based alignment:
    #  - Quadrant I (0 to < 90):    label corner is bottom-left
    #  - Quadrant II (90 to < 180): label corner is bottom-right
    #  - Quadrant III (180 to < 270): label corner is top-right
    #  - Quadrant IV (270 to < 360): label corner is top-left
    # This ensures that the radial line "touches" the same corner of the text.
    if 0 <= tick < 90:
        ha = "left"
        va = "bottom"
    elif 90 <= tick < 180:
        ha = "right"
        va = "bottom"
    elif 180 <= tick < 270:
        ha = "right"
        va = "top"
    else:  # 270 <= tick < 360
        ha = "left"
        va = "top"

    return ha, va

    # Determine horizontal alignment (ha) based on quadrant
    # ha = "left" if 0 <= tick < 90 or 270 <= tick < 360 else "right"
    # # Special case for ticks near the top and bottom
    # if 85 <= tick <= 95:
    #     ha = "center"
    #     va = "bottom"  # Labels at the top
    # elif 265 <= tick <= 275:
    #     ha = "center"
    #     va = "top"  # Labels at the bottom
    # else:
    #     # General vertical alignment: depends on top/bottom half of the circle
    #     va = "center" if 0 <= tick < 180 else "center"
    # return ha, va


def _add_ticklabels(
    ax,
    center,
    ticks,
    ticklabels,
    r_max,
    direction,
    theta_offset,
    ticklabels_pad=0.05,
    ticklabels_size=10,
):
    """
    Add tick labels to the circular colorbar.

    Parameters
    ----------
    ax : matplotlib.axes.Axes
        The axes on which to add the tick labels.
    ticks : array-like
        The angular positions of the ticks (in degrees).
    ticklabels : array-like
        List of labels to display at each tick.
    r_max : float
        Outer radius of the colorbar where the labels will be positioned.
    center: tuple, optional
        The coordinate center (x,y) around which to draw the circular colorbar.
        The default is (0.5, 0.5)
    direction : int
        Rotation direction (-1 for clockwise, 1 for counterclockwise).
    theta_offset : float
        Offset angle (in degrees) for the starting point of the ticks.
    ticklabels_pad : float, optional
        Radial offset for tick labels, by default 0.05.

    Returns
    -------
    None
    """
    ticks = (direction * ticks - theta_offset) % 360
    for tick, label in zip(ticks, ticklabels, strict=False):
        ha, va = _get_ticklabels_alignment(tick)
        ax.text(
            center[0] + np.cos(np.deg2rad(tick)) * (r_max + ticklabels_pad),
            center[1] + np.sin(np.deg2rad(tick)) * (r_max + ticklabels_pad),
            label,
            horizontalalignment=ha,
            verticalalignment=va,
            fontsize=ticklabels_size,
        )


def _add_ticks(ax, ticks, r_max, direction, theta_offset, center, ticklength=0.03, tickcolor="black", tickwidth=2):
    """
    Add tick marks to the circular colorbar.

    Parameters
    ----------
    ax : matplotlib.axes.Axes
        The axes on which to add the tick marks.
    ticks : array-like
        The angular positions of the ticks (in degrees).
    r_max : float
        Outer radius of the colorbar where the tick marks will be positioned.
    center: tuple, optional
        The coordinate center (x,y) around which to draw the circular colorbar.
        The default is (0.5, 0.5)
    direction : int
        Rotation direction (-1 for clockwise, 1 for counterclockwise).
    theta_offset : float
        Offset angle (in degrees) for the starting point of the ticks.
    ticklength : float, optional
        Length of the tick marks, by default 0.03.
    tickcolor : str, optional
        Color of the tick marks, by default 'black'.
    tickwidth : float, optional
        Line width of the tick marks, by default 2.

    Returns
    -------
    None
    """
    ticks = (direction * ticks - theta_offset) % 360
    for tick in ticks:
        # Add small tick lines
        tick_line_x = [
            center[0] + np.cos(np.deg2rad(tick)) * r_max,
            center[0] + np.cos(np.deg2rad(tick)) * (r_max + ticklength),
        ]  # Small extension for tick
        tick_line_y = [
            center[1] + np.sin(np.deg2rad(tick)) * r_max,
            center[1] + np.sin(np.deg2rad(tick)) * (r_max + ticklength),
        ]  # Small extension for tick
        ax.plot(tick_line_x, tick_line_y, color=tickcolor, lw=tickwidth)


def _set_adaptive_limits(ax, r_max, center, margin_factor=0.1):
    """
    Set adaptive x and y limits based on the given radii, with a margin.

    Parameters
    ----------
    ax : matplotlib.axes.Axes
        The axes on which to set the limits.
    r_max : float
        Outer radius of the colorbar.
    center: tuple, optional
        The coordinate center (x,y) around which to draw the circular colorbar.
        The default is (0.5, 0.5)
    margin_factor : float, optional
        Fraction of `r_max` to use as margin around the colorbar, by default 0.1.

    Returns
    -------
    None
    """
    # Calculate margin based on the maximum radius
    margin = margin_factor * r_max
    # Calculate the limits for the axes
    xlim = center[0] - (r_max + margin), center[0] + (r_max + margin)
    ylim = center[1] - (r_max + margin), center[1] + (r_max + margin)
    # Set the limits
    ax.set_xlim(*xlim)
    ax.set_ylim(*ylim)


[docs] def plot_circular_colorbar_wedges( cmap, *, # Location ax=None, cax=None, location="right", size="30%", pad=0.3, box_aspect=1, # Appearance r_min=0.2, r_max=0.5, center=(0.5, 0.5), adapt_limits=True, zero_location="N", clockwise=True, antialiased=False, # Contour add_contour=True, contour_color="black", contour_linewidth=None, wedges_edgecolor="none", wedges_linewidths=None, # Ticks add_ticks=True, ticks=None, ticklength=0.02, tickcolor="black", tickwidth=1, # Ticklabels ticklabels=None, ticklabels_pad=0.05, ticklabels_size=10, ): """ Plot a circular colorbar using wedges. This function plots a circular colorbar representing the specified cyclic colormap. You can either provide: - An existing Axes (`ax`) in which to place the colorbar (the colorbar will be appended to one of its sides). - A dedicated Axes object (`cax`) for direct drawing of the colorbar on the specified `cax`. - Or no Axes at all, in which case a new figure and Axes are created. If both `ax` and `cax` are given, `ax` is ignored !. Parameters ---------- cmap : matplotlib.colors.Colormap Colormap to use for the wedges. ax : matplotlib.axes.Axes or cartopy.mpl.geoaxes.GeoAxesSubplot, optional The Axes to which the colorbar should be appended. Ignored if `cax` is provided. If both `ax` and `cax` are None, a new figure and Axes are created. If ax is specified and cax is None, arguments 'pad', 'size' and 'box_aspect' controls the appearance of the new axis. cax : matplotlib.axes.Axes, optional The Axes in which to directly draw the colorbar. If provided, `ax` is ignored. location : str, optional The side of the plot ('right', 'left', 'top', 'bottom') where the colorbar should be placed. The default location is "right". size : str, optional The size of the colorbar relative to the parent Axes when using `append_axes`. For instance, `'30%'` means 30% of the parent Axes width (or height, depending on `location`). The default value is `'30%'`. pad : float, optional Padding between the main axis and colorbar. The default value is 0.3. box_aspect : float, optional Aspect ratio of the colorbar box. The default value is 1. r_min : float, optional Inner radius of the colorbar. The default value is 0.2. r_max : float, optional Outer radius of the colorbar. The default value is 0.5. center: tuple, optional The coordinate center (x,y) around which to draw the circular colorbar. The default is (0.5, 0.5) zero_location : str, optional Starting location of the colorbar ('N', 'E', 'S', 'W'). The default zero location is "N". clockwise : bool, optional Direction of rotation. If True, clockwise; if False, counterclockwise. The default value is True. adapt_limits: bool, optional If True, automatically adjusts the the limits of the plot to include the circle colorbar if center and r_min or r_max are changed. The default is True. antialiased : bool, optional Whether to antialias the wedges. The default is False. add_contour : bool, optional Whether to add contour circles. The default is True. contour_color : str, optional Color of the contour circles. The default color "black". contour_linewidth : float, optional Line width of the contour circles. wedges_edgecolor : str, optional Edge color of the wedges. The default is "none". wedges_linewidths : float, optional Line widths of the wedges. add_ticks : bool, optional Whether to add tick marks. The default is True. ticklength : float, optional Length of the tick mark. The default value is 0.02. tickcolor : str, optional Color of the tick mark. The default color is "black". tickwidth : float, optional Line width of the tick marks. The default value is 1. ticks : array-like, optional Positions of the ticks in radians. ticklabels : array-like, optional Labels for the ticks. ticklabels_pad : float, optional Radial offset for the tick labels. The default value is 0.05. ticklabels_size : int, optional Font size for the tick labels. The default value is 10. Returns ------- matplotlib.collection.PatchCollection """ # Initialize arguments # - theta_offset in degrees ! zero_location_dict = {"N": -90, "E": 0, "S": 90, "W": -180} theta_offset = zero_location_dict[zero_location] if isinstance(zero_location, str) else zero_location - 90 direction = -1 if clockwise else 1 # Define n n = cmap.N # Determine colorbar axis if cax is not None: pass elif ax is not None: # and cax is None divider = make_axes_locatable(ax) cax = divider.append_axes(location, size=size, pad=pad, axes_class=plt.Axes) cax.set_box_aspect(box_aspect) else: _, cax = plt.subplots() # Set equal axis ratio cax.set_aspect("equal") # Create list of wedges (annular patches) wedges = _create_wedges( r_min=r_min, r_max=r_max, n=n, center=center, theta_offset=theta_offset, direction=direction, ) # Define values values = np.linspace(0, 1, n) # Create a patch collection with the color mapped to the values collection = PatchCollection( wedges, cmap=cmap, match_original=False, antialiased=antialiased, edgecolor=wedges_edgecolor, # "none" linewidths=wedges_linewidths, ) collection.set_array(values) # Add the collection to the axis cax.add_collection(collection) # Add custom ticks and labels if ticks is not None and ticklabels is not None: # Conversion of ticks from radians to degrees ticks = ticks / np.pi * 180 # Add ticks labels _add_ticklabels( ax=cax, ticks=ticks, ticklabels=ticklabels, ticklabels_pad=ticklabels_pad, ticklabels_size=ticklabels_size, r_max=r_max, center=center, direction=direction, theta_offset=theta_offset, ) # Add ticks line if add_ticks: _add_ticks( ax=cax, ticks=ticks, r_max=r_max, center=center, direction=direction, theta_offset=theta_offset, ticklength=ticklength, tickcolor=tickcolor, tickwidth=tickwidth, ) # Add circle if add_contour: cax.add_patch(Circle(center, r_max, color=contour_color, lw=contour_linewidth, fill=False)) cax.add_patch(Circle(center, r_min, color=contour_color, lw=contour_linewidth, fill=False)) # Turn off axis cax.set_axis_off() # Adapt limits if adapt_limits: _set_adaptive_limits(ax=cax, r_max=r_max, center=center, margin_factor=0.1) # Return ax return collection
[docs] def plot_circular_colorbar_polar( cmap, *, # Location ax=None, cax=None, location="right", size="30%", pad=0.3, # Appearance r_min=0.8, r_max=1, antialiased=False, # Orientation clockwise=True, zero_location="N", # Contours add_contour=True, contour_color="black", contour_linewidth=None, # Ticklabels options ticks=None, ticklabels=None, ticklabels_pad=4, ticklabels_size=10, ): """ Plot a circular colorbar on a polar projection. This function plots a circular colorbar representing the specified cyclic colormap using pcolormesh in a polar projection Axes. With this approach, it is not possible to add tick lines with this function ! You can either provide: - An existing Axes (`ax`) in which to place the colorbar (the colorbar will be appended to one of its sides). - A dedicated Axes object (`cax`) for direct drawing of the colorbar on the specified `cax`. - Or no Axes at all, in which case a new figure and Axes are created. If both `ax` and `cax` are given, `ax` is ignored !. Parameters ---------- cmap : matplotlib.colors.Colormap Colormap to use for the wedges. ax : matplotlib.axes.Axes or cartopy.mpl.geoaxes.GeoAxesSubplot, optional The Axes to which the colorbar should be appended. Ignored if `cax` is provided. If both `ax` and `cax` are None, a new figure and Axes are created. If ax is specified and cax is None, arguments 'pad', 'size' and 'box_aspect' controls the appearance of the new axis. cax : matplotlib.axes.Axes, optional The Axes in which to directly draw the colorbar. If provided, `ax` is ignored. location : str, optional The side of the plot ('right', 'left', 'top', 'bottom') where the colorbar should be placed. The default location is "right". size : str, optional The size of the colorbar relative to the parent Axes when using `append_axes`. For instance, `'30%'` means 30% of the parent Axes width (or height, depending on `location`). The default value is `'30%'`. pad : float, optional Padding between the main axis and colorbar. The default value is 0.3. r_min : float, optional Inner radius of the colorbar. The default value is 0.8. r_max : float, optional Outer radius of the colorbar. The default value is 1. zero_location : str, optional Starting location of the colorbar ('N', 'E', 'S', 'W'). The default zero location is "N". clockwise : bool, optional Direction of rotation. If True, clockwise; if False, counterclockwise. The default value is True. antialiased : bool, optional Whether to antialias the mesh. The default is False. add_contour : bool, optional Whether to add contour circles. The default is True. contour_color : str, optional Color of the contour circles. The default color "black". contour_linewidth : float, optional Line width of the contour circles. ticks : array-like, optional Positions of the ticks in radians. ticklabels : array-like, optional Labels for the ticks. ticklabels_pad : float, optional Radial offset for the tick labels. The default value is 0.05. ticklabels_size : int, optional Font size for the tick labels. The default value is 10. Returns ------- matplotlib.collections.QuadMesh """ from pycolorbar.univariate import plot_circular_colormap # Determine colorbar axis if cax is not None: pass elif ax is not None: # and cax is None divider = make_axes_locatable(ax) cax = divider.append_axes(location, size=size, pad=pad, axes_class=mpl.projections.polar.PolarAxes) else: cax = None # New plot created in plot_circular_colormap # Draw circular colormap p = plot_circular_colormap( cmap=cmap, ax=cax, r_min=r_min, r_max=r_max, antialiased=antialiased, add_title=False, # Orientation clockwise=clockwise, zero_location=zero_location, # Contours add_contour=add_contour, contour_color=contour_color, contour_linewidth=contour_linewidth, ) # Improve ticklabels of the colorbar p.axes.tick_params(pad=ticklabels_pad, labelsize=ticklabels_size) # Add ticks if necessary if ticks is not None and ticklabels is not None: p.axes.set_xticks(ticks, labels=ticklabels, fontsize=ticklabels_size) return p
####-----------------------------------------------------------------------------------
[docs] def plot_circular_colorbar( cmap, *, # Location ax=None, cax=None, location="right", size="30%", pad=0.3, box_aspect=1, # Approach use_wedges=True, r_min=0.2, r_max=0.5, antialiased=False, # Not available for method="polar" center=(0.5, 0.5), adapt_limits=True, wedges_edgecolor="none", wedges_linewidths=None, # Orientation zero_location="N", clockwise=True, # Contour add_contour=True, contour_color="black", contour_linewidth=None, # Ticks (not available for method="polar") add_ticks=True, ticklength=0.02, tickcolor="black", tickwidth=1, # Ticklabels ticks=None, ticklabels=None, ticklabels_pad=None, ticklabels_size=10, ): """ Plot a circular colorbar either drawing wedges or a mesh in polar projection. You can either provide: - An existing Axes (`ax`) in which to place the colorbar (the colorbar will be appended to one of its sides). - A dedicated Axes object (`cax`) for direct drawing of the colorbar on the specified `cax`. - Or no Axes at all, in which case a new figure and Axes are created. If both `ax` and `cax` are given, `ax` is ignored !. Parameters ---------- cmap : matplotlib.colors.Colormap Colormap to use for the wedges. ax : matplotlib.axes.Axes or cartopy.mpl.geoaxes.GeoAxesSubplot, optional The Axes to which the colorbar should be appended. Ignored if `cax` is provided. If both `ax` and `cax` are None, a new figure and Axes are created. If ax is specified and cax is None, arguments 'pad', 'size' and 'box_aspect' controls the appearance of the new axis. cax : matplotlib.axes.Axes, optional The Axes in which to directly draw the colorbar. If provided, `ax` is ignored. location : str, optional The side of the plot ('right', 'left', 'top', 'bottom') where the colorbar should be placed. The default location is "right". size : str, optional The size of the colorbar relative to the parent Axes when using `append_axes`. For instance, `'30%'` means 30% of the parent Axes width (or height, depending on `location`). The default value is `'30%'`. pad : float, optional Padding between the main axis and colorbar. The default value is 0.3. box_aspect : float, optional Aspect ratio of the colorbar box. The default value is 1. r_min, r_max : float, optional Inner and outer radius of the colorbar. If use_wedges=True, these define the annular wedge boundaries. If use_wedges=False,these define the radial range in the polar projection. center: tuple, optional The coordinate center (x,y) around which to draw the circular colorbar. The default is (0.5, 0.5). Only used if ``use_wedges=True``. zero_location : str, optional Starting location of the colorbar ('N', 'E', 'S', 'W'). The default zero location is "N". clockwise : bool, optional Direction of rotation. If True, clockwise; if False, counterclockwise. The default value is True. adapt_limits: bool, optional If True, automatically adjusts the the limits of the plot to include the circle colorbar if center and r_min or r_max are changed. Only used if ``use_wedges=True``. The default is True. antialiased : bool, optional Whether to antialias the wedges. The default is False. add_contour : bool, optional Whether to add contour circles. The default is True. contour_color : str, optional Color of the contour circles. The default color is "black". contour_linewidth : float, optional Line width of the contour circles. wedges_edgecolor : str, optional Edge color of the wedges. The default is "none". Only used if ``use_wedges=True``. wedges_linewidths : float, optional Line widths of the wedges. Only used if ``use_wedges=True``. add_ticks : bool, optional Whether to add tick marks. The default is True. Only used if ``use_wedges=True``. ticklength : float, optional Length of the tick marks. The default value is 0.02. Only used if ``use_wedges=True``. tickcolor : str, optional Color of the tick marks. The default color is "black". Only used if ``use_wedges=True``. tickwidth : float, optional Line width of the tick marks. The default value is 1. Only used if ``use_wedges=True``. ticks : array-like, optional Positions of the ticks in radians. ticklabels : array-like, optional Labels for the ticks. ticklabels_pad : float, optional Radial offset for the tick labels. The default value is 0.05. ticklabels_size : int, optional Font size for the tick labels. The default value is 10. Returns ------- matplotlib.collection.PatchCollection or matplotlib.collection.Quadmesh """ # ------------------------------------------------ # Accept LinearSegmentedColormap if not hasattr(cmap, "N"): cmap = cmap.resampled(256) # ------------------------------------------------ # Call colorbar method if use_wedges: ticklabels_pad = 0.05 if ticklabels_pad is None else ticklabels_pad r_min = 0.2 if r_min is None else r_min r_max = 0.5 if r_max is None else r_max p = plot_circular_colorbar_wedges( cmap=cmap, # Location ax=ax, cax=cax, location=location, size=size, pad=pad, box_aspect=box_aspect, # Appearance r_min=r_min, r_max=r_max, antialiased=antialiased, # Not available for method="polar" center=center, adapt_limits=adapt_limits, wedges_edgecolor=wedges_edgecolor, wedges_linewidths=wedges_linewidths, # Orientation clockwise=clockwise, zero_location=zero_location, # Contour add_contour=add_contour, contour_color=contour_color, contour_linewidth=contour_linewidth, # Ticks (not available for method="polar") add_ticks=add_ticks, ticklength=ticklength, tickcolor=tickcolor, tickwidth=tickwidth, # Ticklabels ticks=ticks, ticklabels=ticklabels, ticklabels_pad=ticklabels_pad, ticklabels_size=ticklabels_size, ) return p # ------------------------------------------------ # Polar approach ticklabels_pad = 4 if ticklabels_pad is None else ticklabels_pad r_min = 0.8 if r_min is None else r_min r_max = 0.1 if r_max is None else r_max p = plot_circular_colorbar_polar( cmap=cmap, # Location ax=ax, cax=cax, location=location, size=size, pad=pad, # box_aspect = box_aspect, # unused # Appearance r_min=r_min, r_max=r_max, antialiased=antialiased, # Orientation clockwise=clockwise, zero_location=zero_location, # Contours add_contour=add_contour, contour_color=contour_color, contour_linewidth=contour_linewidth, # Ticklabels options ticks=ticks, ticklabels=ticklabels, ticklabels_pad=ticklabels_pad, ticklabels_size=ticklabels_size, ) return p
####---------------------------------------------------------------------------------.
[docs] def add_circular_colorbar_legend( *, cmap, ax, # Inset options box_aspect=1, height=0.2, pad=0.005, loc="upper right", inside_figure=True, optimize_layout=True, # Fancybox options fancybox=False, fancybox_pad=0, fancybox_fc="white", fancybox_ec="none", fancybox_lw=0.5, fancybox_alpha=0.4, fancybox_shape="square", # Colorbar options use_wedges=True, **kwargs, ): """ Add the circular colorbar legend to a plot. Parameters ---------- ax : matplotlib.axes.Axes The axes to which the colorbar legend will be added. cmap: matplotlib.colors.Colormap A cyclic matplotlib colormap. box_aspect : float, optional Aspect ratio of the inset Axes. Default is 1. height : float, optional Height of the inset as a fraction [0-1] of the main Axes. Default is 0.2. pad : float, optional Padding between the inset and main Axes in figure coordinates. Default is 0.005. loc : str or tuple, optional Location of the inset. Default is 'upper right'. inside_figure : bool, optional Whether inset is inside the figure region. Default is True. optimize_layout : bool, optional Whether to auto-adjust the inset position for ticklabels. Default is True. NOTE: If True, do not call `fig.tight_layout()` afterwards. fancybox : bool, optional Whether to draw a fancy box behind the inset. Default is False. fancybox_pad : float, optional Padding of the fancy box in figure coordinates. Default is 0. fancybox_fc : str, optional Face color of the fancy box. Default is 'white'. fancybox_ec : str, optional Edge color of the fancy box. Default is 'none'. fancybox_lw : float, optional Line width of the fancy box. Default is 0.5. fancybox_alpha : float, optional Alpha of the fancy box. Default is 0.4. fancybox_shape : {'circle', 'square'}, optional Shape of the fancy box. Default is 'square'. discrete: float, optional Whether to plot a discrete or continuous circular colorbar. If True (the default) call the plot_circular_colorbar_discrete function. Otherwise call the plot_circular_colorbar function **kwargs : dict Additional keyword arguments passed to the circular colorbar. See the plot_circular_colorbar documentation. Returns ------- matplotlib.image.AxesImage The image object representing the circular colorbar. """ # Define axis projection projection = None if use_wedges else "polar" # Define plot_circular_colorbar kwargs colorbar_func_kwargs = dict( cmap=cmap, use_wedges=use_wedges, **kwargs, ) # Add colorbar inset p_cbar = add_colorbar_inset( ax=ax, colorbar_func=plot_circular_colorbar, colorbar_func_kwargs=colorbar_func_kwargs, # Inset options projection=projection, box_aspect=box_aspect, height=height, pad=pad, loc=loc, inside_figure=inside_figure, optimize_layout=optimize_layout, fancybox=fancybox, fancybox_pad=fancybox_pad, fancybox_fc=fancybox_fc, fancybox_ec=fancybox_ec, fancybox_lw=fancybox_lw, fancybox_alpha=fancybox_alpha, fancybox_shape=fancybox_shape, ) return p_cbar