Skip to content

Wrap subplot using with statement #822

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 42 commits into from
Feb 14, 2021
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f2ac98b
Wrap subplot - version 3
weiji14 Jan 30, 2021
683ff57
Add fig.subplot and fig.sca to API docs
weiji14 Jan 30, 2021
0742da7
Merge branch 'master' into subplot_v3
weiji14 Feb 3, 2021
95e3a64
Alias fixedlabel (A), clearance (C), verbose (V) for sca
weiji14 Feb 3, 2021
8283f65
Turn fig.sca into a context manager
weiji14 Feb 3, 2021
92b7fbd
Alias projection (J), region (R), verbose (V), x/yshift (X/Y) for sub…
weiji14 Feb 5, 2021
6cb2e59
Merge branch 'master' into subplot_v3
weiji14 Feb 5, 2021
9d6f20b
Fix subplot end -V
weiji14 Feb 5, 2021
3f0c008
Improve docstring of fig.subplot and fig.sca
weiji14 Feb 5, 2021
967fba3
Small tweaks to subplot tutorial
weiji14 Feb 5, 2021
3de8489
Merge branch 'master' into subplot_v3
weiji14 Feb 6, 2021
14ae58c
Allow list or tuple inputs to ax (c) argument in basemap
weiji14 Feb 6, 2021
ddee738
Use sequence_comma instead of sequence
weiji14 Feb 6, 2021
7a2662a
Merge branch 'master' into subplot_v3
weiji14 Feb 6, 2021
dbd4677
Remove use of matplotlib-like Axes (axs) object
weiji14 Feb 6, 2021
4126c16
Allow for spaces in title and labels without needing double quotes
weiji14 Feb 6, 2021
df9f5f0
Merge branch 'master' into subplot_v3
weiji14 Feb 6, 2021
0521851
Ensure that a list can be passed into region (R)
weiji14 Feb 6, 2021
6eb58ea
Allow for list inputs into fig.sca(ax=...)
weiji14 Feb 6, 2021
2d042eb
Merge branch 'master' into subplot_v3
weiji14 Feb 7, 2021
d70cce0
Merge branch 'master' into subplot_v3
weiji14 Feb 10, 2021
b03a5dd
Rename sca to set_panel and ax to panel
weiji14 Feb 10, 2021
1e46b71
Merge branch 'master' into subplot_v3
weiji14 Feb 11, 2021
6ba4d49
Update docstring for layout (S) to say +w is for the figsize/subsize arg
weiji14 Feb 11, 2021
d3c0a50
Apply formatting suggestions from code review
weiji14 Feb 12, 2021
eadb847
Fix bug that prevented boolean to -A from working
weiji14 Feb 12, 2021
f568982
Add note that subplot panel is activated until further notice
weiji14 Feb 12, 2021
2c239c2
Merge branch 'master' into subplot_v3
weiji14 Feb 12, 2021
6c1ed41
Remove arg_str strip() for subplot set and end
weiji14 Feb 12, 2021
fab1b9d
Split layout (S) into sharex (SC) and sharey (SR)
weiji14 Feb 12, 2021
3f042d0
Merge branch 'master' into subplot_v3
weiji14 Feb 12, 2021
ad34871
Fix small typo
weiji14 Feb 12, 2021
ef9ee2d
Proofread edits to subplot and set_panel docstring
weiji14 Feb 13, 2021
c6b0242
Update fig.set_panel clearance docstring with code-block example
weiji14 Feb 13, 2021
c475861
Validate subplot nrows/ncols and figsize/subsize argument inputs
weiji14 Feb 13, 2021
a48b600
Proof edits to subplot round 2
weiji14 Feb 13, 2021
2f18fe7
Merge branch 'master' into subplot_v3
weiji14 Feb 13, 2021
f4499a8
Revise advanced subplot layout subsection to use two subplots calls
weiji14 Feb 13, 2021
e0900e9
Proofread edits to subplot round 3
weiji14 Feb 14, 2021
ebc807e
Replace argument with parameter as per #886
weiji14 Feb 14, 2021
d16de28
Merge branch 'master' into subplot_v3
weiji14 Feb 14, 2021
30ef9c4
Proofread edits to subplot round 4
weiji14 Feb 14, 2021
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
2 changes: 2 additions & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ Plotting data and laying out the map:
Figure.meca
Figure.plot
Figure.plot3d
Figure.set_panel
Figure.shift_origin
Figure.subplot
Figure.text

Color palette table generation:
Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
tutorials/earth-relief.rst
tutorials/3d-perspective-image.rst
tutorials/inset.rst
tutorials/subplots.rst
tutorials/configuration.rst

.. toctree::
Expand Down
244 changes: 244 additions & 0 deletions examples/tutorials/subplots.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
"""
Making subplots
===============

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
'abcd'. These individual plots are called subplots.

There are two main ways to create subplots in GMT:

- Use :meth:`pygmt.Figure.shift_origin` to manually move each individual plot
to the right position.
- Use :meth:`pygmt.Figure.subplot` to define the layout of the subplots.

The first method is easier to use and should handle simple cases involving a
couple of subplots. For more advanced subplot layouts, however, we recommend the
use of :meth:`pygmt.Figure.subplot` which offers finer grained control, and
this is what the tutorial below will cover.
"""
# sphinx_gallery_thumbnail_number = 3

import pygmt

###############################################################################
#
# Let's start by initializing a :class:`pygmt.Figure` instance.

fig = pygmt.Figure()

###############################################################################
# Define subplot layout
# ---------------------
#
# The :meth:`pygmt.Figure.subplot` function is used to set up the layout, size,
# and other attributes of the figure. It divides the whole canvas into regular
# grid areas with *n* rows and *m* columns. Each grid area can contain an
# individual subplot. For example:

###############################################################################
# .. code-block:: default
#
# with fig.subplot(nrows=2, ncols=3, figsize=("15c", "6c"), frame="lrtb"):
# ...

###############################################################################
# will define our figure to have a 2 row and 3 column grid layout.
# ``figsize=("15c", "6c")`` defines the overall size of the figure to be 15 cm
# wide by 6 cm high. Using ``frame="lrtb"`` allows us to customize the map frame
# for all subplots instead of setting them individually. The figure layout will
# look like the following:

with fig.subplot(nrows=2, ncols=3, figsize=("15c", "6c"), frame="lrtb"):
for i in range(2): # row number starting from 0
for j in range(3): # column number starting from 0
index = i * 3 + j # index number starting from 0
with fig.set_panel(panel=index): # sets the current panel
fig.text(
position="MC",
text=f"index: {index}; row: {i}, col: {j}",
region=[0, 1, 0, 1],
)
fig.show()

###############################################################################
# The :meth:`pygmt.Figure.set_panel` function activates a specified subplot, and
# all subsequent plotting functions will take place in that subplot panel. This
# is similar to matplotlib's ``plt.sca`` method. In order to specify a subplot,
# you will need to provide the identifier for that subplot via the ``panel``
# argument. Pass in either the *index* number, or a tuple/list like
# (*row*, *col*) to ``panel``.

###############################################################################
# .. note::
#
# The row and column numbering starts from 0. So for a subplot layout with
# N rows and M columns, row numbers will go from 0 to N-1, and column
# numbers will go from 0 to M-1.

###############################################################################
# For example, to activate the subplot on the top right corner (index: 2) at
# *row*\=0 and *col*\=2, so that all subsequent plotting commands happen
# there, you can use the following command:

###############################################################################
# .. code-block:: default
#
# with fig.set_panel(panel=[0, 2]):
# ...

###############################################################################
# Making your first subplot
# -------------------------
# Next, let's use what we learned above to make a 2 row by 2 column subplot
# figure. We'll also pick up on some new arguments to configure our subplot.

fig = pygmt.Figure()
with fig.subplot(
nrows=2,
ncols=2,
figsize=("15c", "6c"),
autolabel=True,
frame=["af", "WSne"],
margins=["0.1c", "0.2c"],
title="My Subplot Heading",
):
fig.basemap(region=[0, 10, 0, 10], projection="X?", panel=[0, 0])
fig.basemap(region=[0, 20, 0, 10], projection="X?", panel=[0, 1])
fig.basemap(region=[0, 10, 0, 20], projection="X?", panel=[1, 0])
fig.basemap(region=[0, 20, 0, 20], projection="X?", panel=[1, 1])
fig.show()

###############################################################################
# In this example, we define a 2-row, 2-column (2x2) subplot layout using
# :meth:`pygmt.Figure.subplot`. The overall figure dimensions is set to be 15 cm
# wide and 6 cm high (``figsize=["15c", "6c"]``). In addition, we use some
# optional arguments to fine-tune some details of the figure creation:
#
# - ``autolabel=True``: Each subplot is automatically labelled 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.
# - ``title="My Subplot Heading"``: adds a title on top of the whole figure.
#
# Notice that each subplot was set to use a linear projection ``"X?"``.
# Usually, we need to specify the width and height of the map frame, but it is
# also possible to use a question mark ``"?"`` to let GMT decide automatically
# on what is the most appropriate width/height for the each subplot's map
# frame.

###############################################################################
# .. tip::
#
# In the above example, we used the following commands to activate the
# four subplots explicitly one after another::
#
# fig.basemap(..., panel=[0, 0])
# fig.basemap(..., panel=[0, 1])
# fig.basemap(..., panel=[1, 0])
# fig.basemap(..., panel=[1, 1])
#
# In fact, we can just use ``fig.basemap(..., panel=True)`` without
# specifying any subplot index number, and GMT will automatically activate
# the next subplot panel.

###############################################################################
# .. note::
#
# All plotting functions (e.g. :meth:`pygmt.Figure.coast`,
# :meth:`pygmt.Figure.text`, etc) are able to use ``panel`` argument when
# in subplot mode. Once a panel is activated using ``panel`` or
# :meth:`pygmt.Figure.set_panel`, subsequent plotting commands that don't
# set a ``panel`` will have their elements added to the same panel as
# before.

###############################################################################
# Shared X and Y axis labels
# --------------------------
# In the example above with the four subplots, the two subplots for each row
# have the same Y-axis range, and the two subplots for each column have the
# same X-axis range. You can use the ``sharex``/``sharey`` arguments to set a
# common X and/or Y axis between subplots.

fig = pygmt.Figure()
with fig.subplot(
nrows=2,
ncols=2,
figsize=("15c", "6c"), # width of 15 cm, height of 6 cm
autolabel=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
sharey="l", # shared y-axis on the left side
frame="WSrt",
):
fig.basemap(region=[0, 10, 0, 10], projection="X?", panel=True)
fig.basemap(region=[0, 20, 0, 10], projection="X?", panel=True)
fig.basemap(region=[0, 10, 0, 20], projection="X?", panel=True)
fig.basemap(region=[0, 20, 0, 20], projection="X?", panel=True)
fig.show()

###############################################################################
# ``sharex="b"`` indicates that subplots in a column will share the x-axis, and
# only the **b**\ ottom axis is displayed. ``sharey="l"`` indicates that
# subplots within a row will share the y-axis, and only the **l**\ eft axis is
# displayed.
#
# Of course, instead of using the ``sharex``/``sharey`` option, you can also
# set a different ``frame`` for each subplot to control the axis properties
# individually for each subplot.

###############################################################################
# Advanced subplot layouts
# ------------------------
#
# Nested subplot are currently not supported. If you want to create more
# complex subplot layouts, some manual adjustments are needed.
#
# The following example draws three subplots in a 2-row, 2-column layout, with
# the first subplot occupying the first row.

fig = pygmt.Figure()
with fig.subplot(nrows=2, ncols=2, figsize=("15c", "6c"), autolabel=True):
fig.basemap(
region=[0, 10, 0, 10], projection="X15c/3c", frame=["af", "WSne"], panel=[0, 0]
)
fig.text(text="TEXT", x=5, y=5, projection="X15c/3c")
fig.basemap(
region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], panel=[1, 0]
)
fig.basemap(
region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], panel=[1, 1]
)
fig.show()

###############################################################################
#
# When drawing the three basemaps, the last two basemaps use
# ``projection="X?"``, so GMT will automatically determine the size of the
# subplot according to the size of the subplot area. In order for the first
# subplot to fill up the entire top row space, we use manually adjusted the
# subplot width to 15cm using ``projection="X15c/3c"``.

###############################################################################
# .. note::
#
# There are bugs that have not been fixed in the above example.
#
# In subplot mode, the size of each subgraph is controlled by the
# ``figsize`` option of :meth:`pygmt.Figure.subplot`. Users can override
# this and use ``projection`` to specify the size of an individual subplot,
# but this size will not be remembered. If the next command does not
# specify ``projection``, the default size of the subplot mode will be
# used, and the resulting plot will be inccorect.
#
# The current workaround is to use the same ``projection`` option in all
# commands for the subplot. For example, we forced subplot (a) to have a
# different size using ``projection="15c/3c``. The next command within the
# subplot (e.g. ``text``) must also use ``projection="x15c/3c"``, otherwise
# the placement will be wrong.

###############################################################################
# Since we skipped the second subplot, the auto label function will name the
# three subplots as a, c and d, which is not what we want, so we have to use
# ``fig.set_panel(..., fixedlabel="(a)")`` to manually set the subplot label.
2 changes: 2 additions & 0 deletions pygmt/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,5 +387,7 @@ def _repr_html_(self):
meca,
plot,
plot3d,
set_panel,
subplot,
text,
)
1 change: 1 addition & 0 deletions pygmt/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from pygmt.src.meca import meca
from pygmt.src.plot import plot
from pygmt.src.plot3d import plot3d
from pygmt.src.subplot import set_panel, subplot
from pygmt.src.surface import surface
from pygmt.src.text import text_ as text # "text" is an argument within "text_"
from pygmt.src.which import which
Loading