diff --git a/docs/api.rst b/docs/api.rst index 135843b..14b7a54 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -51,3 +51,4 @@ Predicates equals intersects + contains diff --git a/src/predicates.cpp b/src/predicates.cpp index 789a5d2..0c14c0b 100644 --- a/src/predicates.cpp +++ b/src/predicates.cpp @@ -24,6 +24,14 @@ bool equals(PyObjectGeography a, PyObjectGeography b) { return s2geog::s2_equals(a_index, b_index, options); } +bool contains(PyObjectGeography a, PyObjectGeography b) { + const auto& a_index = a.as_geog_ptr()->geog_index(); + const auto& b_index = b.as_geog_ptr()->geog_index(); + + S2BooleanOperation::Options options; + return s2geog::s2_contains(a_index, b_index, options); +} + void init_predicates(py::module& m) { m.def("intersects", py::vectorize(&intersects), py::arg("a"), py::arg("b"), R"pbdoc( @@ -51,4 +59,18 @@ void init_predicates(py::module& m) { Geography object(s) )pbdoc"); + + m.def("contains", py::vectorize(&contains), py::arg("a"), py::arg("b"), + R"pbdoc( + Returns True if B is completely inside A. + + A contains B if no points of B lie in the exterior of A and at least + one point of the interior of B lies in the interior of A. + + Parameters + ---------- + a, b : :py:class:`Geography` or array_like + Geography object(s) + + )pbdoc"); } diff --git a/src/spherely.pyi b/src/spherely.pyi index 21f6b74..2963ba4 100644 --- a/src/spherely.pyi +++ b/src/spherely.pyi @@ -93,6 +93,7 @@ destroy_prepared: _VFunc_Nin1_Nout1[Literal["destroy_prepared"], Geography, Any] intersects: _VFunc_Nin2_Nout1[Literal["intersects"], bool, bool] equals: _VFunc_Nin2_Nout1[Literal["intersects"], bool, bool] +contains: _VFunc_Nin2_Nout1[Literal["contains"], bool, bool] # temp (remove) diff --git a/tests/test_predicates.py b/tests/test_predicates.py index cbc4385..77d67a4 100644 --- a/tests/test_predicates.py +++ b/tests/test_predicates.py @@ -41,3 +41,23 @@ def test_equals() -> None: a2 = spherely.Point(50, 8) b2 = spherely.Point(50, 8) assert spherely.equals(a2, b2) + + +def test_contains(): + # test array + scalar + a = np.array( + [ + spherely.LineString([(40, 8), (60, 8)]), + spherely.LineString([(20, 0), (30, 0)]), + ] + ) + b = spherely.Point(40, 8) + + actual = spherely.contains(a, b) + expected = np.array([True, False]) + np.testing.assert_array_equal(actual, expected) + + # two scalars + a2 = spherely.LineString([(50, 8), (60, 8)]) + b2 = spherely.Point(50, 8) + assert spherely.contains(a2, b2)