Tree Structures#
UXarry supports two different tree structures, BallTree and KDTree. These trees are tailored for neighbor searches, making them useful for spatial data queries.
import uxarray as ux
For this example we will be using a UGRID meshfile
grid_path = "../../test/meshfiles/ugrid/quad-hexagon/grid.nc"
uxgrid = ux.open_grid(grid_path)
BallTree#
UXarray BallTree
is built off of sklearn.neighbors.BallTree. A BallTree data structure organizes points in a multi-dimensional space into a tree of spheres. It is highly efficient for higher-dimensional data and for fast queries.
Parameters#
The BallTree
class can be accessed through the Grid.get_ball_tree(coordinates, coordinate_system, distance_metric, reconstruct)
method, which takes in the following parameters:
coordinates
allows us to specify what nodes to build the tree on. We can choose fromnodes
,face_centers
, oredge_centers
. Each one will change how the tree is built.nodes
builds the tree from the corner nodes of the grid,face_centers
builds the tree from the face centers of each face, andedge_centers
builds from the center of each edge in the grid. The default is to build fromnodes
.coordinate_system
specifies the tree’s coordinate type when constructing. We can use eithercartesian
, which uses the(x, y, z)
coordinate system, orspherical
, which uses the(lat, lon)
coordinate system. The default parameter isspherical
.distance_metric
relates to the distance computation, typically returned as a distance when querying for neighbors. There are a large number of options for us to use. A list of these options can be found here. An important note here is that some distance metrics aren’t compatible with some coordinate systems.BallTree
uses the haversine distance as default, which will only work with spherical coordinates and not with cartesian. The default parameter ishaversine
.reconstruct
is a bool variable that allows the user to reconstruct the tree. As default for performance, if a user callsget_ball_tree
and a tree has already been created, it will simply use that one. Ifreconstruct
is set toTrue
, it will override this and reconstruct the tree. The default parameter isFalse
.
Constructing a BallTree#
We can store the BallTree data structure in a variable, which allows us to access the tree in a simple way for queries.
ball_tree = uxgrid.get_ball_tree(
coordinates="nodes",
coordinate_system="spherical",
distance_metric="haversine",
reconstruct="False",
)
Query#
Now we can use that variable to query for the distance and indexes of the nearest neigbhors. The first parameter is the point from which to do the search. return_distance
allows us to choose to return the distance of the neighbors, and k
controls how many neighbors to find.
d, ind = ball_tree.query([0.0, 0.0], return_distance=True, k=1)
If we don’t plan on using the tree for other things in the future we can skip the extra step and query right away
d, ind = uxgrid.get_ball_tree(
coordinates="nodes",
coordinate_system="spherical",
distance_metric="haversine",
reconstruct="True",
).query([0.0, 0.0], return_distance=True, k=1)
Query Radius#
We can also query the tree using a radius search, instead of a nearest neighbor search. This allows us to get all points within a certain radius of a specific point. For spherical coordinates, the radius is in units of degrees, and for cartesian coordinates, the radius is in meters.
d, ind = ball_tree.query_radius([0.0, 0.0], r=5, return_distance=True)
KDTree#
The KDTree structure is a binary search tree useful for low-dimensional data. Its implementation is almost identical to BallTree, and the parameters are identical. An important note is that the different allowed inputs for distance_metric
change between the trees. For KDTree allowed distance_metrics
can be found here. We can call it using get_kd_tree()
. Generally, KDTree is going to be slower than BallTree, and it is recommended to use BallTree for most im
kd_tree = uxgrid.get_kd_tree()
query()
and query_radius()
work identically to the BallTree.
kd_tree.query([1.0, 0.0, 0.0], k=1)
(array(0.00104681), array(0))
kd_tree.query_radius([1.0, 0.0, 0.0], r=5, return_distance=True)
(array([0.00104681, 0.00335129, 0.00454262, 0.00477313, 0.00355618,
0.00179975, 0.00519071, 0.00539414, 0.00562861, 0.00646756,
0.0066315 , 0.00730387, 0.00334007, 0.00563384, 0.00612996,
0.00436176]),
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]))