From: Paul Ramsey Date: Tue, 17 Nov 2009 20:03:50 +0000 (+0000) Subject: Implement ST_CollectionExtract() to pull specific homogeneous collections out of... X-Git-Tag: 1.5.0b1~215 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b3d2b3c9f992e50c719aa815a229802aa8a1d10a;p=postgis 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 --- diff --git a/doc/reference_editor.xml b/doc/reference_editor.xml index ae9941875..59e38e283 100644 --- a/doc/reference_editor.xml +++ b/doc/reference_editor.xml @@ -605,6 +605,49 @@ MULTILINESTRING((-45.2 -33.2,-46 -32),(-29 -27,-30 -29.7,-36 -31,-45 -33)) + + + ST_CollectionExtract + + Given a GEOMETRYCOLLECTION, returns the a MULTI* geometry consisting only the specified type. Sub-geometries that are not + the specified type are ignored. + + + + + + geometry ST_CollectionExtract + geometry collection + integer type + + + + + + Description + + 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. + + + + + Examples + + SELECT ST_AsText(ST_CollectionExtract(ST_GeomFromText('GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(POINT(0 0)))'),1)); + st_astext + --------------- + MULTIPOINT(0 0) + (1 row) + + + + See Also + + + + ST_Multi diff --git a/liblwgeom/cunit/cu_libgeom.c b/liblwgeom/cunit/cu_libgeom.c index b695afd55..4133d4be6 100644 --- a/liblwgeom/cunit/cu_libgeom.c +++ b/liblwgeom/cunit/cu_libgeom.c @@ -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); + +} diff --git a/liblwgeom/cunit/cu_libgeom.h b/liblwgeom/cunit/cu_libgeom.h index 9ae703c94..a37b24de7 100644 --- a/liblwgeom/cunit/cu_libgeom.h +++ b/liblwgeom/cunit/cu_libgeom.h @@ -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); diff --git a/liblwgeom/liblwgeom.h b/liblwgeom/liblwgeom.h index bdec43cdd..d27b23573 100644 --- a/liblwgeom/liblwgeom.h +++ b/liblwgeom/liblwgeom.h @@ -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 diff --git a/liblwgeom/lwcollection.c b/liblwgeom/lwcollection.c index 78d185dd9..88c738cdc 100644 --- a/liblwgeom/lwcollection.c +++ b/liblwgeom/lwcollection.c @@ -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; +} + diff --git a/liblwgeom/lwutil.c b/liblwgeom/lwutil.c index ceb4e33f1..004fbd6fc 100644 --- a/liblwgeom/lwutil.c +++ b/liblwgeom/lwutil.c @@ -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 ) diff --git a/postgis/lwgeom_functions_basic.c b/postgis/lwgeom_functions_basic.c index 9adbdd929..63d7bc822 100644 --- a/postgis/lwgeom_functions_basic.c +++ b/postgis/lwgeom_functions_basic.c @@ -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 diff --git a/postgis/postgis.sql.in.c b/postgis/postgis.sql.in.c index bec08194b..05dc8f372 100644 --- a/postgis/postgis.sql.in.c +++ b/postgis/postgis.sql.in.c @@ -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 diff --git a/regress/regress.sql b/regress/regress.sql index 6640b949e..f653da3cd 100644 --- a/regress/regress.sql +++ b/regress/regress.sql @@ -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; diff --git a/regress/regress_expected b/regress/regress_expected index d88936d62..9b49694cd 100644 --- a/regress/regress_expected +++ b/regress/regress_expected @@ -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))