[geos-commits] [SCM] GEOS branch main updated. 0028dd486e18abb779d3c1d1dfeb1a3f34e4af5c

git at osgeo.org git at osgeo.org
Mon May 4 12:11:41 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  0028dd486e18abb779d3c1d1dfeb1a3f34e4af5c (commit)
      from  7ce71942d882ca8ed301181d03d4b31177c340d0 (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 0028dd486e18abb779d3c1d1dfeb1a3f34e4af5c
Author: Daniel Baston <dbaston at gmail.com>
Date:   Mon May 4 15:11:19 2026 -0400

    GeometrySplitter: Support splitting CompoundCurve and CircularString at point (#1433)

diff --git a/include/geos/algorithm/CircularArcs.h b/include/geos/algorithm/CircularArcs.h
index d8793d561..a32a197cb 100644
--- a/include/geos/algorithm/CircularArcs.h
+++ b/include/geos/algorithm/CircularArcs.h
@@ -16,11 +16,17 @@
 
 #include <geos/export.h>
 #include <geos/geom/Coordinate.h>
-#include <geos/geom/Envelope.h>
 
 #include <array>
 #include <optional>
 
+namespace geos {
+namespace geom {
+class CoordinateSequence;
+class Envelope;
+}
+}
+
 namespace geos {
 namespace algorithm {
 
@@ -46,6 +52,19 @@ public:
     /// Return the point defined by a circle center, radius, and angle
     static geom::CoordinateXY createPoint(const geom::CoordinateXY& center, double radius, double theta);
 
+    /** Interpolate Z/M values for a point added to an arc.
+     *
+     * @param seq CoordinateSequence containing the arc points
+     * @param i0 Index of the first point in the arc
+     * @param center The center point of the circle defining the arc
+     * @param isCCW Whether the arc is counterclockwise
+     * @param pt The point to interpolate Z/M values for
+     * @param z The interpolated Z value
+     * @param m The interpolated M value
+     */
+    static void interpolateZM(const geom::CoordinateSequence &seq, size_t i0, const geom::CoordinateXY &center, bool isCCW, geom::CoordinateXY &pt, double
+                              &z, double &m);
+
     /** Determines whether and where a circle intersects a line segment.
      *
      * @param center The center point of the circle
diff --git a/include/geos/geom/SimpleCurve.h b/include/geos/geom/SimpleCurve.h
index da24b0125..777ff27a7 100644
--- a/include/geos/geom/SimpleCurve.h
+++ b/include/geos/geom/SimpleCurve.h
@@ -101,6 +101,8 @@ public:
 
     bool isEmpty() const override;
 
+    std::unique_ptr<SimpleCurve> clone() const;
+
     std::unique_ptr<SimpleCurve> reverse() const;
 
 protected:
diff --git a/include/geos/operation/split/GeometrySplitter.h b/include/geos/operation/split/GeometrySplitter.h
index 7511940a8..08ee641c9 100644
--- a/include/geos/operation/split/GeometrySplitter.h
+++ b/include/geos/operation/split/GeometrySplitter.h
@@ -18,8 +18,8 @@
 #include <memory>
 
 namespace geos::geom {
+class Curve;
 class Geometry;
-class LineString;
 class Point;
 }
 
@@ -51,12 +51,14 @@ private:
     splitPolygonalWithEdge(const geom::Geometry& geom, const geom::Geometry& edge);
 
     static std::unique_ptr<geom::Geometry>
-    splitLineWithPoint(const geom::LineString& g, const geom::Point& point);
+    splitCurveWithPoint(const geom::Curve& g, const geom::Point& point);
 
     static std::unique_ptr<geom::Geometry>
     splitAtPoints(const geom::Geometry& geom, const geom::Geometry& splitPoints);
 
     class SplitWithPointTransformer;
+
+    static constexpr double POINT_TO_LINE_TOLERANCE = 1e-10;
 };
 
 }
diff --git a/include/geos/operation/split/SplitGeometryAtVertex.h b/include/geos/operation/split/SplitGeometryAtVertex.h
deleted file mode 100644
index 37c331cb9..000000000
--- a/include/geos/operation/split/SplitGeometryAtVertex.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/**********************************************************************
-*
- * 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.
- *
- **********************************************************************/
-
-#pragma once
-
-#include <geos/export.h>
-#include <memory>
-#include <utility>
-
-// Forward declarations
-namespace geos::geom {
-class CircularString;
-class CoordinateXY;
-class LineString;
-class SimpleCurve;
-}
-
-namespace geos::operation::split {
-
-class GEOS_DLL SplitGeometryAtVertex {
-
-public:
-    static std::pair<std::unique_ptr<geom::SimpleCurve>, std::unique_ptr<geom::SimpleCurve>>
-            splitSimpleCurveAtVertex(const geom::SimpleCurve& sc, std::size_t i);
-
-    static std::pair<std::unique_ptr<geom::LineString>, std::unique_ptr<geom::LineString>>
-            splitLineStringAtVertex(const geom::LineString& ls, std::size_t i);
-
-    static std::pair<std::unique_ptr<geom::CircularString>, std::unique_ptr<geom::CircularString>>
-            splitCircularStringAtVertex(const geom::CircularString& cs, std::size_t i);
-
-    static std::pair<std::unique_ptr<geom::LineString>, std::unique_ptr<geom::LineString>>
-            splitLineStringAtPoint(const geom::LineString& ls, std::size_t i, const geom::CoordinateXY& pt);
-
-};
-
-}
diff --git a/include/geos/operation/split/SplitLinealAtPoint.h b/include/geos/operation/split/SplitLinealAtPoint.h
new file mode 100644
index 000000000..a5e1be445
--- /dev/null
+++ b/include/geos/operation/split/SplitLinealAtPoint.h
@@ -0,0 +1,90 @@
+/**********************************************************************
+*
+ * 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.
+ *
+ **********************************************************************/
+
+#pragma once
+
+#include <geos/export.h>
+#include <memory>
+#include <utility>
+
+// Forward declarations
+namespace geos::geom {
+class CircularString;
+class CompoundCurve;
+class CoordinateXY;
+class Curve;
+class LineString;
+class SimpleCurve;
+}
+
+namespace geos::operation::split {
+
+class GEOS_DLL SplitLinealAtPoint {
+
+public:
+    /** Split a curve into two sections at a specified vertex.
+     *
+     * If the vertex is the first or last vertex of the geometry, one of the returned
+     * geometries will be empty.
+     *
+     * If the input geometry is a CircularString, the vertex must be an endpoint of an arc.
+     *
+     * @param sc the curve to split
+     * @param i the index of the vertex to split at.
+     * @return a pair of split geometries.
+     */
+    static std::pair<std::unique_ptr<geom::SimpleCurve>, std::unique_ptr<geom::SimpleCurve>>
+    splitSimpleCurveAtVertex(const geom::SimpleCurve& sc, std::size_t i);
+
+    /** Split a SimpleCurve into two sections at an arbitrary point.
+     *
+     * @param sc the curve to split
+     * @param i the index of the vertex of the section (segment or arc) that contains pt
+     * @param pt the splitting point
+     * @return a pair of split geometries.
+     */
+    static std::pair<std::unique_ptr<geom::SimpleCurve>, std::unique_ptr<geom::SimpleCurve>>
+    splitSimpleCurveAtPoint(const geom::SimpleCurve &sc, std::size_t i, const geom::CoordinateXY &pt);
+
+    /** Split a CompoundCurve into two sections at an arbitrary point.
+     *
+     * @param sc the curve to split
+     * @param i the component that contains the splitting vertex
+     * @param j the index of the vertex of the section (segment or arc) that contains pt
+     * @param pt the splitting point
+     * @return a pair of split geometries.
+     */
+    static std::pair<std::unique_ptr<geom::Curve>, std::unique_ptr<geom::Curve>>
+    splitCompoundCurveAtPoint(const geom::CompoundCurve &sc, std::size_t i, std::size_t j, const geom::CoordinateXY &pt);
+
+    /** Split a CircularString into two sections at an arbitrary point. */
+    static std::pair<std::unique_ptr<geom::CircularString>, std::unique_ptr<geom::CircularString>>
+    splitCircularStringAtPoint(const geom::CircularString &ls, std::size_t i, const geom::CoordinateXY &pt);
+
+    /** Split CircularString into two sections at vertex i.
+     *  The vertex must be an endpoint of an arc. */
+    static std::pair<std::unique_ptr<geom::CircularString>, std::unique_ptr<geom::CircularString>>
+    splitCircularStringAtVertex(const geom::CircularString &cs, std::size_t i);
+
+    /** Split a LineString into two sections at an arbitrary point. */
+    static std::pair<std::unique_ptr<geom::LineString>, std::unique_ptr<geom::LineString>>
+    splitLineStringAtPoint(const geom::LineString &ls, std::size_t i, const geom::CoordinateXY &pt);
+
+    /** Split LineString into two sections at vertex i */
+    static std::pair<std::unique_ptr<geom::LineString>, std::unique_ptr<geom::LineString>>
+    splitLineStringAtVertex(const geom::LineString &ls, std::size_t i);
+
+};
+
+}
diff --git a/include/geos/util/Assert.h b/include/geos/util/Assert.h
index 275de021e..06acf1bb5 100644
--- a/include/geos/util/Assert.h
+++ b/include/geos/util/Assert.h
@@ -39,6 +39,13 @@ public:
         isTrue(assertion, std::string());
     }
 
+    template<typename T>
+    static void
+    isNotNull(const T& ptr, const std::string& message)
+    {
+        isTrue(ptr != nullptr, message);
+    }
+
 
     static void equals(const geom::CoordinateXY& expectedValue,
                        const geom::CoordinateXY& actualValue,
diff --git a/src/algorithm/CircularArcIntersector.cpp b/src/algorithm/CircularArcIntersector.cpp
index 31a6eea7c..b5e6adcc3 100644
--- a/src/algorithm/CircularArcIntersector.cpp
+++ b/src/algorithm/CircularArcIntersector.cpp
@@ -25,109 +25,6 @@ using geos::geom::CircularArc;
 
 namespace geos::algorithm {
 
-static double
-interpolateValue(double a1, double a2, double frac)
-{
-    frac = std::clamp(frac, 0.0, 1.0);
-    if (std::isnan(a1)) {
-        return a2;
-    }
-    if (std::isnan(a2)) {
-        return a1;
-    }
-    return a1 + frac * (a2 - a1);
-}
-
-static void
-interpolateZM(const CircularArc& arc, const CoordinateXY& pt, double& z, double& m)
-{
-    using geom::Ordinate;
-
-    const CoordinateSequence& seq = *arc.getCoordinateSequence();
-    std::size_t i0 = arc.getCoordinatePosition();
-
-    // Read Z, M from control point
-    double z1, m1;
-    seq.applyAt(i0 + 1, [&z1, &m1](const auto& arcPt) {
-        z1 = arcPt.template get<Ordinate::Z>();
-        m1 = arcPt.template get<Ordinate::M>();
-    });
-    // Test point = control point?
-    // Take Z, M from the control point
-    if (arc.p1().equals2D(pt)) {
-        z = z1;
-        m = m1;
-        return;
-    }
-
-    // Read Z, M from start point
-    double z0, m0;
-    seq.applyAt(i0, [&z0, &m0](const auto& arcPt) {
-        z0 = arcPt.template get<Ordinate::Z>();
-        m0 = arcPt.template get<Ordinate::M>();
-    });
-    // Test point = start point?
-    // Take Z, M from the start point
-    if (arc.p0().equals2D(pt)) {
-        z = z0;
-        m = m0;
-        return;
-    }
-
-    // Read Z, M from end point
-    double z2, m2;
-    seq.applyAt(i0 + 2, [&z2, &m2](const auto& arcPt) {
-        z2 = arcPt.template get<Ordinate::Z>();
-        m2 = arcPt.template get<Ordinate::M>();
-    });
-    // Test point = end point?
-    // Take Z, M from the end point
-    if (arc.p2().equals2D(pt)) {
-        z = z2;
-        m = m2;
-        return;
-    }
-
-    double theta0 = arc.theta0();
-    const double theta1 = arc.theta1();
-    double theta2 = arc.theta2();
-    const double theta = CircularArcs::getAngle(pt, arc.getCenter());
-
-    if (!arc.isCCW()) {
-        std::swap(theta0, theta2);
-        std::swap(z0, z2);
-        std::swap(m0, m2);
-    }
-
-    if (std::isnan(z1)) {
-        // Interpolate between p0 /  p2
-        const double frac = Angle::fractionCCW(theta, theta0, theta2);
-        z = interpolateValue(z0, z2, frac);
-    } else if (Angle::isWithinCCW(theta, theta0, theta1)) {
-        // Interpolate between p0 / p1
-        const double frac = Angle::fractionCCW(theta, theta0, theta1);
-        z = interpolateValue(z0, z1, frac);
-    } else {
-        // Interpolate between p1 / p2
-        const double frac = Angle::fractionCCW(theta, theta1, theta2);
-        z = interpolateValue(z1, z2, frac);
-    }
-
-    if (std::isnan(m1)) {
-        // Interpolate between p0 /  p2
-        const double frac = Angle::fractionCCW(theta, theta0, theta2);
-        m = interpolateValue(m0, m2, frac);
-    } else if (Angle::isWithinCCW(theta, theta0, theta1)) {
-        // Interpolate between p0 / p1
-        const double frac = Angle::fractionCCW(theta, theta0, theta1);
-        m = interpolateValue(m0, m1, frac);
-    } else {
-        // Interpolate between p1 / p2
-        const double frac = Angle::fractionCCW(theta, theta1, theta2);
-        m = interpolateValue(m1, m2, frac);
-    }
-
-}
 
 // Interpolate the Z/M values of a point lying on the provided line segment
 static void
@@ -155,8 +52,8 @@ interpolateZM(const CircularArc& arc0, const CircularArc& arc1, geom::Coordinate
 
     double z0, m0;
     double z1, m1;
-    interpolateZM(arc0, pt, z0, m0);
-    interpolateZM(arc1, pt, z1, m1);
+    CircularArcs::interpolateZM(*arc0.getCoordinateSequence(), arc0.getCoordinatePosition(), arc0.getCenter(), arc0.isCCW(), pt, z0, m0);
+    CircularArcs::interpolateZM(*arc1.getCoordinateSequence(), arc1.getCoordinatePosition(), arc1.getCenter(), arc1.isCCW(), pt, z1, m1);
 
     if (std::isnan(pt.z)) {
         pt.z = Interpolate::getOrAverage(z0, z1);
@@ -179,7 +76,7 @@ interpolateZM(const CircularArc& arc0,
 
     double z0, m0;
     double z1, m1;
-    interpolateZM(arc0, pt, z0, m0);
+    CircularArcs::interpolateZM(*arc0.getCoordinateSequence(), arc0.getCoordinatePosition(), arc0.getCenter(), arc0.isCCW(), pt, z0, m0);
     interpolateSegmentZM(seq, ind0, ind1, pt, z1, m1);
 
     if (std::isnan(pt.z)) {
@@ -191,7 +88,6 @@ interpolateZM(const CircularArc& arc0,
 }
 
 
-
 bool
 CircularArcIntersector::hasIntersection(const geom::CoordinateXY &p) const {
     switch (nPt) {
diff --git a/src/algorithm/CircularArcs.cpp b/src/algorithm/CircularArcs.cpp
index ee28c96c4..eeb779e2b 100644
--- a/src/algorithm/CircularArcs.cpp
+++ b/src/algorithm/CircularArcs.cpp
@@ -20,6 +20,7 @@
 #include <geos/geom/Quadrant.h>
 #include <geos/math/DD.h>
 
+using geos::geom::CoordinateSequence;
 using geos::geom::CoordinateXY;
 using geos::geom::Envelope;
 using geos::geom::LineSegment;
@@ -75,6 +76,112 @@ CircularArcs::createPoint(const CoordinateXY& center, double radius, double thet
     return { center.x + radius* std::cos(theta), center.y + radius* std::sin(theta) };
 }
 
+static double
+interpolateValue(double a1, double a2, double frac)
+{
+    frac = std::clamp(frac, 0.0, 1.0);
+    if (std::isnan(a1)) {
+        return a2;
+    }
+    if (std::isnan(a2)) {
+        return a1;
+    }
+    return a1 + frac * (a2 - a1);
+}
+
+void
+CircularArcs::interpolateZM(const CoordinateSequence& seq, std::size_t i0,
+                            const CoordinateXY &center, bool isCCW,
+                            CoordinateXY &pt, double &z, double &m)
+{
+    using geom::Ordinate;
+
+    const CoordinateXY& p0 = seq.getAt<CoordinateXY>(i0);
+    const CoordinateXY& p1 = seq.getAt<CoordinateXY>(i0 + 1);
+    const CoordinateXY& p2 = seq.getAt<CoordinateXY>(i0 + 2);
+
+    // Read Z, M from control point
+    double z1, m1;
+    seq.applyAt(i0 + 1, [&z1, &m1](const auto& arcPt) {
+        z1 = arcPt.template get<Ordinate::Z>();
+        m1 = arcPt.template get<Ordinate::M>();
+    });
+    // Test point = control point?
+    // Take Z, M from the control point
+    if (p1.equals2D(pt)) {
+        z = z1;
+        m = m1;
+        return;
+    }
+
+    // Read Z, M from start point
+    double z0, m0;
+    seq.applyAt(i0, [&z0, &m0](const auto& arcPt) {
+        z0 = arcPt.template get<Ordinate::Z>();
+        m0 = arcPt.template get<Ordinate::M>();
+    });
+    // Test point = start point?
+    // Take Z, M from the start point
+    if (p0.equals2D(pt)) {
+        z = z0;
+        m = m0;
+        return;
+    }
+
+    // Read Z, M from end point
+    double z2, m2;
+    seq.applyAt(i0 + 2, [&z2, &m2](const auto& arcPt) {
+        z2 = arcPt.template get<Ordinate::Z>();
+        m2 = arcPt.template get<Ordinate::M>();
+    });
+    // Test point = end point?
+    // Take Z, M from the end point
+    if (p2.equals2D(pt)) {
+        z = z2;
+        m = m2;
+        return;
+    }
+
+    double theta0 = getAngle(p0, center);
+    const double theta1 = getAngle(p1, center);
+    double theta2 = getAngle(p2, center);
+    const double theta = getAngle(pt, center);
+
+    if (!isCCW) {
+        std::swap(theta0, theta2);
+        std::swap(z0, z2);
+        std::swap(m0, m2);
+    }
+
+    if (std::isnan(z1)) {
+        // Interpolate between p0 /  p2
+        const double frac = Angle::fractionCCW(theta, theta0, theta2);
+        z = interpolateValue(z0, z2, frac);
+    } else if (Angle::isWithinCCW(theta, theta0, theta1)) {
+        // Interpolate between p0 / p1
+        const double frac = Angle::fractionCCW(theta, theta0, theta1);
+        z = interpolateValue(z0, z1, frac);
+    } else {
+        // Interpolate between p1 / p2
+        const double frac = Angle::fractionCCW(theta, theta1, theta2);
+        z = interpolateValue(z1, z2, frac);
+    }
+
+    if (std::isnan(m1)) {
+        // Interpolate between p0 /  p2
+        const double frac = Angle::fractionCCW(theta, theta0, theta2);
+        m = interpolateValue(m0, m2, frac);
+    } else if (Angle::isWithinCCW(theta, theta0, theta1)) {
+        // Interpolate between p0 / p1
+        const double frac = Angle::fractionCCW(theta, theta0, theta1);
+        m = interpolateValue(m0, m1, frac);
+    } else {
+        // Interpolate between p1 / p2
+        const double frac = Angle::fractionCCW(theta, theta1, theta2);
+        m = interpolateValue(m1, m2, frac);
+    }
+}
+
 template<typename T>
 CoordinateXY getCenterImpl(const CoordinateXY& p0, const CoordinateXY& p1, const CoordinateXY& p2)
 {
diff --git a/src/geom/CircularArc.cpp b/src/geom/CircularArc.cpp
index 7dfcbbf34..dd5a56d85 100644
--- a/src/geom/CircularArc.cpp
+++ b/src/geom/CircularArc.cpp
@@ -14,6 +14,7 @@
 
 #include <geos/algorithm/CircularArcs.h>
 #include <geos/geom/CircularArc.h>
+#include <geos/geom/Envelope.h>
 #include <geos/triangulate/quadedge/TrianglePredicate.h>
 #include <sstream>
 
diff --git a/src/geom/CompoundCurve.cpp b/src/geom/CompoundCurve.cpp
index 70916aa9c..26f8d450f 100644
--- a/src/geom/CompoundCurve.cpp
+++ b/src/geom/CompoundCurve.cpp
@@ -20,7 +20,7 @@
 #include <geos/geom/CircularString.h>
 #include <geos/geom/GeometryFactory.h>
 #include <geos/operation/BoundaryOp.h>
-#include <geos/operation/split/SplitGeometryAtVertex.h>
+#include <geos/operation/split/SplitLinealAtPoint.h>
 #include <geos/util.h>
 
 namespace geos {
@@ -379,7 +379,7 @@ CompoundCurve::normalizeClosed()
     } else if (minInd == curves[minCurve]->getNumPoints() - 1) {
         finalCurve = std::move(curves[minCurve]);
     } else {
-        auto split = operation::split::SplitGeometryAtVertex::splitSimpleCurveAtVertex(*curves[minCurve], minInd);
+        auto split = operation::split::SplitLinealAtPoint::splitSimpleCurveAtVertex(*curves[minCurve], minInd);
         newCurves.push_back(std::move(split.second));
         finalCurve = std::move(split.first);
     }
diff --git a/src/geom/SimpleCurve.cpp b/src/geom/SimpleCurve.cpp
index 40fbf460b..14ff8e39a 100644
--- a/src/geom/SimpleCurve.cpp
+++ b/src/geom/SimpleCurve.cpp
@@ -330,6 +330,12 @@ SimpleCurve::isEmpty() const
     return points->isEmpty();
 }
 
+std::unique_ptr<SimpleCurve>
+SimpleCurve::clone() const
+{
+    return std::unique_ptr<SimpleCurve>(static_cast<SimpleCurve*>(cloneImpl()));
+}
+
 std::unique_ptr<SimpleCurve>
 SimpleCurve::reverse() const
 {
diff --git a/src/noding/NodableArcString.cpp b/src/noding/NodableArcString.cpp
index 488293ac3..d16acafd5 100644
--- a/src/noding/NodableArcString.cpp
+++ b/src/noding/NodableArcString.cpp
@@ -15,6 +15,8 @@
 #include <geos/noding/NodableArcString.h>
 #include <geos/algorithm/Angle.h>
 
+#include <algorithm>
+
 using geos::geom::CoordinateXYZM;
 using geos::geom::CircularArc;
 
diff --git a/src/operation/split/GeometrySplitter.cpp b/src/operation/split/GeometrySplitter.cpp
index 41cac82ce..a795d9f4f 100644
--- a/src/operation/split/GeometrySplitter.cpp
+++ b/src/operation/split/GeometrySplitter.cpp
@@ -15,6 +15,7 @@
 #include <geos/operation/split/GeometrySplitter.h>
 
 #include <geos/geom/CircularString.h>
+#include <geos/geom/CompoundCurve.h>
 #include <geos/geom/Geometry.h>
 #include <geos/geom/GeometryCollection.h>
 #include <geos/geom/GeometryFactory.h>
@@ -31,11 +32,13 @@
 #include <geos/operation/distance/DistanceOp.h>
 #include <geos/operation/distance/GeometryLocation.h>
 #include <geos/operation/polygonize/Polygonizer.h>
-#include <geos/operation/split/SplitGeometryAtVertex.h>
+#include <geos/operation/split/SplitLinealAtPoint.h>
 #include <geos/shape/random/RandomPointsBuilder.h>
+#include <geos/util/Assert.h>
 
 using geos::geom::CircularString;
 using geos::geom::CoordinateXY;
+using geos::geom::CompoundCurve;
 using geos::geom::Curve;
 using geos::geom::CurvePolygon;
 using geos::geom::Geometry;
@@ -45,6 +48,7 @@ using geos::geom::LineString;
 using geos::geom::MultiLineString;
 using geos::geom::MultiPoint;
 using geos::geom::Polygon;
+using geos::geom::SimpleCurve;
 using geos::geom::prep::PreparedGeometryFactory;
 using geos::geom::util::GeometryCombiner;
 using geos::geom::util::GeometryTransformer;
@@ -59,20 +63,27 @@ using geos::shape::random::RandomPointsBuilder;
 
 namespace geos::operation::split {
 
-class GeometrySplitter::SplitWithPointTransformer : public geom::util::GeometryTransformer {
+class GeometrySplitter::SplitWithPointTransformer : public GeometryTransformer {
 public:
-    SplitWithPointTransformer(const Point& pt) : m_pt(pt) {}
+    explicit SplitWithPointTransformer(const Point& pt) : m_pt(pt) {}
+
+protected:
+    std::unique_ptr<Geometry>
+    transformCircularString(const CircularString* geom, const Geometry* /*parent*/) override
+    {
+        return splitCurveWithPoint(*geom, m_pt);
+    }
 
     std::unique_ptr<Geometry>
-    transformCircularString(const CircularString*, const Geometry* /*parent*/) override
+    transformCompoundCurve(const CompoundCurve* geom, const Geometry* /*parent*/) override
     {
-        throw geos::util::UnsupportedOperationException("Splitting a CircularString with a point is not supported.");
+        return splitCurveWithPoint(*geom, m_pt);
     }
 
     std::unique_ptr<Geometry>
     transformLineString(const LineString* geom, const Geometry* /*parent*/) override
     {
-        return GeometrySplitter::splitLineWithPoint(*geom, m_pt);
+        return splitCurveWithPoint(*geom, m_pt);
     }
 
     std::unique_ptr<Geometry>
@@ -210,11 +221,10 @@ GeometrySplitter::split(const Geometry &geom, const Geometry &splitGeom)
     return splitLinealWithEdge(*toSplit, splitGeom);
 }
 
-std::unique_ptr<Geometry>
-GeometrySplitter::splitLineWithPoint(const geom::LineString& g, const Point& point)
-{
-    constexpr double tolerance = 1e-10;
 
+std::unique_ptr<Geometry>
+GeometrySplitter::splitCurveWithPoint(const Curve& g, const Point& point)
+{
     if (g.isEmpty()) {
         std::vector<std::unique_ptr<Geometry>> geoms;
         geoms.push_back(g.clone());
@@ -223,7 +233,7 @@ GeometrySplitter::splitLineWithPoint(const geom::LineString& g, const Point& poi
 
     DistanceOp distance(g, point);
 
-    if (distance.distance() > tolerance) {
+    if (distance.distance() > POINT_TO_LINE_TOLERANCE) {
         std::vector<std::unique_ptr<Geometry>> geoms;
         geoms.push_back(g.clone());
         return g.getFactory()->createGeometryCollection(std::move(geoms));
@@ -231,22 +241,26 @@ GeometrySplitter::splitLineWithPoint(const geom::LineString& g, const Point& poi
 
     const auto& nearestLoc = distance.nearestLocations()[0];
 
-    const auto* seq = detail::down_cast<const LineString*>(nearestLoc.getGeometryComponent())->getCoordinatesRO();
+    std::pair<std::unique_ptr<Curve>, std::unique_ptr<Curve>> split;
+    if (g.getGeometryTypeId() == geom::GEOS_COMPOUNDCURVE) {
+        const CompoundCurve& cc = static_cast<const CompoundCurve&>(g);
 
-    const CoordinateXY& p0 = seq->getAt<CoordinateXY>(nearestLoc.getSegmentIndex());
-    const CoordinateXY& p1 = seq->getAt<CoordinateXY>(nearestLoc.getSegmentIndex() + 1);
+        for (std::size_t i = 0 ; i < cc.getNumCurves(); i++) {
+            const auto* sc = cc.getCurveN(i);
 
-
-    std::pair<std::unique_ptr<LineString>, std::unique_ptr<LineString>> split;
-    if (nearestLoc.getCoordinate().equals2D(p0))  {
-        // no need to add a new point
-        split = SplitGeometryAtVertex::splitLineStringAtVertex(static_cast<const LineString&>(g), nearestLoc.getSegmentIndex());
-    } else if (nearestLoc.getCoordinate().equals2D(p1)) {
-        split = SplitGeometryAtVertex::splitLineStringAtVertex(static_cast<const LineString&>(g), nearestLoc.getSegmentIndex() + 1);
+            if (sc == nearestLoc.getGeometryComponent()) {
+                split = SplitLinealAtPoint::splitCompoundCurveAtPoint(cc, i, nearestLoc.getSegmentIndex(), nearestLoc.getCoordinate());
+                break;
+            }
+        }
     } else {
-        split = SplitGeometryAtVertex::splitLineStringAtPoint(static_cast<const LineString&>(g), nearestLoc.getSegmentIndex(), nearestLoc.getCoordinate());
+        const SimpleCurve& sc = static_cast<const SimpleCurve&>(g);
+        split = SplitLinealAtPoint::splitSimpleCurveAtPoint(sc, nearestLoc.getSegmentIndex(), nearestLoc.getCoordinate());
     }
 
+    util::Assert::isNotNull(split.first, "Split result first curve should not be null");
+    util::Assert::isNotNull(split.second, "Split result second curve should not be null");
+
     std::vector<std::unique_ptr<Geometry>> geoms;
     if (!split.first->isEmpty()) {
         geoms.push_back(std::move(split.first));
diff --git a/src/operation/split/SplitGeometryAtVertex.cpp b/src/operation/split/SplitGeometryAtVertex.cpp
deleted file mode 100644
index 512e6f009..000000000
--- a/src/operation/split/SplitGeometryAtVertex.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/**********************************************************************
-*
- * 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 <geos/operation/split/SplitGeometryAtVertex.h>
-
-#include <geos/algorithm/Interpolate.h>
-#include <geos/geom/LineString.h>
-#include <geos/geom/CircularString.h>
-#include <geos/geom/GeometryFactory.h>
-#include <geos/util/UnsupportedOperationException.h>
-
-using geos::geom::CoordinateSequence;
-using geos::geom::CoordinateXY;
-using geos::geom::CoordinateXYZM;
-using geos::geom::CircularString;
-using geos::geom::LineString;
-using geos::geom::SimpleCurve;
-using geos::geom::GeometryTypeId;
-
-namespace geos::operation::split {
-
-std::pair<std::unique_ptr<LineString>, std::unique_ptr<LineString>>
-SplitGeometryAtVertex::splitLineStringAtPoint(const LineString& ls, std::size_t i, const CoordinateXY& pt)
-{
-    const auto& gf = *ls.getFactory();
-    const CoordinateSequence& pts = *ls.getCoordinatesRO();
-
-    if (i + 1 >= pts.size()) {
-        throw util::IllegalArgumentException("Cannot split LineString at point beyond end");
-    }
-
-    auto pts1 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
-    auto pts2 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
-
-    CoordinateXYZM ptZM(pt);
-    if (pts.hasZ() || pts.hasM()) {
-        CoordinateXYZM prev;
-        CoordinateXYZM next;
-        pts.getAt(i, prev);
-        pts.getAt(i + 1, next);
-
-        ptZM.z = algorithm::Interpolate::zGetOrInterpolate(pt, prev, next);
-        ptZM.m = algorithm::Interpolate::mGetOrInterpolate(pt, prev, next);
-    }
-
-    pts1->add(pts, 0, i);
-    pts1->add(ptZM);
-
-    if (i < pts.size() - 1) {
-        pts2->add(ptZM);
-        pts2->add(pts, i + 1, pts.size() - 1);
-    }
-
-    return { gf.createLineString(pts1), gf.createLineString(pts2) };
-}
-
-std::pair<std::unique_ptr<LineString>, std::unique_ptr<LineString>>
-SplitGeometryAtVertex::splitLineStringAtVertex(const LineString& ls, std::size_t i)
-{
-    const auto& gf = *ls.getFactory();
-    const CoordinateSequence& pts = *ls.getCoordinatesRO();
-
-    auto pts1 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
-    auto pts2 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
-
-    if (i > 0) {
-        pts1->add(pts, 0, i);
-    }
-    if (i < pts.size() - 1) {
-        pts2->add(pts, i, pts.size() - 1);
-    }
-
-    return { gf.createLineString(pts1), gf.createLineString(pts2) };
-}
-
-std::pair<std::unique_ptr<CircularString>, std::unique_ptr<CircularString>>
-        SplitGeometryAtVertex::splitCircularStringAtVertex(const CircularString& cs, std::size_t i)
-{
-    const auto& gf = *cs.getFactory();
-    const CoordinateSequence& pts = *cs.getCoordinatesRO();
-
-    if (i % 2) {
-        throw util::IllegalArgumentException("Cannot split CircularString at arc control point");
-    }
-
-    auto pts1 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
-    auto pts2 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
-
-    if (i > 0) {
-        pts1->add(pts, 0, i);
-    }
-    if (i < pts.size() - 1) {
-        pts2->add(pts, i, pts.size() - 1);
-    }
-
-    return { gf.createCircularString(pts1), gf.createCircularString(pts2) };
-}
-
-std::pair<std::unique_ptr<SimpleCurve>, std::unique_ptr<SimpleCurve>>
-        SplitGeometryAtVertex::splitSimpleCurveAtVertex(const SimpleCurve& sc, std::size_t i)
-{
-    if (sc.getGeometryTypeId() == GeometryTypeId::GEOS_CIRCULARSTRING) {
-        return splitCircularStringAtVertex(static_cast<const CircularString&>(sc), i);
-    }
-    if (sc.getGeometryTypeId() == GeometryTypeId::GEOS_LINESTRING || sc.getGeometryTypeId() == GeometryTypeId::GEOS_LINEARRING) {
-        return splitLineStringAtVertex(static_cast<const LineString&>(sc), i);
-    }
-
-    throw util::UnsupportedOperationException("Unhandled type in SplitGeometryAtVertex::splitAtVertex");
-}
-
-}
\ No newline at end of file
diff --git a/src/operation/split/SplitLinealAtPoint.cpp b/src/operation/split/SplitLinealAtPoint.cpp
new file mode 100644
index 000000000..a3e230af7
--- /dev/null
+++ b/src/operation/split/SplitLinealAtPoint.cpp
@@ -0,0 +1,252 @@
+/**********************************************************************
+*
+ * 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 <geos/operation/split/SplitLinealAtPoint.h>
+
+#include <geos/algorithm/CircularArcs.h>
+#include <geos/algorithm/Interpolate.h>
+#include <geos/geom/LineString.h>
+#include <geos/geom/CircularString.h>
+#include <geos/geom/CompoundCurve.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/util/UnsupportedOperationException.h>
+
+using geos::algorithm::CircularArcs;
+using geos::geom::CircularArc;
+using geos::geom::CircularString;
+using geos::geom::CompoundCurve;
+using geos::geom::CoordinateSequence;
+using geos::geom::CoordinateXY;
+using geos::geom::CoordinateXYZM;
+using geos::geom::Curve;
+using geos::geom::Geometry;
+using geos::geom::GeometryTypeId;
+using geos::geom::LineString;
+using geos::geom::SimpleCurve;
+
+namespace geos::operation::split {
+
+std::pair<std::unique_ptr<LineString>, std::unique_ptr<LineString>>
+SplitLinealAtPoint::splitLineStringAtPoint(const LineString& ls, std::size_t i, const CoordinateXY& pt)
+{
+    const auto& gf = *ls.getFactory();
+    const CoordinateSequence& pts = *ls.getCoordinatesRO();
+
+    if (pt.equals2D(pts.getAt<CoordinateXY>(i))) {
+        return splitLineStringAtVertex(ls, i);
+    }
+
+    if (i + 1 >= pts.size()) {
+        throw util::IllegalArgumentException("Cannot split LineString at point beyond end");
+    }
+
+    if (pt.equals2D(pts.getAt<CoordinateXY>(i + 1))) {
+        return splitLineStringAtVertex(ls, i + 1);
+    }
+
+    auto pts1 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
+    auto pts2 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
+
+    CoordinateXYZM ptZM(pt);
+    if (pts.hasZ() || pts.hasM()) {
+        CoordinateXYZM prev;
+        CoordinateXYZM next;
+        pts.getAt(i, prev);
+        pts.getAt(i + 1, next);
+
+        ptZM.z = algorithm::Interpolate::zGetOrInterpolate(pt, prev, next);
+        ptZM.m = algorithm::Interpolate::mGetOrInterpolate(pt, prev, next);
+    }
+
+    pts1->add(pts, 0, i);
+    pts1->add(ptZM);
+
+    if (i < pts.size() - 1) {
+        pts2->add(ptZM);
+        pts2->add(pts, i + 1, pts.size() - 1);
+    }
+
+    return { gf.createLineString(pts1), gf.createLineString(pts2) };
+}
+
+std::pair<std::unique_ptr<CircularString>, std::unique_ptr<CircularString>>
+SplitLinealAtPoint::splitCircularStringAtPoint(const CircularString& cs, std::size_t i, const CoordinateXY& pt)
+{
+    const auto& gf = *cs.getFactory();
+    const CoordinateSequence& pts = *cs.getCoordinatesRO();
+
+    if (i % 2) {
+        throw util::IllegalArgumentException("Section index must be the start of an arc");
+    }
+
+    if (pt.equals2D(pts.getAt<CoordinateXY>(i))) {
+        return splitCircularStringAtVertex(cs, i);
+    }
+
+    if (i + 2 >= pts.size()) {
+        throw util::IllegalArgumentException("Cannot split CircularString at arc beyond end");
+    }
+
+    const CircularArc arc(pts, i);
+
+    if (pt.equals2D(arc.p2())) {
+        return splitCircularStringAtVertex(cs, i + 2);
+    }
+
+    auto pts1 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
+    auto pts2 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
+
+    CoordinateXYZM splitPointZM(pt);
+    if (pts.hasZ() || pts.hasM()) {
+        CircularArcs::interpolateZM(pts, i, arc.getCenter(), arc.isCCW(), splitPointZM, splitPointZM.z, splitPointZM.m);
+    }
+
+    CoordinateXYZM sec0Midpoint(CircularArcs::getMidpoint(pts.getAt<CoordinateXY>(i), splitPointZM, arc.getCenter(), arc.getRadius(), arc.isCCW()));
+    if (pts.hasZ() || pts.hasM()) {
+        CircularArcs::interpolateZM(pts, i, arc.getCenter(), arc.isCCW(), sec0Midpoint, sec0Midpoint.z, sec0Midpoint.m);
+    }
+    pts1->add(pts, 0, i);
+    pts1->add(sec0Midpoint);
+    pts1->add(splitPointZM);
+
+    CoordinateXYZM sec1Midpoint(CircularArcs::getMidpoint(splitPointZM, pts.getAt<CoordinateXY>(i + 2), arc.getCenter(), arc.getRadius(), arc.isCCW()));
+    if (pts.hasZ() || pts.hasM()) {
+        CircularArcs::interpolateZM(pts, i, arc.getCenter(), arc.isCCW(), sec1Midpoint, sec1Midpoint.z, sec1Midpoint.m);
+    }
+    pts2->add(splitPointZM);
+    pts2->add(sec1Midpoint);
+    pts2->add(pts, i + 2, pts.size() - 1);
+
+    return { gf.createCircularString(pts1), gf.createCircularString(pts2) };
+}
+
+std::pair<std::unique_ptr<LineString>, std::unique_ptr<LineString>>
+SplitLinealAtPoint::splitLineStringAtVertex(const LineString& ls, std::size_t i)
+{
+    const auto& gf = *ls.getFactory();
+    const CoordinateSequence& pts = *ls.getCoordinatesRO();
+
+    auto pts1 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
+    auto pts2 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
+
+    if (i > 0) {
+        pts1->add(pts, 0, i);
+    }
+    if (i < pts.size() - 1) {
+        pts2->add(pts, i, pts.size() - 1);
+    }
+
+    return { gf.createLineString(pts1), gf.createLineString(pts2) };
+}
+
+std::pair<std::unique_ptr<CircularString>, std::unique_ptr<CircularString>>
+        SplitLinealAtPoint::splitCircularStringAtVertex(const CircularString& cs, std::size_t i)
+{
+    const auto& gf = *cs.getFactory();
+    const CoordinateSequence& pts = *cs.getCoordinatesRO();
+
+    if (i % 2) {
+        throw util::IllegalArgumentException("Cannot split CircularString at arc control point");
+    }
+
+    auto pts1 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
+    auto pts2 = std::make_shared<CoordinateSequence>(0, pts.hasZ(), pts.hasM());
+
+    if (i > 0) {
+        pts1->add(pts, 0, i);
+    }
+    if (i < pts.size() - 1) {
+        pts2->add(pts, i, pts.size() - 1);
+    }
+
+    return { gf.createCircularString(pts1), gf.createCircularString(pts2) };
+}
+
+std::pair<std::unique_ptr<SimpleCurve>, std::unique_ptr<SimpleCurve>>
+        SplitLinealAtPoint::splitSimpleCurveAtVertex(const SimpleCurve& sc, std::size_t i)
+{
+    if (sc.getGeometryTypeId() == GeometryTypeId::GEOS_CIRCULARSTRING) {
+        return splitCircularStringAtVertex(static_cast<const CircularString&>(sc), i);
+    }
+    if (sc.getGeometryTypeId() == GeometryTypeId::GEOS_LINESTRING || sc.getGeometryTypeId() == GeometryTypeId::GEOS_LINEARRING) {
+        return splitLineStringAtVertex(static_cast<const LineString&>(sc), i);
+    }
+
+    throw util::UnsupportedOperationException("Unhandled type in SplitLinealAtPoint::splitAtVertex");
+}
+
+std::pair<std::unique_ptr<SimpleCurve>, std::unique_ptr<SimpleCurve>>
+SplitLinealAtPoint::splitSimpleCurveAtPoint(const SimpleCurve& sc, std::size_t i, const CoordinateXY& pt)
+{
+    if (sc.getGeometryTypeId() == GeometryTypeId::GEOS_CIRCULARSTRING) {
+        return splitCircularStringAtPoint(static_cast<const CircularString&>(sc), i, pt);
+    }
+    if (sc.getGeometryTypeId() == GeometryTypeId::GEOS_LINESTRING || sc.getGeometryTypeId() == GeometryTypeId::GEOS_LINEARRING) {
+        return splitLineStringAtPoint(static_cast<const LineString&>(sc), i, pt);
+    }
+
+    throw util::UnsupportedOperationException("Unhandled type in SplitLinealAtPoint::splitAtVertex");
+}
+
+template<typename T>
+void removeEmptyGeometries(T& geoms) {
+    geoms.erase(std::remove_if(geoms.begin(), geoms.end(), [](const auto& curve) { return curve->isEmpty(); }), geoms.end());
+}
+
+static std::unique_ptr<Curve>
+makeCurve(const geom::GeometryFactory& gfact, std::vector<std::unique_ptr<SimpleCurve>> curves)
+{
+    if (curves.size() == 1) {
+        return std::move(curves[0]);
+    }
+
+    removeEmptyGeometries(curves);
+
+    if (curves.size() == 1) {
+        return std::move(curves[0]);
+    }
+
+    return gfact.createCompoundCurve(std::move(curves));
+}
+
+std::pair<std::unique_ptr<Curve>, std::unique_ptr<Curve>>
+SplitLinealAtPoint::splitCompoundCurveAtPoint(const CompoundCurve& cc, std::size_t component, std::size_t section, const CoordinateXY& pt)
+{
+    const auto& gfact = *cc.getFactory();
+
+    std::vector<std::unique_ptr<SimpleCurve>> firstCurves;
+    std::vector<std::unique_ptr<SimpleCurve>> secondCurves;
+
+    for (std::size_t i = 0; i < cc.getNumCurves(); i++) {
+        const auto* sc = cc.getCurveN(i);
+
+        if (i < component) {
+            firstCurves.push_back(sc->clone());
+        } else if (i == component) {
+            auto split = splitSimpleCurveAtPoint(*sc, section, pt);
+            firstCurves.push_back(std::move(split.first));
+            secondCurves.push_back(std::move(split.second));
+        } else {
+            secondCurves.push_back(sc->clone());
+        }
+    }
+
+    std::pair<std::unique_ptr<Curve>, std::unique_ptr<Curve>> result;
+    result.first = makeCurve(gfact, std::move(firstCurves));
+    result.second = makeCurve(gfact, std::move(secondCurves));
+
+    return result;
+}
+
+}
\ No newline at end of file
diff --git a/tests/unit/operation/split/GeometrySplitterTest.cpp b/tests/unit/operation/split/GeometrySplitterTest.cpp
index 15a53d964..988644c10 100644
--- a/tests/unit/operation/split/GeometrySplitterTest.cpp
+++ b/tests/unit/operation/split/GeometrySplitterTest.cpp
@@ -606,13 +606,11 @@ void object::test<49>()
              );
 }
 
-#if 0
 template<>
 template<>
 void object::test<50>()
 {
     set_test_name("split CircularString with point");
-    // not implemented yet: need curve support in DistanceOp
 
     testSplit("CIRCULARSTRING (-5 0, -4 3, 4 3)",
               "POINT (0 5)",
@@ -624,18 +622,27 @@ template<>
 void object::test<51>()
 {
     set_test_name("split CompoundCurve with point on curve");
-    // not implemented yet: need curve support in DistanceOp
 
     testSplit("COMPOUNDCURVE(CIRCULARSTRING (-5 0, -4 3, 4 3), (4 3, 0 0))",
               "POINT (0 5)",
-              "GEOMETRYCOLLECTION (CIRCULARSTRING (-5 0, -3.5355339059327373 3.5355339059327378, 0 5), CIRCULARSTRING (0 5, 2.2360679774997902 4.47213595499958, 4 3))");
+              "GEOMETRYCOLLECTION (CIRCULARSTRING (-5 0, -3.5355339059327373 3.5355339059327378, 0 5), COMPOUNDCURVE(CIRCULARSTRING (0 5, 2.2360679774997902 4.47213595499958, 4 3), (4 3, 0 0)))");
+}
+
+template<>
+template<>
+void object::test<52>()
+{
+    set_test_name("split CompoundCurve with point on line");
+
+    testSplit("COMPOUNDCURVE(CIRCULARSTRING (-5 0, -4 3, 4 3), (4 3, 0 0))",
+              "POINT (2 1.5)",
+              "GEOMETRYCOLLECTION (COMPOUNDCURVE( CIRCULARSTRING (-5 0, -4 3, 4 3), (4 3, 2 1.5)), LINESTRING (2 1.5, 0 0))");
 }
-#endif
 
 #if 0
 template<>
 template<>
-void object::test<52>()
+void object::test<53>()
 {
     set_test_name("nearly-collapsed Polygon (QGIS test #1)");
     // void TestQgsGeometry::splitGeometry()
@@ -651,7 +658,7 @@ void object::test<52>()
 
 template<>
 template<>
-void object::test<53>()
+void object::test<54>()
 {
     set_test_name("Z values of split edge are not used in interpolation; QGIS test #2");
     // See https://github.com/qgis/QGIS/issues/33489
@@ -663,7 +670,7 @@ void object::test<53>()
 
 template<>
 template<>
-void object::test<54>()
+void object::test<55>()
 {
     set_test_name("split CompoundCurve at an existing vertex; QGIS test #3");
 
@@ -674,7 +681,7 @@ void object::test<54>()
 
 template<>
 template<>
-void object::test<55>()
+void object::test<56>()
 {
     set_test_name("Split self-intersecting LineString at points; adaptation of QGIS test #4");
 
@@ -687,7 +694,7 @@ void object::test<55>()
 #if 0
 template<>
 template<>
-void object::test<56>()
+void object::test<57>()
 {
     set_test_name("do not split on self-intersections; QGIS test #4");
 
@@ -705,7 +712,7 @@ void object::test<56>()
 
 template<>
 template<>
-void object::test<57>()
+void object::test<58>()
 {
     set_test_name("do not split on self-intersections; QGIS test #5");
 
@@ -717,7 +724,7 @@ void object::test<57>()
 
 template<>
 template<>
-void object::test<58>()
+void object::test<59>()
 {
     set_test_name("split LineString Z on existing vertex; QGIS test #6");
 
@@ -729,7 +736,7 @@ void object::test<58>()
 
 template<>
 template<>
-void object::test<59>()
+void object::test<60>()
 {
     // Should not crash - https://github.com/qgis/QGIS/issues/50948
     testSplit("LINESTRING ( -63294.10966012725839391 -79156.27234554117603693, -63290.25259721937618451 -79162.78533450335089583, -63290.25259721936890855 -79162.78533450335089583)",
@@ -739,7 +746,7 @@ void object::test<59>()
 
 template<>
 template<>
-void object::test<60>()
+void object::test<61>()
 {
     // Should not split the first part - https://github.com/qgis/QGIS/issues/54155
     testSplit("MULTILINESTRING((0 1, 1 0), (0 2, 2 0))",
@@ -749,7 +756,7 @@ void object::test<60>()
 
 template<>
 template<>
-void object::test<61>()
+void object::test<62>()
 {
     set_test_name("cannot split Polygon with point");
 
@@ -761,7 +768,7 @@ void object::test<61>()
 
 template<>
 template<>
-void object::test<62>()
+void object::test<63>()
 {
     set_test_name("cannot split CurvePolygon with point");
 
@@ -771,4 +778,37 @@ void object::test<62>()
     ensure_THROW(GeometrySplitter::split(*geom, *splitGeom), geos::util::IllegalArgumentException);
 }
 
+template<>
+template<>
+void object::test<64>()
+{
+    set_test_name("split LineString with disjoint point");
+
+    testSplit("LINESTRING (0 0, 10 0)",
+              "POINT (5 1)",
+              "GEOMETRYCOLLECTION (LINESTRING (0 0, 10 0))");
+}
+
+template<>
+template<>
+void object::test<65>()
+{
+    set_test_name("split LineString with empty point");
+
+    testSplit("LINESTRING (0 0, 10 0)",
+              "POINT EMPTY",
+              "GEOMETRYCOLLECTION (LINESTRING (0 0, 10 0))");
+}
+
+template<>
+template<>
+void object::test<66>()
+{
+    set_test_name("split LineString with invalid point");
+
+    testSplit("LINESTRING (0 0, 10 0)",
+              "POINT (6 NaN)",
+              "GEOMETRYCOLLECTION (LINESTRING (0 0, 10 0))");
+}
+
 }
diff --git a/tests/unit/operation/split/SplitGeometryAtVertexTest.cpp b/tests/unit/operation/split/SplitGeometryAtVertexTest.cpp
deleted file mode 100644
index 59af695d2..000000000
--- a/tests/unit/operation/split/SplitGeometryAtVertexTest.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-#include <tut/tut.hpp>
-#include <tut/tut_macros.hpp>
-#include <utility.h>
-
-#include <geos/geom/CircularString.h>
-#include <geos/geom/LineString.h>
-#include <geos/operation/split/SplitGeometryAtVertex.h>
-#include <geos/io/WKTReader.h>
-
-using geos::geom::CoordinateXY;
-using geos::geom::CircularString;
-using geos::geom::LineString;
-using geos::operation::split::SplitGeometryAtVertex;
-
-namespace tut {
-
-struct test_splitgeometryatvertex_data {
-    const geos::io::WKTReader reader_;
-};
-
-typedef test_group<test_splitgeometryatvertex_data, 255> group;
-typedef group::object object;
-
-group test_splitgeometryatvertextest_group("geos::operation::split::SplitGeometryAtVertex");
-
-template<>
-template<>
-void object::test<1>()
-{
-    set_test_name("Split LineString ZM at vertex");
-
-    auto input = reader_.read<LineString>("LINESTRING ZM (0 3 2 3, 5 8 3 4, 2 2 4 5, 6 1 5 6)");
-
-    {
-        auto splitAtStart = SplitGeometryAtVertex::splitSimpleCurveAtVertex(*input, 0);
-
-        ensure(splitAtStart.first->isEmpty());
-        ensure(splitAtStart.first->hasZ());
-        ensure(splitAtStart.first->hasM());
-        ensure(splitAtStart.second->equalsIdentical(input.get()));
-    }
-
-    {
-        auto splitAtEnd = SplitGeometryAtVertex::splitSimpleCurveAtVertex(*input, input->getNumPoints() - 1);
-
-        ensure(splitAtEnd.first->equalsIdentical(input.get()));
-        ensure(splitAtEnd.second->isEmpty());
-        ensure(splitAtEnd.second->hasZ());
-        ensure(splitAtEnd.second->hasM());
-    }
-
-    {
-        auto splitInMiddle = SplitGeometryAtVertex::splitSimpleCurveAtVertex(*input, 2);
-
-        auto expectedFirst = reader_.read("LINESTRING ZM (0 3 2 3, 5 8 3 4, 2 2 4 5)");
-        auto expectedSecond = reader_.read("LINESTRING ZM (2 2 4 5, 6 1 5 6)");
-
-        ensure(splitInMiddle.first->equalsIdentical(expectedFirst.get()));
-        ensure(splitInMiddle.second->equalsIdentical(expectedSecond.get()));
-    }
-}
-
-template<>
-template<>
-void object::test<2>()
-{
-    set_test_name("Split CircularString ZM at vertex");
-
-    auto input = reader_.read<CircularString>("CIRCULARSTRING ZM (-5 0 1 2, 0 5 2 3, 5 0 3 4, 10 -5 4 5, 15 0 5 6)");
-
-    {
-        auto splitAtStart = SplitGeometryAtVertex::splitSimpleCurveAtVertex(*input, 0);
-
-        ensure(splitAtStart.first->isEmpty());
-        ensure(splitAtStart.first->hasZ());
-        ensure(splitAtStart.first->hasM());
-        ensure(splitAtStart.second->equalsIdentical(input.get()));
-    }
-
-    {
-        auto splitAtEnd = SplitGeometryAtVertex::splitSimpleCurveAtVertex(*input, input->getNumPoints() - 1);
-
-        ensure(splitAtEnd.first->equalsIdentical(input.get()));
-        ensure(splitAtEnd.second->isEmpty());
-        ensure(splitAtEnd.second->hasZ());
-        ensure(splitAtEnd.second->hasM());
-    }
-
-    {
-        auto splitInMiddle = SplitGeometryAtVertex::splitSimpleCurveAtVertex(*input, 2);
-
-        auto expectedFirst = reader_.read("CIRCULARSTRING ZM (-5 0 1 2, 0 5 2 3, 5 0 3 4)");
-        auto expectedSecond = reader_.read("CIRCULARSTRING ZM (5 0 3 4, 10 -5 4 5, 15 0 5 6)");
-
-        ensure(splitInMiddle.first->equalsIdentical(expectedFirst.get()));
-        ensure(splitInMiddle.second->equalsIdentical(expectedSecond.get()));
-    }
-
-    ensure_THROW(SplitGeometryAtVertex::splitSimpleCurveAtVertex(*input, 1), geos::util::IllegalArgumentException);
-}
-
-template<>
-template<>
-void object::test<3>()
-{
-    set_test_name("Split LineString ZM at new point");
-
-    auto input = reader_.read<LineString>("LINESTRING ZM (0 3 2 3, 5 8 3 4, 2 2 4 5, 6 1 5 6)");
-
-    CoordinateXY pt{2, 3};
-
-    ensure_THROW(SplitGeometryAtVertex::splitLineStringAtPoint(*input, 3, pt), geos::util::IllegalArgumentException);
-
-    // Split first segment
-    {
-        auto [first, second] = SplitGeometryAtVertex::splitLineStringAtPoint(*input, 0, pt);
-
-        auto expectedFirst = reader_.read("LINESTRING ZM (0 3 2 3, 2 3 2.282842712474619 3.282842712474619)");
-        auto expectedSecond = reader_.read("LINESTRING ZM (2 3 2.282842712474619 3.282842712474619, 5 8 3 4, 2 2 4 5, 6 1 5 6)");
-
-        ensure_equals_exact_geometry_xyzm(first.get(), expectedFirst.get(), 0.0);
-        ensure_equals_exact_geometry_xyzm(second.get(), expectedSecond.get(), 0.0);
-    }
-
-    // Split second segment
-    {
-        auto [first, second] = SplitGeometryAtVertex::splitLineStringAtPoint(*input, 1, pt);
-
-        auto expectedFirst = reader_.read("LINESTRING ZM (0 3 2 3, 5 8 3 4, 2 3 3.8692269873603533 4.869226987360353)");
-        auto expectedSecond = reader_.read("LINESTRING ZM (2 3 3.8692269873603533 4.869226987360353, 2 2 4 5, 6 1 5 6)");
-
-        ensure_equals_exact_geometry_xyzm(first.get(), expectedFirst.get(), 0.0);
-        ensure_equals_exact_geometry_xyzm(second.get(), expectedSecond.get(), 0.0);
-    }
-
-
-}
-
-}
diff --git a/tests/unit/operation/split/SplitLinealAtPointTest.cpp b/tests/unit/operation/split/SplitLinealAtPointTest.cpp
new file mode 100644
index 000000000..8bf29cff7
--- /dev/null
+++ b/tests/unit/operation/split/SplitLinealAtPointTest.cpp
@@ -0,0 +1,243 @@
+#include <tut/tut.hpp>
+#include <tut/tut_macros.hpp>
+#include <utility.h>
+
+#include <geos/geom/CircularString.h>
+#include <geos/geom/LineString.h>
+#include <geos/operation/split/SplitLinealAtPoint.h>
+#include <geos/io/WKTReader.h>
+
+using geos::geom::CoordinateXY;
+using geos::geom::CircularString;
+using geos::geom::LineString;
+using geos::operation::split::SplitLinealAtPoint;
+
+namespace tut {
+
+struct test_splitlinealatpoint_data {
+    const geos::io::WKTReader reader_;
+};
+
+typedef test_group<test_splitlinealatpoint_data, 255> group;
+typedef group::object object;
+
+group test_splitlinealatpointtest_group("geos::operation::split::SplitLinealAtPoint");
+
+template<>
+template<>
+void object::test<1>()
+{
+    set_test_name("Split LineString ZM at vertex");
+
+    auto input = reader_.read<LineString>("LINESTRING ZM (0 3 2 3, 5 8 3 4, 2 2 4 5, 6 1 5 6)");
+
+    {
+        auto splitAtStart = SplitLinealAtPoint::splitSimpleCurveAtVertex(*input, 0);
+
+        ensure(splitAtStart.first->isEmpty());
+        ensure(splitAtStart.first->hasZ());
+        ensure(splitAtStart.first->hasM());
+        ensure(splitAtStart.second->equalsIdentical(input.get()));
+    }
+
+    {
+        auto splitAtEnd = SplitLinealAtPoint::splitSimpleCurveAtVertex(*input, input->getNumPoints() - 1);
+
+        ensure(splitAtEnd.first->equalsIdentical(input.get()));
+        ensure(splitAtEnd.second->isEmpty());
+        ensure(splitAtEnd.second->hasZ());
+        ensure(splitAtEnd.second->hasM());
+    }
+
+    {
+        auto splitInMiddle = SplitLinealAtPoint::splitSimpleCurveAtVertex(*input, 2);
+
+        auto expectedFirst = reader_.read("LINESTRING ZM (0 3 2 3, 5 8 3 4, 2 2 4 5)");
+        auto expectedSecond = reader_.read("LINESTRING ZM (2 2 4 5, 6 1 5 6)");
+
+        ensure(splitInMiddle.first->equalsIdentical(expectedFirst.get()));
+        ensure(splitInMiddle.second->equalsIdentical(expectedSecond.get()));
+    }
+}
+
+template<>
+template<>
+void object::test<2>()
+{
+    set_test_name("Split CircularString ZM at vertex");
+
+    auto input = reader_.read<CircularString>("CIRCULARSTRING ZM (-5 0 1 2, 0 5 2 3, 5 0 3 4, 10 -5 4 5, 15 0 5 6)");
+
+    {
+        auto splitAtStart = SplitLinealAtPoint::splitSimpleCurveAtVertex(*input, 0);
+
+        ensure(splitAtStart.first->isEmpty());
+        ensure(splitAtStart.first->hasZ());
+        ensure(splitAtStart.first->hasM());
+        ensure(splitAtStart.second->equalsIdentical(input.get()));
+    }
+
+    {
+        auto splitAtEnd = SplitLinealAtPoint::splitSimpleCurveAtVertex(*input, input->getNumPoints() - 1);
+
+        ensure(splitAtEnd.first->equalsIdentical(input.get()));
+        ensure(splitAtEnd.second->isEmpty());
+        ensure(splitAtEnd.second->hasZ());
+        ensure(splitAtEnd.second->hasM());
+    }
+
+    {
+        auto splitInMiddle = SplitLinealAtPoint::splitSimpleCurveAtVertex(*input, 2);
+
+        auto expectedFirst = reader_.read("CIRCULARSTRING ZM (-5 0 1 2, 0 5 2 3, 5 0 3 4)");
+        auto expectedSecond = reader_.read("CIRCULARSTRING ZM (5 0 3 4, 10 -5 4 5, 15 0 5 6)");
+
+        ensure(splitInMiddle.first->equalsIdentical(expectedFirst.get()));
+        ensure(splitInMiddle.second->equalsIdentical(expectedSecond.get()));
+    }
+
+    ensure_THROW(SplitLinealAtPoint::splitSimpleCurveAtVertex(*input, 1), geos::util::IllegalArgumentException);
+}
+
+template<>
+template<>
+void object::test<3>()
+{
+    set_test_name("Split LineString ZM at new point");
+
+    auto input = reader_.read<LineString>("LINESTRING ZM (0 3 2 3, 5 8 3 4, 2 2 4 5, 6 1 5 6)");
+
+    CoordinateXY pt{2, 3};
+
+    ensure_THROW(SplitLinealAtPoint::splitLineStringAtPoint(*input, 3, pt), geos::util::IllegalArgumentException);
+
+    // Split first segment
+    {
+        auto [first, second] = SplitLinealAtPoint::splitLineStringAtPoint(*input, 0, pt);
+
+        auto expectedFirst = reader_.read("LINESTRING ZM (0 3 2 3, 2 3 2.282842712474619 3.282842712474619)");
+        auto expectedSecond = reader_.read("LINESTRING ZM (2 3 2.282842712474619 3.282842712474619, 5 8 3 4, 2 2 4 5, 6 1 5 6)");
+
+        ensure_equals_exact_geometry_xyzm(first.get(), expectedFirst.get(), 0.0);
+        ensure_equals_exact_geometry_xyzm(second.get(), expectedSecond.get(), 0.0);
+    }
+
+    // Split second segment
+    {
+        auto [first, second] = SplitLinealAtPoint::splitLineStringAtPoint(*input, 1, pt);
+
+        auto expectedFirst = reader_.read("LINESTRING ZM (0 3 2 3, 5 8 3 4, 2 3 3.8692269873603533 4.869226987360353)");
+        auto expectedSecond = reader_.read("LINESTRING ZM (2 3 3.8692269873603533 4.869226987360353, 2 2 4 5, 6 1 5 6)");
+
+        ensure_equals_exact_geometry_xyzm(first.get(), expectedFirst.get(), 0.0);
+        ensure_equals_exact_geometry_xyzm(second.get(), expectedSecond.get(), 0.0);
+    }
+
+}
+
+template<>
+template<>
+void object::test<4>()
+{
+    set_test_name("Split CompoundCurve ZM at existing vertices");
+
+    auto input = reader_.read<geos::geom::CompoundCurve>("COMPOUNDCURVE ZM(CIRCULARSTRING ZM(2 8 1 2, 4 7 3 4, 1 9 5 6, 3 15 7 8, 8 16 9 10), (8 16 9 10, 8 10 11 12, 6 14 13 14, 4 12 15 16), CIRCULARSTRING (4 12 15 16, 4 10 17 18, 2 8 19 20))");
+
+    // Split at first point
+    {
+        auto [first, second] = SplitLinealAtPoint::splitCompoundCurveAtPoint(*input, 0, 0, CoordinateXY{2, 8});
+
+        ensure(first->isEmpty());
+        ensure(second->equalsIdentical(input.get()));
+    }
+
+    // Split at intermediate point of first curve
+    {
+        auto [first, second] = SplitLinealAtPoint::splitCompoundCurveAtPoint(*input, 0, 2, CoordinateXY{1, 9});
+
+        auto expectedFirst = reader_.read("CIRCULARSTRING ZM (2 8 1 2, 4 7 3 4, 1 9 5 6)");
+        ensure_equals_exact_geometry_xyzm(first.get(), expectedFirst.get(), 0.0);
+
+        auto expectedSecond = reader_.read("COMPOUNDCURVE ZM (CIRCULARSTRING ZM(1 9 5 6, 3 15 7 8, 8 16 9 10), (8 16 9 10, 8 10 11 12, 6 14 13 14, 4 12 15 16), CIRCULARSTRING (4 12 15 16, 4 10 17 18, 2 8 19 20))");
+        ensure_equals_exact_geometry_xyzm(second.get(), expectedSecond.get(), 0.0);
+    }
+
+    // Split at last point of first curve
+    {
+        auto [first, second] = SplitLinealAtPoint::splitCompoundCurveAtPoint(*input, 0, 2, CoordinateXY{8, 16});
+
+        ensure(first->equalsIdentical(input->getCurveN(0)));
+
+        auto expectedSecond = reader_.read("COMPOUNDCURVE ZM ((8 16 9 10, 8 10 11 12, 6 14 13 14, 4 12 15 16), CIRCULARSTRING (4 12 15 16, 4 10 17 18, 2 8 19 20))");
+        ensure_equals_exact_geometry_xyzm(second.get(), expectedSecond.get(), 0.0);
+    }
+
+    // Split at first point of second curve
+    {
+        auto [first, second] = SplitLinealAtPoint::splitCompoundCurveAtPoint(*input, 1, 0, CoordinateXY{8, 16});
+
+        ensure(first->equalsIdentical(input->getCurveN(0)));
+
+        auto expectedSecond = reader_.read("COMPOUNDCURVE ZM ((8 16 9 10, 8 10 11 12, 6 14 13 14, 4 12 15 16), CIRCULARSTRING (4 12 15 16, 4 10 17 18, 2 8 19 20))");
+        ensure_equals_exact_geometry_xyzm(second.get(), expectedSecond.get(), 0.0);
+    }
+
+    // Split at intermate point of second curve
+    {
+        auto [first, second] = SplitLinealAtPoint::splitCompoundCurveAtPoint(*input, 1, 1, CoordinateXY{6, 14});
+
+        auto expectedFirst = reader_.read("COMPOUNDCURVE ZM (CIRCULARSTRING ZM(2 8 1 2, 4 7 3 4, 1 9 5 6, 3 15 7 8, 8 16 9 10), (8 16 9 10, 8 10 11 12, 6 14 13 14))");
+        ensure_equals_exact_geometry_xyzm(first.get(), expectedFirst.get(), 0.0);
+
+        auto expectedSecond = reader_.read("COMPOUNDCURVE ((6 14 13 14, 4 12 15 16), CIRCULARSTRING (4 12 15 16, 4 10 17 18, 2 8 19 20))");
+        ensure_equals_exact_geometry_xyzm(second.get(), expectedSecond.get(), 0.0);
+    }
+
+    // Split at last point of second curve
+    {
+        auto [first, second] = SplitLinealAtPoint::splitCompoundCurveAtPoint(*input, 1, 3, CoordinateXY{4, 12});
+
+        auto expectedFirst = reader_.read("COMPOUNDCURVE (CIRCULARSTRING(2 8 1 2, 4 7 3 4, 1 9 5 6, 3 15 7 8, 8 16 9 10), (8 16 9 10, 8 10 11 12, 6 14 13 14, 4 12 15 16))");
+        ensure_equals_exact_geometry_xyzm(first.get(), expectedFirst.get(), 0.0);
+
+        ensure(second->equalsIdentical(input->getCurveN(2)));
+    }
+
+    // Split at first point of third curve
+    {
+        auto [first, second] = SplitLinealAtPoint::splitCompoundCurveAtPoint(*input, 2, 0, CoordinateXY{4, 12});
+
+        auto expectedFirst = reader_.read("COMPOUNDCURVE (CIRCULARSTRING(2 8 1 2, 4 7 3 4, 1 9 5 6, 3 15 7 8, 8 16 9 10), (8 16 9 10, 8 10 11 12, 6 14 13 14, 4 12 15 16))");
+        ensure_equals_exact_geometry_xyzm(first.get(), expectedFirst.get(), 0.0);
+
+        ensure(second->equalsIdentical(input->getCurveN(2)));
+    }
+
+    // Split at last point of third curve
+    {
+        auto [first, second] = SplitLinealAtPoint::splitCompoundCurveAtPoint(*input, 2, 2, CoordinateXY{2, 8});
+
+        ensure(first->equalsIdentical(input.get()));
+        ensure(second->isEmpty());
+    }
+
+}
+
+template<>
+template<>
+void object::test<5>()
+{
+    set_test_name("Split CompoundCurve ZM at new point");
+
+    auto input = reader_.read<geos::geom::CompoundCurve>("COMPOUNDCURVE ZM(CIRCULARSTRING ZM(2 8 1 2, 4 7 3 4, 1 9 5 6, 3 15 7 8, 8 16 9 10), (8 16 9 10, 8 10 11 12, 6 14 13 14, 4 12 15 16), CIRCULARSTRING (4 12 15 16, 4 10 17 18, 2 8 19 20))");
+
+    auto [first, second] = SplitLinealAtPoint::splitCompoundCurveAtPoint(*input, 0, 2, CoordinateXY{5, 16});
+
+    auto expectedFirst = reader_.read("CIRCULARSTRING ZM (2 8 1 2, 4 7 3 4, 1 9 5 6, 1.5502525316941682 13.328427124746192 6.33570524800221 7.33570524800221, 5 16 7.851489605544531 8.851489605544531)");
+    ensure_equals_exact_geometry_xyzm(first.get(), expectedFirst.get(), 0.0);
+
+    auto expectedSecond = reader_.read("COMPOUNDCURVE ZM (CIRCULARSTRING ZM (5 16 7.851489605544531 8.851489605544531, 6.5 16.20087712549569 8.425744802772266 9.425744802772266, 8 16 9 10), (8 16 9 10, 8 10 11 12, 6 14 13 14, 4 12 15 16), CIRCULARSTRING ZM (4 12 15 16, 4 10 17 18, 2 8 19 20)) ");
+    ensure_equals_exact_geometry_xyzm(second.get(), expectedSecond.get(), 0.0);
+}
+
+}

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

Summary of changes:
 include/geos/algorithm/CircularArcs.h              |  21 +-
 include/geos/geom/SimpleCurve.h                    |   2 +
 include/geos/operation/split/GeometrySplitter.h    |   6 +-
 .../geos/operation/split/SplitGeometryAtVertex.h   |  48 ----
 include/geos/operation/split/SplitLinealAtPoint.h  |  90 ++++++++
 include/geos/util/Assert.h                         |   7 +
 src/algorithm/CircularArcIntersector.cpp           | 110 +--------
 src/algorithm/CircularArcs.cpp                     | 107 +++++++++
 src/geom/CircularArc.cpp                           |   1 +
 src/geom/CompoundCurve.cpp                         |   4 +-
 src/geom/SimpleCurve.cpp                           |   6 +
 src/noding/NodableArcString.cpp                    |   2 +
 src/operation/split/GeometrySplitter.cpp           |  56 +++--
 src/operation/split/SplitGeometryAtVertex.cpp      | 123 ----------
 src/operation/split/SplitLinealAtPoint.cpp         | 252 +++++++++++++++++++++
 .../unit/operation/split/GeometrySplitterTest.cpp  |  72 ++++--
 .../operation/split/SplitGeometryAtVertexTest.cpp  | 139 ------------
 .../operation/split/SplitLinealAtPointTest.cpp     | 243 ++++++++++++++++++++
 18 files changed, 830 insertions(+), 459 deletions(-)
 delete mode 100644 include/geos/operation/split/SplitGeometryAtVertex.h
 create mode 100644 include/geos/operation/split/SplitLinealAtPoint.h
 delete mode 100644 src/operation/split/SplitGeometryAtVertex.cpp
 create mode 100644 src/operation/split/SplitLinealAtPoint.cpp
 delete mode 100644 tests/unit/operation/split/SplitGeometryAtVertexTest.cpp
 create mode 100644 tests/unit/operation/split/SplitLinealAtPointTest.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list