[geos-commits] [SCM] GEOS branch main updated. 5bb0eba2b33b924440b356334540e64b60c86201

git at osgeo.org git at osgeo.org
Tue Sep 7 19:26:51 PDT 2021


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  5bb0eba2b33b924440b356334540e64b60c86201 (commit)
      from  8ac9e91c1bb602f8a98596d9a4da4f1fd4e3cc34 (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 5bb0eba2b33b924440b356334540e64b60c86201
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Tue Sep 7 19:26:36 2021 -0700

    Port JTS https://github.com/locationtech/jts/pull/772 GeometryFixer support for unnested holes

diff --git a/NEWS b/NEWS
index cc8b6f1..da9ee04 100644
--- a/NEWS
+++ b/NEWS
@@ -6,11 +6,13 @@ Changes in 3.10.0
     library (#868, Paul Ramsey)
   - geosop CLI for GEOS (Martin Davis)
   - Full doxygen of the C-API (Paul Ramsey)
+  - GeometryFixer class for validity enforcement (Martin Davis, Paul Ramsey)
   - CAPI: GEOSDensify (Brendan Ward)
   - CAPI: GEOSCoordSeq_copyFromArrays, GEOSCoordSeq_copyFromBuffer,
           GEOSCoordSeq_copyToArrays, GEOSCoordSeq_copyToBuffer (Daniel Baston)
   - CAPI: GEOSMakeValidWithParams new validity enforcement approach from
-          https://github.com/locationtech/jts/pull/704 (Paul Ramsey, Martin Davis)
+          https://github.com/locationtech/jts/pull/704, uses GeometryFixer
+          (Paul Ramsey, Martin Davis)
 
 - Fixes/Improvements:
   - Preserve ordering of lines in overlay results (Martin Davis)
diff --git a/include/geos/geom/util/GeometryFixer.h b/include/geos/geom/util/GeometryFixer.h
index 4998857..7896632 100644
--- a/include/geos/geom/util/GeometryFixer.h
+++ b/include/geos/geom/util/GeometryFixer.h
@@ -54,17 +54,20 @@ namespace util { // geos.geom.util
  *  * Empty atomic geometries are valid and are returned unchanged
  *  * Empty elements are removed from collections
  *  * <code>Point</code>: keep valid coordinate, or EMPTY
- *  * <code>LineString</code>: fix coordinate list
- *  * <code>LinearRing</code>: fix coordinate list, return as valid ring or else <code>LineString</code>
- *  * <code>Polygon</code>: transform into a valid polygon, preserving as much of the extent and vertices as possible
- *  * <code>MultiPolygon</code>: fix each polygon, then ensure result is non-overlapping (via union)
- *  * <code>GeometryCollection</code>: fix each element
+ *  * <code>LineString</code>: coordinates are fixed
+ *  * <code>LinearRing</code>: coordinates are fixed, Keep valid ring or else convert into <code>LineString</code>
+ *  * <code>Polygon</code>: transform into a valid polygon,
+ *  * preserving as much of the extent and vertices as possible.
+ *    * Rings are fixed to ensure they are valid<
+ *    * Holes intersecting the shell are subtracted from the shell
+ *    * Holes outside the shell are converted into polygons
+ *  * <code>MultiPolygon</code>: each polygon is fixed,
+ *    then result made non-overlapping (via union)
+ *  * <code>GeometryCollection</code>: each element is fixed
  *  * Collapsed lines and polygons are handled as follows,
- *
- * depending on the <code>keepCollapsed</code> setting:
- *
- *  * <code>false</code>: (default) collapses are converted to empty geometries
- *  * <code>true</code>: collapses are converted to a valid geometry of lower dimension
+ *    depending on the <code>keepCollapsed</code> setting:
+ *    * <code>false</code>: (default) collapses are converted to empty geometries
+ *    * <code>true</code>: collapses are converted to a valid geometry of lower dimension
  *
  * @author Martin Davis
 */
@@ -123,12 +126,40 @@ private:
     std::unique_ptr<geom::Geometry> fixMultiLineString(const MultiLineString* geom) const;
     std::unique_ptr<geom::Geometry> fixPolygon(const geom::Polygon* geom) const;
     std::unique_ptr<geom::Geometry> fixPolygonElement(const geom::Polygon* geom) const;
-    std::unique_ptr<geom::Geometry> fixHoles(const geom::Polygon* geom) const;
+    std::vector<std::unique_ptr<Geometry>> fixHoles(const geom::Polygon* geom) const;
     std::unique_ptr<geom::Geometry> removeHoles(const geom::Geometry* shell, const geom::Geometry* holes) const;
     std::unique_ptr<geom::Geometry> fixRing(const geom::LinearRing* ring) const;
     std::unique_ptr<geom::Geometry> fixMultiPolygon(const geom::MultiPolygon* geom) const;
     std::unique_ptr<geom::Geometry> fixCollection(const geom::GeometryCollection* geom) const;
 
+    void classifyHoles(
+        const Geometry* shell,
+        std::vector<std::unique_ptr<Geometry>>& holesFixed,
+        std::vector<const Geometry*>& holes,
+        std::vector<const Geometry*>& shells) const;
+
+    /**
+    * Subtracts a list of polygonal geometries from a polygonal geometry.
+    *
+    * @param shell polygonal geometry for shell
+    * @param holes polygonal geometries to subtract
+    * @return the result geometry
+    */
+    std::unique_ptr<Geometry> difference(
+        const Geometry* shell,
+        std::vector<const Geometry*>& holes) const;
+
+    /**
+    * Unions a list of polygonal geometries.
+    * Optimizes case of zero or one input geometries.
+    * Requires that the inputs are net new objects.
+    *
+    * @param polys the polygonal geometries to union
+    * @return the union of the inputs
+    */
+    std::unique_ptr<Geometry> unionGeometry(
+        std::vector<const Geometry*>& polys) const;
+
 };
 
 } // namespace geos.geom.util
diff --git a/src/geom/util/GeometryFixer.cpp b/src/geom/util/GeometryFixer.cpp
index 634380f..67a3026 100644
--- a/src/geom/util/GeometryFixer.cpp
+++ b/src/geom/util/GeometryFixer.cpp
@@ -15,13 +15,18 @@
 #include <geos/geom/util/GeometryFixer.h>
 #include <geos/geom/Geometry.h>
 #include <geos/geom/GeometryFactory.h>
+#include <geos/geom/prep/PreparedGeometry.h>
+#include <geos/geom/prep/PreparedGeometryFactory.h>
 #include <geos/operation/overlayng/OverlayNG.h>
 #include <geos/operation/overlayng/OverlayNGRobust.h>
 #include <geos/operation/buffer/BufferOp.h>
 #include <geos/operation/valid/RepeatedPointRemover.h>
 #include <geos/util/UnsupportedOperationException.h>
+#include <geos/operation/union/UnaryUnionOp.h>
 
 using geos::operation::valid::RepeatedPointRemover;
+using geos::operation::overlayng::OverlayNGRobust;
+using geos::operation::geounion::UnaryUnionOp;
 
 namespace geos {
 namespace geom { // geos.geom
@@ -248,52 +253,99 @@ GeometryFixer::fixPolygonElement(const Polygon* p_geom) const
             std::unique_ptr<LineString> line(factory->createLineString(*cs));
             return fixLineString(line.get());
         }
-        //-- if not allowing collapses then return empty polygon
+        //--- if not allowing collapses then return empty polygon
         return nullptr;
     }
-    // if no holes then done
+    //--- if no holes then done
     if (p_geom->getNumInteriorRing() == 0) {
         return fixShell;
     }
-    std::unique_ptr<Geometry> fixedHoles = fixHoles(p_geom);
-    std::unique_ptr<Geometry> result = removeHoles(fixShell.get(), fixedHoles.get());
-    return result;
+
+    //--- fix holes, classify, and construct shell-true holes
+    std::vector<std::unique_ptr<Geometry>> holesFixed = fixHoles(p_geom);
+    std::vector<const Geometry*> holes;
+    std::vector<const Geometry*> shells;
+
+    classifyHoles(fixShell.get(), holesFixed, holes, shells);
+    std::unique_ptr<Geometry> polyWithHoles = difference(fixShell.get(), holes);
+    if (shells.empty()) {
+        return polyWithHoles;
+    }
+
+    //--- if some holes converted to shells, union all shells
+    shells.push_back(polyWithHoles.get());
+    return unionGeometry(shells);
 }
 
 /* private */
-std::unique_ptr<Geometry>
-GeometryFixer::fixHoles(const Polygon* p_geom) const
+std::vector<std::unique_ptr<Geometry>>
+GeometryFixer::fixHoles(const Polygon* poly) const
 {
     std::vector<std::unique_ptr<Geometry>> holes;
-
-    for (std::size_t i = 0; i < p_geom->getNumInteriorRing(); i++) {
-        std::unique_ptr<Geometry> holeRep = fixRing(p_geom->getInteriorRingN(i));
-        // Do not preserve null/empty holes
-        if (holeRep != nullptr && !holeRep->isEmpty()) {
+    for (std::size_t i = 0; i < poly->getNumInteriorRing(); i++) {
+        std::unique_ptr<Geometry> holeRep = fixRing(poly->getInteriorRingN(i));
+        if (holeRep != nullptr) {
             holes.emplace_back(holeRep.release());
         }
     }
-    if (holes.empty())
-        return nullptr;
+    return holes;
+}
 
-    if (holes.size() == 1) {
-        std::unique_ptr<Geometry> h(holes.at(0).release());
-        return h;
+/* private */
+void
+GeometryFixer::classifyHoles(
+    const Geometry* shell,
+    std::vector<std::unique_ptr<Geometry>>& holesFixed,
+    std::vector<const Geometry*>& holes,
+    std::vector<const Geometry*>& shells) const
+{
+    std::unique_ptr<prep::PreparedGeometry> shellPrep =
+        prep::PreparedGeometryFactory::prepare(shell);
+
+    for (auto& hole : holesFixed) {
+        const Geometry* cptrHole = hole.get();
+        if (shellPrep->intersects(cptrHole)) {
+            holes.push_back(cptrHole);
+        }
+        else {
+            shells.push_back(cptrHole);
+        }
     }
-    std::unique_ptr<GeometryCollection> holesGeom = (factory->createGeometryCollection(std::move(holes)));
-    return operation::overlayng::OverlayNGRobust::Union(holesGeom.get());
 }
 
+
 /* private */
 std::unique_ptr<Geometry>
-GeometryFixer::removeHoles(const Geometry* shell, const Geometry* holes) const
+GeometryFixer::difference(
+    const Geometry* shell,
+    std::vector<const Geometry*>& holes) const
 {
-    if (holes == nullptr || holes->isEmpty())
+    if (holes.empty())
         return shell->clone();
-    return operation::overlayng::OverlayNGRobust::Overlay(shell, holes,
-        operation::overlayng::OverlayNG::DIFFERENCE);
+    if (holes.size() == 1)
+        return OverlayNGRobust::Difference(shell, holes[0]);
+    std::unique_ptr<Geometry> holesUnion = unionGeometry(holes);
+    return OverlayNGRobust::Difference(shell, holesUnion.get());
+}
+
+
+/* private */
+std::unique_ptr<Geometry>
+GeometryFixer::unionGeometry(std::vector<const Geometry*>& polys) const
+{
+    if (polys.empty()) {
+        return factory->createPolygon(geom->getCoordinateDimension());
+    }
+    if (polys.size() == 1) {
+        return (polys[0])->clone();
+    }
+
+    UnaryUnionOp op(polys);
+    return op.Union();
+    // return OverlayNGRobust::Union(polys);
 }
 
+
 /* private */
 std::unique_ptr<Geometry>
 GeometryFixer::fixRing(const LinearRing* ring) const
@@ -322,7 +374,7 @@ GeometryFixer::fixMultiPolygon(const MultiPolygon* p_geom) const
         return factory->createMultiPolygon();
     }
     std::unique_ptr<GeometryCollection> polysGeom = (factory->createGeometryCollection(std::move(polys)));
-    return operation::overlayng::OverlayNGRobust::Union(polysGeom.get());
+    return OverlayNGRobust::Union(polysGeom.get());
 }
 
 /* private */
diff --git a/tests/unit/geom/util/GeometryFixerTest.cpp b/tests/unit/geom/util/GeometryFixerTest.cpp
index f724b13..1a4d7c4 100644
--- a/tests/unit/geom/util/GeometryFixerTest.cpp
+++ b/tests/unit/geom/util/GeometryFixerTest.cpp
@@ -58,6 +58,7 @@ struct test_geometryfixer_data {
     }
 
     void checkFix(const Geometry* input, bool keepCollapse, const std::string& wktExpected) {
+
         std::unique_ptr<Geometry> actual;
         if (keepCollapse) {
             GeometryFixer fixer(input);
@@ -110,9 +111,6 @@ struct test_geometryfixer_data {
         std::string actualWKT = wktwriter_.write(actual.get());
         std::string expectedWKT = wktwriter_.write(expected.get());
         ensure_equals(actualWKT, expectedWKT);
-
-
-        // checkEqualXYZ(expected, actual);
     }
 
     std::unique_ptr<Point> createPoint(double x, double y) {
@@ -123,12 +121,11 @@ struct test_geometryfixer_data {
     }
 };
 
-typedef test_group<test_geometryfixer_data> group;
+typedef test_group<test_geometryfixer_data, 255> group;
 typedef group::object object;
 
 group test_geometryfixer_group("geos::geom::util::GeometryFixer");
 
-
 template<>
 template<>
 void object::test<1>()
@@ -170,7 +167,7 @@ void object::test<5>()
     checkFix(pt.get() , "POINT EMPTY");
 }
 
-  //----------------------------------------
+//----------------------------------------
 
 // testMultiPointNaN
 template<>
@@ -208,7 +205,7 @@ void object::test<9>()
         "MULTIPOINT EMPTY");
 }
 
-  //----------------------------------------
+//----------------------------------------
 
 // testLineStringEmpty
 template<>
@@ -585,6 +582,7 @@ void object::test<47>()
         "GEOMETRYCOLLECTION EMPTY");
 }
 
+
 // testGCWithAllEmpty
 template<>
 template<>
@@ -595,9 +593,6 @@ void object::test<48>()
         "GEOMETRYCOLLECTION (POINT EMPTY, LINESTRING EMPTY, POLYGON EMPTY)");
 }
 
-
-   //----------------------------------------
-
 template<>
 template<>
 void object::test<49>()
@@ -607,10 +602,20 @@ void object::test<49>()
         "MULTIPOLYGON Z(((10 10 1, 10 90 1, 50 50 5, 10 10 1)), ((50 50 5, 90 90 9, 90 10 9, 50 50 5)))");
 }
 
+// testPolygonHoleOverlapAndOutsideOverlap
 template<>
 template<>
 void object::test<50>()
 {
+    checkFix(
+        "POLYGON ((50 90, 80 90, 80 10, 50 10, 50 90), (70 80, 90 80, 90 20, 70 20, 70 80), (40 80, 40 50, 0 50, 0 80, 40 80), (30 40, 10 40, 10 60, 30 60, 30 40), (60 70, 80 70, 80 30, 60 30, 60 70))",
+        "MULTIPOLYGON (((10 40, 10 50, 0 50, 0 80, 40 80, 40 50, 30 50, 30 40, 10 40)), ((70 80, 70 70, 60 70, 60 30, 70 30, 70 20, 80 20, 80 10, 50 10, 50 90, 80 90, 80 80, 70 80)))");
+}
+
+template<>
+template<>
+void object::test<51>()
+{
     checkFixZ(
         "POLYGON Z ((10 90 1, 60 90 6, 60 10 6, 10 10 1, 10 90 1), (20 80 2, 90 80 9, 90 20 9, 20 20 2, 20 80 2))",
         "POLYGON Z((10 10 1, 10 90 1, 60 90 6, 60 80 6, 20 80 2, 20 20 2, 60 20 6, 60 10 6, 10 10 1))");
@@ -627,5 +632,4 @@ void object::test<52>()
 
 
 
-
 } // namespace tut

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

Summary of changes:
 NEWS                                       |   4 +-
 include/geos/geom/util/GeometryFixer.h     |  53 +++++++++++----
 src/geom/util/GeometryFixer.cpp            | 100 ++++++++++++++++++++++-------
 tests/unit/geom/util/GeometryFixerTest.cpp |  26 ++++----
 4 files changed, 136 insertions(+), 47 deletions(-)


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list