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