[geos-commits] [SCM] GEOS branch main updated. 93472ce0f6a02dd5dfcbf8e41eca4e75e714209f
git at osgeo.org
git at osgeo.org
Mon Nov 10 09:52:51 PST 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 93472ce0f6a02dd5dfcbf8e41eca4e75e714209f (commit)
from b5960c3a08c3ccaffee019529f847dc02371a790 (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 93472ce0f6a02dd5dfcbf8e41eca4e75e714209f
Author: Daniel Baston <dbaston at gmail.com>
Date: Mon Nov 10 12:52:28 2025 -0500
Buffer: Ensure buffer of single-part returns single-part (#1323)
* Buffer: Ensure buffer of single-part returns single-part
* Buffer: Move location of fix for #1321
* BufferOp: Add a test case
diff --git a/src/operation/buffer/BufferBuilder.cpp b/src/operation/buffer/BufferBuilder.cpp
index a5d423e9e..5cc5685d2 100644
--- a/src/operation/buffer/BufferBuilder.cpp
+++ b/src/operation/buffer/BufferBuilder.cpp
@@ -363,6 +363,25 @@ BufferBuilder::bufferLineSingleSided(const Geometry* g, double distance,
}
}
+static void
+keepLargestArea(std::vector<std::unique_ptr<Geometry>> & polyList) {
+ size_t maxAreaGeom = 0;
+ double maxArea = polyList[0]->getArea();
+
+ for (size_t i = 1; i < polyList.size(); i++) {
+ if (polyList[i]->getArea() > maxArea) {
+ maxArea = polyList[i]->getArea();
+ maxAreaGeom = i;
+ }
+ }
+
+ if (maxAreaGeom > 0) {
+ polyList[0] = std::move(polyList[maxAreaGeom]);
+ }
+
+ polyList.resize(1);
+}
+
/*public*/
std::unique_ptr<Geometry>
BufferBuilder::buffer(const Geometry* g, double distance)
@@ -485,9 +504,18 @@ BufferBuilder::buffer(const Geometry* g, double distance)
return createEmptyResultGeometry();
}
+ // Heuristic to remove artifacts caused by topology robustness problems
+ // or buffer curve generation anomalies.
+ // Uses fact that for distance > 0 single-element inputs must create single element buffers.
+ // This does not hold if distance <= 0;
+ // distance = 0 can create multipolygon results due to topology collapse,
+ // and distance < 0 may erode polygons so they are disconnected.
+ if (distance > 0 && g->getNumGeometries() == 1 && resultPolyList.size() > 1) {
+ keepLargestArea(resultPolyList);
+ }
+
// resultPolyList ownership transferred here
resultGeom = geomFact->buildGeometry(std::move(resultPolyList));
-
}
catch(const util::GEOSException& /* exc */) {
diff --git a/src/operation/buffer/BufferOp.cpp b/src/operation/buffer/BufferOp.cpp
index 379ca1d4d..0bdbbfdb7 100644
--- a/src/operation/buffer/BufferOp.cpp
+++ b/src/operation/buffer/BufferOp.cpp
@@ -116,6 +116,7 @@ BufferOp::getResultGeometry(double nDistance)
}
distance = nDistance;
computeGeometry();
+
return std::unique_ptr<Geometry>(resultGeometry.release());
}
diff --git a/tests/unit/operation/buffer/BufferOpTest.cpp b/tests/unit/operation/buffer/BufferOpTest.cpp
index b51914ca8..7ac585ccc 100644
--- a/tests/unit/operation/buffer/BufferOpTest.cpp
+++ b/tests/unit/operation/buffer/BufferOpTest.cpp
@@ -598,7 +598,7 @@ void object::test<26>
}
// testElementErodedEx
-// Checks that a skinny element polygon is eroded with no internal predicision reduction due to topo exes
+// Checks that a skinny element polygon is eroded with no internal precision reduction due to topo exes
// see https://github.com/libgeos/geos/issues/1182
template<>
template<>
@@ -623,4 +623,43 @@ void object::test<28>
"MULTIPOLYGON (((24 95.239, 24 96, 24 99, 24.816 99, 24 95.239)), ((3 90, 3 93, 3 96, 3 99, 21 99, 21 96, 21 93, 21 90, 3 90)))");
}
+template<>
+template<>
+void object::test<29>
+()
+{
+ set_test_name("GH-1321");
+ // Ensure positive buffer of LineString produces a single-part Polygon
+ // See https://github.com/libgeos/geos/issues/1321
+
+ std::string wkt = "LINESTRING(640770.332537465 216785.425146015,640770.356311913 216785.560172686,640770.475488952 216786.226162019,640770.647396495 216787.800152365,640770.658978918 216789.268376902,640770.447752096 216792.399898366,640769.98018796 216795.624688816,640769.110287021 216798.527305023,640767.648768098 216801.891389348,640766.02448565 216804.805892778,640764.74005588 216807.134569434,640763.337363474 216809.560477487,640761.650901703 216812.246990061,640759.955600712 216815.327305019,640758.482499366 216817.868732504,640757.383693167 216819.548488661,640756.238862077 216821.226720821,640754.01503683 216824.434442434,640751.366929134 216828.435255234,640749.204368809 216832.450088865,640747.25516891 216836.227787618,640746.014020829 216838.801219168,640745.393751588 216840.360274284,640744.315976633 216842.854457673,640743.400050801 216844.65704848,640742.582575565 216846.277673322,640741.52491745 216848.478638524,640740.503835408 216850.705816576,640739.787553975 2
16852.270967708,640739.053289307 216853.851968469,640738.173025147 216855.546355058,640737.55488951 216856.568961104,640736.817272035 216857.822910812,640735.910795021 216859.35697228,640734.77510795 216861.236982439,640733.832969266 216862.938074642,640732.814325629 216865.039674844,640731.225095251 216869.012141189,640729.979984761 216871.879095724,640729.445974092 216873.02148841,640729.002794006 216873.679857725,640728.952197105 216873.745389857,640728.676962154 216874.089814544)";
+
+ auto bufGeom = wktreader.read(wkt)->buffer(100);
+
+ ensure_equals(bufGeom->getGeometryTypeId(), geos::geom::GEOS_POLYGON);
+ ensure(bufGeom->getArea() > 51000);
+}
+
+template<>
+template<>
+void object::test<30>
+()
+{
+ set_test_name("sf-2552");
+ // Ensure positive buffer of LineString produces a single-part Polygon
+ // See https://github.com/r-spatial/sf/issues/2552
+
+ std::string wkt = "LINESTRING (245184.6 6045650, 245193.3 6045649, 245201.7 6045651, 245204.3 6045653)";
+
+ auto geom = wktreader.read(wkt);
+
+ BufferOp bop(geom.get());
+ bop.setEndCapStyle(BufferParameters::CAP_SQUARE);
+
+ auto bufGeom = bop.getResultGeometry(50);
+ ensure_equals(bufGeom->getGeometryTypeId(), geos::geom::GEOS_POLYGON);
+
+ ensure(bufGeom->getArea() > 12000);
+}
+
} // namespace tut
diff --git a/tests/unit/simplify/DouglasPeuckerSimplifierTest.cpp b/tests/unit/simplify/DouglasPeuckerSimplifierTest.cpp
index 73a8a3dd1..83f3e9e45 100644
--- a/tests/unit/simplify/DouglasPeuckerSimplifierTest.cpp
+++ b/tests/unit/simplify/DouglasPeuckerSimplifierTest.cpp
@@ -45,7 +45,7 @@ struct test_dpsimp_data {
ensure("Simplified geometry is invalid!", simplified->isValid());
GeomPtr exp(wktreader.read(wkt_expected));
- ensure_equals_geometry(exp.get(), simplified.get());
+ ensure_equals_geometry(simplified.get(), exp.get());
}
void
-----------------------------------------------------------------------
Summary of changes:
src/operation/buffer/BufferBuilder.cpp | 30 +++++++++++++++-
src/operation/buffer/BufferOp.cpp | 1 +
tests/unit/operation/buffer/BufferOpTest.cpp | 41 +++++++++++++++++++++-
.../unit/simplify/DouglasPeuckerSimplifierTest.cpp | 2 +-
4 files changed, 71 insertions(+), 3 deletions(-)
hooks/post-receive
--
GEOS
More information about the geos-commits
mailing list