PostGIS  3.4.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 
42 lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
43 {
44  LWCOLLECTION *ret;
45  int hasz, hasm;
46 #ifdef CHECK_LWGEOM_ZM
47  char zm;
48  uint32_t i;
49 #endif
50 
51  LWDEBUGF(2, "lwcollection_construct called with %d, %d, %p, %d, %p.", type, srid, bbox, ngeoms, geoms);
52 
53  if( ! lwtype_is_collection(type) )
54  lwerror("Non-collection type specified in collection constructor!");
55 
56  hasz = 0;
57  hasm = 0;
58  if ( ngeoms > 0 )
59  {
60  hasz = FLAGS_GET_Z(geoms[0]->flags);
61  hasm = FLAGS_GET_M(geoms[0]->flags);
62 #ifdef CHECK_LWGEOM_ZM
63  zm = FLAGS_GET_ZM(geoms[0]->flags);
64 
65  LWDEBUGF(3, "lwcollection_construct type[0]=%d", geoms[0]->type);
66 
67  for (i=1; i<ngeoms; i++)
68  {
69  LWDEBUGF(3, "lwcollection_construct type=[%d]=%d", i, geoms[i]->type);
70 
71  if ( zm != FLAGS_GET_ZM(geoms[i]->flags) )
72  lwerror("lwcollection_construct: mixed dimension geometries: %d/%d", zm, FLAGS_GET_ZM(geoms[i]->flags));
73  }
74 #endif
75  }
76 
77 
78  ret = lwalloc(sizeof(LWCOLLECTION));
79  ret->type = type;
80  ret->flags = lwflags(hasz,hasm,0);
81  FLAGS_SET_BBOX(ret->flags, bbox?1:0);
82  ret->srid = srid;
83  ret->ngeoms = ngeoms;
84  ret->maxgeoms = ngeoms;
85  ret->geoms = geoms;
86  ret->bbox = bbox;
87 
88  return ret;
89 }
90 
92 lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
93 {
94  LWCOLLECTION *ret;
95  if( ! lwtype_is_collection(type) )
96  {
97  lwerror("Non-collection type specified in collection constructor!");
98  return NULL;
99  }
100 
101  ret = lwalloc(sizeof(LWCOLLECTION));
102  ret->type = type;
103  ret->flags = lwflags(hasz,hasm,0);
104  ret->srid = srid;
105  ret->ngeoms = 0;
106  ret->maxgeoms = 1; /* Allocate room for sub-members, just in case. */
107  ret->geoms = lwalloc(ret->maxgeoms * sizeof(LWGEOM*));
108  ret->bbox = NULL;
109 
110  return ret;
111 }
112 
113 LWGEOM *
115 {
116  return (LWGEOM *)col->geoms[gnum];
117 }
118 
123 LWCOLLECTION *
125 {
126  uint32_t i;
127  LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION));
128  memcpy(ret, g, sizeof(LWCOLLECTION));
129  if ( g->ngeoms > 0 )
130  {
131  ret->geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms);
132  for (i=0; i<g->ngeoms; i++)
133  {
134  ret->geoms[i] = lwgeom_clone(g->geoms[i]);
135  }
136  if ( g->bbox ) ret->bbox = gbox_copy(g->bbox);
137  }
138  else
139  {
140  ret->bbox = NULL; /* empty collection */
141  ret->geoms = NULL;
142  }
143  return ret;
144 }
145 
149 LWCOLLECTION *
151 {
152  uint32_t i;
153  LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION));
154  memcpy(ret, g, sizeof(LWCOLLECTION));
155  if ( g->ngeoms > 0 )
156  {
157  ret->geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms);
158  for (i=0; i<g->ngeoms; i++)
159  {
160  ret->geoms[i] = lwgeom_clone_deep(g->geoms[i]);
161  }
162  if ( g->bbox ) ret->bbox = gbox_copy(g->bbox);
163  }
164  else
165  {
166  ret->bbox = NULL; /* empty collection */
167  ret->geoms = NULL;
168  }
169  return ret;
170 }
171 
175 void lwcollection_reserve(LWCOLLECTION *col, uint32_t ngeoms)
176 {
177  if ( ngeoms <= col->maxgeoms ) return;
178 
179  /* Allocate more space if we need it */
180  do { col->maxgeoms *= 2; } while ( col->maxgeoms < ngeoms );
181  col->geoms = lwrealloc(col->geoms, sizeof(LWGEOM*) * col->maxgeoms);
182 }
183 
189 {
190  if (!col || !geom) return NULL;
191 
192  if (!col->geoms && (col->ngeoms || col->maxgeoms))
193  {
194  lwerror("Collection is in inconsistent state. Null memory but non-zero collection counts.");
195  return NULL;
196  }
197 
198  /* Check type compatibility */
199  if ( ! lwcollection_allows_subtype(col->type, geom->type) ) {
200  lwerror("%s cannot contain %s element", lwtype_name(col->type), lwtype_name(geom->type));
201  return NULL;
202  }
203 
204  /* In case this is a truly empty, make some initial space */
205  if (!col->geoms)
206  {
207  col->maxgeoms = 2;
208  col->ngeoms = 0;
209  col->geoms = lwalloc(col->maxgeoms * sizeof(LWGEOM*));
210  }
211 
212  /* Allocate more space if we need it */
213  lwcollection_reserve(col, col->ngeoms + 1);
214 
215 #if PARANOIA_LEVEL > 1
216  /* See http://trac.osgeo.org/postgis/ticket/2933 */
217  /* Make sure we don't already have a reference to this geom */
218  {
219  uint32_t i = 0;
220  for (i = 0; i < col->ngeoms; i++)
221  {
222  if (col->geoms[i] == geom)
223  {
224  lwerror("%s [%d] found duplicate geometry in collection %p == %p", __FILE__, __LINE__, col->geoms[i], geom);
225  return col;
226  }
227  }
228  }
229 #endif
230 
231  col->geoms[col->ngeoms] = (LWGEOM*)geom;
232  col->ngeoms++;
233  return col;
234 }
235 
240 LWCOLLECTION *
242 {
243  uint32_t i;
244  if (!col1 || !col2) return NULL;
245  for (i = 0; i < col2->ngeoms; i++)
246  col1 = lwcollection_add_lwgeom(col1, col2->geoms[i]);
247  return col1;
248 }
249 
251 lwcollection_segmentize2d(const LWCOLLECTION* col, double dist)
252 {
253  uint32_t i, j;
254  LWGEOM** newgeoms;
255 
256  if (!col->ngeoms) return lwcollection_clone(col);
257 
258  newgeoms = lwalloc(sizeof(LWGEOM*) * col->ngeoms);
259  for (i = 0; i < col->ngeoms; i++)
260  {
261  newgeoms[i] = lwgeom_segmentize2d(col->geoms[i], dist);
262  if (!newgeoms[i])
263  {
264  for (j = 0; j < i; j++)
265  lwgeom_free(newgeoms[j]);
266  lwfree(newgeoms);
267  return NULL;
268  }
269  }
270 
271  return lwcollection_construct(
272  col->type, col->srid, NULL, col->ngeoms, newgeoms);
273 }
274 
278 char
280 {
281  uint32_t i;
282 
283  LWDEBUG(2, "lwcollection_same called");
284 
285  if ( c1->type != c2->type ) return LW_FALSE;
286  if ( c1->ngeoms != c2->ngeoms ) return LW_FALSE;
287 
288  for ( i = 0; i < c1->ngeoms; i++ )
289  {
290  if ( ! lwgeom_same(c1->geoms[i], c2->geoms[i]) )
291  return LW_FALSE;
292  }
293 
294  /* Former method allowed out-of-order equality between collections
295 
296  hit = lwalloc(sizeof(uint32_t)*c1->ngeoms);
297  memset(hit, 0, sizeof(uint32_t)*c1->ngeoms);
298 
299  for (i=0; i<c1->ngeoms; i++)
300  {
301  char found=0;
302  for (j=0; j<c2->ngeoms; j++)
303  {
304  if ( hit[j] ) continue;
305  if ( lwgeom_same(c1->geoms[i], c2->geoms[j]) )
306  {
307  hit[j] = 1;
308  found=1;
309  break;
310  }
311  }
312  if ( ! found ) return LW_FALSE;
313  }
314  */
315 
316  return LW_TRUE;
317 }
318 
320 {
321  uint32_t i;
322  int ngeoms = 0;
323 
324  if ( ! col )
325  {
326  lwerror("Null input geometry.");
327  return 0;
328  }
329 
330  for ( i = 0; i < col->ngeoms; i++ )
331  {
332  if ( col->geoms[i])
333  {
334  switch (col->geoms[i]->type)
335  {
336  case POINTTYPE:
337  case LINETYPE:
338  case CIRCSTRINGTYPE:
339  case POLYGONTYPE:
340  ngeoms += 1;
341  break;
342  case MULTIPOINTTYPE:
343  case MULTILINETYPE:
344  case MULTICURVETYPE:
345  case MULTIPOLYGONTYPE:
346  ngeoms += col->ngeoms;
347  break;
348  case COLLECTIONTYPE:
349  ngeoms += lwcollection_ngeoms((LWCOLLECTION*)col->geoms[i]);
350  break;
351  }
352  }
353  }
354  return ngeoms;
355 }
356 
358 {
359  uint32_t i;
360  if ( ! col ) return;
361 
362  if ( col->bbox )
363  {
364  lwfree(col->bbox);
365  }
366  for ( i = 0; i < col->ngeoms; i++ )
367  {
368  LWDEBUGF(4,"freeing geom[%d]", i);
369  if ( col->geoms && col->geoms[i] )
370  lwgeom_free(col->geoms[i]);
371  }
372  if ( col->geoms )
373  {
374  lwfree(col->geoms);
375  }
376  lwfree(col);
377 }
378 
383 static uint32_t
385 {
386  int largest_type = 0;
387  size_t i;
388  for (i = 0; i < col->ngeoms; i++)
389  {
390  LWGEOM *g = col->geoms[i];
391  int gtype = lwgeom_get_type(g);
392  if (lwgeom_is_collection(g))
393  {
395  }
396 
397  if (gtype == POINTTYPE || gtype == LINETYPE || gtype == POLYGONTYPE)
398  {
399  if (gtype > largest_type)
400  largest_type = gtype;
401  }
402  }
403  return largest_type;
404 }
405 
406 
407 static int
409 {
410  size_t i;
411  size_t geoms_added = 0;
412 
413  for (i = 0; i < col->ngeoms; i++)
414  {
415  LWGEOM *g = col->geoms[i];
416  if (lwgeom_is_collection(g))
417  {
418  LWCOLLECTION *col_part = lwgeom_as_lwcollection(g);
419  geoms_added += lwcollection_extract_recursive(col_part, type, col_out);
420  }
421 
422  if (lwgeom_get_type(g) == type && !lwgeom_is_empty(g))
423  {
424  lwcollection_add_lwgeom(col_out, lwgeom_clone(col->geoms[i]));
425  geoms_added++;
426  }
427  }
428  return geoms_added;
429 }
430 
433 {
434  LWCOLLECTION* outcol;
435 
436  if (!col) return NULL;
437 
438  /* Self-discover output type when it is not specified */
439  if (!type)
441 
442  /*
443  * If self-discovery failed, there were no primitive points
444  * lines or polygons in the collection, so send back an
445  * empty collection.
446  */
447  if (!type)
448  {
450  col->srid,
451  FLAGS_GET_Z(col->flags),
452  FLAGS_GET_M(col->flags));
453  }
454 
455  if (!(type == POINTTYPE || type == LINETYPE || type == POLYGONTYPE))
456  {
457  lwerror(
458  "Only POLYGON, LINESTRING and POINT are supported by "
459  "lwcollection_extract. %s requested.",
460  lwtype_name(type));
461  return NULL;
462  }
463 
465  col->srid,
466  FLAGS_GET_Z(col->flags),
467  FLAGS_GET_M(col->flags));
468 
469  lwcollection_extract_recursive(col, type, outcol);
471  return outcol;
472 }
473 
474 
476 lwcollection_force_dims(const LWCOLLECTION *col, int hasz, int hasm, double zval, double mval)
477 {
478  LWCOLLECTION *colout;
479 
480  /* Return 2D empty */
481  if( lwcollection_is_empty(col) )
482  {
483  colout = lwcollection_construct_empty(col->type, col->srid, hasz, hasm);
484  }
485  else
486  {
487  uint32_t i;
488  LWGEOM **geoms = NULL;
489  geoms = lwalloc(sizeof(LWGEOM*) * col->ngeoms);
490  for( i = 0; i < col->ngeoms; i++ )
491  {
492  geoms[i] = lwgeom_force_dims(col->geoms[i], hasz, hasm, zval, mval);
493  }
494  colout = lwcollection_construct(col->type, col->srid, NULL, col->ngeoms, geoms);
495  }
496  return colout;
497 }
498 
499 
501 {
502  uint32_t i = 0;
503  uint32_t v = 0; /* vertices */
504  assert(col);
505  for ( i = 0; i < col->ngeoms; i++ )
506  {
507  v += lwgeom_count_vertices(col->geoms[i]);
508  }
509  return v;
510 }
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 }
557 
558 
GBOX * gbox_copy(const GBOX *box)
Return a copy of the GBOX, based on dimensionality of flags.
Definition: gbox.c:426
char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
geom1 same as geom2 iff
Definition: lwgeom.c:591
#define LW_FALSE
Definition: liblwgeom.h:94
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:309
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
#define COMPOUNDTYPE
Definition: liblwgeom.h:110
int lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt)
Definition: lwgeom.c:2135
#define LW_FAILURE
Definition: liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
#define CURVEPOLYTYPE
Definition: liblwgeom.h:111
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define MULTISURFACETYPE
Definition: liblwgeom.h:113
#define LINETYPE
Definition: liblwgeom.h:103
LWGEOM * lwgeom_segmentize2d(const LWGEOM *line, double dist)
Definition: lwgeom.c:771
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
#define FLAGS_SET_BBOX(flags, value)
Definition: liblwgeom.h:174
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:529
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
Definition: lwgeom.c:1105
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:165
#define TINTYPE
Definition: liblwgeom.h:116
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
uint32_t lwgeom_count_vertices(const LWGEOM *geom)
Count the total number of vertices in any LWGEOM.
Definition: lwgeom.c:1246
void * lwrealloc(void *mem, size_t size)
Definition: lwutil.c:235
void lwfree(void *mem)
Definition: lwutil.c:242
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
Definition: lwgeom.c:1097
#define POLYGONTYPE
Definition: liblwgeom.h:104
uint8_t lwtype_multitype(uint8_t type)
Definition: lwgeom.c:370
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:114
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:109
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:166
#define FLAGS_GET_ZM(flags)
Definition: liblwgeom.h:180
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
Definition: lwgeom.c:491
#define MULTICURVETYPE
Definition: liblwgeom.h:112
#define TRIANGLETYPE
Definition: liblwgeom.h:115
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition: lwgeom.c:233
lwflags_t lwflags(int hasz, int hasm, int geodetic)
Construct a new flags bitmask.
Definition: lwutil.c:471
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
void lwgeom_release(LWGEOM *lwgeom)
Free the containing LWGEOM and the associated BOX.
Definition: lwgeom.c:468
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition: lwgeom.c:695
LWGEOM * lwgeom_force_dims(const LWGEOM *lwgeom, int hasz, int hasm, double zval, double mval)
Definition: lwgeom.c:817
int lwcollection_is_empty(const LWCOLLECTION *col)
LWCOLLECTION * lwcollection_clone(const LWCOLLECTION *g)
Clone LWCOLLECTION object.
Definition: lwcollection.c:124
char lwcollection_same(const LWCOLLECTION *c1, const LWCOLLECTION *c2)
check for same geometry composition
Definition: lwcollection.c:279
static int lwcollection_extract_recursive(const LWCOLLECTION *col, uint32_t type, LWCOLLECTION *col_out)
Definition: lwcollection.c:408
LWCOLLECTION * lwcollection_segmentize2d(const LWCOLLECTION *col, double dist)
Definition: lwcollection.c:251
LWGEOM * lwcollection_getsubgeom(LWCOLLECTION *col, int gnum)
Definition: lwcollection.c:114
int lwcollection_startpoint(const LWCOLLECTION *col, POINT4D *pt)
Definition: lwcollection.c:550
static uint32_t lwcollection_largest_dimension(const LWCOLLECTION *col)
Examines contents of collection and finds the largest coordinate dimension of all components.
Definition: lwcollection.c:384
LWCOLLECTION * lwcollection_extract(const LWCOLLECTION *col, uint32_t type)
Definition: lwcollection.c:432
void lwcollection_release(LWCOLLECTION *lwcollection)
Definition: lwcollection.c:36
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
Definition: lwcollection.c:92
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:357
int lwcollection_ngeoms(const LWCOLLECTION *col)
Definition: lwcollection.c:319
LWCOLLECTION * lwcollection_concat_in_place(LWCOLLECTION *col1, const LWCOLLECTION *col2)
Appends all geometries from col2 to col1 in place.
Definition: lwcollection.c:241
void lwcollection_reserve(LWCOLLECTION *col, uint32_t ngeoms)
Ensure the collection can hold up at least ngeoms.
Definition: lwcollection.c:175
uint32_t lwcollection_count_vertices(const LWCOLLECTION *col)
Definition: lwcollection.c:500
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:188
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:42
LWCOLLECTION * lwcollection_force_dims(const LWCOLLECTION *col, int hasz, int hasm, double zval, double mval)
Definition: lwcollection.c:476
int lwcollection_allows_subtype(int collectiontype, int subtype)
Check if subtype is allowed in collectiontype.
Definition: lwcollection.c:513
LWCOLLECTION * lwcollection_clone_deep(const LWCOLLECTION *g)
Deep clone LWCOLLECTION object.
Definition: lwcollection.c:150
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:83
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:145
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwinline.h:203
type
Definition: ovdump.py:42
lwflags_t flags
Definition: liblwgeom.h:577
uint32_t ngeoms
Definition: liblwgeom.h:580
uint32_t maxgeoms
Definition: liblwgeom.h:581
uint8_t type
Definition: liblwgeom.h:578
GBOX * bbox
Definition: liblwgeom.h:574
LWGEOM ** geoms
Definition: liblwgeom.h:575
int32_t srid
Definition: liblwgeom.h:576
uint8_t type
Definition: liblwgeom.h:462