Skip to content

Commit a6cef02

Browse files
committed
Wrap grdtrack
Initial commit for #307. Implement GMT grdtrack function under sampling.py. Test cases checking proper pandas.DataFrame and xarray.DataArray inputs stored in test_grdtrack.py. Sample datasets for tests uses newly created load_east_pacific_rise_grid and load_ocean_ridge_points functions in datasets/tutorial.py. GMT grdtrack documentation can be found at https://gmt.soest.hawaii.edu/doc/latest/grdtrack. Originally, grdtrack should take in an xyfile and -Ggridfile as parameters, and pass the output table to stdout. Here, the implementation (currently) takes in a pandas.DataFrame table and xarray.DataArray grid instead as input, and returns a pandas.DataFrame with an extra column for the sampled grid data.
1 parent 997b88c commit a6cef02

File tree

5 files changed

+189
-1
lines changed

5 files changed

+189
-1
lines changed

pygmt/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from .session_management import begin as _begin, end as _end
1616
from .figure import Figure
1717
from .gridding import surface
18+
from .sampling import grdtrack
1819
from .modules import info, grdinfo, which
1920
from . import datasets
2021

pygmt/datasets/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,11 @@
22
#
33
# Load sample data included with GMT (downloaded from the GMT cache server).
44

5-
from .tutorial import load_japan_quakes, load_sample_bathymetry, load_usgs_quakes
5+
from .tutorial import (
6+
load_east_pacific_rise_grid,
7+
load_japan_quakes,
8+
load_ocean_ridge_points,
9+
load_sample_bathymetry,
10+
load_usgs_quakes,
11+
)
612
from .earth_relief import load_earth_relief

pygmt/datasets/tutorial.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,33 @@
22
Functions to load sample data from the GMT tutorials.
33
"""
44
import pandas as pd
5+
import xarray as xr
56

67
from .. import which
78

89

10+
def load_east_pacific_rise_grid():
11+
"""
12+
Load a grid of bathymetry over part of the East Pacific Rise as a xarray.DataArray.
13+
14+
This is the ``@spac_33.nc`` dataset used in the GMT tutorials.
15+
16+
The data are downloaded to a cache directory (usually ``~/.gmt/cache``) the
17+
first time you invoke this function. Afterwards, it will load the data from
18+
the cache. So you'll need an internet connection the first time around.
19+
20+
Returns
21+
-------
22+
data : xarray.DataArray
23+
The data grid. Coordinates in longitude (lon) and latitude (lat).
24+
Data attributes: bathymetry (z) in metres.
25+
"""
26+
fname = which("@spac_33.nc", download="c")
27+
with xr.open_dataarray(fname) as dataarray:
28+
data = dataarray.load()
29+
return data
30+
31+
932
def load_japan_quakes():
1033
"""
1134
Load a table of earthquakes around Japan as a pandas.Dataframe.
@@ -38,6 +61,28 @@ def load_japan_quakes():
3861
return data
3962

4063

64+
def load_ocean_ridge_points():
65+
"""
66+
Load a table of ocean ridge points for the entire world as a pandas.DataFrame.
67+
68+
This is the ``@ridge.txt`` dataset used in the GMT tutorials.
69+
70+
The data are downloaded to a cache directory (usually ``~/.gmt/cache``) the
71+
first time you invoke this function. Afterwards, it will load the data from
72+
the cache. So you'll need an internet connection the first time around.
73+
74+
Returns
75+
-------
76+
data : pandas.Dataframe
77+
The data table. Columns are longitude and latitude.
78+
"""
79+
fname = which("@ridge.txt", download="c")
80+
data = pd.read_csv(
81+
fname, sep=r"\s+", names=["longitude", "latitude"], skiprows=1, comment=">"
82+
)
83+
return data
84+
85+
4186
def load_sample_bathymetry():
4287
"""
4388
Load a table of ship observations of bathymetry off Baja California as a

pygmt/sampling.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""
2+
GMT modules for Sampling of 1-D and 2-D Data
3+
"""
4+
import pandas as pd
5+
import xarray as xr
6+
7+
from .clib import Session
8+
from .helpers import build_arg_string, fmt_docstring, GMTTempFile, data_kind
9+
from .exceptions import GMTInvalidInput
10+
11+
12+
@fmt_docstring
13+
def grdtrack(table: pd.DataFrame, grid: xr.DataArray, newcolname: str = "z_", **kwargs):
14+
"""
15+
Sample grids at specified (x,y) locations.
16+
17+
Grdtrack reads one or more grid files and a table with (x,y) [or (lon,lat)]
18+
positions in the first two columns (more columns may be present). It interpolates
19+
the grid(s) at the positions in the table and writes out the table with the
20+
interpolated values added as (one or more) new columns. A bicubic [Default],
21+
bilinear, B-spline or nearest-neighbor (see -n) interpolation is used, requiring
22+
boundary conditions at the limits of the region.
23+
24+
Parameters
25+
----------
26+
table: pandas.DataFrame
27+
Table with (x, y) or (lon, lat) values in the first two columns. More columns
28+
may be present.
29+
30+
grid: xarray.DataArray
31+
Gridded array from which to sample values from.
32+
33+
newcolname: str
34+
Name for the new column in the table where the sampled values will be placed.
35+
Defaults to "z_".
36+
37+
Returns
38+
-------
39+
ret: pandas.DataFrame
40+
Table with (x, y, ..., z_) or (lon, lat, ..., z_) values.
41+
42+
"""
43+
with GMTTempFile(suffix=".csv") as tmpfile:
44+
with Session() as lib:
45+
# Store the pandas.DataFrame table in virtualfile
46+
if data_kind(table) == "matrix":
47+
table_context = lib.virtualfile_from_matrix(table.values)
48+
else:
49+
raise GMTInvalidInput(f"Unrecognized data type {type(table)}")
50+
51+
# Store the xarray.DataArray grid in virtualfile
52+
if data_kind(grid) == "grid":
53+
grid_context = lib.virtualfile_from_grid(grid)
54+
else:
55+
raise GMTInvalidInput(f"Unrecognized data type {type(grid)}")
56+
57+
# Run grdtrack on the temporary (csv) table and (netcdf) grid virtualfiles
58+
with table_context as csvfile:
59+
with grid_context as grdfile:
60+
kwargs = {"G": grdfile}
61+
arg_str = " ".join(
62+
[csvfile, build_arg_string(kwargs), "->" + tmpfile.name]
63+
)
64+
lib.call_module(module="grdtrack", args=arg_str)
65+
66+
# Read temporary csv output to a pandas table
67+
column_names = table.columns.to_list() + [newcolname]
68+
result = pd.read_csv(tmpfile.name, sep="\t", names=column_names)
69+
70+
return result

pygmt/tests/test_grdtrack.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""
2+
Tests for grdtrack
3+
"""
4+
5+
import pandas as pd
6+
import pytest
7+
8+
from .. import grdtrack
9+
from ..datasets import load_east_pacific_rise_grid, load_ocean_ridge_points
10+
from ..exceptions import GMTInvalidInput
11+
from ..helpers import data_kind
12+
13+
14+
def test_grdtrack_input_dataframe_and_dataarray():
15+
"""
16+
Run grdtrack by passing in a pandas.DataFrame and xarray.DataArray as inputs
17+
"""
18+
dataframe = load_ocean_ridge_points()
19+
dataarray = load_east_pacific_rise_grid()
20+
21+
output = grdtrack(table=dataframe, grid=dataarray)
22+
assert isinstance(output, pd.DataFrame)
23+
assert output.columns.to_list() == ["longitude", "latitude", "z_"]
24+
assert output.iloc[0].to_list() == [-110.9536, -42.2489, -2950.49576833]
25+
26+
return output
27+
28+
29+
def test_grdtrack_input_wrong_kind_of_table():
30+
"""
31+
Run grdtrack using table input that is not a pandas.DataFrame (matrix)
32+
"""
33+
dataframe = load_ocean_ridge_points()
34+
invalid_table = dataframe.longitude.to_xarray()
35+
dataarray = load_east_pacific_rise_grid()
36+
37+
assert data_kind(invalid_table) == "grid"
38+
with pytest.raises(GMTInvalidInput):
39+
grdtrack(table=invalid_table, grid=dataarray)
40+
41+
42+
def test_grdtrack_input_wrong_kind_of_grid():
43+
"""
44+
Run grdtrack using grid input that is not an xarray.DataArray (grid)
45+
"""
46+
dataframe = load_ocean_ridge_points()
47+
dataarray = load_east_pacific_rise_grid()
48+
invalid_grid = dataarray.to_dataset()
49+
50+
assert data_kind(invalid_grid) == "matrix"
51+
with pytest.raises(GMTInvalidInput):
52+
grdtrack(table=dataframe, grid=invalid_grid)
53+
54+
55+
def test_grdtrack_newcolname_setting():
56+
"""
57+
Run grdtrack by passing in a non-default newcolname parameter setting
58+
"""
59+
dataframe = load_ocean_ridge_points()
60+
dataarray = load_east_pacific_rise_grid()
61+
62+
output = grdtrack(table=dataframe, grid=dataarray, newcolname="bathymetry")
63+
assert output.columns.to_list() == ["longitude", "latitude", "bathymetry"]
64+
assert output.iloc[0].to_list() == [-110.9536, -42.2489, -2950.49576833]
65+
66+
return output

0 commit comments

Comments
 (0)