[geos-commits] [SCM] GEOS branch main updated. 4c3bd72ba73e9688b0712d9878d073559c84e4d6

git at osgeo.org git at osgeo.org
Thu May 4 03:57:52 PDT 2023


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  4c3bd72ba73e9688b0712d9878d073559c84e4d6 (commit)
       via  cbe7a6c2f7334d301f7fa4262fbb43b9e394a23b (commit)
       via  714af1e46852f3638d4bf81bd374edf597ddbfe5 (commit)
      from  2bda4d22238c255ce7f0ddfe61a03da082a8737f (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 4c3bd72ba73e9688b0712d9878d073559c84e4d6
Author: Dan Baston <dbaston at gmail.com>
Date:   Thu May 4 06:57:25 2023 -0400

    Add GEOSOrientPolygons (#818)
    
    * Add GEOSOrientPolygons
    
    Fixes https://github.com/libgeos/geos/issues/779
    
    Co-authored-by: Joris Van den Bossche <jorisvandenbossche at gmail.com>

diff --git a/NEWS.md b/NEWS.md
index 4e3f67489..0eeb49079 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -15,6 +15,7 @@ xxxx-xx-xx
   - CAPI: GEOSDisjointSubsetUnion (GH-692, Dan Baston)
   - CAPI: GEOSLineSubstring (GH-706, Dan Baston)
   - CAPI: GEOSEqualsIdentical (GH-810, Dan Baston)
+  - CAPI: GEOSOrientPolygons (GH-818, Dan Baston)
   - CAPI: GEOSSTRtree_build (GH-835, Dan Baston)
   - CAPI: GEOSConcaveHullByLength (GH-849, Martin Davis)
   - CAPI: GEOSGeomGetM (GH-864, Mike Taves)
diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp
index 43224e5c9..04e6c4fd3 100644
--- a/capi/geos_c.cpp
+++ b/capi/geos_c.cpp
@@ -681,6 +681,12 @@ extern "C" {
         return GEOSNormalize_r(handle, g);
     }
 
+    int
+    GEOSOrientPolygons(Geometry* g, int exteriorCW)
+    {
+        return GEOSOrientPolygons_r(handle, g, exteriorCW);
+    }
+
     int
     GEOSGetNumInteriorRings(const Geometry* g)
     {
diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in
index 4574d6bba..94a39f9ad 100644
--- a/capi/geos_c.h.in
+++ b/capi/geos_c.h.in
@@ -1535,6 +1535,12 @@ extern int GEOS_DLL GEOSNormalize_r(
     GEOSContextHandle_t handle,
     GEOSGeometry* g);
 
+/** \see GEOSOrientPolygons */
+extern int GEOS_DLL GEOSOrientPolygons_r(
+    GEOSContextHandle_t handle,
+    GEOSGeometry* g,
+    int exteriorCW);
+
 /**
 * Controls the behavior of GEOSGeom_setPrecision()
 * when altering the precision of a geometry.
@@ -2916,6 +2922,18 @@ extern void GEOS_DLL GEOSGeom_setUserData(GEOSGeometry* g, void* userData);
 */
 extern int GEOS_DLL GEOSNormalize(GEOSGeometry* g);
 
+/**
+* Enforce a ring orientation on all polygonal elements in the input geometry.
+* Non-polygonal geometries will not be modified.
+*
+* \param g Input geometry
+* \param exteriorCW if 1, exterior rings will be clockwise and interior rings
+*                         will be counter-clockwise
+* \return 0 on success or -1 on exception
+*/
+extern int GEOS_DLL GEOSOrientPolygons(GEOSGeometry* g,
+                                       int exteriorCW);
+
 ///@}
 
 /* ========== Validity checking ============================================================ */
diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index 7cf62d528..363e34864 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -1671,6 +1671,32 @@ extern "C" {
         });
     }
 
+    int
+    GEOSOrientPolygons_r(GEOSContextHandle_t extHandle, Geometry* g, int exteriorCW)
+    {
+        return execute(extHandle, -1, [&]() {
+            class OrientPolygons : public geos::geom::GeometryComponentFilter {
+            public:
+                OrientPolygons(bool isExteriorCW) : exteriorCW(isExteriorCW) {}
+
+                void filter_rw(Geometry* g) override {
+                    if (g->getGeometryTypeId() == geos::geom::GeometryTypeId::GEOS_POLYGON) {
+                        auto p = geos::detail::down_cast<Polygon*>(g);
+                        p->orientRings(exteriorCW);
+                    }
+                }
+
+            private:
+                bool exteriorCW;
+            };
+
+            OrientPolygons op(exteriorCW);
+            g->apply_rw(&op);
+
+            return 0;
+        });
+    }
+
     int
     GEOSGetNumInteriorRings_r(GEOSContextHandle_t extHandle, const Geometry* g1)
     {
diff --git a/include/geos/geom/LinearRing.h b/include/geos/geom/LinearRing.h
index 7789565bd..b04b3bd35 100644
--- a/include/geos/geom/LinearRing.h
+++ b/include/geos/geom/LinearRing.h
@@ -103,6 +103,8 @@ public:
 
     std::unique_ptr<LinearRing> reverse() const { return std::unique_ptr<LinearRing>(reverseImpl()); }
 
+    void orient(bool isCW);
+
 protected:
 
     int
diff --git a/include/geos/geom/Polygon.h b/include/geos/geom/Polygon.h
index 8720c2ae4..abd5f100f 100644
--- a/include/geos/geom/Polygon.h
+++ b/include/geos/geom/Polygon.h
@@ -152,6 +152,16 @@ public:
 
     void normalize() override;
 
+    /**
+     * \brief
+     * Apply a ring ordering convention to this polygon, with
+     * interior rings having an opposite orientation to the
+     * specified exterior orientation.
+     *
+     * \param exteriorCW should exterior ring be clockwise?
+     */
+    void orientRings(bool exteriorCW);
+
     std::unique_ptr<Polygon> reverse() const { return std::unique_ptr<Polygon>(reverseImpl()); }
 
     const CoordinateXY* getCoordinate() const override;
diff --git a/src/geom/LinearRing.cpp b/src/geom/LinearRing.cpp
index aa4097b4e..6afc506c0 100644
--- a/src/geom/LinearRing.cpp
+++ b/src/geom/LinearRing.cpp
@@ -17,6 +17,8 @@
  *
  **********************************************************************/
 
+#include <geos/algorithm/Orientation.h>
+
 #include <geos/geom/LinearRing.h>
 #include <geos/geom/Dimension.h>
 #include <geos/geom/CoordinateSequence.h>
@@ -98,6 +100,19 @@ LinearRing::getGeometryTypeId() const
     return GEOS_LINEARRING;
 }
 
+void
+LinearRing::orient(bool isCW)
+{
+    if (isEmpty()) {
+        return;
+    }
+
+    if (algorithm::Orientation::isCCW(points.get()) == isCW) {
+        points->reverse();
+    }
+
+}
+
 LinearRing*
 LinearRing::reverseImpl() const
 {
diff --git a/src/geom/Polygon.cpp b/src/geom/Polygon.cpp
index 34c6d4560..515275d51 100644
--- a/src/geom/Polygon.cpp
+++ b/src/geom/Polygon.cpp
@@ -363,6 +363,15 @@ Polygon::normalize()
     });
 }
 
+void
+Polygon::orientRings(bool exteriorCW)
+{
+    shell->orient(exteriorCW);
+    for (auto& hole : holes) {
+        hole->orient(!exteriorCW);
+    }
+}
+
 int
 Polygon::compareToSameClass(const Geometry* g) const
 {
diff --git a/tests/unit/capi/GEOSOrientPolygonsTest.cpp b/tests/unit/capi/GEOSOrientPolygonsTest.cpp
new file mode 100644
index 000000000..745ac1bc0
--- /dev/null
+++ b/tests/unit/capi/GEOSOrientPolygonsTest.cpp
@@ -0,0 +1,94 @@
+//
+// Test Suite for C-API GEOSOrientationPolygons
+
+#include <tut/tut.hpp>
+// geos
+#include <geos_c.h>
+// std
+#include <string>
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <memory>
+
+#include "capi_test_utils.h"
+
+namespace tut {
+//
+// Test Group
+//
+
+// Common data used in test cases.
+struct test_capigeosorientpolygons_data : public capitest::utility {};
+
+typedef test_group<test_capigeosorientpolygons_data> group;
+typedef group::object object;
+
+group test_capigeosorientpolygons_group("capi::GEOSOrientPolygons");
+
+// empty polygon is passed through
+template<>
+template<>
+void object::test<1>()
+{
+    geom1_ = GEOSGeomFromWKT("POLYGON EMPTY");
+    ensure_equals(GEOSOrientPolygons(geom1_, 1), 0);
+
+    ensure_equals(toWKT(geom1_), "POLYGON EMPTY");
+}
+
+// hole orientation is opposite to shell
+template<>
+template<>
+void object::test<2>()
+{
+    geom1_ = GEOSGeomFromWKT("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))");
+
+    ensure_equals(GEOSOrientPolygons(geom1_, 0), 0);
+    ensure_equals(toWKT(geom1_), "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))");
+
+    ensure_equals(GEOSOrientPolygons(geom1_, 1), 0);
+    ensure_equals(toWKT(geom1_), "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))");
+}
+
+// all polygons in collection are processed
+template<>
+template<>
+void object::test<3>()
+{
+    geom1_ = GEOSGeomFromWKT("MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), ((100 100, 200 100, 200 200, 100 100)))");
+
+    ensure_equals(GEOSOrientPolygons(geom1_, 0), 0);
+    ensure_equals(toWKT(geom1_), "MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1)), ((100 100, 200 100, 200 200, 100 100)))");
+
+    ensure_equals(GEOSOrientPolygons(geom1_, 1), 0);
+    ensure_equals(toWKT(geom1_), "MULTIPOLYGON (((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), ((100 100, 200 200, 200 100, 100 100)))");
+}
+
+// polygons in collection are oriented, closed linestring unchanged
+template<>
+template<>
+void object::test<4>()
+{
+    geom1_ = GEOSGeomFromWKT("GEOMETRYCOLLECTION (POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), LINESTRING (100 100, 200 100, 200 200, 100 100))");
+
+    ensure_equals(GEOSOrientPolygons(geom1_, 1), 0);
+    ensure_equals(toWKT(geom1_), "GEOMETRYCOLLECTION (POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), LINESTRING (100 100, 200 100, 200 200, 100 100))");
+}
+
+// nested collection handled correctly
+template<>
+template<>
+void object::test<5>()
+{
+    geom1_ = GEOSGeomFromWKT("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))))");
+
+    ensure_equals(GEOSOrientPolygons(geom1_, 0), 0);
+    ensure_equals(toWKT(geom1_), "GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))))");
+
+    ensure_equals(GEOSOrientPolygons(geom1_, 1), 0);
+    ensure_equals(toWKT(geom1_), "GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (MULTIPOLYGON (((0 0, 0 10, 10 10, 10 0, 0 0)))))");
+}
+
+} // namespace tut
+

commit cbe7a6c2f7334d301f7fa4262fbb43b9e394a23b
Author: Mike Taves <mwtoews at gmail.com>
Date:   Thu May 4 15:25:02 2023 +1200

    CAPI: fix GEOSHasZ with empty geometries (#887)

diff --git a/NEWS.md b/NEWS.md
index c440567ea..4e3f67489 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -62,6 +62,7 @@ xxxx-xx-xx
   - Tri: add exceptions for invalid indexes (GH-853, Martin Davis)
   - LargestEmptyCircle: enhance boundary to allow any polygonal geometry (GH-859, Martin Davis)
   - Fix MaximumInscribedCircle and LargestEmptyCircle performance and memory issues (GH-883, Martin Davis)
+  - GEOSHasZ: Fix handling with empty geometries (GH-887, Mike Taves)
 
 - Changes:
   - Remove Orientation.isCCW exception to simplify logic and align with JTS (GH-878, Martin Davis)
diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index 5ca390319..7cf62d528 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -2410,10 +2410,6 @@ extern "C" {
     GEOSHasZ_r(GEOSContextHandle_t extHandle, const Geometry* g)
     {
         return execute(extHandle, 2, [&]() {
-            if(g->isEmpty()) {
-                return false;
-            }
-
             return g->hasZ();
         });
     }
diff --git a/tests/unit/capi/GEOSHasZMTest.cpp b/tests/unit/capi/GEOSHasZMTest.cpp
index 1b2ab36c4..54b0431cf 100644
--- a/tests/unit/capi/GEOSHasZMTest.cpp
+++ b/tests/unit/capi/GEOSHasZMTest.cpp
@@ -46,5 +46,35 @@ void object::test<3>()
     ensure_equals(GEOSHasM(input_), 0);
 }
 
+template<>
+template<>
+void object::test<4>()
+{
+    input_ = GEOSGeomFromWKT("POINT Z EMPTY");
+
+    ensure_equals(GEOSHasZ(input_), 1);
+    ensure_equals(GEOSHasM(input_), 0);
+}
+
+template<>
+template<>
+void object::test<5>()
+{
+    input_ = GEOSGeomFromWKT("POINT M EMPTY");
+
+    ensure_equals(GEOSHasZ(input_), 0);
+    ensure_equals(GEOSHasM(input_), 1);
+}
+
+template<>
+template<>
+void object::test<6>()
+{
+    input_ = GEOSGeomFromWKT("POINT ZM EMPTY");
+
+    ensure_equals(GEOSHasZ(input_), 1);
+    ensure_equals(GEOSHasM(input_), 1);
+}
+
 } // namespace tut
 

commit 714af1e46852f3638d4bf81bd374edf597ddbfe5
Author: Peter Stace <peterstace at gmail.com>
Date:   Thu May 4 11:34:30 2023 +1000

    Clarify coverage union behaviour with unmet constraints (#890)

diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in
index 5b5e9af03..4574d6bba 100644
--- a/capi/geos_c.h.in
+++ b/capi/geos_c.h.in
@@ -3762,8 +3762,9 @@ extern GEOSGeometry GEOS_DLL *GEOSOffsetCurve(const GEOSGeometry* g,
 
 /**
 * Optimized union algorithm for polygonal inputs that are correctly
-* noded and do not overlap. It will generate an error (return NULL)
-* for inputs that do not satisfy this constraint.
+* noded and do not overlap. It may generate an error (return NULL)
+* for inputs that do not satisfy this constraint, however this is not
+* guaranteed.
 * \param g The input geometry
 * \return A geometry that covers all the points of the input geometry.
 * Caller is responsible for freeing with GEOSGeom_destroy().

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

Summary of changes:
 NEWS.md                                    |  2 +
 capi/geos_c.cpp                            |  6 ++
 capi/geos_c.h.in                           | 23 +++++++-
 capi/geos_ts_c.cpp                         | 30 ++++++++--
 include/geos/geom/LinearRing.h             |  2 +
 include/geos/geom/Polygon.h                | 10 ++++
 src/geom/LinearRing.cpp                    | 15 +++++
 src/geom/Polygon.cpp                       |  9 +++
 tests/unit/capi/GEOSHasZMTest.cpp          | 30 ++++++++++
 tests/unit/capi/GEOSOrientPolygonsTest.cpp | 94 ++++++++++++++++++++++++++++++
 10 files changed, 215 insertions(+), 6 deletions(-)
 create mode 100644 tests/unit/capi/GEOSOrientPolygonsTest.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list