[geos-commits] [SCM] GEOS branch main updated. 7312930ea53f1f3954ad3f455ef549576f69743e

git at osgeo.org git at osgeo.org
Fri Oct 2 05:53:47 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  7312930ea53f1f3954ad3f455ef549576f69743e (commit)
      from  e8d427e4f8d323b68a8f1f6d2c99e68522ec16d2 (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 7312930ea53f1f3954ad3f455ef549576f69743e
Author: Daniel Baston <dbaston at gmail.com>
Date:   Fri Oct 2 08:53:22 2026 -0400

    CurveBuilder: Copy Z/M values at shared endpoints of mixed-dimension sequences (#1545)
    
    References https://github.com/libgeos/geos/issues/1541

diff --git a/include/geos/algorithm/LineToCurveConverter.h b/include/geos/algorithm/LineToCurveConverter.h
index b14b3f98a..c0efb0c06 100644
--- a/include/geos/algorithm/LineToCurveConverter.h
+++ b/include/geos/algorithm/LineToCurveConverter.h
@@ -53,17 +53,6 @@ private:
 
     void addLineCoords(const geom::CoordinateSequence& points, std::size_t from, std::size_t to);
 
-    /*
-
-    void finishArc();
-
-    void finishLine();
-
-    std::shared_ptr<geom::CoordinateSequence> lineCoords;
-    std::shared_ptr<geom::CoordinateSequence> arcCoords;
-    std::vector<std::unique_ptr<geom::SimpleCurve>> curves;
-    const geom::GeometryFactory& factory;
-    */
     geom::util::CurveBuilder curveBuilder;
 
     /// Declared as non-copyable
diff --git a/include/geos/geom/util/CurveBuilder.h b/include/geos/geom/util/CurveBuilder.h
index 27fccd2fa..ab5913bf8 100644
--- a/include/geos/geom/util/CurveBuilder.h
+++ b/include/geos/geom/util/CurveBuilder.h
@@ -40,6 +40,9 @@ public:
     // Add all coordinates in the provided sequence
     void add(const CoordinateSequence& seq, bool isCurved);
 
+    // Add all coordinates in the provided sequence (optionally reversed)
+    void add(const CoordinateSequence& seq, bool isCurved, bool isForward);
+
     // Add coordinates between the specified indices (inclusive)
     void add(const CoordinateSequence& seq, std::size_t from, std::size_t to, bool isCurved);
 
@@ -62,28 +65,29 @@ public:
     }
 
     bool hasActiveSequence() const {
-        return m_pts != nullptr;
+        return !m_pts.empty();
     }
 
     bool isCurved() const {
-        return m_isCurved;
+        return hasActiveSequence() && m_pts.back().second;
     }
 
     void setOutputLinearRing(bool outputLinearRing) {
         m_outputLinearRing = outputLinearRing;
     }
 
-private:
-    void finishCurve();
-    void finishLine();
+    void matchRingZM();
 
-    std::vector<std::unique_ptr<SimpleCurve>> m_curves;
-    std::unique_ptr<CoordinateSequence> m_pts{nullptr};
+    // Declare type as noncopyable
+    CurveBuilder(const CurveBuilder& other) = delete;
+    CurveBuilder& operator=(const CurveBuilder& rhs) = delete;
+
+private:
+    std::vector<std::pair<std::unique_ptr<CoordinateSequence>, bool>> m_pts;
     const GeometryFactory& m_gfact;
     const bool m_hasZ;
     const bool m_hasM;
     bool m_outputLinearRing{true};
-    bool m_isCurved{false};
 };
 
 }
\ No newline at end of file
diff --git a/include/geos/operation/polygonize/EdgeRing.h b/include/geos/operation/polygonize/EdgeRing.h
index 5f8b7df5b..281b200d9 100644
--- a/include/geos/operation/polygonize/EdgeRing.h
+++ b/include/geos/operation/polygonize/EdgeRing.h
@@ -82,10 +82,6 @@ private:
         return *getRingInternal()->getEnvelopeInternal();
     }
 
-    static void addEdge(const geom::CoordinateSequence* coords,
-                        bool isForward,
-                        geom::CoordinateSequence* coordList);
-
     algorithm::locate::PointOnGeometryLocator* getLocator() const;
 
     bool contains(const EdgeRing& otherRing) const;
diff --git a/src/geom/util/CurveBuilder.cpp b/src/geom/util/CurveBuilder.cpp
index 28859a7cc..2996a81f4 100644
--- a/src/geom/util/CurveBuilder.cpp
+++ b/src/geom/util/CurveBuilder.cpp
@@ -20,6 +20,9 @@
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/GeometryFactory.h>
 #include <geos/geom/LineString.h>
+#include <geos/util/GEOSException.h>
+
+#include <cmath>
 
 namespace geos::geom::util {
 
@@ -47,108 +50,186 @@ CurveBuilder::add(const Curve& geom)
 void
 CurveBuilder::add(const CoordinateSequence& coords, bool isCurved)
 {
-    getSeq(isCurved).add(coords, false);
+    add(coords, isCurved, true);
 }
 
 void
-CurveBuilder::add(const CoordinateSequence& coords, std::size_t from, std::size_t to, bool isCurved)
+CurveBuilder::add(const CoordinateSequence& coords, bool isCurved, bool isForward)
 {
-    getSeq(isCurved).add(coords, from, to, false);
+    if (coords.isEmpty()) {
+        return;
+    }
+
+    if (isForward) {
+        add(coords, 0, coords.size() - 1, isCurved);
+    } else {
+        add(coords, coords.size() - 1, 0, isCurved);
+    }
+}
+
+void
+CurveBuilder::add(const CoordinateSequence& src, std::size_t from, std::size_t to, bool isCurved)
+{
+    CoordinateSequence& dst = getSeq(isCurved);
+    const std::size_t insertionPos = dst.size();
+
+    if (from > to) {
+        for(std::size_t i = from + 1; i > to; --i) {
+            src.applyAt(i-1, [&dst](const auto& coord) {
+                dst.add(coord, false);
+            });
+        }
+    } else {
+        dst.add(src, from, to, false);
+    }
+
+    // When adding a sequence whose initial point is the same as the current final point, we may need to
+    // copy Z/M values from the initial point to the final point, or vice-versa.
+
+    if (!m_hasZ && !m_hasM) {
+        return;
+    }
+
+    if (insertionPos == 0 && m_pts.size() == 1) {
+        return;
+    }
+
+    CoordinateSequence& prev = insertionPos == 0 ? *m_pts[m_pts.size() - 2].first : dst;
+    std::size_t prevPos = insertionPos == 0 ? prev.size() - 1 : insertionPos - 1;
+    std::size_t currPos = insertionPos == 0 ? 0 : insertionPos - 1;
+
+    const bool firstPointEqualsLast = prev.getAt<CoordinateXY>(prevPos).equals2D(src.getAt<CoordinateXY>(from));
+
+    if (!firstPointEqualsLast) {
+        return;
+    }
+
+    if (m_hasZ) {
+        const double currZ = src.getZ(from);
+        const double prevZ = prev.getZ(prevPos);
+
+        if (std::isnan(prevZ) && !std::isnan(currZ)) {
+            prev.setZ(prevPos, currZ);
+        }
+        if (std::isnan(currZ) && !std::isnan(prevZ)) {
+            dst.setZ(currPos, prevZ);
+        }
+    }
+
+    if (m_hasM) {
+        const double currM = src.getM(from);
+        const double prevM = prev.getM(prevPos);
+
+        if (std::isnan(prevM) && !std::isnan(currM)) {
+            prev.setM(prevPos, currM);
+        }
+        if (std::isnan(currM) && !std::isnan(prevM)) {
+            dst.setM(currPos, prevM);
+        }
+    }
+}
+
+void
+CurveBuilder::matchRingZM()
+{
+    if (m_pts.empty()) {
+        return;
+    }
+
+    CoordinateSequence& firstSeq = *m_pts.front().first;
+    CoordinateSequence& lastSeq = *m_pts.back().first;
+
+    if (!firstSeq.front<CoordinateXY>().equals2D(lastSeq.back<CoordinateXY>())) {
+        throw geos::util::GEOSException("CurveBuilder::matchRingZM called on non-ring");
+    }
+
+    if (m_hasZ) {
+        const double firstZ = firstSeq.getZ(0);
+        const double lastZ = lastSeq.getZ(lastSeq.size() - 1);
+
+        if (std::isnan(firstZ) && !std::isnan(lastZ)) {
+            firstSeq.setZ(0, lastZ);
+        }
+        if (std::isnan(lastZ) && !std::isnan(firstZ)) {
+            lastSeq.setZ(lastSeq.size() - 1, firstZ);
+        }
+    }
+
+    if (m_hasM) {
+        const double firstM = firstSeq.getM(0);
+        const double lastM = lastSeq.getM(lastSeq.size() - 1);
+
+        if (std::isnan(firstM) && !std::isnan(lastM)) {
+            firstSeq.setM(0, lastM);
+        }
+        if (std::isnan(lastM) && !std::isnan(firstM)) {
+            lastSeq.setM(lastSeq.size() - 1, firstM);
+        }
+    }
 }
 
 void
 CurveBuilder::closeRing()
 {
-    if (m_curves.empty() && (m_pts == nullptr || m_pts->isEmpty())) {
+    if (m_pts.empty()) {
         return;
     }
 
     CoordinateXYZM first;
-    if (!m_curves.empty()) {
-        m_curves.front()->getCoordinatesRO()->getAt(0, first);
-    } else {
-        m_pts->getAt(0, first);
-    }
+    m_pts.front().first->getAt(0, first);
 
     CoordinateXYZM last;
-    if (m_pts && !m_pts->isEmpty()) {
-        m_pts->getAt(m_pts->size() - 1, last);
-    } else {
-        const auto* seq = m_curves.back()->getCoordinatesRO();
-        seq->getAt(seq->size() - 1, last);
-    }
+    m_pts.back().first->getAt(m_pts.back().first->size() - 1, last);
 
     if (first.equals2D(last)) {
+        matchRingZM();
         return;
     }
 
-    if (m_pts && m_isCurved) {
-        finishCurve();
+    if (isCurved()) {
         getSeq(false).add(last);
     }
-
     getSeq(false).add(first);
 }
 
-void
-CurveBuilder::finishCurve()
-{
-    m_curves.push_back(m_gfact.createCircularString(std::move(m_pts)));
-    m_pts = nullptr;
-}
-
-void
-CurveBuilder::finishLine()
-{
-    m_curves.push_back(m_gfact.createLineString(std::move(m_pts)));
-    m_pts = nullptr;
-}
-
 std::unique_ptr<Curve>
 CurveBuilder::getGeometry()
 {
-    if (m_pts) {
-        if (m_isCurved) {
-            finishCurve();
-        } else {
+    if (m_pts.empty()) {
+        return m_gfact.createLineString(std::make_unique<CoordinateSequence>(0, m_hasZ, m_hasM));
+    }
 
-            if (m_outputLinearRing && m_curves.empty() && m_pts->isRing()) {
-                m_curves.push_back(m_gfact.createLinearRing(std::move(m_pts)));
-            } else {
-                finishLine();
-            }
+    if (m_pts.size() == 1) {
+        auto& [pts, isCurve] = m_pts.front();
+        if (isCurve) {
+            return m_gfact.createCircularString(std::move(pts));
+        }
+        if (m_outputLinearRing && pts->isRing()) {
+            return m_gfact.createLinearRing(std::move(pts));
+        }
+        return m_gfact.createLineString(std::move(pts));
+    }
+
+    std::vector<std::unique_ptr<SimpleCurve>> curves;
+    for (auto& [pts, isCurve] : m_pts) {
+        if (isCurve) {
+            curves.push_back(m_gfact.createCircularString(std::move(pts)));
+        } else {
+            curves.push_back(m_gfact.createLineString(std::move(pts)));
         }
     }
 
-    if (m_curves.empty()) {
-        auto seq = std::make_unique<CoordinateSequence>(0, m_hasZ, m_hasM);
-        return m_gfact.createLineString(std::move(seq));
-    }
-
-    if (m_curves.size() == 1) {
-        return std::move(m_curves[0]);
-    }
-
-    return m_gfact.createCompoundCurve(std::move(m_curves));
+    return m_gfact.createCompoundCurve(std::move(curves));
 }
 
 CoordinateSequence&
 CurveBuilder::getSeq(bool isCurved)
 {
-    if (m_pts) {
-        if (m_isCurved && !isCurved) {
-            finishCurve();
-        } else if (isCurved && !m_isCurved) {
-            finishLine();
-        }
+    if (m_pts.empty() || m_pts.back().second != isCurved) {
+        m_pts.emplace_back(std::make_unique<CoordinateSequence>(0, m_hasZ, m_hasM), isCurved);
     }
 
-    if (!m_pts) {
-        m_pts = std::make_unique<CoordinateSequence>(0, m_hasZ, m_hasM);
-        m_isCurved = isCurved;
-    }
-
-    return *m_pts;
+    return *m_pts.back().first;
 }
 
 }
diff --git a/src/operation/linemerge/EdgeString.cpp b/src/operation/linemerge/EdgeString.cpp
index 54fbbae5c..3b826c8dc 100644
--- a/src/operation/linemerge/EdgeString.cpp
+++ b/src/operation/linemerge/EdgeString.cpp
@@ -64,26 +64,9 @@ addCoordinates(geom::util::CurveBuilder& curveBuilder, const SimpleCurve* curve,
     }
 
     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);
+
+    curveBuilder.add(srcCoords, isCurved, forward);
 }
 
 std::unique_ptr<Curve>
diff --git a/src/operation/polygonize/EdgeRing.cpp b/src/operation/polygonize/EdgeRing.cpp
index 8917c1bf7..2ce1bd724 100644
--- a/src/operation/polygonize/EdgeRing.cpp
+++ b/src/operation/polygonize/EdgeRing.cpp
@@ -305,13 +305,12 @@ EdgeRing::getRingInternal() const
         for(const auto& de : deList) {
             const auto edge = detail::down_cast<PolygonizeEdge*>(de->getEdge());
             const bool isCurved = edge->getLine()->getGeometryTypeId() == GEOS_CIRCULARSTRING;
+            const bool isForward = de->getEdgeDirection();
 
-            CoordinateSequence& ringPts = builder.getSeq(isCurved);
-
-            addEdge(edge->getLine()->getCoordinatesRO(),
-                    de->getEdgeDirection(), &ringPts);
+            builder.add(*edge->getLine()->getCoordinatesRO(), isCurved, isForward);
         }
 
+        builder.matchRingZM();
         ring = builder.getGeometry();
     }
 
@@ -326,36 +325,6 @@ EdgeRing::getRingOwnership()
     return std::move(ring);
 }
 
-/*private*/
-void
-EdgeRing::addEdge(const CoordinateSequence* srcCoords, bool isForward,
-                  CoordinateSequence* dstCoords)
-{
-    const std::size_t npts = srcCoords->getSize();
-
-    if (!dstCoords->isEmpty() && !srcCoords->isEmpty()) {
-        // Patch Z value in last coordinate, if needed
-        if (srcCoords->hasZ() && std::isnan(dstCoords->getZ(dstCoords->size() - 1))) {
-            dstCoords->setZ(dstCoords->size() - 1, isForward ? srcCoords->getZ(0) : srcCoords->getZ(srcCoords->getSize() - 1));
-        }
-        // Patch M value in last coordinate, if needed
-        if (srcCoords->hasM() && std::isnan(dstCoords->getM(dstCoords->size() - 1))) {
-            dstCoords->setM(dstCoords->size() - 1, isForward ? srcCoords->getM(0) : srcCoords->getM(srcCoords->getSize() - 1));
-        }
-    }
-
-    if(isForward) {
-        dstCoords->add(*srcCoords, 0, npts - 1, false);
-    }
-    else {
-        for(std::size_t i = npts; i > 0; --i) {
-            srcCoords->applyAt(i-1, [&dstCoords](const auto& coord) {
-                dstCoords->add(coord, false);
-            });
-        }
-    }
-}
-
 EdgeRing*
 EdgeRing::getOuterHole() const {
     // Only shells can have outer holes
diff --git a/tests/unit/geom/util/CurveBuilderTest.cpp b/tests/unit/geom/util/CurveBuilderTest.cpp
index d7e801746..05a44fe48 100644
--- a/tests/unit/geom/util/CurveBuilderTest.cpp
+++ b/tests/unit/geom/util/CurveBuilderTest.cpp
@@ -174,4 +174,86 @@ void object::test<8>()
     ensure_equals_geometry(static_cast<const Geometry*>(result.get()), expected.get());
 }
 
+template<>
+template<>
+void object::test<9>()
+{
+    set_test_name("Z/M matched when combining mixed-dimension sequences");
+
+    CurveBuilder builder(*factory_, true, true);
+    builder.add(*reader_.read<LineString>("LINESTRING (0 0, 1 1)"));
+    // Patch Z Linear <- Linear
+    builder.add(*reader_.read<LineString>("LINESTRING Z (1 1 5, 2 2 4)"));
+    // Patch Z Linear -> Linear
+    builder.add(*reader_.read<LineString>("LINESTRING (2 2, 5 0)"));
+    // Patch M Linear <- Curve
+    builder.add(*reader_.read<CircularString>("CIRCULARSTRING M (5 0 8, 6 1 7, 7 0 8)"));
+    // Patch M Curve -> Curve
+    builder.add(*reader_.read<CircularString>("CIRCULARSTRING (7 0, 8 -1, 9 0)"));
+    // Patch Z Curve <- Linear
+    builder.add(*reader_.read<LineString>("LINESTRING (9 0 11, 10 20 12)"));
+
+    auto result = builder.getGeometry();
+
+    auto expected = reader_.read("COMPOUNDCURVE ZM ((0 0 NaN NaN, 1 1 5 NaN, 2 2 4 NaN, 5 0 NaN 8), CIRCULARSTRING ZM (5 0 NaN 8, 6 1 NaN 7, 7 0 NaN 8, 8 -1 NaN NaN, 9 0 11 NaN), (9 0 11 NaN, 10 20 12 NaN))");
+
+    ensure_equals_geometry_xyzm(static_cast<const Geometry*>(result.get()), expected.get());
+}
+
+template<>
+template<>
+void object::test<10>()
+{
+    set_test_name("Z/M matched when calling closeRing on open CircularString");
+
+    CurveBuilder builder(*factory_, true, true);
+    builder.add(*reader_.read<CircularString>("CIRCULARSTRING ZM (0 0 100 200, 1 1 200 400, 2 0 300 600)"));
+    builder.closeRing();
+
+    auto result = builder.getGeometry();
+
+    auto expected = reader_.read("COMPOUNDCURVE ZM (CIRCULARSTRING(0 0 100 200, 1 1 200 400, 2 0 300 600), (2 0 300 600, 0 0 100 200))");
+
+    ensure_equals_geometry_xyzm(static_cast<const Geometry*>(result.get()), expected.get());
+}
+
+template<>
+template<>
+void object::test<11>()
+{
+    set_test_name("Z/M matched when calling closeRing on closed CircularString ZM -> LineString");
+
+    CurveBuilder builder(*factory_, true, true);
+    builder.add(*reader_.read<CircularString>("CIRCULARSTRING ZM (0 0 100 200, 1 1 200 400, 2 0 300 600)"));
+    builder.add(*reader_.read<LineString>("LINESTRING (2 0, 0 0)"));
+
+    builder.closeRing();
+
+    auto result = builder.getGeometry();
+
+    auto expected = reader_.read("COMPOUNDCURVE ZM (CIRCULARSTRING(0 0 100 200, 1 1 200 400, 2 0 300 600), (2 0 300 600, 0 0 100 200))");
+
+    ensure_equals_geometry_xyzm(static_cast<const Geometry*>(result.get()), expected.get());
+}
+
+template<>
+template<>
+void object::test<12>()
+{
+    set_test_name("Z/M matched when calling closeRing on closed CircularString -> LineString ZM");
+
+    CurveBuilder builder(*factory_, true, true);
+    builder.add(*reader_.read<CircularString>("CIRCULARSTRING (0 0, 1 1, 2 0)"));
+    builder.add(*reader_.read<LineString>("LINESTRING ZM (2 0 300 600, 0 0 100 200)"));
+
+    builder.closeRing();
+
+    auto result = builder.getGeometry();
+
+    auto expected = reader_.read("COMPOUNDCURVE ZM (CIRCULARSTRING(0 0 100 200, 1 1 NaN NaN, 2 0 300 600), (2 0 300 600, 0 0 100 200))");
+
+    ensure_equals_geometry_xyzm(static_cast<const Geometry*>(result.get()), expected.get());
+}
+
+
 } // namespace tut
diff --git a/tests/unit/operation/polygonize/PolygonizeTest.cpp b/tests/unit/operation/polygonize/PolygonizeTest.cpp
index 502fc2c84..9c6002a62 100644
--- a/tests/unit/operation/polygonize/PolygonizeTest.cpp
+++ b/tests/unit/operation/polygonize/PolygonizeTest.cpp
@@ -62,8 +62,8 @@ struct test_polygonizetest_data {
     void
     printAll(std::ostream& os, T& cnt)
     {
-        for(typename T::iterator i = cnt.begin(), e = cnt.end(); i != e; ++i) {
-            os << **i << std::endl;
+        for(auto& geom : cnt) {
+            os << geom->toString() << std::endl;
         }
     }
 
@@ -71,9 +71,8 @@ struct test_polygonizetest_data {
     bool
     contains(T& cnt, const Geom* g)
     {
-        for(typename T::iterator i = cnt.begin(), e = cnt.end(); i != e; ++i) {
-            const auto& element = *i;
-            if(element->equalsExact(g)) {
+        for(const auto& element : cnt) {
+            if(element->equalsIdentical(g)) {
                 return true;
             }
         }
@@ -92,9 +91,9 @@ struct test_polygonizetest_data {
                  << ob.size() << endl;
             return false;
         }
-        for(typename T::iterator i = ex.begin(), e = ex.end(); i != e; ++i) {
-            if(! contains(ob, i->get())) {
-                cout << "Expected " << wktwriter.write(i->get())
+        for(const auto& expected : ex) {
+            if(! contains(ob, expected.get())) {
+                cout << "Expected " << wktwriter.write(expected.get())
                      << " not found" << endl;
                 return false;
             }
@@ -395,7 +394,7 @@ void object::test<11>()
         },
         {
           "POLYGON ZM ((0 0 0 0, 0 10 1 2, 10 10 2 3, 10 0 3 4, 0 0 0 0))",
-          "POLYGON ZM ((10 0 3 4, 10 10 2 3, 20 0 NaN 9, 10 0 3 4))"
+          "POLYGON ZM ((10 0 3 4, 10 10 2 3, 20 0 NaN 9, 10 0 3 8))"
         },
         false,
         POLYGONS);
@@ -481,5 +480,23 @@ void object::test<15>()
     doTest(input, expected, false, POLYGONS);
 }
 
+template<>
+template<>
+void object::test<16>()
+{
+    set_test_name("mixed-dimension curved and linear inputs");
+
+    std::vector<std::string> input{
+        "LINESTRING Z (-5 0 10, 5 0 20)",
+        "CIRCULARSTRING (-5 0, 0 5, 5 0)"
+    };
+
+    std::vector<std::string> expected{
+        "CURVEPOLYGON Z (COMPOUNDCURVE((-5 0 10, 5 0 20), CIRCULARSTRING (5 0 20, 0 5 NaN, -5 0 10)))"
+    };
+
+    doTest(input, expected, false, POLYGONS);
+}
+
 } // namespace tut
 

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

Summary of changes:
 include/geos/algorithm/LineToCurveConverter.h      |  11 --
 include/geos/geom/util/CurveBuilder.h              |  20 ++-
 include/geos/operation/polygonize/EdgeRing.h       |   4 -
 src/geom/util/CurveBuilder.cpp                     | 195 +++++++++++++++------
 src/operation/linemerge/EdgeString.cpp             |  21 +--
 src/operation/polygonize/EdgeRing.cpp              |  37 +---
 tests/unit/geom/util/CurveBuilderTest.cpp          |  82 +++++++++
 tests/unit/operation/polygonize/PolygonizeTest.cpp |  35 +++-
 8 files changed, 263 insertions(+), 142 deletions(-)


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list