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