[SCM] PostGIS branch stable-3.6 updated. 3.6.3-4-g79f2b6bc4

git at osgeo.org git at osgeo.org
Mon May 11 08:58:25 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  79f2b6bc45c66b01d3455c14591fbff4e2b90cf0 (commit)
       via  2d60db735cd96d889d7e43474deb436563ec3b77 (commit)
      from  8591f818942c094254d0f9bbaface72de2795180 (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 79f2b6bc45c66b01d3455c14591fbff4e2b90cf0
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Mon May 11 08:58:16 2026 -0700

    News item for #6076

diff --git a/NEWS b/NEWS
index 769000fe3..249ca7adb 100644
--- a/NEWS
+++ b/NEWS
@@ -3,7 +3,8 @@ PostGIS 3.6.4
 
 * Fixes *
 
-- 
+- #6076, ST_Length(geography) for GeometryCollection not consistent
+         with geometry implementation
 
 
 PostGIS 3.6.3

commit 2d60db735cd96d889d7e43474deb436563ec3b77
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Mon May 11 08:56:23 2026 -0700

    Fix ST_Length and ST_Perimeter for geography collections, closes #6076
    
    ST_Length(geography) on collections now only sums linear components,
    and ST_Perimeter(geography) now only sums areal components. Added
    lwgeom_perimeter_spheroid to support the separation of logic.
    Preserve legacy behaviour in ST_LengthSpheroid.

diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in
index 391c383aa..16bace594 100644
--- a/liblwgeom/liblwgeom.h.in
+++ b/liblwgeom/liblwgeom.h.in
@@ -1811,6 +1811,10 @@ extern double lwgeom_area_spheroid(const LWGEOM *lwgeom, const SPHEROID *spheroi
 */
 extern double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s);
 
+/**
+* Calculate the geodetic perimeter of a lwgeom on the unit sphere.
+*/
+extern double lwgeom_perimeter_spheroid(const LWGEOM *geom, const SPHEROID *s);
 /**
 * Calculate covers predicate for two lwgeoms on the sphere. Currently
 * only handles point-in-polygon.
diff --git a/liblwgeom/lwgeodetic.c b/liblwgeom/lwgeodetic.c
index 128ce4591..252f54c29 100644
--- a/liblwgeom/lwgeodetic.c
+++ b/liblwgeom/lwgeodetic.c
@@ -3150,31 +3150,22 @@ double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s)
 
 	assert(geom);
 
-	/* No area in nothing */
+	/* No length in nothing */
 	if ( lwgeom_is_empty(geom) )
 		return 0.0;
 
 	type = geom->type;
 
-	if ( type == POINTTYPE || type == MULTIPOINTTYPE )
+	if ( type == POINTTYPE || type == MULTIPOINTTYPE ||
+	     type == POLYGONTYPE || type == MULTIPOLYGONTYPE ||
+	     type == TRIANGLETYPE || type == TINTYPE ||
+	     type == POLYHEDRALSURFACETYPE || type == CURVEPOLYTYPE ||
+	     type == MULTISURFACETYPE )
 		return 0.0;
 
 	if ( type == LINETYPE )
 		return ptarray_length_spheroid(((LWLINE*)geom)->points, s);
 
-	if ( type == POLYGONTYPE )
-	{
-		LWPOLY *poly = (LWPOLY*)geom;
-		for ( i = 0; i < poly->nrings; i++ )
-		{
-			length += ptarray_length_spheroid(poly->rings[i], s);
-		}
-		return length;
-	}
-
-	if ( type == TRIANGLETYPE )
-		return ptarray_length_spheroid(((LWTRIANGLE*)geom)->points, s);
-
 	if ( lwtype_is_collection( type ) )
 	{
 		LWCOLLECTION *col = (LWCOLLECTION*)geom;
@@ -3186,7 +3177,48 @@ double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s)
 		return length;
 	}
 
-	lwerror("unsupported type passed to lwgeom_length_sphere");
+	lwerror("unsupported type passed to lwgeom_length_spheroid");
+	return 0.0;
+}
+
+double lwgeom_perimeter_spheroid(const LWGEOM *geom, const SPHEROID *s)
+{
+	int type;
+	uint32_t i = 0;
+	double perimeter = 0.0;
+
+	assert(geom);
+
+	/* No perimeter in nothing */
+	if ( lwgeom_is_empty(geom) )
+		return 0.0;
+
+	type = geom->type;
+
+	if ( type == POLYGONTYPE )
+	{
+		LWPOLY *poly = (LWPOLY*)geom;
+		for ( i = 0; i < poly->nrings; i++ )
+		{
+			perimeter += ptarray_length_spheroid(poly->rings[i], s);
+		}
+		return perimeter;
+	}
+
+	if ( type == TRIANGLETYPE )
+		return ptarray_length_spheroid(((LWTRIANGLE*)geom)->points, s);
+
+	if ( lwtype_is_collection( type ) )
+	{
+		LWCOLLECTION *col = (LWCOLLECTION*)geom;
+
+		for ( i = 0; i < col->ngeoms; i++ )
+		{
+			perimeter += lwgeom_perimeter_spheroid(col->geoms[i], s);
+		}
+		return perimeter;
+	}
+
 	return 0.0;
 }
 
diff --git a/postgis/geography_measurement.c b/postgis/geography_measurement.c
index ece265fc0..15090d670 100644
--- a/postgis/geography_measurement.c
+++ b/postgis/geography_measurement.c
@@ -599,12 +599,12 @@ Datum geography_perimeter(PG_FUNCTION_ARGS)
 		s.a = s.b = s.radius;
 
 	/* Calculate the length */
-	length = lwgeom_length_spheroid(lwgeom, &s);
+	length = lwgeom_perimeter_spheroid(lwgeom, &s);
 
 	/* Something went wrong... */
 	if ( length < 0.0 )
 	{
-		elog(ERROR, "lwgeom_length_spheroid returned length < 0.0");
+		elog(ERROR, "lwgeom_perimeter_spheroid returned length < 0.0");
 		PG_RETURN_NULL();
 	}
 
diff --git a/postgis/lwgeom_spheroid.c b/postgis/lwgeom_spheroid.c
index 8c11edff5..a8af0f2d9 100644
--- a/postgis/lwgeom_spheroid.c
+++ b/postgis/lwgeom_spheroid.c
@@ -370,7 +370,7 @@ Datum LWGEOM_length_ellipsoid_linestring(PG_FUNCTION_ARGS)
 		PG_RETURN_FLOAT8(0.0);
 	}
 
-	length = lwgeom_length_spheroid(lwgeom, sphere);
+	length = lwgeom_length_spheroid(lwgeom, sphere) + lwgeom_perimeter_spheroid(lwgeom, sphere);
 	lwgeom_free(lwgeom);
 	PG_FREE_IF_COPY(geom, 0);
 
diff --git a/regress/core/geography.sql b/regress/core/geography.sql
index 9b80cd757..5f98c7066 100644
--- a/regress/core/geography.sql
+++ b/regress/core/geography.sql
@@ -149,6 +149,10 @@ SELECT 'lrs_llp_4', round(ST_LineLocatePoint(geography 'linestring(0 1, 50 1)',
 
 SELECT 'lrs_substr_1', ST_AsText(ST_LineSubstring(geography 'linestring(0 20, 100 20)', 0.1, 0.2),2);
 
+-- Ticket #6076
+SELECT 'ticket_6076_length', round(ST_Length(ST_GeogFromText('GEOMETRYCOLLECTION (POINT (5 5), LINESTRING (0 0, 0 1), POLYGON ((0 0, 0 1, 1 0, 0 0)))'))::numeric, 3);
+SELECT 'ticket_6076_perimeter', round(ST_Perimeter(ST_GeogFromText('GEOMETRYCOLLECTION (POINT (5 5), LINESTRING (0 0, 0 1), POLYGON ((0 0, 0 1, 1 0, 0 0)))'))::numeric, 3);
+
 --SELECT 'lrs_cp_1', ST_AsText(ST_ClosestPoint(geography 'Linestring(0 20, 50 20)', 'Point(25 20)'), 3);
 --SELECT 'lrs_cp_2', ST_AsText(ST_ClosestPoint(geography 'Point(25 20)', geography 'Linestring(0 20, 50 20)'), 3);
 
diff --git a/regress/core/geography_expected b/regress/core/geography_expected
index 0ca174883..8584b7df0 100644
--- a/regress/core/geography_expected
+++ b/regress/core/geography_expected
@@ -66,4 +66,6 @@ lrs_llp_2|0.00
 lrs_llp_3|1.00
 ERROR:  geography_line_locate_point: 2nd arg is not a point
 lrs_substr_1|LINESTRING(9.28 23.25,18.97 25.93)
+ticket_6076_length|110574.389
+ticket_6076_perimeter|378793.448
 lrs_sl_1|LINESTRING(25 42.79,25 40)

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

Summary of changes:
 NEWS                            |  3 ++-
 liblwgeom/liblwgeom.h.in        |  4 ++++
 liblwgeom/lwgeodetic.c          | 46 ++++++++++++++++++++++++++++++++++-------
 postgis/geography_measurement.c |  4 ++--
 postgis/lwgeom_spheroid.c       |  2 +-
 regress/core/geography.sql      |  4 ++++
 regress/core/geography_expected |  2 ++
 7 files changed, 54 insertions(+), 11 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list