Skip to content

Commit 12adb41

Browse files
willschlitzerweiji14seisman
authored
Wrap inset (#788)
*Wrap inset function to use a context manager *Create src/inset.py and import inset function into base_plotting.py Co-authored-by: Wei Ji <[email protected]> Co-authored-by: Dongdong Tian <[email protected]>
1 parent 213a414 commit 12adb41

File tree

8 files changed

+313
-2
lines changed

8 files changed

+313
-2
lines changed

doc/api/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Plotting data and laying out the map:
3737
Figure.shift_origin
3838
Figure.text
3939
Figure.meca
40+
Figure.inset
4041

4142
Color palette table generation:
4243

doc/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
tutorials/contour-map.rst
3939
tutorials/earth-relief.rst
4040
tutorials/3d-perspective-image.rst
41+
tutorials/inset.rst
4142
tutorials/configuration.rst
4243

4344
.. toctree::

examples/gallery/plot/inset.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
Inset
3+
-----
4+
5+
The :meth:`pygmt.Figure.inset` method adds an inset figure inside a larger
6+
figure. The function is called using a ``with`` statement, and its position,
7+
box, offset, and margin parameters are set. Within the ``with`` statement,
8+
PyGMT plotting functions can be called that add to the inset figure.
9+
"""
10+
import pygmt
11+
12+
fig = pygmt.Figure()
13+
# Create the primary figure, setting the region to Madagascar, the land color to
14+
# "brown", the water to "lightblue", the shorelines width to "thin", and adding a frame
15+
fig.coast(region="MG+r2", land="brown", water="lightblue", shorelines="thin", frame="a")
16+
# Create an inset, setting the position to top left, the width to 3.5 centimeters, and
17+
# the x- and y-offsets to 0.2 centimeters. The margin is set to 0, and the border is "green".
18+
with fig.inset(position="jTL+w3.5c+o0.2c", margin=0, box="+pgreen"):
19+
# Create a figure in the inset using coast. This example uses the azimuthal
20+
# orthogonal projection centered at 47E, 20S. The land is set to "gray" and
21+
# Madagascar is highlighted in "red".
22+
fig.coast(
23+
region="g", projection="G47/-20/3.5c", land="gray", water="white", dcw="MG+gred"
24+
)
25+
fig.show()

examples/tutorials/inset.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""
2+
Adding an inset to the figure
3+
=============================
4+
5+
To plot an inset figure inside another larger figure, we can use the
6+
:meth:`pygmt.Figure.inset` method. After a large figure has been created,
7+
call ``inset`` using a ``with`` statement, and new plot elements will be
8+
added to the inset figure instead of the larger figure.
9+
"""
10+
# sphinx_gallery_thumbnail_number = 4
11+
12+
import pygmt
13+
14+
########################################################################################
15+
#
16+
# Prior to creating an inset figure, a larger figure must first be plotted. In the
17+
# example below, :meth:`pygmt.Figure.coast` is used to create a map of the US state of
18+
# Massachusetts.
19+
20+
fig = pygmt.Figure()
21+
fig.coast(
22+
region=[-74, -69.5, 41, 43], # Set bounding box of the large figure
23+
borders="2/thin", # Plot state boundaries with thin lines
24+
shorelines="thin", # Plot coastline with thin lines
25+
projection="M15c", # Set Mercator projection and size of 15 centimeter
26+
land="lightyellow", # Color land areas light yellow
27+
water="lightblue", # Color water areas light blue
28+
frame="a", # Set frame with annotation and major tick spacing
29+
)
30+
fig.show()
31+
32+
########################################################################################
33+
#
34+
# The :meth:`pygmt.Figure.inset` method uses a context manager, and is called using a
35+
# ``with`` statement. The ``position`` argument, including the inset width, is required
36+
# to plot the inset. Using the **j** argument, the location of the inset is
37+
# set to one of the 9 anchors (bottom-middle-top and left-center-right). In the
38+
# example below, ``BL`` sets the inset to the bottom left. The ``box`` argument can
39+
# set the fill and border of the inset. In the example below, ``+pblack`` sets the
40+
# border color to black and ``+gred`` sets the fill to red.
41+
42+
fig = pygmt.Figure()
43+
fig.coast(
44+
region=[-74, -69.5, 41, 43],
45+
borders="2/thin",
46+
shorelines="thin",
47+
projection="M15c",
48+
land="lightyellow",
49+
water="lightblue",
50+
frame="a",
51+
)
52+
with fig.inset(position="jBL+w3c", box="+pblack+glightred"):
53+
# pass is used to exit the with statement as no plotting functions are called
54+
pass
55+
fig.show()
56+
57+
########################################################################################
58+
#
59+
# When using **j** to set the anchor of the inset, the default location is in
60+
# contact with the nearby axis or axes. The offset of the inset can be set with **+o**,
61+
# followed by the offsets along the x- and y-axis. If only one offset is
62+
# passed, it is applied to both axes. Each offset can have its own unit. In
63+
# the example below, the inset is shifted 0.5 centimeters on the x-axis and
64+
# 0.2 centimeters on the y-axis.
65+
66+
fig = pygmt.Figure()
67+
fig.coast(
68+
region=[-74, -69.5, 41, 43],
69+
borders="2/thin",
70+
shorelines="thin",
71+
projection="M15c",
72+
land="lightyellow",
73+
water="lightblue",
74+
frame="a",
75+
)
76+
with fig.inset(position="jBL+w3c+o0.5c/0.2c", box="+pblack+glightred"):
77+
pass
78+
fig.show()
79+
80+
########################################################################################
81+
#
82+
# Standard plotting functions can be called from within the ``inset`` context manager.
83+
# The example below uses :meth:`pygmt.Figure.coast` to plot a zoomed out map that
84+
# selectively paints the state of Massachusetts to shows its location relative to
85+
# other states.
86+
87+
fig = pygmt.Figure()
88+
fig.coast(
89+
region=[-74, -69.5, 41, 43],
90+
borders="2/thin",
91+
shorelines="thin",
92+
projection="M15c",
93+
land="lightyellow",
94+
water="lightblue",
95+
frame="a",
96+
)
97+
# This does not include an inset fill as it is covered by the inset figure
98+
with fig.inset(position="jBL+w3c+o0.5c/0.2c", box="+pblack"):
99+
# Use a plotting function to create a figure inside the inset
100+
fig.coast(
101+
region=[-80, -65, 35, 50],
102+
projection="M3c",
103+
land="gray",
104+
borders=[1, 2],
105+
shorelines="1/thin",
106+
water="white",
107+
# Use dcw to selectively highlight an area
108+
dcw="US.MA+gred",
109+
)
110+
fig.show()

pygmt/base_plotting.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1642,5 +1642,4 @@ def text(
16421642
arg_str = " ".join([fname, build_arg_string(kwargs)])
16431643
lib.call_module("text", arg_str)
16441644

1645-
# GMT Supplementary modules
1646-
from pygmt.src import meca # pylint: disable=import-outside-toplevel
1645+
from pygmt.src import inset, meca # pylint: disable=import-outside-toplevel

pygmt/src/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
Source code for PyGMT modules.
33
"""
44
# pylint: disable=import-outside-toplevel
5+
from pygmt.src.inset import inset
56
from pygmt.src.meca import meca

pygmt/src/inset.py

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
"""
2+
inset - Create inset figures.
3+
"""
4+
import contextlib
5+
6+
from pygmt.clib import Session
7+
from pygmt.helpers import build_arg_string, fmt_docstring, kwargs_to_strings, use_alias
8+
9+
10+
@fmt_docstring
11+
@contextlib.contextmanager
12+
@use_alias(D="position", F="box", M="margin", N="no_clip", V="verbose")
13+
@kwargs_to_strings(D="sequence", M="sequence")
14+
def inset(self, **kwargs):
15+
r"""
16+
Create an inset figure to be placed within a larger figure.
17+
18+
This function sets the position, frame, and margins for a smaller figure
19+
inside of the larger figure. Plotting functions that are called within the
20+
context manager are added to the inset figure.
21+
22+
Full option list at :gmt-docs:`inset.html`
23+
24+
{aliases}
25+
26+
Parameters
27+
----------
28+
position : str or list
29+
*xmin/xmax/ymin/ymax*\ [**+r**][**+u**\ *unit*]] \
30+
| [**g**\|\ **j**\|\ **J**\|\ **n**\|\ **x**]\ *refpoint*\
31+
**+w**\ *width*\ [/*height*][**+j**\ *justify*]
32+
[**+o**\ *dx*\ [/*dy*]]
33+
34+
*This is the only required argument.*
35+
Define the map inset rectangle on the map. Specify the rectangle
36+
in one of three ways:
37+
38+
Append **g**\ *lon*/*lat* for map (user) coordinates,
39+
**j**\ *code* or **J**\ *code* for setting the *refpoint* via a
40+
2-char justification code \ that refers to the (invisible)
41+
projected map bounding box, **n**\ *xn*/*yn* for normalized (0-1)
42+
bounding box coordinates, or **x**\ *x*/*y* for plot
43+
coordinates (inches, cm, points, append unit).
44+
All but **x** requires both ``region`` and ``projection`` to be
45+
specified. You can offset the reference point via
46+
**+o**\ *dx*/*dy* in the direction implied by *code* or
47+
**+j**\ *justify*.
48+
49+
Alternatively, Give *west/east/south/north* of geographic
50+
rectangle bounded by parallels and meridians; append **+r** if the
51+
coordinates instead are the lower left and upper right corners of
52+
the desired rectangle. (Or, give *xmin/xmax/ymin/ymax* of bounding
53+
rectangle in projected coordinates and optionally
54+
append **+u**\ *unit* [Default coordinate unit is meter (e)].
55+
56+
Append **+w**\ *width*\ [/*height*] of bounding rectangle or box
57+
in plot coordinates (inches, cm, etc.). By default, the anchor
58+
point on the scale is assumed to be the bottom left corner (BL),
59+
but this can be changed by appending **+j** followed by a 2-char
60+
justification code *justify*.
61+
**Note**: If **j** is used then *justify* defaults to the same
62+
as *refpoint*, if **J** is used then *justify* defaults to the
63+
mirror opposite of *refpoint*. Specify inset box attributes via
64+
the ``box`` option [outline only].
65+
box : str or bool
66+
[**+c**\ *clearances*][**+g**\ *fill*][**+i**\ [[*gap*/]\
67+
*pen*]][**+p**\ [*pen*]][**+r**\ [*radius*]][**+s**\
68+
[[*dx*/*dy*/][*shade*]]]
69+
70+
If passed ``True``, this draws a rectangular box around the map
71+
inset using the default pen; specify a different pen
72+
with **+p**\ *pen*. Add **+g**\ *fill* to fill the logo box
73+
[Default is no fill].
74+
Append **+c**\ *clearance* where *clearance* is either
75+
*gap*, *xgap*\ /\ *ygap*, or *lgap*\ /\ *rgap*\ /\ *bgap*\ /\
76+
*tgap* where these items are uniform, separate in x- and
77+
y-direction, or individual side spacings between logo and border.
78+
Append **+i** to draw a secondary, inner border as well. We use a
79+
uniform *gap* between borders of 2\ **p** and the default pen
80+
unless other values are specified. Append **+r** to draw rounded
81+
rectangular borders instead, with a 6\ **p** corner radius. You
82+
can override this radius by appending another value. Append
83+
**+s** to draw an offset background shaded region. Here, *dx*/*dy*
84+
indicates the shift relative to the foreground frame
85+
[4\ **p**/-4\ **p**] and *shade* sets the fill style to use for
86+
shading [Default is gray50].
87+
margin : int or str or list
88+
This is clearance that is added around the inside of the inset.
89+
Plotting will take place within the inner region only. The margins
90+
can be a single value, a pair of values separated (for setting
91+
separate horizontal and vertical margins), or the full set of four
92+
margins (for setting separate left, right, bottom, and top
93+
margins). When passing multiple values, it can be either a list or
94+
a string with the values separated by forward
95+
slashes [Default is no margins].
96+
no_clip : bool
97+
Do NOT clip features extruding outside map inset boundaries [Default
98+
will clip].
99+
{V}
100+
101+
Examples
102+
--------
103+
>>> import pygmt
104+
>>>
105+
>>> # Create the larger figure
106+
>>> fig = pygmt.Figure()
107+
>>> fig.coast(region="MG+r2", water="lightblue", shorelines="thin")
108+
>>> # Use a "with" statement to initialize the inset context manager
109+
>>> # Setting the position to top left and a width of 3.5 centimeters
110+
>>> with fig.inset(position="jTL+w3.5c+o0.2c", margin=0, box="+pgreen"):
111+
... # Map elements under the "with" statement are plotted in the inset
112+
... fig.coast(
113+
... region="g",
114+
... projection="G47/-20/3.5c",
115+
... land="gray",
116+
... water="white",
117+
... dcw="MG+gred",
118+
... )
119+
...
120+
>>> # Map elements outside the "with" block are plotted in the main figure
121+
>>> fig.logo(position="jBR+o0.2c+w3c")
122+
>>> fig.show()
123+
<IPython.core.display.Image object>
124+
"""
125+
kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access
126+
with Session() as lib:
127+
try:
128+
lib.call_module("inset", f"begin {build_arg_string(kwargs)}")
129+
yield
130+
finally:
131+
v_arg = build_arg_string({"V": kwargs.get("V")})
132+
lib.call_module("inset", f"end {v_arg}".strip())

pygmt/tests/test_inset.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
Tests for the inset function.
3+
"""
4+
from pygmt import Figure
5+
from pygmt.helpers.testing import check_figures_equal
6+
7+
8+
@check_figures_equal()
9+
def test_inset_aliases():
10+
"""
11+
Test the aliases for the inset function.
12+
"""
13+
fig_ref, fig_test = Figure(), Figure()
14+
fig_ref.basemap(R="MG+r2", B="afg")
15+
with fig_ref.inset(D="jTL+w3.5c+o0.2c", M=0, F="+pgreen"):
16+
fig_ref.basemap(R="g", J="G47/-20/4c", B="afg")
17+
18+
fig_test.basemap(region="MG+r2", frame="afg")
19+
with fig_test.inset(position="jTL+w3.5c+o0.2c", margin=0, box="+pgreen"):
20+
fig_test.basemap(region="g", projection="G47/-20/4c", frame="afg")
21+
return fig_ref, fig_test
22+
23+
24+
@check_figures_equal()
25+
def test_inset_context_manager():
26+
"""
27+
Test that the inset context manager works and, once closed, plotting
28+
elements are added to the larger figure.
29+
"""
30+
fig_ref, fig_test = Figure(), Figure()
31+
32+
fig_ref.basemap(region=[-74, -69.5, 41, 43], projection="M9c", frame=True)
33+
fig_ref.basemap(rose="jTR+w3c") # Pass rose argument with basemap before the inset
34+
with fig_ref.inset(position="jBL+w3c+o0.2c", margin=0, box="+pblack"):
35+
fig_ref.basemap(region=[-80, -65, 35, 50], projection="M3c", frame="afg")
36+
37+
fig_test.basemap(region=[-74, -69.5, 41, 43], projection="M9c", frame=True)
38+
with fig_test.inset(position="jBL+w3c+o0.2c", margin=0, box="+pblack"):
39+
fig_test.basemap(region=[-80, -65, 35, 50], projection="M3c", frame="afg")
40+
fig_test.basemap(rose="jTR+w3c") # Pass rose argument with basemap after the inset
41+
42+
return fig_ref, fig_test

0 commit comments

Comments
 (0)