-
Notifications
You must be signed in to change notification settings - Fork 228
Wrap nearneighbor #1379
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
Merged
weiji14
merged 29 commits into
GenericMappingTools:main
from
JamieJQuinn:feature/implement-nearneighbour
Sep 23, 2021
Merged
Wrap nearneighbor #1379
Changes from 19 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
3777b07
Add implementation of nearneighbor
JamieJQuinn 0ef1683
Improve formatting
JamieJQuinn 1fccc07
Add nearneighbor to API index
JamieJQuinn 91c1707
Apply suggestions from code review
JamieJQuinn 51fa503
Fix unused import
JamieJQuinn 0fbc83a
Update pygmt/src/nearneighbor.py
JamieJQuinn 4ae19cb
Add similar figure to that found in GMTs nearneighbor documentation
JamieJQuinn cedcda2
Don't use which in test
JamieJQuinn a4bb96e
Name test more appropriately
JamieJQuinn fc43c85
Add newer common options
JamieJQuinn 57aba71
Update pygmt/src/nearneighbor.py
JamieJQuinn 620e99a
Remove unused import
JamieJQuinn 62d4522
Apply suggestions from code review
weiji14 e09981f
Merge branch 'main' into feature/implement-nearneighbour
weiji14 c22ebb0
Fix lint errors
weiji14 38cb0f7
Set incols (i) to use sequence_comma
weiji14 dcdf379
Merge branch 'main' into feature/implement-nearneighbour
weiji14 d09d63d
Remove test_nearneighbor_input_xy_no_z
weiji14 3e33f3a
Ignore flake8 error using noqa W505
weiji14 b7fb6a9
Merge branch 'main' into feature/implement-nearneighbour
weiji14 6118d23
Fix incorrect indentation
weiji14 f344cf5
Shorten line length to under 100 using the dev image
weiji14 b187b4c
Merge branch 'main' into feature/implement-nearneighbour
weiji14 a0da121
Apply suggestions from code review
weiji14 3349396
Merge two tests using pytest parametrize
weiji14 758e8bb
Merge branch 'main' into feature/implement-nearneighbour
weiji14 523e5ec
Remove unused GMTInvalidInput import
weiji14 281c49d
Merge branch 'main' into feature/implement-nearneighbour
weiji14 3857cf9
Typo fixes
weiji14 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,7 @@ | |
grdtrack, | ||
info, | ||
makecpt, | ||
nearneighbor, | ||
sphdistance, | ||
surface, | ||
which, | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
""" | ||
nearneighbor - Grid table data using a "Nearest neighbor" algorithm | ||
""" | ||
|
||
from pygmt.clib import Session | ||
from pygmt.exceptions import GMTInvalidInput | ||
from pygmt.helpers import ( | ||
GMTTempFile, | ||
build_arg_string, | ||
data_kind, | ||
fmt_docstring, | ||
kwargs_to_strings, | ||
use_alias, | ||
) | ||
from pygmt.io import load_dataarray | ||
|
||
|
||
@fmt_docstring | ||
@use_alias( | ||
E="empty", | ||
G="outgrid", | ||
I="spacing", | ||
N="sectors", | ||
R="region", | ||
S="search_radius", | ||
V="verbose", | ||
a="aspatial", | ||
b="binary", | ||
d="nodata", | ||
e="find", | ||
f="coltypes", | ||
g="gap", | ||
h="header", | ||
i="incols", | ||
r="registration", | ||
w="wrap", | ||
) | ||
@kwargs_to_strings(R="sequence", i="sequence_comma") | ||
def nearneighbor(data=None, x=None, y=None, z=None, **kwargs): | ||
r""" | ||
Grid table data using a "Nearest neighbor" algorithm | ||
|
||
**nearneighbor** reads arbitrarily located (*x,y,z*\ [,\ *w*]) triples | ||
[quadruplets] and uses a nearest neighbor algorithm to assign a weighted | ||
average value to each node that has one or more data points within a search | ||
radius centered on the node with adequate coverage across a subset of the | ||
chosen sectors. The node value is computed as a weighted mean of the | ||
nearest point from each sector inside the search radius. The weighting | ||
function and the averaging used is given by: | ||
|
||
.. math:: | ||
w(r_i) = \frac{{w_i}}{{1 + d(r_i) ^ 2}}, | ||
\quad d(r) = \frac {{3r}}{{R}}, | ||
\quad \bar{{z}} = \frac{{\sum_i^n w(r_i) z_i}}{{\sum_i^n w(r_i)}} | ||
|
||
where :math:`n` is the number of data points that satisfy the selection | ||
criteria and :math:`r_i` is the distance from the node to the *i*'th data | ||
point. If no data weights are supplied then :math:`w_i = 1`. | ||
|
||
.. figure:: https://docs.generic-mapping-tools.org/latest/_images/GMT_nearneighbor.png # noqa: W505 | ||
:width: 300 px | ||
:align: center | ||
|
||
Search geometry includes the search radius (R) which limits the points | ||
considered and the number of sectors (here 4), which restricts how | ||
points inside the search radius contribute to the value at the node. | ||
Only the closest point in each sector (red circles) contribute to the | ||
weighted estimate. | ||
|
||
Takes a matrix, xyz triples, or a file name as input. | ||
|
||
Must provide either ``data`` or ``x``, ``y``, and ``z``. | ||
|
||
Full option list at :gmt-docs:`nearneighbor.html` | ||
|
||
{aliases} | ||
|
||
Parameters | ||
---------- | ||
data : 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}. | ||
x/y/z : 1d arrays | ||
Arrays of x and y coordinates and values z of the data points. | ||
|
||
{I} | ||
|
||
{R} | ||
|
||
search_radius : str | ||
Sets the search radius that determines which data points are considered | ||
close to a node. | ||
|
||
outgrid : str | ||
Optional. The file name for the output netcdf file with extension .nc | ||
to store the grid in. | ||
|
||
empty : str | ||
Optional. Set the value assigned to empty nodes. Defaults to NaN. | ||
|
||
sectors : str | ||
*sectors*\ [**+m**\ *min_sectors*]\|\ **n**. | ||
Optional. The circular search area centered on each node is divided | ||
into *sectors* sectors. Average values will only be computed if there | ||
is *at least* one value inside each of at least *min_sectors* of the | ||
sectors for a given node. Nodes that fail this test are assigned the | ||
value NaN (but see ``empty``). If +m is omitted then *min_sectors* is | ||
set to be at least 50% of *sectors* (i.e., rounded up to next integer) | ||
[Default is a quadrant search with 100% coverage, i.e., *sectors* = | ||
*min_sectors* = 4]. Note that only the nearest value per sector enters | ||
into the averaging; the more distant points are ignored. Alternatively, | ||
use ``sectors="n"`` to call GDALʻs nearest neighbor algorithm instead. | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
{V} | ||
{a} | ||
{b} | ||
{d} | ||
{e} | ||
{f} | ||
{g} | ||
{h} | ||
{i} | ||
{r} | ||
{w} | ||
|
||
Returns | ||
------- | ||
ret: xarray.DataArray or None | ||
Return type depends on whether the ``outgrid`` parameter is set: | ||
|
||
- :class:`xarray.DataArray`: if ``outgrid`` is not set | ||
- None if ``outgrid`` is set (grid output will be stored in file set by | ||
``outgrid``) | ||
""" | ||
|
||
kind = data_kind(data, x, y, z) | ||
if kind == "vectors" and z is None: | ||
raise GMTInvalidInput("Must provide z with x and y.") | ||
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=data, x=x, y=y, z=z | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
with table_context as infile: | ||
if "G" not in kwargs.keys(): # if outgrid is unset, output to tmpfile | ||
kwargs.update({"G": tmpfile.name}) | ||
outgrid = kwargs["G"] | ||
arg_str = " ".join([infile, build_arg_string(kwargs)]) | ||
lib.call_module(module="nearneighbor", args=arg_str) | ||
|
||
return load_dataarray(outgrid) if outgrid == tmpfile.name else None |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
""" | ||
Tests for nearneighbor. | ||
""" | ||
import os | ||
|
||
import numpy.testing as npt | ||
import pytest | ||
JamieJQuinn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import xarray as xr | ||
from pygmt import nearneighbor | ||
from pygmt.datasets import load_sample_bathymetry | ||
from pygmt.exceptions import GMTInvalidInput | ||
from pygmt.helpers import GMTTempFile, data_kind | ||
|
||
|
||
@pytest.fixture(scope="module", name="ship_data") | ||
def fixture_ship_data(): | ||
""" | ||
Load the data from the sample bathymetry dataset. | ||
""" | ||
return load_sample_bathymetry() | ||
|
||
|
||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def test_nearneighbor_input_file(): | ||
""" | ||
Run nearneighbor by passing in a filename. | ||
""" | ||
output = nearneighbor( | ||
data="@tut_ship.xyz", | ||
spacing="5m", | ||
region=[245, 255, 20, 30], | ||
search_radius="10m", | ||
) | ||
assert isinstance(output, xr.DataArray) | ||
assert output.gmt.registration == 0 # Gridline registration | ||
assert output.gmt.gtype == 0 # Cartesian type | ||
JamieJQuinn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert output.shape == (121, 121) | ||
npt.assert_allclose(output.mean(), -2378.2385) | ||
return output | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def test_nearneighbor_input_numpy_array(ship_data): | ||
""" | ||
Run nearneighbor by passing in a numpy array into data. | ||
""" | ||
data = ship_data.values # convert pandas.DataFrame to numpy.ndarray | ||
output = nearneighbor( | ||
data=data, spacing="5m", region=[245, 255, 20, 30], search_radius="10m" | ||
) | ||
assert isinstance(output, xr.DataArray) | ||
JamieJQuinn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert output.shape == (121, 121) | ||
npt.assert_allclose(output.mean(), -2378.2385) | ||
return output | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def test_nearneighbor_input_xyz(ship_data): | ||
""" | ||
Run nearneighbor by passing in x, y, z numpy.ndarrays individually. | ||
""" | ||
output = nearneighbor( | ||
x=ship_data.longitude, | ||
y=ship_data.latitude, | ||
z=ship_data.bathymetry, | ||
spacing="5m", | ||
region=[245, 255, 20, 30], | ||
search_radius="10m", | ||
) | ||
assert isinstance(output, xr.DataArray) | ||
JamieJQuinn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert output.shape == (121, 121) | ||
npt.assert_allclose(output.mean(), -2378.2385) | ||
return output | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def test_nearneighbor_wrong_kind_of_input(ship_data): | ||
""" | ||
Run nearneighbor using grid input that is not file/matrix/vectors. | ||
""" | ||
data = ship_data.bathymetry.to_xarray() # convert pandas.Series to xarray.DataArray | ||
assert data_kind(data) == "grid" | ||
with pytest.raises(GMTInvalidInput): | ||
nearneighbor( | ||
data=data, spacing="5m", region=[245, 255, 20, 30], search_radius="10m" | ||
) | ||
|
||
|
||
def test_nearneighbor_with_outgrid_param(ship_data): | ||
""" | ||
Run nearneighbor with the -Goutputfile.nc parameter. | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
data = ship_data.values # convert pandas.DataFrame to numpy.ndarray | ||
with GMTTempFile() as tmpfile: | ||
output = nearneighbor( | ||
data=data, | ||
spacing="5m", | ||
region=[245, 255, 20, 30], | ||
outgrid=tmpfile.name, | ||
search_radius="10m", | ||
) | ||
assert output is None # check that output is None since outgrid is set | ||
assert os.path.exists(path=tmpfile.name) # check that outgrid exists at path | ||
with xr.open_dataarray(tmpfile.name) as grid: | ||
assert isinstance(grid, xr.DataArray) # ensure netcdf grid loads ok | ||
assert grid.shape == (121, 121) | ||
npt.assert_allclose(grid.mean(), -2378.2385) | ||
return output | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.