Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Figure.plot: Add the "symbol" parameter to support plotting data points with varying symbols #1117

Merged
merged 19 commits into from
Oct 29, 2024
Merged
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
12 changes: 11 additions & 1 deletion pygmt/src/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@
w="wrap",
)
@kwargs_to_strings(R="sequence", c="sequence_comma", i="sequence_comma", p="sequence")
def plot(self, data=None, x=None, y=None, size=None, direction=None, **kwargs):
def plot(
self, data=None, x=None, y=None, size=None, symbol=None, direction=None, **kwargs
):
r"""
Plot lines, polygons, and symbols in 2-D.

Expand Down Expand Up @@ -87,6 +89,8 @@ def plot(self, data=None, x=None, y=None, size=None, direction=None, **kwargs):
size : 1-D array
The size of the data points in units specified using ``style``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. But if size is used with symbol the unit for size is given by the GMT / PyGMT defaults, correct?
Using style for specifing the unit in this case would be super confusing, e.g.:

...
size=[0.5, 0.8, 0.9],
symbol=["s", "c", "d"],
style="c",  # now centimeters, not circle
...

Copy link
Member

@seisman seisman Oct 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it produces three circles with different sizes.

I want to emphasize that the "style" parameter was introduced in very early PyGMT versions. The docstrings are mostly copied from the upstream GMT documentation without further polishes, so they may make no sense for PyGMT.

Only valid if using ``x``/``y``.
symbol : 1-D array
Copy link
Member

@weiji14 weiji14 Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symbol can also be a single string right? E.g. symbol="t" for triangle. I think we need to add another unit test for the "constant-symbol and different sizes" case as mentioned at #1117 (comment)

Suggested change
symbol : 1-D array
symbol : str or 1-D array

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, similar to the size parameter, symbol must be a 1-D array.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm ok, I just tried with symbol="t" and it plotted a straight line:

import pygmt
fig = pygmt.Figure()
fig.plot(
    x=[1, 2, 3, 4],
    y=[1, 1, 1, 1],
    region=[0, 5, 0, 2],
    projection="X2c",
    fill="blue",
    size=[0.1, 0.2, 0.3, 0.4],
    symbol="t",
    frame="af",
)
fig.show()

plot_symbol

So yes, it does have to be a 1-D array.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. Maybe writing "list of str" is clearer here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1-D array is used here to match the types of other parameters. We probably can change it to Sequence[str] when we add typing hints.

The symbols of the data points. Only valid if using ``x``/``y``.
direction : list of two 1-D arrays
If plotting vectors (using ``style="V"`` or ``style="v"``), then
should be a list of two 1-D arrays with the vector directions. These
Expand Down Expand Up @@ -226,13 +230,19 @@ def plot(self, data=None, x=None, y=None, size=None, direction=None, **kwargs):
if is_nonstr_iter(kwargs.get(flag)):
extra_arrays.append(kwargs.get(flag))
kwargs[flag] = ""
# Symbol must be at the last column
if is_nonstr_iter(symbol):
if "S" not in kwargs:
kwargs["S"] = True
extra_arrays.append(symbol)
else:
for name, value in [
("direction", direction),
("fill", kwargs.get("G")),
("size", size),
("intensity", kwargs.get("I")),
("transparency", kwargs.get("t")),
("symbol", symbol),
]:
if is_nonstr_iter(value):
raise GMTInvalidInput(f"'{name}' can't be 1-D array if 'data' is used.")
Expand Down
5 changes: 5 additions & 0 deletions pygmt/tests/baseline/test_plot_symbol.png.dvc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
outs:
- md5: bf71e5e72529a85d9ac45aa71321962e
size: 3798
hash: md5
path: test_plot_symbol.png
25 changes: 23 additions & 2 deletions pygmt/tests/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ def test_plot_fail_no_data(data, region):

def test_plot_fail_1d_array_with_data(data, region):
"""
Should raise an exception if array fill, size, intensity and transparency are used
with matrix.
Should raise an exception if arrays of fill, size, intensity, transparency and
symbol are specified when data is given.
"""
fig = Figure()
kwargs = {"data": data, "region": region, "projection": "X10c", "frame": "afg"}
Expand All @@ -106,6 +106,8 @@ def test_plot_fail_1d_array_with_data(data, region):
fig.plot(style="c0.2c", fill="red", intensity=data[:, 2], **kwargs)
with pytest.raises(GMTInvalidInput):
fig.plot(style="c0.2c", fill="red", transparency=data[:, 2] * 100, **kwargs)
with pytest.raises(GMTInvalidInput):
fig.plot(style="0.2c", fill="red", symbol=["c"] * data.shape[0], **kwargs)


@pytest.mark.mpl_image_compare
Expand Down Expand Up @@ -297,6 +299,25 @@ def test_plot_sizes_colors_transparencies():
return fig


@pytest.mark.mpl_image_compare
def test_plot_symbol():
"""
Plot the data using array-like symbols.
"""
fig = Figure()
fig.plot(
x=[1, 2, 3, 4],
y=[1, 1, 1, 1],
region=[0, 5, 0, 5],
projection="X4c",
fill="blue",
size=[0.1, 0.2, 0.3, 0.4],
symbol=["c", "t", "i", "s"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do size and symbol need to have to same length? Or is something like:

size=[0.1, 0.2, 0.3, 0.4],  # different sizes but same symbol
symbol=["c"],

or

size=[0.4],  # same size but different symbols
symbol=["c", "t", "i", "s"],

allowed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, they must be the same size.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I figured it out: One has to use the size or symbol parameter together with the style parameter 🙂.

frame="af",
)
return fig


@pytest.mark.mpl_image_compare(filename="test_plot_matrix.png")
@pytest.mark.parametrize("fill", ["#aaaaaa", 170])
def test_plot_matrix(data, fill):
Expand Down
Loading