Skip to content

Commit 98d0a0d

Browse files
committed
Add Figure.magnetic_rose to plot a magnetic rose on map
1 parent 3dd7379 commit 98d0a0d

File tree

7 files changed

+210
-0
lines changed

7 files changed

+210
-0
lines changed

doc/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Plotting map elements
3131
Figure.inset
3232
Figure.legend
3333
Figure.logo
34+
Figure.magnetic_rose
3435
Figure.solar
3536
Figure.text
3637
Figure.timestamp

pygmt/figure.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ def _repr_html_(self) -> str:
422422
inset,
423423
legend,
424424
logo,
425+
magnetic_rose,
425426
meca,
426427
plot,
427428
plot3d,

pygmt/src/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from pygmt.src.inset import inset
3636
from pygmt.src.legend import legend
3737
from pygmt.src.logo import logo
38+
from pygmt.src.magnetic_rose import magnetic_rose
3839
from pygmt.src.makecpt import makecpt
3940
from pygmt.src.meca import meca
4041
from pygmt.src.nearneighbor import nearneighbor

pygmt/src/magnetic_rose.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
"""
2+
magnetic_rose - Add a map magnetic 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 magnetic_rose( # noqa: PLR0913
19+
self,
20+
position: Position | Sequence[float | str] | AnchorCode | None = None,
21+
width: float | str | None = None,
22+
labels: Sequence[str] | bool = False,
23+
outer_pen: str | bool = False,
24+
inner_pen: str | bool = False,
25+
declination: float | None = None,
26+
declination_label: str | None = None,
27+
intervals: Sequence[float] | None = None,
28+
box: Box | bool = False,
29+
verbose: Literal["quiet", "error", "warning", "timing", "info", "compat", "debug"]
30+
| bool = False,
31+
panel: int | Sequence[int] | bool = False,
32+
perspective: str | bool = False,
33+
transparency: float | None = None,
34+
):
35+
"""
36+
Add a magnetic rose to the map.
37+
38+
Parameters
39+
----------
40+
position
41+
Position of the magnetic rose on the plot. It can be specified in multiple ways:
42+
43+
- A :class:`pygmt.params.Position` object to fully control the reference point,
44+
anchor point, and offset.
45+
- A sequence of two values representing the x and y coordinates in plot
46+
coordinates, e.g., ``(1, 2)`` or ``("1c", "2c")``.
47+
- A :doc:`2-character justification code </techref/justification_codes>` for a
48+
position inside the plot, e.g., ``"TL"`` for Top Left corner inside the plot.
49+
50+
If not specified, defaults to the bottom-left corner of the plot (position
51+
``(0, 0)`` with anchor ``"BL"``).
52+
width
53+
Width of the rose in plot coordinates, or append unit ``%`` for a size in
54+
percentage of plot width [Default is 15%].
55+
labels
56+
A sequence of four strings to label the cardinal points W, E, S, N. Use an empty
57+
string to skip a specific label. If the north label is ``"*"``, then a north
58+
star is plotted instead of the north label. If set to ``True``, use the default
59+
labels ``["W", "E", "S", "N"]``.
60+
outer_pen
61+
Draw the outer circle of the magnetic rose, using the given pen attributes.
62+
inner_pen
63+
Draw the inner circle of the magnetic rose, using the given pen attributes.
64+
declination
65+
Magnetic declination in degrees. By default, only a geographic north is plotted.
66+
With this parameter set, a magnetic north is also plotted. A magnetic compass
67+
needle is drawn inside the rose to indicate the direction to magnetic north.
68+
declination_label
69+
Label for the magnetic compass needle. Default is to format a label based on
70+
``declination``. To bypass the label, set to ``"-"``.
71+
intervals
72+
Specify the annotation and tick intervals for the geographic and magnetic
73+
directions. It can be a sequence of three or six values. If three values are
74+
given, they are used for both geographic and magnetic directions. If six values
75+
are given, the first three are used for geographic directions and the last three
76+
for magnetic directions. [Default is ``(30, 5, 1)``].
77+
**Note**: If :gmt-term:`MAP_EMBELLISHMENT_MODE` is ``"auto"`` and the compass
78+
size is smaller than 2.5 cm then the interval defaults are reset to
79+
``(90,30, 3, 45, 15, 3)``.
80+
box
81+
Draw a background box behind the magnetic rose. If set to ``True``, a simple
82+
rectangular box is drawn using :gmt-term:`MAP_FRAME_PEN`. To customize the box
83+
appearance, pass a :class:`pygmt.params.Box` object to control style, fill, pen,
84+
and other box properties.
85+
$perspective
86+
$verbose
87+
$transparency
88+
89+
Examples
90+
--------
91+
>>> import pygmt
92+
>>> from pygmt.params import Position
93+
>>> fig = pygmt.Figure()
94+
>>> fig.basemap(region=[-10, 10, -10, 10], projection="M15c", frame=True)
95+
>>> fig.magnetic_rose(
96+
... position=Position((-5, -5), cstype="mapcoords"),
97+
... width="4c",
98+
... labels=["W", "E", "S", "*"],
99+
... intervals=(45, 15, 3, 60, 20, 4),
100+
... outer_pen="1p,red",
101+
... inner_pen="1p,blue",
102+
... declination=11.5,
103+
... declination_label="11.5°E",
104+
... )
105+
>>> fig.show()
106+
"""
107+
self._activate_figure()
108+
109+
position = _parse_position(
110+
position,
111+
kwdict={
112+
"width": width,
113+
"labels": labels,
114+
"outer_pen": outer_pen,
115+
"inner_pen": inner_pen,
116+
"declination": declination,
117+
"declination_label": declination_label,
118+
"intervals": intervals,
119+
},
120+
default=Position("BL", cstype="inside"), # Default to BL.
121+
)
122+
123+
if declination_label is not None and declination is None:
124+
msg = "Parameter 'declination' must be set when 'declination_label' is set."
125+
raise GMTInvalidInput(msg)
126+
127+
aliasdict = AliasSystem(
128+
F=Alias(box, name="box"),
129+
Tm=[
130+
Alias(position, name="position"),
131+
Alias(width, name="width", prefix="+w"),
132+
Alias(labels, name="labels", prefix="+l", sep=",", size=4),
133+
Alias(outer_pen, name="outer_pen", prefix="+p"),
134+
Alias(inner_pen, name="inner_pen", prefix="+i"),
135+
Alias(declination, name="declination", prefix="+d"),
136+
Alias(declination_label, name="declination_label", prefix="/"),
137+
Alias(intervals, name="intervals", prefix="+t", sep="/", size=(3, 6)),
138+
],
139+
).add_common(
140+
V=verbose,
141+
c=panel,
142+
p=perspective,
143+
t=transparency,
144+
)
145+
146+
with Session() as lib:
147+
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: 5af8ffee19917b1fbc3371cd3aed53cd
3+
size: 27013
4+
hash: md5
5+
path: test_magnetic_rose.png
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
outs:
2+
- md5: 78c53322b2514fbcd56483b793d47ebe
3+
size: 28831
4+
hash: md5
5+
path: test_magnetic_rose_complete.png

pygmt/tests/test_magnetic_rose.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""
2+
Test Figure.magnetic_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
12+
def test_magnetic_rose():
13+
"""
14+
Create a map with a compass. Modified from the test_basemap_compass test.
15+
"""
16+
fig = Figure()
17+
fig.basemap(region=[127.5, 128.5, 26, 27], projection="M10c", frame=True)
18+
fig.magnetic_rose()
19+
return fig
20+
21+
22+
@pytest.mark.mpl_image_compare
23+
def test_magnetic_rose_complete():
24+
"""
25+
Test all parameters of Figure.magnetic_rose.
26+
"""
27+
fig = Figure()
28+
fig.basemap(region=[-10, 10, -10, 10], projection="M10c", frame=True)
29+
fig.magnetic_rose(
30+
position=Position("BL"),
31+
width="2c",
32+
labels=["W", "E", "S", "*"],
33+
intervals=(45, 15, 3, 60, 20, 4),
34+
outer_pen="1p,red",
35+
inner_pen="1p,blue",
36+
declination=11.5,
37+
declination_label="11.5°E",
38+
)
39+
return fig
40+
41+
42+
def test_magnetic_rose_invalid_declination_label():
43+
"""
44+
Test that an exception is raised when declination_label is set but declination is
45+
not set.
46+
"""
47+
fig = Figure()
48+
fig.basemap(region=[-10, 10, -10, 10], projection="M10c", frame=True)
49+
with pytest.raises(GMTInvalidInput):
50+
fig.magnetic_rose(declination_label="11.5°E")

0 commit comments

Comments
 (0)