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

git at osgeo.org git at osgeo.org
Thu May 29 15:28:11 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  cd8275eb1f6e81885cf7483ed4f5c24df6c12512 (commit)
      from  3ae49122f47bae55819b0ab1cfb1a9d2dfa9d6e3 (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 cd8275eb1f6e81885cf7483ed4f5c24df6c12512
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Thu May 29 15:27:10 2025 -0700

    Add CAPI for CoverageCleaner

diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp
index 87b72163c..9bd1068ba 100644
--- a/capi/geos_c.cpp
+++ b/capi/geos_c.cpp
@@ -52,12 +52,20 @@
 #define GEOSGeoJSONReader geos::io::GeoJSONReader
 #define GEOSGeoJSONWriter geos::io::GeoJSONWriter
 
+// Implementation struct for the GEOSCoverageCleanParams object
+typedef struct {
+    double snappingDistance;
+    int    overlapMergeStrategy;
+    double gapMaximumWidth;
+} GEOSCoverageCleanParams;
+
 // Implementation struct for the GEOSMakeValidParams object
 typedef struct {
     int method;
     int keepCollapsed;
 } GEOSMakeValidParams;
 
+
 #include "geos_c.h"
 
 /// Define this if you want operations triggering Exceptions to
@@ -1007,6 +1015,60 @@ extern "C" {
         return GEOSMakeValidWithParams_r(handle, g, params);
     }
 
+    GEOSCoverageCleanParams*
+    GEOSCoverageCleanParams_create()
+    {
+        return GEOSCoverageCleanParams_create_r(handle);
+    }
+
+    void
+    GEOSCoverageCleanParams_destroy(
+        GEOSCoverageCleanParams* params)
+    {
+        return GEOSCoverageCleanParams_destroy_r(handle, params);
+    }
+
+    int
+    GEOSCoverageCleanParams_setSnappingDistance(
+        GEOSCoverageCleanParams* params, double snappingDistance)
+    {
+        return GEOSCoverageCleanParams_setSnappingDistance_r(
+            handle, params, snappingDistance);
+    }
+
+    int
+    GEOSCoverageCleanParams_setGapMaximumWidth(
+        GEOSCoverageCleanParams* params, double gapMaximumWidth)
+    {
+        return GEOSCoverageCleanParams_setGapMaximumWidth_r(
+            handle, params, gapMaximumWidth);
+    }
+
+    int
+    GEOSCoverageCleanParams_setOverlapMergeStrategy(
+        GEOSCoverageCleanParams* params, int overlapMergeStrategy)
+    {
+        return GEOSCoverageCleanParams_setOverlapMergeStrategy_r(
+            handle, params, overlapMergeStrategy);
+    }
+
+    GEOSGeometry *
+    GEOSCoverageCleanWithParams(
+        const GEOSGeometry* input,
+        const GEOSCoverageCleanParams* params)
+    {
+        return GEOSCoverageCleanWithParams_r(
+            handle, input, params);
+    }
+
+    GEOSGeometry *
+    GEOSCoverageClean(
+        const GEOSGeometry * input)
+    {
+        return GEOSCoverageClean_r(
+            handle, input);
+    }
+
     Geometry*
     GEOSRemoveRepeatedPoints(
         const Geometry* g,
diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in
index 6d32e95ba..39671cc69 100644
--- a/capi/geos_c.h.in
+++ b/capi/geos_c.h.in
@@ -168,6 +168,13 @@ typedef struct GEOSSTRtree_t GEOSSTRtree;
 */
 typedef struct GEOSBufParams_t GEOSBufferParams;
 
+/**
+* Parameter object for coverage cleaning options.
+* \see GEOSCoverageCleanParams_create()
+* \see GEOSCoverageCleanParams_destroy()
+*/
+typedef struct GEOSCoverageCleanParams_t GEOSCoverageCleanParams;
+
 /**
 * Parameter object for validity enforcement.
 * \see GEOSMakeValidParams_create()
@@ -879,6 +886,51 @@ GEOSCoverageSimplifyVW_r(
     double tolerance,
     int preserveBoundary);
 
+/** \see GEOSCoverageCleanParams_create */
+extern GEOSCoverageCleanParams GEOS_DLL *
+GEOSCoverageCleanParams_create_r(
+    GEOSContextHandle_t extHandle);
+
+/** \see GEOSCoverageCleanParams_destroy */
+extern void GEOS_DLL
+GEOSCoverageCleanParams_destroy_r(
+    GEOSContextHandle_t extHandle,
+    GEOSCoverageCleanParams* params);
+
+/** \see GEOSCoverageCleanParams_setSnappingDistance */
+extern int GEOS_DLL
+GEOSCoverageCleanParams_setSnappingDistance_r(
+    GEOSContextHandle_t extHandle,
+    GEOSCoverageCleanParams* params,
+    double snappingDistance);
+
+/** \see GEOSCoverageCleanParams_setGapMaximumWidth */
+extern int GEOS_DLL
+GEOSCoverageCleanParams_setGapMaximumWidth_r(
+    GEOSContextHandle_t extHandle,
+    GEOSCoverageCleanParams* params,
+    double gapMaximumWidth);
+
+/** \see GEOSCoverageCleanParams_setOverlapMergeStrategy */
+extern int GEOS_DLL
+GEOSCoverageCleanParams_setOverlapMergeStrategy_r(
+    GEOSContextHandle_t extHandle,
+    GEOSCoverageCleanParams* params,
+    int overlapMergeStrategy);
+
+/** \see GEOSCoverageCleanWithParams */
+extern GEOSGeometry GEOS_DLL *
+GEOSCoverageCleanWithParams_r(
+    GEOSContextHandle_t extHandle,
+    const GEOSGeometry* input,
+    const GEOSCoverageCleanParams* params);
+
+/** \see GEOSCoverageClean */
+extern GEOSGeometry GEOS_DLL *
+GEOSCoverageClean_r(
+    GEOSContextHandle_t extHandle,
+    const GEOSGeometry* input);
+
 /* ========= Topology Operations ========= */
 
 /** \see GEOSEnvelope */
@@ -4269,8 +4321,154 @@ extern GEOSGeometry GEOS_DLL * GEOSCoverageSimplifyVW(
     double tolerance,
     int preserveBoundary);
 
+/**
+* Create a default GEOSCoverageCleanParams object for controlling
+* the way invalid polygon interactions are repaird by \ref GEOSCoverageCleanWithParams.
+* \return A newly allocated GEOSCoverageCleanParams. NULL on exception.
+* Caller is responsible for freeing with GEOSCoverageCleanParams_destroy().
+*
+* \since 3.14
+*/
+extern GEOSCoverageCleanParams GEOS_DLL *
+GEOSCoverageCleanParams_create();
+
+/**
+* Destroy a GEOSCoverageCleanParams and free all associated memory.
+* \param params The object to destroy.
+*
+* \since 3.14
+*/
+extern void GEOS_DLL
+GEOSCoverageCleanParams_destroy(
+    GEOSCoverageCleanParams* params);
+
+/**
+* Snapping to nearby vertices and line segment snapping
+* is used to improve noding robustness
+* and eliminate small errors in an efficient way,
+* By default this uses a very small snapping distance
+* based on the extent of the input data.
+* The snapping distance may be specified explicitly.
+* This can reduce the number of overlaps and gaps that need to be merged,
+* and reduce the risk of spikes formed by merging gaps.
+* However, a large snapping distance may introduce undesirable
+* data alteration.
+*
+* A distance of zero prevents snapping from being used.
+* \see geos::coverage::CoverageCleaner::setSnappingDistance
+* \param params The GEOSCoverageCleanParams to operate on
+* \param snappingDistance Set to 0.0 for no snapping.
+* \return 0 on exception, 1 on success.
+*
+* \since 3.14
+*/
+extern int GEOS_DLL
+GEOSCoverageCleanParams_setSnappingDistance(
+    GEOSCoverageCleanParams *params,
+    double snappingDistance);
+
+/**
+* Gaps which are wider than a given distance are merged with an adjacent polygon.
+* Polygon width is determined as twice the radius of the MaximumInscribedCircle
+* of the gap polygon.
+* Gaps are merged with the adjacent polygon with longest shared border.
+* Empty holes in input polygons are treated as gaps, and may be filled in.
+* Gaps which are not fully enclosed ("inlets") are not removed.
+*
+* The width of a gap is twice the radius of the Maximum Inscribed Circle in the gap polygon,
+* A width of zero prevents gaps from being merged.
+*
+* \see geos::coverage::CoverageCleaner::setGapMaximumWidth
+* \param params The GEOSCoverageCleanParams to operate on
+* \param gapMaximumWidth Set to 0.0 for no snapping.
+* \return 0 on exception, 1 on success.
+*
+* \since 3.14
+*/
+extern int GEOS_DLL
+GEOSCoverageCleanParams_setGapMaximumWidth(
+    GEOSCoverageCleanParams* params,
+    double gapMaximumWidth);
+
+/**
+* Overlap repair strategies.
+* \see GEOSCoverageCleanParams_setOverlapMergeStrategy
+*/
+enum GEOSOverlapMerge {
+    /** Merge strategy that chooses polygon with longest common border */
+    GEOS_MERGE_LONGEST_BORDER = 0,
+    /** Merge strategy that chooses polygon with maximum area */
+    GEOS_MERGE_MAX_AREA       = 1,
+    /** Merge strategy that chooses polygon with minimum area */
+    GEOS_MERGE_MIN_AREA       = 2,
+    /** Merge strategy that chooses polygon with smallest input index */
+    GEOS_MERGE_MIN_INDEX      = 3
+};
+
+/*
+* Sets the overlap merge strategy to use, using one from \ref GEOSOverlapMerge.
+* Overlaps are merged with an adjacent polygon chosen according to a specified merge strategy.
+* The supported strategies are:
+*
+*   * **Longest Border**: (default) merge with the polygon with longest shared border (GEOS_MERGE_LONGEST_BORDER.)
+*   * **Maximum/Minimum Area**: merge with the polygon with largest or smallest area (GEOS_MERGE_MAX_AREA, GEOS_MERGE_MIN_AREA.)
+*   * **Minimum Index**: merge with the polygon with the lowest index in the input array (GEOS_MERGE_MIN_INDEX.)
+*
+* This allows sorting the input according to some criteria to provide a priority
+* for merging gaps.
+*
+* The default is GEOS_MERGE_LONGEST_BORDER.
+*
+* \see geos::coverage::CoverageCleaner::setGapMaximumWidth
+* \param params The GEOSCoverageCleanParams to operate on
+* \param overlapMergeStrategy One of \ref GEOSOverlapMerge strategies
+* \return 0 on exception, 1 on success.
+*
+* \since 3.14
+*/
+extern int GEOS_DLL
+GEOSCoverageCleanParams_setOverlapMergeStrategy(
+    GEOSCoverageCleanParams* params,
+    int overlapMergeStrategy);
+
+/**
+* Operates on a list of polygonal geometry with "exactly matching"
+* edge geometry, to fix cases where the geometry does not in fact
+* exactly match.
+*
+* The input is a collection of polygons, and the output is a collection
+* with the same number of cleaned polygons, in the same order as
+* the input. Polygons that have collapsed during cleaning will be returned
+* as empties.
+*
+* \param input The dirty polygonal coverage,
+*        stored in a geometry collection. All members must be POLYGON
+*        or MULTIPOLYGON.
+* \param params A GEOSCoverageCleanParams to control the options
+*        used in cleaning the coverage.
+* \return A collection containing the cleaned geometries, or null
+*         on error. Where cleaning has resulted in polygon collapse,
+*         an EMPTY geometry will be returned as part of the collection.
+*
+* \since 3.14
+*/
+extern GEOSGeometry GEOS_DLL *
+GEOSCoverageCleanWithParams(
+    const GEOSGeometry* input,
+    const GEOSCoverageCleanParams* params
+);
+
+/** \see GEOSCoverageCleanWithParams */
+extern GEOSGeometry GEOS_DLL *
+GEOSCoverageClean(
+    const GEOSGeometry* input);
+
+
+
+
 ///@}
 
+
 /* ========== Construction Operations ========== */
 /** @name Geometric Constructions
 * Functions for computing geometric constructions.
diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index 5c2080115..27ee5b812 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -28,9 +28,10 @@
 #include <geos/algorithm/distance/DiscreteFrechetDistance.h>
 #include <geos/algorithm/hull/ConcaveHull.h>
 #include <geos/algorithm/hull/ConcaveHullOfPolygons.h>
-#include <geos/coverage/CoverageValidator.h>
+#include <geos/coverage/CoverageCleaner.h>
 #include <geos/coverage/CoverageSimplifier.h>
 #include <geos/coverage/CoverageUnion.h>
+#include <geos/coverage/CoverageValidator.h>
 #include <geos/geom/CircularString.h>
 #include <geos/geom/CompoundCurve.h>
 #include <geos/geom/Coordinate.h>
@@ -139,8 +140,8 @@
 // violations.
 #define GEOSGeometry geos::geom::Geometry
 #define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry
-#define GEOSCoordSequence geos::geom::CoordinateSequence
 #define GEOSClusterInfo geos::operation::cluster::Clusters
+#define GEOSCoordSequence geos::geom::CoordinateSequence
 #define GEOSBufferParams geos::operation::buffer::BufferParameters
 #define GEOSSTRtree geos::index::strtree::TemplateSTRtree<void*>
 #define GEOSWKTReader geos::io::WKTReader
@@ -150,6 +151,13 @@
 #define GEOSGeoJSONReader geos::io::GeoJSONReader
 #define GEOSGeoJSONWriter geos::io::GeoJSONWriter
 
+// Implementation struct for the GEOSCoverageCleanParams object
+typedef struct {
+    double snappingDistance;
+    int    overlapMergeStrategy;
+    double gapMaximumWidth;
+} GEOSCoverageCleanParams;
+
 // Implementation struct for the GEOSMakeValidParams object
 typedef struct {
     int method;
@@ -2331,6 +2339,10 @@ extern "C" {
         });
     }
 
+/************************************************************************
+ * Make Valid
+ */
+
     Geometry*
     GEOSMakeValid_r(GEOSContextHandle_t extHandle, const Geometry* g)
     {
@@ -4538,6 +4550,105 @@ extern "C" {
     }
 
 
+/************************************************************************
+ * Coverage Cleaner
+ */
+
+    GEOSCoverageCleanParams*
+    GEOSCoverageCleanParams_create_r(GEOSContextHandle_t extHandle)
+    {
+        return execute(extHandle, [&]() {
+            GEOSCoverageCleanParams* p = new GEOSCoverageCleanParams();
+            p->overlapMergeStrategy = geos::coverage::CoverageCleaner::MERGE_LONGEST_BORDER;
+            p->snappingDistance = -1.0;
+            p->gapMaximumWidth = 0.0;
+            return p;
+        });
+    }
+
+    void
+    GEOSCoverageCleanParams_destroy_r(GEOSContextHandle_t extHandle,
+        GEOSCoverageCleanParams* params)
+    {
+        (void)extHandle;
+        delete params;
+    }
+
+    int
+    GEOSCoverageCleanParams_setSnappingDistance_r(GEOSContextHandle_t extHandle,
+        GEOSCoverageCleanParams* params, double snappingDistance)
+    {
+        return execute(extHandle, 0, [&]() {
+            params->snappingDistance = snappingDistance;
+            return 1;
+        });
+    }
+
+    int
+    GEOSCoverageCleanParams_setGapMaximumWidth_r(GEOSContextHandle_t extHandle,
+        GEOSCoverageCleanParams* params, double gapMaximumWidth)
+    {
+        return execute(extHandle, 0, [&]() {
+            params->gapMaximumWidth = gapMaximumWidth;
+            return 1;
+        });
+    }
+
+    int
+    GEOSCoverageCleanParams_setOverlapMergeStrategy_r(GEOSContextHandle_t extHandle,
+        GEOSCoverageCleanParams* params, int overlapMergeStrategy)
+    {
+        return execute(extHandle, 0, [&]() {
+            if (   overlapMergeStrategy != geos::coverage::CoverageCleaner::MERGE_LONGEST_BORDER
+                && overlapMergeStrategy != geos::coverage::CoverageCleaner::MERGE_MAX_AREA
+                && overlapMergeStrategy != geos::coverage::CoverageCleaner::MERGE_MIN_AREA
+                && overlapMergeStrategy != geos::coverage::CoverageCleaner::MERGE_MIN_INDEX)
+            {
+                extHandle->ERROR_MESSAGE("GEOSCoverageCleanParams_setOverlapMergeStrategy: Invalid overlapMergeStrategy");
+                return 0;
+            }
+            params->overlapMergeStrategy = overlapMergeStrategy;
+            return 1;
+        });
+    }
+
+    GEOSGeometry *
+    GEOSCoverageCleanWithParams_r(GEOSContextHandle_t extHandle,
+        const GEOSGeometry* input,
+        const GEOSCoverageCleanParams* params)
+    {
+        using geos::coverage::CoverageCleaner;
+
+        return execute(extHandle, [&]() -> Geometry* {
+            const GeometryCollection* col = dynamic_cast<const GeometryCollection*>(input);
+            if (!col)
+                return nullptr;
+
+            std::vector<const Geometry*> coverage;
+            for (const auto& g : *col) {
+                coverage.push_back(g.get());
+            }
+            geos::coverage::CoverageCleaner c(coverage);
+            if (params) {
+                c.setSnappingDistance(params->snappingDistance);
+                c.setGapMaximumWidth(params->gapMaximumWidth);
+                c.setOverlapMergeStrategy(params->overlapMergeStrategy);
+            }
+            c.clean();
+
+            auto cleanCov = c.getResult();
+            const GeometryFactory* gf = input->getFactory();
+            std::unique_ptr<Geometry> r = gf->createGeometryCollection(std::move(cleanCov));
+            return r.release();
+        });
+    }
+
+    GEOSGeometry *
+    GEOSCoverageClean_r(GEOSContextHandle_t extHandle,
+        const GEOSGeometry* input)
+    {
+        return GEOSCoverageCleanWithParams_r(extHandle, input, nullptr);
+    }
 
 
 } /* extern "C" */
diff --git a/tests/unit/coverage/CoverageCleanerTest.cpp b/tests/unit/coverage/CoverageCleanerTest.cpp
index 5eb141be1..7256f5f72 100644
--- a/tests/unit/coverage/CoverageCleanerTest.cpp
+++ b/tests/unit/coverage/CoverageCleanerTest.cpp
@@ -420,6 +420,27 @@ void object::test<17>()
         );
 }
 
+// Tests that a collapsed polygon due to snapping is returned as EMPTY
+template<>
+template<>
+void object::test<18>()
+{
+    checkCleanSnap({
+        "POLYGON ((1 1, 1 9, 6 5, 9 1, 1 1))",
+        "POLYGON ((9 1, 6 5.1, 1 9, 9 9, 9 1))",
+        "POLYGON ((9 1, 6 5, 1 9, 6 5.1, 9 1))"},
+        1,
+        {
+        "POLYGON ((6 5, 9 1, 1 1, 1 9, 6 5))",
+        "POLYGON ((9 9, 9 1, 6 5, 1 9, 9 9))",
+        "POLYGON EMPTY"
+        });
+}
+
+
+
+
+
 
 
 } // namespace tut

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

Summary of changes:
 capi/geos_c.cpp                             |  62 +++++++++
 capi/geos_c.h.in                            | 198 ++++++++++++++++++++++++++++
 capi/geos_ts_c.cpp                          | 115 +++++++++++++++-
 tests/unit/coverage/CoverageCleanerTest.cpp |  21 +++
 4 files changed, 394 insertions(+), 2 deletions(-)


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list