Skip to content
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 labelled. 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.5c"):
# 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
214 changes: 180 additions & 34 deletions pygmt/src/subplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,130 @@
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 GMTInvalidInput, 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.18.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 tag_box.clearance 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"),
Alias(
_parse_position(tag_position), name="tag_position", prefix="+"
), # Prefix is "+".
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 +170,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 @@ -170,6 +301,14 @@ def subplot(
raise GMTInvalidInput(msg)

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 @@ -180,6 +319,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 @@ -188,7 +330,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