PostGIS  2.2.8dev-r@@SVN_REVISION@@
effectivearea.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  * Copyright 2014 Nicklas Avén
6  *
7  * This is free software; you can redistribute and/or modify it under
8  * the terms of the GNU General Public Licence. See the COPYING file.
9  *
10  **********************************************************************/
11 
12  #include "effectivearea.h"
13 
14 
17 {
18  LWDEBUG(2, "Entered initiate_effectivearea");
19  EFFECTIVE_AREAS *ea;
20  ea=lwalloc(sizeof(EFFECTIVE_AREAS));
21  ea->initial_arealist = lwalloc(inpts->npoints*sizeof(areanode));
22  ea->res_arealist = lwalloc(inpts->npoints*sizeof(double));
23  ea->inpts=inpts;
24  return ea;
25 }
26 
27 
29 {
31  lwfree(ea->res_arealist);
32  lwfree(ea);
33 }
34 
35 
36 static MINHEAP
37 initiate_minheap(int npoints)
38 {
39  MINHEAP tree;
40  tree.key_array = lwalloc(npoints*sizeof(void*));
41  tree.maxSize=npoints;
42  tree.usedSize=0;
43  return tree;
44 }
45 
46 
47 static void
49 {
50  lwfree(tree.key_array);
51 }
52 
53 
58 static double triarea2d(const double *P1, const double *P2, const double *P3)
59 {
60  return fabs(0.5*((P1[0]-P2[0])*(P3[1]-P2[1])-(P1[1]-P2[1])*(P3[0]-P2[0])));
61 }
62 
67 static double triarea3d(const double *P1, const double *P2, const double *P3)
68 {
69  LWDEBUG(2, "Entered triarea3d");
70  double ax,bx,ay,by,az,bz,cx,cy,cz, area;
71 
72  ax=P1[0]-P2[0];
73  bx=P3[0]-P2[0];
74  ay=P1[1]-P2[1];
75  by=P3[1]-P2[1];
76  az=P1[2]-P2[2];
77  bz=P3[2]-P2[2];
78 
79  cx = ay*bz - az*by;
80  cy = az*bx - ax*bz;
81  cz = ax*by - ay*bx;
82 
83  area = fabs(0.5*(sqrt(cx*cx+cy*cy+cz*cz)));
84  return area;
85 }
86 
91 static int cmpfunc (const void * a, const void * b)
92 {
93  double v1 = (*(areanode**)a)->area;
94  double v2 = (*(areanode**)b)->area;
95  /*qsort gives unpredictable results when comaping identical values.
96  If two values is the same we force returning the last point in hte point array.
97  That way we get the same ordering on diffreent machines and pllatforms*/
98  if (v1==v2)
99  return (*(areanode**)a)-(*(areanode**)b);
100  else
101  return (v1 > v2) ? 1 : ((v1 < v2) ? -1 : 0);
102 }
103 
104 
109 static void down(MINHEAP *tree,areanode *arealist,int parent)
110 {
111  LWDEBUG(2, "Entered down");
112  areanode **treearray=tree->key_array;
113  int left=parent*2+1;
114  int right = left +1;
115  void *tmp;
116  int swap=parent;
117  double leftarea=0;
118  double rightarea=0;
119 
120  double parentarea=((areanode*) treearray[parent])->area;
121 
122  if(left<tree->usedSize)
123  {
124  leftarea=((areanode*) treearray[left])->area;
125  if(parentarea>leftarea)
126  swap=left;
127  }
128  if(right<tree->usedSize)
129  {
130  rightarea=((areanode*) treearray[right])->area;
131  if(rightarea<parentarea&&rightarea<leftarea)
132  swap=right;
133  }
134  if(swap>parent)
135  {
136  /*ok, we have to swap something*/
137  tmp=treearray[parent];
138  treearray[parent]=treearray[swap];
139  /*Update reference*/
140  ((areanode*) treearray[parent])->treeindex=parent;
141  treearray[swap]=tmp;
142  /*Update reference*/
143  ((areanode*) treearray[swap])->treeindex=swap;
144  if(swap<tree->usedSize)
145  down(tree,arealist,swap);
146  }
147  return;
148 }
149 
150 
155 static void up(MINHEAP *tree,areanode *arealist,int c)
156 {
157  LWDEBUG(2, "Entered up");
158  void *tmp;
159 
160  areanode **treearray=tree->key_array;
161 
162  int parent=floor((c-1)/2);
163 
164  while(((areanode*) treearray[c])->area<((areanode*) treearray[parent])->area)
165  {
166  /*ok, we have to swap*/
167  tmp=treearray[parent];
168  treearray[parent]=treearray[c];
169  /*Update reference*/
170  ((areanode*) treearray[parent])->treeindex=parent;
171  treearray[c]=tmp;
172  /*Update reference*/
173  ((areanode*) treearray[c])->treeindex=c;
174  c=parent;
175  parent=floor((c-1)/2);
176  }
177  return;
178 }
179 
180 
185 static areanode* minheap_pop(MINHEAP *tree,areanode *arealist )
186 {
187  LWDEBUG(2, "Entered minheap_pop");
188  areanode *res = tree->key_array[0];
189 
190  /*put last value first*/
191  tree->key_array[0]=tree->key_array[(tree->usedSize)-1];
192  ((areanode*) tree->key_array[0])->treeindex=0;
193 
194  tree->usedSize--;
195  down(tree,arealist,0);
196  return res;
197 }
198 
199 
204 static void minheap_update(MINHEAP *tree,areanode *arealist , int idx)
205 {
206  areanode **treearray=tree->key_array;
207  int parent=floor((idx-1)/2);
208 
209  if(((areanode*) treearray[idx])->area<((areanode*) treearray[parent])->area)
210  up(tree,arealist,idx);
211  else
212  down(tree,arealist,idx);
213  return;
214 }
215 
220 static void tune_areas(EFFECTIVE_AREAS *ea, int avoid_collaps, int set_area, double trshld)
221 {
222  LWDEBUG(2, "Entered tune_areas");
223  const double *P1;
224  const double *P2;
225  const double *P3;
226  double area;
227  int go_on=1;
228  double check_order_min_area = 0;
229 
230  int npoints=ea->inpts->npoints;
231  int i;
232  int current, before_current, after_current;
233 
234  MINHEAP tree = initiate_minheap(npoints);
235 
236  int is3d = FLAGS_GET_Z(ea->inpts->flags);
237 
238 
239  /*Add all keys (index in initial_arealist) into minheap array*/
240  for (i=0;i<npoints;i++)
241  {
242  tree.key_array[i]=ea->initial_arealist+i;
243  LWDEBUGF(2, "add nr %d, with area %lf, and %lf",i,ea->initial_arealist[i].area, tree.key_array[i]->area );
244  }
245  tree.usedSize=npoints;
246 
247  /*order the keys by area, small to big*/
248  qsort(tree.key_array, npoints, sizeof(void*), cmpfunc);
249 
250  /*We have to put references to our tree in our point-list*/
251  for (i=0;i<npoints;i++)
252  {
253  ((areanode*) tree.key_array[i])->treeindex=i;
254  LWDEBUGF(4,"Check ordering qsort gives, area=%lf and belong to point %d",((areanode*) tree.key_array[i])->area, tree.key_array[i]-ea->initial_arealist);
255  }
256  /*Ok, now we have a minHeap, just need to keep it*/
257 
258  /*for (i=0;i<npoints-1;i++)*/
259  i=0;
260  while (go_on)
261  {
262  /*Get a reference to the point with the currently smallest effective area*/
263  current=minheap_pop(&tree, ea->initial_arealist)-ea->initial_arealist;
264 
265  /*We have found the smallest area. That is the resulting effective area for the "current" point*/
266  if (i<npoints-avoid_collaps)
267  ea->res_arealist[current]=ea->initial_arealist[current].area;
268  else
269  ea->res_arealist[current]=FLT_MAX;
270 
271  if(ea->res_arealist[current]<check_order_min_area)
272  lwerror("Oh no, this is a bug. For some reason the minHeap returned our points in the wrong order. Please file a ticket in PostGIS ticket system, or send a mial at the mailing list.Returned area = %lf, and last area = %lf",ea->res_arealist[current],check_order_min_area);
273 
274  check_order_min_area=ea->res_arealist[current];
275 
276  /*The found smallest area point is now regarded as elimnated and we have to recalculate the area the adjacent (ignoring earlier elimnated points) points gives*/
277 
278  /*FInd point before and after*/
279  before_current=ea->initial_arealist[current].prev;
280  after_current=ea->initial_arealist[current].next;
281 
282  P2= (double*)getPoint_internal(ea->inpts, before_current);
283  P3= (double*)getPoint_internal(ea->inpts, after_current);
284 
285  /*Check if point before current point is the first in the point array. */
286  if(before_current>0)
287  {
288 
289  P1= (double*)getPoint_internal(ea->inpts, ea->initial_arealist[before_current].prev);
290  if(is3d)
291  area=triarea3d(P1, P2, P3);
292  else
293  area=triarea2d(P1, P2, P3);
294 
295  ea->initial_arealist[before_current].area = FP_MAX(area,ea->res_arealist[current]);
296  minheap_update(&tree, ea->initial_arealist, ea->initial_arealist[before_current].treeindex);
297  }
298  if(after_current<npoints-1)/*Check if point after current point is the last in the point array. */
299  {
300  P1=P2;
301  P2=P3;
302 
303  P3= (double*)getPoint_internal(ea->inpts, ea->initial_arealist[after_current].next);
304 
305 
306  if(is3d)
307  area=triarea3d(P1, P2, P3);
308  else
309  area=triarea2d(P1, P2, P3);
310 
311 
312  ea->initial_arealist[after_current].area = FP_MAX(area,ea->res_arealist[current]);
313  minheap_update(&tree, ea->initial_arealist, ea->initial_arealist[after_current].treeindex);
314  }
315 
316  /*rearrange the nodes so the eliminated point will be ingored on the next run*/
317  ea->initial_arealist[before_current].next = ea->initial_arealist[current].next;
318  ea->initial_arealist[after_current].prev = ea->initial_arealist[current].prev;
319 
320  /*Check if we are finnished*/
321  if((!set_area && ea->res_arealist[current]>trshld) || (ea->initial_arealist[0].next==(npoints-1)))
322  go_on=0;
323 
324  i++;
325  };
326  destroy_minheap(tree);
327  return;
328 }
329 
330 
335 void ptarray_calc_areas(EFFECTIVE_AREAS *ea, int avoid_collaps, int set_area, double trshld)
336 {
337  LWDEBUG(2, "Entered ptarray_calc_areas");
338  int i;
339  int npoints=ea->inpts->npoints;
340  int is3d = FLAGS_GET_Z(ea->inpts->flags);
341  double area;
342 
343  const double *P1;
344  const double *P2;
345  const double *P3;
346 
347  P1 = (double*)getPoint_internal(ea->inpts, 0);
348  P2 = (double*)getPoint_internal(ea->inpts, 1);
349 
350  /*The first and last point shall always have the maximum effective area. We use float max to not make trouble for bbox*/
351  ea->initial_arealist[0].area=ea->initial_arealist[npoints-1].area=FLT_MAX;
352  ea->res_arealist[0]=ea->res_arealist[npoints-1]=FLT_MAX;
353 
354  ea->initial_arealist[0].next=1;
355  ea->initial_arealist[0].prev=0;
356 
357  for (i=1;i<(npoints)-1;i++)
358  {
359  ea->initial_arealist[i].next=i+1;
360  ea->initial_arealist[i].prev=i-1;
361  P3 = (double*)getPoint_internal(ea->inpts, i+1);
362 
363  if(is3d)
364  area=triarea3d(P1, P2, P3);
365  else
366  area=triarea2d(P1, P2, P3);
367 
368  LWDEBUGF(4,"Write area %lf to point %d on address %p",area,i,&(ea->initial_arealist[i].area));
369  ea->initial_arealist[i].area=area;
370  P1=P2;
371  P2=P3;
372 
373  }
374  ea->initial_arealist[npoints-1].next=npoints-1;
375  ea->initial_arealist[npoints-1].prev=npoints-2;
376 
377  for (i=1;i<(npoints)-1;i++)
378  {
379  ea->res_arealist[i]=FLT_MAX;
380  }
381 
382  tune_areas(ea,avoid_collaps,set_area, trshld);
383  return ;
384 }
385 
386 
387 
388 static POINTARRAY * ptarray_set_effective_area(POINTARRAY *inpts,int avoid_collaps,int set_area, double trshld)
389 {
390  LWDEBUG(2, "Entered ptarray_set_effective_area");
391  int p;
392  POINT4D pt;
393  EFFECTIVE_AREAS *ea;
394  POINTARRAY *opts;
395  int set_m;
396  if(set_area)
397  set_m=1;
398  else
399  set_m=FLAGS_GET_M(inpts->flags);
400  ea=initiate_effectivearea(inpts);
401 
402  opts = ptarray_construct_empty(FLAGS_GET_Z(inpts->flags), set_m, inpts->npoints);
403 
404  ptarray_calc_areas(ea,avoid_collaps,set_area,trshld);
405 
406  if(set_area)
407  {
408  /*Only return points with an effective area above the threashold*/
409  for (p=0;p<ea->inpts->npoints;p++)
410  {
411  if(ea->res_arealist[p]>trshld)
412  {
413  pt=getPoint4d(ea->inpts, p);
414  pt.m=ea->res_arealist[p];
415  ptarray_append_point(opts, &pt, LW_TRUE);
416  }
417  }
418  }
419  else
420  {
421  /*Only return points with an effective area above the threashold*/
422  for (p=0;p<ea->inpts->npoints;p++)
423  {
424  if(ea->res_arealist[p]>trshld)
425  {
426  pt=getPoint4d(ea->inpts, p);
427  ptarray_append_point(opts, &pt, LW_TRUE);
428  }
429  }
430  }
432 
433  return opts;
434 
435 }
436 
437 static LWLINE* lwline_set_effective_area(const LWLINE *iline,int set_area, double trshld)
438 {
439  LWDEBUG(2, "Entered lwline_set_effective_area");
440 
441  /* Skip empty case or too small to simplify */
442  if( lwline_is_empty(iline) || iline->points->npoints<3)
443  return lwline_clone(iline);
444 
445  int set_m;
446  if(set_area)
447  set_m=1;
448  else
449  set_m=FLAGS_GET_M(iline->flags);
450 
451  LWLINE *oline = lwline_construct_empty(iline->srid, FLAGS_GET_Z(iline->flags), set_m);
452 
453 
454 
455  oline = lwline_construct(iline->srid, NULL, ptarray_set_effective_area(iline->points,2,set_area,trshld));
456 
457  oline->type = iline->type;
458  return oline;
459 
460 }
461 
462 
463 static LWPOLY* lwpoly_set_effective_area(const LWPOLY *ipoly,int set_area, double trshld)
464 {
465  LWDEBUG(2, "Entered lwpoly_set_effective_area");
466  int i;
467  int set_m;
468  int avoid_collapse=4;
469  if(set_area)
470  set_m=1;
471  else
472  set_m=FLAGS_GET_M(ipoly->flags);
473  LWPOLY *opoly = lwpoly_construct_empty(ipoly->srid, FLAGS_GET_Z(ipoly->flags), set_m);
474 
475  if( lwpoly_is_empty(ipoly) )
476  return opoly; /* should we return NULL instead ? */
477 
478  for (i = 0; i < ipoly->nrings; i++)
479  {
480  POINTARRAY *pa = ptarray_set_effective_area(ipoly->rings[i],avoid_collapse,set_area,trshld);
481  /* Add ring to simplified polygon */
482  if(pa->npoints>=4)
483  {
484  if( lwpoly_add_ring(opoly,pa ) == LW_FAILURE )
485  return NULL;
486  }
487  /*Inner rings we allow to ocollapse and then we remove them*/
488  avoid_collapse=0;
489  }
490 
491 
492  opoly->type = ipoly->type;
493 
494  if( lwpoly_is_empty(opoly) )
495  return NULL;
496 
497  return opoly;
498 
499 }
500 
501 
502 static LWCOLLECTION* lwcollection_set_effective_area(const LWCOLLECTION *igeom,int set_area, double trshld)
503 {
504  LWDEBUG(2, "Entered lwcollection_set_effective_area");
505  int i;
506  int set_m;
507  if(set_area)
508  set_m=1;
509  else
510  set_m=FLAGS_GET_M(igeom->flags);
511  LWCOLLECTION *out = lwcollection_construct_empty(igeom->type, igeom->srid, FLAGS_GET_Z(igeom->flags), set_m);
512 
513  if( lwcollection_is_empty(igeom) )
514  return out; /* should we return NULL instead ? */
515 
516  for( i = 0; i < igeom->ngeoms; i++ )
517  {
518  LWGEOM *ngeom = lwgeom_set_effective_area(igeom->geoms[i],set_area,trshld);
519  if ( ngeom ) out = lwcollection_add_lwgeom(out, ngeom);
520  }
521 
522  return out;
523 }
524 
525 
526 LWGEOM* lwgeom_set_effective_area(const LWGEOM *igeom,int set_area, double trshld)
527 {
528  LWDEBUG(2, "Entered lwgeom_set_effective_area");
529  switch (igeom->type)
530  {
531  case POINTTYPE:
532  case MULTIPOINTTYPE:
533  return lwgeom_clone(igeom);
534  case LINETYPE:
535  return (LWGEOM*)lwline_set_effective_area((LWLINE*)igeom,set_area, trshld);
536  case POLYGONTYPE:
537  return (LWGEOM*)lwpoly_set_effective_area((LWPOLY*)igeom,set_area, trshld);
538  case MULTILINETYPE:
539  case MULTIPOLYGONTYPE:
540  case COLLECTIONTYPE:
541  return (LWGEOM*)lwcollection_set_effective_area((LWCOLLECTION *)igeom,set_area, trshld);
542  default:
543  lwerror("lwgeom_simplify: unsupported geometry type: %s",lwtype_name(igeom->type));
544  }
545  return NULL;
546 }
547 
void destroy_effectivearea(EFFECTIVE_AREAS *ea)
Definition: effectivearea.c:28
#define LINETYPE
Definition: liblwgeom.h:71
uint8_t type
Definition: liblwgeom.h:402
static int cmpfunc(const void *a, const void *b)
We create the minheap by ordering the minheap array by the areas in the areanode structs that the min...
Definition: effectivearea.c:91
static double triarea3d(const double *P1, const double *P2, const double *P3)
Calculate the area of a triangle in 3d space.
Definition: effectivearea.c:67
tuple res
Definition: window.py:78
This structure is placed in an array with one member per point.
Definition: effectivearea.h:24
LWLINE * lwline_construct_empty(int srid, char hasz, char hasm)
Definition: lwline.c:51
double m
Definition: liblwgeom.h:336
static void destroy_minheap(MINHEAP tree)
Definition: effectivearea.c:48
Structure to hold pointarray and it&#39;s arealist.
Definition: effectivearea.h:50
void lwfree(void *mem)
Definition: lwutil.c:214
LWLINE * lwline_clone(const LWLINE *lwgeom)
Definition: lwline.c:89
int npoints
Definition: liblwgeom.h:355
uint8_t type
Definition: liblwgeom.h:487
This structure holds a minheap tree that is used to keep track of what points that has the smallest e...
Definition: effectivearea.h:38
#define POLYGONTYPE
Definition: liblwgeom.h:72
Datum area(PG_FUNCTION_ARGS)
static MINHEAP initiate_minheap(int npoints)
Definition: effectivearea.c:37
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition: ptarray.c:70
#define MULTIPOINTTYPE
Definition: liblwgeom.h:73
double area
Definition: effectivearea.h:26
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:50
int maxSize
Definition: effectivearea.h:40
static void up(MINHEAP *tree, areanode *arealist, int c)
Sift Up.
static double triarea2d(const double *P1, const double *P2, const double *P3)
Calculate the area of a triangle in 2d.
Definition: effectivearea.c:58
void ptarray_calc_areas(EFFECTIVE_AREAS *ea, int avoid_collaps, int set_area, double trshld)
We calculate the effective area for the first time.
static POINTARRAY * ptarray_set_effective_area(POINTARRAY *inpts, int avoid_collaps, int set_area, double trshld)
int32_t srid
Definition: liblwgeom.h:405
const POINTARRAY * inpts
Definition: effectivearea.h:52
LWGEOM * lwgeom_set_effective_area(const LWGEOM *igeom, int set_area, double trshld)
#define LW_FAILURE
Definition: liblwgeom.h:64
static LWPOLY * lwpoly_set_effective_area(const LWPOLY *ipoly, int set_area, double trshld)
EFFECTIVE_AREAS * initiate_effectivearea(const POINTARRAY *inpts)
Definition: effectivearea.c:16
uint8_t flags
Definition: liblwgeom.h:488
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:188
static LWCOLLECTION * lwcollection_set_effective_area(const LWCOLLECTION *igeom, int set_area, double trshld)
POINT4D getPoint4d(const POINTARRAY *pa, int n)
Definition: lwgeom_api.c:216
int lwcollection_is_empty(const LWCOLLECTION *col)
Definition: lwcollection.c:481
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_TRUE, then a duplicate point will not be added.
Definition: ptarray.c:156
uint8_t type
Definition: liblwgeom.h:435
uint8_t flags
Definition: liblwgeom.h:353
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:61
LWLINE * lwline_construct(int srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:29
LWGEOM ** geoms
Definition: liblwgeom.h:493
uint8_t * getPoint_internal(const POINTARRAY *pa, int n)
Definition: ptarray.c:1706
static void minheap_update(MINHEAP *tree, areanode *arealist, int idx)
The member of the minheap at index idx is changed.
POINTARRAY ** rings
Definition: liblwgeom.h:441
int lwpoly_is_empty(const LWPOLY *poly)
Definition: lwpoly.c:332
int nrings
Definition: liblwgeom.h:439
int32_t srid
Definition: liblwgeom.h:490
#define FLAGS_GET_Z(flags)
Macros for manipulating the &#39;flags&#39; byte.
Definition: liblwgeom.h:124
double * res_arealist
Definition: effectivearea.h:54
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
Definition: lwgeom.c:395
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:75
int lwline_is_empty(const LWLINE *line)
Definition: lwline.c:496
int32_t srid
Definition: liblwgeom.h:438
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:70
LWPOLY * lwpoly_construct_empty(int srid, char hasz, char hasm)
Definition: lwpoly.c:66
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:125
int treeindex
Definition: effectivearea.h:27
int usedSize
Definition: effectivearea.h:41
uint8_t type
Definition: liblwgeom.h:380
areanode ** key_array
Definition: effectivearea.h:42
int lwpoly_add_ring(LWPOLY *poly, POINTARRAY *pa)
Add a ring, allocating extra space if necessary.
Definition: lwpoly.c:154
uint8_t flags
Definition: liblwgeom.h:436
static void tune_areas(EFFECTIVE_AREAS *ea, int avoid_collaps, int set_area, double trshld)
To get the effective area, we have to check what area a point results in when all smaller areas are e...
void * lwalloc(size_t size)
Definition: lwutil.c:199
#define MULTILINETYPE
Definition: liblwgeom.h:74
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int srid, char hasz, char hasm)
Definition: lwcollection.c:81
static LWLINE * lwline_set_effective_area(const LWLINE *iline, int set_area, double trshld)
opts
Definition: ovdump.py:44
uint8_t flags
Definition: liblwgeom.h:403
static areanode * minheap_pop(MINHEAP *tree, areanode *arealist)
Get a reference to the point with the smallest effective area from the root of the min heap...
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:55
static void down(MINHEAP *tree, areanode *arealist, int parent)
Sift Down.
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:174
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:74
return(const char *)
Definition: dbfopen.c:1609
#define COLLECTIONTYPE
Definition: liblwgeom.h:76
#define FP_MAX(A, B)
areanode * initial_arealist
Definition: effectivearea.h:53
POINTARRAY * points
Definition: liblwgeom.h:406