[geos-commits] [SCM] GEOS branch main updated. 6e25f52988427e80142f15d54e9b618a89e680d5

git at osgeo.org git at osgeo.org
Tue Apr 7 10:04:24 PDT 2026


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  6e25f52988427e80142f15d54e9b618a89e680d5 (commit)
      from  3cee790e4d6f87feb09864b1941cce6b3fb7fbaf (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 6e25f52988427e80142f15d54e9b618a89e680d5
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu Apr 2 10:48:57 2026 -0400

    Refactor PathString context management and unify Noder API for path handling
    
    Moves context data to PathString base class, makes computePathNodes() and getNodedPaths() non-pure virtuals in Noder, and simplifies GeometryNoder to use a unified interface for handling both curved and linear paths.

diff --git a/include/geos/noding/ArcNoder.h b/include/geos/noding/ArcNoder.h
index e5f46f7fe..ea1635bbf 100644
--- a/include/geos/noding/ArcNoder.h
+++ b/include/geos/noding/ArcNoder.h
@@ -42,12 +42,12 @@ public:
 
     std::vector<std::unique_ptr<SegmentString>> getNodedSubstrings() override;
 
-    virtual void computePathNodes(const std::vector<PathString*>& inputPaths) = 0;
+    void computePathNodes(const std::vector<PathString*>& inputPaths) override = 0;
 
-    virtual std::vector<std::unique_ptr<PathString>> getNodedPaths() = 0;
+    std::vector<std::unique_ptr<PathString>> getNodedPaths() override = 0;
 
 protected:
-    ArcIntersector* m_intersector;
+    ArcIntersector* m_intersector{nullptr};
 };
 
 }
\ No newline at end of file
diff --git a/include/geos/noding/ArcString.h b/include/geos/noding/ArcString.h
index e758b7307..86a23db39 100644
--- a/include/geos/noding/ArcString.h
+++ b/include/geos/noding/ArcString.h
@@ -32,10 +32,10 @@ public:
     explicit ArcString(std::vector<geom::CircularArc> arcs) : m_arcs(std::move(arcs)) {
     }
 
-    ArcString(std::vector<geom::CircularArc> arcs, const std::shared_ptr<const geom::CoordinateSequence>& seq, void* context)
-        : m_arcs(std::move(arcs)),
-          m_seq(seq),
-          m_context(context)
+    ArcString(std::vector<geom::CircularArc> arcs, const std::shared_ptr<const geom::CoordinateSequence>& seq, const void* p_context)
+        : PathString(p_context),
+          m_arcs(std::move(arcs)),
+          m_seq(seq)
     {}
 
     std::size_t getSize() const override {
@@ -67,7 +67,6 @@ public:
 protected:
     std::vector<geom::CircularArc> m_arcs;
     std::shared_ptr<const geom::CoordinateSequence> m_seq;
-    void* m_context;
 };
 
 }
\ No newline at end of file
diff --git a/include/geos/noding/GeometryNoder.h b/include/geos/noding/GeometryNoder.h
index 0938302a9..007010c35 100644
--- a/include/geos/noding/GeometryNoder.h
+++ b/include/geos/noding/GeometryNoder.h
@@ -25,10 +25,14 @@
 
 // Forward declarations
 namespace geos {
+namespace algorithm {
+class CircularArcIntersector;
+}
 namespace geom {
 class Geometry;
 }
 namespace noding {
+class ArcIntersectionAdder;
 class Noder;
 }
 }
@@ -56,13 +60,15 @@ private:
     const geom::Geometry& argGeom;
     const bool argGeomHasCurves;
 
+    std::unique_ptr<Noder> noder;
+    std::unique_ptr<algorithm::CircularArcIntersector> m_cai;
+    std::unique_ptr<ArcIntersectionAdder> m_aia;
+
     static void extractPathStrings(const geom::Geometry& g,
                                    std::vector<std::unique_ptr<PathString>>& to);
 
     Noder& getNoder();
 
-    std::unique_ptr<Noder> noder;
-
     std::unique_ptr<geom::Geometry> toGeometry(std::vector<std::unique_ptr<PathString>>& noded) const;
 
 };
diff --git a/include/geos/noding/NodableArcString.h b/include/geos/noding/NodableArcString.h
index 505cbbba6..47fea6825 100644
--- a/include/geos/noding/NodableArcString.h
+++ b/include/geos/noding/NodableArcString.h
@@ -23,7 +23,7 @@ namespace geos::noding {
 class GEOS_DLL NodableArcString : public ArcString, public NodablePath {
 
 public:
-    NodableArcString(std::vector<geom::CircularArc> arcs, const std::shared_ptr<const geom::CoordinateSequence>& coords, bool constructZ, bool constructM, void* context);
+    NodableArcString(std::vector<geom::CircularArc> arcs, const std::shared_ptr<const geom::CoordinateSequence>& coords, bool constructZ, bool constructM, const void* context);
 
     std::unique_ptr<ArcString> clone() const;
 
diff --git a/include/geos/noding/Noder.h b/include/geos/noding/Noder.h
index e41a80e50..ecb9e98f6 100644
--- a/include/geos/noding/Noder.h
+++ b/include/geos/noding/Noder.h
@@ -22,6 +22,7 @@
 // Forward declarations
 namespace geos {
 namespace noding {
+class PathString;
 class SegmentString;
 }
 }
@@ -65,6 +66,11 @@ public:
      */
     virtual std::vector<std::unique_ptr<SegmentString>> getNodedSubstrings() = 0;
 
+    virtual void computePathNodes(const std::vector<PathString*>& inputPaths);
+
+    virtual std::vector<std::unique_ptr<PathString>> getNodedPaths();
+
+
     virtual
     ~Noder() {}
 
diff --git a/include/geos/noding/PathString.h b/include/geos/noding/PathString.h
index 0de86c23a..d8aedde6e 100644
--- a/include/geos/noding/PathString.h
+++ b/include/geos/noding/PathString.h
@@ -30,12 +30,36 @@ namespace geos::noding {
 /// of a noding process.
 class GEOS_DLL PathString {
 public:
+    explicit PathString(const void* p_context = nullptr) : context(p_context) {}
+
     virtual ~PathString() = default;
 
     virtual std::size_t getSize() const = 0;
 
     virtual double getLength() const = 0;
 
+    /** \brief
+     * Gets the user-defined data for this segment string.
+     *
+     * @return the user-defined data
+     */
+    const void*
+    getData() const
+    {
+        return context;
+    }
+
+    /** \brief
+     * Sets the user-defined data for this segment string.
+     *
+     * @param data an Object containing user-defined data
+     */
+    void
+    setData(const void* data)
+    {
+        context = data;
+    }
+
     /// \brief
     /// Return a pointer to the CoordinateSequence associated
     /// with this PathString.
@@ -43,6 +67,10 @@ public:
 
     std::vector<PathString*>
     static toRawPointerVector(const std::vector<std::unique_ptr<PathString>> & segStrings);
+
+private:
+
+    const void* context;
 };
 
 }
diff --git a/include/geos/noding/SegmentString.h b/include/geos/noding/SegmentString.h
index d13c70fda..73676dfe1 100644
--- a/include/geos/noding/SegmentString.h
+++ b/include/geos/noding/SegmentString.h
@@ -60,35 +60,11 @@ public:
     /// @param newSeq coordinates of this SegmentString
     ///
     SegmentString(const void* newContext, const std::shared_ptr<const geom::CoordinateSequence>& newSeq)
-        :
-        seq(newSeq),
-        context(newContext)
+        : PathString(newContext),
+        seq(newSeq)
     {}
 
-    virtual
-    ~SegmentString() {}
-
-    /** \brief
-     * Gets the user-defined data for this segment string.
-     *
-     * @return the user-defined data
-     */
-    const void*
-    getData() const
-    {
-        return context;
-    }
-
-    /** \brief
-     * Sets the user-defined data for this segment string.
-     *
-     * @param data an Object containing user-defined data
-     */
-    void
-    setData(const void* data)
-    {
-        context = data;
-    }
+    ~SegmentString() override = default;
 
     std::size_t size() const {
         // FIXME: Remove this method, or make consistent with getSize
@@ -189,8 +165,6 @@ protected:
     std::shared_ptr<const geom::CoordinateSequence> seq;
 
 private:
-    const void* context;
-
     static int safeOctant(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1)
     {
         if(p0.equals2D(p1)) {
diff --git a/src/noding/GeometryNoder.cpp b/src/noding/GeometryNoder.cpp
index 208f5885f..0d31dcde9 100644
--- a/src/noding/GeometryNoder.cpp
+++ b/src/noding/GeometryNoder.cpp
@@ -158,28 +158,11 @@ GeometryNoder::getNoded()
         return argGeom.clone();
 
     std::vector<std::unique_ptr<PathString>> lineList;
-    std::vector<std::unique_ptr<PathString>> nodedEdges;
-
     extractPathStrings(argGeom, lineList);
 
-    if (argGeomHasCurves) {
-        ArcNoder& p_noder = static_cast<ArcNoder&>(getNoder());
-
-        algorithm::CircularArcIntersector cai(argGeom.getPrecisionModel());
-        ArcIntersectionAdder aia(cai);
-        p_noder.setArcIntersector(aia);
-
-        p_noder.computePathNodes(PathString::toRawPointerVector(lineList));
-        nodedEdges = p_noder.getNodedPaths();
-    } else {
-        Noder& p_noder = getNoder();
-        p_noder.computeNodes(SegmentString::toRawPointerVector(lineList));
-        auto nodedSegStrings = p_noder.getNodedSubstrings();
-        nodedEdges.resize(nodedSegStrings.size());
-        for (size_t i = 0; i < nodedSegStrings.size(); i++) {
-            nodedEdges[i] = std::move(nodedSegStrings[i]);
-        }
-    }
+    Noder& p_noder = getNoder();
+    p_noder.computePathNodes(PathString::toRawPointerVector(lineList));
+    auto nodedEdges = p_noder.getNodedPaths();
 
     std::unique_ptr<geom::Geometry> noded = toGeometry(nodedEdges);
 
@@ -203,6 +186,10 @@ GeometryNoder::getNoder()
         const geom::PrecisionModel* pm = argGeom.getFactory()->getPrecisionModel();
         if (argGeomHasCurves) {
             noder = std::make_unique<SimpleNoder>();
+
+            m_cai = std::make_unique<algorithm::CircularArcIntersector>(argGeom.getPrecisionModel());
+            m_aia = std::make_unique<ArcIntersectionAdder>(*m_cai);
+            detail::down_cast<SimpleNoder*>(noder.get())->setArcIntersector(*m_aia);
         } else {
             noder = std::make_unique<IteratedNoder>(pm);
         }
diff --git a/src/noding/NodableArcString.cpp b/src/noding/NodableArcString.cpp
index bbecddf99..488293ac3 100644
--- a/src/noding/NodableArcString.cpp
+++ b/src/noding/NodableArcString.cpp
@@ -31,8 +31,8 @@ pseudoAngleDiffCCW(double paStart, double pa) {
     return diff;
 }
 
-NodableArcString::NodableArcString(std::vector<geom::CircularArc> arcs, const std::shared_ptr<const geom::CoordinateSequence>& coords, bool constructZ, bool constructM, void* context) :
-    ArcString(std::move(arcs), coords, context),
+NodableArcString::NodableArcString(std::vector<geom::CircularArc> arcs, const std::shared_ptr<const geom::CoordinateSequence>& coords, bool constructZ, bool constructM, const void* p_context) :
+    ArcString(std::move(arcs), coords, p_context),
     m_constructZ(constructZ),
     m_constructM(constructM)
 {
@@ -144,6 +144,7 @@ NodableArcString::getNoded(std::vector<std::unique_ptr<ArcString>>& splitArcs) {
         const int orientation = toSplit.getOrientation();
 
         bool arcIsSplit = true;
+        const bool preserveControlPoint = true;
         std::vector<CoordinateXYZM> arcPoints;
         const auto it = m_adds.find(arcIndex);
         if (it == m_adds.end()) {
@@ -157,7 +158,7 @@ NodableArcString::getNoded(std::vector<std::unique_ptr<ArcString>>& splitArcs) {
             }
         }
 
-        if (!arcIsSplit) {
+        if (preserveControlPoint && !arcIsSplit) {
             // No nodes added, just copy the coordinates into the sequence.
             const geom::CoordinateSequence* srcSeq = m_arcs[arcIndex].getCoordinateSequence();
             std::size_t srcPos = m_arcs[arcIndex].getCoordinatePosition();
@@ -174,6 +175,8 @@ NodableArcString::getNoded(std::vector<std::unique_ptr<ArcString>>& splitArcs) {
             const CoordinateXYZM& p0 = arcPoints[i - 1];
             const CoordinateXYZM& p2 = arcPoints[i];
 
+            // TODO: Check if control point of original arc falls into this section,
+            // and use it instead of calculating a midpoint here?
             CoordinateXYZM p1(algorithm::CircularArcs::getMidpoint(p0, p2, center, radius, isCCW));
             p1.z = (p0.z + p2.z) / 2;
             p1.m = (p0.m + p2.m) / 2;
@@ -188,14 +191,17 @@ NodableArcString::getNoded(std::vector<std::unique_ptr<ArcString>>& splitArcs) {
             arcs.emplace_back(*dstSeq, dstPos, center, radius, orientation);
 
             // Finish the ArcString, start a new one.
-            splitArcs.push_back(std::make_unique<NodableArcString>(std::move(arcs), std::move(dstSeq), m_constructZ, m_constructM, nullptr));
-            dstSeq = std::make_unique<geom::CoordinateSequence>(0, m_constructZ, m_constructM);
-            arcs.clear();
+            const bool isSplitPoint = i != arcPoints.size() - 1;
+            if (isSplitPoint) {
+                splitArcs.push_back(std::make_unique<NodableArcString>(std::move(arcs), std::move(dstSeq), m_constructZ, m_constructM, getData()));
+                dstSeq = std::make_unique<geom::CoordinateSequence>(0, m_constructZ, m_constructM);
+                arcs.clear();
+            }
         }
     }
 
     if (!arcs.empty()) {
-        splitArcs.push_back(std::make_unique<NodableArcString>(std::move(arcs), std::move(dstSeq), m_constructZ, m_constructM, nullptr));
+        splitArcs.push_back(std::make_unique<NodableArcString>(std::move(arcs), std::move(dstSeq), m_constructZ, m_constructM, getData()));
     }
 }
 
diff --git a/src/noding/Noder.cpp b/src/noding/Noder.cpp
new file mode 100644
index 000000000..89119928e
--- /dev/null
+++ b/src/noding/Noder.cpp
@@ -0,0 +1,53 @@
+/**********************************************************************
+*
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2026 ISciences, LLC
+ *
+ * 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 <filesystem>
+#include <geos/noding/Noder.h>
+#include <geos/noding/PathString.h>
+#include <geos/noding/SegmentString.h>
+
+namespace geos::noding {
+
+void
+Noder::computePathNodes(const std::vector<PathString*>& inputPaths)
+{
+    std::vector<SegmentString*> segStrings(inputPaths.size());
+    for (std::size_t i = 0; i < inputPaths.size(); i++) {
+        auto* path = inputPaths[i];
+
+        if (auto* ss = dynamic_cast<SegmentString*>(path)) {
+            segStrings[i] = ss;
+        } else {
+            throw util::UnsupportedOperationException("Noder does not support curved paths");
+        }
+    }
+
+    computeNodes(segStrings);
+}
+
+std::vector<std::unique_ptr<PathString>>
+Noder::getNodedPaths()
+{
+    auto nodedSS = getNodedSubstrings();
+    std::vector<std::unique_ptr<PathString>> ret(nodedSS.size());
+    for (std::size_t i = 0; i < nodedSS.size(); i++) {
+        ret[i] = std::move(nodedSS[i]);
+    }
+    return ret;
+}
+
+
+
+
+}
\ No newline at end of file

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

Summary of changes:
 include/geos/noding/ArcNoder.h         |  6 ++--
 include/geos/noding/ArcString.h        |  9 +++---
 include/geos/noding/GeometryNoder.h    | 10 +++++--
 include/geos/noding/NodableArcString.h |  2 +-
 include/geos/noding/Noder.h            |  6 ++++
 include/geos/noding/PathString.h       | 28 ++++++++++++++++++
 include/geos/noding/SegmentString.h    | 32 ++------------------
 src/noding/GeometryNoder.cpp           | 27 +++++------------
 src/noding/NodableArcString.cpp        | 20 ++++++++-----
 src/noding/Noder.cpp                   | 53 ++++++++++++++++++++++++++++++++++
 10 files changed, 126 insertions(+), 67 deletions(-)
 create mode 100644 src/noding/Noder.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list