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