[geos-commits] [SCM] GEOS branch main updated. e308bf7a0dddb6842dc2ac73b5fd5c8a5c599263

git at osgeo.org git at osgeo.org
Tue Aug 12 16:11:47 PDT 2025


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GEOS".

The branch, main has been updated
       via  e308bf7a0dddb6842dc2ac73b5fd5c8a5c599263 (commit)
      from  b142156227e2c484f80c62b8cf28b02ada859518 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit e308bf7a0dddb6842dc2ac73b5fd5c8a5c599263
Author: Daniel Baston <dbaston at gmail.com>
Date:   Tue Aug 12 19:11:20 2025 -0400

    Add GEOSisSimpleDetail (#1296)

diff --git a/NEWS.md b/NEWS.md
index 78d2c7c24..da909eb34 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -13,6 +13,7 @@
   - Add GEOSCoordSeq_createWithDimensions, GEOSCoordSeq_setM, GEOSCoordSeq_getM (GH-1246, Dan Baston)
   - Add GEOSGeoJSONWriter_setOutputDimension (GH-1260, Aurele Ferotin)
   - Add GEOSGeom_transformXYZ (GH-1157, Aurele Ferotin)
+  - Add GEOSisSimpleDetail (GH-1296, Dan Baston)
 
 - Breaking Changes:
   - C++17 is now required (GH-1144)
diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp
index e4af7acc9..35d14fd18 100644
--- a/capi/geos_c.cpp
+++ b/capi/geos_c.cpp
@@ -459,6 +459,12 @@ extern "C" {
         return GEOSisSimple_r(handle, g);
     }
 
+    char
+    GEOSisSimpleDetail(const Geometry* g, int returnAllPoints, Geometry** result)
+    {
+        return GEOSisSimpleDetail_r(handle, g, returnAllPoints, result);
+    }
+
     char
     GEOSisRing(const Geometry* g)
     {
diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in
index ee30bb461..e52e2cac4 100644
--- a/capi/geos_c.h.in
+++ b/capi/geos_c.h.in
@@ -1638,6 +1638,13 @@ extern char GEOS_DLL *GEOSisValidReason_r(
     GEOSContextHandle_t handle,
     const GEOSGeometry* g);
 
+/** \see GEOSisSimpleDetail */
+extern char GEOS_DLL GEOSisSimpleDetail_r(
+    GEOSContextHandle_t handle,
+    const GEOSGeometry* g,
+    int findAllLocations,
+    GEOSGeometry** location);
+
 /** \see GEOSisValidDetail */
 extern char GEOS_DLL GEOSisValidDetail_r(
     GEOSContextHandle_t handle,
@@ -3367,6 +3374,25 @@ and geometric quality.
 */
 extern char GEOS_DLL GEOSisSimple(const GEOSGeometry* g);
 
+/**
+* In one step, calculate and return whether a geometry is simple
+* and one more more points at which the geometry self-intersects
+* at interior points.
+* Caller has the responsibility to destroy 'location' with 
+* GEOSGeom_destroy()
+*
+* \param g The geometry to test
+* \param findAllLocations Whether to return all self-intersection locations, or just one
+* \param locations A pointer in which the location GEOSGeometry will be placed
+* \return 1 when simple, 0 when non-simple, 2 on exception
+*
+* \since 3.14
+*/
+extern char GEOS_DLL GEOSisSimpleDetail(
+    const GEOSGeometry* g,
+    int findAllLocations,
+    GEOSGeometry** locations);
+
 /**
 * Check the validity of the provided geometry.
 * - All points are valid.
diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index c00f0aa8f..f4690f04f 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -97,6 +97,7 @@
 #include <geos/operation/sharedpaths/SharedPathsOp.h>
 #include <geos/operation/union/CascadedPolygonUnion.h>
 #include <geos/operation/union/DisjointSubsetUnion.h>
+#include <geos/operation/valid/IsSimpleOp.h>
 #include <geos/operation/valid/IsValidOp.h>
 #include <geos/operation/valid/MakeValid.h>
 #include <geos/operation/valid/RepeatedPointRemover.h>
@@ -1242,6 +1243,29 @@ extern "C" {
         });
     }
 
+    char
+    GEOSisSimpleDetail_r(GEOSContextHandle_t extHandle, const Geometry* g1, int returnAllPoints, Geometry** result)
+    {
+        return execute(extHandle, 2, [&]() {
+            geos::operation::valid::IsSimpleOp iso(g1);
+            iso.setFindAllLocations(returnAllPoints);
+
+            *result = nullptr;
+
+            bool simple = iso.isSimple();
+            if (!simple) {
+                auto locations = iso.getNonSimpleLocations();
+                if (locations.size() == 1 || !returnAllPoints) {
+                    *result = extHandle->geomFactory->createPoint(locations.front()).release();
+                } else {
+                    *result = extHandle->geomFactory->createMultiPoint(locations).release();
+                }
+            }
+
+            return simple;
+        });
+    }
+
     char
     GEOSisRing_r(GEOSContextHandle_t extHandle, const Geometry* g)
     {
diff --git a/tests/unit/capi/GEOSisSimpleDetailTest.cpp b/tests/unit/capi/GEOSisSimpleDetailTest.cpp
new file mode 100644
index 000000000..95ce75675
--- /dev/null
+++ b/tests/unit/capi/GEOSisSimpleDetailTest.cpp
@@ -0,0 +1,105 @@
+#include <tut/tut.hpp>
+// geos
+#include <geos_c.h>
+
+#include "capi_test_utils.h"
+
+namespace tut {
+//
+// Test Group
+//
+
+struct test_geosissimpledetail_data : public capitest::utility {};
+
+typedef test_group<test_geosissimpledetail_data> group;
+typedef group::object object;
+
+group test_geosissimpledetail("capi::GEOSisSimpleDetail");
+
+template<>
+template<>
+void object::test<1>()
+{
+    set_test_name("simple LineString");
+
+    input_ = GEOSGeomFromWKT("LINESTRING (0 0, 1 1)");
+    ensure_equals(1, GEOSisSimpleDetail(input_, 0, &result_));
+    ensure(result_ == nullptr);
+}
+
+template<>
+template<>
+void object::test<2>()
+{
+    set_test_name("LineString with single self-intersection");
+
+    input_ = GEOSGeomFromWKT("LINESTRING (0 0, 2 2, 1 2, 1 0)");
+    ensure_equals(0, GEOSisSimpleDetail(input_, 0, &result_));
+    ensure_geometry_equals(result_, "POINT (1 1)");
+}
+
+template<>
+template<>
+void object::test<3>()
+{
+    set_test_name("LineString with multiple self-intersections");
+
+    input_ = GEOSGeomFromWKT("LINESTRING (2 1, 0 0, 2 2, 1 2, 1 0)");
+    expected_ = GEOSGeomFromWKT("MULTIPOINT (1 1, 1 0.5)");
+
+    ensure_equals(0, GEOSisSimpleDetail(input_, 0, &result_));
+    ensure(result_ != nullptr);
+    ensure(GEOSEquals(result_, GEOSGetGeometryN(expected_, 0)) || GEOSEquals(result_, GEOSGetGeometryN(expected_, 1)));
+    GEOSGeom_destroy(result_);
+
+    ensure_equals(0, GEOSisSimpleDetail(input_, 1, &result_));
+    ensure(result_ != nullptr);
+    ensure_geometry_equals(result_, expected_);
+}
+
+template<>
+template<>
+void object::test<4>()
+{
+    set_test_name("error raised on curved geometry");
+
+    input_ = fromWKT("CIRCULARSTRING (0 0, 1 1, 2 0)");
+    ensure(input_ != nullptr);
+
+    ensure_equals(GEOSisSimpleDetail(input_, 0, &result_), 2);
+}
+
+template<>
+template<>
+void object::test<5>()
+{
+    set_test_name("points are always simple");
+
+    input_ = GEOSGeomFromWKT("POINT (3 2)");
+    ensure_equals(1, GEOSisSimpleDetail(input_, 0, &result_));
+    ensure(result_ == nullptr);
+}
+
+template<>
+template<>
+void object::test<6>()
+{
+    set_test_name("empty LineString is simple");
+
+    input_ = GEOSGeomFromWKT("LINESTRING EMPTY");
+    ensure_equals(1, GEOSisSimpleDetail(input_, 0, &result_));
+    ensure(result_ == nullptr);
+}
+
+template<>
+template<>
+void object::test<7>()
+{
+    set_test_name("Polygon is non-simple if ring is non-simple");
+
+    input_ = GEOSGeomFromWKT("POLYGON ((0 0, 1 0, 0 1, 1 1, 0 0))");
+    ensure_equals(0, GEOSisSimpleDetail(input_, 0, &result_));
+    ensure_geometry_equals(result_, "POINT (0.5 0.5)");
+}
+
+} // namespace tut

-----------------------------------------------------------------------

Summary of changes:
 NEWS.md                                    |   1 +
 capi/geos_c.cpp                            |   6 ++
 capi/geos_c.h.in                           |  26 +++++++
 capi/geos_ts_c.cpp                         |  24 +++++++
 tests/unit/capi/GEOSisSimpleDetailTest.cpp | 105 +++++++++++++++++++++++++++++
 5 files changed, 162 insertions(+)
 create mode 100644 tests/unit/capi/GEOSisSimpleDetailTest.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list