From eb06d8ea4168244d8c6c293ff7379d7bdac6be59 Mon Sep 17 00:00:00 2001 From: Dan Baston Date: Tue, 28 Jul 2026 17:18:20 -0400 Subject: [PATCH 1/2] Add GEOSGeom_homogenize --- capi/geos_c.cpp | 6 ++ capi/geos_c.h.in | 21 +++++ capi/geos_ts_c.cpp | 12 +++ include/geos/geom/util/GeometryExtracter.h | 2 +- tests/unit/capi/GEOSGeom_homogenizeTest.cpp | 96 +++++++++++++++++++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 tests/unit/capi/GEOSGeom_homogenizeTest.cpp diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp index 568f1c0b47..83d77eed1d 100644 --- a/capi/geos_c.cpp +++ b/capi/geos_c.cpp @@ -1510,6 +1510,12 @@ extern "C" { return GEOSGeom_createCurvePolygon_r(handle, shell, holes, nholes); } + Geometry* + GEOSGeom_homogenize(Geometry* g) + { + return GEOSGeom_homogenize_r(handle, g); + } + Geometry* GEOSGeom_clone(const Geometry* g) { diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index 8121146bae..b0a09e7b82 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -944,6 +944,11 @@ extern GEOSGeometry GEOS_DLL *GEOSGeom_createRectangle_r( double xmin, double ymin, double xmax, double ymax); +/** \see GEOSGeom_homogenize */ +extern GEOSGeometry GEOS_DLL * GEOSGeom_homogenize_r( + GEOSContextHandle_t handle, + GEOSGeometry * g); + /** \see GEOSGeom_clone */ extern GEOSGeometry GEOS_DLL *GEOSGeom_clone_r( GEOSContextHandle_t handle, @@ -3107,6 +3112,22 @@ extern GEOSGeometry GEOS_DLL *GEOSGeom_createRectangle( double xmin, double ymin, double xmax, double ymax); +/** +* Returns the most specific representation of a geometry. +* +* If the input is a multi-element GeometryCollection whose elements all have +* the same type, then the input will be destroyed and a new collection +* of an appropriate type returned (MultiLineString, MultiPolygon, etc.) If the +* input is a single-element GeometryCollection, then the input will be +* destroyed and the single element returned. If the elements are not +* homogeneous, or if the input is not a collection, it will be returned +* unmodified. Nested collections are collapsed during processing. +* +* \since 3.15 +*/ +extern GEOSGeometry GEOS_DLL *GEOSGeom_homogenize( + GEOSGeometry* g); + /** * Create a new copy of the input geometry. * \param g The geometry to copy diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index ee6391ca2c..7b92af1b06 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -3860,6 +3860,18 @@ extern "C" { }); } + Geometry* + GEOSGeom_homogenize_r(GEOSContextHandle_t extHandle, Geometry* g) + { + return execute(extHandle, [&]() -> Geometry* { + const int srid = g->getSRID(); + + auto g3 = geos::operation::cluster::GeometryFlattener::flatten(std::unique_ptr(g)); + g3->setSRID(srid); + return g3.release(); + }); + } + Geometry* GEOSGeom_clone_r(GEOSContextHandle_t extHandle, const Geometry* g) { diff --git a/include/geos/geom/util/GeometryExtracter.h b/include/geos/geom/util/GeometryExtracter.h index f1bed3bd4e..0773e103b3 100644 --- a/include/geos/geom/util/GeometryExtracter.h +++ b/include/geos/geom/util/GeometryExtracter.h @@ -67,7 +67,7 @@ class GEOS_DLL GeometryExtracter { * * @param comps the container to extract into (will push_back to it) */ - Extracter(TargetContainer& comps) : comps_(comps) {} + explicit Extracter(TargetContainer& comps) : comps_(comps) {} TargetContainer& comps_; diff --git a/tests/unit/capi/GEOSGeom_homogenizeTest.cpp b/tests/unit/capi/GEOSGeom_homogenizeTest.cpp new file mode 100644 index 0000000000..92f7284323 --- /dev/null +++ b/tests/unit/capi/GEOSGeom_homogenizeTest.cpp @@ -0,0 +1,96 @@ +#include +// geos +#include + +#include "capi_test_utils.h" + +namespace tut { +struct test_capigeosgeom_homogenize : public capitest::utility { + + void testHomogenize(const std::string& wkt_in, const std::string& wkt_expected) { + GEOSGeometry* input = fromWKT(wkt_in.c_str()); + result_ = GEOSGeom_homogenize(input); + expected_ = fromWKT(wkt_expected.c_str()); + + ensure_geometry_equals(result_, expected_); + } + + void testHomogenizeUnchanged(const std::string& wkt) { + testHomogenize(wkt, wkt); + } +}; + +typedef test_group group; +typedef group::object object; + +group test_capigeosgeom_homogenize_group("capi::GEOSGeom_homogenize"); + +template<> +template<> +void object::test<1>() +{ + set_test_name("non-collection type"); + + testHomogenizeUnchanged("LINESTRING (0 0, 1 1)"); +} + +template<> +template<> +void object::test<2>() +{ + set_test_name("non-homogeneous collection"); + + testHomogenizeUnchanged("GEOMETRYCOLLECTION (LINESTRING (0 0, 1 1), POINT (2 2))"); +} + +template<> +template<> +void object::test<3>() +{ + set_test_name("single-element collection"); + + testHomogenize("GEOMETRYCOLLECTION (POINT (0 2))", "POINT (0 2)"); + testHomogenize("MULTIPOINT ((8 2))", "POINT (8 2)"); +} + +template<> +template<> +void object::test<4>() +{ + set_test_name("homogeneous collection"); + + testHomogenize("GEOMETRYCOLLECTION (LINESTRING (0 0, 1 1), LINESTRING (4 3, 2 7))", + "MULTILINESTRING ((0 0, 1 1), (4 3, 2 7))" ); +} + +template<> +template<> +void object::test<5>() +{ + set_test_name("empty collection"); + + testHomogenizeUnchanged("GEOMETRYCOLLECTION EMPTY"); +} + +template<> +template<> +void object::test<6>() +{ + set_test_name("nested homogeneous collection"); + + testHomogenize("GEOMETRYCOLLECTION (LINESTRING (0 0, 1 1), GEOMETRYCOLLECTION(LINESTRING EMPTY, MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))))", + "MULTILINESTRING ((0 0, 1 1), (1 1, 2 2), (2 2, 3 3), EMPTY)"); +} + +template<> +template<> +void object::test<7>() +{ + set_test_name("nested non-homogeneous collection"); + + testHomogenize("GEOMETRYCOLLECTION (LINESTRING (0 0, 1 1), GEOMETRYCOLLECTION(COMPOUNDCURVE ((1 1, 2 2))))", + "MULTICURVE (LINESTRING (0 0, 1 1), COMPOUNDCURVE ((1 1, 2 2)))"); +} + +} // namespace tut + From 4b1fce0af75eba602b27c39e52dea28b32c18b7c Mon Sep 17 00:00:00 2001 From: Dan Baston Date: Tue, 28 Jul 2026 21:48:57 -0400 Subject: [PATCH 2/2] fix memory leak in test --- tests/unit/capi/GEOSGeom_homogenizeTest.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/unit/capi/GEOSGeom_homogenizeTest.cpp b/tests/unit/capi/GEOSGeom_homogenizeTest.cpp index 92f7284323..5f03dd8072 100644 --- a/tests/unit/capi/GEOSGeom_homogenizeTest.cpp +++ b/tests/unit/capi/GEOSGeom_homogenizeTest.cpp @@ -47,15 +47,23 @@ template<> template<> void object::test<3>() { - set_test_name("single-element collection"); + set_test_name("single-element generic collection"); testHomogenize("GEOMETRYCOLLECTION (POINT (0 2))", "POINT (0 2)"); - testHomogenize("MULTIPOINT ((8 2))", "POINT (8 2)"); } template<> template<> void object::test<4>() +{ + set_test_name("single-element typed collection"); + + testHomogenize("MULTIPOINT ((8 2))", "POINT (8 2)"); +} + +template<> +template<> +void object::test<5>() { set_test_name("homogeneous collection"); @@ -65,7 +73,7 @@ void object::test<4>() template<> template<> -void object::test<5>() +void object::test<6>() { set_test_name("empty collection"); @@ -74,7 +82,7 @@ void object::test<5>() template<> template<> -void object::test<6>() +void object::test<7>() { set_test_name("nested homogeneous collection"); @@ -84,7 +92,7 @@ void object::test<6>() template<> template<> -void object::test<7>() +void object::test<8>() { set_test_name("nested non-homogeneous collection");