[geos-commits] [SCM] GEOS branch main updated. 927419638aef65e362403beb5110c15ea3ec2290

git at osgeo.org git at osgeo.org
Tue Jul 6 13:35:13 PDT 2021


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GEOS".

The branch, main has been updated
       via  927419638aef65e362403beb5110c15ea3ec2290 (commit)
      from  a752894f42e5c7b601cb6810fd03e011a0117eb6 (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 927419638aef65e362403beb5110c15ea3ec2290
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Tue Jul 6 13:35:00 2021 -0700

    Port https://github.com/locationtech/jts/pull/754, Fix IsSimpleOp to support FindAllLocations for all geometry types

diff --git a/include/geos/operation/valid/IsSimpleOp.h b/include/geos/operation/valid/IsSimpleOp.h
index 1ae8e8f..7a8043d 100644
--- a/include/geos/operation/valid/IsSimpleOp.h
+++ b/include/geos/operation/valid/IsSimpleOp.h
@@ -47,7 +47,49 @@ namespace geos {      // geos
 namespace operation { // geos.operation
 namespace valid {     // geos.operation.valid
 
-
+/**
+ * Tests whether a Geometry is simple as defined by the OGC SFS specification.
+ *
+ * Simplicity is defined for each geom::Geometry type as follows:
+ *
+ *   * geom::Point geometries are simple.
+ *   * geom::MultiPoint geometries are simple if every point is unique
+ *   * geom::LineString geometries are simple if they do not self-intersect at interior points
+ *     (i.e. points other than the endpoints).
+ *   * geom::MultiLineString geometries are simple if
+ *     their elements are simple and they intersect only at points
+ *     which are boundary points of both elements.
+ *     (The notion of boundary points can be user-specified - see below).
+ *   * Polygonal geometries have no definition of simplicity.
+ *     The isSimple code checks if all polygon rings are simple.
+ *     (Note: this means that isSimple cannot be used to test
+ *     for ALL self-intersections in Polygons.
+ *     In order to check if a Polygonal geometry has self-intersections,
+ *     use geom::Geometry::isValid()).
+ *   * geom::GeometryCollection geometries are simple if all their elements are simple.
+ *   * Empty geometries are simple
+ *
+ * For linear geometries the evaluation of simplicity
+ * can be customized by supplying a BoundaryNodeRule
+ * to define how boundary points are determined.
+ * The default is the SFS-standard.
+ *
+ * Note that under the Mod-2 rule, closed LineStrings (rings)
+ * have no boundary.
+ * This means that an intersection at their endpoints makes the geometry non-simple.
+ * If it is required to test whether a set of LineStrings touch
+ * only at their endpoints, use BoundaryNodeRule::getBoundaryEndPoint().
+ * For example, this can be used to validate that a collection of lines
+ * form a topologically valid linear network.
+ *
+ * By default this class finds a single non-simple location.
+ * To find all non-simple locations, set setFindAllLocations(bool)
+ * before calling isSimple(), and retrieve the locations
+ * via getNonSimpleLocations().
+ * This can be used to find all intersection points in a linear network.
+ *
+ * @see BoundaryNodeRule
+ */
 class GEOS_DLL IsSimpleOp {
 
 private:
@@ -101,10 +143,10 @@ private:
         std::vector<geom::Coordinate>& intersectionPts;
         algorithm::LineIntersector li;
 
-        bool hasInteriorInt;
-        bool hasInteriorVertexInt;
-        bool hasEqualSegments;
-        bool hasInteriorEndpointInt;
+        // bool hasInteriorInt;
+        // bool hasInteriorVertexInt;
+        // bool hasEqualSegments;
+        // bool hasInteriorEndpointInt;
 
         bool findIntersection(
             noding::SegmentString* ss0, std::size_t segIndex0,
@@ -179,10 +221,10 @@ public:
         {};
 
     /**
-    * Creates a simplicity checker using a given {@link BoundaryNodeRule}
+    * Creates a simplicity checker using a given {@link algorithm::BoundaryNodeRule}
     *
     * @param geom the geometry to test
-    * @param boundaryNodeRule the boundary node rule to use.
+    * @param p_boundaryNodeRule the boundary node rule to use.
     */
     IsSimpleOp(const geom::Geometry& geom, const algorithm::BoundaryNodeRule& p_boundaryNodeRule)
         : inputGeom(geom)
diff --git a/src/operation/valid/IsSimpleOp.cpp b/src/operation/valid/IsSimpleOp.cpp
index 3659638..da7c60e 100644
--- a/src/operation/valid/IsSimpleOp.cpp
+++ b/src/operation/valid/IsSimpleOp.cpp
@@ -140,6 +140,7 @@ bool
 IsSimpleOp::isSimpleMultiPoint(const MultiPoint& mp)
 {
     if (mp.isEmpty()) return true;
+    bool bIsSimple = true;
     std::unordered_set<Coordinate, Coordinate::HashCode> points;
 
     for (std::size_t i = 0; i < mp.getNumGeometries(); i++) {
@@ -147,11 +148,15 @@ IsSimpleOp::isSimpleMultiPoint(const MultiPoint& mp)
         const Coordinate* p = pt->getCoordinate();
         if (points.find(*p) != points.end()) {
             nonSimplePts.push_back(*p);
-            return false;
+            bIsSimple = false;
+            if (!isFindAllLocations)
+                break;
+        }
+        else {
+            points.emplace(*p);
         }
-        points.emplace(*p);
     }
-    return true;
+    return bIsSimple;
 }
 
 
@@ -159,15 +164,18 @@ IsSimpleOp::isSimpleMultiPoint(const MultiPoint& mp)
 bool
 IsSimpleOp::isSimplePolygonal(const Geometry& geom)
 {
-
+    bool bIsSimple = true;
     std::vector<const LineString*> rings;
     LinearComponentExtracter::getLines(geom, rings);
 
     for (const LineString* ring : rings) {
-        if (! isSimpleLinearGeometry(*ring))
-            return false;
+        if (! isSimpleLinearGeometry(*ring)) {
+            bIsSimple = false;
+            if (!isFindAllLocations)
+                break;
+        }
     }
-    return true;
+    return bIsSimple;
 }
 
 
@@ -175,12 +183,16 @@ IsSimpleOp::isSimplePolygonal(const Geometry& geom)
 bool
 IsSimpleOp::isSimpleGeometryCollection(const Geometry& geom)
 {
+    bool bIsSimple = true;
     for (std::size_t i = 0; i < geom.getNumGeometries(); i++) {
         const Geometry* comp = geom.getGeometryN(i);
-        if (! computeSimple(*comp))
-            return false;
+        if (! computeSimple(*comp)) {
+            bIsSimple = false;
+            if (!isFindAllLocations)
+                break;
+        }
     }
-    return true;
+    return bIsSimple;
 }
 
 /* private */
@@ -254,9 +266,16 @@ IsSimpleOp::NonSimpleIntersectionFinder::processIntersections(
         ss0, segIndex0, ss1, segIndex1,
         p00, p01, p10, p11);
 
+    // found an intersection!
     if (hasInt) {
-        // found an intersection!
-        intersectionPts.emplace_back(li.getIntersection(0));
+        const Coordinate& intPt = li.getIntersection(0);
+        // don't save dupes
+        for (auto& pt: intersectionPts) {
+            if (intPt.equals2D(pt))
+                return;
+        }
+        // do save new ones!
+        intersectionPts.emplace_back(intPt);
     }
 }
 
@@ -275,7 +294,7 @@ IsSimpleOp::NonSimpleIntersectionFinder::findIntersection(
     /**
     * Check for an intersection in the interior of a segment.
     */
-    hasInteriorInt = li.isInteriorIntersection();
+    bool hasInteriorInt = li.isInteriorIntersection();
     if (hasInteriorInt) return true;
 
     /**
@@ -284,7 +303,7 @@ IsSimpleOp::NonSimpleIntersectionFinder::findIntersection(
     * (This is not triggered by zero-length segments, since they
     * are filtered out by the MC index).
     */
-    hasEqualSegments = li.getIntersectionNum() >= 2;
+    bool hasEqualSegments = li.getIntersectionNum() >= 2;
     if (hasEqualSegments) return true;
 
     /**
@@ -306,7 +325,7 @@ IsSimpleOp::NonSimpleIntersectionFinder::findIntersection(
     bool isIntersectionEndpt0 = isIntersectionEndpoint(ss0, segIndex0, li, 0);
     bool isIntersectionEndpt1 = isIntersectionEndpoint(ss1, segIndex1, li, 1);
 
-    hasInteriorVertexInt = ! (isIntersectionEndpt0 && isIntersectionEndpt1);
+    bool hasInteriorVertexInt = ! (isIntersectionEndpt0 && isIntersectionEndpt1);
     if (hasInteriorVertexInt) return true;
 
     /**
@@ -317,7 +336,7 @@ IsSimpleOp::NonSimpleIntersectionFinder::findIntersection(
     * (which avoids reporting ring endpoints).
     */
     if (isClosedEndpointsInInterior && !isSameSegString) {
-        hasInteriorEndpointInt = ss0->isClosed() || ss1->isClosed();
+        bool hasInteriorEndpointInt = ss0->isClosed() || ss1->isClosed();
         if (hasInteriorEndpointInt) return true;
     }
     return false;
diff --git a/tests/unit/operation/valid/IsSimpleOpTest.cpp b/tests/unit/operation/valid/IsSimpleOpTest.cpp
index 31afac1..25b5b1a 100644
--- a/tests/unit/operation/valid/IsSimpleOpTest.cpp
+++ b/tests/unit/operation/valid/IsSimpleOpTest.cpp
@@ -84,6 +84,7 @@ struct test_issimpleop_data {
 
         auto nonSimpleCoords = op.getNonSimpleLocations();
         std::unique_ptr<Geometry> nsPts(g->getFactory()->createMultiPoint(nonSimpleCoords));
+        // std::cout << *nsPts << std::endl;
         auto expectedPts = reader_.read(wktExpectedPts);
         ensure_equals_geometry(expectedPts.get(), nsPts.get());
     }
@@ -202,7 +203,35 @@ void object::test<7> ()
     checkIsSimpleAll(a, BoundaryNodeRule::getBoundaryRuleMod2(), b);
 }
 
+// testPolygonAll
+template<>
+template<>
+void object::test<8> ()
+{
+    std::string a("POLYGON ((0 0, 7 0, 6 -1, 6 -0.1, 6 0.1, 3 5.9, 3 6.1, 3.1 6, 2.9 6, 0 0))");
+    std::string b("MULTIPOINT((6 0), (3 6))");
+    checkIsSimpleAll(a, BoundaryNodeRule::getBoundaryRuleMod2(), b);
+}
+
+// testMultiPointAll
+template<>
+template<>
+void object::test<9> ()
+{
+    std::string a("MULTIPOINT((1 1), (1 2), (1 2), (1 3), (1 4), (1 4), (1 5), (1 5))");
+    std::string b("MULTIPOINT((1 2), (1 4), (1 5))");
+    checkIsSimpleAll(a, BoundaryNodeRule::getBoundaryRuleMod2(), b);
+}
 
+// testGeometryCollectionAll
+template<>
+template<>
+void object::test<10> ()
+{
+    std::string a("GEOMETRYCOLLECTION(MULTILINESTRING ((10 20, 90 20), (10 30, 90 30), (50 40, 50 10)), MULTIPOINT((1 1), (1 2), (1 2), (1 3), (1 4), (1 4), (1 5), (1 5)))");
+    std::string b("MULTIPOINT((50 20), (50 30), (1 2), (1 4), (1 5))");
+    checkIsSimpleAll(a, BoundaryNodeRule::getBoundaryRuleMod2(), b);
+}
 
 
 } // namespace tut

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

Summary of changes:
 include/geos/operation/valid/IsSimpleOp.h     | 56 +++++++++++++++++++++++----
 src/operation/valid/IsSimpleOp.cpp            | 51 ++++++++++++++++--------
 tests/unit/operation/valid/IsSimpleOpTest.cpp | 29 ++++++++++++++
 3 files changed, 113 insertions(+), 23 deletions(-)


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list