[geos-commits] [SCM] GEOS branch 3.11 updated. 0bb7c64df714be5772671b27726de45f2553d40f

git at osgeo.org git at osgeo.org
Tue Feb 11 16:39:41 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, 3.11 has been updated
       via  0bb7c64df714be5772671b27726de45f2553d40f (commit)
      from  610e2a764ae6c713a2d39e69556fb809b8c2a8ef (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 0bb7c64df714be5772671b27726de45f2553d40f
Author: Martin Davis <mtnclimb at gmail.com>
Date:   Tue Feb 11 15:19:28 2025 -0800

    Fix CCW ring buffer generation

diff --git a/include/geos/operation/buffer/BufferCurveSetBuilder.h b/include/geos/operation/buffer/BufferCurveSetBuilder.h
index 217359e00..12dfe06a7 100644
--- a/include/geos/operation/buffer/BufferCurveSetBuilder.h
+++ b/include/geos/operation/buffer/BufferCurveSetBuilder.h
@@ -120,7 +120,7 @@ private:
 
     void addPolygon(const geom::Polygon* p);
 
-    void addRingBothSides(const geom::CoordinateSequence* coord, double p_distance);
+    void addLinearRingSides(const geom::CoordinateSequence* coord, double p_distance);
 
     /**
      * Add an offset curve for a polygon ring.
@@ -140,10 +140,14 @@ private:
      * @param cwRightLoc the location on the R side of the ring
      *                   (if it is CW)
      */
-    void addRingSide(const geom::CoordinateSequence* coord,
+    void addPolygonRingSide(const geom::CoordinateSequence* coord,
                      double offsetDistance, int side, geom::Location cwLeftLoc,
                      geom::Location cwRightLoc);
 
+    void addRingSide(const geom::CoordinateSequence* coord,
+                     double offsetDistance, int side, geom::Location leftLoc,
+                     geom::Location rightLoc);
+
     /**
      * Tests whether the offset curve for a ring is fully inverted.
      * An inverted ("inside-out") curve occurs in some specific situations
diff --git a/src/operation/buffer/BufferCurveSetBuilder.cpp b/src/operation/buffer/BufferCurveSetBuilder.cpp
index c438dfcfa..787f1b5c6 100644
--- a/src/operation/buffer/BufferCurveSetBuilder.cpp
+++ b/src/operation/buffer/BufferCurveSetBuilder.cpp
@@ -208,16 +208,44 @@ BufferCurveSetBuilder::addLineString(const LineString* line)
      * Singled-sided buffers currently treat rings as if they are lines.
      */
     if (coord->isRing() && ! curveBuilder.getBufferParameters().isSingleSided()) {
-        addRingBothSides(coord.get(), distance);
+        addLinearRingSides(coord.get(), distance);
     }
     else {
         std::vector<CoordinateSequence*> lineList;
         curveBuilder.getLineCurve(coord.get(), distance, lineList);
         addCurves(lineList, Location::EXTERIOR, Location::INTERIOR);
     }
-
 }
 
+/* private */
+void
+BufferCurveSetBuilder::addLinearRingSides(const CoordinateSequence* coord, double p_distance)
+{
+    /*
+     * (f "hole" side will be eroded completely, avoid generating it.
+     * This prevents hole artifacts (e.g. https://github.com/libgeos/geos/issues/1223)
+     */
+    //-- distance is assumed positive, due to previous checks
+    Envelope env;
+    coord->expandEnvelope(env);
+    bool isHoleComputed = ! isRingFullyEroded(coord, &env, true, distance);
+
+    bool isCCW = isRingCCW(coord);
+
+    bool isShellLeft = ! isCCW;
+    if (isShellLeft || isHoleComputed) {
+        addRingSide(coord, p_distance,
+                    Position::LEFT,
+                    Location::EXTERIOR, Location::INTERIOR);
+    }
+
+    bool isShellRight = isCCW;
+    if (isShellRight || isHoleComputed) {
+        addRingSide(coord, p_distance,
+                    Position::RIGHT,
+                    Location::INTERIOR, Location::EXTERIOR);
+    }
+}
 
 /*private*/
 void
@@ -251,7 +279,7 @@ BufferCurveSetBuilder::addPolygon(const Polygon* p)
         return;
     }
 
-    addRingSide(
+    addPolygonRingSide(
         shellCoord.get(),
         offsetDistance,
         offsetSide,
@@ -273,7 +301,7 @@ BufferCurveSetBuilder::addPolygon(const Polygon* p)
         // Holes are topologically labelled opposite to the shell,
         // since the interior of the polygon lies on their opposite
         // side (on the left, if the hole is oriented CCW)
-        addRingSide(
+        addPolygonRingSide(
             holeCoord.get(),
             offsetDistance,
             Position::opposite(offsetSide),
@@ -284,38 +312,7 @@ BufferCurveSetBuilder::addPolygon(const Polygon* p)
 
 /* private */
 void
-BufferCurveSetBuilder::addRingBothSides(const CoordinateSequence* coord, double p_distance)
-{
-    /*
-     * (f "hole" side will be eroded completely, avoid generating it.
-     * This prevents hole artifacts (e.g. https://github.com/libgeos/geos/issues/1223)
-     */
-    //-- distance is assumed positive, due to previous checks
-    Envelope env;
-    coord->expandEnvelope(env);
-    bool isHoleComputed = ! isRingFullyEroded(coord, &env, true, distance);
-
-    bool isCCW = isRingCCW(coord);
-
-    bool isShellLeft = ! isCCW;
-    if (isShellLeft || isHoleComputed) {
-        addRingSide(coord, p_distance,
-                    Position::LEFT,
-                    Location::EXTERIOR, Location::INTERIOR);
-    }
-
-    bool isShellRight = isCCW;
-    if (isShellRight || isHoleComputed) {
-        addRingSide(coord, p_distance,
-                    Position::RIGHT,
-                    Location::INTERIOR, Location::EXTERIOR);
-    }
-}
-
-
-/* private */
-void
-BufferCurveSetBuilder::addRingSide(const CoordinateSequence* coord,
+BufferCurveSetBuilder::addPolygonRingSide(const CoordinateSequence* coord,
                                       double offsetDistance, int side, geom::Location cwLeftLoc, geom::Location cwRightLoc)
 {
 
@@ -347,6 +344,14 @@ BufferCurveSetBuilder::addRingSide(const CoordinateSequence* coord,
 #endif
         side = Position::opposite(side);
     }
+    addRingSide(coord, offsetDistance, side, leftLoc, rightLoc);
+}
+
+/* private */
+void
+BufferCurveSetBuilder::addRingSide(const CoordinateSequence* coord,
+                                      double offsetDistance, int side, geom::Location leftLoc, geom::Location rightLoc)
+{
     std::vector<CoordinateSequence*> lineList;
     curveBuilder.getRingCurve(coord, side, offsetDistance, lineList);
     // ASSERT: lineList contains exactly 1 curve (this is teh JTS semantics)
diff --git a/tests/unit/operation/buffer/BufferOpTest.cpp b/tests/unit/operation/buffer/BufferOpTest.cpp
index 387b91eb6..dff461b16 100644
--- a/tests/unit/operation/buffer/BufferOpTest.cpp
+++ b/tests/unit/operation/buffer/BufferOpTest.cpp
@@ -647,4 +647,17 @@ void object::test<25>
 
 }
 
+// testRingCCW
+// See https://github.com/libgeos/geos/issues/1236
+template<>
+template<>
+void object::test<26>
+()
+{
+    std::string wkt("LINEARRING (-0.25 0.25, -0.25 0.75, -0.75 0.75, -0.75 0.25, -0.25 0.25)");
+
+    checkBuffer(wkt, 1, 0.1, 
+        "POLYGON ((0.73 0.05, 0.67 -0.13, 0.58 -0.31, 0.46 -0.46, 0.31 -0.58, 0.13 -0.67, -0.05 -0.73, -0.25 -0.75, -0.75 -0.75, -0.95 -0.73, -1.13 -0.67, -1.31 -0.58, -1.46 -0.46, -1.58 -0.31, -1.67 -0.13, -1.73 0.05, -1.75 0.25, -1.75 0.75, -1.73 0.95, -1.67 1.13, -1.58 1.31, -1.46 1.46, -1.31 1.58, -1.13 1.67, -0.95 1.73, -0.75 1.75, -0.25 1.75, -0.05 1.73, 0.13 1.67, 0.31 1.58, 0.46 1.46, 0.58 1.31, 0.67 1.13, 0.73 0.95, 0.75 0.75, 0.75 0.25, 0.73 0.05))");
+}
+
 } // namespace tut
diff --git a/tests/xmltester/tests/general/TestBuffer.xml b/tests/xmltester/tests/general/TestBuffer.xml
index 865a7ac07..acc8efc2f 100644
--- a/tests/xmltester/tests/general/TestBuffer.xml
+++ b/tests/xmltester/tests/general/TestBuffer.xml
@@ -33,6 +33,57 @@
   </op></test>
 </case>
 
+<case>
+  <desc>
+    Closed Line
+  </desc>
+  <a>
+    LINESTRING (1 9, 9 9, 9 1, 1 1, 1 9)
+  </a>
+<test><op name='buffer' arg1='A' arg2='-1.0'>   POLYGON EMPTY  </op></test>
+<test><op name='buffer' arg1='A' arg2='0.0'>    POLYGON EMPTY  </op></test>
+<test><op name='buffer' arg1='A' arg2='1'>   
+POLYGON ((1 10, 9 10, 9.195090322016128 9.98078528040323, 9.38268343236509 9.923879532511286, 9.555570233019603 9.831469612302545, 9.707106781186548 9.707106781186548, 9.831469612302545 9.555570233019601, 9.923879532511286 9.38268343236509, 9.98078528040323 9.195090322016128, 10 9, 10 1, 9.98078528040323 0.8049096779838718, 9.923879532511286 0.6173165676349102, 9.831469612302545 0.4444297669803978, 9.707106781186548 0.2928932188134525, 9.555570233019603 0.1685303876974548, 9.38268343236509 0.0761204674887133, 9.195090322016128 0.0192147195967696, 9 0, 1 0, 0.8049096779838718 0.0192147195967696, 0.6173165676349103 0.0761204674887133, 0.444429766980398 0.1685303876974547, 0.2928932188134525 0.2928932188134524, 0.1685303876974547 0.4444297669803978, 0.0761204674887133 0.6173165676349102, 0.0192147195967696 0.8049096779838714, 0 1, 0 9, 0.0192147195967696 9.195090322016128, 0.0761204674887133 9.38268343236509, 0.1685303876974547 9.555570233019601, 0.2928932188134525 9.707106781186548, 0
 .444429766980398 9.831469612302545, 0.6173165676349103 9.923879532511286, 0.8049096779838718 9.98078528040323, 1 10), (2 8, 2 2, 8 2, 8 8, 2 8))
+  </op></test>
+<test><op name='buffer' arg1='A' arg2='10.0'>   
+POLYGON ((1 19, 9 19, 10.950903220161283 18.807852804032304, 12.826834323650898 18.238795325112868, 14.555702330196024 17.314696123025453, 16.071067811865476 16.071067811865476, 17.314696123025453 14.555702330196022, 18.238795325112868 12.826834323650898, 18.807852804032304 10.950903220161283, 19 9, 19 1, 18.807852804032304 -0.9509032201612824, 18.238795325112868 -2.826834323650898, 17.314696123025453 -4.555702330196022, 16.071067811865476 -6.071067811865475, 14.555702330196024 -7.314696123025453, 12.826834323650898 -8.238795325112868, 10.950903220161283 -8.807852804032304, 9 -9, 1 -9, -0.9509032201612819 -8.807852804032304, -2.826834323650897 -8.238795325112868, -4.55570233019602 -7.314696123025453, -6.071067811865475 -6.0710678118654755, -7.314696123025453 -4.555702330196022, -8.238795325112868 -2.826834323650899, -8.807852804032304 -0.9509032201612861, -9 1, -9 9, -8.807852804032304 10.950903220161287, -8.238795325112868 12.8268343236509, -7.314696123025453 14.555702330196022, -6
 .071067811865475 16.071067811865476, -4.55570233019602 17.314696123025453, -2.826834323650897 18.238795325112868, -0.9509032201612819 18.807852804032304, 1 19))
+  </op></test>
+</case>
+
+<case>
+  <desc>
+    Closed Line - CCW
+  </desc>
+  <a>
+    LINESTRING (1 9, 1 1, 9 1, 9 9, 1 9)
+  </a>
+<test><op name='buffer' arg1='A' arg2='-1.0'>   POLYGON EMPTY  </op></test>
+<test><op name='buffer' arg1='A' arg2='0.0'>    POLYGON EMPTY  </op></test>
+<test><op name='buffer' arg1='A' arg2='1'>   
+POLYGON ((1 10, 9 10, 9.195090322016128 9.98078528040323, 9.38268343236509 9.923879532511286, 9.555570233019603 9.831469612302545, 9.707106781186548 9.707106781186548, 9.831469612302545 9.555570233019601, 9.923879532511286 9.38268343236509, 9.98078528040323 9.195090322016128, 10 9, 10 1, 9.98078528040323 0.8049096779838718, 9.923879532511286 0.6173165676349102, 9.831469612302545 0.4444297669803978, 9.707106781186548 0.2928932188134525, 9.555570233019603 0.1685303876974548, 9.38268343236509 0.0761204674887133, 9.195090322016128 0.0192147195967696, 9 0, 1 0, 0.8049096779838718 0.0192147195967696, 0.6173165676349103 0.0761204674887133, 0.444429766980398 0.1685303876974547, 0.2928932188134525 0.2928932188134524, 0.1685303876974547 0.4444297669803978, 0.0761204674887133 0.6173165676349102, 0.0192147195967696 0.8049096779838714, 0 1, 0 9, 0.0192147195967696 9.195090322016128, 0.0761204674887133 9.38268343236509, 0.1685303876974547 9.555570233019601, 0.2928932188134525 9.707106781186548, 0
 .444429766980398 9.831469612302545, 0.6173165676349103 9.923879532511286, 0.8049096779838718 9.98078528040323, 1 10), (2 8, 2 2, 8 2, 8 8, 2 8))
+  </op></test>
+<test><op name='buffer' arg1='A' arg2='10.0'>   
+POLYGON ((1 19, 9 19, 10.950903220161283 18.807852804032304, 12.826834323650898 18.238795325112868, 14.555702330196024 17.314696123025453, 16.071067811865476 16.071067811865476, 17.314696123025453 14.555702330196022, 18.238795325112868 12.826834323650898, 18.807852804032304 10.950903220161283, 19 9, 19 1, 18.807852804032304 -0.9509032201612824, 18.238795325112868 -2.826834323650898, 17.314696123025453 -4.555702330196022, 16.071067811865476 -6.071067811865475, 14.555702330196024 -7.314696123025453, 12.826834323650898 -8.238795325112868, 10.950903220161283 -8.807852804032304, 9 -9, 1 -9, -0.9509032201612819 -8.807852804032304, -2.826834323650897 -8.238795325112868, -4.55570233019602 -7.314696123025453, -6.071067811865475 -6.0710678118654755, -7.314696123025453 -4.555702330196022, -8.238795325112868 -2.826834323650899, -8.807852804032304 -0.9509032201612861, -9 1, -9 9, -8.807852804032304 10.950903220161287, -8.238795325112868 12.8268343236509, -7.314696123025453 14.555702330196022, -6
 .071067811865475 16.071067811865476, -4.55570233019602 17.314696123025453, -2.826834323650897 18.238795325112868, -0.9509032201612819 18.807852804032304, 1 19))
+  </op></test>
+</case>
+
+<case>
+  <desc>
+    Linear Ring
+  </desc>
+  <a>
+    LINEARRING (1 9, 1 1, 9 1, 9 9, 1 9)
+  </a>
+<test><op name='buffer' arg1='A' arg2='-1.0'>   POLYGON EMPTY  </op></test>
+<test><op name='buffer' arg1='A' arg2='0.0'>    POLYGON EMPTY  </op></test>
+<test><op name='buffer' arg1='A' arg2='1'>   
+POLYGON ((1 10, 9 10, 9.195090322016128 9.98078528040323, 9.38268343236509 9.923879532511286, 9.555570233019603 9.831469612302545, 9.707106781186548 9.707106781186548, 9.831469612302545 9.555570233019601, 9.923879532511286 9.38268343236509, 9.98078528040323 9.195090322016128, 10 9, 10 1, 9.98078528040323 0.8049096779838718, 9.923879532511286 0.6173165676349102, 9.831469612302545 0.4444297669803978, 9.707106781186548 0.2928932188134525, 9.555570233019603 0.1685303876974548, 9.38268343236509 0.0761204674887133, 9.195090322016128 0.0192147195967696, 9 0, 1 0, 0.8049096779838718 0.0192147195967696, 0.6173165676349103 0.0761204674887133, 0.444429766980398 0.1685303876974547, 0.2928932188134525 0.2928932188134524, 0.1685303876974547 0.4444297669803978, 0.0761204674887133 0.6173165676349102, 0.0192147195967696 0.8049096779838714, 0 1, 0 9, 0.0192147195967696 9.195090322016128, 0.0761204674887133 9.38268343236509, 0.1685303876974547 9.555570233019601, 0.2928932188134525 9.707106781186548, 0
 .444429766980398 9.831469612302545, 0.6173165676349103 9.923879532511286, 0.8049096779838718 9.98078528040323, 1 10), (2 8, 2 2, 8 2, 8 8, 2 8))
+  </op></test>
+<test><op name='buffer' arg1='A' arg2='10.0'>   
+POLYGON ((1 19, 9 19, 10.950903220161283 18.807852804032304, 12.826834323650898 18.238795325112868, 14.555702330196024 17.314696123025453, 16.071067811865476 16.071067811865476, 17.314696123025453 14.555702330196022, 18.238795325112868 12.826834323650898, 18.807852804032304 10.950903220161283, 19 9, 19 1, 18.807852804032304 -0.9509032201612824, 18.238795325112868 -2.826834323650898, 17.314696123025453 -4.555702330196022, 16.071067811865476 -6.071067811865475, 14.555702330196024 -7.314696123025453, 12.826834323650898 -8.238795325112868, 10.950903220161283 -8.807852804032304, 9 -9, 1 -9, -0.9509032201612819 -8.807852804032304, -2.826834323650897 -8.238795325112868, -4.55570233019602 -7.314696123025453, -6.071067811865475 -6.0710678118654755, -7.314696123025453 -4.555702330196022, -8.238795325112868 -2.826834323650899, -8.807852804032304 -0.9509032201612861, -9 1, -9 9, -8.807852804032304 10.950903220161287, -8.238795325112868 12.8268343236509, -7.314696123025453 14.555702330196022, -6
 .071067811865475 16.071067811865476, -4.55570233019602 17.314696123025453, -2.826834323650897 18.238795325112868, -0.9509032201612819 18.807852804032304, 1 19))
+  </op></test>
+</case>
+
 <case>
   <desc>
   	Polygon

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

Summary of changes:
 .../geos/operation/buffer/BufferCurveSetBuilder.h  |  8 ++-
 src/operation/buffer/BufferCurveSetBuilder.cpp     | 77 ++++++++++++----------
 tests/unit/operation/buffer/BufferOpTest.cpp       | 13 ++++
 tests/xmltester/tests/general/TestBuffer.xml       | 51 ++++++++++++++
 4 files changed, 111 insertions(+), 38 deletions(-)


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list