Coverage Report

Created: 2026-08-13 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/lwgeom.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 (C) 2001-2006 Refractions Research Inc.
22
 * Copyright (C) 2017-2018 Daniel Baston <dbaston@gmail.com>
23
 *
24
 **********************************************************************/
25
26
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <stdarg.h>
30
#include <string.h>
31
32
#include "liblwgeom_internal.h"
33
#include "lwgeom_log.h"
34
35
#define out_stack_size 32
36
37
static char
38
lwnurbscurve_weight_same(const LWNURBSCURVE *curve1, const LWNURBSCURVE *curve2)
39
0
{
40
0
  uint32_t npoints = curve1->points->npoints;
41
42
0
  if ((curve1->nweights && curve1->nweights != npoints) ||
43
0
      (curve2->nweights && curve2->nweights != npoints))
44
0
    return LW_FALSE;
45
46
0
  for (uint32_t i = 0; i < npoints; i++)
47
0
  {
48
0
    double weight1 = curve1->weights ? curve1->weights[i] : 1.0;
49
0
    double weight2 = curve2->weights ? curve2->weights[i] : 1.0;
50
0
    if (!FP_EQUALS(weight1, weight2))
51
0
      return LW_FALSE;
52
0
  }
53
54
0
  return LW_TRUE;
55
0
}
56
57
static double
58
lwnurbscurve_uniform_knot_value(uint32_t degree, uint32_t npoints, uint32_t idx)
59
0
{
60
0
  uint32_t nknots = npoints + degree + 1;
61
0
  uint32_t internal_knots = nknots - 2 * (degree + 1);
62
63
0
  if (idx <= degree)
64
0
    return 0.0;
65
0
  if (idx >= nknots - degree - 1)
66
0
    return 1.0;
67
68
0
  return (double)(idx - degree) / (internal_knots + 1);
69
0
}
70
71
static char
72
lwnurbscurve_knot_same(const LWNURBSCURVE *curve1, const LWNURBSCURVE *curve2)
73
0
{
74
0
  uint32_t npoints = curve1->points->npoints;
75
0
  uint32_t nknots;
76
77
0
  if (npoints < curve1->degree + 1)
78
0
    return LW_FALSE;
79
80
0
  nknots = npoints + curve1->degree + 1;
81
0
  if ((curve1->nknots && curve1->nknots != nknots) ||
82
0
      (curve2->nknots && curve2->nknots != nknots))
83
0
    return LW_FALSE;
84
85
0
  for (uint32_t i = 0; i < nknots; i++)
86
0
  {
87
0
    double knot1 = curve1->knots ? curve1->knots[i] :
88
0
      lwnurbscurve_uniform_knot_value(curve1->degree, npoints, i);
89
0
    double knot2 = curve2->knots ? curve2->knots[i] :
90
0
      lwnurbscurve_uniform_knot_value(curve2->degree, npoints, i);
91
0
    if (!FP_EQUALS(knot1, knot2))
92
0
      return LW_FALSE;
93
0
  }
94
95
0
  return LW_TRUE;
96
0
}
97
98
static char
99
lwnurbscurve_same(const LWNURBSCURVE *curve1, const LWNURBSCURVE *curve2)
100
0
{
101
0
  if (lwnurbscurve_is_empty(curve1) && lwnurbscurve_is_empty(curve2))
102
0
    return LW_TRUE;
103
104
0
  if (curve1->degree != curve2->degree)
105
0
    return LW_FALSE;
106
0
  if (!curve1->points || !curve2->points || !ptarray_same(curve1->points, curve2->points))
107
0
    return LW_FALSE;
108
0
  if (!lwnurbscurve_weight_same(curve1, curve2))
109
0
    return LW_FALSE;
110
0
  if (!lwnurbscurve_knot_same(curve1, curve2))
111
0
    return LW_FALSE;
112
113
0
  return LW_TRUE;
114
0
}
115
116
117
/**
118
 * Force an orientation onto geometries made up of rings,
119
 * so the rings circle in a particular direction, clockwise
120
 * or counter-clockwise. The orientation is one of
121
 * LW_CLOCKWISE or LW_COUNTERCLOCKWISE. Non-polygonal
122
 * geometries are ignored and returned untouched.
123
 */
124
static void
125
lwgeom_force_orientation(LWGEOM *lwgeom, int orientation)
126
0
{
127
0
  switch (lwgeom->type)
128
0
  {
129
0
    case POLYGONTYPE:
130
0
      lwpoly_force_orientation(lwgeom_as_lwpoly(lwgeom), orientation);
131
0
      return;
132
133
0
    case TRIANGLETYPE:
134
0
      lwtriangle_force_orientation(lwgeom_as_lwtriangle(lwgeom), orientation);
135
0
      return;
136
137
    /* Not handle POLYHEDRALSURFACE and TIN
138
       as they are supposed to be well oriented */
139
0
    case MULTIPOLYGONTYPE:
140
0
    case COLLECTIONTYPE:
141
0
    {
142
0
      LWCOLLECTION* coll = lwgeom_as_lwcollection(lwgeom);
143
0
      for (uint32_t i = 0; coll && i < coll->ngeoms; i++)
144
0
      {
145
0
        lwgeom_force_orientation(coll->geoms[i], orientation);
146
0
      }
147
0
      return;
148
0
    }
149
0
  }
150
0
}
151
152
void
153
lwgeom_force_clockwise(LWGEOM *lwgeom)
154
0
{
155
0
  return lwgeom_force_orientation(lwgeom, LW_CLOCKWISE);
156
0
}
157
158
void
159
lwgeom_force_counterclockwise(LWGEOM *lwgeom)
160
0
{
161
0
  return lwgeom_force_orientation(lwgeom, LW_COUNTERCLOCKWISE);
162
0
}
163
164
165
/**
166
 * Check clockwise orientation on LWGEOM polygons
167
 * returns LW_CLOCKWISE, LW_NONE, LW_COUNTERCLOCKWISE
168
 * Non-polygonal geometries return LW_NONE
169
 * Geometries must have *all* rings correctly oriented
170
 * to return a non-none orientation
171
 */
172
int
173
lwgeom_has_orientation(const LWGEOM *lwgeom, int orientation)
174
0
{
175
0
  switch (lwgeom->type)
176
0
  {
177
0
    case POLYGONTYPE:
178
0
      return lwpoly_has_orientation(lwgeom_as_lwpoly(lwgeom), orientation);
179
180
0
    case TRIANGLETYPE:
181
0
      return lwtriangle_has_orientation(lwgeom_as_lwtriangle(lwgeom), orientation);
182
183
0
    case MULTIPOLYGONTYPE:
184
0
    case COLLECTIONTYPE:
185
0
    {
186
0
      LWCOLLECTION* coll = lwgeom_as_lwcollection(lwgeom);
187
0
      for (uint32_t i = 0; coll && i < coll->ngeoms; i++)
188
0
      {
189
0
        if(!lwgeom_has_orientation(coll->geoms[i], orientation))
190
0
          return LW_FALSE;
191
0
      }
192
0
      return LW_TRUE;
193
0
    }
194
195
0
    default:
196
0
      return LW_TRUE;
197
0
  }
198
0
  return LW_FALSE;
199
0
}
200
201
LWGEOM *
202
lwgeom_reverse(const LWGEOM *geom)
203
0
{
204
0
  LWGEOM *geomout = lwgeom_clone_deep(geom);
205
0
  lwgeom_reverse_in_place(geomout);
206
0
  return geomout;
207
0
}
208
209
/** Reverse vertex order of LWGEOM **/
210
void
211
lwgeom_reverse_in_place(LWGEOM *geom)
212
0
{
213
0
  uint32_t i;
214
0
  LWCOLLECTION *col;
215
0
  if (!geom)
216
0
    return;
217
218
0
  switch (geom->type)
219
0
  {
220
0
    case MULTIPOINTTYPE:
221
0
    case POINTTYPE:
222
0
    {
223
0
      return;
224
0
    }
225
0
    case TRIANGLETYPE:
226
0
    case CIRCSTRINGTYPE:
227
0
    case LINETYPE:
228
0
    {
229
0
      LWLINE *line = (LWLINE *)(geom);
230
0
      ptarray_reverse_in_place(line->points);
231
0
      return;
232
0
    }
233
0
    case POLYGONTYPE:
234
0
    {
235
0
      LWPOLY *poly = (LWPOLY *)(geom);
236
0
      if (!poly->rings)
237
0
        return;
238
0
      uint32_t r;
239
0
      for (r = 0; r < poly->nrings; r++)
240
0
        ptarray_reverse_in_place(poly->rings[r]);
241
0
      return;
242
0
    }
243
    /* CompoundCurve needs to also reverse the sub-geometries */
244
    /* so that the end-points remain coincident */
245
0
    case COMPOUNDTYPE:
246
0
    {
247
0
      uint32_t ngeoms;
248
0
      col = (LWCOLLECTION *)(geom);
249
0
      if (!col->geoms)
250
0
        return;
251
0
      ngeoms = col->ngeoms;
252
0
      for (i=0; i<ngeoms; i++)
253
0
        lwgeom_reverse_in_place(col->geoms[i]);
254
0
      for (i=0; i<col->ngeoms/2; i++) {
255
0
        LWGEOM* tmp = col->geoms[i];
256
0
        col->geoms[i] = col->geoms[ngeoms-i-1];
257
0
        col->geoms[ngeoms-i-1] = tmp;
258
0
      }
259
0
      return;
260
0
    }
261
0
    case MULTICURVETYPE:
262
0
    case MULTILINETYPE:
263
0
    case MULTIPOLYGONTYPE:
264
0
    case MULTISURFACETYPE:
265
0
    case POLYHEDRALSURFACETYPE:
266
0
    case TINTYPE:
267
0
    case COLLECTIONTYPE:
268
0
    case CURVEPOLYTYPE:
269
0
    {
270
0
      col = (LWCOLLECTION *)(geom);
271
0
      if (!col->geoms)
272
0
        return;
273
0
      for (i=0; i<col->ngeoms; i++)
274
0
        lwgeom_reverse_in_place(col->geoms[i]);
275
0
      return;
276
0
    }
277
0
    default:
278
0
    {
279
0
      lwerror("%s: Unknown geometry type: %s", __func__, lwtype_name(geom->type));
280
0
      return;
281
0
    }
282
283
0
  }
284
0
}
285
286
LWLINE *
287
lwgeom_as_lwline(const LWGEOM *lwgeom)
288
0
{
289
0
  if ( lwgeom == NULL ) return NULL;
290
0
  if ( lwgeom->type == LINETYPE )
291
0
    return (LWLINE *)lwgeom;
292
0
  else return NULL;
293
0
}
294
295
LWCIRCSTRING *
296
lwgeom_as_lwcircstring(const LWGEOM *lwgeom)
297
0
{
298
0
  if ( lwgeom == NULL ) return NULL;
299
0
  if ( lwgeom->type == CIRCSTRINGTYPE )
300
0
    return (LWCIRCSTRING *)lwgeom;
301
0
  else return NULL;
302
0
}
303
304
LWCOMPOUND *
305
lwgeom_as_lwcompound(const LWGEOM *lwgeom)
306
0
{
307
0
  if ( lwgeom == NULL ) return NULL;
308
0
  if ( lwgeom->type == COMPOUNDTYPE )
309
0
    return (LWCOMPOUND *)lwgeom;
310
0
  else return NULL;
311
0
}
312
313
LWCURVEPOLY *
314
lwgeom_as_lwcurvepoly(const LWGEOM *lwgeom)
315
0
{
316
0
  if ( lwgeom == NULL ) return NULL;
317
0
  if ( lwgeom->type == CURVEPOLYTYPE )
318
0
    return (LWCURVEPOLY *)lwgeom;
319
0
  else return NULL;
320
0
}
321
322
LWPOLY *
323
lwgeom_as_lwpoly(const LWGEOM *lwgeom)
324
0
{
325
0
  if ( lwgeom == NULL ) return NULL;
326
0
  if ( lwgeom->type == POLYGONTYPE )
327
0
    return (LWPOLY *)lwgeom;
328
0
  else return NULL;
329
0
}
330
331
LWTRIANGLE *
332
lwgeom_as_lwtriangle(const LWGEOM *lwgeom)
333
0
{
334
0
  if ( lwgeom == NULL ) return NULL;
335
0
  if ( lwgeom->type == TRIANGLETYPE )
336
0
    return (LWTRIANGLE *)lwgeom;
337
0
  else return NULL;
338
0
}
339
340
LWCOLLECTION *
341
lwgeom_as_lwcollection(const LWGEOM *lwgeom)
342
0
{
343
0
  if ( lwgeom == NULL ) return NULL;
344
0
  if ( lwgeom_is_collection(lwgeom) )
345
0
    return (LWCOLLECTION*)lwgeom;
346
0
  else return NULL;
347
0
}
348
349
LWMPOINT *
350
lwgeom_as_lwmpoint(const LWGEOM *lwgeom)
351
0
{
352
0
  if ( lwgeom == NULL ) return NULL;
353
0
  if ( lwgeom->type == MULTIPOINTTYPE )
354
0
    return (LWMPOINT *)lwgeom;
355
0
  else return NULL;
356
0
}
357
358
LWMLINE *
359
lwgeom_as_lwmline(const LWGEOM *lwgeom)
360
0
{
361
0
  if ( lwgeom == NULL ) return NULL;
362
0
  if ( lwgeom->type == MULTILINETYPE )
363
0
    return (LWMLINE *)lwgeom;
364
0
  else return NULL;
365
0
}
366
367
LWMPOLY *
368
lwgeom_as_lwmpoly(const LWGEOM *lwgeom)
369
0
{
370
0
  if ( lwgeom == NULL ) return NULL;
371
0
  if ( lwgeom->type == MULTIPOLYGONTYPE )
372
0
    return (LWMPOLY *)lwgeom;
373
0
  else return NULL;
374
0
}
375
376
LWPSURFACE *
377
lwgeom_as_lwpsurface(const LWGEOM *lwgeom)
378
0
{
379
0
  if ( lwgeom->type == POLYHEDRALSURFACETYPE )
380
0
    return (LWPSURFACE *)lwgeom;
381
0
  else return NULL;
382
0
}
383
384
LWTIN *
385
lwgeom_as_lwtin(const LWGEOM *lwgeom)
386
0
{
387
0
  if ( lwgeom->type == TINTYPE )
388
0
    return (LWTIN *)lwgeom;
389
0
  else return NULL;
390
0
}
391
392
LWGEOM *lwtin_as_lwgeom(const LWTIN *obj)
393
0
{
394
0
  return (LWGEOM *)obj;
395
0
}
396
397
LWGEOM *lwpsurface_as_lwgeom(const LWPSURFACE *obj)
398
0
{
399
0
  return (LWGEOM *)obj;
400
0
}
401
402
LWGEOM *lwmpoly_as_lwgeom(const LWMPOLY *obj)
403
0
{
404
0
  if ( obj == NULL ) return NULL;
405
0
  return (LWGEOM *)obj;
406
0
}
407
LWGEOM *lwmline_as_lwgeom(const LWMLINE *obj)
408
0
{
409
0
  if ( obj == NULL ) return NULL;
410
0
  return (LWGEOM *)obj;
411
0
}
412
LWGEOM *lwmpoint_as_lwgeom(const LWMPOINT *obj)
413
0
{
414
0
  if ( obj == NULL ) return NULL;
415
0
  return (LWGEOM *)obj;
416
0
}
417
LWGEOM *lwcollection_as_lwgeom(const LWCOLLECTION *obj)
418
0
{
419
0
  if ( obj == NULL ) return NULL;
420
0
  return (LWGEOM *)obj;
421
0
}
422
LWGEOM *lwcircstring_as_lwgeom(const LWCIRCSTRING *obj)
423
0
{
424
0
  if ( obj == NULL ) return NULL;
425
0
  return (LWGEOM *)obj;
426
0
}
427
LWGEOM *lwcurvepoly_as_lwgeom(const LWCURVEPOLY *obj)
428
0
{
429
0
  if ( obj == NULL ) return NULL;
430
0
  return (LWGEOM *)obj;
431
0
}
432
LWGEOM *lwcompound_as_lwgeom(const LWCOMPOUND *obj)
433
0
{
434
0
  if ( obj == NULL ) return NULL;
435
0
  return (LWGEOM *)obj;
436
0
}
437
LWGEOM *lwpoly_as_lwgeom(const LWPOLY *obj)
438
0
{
439
0
  if ( obj == NULL ) return NULL;
440
0
  return (LWGEOM *)obj;
441
0
}
442
LWGEOM *lwtriangle_as_lwgeom(const LWTRIANGLE *obj)
443
0
{
444
0
  if ( obj == NULL ) return NULL;
445
0
  return (LWGEOM *)obj;
446
0
}
447
LWGEOM *lwline_as_lwgeom(const LWLINE *obj)
448
0
{
449
0
  if ( obj == NULL ) return NULL;
450
0
  return (LWGEOM *)obj;
451
0
}
452
LWGEOM *lwpoint_as_lwgeom(const LWPOINT *obj)
453
0
{
454
0
  if ( obj == NULL ) return NULL;
455
0
  return (LWGEOM *)obj;
456
0
}
457
458
459
/**
460
** Look-up for the correct MULTI* type promotion for singleton types.
461
*/
462
uint8_t MULTITYPE[NUMTYPES] =
463
{
464
  0,
465
  MULTIPOINTTYPE,        /*  1 */
466
  MULTILINETYPE,         /*  2 */
467
  MULTIPOLYGONTYPE,      /*  3 */
468
  0,0,0,0,
469
  MULTICURVETYPE,        /*  8 */
470
  MULTICURVETYPE,        /*  9 */
471
  MULTISURFACETYPE,      /* 10 */
472
  POLYHEDRALSURFACETYPE, /* 11 */
473
  0, 0,
474
  TINTYPE,               /* 14 */
475
  0,
476
  MULTICURVETYPE         /* 16 */
477
};
478
479
uint8_t lwtype_multitype(uint8_t type)
480
0
{
481
0
  if (type >= NUMTYPES) return 0;
482
0
  return MULTITYPE[type];
483
0
}
484
485
/**
486
* Create a new LWGEOM of the appropriate MULTI* type.
487
*/
488
LWGEOM *
489
lwgeom_as_multi(const LWGEOM *lwgeom)
490
0
{
491
0
  LWGEOM **ogeoms;
492
0
  LWGEOM *ogeom = NULL;
493
0
  GBOX *box = NULL;
494
0
  int type;
495
496
0
  type = lwgeom->type;
497
498
  /*
499
  * PolyhedralSurface and MultiPolygon have identical structures
500
  * (both are collections of LWPOLY). Convert PolyhedralSurface
501
  * to MultiPolygon by cloning and changing the type.
502
  */
503
0
  if (type == POLYHEDRALSURFACETYPE)
504
0
  {
505
0
    ogeom = (LWGEOM *)lwcollection_clone((LWCOLLECTION *)lwgeom);
506
0
    ogeom->type = MULTIPOLYGONTYPE;
507
0
    return ogeom;
508
0
  }
509
510
  /*
511
  * TIN to MultiPolygon: convert each triangle to a polygon.
512
  * LWTRIANGLE and LWLINE have compatible memory layouts,
513
  * so we can use lwpoly_from_lwlines to create polygons.
514
  */
515
0
  if (type == TINTYPE)
516
0
  {
517
0
    uint32_t i;
518
0
    LWCOLLECTION *col = (LWCOLLECTION *)lwgeom;
519
0
    LWMPOLY *mpoly = lwmpoly_construct_empty(lwgeom->srid,
520
0
      FLAGS_GET_Z(lwgeom->flags),
521
0
      FLAGS_GET_M(lwgeom->flags));
522
523
0
    for (i = 0; i < col->ngeoms; i++)
524
0
    {
525
0
      LWPOLY *poly = lwpoly_from_lwlines((LWLINE *)col->geoms[i], 0, NULL);
526
0
      lwmpoly_add_lwpoly(mpoly, poly);
527
0
    }
528
529
0
    if (lwgeom->bbox)
530
0
      mpoly->bbox = gbox_clone(lwgeom->bbox);
531
532
0
    return lwmpoly_as_lwgeom(mpoly);
533
0
  }
534
535
0
  if ( ! MULTITYPE[type] ) return lwgeom_clone(lwgeom);
536
537
0
  if( lwgeom_is_empty(lwgeom) )
538
0
  {
539
0
    ogeom = (LWGEOM *)lwcollection_construct_empty(
540
0
      MULTITYPE[type],
541
0
      lwgeom->srid,
542
0
      FLAGS_GET_Z(lwgeom->flags),
543
0
      FLAGS_GET_M(lwgeom->flags)
544
0
    );
545
0
  }
546
0
  else
547
0
  {
548
0
    ogeoms = lwalloc(sizeof(LWGEOM*));
549
0
    ogeoms[0] = lwgeom_clone(lwgeom);
550
551
    /* Sub-geometries are not allowed to have bboxes or SRIDs, move the bbox to the collection */
552
0
    box = ogeoms[0]->bbox;
553
0
    ogeoms[0]->bbox = NULL;
554
0
    ogeoms[0]->srid = SRID_UNKNOWN;
555
556
0
    ogeom = (LWGEOM *)lwcollection_construct(MULTITYPE[type], lwgeom->srid, box, 1, ogeoms);
557
0
  }
558
559
0
  return ogeom;
560
0
}
561
562
/**
563
* Create a new LWGEOM of the appropriate CURVE* type.
564
*/
565
LWGEOM *
566
lwgeom_as_curve(const LWGEOM *lwgeom)
567
0
{
568
0
  LWGEOM *ogeom;
569
0
  int type = lwgeom->type;
570
  /*
571
  int hasz = FLAGS_GET_Z(lwgeom->flags);
572
  int hasm = FLAGS_GET_M(lwgeom->flags);
573
  int32_t srid = lwgeom->srid;
574
  */
575
576
0
  switch(type)
577
0
  {
578
0
    case LINETYPE:
579
      /* turn to COMPOUNDCURVE */
580
0
      ogeom = (LWGEOM*)lwcompound_construct_from_lwline((LWLINE*)lwgeom);
581
0
      break;
582
0
    case POLYGONTYPE:
583
0
      ogeom = (LWGEOM*)lwcurvepoly_construct_from_lwpoly(lwgeom_as_lwpoly(lwgeom));
584
0
      break;
585
0
    case MULTILINETYPE:
586
      /* turn to MULTICURVE */
587
0
      ogeom = lwgeom_clone(lwgeom);
588
0
      ogeom->type = MULTICURVETYPE;
589
0
      break;
590
0
    case MULTIPOLYGONTYPE:
591
      /* turn to MULTISURFACE */
592
0
      ogeom = lwgeom_clone(lwgeom);
593
0
      ogeom->type = MULTISURFACETYPE;
594
0
      break;
595
0
    case COLLECTIONTYPE:
596
0
    default:
597
0
      ogeom = lwgeom_clone(lwgeom);
598
0
      break;
599
0
  }
600
601
  /* TODO: copy bbox from input geom ? */
602
603
0
  return ogeom;
604
0
}
605
606
607
/**
608
* Free the containing LWGEOM and the associated BOX. Leave the underlying
609
* geoms/points/point objects intact. Useful for functions that are stripping
610
* out subcomponents of complex objects, or building up new temporary objects
611
* on top of subcomponents.
612
*/
613
void
614
lwgeom_release(LWGEOM *lwgeom)
615
0
{
616
0
  if ( ! lwgeom )
617
0
    lwerror("lwgeom_release: someone called on 0x0");
618
619
0
  LWDEBUGF(3, "releasing type %s", lwtype_name(lwgeom->type));
620
621
  /* Drop bounding box (always a copy) */
622
0
  if ( lwgeom->bbox )
623
0
  {
624
0
    LWDEBUGF(3, "lwgeom_release: releasing bbox. %p", lwgeom->bbox);
625
0
    lwfree(lwgeom->bbox);
626
0
  }
627
0
  lwfree(lwgeom);
628
629
0
}
630
631
632
/* @brief Clone LWGEOM object. Serialized point lists are not copied.
633
 *
634
 * @see ptarray_clone
635
 */
636
LWGEOM *
637
lwgeom_clone(const LWGEOM *lwgeom)
638
0
{
639
0
  LWDEBUGF(2, "lwgeom_clone called with %p, %s",
640
0
           lwgeom, lwtype_name(lwgeom->type));
641
642
0
  switch (lwgeom->type)
643
0
  {
644
0
  case POINTTYPE:
645
0
    return (LWGEOM *)lwpoint_clone((LWPOINT *)lwgeom);
646
0
  case LINETYPE:
647
0
    return (LWGEOM *)lwline_clone((LWLINE *)lwgeom);
648
0
  case CIRCSTRINGTYPE:
649
0
    return (LWGEOM *)lwcircstring_clone((LWCIRCSTRING *)lwgeom);
650
0
  case NURBSCURVETYPE:
651
0
    return (LWGEOM *)lwnurbscurve_clone_deep((LWNURBSCURVE *)lwgeom);
652
0
  case POLYGONTYPE:
653
0
    return (LWGEOM *)lwpoly_clone((LWPOLY *)lwgeom);
654
0
  case TRIANGLETYPE:
655
0
    return (LWGEOM *)lwtriangle_clone((LWTRIANGLE *)lwgeom);
656
0
  case COMPOUNDTYPE:
657
0
  case CURVEPOLYTYPE:
658
0
  case MULTICURVETYPE:
659
0
  case MULTISURFACETYPE:
660
0
  case MULTIPOINTTYPE:
661
0
  case MULTILINETYPE:
662
0
  case MULTIPOLYGONTYPE:
663
0
  case POLYHEDRALSURFACETYPE:
664
0
  case TINTYPE:
665
0
  case COLLECTIONTYPE:
666
0
    return (LWGEOM *)lwcollection_clone((LWCOLLECTION *)lwgeom);
667
0
  default:
668
0
    lwerror("lwgeom_clone: Unknown geometry type: %s", lwtype_name(lwgeom->type));
669
0
    return NULL;
670
0
  }
671
0
}
672
673
/**
674
 * Create a deep copy of an LWGEOM.
675
 *
676
 * Performs a full (deep) clone of the provided geometry, including copying
677
 * internal vertex/point arrays so the returned geometry is independent of the
678
 * original. Dispatches to type-specific deep-clone helpers (e.g. line, polygon,
679
 * collection, NURBS curve).
680
 *
681
 * @param lwgeom Source geometry to clone. Must be non-NULL.
682
 * @returns Pointer to a newly allocated LWGEOM that is a deep clone of
683
 *          `lwgeom`, or NULL if `lwgeom->type` is unrecognized (an error is
684
 *          reported in that case).
685
 */
686
LWGEOM *
687
lwgeom_clone_deep(const LWGEOM *lwgeom)
688
0
{
689
0
  LWDEBUGF(2, "lwgeom_clone called with %p, %s",
690
0
           lwgeom, lwtype_name(lwgeom->type));
691
692
0
  switch (lwgeom->type)
693
0
  {
694
0
  case POINTTYPE:
695
0
  case LINETYPE:
696
0
  case CIRCSTRINGTYPE:
697
0
  case TRIANGLETYPE:
698
0
    return (LWGEOM *)lwline_clone_deep((LWLINE *)lwgeom);
699
0
  case POLYGONTYPE:
700
0
    return (LWGEOM *)lwpoly_clone_deep((LWPOLY *)lwgeom);
701
0
  case COMPOUNDTYPE:
702
0
  case CURVEPOLYTYPE:
703
0
  case MULTICURVETYPE:
704
0
  case MULTISURFACETYPE:
705
0
  case MULTIPOINTTYPE:
706
0
  case MULTILINETYPE:
707
0
  case MULTIPOLYGONTYPE:
708
0
  case POLYHEDRALSURFACETYPE:
709
0
  case TINTYPE:
710
0
  case COLLECTIONTYPE:
711
0
    return (LWGEOM *)lwcollection_clone_deep((LWCOLLECTION *)lwgeom);
712
0
  case NURBSCURVETYPE:
713
0
    return (LWGEOM *)lwnurbscurve_clone_deep((LWNURBSCURVE *)lwgeom);
714
0
  default:
715
0
    lwerror("lwgeom_clone_deep: Unknown geometry type: %s", lwtype_name(lwgeom->type));
716
0
    return NULL;
717
0
  }
718
0
}
719
720
721
/**
722
 * Return an allocated string
723
 */
724
char*
725
lwgeom_to_ewkt(const LWGEOM *lwgeom)
726
0
{
727
0
  char* wkt = NULL;
728
0
  size_t wkt_size = 0;
729
730
0
  wkt = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, 12, &wkt_size);
731
732
0
  if ( ! wkt )
733
0
  {
734
0
    lwerror("Error writing geom %p to WKT", (void *)lwgeom);
735
0
  }
736
737
0
  return wkt;
738
0
}
739
740
/**
741
 * @brief geom1 same as geom2
742
 *    iff
743
 *        + have same type
744
 *      + have same # objects
745
 *        + have same bvol
746
 *        + each object in geom1 has a corresponding object in geom2 (see above)
747
 *  @param lwgeom1
748
 *  @param lwgeom2
749
 */
750
char
751
lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
752
0
{
753
0
  LWDEBUGF(2, "lwgeom_same(%s, %s) called",
754
0
           lwtype_name(lwgeom1->type),
755
0
           lwtype_name(lwgeom2->type));
756
757
0
  if ( lwgeom1->type != lwgeom2->type )
758
0
  {
759
0
    LWDEBUG(3, " type differ");
760
761
0
    return LW_FALSE;
762
0
  }
763
764
0
  if ( FLAGS_GET_ZM(lwgeom1->flags) != FLAGS_GET_ZM(lwgeom2->flags) )
765
0
  {
766
0
    LWDEBUG(3, " ZM flags differ");
767
768
0
    return LW_FALSE;
769
0
  }
770
771
  /* Check boxes if both already computed  */
772
0
  if ( lwgeom1->bbox && lwgeom2->bbox )
773
0
  {
774
    /*lwnotice("bbox1:%p, bbox2:%p", lwgeom1->bbox, lwgeom2->bbox);*/
775
0
    if ( ! gbox_same(lwgeom1->bbox, lwgeom2->bbox) )
776
0
    {
777
0
      LWDEBUG(3, " bounding boxes differ");
778
779
0
      return LW_FALSE;
780
0
    }
781
0
  }
782
783
  /* geoms have same type, invoke type-specific function */
784
0
  switch (lwgeom1->type)
785
0
  {
786
0
  case POINTTYPE:
787
0
    return lwpoint_same((LWPOINT *)lwgeom1,
788
0
                        (LWPOINT *)lwgeom2);
789
0
  case LINETYPE:
790
0
    return lwline_same((LWLINE *)lwgeom1,
791
0
                       (LWLINE *)lwgeom2);
792
0
  case POLYGONTYPE:
793
0
    return lwpoly_same((LWPOLY *)lwgeom1,
794
0
                       (LWPOLY *)lwgeom2);
795
0
  case TRIANGLETYPE:
796
0
    return lwtriangle_same((LWTRIANGLE *)lwgeom1,
797
0
                           (LWTRIANGLE *)lwgeom2);
798
0
  case CIRCSTRINGTYPE:
799
0
    return lwcircstring_same((LWCIRCSTRING *)lwgeom1,
800
0
           (LWCIRCSTRING *)lwgeom2);
801
0
  case NURBSCURVETYPE:
802
0
    return lwnurbscurve_same((LWNURBSCURVE *)lwgeom1,
803
0
                             (LWNURBSCURVE *)lwgeom2);
804
0
  case MULTIPOINTTYPE:
805
0
  case MULTILINETYPE:
806
0
  case MULTIPOLYGONTYPE:
807
0
  case MULTICURVETYPE:
808
0
  case MULTISURFACETYPE:
809
0
  case COMPOUNDTYPE:
810
0
  case CURVEPOLYTYPE:
811
0
  case POLYHEDRALSURFACETYPE:
812
0
  case TINTYPE:
813
0
  case COLLECTIONTYPE:
814
0
    return lwcollection_same((LWCOLLECTION *)lwgeom1,
815
0
                             (LWCOLLECTION *)lwgeom2);
816
0
  default:
817
0
    lwerror("lwgeom_same: unsupported geometry type: %s",
818
0
            lwtype_name(lwgeom1->type));
819
0
    return LW_FALSE;
820
0
  }
821
822
0
}
823
824
int
825
lwpoint_inside_circle(const LWPOINT *p, double cx, double cy, double rad)
826
0
{
827
0
  const POINT2D *pt;
828
0
  POINT2D center;
829
830
0
  if ( ! p || ! p->point )
831
0
    return LW_FALSE;
832
833
0
  pt = getPoint2d_cp(p->point, 0);
834
835
0
  center.x = cx;
836
0
  center.y = cy;
837
838
0
  if ( distance2d_pt_pt(pt, &center) < rad )
839
0
    return LW_TRUE;
840
841
0
  return LW_FALSE;
842
0
}
843
844
void
845
lwgeom_drop_bbox(LWGEOM *lwgeom)
846
0
{
847
0
  if ( lwgeom->bbox ) lwfree(lwgeom->bbox);
848
0
  lwgeom->bbox = NULL;
849
0
  FLAGS_SET_BBOX(lwgeom->flags, 0);
850
0
}
851
852
/**
853
 * Ensure there's a box in the LWGEOM.
854
 * If the box is already there just return,
855
 * else compute it.
856
 */
857
void
858
lwgeom_add_bbox(LWGEOM *lwgeom)
859
0
{
860
  /* an empty LWGEOM has no bbox */
861
0
  if ( lwgeom_is_empty(lwgeom) ) return;
862
863
0
  if ( lwgeom->bbox ) return;
864
0
  FLAGS_SET_BBOX(lwgeom->flags, 1);
865
0
  lwgeom->bbox = gbox_new(lwgeom->flags);
866
0
  lwgeom_calculate_gbox(lwgeom, lwgeom->bbox);
867
0
}
868
869
void
870
lwgeom_refresh_bbox(LWGEOM *lwgeom)
871
0
{
872
0
  lwgeom_drop_bbox(lwgeom);
873
0
  lwgeom_add_bbox(lwgeom);
874
0
}
875
876
void
877
lwgeom_add_bbox_deep(LWGEOM *lwgeom, GBOX *gbox)
878
0
{
879
0
  if ( lwgeom_is_empty(lwgeom) ) return;
880
881
0
  FLAGS_SET_BBOX(lwgeom->flags, 1);
882
883
0
  if ( ! ( gbox || lwgeom->bbox ) )
884
0
  {
885
0
    lwgeom->bbox = gbox_new(lwgeom->flags);
886
0
    lwgeom_calculate_gbox(lwgeom, lwgeom->bbox);
887
0
  }
888
0
  else if ( gbox && ! lwgeom->bbox )
889
0
  {
890
0
    lwgeom->bbox = gbox_clone(gbox);
891
0
  }
892
893
0
  if ( lwgeom_is_collection(lwgeom) )
894
0
  {
895
0
    uint32_t i;
896
0
    LWCOLLECTION *lwcol = (LWCOLLECTION*)lwgeom;
897
898
0
    for ( i = 0; i < lwcol->ngeoms; i++ )
899
0
    {
900
0
      lwgeom_add_bbox_deep(lwcol->geoms[i], lwgeom->bbox);
901
0
    }
902
0
  }
903
0
}
904
905
const GBOX *
906
lwgeom_get_bbox(const LWGEOM *lwg)
907
0
{
908
  /* add it if not already there */
909
0
  lwgeom_add_bbox((LWGEOM *)lwg);
910
0
  return lwg->bbox;
911
0
}
912
913
914
/**
915
* Calculate the gbox for this geometry, a cartesian box or
916
* geodetic box, depending on how it is flagged.
917
*/
918
int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
919
0
{
920
0
  gbox->flags = lwgeom->flags;
921
0
  if( FLAGS_GET_GEODETIC(lwgeom->flags) )
922
0
    return lwgeom_calculate_gbox_geodetic(lwgeom, gbox);
923
0
  else
924
0
    return lwgeom_calculate_gbox_cartesian(lwgeom, gbox);
925
0
}
926
927
void
928
lwgeom_drop_srid(LWGEOM *lwgeom)
929
0
{
930
0
  lwgeom->srid = SRID_UNKNOWN; /* TODO: To be changed to SRID_UNKNOWN */
931
0
}
932
933
LWGEOM *
934
lwgeom_segmentize2d(const LWGEOM *lwgeom, double dist)
935
0
{
936
0
  switch (lwgeom->type)
937
0
  {
938
0
  case LINETYPE:
939
0
    return (LWGEOM *)lwline_segmentize2d((LWLINE *)lwgeom,
940
0
                                         dist);
941
0
  case POLYGONTYPE:
942
0
    return (LWGEOM *)lwpoly_segmentize2d((LWPOLY *)lwgeom,
943
0
                                         dist);
944
0
  case MULTILINETYPE:
945
0
  case MULTIPOLYGONTYPE:
946
0
  case COLLECTIONTYPE:
947
0
    return (LWGEOM *)lwcollection_segmentize2d(
948
0
               (LWCOLLECTION *)lwgeom, dist);
949
950
0
  default:
951
0
    return lwgeom_clone(lwgeom);
952
0
  }
953
0
}
954
955
LWGEOM*
956
lwgeom_force_2d(const LWGEOM *geom)
957
0
{
958
0
  return lwgeom_force_dims(geom, 0, 0, 0, 0);
959
0
}
960
961
LWGEOM*
962
lwgeom_force_3dz(const LWGEOM *geom, double zval)
963
0
{
964
0
  return lwgeom_force_dims(geom, 1, 0, zval, 0);
965
0
}
966
967
LWGEOM*
968
lwgeom_force_3dm(const LWGEOM *geom, double mval)
969
0
{
970
0
  return lwgeom_force_dims(geom, 0, 1, 0, mval);
971
0
}
972
973
LWGEOM*
974
lwgeom_force_4d(const LWGEOM *geom, double zval, double mval)
975
0
{
976
0
  return lwgeom_force_dims(geom, 1, 1, zval, mval);
977
0
}
978
979
LWGEOM*
980
lwgeom_force_dims(const LWGEOM *geom, int hasz, int hasm, double zval, double mval)
981
0
{
982
0
  if (!geom)
983
0
    return NULL;
984
0
  switch(geom->type)
985
0
  {
986
0
    case POINTTYPE:
987
0
      return lwpoint_as_lwgeom(lwpoint_force_dims((LWPOINT*)geom, hasz, hasm, zval, mval));
988
0
    case CIRCSTRINGTYPE:
989
0
    case LINETYPE:
990
0
    case TRIANGLETYPE:
991
0
      return lwline_as_lwgeom(lwline_force_dims((LWLINE*)geom, hasz, hasm, zval, mval));
992
0
    case POLYGONTYPE:
993
0
      return lwpoly_as_lwgeom(lwpoly_force_dims((LWPOLY*)geom, hasz, hasm, zval, mval));
994
0
    case COMPOUNDTYPE:
995
0
    case CURVEPOLYTYPE:
996
0
    case MULTICURVETYPE:
997
0
    case MULTISURFACETYPE:
998
0
    case MULTIPOINTTYPE:
999
0
    case MULTILINETYPE:
1000
0
    case MULTIPOLYGONTYPE:
1001
0
    case POLYHEDRALSURFACETYPE:
1002
0
    case TINTYPE:
1003
0
    case COLLECTIONTYPE:
1004
0
      return lwcollection_as_lwgeom(lwcollection_force_dims((LWCOLLECTION*)geom, hasz, hasm, zval, mval));
1005
0
    case NURBSCURVETYPE:
1006
0
      return lwnurbscurve_as_lwgeom(lwnurbscurve_force_dims((LWNURBSCURVE*)geom, hasz, hasm, zval, mval));
1007
0
    default:
1008
0
      lwerror("lwgeom_force_2d: unsupported geom type: %s", lwtype_name(geom->type));
1009
0
      return NULL;
1010
0
  }
1011
0
}
1012
1013
LWGEOM*
1014
lwgeom_force_sfs(LWGEOM *geom, int version)
1015
0
{
1016
0
  LWCOLLECTION *col;
1017
0
  uint32_t i;
1018
0
  LWGEOM *g;
1019
1020
  /* SFS 1.2 version */
1021
0
  if (version == 120)
1022
0
  {
1023
0
    switch(geom->type)
1024
0
    {
1025
      /* SQL/MM types */
1026
0
      case CIRCSTRINGTYPE:
1027
0
      case COMPOUNDTYPE:
1028
0
      case CURVEPOLYTYPE:
1029
0
      case MULTICURVETYPE:
1030
0
      case MULTISURFACETYPE:
1031
0
        return lwgeom_stroke(geom, 32);
1032
1033
0
      case COLLECTIONTYPE:
1034
0
        col = (LWCOLLECTION*)geom;
1035
0
        for ( i = 0; i < col->ngeoms; i++ )
1036
0
          col->geoms[i] = lwgeom_force_sfs((LWGEOM*)col->geoms[i], version);
1037
1038
0
        return lwcollection_as_lwgeom((LWCOLLECTION*)geom);
1039
1040
0
      default:
1041
0
        return (LWGEOM *)geom;
1042
0
    }
1043
0
  }
1044
1045
1046
  /* SFS 1.1 version */
1047
0
  switch(geom->type)
1048
0
  {
1049
    /* SQL/MM types */
1050
0
    case CIRCSTRINGTYPE:
1051
0
    case COMPOUNDTYPE:
1052
0
    case CURVEPOLYTYPE:
1053
0
    case MULTICURVETYPE:
1054
0
    case MULTISURFACETYPE:
1055
0
      return lwgeom_stroke(geom, 32);
1056
1057
    /* SFS 1.2 types */
1058
0
    case TRIANGLETYPE:
1059
0
      g = lwpoly_as_lwgeom(lwpoly_from_lwlines((LWLINE*)geom, 0, NULL));
1060
0
      lwgeom_free(geom);
1061
0
      return g;
1062
1063
0
    case TINTYPE:
1064
0
      col = (LWCOLLECTION*) geom;
1065
0
      for ( i = 0; i < col->ngeoms; i++ )
1066
0
      {
1067
0
        g = lwpoly_as_lwgeom(lwpoly_from_lwlines((LWLINE*)col->geoms[i], 0, NULL));
1068
0
        lwgeom_free(col->geoms[i]);
1069
0
        col->geoms[i] = g;
1070
0
      }
1071
0
      col->type = COLLECTIONTYPE;
1072
0
      return lwmpoly_as_lwgeom((LWMPOLY*)geom);
1073
1074
0
    case POLYHEDRALSURFACETYPE:
1075
0
      geom->type = COLLECTIONTYPE;
1076
0
      return (LWGEOM *)geom;
1077
1078
    /* Collection */
1079
0
    case COLLECTIONTYPE:
1080
0
      col = (LWCOLLECTION*)geom;
1081
0
      for ( i = 0; i < col->ngeoms; i++ )
1082
0
        col->geoms[i] = lwgeom_force_sfs((LWGEOM*)col->geoms[i], version);
1083
1084
0
      return lwcollection_as_lwgeom((LWCOLLECTION*)geom);
1085
1086
0
    default:
1087
0
      return (LWGEOM *)geom;
1088
0
  }
1089
0
}
1090
1091
int32_t
1092
lwgeom_get_srid(const LWGEOM *geom)
1093
0
{
1094
0
  if ( ! geom ) return SRID_UNKNOWN;
1095
0
  return geom->srid;
1096
0
}
1097
1098
int
1099
lwgeom_has_z(const LWGEOM *geom)
1100
0
{
1101
0
  if ( ! geom ) return LW_FALSE;
1102
0
  return FLAGS_GET_Z(geom->flags);
1103
0
}
1104
1105
int
1106
lwgeom_has_m(const LWGEOM *geom)
1107
0
{
1108
0
  if ( ! geom ) return LW_FALSE;
1109
0
  return FLAGS_GET_M(geom->flags);
1110
0
}
1111
1112
int
1113
lwgeom_is_solid(const LWGEOM *geom)
1114
0
{
1115
0
  if ( ! geom ) return LW_FALSE;
1116
0
  return FLAGS_GET_SOLID(geom->flags);
1117
0
}
1118
1119
int
1120
lwgeom_ndims(const LWGEOM *geom)
1121
0
{
1122
0
  if ( ! geom ) return 0;
1123
0
  return FLAGS_NDIMS(geom->flags);
1124
0
}
1125
1126
1127
1128
void
1129
lwgeom_set_geodetic(LWGEOM *geom, int value)
1130
0
{
1131
0
  LWPOINT *pt;
1132
0
  LWLINE *ln;
1133
0
  LWPOLY *ply;
1134
0
  LWCOLLECTION *col;
1135
0
  uint32_t i;
1136
1137
0
  FLAGS_SET_GEODETIC(geom->flags, value);
1138
0
  if ( geom->bbox )
1139
0
    FLAGS_SET_GEODETIC(geom->bbox->flags, value);
1140
1141
0
  switch(geom->type)
1142
0
  {
1143
0
    case POINTTYPE:
1144
0
      pt = (LWPOINT*)geom;
1145
0
      if ( pt->point )
1146
0
        FLAGS_SET_GEODETIC(pt->point->flags, value);
1147
0
      break;
1148
0
    case LINETYPE:
1149
0
      ln = (LWLINE*)geom;
1150
0
      if ( ln->points )
1151
0
        FLAGS_SET_GEODETIC(ln->points->flags, value);
1152
0
      break;
1153
0
    case POLYGONTYPE:
1154
0
      ply = (LWPOLY*)geom;
1155
0
      for ( i = 0; i < ply->nrings; i++ )
1156
0
        FLAGS_SET_GEODETIC(ply->rings[i]->flags, value);
1157
0
      break;
1158
0
    case MULTIPOINTTYPE:
1159
0
    case MULTILINETYPE:
1160
0
    case MULTIPOLYGONTYPE:
1161
0
    case COLLECTIONTYPE:
1162
0
      col = (LWCOLLECTION*)geom;
1163
0
      for ( i = 0; i < col->ngeoms; i++ )
1164
0
        lwgeom_set_geodetic(col->geoms[i], value);
1165
0
      break;
1166
0
    default:
1167
0
      lwerror("lwgeom_set_geodetic: unsupported geom type: %s", lwtype_name(geom->type));
1168
0
      return;
1169
0
  }
1170
0
}
1171
1172
void
1173
lwgeom_longitude_shift(LWGEOM *lwgeom)
1174
0
{
1175
0
  uint32_t i;
1176
0
  switch (lwgeom->type)
1177
0
  {
1178
0
    LWPOINT *point;
1179
0
    LWLINE *line;
1180
0
    LWPOLY *poly;
1181
0
    LWTRIANGLE *triangle;
1182
0
    LWCOLLECTION *coll;
1183
1184
0
  case POINTTYPE:
1185
0
    point = (LWPOINT *)lwgeom;
1186
0
    ptarray_longitude_shift(point->point);
1187
0
    return;
1188
0
  case LINETYPE:
1189
0
    line = (LWLINE *)lwgeom;
1190
0
    ptarray_longitude_shift(line->points);
1191
0
    return;
1192
0
  case POLYGONTYPE:
1193
0
    poly = (LWPOLY *)lwgeom;
1194
0
    for (i=0; i<poly->nrings; i++)
1195
0
      ptarray_longitude_shift(poly->rings[i]);
1196
0
    return;
1197
0
  case TRIANGLETYPE:
1198
0
    triangle = (LWTRIANGLE *)lwgeom;
1199
0
    ptarray_longitude_shift(triangle->points);
1200
0
    return;
1201
0
  case MULTIPOINTTYPE:
1202
0
  case MULTILINETYPE:
1203
0
  case MULTIPOLYGONTYPE:
1204
0
  case POLYHEDRALSURFACETYPE:
1205
0
  case TINTYPE:
1206
0
  case COLLECTIONTYPE:
1207
0
    coll = (LWCOLLECTION *)lwgeom;
1208
0
    for (i=0; i<coll->ngeoms; i++)
1209
0
      lwgeom_longitude_shift(coll->geoms[i]);
1210
0
    return;
1211
0
  default:
1212
0
    lwerror("lwgeom_longitude_shift: unsupported geom type: %s",
1213
0
            lwtype_name(lwgeom->type));
1214
0
  }
1215
0
}
1216
1217
int
1218
lwgeom_is_closed(const LWGEOM *geom)
1219
0
{
1220
0
  int type = geom->type;
1221
1222
0
  if( lwgeom_is_empty(geom) )
1223
0
    return LW_FALSE;
1224
1225
  /* Test linear types for closure */
1226
0
  switch (type)
1227
0
  {
1228
0
  case LINETYPE:
1229
0
    return lwline_is_closed((LWLINE*)geom);
1230
0
  case POLYGONTYPE:
1231
0
    return lwpoly_is_closed((LWPOLY*)geom);
1232
0
  case CIRCSTRINGTYPE:
1233
0
    return lwcircstring_is_closed((LWCIRCSTRING*)geom);
1234
0
  case NURBSCURVETYPE:
1235
0
  {
1236
0
    POINT4D start, end;
1237
0
    LWPOINT *startpt = lwnurbscurve_evaluate((const LWNURBSCURVE*)geom, 0.0);
1238
0
    LWPOINT *endpt = lwnurbscurve_evaluate((const LWNURBSCURVE*)geom, 1.0);
1239
0
    int closed = LW_FALSE;
1240
1241
0
    if (startpt && endpt &&
1242
0
        lwpoint_getPoint4d_p(startpt, &start) &&
1243
0
        lwpoint_getPoint4d_p(endpt, &end))
1244
0
    {
1245
0
      if (FLAGS_GET_Z(geom->flags))
1246
0
        closed = FP_EQUALS(start.x, end.x) && FP_EQUALS(start.y, end.y) && FP_EQUALS(start.z, end.z);
1247
0
      else
1248
0
        closed = FP_EQUALS(start.x, end.x) && FP_EQUALS(start.y, end.y);
1249
0
    }
1250
1251
0
    lwpoint_free(startpt);
1252
0
    lwpoint_free(endpt);
1253
0
    return closed;
1254
0
  }
1255
0
  case COMPOUNDTYPE:
1256
0
    return lwcompound_is_closed((LWCOMPOUND*)geom);
1257
0
  case TINTYPE:
1258
0
    return lwtin_is_closed((LWTIN*)geom);
1259
0
  case POLYHEDRALSURFACETYPE:
1260
0
    return lwpsurface_is_closed((LWPSURFACE*)geom);
1261
0
  }
1262
1263
  /* Recurse into collections and see if anything is not closed */
1264
0
  if ( lwgeom_is_collection(geom) )
1265
0
  {
1266
0
    LWCOLLECTION *col = lwgeom_as_lwcollection(geom);
1267
0
    uint32_t i;
1268
0
    int closed;
1269
0
    for ( i = 0; i < col->ngeoms; i++ )
1270
0
    {
1271
0
      closed = lwgeom_is_closed(col->geoms[i]);
1272
0
      if ( ! closed )
1273
0
        return LW_FALSE;
1274
0
    }
1275
0
    return LW_TRUE;
1276
0
  }
1277
1278
  /* All non-linear non-collection types we will call closed */
1279
0
  return LW_TRUE;
1280
0
}
1281
1282
int
1283
lwgeom_is_collection(const LWGEOM *geom)
1284
0
{
1285
0
  if( ! geom ) return LW_FALSE;
1286
0
  return lwtype_is_collection(geom->type);
1287
0
}
1288
1289
int
1290
lwtype_is_unitary(uint32_t lwtype)
1291
0
{
1292
0
  switch (lwtype)
1293
0
  {
1294
0
  case POINTTYPE:
1295
0
  case LINETYPE:
1296
0
  case POLYGONTYPE:
1297
0
  case CURVEPOLYTYPE:
1298
0
  case COMPOUNDTYPE:
1299
0
  case CIRCSTRINGTYPE:
1300
0
  case NURBSCURVETYPE:
1301
0
  case TRIANGLETYPE:
1302
0
  case POLYHEDRALSURFACETYPE:
1303
0
  case TINTYPE:
1304
0
    return LW_TRUE;
1305
0
    break;
1306
1307
0
  default:
1308
0
    return LW_FALSE;
1309
0
  }
1310
0
}
1311
1312
int
1313
lwgeom_has_patches(const LWGEOM *geom)
1314
0
{
1315
0
  switch (geom->type)
1316
0
  {
1317
0
  case TINTYPE:
1318
0
  case POLYHEDRALSURFACETYPE:
1319
0
    return LW_TRUE;
1320
0
    break;
1321
0
  default:
1322
0
    return LW_FALSE;
1323
0
  }
1324
0
}
1325
1326
int
1327
lwgeom_is_unitary(const LWGEOM *geom)
1328
0
{
1329
0
  return lwtype_is_unitary(geom->type);
1330
0
}
1331
1332
int
1333
lwgeom_has_rings(const LWGEOM *geom)
1334
0
{
1335
0
  switch (geom->type)
1336
0
  {
1337
0
  case POLYGONTYPE:
1338
0
  case CURVEPOLYTYPE:
1339
0
  case TRIANGLETYPE:
1340
0
    return LW_TRUE;
1341
0
    break;
1342
1343
0
  default:
1344
0
    return LW_FALSE;
1345
0
  }
1346
0
}
1347
1348
/**
1349
 * Return TRUE if the geometry is structured as a wrapper on a geoms/ngeoms
1350
 * list of sub-geometries.
1351
 * Use lwgeom_is_unitary to determine if the numgeometries/geometryn accessor
1352
 * pattery makes sense
1353
 */
1354
int
1355
lwtype_is_collection(uint8_t type)
1356
0
{
1357
0
  switch (type)
1358
0
  {
1359
0
  case MULTIPOINTTYPE:
1360
0
  case MULTILINETYPE:
1361
0
  case MULTIPOLYGONTYPE:
1362
0
  case COLLECTIONTYPE:
1363
0
  case CURVEPOLYTYPE:
1364
0
  case COMPOUNDTYPE:
1365
0
  case MULTICURVETYPE:
1366
0
  case MULTISURFACETYPE:
1367
0
  case POLYHEDRALSURFACETYPE:
1368
0
  case TINTYPE:
1369
0
    return LW_TRUE;
1370
0
    break;
1371
1372
0
  default:
1373
0
    return LW_FALSE;
1374
0
  }
1375
0
}
1376
1377
/**
1378
* Given an lwtype number, what homogeneous collection can hold it?
1379
*/
1380
uint32_t
1381
lwtype_get_collectiontype(uint8_t type)
1382
0
{
1383
0
  switch (type)
1384
0
  {
1385
0
    case POINTTYPE:
1386
0
      return MULTIPOINTTYPE;
1387
0
    case LINETYPE:
1388
0
      return MULTILINETYPE;
1389
0
    case POLYGONTYPE:
1390
0
      return MULTIPOLYGONTYPE;
1391
0
    case CIRCSTRINGTYPE:
1392
0
      return MULTICURVETYPE;
1393
0
    case COMPOUNDTYPE:
1394
0
      return MULTICURVETYPE;
1395
0
    case NURBSCURVETYPE:
1396
0
      return MULTICURVETYPE;
1397
0
    case CURVEPOLYTYPE:
1398
0
      return MULTISURFACETYPE;
1399
0
    case TRIANGLETYPE:
1400
0
      return TINTYPE;
1401
0
    default:
1402
0
      return COLLECTIONTYPE;
1403
0
  }
1404
0
}
1405
1406
1407
/**
1408
 * Free an LWGEOM and its associated resources.
1409
 *
1410
 * Dispatches to the appropriate type-specific free function based on lwgeom->type.
1411
 * If `lwgeom` is NULL the function is a no-op. Logs an error if the geometry type
1412
 * is unknown.
1413
 *
1414
 * @param lwgeom Pointer to the LWGEOM to free (may be NULL). After return the
1415
 *               memory for the provided geometry will have been released.
1416
 */
1417
void lwgeom_free(LWGEOM *lwgeom)
1418
0
{
1419
1420
  /* There's nothing here to free... */
1421
0
  if( ! lwgeom ) return;
1422
1423
0
  LWDEBUGF(5,"freeing a %s",lwtype_name(lwgeom->type));
1424
1425
0
  switch (lwgeom->type)
1426
0
  {
1427
0
  case POINTTYPE:
1428
0
    lwpoint_free((LWPOINT *)lwgeom);
1429
0
    break;
1430
0
  case LINETYPE:
1431
0
    lwline_free((LWLINE *)lwgeom);
1432
0
    break;
1433
0
  case POLYGONTYPE:
1434
0
    lwpoly_free((LWPOLY *)lwgeom);
1435
0
    break;
1436
0
  case CIRCSTRINGTYPE:
1437
0
    lwcircstring_free((LWCIRCSTRING *)lwgeom);
1438
0
    break;
1439
0
  case TRIANGLETYPE:
1440
0
    lwtriangle_free((LWTRIANGLE *)lwgeom);
1441
0
    break;
1442
0
  case MULTIPOINTTYPE:
1443
0
    lwmpoint_free((LWMPOINT *)lwgeom);
1444
0
    break;
1445
0
  case MULTILINETYPE:
1446
0
    lwmline_free((LWMLINE *)lwgeom);
1447
0
    break;
1448
0
  case MULTIPOLYGONTYPE:
1449
0
    lwmpoly_free((LWMPOLY *)lwgeom);
1450
0
    break;
1451
0
  case POLYHEDRALSURFACETYPE:
1452
0
    lwpsurface_free((LWPSURFACE *)lwgeom);
1453
0
    break;
1454
0
  case TINTYPE:
1455
0
    lwtin_free((LWTIN *)lwgeom);
1456
0
    break;
1457
0
  case CURVEPOLYTYPE:
1458
0
  case COMPOUNDTYPE:
1459
0
  case MULTICURVETYPE:
1460
0
  case MULTISURFACETYPE:
1461
0
  case COLLECTIONTYPE:
1462
0
    lwcollection_free((LWCOLLECTION *)lwgeom);
1463
0
    break;
1464
0
  case NURBSCURVETYPE:
1465
0
    lwnurbscurve_free((LWNURBSCURVE *)lwgeom);
1466
0
    break;
1467
0
  default:
1468
0
    lwerror("lwgeom_free called with unknown type (%d) %s", lwgeom->type, lwtype_name(lwgeom->type));
1469
0
  }
1470
0
  return;
1471
0
}
1472
1473
/**
1474
 * Determine whether a geometry requires a bounding box.
1475
 *
1476
 * Returns LW_TRUE if callers should attach/maintain a bounding box for the
1477
 * provided geometry type and state, or LW_FALSE when a bbox is unnecessary
1478
 * (e.g., point-like or trivially short line geometries).
1479
 *
1480
 * Detailed behavior:
1481
 * - POINTTYPE: returns LW_FALSE.
1482
 * - LINETYPE: returns LW_FALSE for lines with 0–2 vertices, otherwise LW_TRUE.
1483
 * - NURBSCURVETYPE: currently treated as not requiring a bbox (LW_FALSE).
1484
 * - MULTIPOINTTYPE: returns LW_FALSE for a single-member collection, otherwise LW_TRUE.
1485
 * - MULTILINETYPE: returns LW_FALSE when the collection has one member and that member has ≤2 vertices, otherwise LW_TRUE.
1486
 * - All other types: returns LW_TRUE.
1487
 *
1488
 * @param geom Geometry to inspect (must not be NULL).
1489
 * @return LW_TRUE if a bbox is needed, LW_FALSE otherwise.
1490
 */
1491
int lwgeom_needs_bbox(const LWGEOM *geom)
1492
0
{
1493
0
  assert(geom);
1494
0
  if ( geom->type == POINTTYPE )
1495
0
  {
1496
0
    return LW_FALSE;
1497
0
  }
1498
0
  else if ( geom->type == LINETYPE )
1499
0
  {
1500
0
    if ( lwgeom_count_vertices(geom) <= 2 )
1501
0
      return LW_FALSE;
1502
0
    else
1503
0
      return LW_TRUE;
1504
0
  }
1505
0
  else if ( geom->type == NURBSCURVETYPE )
1506
0
  {
1507
0
    const LWNURBSCURVE *nurbs = (LWNURBSCURVE*)geom;
1508
    /* Empty curves don't need bbox */
1509
0
    if ( !nurbs->points || nurbs->points->npoints == 0 )
1510
0
      return LW_FALSE;
1511
    /* Single control point doesn't need bbox */
1512
0
    else if ( nurbs->points->npoints == 1 )
1513
0
      return LW_FALSE;
1514
0
    else
1515
0
      return LW_TRUE;
1516
0
  }
1517
0
  else if ( geom->type == MULTIPOINTTYPE )
1518
0
  {
1519
0
    if ( ((LWCOLLECTION*)geom)->ngeoms == 1 )
1520
0
      return LW_FALSE;
1521
0
    else
1522
0
      return LW_TRUE;
1523
0
  }
1524
0
  else if ( geom->type == MULTILINETYPE )
1525
0
  {
1526
0
    if ( ((LWCOLLECTION*)geom)->ngeoms == 1 && lwgeom_count_vertices(geom) <= 2 )
1527
0
      return LW_FALSE;
1528
0
    else
1529
0
      return LW_TRUE;
1530
0
  }
1531
0
  else
1532
0
  {
1533
0
    return LW_TRUE;
1534
0
  }
1535
0
}
1536
1537
/**
1538
* Count points in an #LWGEOM.
1539
* TODO: Make sure the internal functions don't overflow
1540
*/
1541
uint32_t lwgeom_count_vertices(const LWGEOM *geom)
1542
0
{
1543
0
  int result = 0;
1544
1545
  /* Null? Zero. */
1546
0
  if( ! geom ) return 0;
1547
1548
0
  LWDEBUGF(4, "lwgeom_count_vertices got type %s",
1549
0
           lwtype_name(geom->type));
1550
1551
  /* Empty? Zero. */
1552
0
  if( lwgeom_is_empty(geom) ) return 0;
1553
1554
0
  switch (geom->type)
1555
0
  {
1556
0
  case POINTTYPE:
1557
0
    result = 1;
1558
0
    break;
1559
0
  case TRIANGLETYPE:
1560
0
  case CIRCSTRINGTYPE:
1561
0
  case LINETYPE:
1562
0
    result = lwline_count_vertices((const LWLINE *)geom);
1563
0
    break;
1564
0
  case NURBSCURVETYPE:
1565
0
    {
1566
0
      const LWNURBSCURVE *nurbs = (const LWNURBSCURVE *)geom;
1567
0
      result = (nurbs->points != NULL) ? nurbs->points->npoints : 0;
1568
0
    }
1569
0
    break;
1570
0
  case POLYGONTYPE:
1571
0
    result = lwpoly_count_vertices((const LWPOLY *)geom);
1572
0
    break;
1573
0
  case COMPOUNDTYPE:
1574
0
  case CURVEPOLYTYPE:
1575
0
  case MULTICURVETYPE:
1576
0
  case MULTISURFACETYPE:
1577
0
  case MULTIPOINTTYPE:
1578
0
  case MULTILINETYPE:
1579
0
  case MULTIPOLYGONTYPE:
1580
0
  case POLYHEDRALSURFACETYPE:
1581
0
  case TINTYPE:
1582
0
  case COLLECTIONTYPE:
1583
0
    result = lwcollection_count_vertices((const LWCOLLECTION *)geom);
1584
0
    break;
1585
0
  default:
1586
0
    lwerror("%s: unsupported input geometry type: %s",
1587
0
            __func__, lwtype_name(geom->type));
1588
0
    break;
1589
0
  }
1590
0
  LWDEBUGF(3, "counted %d vertices", result);
1591
0
  return result;
1592
0
}
1593
1594
/**
1595
 * Return the topological dimension of a geometry.
1596
 *
1597
 * Determines the topological dimension for the given LWGEOM:
1598
 * - 0 for point-like geometries,
1599
 * - 1 for curve-like geometries (lines, circular strings, compound/curve collections, NURBS),
1600
 * - 2 for surface-like geometries (polygons, triangles, surfaces, TIN),
1601
 * - 3 for a closed polyhedral surface (volume).
1602
 * For a COLLECTION, returns the maximum dimension among its members.
1603
 *
1604
 * @param geom Pointer to the geometry; may be NULL.
1605
 * @return 0..3 for the geometry's topological dimension, the maximum member dimension for collections,
1606
 *         -1 if geom is NULL or its type is unsupported.
1607
 */
1608
int lwgeom_dimension(const LWGEOM *geom)
1609
0
{
1610
1611
  /* Null? Zero. */
1612
0
  if( ! geom ) return -1;
1613
1614
0
  LWDEBUGF(4, "lwgeom_dimension got type %s",
1615
0
           lwtype_name(geom->type));
1616
1617
  /* Empty? Zero. */
1618
  /* if( lwgeom_is_empty(geom) ) return 0; */
1619
1620
0
  switch (geom->type)
1621
0
  {
1622
0
  case POINTTYPE:
1623
0
  case MULTIPOINTTYPE:
1624
0
    return 0;
1625
0
  case CIRCSTRINGTYPE:
1626
0
  case LINETYPE:
1627
0
  case COMPOUNDTYPE:
1628
0
  case MULTICURVETYPE:
1629
0
  case MULTILINETYPE:
1630
0
  case NURBSCURVETYPE:
1631
0
    return 1;
1632
0
  case TRIANGLETYPE:
1633
0
  case POLYGONTYPE:
1634
0
  case CURVEPOLYTYPE:
1635
0
  case MULTISURFACETYPE:
1636
0
  case MULTIPOLYGONTYPE:
1637
0
    return 2;
1638
0
  case TINTYPE:
1639
    /* A 3D TIN surface is dimension 3 only when explicitly marked solid. */
1640
0
    return (FLAGS_GET_SOLID(geom->flags) && FLAGS_GET_Z(geom->flags)) ? 3 : 2;
1641
0
  case POLYHEDRALSURFACETYPE:
1642
0
  {
1643
    /* A closed polyhedral surface contains a volume. */
1644
0
    int closed = lwpsurface_is_closed((LWPSURFACE*)geom);
1645
0
    return ( closed ? 3 : 2 );
1646
0
  }
1647
0
  case COLLECTIONTYPE:
1648
0
  {
1649
0
    int maxdim = 0;
1650
0
    uint32_t i;
1651
0
    LWCOLLECTION *col = (LWCOLLECTION*)geom;
1652
0
    for( i = 0; i < col->ngeoms; i++ )
1653
0
    {
1654
0
      int dim = lwgeom_dimension(col->geoms[i]);
1655
0
      maxdim = ( dim > maxdim ? dim : maxdim );
1656
0
    }
1657
0
    return maxdim;
1658
0
  }
1659
0
  default:
1660
0
    lwerror("%s: unsupported input geometry type: %s",
1661
0
            __func__, lwtype_name(geom->type));
1662
0
  }
1663
0
  return -1;
1664
0
}
1665
1666
/**
1667
* Count rings in an #LWGEOM.
1668
*/
1669
uint32_t lwgeom_count_rings(const LWGEOM *geom)
1670
0
{
1671
0
  int result = 0;
1672
1673
  /* Null? Empty? Zero. */
1674
0
  if( ! geom || lwgeom_is_empty(geom) )
1675
0
    return 0;
1676
1677
0
  switch (geom->type)
1678
0
  {
1679
0
  case POINTTYPE:
1680
0
  case CIRCSTRINGTYPE:
1681
0
  case COMPOUNDTYPE:
1682
0
  case MULTICURVETYPE:
1683
0
  case MULTIPOINTTYPE:
1684
0
  case MULTILINETYPE:
1685
0
  case LINETYPE:
1686
0
    result = 0;
1687
0
    break;
1688
0
  case TRIANGLETYPE:
1689
0
    result = 1;
1690
0
    break;
1691
0
  case POLYGONTYPE:
1692
0
    result = ((LWPOLY *)geom)->nrings;
1693
0
    break;
1694
0
  case CURVEPOLYTYPE:
1695
0
    result = ((LWCURVEPOLY *)geom)->nrings;
1696
0
    break;
1697
0
  case MULTISURFACETYPE:
1698
0
  case MULTIPOLYGONTYPE:
1699
0
  case POLYHEDRALSURFACETYPE:
1700
0
  case TINTYPE:
1701
0
  case COLLECTIONTYPE:
1702
0
  {
1703
0
    LWCOLLECTION *col = (LWCOLLECTION*)geom;
1704
0
    uint32_t i = 0;
1705
0
    for( i = 0; i < col->ngeoms; i++ )
1706
0
      result += lwgeom_count_rings(col->geoms[i]);
1707
0
    break;
1708
0
  }
1709
0
  default:
1710
0
    lwerror("lwgeom_count_rings: unsupported input geometry type: %s", lwtype_name(geom->type));
1711
0
    break;
1712
0
  }
1713
0
  LWDEBUGF(3, "counted %d rings", result);
1714
0
  return result;
1715
0
}
1716
1717
int lwgeom_has_srid(const LWGEOM *geom)
1718
0
{
1719
0
  if ( geom->srid != SRID_UNKNOWN )
1720
0
    return LW_TRUE;
1721
1722
0
  return LW_FALSE;
1723
0
}
1724
1725
1726
static int lwcollection_dimensionality(const LWCOLLECTION *col)
1727
0
{
1728
0
  uint32_t i;
1729
0
  int dimensionality = 0;
1730
0
  for ( i = 0; i < col->ngeoms; i++ )
1731
0
  {
1732
0
    int d = lwgeom_dimensionality(col->geoms[i]);
1733
0
    if ( d > dimensionality )
1734
0
      dimensionality = d;
1735
0
  }
1736
0
  return dimensionality;
1737
0
}
1738
1739
/**
1740
 * Determine the topological dimensionality of a geometry.
1741
 *
1742
 * Returns the maximal topological dimension for the input geometry:
1743
 * - 0 for points and multipoints,
1744
 * - 1 for linear/curve types (lines, circular strings, compound/multi-curves, NURBS),
1745
 * - 2 for polygonal/surface types,
1746
 * Closure-aware dimensionality used by internal validity/repair checks.
1747
 *
1748
 * This is intentionally not the same contract as lwgeom_dimension(), which
1749
 * backs ST_Dimension.  Closed polyhedral surfaces and TINs are reported as 3
1750
 * here so callers that reason about solid-like inputs keep their legacy
1751
 * behavior.
1752
 *
1753
 * For COLLECTIONTYPE the result is the maximum dimensionality of its members.
1754
 *
1755
 * @param geom Geometry to inspect (must be non-NULL).
1756
 * @return The closure-aware dimensionality (0..3). On unsupported types the function logs an error and returns 0.
1757
 */
1758
extern int lwgeom_dimensionality(const LWGEOM *geom)
1759
0
{
1760
0
  int dim;
1761
1762
0
  LWDEBUGF(3, "lwgeom_dimensionality got type %s",
1763
0
           lwtype_name(geom->type));
1764
1765
0
  switch (geom->type)
1766
0
  {
1767
0
  case POINTTYPE:
1768
0
  case MULTIPOINTTYPE:
1769
0
    return 0;
1770
0
    break;
1771
0
  case LINETYPE:
1772
0
  case CIRCSTRINGTYPE:
1773
0
  case MULTILINETYPE:
1774
0
  case COMPOUNDTYPE:
1775
0
  case MULTICURVETYPE:
1776
0
  case NURBSCURVETYPE:
1777
0
    return 1;
1778
0
    break;
1779
0
  case POLYGONTYPE:
1780
0
  case TRIANGLETYPE:
1781
0
  case CURVEPOLYTYPE:
1782
0
  case MULTIPOLYGONTYPE:
1783
0
  case MULTISURFACETYPE:
1784
0
    return 2;
1785
0
    break;
1786
1787
0
  case POLYHEDRALSURFACETYPE:
1788
0
  case TINTYPE:
1789
0
    dim = lwgeom_is_closed(geom)?3:2;
1790
0
    return dim;
1791
0
    break;
1792
1793
0
  case COLLECTIONTYPE:
1794
0
    return lwcollection_dimensionality((const LWCOLLECTION *)geom);
1795
0
    break;
1796
0
  default:
1797
0
    lwerror("lwgeom_dimensionality: unsupported input geometry type: %s",
1798
0
            lwtype_name(geom->type));
1799
0
    break;
1800
0
  }
1801
0
  return 0;
1802
0
}
1803
1804
extern LWGEOM* lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance)
1805
0
{
1806
0
  LWGEOM *out = lwgeom_clone_deep(in);
1807
0
  lwgeom_remove_repeated_points_in_place(out, tolerance);
1808
0
  return out;
1809
0
}
1810
1811
void lwgeom_swap_ordinates(LWGEOM *in, LWORD o1, LWORD o2)
1812
0
{
1813
0
  LWCOLLECTION *col;
1814
0
  LWPOLY *poly;
1815
0
  uint32_t i;
1816
1817
0
  if ( (!in) || lwgeom_is_empty(in) ) return;
1818
1819
  /* TODO: check for lwgeom NOT having the specified dimension ? */
1820
1821
0
  LWDEBUGF(4, "lwgeom_flip_coordinates, got type: %s",
1822
0
           lwtype_name(in->type));
1823
1824
0
  switch (in->type)
1825
0
  {
1826
0
  case POINTTYPE:
1827
0
    ptarray_swap_ordinates(lwgeom_as_lwpoint(in)->point, o1, o2);
1828
0
    break;
1829
1830
0
  case LINETYPE:
1831
0
    ptarray_swap_ordinates(lwgeom_as_lwline(in)->points, o1, o2);
1832
0
    break;
1833
1834
0
  case CIRCSTRINGTYPE:
1835
0
    ptarray_swap_ordinates(lwgeom_as_lwcircstring(in)->points, o1, o2);
1836
0
    break;
1837
1838
0
  case NURBSCURVETYPE:
1839
0
  {
1840
0
    LWNURBSCURVE *n = (LWNURBSCURVE *)in;
1841
0
    if (n->points)
1842
0
      ptarray_swap_ordinates(n->points, o1, o2);
1843
0
    break;
1844
0
  }
1845
1846
0
  case POLYGONTYPE:
1847
0
    poly = (LWPOLY *) in;
1848
0
    for (i=0; i<poly->nrings; i++)
1849
0
    {
1850
0
      ptarray_swap_ordinates(poly->rings[i], o1, o2);
1851
0
    }
1852
0
    break;
1853
1854
0
  case TRIANGLETYPE:
1855
0
    ptarray_swap_ordinates(lwgeom_as_lwtriangle(in)->points, o1, o2);
1856
0
    break;
1857
1858
0
  case MULTIPOINTTYPE:
1859
0
  case MULTILINETYPE:
1860
0
  case MULTIPOLYGONTYPE:
1861
0
  case COLLECTIONTYPE:
1862
0
  case COMPOUNDTYPE:
1863
0
  case CURVEPOLYTYPE:
1864
0
  case MULTISURFACETYPE:
1865
0
  case MULTICURVETYPE:
1866
0
  case POLYHEDRALSURFACETYPE:
1867
0
  case TINTYPE:
1868
0
    col = (LWCOLLECTION *) in;
1869
0
    for (i=0; i<col->ngeoms; i++)
1870
0
    {
1871
0
      lwgeom_swap_ordinates(col->geoms[i], o1, o2);
1872
0
    }
1873
0
    break;
1874
1875
0
  default:
1876
0
    lwerror("lwgeom_swap_ordinates: unsupported geometry type: %s",
1877
0
            lwtype_name(in->type));
1878
0
    return;
1879
0
  }
1880
1881
  /* only refresh bbox if X or Y changed */
1882
0
  if ( in->bbox && (o1 < 2 || o2 < 2) )
1883
0
  {
1884
0
    lwgeom_refresh_bbox(in);
1885
0
  }
1886
0
}
1887
1888
void lwgeom_set_srid(LWGEOM *geom, int32_t srid)
1889
0
{
1890
0
  uint32_t i;
1891
1892
0
  LWDEBUGF(4,"entered with srid=%d",srid);
1893
1894
0
  geom->srid = srid;
1895
1896
0
  if ( lwgeom_is_collection(geom) )
1897
0
  {
1898
    /* All the children are set to the same SRID value */
1899
0
    LWCOLLECTION *col = lwgeom_as_lwcollection(geom);
1900
0
    for ( i = 0; i < col->ngeoms; i++ )
1901
0
    {
1902
0
      lwgeom_set_srid(col->geoms[i], srid);
1903
0
    }
1904
0
  }
1905
0
}
1906
1907
1908
/**************************************************************/
1909
1910
static int
1911
cmp_point_x(const void *pa, const void *pb)
1912
0
{
1913
0
  LWPOINT *p1 = *((LWPOINT **)pa);
1914
0
  LWPOINT *p2 = *((LWPOINT **)pb);
1915
1916
0
  const POINT2D *pt1 = getPoint2d_cp(p1->point, 0);
1917
0
  const POINT2D *pt2 = getPoint2d_cp(p2->point, 0);
1918
1919
0
  if (pt1 && pt2)
1920
0
    return (pt1->x > pt2->x) ? 1 : ((pt1->x < pt2->x) ? -1 : 0);
1921
1922
0
  if (pt1) return -1;
1923
0
  if (pt2) return 1;
1924
0
  return 0;
1925
0
}
1926
1927
static int
1928
cmp_point_y(const void *pa, const void *pb)
1929
0
{
1930
0
  LWPOINT *p1 = *((LWPOINT **)pa);
1931
0
  LWPOINT *p2 = *((LWPOINT **)pb);
1932
1933
0
  const POINT2D *pt1 = getPoint2d_cp(p1->point, 0);
1934
0
  const POINT2D *pt2 = getPoint2d_cp(p2->point, 0);
1935
1936
0
  if (pt1 && pt2)
1937
0
    return (pt1->y > pt2->y) ? 1 : ((pt1->y < pt2->y) ? -1 : 0);
1938
1939
0
  if (pt1) return -1;
1940
0
  if (pt2) return 1;
1941
0
  return 0;
1942
0
}
1943
1944
int
1945
lwgeom_remove_repeated_points_in_place(LWGEOM *geom, double tolerance)
1946
0
{
1947
0
  int geometry_modified = LW_FALSE;
1948
0
  switch (geom->type)
1949
0
  {
1950
  /* No-op! Cannot remove points */
1951
0
  case POINTTYPE:
1952
0
  case TRIANGLETYPE:
1953
0
    return geometry_modified;
1954
0
  case LINETYPE: {
1955
0
    LWLINE *g = (LWLINE *)(geom);
1956
0
    POINTARRAY *pa = g->points;
1957
0
    uint32_t npoints = pa->npoints;
1958
0
    ptarray_remove_repeated_points_in_place(pa, tolerance, 2);
1959
0
    geometry_modified = npoints != pa->npoints;
1960
    /* Invalid input, discard the collapsed line */
1961
0
    if (pa->npoints < 2)
1962
0
    {
1963
0
      pa->npoints = 0;
1964
0
      geometry_modified = LW_TRUE;
1965
0
    }
1966
0
    break;
1967
0
  }
1968
0
  case POLYGONTYPE: {
1969
0
    uint32_t j = 0;
1970
0
    LWPOLY *g = (LWPOLY *)(geom);
1971
0
    for (uint32_t i = 0; i < g->nrings; i++)
1972
0
    {
1973
0
      POINTARRAY *pa = g->rings[i];
1974
0
      uint32_t npoints = pa->npoints;
1975
0
      ptarray_remove_repeated_points_in_place(pa, tolerance, 4);
1976
0
      geometry_modified |= npoints != pa->npoints;
1977
      /* Drop collapsed rings */
1978
0
      if (pa->npoints < 4)
1979
0
      {
1980
0
        geometry_modified = LW_TRUE;
1981
0
        ptarray_free(pa);
1982
0
        continue;
1983
0
      }
1984
0
      g->rings[j++] = pa;
1985
0
    }
1986
    /* Update ring count */
1987
0
    g->nrings = j;
1988
0
    break;
1989
0
  }
1990
0
  case MULTIPOINTTYPE: {
1991
0
    double tolsq = tolerance * tolerance;
1992
0
    LWMPOINT *mpt = (LWMPOINT *)geom;
1993
1994
0
    for (uint8_t dim = 0; dim < 2; dim++)
1995
0
    {
1996
      /* sort by y, then by x - this way the result is sorted by x */
1997
0
      qsort(mpt->geoms, mpt->ngeoms, sizeof(LWPOINT *), dim ? cmp_point_x : cmp_point_y);
1998
0
      for (uint32_t i = 0; i < mpt->ngeoms; i++)
1999
0
      {
2000
0
        if (!mpt->geoms[i])
2001
0
          continue;
2002
2003
0
        const POINT2D *pti = getPoint2d_cp(mpt->geoms[i]->point, 0);
2004
0
        if (!pti) continue;
2005
2006
        /* check upcoming points if they're within tolerance of current one */
2007
0
        for (uint32_t j = i + 1; j < mpt->ngeoms; j++)
2008
0
        {
2009
0
          if (!mpt->geoms[j])
2010
0
            continue;
2011
2012
0
          const POINT2D *ptj = getPoint2d_cp(mpt->geoms[j]->point, 0);
2013
0
          if (!ptj) continue;
2014
2015
          /* check that the point is in the strip of tolerance around the point */
2016
0
          if ((dim ? ptj->x - pti->x : ptj->y - pti->y) > tolerance)
2017
0
            break;
2018
2019
          /* remove any upcoming point that is within tolerance circle */
2020
0
          if (distance2d_sqr_pt_pt(pti, ptj) <= tolsq)
2021
0
          {
2022
0
            lwpoint_free(mpt->geoms[j]);
2023
0
            mpt->geoms[j] = NULL;
2024
0
            geometry_modified = LW_TRUE;
2025
0
          }
2026
0
        }
2027
0
      }
2028
2029
      /* null out empties */
2030
0
      for (uint32_t j = 0; j < mpt->ngeoms; j++)
2031
0
      {
2032
0
        if (mpt->geoms[j] && lwpoint_is_empty(mpt->geoms[j]))
2033
0
        {
2034
0
          lwpoint_free(mpt->geoms[j]);
2035
0
          mpt->geoms[j] = NULL;
2036
0
          geometry_modified = LW_TRUE;
2037
0
        }
2038
0
      }
2039
2040
      /* compactify array of points */
2041
0
      uint32_t i = 0;
2042
0
      for (uint32_t j = 0; j < mpt->ngeoms; j++)
2043
0
        if (mpt->geoms[j])
2044
0
          mpt->geoms[i++] = mpt->geoms[j];
2045
0
      mpt->ngeoms = i;
2046
0
    }
2047
2048
0
    break;
2049
0
  }
2050
2051
0
    case CIRCSTRINGTYPE:
2052
0
    case NURBSCURVETYPE:
2053
      /* Dunno how to handle these, will return untouched */
2054
0
      return geometry_modified;
2055
2056
  /* Can process most multi* types as generic collection */
2057
0
  case MULTILINETYPE:
2058
0
  case MULTIPOLYGONTYPE:
2059
0
  case TINTYPE:
2060
0
  case COLLECTIONTYPE:
2061
  /* Curve types we mostly ignore, but allow the linear */
2062
  /* portions to be processed by recursing into them */
2063
0
  case MULTICURVETYPE:
2064
0
  case CURVEPOLYTYPE:
2065
0
  case MULTISURFACETYPE:
2066
0
  case COMPOUNDTYPE: {
2067
0
    uint32_t i, j = 0;
2068
0
    LWCOLLECTION *col = (LWCOLLECTION *)(geom);
2069
0
    for (i = 0; i < col->ngeoms; i++)
2070
0
    {
2071
0
      LWGEOM *g = col->geoms[i];
2072
0
      if (!g)
2073
0
        continue;
2074
0
      geometry_modified |= lwgeom_remove_repeated_points_in_place(g, tolerance);
2075
      /* Drop zero'ed out geometries */
2076
0
      if (lwgeom_is_empty(g))
2077
0
      {
2078
0
        lwgeom_free(g);
2079
0
        continue;
2080
0
      }
2081
0
      col->geoms[j++] = g;
2082
0
    }
2083
    /* Update geometry count */
2084
0
    col->ngeoms = j;
2085
0
    break;
2086
0
  }
2087
0
  default: {
2088
0
    lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type));
2089
0
    break;
2090
0
  }
2091
0
  }
2092
2093
0
  if (geometry_modified)
2094
0
    lwgeom_drop_bbox(geom);
2095
0
  return geometry_modified;
2096
0
}
2097
2098
2099
/**************************************************************/
2100
2101
int
2102
lwgeom_simplify_in_place(LWGEOM *geom, double epsilon, int preserve_collapsed)
2103
0
{
2104
0
  int modified = LW_FALSE;
2105
2106
0
  if (!geom)
2107
0
  {
2108
0
    lwerror("NULL geometry encountered");
2109
0
    return LW_FALSE;
2110
0
  }
2111
2112
0
  switch (geom->type)
2113
0
  {
2114
    /* No-op! Cannot simplify points or triangles */
2115
0
    case POINTTYPE:
2116
0
      return modified;
2117
0
    case TRIANGLETYPE:
2118
0
    {
2119
0
      if (preserve_collapsed)
2120
0
        return modified;
2121
0
      LWTRIANGLE *t = lwgeom_as_lwtriangle(geom);
2122
0
      POINTARRAY *pa = t->points;
2123
0
      ptarray_simplify_in_place(pa, epsilon, 0);
2124
0
      if (pa->npoints < 3)
2125
0
      {
2126
0
        pa->npoints = 0;
2127
0
        modified = LW_TRUE;
2128
0
      }
2129
0
      break;
2130
0
    }
2131
0
    case LINETYPE:
2132
0
    {
2133
0
      LWLINE *g = (LWLINE*)(geom);
2134
0
      POINTARRAY *pa = g->points;
2135
0
      uint32_t in_npoints = pa->npoints;
2136
0
      ptarray_simplify_in_place(pa, epsilon, 2);
2137
0
      modified = in_npoints != pa->npoints;
2138
      /* Invalid output */
2139
0
      if (pa->npoints == 1 && pa->maxpoints > 1)
2140
0
      {
2141
        /* Use first point as last point */
2142
0
        if (preserve_collapsed)
2143
0
        {
2144
0
          pa->npoints = 2;
2145
0
          ptarray_copy_point(pa, 0, 1);
2146
0
        }
2147
        /* Finish the collapse process */
2148
0
        else
2149
0
        {
2150
0
          pa->npoints = 0;
2151
0
        }
2152
0
      }
2153
      /* Duped output, force collapse */
2154
0
      if (pa->npoints == 2 && !preserve_collapsed)
2155
0
      {
2156
0
        if (p2d_same(getPoint2d_cp(pa, 0), getPoint2d_cp(pa, 1)))
2157
0
          pa->npoints = 0;
2158
0
      }
2159
0
      break;
2160
0
    }
2161
0
    case POLYGONTYPE:
2162
0
    {
2163
0
      uint32_t i, j = 0;
2164
0
      LWPOLY *g = (LWPOLY*)(geom);
2165
0
      for (i = 0; i < g->nrings; i++)
2166
0
      {
2167
0
        POINTARRAY *pa = g->rings[i];
2168
        /* Only stop collapse on first ring */
2169
0
        int minpoints = (preserve_collapsed && i == 0) ? 4 : 0;
2170
        /* Skip zero'ed out rings */
2171
0
        if(!pa)
2172
0
          continue;
2173
0
        uint32_t in_npoints = pa->npoints;
2174
0
        ptarray_simplify_in_place(pa, epsilon, minpoints);
2175
0
        modified |= in_npoints != pa->npoints;
2176
        /* Drop collapsed rings */
2177
0
        if(pa->npoints < 4)
2178
0
        {
2179
0
          if (i == 0)
2180
0
          {
2181
            /* If the outer ring is dropped, all can be dropped */
2182
0
            for (i = 0; i < g->nrings; i++)
2183
0
            {
2184
0
              pa = g->rings[i];
2185
0
              ptarray_free(pa);
2186
0
            }
2187
0
            break;
2188
0
          }
2189
0
          else
2190
0
          {
2191
            /* Drop this inner ring only */
2192
0
            ptarray_free(pa);
2193
0
            continue;
2194
0
          }
2195
0
        }
2196
0
        g->rings[j++] = pa;
2197
0
      }
2198
      /* Update ring count */
2199
0
      g->nrings = j;
2200
0
      break;
2201
0
    }
2202
    /* Can process all multi* types as generic collection */
2203
0
    case MULTIPOINTTYPE:
2204
0
    case MULTILINETYPE:
2205
0
    case MULTIPOLYGONTYPE:
2206
0
    case TINTYPE:
2207
0
    case COLLECTIONTYPE:
2208
0
    {
2209
0
      uint32_t i, j = 0;
2210
0
      LWCOLLECTION *col = (LWCOLLECTION*)geom;
2211
0
      for (i = 0; i < col->ngeoms; i++)
2212
0
      {
2213
0
        LWGEOM *g = col->geoms[i];
2214
0
        if (!g) continue;
2215
0
        modified |= lwgeom_simplify_in_place(g, epsilon, preserve_collapsed);
2216
        /* Drop zero'ed out geometries */
2217
0
        if(lwgeom_is_empty(g))
2218
0
        {
2219
0
          lwgeom_free(g);
2220
0
          continue;
2221
0
        }
2222
0
        col->geoms[j++] = g;
2223
0
      }
2224
      /* Update geometry count */
2225
0
      col->ngeoms = j;
2226
0
      break;
2227
0
    }
2228
0
    default:
2229
0
    {
2230
0
      lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(geom->type));
2231
0
      break;
2232
0
    }
2233
0
  }
2234
2235
0
  if (modified)
2236
0
  {
2237
0
    lwgeom_drop_bbox(geom);
2238
0
  }
2239
0
  return modified;
2240
0
}
2241
2242
LWGEOM* lwgeom_simplify(const LWGEOM *igeom, double dist, int preserve_collapsed)
2243
0
{
2244
0
  LWGEOM *lwgeom_out = lwgeom_clone_deep(igeom);
2245
0
  lwgeom_simplify_in_place(lwgeom_out, dist, preserve_collapsed);
2246
0
  if (lwgeom_is_empty(lwgeom_out))
2247
0
  {
2248
0
    lwgeom_free(lwgeom_out);
2249
0
    return NULL;
2250
0
  }
2251
0
  return lwgeom_out;
2252
0
}
2253
2254
/**************************************************************/
2255
2256
2257
double lwgeom_area(const LWGEOM *geom)
2258
0
{
2259
0
  int type = geom->type;
2260
2261
0
  if ( type == POLYGONTYPE )
2262
0
    return lwpoly_area((LWPOLY*)geom);
2263
0
  else if ( type == CURVEPOLYTYPE )
2264
0
    return lwcurvepoly_area((LWCURVEPOLY*)geom);
2265
0
  else if (type ==  TRIANGLETYPE )
2266
0
    return lwtriangle_area((LWTRIANGLE*)geom);
2267
0
  else if ( lwgeom_is_collection(geom) )
2268
0
  {
2269
0
    double area = 0.0;
2270
0
    uint32_t i;
2271
0
    LWCOLLECTION *col = (LWCOLLECTION*)geom;
2272
0
    for ( i = 0; i < col->ngeoms; i++ )
2273
0
      area += lwgeom_area(col->geoms[i]);
2274
0
    return area;
2275
0
  }
2276
0
  else
2277
0
    return 0.0;
2278
0
}
2279
2280
double lwgeom_perimeter(const LWGEOM *geom)
2281
0
{
2282
0
  int type = geom->type;
2283
0
  if ( type == POLYGONTYPE )
2284
0
    return lwpoly_perimeter((LWPOLY*)geom);
2285
0
  else if ( type == CURVEPOLYTYPE )
2286
0
    return lwcurvepoly_perimeter((LWCURVEPOLY*)geom);
2287
0
  else if ( type == TRIANGLETYPE )
2288
0
    return lwtriangle_perimeter((LWTRIANGLE*)geom);
2289
0
  else if ( lwgeom_is_collection(geom) )
2290
0
  {
2291
0
    double perimeter = 0.0;
2292
0
    uint32_t i;
2293
0
    LWCOLLECTION *col = (LWCOLLECTION*)geom;
2294
0
    for ( i = 0; i < col->ngeoms; i++ )
2295
0
      perimeter += lwgeom_perimeter(col->geoms[i]);
2296
0
    return perimeter;
2297
0
  }
2298
0
  else
2299
0
    return 0.0;
2300
0
}
2301
2302
double lwgeom_perimeter_2d(const LWGEOM *geom)
2303
0
{
2304
0
  int type = geom->type;
2305
0
  if ( type == POLYGONTYPE )
2306
0
    return lwpoly_perimeter_2d((LWPOLY*)geom);
2307
0
  else if ( type == CURVEPOLYTYPE )
2308
0
    return lwcurvepoly_perimeter_2d((LWCURVEPOLY*)geom);
2309
0
  else if ( type == TRIANGLETYPE )
2310
0
    return lwtriangle_perimeter_2d((LWTRIANGLE*)geom);
2311
0
  else if ( lwgeom_is_collection(geom) )
2312
0
  {
2313
0
    double perimeter = 0.0;
2314
0
    uint32_t i;
2315
0
    LWCOLLECTION *col = (LWCOLLECTION*)geom;
2316
0
    for ( i = 0; i < col->ngeoms; i++ )
2317
0
      perimeter += lwgeom_perimeter_2d(col->geoms[i]);
2318
0
    return perimeter;
2319
0
  }
2320
0
  else
2321
0
    return 0.0;
2322
0
}
2323
2324
double lwgeom_length(const LWGEOM *geom)
2325
0
{
2326
0
  int type = geom->type;
2327
0
  if ( type == LINETYPE )
2328
0
    return lwline_length((LWLINE*)geom);
2329
0
  else if ( type == CIRCSTRINGTYPE )
2330
0
    return lwcircstring_length((LWCIRCSTRING*)geom);
2331
0
  else if ( type == NURBSCURVETYPE )
2332
0
  {
2333
0
    double length;
2334
0
    LWLINE *line = lwnurbscurve_to_linestring((LWNURBSCURVE*)geom, 32);
2335
0
    length = lwline_length(line);
2336
0
    lwline_free(line);
2337
0
    return length;
2338
0
  }
2339
0
  else if ( type == COMPOUNDTYPE )
2340
0
    return lwcompound_length((LWCOMPOUND*)geom);
2341
0
  else if ( lwgeom_is_collection(geom) )
2342
0
  {
2343
0
    double length = 0.0;
2344
0
    uint32_t i;
2345
0
    LWCOLLECTION *col = (LWCOLLECTION*)geom;
2346
0
    for ( i = 0; i < col->ngeoms; i++ )
2347
0
      length += lwgeom_length(col->geoms[i]);
2348
0
    return length;
2349
0
  }
2350
0
  else
2351
0
    return 0.0;
2352
0
}
2353
2354
double lwgeom_length_2d(const LWGEOM *geom)
2355
0
{
2356
0
  int type = geom->type;
2357
0
  if ( type == LINETYPE )
2358
0
    return lwline_length_2d((LWLINE*)geom);
2359
0
  else if ( type == CIRCSTRINGTYPE )
2360
0
    return lwcircstring_length_2d((LWCIRCSTRING*)geom);
2361
0
  else if ( type == NURBSCURVETYPE )
2362
0
  {
2363
0
    double length;
2364
0
    LWLINE *line = lwnurbscurve_to_linestring((LWNURBSCURVE*)geom, 32);
2365
0
    length = lwline_length_2d(line);
2366
0
    lwline_free(line);
2367
0
    return length;
2368
0
  }
2369
0
  else if ( type == COMPOUNDTYPE )
2370
0
    return lwcompound_length_2d((LWCOMPOUND*)geom);
2371
0
  else if ( type != CURVEPOLYTYPE && lwgeom_is_collection(geom) )
2372
0
  {
2373
0
    double length = 0.0;
2374
0
    uint32_t i;
2375
0
    LWCOLLECTION *col = (LWCOLLECTION*)geom;
2376
0
    for ( i = 0; i < col->ngeoms; i++ )
2377
0
      length += lwgeom_length_2d(col->geoms[i]);
2378
0
    return length;
2379
0
  }
2380
0
  else
2381
0
    return 0.0;
2382
0
}
2383
2384
void
2385
lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
2386
0
{
2387
0
  int type = geom->type;
2388
0
  uint32_t i;
2389
2390
0
  switch(type)
2391
0
  {
2392
    /* Take advantage of fact that pt/ln/circ/tri have same memory structure */
2393
0
    case POINTTYPE:
2394
0
    case LINETYPE:
2395
0
    case CIRCSTRINGTYPE:
2396
0
    case TRIANGLETYPE:
2397
0
    {
2398
0
      LWLINE *l = (LWLINE*)geom;
2399
0
      ptarray_affine(l->points, affine);
2400
0
      break;
2401
0
    }
2402
0
    case NURBSCURVETYPE:
2403
0
    {
2404
0
      LWNURBSCURVE *n = (LWNURBSCURVE*)geom;
2405
0
      if (n->points != NULL)
2406
0
        ptarray_affine(n->points, affine);
2407
0
      break;
2408
0
    }
2409
0
    case POLYGONTYPE:
2410
0
    {
2411
0
      LWPOLY *p = (LWPOLY*)geom;
2412
0
      for( i = 0; i < p->nrings; i++ )
2413
0
        ptarray_affine(p->rings[i], affine);
2414
0
      break;
2415
0
    }
2416
0
    case CURVEPOLYTYPE:
2417
0
    {
2418
0
      LWCURVEPOLY *c = (LWCURVEPOLY*)geom;
2419
0
      for( i = 0; i < c->nrings; i++ )
2420
0
        lwgeom_affine(c->rings[i], affine);
2421
0
      break;
2422
0
    }
2423
0
    default:
2424
0
    {
2425
0
      if( lwgeom_is_collection(geom) )
2426
0
      {
2427
0
        LWCOLLECTION *c = (LWCOLLECTION*)geom;
2428
0
        for( i = 0; i < c->ngeoms; i++ )
2429
0
        {
2430
0
          lwgeom_affine(c->geoms[i], affine);
2431
0
        }
2432
0
      }
2433
0
      else
2434
0
      {
2435
0
        lwerror("lwgeom_affine: unable to handle type '%s'", lwtype_name(type));
2436
0
      }
2437
0
    }
2438
0
  }
2439
2440
  /* Recompute bbox if needed */
2441
0
  if (geom->bbox)
2442
0
    lwgeom_refresh_bbox(geom);
2443
0
}
2444
2445
/**
2446
 * Scale a geometry in-place by per-ordinate factors.
2447
 *
2448
 * Applies the provided scale factors to all coordinates of the given geometry.
2449
 * Scaling is applied component-wise: X by factor->x, Y by factor->y, Z by factor->z
2450
 * and M by factor->m when those ordinates are present. The operation mutates
2451
 * the input geometry and recurses into collections and curve/polygon components.
2452
 * If the geometry has an attached bounding box it is refreshed after scaling.
2453
 *
2454
 * @param geom Geometry to be scaled (modified in place). Behavior is undefined if NULL.
2455
 * @param factor Per-ordinate scale factors (X, Y, Z, M).
2456
 */
2457
void
2458
lwgeom_scale(LWGEOM *geom, const POINT4D *factor)
2459
0
{
2460
0
  int type = geom->type;
2461
0
  uint32_t i;
2462
2463
0
  switch(type)
2464
0
  {
2465
    /* Take advantage of fact that pt/ln/circ/tri have same memory structure */
2466
0
    case POINTTYPE:
2467
0
    case LINETYPE:
2468
0
    case CIRCSTRINGTYPE:
2469
0
    case TRIANGLETYPE:
2470
0
    {
2471
0
      LWLINE *l = (LWLINE*)geom;
2472
0
      ptarray_scale(l->points, factor);
2473
0
      break;
2474
0
    }
2475
0
    case NURBSCURVETYPE:
2476
0
    {
2477
0
      LWNURBSCURVE *n = (LWNURBSCURVE*)geom;
2478
0
      if (n->points != NULL)
2479
0
        ptarray_scale(n->points, factor);
2480
0
      break;
2481
0
    }
2482
0
    case POLYGONTYPE:
2483
0
    {
2484
0
      LWPOLY *p = (LWPOLY*)geom;
2485
0
      for( i = 0; i < p->nrings; i++ )
2486
0
        ptarray_scale(p->rings[i], factor);
2487
0
      break;
2488
0
    }
2489
0
    case CURVEPOLYTYPE:
2490
0
    {
2491
0
      LWCURVEPOLY *c = (LWCURVEPOLY*)geom;
2492
0
      for( i = 0; i < c->nrings; i++ )
2493
0
        lwgeom_scale(c->rings[i], factor);
2494
0
      break;
2495
0
    }
2496
0
    default:
2497
0
    {
2498
0
      if( lwgeom_is_collection(geom) )
2499
0
      {
2500
0
        LWCOLLECTION *c = (LWCOLLECTION*)geom;
2501
0
        for( i = 0; i < c->ngeoms; i++ )
2502
0
        {
2503
0
          lwgeom_scale(c->geoms[i], factor);
2504
0
        }
2505
0
      }
2506
0
      else
2507
0
      {
2508
0
        lwerror("lwgeom_scale: unable to handle type '%s'", lwtype_name(type));
2509
0
      }
2510
0
    }
2511
0
  }
2512
2513
  /* Recompute bbox if needed */
2514
0
  if (geom->bbox)
2515
0
    lwgeom_refresh_bbox(geom);
2516
0
}
2517
2518
/**
2519
 * Construct an empty LWGEOM of the requested geometry type.
2520
 *
2521
 * Creates and returns a newly allocated geometry instance with no coordinates
2522
 * (an "empty" geometry), using the provided SRID and dimensionality flags.
2523
 * For homogeneous collection types (MULTI*, COLLECTION, COMPOUND) the `type`
2524
 * argument selects the specific collection subtype to construct.
2525
 *
2526
 * @param type Geometry type code (e.g., POINTTYPE, LINETYPE, POLYGONTYPE,
2527
 *             CURVEPOLYTYPE, CIRCSTRINGTYPE, TRIANGLETYPE, COMPOUNDTYPE,
2528
 *             MULTIPOINTTYPE, MULTILINETYPE, MULTIPOLYGONTYPE, COLLECTIONTYPE,
2529
 *             NURBSCURVETYPE). Unsupported types cause an error to be logged
2530
 *             and the function to return NULL.
2531
 * @param srid Spatial reference identifier to assign to the constructed geometry
2532
 *             (use SRID_UNKNOWN if not set).
2533
 * @param hasz Non-zero to include a Z ordinate in the geometry, zero to omit Z.
2534
 * @param hasm Non-zero to include an M ordinate in the geometry, zero to omit M.
2535
 *
2536
 * @return Pointer to the newly constructed LWGEOM, or NULL if `type` is not
2537
 *         supported.
2538
 */
2539
LWGEOM *
2540
lwgeom_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
2541
0
{
2542
0
  switch(type)
2543
0
  {
2544
0
    case POINTTYPE:
2545
0
      return lwpoint_as_lwgeom(lwpoint_construct_empty(srid, hasz, hasm));
2546
0
    case LINETYPE:
2547
0
      return lwline_as_lwgeom(lwline_construct_empty(srid, hasz, hasm));
2548
0
    case POLYGONTYPE:
2549
0
      return lwpoly_as_lwgeom(lwpoly_construct_empty(srid, hasz, hasm));
2550
0
    case CURVEPOLYTYPE:
2551
0
      return lwcurvepoly_as_lwgeom(lwcurvepoly_construct_empty(srid, hasz, hasm));
2552
0
    case CIRCSTRINGTYPE:
2553
0
      return lwcircstring_as_lwgeom(lwcircstring_construct_empty(srid, hasz, hasm));
2554
0
    case TRIANGLETYPE:
2555
0
      return lwtriangle_as_lwgeom(lwtriangle_construct_empty(srid, hasz, hasm));
2556
0
    case COMPOUNDTYPE:
2557
0
    case MULTIPOINTTYPE:
2558
0
    case MULTILINETYPE:
2559
0
    case MULTIPOLYGONTYPE:
2560
0
    case COLLECTIONTYPE:
2561
0
      return lwcollection_as_lwgeom(lwcollection_construct_empty(type, srid, hasz, hasm));
2562
0
    case NURBSCURVETYPE:
2563
0
      return lwnurbscurve_as_lwgeom(lwnurbscurve_construct_empty(srid, hasz, hasm));
2564
0
    default:
2565
0
      lwerror("lwgeom_construct_empty: unsupported geometry type: %s",
2566
0
              lwtype_name(type));
2567
0
      return NULL;
2568
0
  }
2569
0
}
2570
2571
int
2572
lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt)
2573
0
{
2574
0
  if ( ! lwgeom || lwgeom_is_empty(lwgeom) )
2575
0
    return LW_FAILURE;
2576
2577
0
  switch( lwgeom->type )
2578
0
  {
2579
0
    case POINTTYPE:
2580
0
      return ptarray_startpoint(((LWPOINT*)lwgeom)->point, pt);
2581
0
    case TRIANGLETYPE:
2582
0
    case CIRCSTRINGTYPE:
2583
0
    case LINETYPE:
2584
0
      return ptarray_startpoint(((LWLINE*)lwgeom)->points, pt);
2585
0
    case NURBSCURVETYPE:
2586
0
    {
2587
0
      LWPOINT *start = lwnurbscurve_evaluate((const LWNURBSCURVE*)lwgeom, 0.0);
2588
0
      int rv = LW_FAILURE;
2589
0
      if ( start && start->point && start->point->npoints > 0 )
2590
0
        rv = getPoint4d_p(start->point, 0, pt) ? LW_SUCCESS : LW_FAILURE;
2591
0
      lwpoint_free(start);
2592
0
      return rv;
2593
0
    }
2594
0
    case POLYGONTYPE:
2595
0
      return lwpoly_startpoint((LWPOLY*)lwgeom, pt);
2596
0
    case TINTYPE:
2597
0
    case CURVEPOLYTYPE:
2598
0
    case COMPOUNDTYPE:
2599
0
    case MULTIPOINTTYPE:
2600
0
    case MULTILINETYPE:
2601
0
    case MULTIPOLYGONTYPE:
2602
0
    case COLLECTIONTYPE:
2603
0
    case POLYHEDRALSURFACETYPE:
2604
0
      return lwcollection_startpoint((LWCOLLECTION*)lwgeom, pt);
2605
0
    default:
2606
0
      lwerror("lwgeom_startpoint: unsupported geometry type: %s", lwtype_name(lwgeom->type));
2607
0
      return LW_FAILURE;
2608
0
  }
2609
0
}
2610
2611
static inline double
2612
snap_to_int(double val)
2613
0
{
2614
0
  const double tolerance = 1e-6;
2615
0
    double rintval = rint(val);
2616
0
    if (fabs(val - rintval) < tolerance)
2617
0
    {
2618
0
        return rintval;
2619
0
    }
2620
0
    return val;
2621
0
}
2622
2623
/*
2624
 * See https://github.com/libgeos/geos/pull/956
2625
 * We use scale for rounding when gridsize is < 1 and
2626
 * gridsize for rounding when scale < 1.
2627
 */
2628
static inline void
2629
condition_gridspec_scale(gridspec *grid)
2630
0
{
2631
0
  if(grid->xsize > 0)
2632
0
  {
2633
0
    if(grid->xsize < 1)
2634
0
      grid->xscale = snap_to_int(1/grid->xsize);
2635
0
    else
2636
0
      grid->xsize = snap_to_int(grid->xsize);
2637
0
  }
2638
0
  if(grid->ysize > 0)
2639
0
  {
2640
0
    if(grid->ysize < 1)
2641
0
      grid->yscale = snap_to_int(1/grid->ysize);
2642
0
    else
2643
0
      grid->ysize = snap_to_int(grid->ysize);
2644
0
  }
2645
0
}
2646
2647
2648
void
2649
lwgeom_grid_in_place(LWGEOM *geom, gridspec *grid)
2650
0
{
2651
0
  if (!geom) return;
2652
0
  if (lwgeom_is_empty(geom)) return;
2653
2654
0
  condition_gridspec_scale(grid);
2655
2656
0
  switch ( geom->type )
2657
0
  {
2658
0
    case POINTTYPE:
2659
0
    {
2660
0
      LWPOINT *pt = (LWPOINT*)(geom);
2661
0
      ptarray_grid_in_place(pt->point, grid);
2662
0
      return;
2663
0
    }
2664
0
    case CIRCSTRINGTYPE:
2665
0
    case TRIANGLETYPE:
2666
0
    case LINETYPE:
2667
0
    {
2668
0
      LWLINE *ln = (LWLINE*)(geom);
2669
0
      ptarray_grid_in_place(ln->points, grid);
2670
      /* For invalid line, return an EMPTY */
2671
0
      if (ln->points->npoints < 2)
2672
0
        ln->points->npoints = 0;
2673
0
      return;
2674
0
    }
2675
0
    case NURBSCURVETYPE:
2676
0
    {
2677
0
      LWNURBSCURVE *n = (LWNURBSCURVE *)(geom);
2678
0
      uint32_t old_npoints;
2679
0
      if (!n->points)
2680
0
        return;
2681
2682
0
      old_npoints = n->points->npoints;
2683
0
      ptarray_grid_in_place(n->points, grid);
2684
0
      if (n->points->npoints < n->degree + 1)
2685
0
        n->points->npoints = 0;
2686
2687
0
      if (n->points->npoints != old_npoints)
2688
0
      {
2689
0
        if (n->weights)
2690
0
          lwfree(n->weights);
2691
0
        if (n->knots)
2692
0
          lwfree(n->knots);
2693
0
        n->weights = NULL;
2694
0
        n->knots = NULL;
2695
0
        n->nweights = 0;
2696
0
        n->nknots = 0;
2697
0
      }
2698
0
      return;
2699
0
    }
2700
0
    case POLYGONTYPE:
2701
0
    {
2702
0
      LWPOLY *ply = (LWPOLY*)(geom);
2703
0
      if (!ply->rings) return;
2704
2705
      /* Check first the external ring */
2706
0
      uint32_t i = 0;
2707
0
      POINTARRAY *pa = ply->rings[0];
2708
0
      ptarray_grid_in_place(pa, grid);
2709
0
      if (pa->npoints < 4)
2710
0
      {
2711
        /* External ring collapsed: free everything */
2712
0
        for (i = 0; i < ply->nrings; i++)
2713
0
        {
2714
0
          ptarray_free(ply->rings[i]);
2715
0
        }
2716
0
        ply->nrings = 0;
2717
0
        return;
2718
0
      }
2719
2720
      /* Check the other rings */
2721
0
      uint32_t j = 1;
2722
0
      for (i = 1; i < ply->nrings; i++)
2723
0
      {
2724
0
        POINTARRAY *pa = ply->rings[i];
2725
0
        ptarray_grid_in_place(pa, grid);
2726
2727
        /* Skip bad rings */
2728
0
        if (pa->npoints >= 4)
2729
0
        {
2730
0
          ply->rings[j++] = pa;
2731
0
        }
2732
0
        else
2733
0
        {
2734
0
          ptarray_free(pa);
2735
0
        }
2736
0
      }
2737
      /* Adjust ring count appropriately */
2738
0
      ply->nrings = j;
2739
0
      return;
2740
0
    }
2741
0
    case MULTIPOINTTYPE:
2742
0
    case MULTILINETYPE:
2743
0
    case MULTIPOLYGONTYPE:
2744
0
    case TINTYPE:
2745
0
    case COLLECTIONTYPE:
2746
0
    case COMPOUNDTYPE:
2747
0
    {
2748
0
      LWCOLLECTION *col = (LWCOLLECTION*)(geom);
2749
0
      uint32_t i, j = 0;
2750
0
      if (!col->geoms) return;
2751
0
      for (i = 0; i < col->ngeoms; i++)
2752
0
      {
2753
0
        LWGEOM *g = col->geoms[i];
2754
0
        lwgeom_grid_in_place(g, grid);
2755
        /* Empty geoms need to be freed */
2756
        /* before we move on */
2757
0
        if (lwgeom_is_empty(g))
2758
0
        {
2759
0
          lwgeom_free(g);
2760
0
          continue;
2761
0
        }
2762
0
        col->geoms[j++] = g;
2763
0
      }
2764
0
      col->ngeoms = j;
2765
0
      return;
2766
0
    }
2767
0
    default:
2768
0
    {
2769
0
      lwerror("%s: Unsupported geometry type: %s", __func__,
2770
0
              lwtype_name(geom->type));
2771
0
      return;
2772
0
    }
2773
0
  }
2774
0
}
2775
2776
2777
LWGEOM *
2778
lwgeom_grid(const LWGEOM *lwgeom, gridspec *grid)
2779
0
{
2780
0
  LWGEOM *lwgeom_out = lwgeom_clone_deep(lwgeom);
2781
0
  lwgeom_grid_in_place(lwgeom_out, grid);
2782
0
  return lwgeom_out;
2783
0
}
2784
2785
2786
/* Prototype for recursion */
2787
static void lwgeom_subdivide_recursive(const LWGEOM *geom,
2788
               uint8_t dimension,
2789
               uint32_t maxvertices,
2790
               uint32_t depth,
2791
               LWCOLLECTION *col,
2792
               double gridSize);
2793
2794
static void
2795
lwgeom_subdivide_recursive(const LWGEOM *geom,
2796
         uint8_t dimension,
2797
         uint32_t maxvertices,
2798
         uint32_t depth,
2799
         LWCOLLECTION *col,
2800
         double gridSize)
2801
0
{
2802
0
  const uint32_t maxdepth = 50;
2803
2804
0
  if (!geom)
2805
0
    return;
2806
2807
0
  const GBOX *box_in = lwgeom_get_bbox(geom);
2808
0
  if (!box_in)
2809
0
    return;
2810
2811
0
  LW_ON_INTERRUPT(return);
2812
2813
0
  GBOX clip;
2814
0
  gbox_duplicate(box_in, &clip);
2815
0
  double width = clip.xmax - clip.xmin;
2816
0
  double height = clip.ymax - clip.ymin;
2817
2818
0
  if ( geom->type == POLYHEDRALSURFACETYPE || geom->type == TINTYPE )
2819
0
    lwerror("%s: unsupported geometry type '%s'", __func__, lwtype_name(geom->type));
2820
2821
0
  if ( width == 0.0 && height == 0.0 )
2822
0
  {
2823
0
    if ( geom->type == POINTTYPE && dimension == 0)
2824
0
      lwcollection_add_lwgeom(col, lwgeom_clone_deep(geom));
2825
0
    return;
2826
0
  }
2827
2828
0
  if (width == 0.0)
2829
0
  {
2830
0
    clip.xmax += FP_TOLERANCE;
2831
0
    clip.xmin -= FP_TOLERANCE;
2832
0
    width = 2 * FP_TOLERANCE;
2833
0
  }
2834
0
  if (height == 0.0)
2835
0
  {
2836
0
    clip.ymax += FP_TOLERANCE;
2837
0
    clip.ymin -= FP_TOLERANCE;
2838
0
    height = 2 * FP_TOLERANCE;
2839
0
  }
2840
2841
  /* Always just recurse into collections */
2842
0
  if ( lwgeom_is_collection(geom) && geom->type != MULTIPOINTTYPE )
2843
0
  {
2844
0
    LWCOLLECTION *incol = (LWCOLLECTION*)geom;
2845
    /* Don't increment depth yet, since we aren't actually
2846
     * subdividing geometries yet */
2847
0
    for (uint32_t i = 0; i < incol->ngeoms; i++ )
2848
0
      lwgeom_subdivide_recursive(incol->geoms[i], dimension, maxvertices, depth, col, gridSize);
2849
0
    return;
2850
0
  }
2851
2852
0
  if (lwgeom_dimension(geom) < dimension)
2853
0
  {
2854
    /* We've hit a lower dimension object produced by clipping at
2855
     * a shallower recursion level. Ignore it. */
2856
0
    return;
2857
0
  }
2858
2859
  /* But don't go too far. 2^50 ~= 10^15, that's enough subdivision */
2860
  /* Just add what's left */
2861
0
  if ( depth > maxdepth )
2862
0
  {
2863
0
    lwcollection_add_lwgeom(col, lwgeom_clone_deep(geom));
2864
0
    return;
2865
0
  }
2866
2867
0
  uint32_t nvertices = lwgeom_count_vertices(geom);
2868
2869
  /* Skip empties entirely */
2870
0
  if (nvertices == 0)
2871
0
    return;
2872
2873
  /* If it is under the vertex tolerance, just add it, we're done */
2874
0
  if (nvertices <= maxvertices)
2875
0
  {
2876
0
    lwcollection_add_lwgeom(col, lwgeom_clone_deep(geom));
2877
0
    return;
2878
0
  }
2879
2880
0
  uint8_t split_ordinate = (width > height) ? 0 : 1;
2881
0
  double center = (split_ordinate == 0) ? (clip.xmin + clip.xmax) / 2 : (clip.ymin + clip.ymax) / 2;
2882
0
  double pivot = DBL_MAX;
2883
0
  if (geom->type == POLYGONTYPE)
2884
0
  {
2885
0
    uint32_t ring_to_trim = 0;
2886
0
    double ring_area = 0;
2887
0
    double pivot_eps = DBL_MAX;
2888
0
    double pt_eps = DBL_MAX;
2889
0
    POINTARRAY *pa;
2890
0
    LWPOLY *lwpoly = (LWPOLY *)geom;
2891
2892
    /* if there are more points in holes than in outer ring */
2893
0
    if (nvertices >= 2 * lwpoly->rings[0]->npoints)
2894
0
    {
2895
      /* trim holes starting from biggest */
2896
0
      for (uint32_t i = 1; i < lwpoly->nrings; i++)
2897
0
      {
2898
0
        double current_ring_area = fabs(ptarray_signed_area(lwpoly->rings[i]));
2899
0
        if (current_ring_area >= ring_area)
2900
0
        {
2901
0
          ring_area = current_ring_area;
2902
0
          ring_to_trim = i;
2903
0
        }
2904
0
      }
2905
0
    }
2906
2907
0
    pa = lwpoly->rings[ring_to_trim];
2908
2909
    /* find most central point in chosen ring */
2910
0
    for (uint32_t i = 0; i < pa->npoints; i++)
2911
0
    {
2912
0
      double pt;
2913
0
      if (split_ordinate == 0)
2914
0
        pt = getPoint2d_cp(pa, i)->x;
2915
0
      else
2916
0
        pt = getPoint2d_cp(pa, i)->y;
2917
0
      pt_eps = fabs(pt - center);
2918
0
      if (pivot_eps > pt_eps)
2919
0
      {
2920
0
        pivot = pt;
2921
0
        pivot_eps = pt_eps;
2922
0
      }
2923
0
    }
2924
0
  }
2925
0
  GBOX subbox1, subbox2;
2926
0
  gbox_duplicate(&clip, &subbox1);
2927
0
  gbox_duplicate(&clip, &subbox2);
2928
2929
0
  if (pivot == DBL_MAX)
2930
0
    pivot = center;
2931
2932
0
  if (split_ordinate == 0)
2933
0
  {
2934
0
    if (FP_NEQUALS(subbox1.xmax, pivot) && FP_NEQUALS(subbox1.xmin, pivot))
2935
0
      subbox1.xmax = subbox2.xmin = pivot;
2936
0
    else
2937
0
      subbox1.xmax = subbox2.xmin = center;
2938
0
  }
2939
0
  else
2940
0
  {
2941
0
    if (FP_NEQUALS(subbox1.ymax, pivot) && FP_NEQUALS(subbox1.ymin, pivot))
2942
0
      subbox1.ymax = subbox2.ymin = pivot;
2943
0
    else
2944
0
      subbox1.ymax = subbox2.ymin = center;
2945
0
  }
2946
2947
0
  ++depth;
2948
2949
0
  {
2950
0
    LWGEOM *subbox = (LWGEOM *)lwpoly_construct_envelope(
2951
0
        geom->srid, subbox1.xmin, subbox1.ymin, subbox1.xmax, subbox1.ymax);
2952
0
    LWGEOM *clipped = lwgeom_intersection_prec(geom, subbox, gridSize);
2953
0
    lwgeom_simplify_in_place(clipped, 0.0, LW_TRUE);
2954
0
    lwgeom_free(subbox);
2955
0
    if (clipped && !lwgeom_is_empty(clipped))
2956
0
    {
2957
0
      lwgeom_subdivide_recursive(clipped, dimension, maxvertices, depth, col, gridSize);
2958
0
      lwgeom_free(clipped);
2959
0
    }
2960
0
  }
2961
0
  {
2962
0
    LWGEOM *subbox = (LWGEOM *)lwpoly_construct_envelope(
2963
0
        geom->srid, subbox2.xmin, subbox2.ymin, subbox2.xmax, subbox2.ymax);
2964
0
    LWGEOM *clipped = lwgeom_intersection_prec(geom, subbox, gridSize);
2965
0
    lwgeom_simplify_in_place(clipped, 0.0, LW_TRUE);
2966
0
    lwgeom_free(subbox);
2967
0
    if (clipped && !lwgeom_is_empty(clipped))
2968
0
    {
2969
0
      lwgeom_subdivide_recursive(clipped, dimension, maxvertices, depth, col, gridSize);
2970
0
      lwgeom_free(clipped);
2971
0
    }
2972
0
  }
2973
0
}
2974
2975
LWCOLLECTION *
2976
lwgeom_subdivide_prec(const LWGEOM *geom, uint32_t maxvertices, double gridSize)
2977
0
{
2978
0
  static uint32_t startdepth = 0;
2979
0
  static uint32_t minmaxvertices = 5;
2980
0
  LWCOLLECTION *col;
2981
2982
0
  col = lwcollection_construct_empty(COLLECTIONTYPE, geom->srid, lwgeom_has_z(geom), lwgeom_has_m(geom));
2983
2984
0
  if ( lwgeom_is_empty(geom) )
2985
0
    return col;
2986
2987
0
  if ( maxvertices < minmaxvertices )
2988
0
  {
2989
0
    lwcollection_free(col);
2990
0
    lwerror("%s: cannot subdivide to fewer than %d vertices per output", __func__, minmaxvertices);
2991
0
  }
2992
2993
0
  lwgeom_subdivide_recursive(geom, lwgeom_dimension(geom), maxvertices, startdepth, col, gridSize);
2994
0
  lwgeom_set_srid((LWGEOM*)col, geom->srid);
2995
0
  return col;
2996
0
}
2997
2998
LWCOLLECTION *
2999
lwgeom_subdivide(const LWGEOM *geom, uint32_t maxvertices)
3000
0
{
3001
0
  return lwgeom_subdivide_prec(geom, maxvertices, -1);
3002
0
}
3003
3004
3005
3006
int
3007
lwgeom_is_trajectory(const LWGEOM *geom)
3008
0
{
3009
0
  int type = geom->type;
3010
3011
0
  if( type != LINETYPE )
3012
0
  {
3013
0
    lwnotice("Geometry is not a LINESTRING");
3014
0
    return LW_FALSE;
3015
0
  }
3016
0
  return lwline_is_trajectory((LWLINE*)geom);
3017
0
}
3018
3019
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
3020
STATIC_ASSERT(sizeof(double) == sizeof(uint64_t),this_should_be_true);
3021
3022
static double trim_preserve_decimal_digits(double d, int32_t decimal_digits)
3023
0
{
3024
0
  uint64_t dint = 0;
3025
0
  memcpy(&dint, &d, sizeof(double));
3026
  /* Extract the exponent from the IEEE 754 integer representation, which */
3027
  /* corresponds to floor(log2(fabs(d))) */
3028
0
  const int exponent = (int)((dint >> 52) & 2047) - 1023;
3029
  /* (x * 851 + 255) / 256 == 1 + (int)(x * log2(10)) for x in [0,30] */
3030
0
  int bits_needed = 1 + exponent + (decimal_digits * 851 + 255) / 256;
3031
  /* for negative values, (x * 851 + 255) / 256 == (int)(x * log2(10)), so */
3032
  /* subtract one */
3033
0
  if (decimal_digits < 0)
3034
0
    bits_needed --;
3035
3036
  /* This will also handle NaN and Inf since exponent = 1023, and thus for */
3037
  /* reasonable decimal_digits values bits_needed will be > 52 */
3038
0
  if (bits_needed >= 52)
3039
0
  {
3040
0
    return d;
3041
0
  }
3042
0
  if (bits_needed < 1 )
3043
0
    bits_needed = 1;
3044
0
  const uint64_t mask = 0xffffffffffffffffULL << (52 - bits_needed);
3045
0
  dint &= mask;
3046
0
  memcpy(&d, &dint, sizeof(double));
3047
0
  return d;
3048
0
}
3049
3050
void lwgeom_trim_bits_in_place(LWGEOM* geom, int32_t prec_x, int32_t prec_y, int32_t prec_z, int32_t prec_m)
3051
0
{
3052
0
  LWPOINTITERATOR* it = lwpointiterator_create_rw(geom);
3053
0
  POINT4D p;
3054
3055
0
  while (lwpointiterator_has_next(it))
3056
0
  {
3057
0
    lwpointiterator_peek(it, &p);
3058
0
    p.x = trim_preserve_decimal_digits(p.x, prec_x);
3059
0
    p.y = trim_preserve_decimal_digits(p.y, prec_y);
3060
0
    if (lwgeom_has_z(geom))
3061
0
      p.z = trim_preserve_decimal_digits(p.z, prec_z);
3062
0
    if (lwgeom_has_m(geom))
3063
0
      p.m = trim_preserve_decimal_digits(p.m, prec_m);
3064
0
    lwpointiterator_modify_next(it, &p);
3065
0
  }
3066
3067
0
  lwpointiterator_destroy(it);
3068
0
}
3069
3070
LWGEOM *
3071
lwgeom_boundary(LWGEOM *lwgeom)
3072
0
{
3073
0
  int32_t srid = lwgeom_get_srid(lwgeom);
3074
0
  uint8_t hasz = lwgeom_has_z(lwgeom);
3075
0
  uint8_t hasm = lwgeom_has_m(lwgeom);
3076
3077
0
  switch (lwgeom->type)
3078
0
  {
3079
0
  case POINTTYPE:
3080
0
  case MULTIPOINTTYPE: {
3081
0
    return lwgeom_construct_empty(lwgeom->type, srid, hasz, hasm);
3082
0
  }
3083
0
  case LINETYPE:
3084
0
  case CIRCSTRINGTYPE: {
3085
0
    if (lwgeom_is_closed(lwgeom) || lwgeom_is_empty(lwgeom))
3086
0
      return (LWGEOM *)lwmpoint_construct_empty(srid, hasz, hasm);
3087
0
    else
3088
0
    {
3089
0
      LWLINE *lwline = (LWLINE *)lwgeom;
3090
0
      LWMPOINT *lwmpoint = lwmpoint_construct_empty(srid, hasz, hasm);
3091
0
      POINT4D pt;
3092
0
      getPoint4d_p(lwline->points, 0, &pt);
3093
0
      lwmpoint_add_lwpoint(lwmpoint, lwpoint_make(srid, hasz, hasm, &pt));
3094
0
      getPoint4d_p(lwline->points, lwline->points->npoints - 1, &pt);
3095
0
      lwmpoint_add_lwpoint(lwmpoint, lwpoint_make(srid, hasz, hasm, &pt));
3096
3097
0
      return (LWGEOM *)lwmpoint;
3098
0
    }
3099
0
  }
3100
0
  case NURBSCURVETYPE: {
3101
0
    if (lwgeom_is_closed(lwgeom) || lwgeom_is_empty(lwgeom))
3102
0
      return (LWGEOM *)lwmpoint_construct_empty(srid, hasz, hasm);
3103
0
    else
3104
0
    {
3105
0
      LWMPOINT *lwmpoint = lwmpoint_construct_empty(srid, hasz, hasm);
3106
0
      POINT4D pt;
3107
0
      LWPOINT *startpt = lwnurbscurve_evaluate((const LWNURBSCURVE *)lwgeom, 0.0);
3108
0
      LWPOINT *endpt   = lwnurbscurve_evaluate((const LWNURBSCURVE *)lwgeom, 1.0);
3109
0
      if (startpt)
3110
0
      {
3111
0
        lwpoint_getPoint4d_p(startpt, &pt);
3112
0
        lwmpoint_add_lwpoint(lwmpoint, lwpoint_make(srid, hasz, hasm, &pt));
3113
0
        lwpoint_free(startpt);
3114
0
      }
3115
0
      if (endpt)
3116
0
      {
3117
0
        lwpoint_getPoint4d_p(endpt, &pt);
3118
0
        lwmpoint_add_lwpoint(lwmpoint, lwpoint_make(srid, hasz, hasm, &pt));
3119
0
        lwpoint_free(endpt);
3120
0
      }
3121
0
      return (LWGEOM *)lwmpoint;
3122
0
    }
3123
0
  }
3124
0
  case MULTILINETYPE:
3125
0
  case MULTICURVETYPE: {
3126
0
    LWMLINE *lwmline = (LWMLINE *)lwgeom;
3127
0
    POINT4D *out = lwalloc(sizeof(POINT4D) * lwmline->ngeoms * 2);
3128
0
    uint32_t n = 0;
3129
3130
0
    for (uint32_t i = 0; i < lwmline->ngeoms; i++)
3131
0
    {
3132
0
      LWMPOINT *points = lwgeom_as_lwmpoint(lwgeom_boundary((LWGEOM *)lwmline->geoms[i]));
3133
0
      if (!points)
3134
0
        continue;
3135
3136
0
      for (uint32_t k = 0; k < points->ngeoms; k++)
3137
0
      {
3138
0
        POINT4D pt = getPoint4d(points->geoms[k]->point, 0);
3139
3140
0
        uint8_t seen = LW_FALSE;
3141
0
        for (uint32_t j = 0; j < n; j++)
3142
0
        {
3143
0
          if (memcmp(&(out[j]), &pt, sizeof(POINT4D)) == 0)
3144
0
          {
3145
0
            seen = LW_TRUE;
3146
0
            out[j] = out[--n];
3147
0
            break;
3148
0
          }
3149
0
        }
3150
0
        if (!seen)
3151
0
          out[n++] = pt;
3152
0
      }
3153
3154
0
      lwgeom_free((LWGEOM *)points);
3155
0
    }
3156
3157
0
    LWMPOINT *lwmpoint = lwmpoint_construct_empty(srid, hasz, hasm);
3158
3159
0
    for (uint32_t i = 0; i < n; i++)
3160
0
      lwmpoint_add_lwpoint(lwmpoint, lwpoint_make(srid, hasz, hasm, &(out[i])));
3161
3162
0
    lwfree(out);
3163
3164
0
    return (LWGEOM *)lwmpoint;
3165
0
  }
3166
0
  case TRIANGLETYPE: {
3167
0
    LWTRIANGLE *lwtriangle = (LWTRIANGLE *)lwgeom;
3168
0
    POINTARRAY *points = ptarray_clone_deep(lwtriangle->points);
3169
0
    return (LWGEOM *)lwline_construct(srid, 0, points);
3170
0
  }
3171
0
  case POLYGONTYPE: {
3172
0
    LWPOLY *lwpoly = (LWPOLY *)lwgeom;
3173
3174
0
    LWMLINE *lwmline = lwmline_construct_empty(srid, hasz, hasm);
3175
0
    for (uint32_t i = 0; i < lwpoly->nrings; i++)
3176
0
    {
3177
0
      POINTARRAY *ring = ptarray_clone_deep(lwpoly->rings[i]);
3178
0
      lwmline_add_lwline(lwmline, lwline_construct(srid, 0, ring));
3179
0
    }
3180
3181
    /* Homogenize the multilinestring to hopefully get a single LINESTRING */
3182
0
    LWGEOM *lwout = lwgeom_homogenize((LWGEOM *)lwmline);
3183
0
    lwgeom_free((LWGEOM *)lwmline);
3184
0
    return lwout;
3185
0
  }
3186
0
  case CURVEPOLYTYPE: {
3187
0
    LWCURVEPOLY *lwcurvepoly = (LWCURVEPOLY *)lwgeom;
3188
0
    LWCOLLECTION *lwcol = lwcollection_construct_empty(MULTICURVETYPE, srid, hasz, hasm);
3189
3190
0
    for (uint32_t i = 0; i < lwcurvepoly->nrings; i++)
3191
0
      lwcol = lwcollection_add_lwgeom(lwcol, lwgeom_clone_deep(lwcurvepoly->rings[i]));
3192
3193
0
    return (LWGEOM *)lwcol;
3194
0
  }
3195
0
  case MULTIPOLYGONTYPE:
3196
0
  case COLLECTIONTYPE:
3197
0
  case TINTYPE: {
3198
0
    LWCOLLECTION *lwcol = (LWCOLLECTION *)lwgeom;
3199
0
    LWCOLLECTION *lwcol_boundary = lwcollection_construct_empty(COLLECTIONTYPE, srid, hasz, hasm);
3200
3201
0
    for (uint32_t i = 0; i < lwcol->ngeoms; i++)
3202
0
      lwcollection_add_lwgeom(lwcol_boundary, lwgeom_boundary(lwcol->geoms[i]));
3203
3204
0
    LWGEOM *lwout = lwgeom_homogenize((LWGEOM *)lwcol_boundary);
3205
0
    lwgeom_free((LWGEOM *)lwcol_boundary);
3206
3207
0
    return lwout;
3208
0
  }
3209
0
  default:
3210
0
    lwerror("%s: unsupported geometry type: %s", __func__, lwtype_name(lwgeom->type));
3211
0
    return NULL;
3212
0
  }
3213
0
}
3214
3215
int
3216
lwgeom_isfinite(const LWGEOM *lwgeom)
3217
0
{
3218
0
  LWPOINTITERATOR* it = lwpointiterator_create(lwgeom);
3219
0
  int hasz = lwgeom_has_z(lwgeom);
3220
0
  int hasm = lwgeom_has_m(lwgeom);
3221
3222
0
  while (lwpointiterator_has_next(it))
3223
0
  {
3224
0
    POINT4D p;
3225
0
    lwpointiterator_next(it, &p);
3226
0
    int finite = isfinite(p.x) &&
3227
0
      isfinite(p.y) &&
3228
0
      (hasz ? isfinite(p.z) : 1) &&
3229
0
      (hasm ? isfinite(p.m) : 1);
3230
3231
0
    if (!finite)
3232
0
    {
3233
0
      lwpointiterator_destroy(it);
3234
0
      return LW_FALSE;
3235
0
    }
3236
0
  }
3237
0
  lwpointiterator_destroy(it);
3238
0
  return LW_TRUE;
3239
0
}