Skip to content

Commit ff90b2e

Browse files
committed
Figure.legend: Refactor to simplify the logic of checking legend specification
1 parent 2bb65df commit ff90b2e

File tree

2 files changed

+65
-52
lines changed

2 files changed

+65
-52
lines changed

pygmt/src/legend.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
legend - Plot a legend.
33
"""
44

5+
import pathlib
6+
57
from pygmt.clib import Session
68
from pygmt.exceptions import GMTInvalidInput
79
from pygmt.helpers import (
@@ -26,7 +28,13 @@
2628
t="transparency",
2729
)
2830
@kwargs_to_strings(R="sequence", c="sequence_comma", p="sequence")
29-
def legend(self, spec=None, position="JTR+jTR+o0.2c", box="+gwhite+p1p", **kwargs):
31+
def legend(
32+
self,
33+
spec: str | pathlib.PurePath | None = None,
34+
position="JTR+jTR+o0.2c",
35+
box="+gwhite+p1p",
36+
**kwargs,
37+
):
3038
r"""
3139
Plot legends on maps.
3240
@@ -42,10 +50,14 @@ def legend(self, spec=None, position="JTR+jTR+o0.2c", box="+gwhite+p1p", **kwarg
4250
4351
Parameters
4452
----------
45-
spec : None or str
46-
Either ``None`` [Default] for using the automatically generated legend
47-
specification file, or a *filename* pointing to the legend
48-
specification file.
53+
spec
54+
The legend specification. It can be:
55+
56+
- ``None`` means using the automatically generated legend specification file.
57+
- A string or a :class:`pathlib.PurePath` object pointing to the legend
58+
specification file.
59+
60+
See :gmt-docs:`legend.html` for the definition of the legend specification.
4961
{projection}
5062
{region}
5163
position : str
@@ -75,12 +87,11 @@ def legend(self, spec=None, position="JTR+jTR+o0.2c", box="+gwhite+p1p", **kwarg
7587
if kwargs.get("F") is None:
7688
kwargs["F"] = box
7789

90+
kind = data_kind(spec)
91+
if kind not in {"vectors", "file"}: # kind="vectors" means spec is None
92+
raise GMTInvalidInput(f"Unrecognized data type: {type(spec)}")
93+
if kind == "file" and is_nonstr_iter(spec):
94+
raise GMTInvalidInput("Only one legend specification file is allowed.")
95+
7896
with Session() as lib:
79-
if spec is None:
80-
specfile = ""
81-
elif data_kind(spec) == "file" and not is_nonstr_iter(spec):
82-
# Is a file but not a list of files
83-
specfile = spec
84-
else:
85-
raise GMTInvalidInput(f"Unrecognized data type: {type(spec)}")
86-
lib.call_module(module="legend", args=build_arg_list(kwargs, infile=specfile))
97+
lib.call_module(module="legend", args=build_arg_list(kwargs, infile=spec))

pygmt/tests/test_legend.py

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,44 @@
1010
from pygmt.helpers import GMTTempFile
1111

1212

13+
@pytest.fixture(scope="module", name="legend_spec")
14+
def fixture_legend_spec():
15+
"""
16+
A string contains a legend specification.
17+
"""
18+
return """
19+
G -0.1i
20+
H 24 Times-Roman My Map Legend
21+
D 0.2i 1p
22+
N 2
23+
V 0 1p
24+
S 0.1i c 0.15i p300/12 0.25p 0.3i This circle is hachured
25+
S 0.1i e 0.15i yellow 0.25p 0.3i This ellipse is yellow
26+
S 0.1i w 0.15i green 0.25p 0.3i This wedge is green
27+
S 0.1i f0.1i+l+t 0.25i blue 0.25p 0.3i This is a fault
28+
S 0.1i - 0.15i - 0.25p,- 0.3i A dashed contour
29+
S 0.1i v0.1i+a40+e 0.25i magenta 0.25p 0.3i This is a vector
30+
S 0.1i i 0.15i cyan 0.25p 0.3i This triangle is boring
31+
V 0 1p
32+
D 0.2i 1p
33+
N 1
34+
G 0.05i
35+
G 0.05i
36+
G 0.05i
37+
L 9 4 R Smith et al., @%5%J. Geophys. Res., 99@%%, 2000
38+
G 0.1i
39+
P
40+
T Let us just try some simple text that can go on a few lines.
41+
T There is no easy way to predetermine how many lines will be required,
42+
T so we may have to adjust the box height to get the right size box.
43+
"""
44+
45+
1346
@pytest.mark.mpl_image_compare
1447
def test_legend_position():
1548
"""
16-
Test that plots a position with each of the four legend coordinate systems.
49+
Test positioning the legend with different coordinate systems.
1750
"""
18-
1951
fig = Figure()
2052
fig.basemap(region=[-2, 2, -2, 2], frame=True)
2153
positions = ["jTR+jTR", "g0/1", "n0.2/0.2", "x4i/2i/2i"]
@@ -30,22 +62,18 @@ def test_legend_default_position():
3062
"""
3163
Test using the default legend position.
3264
"""
33-
3465
fig = Figure()
35-
3666
fig.basemap(region=[-1, 1, -1, 1], frame=True)
37-
3867
fig.plot(x=[0], y=[0], style="p10p", label="Default")
3968
fig.legend()
40-
4169
return fig
4270

4371

4472
@pytest.mark.benchmark
4573
@pytest.mark.mpl_image_compare
4674
def test_legend_entries():
4775
"""
48-
Test different marker types/shapes.
76+
Test legend using the automatically generated legend entries.
4977
"""
5078
fig = Figure()
5179
fig.basemap(projection="x1i", region=[0, 7, 3, 7], frame=True)
@@ -59,45 +87,16 @@ def test_legend_entries():
5987
fig.plot(data="@Table_5_11.txt", pen="1.5p,gray", label="My lines")
6088
fig.plot(data="@Table_5_11.txt", style="t0.15i", fill="orange", label="Oranges")
6189
fig.legend(position="JTR+jTR")
62-
6390
return fig
6491

6592

6693
@pytest.mark.mpl_image_compare
67-
def test_legend_specfile():
94+
def test_legend_specfile(legend_spec):
6895
"""
69-
Test specfile functionality.
96+
Test passing a legend specification file.
7097
"""
71-
72-
specfile_contents = """
73-
G -0.1i
74-
H 24 Times-Roman My Map Legend
75-
D 0.2i 1p
76-
N 2
77-
V 0 1p
78-
S 0.1i c 0.15i p300/12 0.25p 0.3i This circle is hachured
79-
S 0.1i e 0.15i yellow 0.25p 0.3i This ellipse is yellow
80-
S 0.1i w 0.15i green 0.25p 0.3i This wedge is green
81-
S 0.1i f0.1i+l+t 0.25i blue 0.25p 0.3i This is a fault
82-
S 0.1i - 0.15i - 0.25p,- 0.3i A dashed contour
83-
S 0.1i v0.1i+a40+e 0.25i magenta 0.25p 0.3i This is a vector
84-
S 0.1i i 0.15i cyan 0.25p 0.3i This triangle is boring
85-
V 0 1p
86-
D 0.2i 1p
87-
N 1
88-
G 0.05i
89-
G 0.05i
90-
G 0.05i
91-
L 9 4 R Smith et al., @%5%J. Geophys. Res., 99@%%, 2000
92-
G 0.1i
93-
P
94-
T Let us just try some simple text that can go on a few lines.
95-
T There is no easy way to predetermine how many lines will be required,
96-
T so we may have to adjust the box height to get the right size box.
97-
"""
98-
9998
with GMTTempFile() as specfile:
100-
Path(specfile.name).write_text(specfile_contents, encoding="utf-8")
99+
Path(specfile.name).write_text(legend_spec, encoding="utf-8")
101100
fig = Figure()
102101
fig.basemap(projection="x6i", region=[0, 1, 0, 1], frame=True)
103102
fig.legend(specfile.name, position="JTM+jCM+w5i")
@@ -111,3 +110,6 @@ def test_legend_fails():
111110
fig = Figure()
112111
with pytest.raises(GMTInvalidInput):
113112
fig.legend(spec=["@Table_5_11.txt"])
113+
114+
with pytest.raises(GMTInvalidInput):
115+
fig.legend(spec=[1, 2])

0 commit comments

Comments
 (0)