Skip to content
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d8eabe7
Add the Frame/Axis class for setting frame and axes
seisman Feb 10, 2026
87a2407
Merge branch 'main' into class/frame-part1
seisman Feb 11, 2026
a184f48
Add to doc index
seisman Feb 11, 2026
3e1ad10
Improve docstrings for 'axes'
seisman Feb 11, 2026
8f3a67c
Fix docstrings
seisman Feb 11, 2026
5fbe28b
Add docstrings for annot/grid/tick
seisman Feb 11, 2026
d320711
Add one more test
seisman Feb 11, 2026
0ba431d
Add doctest
seisman Feb 11, 2026
b61639a
Add doctests
seisman Feb 11, 2026
3a33364
Fix docstrings
seisman Feb 11, 2026
c1dadd6
Fix an edge case
seisman Feb 12, 2026
2dde989
Allow setting annotation angles
seisman Feb 12, 2026
e990cbd
Figure.colorbar: Pythonic way to set labels and annotations
seisman Feb 10, 2026
3c75b93
Fix doctest
seisman Feb 11, 2026
c946198
Fix typing issue
seisman Feb 12, 2026
de43548
Support angle/prefix/unit
seisman Feb 12, 2026
072fde2
Fix a typo
seisman Feb 12, 2026
7aa50bd
Add more doctests
seisman Feb 12, 2026
13f13bf
Fix a typo in doctest
seisman Feb 12, 2026
b003726
Fix doctest
seisman Feb 12, 2026
a36b687
Merge branch 'main' into class/frame-part1
seisman Feb 24, 2026
03e0ee7
Merge branch 'main' into class/frame-part1
seisman Feb 25, 2026
5832ad9
Merge branch 'class/frame-part1' into colorbar/frame
seisman Feb 25, 2026
68261ed
Fix a typo
seisman Feb 25, 2026
c3e7d29
Merge branch 'class/frame-part1' into colorbar/frame
seisman Feb 25, 2026
3f94e41
Fix merge conflicts
seisman Feb 25, 2026
5498316
Allow frame='none' in colorbar
seisman Feb 25, 2026
73bb75f
Merge branch 'main' into colorbar/frame
seisman Mar 9, 2026
e30070f
Improve docstrings
seisman Mar 9, 2026
e89cc2e
Merge branch 'main' into colorbar/frame
seisman Mar 10, 2026
bbbda25
Merge branch 'main' into colorbar/frame
yvonnefroehlich Mar 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 105 additions & 12 deletions pygmt/src/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,77 @@
from pygmt.exceptions import GMTValueError
from pygmt.helpers import build_arg_list, fmt_docstring, use_alias
from pygmt.helpers.utils import is_nonstr_iter
from pygmt.params import Box, Position
from pygmt.params import Axis, Box, Frame, Position
from pygmt.src._common import _parse_position

__doctest_skip__ = ["colorbar"]


def _build_frame(
annot: float | bool = False,
tick: float | bool = False,
grid: float | bool = False,
annot_angle: float | None = None,
annot_prefix: str | None = None,
annot_unit: str | None = None,
label: str | None = None,
unit: str | None = None,
frame=None,
):
"""
Create the list of Alias objects for the -B option.

>>> list(_build_frame(annot=1, tick=0.5, label="Distance", unit="km"))
['xa1f0.5+lDistance', 'y+lkm']

>>> list(
... _build_frame(
... annot=1,
... tick=0.5,
... grid=0.2,
... annot_angle=30,
... label="Distance",
... unit="km",
... )
... )
['xa1f0.5g0.2+lDistance+a30', 'y+lkm']
>>> list(_build_frame(frame=["xaf0.5+lDistance", "y+lkm"]))
['xaf0.5+lDistance', 'y+lkm']

>>> _build_frame(frame="none")
'none'
>>> _build_frame() # Passing no parameters returns None
"""
# Using the old 'frame' parameter.
if frame is not None and frame is not False:
return frame

_xaxis_is_set = any(
v is not None and v is not False
for v in {annot, tick, grid, annot_angle, annot_prefix, annot_unit, label}
)
_yaxis_is_set = unit is not None

# Need to return None if no parameters are give. Otherwise, it may return "".
if not (_xaxis_is_set or _yaxis_is_set):
return None

xaxis, yaxis = None, None
if _xaxis_is_set:
xaxis = Axis(
annot=annot,
tick=tick,
grid=grid,
angle=annot_angle,
prefix=annot_prefix,
unit=annot_unit,
label=label,
)
if _yaxis_is_set:
yaxis = Axis(label=unit)
return Frame(xaxis=xaxis, yaxis=yaxis)


def _alias_option_D( # noqa: N802, PLR0913
position=None,
length=None,
Expand Down Expand Up @@ -153,6 +218,14 @@ def colorbar( # noqa: PLR0913
length: float | str | None = None,
width: float | str | None = None,
orientation: Literal["horizontal", "vertical"] | None = None,
label: str | None = None,
unit: str | None = None,
annot: float | bool = False,
tick: float | bool = False,
grid: float | bool = False,
annot_angle: float | None = None,
annot_prefix: str | None = None,
annot_unit: str | None = None,
reverse: bool = False,
nan: bool = False,
nan_position: Literal["start", "end"] | None = None,
Expand Down Expand Up @@ -201,7 +274,6 @@ def colorbar( # noqa: PLR0913
Full GMT docs at :gmt-docs:`colorbar.html`.

$aliases
- B = frame
- F = box
- G = truncate
- I = shading
Expand All @@ -217,6 +289,7 @@ def colorbar( # noqa: PLR0913
.. hlist::
:columns: 1

- B = label, unit, annot, tick, grid, annot_angle, annot_prefix, annot_unit
- D = position, **+w**: length/width, **+h**/**+v**: orientation,
**+r**: reverse, **+n**: nan/nan_position,
**+e**: fg_triangle/bg_triangle/triangle_height,
Expand Down Expand Up @@ -244,6 +317,24 @@ def colorbar( # noqa: PLR0913
given with unit ``%`` then it is in percentage of the bar length. [Length
defaults to 80% of the corresponding plot side dimension, and width defaults
to 4% of the bar length].
label
unit
Set the label and unit for the colorbar. The label is placed along the colorbar
and the unit is placed at the end of the colorbar.
annot
tick
grid
Intervals for annotations, ticks, and gridlines. Refer to
:class:`pygmt.params.Axis` for more details on how these parameters work.
annot_prefix
annot_unit
annot_angle
The prefix, unit and angle for the annotations. The prefix is placed before the
annotation text; the unit is placed after the annotation text; and the angle is
the angle of the annotation text.
Comment on lines +321 to +334
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is coming from to be consistent with GMT.
I personally find it confusing to call the y-label "unit" in case the frame is related to a colorbar. Thus we have here "annot_unit" for the unit opitionally added after the annotations, but "unit" in case the frame is related to a basemap.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also debating which one to choose:

  • If we follow the convention used for the frame parameter, the names should be xlabel/ylabel/annot_prefix/annot_unit/annot_angle (x- and y-label mean labels of long and short dimensions of the colorbar)
  • If we follow the convention used by the GMT colorbar docs, the names should be label/unit/annot_prefix/annot_unit/annot_angle (label and unit are text strings displayed along the long and short dimensions of a colorbar; unit doesn't have to be a "unit"; unit and annnot_unit may be more confusing)

Or maybe we should call them xlabel/ylabel/annot_prefix/annot_suffix/annot_angle?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also debating which one to choose:

  • If we follow the convention used for the frame parameter, the names should be xlabel/ylabel/annot_prefix/annot_unit/annot_angle (x- and y-label mean labels of long and short dimensions of the colorbar)

Hm. Maybe I am already confused by myself 🙃. But do you maybe mean xlabel/ylabel/prefix/unit/angle here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I meant annot_prefix/annot_unit/annot_angle.

While prefix/unit/angle work well in the Axis class for the frame parameter, they're too ambiguous for colorbar annotations. In a colorbar context, it's unclear what prefix mean? I think adding the annot_ prefix can make the parameter's purpose clear to understand.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the changes in https://github.com/GenericMappingTools/pygmt-paper-figures/pull/39/changes, only label/unit/annot/tick/grid are used in the examples, and annot_prefix/annot_unit/annot_angle are not used.

So we just need to decide if we want xlabel/ylable or label/unit. We can discuss/implement annot_prefix/annot_unit/annot_angle in a separate PR instead.

Copy link
Member

@weiji14 weiji14 Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I somewhat prefer label/unit because it'll get confusing when you have a vertical colorbar (see elevation example in https://www.pygmt.org/v0.18.0/gallery/embellishments/colorbar.html), in which case xlabel (label) is on the 'y-axis' and ylabel (unit) is on the x-axis. But no strong opinions if we decide to go with xlabel/ylabel.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another aspect is, that we have for the move_text parameter already "label" and "unit" (and "annotations") as arguments. If we use now xlabel and ylabel as parameter names we probably have to adjust the arguments for move_text.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So let's follow the GMT convention and use label/unit. Users need to read the documentation anyway

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, with these two arguments, keeping label and unit appears better to me.

frame
Set colorbar boundary frame, labels, and axes attributes. If set to ``"none"``,
then no frame will be drawn.
orientation
Set the colorbar orientation to either ``"horizontal"`` or ``"vertical"``.
[Default is vertical, unless ``position`` is set to bottom-center or top-center
Expand Down Expand Up @@ -321,9 +412,6 @@ def colorbar( # noqa: PLR0913
requested colorbar length.
$projection
$region
frame
Set colorbar boundary frame, labels, and axes attributes. If set to ``"none"``,
then no frame will be drawn.
$verbose
$panel
$perspective
Expand All @@ -337,12 +425,7 @@ def colorbar( # noqa: PLR0913
>>> # Create a basemap
>>> fig.basemap(region=[0, 10, 0, 3], projection="X10c/3c", frame=True)
>>> # Call the colorbar method for the plot
>>> fig.colorbar(
... # Set cmap to the "roma" CPT
... cmap="SCM/roma",
... # Label the x-axis "Velocity" and the y-axis "m/s"
... frame=["x+lVelocity", "y+lm/s"],
... )
>>> fig.colorbar(cmap="SCM/roma", label="Velocity", unit="m/s")
>>> # Show the plot
>>> fig.show()
"""
Expand Down Expand Up @@ -387,7 +470,17 @@ def colorbar( # noqa: PLR0913
Q=Alias(log, name="log"),
W=Alias(scale, name="scale"),
).add_common(
B=frame,
B=_build_frame(
annot=annot,
tick=tick,
grid=grid,
annot_angle=annot_angle,
annot_prefix=annot_prefix,
annot_unit=annot_unit,
label=label,
unit=unit,
frame=frame,
),
J=projection,
R=region,
V=verbose,
Expand Down
Loading