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