Commit b3d2b3c9 authored by Paul Ramsey's avatar Paul Ramsey

Implement ST_CollectionExtract() to pull specific homogeneous collections out...

Implement ST_CollectionExtract() to pull specific homogeneous collections out of heterogeneous collections. Regressions and documentation included. (#218)


git-svn-id: http://svn.osgeo.org/postgis/trunk@4847 b70326c6-7e19-0410-871a-916f4a2858ee
parent e05eff13
......@@ -605,6 +605,49 @@ MULTILINESTRING((-45.2 -33.2,-46 -32),(-29 -27,-30 -29.7,-36 -31,-45 -33))
</refsection>
</refentry>
<refentry id="ST_CollectionExtract">
<refnamediv>
<refname>ST_CollectionExtract</refname>
<refpurpose>Given a GEOMETRYCOLLECTION, returns the a MULTI* geometry consisting only the specified type. Sub-geometries that are not
the specified type are ignored.</refpurpose>
</refnamediv>
<refsynopsisdiv>
<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 GEOMETRYCOLLECTION, returns the a MULTI* geometry consisting only 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 collection will be returned. Only
points, lines and polygons are supported.</para>
</refsection>
<refsection>
<title>Examples</title>
<programlisting>SELECT ST_AsText(ST_CollectionExtract(ST_GeomFromText('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(POINT(0 0)))'),1));
st_astext
---------------
MULTIPOINT(0 0)
(1 row)
</programlisting>
</refsection>
<refsection>
<title>See Also</title>
<para><xref linkend="ST_Multi" /></para>
</refsection>
</refentry>
<refentry id="ST_Multi">
<refnamediv>
<refname>ST_Multi</refname>
......
......@@ -37,7 +37,8 @@ CU_pSuite register_libgeom_suite(void)
(NULL == CU_add_test(pSuite, "test_lwgeom_check_geodetic()", test_lwgeom_check_geodetic)) ||
(NULL == CU_add_test(pSuite, "test_lwgeom_count_vertices()", test_lwgeom_count_vertices)) ||
(NULL == CU_add_test(pSuite, "test_on_gser_lwgeom_count_vertices()", test_on_gser_lwgeom_count_vertices)) ||
(NULL == CU_add_test(pSuite, "test_gbox_calculation()", test_gbox_calculation))
(NULL == CU_add_test(pSuite, "test_gbox_calculation()", test_gbox_calculation)) ||
(NULL == CU_add_test(pSuite, "test_lwcollection_extract()", test_lwcollection_extract))
)
{
......@@ -457,3 +458,18 @@ void test_gbox_calculation(void)
}
lwfree(gbox);
}
void test_lwcollection_extract(void)
{
LWGEOM *geom;
LWCOLLECTION *col;
geom = lwgeom_from_ewkt("GEOMETRYCOLLECTION(POINT(0 0))", PARSER_CHECK_NONE);
col = lwcollection_extract((LWCOLLECTION*)geom, 1);
CU_ASSERT_EQUAL(TYPE_GETTYPE(col->type), MULTIPOINTTYPE);
lwcollection_release(col);
lwgeom_free(geom);
}
......@@ -37,3 +37,4 @@ void test_lwgeom_count_vertices(void);
void test_on_gser_lwgeom_count_vertices(void);
void test_gbox_serialized_size(void);
void test_gbox_calculation(void);
void test_lwcollection_extract(void);
......@@ -916,7 +916,7 @@ LWMSURFACE *lwmsurface_deserialize(uchar *serialized_form);
LWGEOM *lwcollection_getsubgeom(LWCOLLECTION *col, int gnum);
BOX3D *lwcollection_compute_box3d(LWCOLLECTION *col);
LWCOLLECTION* lwcollection_extract(LWCOLLECTION *col, int type);
/******************************************************************
* SERIALIZED FORM functions
......
......@@ -557,3 +557,87 @@ BOX3D *lwcollection_compute_box3d(LWCOLLECTION *col)
}
return boxfinal;
}
/**
* Takes a potentially heterogeneous collection and returns a homogeneous
* collection consisting only of the specified type.
*/
LWCOLLECTION* lwcollection_extract(LWCOLLECTION *col, int type)
{
int i = 0;
LWGEOM **geomlist;
BOX3D *b3d;
LWCOLLECTION *outcol;
int geomlistsize = 16;
int geomlistlen = 0;
uchar outtype;
if( ! col ) return NULL;
switch (type)
{
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.", lwgeom_typename(type));
return NULL;
}
geomlist = lwalloc(sizeof(LWGEOM*) * geomlistsize);
/* Process each sub-geometry */
for( i = 0; i < col->ngeoms; i++ )
{
int subtype = TYPE_GETTYPE(col->geoms[i]->type);
/* Copy our sub-types into the output list */
if( subtype == type )
{
/* We've over-run our buffer, double the memory segment */
if( geomlistlen == geomlistsize )
{
geomlistsize *= 2;
geomlist = lwrealloc(geomlist, sizeof(LWGEOM*) * geomlistsize);
}
geomlist[geomlistlen] = col->geoms[i];
geomlistlen++;
}
if( lwgeom_is_collection( subtype ) )
{
int 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++;
}
lwfree(tmpcol);
}
}
if( geomlistlen > 0 )
{
outcol = lwcollection_construct(outtype, col->SRID, NULL, geomlistlen, geomlist);
b3d = lwcollection_compute_box3d(outcol);
outcol->bbox = box3d_to_box2df(b3d);
}
else
{
outcol = lwcollection_construct_empty(col->SRID, TYPE_HASZ(col->type), TYPE_HASM(col->type));
}
return outcol;
}
......@@ -207,8 +207,7 @@ void lwgeom_install_default_allocators(void)
}
const char *
lwgeom_typename(int type)
const char* lwgeom_typename(int type)
{
/* something went wrong somewhere */
if ( type < 0 || type > 15 )
......
......@@ -78,6 +78,7 @@ Datum LWGEOM_longitude_shift(PG_FUNCTION_ARGS);
Datum optimistic_overlap(PG_FUNCTION_ARGS);
Datum ST_GeoHash(PG_FUNCTION_ARGS);
Datum ST_MakeEnvelope(PG_FUNCTION_ARGS);
Datum ST_CollectionExtract(PG_FUNCTION_ARGS);
void lwgeom_affine_ptarray(POINTARRAY *pa, double afac, double bfac, double cfac,
double dfac, double efac, double ffac, double gfac, double hfac, double ifac, double xoff, double yoff, double zoff);
......@@ -3435,3 +3436,38 @@ Datum ST_GeoHash(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(result);
}
PG_FUNCTION_INFO_V1(ST_CollectionExtract);
Datum ST_CollectionExtract(PG_FUNCTION_ARGS)
{
PG_LWGEOM *input = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
PG_LWGEOM *output;
LWGEOM *lwgeom = pglwgeom_deserialize(input);
LWCOLLECTION *lwcol = NULL;
int type = PG_GETARG_INT32(1);
int lwgeom_type = TYPE_GETTYPE(lwgeom->type);
/* Ensure the right type was input */
if ( ! ( type == POINTTYPE || type == LINETYPE || type == POLYGONTYPE ) )
{
lwgeom_free(lwgeom);
elog(ERROR, "ST_CollectionExtract: only point, linestring and polygon may be extracted");
PG_RETURN_NULL();
}
/* Mirror non-collections right back */
if ( ! lwgeom_is_collection(lwgeom_type) )
{
output = palloc(VARSIZE(input));
memcpy(VARDATA(output), VARDATA(input), VARSIZE(input) - VARHDRSZ);
SET_VARSIZE(output, VARSIZE(input));
lwgeom_free(lwgeom);
PG_RETURN_POINTER(output);
}
lwcol = lwcollection_extract((LWCOLLECTION*)lwgeom, type);
output = pglwgeom_serialize((LWGEOM*)lwcol);
lwgeom_free(lwgeom);
PG_RETURN_POINTER(output);
}
\ No newline at end of file
......@@ -1438,6 +1438,12 @@ CREATE OR REPLACE FUNCTION ST_force_collection(geometry)
AS 'MODULE_PATHNAME', 'LWGEOM_force_collection'
LANGUAGE 'C' IMMUTABLE STRICT;
-- Availability: 1.5.0
CREATE OR REPLACE FUNCTION ST_CollectionExtract(geometry, integer)
RETURNS geometry
AS 'MODULE_PATHNAME', 'ST_CollectionExtract'
LANGUAGE 'C' IMMUTABLE STRICT;
-- Deprecation in 1.2.3
CREATE OR REPLACE FUNCTION multi(geometry)
RETURNS geometry
......
......@@ -277,6 +277,16 @@ select '150_', asewkt(force_collection(setsrid('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0
select '151', ST_MakeEnvelope(0, 0, 1, 1, 4326);
select '152', ST_SRID(ST_MakeEnvelope(0, 0, 1, 1, 4326));
select '153', ST_AsText(ST_CollectionExtract('GEOMETRYCOLLECTION(POINT(0 0))',1));
select '154', ST_AsText(ST_CollectionExtract('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(POINT(0 0)))',1));
select '155', ST_AsText(ST_CollectionExtract('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(POINT(0 0), POINT(1 1)))',1));
select '156', ST_AsText(ST_CollectionExtract('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(LINESTRING(0 0, 1 1), POINT(1 1)))',1));
select '157', ST_AsText(ST_CollectionExtract('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(LINESTRING(0 0, 1 1), POINT(1 1)))',2));
select '158', ST_AsText(ST_CollectionExtract('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(LINESTRING(0 0, 1 1), POINT(1 1)),LINESTRING(2 2, 3 3))',2));
select '159', ST_AsText(ST_CollectionExtract('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(LINESTRING(0 0, 1 1), POINT(1 1)),LINESTRING(2 2, 3 3))',3));
select '160', ST_AsText(ST_CollectionExtract('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(LINESTRING(0 0, 1 1), POINT(1 1)),LINESTRING(2 2, 3 3))',1));
select '161', ST_AsText(ST_CollectionExtract('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(LINESTRING(0 0, 1 1), GEOMETRYCOLLECTION(POINT(1 1))),LINESTRING(2 2, 3 3))',2));
-- Drop test table
DROP table test;
......@@ -197,3 +197,12 @@ HINT: "MULTIPOINT(1 1, 2 2" <-- parse error at position 19 within geometry
150_|SRID=6;GEOMETRYCOLLECTION(POLYGON((0 0,1 0,1 1,0 1,0 0)))
151|0103000020E61000000100000005000000000000000000000000000000000000000000000000000000000000000000F03F000000000000F03F000000000000F03F000000000000F03F000000000000000000000000000000000000000000000000
152|4326
153|MULTIPOINT(0 0)
154|MULTIPOINT(0 0)
155|MULTIPOINT(0 0,1 1)
156|MULTIPOINT(1 1)
157|MULTILINESTRING((0 0,1 1))
158|MULTILINESTRING((0 0,1 1),(2 2,3 3))
159|GEOMETRYCOLLECTION EMPTY
160|MULTIPOINT(1 1)
161|MULTILINESTRING((0 0,1 1),(2 2,3 3))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment