PostGIS  3.7.0dev-r@@SVN_REVISION@@
postgis/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 2009-2014 Sandro Santilli <strk@kbt.io>
22  * Copyright 2008 Paul Ramsey <pramsey@cleverelephant.ca>
23  * Copyright 2001-2003 Refractions Research Inc.
24  *
25  **********************************************************************/
26 
27 
28 #include "../postgis_config.h"
29 
30 /* PostgreSQL */
31 #include "postgres.h"
32 #include "funcapi.h"
33 #include "utils/array.h"
34 #include "utils/builtins.h"
35 #include "utils/lsyscache.h"
36 #include "utils/numeric.h"
37 #include "access/htup_details.h"
38 
39 /* PostGIS */
40 #include "lwgeom_geos.h"
41 #include "liblwgeom.h"
42 #include "liblwgeom_internal.h"
43 #include "lwgeom_itree.h"
44 #include "lwgeom_geos_prepared.h"
45 #include "lwgeom_accum.h"
46 
47 
48 
49 
50 /*
51 ** Prototypes for SQL-bound functions
52 */
53 Datum isvalid(PG_FUNCTION_ARGS);
54 Datum isvalidreason(PG_FUNCTION_ARGS);
55 Datum isvaliddetail(PG_FUNCTION_ARGS);
56 Datum buffer(PG_FUNCTION_ARGS);
57 Datum ST_Intersection(PG_FUNCTION_ARGS);
58 Datum convexhull(PG_FUNCTION_ARGS);
59 Datum topologypreservesimplify(PG_FUNCTION_ARGS);
60 Datum ST_Difference(PG_FUNCTION_ARGS);
61 Datum boundary(PG_FUNCTION_ARGS);
62 Datum ST_SymDifference(PG_FUNCTION_ARGS);
63 Datum ST_Union(PG_FUNCTION_ARGS);
64 Datum issimple(PG_FUNCTION_ARGS);
65 Datum isring(PG_FUNCTION_ARGS);
66 Datum pointonsurface(PG_FUNCTION_ARGS);
67 Datum GEOSnoop(PG_FUNCTION_ARGS);
68 Datum postgis_geos_version(PG_FUNCTION_ARGS);
69 Datum postgis_geos_compiled_version(PG_FUNCTION_ARGS);
70 Datum centroid(PG_FUNCTION_ARGS);
71 Datum polygonize_garray(PG_FUNCTION_ARGS);
72 Datum clusterintersecting_garray(PG_FUNCTION_ARGS);
73 Datum cluster_within_distance_garray(PG_FUNCTION_ARGS);
74 Datum linemerge(PG_FUNCTION_ARGS);
75 Datum hausdorffdistance(PG_FUNCTION_ARGS);
76 Datum hausdorffdistancedensify(PG_FUNCTION_ARGS);
77 Datum ST_FrechetDistance(PG_FUNCTION_ARGS);
78 Datum ST_UnaryUnion(PG_FUNCTION_ARGS);
79 Datum ST_Equals(PG_FUNCTION_ARGS);
80 Datum ST_BuildArea(PG_FUNCTION_ARGS);
81 Datum ST_DelaunayTriangles(PG_FUNCTION_ARGS);
82 Datum ST_MaximumInscribedCircle(PG_FUNCTION_ARGS);
83 Datum ST_ConcaveHull(PG_FUNCTION_ARGS);
84 Datum ST_SimplifyPolygonHull(PG_FUNCTION_ARGS);
85 Datum pgis_union_geometry_array(PG_FUNCTION_ARGS);
86 
87 /*
88 ** Prototypes end
89 */
90 
92 Datum postgis_geos_version(PG_FUNCTION_ARGS)
93 {
94  const char *ver = lwgeom_geos_version();
95  text *result = cstring_to_text(ver);
96  PG_RETURN_POINTER(result);
97 }
98 
100 Datum postgis_geos_compiled_version(PG_FUNCTION_ARGS)
101 {
102  const char *ver = lwgeom_geos_compiled_version();
103  text *result = cstring_to_text(ver);
104  PG_RETURN_POINTER(result);
105 }
106 
114 Datum hausdorffdistance(PG_FUNCTION_ARGS)
115 {
116  GSERIALIZED *geom1;
117  GSERIALIZED *geom2;
118  GEOSGeometry *g1;
119  GEOSGeometry *g2;
120  double result;
121  int retcode;
122 
123  geom1 = PG_GETARG_GSERIALIZED_P(0);
124  geom2 = PG_GETARG_GSERIALIZED_P(1);
125 
126  if ( gserialized_is_empty(geom1) || gserialized_is_empty(geom2) )
127  PG_RETURN_NULL();
128 
129  initGEOS(lwpgnotice, lwgeom_geos_error);
130 
131  g1 = POSTGIS2GEOS(geom1);
132  if (!g1)
133  HANDLE_GEOS_ERROR("First argument geometry could not be converted to GEOS");
134 
135  g2 = POSTGIS2GEOS(geom2);
136  if (!g2)
137  {
138  GEOSGeom_destroy(g1);
139  HANDLE_GEOS_ERROR("Second argument geometry could not be converted to GEOS");
140  }
141 
142  retcode = GEOSHausdorffDistance(g1, g2, &result);
143  GEOSGeom_destroy(g1);
144  GEOSGeom_destroy(g2);
145 
146  if (retcode == 0) HANDLE_GEOS_ERROR("GEOSHausdorffDistance");
147 
148  PG_FREE_IF_COPY(geom1, 0);
149  PG_FREE_IF_COPY(geom2, 1);
150 
151  PG_RETURN_FLOAT8(result);
152 }
153 
163 Datum hausdorffdistancedensify(PG_FUNCTION_ARGS)
164 {
165  GSERIALIZED *geom1;
166  GSERIALIZED *geom2;
167  GEOSGeometry *g1;
168  GEOSGeometry *g2;
169  double densifyFrac;
170  double result;
171  int retcode;
172 
173  geom1 = PG_GETARG_GSERIALIZED_P(0);
174  geom2 = PG_GETARG_GSERIALIZED_P(1);
175  densifyFrac = PG_GETARG_FLOAT8(2);
176 
177  if ( gserialized_is_empty(geom1) || gserialized_is_empty(geom2) )
178  PG_RETURN_NULL();
179 
180  initGEOS(lwpgnotice, lwgeom_geos_error);
181 
182  g1 = POSTGIS2GEOS(geom1);
183  if (!g1)
184  HANDLE_GEOS_ERROR("First argument geometry could not be converted to GEOS");
185 
186  g2 = POSTGIS2GEOS(geom2);
187  if (!g2)
188  {
189  GEOSGeom_destroy(g1);
190  HANDLE_GEOS_ERROR("Second argument geometry could not be converted to GEOS");
191  }
192 
193  retcode = GEOSHausdorffDistanceDensify(g1, g2, densifyFrac, &result);
194  GEOSGeom_destroy(g1);
195  GEOSGeom_destroy(g2);
196 
197  if (retcode == 0) HANDLE_GEOS_ERROR("GEOSHausdorffDistanceDensify");
198 
199  PG_FREE_IF_COPY(geom1, 0);
200  PG_FREE_IF_COPY(geom2, 1);
201 
202  PG_RETURN_FLOAT8(result);
203 }
204 
213 Datum ST_FrechetDistance(PG_FUNCTION_ARGS)
214 {
215  GSERIALIZED *geom1;
216  GSERIALIZED *geom2;
217  GEOSGeometry *g1;
218  GEOSGeometry *g2;
219  double densifyFrac;
220  double result;
221  int retcode;
222 
223  geom1 = PG_GETARG_GSERIALIZED_P(0);
224  geom2 = PG_GETARG_GSERIALIZED_P(1);
225  densifyFrac = PG_GETARG_FLOAT8(2);
226 
227  if ( gserialized_is_empty(geom1) || gserialized_is_empty(geom2) )
228  PG_RETURN_NULL();
229 
230  initGEOS(lwpgnotice, lwgeom_geos_error);
231 
232  g1 = POSTGIS2GEOS(geom1);
233  if (!g1)
234  HANDLE_GEOS_ERROR("First argument geometry could not be converted to GEOS");
235 
236  g2 = POSTGIS2GEOS(geom2);
237  if (!g2)
238  {
239  GEOSGeom_destroy(g1);
240  HANDLE_GEOS_ERROR("Second argument geometry could not be converted to GEOS");
241  }
242 
243  if (densifyFrac <= 0.0)
244  {
245  retcode = GEOSFrechetDistance(g1, g2, &result);
246  }
247  else
248  {
249  retcode = GEOSFrechetDistanceDensify(g1, g2, densifyFrac, &result);
250  }
251 
252  GEOSGeom_destroy(g1);
253  GEOSGeom_destroy(g2);
254 
255  if (retcode == 0) HANDLE_GEOS_ERROR("GEOSFrechetDistance");
256 
257  PG_FREE_IF_COPY(geom1, 0);
258  PG_FREE_IF_COPY(geom2, 1);
259 
260  PG_RETURN_FLOAT8(result);
261 }
262 
263 
264 
266 Datum ST_MaximumInscribedCircle(PG_FUNCTION_ARGS)
267 {
268 #if POSTGIS_GEOS_VERSION < 30900
269 
270  lwpgerror("The GEOS version this PostGIS binary "
271  "was compiled against (%d) doesn't support "
272  "'GEOSMaximumInscribedCircle' function (3.9.0+ required)",
274  PG_RETURN_NULL();
275 
276 #else /* POSTGIS_GEOS_VERSION >= 30900 */
277  GSERIALIZED* geom;
278  GSERIALIZED* center;
279  GSERIALIZED* nearest;
280  TupleDesc resultTupleDesc;
281  HeapTuple resultTuple;
282  Datum result;
283  Datum result_values[3];
284  bool result_is_null[3];
285  double radius = 0.0;
286  int32 srid = SRID_UNKNOWN;
287  bool is3d;
288 
289  if (PG_ARGISNULL(0))
290  PG_RETURN_NULL();
291 
292  geom = PG_GETARG_GSERIALIZED_P(0);
293  srid = gserialized_get_srid(geom);
294  is3d = gserialized_has_z(geom);
295 
296  /* Empty geometry? Return POINT EMPTY with zero radius */
297  if (gserialized_is_empty(geom))
298  {
301  center = geometry_serialize(lwcenter);
302  nearest = geometry_serialize(lwnearest);
303  radius = 0.0;
304  }
305  else
306  {
307  GEOSGeometry *ginput, *gcircle, *gcenter, *gnearest;
308  double width, height, size, tolerance;
309  GBOX gbox;
310  int gtype;
311  LWGEOM *lwg;
312  lwg = lwgeom_from_gserialized(geom);
313  if (!lwgeom_isfinite(lwg))
314  {
315  lwpgerror("Geometry contains invalid coordinates");
316  PG_RETURN_NULL();
317  }
318  lwgeom_free(lwg);
319 
320  if (!gserialized_get_gbox_p(geom, &gbox))
321  PG_RETURN_NULL();
322 
323  width = gbox.xmax - gbox.xmin;
324  height = gbox.ymax - gbox.ymin;
325  size = width > height ? width : height;
326  tolerance = size / 1000.0;
327 
328  initGEOS(lwpgnotice, lwgeom_geos_error);
329 
330  ginput = POSTGIS2GEOS(geom);
331  if (!ginput)
332  HANDLE_GEOS_ERROR("Geometry could not be converted to GEOS");
333 
334  gtype = gserialized_get_type(geom);
335  if (gtype == POLYGONTYPE || gtype == MULTIPOLYGONTYPE)
336  {
337  gcircle = GEOSMaximumInscribedCircle(ginput, tolerance);
338  if (!gcircle)
339  {
340  lwpgerror("Error calculating GEOSMaximumInscribedCircle.");
341  GEOSGeom_destroy(ginput);
342  PG_RETURN_NULL();
343  }
344  }
345  else
346  {
347  gcircle = GEOSLargestEmptyCircle(ginput, NULL, tolerance);
348  if (!gcircle)
349  {
350  lwpgerror("Error calculating GEOSLargestEmptyCircle.");
351  GEOSGeom_destroy(ginput);
352  PG_RETURN_NULL();
353  }
354  }
355 
356  gcenter = GEOSGeomGetStartPoint(gcircle);
357  gnearest = GEOSGeomGetEndPoint(gcircle);
358  GEOSDistance(gcenter, gnearest, &radius);
359  GEOSSetSRID(gcenter, srid);
360  GEOSSetSRID(gnearest, srid);
361 
362  center = GEOS2POSTGIS(gcenter, is3d);
363  nearest = GEOS2POSTGIS(gnearest, is3d);
364  GEOSGeom_destroy(gcenter);
365  GEOSGeom_destroy(gnearest);
366  GEOSGeom_destroy(gcircle);
367  GEOSGeom_destroy(ginput);
368  }
369 
370  get_call_result_type(fcinfo, NULL, &resultTupleDesc);
371  BlessTupleDesc(resultTupleDesc);
372 
373  result_values[0] = PointerGetDatum(center);
374  result_is_null[0] = false;
375  result_values[1] = PointerGetDatum(nearest);
376  result_is_null[1] = false;
377  result_values[2] = Float8GetDatum(radius);
378  result_is_null[2] = false;
379  resultTuple = heap_form_tuple(resultTupleDesc, result_values, result_is_null);
380 
381  result = HeapTupleGetDatum(resultTuple);
382 
383  PG_RETURN_DATUM(result);
384 
385 #endif /* POSTGIS_GEOS_VERSION >= 30900 */
386 }
387 
388 
389 /* ST_LargestEmptyCircle(geom, boundary, tolerance) */
391 Datum ST_LargestEmptyCircle(PG_FUNCTION_ARGS)
392 {
393 #if POSTGIS_GEOS_VERSION < 30900
394 
395  lwpgerror("The GEOS version this PostGIS binary "
396  "was compiled against (%d) doesn't support "
397  "'GEOSMaximumInscribedCircle' function (3.9.0+ required)",
399  PG_RETURN_NULL();
400 
401 #else /* POSTGIS_GEOS_VERSION >= 30900 */
402  GSERIALIZED* geom;
404  GSERIALIZED* center;
405  GSERIALIZED* nearest;
406  TupleDesc resultTupleDesc;
407  HeapTuple resultTuple;
408  Datum result;
409  Datum result_values[3];
410  bool result_is_null[3];
411  double radius = 0.0, tolerance = 0.0;
412  int32 srid = SRID_UNKNOWN;
413  bool is3d = false, hasBoundary = false;
414 
415  if (PG_ARGISNULL(0))
416  PG_RETURN_NULL();
417 
418  geom = PG_GETARG_GSERIALIZED_P(0);
419  tolerance = PG_GETARG_FLOAT8(1);
420  boundary = PG_GETARG_GSERIALIZED_P(2);
421  srid = gserialized_get_srid(geom);
422  is3d = gserialized_has_z(geom);
423 
425  hasBoundary = true;
426 
427  /* Empty geometry? Return POINT EMPTY with zero radius */
428  if (gserialized_is_empty(geom))
429  {
432  center = geometry_serialize(lwcenter);
433  nearest = geometry_serialize(lwnearest);
434  radius = 0.0;
435  }
436  else
437  {
438  GEOSGeometry *ginput, *gcircle, *gcenter, *gnearest;
439  GEOSGeometry *gboundary = NULL;
440  double width, height, size;
441  GBOX gbox;
442  LWGEOM *lwg;
443  lwg = lwgeom_from_gserialized(geom);
444  if (!lwgeom_isfinite(lwg))
445  {
446  lwpgerror("Geometry contains invalid coordinates");
447  PG_RETURN_NULL();
448  }
449  lwgeom_free(lwg);
450 
451 
452  if (!gserialized_get_gbox_p(geom, &gbox))
453  PG_RETURN_NULL();
454 
455  if (tolerance <= 0)
456  {
457  width = gbox.xmax - gbox.xmin;
458  height = gbox.ymax - gbox.ymin;
459  size = width > height ? width : height;
460  tolerance = size / 1000.0;
461  }
462 
463  initGEOS(lwpgnotice, lwgeom_geos_error);
464 
465  ginput = POSTGIS2GEOS(geom);
466  if (!ginput)
467  HANDLE_GEOS_ERROR("Geometry could not be converted to GEOS");
468 
469  if (hasBoundary)
470  {
471  gboundary = POSTGIS2GEOS(boundary);
472  if (!gboundary)
473  HANDLE_GEOS_ERROR("Boundary could not be converted to GEOS");
474  }
475 
476  gcircle = GEOSLargestEmptyCircle(ginput, gboundary, tolerance);
477  if (!gcircle)
478  {
479  lwpgerror("Error calculating GEOSLargestEmptyCircle.");
480  GEOSGeom_destroy(ginput);
481  PG_RETURN_NULL();
482  }
483 
484  gcenter = GEOSGeomGetStartPoint(gcircle);
485  gnearest = GEOSGeomGetEndPoint(gcircle);
486  GEOSDistance(gcenter, gnearest, &radius);
487  GEOSSetSRID(gcenter, srid);
488  GEOSSetSRID(gnearest, srid);
489 
490  center = GEOS2POSTGIS(gcenter, is3d);
491  nearest = GEOS2POSTGIS(gnearest, is3d);
492  GEOSGeom_destroy(gcenter);
493  GEOSGeom_destroy(gnearest);
494  GEOSGeom_destroy(gcircle);
495  GEOSGeom_destroy(ginput);
496  if (gboundary) GEOSGeom_destroy(gboundary);
497  }
498 
499  get_call_result_type(fcinfo, NULL, &resultTupleDesc);
500  BlessTupleDesc(resultTupleDesc);
501 
502  result_values[0] = PointerGetDatum(center);
503  result_is_null[0] = false;
504  result_values[1] = PointerGetDatum(nearest);
505  result_is_null[1] = false;
506  result_values[2] = Float8GetDatum(radius);
507  result_is_null[2] = false;
508  resultTuple = heap_form_tuple(resultTupleDesc, result_values, result_is_null);
509 
510  result = HeapTupleGetDatum(resultTuple);
511 
512  PG_RETURN_DATUM(result);
513 
514 #endif /* POSTGIS_GEOS_VERSION >= 30900 */
515 }
516 
517 
518 
527 Datum pgis_union_geometry_array(PG_FUNCTION_ARGS)
528 {
529  ArrayType *array;
530 
531  ArrayIterator iterator;
532  Datum value;
533  bool isnull;
534 
535  int is3d = LW_FALSE, gotsrid = LW_FALSE;
536  int nelems = 0, geoms_size = 0, curgeom = 0, count = 0;
537 
538  GSERIALIZED *gser_out = NULL;
539 
540  GEOSGeometry *g = NULL;
541  GEOSGeometry *g_union = NULL;
542  GEOSGeometry **geoms = NULL;
543 
544  int32_t srid = SRID_UNKNOWN;
545 
546  int empty_type = 0;
547 
548  /* Null array, null geometry (should be empty?) */
549  if ( PG_ARGISNULL(0) )
550  PG_RETURN_NULL();
551 
552  array = PG_GETARG_ARRAYTYPE_P(0);
553  nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
554 
555  /* Empty array? Null return */
556  if ( nelems == 0 ) PG_RETURN_NULL();
557 
558  /* Quick scan for nulls */
559  iterator = array_create_iterator(array, 0, NULL);
560  while (array_iterate(iterator, &value, &isnull))
561  {
562  /* Skip null array items */
563  if (isnull) continue;
564  count++;
565  }
566  array_free_iterator(iterator);
567 
568 
569  /* All-nulls? Return null */
570  if ( count == 0 )
571  PG_RETURN_NULL();
572 
573  /* Ok, we really need GEOS now ;) */
574  initGEOS(lwpgnotice, lwgeom_geos_error);
575 
576  /* One geom, good geom? Return it */
577  if ( count == 1 && nelems == 1 )
578  {
579 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
580 #pragma GCC diagnostic push
581 #pragma GCC diagnostic ignored "-Wsign-compare"
582 #endif
583  g = POSTGIS2GEOS((GSERIALIZED *)(ARR_DATA_PTR(array)));
584 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
585 #pragma GCC diagnostic pop
586 #endif
587  srid = GEOSGetSRID(g);
588  g_union = GEOSUnaryUnion(g);
589  GEOSGeom_destroy(g);
590  if (!g_union) HANDLE_GEOS_ERROR("GEOSUnaryUnion");
591  GEOSSetSRID(g_union, srid);
592  gser_out = GEOS2POSTGIS(g_union, is3d);
593  GEOSGeom_destroy(g_union);
594  PG_RETURN_POINTER(gser_out);
595  }
596 
597  /*
598  ** Collect the non-empty inputs and stuff them into a GEOS collection
599  */
600  geoms_size = nelems;
601  geoms = palloc(sizeof(GEOSGeometry*) * geoms_size);
602 
603  /*
604  ** We need to convert the array of GSERIALIZED into a GEOS collection.
605  ** First make an array of GEOS geometries.
606  */
607  iterator = array_create_iterator(array, 0, NULL);
608  while (array_iterate(iterator, &value, &isnull))
609  {
610  GSERIALIZED *gser_in;
611 
612  /* Skip null array items */
613  if (isnull) continue;
614  gser_in = (GSERIALIZED *)DatumGetPointer(value);
615 
616  /* Check for SRID mismatch in array elements */
617  if ( gotsrid )
618  gserialized_error_if_srid_mismatch_reference(gser_in, srid, __func__);
619  else
620  {
621  /* Initialize SRID/dimensions info */
622  srid = gserialized_get_srid(gser_in);
623  is3d = gserialized_has_z(gser_in);
624  gotsrid = 1;
625  }
626 
627  /* Don't include empties in the union */
628  if ( gserialized_is_empty(gser_in) )
629  {
630  int gser_type = gserialized_get_type(gser_in);
631  if (gser_type > empty_type)
632  {
633  empty_type = gser_type;
634  POSTGIS_DEBUGF(4, "empty_type = %d gser_type = %d", empty_type, gser_type);
635  }
636  }
637  else
638  {
639  g = POSTGIS2GEOS(gser_in);
640 
641  /* Uh oh! Exception thrown at construction... */
642  if ( ! g )
643  {
645  "One of the geometries in the set "
646  "could not be converted to GEOS");
647  }
648 
649  /* Ensure we have enough space in our storage array */
650  if ( curgeom == geoms_size )
651  {
652  geoms_size *= 2;
653  geoms = repalloc( geoms, sizeof(GEOSGeometry*) * geoms_size );
654  }
655 
656  geoms[curgeom] = g;
657  curgeom++;
658  }
659 
660  }
661  array_free_iterator(iterator);
662 
663  /*
664  ** Take our GEOS geometries and turn them into a GEOS collection,
665  ** then pass that into cascaded union.
666  */
667  if (curgeom > 0)
668  {
669  g = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, geoms, curgeom);
670  if (!g) HANDLE_GEOS_ERROR("Could not create GEOS COLLECTION from geometry array");
671 
672  g_union = GEOSUnaryUnion(g);
673  GEOSGeom_destroy(g);
674  if (!g_union) HANDLE_GEOS_ERROR("GEOSUnaryUnion");
675 
676  GEOSSetSRID(g_union, srid);
677  gser_out = GEOS2POSTGIS(g_union, is3d);
678  GEOSGeom_destroy(g_union);
679  }
680  /* No real geometries in our array, any empties? */
681  else
682  {
683  /* If it was only empties, we'll return the largest type number */
684  if ( empty_type > 0 )
685  {
686  PG_RETURN_POINTER(geometry_serialize(lwgeom_construct_empty(empty_type, srid, is3d, 0)));
687  }
688  /* Nothing but NULL, returns NULL */
689  else
690  {
691  PG_RETURN_NULL();
692  }
693  }
694 
695  if ( ! gser_out )
696  {
697  /* Union returned a NULL geometry */
698  PG_RETURN_NULL();
699  }
700 
701  PG_RETURN_POINTER(gser_out);
702 }
703 
704 
712 Datum ST_UnaryUnion(PG_FUNCTION_ARGS)
713 {
714  GSERIALIZED *geom1;
716  LWGEOM *lwgeom1, *lwresult ;
717  double prec = -1;
718 
719  geom1 = PG_GETARG_GSERIALIZED_P(0);
720  if (PG_NARGS() > 1 && ! PG_ARGISNULL(1))
721  prec = PG_GETARG_FLOAT8(1);
722 
723  lwgeom1 = lwgeom_from_gserialized(geom1) ;
724 
725  lwresult = lwgeom_unaryunion_prec(lwgeom1, prec);
726  result = geometry_serialize(lwresult) ;
727 
728  lwgeom_free(lwgeom1) ;
729  lwgeom_free(lwresult) ;
730 
731  PG_FREE_IF_COPY(geom1, 0);
732 
733  PG_RETURN_POINTER(result);
734 }
735 
737 Datum ST_Union(PG_FUNCTION_ARGS)
738 {
739  GSERIALIZED *geom1;
740  GSERIALIZED *geom2;
742  LWGEOM *lwgeom1, *lwgeom2, *lwresult;
743  double gridSize = -1;
744 
745  geom1 = PG_GETARG_GSERIALIZED_P(0);
746  geom2 = PG_GETARG_GSERIALIZED_P(1);
747  if (PG_NARGS() > 2 && ! PG_ARGISNULL(2))
748  gridSize = PG_GETARG_FLOAT8(2);
749 
750  lwgeom1 = lwgeom_from_gserialized(geom1);
751  lwgeom2 = lwgeom_from_gserialized(geom2);
752 
753  lwresult = lwgeom_union_prec(lwgeom1, lwgeom2, gridSize);
754  result = geometry_serialize(lwresult);
755 
756  lwgeom_free(lwgeom1);
757  lwgeom_free(lwgeom2);
758  lwgeom_free(lwresult);
759 
760  PG_FREE_IF_COPY(geom1, 0);
761  PG_FREE_IF_COPY(geom2, 1);
762 
763  PG_RETURN_POINTER(result);
764 }
765 
766 /* This is retained for backward ABI compatibility
767  * with PostGIS < 3.1.0 */
769 Datum symdifference(PG_FUNCTION_ARGS)
770 {
771  PG_RETURN_DATUM(DirectFunctionCall2(
773  PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)
774  ));
775 }
776 
783 Datum ST_SymDifference(PG_FUNCTION_ARGS)
784 {
785  GSERIALIZED *geom1;
786  GSERIALIZED *geom2;
788  LWGEOM *lwgeom1, *lwgeom2, *lwresult ;
789  double prec = -1;
790 
791  geom1 = PG_GETARG_GSERIALIZED_P(0);
792  geom2 = PG_GETARG_GSERIALIZED_P(1);
793  if (PG_NARGS() > 2 && ! PG_ARGISNULL(2))
794  prec = PG_GETARG_FLOAT8(2);
795 
796  lwgeom1 = lwgeom_from_gserialized(geom1) ;
797  lwgeom2 = lwgeom_from_gserialized(geom2) ;
798 
799  lwresult = lwgeom_symdifference_prec(lwgeom1, lwgeom2, prec);
800  result = geometry_serialize(lwresult) ;
801 
802  lwgeom_free(lwgeom1) ;
803  lwgeom_free(lwgeom2) ;
804  lwgeom_free(lwresult) ;
805 
806  PG_FREE_IF_COPY(geom1, 0);
807  PG_FREE_IF_COPY(geom2, 1);
808 
809  PG_RETURN_POINTER(result);
810 }
811 
813 Datum convexhull(PG_FUNCTION_ARGS)
814 {
815  GSERIALIZED *geom1;
816  GEOSGeometry *g1, *g3;
818  LWGEOM *lwout;
819  int32_t srid;
820  GBOX bbox;
821 
822  geom1 = PG_GETARG_GSERIALIZED_P(0);
823 
824  /* Empty.ConvexHull() == Empty */
825  if ( gserialized_is_empty(geom1) )
826  PG_RETURN_POINTER(geom1);
827 
828  srid = gserialized_get_srid(geom1);
829 
830  initGEOS(lwpgnotice, lwgeom_geos_error);
831 
832  g1 = POSTGIS2GEOS(geom1);
833 
834  if (!g1)
835  HANDLE_GEOS_ERROR("First argument geometry could not be converted to GEOS");
836 
837  g3 = GEOSConvexHull(g1);
838  GEOSGeom_destroy(g1);
839 
840  if (!g3) HANDLE_GEOS_ERROR("GEOSConvexHull");
841 
842  POSTGIS_DEBUGF(3, "result: %s", GEOSGeomToWKT(g3));
843 
844  GEOSSetSRID(g3, srid);
845 
846  lwout = GEOS2LWGEOM(g3, gserialized_has_z(geom1));
847  GEOSGeom_destroy(g3);
848 
849  if (!lwout)
850  {
851  elog(ERROR,
852  "convexhull() failed to convert GEOS geometry to LWGEOM");
853  PG_RETURN_NULL(); /* never get here */
854  }
855 
856  /* Copy input bbox if any */
857  if ( gserialized_get_gbox_p(geom1, &bbox) )
858  {
859  /* Force the box to have the same dimensionality as the lwgeom */
860  bbox.flags = lwout->flags;
861  lwout->bbox = gbox_copy(&bbox);
862  }
863 
864  result = geometry_serialize(lwout);
865  lwgeom_free(lwout);
866 
867  if (!result)
868  {
869  elog(ERROR,"GEOS convexhull() threw an error (result postgis geometry formation)!");
870  PG_RETURN_NULL(); /* never get here */
871  }
872 
873  PG_FREE_IF_COPY(geom1, 0);
874  PG_RETURN_POINTER(result);
875 }
876 
877 
879 Datum ST_ConcaveHull(PG_FUNCTION_ARGS)
880 {
881 #if POSTGIS_GEOS_VERSION < 31100
882 
883  lwpgerror("The GEOS version this PostGIS binary "
884  "was compiled against (%d) doesn't support "
885  "'GEOSConcaveHull' function (3.11.0+ required)",
887  PG_RETURN_NULL();
888 
889 #else /* POSTGIS_GEOS_VERSION >= 31100 */
890  GSERIALIZED* geom = PG_GETARG_GSERIALIZED_P(0);
891  double ratio = PG_GETARG_FLOAT8(1);
892  bool allow_holes = PG_GETARG_BOOL(2);
893 
894  LWGEOM* lwgeom = lwgeom_from_gserialized(geom);
895  LWGEOM* lwresult = lwgeom_concavehull(lwgeom, ratio, allow_holes);
896  GSERIALIZED* result = geometry_serialize(lwresult);
897 
898  lwgeom_free(lwgeom);
899  lwgeom_free(lwresult);
900  PG_FREE_IF_COPY(geom, 0);
901  PG_RETURN_POINTER(result);
902 #endif
903 }
904 
905 
907 Datum ST_SimplifyPolygonHull(PG_FUNCTION_ARGS)
908 {
909 #if POSTGIS_GEOS_VERSION < 31100
910 
911  lwpgerror("The GEOS version this PostGIS binary "
912  "was compiled against (%d) doesn't support "
913  "'ST_SimplifyPolygonHull' function (3.11.0+ required)",
915  PG_RETURN_NULL();
916 
917 #else /* POSTGIS_GEOS_VERSION >= 31100 */
918  GSERIALIZED* geom = PG_GETARG_GSERIALIZED_P(0);
919  double vertex_fraction = PG_GETARG_FLOAT8(1);
920  uint32_t is_outer = PG_GETARG_BOOL(2);
921 
922  LWGEOM* lwgeom = lwgeom_from_gserialized(geom);
923  LWGEOM* lwresult = lwgeom_simplify_polygonal(lwgeom, vertex_fraction, is_outer);
924  GSERIALIZED* result = geometry_serialize(lwresult);
925 
926  lwgeom_free(lwgeom);
927  lwgeom_free(lwresult);
928  PG_FREE_IF_COPY(geom, 0);
929  PG_RETURN_POINTER(result);
930 #endif
931 }
932 
933 
935 Datum topologypreservesimplify(PG_FUNCTION_ARGS)
936 {
937  GSERIALIZED *gs1;
938  LWGEOM *lwg1;
939  double tolerance;
940  GEOSGeometry *g1, *g3;
942  uint32_t type;
943 
944  gs1 = PG_GETARG_GSERIALIZED_P(0);
945  tolerance = PG_GETARG_FLOAT8(1);
946  lwg1 = lwgeom_from_gserialized(gs1);
947 
948  /* Empty.Simplify() == Empty */
949  type = lwgeom_get_type(lwg1);
950  if (lwgeom_is_empty(lwg1) || type == TINTYPE || type == TRIANGLETYPE)
951  PG_RETURN_POINTER(gs1);
952 
953  if (!lwgeom_isfinite(lwg1))
954  {
955  lwpgerror("Geometry contains invalid coordinates");
956  PG_RETURN_NULL();
957  }
958 
959  initGEOS(lwpgnotice, lwgeom_geos_error);
960 
961  g1 = LWGEOM2GEOS(lwg1, LW_TRUE);
962  lwgeom_free(lwg1);
963  if (!g1)
964  HANDLE_GEOS_ERROR("First argument geometry could not be converted to GEOS");
965 
966  g3 = GEOSTopologyPreserveSimplify(g1,tolerance);
967  GEOSGeom_destroy(g1);
968 
969  if (!g3) HANDLE_GEOS_ERROR("GEOSTopologyPreserveSimplify");
970 
971  POSTGIS_DEBUGF(3, "result: %s", GEOSGeomToWKT(g3));
972 
973  GEOSSetSRID(g3, gserialized_get_srid(gs1));
974 
976  GEOSGeom_destroy(g3);
977 
978  if (!result)
979  {
980  elog(ERROR,"GEOS topologypreservesimplify() threw an error (result postgis geometry formation)!");
981  PG_RETURN_NULL(); /* never get here */
982  }
983 
984  PG_FREE_IF_COPY(gs1, 0);
985  PG_RETURN_POINTER(result);
986 }
987 
989 Datum buffer(PG_FUNCTION_ARGS)
990 {
991  GEOSBufferParams *bufferparams;
992  GEOSGeometry *g1, *g3 = NULL;
994  LWGEOM *lwg;
995  int quadsegs = 8; /* the default */
996  int singleside = 0; /* the default */
997  enum
998  {
999  ENDCAP_ROUND = 1,
1000  ENDCAP_FLAT = 2,
1001  ENDCAP_SQUARE = 3
1002  };
1003  enum
1004  {
1005  JOIN_ROUND = 1,
1006  JOIN_MITRE = 2,
1007  JOIN_BEVEL = 3
1008  };
1009  const double DEFAULT_MITRE_LIMIT = 5.0;
1010  const int DEFAULT_ENDCAP_STYLE = ENDCAP_ROUND;
1011  const int DEFAULT_JOIN_STYLE = JOIN_ROUND;
1012  double mitreLimit = DEFAULT_MITRE_LIMIT;
1013  int endCapStyle = DEFAULT_ENDCAP_STYLE;
1014  int joinStyle = DEFAULT_JOIN_STYLE;
1015 
1016  GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
1017  double size = PG_GETARG_FLOAT8(1);
1018  text *params_text;
1019 
1020  if (PG_NARGS() > 2)
1021  {
1022  params_text = PG_GETARG_TEXT_P(2);
1023  }
1024  else
1025  {
1026  params_text = cstring_to_text("");
1027  }
1028 
1029  /* Empty.Buffer() == Empty[polygon] */
1030  if ( gserialized_is_empty(geom1) )
1031  {
1033  gserialized_get_srid(geom1),
1034  0, 0)); // buffer wouldn't give back z or m anyway
1035  PG_RETURN_POINTER(geometry_serialize(lwg));
1036  }
1037 
1038  lwg = lwgeom_from_gserialized(geom1);
1039 
1040  if (!lwgeom_isfinite(lwg))
1041  {
1042  lwpgerror("Geometry contains invalid coordinates");
1043  PG_RETURN_NULL();
1044  }
1045  lwgeom_free(lwg);
1046 
1047  initGEOS(lwpgnotice, lwgeom_geos_error);
1048 
1049  g1 = POSTGIS2GEOS(geom1);
1050  if (!g1)
1051  HANDLE_GEOS_ERROR("First argument geometry could not be converted to GEOS");
1052 
1053 
1054  if (VARSIZE_ANY_EXHDR(params_text) > 0)
1055  {
1056  char *param;
1057  char *params = text_to_cstring(params_text);
1058 
1059  for (param=params; ; param=NULL)
1060  {
1061  char *key, *val;
1062  param = strtok(param, " ");
1063  if (!param) break;
1064  POSTGIS_DEBUGF(3, "Param: %s", param);
1065 
1066  key = param;
1067  val = strchr(key, '=');
1068  if (!val || *(val + 1) == '\0')
1069  {
1070  lwpgerror("Missing value for buffer parameter %s", key);
1071  break;
1072  }
1073  *val = '\0';
1074  ++val;
1075 
1076  POSTGIS_DEBUGF(3, "Param: %s : %s", key, val);
1077 
1078  if ( !strcmp(key, "endcap") )
1079  {
1080  /* Supported end cap styles:
1081  * "round", "flat", "square"
1082  */
1083  if ( !strcmp(val, "round") )
1084  {
1085  endCapStyle = ENDCAP_ROUND;
1086  }
1087  else if ( !strcmp(val, "flat") ||
1088  !strcmp(val, "butt") )
1089  {
1090  endCapStyle = ENDCAP_FLAT;
1091  }
1092  else if ( !strcmp(val, "square") )
1093  {
1094  endCapStyle = ENDCAP_SQUARE;
1095  }
1096  else
1097  {
1098  lwpgerror("Invalid buffer end cap "
1099  "style: %s (accept: "
1100  "'round', 'flat', 'butt' "
1101  "or 'square'"
1102  ")", val);
1103  break;
1104  }
1105 
1106  }
1107  else if ( !strcmp(key, "join") )
1108  {
1109  if ( !strcmp(val, "round") )
1110  {
1111  joinStyle = JOIN_ROUND;
1112  }
1113  else if ( !strcmp(val, "mitre") ||
1114  !strcmp(val, "miter") )
1115  {
1116  joinStyle = JOIN_MITRE;
1117  }
1118  else if ( !strcmp(val, "bevel") )
1119  {
1120  joinStyle = JOIN_BEVEL;
1121  }
1122  else
1123  {
1124  lwpgerror("Invalid buffer end cap "
1125  "style: %s (accept: "
1126  "'round', 'mitre', 'miter' "
1127  " or 'bevel'"
1128  ")", val);
1129  break;
1130  }
1131  }
1132  else if ( !strcmp(key, "mitre_limit") ||
1133  !strcmp(key, "miter_limit") )
1134  {
1135  /* mitreLimit is a float */
1136  mitreLimit = atof(val);
1137  }
1138  else if ( !strcmp(key, "quad_segs") )
1139  {
1140  /* quadrant segments is an int */
1141  quadsegs = atoi(val);
1142  }
1143  else if ( !strcmp(key, "side") )
1144  {
1145  if ( !strcmp(val, "both") )
1146  {
1147  singleside = 0;
1148  }
1149  else if ( !strcmp(val, "left") )
1150  {
1151  singleside = 1;
1152  }
1153  else if ( !strcmp(val, "right") )
1154  {
1155  singleside = 1;
1156  size *= -1;
1157  }
1158  else
1159  {
1160  lwpgerror("Invalid side parameter: %s (accept: 'right', 'left', 'both')", val);
1161  break;
1162  }
1163  }
1164  else
1165  {
1166  lwpgerror(
1167  "Invalid buffer parameter: %s (accept: 'endcap', 'join', 'mitre_limit', 'miter_limit', 'quad_segs' and 'side')",
1168  key);
1169  break;
1170  }
1171  }
1172  pfree(params); /* was pstrduped */
1173  }
1174 
1175 
1176  POSTGIS_DEBUGF(3, "endCap:%d joinStyle:%d mitreLimit:%g",
1177  endCapStyle, joinStyle, mitreLimit);
1178 
1179  bufferparams = GEOSBufferParams_create();
1180  if (bufferparams)
1181  {
1182  if (GEOSBufferParams_setEndCapStyle(bufferparams, endCapStyle) &&
1183  GEOSBufferParams_setJoinStyle(bufferparams, joinStyle) &&
1184  GEOSBufferParams_setMitreLimit(bufferparams, mitreLimit) &&
1185  GEOSBufferParams_setQuadrantSegments(bufferparams, quadsegs) &&
1186  GEOSBufferParams_setSingleSided(bufferparams, singleside))
1187  {
1188  g3 = GEOSBufferWithParams(g1, bufferparams, size);
1189  }
1190  else
1191  {
1192  lwpgerror("Error setting buffer parameters.");
1193  }
1194  GEOSBufferParams_destroy(bufferparams);
1195  }
1196  else
1197  {
1198  lwpgerror("Error setting buffer parameters.");
1199  }
1200 
1201  GEOSGeom_destroy(g1);
1202 
1203  if (!g3) HANDLE_GEOS_ERROR("GEOSBuffer");
1204 
1205  POSTGIS_DEBUGF(3, "result: %s", GEOSGeomToWKT(g3));
1206 
1207  GEOSSetSRID(g3, gserialized_get_srid(geom1));
1208 
1209  result = GEOS2POSTGIS(g3, gserialized_has_z(geom1));
1210  GEOSGeom_destroy(g3);
1211 
1212  if (!result)
1213  {
1214  elog(ERROR,"GEOS buffer() threw an error (result postgis geometry formation)!");
1215  PG_RETURN_NULL(); /* never get here */
1216  }
1217 
1218  PG_FREE_IF_COPY(geom1, 0);
1219  PG_RETURN_POINTER(result);
1220 }
1221 
1222 /*
1223 * Generate a field of random points within the area of a
1224 * polygon or multipolygon. Throws an error for other geometry
1225 * types.
1226 */
1227 Datum ST_GeneratePoints(PG_FUNCTION_ARGS);
1229 Datum ST_GeneratePoints(PG_FUNCTION_ARGS)
1230 {
1231  GSERIALIZED *gser_input;
1232  GSERIALIZED *gser_result;
1233  LWGEOM *lwgeom_input;
1234  LWGEOM *lwgeom_result;
1235  int32 npoints;
1236  int32 seed = 0;
1237 
1238  gser_input = PG_GETARG_GSERIALIZED_P(0);
1239  npoints = PG_GETARG_INT32(1);
1240 
1241  if (npoints < 0)
1242  PG_RETURN_NULL();
1243 
1244  if (PG_NARGS() > 2 && ! PG_ARGISNULL(2))
1245  {
1246  seed = PG_GETARG_INT32(2);
1247  if (seed < 1)
1248  {
1249  lwpgerror("ST_GeneratePoints: seed must be greater than zero");
1250  PG_RETURN_NULL();
1251  }
1252  }
1253 
1254  /* Types get checked in the code, we'll keep things small out there */
1255  lwgeom_input = lwgeom_from_gserialized(gser_input);
1256  lwgeom_result = (LWGEOM*)lwgeom_to_points(lwgeom_input, npoints, seed);
1257  lwgeom_free(lwgeom_input);
1258  PG_FREE_IF_COPY(gser_input, 0);
1259 
1260  /* Return null as null */
1261  if (!lwgeom_result)
1262  PG_RETURN_NULL();
1263 
1264  /* Serialize and return */
1265  gser_result = geometry_serialize(lwgeom_result);
1266  lwgeom_free(lwgeom_result);
1267  PG_RETURN_POINTER(gser_result);
1268 }
1269 
1270 
1271 /*
1272 * Compute at offset curve to a line
1273 */
1274 Datum ST_OffsetCurve(PG_FUNCTION_ARGS);
1276 Datum ST_OffsetCurve(PG_FUNCTION_ARGS)
1277 {
1278  GSERIALIZED *gser_input;
1279  GSERIALIZED *gser_result;
1280  LWGEOM *lwgeom_input;
1281  LWGEOM *lwgeom_result;
1282  double size;
1283  int quadsegs = 8; /* the default */
1284  int nargs;
1285 
1286  enum
1287  {
1288  JOIN_ROUND = 1,
1289  JOIN_MITRE = 2,
1290  JOIN_BEVEL = 3
1291  };
1292 
1293  static const double DEFAULT_MITRE_LIMIT = 5.0;
1294  static const int DEFAULT_JOIN_STYLE = JOIN_ROUND;
1295  double mitreLimit = DEFAULT_MITRE_LIMIT;
1296  int joinStyle = DEFAULT_JOIN_STYLE;
1297  char *param = NULL;
1298  char *paramstr = NULL;
1299 
1300  /* Read SQL arguments */
1301  nargs = PG_NARGS();
1302  gser_input = PG_GETARG_GSERIALIZED_P(0);
1303  size = PG_GETARG_FLOAT8(1);
1304 
1305  /* For distance == 0, just return the input. */
1306  if (size == 0) PG_RETURN_POINTER(gser_input);
1307 
1308  /* Read the lwgeom, check for errors */
1309  lwgeom_input = lwgeom_from_gserialized(gser_input);
1310  if ( ! lwgeom_input )
1311  lwpgerror("ST_OffsetCurve: lwgeom_from_gserialized returned NULL");
1312 
1313  /* For empty inputs, just echo them back */
1314  if ( lwgeom_is_empty(lwgeom_input) )
1315  PG_RETURN_POINTER(gser_input);
1316 
1317  /* Process the optional arguments */
1318  if ( nargs > 2 )
1319  {
1320  text *wkttext = PG_GETARG_TEXT_P(2);
1321  paramstr = text_to_cstring(wkttext);
1322 
1323  POSTGIS_DEBUGF(3, "paramstr: %s", paramstr);
1324 
1325  for ( param=paramstr; ; param=NULL )
1326  {
1327  char *key, *val;
1328  param = strtok(param, " ");
1329  if (!param) break;
1330  POSTGIS_DEBUGF(3, "Param: %s", param);
1331 
1332  key = param;
1333  val = strchr(key, '=');
1334  if (!val || *(val + 1) == '\0')
1335  {
1336  lwpgerror("ST_OffsetCurve: Missing value for buffer parameter %s", key);
1337  break;
1338  }
1339  *val = '\0';
1340  ++val;
1341 
1342  POSTGIS_DEBUGF(3, "Param: %s : %s", key, val);
1343 
1344  if ( !strcmp(key, "join") )
1345  {
1346  if ( !strcmp(val, "round") )
1347  {
1348  joinStyle = JOIN_ROUND;
1349  }
1350  else if ( !(strcmp(val, "mitre") && strcmp(val, "miter")) )
1351  {
1352  joinStyle = JOIN_MITRE;
1353  }
1354  else if ( ! strcmp(val, "bevel") )
1355  {
1356  joinStyle = JOIN_BEVEL;
1357  }
1358  else
1359  {
1360  lwpgerror(
1361  "Invalid buffer end cap style: %s (accept: 'round', 'mitre', 'miter' or 'bevel')",
1362  val);
1363  break;
1364  }
1365  }
1366  else if ( !strcmp(key, "mitre_limit") ||
1367  !strcmp(key, "miter_limit") )
1368  {
1369  /* mitreLimit is a float */
1370  mitreLimit = atof(val);
1371  }
1372  else if ( !strcmp(key, "quad_segs") )
1373  {
1374  /* quadrant segments is an int */
1375  quadsegs = atoi(val);
1376  }
1377  else
1378  {
1379  lwpgerror(
1380  "Invalid buffer parameter: %s (accept: 'join', 'mitre_limit', 'miter_limit and 'quad_segs')",
1381  key);
1382  break;
1383  }
1384  }
1385  POSTGIS_DEBUGF(3, "joinStyle:%d mitreLimit:%g", joinStyle, mitreLimit);
1386  pfree(paramstr); /* alloc'ed in text_to_cstring */
1387  }
1388 
1389  lwgeom_result = lwgeom_offsetcurve(lwgeom_input, size, quadsegs, joinStyle, mitreLimit);
1390 
1391  if (!lwgeom_result)
1392  lwpgerror("ST_OffsetCurve: lwgeom_offsetcurve returned NULL");
1393 
1394  gser_result = geometry_serialize(lwgeom_result);
1395  lwgeom_free(lwgeom_input);
1396  lwgeom_free(lwgeom_result);
1397  PG_RETURN_POINTER(gser_result);
1398 }
1399 
1401 Datum ST_Intersection(PG_FUNCTION_ARGS)
1402 {
1403  GSERIALIZED *geom1;
1404  GSERIALIZED *geom2;
1406  LWGEOM *lwgeom1, *lwgeom2, *lwresult;
1407  double prec = -1;
1408 
1409  geom1 = PG_GETARG_GSERIALIZED_P(0);
1410  geom2 = PG_GETARG_GSERIALIZED_P(1);
1411  if (PG_NARGS() > 2 && ! PG_ARGISNULL(2))
1412  prec = PG_GETARG_FLOAT8(2);
1413 
1414  lwgeom1 = lwgeom_from_gserialized(geom1);
1415  lwgeom2 = lwgeom_from_gserialized(geom2);
1416 
1417  lwresult = lwgeom_intersection_prec(lwgeom1, lwgeom2, prec);
1418  result = geometry_serialize(lwresult);
1419 
1420  lwgeom_free(lwgeom1);
1421  lwgeom_free(lwgeom2);
1422  lwgeom_free(lwresult);
1423 
1424  PG_FREE_IF_COPY(geom1, 0);
1425  PG_FREE_IF_COPY(geom2, 1);
1426 
1427  PG_RETURN_POINTER(result);
1428 }
1429 
1431 Datum ST_Difference(PG_FUNCTION_ARGS)
1432 {
1433  GSERIALIZED *geom1;
1434  GSERIALIZED *geom2;
1436  LWGEOM *lwgeom1, *lwgeom2, *lwresult;
1437  double prec = -1;
1438 
1439  geom1 = PG_GETARG_GSERIALIZED_P(0);
1440  geom2 = PG_GETARG_GSERIALIZED_P(1);
1441  if (PG_NARGS() > 2 && ! PG_ARGISNULL(2))
1442  prec = PG_GETARG_FLOAT8(2);
1443 
1444  lwgeom1 = lwgeom_from_gserialized(geom1);
1445  lwgeom2 = lwgeom_from_gserialized(geom2);
1446 
1447  lwresult = lwgeom_difference_prec(lwgeom1, lwgeom2, prec);
1448  result = geometry_serialize(lwresult);
1449 
1450  lwgeom_free(lwgeom1);
1451  lwgeom_free(lwgeom2);
1452  lwgeom_free(lwresult);
1453 
1454  PG_FREE_IF_COPY(geom1, 0);
1455  PG_FREE_IF_COPY(geom2, 1);
1456 
1457  PG_RETURN_POINTER(result);
1458 }
1459 
1465 Datum pointonsurface(PG_FUNCTION_ARGS)
1466 {
1467  GSERIALIZED *geom, *result;
1468  LWGEOM *lwgeom, *lwresult;
1469 
1470  geom = PG_GETARG_GSERIALIZED_P(0);
1471 
1472  lwgeom = lwgeom_from_gserialized(geom);
1473  lwresult = lwgeom_pointonsurface(lwgeom);
1474  lwgeom_free(lwgeom);
1475  PG_FREE_IF_COPY(geom, 0);
1476 
1477  if (!lwresult) PG_RETURN_NULL();
1478 
1479  result = geometry_serialize(lwresult);
1480  lwgeom_free(lwresult);
1481  PG_RETURN_POINTER(result);
1482 }
1483 
1485 Datum centroid(PG_FUNCTION_ARGS)
1486 {
1487  GSERIALIZED *geom, *result;
1488  LWGEOM *lwgeom, *lwresult;
1489 
1490  geom = PG_GETARG_GSERIALIZED_P(0);
1491 
1492  lwgeom = lwgeom_from_gserialized(geom);
1493  lwresult = lwgeom_centroid(lwgeom);
1494  lwgeom_free(lwgeom);
1495  PG_FREE_IF_COPY(geom, 0);
1496 
1497  if (!lwresult) PG_RETURN_NULL();
1498 
1499  result = geometry_serialize(lwresult);
1500  lwgeom_free(lwresult);
1501  PG_RETURN_POINTER(result);
1502 }
1503 
1504 Datum ST_ReducePrecision(PG_FUNCTION_ARGS);
1506 Datum ST_ReducePrecision(PG_FUNCTION_ARGS)
1507 {
1508  GSERIALIZED *geom, *result;
1509  LWGEOM *lwgeom, *lwresult;
1510  double gridSize = PG_GETARG_FLOAT8(1);
1511  geom = PG_GETARG_GSERIALIZED_P(0);
1512 
1513  lwgeom = lwgeom_from_gserialized(geom);
1514  lwresult = lwgeom_reduceprecision(lwgeom, gridSize);
1515  lwgeom_free(lwgeom);
1516  PG_FREE_IF_COPY(geom, 0);
1517 
1518  if (!lwresult) PG_RETURN_NULL();
1519 
1520  result = geometry_serialize(lwresult);
1521  lwgeom_free(lwresult);
1522  PG_RETURN_POINTER(result);
1523 }
1524 
1525 Datum ST_ClipByBox2d(PG_FUNCTION_ARGS);
1527 Datum ST_ClipByBox2d(PG_FUNCTION_ARGS)
1528 {
1529  static const uint32_t geom_idx = 0;
1530  static const uint32_t box2d_idx = 1;
1532  LWGEOM *lwgeom1, *lwresult ;
1533  GBOX bbox1 = {0};
1534  GBOX *bbox2;
1535  uint8_t type;
1536  int32_t srid;
1537  lwflags_t flags;
1538 
1539  if (!gserialized_datum_get_internals_p(PG_GETARG_DATUM(geom_idx), &bbox1, &flags, &type, &srid))
1540  {
1541  /* empty clips to empty, no matter rect */
1542  PG_RETURN_DATUM(PG_GETARG_DATUM(geom_idx));
1543  }
1544 
1545  /* WARNING: this is really a BOX2DF, use only xmin and ymin fields */
1546  bbox2 = (GBOX *)PG_GETARG_POINTER(box2d_idx);
1547  bbox2->flags = 0;
1548 
1549  /* if bbox1 is covered by bbox2, return lwgeom1 */
1550  if (gbox_contains_2d(bbox2, &bbox1))
1551  {
1552  PG_RETURN_DATUM(PG_GETARG_DATUM(geom_idx));
1553  }
1554 
1555  /* If bbox1 outside of bbox2, return empty */
1556  if (!gbox_overlaps_2d(&bbox1, bbox2))
1557  {
1558  /* Get type and srid from datum */
1559  lwresult = lwgeom_construct_empty(type, srid, 0, 0);
1560  result = geometry_serialize(lwresult) ;
1561  lwgeom_free(lwresult) ;
1562  PG_RETURN_POINTER(result);
1563  }
1564 
1565  lwgeom1 = lwgeom_from_gserialized(PG_GETARG_GSERIALIZED_P(geom_idx));
1566  lwresult = lwgeom_clip_by_rect(lwgeom1, bbox2->xmin, bbox2->ymin,
1567  bbox2->xmax, bbox2->ymax);
1568 
1569  lwgeom_free(lwgeom1);
1570 
1571  if (!lwresult)
1572  PG_RETURN_NULL();
1573 
1574  result = geometry_serialize(lwresult) ;
1575  PG_RETURN_POINTER(result);
1576 }
1577 
1578 
1579 /*---------------------------------------------*/
1580 
1582 Datum isvalid(PG_FUNCTION_ARGS)
1583 {
1584  GSERIALIZED *geom1;
1585  LWGEOM *lwgeom;
1586  char result;
1587  GEOSGeom g1;
1588 
1589  geom1 = PG_GETARG_GSERIALIZED_P(0);
1590 
1591  /* Empty.IsValid() == TRUE */
1592  if ( gserialized_is_empty(geom1) )
1593  PG_RETURN_BOOL(true);
1594 
1595  initGEOS(lwpgnotice, lwgeom_geos_error);
1596 
1597  lwgeom = lwgeom_from_gserialized(geom1);
1598  if ( ! lwgeom )
1599  {
1600  lwpgerror("unable to deserialize input");
1601  }
1602  g1 = LWGEOM2GEOS(lwgeom, 0);
1603  lwgeom_free(lwgeom);
1604 
1605  if ( ! g1 )
1606  {
1607  PG_RETURN_BOOL(false);
1608  }
1609 
1610  result = GEOSisValid(g1);
1611  GEOSGeom_destroy(g1);
1612 
1613  if (result == 2)
1614  {
1615  elog(ERROR,"GEOS isvalid() threw an error!");
1616  PG_RETURN_NULL(); /*never get here */
1617  }
1618 
1619  PG_FREE_IF_COPY(geom1, 0);
1620  PG_RETURN_BOOL(result);
1621 }
1622 
1624 Datum isvalidreason(PG_FUNCTION_ARGS)
1625 {
1626  GSERIALIZED *geom = NULL;
1627  char *reason_str = NULL;
1628  text *result = NULL;
1629  const GEOSGeometry *g1 = NULL;
1630 
1631  geom = PG_GETARG_GSERIALIZED_P(0);
1632 
1633  initGEOS(lwpgnotice, lwgeom_geos_error);
1634 
1635  g1 = POSTGIS2GEOS(geom);
1636  if ( g1 )
1637  {
1638  reason_str = GEOSisValidReason(g1);
1639  GEOSGeom_destroy((GEOSGeometry *)g1);
1640  if (!reason_str) HANDLE_GEOS_ERROR("GEOSisValidReason");
1641  result = cstring_to_text(reason_str);
1642  GEOSFree(reason_str);
1643  }
1644  else
1645  {
1646  result = cstring_to_text(lwgeom_geos_errmsg);
1647  }
1648 
1649  PG_FREE_IF_COPY(geom, 0);
1650  PG_RETURN_POINTER(result);
1651 }
1652 
1654 Datum isvaliddetail(PG_FUNCTION_ARGS)
1655 {
1656  GSERIALIZED *geom = NULL;
1657  const GEOSGeometry *g1 = NULL;
1658  char *values[3]; /* valid bool, reason text, location geometry */
1659  char *geos_reason = NULL;
1660  char *reason = NULL;
1661  GEOSGeometry *geos_location = NULL;
1662  LWGEOM *location = NULL;
1663  char valid = 0;
1664  HeapTupleHeader result;
1665  TupleDesc tupdesc;
1666  HeapTuple tuple;
1667  AttInMetadata *attinmeta;
1668  int flags = 0;
1669 
1670  /*
1671  * Build a tuple description for a
1672  * valid_detail tuple
1673  */
1674  get_call_result_type(fcinfo, 0, &tupdesc);
1675  BlessTupleDesc(tupdesc);
1676 
1677  /*
1678  * generate attribute metadata needed later to produce
1679  * tuples from raw C strings
1680  */
1681  attinmeta = TupleDescGetAttInMetadata(tupdesc);
1682 
1683  geom = PG_GETARG_GSERIALIZED_P(0);
1684  flags = PG_GETARG_INT32(1);
1685 
1686  initGEOS(lwpgnotice, lwgeom_geos_error);
1687 
1688  g1 = POSTGIS2GEOS(geom);
1689 
1690  if ( g1 )
1691  {
1692  valid = GEOSisValidDetail(g1, flags, &geos_reason, &geos_location);
1693  GEOSGeom_destroy((GEOSGeometry *)g1);
1694  if ( geos_reason )
1695  {
1696  reason = pstrdup(geos_reason);
1697  GEOSFree(geos_reason);
1698  }
1699  if ( geos_location )
1700  {
1701  location = GEOS2LWGEOM(geos_location, GEOSHasZ(geos_location));
1702  GEOSGeom_destroy(geos_location);
1703  }
1704 
1705  if (valid == 2)
1706  {
1707  /* NOTE: should only happen on OOM or similar */
1708  lwpgerror("GEOS isvaliddetail() threw an exception!");
1709  PG_RETURN_NULL(); /* never gets here */
1710  }
1711  }
1712  else
1713  {
1714  /* TODO: check lwgeom_geos_errmsg for validity error */
1715  reason = pstrdup(lwgeom_geos_errmsg);
1716  }
1717 
1718  /* the boolean validity */
1719  values[0] = valid ? "t" : "f";
1720 
1721  /* the reason */
1722  values[1] = reason;
1723 
1724  /* the location */
1725  values[2] = location ? lwgeom_to_hexwkb_buffer(location, WKB_EXTENDED) : 0;
1726 
1727  tuple = BuildTupleFromCStrings(attinmeta, values);
1728  result = (HeapTupleHeader) palloc(tuple->t_len);
1729  memcpy(result, tuple->t_data, tuple->t_len);
1730  heap_freetuple(tuple);
1731 
1732  PG_RETURN_HEAPTUPLEHEADER(result);
1733 }
1734 
1735 
1737 Datum issimple(PG_FUNCTION_ARGS)
1738 {
1739  GSERIALIZED *geom;
1740  LWGEOM *lwgeom_in;
1741  int result;
1742 
1743  POSTGIS_DEBUG(2, "issimple called");
1744 
1745  geom = PG_GETARG_GSERIALIZED_P(0);
1746 
1747  if ( gserialized_is_empty(geom) )
1748  PG_RETURN_BOOL(true);
1749 
1750  lwgeom_in = lwgeom_from_gserialized(geom);
1751  result = lwgeom_is_simple(lwgeom_in);
1752  lwgeom_free(lwgeom_in) ;
1753  PG_FREE_IF_COPY(geom, 0);
1754 
1755  if (result == -1) {
1756  PG_RETURN_NULL(); /*never get here */
1757  }
1758 
1759  PG_RETURN_BOOL(result);
1760 }
1761 
1763 Datum isring(PG_FUNCTION_ARGS)
1764 {
1765  GSERIALIZED *geom;
1766  GEOSGeometry *g1;
1767  int result;
1768 
1769  geom = PG_GETARG_GSERIALIZED_P(0);
1770 
1771  /* Empty things can't close */
1772  if ( gserialized_is_empty(geom) )
1773  PG_RETURN_BOOL(false);
1774 
1775  initGEOS(lwpgnotice, lwgeom_geos_error);
1776 
1777  g1 = POSTGIS2GEOS(geom);
1778  if (!g1)
1779  HANDLE_GEOS_ERROR("First argument geometry could not be converted to GEOS");
1780 
1781  if ( GEOSGeomTypeId(g1) != GEOS_LINESTRING )
1782  {
1783  GEOSGeom_destroy(g1);
1784  elog(ERROR, "ST_IsRing() should only be called on a linear feature");
1785  }
1786 
1787  result = GEOSisRing(g1);
1788  GEOSGeom_destroy(g1);
1789 
1790  if (result == 2) HANDLE_GEOS_ERROR("GEOSisRing");
1791 
1792  PG_FREE_IF_COPY(geom, 0);
1793  PG_RETURN_BOOL(result);
1794 }
1795 
1796 GSERIALIZED *
1797 GEOS2POSTGIS(GEOSGeom geom, char want3d)
1798 {
1799  LWGEOM *lwgeom;
1801 
1802  lwgeom = GEOS2LWGEOM(geom, want3d);
1803  if ( ! lwgeom )
1804  {
1805  lwpgerror("%s: GEOS2LWGEOM returned NULL", __func__);
1806  return NULL;
1807  }
1808 
1809  POSTGIS_DEBUGF(4, "%s: GEOS2LWGEOM returned a %s", __func__, lwgeom_summary(lwgeom, 0));
1810 
1811  if (lwgeom_needs_bbox(lwgeom)) lwgeom_add_bbox(lwgeom);
1812 
1813  result = geometry_serialize(lwgeom);
1814  lwgeom_free(lwgeom);
1815 
1816  return result;
1817 }
1818 
1819 /*-----=POSTGIS2GEOS= */
1820 
1821 GEOSGeometry *
1822 POSTGIS2GEOS(const GSERIALIZED *pglwgeom)
1823 {
1824  GEOSGeometry *ret;
1825  LWGEOM *lwgeom = lwgeom_from_gserialized(pglwgeom);
1826  if ( ! lwgeom )
1827  {
1828  lwpgerror("POSTGIS2GEOS: unable to deserialize input");
1829  return NULL;
1830  }
1831  ret = LWGEOM2GEOS(lwgeom, 0);
1832  lwgeom_free(lwgeom);
1833 
1834  return ret;
1835 }
1836 
1837 static uint32_t
1838 array_nelems_not_null(ArrayType* array)
1839 {
1840  ArrayIterator iterator;
1841  Datum value;
1842  bool isnull;
1843  uint32_t nelems_not_null = 0;
1844  iterator = array_create_iterator(array, 0, NULL);
1845  while(array_iterate(iterator, &value, &isnull))
1846  {
1847  if (!isnull)
1848  nelems_not_null++;
1849  }
1850 
1851  array_free_iterator(iterator);
1852 
1853  return nelems_not_null;
1854 }
1855 
1856 /* ARRAY2LWGEOM: Converts the non-null elements of a Postgres array into a LWGEOM* array */
1857 LWGEOM** ARRAY2LWGEOM(ArrayType* array, uint32_t nelems, int* is3d, int* srid)
1858 {
1859  ArrayIterator iterator;
1860  Datum value;
1861  bool isnull;
1862  bool gotsrid = false;
1863  uint32_t i = 0;
1864 
1865  LWGEOM** lw_geoms = palloc(nelems * sizeof(LWGEOM*));
1866 
1867  iterator = array_create_iterator(array, 0, NULL);
1868 
1869  while (array_iterate(iterator, &value, &isnull))
1870  {
1871  GSERIALIZED *geom = (GSERIALIZED *)DatumGetPointer(value);
1872 
1873  if (isnull)
1874  continue;
1875 
1876  *is3d = *is3d || gserialized_has_z(geom);
1877 
1878  lw_geoms[i] = lwgeom_from_gserialized(geom);
1879  if (!lw_geoms[i]) /* error in creation */
1880  {
1881  lwpgerror("Geometry deserializing geometry");
1882  return NULL;
1883  }
1884  if (!gotsrid)
1885  {
1886  gotsrid = true;
1887  *srid = gserialized_get_srid(geom);
1888  }
1889  else
1890  gserialized_error_if_srid_mismatch_reference(geom, *srid, __func__);
1891 
1892  i++;
1893  }
1894 
1895  return lw_geoms;
1896 }
1897 
1898 /* ARRAY2GEOS: Converts the non-null elements of a Postgres array into a GEOSGeometry* array */
1899 GEOSGeometry** ARRAY2GEOS(ArrayType* array, uint32_t nelems, int* is3d, int* srid)
1900 {
1901  ArrayIterator iterator;
1902  Datum value;
1903  bool isnull;
1904  bool gotsrid = false;
1905  uint32_t i = 0;
1906 
1907  GEOSGeometry** geos_geoms = palloc(nelems * sizeof(GEOSGeometry*));
1908 
1909  iterator = array_create_iterator(array, 0, NULL);
1910 
1911  while(array_iterate(iterator, &value, &isnull))
1912  {
1913  GSERIALIZED *geom = (GSERIALIZED*) DatumGetPointer(value);
1914 
1915  if (isnull)
1916  continue;
1917 
1918  *is3d = *is3d || gserialized_has_z(geom);
1919 
1920  geos_geoms[i] = POSTGIS2GEOS(geom);
1921  if (!geos_geoms[i])
1922  {
1923  uint32_t j;
1924  lwpgerror("Geometry could not be converted to GEOS");
1925 
1926  for (j = 0; j < i; j++) {
1927  GEOSGeom_destroy(geos_geoms[j]);
1928  }
1929  return NULL;
1930  }
1931 
1932  if (!gotsrid)
1933  {
1934  *srid = gserialized_get_srid(geom);
1935  gotsrid = true;
1936  }
1937  else if (*srid != gserialized_get_srid(geom))
1938  {
1939  uint32_t j;
1940  for (j = 0; j <= i; j++) {
1941  GEOSGeom_destroy(geos_geoms[j]);
1942  }
1943  gserialized_error_if_srid_mismatch_reference(geom, *srid, __func__);
1944  return NULL;
1945  }
1946 
1947  i++;
1948  }
1949 
1950  array_free_iterator(iterator);
1951  return geos_geoms;
1952 }
1953 
1955 Datum GEOSnoop(PG_FUNCTION_ARGS)
1956 {
1957  GSERIALIZED *geom;
1958  GEOSGeometry *geosgeom;
1959  GSERIALIZED *lwgeom_result;
1960 
1961  initGEOS(lwpgnotice, lwgeom_geos_error);
1962 
1963  geom = PG_GETARG_GSERIALIZED_P(0);
1964  geosgeom = POSTGIS2GEOS(geom);
1965  if ( ! geosgeom ) PG_RETURN_NULL();
1966 
1967  lwgeom_result = GEOS2POSTGIS(geosgeom, gserialized_has_z(geom));
1968  GEOSGeom_destroy(geosgeom);
1969 
1970  PG_FREE_IF_COPY(geom, 0);
1971 
1972  PG_RETURN_POINTER(lwgeom_result);
1973 }
1974 
1976 Datum polygonize_garray(PG_FUNCTION_ARGS)
1977 {
1978  ArrayType *array;
1979  int is3d = 0;
1980  uint32 nelems, i;
1982  GEOSGeometry *geos_result;
1983  const GEOSGeometry **vgeoms;
1984  int32_t srid = SRID_UNKNOWN;
1985 
1986  if (PG_ARGISNULL(0))
1987  PG_RETURN_NULL();
1988 
1989  array = PG_GETARG_ARRAYTYPE_P(0);
1990  nelems = array_nelems_not_null(array);
1991 
1992  if (nelems == 0)
1993  PG_RETURN_NULL();
1994 
1995  POSTGIS_DEBUGF(3, "polygonize_garray: number of non-null elements: %d", nelems);
1996 
1997  /* Ok, we really need geos now ;) */
1998  initGEOS(lwpgnotice, lwgeom_geos_error);
1999 
2000  vgeoms = (const GEOSGeometry**) ARRAY2GEOS(array, nelems, &is3d, &srid);
2001 
2002  POSTGIS_DEBUG(3, "polygonize_garray: invoking GEOSpolygonize");
2003 
2004  geos_result = GEOSPolygonize(vgeoms, nelems);
2005 
2006  POSTGIS_DEBUG(3, "polygonize_garray: GEOSpolygonize returned");
2007 
2008  for (i=0; i<nelems; ++i) GEOSGeom_destroy((GEOSGeometry *)vgeoms[i]);
2009  pfree(vgeoms);
2010 
2011  if ( ! geos_result ) PG_RETURN_NULL();
2012 
2013  GEOSSetSRID(geos_result, srid);
2014  result = GEOS2POSTGIS(geos_result, is3d);
2015  GEOSGeom_destroy(geos_result);
2016  if (!result)
2017  {
2018  elog(ERROR, "%s returned an error", __func__);
2019  PG_RETURN_NULL(); /*never get here */
2020  }
2021 
2022  PG_RETURN_POINTER(result);
2023 }
2024 
2025 
2027 Datum clusterintersecting_garray(PG_FUNCTION_ARGS)
2028 {
2029  Datum* result_array_data;
2030  ArrayType *array, *result;
2031  int is3d = 0;
2032  uint32 nelems, nclusters, i;
2033  GEOSGeometry **geos_inputs, **geos_results;
2034  int32_t srid = SRID_UNKNOWN;
2035 
2036  /* Parameters used to construct a result array */
2037  int16 elmlen;
2038  bool elmbyval;
2039  char elmalign;
2040 
2041  /* Null array, null geometry (should be empty?) */
2042  if (PG_ARGISNULL(0))
2043  PG_RETURN_NULL();
2044 
2045  array = PG_GETARG_ARRAYTYPE_P(0);
2046  nelems = array_nelems_not_null(array);
2047 
2048  POSTGIS_DEBUGF(3, "clusterintersecting_garray: number of non-null elements: %d", nelems);
2049 
2050  if ( nelems == 0 ) PG_RETURN_NULL();
2051 
2052  /* TODO short-circuit for one element? */
2053 
2054  /* Ok, we really need geos now ;) */
2055  initGEOS(lwpgnotice, lwgeom_geos_error);
2056 
2057  geos_inputs = ARRAY2GEOS(array, nelems, &is3d, &srid);
2058  if(!geos_inputs)
2059  {
2060  PG_RETURN_NULL();
2061  }
2062 
2063  if (cluster_intersecting(geos_inputs, nelems, &geos_results, &nclusters) != LW_SUCCESS)
2064  {
2065  elog(ERROR, "clusterintersecting: Error performing clustering");
2066  PG_RETURN_NULL();
2067  }
2068  pfree(geos_inputs); /* don't need to destroy items because GeometryCollections have taken ownership */
2069 
2070  if (!geos_results) PG_RETURN_NULL();
2071 
2072  result_array_data = palloc(nclusters * sizeof(Datum));
2073  for (i=0; i<nclusters; ++i)
2074  {
2075  result_array_data[i] = PointerGetDatum(GEOS2POSTGIS(geos_results[i], is3d));
2076  GEOSGeom_destroy(geos_results[i]);
2077  }
2078  lwfree(geos_results);
2079 
2080  get_typlenbyvalalign(array->elemtype, &elmlen, &elmbyval, &elmalign);
2081  result = construct_array(result_array_data, nclusters, array->elemtype, elmlen, elmbyval, elmalign);
2082 
2083  if (!result)
2084  {
2085  elog(ERROR, "clusterintersecting: Error constructing return-array");
2086  PG_RETURN_NULL();
2087  }
2088 
2089  PG_RETURN_POINTER(result);
2090 }
2091 
2093 Datum cluster_within_distance_garray(PG_FUNCTION_ARGS)
2094 {
2095  Datum* result_array_data;
2096  ArrayType *array, *result;
2097  int is3d = 0;
2098  uint32 nelems, nclusters, i;
2099  LWGEOM** lw_inputs;
2100  LWGEOM** lw_results;
2101  double tolerance;
2102  int32_t srid = SRID_UNKNOWN;
2103 
2104  /* Parameters used to construct a result array */
2105  int16 elmlen;
2106  bool elmbyval;
2107  char elmalign;
2108 
2109  /* Null array, null geometry (should be empty?) */
2110  if (PG_ARGISNULL(0))
2111  PG_RETURN_NULL();
2112 
2113  array = PG_GETARG_ARRAYTYPE_P(0);
2114 
2115  tolerance = PG_GETARG_FLOAT8(1);
2116  if (tolerance < 0)
2117  {
2118  lwpgerror("Tolerance must be a positive number.");
2119  PG_RETURN_NULL();
2120  }
2121 
2122  nelems = array_nelems_not_null(array);
2123 
2124  POSTGIS_DEBUGF(3, "cluster_within_distance_garray: number of non-null elements: %d", nelems);
2125 
2126  if ( nelems == 0 ) PG_RETURN_NULL();
2127 
2128  /* TODO short-circuit for one element? */
2129 
2130  /* Ok, we really need geos now ;) */
2131  initGEOS(lwpgnotice, lwgeom_geos_error);
2132 
2133  lw_inputs = ARRAY2LWGEOM(array, nelems, &is3d, &srid);
2134  if (!lw_inputs)
2135  {
2136  PG_RETURN_NULL();
2137  }
2138 
2139  if (cluster_within_distance(lw_inputs, nelems, tolerance, &lw_results, &nclusters) != LW_SUCCESS)
2140  {
2141  elog(ERROR, "cluster_within: Error performing clustering");
2142  PG_RETURN_NULL();
2143  }
2144  pfree(lw_inputs); /* don't need to destroy items because GeometryCollections have taken ownership */
2145 
2146  if (!lw_results) PG_RETURN_NULL();
2147 
2148  result_array_data = palloc(nclusters * sizeof(Datum));
2149  for (i=0; i<nclusters; ++i)
2150  {
2151  result_array_data[i] = PointerGetDatum(geometry_serialize(lw_results[i]));
2152  lwgeom_free(lw_results[i]);
2153  }
2154  lwfree(lw_results);
2155 
2156  get_typlenbyvalalign(array->elemtype, &elmlen, &elmbyval, &elmalign);
2157  result = construct_array(result_array_data, nclusters, array->elemtype, elmlen, elmbyval, elmalign);
2158 
2159  if (!result)
2160  {
2161  elog(ERROR, "clusterwithin: Error constructing return-array");
2162  PG_RETURN_NULL();
2163  }
2164 
2165  PG_RETURN_POINTER(result);
2166 }
2167 
2169 Datum linemerge(PG_FUNCTION_ARGS)
2170 {
2171  GSERIALIZED *geom1;
2172  bool directed = false;
2174  LWGEOM *lwgeom1, *lwresult ;
2175 
2176  geom1 = PG_GETARG_GSERIALIZED_P(0);
2177 
2178  if ( PG_NARGS() > 1 )
2179  directed = PG_GETARG_BOOL(1);
2180 
2181  lwgeom1 = lwgeom_from_gserialized(geom1) ;
2182 
2183  lwresult = lwgeom_linemerge_directed(lwgeom1, directed ? LW_TRUE : LW_FALSE);
2184  result = geometry_serialize(lwresult) ;
2185 
2186  lwgeom_free(lwgeom1) ;
2187  lwgeom_free(lwresult) ;
2188 
2189  PG_FREE_IF_COPY(geom1, 0);
2190 
2191  PG_RETURN_POINTER(result);
2192 }
2193 
2194 /*
2195  * Take a geometry and return an areal geometry
2196  * (Polygon or MultiPolygon).
2197  * Actually a wrapper around GEOSpolygonize,
2198  * transforming the resulting collection into
2199  * a valid polygon Geometry.
2200  */
2202 Datum ST_BuildArea(PG_FUNCTION_ARGS)
2203 {
2205  GSERIALIZED *geom;
2206  LWGEOM *lwgeom_in, *lwgeom_out;
2207 
2208  geom = PG_GETARG_GSERIALIZED_P(0);
2209  lwgeom_in = lwgeom_from_gserialized(geom);
2210 
2211  lwgeom_out = lwgeom_buildarea(lwgeom_in);
2212  lwgeom_free(lwgeom_in) ;
2213 
2214  if ( ! lwgeom_out )
2215  {
2216  PG_FREE_IF_COPY(geom, 0);
2217  PG_RETURN_NULL();
2218  }
2219 
2220  result = geometry_serialize(lwgeom_out) ;
2221  lwgeom_free(lwgeom_out) ;
2222 
2223  PG_FREE_IF_COPY(geom, 0);
2224  PG_RETURN_POINTER(result);
2225 }
2226 
2227 /*
2228  * Take the vertices of a geometry and builds
2229  * Delaunay triangles around them.
2230  */
2232 Datum ST_DelaunayTriangles(PG_FUNCTION_ARGS)
2233 {
2235  GSERIALIZED *geom;
2236  LWGEOM *lwgeom_in, *lwgeom_out;
2237  double tolerance = 0.0;
2238  int flags = 0;
2239 
2240  geom = PG_GETARG_GSERIALIZED_P(0);
2241  tolerance = PG_GETARG_FLOAT8(1);
2242  flags = PG_GETARG_INT32(2);
2243 
2244  lwgeom_in = lwgeom_from_gserialized(geom);
2245  lwgeom_out = lwgeom_delaunay_triangulation(lwgeom_in, tolerance, flags);
2246  lwgeom_free(lwgeom_in) ;
2247 
2248  if ( ! lwgeom_out )
2249  {
2250  PG_FREE_IF_COPY(geom, 0);
2251  PG_RETURN_NULL();
2252  }
2253 
2254  result = geometry_serialize(lwgeom_out) ;
2255  lwgeom_free(lwgeom_out) ;
2256 
2257  PG_FREE_IF_COPY(geom, 0);
2258  PG_RETURN_POINTER(result);
2259 }
2260 
2261 /*
2262  * Take a polygon and build a constrained
2263  * triangulation that respect the edges of the
2264  * polygon.
2265  */
2267 Datum ST_TriangulatePolygon(PG_FUNCTION_ARGS)
2268 {
2269 #if POSTGIS_GEOS_VERSION < 31100
2270 
2271  lwpgerror("The GEOS version this PostGIS binary "
2272  "was compiled against (%d) doesn't support "
2273  "'GEOSConstrainedDelaunayTriangulation' function (3.11.0+ required)",
2275  PG_RETURN_NULL();
2276 
2277 #else /* POSTGIS_GEOS_VERSION >= 31100 */
2279  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
2280  LWGEOM *lwgeom_in = lwgeom_from_gserialized(geom);
2281  LWGEOM *lwgeom_out = lwgeom_triangulate_polygon(lwgeom_in);
2282  lwgeom_free(lwgeom_in);
2283 
2284  if (!lwgeom_out)
2285  {
2286  PG_FREE_IF_COPY(geom, 0);
2287  PG_RETURN_NULL();
2288  }
2289 
2290  result = geometry_serialize(lwgeom_out);
2291  lwgeom_free(lwgeom_out);
2292 
2293  PG_FREE_IF_COPY(geom, 0);
2294  PG_RETURN_POINTER(result);
2295 #endif
2296 }
2297 
2298 /*
2299  * ST_Snap
2300  *
2301  * Snap a geometry to another with a given tolerance
2302  */
2303 Datum ST_Snap(PG_FUNCTION_ARGS);
2305 Datum ST_Snap(PG_FUNCTION_ARGS)
2306 {
2307  GSERIALIZED *geom1, *geom2, *result;
2308  LWGEOM *lwgeom1, *lwgeom2, *lwresult;
2309  double tolerance;
2310 
2311  geom1 = PG_GETARG_GSERIALIZED_P(0);
2312  geom2 = PG_GETARG_GSERIALIZED_P(1);
2313  tolerance = PG_GETARG_FLOAT8(2);
2314 
2315  lwgeom1 = lwgeom_from_gserialized(geom1);
2316  lwgeom2 = lwgeom_from_gserialized(geom2);
2317 
2318  lwresult = lwgeom_snap(lwgeom1, lwgeom2, tolerance);
2319  lwgeom_free(lwgeom1);
2320  lwgeom_free(lwgeom2);
2321  PG_FREE_IF_COPY(geom1, 0);
2322  PG_FREE_IF_COPY(geom2, 1);
2323 
2324  result = geometry_serialize(lwresult);
2325  lwgeom_free(lwresult);
2326 
2327  PG_RETURN_POINTER(result);
2328 }
2329 
2330 /*
2331  * ST_Split
2332  *
2333  * Split polygon by line, line by line, line by point.
2334  * Returns at most components as a collection.
2335  * First element of the collection is always the part which
2336  * remains after the cut, while the second element is the
2337  * part which has been cut out. We arbitrarily take the part
2338  * on the *right* of cut lines as the part which has been cut out.
2339  * For a line cut by a point the part which remains is the one
2340  * from start of the line to the cut point.
2341  *
2342  *
2343  * Author: Sandro Santilli <strk@kbt.io>
2344  *
2345  * Work done for Faunalia (http://www.faunalia.it) with fundings
2346  * from Regione Toscana - Sistema Informativo per il Governo
2347  * del Territorio e dell'Ambiente (RT-SIGTA).
2348  *
2349  * Thanks to the PostGIS community for sharing poly/line ideas [1]
2350  *
2351  * [1] http://trac.osgeo.org/postgis/wiki/UsersWikiSplitPolygonWithLineString
2352  *
2353  */
2354 Datum ST_Split(PG_FUNCTION_ARGS);
2356 Datum ST_Split(PG_FUNCTION_ARGS)
2357 {
2358  GSERIALIZED *in, *blade_in, *out;
2359  LWGEOM *lwgeom_in, *lwblade_in, *lwgeom_out;
2360 
2361  in = PG_GETARG_GSERIALIZED_P(0);
2362  blade_in = PG_GETARG_GSERIALIZED_P(1);
2363  gserialized_error_if_srid_mismatch(in, blade_in, __func__);
2364 
2365  lwgeom_in = lwgeom_from_gserialized(in);
2366  lwblade_in = lwgeom_from_gserialized(blade_in);
2367 
2368  if (!lwgeom_isfinite(lwgeom_in))
2369  {
2370  lwpgerror("Input Geometry contains invalid coordinates");
2371  PG_RETURN_NULL();
2372  }
2373 
2374  if (!lwgeom_isfinite(lwblade_in))
2375  {
2376  lwpgerror("Blade Geometry contains invalid coordinates");
2377  PG_RETURN_NULL();
2378  }
2379 
2380 
2381  lwgeom_out = lwgeom_split(lwgeom_in, lwblade_in);
2382  lwgeom_free(lwgeom_in);
2383  lwgeom_free(lwblade_in);
2384 
2385  if ( ! lwgeom_out )
2386  {
2387  PG_FREE_IF_COPY(in, 0); /* possibly referenced by lwgeom_out */
2388  PG_FREE_IF_COPY(blade_in, 1);
2389  PG_RETURN_NULL();
2390  }
2391 
2392  out = geometry_serialize(lwgeom_out);
2393  lwgeom_free(lwgeom_out);
2394  PG_FREE_IF_COPY(in, 0); /* possibly referenced by lwgeom_out */
2395  PG_FREE_IF_COPY(blade_in, 1);
2396 
2397  PG_RETURN_POINTER(out);
2398 }
2399 
2400 /**********************************************************************
2401  *
2402  * ST_SharedPaths
2403  *
2404  * Return the set of paths shared between two linear geometries,
2405  * and their direction (same or opposite).
2406  *
2407  * Developed by Sandro Santilli (strk@kbt.io) for Faunalia
2408  * (http://www.faunalia.it) with funding from Regione Toscana - Sistema
2409  * Informativo per la Gestione del Territorio e dell' Ambiente
2410  * [RT-SIGTA]". For the project: "Sviluppo strumenti software per il
2411  * trattamento di dati geografici basati su QuantumGIS e Postgis (CIG
2412  * 0494241492)"
2413  *
2414  **********************************************************************/
2415 Datum ST_SharedPaths(PG_FUNCTION_ARGS);
2417 Datum ST_SharedPaths(PG_FUNCTION_ARGS)
2418 {
2419  GSERIALIZED *geom1, *geom2, *out;
2420  LWGEOM *g1, *g2, *lwgeom_out;
2421 
2422  geom1 = PG_GETARG_GSERIALIZED_P(0);
2423  geom2 = PG_GETARG_GSERIALIZED_P(1);
2424 
2425  g1 = lwgeom_from_gserialized(geom1);
2426  g2 = lwgeom_from_gserialized(geom2);
2427 
2428  lwgeom_out = lwgeom_sharedpaths(g1, g2);
2429  lwgeom_free(g1);
2430  lwgeom_free(g2);
2431 
2432  if ( ! lwgeom_out )
2433  {
2434  PG_FREE_IF_COPY(geom1, 0);
2435  PG_FREE_IF_COPY(geom2, 1);
2436  PG_RETURN_NULL();
2437  }
2438 
2439  out = geometry_serialize(lwgeom_out);
2440  lwgeom_free(lwgeom_out);
2441 
2442  PG_FREE_IF_COPY(geom1, 0);
2443  PG_FREE_IF_COPY(geom2, 1);
2444  PG_RETURN_POINTER(out);
2445 }
2446 
2447 
2448 /**********************************************************************
2449  *
2450  * ST_Node
2451  *
2452  * Fully node a set of lines using the least possible nodes while
2453  * preserving all of the input ones.
2454  *
2455  **********************************************************************/
2456 Datum ST_Node(PG_FUNCTION_ARGS);
2458 Datum ST_Node(PG_FUNCTION_ARGS)
2459 {
2460  GSERIALIZED *geom1, *out;
2461  LWGEOM *g1, *lwgeom_out;
2462 
2463  geom1 = PG_GETARG_GSERIALIZED_P(0);
2464 
2465  g1 = lwgeom_from_gserialized(geom1);
2466 
2467  lwgeom_out = lwgeom_node(g1);
2468  lwgeom_free(g1);
2469 
2470  if ( ! lwgeom_out )
2471  {
2472  PG_FREE_IF_COPY(geom1, 0);
2473  PG_RETURN_NULL();
2474  }
2475 
2476  out = geometry_serialize(lwgeom_out);
2477  lwgeom_free(lwgeom_out);
2478 
2479  PG_FREE_IF_COPY(geom1, 0);
2480  PG_RETURN_POINTER(out);
2481 }
2482 
2483 /******************************************
2484  *
2485  * ST_Voronoi
2486  *
2487  * Returns a Voronoi diagram constructed
2488  * from the points of the input geometry.
2489  *
2490  ******************************************/
2491 Datum ST_Voronoi(PG_FUNCTION_ARGS);
2493 Datum ST_Voronoi(PG_FUNCTION_ARGS)
2494 {
2495  GSERIALIZED* input;
2496  GSERIALIZED* clip;
2498  LWGEOM* lwgeom_input;
2499  LWGEOM* lwgeom_result;
2500  double tolerance;
2501  GBOX clip_envelope;
2502  int custom_clip_envelope;
2503  int return_polygons;
2504 
2505  /* Return NULL on NULL geometry */
2506  if (PG_ARGISNULL(0))
2507  PG_RETURN_NULL();
2508 
2509  /* Read our tolerance value */
2510  if (PG_ARGISNULL(2))
2511  {
2512  lwpgerror("Tolerance must be a positive number.");
2513  PG_RETURN_NULL();
2514  }
2515 
2516  tolerance = PG_GETARG_FLOAT8(2);
2517 
2518  if (tolerance < 0)
2519  {
2520  lwpgerror("Tolerance must be a positive number.");
2521  PG_RETURN_NULL();
2522  }
2523 
2524  /* Are we returning lines or polygons? */
2525  if (PG_ARGISNULL(3))
2526  {
2527  lwpgerror("return_polygons must be true or false.");
2528  PG_RETURN_NULL();
2529  }
2530  return_polygons = PG_GETARG_BOOL(3);
2531 
2532  /* Read our clipping envelope, if applicable. */
2533  custom_clip_envelope = !PG_ARGISNULL(1);
2534  if (custom_clip_envelope) {
2535  clip = PG_GETARG_GSERIALIZED_P(1);
2536  if (!gserialized_get_gbox_p(clip, &clip_envelope))
2537  {
2538  lwpgerror("Could not determine envelope of clipping geometry.");
2539  PG_FREE_IF_COPY(clip, 1);
2540  PG_RETURN_NULL();
2541  }
2542  PG_FREE_IF_COPY(clip, 1);
2543  }
2544 
2545  /* Read our input geometry */
2546  input = PG_GETARG_GSERIALIZED_P(0);
2547 
2548  lwgeom_input = lwgeom_from_gserialized(input);
2549 
2550  if(!lwgeom_input)
2551  {
2552  lwpgerror("Could not read input geometry.");
2553  PG_FREE_IF_COPY(input, 0);
2554  PG_RETURN_NULL();
2555  }
2556 
2557  lwgeom_result = lwgeom_voronoi_diagram(lwgeom_input, custom_clip_envelope ? &clip_envelope : NULL, tolerance, !return_polygons);
2558  lwgeom_free(lwgeom_input);
2559 
2560  if (!lwgeom_result)
2561  {
2562  lwpgerror("Error computing Voronoi diagram.");
2563  PG_FREE_IF_COPY(input, 0);
2564  PG_RETURN_NULL();
2565  }
2566 
2567  result = geometry_serialize(lwgeom_result);
2568  lwgeom_free(lwgeom_result);
2569 
2570  PG_FREE_IF_COPY(input, 0);
2571  PG_RETURN_POINTER(result);
2572 }
2573 
2574 /******************************************
2575  *
2576  * ST_MinimumClearance
2577  *
2578  * Returns the minimum clearance of a geometry.
2579  *
2580  ******************************************/
2581 Datum ST_MinimumClearance(PG_FUNCTION_ARGS);
2583 Datum ST_MinimumClearance(PG_FUNCTION_ARGS)
2584 {
2585  GSERIALIZED* input;
2586  GEOSGeometry* input_geos;
2587  int error;
2588  double result;
2589 
2590  initGEOS(lwpgnotice, lwgeom_geos_error);
2591 
2592  input = PG_GETARG_GSERIALIZED_P(0);
2593  input_geos = POSTGIS2GEOS(input);
2594  if (!input_geos)
2595  HANDLE_GEOS_ERROR("Geometry could not be converted to GEOS");
2596 
2597  error = GEOSMinimumClearance(input_geos, &result);
2598  GEOSGeom_destroy(input_geos);
2599  if (error) HANDLE_GEOS_ERROR("Error computing minimum clearance");
2600 
2601  PG_FREE_IF_COPY(input, 0);
2602  PG_RETURN_FLOAT8(result);
2603 }
2604 
2605 /******************************************
2606  *
2607  * ST_MinimumClearanceLine
2608  *
2609  * Returns the minimum clearance line of a geometry.
2610  *
2611  ******************************************/
2612 Datum ST_MinimumClearanceLine(PG_FUNCTION_ARGS);
2614 Datum ST_MinimumClearanceLine(PG_FUNCTION_ARGS)
2615 {
2616  GSERIALIZED* input;
2618  GEOSGeometry* input_geos;
2619  GEOSGeometry* result_geos;
2620  int32_t srid;
2621 
2622  initGEOS(lwpgnotice, lwgeom_geos_error);
2623 
2624  input = PG_GETARG_GSERIALIZED_P(0);
2625  srid = gserialized_get_srid(input);
2626  input_geos = POSTGIS2GEOS(input);
2627  if (!input_geos)
2628  HANDLE_GEOS_ERROR("Geometry could not be converted to GEOS");
2629 
2630  result_geos = GEOSMinimumClearanceLine(input_geos);
2631  GEOSGeom_destroy(input_geos);
2632  if (!result_geos)
2633  HANDLE_GEOS_ERROR("Error computing minimum clearance");
2634 
2635  GEOSSetSRID(result_geos, srid);
2636  result = GEOS2POSTGIS(result_geos, LW_FALSE);
2637  GEOSGeom_destroy(result_geos);
2638 
2639  PG_FREE_IF_COPY(input, 0);
2640  PG_RETURN_POINTER(result);
2641 }
2642 
2643 /******************************************
2644  *
2645  * ST_OrientedEnvelope
2646  *
2647  ******************************************/
2648 Datum ST_OrientedEnvelope(PG_FUNCTION_ARGS);
2650 Datum ST_OrientedEnvelope(PG_FUNCTION_ARGS)
2651 {
2652  GSERIALIZED* input;
2654  GEOSGeometry* input_geos;
2655  GEOSGeometry* result_geos;
2656  int32_t srid;
2657 
2658  initGEOS(lwpgnotice, lwgeom_geos_error);
2659 
2660  input = PG_GETARG_GSERIALIZED_P(0);
2661  srid = gserialized_get_srid(input);
2662  input_geos = POSTGIS2GEOS(input);
2663  if (!input_geos)
2664  HANDLE_GEOS_ERROR("Geometry could not be converted to GEOS");
2665 
2666  result_geos = GEOSMinimumRotatedRectangle(input_geos);
2667  GEOSGeom_destroy(input_geos);
2668  if (!result_geos)
2669  HANDLE_GEOS_ERROR("Error computing oriented envelope");
2670 
2671  GEOSSetSRID(result_geos, srid);
2672  result = GEOS2POSTGIS(result_geos, LW_FALSE);
2673  GEOSGeom_destroy(result_geos);
2674 
2675  PG_FREE_IF_COPY(input, 0);
2676  PG_RETURN_POINTER(result);
2677 }
2678 
2679 
2685 Datum LWGEOM_dfullywithin(PG_FUNCTION_ARGS);
2687 Datum LWGEOM_dfullywithin(PG_FUNCTION_ARGS)
2688 {
2689  GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
2690  GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
2691  LWGEOM *lwg1 = lwgeom_from_gserialized(geom1);
2692  LWGEOM *lwg2 = lwgeom_from_gserialized(geom2);
2693  double radius = PG_GETARG_FLOAT8(2);
2694  GEOSGeometry *buffer1 = NULL;
2695  GEOSGeometry *geos1 = NULL, *geos2 = NULL;
2696  char contained;
2697 
2698  if (radius < 0.0)
2699  {
2700  elog(ERROR, "Tolerance cannot be less than zero\n");
2701  PG_RETURN_NULL();
2702  }
2703 
2704  gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
2705 
2706  if (lwgeom_is_empty(lwg1) || lwgeom_is_empty(lwg2))
2707  PG_RETURN_BOOL(false);
2708 
2709  if (!(lwgeom_isfinite(lwg1) && lwgeom_isfinite(lwg2)))
2710  PG_RETURN_BOOL(false);
2711 
2712  initGEOS(lwpgnotice, lwgeom_geos_error);
2713 
2714  geos1 = LWGEOM2GEOS(lwg1, true);
2715  geos2 = LWGEOM2GEOS(lwg2, true);
2716  lwgeom_free(lwg1);
2717  lwgeom_free(lwg2);
2718  if (!(geos1 && geos2))
2719  HANDLE_GEOS_ERROR("Geometry could not be converted to GEOS");
2720 
2721  buffer1 = GEOSBuffer(geos1, radius, 16);
2722  GEOSGeom_destroy(geos1);
2723  if (!(buffer1))
2724  HANDLE_GEOS_ERROR("Buffer operation failed");
2725 
2726  contained = GEOSCovers(buffer1, geos2);
2727  GEOSGeom_destroy(buffer1);
2728  GEOSGeom_destroy(geos2);
2729 
2730  if (contained == 2) HANDLE_GEOS_ERROR("GEOSContains");
2731 
2732  PG_FREE_IF_COPY(geom1, 0);
2733  PG_FREE_IF_COPY(geom2, 1);
2734 
2735  PG_RETURN_BOOL(contained == 1);
2736 }
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:267
int gbox_overlaps_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps on the 2d plane, LW_FALSE otherwise.
Definition: gbox.c:323
GBOX * gbox_copy(const GBOX *box)
Return a copy of the GBOX, based on dimensionality of flags.
Definition: gbox.c:438
int gbox_contains_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the first GBOX contains the second on the 2d plane, LW_FALSE otherwise.
Definition: gbox.c:339
void gserialized_error_if_srid_mismatch(const GSERIALIZED *g1, const GSERIALIZED *g2, const char *funcname)
Definition: gserialized.c:432
void gserialized_error_if_srid_mismatch_reference(const GSERIALIZED *g1, const int32_t srid2, const char *funcname)
Definition: gserialized.c:447
int32_t gserialized_get_srid(const GSERIALIZED *g)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
Definition: gserialized.c:155
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox)
Read the box from the GSERIALIZED or calculate it if necessary.
Definition: gserialized.c:94
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:268
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: gserialized.c:181
int gserialized_has_z(const GSERIALIZED *g)
Check if a GSERIALIZED has a Z ordinate.
Definition: gserialized.c:203
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition: gserialized.c:118
int gserialized_datum_get_internals_p(Datum gsdatum, GBOX *gbox, lwflags_t *flags, uint8_t *type, int32_t *srid)
Peak into a GSERIALIZED datum to find its bounding box and some other metadata.
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
void lwgeom_geos_error(const char *fmt,...)
void(*) LWGEOM GEOS2LWGEOM)(const GEOSGeometry *geom, uint8_t want3d)
int cluster_within_distance(LWGEOM **geoms, uint32_t num_geoms, double tolerance, LWGEOM ***clusterGeoms, uint32_t *num_clusters)
Takes an array of LWGEOM* and constructs an array of LWGEOM*, where each element in the constructed a...
int cluster_intersecting(GEOSGeometry **geoms, uint32_t num_geoms, GEOSGeometry ***clusterGeoms, uint32_t *num_clusters)
Takes an array of GEOSGeometry* and constructs an array of GEOSGeometry*, where each element in the c...
LWPOINT * lwpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoint.c:151
LWGEOM * lwgeom_centroid(const LWGEOM *geom)
#define LW_FALSE
Definition: liblwgeom.h:94
LWGEOM * lwgeom_delaunay_triangulation(const LWGEOM *geom, double tolerance, int32_t edgeOnly)
Take vertices of a geometry and build a delaunay triangulation on them.
LWGEOM * lwgeom_node(const LWGEOM *lwgeom_in)
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1218
LWGEOM * lwgeom_concavehull(const LWGEOM *geom, double ratio, uint32_t allow_holes)
Take a geometry and build the concave hull.
LWGEOM * lwgeom_split(const LWGEOM *lwgeom_in, const LWGEOM *blade_in)
LWGEOM * lwgeom_offsetcurve(const LWGEOM *geom, double size, int quadsegs, int joinStyle, double mitreLimit)
#define LW_SUCCESS
Definition: liblwgeom.h:97
uint16_t lwflags_t
Definition: liblwgeom.h:299
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:329
int lwgeom_is_simple(const LWGEOM *lwgeom)
LWGEOM * lwgeom_snap(const LWGEOM *geom1, const LWGEOM *geom2, double tolerance)
Snap vertices and segments of a geometry to another using a given tolerance.
char * lwgeom_to_hexwkb_buffer(const LWGEOM *geom, uint8_t variant)
Definition: lwout_wkb.c:845
const char * lwgeom_geos_version(void)
Return GEOS version string (not to be freed)
int lwgeom_needs_bbox(const LWGEOM *geom)
Check whether or not a lwgeom is big enough to warrant a bounding box.
Definition: lwgeom.c:1271
int lwgeom_isfinite(const LWGEOM *lwgeom)
Check if a LWGEOM has any non-finite (NaN or Inf) coordinates.
Definition: lwgeom.c:2807
char * lwgeom_summary(const LWGEOM *lwgeom, int offset)
Definition: lwgeom_debug.c:166
LWGEOM * lwgeom_pointonsurface(const LWGEOM *geom)
LWGEOM * lwgeom_sharedpaths(const LWGEOM *geom1, const LWGEOM *geom2)
#define TINTYPE
Definition: liblwgeom.h:116
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.
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
LWGEOM * lwgeom_difference_prec(const LWGEOM *geom1, const LWGEOM *geom2, double gridSize)
void lwfree(void *mem)
Definition: lwutil.c:248
LWGEOM * lwgeom_union_prec(const LWGEOM *geom1, const LWGEOM *geom2, double gridSize)
#define POLYGONTYPE
Definition: liblwgeom.h:104
LWGEOM * lwgeom_unaryunion_prec(const LWGEOM *geom1, double gridSize)
const char * lwgeom_geos_compiled_version(void)
LWGEOM * lwgeom_buildarea(const LWGEOM *geom)
Take a geometry and return an areal geometry (Polygon or MultiPolygon).
#define WKB_EXTENDED
Definition: liblwgeom.h:2209
LWGEOM * lwgeom_symdifference_prec(const LWGEOM *geom1, const LWGEOM *geom2, double gridSize)
LWGEOM * lwgeom_intersection_prec(const LWGEOM *geom1, const LWGEOM *geom2, double gridSize)
LWGEOM * lwgeom_linemerge_directed(const LWGEOM *geom1, int directed)
#define TRIANGLETYPE
Definition: liblwgeom.h:115
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_clip_by_rect(const LWGEOM *geom1, double x0, double y0, double x1, double y1)
LWGEOM * lwgeom_reduceprecision(const LWGEOM *geom, double gridSize)
LWMPOINT * lwgeom_to_points(const LWGEOM *lwgeom, uint32_t npoints, int32_t seed)
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:215
LWPOLY * lwpoly_construct_empty(int32_t srid, char hasz, char hasm)
Definition: lwpoly.c:161
LWGEOM * lwgeom_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
Definition: lwgeom.c:2191
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition: lwgeom.c:695
This library is the generic geometry handling section of PostGIS.
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:141
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
int value
Definition: genraster.py:62
int count
Definition: genraster.py:57
type
Definition: ovdump.py:42
Datum ST_ReducePrecision(PG_FUNCTION_ARGS)
Datum polygonize_garray(PG_FUNCTION_ARGS)
Datum ST_Intersection(PG_FUNCTION_ARGS)
Datum isvaliddetail(PG_FUNCTION_ARGS)
Datum isvalid(PG_FUNCTION_ARGS)
Datum ST_SimplifyPolygonHull(PG_FUNCTION_ARGS)
Datum ST_Snap(PG_FUNCTION_ARGS)
Datum ST_Union(PG_FUNCTION_ARGS)
Datum ST_BuildArea(PG_FUNCTION_ARGS)
Datum postgis_geos_compiled_version(PG_FUNCTION_ARGS)
Datum ST_Split(PG_FUNCTION_ARGS)
Datum ST_Node(PG_FUNCTION_ARGS)
Datum LWGEOM_dfullywithin(PG_FUNCTION_ARGS)
Returns boolean true if the second argument is fully contained in a buffer of the first argument.
Datum ST_GeneratePoints(PG_FUNCTION_ARGS)
Datum hausdorffdistancedensify(PG_FUNCTION_ARGS)
Datum ST_OffsetCurve(PG_FUNCTION_ARGS)
Datum cluster_within_distance_garray(PG_FUNCTION_ARGS)
Datum centroid(PG_FUNCTION_ARGS)
Datum ST_MinimumClearance(PG_FUNCTION_ARGS)
Datum GEOSnoop(PG_FUNCTION_ARGS)
Datum ST_FrechetDistance(PG_FUNCTION_ARGS)
Datum buffer(PG_FUNCTION_ARGS)
Datum convexhull(PG_FUNCTION_ARGS)
Datum ST_ClipByBox2d(PG_FUNCTION_ARGS)
static uint32_t array_nelems_not_null(ArrayType *array)
Datum ST_SymDifference(PG_FUNCTION_ARGS)
Datum issimple(PG_FUNCTION_ARGS)
Datum symdifference(PG_FUNCTION_ARGS)
LWGEOM ** ARRAY2LWGEOM(ArrayType *array, uint32_t nelems, int *is3d, int *srid)
Datum topologypreservesimplify(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(postgis_geos_version)
Datum ST_MaximumInscribedCircle(PG_FUNCTION_ARGS)
GSERIALIZED * GEOS2POSTGIS(GEOSGeom geom, char want3d)
Datum ST_DelaunayTriangles(PG_FUNCTION_ARGS)
Datum ST_ConcaveHull(PG_FUNCTION_ARGS)
Datum boundary(PG_FUNCTION_ARGS)
Datum postgis_geos_version(PG_FUNCTION_ARGS)
GEOSGeometry * POSTGIS2GEOS(const GSERIALIZED *pglwgeom)
Datum ST_UnaryUnion(PG_FUNCTION_ARGS)
Datum ST_Equals(PG_FUNCTION_ARGS)
Datum ST_OrientedEnvelope(PG_FUNCTION_ARGS)
Datum ST_SharedPaths(PG_FUNCTION_ARGS)
Datum ST_MinimumClearanceLine(PG_FUNCTION_ARGS)
Datum ST_Voronoi(PG_FUNCTION_ARGS)
Datum clusterintersecting_garray(PG_FUNCTION_ARGS)
GEOSGeometry ** ARRAY2GEOS(ArrayType *array, uint32_t nelems, int *is3d, int *srid)
Datum ST_Difference(PG_FUNCTION_ARGS)
Datum pgis_union_geometry_array(PG_FUNCTION_ARGS)
Datum ST_TriangulatePolygon(PG_FUNCTION_ARGS)
Datum ST_LargestEmptyCircle(PG_FUNCTION_ARGS)
Datum hausdorffdistance(PG_FUNCTION_ARGS)
Datum isvalidreason(PG_FUNCTION_ARGS)
Datum isring(PG_FUNCTION_ARGS)
Datum linemerge(PG_FUNCTION_ARGS)
Datum pointonsurface(PG_FUNCTION_ARGS)
#define HANDLE_GEOS_ERROR(label)
unsigned int int32
Definition: shpopen.c:54
#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:353
GBOX * bbox
Definition: liblwgeom.h:458
lwflags_t flags
Definition: liblwgeom.h:461