diff --git a/docs/api.rst b/docs/api.rst index 4ae2180..aa5cb98 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -53,3 +53,5 @@ Predicates equals intersects contains + within + disjoint diff --git a/src/predicates.cpp b/src/predicates.cpp index 0c14c0b..5e7c5d0 100644 --- a/src/predicates.cpp +++ b/src/predicates.cpp @@ -32,6 +32,22 @@ bool contains(PyObjectGeography a, PyObjectGeography b) { return s2geog::s2_contains(a_index, b_index, options); } +bool within(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(b_index, a_index, options); +} + +bool disjoint(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_intersects(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( @@ -64,8 +80,16 @@ void init_predicates(py::module& m) { 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"); + + m.def("within", py::vectorize(&within), py::arg("a"), py::arg("b"), + R"pbdoc( + Returns True if A is completely inside B. Parameters ---------- @@ -73,4 +97,17 @@ void init_predicates(py::module& m) { Geography object(s) )pbdoc"); -} + + m.def("disjoint", py::vectorize(&disjoint), py::arg("a"), py::arg("b"), + R"pbdoc( + Returns True if A boundaries and interior does not intersect at all + with those of B. + + Parameters + ---------- + a, b : :py:class:`Geography` or array_like + Geography object(s) + + )pbdoc"); + + } diff --git a/src/spherely.pyi b/src/spherely.pyi index b241463..64ea736 100644 --- a/src/spherely.pyi +++ b/src/spherely.pyi @@ -104,6 +104,8 @@ 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] +within: _VFunc_Nin2_Nout1[Literal["within"], bool, bool] +disjoint: _VFunc_Nin2_Nout1[Literal["disjoint"], bool, bool] # temp (remove) diff --git a/tests/test_predicates.py b/tests/test_predicates.py index 77d67a4..57c78c7 100644 --- a/tests/test_predicates.py +++ b/tests/test_predicates.py @@ -61,3 +61,42 @@ def test_contains(): a2 = spherely.LineString([(50, 8), (60, 8)]) b2 = spherely.Point(50, 8) assert spherely.contains(a2, b2) + +def test_within(): + # test array + scalar + a = spherely.Point(40, 8) + b = np.array( + [ + spherely.LineString([(40, 8), (60, 8)]), + spherely.LineString([(20, 0), (30, 0)]), + ] + ) + + actual = spherely.within(a, b) + expected = np.array([True, False]) + np.testing.assert_array_equal(actual, expected) + + # two scalars + a2 = spherely.Point(50, 8) + b2 = spherely.LineString([(50, 8), (60, 8)]) + assert spherely.within(a2, b2) + +def test_disjoint(): + + a = spherely.Point(40, 9) + b = np.array( + [ + spherely.LineString([(40, 8), (60, 8)]), + spherely.LineString([(20, 0), (30, 0)]), + ] + ) + + actual = spherely.disjoint(a, b) + expected = np.array([True, True]) + np.testing.assert_array_equal(actual, expected) + + # two scalars + a2 = spherely.Point(50, 9) + b2 = spherely.LineString([(50, 8), (60, 8)]) + assert spherely.disjoint(a2, b2) + \ No newline at end of file