Skip to content

Commit

Permalink
Update quadtree spatial join docstrings (#797)
Browse files Browse the repository at this point in the history
* Aligns docstrings between C++ and Python
* Adds missing methods to Python docs
* Fixes #733 
* Fixes #397

Authors:
  - Paul Taylor (https://github.com/trxcllnt)

Approvers:
  - Mark Harris (https://github.com/harrism)
  - H. Thomson Comer (https://github.com/thomcom)

URL: #797
  • Loading branch information
trxcllnt authored Nov 22, 2022
1 parent 19e7661 commit da7d3a4
Show file tree
Hide file tree
Showing 14 changed files with 170 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ inline std::pair<uint32_t, uint32_t> remove_unqualified_quads(
}

/**
* @brief Construct the `is_parent_node` vector indicating if a quadrant is a parent or leaf node
* @brief Construct the `is_internal_node` vector indicating if a quadrant is a parent or leaf node
* @param quad_point_count
* @param num_parent_nodes
* @param num_valid_nodes
Expand All @@ -314,31 +314,33 @@ inline rmm::device_uvector<bool> construct_non_leaf_indicator(
{
//
// Construct the indicator output column
rmm::device_uvector<bool> is_parent_node(num_valid_nodes, stream, mr);
rmm::device_uvector<bool> is_internal_node(num_valid_nodes, stream, mr);

// line 6 of algorithm in Fig. 5 in ref.
thrust::transform(rmm::exec_policy(stream),
quad_point_count.begin(),
quad_point_count.begin() + num_parent_nodes,
is_parent_node.begin(),
is_internal_node.begin(),
thrust::placeholders::_1 > max_size);

// line 7 of algorithm in Fig. 5 in ref.
thrust::replace_if(rmm::exec_policy(stream),
quad_point_count.begin(),
quad_point_count.begin() + num_parent_nodes,
is_parent_node.begin(),
is_internal_node.begin(),
thrust::placeholders::_1,
0);

if (num_valid_nodes > num_parent_nodes) {
// zero-fill the rest of the indicator column because
// device_memory_resources aren't required to initialize allocations
thrust::fill(
rmm::exec_policy(stream), is_parent_node.begin() + num_parent_nodes, is_parent_node.end(), 0);
thrust::fill(rmm::exec_policy(stream),
is_internal_node.begin() + num_parent_nodes,
is_internal_node.end(),
0);
}

return is_parent_node;
return is_internal_node;
}

} // namespace detail
Expand Down
24 changes: 12 additions & 12 deletions cpp/include/cuspatial/experimental/detail/point_quadtree.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ inline point_quadtree make_quad_tree(rmm::device_uvector<uint32_t>& keys,

// Construct the indicator output column.
// line 6 and 7 of algorithm in Fig. 5 in ref.
auto is_parent_node = construct_non_leaf_indicator(
auto is_internal_node = construct_non_leaf_indicator(
quad_point_count, num_parent_nodes, num_valid_nodes, max_size, mr, stream);

// Construct the offsets output column
Expand All @@ -80,13 +80,13 @@ inline point_quadtree make_quad_tree(rmm::device_uvector<uint32_t>& keys,
// revision to line 8: adjust quad_point_pos based on last-level z-order
// code
auto quad_point_pos = compute_flattened_first_point_positions(
keys, levels, quad_point_count, is_parent_node, num_valid_nodes, max_depth, stream);
keys, levels, quad_point_count, is_internal_node, num_valid_nodes, max_depth, stream);

// line 9 of algorithm in Fig. 5 in ref.
thrust::replace_if(rmm::exec_policy(stream),
quad_child_count.begin(),
quad_child_count.begin() + num_valid_nodes,
is_parent_node.begin(),
is_internal_node.begin(),
!thrust::placeholders::_1,
0);

Expand All @@ -100,14 +100,14 @@ inline point_quadtree make_quad_tree(rmm::device_uvector<uint32_t>& keys,

auto& offsets = quad_child_pos;
auto offsets_iter = thrust::make_zip_iterator(
is_parent_node.begin(), quad_child_pos.begin(), quad_point_pos.begin());
is_internal_node.begin(), quad_child_pos.begin(), quad_point_pos.begin());

// copy each value in `is_parent_node` from `quad_child_pos` if true, else `quad_point_pos`
// copy each value in `is_internal_node` from `quad_child_pos` if true, else `quad_point_pos`
thrust::transform(rmm::exec_policy(stream),
offsets_iter,
offsets_iter + num_valid_nodes,
offsets.begin(),
// return is_parent_node ? lhs : rhs
// return is_internal_node ? lhs : rhs
[] __device__(auto const& t) {
return thrust::get<0>(t) ? thrust::get<1>(t) : thrust::get<2>(t);
});
Expand All @@ -117,8 +117,8 @@ inline point_quadtree make_quad_tree(rmm::device_uvector<uint32_t>& keys,

// Construct the lengths output column
rmm::device_uvector<uint32_t> lengths(num_valid_nodes, stream, mr);
// copy `quad_child_count` if `is_parent_node` is true, otherwise `quad_point_count`
auto lengths_iter = thrust::make_zip_iterator(is_parent_node.begin(), //
// copy `quad_child_count` if `is_internal_node` is true, otherwise `quad_point_count`
auto lengths_iter = thrust::make_zip_iterator(is_internal_node.begin(), //
quad_child_count.begin(),
quad_point_count.begin());
thrust::transform(rmm::exec_policy(stream),
Expand All @@ -141,7 +141,7 @@ inline point_quadtree make_quad_tree(rmm::device_uvector<uint32_t>& keys,
return {
std::move(keys),
std::move(levels),
std::move(is_parent_node),
std::move(is_internal_node),
std::move(lengths),
std::move(offsets),
};
Expand All @@ -157,7 +157,7 @@ inline point_quadtree make_leaf_tree(rmm::device_uvector<uint32_t>& keys,
rmm::mr::device_memory_resource* mr)
{
rmm::device_uvector<uint8_t> levels(num_top_quads, stream, mr);
rmm::device_uvector<bool> is_parent_node(num_top_quads, stream, mr);
rmm::device_uvector<bool> is_internal_node(num_top_quads, stream, mr);
rmm::device_uvector<uint32_t> offsets(num_top_quads, stream, mr);

// only keep the front of the keys list
Expand All @@ -171,15 +171,15 @@ inline point_quadtree make_leaf_tree(rmm::device_uvector<uint32_t>& keys,
thrust::fill(rmm::exec_policy(stream), levels.begin(), levels.end(), 0);

// Quad node indicators are false for leaf nodes
thrust::fill(rmm::exec_policy(stream), is_parent_node.begin(), is_parent_node.end(), false);
thrust::fill(rmm::exec_policy(stream), is_internal_node.begin(), is_internal_node.end(), false);

// compute offsets from lengths
thrust::exclusive_scan(rmm::exec_policy(stream), lengths.begin(), lengths.end(), offsets.begin());

return {
std::move(keys),
std::move(levels),
std::move(is_parent_node),
std::move(is_internal_node),
std::move(lengths),
std::move(offsets),
};
Expand Down
10 changes: 5 additions & 5 deletions cpp/include/cuspatial/experimental/point_quadtree.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ struct point_quadtree {
// uint8_t vector of quadtree levels
rmm::device_uvector<uint8_t> level;
// bool vector indicating whether the node is a parent (true) or leaf (false) node
rmm::device_uvector<bool> is_parent_node;
// uint32_t vector for the number of child nodes (if is_parent_node), or number of points
rmm::device_uvector<bool> is_internal_node;
// uint32_t vector for the number of child nodes (if is_internal_node), or number of points
rmm::device_uvector<uint32_t> length;
// uint32_t vector for the first child position (if is_parent_node), or first point position
// uint32_t vector for the first child position (if is_internal_node), or first point position
rmm::device_uvector<uint32_t> offset;
};

Expand All @@ -53,8 +53,8 @@ struct point_quadtree {
*
* @see http://www.adms-conf.org/2019-camera-ready/zhang_adms19.pdf for details.
*
* @note `scale` is applied to (x - min.x) and (y - min.y) to convert coordinates into a Morton code
* in 2D space.
* @note 2D coordinates are converted into a 1D Morton code by dividing each x/y by the `scale`:
* (`(x - min_x) / scale` and `(y - min_y) / scale`).
* @note `max_depth` should be less than 16, since Morton codes are represented as `uint32_t`. The
* eventual number of levels may be less than `max_depth` if the number of points is small or
* `max_size` is large.
Expand Down
7 changes: 4 additions & 3 deletions cpp/include/cuspatial/point_quadtree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ namespace cuspatial {
* columns for a complete quadtree:
* key - UINT32 column of quad node keys
* level - UINT8 column of quadtree levels
* is_quad - BOOL8 column indicating whether the node is a quad (true) or leaf (false)
* length - UINT32 column for the number of child nodes (if is_quad), or number of points
* offset - UINT32 column for the first child position (if is_quad), or first point position
* is_internal_node - BOOL8 column indicating whether the node is a quad (true) or leaf (false)
* length - UINT32 column for the number of child nodes (if is_internal_node), or number of points
* offset - UINT32 column for the first child position (if is_internal_node), or first point
* position
*/
std::pair<std::unique_ptr<cudf::column>, std::unique_ptr<cudf::table>> quadtree_on_points(
cudf::column_view const& x,
Expand Down
27 changes: 16 additions & 11 deletions cpp/include/cuspatial/spatial_join.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@ namespace cuspatial {
/**
* @brief Search a quadtree for polygon or linestring bounding box intersections.
*
* @note `scale` is applied to (x - x_min) and (y - y_min) to convert coordinates into a Morton code
* in 2D space.
* @note `max_depth` should be less than 16, since Morton codes are represented as `uint32_t`.
*
* @param quadtree: cudf table representing a quadtree (key, level, is_quad, length, offset).
* @note 2D coordinates are converted into a 1D Morton code by dividing each x/y by the `scale`:
* (`(x - min_x) / scale` and `(y - min_y) / scale`).
* @note `max_depth` should be less than 16, since Morton codes are represented as `uint32_t`. The
* eventual number of levels may be less than `max_depth` if the number of points is small or
* `max_size` is large.
*
* @param quadtree: cudf table representing a quadtree (key, level, is_internal_node, length,
* offset).
* @param bbox: cudf table of bounding boxes as four columns (x_min, y_min, x_max, y_max).
* @param x_min The lower-left x-coordinate of the area of interest bounding box.
* @param x_max The upper-right x-coordinate of the area of interest bounding box.
Expand Down Expand Up @@ -83,7 +86,8 @@ std::unique_ptr<cudf::table> join_quadtree_and_bounding_boxes(
*
* @param poly_quad_pairs cudf table of (polygon, quadrant) index pairs returned by
* `cuspatial::join_quadtree_and_bounding_boxes`
* @param quadtree cudf table representing a quadtree (key, level, is_quad, length, offset).
* @param quadtree cudf table representing a quadtree (key, level, is_internal_node, length,
* offset).
* @param point_indices Sorted point indices returned by `cuspatial::quadtree_on_points`
* @param point_x x-coordinates of points to test
* @param point_y y-coordinates of points to test
Expand All @@ -104,8 +108,8 @@ std::unique_ptr<cudf::table> join_quadtree_and_bounding_boxes(
* polygon_offset - UINT32 column of polygon indices
* point_offset - UINT32 column of point indices
*
* @note The returned polygon and point indices are offsets into the poly_quad_pairs inputs and
* (point_x, point_y), respectively.
* @note The returned polygon and point indices are offsets into the `poly_quad_pairs` inputs and
* `point_indices`, respectively.
*
**/
std::unique_ptr<cudf::table> quadtree_point_in_polygon(
Expand All @@ -130,7 +134,8 @@ std::unique_ptr<cudf::table> quadtree_point_in_polygon(
*
* @param linestring_quad_pairs cudf table of (linestring, quadrant) index pairs returned by
* `cuspatial::join_quadtree_and_bounding_boxes`
* @param quadtree cudf table representing a quadtree (key, level, is_quad, length, offset).
* @param quadtree cudf table representing a quadtree (key, level, is_internal_node, length,
* offset).
* @param point_indices Sorted point indices returned by `cuspatial::quadtree_on_points`
* @param point_x x-coordinates of points to test
* @param point_y y-coordinates of points to test
Expand All @@ -153,8 +158,8 @@ std::unique_ptr<cudf::table> quadtree_point_in_polygon(
* distance - FLOAT or DOUBLE column (based on input point data type) of distances
* between each point and linestring
*
* @note The returned point and linestring indices are offsets into the (point_x, point_y) and
* linestring_quad_pairs inputs, respectively.
* @note The returned point and linestring indices are offsets into the `point_indices` and
* `linestring_quad_pairs` inputs, respectively.
*
**/
std::unique_ptr<cudf::table> quadtree_point_to_nearest_linestring(
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/indexing/construction/point_quadtree.cu
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ struct dispatch_construct_quadtree {
cols.push_back(std::make_unique<cudf::column>(
cudf::data_type{cudf::type_id::UINT8}, size, tree.level.release()));
cols.push_back(std::make_unique<cudf::column>(
cudf::data_type{cudf::type_id::BOOL8}, size, tree.is_parent_node.release()));
cudf::data_type{cudf::type_id::BOOL8}, size, tree.is_internal_node.release()));
cols.push_back(std::make_unique<cudf::column>(
cudf::data_type{cudf::type_id::UINT32}, size, tree.length.release()));
cols.push_back(std::make_unique<cudf::column>(
Expand Down
31 changes: 16 additions & 15 deletions cpp/src/join/detail/intersection.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ inline std::pair<cudf::size_type, cudf::size_type> find_intersections(
int8_t max_depth,
rmm::cuda_stream_view stream)
{
auto d_keys = cudf::column_device_view::create(quadtree.column(0), stream);
auto d_levels = cudf::column_device_view::create(quadtree.column(1), stream);
auto d_is_quad = cudf::column_device_view::create(quadtree.column(2), stream);
auto d_poly_x_min = cudf::column_device_view::create(poly_bbox.column(0), stream);
auto d_poly_y_min = cudf::column_device_view::create(poly_bbox.column(1), stream);
auto d_poly_x_max = cudf::column_device_view::create(poly_bbox.column(2), stream);
auto d_poly_y_max = cudf::column_device_view::create(poly_bbox.column(3), stream);
auto d_keys = cudf::column_device_view::create(quadtree.column(0), stream);
auto d_levels = cudf::column_device_view::create(quadtree.column(1), stream);
auto d_is_internal_node = cudf::column_device_view::create(quadtree.column(2), stream);
auto d_poly_x_min = cudf::column_device_view::create(poly_bbox.column(0), stream);
auto d_poly_y_min = cudf::column_device_view::create(poly_bbox.column(1), stream);
auto d_poly_x_max = cudf::column_device_view::create(poly_bbox.column(2), stream);
auto d_poly_y_max = cudf::column_device_view::create(poly_bbox.column(3), stream);

thrust::transform(rmm::exec_policy(stream),
thrust::make_zip_iterator(node_indices, poly_indices),
Expand All @@ -107,13 +107,13 @@ inline std::pair<cudf::size_type, cudf::size_type> find_intersections(
y_min,
scale,
max_depth,
keys = *d_keys,
levels = *d_levels,
is_quad = *d_is_quad,
poly_x_mins = *d_poly_x_min,
poly_y_mins = *d_poly_y_min,
poly_x_maxs = *d_poly_x_max,
poly_y_maxs = *d_poly_y_max] __device__(auto const& node_and_poly) {
keys = *d_keys,
levels = *d_levels,
is_internal_node = *d_is_internal_node,
poly_x_mins = *d_poly_x_min,
poly_y_mins = *d_poly_y_min,
poly_x_maxs = *d_poly_x_max,
poly_y_maxs = *d_poly_y_max] __device__(auto const& node_and_poly) {
auto& node = thrust::get<0>(node_and_poly);
auto& poly = thrust::get<1>(node_and_poly);
auto key = keys.element<uint32_t>(node);
Expand All @@ -137,7 +137,8 @@ inline std::pair<cudf::size_type, cudf::size_type> find_intersections(
return thrust::make_tuple(none_indicator, level, node, poly);
}
// otherwise return type = leaf_indicator (0) or quad_indicator (1)
return thrust::make_tuple(is_quad.element<uint8_t>(node), level, node, poly);
return thrust::make_tuple(
is_internal_node.element<uint8_t>(node), level, node, poly);
});

auto num_leaves = copy_leaf_intersections(node_pairs, node_pairs + num_pairs, leaf_pairs, stream);
Expand Down
16 changes: 8 additions & 8 deletions cpp/tests/experimental/indexing/point_quadtree_test.cu
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct QuadtreeOnPointIndexingTest : public ::testing::Test {
const int32_t max_size,
std::vector<uint32_t> const& expected_key,
std::vector<uint8_t> const& expected_level,
std::vector<bool> const& expected_is_parent_node,
std::vector<bool> const& expected_is_internal_node,
std::vector<uint32_t> const& expected_length,
std::vector<uint32_t> const& expected_offset)
{
Expand All @@ -40,23 +40,23 @@ struct QuadtreeOnPointIndexingTest : public ::testing::Test {

EXPECT_EQ(point_indices.size(), points.size());

auto& key_d = tree.key;
auto& level_d = tree.level;
auto& is_parent_node_d = tree.is_parent_node;
auto& length_d = tree.length;
auto& offset_d = tree.offset;
auto& key_d = tree.key;
auto& level_d = tree.level;
auto& is_internal_node_d = tree.is_internal_node;
auto& length_d = tree.length;
auto& offset_d = tree.offset;

EXPECT_EQ(key_d.size(), expected_key.size());
EXPECT_EQ(level_d.size(), expected_level.size());
EXPECT_EQ(is_parent_node_d.size(), expected_is_parent_node.size());
EXPECT_EQ(is_internal_node_d.size(), expected_is_internal_node.size());
EXPECT_EQ(length_d.size(), expected_length.size());
EXPECT_EQ(offset_d.size(), expected_offset.size());

using namespace cuspatial::test;

expect_vector_equivalent(expected_key, key_d);
expect_vector_equivalent(expected_level, level_d);
expect_vector_equivalent(expected_is_parent_node, is_parent_node_d);
expect_vector_equivalent(expected_is_internal_node, is_internal_node_d);
expect_vector_equivalent(expected_length, length_d);
expect_vector_equivalent(expected_offset, offset_d);
}
Expand Down
2 changes: 2 additions & 0 deletions docs/source/api_docs/spatial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ Measurement Functions

.. autofunction:: cuspatial.directed_hausdorff_distance
.. autofunction:: cuspatial.haversine_distance
.. autofunction:: cuspatial.pairwise_point_distance
.. autofunction:: cuspatial.pairwise_linestring_distance
.. autofunction:: cuspatial.pairwise_point_linestring_distance

Nearest Points Function
+++++++++++++++++++++++
Expand Down
8 changes: 7 additions & 1 deletion python/cuspatial/cuspatial/_lib/quadtree.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ cpdef quadtree_on_points(Column x, Column y,
Column.from_unique_ptr(move(result.first)),
data_from_unique_ptr(
move(result.second),
column_names=["key", "level", "is_quad", "length", "offset"]
column_names=[
"key",
"level",
"is_internal_node",
"length",
"offset"
]
)
)
3 changes: 3 additions & 0 deletions python/cuspatial/cuspatial/core/geoseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class GeoSeries(cudf.Series):
stored in the `GeoArrowBuffers` object, accessible with the `points`,
`multipoints`, `lines`, and `polygons` accessors.
Examples
--------
>>> from shapely.geometry import Point
import geopandas
import cuspatial
Expand Down
Loading

0 comments on commit da7d3a4

Please sign in to comment.