Skip to content

Commit efbc0fb

Browse files
michaelgrundseismanyvonnefroehlichactions-bot
authored
Add Figure.hlines for plotting horizontal lines (#923)
Co-authored-by: Dongdong Tian <seisman.info@gmail.com> Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com> Co-authored-by: actions-bot <58130806+actions-bot@users.noreply.github.com>
1 parent 85c78f8 commit efbc0fb

11 files changed

+291
-0
lines changed

doc/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Plotting map elements
2929
Figure.basemap
3030
Figure.coast
3131
Figure.colorbar
32+
Figure.hlines
3233
Figure.inset
3334
Figure.legend
3435
Figure.logo

pygmt/figure.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ def _repr_html_(self) -> str:
417417
grdimage,
418418
grdview,
419419
histogram,
420+
hlines,
420421
image,
421422
inset,
422423
legend,

pygmt/src/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from pygmt.src.grdview import grdview
3030
from pygmt.src.grdvolume import grdvolume
3131
from pygmt.src.histogram import histogram
32+
from pygmt.src.hlines import hlines
3233
from pygmt.src.image import image
3334
from pygmt.src.info import info
3435
from pygmt.src.inset import inset

pygmt/src/hlines.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""
2+
hlines - Plot horizontal lines.
3+
"""
4+
5+
from collections.abc import Sequence
6+
7+
import numpy as np
8+
from pygmt.exceptions import GMTInvalidInput
9+
10+
__doctest_skip__ = ["hlines"]
11+
12+
13+
def hlines(
14+
self,
15+
y: float | Sequence[float],
16+
xmin: float | Sequence[float] | None = None,
17+
xmax: float | Sequence[float] | None = None,
18+
pen: str | None = None,
19+
label: str | None = None,
20+
no_clip: bool = False,
21+
perspective: str | bool | None = None,
22+
):
23+
"""
24+
Plot one or multiple horizontal line(s).
25+
26+
This method is a high-level wrapper around :meth:`pygmt.Figure.plot` that focuses on
27+
plotting horizontal lines at Y-coordinates specified by the ``y`` parameter. The
28+
``y`` parameter can be a single value (for a single horizontal line) or a sequence
29+
of values (for multiple horizontal lines).
30+
31+
By default, the X-coordinates of the start and end points of the lines are set to
32+
be the X-limits of the current plot, but this can be overridden by specifying the
33+
``xmin`` and ``xmax`` parameters. ``xmin`` and ``xmax`` can be either a single
34+
value or a sequence of values. If a single value is provided, it is applied to all
35+
lines. If a sequence is provided, the length of ``xmin`` and ``xmax`` must match
36+
the length of ``y``.
37+
38+
The term "horizontal" lines can be interpreted differently in different coordinate
39+
systems:
40+
41+
- **Cartesian** coordinate system: lines are plotted as straight lines.
42+
- **Polar** projection: lines are plotted as arcs along a constant radius.
43+
- **Geographic** projection: lines are plotted as parallels along constant latitude.
44+
45+
Parameters
46+
----------
47+
y
48+
Y-coordinates to plot the lines. It can be a single value (for a single line)
49+
or a sequence of values (for multiple lines).
50+
xmin/xmax
51+
X-coordinates of the start/end point of the line(s). If ``None``, defaults to
52+
the X-limits of the current plot. ``xmin`` and ``xmax`` can be either a single
53+
value or a sequence of values. If a single value is provided, it is applied to
54+
all lines. If a sequence is provided, the length of ``xmin`` and ``xmax`` must
55+
match the length of ``y``.
56+
pen
57+
Pen attributes for the line(s), in the format of *width,color,style*.
58+
label
59+
Label for the line(s), to be displayed in the legend.
60+
no_clip
61+
If ``True``, do not clip lines outside the plot region. Only makes sense in the
62+
Cartesian coordinate system.
63+
perspective
64+
Select perspective view and set the azimuth and elevation angle of the
65+
viewpoint. Refer to :meth:`pygmt.Figure.plot` for details.
66+
67+
Examples
68+
--------
69+
>>> import pygmt
70+
>>> fig = pygmt.Figure()
71+
>>> fig.basemap(region=[0, 10, 0, 10], projection="X10c/10c", frame=True)
72+
>>> fig.hlines(y=1, pen="1p,black", label="Line at y=1")
73+
>>> fig.hlines(y=2, xmin=2, xmax=8, pen="1p,red,-", label="Line at y=2")
74+
>>> fig.hlines(y=[3, 4], xmin=3, xmax=7, pen="1p,black,.", label="Lines at y=3,4")
75+
>>> fig.hlines(y=[5, 6], xmin=4, xmax=9, pen="1p,red", label="Lines at y=5,6")
76+
>>> fig.hlines(
77+
... y=[7, 8], xmin=[0, 1], xmax=[7, 8], pen="1p,blue", label="Lines at y=7,8"
78+
... )
79+
>>> fig.legend()
80+
>>> fig.show()
81+
"""
82+
self._preprocess()
83+
84+
# Determine the x limits from the current plot region if not specified.
85+
if xmin is None or xmax is None:
86+
xlimits = self.region[:2]
87+
if xmin is None:
88+
xmin = xlimits[0]
89+
if xmax is None:
90+
xmax = xlimits[1]
91+
92+
# Ensure y/xmin/xmax are 1-D arrays.
93+
_y = np.atleast_1d(y)
94+
_xmin = np.atleast_1d(xmin)
95+
_xmax = np.atleast_1d(xmax)
96+
97+
nlines = len(_y) # Number of lines to plot.
98+
99+
# Check if xmin/xmax are scalars or have the expected length.
100+
if _xmin.size not in {1, nlines} or _xmax.size not in {1, nlines}:
101+
msg = (
102+
f"'xmin' and 'xmax' are expected to be scalars or have lengths '{nlines}', "
103+
f"but lengths '{_xmin.size}' and '{_xmax.size}' are given."
104+
)
105+
raise GMTInvalidInput(msg)
106+
107+
# Repeat xmin/xmax to match the length of y if they are scalars.
108+
if nlines != 1:
109+
if _xmin.size == 1:
110+
_xmin = np.repeat(_xmin, nlines)
111+
if _xmax.size == 1:
112+
_xmax = np.repeat(_xmax, nlines)
113+
114+
# Call the Figure.plot method to plot the lines.
115+
for i in range(nlines):
116+
# Special handling for label.
117+
# 1. Only specify a label when plotting the first line.
118+
# 2. The -l option can accept comma-separated labels for labeling multiple lines
119+
# with auto-coloring enabled. We don't need this feature here, so we need to
120+
# replace comma with \054 if the label contains commas.
121+
_label = label.replace(",", "\\054") if label and i == 0 else None
122+
123+
# By default, points are connected as great circle arcs in geographic coordinate
124+
# systems and straight lines in Cartesian coordinate systems (including polar
125+
# projection). To plot "horizontal" lines along constant latitude (in geographic
126+
# coordinate systems) or constant radius (in polar projection), we need to
127+
# resample the line to at least 4 points.
128+
npoints = 4 # 2 for Cartesian, at least 4 for geographic and polar projections.
129+
self.plot(
130+
x=np.linspace(_xmin[i], _xmax[i], npoints),
131+
y=[_y[i]] * npoints,
132+
pen=pen,
133+
label=_label,
134+
no_clip=no_clip,
135+
perspective=perspective,
136+
straight_line="x",
137+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
outs:
2+
- md5: e87ea1b80ae5d32d49e9ad94a5c25f96
3+
size: 7199
4+
hash: md5
5+
path: test_hlines_clip.png
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
outs:
2+
- md5: b7055f03ff5bc152c0f6b72f2d39f32c
3+
size: 29336
4+
hash: md5
5+
path: test_hlines_geographic_global_d.png
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
outs:
2+
- md5: ab2e7717cad6ac4132fd3e3af1fefa89
3+
size: 29798
4+
hash: md5
5+
path: test_hlines_geographic_global_g.png
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
outs:
2+
- md5: 70c8decbffd37fc48b2eb9ff84442ec0
3+
size: 14139
4+
hash: md5
5+
path: test_hlines_multiple_lines.png
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
outs:
2+
- md5: 121970f75d34c552e632cacc692f09e9
3+
size: 13685
4+
hash: md5
5+
path: test_hlines_one_line.png
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
outs:
2+
- md5: 0c0eeb160dd6beb06bb6d3dcc264127a
3+
size: 57789
4+
hash: md5
5+
path: test_hlines_polar_projection.png

0 commit comments

Comments
 (0)