Skip to content

Commit c3954bf

Browse files
committed
Add Figure.directional_rose for adding directional rose on maps
1 parent 626a734 commit c3954bf

File tree

8 files changed

+182
-1
lines changed

8 files changed

+182
-1
lines changed

doc/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Plotting map elements
2727
Figure.basemap
2828
Figure.coast
2929
Figure.colorbar
30+
Figure.directional_rose
3031
Figure.hlines
3132
Figure.inset
3233
Figure.legend

pygmt/figure.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ def _repr_html_(self) -> str:
413413
coast,
414414
colorbar,
415415
contour,
416+
directional_rose,
416417
grdcontour,
417418
grdimage,
418419
grdview,

pygmt/src/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pygmt.src.config import config
1111
from pygmt.src.contour import contour
1212
from pygmt.src.dimfilter import dimfilter
13+
from pygmt.src.directional_rose import directional_rose
1314
from pygmt.src.filter1d import filter1d
1415
from pygmt.src.grd2cpt import grd2cpt
1516
from pygmt.src.grd2xyz import grd2xyz

pygmt/src/directional_rose.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"""
2+
directional_rose - Add a map directional rose.
3+
"""
4+
5+
from collections.abc import Sequence
6+
from typing import Literal
7+
8+
from pygmt._typing import AnchorCode
9+
from pygmt.alias import Alias, AliasSystem
10+
from pygmt.clib import Session
11+
from pygmt.exceptions import GMTInvalidInput
12+
from pygmt.helpers import build_arg_list, fmt_docstring
13+
from pygmt.params import Box, Position
14+
from pygmt.src._common import _parse_position
15+
16+
17+
@fmt_docstring
18+
def directional_rose(
19+
self,
20+
position: Position | Sequence[float | str] | AnchorCode | None = None,
21+
width: float | str | None = None,
22+
labels: Sequence[str] | bool = False,
23+
fancy: Literal[1, 2, 3] | bool = False,
24+
box: Box | bool = False,
25+
verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"]
26+
| bool = False,
27+
panel: int | Sequence[int] | bool = False,
28+
perspective: str | bool = False,
29+
transparency: float | None = None,
30+
):
31+
"""
32+
Add a directional rose on the map.
33+
34+
Parameters
35+
----------
36+
position
37+
Position of the directional rose on the plot. It can be specified in multiple
38+
ways:
39+
40+
- A :class:`pygmt.params.Position` object to fully control the reference point,
41+
anchor point, and offset.
42+
- A sequence of two values representing the x and y coordinates in plot
43+
coordinates, e.g., ``(1, 2)`` or ``("1c", "2c")``.
44+
- A :doc:`2-character justification code </techref/justification_codes>` for a
45+
position inside the plot, e.g., ``"TL"`` for Top Left corner inside the plot.
46+
47+
If not specified, defaults to the lower-left corner of the plot.
48+
width
49+
Width of the rose in plot coordinates, or append % for a size in percentage of
50+
map width [Default is 10%].
51+
labels
52+
A sequence of four strings to label the cardinal points W,E,S,N. Use an empty
53+
string to skip a specific label. If set to ``True``, the default labels
54+
``["W", "E", "S", "N"]`` are used.
55+
fancy
56+
Get a fancy rose. The fanciness level can be set to 1, 2, or 3:
57+
58+
- Level 1 draws the two principal E-W, N-S orientations
59+
- Level 2 adds the two intermediate NW-SE and NE-SW orientations
60+
- Level 3 adds the four minor orientations WNW-ESE, NNW-SSE, NNE-SSW, and
61+
ENE-WSW
62+
63+
If set to ``True``, defaults to level 1.
64+
box
65+
Draw a background box behind the directional rose. If set to ``True``, a simple
66+
rectangular box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box
67+
appearance, pass a :class:`pygmt.params.Box` object to control style, fill, pen,
68+
and other box properties.
69+
$verbose
70+
$panel
71+
$perspective
72+
$transparency
73+
74+
Examples
75+
--------
76+
>>> import pygmt
77+
>>> fig = pygmt.Figure()
78+
>>> fig.basemap(region=[0, 80, -30, 30], projection="M10c", frame=True)
79+
>>> fig.directional_rose(position=Position((10, 10), cstype="mapcoords"))
80+
>>> fig.show()
81+
"""
82+
self._activate_figure()
83+
84+
position = _parse_position(
85+
position,
86+
kwdict={"width": width, "fancy": fancy, "labels": labels},
87+
default=Position((0, 0), cstype="plotcoords"), # Default to (0,0) in plotcoords
88+
)
89+
90+
# width is mandatory.
91+
if width is None and not isinstance(position, str):
92+
msg = "Parameter 'width' is required."
93+
raise GMTInvalidInput(msg)
94+
95+
aliasdict = AliasSystem(
96+
F=Alias(box, name="box"),
97+
Td=[
98+
Alias(position, name="position"),
99+
Alias(width, name="width", prefix="+w"),
100+
Alias(fancy, name="fancy", prefix="+f"), # +F is not supported yet.
101+
Alias(labels, name="labels", prefix="+l", sep=",", size=4),
102+
],
103+
).add_common(
104+
V=verbose,
105+
c=panel,
106+
p=perspective,
107+
t=transparency,
108+
)
109+
110+
with Session() as lib:
111+
lib.call_module(module="basemap", args=build_arg_list(aliasdict))
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
outs:
2+
- md5: 4bf9dc9a74af3df5fe03ba4cdfaf27ca
3+
size: 14424
4+
hash: md5
5+
path: test_directional_rose_complex.png
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
outs:
2+
- md5: 4d113eabadf8acafed3d8a73da7f4b7e
3+
size: 13902
4+
hash: md5
5+
path: test_directional_rose_default_position.png
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""
2+
Test Figure.directional_rose.
3+
"""
4+
5+
import pytest
6+
from pygmt import Figure
7+
from pygmt.exceptions import GMTInvalidInput
8+
from pygmt.params import Position
9+
10+
11+
@pytest.mark.mpl_image_compare(filename="test_basemap_rose.png")
12+
def test_directional_rose():
13+
"""
14+
Test the Figure.directional_rose method.
15+
"""
16+
fig = Figure()
17+
fig.basemap(region=[127.5, 128.5, 26, 27], projection="H15c", frame=True)
18+
fig.directional_rose(position="MC", width="5c")
19+
return fig
20+
21+
22+
@pytest.mark.mpl_image_compare
23+
def test_directional_rose_default_position():
24+
"""
25+
Test the Figure.directional_rose method with default position.
26+
"""
27+
fig = Figure()
28+
fig.basemap(region=[0, 80, -30, 30], projection="M10c", frame=True)
29+
fig.directional_rose(width="3c")
30+
return fig
31+
32+
33+
@pytest.mark.mpl_image_compare
34+
def test_directional_rose_complex():
35+
"""
36+
Test the Figure.directional_rose method with more parameters.
37+
"""
38+
fig = Figure()
39+
fig.basemap(region=[0, 80, -30, 30], projection="M10c", frame=True)
40+
fig.directional_rose(
41+
position=Position((50, 0), cstype="mapcoords", anchor="MC", offset=(1, 1)),
42+
width="1c",
43+
labels=["", "", "", "N"],
44+
fancy=2,
45+
)
46+
return fig
47+
48+
49+
def test_directional_rose_no_width():
50+
"""
51+
Test the Figure.directional_rose method without width parameter.
52+
"""
53+
fig = Figure()
54+
fig.basemap(region=[0, 80, -30, 30], projection="M10c", frame=True)
55+
with pytest.raises(GMTInvalidInput):
56+
fig.directional_rose()

pygmt/tests/test_inset.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ def test_inset_context_manager():
3030
fig.basemap(region=[-74, -69.5, 41, 43], projection="M9c", frame=True)
3131
with fig.inset(position="jBL+w3c+o0.2c", clearance=0, box=Box(pen="black")):
3232
fig.basemap(region=[-80, -65, 35, 50], projection="M3c", frame="afg")
33-
fig.basemap(rose="jTR+w3c") # Pass rose argument with basemap after the inset
33+
# Plot an rose after the inset
34+
fig.directional_rose(position="TR", width="3c")
3435
return fig

0 commit comments

Comments
 (0)