[geos-commits] [SCM] GEOS branch master updated. b0d8c28126c951d551db093201994a88f5249a6b

git at osgeo.org git at osgeo.org
Tue May 14 09:18:48 PDT 2019


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, master has been updated
       via  b0d8c28126c951d551db093201994a88f5249a6b (commit)
       via  548d51586033a243a573a7c26cb9bc7148b45b17 (commit)
       via  6e98b88986e0c516c99eeab317683b3e67c58f94 (commit)
       via  6aba335ce91576fc8925b60eca8ee665e800e2f4 (commit)
       via  ff1cf7d6f9e44affc2e290050a907ae903474973 (commit)
       via  1a557e9d49b0bb37b5a433deb53ae48fe14cf193 (commit)
      from  2de4bc0deba860d74feb81b3551bbb9d95a3e762 (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 b0d8c28126c951d551db093201994a88f5249a6b
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu May 9 13:46:54 2019 -0400

    Add GEOSPolygonize_valid to CAPI
    
    Closes #727

diff --git a/NEWS b/NEWS
index 59f7f4a..b014dce 100644
--- a/NEWS
+++ b/NEWS
@@ -4,9 +4,12 @@ Changes in 3.8.0
 - New things:
   - CAPI: GEOSBuildArea (#952, Even Rouault)
   - CAPI: GEOSMakeValid (#952, Even Rouault)
+  - CAPI: GEOSPolygonize_valid (#727, Dan Baston)
 
 - Improvements:
   - Improve performance and robustness of GEOSPointOnSurface (Martin Davis)
+  - Improve performance of GEOSPolygonize for cases with many potential
+    holes (#748, Dan Baston)
 
 
 Changes in 3.7.2
diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp
index 78e28c8..11f2e81 100644
--- a/capi/geos_c.cpp
+++ b/capi/geos_c.cpp
@@ -712,6 +712,12 @@ extern "C" {
     }
 
     Geometry*
+    GEOSPolygonize_valid(const Geometry* const* g, unsigned int ngeoms)
+    {
+        return GEOSPolygonize_valid_r(handle, g, ngeoms);
+    }
+
+    Geometry*
     GEOSPolygonizer_getCutEdges(const Geometry* const* g, unsigned int ngeoms)
     {
         return GEOSPolygonizer_getCutEdges_r(handle, g, ngeoms);
diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in
index 6b5fc57..0cd0ef1 100644
--- a/capi/geos_c.h.in
+++ b/capi/geos_c.h.in
@@ -596,20 +596,20 @@ extern GEOSGeometry GEOS_DLL *GEOSClipByRect_r(GEOSContextHandle_t handle,
  * Polygonizes a set of Geometries which contain linework that
  * represents the edges of a planar graph.
  *
- * Any dimension of Geometry is handled - the constituent linework
- * is extracted to form the edges.
+ * All types of Geometry are accepted as input; the constituent
+ * linework is extracted as the edges to be polygonized.
  *
  * The edges must be correctly noded; that is, they must only meet
- * at their endpoints.
- * The Polygonizer will still run on incorrectly noded input
- * but will not form polygons from incorrectly noded edges.
+ * at their endpoints. Polygonization will accept incorrectly noded
+ * input but will not form polygons from non-noded edges, and reports
+ * them as errors.
  *
  * The Polygonizer reports the follow kinds of errors:
  *
  * - Dangles - edges which have one or both ends which are
  *   not incident on another edge endpoint
  * - Cut Edges - edges which are connected at both ends but
- *   which do not form part of polygon
+ *   which do not form part of a polygon
  * - Invalid Ring Lines - edges which form rings which are invalid
  *   (e.g. the component lines contain a self-intersection)
  *
@@ -618,11 +618,19 @@ extern GEOSGeometry GEOS_DLL *GEOSClipByRect_r(GEOSContextHandle_t handle,
  * collection. NULL is returned on exception. All returned
  * geometries must be destroyed by caller.
  *
+ * The GEOSPolygonize_valid_r variant allows extracting only polygons
+ * which form a valid polygonal result. The set of extracted polygons
+ * is guaranteed to be edge-disjoint. This is useful when it is known
+ * that the input lines form a valid polygonal geometry (which may
+ * include holes or nested polygons).
  */
 
 extern GEOSGeometry GEOS_DLL *GEOSPolygonize_r(GEOSContextHandle_t handle,
                               const GEOSGeometry *const geoms[],
                               unsigned int ngeoms);
+extern GEOSGeometry GEOS_DLL *GEOSPolygonize_valid_r(GEOSContextHandle_t handle,
+                                                     const GEOSGeometry *const geoms[],
+                                                     unsigned int ngems);
 extern GEOSGeometry GEOS_DLL *GEOSPolygonizer_getCutEdges_r(
                               GEOSContextHandle_t handle,
                               const GEOSGeometry * const geoms[],
@@ -1607,34 +1615,8 @@ extern GEOSGeometry GEOS_DLL *GEOSClipByRect(const GEOSGeometry* g, double xmin,
  * (both Geometries and pointers)
  */
 extern GEOSGeometry GEOS_DLL *GEOSPolygonize(const GEOSGeometry * const geoms[], unsigned int ngeoms);
+extern GEOSGeometry GEOS_DLL *GEOSPolygonize_valid(const GEOSGeometry * const geoms[], unsigned int ngeoms);
 extern GEOSGeometry GEOS_DLL *GEOSPolygonizer_getCutEdges(const GEOSGeometry * const geoms[], unsigned int ngeoms);
-/*
- * Polygonizes a set of Geometries which contain linework that
- * represents the edges of a planar graph.
- *
- * Any dimension of Geometry is handled - the constituent linework
- * is extracted to form the edges.
- *
- * The edges must be correctly noded; that is, they must only meet
- * at their endpoints.
- * The Polygonizer will still run on incorrectly noded input
- * but will not form polygons from incorrectly noded edges.
- *
- * The Polygonizer reports the follow kinds of errors:
- *
- * - Dangles - edges which have one or both ends which are
- *   not incident on another edge endpoint
- * - Cut Edges - edges which are connected at both ends but
- *   which do not form part of polygon
- * - Invalid Ring Lines - edges which form rings which are invalid
- *   (e.g. the component lines contain a self-intersection)
- *
- * Errors are reported to output parameters "cuts", "dangles" and
- * "invalid" (if not-null). Formed polygons are returned as a
- * collection. NULL is returned on exception. All returned
- * geometries must be destroyed by caller.
- *
- */
 extern GEOSGeometry GEOS_DLL *GEOSPolygonize_full(const GEOSGeometry* input,
     GEOSGeometry** cuts, GEOSGeometry** dangles, GEOSGeometry** invalid);
 
diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index 58c0cc0..5744252 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -3157,6 +3157,54 @@ extern "C" {
     }
 
     Geometry*
+    GEOSPolygonize_valid_r(GEOSContextHandle_t extHandle, const Geometry* const* g, unsigned int ngeoms)
+    {
+        if(0 == extHandle) {
+            return 0;
+        }
+
+        GEOSContextHandleInternal_t* handle = 0;
+        handle = reinterpret_cast<GEOSContextHandleInternal_t*>(extHandle);
+        if(0 == handle->initialized) {
+            return 0;
+        }
+
+        Geometry* out = 0;
+
+        try {
+            // Polygonize
+            using geos::operation::polygonize::Polygonizer;
+            Polygonizer plgnzr(true);
+            for(std::size_t i = 0; i < ngeoms; ++i) {
+                plgnzr.add(g[i]);
+            }
+
+            auto polys = plgnzr.getPolygons();
+            if (polys->empty()) {
+                out = handle->geomFactory->createGeometryCollection();
+            } else if (polys->size() == 1) {
+                out = (*polys)[0].release();
+            } else {
+                auto geoms = new std::vector<Geometry *>(polys->size());
+                for (size_t i = 0; i < polys->size(); i++) {
+                    (*geoms)[i] = (*polys)[i].release();
+                }
+
+                out = handle->geomFactory->createMultiPolygon(geoms);
+            }
+        }
+        catch(const std::exception& e) {
+            handle->ERROR_MESSAGE("%s", e.what());
+        }
+        catch(...) {
+            handle->ERROR_MESSAGE("Unknown exception thrown");
+        }
+
+        return out;
+    }
+
+
+    Geometry*
     GEOSBuildArea_r(GEOSContextHandle_t extHandle, const Geometry* g)
     {
         if(nullptr == extHandle) {
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
index e965957..0531e38 100644
--- a/tests/unit/Makefile.am
+++ b/tests/unit/Makefile.am
@@ -160,7 +160,7 @@ geos_unit_SOURCES = \
 	capi/GEOSUserDataTest.cpp \
 	capi/GEOSPreparedGeometryTest.cpp \
 	capi/GEOSPointOnSurfaceTest.cpp \
-	capi/GEOSPolygonizer_getCutEdgesTest.cpp \
+	capi/GEOSPolygonizeTest.cpp \
 	capi/GEOSBufferTest.cpp \
 	capi/GEOSOffsetCurveTest.cpp \
 	capi/GEOSGeom_create.cpp \
diff --git a/tests/unit/capi/GEOSPolygonizeTest.cpp b/tests/unit/capi/GEOSPolygonizeTest.cpp
new file mode 100644
index 0000000..56cfcdb
--- /dev/null
+++ b/tests/unit/capi/GEOSPolygonizeTest.cpp
@@ -0,0 +1,179 @@
+//
+// Test Suite for C-API GEOSPolygonize*
+
+#include <tut/tut.hpp>
+// geos
+#include <geos_c.h>
+// std
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <memory>
+
+namespace tut {
+//
+// Test Group
+//
+
+// Common data used in test cases.
+struct test_capigeospolygonize_data {
+    static void
+    notice(const char* fmt, ...)
+    {
+        std::fprintf(stdout, "NOTICE: ");
+
+        va_list ap;
+        va_start(ap, fmt);
+        std::vfprintf(stdout, fmt, ap);
+        va_end(ap);
+
+        std::fprintf(stdout, "\n");
+    }
+
+    test_capigeospolygonize_data()
+    {
+        initGEOS(notice, notice);
+    }
+
+    ~test_capigeospolygonize_data()
+    {
+        finishGEOS();
+    }
+
+};
+
+typedef test_group<test_capigeospolygonize_data> group;
+typedef group::object object;
+
+group test_capigeospolygonize_group("capi::GEOSPolygonize");
+
+//
+// Test Cases
+//
+
+template<>
+template<>
+void object::test<1>
+()
+{
+    constexpr int size = 2;
+    GEOSGeometry* geoms[size] = { nullptr };
+
+    geoms[0] = GEOSGeomFromWKT("LINESTRING(1 3, 3 3, 3 1, 1 1, 1 3)");
+    geoms[1] = GEOSGeomFromWKT("LINESTRING(1 3, 3 3, 3 1, 1 1, 1 3)");
+
+    GEOSGeometry* g = GEOSPolygonizer_getCutEdges(geoms, size);
+
+    ensure(nullptr != g);
+    ensure_equals(GEOSGetNumGeometries(g), size);
+
+    GEOSGeom_destroy(g);
+
+    for(auto& input : geoms) {
+        GEOSGeom_destroy(input);
+    }
+}
+
+template<>
+template<>
+void object::test<2>
+()
+{
+    constexpr int size = 6;
+    GEOSGeometry* geoms[size] = { nullptr };
+
+    // Example from JTS Developer's Guide, Chapter 6 - Polygonization
+    geoms[0] = GEOSGeomFromWKT("LINESTRING(0 0, 10 10)"); // isolated edge
+    geoms[1] = GEOSGeomFromWKT("LINESTRING(185 221, 100 100)"); // dangling edge
+    geoms[2] = GEOSGeomFromWKT("LINESTRING(185 221, 88 275, 180 316)");
+    geoms[3] = GEOSGeomFromWKT("LINESTRING(185 221, 292 281, 180 316)");
+    geoms[4] = GEOSGeomFromWKT("LINESTRING(189 98, 83 187, 185 221)");
+    geoms[5] = GEOSGeomFromWKT("LINESTRING(189 98, 325 168, 185 221)");
+
+    GEOSGeometry* g = GEOSPolygonizer_getCutEdges(geoms, size);
+
+    ensure(nullptr != g);
+    ensure_equals(GEOSGetNumGeometries(g), 0);
+
+    GEOSGeom_destroy(g);
+
+    for(auto& input : geoms) {
+        GEOSGeom_destroy(input);
+    }
+}
+
+template<>
+template<>
+void object::test<3>
+()
+{
+    constexpr int size = 2;
+    GEOSGeometry* geoms[size];
+    geoms[0] = GEOSGeomFromWKT("LINESTRING (100 100, 100 300, 300 300, 300 100, 100 100)");
+    geoms[1] = GEOSGeomFromWKT("LINESTRING (150 150, 150 250, 250 250, 250 150, 150 150)");
+
+    // GEOSPolygonize gives us a collection of two polygons
+    GEOSGeometry* g = GEOSPolygonize(geoms, size);
+    ensure(nullptr != g);
+    ensure_equals(GEOSGetNumGeometries(g), 2);
+    ensure_equals(GEOSGeomTypeId(g), GEOS_GEOMETRYCOLLECTION);
+    GEOSGeom_destroy(g);
+
+    // GEOSPolygonize_valid gives us a single polygon with a hole
+    g = GEOSPolygonize_valid(geoms, 2);
+
+    ensure(nullptr != g);
+    ensure_equals(GEOSGetNumGeometries(g), 1);
+    ensure_equals(GEOSGeomTypeId(g), GEOS_POLYGON);
+    GEOSGeom_destroy(g);
+
+    for(auto& input : geoms) {
+        GEOSGeom_destroy(input);
+    }
+}
+
+template<>
+template<>
+void object::test<4>
+()
+{
+    constexpr int size = 1;
+    GEOSGeometry* geoms[size];
+    geoms[0] = GEOSGeomFromWKT("LINESTRING (0 0, 1 1)");
+
+    GEOSGeometry* g = GEOSPolygonize_valid(geoms, size);
+
+    ensure(nullptr != g);
+    ensure_equals(GEOSGetNumGeometries(g), 0);
+    ensure_equals(GEOSGeomTypeId(g), GEOS_GEOMETRYCOLLECTION);
+    GEOSGeom_destroy(g);
+
+    for(auto& input : geoms) {
+        GEOSGeom_destroy(input);
+    }
+}
+
+template<>
+template<>
+void object::test<5>
+()
+{
+    constexpr int size = 2;
+    GEOSGeometry* geoms[size];
+    geoms[0] = GEOSGeomFromWKT("LINESTRING (0 0, 1 0, 1 1, 0 1, 0 0)");
+    geoms[1] = GEOSGeomFromWKT("LINESTRING (1 1, 2 1, 2 2, 1 2, 1 1)");
+
+    GEOSGeometry* g = GEOSPolygonize_valid(geoms, size);
+
+    ensure(nullptr != g);
+    ensure_equals(GEOSGetNumGeometries(g), 2);
+    ensure_equals(GEOSGeomTypeId(g), GEOS_MULTIPOLYGON);
+    GEOSGeom_destroy(g);
+
+    for(auto& input : geoms) {
+        GEOSGeom_destroy(input);
+    }
+}
+
+} // namespace tut
+
diff --git a/tests/unit/capi/GEOSPolygonizer_getCutEdgesTest.cpp b/tests/unit/capi/GEOSPolygonizer_getCutEdgesTest.cpp
deleted file mode 100644
index f791b04..0000000
--- a/tests/unit/capi/GEOSPolygonizer_getCutEdgesTest.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-//
-// Test Suite for C-API GEOSPolygonizeGetCutEdges
-
-#include <tut/tut.hpp>
-// geos
-#include <geos_c.h>
-// std
-#include <cstdarg>
-#include <cstdio>
-#include <cstdlib>
-#include <memory>
-
-namespace tut {
-//
-// Test Group
-//
-
-// Common data used in test cases.
-struct test_capigeospolygonizegetcutedges_data {
-    static void
-    notice(const char* fmt, ...)
-    {
-        std::fprintf(stdout, "NOTICE: ");
-
-        va_list ap;
-        va_start(ap, fmt);
-        std::vfprintf(stdout, fmt, ap);
-        va_end(ap);
-
-        std::fprintf(stdout, "\n");
-    }
-
-    test_capigeospolygonizegetcutedges_data()
-    {
-        initGEOS(notice, notice);
-    }
-
-    ~test_capigeospolygonizegetcutedges_data()
-    {
-        finishGEOS();
-    }
-
-};
-
-typedef test_group<test_capigeospolygonizegetcutedges_data> group;
-typedef group::object object;
-
-group test_capigeospolygonizegetcutedges_group("capi::GEOSPolygonizeGetCutEdges");
-
-//
-// Test Cases
-//
-
-template<>
-template<>
-void object::test<1>
-()
-{
-    const int size = 2;
-    GEOSGeometry* geoms[size] = { nullptr };
-
-    geoms[0] = GEOSGeomFromWKT("LINESTRING(1 3, 3 3, 3 1, 1 1, 1 3)");
-    geoms[1] = GEOSGeomFromWKT("LINESTRING(1 3, 3 3, 3 1, 1 1, 1 3)");
-
-    GEOSGeometry* g = GEOSPolygonizer_getCutEdges(geoms, size);
-
-    ensure(nullptr != g);
-    ensure_equals(GEOSGetNumGeometries(g), size);
-
-    GEOSGeom_destroy(g);
-    GEOSGeom_destroy(geoms[0]);
-    GEOSGeom_destroy(geoms[1]);
-}
-
-template<>
-template<>
-void object::test<2>
-()
-{
-    const int size = 6;
-    GEOSGeometry* geoms[size] = { nullptr };
-
-    // Example from JTS Developer's Guide, Chapter 6 - Polygonization
-    geoms[0] = GEOSGeomFromWKT("LINESTRING(0 0, 10 10)"); // isolated edge
-    geoms[1] = GEOSGeomFromWKT("LINESTRING(185 221, 100 100)"); // dangling edge
-    geoms[2] = GEOSGeomFromWKT("LINESTRING(185 221, 88 275, 180 316)");
-    geoms[3] = GEOSGeomFromWKT("LINESTRING(185 221, 292 281, 180 316)");
-    geoms[4] = GEOSGeomFromWKT("LINESTRING(189 98, 83 187, 185 221)");
-    geoms[5] = GEOSGeomFromWKT("LINESTRING(189 98, 325 168, 185 221)");
-
-    GEOSGeometry* g = GEOSPolygonizer_getCutEdges(geoms, size);
-
-    ensure(nullptr != g);
-    ensure_equals(GEOSGetNumGeometries(g), 0);
-
-    GEOSGeom_destroy(g);
-
-    for(int i = 0; i < size; ++i) {
-        if(nullptr != geoms[i]) {
-            GEOSGeom_destroy(geoms[i]);
-        }
-    }
-}
-
-} // namespace tut
-

commit 548d51586033a243a573a7c26cb9bc7148b45b17
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu May 9 12:26:23 2019 -0400

    Simplify for loops in PolygonizeGraph

diff --git a/src/operation/polygonize/PolygonizeGraph.cpp b/src/operation/polygonize/PolygonizeGraph.cpp
index fb4abef..afb3f4f 100644
--- a/src/operation/polygonize/PolygonizeGraph.cpp
+++ b/src/operation/polygonize/PolygonizeGraph.cpp
@@ -45,10 +45,9 @@ namespace polygonize { // geos.operation.polygonize
 int
 PolygonizeGraph::getDegreeNonDeleted(Node* node)
 {
-    std::vector<DirectedEdge*>& edges = node->getOutEdges()->getEdges();
+    auto edges = node->getOutEdges()->getEdges();
     int degree = 0;
-    for(unsigned int i = 0; i < edges.size(); ++i) {
-        PolygonizeDirectedEdge* de = (PolygonizeDirectedEdge*)edges[i];
+    for(const auto& de : edges) {
         if(!de->isMarked()) {
             ++degree;
         }
@@ -59,11 +58,11 @@ PolygonizeGraph::getDegreeNonDeleted(Node* node)
 int
 PolygonizeGraph::getDegree(Node* node, long label)
 {
-    std::vector<DirectedEdge*>& edges = node->getOutEdges()->getEdges();
+    auto edges = node->getOutEdges()->getEdges();
     int degree = 0;
-    for(unsigned int i = 0; i < edges.size(); ++i) {
-        PolygonizeDirectedEdge* de = (PolygonizeDirectedEdge*)edges[i];
-        if(de->getLabel() == label) {
+    for(const auto& de : edges) {
+        auto pde = dynamic_cast<PolygonizeDirectedEdge*>(de);
+        if(pde->getLabel() == label) {
             ++degree;
         }
     }
@@ -76,11 +75,10 @@ PolygonizeGraph::getDegree(Node* node, long label)
 void
 PolygonizeGraph::deleteAllEdges(Node* node)
 {
-    std::vector<DirectedEdge*>& edges = node->getOutEdges()->getEdges();
-    for(unsigned int i = 0; i < edges.size(); ++i) {
-        PolygonizeDirectedEdge* de = (PolygonizeDirectedEdge*)edges[i];
+    auto edges = node->getOutEdges()->getEdges();
+    for(const auto& de : edges) {
         de->setMarked(true);
-        PolygonizeDirectedEdge* sym = (PolygonizeDirectedEdge*) de->getSym();
+        auto sym = de->getSym();
         if(sym != nullptr) {
             sym->setMarked(true);
         }
@@ -177,8 +175,7 @@ PolygonizeGraph::computeNextCWEdges()
     Nodes pns;
     getNodes(pns);
     // set the next pointers for the edges around each node
-    for(Nodes::size_type i = 0, in = pns.size(); i < in; ++i) {
-        Node* node = pns[i];
+    for(Node* node : pns) {
         computeNextCWEdges(node);
     }
 }
@@ -188,20 +185,13 @@ void
 PolygonizeGraph::convertMaximalToMinimalEdgeRings(
     std::vector<PolygonizeDirectedEdge*>& ringEdges)
 {
-    typedef std::vector<Node*> IntersectionNodes;
-    typedef std::vector<PolygonizeDirectedEdge*> RingEdges;
-
-    IntersectionNodes intNodes;
-    for(RingEdges::size_type i = 0, in = ringEdges.size();
-            i < in; ++i) {
-        PolygonizeDirectedEdge* de = ringEdges[i];
+    std::vector<Node*> intNodes;
+    for(PolygonizeDirectedEdge* de : ringEdges) {
         long p_label = de->getLabel();
         findIntersectionNodes(de, p_label, intNodes);
 
         // set the next pointers for the edges around each node
-        for(IntersectionNodes::size_type j = 0, jn = intNodes.size();
-                j < jn; ++j) {
-            Node* node = intNodes[j];
+        for(Node* node : intNodes) {
             computeNextCCWEdges(node, p_label);
         }
 
@@ -244,15 +234,15 @@ PolygonizeGraph::getEdgeRings(std::vector<EdgeRing*>& edgeRingList)
     maximalRings.clear(); // not needed anymore
 
     // find all edgerings
-    for(unsigned int i = 0; i < dirEdges.size(); ++i) {
-        PolygonizeDirectedEdge* de = (PolygonizeDirectedEdge*)dirEdges[i];
-        if(de->isMarked()) {
+    for(DirectedEdge* de : dirEdges) {
+        auto pde = dynamic_cast<PolygonizeDirectedEdge*>(de);
+        if(pde->isMarked()) {
             continue;
         }
-        if(de->isInRing()) {
+        if(pde->isInRing()) {
             continue;
         }
-        EdgeRing* er = findEdgeRing(de);
+        EdgeRing* er = findEdgeRing(pde);
         edgeRingList.push_back(er);
     }
 }
@@ -264,7 +254,7 @@ PolygonizeGraph::findLabeledEdgeRings(std::vector<DirectedEdge*>& dirEdges,
 {
     // label the edge rings formed
     long currLabel = 1;
-    for(const auto& de : dirEdges) {
+    for(DirectedEdge* de : dirEdges) {
         auto pde = dynamic_cast<PolygonizeDirectedEdge*>(de);
 
         if(pde->isMarked()) {
@@ -300,35 +290,21 @@ PolygonizeGraph::deleteCutEdges(std::vector<const LineString*>& cutLines)
      * Cut Edges are edges where both dirEdges have the same label.
      * Delete them, and record them
      */
-    for(DirEdges::size_type i = 0, in = dirEdges.size(); i < in; ++i) {
-        DirectedEdge* de_ = dirEdges[i];
-#ifdef GEOS_CAST_PARANOIA
-        assert(dynamic_cast<PolygonizeDirectedEdge*>(de_));
-#endif
-        PolygonizeDirectedEdge* de =
-            static_cast<PolygonizeDirectedEdge*>(de_);
+    for(DirectedEdge* de : dirEdges) {
+        auto pde = dynamic_cast<PolygonizeDirectedEdge*>(de);
 
         if(de->isMarked()) {
             continue;
         }
 
-        DirectedEdge* sym_ = de->getSym();
-#ifdef GEOS_CAST_PARANOIA
-        assert(dynamic_cast<PolygonizeDirectedEdge*>(sym_));
-#endif
-        PolygonizeDirectedEdge* sym =
-            static_cast<PolygonizeDirectedEdge*>(sym_);
+        auto sym = dynamic_cast<PolygonizeDirectedEdge*>(de->getSym());
 
-        if(de->getLabel() == sym->getLabel()) {
+        if(pde->getLabel() == sym->getLabel()) {
             de->setMarked(true);
             sym->setMarked(true);
 
             // save the line as a cut edge
-            Edge* e_ = de->getEdge();
-#ifdef GEOS_CAST_PARANOIA
-            assert(dynamic_cast<PolygonizeEdge*>(e_));
-#endif
-            PolygonizeEdge* e = static_cast<PolygonizeEdge*>(e_);
+            auto e = dynamic_cast<PolygonizeEdge*>(de->getEdge());
 
             cutLines.push_back(e->getLine());
         }
@@ -361,8 +337,8 @@ PolygonizeGraph::computeNextCWEdges(Node* node)
 
     // the edges are stored in CCW order around the star
     std::vector<DirectedEdge*>& pde = deStar->getEdges();
-    for(unsigned int i = 0; i < pde.size(); ++i) {
-        PolygonizeDirectedEdge* outDE = (PolygonizeDirectedEdge*)pde[i];
+    for(DirectedEdge* de : pde) {
+        auto outDE = dynamic_cast<PolygonizeDirectedEdge*>(de);
         if(outDE->isMarked()) {
             continue;
         }
@@ -370,13 +346,13 @@ PolygonizeGraph::computeNextCWEdges(Node* node)
             startDE = outDE;
         }
         if(prevDE != nullptr) {
-            PolygonizeDirectedEdge* sym = (PolygonizeDirectedEdge*) prevDE->getSym();
+            auto sym = dynamic_cast<PolygonizeDirectedEdge*>(prevDE->getSym());
             sym->setNext(outDE);
         }
         prevDE = outDE;
     }
     if(prevDE != nullptr) {
-        PolygonizeDirectedEdge* sym = (PolygonizeDirectedEdge*) prevDE->getSym();
+        auto sym = dynamic_cast<PolygonizeDirectedEdge*>(prevDE->getSym());
         sym->setNext(startDE);
     }
 }
@@ -397,13 +373,9 @@ PolygonizeGraph::computeNextCCWEdges(Node* node, long label)
     // the edges are stored in CCW order around the star
     std::vector<DirectedEdge*>& edges = deStar->getEdges();
 
-    /*
-     * Must use a SIGNED int here to allow for beak condition
-     * to be true.
-     */
     for(auto i = edges.size(); i > 0; --i) {
-        PolygonizeDirectedEdge* de = (PolygonizeDirectedEdge*)edges[i - 1];
-        PolygonizeDirectedEdge* sym = (PolygonizeDirectedEdge*) de->getSym();
+        PolygonizeDirectedEdge* de = dynamic_cast<PolygonizeDirectedEdge*>(edges[i - 1]);
+        PolygonizeDirectedEdge* sym = dynamic_cast<PolygonizeDirectedEdge*>(de->getSym());
         PolygonizeDirectedEdge* outDE = nullptr;
         if(de->getLabel() == label) {
             outDE = de;
@@ -465,17 +437,16 @@ PolygonizeGraph::deleteDangles(std::vector<const LineString*>& dangleLines)
         Node* node = nodeStack.back();
         nodeStack.pop_back();
         deleteAllEdges(node);
-        std::vector<DirectedEdge*>& nodeOutEdges = node->getOutEdges()->getEdges();
-        for(unsigned int j = 0; j < nodeOutEdges.size(); ++j) {
-            PolygonizeDirectedEdge* de = (PolygonizeDirectedEdge*)nodeOutEdges[j];
+        auto nodeOutEdges = node->getOutEdges()->getEdges();
+        for(DirectedEdge* de : nodeOutEdges) {
             // delete this edge and its sym
             de->setMarked(true);
-            PolygonizeDirectedEdge* sym = (PolygonizeDirectedEdge*) de->getSym();
+            auto sym = dynamic_cast<PolygonizeDirectedEdge*>(de->getSym());
             if(sym != nullptr) {
                 sym->setMarked(true);
             }
             // save the line as a dangle
-            PolygonizeEdge* e = (PolygonizeEdge*) de->getEdge();
+            auto e = dynamic_cast<PolygonizeEdge*>(de->getEdge());
             const LineString* ls = e->getLine();
             if(uniqueDangles.insert(ls).second) {
                 dangleLines.push_back(ls);

commit 6e98b88986e0c516c99eeab317683b3e67c58f94
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu May 9 12:25:56 2019 -0400

    Make Polygonizer::findValidRings static

diff --git a/include/geos/operation/polygonize/Polygonizer.h b/include/geos/operation/polygonize/Polygonizer.h
index dfbb87c..eddf0a4 100644
--- a/include/geos/operation/polygonize/Polygonizer.h
+++ b/include/geos/operation/polygonize/Polygonizer.h
@@ -106,9 +106,9 @@ private:
      */
     void polygonize();
 
-    void findValidRings(const std::vector<EdgeRing*>& edgeRingList,
-                        std::vector<EdgeRing*>& validEdgeRingList,
-                        std::vector<geom::LineString*>& invalidRingList);
+    static void findValidRings(const std::vector<EdgeRing*>& edgeRingList,
+                               std::vector<EdgeRing*>& validEdgeRingList,
+                               std::vector<geom::LineString*>& invalidRingList);
 
     void findShellsAndHoles(const std::vector<EdgeRing*>& edgeRingList);
 

commit 6aba335ce91576fc8925b60eca8ee665e800e2f4
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu May 9 11:04:02 2019 -0400

    Improve Polygonizer performance for cases with many potential holes
    
    JTS commit r607
    
    Closes #748

diff --git a/src/operation/polygonize/EdgeRing.cpp b/src/operation/polygonize/EdgeRing.cpp
index b88f71f..730f594 100644
--- a/src/operation/polygonize/EdgeRing.cpp
+++ b/src/operation/polygonize/EdgeRing.cpp
@@ -57,40 +57,31 @@ EdgeRing::findEdgeRingContaining(EdgeRing* testEr,
     }
     const Envelope* testEnv = testRing->getEnvelopeInternal();
     EdgeRing* minShell = nullptr;
-    const Envelope* minEnv = nullptr;
+    const Envelope* minShellEnv = nullptr;
 
     for(EdgeRing* tryShell : *shellList) {
-        LinearRing* tryRing = tryShell->getRingInternal();
-        const Envelope* tryEnv = tryRing->getEnvelopeInternal();
-        if(minShell != nullptr) {
-            minEnv = minShell->getRingInternal()->getEnvelopeInternal();
-        }
-        bool isContained = false;
-
+        auto tryShellRing = tryShell->getRingInternal();
+        auto tryShellEnv = tryShellRing->getEnvelopeInternal();
         // the hole envelope cannot equal the shell envelope
-
-        if(tryEnv->equals(testEnv)) {
+        // (also guards against testing rings against themselves)
+        if (tryShellEnv->equals(testEnv)) {
+            continue;
+        }
+        // hole must be contained in shell
+        if (!tryShellEnv->contains(testEnv)) {
             continue;
         }
 
-        const CoordinateSequence* tryCoords =
-            tryRing->getCoordinatesRO();
-
-        if(tryEnv->contains(testEnv)) {
-            // TODO: don't copy testPt !
-            Coordinate testPt = ptNotInList(testRing->getCoordinatesRO(), tryCoords);
-
-            if(PointLocation::isInRing(testPt, tryCoords)) {
-                isContained = true;
-            }
+        auto tryCoords = tryShellRing->getCoordinatesRO();
+        Coordinate testPt = ptNotInList(testRing->getCoordinatesRO(), tryCoords); // TODO: don't copy testPt !
 
-        }
+        bool isContained = PointLocation::isInRing(testPt, tryCoords);
 
-        // check if this new containing ring is smaller
-        // than the current minimum ring
+        // check if this new containing ring is smaller than the current minimum ring
         if(isContained) {
-            if(minShell == nullptr || minEnv->contains(tryEnv)) {
+            if(minShell == nullptr || minShellEnv->contains(tryShellEnv)) {
                 minShell = tryShell;
+                minShellEnv = minShell->getRingInternal()->getEnvelopeInternal();
             }
         }
     }

commit ff1cf7d6f9e44affc2e290050a907ae903474973
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu May 9 10:55:57 2019 -0400

    Remove some manual memory management

diff --git a/include/geos/operation/polygonize/EdgeRing.h b/include/geos/operation/polygonize/EdgeRing.h
index 4e744ba..f7edb17 100644
--- a/include/geos/operation/polygonize/EdgeRing.h
+++ b/include/geos/operation/polygonize/EdgeRing.h
@@ -23,6 +23,8 @@
 
 #include <geos/export.h>
 #include <geos/operation/polygonize/PolygonizeDirectedEdge.h>
+#include <geos/geom/Geometry.h>
+#include <geos/geom/LinearRing.h>
 #include <geos/geom/Polygon.h>
 
 #include <memory>
@@ -37,9 +39,7 @@
 namespace geos {
 namespace geom {
 class LineString;
-class LinearRing;
 class CoordinateSequence;
-class Geometry;
 class GeometryFactory;
 class Coordinate;
 }
@@ -64,11 +64,10 @@ private:
     DeList deList;
 
     // cache the following data for efficiency
-    geom::LinearRing* ring;
-    geom::CoordinateSequence* ringPts;
+    std::unique_ptr<geom::LinearRing> ring;
+    std::unique_ptr<geom::CoordinateSequence> ringPts;
 
-    typedef std::vector<geom::Geometry*> GeomVect;
-    GeomVect* holes;
+    std::unique_ptr<std::vector<geom::Geometry*>> holes;
 
     const EdgeRing* shell = nullptr;
     bool is_hole;
@@ -78,11 +77,11 @@ private:
 
     /** \brief
      * Computes the list of coordinates which are contained in this ring.
-     * The coordinatea are computed once only and cached.
+     * The coordinates are computed once only and cached.
      *
      * @return an array of the Coordinate in this ring
      */
-    geom::CoordinateSequence* getCoordinates();
+    const geom::CoordinateSequence* getCoordinates();
 
     static void addEdge(const geom::CoordinateSequence* coords,
                         bool isForward,
@@ -318,7 +317,7 @@ public:
      * is topologically invalid.
      * @return a LineString containing the coordinates in this ring
      */
-    geom::LineString* getLineString();
+    std::unique_ptr<geom::LineString> getLineString();
 
     /** \brief
      * Returns this ring as a LinearRing, or null if an Exception
@@ -336,7 +335,7 @@ public:
      * Details of problems are written to standard output.
      * Caller gets ownership of ring.
      */
-    geom::LinearRing* getRingOwnership();
+    std::unique_ptr<geom::LinearRing> getRingOwnership();
 };
 
 } // namespace geos::operation::polygonize
diff --git a/include/geos/operation/polygonize/Polygonizer.h b/include/geos/operation/polygonize/Polygonizer.h
index ec6993d..dfbb87c 100644
--- a/include/geos/operation/polygonize/Polygonizer.h
+++ b/include/geos/operation/polygonize/Polygonizer.h
@@ -14,7 +14,7 @@
  *
  **********************************************************************
  *
- * Last port: operation/polygonize/Polygonizer.java rev. 1.6 (JTS-1.10)
+ * Last port: operation/polygonize/Polygonizer.java rev. 974
  *
  **********************************************************************/
 
@@ -24,6 +24,7 @@
 #include <geos/export.h>
 #include <geos/geom/Polygon.h>
 #include <geos/geom/GeometryComponentFilter.h> // for LineStringAdder inheritance
+#include <geos/operation/polygonize/PolygonizeGraph.h>
 
 #include <memory>
 #include <vector>
@@ -43,7 +44,6 @@ class Polygon;
 namespace operation {
 namespace polygonize {
 class EdgeRing;
-class PolygonizeGraph;
 }
 }
 }
@@ -128,7 +128,7 @@ private:
 
 protected:
 
-    PolygonizeGraph* graph;
+    std::unique_ptr<PolygonizeGraph> graph;
 
     // initialize with empty collections, in case nothing is computed
     std::vector<const geom::LineString*> dangles;
diff --git a/src/operation/polygonize/EdgeRing.cpp b/src/operation/polygonize/EdgeRing.cpp
index b74b7d6..b88f71f 100644
--- a/src/operation/polygonize/EdgeRing.cpp
+++ b/src/operation/polygonize/EdgeRing.cpp
@@ -13,7 +13,7 @@
  *
  **********************************************************************
  *
- * Last port: operation/polygonize/EdgeRing.java rev. 109/138 (JTS-1.10)
+ * Last port: operation/polygonize/EdgeRing.java rev. 974
  *
  **********************************************************************/
 
@@ -56,13 +56,10 @@ EdgeRing::findEdgeRingContaining(EdgeRing* testEr,
         return nullptr;
     }
     const Envelope* testEnv = testRing->getEnvelopeInternal();
-    Coordinate testPt = testRing->getCoordinateN(0);
     EdgeRing* minShell = nullptr;
     const Envelope* minEnv = nullptr;
 
-    typedef std::vector<EdgeRing*> ERList;
-    for(ERList::size_type i = 0, e = shellList->size(); i < e; ++i) {
-        EdgeRing* tryShell = (*shellList)[i];
+    for(EdgeRing* tryShell : *shellList) {
         LinearRing* tryRing = tryShell->getRingInternal();
         const Envelope* tryEnv = tryRing->getEnvelopeInternal();
         if(minShell != nullptr) {
@@ -80,9 +77,8 @@ EdgeRing::findEdgeRingContaining(EdgeRing* testEr,
             tryRing->getCoordinatesRO();
 
         if(tryEnv->contains(testEnv)) {
-
             // TODO: don't copy testPt !
-            testPt = ptNotInList(testRing->getCoordinatesRO(), tryCoords);
+            Coordinate testPt = ptNotInList(testRing->getCoordinatesRO(), tryCoords);
 
             if(PointLocation::isInRing(testPt, tryCoords)) {
                 isContained = true;
@@ -163,13 +159,10 @@ EdgeRing::~EdgeRing()
     cerr << "[" << this << "] ~EdgeRing()" << endl;
 #endif // DEBUG_ALLOC
     if(holes) {
-        for(GeomVect::size_type i = 0, e = holes->size(); i < e; ++i) {
-            delete(*holes)[i];
+        for(auto& hole : *holes) {
+            delete hole;
         }
-        delete holes;
     }
-    delete ring;
-    delete ringPts;
 }
 
 void
@@ -202,7 +195,7 @@ void
 EdgeRing::addHole(LinearRing* hole)
 {
     if(holes == nullptr) {
-        holes = new vector<Geometry*>();
+        holes.reset(new vector<Geometry*>());
     }
     holes->push_back(hole);
 }
@@ -211,16 +204,14 @@ void
 EdgeRing::addHole(EdgeRing* holeER) {
     holeER->setShell(this);
     auto hole = holeER->getRingOwnership(); // TODO is this right method?
-    addHole(hole);
+    addHole(hole.release());
 }
 
 /*public*/
 std::unique_ptr<Polygon>
 EdgeRing::getPolygon()
 {
-    std::unique_ptr<Polygon> poly(factory->createPolygon(ring, holes));
-    ring = nullptr;
-    holes = nullptr;
+    std::unique_ptr<Polygon> poly(factory->createPolygon(ring.release(), holes.release()));
     return poly;
 }
 
@@ -235,28 +226,26 @@ EdgeRing::isValid()
 }
 
 /*private*/
-CoordinateSequence*
+const CoordinateSequence*
 EdgeRing::getCoordinates()
 {
     if(ringPts == nullptr) {
-        ringPts = factory->getCoordinateSequenceFactory()->create();
-        for(DeList::size_type i = 0, e = deList.size(); i < e; ++i) {
-            const DirectedEdge* de = deList[i];
-            assert(dynamic_cast<PolygonizeEdge*>(de->getEdge()));
-            PolygonizeEdge* edge = static_cast<PolygonizeEdge*>(de->getEdge());
+        ringPts.reset(factory->getCoordinateSequenceFactory()->create());
+        for(const auto& de : deList) {
+            auto edge = dynamic_cast<PolygonizeEdge*>(de->getEdge());
             addEdge(edge->getLine()->getCoordinatesRO(),
-                    de->getEdgeDirection(), ringPts);
+                    de->getEdgeDirection(), ringPts.get());
         }
     }
-    return ringPts;
+    return ringPts.get();
 }
 
 /*public*/
-LineString*
+std::unique_ptr<LineString>
 EdgeRing::getLineString()
 {
     getCoordinates();
-    return factory->createLineString(*ringPts);
+    return std::unique_ptr<LineString>(factory->createLineString(*ringPts));
 }
 
 /*public*/
@@ -264,12 +253,12 @@ LinearRing*
 EdgeRing::getRingInternal()
 {
     if(ring != nullptr) {
-        return ring;
+        return ring.get();
     }
 
     getCoordinates();
     try {
-        ring = factory->createLinearRing(*ringPts);
+        ring.reset(factory->createLinearRing(*ringPts));
     }
     catch(const geos::util::IllegalArgumentException& e) {
 #if GEOS_DEBUG
@@ -280,16 +269,15 @@ EdgeRing::getRingInternal()
 #endif
         ::geos::ignore_unused_variable_warning(e);
     }
-    return ring;
+    return ring.get();
 }
 
 /*public*/
-LinearRing*
+std::unique_ptr<LinearRing>
 EdgeRing::getRingOwnership()
 {
-    LinearRing* ret = getRingInternal();
-    ring = nullptr;
-    return ret;
+    getRingInternal(); // active lazy generation
+    return std::move(ring);
 }
 
 /*private*/
diff --git a/src/operation/polygonize/Polygonizer.cpp b/src/operation/polygonize/Polygonizer.cpp
index 55a698b..d670c46 100644
--- a/src/operation/polygonize/Polygonizer.cpp
+++ b/src/operation/polygonize/Polygonizer.cpp
@@ -14,7 +14,7 @@
  *
  **********************************************************************
  *
- * Last port: operation/polygonize/Polygonizer.java rev. 1.6 (JTS-1.10)
+ * Last port: operation/polygonize/Polygonizer.java rev. 974
  *
  **********************************************************************/
 
@@ -51,7 +51,7 @@ Polygonizer::LineStringAdder::LineStringAdder(Polygonizer* p):
 void
 Polygonizer::LineStringAdder::filter_ro(const Geometry* g)
 {
-    const LineString* ls = dynamic_cast<const LineString*>(g);
+    auto ls = dynamic_cast<const LineString*>(g);
     if(ls) {
         pol->add(ls);
     }
@@ -72,8 +72,6 @@ Polygonizer::Polygonizer(bool onlyPolygonal):
 
 Polygonizer::~Polygonizer()
 {
-    delete graph;
-
     for(auto& r : invalidRingLines) {
         delete r;
     }
@@ -149,7 +147,7 @@ Polygonizer::add(const LineString* line)
 {
     // create a new graph using the factory from the input Geometry
     if(graph == nullptr) {
-        graph = new PolygonizeGraph(line->getFactory());
+        graph.reset(new PolygonizeGraph(line->getFactory()));
     }
     graph->addEdge(line);
 }
@@ -248,9 +246,7 @@ Polygonizer::findValidRings(const vector<EdgeRing*>& edgeRingList,
             validEdgeRingList.push_back(er);
         }
         else {
-            // NOTE: polygonize::EdgeRing::getLineString
-            // returned LineString ownership is transferred.
-            invalidRingList.push_back(er->getLineString());
+            invalidRingList.push_back(er->getLineString().release());
         }
         GEOS_CHECK_FOR_INTERRUPTS();
     }

commit 1a557e9d49b0bb37b5a433deb53ae48fe14cf193
Author: Daniel Baston <dbaston at gmail.com>
Date:   Wed May 8 22:42:00 2019 -0400

    Add ability to retrieve only valid polygons from Polygonizer.
    
    Tests pass and valgrind clean. Not yet added to C API.
    
    References #727

diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index 80d4795..58c0cc0 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -3117,7 +3117,7 @@ extern "C" {
             handle->NOTICE_MESSAGE("geometry vector added to polygonizer");
 #endif
 
-            std::vector<Polygon*>* polys = plgnzr.getPolygons();
+            auto polys = plgnzr.getPolygons();
             assert(0 != polys);
 
 #if GEOS_DEBUG
@@ -3136,9 +3136,8 @@ extern "C" {
             std::vector<Geometry*>* polyvec = new std::vector<Geometry*>(polys->size());
 
             for(std::size_t i = 0; i < polys->size(); ++i) {
-                (*polyvec)[i] = (*polys)[i];
+                (*polyvec)[i] = (*polys)[i].release();
             }
-            delete polys;
             polys = 0;
 
             const GeometryFactory* gf = handle->geomFactory;
@@ -3344,12 +3343,11 @@ extern "C" {
                 *invalid = gf->createGeometryCollection(linevec);
             }
 
-            std::vector<Polygon*>* polys = plgnzr.getPolygons();
+            auto polys = plgnzr.getPolygons();
             std::vector<Geometry*>* polyvec = new std::vector<Geometry*>(polys->size());
             for(std::size_t i = 0; i < polys->size(); ++i) {
-                (*polyvec)[i] = (*polys)[i];
+                (*polyvec)[i] = (*polys)[i].release();
             }
-            delete polys;
 
             return gf->createGeometryCollection(polyvec);
 
diff --git a/doc/example.cpp b/doc/example.cpp
index 3756e32..081d711 100644
--- a/doc/example.cpp
+++ b/doc/example.cpp
@@ -1101,12 +1101,11 @@ do_all()
     /////////////////////////////////////////////
     Polygonizer plgnzr;
     plgnzr.add(geoms);
-    vector<Polygon*>* polys = plgnzr.getPolygons();
+    auto polys = plgnzr.getPolygons();
     newgeoms = new vector<Geometry*>;
     for(unsigned int i = 0; i < polys->size(); i++) {
-        newgeoms->push_back((*polys)[i]);
+        newgeoms->push_back((*polys)[i].release());
     }
-    delete polys;
 
     cout << endl << "----- HERE IS POLYGONIZE OUTPUT ------" << endl;
     wkt_print_geoms(newgeoms);
diff --git a/include/geos/operation/polygonize/EdgeRing.h b/include/geos/operation/polygonize/EdgeRing.h
index 42cc4e3..4e744ba 100644
--- a/include/geos/operation/polygonize/EdgeRing.h
+++ b/include/geos/operation/polygonize/EdgeRing.h
@@ -13,7 +13,7 @@
  *
  **********************************************************************
  *
- * Last port: operation/polygonize/EdgeRing.java rev. 109/138 (JTS-1.10)
+ * Last port: operation/polygonize/EdgeRing.java rev. 974
  *
  **********************************************************************/
 
@@ -22,7 +22,10 @@
 #define GEOS_OP_POLYGONIZE_EDGERING_H
 
 #include <geos/export.h>
+#include <geos/operation/polygonize/PolygonizeDirectedEdge.h>
+#include <geos/geom/Polygon.h>
 
+#include <memory>
 #include <vector>
 
 #ifdef _MSC_VER
@@ -35,7 +38,6 @@ namespace geos {
 namespace geom {
 class LineString;
 class LinearRing;
-class Polygon;
 class CoordinateSequence;
 class Geometry;
 class GeometryFactory;
@@ -58,7 +60,7 @@ class GEOS_DLL EdgeRing {
 private:
     const geom::GeometryFactory* factory;
 
-    typedef std::vector<const planargraph::DirectedEdge*> DeList;
+    typedef std::vector<const PolygonizeDirectedEdge*> DeList;
     DeList deList;
 
     // cache the following data for efficiency
@@ -68,6 +70,12 @@ private:
     typedef std::vector<geom::Geometry*> GeomVect;
     GeomVect* holes;
 
+    const EdgeRing* shell = nullptr;
+    bool is_hole;
+    bool is_processed = false;
+    bool is_included_set = false;
+    bool is_included = false;
+
     /** \brief
      * Computes the list of coordinates which are contained in this ring.
      * The coordinatea are computed once only and cached.
@@ -81,6 +89,13 @@ private:
                         geom::CoordinateSequence* coordList);
 
 public:
+    /** \brief
+     * Adds a DirectedEdge which is known to form part of this ring.
+     *
+     * @param de the DirectedEdge to add. Ownership to the caller.
+     */
+    void add(const PolygonizeDirectedEdge* de);
+
     /**
      * \brief
      * Find the innermost enclosing shell EdgeRing
@@ -105,6 +120,18 @@ public:
 
     /**
      * \brief
+     * Traverses a ring of DirectedEdges, accumulating them into a list.
+     *
+     * This assumes that all dangling directed edges have been removed from
+     * the graph, so that there is always a next dirEdge.
+     *
+     * @param startDE the DirectedEdge to start traversing at
+     * @return a vector of DirectedEdges that form a ring
+     */
+    static std::vector<PolygonizeDirectedEdge*> findDirEdgesInRing(PolygonizeDirectedEdge* startDE);
+
+    /**
+     * \brief
      * Finds a point in a list of points which is not contained in
      * another list of points.
      *
@@ -128,16 +155,12 @@ public:
     static bool isInList(const geom::Coordinate& pt,
                          const geom::CoordinateSequence* pts);
 
-    EdgeRing(const geom::GeometryFactory* newFactory);
+    explicit EdgeRing(const geom::GeometryFactory* newFactory);
 
     ~EdgeRing();
 
-    /** \brief
-     * Adds a DirectedEdge which is known to form part of this ring.
-     *
-     * @param de the DirectedEdge to add. Ownership to the caller.
-     */
-    void add(const planargraph::DirectedEdge* de);
+
+    void build(PolygonizeDirectedEdge* startDE);
 
     /** \brief
      * Tests whether this ring is a hole.
@@ -146,7 +169,121 @@ public:
      * a ring is a hole if it is oriented counter-clockwise.
      * @return <code>true</code> if this ring is a hole
      */
-    bool isHole();
+    void computeHole();
+
+    bool isHole() const {
+        return is_hole;
+    }
+
+    bool isIncludedSet() const {
+        return is_included_set;
+    }
+
+    bool isIncluded() const {
+        return is_included;
+    }
+
+    void setIncluded(bool included) {
+        is_included = included;
+        is_included_set = true;
+    }
+
+    bool isProcessed() const {
+        return is_processed;
+    }
+
+    void setProcessed(bool processed) {
+        is_processed = processed;
+    }
+
+    /** \brief
+     *  Sets the containing shell ring of a ring that has been determined to be a hole.
+     *
+     *  @param shell the shell ring
+     */
+     void setShell(const EdgeRing* shellRing) {
+        shell = shellRing;
+     }
+
+     /** \brief
+      * Tests whether this ring has a shell assigned to it.
+      *
+      * @return true if the ring has a shell
+      */
+     bool hasShell() const {
+         return shell != nullptr;
+     }
+
+     /** \brief
+      * Gets the shell for this ring. The shell is the ring itself if it is
+      * not a hole, otherwise it is the parent shell.
+      *
+      * @return the shell for the ring
+      */
+     const EdgeRing* getShell() const {
+         return isHole() ? shell : this;
+     }
+
+     /** \brief
+      * Tests whether this ring is an outer hole.
+      * A hole is an outer hole if it is not contained by any shell.
+      *
+      * @return true if the ring is an outer hole.
+      */
+      bool isOuterHole() const {
+          if (!isHole()) {
+              return false;
+          }
+
+          return !hasShell();
+      }
+
+      /** \brief
+       * Tests whether this ring is an outer shell.
+       *
+       * @return true if the ring is an outer shell.
+       */
+       bool isOuterShell() const {
+           return getOuterHole() != nullptr;
+       }
+
+        EdgeRing* getOuterHole() const {
+           if (isHole()) {
+               return nullptr;
+           }
+
+           // A shell is an outer shell if any edge is also in an outer hole.
+           // A hole is an outer shell if it is not contained by a shell.
+           for (auto& de : deList) {
+               auto adjRing = (dynamic_cast<PolygonizeDirectedEdge*>(de->getSym()))->getRing();
+               if (adjRing->isOuterHole()) {
+                   return adjRing;
+               }
+           }
+
+           return nullptr;
+       }
+
+       /** \brief
+        * Updates the included status for currently non-included shells
+        * based on whether they are adjacent to an included shell.
+        */
+        void updateIncluded() {
+            if (isHole()) {
+                return;
+            }
+
+            for (const auto& de : deList) {
+                auto adjShell = (dynamic_cast<const PolygonizeDirectedEdge*>(de->getSym()))->getRing()->getShell();
+
+                if (adjShell != nullptr && adjShell->isIncludedSet()) {
+                    // adjacent ring has been processed, so set included to inverse of adjacent included
+                    setIncluded(!adjShell->isIncluded());
+                    return;
+                }
+
+            }
+        }
 
     /** \brief
      * Adds a hole to the polygon formed by this ring.
@@ -155,6 +292,8 @@ public:
      */
     void addHole(geom::LinearRing* hole);
 
+    void addHole(EdgeRing* holeER);
+
     /** \brief
      * Computes the Polygon formed by this ring and any contained holes.
      *
@@ -163,7 +302,7 @@ public:
      *
      * @return the Polygon formed by this ring and its holes.
      */
-    geom::Polygon* getPolygon();
+    std::unique_ptr<geom::Polygon> getPolygon();
 
     /** \brief
      * Tests if the LinearRing ring formed by this edge ring
diff --git a/include/geos/operation/polygonize/PolygonizeDirectedEdge.h b/include/geos/operation/polygonize/PolygonizeDirectedEdge.h
index 82daef9..f6ae307 100644
--- a/include/geos/operation/polygonize/PolygonizeDirectedEdge.h
+++ b/include/geos/operation/polygonize/PolygonizeDirectedEdge.h
@@ -98,6 +98,13 @@ public:
     PolygonizeDirectedEdge* getNext() const;
 
     /*
+     * Gets the EdgeRing this edge is a member of.
+     */
+    EdgeRing* getRing() const {
+        return edgeRing;
+    }
+
+    /*
      * Sets the next directed edge in the EdgeRing that this
      * directed edge is a member of.
      */
diff --git a/include/geos/operation/polygonize/PolygonizeGraph.h b/include/geos/operation/polygonize/PolygonizeGraph.h
index a8ada5b..3e52b27 100644
--- a/include/geos/operation/polygonize/PolygonizeGraph.h
+++ b/include/geos/operation/polygonize/PolygonizeGraph.h
@@ -13,7 +13,7 @@
  *
  **********************************************************************
  *
- * Last port: operation/polygonize/PolygonizeGraph.java rev. 6/138 (JTS-1.10)
+ * Last port: operation/polygonize/PolygonizeGraph.java rev. 974
  *
  **********************************************************************/
 
@@ -80,7 +80,7 @@ public:
      * \brief
      * Create a new polygonization graph.
      */
-    PolygonizeGraph(const geom::GeometryFactory* newFactory);
+    explicit PolygonizeGraph(const geom::GeometryFactory* newFactory);
 
     /**
      * \brief
@@ -171,8 +171,8 @@ private:
     /**
      * Finds and labels all edgerings in the graph.
      *
-     * The edge rings are labelling with unique integers.
-     * The labelling allows detecting cut edges.
+     * The edge rings are labeling with unique integers.
+     * The labeling allows detecting cut edges.
      *
      * @param dirEdgesIn  a list of the DirectedEdges in the graph
      * @param dirEdgesOut each ring found will be pushed here
@@ -181,6 +181,7 @@ private:
         std::vector<planargraph::DirectedEdge*>& dirEdgesIn,
         std::vector<PolygonizeDirectedEdge*>& dirEdgesOut);
 
+    static void label(std::vector<PolygonizeDirectedEdge*>& dirEdges, long label);
     static void label(std::vector<planargraph::DirectedEdge*>& dirEdges, long label);
 
     static void computeNextCWEdges(planargraph::Node* node);
@@ -194,19 +195,6 @@ private:
      */
     static void computeNextCCWEdges(planargraph::Node* node, long label);
 
-    /**
-     * \brief
-     * Traverse a ring of DirectedEdges, accumulating them into a list.
-     * This assumes that all dangling directed edges have been removed
-     * from the graph, so that there is always a next dirEdge.
-     *
-     * @param startDE the DirectedEdge to start traversing at
-     * @param edgesInRing : the DirectedEdges that form a ring will
-     *                      be pushed here.
-     */
-    static void findDirEdgesInRing(PolygonizeDirectedEdge* startDE,
-                                   std::vector<planargraph::DirectedEdge*>& edgesInRing);
-
     EdgeRing* findEdgeRing(PolygonizeDirectedEdge* startDE);
 
     /* Tese are for memory management */
diff --git a/include/geos/operation/polygonize/Polygonizer.h b/include/geos/operation/polygonize/Polygonizer.h
index b38bdd2..ec6993d 100644
--- a/include/geos/operation/polygonize/Polygonizer.h
+++ b/include/geos/operation/polygonize/Polygonizer.h
@@ -22,8 +22,10 @@
 #define GEOS_OP_POLYGONIZE_POLYGONIZER_H
 
 #include <geos/export.h>
+#include <geos/geom/Polygon.h>
 #include <geos/geom/GeometryComponentFilter.h> // for LineStringAdder inheritance
 
+#include <memory>
 #include <vector>
 
 #ifdef _MSC_VER
@@ -54,21 +56,27 @@ namespace polygonize { // geos::operation::polygonize
  * Polygonizes a set of Geometrys which contain linework that
  * represents the edges of a planar graph.
  *
- * Any dimension of Geometry is handled - the constituent linework is extracted
- * to form the edges.
+ * All types of Geometry are accepted as input; the constituent linework is extracted
+ * as the edges to be polygonized.
  * The edges must be correctly noded; that is, they must only meet
- * at their endpoints.  The Polygonizer will still run on incorrectly noded input
- * but will not form polygons from incorrected noded edges.
+ * at their endpoints. Polygonization will accept incorrectly noded input but will
+ * not form polygons from non-noded edges, and reports them as errors.
  *
  * The Polygonizer reports the follow kinds of errors:
  *
  * - <b>Dangles</b> - edges which have one or both ends which are
  *   not incident on another edge endpoint
  * - <b>Cut Edges</b> - edges which are connected at both ends but
- *   which do not form part of polygon
+ *   which do not form part of a polygon
  * - <b>Invalid Ring Lines</b> - edges which form rings which are invalid
  *   (e.g. the component lines contain a self-intersection)
  *
+ *   The Polygonizer constructor allows extracting only polygons which form a
+ *   valid polygonal result.
+ *   The set of extracted polygons is guaranteed to be edge-disjoint.
+ *   This is useful when it is known that the input lines form a valid
+ *   polygonal geometry (which may include holes or nested polygons).
+ *
  */
 class GEOS_DLL Polygonizer {
 private:
@@ -78,7 +86,7 @@ private:
     class GEOS_DLL LineStringAdder: public geom::GeometryComponentFilter {
     public:
         Polygonizer* pol;
-        LineStringAdder(Polygonizer* p);
+        explicit LineStringAdder(Polygonizer* p);
         //void filter_rw(geom::Geometry *g);
         void filter_ro(const geom::Geometry* g) override;
     };
@@ -110,6 +118,14 @@ private:
     static void assignHoleToShell(EdgeRing* holeER,
                                   std::vector<EdgeRing*>& shellList);
 
+    static void findDisjointShells(std::vector<EdgeRing*>& shellList);
+
+    static void findOuterShells(std::vector<EdgeRing*>& shellList);
+
+    static std::unique_ptr<std::vector<std::unique_ptr<geom::Polygon>>> extractPolygons(std::vector<EdgeRing*> & shellList, bool includeAll);
+
+    bool extractOnlyPolygonal;
+
 protected:
 
     PolygonizeGraph* graph;
@@ -121,15 +137,17 @@ protected:
 
     std::vector<EdgeRing*> holeList;
     std::vector<EdgeRing*> shellList;
-    std::vector<geom::Polygon*>* polyList;
+    std::unique_ptr<std::vector<std::unique_ptr<geom::Polygon>>> polyList;
 
 public:
 
     /** \brief
-     * Create a polygonizer with the same GeometryFactory
-     * as the input Geometry
+     * Create a Polygonizer with the same GeometryFactory
+     * as the input Geometrys.
+     *
+     * @param onlyPolygonal true if only polygons which form a valid polygonal geometry should be extracted
      */
-    Polygonizer();
+    explicit Polygonizer(bool onlyPolygonal = false);
 
     ~Polygonizer();
 
@@ -180,7 +198,7 @@ public:
      * calls will return NULL.
      * @return a collection of Polygons
      */
-    std::vector<geom::Polygon*>* getPolygons();
+    std::unique_ptr<std::vector<std::unique_ptr<geom::Polygon>>> getPolygons();
 
     /** \brief
      * Get the list of dangling lines found during polygonization.
@@ -191,7 +209,6 @@ public:
      */
     const std::vector<const geom::LineString*>& getDangles();
 
-
     /** \brief
      * Get the list of cut edges found during polygonization.
      *
diff --git a/src/operation/polygonize/BuildArea.cpp b/src/operation/polygonize/BuildArea.cpp
index 5b61e4b..ec39757 100644
--- a/src/operation/polygonize/BuildArea.cpp
+++ b/src/operation/polygonize/BuildArea.cpp
@@ -141,24 +141,20 @@ static void dumpGeometry(const geom::Geometry* geom)
 unique_ptr<geom::Geometry> BuildArea::build(const geom::Geometry* geom) {
     Polygonizer polygonizer;
     polygonizer.add(geom);
-    std::vector<geom::Polygon*>* polys = polygonizer.getPolygons();
-    if( !polys )
-        return nullptr;
+    auto polys = polygonizer.getPolygons();
 
     // No geometries in collection, early out
     if( polys->empty() ) {
         auto emptyGeomCollection = unique_ptr<geom::Geometry>(
             GeometryFactory::create()->createGeometryCollection());
         emptyGeomCollection->setSRID(geom->getSRID());
-        delete polys;
         return emptyGeomCollection;
     }
 
     // Return first geometry if we only have one in collection
     if( polys->size() == 1 ) {
-        auto ret = unique_ptr<geom::Geometry>((*polys)[0]);
+        auto ret = std::unique_ptr<geom::Geometry>((*polys)[0].release());
         ret->setSRID(geom->getSRID());
-        delete polys;
         return ret;
     }
 
@@ -190,54 +186,37 @@ unique_ptr<geom::Geometry> BuildArea::build(const geom::Geometry* geom) {
     *
     */
 
-    try {
-        /* Prepare face structures for later analysis */
-        std::vector<std::unique_ptr<Face>> faces;
-        for(auto poly: *polys) {
-            faces.emplace_back(newFace(poly));
-        }
+    /* Prepare face structures for later analysis */
+    std::vector<std::unique_ptr<Face>> faces;
+    for(auto& poly: *polys) {
+        faces.emplace_back(newFace(poly.get()));
+    }
 
-        /* Find faces representing other faces holes */
-        findFaceHoles(faces);
+    /* Find faces representing other faces holes */
+    findFaceHoles(faces);
 
-        /* Build a MultiPolygon composed only by faces with an
-        * even number of ancestors */
-        auto tmp = collectFacesWithEvenAncestors(faces);
-
-        for(auto poly: *polys) {
-            delete poly;
-        }
-        delete polys;
-        polys = nullptr;
+    /* Build a MultiPolygon composed only by faces with an
+    * even number of ancestors */
+    auto tmp = collectFacesWithEvenAncestors(faces);
 
 #ifdef DUMP_GEOM
-        std::cerr << "after collectFacesWithEvenAncestors:" << std::endl;
-        dumpGeometry(tmp.get());
+    std::cerr << "after collectFacesWithEvenAncestors:" << std::endl;
+    dumpGeometry(tmp.get());
 #endif
 
-        /* Run a single overlay operation to dissolve shared edges */
-        auto shp = std::unique_ptr<geom::Geometry>(
-            geos::operation::geounion::CascadedPolygonUnion::CascadedPolygonUnion::Union(tmp.get()));
-        if( shp ) {
-            shp->setSRID(geom->getSRID());
-        }
+    /* Run a single overlay operation to dissolve shared edges */
+    auto shp = std::unique_ptr<geom::Geometry>(
+        geos::operation::geounion::CascadedPolygonUnion::CascadedPolygonUnion::Union(tmp.get()));
+    if( shp ) {
+        shp->setSRID(geom->getSRID());
+    }
 
 #ifdef DUMP_GEOM
-        std::cerr << "after CascadedPolygonUnion:" << std::endl;
-        dumpGeometry(shp.get());
+    std::cerr << "after CascadedPolygonUnion:" << std::endl;
+    dumpGeometry(shp.get());
 #endif
 
-        return shp;
-    }
-    catch( ... ) {
-        if( polys ) {
-            for(auto poly: *polys) {
-                delete poly;
-            }
-            delete polys;
-        }
-        throw;
-    }
+    return shp;
 }
 
 } // namespace geos.operation.polygonize
diff --git a/src/operation/polygonize/EdgeRing.cpp b/src/operation/polygonize/EdgeRing.cpp
index f81e5f1..b74b7d6 100644
--- a/src/operation/polygonize/EdgeRing.cpp
+++ b/src/operation/polygonize/EdgeRing.cpp
@@ -101,6 +101,19 @@ EdgeRing::findEdgeRingContaining(EdgeRing* testEr,
     return minShell;
 }
 
+std::vector<PolygonizeDirectedEdge*>
+EdgeRing::findDirEdgesInRing(PolygonizeDirectedEdge* startDE) {
+    auto de = startDE;
+    std::vector<decltype(de)> edges;
+
+    do {
+        edges.push_back(de);
+        de = de->getNext();
+    } while (de != startDE);
+
+    return edges;
+}
+
 /*public static*/
 const Coordinate&
 EdgeRing::ptNotInList(const CoordinateSequence* testPts,
@@ -136,7 +149,8 @@ EdgeRing::EdgeRing(const GeometryFactory* newFactory)
     factory(newFactory),
     ring(nullptr),
     ringPts(nullptr),
-    holes(nullptr)
+    holes(nullptr),
+    is_hole(false)
 {
 #ifdef DEBUG_ALLOC
     cerr << "[" << this << "] EdgeRing(factory)" << endl;
@@ -158,19 +172,29 @@ EdgeRing::~EdgeRing()
     delete ringPts;
 }
 
+void
+EdgeRing::build(PolygonizeDirectedEdge* startDE) {
+    auto de = startDE;
+    do {
+        add(de);
+        de->setRing(this);
+        de = de->getNext();
+    } while (de != startDE);
+}
+
 /*public*/
 void
-EdgeRing::add(const DirectedEdge* de)
+EdgeRing::add(const PolygonizeDirectedEdge* de)
 {
     deList.push_back(de);
 }
 
 /*public*/
-bool
-EdgeRing::isHole()
+void
+EdgeRing::computeHole()
 {
     getRingInternal();
-    return Orientation::isCCW(ring->getCoordinatesRO());
+    is_hole = Orientation::isCCW(ring->getCoordinatesRO());
 }
 
 /*public*/
@@ -183,11 +207,18 @@ EdgeRing::addHole(LinearRing* hole)
     holes->push_back(hole);
 }
 
+void
+EdgeRing::addHole(EdgeRing* holeER) {
+    holeER->setShell(this);
+    auto hole = holeER->getRingOwnership(); // TODO is this right method?
+    addHole(hole);
+}
+
 /*public*/
-Polygon*
+std::unique_ptr<Polygon>
 EdgeRing::getPolygon()
 {
-    Polygon* poly = factory->createPolygon(ring, holes);
+    std::unique_ptr<Polygon> poly(factory->createPolygon(ring, holes));
     ring = nullptr;
     holes = nullptr;
     return poly;
diff --git a/src/operation/polygonize/PolygonizeGraph.cpp b/src/operation/polygonize/PolygonizeGraph.cpp
index 63df2d9..fb4abef 100644
--- a/src/operation/polygonize/PolygonizeGraph.cpp
+++ b/src/operation/polygonize/PolygonizeGraph.cpp
@@ -262,28 +262,20 @@ void
 PolygonizeGraph::findLabeledEdgeRings(std::vector<DirectedEdge*>& dirEdges,
                                       std::vector<PolygonizeDirectedEdge*>& edgeRingStarts)
 {
-    typedef std::vector<DirectedEdge*> Edges;
-
-    Edges edges;
-
     // label the edge rings formed
     long currLabel = 1;
-    for(Edges::size_type i = 0, n = dirEdges.size(); i < n; ++i) {
-#ifdef GEOS_CAST_PARANOIA
-        assert(dynamic_cast<PolygonizeDirectedEdge*>(dirEdges[i]));
-#endif
-        PolygonizeDirectedEdge* de =
-            static_cast<PolygonizeDirectedEdge*>(dirEdges[i]);
+    for(const auto& de : dirEdges) {
+        auto pde = dynamic_cast<PolygonizeDirectedEdge*>(de);
 
-        if(de->isMarked()) {
+        if(pde->isMarked()) {
             continue;
         }
-        if(de->getLabel() >= 0) {
+        if(pde->getLabel() >= 0) {
             continue;
         }
-        edgeRingStarts.push_back(de);
+        edgeRingStarts.push_back(pde);
 
-        findDirEdgesInRing(de, edges);
+        auto edges = EdgeRing::findDirEdgesInRing(pde);
         label(edges, currLabel);
         edges.clear();
 
@@ -344,11 +336,19 @@ PolygonizeGraph::deleteCutEdges(std::vector<const LineString*>& cutLines)
 }
 
 void
+PolygonizeGraph::label(std::vector<PolygonizeDirectedEdge*>& dirEdges, long label)
+{
+    for (auto & pde: dirEdges) {
+        pde->setLabel(label);
+    }
+}
+
+void
 PolygonizeGraph::label(std::vector<DirectedEdge*>& dirEdges, long label)
 {
-    for(unsigned int i = 0; i < dirEdges.size(); ++i) {
-        PolygonizeDirectedEdge* de = (PolygonizeDirectedEdge*)dirEdges[i];
-        de->setLabel(label);
+    for(auto& de : dirEdges) {
+        auto pde = dynamic_cast<PolygonizeDirectedEdge*>(de);
+        pde->setLabel(label);
     }
 }
 
@@ -434,21 +434,6 @@ PolygonizeGraph::computeNextCCWEdges(Node* node, long label)
     }
 }
 
-/* static private */
-void
-PolygonizeGraph::findDirEdgesInRing(PolygonizeDirectedEdge* startDE,
-                                    std::vector<DirectedEdge*>& edges)
-{
-    PolygonizeDirectedEdge* de = startDE;
-    do {
-        edges.push_back(de);
-        de = de->getNext();
-        assert(de != nullptr); // found NULL DE in ring
-        assert(de == startDE || !de->isInRing()); // found DE already in ring
-    }
-    while(de != startDE);
-}
-
 EdgeRing*
 PolygonizeGraph::findEdgeRing(PolygonizeDirectedEdge* startDE)
 {
diff --git a/src/operation/polygonize/Polygonizer.cpp b/src/operation/polygonize/Polygonizer.cpp
index 1b84e57..55a698b 100644
--- a/src/operation/polygonize/Polygonizer.cpp
+++ b/src/operation/polygonize/Polygonizer.cpp
@@ -57,13 +57,9 @@ Polygonizer::LineStringAdder::filter_ro(const Geometry* g)
     }
 }
 
-
-/*
- * Create a polygonizer with the same GeometryFactory
- * as the input Geometry
- */
-Polygonizer::Polygonizer():
+Polygonizer::Polygonizer(bool onlyPolygonal):
     lineStringAdder(this),
+    extractOnlyPolygonal(onlyPolygonal),
     graph(nullptr),
     dangles(),
     cutEdges(),
@@ -81,13 +77,6 @@ Polygonizer::~Polygonizer()
     for(auto& r : invalidRingLines) {
         delete r;
     }
-
-    if(polyList) {
-        for(auto& p : (*polyList)) {
-            delete p;
-        }
-        delete polyList;
-    }
 }
 
 /*
@@ -169,13 +158,11 @@ Polygonizer::add(const LineString* line)
  * Gets the list of polygons formed by the polygonization.
  * @return a collection of Polygons
  */
-vector<Polygon*>*
+unique_ptr<vector<unique_ptr<Polygon>>>
 Polygonizer::getPolygons()
 {
     polygonize();
-    vector<Polygon*>* ret = polyList;
-    polyList = nullptr;
-    return ret;
+    return std::move(polyList);
 }
 
 /* public */
@@ -211,10 +198,9 @@ Polygonizer::polygonize()
         return;
     }
 
-    polyList = new vector<Polygon*>();
-
     // if no geometries were supplied it's possible graph could be null
     if(graph == nullptr) {
+        polyList.reset(new std::vector<std::unique_ptr<Polygon>>());
         return;
     }
 
@@ -243,9 +229,12 @@ Polygonizer::polygonize()
 
     assignHolesToShells(holeList, shellList);
 
-    for(const auto& er : shellList) {
-        polyList->push_back(er->getPolygon());
+    bool includeAll = true;
+    if (extractOnlyPolygonal) {
+        findDisjointShells(shellList);
+        includeAll = false;
     }
+    polyList = extractPolygons(shellList, includeAll);
 }
 
 /* private */
@@ -273,7 +262,8 @@ Polygonizer::findShellsAndHoles(const vector<EdgeRing*>& edgeRingList)
 {
     holeList.clear();
     shellList.clear();
-    for(const auto& er : edgeRingList) {
+    for(auto& er : edgeRingList) {
+        er->computeHole();
         if(er->isHole()) {
             holeList.push_back(er);
         }
@@ -303,10 +293,54 @@ Polygonizer::assignHoleToShell(EdgeRing* holeER,
     EdgeRing* shell = EdgeRing::findEdgeRingContaining(holeER, &shellList);
 
     if(shell != nullptr) {
-        shell->addHole(holeER->getRingOwnership());
+        shell->addHole(holeER);
+    }
+}
+
+void
+Polygonizer::findDisjointShells(vector<EdgeRing*> & shells)
+{
+    findOuterShells(shells);
+
+    bool isMoreToScan;
+    do {
+        isMoreToScan = false;
+        for (EdgeRing* er : shells) {
+            if (er->isIncludedSet()) {
+                continue;
+            }
+            er->updateIncluded();
+            if (!er->isIncludedSet()) {
+                isMoreToScan = true;
+            }
+        }
+    } while (isMoreToScan);
+}
+
+void
+Polygonizer::findOuterShells(vector<EdgeRing*> & shells)
+{
+    for (EdgeRing* er : shells) {
+        auto outerHoleER = er->getOuterHole();
+        if (outerHoleER != nullptr && !outerHoleER->isProcessed()) {
+            er->setIncluded(true);
+            outerHoleER->setProcessed(true);
+        }
     }
 }
 
+std::unique_ptr<std::vector<std::unique_ptr<Polygon>>>
+Polygonizer::extractPolygons(vector<EdgeRing*> & shells, bool includeAll)
+{
+    std::unique_ptr<std::vector<std::unique_ptr<Polygon>>> polys(new std::vector<std::unique_ptr<Polygon>>);
+    for (EdgeRing* er : shells) {
+        if (includeAll || er->isIncluded()) {
+            polys->emplace_back(er->getPolygon());
+        }
+    }
+
+    return polys;
+}
 
 } // namespace geos.operation.polygonize
 } // namespace geos.operation
diff --git a/tests/unit/operation/polygonize/PolygonizeTest.cpp b/tests/unit/operation/polygonize/PolygonizeTest.cpp
index 6528bc9..065722c 100644
--- a/tests/unit/operation/polygonize/PolygonizeTest.cpp
+++ b/tests/unit/operation/polygonize/PolygonizeTest.cpp
@@ -59,26 +59,12 @@ struct test_polygonizetest_data {
         }
     }
 
-    GeomPtr
-    readWKT(const std::string& inputWKT)
-    {
-        return GeomPtr(wktreader.read(inputWKT));
-    }
-
-    void
-    readWKT(const char* const* inputWKT, std::vector<Geom*>& geoms)
-    {
-        for(const char* const* ptr = inputWKT; *ptr; ++ptr) {
-            geoms.push_back(readWKT(*ptr).release());
-        }
-    }
-
     template <class T>
     bool
     contains(T& cnt, const Geom* g)
     {
         for(typename T::iterator i = cnt.begin(), e = cnt.end(); i != e; ++i) {
-            const Geom* element = *i;
+            const auto& element = *i;
             if(element->equalsExact(g)) {
                 return true;
             }
@@ -111,21 +97,31 @@ struct test_polygonizetest_data {
     }
 
     bool
-    doTest(const char* const* inputWKT, const char* const* expectWKT)
+    doTest(const std::vector<std::string> & inputWKT, const std::vector<std::string> & expectWKT, bool onlyPolygonal)
     {
         using std::cout;
         using std::endl;
 
         std::vector<Geom*> inputGeoms, expectGeoms;
 
-        readWKT(inputWKT, inputGeoms);
-        readWKT(expectWKT, expectGeoms);
+        for (const auto& wkt : inputWKT) {
+            inputGeoms.push_back(wktreader.read(wkt));
+        }
 
-        Polygonizer polygonizer;
+        for (const auto& wkt : expectWKT) {
+            auto g = wktreader.read(wkt);
+            g->normalize();
+            expectGeoms.push_back(g);
+        }
+
+        Polygonizer polygonizer(onlyPolygonal);
         polygonizer.add(&inputGeoms);
 
-        std::unique_ptr< std::vector<Poly*> > retGeoms;
-        retGeoms.reset(polygonizer.getPolygons());
+        std::unique_ptr<std::vector<std::unique_ptr<Poly>> > retGeoms;
+        retGeoms = polygonizer.getPolygons();
+        for (const auto& g : *retGeoms) {
+            g->normalize();
+        }
 
         delAll(inputGeoms);
 
@@ -139,7 +135,6 @@ struct test_polygonizetest_data {
         }
 
         delAll(expectGeoms);
-        delAll(*retGeoms);
 
         return ok;
     }
@@ -157,17 +152,14 @@ template<>
 void object::test<1>
 ()
 {
-    static char const* const inp[] = {
+    std::vector<std::string> inp {
         "LINESTRING EMPTY",
         "LINESTRING EMPTY",
-        nullptr
     };
 
-    static char const* const exp[] = {
-        nullptr
-    };
+    std::vector<std::string> exp{};
 
-    doTest(inp, exp);
+    doTest(inp, exp, false);
 }
 
 // test2() in JTS
@@ -176,19 +168,161 @@ template<>
 void object::test<2>
 ()
 {
-    static char const* const inp[] = {
+    std::vector<std::string> inp{
         "LINESTRING (100 180, 20 20, 160 20, 100 180)",
         "LINESTRING (100 180, 80 60, 120 60, 100 180)",
-        nullptr
     };
 
-    static char const* const exp[] = {
+    std::vector<std::string> exp{
         "POLYGON ((100 180, 120 60, 80 60, 100 180))",
-        "POLYGON ((100 180, 160 20, 20 20, 100 180), (100 180, 80 60, 120 60, 100 180))",
-        nullptr
+        "POLYGON ((100 180, 160 20, 20 20, 100 180), (100 180, 80 60, 120 60, 100 180))"
+    };
+
+    doTest(inp, exp, false);
+}
+
+// JTS test3
+template<>
+template<>
+void object::test<3>()
+{
+    std::vector<std::string> inp{
+        "LINESTRING (0 0, 4 0)",
+        "LINESTRING (4 0, 5 3)",
+        "LINESTRING (5 3, 4 6, 6 6, 5 3)",
+        "LINESTRING (5 3, 6 0)",
+        "LINESTRING (6 0, 10 0, 5 10, 0 0)",
+        "LINESTRING (4 0, 6 0)"
+    };
+
+    std::vector<std::string> exp{
+        "POLYGON ((5 3, 4 0, 0 0, 5 10, 10 0, 6 0, 5 3), (5 3, 6 6, 4 6, 5 3))",
+        "POLYGON ((5 3, 4 6, 6 6, 5 3))",
+        "POLYGON ((4 0, 5 3, 6 0, 4 0))"
+    };
+
+    doTest(inp, exp, false);
+}
+
+// JTS testPolygonal1
+template<>
+template<>
+void object::test<4>()
+{
+    std::vector<std::string> inp{
+            "LINESTRING (100 100, 100 300, 300 300, 300 100, 100 100)",
+            "LINESTRING (150 150, 150 250, 250 250, 250 150, 150 150)"
+    };
+
+    std::vector<std::string> exp{
+            "POLYGON ((100 100, 100 300, 300 300, 300 100, 100 100), (150 150, 150 250, 250 250, 250 150, 150 150))"
+    };
+
+    doTest(inp, exp, true);
+}
+
+// JTS testPolygonal2
+template<>
+template<>
+void object::test<5>()
+{
+    std::vector<std::string> inp{
+            "LINESTRING (100 100, 100 0, 0 0, 0 100, 100 100)",
+            "LINESTRING (10 10, 10 30, 20 30)",
+            "LINESTRING (20 30, 30 30, 30 20)",
+            "LINESTRING (30 20, 30 10, 10 10)",
+            "LINESTRING (40 40, 40 20, 30 20)",
+            "LINESTRING (30 20, 20 20, 20 30)",
+            "LINESTRING (20 30, 20 40, 40 40))"
+    };
+
+    std::vector<std::string> exp{
+            "POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0), (10 10, 30 10, 30 20, 40 20, 40 40, 20 40, 20 30, 10 30, 10 10))",
+            "POLYGON ((20 20, 20 30, 30 30, 30 20, 20 20))"
+    };
+
+    doTest(inp, exp, true);
+}
+
+// JTS testPolygonal_OuterOnly_1
+template<>
+template<>
+void object::test<6>()
+{
+    std::vector<std::string> inp{
+            "LINESTRING (10 10, 10 20, 20 20)",
+            "LINESTRING (20 20, 20 10)",
+            "LINESTRING (20 10, 10 10)",
+            "LINESTRING (20 20, 30 20, 30 10, 20 10)"
+    };
+
+    std::vector<std::string> exp{
+            "POLYGON ((20 20, 20 10, 10 10, 10 20, 20 20))"
+    };
+
+    doTest(inp, exp, true);
+}
+
+// JTS testPolygonal_OuterOnly_2
+template<>
+template<>
+void object::test<7>()
+{
+    std::vector<std::string> inp{
+            "LINESTRING (100 400, 200 400, 200 300)",
+            "LINESTRING (200 300, 150 300)",
+            "LINESTRING (150 300, 100 300, 100 400)",
+            "LINESTRING (200 300, 250 300, 250 200)",
+            "LINESTRING (250 200, 200 200)",
+            "LINESTRING (200 200, 150 200, 150 300)",
+            "LINESTRING (250 200, 300 200, 300 100, 200 100, 200 200)"
+    };
+
+    std::vector<std::string> exp{
+         "POLYGON ((150 300, 100 300, 100 400, 200 400, 200 300, 150 300))",
+         "POLYGON ((200 200, 250 200, 300 200, 300 100, 200 100, 200 200))"
+    };
+
+    doTest(inp, exp, true);
+}
+
+// JTS testPolygonal_OuterOnly_Checkerboard
+template<>
+template<>
+void object::test<8>()
+{
+    std::vector<std::string> inp{
+            "LINESTRING (10 20, 20 20)",
+            "LINESTRING (10 20, 10 30)",
+            "LINESTRING (20 10, 10 10, 10 20)",
+            "LINESTRING (10 30, 20 30)",
+            "LINESTRING (10 30, 10 40, 20 40)",
+            "LINESTRING (30 10, 20 10)",
+            "LINESTRING (20 20, 20 10)",
+            "LINESTRING (20 20, 30 20)",
+            "LINESTRING (20 30, 20 20)",
+            "LINESTRING (20 30, 30 30)",
+            "LINESTRING (20 40, 20 30)",
+            "LINESTRING (20 40, 30 40)",
+            "LINESTRING (40 20, 40 10, 30 10)",
+            "LINESTRING (30 20, 30 10)",
+            "LINESTRING (30 20, 40 20)",
+            "LINESTRING (30 30, 30 20)",
+            "LINESTRING (30 30, 40 30)",
+            "LINESTRING (30 40, 30 30)",
+            "LINESTRING (30 40, 40 40, 40 30)",
+            "LINESTRING (40 30, 40 20)"
+    };
+
+    std::vector<std::string> exp{
+            "POLYGON ((10 20, 20 20, 20 10, 10 10, 10 20))",
+            "POLYGON ((20 30, 10 30, 10 40, 20 40, 20 30))",
+            "POLYGON ((30 20, 20 20, 20 30, 30 30, 30 20))",
+            "POLYGON ((30 10, 30 20, 40 20, 40 10, 30 10))",
+            "POLYGON ((30 40, 40 40, 40 30, 30 30, 30 40))"
     };
 
-    doTest(inp, exp);
+    doTest(inp, exp, true);
 }
 
 } // namespace tut
diff --git a/tests/xmltester/XMLTester.cpp b/tests/xmltester/XMLTester.cpp
index e214278..715d3f2 100644
--- a/tests/xmltester/XMLTester.cpp
+++ b/tests/xmltester/XMLTester.cpp
@@ -1510,12 +1510,11 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)
             plgnzr.add(gA);
 
 
-            std::vector<geos::geom::Polygon*>* polys = plgnzr.getPolygons();
+            auto polys = plgnzr.getPolygons();
             std::vector<geom::Geometry*>* newgeoms = new std::vector<geom::Geometry*>;
             for(unsigned int i = 0; i < polys->size(); i++) {
-                newgeoms->push_back((*polys)[i]);
+                newgeoms->push_back((*polys)[i].release());
             }
-            delete polys;
 
             GeomPtr gRealRes(factory->createGeometryCollection(newgeoms));
             gRealRes->normalize();

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

Summary of changes:
 NEWS                                               |   3 +
 capi/geos_c.cpp                                    |   6 +
 capi/geos_c.h.in                                   |  48 ++---
 capi/geos_ts_c.cpp                                 |  58 +++++-
 doc/example.cpp                                    |   5 +-
 include/geos/operation/polygonize/EdgeRing.h       | 182 ++++++++++++++++---
 .../operation/polygonize/PolygonizeDirectedEdge.h  |   7 +
 .../geos/operation/polygonize/PolygonizeGraph.h    |  22 +--
 include/geos/operation/polygonize/Polygonizer.h    |  53 ++++--
 src/operation/polygonize/BuildArea.cpp             |  67 +++----
 src/operation/polygonize/EdgeRing.cpp              | 134 +++++++-------
 src/operation/polygonize/PolygonizeGraph.cpp       | 144 +++++----------
 src/operation/polygonize/Polygonizer.cpp           |  92 ++++++----
 tests/unit/Makefile.am                             |   2 +-
 tests/unit/capi/GEOSPolygonizeTest.cpp             | 179 ++++++++++++++++++
 .../unit/capi/GEOSPolygonizer_getCutEdgesTest.cpp  | 106 -----------
 tests/unit/operation/polygonize/PolygonizeTest.cpp | 202 +++++++++++++++++----
 tests/xmltester/XMLTester.cpp                      |   5 +-
 18 files changed, 841 insertions(+), 474 deletions(-)
 create mode 100644 tests/unit/capi/GEOSPolygonizeTest.cpp
 delete mode 100644 tests/unit/capi/GEOSPolygonizer_getCutEdgesTest.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list