[geos-commits] [SCM] GEOS branch main updated. d56ef91b6b0345e021325b5ca8c0f00ad659758f

git at osgeo.org git at osgeo.org
Fri May 29 09:26:28 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  d56ef91b6b0345e021325b5ca8c0f00ad659758f (commit)
       via  ef7fe8bc4d51c2665571d1238444a54c96be3ee5 (commit)
       via  45a954af8c003a5fdca7d06200c3e5271a2af21d (commit)
       via  44a20b3920bec49311b505e5f3938b94309ff450 (commit)
      from  24ec89dc300e1f403dfb234506bb54a8c9f3e68c (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 d56ef91b6b0345e021325b5ca8c0f00ad659758f
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu May 21 13:35:52 2026 -0400

    LineMerger: support curved geometry types

diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in
index c7fb1d389..855ef4d36 100644
--- a/capi/geos_c.h.in
+++ b/capi/geos_c.h.in
@@ -5444,6 +5444,8 @@ extern GEOSGeometry GEOS_DLL *GEOSDensify(
 * values differ at the points where two lines are joined, the values
 * from one of the points will be selected.
 *
+* Curved geometry types are supported since GEOS 3.15.
+*
 * \param g The input linework
 * \return The merged linework.
 * Caller is responsible for freeing with GEOSGeom_destroy().
@@ -5479,6 +5481,8 @@ extern size_t GEOS_DLL *GEOSMinimumSpanningTree(const GEOSGeometry * const geoms
 * values differ at the points where two lines are joined, the values
 * from one of the points will be selected.
 *
+* Curved geometry types are supported since GEOS 3.15.
+*
 * \param g The input linework
 * \return The merged linework.
 * Caller is responsible for freeing with GEOSGeom_destroy().
diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index 9488b4be9..e5f9e93d1 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -2742,7 +2742,7 @@ extern "C" {
             LineMerger lmrgr;
             lmrgr.add(g);
 
-            auto lines = lmrgr.getMergedLineStrings();
+            auto lines = lmrgr.getMergedCurves();
 
             auto out = gf->buildGeometry(std::move(lines));
             out->setSRID(g->getSRID());
@@ -2802,7 +2802,7 @@ extern "C" {
             LineMerger lmrgr(true);
             lmrgr.add(g);
 
-            auto lines = lmrgr.getMergedLineStrings();
+            auto lines = lmrgr.getMergedCurves();
 
             auto out = gf->buildGeometry(std::move(lines));
             out->setSRID(g->getSRID());
diff --git a/include/geos/geom/util/CurveBuilder.h b/include/geos/geom/util/CurveBuilder.h
index 4309d73d5..b1dc0dcbb 100644
--- a/include/geos/geom/util/CurveBuilder.h
+++ b/include/geos/geom/util/CurveBuilder.h
@@ -58,6 +58,14 @@ public:
         return m_hasM;
     }
 
+    bool hasActiveSequence() const {
+        return m_pts != nullptr;
+    }
+
+    bool isCurved() const {
+        return m_isCurved;
+    }
+
     void setOutputLinearRing(bool outputLinearRing) {
         m_outputLinearRing = outputLinearRing;
     }
diff --git a/include/geos/operation/linemerge/EdgeString.h b/include/geos/operation/linemerge/EdgeString.h
index 5bacf8316..5b06665b6 100644
--- a/include/geos/operation/linemerge/EdgeString.h
+++ b/include/geos/operation/linemerge/EdgeString.h
@@ -32,6 +32,7 @@
 // Forward declarations
 namespace geos {
 namespace geom {
+class Curve;
 class GeometryFactory;
 class CoordinateSequence;
 class LineString;
@@ -56,7 +57,6 @@ class GEOS_DLL EdgeString {
 private:
     const geom::GeometryFactory* factory;
     std::vector<LineMergeDirectedEdge*> directedEdges;
-    std::unique_ptr<geom::CoordinateSequence> getCoordinates() const;
 public:
     /**
      * \brief
@@ -73,9 +73,9 @@ public:
     void add(LineMergeDirectedEdge* directedEdge);
 
     /**
-     * Converts this EdgeString into a LineString.
+     * Converts this EdgeString into a Curve.
      */
-    std::unique_ptr<geom::LineString> toLineString() const;
+    std::unique_ptr<geom::Curve> getGeometry() const;
 };
 
 } // namespace geos::operation::linemerge
diff --git a/include/geos/operation/linemerge/LineMergeEdge.h b/include/geos/operation/linemerge/LineMergeEdge.h
index b4deca4b6..f420c58d5 100644
--- a/include/geos/operation/linemerge/LineMergeEdge.h
+++ b/include/geos/operation/linemerge/LineMergeEdge.h
@@ -26,7 +26,7 @@
 // Forward declarations
 namespace geos {
 namespace geom {
-class LineString;
+class Curve;
 }
 }
 
@@ -41,18 +41,18 @@ namespace linemerge { // geos::operation::linemerge
  */
 class GEOS_DLL LineMergeEdge: public planargraph::Edge {
 private:
-    const geom::LineString* line;
+    const geom::Curve* line;
 public:
     /**
      * Constructs a LineMergeEdge with vertices given by the specified
-     * LineString.
+     * Curve.
      */
-    LineMergeEdge(const geom::LineString* newLine);
+    LineMergeEdge(const geom::Curve* newCurve);
 
     /**
-     * Returns the LineString specifying the vertices of this edge.
+     * Returns the Curve specifying the vertices of this edge.
      */
-    const geom::LineString* getLine() const;
+    const geom::Curve* getCurve() const;
 };
 
 
diff --git a/include/geos/operation/linemerge/LineMergeGraph.h b/include/geos/operation/linemerge/LineMergeGraph.h
index bf5c1f315..7421f155b 100644
--- a/include/geos/operation/linemerge/LineMergeGraph.h
+++ b/include/geos/operation/linemerge/LineMergeGraph.h
@@ -34,7 +34,7 @@
 // Forward declarations
 namespace geos {
 namespace geom {
-class LineString;
+class Curve;
 class Coordinate;
 }
 namespace planargraph {
@@ -74,13 +74,13 @@ public:
 
     /** \brief
      * Adds an Edge, DirectedEdges, and Nodes for the given
-     * LineString representation of an edge.
+     * LineString/CircularString/CompoundCurve representation of an edge.
      *
      * Empty lines or lines with all coordinates equal are not added.
      *
-     * @param lineString the linestring to add to the graph
+     * @param curve the curve to add to the graph
      */
-    void addEdge(const geom::LineString* lineString);
+    void addEdge(const geom::Curve* curve);
 
     ~LineMergeGraph() override;
 
diff --git a/include/geos/operation/linemerge/LineMerger.h b/include/geos/operation/linemerge/LineMerger.h
index a641838e4..1075f663c 100644
--- a/include/geos/operation/linemerge/LineMerger.h
+++ b/include/geos/operation/linemerge/LineMerger.h
@@ -80,7 +80,7 @@ private:
 
     bool isDirected;
 
-    std::vector<std::unique_ptr<geom::LineString>> mergedLineStrings;
+    std::vector<std::unique_ptr<geom::Curve>> mergedGeometries;
 
     std::vector<std::unique_ptr<EdgeString>> edgeStrings;
 
@@ -132,7 +132,9 @@ public:
      */
     std::vector<std::unique_ptr<geom::LineString>> getMergedLineStrings();
 
-    void add(const geom::LineString* lineString);
+    std::vector<std::unique_ptr<geom::Curve>> getMergedCurves();
+
+    void add(const geom::Curve* curve);
 
     // Declare type as noncopyable
     LineMerger(const LineMerger& other) = delete;
diff --git a/src/operation/linemerge/EdgeString.cpp b/src/operation/linemerge/EdgeString.cpp
index 88abfadee..54fbbae5c 100644
--- a/src/operation/linemerge/EdgeString.cpp
+++ b/src/operation/linemerge/EdgeString.cpp
@@ -24,8 +24,10 @@
 #include <geos/geom/GeometryFactory.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/LineString.h>
+#include <geos/geom/util/CurveBuilder.h>
 #include <geos/util.h>
 
+#include <cmath>
 #include <vector>
 
 
@@ -54,8 +56,38 @@ EdgeString::add(LineMergeDirectedEdge* directedEdge)
     directedEdges.push_back(directedEdge);
 }
 
-std::unique_ptr<CoordinateSequence>
-EdgeString::getCoordinates() const
+static void
+addCoordinates(geom::util::CurveBuilder& curveBuilder, const SimpleCurve* curve, bool forward)
+{
+    if (curve->isEmpty()) {
+        return;
+    }
+
+    const CoordinateSequence& srcCoords = *curve->getCoordinatesRO();
+
+    // Patch Z value in last coordinate, if needed
+    if (curveBuilder.hasActiveSequence() && curve->hasZ()) {
+        CoordinateSequence& dstCoords = curveBuilder.getSeq(curveBuilder.isCurved());
+        if (std::isnan(dstCoords.getZ(dstCoords.getSize() - 1))) {
+            dstCoords.setZ(dstCoords.getSize() - 1, forward ? srcCoords.getZ(0) : srcCoords.getZ(srcCoords.getSize() - 1));
+        }
+    }
+
+    // Patch M value in last coordinate, if needed
+    if (curveBuilder.hasActiveSequence() && curve->hasM()) {
+        CoordinateSequence& dstCoords = curveBuilder.getSeq(curveBuilder.isCurved());
+        if (std::isnan(dstCoords.getM(dstCoords.getSize() - 1))) {
+            dstCoords.setM(dstCoords.getSize() - 1, forward ? srcCoords.getM(0) : srcCoords.getM(srcCoords.getSize() - 1));
+        }
+    }
+
+    const bool isCurved = curve->getGeometryTypeId() == GEOS_CIRCULARSTRING;
+    CoordinateSequence& dstCoords = curveBuilder.getSeq(isCurved);
+    dstCoords.add(srcCoords, false, forward);
+}
+
+std::unique_ptr<Curve>
+EdgeString::getGeometry() const
 {
     int forwardDirectedEdges = 0;
     int reverseDirectedEdges = 0;
@@ -66,17 +98,17 @@ EdgeString::getCoordinates() const
     for (const LineMergeDirectedEdge* directedEdge : directedEdges) {
         const LineMergeEdge* lme = detail::down_cast<LineMergeEdge*>(directedEdge->getEdge());
 
-        resultHasZ |= lme->getLine()->hasZ();
-        resultHasM |= lme->getLine()->hasM();
+        resultHasZ |= lme->getCurve()->hasZ();
+        resultHasM |= lme->getCurve()->hasM();
     }
 
-    auto coordinates = std::make_unique<CoordinateSequence>(0, resultHasZ, resultHasM);
-
-    bool lastPointMissingZ = false;
-    bool lastPointMissingM = false;
+    geom::util::CurveBuilder curveBuilder(*factory, resultHasZ, resultHasM);
+    curveBuilder.setOutputLinearRing(false);
 
     for (const LineMergeDirectedEdge* directedEdge : directedEdges) {
-        if(directedEdge->getEdgeDirection()) {
+        const bool isForward = directedEdge->getEdgeDirection();
+
+        if (isForward) {
             forwardDirectedEdges++;
         }
         else {
@@ -84,39 +116,33 @@ EdgeString::getCoordinates() const
         }
 
         const LineMergeEdge* lme = detail::down_cast<LineMergeEdge*>(directedEdge->getEdge());
-        const CoordinateSequence* seq = lme->getLine()->getCoordinatesRO();
+        const Curve* curve = lme->getCurve();
 
-        if (lastPointMissingZ && seq->hasZ()) {
-            const double z = directedEdge->getEdgeDirection() ? seq->getZ(0) : seq->getZ(seq->getSize() - 1);
-            coordinates->setZ(coordinates->getSize() - 1, z);
+        if (curve->getGeometryTypeId() == geom::GEOS_COMPOUNDCURVE) {
+            if (isForward) {
+                for (std::size_t i = 0; i < curve->getNumCurves(); i++) {
+                    const SimpleCurve* sc = curve->getCurveN(i);
+                    addCoordinates(curveBuilder, sc, isForward);
+                }
+            } else {
+                for (std::size_t i = curve->getNumCurves(); i > 0; i--) {
+                    const SimpleCurve* sc = curve->getCurveN(i - 1);
+                    addCoordinates(curveBuilder, sc, isForward);
+                }
+            }
+        } else {
+            const SimpleCurve* sc = detail::down_cast<const SimpleCurve*>(curve);
+            addCoordinates(curveBuilder, sc, directedEdge->getEdgeDirection());
         }
-        if (lastPointMissingM && seq->hasM()) {
-            const double m = directedEdge->getEdgeDirection() ? seq->getM(0) : seq->getM(seq->getSize() - 1);
-            coordinates->setM(coordinates->getSize() - 1, m);
-        }
-
-        coordinates->add(*seq,
-                         false,
-                         directedEdge->getEdgeDirection());
-
-        lastPointMissingZ = resultHasZ && !seq->hasZ();
-        lastPointMissingM = resultHasM && !seq->hasM();
     }
 
+    auto result = curveBuilder.getGeometry();
+
     if(reverseDirectedEdges > forwardDirectedEdges) {
-        coordinates->reverse();
+        return result->reverse();
     }
 
-    return coordinates;
-}
-
-/*
- * Converts this EdgeString into a new LineString.
- */
-std::unique_ptr<LineString>
-EdgeString::toLineString() const
-{
-    return factory->createLineString(getCoordinates());
+    return result;
 }
 
 } // namespace geos.operation.linemerge
diff --git a/src/operation/linemerge/LineMergeEdge.cpp b/src/operation/linemerge/LineMergeEdge.cpp
index 0a7740436..fe62bdc7c 100644
--- a/src/operation/linemerge/LineMergeEdge.cpp
+++ b/src/operation/linemerge/LineMergeEdge.cpp
@@ -25,20 +25,13 @@ namespace geos {
 namespace operation { // geos.operation
 namespace linemerge { // geos.operation.linemerge
 
-/**
- * Constructs a LineMergeEdge with vertices given by the specified LineString.
- */
-LineMergeEdge::LineMergeEdge(const LineString* newLine):
-    line(newLine)
+LineMergeEdge::LineMergeEdge(const Curve* newCurve):
+    line(newCurve)
 {
-    //line=newLine;
 }
 
-/**
- * Returns the LineString specifying the vertices of this edge.
- */
-const LineString*
-LineMergeEdge::getLine() const
+const Curve*
+LineMergeEdge::getCurve() const
 {
     return line;
 }
diff --git a/src/operation/linemerge/LineMergeGraph.cpp b/src/operation/linemerge/LineMergeGraph.cpp
index f464ce435..7f12375cb 100644
--- a/src/operation/linemerge/LineMergeGraph.cpp
+++ b/src/operation/linemerge/LineMergeGraph.cpp
@@ -26,10 +26,15 @@
 #include <geos/planargraph/Node.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/LineString.h>
-#include <memory>
+#include <geos/geom/Point.h>
+#include <geos/util.h>
+#include <geos/util/Assert.h>
 
+#include <memory>
+#include <optional>
 #include <vector>
 
+
 #ifndef GEOS_DEBUG
 #define GEOS_DEBUG 0
 #endif
@@ -48,28 +53,70 @@ namespace linemerge { // geos.operation.linemerge
 
 LineMergeGraph::LineMergeGraph() = default;
 
-void
-LineMergeGraph::addEdge(const LineString* lineString)
+std::optional<const CoordinateXY*>
+getDirectionPoint(const Curve& curve, bool forward)
 {
-    if(lineString->isEmpty()) {
+    if (curve.isEmpty()) {
+        return std::nullopt;
+    }
+
+    if (curve.getGeometryTypeId() == GEOS_COMPOUNDCURVE) {
+        if (forward) {
+            for (std::size_t i = 0; i < curve.getNumCurves(); i++) {
+                const auto dirPt = getDirectionPoint(*curve.getCurveN(i), true);
+                if (dirPt.has_value()) {
+                    return dirPt;
+                }
+            }
+        } else {
+            for (std::size_t i = curve.getNumCurves(); i > 0; i--) {
+                const auto dirPt = getDirectionPoint(*curve.getCurveN(i - 1), false);
+                if (dirPt.has_value()) {
+                    return dirPt;
+                }
+            }
+        }
+    } else {
+        // Although the method claims to be providing a direction point, we don't actually need to order our edges
+        // around a node like we do for overlay. So we don't need to take the trouble of calculating a point along
+        // a tangent a CircularString -- we can just use the first point that is not equal to the origin. This
+        // also allows us to check the return value to make sure that the Curve is not collapsed to a single point.
+        const CoordinateSequence& seq = *detail::down_cast<const SimpleCurve*>(&curve)->getCoordinatesRO();
+        if (forward) {
+            const CoordinateXY& startPt = seq.front<CoordinateXY>();
+            for (std::size_t i = 1; i < seq.size(); i++) {
+                const CoordinateXY& pt = seq.getAt<CoordinateXY>(i);
+                if (!pt.equals2D(startPt)) {
+                    return &pt;
+                }
+            }
+        } else {
+            const CoordinateXY& endPt = seq.back<CoordinateXY>();
+            for (std::size_t i = seq.size(); i > 0; i--) {
+                const CoordinateXY& pt = seq.getAt<CoordinateXY>(i - 1);
+                if (!pt.equals2D(endPt)) {
+                    return &pt;
+                }
+            }
+        }
+    }
+
+    return std::nullopt;
+}
+
+void
+LineMergeGraph::addEdge(const Curve* curve)
+{
+    if(curve->isEmpty()) {
         return;
     }
 
 #if GEOS_DEBUG
-    std::cerr << "Adding LineString " << lineString->toString() << std::endl;
+    std::cerr << "Adding Curve " << curve->toString() << std::endl;
 #endif
 
-    auto coordinates = valid::RepeatedPointRemover::removeRepeatedPoints(lineString->getCoordinatesRO());
-
-    std::size_t nCoords = coordinates->size(); // virtual call..
-
-    // don't add lines with all coordinates equal
-    if(nCoords <= 1) {
-        return;
-    }
-
-    const CoordinateXY& startCoordinate = coordinates->getAt<CoordinateXY>(0);
-    const CoordinateXY& endCoordinate = coordinates->getAt<CoordinateXY>(nCoords - 1);
+    const CoordinateXY& startCoordinate = curve->getStartCoordinate();
+    const CoordinateXY& endCoordinate = curve->getEndCoordinate();
 
     planargraph::Node* startNode = getNode(startCoordinate);
     planargraph::Node* endNode = getNode(endCoordinate);
@@ -78,15 +125,22 @@ LineMergeGraph::addEdge(const LineString* lineString)
     std::cerr << " endNode: " << *endNode << std::endl;
 #endif
 
-    const CoordinateXY& dirPt0 = coordinates->getAt<CoordinateXY>(1);
-    newDirEdges.push_back(std::make_unique<LineMergeDirectedEdge>(startNode, endNode, dirPt0, true));
+    const auto dirPt0 = getDirectionPoint(*curve, true);
+
+    if (!dirPt0.has_value()) {
+        // edge has < 2 unique coordinates
+        return;
+    }
+
+    newDirEdges.push_back(std::make_unique<LineMergeDirectedEdge>(startNode, endNode, *dirPt0.value(), true));
     auto* directedEdge0 = newDirEdges.back().get();
 
-    const CoordinateXY& dirPt1 = coordinates->getAt<CoordinateXY>(nCoords - 2);
-    newDirEdges.push_back(std::make_unique<LineMergeDirectedEdge>(endNode, startNode, dirPt1, false));
+    const auto dirPt1 = getDirectionPoint(*curve, false);
+    util::Assert::isTrue(dirPt1.has_value(), "dirPt1 should have been set");
+    newDirEdges.push_back(std::make_unique<LineMergeDirectedEdge>(endNode, startNode, *dirPt1.value(), false));
     auto* directedEdge1 = newDirEdges.back().get();
 
-    newEdges.push_back(std::make_unique<LineMergeEdge>(lineString));
+    newEdges.push_back(std::make_unique<LineMergeEdge>(curve));
     planargraph::Edge* edge = newEdges.back().get();
     edge->setDirectedEdges(directedEdge0, directedEdge1);
 
diff --git a/src/operation/linemerge/LineMerger.cpp b/src/operation/linemerge/LineMerger.cpp
index 23d79fcdf..431fbd2e2 100644
--- a/src/operation/linemerge/LineMerger.cpp
+++ b/src/operation/linemerge/LineMerger.cpp
@@ -71,7 +71,7 @@ struct LMGeometryComponentFilter: public GeometryComponentFilter {
     void
     filter_ro(const Geometry* geom) override
     {
-        const LineString* ls = dynamic_cast<const LineString*>(geom);
+        const Curve* ls = dynamic_cast<const Curve*>(geom);
         if(ls) {
             lm->add(ls);
         }
@@ -87,25 +87,23 @@ struct LMGeometryComponentFilter: public GeometryComponentFilter {
 void
 LineMerger::add(const Geometry* geometry)
 {
-    util::ensureNoCurvedComponents(geometry);
-
     LMGeometryComponentFilter lmgcf(this);
     geometry->apply_ro(&lmgcf);
 }
 
 void
-LineMerger::add(const LineString* lineString)
+LineMerger::add(const Curve* curve)
 {
     if(factory == nullptr) {
-        factory = lineString->getFactory();
+        factory = curve->getFactory();
     }
-    graph.addEdge(lineString);
+    graph.addEdge(curve);
 }
 
 void
 LineMerger::merge()
 {
-    if(!mergedLineStrings.empty()) {
+    if(!mergedGeometries.empty()) {
         return;
     }
 
@@ -121,10 +119,10 @@ LineMerger::merge()
     buildEdgeStringsForIsolatedLoops();
 
     auto numEdgeStrings = edgeStrings.size();
-    mergedLineStrings.reserve(numEdgeStrings);
+    mergedGeometries.reserve(numEdgeStrings);
 
     for(const auto& edgeString : edgeStrings) {
-        mergedLineStrings.emplace_back(edgeString->toLineString());
+        mergedGeometries.emplace_back(edgeString->getGeometry());
     }
 }
 
@@ -236,11 +234,27 @@ LineMerger::getMergedLineStrings()
     merge();
 
     // Explicitly give ownership to the caller.
-    auto ret = std::move(mergedLineStrings);
-    mergedLineStrings.clear();
-    return ret;
+    std::vector<std::unique_ptr<LineString>> mergedLineStrings;
+    mergedLineStrings.reserve(mergedGeometries.size());
+    for (auto& geom : mergedGeometries) {
+        if (geom->getGeometryTypeId() == GEOS_LINESTRING || geom->getGeometryTypeId() == GEOS_LINEARRING) {
+            mergedLineStrings.emplace_back(detail::down_cast<LineString*>(geom.release()));
+        }
+    }
+
+    mergedGeometries.clear();
+    return mergedLineStrings;
 }
 
+std::vector<std::unique_ptr<Curve>>
+LineMerger::getMergedCurves()
+{
+    merge();
+
+    return std::move(mergedGeometries);
+}
+
+
 } // namespace geos.operation.linemerge
 } // namespace geos.operation
 } // namespace geos
diff --git a/src/operation/linemerge/LineSequencer.cpp b/src/operation/linemerge/LineSequencer.cpp
index a8ffa894f..2cb9dd8e0 100644
--- a/src/operation/linemerge/LineSequencer.cpp
+++ b/src/operation/linemerge/LineSequencer.cpp
@@ -208,16 +208,16 @@ LineSequencer::buildSequencedGeometry(const Sequences& sequences)
                 i2End = seq.end(); i2 != i2End; ++i2) {
             const planargraph::DirectedEdge* de = *i2;
             LineMergeEdge* e = detail::down_cast<LineMergeEdge* >(de->getEdge());
-            const LineString* line = e->getLine();
+            const auto* curve = e->getCurve();
 
             // lineToAdd will be a *copy* of input things
-            std::unique_ptr<LineString> lineToAdd;
+            std::unique_ptr<Geometry> lineToAdd;
 
-            if(! de->getEdgeDirection() && ! line->isClosed()) {
-                lineToAdd = line->reverse();
+            if(! de->getEdgeDirection() && ! curve->isClosed()) {
+                lineToAdd = curve->reverse();
             }
             else {
-                lineToAdd = line->clone();
+                lineToAdd = curve->clone();
             }
 
             lines.push_back(std::move(lineToAdd));
diff --git a/tests/unit/capi/GEOSLineMergeDirectedTest.cpp b/tests/unit/capi/GEOSLineMergeDirectedTest.cpp
index 91a2e5072..b20b906aa 100644
--- a/tests/unit/capi/GEOSLineMergeDirectedTest.cpp
+++ b/tests/unit/capi/GEOSLineMergeDirectedTest.cpp
@@ -62,12 +62,16 @@ template<>
 template<>
 void object::test<3>()
 {
+    set_test_name("curved inputs");
+
     input_ = fromWKT("MULTICURVE (CIRCULARSTRING (0 0, 1 1, 2 0), (2 0, 3 0))");
-    ensure(input_);
 
     result_ = GEOSLineMergeDirected(input_);
+    ensure(result_);
 
-    ensure("curved geometries not supported", result_ == nullptr);
+    expected_ = fromWKT("COMPOUNDCURVE (CIRCULARSTRING (0 0, 1 1, 2 0), (2 0, 3 0))");
+
+    ensure_geometry_equals(result_, expected_);
 }
 
 
diff --git a/tests/unit/capi/GEOSLineMergeTest.cpp b/tests/unit/capi/GEOSLineMergeTest.cpp
index ba728b82c..c84b7aa16 100644
--- a/tests/unit/capi/GEOSLineMergeTest.cpp
+++ b/tests/unit/capi/GEOSLineMergeTest.cpp
@@ -45,12 +45,16 @@ template<>
 template<>
 void object::test<2>()
 {
+    set_test_name("curved inputs");
+
     input_ = fromWKT("MULTICURVE (CIRCULARSTRING (0 0, 1 1, 2 0), (2 0, 3 0))");
-    ensure(input_);
 
     result_ = GEOSLineMerge(input_);
+    ensure(result_);
 
-    ensure("curved geometries not supported", result_ == nullptr);
+    expected_ = fromWKT("COMPOUNDCURVE (CIRCULARSTRING (0 0, 1 1, 2 0), (2 0, 3 0))");
+
+    ensure_geometry_equals(result_, expected_);
 }
 
 template<>
diff --git a/tests/unit/operation/linemerge/LineMergerTest.cpp b/tests/unit/operation/linemerge/LineMergerTest.cpp
index e99ad4280..c27537b5f 100644
--- a/tests/unit/operation/linemerge/LineMergerTest.cpp
+++ b/tests/unit/operation/linemerge/LineMergerTest.cpp
@@ -13,6 +13,7 @@
 #include <geos/io/WKTWriter.h>
 #include <geos/util/IllegalArgumentException.h>
 // std
+#include <cmath>
 #include <memory>
 #include <string>
 #include <vector>
@@ -76,9 +77,9 @@ struct test_linemerger_data {
         readWKT(expectedWKT, expGeoms);
 
         lineMerger.add(&inpGeoms);
-        auto mrgGeoms = lineMerger.getMergedLineStrings();
-        compare(expGeoms, mrgGeoms, compareDirections);
+        auto mrgGeoms = lineMerger.getMergedCurves();
 
+        compare(expGeoms, mrgGeoms, compareDirections);
     }
 
     template <class TargetContainer>
@@ -109,7 +110,7 @@ struct test_linemerger_data {
     {
         for(const auto& e : actualGeometries) {
             Geom* element = dynamic_cast<Geom*>(e.get());
-            if(exact && element->equalsExact(g)) {
+            if(exact && element->equalsIdentical(g)) {
                 return true;
             }
             if(!exact && element->equals(g)) {
@@ -523,5 +524,48 @@ void object::test<20>
     ensure_equals_exact_geometry_xyzm(merged.front().get(), expected.get(), 0.0);
 }
 
+template<>
+template<>
+void object::test<21>()
+{
+    set_test_name("basic curved example");
+
+    const char* inpWKT[] = {
+        "LINESTRING (-10 0, -5 0)",
+        "LINESTRING (10 0, 5 0)",
+        "CIRCULARSTRING (-5 0, 0 5, 5 0)",
+        "CIRCULARSTRING (20 0, 25 5, 30 0)",
+        "CIRCULARSTRING (40 0, 35 -5, 30 0)",
+        nullptr
+    };
+    const char* expWKT[] = {
+        "COMPOUNDCURVE ((-10 0, -5 0), CIRCULARSTRING (-5 0, 0 5, 5 0), (5 0, 10 0))",
+        "CIRCULARSTRING (20 0, 25 5, 30 0, 35 -5, 40 0)",
+        nullptr
+    };
+
+    doTest(inpWKT, expWKT);
+}
+
+template<>
+template<>
+void object::test<22>()
+{
+    set_test_name("mixed-dimensionality curves");
+
+    const char* inpWKT[] = {
+        "LINESTRING (0 0, 1 1)",
+        "CIRCULARSTRING Z (1 1 5, 2 2 6, 3 1 7)",
+        nullptr
+    };
+
+    const char* expWKT[] = {
+        "COMPOUNDCURVE Z ((0 0 NaN, 1 1 5), CIRCULARSTRING (1 1 5, 2 2 6, 3 1 7))",
+        nullptr
+    };
+
+    doTest(inpWKT, expWKT);
+}
+
 } // namespace tut
 

commit ef7fe8bc4d51c2665571d1238444a54c96be3ee5
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu May 21 12:32:59 2026 -0400

    CurveBuilder: make LinearRing output optional

diff --git a/include/geos/geom/util/CurveBuilder.h b/include/geos/geom/util/CurveBuilder.h
index dcd2416e3..4309d73d5 100644
--- a/include/geos/geom/util/CurveBuilder.h
+++ b/include/geos/geom/util/CurveBuilder.h
@@ -50,6 +50,18 @@ public:
     // coordinates are currently being added.
     CoordinateSequence& getSeq(bool isCurved);
 
+    bool hasZ() const {
+        return m_hasZ;
+    }
+
+    bool hasM() const {
+        return m_hasM;
+    }
+
+    void setOutputLinearRing(bool outputLinearRing) {
+        m_outputLinearRing = outputLinearRing;
+    }
+
 private:
     void finishCurve();
     void finishLine();
@@ -59,6 +71,7 @@ private:
     const GeometryFactory& m_gfact;
     const bool m_hasZ;
     const bool m_hasM;
+    bool m_outputLinearRing{true};
     bool m_isCurved{false};
 };
 
diff --git a/src/geom/util/CurveBuilder.cpp b/src/geom/util/CurveBuilder.cpp
index 0fd0f491b..300c4ad21 100644
--- a/src/geom/util/CurveBuilder.cpp
+++ b/src/geom/util/CurveBuilder.cpp
@@ -106,7 +106,7 @@ CurveBuilder::getGeometry()
             finishCurve();
         } else {
 
-            if (m_curves.empty() && m_pts->isRing()) {
+            if (m_outputLinearRing && m_curves.empty() && m_pts->isRing()) {
                 m_curves.push_back(m_gfact.createLinearRing(std::move(m_pts)));
             } else {
                 finishLine();
diff --git a/tests/unit/geom/util/CurveBuilderTest.cpp b/tests/unit/geom/util/CurveBuilderTest.cpp
index 80e140f08..d7e801746 100644
--- a/tests/unit/geom/util/CurveBuilderTest.cpp
+++ b/tests/unit/geom/util/CurveBuilderTest.cpp
@@ -95,6 +95,24 @@ void object::test<3>()
 template<>
 template<>
 void object::test<4>()
+{
+    set_test_name("closed LineString result");
+
+    builder_.setOutputLinearRing(false);
+
+    add("LINESTRING (0 0, 1 0, 1 1)");
+    add("LINESTRING (1 1, 0 1, 0 0)");
+
+    auto result = builder_.getGeometry();
+
+    auto expected = reader_.read("LINESTRING (0 0, 1 0, 1 1, 0 1, 0 0)");
+
+    ensure_equals_geometry(static_cast<const Geometry*>(result.get()), expected.get());
+}
+
+template<>
+template<>
+void object::test<5>()
 {
     set_test_name("empty component");
 
@@ -111,7 +129,7 @@ void object::test<4>()
 
 template<>
 template<>
-void object::test<5>()
+void object::test<6>()
 {
     set_test_name("disjoint curves");
 
@@ -123,7 +141,7 @@ void object::test<5>()
 
 template<>
 template<>
-void object::test<6>()
+void object::test<7>()
 {
     set_test_name("closeRing after arc");
 
@@ -141,7 +159,7 @@ void object::test<6>()
 
 template<>
 template<>
-void object::test<7>()
+void object::test<8>()
 {
     set_test_name("closeRing after line");
 

commit 45a954af8c003a5fdca7d06200c3e5271a2af21d
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu May 21 12:30:15 2026 -0400

    Curve: add getStartCoordinate, getEndCoordinate

diff --git a/include/geos/geom/CompoundCurve.h b/include/geos/geom/CompoundCurve.h
index cb9243d9e..093e8ed57 100644
--- a/include/geos/geom/CompoundCurve.h
+++ b/include/geos/geom/CompoundCurve.h
@@ -56,6 +56,8 @@ public:
     /// Returns the nth section of the CompoundCurve
     const SimpleCurve* getCurveN(std::size_t) const override;
 
+    const CoordinateXY& getEndCoordinate() const override;
+
     std::unique_ptr<Point> getEndPoint() const override;
 
     const Envelope* getEnvelopeInternal() const override
@@ -76,6 +78,8 @@ public:
 
     std::unique_ptr<Point> getPointN(std::size_t n) const override;
 
+    const CoordinateXY& getStartCoordinate() const override;
+
     std::unique_ptr<Point> getStartPoint() const override;
 
     bool hasCurvedComponents() const override;
diff --git a/include/geos/geom/Curve.h b/include/geos/geom/Curve.h
index 807760919..1bb16df37 100644
--- a/include/geos/geom/Curve.h
+++ b/include/geos/geom/Curve.h
@@ -52,6 +52,8 @@ public:
         return Dimension::L; // line
     }
 
+    virtual const CoordinateXY& getEndCoordinate() const = 0;
+
     /// \brief
     /// Return the end point of the Curve
     /// or NULL if this is an EMPTY Curve.
@@ -62,6 +64,8 @@ public:
     /// or NULL if this is an EMPTY Curve.
     virtual std::unique_ptr<Point> getPointN(std::size_t n) const = 0;
 
+    virtual const CoordinateXY& getStartCoordinate() const = 0;
+
     /// \brief
     /// Return the start point of the Curve
     /// or NULL if this is an EMPTY Curve.
diff --git a/include/geos/geom/SimpleCurve.h b/include/geos/geom/SimpleCurve.h
index 777ff27a7..07c071b2e 100644
--- a/include/geos/geom/SimpleCurve.h
+++ b/include/geos/geom/SimpleCurve.h
@@ -66,6 +66,8 @@ public:
 
     const SimpleCurve* getCurveN(std::size_t) const override;
 
+    const CoordinateXY& getEndCoordinate() const override;
+
     /// \brief
     /// Return the end point of the LineString
     /// or NULL if this is an EMPTY LineString.
@@ -83,6 +85,8 @@ public:
 
     std::unique_ptr<Point> getPointN(std::size_t n) const override;
 
+    const CoordinateXY& getStartCoordinate() const override;
+
     /// \brief
     /// Return the start point of the LineString
     /// or NULL if this is an EMPTY LineString.
diff --git a/src/geom/CompoundCurve.cpp b/src/geom/CompoundCurve.cpp
index 26f8d450f..bea7c5a6d 100644
--- a/src/geom/CompoundCurve.cpp
+++ b/src/geom/CompoundCurve.cpp
@@ -205,6 +205,18 @@ CompoundCurve::getCurveN(std::size_t i) const
     return curves[i].get();
 }
 
+const CoordinateXY&
+CompoundCurve::getEndCoordinate() const
+{
+    for (std::size_t i = curves.size(); i != 0; i--) {
+        if (!curves[i-1]->isEmpty()) {
+            return curves[i-1]->getCoordinatesRO()->back<CoordinateXY>();
+        }
+    }
+
+    return CoordinateXY::getNull();
+}
+
 std::unique_ptr<Point>
 CompoundCurve::getEndPoint() const
 {
@@ -255,6 +267,18 @@ CompoundCurve::getNumPoints() const
     return n;
 }
 
+const CoordinateXY&
+CompoundCurve::getStartCoordinate() const
+{
+    for (const auto& curve : curves) {
+        if (!curve->isEmpty()) {
+            return curve->getCoordinatesRO()->front<CoordinateXY>();
+        }
+    }
+
+    return CoordinateXY::getNull();
+}
+
 std::unique_ptr<Point>
 CompoundCurve::getStartPoint() const
 {
diff --git a/src/geom/SimpleCurve.cpp b/src/geom/SimpleCurve.cpp
index 14ff8e39a..d65689714 100644
--- a/src/geom/SimpleCurve.cpp
+++ b/src/geom/SimpleCurve.cpp
@@ -246,6 +246,15 @@ SimpleCurve::getCurveN(std::size_t) const
     return this;
 }
 
+const CoordinateXY&
+SimpleCurve::getEndCoordinate() const
+{
+    if (isEmpty()) {
+        return CoordinateXY::getNull();
+    }
+    return points->back<CoordinateXY>();
+}
+
 std::unique_ptr<Point>
 SimpleCurve::getEndPoint() const
 {
@@ -279,6 +288,15 @@ SimpleCurve::getPointN(std::size_t n) const
     });
 }
 
+const CoordinateXY&
+SimpleCurve::getStartCoordinate() const
+{
+    if (isEmpty()) {
+        return CoordinateXY::getNull();
+    }
+    return points->front<CoordinateXY>();
+}
+
 std::unique_ptr<Point>
 SimpleCurve::getStartPoint() const
 {
diff --git a/tests/unit/geom/CircularStringTest.cpp b/tests/unit/geom/CircularStringTest.cpp
index 24a81086a..a31b70769 100644
--- a/tests/unit/geom/CircularStringTest.cpp
+++ b/tests/unit/geom/CircularStringTest.cpp
@@ -180,6 +180,9 @@ void object::test<4>()
     ensure("getStartPoint", cs_->getStartPoint()->equalsIdentical(wktreader_.read("POINT (0 0)").get()));
     ensure("getEndPoint", cs_->getEndPoint()->equalsIdentical(wktreader_.read("POINT (4 0)").get()));
 
+    ensure_equals("getStartCoordinate", cs_->getStartCoordinate(), XY(0, 0));
+    ensure_equals("getEndCoordinate", cs_->getEndCoordinate(), XY(4, 0));
+
     ensure("getCoordinatesRO", cs_->getCoordinatesRO()->getSize() == 5u);
     ensure("isClosed", !cs_->isClosed());
     XY pt(4, 0);
diff --git a/tests/unit/geom/CompoundCurveTest.cpp b/tests/unit/geom/CompoundCurveTest.cpp
index 574c41ae6..cc2f79cd8 100644
--- a/tests/unit/geom/CompoundCurveTest.cpp
+++ b/tests/unit/geom/CompoundCurveTest.cpp
@@ -129,6 +129,9 @@ void object::test<2>()
     ensure_equals_geometry(static_cast<Geometry*>(cc_->getStartPoint().get()), wktreader_.read("POINT (0 0)").get());
     ensure_equals_geometry(static_cast<Geometry*>(cc_->getEndPoint().get()), wktreader_.read("POINT (2 2)").get());
 
+    ensure_equals("getStartCoordinate", cc_->getStartCoordinate(), CoordinateXY(0, 0));
+    ensure_equals("getEndCoordinate", cc_->getEndCoordinate(), CoordinateXY(2, 2));
+
     ensure_equals_geometry(cc_->getStartPoint().get(), factory_->createPoint(CoordinateXY{0, 0}).get());
     ensure_equals_geometry(cc_->getEndPoint().get(), factory_->createPoint(CoordinateXY{2, 2}).get());
     ensure_equals_geometry(cc_->getPointN(4).get(), factory_->createPoint(CoordinateXY{2, 2}).get());

commit 44a20b3920bec49311b505e5f3938b94309ff450
Author: Daniel Baston <dbaston at gmail.com>
Date:   Thu May 21 12:26:03 2026 -0400

    GeometryFactory: add buildGeometry for vector of Curves

diff --git a/include/geos/geom/GeometryFactory.h b/include/geos/geom/GeometryFactory.h
index d58b4dfe7..73fcb5995 100644
--- a/include/geos/geom/GeometryFactory.h
+++ b/include/geos/geom/GeometryFactory.h
@@ -41,6 +41,7 @@ class Coordinate;
 class CircularString;
 class CompoundCurve;
 class CoordinateSequence;
+class Curve;
 class Envelope;
 class Geometry;
 class GeometryCollection;
@@ -191,6 +192,9 @@ public:
     std::unique_ptr<MultiLineString> createMultiLineString(
             std::vector<std::unique_ptr<LineString>> && fromLines) const;
 
+    std::unique_ptr<MultiLineString> createMultiLineString(
+            std::vector<std::unique_ptr<Curve>> && fromLines) const;
+
     std::unique_ptr<MultiLineString> createMultiLineString(
             std::vector<std::unique_ptr<Geometry>> && fromLines) const;
 
@@ -416,6 +420,8 @@ public:
 
     std::unique_ptr<Geometry> buildGeometry(std::vector<std::unique_ptr<Polygon>> && geoms) const;
 
+    std::unique_ptr<Geometry> buildGeometry(std::vector<std::unique_ptr<Curve>> && geoms) const;
+
     /// See buildGeometry(std::vector<Geometry *>&) for semantics
     //
     /// Will clone the geometries accessible through the iterator.
diff --git a/include/geos/geom/MultiLineString.h b/include/geos/geom/MultiLineString.h
index 2b6cc76fd..e8cff368a 100644
--- a/include/geos/geom/MultiLineString.h
+++ b/include/geos/geom/MultiLineString.h
@@ -124,6 +124,9 @@ protected:
     MultiLineString(std::vector<std::unique_ptr<Geometry>> && newLines,
                     const GeometryFactory& newFactory);
 
+    MultiLineString(std::vector<std::unique_ptr<Curve>> && newLines,
+                    const GeometryFactory& newFactory);
+
     MultiLineString(const MultiLineString& mp)
         : GeometryCollection(mp)
         {};
diff --git a/src/geom/GeometryFactory.cpp b/src/geom/GeometryFactory.cpp
index 93dc39f61..f53012a4c 100644
--- a/src/geom/GeometryFactory.cpp
+++ b/src/geom/GeometryFactory.cpp
@@ -41,6 +41,7 @@
 #include <geos/util/IllegalArgumentException.h>
 #include <geos/util.h>
 
+#include <algorithm>
 #include <cassert>
 #include <vector>
 #include <typeinfo>
@@ -310,6 +311,12 @@ GeometryFactory::createMultiLineString(std::vector<std::unique_ptr<LineString>>
     return std::unique_ptr<MultiLineString>(new MultiLineString(std::move(fromLines), *this));
 }
 
+std::unique_ptr<MultiLineString>
+GeometryFactory::createMultiLineString(std::vector<std::unique_ptr<Curve>> && fromLines) const {
+    // Can't use make_unique because constructor is protected
+    return std::unique_ptr<MultiLineString>(new MultiLineString(std::move(fromLines), *this));
+}
+
 std::unique_ptr<MultiLineString>
 GeometryFactory::createMultiLineString(std::vector<std::unique_ptr<Geometry>> && fromLines) const {
     // Can't use make_unique because constructor is protected
@@ -956,6 +963,29 @@ GeometryFactory::buildGeometry(std::vector<std::unique_ptr<Polygon>> && geoms) c
     return createMultiPolygon(std::move(geoms));
 }
 
+std::unique_ptr<Geometry>
+GeometryFactory::buildGeometry(std::vector<std::unique_ptr<Curve>> && geoms) const
+{
+    if (geoms.empty()) {
+        return createGeometryCollection();
+    }
+
+    if (geoms.size() == 1) {
+        return std::move(geoms[0]);
+    }
+
+    const bool hasCurves = std::any_of(geoms.begin(), geoms.end(), [](const auto& geom) {
+        const auto typeId = geom->getGeometryTypeId();
+        return typeId == GEOS_COMPOUNDCURVE || typeId == GEOS_CIRCULARSTRING;
+    });
+
+    if (hasCurves) {
+        return createMultiCurve(std::move(geoms));
+    } else {
+        return createMultiLineString(std::move(geoms));
+    }
+}
+
 /*public*/
 std::unique_ptr<Geometry>
 GeometryFactory::buildGeometry(const std::vector<const Geometry*>& fromGeoms) const
diff --git a/src/geom/MultiLineString.cpp b/src/geom/MultiLineString.cpp
index 00b52dfa5..3a2b1ac37 100644
--- a/src/geom/MultiLineString.cpp
+++ b/src/geom/MultiLineString.cpp
@@ -45,6 +45,18 @@ MultiLineString::MultiLineString(std::vector<std::unique_ptr<Geometry>> && newLi
         : GeometryCollection(std::move(newLines), factory)
 {}
 
+MultiLineString::MultiLineString(std::vector<std::unique_ptr<Curve>> && newLines,
+                                 const GeometryFactory& factory)
+        : GeometryCollection(std::move(newLines), factory)
+{
+    for (const auto& g : geometries) {
+        if (g->getGeometryTypeId() != GEOS_LINESTRING) {
+            throw util::IllegalArgumentException("MultiLineString must only contain LineStrings");
+        }
+    }
+}
+
+
 Dimension::DimensionType
 MultiLineString::getDimension() const
 {
diff --git a/tests/unit/geom/GeometryFactoryTest.cpp b/tests/unit/geom/GeometryFactoryTest.cpp
index 7e80d6703..ca45c8a5a 100644
--- a/tests/unit/geom/GeometryFactoryTest.cpp
+++ b/tests/unit/geom/GeometryFactoryTest.cpp
@@ -1514,4 +1514,47 @@ void object::test<42>
 
 }
 
+template<>
+template<>
+void object::test<43>()
+{
+    set_test_name("buildGeometry with Curve vector");
+
+    {
+        std::vector<std::unique_ptr<geos::geom::Curve>> curves;
+        curves.push_back(reader_.read<geos::geom::Curve>("LINESTRING(0 0, 10 10)"));
+
+        auto g = factory_->buildGeometry(std::move(curves));
+        ensure_equals(g->getGeometryTypeId(), geos::geom::GEOS_LINESTRING);
+    }
+
+    {
+        std::vector<std::unique_ptr<geos::geom::Curve>> curves;
+        curves.push_back(reader_.read<geos::geom::Curve>("LINESTRING(0 0, 10 10)"));
+        curves.push_back(reader_.read<geos::geom::Curve>("LINESTRING(10 10, 20 20)"));
+
+        auto g = factory_->buildGeometry(std::move(curves));
+        ensure_equals(g->getGeometryTypeId(), geos::geom::GEOS_MULTILINESTRING);
+        ensure_equals(g->getNumGeometries(), 2u);
+    }
+
+    {
+        std::vector<std::unique_ptr<geos::geom::Curve>> curves;
+        curves.push_back(reader_.read<geos::geom::Curve>("CIRCULARSTRING(0 0, 1 1, 2 0)"));
+
+        auto g = factory_->buildGeometry(std::move(curves));
+        ensure_equals(g->getGeometryTypeId(), geos::geom::GEOS_CIRCULARSTRING);
+    }
+
+    {
+        std::vector<std::unique_ptr<geos::geom::Curve>> curves;
+        curves.push_back(reader_.read<geos::geom::Curve>("CIRCULARSTRING(0 0, 1 1, 2 0)"));
+        curves.push_back(reader_.read<geos::geom::Curve>("LINESTRING (3 3, 4 4)"));
+
+        auto g = factory_->buildGeometry(std::move(curves));
+        ensure_equals(g->getGeometryTypeId(), geos::geom::GEOS_MULTICURVE);
+        ensure_equals(g->getNumGeometries(), 2u);
+    }
+}
+    
 } // namespace tut

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

Summary of changes:
 capi/geos_c.h.in                                  |  4 +
 capi/geos_ts_c.cpp                                |  4 +-
 include/geos/geom/CompoundCurve.h                 |  4 +
 include/geos/geom/Curve.h                         |  4 +
 include/geos/geom/GeometryFactory.h               |  6 ++
 include/geos/geom/MultiLineString.h               |  3 +
 include/geos/geom/SimpleCurve.h                   |  4 +
 include/geos/geom/util/CurveBuilder.h             | 21 +++++
 include/geos/operation/linemerge/EdgeString.h     |  6 +-
 include/geos/operation/linemerge/LineMergeEdge.h  | 12 +--
 include/geos/operation/linemerge/LineMergeGraph.h |  8 +-
 include/geos/operation/linemerge/LineMerger.h     |  6 +-
 src/geom/CompoundCurve.cpp                        | 24 ++++++
 src/geom/GeometryFactory.cpp                      | 30 +++++++
 src/geom/MultiLineString.cpp                      | 12 +++
 src/geom/SimpleCurve.cpp                          | 18 +++++
 src/geom/util/CurveBuilder.cpp                    |  2 +-
 src/operation/linemerge/EdgeString.cpp            | 98 ++++++++++++++---------
 src/operation/linemerge/LineMergeEdge.cpp         | 15 +---
 src/operation/linemerge/LineMergeGraph.cpp        | 94 +++++++++++++++++-----
 src/operation/linemerge/LineMerger.cpp            | 38 ++++++---
 src/operation/linemerge/LineSequencer.cpp         | 10 +--
 tests/unit/capi/GEOSLineMergeDirectedTest.cpp     |  8 +-
 tests/unit/capi/GEOSLineMergeTest.cpp             |  8 +-
 tests/unit/geom/CircularStringTest.cpp            |  3 +
 tests/unit/geom/CompoundCurveTest.cpp             |  3 +
 tests/unit/geom/GeometryFactoryTest.cpp           | 43 ++++++++++
 tests/unit/geom/util/CurveBuilderTest.cpp         | 24 +++++-
 tests/unit/operation/linemerge/LineMergerTest.cpp | 50 +++++++++++-
 29 files changed, 450 insertions(+), 112 deletions(-)


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list