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