Skip to content

Commit

Permalink
Wrap legend (#333)
Browse files Browse the repository at this point in the history
Basic first implementation of legend wrapper. Uses GMT's auto-legend feature. Aliased for position (D), box (F) for `legend` and also label (l) for `plot`.
  • Loading branch information
liamtoney authored and weiji14 committed Oct 31, 2019
1 parent b8bbcfd commit 13dfb56
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Plotting data and laying out the map:
Figure.contour
Figure.grdcontour
Figure.grdimage
Figure.legend
Figure.logo
Figure.image
Figure.shift_origin
Expand Down
46 changes: 46 additions & 0 deletions pygmt/base_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ def grdcontour(self, grid, **kwargs):
Do not draw contours with less than `cut` number of points.
S : string or int
Resample smoothing factor.
l : str
Add a legend entry for the symbol or line being plotted.
{J}
{R}
{B}
Expand Down Expand Up @@ -241,6 +243,7 @@ def grdimage(self, grid, **kwargs):
G="color",
W="pen",
i="columns",
l="label",
C="cmap",
)
@kwargs_to_strings(R="sequence", i="sequence_comma")
Expand Down Expand Up @@ -530,3 +533,46 @@ def image(self, imagefile, **kwargs):
with Session() as lib:
arg_str = " ".join([imagefile, build_arg_string(kwargs)])
lib.call_module("image", arg_str)

@fmt_docstring
@use_alias(R="region", J="projection", D="position", F="box")
@kwargs_to_strings(R="sequence")
def legend(self, spec=None, **kwargs):
"""
Plot legends on maps.
Makes legends that can be overlaid on maps. Reads specific legend-related
information from either a) an input file or b) a list containing a list
of figure handles and a list of corresponding labels. Unless otherwise
noted, annotations will be made using the primary annotation font and
size in effect (i.e., FONT_ANNOT_PRIMARY).
Full option list at :gmt-docs:`legend.html`
{aliases}
Parameters
----------
spec : None or str
Either None (default) for using the automatically generated legend
specification file, or a filename pointing to the legend specification file.
{J}
{R}
position (D) : str
``'[g|j|J|n|x]refpoint+wwidth[/height][+jjustify][+lspacing][+odx[/dy]]'``
Defines the reference point on the map for the legend.
box (F) : bool or str
``'[+cclearances][+gfill][+i[[gap/]pen]][+p[pen]][+r[radius]][+s[[dx/dy/][shade]]]'``
Without further options, draws a rectangular border around the
legend using **MAP_FRAME_PEN**.
"""
kwargs = self._preprocess(**kwargs)
with Session() as lib:
if spec is None:
specfile = ""
elif data_kind(spec) == "file":
specfile = spec
else:
raise GMTInvalidInput("Unrecognized data type: {}".format(type(spec)))
arg_str = " ".join([specfile, build_arg_string(kwargs)])
lib.call_module("legend", arg_str)
Binary file added pygmt/tests/baseline/test_legend_entries.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pygmt/tests/baseline/test_legend_position.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pygmt/tests/baseline/test_legend_specfile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 108 additions & 0 deletions pygmt/tests/test_legend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Tests for legend
"""
import pytest

from .. import Figure
from ..exceptions import GMTInvalidInput
from ..helpers import GMTTempFile


@pytest.mark.mpl_image_compare
def test_legend_position():
"""
Try positioning with each of the four legend coordinate systems.
"""

fig = Figure()

fig.basemap(region=[-2, 2, -2, 2], frame=True)

positions = ["jTR+jTR", "g0/1", "n0.2/0.2", "x4i/2i/2i"]

for i, position in enumerate(positions):

fig.plot(x=[0], y=[0], style="p10p", label=i)
fig.legend(position=position, box=True)

return fig


@pytest.mark.mpl_image_compare
def test_legend_entries():
"""
Test different marker types/shapes.
"""

fig = Figure()

fig.basemap(projection="x1i", region=[0, 7, 3, 7], frame=True)

fig.plot(
data="@Table_5_11.txt",
style="c0.15i",
color="lightgreen",
pen="faint",
l="Apples",
)
fig.plot(data="@Table_5_11.txt", pen="1.5p,gray", l='"My lines"')
fig.plot(data="@Table_5_11.txt", style="t0.15i", color="orange", l="Oranges")

fig.legend(position="JTR+jTR")

return fig


@pytest.mark.mpl_image_compare
def test_legend_specfile():
"""
Test specfile functionality.
"""

specfile_contents = """
G -0.1i
H 24 Times-Roman My Map Legend
D 0.2i 1p
N 2
V 0 1p
S 0.1i c 0.15i p300/12 0.25p 0.3i This circle is hachured
S 0.1i e 0.15i yellow 0.25p 0.3i This ellipse is yellow
S 0.1i w 0.15i green 0.25p 0.3i This wedge is green
S 0.1i f0.1i+l+t 0.25i blue 0.25p 0.3i This is a fault
S 0.1i - 0.15i - 0.25p,- 0.3i A dashed contour
S 0.1i v0.1i+a40+e 0.25i magenta 0.25p 0.3i This is a vector
S 0.1i i 0.15i cyan 0.25p 0.3i This triangle is boring
V 0 1p
D 0.2i 1p
N 1
G 0.05i
G 0.05i
G 0.05i
L 9 4 R Smith et al., @%5%J. Geophys. Res., 99@%%, 2000
G 0.1i
P
T Let us just try some simple text that can go on a few lines.
T There is no easy way to predetermine how many lines will be required,
T so we may have to adjust the box height to get the right size box.
"""

with GMTTempFile() as specfile:

with open(specfile.name, "w") as file:
file.write(specfile_contents)

fig = Figure()

fig.basemap(projection="x6i", region=[0, 1, 0, 1], frame=True)
fig.legend(specfile.name, position="JTM+jCM+w5i")

return fig


def test_legend_fails():
"""
Test legend fails with invalid spec
"""
fig = Figure()
with pytest.raises(GMTInvalidInput):
fig.legend(spec=["@Table_5_11.txt"])

0 comments on commit 13dfb56

Please sign in to comment.