PostGIS  2.1.10dev-r@@SVN_REVISION@@
lwcollection.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * Copyright (C) 2001-2006 Refractions Research Inc.
7  *
8  * This is free software; you can redistribute and/or modify it under
9  * the terms of the GNU General Public Licence. See the COPYING file.
10  *
11  **********************************************************************/
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include "liblwgeom_internal.h"
17 #include "lwgeom_log.h"
18 
19 
20 #define CHECK_LWGEOM_ZM 1
21 
22 void
24 {
26 }
27 
28 
30 lwcollection_construct(uint8_t type, int srid, GBOX *bbox,
31  uint32_t ngeoms, LWGEOM **geoms)
32 {
33  LWCOLLECTION *ret;
34  int hasz, hasm;
35 #ifdef CHECK_LWGEOM_ZM
36  char zm;
37  uint32_t i;
38 #endif
39 
40  LWDEBUGF(2, "lwcollection_construct called with %d, %d, %p, %d, %p.", type, srid, bbox, ngeoms, geoms);
41 
42  if( ! lwtype_is_collection(type) )
43  lwerror("Non-collection type specified in collection constructor!");
44 
45  hasz = 0;
46  hasm = 0;
47  if ( ngeoms > 0 )
48  {
49  hasz = FLAGS_GET_Z(geoms[0]->flags);
50  hasm = FLAGS_GET_M(geoms[0]->flags);
51 #ifdef CHECK_LWGEOM_ZM
52  zm = FLAGS_GET_ZM(geoms[0]->flags);
53 
54  LWDEBUGF(3, "lwcollection_construct type[0]=%d", geoms[0]->type);
55 
56  for (i=1; i<ngeoms; i++)
57  {
58  LWDEBUGF(3, "lwcollection_construct type=[%d]=%d", i, geoms[i]->type);
59 
60  if ( zm != FLAGS_GET_ZM(geoms[i]->flags) )
61  lwerror("lwcollection_construct: mixed dimension geometries: %d/%d", zm, FLAGS_GET_ZM(geoms[i]->flags));
62  }
63 #endif
64  }
65 
66 
67  ret = lwalloc(sizeof(LWCOLLECTION));
68  ret->type = type;
69  ret->flags = gflags(hasz,hasm,0);
70  FLAGS_SET_BBOX(ret->flags, bbox?1:0);
71  ret->srid = srid;
72  ret->ngeoms = ngeoms;
73  ret->maxgeoms = ngeoms;
74  ret->geoms = geoms;
75  ret->bbox = bbox;
76 
77  return ret;
78 }
79 
81 lwcollection_construct_empty(uint8_t type, int srid, char hasz, char hasm)
82 {
83  LWCOLLECTION *ret;
84  if( ! lwtype_is_collection(type) )
85  lwerror("Non-collection type specified in collection constructor!");
86 
87  ret = lwalloc(sizeof(LWCOLLECTION));
88  ret->type = type;
89  ret->flags = gflags(hasz,hasm,0);
90  ret->srid = srid;
91  ret->ngeoms = 0;
92  ret->maxgeoms = 1; /* Allocate room for sub-members, just in case. */
93  ret->geoms = lwalloc(ret->maxgeoms * sizeof(LWGEOM*));
94  ret->bbox = NULL;
95 
96  return ret;
97 }
98 
99 LWGEOM *
101 {
102  return (LWGEOM *)col->geoms[gnum];
103 }
104 
109 LWCOLLECTION *
111 {
112  uint32_t i;
113  LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION));
114  memcpy(ret, g, sizeof(LWCOLLECTION));
115  if ( g->ngeoms > 0 )
116  {
117  ret->geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms);
118  for (i=0; i<g->ngeoms; i++)
119  {
120  ret->geoms[i] = lwgeom_clone(g->geoms[i]);
121  }
122  if ( g->bbox ) ret->bbox = gbox_copy(g->bbox);
123  }
124  else
125  {
126  ret->bbox = NULL; /* empty collection */
127  ret->geoms = NULL;
128  }
129  return ret;
130 }
131 
135 LWCOLLECTION *
137 {
138  uint32_t i;
139  LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION));
140  memcpy(ret, g, sizeof(LWCOLLECTION));
141  if ( g->ngeoms > 0 )
142  {
143  ret->geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms);
144  for (i=0; i<g->ngeoms; i++)
145  {
146  ret->geoms[i] = lwgeom_clone_deep(g->geoms[i]);
147  }
148  if ( g->bbox ) ret->bbox = gbox_copy(g->bbox);
149  }
150  else
151  {
152  ret->bbox = NULL; /* empty collection */
153  ret->geoms = NULL;
154  }
155  return ret;
156 }
157 
161 void lwcollection_reserve(LWCOLLECTION *col, int ngeoms)
162 {
163  if ( ngeoms <= col->maxgeoms ) return;
164 
165  /* Allocate more space if we need it */
166  do { col->maxgeoms *= 2; } while ( col->maxgeoms < ngeoms );
167  col->geoms = lwrealloc(col->geoms, sizeof(LWGEOM*) * col->maxgeoms);
168 }
169 
175 {
176  if ( col == NULL || geom == NULL ) return NULL;
177 
178  if ( col->geoms == NULL && (col->ngeoms || col->maxgeoms) ) {
179  lwerror("Collection is in inconsistent state. Null memory but non-zero collection counts.");
180  return NULL;
181  }
182 
183  /* Check type compatibility */
184  if ( ! lwcollection_allows_subtype(col->type, geom->type) ) {
185  lwerror("%s cannot contain %s element", lwtype_name(col->type), lwtype_name(geom->type));
186  return NULL;
187  }
188 
189  /* In case this is a truly empty, make some initial space */
190  if ( col->geoms == NULL )
191  {
192  col->maxgeoms = 2;
193  col->ngeoms = 0;
194  col->geoms = lwalloc(col->maxgeoms * sizeof(LWGEOM*));
195  }
196 
197  /* Allocate more space if we need it */
198  lwcollection_reserve(col, col->ngeoms + 1);
199 
200 #if PARANOIA_LEVEL > 1
201  /* See http://trac.osgeo.org/postgis/ticket/2933 */
202  /* Make sure we don't already have a reference to this geom */
203  for ( i = 0; i < col->ngeoms; i++ )
204  {
205  if ( col->geoms[i] == geom )
206  {
207  LWDEBUGF(4, "Found duplicate geometry in collection %p == %p", col->geoms[i], geom);
208  return col;
209  }
210  }
211 #endif
212 
213  col->geoms[col->ngeoms] = (LWGEOM*)geom;
214  col->ngeoms++;
215  return col;
216 }
217 
218 
219 LWCOLLECTION *
221 {
222  uint32_t i;
223  LWGEOM **newgeoms;
224 
225  if ( ! col->ngeoms ) return lwcollection_clone(col);
226 
227  newgeoms = lwalloc(sizeof(LWGEOM *)*col->ngeoms);
228  for (i=0; i<col->ngeoms; i++)
229  newgeoms[i] = lwgeom_segmentize2d(col->geoms[i], dist);
230 
231  return lwcollection_construct(col->type, col->srid, NULL, col->ngeoms, newgeoms);
232 }
233 
237 char
239 {
240  uint32_t i;
241 
242  LWDEBUG(2, "lwcollection_same called");
243 
244  if ( c1->type != c2->type ) return LW_FALSE;
245  if ( c1->ngeoms != c2->ngeoms ) return LW_FALSE;
246 
247  for ( i = 0; i < c1->ngeoms; i++ )
248  {
249  if ( ! lwgeom_same(c1->geoms[i], c2->geoms[i]) )
250  return LW_FALSE;
251  }
252 
253  /* Former method allowed out-of-order equality between collections
254 
255  hit = lwalloc(sizeof(uint32_t)*c1->ngeoms);
256  memset(hit, 0, sizeof(uint32_t)*c1->ngeoms);
257 
258  for (i=0; i<c1->ngeoms; i++)
259  {
260  char found=0;
261  for (j=0; j<c2->ngeoms; j++)
262  {
263  if ( hit[j] ) continue;
264  if ( lwgeom_same(c1->geoms[i], c2->geoms[j]) )
265  {
266  hit[j] = 1;
267  found=1;
268  break;
269  }
270  }
271  if ( ! found ) return LW_FALSE;
272  }
273  */
274 
275  return LW_TRUE;
276 }
277 
279 {
280  int i;
281  int ngeoms = 0;
282 
283  if ( ! col )
284  {
285  lwerror("Null input geometry.");
286  return 0;
287  }
288 
289  for ( i = 0; i < col->ngeoms; i++ )
290  {
291  if ( col->geoms[i])
292  {
293  switch (col->geoms[i]->type)
294  {
295  case POINTTYPE:
296  case LINETYPE:
297  case CIRCSTRINGTYPE:
298  case POLYGONTYPE:
299  ngeoms += 1;
300  break;
301  case MULTIPOINTTYPE:
302  case MULTILINETYPE:
303  case MULTICURVETYPE:
304  case MULTIPOLYGONTYPE:
305  ngeoms += col->ngeoms;
306  break;
307  case COLLECTIONTYPE:
308  ngeoms += lwcollection_ngeoms((LWCOLLECTION*)col->geoms[i]);
309  break;
310  }
311  }
312  }
313  return ngeoms;
314 }
315 
317 {
318  int i;
319  if ( ! col ) return;
320 
321  if ( col->bbox )
322  {
323  lwfree(col->bbox);
324  }
325  for ( i = 0; i < col->ngeoms; i++ )
326  {
327  LWDEBUGF(4,"freeing geom[%d]", i);
328  if ( col->geoms && col->geoms[i] )
329  lwgeom_free(col->geoms[i]);
330  }
331  if ( col->geoms )
332  {
333  lwfree(col->geoms);
334  }
335  lwfree(col);
336 }
337 
338 
344 {
345  int i = 0;
346  LWGEOM **geomlist;
347  LWCOLLECTION *outcol;
348  int geomlistsize = 16;
349  int geomlistlen = 0;
350  uint8_t outtype;
351 
352  if ( ! col ) return NULL;
353 
354  switch (type)
355  {
356  case POINTTYPE:
357  outtype = MULTIPOINTTYPE;
358  break;
359  case LINETYPE:
360  outtype = MULTILINETYPE;
361  break;
362  case POLYGONTYPE:
363  outtype = MULTIPOLYGONTYPE;
364  break;
365  default:
366  lwerror("Only POLYGON, LINESTRING and POINT are supported by lwcollection_extract. %s requested.", lwtype_name(type));
367  return NULL;
368  }
369 
370  geomlist = lwalloc(sizeof(LWGEOM*) * geomlistsize);
371 
372  /* Process each sub-geometry */
373  for ( i = 0; i < col->ngeoms; i++ )
374  {
375  int subtype = col->geoms[i]->type;
376  /* Don't bother adding empty sub-geometries */
377  if ( lwgeom_is_empty(col->geoms[i]) )
378  {
379  continue;
380  }
381  /* Copy our sub-types into the output list */
382  if ( subtype == type )
383  {
384  /* We've over-run our buffer, double the memory segment */
385  if ( geomlistlen == geomlistsize )
386  {
387  geomlistsize *= 2;
388  geomlist = lwrealloc(geomlist, sizeof(LWGEOM*) * geomlistsize);
389  }
390  geomlist[geomlistlen] = lwgeom_clone(col->geoms[i]);
391  geomlistlen++;
392  }
393  /* Recurse into sub-collections */
394  if ( lwtype_is_collection( subtype ) )
395  {
396  int j = 0;
397  LWCOLLECTION *tmpcol = lwcollection_extract((LWCOLLECTION*)col->geoms[i], type);
398  for ( j = 0; j < tmpcol->ngeoms; j++ )
399  {
400  /* We've over-run our buffer, double the memory segment */
401  if ( geomlistlen == geomlistsize )
402  {
403  geomlistsize *= 2;
404  geomlist = lwrealloc(geomlist, sizeof(LWGEOM*) * geomlistsize);
405  }
406  geomlist[geomlistlen] = tmpcol->geoms[j];
407  geomlistlen++;
408  }
409  lwfree(tmpcol);
410  }
411  }
412 
413  if ( geomlistlen > 0 )
414  {
415  GBOX gbox;
416  outcol = lwcollection_construct(outtype, col->srid, NULL, geomlistlen, geomlist);
417  lwgeom_calculate_gbox((LWGEOM *) outcol, &gbox);
418  outcol->bbox = gbox_copy(&gbox);
419  }
420  else
421  {
422  lwfree(geomlist);
423  outcol = lwcollection_construct_empty(outtype, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags));
424  }
425 
426  return outcol;
427 }
428 
429 LWGEOM*
431 {
432  uint32_t i;
433  LWGEOM **newgeoms;
434 
435  newgeoms = lwalloc(sizeof(LWGEOM *)*coll->ngeoms);
436  for (i=0; i<coll->ngeoms; i++)
437  {
438  newgeoms[i] = lwgeom_remove_repeated_points(coll->geoms[i]);
439  }
440 
441  return (LWGEOM*)lwcollection_construct(coll->type,
442  coll->srid, coll->bbox ? gbox_copy(coll->bbox) : NULL,
443  coll->ngeoms, newgeoms);
444 }
445 
446 
448 lwcollection_force_dims(const LWCOLLECTION *col, int hasz, int hasm)
449 {
450  LWCOLLECTION *colout;
451 
452  /* Return 2D empty */
453  if( lwcollection_is_empty(col) )
454  {
455  colout = lwcollection_construct_empty(col->type, col->srid, hasz, hasm);
456  }
457  else
458  {
459  int i;
460  LWGEOM **geoms = NULL;
461  geoms = lwalloc(sizeof(LWGEOM*) * col->ngeoms);
462  for( i = 0; i < col->ngeoms; i++ )
463  {
464  geoms[i] = lwgeom_force_dims(col->geoms[i], hasz, hasm);
465  }
466  colout = lwcollection_construct(col->type, col->srid, NULL, col->ngeoms, geoms);
467  }
468  return colout;
469 }
470 
472 {
473  int i;
474  if ( (col->ngeoms == 0) || (!col->geoms) )
475  return LW_TRUE;
476  for( i = 0; i < col->ngeoms; i++ )
477  {
478  if ( ! lwgeom_is_empty(col->geoms[i]) ) return LW_FALSE;
479  }
480  return LW_TRUE;
481 }
482 
483 
485 {
486  int i = 0;
487  int v = 0; /* vertices */
488  assert(col);
489  for ( i = 0; i < col->ngeoms; i++ )
490  {
491  v += lwgeom_count_vertices(col->geoms[i]);
492  }
493  return v;
494 }
495 
497 {
498  int i;
499  LWCOLLECTION *out = lwcollection_construct_empty(igeom->type, igeom->srid, FLAGS_GET_Z(igeom->flags), FLAGS_GET_M(igeom->flags));
500 
501  if( lwcollection_is_empty(igeom) )
502  return out; /* should we return NULL instead ? */
503 
504  for( i = 0; i < igeom->ngeoms; i++ )
505  {
506  LWGEOM *ngeom = lwgeom_simplify(igeom->geoms[i], dist);
507  if ( ngeom ) out = lwcollection_add_lwgeom(out, ngeom);
508  }
509 
510  return out;
511 }
512 
513 int lwcollection_allows_subtype(int collectiontype, int subtype)
514 {
515  if ( collectiontype == COLLECTIONTYPE )
516  return LW_TRUE;
517  if ( collectiontype == MULTIPOINTTYPE &&
518  subtype == POINTTYPE )
519  return LW_TRUE;
520  if ( collectiontype == MULTILINETYPE &&
521  subtype == LINETYPE )
522  return LW_TRUE;
523  if ( collectiontype == MULTIPOLYGONTYPE &&
524  subtype == POLYGONTYPE )
525  return LW_TRUE;
526  if ( collectiontype == COMPOUNDTYPE &&
527  (subtype == LINETYPE || subtype == CIRCSTRINGTYPE) )
528  return LW_TRUE;
529  if ( collectiontype == CURVEPOLYTYPE &&
530  (subtype == CIRCSTRINGTYPE || subtype == LINETYPE || subtype == COMPOUNDTYPE) )
531  return LW_TRUE;
532  if ( collectiontype == MULTICURVETYPE &&
533  (subtype == CIRCSTRINGTYPE || subtype == LINETYPE || subtype == COMPOUNDTYPE) )
534  return LW_TRUE;
535  if ( collectiontype == MULTISURFACETYPE &&
536  (subtype == POLYGONTYPE || subtype == CURVEPOLYTYPE) )
537  return LW_TRUE;
538  if ( collectiontype == POLYHEDRALSURFACETYPE &&
539  subtype == POLYGONTYPE )
540  return LW_TRUE;
541  if ( collectiontype == TINTYPE &&
542  subtype == TRIANGLETYPE )
543  return LW_TRUE;
544 
545  /* Must be a bad combination! */
546  return LW_FALSE;
547 }
548 
549 int
551 {
552  if ( col->ngeoms < 1 )
553  return LW_FAILURE;
554 
555  return lwgeom_startpoint(col->geoms[0], pt);
556 }
#define LINETYPE
Definition: liblwgeom.h:61
GBOX * gbox_copy(const GBOX *box)
Return a copy of the GBOX, based on dimensionality of flags.
Definition: g_box.c:362
void lwcollection_release(LWCOLLECTION *lwcollection)
Definition: lwcollection.c:23
void lwcollection_reserve(LWCOLLECTION *col, int ngeoms)
Ensure the collection can hold up at least ngeoms.
Definition: lwcollection.c:161
#define MULTICURVETYPE
Definition: liblwgeom.h:70
int lwcollection_count_vertices(LWCOLLECTION *col)
Definition: lwcollection.c:484
void lwfree(void *mem)
Definition: lwutil.c:190
uint8_t type
Definition: liblwgeom.h:459
LWCOLLECTION * lwcollection_simplify(const LWCOLLECTION *igeom, double dist)
Definition: lwcollection.c:496
int lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt)
Definition: lwgeom.c:1692
#define POLYGONTYPE
Definition: liblwgeom.h:62
LWCOLLECTION * lwcollection_clone(const LWCOLLECTION *g)
Clone LWCOLLECTION object.
Definition: lwcollection.c:110
#define CURVEPOLYTYPE
Definition: liblwgeom.h:69
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1006
#define COMPOUNDTYPE
Definition: liblwgeom.h:68
#define MULTIPOINTTYPE
Definition: liblwgeom.h:63
GBOX * bbox
Definition: liblwgeom.h:461
#define FLAGS_GET_ZM(flags)
Definition: liblwgeom.h:119
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:316
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:50
#define TRIANGLETYPE
Definition: liblwgeom.h:73
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:72
LWCOLLECTION * lwcollection_segmentize2d(LWCOLLECTION *col, double dist)
Definition: lwcollection.c:220
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:389
int lwcollection_is_empty(const LWCOLLECTION *col)
Definition: lwcollection.c:471
#define LW_FAILURE
Definition: liblwgeom.h:54
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int srid, char hasz, char hasm)
Definition: lwcollection.c:81
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:67
int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
Calculate bounding box of a geometry, automatically taking into account whether it is cartesian or ge...
Definition: lwgeom.c:608
uint8_t flags
Definition: liblwgeom.h:460
LWCOLLECTION * lwcollection_construct(uint8_t type, int srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:30
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:164
#define LW_FALSE
Definition: liblwgeom.h:52
LWCOLLECTION * lwcollection_extract(LWCOLLECTION *col, int type)
Takes a potentially heterogeneous collection and returns a homogeneous collection consisting only of ...
Definition: lwcollection.c:343
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:174
int lwcollection_ngeoms(const LWCOLLECTION *col)
Definition: lwcollection.c:278
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:51
LWGEOM ** geoms
Definition: liblwgeom.h:465
#define TINTYPE
Definition: liblwgeom.h:74
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
Definition: lwgeom.c:955
char lwcollection_same(const LWCOLLECTION *c1, const LWCOLLECTION *c2)
check for same geometry composition
Definition: lwcollection.c:238
int lwcollection_allows_subtype(int collectiontype, int subtype)
Check if subtype is allowed in collectiontype.
Definition: lwcollection.c:513
int32_t srid
Definition: liblwgeom.h:462
LWCOLLECTION * lwcollection_clone_deep(const LWCOLLECTION *g)
Deep clone LWCOLLECTION object.
Definition: lwcollection.c:136
#define FLAGS_GET_Z(flags)
Macros for manipulating the 'flags' byte.
Definition: liblwgeom.h:106
LWGEOM * lwgeom_force_dims(const LWGEOM *lwgeom, int hasz, int hasm)
Definition: lwgeom.c:670
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
Definition: lwgeom.c:351
char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
geom1 same as geom2 iff
Definition: lwgeom.c:451
uint8_t gflags(int hasz, int hasm, int geodetic)
Construct a new flags char.
Definition: g_util.c:131
LWGEOM * lwcollection_remove_repeated_points(LWCOLLECTION *coll)
Definition: lwcollection.c:430
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:65
#define FLAGS_SET_BBOX(flags, value)
Definition: liblwgeom.h:114
LWCOLLECTION * lwcollection_force_dims(const LWCOLLECTION *col, int hasz, int hasm)
Definition: lwcollection.c:448
int lwcollection_startpoint(const LWCOLLECTION *col, POINT4D *pt)
Definition: lwcollection.c:550
#define MULTISURFACETYPE
Definition: liblwgeom.h:71
LWGEOM * lwcollection_getsubgeom(LWCOLLECTION *col, int gnum)
Definition: lwcollection.c:100
void * lwrealloc(void *mem, size_t size)
Definition: lwutil.c:183
LWGEOM * lwgeom_segmentize2d(LWGEOM *line, double dist)
Definition: lwgeom.c:624
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:60
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:107
void lwgeom_release(LWGEOM *lwgeom)
Free the containing LWGEOM and the associated BOX.
Definition: lwgeom.c:328
uint8_t type
Definition: liblwgeom.h:352
LWGEOM * lwgeom_simplify(const LWGEOM *igeom, double dist)
Definition: lwgeom.c:1478
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:67
void * lwalloc(size_t size)
Definition: lwutil.c:175
int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members) ...
Definition: lwgeom.c:1229
int lwgeom_count_vertices(const LWGEOM *geom)
Count the total number of vertices in any LWGEOM.
Definition: lwgeom.c:1072
#define MULTILINETYPE
Definition: liblwgeom.h:64
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:55
LWGEOM * lwgeom_remove_repeated_points(LWGEOM *in)
Remove repeated points!
Definition: lwgeom.c:1339
#define COLLECTIONTYPE
Definition: liblwgeom.h:66
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:219