[postgis-tickets] [SCM] PostGIS branch master updated. 3.1.0alpha2-145-g6812b00

git at osgeo.org git at osgeo.org
Thu Nov 5 09:00:20 PST 2020


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, master has been updated
       via  6812b00ed6195e151d04be8c78aa8f1c20c628ea (commit)
      from  04ecdcb2568e27e9a46e1849e002ba24299cb80a (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 6812b00ed6195e151d04be8c78aa8f1c20c628ea
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Thu Nov 5 08:59:39 2020 -0800

    Add ST_CollectionExtract(geometry)
    with a behaviour of extracting
    the primitive geometry (point/line/poly) with the highest coordinate
    dimension. So a collection of lines and polys will result in a
    multipoly output. A collection of lines and points will result in a
    multiline output.
    Closes #4784

diff --git a/NEWS b/NEWS
index ec3545c..4ef2903 100644
--- a/NEWS
+++ b/NEWS
@@ -38,6 +38,8 @@ Only tickets not included in 3.1.0alpha2
   - #4746, Micro optimizations to the serialization process (Raúl Marín)
   - #4719, Fail fast when srids don't match ST_Intersection(geometry,raster)
             Also schema qualify calls in function. (Regina Obe)
+  - #4784, Add ST_CollectionExtract(geometry) with default behaviour of
+           extracting the components of highest coordinate dimension. (Paul Ramsey)
 
 * Bug fixes *
   - #4691, Fix segfault during gist index creation with empty geometries (Raúl Marín)
diff --git a/doc/reference_editor.xml b/doc/reference_editor.xml
index 7e52ea2..ddb0de9 100644
--- a/doc/reference_editor.xml
+++ b/doc/reference_editor.xml
@@ -76,26 +76,35 @@ Given a (multi)geometry, return a (multi)geometry consisting only of elements of
 			  <funcprototype>
 				<funcdef>geometry <function>ST_CollectionExtract</function></funcdef>
 				<paramdef><type>geometry </type> <parameter>collection</parameter></paramdef>
-				<paramdef><type>integer </type> <parameter>type</parameter></paramdef>
 			  </funcprototype>
 			</funcsynopsis>
+            <funcsynopsis>
+              <funcprototype>
+                <funcdef>geometry <function>ST_CollectionExtract</function></funcdef>
+                <paramdef><type>geometry </type> <parameter>collection</parameter></paramdef>
+                <paramdef><type>integer </type> <parameter>type</parameter></paramdef>
+              </funcprototype>
+            </funcsynopsis>
 		</refsynopsisdiv>
 
 		<refsection>
 			<title>Description</title>
 
-			<para>
-Given a (multi)geometry, returns a (multi)geometry consisting only of elements of the specified type.
-Sub-geometries that are not the specified type are ignored. If there are no sub-geometries of the right type, an EMPTY geometry will be returned.
-Only points, lines and polygons are supported. Type numbers are 1 == POINT, 2 == LINESTRING, 3 == POLYGON.
-			</para>
+			<para>Given a geometry collection, return a homogeneous multi-geometry.
+            If the type is specified, return a multi-geometry containing only that type, and an EMPTY multigeometry otherwise. If the type is not specified, return a multi-geometry containing only geometries of the highest coordinate dimension. So polygons are preferred over lines, which are preferred over points. Sub-geometries that are not the desired type are ignored. If there are no sub-geometries of the right type, an EMPTY geometry will be returned. </para>
+            <para>Only points, lines and polygons are supported. The type numbers are:</para>
+            <itemizedlist>
+            <listitem><para>1 == POINT</para></listitem>
+            <listitem><para>2 == LINESTRING</para></listitem>
+            <listitem><para>3 == POLYGON</para></listitem>
+            </itemizedlist>
 
 			<para>Availability: 1.5.0</para>
 
 			<note><para>
-Prior to 1.5.3 this function returned non-collection inputs untouched, no matter type.
-In 1.5.3 non-matching single geometries result in a NULL return.
-In of 2.0.0 every case of missing match results in a typed EMPTY return.
+            Prior to 1.5.3 this function returned non-collection inputs untouched, no matter type.
+            In 1.5.3 non-matching single geometries result in a NULL return.
+            In 2.0.0 every case of missing match results in a typed EMPTY return.
 			</para></note>
 
 			<warning><para>When specifying 3 == POLYGON a multipolygon is returned even when the edges are shared.  This results in an invalid multipolygon for many cases
diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in
index f44588f..57d7f32 100644
--- a/liblwgeom/liblwgeom.h.in
+++ b/liblwgeom/liblwgeom.h.in
@@ -1197,10 +1197,7 @@ extern LWGEOM *lwgeom_homogenize(const LWGEOM *geom);
  ******************************************************************/
 
 LWGEOM *lwcollection_getsubgeom(LWCOLLECTION *col, int gnum);
-
-/* WARNING: the output will contain references to geometries in the input, */
-/* so the result must be carefully released, not freed. */
-LWCOLLECTION* lwcollection_extract(LWCOLLECTION *col, int type);
+LWCOLLECTION* lwcollection_extract(const LWCOLLECTION *col, uint32_t type);
 
 
 /******************************************************************
diff --git a/liblwgeom/lwcollection.c b/liblwgeom/lwcollection.c
index e64d723..6d5c5ce 100644
--- a/liblwgeom/lwcollection.c
+++ b/liblwgeom/lwcollection.c
@@ -376,112 +376,102 @@ void lwcollection_free(LWCOLLECTION *col)
 	lwfree(col);
 }
 
-
 /**
-* Takes a potentially heterogeneous collection and returns a homogeneous
-* collection consisting only of the specified type.
-* WARNING: the output will contain references to geometries in the input,
-* so the result must be carefully released, not freed.
+* Examines contents of collection and finds the largest coordinate
+* dimension of all components. Areal > linear > puntal.
 */
-LWCOLLECTION*
-lwcollection_extract(LWCOLLECTION* col, int type)
+static uint32_t
+lwcollection_largest_dimension(const LWCOLLECTION *col)
 {
-	uint32_t i = 0;
-	LWGEOM** geomlist;
-	LWCOLLECTION* outcol;
-	int geomlistsize = 16;
-	int geomlistlen = 0;
-	uint8_t outtype;
-
-	if (!col) return NULL;
-
-	switch (type)
+	int largest_type = 0;
+	size_t i;
+	for (i = 0; i < col->ngeoms; i++)
 	{
-	case POINTTYPE:
-		outtype = MULTIPOINTTYPE;
-		break;
-	case LINETYPE:
-		outtype = MULTILINETYPE;
-		break;
-	case POLYGONTYPE:
-		outtype = MULTIPOLYGONTYPE;
-		break;
-	default:
-		lwerror(
-		    "Only POLYGON, LINESTRING and POINT are supported by "
-		    "lwcollection_extract. %s requested.",
-		    lwtype_name(type));
-		return NULL;
+		LWGEOM *g = col->geoms[i];
+		int gtype = lwgeom_get_type(g);
+		if (lwgeom_is_collection(g))
+		{
+			gtype = lwcollection_largest_dimension((LWCOLLECTION*)g);
+		}
+
+		if (gtype == POINTTYPE || gtype == LINETYPE || gtype == POLYGONTYPE)
+		{
+			if (gtype > largest_type)
+				largest_type = gtype;
+		}
 	}
+	return largest_type;
+}
+
 
-	geomlist = lwalloc(sizeof(LWGEOM*) * geomlistsize);
+static int
+lwcollection_extract_recursive(const LWCOLLECTION* col, uint32_t type, LWCOLLECTION *col_out)
+{
+	size_t i;
+	size_t geoms_added = 0;
 
-	/* Process each sub-geometry */
 	for (i = 0; i < col->ngeoms; i++)
 	{
-		int subtype = col->geoms[i]->type;
-		/* Don't bother adding empty sub-geometries */
-		if (lwgeom_is_empty(col->geoms[i])) continue;
-		/* Copy our sub-types into the output list */
-		if (subtype == type)
+		LWGEOM *g = col->geoms[i];
+		if (lwgeom_is_collection(g))
 		{
-			/* We've over-run our buffer, double the memory segment
-			 */
-			if (geomlistlen == geomlistsize)
-			{
-				geomlistsize *= 2;
-				geomlist = lwrealloc(
-				    geomlist, sizeof(LWGEOM*) * geomlistsize);
-			}
-			geomlist[geomlistlen] = lwgeom_clone(col->geoms[i]);
-			geomlistlen++;
+			LWCOLLECTION *col_part = lwgeom_as_lwcollection(g);
+			geoms_added += lwcollection_extract_recursive(col_part, type, col_out);
 		}
-		/* Recurse into sub-collections */
-		if (lwtype_is_collection(subtype))
+
+		if (lwgeom_get_type(g) == type && !lwgeom_is_empty(g))
 		{
-			uint32_t j = 0;
-			LWCOLLECTION* tmpcol = lwcollection_extract(
-			    (LWCOLLECTION*)col->geoms[i], type);
-			for (j = 0; j < tmpcol->ngeoms; j++)
-			{
-				/* We've over-run our buffer, double the memory
-				 * segment */
-				if (geomlistlen == geomlistsize)
-				{
-					geomlistsize *= 2;
-					geomlist = lwrealloc(geomlist,
-							     sizeof(LWGEOM*) *
-								 geomlistsize);
-				}
-				geomlist[geomlistlen] = tmpcol->geoms[j];
-				geomlistlen++;
-			}
-			if (tmpcol->ngeoms) lwfree(tmpcol->geoms);
-			if (tmpcol->bbox) lwfree(tmpcol->bbox);
-			lwfree(tmpcol);
+			lwcollection_add_lwgeom(col_out, lwgeom_clone(col->geoms[i]));
+			geoms_added++;
 		}
 	}
+	return geoms_added;
+}
+
+LWCOLLECTION*
+lwcollection_extract(const LWCOLLECTION* col, uint32_t type)
+{
+	LWCOLLECTION* outcol;
+
+	if (!col) return NULL;
 
-	if (geomlistlen > 0)
+	/* Self-discover output type when it is not specified */
+	if (!type)
+		type = lwcollection_largest_dimension(col);
+
+	/*
+	* If self-discovery failed, there were no primitive points
+	* lines or polygons in the collection, so send back an
+	* empty collection.
+	*/
+	if (!type)
 	{
-		GBOX gbox;
-		outcol = lwcollection_construct(
-		    outtype, col->srid, NULL, geomlistlen, geomlist);
-		lwgeom_calculate_gbox((LWGEOM*)outcol, &gbox);
-		outcol->bbox = gbox_copy(&gbox);
+		return lwcollection_construct_empty(COLLECTIONTYPE,
+	           col->srid,
+	           FLAGS_GET_Z(col->flags),
+	           FLAGS_GET_M(col->flags));
 	}
-	else
+
+	if (!(type == POINTTYPE || type == LINETYPE || type == POLYGONTYPE))
 	{
-		lwfree(geomlist);
-		outcol = lwcollection_construct_empty(outtype,
-						      col->srid,
-						      FLAGS_GET_Z(col->flags),
-						      FLAGS_GET_M(col->flags));
+		lwerror(
+		    "Only POLYGON, LINESTRING and POINT are supported by "
+		    "lwcollection_extract. %s requested.",
+		    lwtype_name(type));
+		return NULL;
 	}
 
+	outcol = lwcollection_construct_empty(lwtype_multitype(type),
+	           col->srid,
+	           FLAGS_GET_Z(col->flags),
+	           FLAGS_GET_M(col->flags));
+
+	lwcollection_extract_recursive(col, type, outcol);
+	lwgeom_add_bbox(lwcollection_as_lwgeom(outcol));
 	return outcol;
 }
 
+
 LWCOLLECTION*
 lwcollection_force_dims(const LWCOLLECTION *col, int hasz, int hasm, double zval, double mval)
 {
diff --git a/liblwgeom/lwgeom.c b/liblwgeom/lwgeom.c
index 1ca1bd5..34c1fea 100644
--- a/liblwgeom/lwgeom.c
+++ b/liblwgeom/lwgeom.c
@@ -1087,7 +1087,6 @@ lwgeom_is_collection(const LWGEOM *geom)
 int
 lwtype_is_collection(uint8_t type)
 {
-
 	switch (type)
 	{
 	case MULTIPOINTTYPE:
diff --git a/liblwgeom/lwgeom_topo.c b/liblwgeom/lwgeom_topo.c
index 964b7ae..7ec18bb 100644
--- a/liblwgeom/lwgeom_topo.c
+++ b/liblwgeom/lwgeom_topo.c
@@ -5418,22 +5418,22 @@ _lwt_AddLineEdge( LWT_TOPOLOGY* topo, LWLINE* edge, double tol,
     if ( col )
     {{
 
-      col = lwcollection_extract(col, LINETYPE);
+      LWCOLLECTION *colex = lwcollection_extract(col, LINETYPE);
 
       /* Check if the so-snapped edge collapsed (see #1650) */
-      if ( col->ngeoms == 0 )
+      if ( colex->ngeoms == 0 )
       {
-        lwcollection_free(col);
+        lwcollection_free(colex);
         lwgeom_free(tmp);
         LWDEBUG(1, "Made-valid snapped edge collapsed");
         return 0;
       }
 
-      tmp2 = lwgeom_clone_deep( col->geoms[0] );
+      tmp2 = lwgeom_clone_deep(colex->geoms[0]);
       lwgeom_free(tmp);
       tmp = tmp2;
       edge = lwgeom_as_lwline(tmp);
-      lwcollection_free(col);
+      lwcollection_free(colex);
       if ( ! edge )
       {
         /* should never happen */
diff --git a/postgis/lwgeom_functions_basic.c b/postgis/lwgeom_functions_basic.c
index 369ea1e..c5b9e30 100644
--- a/postgis/lwgeom_functions_basic.c
+++ b/postgis/lwgeom_functions_basic.c
@@ -2713,47 +2713,47 @@ Datum ST_GeoHash(PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1(ST_CollectionExtract);
 Datum ST_CollectionExtract(PG_FUNCTION_ARGS)
 {
-	GSERIALIZED *input = PG_GETARG_GSERIALIZED_P(0);
-	GSERIALIZED *output;
-	LWGEOM *lwgeom = lwgeom_from_gserialized(input);
-	LWGEOM *lwcol = NULL;
-	int type = PG_GETARG_INT32(1);
-	int lwgeom_type = lwgeom->type;
+	GSERIALIZED *gser_in, *gser_out;
+	LWGEOM *lwg_in = NULL;
+	LWGEOM *lwg_out = NULL;
+	int extype = 0;
+
+	if (PG_NARGS() > 1)
+		extype = PG_GETARG_INT32(1);
 
 	/* Ensure the right type was input */
-	if (!(type == POINTTYPE || type == LINETYPE || type == POLYGONTYPE))
+	if (!(extype == 0 || extype == POINTTYPE || extype == LINETYPE || extype == POLYGONTYPE))
 	{
-		lwgeom_free(lwgeom);
 		elog(ERROR, "ST_CollectionExtract: only point, linestring and polygon may be extracted");
 		PG_RETURN_NULL();
 	}
 
+	gser_in = PG_GETARG_GSERIALIZED_P(0);
+	lwg_in = lwgeom_from_gserialized(gser_in);
+
 	/* Mirror non-collections right back */
-	if (!lwgeom_is_collection(lwgeom))
+	if (!lwgeom_is_collection(lwg_in))
 	{
 		/* Non-collections of the matching type go back */
-		if (lwgeom_type == type)
+		if (lwg_in->type == extype || !extype)
 		{
-			lwgeom_free(lwgeom);
-			PG_RETURN_POINTER(input);
+			lwgeom_free(lwg_in);
+			PG_RETURN_POINTER(gser_in);
 		}
 		/* Others go back as EMPTY */
 		else
 		{
-			lwcol = lwgeom_construct_empty(
-			    type, lwgeom->srid, lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom));
+			lwg_out = lwgeom_construct_empty(extype, lwg_in->srid, lwgeom_has_z(lwg_in), lwgeom_has_m(lwg_in));
+			PG_RETURN_POINTER(geometry_serialize(lwg_out));
 		}
 	}
-	else
-	{
-		lwcol = lwcollection_as_lwgeom(lwcollection_extract((LWCOLLECTION *)lwgeom, type));
-	}
 
-	output = geometry_serialize((LWGEOM *)lwcol);
-	lwgeom_free(lwgeom);
-	lwgeom_free(lwcol);
+	lwg_out = (LWGEOM*)lwcollection_extract((LWCOLLECTION*)lwg_in, extype);
 
-	PG_RETURN_POINTER(output);
+	gser_out = geometry_serialize(lwg_out);
+	lwgeom_free(lwg_in);
+	lwgeom_free(lwg_out);
+	PG_RETURN_POINTER(gser_out);
 }
 
 PG_FUNCTION_INFO_V1(ST_CollectionHomogenize);
diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in
index 226fdff..45b053a 100644
--- a/postgis/postgis.sql.in
+++ b/postgis/postgis.sql.in
@@ -1402,6 +1402,13 @@ CREATE OR REPLACE FUNCTION ST_CollectionExtract(geometry, integer)
 	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
 	_COST_LOW;
 
+-- Availability: 3.1.0
+CREATE OR REPLACE FUNCTION ST_CollectionExtract(geometry)
+	RETURNS geometry
+	AS 'MODULE_PATHNAME', 'ST_CollectionExtract'
+	LANGUAGE 'c' IMMUTABLE STRICT PARALLEL SAFE
+	_COST_LOW;
+
 -- Availability: 2.0.0
 CREATE OR REPLACE FUNCTION ST_CollectionHomogenize(geometry)
 	RETURNS geometry

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

Summary of changes:
 NEWS                             |   2 +
 doc/reference_editor.xml         |  27 ++++---
 liblwgeom/liblwgeom.h.in         |   5 +-
 liblwgeom/lwcollection.c         | 156 ++++++++++++++++++---------------------
 liblwgeom/lwgeom.c               |   1 -
 liblwgeom/lwgeom_topo.c          |  10 +--
 postgis/lwgeom_functions_basic.c |  44 +++++------
 postgis/postgis.sql.in           |   7 ++
 8 files changed, 128 insertions(+), 124 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list