Skip to content

Commit a851624

Browse files
authored
Wrap x2sys_init and x2sys_cross (#546)
Wrapping the x2sys_cross function (and x2sys_init), implemented under x2sys.py. Original GMT `x2sys_cross` documentation can be found at https://docs.generic-mapping-tools.org/6.1/supplements/x2sys/x2sys_cross.html. Sample test cases stored under test_x2sys_cross.py, and they uses the load_sample_bathymetry dataset. Aliased fmtfile (D) and force (F) for x2sys_init. Aliased tag (T) and coe (Q) for x2sys_cross. * Check that filename input/output works in x2sys_cross * Pass in remote filename directly to x2sys_cross, turn on debug mode too * Let x2sys_file handle multiple input tracks and external crossovers * NotImplementedError for pd.DataFrame, GMTInvalidInput for invalid types * Complete long aliases for x2sys_init Aliased suffix (E), discontinuity (G), spacing (I), units (N), region (R), gap (W), distcalc (j) for x2sys_init. Also checked that passing a list into region (R), spacing (I), units (N), and gap (W) works properly. * Complete most long aliases for x2sys_cross Aliased combitable (A), runtimes(C), interpolation (I), region (R), speed(S), numpoints (W) for x2sys_cross. Also checked that setting the region (R), interpolation (I) and numpoints (W) args work properly. * Parse datetimes on the 3rd and 4th columns * Alias override (D) and trackvalues (Z) for x2sys_cross
1 parent f47a61e commit a851624

File tree

5 files changed

+536
-0
lines changed

5 files changed

+536
-0
lines changed

doc/api/index.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ Operations on grids:
7272
grdcut
7373
grdtrack
7474

75+
Crossover analysis with x2sys:
76+
77+
.. autosummary::
78+
:toctree: generated
79+
80+
x2sys_init
81+
x2sys_cross
82+
7583
GMT Defaults
7684
------------
7785

pygmt/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from .mathops import makecpt
2121
from .modules import GMTDataArrayAccessor, config, info, grdinfo, which
2222
from .gridops import grdcut
23+
from .x2sys import x2sys_init, x2sys_cross
2324
from . import datasets
2425

2526

pygmt/tests/test_x2sys_cross.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# pylint: disable=unused-argument
2+
"""
3+
Tests for x2sys_cross
4+
"""
5+
import os
6+
from tempfile import TemporaryDirectory
7+
8+
import numpy as np
9+
import numpy.testing as npt
10+
import pandas as pd
11+
import pytest
12+
13+
from .. import x2sys_cross, x2sys_init
14+
from ..datasets import load_sample_bathymetry
15+
from ..exceptions import GMTInvalidInput
16+
from ..helpers import data_kind
17+
18+
19+
@pytest.fixture(name="mock_x2sys_home")
20+
def fixture_mock_x2sys_home(monkeypatch):
21+
"""
22+
Set the X2SYS_HOME environment variable to the current working directory
23+
for the test session
24+
"""
25+
monkeypatch.setenv("X2SYS_HOME", os.getcwd())
26+
27+
28+
@pytest.fixture(scope="module", name="tracks")
29+
def fixture_tracks():
30+
"""
31+
Load track data from the sample bathymetry file
32+
"""
33+
dataframe = load_sample_bathymetry()
34+
return [dataframe.query(expr="bathymetry > -20")] # reduce size of dataset
35+
36+
37+
def test_x2sys_cross_input_file_output_file(mock_x2sys_home):
38+
"""
39+
Run x2sys_cross by passing in a filename, and output internal crossovers to
40+
an ASCII txt file
41+
"""
42+
with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir:
43+
tag = os.path.basename(tmpdir)
44+
x2sys_init(tag=tag, fmtfile="xyz", force=True)
45+
outfile = os.path.join(tmpdir, "tmp_coe.txt")
46+
output = x2sys_cross(
47+
tracks=["@tut_ship.xyz"], tag=tag, coe="i", outfile=outfile, verbose="i"
48+
)
49+
50+
assert output is None # check that output is None since outfile is set
51+
assert os.path.exists(path=outfile) # check that outfile exists at path
52+
_ = pd.read_csv(outfile, sep="\t", header=2) # ensure ASCII text file loads ok
53+
54+
return output
55+
56+
57+
def test_x2sys_cross_input_file_output_dataframe(mock_x2sys_home):
58+
"""
59+
Run x2sys_cross by passing in a filename, and output internal crossovers to
60+
a pandas.DataFrame
61+
"""
62+
with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir:
63+
tag = os.path.basename(tmpdir)
64+
x2sys_init(tag=tag, fmtfile="xyz", force=True)
65+
output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, coe="i", verbose="i")
66+
67+
assert isinstance(output, pd.DataFrame)
68+
assert output.shape == (14294, 12)
69+
columns = list(output.columns)
70+
assert columns[:6] == ["x", "y", "i_1", "i_2", "dist_1", "dist_2"]
71+
assert columns[6:] == ["head_1", "head_2", "vel_1", "vel_2", "z_X", "z_M"]
72+
73+
return output
74+
75+
76+
def test_x2sys_cross_input_dataframe_output_dataframe(mock_x2sys_home, tracks):
77+
"""
78+
Run x2sys_cross by passing in one dataframe, and output external crossovers
79+
to a pandas.DataFrame. Not actually implemented yet, wait for
80+
https://github.com/GenericMappingTools/gmt/issues/3717
81+
"""
82+
with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir:
83+
tag = os.path.basename(tmpdir)
84+
x2sys_init(tag=tag, fmtfile="xyz", force=True)
85+
86+
with pytest.raises(NotImplementedError):
87+
_ = x2sys_cross(tracks=tracks, tag=tag, coe="i", verbose="i")
88+
89+
# assert isinstance(output, pd.DataFrame)
90+
# assert output.shape == (4, 12)
91+
# columns = list(output.columns)
92+
# assert columns[:6] == ["x", "y", "t_1", "t_2", "dist_1", "dist_2"]
93+
# assert columns[6:] == ["head_1","head_2","vel_1","vel_2","z_X","z_M"]
94+
# assert output.dtypes["t_1"].type == np.datetime64
95+
# assert output.dtypes["t_2"].type == np.datetime64
96+
97+
# return output
98+
99+
100+
def test_x2sys_cross_input_two_filenames(mock_x2sys_home):
101+
"""
102+
Run x2sys_cross by passing in two filenames, and output external crossovers
103+
to a pandas.DataFrame
104+
"""
105+
with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir:
106+
tag = os.path.basename(tmpdir)
107+
x2sys_init(tag=tag, fmtfile="xyz", force=True)
108+
109+
# Create temporary xyz files
110+
for i in range(2):
111+
np.random.seed(seed=i)
112+
with open(os.path.join(os.getcwd(), f"track_{i}.xyz"), mode="w") as fname:
113+
np.savetxt(fname=fname, X=np.random.rand(10, 3))
114+
115+
output = x2sys_cross(
116+
tracks=["track_0.xyz", "track_1.xyz"], tag=tag, coe="e", verbose="i"
117+
)
118+
119+
assert isinstance(output, pd.DataFrame)
120+
assert output.shape == (24, 12)
121+
columns = list(output.columns)
122+
assert columns[:6] == ["x", "y", "i_1", "i_2", "dist_1", "dist_2"]
123+
assert columns[6:] == ["head_1", "head_2", "vel_1", "vel_2", "z_X", "z_M"]
124+
125+
return output
126+
127+
128+
def test_x2sys_cross_invalid_tracks_input_type(tracks):
129+
"""
130+
Run x2sys_cross using tracks input that is not a pandas.DataFrame (matrix)
131+
or str (file) type, which would raise a GMTInvalidInput error.
132+
"""
133+
invalid_tracks = tracks[0].to_xarray().bathymetry
134+
assert data_kind(invalid_tracks) == "grid"
135+
with pytest.raises(GMTInvalidInput):
136+
x2sys_cross(tracks=[invalid_tracks])
137+
138+
139+
def test_x2sys_cross_region_interpolation_numpoints(mock_x2sys_home):
140+
"""
141+
Test that x2sys_cross's region (R), interpolation (l) and numpoints (W)
142+
arguments work.
143+
"""
144+
with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir:
145+
tag = os.path.basename(tmpdir)
146+
x2sys_init(tag=tag, fmtfile="xyz", force=True)
147+
output = x2sys_cross(
148+
tracks=["@tut_ship.xyz"],
149+
tag=tag,
150+
coe="i",
151+
region=[245, 250, 20, 25],
152+
interpolation="a", # Akima spline interpolation
153+
numpoints=5, # Use up to 5 data points in interpolation
154+
)
155+
156+
assert isinstance(output, pd.DataFrame)
157+
assert output.shape == (3867, 12)
158+
# Check crossover errors (z_X) and mean value of observables (z_M)
159+
npt.assert_allclose(output.z_X.mean(), -139.2, rtol=1e-4)
160+
npt.assert_allclose(output.z_M.mean(), -2890.465813)
161+
162+
163+
def test_x2sys_cross_trackvalues(mock_x2sys_home):
164+
"""
165+
Test that x2sys_cross's trackvalues (Z) argument work.
166+
"""
167+
with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir:
168+
tag = os.path.basename(tmpdir)
169+
x2sys_init(tag=tag, fmtfile="xyz", force=True)
170+
output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, trackvalues=True)
171+
172+
assert isinstance(output, pd.DataFrame)
173+
assert output.shape == (14294, 12)
174+
# Check mean of track 1 values (z_1) and track 2 values (z_2)
175+
npt.assert_allclose(output.z_1.mean(), -2420.569767)
176+
npt.assert_allclose(output.z_2.mean(), -2400.357549)

pygmt/tests/test_x2sys_init.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# pylint: disable=unused-argument
2+
"""
3+
Tests for x2sys_init
4+
"""
5+
import os
6+
from tempfile import TemporaryDirectory
7+
8+
import pytest
9+
10+
from .. import x2sys_init
11+
12+
13+
@pytest.fixture(name="mock_x2sys_home")
14+
def fixture_mock_x2sys_home(monkeypatch):
15+
"""
16+
Set the X2SYS_HOME environment variable to the current working directory
17+
for the test session
18+
"""
19+
monkeypatch.setenv("X2SYS_HOME", os.getcwd())
20+
21+
22+
def test_x2sys_init_region_spacing(mock_x2sys_home):
23+
"""
24+
Test that x2sys_init's region (R) and spacing (I) sequence arguments accept
25+
a list properly
26+
"""
27+
with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir:
28+
tag = os.path.basename(tmpdir)
29+
x2sys_init(
30+
tag=tag, fmtfile="xyz", force=True, region=[0, 10, 20, 30], spacing=[5, 5]
31+
)
32+
33+
with open(os.path.join(tmpdir, f"{tag}.tag"), "r") as tagpath:
34+
tail_line = tagpath.readlines()[-1]
35+
assert "-R0/10/20/30" in tail_line
36+
assert "-I5/5" in tail_line
37+
38+
39+
def test_x2sys_init_units_gap(mock_x2sys_home):
40+
"""
41+
Test that x2sys_init's units (N) and gap (W) arguments accept a list
42+
properly
43+
"""
44+
with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir:
45+
tag = os.path.basename(tmpdir)
46+
x2sys_init(
47+
tag=tag,
48+
fmtfile="xyz",
49+
force=True,
50+
units=["de", "se"],
51+
gap=["tseconds", "de"],
52+
)
53+
54+
with open(os.path.join(tmpdir, f"{tag}.tag"), "r") as tagpath:
55+
tail_line = tagpath.readlines()[-1]
56+
assert "-Nse -Nde" in tail_line
57+
assert "-Wtseconds -Wde" in tail_line

0 commit comments

Comments
 (0)