extern char *lwgeom_serialize(LWGEOM *geom);
extern void lwcollection_serialize_buf(LWCOLLECTION *mcoll, char *buf, int *size);
+//-----------------------------------------------------
+
+// Deserialize an lwgeom serialized form.
+// The deserialized (recursive) structure will store
+// pointers to the serialized form (POINTARRAYs).
LWGEOM *lwgeom_deserialize(char *serializedform);
+
+// Release memory associated with LWGEOM.
+// POINTARRAYs are not released as they are usually
+// pointers to user-managed memory.
+void lwgeom_release(LWGEOM *lwgeom);
+
LWMPOINT *lwmpoint_deserialize(char *serializedform);
LWMLINE *lwmline_deserialize(char *serializedform);
LWMPOLY *lwmpoly_deserialize(char *serializedform);
extern int lwline_compute_bbox_p(LWLINE *line, BOX2DFLOAT4 *box);
extern int lwpoly_compute_bbox_p(LWPOLY *poly, BOX2DFLOAT4 *box);
extern int lwcollection_compute_bbox_p(LWCOLLECTION *col, BOX2DFLOAT4 *box);
+extern BOX2DFLOAT4 *lwgeom_compute_bbox(LWGEOM *lwgeom);
+int lwgeom_compute_bbox_p(LWGEOM *lwgeom, BOX2DFLOAT4 *buf);
// return alloced memory
extern BOX2DFLOAT4 *box2d_union(BOX2DFLOAT4 *b1, BOX2DFLOAT4 *b2);
// args may overlap !
extern int box2d_union_p(BOX2DFLOAT4 *b1, BOX2DFLOAT4 *b2, BOX2DFLOAT4 *ubox);
extern int lwgeom_compute_bbox_p(LWGEOM *lwgeom, BOX2DFLOAT4 *box);
+// Add 'what' to 'to' at position 'where'.
+// where=0 == prepend
+// where=-1 == append
+// Mix of dimensions is not allowed (TODO: allow it?).
+// Returns a newly allocated LWGEOM
+extern LWGEOM *lwgeom_add(const LWGEOM *to, uint32 where, const LWGEOM *what);
+
+LWGEOM *lwpoint_add(const LWPOINT *to, uint32 where, const LWGEOM *what);
+LWGEOM *lwline_add(const LWLINE *to, uint32 where, const LWGEOM *what);
+LWGEOM *lwpoly_add(const LWPOLY *to, uint32 where, const LWGEOM *what);
+LWGEOM *lwmpoly_add(const LWMPOLY *to, uint32 where, const LWGEOM *what);
+LWGEOM *lwmline_add(const LWMLINE *to, uint32 where, const LWGEOM *what);
+LWGEOM *lwmpoint_add(const LWMPOINT *to, uint32 where, const LWGEOM *what);
+LWGEOM *lwcollection_add(const LWCOLLECTION *to, uint32 where, const LWGEOM *what);
+
+// Clone an LWGEOM (pointarray are not copied)
+extern LWGEOM *lwgeom_clone(const LWGEOM *lwgeom);
+extern LWPOINT *lwpoint_clone(const LWPOINT *lwgeom);
+extern LWLINE *lwline_clone(const LWLINE *lwgeom);
+extern LWPOLY *lwpoly_clone(const LWPOLY *lwgeom);
+extern LWCOLLECTION *lwcollection_clone(const LWCOLLECTION *lwgeom);
+
+extern LWCOLLECTION *lwcollection_construct(int type, int ndims, uint32 SRID, char hasbbox, int ngeoms, LWGEOM **geoms);
+
+
#endif // !defined _LIBLWGEOM_H
}
return 1;
}
+
+LWCOLLECTION *
+lwcollection_construct(int type, int ndims, uint32 SRID, char hasbbox,
+ int ngeoms, LWGEOM **geoms)
+{
+ LWCOLLECTION *ret;
+ ret = lwalloc(sizeof(LWCOLLECTION));
+ ret->type = type;
+ ret->ndims = ndims;
+ ret->SRID = SRID;
+ ret->hasbbox = hasbbox;
+ ret->ngeoms = ngeoms;
+ ret->geoms = geoms;
+ return ret;
+}
+
+// Clone LWCOLLECTION object. POINTARRAY are not copied.
+LWCOLLECTION *
+lwcollection_clone(const LWCOLLECTION *g)
+{
+ uint32 i;
+ LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION));
+ memcpy(ret, g, sizeof(LWCOLLECTION));
+ for (i=0; i<g->ngeoms; i++)
+ {
+ ret->geoms[i] = lwgeom_clone(g->geoms[i]);
+ }
+ return ret;
+}
+
+// Add 'what' to this collection at position 'where'.
+// where=0 == prepend
+// where=-1 == append
+// Returns a GEOMETRYCOLLECTION
+LWGEOM *
+lwcollection_add(const LWCOLLECTION *to, uint32 where, const LWGEOM *what)
+{
+ LWCOLLECTION *col;
+ LWGEOM **geoms;
+ uint32 i;
+
+ if ( where == -1 ) where = to->ngeoms;
+ else if ( where < -1 || where > to->ngeoms )
+ {
+ lwerror("lwcollection_add: add position out of range %d..%d",
+ -1, to->ngeoms);
+ return NULL;
+ }
+
+ // dimensions compatibility are checked by caller
+
+ // Construct geoms array
+ geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
+ for (i=0; i<where; i++)
+ {
+ geoms[i] = lwgeom_clone(to->geoms[i]);
+ }
+ geoms[where] = lwgeom_clone(what);
+ for (i=where; i<to->ngeoms; i++)
+ {
+ geoms[i+1] = lwgeom_clone(to->geoms[i]);
+ }
+
+ col = lwcollection_construct(COLLECTIONTYPE, to->ndims, to->SRID,
+ (what->hasbbox || to->hasbbox ), to->ngeoms+1, geoms);
+
+ return (LWGEOM *)col;
+
+}
}
//dont forget to lwfree() result
+
BOX2DFLOAT4 *
lwgeom_compute_bbox(LWGEOM *lwgeom)
{
LWGEOM *lwpoly_as_lwgeom(LWPOLY *obj) { return (LWGEOM *)obj; }
LWGEOM *lwline_as_lwgeom(LWLINE *obj) { return (LWGEOM *)obj; }
LWGEOM *lwpoint_as_lwgeom(LWPOINT *obj) { return (LWGEOM *)obj; }
+
+void
+lwgeom_release(LWGEOM *lwgeom)
+{
+ uint32 i;
+ LWCOLLECTION *col;
+
+ // Collection
+ if ( (col=lwgeom_as_lwcollection(lwgeom)) )
+ {
+ for (i=0; i<col->ngeoms; i++)
+ {
+ lwgeom_release(col->geoms[i]);
+ }
+ lwfree(lwgeom);
+ }
+
+ // Single element
+ else lwfree(lwgeom);
+
+}
+
+// Clone an LWGEOM object. POINTARRAY are not copied.
+LWGEOM *
+lwgeom_clone(const LWGEOM *lwgeom)
+{
+ switch(lwgeom->type)
+ {
+ case POINTTYPE:
+ return (LWGEOM *)lwpoint_clone((LWPOINT *)lwgeom);
+ case LINETYPE:
+ return (LWGEOM *)lwline_clone((LWLINE *)lwgeom);
+ case POLYGONTYPE:
+ return (LWGEOM *)lwpoly_clone((LWPOLY *)lwgeom);
+ case MULTIPOINTTYPE:
+ case MULTILINETYPE:
+ case MULTIPOLYGONTYPE:
+ case COLLECTIONTYPE:
+ return (LWGEOM *)lwcollection_clone((LWCOLLECTION *)lwgeom);
+ default:
+ return NULL;
+ }
+}
+
+// Add 'what' to 'to' at position 'where'.
+// where=0 == prepend
+// where=-1 == append
+// Appended-to LWGEOM gets a new type based on new condition.
+// Mix of dimensions is not allowed (TODO: allow it?).
+LWGEOM *
+lwgeom_add(const LWGEOM *to, uint32 where, const LWGEOM *what)
+{
+ if ( what->ndims != to->ndims )
+ {
+ lwerror("lwgeom_add: mixed dimensions not supported");
+ return NULL;
+ }
+
+ switch(to->type)
+ {
+ case POINTTYPE:
+ return (LWGEOM *)lwpoint_add((const LWPOINT *)to, where, what);
+ case LINETYPE:
+ return (LWGEOM *)lwline_add((const LWLINE *)to, where, what);
+ case POLYGONTYPE:
+ return (LWGEOM *)lwpoly_add((const LWPOLY *)to, where, what);
+
+ case MULTIPOINTTYPE:
+ case MULTILINETYPE:
+ case MULTIPOLYGONTYPE:
+ return (LWGEOM *)lwcollection_add((const LWCOLLECTION *)to, where, what);
+ default:
+ return NULL;
+ }
+}
{
int size;
+ if ( pa == NULL ) {
+ lwerror("getPoint got NULL pointarray");
+ return NULL;
+ }
+
if ( (n<0) || (n>=pa->npoints))
{
return NULL; //error
#include "utils/builtins.h"
#include "fmgr.h"
-#include "liblwgeom.h"
#include "lwgeom_pg.h"
+#include "liblwgeom.h"
#include "profile.h"
#include "wktparse.h"
* during postgis->geos and geos->postgis conversions
*/
#undef DEBUG_CONVERTER
-#undef DEBUG_POSTGIS2GEOS
-#undef DEBUG_GEOS2POSTGIS
+#undef DEBUG_POSTGIS2GEOS
+#undef DEBUG_GEOS2POSTGIS
typedef struct Geometry Geometry;
//-----=GEOS2POSTGIS=
+
/*
* Recursively add a Geometry to the LWGEOM_EXPLODED structure
* The exploded struct contains note about the number of dimensions
//-----=POSTGIS2GEOS=
+Geometry *LWGEOM2GEOS(LWGEOM *lwgeom);
+
+Geometry *
+LWGEOM2GEOS(LWGEOM *lwgeom)
+{
+ uint32 i;
+ Geometry **collected;
+ LWCOLLECTION *col;
+ if ( ! lwgeom ) return NULL;
+
+#ifdef DEBUG_POSTGIS2GEOS
+ elog(NOTICE, "LWGEOM2GEOS: got lwgeom[%p]", lwgeom);
+#endif
+
+ switch (lwgeom->type)
+ {
+ LWPOINT *p;
+
+ case POINTTYPE:
+ p = (LWPOINT *)lwgeom;
+ elog(NOTICE, "LWGEOM2GEOS: lwpoint[%p]", p);
+ return PostGIS2GEOS_point(p);
+
+ case LINETYPE:
+ return PostGIS2GEOS_linestring((LWLINE *)lwgeom);
+
+ case POLYGONTYPE:
+ return PostGIS2GEOS_polygon((LWPOLY *)lwgeom);
+
+ case MULTIPOINTTYPE:
+ case MULTILINETYPE:
+ case MULTIPOLYGONTYPE:
+ case COLLECTIONTYPE:
+ col = (LWCOLLECTION *)lwgeom;
+ collected = (Geometry **)lwalloc(sizeof(Geometry *)*col->ngeoms);
+ for (i=0; i<col->ngeoms; i++)
+ {
+ collected[i] = LWGEOM2GEOS(col->geoms[i]);
+ }
+ return PostGIS2GEOS_collection(collected, col->ngeoms,
+ col->SRID, col->ndims > 2);
+
+ default:
+ lwerror("Unknown geometry type: %d", lwgeom->type);
+ return NULL;
+ }
+
+}
+
Geometry *
POSTGIS2GEOS(PG_LWGEOM *geom)
+{
+ Geometry *ret;
+ LWGEOM *lwgeom = lwgeom_deserialize(SERIALIZED_FORM(geom));
+ ret = LWGEOM2GEOS(lwgeom);
+ lwgeom_release(lwgeom);
+ return ret;
+}
+
+Geometry *
+_POSTGIS2GEOS(PG_LWGEOM *geom)
{
LWPOINT *point;
LWLINE *line;
#include "geos/geom.h"
#include "geos/util.h"
+#define DEBUG_POSTGIS2GEOS 1
+
using namespace geos;
//WARNING THIS *MUST* BE SET CORRECTLY.
typedef struct
{
- char ndims;
- int SRID;
- POINTARRAY *point;
+ int type; // POINTTYPE
+ char ndims;
+ uint32 SRID;
+ char hasbbox;
+ POINTARRAY *point; // hide 2d/3d (this will be an array of 1 point)
} LWPOINT; // "light-weight point"
+// LINETYPE
typedef struct
{
- char ndims;
- int SRID;
- POINTARRAY *points;
-} LWLINE;
-
+ int type; // LINETYPE
+ char ndims;
+ uint32 SRID;
+ char hasbbox;
+ POINTARRAY *points; // array of POINT3D
+} LWLINE; //"light-weight line"
+
+// POLYGONTYPE
typedef struct
{
- int32 SRID;
- char ndims;
+ int type; // POLYGONTYPE
+ char ndims;
+ uint32 SRID;
+ char hasbbox;
int nrings;
- POINTARRAY **rings;
-} LWPOLY;
+ POINTARRAY **rings; // list of rings (list of points)
+} LWPOLY; // "light-weight polygon"
extern "C" char *getPoint(POINTARRAY *pa, int n);
Geometry *PostGIS2GEOS_point(const LWPOINT *lwpoint)
{
- POINT3D *point = (POINT3D *)getPoint(lwpoint->point, 0);
- int SRID = lwpoint->SRID;
- bool is3d = lwpoint->point->ndims > 2 ? 1 : 0;
+ POINT3D *point;
+ int SRID;
+ bool is3d;
+
+#ifdef DEBUG_POSTGIS2GEOS
+ char buf[256];
+ sprintf(buf, "PostGIS2GEOS_point got lwpoint[%p]", lwpoint);
+ NOTICE_MESSAGE(buf);
+#endif
+
+ if ( lwpoint == NULL )
+ {
+ NOTICE_MESSAGE("PostGIS2GEOS_point got a NULL lwpoint");
+ return NULL;
+ }
+
+ point = (POINT3D *)getPoint(lwpoint->point, 0);
+ SRID = lwpoint->SRID;
+ is3d = lwpoint->point->ndims > 2 ? 1 : 0;
+
try
{
Coordinate *c;
hasSRID = (line->SRID != -1);
- if (hasSRID) size +=4; //4 byte SRID
- if (line->hasbbox) size += sizeof(BOX2DFLOAT4); // bvol
- size += ptsize * line->points->npoints;
-
- size+=4; // npoints
-
buf[0] = (unsigned char) lwgeom_makeType_full(line->ndims,
hasSRID, LINETYPE, line->hasbbox);
loc = buf+1;
{
lwgeom_compute_bbox_p((LWGEOM *)line, (BOX2DFLOAT4 *)loc);
loc += sizeof(BOX2DFLOAT4);
+ size += sizeof(BOX2DFLOAT4); // bvol
}
if (hasSRID)
{
memcpy(loc, &line->SRID, sizeof(int32));
+ size +=4; //4 byte SRID
loc += 4;
}
memcpy(loc, &line->points->npoints, sizeof(int32));
loc +=4;
+ size+=4; // npoints
+
//copy in points
//lwnotice(" line serialize - size = %i", size);
loc += 32; // size of a 2d point
}
}
+ size += ptsize * line->points->npoints;
+
//printBYTES((unsigned char *)result, size);
if (retsize) *retsize = size;
if ( line->hasbbox ) size += sizeof(BOX2DFLOAT4);
size += sizeof(double)*line->ndims*line->points->npoints; // points
+ size += 4; // npoints
return size;
}
{
return ptarray_compute_bbox_p(line->points, box);
}
+
+// Clone LWLINE object. POINTARRAY is not copied.
+LWLINE *
+lwline_clone(const LWLINE *g)
+{
+ LWLINE *ret = lwalloc(sizeof(LWLINE));
+ memcpy(ret, g, sizeof(LWLINE));
+ return ret;
+}
+
+// Add 'what' to this line at position 'where'.
+// where=0 == prepend
+// where=-1 == append
+// Returns a MULTILINE or a GEOMETRYCOLLECTION
+LWGEOM *
+lwline_add(const LWLINE *to, uint32 where, const LWGEOM *what)
+{
+ LWCOLLECTION *col;
+ LWGEOM **geoms;
+ int newtype;
+
+ if ( where != -1 && where != 0 )
+ {
+ lwerror("lwline_add only supports 0 or -1 as second argument, got %d", where);
+ return NULL;
+ }
+
+ // dimensions compatibility are checked by caller
+
+ // Construct geoms array
+ geoms = lwalloc(sizeof(LWGEOM *)*2);
+ if ( where == -1 ) // append
+ {
+ geoms[0] = lwgeom_clone((LWGEOM *)to);
+ geoms[1] = lwgeom_clone(what);
+ }
+ else // prepend
+ {
+ geoms[0] = lwgeom_clone(what);
+ geoms[1] = lwgeom_clone((LWGEOM *)to);
+ }
+ // reset SRID and wantbbox flag from component types
+ geoms[0]->SRID = geoms[1]->SRID = -1;
+ geoms[0]->hasbbox = geoms[1]->hasbbox = 0;
+
+ // Find appropriate geom type
+ if ( what->type == LINETYPE ) newtype = MULTILINETYPE;
+ else newtype = COLLECTIONTYPE;
+
+ col = lwcollection_construct(newtype, to->ndims, to->SRID,
+ (what->hasbbox || to->hasbbox ), 2, geoms);
+
+ return (LWGEOM *)col;
+}
return result;
}
+// Add 'what' to this multiline at position 'where'.
+// where=0 == prepend
+// where=-1 == append
+// Returns a MULTILINE or a COLLECTION
+LWGEOM *
+lwmline_add(const LWMLINE *to, uint32 where, const LWGEOM *what)
+{
+ LWCOLLECTION *col;
+ LWGEOM **geoms;
+ int newtype;
+ uint32 i;
+
+ if ( where == -1 ) where = to->ngeoms;
+ else if ( where < -1 || where > to->ngeoms )
+ {
+ lwerror("lwmline_add: add position out of range %d..%d",
+ -1, to->ngeoms);
+ return NULL;
+ }
+
+ // dimensions compatibility are checked by caller
+
+ // Construct geoms array
+ geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
+ for (i=0; i<where; i++)
+ {
+ geoms[i] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+ }
+ geoms[where] = lwgeom_clone(what);
+ for (i=where; i<to->ngeoms; i++)
+ {
+ geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+ }
+
+ if ( what->type == LINETYPE ) newtype = MULTILINETYPE;
+ else newtype = COLLECTIONTYPE;
+
+ col = lwcollection_construct(newtype, to->ndims, to->SRID,
+ (what->hasbbox || to->hasbbox ), to->ngeoms+1, geoms);
+
+ return (LWGEOM *)col;
+
+}
return result;
}
+// Add 'what' to this multipoint at position 'where'.
+// where=0 == prepend
+// where=-1 == append
+// Returns a MULTIPOINT or a COLLECTION
+LWGEOM *
+lwmpoint_add(const LWMPOINT *to, uint32 where, const LWGEOM *what)
+{
+ LWCOLLECTION *col;
+ LWGEOM **geoms;
+ int newtype;
+ uint32 i;
+
+ if ( where == -1 ) where = to->ngeoms;
+ else if ( where < -1 || where > to->ngeoms )
+ {
+ lwerror("lwmpoint_add: add position out of range %d..%d",
+ -1, to->ngeoms);
+ return NULL;
+ }
+
+ // dimensions compatibility are checked by caller
+
+ // Construct geoms array
+ geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
+ for (i=0; i<where; i++)
+ {
+ geoms[i] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+ }
+ geoms[where] = lwgeom_clone(what);
+ for (i=where; i<to->ngeoms; i++)
+ {
+ geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+ }
+
+ if ( what->type == POINTTYPE ) newtype = MULTIPOINTTYPE;
+ else newtype = COLLECTIONTYPE;
+
+ col = lwcollection_construct(newtype, to->ndims, to->SRID,
+ (what->hasbbox || to->hasbbox ), to->ngeoms+1, geoms);
+
+ return (LWGEOM *)col;
+
+}
return result;
}
+// Add 'what' to this multiline at position 'where'.
+// where=0 == prepend
+// where=-1 == append
+// Returns a MULTIPOLY or a COLLECTION
+LWGEOM *
+lwmpoly_add(const LWMPOLY *to, uint32 where, const LWGEOM *what)
+{
+ LWCOLLECTION *col;
+ LWGEOM **geoms;
+ int newtype;
+ uint32 i;
+
+ if ( where == -1 ) where = to->ngeoms;
+ else if ( where < -1 || where > to->ngeoms )
+ {
+ lwerror("lwmline_add: add position out of range %d..%d",
+ -1, to->ngeoms);
+ return NULL;
+ }
+
+ // dimensions compatibility are checked by caller
+
+ // Construct geoms array
+ geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
+ for (i=0; i<where; i++)
+ {
+ geoms[i] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+ }
+ geoms[where] = lwgeom_clone(what);
+ for (i=where; i<to->ngeoms; i++)
+ {
+ geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
+ }
+
+ if ( what->type == POLYGONTYPE ) newtype = MULTIPOLYGONTYPE;
+ else newtype = COLLECTIONTYPE;
+
+ col = lwcollection_construct(newtype, to->ndims, to->SRID,
+ (what->hasbbox || to->hasbbox ), to->ngeoms+1, geoms);
+
+ return (LWGEOM *)col;
+
+}
{
return ptarray_compute_bbox_p(point->point, box);
}
+
+// Clone LWPOINT object. POINTARRAY is not copied.
+LWPOINT *
+lwpoint_clone(const LWPOINT *g)
+{
+ LWPOINT *ret = lwalloc(sizeof(LWPOINT));
+ memcpy(ret, g, sizeof(LWPOINT));
+ return ret;
+}
+
+// Add 'what' to this point at position 'where'.
+// where=0 == prepend
+// where=-1 == append
+// Returns a MULTIPOINT or a GEOMETRYCOLLECTION
+LWGEOM *
+lwpoint_add(const LWPOINT *to, uint32 where, const LWGEOM *what)
+{
+ LWCOLLECTION *col;
+ LWGEOM **geoms;
+ int newtype;
+
+ if ( where != -1 && where != 0 )
+ {
+ lwerror("lwpoint_add only supports 0 or -1 as second argument, got %d", where);
+ return NULL;
+ }
+
+ // dimensions compatibility are checked by caller
+
+
+ // Construct geoms array
+ geoms = lwalloc(sizeof(LWGEOM *)*2);
+ if ( where == -1 ) // append
+ {
+ geoms[0] = lwgeom_clone((LWGEOM *)to);
+ geoms[1] = lwgeom_clone(what);
+ }
+ else // prepend
+ {
+ geoms[0] = lwgeom_clone(what);
+ geoms[1] = lwgeom_clone((LWGEOM *)to);
+ }
+ // reset SRID and wantbbox flag from component types
+ geoms[0]->SRID = geoms[1]->SRID = -1;
+ geoms[0]->hasbbox = geoms[1]->hasbbox = 0;
+
+ // Find appropriate geom type
+ if ( what->type == POINTTYPE ) newtype = MULTIPOINTTYPE;
+ else newtype = COLLECTIONTYPE;
+
+ col = lwcollection_construct(newtype, to->ndims, to->SRID,
+ (what->hasbbox || to->hasbbox ), 2, geoms);
+
+ return (LWGEOM *)col;
+}
}
return 1;
}
+
+// Clone LWLINE object. POINTARRAY are not copied, it's ring array is.
+LWPOLY *
+lwpoly_clone(const LWPOLY *g)
+{
+ LWPOLY *ret = lwalloc(sizeof(LWPOLY));
+ memcpy(ret, g, sizeof(LWPOLY));
+ ret->rings = lwalloc(sizeof(POINTARRAY *)*g->nrings);
+ memcpy(ret->rings, g->rings, sizeof(POINTARRAY *)*g->nrings);
+ return ret;
+}
+
+// Add 'what' to this poly at position 'where'.
+// where=0 == prepend
+// where=-1 == append
+// Returns a MULTIPOLYGON or a GEOMETRYCOLLECTION
+LWGEOM *
+lwpoly_add(const LWPOLY *to, uint32 where, const LWGEOM *what)
+{
+ LWCOLLECTION *col;
+ LWGEOM **geoms;
+ int newtype;
+
+ if ( where != -1 && where != 0 )
+ {
+ lwerror("lwpoly_add only supports 0 or -1 as second argument, got %d", where);
+ return NULL;
+ }
+
+ // dimensions compatibility are checked by caller
+
+ // Construct geoms array
+ geoms = lwalloc(sizeof(LWGEOM *)*2);
+ if ( where == -1 ) // append
+ {
+ geoms[0] = lwgeom_clone((LWGEOM *)to);
+ geoms[1] = lwgeom_clone(what);
+ }
+ else // prepend
+ {
+ geoms[0] = lwgeom_clone(what);
+ geoms[1] = lwgeom_clone((LWGEOM *)to);
+ }
+ // reset SRID and wantbbox flag from component types
+ geoms[0]->SRID = geoms[1]->SRID = -1;
+ geoms[0]->hasbbox = geoms[1]->hasbbox = 0;
+
+ // Find appropriate geom type
+ if ( what->type == POLYGONTYPE ) newtype = MULTIPOLYGONTYPE;
+ else newtype = COLLECTIONTYPE;
+
+ col = lwcollection_construct(newtype, to->ndims, to->SRID,
+ (what->hasbbox || to->hasbbox ), 2, geoms);
+
+ return (LWGEOM *)col;
+}