[postgis-users] C question, PG_RETURN_POINTER

Nicolas Ribot nicolas.ribot at scot.cnes.fr
Thu Apr 25 06:52:44 PDT 2002


Mr ONO, thank you for your answer,

Here is, attached to this mail, the file containing the c code I use,
and header file, with the 2 functions scot_gpc_read_polygon and
scot_gpc_write_polygon.

Nicolas Ribot.
Hisaji ONO wrote:

> Hello.  Until last February, I've tackled with GPC library with
> shapelib for triangulation of polygons to create VRML files.  I've
> tried to compile you code in Cygwin on NT 4.0 Sp6a, commneting out
> some functions.  Could you show me two your "original" functions
> codes, "scot_gpc_read_polygon" and
> "scot_gpc_write_polygon"?  Regards.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osgeo.org/pipermail/postgis-users/attachments/20020425/81e8d78d/attachment.html>
-------------- next part --------------
/*************************************************************
 First attempt to integrate GPC into postgis, by scot

 *************************************************************/



#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 "utils/elog.h"

#include "scot_gpc.h"


PG_FUNCTION_INFO_V1(test_geo);
Datum test_geo(PG_FUNCTION_ARGS) {
	GEOMETRY		      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
	int32				*offsets1;
	char				*o1;
	int32				type1,j,i;
	POLYGON3D			*poly;
	LINE3D			*line;
	int32				numb_points = 0;
	
	offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;

	//now have to do a scan of each object 

	for (j=0; j< geom1->nobjs; j++)		//for each object in geom1
	{
		o1 = (char *) geom1 +offsets1[j] ;  
		type1=  geom1->objType[j];

		if (type1 == POINTTYPE)	//point
		{
			numb_points ++;
		}

		if (type1 == LINETYPE)	//line
		{
			line = (LINE3D *) o1;
			numb_points += line->npoints;
		}

		if (type1 == POLYGONTYPE)	//POLYGON
		{
			poly = (POLYGON3D *) o1;
			for (i=0; i<poly->nrings;i++)
			{
				numb_points += poly->npoints[i];
			}
		}
	}
	PG_RETURN_INT32(numb_points);
}


/*
 tests 
  very first gpc operation on 2 geometries
  Works only on 2 real POLYGON object,
*/
PG_FUNCTION_INFO_V1(test_geogpc);
Datum test_geogpc(PG_FUNCTION_ARGS) {
	GEOMETRY		    *geom1 =  (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
	GEOMETRY		    *geom2 =  (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
	GEOMETRY		    *result = (GEOMETRY *) palloc(sizeof(GEOMETRY) );
	POLYGON3D           *poly1;
	POLYGON3D           *poly2;
	POLYGON3D           *poly_res;
	int32 			    *offset1;
	int32               *offset2;
	int 				poly_res_size;
	char                *o1;
	char                *o2;
	
	// for debug only
	int toto = 666;
	/* gpc types */
	gpc_polygon gpcsubject, gpcclip, gpcresult;

elog(NOTICE,"Entering test_geogpc. Int: %d", toto);	
	// looks at the input geometries type
	if (geom1->type != POLYGONTYPE || geom2->type != POLYGONTYPE) {
		elog(ERROR,"Incompatible types for 2 geometries (expected POLYGON)");
		PG_RETURN_NULL();
	}
	
elog(NOTICE,"geom type is polygon ");	
	if (geom1->SRID != geom2->SRID)	{
		elog(ERROR,"Operation on two GEOMETRIES with different SRIDs");
		PG_RETURN_NULL();
	}
elog(NOTICE,"SRID's are equals ");	

	// creates the 2 polygons and 2 gpc polygons on which operations will be done
	offset1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs );
	offset2 = (int32 *) ( ((char *) &(geom2->objType[0] ))+ sizeof(int32) * geom2->nobjs );
	
elog(NOTICE,"offsets set, hihi");	

	o1 = (char *) geom1 +offset1[0];
	o2 = (char *) geom2 +offset2[0];
	
	poly1 = (POLYGON3D *)o1;
	poly2 = (POLYGON3D *)o2;
	
elog(NOTICE,"postgis polygon cast");
	
	// creates gpc polygon from postgis polygons:
	scot_gpc_read_polygon(poly1, &gpcsubject);
elog(NOTICE,"read first poly into gpcsubject");	
	scot_gpc_read_polygon(poly2, &gpcclip);
elog(NOTICE,"read first poly into gpcclip");	
	
	// perform the clipping operation (union geom1-geom2 for test)
	gpc_polygon_clip(GPC_UNION, &gpcsubject, &gpcclip, &gpcresult);
elog(NOTICE,"clipping performed by gcp...");	

	//Convert result to opengis polygon:
	poly_res = scot_gpc_write_polygon(&gpcresult, &poly_res_size);
elog(NOTICE,"poly_res written... poly_res_size: %d", poly_res_size);

	result = make_oneobj_geometry(poly_res_size, 
	                              (char *)poly_res, 
	                              POLYGONTYPE, 
	                              FALSE,
	                              geom1->SRID,
	                              geom1->scale, 
	                              geom1->offsetX, 
	                              geom1->offsetY
	                              );
elog(NOTICE,"result geometry made...");
elog(NOTICE,"result size: %d, SRID: %d, type: %d, nobjs: %d", result->size, result->SRID, result->type, result->nobjs);
	
	// free memory for gpc polygons.
	gpc_free_polygon(&gpcsubject);
	gpc_free_polygon(&gpcclip);
	gpc_free_polygon(&gpcresult);
elog(NOTICE,"gpc objects (3 polygons) freed.");	
	
	// free memory for postgis polygons ?
	PG_FREE_IF_COPY(geom1, 0);
	PG_FREE_IF_COPY(geom2, 1);

elog(NOTICE,"before return");	
	PG_RETURN_POINTER(result);
	//PG_RETURN_NULL();
}

/* 
add-on to GPC to allow the construction of a polygon from an array of coordinates instead of file 
method should allow the construction of a GPC polygon from a postgis POLYGON or MULTIPOLYGON
// original method from gpc.c
void gpc_read_polygon(FILE *fp, int read_hole_flags, gpc_polygon *p) {
  int c, v;

  fscanf(fp, "%d", &(p->num_contours));
  MALLOC(p->hole, p->num_contours * sizeof(int),
         "hole flag array creation");
  MALLOC(p->contour, p->num_contours
         * sizeof(gpc_vertex_list), "contour creation");
  for (c= 0; c < p->num_contours; c++)
  {
    fscanf(fp, "%d", &(p->contour[c].num_vertices));

    if (read_hole_flags)
      fscanf(fp, "%d", &(p->hole[c]));
    else
      p->hole[c]= FALSE; 

    MALLOC(p->contour[c].vertex, p->contour[c].num_vertices
           * sizeof(gpc_vertex), "vertex creation");
    for (v= 0; v < p->contour[c].num_vertices; v++)
      fscanf(fp, "%lf %lf", &(p->contour[c].vertex[v].x),
                            &(p->contour[c].vertex[v].y));
  }
}
No control is made on the input polygon3d geometry. Use this function with caution
Convention: when referening to gpc polygons, contour is the ring
for opengis poly, ring is a contour, first one is exterior, all other are holes.
CAUTION: postgis polygons have their last ring point = first one, must deal with this 
when building gpc polygons.
*/
void scot_gpc_read_polygon(POLYGON3D *ppoly, gpc_polygon *p) {
	int c, v;
	// is the current polygon ring a hole ? 
	int isHole;
	// the position of the current point in the array of points for the poly
	int point_offset;
	// the array of points for each ring
	POINT3D	*pts;
	
	// First ring is always exterior for OpenGIS polygon
	isHole = 0;
	// copy the number of rings of the input polygon
	p->num_contours = ppoly->nrings;
elog(NOTICE,"gpc_read_polygon: num rings to convert: %d", ppoly->nrings);	
	
	// allocates rings
	MALLOC(p->hole,    p->num_contours * sizeof(int),             "hole flag array creation");
	MALLOC(p->contour, p->num_contours * sizeof(gpc_vertex_list), "contour creation");
	
	// gets the array of points for all the rings forming the polygon:
	pts = (POINT3D *) ( (char *)&(ppoly->npoints[ppoly->nrings] )  );
	pts = (POINT3D *) MAXALIGN(pts);
	
	point_offset = 0;
	// loop for each ring, num_countours is the number of rings in ppoly
	for (c= 0; c < p->num_contours; c++) {
		// num vertices for this ring: 1 less thant postgis polygon
		p->contour[c].num_vertices = ppoly->npoints[c] - 1;

		// set hole feature of this poly
		p->hole[c] = isHole;
		
		if (c == 0) {
			// now first ring is treated, all other rings are holes
			isHole = 1;
		}
	
		// allocates vertex for this ring
		MALLOC(p->contour[c].vertex, p->contour[c].num_vertices * sizeof(gpc_vertex), "vertex creation");
		
		// copies each vertex coordinates from opengis poly to gpc poly
		for (v= 0; v < p->contour[c].num_vertices; v++) {
			p->contour[c].vertex[v].x = pts[v].x;
			p->contour[c].vertex[v].y = pts[v].y;
elog(NOTICE,"gpc_read_polygon: vertex read from ring %d: %f %f", c, p->contour[c].vertex[v].x, p->contour[c].vertex[v].y);
	  	}
elog(NOTICE,"gpc_read_polygon: read %d vertices for ring %d", v, c);	

		//increments current ring offset: must go forward in the array of points to the next ring's first point,
		// be careful that postgis polygons contain 1 more vertex than gpc polygon do
		point_offset += ppoly->npoints[c];
	}
}

/*
Write function, from GPC code
// original method from gpc.c
void gpc_write_polygon(FILE *fp, int write_hole_flags, gpc_polygon *p)
{
  int c, v;

  fprintf(fp, "%d\n", p->num_contours);
  for (c= 0; c < p->num_contours; c++)
  {
    fprintf(fp, "%d\n", p->contour[c].num_vertices);

    if (write_hole_flags)
      fprintf(fp, "%d\n", p->hole[c]);
    
    for (v= 0; v < p->contour[c].num_vertices; v++)
      fprintf(fp, "% .*lf % .*lf\n",
              DBL_DIG, p->contour[c].vertex[v].x,
              DBL_DIG, p->contour[c].vertex[v].y);
  }
}
Writes an existing gpc polygon into a newly created postgis polygon
 
No control is made on the input gpc polygon geometry. Use this function with caution
Convention: when referening to gpc polygons, contour is the ring
for opengis poly, ring is a contour, first one is exterior, all other are holes.
caution: the number of vertices in each gpc polygon is 1 less than the number in
postgis polygon, as OpenGIS says a ring must have its last point=first point.
Force this point in this code.
*/
POLYGON3D * scot_gpc_write_polygon(gpc_polygon *p, int *ppoly_size) {
	int 			c, v;
	// The array of points: total number of points for all rings
	POINT3D			*pts;
	POLYGON3D       *ppoly; /*the result*/
	int  			num_vertices = 0;
	// array of points per ring	
	int				*pts_per_ring;

elog(NOTICE,"gpc_write_polygon: num rings to write: %d", p->num_contours);	
	// first, creates a new polygon from scratch.
	pts_per_ring = malloc(p->num_contours * sizeof(int));
	 
	for (c = 0; c < p->num_contours; c++) {
		num_vertices += p->contour[c].num_vertices + 1; /* reserve an extra point */
		pts_per_ring[c] = p->contour[c].num_vertices + 1;
	}
	pts = malloc(num_vertices * sizeof(POINT3D));

	//loop for all contours to take their vertices
	// no holes to take into account as postgis will treat them correctly
	// adds the first point at the end to close the postgis ring
	for (c= 0; c < p->num_contours; c++) {
		for (v = 0; v < p->contour[c].num_vertices; v++) {
			set_point( &pts[c+v], p->contour[c].vertex[v].x, p->contour[c].vertex[v].y, 0.0);
elog(NOTICE,"gpc_write_polygon: coord written: %f %f", p->contour[c].vertex[v].x, p->contour[c].vertex[v].y);	
		}
		// adds the first point at the end to close the postgis ring
		set_point( &pts[c+v+1], p->contour[c].vertex[0].x, p->contour[c].vertex[0].y, 0.0);
elog(NOTICE,"gpc_write_polygon: coord written: %f %f", p->contour[c].vertex[0].x, p->contour[c].vertex[0].y);	
elog(NOTICE,"gpc_write_polygon: written %d vertices for ring: %d", v+1, c);	
	}
	//make the polygon
	ppoly = make_polygon(p->num_contours, pts_per_ring, pts, num_vertices, ppoly_size);

// checks the produced postgis  polygon.
elog(NOTICE,"num rings in res: %d", ppoly->nrings);
elog(NOTICE,"num vertices for exterior: %d", ppoly->npoints[0]);
elog(NOTICE,"size of this object %d", size_subobject ((char*)ppoly, POLYGONTYPE));
elog(NOTICE,"pointer value: %d", (int)ppoly);
	return ppoly;
}
-------------- next part --------------
#include "utils/geo_decls.h"
#include "postgis.h"

#include "gpc.h"

// taken from gpc.c to avoid inclusion:
#define MALLOC(p, b, s)    {if ((b) > 0) { \
                            p= malloc(b); if (!(p)) { \
                            fprintf(stderr, "gpc malloc failure: %s\n", s); \
		            exit(0);}} else p= NULL;}


// geo functions
Datum test_geo(PG_FUNCTION_ARGS);
Datum test_geo_2(PG_FUNCTION_ARGS);
Datum test_geogpc(PG_FUNCTION_ARGS);

// transformation functions:
void scot_gpc_read_polygon(POLYGON3D *ppoly, gpc_polygon *p);
//void scot_gpc_write_polygon(gpc_polygon *p, POLYGON3D *ppoly, int *ppoly_size);
POLYGON3D * scot_gpc_write_polygon(gpc_polygon *p, int *ppoly_size);


More information about the postgis-users mailing list