Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 42 additions & 13 deletions pygmt/src/fill_between.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
from pygmt.helpers import build_arg_list, fmt_docstring
from pygmt.params import Axis, Frame

__doctest_skip__ = ["fill_between"]


@fmt_docstring
def fill_between( # noqa: PLR0913
self,
x: Sequence[float],
y: Sequence[float],
y2: float | Sequence[float] = 0,
x2: Sequence[float] | None = None,
fill: str | None = None,
pen: str | None = None,
label: str | None = None,
Expand All @@ -39,19 +42,23 @@ def fill_between( # noqa: PLR0913

This method is a high-level wrapper around :meth:`pygmt.Figure.plot` to fill the
area between a primary curve ``y(x)`` and a secondary curve ``y2(x)``. The ``y2``
parameter can be either a single value [Default is 0] or a sequence with the same
length as ``x`` and ``y``.
parameter can be either a single value [Default is 0] or a sequence. It can share
the same ``x`` coordinates as ``y`` or use a separate ``x2`` coordinate sequence.

Parameters
----------
x
X-coordinates shared by the two curves.
X-coordinates of the primary curve.
y
Y-coordinates of the primary curve.
y2
Y-coordinates of the secondary curve. It can be a scalar value for a horizontal
reference line, or a sequence with the same length as ``x`` and ``y``. Default
is 0.
reference line, or a sequence with the same length as ``x`` and ``y`` when
``x2`` is not used. Default is 0.
x2
X-coordinates of the secondary curve. Use this parameter only when ``y2`` is a
sequence sampled at different x-coordinates from ``y``. In that case, ``y2``
must have the same length as ``x2``.
fill
Fill for areas where the primary curve is greater than the secondary curve.
fill2
Expand Down Expand Up @@ -111,19 +118,30 @@ def fill_between( # noqa: PLR0913
description="size for 'y'",
reason=f"'y' is expected to have length {npoints!r}.",
)
if not y2_is_scalar and _y2.size != npoints:
if y2_is_scalar and x2 is not None:
raise GMTValueError(
x2,
description="value for 'x2'",
reason="'x2' can only be used when 'y2' is a sequence.",
)
if not y2_is_scalar and x2 is None and _y2.size != npoints:
raise GMTValueError(
_y2.size,
description="size for 'y2'",
reason=f"'y2' is expected to be a scalar or have length {npoints!r}.",
)

data = {"x": _x, "y": _y} if y2_is_scalar else {"x": _x, "y": _y, "y2": _y2}
_x2 = None if x2 is None else np.atleast_1d(x2)
if _x2 is not None and _y2.size != _x2.size:
raise GMTValueError(
_y2.size,
description="size for 'y2'",
reason=f"'y2' is expected to have length {_x2.size!r} when 'x2' is used.",
)

aliasdict = AliasSystem(
G=Alias(fill, name="fill"),
M=[
Alias("c"),
Alias("s" if _x2 is not None else "c"),
Alias(fill2, name="fill2", prefix="+g"),
Alias(pen2, name="pen2", prefix="+p"),
Alias(label2, name="label2", prefix="+l"),
Expand All @@ -142,7 +160,18 @@ def fill_between( # noqa: PLR0913
)

with Session() as lib:
with lib.virtualfile_in(data=data) as vintbl:
lib.call_module(
module="plot", args=build_arg_list(aliasdict, infile=vintbl)
)
if _x2 is not None:
with (
lib.virtualfile_in(data={"x": _x, "y": _y}) as vintbl1,
lib.virtualfile_in(data={"x": _x2, "y": _y2}) as vintbl2,
):
lib.call_module(
module="plot",
args=build_arg_list(aliasdict, infile=[vintbl1, vintbl2]),
)
else:
data = {"x": _x, "y": _y} if y2_is_scalar else {"x": _x, "y": _y, "y2": _y2}
with lib.virtualfile_in(data=data) as vintbl:
lib.call_module(
module="plot", args=build_arg_list(aliasdict, infile=vintbl)
)
4 changes: 2 additions & 2 deletions pygmt/tests/baseline/test_fill_between_coregistered.png.dvc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
outs:
- md5: 27d288932c7094da06cd2532bfcb707c
size: 29497
- md5: 390a2f2403f84cda79e9248c866db0ee
size: 39880
hash: md5
path: test_fill_between_coregistered.png
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
outs:
- md5: d0a262ef052241b331975e471f777785
size: 33972
hash: md5
path: test_fill_between_noncoregistered.png
4 changes: 2 additions & 2 deletions pygmt/tests/baseline/test_fill_between_y2_scalar.png.dvc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
outs:
- md5: 43a8d89f104ef27788da4ce04a31f941
size: 20863
- md5: 50d5b464fcff97f1f42f62d83e98c6de
size: 25471
hash: md5
path: test_fill_between_y2_scalar.png
52 changes: 51 additions & 1 deletion pygmt/tests/test_fill_between.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def fixture_x():
"""
X-coordinates of the primary curve.
"""
return np.linspace(0, 4, 200)
return np.linspace(0, 4, 100)


@pytest.fixture(scope="module", name="y")
Expand All @@ -32,6 +32,24 @@ def fixture_y2(x):
return 0.5 * np.cos(3 * x)


@pytest.fixture(scope="module", name="x3")
def fixture_x3():
"""
X-coordinates of the secondary curve with non-co-registered sampling.
"""
return np.array(
[0, 0.21, 0.4, 0.63, 0.89, 1.18, 1.45, 1.69, 1.96, 2.26, 2.61, 3.23, 3.49, 4.0]
)


@pytest.fixture(scope="module", name="y3")
def fixture_y3(x3):
"""
Y-coordinates of the secondary curve with non-co-registered sampling.
"""
return 0.5 * np.cos(3 * x3)


@pytest.mark.mpl_image_compare
def test_fill_between_y2_scalar(x, y):
"""
Expand All @@ -50,6 +68,7 @@ def test_fill_between_y2_scalar(x, y):
label="y=sin(5x)",
label2="y=0",
)
fig.plot(x=x, y=y, style="c0.06c", fill="blue", pen="blue")
fig.legend()
return fig

Expand All @@ -72,6 +91,33 @@ def test_fill_between_coregistered(x, y, y2):
label="y=sin(5x)",
label2="y=0.5*cos(3x)",
)
fig.plot(x=x, y=y, style="c0.06c", fill="green", pen="green")
fig.plot(x=x, y=y2, style="c0.06c", fill="brown", pen="brown")
fig.legend()
return fig


@pytest.mark.mpl_image_compare
def test_fill_between_noncoregistered(x, y, x3, y3):
"""
Fill between two curves that do not share the same x-coordinates.
"""
fig = Figure()
fig.basemap(region=[0, 4, -1.2, 1.2], projection="X10c/5c", frame=True)
fig.fill_between(
x=x,
y=y,
x2=x3,
y2=y3,
fill="lightgreen",
fill2="lightbrown",
pen="1p,green",
pen2="1p,brown",
label="y=sin(5x)",
label2="y=0.5*cos(3x)",
)
fig.plot(x=x, y=y, style="c0.06c", fill="green", pen="green")
fig.plot(x=x3, y=y3, style="c0.06c", fill="brown", pen="brown")
fig.legend()
return fig

Expand All @@ -93,3 +139,7 @@ def test_fill_between_invalid_input():
fig.fill_between(x=[0, 1], y=[1, 2], y2=[0])
with pytest.raises(GMTValueError):
fig.fill_between(x=[0, 1], y=[1, 2], y2=[0, 1, 2])
with pytest.raises(GMTValueError):
fig.fill_between(x=[0, 1], y=[1, 2], y2=0, x2=[0, 1])
with pytest.raises(GMTValueError):
fig.fill_between(x=[0, 1], y=[1, 2], y2=[0, 1], x2=[0])
Loading