Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9087c6e
Figure.subplot: Pythonic implemention for the subplot tagging
seisman Dec 20, 2025
6a05085
Figure.subplot: Pythonic implemention for the subplot tagging
seisman Dec 20, 2025
72671b7
Merge branch 'main' into subplot/A
seisman Jan 30, 2026
b316a22
Checking tag_position and tag_box
seisman Jan 30, 2026
58e8044
Add doctests
seisman Jan 30, 2026
7675235
Merge remote-tracking branch 'origin/subplot/A' into subplot/A
seisman Jan 30, 2026
7096da6
Update tests and gallery examples
seisman Jan 30, 2026
8340829
Fix the validation logic for tag_position
seisman Jan 30, 2026
9d27178
Rename test_subplot_autolabel_margins_title.png.dvc -> test_subplot_…
seisman Jan 30, 2026
d91d395
Merge branch 'main' into subplot/A
seisman Feb 5, 2026
0c7de28
Fix conflicts
seisman Feb 5, 2026
89e4e88
Add more tests
seisman Feb 5, 2026
c3a3282
Improve comments
seisman Feb 5, 2026
af724ea
Fix type hints
seisman Feb 5, 2026
a002c23
Merge branch 'main' into subplot/A
seisman Feb 6, 2026
76bee1e
Merge branch 'main' into subplot/A
seisman Feb 6, 2026
a009dac
Merge branch 'main' into subplot/A
seisman Feb 6, 2026
1f15907
Rename autotag to tag
seisman Feb 7, 2026
1f7af38
Fix a typo
seisman Feb 7, 2026
d652e01
Fix tests
seisman Feb 7, 2026
f768117
Merge remote-tracking branch 'origin/subplot/A' into subplot/A
seisman Feb 7, 2026
81ae0ee
Improve docstrings
seisman Feb 7, 2026
0c85280
Rewrap to 88 characeters
seisman Feb 7, 2026
48ccdf9
Merge branch 'main' into subplot/A
seisman Feb 7, 2026
6020654
Merge branch 'main' into subplot/A
seisman Feb 8, 2026
719ce8e
Split tests for tag_box and tag_position
seisman Feb 9, 2026
b24ca35
Merge branch 'main' into subplot/A
seisman Feb 9, 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
33 changes: 15 additions & 18 deletions examples/gallery/embellishments/colorbars_multiple.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
Multiple colormaps
==================

This gallery example shows how to create multiple colormaps for different
subplots. To better understand how GMT modern mode maintains several levels of
colormaps, please refer to
:gmt-docs:`reference/features.html#gmt-modern-mode-hierarchical-levels` for
This gallery example shows how to create multiple colormaps for different subplots. To
better understand how GMT modern mode maintains several levels of colormaps, please
refer to :gmt-docs:`reference/features.html#gmt-modern-mode-hierarchical-levels` for
details.
"""

Expand All @@ -19,26 +18,24 @@
subset_region = [-14, 30, 35, 60]
grid_subset = pygmt.datasets.load_earth_relief(resolution="10m", region=subset_region)

# Define a 1-row, 2-column subplot layout. The overall figure dimensions are
# set to be 15 cm wide and 8 cm high. Each subplot is automatically labelled.
# The space between the subplots is set to be 0.5 cm.
with fig.subplot(
nrows=1, ncols=2, figsize=("15c", "8c"), autolabel=True, margins="0.5c"
):
# Activate the first panel so that the colormap created by the makecpt
# function is a panel-level CPT
# Define a 1-row, 2-column subplot layout. The overall figure dimensions are set to be
# 15 cm wide and 8 cm high. Each subplot is automatically tagged. The space between the
# subplots is set to be 0.5 cm.
with fig.subplot(nrows=1, ncols=2, figsize=("15c", "8c"), autotag=True, margins=0.5):
# Activate the first panel so that the colormap created by the makecpt function is
# a panel-level CPT
with fig.set_panel(panel=0):
pygmt.makecpt(cmap="gmt/geo", series=[-8000, 8000])
# "R?" means Winkel Tripel projection with map width automatically
# determined from the subplot width.
# "R?" means Winkel Tripel projection with map width automatically determined
# from the subplot width.
fig.grdimage(grid=grid_globe, projection="R?", region="g", frame="a")
fig.colorbar(frame=["a4000f2000", "x+lElevation", "y+lm"])
# Activate the second panel so that the colormap created by the makecpt
# function is a panel-level CPT
# Activate the second panel so that the colormap created by the makecpt function is
# a panel-level CPT
with fig.set_panel(panel=1):
pygmt.makecpt(cmap="gmt/globe", series=[-6000, 3000])
# "M?" means Mercator projection with map width also automatically
# determined from the subplot width.
# "M?" means Mercator projection with map width also automatically determined
# from the subplot width.
fig.grdimage(grid=grid_subset, projection="M?", region=subset_region, frame="a")
fig.colorbar(frame=["a2000f1000", "x+lElevation", "y+lm"])

Expand Down
22 changes: 11 additions & 11 deletions examples/tutorials/advanced/subplots.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
===============

When you're preparing a figure for a paper, there will often be times when
you'll need to put many individual plots into one large figure, and label them
you'll need to put many individual plots into one large figure, and tag them
'abcd'. These individual plots are called subplots.

There are two main ways to create subplots in GMT:
Expand Down Expand Up @@ -100,7 +100,7 @@
nrows=2,
ncols=2,
figsize=("15c", "6c"),
autolabel=True,
autotag=True,
frame=["af", "WSne"],
margins=["0.1c", "0.2c"],
title="My Subplot Heading",
Expand All @@ -117,7 +117,7 @@
# 15 cm wide and 6 cm high (``figsize=["15c", "6c"]``). In addition, we use
# some optional parameters to fine-tune some details of the figure creation:
#
# - ``autolabel=True``: Each subplot is automatically labelled 'abcd'.
# - ``autotag=True``: Each subplot is automatically tagged 'abcd'.
# - ``margins=["0.1c", "0.2c"]``: Adjusts the space between adjacent subplots.
# In this case, it is set as 0.1 cm in the x-direction and 0.2 cm in the
# y-direction.
Expand Down Expand Up @@ -168,7 +168,7 @@
nrows=2,
ncols=2,
figsize=("15c", "6c"), # width of 15 cm, height of 6 cm
autolabel=True,
autotag=True,
margins=["0.3c", "0.2c"], # horizontal 0.3 cm and vertical 0.2 cm margins
title="My Subplot Heading",
sharex="b", # shared x-axis on the bottom side
Expand Down Expand Up @@ -204,7 +204,7 @@

fig = pygmt.Figure()
# Bottom row, two subplots
with fig.subplot(nrows=1, ncols=2, figsize=("15c", "3c"), autolabel="b)"):
with fig.subplot(nrows=1, ncols=2, figsize=("15c", "3c"), autotag="b)"):
fig.basemap(
region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], panel=[0, 0]
)
Expand All @@ -214,7 +214,7 @@
# Move plot origin by 1 cm above the height of the entire figure
fig.shift_origin(yshift="h+1c")
# Top row, one subplot
with fig.subplot(nrows=1, ncols=1, figsize=("15c", "3c"), autolabel="a)"):
with fig.subplot(nrows=1, ncols=1, figsize=("15c", "3c"), autotag="a)"):
fig.basemap(
region=[0, 10, 0, 10], projection="X?", frame=["af", "WSne"], panel=[0, 0]
)
Expand All @@ -223,20 +223,20 @@
fig.show()

# %%
# We start by drawing the bottom two subplots, setting ``autolabel="b)"`` so
# that the subplots are labelled 'b)' and 'c)'. Next, we use
# We start by drawing the bottom two subplots, setting ``autotag="b)"`` so
# that the subplots are tagged 'b)' and 'c)'. Next, we use
# :meth:`pygmt.Figure.shift_origin` to move the plot origin 1 cm above the
# **h**\ eight of the entire figure that is currently plotted (i.e. the bottom
# row subplots). A single subplot is then plotted on the top row. You may need
# to adjust the ``yshift`` parameter to make your plot look nice. This top row
# uses ``autolabel="a)"``, and we also plotted some text inside. Note that
# uses ``autotag="a)"``, and we also plotted some text inside. Note that
# ``projection="X?"`` was used to let GMT automatically determine the size of
# the subplot according to the size of the subplot area.

# %%
# You can also manually override the ``autolabel`` for each subplot using for
# You can also manually override the ``autotag`` for each subplot using for
# example, ``fig.set_panel(..., fixedlabel="b) Panel 2")`` which would allow
# you to manually label a single subplot as you wish. This can be useful for
# you to manually tag a single subplot as you wish. This can be useful for
# adding a more descriptive subtitle to individual subplots.

# sphinx_gallery_thumbnail_number = 3
215 changes: 180 additions & 35 deletions pygmt/src/subplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,129 @@
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 GMTParameterError, GMTValueError
from pygmt.exceptions import GMTInvalidInput, GMTParameterError, GMTValueError
from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias
from pygmt.params import Box, Position
from pygmt.src._common import _parse_position


def _alias_option_A( # noqa: N802
autotag: str | bool = False,
tag_position: AnchorCode | Position | None = None,
tag_box: Box | None = None,
tag_number_style: Literal["arabic", "roman", "Roman"] | None = None,
tag_orientation: Literal["horizontal", "vertical"] | None = None,
autolabel: str | bool = False,
):
"""
Helper function to create the alias list for the -A option.

Examples
--------
>>> def parse(**kwargs):
... return AliasSystem(A=_alias_option_A(**kwargs)).get("A")
>>> parse(autotag="a)")
'a)'
>>> parse(tag_position="TL")
'+jTL'
>>> parse(tag_position=Position("TL", cstype="inside", offset=("2c", "2c")))
'+jTL+o2c/2c'
>>> parse(tag_position=Position("TL", cstype="outside", offset=("2c", "2c")))
'+JTL+o2c/2c'
>>> parse(tag_box=Box(pen="1p,red", clearance="2c"))
'+c2c+p1p,red'
>>> parse(tag_number_style="roman")
'+r'
>>> parse(tag_orientation="vertical")
'+v'
>>> parse(
... autotag="(1)",
... tag_position="TL",
... tag_box=Box(pen="1p,red"),
... tag_number_style="Roman",
... tag_orientation="horizontal",
... )
'(1)+jTL+p1p,red+R'
"""
# Check conflicts with deprecated 'autolabel' parameter.
if autolabel:
if any(
v is not None and v is not False
for v in [autotag, tag_position, tag_box, tag_number_style, tag_orientation]
):
msg = (
"The 'autolabel' parameter is deprecated since v0.19.0. "
"Please use the parameters 'autotag', 'tag_position', 'tag_box', "
"'tag_number_style', 'tag_orientation', and 'tag_font' instead."
)
raise GMTInvalidInput(msg)
return Alias(autolabel, name="autolabel")

# Validate tag_box if provided.
if tag_box:
if any(
v is not None and v is not False
for v in {tag_box.inner_pen, tag_box.inner_gap, tag_box.radius}
):
raise GMTValueError(
tag_box,
description="Box properties for 'tag_box' in 'Figure.subplot'",
reason="The 'inner_pen', 'inner_gap', and 'radius' properties are not supported.",
)
if isinstance(tag_box.clearance, Sequence) and len(tag_box.clearance) > 2:
raise GMTValueError(
tag_box,
description="Box 'clearance' property for 'tag_box' in 'Figure.subplot'",
reason="Only one or two values are accepted.",
)
# Validate the tag_position if provided.
if getattr(tag_position, "cstype", None) in {
"mapcoords",
"plotcoords",
"boxcoords",
}:
raise GMTValueError(
tag_position,
description="tag position for 'Figure.subplot'.",
reason="Only 'inside' or 'outside' cstype is allowed.",
)

return [
Alias(autotag, name="autotag"),
# tag_position's prefix is "+", not "+j" or "+J".
Alias(_parse_position(tag_position), name="tag_position", prefix="+"),
Alias(tag_box, name="tag_box"),
Alias(
tag_number_style,
name="tag_number_style",
mapping={"arabic": "", "roman": "+r", "Roman": "+R"},
),
Alias(
tag_orientation,
name="tag_orientation",
mapping={"horizontal": "", "vertical": "+v"},
),
]


@fmt_docstring
@contextlib.contextmanager
@use_alias(
Ff="figsize",
Fs="subsize",
A="autolabel",
C="clearance",
SC="sharex",
SR="sharey",
)
@use_alias(Ff="figsize", Fs="subsize", C="clearance", SC="sharex", SR="sharey")
@kwargs_to_strings(Ff="sequence", Fs="sequence")
def subplot(
def subplot( # noqa: PLR0913
self,
nrows: int = 1,
ncols: int = 1,
autotag: str | bool = False,
tag_position: AnchorCode | Position | None = None,
tag_box: Box | None = None,
tag_orientation: Literal["horizontal", "vertical"] | None = None,
tag_number_style: Literal["arabic", "roman", "Roman"] | None = None,
tag_font: str | None = None,
autolabel: str | bool = False,
margins: float | str | Sequence[float | str] | None = None,
title: str | None = None,
projection: str | None = None,
Expand Down Expand Up @@ -67,31 +169,59 @@ def subplot(
Specify the dimensions of each subplot directly as [*width*, *height*].
Note that only one of ``figsize`` or ``subsize`` can be provided at
once.
autotag
Specify automatic tagging of each subplot. It can accept a number, or a letter.
The number or letter can be surrounded by parentheses on any side if these
should be typeset as part of the tag. This sets the tag of the first, top-left
subplot and others follow sequentially. If set to ``True``, default to ``"a)"``.

Examples are:

- ``autotag="a"``: tags are ``a``, ``b``, ``c``, ...
- ``autotag="1"``: tags are ``1``, ``2``, ``3``, ...
- ``autotag="a)"``: tags are ``a)``, ``b)``, ``c)``, ...
- ``autotag="(c)"``: tags are ``(c)``, ``(d)``, ``(e)``, ...
- ``autotag=True``: same as ``autotag="a)"``.
tag_position
Position of the subplot tag on the plot. It can be specified in two ways:

- 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.
- A :class:`pygmt.params.Position` object to fully control the position and
offset. **Note**: the ``refpoint`` propterty of the Position object must be
an two-character justification code, and ``cstype`` must be set to either
``"inside"`` or ``"outside"``,

If not specified, defaults to Top Left corner inside the plot with the offset
default to ``("4p", "4p")``, i.e., 20% of the :gmt-term:`FONT_TAG` size.
tag_box
Draw a box around the subplot tag. See :class:`pygmt.params.Box` for details on
how to specify the box.

**Notes on the use of the ``Box`` class:**

autolabel : bool or str
[*autolabel*][**+c**\ *dx*\ [/*dy*]][**+g**\ *fill*][**+j**\|\ **J**\
*refpoint*][**+o**\ *dx*\ [/*dy*]][**+p**\ *pen*][**+r**\|\ **R**]\ [**+v**].
Specify automatic tagging of each subplot. Append either a number or letter
[Default is ``"a"``]. This sets the tag of the first, top-left subplot and
others follow sequentially. Surround the number or letter by parentheses on
any side if these should be typeset as part of the tag [Default is ``")"``].
Use **+j**\|\ **J** for setting *refpoint* via a
:doc:`2-character justification code </techref/justification_codes>`
to specify where the tag should be placed in the subplot [Default is ``"TL"``
for the Top Left corner]. **Note**: **+j** sets the justification of the tag
to *refpoint* (suitable for interior tags) while **+J** instead selects the
mirror opposite (suitable for exterior tags). Append **+c**\ *dx*\[/*dy*] to
set the clearance between the tag and a surrounding text box requested via
**+g** or **+p** [Default is ``"3p/3p"``, i.e., 15 % of the
:gmt-term:`FONT_TAG` size dimension]. Append **+g**\ *fill* to paint the tag's
text box with *fill* [Default is no fill]. Append **+o**\ *dx*\ [/*dy*] to
offset the tag's reference point in the direction implied by the justification
[Default is ``"4p/4p"``, i.e., 20 % of the :gmt-term:`FONT_TAG` size]. Append
**+p**\ *pen* to draw the outline of the tag's text box using the selected *pen*
[Default is no outline]. Append **+r** to typeset your tag numbers using
lowercase Roman numerals; use **+R** for uppercase Roman numerals [Default is
Arabic numerals]. Append **+v** to increase tag numbers vertically down columns
[Default is horizontally across rows].
- The property ``clearance`` only accept one or two values.
- The property ``inner_pen``/``inner_gap``/``radius`` is not supported.
tag_number_style
Style of the subplot tag numbers. It can be:

- ``"arabic"``: Arabic numerals: 1, 2, 3, ... [Default].
- ``"roman"``: Lowercase Roman numerals: i, ii, iii, ...
- ``"Roman"``: Uppercase Roman numerals: I, II, III, ...
tag_orientation
Orientation of the subplot tag. It can be:

- ``"horizontal"``: Increase tag numbers horizontally across rows [Default].
- ``"vertical"``: Increase tag numbers vertically down columns.
tag_font
Font for the subplot tag [Default to ``"20p,Helvetica,black"``].
autolabel
Specify automatic tagging of each subplot.

.. deprecated:: v0.19.0

Use the parameters ``autotag``, ``tag_position``, ``tag_box``,
``tag_number_style``, ``tag_orientation``, and ``tag_font`` instead.
clearance : str or list
[*side*]\ *clearance*.
Reserve a space of dimension *clearance* between the margin and the
Expand Down Expand Up @@ -169,6 +299,14 @@ def subplot(
raise GMTParameterError(at_most_one=["figsize", "subsize"])

aliasdict = AliasSystem(
A=_alias_option_A(
autotag=autotag,
tag_position=tag_position,
tag_box=tag_box,
tag_number_style=tag_number_style,
tag_orientation=tag_orientation,
autolabel=autolabel,
),
M=Alias(margins, name="margins", sep="/", size=(2, 4)),
T=Alias(title, name="title"),
).add_common(
Expand All @@ -179,6 +317,9 @@ def subplot(
)
aliasdict.merge(kwargs)

# Configure FONT_TAG if tag_font is set
confdict = {"FONT_TAG": tag_font} if tag_font is not None else {}

# Need to use separate sessions for "subplot begin" and "subplot end".
# Otherwise, "subplot end" will use the last session, which may cause
# strange positioning issues for later plotting calls.
Expand All @@ -187,7 +328,11 @@ def subplot(
with Session() as lib:
lib.call_module(
module="subplot",
args=["begin", f"{nrows}x{ncols}", *build_arg_list(aliasdict)],
args=[
"begin",
f"{nrows}x{ncols}",
*build_arg_list(aliasdict, confdict=confdict),
],
)
yield
finally:
Expand Down
Loading
Loading