Skip to content

Commit

Permalink
GmMultiPolyIntersector for python - Changed traverse_line_segment to …
Browse files Browse the repository at this point in the history
…take 2 points instead of 4 args, removed __str__() and __repr__(), testing passing both lists and tuples, more type hints and docstrings
  • Loading branch information
mkennard-aquaveo committed Jan 4, 2024
1 parent 565748e commit fba3e81
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,14 @@ def setUp(self):
def _run_test(self, pt1, pt2, poly_points, polys, poly_ids, t_vals, pts, starting_id=1, query='covered_by'):
"""Runs the test."""
mpi = grid.geometry.MultiPolyIntersector(poly_points, polys, starting_id, query)
assert mpi.query == 'covered_by'

out_poly_ids, out_t_vals, out_pts = mpi.traverse_line_segment(pt1[0], pt1[1], pt2[0], pt2[1])
# Check some basic stuff
assert mpi.query == query
print(mpi.__str__())
print(mpi.__repr__())

# Traverse the line segment
out_poly_ids, out_t_vals, out_pts = mpi.traverse_line_segment(pt1, pt2)

# Check poly ids
assert len(out_poly_ids) == len(poly_ids)
Expand Down Expand Up @@ -65,14 +70,15 @@ def test_traverse_line_segment_1_out_out(self):
0-------------1
(0,0)
"""
# Use lists to prove that we can
pts = [[0, 0, 0], [10, 0, 0], [10, 10, 0], [0, 10, 0]]
polys = [[0, 1, 2, 3]]
expected_ids = (1, -1)
expected_t_vals = (0.08333333333333333, 0.91666666666666667)
expected_pts = ((0.0, 5.0, 0.0), (10.0, 5.0, 0.0))
self._run_test((-1, 5), (11, 5), pts, polys, expected_ids, expected_t_vals, expected_pts)
self._run_test([-1, 5], [11, 5], pts, polys, expected_ids, expected_t_vals, expected_pts)
polys = [[1, 2, 3, 4]]
self._run_test((-1, 5), (11, 5), pts, polys, expected_ids, expected_t_vals, expected_pts, 1, 'intersects')
self._run_test([-1, 5], [11, 5], pts, polys, expected_ids, expected_t_vals, expected_pts, 1, 'intersects')

def test_traverse_line_segment_1_out_in(self):
"""A test.
Expand All @@ -87,6 +93,7 @@ def test_traverse_line_segment_1_out_in(self):
0-------------1
(0,0)
"""
# Use tuples to prove that we can
pts = ((0, 0, 0), (10, 0, 0), (10, 10, 0), (0, 10, 0))
polys = [(0, 1, 2, 3)]
expected_ids = (1, -1)
Expand Down Expand Up @@ -115,8 +122,8 @@ def test_polygon_from_point(self):
4-------------5
(0,0) (20,0)
"""
pts = [[0, 10, 0], [10, 10, 0], [10, 20, 0], [0, 20, 0], [10, 0, 0], [20, 0, 0], [20, 10, 0]]
polys = [[0, 1, 2, 3], [4, 5, 6, 1]]
pts = [(0, 10, 0), (10, 10, 0), (10, 20, 0), (0, 20, 0), (10, 0, 0), (20, 0, 0), (20, 10, 0)]
polys = [(0, 1, 2, 3), (4, 5, 6, 1)]
mpi = grid.geometry.MultiPolyIntersector(pts, polys)
assert mpi.polygon_from_point((5, 5, 0)) == -1
assert mpi.polygon_from_point((5, 10, 0)) == 1
Expand Down
46 changes: 25 additions & 21 deletions _package/xms/grid/geometry/multi_poly_intersector.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class MultiPolyIntersector(object):
"""Intersects a line segment with any number of polygons in 2D and returns the polygons in order with t values."""

def __init__(self, points, polys, starting_id=1, query='covered_by', **kwargs):
def __init__(self, points, polys, starting_id=1, query='covered_by', **kwargs) -> None:
"""Constructor.
Args:
Expand All @@ -40,7 +40,7 @@ def __init__(self, points, polys, starting_id=1, query='covered_by', **kwargs):
raise ValueError('"instance" must be of type _xmsgrid.geometry.GmMultiPolyIntersector')
self._instance = kwargs['instance']

def __eq__(self, other):
def __eq__(self, other) -> bool:
"""Equality operator.
Args:
Expand All @@ -55,7 +55,7 @@ def __eq__(self, other):
return False
return other_instance == self._instance

def __ne__(self, other):
def __ne__(self, other) -> bool:
"""Equality operator.
Args:
Expand All @@ -67,40 +67,44 @@ def __ne__(self, other):
result = self.__eq__(other)
return not result

def __repr__(self):
"""Returns a string representation of the MultiPolyIntersector."""
return "<xms.grid.geometry.MultiPolyIntersector>"

def __str__(self):
"""Returns a string representation of the MultiPolyIntersector."""
return "<xms.grid.geometry.MultiPolyIntersector>"
# def __repr__(self) -> str:
# """Returns a string representation of the MultiPolyIntersector."""
# return "<xms.grid.geometry.MultiPolyIntersector>"
#
# def __str__(self) -> str:
# """Returns a string representation of the MultiPolyIntersector."""
# return "<xms.grid.geometry.MultiPolyIntersector>"

@property
def query(self):
def query(self) -> str:
"""The query to use ('covered_by', 'intersects')."""
return self._instance.GetQuery()

@query.setter
def query(self, query):
def query(self, query: str):
"""Set the query to use ('covered_by', 'intersects')."""
self._instance.SetQuery(query)

def traverse_line_segment(self, x1, y1, x2, y2):
def traverse_line_segment(self, pt1, pt2) -> tuple[tuple[int], tuple[float], tuple[tuple[float, float, float]]]:
"""Intersect segment with polys and return intersected polys, t-values, and points.
Args:
x1: x coordinate of 1st point defining a line segment.
y1: y coordinate of 1st point defining a line segment.
x2: x coordinate of 2nd point defining a line segment.
y2: y coordinate of 2nd point defining a line segment.
pt1 (iterable): 1st point defining a line segment.
pt2 (iterable): 2nd point defining a line segment.
Returns:
tuple containing: list of poly ids, list of t values, list of points
tuple containing:
- Ids of polygons intersected by line segment. Can be zero or 1 based depending on starting_id.
- Values from 0.0 to 1.0 representing where on the line segment the intersection with the polygon occurs.
If there are any t values there are always at least 2 and all represent where the line enters the polygon,
except the last which represents where it exited. There would therefore be one more t value than poly id
but we make the sizes equal by always making the last poly id -1.
- Intersection points.
"""
return self._instance.TraverseLineSegment(x1, y1, x2, y2)
return self._instance.TraverseLineSegment(pt1, pt2)

def polygon_from_point(self, point):
"""Returns the polygon containing the point.
def polygon_from_point(self, point) -> int:
"""Returns the ID of the polygon containing the point.
Args:
point (iterable): The point.
Expand Down
22 changes: 11 additions & 11 deletions xmsgrid/python/geometry/GmMultiPolyIntersector_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,26 @@ void initGmMultiPolyIntersector(py::module &m) {
// ---------------------------------------------------------------------------
// function: init
// ---------------------------------------------------------------------------
gmMpi.def(py::init([](py::iterable points, py::iterable polys, int starting_id,
gmMpi.def(py::init([](py::iterable poly_points, py::iterable polys, int starting_id,
const std::string& query) {
BSHP<xms::VecPt3d> vec_pts = xms::VecPt3dFromPyIter(points);
BSHP<xms::VecPt3d> vec_pts = xms::VecPt3dFromPyIter(poly_points);
BSHP<xms::VecInt2d> vec_polys = xms::VecInt2dFromPyIter(polys);
BSHP<xms::GmMultiPolyIntersectionSorterTerse> sorter(new xms::GmMultiPolyIntersectionSorterTerse);
BSHP<xms::GmMultiPolyIntersector> rval(xms::GmMultiPolyIntersector::New(*vec_pts, *vec_polys, sorter, starting_id));
rval->SetQuery(query == "covered_by" ? xms::GMMPIQ_COVEREDBY : xms::GMMPIQ_INTERSECTS);
return rval;
}), py::arg("points"), py::arg("polys"), py::arg("starting_id") = 1, py::arg("query") = "covered_by");
}), py::arg("poly_points"), py::arg("polys"), py::arg("starting_id") = 1, py::arg("query") = "covered_by");
// ---------------------------------------------------------------------------
// function: TraverseLineSegment
// ---------------------------------------------------------------------------
gmMpi.def("TraverseLineSegment", [](xms::GmMultiPolyIntersector &self, double x1, double y1,
double x2, double y2) -> py::tuple {
xms::VecInt polyids;
xms::VecDbl tvalues;
gmMpi.def("TraverseLineSegment", [](xms::GmMultiPolyIntersector &self, py::iterable pt1, py::iterable pt2)
-> py::tuple {
xms::VecInt poly_ids;
xms::VecDbl t_vals;
xms::VecPt3d pts;
self.TraverseLineSegment(x1, y1, x2, y2, polyids, tvalues, pts);
return py::make_tuple(xms::PyIterFromVecInt(polyids), xms::PyIterFromVecDbl(tvalues), xms::PyIterFromVecPt3d(pts));
}, py::arg("x1"), py::arg("y1"), py::arg("x2"), py::arg("y2"));
self.TraverseLineSegment(pt1[0], pt1[1], pt2[0], pt2[1], poly_ids, t_vals, pts);
return py::make_tuple(xms::PyIterFromVecInt(poly_ids), xms::PyIterFromVecDbl(t_vals), xms::PyIterFromVecPt3d(pts));
}, py::arg("pt1"), py::arg("pt2"));
// ---------------------------------------------------------------------------
// function: PolygonFromPoint
// ---------------------------------------------------------------------------
Expand All @@ -68,5 +68,5 @@ void initGmMultiPolyIntersector(py::module &m) {
gmMpi.def("SetQuery", [](xms::GmMultiPolyIntersector &self, std::string query) {
xms::GmMultiPolyIntersectorQueryEnum q = (query == "covered_by" ? xms::GMMPIQ_COVEREDBY : xms::GMMPIQ_INTERSECTS);
self.SetQuery(q);
});
}, py::arg("query"));
}

0 comments on commit fba3e81

Please sign in to comment.