]> granicus.if.org Git - postgis/commitdiff
Initial wrapper to GEOS C api
authorSandro Santilli <strk@keybit.net>
Fri, 23 Sep 2005 17:06:32 +0000 (17:06 +0000)
committerSandro Santilli <strk@keybit.net>
Fri, 23 Sep 2005 17:06:32 +0000 (17:06 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@1927 b70326c6-7e19-0410-871a-916f4a2858ee

lwgeom/lwgeom_geos_c.c [new file with mode: 0644]

diff --git a/lwgeom/lwgeom_geos_c.c b/lwgeom/lwgeom_geos_c.c
new file mode 100644 (file)
index 0000000..a231b96
--- /dev/null
@@ -0,0 +1,2350 @@
+#include "postgres.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "fmgr.h"
+
+#include "lwgeom_pg.h"
+#include "liblwgeom.h"
+#include "profile.h"
+#include "wktparse.h"
+#include "geos_c.h"
+
+//
+// WARNING: buffer-based GeomUnion has been disabled due to
+//          limitations in the GEOS code (it would only work
+//         against polygons)
+//
+// Fuzzy way of finding out how many points to stuff
+// in each chunk: 680 * Mb of memory
+//
+// The example below is for about 32 MB (fuzzy pragmatic check)
+//
+#define UNITE_USING_BUFFER 0
+#define MAXGEOMSPOINTS 21760
+
+Datum relate_full(PG_FUNCTION_ARGS);
+Datum relate_pattern(PG_FUNCTION_ARGS);
+Datum disjoint(PG_FUNCTION_ARGS);
+Datum touches(PG_FUNCTION_ARGS);
+Datum intersects(PG_FUNCTION_ARGS);
+Datum crosses(PG_FUNCTION_ARGS);
+Datum within(PG_FUNCTION_ARGS);
+Datum contains(PG_FUNCTION_ARGS);
+Datum overlaps(PG_FUNCTION_ARGS);
+Datum isvalid(PG_FUNCTION_ARGS);
+Datum buffer(PG_FUNCTION_ARGS);
+Datum intersection(PG_FUNCTION_ARGS);
+Datum convexhull(PG_FUNCTION_ARGS);
+Datum difference(PG_FUNCTION_ARGS);
+Datum boundary(PG_FUNCTION_ARGS);
+Datum symdifference(PG_FUNCTION_ARGS);
+Datum geomunion(PG_FUNCTION_ARGS);
+Datum unite_garray(PG_FUNCTION_ARGS);
+Datum issimple(PG_FUNCTION_ARGS);
+Datum isring(PG_FUNCTION_ARGS);
+Datum geomequals(PG_FUNCTION_ARGS);
+Datum pointonsurface(PG_FUNCTION_ARGS);
+Datum GEOSnoop(PG_FUNCTION_ARGS);
+Datum postgis_geos_version(PG_FUNCTION_ARGS);
+Datum postgis_jts_version(PG_FUNCTION_ARGS);
+Datum centroid(PG_FUNCTION_ARGS);
+Datum polygonize_garray(PG_FUNCTION_ARGS);
+
+
+
+/*
+ * Define this to have have many notices printed
+ * during postgis->geos and geos->postgis conversions
+ */
+//#define PGIS_DEBUG_CONVERTER 1
+#ifdef PGIS_DEBUG_CONVERTER
+#define PGIS_DEBUG_POSTGIS2GEOS 1
+#define PGIS_DEBUG_GEOS2POSTGIS 1
+#endif // PGIS_DEBUG_CONVERTER
+//#define PGIS_DEBUG 1
+
+LWGEOM *lwgeom_from_geometry(GEOSGeom geom, char want3d);
+PG_LWGEOM *GEOS2POSTGIS(GEOSGeom geom, char want3d);
+GEOSGeom POSTGIS2GEOS(PG_LWGEOM *g);
+GEOSGeom LWGEOM2GEOS(LWGEOM *g);
+void errorIfGeometryCollection(PG_LWGEOM *g1, PG_LWGEOM *g2);
+
+PG_FUNCTION_INFO_V1(postgis_geos_version);
+Datum postgis_geos_version(PG_FUNCTION_ARGS)
+{
+       const char *ver = GEOSversion();
+       text *result;
+       result = (text *) palloc(VARHDRSZ  + strlen(ver));
+       VARATT_SIZEP(result) = VARHDRSZ + strlen(ver) ;
+       memcpy(VARDATA(result), ver, strlen(ver));
+       PG_RETURN_POINTER(result);
+}
+
+
+
+#ifndef UNITE_USING_BUFFER
+/*
+ * This is the final function for GeomUnion
+ * aggregate. Will have as input an array of Geometries.
+ * Will iteratively call GEOSUnion on the GEOS-converted
+ * versions of them and return PGIS-converted version back.
+ * Changing combination order *might* speed up performance.
+ */
+PG_FUNCTION_INFO_V1(unite_garray);
+Datum unite_garray(PG_FUNCTION_ARGS)
+{
+       Datum datum;
+       ArrayType *array;
+       int is3d = 0;
+       int nelems, i;
+       PG_LWGEOM *result, *pgis_geom;
+       GEOSGeom g1, g2, *geos_result=NULL;
+       int SRID=-1;
+       size_t offset;
+#ifdef PGIS_DEBUG
+       static int call=1;
+#endif
+
+#ifdef PGIS_DEBUG
+       call++;
+#endif
+
+       //lwnotice("GEOS incremental union");
+
+       datum = PG_GETARG_DATUM(0);
+
+       /* Null array, null geometry (should be empty?) */
+       if ( (Pointer *)datum == NULL ) PG_RETURN_NULL();
+
+       array = (ArrayType *) PG_DETOAST_DATUM(datum);
+
+       nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
+
+#ifdef PGIS_DEBUG
+       elog(NOTICE, "unite_garray: number of elements: %d", nelems);
+#endif
+
+       if ( nelems == 0 ) PG_RETURN_NULL();
+
+       /* One-element union is the element itself */
+       if ( nelems == 1 ) PG_RETURN_POINTER((PG_LWGEOM *)(ARR_DATA_PTR(array)));
+
+       /* Ok, we really need geos now ;) */
+       initGEOS(lwnotice, lwerror);
+
+       offset = 0;
+       for (i=0; i<nelems; i++)
+       {
+               PG_LWGEOM *geom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset);
+               offset += INTALIGN(geom->size);
+
+               pgis_geom = geom;
+
+#ifdef PGIS_DEBUG
+               elog(NOTICE, "geom %d @ %p", i, geom);
+#endif
+
+               // Check is3d flag
+               if ( TYPE_NDIMS(geom->type) > 2 ) is3d = 1;
+
+               // Check SRID homogeneity and initialize geos result
+               if ( ! i )
+               {
+                       geos_result = POSTGIS2GEOS(geom);
+                       SRID = pglwgeom_getSRID(geom);
+#ifdef PGIS_DEBUG
+               elog(NOTICE, "first geom is a %s", lwgeom_typename(TYPE_GETTYPE(geom->type)));
+#endif
+                       continue;
+               }
+               else
+               {
+                       if ( SRID != pglwgeom_getSRID(geom) )
+                       {
+                               elog(ERROR,
+                                       "Operation on mixed SRID geometries");
+                               PG_RETURN_NULL();
+                       }
+               }
+               
+               g1 = POSTGIS2GEOS(pgis_geom);
+
+#ifdef PGIS_DEBUG
+               elog(NOTICE, "unite_garray(%d): adding geom %d to union (%s)",
+                               call, i, lwgeom_typename(TYPE_GETTYPE(geom->type)));
+#endif
+
+               g2 = GEOSUnion(g1,geos_result);
+               if ( g2 == NULL )
+               {
+                       GEOSGeom_destroy(g1);
+                       GEOSGeom_destroy(geos_result);
+                       elog(ERROR,"GEOS union() threw an error!");
+               }
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(geos_result);
+               geos_result = g2;
+       }
+
+       GEOSSetSRID(geos_result, SRID);
+       result = GEOS2POSTGIS(geos_result, is3d);
+       GEOSGeom_destroy(geos_result);
+       if ( result == NULL )
+       {
+               elog(ERROR, "GEOS2POSTGIS returned an error");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       //compressType(result);
+
+       PG_RETURN_POINTER(result);
+
+}
+
+#else // def UNITE_USING_BUFFER
+
+/*
+ * This is the final function for GeomUnion
+ * aggregate. Will have as input an array of Geometries.
+ * Builds a GEOMETRYCOLLECTION from input and call
+ * GEOSBuffer(collection, 0) on the GEOS-converted
+ * versions of it. Returns PGIS-converted version back.
+ */
+PG_FUNCTION_INFO_V1(unite_garray);
+Datum unite_garray(PG_FUNCTION_ARGS)
+{
+       Datum datum;
+       ArrayType *array;
+       int is3d = 0;
+       int nelems, i, ngeoms, npoints;
+       PG_LWGEOM *result=NULL;
+       GEOSGeom *geoms, collection;
+       GEOSGeom g1, geos_result=NULL;
+       int SRID=-1;
+       size_t offset;
+#ifdef PGIS_DEBUG
+       static int call=1;
+#endif
+
+#ifdef PGIS_DEBUG
+       call++;
+#endif
+
+       //lwnotice("GEOS buffer union");
+
+       datum = PG_GETARG_DATUM(0);
+
+       /* Null array, null geometry (should be empty?) */
+       if ( (Pointer *)datum == NULL ) PG_RETURN_NULL();
+
+       array = (ArrayType *) PG_DETOAST_DATUM(datum);
+
+       nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
+
+#ifdef PGIS_DEBUG
+       elog(NOTICE, "unite_garray: number of elements: %d", nelems);
+#endif
+
+       if ( nelems == 0 ) PG_RETURN_NULL();
+
+       /* One-element union is the element itself */
+       if ( nelems == 1 ) PG_RETURN_POINTER((PG_LWGEOM *)(ARR_DATA_PTR(array)));
+
+       geoms = lwalloc(sizeof(GEOSGeom )*nelems);
+
+       /* We need geos here */
+       initGEOS(lwnotice, lwerror);
+
+       offset = 0; i=0;
+       ngeoms = 0; npoints=0;
+//lwnotice("Nelems %d, MAXGEOMSPOINST %d", nelems, MAXGEOMSPOINTS);
+       while (!result) 
+       {
+               PG_LWGEOM *geom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset);
+               offset += INTALIGN(geom->size);
+
+               // Check is3d flag
+               if ( TYPE_NDIMS(geom->type) > 2 ) is3d = 1;
+
+               // Check SRID homogeneity 
+               if ( ! i ) SRID = pglwgeom_getSRID(geom);
+               else if ( SRID != pglwgeom_getSRID(geom) )
+                       lwerror("Operation on mixed SRID geometries");
+
+               geoms[ngeoms] =
+                       g1 = POSTGIS2GEOS(geom);
+                       //lwgeom_deserialize(SERIALIZED_FORM(geom));
+
+               npoints += GEOSGetNumCoordinate(geoms[ngeoms]);
+
+               ++ngeoms;
+               ++i;
+
+//lwnotice("Loop %d, npoints: %d", i, npoints);
+
+               /*
+                * Maximum count of geometry points reached
+                * or end of them, collect and buffer(0).
+                */
+               if ( (npoints>=MAXGEOMSPOINTS && ngeoms>1) || i==nelems)
+               {
+//lwnotice(" CHUNK (ngeoms:%d, npoints:%d, left:%d)", ngeoms, npoints, nelems-i);
+
+                       collection = GEOSMakeCollection(GEOS_GEOMETRYCOLLECTION,
+                               geoms, ngeoms);
+
+                       geos_result = GEOSBuffer(collection, 0, 0);
+                       if ( geos_result == NULL )
+                       {
+                               GEOSGeom_destroy(g1);
+                               lwerror("GEOS buffer() threw an error!");
+                       }
+                       GEOSGeom_destroy(collection);
+
+//lwnotice(" Buffer() executed");
+
+                       /*
+                        * If there are no other geoms in input
+                        * we've finished, otherwise we push
+                        * the result back on the input stack.
+                        */
+                       if ( i == nelems )
+                       {
+//lwnotice("  Final result points: %d", GEOSGetNumCoordinate(geos_result));
+                               GEOSSetSRID(geos_result, SRID);
+                               result = GEOS2POSTGIS(geos_result, is3d);
+                               GEOSGeom_destroy(geos_result);
+//lwnotice(" Result computed");
+                       }
+                       else
+                       {
+                               //lwgeoms = lwalloc(sizeof(LWGEOM *)*MAXGEOMS);
+                               //lwgeoms[0] = lwgeom_from_geometry(geos_result, is3d);
+                               geoms[0] = geos_result;
+                               ngeoms=1;
+                               npoints = GEOSGetNumCoordinate(geoms[0]);
+//lwnotice("  Result pushed back on lwgeoms array (npoints:%d)", npoints);
+                       }
+               }
+       }
+
+
+       //compressType(result);
+
+       PG_RETURN_POINTER(result);
+
+}
+
+#endif // def UNITE_USING_BUFFER
+
+
+//select geomunion('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((5 5, 15 5, 15 7, 5 7, 5 5))');
+PG_FUNCTION_INFO_V1(geomunion);
+Datum geomunion(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       int is3d;
+       int SRID;
+       GEOSGeom g1,g2,g3;
+       PG_LWGEOM *result;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       is3d = ( TYPE_NDIMS(geom1->type) > 2 ) ||
+               ( TYPE_NDIMS(geom2->type) > 2 );
+
+       SRID = pglwgeom_getSRID(geom1);
+       if ( SRID != pglwgeom_getSRID(geom2) )
+       {
+               elog(ERROR, "Operation on two geometries with different SRIDs");
+               PG_RETURN_NULL();
+       }
+
+       initGEOS(lwnotice, lwerror);
+//elog(NOTICE,"in geomunion");
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+//elog(NOTICE,"g1=%s",GEOSasText(g1));
+//elog(NOTICE,"g2=%s",GEOSasText(g2));
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       g3 = GEOSUnion(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+//elog(NOTICE,"g3=%s",GEOSasText(g3));
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS union() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+       GEOSSetSRID(g3, SRID);
+
+//elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+#ifdef PROFILE
+       profstart(PROF_G2P);
+#endif
+       result = GEOS2POSTGIS(g3, is3d);
+#ifdef PROFILE
+       profstop(PROF_G2P);
+#endif
+
+       GEOSGeom_destroy(g3);
+
+       if (result == NULL)
+       {
+               elog(ERROR,"GEOS union() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       //compressType(result);  // convert multi* to single item if appropriate
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, result);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_POINTER(result);
+}
+
+
+// select symdifference('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((5 5, 15 5, 15 7, 5 7, 5 5))');
+PG_FUNCTION_INFO_V1(symdifference);
+Datum symdifference(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2,g3;
+       PG_LWGEOM *result;
+       int is3d;
+       int SRID;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       is3d = ( TYPE_NDIMS(geom1->type) > 2 ) ||
+               ( TYPE_NDIMS(geom2->type) > 2 );
+
+       SRID = pglwgeom_getSRID(geom1);
+       if ( SRID != pglwgeom_getSRID(geom2) )
+       {
+               elog(ERROR, "Operation on two geometries with different SRIDs");
+               PG_RETURN_NULL();
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       g3 = GEOSSymDifference(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS symdifference() threw an error!");
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+               PG_RETURN_NULL(); //never get here
+       }
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       GEOSSetSRID(g3, SRID);
+
+#ifdef PROFILE
+       profstart(PROF_G2P);
+#endif
+       result = GEOS2POSTGIS(g3, is3d);
+#ifdef PROFILE
+       profstop(PROF_G2P);
+#endif
+
+       if (result == NULL)
+       {
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+               GEOSGeom_destroy(g3);
+               elog(ERROR,"GEOS symdifference() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+       GEOSGeom_destroy(g3);
+
+       //compressType(result);  // convert multi* to single item if appropriate
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, result);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_POINTER(result);
+}
+
+
+PG_FUNCTION_INFO_V1(boundary);
+Datum boundary(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM       *geom1;
+       GEOSGeom g1,g3;
+       PG_LWGEOM *result;
+       int SRID;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       SRID = pglwgeom_getSRID(geom1);
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1 );
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       g3 = GEOSBoundary(g1);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS bounary() threw an error!");
+               GEOSGeom_destroy(g1);
+               PG_RETURN_NULL(); //never get here
+       }
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       GEOSSetSRID(g3, SRID);
+
+#ifdef PROFILE
+       profstart(PROF_G2P);
+#endif
+       result = GEOS2POSTGIS(g3, TYPE_NDIMS(geom1->type) > 2);
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+
+       if (result == NULL)
+       {
+               GEOSGeom_destroy(g1);
+
+               GEOSGeom_destroy(g3);
+               elog(ERROR,"GEOS boundary() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g3);
+
+       //compressType(result);  // convert multi* to single item if appropriate
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, NULL, result);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(convexhull);
+Datum convexhull(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       GEOSGeom g1, g3;
+       PG_LWGEOM *result;
+       LWGEOM *lwout;
+       int SRID;
+       BOX2DFLOAT4 bbox;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       SRID = pglwgeom_getSRID(geom1);
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       g3 = GEOSConvexHull(g1);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS convexhull() threw an error!");
+               GEOSGeom_destroy(g1);
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+       //elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+       GEOSSetSRID(g3, SRID);
+
+#ifdef PROFILE
+       profstart(PROF_G2P);
+#endif
+       lwout = lwgeom_from_geometry(g3, TYPE_NDIMS(geom1->type) > 2);
+#ifdef PROFILE
+       profstop(PROF_G2P);
+#endif
+       if (lwout == NULL)
+       {
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g3);
+               elog(ERROR,"convexhull() failed to convert GEOS geometry to LWGEOM");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       /* Copy input bbox if any */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &bbox) )
+       {
+               lwout->bbox = &bbox;
+               /* Mark lwgeom bbox to be externally owned */
+               TYPE_SETHASBBOX(lwout->type, 1);
+       }
+
+       result = pglwgeom_serialize(lwout);
+       if (result == NULL)
+       {
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g3);
+               elog(ERROR,"GEOS convexhull() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+       lwgeom_release(lwout);
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g3);
+
+
+       //compressType(result);  // convert multi* to single item if appropriate
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, NULL, result);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+
+       PG_RETURN_POINTER(result);
+
+}
+
+PG_FUNCTION_INFO_V1(buffer);
+Datum buffer(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM       *geom1;
+       double  size;
+       GEOSGeom g1,g3;
+       PG_LWGEOM *result;
+       int quadsegs = 8; // the default
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       size = PG_GETARG_FLOAT8(1);
+       if ( PG_NARGS() > 2 ) quadsegs = PG_GETARG_INT32(2);
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       g3 = GEOSBuffer(g1,size,quadsegs);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS buffer() threw an error!");
+               GEOSGeom_destroy(g1);
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       GEOSSetSRID(g3, pglwgeom_getSRID(geom1));
+
+#ifdef PROFILE
+       profstart(PROF_G2P);
+#endif
+       result = GEOS2POSTGIS(g3, TYPE_NDIMS(geom1->type) > 2);
+#ifdef PROFILE
+       profstop(PROF_G2P);
+#endif
+       if (result == NULL)
+       {
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g3);
+               elog(ERROR,"GEOS buffer() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g3);
+
+
+       //compressType(result);  // convert multi* to single item if appropriate
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, NULL, result);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(intersection);
+Datum intersection(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2,g3;
+       PG_LWGEOM *result;
+       int is3d;
+       int SRID;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       is3d = ( TYPE_NDIMS(geom1->type) > 2 ) ||
+               ( TYPE_NDIMS(geom2->type) > 2 );
+
+       SRID = pglwgeom_getSRID(geom1);
+       if ( SRID != pglwgeom_getSRID(geom2) )
+       {
+               elog(ERROR, "Operation on two geometries with different SRIDs");
+               PG_RETURN_NULL();
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+//elog(NOTICE,"intersection() START");
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+//elog(NOTICE,"               constructed geometrys - calling geos");
+
+//elog(NOTICE,"g1 = %s",GEOSasText(g1));
+//elog(NOTICE,"g2 = %s",GEOSasText(g2));
+
+
+//if (g1==NULL)
+//     elog(NOTICE,"g1 is null");
+//if (g2==NULL)
+//     elog(NOTICE,"g2 is null");
+//elog(NOTICE,"g2 is valid = %i",GEOSisvalid(g2));
+//elog(NOTICE,"g1 is valid = %i",GEOSisvalid(g1));
+
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       g3 = GEOSIntersection(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+//elog(NOTICE,"               intersection finished");
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS Intersection() threw an error!");
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+       //elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+       GEOSSetSRID(g3, SRID);
+
+#ifdef PROFILE
+       profstart(PROF_G2P);
+#endif
+       result = GEOS2POSTGIS(g3, is3d);
+#ifdef PROFILE
+       profstop(PROF_G2P);
+#endif
+
+       if (result == NULL)
+       {
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+               GEOSGeom_destroy(g3);
+               elog(ERROR,"GEOS Intersection() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+       GEOSGeom_destroy(g3);
+
+       //compressType(result);  // convert multi* to single item if appropriate
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, result);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_POINTER(result);
+}
+
+//select difference('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((5 5, 15 5, 15 7, 5 7, 5 5))');
+PG_FUNCTION_INFO_V1(difference);
+Datum difference(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2,g3;
+       PG_LWGEOM *result;
+       int is3d;
+       int SRID;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       is3d = ( TYPE_NDIMS(geom1->type) > 2 ) ||
+               ( TYPE_NDIMS(geom2->type) > 2 );
+
+       SRID = pglwgeom_getSRID(geom1);
+       if ( SRID != pglwgeom_getSRID(geom2) )
+       {
+               elog(ERROR, "Operation on two geometries with different SRIDs");
+               PG_RETURN_NULL();
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       g3 = GEOSDifference(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS difference() threw an error!");
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+               PG_RETURN_NULL(); //never get here
+       }
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       GEOSSetSRID(g3, SRID);
+
+#ifdef PROFILE
+       profstart(PROF_G2P);
+#endif
+       result = GEOS2POSTGIS(g3, is3d);
+#ifdef PROFILE
+       profstop(PROF_G2P);
+#endif
+       if (result == NULL)
+       {
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+               GEOSGeom_destroy(g3);
+               elog(ERROR,"GEOS difference() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+       GEOSGeom_destroy(g3);
+
+       ////compressType(result);  // convert multi* to single item if appropriate
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, result);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_POINTER(result);
+}
+
+
+//select pointonsurface('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+PG_FUNCTION_INFO_V1(pointonsurface);
+Datum pointonsurface(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       GEOSGeom g1,g3;
+       PG_LWGEOM *result;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       g3 = GEOSPointOnSurface(g1);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS pointonsurface() threw an error!");
+               GEOSGeom_destroy(g1);
+               PG_RETURN_NULL(); //never get here
+       }
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       GEOSSetSRID(g3, pglwgeom_getSRID(geom1));
+#ifdef PROFILE
+       profstart(PROF_G2P);
+#endif
+       result = GEOS2POSTGIS(g3, (TYPE_NDIMS(geom1->type) > 2));
+#ifdef PROFILE
+       profstop(PROF_G2P);
+#endif
+       if (result == NULL)
+       {
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g3);
+               elog(ERROR,"GEOS pointonsurface() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g3);
+
+       // convert multi* to single item if appropriate
+       //compressType(result); 
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, NULL, result);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(centroid);
+Datum centroid(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom, *result;
+       GEOSGeom geosgeom, geosresult;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       geosgeom = POSTGIS2GEOS(geom);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       geosresult = GEOSGetCentroid(geosgeom);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       if ( geosresult == NULL )
+       {
+               GEOSGeom_destroy(geosgeom);
+               elog(ERROR,"GEOS getCentroid() threw an error!");
+               PG_RETURN_NULL(); 
+       }
+
+       GEOSSetSRID(geosresult, pglwgeom_getSRID(geom));
+
+#ifdef PROFILE
+       profstart(PROF_G2P);
+#endif
+       result = GEOS2POSTGIS(geosresult, (TYPE_NDIMS(geom->type) > 2));
+#ifdef PROFILE
+       profstop(PROF_G2P);
+#endif
+       if (result == NULL)
+       {
+               GEOSGeom_destroy(geosgeom);
+               GEOSGeom_destroy(geosresult);
+               elog(ERROR,"Error in GEOS-PGIS conversion");
+               PG_RETURN_NULL(); 
+       }
+       GEOSGeom_destroy(geosgeom);
+       GEOSGeom_destroy(geosresult);
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom, NULL, result);
+#endif
+
+       PG_FREE_IF_COPY(geom, 0);
+
+       PG_RETURN_POINTER(result);
+}
+
+
+
+//----------------------------------------------
+
+
+
+void errorIfGeometryCollection(PG_LWGEOM *g1, PG_LWGEOM *g2)
+{
+       int t1 = lwgeom_getType(g1->type);
+       int t2 = lwgeom_getType(g2->type);
+
+       if (  (t1 == COLLECTIONTYPE) || (t2 == COLLECTIONTYPE) )
+               elog(ERROR,"Relate Operation called with a LWGEOMCOLLECTION type.  This is unsupported");
+}
+
+PG_FUNCTION_INFO_V1(isvalid);
+Datum isvalid(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM       *geom1;
+       LWGEOM *lwgeom;
+       bool result;
+       GEOSGeom g1;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       lwgeom = lwgeom_deserialize(SERIALIZED_FORM(geom1));
+       if ( ! lwgeom )
+       {
+               lwerror("unable to deserialize input");
+       }
+       g1 = LWGEOM2GEOS(lwgeom);
+       if ( ! g1 )
+       {
+               lwgeom_release(lwgeom);
+               PG_RETURN_BOOL(FALSE);
+       }
+       lwgeom_release(lwgeom);
+       //g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSisValid(g1);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       GEOSGeom_destroy(g1);
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS isvalid() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, NULL, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+// overlaps(PG_LWGEOM g1,PG_LWGEOM g2)
+// returns  if GEOS::g1->overlaps(g2) returns true
+// throws an error (elog(ERROR,...)) if GEOS throws an error
+PG_FUNCTION_INFO_V1(overlaps);
+Datum overlaps(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2;
+       bool result;
+       BOX2DFLOAT4 box1, box2;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       /*
+        * short-circuit 1: if geom2 bounding box does not overlap
+        * geom1 bounding box we can prematurely return FALSE.
+        * Do the test IFF BOUNDING BOX AVAILABLE.
+        */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+               getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if ( box2.xmax < box1.xmin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.xmin > box1.xmax ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymax < box1.ymin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymin > box2.ymax ) PG_RETURN_BOOL(FALSE);
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSOverlaps(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS overlaps() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+
+PG_FUNCTION_INFO_V1(contains);
+Datum contains(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2;
+       bool result;
+       BOX2DFLOAT4 box1, box2;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       /*
+        * short-circuit 1: if geom2 bounding box is not completely inside
+        * geom1 bounding box we can prematurely return FALSE.
+        * Do the test IFF BOUNDING BOX AVAILABLE.
+        */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+               getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if ( box2.xmin < box1.xmin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.xmax > box1.xmax ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymin < box1.ymin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymax > box1.ymax ) PG_RETURN_BOOL(FALSE);
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSContains(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS contains() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(within);
+Datum within(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2;
+       bool result;
+       BOX2DFLOAT4 box1, box2;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       /*
+        * short-circuit 1: if geom1 bounding box is not completely inside
+        * geom2 bounding box we can prematurely return FALSE.
+        * Do the test IFF BOUNDING BOX AVAILABLE.
+        */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+               getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if ( box1.xmin < box2.xmin ) PG_RETURN_BOOL(FALSE);
+               if ( box1.xmax > box2.xmax ) PG_RETURN_BOOL(FALSE);
+               if ( box1.ymin < box2.ymin ) PG_RETURN_BOOL(FALSE);
+               if ( box1.ymax > box2.ymax ) PG_RETURN_BOOL(FALSE);
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSWithin(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS within() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+
+PG_FUNCTION_INFO_V1(crosses);
+Datum crosses(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2;
+       bool result;
+       BOX2DFLOAT4 box1, box2;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       /*
+        * short-circuit 1: if geom2 bounding box does not overlap
+        * geom1 bounding box we can prematurely return FALSE.
+        * Do the test IFF BOUNDING BOX AVAILABLE.
+        */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+               getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if ( box2.xmax < box1.xmin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.xmin > box1.xmax ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymax < box1.ymin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymin > box2.ymax ) PG_RETURN_BOOL(FALSE);
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSCrosses(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS crosses() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+
+PG_FUNCTION_INFO_V1(intersects);
+Datum intersects(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2;
+       bool result;
+       BOX2DFLOAT4 box1, box2;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       /*
+        * short-circuit 1: if geom2 bounding box does not overlap
+        * geom1 bounding box we can prematurely return FALSE.
+        * Do the test IFF BOUNDING BOX AVAILABLE.
+        */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+               getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if ( box2.xmax < box1.xmin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.xmin > box1.xmax ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymax < box1.ymin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymin > box2.ymax ) PG_RETURN_BOOL(FALSE);
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1 );
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2 );
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSIntersects(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS intersects() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("intr",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(touches);
+Datum touches(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2;
+       bool result;
+       BOX2DFLOAT4 box1, box2;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       /*
+        * short-circuit 1: if geom2 bounding box does not overlap
+        * geom1 bounding box we can prematurely return FALSE.
+        * Do the test IFF BOUNDING BOX AVAILABLE.
+        */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+               getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if ( box2.xmax < box1.xmin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.xmin > box1.xmax ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymax < box1.ymin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymin > box2.ymax ) PG_RETURN_BOOL(FALSE);
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1 );
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2 );
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSTouches(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS touches() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(disjoint);
+Datum disjoint(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2;
+       bool result;
+       BOX2DFLOAT4 box1, box2;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       /*
+        * short-circuit 1: if geom2 bounding box does not overlap
+        * geom1 bounding box we can prematurely return TRUE.
+        * Do the test IFF BOUNDING BOX AVAILABLE.
+        */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+               getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if ( box2.xmax < box1.xmin ) PG_RETURN_BOOL(TRUE);
+               if ( box2.xmin > box1.xmax ) PG_RETURN_BOOL(TRUE);
+               if ( box2.ymax < box1.ymin ) PG_RETURN_BOOL(TRUE);
+               if ( box2.ymin > box2.ymax ) PG_RETURN_BOOL(TRUE);
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSDisjoint(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS disjoin() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(relate_pattern);
+Datum relate_pattern(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       char *patt;
+       bool result;
+       GEOSGeom g1,g2;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+       patt =  DatumGetCString(DirectFunctionCall1(textout,
+                        PointerGetDatum(PG_GETARG_DATUM(2))));
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSRelatePattern(g1,g2,patt);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+       pfree(patt);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS relate_pattern() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+
+PG_FUNCTION_INFO_V1(relate_full);
+Datum relate_full(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2;
+       char *relate_str;
+       int len;
+       text *result;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+//elog(NOTICE,"in relate_full()");
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+
+       initGEOS(lwnotice, lwerror);
+
+//elog(NOTICE,"GEOS init()");
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1 );
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2 );
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+//elog(NOTICE,"constructed geometries ");
+
+       if ((g1==NULL) || (g2 == NULL))
+               elog(NOTICE,"g1 or g2 are null");
+
+//elog(NOTICE,GEOSasText(g1));
+//elog(NOTICE,GEOSasText(g2));
+
+//elog(NOTICE,"valid g1 = %i", GEOSisvalid(g1));
+//elog(NOTICE,"valid g2 = %i",GEOSisvalid(g2));
+
+//elog(NOTICE,"about to relate()");
+
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       relate_str = GEOSRelate(g1, g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+
+//elog(NOTICE,"finished relate()");
+
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+
+       if (relate_str == NULL)
+       {
+               //free(relate_str);
+               elog(ERROR,"GEOS relate() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       len = strlen(relate_str) + VARHDRSZ;
+
+       result= palloc(len);
+       VARATT_SIZEP(result) = len;
+       //*((int *) result) = len;
+
+       memcpy(VARDATA(result), relate_str, len-VARHDRSZ);
+
+       free(relate_str);
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_POINTER(result);
+}
+
+//==============================
+
+PG_FUNCTION_INFO_V1(geomequals);
+Datum geomequals(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom1;
+       PG_LWGEOM *geom2;
+       GEOSGeom g1,g2;
+       bool result;
+       BOX2DFLOAT4 box1, box2;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       /*
+        * short-circuit 1: if geom2 bounding box does not equal
+        * geom1 bounding box we can prematurely return FALSE.
+        * Do the test IFF BOUNDING BOX AVAILABLE.
+        */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+               getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if ( box2.xmax != box1.xmax ) PG_RETURN_BOOL(FALSE);
+               if ( box2.xmin != box1.xmin ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymax != box1.ymax ) PG_RETURN_BOOL(FALSE);
+               if ( box2.ymin != box2.ymin ) PG_RETURN_BOOL(FALSE);
+       }
+
+       initGEOS(lwnotice, lwerror);
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom1);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+#ifdef PROFILE
+       profstart(PROF_P2G2);
+#endif
+       g2 = POSTGIS2GEOS(geom2);
+#ifdef PROFILE
+       profstop(PROF_P2G2);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSEquals(g1,g2);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+       GEOSGeom_destroy(g1);
+       GEOSGeom_destroy(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS equals() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom1, geom2, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+}
+
+PG_FUNCTION_INFO_V1(issimple);
+Datum issimple(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom;
+       GEOSGeom g1;
+       int result;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       if (lwgeom_getnumgeometries(SERIALIZED_FORM(geom)) == 0)
+               PG_RETURN_BOOL(true);
+
+       initGEOS(lwnotice, lwerror);
+
+       //elog(NOTICE,"GEOS init()");
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom);
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSisSimple(g1);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+       GEOSGeom_destroy(g1);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS issimple() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom, NULL, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom, 0);
+
+       PG_RETURN_BOOL(result);
+}
+
+PG_FUNCTION_INFO_V1(isring);
+Datum isring(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom;
+       GEOSGeom g1;
+       int result;
+
+#ifdef PROFILE
+       profstart(PROF_QRUN);
+#endif
+
+       geom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       if (lwgeom_getType(geom->type) != LINETYPE)
+       {
+               elog(ERROR,"isring() should only be called on a LINE");
+       }
+
+       if (lwgeom_getnumgeometries(SERIALIZED_FORM(geom)) == 0)
+               PG_RETURN_BOOL(false);
+
+       initGEOS(lwnotice, lwerror);
+
+       //elog(NOTICE,"GEOS init()");
+
+#ifdef PROFILE
+       profstart(PROF_P2G1);
+#endif
+       g1 = POSTGIS2GEOS(geom );
+#ifdef PROFILE
+       profstop(PROF_P2G1);
+#endif
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+#endif
+       result = GEOSisRing(g1);
+#ifdef PROFILE
+       profstop(PROF_GRUN);
+#endif
+       GEOSGeom_destroy(g1);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS isring() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+#ifdef PROFILE
+       profstop(PROF_QRUN);
+       profreport("geos",geom, NULL, NULL);
+#endif
+
+       PG_FREE_IF_COPY(geom, 0);
+
+       PG_RETURN_BOOL(result);
+}
+
+
+
+//= GEOS <=> POSTGIS CONVERSION =========================
+
+//-----=GEOS2POSTGIS=
+
+
+// Return an LWGEOM from a GEOSGeom
+LWGEOM *
+lwgeom_from_geometry(GEOSGeom geom, char want3d)
+{
+       size_t size;
+       char *wkb;
+       LWGEOM *lwgeom;
+
+       wkb = GEOSGeomToWKB_buf(geom, &size);
+       lwgeom = lwgeom_from_ewkb(wkb, size);
+
+       return lwgeom;
+}
+
+PG_LWGEOM *
+GEOS2POSTGIS(GEOSGeom geom, char want3d)
+{
+       size_t size;
+       char *wkb;
+       PG_LWGEOM *pglwgeom, *ret;
+
+       wkb = GEOSGeomToWKB_buf(geom, &size);
+       pglwgeom = pglwgeom_from_ewkb(wkb, size);
+
+       if ( ! pglwgeom )
+       {
+               lwerror("GEOS2POSTGIS: lwgeom_from_geometry returned NULL");
+               return NULL;
+       }
+
+#ifdef PGIS_DEBUG_GEOS2POSTGIS
+       lwnotice("GEOS2POSTGIS: lwgeom_from_geometry returned a %s", lwgeom_summary(lwgeom, 0)); 
+#endif
+
+       if ( is_worth_caching_pglwgeom_bbox(pglwgeom) )
+       {
+               ret = (PG_LWGEOM *)DatumGetPointer(DirectFunctionCall1(
+                       LWGEOM_addBBOX, PointerGetDatum(pglwgeom)));
+               lwfree(pglwgeom);
+       }
+       else
+       {
+               ret = pglwgeom;
+       }
+
+       return ret;
+}
+
+//-----=POSTGIS2GEOS=
+
+GEOSGeom LWGEOM2GEOS(LWGEOM *lwgeom);
+
+GEOSGeom 
+LWGEOM2GEOS(LWGEOM *lwgeom)
+{
+       size_t size;
+       char *wkb;
+       GEOSGeom geom;
+
+       wkb = lwgeom_to_ewkb(lwgeom, getMachineEndian(), &size);
+       geom = GEOSGeomFromWKB_buf(wkb, size);
+       return geom;
+}
+
+GEOSGeom 
+POSTGIS2GEOS(PG_LWGEOM *pglwgeom)
+{
+       size_t size;
+       char *wkb;
+       GEOSGeom geom;
+
+       wkb = pglwgeom_to_ewkb(pglwgeom, getMachineEndian(), &size);
+       geom = GEOSGeomFromWKB_buf(wkb, size);
+       lwfree(wkb);
+
+       wkb = GEOSGeomToWKT(geom);
+       lwnotice("GEOS geom: %s", wkb);
+
+       return geom;
+}
+
+PG_FUNCTION_INFO_V1(GEOSnoop);
+Datum GEOSnoop(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom;
+       GEOSGeom geosgeom;
+       PG_LWGEOM *result;
+
+       initGEOS(lwnotice, lwerror);
+
+       geom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+#ifdef PGIS_DEBUG_CONVERTER
+       elog(NOTICE, "GEOSnoop: IN: %s", unparse_WKT(SERIALIZED_FORM(geom), malloc, free));
+#endif
+
+       geosgeom = POSTGIS2GEOS(geom);
+       if ( ! geosgeom ) PG_RETURN_NULL();
+
+#ifdef PROFILE
+       profstart(PROF_GRUN);
+       profstop(PROF_GRUN);
+#endif
+
+       result = GEOS2POSTGIS(geosgeom, TYPE_NDIMS(geom->type) > 2);
+       GEOSGeom_destroy(geosgeom);
+
+#ifdef PGIS_DEBUG_CONVERTER
+       elog(NOTICE, "GEOSnoop: OUT: %s", unparse_WKT(SERIALIZED_FORM(result), malloc, free));
+#endif
+
+       PG_FREE_IF_COPY(geom, 0);
+
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(polygonize_garray);
+Datum polygonize_garray(PG_FUNCTION_ARGS)
+{
+       Datum datum;
+       ArrayType *array;
+       int is3d = 0;
+       unsigned int nelems, i;
+       PG_LWGEOM *result;
+       GEOSGeom geos_result;
+       GEOSGeom *vgeoms;
+       int SRID=-1;
+       size_t offset;
+#ifdef PGIS_DEBUG
+       static int call=1;
+#endif
+
+#ifdef PGIS_DEBUG
+       call++;
+#endif
+
+       datum = PG_GETARG_DATUM(0);
+
+       /* Null array, null geometry (should be empty?) */
+       if ( (Pointer *)datum == NULL ) PG_RETURN_NULL();
+
+       array = (ArrayType *) PG_DETOAST_DATUM(datum);
+
+       nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
+
+#ifdef PGIS_DEBUG
+       elog(NOTICE, "polygonize_garray: number of elements: %d", nelems);
+#endif
+
+       if ( nelems == 0 ) PG_RETURN_NULL();
+
+       /* Ok, we really need geos now ;) */
+       initGEOS(lwnotice, lwerror);
+
+       vgeoms = palloc(sizeof(GEOSGeom )*nelems);
+       offset = 0;
+       for (i=0; i<nelems; i++)
+       {
+               PG_LWGEOM *geom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset);
+               offset += INTALIGN(geom->size);
+
+               vgeoms[i] = POSTGIS2GEOS(geom);
+               if ( ! i )
+               {
+                       SRID = pglwgeom_getSRID(geom);
+               }
+               else
+               {
+                       if ( SRID != pglwgeom_getSRID(geom) )
+                       {
+       elog(ERROR, "polygonize: operation on mixed SRID geometries");
+       PG_RETURN_NULL();
+                       }
+               }
+       }
+
+#ifdef PGIS_DEBUG
+       elog(NOTICE, "polygonize_garray: invoking GEOSpolygonize");
+#endif
+
+       geos_result = GEOSPolygonize(vgeoms, nelems);
+#ifdef PGIS_DEBUG
+       elog(NOTICE, "polygonize_garray: GEOSpolygonize returned");
+#endif
+       //pfree(vgeoms);
+       if ( ! geos_result ) PG_RETURN_NULL();
+
+       GEOSSetSRID(geos_result, SRID);
+       result = GEOS2POSTGIS(geos_result, is3d);
+       GEOSGeom_destroy(geos_result);
+       if ( result == NULL )
+       {
+               elog(ERROR, "GEOS2POSTGIS returned an error");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       //compressType(result);
+
+       PG_RETURN_POINTER(result);
+
+}
+
+Datum JTSnoop(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(JTSnoop);
+Datum JTSnoop(PG_FUNCTION_ARGS)
+{
+       elog(ERROR, "JTS support is disabled");
+       PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(postgis_jts_version);
+Datum postgis_jts_version(PG_FUNCTION_ARGS)
+{
+       PG_RETURN_NULL();
+}