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