Added geom_accum(), collect_garray() and collect()

git-svn-id: http://svn.osgeo.org/postgis/trunk@762 b70326c6-7e19-0410-871a-916f4a2858ee
This commit is contained in:
Sandro Santilli 2004-08-27 16:01:48 +00:00
parent daf5128a44
commit 268e8bf615
3 changed files with 248 additions and 5 deletions

View 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)]

View 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 );
}

View 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
);
------------------------------------------------------------------------
--