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