diff --git a/doc/api/index.rst b/doc/api/index.rst index 264f5a9175a..317f7368095 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -27,6 +27,7 @@ Plotting map elements Figure.basemap Figure.coast Figure.colorbar + Figure.directional_rose Figure.hlines Figure.inset Figure.legend diff --git a/pygmt/figure.py b/pygmt/figure.py index 56ad2c3d5cf..b7e49b6132f 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -413,6 +413,7 @@ def _repr_html_(self) -> str: coast, colorbar, contour, + directional_rose, grdcontour, grdimage, grdview, diff --git a/pygmt/src/__init__.py b/pygmt/src/__init__.py index 8905124f917..1e34c3ba669 100644 --- a/pygmt/src/__init__.py +++ b/pygmt/src/__init__.py @@ -10,6 +10,7 @@ from pygmt.src.config import config from pygmt.src.contour import contour from pygmt.src.dimfilter import dimfilter +from pygmt.src.directional_rose import directional_rose from pygmt.src.filter1d import filter1d from pygmt.src.grd2cpt import grd2cpt from pygmt.src.grd2xyz import grd2xyz diff --git a/pygmt/src/directional_rose.py b/pygmt/src/directional_rose.py new file mode 100644 index 00000000000..3689a204f3a --- /dev/null +++ b/pygmt/src/directional_rose.py @@ -0,0 +1,90 @@ +""" +directional_rose - Add a map directional rose. +""" + +from collections.abc import Sequence +from typing import Literal + +from pygmt.alias import Alias, AliasSystem +from pygmt.clib import Session +from pygmt.exceptions import GMTInvalidInput +from pygmt.helpers import build_arg_list +from pygmt.params import Box, Position + + +def directional_rose( + self, + position: Position | None = None, + width: float | str | None = None, + label: Sequence[str] | bool = False, + fancy: Literal[1, 2, 3] | bool = False, + box: Box | bool = False, + perspective: str | bool = False, + verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"] + | bool = False, + transparency: float | None = None, +): + """ + Add a directional rose on the map. + + Parameters + ---------- + position + Specify the position of the directional rose on a map. See + :class:`pygmt.params.Position` for details. + width + Width of the rose in plot coordinates (append **i** (inch), **cm** + (centimeters), or **p** (points)), or append % for a size in percentage of map + width [Default is 10 %]. + label + A sequence of four strings to label the cardinal points W,E,S,N. Use an empty + string to skip a specific label. If set to ``True``, use the default labels + ``["W", "E", "S", "N"]``. + fancy + Get a fancy rose. The fanciness level can be set to 1, 2, or 3: + + - Level 1 draws the two principal E-W, N-S orientations + - Level 2 adds the two intermediate NW-SE and NE-SW orientations + - Level 3 adds the four minor orientations WNW-ESE, NNW-SSE, NNE-SSW, and + ENE-WSW + + If set to ``True``, defaults to level 1. + box + Draw a background box behind the directional rose. If set to ``True``, a simple + rectangular box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box + appearance, pass a :class:`pygmt.params.Box` object to control style, fill, pen, + and other box properties. + $perspective + $verbose + $transparency + + Examples + -------- + >>> import pygmt + >>> fig = pygmt.Figure() + >>> fig.basemap(region=[0, 80, -30, 30], projection="M10c", frame=True) + >>> fig.directional_rose(position=Position((10, 10), cstype="mapcoords")) + >>> fig.show() + """ + self._activate_figure() + + if position is None: + msg = "Parameter 'position' is required." + raise GMTInvalidInput(msg) + + aliasdict = AliasSystem( + F=Alias(box, name="box"), + Td=[ + Alias(position, name="position"), + Alias(width, name="width", prefix="+w"), + Alias(fancy, name="fancy", prefix="+f"), # +F is not supported yet. + Alias(label, name="label", prefix="+l", sep=",", size=4), + ], + ).add_common( + V=verbose, + p=perspective, + t=transparency, + ) + + with Session() as lib: + lib.call_module(module="basemap", args=build_arg_list(aliasdict)) diff --git a/pygmt/tests/baseline/test_directional_rose_complex.png.dvc b/pygmt/tests/baseline/test_directional_rose_complex.png.dvc new file mode 100644 index 00000000000..6cf64af3cf6 --- /dev/null +++ b/pygmt/tests/baseline/test_directional_rose_complex.png.dvc @@ -0,0 +1,5 @@ +outs: +- md5: 4bf9dc9a74af3df5fe03ba4cdfaf27ca + size: 14424 + hash: md5 + path: test_directional_rose_complex.png diff --git a/pygmt/tests/test_directional_rose.py b/pygmt/tests/test_directional_rose.py new file mode 100644 index 00000000000..820d119cbd8 --- /dev/null +++ b/pygmt/tests/test_directional_rose.py @@ -0,0 +1,34 @@ +""" +Test Figure.directional_rose. +""" + +import pytest +from pygmt import Figure +from pygmt.params import Position + + +@pytest.mark.mpl_image_compare(filename="test_basemap_rose.png") +def test_directional_rose(): + """ + Test the Figure.directional_rose method. + """ + fig = Figure() + fig.basemap(region=[127.5, 128.5, 26, 27], projection="H15c", frame=True) + fig.directional_rose(position=Position("MC"), width="5c") + return fig + + +@pytest.mark.mpl_image_compare +def test_directional_rose_complex(): + """ + Test the Figure.directional_rose method with more parameters. + """ + fig = Figure() + fig.basemap(region=[0, 80, -30, 30], projection="M10c", frame=True) + fig.directional_rose( + position=Position((50, 0), cstype="mapcoords", anchor="MC", offset=(1, 1)), + width="1c", + label=["", "", "", "N"], + fancy=2, + ) + return fig diff --git a/pygmt/tests/test_inset.py b/pygmt/tests/test_inset.py index ca5b411fe68..7a77ecc3818 100644 --- a/pygmt/tests/test_inset.py +++ b/pygmt/tests/test_inset.py @@ -30,5 +30,6 @@ def test_inset_context_manager(): fig.basemap(region=[-74, -69.5, 41, 43], projection="M9c", frame=True) with fig.inset(position="jBL+w3c+o0.2c", margin=0, box=Box(pen="black")): fig.basemap(region=[-80, -65, 35, 50], projection="M3c", frame="afg") - fig.basemap(rose="jTR+w3c") # Pass rose argument with basemap after the inset + # Plot an rose after the inset + fig.directional_rose(position="TR", position_type="inside", width="3c") return fig