]> granicus.if.org Git - postgis/commitdiff
Added geom_accum(), collect_garray() and collect()
authorSandro Santilli <strk@keybit.net>
Fri, 27 Aug 2004 16:01:48 +0000 (16:01 +0000)
committerSandro Santilli <strk@keybit.net>
Fri, 27 Aug 2004 16:01:48 +0000 (16:01 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@762 b70326c6-7e19-0410-871a-916f4a2858ee

lwgeom/MISSING_OBJECTS
lwgeom/lwgeom_functions_basic.c
lwgeom/lwpostgis.sql.in

index ed1678852a991408d1c2b49de12b0087a3f716f5..91a6dd227ad345a955347aae53ea87401725ae6b 100644 (file)
@@ -8,10 +8,6 @@ FUNC: KEEPING FUNCTION: [asbinary(geometry, text)]
 FUNC: KEEPING FUNCTION: [boundary(geometry)]
 FUNC: KEEPING FUNCTION: [box(geometry)]
 
-FUNC: KEEPING FUNCTION: [geom_accum(geometry[], geometry)]
-FUNC: KEEPING FUNCTION: [collect_garray(geometry[])]
-AGGREGATE: KEEPING AGGREGATE [collect(geometry)]
-
 FUNC: KEEPING FUNCTION: [envelope(geometry)]
 FUNC: KEEPING FUNCTION: [equals(geometry, geometry)]
 FUNC: KEEPING FUNCTION: [expand(geometry, double precision)]
index 632f2f6226dbe141cf3e9561bf741d348491ba28..a0e04a88d158a1c0499a6c314d5ae1aa8226efba 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "fmgr.h"
 #include "utils/elog.h"
+#include "utils/array.h"
 
 #include "lwgeom.h"
 
@@ -35,6 +36,8 @@ Datum LWGEOM_maxdistance2d_linestring(PG_FUNCTION_ARGS);
 Datum LWGEOM_translate(PG_FUNCTION_ARGS);
 Datum LWGEOM_inside_circle_point(PG_FUNCTION_ARGS);
 Datum LWGEOM_collect(PG_FUNCTION_ARGS);
+Datum LWGEOM_accum(PG_FUNCTION_ARGS);
+Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS);
 
 // internal
 char * lwgeom_summary_recursive(char *serialized, int offset);
@@ -1931,7 +1934,6 @@ dump_lwexploded(LWGEOM_EXPLODED *exploded)
                }
        }
 
-       return 0;
 }
 
 // collect( geom, geom ) returns a geometry which contains
@@ -2046,3 +2048,231 @@ Datum LWGEOM_collect(PG_FUNCTION_ARGS)
        //elog(ERROR, "Not implemented yet");
        PG_RETURN_POINTER(result);
 }
+
+/*
+ * This is a geometry array constructor
+ * for use as aggregates sfunc.
+ * Will have as input an array of Geometry pointers and a Geometry.
+ * Will DETOAST given geometry and put a pointer to it
+ * in the given array. DETOASTED value is first copied
+ * to a safe memory context to avoid premature deletion.
+ */
+PG_FUNCTION_INFO_V1(LWGEOM_accum);
+Datum LWGEOM_accum(PG_FUNCTION_ARGS)
+{
+       ArrayType *array;
+       int nelems, nbytes;
+       Datum datum;
+       LWGEOM *geom;
+       ArrayType *result;
+       Pointer **pointers;
+       MemoryContext oldcontext;
+
+//elog(NOTICE, "LWGEOM_accum called");
+
+       datum = PG_GETARG_DATUM(0);
+       if ( (Pointer *)datum == NULL ) {
+               array = NULL;
+               nelems = 0;
+               //elog(NOTICE, "geom_accum: NULL array, nelems=%d", nelems);
+       } else {
+               array = (ArrayType *) PG_DETOAST_DATUM_COPY(datum);
+               nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
+       }
+
+       datum = PG_GETARG_DATUM(1);
+       // Do nothing, return state array
+       if ( (Pointer *)datum == NULL )
+       {
+               //elog(NOTICE, "geom_accum: NULL geom, nelems=%d", nelems);
+               PG_RETURN_ARRAYTYPE_P(array);
+       }
+
+       /*
+        * Switch to * flinfo->fcinfo->fn_mcxt
+        * memory context to be sure both detoasted
+        * geometry AND array of pointers to it
+        * last till the call to unite_finalfunc.
+        */
+       oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+
+       /* Make a DETOASTED copy of input geometry */
+       geom = (LWGEOM *)PG_DETOAST_DATUM_COPY(datum);
+
+       //elog(NOTICE, "geom_accum: adding %p (nelems=%d)", geom, nelems);
+
+       /*
+        * Might use a more optimized version instead of repalloc'ing
+        * at every iteration. This is not the bottleneck anyway.
+        *              --strk(TODO);
+        */
+       ++nelems;
+       nbytes = ARR_OVERHEAD(1) + sizeof(Pointer *) * nelems;
+       if ( ! array ) {
+               result = (ArrayType *) palloc(nbytes);
+               result->size = nbytes;
+               result->ndim = 1;
+               *((int *) ARR_DIMS(result)) = nelems;
+       } else {
+               result = (ArrayType *) repalloc(array, nbytes);
+               result->size = nbytes;
+               result->ndim = 1;
+               *((int *) ARR_DIMS(result)) = nelems;
+       }
+
+       pointers = (Pointer **)ARR_DATA_PTR(result);
+       pointers[nelems-1] = (Pointer *)geom;
+
+       /* Go back to previous memory context */
+       MemoryContextSwitchTo(oldcontext);
+
+
+       PG_RETURN_ARRAYTYPE_P(result);
+
+}
+
+/*
+ * collect_garray ( GEOMETRY[] ) returns a geometry which contains
+ * all the sub_objects from all of the geometries in given array.
+ *
+ * returned geometry is the simplest possible, based on the types
+ * of the collected objects
+ * ie. if all are of either X or multiX, then a multiX is returned
+ * bboxonly types are treated as null geometries (no sub_objects)
+ */
+PG_FUNCTION_INFO_V1(LWGEOM_collect_garray);
+Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS)
+{
+       Datum datum;
+       ArrayType *array;
+       int nelems, srid=-1;
+       LWGEOM **geoms;
+       LWGEOM *result=NULL, *geom;
+       LWGEOM_EXPLODED *exploded=NULL;
+       int i;
+       int wantbbox = 0; // don't compute a bounding box...
+       int size;
+       char *serialized_result = NULL;
+
+//elog(NOTICE, "LWGEOM_collect_garray called");
+
+       /* Get input datum */
+       datum = PG_GETARG_DATUM(0);
+
+       /* Return null on null input */
+       if ( (Pointer *)datum == NULL )
+       {
+               elog(NOTICE, "NULL input");
+               PG_RETURN_NULL();
+       }
+
+       /* Get actual ArrayType */
+       array = (ArrayType *) PG_DETOAST_DATUM(datum);
+
+       /* Get number of geometries in array */
+       nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
+
+       /* Return null on 0-elements input array */
+       if ( nelems == 0 )
+       {
+               elog(NOTICE, "0 elements input array");
+               PG_RETURN_NULL();
+       }
+
+       /* Get pointer to GEOMETRY pointers array */
+       geoms = (LWGEOM **)ARR_DATA_PTR(array);
+
+       /* Return the only present element of a 1-element array */
+       if ( nelems == 1 ) PG_RETURN_POINTER(geoms[0]);
+
+       /* Iterate over all geometries in array */
+       for (i=0; i<nelems; i++)
+       {
+               LWGEOM_EXPLODED *subexp, *tmpexp;
+
+               geom = geoms[i];
+
+               /* Skip NULL array elements (are them possible?) */
+               if ( geom == NULL ) continue;
+
+               /* Use first NOT-NULL GEOMETRY as the base */
+               if ( ! exploded )
+               {
+                       /* Remember first geometry's SRID for later checks */
+                       srid = lwgeom_getSRID(geom);
+
+                       exploded = lwgeom_explode(SERIALIZED_FORM(geom));
+                       if ( ! exploded ) {
+       elog(ERROR, "collect_garray: out of virtual memory");
+       PG_RETURN_NULL();
+                       }
+
+                       continue;
+               }
+
+               /* Skip geometry if it contains no sub-objects */
+               if ( ! lwgeom_getnumgeometries(SERIALIZED_FORM(geom)) )
+               {
+                       pfree(geom); // se note above
+                       continue;
+               }
+
+               /*
+                * If we are here this means we are in front of a
+                * good (non empty) geometry beside the first
+                */
+
+               /* Fist let's check for SRID compatibility */
+               if ( lwgeom_getSRID(geom) != srid )
+               {
+       elog(ERROR, "Operation on GEOMETRIES with different SRIDs (need %d, have %d)", srid, lwgeom_getSRID(geom));
+       PG_RETURN_NULL();
+               }
+
+               subexp = lwgeom_explode(SERIALIZED_FORM(geom));
+
+               tmpexp = lwexploded_sum(exploded, subexp);
+               if ( ! tmpexp )
+               {
+       elog(ERROR, "Out of virtual memory");
+       PG_RETURN_NULL();
+               }
+               pfree_exploded(subexp);
+               pfree_exploded(exploded);
+               exploded = tmpexp;
+
+       }
+
+       /* Check we got something in our result */
+       if ( exploded == NULL )
+       {
+               elog(NOTICE, "NULL exploded");
+               PG_RETURN_NULL();
+       }
+
+       /*
+        * We should now have a big fat exploded geometry
+        * with of all sub-objects from all geometries in array
+        */
+
+       // Now serialized collected LWGEOM_EXPLODED
+       serialized_result = lwexploded_serialize(exploded, wantbbox);
+       if ( ! serialized_result )
+       {
+               elog(ERROR, "Could not serialize exploded geoms");
+               pfree_exploded(exploded);
+               PG_RETURN_NULL();
+       }
+
+       // now we could release all memory associated with geometries
+       // in the array.... TODO.
+
+       // Create LWGEOM type (could provide a _buf version of
+       // the serializer instead)
+       size = lwgeom_seralizedformlength_simple(serialized_result);
+       result = LWGEOM_construct(serialized_result,
+               lwgeom_getsrid(serialized_result), wantbbox);
+       pfree(serialized_result);
+
+       PG_RETURN_POINTER( result );
+}
index 141e8a4a34d047d13e2c1f7a533d225c460a4078..8e49f7b57dbca5091e8f64141d3f8252e247da58 100644 (file)
@@ -1197,6 +1197,23 @@ CREATE AGGREGATE memcollect(
        stype = geometry
        );
 
+CREATEFUNCTION geom_accum (geometry[],geometry)
+       RETURNS geometry[]
+       AS '@MODULE_FILENAME@', 'LWGEOM_accum'
+       LANGUAGE 'C';
+
+CREATEFUNCTION collect_garray (geometry[])
+       RETURNS geometry
+       AS '@MODULE_FILENAME@', 'LWGEOM_collect_garray'
+       LANGUAGE 'C';
+
+CREATE AGGREGATE collect (
+       sfunc = geom_accum,
+       basetype = geometry,
+       stype = geometry[],
+       finalfunc = collect_garray
+       );
+
 ------------------------------------------------------------------------
 
 --