From 8bdb98ef06661e876b172467ff353f0db3d99f05 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sun, 10 Aug 2025 21:17:41 +0800 Subject: [PATCH 1/9] Figure.colorbar: Add parameters position/width/height and more to specify colorbar position and properties --- pygmt/src/_common.py | 9 +- pygmt/src/colorbar.py | 197 +++++++++++++++++++++++++++++++++++------- 2 files changed, 173 insertions(+), 33 deletions(-) diff --git a/pygmt/src/_common.py b/pygmt/src/_common.py index 92202828df2..231e9907795 100644 --- a/pygmt/src/_common.py +++ b/pygmt/src/_common.py @@ -251,7 +251,7 @@ def _parse_position( position: Position | Sequence[float | str] | str | None, kwdict: dict[str, Any], default: Position | None, -) -> Position | str: +) -> Position | str | None: """ Parse the "position" parameter for embellishment-plotting functions. @@ -269,7 +269,8 @@ def _parse_position( The keyword arguments dictionary that conflicts with ``position`` if ``position`` is given as a raw GMT command string. default - The default Position object to use if ``position`` is ``None``. + The default Position object to use if ``position`` is ``None``. If ``default`` + is ``None``, the GMT default is used. Returns ------- @@ -339,7 +340,7 @@ def _parse_position( case str() if position in _valid_anchors: # Anchor code position = Position(position, cstype="inside") case str(): # Raw GMT command string. - if any(v is not None for v in kwdict.values()): + if any(v not in {None, False} for v in kwdict.values()): msg = ( "Parameter 'position' is given with a raw GMT command string, and " f"conflicts with parameters {', '.join(repr(c) for c in kwdict)}." @@ -349,7 +350,7 @@ def _parse_position( position = Position(position, cstype="plotcoords") case Position(): # Already a Position object. pass - case None if default is not None: # Set default position. + case None: # Set default position. position = default case _: msg = f"Invalid type for parameter 'position': {type(position)}." diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index 3e7efb32984..f6b447fa25b 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -5,31 +5,87 @@ 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.exceptions import GMTValueError from pygmt.helpers import build_arg_list, fmt_docstring, use_alias -from pygmt.params import Box +from pygmt.helpers.utils import is_nonstr_iter +from pygmt.params import Box, Position +from pygmt.src._common import _parse_position __doctest_skip__ = ["colorbar"] +def _parse_move_text( + move_text: Sequence[str] | None, label_as_column: bool = False +) -> str | None: + """ + Parse the move_text parameter into the format required by GMT. + + Examples + -------- + >>> _parse_move_text(None) + >>> _parse_move_text(["annotations", "label"]) + 'al' + >>> _parse_move_text(["unit"]) + 'u' + >>> _parse_move_text(["annotations", "label", "unit"]) + 'alu' + >>> _parse_move_text(["annotations"], label_as_column=True) + 'ac' + >>> _parse_move_text(["invalid"]) + Traceback (most recent call last): + ... + pygmt.exceptions.GMTValueError: Invalid move_text: ['invalid']. Expected ... + """ + _valids = {"annotations", "label", "unit"} + match move_text: + case Sequence() if is_nonstr_iter(move_text) and all( + v in _valids for v in move_text + ): + argstr = "".join(item[0] for item in move_text) + if label_as_column is True: + argstr += "c" + return argstr + case None: + return None + case _: + raise GMTValueError( + move_text, + description="move_text", + choices=_valids, + ) + + @fmt_docstring -@use_alias(C="cmap", D="position", L="equalsize", Z="zfile") +@use_alias(C="cmap", L="equalsize", Z="zfile") def colorbar( # noqa: PLR0913 self, + position: Position | Sequence[float | str] | AnchorCode | None = None, + length: float | str | None = None, + width: float | str | None = None, + orientation: Literal["horizontal", "vertical"] | None = None, + reverse: bool = False, + nan_rectangle: bool | str = False, + nan_rectangle_position: Literal["start", "end"] | None = None, + sidebar_triangles: bool | Literal["foreground", "background"] = False, + sidebar_triangles_height: float | None = None, + move_text: Sequence[str] | None = None, + label_as_column: bool = False, + box: Box | bool = False, truncate: Sequence[float] | None = None, shading: float | Sequence[float] | bool = False, log: bool = False, scale: float | None = None, projection: str | None = None, - box: Box | bool = False, - frame: str | Sequence[str] | bool = False, region: Sequence[float | str] | str | None = None, + frame: str | Sequence[str] | bool = False, verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] | bool = False, panel: int | Sequence[int] | bool = False, - transparency: float | None = None, perspective: float | Sequence[float] | str | bool = False, + transparency: float | None = None, **kwargs, ): r""" @@ -58,6 +114,10 @@ def colorbar( # noqa: PLR0913 $aliases - B = frame + - D = position, **+w**: length/width, **+h**/**+v**: orientation, + **+r**: reverse, **+n**: nan_rectangle/nan_rectangle_position, + **+e**: sidebar_triangles/scalebar_triangles_height, + **+m**: move_text/label_as_column - F = box - G = truncate - I = shading @@ -72,31 +132,56 @@ def colorbar( # noqa: PLR0913 Parameters ---------- - frame : str or list - Set colorbar boundary frame, labels, and axes attributes. $cmap - position : str - [**g**\|\ **j**\|\ **J**\|\ **n**\|\ **x**]\ *refpoint*\ - [**+w**\ *length*\ [/\ *width*]]\ [**+e**\ [**b**\|\ **f**][*length*]]\ - [**+h**\|\ **v**][**+j**\ *justify*]\ - [**+m**\ [**a**\|\ **c**\|\ **l**\|\ **u**]]\ - [**+n**\ [*txt*]][**+o**\ *dx*\ [/*dy*]]. - Define the reference point on the map for the color scale using one of - four coordinate systems: (1) Use **g** for map (user) coordinates, (2) - use **j** or **J** for setting *refpoint* via a - :doc:`2-character justification code ` - that refers to the (invisible) map domain rectangle, - (3) use **n** for normalized (0-1) coordinates, or (4) use **x** for - plot coordinates (inches, cm, etc.). All but **x** requires both - ``region`` and ``projection`` to be specified. Append **+w** followed - by the length and width of the colorbar. If width is not specified - then it is set to 4% of the given length. Give a negative length to - reverse the scale bar. Append **+h** to get a horizontal scale - [Default is vertical (**+v**)]. By default, the anchor point on the - scale is assumed to be the bottom left corner (**BL**), but this can - be changed by appending **+j** followed by a - :doc:`2-character justification code ` - *justify*. + position + Position of the GMT logo 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 ` for a + position inside the plot, e.g., ``"TL"`` for Top Left corner inside the plot. + + If not specified, defaults to the bottom-center corner outside of the plot. + length + width + Length and width of the color bar. If length is given with a unit ``%`` then it + is in percentage of the corrresponding plot side dimension (i.e., plot width for + a horizontal colorbar, or plot height for a vertical colorbar). If width is + given with unit ``%`` then it is in percentage of the bar length. [Length + default to 80% of the corresponding plot side dimension, and width default to + 4% of the bar length]. + orientation + Set the colorbar orientation to either ``"horizontal"`` or ``"vertical"``. + [Default is vertical, unless position is set to bottom-center or top-center with + ``cstype="outside"`` or ``cstype="inside"``, then horizontal is the default]. + reverse + Reverse the positive direction of the bar. + nan_rectangle + Draw a rectangle filled with the NaN color (via the **N** entry in the CPT or + :term:`COLOR_NAN` if no such entry) at the start of the colorbar. If a string + is given, use that string as the label for the NaN color. + nan_rectangle_position + Set the position of the NaN rectangle. Choose from ``"start"`` or ``"end"``. + [Default is ``"start"``]. + sidebar_triangles + Draw sidebar triangles for back- and/or foreground colors. If set to ``True``, + both triangles are drawn. Alternatively, set it to ``"foreground"`` or + ``"background"`` to draw only one triangle. The back- and/or foreground colors + are taken from the **B** and **F** entries in the CPT. If no such entries exist, + then the system default colors for **B** and **F** are used instead ( + :term:`COLOR_BACKGROUND` and :term:`COLOR_FOREGROUND`). + sidebar_triangles_height + Height of the sidebar triangles [Default is half the bar width]. + move_text + Move text (annotations, label, and unit) to opposite side. Accept a sequence of + strings containing one or more of ``"annotations"``, ``"label"``, and + ``"unit"``. The default placement of these texts depends on the colorbar + orientation and position. + label_as_column + Print a vertical label as a column of characters (does not work with special + characters). box Draw a background box behind the colorbar. If set to ``True``, a simple rectangular box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box @@ -138,6 +223,10 @@ def colorbar( # noqa: PLR0913 may be in plot distance units or given as relative fractions and will be automatically scaled so that the sum of the widths equals the requested colorbar length. + $projection + $region + frame : str or list + Set colorbar boundary frame, labels, and axes attributes. $verbose $panel $perspective @@ -162,7 +251,57 @@ def colorbar( # noqa: PLR0913 """ self._activate_figure() + position = _parse_position( + position, + kwdict={ + "length": length, + "width": width, + "orientation": orientation, + "reverse": reverse, + "nan_rectangle": nan_rectangle, + "nan_rectangle_position": nan_rectangle_position, + "sidebar_triangles": sidebar_triangles, + "sidebar_triangles_height": sidebar_triangles_height, + "move_text": move_text, + "label_as_column": label_as_column, + }, + default=None, # Use GMT's default behavior if position is not provided. + ) + aliasdict = AliasSystem( + D=[ + Alias(position, name="position"), + Alias(length, name="length", prefix="+w"), # +wlength/width + Alias(width, name="width", prefix="/"), + Alias( + orientation, + name="orientation", + mapping={"horizontal": "+h", "vertical": "+v"}, + ), + Alias(reverse, name="reverse", prefix="+r"), + Alias( + nan_rectangle, + name="nan_rectangle", + prefix="+n" if nan_rectangle_position == "start" else "+N", + ), + Alias( + sidebar_triangles, + name="sidebar_triangles", + prefix="+e", + mapping={ + True: True, + False: False, + "foreground": "f", + "background": "b", + }, + ), + Alias(sidebar_triangles_height, name="sidebar_triangles_height"), + Alias( + _parse_move_text(move_text, label_as_column), + name="move_text", + prefix="+m", + ), + ], F=Alias(box, name="box"), G=Alias(truncate, name="truncate", sep="/", size=2), I=Alias(shading, name="shading", sep="/", size=2), From 573f0a723e9ba699859765425b334d06dc4dc953 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sat, 20 Dec 2025 12:24:06 +0800 Subject: [PATCH 2/9] Fix typos --- pygmt/src/colorbar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index f6b447fa25b..42bb897ed1b 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -138,12 +138,12 @@ def colorbar( # noqa: PLR0913 - 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 + - 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 ` for a position inside the plot, e.g., ``"TL"`` for Top Left corner inside the plot. - If not specified, defaults to the bottom-center corner outside of the plot. + If not specified, defaults to the Bottom Center outside of the plot. length width Length and width of the color bar. If length is given with a unit ``%`` then it From b04b4e35d3e85f0f576c9faaf65e8e655d639447 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 26 Dec 2025 18:13:39 +0900 Subject: [PATCH 3/9] Add tests for the complicated -D option --- pygmt/src/colorbar.py | 151 +++++++++++++++++++---------------- pygmt/tests/test_colorbar.py | 37 +++++++++ 2 files changed, 118 insertions(+), 70 deletions(-) diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index 42bb897ed1b..802578481e6 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -17,45 +17,76 @@ __doctest_skip__ = ["colorbar"] -def _parse_move_text( - move_text: Sequence[str] | None, label_as_column: bool = False -) -> str | None: +def _alias_option_D( # noqa: N802, PLR0913 + position=None, + length=None, + width=None, + orientation=None, + reverse=None, + nan_rectangle=None, + nan_rectangle_position=None, + sidebar_triangles=None, + sidebar_triangles_height=None, + move_text=None, + label_as_column=None, +): """ - Parse the move_text parameter into the format required by GMT. - - Examples - -------- - >>> _parse_move_text(None) - >>> _parse_move_text(["annotations", "label"]) - 'al' - >>> _parse_move_text(["unit"]) - 'u' - >>> _parse_move_text(["annotations", "label", "unit"]) - 'alu' - >>> _parse_move_text(["annotations"], label_as_column=True) - 'ac' - >>> _parse_move_text(["invalid"]) - Traceback (most recent call last): - ... - pygmt.exceptions.GMTValueError: Invalid move_text: ['invalid']. Expected ... + Return a list of Alias objects for the -D option. """ - _valids = {"annotations", "label", "unit"} - match move_text: - case Sequence() if is_nonstr_iter(move_text) and all( - v in _valids for v in move_text - ): - argstr = "".join(item[0] for item in move_text) - if label_as_column is True: - argstr += "c" - return argstr - case None: - return None - case _: - raise GMTValueError( - move_text, - description="move_text", - choices=_valids, - ) + # Parse the 'move_text' and 'label_as_column' parameters for the +m modifier. + if move_text or label_as_column: + modifier_m = "" + _valids = {"annotations", "label", "unit"} + + match move_text: + case None: + pass + case str() if move_text in _valids: + modifier_m = move_text[0] + case Sequence() if is_nonstr_iter(move_text) and all( + v in _valids for v in move_text + ): + modifier_m = "".join(item[0] for item in move_text) + case _: + raise GMTValueError( + move_text, + description="move_text", + choices=_valids, + ) + if label_as_column: + modifier_m += "c" + else: + modifier_m = None + + return [ + Alias(position, name="position"), + Alias(length, name="length", prefix="+w"), # +wlength/width + Alias(width, name="width", prefix="/"), + Alias( + orientation, + name="orientation", + mapping={"horizontal": "+h", "vertical": "+v"}, + ), + Alias(reverse, name="reverse", prefix="+r"), + Alias( + nan_rectangle, + name="nan_rectangle", + prefix="+n" if nan_rectangle_position in {"start", None} else "+N", + ), + Alias( + sidebar_triangles, + name="sidebar_triangles", + prefix="+e", + mapping={ + True: True, + False: False, + "foreground": "f", + "background": "b", + }, + ), + Alias(sidebar_triangles_height, name="sidebar_triangles_height"), + Alias(modifier_m, name="move_text/label_as_column", prefix="+m"), + ] @fmt_docstring @@ -269,39 +300,19 @@ def colorbar( # noqa: PLR0913 ) aliasdict = AliasSystem( - D=[ - Alias(position, name="position"), - Alias(length, name="length", prefix="+w"), # +wlength/width - Alias(width, name="width", prefix="/"), - Alias( - orientation, - name="orientation", - mapping={"horizontal": "+h", "vertical": "+v"}, - ), - Alias(reverse, name="reverse", prefix="+r"), - Alias( - nan_rectangle, - name="nan_rectangle", - prefix="+n" if nan_rectangle_position == "start" else "+N", - ), - Alias( - sidebar_triangles, - name="sidebar_triangles", - prefix="+e", - mapping={ - True: True, - False: False, - "foreground": "f", - "background": "b", - }, - ), - Alias(sidebar_triangles_height, name="sidebar_triangles_height"), - Alias( - _parse_move_text(move_text, label_as_column), - name="move_text", - prefix="+m", - ), - ], + D=_alias_option_D( + position=position, + length=length, + width=width, + orientation=orientation, + reverse=None, + nan_rectangle=None, + nan_rectangle_position=None, + sidebar_triangles=None, + sidebar_triangles_height=None, + move_text=None, + label_as_column=None, + ), F=Alias(box, name="box"), G=Alias(truncate, name="truncate", sep="/", size=2), I=Alias(shading, name="shading", sep="/", size=2), diff --git a/pygmt/tests/test_colorbar.py b/pygmt/tests/test_colorbar.py index 3b132a5d64f..2f9558a1fd1 100644 --- a/pygmt/tests/test_colorbar.py +++ b/pygmt/tests/test_colorbar.py @@ -4,6 +4,9 @@ import pytest from pygmt import Figure +from pygmt.alias import AliasSystem +from pygmt.params.position import Position +from pygmt.src.colorbar import _alias_option_D @pytest.mark.benchmark @@ -26,3 +29,37 @@ def test_colorbar_shading_list(): fig.basemap(region=[0, 10, 0, 2], projection="X10c/2c", frame="a") fig.colorbar(cmap="geo", shading=[-0.7, 0.2], frame=True) return fig + + +def test_colorbar_alias_D(): # noqa: N802 + """ + Test the parameters for the -D option. + """ + + def alias_wrapper(**kwargs): + """ + A wrapper function for testing the parameters of -D option. + """ + aliasdict = AliasSystem(D=_alias_option_D(**kwargs)) + return aliasdict.get("D") + + argstr = alias_wrapper(position=Position("TL", offset=0.2), length=4, width=0.5) + assert argstr == "jTL+o0.2+w4/0.5" + + assert alias_wrapper(orientation="horizontal") == "+h" + assert alias_wrapper(orientation="vertical") == "+v" + + assert alias_wrapper(reverse=True) == "+r" + + assert alias_wrapper(nan_rectangle=True) == "+n" + assert alias_wrapper(nan_rectangle=True, nan_rectangle_position="end") == "+N" + + assert alias_wrapper(sidebar_triangles=True) == "+e" + assert alias_wrapper(sidebar_triangles="foreground") == "+ef" + assert alias_wrapper(sidebar_triangles="background") == "+eb" + assert ( + alias_wrapper(sidebar_triangles=True, sidebar_triangles_height=0.3) == "+e0.3" + ) + + assert alias_wrapper(move_text=["annotations", "label", "unit"]) == "+malu" + assert alias_wrapper(label_as_column=True) == "+mc" From fb107685784d85df90cfa936b3b1ebf2d65e5490 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 30 Dec 2025 14:41:04 +0800 Subject: [PATCH 4/9] Fix docstring --- pygmt/src/colorbar.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index 04ca993536c..9b63b1cd599 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -145,10 +145,6 @@ def colorbar( # noqa: PLR0913 $aliases - B = frame - - D = position, **+w**: length/width, **+h**/**+v**: orientation, - **+r**: reverse, **+n**: nan_rectangle/nan_rectangle_position, - **+e**: sidebar_triangles/scalebar_triangles_height, - **+m**: move_text/label_as_column - F = box - G = truncate - I = shading @@ -161,6 +157,14 @@ def colorbar( # noqa: PLR0913 - p = perspective - t = transparency + .. hlist:: + :columns: 1 + + - D = position, **+w**: length/width, **+h**/**+v**: orientation, + **+r**: reverse, **+n**: nan_rectangle/nan_rectangle_position, + **+e**: sidebar_triangles/scalebar_triangles_height, + **+m**: move_text/label_as_column + Parameters ---------- $cmap From d62eb93efde208673da867d053b87ee4377128a5 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 30 Dec 2025 14:42:14 +0800 Subject: [PATCH 5/9] Fix term to gmt-term --- pygmt/src/colorbar.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index 9b63b1cd599..26bd5eb9c86 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -195,8 +195,8 @@ def colorbar( # noqa: PLR0913 Reverse the positive direction of the bar. nan_rectangle Draw a rectangle filled with the NaN color (via the **N** entry in the CPT or - :term:`COLOR_NAN` if no such entry) at the start of the colorbar. If a string - is given, use that string as the label for the NaN color. + :gmt-term:`COLOR_NAN` if no such entry) at the start of the colorbar. If a + string is given, use that string as the label for the NaN color. nan_rectangle_position Set the position of the NaN rectangle. Choose from ``"start"`` or ``"end"``. [Default is ``"start"``]. @@ -206,7 +206,7 @@ def colorbar( # noqa: PLR0913 ``"background"`` to draw only one triangle. The back- and/or foreground colors are taken from the **B** and **F** entries in the CPT. If no such entries exist, then the system default colors for **B** and **F** are used instead ( - :term:`COLOR_BACKGROUND` and :term:`COLOR_FOREGROUND`). + :gmt-term:`COLOR_BACKGROUND` and :gmt-term:`COLOR_FOREGROUND`). sidebar_triangles_height Height of the sidebar triangles [Default is half the bar width]. move_text From 93fe64348dcb56270e00bd70daa717e7f5072078 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 31 Dec 2025 08:28:04 +0800 Subject: [PATCH 6/9] Fix --- pygmt/src/colorbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index 26bd5eb9c86..eefd8afc28b 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -195,7 +195,7 @@ def colorbar( # noqa: PLR0913 Reverse the positive direction of the bar. nan_rectangle Draw a rectangle filled with the NaN color (via the **N** entry in the CPT or - :gmt-term:`COLOR_NAN` if no such entry) at the start of the colorbar. If a + :gmt-term:`COLOR_NAN` if no such entry) at the start of the colorbar. If a string is given, use that string as the label for the NaN color. nan_rectangle_position Set the position of the NaN rectangle. Choose from ``"start"`` or ``"end"``. From 75e09a3d314e0ba2ed714c3bf5490d952f04a8b7 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 1 Jan 2026 13:06:47 +0800 Subject: [PATCH 7/9] Add tests for colorbar --- pygmt/src/_common.py | 2 +- pygmt/tests/test_colorbar.py | 41 +++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/pygmt/src/_common.py b/pygmt/src/_common.py index 46a6a152f04..0dba544fe67 100644 --- a/pygmt/src/_common.py +++ b/pygmt/src/_common.py @@ -345,7 +345,7 @@ def _parse_position( case str() if position in _valid_anchors: # Anchor code position = Position(position, cstype="inside") case str(): # Raw GMT command string. - if any(v not in {None, False} for v in kwdict.values()): + if any(v is not None and v is not False for v in kwdict.values()): msg = ( "Parameter 'position' is given with a raw GMT command string, and " f"conflicts with parameters {', '.join(repr(c) for c in kwdict)}." diff --git a/pygmt/tests/test_colorbar.py b/pygmt/tests/test_colorbar.py index a86d9f9dba0..3ef937ecc27 100644 --- a/pygmt/tests/test_colorbar.py +++ b/pygmt/tests/test_colorbar.py @@ -5,6 +5,7 @@ import pytest from pygmt import Figure from pygmt.alias import AliasSystem +from pygmt.exceptions import GMTInvalidInput from pygmt.params.position import Position from pygmt.src.colorbar import _alias_option_D @@ -16,7 +17,12 @@ def test_colorbar(): Create a simple colorbar. """ fig = Figure() - fig.colorbar(cmap="gmt/rainbow", position="x0c/0c+w4c", frame=True) + fig.colorbar( + cmap="gmt/rainbow", + position=Position((0, 0), cstype="plotcoords"), + length=4, + frame=True, + ) return fig @@ -63,3 +69,36 @@ def alias_wrapper(**kwargs): assert alias_wrapper(move_text=["annotations", "label", "unit"]) == "+malu" assert alias_wrapper(label_as_column=True) == "+mc" + + +@pytest.mark.mpl_image_compare(filename="test_colorbar.png") +def test_colorbar_position_deprecated_syntax(): + """ + Check that passing the deprecated GMT CLI syntax string to 'position' works. + """ + fig = Figure() + fig.colorbar(cmap="gmt/rainbow", position="x0/0+w4c", frame=True) + return fig + + +def test_image_position_mixed_syntax(): + """ + Test that mixing deprecated GMT CLI syntax string with new parameters. + """ + fig = Figure() + with pytest.raises(GMTInvalidInput): + fig.colorbar(cmap="gmt/rainbow", position="x0/0", length="4c") + with pytest.raises(GMTInvalidInput): + fig.colorbar(cmap="gmt/rainbow", position="x0/0", width="0.5c") + with pytest.raises(GMTInvalidInput): + fig.colorbar(cmap="gmt/rainbow", position="x0/0", orientation="horizontal") + with pytest.raises(GMTInvalidInput): + fig.colorbar(cmap="gmt/rainbow", position="x0/0", reverse=True) + with pytest.raises(GMTInvalidInput): + fig.colorbar(cmap="gmt/rainbow", position="x0/0", nan_rectangle=True) + with pytest.raises(GMTInvalidInput): + fig.colorbar(cmap="gmt/rainbow", position="x0/0", sidebar_triangles=True) + with pytest.raises(GMTInvalidInput): + fig.colorbar(cmap="gmt/rainbow", position="x0/0", move_text=["label"]) + with pytest.raises(GMTInvalidInput): + fig.colorbar(cmap="gmt/rainbow", position="x0/0", label_as_column=True) From a638a03b71c9785154620763b23813796e86419f Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 1 Jan 2026 13:22:18 +0800 Subject: [PATCH 8/9] Fix typos --- pygmt/src/colorbar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index eefd8afc28b..e3e8f817e87 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -169,7 +169,7 @@ def colorbar( # noqa: PLR0913 ---------- $cmap position - Position of the GMT logo on the plot. It can be specified in multiple ways: + Position of the colorbar 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. @@ -181,7 +181,7 @@ def colorbar( # noqa: PLR0913 If not specified, defaults to the Bottom Center outside of the plot. length width - Length and width of the color bar. If length is given with a unit ``%`` then it + Length and width of the colorbar. If length is given with a unit ``%`` then it is in percentage of the corrresponding plot side dimension (i.e., plot width for a horizontal colorbar, or plot height for a vertical colorbar). If width is given with unit ``%`` then it is in percentage of the bar length. [Length From 65f094aa953bc58fd1fe75974151258f26a0ec33 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 6 Jan 2026 08:12:18 +0800 Subject: [PATCH 9/9] Update pygmt/src/colorbar.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com> --- pygmt/src/colorbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/colorbar.py b/pygmt/src/colorbar.py index e3e8f817e87..decc3dbdadc 100644 --- a/pygmt/src/colorbar.py +++ b/pygmt/src/colorbar.py @@ -182,7 +182,7 @@ def colorbar( # noqa: PLR0913 length width Length and width of the colorbar. If length is given with a unit ``%`` then it - is in percentage of the corrresponding plot side dimension (i.e., plot width for + is in percentage of the corresponding plot side dimension (i.e., plot width for a horizontal colorbar, or plot height for a vertical colorbar). If width is given with unit ``%`` then it is in percentage of the bar length. [Length default to 80% of the corresponding plot side dimension, and width default to