Skip to content

Commit

Permalink
GmMultiPolyIntersector for python
Browse files Browse the repository at this point in the history
  • Loading branch information
mkennard-aquaveo committed Jan 3, 2024
1 parent d4bc793 commit e4820fc
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ set(xmsgrid_py
xmsgrid/python/xmsgrid_py.cpp
# Geometry
xmsgrid/python/geometry/geometry_py.cpp
xmsgrid/python/geometry/GmMultiPolyIntersector_py.cpp
xmsgrid/python/geometry/GmTriSearch_py.cpp
# Triangulate
xmsgrid/python/triangulate/triangulate_py.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Tests for the TriSearch class."""
import unittest

import numpy as np

from xms import grid


class TestMultiPolyIntersector(unittest.TestCase):
"""Tests for the MultiPolyIntersector class."""

def setUp(self):
"""Runs before each test case."""
pass

def _run_test(self, x1, y1, x2, y2, points, polys, expected_poly_ids, expected_t_values, expected_points):
"""Runs the test."""
mpi = MultiPolyIntersector(points, polys)
poly_ids, t_values, pts = mpi.traverse_line_segment(x1, y1, x2, y2)
assert poly_ids == expected_poly_ids
assert t_values == expected_t_values
assert np.testing.assert_allclose(pts, expected_points)

def test_create_class(self):
"""Test creating class."""
mpi = grid.geometry.MultiPolyIntersector(self.pts, self.tris)
self.assertIsInstance(mpi, grid.geometry.MultiPolyIntersector)

def test_traverse_line_segment_1_out_out(self):
"""A test."""
pts = [[0, 0, 0], [10, 0, 0], [10, 10, 0], [0, 10, 0]]
polys = [[0, 1, 2, 3]]
expectedIds = [1, -1]
expectedTvals = [0.0833333, 0.916667]
expectedPoints = [[0.0, 5.0, 0.0], [10.0, 5.0, 0.0]]
self._run_test(-1, 5, 11, 5, pts, polys, expectedIds, expectedTvals, expectedPoints)


if __name__ == '__main__':
unittest.main()
1 change: 1 addition & 0 deletions _package/xms/grid/geometry/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Initialize the module."""
from . import geometry # NOQA: F401
from .multi_poly_intersector import MultiPolyIntersector # NOQA: F401
from .tri_search import TriSearch # NOQA: F401
101 changes: 101 additions & 0 deletions _package/xms/grid/geometry/multi_poly_intersector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""Pure Python wrapper for GmMultiPolyIntersector class."""
from .._xmsgrid.geometry import GmMultiPolyIntersector


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):
"""Constructor.
Args:
points (list): The points that make up the polygon.
polys (list): 0-based indexes into a_points array to define polygons. The first point is NOT repeated as
the last point.
starting_id (int): If the polygon IDs should start at something other than 1, specify the starting value.
query (str): The query to use ('covered_by', 'intersects')
**kwargs (dict): Generic keyword arguments
"""
if 'instance' not in kwargs:
if not points:
raise ValueError('points is a required arguments.')
if not polys:
raise ValueError('polys is a required argument.')
if query not in {'covered_by', 'intersects'}:
raise ValueError('query must be either "covered_by" or "intersects".')
query_ = GMMPIQ_COVEREDBY if query == 'covered_by' else GMMPIQ_INTERSECTS
self._instance = GmMultiPolyIntersector(points, polys, starting_id, query)
else:
if not isinstance(kwargs['instance'], GmMultiPolyIntersector):
raise ValueError('"instance" must be of type _xmsgrid.geometry.GmMultiPolyIntersector')
self._instance = kwargs['instance']

def __eq__(self, other):
"""Equality operator.
Args:
other (MultiPolyIntersector): MultiPolyIntersector to compare
Returns:
bool: True if MultiPolyIntersector are equal
"""
other_instance = getattr(other, '_instance', None)
if not other_instance or not isinstance(other_instance, GmMultiPolyIntersector):
print("not instance or no value")
return False
return other_instance == self._instance

def __ne__(self, other):
"""Equality operator.
Args:
other (MultiPolyIntersector): MultiPolyIntersector to compare
Returns:
bool: True if MultiPolyIntersector are not equal
"""
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>"

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

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

def traverse_line_segment(self, x1, y1, x2, y2):
"""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.
Returns:
tuple containing: list of poly ids, list of t values, list of points
"""
return self._instance.TraverseLineSegment(x1, y1, x2, y2)

def polygon_from_point(self, point):
"""Returns the polygon containing the point.
Args:
point (iterable): The point.
Returns:
The polygon id.
"""
return self._instance.PolygonFromPoint(point)
2 changes: 2 additions & 0 deletions pydocs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Index

* :doc:`modules/ugrid/UGrid`
* :doc:`modules/ugrid/ugrid_utils`
* :doc:`modules/geometry/MultiPolyIntersector`
* :doc:`modules/geometry/TriSearch`
* :doc:`modules/triangulate/Tin`

Expand All @@ -46,6 +47,7 @@ Index

modules/ugrid/UGrid.rst
modules/ugrid/ugrid_utils.rst
modules/geometry/MultiPolyIntersector.rst
modules/geometry/TriSearch.rst
modules/triangulate/Tin.rst

Expand Down
10 changes: 10 additions & 0 deletions pydocs/source/modules/geometry/MultiPolyIntersector.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
********************
MultiPolyIntersector
********************

Intersects a line segment with any number of polygons in 2D and returns the polygons in order with t values.

.. autoclass:: xms.grid.geometry.MultiPolyIntersector
:members:

.. automethod:: __init__
9 changes: 9 additions & 0 deletions xmsgrid/geometry/GmMultiPolyIntersector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class GmMultiPolyIntersectorImpl : public GmMultiPolyIntersector
int a_startingId = 1);
virtual ~GmMultiPolyIntersectorImpl();

virtual GmMultiPolyIntersectorQueryEnum GetQuery() const override;
virtual void SetQuery(GmMultiPolyIntersectorQueryEnum a_query) override;
virtual void TraverseLineSegment(double a_x1,
double a_y1,
Expand Down Expand Up @@ -347,6 +348,14 @@ void GmMultiPolyIntersectorImpl::CreateLine()
m_line.push_back(m_pt2);
} // GmMultiPolyIntersectorImpl::CreateLine
//------------------------------------------------------------------------------
/// \brief Returns the query being used (covered by, intersects...).
/// \return GmMultiPolyIntersectorQueryEnum
//------------------------------------------------------------------------------
GmMultiPolyIntersectorQueryEnum GmMultiPolyIntersectorImpl::GetQuery() const
{
return m_query;
} // GmMultiPolyIntersectorImpl::GetQuery
//------------------------------------------------------------------------------
/// \brief Set the query to use (covered by, intersects...).
/// \param a_query: GmMultiPolyIntersectorQueryEnum
//------------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions xmsgrid/geometry/GmMultiPolyIntersector.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class GmMultiPolyIntersector
int a_startingId = 1);
/// \cond

virtual GmMultiPolyIntersectorQueryEnum GetQuery() const = 0;
virtual void SetQuery(GmMultiPolyIntersectorQueryEnum a_query) = 0;
virtual void TraverseLineSegment(double a_x1,
double a_y1,
Expand Down
61 changes: 61 additions & 0 deletions xmsgrid/python/geometry/GmMultiPolyIntersector_py.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//------------------------------------------------------------------------------
/// \file
/// \brief
/// \copyright (C) Copyright Aquaveo 2018. Distributed under FreeBSD License
/// (See accompanying file LICENSE or https://aqaveo.com/bsd/license.txt)
//------------------------------------------------------------------------------

//----- Included files ---------------------------------------------------------
#include <sstream>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h> // Needed for PyUtils.h

#include <xmscore/python/misc/PyUtils.h>
#include <xmsgrid/geometry/GmMultiPolyIntersector.h>
#include <xmsgrid/geometry/GmMultiPolyIntersectorSorterTerse.h>
#include <xmsgrid/python/geometry/geometry_py.h>

//----- Namespace declaration --------------------------------------------------
namespace py = pybind11;

//----- Python Interface -------------------------------------------------------
PYBIND11_DECLARE_HOLDER_TYPE(T, boost::shared_ptr<T>);

void initGmMultiPolyIntersector(py::module &m) {
// GmMultiPolyIntersector class
py::class_<xms::GmMultiPolyIntersector, boost::shared_ptr<xms::GmMultiPolyIntersector>>
gmMpi(m, "GmMultiPolyIntersector");

// ---------------------------------------------------------------------------
// function: init
// ---------------------------------------------------------------------------
gmMpi.def(py::init([](py::iterable points, py::iterable polys, int starting_id,
const std::string& query) {
boost::shared_ptr<xms::VecPt3d> vec_pts = xms::VecPt3dFromPyIter(pts);
boost::shared_ptr<std::vector<xms::VecInt>> vec_polys = std::vector<xms::VecIntFromPyIter(polys)>;
boost::shared_ptr<GmMultiPolyIntersectionSorterTerse> sorter(new GmMultiPolyIntersectionSorterTerse);
boost::shared_ptr<xms::GmMultiPolyIntersector> rval(xms::GmMultiPolyIntersector::New(vec_pts, vec_polys, sorter,
starting_id));
rval->SetQuery(query);
return rval;
}), py::arg("points"), py::arg("polys"), py::arg("starting_id") = 1, py::arg("query") = 'covered_by')
.def_property("query", &GmMultiPolyIntersector::GetQuery, &GmMultiPolyIntersector::SetQuery);
// ---------------------------------------------------------------------------
// function: TraverseLineSegment
// ---------------------------------------------------------------------------
gmMpi.def("TraverseLineSegment", [](xms::GmMultiPolyIntersector &self, double x1, double y1,
double x2, double y2) -> py::tuple {
std::vector<int> polyids;
std::vector<double> tvalues;
std::vector<Pt3d> 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"));
// ---------------------------------------------------------------------------
// function: PolygonFromPoint
// ---------------------------------------------------------------------------
gmMpi.def("PolygonFromPoint", [](xms::GmMultiPolyIntersector &self, py::iterable point) -> int {
xms::Pt3d p = xms::Pt3dFromPyIter(point);
return self.PolygonFromPoint(p);
}, py::arg("point"));
}
1 change: 1 addition & 0 deletions xmsgrid/python/geometry/geometry_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,5 +233,6 @@ void initGeometry(py::module &geometry)
{
return xms::gmCalculateWavelength(a_period, a_depth, a_gravity);
});
initGmMultiPolyIntersector(geometry);
initGmTriSearch(geometry);
}
1 change: 1 addition & 0 deletions xmsgrid/python/geometry/geometry_py.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ namespace py = pybind11;
//----- Function declarations --------------------------------------------------
void initGeometry(py::module &);

void initGmMultiPolyIntersector(py::module &);
void initGmTriSearch(py::module &);

0 comments on commit e4820fc

Please sign in to comment.