#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" /* * 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 */ /* #define WKB_CONVERSION 1 */ /* * * 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 1 */ /* #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); Datum LWGEOM_buildarea(PG_FUNCTION_ARGS); Datum linemerge(PG_FUNCTION_ARGS); LWGEOM *GEOS2LWGEOM(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++; lwnotice("GEOS incremental union (call %d)", 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, "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, lwnotice); offset = 0; for (i=0; isize); pgis_geom = geom; #ifdef PGIS_DEBUG elog(NOTICE, "geom %d @ %p", i, geom); #endif /* Check is3d flag */ if ( TYPE_HASZ(geom->type) ) 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 { errorIfSRIDMismatch(SRID, pglwgeom_getSRID(geom)); } 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++; lwnotice("GEOS buffer union (call %d)", 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, "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, lwnotice); offset = 0; i=0; ngeoms = 0; npoints=0; #ifdef PGIS_DEBUG lwnotice("Nelems %d, MAXGEOMSPOINST %d", nelems, MAXGEOMSPOINTS); #endif while (!result) { PG_LWGEOM *geom = (PG_LWGEOM *)(ARR_DATA_PTR(array)+offset); offset += INTALIGN(geom->size); /* Check is3d flag */ if ( TYPE_HASZ(geom->type) ) is3d = 1; /* Check SRID homogeneity */ if ( ! i ) SRID = pglwgeom_getSRID(geom); else errorIfSRIDMismatch(SRID, pglwgeom_getSRID(geom)); geoms[ngeoms] = g1 = POSTGIS2GEOS(geom); npoints += GEOSGetNumCoordinate(geoms[ngeoms]); ++ngeoms; ++i; #if PGIS_DEBUG > 1 lwnotice("Loop %d, npoints: %d", i, npoints); #endif /* * Maximum count of geometry points reached * or end of them, collect and buffer(0). */ if ( (npoints>=MAXGEOMSPOINTS && ngeoms>1) || i==nelems) { #if PGIS_DEBUG > 1 lwnotice(" CHUNK (ngeoms:%d, npoints:%d, left:%d)", ngeoms, npoints, nelems-i); #endif 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); #if PGIS_DEBUG > 1 lwnotice(" Buffer() executed"); #endif /* * If there are no other geoms in input * we've finished, otherwise we push * the result back on the input stack. */ if ( i == nelems ) { #if PGIS_DEBUG > 1 i lwnotice(" Final result points: %d", GEOSGetNumCoordinate(geos_result)); #endif GEOSSetSRID(geos_result, SRID); result = GEOS2POSTGIS(geos_result, is3d); GEOSGeom_destroy(geos_result); #if PGIS_DEBUG > 1 lwnotice(" Result computed"); #endif } else { geoms[0] = geos_result; ngeoms=1; npoints = GEOSGetNumCoordinate(geoms[0]); #if PGIS_DEBUG > 1 lwnotice(" Result pushed back on lwgeoms array (npoints:%d)", npoints); #endif } } } /* 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 PGIS_DEBUG elog(NOTICE,"in geomunion"); #endif #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_HASZ(geom1->type) ) || ( TYPE_HASZ(geom2->type) ); SRID = pglwgeom_getSRID(geom1); errorIfSRIDMismatch(SRID, pglwgeom_getSRID(geom2)); initGEOS(lwnotice, lwnotice); #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 PGIS_DEBUG elog(NOTICE,"g1=%s", GEOSGeomToWKT(g1)); elog(NOTICE,"g2=%s", GEOSGeomToWKT(g2)); #endif #ifdef PROFILE profstart(PROF_GRUN); #endif g3 = GEOSUnion(g1,g2); #ifdef PROFILE profstop(PROF_GRUN); #endif #ifdef PGIS_DEBUG elog(NOTICE,"g3=%s", GEOSGeomToWKT(g3)); #endif 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); #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); */ #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_HASZ(geom1->type) ) || ( TYPE_HASZ(geom2->type) ); SRID = pglwgeom_getSRID(geom1); errorIfSRIDMismatch(SRID, pglwgeom_getSRID(geom2)); initGEOS(lwnotice, lwnotice); #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 */ } #ifdef PGIS_DEBUG elog(NOTICE,"result: %s", GEOSGeomToWKT(g3)); #endif 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); */ #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, lwnotice); #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 */ } #ifdef PGIS_DEBUG elog(NOTICE,"result: %s", GEOSGeomToWKT(g3)); #endif GEOSSetSRID(g3, SRID); #ifdef PROFILE profstart(PROF_G2P); #endif result = GEOS2POSTGIS(g3, TYPE_HASZ(geom1->type)); #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); */ #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, lwnotice); #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 */ } #ifdef PGIS_DEBUG elog(NOTICE,"result: %s", GEOSGeomToWKT(g3)); #endif GEOSSetSRID(g3, SRID); #ifdef PROFILE profstart(PROF_G2P); #endif lwout = GEOS2LWGEOM(g3, TYPE_HASZ(geom1->type)); #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 = box2d_clone(&bbox); } 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); */ #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, lwnotice); #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 */ } #ifdef PGIS_DEBUG elog(NOTICE,"result: %s", GEOSGeomToWKT(g3)); #endif GEOSSetSRID(g3, pglwgeom_getSRID(geom1)); #ifdef PROFILE profstart(PROF_G2P); #endif result = GEOS2POSTGIS(g3, TYPE_HASZ(geom1->type)); #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); */ #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_HASZ(geom1->type) ) || ( TYPE_HASZ(geom2->type) ); SRID = pglwgeom_getSRID(geom1); errorIfSRIDMismatch(SRID, pglwgeom_getSRID(geom2)); initGEOS(lwnotice, lwnotice); #ifdef PGIS_DEBUG elog(NOTICE,"intersection() START"); #endif #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 PGIS_DEBUG elog(NOTICE," constructed geometrys - calling geos"); elog(NOTICE," g1 = %s", GEOSGeomToWKT(g1)); elog(NOTICE," g2 = %s", GEOSGeomToWKT(g2)); /*elog(NOTICE,"g2 is valid = %i",GEOSisvalid(g2)); */ /*elog(NOTICE,"g1 is valid = %i",GEOSisvalid(g1)); */ #endif #ifdef PROFILE profstart(PROF_GRUN); #endif g3 = GEOSIntersection(g1,g2); #ifdef PROFILE profstop(PROF_GRUN); #endif #ifdef PGIS_DEBUG elog(NOTICE," intersection finished"); #endif if (g3 == NULL) { elog(ERROR,"GEOS Intersection() threw an error!"); GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); PG_RETURN_NULL(); /* never get here */ } #ifdef PGIS_DEBUG elog(NOTICE,"result: %s", GEOSGeomToWKT(g3) ) ; #endif 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); */ #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_HASZ(geom1->type) ) || ( TYPE_HASZ(geom2->type) ); SRID = pglwgeom_getSRID(geom1); errorIfSRIDMismatch(SRID, pglwgeom_getSRID(geom2)); initGEOS(lwnotice, lwnotice); #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 */ } #ifdef PGIS_DEBUG elog(NOTICE,"result: %s", GEOSGeomToWKT(g3) ) ; #endif 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); */ #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, lwnotice); #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 */ } #ifdef PGIS_DEBUG elog(NOTICE,"result: %s", GEOSGeomToWKT(g3) ) ; #endif GEOSSetSRID(g3, pglwgeom_getSRID(geom1)); #ifdef PROFILE profstart(PROF_G2P); #endif result = GEOS2POSTGIS(g3, TYPE_HASZ(geom1->type)); #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); /* 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, lwnotice); #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_HASZ(geom->type)); #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, lwnotice); #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); #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); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(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, lwnotice); #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); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(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, lwnotice); #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); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(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, lwnotice); #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); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(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, lwnotice); #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); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(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, lwnotice); #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); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(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, lwnotice); #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); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(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, lwnotice); #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); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2)); initGEOS(lwnotice, lwnotice); #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 #ifdef PGIS_DEBUG elog(NOTICE,"in relate_full()"); #endif geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); geom2 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); errorIfGeometryCollection(geom1,geom2); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2)); initGEOS(lwnotice, lwnotice); #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 PGIS_DEBUG elog(NOTICE,"constructed geometries "); #endif if ((g1==NULL) || (g2 == NULL)) elog(NOTICE,"g1 or g2 are null"); #ifdef PGIS_DEBUG elog(NOTICE,GEOSGeomToWKT(g1)); elog(NOTICE,GEOSGeomToWKT(g2)); /*elog(NOTICE,"valid g1 = %i", GEOSisvalid(g1));*/ /*elog(NOTICE,"valid g2 = %i",GEOSisvalid(g2));*/ elog(NOTICE,"about to relate()"); #endif #ifdef PROFILE profstart(PROF_GRUN); #endif relate_str = GEOSRelate(g1, g2); #ifdef PROFILE profstop(PROF_GRUN); #endif #ifdef PGIS_DEBUG elog(NOTICE,"finished relate()"); #endif GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); if (relate_str == NULL) { 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; 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); errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(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, lwnotice); #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 PGIS_DEBUG elog(NOTICE,"issimple called"); #endif #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, lwnotice); #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, lwnotice); #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(); } #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= */ #ifdef WKB_CONVERSION /* Return an LWGEOM from a GEOSGeom */ LWGEOM * GEOS2LWGEOM(GEOSGeom geom, char want3d) { size_t size; char *wkb; LWGEOM *lwgeom; if ( want3d ) GEOS_setWKBOutputDims(3); else GEOS_setWKBOutputDims(2); 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; if ( want3d ) GEOS_setWKBOutputDims(3); else GEOS_setWKBOutputDims(2); wkb = GEOSGeomToWKB_buf(geom, &size); if ( ! wkb ) { lwerror("GEOS failed to export WKB"); } pglwgeom = pglwgeom_from_ewkb(wkb, size); if ( ! pglwgeom ) { lwerror("GEOS2POSTGIS: lwgeom_from_ewkb returned NULL"); } if ( is_worth_caching_pglwgeom_bbox(pglwgeom) ) { ret = (PG_LWGEOM *)DatumGetPointer(DirectFunctionCall1( LWGEOM_addBBOX, PointerGetDatum(pglwgeom))); lwfree(pglwgeom); } else { ret = pglwgeom; } return ret; } #else /* !ndef WKB_CONVERSION */ /* Return a POINTARRAY from a GEOSCoordSeq */ POINTARRAY * ptarray_from_GEOSCoordSeq(GEOSCoordSeq cs, char want3d) { unsigned int dims=2; unsigned int size, i, ptsize; uchar *points, *ptr; POINTARRAY *ret; #ifdef PGIS_DEBUG_GEOS2POSTGIS lwnotice("ptarray_fromGEOSCoordSeq called"); #endif if ( ! GEOSCoordSeq_getSize(cs, &size) ) lwerror("Exception thrown"); #ifdef PGIS_DEBUG_GEOS2POSTGIS lwnotice(" GEOSCoordSeq size: %d", size); #endif if ( want3d ) { if ( ! GEOSCoordSeq_getDimensions(cs, &dims) ) lwerror("Exception thrown"); #ifdef PGIS_DEBUG_GEOS2POSTGIS lwnotice(" GEOSCoordSeq dimensions: %d", dims); #endif /* forget higher dimensions (if any) */ if ( dims > 3 ) dims = 3; } #ifdef PGIS_DEBUG_GEOS2POSTGIS lwnotice(" output dimensions: %d", dims); #endif ptsize = sizeof(double)*dims; ret = ptarray_construct((dims==3), 0, size); points = ret->serialized_pointlist; ptr = points; for (i=0; i= 3 ) GEOSCoordSeq_getZ(cs, i, &(point.z)); memcpy(ptr, &point, ptsize); ptr += ptsize; } return ret; } /* Return an LWGEOM from a Geometry */ LWGEOM * GEOS2LWGEOM(GEOSGeom geom, char want3d) { int type = GEOSGeomTypeId(geom) ; bool hasZ = GEOSHasZ(geom); int SRID = GEOSGetSRID(geom); /* GEOS's 0 is equivalent to our -1 as for SRID values */ if ( SRID == 0 ) SRID = -1; if ( ! hasZ ) { if ( want3d ) { #ifdef PGIS_DEBUG elog(NOTICE, "Geometry has no Z, won't provide one"); #endif want3d = 0; } } switch (type) { GEOSCoordSeq cs; POINTARRAY *pa, **ppaa; GEOSGeom g; LWGEOM **geoms; unsigned int i, ngeoms; case GEOS_POINT: #ifdef PGIS_DEBUG_GEOS2POSTGIS lwnotice("lwgeom_from_geometry: it's a Point"); #endif cs = GEOSGeom_getCoordSeq(geom); pa = ptarray_from_GEOSCoordSeq(cs, want3d); return (LWGEOM *)lwpoint_construct(SRID, NULL, pa); case GEOS_LINESTRING: case GEOS_LINEARRING: #ifdef PGIS_DEBUG_GEOS2POSTGIS lwnotice("lwgeom_from_geometry: it's a LineString or LinearRing"); #endif cs = GEOSGeom_getCoordSeq(geom); pa = ptarray_from_GEOSCoordSeq(cs, want3d); return (LWGEOM *)lwline_construct(SRID, NULL, pa); case GEOS_POLYGON: #ifdef PGIS_DEBUG_GEOS2POSTGIS lwnotice("lwgeom_from_geometry: it's a Polygon"); #endif ngeoms = GEOSGetNumInteriorRings(geom); ppaa = lwalloc(sizeof(POINTARRAY *)*(ngeoms+1)); g = GEOSGetExteriorRing(geom); cs = GEOSGeom_getCoordSeq(g); ppaa[0] = ptarray_from_GEOSCoordSeq(cs, want3d); for (i=0; idims) ) dims = 3; size = pa->npoints; sq = GEOSCoordSeq_create(size, dims); if ( ! sq ) lwerror("Error creating GEOS Coordinate Sequence"); for (i=0; itype); int geostype; #ifdef PGIS_DEBUG_POSTGIS2GEOS char *wkt; #endif #ifdef PGIS_DEBUG_POSTGIS2GEOS lwnotice("LWGEOM2GEOS got a %s", lwgeom_typename(type)); #endif switch (type) { LWPOINT *lwp; LWPOLY *lwpoly; LWLINE *lwl; LWCOLLECTION *lwc; case POINTTYPE: lwp = (LWPOINT *)lwgeom; sq = ptarray_to_GEOSCoordSeq(lwp->point); g = GEOSGeom_createPoint(sq); if ( ! g ) lwerror("Exception in LWGEOM2GEOS"); break; case LINETYPE: lwl = (LWLINE *)lwgeom; sq = ptarray_to_GEOSCoordSeq(lwl->points); g = GEOSGeom_createLineString(sq); if ( ! g ) lwerror("Exception in LWGEOM2GEOS"); break; case POLYGONTYPE: lwpoly = (LWPOLY *)lwgeom; sq = ptarray_to_GEOSCoordSeq(lwpoly->rings[0]); shell = GEOSGeom_createLinearRing(sq); if ( ! shell ) return NULL; /*lwerror("LWGEOM2GEOS: exception during polygon shell conversion"); */ ngeoms = lwpoly->nrings-1; geoms = malloc(sizeof(GEOSGeom)*ngeoms); for (i=1; inrings; ++i) { sq = ptarray_to_GEOSCoordSeq(lwpoly->rings[i]); geoms[i-1] = GEOSGeom_createLinearRing(sq); if ( ! geoms[i-1] ) return NULL; /*lwerror("LWGEOM2GEOS: exception during polygon hole conversion"); */ } g = GEOSGeom_createPolygon(shell, geoms, ngeoms); if ( ! g ) return NULL; free(geoms); break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: if ( type == MULTIPOINTTYPE ) geostype = GEOS_MULTIPOINT; else if ( type == MULTILINETYPE ) geostype = GEOS_MULTILINESTRING; else if ( type == MULTIPOLYGONTYPE ) geostype = GEOS_MULTIPOLYGON; else geostype = GEOS_GEOMETRYCOLLECTION; lwc = (LWCOLLECTION *)lwgeom; ngeoms = lwc->ngeoms; geoms = malloc(sizeof(GEOSGeom)*ngeoms); for (i=0; igeoms[i]); if ( ! geoms[i] ) return NULL; } g = GEOSGeom_createCollection(geostype, geoms, ngeoms); if ( ! g ) return NULL; free(geoms); break; default: lwerror("Unknown geometry type: %d", type); return NULL; } GEOSSetSRID(g, lwgeom->SRID); #ifdef PGIS_DEBUG_POSTGIS2GEOS wkt = GEOSGeomToWKT(g); lwnotice("LWGEOM2GEOS: GEOSGeom: %s", wkt); free(wkt); #endif return g; } GEOSGeom POSTGIS2GEOS(PG_LWGEOM *pglwgeom) { GEOSGeom ret; LWGEOM *lwgeom = lwgeom_deserialize(SERIALIZED_FORM(pglwgeom)); if ( ! lwgeom ) { lwerror("POSTGIS2GEOS: unable to deserialize input"); return NULL; } ret = LWGEOM2GEOS(lwgeom); lwgeom_release(lwgeom); if ( ! ret ) { lwerror("POSTGIS2GEOS conversion failed"); return NULL; } return ret; } #endif /* WKB_CONVERSION */ PG_FUNCTION_INFO_V1(GEOSnoop); Datum GEOSnoop(PG_FUNCTION_ARGS) { PG_LWGEOM *geom; GEOSGeom geosgeom; PG_LWGEOM *result; initGEOS(lwnotice, lwnotice); 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_HASZ(geom->type)); 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, lwnotice); vgeoms = palloc(sizeof(GEOSGeom)*nelems); offset = 0; for (i=0; isize); 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 for (i=0; itype)); #ifdef PROFILE profstop(PROF_G2P); #endif if (result == NULL) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g3); elog(ERROR,"GEOS LineMerge() threw an error (result postgis geometry formation)!"); PG_RETURN_NULL(); /*never get here */ } GEOSGeom_destroy(g1); GEOSGeom_destroy(g3); /* compressType(result); */ #ifdef PROFILE profstop(PROF_QRUN); profreport("geos",geom1, NULL, result); #endif PG_FREE_IF_COPY(geom1, 0); 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(); } /* * Take a geometry and return an areal geometry * (Polygon or MultiPolygon). * Actually a wrapper around GEOSpolygonize, * transforming the resulting collection into * a valid polygonzl Geometry. */ PG_FUNCTION_INFO_V1(LWGEOM_buildarea); Datum LWGEOM_buildarea(PG_FUNCTION_ARGS) { int is3d = 0; unsigned int i, ngeoms; PG_LWGEOM *result; LWGEOM *lwg; GEOSGeom geos_result, shp; GEOSGeom vgeoms[1]; int SRID=-1; #ifdef PGIS_DEBUG static int call=1; #endif #ifdef PGIS_DEBUG call++; lwnotice("buildarea called (call %d)", call); #endif PG_LWGEOM *geom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); SRID = pglwgeom_getSRID(geom); is3d = TYPE_HASZ(geom->type); #ifdef PGIS_DEBUG lwnotice("LWGEOM_buildarea got geom @ %x", geom); #endif initGEOS(lwnotice, lwnotice); vgeoms[0] = POSTGIS2GEOS(geom); geos_result = GEOSPolygonize(vgeoms, 1); GEOSGeom_destroy(vgeoms[0]); #ifdef PGIS_DEBUG lwnotice("GEOSpolygonize returned @ %x", geos_result); #endif /* Null return from GEOSpolygonize */ if ( ! geos_result ) PG_RETURN_NULL(); /* * We should now have a collection */ #if PARANOIA_LEVEL > 0 if ( GEOSGeometryTypeId(geos_result) != COLLECTIONTYPE ) { GEOSGeom_destroy(geos_result); lwerror("Unexpected return from GEOSpolygonize"); PG_RETURN_NULL(); } #endif ngeoms = GEOSGetNumGeometries(geos_result); #ifdef PGIS_DEBUG lwnotice("GEOSpolygonize: ngeoms in polygonize output: %d", ngeoms); #endif /* * No geometries in collection, return NULL */ if ( ngeoms == 0 ) { GEOSGeom_destroy(geos_result); PG_RETURN_NULL(); } /* * Return first geometry if we only have one in collection, * to avoid the unnecessary Geometry clone below. */ if ( ngeoms == 1 ) { shp = GEOSGetGeometryN(geos_result, 0); lwg = GEOS2LWGEOM(shp, is3d); lwg->SRID = SRID; result = pglwgeom_serialize(lwg); lwgeom_release(lwg); GEOSGeom_destroy(geos_result); PG_RETURN_POINTER(result); } /* * Iteratively invoke symdifference on outer rings * as suggested by Carl Anderson: * postgis-devel/2005-December/001805.html */ shp = NULL; for (i=0; i 0 if ( result == NULL ) { lwerror("serialization error"); PG_RETURN_NULL(); /*never get here */ } #endif PG_RETURN_POINTER(result); }