Source code for pygmt.src.scalebar

"""
scalebar - Add a scale bar.
"""

from collections.abc import Sequence
from typing import Literal

from pygmt._typing import AnchorCode
from pygmt.alias import Alias, AliasSystem
from pygmt.clib import Session
from pygmt.helpers import build_arg_list, fmt_docstring
from pygmt.params import Box, Position
from pygmt.src._common import _parse_position

__doctest_skip__ = ["scalebar"]


@fmt_docstring
def scalebar(  # noqa: PLR0913
    self,
    length: float | str,
    height: float | str | None = None,
    position: Position | Sequence[float | str] | AnchorCode | None = None,
    scale_loc: float | Sequence[float] | bool = False,
    label: str | bool = False,
    label_alignment: Literal["left", "right", "top", "bottom"] | None = None,
    unit: bool = False,
    fancy: bool = False,
    vertical: bool = False,
    box: Box | bool = False,
    verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"]
    | bool = False,
    panel: int | Sequence[int] | bool = False,
    perspective: float | Sequence[float] | str | bool = False,
    transparency: float | None = None,
):
    """
    Add a scale bar on the plot.

    Parameters
    ----------
    length
        Length of the scale bar in kilometers. Append a suffix to specify another unit.
        Valid units are: **e**: meters; **f**: feet; **k**: kilometers; **M**: statute
        miles; **n**: nautical miles; **u**: US survey feet.
    height
        Height of the scale bar [Default is ``"5p"``]. Only works when ``fancy=True``.
    position
        Position of the scale bar on the plot. It can be specified in multiple ways:

        - A :class:`pygmt.params.Position` object to fully control the reference point,
          anchor point, and offset.
        - A sequence of two values representing the x- and y-coordinates in plot
          coordinates, e.g., ``(1, 2)`` or ``("1c", "2c")``.
        - A :doc:`2-character justification code </techref/justification_codes>` for a
          position inside the plot, e.g., ``"TL"`` for Top Left corner inside the plot.

        If not specified, defaults to the Bottom Left corner of the plot with a 0.2-cm
        and 0.4-cm offset in the x- and y-directions, respectively.
    scale_loc
        Specify the location where the map scale is calculated. It can be:

        - *slat*: Map scale is calculated for latitude *slat*.
        - (*slon*, *slat*): Map scale is calculated for latitude *slat* and longitude
          *slon*, which is useful for oblique projections.
        - ``True``: Map scale is calculated for the middle of the map.
        - ``False``: Default to the location of the reference point.
    label
        Text string for the scale bar label. If ``False``, no label is added. If
        ``True``, the distance unit provided in the ``length`` parameter is used
        [Default is ``"km"``]. Requires ``fancy=True``.
    label_alignment
        Alignment of the scale bar label. Choose from ``"left"``, ``"right"``,
        ``"top"``, or ``"bottom"`` [Default is ``"top"``].
    fancy
        If ``True``, draw a "fancy" scale bar, which is a segmented bar with alternating
        black and white rectangles. If ``False``, draw a plain scale bar. Only supported
        for non-Cartesian projections.
    unit
        If ``True``, append the unit to all distance annotations along the scale. For a
        plain scale, this will instead select the unit to be appended to the distance
        length. The unit is determined from the suffix provided to the ``length``
        parameter or defaults to ``"km"``.
    vertical
        If ``True``, plot a vertical rather than a horizontal scale. Only
        supported for Cartesian projections.
    box
        Draw a background box behind the scale bar. If set to ``True``, a simple
        rectangular box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box
        appearance, pass a :class:`pygmt.params.Box` object to control style, fill, pen,
        and other box properties.
    $verbose
    $panel
    $perspective
    $transparency

    Examples
    --------
    >>> import pygmt
    >>> from pygmt.params import Position
    >>> fig = pygmt.Figure()
    >>> fig.basemap(region=[0, 80, -30, 30], projection="M10c", frame=True)
    >>> fig.scalebar(
    ...     length=1000,
    ...     position=Position((10, 10), cstype="mapcoords"),
    ...     fancy=True,
    ...     label="Scale",
    ...     unit=True,
    ... )
    >>> fig.show()
    """
    self._activate_figure()
    position = _parse_position(position, default=Position("BL", offset=(0.2, 0.4)))

    aliasdict = AliasSystem(
        F=Alias(box, name="box"),
        L=[
            Alias(position, name="position"),
            Alias(length, name="length", prefix="+w"),
            Alias(
                label_alignment,
                name="label_alignment",
                prefix="+a",
                mapping={"left": "l", "right": "r", "top": "t", "bottom": "b"},
            ),
            Alias(scale_loc, name="scale_loc", prefix="+c", sep="/", size=2),
            Alias(fancy, name="fancy", prefix="+f"),
            Alias(label, name="label", prefix="+l"),
            Alias(unit, name="unit", prefix="+u"),
            Alias(vertical, name="vertical", prefix="+v"),
        ],
    ).add_common(
        V=verbose,
        c=panel,
        p=perspective,
        t=transparency,
    )

    confdict = {}
    if height is not None:
        confdict["MAP_SCALE_HEIGHT"] = height

    with Session() as lib:
        lib.call_module(
            module="basemap", args=build_arg_list(aliasdict, confdict=confdict)
        )