PostGIS  3.0.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-2014 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_has_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_createEmptyPolygon();
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* geom1, const LWGEOM* geom2)
677 {
678  LWGEOM* result;
679  int32_t srid = RESULT_SRID(geom1, geom2);
680  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
681  GEOSGeometry* g1;
682  GEOSGeometry* g2;
683  GEOSGeometry* g3;
684 
685  if (srid == SRID_INVALID) return NULL;
686 
687  /* A.Intersection(Empty) == Empty */
688  if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom2); /* match empty type? */
689 
690  /* Empty.Intersection(A) == Empty */
691  if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom1); /* match empty type? */
692 
693  initGEOS(lwnotice, lwgeom_geos_error);
694 
695  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
696  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
697 
698  g3 = GEOSIntersection(g1, g2);
699 
700  if (!g3) GEOS_FREE_AND_FAIL(g1);
701  GEOSSetSRID(g3, srid);
702 
703  if (!(result = GEOS2LWGEOM(g3, is3d))) GEOS_FREE_AND_FAIL(g1, g2, g3);
704 
705  GEOS_FREE(g1, g2, g3);
706  return result;
707 }
708 
709 LWGEOM*
711 {
712  LWGEOM* result;
713  int32_t srid = RESULT_SRID(geom);
714  uint8_t is3d = FLAGS_GET_Z(geom->flags);
715  GEOSGeometry* g1;
716  GEOSGeometry* g3;
717 
718  if (srid == SRID_INVALID) return NULL;
719 
720  /* Empty.Linemerge() == Empty */
721  if (lwgeom_is_empty(geom)) return lwgeom_clone_deep(geom); /* match empty type to linestring? */
722 
723  initGEOS(lwnotice, lwgeom_geos_error);
724 
725  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
726 
727  g3 = GEOSLineMerge(g1);
728 
729  if (!g3) GEOS_FREE_AND_FAIL(g1);
730  GEOSSetSRID(g3, srid);
731 
732  if (!(result = GEOS2LWGEOM(g3, is3d)))
733  GEOS_FREE_AND_FAIL(g1, g3);
734 
735  GEOS_FREE(g1, g3);
736 
737  return result;
738 }
739 
740 LWGEOM*
742 {
743  LWGEOM* result;
744  int32_t srid = RESULT_SRID(geom);
745  uint8_t is3d = FLAGS_GET_Z(geom->flags);
746  GEOSGeometry* g1;
747  GEOSGeometry* g3;
748 
749  if (srid == SRID_INVALID) return NULL;
750 
751  /* Empty.UnaryUnion() == Empty */
752  if (lwgeom_is_empty(geom)) return lwgeom_clone_deep(geom);
753 
754  initGEOS(lwnotice, lwgeom_geos_error);
755 
756  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
757 
758  g3 = GEOSUnaryUnion(g1);
759 
760  if (!g3) GEOS_FREE_AND_FAIL(g1);
761  GEOSSetSRID(g3, srid);
762 
763  if (!(result = GEOS2LWGEOM(g3, is3d)))
764  GEOS_FREE_AND_FAIL(g1, g3);
765 
766  GEOS_FREE(g1, g3);
767 
768  return result;
769 }
770 
771 LWGEOM*
772 lwgeom_difference(const LWGEOM* geom1, const LWGEOM* geom2)
773 {
774  LWGEOM* result;
775  int32_t srid = RESULT_SRID(geom1, geom2);
776  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
777  GEOSGeometry *g1, *g2, *g3;
778 
779  if (srid == SRID_INVALID) return NULL;
780 
781  /* A.Intersection(Empty) == Empty */
782  if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1); /* match empty type? */
783 
784  /* Empty.Intersection(A) == Empty */
785  if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom1); /* match empty type? */
786 
787  initGEOS(lwnotice, lwgeom_geos_error);
788 
789  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
790  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
791 
792  g3 = GEOSDifference(g1, g2);
793 
794  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
795  GEOSSetSRID(g3, srid);
796 
797  if (!(result = GEOS2LWGEOM(g3, is3d)))
798  GEOS_FREE_AND_FAIL(g1, g2, g3);
799 
800  GEOS_FREE(g1, g2, g3);
801  return result;
802 }
803 
804 LWGEOM*
805 lwgeom_symdifference(const LWGEOM* geom1, const LWGEOM* geom2)
806 {
807  LWGEOM* result;
808  int32_t srid = RESULT_SRID(geom1, geom2);
809  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
810  GEOSGeometry *g1, *g2, *g3;
811 
812  if (srid == SRID_INVALID) return NULL;
813 
814  /* A.SymDifference(Empty) == A */
815  if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1);
816 
817  /* Empty.DymDifference(B) == B */
818  if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom2);
819 
820  initGEOS(lwnotice, lwgeom_geos_error);
821 
822  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
823  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
824 
825  g3 = GEOSSymDifference(g1, g2);
826 
827  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
828  GEOSSetSRID(g3, srid);
829 
830  if (!(result = GEOS2LWGEOM(g3, is3d)))
831  GEOS_FREE_AND_FAIL(g1, g2, g3);
832 
833  GEOS_FREE(g1, g2, g3);
834  return result;
835 }
836 
837 LWGEOM*
839 {
840  LWGEOM* result;
841  int32_t srid = RESULT_SRID(geom);
842  uint8_t is3d = FLAGS_GET_Z(geom->flags);
843  GEOSGeometry *g1, *g3;
844 
845  if (srid == SRID_INVALID) return NULL;
846 
847  if (lwgeom_is_empty(geom))
848  {
849  LWPOINT* lwp = lwpoint_construct_empty(srid, is3d, lwgeom_has_m(geom));
850  return lwpoint_as_lwgeom(lwp);
851  }
852 
853  initGEOS(lwnotice, lwgeom_geos_error);
854 
855  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
856 
857  g3 = GEOSGetCentroid(g1);
858 
859  if (!g3) GEOS_FREE_AND_FAIL(g1);
860  GEOSSetSRID(g3, srid);
861 
862  if (!(result = GEOS2LWGEOM(g3, is3d)))
863  GEOS_FREE_AND_FAIL(g1);
864 
865  GEOS_FREE(g1, g3);
866 
867  return result;
868 }
869 
870 LWGEOM *
872 {
873  LWGEOM *result;
874  int32_t srid = RESULT_SRID(geom);
875  uint8_t is3d = FLAGS_GET_Z(geom->flags);
876  GEOSGeometry *g1, *g3;
877 
878  if (srid == SRID_INVALID) return NULL;
879 
880  if (lwgeom_is_empty(geom))
881  {
882  LWPOINT *lwp = lwpoint_construct_empty(srid, is3d, lwgeom_has_m(geom));
883  return lwpoint_as_lwgeom(lwp);
884  }
885 
886  initGEOS(lwnotice, lwgeom_geos_error);
887 
888  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
889 
890  g3 = GEOSPointOnSurface(g1);
891 
892  if (!g3) GEOS_FREE_AND_FAIL(g1);
893  GEOSSetSRID(g3, srid);
894 
895  if (!(result = GEOS2LWGEOM(g3, is3d)))
896  GEOS_FREE_AND_FAIL(g1, g3);
897 
898  GEOS_FREE(g1, g3);
899 
900  return result;
901 }
902 
903 LWGEOM*
904 lwgeom_union(const LWGEOM* geom1, const LWGEOM* geom2)
905 {
906  LWGEOM* result;
907  int32_t srid = RESULT_SRID(geom1, geom2);
908  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
909  GEOSGeometry *g1, *g2, *g3;
910 
911  if (srid == SRID_INVALID) return NULL;
912 
913  /* A.Union(empty) == A */
914  if (lwgeom_is_empty(geom1)) return lwgeom_clone_deep(geom2);
915 
916  /* B.Union(empty) == B */
917  if (lwgeom_is_empty(geom2)) return lwgeom_clone_deep(geom1);
918 
919  initGEOS(lwnotice, lwgeom_geos_error);
920 
921  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
922  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
923 
924  g3 = GEOSUnion(g1, g2);
925 
926  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
927  GEOSSetSRID(g3, srid);
928 
929  if (!(result = GEOS2LWGEOM(g3, is3d)))
930  GEOS_FREE_AND_FAIL(g1, g2, g3);
931 
932  GEOS_FREE(g1, g2, g3);
933  return result;
934 }
935 
936 LWGEOM *
937 lwgeom_clip_by_rect(const LWGEOM *geom1, double x1, double y1, double x2, double y2)
938 {
939  LWGEOM *result;
940  GEOSGeometry *g1, *g3;
941  int is3d;
942 
943  /* A.Intersection(Empty) == Empty */
944  if ( lwgeom_is_empty(geom1) )
945  return lwgeom_clone_deep(geom1);
946 
947  is3d = FLAGS_GET_Z(geom1->flags);
948 
949  initGEOS(lwnotice, lwgeom_geos_error);
950 
951  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX)))
952  GEOS_FAIL_DEBUG();
953 
954  if (!(g3 = GEOSClipByRect(g1, x1, y1, x2, y2)))
956 
957  GEOS_FREE(g1);
958  result = GEOS2LWGEOM(g3, is3d);
959  GEOS_FREE(g3);
960 
961  if (!result)
962  GEOS_FAIL_DEBUG();
963 
964  result->srid = geom1->srid;
965 
966  return result;
967 }
968 
969 /* ------------ BuildArea stuff ---------------------------------------------------------------------{ */
970 #if POSTGIS_GEOS_VERSION < 38
971 typedef struct Face_t
972 {
973  const GEOSGeometry* geom;
974  GEOSGeometry* env;
975  double envarea;
976  struct Face_t* parent; /* if this face is an hole of another one, or NULL */
977 } Face;
978 
979 static Face* newFace(const GEOSGeometry* g);
980 static void delFace(Face* f);
981 static unsigned int countParens(const Face* f);
982 static void findFaceHoles(Face** faces, int nfaces);
983 
984 static Face*
985 newFace(const GEOSGeometry* g)
986 {
987  Face* f = lwalloc(sizeof(Face));
988  f->geom = g;
989  f->env = GEOSEnvelope(f->geom);
990  GEOSArea(f->env, &f->envarea);
991  f->parent = NULL;
992  return f;
993 }
994 
995 static unsigned int
996 countParens(const Face* f)
997 {
998  unsigned int pcount = 0;
999  while (f->parent)
1000  {
1001  ++pcount;
1002  f = f->parent;
1003  }
1004  return pcount;
1005 }
1006 
1007 /* Destroy the face and release memory associated with it */
1008 static void
1009 delFace(Face* f)
1010 {
1011  GEOSGeom_destroy(f->env);
1012  lwfree(f);
1013 }
1014 
1015 static int
1016 compare_by_envarea(const void* g1, const void* g2)
1017 {
1018  Face* f1 = *(Face**)g1;
1019  Face* f2 = *(Face**)g2;
1020  double n1 = f1->envarea;
1021  double n2 = f2->envarea;
1022 
1023  if (n1 < n2) return 1;
1024  if (n1 > n2) return -1;
1025  return 0;
1026 }
1027 
1028 /* Find holes of each face */
1029 static void
1030 findFaceHoles(Face** faces, int nfaces)
1031 {
1032  int i, j, h;
1033 
1034  /* We sort by envelope area so that we know holes are only after their shells */
1035  qsort(faces, nfaces, sizeof(Face*), compare_by_envarea);
1036  for (i = 0; i < nfaces; ++i)
1037  {
1038  Face* f = faces[i];
1039  int nholes = GEOSGetNumInteriorRings(f->geom);
1040  LWDEBUGF(2, "Scanning face %d with env area %g and %d holes", i, f->envarea, nholes);
1041  for (h = 0; h < nholes; ++h)
1042  {
1043  const GEOSGeometry* hole = GEOSGetInteriorRingN(f->geom, h);
1044  LWDEBUGF(2,
1045  "Looking for hole %d/%d of face %d among %d other faces",
1046  h + 1,
1047  nholes,
1048  i,
1049  nfaces - i - 1);
1050  for (j = i + 1; j < nfaces; ++j)
1051  {
1052  const GEOSGeometry* f2er;
1053  Face* f2 = faces[j];
1054  if (f2->parent) continue; /* hole already assigned */
1055  f2er = GEOSGetExteriorRing(f2->geom);
1056  /* TODO: can be optimized as the ring would have the same vertices, possibly in
1057  * different order. Maybe comparing number of points could already be useful. */
1058  if (GEOSEquals(f2er, hole))
1059  {
1060  LWDEBUGF(2, "Hole %d/%d of face %d is face %d", h + 1, nholes, i, j);
1061  f2->parent = f;
1062  break;
1063  }
1064  }
1065  }
1066  }
1067 }
1068 
1069 static GEOSGeometry*
1070 collectFacesWithEvenAncestors(Face** faces, int nfaces)
1071 {
1072  GEOSGeometry** geoms = lwalloc(sizeof(GEOSGeometry*) * nfaces);
1073  GEOSGeometry* ret;
1074  unsigned int ngeoms = 0;
1075  int i;
1076 
1077  for (i = 0; i < nfaces; ++i)
1078  {
1079  Face* f = faces[i];
1080  if (countParens(f) % 2) continue; /* we skip odd parents geoms */
1081  geoms[ngeoms++] = GEOSGeom_clone(f->geom);
1082  }
1083 
1084  ret = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, geoms, ngeoms);
1085  lwfree(geoms);
1086  return ret;
1087 }
1088 
1089 GEOSGeometry*
1090 LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in)
1091 {
1092  GEOSGeometry* tmp;
1093  GEOSGeometry *geos_result, *shp;
1094  GEOSGeometry const* vgeoms[1];
1095  uint32_t i, ngeoms;
1096  int srid = GEOSGetSRID(geom_in);
1097  Face** geoms;
1098 #if POSTGIS_DEBUG_LEVEL >= 3
1099  LWGEOM *geos_geom;
1100  char *geom_ewkt;
1101 #endif
1102 
1103  vgeoms[0] = geom_in;
1104  geos_result = GEOSPolygonize(vgeoms, 1);
1105 
1106  LWDEBUGF(3, "GEOSpolygonize returned @ %p", geos_result);
1107 
1108  /* Null return from GEOSpolygonize (an exception) */
1109  if (!geos_result) return 0;
1110 
1111  /* We should now have a collection */
1112 #if PARANOIA_LEVEL > 0
1113  if (GEOSGeomTypeId(geos_result) != COLLECTIONTYPE)
1114  {
1115  GEOSGeom_destroy(geos_result);
1116  lwerror("%s [%d] Unexpected return from GEOSpolygonize", __FILE__, __LINE__);
1117  return 0;
1118  }
1119 #endif
1120 
1121  ngeoms = GEOSGetNumGeometries(geos_result);
1122 
1123 #if POSTGIS_DEBUG_LEVEL >= 3
1124  LWDEBUGF(3, "GEOSpolygonize: ngeoms in polygonize output: %d", ngeoms);
1125  geos_geom = GEOS2LWGEOM(geos_result, 0);
1126  geom_ewkt = lwgeom_to_ewkt(geos_geom);
1127  LWDEBUGF(3, "GEOSpolygonize: polygonized:%s", geom_ewkt);
1128  lwgeom_free(geos_geom);
1129  lwfree(geom_ewkt);
1130 #endif
1131 
1132  /* No geometries in collection, early out */
1133  if (ngeoms == 0)
1134  {
1135  GEOSSetSRID(geos_result, srid);
1136  return geos_result;
1137  }
1138 
1139  /* Return first geometry if we only have one in collection, to avoid the unnecessary Geometry clone below. */
1140  if (ngeoms == 1)
1141  {
1142  tmp = (GEOSGeometry*)GEOSGetGeometryN(geos_result, 0);
1143  if (!tmp)
1144  {
1145  GEOSGeom_destroy(geos_result);
1146  return 0; /* exception */
1147  }
1148  shp = GEOSGeom_clone(tmp);
1149  GEOSGeom_destroy(geos_result); /* only safe after the clone above */
1150  GEOSSetSRID(shp, srid);
1151  return shp;
1152  }
1153 
1154  LWDEBUGF(2, "Polygonize returned %d geoms", ngeoms);
1155 
1156  /*
1157  * Polygonizer returns a polygon for each face in the built topology.
1158  *
1159  * This means that for any face with holes we'll have other faces representing each hole. We can imagine a
1160  * parent-child relationship between these faces.
1161  *
1162  * In order to maximize the number of visible rings in output we only use those faces which have an even number
1163  * of parents.
1164  *
1165  * Example:
1166  *
1167  * +---------------+
1168  * | L0 | L0 has no parents
1169  * | +---------+ |
1170  * | | L1 | | L1 is an hole of L0
1171  * | | +---+ | |
1172  * | | |L2 | | | L2 is an hole of L1 (which is an hole of L0)
1173  * | | | | | |
1174  * | | +---+ | |
1175  * | +---------+ |
1176  * | |
1177  * +---------------+
1178  *
1179  * See http://trac.osgeo.org/postgis/ticket/1806
1180  *
1181  */
1182 
1183  /* Prepare face structures for later analysis */
1184  geoms = lwalloc(sizeof(Face**) * ngeoms);
1185  for (i = 0; i < ngeoms; ++i)
1186  geoms[i] = newFace(GEOSGetGeometryN(geos_result, i));
1187 
1188  /* Find faces representing other faces holes */
1189  findFaceHoles(geoms, ngeoms);
1190 
1191  /* Build a MultiPolygon composed only by faces with an even number of ancestors */
1192  tmp = collectFacesWithEvenAncestors(geoms, ngeoms);
1193 
1194  /* Cleanup face structures */
1195  for (i = 0; i < ngeoms; ++i)
1196  delFace(geoms[i]);
1197  lwfree(geoms);
1198 
1199  /* Faces referenced memory owned by geos_result. It is safe to destroy geos_result after deleting them. */
1200  GEOSGeom_destroy(geos_result);
1201 
1202  /* Run a single overlay operation to dissolve shared edges */
1203  shp = GEOSUnionCascaded(tmp);
1204  if (!shp)
1205  {
1206  GEOSGeom_destroy(tmp);
1207  return 0; /* exception */
1208  }
1209 
1210  GEOSGeom_destroy(tmp);
1211 
1212  GEOSSetSRID(shp, srid);
1213 
1214  return shp;
1215 }
1216 #endif
1217 
1218 LWGEOM*
1220 {
1221  LWGEOM* result;
1222  int32_t srid = RESULT_SRID(geom);
1223  uint8_t is3d = FLAGS_GET_Z(geom->flags);
1224  GEOSGeometry *g1, *g3;
1225 
1226  if (srid == SRID_INVALID) return NULL;
1227 
1228  /* Can't build an area from an empty! */
1229  if (lwgeom_is_empty(geom)) return (LWGEOM*)lwpoly_construct_empty(srid, is3d, 0);
1230 
1231  initGEOS(lwnotice, lwgeom_geos_error);
1232 
1233  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1234 
1235 #if POSTGIS_GEOS_VERSION < 38
1236  g3 = LWGEOM_GEOS_buildArea(g1);
1237 #else
1238  g3 = GEOSBuildArea(g1);
1239 #endif
1240 
1241  if (!g3) GEOS_FREE_AND_FAIL(g1);
1242  GEOSSetSRID(g3, srid);
1243 
1244  /* If no geometries are in result collection, return NULL */
1245  if (GEOSGetNumGeometries(g3) == 0)
1246  {
1247  GEOS_FREE(g1);
1248  return NULL;
1249  }
1250 
1251  if (!(result = GEOS2LWGEOM(g3, is3d)))
1252  GEOS_FREE_AND_FAIL(g1, g3);
1253 
1254  GEOS_FREE(g1, g3);
1255 
1256  return result;
1257 }
1258 
1259 /* ------------ end of BuildArea stuff ---------------------------------------------------------------------} */
1260 
1261 int
1263 {
1264  GEOSGeometry* g;
1265  int simple;
1266 
1267  /* Empty is always simple */
1268  if (lwgeom_is_empty(geom)) return LW_TRUE;
1269 
1270  initGEOS(lwnotice, lwgeom_geos_error);
1271 
1272  if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) return -1;
1273 
1274  simple = GEOSisSimple(g);
1275  GEOSGeom_destroy(g);
1276 
1277  if (simple == 2) /* exception thrown */
1278  {
1279  lwerror("lwgeom_is_simple: %s", lwgeom_geos_errmsg);
1280  return -1;
1281  }
1282 
1283  return simple ? LW_TRUE : LW_FALSE;
1284 }
1285 
1286 LWGEOM*
1288 {
1289  LWGEOM* result;
1290  int32_t srid = RESULT_SRID(geom);
1291  uint8_t is3d = FLAGS_GET_Z(geom->flags);
1292  GEOSGeometry* g;
1293 
1294  if (srid == SRID_INVALID) return NULL;
1295 
1296  initGEOS(lwnotice, lwgeom_geos_error);
1297 
1298  if (!(g = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1299 
1300  if (!g) GEOS_FREE_AND_FAIL(g);
1301  GEOSSetSRID(g, srid);
1302 
1303  if (!(result = GEOS2LWGEOM(g, is3d)))
1304  GEOS_FREE_AND_FAIL(g);
1305 
1306  GEOS_FREE(g);
1307 
1308  return result;
1309 }
1310 
1311 LWGEOM*
1312 lwgeom_snap(const LWGEOM* geom1, const LWGEOM* geom2, double tolerance)
1313 {
1314  LWGEOM* result;
1315  int32_t srid = RESULT_SRID(geom1, geom2);
1316  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
1317  GEOSGeometry *g1, *g2, *g3;
1318 
1319  if (srid == SRID_INVALID) return NULL;
1320 
1321  initGEOS(lwnotice, lwgeom_geos_error);
1322 
1323  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
1324  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
1325 
1326  g3 = GEOSSnap(g1, g2, tolerance);
1327 
1328  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
1329  GEOSSetSRID(g3, srid);
1330 
1331  if (!(result = GEOS2LWGEOM(g3, is3d)))
1332  GEOS_FREE_AND_FAIL(g1, g2, g3);
1333 
1334  GEOS_FREE(g1, g2, g3);
1335  return result;
1336 }
1337 
1338 LWGEOM*
1339 lwgeom_sharedpaths(const LWGEOM* geom1, const LWGEOM* geom2)
1340 {
1341  LWGEOM* result;
1342  int32_t srid = RESULT_SRID(geom1, geom2);
1343  uint8_t is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags));
1344  GEOSGeometry *g1, *g2, *g3;
1345 
1346  if (srid == SRID_INVALID) return NULL;
1347 
1348  initGEOS(lwnotice, lwgeom_geos_error);
1349 
1350  if (!(g1 = LWGEOM2GEOS(geom1, AUTOFIX))) GEOS_FAIL();
1351  if (!(g2 = LWGEOM2GEOS(geom2, AUTOFIX))) GEOS_FREE_AND_FAIL(g1);
1352 
1353  g3 = GEOSSharedPaths(g1, g2);
1354 
1355  if (!g3) GEOS_FREE_AND_FAIL(g1, g2);
1356  GEOSSetSRID(g3, srid);
1357 
1358  if (!(result = GEOS2LWGEOM(g3, is3d)))
1359  GEOS_FREE_AND_FAIL(g1, g2, g3);
1360 
1361  GEOS_FREE(g1, g2, g3);
1362  return result;
1363 }
1364 
1365 static LWGEOM *
1366 lwline_offsetcurve(const LWLINE *lwline, double size, int quadsegs, int joinStyle, double mitreLimit)
1367 {
1368  LWGEOM* result;
1369  LWGEOM* geom = lwline_as_lwgeom(lwline);
1370  int32_t srid = RESULT_SRID(geom);
1371  uint8_t is3d = FLAGS_GET_Z(geom->flags);
1372  GEOSGeometry *g1, *g3;
1373 
1374  if (srid == SRID_INVALID) return NULL;
1375 
1376  initGEOS(lwnotice, lwgeom_geos_error);
1377 
1378  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1379 
1380  g3 = GEOSOffsetCurve(g1, size, quadsegs, joinStyle, mitreLimit);
1381 
1382  if (!g3)
1383  {
1384  GEOS_FREE(g1);
1385  return NULL;
1386  }
1387 
1388  GEOSSetSRID(g3, srid);
1389 
1390  if (!(result = GEOS2LWGEOM(g3, is3d)))
1391  GEOS_FREE_AND_FAIL(g1, g3);
1392 
1393  GEOS_FREE(g1, g3);
1394  return result;
1395 }
1396 
1397 static LWGEOM *
1398 lwcollection_offsetcurve(const LWCOLLECTION *col, double size, int quadsegs, int joinStyle, double mitreLimit)
1399 {
1400  const LWGEOM *geom = lwcollection_as_lwgeom(col);
1401  int32_t srid = RESULT_SRID(geom);
1402  uint8_t is3d = FLAGS_GET_Z(col->flags);
1403  LWCOLLECTION *result;
1404  LWGEOM *tmp;
1405  uint32_t i;
1406  if (srid == SRID_INVALID) return NULL;
1407 
1408  result = lwcollection_construct_empty(MULTILINETYPE, srid, is3d, LW_FALSE);
1409 
1410  for (i = 0; i < col->ngeoms; i++)
1411  {
1412  tmp = lwgeom_offsetcurve(col->geoms[i], size, quadsegs, joinStyle, mitreLimit);
1413 
1414  if (!tmp)
1415  {
1416  lwcollection_free(result);
1417  return NULL;
1418  }
1419 
1420  if (!lwgeom_is_empty(tmp))
1421  {
1422  if (lwgeom_is_collection(tmp))
1424  else
1425  result = lwcollection_add_lwgeom(result, tmp);
1426 
1427  if (!result)
1428  {
1429  lwgeom_free(tmp);
1430  return NULL;
1431  }
1432  }
1433  }
1434 
1435  if (result->ngeoms == 1)
1436  {
1437  tmp = result->geoms[0];
1438  lwcollection_release(result);
1439  return tmp;
1440  }
1441  else
1442  return lwcollection_as_lwgeom(result);
1443 }
1444 
1445 LWGEOM*
1446 lwgeom_offsetcurve(const LWGEOM* geom, double size, int quadsegs, int joinStyle, double mitreLimit)
1447 {
1448  int32_t srid = RESULT_SRID(geom);
1449  LWGEOM *result = NULL;
1450  LWGEOM *noded = NULL;
1451  if (srid == SRID_INVALID) return NULL;
1452 
1453  if (lwgeom_dimension(geom) != 1)
1454  {
1455  lwerror("%s: input is not linear", __func__, lwtype_name(geom->type));
1456  return NULL;
1457  }
1458 
1459  while (!result)
1460  {
1461  switch (geom->type)
1462  {
1463  case LINETYPE:
1464  result = lwline_offsetcurve(lwgeom_as_lwline(geom), size, quadsegs, joinStyle, mitreLimit);
1465  break;
1466  case COLLECTIONTYPE:
1467  case MULTILINETYPE:
1468  result = lwcollection_offsetcurve(lwgeom_as_lwcollection(geom), size, quadsegs, joinStyle, mitreLimit);
1469  break;
1470  default:
1471  lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type));
1472  return NULL;
1473  }
1474 
1475  if (result)
1476  {
1477  if (noded) lwgeom_free(noded);
1478  return result;
1479  }
1480  else if (!noded)
1481  {
1482  noded = lwgeom_node(geom);
1483  if (!noded)
1484  {
1485  lwerror("lwgeom_offsetcurve: cannot node input");
1486  return NULL;
1487  }
1488  geom = noded;
1489  }
1490  else
1491  {
1492  lwgeom_free(noded);
1493  lwerror("lwgeom_offsetcurve: noded geometry cannot be offset");
1494  return NULL;
1495  }
1496  }
1497 
1498  return result;
1499 }
1500 
1501 LWMPOINT*
1502 lwpoly_to_points(const LWPOLY* lwpoly, uint32_t npoints, int32_t seed)
1503 {
1504  double area, bbox_area, bbox_width, bbox_height;
1505  GBOX bbox;
1506  const LWGEOM* lwgeom = (LWGEOM*)lwpoly;
1507  uint32_t sample_npoints, sample_sqrt, sample_width, sample_height;
1508  double sample_cell_size;
1509  uint32_t i, j, n;
1510  uint32_t iterations = 0;
1511  uint32_t npoints_generated = 0;
1512  uint32_t npoints_tested = 0;
1513  GEOSGeometry* g;
1514  const GEOSPreparedGeometry* gprep;
1515  GEOSGeometry* gpt;
1516  GEOSCoordSequence* gseq;
1517  LWMPOINT* mpt;
1518  int32_t srid = lwgeom_get_srid(lwgeom);
1519  int done = 0;
1520  int* cells;
1521  const size_t size = 2 * sizeof(int);
1522  char tmp[2 * sizeof(int)];
1523  const size_t stride = 2 * sizeof(int);
1524 
1525  if (lwgeom_get_type(lwgeom) != POLYGONTYPE)
1526  {
1527  lwerror("%s: only polygons supported", __func__);
1528  return NULL;
1529  }
1530 
1531  if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL;
1532 
1533  if (!lwpoly->bbox)
1534  lwgeom_calculate_gbox(lwgeom, &bbox);
1535  else
1536  bbox = *(lwpoly->bbox);
1537 
1538  area = lwpoly_area(lwpoly);
1539  bbox_width = bbox.xmax - bbox.xmin;
1540  bbox_height = bbox.ymax - bbox.ymin;
1541  bbox_area = bbox_width * bbox_height;
1542 
1543  if (area == 0.0 || bbox_area == 0.0)
1544  {
1545  lwerror("%s: zero area input polygon, TBD", __func__);
1546  return NULL;
1547  }
1548 
1549  /* Gross up our test set a bit to increase odds of getting coverage in one pass */
1550  sample_npoints = npoints * bbox_area / area;
1551 
1552  /* We're going to generate points using a sample grid as described
1553  * http://lin-ear-th-inking.blogspot.ca/2010/05/more-random-points-in-jts.html to try and get a more uniform
1554  * "random" set of points. So we have to figure out how to stick a grid into our box */
1555  sample_sqrt = lround(sqrt(sample_npoints));
1556  if (sample_sqrt == 0)
1557  sample_sqrt = 1;
1558 
1559  /* Calculate the grids we're going to randomize within */
1560  if (bbox_width > bbox_height)
1561  {
1562  sample_width = sample_sqrt;
1563  sample_height = ceil((double)sample_npoints / (double)sample_width);
1564  sample_cell_size = bbox_width / sample_width;
1565  }
1566  else
1567  {
1568  sample_height = sample_sqrt;
1569  sample_width = ceil((double)sample_npoints / (double)sample_height);
1570  sample_cell_size = bbox_height / sample_height;
1571  }
1572 
1573  /* Prepare the polygon for fast true/false testing */
1574  initGEOS(lwnotice, lwgeom_geos_error);
1575  g = (GEOSGeometry*)LWGEOM2GEOS(lwgeom, 0);
1576  if (!g)
1577  {
1578  lwerror("%s: Geometry could not be converted to GEOS: %s", __func__, lwgeom_geos_errmsg);
1579  return NULL;
1580  }
1581  gprep = GEOSPrepare(g);
1582 
1583  /* Get an empty multi-point ready to return */
1584  mpt = lwmpoint_construct_empty(srid, 0, 0);
1585 
1586  /* Initiate random number generator.
1587  * Repeatable numbers are generated with seed values >= 1.
1588  * When seed is zero and has not previously been set, it is based on
1589  * Unix time (seconds) and process ID. */
1590  lwrandom_set_seed(seed);
1591 
1592  /* Now we fill in an array of cells, and then shuffle that array, */
1593  /* so we can visit the cells in random order to avoid visual ugliness */
1594  /* caused by visiting them sequentially */
1595  cells = lwalloc(2 * sizeof(int) * sample_height * sample_width);
1596  for (i = 0; i < sample_width; i++)
1597  {
1598  for (j = 0; j < sample_height; j++)
1599  {
1600  cells[2 * (i * sample_height + j)] = i;
1601  cells[2 * (i * sample_height + j) + 1] = j;
1602  }
1603  }
1604 
1605  /* Fisher-Yates shuffle */
1606  n = sample_height * sample_width;
1607  if (n > 1)
1608  {
1609  for (i = n - 1; i > 0; i--)
1610  {
1611  size_t j = (size_t)(lwrandom_uniform() * (i + 1));
1612 
1613  memcpy(tmp, (char *)cells + j * stride, size);
1614  memcpy((char *)cells + j * stride, (char *)cells + i * stride, size);
1615  memcpy((char *)cells + i * stride, tmp, size);
1616  }
1617  }
1618 
1619  /* Start testing points */
1620  while (npoints_generated < npoints)
1621  {
1622  iterations++;
1623  for (i = 0; i < sample_width * sample_height; i++)
1624  {
1625  int contains = 0;
1626  double y = bbox.ymin + cells[2 * i] * sample_cell_size;
1627  double x = bbox.xmin + cells[2 * i + 1] * sample_cell_size;
1628  x += lwrandom_uniform() * sample_cell_size;
1629  y += lwrandom_uniform() * sample_cell_size;
1630  if (x >= bbox.xmax || y >= bbox.ymax) continue;
1631 
1632  gseq = GEOSCoordSeq_create(1, 2);
1633 #if POSTGIS_GEOS_VERSION < 38
1634  GEOSCoordSeq_setX(gseq, 0, x);
1635  GEOSCoordSeq_setY(gseq, 0, y);
1636 #else
1637  GEOSCoordSeq_setXY(gseq, 0, x, y);
1638 #endif
1639  gpt = GEOSGeom_createPoint(gseq);
1640 
1641  contains = GEOSPreparedIntersects(gprep, gpt);
1642 
1643  GEOSGeom_destroy(gpt);
1644 
1645  if (contains == 2)
1646  {
1647  GEOSPreparedGeom_destroy(gprep);
1648  GEOSGeom_destroy(g);
1649  lwerror("%s: GEOS exception on PreparedContains: %s", __func__, lwgeom_geos_errmsg);
1650  return NULL;
1651  }
1652  if (contains)
1653  {
1654  npoints_generated++;
1655  mpt = lwmpoint_add_lwpoint(mpt, lwpoint_make2d(srid, x, y));
1656  if (npoints_generated == npoints)
1657  {
1658  done = 1;
1659  break;
1660  }
1661  }
1662 
1663  /* Short-circuit check for ctrl-c occasionally */
1664  npoints_tested++;
1665  if (npoints_tested % 10000 == 0)
1666  LW_ON_INTERRUPT(GEOSPreparedGeom_destroy(gprep); GEOSGeom_destroy(g); return NULL);
1667 
1668  if (done) break;
1669  }
1670  if (done || iterations > 100) break;
1671  }
1672 
1673  GEOSPreparedGeom_destroy(gprep);
1674  GEOSGeom_destroy(g);
1675  lwfree(cells);
1676 
1677  return mpt;
1678 }
1679 
1680 /* Allocate points to sub-geometries by area, then call lwgeom_poly_to_points and bundle up final result in a single
1681  * multipoint. */
1682 LWMPOINT*
1683 lwmpoly_to_points(const LWMPOLY* lwmpoly, uint32_t npoints, int32_t seed)
1684 {
1685  const LWGEOM* lwgeom = (LWGEOM*)lwmpoly;
1686  double area;
1687  uint32_t i;
1688  LWMPOINT* mpt = NULL;
1689 
1690  if (lwgeom_get_type(lwgeom) != MULTIPOLYGONTYPE)
1691  {
1692  lwerror("%s: only multipolygons supported", __func__);
1693  return NULL;
1694  }
1695  if (npoints == 0 || lwgeom_is_empty(lwgeom)) return NULL;
1696 
1697  area = lwgeom_area(lwgeom);
1698 
1699  for (i = 0; i < lwmpoly->ngeoms; i++)
1700  {
1701  double sub_area = lwpoly_area(lwmpoly->geoms[i]);
1702  int sub_npoints = lround(npoints * sub_area / area);
1703  if (sub_npoints > 0)
1704  {
1705  LWMPOINT* sub_mpt = lwpoly_to_points(lwmpoly->geoms[i], sub_npoints, seed);
1706  if (!mpt)
1707  mpt = sub_mpt;
1708  else
1709  {
1710  uint32_t j;
1711  for (j = 0; j < sub_mpt->ngeoms; j++)
1712  mpt = lwmpoint_add_lwpoint(mpt, sub_mpt->geoms[j]);
1713  /* Just free the shell, leave the underlying lwpoints alone, as they are now owned by
1714  * the returning multipoint */
1715  lwfree(sub_mpt->geoms);
1716  lwgeom_release((LWGEOM*)sub_mpt);
1717  }
1718  }
1719  }
1720  return mpt;
1721 }
1722 
1723 LWMPOINT*
1724 lwgeom_to_points(const LWGEOM* lwgeom, uint32_t npoints, int32_t seed)
1725 {
1726  switch (lwgeom_get_type(lwgeom))
1727  {
1728  case MULTIPOLYGONTYPE:
1729  return lwmpoly_to_points((LWMPOLY*)lwgeom, npoints, seed);
1730  case POLYGONTYPE:
1731  return lwpoly_to_points((LWPOLY*)lwgeom, npoints, seed);
1732  default:
1733  lwerror("%s: unsupported geometry type '%s'", __func__, lwtype_name(lwgeom_get_type(lwgeom)));
1734  return NULL;
1735  }
1736 }
1737 
1738 LWTIN*
1739 lwtin_from_geos(const GEOSGeometry* geom, uint8_t want3d)
1740 {
1741  int type = GEOSGeomTypeId(geom);
1742  int SRID = GEOSGetSRID(geom);
1743 
1744  /* GEOS's 0 is equivalent to our unknown as for SRID values */
1745  if (SRID == 0) SRID = SRID_UNKNOWN;
1746 
1747  if (want3d && !GEOSHasZ(geom))
1748  {
1749  LWDEBUG(3, "Geometry has no Z, won't provide one");
1750  want3d = 0;
1751  }
1752 
1753  switch (type)
1754  {
1755  LWTRIANGLE** geoms;
1756  uint32_t i, ngeoms;
1757  case GEOS_GEOMETRYCOLLECTION:
1758  LWDEBUG(4, "lwgeom_from_geometry: it's a Collection or Multi");
1759 
1760  ngeoms = GEOSGetNumGeometries(geom);
1761  geoms = NULL;
1762  if (ngeoms)
1763  {
1764  geoms = lwalloc(ngeoms * sizeof *geoms);
1765  if (!geoms)
1766  {
1767  lwerror("lwtin_from_geos: can't allocate geoms");
1768  return NULL;
1769  }
1770  for (i = 0; i < ngeoms; i++)
1771  {
1772  const GEOSGeometry *poly, *ring;
1773  const GEOSCoordSequence* cs;
1774  POINTARRAY* pa;
1775 
1776  poly = GEOSGetGeometryN(geom, i);
1777  ring = GEOSGetExteriorRing(poly);
1778  cs = GEOSGeom_getCoordSeq(ring);
1779  pa = ptarray_from_GEOSCoordSeq(cs, want3d);
1780 
1781  geoms[i] = lwtriangle_construct(SRID, NULL, pa);
1782  }
1783  }
1784  return (LWTIN*)lwcollection_construct(TINTYPE, SRID, NULL, ngeoms, (LWGEOM**)geoms);
1785  case GEOS_POLYGON:
1786  case GEOS_MULTIPOINT:
1787  case GEOS_MULTILINESTRING:
1788  case GEOS_MULTIPOLYGON:
1789  case GEOS_LINESTRING:
1790  case GEOS_LINEARRING:
1791  case GEOS_POINT:
1792  lwerror("lwtin_from_geos: invalid geometry type for tin: %d", type);
1793  break;
1794 
1795  default:
1796  lwerror("GEOS2LWGEOM: unknown geometry type: %d", type);
1797  return NULL;
1798  }
1799 
1800  /* shouldn't get here */
1801  return NULL;
1802 }
1803 /*
1804  * output = 1 for edges, 2 for TIN, 0 for polygons
1805  */
1806 LWGEOM*
1807 lwgeom_delaunay_triangulation(const LWGEOM* geom, double tolerance, int32_t output)
1808 {
1809  LWGEOM* result;
1810  int32_t srid = RESULT_SRID(geom);
1811  uint8_t is3d = FLAGS_GET_Z(geom->flags);
1812  GEOSGeometry *g1, *g3;
1813 
1814  if (output < 0 || output > 2)
1815  {
1816  lwerror("%s: invalid output type specified %d", __func__, output);
1817  return NULL;
1818  }
1819 
1820  if (srid == SRID_INVALID) return NULL;
1821 
1822  initGEOS(lwnotice, lwgeom_geos_error);
1823 
1824  if (!(g1 = LWGEOM2GEOS(geom, AUTOFIX))) GEOS_FAIL();
1825 
1826  /* if output != 1 we want polys */
1827  g3 = GEOSDelaunayTriangulation(g1, tolerance, output == 1);
1828 
1829  if (!g3) GEOS_FREE_AND_FAIL(g1);
1830  GEOSSetSRID(g3, srid);
1831 
1832  if (output == 2)
1833  {
1834  result = (LWGEOM*)lwtin_from_geos(g3, is3d);
1835  if (!result)
1836  {
1837  GEOS_FREE(g1, g3);
1838  lwerror("%s: cannot convert output geometry", __func__);
1839  return NULL;
1840  }
1841  lwgeom_set_srid(result, srid);
1842  }
1843  else if (!(result = GEOS2LWGEOM(g3, is3d)))
1844  GEOS_FREE_AND_FAIL(g1, g3);
1845 
1846  GEOS_FREE(g1, g3);
1847  return result;
1848 }
1849 
1850 static GEOSCoordSequence*
1851 lwgeom_get_geos_coordseq_2d(const LWGEOM* g, uint32_t num_points)
1852 {
1853  uint32_t i = 0;
1854  uint8_t num_dims = 2;
1855  LWPOINTITERATOR* it;
1856  GEOSCoordSequence* coords;
1857  POINT4D tmp;
1858 
1859  coords = GEOSCoordSeq_create(num_points, num_dims);
1860  if (!coords) return NULL;
1861 
1862  it = lwpointiterator_create(g);
1863  while (lwpointiterator_next(it, &tmp))
1864  {
1865  if (i >= num_points)
1866  {
1867  lwerror("Incorrect num_points provided to lwgeom_get_geos_coordseq_2d");
1868  GEOSCoordSeq_destroy(coords);
1870  return NULL;
1871  }
1872 
1873 #if POSTGIS_GEOS_VERSION < 38
1874  if (!GEOSCoordSeq_setX(coords, i, tmp.x) || !GEOSCoordSeq_setY(coords, i, tmp.y))
1875 #else
1876  if (!GEOSCoordSeq_setXY(coords, i, tmp.x, tmp.y))
1877 #endif
1878  {
1879  GEOSCoordSeq_destroy(coords);
1881  return NULL;
1882  }
1883  i++;
1884  }
1886 
1887  return coords;
1888 }
1889 
1890 LWGEOM*
1891 lwgeom_voronoi_diagram(const LWGEOM* g, const GBOX* env, double tolerance, int output_edges)
1892 {
1893  uint32_t num_points = lwgeom_count_vertices(g);
1894  LWGEOM* lwgeom_result;
1895  char is_3d = LW_FALSE;
1896  int32_t srid = lwgeom_get_srid(g);
1897  GEOSCoordSequence* coords;
1898  GEOSGeometry* geos_geom;
1899  GEOSGeometry* geos_env = NULL;
1900  GEOSGeometry* geos_result;
1901 
1902  if (num_points < 2)
1903  {
1905  return lwcollection_as_lwgeom(empty);
1906  }
1907 
1908  initGEOS(lwnotice, lwgeom_geos_error);
1909 
1910  /* Instead of using the standard LWGEOM2GEOS transformer, we read the vertices of the LWGEOM directly and put
1911  * them into a single GEOS CoordinateSeq that can be used to define a LineString. This allows us to process
1912  * geometry types that may not be supported by GEOS, and reduces the memory requirements in cases of many
1913  * geometries with few points (such as LWMPOINT).*/
1914  coords = lwgeom_get_geos_coordseq_2d(g, num_points);
1915  if (!coords) return NULL;
1916 
1917  geos_geom = GEOSGeom_createLineString(coords);
1918  if (!geos_geom)
1919  {
1920  GEOSCoordSeq_destroy(coords);
1921  return NULL;
1922  }
1923 
1924  if (env) geos_env = GBOX2GEOS(env);
1925 
1926  geos_result = GEOSVoronoiDiagram(geos_geom, geos_env, tolerance, output_edges);
1927 
1928  GEOSGeom_destroy(geos_geom);
1929  if (env) GEOSGeom_destroy(geos_env);
1930 
1931  if (!geos_result)
1932  {
1933  lwerror("GEOSVoronoiDiagram: %s", lwgeom_geos_errmsg);
1934  return NULL;
1935  }
1936 
1937  lwgeom_result = GEOS2LWGEOM(geos_result, is_3d);
1938  GEOSGeom_destroy(geos_result);
1939 
1940  lwgeom_set_srid(lwgeom_result, srid);
1941 
1942  return lwgeom_result;
1943 }
#define LWGEOM_GEOS_ERRMSG_MAXSIZE
#define GEOS_FREE(...)
LWGEOM * lwgeom_centroid(const LWGEOM *geom)
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_intersection(const LWGEOM *geom1, const LWGEOM *geom2)
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.
#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)
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_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)
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)
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_union(const LWGEOM *geom1, const LWGEOM *geom2)
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:161
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:321
#define LW_FALSE
Definition: liblwgeom.h:108
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:291
#define COLLECTIONTYPE
Definition: liblwgeom.h:122
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:909
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)
Definition: lwstroke.c:859
#define MULTIPOINTTYPE
Definition: liblwgeom.h:119
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:511
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:916
#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:547
#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:1863
#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:326
#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:1079
#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:737
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:215
#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:450
#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:923
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:376
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
int lwgeom_has_arc(const LWGEOM *geom)
Definition: lwstroke.c:55
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:91
static const POINT3D * getPoint3d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:103
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:135
static uint8_t * getPoint_internal(const POINTARRAY *pa, uint32_t n)
Definition: lwinline.h:67
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:193
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:343
double xmax
Definition: liblwgeom.h:341
double ymin
Definition: liblwgeom.h:342
double xmin
Definition: liblwgeom.h:340
lwflags_t flags
Definition: liblwgeom.h:563
uint32_t ngeoms
Definition: liblwgeom.h:566
LWGEOM ** geoms
Definition: liblwgeom.h:561
uint8_t type
Definition: liblwgeom.h:448
int32_t srid
Definition: liblwgeom.h:446
lwflags_t flags
Definition: liblwgeom.h:447
POINTARRAY * points
Definition: liblwgeom.h:469
uint32_t ngeoms
Definition: liblwgeom.h:524
LWPOINT ** geoms
Definition: liblwgeom.h:519
uint32_t ngeoms
Definition: liblwgeom.h:552
LWPOLY ** geoms
Definition: liblwgeom.h:547
POINTARRAY * point
Definition: liblwgeom.h:457
POINTARRAY ** rings
Definition: liblwgeom.h:505
uint32_t nrings
Definition: liblwgeom.h:510
GBOX * bbox
Definition: liblwgeom.h:504
POINTARRAY * points
Definition: liblwgeom.h:481
double y
Definition: liblwgeom.h:376
double x
Definition: liblwgeom.h:376
double z
Definition: liblwgeom.h:388
double x
Definition: liblwgeom.h:388
double y
Definition: liblwgeom.h:388
double x
Definition: liblwgeom.h:400
double z
Definition: liblwgeom.h:400
double y
Definition: liblwgeom.h:400
lwflags_t flags
Definition: liblwgeom.h:417
uint32_t npoints
Definition: liblwgeom.h:413