Skip to content

Commit 03d6797

Browse files
yohaimagenMeghan Jonesseisman
authored
Raise GMTInvalidInput exception when required z is missing (#1478)
Co-authored-by: Meghan Jones <[email protected]> Co-authored-by: Dongdong Tian <[email protected]>
1 parent a99533f commit 03d6797

File tree

11 files changed

+89
-111
lines changed

11 files changed

+89
-111
lines changed

pygmt/clib/session.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,7 +1362,14 @@ def virtualfile_from_grid(self, grid):
13621362

13631363
@fmt_docstring
13641364
def virtualfile_from_data(
1365-
self, check_kind=None, data=None, x=None, y=None, z=None, extra_arrays=None
1365+
self,
1366+
check_kind=None,
1367+
data=None,
1368+
x=None,
1369+
y=None,
1370+
z=None,
1371+
extra_arrays=None,
1372+
required_z=False,
13661373
):
13671374
"""
13681375
Store any data inside a virtual file.
@@ -1415,7 +1422,7 @@ def virtualfile_from_data(
14151422
...
14161423
<vector memory>: N = 3 <7/9> <4/6> <1/3>
14171424
"""
1418-
kind = data_kind(data, x, y, z)
1425+
kind = data_kind(data, x, y, z, required_z=required_z)
14191426

14201427
if check_kind == "raster" and kind not in ("file", "grid"):
14211428
raise GMTInvalidInput(f"Unrecognized data type for grid: {type(data)}")

pygmt/helpers/utils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from pygmt.exceptions import GMTInvalidInput
1616

1717

18-
def data_kind(data, x=None, y=None, z=None):
18+
def data_kind(data, x=None, y=None, z=None, required_z=False):
1919
"""
2020
Check what kind of data is provided to a module.
2121
@@ -69,7 +69,9 @@ def data_kind(data, x=None, y=None, z=None):
6969
if data is not None and (x is not None or y is not None or z is not None):
7070
raise GMTInvalidInput("Too much data. Use either data or x and y.")
7171
if data is None and (x is None or y is None):
72-
raise GMTInvalidInput("Must provided both x and y.")
72+
raise GMTInvalidInput("Must provide both x and y.")
73+
if data is None and required_z and z is None:
74+
raise GMTInvalidInput("Must provide x, y, and z.")
7375

7476
if isinstance(data, (str, pathlib.PurePath)):
7577
kind = "file"
@@ -78,6 +80,8 @@ def data_kind(data, x=None, y=None, z=None):
7880
elif hasattr(data, "__geo_interface__"):
7981
kind = "geojson"
8082
elif data is not None:
83+
if required_z and data.shape[1] < 3:
84+
raise GMTInvalidInput("data must provide x, y, and z columns.")
8185
kind = "matrix"
8286
else:
8387
kind = "vectors"

pygmt/src/blockm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def _blockm(block_method, table, outfile, x, y, z, **kwargs):
4343
with Session() as lib:
4444
# Choose how data will be passed into the module
4545
table_context = lib.virtualfile_from_data(
46-
check_kind="vector", data=table, x=x, y=y, z=z
46+
check_kind="vector", data=table, x=x, y=y, z=z, required_z=True
4747
)
4848
# Run blockm* on data table
4949
with table_context as infile:

pygmt/src/contour.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"""
44

55
from pygmt.clib import Session
6-
from pygmt.exceptions import GMTInvalidInput
76
from pygmt.helpers import (
87
build_arg_string,
98
data_kind,
@@ -127,9 +126,7 @@ def contour(self, x=None, y=None, z=None, data=None, **kwargs):
127126
"""
128127
kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access
129128

130-
kind = data_kind(data, x, y, z)
131-
if kind == "vectors" and z is None:
132-
raise GMTInvalidInput("Must provided both x, y, and z.")
129+
kind = data_kind(data, x, y, z, required_z=True)
133130

134131
with Session() as lib:
135132
# Choose how data will be passed in to the module

pygmt/src/plot3d.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,13 @@ def plot3d(
235235
with Session() as lib:
236236
# Choose how data will be passed in to the module
237237
file_context = lib.virtualfile_from_data(
238-
check_kind="vector", data=data, x=x, y=y, z=z, extra_arrays=extra_arrays
238+
check_kind="vector",
239+
data=data,
240+
x=x,
241+
y=y,
242+
z=z,
243+
extra_arrays=extra_arrays,
244+
required_z=True,
239245
)
240246

241247
with file_context as fname:

pygmt/src/surface.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,7 @@ def surface(x=None, y=None, z=None, data=None, **kwargs):
9494
- None if ``outgrid`` is set (grid output will be stored in file set by
9595
``outgrid``)
9696
"""
97-
kind = data_kind(data, x, y, z)
98-
if kind == "vectors" and z is None:
99-
raise GMTInvalidInput("Must provide z with x and y.")
97+
kind = data_kind(data, x, y, z, required_z=True)
10098

10199
with GMTTempFile(suffix=".nc") as tmpfile:
102100
with Session() as lib:

pygmt/src/wiggle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def wiggle(self, x=None, y=None, z=None, data=None, **kwargs):
107107
with Session() as lib:
108108
# Choose how data will be passed in to the module
109109
file_context = lib.virtualfile_from_data(
110-
check_kind="vector", data=data, x=x, y=y, z=z
110+
check_kind="vector", data=data, x=x, y=y, z=z, required_z=True
111111
)
112112

113113
with file_context as fname:

pygmt/tests/test_clib.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# pylint: disable=protected-access
2+
# pylint: disable=redefined-outer-name
23
"""
34
Test the wrappers for the C API.
45
"""
56
import os
67
from contextlib import contextmanager
8+
from itertools import product
79

810
import numpy as np
911
import numpy.testing as npt
@@ -23,11 +25,20 @@
2325
from pygmt.helpers import GMTTempFile
2426

2527
TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
28+
POINTS_DATA = os.path.join(TEST_DATA_DIR, "points.txt")
2629

2730
with clib.Session() as _lib:
2831
gmt_version = Version(_lib.info["version"])
2932

3033

34+
@pytest.fixture(scope="module")
35+
def data():
36+
"""
37+
Load the point data from the test file.
38+
"""
39+
return np.loadtxt(POINTS_DATA)
40+
41+
3142
@contextmanager
3243
def mock(session, func, returns=None, mock_func=None):
3344
"""
@@ -410,6 +421,58 @@ def test_virtual_file_bad_direction():
410421
print("This should have failed")
411422

412423

424+
def test_virtualfile_from_data_required_z_matrix():
425+
"""
426+
Test that function fails when third z column in a matrix is needed but not
427+
provided.
428+
"""
429+
data = np.ones((5, 2))
430+
with clib.Session() as lib:
431+
with pytest.raises(GMTInvalidInput):
432+
with lib.virtualfile_from_data(data=data, required_z=True):
433+
pass
434+
435+
436+
def test_virtualfile_from_data_fail_non_valid_data(data):
437+
"""
438+
Should raise an exception if too few or too much data is given.
439+
"""
440+
# Test all combinations where at least one data variable
441+
# is not given in the x, y case:
442+
for variable in product([None, data[:, 0]], repeat=2):
443+
# Filter one valid configuration:
444+
if not any(item is None for item in variable):
445+
continue
446+
with clib.Session() as lib:
447+
with pytest.raises(GMTInvalidInput):
448+
lib.virtualfile_from_data(
449+
x=variable[0],
450+
y=variable[1],
451+
)
452+
453+
# Test all combinations where at least one data variable
454+
# is not given in the x, y, z case:
455+
for variable in product([None, data[:, 0]], repeat=3):
456+
# Filter one valid configuration:
457+
if not any(item is None for item in variable):
458+
continue
459+
with clib.Session() as lib:
460+
with pytest.raises(GMTInvalidInput):
461+
lib.virtualfile_from_data(
462+
x=variable[0], y=variable[1], z=variable[2], required_z=True
463+
)
464+
465+
# Should also fail if given too much data
466+
with clib.Session() as lib:
467+
with pytest.raises(GMTInvalidInput):
468+
lib.virtualfile_from_data(
469+
x=data[:, 0],
470+
y=data[:, 1],
471+
z=data[:, 2],
472+
data=data,
473+
)
474+
475+
413476
def test_virtualfile_from_vectors():
414477
"""
415478
Test the automation for transforming vectors to virtual file dataset.

pygmt/tests/test_contour.py

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
Tests contour.
44
"""
55
import os
6-
from itertools import product
76

87
import numpy as np
98
import pytest
109
from pygmt import Figure
11-
from pygmt.exceptions import GMTInvalidInput
1210

1311
TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
1412
POINTS_DATA = os.path.join(TEST_DATA_DIR, "points.txt")
@@ -30,46 +28,6 @@ def region():
3028
return [10, 70, -5, 10]
3129

3230

33-
def test_contour_fail_no_data(data):
34-
"""
35-
Should raise an exception if no data is given.
36-
"""
37-
# Contour should raise an exception if no or not sufficient data
38-
# is given
39-
fig = Figure()
40-
# Test all combinations where at least one data variable
41-
# is not given:
42-
for variable in product([None, data[:, 0]], repeat=3):
43-
# Filter one valid configuration:
44-
if not any(item is None for item in variable):
45-
continue
46-
with pytest.raises(GMTInvalidInput):
47-
fig.contour(
48-
x=variable[0],
49-
y=variable[1],
50-
z=variable[2],
51-
region=region,
52-
projection="X4i",
53-
color="red",
54-
frame="afg",
55-
pen="",
56-
)
57-
# Should also fail if given too much data
58-
with pytest.raises(GMTInvalidInput):
59-
fig.contour(
60-
x=data[:, 0],
61-
y=data[:, 1],
62-
z=data[:, 2],
63-
data=data,
64-
region=region,
65-
projection="X10c",
66-
style="c0.2c",
67-
color="red",
68-
frame="afg",
69-
pen=True,
70-
)
71-
72-
7331
@pytest.mark.mpl_image_compare
7432
def test_contour_vec(region):
7533
"""

pygmt/tests/test_plot3d.py

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -68,48 +68,6 @@ def test_plot3d_red_circles_zsize(data, region):
6868
return fig
6969

7070

71-
def test_plot3d_fail_no_data(data, region):
72-
"""
73-
Plot should raise an exception if no data is given.
74-
"""
75-
fig = Figure()
76-
with pytest.raises(GMTInvalidInput):
77-
fig.plot3d(
78-
region=region, projection="X10c", style="c0.2c", color="red", frame="afg"
79-
)
80-
with pytest.raises(GMTInvalidInput):
81-
fig.plot3d(
82-
x=data[:, 0],
83-
region=region,
84-
projection="X10c",
85-
style="c0.2c",
86-
color="red",
87-
frame="afg",
88-
)
89-
with pytest.raises(GMTInvalidInput):
90-
fig.plot3d(
91-
y=data[:, 0],
92-
region=region,
93-
projection="X10c",
94-
style="c0.2c",
95-
color="red",
96-
frame="afg",
97-
)
98-
# Should also fail if given too much data
99-
with pytest.raises(GMTInvalidInput):
100-
fig.plot3d(
101-
x=data[:, 0],
102-
y=data[:, 1],
103-
z=data[:, 2],
104-
data=data,
105-
region=region,
106-
projection="X10c",
107-
style="c0.2c",
108-
color="red",
109-
frame="afg",
110-
)
111-
112-
11371
def test_plot3d_fail_1d_array_with_data(data, region):
11472
"""
11573
Should raise an exception if array color, size, intensity and transparency

pygmt/tests/test_surface.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,6 @@ def test_surface_input_xyz(ship_data):
5959
return output
6060

6161

62-
def test_surface_input_xy_no_z(ship_data):
63-
"""
64-
Run surface by passing in x and y, but no z.
65-
"""
66-
with pytest.raises(GMTInvalidInput):
67-
surface(
68-
x=ship_data.longitude,
69-
y=ship_data.latitude,
70-
spacing="5m",
71-
region=[245, 255, 20, 30],
72-
)
73-
74-
7562
def test_surface_wrong_kind_of_input(ship_data):
7663
"""
7764
Run surface using grid input that is not file/matrix/vectors.

0 commit comments

Comments
 (0)