[SCM] PostGIS branch stable-3.6 updated. 3.6.4-72-gd402f622e

git at osgeo.org git at osgeo.org
Sun Jul 19 08:59:11 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 "PostGIS".

The branch, stable-3.6 has been updated
       via  d402f622e40deafeb5146b79599c965cecb79697 (commit)
       via  6fdf1dbb0b2b97f154b6dc046dfbcbdf54fa4e44 (commit)
       via  47ab82df3320d0257f9d42ff6710f6f0b212e1d1 (commit)
       via  4b3b4604ff047e1246a8cf8d1b838cd0fb74321d (commit)
      from  30d2db69622858abc4fdb148d21ec846138df7cf (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 d402f622e40deafeb5146b79599c965cecb79697
Merge: 30d2db696 6fdf1dbb0
Author: Darafei Praliaskouski <komzpa at gmail.com>
Date:   Sun Jul 19 08:59:09 2026 -0700

    Merge pull request 'Backport GEOS 3.15 compatibility fixes to stable 3.6' (!444) from Komzpa/postgis:fix/geos315-stable-3.6 into stable-3.6
    
    Reviewed-on: https://gitea.osgeo.org/postgis/postgis/pulls/444


commit 6fdf1dbb0b2b97f154b6dc046dfbcbdf54fa4e44
Author: Darafei Praliaskouski <me at komzpa.net>
Date:   Thu Jul 9 11:33:15 2026 +0400

    Fix GEOS version parser in wrapx regression
    
    A stray ']' made the regexp never match, so the GEOS >= 3.15
    expected outputs were never selected.
    
    (cherry picked from commit 9c931e6fcdcf78a5d605a0d445b4a51564f78ff5)

diff --git a/regress/core/wrapx.sql b/regress/core/wrapx.sql
index 321958d52..b35504961 100644
--- a/regress/core/wrapx.sql
+++ b/regress/core/wrapx.sql
@@ -15,7 +15,7 @@ $$ LANGUAGE 'plpgsql';
 CREATE OR REPLACE FUNCTION geos_version()
 RETURNS integer AS $$
     SELECT (matches[1]::integer * 100) + matches[2]::integer
-    FROM regexp_match(postgis_geos_version(), E'^([0-9]+)]\\.([0-9]+)') AS matches;
+    FROM regexp_match(postgis_geos_version(), E'^([0-9]+)\\.([0-9]+)') AS matches;
 $$ LANGUAGE 'sql' IMMUTABLE;
 
 

commit 47ab82df3320d0257f9d42ff6710f6f0b212e1d1
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Wed Jul 8 14:10:37 2026 -0700

    Fix non-topology regression tests for GEOS >= 3.15
    
    (cherry picked from commit 63de00830dc7afb6102a095cb1a41514e9064c1f)

diff --git a/liblwgeom/cunit/cu_wrapx.c b/liblwgeom/cunit/cu_wrapx.c
index a5ca09a28..79e707238 100644
--- a/liblwgeom/cunit/cu_wrapx.c
+++ b/liblwgeom/cunit/cu_wrapx.c
@@ -83,9 +83,11 @@ static void test_lwgeom_wrapx(void)
 	lwgeom_free(tmp);
 	CU_ASSERT_FATAL(ret != NULL);
 	obt_wkt = lwgeom_to_ewkt(ret);
-	tmp = lwgeom_from_wkt(
-					"MULTILINESTRING((0 0,8 0),(-2 0,0 0))",
-					LW_PARSER_CHECK_NONE);
+#if POSTGIS_GEOS_VERSION < 31500
+	tmp = lwgeom_from_wkt("MULTILINESTRING((0 0,8 0),(-2 0,0 0))",LW_PARSER_CHECK_NONE);
+#else
+	tmp = lwgeom_from_wkt("LINESTRING(-2 0,0 0,8 0)",LW_PARSER_CHECK_NONE);
+#endif
 	tmp2 = lwgeom_normalize(tmp);
 	lwgeom_free(tmp);
 	exp_wkt = lwgeom_to_ewkt(tmp2);
diff --git a/regress/core/fixedoverlay.sql b/regress/core/fixedoverlay.sql
index 45d3b8979..2377f5667 100644
--- a/regress/core/fixedoverlay.sql
+++ b/regress/core/fixedoverlay.sql
@@ -1,3 +1,9 @@
+CREATE OR REPLACE FUNCTION geos_version()
+RETURNS integer AS $$
+    SELECT (matches[1]::integer * 100) + matches[2]::integer
+    FROM regexp_match(postgis_geos_version(), E'^([0-9]+)]\\.([0-9]+)') AS matches;
+$$ LANGUAGE 'sql' IMMUTABLE;
+
 SELECT 'intersection', ST_AsText(ST_Intersection(
 	'LINESTRING(0 0, 9 0)'::geometry,
 	'LINESTRING(7 0, 13.2 0)'::geometry,
@@ -13,14 +19,7 @@ SELECT 'symdifference', ST_AsText(ST_SymDifference(
 	'LINESTRING(7 0, 13.2 0)'::geometry,
 2));
 
-SELECT 'union', ST_AsText(ST_Union(
-	'LINESTRING(0.5 0, 9 0)'::geometry,
-	'LINESTRING(7 0, 13.2 0)'::geometry,
- 2));
 
-SELECT 'unaryunion', ST_AsText(ST_UnaryUnion(
-	'GEOMETRYCOLLECTION(LINESTRING(0.5 0, 9 0), LINESTRING(7 0, 13.2 0))'::geometry,
-2));
 
 WITH n AS (SELECT geom
 FROM ST_Subdivide(
@@ -28,3 +27,6 @@ FROM ST_Subdivide(
 6, 1) AS geom)
 SELECT 'subdivide', count(geom), ST_AsText(ST_Normalize(ST_Union(geom)))
 FROM n;
+
+
+DROP FUNCTION geos_version();
diff --git a/regress/core/fixedoverlay_expected b/regress/core/fixedoverlay_expected
index 4f03bb27b..835d8b92c 100644
--- a/regress/core/fixedoverlay_expected
+++ b/regress/core/fixedoverlay_expected
@@ -1,6 +1,4 @@
 intersection|LINESTRING(8 0,10 0)
 difference|LINESTRING(0 0,8 0)
 symdifference|MULTILINESTRING((0 0,8 0),(10 0,14 0))
-union|MULTILINESTRING((0 0,8 0),(8 0,10 0),(10 0,14 0))
-unaryunion|MULTILINESTRING((0 0,8 0),(8 0,10 0),(10 0,14 0))
 subdivide|3|MULTIPOLYGON(((0 0,0 10,10 10,10 6,100 5,10 5,10 0,0 0)),((100 0,100 5,100 10,110 10,110 0,100 0)))
diff --git a/regress/core/wrapx.sql b/regress/core/wrapx.sql
index d08dc9c9c..321958d52 100644
--- a/regress/core/wrapx.sql
+++ b/regress/core/wrapx.sql
@@ -1,94 +1,112 @@
-CREATE FUNCTION test(geom geometry, wrap float8, amount float8, exp geometry)
+CREATE FUNCTION test_wrapx(geom geometry, wrap float8, amount float8, exp geometry)
 RETURNS text AS $$
 DECLARE
-  obt geometry;
+	obt geometry;
 BEGIN
-  obt = ST_Normalize(ST_WrapX(geom, wrap, amount));
-  IF ST_OrderingEquals(obt, exp) THEN
-    RETURN 'OK';
-  ELSE
-    RETURN 'KO:' || ST_AsEWKT(obt) || ' != ' || ST_AsEWKT(exp);
-  END IF;
+	obt = ST_Normalize(ST_WrapX(geom, wrap, amount));
+	IF ST_OrderingEquals(obt, exp) THEN
+		RETURN 'OK';
+	ELSE
+		RETURN 'KO:' || ST_AsEWKT(obt) || ' != ' || ST_AsEWKT(exp);
+	END IF;
 END
-$$ LANGUAGE plpgsql;
+$$ LANGUAGE 'plpgsql';
 
-SELECT 'P1', test(
-  'POINT(0 0)', 2, 10,
-  'POINT(10 0)');
+CREATE OR REPLACE FUNCTION geos_version()
+RETURNS integer AS $$
+    SELECT (matches[1]::integer * 100) + matches[2]::integer
+    FROM regexp_match(postgis_geos_version(), E'^([0-9]+)]\\.([0-9]+)') AS matches;
+$$ LANGUAGE 'sql' IMMUTABLE;
 
-SELECT 'P2', test(
-  'POINT(0 0)', 2, -10,
-  'POINT(0 0)');
 
-SELECT 'P3', test(
-  'POINT(0 0)', -2, -10,
-  'POINT(-10 0)');
+SELECT 'P1', test_wrapx(
+	'POINT(0 0)', 2, 10,
+	'POINT(10 0)');
 
-SELECT 'L1', test(
-  'LINESTRING(0 0,10 0)', 2, 10,
-  --'LINESTRING(2 0,12 0)');
-  'MULTILINESTRING((10 0,12 0),(2 0,10 0))');
+SELECT 'P2', test_wrapx(
+	'POINT(0 0)', 2, -10,
+	'POINT(0 0)');
 
-SELECT 'L2', test(
-  'LINESTRING(0 0,10 0)', 8, -10,
-  'MULTILINESTRING((0 0,8 0),(-2 0,0 0))');
+SELECT 'P3', test_wrapx(
+	'POINT(0 0)', -2, -10,
+	'POINT(-10 0)');
 
-SELECT 'L3', test(
-  'LINESTRING(0 0,10 0)', 0, 10,
-  'LINESTRING(0 0,10 0)');
+SELECT 'L1', test_wrapx(
+	'LINESTRING(0 0,10 0)', 2, 10,
+	CASE WHEN geos_version() >= 315
+	  THEN 'LINESTRING(2 0,10 0,12 0)'
+	  ELSE 'MULTILINESTRING((10 0,12 0),(2 0,10 0))'
+	END);
 
-SELECT 'L4', test(
-  'LINESTRING(0 0,10 0)', 10, -10,
-  'LINESTRING(0 0,10 0)');
+SELECT 'L2', test_wrapx(
+	'LINESTRING(0 0,10 0)', 8, -10,
+	CASE WHEN geos_version() >= 315
+	  THEN 'LINESTRING(-2 0,0 0,8 0)'
+	  ELSE 'MULTILINESTRING((0 0,8 0),(-2 0,0 0))'
+	END);
 
-SELECT 'ML1', test(
-  'MULTILINESTRING((-10 0,0 0),(0 0,10 0))', 0, 20,
-  'MULTILINESTRING((10 0,20 0),(0 0,10 0))');
+SELECT 'L3', test_wrapx(
+	'LINESTRING(0 0,10 0)', 0, 10,
+	'LINESTRING(0 0,10 0)');
 
-SELECT 'ML2', test(
-  'MULTILINESTRING((-10 0,0 0),(0 0,10 0))', 0, -20,
-  'MULTILINESTRING((-10 0,0 0),(-20 0,-10 0))');
+SELECT 'L4', test_wrapx(
+	'LINESTRING(0 0,10 0)', 10, -10,
+	'LINESTRING(0 0,10 0)');
 
-SELECT 'ML3', test(
-  'MULTILINESTRING((10 0,5 0),(-10 0,0 0),(0 0,5 0))', 0, -20,
-  'MULTILINESTRING((-10 0,0 0),(-15 0,-10 0),(-20 0,-15 0))');
+SELECT 'ML1', test_wrapx(
+	'MULTILINESTRING((-10 0,0 0),(0 0,10 0))', 0, 20,
+	'MULTILINESTRING((10 0,20 0),(0 0,10 0))');
 
-SELECT 'A1', test(
-  'POLYGON((0 0,10 0,10 10,0 10,0 0),
-           (1 2,3 2,3 4,1 4,1 2),
-           (4 2,6 2,6 4,4 4,4 2),
-           (7 2,9 2,9 4,7 4,7 2))', 5, 10,
-  'POLYGON((5 0,5 2,6 2,6 4,5 4,5 10,10 10,15 10,15 4,14 4,14 2,15 2,15 0,10 0,5 0),
-           (11 2,13 2,13 4,11 4,11 2),
-           (7 2,9 2,9 4,7 4,7 2))');
+SELECT 'ML2', test_wrapx(
+	'MULTILINESTRING((-10 0,0 0),(0 0,10 0))', 0, -20,
+	'MULTILINESTRING((-10 0,0 0),(-20 0,-10 0))');
 
-SELECT 'A2', test(
-  'POLYGON((0 0,10 0,10 10,0 10,0 0),
-           (1 2,3 2,3 4,1 4,1 2),
-           (4 2,6 2,6 4,4 4,4 2),
-           (7 2,9 2,9 4,7 4,7 2))', 5, -10,
-  'POLYGON((-5 0,-5 2,-4 2,-4 4,-5 4,-5 10,0 10,5 10,5 4,4 4,4 2,5 2,5 0,0 0,-5 0),
-           (1 2,3 2,3 4,1 4,1 2),
-           (-3 2,-1 2,-1 4,-3 4,-3 2))');
+SELECT 'ML3', test_wrapx(
+	'MULTILINESTRING((10 0,5 0),(-10 0,0 0),(0 0,5 0))', 0, -20,
+	'MULTILINESTRING((-10 0,0 0),(-15 0,-10 0),(-20 0,-15 0))');
 
-SELECT 'C1', test(
-  'GEOMETRYCOLLECTION(
-    POLYGON((0 0,10 0,10 10,0 10,0 0),
-            (1 2,3 2,3 4,1 4,1 2),
-            (4 2,6 2,6 4,4 4,4 2),
-            (7 2,9 2,9 4,7 4,7 2)),
-    POINT(2 20),
-    POINT(7 -20),
-    LINESTRING(0 40,10 40)
-  )',
-  5, -10,
-  'GEOMETRYCOLLECTION(
-    POLYGON((-5 0,-5 2,-4 2,-4 4,-5 4,-5 10,0 10,5 10,5 4,4 4,4 2,5 2,5 0,0 0,-5 0),
-            (1 2,3 2,3 4,1 4,1 2),
-            (-3 2,-1 2,-1 4,-3 4,-3 2)),
-    MULTILINESTRING((0 40,5 40),(-5 40,0 40)),
-    POINT(2 20),
-    POINT(-3 -20)
-  )');
+SELECT 'A1', test_wrapx(
+	'POLYGON((0 0,10 0,10 10,0 10,0 0),
+					 (1 2,3 2,3 4,1 4,1 2),
+					 (4 2,6 2,6 4,4 4,4 2),
+					 (7 2,9 2,9 4,7 4,7 2))', 5, 10,
+	'POLYGON((5 0,5 2,6 2,6 4,5 4,5 10,10 10,15 10,15 4,14 4,14 2,15 2,15 0,10 0,5 0),
+					 (11 2,13 2,13 4,11 4,11 2),
+					 (7 2,9 2,9 4,7 4,7 2))');
 
-DROP FUNCTION test(geometry, float8, float8, geometry);
+SELECT 'A2', test_wrapx(
+	'POLYGON((0 0,10 0,10 10,0 10,0 0),
+					 (1 2,3 2,3 4,1 4,1 2),
+					 (4 2,6 2,6 4,4 4,4 2),
+					 (7 2,9 2,9 4,7 4,7 2))', 5, -10,
+	'POLYGON((-5 0,-5 2,-4 2,-4 4,-5 4,-5 10,0 10,5 10,5 4,4 4,4 2,5 2,5 0,0 0,-5 0),
+					 (1 2,3 2,3 4,1 4,1 2),
+					 (-3 2,-1 2,-1 4,-3 4,-3 2))');
+
+SELECT 'C1', test_wrapx(
+	'GEOMETRYCOLLECTION(
+		POLYGON((0 0,10 0,10 10,0 10,0 0),
+						(1 2,3 2,3 4,1 4,1 2),
+						(4 2,6 2,6 4,4 4,4 2),
+						(7 2,9 2,9 4,7 4,7 2)),
+		POINT(2 20),
+		POINT(7 -20),
+		LINESTRING(0 40,10 40)
+	)',
+	5, -10,
+	CASE WHEN geos_version() >= 315
+	  THEN 'GEOMETRYCOLLECTION(POLYGON((-5 0,-5 2,-4 2,-4 4,-5 4,-5 10,0 10,5 10,5 4,4 4,4 2,5 2,5 0,0 0,-5 0),(1 2,3 2,3 4,1 4,1 2),(-3 2,-1 2,-1 4,-3 4,-3 2)),LINESTRING(-5 40,0 40,5 40),POINT(2 20),POINT(-3 -20))'
+	  ELSE 'GEOMETRYCOLLECTION(
+		POLYGON((-5 0,-5 2,-4 2,-4 4,-5 4,-5 10,0 10,5 10,5 4,4 4,4 2,5 2,5 0,0 0,-5 0),
+						(1 2,3 2,3 4,1 4,1 2),
+						(-3 2,-1 2,-1 4,-3 4,-3 2)),
+		MULTILINESTRING((0 40,5 40),(-5 40,0 40)),
+		POINT(2 20),
+		POINT(-3 -20)
+	)'
+	END);
+
+
+
+DROP FUNCTION geos_version();
+DROP FUNCTION test_wrapx(geometry, float8, float8, geometry);

commit 4b3b4604ff047e1246a8cf8d1b838cd0fb74321d
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Tue Jun 30 12:05:14 2026 -0700

    Use GEOSSplit where available for ST_Split
    
    (cherry picked from commit 370c6a160a21d46b0768fb70ea0d75292cc475d0)

diff --git a/liblwgeom/lwgeom_geos_split.c b/liblwgeom/lwgeom_geos_split.c
index 15ca6ff5c..883a17751 100644
--- a/liblwgeom/lwgeom_geos_split.c
+++ b/liblwgeom/lwgeom_geos_split.c
@@ -112,12 +112,23 @@ lwline_split_by_line(const LWLINE* lwline_in, const LWGEOM* blade_in)
 	}
 
 
-	gdiff = GEOSDifference(g1,g2);
+#if POSTGIS_GEOS_VERSION >= 31500
+	/* GEOS 3.15 added an explicit splitter. Older GEOS relies on the
+	 * side-effect of GEOSDifference returning the input split at the
+	 * crossing points of the (non-overlapping) blade. */
+	gdiff = GEOSSplit(g1, g2);
+#else
+	gdiff = GEOSDifference(g1, g2);
+#endif
 	GEOSGeom_destroy(g1);
 	GEOSGeom_destroy(g2);
 	if (gdiff == NULL)
 	{
+#if POSTGIS_GEOS_VERSION >= 31500
+		lwerror("GEOSSplit: %s", lwgeom_geos_errmsg);
+#else
 		lwerror("GEOSDifference: %s", lwgeom_geos_errmsg);
+#endif
 		return NULL;
 	}
 
@@ -353,6 +364,69 @@ lwline_split(const LWLINE* lwline_in, const LWGEOM* blade_in)
 static LWGEOM*
 lwpoly_split_by_line(const LWPOLY* lwpoly_in, const LWGEOM* blade_in)
 {
+#if POSTGIS_GEOS_VERSION >= 31500
+	/* GEOS 3.15 splits polygons directly, filtering out the pieces that
+	 * fall inside holes for us. On older GEOS we reproduce that logic by
+	 * polygonizing the boundary unioned with the blade and discarding the
+	 * pieces whose point-on-surface is not contained by the input (#else). */
+	LWGEOM* split;
+	LWCOLLECTION* out;
+	GEOSGeometry* g1;
+	GEOSGeometry* g2;
+	GEOSGeometry* gsplit;
+	int hasZ = FLAGS_GET_Z(lwpoly_in->flags);
+
+	initGEOS(lwgeom_geos_error, lwgeom_geos_error);
+
+	g1 = LWGEOM2GEOS((LWGEOM*)lwpoly_in, 0);
+	if ( NULL == g1 )
+	{
+		lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
+		return NULL;
+	}
+
+	g2 = LWGEOM2GEOS(blade_in, 0);
+	if ( NULL == g2 )
+	{
+		GEOSGeom_destroy(g1);
+		lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
+		return NULL;
+	}
+
+	gsplit = GEOSSplit(g1, g2);
+	GEOSGeom_destroy(g1);
+	GEOSGeom_destroy(g2);
+	if ( NULL == gsplit )
+	{
+		lwerror("GEOSSplit: %s", lwgeom_geos_errmsg);
+		return NULL;
+	}
+
+	split = GEOS2LWGEOM(gsplit, hasZ);
+	GEOSGeom_destroy(gsplit);
+	if ( NULL == split )
+	{
+		lwerror("GEOS2LWGEOM: %s", lwgeom_geos_errmsg);
+		return NULL;
+	}
+
+	/* GEOSSplit always returns a collection, but wrap defensively */
+	out = lwgeom_as_lwcollection(split);
+	if ( ! out )
+	{
+		LWGEOM** components = lwalloc(sizeof(LWGEOM*));
+		components[0] = split;
+		out = lwcollection_construct(COLLECTIONTYPE, lwpoly_in->srid,
+		                             NULL, 1, components);
+	}
+	else
+	{
+		lwgeom_set_srid((LWGEOM*)out, lwpoly_in->srid);
+		out->type = COLLECTIONTYPE;
+	}
+
+	return (LWGEOM*)out;
+#else
 	LWCOLLECTION* out;
 	GEOSGeometry* g1;
 	GEOSGeometry* g2;
@@ -491,6 +565,7 @@ lwpoly_split_by_line(const LWPOLY* lwpoly_in, const LWGEOM* blade_in)
 	GEOSGeom_destroy(polygons);
 
 	return (LWGEOM*)out;
+#endif /* POSTGIS_GEOS_VERSION >= 31500 */
 }
 
 static LWGEOM*

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

Summary of changes:
 liblwgeom/cunit/cu_wrapx.c         |   8 +-
 liblwgeom/lwgeom_geos_split.c      |  77 ++++++++++++++-
 regress/core/fixedoverlay.sql      |  16 +--
 regress/core/fixedoverlay_expected |   2 -
 regress/core/wrapx.sql             | 198 ++++++++++++++++++++-----------------
 5 files changed, 198 insertions(+), 103 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list