Coverage Report

Created: 2026-08-13 07:11

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