Skip to content

Commit b7926ae

Browse files
seismanyvonnefroehlichmichaelgrundweiji14
authored
Add Figure.timestamp to plot the GMT timestamp logo (#2208)
Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com> Co-authored-by: Michael Grund <23025878+michaelgrund@users.noreply.github.com> Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com>
1 parent d546ae5 commit b7926ae

10 files changed

Lines changed: 213 additions & 0 deletions

doc/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Plotting map elements
3434
Figure.logo
3535
Figure.solar
3636
Figure.text
37+
Figure.timestamp
3738

3839
Plotting tabular data
3940
~~~~~~~~~~~~~~~~~~~~~

pygmt/figure.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ def _repr_html_(self):
518518
subplot,
519519
ternary,
520520
text,
521+
timestamp,
521522
velo,
522523
wiggle,
523524
)

pygmt/src/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
from pygmt.src.surface import surface
5252
from pygmt.src.ternary import ternary
5353
from pygmt.src.text import text_ as text # "text" is an argument within "text_"
54+
from pygmt.src.timestamp import timestamp
5455
from pygmt.src.triangulate import triangulate
5556
from pygmt.src.velo import velo
5657
from pygmt.src.which import which

pygmt/src/timestamp.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""
2+
timestamp - Plot the GMT timestamp logo.
3+
"""
4+
from packaging.version import Version
5+
from pygmt import __gmt_version__
6+
from pygmt.clib import Session
7+
from pygmt.exceptions import GMTInvalidInput
8+
from pygmt.helpers import build_arg_string, is_nonstr_iter
9+
10+
__doctest_skip__ = ["timestamp"]
11+
12+
13+
def timestamp(
14+
self,
15+
text=None,
16+
label=None,
17+
justification="BL",
18+
offset=("-54p", "-54p"),
19+
font="Helvetica,black",
20+
timefmt="%Y %b %d %H:%M:%S",
21+
):
22+
r"""
23+
Plot the GMT timestamp logo.
24+
25+
Parameters
26+
----------
27+
text : None or str
28+
If ``None``, the current UNIX timestamp is shown in the GMT timestamp
29+
logo. Set this parameter to replace the UNIX timestamp with a
30+
custom text string instead. The text must be less than 64 characters.
31+
*Requires GMT>=6.5.0*.
32+
label : None or str
33+
The text string shown after the GMT timestamp logo.
34+
justification : str
35+
Justification of the timestamp. The *justification* is a two-character
36+
code that is a combination of a horizontal (**L**\ (eft),
37+
**C**\ (enter), or **R**\ (ight)) and a vertical (**T**\ (op),
38+
**M**\ (iddle), or **B**\ (ottom)) code.
39+
offset : str or tuple
40+
*offset* or (*offset_x*, *offset_y*).
41+
Offset the anchor point of the timestamp by *offset_x* and *offset_y*.
42+
If a single value *offset* is given, *offset_y* = *offset_x* =
43+
*offset*.
44+
font : str
45+
Font of the timestamp and the optional label. The parameter can't
46+
change the font color for GMT<=6.4.0, only the font ID.
47+
timefmt : str
48+
Format string for the UNIX timestamp. The format string is parsed by
49+
the C function ``strftime``, so that virtually any text can be used
50+
(even not containing any time information).
51+
52+
Examples
53+
--------
54+
>>> # Plot the GMT timestamp logo.
55+
>>> import pygmt
56+
>>> fig = pygmt.Figure()
57+
>>> fig.timestamp()
58+
>>> fig.show()
59+
<IPython.core.display.Image object>
60+
61+
>>> # Plot the GMT timestamp logo with a custom label.
62+
>>> fig = pygmt.Figure()
63+
>>> fig.timestamp(label="Powered by PyGMT")
64+
>>> fig.show()
65+
<IPython.core.display.Image object>
66+
"""
67+
self._preprocess() # pylint: disable=protected-access
68+
69+
# Build the options passed to the "plot" module
70+
kwdict = {"T": True, "U": ""}
71+
if label is not None:
72+
kwdict["U"] += f"{label}"
73+
kwdict["U"] += f"+j{justification}"
74+
75+
if is_nonstr_iter(offset): # given a tuple
76+
kwdict["U"] += "+o" + "/".join(f"{item}" for item in offset)
77+
else: # given a single value
78+
if "/" not in offset and Version(__gmt_version__) <= Version("6.4.0"):
79+
# Giving a single offset doesn't work in GMT <= 6.4.0.
80+
# See https://github.com/GenericMappingTools/gmt/issues/7107.
81+
kwdict["U"] += f"+o{offset}/{offset}"
82+
else:
83+
kwdict["U"] += f"+o{offset}"
84+
85+
# The +t modifier was added in GMT 6.5.0.
86+
# See https://github.com/GenericMappingTools/gmt/pull/7127.
87+
if text is not None:
88+
if Version(__gmt_version__) < Version("6.5.0"):
89+
raise GMTInvalidInput("The parameter 'text' requires GMT>=6.5.0.")
90+
if len(str(text)) > 64:
91+
raise GMTInvalidInput(
92+
"The parameter 'text' must be less than 64 characters."
93+
)
94+
kwdict["U"] += f"+t{text}"
95+
96+
with Session() as lib:
97+
lib.call_module(
98+
module="plot",
99+
args=build_arg_string(
100+
kwdict, confdict={"FONT_LOGO": font, "FORMAT_TIME_STAMP": timefmt}
101+
),
102+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
outs:
2+
- md5: b4ca6cb54d80463606bb3d28eb077227
3+
size: 1668
4+
path: test_timestamp.png
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
outs:
2+
- md5: e1c3c80a85ebaadc0a820bcc7954a539
3+
size: 2840
4+
path: test_timestamp_font.png
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
outs:
2+
- md5: c2bfca8624e4bf966eeb57bbc4aa0726
3+
size: 8843
4+
path: test_timestamp_justification.png
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
outs:
2+
- md5: 868d6a5912b586bc7e095b9923ce8d83
3+
size: 3036
4+
path: test_timestamp_label.png
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
outs:
2+
- md5: be0021731b881e32e3d8a46d6fa8abc3
3+
size: 9450
4+
path: test_timestamp_offset.png

pygmt/tests/test_timestamp.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""
2+
Tests for Figure.timestamp.
3+
"""
4+
import pytest
5+
from packaging.version import Version
6+
from pygmt import Figure, __gmt_version__
7+
8+
9+
@pytest.fixture(scope="module", name="faketime")
10+
def fixture_faketime():
11+
"""
12+
Fake datetime that will be passed to the "timefmt" parameter, so that the
13+
timestamp string always has a fixed value.
14+
"""
15+
return "9999-99-99T99:99:99"
16+
17+
18+
@pytest.mark.mpl_image_compare
19+
def test_timestamp(faketime):
20+
"""
21+
Test that the simplest timestamp() call works.
22+
"""
23+
fig = Figure()
24+
fig.timestamp(timefmt=faketime)
25+
return fig
26+
27+
28+
@pytest.mark.mpl_image_compare
29+
def test_timestamp_label(faketime):
30+
"""
31+
Check if the "label" parameter works.
32+
"""
33+
fig = Figure()
34+
fig.timestamp(label="Powered by PyGMT", timefmt=faketime)
35+
return fig
36+
37+
38+
@pytest.mark.mpl_image_compare
39+
def test_timestamp_justification():
40+
"""
41+
Check if the "justification" parameter works.
42+
43+
Only a subset of justification codes are tested to avoid overlapping
44+
timestamps.
45+
"""
46+
fig = Figure()
47+
fig.basemap(projection="X10c/5c", region=[0, 10, 0, 5], frame=0)
48+
for just in ["BL", "BR", "TL", "TR"]:
49+
fig.timestamp(justification=just, timefmt=just)
50+
return fig
51+
52+
53+
@pytest.mark.mpl_image_compare
54+
def test_timestamp_offset():
55+
"""
56+
Check if the "offset" parameter works.
57+
"""
58+
fig = Figure()
59+
fig.basemap(projection="X10c/5c", region=[0, 10, 0, 5], frame="g1")
60+
for offset in ["1c", "1c/2c", ("1c", "3c")]:
61+
fig.timestamp(offset=offset, timefmt=f"offset={offset}")
62+
return fig
63+
64+
65+
@pytest.mark.mpl_image_compare
66+
def test_timestamp_font(faketime):
67+
"""
68+
Test if the "font" parameter works.
69+
"""
70+
fig = Figure()
71+
fig.timestamp(font="Times-Roman", label="Powered by GMT", timefmt=faketime)
72+
return fig
73+
74+
75+
@pytest.mark.skipif(
76+
Version(__gmt_version__) < Version("6.5.0"),
77+
reason="The 'text' parameter requires GMT>=6.5.0",
78+
)
79+
@pytest.mark.mpl_image_compare(filename="test_timestamp.png")
80+
def test_timestamp_text(faketime):
81+
"""
82+
Test if the "text" parameter works.
83+
84+
Requires GMT>=6.5.0.
85+
"""
86+
fig = Figure()
87+
fig.timestamp(text=faketime)
88+
return fig

0 commit comments

Comments
 (0)