]> granicus.if.org Git - postgis/commitdiff
Implement ST_CollectionExtract() to pull specific homogeneous collections out of...
authorPaul Ramsey <pramsey@cleverelephant.ca>
Tue, 17 Nov 2009 20:03:50 +0000 (20:03 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Tue, 17 Nov 2009 20:03:50 +0000 (20:03 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@4847 b70326c6-7e19-0410-871a-916f4a2858ee

doc/reference_editor.xml
liblwgeom/cunit/cu_libgeom.c
liblwgeom/cunit/cu_libgeom.h
liblwgeom/liblwgeom.h
liblwgeom/lwcollection.c
liblwgeom/lwutil.c
postgis/lwgeom_functions_basic.c
postgis/postgis.sql.in.c
regress/regress.sql
regress/regress_expected

index ae994187593c8b66ac584c585fdb0bef7fcbb012..59e38e283ba802e981841d0c05f232ca098d3ef6 100644 (file)
@@ -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>
index b695afd55c1484ec409e132d9faa00d2de1a4f55..4133d4be6781862d61a7facbe552c0c09cf622ae 100644 (file)
@@ -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);
+       
+}
index 9ae703c94ad6d5036ae8cda5586d162aca1ca2f0..a37b24de724d8d5c17e7f2e3fe1fbe4399d0827a 100644 (file)
@@ -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);
index bdec43cdd41109350c3d75367570477f19f94da6..d27b23573cbef7787d9ebfb4fe29ab2afd8342e5 100644 (file)
@@ -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
index 78d185dd9a34aa21f21901d1398441ace44a2f22..88c738cdc0264934cb0427677ec5b8dd36e4bbcb 100644 (file)
@@ -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;
+}
+
index ceb4e33f1214dd31ac387ce9fa012c77337a6215..004fbd6fcb678dbb7b7d58e3376b51a550cc603a 100644 (file)
@@ -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 )
index 9adbdd9292e7f1680d6d7c1866b21d404e6e1b3d..63d7bc82230c6d261d8d635706438ce0085ac953 100644 (file)
@@ -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
index bec08194b278b739918e89a1c7961a4d0fbc96e3..05dc8f37296ed3f33ead9ef197d849fca4ab26ae 100644 (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
index 6640b949e89700467ff9d78b0dda0cf8122e33eb..f653da3cdbfbfaed3552dd6991c7f69c7b737943 100644 (file)
@@ -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;
index d88936d62e7c9ba29d259c01acda7ffe8ae7a112..9b49694cd684ecf01823cac6ff304e6a95288088 100644 (file)
@@ -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))