2004-04-26 23:05:20 +00:00
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <float.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "access/gist.h"
|
|
|
|
#include "access/itup.h"
|
|
|
|
#include "access/rtree.h"
|
|
|
|
|
|
|
|
#include "fmgr.h"
|
|
|
|
#include "utils/elog.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "lwgeom.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
//#define DEBUG
|
2004-04-26 23:05:20 +00:00
|
|
|
|
|
|
|
#include "wktparse.h"
|
|
|
|
|
2004-08-17 15:27:47 +00:00
|
|
|
Datum combine_box2d(PG_FUNCTION_ARGS);
|
2004-08-24 10:01:16 +00:00
|
|
|
Datum LWGEOM_mem_size(PG_FUNCTION_ARGS);
|
|
|
|
Datum LWGEOM_summary(PG_FUNCTION_ARGS);
|
|
|
|
Datum LWGEOM_npoints(PG_FUNCTION_ARGS);
|
2004-08-25 12:32:12 +00:00
|
|
|
Datum LWGEOM_area_polygon(PG_FUNCTION_ARGS);
|
2004-08-23 15:37:16 +00:00
|
|
|
Datum postgis_uses_stats(PG_FUNCTION_ARGS);
|
|
|
|
Datum postgis_scripts_released(PG_FUNCTION_ARGS);
|
|
|
|
Datum postgis_lib_version(PG_FUNCTION_ARGS);
|
2004-08-25 12:32:12 +00:00
|
|
|
Datum LWGEOM_length2d_linestring(PG_FUNCTION_ARGS);
|
|
|
|
Datum LWGEOM_length3d_linestring(PG_FUNCTION_ARGS);
|
|
|
|
Datum LWGEOM_length_linestring(PG_FUNCTION_ARGS);
|
|
|
|
Datum LWGEOM_perimeter2d_poly(PG_FUNCTION_ARGS);
|
|
|
|
Datum LWGEOM_perimeter_poly(PG_FUNCTION_ARGS);
|
|
|
|
Datum LWGEOM_force_2d(PG_FUNCTION_ARGS);
|
2004-08-25 13:41:43 +00:00
|
|
|
Datum LWGEOM_force_3d(PG_FUNCTION_ARGS);
|
2004-08-24 10:01:16 +00:00
|
|
|
|
|
|
|
// internal
|
2004-08-24 06:59:14 +00:00
|
|
|
char * lwgeom_summary_recursive(char *serialized, int offset);
|
2004-08-24 09:34:33 +00:00
|
|
|
int32 lwgeom_npoints_recursive(char *serialized);
|
2004-08-25 13:41:43 +00:00
|
|
|
|
|
|
|
// general utilities (might be moved in lwgeom_api.c)
|
2004-08-25 12:32:12 +00:00
|
|
|
double lwgeom_polygon_area(LWPOLY *poly);
|
|
|
|
double lwgeom_polygon_perimeter(LWPOLY *poly);
|
|
|
|
double lwgeom_polygon_perimeter2d(LWPOLY *poly);
|
|
|
|
double lwgeom_line_length2d(LWLINE *line);
|
|
|
|
double lwgeom_pointarray_length2d(POINTARRAY *pts);
|
|
|
|
double lwgeom_pointarray_length(POINTARRAY *pts);
|
|
|
|
void lwgeom_force2d_recursive(char *serialized, char *optr, int *retsize);
|
2004-08-25 13:41:43 +00:00
|
|
|
void lwgeom_force3d_recursive(char *serialized, char *optr, int *retsize);
|
2004-08-25 12:32:12 +00:00
|
|
|
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
//find the 2d length of the given POINTARRAY (even if it's 3d)
|
|
|
|
double lwgeom_pointarray_length2d(POINTARRAY *pts)
|
|
|
|
{
|
|
|
|
double dist = 0.0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ( pts->npoints < 2 ) return 0.0;
|
|
|
|
for (i=0; i<pts->npoints-1;i++)
|
|
|
|
{
|
|
|
|
POINT2D *frm = (POINT2D *)getPoint(pts, i);
|
|
|
|
POINT2D *to = (POINT2D *)getPoint(pts, i+1);
|
|
|
|
dist += sqrt( ( (frm->x - to->x)*(frm->x - to->x) ) +
|
|
|
|
((frm->y - to->y)*(frm->y - to->y) ) );
|
|
|
|
}
|
|
|
|
return dist;
|
|
|
|
}
|
|
|
|
|
|
|
|
//find the 3d/2d length of the given POINTARRAY (depending on its dimensions)
|
|
|
|
double lwgeom_pointarray_length(POINTARRAY *pts)
|
|
|
|
{
|
|
|
|
double dist = 0.0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ( pts->npoints < 2 ) return 0.0;
|
|
|
|
|
|
|
|
// compute 2d length if 3d is not available
|
|
|
|
if ( pts->ndims < 3 ) return lwgeom_pointarray_length2d(pts);
|
|
|
|
|
|
|
|
for (i=0; i<pts->npoints-1;i++)
|
|
|
|
{
|
|
|
|
POINT3D *frm = (POINT3D *)getPoint(pts, i);
|
|
|
|
POINT3D *to = (POINT3D *)getPoint(pts, i+1);
|
|
|
|
dist += sqrt( ( (frm->x - to->x)*(frm->x - to->x) ) +
|
|
|
|
((frm->y - to->y)*(frm->y - to->y) ) +
|
|
|
|
((frm->z - to->z)*(frm->z - to->z) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return dist;
|
|
|
|
}
|
|
|
|
|
|
|
|
//find the area of the outer ring - sum (area of inner rings)
|
|
|
|
// Could use a more numerically stable calculator...
|
|
|
|
double lwgeom_polygon_area(LWPOLY *poly)
|
|
|
|
{
|
|
|
|
double poly_area=0.0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
//elog(NOTICE,"in lwgeom_polygon_area (%d rings)", poly->nrings);
|
|
|
|
|
|
|
|
for (i=0; i<poly->nrings; i++)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
POINTARRAY *ring = poly->rings[i];
|
|
|
|
double ringarea = 0.0;
|
|
|
|
|
|
|
|
//elog(NOTICE," rings %d has %d points", i, ring->npoints);
|
|
|
|
for (j=0; j<ring->npoints-1; j++)
|
|
|
|
{
|
|
|
|
POINT2D *p1 = (POINT2D *)getPoint(ring, j);
|
|
|
|
POINT2D *p2 = (POINT2D *)getPoint(ring, j+1);
|
|
|
|
ringarea += ( p1->x * p2->y ) - ( p1->y * p2->x );
|
|
|
|
}
|
|
|
|
|
|
|
|
ringarea /= 2.0;
|
|
|
|
//elog(NOTICE," ring 1 has area %lf",ringarea);
|
|
|
|
ringarea = fabs(ringarea );
|
|
|
|
if (i != 0) //outer
|
|
|
|
ringarea = -1.0*ringarea ; // its a hole
|
|
|
|
|
|
|
|
poly_area += ringarea;
|
|
|
|
}
|
|
|
|
|
|
|
|
return poly_area;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the sum of polygon rings length.
|
|
|
|
// Could use a more numerically stable calculator...
|
|
|
|
double lwgeom_polygon_perimeter(LWPOLY *poly)
|
|
|
|
{
|
|
|
|
double result=0.0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
//elog(NOTICE,"in lwgeom_polygon_perimeter (%d rings)", poly->nrings);
|
|
|
|
|
|
|
|
for (i=0; i<poly->nrings; i++)
|
|
|
|
result += lwgeom_pointarray_length(poly->rings[i]);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the sum of polygon rings length (forcing 2d computation).
|
|
|
|
// Could use a more numerically stable calculator...
|
|
|
|
double lwgeom_polygon_perimeter2d(LWPOLY *poly)
|
|
|
|
{
|
|
|
|
double result=0.0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
//elog(NOTICE,"in lwgeom_polygon_perimeter (%d rings)", poly->nrings);
|
|
|
|
|
|
|
|
for (i=0; i<poly->nrings; i++)
|
|
|
|
result += lwgeom_pointarray_length2d(poly->rings[i]);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2004-08-20 14:08:41 +00:00
|
|
|
|
2004-08-24 10:01:16 +00:00
|
|
|
/*------------------------------------------------------------------*/
|
2004-08-17 15:27:47 +00:00
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(combine_box2d);
|
|
|
|
Datum combine_box2d(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Pointer box2d_ptr = PG_GETARG_POINTER(0);
|
|
|
|
Pointer geom_ptr = PG_GETARG_POINTER(1);
|
|
|
|
BOX2DFLOAT4 *a,*b;
|
|
|
|
char *lwgeom;
|
|
|
|
BOX2DFLOAT4 box, *result;
|
|
|
|
|
|
|
|
if ( (box2d_ptr == NULL) && (geom_ptr == NULL) )
|
|
|
|
{
|
|
|
|
PG_RETURN_NULL(); // combine_box2d(null,null) => null
|
|
|
|
}
|
|
|
|
|
|
|
|
result = (BOX2DFLOAT4 *)palloc(sizeof(BOX2DFLOAT4));
|
|
|
|
|
|
|
|
if (box2d_ptr == NULL)
|
|
|
|
{
|
|
|
|
lwgeom = (char *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
|
|
|
|
|
|
|
box = getbox2d(lwgeom+4);
|
|
|
|
memcpy(result, &box, sizeof(BOX2DFLOAT4));
|
|
|
|
PG_RETURN_POINTER(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
// combine_bbox(BOX3D, null) => BOX3D
|
|
|
|
if (geom_ptr == NULL)
|
|
|
|
{
|
|
|
|
memcpy(result, (char *)PG_GETARG_DATUM(0), sizeof(BOX2DFLOAT4));
|
|
|
|
PG_RETURN_POINTER(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
//combine_bbox(BOX3D, geometry) => union(BOX3D, geometry->bvol)
|
|
|
|
|
|
|
|
lwgeom = (char *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
|
|
|
box = getbox2d(lwgeom+4);
|
|
|
|
|
|
|
|
a = (BOX2DFLOAT4 *)PG_GETARG_DATUM(0);
|
|
|
|
b = &box;
|
|
|
|
|
|
|
|
result->xmax = LWGEOM_Maxf(a->xmax, b->xmax);
|
|
|
|
result->ymax = LWGEOM_Maxf(a->ymax, b->ymax);
|
|
|
|
result->xmin = LWGEOM_Minf(a->xmin, b->xmin);
|
|
|
|
result->ymin = LWGEOM_Minf(a->ymin, b->ymin);
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(result);
|
|
|
|
}
|
2004-08-19 13:56:30 +00:00
|
|
|
|
|
|
|
//find the size of geometry
|
2004-08-24 10:01:16 +00:00
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_mem_size);
|
|
|
|
Datum LWGEOM_mem_size(PG_FUNCTION_ARGS)
|
2004-08-19 13:56:30 +00:00
|
|
|
{
|
2004-08-23 10:49:08 +00:00
|
|
|
LWGEOM *geom = (LWGEOM *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
int32 size = geom->size;
|
|
|
|
int32 computed_size = lwgeom_seralizedformlength_simple(SERIALIZED_FORM(geom));
|
|
|
|
computed_size += 4; // varlena size
|
|
|
|
if ( size != computed_size )
|
|
|
|
{
|
|
|
|
elog(NOTICE, "varlena size (%d) != computed size+4 (%d)",
|
|
|
|
size, computed_size);
|
|
|
|
}
|
|
|
|
|
2004-08-20 09:31:32 +00:00
|
|
|
PG_FREE_IF_COPY(geom,0);
|
|
|
|
PG_RETURN_INT32(size);
|
2004-08-19 13:56:30 +00:00
|
|
|
}
|
2004-08-23 08:32:14 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a palloced string containing summary for the serialized
|
|
|
|
* LWGEOM object
|
|
|
|
*/
|
|
|
|
char *
|
2004-08-23 10:49:08 +00:00
|
|
|
lwgeom_summary_recursive(char *serialized, int offset)
|
2004-08-23 08:32:14 +00:00
|
|
|
{
|
2004-08-23 10:49:08 +00:00
|
|
|
static int idx = 0;
|
2004-08-23 08:32:14 +00:00
|
|
|
LWGEOM_INSPECTED *inspected;
|
|
|
|
char *result;
|
|
|
|
char *ptr;
|
|
|
|
char tmp[100];
|
|
|
|
int size;
|
|
|
|
int32 j,i;
|
|
|
|
|
|
|
|
size = 1;
|
|
|
|
result = palloc(1);
|
|
|
|
result[0] = '\0';
|
|
|
|
|
2004-08-23 10:49:08 +00:00
|
|
|
if ( offset == 0 ) idx = 0;
|
|
|
|
|
2004-08-23 08:32:14 +00:00
|
|
|
inspected = lwgeom_inspect(serialized);
|
2004-08-23 10:49:08 +00:00
|
|
|
|
2004-08-23 08:32:14 +00:00
|
|
|
//now have to do a scan of each object
|
|
|
|
for (j=0; j<inspected->ngeometries; j++)
|
|
|
|
{
|
|
|
|
LWLINE *line=NULL;
|
|
|
|
LWPOINT *point=NULL;
|
|
|
|
LWPOLY *poly=NULL;
|
2004-08-23 10:49:08 +00:00
|
|
|
char *subgeom=NULL;
|
2004-08-23 08:32:14 +00:00
|
|
|
|
|
|
|
point = lwgeom_getpoint_inspected(inspected,j);
|
|
|
|
if (point !=NULL)
|
|
|
|
{
|
|
|
|
size += 30;
|
|
|
|
result = repalloc(result,size);
|
2004-08-23 10:49:08 +00:00
|
|
|
sprintf(tmp,"Object %i is a POINT()\n",
|
|
|
|
idx++);
|
2004-08-23 08:32:14 +00:00
|
|
|
strcat(result,tmp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
poly = lwgeom_getpoly_inspected(inspected, j);
|
|
|
|
if (poly !=NULL)
|
|
|
|
{
|
|
|
|
size += 57*(poly->nrings+1);
|
|
|
|
result = repalloc(result,size);
|
2004-08-23 10:49:08 +00:00
|
|
|
sprintf(tmp,"Object %i is a POLYGON() with %i rings\n",
|
|
|
|
idx++, poly->nrings);
|
2004-08-23 08:32:14 +00:00
|
|
|
strcat(result,tmp);
|
|
|
|
for (i=0; i<poly->nrings;i++)
|
|
|
|
{
|
|
|
|
sprintf(tmp," + ring %i has %i points\n",
|
|
|
|
i, poly->rings[i]->npoints);
|
|
|
|
strcat(result,tmp);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
line = lwgeom_getline_inspected(inspected, j);
|
|
|
|
if (line != NULL)
|
|
|
|
{
|
|
|
|
size += 57;
|
|
|
|
result = repalloc(result,size);
|
|
|
|
sprintf(tmp,
|
|
|
|
"Object %i is a LINESTRING() with %i points\n",
|
2004-08-23 10:49:08 +00:00
|
|
|
idx++, line->points->npoints);
|
2004-08-23 08:32:14 +00:00
|
|
|
strcat(result,tmp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
subgeom = lwgeom_getsubgeometry_inspected(inspected, j);
|
|
|
|
if ( subgeom != NULL )
|
|
|
|
{
|
2004-08-23 10:49:08 +00:00
|
|
|
ptr = lwgeom_summary_recursive(subgeom, 1);
|
2004-08-23 08:32:14 +00:00
|
|
|
size += strlen(ptr);
|
|
|
|
result = repalloc(result,size);
|
|
|
|
strcat(result, ptr);
|
|
|
|
pfree(ptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
elog(ERROR, "What ? lwgeom_getsubgeometry_inspected returned NULL??");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-23 10:49:08 +00:00
|
|
|
pfree_inspected(inspected);
|
2004-08-23 08:32:14 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//get summary info on a GEOMETRY
|
2004-08-24 10:01:16 +00:00
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_summary);
|
|
|
|
Datum LWGEOM_summary(PG_FUNCTION_ARGS)
|
2004-08-23 08:32:14 +00:00
|
|
|
{
|
|
|
|
LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
char *result;
|
|
|
|
text *mytext;
|
|
|
|
|
2004-08-23 10:49:08 +00:00
|
|
|
result = lwgeom_summary_recursive(SERIALIZED_FORM(geom), 0);
|
2004-08-23 08:32:14 +00:00
|
|
|
|
|
|
|
// create a text obj to return
|
|
|
|
mytext = (text *) palloc(VARHDRSZ + strlen(result) );
|
|
|
|
VARATT_SIZEP(mytext) = VARHDRSZ + strlen(result) ;
|
|
|
|
memcpy(VARDATA(mytext) , result, strlen(result) );
|
|
|
|
pfree(result);
|
|
|
|
PG_RETURN_POINTER(mytext);
|
|
|
|
}
|
2004-08-23 15:37:16 +00:00
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(postgis_lib_version);
|
|
|
|
Datum postgis_lib_version(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
char *ver = POSTGIS_LIB_VERSION;
|
|
|
|
text *result;
|
|
|
|
result = (text *) palloc(VARHDRSZ + strlen(ver));
|
|
|
|
VARATT_SIZEP(result) = VARHDRSZ + strlen(ver) ;
|
|
|
|
memcpy(VARDATA(result), ver, strlen(ver));
|
|
|
|
PG_RETURN_POINTER(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(postgis_scripts_released);
|
|
|
|
Datum postgis_scripts_released(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
char *ver = POSTGIS_SCRIPTS_VERSION;
|
|
|
|
text *result;
|
|
|
|
result = (text *) palloc(VARHDRSZ + strlen(ver));
|
|
|
|
VARATT_SIZEP(result) = VARHDRSZ + strlen(ver) ;
|
|
|
|
memcpy(VARDATA(result), ver, strlen(ver));
|
|
|
|
PG_RETURN_POINTER(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(postgis_uses_stats);
|
|
|
|
Datum postgis_uses_stats(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
#ifdef USE_STATS
|
|
|
|
PG_RETURN_BOOL(TRUE);
|
|
|
|
#else
|
|
|
|
PG_RETURN_BOOL(FALSE);
|
|
|
|
#endif
|
|
|
|
}
|
2004-08-24 09:34:33 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Recursively count points in a SERIALIZED lwgeom
|
|
|
|
*/
|
|
|
|
int32
|
|
|
|
lwgeom_npoints_recursive(char *serialized)
|
|
|
|
{
|
|
|
|
LWGEOM_INSPECTED *inspected = lwgeom_inspect(serialized);
|
|
|
|
int i, j;
|
|
|
|
int npoints=0;
|
|
|
|
|
|
|
|
//now have to do a scan of each object
|
|
|
|
for (i=0; i<inspected->ngeometries; i++)
|
|
|
|
{
|
|
|
|
LWLINE *line=NULL;
|
|
|
|
LWPOINT *point=NULL;
|
|
|
|
LWPOLY *poly=NULL;
|
|
|
|
char *subgeom=NULL;
|
|
|
|
|
|
|
|
point = lwgeom_getpoint_inspected(inspected, i);
|
|
|
|
if (point !=NULL)
|
|
|
|
{
|
|
|
|
npoints++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
poly = lwgeom_getpoly_inspected(inspected, i);
|
|
|
|
if (poly !=NULL)
|
|
|
|
{
|
|
|
|
for (j=0; j<poly->nrings; j++)
|
|
|
|
{
|
|
|
|
npoints += poly->rings[j]->npoints;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
line = lwgeom_getline_inspected(inspected, i);
|
|
|
|
if (line != NULL)
|
|
|
|
{
|
|
|
|
npoints += line->points->npoints;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
subgeom = lwgeom_getsubgeometry_inspected(inspected, i);
|
|
|
|
if ( subgeom != NULL )
|
|
|
|
{
|
|
|
|
npoints += lwgeom_npoints_recursive(subgeom);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
elog(ERROR, "What ? lwgeom_getsubgeometry_inspected returned NULL??");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return npoints;
|
|
|
|
}
|
|
|
|
|
|
|
|
//number of points in an object
|
2004-08-24 10:01:16 +00:00
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_npoints);
|
|
|
|
Datum LWGEOM_npoints(PG_FUNCTION_ARGS)
|
2004-08-24 09:34:33 +00:00
|
|
|
{
|
|
|
|
LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
int32 npoints = 0;
|
|
|
|
|
|
|
|
npoints = lwgeom_npoints_recursive(SERIALIZED_FORM(geom));
|
|
|
|
|
|
|
|
PG_RETURN_INT32(npoints);
|
|
|
|
}
|
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
// Calculate the area of all the subobj in a polygon
|
|
|
|
// area(point) = 0
|
|
|
|
// area (line) = 0
|
|
|
|
// area(polygon) = find its 2d area
|
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_area_polygon);
|
|
|
|
Datum LWGEOM_area_polygon(PG_FUNCTION_ARGS)
|
2004-08-25 07:29:32 +00:00
|
|
|
{
|
2004-08-25 12:32:12 +00:00
|
|
|
LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
LWGEOM_INSPECTED *inspected = lwgeom_inspect(SERIALIZED_FORM(geom));
|
|
|
|
LWPOLY *poly;
|
|
|
|
double area = 0.0;
|
2004-08-25 07:29:32 +00:00
|
|
|
int i;
|
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
//elog(NOTICE, "in LWGEOM_area_polygon");
|
2004-08-25 07:29:32 +00:00
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
for (i=0; i<inspected->ngeometries; i++)
|
2004-08-25 07:29:32 +00:00
|
|
|
{
|
2004-08-25 12:32:12 +00:00
|
|
|
poly = lwgeom_getpoly_inspected(inspected, i);
|
|
|
|
if ( poly == NULL ) continue;
|
|
|
|
area += lwgeom_polygon_area(poly);
|
|
|
|
//elog(NOTICE, " LWGEOM_area_polygon found a poly (%f)", area);
|
|
|
|
}
|
|
|
|
|
|
|
|
pfree_inspected(inspected);
|
2004-08-25 07:29:32 +00:00
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
PG_RETURN_FLOAT8(area);
|
|
|
|
}
|
2004-08-25 07:29:32 +00:00
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
//find the "length of a geometry"
|
|
|
|
// length2d(point) = 0
|
|
|
|
// length2d(line) = length of line
|
|
|
|
// length2d(polygon) = 0 -- could make sense to return sum(ring perimeter)
|
|
|
|
// uses euclidian 2d length (even if input is 3d)
|
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_length2d_linestring);
|
|
|
|
Datum LWGEOM_length2d_linestring(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
LWGEOM_INSPECTED *inspected = lwgeom_inspect(SERIALIZED_FORM(geom));
|
|
|
|
LWLINE *line;
|
|
|
|
double dist = 0.0;
|
|
|
|
int i;
|
2004-08-25 07:29:32 +00:00
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
//elog(NOTICE, "in LWGEOM_length2d");
|
|
|
|
|
|
|
|
for (i=0; i<inspected->ngeometries; i++)
|
|
|
|
{
|
|
|
|
line = lwgeom_getline_inspected(inspected, i);
|
|
|
|
if ( line == NULL ) continue;
|
|
|
|
dist += lwgeom_pointarray_length2d(line->points);
|
|
|
|
//elog(NOTICE, " LWGEOM_length2d found a line (%f)", dist);
|
2004-08-25 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
pfree_inspected(inspected);
|
|
|
|
|
|
|
|
PG_RETURN_FLOAT8(dist);
|
2004-08-25 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
//find the "length of a geometry"
|
|
|
|
// length(point) = 0
|
|
|
|
// length(line) = length of line
|
|
|
|
// length(polygon) = 0 -- could make sense to return sum(ring perimeter)
|
|
|
|
// uses euclidian 3d/2d length depending on input dimensions.
|
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_length_linestring);
|
|
|
|
Datum LWGEOM_length_linestring(PG_FUNCTION_ARGS)
|
2004-08-25 07:29:32 +00:00
|
|
|
{
|
|
|
|
LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
LWGEOM_INSPECTED *inspected = lwgeom_inspect(SERIALIZED_FORM(geom));
|
2004-08-25 12:32:12 +00:00
|
|
|
LWLINE *line;
|
|
|
|
double dist = 0.0;
|
2004-08-25 07:29:32 +00:00
|
|
|
int i;
|
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
for (i=0; i<inspected->ngeometries; i++)
|
|
|
|
{
|
|
|
|
line = lwgeom_getline_inspected(inspected, i);
|
|
|
|
if ( line == NULL ) continue;
|
|
|
|
dist += lwgeom_pointarray_length(line->points);
|
|
|
|
}
|
|
|
|
|
|
|
|
pfree_inspected(inspected);
|
|
|
|
|
|
|
|
PG_RETURN_FLOAT8(dist);
|
|
|
|
}
|
|
|
|
|
|
|
|
// find the "perimeter of a geometry"
|
|
|
|
// perimeter(point) = 0
|
|
|
|
// perimeter(line) = 0
|
|
|
|
// perimeter(polygon) = sum of ring perimeters
|
|
|
|
// uses euclidian 3d/2d computation depending on input dimension.
|
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_perimeter_poly);
|
|
|
|
Datum LWGEOM_perimeter_poly(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
LWGEOM_INSPECTED *inspected = lwgeom_inspect(SERIALIZED_FORM(geom));
|
|
|
|
double ret = 0.0;
|
|
|
|
int i;
|
2004-08-25 07:29:32 +00:00
|
|
|
|
|
|
|
for (i=0; i<inspected->ngeometries; i++)
|
|
|
|
{
|
2004-08-25 12:32:12 +00:00
|
|
|
LWPOLY *poly;
|
2004-08-25 07:29:32 +00:00
|
|
|
poly = lwgeom_getpoly_inspected(inspected, i);
|
|
|
|
if ( poly == NULL ) continue;
|
2004-08-25 12:32:12 +00:00
|
|
|
ret += lwgeom_polygon_perimeter(poly);
|
2004-08-25 07:29:32 +00:00
|
|
|
}
|
2004-08-25 12:32:12 +00:00
|
|
|
|
2004-08-25 07:29:32 +00:00
|
|
|
pfree_inspected(inspected);
|
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
PG_RETURN_FLOAT8(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
// find the "perimeter of a geometry"
|
|
|
|
// perimeter(point) = 0
|
|
|
|
// perimeter(line) = 0
|
|
|
|
// perimeter(polygon) = sum of ring perimeters
|
|
|
|
// uses euclidian 2d computation even if input is 3d
|
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_perimeter2d_poly);
|
|
|
|
Datum LWGEOM_perimeter2d_poly(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
LWGEOM_INSPECTED *inspected = lwgeom_inspect(SERIALIZED_FORM(geom));
|
|
|
|
double ret = 0.0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=0; i<inspected->ngeometries; i++)
|
|
|
|
{
|
|
|
|
LWPOLY *poly;
|
|
|
|
poly = lwgeom_getpoly_inspected(inspected, i);
|
|
|
|
if ( poly == NULL ) continue;
|
|
|
|
ret += lwgeom_polygon_perimeter2d(poly);
|
|
|
|
}
|
|
|
|
|
|
|
|
pfree_inspected(inspected);
|
|
|
|
|
|
|
|
PG_RETURN_FLOAT8(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write to already allocated memory 'optr' a 2d version of
|
|
|
|
* the given serialized form.
|
2004-08-25 13:41:43 +00:00
|
|
|
* Higher dimensions in input geometry are discarder.
|
2004-08-25 12:32:12 +00:00
|
|
|
* Return number bytes written in given int pointer.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
lwgeom_force2d_recursive(char *serialized, char *optr, int *retsize)
|
|
|
|
{
|
|
|
|
LWGEOM_INSPECTED *inspected;
|
|
|
|
int i;
|
|
|
|
int totsize=0;
|
|
|
|
int size=0;
|
|
|
|
int type;
|
|
|
|
LWPOINT *point = NULL;
|
|
|
|
LWLINE *line = NULL;
|
|
|
|
LWPOLY *poly = NULL;
|
|
|
|
char *loc;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force2d_recursive: call");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
type = lwgeom_getType(serialized[0]);
|
|
|
|
|
|
|
|
if ( type == POINTTYPE )
|
|
|
|
{
|
|
|
|
point = lwpoint_deserialize(serialized);
|
|
|
|
point->ndims = 2;
|
|
|
|
lwpoint_serialize_buf(point, optr, retsize);
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force2d_recursive: it's a point, size:%d", *retsize);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( type == LINETYPE )
|
|
|
|
{
|
|
|
|
line = lwline_deserialize(serialized);
|
|
|
|
line->ndims = 2;
|
|
|
|
lwline_serialize_buf(line, optr, retsize);
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force2d_recursive: it's a line, size:%d", *retsize);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( type == POLYGONTYPE )
|
|
|
|
{
|
|
|
|
poly = lwpoly_deserialize(serialized);
|
|
|
|
poly->ndims = 2;
|
|
|
|
lwpoly_serialize_buf(poly, optr, retsize);
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force2d_recursive: it's a poly, size:%d", *retsize);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// OK, this is a collection, so we write down its metadata
|
|
|
|
// first and then call us again
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force2d_recursive: it's a collection (type:%d)", type);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Add type
|
|
|
|
*optr = lwgeom_makeType_full(2, lwgeom_hasSRID(serialized[0]),
|
|
|
|
type, lwgeom_hasBBOX(serialized[0]));
|
|
|
|
optr++;
|
|
|
|
totsize++;
|
|
|
|
loc=serialized+1;
|
|
|
|
|
|
|
|
// Add BBOX if any
|
|
|
|
if (lwgeom_hasBBOX(serialized[0]))
|
|
|
|
{
|
|
|
|
memcpy(optr, loc, sizeof(BOX2DFLOAT4));
|
|
|
|
optr += sizeof(BOX2DFLOAT4);
|
|
|
|
totsize += sizeof(BOX2DFLOAT4);
|
|
|
|
loc += sizeof(BOX2DFLOAT4);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add SRID if any
|
|
|
|
if (lwgeom_hasSRID(serialized[0]))
|
|
|
|
{
|
|
|
|
memcpy(optr, loc, 4);
|
|
|
|
optr += 4;
|
|
|
|
totsize += 4;
|
|
|
|
loc += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add numsubobjects
|
|
|
|
memcpy(optr, loc, 4);
|
|
|
|
optr += 4;
|
|
|
|
totsize += 4;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, " collection header size:%d", totsize);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Now recurse for each suboject
|
|
|
|
inspected = lwgeom_inspect(serialized);
|
|
|
|
for (i=0; i<inspected->ngeometries; i++)
|
|
|
|
{
|
|
|
|
char *subgeom = lwgeom_getsubgeometry_inspected(inspected, i);
|
|
|
|
lwgeom_force2d_recursive(subgeom, optr, &size);
|
|
|
|
totsize += size;
|
|
|
|
optr += size;
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, " elem %d size: %d (tot: %d)", i, size, totsize);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
pfree_inspected(inspected);
|
|
|
|
|
|
|
|
*retsize = totsize;
|
|
|
|
}
|
|
|
|
|
2004-08-25 13:41:43 +00:00
|
|
|
/*
|
|
|
|
* Write to already allocated memory 'optr' a 3d version of
|
|
|
|
* the given serialized form.
|
|
|
|
* Higher dimensions in input geometry are discarder.
|
|
|
|
* If the given version is 2d Z is set to 0.
|
|
|
|
* Return number bytes written in given int pointer.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
lwgeom_force3d_recursive(char *serialized, char *optr, int *retsize)
|
|
|
|
{
|
|
|
|
LWGEOM_INSPECTED *inspected;
|
|
|
|
int i,j,k;
|
|
|
|
int totsize=0;
|
|
|
|
int size=0;
|
|
|
|
int type;
|
|
|
|
LWPOINT *point = NULL;
|
|
|
|
LWLINE *line = NULL;
|
|
|
|
LWPOLY *poly = NULL;
|
|
|
|
POINTARRAY newpts;
|
|
|
|
POINTARRAY **nrings;
|
|
|
|
char *loc;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force3d_recursive: call");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
type = lwgeom_getType(serialized[0]);
|
|
|
|
|
|
|
|
if ( type == POINTTYPE )
|
|
|
|
{
|
|
|
|
point = lwpoint_deserialize(serialized);
|
|
|
|
if ( point->ndims < 3 )
|
|
|
|
{
|
|
|
|
newpts.ndims = 3;
|
|
|
|
newpts.npoints = 1;
|
|
|
|
newpts.serialized_pointlist = palloc(sizeof(POINT3D));
|
|
|
|
loc = newpts.serialized_pointlist;
|
|
|
|
getPoint3d_p(point->point, 0, loc);
|
|
|
|
point->point = &newpts;
|
|
|
|
}
|
|
|
|
point->ndims = 3;
|
|
|
|
lwpoint_serialize_buf(point, optr, retsize);
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force3d_recursive: it's a point, size:%d", *retsize);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( type == LINETYPE )
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force3d_recursive: it's a line");
|
|
|
|
#endif
|
|
|
|
line = lwline_deserialize(serialized);
|
|
|
|
if ( line->ndims < 3 )
|
|
|
|
{
|
|
|
|
newpts.ndims = 3;
|
|
|
|
newpts.npoints = line->points->npoints;
|
|
|
|
newpts.serialized_pointlist = palloc(24*line->points->npoints);
|
|
|
|
loc = newpts.serialized_pointlist;
|
|
|
|
for (j=0; j<line->points->npoints; j++)
|
|
|
|
{
|
|
|
|
getPoint3d_p(line->points, j, loc);
|
|
|
|
loc+=24;
|
|
|
|
}
|
|
|
|
line->points = &newpts;
|
|
|
|
}
|
|
|
|
|
|
|
|
line->ndims = 3;
|
|
|
|
lwline_serialize_buf(line, optr, retsize);
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force3d_recursive: it's a line, size:%d", *retsize);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( type == POLYGONTYPE )
|
|
|
|
{
|
|
|
|
poly = lwpoly_deserialize(serialized);
|
|
|
|
if ( poly->ndims < 3 )
|
|
|
|
{
|
|
|
|
newpts.ndims = 3;
|
|
|
|
newpts.npoints = 0;
|
|
|
|
newpts.serialized_pointlist = palloc(1);
|
|
|
|
nrings = palloc(sizeof(POINTARRAY *)*poly->nrings);
|
|
|
|
loc = newpts.serialized_pointlist;
|
|
|
|
for (j=0; j<poly->nrings; j++)
|
|
|
|
{
|
|
|
|
POINTARRAY *ring = poly->rings[j];
|
|
|
|
POINTARRAY *nring = palloc(sizeof(POINTARRAY));
|
|
|
|
nring->ndims = 2;
|
|
|
|
nring->npoints = ring->npoints;
|
|
|
|
nring->serialized_pointlist =
|
|
|
|
palloc(ring->npoints*24);
|
|
|
|
loc = nring->serialized_pointlist;
|
|
|
|
for (k=0; k<ring->npoints; k++)
|
|
|
|
{
|
|
|
|
getPoint3d_p(ring, k, loc);
|
|
|
|
loc+=24;
|
|
|
|
}
|
|
|
|
nrings[j] = nring;
|
|
|
|
}
|
|
|
|
poly->rings = nrings;
|
|
|
|
}
|
|
|
|
poly->ndims = 3;
|
|
|
|
lwpoly_serialize_buf(poly, optr, retsize);
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force3d_recursive: it's a poly, size:%d", *retsize);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// OK, this is a collection, so we write down its metadata
|
|
|
|
// first and then call us again
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, "lwgeom_force3d_recursive: it's a collection (type:%d)", type);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Add type
|
|
|
|
*optr = lwgeom_makeType_full(3, lwgeom_hasSRID(serialized[0]),
|
|
|
|
type, lwgeom_hasBBOX(serialized[0]));
|
|
|
|
optr++;
|
|
|
|
totsize++;
|
|
|
|
loc=serialized+1;
|
|
|
|
|
|
|
|
// Add BBOX if any
|
|
|
|
if (lwgeom_hasBBOX(serialized[0]))
|
|
|
|
{
|
|
|
|
memcpy(optr, loc, sizeof(BOX2DFLOAT4));
|
|
|
|
optr += sizeof(BOX2DFLOAT4);
|
|
|
|
totsize += sizeof(BOX2DFLOAT4);
|
|
|
|
loc += sizeof(BOX2DFLOAT4);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add SRID if any
|
|
|
|
if (lwgeom_hasSRID(serialized[0]))
|
|
|
|
{
|
|
|
|
memcpy(optr, loc, 4);
|
|
|
|
optr += 4;
|
|
|
|
totsize += 4;
|
|
|
|
loc += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add numsubobjects
|
|
|
|
memcpy(optr, loc, 4);
|
|
|
|
optr += 4;
|
|
|
|
totsize += 4;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, " collection header size:%d", totsize);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Now recurse for each suboject
|
|
|
|
inspected = lwgeom_inspect(serialized);
|
|
|
|
for (i=0; i<inspected->ngeometries; i++)
|
|
|
|
{
|
|
|
|
char *subgeom = lwgeom_getsubgeometry_inspected(inspected, i);
|
|
|
|
lwgeom_force3d_recursive(subgeom, optr, &size);
|
|
|
|
totsize += size;
|
|
|
|
optr += size;
|
|
|
|
#ifdef DEBUG
|
|
|
|
elog(NOTICE, " elem %d size: %d (tot: %d)", i, size, totsize);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
pfree_inspected(inspected);
|
|
|
|
|
|
|
|
*retsize = totsize;
|
|
|
|
}
|
|
|
|
|
2004-08-25 12:32:12 +00:00
|
|
|
// transform input geometry to 2d if not 2d already
|
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_force_2d);
|
|
|
|
Datum LWGEOM_force_2d(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
LWGEOM *result;
|
|
|
|
int32 size = 0;
|
|
|
|
|
|
|
|
// already 2d
|
|
|
|
if ( lwgeom_ndims(geom->type) == 2 ) PG_RETURN_POINTER(geom);
|
|
|
|
|
|
|
|
// allocate a larger for safety and simplicity
|
|
|
|
result = (LWGEOM *) palloc(geom->size);
|
|
|
|
|
|
|
|
lwgeom_force2d_recursive(SERIALIZED_FORM(geom),
|
|
|
|
SERIALIZED_FORM(result), &size);
|
|
|
|
|
|
|
|
// we can safely avoid this... memory will be freed at
|
|
|
|
// end of query processing anyway.
|
|
|
|
//result = repalloc(result, size+4);
|
|
|
|
|
|
|
|
result->size = size+4;
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(result);
|
2004-08-25 07:29:32 +00:00
|
|
|
}
|
2004-08-25 13:41:43 +00:00
|
|
|
|
|
|
|
// transform input geometry to 3d if not 2d already
|
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_force_3d);
|
|
|
|
Datum LWGEOM_force_3d(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
LWGEOM *geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
LWGEOM *result;
|
|
|
|
int olddims;
|
|
|
|
int32 size = 0;
|
|
|
|
|
|
|
|
olddims = lwgeom_ndims(geom->type);
|
|
|
|
|
|
|
|
// already 3d
|
|
|
|
if ( olddims == 3 ) PG_RETURN_POINTER(geom);
|
|
|
|
|
|
|
|
if ( olddims > 3 ) {
|
|
|
|
result = (LWGEOM *) palloc(geom->size);
|
|
|
|
} else {
|
|
|
|
// allocate double as memory a larger for safety
|
|
|
|
result = (LWGEOM *) palloc(geom->size*1.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
lwgeom_force3d_recursive(SERIALIZED_FORM(geom),
|
|
|
|
SERIALIZED_FORM(result), &size);
|
|
|
|
|
|
|
|
// we can safely avoid this... memory will be freed at
|
|
|
|
// end of query processing anyway.
|
|
|
|
//result = repalloc(result, size+4);
|
|
|
|
|
|
|
|
result->size = size+4;
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(result);
|
|
|
|
}
|