[geos-commits] [SCM] GEOS branch main updated. 9f2be966553f6b81b69b1d2ec7aaf2b6bc3b79ac
git at osgeo.org
git at osgeo.org
Sat Mar 7 10:32:23 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 9f2be966553f6b81b69b1d2ec7aaf2b6bc3b79ac (commit)
from 0f150b39c332b89db633e88c83a7e27dc3b13c1b (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 9f2be966553f6b81b69b1d2ec7aaf2b6bc3b79ac
Author: Daniel Baston <dbaston at gmail.com>
Date: Sat Mar 7 13:31:57 2026 -0500
GEOSInterpolate: Interpolate M value (#1390)
diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in
index 8c85598e4..800d3f473 100644
--- a/capi/geos_c.h.in
+++ b/capi/geos_c.h.in
@@ -3874,10 +3874,13 @@ extern double GEOS_DLL GEOSProject(const GEOSGeometry* line,
const GEOSGeometry* point);
/**
-* Measuring from start of line, return point that is distance
-* the start. Line parameter must be a LineString.
-* The returned point is not guaranteed to intersect the line due to limitations
-* of floating point calculations.
+* Return the point that is the specified 2D distance along a LineString from the start.
+*
+* If applicable, the Z value of the returned point will be interpolated from the input line.
+* Since GEOS 3.15, the M value will also be interpolated.
+*
+* If the input is not a LineString, NULL will be returned.
+*
* \param line linear target of projection
* \param d distance from start of line to created point
* \return The point \ref GEOSGeometry that is distance from the start of line.
@@ -3901,8 +3904,14 @@ extern double GEOS_DLL GEOSProjectNormalized(const GEOSGeometry* line,
const GEOSGeometry* point);
/**
-* Measuring from start of line, return point that is a proportion
-* the start. Line parameter must be a LineString.
+* Measuring from start of a LineString, return the point at the specified
+* fraction of the total length.
+*
+* If applicable, the Z value of the returned point will be interpolated from the input line.
+* Since GEOS 3.15, the M value will also be interpolated.
+*
+* If the input is not a LineString, NULL will be returned.
+*
* \param line linear target of projection
* \param proportion The proportion from the start of line to created point
* \return The point \ref GEOSGeometry that is distance from the start of line.
diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index e082f799c..78a96a612 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -4291,11 +4291,21 @@ extern "C" {
{
return execute(extHandle, [&]() {
GEOSContextHandleInternal_t* handle = reinterpret_cast<GEOSContextHandleInternal_t*>(extHandle);
+ const GeometryFactory* gf = handle->geomFactory;
geos::linearref::LengthIndexedLine lil(g);
- geos::geom::Coordinate coord = lil.extractPoint(d);
- const GeometryFactory* gf = handle->geomFactory;
- auto point = coord.isNull() ? gf->createPoint(g->getCoordinateDimension()) : gf->createPoint(coord);
+ CoordinateXYZM coord = lil.extractPoint(d);
+
+ std::unique_ptr<Point> point;
+
+ if (coord.isNull()) {
+ point = gf->createPoint(g->hasZ(), g->hasM());
+ } else {
+ auto seq = std::make_unique<CoordinateSequence>(1, g->hasZ(), g->hasM());
+ seq->setAt(coord, 0);
+ point = gf->createPoint(std::move(seq));
+ }
+
point->setSRID(g->getSRID());
return point.release();
});
diff --git a/include/geos/linearref/LengthIndexedLine.h b/include/geos/linearref/LengthIndexedLine.h
index 42af91e58..7f5c51075 100644
--- a/include/geos/linearref/LengthIndexedLine.h
+++ b/include/geos/linearref/LengthIndexedLine.h
@@ -64,14 +64,13 @@ public:
*
* If the index is out of range the first or last point on the
* line will be returned.
- * The Z-ordinate of the computed point will be interpolated from
- * the Z-ordinates of the line segment containing it, if they exist.
+ * The Z and M ordinates of the computed point will be interpolated from
+ * the Z and M ordinates of the line segment containing it, if they exist.
*
* @param index the index of the desired point
* @return the Coordinate at the given index
*/
- geom::Coordinate extractPoint(double index) const;
-
+ geom::CoordinateXYZM extractPoint(double index) const;
/**
* \brief
diff --git a/include/geos/linearref/LinearLocation.h b/include/geos/linearref/LinearLocation.h
index bbce2b020..623204587 100644
--- a/include/geos/linearref/LinearLocation.h
+++ b/include/geos/linearref/LinearLocation.h
@@ -84,6 +84,8 @@ public:
*/
static geom::Coordinate pointAlongSegmentByFraction(const geom::Coordinate& p0, const geom::Coordinate& p1,
double frac);
+ static geom::CoordinateXYZM pointAlongSegmentByFraction(const geom::CoordinateXYZM& p0, const geom::CoordinateXYZM& p1,
+ double frac);
/** \brief
@@ -161,7 +163,7 @@ public:
* @param linearGeom the linear geometry referenced by this location
* @return the Coordinate at the location
*/
- geom::Coordinate getCoordinate(const geom::Geometry* linearGeom) const;
+ geom::CoordinateXYZM getCoordinate(const geom::Geometry* linearGeom) const;
/** \brief
* Gets a [LineSegment](@ref geom::LineSegment) representing the segment of the
diff --git a/src/linearref/LengthIndexedLine.cpp b/src/linearref/LengthIndexedLine.cpp
index 5522eb32c..d25c02168 100644
--- a/src/linearref/LengthIndexedLine.cpp
+++ b/src/linearref/LengthIndexedLine.cpp
@@ -36,11 +36,11 @@ namespace linearref { // geos.linearref
LengthIndexedLine::LengthIndexedLine(const Geometry* p_linearGeom) :
linearGeom(p_linearGeom) {}
-Coordinate
+CoordinateXYZM
LengthIndexedLine::extractPoint(double index) const
{
LinearLocation loc = LengthLocationMap::getLocation(linearGeom, index);
- Coordinate coord = loc.getCoordinate(linearGeom);
+ CoordinateXYZM coord = loc.getCoordinate(linearGeom);
return coord;
}
diff --git a/src/linearref/LinearLocation.cpp b/src/linearref/LinearLocation.cpp
index 5eabfab9c..09bbb1c32 100644
--- a/src/linearref/LinearLocation.cpp
+++ b/src/linearref/LinearLocation.cpp
@@ -59,6 +59,25 @@ LinearLocation::pointAlongSegmentByFraction(const Coordinate& p0, const Coordina
return Coordinate(x, y, z);
}
+CoordinateXYZM
+LinearLocation::pointAlongSegmentByFraction(const CoordinateXYZM& p0, const CoordinateXYZM& p1, double frac)
+{
+ if(frac <= 0.0) {
+ return p0;
+ }
+ if(frac >= 1.0) {
+ return p1;
+ }
+
+ double x = (p1.x - p0.x) * frac + p0.x;
+ double y = (p1.y - p0.y) * frac + p0.y;
+ // interpolate Z/M values. If either is NaN, result will be NaN as well.
+ double z = (p1.z - p0.z) * frac + p0.z;
+ double m = (p1.m - p0.m) * frac + p0.m;
+
+ return CoordinateXYZM(x, y, z, m);
+}
+
/* public */
LinearLocation::LinearLocation(std::size_t p_segmentIndex,
double p_segmentFraction)
@@ -201,21 +220,27 @@ LinearLocation::isVertex() const
}
/* public */
-Coordinate
+CoordinateXYZM
LinearLocation::getCoordinate(const Geometry* linearGeom) const
{
if(linearGeom->isEmpty()) {
- return Coordinate::getNull();
+ return CoordinateXYZM::getNull();
}
const LineString* lineComp = dynamic_cast<const LineString*>(linearGeom->getGeometryN(componentIndex));
if(! lineComp) {
throw util::IllegalArgumentException("LinearLocation::getCoordinate only works with LineString geometries");
}
- Coordinate p0 = lineComp->getCoordinateN(segmentIndex);
- if(segmentIndex >= lineComp->getNumPoints() - 1) {
+
+ const CoordinateSequence* pts = lineComp->getCoordinatesRO();
+ CoordinateXYZM p0;
+ pts->getAt(segmentIndex, p0);
+ if (segmentIndex >= pts->size() - 1) {
return p0;
}
- Coordinate p1 = lineComp->getCoordinateN(segmentIndex + 1);
+
+ CoordinateXYZM p1;
+ pts->getAt(segmentIndex + 1, p1);
+
return pointAlongSegmentByFraction(p0, p1, segmentFraction);
}
diff --git a/tests/unit/capi/GEOSInterpolateTest.cpp b/tests/unit/capi/GEOSInterpolateTest.cpp
index 44992617f..5cfeec787 100644
--- a/tests/unit/capi/GEOSInterpolateTest.cpp
+++ b/tests/unit/capi/GEOSInterpolateTest.cpp
@@ -3,11 +3,6 @@
#include <tut/tut.hpp>
// geos
#include <geos_c.h>
-// std
-#include <cstdarg>
-#include <cstdio>
-#include <cstdlib>
-#include <cmath>
#include "capi_test_utils.h"
@@ -57,7 +52,7 @@ void object::test<3>
result_ = GEOSInterpolate(geom1_, 0.5);
expected_ = GEOSGeomFromWKT("POINT (0.5 0)");
- ensure_geometry_equals(result_, expected_);
+ ensure_geometry_equals_identical(result_, expected_);
}
template<>
@@ -69,7 +64,7 @@ void object::test<4>
result_ = GEOSInterpolateNormalized(geom1_, 0.5);
expected_ = GEOSGeomFromWKT("POINT (5 0)");
- ensure_geometry_equals(result_, expected_);
+ ensure_geometry_equals_identical(result_, expected_);
}
// ensure NaN argument does not crash
@@ -82,4 +77,75 @@ void object::test<5>
result_ = GEOSInterpolate(geom1_, std::numeric_limits<double>::quiet_NaN());
}
+template<>
+template<>
+void object::test<6>
+()
+{
+ set_test_name("Z/M interpolated");
+
+ geom1_ = fromWKT("LINESTRING ZM (0 0 6 8, 10 0 12 16)");
+ result_ = GEOSInterpolate(geom1_, 5);
+ expected_ = fromWKT("POINT ZM (5 0 9 12)");
+
+ ensure_geometry_equals_identical(result_, expected_);
+}
+
+template<>
+template<>
+void object::test<7>
+()
+{
+ set_test_name("Empty LINESTRING M");
+
+ geom1_ = fromWKT("LINESTRING M EMPTY");
+ result_ = GEOSInterpolate(geom1_, 5);
+ expected_ = fromWKT("POINT M EMPTY");
+
+ ensure_geometry_equals_identical(result_, expected_);
+}
+
+template<>
+template<>
+void object::test<8>
+()
+{
+ set_test_name("Empty LINESTRING Z");
+
+ geom1_ = fromWKT("LINESTRING Z EMPTY");
+ result_ = GEOSInterpolate(geom1_, 5);
+ expected_ = fromWKT("POINT Z EMPTY");
+
+ ensure_geometry_equals_identical(result_, expected_);
+}
+
+template<>
+template<>
+void object::test<9>
+()
+{
+ set_test_name("Empty LINESTRING ZM");
+
+ geom1_ = fromWKT("LINESTRING ZM EMPTY");
+ result_ = GEOSInterpolate(geom1_, 5);
+ expected_ = fromWKT("POINT ZM EMPTY");
+
+ ensure_geometry_equals_identical(result_, expected_);
+}
+
+template<>
+template<>
+void object::test<10>
+()
+{
+ set_test_name("Non-LineString input");
+
+ input_ = fromWKT("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))");
+ ensure(input_);
+
+ result_ = GEOSInterpolate(input_, 5);
+
+ ensure(result_ == nullptr);
+}
+
} // namespace tut
-----------------------------------------------------------------------
Summary of changes:
capi/geos_c.h.in | 21 +++++---
capi/geos_ts_c.cpp | 16 ++++--
include/geos/linearref/LengthIndexedLine.h | 7 ++-
include/geos/linearref/LinearLocation.h | 4 +-
src/linearref/LengthIndexedLine.cpp | 4 +-
src/linearref/LinearLocation.cpp | 35 +++++++++++--
tests/unit/capi/GEOSInterpolateTest.cpp | 80 +++++++++++++++++++++++++++---
7 files changed, 139 insertions(+), 28 deletions(-)
hooks/post-receive
--
GEOS
More information about the geos-commits
mailing list