[geos-commits] [SCM] GEOS branch master updated. 3c5ed392f4570a9a434c059b68e26d777570ed47

git at osgeo.org git at osgeo.org
Thu Sep 17 13:11:14 PDT 2020


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  3c5ed392f4570a9a434c059b68e26d777570ed47 (commit)
      from  f54cd8347c070a3b76b02b9b772c31a1feca6173 (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 3c5ed392f4570a9a434c059b68e26d777570ed47
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Thu Sep 17 13:11:07 2020 -0700

    Add OverlayNG strict mode, from https://github.com/dr-jts/jts/commit/0da1f61ad6e71c04c7de22853cb06fb1b394bec6 and https://github.com/dr-jts/jts/commit/b0d6f035777de7035cbe915ec18e65c2b9f0838e

diff --git a/include/geos/operation/overlayng/IntersectionPointBuilder.h b/include/geos/operation/overlayng/IntersectionPointBuilder.h
index 08b5e09..63637e6 100644
--- a/include/geos/operation/overlayng/IntersectionPointBuilder.h
+++ b/include/geos/operation/overlayng/IntersectionPointBuilder.h
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <geos/geom/Point.h>
+#include <geos/operation/overlayng/OverlayNG.h>
 
 #include <geos/export.h>
 #include <vector>
@@ -64,6 +65,12 @@ private:
     OverlayGraph* graph;
     const geom::GeometryFactory* geometryFactory;
     std::vector<std::unique_ptr<geom::Point>> points;
+    /**
+    * Controls whether lines created by area topology collapses
+    * to participate in the result computation.
+    * True provides the original JTS semantics.
+    */
+    bool isAllowCollapseLines;
 
     // Methods
     void addResultPoints();
@@ -77,18 +84,26 @@ private:
     bool isEdgeOf(const OverlayLabel* label, int i) const;
 
 
-
 public:
 
 
     IntersectionPointBuilder(OverlayGraph* p_graph, const geom::GeometryFactory* geomFact)
         : graph(p_graph)
-        , geometryFactory(geomFact) {}
+        , geometryFactory(geomFact)
+        , isAllowCollapseLines(!OverlayNG::STRICT_MODE_DEFAULT)
+        {}
 
     std::vector<std::unique_ptr<geom::Point>> getPoints();
 
     IntersectionPointBuilder(const IntersectionPointBuilder&) = delete;
     IntersectionPointBuilder& operator=(const IntersectionPointBuilder&) = delete;
+
+    void setStrictMode(bool p_isStrictMode)
+    {
+        isAllowCollapseLines = ! p_isStrictMode;
+    }
+
+
 };
 
 
diff --git a/include/geos/operation/overlayng/LineBuilder.h b/include/geos/operation/overlayng/LineBuilder.h
index 63039b8..f0a4a12 100644
--- a/include/geos/operation/overlayng/LineBuilder.h
+++ b/include/geos/operation/overlayng/LineBuilder.h
@@ -17,6 +17,7 @@
 #include <geos/export.h>
 
 #include <geos/operation/overlayng/InputGeometry.h>
+#include <geos/operation/overlayng/OverlayNG.h>
 #include <geos/geom/Location.h>
 #include <geos/geom/LineString.h>
 
@@ -78,6 +79,21 @@ private:
     int inputAreaIndex;
     std::vector<std::unique_ptr<geom::LineString>> lines;
 
+    /**
+    * Indicates whether intersections are allowed to produce
+    * heterogeneous results including proper boundary touches.
+    * This does not control inclusion of touches along collapses.
+    * True provides the original JTS semantics.
+    */
+    bool isAllowMixedResult = ! OverlayNG::STRICT_MODE_DEFAULT;
+
+    /**
+    * Allow lines created by area topology collapses
+    * to appear in the result.
+    * True provides the original JTS semantics.
+    */
+    bool isAllowCollapseLines = ! OverlayNG::STRICT_MODE_DEFAULT;
+
     void markResultLines();
 
     /**
@@ -149,13 +165,22 @@ public:
         , opCode(p_opCode)
         , geometryFactory(geomFact)
         , hasResultArea(p_hasResultArea)
-        , inputAreaIndex(inputGeom->getAreaIndex()) {}
+        , inputAreaIndex(inputGeom->getAreaIndex())
+        , isAllowMixedResult(! OverlayNG::STRICT_MODE_DEFAULT)
+        , isAllowCollapseLines(! OverlayNG::STRICT_MODE_DEFAULT)
+        {}
 
     LineBuilder(const LineBuilder&) = delete;
     LineBuilder& operator=(const LineBuilder&) = delete;
 
     std::vector<std::unique_ptr<geom::LineString>> getLines();
 
+    void setStrictMode(bool p_isStrictResultMode)
+    {
+        isAllowCollapseLines = ! p_isStrictResultMode;
+        isAllowMixedResult = ! p_isStrictResultMode;
+    }
+
 };
 
 
diff --git a/include/geos/operation/overlayng/OverlayNG.h b/include/geos/operation/overlayng/OverlayNG.h
index 74efe8d..0c40858 100644
--- a/include/geos/operation/overlayng/OverlayNG.h
+++ b/include/geos/operation/overlayng/OverlayNG.h
@@ -77,8 +77,21 @@ namespace overlayng { // geos.operation.overlayng
  * since the intersection clipping optimization can
  * interact with the snapping to alter the result.
  *
- * @author mdavis
+ * TOptionally the overlay computation can process using strict mode
+ * (via setStrictMode(boolean). In strict mode result semantics are:
+ *
+ *  - Result geometries are homogeneous (all components are of same dimension),
+ *    except for some cases of symmetricDifference.
+ *  - Lines and Points resulting from topology collapses are not included in the result
+ *
+ * Strict mode has the following benefits:
  *
+ *  - Results are simpler
+ *  - Overlay operations are easily chainable
+ *
+ * The original JTS overlay semantics correspond to non-strict mode.
+ *
+ * @author mdavis
  * @see OverlayNGRobust
  *
  */
@@ -86,19 +99,13 @@ class GEOS_DLL OverlayNG {
 
 private:
 
-    /**
-    * Indicates whether the old overlay result semantics are used:
-    * - Intersection result can be mixed-dimension
-    * - Results can include lines caused by Area topology collapse
-    */
-    static constexpr bool USE_OLD_RESULT_SEMANTICS = true;
-
     // Members
     const geom::PrecisionModel* pm;
     InputGeometry inputGeom;
     const geom::GeometryFactory* geomFact;
     int opCode;
     noding::Noder* noder;
+    bool isStrictMode;
     bool isOptimized;
     bool isAreaResultOnly;
     bool isOutputEdges;
@@ -129,28 +136,22 @@ private:
 
 
 public:
+    /**
+    * The default setting for Strict Mode.
+    *
+    * The original JTS overlay semantics used non-strict result
+    * semantics, including;
+    * - An Intersection result can be mixed-dimension,
+    *   due to inclusion of intersection components of all dimensions
+    * - Results can include lines caused by Area topology collapse
+    */
+    static constexpr bool STRICT_MODE_DEFAULT = false;
 
     static constexpr int INTERSECTION   = overlay::OverlayOp::opINTERSECTION;
     static constexpr int UNION          = overlay::OverlayOp::opUNION;
     static constexpr int DIFFERENCE     = overlay::OverlayOp::opDIFFERENCE;
     static constexpr int SYMDIFFERENCE  = overlay::OverlayOp::opSYMDIFFERENCE;
 
-
-    /**
-    * Indicates whether intersections are allowed to produce
-    * heterogeneous results including proper boundary touches.
-    * This does not control inclusion of touches along collapses.
-    * True provides the original JTS semantics.
-    */
-    static constexpr bool ALLOW_INT_MIXED_RESULT = USE_OLD_RESULT_SEMANTICS;
-
-    /**
-    * Allow lines created by area topology collapses
-    * to appear in the result.
-    * True provides the original JTS semantics.
-    */
-    static constexpr bool ALLOW_COLLAPSE_LINES = USE_OLD_RESULT_SEMANTICS;
-
     /**
     * Creates an overlay operation on the given geometries,
     * with a defined precision model.
@@ -162,6 +163,7 @@ public:
         , geomFact(p_geomFact)
         , opCode(p_opCode)
         , noder(nullptr)
+        , isStrictMode(STRICT_MODE_DEFAULT)
         , isOptimized(true)
         , isAreaResultOnly(false)
         , isOutputEdges(false)
@@ -180,6 +182,7 @@ public:
         , geomFact(geom0->getFactory())
         , opCode(p_opCode)
         , noder(nullptr)
+        , isStrictMode(STRICT_MODE_DEFAULT)
         , isOptimized(true)
         , isAreaResultOnly(false)
         , isOutputEdges(false)
@@ -217,6 +220,7 @@ public:
     * @param p_isOptimized whether to optimize processing
     */
     void setOptimized(bool p_isOptimized) { isOptimized = p_isOptimized; }
+    void setStrictMode(bool p_isStrictMode) { isStrictMode = p_isStrictMode; }
     void setAreaResultOnly(bool p_areaResultOnly) { isAreaResultOnly = p_areaResultOnly; }
     void setOutputEdges(bool p_isOutputEdges) { isOutputEdges = p_isOutputEdges; }
     void setOutputResultEdges(bool p_isOutputResultEdges) { isOutputResultEdges = p_isOutputResultEdges; }
diff --git a/src/operation/overlayng/IntersectionPointBuilder.cpp b/src/operation/overlayng/IntersectionPointBuilder.cpp
index d1c8484..fed278a 100644
--- a/src/operation/overlayng/IntersectionPointBuilder.cpp
+++ b/src/operation/overlayng/IntersectionPointBuilder.cpp
@@ -75,6 +75,9 @@ IntersectionPointBuilder::isResultPoint(OverlayEdge* nodeEdge) const
 bool
 IntersectionPointBuilder::isEdgeOf(const OverlayLabel* label, int i) const
 {
+    if (!isAllowCollapseLines && label->isBoundaryCollapse()) {
+        return false;
+    }
     return label->isBoundary(i) || label->isLine(i);
 }
 
diff --git a/src/operation/overlayng/LineBuilder.cpp b/src/operation/overlayng/LineBuilder.cpp
index 52fa91e..06e0dcc 100644
--- a/src/operation/overlayng/LineBuilder.cpp
+++ b/src/operation/overlayng/LineBuilder.cpp
@@ -82,7 +82,7 @@ LineBuilder::isResultLine(const OverlayLabel* lbl) const
     * OR two coincident area boundaries.
     * This logic is only used if not including collapse lines in result.
     */
-    if (! OverlayNG::ALLOW_COLLAPSE_LINES && lbl->isBoundaryCollapse())
+    if (!isAllowCollapseLines && lbl->isBoundaryCollapse())
         return false;
 
     /**
@@ -120,7 +120,7 @@ LineBuilder::isResultLine(const OverlayLabel* lbl) const
     * Include line edge formed by touching area boundaries,
     * if enabled.
     */
-    if (OverlayNG::ALLOW_INT_MIXED_RESULT &&
+    if (isAllowMixedResult &&
         opCode == OverlayNG::INTERSECTION &&
         lbl->isBoundaryTouch()) {
         return true;
diff --git a/src/operation/overlayng/OverlayNG.cpp b/src/operation/overlayng/OverlayNG.cpp
index 3c1a05b..baba411 100644
--- a/src/operation/overlayng/OverlayNG.cpp
+++ b/src/operation/overlayng/OverlayNG.cpp
@@ -232,6 +232,8 @@ OverlayNG::extractResult(int p_opCode, OverlayGraph* graph)
     std::cerr << "OverlayNG::extractResult: graph: " << *graph << std::endl;
 #endif
 
+    bool isAllowMixedIntResult = ! isStrictMode;
+
     //--- Build polygons
     std::vector<OverlayEdge*> resultAreaEdges = graph->getResultAreaEdges();
     PolygonBuilder polyBuilder(resultAreaEdges, geomFact);
@@ -243,9 +245,14 @@ OverlayNG::extractResult(int p_opCode, OverlayGraph* graph)
 
     if (!isAreaResultOnly) {
         //--- Build lines
-        bool allowResultLines = ! hasResultAreaComponents || ALLOW_INT_MIXED_RESULT;
+        bool allowResultLines = !hasResultAreaComponents ||
+                                isAllowMixedIntResult ||
+                                opCode == SYMDIFFERENCE ||
+                                opCode == UNION;
+
         if (allowResultLines) {
             LineBuilder lineBuilder(&inputGeom, graph, hasResultAreaComponents, p_opCode, geomFact);
+            lineBuilder.setStrictMode(isStrictMode);
             resultLineList = lineBuilder.getLines();
         }
         /**
@@ -254,9 +261,10 @@ OverlayNG::extractResult(int p_opCode, OverlayGraph* graph)
          * from non-point inputs.
          */
         bool hasResultComponents = hasResultAreaComponents || resultLineList.size() > 0;
-        bool allowResultPoints = ! hasResultComponents || ALLOW_INT_MIXED_RESULT;
+        bool allowResultPoints = ! hasResultComponents || isAllowMixedIntResult;
         if (opCode == INTERSECTION && allowResultPoints) {
             IntersectionPointBuilder pointBuilder(graph, geomFact);
+            pointBuilder.setStrictMode(isStrictMode);
             resultPointList = pointBuilder.getPoints();
         }
     }
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
index d7290cd..f8d4c8c 100644
--- a/tests/unit/Makefile.am
+++ b/tests/unit/Makefile.am
@@ -194,11 +194,12 @@ geos_unit_SOURCES = \
 	operation/overlayng/LineLimiterTest.cpp \
 	operation/overlayng/OverlayGraphTest.cpp \
 	operation/overlayng/OverlayNGFloatingNoderTest.cpp \
-	operation/overlayng/OverlayNGPointsTest.cpp \
 	operation/overlayng/OverlayNGMixedPointsTest.cpp \
+	operation/overlayng/OverlayNGPointsTest.cpp \
 	operation/overlayng/OverlayNGRobustTest.cpp \
 	operation/overlayng/OverlayNGSnappingNoderTest.cpp \
 	operation/overlayng/OverlayNGSnappingOneTest.cpp \
+	operation/overlayng/OverlayNGStrictModeTest.cpp \
 	operation/overlayng/OverlayNGTest.cpp \
 	operation/overlayng/PrecisionReducerTest.cpp \
 	operation/overlayng/PrecisionUtilTest.cpp \
diff --git a/tests/unit/operation/overlayng/OverlayNGStrictModeTest.cpp b/tests/unit/operation/overlayng/OverlayNGStrictModeTest.cpp
new file mode 100644
index 0000000..ed2c588
--- /dev/null
+++ b/tests/unit/operation/overlayng/OverlayNGStrictModeTest.cpp
@@ -0,0 +1,194 @@
+//
+// Test Suite for geos::operation::overlayng::OverlayNGRobust class.
+//
+// Useful place for test cases raised by 3rd party software, that will be using
+// Geometry->Intersection(), Geometry->Union(), etc, that call into the
+// OverlayNGRobust utility class that bundles up different precision models
+// and noders to provide a "best case" overlay for all inputs.
+//
+
+#include <tut/tut.hpp>
+#include <utility.h>
+
+// geos
+#include <geos/operation/overlayng/OverlayNG.h>
+
+// std
+#include <memory>
+
+using namespace geos::geom;
+using namespace geos::operation::overlayng;
+using geos::io::WKTReader;
+using geos::io::WKTWriter;
+
+namespace tut {
+//
+// Test Group
+//
+
+// Common data used by all tests
+struct test_overlayngstring_data {
+
+    WKTReader r;
+    WKTWriter w;
+
+    void
+    testOverlay(const std::string& a, const std::string& b, const std::string& expected, int opCode)
+    {
+        std::unique_ptr<Geometry> geom_a = r.read(a);
+        std::unique_ptr<Geometry> geom_b = r.read(b);
+        std::unique_ptr<Geometry> geom_expected = r.read(expected);
+        OverlayNG ov(geom_a.get(), geom_b.get(), opCode);
+        ov.setStrictMode(true);
+        std::unique_ptr<Geometry> geom_result = ov.getResult();
+        // std::string wkt_result = w.write(geom_result.get());
+        // std::cout << std::endl << wkt_result << std::endl;
+        ensure_equals_geometry(geom_expected.get(), geom_result.get());
+    }
+
+    void
+    testOverlay(const std::string& a, const std::string& b, const std::string& expected, int opCode, double scaleFactor)
+    {
+        PrecisionModel pm(scaleFactor);
+        std::unique_ptr<Geometry> geom_a = r.read(a);
+        std::unique_ptr<Geometry> geom_b = r.read(b);
+        std::unique_ptr<Geometry> geom_expected = r.read(expected);
+        OverlayNG ov(geom_a.get(), geom_b.get(), &pm, opCode);
+        ov.setStrictMode(true);
+        std::unique_ptr<Geometry> geom_result = ov.getResult();
+        // std::string wkt_result = w.write(geom_result.get());
+        // std::cout << std::endl << wkt_result << std::endl;
+        ensure_equals_geometry(geom_expected.get(), geom_result.get());
+    }
+
+};
+
+typedef test_group<test_overlayngstring_data> group;
+typedef group::object object;
+
+group test_overlayngstring_data("geos::operation::overlayng::OverlayNGStrictMode");
+
+//
+// Test Cases
+//
+
+// testPolygonTouchALPIntersection
+template<>
+template<>
+void object::test<1> ()
+{
+    std::string a = "POLYGON ((10 10, 10 30, 30 30, 30 10, 10 10))";
+    std::string b = "POLYGON ((40 10, 30 10, 35 15, 30 15, 30 20, 35 20, 25 30, 40 30, 40 10))";
+    std::string expected = "POLYGON ((30 25, 25 30, 30 30, 30 25))";
+    testOverlay(a, b, expected, OverlayNG::INTERSECTION);
+}
+
+// testPolygonTouchALIntersection
+template<>
+template<>
+void object::test<3> ()
+{
+    std::string a = "POLYGON ((10 30, 60 30, 60 10, 10 10, 10 30))";
+    std::string b = "POLYGON ((10 50, 60 50, 60 30, 30 30, 10 10, 10 50))";
+    std::string expected = "POLYGON ((30 30, 10 10, 10 30, 30 30))";
+    testOverlay(a, b, expected, OverlayNG::INTERSECTION);
+}
+
+// testPolygonTouchLPIntersection
+template<>
+template<>
+void object::test<4> ()
+{
+    std::string a = "POLYGON ((10 10, 10 30, 30 30, 30 10, 10 10))";
+    std::string b = "POLYGON ((40 25, 30 25, 30 20, 35 20, 30 15, 40 15, 40 25))";
+    std::string expected = "LINESTRING (30 25, 30 20)";
+    testOverlay(a, b, expected, OverlayNG::INTERSECTION);
+}
+
+// testLineTouchLPIntersection
+template<>
+template<>
+void object::test<5> ()
+{
+    std::string a = "LINESTRING (10 10, 20 10, 20 20, 30 10)";
+    std::string b = "LINESTRING (10 10, 30 10)";
+    std::string expected = "LINESTRING (10 10, 20 10)";
+    testOverlay(a, b, expected, OverlayNG::INTERSECTION);
+}
+
+// testPolygonResultMixedIntersection
+template<>
+template<>
+void object::test<6> ()
+{
+    std::string a = "POLYGON ((10 30, 60 30, 60 10, 10 10, 10 30))";
+    std::string b = "POLYGON ((10 50, 60 50, 60 30, 30 30, 10 10, 10 50))";
+    std::string expected = "POLYGON ((30 30, 10 10, 10 30, 30 30))";
+    testOverlay(a, b, expected, OverlayNG::INTERSECTION);
+}
+
+// testPolygonResultLineIntersection
+template<>
+template<>
+void object::test<7> ()
+{
+    std::string a = "POLYGON ((10 20, 20 20, 20 10, 10 10, 10 20))";
+    std::string b = "POLYGON ((30 20, 30 10, 20 10, 20 20, 30 20))";
+    std::string expected = "LINESTRING (20 20, 20 10)";
+    testOverlay(a, b, expected, OverlayNG::INTERSECTION);
+}
+
+/**
+* Symmetric Difference is the one exception
+* to the Strict Mode homogeneous output rule.
+*/
+// testPolygonLineSymDifference
+template<>
+template<>
+void object::test<8> ()
+{
+    std::string a = "POLYGON ((10 20, 20 20, 20 10, 10 10, 10 20))";
+    std::string b = "LINESTRING (15 15, 25 15)";
+    std::string expected = "GEOMETRYCOLLECTION (POLYGON ((20 20, 20 15, 20 10, 10 10, 10 20, 20 20)), LINESTRING (20 15, 25 15))";
+    testOverlay(a, b, expected, OverlayNG::SYMDIFFERENCE);
+}
+
+/**
+* Check that result does not include collapsed line intersection
+*/
+// testPolygonIntersectionCollapse
+template<>
+template<>
+void object::test<9> ()
+{
+    std::string a = "POLYGON ((1 1, 1 5, 3 5, 3 2, 9 1, 1 1))";
+    std::string b = "POLYGON ((7 5, 9 5, 9 1, 7 1, 7 5))";
+    std::string expected = "POLYGON EMPTY";
+    testOverlay(a, b, expected, OverlayNG::INTERSECTION, 1.0);
+}
+
+// testPolygonUnionCollapse
+template<>
+template<>
+void object::test<10> ()
+{
+    std::string a = "POLYGON ((1 1, 1 5, 3 5, 3 1.4, 7 1, 1 1))";
+    std::string b = "POLYGON ((7 5, 9 5, 9 1, 7 1, 7 5))";
+    std::string expected = "MULTIPOLYGON (((1 1, 1 5, 3 5, 3 1, 1 1)), ((7 1, 7 5, 9 5, 9 1, 7 1)))";
+    testOverlay(a, b, expected, OverlayNG::UNION, 1.0);
+}
+
+
+// testPolygonLineUnion
+template<>
+template<>
+void object::test<11> ()
+{
+    std::string a = "POLYGON ((10 20, 20 20, 20 10, 10 10, 10 20))";
+    std::string b = "LINESTRING (15 15, 25 15)";
+    std::string expected = "GEOMETRYCOLLECTION (POLYGON ((20 20, 20 15, 20 10, 10 10, 10 20, 20 20)), LINESTRING (20 15, 25 15))";
+    testOverlay(a, b, expected, OverlayNG::UNION);
+}
+
+
+} // namespace tut

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

Summary of changes:
 .../operation/overlayng/IntersectionPointBuilder.h |  19 +-
 include/geos/operation/overlayng/LineBuilder.h     |  27 ++-
 include/geos/operation/overlayng/OverlayNG.h       |  52 +++---
 .../overlayng/IntersectionPointBuilder.cpp         |   3 +
 src/operation/overlayng/LineBuilder.cpp            |   4 +-
 src/operation/overlayng/OverlayNG.cpp              |  12 +-
 tests/unit/Makefile.am                             |   3 +-
 .../overlayng/OverlayNGStrictModeTest.cpp          | 194 +++++++++++++++++++++
 8 files changed, 282 insertions(+), 32 deletions(-)
 create mode 100644 tests/unit/operation/overlayng/OverlayNGStrictModeTest.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list