[geos-commits] [SCM] GEOS branch main updated. 3e5548c497271eda29d5e992b0f22da8702c7b4d
git at osgeo.org
git at osgeo.org
Mon Jun 16 16:54:13 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 3e5548c497271eda29d5e992b0f22da8702c7b4d (commit)
via f6a9db53032b442d805bf0966fabf37b563b9d4a (commit)
via 7c4e33315a10b1710c543766f523d206306b5204 (commit)
via 8096afcdd58bcc9447fcbcf83d19036da21aceaf (commit)
via 490e5a36f34bbebba91021e4838878fe39491f40 (commit)
via c34f12e76629fba57ca2c244a9488249709ecc48 (commit)
via aaf1cde5221952fad66c7eaaf602b75e6422b74c (commit)
via a3957684fcd10ee3bdaf7003f6daeb8b4b66827c (commit)
via cccf34e33713c7d512ca70ec9e00cff8996a518a (commit)
via 56023a0c2bc65fb322aaa6253843644bedb94477 (commit)
via e6c64e0886b92df50a822a239d47781326a938c5 (commit)
via 7c169a996f004c8e4c7a64b7dbfb9df38be8a4a8 (commit)
via d47dbeb640687f5ecfa2efcbae53c7c3d96adfcf (commit)
via 31b6261f4b0bb15cfce82f8ea0b067e2aa9da1d9 (commit)
via a1f91eacad2904c6249f46c7f9cec42b2bc91d67 (commit)
via 0bac2cb5566b8ff37b5a9717ad286236c99a9544 (commit)
via 9bbcd6b873a9f8ea00e0f1542274fff75312fcb5 (commit)
via 219ba596545b4110f6b935cb0a0dfabd157a31b9 (commit)
via af2c281c1e508b0973ab67324f8db6ee91e6f85f (commit)
via abbaa2e0c1e962614cbed8e662e9e76e7e24bea9 (commit)
via 99d1fdcb6ee51682d3cc7e370c486d8092f9e8aa (commit)
via ee84829cc6639f2be38e2f49a65f06b7626ae5d8 (commit)
via 4d7c5deca2961823504d1458f2b23613c93a0094 (commit)
via ef44cf56a4d4f8831e9beb5d61627186a8314584 (commit)
via 6ff3c246417b780123e5dc2a1f48164b6c87e791 (commit)
via b6e7efad2186e11a4a8f65e951b3d7c735d5a216 (commit)
via 886c61809a96b7f32002b69828e021fa8ef6d6c0 (commit)
via 99e8bebb3aa9f0aec817109b6a9403e9aca7cebb (commit)
via 9dd00040515fadcdac3562e3308784bdcf7eaec7 (commit)
via 6fd09a1ed585800965ca1b314b0840ef0a0466bf (commit)
via 45a83828b4296a47e2979308d6e91c26bbc5f8ba (commit)
from 3aaa42b5ead736456c8c91f98e3bde3953450189 (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 3e5548c497271eda29d5e992b0f22da8702c7b4d
Merge: f6a9db530 3aaa42b5e
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Mon Jun 16 16:53:33 2025 -0700
Merge branch 'main' of github.com:libgeos/geos
commit f6a9db53032b442d805bf0966fabf37b563b9d4a
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Mon Jun 16 16:34:53 2025 -0700
Deprecated Windows 2019
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8e4b12b39..11e1e9a62 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -264,8 +264,8 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
windows-mingw:
- name: 'Windows (mingw-w64, x86_64, windows-2019)'
- runs-on: windows-2019
+ name: 'Windows (mingw-w64, x86_64, windows-2022)'
+ runs-on: windows-2022
defaults:
run:
shell: msys2 {0}
@@ -356,13 +356,12 @@ jobs:
fail-fast: false
matrix:
ci:
- - os: windows-2019
- cxxstd: 14
- arch: x86
-
- os: windows-2022
cxxstd: 20
arch: x64
+ - os: windows-2025
+ cxxstd: 20
+ arch: x64
runs-on: ${{ matrix.ci.os }}
steps:
commit 7c4e33315a10b1710c543766f523d206306b5204
Merge: 8096afcdd e50b5af9c
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Fri Jun 6 13:49:22 2025 -0700
Merge branch 'main' of github.com:libgeos/geos
commit 8096afcdd58bcc9447fcbcf83d19036da21aceaf
Merge: 490e5a36f f2c286506
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Fri Jun 6 13:08:18 2025 -0700
Merge branch 'main' of github.com:libgeos/geos
commit 490e5a36f34bbebba91021e4838878fe39491f40
Merge: c34f12e76 b577e3056
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Mon Jun 2 09:46:32 2025 -0700
Merge branch 'main' of github.com:libgeos/geos
commit c34f12e76629fba57ca2c244a9488249709ecc48
Merge: cd8275eb1 aaf1cde52
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu May 29 15:41:57 2025 -0700
Add bad input test
commit aaf1cde5221952fad66c7eaaf602b75e6422b74c
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu May 29 15:40:54 2025 -0700
Add test for invalid inputs
diff --git a/tests/unit/coverage/CoverageCleanerTest.cpp b/tests/unit/coverage/CoverageCleanerTest.cpp
index 7256f5f72..f36f1f887 100644
--- a/tests/unit/coverage/CoverageCleanerTest.cpp
+++ b/tests/unit/coverage/CoverageCleanerTest.cpp
@@ -437,7 +437,25 @@ void object::test<18>()
});
}
+// testCoverageWithNonPolygon
+template<>
+template<>
+void object::test<19> ()
+{
+ checkClean(
+ "GEOMETRYCOLLECTION (LINESTRING EMPTY, POLYGON EMPTY, LINESTRING (2 1, 2 5, 8 5, 8 1, 2 1))",
+ "GEOMETRYCOLLECTION (POLYGON EMPTY, POLYGON EMPTY, POLYGON EMPTY)");
+}
+// testCoverageWithNonPolygon
+template<>
+template<>
+void object::test<20> ()
+{
+ checkClean(
+ "GEOMETRYCOLLECTION (POLYGON ((1 9, 9 9, 9 4, 1 4, 1 9)), LINESTRING EMPTY)",
+ "GEOMETRYCOLLECTION (POLYGON ((1 9, 9 9, 9 4, 1 4, 1 9)), POLYGON EMPTY)");
+}
commit a3957684fcd10ee3bdaf7003f6daeb8b4b66827c
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu May 29 15:13:03 2025 -0700
empty test
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
commit cccf34e33713c7d512ca70ec9e00cff8996a518a
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu May 29 15:09:10 2025 -0700
spel
diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in
index 84fdae64a..39671cc69 100644
--- a/capi/geos_c.h.in
+++ b/capi/geos_c.h.in
@@ -4323,7 +4323,7 @@ extern GEOSGeometry GEOS_DLL * GEOSCoverageSimplifyVW(
/**
* Create a default GEOSCoverageCleanParams object for controlling
-* the way invalid polygon iteractions are repaird by \ref GEOSCoverageCleanWithParams.
+* 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().
*
commit 56023a0c2bc65fb322aaa6253843644bedb94477
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu May 29 14:41:22 2025 -0700
capi
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..84fdae64a 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 iteractions 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" */
commit e6c64e0886b92df50a822a239d47781326a938c5
Merge: 7c169a996 3ae49122f
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu May 29 14:00:02 2025 -0700
Merge branch 'main' of github.com:libgeos/geos into main-covclean
commit 7c169a996f004c8e4c7a64b7dbfb9df38be8a4a8
Merge: d47dbeb64 605ecc1f0
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 28 13:13:45 2025 -0700
Merge branch 'main' of github.com:libgeos/geos into main-covclean
commit d47dbeb640687f5ecfa2efcbae53c7c3d96adfcf
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 28 13:13:42 2025 -0700
includes
diff --git a/src/coverage/CoverageCleaner.cpp b/src/coverage/CoverageCleaner.cpp
index 9093420e9..80c044eb9 100644
--- a/src/coverage/CoverageCleaner.cpp
+++ b/src/coverage/CoverageCleaner.cpp
@@ -24,15 +24,11 @@
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/Point.h>
#include <geos/geom/Polygon.h>
-#include <geos/index/strtree/STRtree.h>
-#include <geos/index/quadtree/Quadtree.h>
-#include <geos/noding/NodedSegmentString.h>
#include <geos/noding/Noder.h>
#include <geos/noding/SegmentStringUtil.h>
#include <geos/noding/snap/SnappingNoder.h>
#include <geos/operation/polygonize/Polygonizer.h>
#include <geos/operation/relateng/RelateNG.h>
-#include <geos/operation/relateng/RelatePredicate.h>
#include <geos/util/IllegalArgumentException.h>
@@ -45,15 +41,11 @@ using geos::geom::GeometryFactory;
using geos::geom::MultiPolygon;
using geos::geom::Point;
using geos::geom::Polygon;
-using geos::index::strtree::STRtree;
-using geos::index::quadtree::Quadtree;
-using geos::noding::NodedSegmentString;
using geos::noding::Noder;
using geos::noding::SegmentStringUtil;
using geos::noding::snap::SnappingNoder;
using geos::operation::polygonize::Polygonizer;
using geos::operation::relateng::RelateNG;
-using geos::operation::relateng::RelatePredicate;
namespace geos { // geos
@@ -116,13 +108,7 @@ CoverageCleaner::CoverageCleaner(std::vector<const Geometry*>& p_coverage)
, snappingDistance(computeDefaultSnappingDistance(p_coverage))
{}
-/**
- * Sets the snapping distance tolerance.
- * The default is to use a small fraction of the input extent diameter.
- * A distance of zero prevents snapping from being used.
- *
- * @param snappingDistance the snapping distance tolerance
- */
+
/* public */
void
CoverageCleaner::setSnappingDistance(double p_snappingDistance)
@@ -133,12 +119,7 @@ CoverageCleaner::setSnappingDistance(double p_snappingDistance)
snappingDistance = p_snappingDistance;
}
-/**
- * Sets the overlap merge strategy to use.
- * The default is {@link #MERGE_LONGEST_BORDER}.
- *
- * @param mergeStrategy the merge strategy code
- */
+
/* public */
void
CoverageCleaner::setOverlapMergeStrategy(int mergeStrategy)
@@ -150,13 +131,7 @@ CoverageCleaner::setOverlapMergeStrategy(int mergeStrategy)
overlapMergeStrategy = mergeStrategy;
}
-/**
- * Sets the maximum width of the gaps that will be filled and merged.
- * 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.
- *
- * @param maxWidth the maximum gap width to merge
- */
+
/* public */
void
CoverageCleaner::setGapMaximumWidth(double maxWidth)
@@ -167,10 +142,6 @@ CoverageCleaner::setGapMaximumWidth(double maxWidth)
}
-/**
- * Cleans the coverage.
- *
- */
/* public */
void
CoverageCleaner::clean()
@@ -186,11 +157,7 @@ CoverageCleaner::clean()
//System.out.println("Merge Gaps: " + sw.getTimeString());
}
-/**
- * Gets the cleaned coverage.
- *
- * @return the clean coverage
- */
+
/* public */
std::vector<std::unique_ptr<Geometry>>
CoverageCleaner::getResult()
@@ -198,12 +165,7 @@ CoverageCleaner::getResult()
return cleanCov->toCoverage(geomFactory);
}
-/**
- * Gets polygons representing the overlaps in the input,
- * which have been merged.
- *
- * @return a list of overlap polygons
- */
+
/* public */
std::vector<const Polygon*>
CoverageCleaner::getOverlaps()
@@ -211,12 +173,7 @@ CoverageCleaner::getOverlaps()
return overlaps;
}
-/**
- * Gets polygons representing the gaps in the input
- * which have been merged.
- *
- * @return a list of gap polygons
- */
+
/* public */
std::vector<const Polygon*>
CoverageCleaner::getMergedGaps()
@@ -226,7 +183,6 @@ CoverageCleaner::getMergedGaps()
//-------------------------------------------------
-
/* private static */
double
CoverageCleaner::computeDefaultSnappingDistance(std::vector<const Geometry*>& geoms)
@@ -311,6 +267,7 @@ CoverageCleaner::computeResultants(double tolerance)
mergableGaps = findMergableGaps(gaps);
}
+
/* private */
void
CoverageCleaner::createCoverageIndex()
@@ -321,6 +278,7 @@ CoverageCleaner::createCoverageIndex()
}
}
+
/* private */
void
CoverageCleaner::classifyResult(std::vector<std::unique_ptr<Polygon>>& rs)
@@ -330,6 +288,7 @@ CoverageCleaner::classifyResult(std::vector<std::unique_ptr<Polygon>>& rs)
}
}
+
/* private */
void
CoverageCleaner::classifyResultant(std::size_t resultIndex, const Polygon* resPoly)
@@ -498,9 +457,6 @@ CoverageCleaner::toPolygonArray(const Geometry* geom)
}
-
-
-
} // namespace geos.coverage
} // namespace geos
diff --git a/tests/unit/coverage/CoverageCleanerTest.cpp b/tests/unit/coverage/CoverageCleanerTest.cpp
index bc7e71a2b..5eb141be1 100644
--- a/tests/unit/coverage/CoverageCleanerTest.cpp
+++ b/tests/unit/coverage/CoverageCleanerTest.cpp
@@ -169,14 +169,6 @@ struct test_coveragecleaner_data {
checkCleanSnapInt(covArr, snapDist, expArr);
}
- // void
- // checkCleanSnap(const std::string& wkt, double snapDist)
- // {
- // std::unique_ptr<Geometry> covGeom = r.read(wkt);
- // auto covArr = toArray(covGeom);
- // checkCleanSnap(covArr, snapDist);
- // }
-
void
checkValidCoverage(std::vector<const Geometry*> coverage, double tolerance)
{
@@ -200,17 +192,6 @@ struct test_coveragecleaner_data {
return geometries;
}
- // std::vector<std::unique_ptr<Geometry>>
- // readArray(const std::string& wkt)
- // {
- // auto geom = r.read(wkt);
- // std::vector<std::unique_ptr<Geometry>> geoms;
- // for (std::size_t i = 0; i < geom->getNumGeometries(); i++) {
- // geoms.emplace_back(geom->getGeometryN(i)->clone().release());
- // }
- // return geoms;
- // }
-
};
commit 31b6261f4b0bb15cfce82f8ea0b067e2aa9da1d9
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 28 12:53:29 2025 -0700
ci
diff --git a/tests/unit/coverage/CoverageCleanerTest.cpp b/tests/unit/coverage/CoverageCleanerTest.cpp
index e137d8943..bc7e71a2b 100644
--- a/tests/unit/coverage/CoverageCleanerTest.cpp
+++ b/tests/unit/coverage/CoverageCleanerTest.cpp
@@ -128,7 +128,7 @@ struct test_coveragecleaner_data {
}
void
- checkCleanSnap(
+ checkCleanSnapInt(
std::vector<const Geometry*> cov,
double snapDist,
std::vector<const Geometry*> expected)
@@ -140,7 +140,7 @@ struct test_coveragecleaner_data {
}
void
- checkCleanSnap(
+ checkCleanSnapInt(
std::vector<const Geometry*> cov,
double snapDist)
{
@@ -153,7 +153,7 @@ struct test_coveragecleaner_data {
{
std::vector<std::unique_ptr<Geometry>> cov = readArray(covStrs);
std::vector<const Geometry*> covArr = toArray(cov);
- checkCleanSnap(covArr, snapDist);
+ checkCleanSnapInt(covArr, snapDist);
}
void
@@ -166,7 +166,7 @@ struct test_coveragecleaner_data {
std::vector<const Geometry*> covArr = toArray(cov);
std::vector<std::unique_ptr<Geometry>> exp = readArray(expStrs);
std::vector<const Geometry*> expArr = toArray(exp);
- checkCleanSnap(covArr, snapDist, expArr);
+ checkCleanSnapInt(covArr, snapDist, expArr);
}
// void
commit a1f91eacad2904c6249f46c7f9cec42b2bc91d67
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 28 12:14:17 2025 -0700
tests
diff --git a/tests/unit/coverage/CoverageCleanerTest.cpp b/tests/unit/coverage/CoverageCleanerTest.cpp
index 338ed85ff..e137d8943 100644
--- a/tests/unit/coverage/CoverageCleanerTest.cpp
+++ b/tests/unit/coverage/CoverageCleanerTest.cpp
@@ -128,14 +128,10 @@ struct test_coveragecleaner_data {
}
void
- checkCleanSnap(std::vector<const Geometry*> cov, double snapDist)
- {
- std::vector<std::unique_ptr<Geometry>> covClean = CoverageCleaner::clean(cov, snapDist, 0);
- checkValidCoverage(toArray(covClean), snapDist);
- }
-
- void
- checkCleanSnap(std::vector<const Geometry*> cov, double snapDist, std::vector<const Geometry*> expected)
+ checkCleanSnap(
+ std::vector<const Geometry*> cov,
+ double snapDist,
+ std::vector<const Geometry*> expected)
{
std::vector<std::unique_ptr<Geometry>> actualPtr = CoverageCleaner::clean(cov, snapDist, 0);
std::vector<const Geometry*> actual = toArray(actualPtr);
@@ -144,13 +140,43 @@ struct test_coveragecleaner_data {
}
void
- checkCleanSnap(const std::string& wkt, double snapDist)
+ checkCleanSnap(
+ std::vector<const Geometry*> cov,
+ double snapDist)
{
- std::vector<std::unique_ptr<Geometry>> cov = readArray(wkt);
- auto covArr = toArray(cov);
+ std::vector<std::unique_ptr<Geometry>> covClean = CoverageCleaner::clean(cov, snapDist, 0);
+ checkValidCoverage(toArray(covClean), snapDist);
+ }
+
+ void
+ checkCleanSnap(const std::vector<std::string>& covStrs, double snapDist)
+ {
+ std::vector<std::unique_ptr<Geometry>> cov = readArray(covStrs);
+ std::vector<const Geometry*> covArr = toArray(cov);
checkCleanSnap(covArr, snapDist);
}
+ void
+ checkCleanSnap(
+ const std::vector<std::string>& covStrs,
+ double snapDist,
+ const std::vector<std::string>& expStrs)
+ {
+ std::vector<std::unique_ptr<Geometry>> cov = readArray(covStrs);
+ std::vector<const Geometry*> covArr = toArray(cov);
+ std::vector<std::unique_ptr<Geometry>> exp = readArray(expStrs);
+ std::vector<const Geometry*> expArr = toArray(exp);
+ checkCleanSnap(covArr, snapDist, expArr);
+ }
+
+ // void
+ // checkCleanSnap(const std::string& wkt, double snapDist)
+ // {
+ // std::unique_ptr<Geometry> covGeom = r.read(wkt);
+ // auto covArr = toArray(covGeom);
+ // checkCleanSnap(covArr, snapDist);
+ // }
+
void
checkValidCoverage(std::vector<const Geometry*> coverage, double tolerance)
{
@@ -162,10 +188,10 @@ struct test_coveragecleaner_data {
}
std::vector<std::unique_ptr<Geometry>>
- readArray(std::vector<std::string>& wkts)
+ readArray(const std::vector<std::string>& wkts)
{
std::vector<std::unique_ptr<Geometry>> geometries;
- for (std::string& wkt : wkts) {
+ for (const std::string& wkt : wkts) {
auto geom = r.read(wkt);
if (geom != nullptr) {
geometries.push_back(std::move(geom));
@@ -174,16 +200,16 @@ struct test_coveragecleaner_data {
return geometries;
}
- std::vector<std::unique_ptr<Geometry>>
- readArray(const std::string& wkt)
- {
- auto geom = r.read(wkt);
- std::vector<std::unique_ptr<Geometry>> geoms;
- for (std::size_t i = 0; i < geom->getNumGeometries(); i++) {
- geoms.emplace_back(geom->getGeometryN(i)->clone().release());
- }
- return geoms;
- }
+ // std::vector<std::unique_ptr<Geometry>>
+ // readArray(const std::string& wkt)
+ // {
+ // auto geom = r.read(wkt);
+ // std::vector<std::unique_ptr<Geometry>> geoms;
+ // for (std::size_t i = 0; i < geom->getNumGeometries(); i++) {
+ // geoms.emplace_back(geom->getGeometryN(i)->clone().release());
+ // }
+ // return geoms;
+ // }
};
@@ -207,7 +233,211 @@ void object::test<1> ()
}
+// testSingleNearMatch
+template<>
+template<>
+void object::test<2>()
+{
+ checkCleanSnap(
+ {
+ "POLYGON ((1 9, 9 9, 9 4.99, 1 5, 1 9))",
+ "POLYGON ((1 1, 1 5, 9 5, 9 1, 1 1))"
+ },
+ 0.1);
+}
+// testManyNearMatches
+template<>
+template<>
+void object::test<3>()
+{
+ checkCleanSnap(
+ {
+ "POLYGON ((1 9, 9 9, 9 5, 8 5, 7 5, 4 5.5, 3 5, 2 5, 1 5, 1 9))",
+ "POLYGON ((1 1, 1 4.99, 2 5.01, 3.01 4.989, 5 3, 6.99 4.99, 7.98 4.98, 9 5, 9 1, 1 1))"
+ },
+ 0.1);
+}
+
+// testPolygonSnappedPreserved
+// Tests that if interior point lies in a spike that is snapped away, polygon is still in result
+template<>
+template<>
+void object::test<4>()
+{
+ checkCleanSnap(
+ {"POLYGON ((90 0, 10 0, 89.99 30, 90 100, 90 0))"},
+ 0.1,
+ {"POLYGON ((90 0, 10 0, 89.99 30, 90 0))"}
+ );
+}
+
+// testPolygonsSnappedPreserved
+// Tests that if interior point lies in a spike that is snapped away, polygon is still in result
+template<>
+template<>
+void object::test<5>()
+{
+ checkCleanSnap(
+ {
+ "POLYGON ((0 0, 0 2, 5 2, 5 8, 5.01 0, 0 0))",
+ "POLYGON ((0 8, 5 8, 5 2, 0 2, 0 8))"
+ },
+ 0.02,
+ {
+ "POLYGON ((0 0, 0 2, 5 2, 5.01 0, 0 0))",
+ "POLYGON ((0 8, 5 8, 5 2, 0 2, 0 8))"
+ });
+}
+
+// testMergeGapToLongestBorder
+template<>
+template<>
+void object::test<6>()
+{
+ checkCleanGapWidth(
+ "GEOMETRYCOLLECTION (POLYGON ((1 9, 9 9, 9 5, 1 5, 1 9)), POLYGON ((5 1, 5 5, 1 5, 5 1)), POLYGON ((5 1, 5.1 5, 9 5, 5 1)))",
+ 1,
+ "GEOMETRYCOLLECTION (POLYGON ((5.1 5, 5 5, 1 5, 1 9, 9 9, 9 5, 5.1 5)), POLYGON ((5 1, 1 5, 5 5, 5 1)), POLYGON ((5 1, 5 5, 5.1 5, 9 5, 5 1)))"
+ );
+}
+
+std::string covWithGaps = "GEOMETRYCOLLECTION (POLYGON ((1 3, 9 3, 9 1, 1 1, 1 3)), POLYGON ((1 3, 1 9, 4 9, 4 3, 3 4, 1 3)), POLYGON ((4 9, 7 9, 7 3, 6 5, 5 5, 4 3, 4 9)), POLYGON ((7 9, 9 9, 9 3, 8 3.1, 7 3, 7 9)))";
+
+// testMergeGapWidth_0
+template<>
+template<>
+void object::test<7>()
+{
+ checkCleanGapWidth(covWithGaps,
+ 0,
+ "GEOMETRYCOLLECTION (POLYGON ((9 3, 9 1, 1 1, 1 3, 4 3, 7 3, 9 3)), POLYGON ((1 9, 4 9, 4 3, 3 4, 1 3, 1 9)), POLYGON ((6 5, 5 5, 4 3, 4 9, 7 9, 7 3, 6 5)), POLYGON ((7 9, 9 9, 9 3, 8 3.1, 7 3, 7 9)))"
+ );
+}
+
+// testMergeGapWidth_1
+template<>
+template<>
+void object::test<8>()
+{
+ checkCleanGapWidth(covWithGaps,
+ 1,
+ "GEOMETRYCOLLECTION (POLYGON ((7 3, 9 3, 9 1, 1 1, 1 3, 4 3, 7 3)), POLYGON ((1 9, 4 9, 4 3, 1 3, 1 9)), POLYGON ((7 3, 6 5, 5 5, 4 3, 4 9, 7 9, 7 3)), POLYGON ((7 9, 9 9, 9 3, 7 3, 7 9)))"
+ );
+}
+
+// testMergeGapWidth_2
+template<>
+template<>
+void object::test<9>()
+{
+ checkCleanGapWidth(covWithGaps,
+ 2,
+ "GEOMETRYCOLLECTION (POLYGON ((9 3, 9 1, 1 1, 1 3, 4 3, 7 3, 9 3)), POLYGON ((1 9, 4 9, 4 3, 1 3, 1 9)), POLYGON ((7 3, 4 3, 4 9, 7 9, 7 3)), POLYGON ((9 9, 9 3, 7 3, 7 9, 9 9)))"
+ );
+}
+
+std::string covWithOverlap = "GEOMETRYCOLLECTION (POLYGON ((1 3, 5 3, 4 1, 1 1, 1 3)), POLYGON ((1 3, 1 9, 4 9, 4 3, 3 1.9, 1 3)))";
+
+// testMergeOverlapMinArea
+template<>
+template<>
+void object::test<10>()
+{
+ checkCleanOverlapMerge(covWithOverlap,
+ CoverageCleaner::MERGE_MIN_AREA,
+ "GEOMETRYCOLLECTION (POLYGON ((5 3, 4 1, 1 1, 1 3, 4 3, 5 3)), POLYGON ((1 9, 4 9, 4 3, 1 3, 1 9)))"
+ );
+}
+
+// testMergeOverlapMaxArea
+template<>
+template<>
+void object::test<11>()
+{
+ checkCleanOverlapMerge(covWithOverlap,
+ CoverageCleaner::MERGE_MAX_AREA,
+ "GEOMETRYCOLLECTION (POLYGON ((1 1, 1 3, 3 1.9, 4 3, 5 3, 4 1, 1 1)), POLYGON ((1 3, 1 9, 4 9, 4 3, 3 1.9, 1 3)))"
+ );
+}
+
+// testMergeOverlapMinId
+template<>
+template<>
+void object::test<12>()
+{
+ checkCleanOverlapMerge(covWithOverlap,
+ CoverageCleaner::MERGE_MIN_INDEX,
+ "GEOMETRYCOLLECTION (POLYGON ((5 3, 4 1, 1 1, 1 3, 4 3, 5 3)), POLYGON ((1 9, 4 9, 4 3, 1 3, 1 9)))"
+ );
+}
+
+// testMergeOverlap2
+template<>
+template<>
+void object::test<13>()
+{
+ checkCleanSnap(
+ {
+ "POLYGON ((5 9, 9 9, 9 1, 5 1, 5 9))",
+ "POLYGON ((1 5, 5 5, 5 2, 1 2, 1 5))",
+ "POLYGON ((2 7, 5 7, 5 4, 2 4, 2 7))"
+ },
+ 0.1,
+ {
+ "POLYGON ((5 1, 5 2, 5 4, 5 5, 5 7, 5 9, 9 9, 9 1, 5 1))",
+ "POLYGON ((5 2, 1 2, 1 5, 2 5, 5 5, 5 4, 5 2))",
+ "POLYGON ((5 5, 2 5, 2 7, 5 7, 5 5))"
+ });
+}
+
+// testMergeOverlap
+template<>
+template<>
+void object::test<14>()
+{
+ checkCleanOverlapMerge(
+ "GEOMETRYCOLLECTION (POLYGON ((5 9, 9 9, 9 1, 5 1, 5 9)), POLYGON ((1 5, 5 5, 5 2, 1 2, 1 5)), POLYGON ((2 7, 5 7, 5 4, 2 4, 2 7)))",
+ CoverageCleaner::MERGE_LONGEST_BORDER,
+ "GEOMETRYCOLLECTION (POLYGON ((5 7, 5 9, 9 9, 9 1, 5 1, 5 2, 5 4, 5 5, 5 7)), POLYGON ((5 2, 1 2, 1 5, 2 5, 5 5, 5 4, 5 2)), POLYGON ((2 5, 2 7, 5 7, 5 5, 2 5)))"
+ );
+}
+
+//-------------------------------------------
+
+//-- a duplicate coverage element is assigned to the lowest result index
+// testDuplicateItems
+template<>
+template<>
+void object::test<15>()
+{
+ checkClean(
+ "GEOMETRYCOLLECTION (POLYGON ((1 9, 9 1, 1 1, 1 9)), POLYGON ((1 9, 9 1, 1 1, 1 9)))",
+ "GEOMETRYCOLLECTION (POLYGON ((1 9, 9 1, 1 1, 1 9)), POLYGON EMPTY)"
+ );
+}
+
+// testCoveredItem
+template<>
+template<>
+void object::test<16>()
+{
+ checkClean(
+ "GEOMETRYCOLLECTION (POLYGON ((1 9, 9 9, 9 4, 1 4, 1 9)), POLYGON ((2 5, 2 8, 8 8, 8 5, 2 5)))",
+ "GEOMETRYCOLLECTION (POLYGON ((9 9, 9 4, 1 4, 1 9, 9 9)), POLYGON EMPTY)"
+ );
+}
+
+// testCoveredItemMultiPolygon
+template<>
+template<>
+void object::test<17>()
+{
+ checkClean(
+ "GEOMETRYCOLLECTION (MULTIPOLYGON (((1 1, 1 5, 5 5, 5 1, 1 1)), ((6 5, 6 1, 9 1, 6 5))), POLYGON ((6 1, 6 5, 9 1, 6 1)))",
+ "GEOMETRYCOLLECTION (MULTIPOLYGON (((1 5, 5 5, 5 1, 1 1, 1 5)), ((6 5, 9 1, 6 1, 6 5))), POLYGON EMPTY)"
+ );
+}
commit 0bac2cb5566b8ff37b5a9717ad286236c99a9544
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 28 10:54:52 2025 -0700
build
diff --git a/include/geos/coverage/CoverageValidator.h b/include/geos/coverage/CoverageValidator.h
index 2185d23cb..f285ee6e7 100644
--- a/include/geos/coverage/CoverageValidator.h
+++ b/include/geos/coverage/CoverageValidator.h
@@ -115,6 +115,18 @@ public:
static bool isValid(
std::vector<const Geometry*>& coverage);
+ /**
+ * Tests whether a polygonal coverage is valid
+ * and contains no gaps narrower than a specified width.
+ *
+ * @param coverage an array of polygons forming a coverage
+ * @param gapWidth the maximum width of invalid gaps
+ * @return true if the coverage is valid with no narrow gaps
+ */
+ static bool isValid(
+ std::vector<const Geometry*>& coverage,
+ double gapWidth);
+
/**
* Tests if some element of an array of geometries is a coverage invalidity
* indicator.
diff --git a/src/coverage/CoverageValidator.cpp b/src/coverage/CoverageValidator.cpp
index 644558334..19a933ef6 100644
--- a/src/coverage/CoverageValidator.cpp
+++ b/src/coverage/CoverageValidator.cpp
@@ -36,6 +36,15 @@ CoverageValidator::isValid(std::vector<const Geometry*>& coverage)
return ! hasInvalidResult(v.validate());
}
+/* public static */
+bool
+CoverageValidator::isValid(std::vector<const Geometry*>& coverage, double p_gapWidth)
+{
+ CoverageValidator v(coverage);
+ v.setGapWidth(p_gapWidth);
+ return ! hasInvalidResult(v.validate());
+}
+
/* public static */
bool
CoverageValidator::hasInvalidResult(const std::vector<std::unique_ptr<Geometry>>& validateResult)
diff --git a/tests/unit/coverage/CoverageCleanerTest.cpp b/tests/unit/coverage/CoverageCleanerTest.cpp
new file mode 100644
index 000000000..338ed85ff
--- /dev/null
+++ b/tests/unit/coverage/CoverageCleanerTest.cpp
@@ -0,0 +1,214 @@
+//
+// Test Suite for geos::coverage::CoverageGapFinderTest class.
+
+#include <tut/tut.hpp>
+#include <utility.h>
+
+// geos
+#include <geos/coverage/CoverageCleaner.h>
+#include <geos/coverage/CoverageValidator.h>
+
+using geos::coverage::CoverageCleaner;
+using geos::coverage::CoverageValidator;
+
+namespace tut {
+//
+// Test Group
+//
+
+// Common data used by all tests
+struct test_coveragecleaner_data {
+
+ WKTReader r;
+ WKTWriter w;
+
+ void
+ printResult(
+ const std::unique_ptr<Geometry>& expected,
+ const std::unique_ptr<Geometry>& actual)
+ {
+ std::cout << std::endl;
+ std::cout << "--expect--" << std::endl;
+ std::cout << w.write(expected.get()) << std::endl;
+ std::cout << "--actual--" << std::endl;
+ std::cout << w.write(actual.get()) << std::endl;
+ }
+
+ void
+ printResult(
+ const std::vector<std::unique_ptr<Geometry>>& expected,
+ const std::vector<std::unique_ptr<Geometry>>& actual)
+ {
+ std::cout << std::endl;
+ std::cout << "--expect--" << std::endl;
+ for (auto& e : expected) {
+ std::cout << w.write(e.get()) << std::endl;
+ }
+ std::cout << "--actual--" << std::endl;
+ for (auto& a : actual) {
+ std::cout << w.write(a.get()) << std::endl;
+ }
+ std::cout << std::endl;
+ }
+
+
+ std::vector<const Geometry*>
+ toArray(const std::unique_ptr<Geometry>& geom)
+ {
+ std::vector<const Geometry*> geoms;
+ for (std::size_t i = 0; i < geom->getNumGeometries(); i++) {
+ geoms.push_back(geom->getGeometryN(i));
+ }
+ return geoms;
+ }
+
+ std::vector<const Geometry*>
+ toArray(const std::vector<std::unique_ptr<Geometry>>& cov)
+ {
+ std::vector<const Geometry*> geoms;
+ for (const auto& g : cov) {
+ geoms.push_back(g.get());
+ }
+ return geoms;
+ }
+
+ void
+ checkEqual(std::vector<const Geometry*>& expected, std::vector<const Geometry*>& actual)
+ {
+ ensure_equals("checkEqual sizes", actual.size(), expected.size());
+ for (std::size_t i = 0; i < actual.size(); i++) {
+ ensure_equals_geometry(actual[i], expected[i]);
+ }
+ }
+
+ void
+ checkEqual(
+ std::vector<std::unique_ptr<Geometry>>& expected,
+ std::vector<std::unique_ptr<Geometry>>& actual)
+ {
+ auto actualArr = toArray(actual);
+ auto expectedArr = toArray(expected);
+ checkEqual(expectedArr, actualArr);
+ }
+
+ void
+ checkClean(const std::string& wkt, const std::string& wktExpected)
+ {
+ std::unique_ptr<Geometry> geom = r.read(wkt);
+ std::vector<const Geometry*> cov = toArray(geom);
+ std::vector<std::unique_ptr<Geometry>> actual = CoverageCleaner::cleanGapWidth(cov, 0);
+ std::unique_ptr<Geometry> expected = r.read(wktExpected);
+ auto expectedArr = toArray(expected);
+ auto actualArr = toArray(actual);
+ checkEqual(expectedArr, actualArr);
+ }
+
+ void
+ checkCleanGapWidth(const std::string& wkt, double gapWidth, const std::string& wktExpected)
+ {
+ std::unique_ptr<Geometry> geom = r.read(wkt);
+ std::vector<const Geometry*> cov = toArray(geom);
+ std::vector<std::unique_ptr<Geometry>> actual = CoverageCleaner::cleanGapWidth(cov, gapWidth);
+ std::unique_ptr<Geometry> expected = r.read(wktExpected);
+ auto expectedArr = toArray(expected);
+ auto actualArr = toArray(actual);
+ checkEqual(expectedArr, actualArr);
+ }
+
+ void
+ checkCleanOverlapMerge(const std::string& wkt, int mergeStrategy, const std::string& wktExpected)
+ {
+ std::unique_ptr<Geometry> geom = r.read(wkt);
+ std::vector<const Geometry*> cov = toArray(geom);
+ std::vector<std::unique_ptr<Geometry>> actual = CoverageCleaner::cleanOverlapGap(cov, mergeStrategy, 0);
+ std::unique_ptr<Geometry> expected = r.read(wktExpected);
+ auto expectedArr = toArray(expected);
+ auto actualArr = toArray(actual);
+ checkEqual(expectedArr, actualArr);
+ }
+
+ void
+ checkCleanSnap(std::vector<const Geometry*> cov, double snapDist)
+ {
+ std::vector<std::unique_ptr<Geometry>> covClean = CoverageCleaner::clean(cov, snapDist, 0);
+ checkValidCoverage(toArray(covClean), snapDist);
+ }
+
+ void
+ checkCleanSnap(std::vector<const Geometry*> cov, double snapDist, std::vector<const Geometry*> expected)
+ {
+ std::vector<std::unique_ptr<Geometry>> actualPtr = CoverageCleaner::clean(cov, snapDist, 0);
+ std::vector<const Geometry*> actual = toArray(actualPtr);
+ checkValidCoverage(actual, snapDist);
+ checkEqual(expected, actual);
+ }
+
+ void
+ checkCleanSnap(const std::string& wkt, double snapDist)
+ {
+ std::vector<std::unique_ptr<Geometry>> cov = readArray(wkt);
+ auto covArr = toArray(cov);
+ checkCleanSnap(covArr, snapDist);
+ }
+
+ void
+ checkValidCoverage(std::vector<const Geometry*> coverage, double tolerance)
+ {
+ for (const Geometry* geom : coverage) {
+ ensure("checkValidCoverage geom->isValid()", geom->isValid());
+ }
+ bool isValid = CoverageValidator::isValid(coverage, tolerance);
+ ensure("checkValidCoverage CoverageValidator", isValid);
+ }
+
+ std::vector<std::unique_ptr<Geometry>>
+ readArray(std::vector<std::string>& wkts)
+ {
+ std::vector<std::unique_ptr<Geometry>> geometries;
+ for (std::string& wkt : wkts) {
+ auto geom = r.read(wkt);
+ if (geom != nullptr) {
+ geometries.push_back(std::move(geom));
+ }
+ }
+ return geometries;
+ }
+
+ std::vector<std::unique_ptr<Geometry>>
+ readArray(const std::string& wkt)
+ {
+ auto geom = r.read(wkt);
+ std::vector<std::unique_ptr<Geometry>> geoms;
+ for (std::size_t i = 0; i < geom->getNumGeometries(); i++) {
+ geoms.emplace_back(geom->getGeometryN(i)->clone().release());
+ }
+ return geoms;
+ }
+
+};
+
+
+
+typedef test_group<test_coveragecleaner_data> group;
+typedef group::object object;
+
+group test_coveragecleaner_data("geos::coverage::CoverageCleaner");
+
+
+
+// testCoverageWithEmpty
+template<>
+template<>
+void object::test<1> ()
+{
+ checkClean(
+ "GEOMETRYCOLLECTION (POLYGON ((1 9, 9 9, 9 4, 1 4, 1 9)), POLYGON EMPTY, POLYGON ((2 1, 2 5, 8 5, 8 1, 2 1)))",
+ "GEOMETRYCOLLECTION (POLYGON ((1 4, 1 9, 9 9, 9 4, 8 4, 2 4, 1 4)), POLYGON EMPTY, POLYGON ((8 1, 2 1, 2 4, 8 4, 8 1)))");
+}
+
+
+
+
+
+
+} // namespace tut
commit 9bbcd6b873a9f8ea00e0f1542274fff75312fcb5
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Tue May 27 16:28:58 2025 -0700
doxy
diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h
index a081e030a..8452a4925 100644
--- a/include/geos/coverage/CoverageCleaner.h
+++ b/include/geos/coverage/CoverageCleaner.h
@@ -262,7 +262,7 @@ public:
* and merging gaps which are narrower than a specified width.
*
* @param coverage an array of polygonal geometries to clean
- * @param coverage the maximum width of gaps to merge
+ * @param maxWidth the maximum width of gaps to merge
* @return the clean coverage
*/
static std::vector<std::unique_ptr<Geometry>> cleanGapWidth(
commit 219ba596545b4110f6b935cb0a0dfabd157a31b9
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Tue May 27 16:15:45 2025 -0700
doxy
diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h
index 6c8b3239d..a081e030a 100644
--- a/include/geos/coverage/CoverageCleaner.h
+++ b/include/geos/coverage/CoverageCleaner.h
@@ -174,7 +174,7 @@ private:
static Envelope extent(std::vector<const Geometry*>& geoms);
void mergeOverlaps(
- std::map<std::size_t, std::vector<std::size_t>>& overlapParentMap_p);
+ std::map<std::size_t, std::vector<std::size_t>>& overlapMap);
std::unique_ptr<CleanCoverage::MergeStrategy> mergeStrategy(
int mergeStrategyId);
@@ -210,7 +210,7 @@ public:
*
* @param coverage an array of polygonal geometries to clean
*/
- CoverageCleaner(std::vector<const Geometry*>& p_coverage);
+ CoverageCleaner(std::vector<const Geometry*>& coverage);
/**
* Cleans a set of polygonal geometries to form a valid coverage,
@@ -261,7 +261,7 @@ public:
* with default snapping tolerance and overlap merging,
* and merging gaps which are narrower than a specified width.
*
- * @param p_coverage an array of polygonal geometries to clean
+ * @param coverage an array of polygonal geometries to clean
* @param coverage the maximum width of gaps to merge
* @return the clean coverage
*/
commit af2c281c1e508b0973ab67324f8db6ee91e6f85f
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Tue May 27 16:00:55 2025 -0700
doxy
diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h
index cd664b70a..6c8b3239d 100644
--- a/include/geos/coverage/CoverageCleaner.h
+++ b/include/geos/coverage/CoverageCleaner.h
@@ -217,57 +217,57 @@ public:
* allowing all cleaning parameters to be specified.
*
* @param coverage an array of polygonal geometries to clean
- * @param snappingDistance the distance tolerance for snapping
- * @param overlapMergeStrategy the strategy to use for merging overlaps
- * @param maxGapWidth the maximum width of gaps to merge
+ * @param snapDistance the distance tolerance for snapping
+ * @param mergeStrategy the strategy to use for merging overlaps
+ * @param maxWidth the maximum width of gaps to merge
* @return the clean coverage
*/
static std::vector<std::unique_ptr<Geometry>> clean(
- std::vector<const Geometry*>& p_coverage,
- double p_snappingDistance,
- int p_overlapMergeStrategy,
- double p_maxGapWidth);
+ std::vector<const Geometry*>& coverage,
+ double snapDistance,
+ int mergeStrategy,
+ double maxWidth);
/**
* Cleans a set of polygonal geometries to form a valid coverage,
* using the default overlap merge strategy {@link #MERGE_LONGEST_BORDER}.
*
* @param coverage an array of polygonal geometries to clean
- * @param snappingDistance the distance tolerance for snapping
- * @param maxGapWidth the maximum width of gaps to merge
+ * @param snapDistance the distance tolerance for snapping
+ * @param maxWidth the maximum width of gaps to merge
* @return the clean coverage
*/
static std::vector<std::unique_ptr<Geometry>> clean(
- std::vector<const Geometry*>& p_coverage,
- double p_snappingDistance,
- double p_maxGapWidth);
+ std::vector<const Geometry*>& coverage,
+ double snapDistance,
+ double maxWidth);
/**
* Cleans a set of polygonal geometries to form a valid coverage,
* using the default snapping distance tolerance.
*
* @param coverage an array of polygonal geometries to clean
- * @param overlapMergeStrategy the strategy to use for merging overlaps
- * @param maxGapWidth the maximum width of gaps to merge
+ * @param mergeStrategy the strategy to use for merging overlaps
+ * @param maxWidth the maximum width of gaps to merge
* @return the clean coverage
*/
static std::vector<std::unique_ptr<Geometry>> cleanOverlapGap(
- std::vector<const Geometry*>& p_coverage,
- int p_overlapMergeStrategy,
- double p_maxGapWidth);
+ std::vector<const Geometry*>& coverage,
+ int mergeStrategy,
+ double maxWidth);
/**
* Cleans a set of polygonal geometries to form a valid coverage,
* with default snapping tolerance and overlap merging,
* and merging gaps which are narrower than a specified width.
*
- * @param coverage an array of polygonal geometries to clean
- * @param maxGapWidth the maximum width of gaps to merge
+ * @param p_coverage an array of polygonal geometries to clean
+ * @param coverage the maximum width of gaps to merge
* @return the clean coverage
*/
static std::vector<std::unique_ptr<Geometry>> cleanGapWidth(
- std::vector<const Geometry*>& p_coverage,
- double p_maxGapWidth);
+ std::vector<const Geometry*>& coverage,
+ double maxWidth);
/**
* Sets the snapping distance tolerance.
commit abbaa2e0c1e962614cbed8e662e9e76e7e24bea9
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Tue May 27 15:40:19 2025 -0700
static methods
diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h
index e8ab86b9b..cd664b70a 100644
--- a/include/geos/coverage/CoverageCleaner.h
+++ b/include/geos/coverage/CoverageCleaner.h
@@ -205,8 +205,70 @@ private:
public:
+ /**
+ * Create a new cleaner instance for a set of polygonal geometries.
+ *
+ * @param coverage an array of polygonal geometries to clean
+ */
CoverageCleaner(std::vector<const Geometry*>& p_coverage);
+ /**
+ * Cleans a set of polygonal geometries to form a valid coverage,
+ * allowing all cleaning parameters to be specified.
+ *
+ * @param coverage an array of polygonal geometries to clean
+ * @param snappingDistance the distance tolerance for snapping
+ * @param overlapMergeStrategy the strategy to use for merging overlaps
+ * @param maxGapWidth the maximum width of gaps to merge
+ * @return the clean coverage
+ */
+ static std::vector<std::unique_ptr<Geometry>> clean(
+ std::vector<const Geometry*>& p_coverage,
+ double p_snappingDistance,
+ int p_overlapMergeStrategy,
+ double p_maxGapWidth);
+
+ /**
+ * Cleans a set of polygonal geometries to form a valid coverage,
+ * using the default overlap merge strategy {@link #MERGE_LONGEST_BORDER}.
+ *
+ * @param coverage an array of polygonal geometries to clean
+ * @param snappingDistance the distance tolerance for snapping
+ * @param maxGapWidth the maximum width of gaps to merge
+ * @return the clean coverage
+ */
+ static std::vector<std::unique_ptr<Geometry>> clean(
+ std::vector<const Geometry*>& p_coverage,
+ double p_snappingDistance,
+ double p_maxGapWidth);
+
+ /**
+ * Cleans a set of polygonal geometries to form a valid coverage,
+ * using the default snapping distance tolerance.
+ *
+ * @param coverage an array of polygonal geometries to clean
+ * @param overlapMergeStrategy the strategy to use for merging overlaps
+ * @param maxGapWidth the maximum width of gaps to merge
+ * @return the clean coverage
+ */
+ static std::vector<std::unique_ptr<Geometry>> cleanOverlapGap(
+ std::vector<const Geometry*>& p_coverage,
+ int p_overlapMergeStrategy,
+ double p_maxGapWidth);
+
+ /**
+ * Cleans a set of polygonal geometries to form a valid coverage,
+ * with default snapping tolerance and overlap merging,
+ * and merging gaps which are narrower than a specified width.
+ *
+ * @param coverage an array of polygonal geometries to clean
+ * @param maxGapWidth the maximum width of gaps to merge
+ * @return the clean coverage
+ */
+ static std::vector<std::unique_ptr<Geometry>> cleanGapWidth(
+ std::vector<const Geometry*>& p_coverage,
+ double p_maxGapWidth);
+
/**
* Sets the snapping distance tolerance.
* The default is to use a small fraction of the input extent diameter.
diff --git a/src/coverage/CoverageCleaner.cpp b/src/coverage/CoverageCleaner.cpp
index d09557011..9093420e9 100644
--- a/src/coverage/CoverageCleaner.cpp
+++ b/src/coverage/CoverageCleaner.cpp
@@ -60,11 +60,55 @@ namespace geos { // geos
namespace coverage { // geos.coverage
-/**
- * Create a new cleaner instance for a set of polygonal geometries.
- *
- * @param coverage an array of polygonal geometries to clean
- */
+/* public static */
+std::vector<std::unique_ptr<Geometry>>
+CoverageCleaner::clean(std::vector<const Geometry*>& p_coverage,
+ double p_snappingDistance,
+ int p_overlapMergeStrategy,
+ double p_maxGapWidth)
+{
+ CoverageCleaner cc(p_coverage);
+ cc.setSnappingDistance(p_snappingDistance);
+ cc.setGapMaximumWidth(p_maxGapWidth);
+ cc.setOverlapMergeStrategy(p_overlapMergeStrategy);
+ cc.clean();
+ return cc.getResult();
+}
+
+
+/* public static */
+std::vector<std::unique_ptr<Geometry>>
+CoverageCleaner::clean(std::vector<const Geometry*>& p_coverage,
+ double p_snappingDistance,
+ double p_maxGapWidth)
+{
+ CoverageCleaner cc(p_coverage);
+ cc.setSnappingDistance(p_snappingDistance);
+ cc.setGapMaximumWidth(p_maxGapWidth);
+ cc.clean();
+ return cc.getResult();
+}
+
+
+/* public static */
+std::vector<std::unique_ptr<Geometry>>
+CoverageCleaner::cleanOverlapGap(std::vector<const Geometry*>& p_coverage,
+ int p_overlapMergeStrategy,
+ double p_maxGapWidth)
+{
+ return clean(p_coverage, -1, p_overlapMergeStrategy, p_maxGapWidth);
+}
+
+
+/* public static */
+std::vector<std::unique_ptr<Geometry>>
+CoverageCleaner::cleanGapWidth(std::vector<const Geometry*>& p_coverage,
+ double p_maxGapWidth)
+{
+ return clean(p_coverage, -1, p_maxGapWidth);
+}
+
+
/* public */
CoverageCleaner::CoverageCleaner(std::vector<const Geometry*>& p_coverage)
: coverage(p_coverage)
commit 99d1fdcb6ee51682d3cc7e370c486d8092f9e8aa
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Fri May 23 16:25:40 2025 -0700
codespell
diff --git a/include/geos/coverage/CleanCoverage.h b/include/geos/coverage/CleanCoverage.h
index 95615bd4c..49aee1886 100644
--- a/include/geos/coverage/CleanCoverage.h
+++ b/include/geos/coverage/CleanCoverage.h
@@ -218,7 +218,7 @@ public:
std::vector<std::size_t>& parentIndexes);
static std::size_t findMergeTarget(const Polygon* poly,
- MergeStrategy& strat,
+ MergeStrategy& strategy,
std::vector<std::size_t>& parentIndexes,
std::vector<std::unique_ptr<CleanArea>>& cov);
diff --git a/src/coverage/CleanCoverage.cpp b/src/coverage/CleanCoverage.cpp
index 302ecf722..2b616c5e0 100644
--- a/src/coverage/CleanCoverage.cpp
+++ b/src/coverage/CleanCoverage.cpp
@@ -76,7 +76,7 @@ CleanCoverage::mergeOverlap(const Polygon* overlap,
/* public static */
std::size_t
CleanCoverage::findMergeTarget(const Polygon* poly,
- MergeStrategy& strat,
+ MergeStrategy& strategy,
std::vector<std::size_t>& parentIndexes,
std::vector<std::unique_ptr<CleanArea>>& cov)
{
@@ -86,9 +86,9 @@ CleanCoverage::findMergeTarget(const Polygon* poly,
std::sort(indexesAsc.begin(), indexesAsc.end());
for (std::size_t index : indexesAsc) {
- strat.checkMergeTarget(index, cov[index].get(), poly);
+ strategy.checkMergeTarget(index, cov[index].get(), poly);
}
- return strat.getTarget();
+ return strategy.getTarget();
}
commit ee84829cc6639f2be38e2f49a65f06b7626ae5d8
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Fri May 23 16:16:06 2025 -0700
qt
diff --git a/include/geos/coverage/CleanCoverage.h b/include/geos/coverage/CleanCoverage.h
index 7ebd00e18..95615bd4c 100644
--- a/include/geos/coverage/CleanCoverage.h
+++ b/include/geos/coverage/CleanCoverage.h
@@ -17,6 +17,7 @@
#include <geos/geom/Envelope.h>
#include <geos/constants.h>
+#include <geos/index/quadtree/Quadtree.h>
#include <vector>
#include <memory>
@@ -37,7 +38,6 @@ namespace relateng {
}
namespace index {
namespace quadtree {
- class Quadtree;
}
}
}
commit 4d7c5deca2961823504d1458f2b23613c93a0094
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Fri May 23 15:55:03 2025 -0700
doxy
diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h
index b04b56e6e..e8ab86b9b 100644
--- a/include/geos/coverage/CoverageCleaner.h
+++ b/include/geos/coverage/CoverageCleaner.h
@@ -212,9 +212,9 @@ public:
* The default is to use a small fraction of the input extent diameter.
* A distance of zero prevents snapping from being used.
*
- * @param snappingDistance the snapping distance tolerance
+ * @param snapDistance the snapping distance tolerance
*/
- void setSnappingDistance(double p_snappingDistance);
+ void setSnappingDistance(double snapDistance);
/**
* Sets the overlap merge strategy to use.
commit ef44cf56a4d4f8831e9beb5d61627186a8314584
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Fri May 23 15:42:33 2025 -0700
build
diff --git a/include/geos/coverage/CleanCoverage.h b/include/geos/coverage/CleanCoverage.h
index 8d729c4d4..7ebd00e18 100644
--- a/include/geos/coverage/CleanCoverage.h
+++ b/include/geos/coverage/CleanCoverage.h
@@ -137,7 +137,7 @@ public:
AreaMergeStrategy(bool isMax) : m_isMax(isMax) {};
- std::size_t getTarget() const override {
+ std::size_t getTarget() const override {
return m_targetIndex;
}
@@ -177,7 +177,7 @@ public:
bool isBetter = m_isMax
? areaIndex > m_targetIndex
: areaIndex < m_targetIndex;
- if (m_targetIndex < 0 || isBetter) {
+ if (isBetter) {
m_targetIndex = areaIndex;
}
}
commit 6ff3c246417b780123e5dc2a1f48164b6c87e791
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Thu May 22 16:02:55 2025 -0700
builds
diff --git a/include/geos/coverage/CleanCoverage.h b/include/geos/coverage/CleanCoverage.h
index 636c11477..8d729c4d4 100644
--- a/include/geos/coverage/CleanCoverage.h
+++ b/include/geos/coverage/CleanCoverage.h
@@ -87,6 +87,8 @@ public:
public:
+ virtual ~MergeStrategy() = default;
+
virtual std::size_t getTarget() const = 0;
virtual void checkMergeTarget(
@@ -106,9 +108,11 @@ public:
public:
+ BorderMergeStrategy() {};
+
std::size_t getTarget() const override {
return m_targetIndex;
- }
+ };
void checkMergeTarget(std::size_t areaIndex, CleanArea* area, const Polygon* poly) override {
double borderLen = area == nullptr ? 0.0 : area->getBorderLength(poly);
@@ -116,7 +120,7 @@ public:
m_targetIndex = areaIndex;
m_targetBorderLen = borderLen;
}
- }
+ };
}; // BorderStrategy
diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h
index 364f252f4..b04b56e6e 100644
--- a/include/geos/coverage/CoverageCleaner.h
+++ b/include/geos/coverage/CoverageCleaner.h
@@ -16,6 +16,8 @@
#pragma once
#include <geos/index/strtree/TemplateSTRtree.h>
+#include <geos/coverage/CleanCoverage.h>
+#include <geos/geom/Envelope.h>
#include <geos/constants.h>
#include <geos/export.h>
@@ -37,6 +39,9 @@ namespace geom {
}
namespace index {
}
+namespace noding {
+ class SegmentString;
+}
namespace coverage {
class CleanCoverage;
}
@@ -126,9 +131,12 @@ class GEOS_DLL CoverageCleaner {
using CoordinateSequence = geos::geom::CoordinateSequence;
using Geometry = geos::geom::Geometry;
using GeometryFactory = geos::geom::GeometryFactory;
+ using Point = geos::geom::Point;
using Polygon = geos::geom::Polygon;
using LineString = geos::geom::LineString;
using LinearRing = geos::geom::LinearRing;
+ using Envelope = geos::geom::Envelope;
+ using SegmentString = geos::noding::SegmentString;
public:
@@ -144,11 +152,11 @@ public:
private:
std::vector<const Geometry*> coverage;
- double snappingDistance; // set to compute default
+ const GeometryFactory* geomFactory;
+ double snappingDistance;
+
double gapMaximumWidth = 0.0;
int overlapMergeStrategy = MERGE_LONGEST_BORDER;
-
- const GeometryFactory* geomFactory;
std::unique_ptr<index::strtree::TemplateSTRtree<std::size_t>> covIndex;
std::vector<std::unique_ptr<Polygon>> resultants;
std::unique_ptr<CleanCoverage> cleanCov;
@@ -159,8 +167,108 @@ private:
static constexpr double DEFAULT_SNAPPING_FACTOR = 1.0e8;
+
+ static double computeDefaultSnappingDistance(
+ std::vector<const Geometry*>& geoms);
+
+ static Envelope extent(std::vector<const Geometry*>& geoms);
+
+ void mergeOverlaps(
+ std::map<std::size_t, std::vector<std::size_t>>& overlapParentMap_p);
+
+ std::unique_ptr<CleanCoverage::MergeStrategy> mergeStrategy(
+ int mergeStrategyId);
+
+ void computeResultants(double tolerance);
+
+ void createCoverageIndex();
+
+ void classifyResult(std::vector<std::unique_ptr<Polygon>>& rs);
+
+ void classifyResultant(std::size_t resultIndex, const Polygon* resPoly);
+
+ static bool covers(const Geometry* poly, const Point* intPt);
+
+ std::vector<const Polygon*> findMergableGaps(
+ std::vector<const Polygon*> gaps);
+
+ bool isMergableGap(const Polygon* gap);
+
+ static std::vector<std::unique_ptr<geom::Polygon>> polygonize(
+ const Geometry* cleanEdges);
+
+ static bool isPolygonal(const Geometry* geom);
+
+ static std::vector<const Polygon*> toPolygonArray(
+ const Geometry* geom);
+
+
public:
+ CoverageCleaner(std::vector<const Geometry*>& p_coverage);
+
+ /**
+ * Sets the snapping distance tolerance.
+ * The default is to use a small fraction of the input extent diameter.
+ * A distance of zero prevents snapping from being used.
+ *
+ * @param snappingDistance the snapping distance tolerance
+ */
+ void setSnappingDistance(double p_snappingDistance);
+
+ /**
+ * Sets the overlap merge strategy to use.
+ * The default is {@link #MERGE_LONGEST_BORDER}.
+ *
+ * @param mergeStrategy the merge strategy code
+ */
+ void setOverlapMergeStrategy(int mergeStrategy);
+
+ /**
+ * Sets the maximum width of the gaps that will be filled and merged.
+ * 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.
+ *
+ * @param maxWidth the maximum gap width to merge
+ */
+ void setGapMaximumWidth(double maxWidth);
+
+ /**
+ * Cleans the coverage.
+ */
+ void clean();
+
+ /**
+ * Gets the cleaned coverage.
+ *
+ * @return the clean coverage
+ */
+ std::vector<std::unique_ptr<Geometry>> getResult();
+
+ /**
+ * Gets polygons representing the overlaps in the input,
+ * which have been merged.
+ *
+ * @return a list of overlap polygons
+ */
+ std::vector<const Polygon*> getOverlaps();
+
+ /**
+ * Gets polygons representing the gaps in the input
+ * which have been merged.
+ *
+ * @return a list of gap polygons
+ */
+ std::vector<const Polygon*> getMergedGaps();
+
+ std::unique_ptr<Geometry> toGeometry(
+ std::vector<SegmentString*>& segStrings,
+ const GeometryFactory* geomFact);
+
+ std::unique_ptr<Geometry> node(
+ std::vector<const Geometry*>& coverage,
+ double snapDistance);
+
/**
* Disable copy construction and assignment. Apparently needed to make this
* class compile under MSVC. (See https://stackoverflow.com/q/29565299)
@@ -168,9 +276,6 @@ public:
CoverageCleaner(const CoverageCleaner&) = delete;
CoverageCleaner& operator=(const CoverageCleaner&) = delete;
-
-
-
};
} // namespace geos.coverage
diff --git a/src/coverage/CoverageCleaner.cpp b/src/coverage/CoverageCleaner.cpp
index e419470b5..d09557011 100644
--- a/src/coverage/CoverageCleaner.cpp
+++ b/src/coverage/CoverageCleaner.cpp
@@ -25,6 +25,7 @@
#include <geos/geom/Point.h>
#include <geos/geom/Polygon.h>
#include <geos/index/strtree/STRtree.h>
+#include <geos/index/quadtree/Quadtree.h>
#include <geos/noding/NodedSegmentString.h>
#include <geos/noding/Noder.h>
#include <geos/noding/SegmentStringUtil.h>
@@ -32,6 +33,8 @@
#include <geos/operation/polygonize/Polygonizer.h>
#include <geos/operation/relateng/RelateNG.h>
#include <geos/operation/relateng/RelatePredicate.h>
+#include <geos/util/IllegalArgumentException.h>
+
using geos::algorithm::construct::MaximumInscribedCircle;
using geos::algorithm::locate::SimplePointInAreaLocator;
@@ -43,6 +46,7 @@ using geos::geom::MultiPolygon;
using geos::geom::Point;
using geos::geom::Polygon;
using geos::index::strtree::STRtree;
+using geos::index::quadtree::Quadtree;
using geos::noding::NodedSegmentString;
using geos::noding::Noder;
using geos::noding::SegmentStringUtil;
@@ -52,7 +56,6 @@ using geos::operation::relateng::RelateNG;
using geos::operation::relateng::RelatePredicate;
-
namespace geos { // geos
namespace coverage { // geos.coverage
@@ -66,7 +69,7 @@ namespace coverage { // geos.coverage
CoverageCleaner::CoverageCleaner(std::vector<const Geometry*>& p_coverage)
: coverage(p_coverage)
, geomFactory(p_coverage.empty() ? nullptr : coverage[0]->getFactory())
- , computeDefaultSnappingDistance(p_coverage)
+ , snappingDistance(computeDefaultSnappingDistance(p_coverage))
{}
/**
@@ -98,7 +101,7 @@ CoverageCleaner::setOverlapMergeStrategy(int mergeStrategy)
{
if (mergeStrategy < MERGE_LONGEST_BORDER ||
mergeStrategy > MERGE_MIN_INDEX)
- throw IllegalArgumentException("Invalid merge strategy code");
+ throw util::IllegalArgumentException("Invalid merge strategy code");
overlapMergeStrategy = mergeStrategy;
}
@@ -119,8 +122,6 @@ CoverageCleaner::setGapMaximumWidth(double maxWidth)
gapMaximumWidth = maxWidth;
}
-//TODO: support snap-rounding noder for precision reduction
-//TODO: add merge gaps by: area?
/**
* Cleans the coverage.
@@ -206,14 +207,14 @@ CoverageCleaner::extent(std::vector<const Geometry*>& geoms)
/* private */
void
CoverageCleaner::mergeOverlaps(
- std::map<std::size_t, std::vector<std::size_t>>& overlapParentMap)
+ std::map<std::size_t, std::vector<std::size_t>>& overlapParentMap_p)
{
- for (const auto& [resIndex, _] : overlapParentMap) {
+ for (const auto& [resIndex, _] : overlapParentMap_p) {
auto ms = mergeStrategy(overlapMergeStrategy);
cleanCov->mergeOverlap(
resultants[resIndex].get(),
*ms,
- overlapParentMap[resIndex]);
+ overlapParentMap_p[resIndex]);
}
}
@@ -232,7 +233,7 @@ CoverageCleaner::mergeStrategy(int mergeStrategyId)
case MERGE_MIN_INDEX:
return std::make_unique<CleanCoverage::IndexMergeStrategy>(false);
}
- throw IllegalArgumentException("Unknown merge strategy: " + mergeStrategyId);
+ throw util::IllegalArgumentException("CoverageCleaner::mergeStrategy - Unknown merge strategy");
}
@@ -249,7 +250,7 @@ CoverageCleaner::computeResultants(double tolerance)
//System.out.println("Noding: " + sw.getTimeString());
//sw.reset();
- std::unique_ptr<Geometry> cleanEdges = LineDissolver::dissolve(nodedEdges);
+ std::unique_ptr<Geometry> cleanEdges = LineDissolver::dissolve(nodedEdges.get());
//System.out.println("Dissolve: " + sw.getTimeString());
//sw.reset();
@@ -270,9 +271,9 @@ CoverageCleaner::computeResultants(double tolerance)
void
CoverageCleaner::createCoverageIndex()
{
- covIndex = std::make_unique<STRtree>();
+ covIndex = std::make_unique<index::strtree::TemplateSTRtree<std::size_t>>();
for (std::size_t i = 0; i < coverage.size(); i++) {
- covIndex->insert(coverage[i]->getEnvelopeInternal(), i);
+ covIndex->insert(*(coverage[i]->getEnvelopeInternal()), i);
}
}
@@ -294,7 +295,7 @@ CoverageCleaner::classifyResultant(std::size_t resultIndex, const Polygon* resPo
std::vector<std::size_t> overlapIndexes;
std::vector<std::size_t> candidateParentIndex;
- covIndex->query(intPt->getEnvelopeInternal(), candidateParentIndex);
+ covIndex->query(*(intPt->getEnvelopeInternal()), candidateParentIndex);
for (std::size_t i : candidateParentIndex) {
const Geometry* parent = coverage[i];
@@ -341,13 +342,13 @@ CoverageCleaner::covers(const Geometry* poly, const Point* intPt)
/* private */
std::vector<const Polygon*>
-CoverageCleaner::findMergableGaps(std::vector<const Polygon*> gaps)
+CoverageCleaner::findMergableGaps(std::vector<const Polygon*> p_gaps)
{
std::vector<const Polygon*> filtered;
- std::copy_if(gaps.begin(), gaps.end(),
+ std::copy_if(p_gaps.begin(), p_gaps.end(),
std::back_inserter(filtered),
- [](const Polygon* xgap) { return isMergableGap(gap); }
+ [this](const Polygon* gap) { return isMergableGap(gap); }
);
return filtered;
@@ -389,30 +390,35 @@ CoverageCleaner::toGeometry(
std::unique_ptr<LineString> line = geomFact->createLineString(std::move(cs));
lines.emplace_back(line.release());
}
- if (lines.size() == 1) return lines[0];
+ if (lines.size() == 1) return lines[0]->clone();
return geomFact->createMultiLineString(std::move(lines));
}
/* public static */
std::unique_ptr<Geometry>
-CoverageCleaner::node(std::vector<const Geometry*>& coverage, double snapDistance)
+CoverageCleaner::node(std::vector<const Geometry*>& p_coverage, double p_snapDistance)
{
- std::vector<SegmentString*> segs;
+ std::vector<const SegmentString*> csegs;
- for (const Geometry* geom : coverage) {
+ for (const Geometry* geom : p_coverage) {
//-- skip non-polygonal and empty elements
if (! isPolygonal(geom))
continue;
if (geom->isEmpty())
continue;
- SegmentStringUtil::extractSegmentStrings(geom, segs);
+ SegmentStringUtil::extractSegmentStrings(geom, csegs);
}
- SnappingNoder noder(snapDistance);
+ std::vector<SegmentString*> segs;
+ for (auto* css : csegs) {
+ segs.push_back(const_cast<SegmentString*>(css));
+ }
+
+ SnappingNoder noder(p_snapDistance);
noder.computeNodes(&segs);
std::unique_ptr<std::vector<SegmentString*>> nodedSegStrings(noder.getNodedSubstrings());
- for (SegmentString* ss : *segs) {
+ for (auto* ss : segs) {
delete ss;
}
@@ -428,8 +434,8 @@ CoverageCleaner::node(std::vector<const Geometry*>& coverage, double snapDistanc
bool
CoverageCleaner::isPolygonal(const Geometry* geom)
{
- return geom->getGeometryTypeId() == GEOS_POLYGON ||
- geom->getGeometryTypeId() == GEOS_MULTIPOLYGON;
+ return geom->getGeometryTypeId() == geom::GEOS_POLYGON ||
+ geom->getGeometryTypeId() == geom::GEOS_MULTIPOLYGON;
}
commit b6e7efad2186e11a4a8f65e951b3d7c735d5a216
Merge: 886c61809 31399d845
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 21 12:18:08 2025 -0700
Merge branch 'main' of github.com:libgeos/geos into main-covclean
commit 886c61809a96b7f32002b69828e021fa8ef6d6c0
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 21 12:18:02 2025 -0700
wip
diff --git a/include/geos/coverage/CleanCoverage.h b/include/geos/coverage/CleanCoverage.h
index 053b05cd2..636c11477 100644
--- a/include/geos/coverage/CleanCoverage.h
+++ b/include/geos/coverage/CleanCoverage.h
@@ -15,27 +15,29 @@
#pragma once
-// #include <geos/noding/BasicSegmentString.h>
+#include <geos/geom/Envelope.h>
+#include <geos/constants.h>
+#include <vector>
+#include <memory>
// Forward declarations
namespace geos {
namespace geom {
-class Envelope;
-class Geometry;
-class GeometryFactory;
-class LineString;
-class LinearRing;
-class Polygon;
+ class Geometry;
+ class GeometryFactory;
+ class LineString;
+ class LinearRing;
+ class Polygon;
}
namespace operation {
namespace relateng {
-class RelateNG;
+ class RelateNG;
}
}
namespace index {
namespace quadtree {
-class Quadtree;
+ class Quadtree;
}
}
}
@@ -44,7 +46,7 @@ class Quadtree;
namespace geos { // geos.
namespace coverage { // geos.coverage
-class GEOS_DLL CleanCoverage {
+class CleanCoverage {
using Envelope = geos::geom::Envelope;
using Geometry = geos::geom::Geometry;
@@ -76,7 +78,7 @@ public:
double getBorderLength(const Polygon* adjPoly);
double getArea();
bool isAdjacent(RelateNG& rel);
- std::unique_ptr<Geometry> union();
+ std::unique_ptr<Geometry> getUnion();
}; // CleanArea
@@ -136,6 +138,7 @@ public:
}
void checkMergeTarget(std::size_t areaIndex, CleanArea* area, const Polygon* poly) override {
+ (void)poly;
double areaVal = area == nullptr ? 0.0 : area->getArea();
bool isBetter = m_isMax
? areaVal > m_targetArea
@@ -165,6 +168,8 @@ public:
}
void checkMergeTarget(std::size_t areaIndex, CleanArea* area, const Polygon* poly) override {
+ (void)area;
+ (void)poly;
bool isBetter = m_isMax
? areaIndex > m_targetIndex
: areaIndex < m_targetIndex;
@@ -183,13 +188,13 @@ private:
* The areas in the clean coverage.
* Entries may be null, if no resultant corresponded to the input area.
*/
- std::vector<str::unique_ptr<CleanArea>> cov;
+ std::vector<std::unique_ptr<CleanArea>> cov;
//-- used for finding areas to merge gaps
std::unique_ptr<Quadtree> covIndex = nullptr;
void mergeGap(const Polygon* gap);
- CleanArea* indMaxBorderLength(const Polygon* poly, std::vector<CleanArea*>& areas);
+ CleanArea* findMaxBorderLength(const Polygon* poly, std::vector<CleanArea*>& areas);
std::vector<CleanArea*> findAdjacentAreas(const Geometry* poly);
@@ -217,6 +222,12 @@ public:
std::vector<std::unique_ptr<Geometry>> toCoverage(const GeometryFactory* geomFactory);
+ /**
+ * Disable copy construction and assignment. Apparently needed to make this
+ * class compile under MSVC. (See https://stackoverflow.com/q/29565299)
+ */
+ CleanCoverage(const CleanCoverage&) = delete;
+ CleanCoverage& operator=(const CleanCoverage&) = delete;
};
diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h
index 089c77bc9..364f252f4 100644
--- a/include/geos/coverage/CoverageCleaner.h
+++ b/include/geos/coverage/CoverageCleaner.h
@@ -15,19 +15,30 @@
#pragma once
-// #include <geos/noding/BasicSegmentString.h>
+#include <geos/index/strtree/TemplateSTRtree.h>
+#include <geos/constants.h>
+#include <geos/export.h>
+
+#include <vector>
+#include <memory>
+#include <map>
// Forward declarations
namespace geos {
namespace geom {
-class Coordinate;
-class CoordinateSequence;
-class Geometry;
-class GeometryFactory;
-class LineString;
-class LinearRing;
-class Polygon;
+ class Coordinate;
+ class CoordinateSequence;
+ class Geometry;
+ class GeometryFactory;
+ class LineString;
+ class LinearRing;
+ class Polygon;
+}
+namespace index {
+}
+namespace coverage {
+ class CleanCoverage;
}
}
@@ -138,7 +149,7 @@ private:
int overlapMergeStrategy = MERGE_LONGEST_BORDER;
const GeometryFactory* geomFactory;
- std::unique_ptr<STRtree> covIndex;
+ std::unique_ptr<index::strtree::TemplateSTRtree<std::size_t>> covIndex;
std::vector<std::unique_ptr<Polygon>> resultants;
std::unique_ptr<CleanCoverage> cleanCov;
std::map<std::size_t, std::vector<std::size_t>> overlapParentMap;
@@ -150,6 +161,12 @@ private:
public:
+ /**
+ * Disable copy construction and assignment. Apparently needed to make this
+ * class compile under MSVC. (See https://stackoverflow.com/q/29565299)
+ */
+ CoverageCleaner(const CoverageCleaner&) = delete;
+ CoverageCleaner& operator=(const CoverageCleaner&) = delete;
diff --git a/src/coverage/CleanCoverage.cpp b/src/coverage/CleanCoverage.cpp
index 01e23958c..302ecf722 100644
--- a/src/coverage/CleanCoverage.cpp
+++ b/src/coverage/CleanCoverage.cpp
@@ -15,6 +15,7 @@
#include <geos/coverage/CleanCoverage.h>
+#include <geos/coverage/CoverageUnion.h>
#include <geos/geom/Envelope.h>
#include <geos/geom/Geometry.h>
#include <geos/geom/GeometryFactory.h>
@@ -25,6 +26,7 @@
#include <geos/operation/relateng/IntersectionMatrixPattern.h>
#include <geos/operation/relateng/RelateNG.h>
+#include <algorithm>
using geos::geom::Envelope;
using geos::geom::Geometry;
@@ -40,6 +42,7 @@ using geos::operation::relateng::RelateNG;
namespace geos { // geos
namespace coverage { // geos.coverage
+using CleanArea = geos::coverage::CleanCoverage::CleanArea;
/* public */
CleanCoverage::CleanCoverage(std::size_t size)
@@ -79,7 +82,7 @@ CleanCoverage::findMergeTarget(const Polygon* poly,
{
//-- sort parent indexes ascending, so that overlaps merge to first parent by default
std::vector<size_t> indexesAsc;
- indexesAsc.copy(parentIndexes.begin(), parentIndexes.end(), back_inserter(indexesAsc));
+ std::copy(parentIndexes.begin(), parentIndexes.end(), back_inserter(indexesAsc));
std::sort(indexesAsc.begin(), indexesAsc.end());
for (std::size_t index : indexesAsc) {
@@ -186,7 +189,7 @@ CleanCoverage::toCoverage(const GeometryFactory* geomFactory)
cleanCov[i] = geomFactory->createEmpty(2);
}
else {
- cleanCov[i] = cov[i]->union();
+ cleanCov[i] = cov[i]->getUnion();
}
}
return cleanCov;
@@ -224,7 +227,7 @@ CleanCoverage::CleanArea::getBorderLength(const Polygon* adjPoly)
double len = 0.0;
for (const Polygon* poly : polys) {
//TODO: find longest connected border len
- auto border = OverlayNGRobust::overlay(
+ auto border = OverlayNGRobust::Overlay(
static_cast<const Geometry*>(poly),
static_cast<const Geometry*>(adjPoly),
OverlayNG::INTERSECTION);
@@ -265,13 +268,13 @@ CleanCoverage::CleanArea::isAdjacent(RelateNG& rel)
/* public */
std::unique_ptr<Geometry>
-CleanCoverage::CleanArea::union()
+CleanCoverage::CleanArea::getUnion()
{
std::vector<const Geometry*> geoms;
for (const Polygon* poly : polys) {
geoms.push_back(static_cast<const Geometry*>(poly));
}
- return CoverageUnion::union(geoms);
+ return CoverageUnion::Union(geoms);
}
commit 99e8bebb3aa9f0aec817109b6a9403e9aca7cebb
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 21 10:08:54 2025 -0700
wip
diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h
index 45a103c1f..089c77bc9 100644
--- a/include/geos/coverage/CoverageCleaner.h
+++ b/include/geos/coverage/CoverageCleaner.h
@@ -124,11 +124,11 @@ public:
/** Merge strategy that chooses polygon with longest common border */
static constexpr int MERGE_LONGEST_BORDER = 0;
/** Merge strategy that chooses polygon with maximum area */
- static constexpr int MERGE_MAX_AREA = 1;
+ static constexpr int MERGE_MAX_AREA = 1;
/** Merge strategy that chooses polygon with minimum area */
- static constexpr int MERGE_MIN_AREA = 2;
+ static constexpr int MERGE_MIN_AREA = 2;
/** Merge strategy that chooses polygon with smallest input index */
- static constexpr int MERGE_MIN_INDEX = 3;
+ static constexpr int MERGE_MIN_INDEX = 3;
private:
@@ -142,9 +142,9 @@ private:
std::vector<std::unique_ptr<Polygon>> resultants;
std::unique_ptr<CleanCoverage> cleanCov;
std::map<std::size_t, std::vector<std::size_t>> overlapParentMap;
- std::unique_ptr<const Polygon*> overlaps;
- std::unique_ptr<const Polygon*> gaps;
- std::unique_ptr<const Polygon*> mergableGaps;
+ std::vector<const Polygon*> overlaps;
+ std::vector<const Polygon*> gaps;
+ std::vector<const Polygon*> mergableGaps;
static constexpr double DEFAULT_SNAPPING_FACTOR = 1.0e8;
diff --git a/src/coverage/CleanCoverage.cpp b/src/coverage/CleanCoverage.cpp
index 7bb73f276..01e23958c 100644
--- a/src/coverage/CleanCoverage.cpp
+++ b/src/coverage/CleanCoverage.cpp
@@ -147,12 +147,10 @@ CleanCoverage::findAdjacentAreas(const Geometry* poly)
auto rel = RelateNG::prepare(poly);
const Envelope* queryEnv = poly->getEnvelopeInternal();
- std::vector<CleanArea*> candidateAdjIndex = covIndex->query(queryEnv);
+ std::vector<void*> candidateAdjIndex;
+ covIndex->query(queryEnv, candidateAdjIndex);
- std::vector<void*> queryResult;
- query(queryEnv, queryResult);
-
- for (void* ptr : queryResult) {
+ for (void* ptr : candidateAdjIndex) {
CleanArea* area = static_cast<CleanArea*>(ptr);
if (area != nullptr && area->isAdjacent(*rel)) {
adjacents.push_back(area);
@@ -185,12 +183,11 @@ CleanCoverage::toCoverage(const GeometryFactory* geomFactory)
for (std::size_t i = 0; i < cov.size(); i++) {
std::unique_ptr<Geometry> merged;
if (cov[i] == nullptr) {
- merged = geomFactory->createEmpty(2);
+ cleanCov[i] = geomFactory->createEmpty(2);
}
else {
- merged = cov[i]->union();
+ cleanCov[i] = cov[i]->union();
}
- cleanCov[i] = std::move(merged);
}
return cleanCov;
}
diff --git a/src/coverage/CoverageCleaner.cpp b/src/coverage/CoverageCleaner.cpp
index 088ed8d17..e419470b5 100644
--- a/src/coverage/CoverageCleaner.cpp
+++ b/src/coverage/CoverageCleaner.cpp
@@ -205,31 +205,37 @@ CoverageCleaner::extent(std::vector<const Geometry*>& geoms)
/* private */
void
-CoverageCleaner::mergeOverlaps(std::map<std::size_t, std::vector<std::size_t>>& overlapParentMap)
+CoverageCleaner::mergeOverlaps(
+ std::map<std::size_t, std::vector<std::size_t>>& overlapParentMap)
{
for (const auto& [resIndex, _] : overlapParentMap) {
+ auto ms = mergeStrategy(overlapMergeStrategy);
cleanCov->mergeOverlap(
- resultants[resIndex],
- mergeStrategy(overlapMergeStrategy),
+ resultants[resIndex].get(),
+ *ms,
overlapParentMap[resIndex]);
}
}
+
/* private */
-//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-// yucks! xxxxxxxx
-CleanCoverage.MergeStrategy
+std::unique_ptr<CleanCoverage::MergeStrategy>
CoverageCleaner::mergeStrategy(int mergeStrategyId)
{
switch (mergeStrategyId) {
- case MERGE_LONGEST_BORDER: return new CleanCoverage.MergeStrategy.BorderMergeStrategy();
- case MERGE_MAX_AREA: return new CleanCoverage.MergeStrategy.AreaMergeStrategy(true);
- case MERGE_MIN_AREA: return new CleanCoverage.MergeStrategy.AreaMergeStrategy(false);
- case MERGE_MIN_INDEX: return new CleanCoverage.MergeStrategy.IndexMergeStrategy(false);
+ case MERGE_LONGEST_BORDER:
+ return std::make_unique<CleanCoverage::BorderMergeStrategy>();
+ case MERGE_MAX_AREA:
+ return std::make_unique<CleanCoverage::AreaMergeStrategy>(true);
+ case MERGE_MIN_AREA:
+ return std::make_unique<CleanCoverage::AreaMergeStrategy>(false);
+ case MERGE_MIN_INDEX:
+ return std::make_unique<CleanCoverage::IndexMergeStrategy>(false);
}
throw IllegalArgumentException("Unknown merge strategy: " + mergeStrategyId);
}
+
/* private */
void
CoverageCleaner::computeResultants(double tolerance)
@@ -247,7 +253,7 @@ CoverageCleaner::computeResultants(double tolerance)
//System.out.println("Dissolve: " + sw.getTimeString());
//sw.reset();
- resultants = polygonize(cleanEdges);
+ resultants = polygonize(cleanEdges.get());
//System.out.println("Polygonize: " + sw.getTimeString());
cleanCov = std::make_unique<CleanCoverage>(coverage.size());
@@ -264,7 +270,7 @@ CoverageCleaner::computeResultants(double tolerance)
void
CoverageCleaner::createCoverageIndex()
{
- covIndex = std::made_unique<STRtree>();
+ covIndex = std::make_unique<STRtree>();
for (std::size_t i = 0; i < coverage.size(); i++) {
covIndex->insert(coverage[i]->getEnvelopeInternal(), i);
}
@@ -272,11 +278,10 @@ CoverageCleaner::createCoverageIndex()
/* private */
void
-CoverageCleaner::classifyResult(std::vector<const Polygon*>& resultants)
+CoverageCleaner::classifyResult(std::vector<std::unique_ptr<Polygon>>& rs)
{
- for (std::size_t i = 0; i < resultants.size(); i++) {
- const Polygon* res = resultants[i];
- classifyResultant(i, res);
+ for (std::size_t i = 0; i < rs.size(); i++) {
+ classifyResultant(i, rs[i].get());
}
}
@@ -288,8 +293,8 @@ CoverageCleaner::classifyResultant(std::size_t resultIndex, const Polygon* resPo
std::size_t parentIndex = INDEX_UNKNOWN;
std::vector<std::size_t> overlapIndexes;
-//xxxxxxx
- std::vector<std::size_t> candidateParentIndex = covIndex.query(intPt.getEnvelopeInternal());
+ std::vector<std::size_t> candidateParentIndex;
+ covIndex->query(intPt->getEnvelopeInternal(), candidateParentIndex);
for (std::size_t i : candidateParentIndex) {
const Geometry* parent = coverage[i];
@@ -323,15 +328,17 @@ CoverageCleaner::classifyResultant(std::size_t resultIndex, const Polygon* resPo
}
}
+
/* private static */
bool
CoverageCleaner::covers(const Geometry* poly, const Point* intPt)
{
return SimplePointInAreaLocator::isContained(
- intPt.getCoordinate(),
+ *(intPt->getCoordinate()),
poly);
}
+
/* private */
std::vector<const Polygon*>
CoverageCleaner::findMergableGaps(std::vector<const Polygon*> gaps)
@@ -348,6 +355,7 @@ CoverageCleaner::findMergableGaps(std::vector<const Polygon*> gaps)
// return gaps.stream().filter(gap -> isMergableGap(gap)).collect(Collectors.toList());
}
+
/* private */
bool
CoverageCleaner::isMergableGap(const Polygon* gap)
@@ -358,6 +366,7 @@ CoverageCleaner::isMergableGap(const Polygon* gap)
return MaximumInscribedCircle::isRadiusWithin(gap, gapMaximumWidth / 2.0);
}
+
/* private static */
std::vector<std::unique_ptr<geom::Polygon>>
CoverageCleaner::polygonize(const Geometry* cleanEdges)
@@ -417,7 +426,7 @@ CoverageCleaner::node(std::vector<const Geometry*>& coverage, double snapDistanc
/* private static */
bool
-CoverageCleaner::isPolygonal(Geometry geom)
+CoverageCleaner::isPolygonal(const Geometry* geom)
{
return geom->getGeometryTypeId() == GEOS_POLYGON ||
geom->getGeometryTypeId() == GEOS_MULTIPOLYGON;
@@ -425,7 +434,7 @@ CoverageCleaner::isPolygonal(Geometry geom)
/* private static */
-std::unique_ptr<const Polygon*>
+std::vector<const Polygon*>
CoverageCleaner::toPolygonArray(const Geometry* geom)
{
std::size_t sz = geom->getNumGeometries();
commit 9dd00040515fadcdac3562e3308784bdcf7eaec7
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 7 12:47:46 2025 -0700
wip
diff --git a/include/geos/coverage/CleanCoverage.h b/include/geos/coverage/CleanCoverage.h
index 223b82cfe..053b05cd2 100644
--- a/include/geos/coverage/CleanCoverage.h
+++ b/include/geos/coverage/CleanCoverage.h
@@ -21,35 +21,44 @@
// Forward declarations
namespace geos {
namespace geom {
-class Coordinate;
-class CoordinateSequence;
+class Envelope;
class Geometry;
class GeometryFactory;
class LineString;
class LinearRing;
class Polygon;
}
+namespace operation {
+namespace relateng {
+class RelateNG;
}
+}
+namespace index {
+namespace quadtree {
+class Quadtree;
+}
+}
+}
+
namespace geos { // geos.
namespace coverage { // geos.coverage
class GEOS_DLL CleanCoverage {
- using Coordinate = geos::geom::Coordinate;
- using CoordinateSequence = geos::geom::CoordinateSequence;
+ using Envelope = geos::geom::Envelope;
using Geometry = geos::geom::Geometry;
using GeometryFactory = geos::geom::GeometryFactory;
- using Polygon = geos::geom::Polygon;
using LineString = geos::geom::LineString;
using LinearRing = geos::geom::LinearRing;
-
+ using Polygon = geos::geom::Polygon;
+ using RelateNG = geos::operation::relateng::RelateNG;
+ using Quadtree = geos::index::quadtree::Quadtree;
public:
- CleanCoverage();
-
+ // Classes
class CleanArea {
@@ -178,6 +187,37 @@ private:
//-- used for finding areas to merge gaps
std::unique_ptr<Quadtree> covIndex = nullptr;
+ void mergeGap(const Polygon* gap);
+
+ CleanArea* indMaxBorderLength(const Polygon* poly, std::vector<CleanArea*>& areas);
+
+ std::vector<CleanArea*> findAdjacentAreas(const Geometry* poly);
+
+ void createIndex();
+
+
+public:
+
+ // Methods
+
+ CleanCoverage(std::size_t size);
+
+ void add(std::size_t i, const Polygon* poly);
+
+ void mergeOverlap(const Polygon* overlap,
+ MergeStrategy& mergeStrategy,
+ std::vector<std::size_t>& parentIndexes);
+
+ static std::size_t findMergeTarget(const Polygon* poly,
+ MergeStrategy& strat,
+ std::vector<std::size_t>& parentIndexes,
+ std::vector<std::unique_ptr<CleanArea>>& cov);
+
+ void mergeGaps(std::vector<const Polygon*>& gaps);
+
+ std::vector<std::unique_ptr<Geometry>> toCoverage(const GeometryFactory* geomFactory);
+
+
};
diff --git a/include/geos/coverage/CoverageCleaner.h b/include/geos/coverage/CoverageCleaner.h
new file mode 100644
index 000000000..45a103c1f
--- /dev/null
+++ b/include/geos/coverage/CoverageCleaner.h
@@ -0,0 +1,165 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2025 Martin Davis
+ * Copyright (C) 2025 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#pragma once
+
+// #include <geos/noding/BasicSegmentString.h>
+
+
+// Forward declarations
+namespace geos {
+namespace geom {
+class Coordinate;
+class CoordinateSequence;
+class Geometry;
+class GeometryFactory;
+class LineString;
+class LinearRing;
+class Polygon;
+}
+}
+
+namespace geos { // geos.
+namespace coverage { // geos.coverage
+
+
+/**
+ * Cleans the linework of a set of polygonal geometries to form a valid polygonal coverage.
+ * The input is an array of valid Polygon or MultiPolygon geometries
+ * which may contain topological errors such as overlaps and gaps.
+ * Empty or non-polygonal inputs are removed.
+ * Linework is snapped together to eliminate small discrepancies.
+ * Overlaps are merged with an adjacent polygon, according to a given merge strategy.
+ * Gaps narrower than a given width are filled and merged with an adjacent polygon.
+ * The output is an array of polygonal geometries forming a valid polygonal coverage.
+ *
+ * ** Snapping **
+ *
+ * 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.
+ *
+ * ** Overlap Merging **
+ *
+ * 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 (#MERGE_LONGEST_BORDER.)
+ * * **Maximum/Minimum Area**: merge with the polygon with largest or smallest area (#MERGE_MAX_AREA, #MERGE_MIN_AREA.)
+ * * **Minimum Index**: merge with the polygon with the lowest index in the input array (#MERGE_MIN_INDEX.)
+ *
+ * This allows sorting the input according to some criteria to provide a priority
+ * for merging gaps.
+ *
+ * ** Gap Merging **
+ *
+ * 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.
+ *
+ * Cleaning can be run on a valid coverage to remove gaps.
+ *
+ *
+ * The clean result is an array of polygonal geometries
+ * which match one-to-one with the input array.
+ * A result item may be <tt>null</tt> if:
+ *
+ * * the input item is non-polygonal or empty
+ * * the input item is so small it is snapped to collapse
+ * * the input item is covered by another input item
+ * (which may be a larger or a duplicate (nearly or exactly) geometry)
+ *
+ * The result is a valid coverage according to CoverageValidator#isValid();
+ *
+ * ** Known Issues **
+ *
+ * * Long narrow gaps adjacent to multiple polygons may form spikes when merged with a single polygon.
+ *
+ * ** Future Enhancements **
+ *
+ * * Provide an area-based tolerance for gap merging
+ * * Prevent long narrow gaps from forming spikes by partitioning them before merging.
+ * * Allow merging narrow parts of a gap while leaving wider portions.
+ * * Support a priority value for each input polygon to control overlap and gap merging
+ * (this could also allow blocking polygons from being merge targets)
+ *
+ * @see CoverageValidator
+ * @author Martin Davis
+ *
+ */
+class GEOS_DLL CoverageCleaner {
+
+ using Coordinate = geos::geom::Coordinate;
+ using CoordinateSequence = geos::geom::CoordinateSequence;
+ using Geometry = geos::geom::Geometry;
+ using GeometryFactory = geos::geom::GeometryFactory;
+ using Polygon = geos::geom::Polygon;
+ using LineString = geos::geom::LineString;
+ using LinearRing = geos::geom::LinearRing;
+
+public:
+
+ /** Merge strategy that chooses polygon with longest common border */
+ static constexpr int MERGE_LONGEST_BORDER = 0;
+ /** Merge strategy that chooses polygon with maximum area */
+ static constexpr int MERGE_MAX_AREA = 1;
+ /** Merge strategy that chooses polygon with minimum area */
+ static constexpr int MERGE_MIN_AREA = 2;
+ /** Merge strategy that chooses polygon with smallest input index */
+ static constexpr int MERGE_MIN_INDEX = 3;
+
+private:
+
+ std::vector<const Geometry*> coverage;
+ double snappingDistance; // set to compute default
+ double gapMaximumWidth = 0.0;
+ int overlapMergeStrategy = MERGE_LONGEST_BORDER;
+
+ const GeometryFactory* geomFactory;
+ std::unique_ptr<STRtree> covIndex;
+ std::vector<std::unique_ptr<Polygon>> resultants;
+ std::unique_ptr<CleanCoverage> cleanCov;
+ std::map<std::size_t, std::vector<std::size_t>> overlapParentMap;
+ std::unique_ptr<const Polygon*> overlaps;
+ std::unique_ptr<const Polygon*> gaps;
+ std::unique_ptr<const Polygon*> mergableGaps;
+
+ static constexpr double DEFAULT_SNAPPING_FACTOR = 1.0e8;
+
+public:
+
+
+
+
+
+};
+
+} // namespace geos.coverage
+} // namespace geos
+
+
+
+
+
diff --git a/src/coverage/CleanCoverage.cpp b/src/coverage/CleanCoverage.cpp
index 05d1fd5c9..7bb73f276 100644
--- a/src/coverage/CleanCoverage.cpp
+++ b/src/coverage/CleanCoverage.cpp
@@ -178,7 +178,7 @@ CleanCoverage::createIndex()
/* public */
std::vector<std::unique_ptr<Geometry>>
-CleanCoverage::toCoverage(GeometryFactory* geomFactory)
+CleanCoverage::toCoverage(const GeometryFactory* geomFactory)
{
std::vector<std::unique_ptr<Geometry>> cleanCov;
cleanCov.resize(cov.size());
diff --git a/src/coverage/CoverageCleaner.cpp b/src/coverage/CoverageCleaner.cpp
new file mode 100644
index 000000000..088ed8d17
--- /dev/null
+++ b/src/coverage/CoverageCleaner.cpp
@@ -0,0 +1,448 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2025 Martin Davis
+ * Copyright (C) 2025 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <geos/coverage/CoverageCleaner.h>
+
+#include <geos/algorithm/construct/MaximumInscribedCircle.h>
+#include <geos/algorithm/locate/SimplePointInAreaLocator.h>
+#include <geos/dissolve/LineDissolver.h>
+#include <geos/geom/Envelope.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/MultiPolygon.h>
+#include <geos/geom/Point.h>
+#include <geos/geom/Polygon.h>
+#include <geos/index/strtree/STRtree.h>
+#include <geos/noding/NodedSegmentString.h>
+#include <geos/noding/Noder.h>
+#include <geos/noding/SegmentStringUtil.h>
+#include <geos/noding/snap/SnappingNoder.h>
+#include <geos/operation/polygonize/Polygonizer.h>
+#include <geos/operation/relateng/RelateNG.h>
+#include <geos/operation/relateng/RelatePredicate.h>
+
+using geos::algorithm::construct::MaximumInscribedCircle;
+using geos::algorithm::locate::SimplePointInAreaLocator;
+using geos::dissolve::LineDissolver;
+using geos::geom::Envelope;
+using geos::geom::Geometry;
+using geos::geom::GeometryFactory;
+using geos::geom::MultiPolygon;
+using geos::geom::Point;
+using geos::geom::Polygon;
+using geos::index::strtree::STRtree;
+using geos::noding::NodedSegmentString;
+using geos::noding::Noder;
+using geos::noding::SegmentStringUtil;
+using geos::noding::snap::SnappingNoder;
+using geos::operation::polygonize::Polygonizer;
+using geos::operation::relateng::RelateNG;
+using geos::operation::relateng::RelatePredicate;
+
+
+
+namespace geos { // geos
+namespace coverage { // geos.coverage
+
+
+/**
+ * Create a new cleaner instance for a set of polygonal geometries.
+ *
+ * @param coverage an array of polygonal geometries to clean
+ */
+/* public */
+CoverageCleaner::CoverageCleaner(std::vector<const Geometry*>& p_coverage)
+ : coverage(p_coverage)
+ , geomFactory(p_coverage.empty() ? nullptr : coverage[0]->getFactory())
+ , computeDefaultSnappingDistance(p_coverage)
+{}
+
+/**
+ * Sets the snapping distance tolerance.
+ * The default is to use a small fraction of the input extent diameter.
+ * A distance of zero prevents snapping from being used.
+ *
+ * @param snappingDistance the snapping distance tolerance
+ */
+/* public */
+void
+CoverageCleaner::setSnappingDistance(double p_snappingDistance)
+{
+ //-- use default distance if invalid argument
+ if (p_snappingDistance < 0)
+ return;
+ snappingDistance = p_snappingDistance;
+}
+
+/**
+ * Sets the overlap merge strategy to use.
+ * The default is {@link #MERGE_LONGEST_BORDER}.
+ *
+ * @param mergeStrategy the merge strategy code
+ */
+/* public */
+void
+CoverageCleaner::setOverlapMergeStrategy(int mergeStrategy)
+{
+ if (mergeStrategy < MERGE_LONGEST_BORDER ||
+ mergeStrategy > MERGE_MIN_INDEX)
+ throw IllegalArgumentException("Invalid merge strategy code");
+
+ overlapMergeStrategy = mergeStrategy;
+}
+
+/**
+ * Sets the maximum width of the gaps that will be filled and merged.
+ * 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.
+ *
+ * @param maxWidth the maximum gap width to merge
+ */
+/* public */
+void
+CoverageCleaner::setGapMaximumWidth(double maxWidth)
+{
+ if (maxWidth < 0)
+ return;
+ gapMaximumWidth = maxWidth;
+}
+
+//TODO: support snap-rounding noder for precision reduction
+//TODO: add merge gaps by: area?
+
+/**
+ * Cleans the coverage.
+ *
+ */
+/* public */
+void
+CoverageCleaner::clean()
+{
+ computeResultants(snappingDistance);
+ //System.out.format("Overlaps: %d Gaps: %d\n", overlaps.size(), mergableGaps.size());
+
+ //Stopwatch sw = new Stopwatch();
+ mergeOverlaps(overlapParentMap);
+ //System.out.println("Merge Overlaps: " + sw.getTimeString());
+ //sw.reset();
+ cleanCov->mergeGaps(mergableGaps);
+ //System.out.println("Merge Gaps: " + sw.getTimeString());
+}
+
+/**
+ * Gets the cleaned coverage.
+ *
+ * @return the clean coverage
+ */
+/* public */
+std::vector<std::unique_ptr<Geometry>>
+CoverageCleaner::getResult()
+{
+ return cleanCov->toCoverage(geomFactory);
+}
+
+/**
+ * Gets polygons representing the overlaps in the input,
+ * which have been merged.
+ *
+ * @return a list of overlap polygons
+ */
+/* public */
+std::vector<const Polygon*>
+CoverageCleaner::getOverlaps()
+{
+ return overlaps;
+}
+
+/**
+ * Gets polygons representing the gaps in the input
+ * which have been merged.
+ *
+ * @return a list of gap polygons
+ */
+/* public */
+std::vector<const Polygon*>
+CoverageCleaner::getMergedGaps()
+{
+ return mergableGaps;
+}
+
+//-------------------------------------------------
+
+
+/* private static */
+double
+CoverageCleaner::computeDefaultSnappingDistance(std::vector<const Geometry*>& geoms)
+{
+ double diameter = extent(geoms).getDiameter();
+ return diameter / DEFAULT_SNAPPING_FACTOR;
+}
+
+
+/* private static */
+Envelope
+CoverageCleaner::extent(std::vector<const Geometry*>& geoms)
+{
+ Envelope env;
+ for (const Geometry* geom : geoms) {
+ env.expandToInclude(geom->getEnvelopeInternal());
+ }
+ return env;
+}
+
+
+/* private */
+void
+CoverageCleaner::mergeOverlaps(std::map<std::size_t, std::vector<std::size_t>>& overlapParentMap)
+{
+ for (const auto& [resIndex, _] : overlapParentMap) {
+ cleanCov->mergeOverlap(
+ resultants[resIndex],
+ mergeStrategy(overlapMergeStrategy),
+ overlapParentMap[resIndex]);
+ }
+}
+
+/* private */
+//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+// yucks! xxxxxxxx
+CleanCoverage.MergeStrategy
+CoverageCleaner::mergeStrategy(int mergeStrategyId)
+{
+ switch (mergeStrategyId) {
+ case MERGE_LONGEST_BORDER: return new CleanCoverage.MergeStrategy.BorderMergeStrategy();
+ case MERGE_MAX_AREA: return new CleanCoverage.MergeStrategy.AreaMergeStrategy(true);
+ case MERGE_MIN_AREA: return new CleanCoverage.MergeStrategy.AreaMergeStrategy(false);
+ case MERGE_MIN_INDEX: return new CleanCoverage.MergeStrategy.IndexMergeStrategy(false);
+ }
+ throw IllegalArgumentException("Unknown merge strategy: " + mergeStrategyId);
+}
+
+/* private */
+void
+CoverageCleaner::computeResultants(double tolerance)
+{
+ //System.out.println("Coverage Cleaner ===> polygons: " + coverage.length);
+ //System.out.format("Snapping distance: %f\n", snappingDistance);
+ //Stopwatch sw = new Stopwatch();
+ //sw.start();
+
+ std::unique_ptr<Geometry> nodedEdges = node(coverage, tolerance);
+ //System.out.println("Noding: " + sw.getTimeString());
+
+ //sw.reset();
+ std::unique_ptr<Geometry> cleanEdges = LineDissolver::dissolve(nodedEdges);
+ //System.out.println("Dissolve: " + sw.getTimeString());
+
+ //sw.reset();
+ resultants = polygonize(cleanEdges);
+ //System.out.println("Polygonize: " + sw.getTimeString());
+
+ cleanCov = std::make_unique<CleanCoverage>(coverage.size());
+
+ //sw.reset();
+ createCoverageIndex();
+ classifyResult(resultants);
+ //System.out.println("Classify: " + sw.getTimeString());
+
+ mergableGaps = findMergableGaps(gaps);
+ }
+
+/* private */
+void
+CoverageCleaner::createCoverageIndex()
+{
+ covIndex = std::made_unique<STRtree>();
+ for (std::size_t i = 0; i < coverage.size(); i++) {
+ covIndex->insert(coverage[i]->getEnvelopeInternal(), i);
+ }
+}
+
+/* private */
+void
+CoverageCleaner::classifyResult(std::vector<const Polygon*>& resultants)
+{
+ for (std::size_t i = 0; i < resultants.size(); i++) {
+ const Polygon* res = resultants[i];
+ classifyResultant(i, res);
+ }
+}
+
+/* private */
+void
+CoverageCleaner::classifyResultant(std::size_t resultIndex, const Polygon* resPoly)
+{
+ std::unique_ptr<Point> intPt = resPoly->getInteriorPoint();
+ std::size_t parentIndex = INDEX_UNKNOWN;
+ std::vector<std::size_t> overlapIndexes;
+
+//xxxxxxx
+ std::vector<std::size_t> candidateParentIndex = covIndex.query(intPt.getEnvelopeInternal());
+
+ for (std::size_t i : candidateParentIndex) {
+ const Geometry* parent = coverage[i];
+ if (covers(parent, intPt.get())) {
+ //-- found first parent
+ if (parentIndex == INDEX_UNKNOWN) {
+ parentIndex = i;
+ }
+ else {
+ //-- more than one parent - record them all
+ overlapIndexes.push_back(parentIndex);
+ overlapIndexes.push_back(i);
+ }
+ }
+ }
+ /**
+ * Classify resultant based on # of parents:
+ * 0 - gap
+ * 1 - single polygon face
+ * >1 - overlap
+ */
+ if (parentIndex == INDEX_UNKNOWN) {
+ gaps.push_back(resPoly);
+ }
+ else if (!overlapIndexes.empty()) {
+ overlapParentMap[resultIndex] = overlapIndexes;
+ overlaps.push_back(resPoly);
+ }
+ else {
+ cleanCov->add(parentIndex, resPoly);
+ }
+}
+
+/* private static */
+bool
+CoverageCleaner::covers(const Geometry* poly, const Point* intPt)
+{
+ return SimplePointInAreaLocator::isContained(
+ intPt.getCoordinate(),
+ poly);
+}
+
+/* private */
+std::vector<const Polygon*>
+CoverageCleaner::findMergableGaps(std::vector<const Polygon*> gaps)
+{
+ std::vector<const Polygon*> filtered;
+
+ std::copy_if(gaps.begin(), gaps.end(),
+ std::back_inserter(filtered),
+ [](const Polygon* xgap) { return isMergableGap(gap); }
+ );
+
+ return filtered;
+
+ // return gaps.stream().filter(gap -> isMergableGap(gap)).collect(Collectors.toList());
+}
+
+/* private */
+bool
+CoverageCleaner::isMergableGap(const Polygon* gap)
+{
+ if (gapMaximumWidth <= 0) {
+ return false;
+ }
+ return MaximumInscribedCircle::isRadiusWithin(gap, gapMaximumWidth / 2.0);
+}
+
+/* private static */
+std::vector<std::unique_ptr<geom::Polygon>>
+CoverageCleaner::polygonize(const Geometry* cleanEdges)
+{
+ Polygonizer polygonizer;
+ polygonizer.add(cleanEdges);
+ return polygonizer.getPolygons();
+}
+
+
+/* public static */
+std::unique_ptr<Geometry>
+CoverageCleaner::toGeometry(
+ std::vector<SegmentString*>& segStrings,
+ const GeometryFactory* geomFact)
+{
+ std::vector<std::unique_ptr<LineString>> lines;
+ for (SegmentString* ss : segStrings) {
+ auto cs = ss->getCoordinates()->clone();
+ std::unique_ptr<LineString> line = geomFact->createLineString(std::move(cs));
+ lines.emplace_back(line.release());
+ }
+ if (lines.size() == 1) return lines[0];
+ return geomFact->createMultiLineString(std::move(lines));
+}
+
+
+/* public static */
+std::unique_ptr<Geometry>
+CoverageCleaner::node(std::vector<const Geometry*>& coverage, double snapDistance)
+{
+ std::vector<SegmentString*> segs;
+
+ for (const Geometry* geom : coverage) {
+ //-- skip non-polygonal and empty elements
+ if (! isPolygonal(geom))
+ continue;
+ if (geom->isEmpty())
+ continue;
+ SegmentStringUtil::extractSegmentStrings(geom, segs);
+ }
+
+ SnappingNoder noder(snapDistance);
+ noder.computeNodes(&segs);
+ std::unique_ptr<std::vector<SegmentString*>> nodedSegStrings(noder.getNodedSubstrings());
+ for (SegmentString* ss : *segs) {
+ delete ss;
+ }
+
+ auto result = toGeometry(*nodedSegStrings, geomFactory);
+ for (SegmentString* ss : *nodedSegStrings) {
+ delete ss;
+ }
+
+ return result;
+}
+
+/* private static */
+bool
+CoverageCleaner::isPolygonal(Geometry geom)
+{
+ return geom->getGeometryTypeId() == GEOS_POLYGON ||
+ geom->getGeometryTypeId() == GEOS_MULTIPOLYGON;
+}
+
+
+/* private static */
+std::unique_ptr<const Polygon*>
+CoverageCleaner::toPolygonArray(const Geometry* geom)
+{
+ std::size_t sz = geom->getNumGeometries();
+ std::vector<const Polygon*> geoms;
+ geoms.resize(sz);
+ for (std::size_t i = 0; i < sz; i++) {
+ const Geometry* subgeom = geom->getGeometryN(i);
+ geoms.push_back(static_cast<const Polygon*>(subgeom));
+ }
+ return geoms;
+}
+
+
+
+
+
+} // namespace geos.coverage
+} // namespace geos
+
+
commit 6fd09a1ed585800965ca1b314b0840ef0a0466bf
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Tue May 6 13:53:22 2025 -0700
wip
diff --git a/include/geos/coverage/CleanCoverage.h b/include/geos/coverage/CleanCoverage.h
index b7cc1f32d..223b82cfe 100644
--- a/include/geos/coverage/CleanCoverage.h
+++ b/include/geos/coverage/CleanCoverage.h
@@ -15,9 +15,8 @@
#pragma once
-#include <geos/noding/BasicSegmentString.h>
+// #include <geos/noding/BasicSegmentString.h>
-#include <deque>
// Forward declarations
namespace geos {
@@ -45,12 +44,6 @@ class GEOS_DLL CleanCoverage {
using LineString = geos::geom::LineString;
using LinearRing = geos::geom::LinearRing;
-private:
-
- // Members
-
-
-
public:
@@ -59,28 +52,132 @@ public:
class CleanArea {
- public:
+
private:
- }
+ // Members
+ std::vector<const Polygon*> polys;
+ Envelope env;
+
+ public:
+
+ // Methods
+ void add(const Polygon* poly);
+ const Envelope* getEnvelope();
+ double getBorderLength(const Polygon* adjPoly);
+ double getArea();
+ bool isAdjacent(RelateNG& rel);
+ std::unique_ptr<Geometry> union();
+
+ }; // CleanArea
+
class MergeStrategy {
+
public:
+
+ virtual std::size_t getTarget() const = 0;
+
+ virtual void checkMergeTarget(
+ std::size_t areaIndex,
+ CleanArea* cleanArea,
+ const Polygon* poly) = 0;
+
+ }; // MergeStrategy
+
+
+ class BorderMergeStrategy : public MergeStrategy {
+
private:
- }
+ std::size_t m_targetIndex = INDEX_UNKNOWN;
+ double m_targetBorderLen;
+
+ public:
+
+ std::size_t getTarget() const override {
+ return m_targetIndex;
+ }
+
+ void checkMergeTarget(std::size_t areaIndex, CleanArea* area, const Polygon* poly) override {
+ double borderLen = area == nullptr ? 0.0 : area->getBorderLength(poly);
+ if (m_targetIndex == INDEX_UNKNOWN || borderLen > m_targetBorderLen) {
+ m_targetIndex = areaIndex;
+ m_targetBorderLen = borderLen;
+ }
+ }
+
+ }; // BorderStrategy
+
class AreaMergeStrategy : public MergeStrategy {
- public:
+
private:
- }
+ std::size_t m_targetIndex = INDEX_UNKNOWN;
+ double m_targetArea;
+ bool m_isMax;
+
+ public:
+
+ AreaMergeStrategy(bool isMax) : m_isMax(isMax) {};
+
+ std::size_t getTarget() const override {
+ return m_targetIndex;
+ }
+
+ void checkMergeTarget(std::size_t areaIndex, CleanArea* area, const Polygon* poly) override {
+ double areaVal = area == nullptr ? 0.0 : area->getArea();
+ bool isBetter = m_isMax
+ ? areaVal > m_targetArea
+ : areaVal < m_targetArea;
+ if (m_targetIndex == INDEX_UNKNOWN || isBetter) {
+ m_targetIndex = areaIndex;
+ m_targetArea = areaVal;
+ }
+ }
+
+ }; // AreaMergeStrategy
+
class IndexMergeStrategy : public MergeStrategy {
- public:
+
private:
- }
+ std::size_t m_targetIndex = INDEX_UNKNOWN;
+ bool m_isMax;
+
+ public:
+
+ IndexMergeStrategy(bool isMax) : m_isMax(isMax) {};
+
+ std::size_t getTarget() const override {
+ return m_targetIndex;
+ }
+
+ void checkMergeTarget(std::size_t areaIndex, CleanArea* area, const Polygon* poly) override {
+ bool isBetter = m_isMax
+ ? areaIndex > m_targetIndex
+ : areaIndex < m_targetIndex;
+ if (m_targetIndex < 0 || isBetter) {
+ m_targetIndex = areaIndex;
+ }
+ }
+ }; // MergeStrategy
+
+
+private:
+
+ // Members
+
+ /**
+ * The areas in the clean coverage.
+ * Entries may be null, if no resultant corresponded to the input area.
+ */
+ std::vector<str::unique_ptr<CleanArea>> cov;
+ //-- used for finding areas to merge gaps
+ std::unique_ptr<Quadtree> covIndex = nullptr;
+
};
diff --git a/src/coverage/CleanCoverage.cpp b/src/coverage/CleanCoverage.cpp
index 56f35d45e..05d1fd5c9 100644
--- a/src/coverage/CleanCoverage.cpp
+++ b/src/coverage/CleanCoverage.cpp
@@ -41,8 +41,241 @@ namespace geos { // geos
namespace coverage { // geos.coverage
+/* public */
+CleanCoverage::CleanCoverage(std::size_t size)
+{
+ cov.resize(size);
+}
+/* public */
+void
+CleanCoverage::add(std::size_t i, const Polygon* poly)
+{
+ if (cov[i] == nullptr) {
+ cov[i] = std::make_unique<CleanArea>();
+ }
+ cov[i]->add(poly);
+}
+
+
+/* public */
+void
+CleanCoverage::mergeOverlap(const Polygon* overlap,
+ MergeStrategy& mergeStrategy,
+ std::vector<std::size_t>& parentIndexes)
+{
+ std::size_t mergeTarget = findMergeTarget(overlap, mergeStrategy, parentIndexes, cov);
+ add(mergeTarget, overlap);
+}
+
+
+/* public static */
+std::size_t
+CleanCoverage::findMergeTarget(const Polygon* poly,
+ MergeStrategy& strat,
+ std::vector<std::size_t>& parentIndexes,
+ std::vector<std::unique_ptr<CleanArea>>& cov)
+{
+ //-- sort parent indexes ascending, so that overlaps merge to first parent by default
+ std::vector<size_t> indexesAsc;
+ indexesAsc.copy(parentIndexes.begin(), parentIndexes.end(), back_inserter(indexesAsc));
+ std::sort(indexesAsc.begin(), indexesAsc.end());
+
+ for (std::size_t index : indexesAsc) {
+ strat.checkMergeTarget(index, cov[index].get(), poly);
+ }
+ return strat.getTarget();
+}
+
+
+/* public */
+void
+CleanCoverage::mergeGaps(std::vector<const Polygon*>& gaps)
+{
+ createIndex();
+ for (const Polygon* gap : gaps) {
+ mergeGap(gap);
+ }
+}
+
+
+/* private */
+void
+CleanCoverage::mergeGap(const Polygon* gap)
+{
+ std::vector<CleanArea*> adjacents = findAdjacentAreas(gap);
+
+ /**
+ * No adjacent means this is likely an artifact
+ * of an invalid input polygon.
+ * Discard polygon.
+ */
+ if (adjacents.empty())
+ return;
+
+ CleanArea* mergeTarget = findMaxBorderLength(gap, adjacents);
+ covIndex->remove(mergeTarget->getEnvelope(), mergeTarget);
+ mergeTarget->add(gap);
+ covIndex->insert(mergeTarget->getEnvelope(), mergeTarget);
+}
+
+
+/* private */
+CleanArea*
+CleanCoverage::findMaxBorderLength(const Polygon* poly,
+ std::vector<CleanArea*>& areas)
+{
+ double maxLen = 0;
+ CleanArea* maxLenArea = nullptr;
+ for (CleanArea* a : areas) {
+ double len = a->getBorderLength(poly);
+ if (maxLenArea == nullptr || len > maxLen) {
+ maxLen = len;
+ maxLenArea = a;
+ }
+ }
+ return maxLenArea;
+}
+
+
+/* private */
+std::vector<CleanArea*>
+CleanCoverage::findAdjacentAreas(const Geometry* poly)
+{
+ std::vector<CleanArea*> adjacents;
+ auto rel = RelateNG::prepare(poly);
+ const Envelope* queryEnv = poly->getEnvelopeInternal();
+
+ std::vector<CleanArea*> candidateAdjIndex = covIndex->query(queryEnv);
+
+ std::vector<void*> queryResult;
+ query(queryEnv, queryResult);
+
+ for (void* ptr : queryResult) {
+ CleanArea* area = static_cast<CleanArea*>(ptr);
+ if (area != nullptr && area->isAdjacent(*rel)) {
+ adjacents.push_back(area);
+ }
+ }
+ return adjacents;
+}
+
+
+/* private */
+void
+CleanCoverage::createIndex()
+{
+ covIndex = std::make_unique<Quadtree>();
+ for (std::size_t i = 0; i < cov.size(); i++) {
+ //-- null areas are never merged to
+ if (cov[i] != nullptr) {
+ covIndex->insert(cov[i]->getEnvelope(), static_cast<void*>(cov[i].get()));
+ }
+ }
+}
+
+
+/* public */
+std::vector<std::unique_ptr<Geometry>>
+CleanCoverage::toCoverage(GeometryFactory* geomFactory)
+{
+ std::vector<std::unique_ptr<Geometry>> cleanCov;
+ cleanCov.resize(cov.size());
+ for (std::size_t i = 0; i < cov.size(); i++) {
+ std::unique_ptr<Geometry> merged;
+ if (cov[i] == nullptr) {
+ merged = geomFactory->createEmpty(2);
+ }
+ else {
+ merged = cov[i]->union();
+ }
+ cleanCov[i] = std::move(merged);
+ }
+ return cleanCov;
+}
+
+
+///// CleanCoverage::CleanArea ////////////////////////////////////////
+
+
+/* public */
+void
+CleanCoverage::CleanArea::add(const Polygon* poly)
+{
+ polys.push_back(poly);
+}
+
+
+/* public */
+const Envelope*
+CleanCoverage::CleanArea::getEnvelope()
+{
+ env.init();
+ for (const Polygon* poly : polys) {
+ env.expandToInclude(poly->getEnvelopeInternal());
+ }
+ return &env;
+}
+
+
+/* public */
+double
+CleanCoverage::CleanArea::getBorderLength(const Polygon* adjPoly)
+{
+ //TODO: find optimal way of computing border len given a coverage
+ double len = 0.0;
+ for (const Polygon* poly : polys) {
+ //TODO: find longest connected border len
+ auto border = OverlayNGRobust::overlay(
+ static_cast<const Geometry*>(poly),
+ static_cast<const Geometry*>(adjPoly),
+ OverlayNG::INTERSECTION);
+ double borderLen = border->getLength();
+ len += borderLen;
+ }
+ return len;
+}
+
+
+/* public */
+double
+CleanCoverage::CleanArea::getArea()
+{
+ //TODO: cache area?
+ double area = 0.0;
+ for (const Polygon* poly : polys) {
+ area += poly->getArea();
+ }
+ return area;
+}
+
+
+/* public */
+bool
+CleanCoverage::CleanArea::isAdjacent(RelateNG& rel)
+{
+ for (const Polygon* poly : polys) {
+ //TODO: is there a faster way to check adjacency in coverage?
+ auto geom = static_cast<const Geometry*>(poly);
+ bool isAdjacent = rel.evaluate(geom, IntersectionMatrixPattern::ADJACENT);
+ if (isAdjacent)
+ return true;
+ }
+ return false;
+}
+
+
+/* public */
+std::unique_ptr<Geometry>
+CleanCoverage::CleanArea::union()
+{
+ std::vector<const Geometry*> geoms;
+ for (const Polygon* poly : polys) {
+ geoms.push_back(static_cast<const Geometry*>(poly));
+ }
+ return CoverageUnion::union(geoms);
+}
commit 45a83828b4296a47e2979308d6e91c26bbc5f8ba
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Mon May 5 08:11:20 2025 -0700
wip
diff --git a/include/geos/coverage/CleanCoverage.h b/include/geos/coverage/CleanCoverage.h
new file mode 100644
index 000000000..b7cc1f32d
--- /dev/null
+++ b/include/geos/coverage/CleanCoverage.h
@@ -0,0 +1,93 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2025 Martin Davis
+ * Copyright (C) 2025 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#pragma once
+
+#include <geos/noding/BasicSegmentString.h>
+
+#include <deque>
+
+// Forward declarations
+namespace geos {
+namespace geom {
+class Coordinate;
+class CoordinateSequence;
+class Geometry;
+class GeometryFactory;
+class LineString;
+class LinearRing;
+class Polygon;
+}
+}
+
+namespace geos { // geos.
+namespace coverage { // geos.coverage
+
+class GEOS_DLL CleanCoverage {
+
+ using Coordinate = geos::geom::Coordinate;
+ using CoordinateSequence = geos::geom::CoordinateSequence;
+ using Geometry = geos::geom::Geometry;
+ using GeometryFactory = geos::geom::GeometryFactory;
+ using Polygon = geos::geom::Polygon;
+ using LineString = geos::geom::LineString;
+ using LinearRing = geos::geom::LinearRing;
+
+private:
+
+ // Members
+
+
+
+
+
+public:
+
+ CleanCoverage();
+
+
+ class CleanArea {
+ public:
+ private:
+
+ }
+
+ class MergeStrategy {
+ public:
+ private:
+
+ }
+
+ class AreaMergeStrategy : public MergeStrategy {
+ public:
+ private:
+
+ }
+
+ class IndexMergeStrategy : public MergeStrategy {
+ public:
+ private:
+
+ }
+
+};
+
+} // namespace geos.coverage
+} // namespace geos
+
+
+
+
+
diff --git a/src/coverage/CleanCoverage.cpp b/src/coverage/CleanCoverage.cpp
new file mode 100644
index 000000000..56f35d45e
--- /dev/null
+++ b/src/coverage/CleanCoverage.cpp
@@ -0,0 +1,52 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (c) 2025 Martin Davis
+ * Copyright (C) 2025 Paul Ramsey <pramsey at cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <geos/coverage/CleanCoverage.h>
+
+#include <geos/geom/Envelope.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/Polygon.h>
+#include <geos/index/quadtree/Quadtree.h>
+#include <geos/operation/overlayng/OverlayNG.h>
+#include <geos/operation/overlayng/OverlayNGRobust.h>
+#include <geos/operation/relateng/IntersectionMatrixPattern.h>
+#include <geos/operation/relateng/RelateNG.h>
+
+
+using geos::geom::Envelope;
+using geos::geom::Geometry;
+using geos::geom::GeometryFactory;
+using geos::geom::Polygon;
+using geos::index::quadtree::Quadtree;
+using geos::operation::overlayng::OverlayNG;
+using geos::operation::overlayng::OverlayNGRobust;
+using geos::operation::relateng::IntersectionMatrixPattern;
+using geos::operation::relateng::RelateNG;
+
+
+namespace geos { // geos
+namespace coverage { // geos.coverage
+
+
+
+
+
+
+
+} // namespace geos.coverage
+} // namespace geos
+
+
-----------------------------------------------------------------------
Summary of changes:
.github/workflows/ci.yml | 11 +++++------
tests/unit/coverage/CoverageCleanerTest.cpp | 18 ++++++++++++++++++
2 files changed, 23 insertions(+), 6 deletions(-)
hooks/post-receive
--
GEOS
More information about the geos-commits
mailing list