PostGIS  3.4.0dev-r@@SVN_REVISION@@
liblwgeom/lwgeom_geos.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 2011-2020 Sandro Santilli <strk@kbt.io>
22  * Copyright 2015-2018 Daniel Baston <dbaston@gmail.com>
23  * Copyright 2017-2018 Darafei Praliaskouski <me@komzpa.net>
24  *
25  **********************************************************************/
26 
27 #include "lwgeom_geos.h"
28 #include "liblwgeom.h"
29 #include "liblwgeom_internal.h"
30 #include "lwgeom_log.h"
31 #include "lwrandom.h"
32 
33 #include <stdarg.h>
34 #include <stdlib.h>
35 
36 LWTIN* lwtin_from_geos(const GEOSGeometry* geom, uint8_t want3d);
37 
38 #define AUTOFIX LW_TRUE
39 #define LWGEOM_GEOS_ERRMSG_MAXSIZE 256
41 
42 const char *
44 {
45  static char ver[64];
46  sprintf(
47  ver,
48  "%d.%d.%d",
49  (POSTGIS_GEOS_VERSION/10000),
50  ((POSTGIS_GEOS_VERSION%10000)/100),
51  ((POSTGIS_GEOS_VERSION)%100)
52  );
53  return ver;
54 }
55 
56 extern void
57 lwgeom_geos_error(const char* fmt, ...)
58 {
59  va_list ap;
60  va_start(ap, fmt);
61 
62  /* Call the supplied function */
65 
66  va_end(ap);
67 }
68 
69 void
70 lwgeom_geos_error_minversion(const char *functionality, const char *minver)
71 {
72  lwerror(
73  "%s requires a build against GEOS-%s or higher,"
74  " this version of PostGIS was built against version %s",
75  functionality, minver, lwgeom_geos_compiled_version()
76  );
77 }
78 
79 /* Destroy any non-null GEOSGeometry* pointers passed as arguments */
80 #define GEOS_FREE(...) \
81 do { \
82  geos_destroy((sizeof((void*[]){__VA_ARGS__})/sizeof(void*)), __VA_ARGS__); \
83 } while (0)
84 
85 /* Pass the latest GEOS error to lwerror, then return NULL */
86 #define GEOS_FAIL() \
87 do { \
88  lwerror("%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); \
89  return NULL; \
90 } while (0)
91 
92 /* Pass the latest GEOS error to lwdebug, then return NULL */
93 #define GEOS_FAIL_DEBUG() \
94  do \
95  { \
96  lwdebug(1, "%s: GEOS Error: %s", __func__, lwgeom_geos_errmsg); \
97  return NULL; \
98  } while (0)
99 
100 #define GEOS_FREE_AND_FAIL(...) \
101 do { \
102  GEOS_FREE(__VA_ARGS__); \
103  GEOS_FAIL(); \
104 } while (0)
105 
106 #define GEOS_FREE_AND_FAIL_DEBUG(...) \
107  do \
108  { \
109  GEOS_FREE(__VA_ARGS__); \
110  GEOS_FAIL_DEBUG(); \
111  } while (0)
112 
113 /* Return the consistent SRID of all inputs, or call lwerror
114  * in case of SRID mismatch. */
115 #define RESULT_SRID(...) \
116  (get_result_srid((sizeof((const void*[]){__VA_ARGS__})/sizeof(void*)), __func__, __VA_ARGS__))
117 
118 /* Free any non-null GEOSGeometry* pointers passed as arguments *
119  * Called by GEOS_FREE, which populates 'count' */
120 static void geos_destroy(size_t count, ...) {
121  va_list ap;
122  va_start(ap, count);
123  while (count--)
124  {
125  GEOSGeometry* g = va_arg(ap, GEOSGeometry*);
126  if (g)
127  {
128  GEOSGeom_destroy(g);
129  }
130  }
131  va_end(ap);
132 }
133 
134 /*
135 ** GEOS <==> PostGIS conversion functions
136 **
137 ** Default conversion creates a GEOS point array, then iterates through the
138 ** PostGIS points, setting each value in the GEOS array one at a time.
139 **
140 */
141 
142 /* Return a POINTARRAY from a GEOSCoordSeq */
143 POINTARRAY*
144 ptarray_from_GEOSCoordSeq(const GEOSCoordSequence* cs, uint8_t want3d)
145 {
146  uint32_t dims = 2;
147  POINTARRAY* pa;
148  uint32_t size = 0;
149 #if POSTGIS_GEOS_VERSION < 31000
150  uint32_t i;
151  POINT4D point = { 0.0, 0.0, 0.0, 0.0 };
152 #endif
153 
154  LWDEBUG(2, "ptarray_fromGEOSCoordSeq called");
155 
156  if (!GEOSCoordSeq_getSize(cs, &size)) lwerror("Exception thrown");
157 
158  LWDEBUGF(4, " GEOSCoordSeq size: %d", size);
159 
160  if (want3d)
161  {
162  if (!GEOSCoordSeq_getDimensions(cs, &dims)) lwerror("Exception thrown");
163 
164  LWDEBUGF(4, " GEOSCoordSeq dimensions: %d", dims);
165 
166  /* forget higher dimensions (if any) */
167  if (dims > 3) dims = 3;
168  }
169 
170  LWDEBUGF(4, " output dimensions: %d", dims);
171 
172  pa = ptarray_construct((dims == 3), 0, size);
173 #if POSTGIS_GEOS_VERSION >= 31000
174  GEOSCoordSeq_copyToBuffer(cs, (double*) pa->serialized_pointlist, (dims == 3), 0);
175  return pa;
176 #else
177  for (i = 0; i < size; i++)
178  {
179 #if POSTGIS_GEOS_VERSION < 30800
180  GEOSCoordSeq_getX(cs, i, &(point.x));
181  GEOSCoordSeq_getY(cs, i, &(point.y));
182  if (dims >= 3) GEOSCoordSeq_getZ(cs, i, &(point.z));
183 #else
184  if (dims >= 3)
185  GEOSCoordSeq_getXYZ(cs, i, &(point.x), &(point.y), &(point.z));
186  else
187  GEOSCoordSeq_getXY(cs, i, &(point.x), &(point.y));
188 #endif
189  ptarray_set_point4d(pa, i, &point);
190  }
191 
192  return pa;
193 #endif
194 }
195 
196 /* Return an LWGEOM from a Geometry */
197 LWGEOM*
198 GEOS2LWGEOM(const GEOSGeometry* geom, uint8_t want3d)
199 {
200  int type = GEOSGeomTypeId(geom);
201  int SRID = GEOSGetSRID(geom);
202 
203  /* GEOS's 0 is equivalent to our unknown as for SRID values */
204  if (SRID == 0) SRID = SRID_UNKNOWN;
205 
206  if (want3d && !GEOSHasZ(geom))
207  {
208  LWDEBUG(3, "Geometry has no Z, won't provide one");
209  want3d = 0;
210  }
211 
212  switch (type)
213  {
214  const GEOSCoordSequence* cs;
215  POINTARRAY *pa, **ppaa;
216  const GEOSGeometry* g;
217  LWGEOM** geoms;
218  uint32_t i, ngeoms;
219 
220  case GEOS_POINT:
221  LWDEBUG(4, "lwgeom_from_geometry: it's a Point");
222  cs = GEOSGeom_getCoordSeq(geom);
223  if (GEOSisEmpty(geom)) return (LWGEOM*)lwpoint_construct_empty(SRID, want3d, 0);
224  pa = ptarray_from_GEOSCoordSeq(cs, want3d);
225  return (LWGEOM*)lwpoint_construct(SRID, NULL, pa);
226 
227  case GEOS_LINESTRING:
228  case GEOS_LINEARRING:
229  LWDEBUG(4, "lwgeom_from_geometry: it's a LineString or LinearRing");
230  if (GEOSisEmpty(geom)) return (LWGEOM*)lwline_construct_empty(SRID, want3d, 0);
231 
232  cs = GEOSGeom_getCoordSeq(geom);
233  pa = ptarray_from_GEOSCoordSeq(cs, want3d);
234  return (LWGEOM*)lwline_construct(SRID, NULL, pa);
235 
236  case GEOS_POLYGON:
237  LWDEBUG(4, "lwgeom_from_geometry: it's a Polygon");
238  if (GEOSisEmpty(geom)) return (LWGEOM*)lwpoly_construct_empty(SRID, want3d, 0);
239  ngeoms = GEOSGetNumInteriorRings(geom);
240  ppaa = lwalloc(sizeof(POINTARRAY*) * (ngeoms + 1));
241  g = GEOSGetExteriorRing(geom);
242  cs = GEOSGeom_getCoordSeq(g);
243  ppaa[0] = ptarray_from_GEOSCoordSeq(cs, want3d);
244  for (i = 0; i < ngeoms; i++)
245  {
246  g = GEOSGetInteriorRingN(geom, i);
247  cs = GEOSGeom_getCoordSeq(g);
248  ppaa[i + 1] = ptarray_from_GEOSCoordSeq(cs, want3d);
249  }
250  return (LWGEOM*)lwpoly_construct(SRID, NULL, ngeoms + 1, ppaa);
251 
252  case GEOS_MULTIPOINT:
253  case GEOS_MULTILINESTRING:
254  case GEOS_MULTIPOLYGON:
255  case GEOS_GEOMETRYCOLLECTION:
256  LWDEBUG(4, "lwgeom_from_geometry: it's a Collection or Multi");
257 
258  ngeoms = GEOSGetNumGeometries(geom);
259  geoms = NULL;
260  if (ngeoms)
261  {
262  geoms = lwalloc(sizeof(LWGEOM*) * ngeoms);
263  for (i = 0; i < ngeoms; i++)
264  {
265  g = GEOSGetGeometryN(geom, i);
266  geoms[i] = GEOS2LWGEOM(g, want3d);
267  }
268  }
269  return (LWGEOM*)lwcollection_construct(type, SRID, NULL, ngeoms, geoms);
270 
271  default:
272  lwerror("GEOS2LWGEOM: unknown geometry type: %d", type);
273  return NULL;
274  }
275 }
276 
277 GEOSCoordSeq ptarray_to_GEOSCoordSeq(const POINTARRAY*, uint8_t fix_ring);
278 
279 GEOSCoordSeq
280 ptarray_to_GEOSCoordSeq(const POINTARRAY* pa, uint8_t fix_ring)
281 {
282  uint32_t dims = 2;
283  uint32_t i;
284  int append_points = 0;
285  const POINT3D *p3d = NULL;
286  const POINT2D* p2d = NULL;
287  GEOSCoordSeq sq;
288 
289  if (FLAGS_GET_Z(pa->flags)) dims = 3;
290 
291  if (fix_ring)
292  {
293  if (pa->npoints < 1)
294  {
295  lwerror("ptarray_to_GEOSCoordSeq called with fix_ring and 0 vertices in ring, cannot fix");
296  return NULL;
297  }
298  else
299  {
300  if (pa->npoints < 4) append_points = 4 - pa->npoints;
301  if (!ptarray_is_closed_2d(pa) && append_points == 0) append_points = 1;
302  }
303  }
304 
305 #if POSTGIS_GEOS_VERSION >= 31000
306  if (append_points == 0) {
307  sq = GEOSCoordSeq_copyFromBuffer((const double*) pa->serialized_pointlist, pa->npoints, FLAGS_GET_Z(pa->flags), FLAGS_GET_M(pa->flags));
308  if (!sq)
309  {
310  GEOS_FAIL();
311  }
312  return sq;
313  }
314 #endif
315  if (!(sq = GEOSCoordSeq_create(pa->npoints + append_points, dims)))
316  {
317  GEOS_FAIL();
318  }
319 
320  for (i = 0; i < pa->npoints; i++)
321  {
322  if (dims == 3)
323  {
324  p3d = getPoint3d_cp(pa, i);
325  p2d = (const POINT2D*)p3d;
326  LWDEBUGF(4, "Point: %g,%g,%g", p3d->x, p3d->y, p3d->z);
327  }
328  else
329  {
330  p2d = getPoint2d_cp(pa, i);
331  LWDEBUGF(4, "Point: %g,%g", p2d->x, p2d->y);
332  }
333 
334 #if POSTGIS_GEOS_VERSION < 30800
335  GEOSCoordSeq_setX(sq, i, p2d->x);
336  GEOSCoordSeq_setY(sq, i, p2d->y);
337  if (dims == 3) GEOSCoordSeq_setZ(sq, i, p3d->z);
338 #else
339  if (dims == 3)
340  GEOSCoordSeq_setXYZ(sq, i, p2d->x, p2d->y, p3d->z);
341  else
342  GEOSCoordSeq_setXY(sq, i, p2d->x, p2d->y);
343 #endif
344 
345  }
346 
347  if (append_points)
348  {
349  if (dims == 3)
350  {
351  p3d = getPoint3d_cp(pa, 0);
352  p2d = (const POINT2D*)p3d;
353  }
354  else
355  p2d = getPoint2d_cp(pa, 0);
356  for (i = pa->npoints; i < pa->npoints + append_points; i++)
357  {
358 #if POSTGIS_GEOS_VERSION < 30800
359  GEOSCoordSeq_setX(sq, i, p2d->x);
360  GEOSCoordSeq_setY(sq, i, p2d->y);
361 #else
362  GEOSCoordSeq_setXY(sq, i, p2d->x, p2d->y);
363 #endif
364 
365  if (dims == 3) GEOSCoordSeq_setZ(sq, i, p3d->z);
366  }
367  }
368 
369  return sq;
370 
371 }
372 
373 static inline GEOSGeometry*
374 ptarray_to_GEOSLinearRing(const POINTARRAY* pa, uint8_t autofix)
375 {
376  GEOSCoordSeq sq;
377  GEOSGeom g;
378  sq = ptarray_to_GEOSCoordSeq(pa, autofix);
379  g = GEOSGeom_createLinearRing(sq);
380  return g;
381 }
382 
383 GEOSGeometry*
384 GBOX2GEOS(const GBOX* box)
385 {
386  GEOSGeometry* envelope;
387  GEOSGeometry* ring;
388  GEOSCoordSequence* seq = GEOSCoordSeq_create(5, 2);
389  if (!seq) return NULL;
390 
391 #if POSTGIS_GEOS_VERSION < 30800
392  GEOSCoordSeq_setX(seq, 0, box->xmin);
393  GEOSCoordSeq_setY(seq, 0, box->ymin);
394 
395  GEOSCoordSeq_setX(seq, 1, box->xmax);
396  GEOSCoordSeq_setY(seq, 1, box->ymin);
397 
398  GEOSCoordSeq_setX(seq, 2, box->xmax);
399  GEOSCoordSeq_setY(seq, 2, box->ymax);
400 
401  GEOSCoordSeq_setX(seq, 3, box->xmin);
402  GEOSCoordSeq_setY(seq, 3, box->ymax);
403 
404  GEOSCoordSeq_setX(seq, 4, box->xmin);
405  GEOSCoordSeq_setY(seq, 4, box->ymin);
406 #else
407  GEOSCoordSeq_setXY(seq, 0, box->xmin, box->ymin);
408  GEOSCoordSeq_setXY(seq, 1, box->xmax, box->ymin);
409  GEOSCoordSeq_setXY(seq, 2, box->xmax, box->ymax);
410  GEOSCoordSeq_setXY(seq, 3, box->xmin, box->ymax);
411  GEOSCoordSeq_setXY(seq, 4, box->xmin, box->ymin);
412 #endif
413 
414  ring = GEOSGeom_createLinearRing(seq);
415  if (!ring)
416  {
417  GEOSCoordSeq_destroy(seq);
418  return NULL;
419  }
420 
421  envelope = GEOSGeom_createPolygon(ring, NULL, 0);
422  if (!envelope)
423  {
424  GEOSGeom_destroy(ring);
425  return NULL;
426  }
427 
428  return envelope;
429 }
430 
431 GEOSGeometry*
432 LWGEOM2GEOS(const LWGEOM* lwgeom, uint8_t autofix)
433 {
434  GEOSCoordSeq sq;
435  GEOSGeom g, shell;
436  GEOSGeom* geoms = NULL;
437  uint32_t ngeoms, i, j;
438  int is_empty = LW_FALSE;
439 #if LWDEBUG_LEVEL >= 4
440  char* wkt;
441 #endif
442 
443  if (autofix)
444  {
445  /* cross fingers and try without autofix, maybe it'll work? */
446  g = LWGEOM2GEOS(lwgeom, LW_FALSE);
447  if (g) return g;
448  }
449 
450  LWDEBUGF(4, "LWGEOM2GEOS got a %s", lwtype_name(lwgeom->type));
451 
452  if (lwgeom_type_arc(lwgeom))
453  {
454  LWGEOM* lwgeom_stroked = lwgeom_stroke(lwgeom, 32);
455  GEOSGeometry* g = LWGEOM2GEOS(lwgeom_stroked, autofix);
456  lwgeom_free(lwgeom_stroked);
457  return g;
458  }
459 
460  is_empty = lwgeom_is_empty(lwgeom);
461 
462  switch (lwgeom->type)
463  {
464  case POINTTYPE:
465  {
466  if (is_empty)
467  g = GEOSGeom_createEmptyPoint();
468  else
469  {
470  LWPOINT* lwp = (LWPOINT*)lwgeom;
471  #if POSTGIS_GEOS_VERSION < 30800
472  sq = ptarray_to_GEOSCoordSeq(lwp->point, 0);
473  g = GEOSGeom_createPoint(sq);
474  #else
475  if (lwgeom_has_z(lwgeom))
476  {
477  sq = ptarray_to_GEOSCoordSeq(lwp->point, 0);
478  g = GEOSGeom_createPoint(sq);
479  }
480  else
481  {
482  const POINT2D* p = getPoint2d_cp(lwp->point, 0);
483  g = GEOSGeom_createPointFromXY(p->x, p->y);
484  }
485  #endif
486  }
487  if (!g) return NULL;
488  break;
489  }
490 
491  case LINETYPE:
492  {
493  if (is_empty)
494  g = GEOSGeom_createEmptyLineString();
495  else
496  {
497  LWLINE* lwl = (LWLINE*)lwgeom;
498  /* TODO: if (autofix) */
499  if (lwl->points->npoints == 1)
500  {
501  /* Duplicate point, to make geos-friendly */
502  lwl->points = ptarray_addPoint(lwl->points,
503  getPoint_internal(lwl->points, 0),
504  FLAGS_NDIMS(lwl->points->flags),
505  lwl->points->npoints);
506  }
507  sq = ptarray_to_GEOSCoordSeq(lwl->points, 0);
508  g = GEOSGeom_createLineString(sq);
509  }
510  if (!g) return NULL;
511  break;
512  }
513 
514  case POLYGONTYPE:
515  {
516  LWPOLY* lwpoly = (LWPOLY*)lwgeom;
517  if (is_empty)
518  g = GEOSGeom_createEmptyPolygon();
519  else
520  {
521  shell = ptarray_to_GEOSLinearRing(lwpoly->rings[0], autofix);
522  if (!shell) return NULL;
523  ngeoms = lwpoly->nrings - 1;
524  if (ngeoms > 0) geoms = lwalloc(sizeof(GEOSGeom) * ngeoms);
525 
526  for (i = 1; i < lwpoly->nrings; i++)
527  {
528  geoms[i - 1] = ptarray_to_GEOSLinearRing(lwpoly->rings[i], autofix);
529  if (!geoms[i - 1])
530  {
531  uint32_t k;
532  for (k = 0; k < i - 1; k++)
533  GEOSGeom_destroy(geoms[k]);
534  lwfree(geoms);
535  GEOSGeom_destroy(shell);
536  return NULL;
537  }
538  }
539  g = GEOSGeom_createPolygon(shell, geoms, ngeoms);
540  if (geoms) lwfree(geoms);
541  }
542  if (!g) return NULL;
543  break;
544  }
545 
546  case TRIANGLETYPE:
547  {
548  if (is_empty)
549  g = GEOSGeom_createEmptyPolygon();
550  else
551  {
552  LWTRIANGLE *lwt = (LWTRIANGLE *)lwgeom;
553  shell = ptarray_to_GEOSLinearRing(lwt->points, autofix);
554  if (!shell) return NULL;
555  g = GEOSGeom_createPolygon(shell, NULL, 0);
556  }
557  if (!g) return NULL;
558  break;
559  }
560  case MULTIPOINTTYPE:
561  case MULTILINETYPE:
562  case MULTIPOLYGONTYPE:
563  case TINTYPE:
564  case COLLECTIONTYPE:
565  {
566  int geostype;
567  if (lwgeom->type == MULTIPOINTTYPE)
568  geostype = GEOS_MULTIPOINT;
569  else if (lwgeom->type == MULTILINETYPE)
570  geostype = GEOS_MULTILINESTRING;
571  else if (lwgeom->type == MULTIPOLYGONTYPE)
572  geostype = GEOS_MULTIPOLYGON;
573  else
574  geostype = GEOS_GEOMETRYCOLLECTION;
575 
576  LWCOLLECTION* lwc = (LWCOLLECTION*)lwgeom;
577 
578  ngeoms = lwc->ngeoms;
579  if (ngeoms > 0) geoms = lwalloc(sizeof(GEOSGeom) * ngeoms);
580 
581  j = 0;
582  for (i = 0; i < ngeoms; ++i)
583  {
584  GEOSGeometry* g;
585 
586  /* if (lwgeom_is_empty(lwc->geoms[i])) continue; */
587 
588  g = LWGEOM2GEOS(lwc->geoms[i], 0);
589  if (!g)
590  {
591  uint32_t k;
592  for (k = 0; k < j; k++)
593  GEOSGeom_destroy(geoms[k]);
594  lwfree(geoms);
595  return NULL;
596  }
597  geoms[j++] = g;
598  }
599  g = GEOSGeom_createCollection(geostype, geoms, j);
600  if (ngeoms > 0) lwfree(geoms);
601  if (!g) return NULL;
602  break;
603  }
604 
605  default:
606  {
607  lwerror("Unknown geometry type: %d - %s", lwgeom->type, lwtype_name(lwgeom->type));
608  return NULL;
609  }
610  }
611 
612  GEOSSetSRID(g, lwgeom->srid);
613 
614 #if LWDEBUG_LEVEL >= 4
615  wkt = GEOSGeomToWKT(g);
616  LWDEBUGF(4, "LWGEOM2GEOS: GEOSGeom: %s", wkt);
617  GEOSFree(wkt);
618 #endif
619 
620  return g;
621 }
622 
623 GEOSGeometry*
624 make_geos_point(double x, double y)
625 {
626  GEOSCoordSequence* seq = GEOSCoordSeq_create(1, 2);
627  GEOSGeometry* geom = NULL;
628 
629  if (!seq) return NULL;
630 
631 #if POSTGIS_GEOS_VERSION < 30800
632  GEOSCoordSeq_setX(seq, 0, x);
633  GEOSCoordSeq_setY(seq, 0, y);
634 #else
635  GEOSCoordSeq_setXY(seq, 0, x, y);
636 #endif
637 
638  geom = GEOSGeom_createPoint(seq);
639  if (!geom) GEOSCoordSeq_destroy(seq);
640  return geom;
641 }
642 
643 GEOSGeometry*
644 make_geos_segment(double x1, double y1, double x2, double y2)
645 {
646  GEOSCoordSequence* seq = GEOSCoordSeq_create(2, 2);
647  GEOSGeometry* geom = NULL;
648 
649  if (!seq) return NULL;
650 
651 #if POSTGIS_GEOS_VERSION < 30800
652  GEOSCoordSeq_setX(seq, 0, x1);
653  GEOSCoordSeq_setY(seq, 0, y1);
654  GEOSCoordSeq_setX(seq, 1, x2);
655  GEOSCoordSeq_setY(seq, 1, y2);
656 #else
657  GEOSCoordSeq_setXY(seq, 0, x1, y1);
658  GEOSCoordSeq_setXY(seq, 1, x2, y2);
659 #endif
660 
661  geom = GEOSGeom_createLineString(seq);
662  if (!geom) GEOSCoordSeq_destroy(seq);
663  return geom;
664 }
665 
666 const char*
668 {
669  const char* ver = GEOSversion();
670  return ver;
671 }
672 
673 /* Return the consistent SRID of all input.
674  * Intended to be called from RESULT_SRID macro */
675 static int32_t
676 get_result_srid(size_t count, const char* funcname, ...)
677 {
678  va_list ap;
679  va_start(ap, funcname);
680  int32_t srid = SRID_INVALID;
681  size_t i;
682  for(i = 0; i < count; i++)
683  {
684  LWGEOM* g = va_arg(ap, LWGEOM*);
685  if (!g)
686  {
687  lwerror("%s: Geometry is null", funcname);
688  va_end(ap);
689  return SRID_INVALID;
690  }
691  if (i == 0)
692  {
693  srid = g->srid;
694  }
695  else
696  {
697  if (g->srid != srid)
698  {
699  lwerror("%s: Operation on mixed SRID geometries (%d != %d)", funcname, srid, g->srid);
700  va_end(ap);
701  return SRID_INVALID;
702  }
703  }
704  }
705  va_end(ap);
706  return srid;
707 }
708 
709 LWGEOM*
711 {
712  LWGEOM* result;
713  int32_t srid = RESULT_SRID(geom);
714  uint8_t is3d = FLAGS_GET_Z(geom->flags);
715  GEOSGeometry* g;
716 
717  if (srid == SRID_INVALID) return NULL;
718 
719  initGEOS(lwnotice, lwgeom_geos_error);
720 
721  if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
722 
723  if (GEOSNormalize(g) == -1) GEOS_FREE_AND_FAIL(g);
724  GEOSSetSRID(g, srid);
725 
726  if (!(result = GEOS2LWGEOM(g, is3d))) GEOS_FREE_AND_FAIL(g);
727 
728  GEOSGeom_destroy(g);
729  return result;
730 }
731 
732 LWGEOM*
733 lwgeom_intersection(const LWGEOM* g1, const LWGEOM* g2)
734 {
735  return lwgeom_intersection_prec(g1, g2, -1.0);
736 }
737 
738 LWGEOM*
739 lwgeom_intersection_prec(const LWGEOM* geom1, const LWGEOM* geom2, double prec)
740 {
741  LWGEOM* result;
742  int32_t srid = RESULT_SRID(geom1, geom2);
743  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
744  GEOSGeometry* g1;
745  GEOSGeometry* g2;
746  GEOSGeometry* g3;
747 
748  if (srid == SRID_INVALID) return NULL;
749 
750  /* A.Intersection(Empty) == Empty */
751  if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom2); /* match empty type? */
752 
753  /* Empty.Intersection(A) == Empty */
754  if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom1); /* match empty type? */
755 
756  initGEOS(lwnotice, lwgeom_geos_error);
757 
758  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
759  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
760 
761  if ( prec >= 0) {
762 #if POSTGIS_GEOS_VERSION < 30900
763  lwgeom_geos_error_minversion("Fixed-precision intersection", "3.9");
764  GEOS_FREE_AND_FAIL(g1, g2);
765  return NULL;
766 #else
767  g3 = GEOSIntersectionPrec(g1, g2, prec);
768 #endif
769  }
770  else
771  {
772  g3 = GEOSIntersection(g1, g2);
773  }
774 
775  if (!g3) GEOS_FREE_AND_FAIL(g1);
776  GEOSSetSRID(g3, srid);
777 
778  if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g2, g3);
779 
780  GEOS_FREE(g1, g2, g3);
781  return result;
782 }
783 
784 LWGEOM*
786 {
787  return lwgeom_linemerge_directed(geom, LW_FALSE);
788 }
789 
790 LWGEOM*
791 lwgeom_linemerge_directed(const LWGEOM* geom, int directed)
792 {
793  LWGEOM* result;
794  int32_t srid = RESULT_SRID(geom);
795  uint8_t is3d = FLAGS_GET_Z(geom->flags);
796  GEOSGeometry* g1;
797  GEOSGeometry* g3;
798 
799  if (srid == SRID_INVALID) return NULL;
800 
801  /* Empty.Linemerge() == Empty */
802  if (lwgeom_is_empty(geom)) return lwgeom_clone_deep(geom); /* match empty type to linestring? */
803 
804  initGEOS(lwnotice, lwgeom_geos_error);
805 
806  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
807 
808  if (directed)
809  {
810 #if POSTGIS_GEOS_VERSION < 31100
811  lwgeom_geos_error_minversion("Directed line merging", "3.11");
812  GEOS_FREE_AND_FAIL(g1);
813  return NULL;
814 #else
815  g3 = GEOSLineMergeDirected(g1);
816 #endif
817  }
818  else
819  {
820  g3 = GEOSLineMerge(g1);
821  }
822 
823  if (!g3) GEOS_FREE_AND_FAIL(g1);
824  GEOSSetSRID(g3, srid);
825 
826  if (!(result = GEOS2LWGEOM(g3, is3d)))
827  GEOS_FREE_AND_FAIL(g1, g3);
828 
829  GEOS_FREE(g1, g3);
830 
831  return result;
832 }
833 
834 LWGEOM*
836 {
837  return lwgeom_unaryunion_prec(geom, -1.0);
838 }
839 
840 LWGEOM*
841 lwgeom_unaryunion_prec(const LWGEOM* geom, double prec)
842 {
843  LWGEOM* result;
844  int32_t srid = RESULT_SRID(geom);
845  uint8_t is3d = FLAGS_GET_Z(geom->flags);
846  GEOSGeometry* g1;
847  GEOSGeometry* g3;
848 
849  if (srid == SRID_INVALID) return NULL;
850 
851  /* Empty.UnaryUnion() == Empty */
852  if (lwgeom_is_empty(geom)) return lwgeom_clone_deep(geom);
853 
854  initGEOS(lwnotice, lwgeom_geos_error);
855 
856  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
857 
858  if ( prec >= 0) {
859 #if POSTGIS_GEOS_VERSION < 30900
860  lwgeom_geos_error_minversion("Fixed-precision unary union", "3.9");
861  GEOS_FREE_AND_FAIL(g1);
862  return NULL;
863 #else
864  g3 = GEOSUnaryUnionPrec(g1, prec);
865 #endif
866  }
867  else
868  {
869  g3 = GEOSUnaryUnion(g1);
870  }
871 
872  if (!g3) GEOS_FREE_AND_FAIL(g1);
873  GEOSSetSRID(g3, srid);
874 
875  if (!(result = GEOS2LWGEOM(g3, is3d)))
876  GEOS_FREE_AND_FAIL(g1, g3);
877 
878  GEOS_FREE(g1, g3);
879 
880  return result;
881 }
882 
883 LWGEOM*
884 lwgeom_difference(const LWGEOM* geom1, const LWGEOM* geom2)
885 {
886  return lwgeom_difference_prec(geom1, geom2, -1.0);
887 }
888 
889 LWGEOM*
890 lwgeom_difference_prec(const LWGEOM* geom1, const LWGEOM* geom2, double prec)
891 {
892  LWGEOM* result;
893  int32_t srid = RESULT_SRID(geom1, geom2);
894  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
895  GEOSGeometry *g1, *g2, *g3;
896 
897  if (srid == SRID_INVALID) return NULL;
898 
899  /* A.Intersection(Empty) == Empty */
900  if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1); /* match empty type? */
901 
902  /* Empty.Intersection(A) == Empty */
903  if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom1); /* match empty type? */
904 
905  initGEOS(lwnotice, lwgeom_geos_error);
906 
907  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
908  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
909 
910  if ( prec >= 0) {
911 #if POSTGIS_GEOS_VERSION < 30900
912  lwgeom_geos_error_minversion("Fixed-precision difference", "3.9");
913  GEOS_FREE_AND_FAIL(g1, g2);
914  return NULL;
915 #else
916  g3 = GEOSDifferencePrec(g1, g2, prec);
917 #endif
918  }
919  else
920  {
921  g3 = GEOSDifference(g1, g2);
922  }
923 
924  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
925  GEOSSetSRID(g3, srid);
926 
927  if (!(result = GEOS2LWGEOM(g3, is3d)))
928  GEOS_FREE_AND_FAIL(g1, g2, g3);
929 
930  GEOS_FREE(g1, g2, g3);
931  return result;
932 }
933 
934 LWGEOM*
935 lwgeom_symdifference(const LWGEOM* geom1, const LWGEOM* geom2)
936 {
937  return lwgeom_symdifference_prec(geom1, geom2, -1.0);
938 }
939 
940 LWGEOM*
941 lwgeom_symdifference_prec(const LWGEOM* geom1, const LWGEOM* geom2, double prec)
942 {
943  LWGEOM* result;
944  int32_t srid = RESULT_SRID(geom1, geom2);
945  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
946  GEOSGeometry *g1, *g2, *g3;
947 
948  if (srid == SRID_INVALID) return NULL;
949 
950  /* A.SymDifference(Empty) == A */
951  if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1);
952 
953  /* Empty.DymDifference(B) == B */
954  if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom2);
955 
956  initGEOS(lwnotice, lwgeom_geos_error);
957 
958  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
959  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
960 
961  if ( prec >= 0) {
962 #if POSTGIS_GEOS_VERSION < 30900
963  lwgeom_geos_error_minversion("Fixed-precision symdifference", "3.9");
964  GEOS_FREE_AND_FAIL(g1, g2);
965  return NULL;
966 #else
967  g3 = GEOSSymDifferencePrec(g1, g2, prec);
968 #endif
969  }
970  else
971  {
972  g3 = GEOSSymDifference(g1, g2);
973  }
974 
975  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
976  GEOSSetSRID(g3, srid);
977 
978  if (!(result = GEOS2LWGEOM(g3, is3d)))
979  GEOS_FREE_AND_FAIL(g1, g2, g3);
980 
981  GEOS_FREE(g1, g2, g3);
982  return result;
983 }
984 
985 LWGEOM*
987 {
988  LWGEOM* result;
989  int32_t srid = RESULT_SRID(geom);
990  uint8_t is3d = FLAGS_GET_Z(geom->flags);
991  GEOSGeometry *g1, *g3;
992 
993  if (srid == SRID_INVALID) return NULL;
994 
995  if (lwgeom_is_empty(geom))
996  {
997  LWPOINT* lwp = lwpoint_construct_empty(srid, is3d, lwgeom_has_m(geom));
998  return lwpoint_as_lwgeom(lwp);
999  }
1000 
1001  initGEOS(lwnotice, lwgeom_geos_error);
1002 
1003  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1004 
1005  g3 = GEOSGetCentroid(g1);
1006 
1007  if (!g3) GEOS_FREE_AND_FAIL(g1);
1008  GEOSSetSRID(g3, srid);
1009 
1010  if (!(result = GEOS2LWGEOM(g3, is3d)))
1011  GEOS_FREE_AND_FAIL(g1);
1012 
1013  GEOS_FREE(g1, g3);
1014 
1015  return result;
1016 }
1017 
1018 LWGEOM*
1019 lwgeom_reduceprecision(const LWGEOM* geom, double gridSize)
1020 {
1021 #if POSTGIS_GEOS_VERSION < 30900
1022  lwgeom_geos_error_minversion("Precision reduction", "3.9");
1023  return NULL;
1024 #else
1025 
1026  LWGEOM* result;
1027  int32_t srid = RESULT_SRID(geom);
1028  uint8_t is3d = FLAGS_GET_Z(geom->flags);
1029  GEOSGeometry *g1, *g3;
1030 
1031  if (srid == SRID_INVALID) return NULL;
1032 
1033  if (lwgeom_is_empty(geom))
1034  return lwgeom_clone_deep(geom);
1035 
1036  initGEOS(lwnotice, lwgeom_geos_error);
1037 
1038  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1039 
1040  g3 = GEOSGeom_setPrecision(g1, gridSize, 0);
1041 
1042  if (!g3) GEOS_FREE_AND_FAIL(g1);
1043  GEOSSetSRID(g3, srid);
1044 
1045  if (!(result = GEOS2LWGEOM(g3, is3d)))
1046  GEOS_FREE_AND_FAIL(g1);
1047 
1048  GEOS_FREE(g1, g3);
1049 
1050  return result;
1051 #endif
1052 }
1053 
1054 LWGEOM *
1056 {
1057  LWGEOM *result;
1058  int32_t srid = RESULT_SRID(geom);
1059  uint8_t is3d = FLAGS_GET_Z(geom->flags);
1060  GEOSGeometry *g1, *g3;
1061 
1062  if (srid == SRID_INVALID) return NULL;
1063 
1064  if (lwgeom_is_empty(geom))
1065  {
1066  LWPOINT *lwp = lwpoint_construct_empty(srid, is3d, lwgeom_has_m(geom));
1067  return lwpoint_as_lwgeom(lwp);
1068  }
1069 
1070  initGEOS(lwnotice, lwgeom_geos_error);
1071 
1072  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1073 
1074  g3 = GEOSPointOnSurface(g1);
1075 
1076  if (!g3) GEOS_FREE_AND_FAIL(g1);
1077  GEOSSetSRID(g3, srid);
1078 
1079  if (!(result = GEOS2LWGEOM(g3, is3d)))
1080  GEOS_FREE_AND_FAIL(g1, g3);
1081 
1082  GEOS_FREE(g1, g3);
1083 
1084  return result;
1085 }
1086 
1087 LWGEOM*
1088 lwgeom_union(const LWGEOM* g1, const LWGEOM* g2)
1089 {
1090  return lwgeom_union_prec(g1, g2, -1.0);
1091 }
1092 
1093 LWGEOM*
1094 lwgeom_union_prec(const LWGEOM* geom1, const LWGEOM* geom2, double gridSize)
1095 {
1096  LWGEOM* result;
1097  int32_t srid = RESULT_SRID(geom1, geom2);
1098  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
1099  GEOSGeometry *g1, *g2, *g3;
1100 
1101  if (srid == SRID_INVALID) return NULL;
1102 
1103  /* A.Union(empty) == A */
1104  if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom2);
1105 
1106  /* B.Union(empty) == B */
1107  if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1);
1108 
1109  initGEOS(lwnotice, lwgeom_geos_error);
1110 
1111  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
1112  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
1113 
1114  if ( gridSize >= 0) {
1115 #if POSTGIS_GEOS_VERSION < 30900
1116  lwgeom_geos_error_minversion("Fixed-precision union", "3.9");
1117  GEOS_FREE_AND_FAIL(g1, g2);
1118  return NULL;
1119 #else
1120  g3 = GEOSUnionPrec(g1, g2, gridSize);
1121 #endif
1122  }
1123  else
1124  {
1125  g3 = GEOSUnion(g1, g2);
1126  }
1127 
1128  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
1129  GEOSSetSRID(g3, srid);
1130 
1131  if (!(result = GEOS2LWGEOM(g3, is3d)))
1132  GEOS_FREE_AND_FAIL(g1, g2, g3);
1133 
1134  GEOS_FREE(g1, g2, g3);
1135  return result;
1136 }
1137 
1138 LWGEOM *
1139 lwgeom_clip_by_rect(const LWGEOM *geom1, double x1, double y1, double x2, double y2)
1140 {
1141  LWGEOM *result;
1142  GEOSGeometry *g1, *g3;
1143  int is3d;
1144 
1145  /* A.Intersection(Empty) == Empty */
1146  if ( lwgeom_is_empty(geom1) )
1147  return lwgeom_clone_deep(geom1);
1148 
1149  is3d = FLAGS_GET_Z(geom1->flags);
1150 
1151  initGEOS(lwnotice, lwgeom_geos_error);
1152 
1153  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX)))
1154  GEOS_FAIL_DEBUG();
1155 
1156  if (!(g3 = GEOSClipByRect(g1, x1, y1, x2, y2)))
1158 
1159  GEOS_FREE(g1);
1160  result = GEOS2LWGEOM(g3, is3d);
1161  GEOS_FREE(g3);
1162 
1163  if (!result)
1164  GEOS_FAIL_DEBUG();
1165 
1166  result->srid = geom1->srid;
1167 
1168  return result;
1169 }
1170 
1171 /* ------------ BuildArea stuff ---------------------------------------------------------------------{ */
1172 #if POSTGIS_GEOS_VERSION < 30800
1173 typedef struct Face_t
1174 {
1175  const GEOSGeometry* geom;
1176  GEOSGeometry* env;
1177  double envarea;
1178  struct Face_t* parent; /* if this face is an hole of another one, or NULL */
1179 } Face;
1180 
1181 static Face* newFace(const GEOSGeometry* g);
1182 static void delFace(Face* f);
1183 static unsigned int countParens(const Face* f);
1184 static void findFaceHoles(Face** faces, int nfaces);
1185 
1186 static Face*
1187 newFace(const GEOSGeometry* g)
1188 {
1189  Face* f = lwalloc(sizeof(Face));
1190  f->geom = g;
1191  f->env = GEOSEnvelope(f->geom);
1192  GEOSArea(f->env, &f->envarea);
1193  f->parent = NULL;
1194  return f;
1195 }
1196 
1197 static unsigned int
1198 countParens(const Face* f)
1199 {
1200  unsigned int pcount = 0;
1201  while (f->parent)
1202  {
1203  ++pcount;
1204  f = f->parent;
1205  }
1206  return pcount;
1207 }
1208 
1209 /* Destroy the face and release memory associated with it */
1210 static void
1211 delFace(Face* f)
1212 {
1213  GEOSGeom_destroy(f->env);
1214  lwfree(f);
1215 }
1216 
1217 static int
1218 compare_by_envarea(const void* g1, const void* g2)
1219 {
1220  Face* f1 = *(Face**)g1;
1221  Face* f2 = *(Face**)g2;
1222  double n1 = f1->envarea;
1223  double n2 = f2->envarea;
1224 
1225  if (n1 < n2) return 1;
1226  if (n1 > n2) return -1;
1227  return 0;
1228 }
1229 
1230 /* Find holes of each face */
1231 static void
1232 findFaceHoles(Face** faces, int nfaces)
1233 {
1234  int i, j, h;
1235 
1236  /* We sort by envelope area so that we know holes are only after their shells */
1237  qsort(faces, nfaces, sizeof(Face*), compare_by_envarea);
1238  for (i = 0; i < nfaces; ++i)
1239  {
1240  Face* f = faces[i];
1241  int nholes = GEOSGetNumInteriorRings(f->geom);
1242  LWDEBUGF(2, "Scanning face %d with env area %g and %d holes", i, f->envarea, nholes);
1243  for (h = 0; h < nholes; ++h)
1244  {
1245  const GEOSGeometry* hole = GEOSGetInteriorRingN(f->geom, h);
1246  LWDEBUGF(2,
1247  "Looking for hole %d/%d of face %d among %d other faces",
1248  h + 1,
1249  nholes,
1250  i,
1251  nfaces - i - 1);
1252  for (j = i + 1; j < nfaces; ++j)
1253  {
1254  const GEOSGeometry* f2er;
1255  Face* f2 = faces[j];
1256  if (f2->parent) continue; /* hole already assigned */
1257  f2er = GEOSGetExteriorRing(f2->geom);
1258  /* TODO: can be optimized as the ring would have the same vertices, possibly in
1259  * different order. Maybe comparing number of points could already be useful. */
1260  if (GEOSEquals(f2er, hole))
1261  {
1262  LWDEBUGF(2, "Hole %d/%d of face %d is face %d", h + 1, nholes, i, j);
1263  f2->parent = f;
1264  break;
1265  }
1266  }
1267  }
1268  }
1269 }
1270 
1271 static GEOSGeometry*
1272 collectFacesWithEvenAncestors(Face** faces, int nfaces)
1273 {
1274  GEOSGeometry** geoms = lwalloc(sizeof(GEOSGeometry*) * nfaces);
1275  GEOSGeometry* ret;
1276  unsigned int ngeoms = 0;
1277  int i;
1278 
1279  for (i = 0; i < nfaces; ++i)
1280  {
1281  Face* f = faces[i];
1282  if (countParens(f) % 2) continue; /* we skip odd parents geoms */
1283  geoms[ngeoms++] = GEOSGeom_clone(f->geom);
1284  }
1285 
1286  ret = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, geoms, ngeoms);
1287  lwfree(geoms);
1288  return ret;
1289 }
1290 
1291 GEOSGeometry*
1292 LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in)
1293 {
1294  GEOSGeometry* tmp;
1295  GEOSGeometry *geos_result, *shp;
1296  GEOSGeometry const* vgeoms[1];
1297  uint32_t i, ngeoms;
1298  int srid = GEOSGetSRID(geom_in);
1299  Face** geoms;
1300 #if POSTGIS_DEBUG_LEVEL >= 3
1301  LWGEOM *geos_geom;
1302  char *geom_ewkt;
1303 #endif
1304 
1305  vgeoms[0] = geom_in;
1306  geos_result = GEOSPolygonize(vgeoms, 1);
1307 
1308  LWDEBUGF(3, "GEOSpolygonize returned @ %p", geos_result);
1309 
1310  /* Null return from GEOSpolygonize (an exception) */
1311  if (!geos_result) return 0;
1312 
1313  /* We should now have a collection */
1314 #if PARANOIA_LEVEL > 0
1315  if (GEOSGeomTypeId(geos_result) != COLLECTIONTYPE)
1316  {
1317  GEOSGeom_destroy(geos_result);
1318  lwerror("%s [%d] Unexpected return from GEOSpolygonize", __FILE__, __LINE__);
1319  return 0;
1320  }
1321 #endif
1322 
1323  ngeoms = GEOSGetNumGeometries(geos_result);
1324 
1325 #if POSTGIS_DEBUG_LEVEL >= 3
1326  LWDEBUGF(3, "GEOSpolygonize: ngeoms in polygonize output: %d", ngeoms);
1327  geos_geom = GEOS2LWGEOM(geos_result, 0);
1328  geom_ewkt = lwgeom_to_ewkt(geos_geom);
1329  LWDEBUGF(3, "GEOSpolygonize: polygonized:%s", geom_ewkt);
1330  lwgeom_free(geos_geom);
1331  lwfree(geom_ewkt);
1332 #endif
1333 
1334  /* No geometries in collection, early out */
1335  if (ngeoms == 0)
1336  {
1337  GEOSSetSRID(geos_result, srid);
1338  return geos_result;
1339  }
1340 
1341  /* Return first geometry if we only have one in collection, to avoid the unnecessary Geometry clone below. */
1342  if (ngeoms == 1)
1343  {
1344  tmp = (GEOSGeometry*)GEOSGetGeometryN(geos_result, 0);
1345  if (!tmp)
1346  {
1347  GEOSGeom_destroy(geos_result);
1348  return 0; /* exception */
1349  }
1350  shp = GEOSGeom_clone(tmp);
1351  GEOSGeom_destroy(geos_result); /* only safe after the clone above */
1352  GEOSSetSRID(shp, srid);
1353  return shp;
1354  }
1355 
1356  LWDEBUGF(2, "Polygonize returned %d geoms", ngeoms);
1357 
1358  /*
1359  * Polygonizer returns a polygon for each face in the built topology.
1360  *
1361  * This means that for any face with holes we'll have other faces representing each hole. We can imagine a
1362  * parent-child relationship between these faces.
1363  *
1364  * In order to maximize the number of visible rings in output we only use those faces which have an even number
1365  * of parents.
1366  *
1367  * Example:
1368  *
1369  * +---------------+
1370  * | L0 | L0 has no parents
1371  * | +---------+ |
1372  * | | L1 | | L1 is an hole of L0
1373  * | | +---+ | |
1374  * | | |L2 | | | L2 is an hole of L1 (which is an hole of L0)
1375  * | | | | | |
1376  * | | +---+ | |
1377  * | +---------+ |
1378  * | |
1379  * +---------------+
1380  *
1381  * See http://trac.osgeo.org/postgis/ticket/1806
1382  *
1383  */
1384 
1385  /* Prepare face structures for later analysis */
1386  geoms = lwalloc(sizeof(Face**) * ngeoms);
1387  for (i = 0; i < ngeoms; ++i)
1388  geoms[i] = newFace(GEOSGetGeometryN(geos_result, i));
1389 
1390  /* Find faces representing other faces holes */
1391  findFaceHoles(geoms, ngeoms);
1392 
1393  /* Build a MultiPolygon composed only by faces with an even number of ancestors */
1394  tmp = collectFacesWithEvenAncestors(geoms, ngeoms);
1395 
1396  /* Cleanup face structures */
1397  for (i = 0; i < ngeoms; ++i)
1398  delFace(geoms[i]);
1399  lwfree(geoms);
1400 
1401  /* Faces referenced memory owned by geos_result. It is safe to destroy geos_result after deleting them. */
1402  GEOSGeom_destroy(geos_result);
1403 
1404  /* Run a single overlay operation to dissolve shared edges */
1405  shp = GEOSUnionCascaded(tmp);
1406  if (!shp)
1407  {
1408  GEOSGeom_destroy(tmp);
1409  return 0; /* exception */
1410  }
1411 
1412  GEOSGeom_destroy(tmp);
1413 
1414  GEOSSetSRID(shp, srid);
1415 
1416  return shp;
1417 }
1418 #endif
1419 
1420 LWGEOM*
1422 {
1423  LWGEOM* result;
1424  int32_t srid = RESULT_SRID(geom);
1425  uint8_t is3d = FLAGS_GET_Z(geom->flags);
1426  GEOSGeometry *g1, *g3;
1427 
1428  if (srid == SRID_INVALID) return NULL;
1429 
1430  /* Can't build an area from an empty! */
1431  if (lwgeom_is_empty(geom)) return (LWGEOM*)lwpoly_construct_empty(srid, is3d, 0);
1432 
1433  initGEOS(lwnotice, lwgeom_geos_error);
1434 
1435  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1436 
1437 #if POSTGIS_GEOS_VERSION < 30800
1438  g3 = LWGEOM_GEOS_buildArea(g1);
1439 #else
1440  g3 = GEOSBuildArea(g1);
1441 #endif
1442 
1443  if (!g3) GEOS_FREE_AND_FAIL(g1);
1444  GEOSSetSRID(g3, srid);
1445 
1446  /* If no geometries are in result collection, return NULL */
1447  if (GEOSGetNumGeometries(g3) == 0)
1448  {
1449  GEOS_FREE(g1);
1450  return NULL;
1451  }
1452 
1453  if (!(result = GEOS2LWGEOM(g3, is3d)))
1454  GEOS_FREE_AND_FAIL(g1, g3);
1455 
1456  GEOS_FREE(g1, g3);
1457 
1458  return result;
1459 }
1460 
1461 /* ------------ end of BuildArea stuff ---------------------------------------------------------------------} */
1462 
1463 int
1465 {
1466  GEOSGeometry* g;
1467  int simple;
1468 
1469  /* Empty is always simple */
1470  if (lwgeom_is_empty(geom)) return LW_TRUE;
1471 
1472  initGEOS(lwnotice, lwgeom_geos_error);
1473 
1474  if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) return -1;
1475 
1476  simple = GEOSisSimple(g);
1477  GEOSGeom_destroy(g);
1478 
1479  if (simple == 2) /* exception thrown */
1480  {
1481  lwerror("lwgeom_is_simple: %s", lwgeom_geos_errmsg);
1482  return -1;
1483  }
1484 
1485  return simple ? LW_TRUE : LW_FALSE;
1486 }
1487 
1488 LWGEOM*
1490 {
1491  LWGEOM* result;
1492  int32_t srid = RESULT_SRID(geom);
1493  uint8_t is3d = FLAGS_GET_Z(geom->flags);
1494  GEOSGeometry* g;
1495 
1496  if (srid == SRID_INVALID) return NULL;
1497 
1498  initGEOS(lwnotice, lwgeom_geos_error);
1499 
1500  if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1501 
1502  if (!g) GEOS_FREE_AND_FAIL(g);
1503  GEOSSetSRID(g, srid);
1504 
1505  if (!(result = GEOS2LWGEOM(g, is3d)))
1506  GEOS_FREE_AND_FAIL(g);
1507 
1508  GEOS_FREE(g);
1509 
1510  return result;
1511 }
1512 
1513 LWGEOM*
1514 lwgeom_snap(const LWGEOM* geom1, const LWGEOM* geom2, double tolerance)
1515 {
1516  LWGEOM* result;
1517  int32_t srid = RESULT_SRID(geom1, geom2);
1518  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
1519  GEOSGeometry *g1, *g2, *g3;
1520 
1521  if (srid == SRID_INVALID) return NULL;
1522 
1523  initGEOS(lwnotice, lwgeom_geos_error);
1524 
1525  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
1526  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
1527 
1528  g3 = GEOSSnap(g1, g2, tolerance);
1529 
1530  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
1531  GEOSSetSRID(g3, srid);
1532 
1533  if (!(result = GEOS2LWGEOM(g3, is3d)))
1534  GEOS_FREE_AND_FAIL(g1, g2, g3);
1535 
1536  GEOS_FREE(g1, g2, g3);
1537  return result;
1538 }
1539 
1540 LWGEOM*
1541 lwgeom_sharedpaths(const LWGEOM* geom1, const LWGEOM* geom2)
1542 {
1543  LWGEOM* result;
1544  int32_t srid = RESULT_SRID(geom1, geom2);
1545  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
1546  GEOSGeometry *g1, *g2, *g3;
1547 
1548  if (srid == SRID_INVALID) return NULL;
1549 
1550  initGEOS(lwnotice, lwgeom_geos_error);
1551 
1552  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
1553  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
1554 
1555  g3 = GEOSSharedPaths(g1, g2);
1556 
1557  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
1558  GEOSSetSRID(g3, srid);
1559 
1560  if (!(result = GEOS2LWGEOM(g3, is3d)))
1561  GEOS_FREE_AND_FAIL(g1, g2, g3);
1562 
1563  GEOS_FREE(g1, g2, g3);
1564  return result;
1565 }
1566 
1567 static LWGEOM *
1568 lwline_offsetcurve(const LWLINE *lwline, double size, int quadsegs, int joinStyle, double mitreLimit)
1569 {
1570  LWGEOM* result;
1571  LWGEOM* geom = lwline_as_lwgeom(lwline);
1572  int32_t srid = RESULT_SRID(geom);
1573  uint8_t is3d = FLAGS_GET_Z(geom->flags);
1574  GEOSGeometry *g1, *g3;
1575 
1576  if (srid == SRID_INVALID) return NULL;
1577 
1578  initGEOS(lwnotice, lwgeom_geos_error);
1579 
1580  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1581 
1582  g3 = GEOSOffsetCurve(g1, size, quadsegs, joinStyle, mitreLimit);
1583 
1584  if (!g3)
1585  {
1586  GEOS_FREE(g1);
1587  return NULL;
1588  }
1589 
1590  GEOSSetSRID(g3, srid);
1591 
1592  if (!(result = GEOS2LWGEOM(g3, is3d)))
1593  GEOS_FREE_AND_FAIL(g1, g3);
1594 
1595  GEOS_FREE(g1, g3);
1596  return result;
1597 }
1598 
1599 static LWGEOM *
1600 lwcollection_offsetcurve(const LWCOLLECTION *col, double size, int quadsegs, int joinStyle, double mitreLimit)
1601 {
1602  const LWGEOM *geom = lwcollection_as_lwgeom(col);
1603  int32_t srid = RESULT_SRID(geom);
1604  uint8_t is3d = FLAGS_GET_Z(col->flags);
1606  LWGEOM *tmp;
1607  uint32_t i;
1608  if (srid == SRID_INVALID) return NULL;
1609 
1611 
1612  for (i = 0; i < col->ngeoms; i++)
1613  {
1614  tmp = lwgeom_offsetcurve(col->geoms[i], size, quadsegs, joinStyle, mitreLimit);
1615 
1616  if (!tmp)
1617  {
1619  return NULL;
1620  }
1621 
1622  if (!lwgeom_is_empty(tmp))
1623  {
1624  if (lwgeom_is_collection(tmp))
1626  else
1628 
1629  if (!result)
1630  {
1631  lwgeom_free(tmp);
1632  return NULL;
1633  }
1634  }
1635  }
1636 
1637  if (result->ngeoms == 1)
1638  {
1639  tmp = result->geoms[0];
1641  return tmp;
1642  }
1643  else
1645 }
1646 
1647 LWGEOM*
1648 lwgeom_offsetcurve(const LWGEOM* geom, double size, int quadsegs, int joinStyle, double mitreLimit)
1649 {
1650  int32_t srid = RESULT_SRID(geom);
1651  LWGEOM *result = NULL;
1652  LWGEOM *noded = NULL;
1653  if (srid == SRID_INVALID) return NULL;
1654 
1655  if (lwgeom_dimension(geom) != 1)
1656  {
1657  lwerror("%s: input is not linear", __func__, lwtype_name(geom->type));
1658  return NULL;
1659  }
1660 
1661  while (!result)
1662  {
1663  switch (geom->type)
1664  {
1665  case LINETYPE:
1666  result = lwline_offsetcurve(lwgeom_as_lwline(geom), size, quadsegs, joinStyle, mitreLimit);
1667  break;
1668  case COLLECTIONTYPE:
1669  case MULTILINETYPE:
1670  result = lwcollection_offsetcurve(lwgeom_as_lwcollection(geom), size, quadsegs, joinStyle, mitreLimit);
1671  break;
1672  default:
1673  lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type));
1674  return NULL;
1675  }
1676 
1677  if (result)
1678  {
1679  if (noded) lwgeom_free(noded);
1680  return result;
1681  }
1682  else if (!noded)
1683  {
1684  noded = lwgeom_node(geom);
1685  if (!noded)
1686  {
1687  lwerror("lwgeom_offsetcurve: cannot node input");
1688  return NULL;
1689  }
1690  geom = noded;
1691  }
1692  else
1693  {
1694  lwgeom_free(noded);
1695  lwerror("lwgeom_offsetcurve: noded geometry cannot be offset");
1696  return NULL;
1697  }
1698  }
1699 
1700  return result;
1701 }
1702 
1703 LWMPOINT*
1704 lwpoly_to_points(const LWPOLY* lwpoly, uint32_t npoints, int32_t seed)
1705 {
1706  double area, bbox_area, bbox_width, bbox_height;
1707  GBOX bbox;
1708  const LWGEOM* lwgeom = (LWGEOM*)lwpoly;
1709  uint32_t sample_npoints, sample_sqrt, sample_width, sample_height;
1710  double sample_cell_size;
1711  uint32_t i, j, n;
1712  uint32_t iterations = 0;
1713  uint32_t npoints_generated = 0;
1714  uint32_t npoints_tested = 0;
1715  GEOSGeometry* g;
1716  const GEOSPreparedGeometry* gprep;
1717  GEOSGeometry* gpt;
1718  GEOSCoordSequence* gseq;
1719  LWMPOINT* mpt;
1720  int32_t srid = lwgeom_get_srid(lwgeom);
1721  int done = 0;
1722  int* cells;
1723  const size_t size = 2 * sizeof(int);
1724  char tmp[2 * sizeof(int)];
1725  const size_t stride = 2 * sizeof(int);
1726 
1727  if (lwgeom_get_type(lwgeom) != POLYGONTYPE)
1728  {
1729  lwerror("%s: only polygons supported", __func__);
1730  return NULL;
1731  }
1732 
1733  if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL;
1734 
1735  if (!lwpoly->bbox)
1736  lwgeom_calculate_gbox(lwgeom, &bbox);
1737  else
1738  bbox = *(lwpoly->bbox);
1739 
1740  area = lwpoly_area(lwpoly);
1741  bbox_width = bbox.xmax - bbox.xmin;
1742  bbox_height = bbox.ymax - bbox.ymin;
1743  bbox_area = bbox_width * bbox_height;
1744 
1745  if (area == 0.0 || bbox_area == 0.0)
1746  {
1747  lwerror("%s: zero area input polygon, TBD", __func__);
1748  return NULL;
1749  }
1750 
1751  /* Gross up our test set a bit to increase odds of getting coverage in one pass */
1752  sample_npoints = npoints * bbox_area / area;
1753 
1754  /* We're going to generate points using a sample grid as described
1755  * http://lin-ear-th-inking.blogspot.ca/2010/05/more-random-points-in-jts.html to try and get a more uniform
1756  * "random" set of points. So we have to figure out how to stick a grid into our box */
1757  sample_sqrt = lround(sqrt(sample_npoints));
1758  if (sample_sqrt == 0)
1759  sample_sqrt = 1;
1760 
1761  /* Calculate the grids we're going to randomize within */
1762  if (bbox_width > bbox_height)
1763  {
1764  sample_width = sample_sqrt;
1765  sample_height = ceil((double)sample_npoints / (double)sample_width);
1766  sample_cell_size = bbox_width / sample_width;
1767  }
1768  else
1769  {
1770  sample_height = sample_sqrt;
1771  sample_width = ceil((double)sample_npoints / (double)sample_height);
1772  sample_cell_size = bbox_height / sample_height;
1773  }
1774 
1775  /* Prepare the polygon for fast true/false testing */
1776  initGEOS(lwnotice, lwgeom_geos_error);
1777  g = (GEOSGeometry*)LWGEOM2GEOS(lwgeom, 0);
1778  if (!g)
1779  {
1780  lwerror("%s: Geometry could not be converted to GEOS: %s", __func__, lwgeom_geos_errmsg);
1781  return NULL;
1782  }
1783  gprep = GEOSPrepare(g);
1784 
1785  /* Get an empty multi-point ready to return */
1786  mpt = lwmpoint_construct_empty(srid, 0, 0);
1787 
1788  /* Initiate random number generator.
1789  * Repeatable numbers are generated with seed values >= 1.
1790  * When seed is zero and has not previously been set, it is based on
1791  * Unix time (seconds) and process ID. */
1792  lwrandom_set_seed(seed);
1793 
1794  /* Now we fill in an array of cells, and then shuffle that array, */
1795  /* so we can visit the cells in random order to avoid visual ugliness */
1796  /* caused by visiting them sequentially */
1797  cells = lwalloc(2 * sizeof(int) * sample_height * sample_width);
1798  for (i = 0; i < sample_width; i++)
1799  {
1800  for (j = 0; j < sample_height; j++)
1801  {
1802  cells[2 * (i * sample_height + j)] = i;
1803  cells[2 * (i * sample_height + j) + 1] = j;
1804  }
1805  }
1806 
1807  /* Fisher-Yates shuffle */
1808  n = sample_height * sample_width;
1809  if (n > 1)
1810  {
1811  for (i = n - 1; i > 0; i--)
1812  {
1813  size_t j = (size_t)(lwrandom_uniform() * (i + 1));
1814 
1815  memcpy(tmp, (char *)cells + j * stride, size);
1816  memcpy((char *)cells + j * stride, (char *)cells + i * stride, size);
1817  memcpy((char *)cells + i * stride, tmp, size);
1818  }
1819  }
1820 
1821  /* Start testing points */
1822  while (npoints_generated < npoints)
1823  {
1824  iterations++;
1825  for (i = 0; i < sample_width * sample_height; i++)
1826  {
1827  int contains = 0;
1828  double y = bbox.ymin + cells[2 * i] * sample_cell_size;
1829  double x = bbox.xmin + cells[2 * i + 1] * sample_cell_size;
1830  x += lwrandom_uniform() * sample_cell_size;
1831  y += lwrandom_uniform() * sample_cell_size;
1832  if (x >= bbox.xmax || y >= bbox.ymax) continue;
1833 
1834  gseq = GEOSCoordSeq_create(1, 2);
1835 #if POSTGIS_GEOS_VERSION < 30800
1836  GEOSCoordSeq_setX(gseq, 0, x);
1837  GEOSCoordSeq_setY(gseq, 0, y);
1838 #else
1839  GEOSCoordSeq_setXY(gseq, 0, x, y);
1840 #endif
1841  gpt = GEOSGeom_createPoint(gseq);
1842 
1843  contains = GEOSPreparedIntersects(gprep, gpt);
1844 
1845  GEOSGeom_destroy(gpt);
1846 
1847  if (contains == 2)
1848  {
1849  GEOSPreparedGeom_destroy(gprep);
1850  GEOSGeom_destroy(g);
1851  lwerror("%s: GEOS exception on PreparedContains: %s", __func__, lwgeom_geos_errmsg);
1852  return NULL;
1853  }
1854  if (contains)
1855  {
1856  npoints_generated++;
1857  mpt = lwmpoint_add_lwpoint(mpt, lwpoint_make2d(srid, x, y));
1858  if (npoints_generated == npoints)
1859  {
1860  done = 1;
1861  break;
1862  }
1863  }
1864 
1865  /* Short-circuit check for ctrl-c occasionally */
1866  npoints_tested++;
1867  if (npoints_tested % 10000 == 0)
1868  LW_ON_INTERRUPT(GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); return NULL);
1869 
1870  if (done) break;
1871  }
1872  if (done || iterations > 100) break;
1873  }
1874 
1875  GEOSPreparedGeom_destroy(gprep);
1876  GEOSGeom_destroy(g);
1877  lwfree(cells);
1878 
1879  return mpt;
1880 }
1881 
1882 /* Allocate points to sub-geometries by area, then call lwgeom_poly_to_points and bundle up final result in a single
1883  * multipoint. */
1884 LWMPOINT*
1885 lwmpoly_to_points(const LWMPOLY* lwmpoly, uint32_t npoints, int32_t seed)
1886 {
1887  const LWGEOM* lwgeom = (LWGEOM*)lwmpoly;
1888  double area;
1889  uint32_t i;
1890  LWMPOINT* mpt = NULL;
1891 
1892  if (lwgeom_get_type(lwgeom) != MULTIPOLYGONTYPE)
1893  {
1894  lwerror("%s: only multipolygons supported", __func__);
1895  return NULL;
1896  }
1897  if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL;
1898 
1899  area = lwgeom_area(lwgeom);
1900 
1901  for (i = 0; i < lwmpoly->ngeoms; i++)
1902  {
1903  double sub_area = lwpoly_area(lwmpoly->geoms[i]);
1904  int sub_npoints = lround(npoints * sub_area / area);
1905  if (sub_npoints > 0)
1906  {
1907  LWMPOINT* sub_mpt = lwpoly_to_points(lwmpoly->geoms[i], sub_npoints, seed);
1908  if (!mpt)
1909  mpt = sub_mpt;
1910  else
1911  {
1912  uint32_t j;
1913  for (j = 0; j < sub_mpt->ngeoms; j++)
1914  mpt = lwmpoint_add_lwpoint(mpt, sub_mpt->geoms[j]);
1915  /* Just free the shell, leave the underlying lwpoints alone, as they are now owned by
1916  * the returning multipoint */
1917  lwfree(sub_mpt->geoms);
1918  lwgeom_release((LWGEOM*)sub_mpt);
1919  }
1920  }
1921  }
1922  return mpt;
1923 }
1924 
1925 LWMPOINT*
1926 lwgeom_to_points(const LWGEOM* lwgeom, uint32_t npoints, int32_t seed)
1927 {
1928  switch (lwgeom_get_type(lwgeom))
1929  {
1930  case MULTIPOLYGONTYPE:
1931  return lwmpoly_to_points((LWMPOLY*)lwgeom, npoints, seed);
1932  case POLYGONTYPE:
1933  return lwpoly_to_points((LWPOLY*)lwgeom, npoints, seed);
1934  default:
1935  lwerror("%s: unsupported geometry type '%s'", __func__, lwtype_name(lwgeom_get_type(lwgeom)));
1936  return NULL;
1937  }
1938 }
1939 
1940 LWTIN*
1941 lwtin_from_geos(const GEOSGeometry* geom, uint8_t want3d)
1942 {
1943  int type = GEOSGeomTypeId(geom);
1944  int SRID = GEOSGetSRID(geom);
1945 
1946  /* GEOS's 0 is equivalent to our unknown as for SRID values */
1947  if (SRID == 0) SRID = SRID_UNKNOWN;
1948 
1949  if (want3d && !GEOSHasZ(geom))
1950  {
1951  LWDEBUG(3, "Geometry has no Z, won't provide one");
1952  want3d = 0;
1953  }
1954 
1955  switch (type)
1956  {
1957  LWTRIANGLE** geoms;
1958  uint32_t i, ngeoms;
1959  case GEOS_GEOMETRYCOLLECTION:
1960  LWDEBUG(4, "lwgeom_from_geometry: it's a Collection or Multi");
1961 
1962  ngeoms = GEOSGetNumGeometries(geom);
1963  geoms = NULL;
1964  if (ngeoms)
1965  {
1966  geoms = lwalloc(ngeoms * sizeof *geoms);
1967  if (!geoms)
1968  {
1969  lwerror("lwtin_from_geos: can't allocate geoms");
1970  return NULL;
1971  }
1972  for (i = 0; i < ngeoms; i++)
1973  {
1974  const GEOSGeometry *poly, *ring;
1975  const GEOSCoordSequence* cs;
1976  POINTARRAY* pa;
1977 
1978  poly = GEOSGetGeometryN(geom, i);
1979  ring = GEOSGetExteriorRing(poly);
1980  cs = GEOSGeom_getCoordSeq(ring);
1981  pa = ptarray_from_GEOSCoordSeq(cs, want3d);
1982 
1983  geoms[i] = lwtriangle_construct(SRID, NULL, pa);
1984  }
1985  }
1986  return (LWTIN*)lwcollection_construct(TINTYPE, SRID, NULL, ngeoms, (LWGEOM**)geoms);
1987  case GEOS_POLYGON:
1988  case GEOS_MULTIPOINT:
1989  case GEOS_MULTILINESTRING:
1990  case GEOS_MULTIPOLYGON:
1991  case GEOS_LINESTRING:
1992  case GEOS_LINEARRING:
1993  case GEOS_POINT:
1994  lwerror("lwtin_from_geos: invalid geometry type for tin: %d", type);
1995  break;
1996 
1997  default:
1998  lwerror("GEOS2LWGEOM: unknown geometry type: %d", type);
1999  return NULL;
2000  }
2001 
2002  /* shouldn't get here */
2003  return NULL;
2004 }
2005 /*
2006  * output = 1 for edges, 2 for TIN, 0 for polygons
2007  */
2008 LWGEOM*
2009 lwgeom_delaunay_triangulation(const LWGEOM* geom, double tolerance, int32_t output)
2010 {
2011  LWGEOM* result;
2012  int32_t srid = RESULT_SRID(geom);
2013  uint8_t is3d = FLAGS_GET_Z(geom->flags);
2014  GEOSGeometry *g1, *g3;
2015 
2016  if (output < 0 || output > 2)
2017  {
2018  lwerror("%s: invalid output type specified %d", __func__, output);
2019  return NULL;
2020  }
2021 
2022  if (srid == SRID_INVALID) return NULL;
2023 
2024  initGEOS(lwnotice, lwgeom_geos_error);
2025 
2026  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
2027 
2028  /* if output != 1 we want polys */
2029  g3 = GEOSDelaunayTriangulation(g1, tolerance, output == 1);
2030 
2031  if (!g3) GEOS_FREE_AND_FAIL(g1);
2032  GEOSSetSRID(g3, srid);
2033 
2034  if (output == 2)
2035  {
2036  result = (LWGEOM*)lwtin_from_geos(g3, is3d);
2037  if (!result)
2038  {
2039  GEOS_FREE(g1, g3);
2040  lwerror("%s: cannot convert output geometry", __func__);
2041  return NULL;
2042  }
2043  lwgeom_set_srid(result, srid);
2044  }
2045  else if (!(result = GEOS2LWGEOM(g3, is3d)))
2046  GEOS_FREE_AND_FAIL(g1, g3);
2047 
2048  GEOS_FREE(g1, g3);
2049  return result;
2050 }
2051 
2052 
2053 static GEOSCoordSequence*
2054 lwgeom_get_geos_coordseq_2d(const LWGEOM* g, uint32_t num_points)
2055 {
2056  uint32_t i = 0;
2057  uint8_t num_dims = 2;
2058  LWPOINTITERATOR* it;
2059  GEOSCoordSequence* coords;
2060  POINT4D tmp;
2061 
2062  coords = GEOSCoordSeq_create(num_points, num_dims);
2063  if (!coords) return NULL;
2064 
2065  it = lwpointiterator_create(g);
2066  while (lwpointiterator_next(it, &tmp))
2067  {
2068  if (i >= num_points)
2069  {
2070  lwerror("Incorrect num_points provided to lwgeom_get_geos_coordseq_2d");
2071  GEOSCoordSeq_destroy(coords);
2073  return NULL;
2074  }
2075 
2076 #if POSTGIS_GEOS_VERSION < 30800
2077  if (!GEOSCoordSeq_setX(coords, i, tmp.x) || !GEOSCoordSeq_setY(coords, i, tmp.y))
2078 #else
2079  if (!GEOSCoordSeq_setXY(coords, i, tmp.x, tmp.y))
2080 #endif
2081  {
2082  GEOSCoordSeq_destroy(coords);
2084  return NULL;
2085  }
2086  i++;
2087  }
2089 
2090  return coords;
2091 }
2092 
2093 LWGEOM*
2094 lwgeom_voronoi_diagram(const LWGEOM* g, const GBOX* env, double tolerance, int output_edges)
2095 {
2096  uint32_t num_points = lwgeom_count_vertices(g);
2097  LWGEOM* lwgeom_result;
2098  char is_3d = LW_FALSE;
2099  int32_t srid = lwgeom_get_srid(g);
2100  GEOSCoordSequence* coords;
2101  GEOSGeometry* geos_geom;
2102  GEOSGeometry* geos_env = NULL;
2103  GEOSGeometry* geos_result;
2104 
2105  if (num_points < 2)
2106  {
2108  return lwcollection_as_lwgeom(empty);
2109  }
2110 
2111  initGEOS(lwnotice, lwgeom_geos_error);
2112 
2113  /* Instead of using the standard LWGEOM2GEOS transformer, we read the vertices of the LWGEOM directly and put
2114  * them into a single GEOS CoordinateSeq that can be used to define a LineString. This allows us to process
2115  * geometry types that may not be supported by GEOS, and reduces the memory requirements in cases of many
2116  * geometries with few points (such as LWMPOINT).*/
2117  coords = lwgeom_get_geos_coordseq_2d(g, num_points);
2118  if (!coords) return NULL;
2119 
2120  geos_geom = GEOSGeom_createLineString(coords);
2121  if (!geos_geom)
2122  {
2123  GEOSCoordSeq_destroy(coords);
2124  return NULL;
2125  }
2126 
2127  if (env) geos_env = GBOX2GEOS(env);
2128 
2129  geos_result = GEOSVoronoiDiagram(geos_geom, geos_env, tolerance, output_edges);
2130 
2131  GEOSGeom_destroy(geos_geom);
2132  if (env) GEOSGeom_destroy(geos_env);
2133 
2134  if (!geos_result)
2135  {
2136  lwerror("GEOSVoronoiDiagram: %s", lwgeom_geos_errmsg);
2137  return NULL;
2138  }
2139 
2140  lwgeom_result = GEOS2LWGEOM(geos_result, is_3d);
2141  GEOSGeom_destroy(geos_result);
2142 
2143  lwgeom_set_srid(lwgeom_result, srid);
2144 
2145  return lwgeom_result;
2146 }
2147 
2148 
2149 #if POSTGIS_GEOS_VERSION >= 31100
2150 LWGEOM*
2151 lwgeom_concavehull(const LWGEOM* geom, double ratio, uint32_t allow_holes)
2152 {
2153  LWGEOM* result;
2154  int32_t srid = RESULT_SRID(geom);
2155  uint8_t is3d = FLAGS_GET_Z(geom->flags);
2156  GEOSGeometry *g1, *g3;
2157  int geosGeomType;
2158 
2159  initGEOS(lwnotice, lwgeom_geos_error);
2160 
2161  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
2162 
2163  geosGeomType = GEOSGeomTypeId(g1);
2164  if (geosGeomType == GEOS_POLYGON || geosGeomType == GEOS_MULTIPOLYGON) {
2165  int is_tight = LW_FALSE;
2166  g3 = GEOSConcaveHullOfPolygons(g1, ratio, is_tight, allow_holes);
2167  }
2168  else {
2169  g3 = GEOSConcaveHull(g1, ratio, allow_holes);
2170  }
2171 
2172  if (!g3)
2173  GEOS_FREE_AND_FAIL(g1);
2174 
2175  GEOSSetSRID(g3, srid);
2176 
2177  if (!(result = GEOS2LWGEOM(g3, is3d)))
2178  GEOS_FREE_AND_FAIL(g1, g3);
2179 
2180  GEOS_FREE(g1, g3);
2181  return result;
2182 }
2183 
2184 LWGEOM*
2185 lwgeom_simplify_polygonal(const LWGEOM* geom, double vertex_fraction, uint32_t is_outer)
2186 {
2187  LWGEOM* result;
2188  int32_t srid = RESULT_SRID(geom);
2189  uint8_t is3d = FLAGS_GET_Z(geom->flags);
2190  GEOSGeometry *g1, *g3;
2191 
2192  initGEOS(lwnotice, lwgeom_geos_error);
2193 
2194  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
2195 
2196  g3 = GEOSPolygonHullSimplify(g1, is_outer, vertex_fraction);
2197 
2198  if (!g3)
2199  GEOS_FREE_AND_FAIL(g1);
2200 
2201  GEOSSetSRID(g3, srid);
2202 
2203  if (!(result = GEOS2LWGEOM(g3, is3d)))
2204  GEOS_FREE_AND_FAIL(g1, g3);
2205 
2206  GEOS_FREE(g1, g3);
2207  return result;
2208 }
2209 
2210 LWGEOM*
2212 {
2213  LWGEOM* result;
2214  int32_t srid = RESULT_SRID(geom);
2215  uint8_t is3d = FLAGS_GET_Z(geom->flags);
2216  GEOSGeometry *g1, *g3;
2217 
2218  if (srid == SRID_INVALID) return NULL;
2219 
2220  initGEOS(lwnotice, lwgeom_geos_error);
2221 
2222  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
2223 
2224  /* if output != 1 we want polys */
2225  g3 = GEOSConstrainedDelaunayTriangulation(g1);
2226 
2227  if (!g3) GEOS_FREE_AND_FAIL(g1);
2228  GEOSSetSRID(g3, srid);
2229 
2230  if (!(result = GEOS2LWGEOM(g3, is3d)))
2231  GEOS_FREE_AND_FAIL(g1, g3);
2232 
2233  GEOS_FREE(g1, g3);
2234  return result;
2235 }
2236 
2237 #endif
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:262
#define LWGEOM_GEOS_ERRMSG_MAXSIZE
#define GEOS_FREE(...)
LWGEOM * lwgeom_centroid(const LWGEOM *geom)
LWGEOM * lwgeom_intersection_prec(const LWGEOM *geom1, const LWGEOM *geom2, double prec)
LWGEOM * lwgeom_geos_noop(const LWGEOM *geom)
Convert an LWGEOM to a GEOS Geometry and convert back – for debug only.
static LWGEOM * lwcollection_offsetcurve(const LWCOLLECTION *col, double size, int quadsegs, int joinStyle, double mitreLimit)
LWGEOM * lwgeom_linemerge_directed(const LWGEOM *geom, int directed)
static GEOSGeometry * ptarray_to_GEOSLinearRing(const POINTARRAY *pa, uint8_t autofix)
LWGEOM * lwgeom_symdifference(const LWGEOM *geom1, const LWGEOM *geom2)
LWGEOM * lwgeom_concavehull(const LWGEOM *geom, double ratio, uint32_t allow_holes)
Take a geometry and build the concave hull.
LWGEOM * lwgeom_unaryunion(const LWGEOM *geom)
void lwgeom_geos_error_minversion(const char *functionality, const char *minver)
LWGEOM * lwgeom_union(const LWGEOM *g1, const LWGEOM *g2)
GEOSGeometry * make_geos_point(double x, double y)
#define AUTOFIX
static LWGEOM * lwline_offsetcurve(const LWLINE *lwline, double size, int quadsegs, int joinStyle, double mitreLimit)
LWGEOM * lwgeom_offsetcurve(const LWGEOM *geom, double size, int quadsegs, int joinStyle, double mitreLimit)
LWGEOM * lwgeom_delaunay_triangulation(const LWGEOM *geom, double tolerance, int32_t output)
Take vertices of a geometry and build a delaunay triangulation on them.
LWGEOM * lwgeom_difference_prec(const LWGEOM *geom1, const LWGEOM *geom2, double prec)
#define RESULT_SRID(...)
LWGEOM * lwgeom_snap(const LWGEOM *geom1, const LWGEOM *geom2, double tolerance)
Snap vertices and segments of a geometry to another using a given tolerance.
LWGEOM * lwgeom_difference(const LWGEOM *geom1, const LWGEOM *geom2)
#define GEOS_FAIL()
int lwgeom_is_simple(const LWGEOM *geom)
LWGEOM * lwgeom_pointonsurface(const LWGEOM *geom)
LWGEOM * lwgeom_sharedpaths(const LWGEOM *geom1, const LWGEOM *geom2)
LWGEOM * lwgeom_symdifference_prec(const LWGEOM *geom1, const LWGEOM *geom2, double prec)
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
const char * lwgeom_geos_compiled_version()
LWGEOM * lwgeom_simplify_polygonal(const LWGEOM *geom, double vertex_fraction, uint32_t is_outer)
Computes a boundary-respecting hull of a polygonal geometry, with hull shape determined by a target p...
LWGEOM * lwgeom_voronoi_diagram(const LWGEOM *g, const GBOX *env, double tolerance, int output_edges)
Take vertices of a geometry and build the Voronoi diagram.
LWGEOM * lwgeom_union_prec(const LWGEOM *geom1, const LWGEOM *geom2, double gridSize)
LWGEOM * lwgeom_normalize(const LWGEOM *geom)
static int32_t get_result_srid(size_t count, const char *funcname,...)
#define GEOS_FREE_AND_FAIL_DEBUG(...)
LWGEOM * lwgeom_buildarea(const LWGEOM *geom)
Take a geometry and return an areal geometry (Polygon or MultiPolygon).
#define GEOS_FAIL_DEBUG()
LWGEOM * lwgeom_linemerge(const LWGEOM *geom)
#define GEOS_FREE_AND_FAIL(...)
GEOSGeometry * make_geos_segment(double x1, double y1, double x2, double y2)
static void geos_destroy(size_t count,...)
GEOSGeometry * GBOX2GEOS(const GBOX *box)
const char * lwgeom_geos_version()
Return GEOS version string (not to be freed)
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
LWGEOM * lwgeom_triangulate_polygon(const LWGEOM *geom)
Take vertices of a polygon and build a constrained triangulation that respects the boundary of the po...
LWGEOM * lwgeom_reduceprecision(const LWGEOM *geom, double gridSize)
LWMPOINT * lwgeom_to_points(const LWGEOM *lwgeom, uint32_t npoints, int32_t seed)
LWGEOM * GEOS2LWGEOM(const GEOSGeometry *geom, uint8_t want3d)
GEOSCoordSeq ptarray_to_GEOSCoordSeq(const POINTARRAY *, uint8_t fix_ring)
LWMPOINT * lwmpoly_to_points(const LWMPOLY *lwmpoly, uint32_t npoints, int32_t seed)
LWGEOM * lwgeom_unaryunion_prec(const LWGEOM *geom, double prec)
POINTARRAY * ptarray_from_GEOSCoordSeq(const GEOSCoordSequence *cs, uint8_t want3d)
LWMPOINT * lwpoly_to_points(const LWPOLY *lwpoly, uint32_t npoints, int32_t seed)
LWGEOM * lwgeom_intersection(const LWGEOM *g1, const LWGEOM *g2)
LWGEOM * lwgeom_clip_by_rect(const LWGEOM *geom1, double x1, double y1, double x2, double y2)
void lwgeom_geos_error(const char *fmt,...)
static GEOSCoordSequence * lwgeom_get_geos_coordseq_2d(const LWGEOM *g, uint32_t num_points)
LWTIN * lwtin_from_geos(const GEOSGeometry *geom, uint8_t want3d)
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoint.c:151
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
#define LW_FALSE
Definition: liblwgeom.h:94
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:309
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:927
LWGEOM * lwgeom_node(const LWGEOM *lwgeom_in)
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition: lwpoint.c:163
#define SRID_INVALID
Definition: liblwgeom.h:219
LWPOINTITERATOR * lwpointiterator_create(const LWGEOM *g)
Create a new LWPOINTITERATOR over supplied LWGEOM*.
Definition: lwiterator.c:242
void lwgeom_set_srid(LWGEOM *geom, int32_t srid)
Set the SRID on an LWGEOM For collections, only the parent gets an SRID, all the children get SRID_UN...
Definition: lwgeom.c:1547
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
#define MULTILINETYPE
Definition: liblwgeom.h:106
int lwpointiterator_next(LWPOINTITERATOR *s, POINT4D *p)
Attempts to assign the next point in the iterator to p, and advances the iterator to the next point.
Definition: lwiterator.c:210
#define LINETYPE
Definition: liblwgeom.h:103
POINTARRAY * ptarray_addPoint(const POINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where)
Add a point in a pointarray.
Definition: ptarray.c:522
LWGEOM * lwgeom_stroke(const LWGEOM *geom, uint32_t perQuad)
Convert type with arcs into equivalent linearized type.
Definition: lwstroke.c:871
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:529
void lwpointiterator_destroy(LWPOINTITERATOR *s)
Free all memory associated with the iterator.
Definition: lwiterator.c:267
POINTARRAY * ptarray_construct(char hasz, char hasm, uint32_t npoints)
Construct an empty pointarray, allocating storage and setting the npoints, but not filling in any inf...
Definition: ptarray.c:51
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:934
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
char * lwgeom_to_ewkt(const LWGEOM *lwgeom)
Return an alloced string.
Definition: lwgeom.c:565
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:165
LWLINE * lwline_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition: lwmpoint.c:45
double lwgeom_area(const LWGEOM *geom)
Definition: lwgeom.c:1885
int lwgeom_type_arc(const LWGEOM *geom)
Geometry type is one of the potentially "arc containing" types (circstring, multicurve,...
Definition: lwstroke.c:89
#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
int lwgeom_dimension(const LWGEOM *geom)
For an LWGEOM, returns 0 for points, 1 for lines, 2 for polygons, 3 for volume, and the max dimension...
Definition: lwgeom.c:1298
void lwfree(void *mem)
Definition: lwutil.c:242
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
#define FLAGS_NDIMS(flags)
Definition: liblwgeom.h:179
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
LWMPOINT * lwmpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwmpoint.c:39
LWPOINT * lwpoint_construct(int32_t srid, GBOX *bbox, POINTARRAY *point)
Definition: lwpoint.c:129
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
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
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:357
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:755
LWCOLLECTION * lwcollection_concat_in_place(LWCOLLECTION *col1, const LWCOLLECTION *col2)
Appends all geometries from col2 to col1 in place.
Definition: lwcollection.c:241
#define TRIANGLETYPE
Definition: liblwgeom.h:115
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:188
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:42
int ptarray_is_closed_2d(const POINTARRAY *pa)
Definition: ptarray.c:714
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition: lwgeom.c:233
#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
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:215
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:941
LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoly.c:161
LWPOLY * lwpoly_construct(int32_t srid, GBOX *bbox, uint32_t nrings, POINTARRAY **points)
Definition: lwpoly.c:43
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition: lwgeom_api.c:369
LWLINE * lwline_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwline.c:55
LWTRIANGLE * lwtriangle_construct(int32_t srid, GBOX *bbox, POINTARRAY *points)
Definition: lwtriangle.c:40
This library is the generic geometry handling section of PostGIS.
#define LW_ON_INTERRUPT(x)
double lwpoly_area(const LWPOLY *poly)
Find the area of the outer ring - sum (area of inner rings).
Definition: lwpoly.c:434
#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
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition: lwutil.c:177
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:101
static const POINT3D * getPoint3d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT3D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:113
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:145
static uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
Definition: lwinline.h:77
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
void lwrandom_set_seed(int32_t seed)
Definition: lwrandom.c:48
double lwrandom_uniform(void)
Definition: lwrandom.c:94
int count
Definition: genraster.py:57
type
Definition: ovdump.py:42
def fmt
Definition: pixval.py:93
Datum contains(PG_FUNCTION_ARGS)
#define POSTGIS_GEOS_VERSION
Definition: sqldefines.h:11
double ymax
Definition: liblwgeom.h:357
double xmax
Definition: liblwgeom.h:355
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
lwflags_t flags
Definition: liblwgeom.h:577
uint32_t ngeoms
Definition: liblwgeom.h:580
LWGEOM ** geoms
Definition: liblwgeom.h:575
uint8_t type
Definition: liblwgeom.h:462
int32_t srid
Definition: liblwgeom.h:460
lwflags_t flags
Definition: liblwgeom.h:461
POINTARRAY * points
Definition: liblwgeom.h:483
uint32_t ngeoms
Definition: liblwgeom.h:538
LWPOINT ** geoms
Definition: liblwgeom.h:533
uint32_t ngeoms
Definition: liblwgeom.h:566
LWPOLY ** geoms
Definition: liblwgeom.h:561
POINTARRAY * point
Definition: liblwgeom.h:471
POINTARRAY ** rings
Definition: liblwgeom.h:519
uint32_t nrings
Definition: liblwgeom.h:524
GBOX * bbox
Definition: liblwgeom.h:518
POINTARRAY * points
Definition: liblwgeom.h:495
double y
Definition: liblwgeom.h:390
double x
Definition: liblwgeom.h:390
double z
Definition: liblwgeom.h:402
double x
Definition: liblwgeom.h:402
double y
Definition: liblwgeom.h:402
double x
Definition: liblwgeom.h:414
double z
Definition: liblwgeom.h:414
double y
Definition: liblwgeom.h:414
lwflags_t flags
Definition: liblwgeom.h:431
uint32_t npoints
Definition: liblwgeom.h:427
uint8_t * serialized_pointlist
Definition: liblwgeom.h:434