From f2ac98b48feb5871c44ba013934791888996f079 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 30 Jan 2021 20:21:21 +1300 Subject: [PATCH 01/30] Wrap subplot - version 3 Wrapping the `subplot` function, in a `with` statement! Original GMT `subplot` function can be found at https://docs.generic-mapping-tools.org/6.1/subplot.html. This is my 3rd attempt at implementing `subplot` in PyGMT, with commits heavily re-adapted/cherry-picked from https://github.com/GenericMappingTools/pygmt/pull/412 and https://github.com/GenericMappingTools/pygmt/pull/427. --- .pylintrc | 1 + doc/index.rst | 1 + examples/tutorials/subplots.py | 238 +++++++++++++++++++++++++++++++++ pygmt/base_plotting.py | 9 +- pygmt/src/__init__.py | 1 + pygmt/src/subplot.py | 168 +++++++++++++++++++++++ pygmt/tests/test_subplot.py | 85 ++++++++++++ 7 files changed, 499 insertions(+), 4 deletions(-) create mode 100644 examples/tutorials/subplots.py create mode 100644 pygmt/src/subplot.py create mode 100644 pygmt/tests/test_subplot.py diff --git a/.pylintrc b/.pylintrc index 766bdd46b8b..e42a50bf488 100644 --- a/.pylintrc +++ b/.pylintrc @@ -441,6 +441,7 @@ function-naming-style=snake_case good-names=i, j, k, + ax, ex, Run, _, diff --git a/doc/index.rst b/doc/index.rst index 71fc08e900b..765b70e524d 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -33,6 +33,7 @@ tutorials/coastlines.rst tutorials/regions.rst tutorials/plot.rst + tutorials/subplots.rst tutorials/lines.rst tutorials/text.rst tutorials/contour-map.rst diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py new file mode 100644 index 00000000000..857967a34b5 --- /dev/null +++ b/examples/tutorials/subplots.py @@ -0,0 +1,238 @@ +""" +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. +""" + +############################################################################### +# Let's start by importing the PyGMT library and initializing a Figure + +import pygmt + +fig = pygmt.Figure() + +############################################################################### +# Define subplot layout +# --------------------- +# +# The :meth:`pygmt.Figure.subplot` command is used to setup 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" +# ) as axs: +# ... + +############################################################################### +# 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 15cm +# wide by 6cm 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") as axs: + for index in axs.flatten(): + i = index // axs.shape[1] # row + j = index % axs.shape[1] # column + fig.sca(ax=axs[i, j]) # sets the current Axes + fig.text( + position="MC", + text=f"index: {index}, row: {i}, col: {j}", + region=[0, 1, 0, 1], + ) +fig.show() + +############################################################################### +# The ``fig.sca`` command activates a specified subplot, and all subsequent +# plotting commands will take place in that subplot. 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 ``ax`` argument. This can +# be found in the ``axs`` variable referenced by the ``row`` and ``col`` +# number. + +############################################################################### +# .. 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 +# +# fig.sca(ax=axs[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 parameters to configure our subplot. + +fig = pygmt.Figure() +with fig.subplot( + nrows=2, + ncols=2, + figsize=("15c", "6c"), + autolabel=True, + margins=["0.1c", "0.2c"], + title='"My Subplot Heading"', +) as axs: + fig.basemap( + region=[0, 10, 0, 10], projection="X?", frame=["af", "WSne"], ax=axs[0, 0] + ) + fig.basemap( + region=[0, 20, 0, 10], projection="X?", frame=["af", "WSne"], ax=axs[0, 1] + ) + fig.basemap( + region=[0, 10, 0, 20], projection="X?", frame=["af", "WSne"], ax=axs[1, 0] + ) + fig.basemap( + region=[0, 20, 0, 20], projection="X?", frame=["af", "WSne"], ax=axs[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 15cm +# wide and 6cm high (``figsize=["15c", "6c"]``). In addition, we used some +# optional parameters 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(..., ax=axs[0, 0]) +# fig.basemap(..., ax=axs[0, 1]) +# fig.basemap(..., ax=axs[1, 0]) +# fig.basemap(..., ax=axs[1, 1]) +# +# In fact, we can just use ``fig.basemap(..., ax=True)`` without specifying +# any subplot index number, and GMT will automatically activate the next +# subplot. + +############################################################################### +# 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 **layout** option to set a common X and/or +# Y axis between subplots. + +fig = pygmt.Figure() +with fig.subplot( + nrows=2, + ncols=2, + figsize=("15c", "6c"), + autolabel=True, + margins=["0.3c", "0.2c"], + title='"My Subplot Heading"', + layout=["Rl", "Cb"], + frame="WSrt", +) as axs: + fig.basemap(region=[0, 10, 0, 10], projection="X?", ax=True) + fig.basemap(region=[0, 20, 0, 10], projection="X?", ax=True) + fig.basemap(region=[0, 10, 0, 20], projection="X?", ax=True) + fig.basemap(region=[0, 20, 0, 20], projection="X?", ax=True) +fig.show() + +############################################################################### +# **Rl** indicates that subplots within a **R**\ ow will share the y-axis, and +# only the **l**\ eft axis is displayed. **Cb** indicates that subplots in +# a column will share the x-axis, and only the **b**\ ottom axis is displayed. +# +# Of course, instead of using the **layout** 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"], ax=axs[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"], ax=axs[1, 0] + ) + fig.basemap( + region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], ax=axs[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.sca(A=""(a)"`` to manually set the subplot label. diff --git a/pygmt/base_plotting.py b/pygmt/base_plotting.py index 3a28c9895be..192cf7a17d6 100644 --- a/pygmt/base_plotting.py +++ b/pygmt/base_plotting.py @@ -1222,6 +1222,7 @@ def contour(self, x=None, y=None, z=None, data=None, **kwargs): V="verbose", X="xshift", Y="yshift", + c="ax", p="perspective", t="transparency", ) @@ -1265,9 +1266,9 @@ def basemap(self, **kwargs): {t} """ kwargs = self._preprocess(**kwargs) - if not args_in_kwargs(args=["B", "L", "Td", "Tm"], kwargs=kwargs): + if not args_in_kwargs(args=["B", "L", "Td", "Tm", "c"], kwargs=kwargs): raise GMTInvalidInput( - "At least one of frame, map_scale, compass, or rose must be specified." + "At least one of frame, map_scale, compass, rose, or ax must be specified." ) with Session() as lib: lib.call_module("basemap", build_arg_string(kwargs)) @@ -1642,5 +1643,5 @@ def text( arg_str = " ".join([fname, build_arg_string(kwargs)]) lib.call_module("text", arg_str) - # GMT Supplementary modules - from pygmt.src import meca # pylint: disable=import-outside-toplevel + # GMT plotting modules + from pygmt.src import meca, sca, subplot # pylint: disable=import-outside-toplevel diff --git a/pygmt/src/__init__.py b/pygmt/src/__init__.py index cdbabf1468a..f9b7fb9094a 100644 --- a/pygmt/src/__init__.py +++ b/pygmt/src/__init__.py @@ -3,3 +3,4 @@ """ # pylint: disable=import-outside-toplevel from pygmt.src.meca import meca +from pygmt.src.subplot import sca, subplot diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py new file mode 100644 index 00000000000..a2c55d698df --- /dev/null +++ b/pygmt/src/subplot.py @@ -0,0 +1,168 @@ +""" +subplot - Manage modern mode figure subplot configuration and selection. +""" +import contextlib + +import numpy as np +from pygmt.clib import Session +from pygmt.helpers import build_arg_string, fmt_docstring, kwargs_to_strings, use_alias + + +@fmt_docstring +@contextlib.contextmanager +@use_alias( + Ff="figsize", + Fs="subsize", + A="autolabel", + B="frame", + C="clearance", + M="margins", + S="layout", + T="title", +) +@kwargs_to_strings(Ff="sequence", Fs="sequence", M="sequence") +def subplot(self, nrows=1, ncols=1, **kwargs): + """ + The **subplot** module is used to split the current figure into a + rectangular layout of subplots that each may contain a single self- + contained figure. + + Begin by defining the layout of the entire multi-panel illustration. + Several options are available to specify the systematic layout, labeling, + dimensions, and more for the subplots. + + Full option list at :gmt-docs:`subplot.html#synopsis-begin-mode` + + {aliases} + + Parameters + ---------- + nrows : int + Number of vertical rows of the subplot grid. + ncols : int + Number of horizontal columns of the subplot grid. + figsize : tuple + Specify the final figure dimensions as ``(width, height)``. + subsize : tuple + Specify the dimensions of each subplot directly as ``(width, height)``. + + autolabel : bool or str + ``[autolabel][+cdx[/dy]][+gfill][+j|Jrefpoint][+odx[/dy]][+ppen][+r|R] + [+v]``. + Specify automatic tagging of each subplot. Append either a number or + letter [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. Use + **+j|J**\\ *refpoint* to specify where the tag should be placed in the + subplot [TL]. 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** [3p/3p, i.e., 15% + of the FONT_TAG size dimension]. Append **+g**\\ *fill* to paint the + tag's text box with *fill* [no painting]. Append + **+o**\\ *dx*\\ [/*dy*] to offset the tag's reference point in the + direction implied by the justification [4p/4p, i.e., 20% of the + FONT_TAG size]. Append **+p**\\ *pen* to draw the outline of the tag's + text box using selected *pen* [no outline]. Append **+r** to typeset + your tag numbers using lowercase Roman numerals; use **+R** for + uppercase Roman numerals [Arabic numerals]. Append **+v** to increase + tag numbers vertically down columns [horizontally across rows]. + clearance : str + ``[side]clearance``. + Reserve a space of dimension *clearance* between the margin and the + subplot on the specified side, using *side* values from **w**, **e**, + **s**, or **n**, or **x** for both **w** and **e** or **y** for both + **s** and **n**. No *side* means all sides. The option is repeatable + to set aside space on more than one side. Such space will be left + untouched by the main map plotting but can be accessed by modules that + plot scales, bars, text, etc. Settings specified under **begin** + directive apply to all subplots, while settings under **set** only + apply to the selected (active) subplot. **Note**: Common options + **x_offset** and **y_offset* are not available during subplots; use + **clearance** instead. + margins : tuple + This is margin space that is added between neighboring subplots (i.e., + the interior margins) in addition to the automatic space added for tick + marks, annotations, and labels. The margins can be specified as either: + + - a single value (for same margin on all sides). E.g. '5c'. + - a pair of values (for setting separate horizontal and vertical + margins). E.g. ['5c', '3c']. + - a set of four values (for setting separate left, right, bottom, and + top margins). E.g. ['1c', '2c', '3c', '4c']. + + The actual gap created is always a sum of the margins for the two + opposing sides (e.g., east plus west or south plus north margins) + [Default is half the primary annotation font size, giving the full + annotation font size as the default gap]. + layout : str or list + Set subplot layout for shared axes. May be set separately for rows + (**R**) and columns (**C**). E.g. ``layout=['Rl', 'Cb']``. + Considerations for **C**: Use when all subplots in a **C**\\ olumn + share a common *x*-range. The first (i.e., **t**\\ op) and the last + (i.e., **b**\\ ottom) rows will have *x* annotations; append **t** or + **b** to select only one of those two rows [both]. Append **+l** if + annotated *x*-axes should have a label [none]; optionally append the + label if it is the same for the entire subplot. Append **+t** to make + space for subplot titles for each row; use **+tc** for top row titles + only [no subplot titles]. Labels and titles that depends on which row + or column are specified as usual via a subplot's own **frame** setting. + Considerations for **R**: Use when all subplots in a **R**\\ ow share a + common *y*-range. The first (i.e., **l**\\ eft) and the last (i.e., + **r**\\ ight) columns will have *y*-annotations; append **l** or **r** + to select only one of those two columns [both]. Append **+l** if + annotated *y*-axes will have a label [none]; optionally, append the + label if it is the same for the entire subplot. Append **+p** to make + all annotations axis-parallel [horizontal]; if not used you may have to + set **clearance** to secure extra space for long horizontal + annotations. Append **+w** to draw horizontal and vertical lines + between interior panels using selected pen [no lines]. + title : str + Overarching heading for the entire figure. Font is determined by + setting ``FONT_HEADING``. + + Yields + ------ + axs : numpy.ndarray + Array of Axes objects. + """ + kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access + + with Session() as lib: + try: + arg_str = " ".join(["begin", f"{nrows}x{ncols}", build_arg_string(kwargs)]) + lib.call_module(module="subplot", args=arg_str) + # Setup matplotlib-like Axes + axs = np.empty(shape=(nrows, ncols), dtype=object) + for index in range(nrows * ncols): + i = index // ncols # row + j = index % ncols # column + axs[i, j] = index + yield axs + finally: + v_arg = build_arg_string(kwargs.fromkeys("V")) + lib.call_module("subplot", f"end {v_arg}".strip()) + + +@fmt_docstring +@use_alias(F="dimensions") +def sca(self, ax=None, **kwargs): + """ + Set the current Axes instance to *ax*. + + Before you start plotting you must first select the active subplot. Note: + If any *projection* option is passed with **?** as scale or width when + plotting subplots, then the dimensions of the map are automatically + determined by the subplot size and your region. For Cartesian plots: If you + want the scale to apply equally to both dimensions then you must specify + ``projection="x"`` [The default ``projection="X"`` will fill the subplot by + using unequal scales]. + + {aliases} + """ + kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access + + arg_str = " ".join(["set", f"{ax}", build_arg_string(kwargs)]) + with Session() as lib: + lib.call_module(module="subplot", args=arg_str) diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py new file mode 100644 index 00000000000..3e648ff5b18 --- /dev/null +++ b/pygmt/tests/test_subplot.py @@ -0,0 +1,85 @@ +""" +Tests subplot. +""" +from pygmt import Figure +from pygmt.helpers.testing import check_figures_equal + + +@check_figures_equal() +def test_subplot_basic_frame(): + """ + Create a subplot figure with 1 vertical row and 2 horizontal columns, and + ensure map frame setting is applied to all subplot figures. + """ + fig_ref, fig_test = Figure(), Figure() + with fig_ref.subplot(nrows=1, ncols=2, Ff="6c/3c", B="WSne"): + fig_ref.sca(ax=0) + fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot0") + fig_ref.sca(ax=1) + fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot1") + with fig_test.subplot(nrows=1, ncols=2, figsize=("6c", "3c"), frame="WSne") as axs: + fig_test.sca(ax=axs[0, 0]) + fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot0") + fig_test.sca(ax=axs[0, 1]) + fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot1") + return fig_ref, fig_test + + +@check_figures_equal() +def test_subplot_direct(): + """ + Plot map elements to subplot directly using ax argument. + """ + fig_ref, fig_test = Figure(), Figure() + with fig_ref.subplot(nrows=2, ncols=1, Fs="3c/3c"): + fig_ref.basemap(region=[0, 3, 0, 3], frame=True, ax=0) + fig_ref.basemap(region=[0, 3, 0, 3], frame=True, ax=1) + with fig_test.subplot(nrows=2, ncols=1, subsize=("3c", "3c")) as axs: + fig_test.basemap(region=[0, 3, 0, 3], frame=True, ax=axs[0, 0]) + fig_test.basemap(region=[0, 3, 0, 3], frame=True, ax=axs[1, 0]) + return fig_ref, fig_test + + +@check_figures_equal() +def test_subplot_autolabel_margins_title(): + """ + Make subplot figure with autolabels, setting some margins and a title. + """ + fig_ref, fig_test = Figure(), Figure() + kwargs = dict(nrows=2, ncols=1, figsize=("15c", "6c")) + + with fig_ref.subplot(A="(1)", M="0.3c/0.1c", T='"Subplot Title"', **kwargs): + fig_ref.basemap(region=[0, 1, 2, 3], frame="WSne", c="0,0") + fig_ref.basemap(region=[4, 5, 6, 7], frame="WSne", c="1,0") + + with fig_test.subplot( + autolabel="(1)", margins=["0.3c", "0.1c"], title='"Subplot Title"', **kwargs + ) as axs_test: + fig_test.basemap(region=[0, 1, 2, 3], frame="WSne", ax=axs_test[0, 0]) + fig_test.basemap(region=[4, 5, 6, 7], frame="WSne", ax=axs_test[1, 0]) + + return fig_ref, fig_test + + +@check_figures_equal() +def test_subplot_clearance_and_shared_xy_axis_layout(): + """ + Ensure subplot clearance works, and that the layout can be set to use + shared X and Y axis labels across columns and rows. + """ + fig_ref, fig_test = Figure(), Figure() + kwargs = dict(nrows=2, ncols=2, frame="WSrt", figsize=("5c", "5c")) + + with fig_ref.subplot(C="y0.2", SR="l", SC="t", **kwargs): + fig_ref.basemap(region=[0, 4, 0, 4], projection="X?", ax=True) + fig_ref.basemap(region=[0, 8, 0, 4], projection="X?", ax=True) + fig_ref.basemap(region=[0, 4, 0, 8], projection="X?", ax=True) + fig_ref.basemap(region=[0, 8, 0, 8], projection="X?", ax=True) + + with fig_test.subplot(clearance="y0.2", layout=["Rl", "Ct"], **kwargs): + fig_test.basemap(region=[0, 4, 0, 4], projection="X?", ax=True) + fig_test.basemap(region=[0, 8, 0, 4], projection="X?", ax=True) + fig_test.basemap(region=[0, 4, 0, 8], projection="X?", ax=True) + fig_test.basemap(region=[0, 8, 0, 8], projection="X?", ax=True) + + return fig_ref, fig_test From 683ff57ab471a072f1e1a69f7f748696d7145b1c Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 30 Jan 2021 20:30:01 +1300 Subject: [PATCH 02/30] Add fig.subplot and fig.sca to API docs Also updated tutorial a bit --- doc/api/index.rst | 2 ++ examples/tutorials/subplots.py | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/api/index.rst b/doc/api/index.rst index 0598fd103f8..be87e91f6ee 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -34,7 +34,9 @@ Plotting data and laying out the map: Figure.legend Figure.logo Figure.image + Figure.sca Figure.shift_origin + Figure.subplot Figure.text Figure.meca diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 857967a34b5..989d409eaf5 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -62,11 +62,11 @@ fig.show() ############################################################################### -# The ``fig.sca`` command activates a specified subplot, and all subsequent -# plotting commands will take place in that subplot. 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 ``ax`` argument. This can -# be found in the ``axs`` variable referenced by the ``row`` and ``col`` +# The :meth:`pygmt.Figure.sca` command activates a specified subplot, and all +# subsequent plotting commands will take place in that subplot. 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 ``ax`` argument. This +# can be found in the ``axs`` variable referenced by the ``row`` and ``col`` # number. ############################################################################### @@ -235,4 +235,4 @@ ############################################################################### # 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.sca(A=""(a)"`` to manually set the subplot label. +# ``fig.sca(autolabel=""(a)"`` to manually set the subplot label. From 95e3a6414a110e6bf4c50d1ad8a94578730474a5 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 3 Feb 2021 16:57:19 +1300 Subject: [PATCH 03/30] Alias fixedlabel (A), clearance (C), verbose (V) for sca --- examples/tutorials/subplots.py | 2 +- pygmt/src/subplot.py | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 989d409eaf5..2629ff3adbc 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -235,4 +235,4 @@ ############################################################################### # 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.sca(autolabel=""(a)"`` to manually set the subplot label. +# ``fig.sca(fixedlabel=""(a)"`` to manually set the subplot label. diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index a2c55d698df..333e9383006 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -146,9 +146,9 @@ def subplot(self, nrows=1, ncols=1, **kwargs): @fmt_docstring -@use_alias(F="dimensions") +@use_alias(A="fixedlabel", C="clearance", V="verbose") def sca(self, ax=None, **kwargs): - """ + r""" Set the current Axes instance to *ax*. Before you start plotting you must first select the active subplot. Note: @@ -160,6 +160,36 @@ def sca(self, ax=None, **kwargs): using unequal scales]. {aliases} + + Parameters + ---------- + ax : str + *row,col*\|\ *index*. + Sets the current subplot until further notice. **Note**: First *row* + or *col* is 0, not 1. If not given we go to the next subplot by order + specified via **autolabel**. As an alternative, you may bypass the + **sca** mode and instead supply the common option **ax**\ [*row,col*] + to the first plot command you issue in that subplot. GMT maintains + information about the current figure and subplot. Also, you may give + the one-dimensional *index* instead which starts at 0 and follows the + row or column order set via **autolabel**. + + fixedlabel : str + Overrides the automatic labeling with the given string. No modifiers + are allowed. Placement, justification, etc. are all inherited from how + **autolabel** was specified by the initial **subplot** command. + + clearance : str + [*side*]\ *clearance*. + Reserve a space of dimension *clearance* between the margin and the + subplot on the specified side, using *side* values from **w**, **e**, + **s**, or **n**. The option is repeatable to set aside space on more + than one side. Such space will be left untouched by the main map + plotting but can be accessed by modules that plot scales, bars, text, + etc. This setting overrides the common clearances set by **clearance** + in the initial **subplot** call. + + {V} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access From 8283f65a4877661f78922ac0360347cceabe868c Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 3 Feb 2021 17:46:12 +1300 Subject: [PATCH 04/30] Turn fig.sca into a context manager --- examples/tutorials/subplots.py | 17 +++++++++-------- pygmt/src/subplot.py | 4 +++- pygmt/tests/test_subplot.py | 24 ++++++++++++------------ 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 2629ff3adbc..2f392e6a37e 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -53,12 +53,12 @@ for index in axs.flatten(): i = index // axs.shape[1] # row j = index % axs.shape[1] # column - fig.sca(ax=axs[i, j]) # sets the current Axes - fig.text( - position="MC", - text=f"index: {index}, row: {i}, col: {j}", - region=[0, 1, 0, 1], - ) + with fig.sca(ax=axs[i, j]): # sets the current Axes + fig.text( + position="MC", + text=f"index: {index}, row: {i}, col: {j}", + region=[0, 1, 0, 1], + ) fig.show() ############################################################################### @@ -84,7 +84,8 @@ ############################################################################### # .. code-block:: default # -# fig.sca(ax=axs[0, 2]) +# with fig.sca(ax=axs[0, 2]): +# ... ############################################################################### # Making your first subplot @@ -235,4 +236,4 @@ ############################################################################### # 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.sca(fixedlabel=""(a)"`` to manually set the subplot label. +# ``fig.sca(fixedlabel="(a)"`` to manually set the subplot label. diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 333e9383006..d5d406fd103 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -146,6 +146,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): @fmt_docstring +@contextlib.contextmanager @use_alias(A="fixedlabel", C="clearance", V="verbose") def sca(self, ax=None, **kwargs): r""" @@ -193,6 +194,7 @@ def sca(self, ax=None, **kwargs): """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access - arg_str = " ".join(["set", f"{ax}", build_arg_string(kwargs)]) with Session() as lib: + arg_str = " ".join(["set", f"{ax}", build_arg_string(kwargs)]) lib.call_module(module="subplot", args=arg_str) + yield diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index 3e648ff5b18..e043511bfe3 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -13,15 +13,15 @@ def test_subplot_basic_frame(): """ fig_ref, fig_test = Figure(), Figure() with fig_ref.subplot(nrows=1, ncols=2, Ff="6c/3c", B="WSne"): - fig_ref.sca(ax=0) - fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot0") - fig_ref.sca(ax=1) - fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot1") + with fig_ref.sca(ax=0): + fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot0") + with fig_ref.sca(ax=1): + fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot1") with fig_test.subplot(nrows=1, ncols=2, figsize=("6c", "3c"), frame="WSne") as axs: - fig_test.sca(ax=axs[0, 0]) - fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot0") - fig_test.sca(ax=axs[0, 1]) - fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot1") + with fig_test.sca(ax=axs[0, 0]): + fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot0") + with fig_test.sca(ax=axs[0, 1]): + fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot1") return fig_ref, fig_test @@ -32,11 +32,11 @@ def test_subplot_direct(): """ fig_ref, fig_test = Figure(), Figure() with fig_ref.subplot(nrows=2, ncols=1, Fs="3c/3c"): - fig_ref.basemap(region=[0, 3, 0, 3], frame=True, ax=0) - fig_ref.basemap(region=[0, 3, 0, 3], frame=True, ax=1) + fig_ref.basemap(region=[0, 3, 0, 3], frame="af", ax=0) + fig_ref.basemap(region=[0, 3, 0, 3], frame="af", ax=1) with fig_test.subplot(nrows=2, ncols=1, subsize=("3c", "3c")) as axs: - fig_test.basemap(region=[0, 3, 0, 3], frame=True, ax=axs[0, 0]) - fig_test.basemap(region=[0, 3, 0, 3], frame=True, ax=axs[1, 0]) + fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=axs[0, 0]) + fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=axs[1, 0]) return fig_ref, fig_test From 92b7fbd246f1462b330a2e3f0f6eef6ebb4a33e2 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Fri, 5 Feb 2021 16:28:14 +1300 Subject: [PATCH 05/30] Alias projection (J), region (R), verbose (V), x/yshift (X/Y) for subplot --- pygmt/src/subplot.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index d5d406fd103..1026cfdf471 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -16,9 +16,14 @@ A="autolabel", B="frame", C="clearance", + J="projecton", M="margins", + R="region", S="layout", T="title", + V="verbose", + X="xshift", + Y="yshift", ) @kwargs_to_strings(Ff="sequence", Fs="sequence", M="sequence") def subplot(self, nrows=1, ncols=1, **kwargs): @@ -68,6 +73,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): your tag numbers using lowercase Roman numerals; use **+R** for uppercase Roman numerals [Arabic numerals]. Append **+v** to increase tag numbers vertically down columns [horizontally across rows]. + {B} clearance : str ``[side]clearance``. Reserve a space of dimension *clearance* between the margin and the @@ -81,6 +87,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): apply to the selected (active) subplot. **Note**: Common options **x_offset** and **y_offset* are not available during subplots; use **clearance** instead. + {J} margins : tuple This is margin space that is added between neighboring subplots (i.e., the interior margins) in addition to the automatic space added for tick @@ -96,6 +103,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): opposing sides (e.g., east plus west or south plus north margins) [Default is half the primary annotation font size, giving the full annotation font size as the default gap]. + {R} layout : str or list Set subplot layout for shared axes. May be set separately for rows (**R**) and columns (**C**). E.g. ``layout=['Rl', 'Cb']``. @@ -121,6 +129,8 @@ def subplot(self, nrows=1, ncols=1, **kwargs): title : str Overarching heading for the entire figure. Font is determined by setting ``FONT_HEADING``. + {V} + {XY} Yields ------ From 9d6f20bb5c7f2b58d78a48a91aab55ee71e509d3 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 6 Feb 2021 07:21:58 +1300 Subject: [PATCH 06/30] Fix subplot end -V --- pygmt/src/subplot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 1026cfdf471..ffff266fe49 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -151,7 +151,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): axs[i, j] = index yield axs finally: - v_arg = build_arg_string(kwargs.fromkeys("V")) + v_arg = build_arg_string({"V": kwargs.get("V")}) lib.call_module("subplot", f"end {v_arg}".strip()) From 3f0c00827ccc953dca9359b530fa75b55f6cc10e Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 6 Feb 2021 08:56:09 +1300 Subject: [PATCH 07/30] Improve docstring of fig.subplot and fig.sca --- pygmt/src/subplot.py | 121 ++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 58 deletions(-) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index ffff266fe49..1b60bdb6217 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -27,14 +27,14 @@ ) @kwargs_to_strings(Ff="sequence", Fs="sequence", M="sequence") def subplot(self, nrows=1, ncols=1, **kwargs): - """ + r""" + Create multi-panel subplot figures. + The **subplot** module is used to split the current figure into a rectangular layout of subplots that each may contain a single self- - contained figure. - - Begin by defining the layout of the entire multi-panel illustration. - Several options are available to specify the systematic layout, labeling, - dimensions, and more for the subplots. + contained figure. Begin by defining the layout of the entire multi-panel + illustration. Several options are available to specify the systematic + layout, labeling, dimensions, and more for the subplots. Full option list at :gmt-docs:`subplot.html#synopsis-begin-mode` @@ -52,30 +52,31 @@ def subplot(self, nrows=1, ncols=1, **kwargs): Specify the dimensions of each subplot directly as ``(width, height)``. autolabel : bool or str - ``[autolabel][+cdx[/dy]][+gfill][+j|Jrefpoint][+odx[/dy]][+ppen][+r|R] - [+v]``. + [*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 [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. Use - **+j|J**\\ *refpoint* to specify where the tag should be placed in the - subplot [TL]. 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** [3p/3p, i.e., 15% - of the FONT_TAG size dimension]. Append **+g**\\ *fill* to paint the - tag's text box with *fill* [no painting]. Append - **+o**\\ *dx*\\ [/*dy*] to offset the tag's reference point in the - direction implied by the justification [4p/4p, i.e., 20% of the - FONT_TAG size]. Append **+p**\\ *pen* to draw the outline of the tag's - text box using selected *pen* [no outline]. Append **+r** to typeset - your tag numbers using lowercase Roman numerals; use **+R** for - uppercase Roman numerals [Arabic numerals]. Append **+v** to increase - tag numbers vertically down columns [horizontally across rows]. + any side if these should be typeset as part of the tag. Use **+j|J**\ + *refpoint* to specify where the tag should be placed in the subplot + [TL]. 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** [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* [no painting]. Append **+o**\ *dx*\ [/*dy*] to offset the tag's + reference point in the direction implied by the justification [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 selected *pen* [no + outline]. Append **+r** to typeset your tag numbers using lowercase + Roman numerals; use **+R** for uppercase Roman numerals [Arabic + numerals]. Append **+v** to increase tag numbers vertically down + columns [horizontally across rows]. {B} clearance : str - ``[side]clearance``. + [*side*]\ *clearance*. Reserve a space of dimension *clearance* between the margin and the subplot on the specified side, using *side* values from **w**, **e**, **s**, or **n**, or **x** for both **w** and **e** or **y** for both @@ -106,29 +107,31 @@ def subplot(self, nrows=1, ncols=1, **kwargs): {R} layout : str or list Set subplot layout for shared axes. May be set separately for rows - (**R**) and columns (**C**). E.g. ``layout=['Rl', 'Cb']``. - Considerations for **C**: Use when all subplots in a **C**\\ olumn - share a common *x*-range. The first (i.e., **t**\\ op) and the last - (i.e., **b**\\ ottom) rows will have *x* annotations; append **t** or - **b** to select only one of those two rows [both]. Append **+l** if - annotated *x*-axes should have a label [none]; optionally append the - label if it is the same for the entire subplot. Append **+t** to make - space for subplot titles for each row; use **+tc** for top row titles - only [no subplot titles]. Labels and titles that depends on which row - or column are specified as usual via a subplot's own **frame** setting. - Considerations for **R**: Use when all subplots in a **R**\\ ow share a - common *y*-range. The first (i.e., **l**\\ eft) and the last (i.e., - **r**\\ ight) columns will have *y*-annotations; append **l** or **r** - to select only one of those two columns [both]. Append **+l** if - annotated *y*-axes will have a label [none]; optionally, append the - label if it is the same for the entire subplot. Append **+p** to make - all annotations axis-parallel [horizontal]; if not used you may have to - set **clearance** to secure extra space for long horizontal - annotations. Append **+w** to draw horizontal and vertical lines - between interior panels using selected pen [no lines]. + (**R**) and columns (**C**). E.g. ``layout=['Rl', 'Cb']`` will set + shared axis labels for rows on the **l**eft, and for columns on the + **b**ottom. Considerations for **C**: Use when all subplots in a + **C**\ olumn share a common *x*-range. The first (i.e., **t**\ op) and + the last (i.e., **b**\ ottom) rows will have *x* annotations; append + **t** or **b** to select only one of those two rows [both]. Append + **+l** if annotated *x*-axes should have a label [none]; optionally + append the label if it is the same for the entire subplot. Append + **+t** to make space for subplot titles for each row; use **+tc** for + top row titles only [no subplot titles]. Labels and titles that depends + on which row or column are specified as usual via a subplot's own + **frame** setting. Considerations for **R**: Use when all subplots in a + **R**\ ow share a common *y*-range. The first (i.e., **l**\ eft) and + the last (i.e., **r**\ ight) columns will have *y*-annotations; append + **l** or **r** to select only one of those two columns [both]. Append + **+l** if annotated *y*-axes will have a label [none]; optionally, + append the label if it is the same for the entire subplot. Append + **+p** to make all annotations axis-parallel [horizontal]; if not used + you may have to set **clearance** to secure extra space for long + horizontal annotations. Append **+w** to draw horizontal and vertical + lines between interior panels using selected pen [no lines]. title : str - Overarching heading for the entire figure. Font is determined by - setting ``FONT_HEADING``. + While individual subplots can have titles (see **layout** or + **frame**), the entire figure may also have an overarching *heading* + [no heading]. Font is determined by setting :gmt-term:`FONT_HEADING`. {V} {XY} @@ -176,29 +179,31 @@ def sca(self, ax=None, **kwargs): ---------- ax : str *row,col*\|\ *index*. - Sets the current subplot until further notice. **Note**: First *row* + Sets the current subplot until further notice. **Note**: First *row* or *col* is 0, not 1. If not given we go to the next subplot by order - specified via **autolabel**. As an alternative, you may bypass the - **sca** mode and instead supply the common option **ax**\ [*row,col*] - to the first plot command you issue in that subplot. GMT maintains - information about the current figure and subplot. Also, you may give - the one-dimensional *index* instead which starts at 0 and follows the - row or column order set via **autolabel**. + specified via **autolabel** in :meth:`pygmt.Figure.subplot`. As an + alternative, you may bypass the **sca** mode and instead supply the + common option **ax**=\ [*row,col*] to the first plot command you issue + in that subplot. GMT maintains information about the current figure and + subplot. Also, you may give the one-dimensional *index* instead which + starts at 0 and follows the row or column order set via **autolabel** + in :meth:`pygmt.Figure.subplot`. fixedlabel : str Overrides the automatic labeling with the given string. No modifiers are allowed. Placement, justification, etc. are all inherited from how - **autolabel** was specified by the initial **subplot** command. + **autolabel** was specified by the initial :meth:`pygmt.Figure.subplot` + command. clearance : str [*side*]\ *clearance*. Reserve a space of dimension *clearance* between the margin and the subplot on the specified side, using *side* values from **w**, **e**, - **s**, or **n**. The option is repeatable to set aside space on more - than one side. Such space will be left untouched by the main map + **s**, or **n**. The option is repeatable to set aside space on more + than one side. Such space will be left untouched by the main map plotting but can be accessed by modules that plot scales, bars, text, etc. This setting overrides the common clearances set by **clearance** - in the initial **subplot** call. + in the initial :meth:`pygmt.Figure.subplot` call. {V} """ From 967fba3c12cc93118443af5aafa5b4df7fa0d40c Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 6 Feb 2021 09:24:05 +1300 Subject: [PATCH 08/30] Small tweaks to subplot tutorial --- doc/index.rst | 2 +- examples/tutorials/subplots.py | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index fb02442f63c..7dcc11b8953 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -33,13 +33,13 @@ tutorials/coastlines.rst tutorials/regions.rst tutorials/plot.rst - tutorials/subplots.rst tutorials/lines.rst tutorials/text.rst tutorials/contour-map.rst tutorials/earth-relief.rst tutorials/3d-perspective-image.rst tutorials/inset.rst + tutorials/subplots.rst tutorials/configuration.rst .. toctree:: diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 2f392e6a37e..d3f34d67960 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -1,6 +1,6 @@ """ -Subplots -======== +Making subplot panels +===================== 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 @@ -17,12 +17,14 @@ use of :meth:`pygmt.Figure.subplot` which offers finer grained control, and this is what the tutorial below will cover. """ - -############################################################################### -# Let's start by importing the PyGMT library and initializing a Figure +# sphinx_gallery_thumbnail_number = 3 import pygmt +############################################################################### +# +# Let's start by initializing a :class:`pygmt.Figure` instance. + fig = pygmt.Figure() ############################################################################### @@ -222,10 +224,10 @@ # # 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, +# 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. +# 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 @@ -236,4 +238,4 @@ ############################################################################### # 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.sca(fixedlabel="(a)"`` to manually set the subplot label. +# ``fig.sca(fixedlabel="(a)")`` to manually set the subplot label. From 14ae58c69ed249b0426743a849a0560240f6e0c6 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 6 Feb 2021 17:56:55 +1300 Subject: [PATCH 09/30] Allow list or tuple inputs to ax (c) argument in basemap --- pygmt/base_plotting.py | 2 +- pygmt/src/subplot.py | 2 +- pygmt/tests/test_subplot.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pygmt/base_plotting.py b/pygmt/base_plotting.py index 06e0ddf2fe6..bb1da94b2d5 100644 --- a/pygmt/base_plotting.py +++ b/pygmt/base_plotting.py @@ -1226,7 +1226,7 @@ def contour(self, x=None, y=None, z=None, data=None, **kwargs): p="perspective", t="transparency", ) - @kwargs_to_strings(R="sequence", p="sequence") + @kwargs_to_strings(R="sequence", c="sequence", p="sequence") def basemap(self, **kwargs): """ Plot base maps and frames for the figure. diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 1b60bdb6217..f0996bc79e7 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -68,7 +68,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): dimension]. Append **+g**\ *fill* to paint the tag's text box with *fill* [no painting]. Append **+o**\ *dx*\ [/*dy*] to offset the tag's reference point in the direction implied by the justification [4p/4p, - i.e., 20% of the :gmt-term:`FONT_TAG` size]. Append **+p**\\ *pen* to + i.e., 20% of the :gmt-term:`FONT_TAG` size]. Append **+p**\ *pen* to draw the outline of the tag's text box using selected *pen* [no outline]. Append **+r** to typeset your tag numbers using lowercase Roman numerals; use **+R** for uppercase Roman numerals [Arabic diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index e043511bfe3..374b018d7fe 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -35,8 +35,8 @@ def test_subplot_direct(): fig_ref.basemap(region=[0, 3, 0, 3], frame="af", ax=0) fig_ref.basemap(region=[0, 3, 0, 3], frame="af", ax=1) with fig_test.subplot(nrows=2, ncols=1, subsize=("3c", "3c")) as axs: - fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=axs[0, 0]) - fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=axs[1, 0]) + fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=[0, 0]) + fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=[1, 0]) return fig_ref, fig_test From ddee738548f5b53bdc8832c29938b49e63f79602 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Sat, 6 Feb 2021 18:03:05 +1300 Subject: [PATCH 10/30] Use sequence_comma instead of sequence Co-authored-by: Dongdong Tian --- pygmt/base_plotting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/base_plotting.py b/pygmt/base_plotting.py index bb1da94b2d5..6279592a5fc 100644 --- a/pygmt/base_plotting.py +++ b/pygmt/base_plotting.py @@ -1226,7 +1226,7 @@ def contour(self, x=None, y=None, z=None, data=None, **kwargs): p="perspective", t="transparency", ) - @kwargs_to_strings(R="sequence", c="sequence", p="sequence") + @kwargs_to_strings(R="sequence", c="sequence_comma", p="sequence") def basemap(self, **kwargs): """ Plot base maps and frames for the figure. From dbd4677409ff5a7ba37268e6862c22cb46c97493 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 6 Feb 2021 18:32:37 +1300 Subject: [PATCH 11/30] Remove use of matplotlib-like Axes (axs) object --- examples/tutorials/subplots.py | 59 +++++++++++++--------------------- pygmt/src/subplot.py | 14 +------- pygmt/tests/test_subplot.py | 14 ++++---- 3 files changed, 30 insertions(+), 57 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index d3f34d67960..621c370294d 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -39,9 +39,7 @@ ############################################################################### # .. code-block:: default # -# with fig.subplot( -# nrows=2, ncols=3, figsize=("15c", "6c"), frame="lrtb" -# ) as axs: +# with fig.subplot(nrows=2, ncols=3, figsize=("15c", "6c"), frame="lrtb"): # ... ############################################################################### @@ -51,11 +49,11 @@ # 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") as axs: - for index in axs.flatten(): - i = index // axs.shape[1] # row - j = index % axs.shape[1] # column - with fig.sca(ax=axs[i, j]): # sets the current Axes +with fig.subplot(nrows=2, ncols=3, figsize=("15c", "6c"), frame="lrtb"): + for index in range(2 * 3): + i = index // 3 # row + j = index % 3 # column + with fig.sca(ax=index): # sets the current Axes fig.text( position="MC", text=f"index: {index}, row: {i}, col: {j}", @@ -67,9 +65,8 @@ # The :meth:`pygmt.Figure.sca` command activates a specified subplot, and all # subsequent plotting commands will take place in that subplot. 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 ``ax`` argument. This -# can be found in the ``axs`` variable referenced by the ``row`` and ``col`` -# number. +# need to provide the identifier for that subplot via the ``ax`` argument. Pass +# in either the ``index`` number, or a tuple like (``row``, ``col``) to ``ax``. ############################################################################### # .. note:: @@ -86,7 +83,7 @@ ############################################################################### # .. code-block:: default # -# with fig.sca(ax=axs[0, 2]): +# with fig.sca(ax="0,2"): # ... ############################################################################### @@ -103,19 +100,11 @@ autolabel=True, margins=["0.1c", "0.2c"], title='"My Subplot Heading"', -) as axs: - fig.basemap( - region=[0, 10, 0, 10], projection="X?", frame=["af", "WSne"], ax=axs[0, 0] - ) - fig.basemap( - region=[0, 20, 0, 10], projection="X?", frame=["af", "WSne"], ax=axs[0, 1] - ) - fig.basemap( - region=[0, 10, 0, 20], projection="X?", frame=["af", "WSne"], ax=axs[1, 0] - ) - fig.basemap( - region=[0, 20, 0, 20], projection="X?", frame=["af", "WSne"], ax=axs[1, 1] - ) +): + fig.basemap(region=[0, 10, 0, 10], projection="X?", frame=["af", "WSne"], ax=[0, 0]) + fig.basemap(region=[0, 20, 0, 10], projection="X?", frame=["af", "WSne"], ax=[0, 1]) + fig.basemap(region=[0, 10, 0, 20], projection="X?", frame=["af", "WSne"], ax=[1, 0]) + fig.basemap(region=[0, 20, 0, 20], projection="X?", frame=["af", "WSne"], ax=[1, 1]) fig.show() ############################################################################### @@ -142,10 +131,10 @@ # In the above example, we used the following commands to activate the # four subplots explicitly one after another:: # -# fig.basemap(..., ax=axs[0, 0]) -# fig.basemap(..., ax=axs[0, 1]) -# fig.basemap(..., ax=axs[1, 0]) -# fig.basemap(..., ax=axs[1, 1]) +# fig.basemap(..., ax=[0, 0]) +# fig.basemap(..., ax=[0, 1]) +# fig.basemap(..., ax=[1, 0]) +# fig.basemap(..., ax=[1, 1]) # # In fact, we can just use ``fig.basemap(..., ax=True)`` without specifying # any subplot index number, and GMT will automatically activate the next @@ -169,7 +158,7 @@ title='"My Subplot Heading"', layout=["Rl", "Cb"], frame="WSrt", -) as axs: +): fig.basemap(region=[0, 10, 0, 10], projection="X?", ax=True) fig.basemap(region=[0, 20, 0, 10], projection="X?", ax=True) fig.basemap(region=[0, 10, 0, 20], projection="X?", ax=True) @@ -198,15 +187,11 @@ 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"], ax=axs[0, 0] + region=[0, 10, 0, 10], projection="X15c/3c", frame=["af", "WSne"], ax=[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"], ax=axs[1, 0] - ) - fig.basemap( - region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], ax=axs[1, 1] - ) + fig.basemap(region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], ax=[1, 0]) + fig.basemap(region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], ax=[1, 1]) fig.show() ############################################################################### diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index f0996bc79e7..a90650827e1 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -3,7 +3,6 @@ """ import contextlib -import numpy as np from pygmt.clib import Session from pygmt.helpers import build_arg_string, fmt_docstring, kwargs_to_strings, use_alias @@ -134,11 +133,6 @@ def subplot(self, nrows=1, ncols=1, **kwargs): [no heading]. Font is determined by setting :gmt-term:`FONT_HEADING`. {V} {XY} - - Yields - ------ - axs : numpy.ndarray - Array of Axes objects. """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access @@ -146,13 +140,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): try: arg_str = " ".join(["begin", f"{nrows}x{ncols}", build_arg_string(kwargs)]) lib.call_module(module="subplot", args=arg_str) - # Setup matplotlib-like Axes - axs = np.empty(shape=(nrows, ncols), dtype=object) - for index in range(nrows * ncols): - i = index // ncols # row - j = index % ncols # column - axs[i, j] = index - yield axs + yield finally: v_arg = build_arg_string({"V": kwargs.get("V")}) lib.call_module("subplot", f"end {v_arg}".strip()) diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index 374b018d7fe..b08b699554d 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -17,10 +17,10 @@ def test_subplot_basic_frame(): fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot0") with fig_ref.sca(ax=1): fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot1") - with fig_test.subplot(nrows=1, ncols=2, figsize=("6c", "3c"), frame="WSne") as axs: - with fig_test.sca(ax=axs[0, 0]): + with fig_test.subplot(nrows=1, ncols=2, figsize=("6c", "3c"), frame="WSne"): + with fig_test.sca(ax=[0, 0]): fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot0") - with fig_test.sca(ax=axs[0, 1]): + with fig_test.sca(ax=[0, 1]): fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot1") return fig_ref, fig_test @@ -34,7 +34,7 @@ def test_subplot_direct(): with fig_ref.subplot(nrows=2, ncols=1, Fs="3c/3c"): fig_ref.basemap(region=[0, 3, 0, 3], frame="af", ax=0) fig_ref.basemap(region=[0, 3, 0, 3], frame="af", ax=1) - with fig_test.subplot(nrows=2, ncols=1, subsize=("3c", "3c")) as axs: + with fig_test.subplot(nrows=2, ncols=1, subsize=("3c", "3c")): fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=[0, 0]) fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=[1, 0]) return fig_ref, fig_test @@ -54,9 +54,9 @@ def test_subplot_autolabel_margins_title(): with fig_test.subplot( autolabel="(1)", margins=["0.3c", "0.1c"], title='"Subplot Title"', **kwargs - ) as axs_test: - fig_test.basemap(region=[0, 1, 2, 3], frame="WSne", ax=axs_test[0, 0]) - fig_test.basemap(region=[4, 5, 6, 7], frame="WSne", ax=axs_test[1, 0]) + ): + fig_test.basemap(region=[0, 1, 2, 3], frame="WSne", ax=[0, 0]) + fig_test.basemap(region=[4, 5, 6, 7], frame="WSne", ax=[1, 0]) return fig_ref, fig_test From 4126c16160c67f8d2c405e7d5da56145adc989d4 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 6 Feb 2021 23:55:57 +1300 Subject: [PATCH 12/30] Allow for spaces in title and labels without needing double quotes Mitigates against #247. --- examples/tutorials/subplots.py | 4 ++-- pygmt/src/subplot.py | 5 ++++- pygmt/tests/test_subplot.py | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 621c370294d..8e8d549a357 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -99,7 +99,7 @@ figsize=("15c", "6c"), autolabel=True, margins=["0.1c", "0.2c"], - title='"My Subplot Heading"', + title="My Subplot Heading", ): fig.basemap(region=[0, 10, 0, 10], projection="X?", frame=["af", "WSne"], ax=[0, 0]) fig.basemap(region=[0, 20, 0, 10], projection="X?", frame=["af", "WSne"], ax=[0, 1]) @@ -155,7 +155,7 @@ figsize=("15c", "6c"), autolabel=True, margins=["0.3c", "0.2c"], - title='"My Subplot Heading"', + title="My Subplot Heading", layout=["Rl", "Cb"], frame="WSrt", ): diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index a90650827e1..928c6542e55 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -135,6 +135,8 @@ def subplot(self, nrows=1, ncols=1, **kwargs): {XY} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access + kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") else None + kwargs["T"] = f'"{kwargs.get("T")}"' if kwargs.get("T") else None with Session() as lib: try: @@ -196,8 +198,9 @@ def sca(self, ax=None, **kwargs): {V} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access + kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") else None with Session() as lib: - arg_str = " ".join(["set", f"{ax}", build_arg_string(kwargs)]) + arg_str = " ".join(["set", f"{ax}", build_arg_string(kwargs)]).strip() lib.call_module(module="subplot", args=arg_str) yield diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index b08b699554d..51658c03d20 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -18,9 +18,9 @@ def test_subplot_basic_frame(): with fig_ref.sca(ax=1): fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot1") with fig_test.subplot(nrows=1, ncols=2, figsize=("6c", "3c"), frame="WSne"): - with fig_test.sca(ax=[0, 0]): + with fig_test.sca(ax="0,0"): fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot0") - with fig_test.sca(ax=[0, 1]): + with fig_test.sca(ax="0,1"): fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot1") return fig_ref, fig_test @@ -48,12 +48,12 @@ def test_subplot_autolabel_margins_title(): fig_ref, fig_test = Figure(), Figure() kwargs = dict(nrows=2, ncols=1, figsize=("15c", "6c")) - with fig_ref.subplot(A="(1)", M="0.3c/0.1c", T='"Subplot Title"', **kwargs): + with fig_ref.subplot(A="(1)", M="0.3c/0.1c", T="Subplot Title", **kwargs): fig_ref.basemap(region=[0, 1, 2, 3], frame="WSne", c="0,0") fig_ref.basemap(region=[4, 5, 6, 7], frame="WSne", c="1,0") with fig_test.subplot( - autolabel="(1)", margins=["0.3c", "0.1c"], title='"Subplot Title"', **kwargs + autolabel="(1)", margins=["0.3c", "0.1c"], title="Subplot Title", **kwargs ): fig_test.basemap(region=[0, 1, 2, 3], frame="WSne", ax=[0, 0]) fig_test.basemap(region=[4, 5, 6, 7], frame="WSne", ax=[1, 0]) From 0521851491debff469adb8332d01822104a1a098 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sun, 7 Feb 2021 11:43:33 +1300 Subject: [PATCH 13/30] Ensure that a list can be passed into region (R) --- pygmt/src/subplot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 928c6542e55..a02481d0dd0 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -24,7 +24,7 @@ X="xshift", Y="yshift", ) -@kwargs_to_strings(Ff="sequence", Fs="sequence", M="sequence") +@kwargs_to_strings(Ff="sequence", Fs="sequence", M="sequence", R="sequence") def subplot(self, nrows=1, ncols=1, **kwargs): r""" Create multi-panel subplot figures. @@ -88,7 +88,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): **x_offset** and **y_offset* are not available during subplots; use **clearance** instead. {J} - margins : tuple + margins : list This is margin space that is added between neighboring subplots (i.e., the interior margins) in addition to the automatic space added for tick marks, annotations, and labels. The margins can be specified as either: From 6eb58eada1e7ff6ac2aa0405470ee370523f81b1 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sun, 7 Feb 2021 12:04:41 +1300 Subject: [PATCH 14/30] Allow for list inputs into fig.sca(ax=...) --- examples/tutorials/subplots.py | 4 ++-- pygmt/src/subplot.py | 12 ++++++++++-- pygmt/tests/test_subplot.py | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 8e8d549a357..1eee2fc8d2e 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -83,7 +83,7 @@ ############################################################################### # .. code-block:: default # -# with fig.sca(ax="0,2"): +# with fig.sca(ax=[0, 2]): # ... ############################################################################### @@ -223,4 +223,4 @@ ############################################################################### # 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.sca(fixedlabel="(a)")`` to manually set the subplot label. +# ``fig.sca(..., fixedlabel="(a)")`` to manually set the subplot label. diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index a02481d0dd0..097f6214bf9 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -4,7 +4,13 @@ import contextlib from pygmt.clib import Session -from pygmt.helpers import build_arg_string, fmt_docstring, kwargs_to_strings, use_alias +from pygmt.helpers import ( + build_arg_string, + fmt_docstring, + is_nonstr_iter, + kwargs_to_strings, + use_alias, +) @fmt_docstring @@ -167,7 +173,7 @@ def sca(self, ax=None, **kwargs): Parameters ---------- - ax : str + ax : str or list *row,col*\|\ *index*. Sets the current subplot until further notice. **Note**: First *row* or *col* is 0, not 1. If not given we go to the next subplot by order @@ -199,6 +205,8 @@ def sca(self, ax=None, **kwargs): """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") else None + # convert tuple or list to comma-separated str + ax = ",".join(map(str, ax)) if is_nonstr_iter(ax) else ax with Session() as lib: arg_str = " ".join(["set", f"{ax}", build_arg_string(kwargs)]).strip() diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index 51658c03d20..423b701e662 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -20,7 +20,7 @@ def test_subplot_basic_frame(): with fig_test.subplot(nrows=1, ncols=2, figsize=("6c", "3c"), frame="WSne"): with fig_test.sca(ax="0,0"): fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot0") - with fig_test.sca(ax="0,1"): + with fig_test.sca(ax=[0, 1]): fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot1") return fig_ref, fig_test From b03a5dd294a1fce7d4d490b940ca1203378a5bb8 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Thu, 11 Feb 2021 09:25:39 +1300 Subject: [PATCH 15/30] Rename sca to set_panel and ax to panel --- .pylintrc | 1 - doc/api/index.rst | 2 +- examples/tutorials/subplots.py | 65 ++++++++++++++++++++-------------- pygmt/figure.py | 2 +- pygmt/src/__init__.py | 2 +- pygmt/src/basemap.py | 4 +-- pygmt/src/subplot.py | 20 +++++------ pygmt/tests/test_subplot.py | 36 +++++++++---------- 8 files changed, 72 insertions(+), 60 deletions(-) diff --git a/.pylintrc b/.pylintrc index e42a50bf488..766bdd46b8b 100644 --- a/.pylintrc +++ b/.pylintrc @@ -441,7 +441,6 @@ function-naming-style=snake_case good-names=i, j, k, - ax, ex, Run, _, diff --git a/doc/api/index.rst b/doc/api/index.rst index a13c4686de1..41100e6c281 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -36,7 +36,7 @@ Plotting data and laying out the map: Figure.meca Figure.plot Figure.plot3d - Figure.sca + Figure.set_panel Figure.shift_origin Figure.subplot Figure.text diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 1eee2fc8d2e..47a06699f17 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -53,7 +53,7 @@ for index in range(2 * 3): i = index // 3 # row j = index % 3 # column - with fig.sca(ax=index): # sets the current Axes + with fig.set_panel(panel=index): # sets the current panel fig.text( position="MC", text=f"index: {index}, row: {i}, col: {j}", @@ -62,11 +62,12 @@ fig.show() ############################################################################### -# The :meth:`pygmt.Figure.sca` command activates a specified subplot, and all -# subsequent plotting commands will take place in that subplot. 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 ``ax`` argument. Pass -# in either the ``index`` number, or a tuple like (``row``, ``col``) to ``ax``. +# The :meth:`pygmt.Figure.set_panel` command activates a specified subplot, and +# all subsequent plotting commands 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 like +# (``row``, ``col``) to ``panel``. ############################################################################### # .. note:: @@ -83,7 +84,7 @@ ############################################################################### # .. code-block:: default # -# with fig.sca(ax=[0, 2]): +# with fig.set_panel(panel=[0, 2]): # ... ############################################################################### @@ -101,10 +102,18 @@ margins=["0.1c", "0.2c"], title="My Subplot Heading", ): - fig.basemap(region=[0, 10, 0, 10], projection="X?", frame=["af", "WSne"], ax=[0, 0]) - fig.basemap(region=[0, 20, 0, 10], projection="X?", frame=["af", "WSne"], ax=[0, 1]) - fig.basemap(region=[0, 10, 0, 20], projection="X?", frame=["af", "WSne"], ax=[1, 0]) - fig.basemap(region=[0, 20, 0, 20], projection="X?", frame=["af", "WSne"], ax=[1, 1]) + fig.basemap( + region=[0, 10, 0, 10], projection="X?", frame=["af", "WSne"], panel=[0, 0] + ) + fig.basemap( + region=[0, 20, 0, 10], projection="X?", frame=["af", "WSne"], panel=[0, 1] + ) + fig.basemap( + region=[0, 10, 0, 20], projection="X?", frame=["af", "WSne"], panel=[1, 0] + ) + fig.basemap( + region=[0, 20, 0, 20], projection="X?", frame=["af", "WSne"], panel=[1, 1] + ) fig.show() ############################################################################### @@ -131,14 +140,14 @@ # In the above example, we used the following commands to activate the # four subplots explicitly one after another:: # -# fig.basemap(..., ax=[0, 0]) -# fig.basemap(..., ax=[0, 1]) -# fig.basemap(..., ax=[1, 0]) -# fig.basemap(..., ax=[1, 1]) +# 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(..., ax=True)`` without specifying -# any subplot index number, and GMT will automatically activate the next -# subplot. +# 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. ############################################################################### # Shared X and Y axis labels @@ -159,10 +168,10 @@ layout=["Rl", "Cb"], frame="WSrt", ): - fig.basemap(region=[0, 10, 0, 10], projection="X?", ax=True) - fig.basemap(region=[0, 20, 0, 10], projection="X?", ax=True) - fig.basemap(region=[0, 10, 0, 20], projection="X?", ax=True) - fig.basemap(region=[0, 20, 0, 20], projection="X?", ax=True) + 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() ############################################################################### @@ -187,11 +196,15 @@ 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"], ax=[0, 0] + 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"], ax=[1, 0]) - fig.basemap(region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], ax=[1, 1]) + 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() ############################################################################### @@ -223,4 +236,4 @@ ############################################################################### # 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.sca(..., fixedlabel="(a)")`` to manually set the subplot label. +# ``fig.set_panel(..., fixedlabel="(a)")`` to manually set the subplot label. diff --git a/pygmt/figure.py b/pygmt/figure.py index b374bfda2fe..ba43963fa5e 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -387,7 +387,7 @@ def _repr_html_(self): meca, plot, plot3d, - sca, + set_panel, subplot, text, ) diff --git a/pygmt/src/__init__.py b/pygmt/src/__init__.py index 445b3ed509b..1e702203c14 100644 --- a/pygmt/src/__init__.py +++ b/pygmt/src/__init__.py @@ -23,7 +23,7 @@ from pygmt.src.meca import meca from pygmt.src.plot import plot from pygmt.src.plot3d import plot3d -from pygmt.src.subplot import sca, subplot +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 diff --git a/pygmt/src/basemap.py b/pygmt/src/basemap.py index 8b854bb9e56..0f5eb6ec434 100644 --- a/pygmt/src/basemap.py +++ b/pygmt/src/basemap.py @@ -27,7 +27,7 @@ V="verbose", X="xshift", Y="yshift", - c="ax", + c="panel", p="perspective", t="transparency", ) @@ -73,7 +73,7 @@ def basemap(self, **kwargs): kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access if not args_in_kwargs(args=["B", "L", "Td", "Tm", "c"], kwargs=kwargs): raise GMTInvalidInput( - "At least one of frame, map_scale, compass, rose, or ax must be specified." + "At least one of frame, map_scale, compass, rose, or panel must be specified." ) with Session() as lib: lib.call_module("basemap", build_arg_string(kwargs)) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 097f6214bf9..5ded8a51278 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -157,9 +157,9 @@ def subplot(self, nrows=1, ncols=1, **kwargs): @fmt_docstring @contextlib.contextmanager @use_alias(A="fixedlabel", C="clearance", V="verbose") -def sca(self, ax=None, **kwargs): +def set_panel(self, panel=None, **kwargs): r""" - Set the current Axes instance to *ax*. + Set the current subplot panel to plot on. Before you start plotting you must first select the active subplot. Note: If any *projection* option is passed with **?** as scale or width when @@ -173,17 +173,17 @@ def sca(self, ax=None, **kwargs): Parameters ---------- - ax : str or list + panel : str or list *row,col*\|\ *index*. Sets the current subplot until further notice. **Note**: First *row* or *col* is 0, not 1. If not given we go to the next subplot by order specified via **autolabel** in :meth:`pygmt.Figure.subplot`. As an alternative, you may bypass the **sca** mode and instead supply the - common option **ax**=\ [*row,col*] to the first plot command you issue - in that subplot. GMT maintains information about the current figure and - subplot. Also, you may give the one-dimensional *index* instead which - starts at 0 and follows the row or column order set via **autolabel** - in :meth:`pygmt.Figure.subplot`. + common option **panel**=\ [*row,col*] to the first plot command you + issue in that subplot. GMT maintains information about the current + figure and subplot. Also, you may give the one-dimensional *index* + instead which starts at 0 and follows the row or column order set via + **autolabel** in :meth:`pygmt.Figure.subplot`. fixedlabel : str Overrides the automatic labeling with the given string. No modifiers @@ -206,9 +206,9 @@ def sca(self, ax=None, **kwargs): kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") else None # convert tuple or list to comma-separated str - ax = ",".join(map(str, ax)) if is_nonstr_iter(ax) else ax + panel = ",".join(map(str, panel)) if is_nonstr_iter(panel) else panel with Session() as lib: - arg_str = " ".join(["set", f"{ax}", build_arg_string(kwargs)]).strip() + arg_str = " ".join(["set", f"{panel}", build_arg_string(kwargs)]).strip() lib.call_module(module="subplot", args=arg_str) yield diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index 423b701e662..c6d7010238a 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -13,14 +13,14 @@ def test_subplot_basic_frame(): """ fig_ref, fig_test = Figure(), Figure() with fig_ref.subplot(nrows=1, ncols=2, Ff="6c/3c", B="WSne"): - with fig_ref.sca(ax=0): + with fig_ref.set_panel(panel=0): fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot0") - with fig_ref.sca(ax=1): + with fig_ref.set_panel(panel=1): fig_ref.basemap(region=[0, 3, 0, 3], frame="+tplot1") with fig_test.subplot(nrows=1, ncols=2, figsize=("6c", "3c"), frame="WSne"): - with fig_test.sca(ax="0,0"): + with fig_test.set_panel(panel="0,0"): fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot0") - with fig_test.sca(ax=[0, 1]): + with fig_test.set_panel(panel=[0, 1]): fig_test.basemap(region=[0, 3, 0, 3], frame="+tplot1") return fig_ref, fig_test @@ -32,11 +32,11 @@ def test_subplot_direct(): """ fig_ref, fig_test = Figure(), Figure() with fig_ref.subplot(nrows=2, ncols=1, Fs="3c/3c"): - fig_ref.basemap(region=[0, 3, 0, 3], frame="af", ax=0) - fig_ref.basemap(region=[0, 3, 0, 3], frame="af", ax=1) + fig_ref.basemap(region=[0, 3, 0, 3], frame="af", panel=0) + fig_ref.basemap(region=[0, 3, 0, 3], frame="af", panel=1) with fig_test.subplot(nrows=2, ncols=1, subsize=("3c", "3c")): - fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=[0, 0]) - fig_test.basemap(region=[0, 3, 0, 3], frame="af", ax=[1, 0]) + fig_test.basemap(region=[0, 3, 0, 3], frame="af", panel=[0, 0]) + fig_test.basemap(region=[0, 3, 0, 3], frame="af", panel=[1, 0]) return fig_ref, fig_test @@ -55,8 +55,8 @@ def test_subplot_autolabel_margins_title(): with fig_test.subplot( autolabel="(1)", margins=["0.3c", "0.1c"], title="Subplot Title", **kwargs ): - fig_test.basemap(region=[0, 1, 2, 3], frame="WSne", ax=[0, 0]) - fig_test.basemap(region=[4, 5, 6, 7], frame="WSne", ax=[1, 0]) + fig_test.basemap(region=[0, 1, 2, 3], frame="WSne", panel=[0, 0]) + fig_test.basemap(region=[4, 5, 6, 7], frame="WSne", panel=[1, 0]) return fig_ref, fig_test @@ -71,15 +71,15 @@ def test_subplot_clearance_and_shared_xy_axis_layout(): kwargs = dict(nrows=2, ncols=2, frame="WSrt", figsize=("5c", "5c")) with fig_ref.subplot(C="y0.2", SR="l", SC="t", **kwargs): - fig_ref.basemap(region=[0, 4, 0, 4], projection="X?", ax=True) - fig_ref.basemap(region=[0, 8, 0, 4], projection="X?", ax=True) - fig_ref.basemap(region=[0, 4, 0, 8], projection="X?", ax=True) - fig_ref.basemap(region=[0, 8, 0, 8], projection="X?", ax=True) + fig_ref.basemap(region=[0, 4, 0, 4], projection="X?", panel=True) + fig_ref.basemap(region=[0, 8, 0, 4], projection="X?", panel=True) + fig_ref.basemap(region=[0, 4, 0, 8], projection="X?", panel=True) + fig_ref.basemap(region=[0, 8, 0, 8], projection="X?", panel=True) with fig_test.subplot(clearance="y0.2", layout=["Rl", "Ct"], **kwargs): - fig_test.basemap(region=[0, 4, 0, 4], projection="X?", ax=True) - fig_test.basemap(region=[0, 8, 0, 4], projection="X?", ax=True) - fig_test.basemap(region=[0, 4, 0, 8], projection="X?", ax=True) - fig_test.basemap(region=[0, 8, 0, 8], projection="X?", ax=True) + fig_test.basemap(region=[0, 4, 0, 4], projection="X?", panel=True) + fig_test.basemap(region=[0, 8, 0, 4], projection="X?", panel=True) + fig_test.basemap(region=[0, 4, 0, 8], projection="X?", panel=True) + fig_test.basemap(region=[0, 8, 0, 8], projection="X?", panel=True) return fig_ref, fig_test From 6ba4d49e152218d573aa72824d21af69d5916282 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Fri, 12 Feb 2021 11:15:38 +1300 Subject: [PATCH 16/30] Update docstring for layout (S) to say +w is for the figsize/subsize arg Change needed after https://github.com/GenericMappingTools/gmt/pull/4770 --- pygmt/src/subplot.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 5ded8a51278..8ca46ff3e06 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -131,8 +131,9 @@ def subplot(self, nrows=1, ncols=1, **kwargs): append the label if it is the same for the entire subplot. Append **+p** to make all annotations axis-parallel [horizontal]; if not used you may have to set **clearance** to secure extra space for long - horizontal annotations. Append **+w** to draw horizontal and vertical - lines between interior panels using selected pen [no lines]. + horizontal annotations. Append **+w** to the **figsize** or **subsize** + argument to draw horizontal and vertical lines between interior panels + using selected pen [no lines]. title : str While individual subplots can have titles (see **layout** or **frame**), the entire figure may also have an overarching *heading* From d3c0a503b038d0efc6757ec0bf78b1d120bc780b Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Fri, 12 Feb 2021 15:20:04 +1300 Subject: [PATCH 17/30] Apply formatting suggestions from code review Co-authored-by: Dongdong Tian --- examples/tutorials/subplots.py | 48 +++++++++++++++++----------------- pygmt/src/subplot.py | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 47a06699f17..e983b51f610 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -13,7 +13,7 @@ - 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 +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. """ @@ -31,9 +31,9 @@ # Define subplot layout # --------------------- # -# The :meth:`pygmt.Figure.subplot` command is used to setup the layout, size, +# 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 +# grid areas with *n* rows and *m* columns. Each grid area can contain an # individual subplot. For example: ############################################################################### @@ -44,30 +44,30 @@ ############################################################################### # 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 15cm -# wide by 6cm high. Using ``frame="lrtb"`` allows us to customize the map frame +# ``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 index in range(2 * 3): - i = index // 3 # row - j = index % 3 # column - 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], - ) + 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` command activates a specified subplot, and -# all subsequent plotting commands will take place in that subplot panel. This +# 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 like -# (``row``, ``col``) to ``panel``. +# argument. Pass in either the *index* number, or a tuple/list like +# (*row*, *col*) to ``panel``. ############################################################################### # .. note:: @@ -78,7 +78,7 @@ ############################################################################### # 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 +# *row*\=0 and *col*\=2, so that all subsequent plotting commands happen # there, you can use the following command: ############################################################################### @@ -91,7 +91,7 @@ # 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 parameters to configure our subplot. +# figure. We'll also pick up on some new arguments to configure our subplot. fig = pygmt.Figure() with fig.subplot( @@ -118,9 +118,9 @@ ############################################################################### # 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 15cm -# wide and 6cm high (``figsize=["15c", "6c"]``). In addition, we used some -# optional parameters to fine tune some details of the figure creation: +# :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. @@ -154,7 +154,7 @@ # -------------------------- # 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 **layout** option to set a common X and/or +# same X-axis range. You can use the ``layout`` argument to set a common X and/or # Y axis between subplots. fig = pygmt.Figure() diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 8ca46ff3e06..b6f6a98eda9 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -148,7 +148,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): with Session() as lib: try: arg_str = " ".join(["begin", f"{nrows}x{ncols}", build_arg_string(kwargs)]) - lib.call_module(module="subplot", args=arg_str) + lib.call_module("subplot", arg_str) yield finally: v_arg = build_arg_string({"V": kwargs.get("V")}) From eadb8470065da5436802bc796e868e1ac28d8ac7 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Fri, 12 Feb 2021 18:55:16 +1300 Subject: [PATCH 18/30] Fix bug that prevented boolean to -A from working --- pygmt/src/subplot.py | 6 ++++-- pygmt/tests/test_subplot.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index b6f6a98eda9..f91b15300f9 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -142,7 +142,8 @@ def subplot(self, nrows=1, ncols=1, **kwargs): {XY} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access - kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") else None + # allow for spaces in string with needing double quotes + kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") is not None else None kwargs["T"] = f'"{kwargs.get("T")}"' if kwargs.get("T") else None with Session() as lib: @@ -205,7 +206,8 @@ def set_panel(self, panel=None, **kwargs): {V} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access - kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") else None + # allow for spaces in string with needing double quotes + kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") is not None else None # convert tuple or list to comma-separated str panel = ",".join(map(str, panel)) if is_nonstr_iter(panel) else panel diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index c6d7010238a..8a6d5ae10f7 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -48,12 +48,12 @@ def test_subplot_autolabel_margins_title(): fig_ref, fig_test = Figure(), Figure() kwargs = dict(nrows=2, ncols=1, figsize=("15c", "6c")) - with fig_ref.subplot(A="(1)", M="0.3c/0.1c", T="Subplot Title", **kwargs): + with fig_ref.subplot(A="a)", M="0.3c/0.1c", T="Subplot Title", **kwargs): fig_ref.basemap(region=[0, 1, 2, 3], frame="WSne", c="0,0") fig_ref.basemap(region=[4, 5, 6, 7], frame="WSne", c="1,0") with fig_test.subplot( - autolabel="(1)", margins=["0.3c", "0.1c"], title="Subplot Title", **kwargs + autolabel=True, margins=["0.3c", "0.1c"], title="Subplot Title", **kwargs ): fig_test.basemap(region=[0, 1, 2, 3], frame="WSne", panel=[0, 0]) fig_test.basemap(region=[4, 5, 6, 7], frame="WSne", panel=[1, 0]) From f568982d6bcef13f51c125dfd1cf8aeb18360e6f Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Fri, 12 Feb 2021 19:07:50 +1300 Subject: [PATCH 19/30] Add note that subplot panel is activated until further notice Co-Authored-By: Dongdong Tian --- examples/tutorials/subplots.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index e983b51f610..8b58f419774 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -99,21 +99,14 @@ 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?", frame=["af", "WSne"], panel=[0, 0] - ) - fig.basemap( - region=[0, 20, 0, 10], projection="X?", frame=["af", "WSne"], panel=[0, 1] - ) - fig.basemap( - region=[0, 10, 0, 20], projection="X?", frame=["af", "WSne"], panel=[1, 0] - ) - fig.basemap( - region=[0, 20, 0, 20], projection="X?", frame=["af", "WSne"], panel=[1, 1] - ) + 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() ############################################################################### @@ -149,6 +142,16 @@ # 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 # -------------------------- From 6c1ed41e09962fc1804d2eecf373ffabab8a7e8b Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 13 Feb 2021 00:07:05 +1300 Subject: [PATCH 20/30] Remove arg_str strip() for subplot set and end Co-Authored-By: Dongdong Tian --- pygmt/src/subplot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index f91b15300f9..697ff35bab9 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -153,7 +153,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): yield finally: v_arg = build_arg_string({"V": kwargs.get("V")}) - lib.call_module("subplot", f"end {v_arg}".strip()) + lib.call_module("subplot", f"end {v_arg}") @fmt_docstring @@ -212,6 +212,6 @@ def set_panel(self, panel=None, **kwargs): panel = ",".join(map(str, panel)) if is_nonstr_iter(panel) else panel with Session() as lib: - arg_str = " ".join(["set", f"{panel}", build_arg_string(kwargs)]).strip() + arg_str = " ".join(["set", f"{panel}", build_arg_string(kwargs)]) lib.call_module(module="subplot", args=arg_str) yield From fab1b9d417ee9ece46ca385a7e73d5db07d074a8 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 13 Feb 2021 10:42:38 +1300 Subject: [PATCH 21/30] Split layout (S) into sharex (SC) and sharey (SR) --- examples/tutorials/subplots.py | 24 ++++++++------- pygmt/src/subplot.py | 55 ++++++++++++++++++---------------- pygmt/tests/test_subplot.py | 4 +-- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 8b58f419774..4fe0c99ae57 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -78,7 +78,7 @@ ############################################################################### # 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 +# *row*\=0 and *col*\=2, so that all subsequent plotting commands happen # there, you can use the following command: ############################################################################### @@ -157,18 +157,19 @@ # -------------------------- # 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 ``layout`` argument to set a common X and/or -# Y axis between subplots. +# 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"), + figsize=("15c", "6c"), # width of 15 cm, height of 6 cm autolabel=True, - margins=["0.3c", "0.2c"], + margins=["0.3c", "0.2c"], # horizontal 0.3 cm and vertical 0.2 cm margins title="My Subplot Heading", - layout=["Rl", "Cb"], + 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) @@ -178,12 +179,13 @@ fig.show() ############################################################################### -# **Rl** indicates that subplots within a **R**\ ow will share the y-axis, and -# only the **l**\ eft axis is displayed. **Cb** indicates that subplots in -# a column will share the x-axis, and only the **b**\ ottom axis is displayed. +# ``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 **R**\ ow will share the y-axis, and only the **l**\ eft +# axis is displayed. # -# Of course, instead of using the **layout** option, you can also set a -# different **frame** for each subplot to control the axis properties +# 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. ############################################################################### diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 697ff35bab9..ba3370174c2 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -24,7 +24,8 @@ J="projecton", M="margins", R="region", - S="layout", + SC="sharex", + SR="sharey", T="title", V="verbose", X="xshift", @@ -110,30 +111,34 @@ def subplot(self, nrows=1, ncols=1, **kwargs): [Default is half the primary annotation font size, giving the full annotation font size as the default gap]. {R} - layout : str or list - Set subplot layout for shared axes. May be set separately for rows - (**R**) and columns (**C**). E.g. ``layout=['Rl', 'Cb']`` will set - shared axis labels for rows on the **l**eft, and for columns on the - **b**ottom. Considerations for **C**: Use when all subplots in a - **C**\ olumn share a common *x*-range. The first (i.e., **t**\ op) and - the last (i.e., **b**\ ottom) rows will have *x* annotations; append - **t** or **b** to select only one of those two rows [both]. Append - **+l** if annotated *x*-axes should have a label [none]; optionally - append the label if it is the same for the entire subplot. Append - **+t** to make space for subplot titles for each row; use **+tc** for - top row titles only [no subplot titles]. Labels and titles that depends - on which row or column are specified as usual via a subplot's own - **frame** setting. Considerations for **R**: Use when all subplots in a - **R**\ ow share a common *y*-range. The first (i.e., **l**\ eft) and - the last (i.e., **r**\ ight) columns will have *y*-annotations; append - **l** or **r** to select only one of those two columns [both]. Append - **+l** if annotated *y*-axes will have a label [none]; optionally, - append the label if it is the same for the entire subplot. Append - **+p** to make all annotations axis-parallel [horizontal]; if not used - you may have to set **clearance** to secure extra space for long - horizontal annotations. Append **+w** to the **figsize** or **subsize** - argument to draw horizontal and vertical lines between interior panels - using selected pen [no lines]. + sharex : bool or str + Set subplot layout for shared x-axes. Use when all subplots in a column + share a common *x*-range. If ``sharex=True``, the first (i.e., + **t**\ op) and the last (i.e., **b**\ ottom) rows will have + *x*-annotations; use ``sharex='t'`` or ``sharex='b'`` to select only + one of those two rows [both]. Append **+l** if annotated *x*-axes + should have a label [none]; optionally append the label if it is the + same for the entire subplot. Append **+t** to make space for subplot + titles for each row; use **+tc** for top row titles only [no subplot + titles]. + sharey : bool or str + Set subplot layout for shared y-axes. Use when all subplots in a row + share a common *y*-range. If ``sharey=True``, the first (i.e., + **l**\ eft) and the last (i.e., **r**\ ight) columns will have + *y*-annotations; use ``sharey='l'`` or ``sharey='r'`` to select only + one of those two columns [both]. Append **+l** if annotated *y*-axes + will have a label [none]; optionally, append the label if it is the + same for the entire subplot. Append **+p** to make all annotations + axis-parallel [horizontal]; if not used you may have to set + **clearance** to secure extra space for long horizontal annotations. + + Notes for sharex/sharey: + + - Labels and titles that depends on which row or column are specified + as usual via a subplot's own **frame** setting. + - Append **+w** to the **figsize** or **subsize** argument to draw + horizontal and vertical lines between interior panels using selected + pen [no lines]. title : str While individual subplots can have titles (see **layout** or **frame**), the entire figure may also have an overarching *heading* diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index 8a6d5ae10f7..7404e063079 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -70,13 +70,13 @@ def test_subplot_clearance_and_shared_xy_axis_layout(): fig_ref, fig_test = Figure(), Figure() kwargs = dict(nrows=2, ncols=2, frame="WSrt", figsize=("5c", "5c")) - with fig_ref.subplot(C="y0.2", SR="l", SC="t", **kwargs): + with fig_ref.subplot(C="y0.2", SC="t", SR="", **kwargs): fig_ref.basemap(region=[0, 4, 0, 4], projection="X?", panel=True) fig_ref.basemap(region=[0, 8, 0, 4], projection="X?", panel=True) fig_ref.basemap(region=[0, 4, 0, 8], projection="X?", panel=True) fig_ref.basemap(region=[0, 8, 0, 8], projection="X?", panel=True) - with fig_test.subplot(clearance="y0.2", layout=["Rl", "Ct"], **kwargs): + with fig_test.subplot(clearance="y0.2", sharex="t", sharey=True, **kwargs): fig_test.basemap(region=[0, 4, 0, 4], projection="X?", panel=True) fig_test.basemap(region=[0, 8, 0, 4], projection="X?", panel=True) fig_test.basemap(region=[0, 4, 0, 8], projection="X?", panel=True) From ad34871c15b3ea10a66470f2f1068e4519fc247b Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 13 Feb 2021 12:18:54 +1300 Subject: [PATCH 22/30] Fix small typo --- pygmt/src/subplot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index ba3370174c2..58e36ee652b 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -92,7 +92,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): plot scales, bars, text, etc. Settings specified under **begin** directive apply to all subplots, while settings under **set** only apply to the selected (active) subplot. **Note**: Common options - **x_offset** and **y_offset* are not available during subplots; use + **x_offset** and **y_offset** are not available during subplots; use **clearance** instead. {J} margins : list From ef9ee2d7fa53f642bc23baa88bb037b907c50cb5 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 13 Feb 2021 23:38:01 +1300 Subject: [PATCH 23/30] Proofread edits to subplot and set_panel docstring Co-Authored-By: Dongdong Tian --- pygmt/src/subplot.py | 60 ++++++++++++++++++------------------- pygmt/tests/test_subplot.py | 6 ++-- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 58e36ee652b..ba819b3354b 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -36,11 +36,11 @@ def subplot(self, nrows=1, ncols=1, **kwargs): r""" Create multi-panel subplot figures. - The **subplot** module is used to split the current figure into a - rectangular layout of subplots that each may contain a single self- - contained figure. Begin by defining the layout of the entire multi-panel - illustration. Several options are available to specify the systematic - layout, labeling, dimensions, and more for the subplots. + This function is used to split the current figure into a rectangular layout + of subplots that each may contain a single self-contained figure. Begin by + defining the layout of the entire multi-panel illustration. Several + arguments are available to specify the systematic layout, labeling, + dimensions, and more for the subplots. Full option list at :gmt-docs:`subplot.html#synopsis-begin-mode` @@ -68,7 +68,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): *refpoint* to specify where the tag should be placed in the subplot [TL]. 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 + 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** [3p/3p, i.e., 15% of the :gmt-term:`FONT_TAG` size dimension]. Append **+g**\ *fill* to paint the tag's text box with @@ -81,21 +81,19 @@ def subplot(self, nrows=1, ncols=1, **kwargs): numerals]. Append **+v** to increase tag numbers vertically down columns [horizontally across rows]. {B} - clearance : str + clearance : str or list [*side*]\ *clearance*. Reserve a space of dimension *clearance* between the margin and the subplot on the specified side, using *side* values from **w**, **e**, - **s**, or **n**, or **x** for both **w** and **e** or **y** for both - **s** and **n**. No *side* means all sides. The option is repeatable - to set aside space on more than one side. Such space will be left - untouched by the main map plotting but can be accessed by modules that - plot scales, bars, text, etc. Settings specified under **begin** - directive apply to all subplots, while settings under **set** only - apply to the selected (active) subplot. **Note**: Common options - **x_offset** and **y_offset** are not available during subplots; use - **clearance** instead. + **s**, or **n**; or **x** for both **w** and **e**; or **y** for both + **s** and **n**. No *side* means all sides (i.e. ``clearance='1c'`` + would set a clearance of 1 cm on all sides). The option is repeatable + to set aside space on more than one side (e.g. ``clearance=['w1c', + 's2c']`` would set a clearance of 1 cm on west side and 2 cm on south + side). Such space will be left untouched by the main map plotting but + can be accessed by modules that plot scales, bars, text, etc. {J} - margins : list + margins : str or list This is margin space that is added between neighboring subplots (i.e., the interior margins) in addition to the automatic space added for tick marks, annotations, and labels. The margins can be specified as either: @@ -130,18 +128,18 @@ def subplot(self, nrows=1, ncols=1, **kwargs): will have a label [none]; optionally, append the label if it is the same for the entire subplot. Append **+p** to make all annotations axis-parallel [horizontal]; if not used you may have to set - **clearance** to secure extra space for long horizontal annotations. + ``clearance`` to secure extra space for long horizontal annotations. Notes for sharex/sharey: - Labels and titles that depends on which row or column are specified - as usual via a subplot's own **frame** setting. - - Append **+w** to the **figsize** or **subsize** argument to draw + as usual via a subplot's own ``frame`` setting. + - Append **+w** to the ``figsize`` or ``subsize`` argument to draw horizontal and vertical lines between interior panels using selected pen [no lines]. title : str - While individual subplots can have titles (see **layout** or - **frame**), the entire figure may also have an overarching *heading* + While individual subplots can have titles (see ``layout`` or + ``frame``), the entire figure may also have an overarching *heading* [no heading]. Font is determined by setting :gmt-term:`FONT_HEADING`. {V} {XY} @@ -184,18 +182,18 @@ def set_panel(self, panel=None, **kwargs): *row,col*\|\ *index*. Sets the current subplot until further notice. **Note**: First *row* or *col* is 0, not 1. If not given we go to the next subplot by order - specified via **autolabel** in :meth:`pygmt.Figure.subplot`. As an - alternative, you may bypass the **sca** mode and instead supply the - common option **panel**=\ [*row,col*] to the first plot command you - issue in that subplot. GMT maintains information about the current - figure and subplot. Also, you may give the one-dimensional *index* - instead which starts at 0 and follows the row or column order set via - **autolabel** in :meth:`pygmt.Figure.subplot`. + specified via ``autolabel`` in :meth:`pygmt.Figure.subplot`. As an + alternative, you may bypass using :meth:`pygmt.Figure.set_panel` and + instead supply the common option **panel**\ =[*row,col*] to the first + plot command you issue in that subplot. GMT maintains information about + the current figure and subplot. Also, you may give the one-dimensional + *index* instead which starts at 0 and follows the row or column order + set via ``autolabel`` in :meth:`pygmt.Figure.subplot`. fixedlabel : str Overrides the automatic labeling with the given string. No modifiers are allowed. Placement, justification, etc. are all inherited from how - **autolabel** was specified by the initial :meth:`pygmt.Figure.subplot` + ``autolabel`` was specified by the initial :meth:`pygmt.Figure.subplot` command. clearance : str @@ -205,7 +203,7 @@ def set_panel(self, panel=None, **kwargs): **s**, or **n**. The option is repeatable to set aside space on more than one side. Such space will be left untouched by the main map plotting but can be accessed by modules that plot scales, bars, text, - etc. This setting overrides the common clearances set by **clearance** + etc. This setting overrides the common clearances set by ``clearance`` in the initial :meth:`pygmt.Figure.subplot` call. {V} diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index 7404e063079..7b3353b4f18 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -70,13 +70,15 @@ def test_subplot_clearance_and_shared_xy_axis_layout(): fig_ref, fig_test = Figure(), Figure() kwargs = dict(nrows=2, ncols=2, frame="WSrt", figsize=("5c", "5c")) - with fig_ref.subplot(C="y0.2", SC="t", SR="", **kwargs): + with fig_ref.subplot(C="y0.2c", SC="t", SR="", **kwargs): fig_ref.basemap(region=[0, 4, 0, 4], projection="X?", panel=True) fig_ref.basemap(region=[0, 8, 0, 4], projection="X?", panel=True) fig_ref.basemap(region=[0, 4, 0, 8], projection="X?", panel=True) fig_ref.basemap(region=[0, 8, 0, 8], projection="X?", panel=True) - with fig_test.subplot(clearance="y0.2", sharex="t", sharey=True, **kwargs): + with fig_test.subplot( + clearance=["s0.2c", "n0.2c"], sharex="t", sharey=True, **kwargs + ): fig_test.basemap(region=[0, 4, 0, 4], projection="X?", panel=True) fig_test.basemap(region=[0, 8, 0, 4], projection="X?", panel=True) fig_test.basemap(region=[0, 4, 0, 8], projection="X?", panel=True) From c6b0242141e9508df110de07bd996d503d7d5713 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 13 Feb 2021 23:49:22 +1300 Subject: [PATCH 24/30] Update fig.set_panel clearance docstring with code-block example --- pygmt/src/subplot.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index ba819b3354b..164c5d56d8d 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -196,15 +196,17 @@ def set_panel(self, panel=None, **kwargs): ``autolabel`` was specified by the initial :meth:`pygmt.Figure.subplot` command. - clearance : str + clearance : str or list [*side*]\ *clearance*. Reserve a space of dimension *clearance* between the margin and the subplot on the specified side, using *side* values from **w**, **e**, **s**, or **n**. The option is repeatable to set aside space on more - than one side. Such space will be left untouched by the main map - plotting but can be accessed by modules that plot scales, bars, text, - etc. This setting overrides the common clearances set by ``clearance`` - in the initial :meth:`pygmt.Figure.subplot` call. + than one side (e.g. ``clearance=['w1c', 's2c']`` would set a clearance + of 1 cm on west side and 2 cm on south side). Such space will be left + untouched by the main map plotting but can be accessed by modules that + plot scales, bars, text, etc. This setting overrides the common + clearances set by ``clearance`` in the initial + :meth:`pygmt.Figure.subplot` call. {V} """ From c475861383423bfa312e2bd625a4ea61682c14be Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sun, 14 Feb 2021 08:48:42 +1300 Subject: [PATCH 25/30] Validate subplot nrows/ncols and figsize/subsize argument inputs Add validation checks for inputs to --- pygmt/src/subplot.py | 10 ++++++++++ pygmt/tests/test_subplot.py | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 164c5d56d8d..1a09dc5ef31 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -4,6 +4,7 @@ import contextlib from pygmt.clib import Session +from pygmt.exceptions import GMTInvalidInput from pygmt.helpers import ( build_arg_string, fmt_docstring, @@ -56,6 +57,8 @@ def subplot(self, nrows=1, ncols=1, **kwargs): Specify the final figure dimensions as ``(width, height)``. subsize : tuple Specify the dimensions of each subplot directly as ``(width, height)``. + Note that only one of ``figsize`` or ``subsize`` can be provided at + once. autolabel : bool or str [*autolabel*][**+c**\ *dx*\ [/*dy*]][**+g**\ *fill*][**+j**\|\ **J**\ @@ -149,6 +152,13 @@ def subplot(self, nrows=1, ncols=1, **kwargs): kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") is not None else None kwargs["T"] = f'"{kwargs.get("T")}"' if kwargs.get("T") else None + if nrows < 1 or ncols < 1: + raise GMTInvalidInput("Please ensure that both `nrows`>=1 and `ncols`>=1.") + if kwargs.get("Ff") and kwargs.get("Fs"): + raise GMTInvalidInput( + "Please provide either one of `figsize` or `subsize` only." + ) + with Session() as lib: try: arg_str = " ".join(["begin", f"{nrows}x{ncols}", build_arg_string(kwargs)]) diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index 7b3353b4f18..bfb78ba7632 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -1,7 +1,9 @@ """ Tests subplot. """ +import pytest from pygmt import Figure +from pygmt.exceptions import GMTInvalidInput from pygmt.helpers.testing import check_figures_equal @@ -85,3 +87,24 @@ def test_subplot_clearance_and_shared_xy_axis_layout(): fig_test.basemap(region=[0, 8, 0, 8], projection="X?", panel=True) return fig_ref, fig_test + + +def test_subplot_figsize_and_subsize_error(): + """ + Check that an error is raised when both figsize and subsize arguments are + passed into subplot. + """ + fig = Figure() + with pytest.raises(GMTInvalidInput): + with fig.subplot(figsize=("2c", "1c"), subsize=("2c", "1c")): + pass + + +def test_subplot_nrows_ncols_less_than_one_error(): + """ + Check that an error is raised when nrows or ncols is less than one. + """ + fig = Figure() + with pytest.raises(GMTInvalidInput): + with fig.subplot(nrows=0, ncols=-1, figsize=("2c", "1c")): + pass From a48b600d2b720d48e69d4fd1ae2785e52205411b Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sun, 14 Feb 2021 08:53:21 +1300 Subject: [PATCH 26/30] Proof edits to subplot round 2 Co-Authored-By: Dongdong Tian --- examples/tutorials/subplots.py | 12 ++++++------ pygmt/src/subplot.py | 4 ++-- pygmt/tests/test_subplot.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 4fe0c99ae57..5da4f0cf80a 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -1,6 +1,6 @@ """ -Making subplot panels -===================== +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 @@ -181,11 +181,11 @@ ############################################################################### # ``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 **R**\ ow will share the y-axis, and only the **l**\ eft -# axis is displayed. +# 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 +# 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. ############################################################################### diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 1a09dc5ef31..15e6ccbe85f 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -133,7 +133,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): axis-parallel [horizontal]; if not used you may have to set ``clearance`` to secure extra space for long horizontal annotations. - Notes for sharex/sharey: + Notes for ``sharex``/``sharey``: - Labels and titles that depends on which row or column are specified as usual via a subplot's own ``frame`` setting. @@ -141,7 +141,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): horizontal and vertical lines between interior panels using selected pen [no lines]. title : str - While individual subplots can have titles (see ``layout`` or + While individual subplots can have titles (see ``sharex``/``sharey`` or ``frame``), the entire figure may also have an overarching *heading* [no heading]. Font is determined by setting :gmt-term:`FONT_HEADING`. {V} diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index bfb78ba7632..b03b8a4659f 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -30,7 +30,7 @@ def test_subplot_basic_frame(): @check_figures_equal() def test_subplot_direct(): """ - Plot map elements to subplot directly using ax argument. + Plot map elements to subplot directly using the panel argument. """ fig_ref, fig_test = Figure(), Figure() with fig_ref.subplot(nrows=2, ncols=1, Fs="3c/3c"): From f4499a88a1a20d1d02f5d7a0a3ceb9ac5768aa42 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sun, 14 Feb 2021 09:34:55 +1300 Subject: [PATCH 27/30] Revise advanced subplot layout subsection to use two subplots calls Co-Authored-By: Dongdong Tian --- examples/tutorials/subplots.py | 54 +++++++++++++++------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 5da4f0cf80a..f17708aaa0e 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -199,46 +199,38 @@ # the first subplot occupying the first row. fig = pygmt.Figure() -with fig.subplot(nrows=2, ncols=2, figsize=("15c", "6c"), autolabel=True): +# Bottom row, two subplots +with fig.subplot(nrows=1, ncols=2, figsize=("15c", "3c"), autolabel="b)"): fig.basemap( - region=[0, 10, 0, 10], projection="X15c/3c", frame=["af", "WSne"], panel=[0, 0] + region=[0, 5, 0, 5], projection="X?", 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] + region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], panel=[0, 1] ) +# Top row, one subplot +with fig.subplot( + nrows=1, ncols=1, figsize=("15c", "3c"), autolabel="a)", yshift="h+1c" +): fig.basemap( - region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], panel=[1, 1] + region=[0, 10, 0, 10], projection="X?", frame=["af", "WSne"], panel=[0, 0] ) -fig.show() + fig.text(text="TEXT", x=5, y=5) -############################################################################### -# -# 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"``. +fig.show() ############################################################################### -# .. 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. +# We start by drawing the bottom two subplots, setting ``autolabel="b)"`` so +# that the subplots are labelled 'b)' and 'c)'. Then, we plot a single subplot +# on the top row by using ``fig.subplot(..., yshift="h+1c")`` which shifts the +# plot origin 1 cm beyond the **h**\ eight of the previous (bottom row) plot. +# You may need to adjust this ``yshift`` parameter to make your plot look nice. +# This top row uses ``autolabel="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. ############################################################################### -# 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. +# You can also manually override the ``autolabel`` 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 adding +# a more descriptive subtitle to individual subplots. From e0900e9f18df0d73a754c336cbbdebf05c089ff4 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sun, 14 Feb 2021 21:09:56 +1300 Subject: [PATCH 28/30] Proofread edits to subplot round 3 Co-Authored-By: Dongdong Tian --- examples/tutorials/subplots.py | 23 ++++++++++++----------- pygmt/src/subplot.py | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index f17708aaa0e..32c4394e770 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -207,10 +207,10 @@ fig.basemap( region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], panel=[0, 1] ) +# Move the plot origin upward by 1 cm +fig.shift_origin(yshift="h+1c") # Top row, one subplot -with fig.subplot( - nrows=1, ncols=1, figsize=("15c", "3c"), autolabel="a)", yshift="h+1c" -): +with fig.subplot(nrows=1, ncols=1, figsize=("15c", "3c"), autolabel="a)"): fig.basemap( region=[0, 10, 0, 10], projection="X?", frame=["af", "WSne"], panel=[0, 0] ) @@ -223,14 +223,15 @@ # We start by drawing the bottom two subplots, setting ``autolabel="b)"`` so # that the subplots are labelled 'b)' and 'c)'. Then, we plot a single subplot # on the top row by using ``fig.subplot(..., yshift="h+1c")`` which shifts the -# plot origin 1 cm beyond the **h**\ eight of the previous (bottom row) plot. -# You may need to adjust this ``yshift`` parameter to make your plot look nice. -# This top row uses ``autolabel="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. +# plot origin 1 cm above the **h**\ eight of the entire figure that is +# currently plotted (i.e. the bottom row subplots). You may need to adjust this +# ``yshift`` parameter to make your plot look nice. This top row uses +# ``autolabel="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 -# 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 adding -# a more descriptive subtitle to individual subplots. +# 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 +# adding a more descriptive subtitle to individual subplots. diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 15e6ccbe85f..e608a714201 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -54,9 +54,9 @@ def subplot(self, nrows=1, ncols=1, **kwargs): ncols : int Number of horizontal columns of the subplot grid. figsize : tuple - Specify the final figure dimensions as ``(width, height)``. + Specify the final figure dimensions as (*width*, *height*). subsize : tuple - Specify the dimensions of each subplot directly as ``(width, height)``. + Specify the dimensions of each subplot directly as (*width*, *height*). Note that only one of ``figsize`` or ``subsize`` can be provided at once. @@ -153,10 +153,10 @@ def subplot(self, nrows=1, ncols=1, **kwargs): kwargs["T"] = f'"{kwargs.get("T")}"' if kwargs.get("T") else None if nrows < 1 or ncols < 1: - raise GMTInvalidInput("Please ensure that both `nrows`>=1 and `ncols`>=1.") + raise GMTInvalidInput("Please ensure that both 'nrows'>=1 and 'ncols'>=1.") if kwargs.get("Ff") and kwargs.get("Fs"): raise GMTInvalidInput( - "Please provide either one of `figsize` or `subsize` only." + "Please provide either one of 'figsize' or 'subsize' only." ) with Session() as lib: @@ -177,12 +177,12 @@ def set_panel(self, panel=None, **kwargs): Set the current subplot panel to plot on. Before you start plotting you must first select the active subplot. Note: - If any *projection* option is passed with **?** as scale or width when - plotting subplots, then the dimensions of the map are automatically - determined by the subplot size and your region. For Cartesian plots: If you - want the scale to apply equally to both dimensions then you must specify - ``projection="x"`` [The default ``projection="X"`` will fill the subplot by - using unequal scales]. + If any *projection* option is passed with the question mark **?** as scale + or width when plotting subplots, then the dimensions of the map are + automatically determined by the subplot size and your region. For Cartesian + plots: If you want the scale to apply equally to both dimensions then you + must specify ``projection="x"`` [The default ``projection="X"`` will fill + the subplot by using unequal scales]. {aliases} From ebc807ec7caffa736841db7c17d82c80a603b16a Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sun, 14 Feb 2021 21:57:58 +1300 Subject: [PATCH 29/30] Replace argument with parameter as per #886 --- examples/tutorials/subplots.py | 14 +++++++------- pygmt/src/subplot.py | 4 ++-- pygmt/tests/test_subplot.py | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 32c4394e770..4e1fbc1c967 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -66,7 +66,7 @@ # 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 +# parameter. Pass in either the *index* number, or a tuple/list like # (*row*, *col*) to ``panel``. ############################################################################### @@ -91,7 +91,7 @@ # 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. +# figure. We'll also pick up on some new parameters to configure our subplot. fig = pygmt.Figure() with fig.subplot( @@ -111,9 +111,9 @@ ############################################################################### # 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: +# :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 parameters 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. @@ -146,7 +146,7 @@ # .. note:: # # All plotting functions (e.g. :meth:`pygmt.Figure.coast`, -# :meth:`pygmt.Figure.text`, etc) are able to use ``panel`` argument when +# :meth:`pygmt.Figure.text`, etc) are able to use ``panel`` parameter 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 @@ -157,7 +157,7 @@ # -------------------------- # 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 +# same X-axis range. You can use the ``sharex``/``sharey`` parameters to set a # common X and/or Y axis between subplots. fig = pygmt.Figure() diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index e608a714201..57d272b3432 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -40,7 +40,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): This function is used to split the current figure into a rectangular layout of subplots that each may contain a single self-contained figure. Begin by defining the layout of the entire multi-panel illustration. Several - arguments are available to specify the systematic layout, labeling, + parameters are available to specify the systematic layout, labeling, dimensions, and more for the subplots. Full option list at :gmt-docs:`subplot.html#synopsis-begin-mode` @@ -137,7 +137,7 @@ def subplot(self, nrows=1, ncols=1, **kwargs): - Labels and titles that depends on which row or column are specified as usual via a subplot's own ``frame`` setting. - - Append **+w** to the ``figsize`` or ``subsize`` argument to draw + - Append **+w** to the ``figsize`` or ``subsize`` parameter to draw horizontal and vertical lines between interior panels using selected pen [no lines]. title : str diff --git a/pygmt/tests/test_subplot.py b/pygmt/tests/test_subplot.py index b03b8a4659f..44d78a6aa9d 100644 --- a/pygmt/tests/test_subplot.py +++ b/pygmt/tests/test_subplot.py @@ -30,7 +30,7 @@ def test_subplot_basic_frame(): @check_figures_equal() def test_subplot_direct(): """ - Plot map elements to subplot directly using the panel argument. + Plot map elements to subplot directly using the panel parameter. """ fig_ref, fig_test = Figure(), Figure() with fig_ref.subplot(nrows=2, ncols=1, Fs="3c/3c"): @@ -91,7 +91,7 @@ def test_subplot_clearance_and_shared_xy_axis_layout(): def test_subplot_figsize_and_subsize_error(): """ - Check that an error is raised when both figsize and subsize arguments are + Check that an error is raised when both figsize and subsize parameters are passed into subplot. """ fig = Figure() From 30ef9c459c167b513fc3e489b89234e1631102f4 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Mon, 15 Feb 2021 08:34:57 +1300 Subject: [PATCH 30/30] Proofread edits to subplot round 4 Co-Authored-By: Dongdong Tian --- examples/tutorials/subplots.py | 14 +++++++------- pygmt/src/subplot.py | 32 ++++++++++++++++---------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/tutorials/subplots.py b/examples/tutorials/subplots.py index 4e1fbc1c967..64c5e04533a 100644 --- a/examples/tutorials/subplots.py +++ b/examples/tutorials/subplots.py @@ -207,7 +207,7 @@ fig.basemap( region=[0, 5, 0, 5], projection="X?", frame=["af", "WSne"], panel=[0, 1] ) -# Move the plot origin upward by 1 cm +# 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)"): @@ -221,12 +221,12 @@ ############################################################################### # # We start by drawing the bottom two subplots, setting ``autolabel="b)"`` so -# that the subplots are labelled 'b)' and 'c)'. Then, we plot a single subplot -# on the top row by using ``fig.subplot(..., yshift="h+1c")`` which shifts the -# plot origin 1 cm above the **h**\ eight of the entire figure that is -# currently plotted (i.e. the bottom row subplots). You may need to adjust this -# ``yshift`` parameter to make your plot look nice. This top row uses -# ``autolabel="a)"``, and we also plotted some text inside. Note that +# that the subplots are labelled '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 # ``projection="X?"`` was used to let GMT automatically determine the size of # the subplot according to the size of the subplot area. diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index 57d272b3432..fdf11856bf1 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -67,22 +67,22 @@ def subplot(self, nrows=1, ncols=1, **kwargs): Specify automatic tagging of each subplot. Append either a number or letter [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. Use **+j|J**\ - *refpoint* to specify where the tag should be placed in the subplot - [TL]. 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** [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* [no painting]. Append **+o**\ *dx*\ [/*dy*] to offset the tag's - reference point in the direction implied by the justification [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 selected *pen* [no - outline]. Append **+r** to typeset your tag numbers using lowercase - Roman numerals; use **+R** for uppercase Roman numerals [Arabic - numerals]. Append **+v** to increase tag numbers vertically down - columns [horizontally across rows]. + any side if these should be typeset as part of the tag. Use + **+j**\|\ **J**\ *refpoint* to specify where the tag should be placed + in the subplot [TL]. 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** [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* [no painting]. Append + **+o**\ *dx*\ [/*dy*] to offset the tag's reference point in the + direction implied by the justification [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 selected *pen* [no outline]. Append **+r** to + typeset your tag numbers using lowercase Roman numerals; use **+R** for + uppercase Roman numerals [Arabic numerals]. Append **+v** to increase + tag numbers vertically down columns [horizontally across rows]. {B} clearance : str or list [*side*]\ *clearance*.