[geos-commits] [SCM] GEOS branch main updated. 3b391278f7844f16968e9397dd507c95589ac647

git at osgeo.org git at osgeo.org
Fri Jan 16 08:18:43 PST 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  3b391278f7844f16968e9397dd507c95589ac647 (commit)
       via  49209692ccda59d4f650001ba7a7f8752d54b98b (commit)
       via  c28e77e9502c7496f90e1402dae6420312a69203 (commit)
      from  9563c1805e90523fce74530e8fa3ff4d5e873d35 (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 3b391278f7844f16968e9397dd507c95589ac647
Author: Daniel Baston <dbaston at gmail.com>
Date:   Mon Jan 12 10:21:06 2026 -0500

    LineMerger: Retain M values
    
    Resolves https://github.com/libgeos/geos/issues/1359

diff --git a/src/operation/linemerge/EdgeString.cpp b/src/operation/linemerge/EdgeString.cpp
index 7c10a2a2e..88abfadee 100644
--- a/src/operation/linemerge/EdgeString.cpp
+++ b/src/operation/linemerge/EdgeString.cpp
@@ -27,7 +27,6 @@
 #include <geos/util.h>
 
 #include <vector>
-#include <cassert>
 
 
 using namespace geos::geom;
@@ -60,9 +59,23 @@ EdgeString::getCoordinates() const
 {
     int forwardDirectedEdges = 0;
     int reverseDirectedEdges = 0;
-    auto coordinates = detail::make_unique<CoordinateSequence>();
-    for(std::size_t i = 0, e = directedEdges.size(); i < e; ++i) {
-        LineMergeDirectedEdge* directedEdge = directedEdges[i];
+
+    bool resultHasZ = false;
+    bool resultHasM = false;
+
+    for (const LineMergeDirectedEdge* directedEdge : directedEdges) {
+        const LineMergeEdge* lme = detail::down_cast<LineMergeEdge*>(directedEdge->getEdge());
+
+        resultHasZ |= lme->getLine()->hasZ();
+        resultHasM |= lme->getLine()->hasM();
+    }
+
+    auto coordinates = std::make_unique<CoordinateSequence>(0, resultHasZ, resultHasM);
+
+    bool lastPointMissingZ = false;
+    bool lastPointMissingM = false;
+
+    for (const LineMergeDirectedEdge* directedEdge : directedEdges) {
         if(directedEdge->getEdgeDirection()) {
             forwardDirectedEdges++;
         }
@@ -70,15 +83,30 @@ EdgeString::getCoordinates() const
             reverseDirectedEdges++;
         }
 
-        LineMergeEdge* lme = detail::down_cast<LineMergeEdge*>(directedEdge->getEdge());
+        const LineMergeEdge* lme = detail::down_cast<LineMergeEdge*>(directedEdge->getEdge());
+        const CoordinateSequence* seq = lme->getLine()->getCoordinatesRO();
 
-        coordinates->add(*lme->getLine()->getCoordinatesRO(),
+        if (lastPointMissingZ && seq->hasZ()) {
+            const double z = directedEdge->getEdgeDirection() ? seq->getZ(0) : seq->getZ(seq->getSize() - 1);
+            coordinates->setZ(coordinates->getSize() - 1, z);
+        }
+        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();
     }
+
     if(reverseDirectedEdges > forwardDirectedEdges) {
         coordinates->reverse();
     }
+
     return coordinates;
 }
 
diff --git a/tests/unit/operation/linemerge/LineMergerTest.cpp b/tests/unit/operation/linemerge/LineMergerTest.cpp
index 1fea1cd7b..e99ad4280 100644
--- a/tests/unit/operation/linemerge/LineMergerTest.cpp
+++ b/tests/unit/operation/linemerge/LineMergerTest.cpp
@@ -17,6 +17,8 @@
 #include <string>
 #include <vector>
 
+#include "utility.h"
+
 namespace tut {
 //
 // Test Group
@@ -492,5 +494,34 @@ void object::test<19>
     ensure_equals(geom->getLength(), merged[0]->getLength());
 }
 
+template<>
+template<>
+void object::test<20>
+()
+{
+    std::vector<std::string> wkts{
+        "LINESTRING Z (0 0 0, 1 2 3, 2 4 6)",
+        "LINESTRING M (10 9 8, 2 4 7)",
+        "LINESTRING Z (10 9 2, 11 12 15)"
+    };
+    std::vector<std::unique_ptr<Geometry>> geoms;
+
+    LineMerger lm;
+
+    for (const auto& wkt : wkts) {
+        auto geom = wktreader.read(wkt);
+        lm.add(geom.get());
+        geoms.push_back(std::move(geom));
+    }
+
+    auto merged = lm.getMergedLineStrings();
+
+    ensure_equals(merged.size(), 1u);
+
+    auto expected = wktreader.read("LINESTRING ZM (0 0 0 NaN, 1 2 3 NaN, 2 4 6 7, 10 9 2 8, 11 12 15 NaN)");
+
+    ensure_equals_exact_geometry_xyzm(merged.front().get(), expected.get(), 0.0);
+}
+
 } // namespace tut
 

commit 49209692ccda59d4f650001ba7a7f8752d54b98b
Author: Daniel Baston <dbaston at gmail.com>
Date:   Mon Jan 12 10:20:50 2026 -0500

    geos_unit: Improve assert failure message in ensure_equals_xyzm

diff --git a/tests/unit/utility.h b/tests/unit/utility.h
index 562a95dc8..c86dbce56 100644
--- a/tests/unit/utility.h
+++ b/tests/unit/utility.h
@@ -428,8 +428,8 @@ ensure_equals_exact_xyzm(const geos::geom::CoordinateSequence* seq1,
         seq2->getAt(i, c2);
 
         ensure("xy not in tolerance", c1.distance(c2) <= tol);
-        ensure_same("z not same", c1.z, c2.z);
-        ensure_same("z not same", c1.m, c2.m);
+        ensure_same(("index " + std::to_string(i) + "/" + std::to_string(seq1->getSize() - 1) + " z not same").c_str(), c1.z, c2.z);
+        ensure_same(("index " + std::to_string(i) + "/" + std::to_string(seq1->getSize() - 1) + " m not same").c_str(), c1.m, c2.m);
     }
 }
 

commit c28e77e9502c7496f90e1402dae6420312a69203
Author: Daniel Baston <dbaston at gmail.com>
Date:   Mon Jan 12 10:20:23 2026 -0500

    CoordinateSequence: Add Z/M accessors

diff --git a/include/geos/geom/CoordinateSequence.h b/include/geos/geom/CoordinateSequence.h
index 5e0a7776b..e4a1c5553 100644
--- a/include/geos/geom/CoordinateSequence.h
+++ b/include/geos/geom/CoordinateSequence.h
@@ -337,6 +337,80 @@ public:
         return m_vect[index * stride() + 1];
     }
 
+    /**
+     * Returns ordinate Z of the specified coordinate.
+     *
+     * @param index
+     * @return the value of the Z ordinate in the index'th coordinate, or NaN if the
+     *         CoordinateSequence does not store Z values
+     */
+    double getZ(std::size_t index) const
+    {
+        return getOrdinate(index, Z);
+    }
+
+    /**
+     * Returns ordinate M of the specified coordinate.
+     *
+     * @param index
+     * @return the value of the M ordinate in the index'th coordinate, or NaN if the
+     *         CoordinateSequence does not store M values
+     */
+    double getM(std::size_t index) const
+    {
+        return getOrdinate(index, M);
+    }
+
+    /**
+     * Set the X value of the specified coordinate.
+     *
+     * @param index
+     * @param x the new X value
+     */
+    void setX(std::size_t index, double x)
+    {
+        m_vect[index * stride()] = x;
+    }
+
+    /**
+     * Set the Y value of the specified coordinate.
+     *
+     * @param index
+     * @param y the new Y value
+     */
+    void setY(std::size_t index, double y)
+    {
+        m_vect[index * stride() + 1] = y;
+    }
+
+    /**
+     * Set the Z value of the specified coordinate.
+     *
+     * Has no effect if the CoordinateSequence does not store Z values.
+     *
+     * @param index
+     * @param z the new Z value
+     */
+    void setZ(std::size_t index, double z)
+    {
+        if (hasZ())
+            setOrdinate(index, Z, z);
+    }
+
+    /**
+     * Set the M value of the specified coordinate.
+     *
+     * Has no effect if the CoordinateSequence does not store M values.
+     *
+     * @param index
+     * @param m the new M value
+     */
+    void setM(std::size_t index, double m)
+    {
+        if (hasM())
+            setOrdinate(index, M, m);
+    }
+
     /// Return last Coordinate in the sequence
     template<typename T=Coordinate>
     const T& back() const
diff --git a/tests/unit/geom/CoordinateSequenceTest.cpp b/tests/unit/geom/CoordinateSequenceTest.cpp
index e2a94fb41..a0c86888e 100644
--- a/tests/unit/geom/CoordinateSequenceTest.cpp
+++ b/tests/unit/geom/CoordinateSequenceTest.cpp
@@ -1580,4 +1580,41 @@ void object::test<59>()
     ensure_equals(length, 8);
 }
 
+template<>
+template<>
+void object::test<60>()
+{
+    set_test_name("Z/M accessors on XY sequence");
+
+    CoordinateSequence seq(0, false, false);
+    seq.add(CoordinateXY{0, 0});
+    seq.add(CoordinateXY{3, 0});
+
+    seq.setZ(0, 500);
+    seq.setM(0, 501);
+
+    ensure(std::isnan(seq.getZ(0)));
+    ensure(std::isnan(seq.getM(0)));
+}
+
+template<>
+template<>
+void object::test<61>()
+{
+    set_test_name("Z/M accessors on XYZM sequence");
+
+    CoordinateSequence seq(0, true, true);
+    seq.add(CoordinateXY{0, 0});
+    seq.add(CoordinateXY{3, 0});
+
+    seq.setZ(0, 500);
+    seq.setM(0, 501);
+
+    ensure_equals(seq.getZ(0), 500);
+    ensure_equals(seq.getM(0), 501);
+
+    ensure(std::isnan(seq.getZ(1)));
+    ensure(std::isnan(seq.getM(1)));
+}
+
 } // namespace tut

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

Summary of changes:
 include/geos/geom/CoordinateSequence.h            | 74 +++++++++++++++++++++++
 src/operation/linemerge/EdgeString.cpp            | 40 ++++++++++--
 tests/unit/geom/CoordinateSequenceTest.cpp        | 37 ++++++++++++
 tests/unit/operation/linemerge/LineMergerTest.cpp | 31 ++++++++++
 tests/unit/utility.h                              |  4 +-
 5 files changed, 178 insertions(+), 8 deletions(-)


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list