[geos-commits] [SCM] GEOS branch main updated. 66beb4176912f3daceba65eabfc5542e6047cb60

git at osgeo.org git at osgeo.org
Wed May 22 21:14:31 PDT 2024


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  66beb4176912f3daceba65eabfc5542e6047cb60 (commit)
      from  a13c2aab335ac237b89e0748cf74e0c15c8498cc (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 66beb4176912f3daceba65eabfc5542e6047cb60
Author: Martin Davis <mtnclimb at gmail.com>
Date:   Wed May 22 21:14:08 2024 -0700

    Add CoveragePolygonValidator section performance optimization (#1099)

diff --git a/include/geos/coverage/CoveragePolygon.h b/include/geos/coverage/CoveragePolygon.h
new file mode 100644
index 000000000..e56668c45
--- /dev/null
+++ b/include/geos/coverage/CoveragePolygon.h
@@ -0,0 +1,57 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2024 Martin Davis <mtnclimb at gmail.com>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#pragma once
+
+#include <geos/algorithm/locate/IndexedPointInAreaLocator.h>
+
+// Forward declarations
+namespace geos {
+namespace geom {
+class Coordinate;
+class Envelope;
+class Polygon;
+}
+}
+
+using geos::geom::CoordinateXY;
+using geos::geom::Envelope;
+using geos::geom::Polygon;
+using geos::algorithm::locate::IndexedPointInAreaLocator;
+
+namespace geos {        // geos
+namespace coverage {    // geos::coverage
+
+class GEOS_DLL CoveragePolygon {
+
+    // Members
+    const Polygon* m_polygon;
+    Envelope polyEnv;
+    mutable std::unique_ptr<IndexedPointInAreaLocator> m_locator;
+
+public:
+    CoveragePolygon(const Polygon* poly);
+
+    bool intersectsEnv(const Envelope& env) const;
+    bool intersectsEnv(const CoordinateXY& p) const;
+    bool contains(const CoordinateXY& p) const;
+    
+private:
+    IndexedPointInAreaLocator& getLocator() const;  
+
+};
+
+} // namespace geos::coverage
+} // namespace geos
+
diff --git a/include/geos/coverage/CoveragePolygonValidator.h b/include/geos/coverage/CoveragePolygonValidator.h
index c0150c895..8f635de38 100644
--- a/include/geos/coverage/CoveragePolygonValidator.h
+++ b/include/geos/coverage/CoveragePolygonValidator.h
@@ -17,6 +17,7 @@
 #include <geos/noding/BasicSegmentString.h>
 #include <geos/geom/LineSegment.h>
 #include <geos/algorithm/locate/IndexedPointInAreaLocator.h>
+#include <geos/coverage/CoveragePolygon.h>
 #include <geos/coverage/CoverageRing.h>
 
 #include <unordered_map>
@@ -195,11 +196,10 @@ private:
     // Members
     const Geometry* targetGeom;
     std::vector<const Geometry*> adjGeoms;
-    std::vector<const Polygon*> m_adjPolygons;
+    //std::vector<const Polygon*> m_adjPolygons;
     const GeometryFactory* geomFactory;
     double gapWidth = 0.0;
-    std::map<std::size_t, std::unique_ptr<IndexedPointInAreaLocator>> adjPolygonLocators;
-    // std::vector<std::unique_ptr<CoverageRing>> coverageRingStore;
+    std::vector<std::unique_ptr<CoveragePolygon>> m_adjCovPolygons;
     std::deque<CoverageRing> coverageRingStore;
     std::vector<std::unique_ptr<CoordinateSequence>> localCoordinateSequences;
     std::deque<CoverageRingSegment> coverageRingSegmentStore;
@@ -273,6 +273,8 @@ public:
 
 private:
 
+    static std::vector<std::unique_ptr<CoveragePolygon>> 
+        toCoveragePolygons(const std::vector<const Polygon*> polygons);
     static std::vector<const Polygon*> extractPolygons(std::vector<const Geometry*>& geoms);
 
     /* private */
@@ -336,34 +338,26 @@ private:
     * to an adjacent polygon.
     *
     * @param targetRings the rings with segments to test
-    * @param adjPolygons the adjacent polygons
+    * @param adjCovPolygons the adjacent polygons
     */
     void markInvalidInteriorSegments(
         std::vector<CoverageRing*>& targetRings,
-        std::vector<const Polygon*>& adjPolygons);
+        std::vector<std::unique_ptr<CoveragePolygon>>& adjCovPolygons);
+
+    void markInvalidInteriorSection(
+        CoverageRing& ring,
+        std::size_t iStart, 
+        std::size_t iEnd, 
+        std::vector<std::unique_ptr<CoveragePolygon>>& adjCovPolygons );
+
+    void markInvalidInteriorSegment(
+        CoverageRing& ring, std::size_t i, CoveragePolygon* adjPoly);
 
     void checkTargetRings(
         std::vector<CoverageRing*>& targetRings,
         std::vector<CoverageRing*>& adjRngs,
         const Envelope& targetEnv);
 
-    /**
-    * Tests if a coordinate is in the interior of some adjacent polygon.
-    * Uses the cached Point-In-Polygon indexed locators, for performance.
-    *
-    * @param p the coordinate to test
-    * @param adjPolygons the list of polygons
-    * @return true if the point is in the interior
-    */
-    bool isInteriorVertex(const Coordinate& p,
-        std::vector<const Polygon*>& adjPolygons);
-
-
-    bool polygonContainsPoint(std::size_t index,
-        const Polygon* poly, const Coordinate& pt);
-
-    IndexedPointInAreaLocator* getLocator(std::size_t index, const Polygon* poly);
-
     std::unique_ptr<Geometry> createInvalidLines(std::vector<CoverageRing*>& rings);
 
     std::vector<CoverageRing*> createRings(const Geometry* geom);
diff --git a/include/geos/coverage/CoverageRing.h b/include/geos/coverage/CoverageRing.h
index 45a348e9f..76bd44aaf 100644
--- a/include/geos/coverage/CoverageRing.h
+++ b/include/geos/coverage/CoverageRing.h
@@ -83,6 +83,8 @@ public:
 
     CoverageRing(const LinearRing* ring, bool isShell);
 
+    geom::Envelope getEnvelope(std::size_t start, std::size_t end);
+
     /**
     * Tests if all rings have known status (matched or invalid)
     * for all segments.
diff --git a/src/coverage/CoveragePolygon.cpp b/src/coverage/CoveragePolygon.cpp
new file mode 100644
index 000000000..46ee29c7c
--- /dev/null
+++ b/src/coverage/CoveragePolygon.cpp
@@ -0,0 +1,76 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2024 Martin Davis <mtnclimb at gmail.com>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************/
+
+#include <geos/coverage/CoveragePolygon.h>
+
+#include <geos/geom/Coordinate.h>
+#include <geos/geom/Envelope.h>
+#include <geos/geom/Location.h>
+#include <geos/geom/Polygon.h>
+
+using geos::algorithm::locate::IndexedPointInAreaLocator;
+using geos::geom::CoordinateXY;
+using geos::geom::Envelope;
+using geos::geom::Location;
+using geos::geom::Polygon;
+
+namespace geos {     // geos
+namespace coverage { // geos.coverage
+
+/* public */
+CoveragePolygon::CoveragePolygon(const Polygon* poly)
+    : m_polygon(poly)
+{
+    //-- cache polygon envelope for maximum performance
+    polyEnv = *(poly->getEnvelopeInternal());
+}
+
+/* public */
+bool 
+CoveragePolygon::intersectsEnv(const Envelope& env) const
+{
+    return polyEnv.intersects(env);
+}
+
+/* public */
+bool 
+CoveragePolygon::intersectsEnv(const CoordinateXY& p) const
+{
+    return polyEnv.intersects(p);
+}
+
+/* public */
+bool 
+CoveragePolygon::contains(const CoordinateXY& p) const
+{
+    if (! intersectsEnv(p))
+        return false;
+    IndexedPointInAreaLocator& pia = getLocator();
+    return Location::INTERIOR == pia.locate(&p);
+}
+
+/* private */
+IndexedPointInAreaLocator&
+CoveragePolygon::getLocator() const
+{
+    if (m_locator == nullptr) {
+        m_locator = std::make_unique<IndexedPointInAreaLocator>(*m_polygon);
+    }
+    return *m_locator;
+}
+
+} // namespace geos.coverage
+} // namespace geos
+
+
diff --git a/src/coverage/CoveragePolygonValidator.cpp b/src/coverage/CoveragePolygonValidator.cpp
index a99643903..7690e2542 100644
--- a/src/coverage/CoveragePolygonValidator.cpp
+++ b/src/coverage/CoveragePolygonValidator.cpp
@@ -13,7 +13,9 @@
  **********************************************************************/
 
 #include <geos/coverage/CoveragePolygonValidator.h>
+
 #include <geos/coverage/InvalidSegmentDetector.h>
+#include <geos/coverage/CoveragePolygon.h>
 
 #include <geos/algorithm/Orientation.h>
 #include <geos/geom/Coordinate.h>
@@ -31,7 +33,7 @@
 
 using geos::algorithm::locate::IndexedPointInAreaLocator;
 using geos::algorithm::Orientation;
-using geos::geom::Coordinate;
+using geos::geom::CoordinateXY;
 using geos::geom::Envelope;
 using geos::geom::Geometry;
 using geos::geom::GeometryFactory;
@@ -88,10 +90,10 @@ CoveragePolygonValidator::setGapWidth(double p_gapWidth)
 std::unique_ptr<Geometry>
 CoveragePolygonValidator::validate()
 {
-    m_adjPolygons = extractPolygons(adjGeoms);
-
+    std::vector<const Polygon*> adjPolygons = extractPolygons(adjGeoms);
+    m_adjCovPolygons = toCoveragePolygons(adjPolygons);
     std::vector<CoverageRing*> targetRings = createRings(targetGeom);
-    std::vector<CoverageRing*> adjRings = createRings(m_adjPolygons);
+    std::vector<CoverageRing*> adjRings = createRings(adjPolygons);
 
     /**
     * Mark matching segments first.
@@ -106,6 +108,15 @@ CoveragePolygonValidator::validate()
     return createInvalidLines(targetRings);
 }
 
+/* private static */
+std::vector<std::unique_ptr<CoveragePolygon>> 
+CoveragePolygonValidator::toCoveragePolygons(const std::vector<const Polygon*> polygons) {
+    std::vector<std::unique_ptr<CoveragePolygon>> covPolys;
+    for (const Polygon* poly : polygons) {
+        covPolys.push_back( std::make_unique<CoveragePolygon>(poly) );
+    }
+    return covPolys;
+}
 
 /* private */
 void
@@ -132,10 +143,9 @@ CoveragePolygonValidator::checkTargetRings(
      * Do further checks to see if any of them are are invalid.
      */
     markInvalidInteractingSegments(targetRings, adjRings, gapWidth);
-    markInvalidInteriorSegments(targetRings, m_adjPolygons);
+    markInvalidInteriorSegments(targetRings, m_adjCovPolygons);
 }
 
-
 /* private static */
 std::vector<const Polygon*>
 CoveragePolygonValidator::extractPolygons(std::vector<const Geometry*>& geoms)
@@ -246,85 +256,63 @@ CoveragePolygonValidator::markInvalidInteractingSegments(
 void
 CoveragePolygonValidator::markInvalidInteriorSegments(
     std::vector<CoverageRing*>& targetRings,
-    std::vector<const Polygon*>& adjPolygons)
+    std::vector<std::unique_ptr<CoveragePolygon>>& adjCovPolygons )
 {
     for (CoverageRing* ring : targetRings) {
-        for (std::size_t i = 0; i < ring->size() - 1; i++) {
-            //-- skip check for segments with known state.
-            if (ring->isKnown(i))
-                continue;
+        std::size_t stride = 1000;  //--  RING_SECTION_STRIDE;
+        for (std::size_t i = 0; i < ring->size() - 1; i += stride) {
+            std::size_t iEnd = i + stride;
+            if (iEnd >= ring->size())
+                iEnd = ring->size() - 1;
+            markInvalidInteriorSection(*ring, i, iEnd, adjCovPolygons);
+        }
+    }
+}
 
-            /**
-             * Check if vertex is in interior of an adjacent polygon.
-             * If so, the segments on either side are in the interior.
-             * Mark them invalid, unless they are already matched.
-             */
-            const Coordinate& p = ring->getCoordinate(i);
-            if (isInteriorVertex(p, adjPolygons)) {
-                ring->markInvalid(i);
-                //-- previous segment may be interior (but may also be matched)
-                std::size_t iPrev = i == 0 ? ring->size()-2 : i-1;
-                if (! ring->isKnown(iPrev))
-                    ring->markInvalid(iPrev);
+/* private */
+void
+CoveragePolygonValidator::markInvalidInteriorSection(
+    CoverageRing& ring,
+    std::size_t iStart, 
+    std::size_t iEnd, 
+    std::vector<std::unique_ptr<CoveragePolygon>>& adjCovPolygons )
+{
+    Envelope sectionEnv = ring.getEnvelope(iStart, iEnd);
+    //TODO: is it worth indexing polygons?
+    for (auto& adjPoly : adjCovPolygons) {
+        if (adjPoly->intersectsEnv(sectionEnv)) {
+            //-- test vertices in section
+            for (std::size_t i = iStart; i < iEnd; i++) {
+                markInvalidInteriorSegment(ring, i, adjPoly.get());
             }
         }
     }
 }
 
-
 /* private */
-bool
-CoveragePolygonValidator::isInteriorVertex(
-    const Coordinate& p,
-    std::vector<const Polygon*>& adjPolygons)
+void 
+CoveragePolygonValidator::markInvalidInteriorSegment(
+    CoverageRing& ring, std::size_t i, CoveragePolygon* adjPoly)
 {
+    //-- skip check for segments with known state.
+    if (ring.isKnown(i))
+        return;
+
     /**
-     * There should not be too many adjacent polygons,
-     * and hopefully not too many segments with unknown status
-     * so a linear scan should not be too inefficient
+     * Check if vertex is in interior of an adjacent polygon.
+     * If so, the segments on either side are in the interior.
+     * Mark them invalid, unless they are already matched.
      */
-    //TODO: try a spatial index?
-    for (std::size_t i = 0; i < adjPolygons.size(); i++) {
-        const Polygon* adjPoly = adjPolygons[i];
-        if (polygonContainsPoint(i, adjPoly, p))
-            return true;
-    }
-    return false;
-}
-
-
-/* private */
-bool
-CoveragePolygonValidator::polygonContainsPoint(std::size_t index,
-    const Polygon* poly, const Coordinate& pt)
-{
-    if (! poly->getEnvelopeInternal()->intersects(pt))
-        return false;
-    IndexedPointInAreaLocator* pia = getLocator(index, poly);
-    return Location::INTERIOR == pia->locate(&pt);
-}
-
-
-/* private */
-IndexedPointInAreaLocator*
-CoveragePolygonValidator::getLocator(std::size_t index, const Polygon* poly)
-{
-    auto it = adjPolygonLocators.find(index);
-    // found locator already constructed
-    if (it != adjPolygonLocators.end()) {
-        return (it->second).get();
-    }
-    // construct new locator for this polygon
-    else {
-        IndexedPointInAreaLocator* ipia = new IndexedPointInAreaLocator(*poly);
-        adjPolygonLocators.emplace(std::piecewise_construct,
-              std::forward_as_tuple(index),
-              std::forward_as_tuple(ipia));
-        return ipia;
+    const CoordinateXY& p = ring.getCoordinate(i);
+    if (adjPoly->contains(p)) {
+        ring.markInvalid(i);
+        //-- previous segment may be interior (but may also be matched)
+        std::size_t iPrev = i == 0 ? ring.size()-2 : i-1;
+        if (! ring.isKnown(iPrev))
+            ring.markInvalid(iPrev);
     }
 }
 
-
 /* private */
 std::unique_ptr<Geometry>
 CoveragePolygonValidator::createInvalidLines(std::vector<CoverageRing*>& rings)
diff --git a/src/coverage/CoverageRing.cpp b/src/coverage/CoverageRing.cpp
index 8f258d9d5..9a331291f 100644
--- a/src/coverage/CoverageRing.cpp
+++ b/src/coverage/CoverageRing.cpp
@@ -73,6 +73,16 @@ CoverageRing::CoverageRing(const LinearRing* ring, bool isShell)
         algorithm::Orientation::isCCW(ring->getCoordinatesRO()) != isShell)
 {}
 
+/* public */ 
+geom::Envelope CoverageRing::getEnvelope(std::size_t start, std::size_t end) 
+{
+    geom::Envelope env;
+    for (std::size_t i = start; i < end; i++) {
+        env.expandToInclude(getCoordinate(i));
+    }
+    return env;
+}
+
 
 /* public */
 bool

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

Summary of changes:
 include/geos/coverage/CoveragePolygon.h          |  57 +++++++++++
 include/geos/coverage/CoveragePolygonValidator.h |  38 +++----
 include/geos/coverage/CoverageRing.h             |   2 +
 src/coverage/CoveragePolygon.cpp                 |  76 ++++++++++++++
 src/coverage/CoveragePolygonValidator.cpp        | 124 ++++++++++-------------
 src/coverage/CoverageRing.cpp                    |  10 ++
 6 files changed, 217 insertions(+), 90 deletions(-)
 create mode 100644 include/geos/coverage/CoveragePolygon.h
 create mode 100644 src/coverage/CoveragePolygon.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list