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

Wrap triangulate #731

Merged
merged 38 commits into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e2f66fa
Wrap triangulate
weiji14 Dec 14, 2020
e56daee
Merge branch 'main' into gridding/triangulate
weiji14 Aug 10, 2021
3f4090e
Refactor triangulate to use virtualfile_from_data
weiji14 Aug 11, 2021
9abefa6
Merge branch 'main' into gridding/triangulate
weiji14 Sep 3, 2021
bd6bf57
Refactor triangulate implementation to use pygmt.io.load_dataarray
weiji14 Sep 3, 2021
cceb2a6
Alias binary(b), nodata(d), find(e), coltypes(f), header(h), incols(i)
weiji14 Sep 3, 2021
cfb9572
Apply suggestions from code review
weiji14 Sep 10, 2021
c8c779b
Merge branch 'main' into gridding/triangulate
weiji14 Sep 10, 2021
9e67eef
Wrap docstrings to 79 characters
weiji14 Sep 11, 2021
c90f27c
Merge branch 'main' into gridding/triangulate
weiji14 Mar 11, 2022
d00d347
Rename the parameter 'table' to 'data'
weiji14 Mar 11, 2022
df1b179
Refactor test_triangulate to use Table_5_11_mean.xyz instead of tut_ship
weiji14 Mar 12, 2022
0653be8
Refactor test_triangulate_with_outgrid to use xr.testing.assert_allclose
weiji14 Mar 12, 2022
01607b9
Refactor test_triangulate_input_xyz to use pd.testing.assert_frame_equal
weiji14 Mar 12, 2022
2ee8e17
Remove unused load_sample_bathymetry import
weiji14 Mar 12, 2022
0850d74
Implement regular_grid and delaunay_triples staticmethod for triangulate
weiji14 Mar 12, 2022
4db6812
Let list inputs to spacing (I) and incols (i) work
weiji14 Mar 12, 2022
05a3a08
Ensure triangulate.delaunay_triples output_type is valid
weiji14 Mar 12, 2022
4a42b1a
Autocorrect output_type to 'file' if outfile parameter is set
weiji14 Mar 12, 2022
7ae5f6a
Prevent delaunay triples from setting header for non-file output
weiji14 Mar 12, 2022
39c85b0
Allow only str or None inputs to outgrid parameter
weiji14 Mar 12, 2022
ffa699a
Merge branch 'main' into gridding/triangulate
weiji14 Mar 12, 2022
9c17913
Format test_regular_grid_invalid_format docstring
weiji14 Mar 12, 2022
ed0ba2c
Revert "Prevent delaunay triples from setting header for non-file out…
weiji14 Mar 12, 2022
d69c463
Remove region (R) parameter from delaunay_triples
weiji14 Mar 12, 2022
4417da1
Use gmt get GMT_TRIANGULATE to check whether Watson or Shewchuk is used
weiji14 Mar 12, 2022
d88f2bf
Merge branch 'main' into gridding/triangulate
weiji14 Mar 13, 2022
02ac1d8
State that Shewchuk is the default triangulation algorithm
weiji14 Mar 13, 2022
6fa45e6
Merge branch 'main' into gridding/triangulate
weiji14 Mar 13, 2022
9996fe8
Update docstring with better new formatting conventions
weiji14 Mar 13, 2022
b278606
A few more docstring fixes
weiji14 Mar 13, 2022
d3fb5c6
Handle output types better
weiji14 Mar 13, 2022
1f53beb
Format fix
weiji14 Mar 13, 2022
00d3b71
More triangulate docstring modifications
weiji14 Mar 13, 2022
4ce402b
Wrap line to 79 characters
weiji14 Mar 13, 2022
3006668
Actually document the output_type parameter for delaunay_triples
weiji14 Mar 13, 2022
085620e
Remove kwargs_to_strings decorator from methods
weiji14 Mar 14, 2022
d221c96
Merge branch 'main' into gridding/triangulate
weiji14 Mar 14, 2022
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
1 change: 1 addition & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Operations on tabular data:
blockmean
blockmedian
surface
triangulate

Operations on grids:

Expand Down
1 change: 1 addition & 0 deletions pygmt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
info,
makecpt,
surface,
triangulate,
which,
x2sys_cross,
x2sys_init,
Expand Down
1 change: 1 addition & 0 deletions pygmt/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from pygmt.src.subplot import set_panel, subplot
from pygmt.src.surface import surface
from pygmt.src.text import text_ as text # "text" is an argument within "text_"
from pygmt.src.triangulate import triangulate
from pygmt.src.velo import velo
from pygmt.src.which import which
from pygmt.src.wiggle import wiggle
Expand Down
116 changes: 116 additions & 0 deletions pygmt/src/triangulate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""
triangulate - Delaunay triangulation or Voronoi partitioning and gridding of
Cartesian data.
"""
import pandas as pd
from pygmt.clib import Session
from pygmt.helpers import (
GMTTempFile,
build_arg_string,
fmt_docstring,
kwargs_to_strings,
use_alias,
)
from pygmt.io import load_dataarray


@fmt_docstring
@use_alias(
G="outgrid",
I="spacing",
J="projection",
R="region",
V="verbose",
b="binary",
d="nodata",
e="find",
f="coltypes",
h="header",
i="incols",
r="registration",
)
@kwargs_to_strings(R="sequence")
def triangulate(table=None, x=None, y=None, z=None, **kwargs):
weiji14 marked this conversation as resolved.
Show resolved Hide resolved
"""
Delaunay triangulation or Voronoi partitioning and gridding of Cartesian
data.

Triangulate reads in x,y[,z] data and performs Delaunay triangulation,
i.e., it find how the points should be connected to give the most
equilateral triangulation possible. If a map projection (give *region*
weiji14 marked this conversation as resolved.
Show resolved Hide resolved
and *projection*) is chosen then it is applied before the triangulation
is calculated.

Must provide either *table* or *x*, *y*, and *z*.
weiji14 marked this conversation as resolved.
Show resolved Hide resolved

Full option list at :gmt-docs:`triangulate.html`

{aliases}

Parameters
----------
x/y/z : np.ndarray
Arrays of x and y coordinates and values z of the data points.
table : str or {table-like}
Pass in (x, y, z) or (longitude, latitude, elevation) values by
providing a file name to an ASCII data table, a 2D
{table-classes}.
projection : str
Select map projection.
region
``'xmin/xmax/ymin/ymax[+r][+uunit]'``.
Specify the region of interest.
weiji14 marked this conversation as resolved.
Show resolved Hide resolved
spacing : str
``'xinc[unit][+e|n][/yinc[unit][+e|n]]'``.
x_inc [and optionally y_inc] is the grid spacing.
weiji14 marked this conversation as resolved.
Show resolved Hide resolved
outgrid : bool or str
Use triangulation to grid the data onto an even grid (specified with
*region* and *spacing*). Set to True, or pass in the name of the output
weiji14 marked this conversation as resolved.
Show resolved Hide resolved
grid file. The interpolation is performed in the original coordinates,
so if your triangles are close to the poles you are better off
projecting all data to a local coordinate system before using
*triangulate* (this is true of all gridding routines) or instead
select *sphtriangulate*.
{V}
{b}
{d}
{e}
{f}
{h}
{i}
{r}
Only valid with *outgrid*.
weiji14 marked this conversation as resolved.
Show resolved Hide resolved

Returns
-------
ret: xarray.DataArray or None
weiji14 marked this conversation as resolved.
Show resolved Hide resolved
Return type depends on whether the outgrid parameter is set:

- pandas.DataFrame if outgrid is None (default)
- xarray.DataArray if outgrid is True
- None if outgrid is a str (grid output will be stored in outgrid)
weiji14 marked this conversation as resolved.
Show resolved Hide resolved
"""
with GMTTempFile(suffix=".nc") as tmpfile:
with Session() as lib:
# Choose how data will be passed into the module
table_context = lib.virtualfile_from_data(
check_kind="vector", data=table, x=x, y=y, z=z
)
with table_context as infile:
if "G" not in kwargs: # table output if outgrid is unset
kwargs.update({">": tmpfile.name})
else: # NetCDF or xarray.DataArray output if outgrid is set
if (
kwargs["G"] is True
): # xarray.DataArray output if outgrid is True
kwargs.update({"G": tmpfile.name})
outgrid = kwargs["G"]
arg_str = " ".join([infile, build_arg_string(kwargs)])
lib.call_module(module="triangulate", args=arg_str)

try:
result = load_dataarray(outgrid) if outgrid == tmpfile.name else None
except UnboundLocalError: # if outgrid unset, return pd.DataFrame
result = pd.read_csv(tmpfile.name, sep="\t", header=None)

return result
99 changes: 99 additions & 0 deletions pygmt/tests/test_triangulate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""
Tests for triangulate.
"""
import os

import pandas as pd
import pytest
import xarray as xr
from pygmt import triangulate
from pygmt.datasets import load_sample_bathymetry
from pygmt.exceptions import GMTInvalidInput
from pygmt.helpers import GMTTempFile, data_kind


@pytest.fixture(scope="module", name="dataframe")
def fixture_dataframe():
"""
Load the table data from the sample bathymetry dataset.
"""
return load_sample_bathymetry()
maxrjones marked this conversation as resolved.
Show resolved Hide resolved


def test_triangulate_input_file():
"""
Run triangulate by passing in a filename.
"""
output = triangulate(table="@tut_ship.xyz")
assert isinstance(output, pd.DataFrame)
assert output.shape == (161935, 3)


def test_triangulate_input_data_array(dataframe):
"""
Run triangulate by passing in a numpy array into data.
"""
data = dataframe.to_numpy()
output = triangulate(table=data)
assert isinstance(output, pd.DataFrame)
assert output.shape == (161935, 3)


def test_triangulate_input_xyz(dataframe):
"""
Run triangulate by passing in x, y, z numpy.ndarrays individually.
"""
output = triangulate(
x=dataframe.longitude,
y=dataframe.latitude,
z=dataframe.bathymetry,
)
assert isinstance(output, pd.DataFrame)
assert output.shape == (161935, 3)


def test_triangulate_input_xy_no_z(dataframe):
"""
Run triangulate by passing in x and y, but no z.
"""
output = triangulate(x=dataframe.longitude, y=dataframe.latitude)
assert isinstance(output, pd.DataFrame)
assert output.shape == (161935, 3)


def test_triangulate_wrong_kind_of_input(dataframe):
"""
Run triangulate using grid input that is not file/matrix/vectors.
"""
data = dataframe.bathymetry.to_xarray() # convert pandas.Series to xarray.DataArray
assert data_kind(data) == "grid"
with pytest.raises(GMTInvalidInput):
triangulate(table=data)


def test_triangulate_with_outgrid_true(dataframe):
"""
Run triangulate with outgrid=True and see it load into an xarray.DataArray.
"""
data = dataframe.to_numpy()
output = triangulate(
table=data, spacing="5m", region=[245, 255, 20, 30], outgrid=True
)
assert isinstance(output, xr.DataArray)
assert output.shape == (121, 121)


def test_triangulate_with_outgrid_param(dataframe):
"""
Run triangulate with the -Goutputfile.nc parameter.
"""
data = dataframe.to_numpy()
with GMTTempFile(suffix=".nc") as tmpfile:
output = triangulate(
table=data, spacing="5m", region=[245, 255, 20, 30], outgrid=tmpfile.name
)
assert output is None # check that output is None since outgrid is set
assert os.path.exists(path=tmpfile.name) # check that outgrid exists
with xr.open_dataarray(tmpfile.name) as grid:
assert isinstance(grid, xr.DataArray) # ensure netcdf grid loads ok
assert grid.shape == (121, 121)