Coverage Report

Created: 2026-06-23 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/lwcollection.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
 *
23
 **********************************************************************/
24
25
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include "liblwgeom_internal.h"
30
#include "lwgeom_log.h"
31
32
33
#define CHECK_LWGEOM_ZM 1
34
35
void
36
lwcollection_release(LWCOLLECTION *lwcollection)
37
0
{
38
0
  lwgeom_release(lwcollection_as_lwgeom(lwcollection));
39
0
}
40
41
LWCOLLECTION *
42
lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
43
67.9k
{
44
67.9k
  LWCOLLECTION *ret;
45
67.9k
  int hasz, hasm;
46
67.9k
#ifdef CHECK_LWGEOM_ZM
47
67.9k
  char zm;
48
67.9k
  uint32_t i;
49
67.9k
#endif
50
51
67.9k
  LWDEBUGF(2, "lwcollection_construct called with %d, %d, %p, %d, %p.", type, srid, bbox, ngeoms, geoms);
52
53
67.9k
  if( ! lwtype_is_collection(type) )
54
0
    lwerror("Non-collection type specified in collection constructor!");
55
56
67.9k
  hasz = 0;
57
67.9k
  hasm = 0;
58
67.9k
  if ( ngeoms > 0 )
59
67.9k
  {
60
67.9k
    hasz = FLAGS_GET_Z(geoms[0]->flags);
61
67.9k
    hasm = FLAGS_GET_M(geoms[0]->flags);
62
67.9k
#ifdef CHECK_LWGEOM_ZM
63
67.9k
    zm = FLAGS_GET_ZM(geoms[0]->flags);
64
65
67.9k
    LWDEBUGF(3, "lwcollection_construct type[0]=%d", geoms[0]->type);
66
67
226k
    for (i=1; i<ngeoms; i++)
68
158k
    {
69
158k
      LWDEBUGF(3, "lwcollection_construct type=[%d]=%d", i, geoms[i]->type);
70
71
158k
      if ( zm != FLAGS_GET_ZM(geoms[i]->flags) )
72
0
        lwerror("lwcollection_construct: mixed dimension geometries: %d/%d", zm, FLAGS_GET_ZM(geoms[i]->flags));
73
158k
    }
74
67.9k
#endif
75
67.9k
  }
76
77
78
67.9k
  ret = lwalloc(sizeof(LWCOLLECTION));
79
67.9k
  ret->type = type;
80
67.9k
  ret->flags = lwflags(hasz,hasm,0);
81
67.9k
  FLAGS_SET_BBOX(ret->flags, bbox?1:0);
82
67.9k
  ret->srid = srid;
83
67.9k
  ret->ngeoms = ngeoms;
84
67.9k
  ret->maxgeoms = ngeoms;
85
67.9k
  ret->geoms = geoms;
86
67.9k
  ret->bbox = bbox;
87
88
67.9k
  return ret;
89
67.9k
}
90
91
LWCOLLECTION *
92
lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
93
60.3k
{
94
60.3k
  LWCOLLECTION *ret;
95
96
60.3k
  if( ! lwtype_is_collection(type) )
97
0
  {
98
0
    lwerror("Non-collection type specified in collection constructor!");
99
0
    return NULL;
100
0
  }
101
102
60.3k
  ret = lwalloc(sizeof(LWCOLLECTION));
103
60.3k
  ret->type = type;
104
60.3k
  ret->flags = lwflags(hasz,hasm,0);
105
60.3k
  ret->srid = srid;
106
60.3k
  ret->ngeoms = 0;
107
60.3k
  ret->maxgeoms = 1; /* Allocate room for sub-members, just in case. */
108
60.3k
  ret->geoms = lwalloc(ret->maxgeoms * sizeof(LWGEOM*));
109
60.3k
  ret->bbox = NULL;
110
111
60.3k
  return ret;
112
60.3k
}
113
114
const LWGEOM *
115
lwcollection_getsubgeom(LWCOLLECTION *col, uint32_t gnum)
116
0
{
117
0
  return (const LWGEOM *)col->geoms[gnum];
118
0
}
119
120
/**
121
 * @brief Clone #LWCOLLECTION object. #POINTARRAY are not copied.
122
 *      Bbox is cloned if present in input.
123
 */
124
LWCOLLECTION *
125
lwcollection_clone(const LWCOLLECTION *g)
126
0
{
127
0
  uint32_t i;
128
0
  LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION));
129
0
  memcpy(ret, g, sizeof(LWCOLLECTION));
130
0
  if ( g->ngeoms > 0 )
131
0
  {
132
0
    ret->geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms);
133
0
    for (i=0; i<g->ngeoms; i++)
134
0
    {
135
0
      ret->geoms[i] = lwgeom_clone(g->geoms[i]);
136
0
    }
137
0
    if ( g->bbox ) ret->bbox = gbox_copy(g->bbox);
138
0
  }
139
0
  else
140
0
  {
141
0
    ret->bbox = NULL; /* empty collection */
142
0
    ret->geoms = NULL;
143
0
  }
144
0
  return ret;
145
0
}
146
147
/**
148
* @brief Deep clone #LWCOLLECTION object. #POINTARRAY are copied.
149
*/
150
LWCOLLECTION *
151
lwcollection_clone_deep(const LWCOLLECTION *g)
152
0
{
153
0
  uint32_t i;
154
0
  LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION));
155
0
  memcpy(ret, g, sizeof(LWCOLLECTION));
156
0
  if ( g->ngeoms > 0 )
157
0
  {
158
0
    ret->geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms);
159
0
    for (i=0; i<g->ngeoms; i++)
160
0
    {
161
0
      ret->geoms[i] = lwgeom_clone_deep(g->geoms[i]);
162
0
    }
163
0
    if ( g->bbox ) ret->bbox = gbox_copy(g->bbox);
164
0
  }
165
0
  else
166
0
  {
167
0
    ret->bbox = NULL; /* empty collection */
168
0
    ret->geoms = NULL;
169
0
  }
170
0
  return ret;
171
0
}
172
173
/**
174
 * Ensure the collection can hold up at least ngeoms
175
 */
176
void lwcollection_reserve(LWCOLLECTION *col, uint32_t ngeoms)
177
905k
{
178
905k
  if ( ngeoms <= col->maxgeoms ) return;
179
180
  /* Allocate more space if we need it */
181
25.2k
  do { col->maxgeoms *= 2; } while ( col->maxgeoms < ngeoms );
182
25.2k
  col->geoms = lwrealloc(col->geoms, sizeof(LWGEOM*) * col->maxgeoms);
183
25.2k
}
184
185
/**
186
* Appends geom to the collection managed by col. Does not copy or
187
* clone, simply takes a reference on the passed geom.
188
*/
189
LWCOLLECTION* lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
190
905k
{
191
905k
  if (!col || !geom) return NULL;
192
193
905k
  if (!col->geoms && (col->ngeoms || col->maxgeoms))
194
0
  {
195
0
    lwerror("Collection is in inconsistent state. Null memory but non-zero collection counts.");
196
0
    return NULL;
197
0
  }
198
199
  /* Check type compatibility */
200
905k
  if ( ! lwcollection_allows_subtype(col->type, geom->type) ) {
201
0
    lwerror("%s cannot contain %s element", lwtype_name(col->type), lwtype_name(geom->type));
202
0
    return NULL;
203
0
  }
204
205
  /* In case this is a truly empty, make some initial space  */
206
905k
  if (!col->geoms)
207
0
  {
208
0
    col->maxgeoms = 2;
209
0
    col->ngeoms = 0;
210
0
    col->geoms = lwalloc(col->maxgeoms * sizeof(LWGEOM*));
211
0
  }
212
213
  /* Allocate more space if we need it */
214
905k
  lwcollection_reserve(col, col->ngeoms + 1);
215
216
#ifndef NDEBUG
217
  /* See http://trac.osgeo.org/postgis/ticket/2933 */
218
  /* Make sure we don't already have a reference to this geom */
219
  {
220
    for (uint32_t i = 0; i < col->ngeoms; i++)
221
    {
222
      if (col->geoms[i] == geom)
223
      {
224
        lwerror("%s [%d] found duplicate geometry in collection %p == %p", __FILE__, __LINE__, col->geoms[i], geom);
225
        return col;
226
      }
227
    }
228
  }
229
#endif
230
231
905k
  col->geoms[col->ngeoms] = (LWGEOM*)geom;
232
905k
  col->ngeoms++;
233
905k
  return col;
234
905k
}
235
236
/**
237
 * Appends all geometries from col2 to col1 in place.
238
 * Caller is responsible to release col2.
239
 */
240
LWCOLLECTION *
241
lwcollection_concat_in_place(LWCOLLECTION *col1, const LWCOLLECTION *col2)
242
0
{
243
0
  uint32_t i;
244
0
  if (!col1 || !col2) return NULL;
245
0
  for (i = 0; i < col2->ngeoms; i++)
246
0
    col1 = lwcollection_add_lwgeom(col1, col2->geoms[i]);
247
0
  return col1;
248
0
}
249
250
LWCOLLECTION*
251
lwcollection_segmentize2d(const LWCOLLECTION* col, double dist)
252
0
{
253
0
  uint32_t i, j;
254
0
  LWGEOM** newgeoms;
255
256
0
  if (!col->ngeoms) return lwcollection_clone(col);
257
258
0
  newgeoms = lwalloc(sizeof(LWGEOM*) * col->ngeoms);
259
0
  for (i = 0; i < col->ngeoms; i++)
260
0
  {
261
0
    newgeoms[i] = lwgeom_segmentize2d(col->geoms[i], dist);
262
0
    if (!newgeoms[i])
263
0
    {
264
0
      for (j = 0; j < i; j++)
265
0
        lwgeom_free(newgeoms[j]);
266
0
      lwfree(newgeoms);
267
0
      return NULL;
268
0
    }
269
0
  }
270
271
0
  return lwcollection_construct(
272
0
      col->type, col->srid, NULL, col->ngeoms, newgeoms);
273
0
}
274
275
/** @brief check for same geometry composition
276
 *
277
 */
278
char
279
lwcollection_same(const LWCOLLECTION *c1, const LWCOLLECTION *c2)
280
0
{
281
0
  uint32_t i;
282
283
0
  LWDEBUG(2, "lwcollection_same called");
284
285
0
  if ( c1->type != c2->type ) return LW_FALSE;
286
0
  if ( c1->ngeoms != c2->ngeoms ) return LW_FALSE;
287
288
0
  for ( i = 0; i < c1->ngeoms; i++ )
289
0
  {
290
0
    if ( ! lwgeom_same(c1->geoms[i], c2->geoms[i]) )
291
0
      return LW_FALSE;
292
0
  }
293
294
  /* Former method allowed out-of-order equality between collections
295
296
    hit = lwalloc(sizeof(uint32_t)*c1->ngeoms);
297
    memset(hit, 0, sizeof(uint32_t)*c1->ngeoms);
298
299
    for (i=0; i<c1->ngeoms; i++)
300
    {
301
      char found=0;
302
      for (j=0; j<c2->ngeoms; j++)
303
      {
304
        if ( hit[j] ) continue;
305
        if ( lwgeom_same(c1->geoms[i], c2->geoms[j]) )
306
        {
307
          hit[j] = 1;
308
          found=1;
309
          break;
310
        }
311
      }
312
      if ( ! found ) return LW_FALSE;
313
    }
314
  */
315
316
0
  return LW_TRUE;
317
0
}
318
319
int lwcollection_ngeoms(const LWCOLLECTION *col)
320
0
{
321
0
  uint32_t i;
322
0
  int ngeoms = 0;
323
324
0
  if ( ! col )
325
0
  {
326
0
    lwerror("Null input geometry.");
327
0
    return 0;
328
0
  }
329
330
0
  for ( i = 0; i < col->ngeoms; i++ )
331
0
  {
332
0
    if ( col->geoms[i])
333
0
    {
334
0
      switch (col->geoms[i]->type)
335
0
      {
336
0
      case POINTTYPE:
337
0
      case LINETYPE:
338
0
      case CIRCSTRINGTYPE:
339
0
      case POLYGONTYPE:
340
0
        ngeoms += 1;
341
0
        break;
342
0
      case MULTIPOINTTYPE:
343
0
      case MULTILINETYPE:
344
0
      case MULTICURVETYPE:
345
0
      case MULTIPOLYGONTYPE:
346
0
        ngeoms += col->ngeoms;
347
0
        break;
348
0
      case COLLECTIONTYPE:
349
0
        ngeoms += lwcollection_ngeoms((LWCOLLECTION*)col->geoms[i]);
350
0
        break;
351
0
      }
352
0
    }
353
0
  }
354
0
  return ngeoms;
355
0
}
356
357
void lwcollection_free(LWCOLLECTION *col)
358
74.6k
{
359
74.6k
  uint32_t i;
360
74.6k
  if ( ! col ) return;
361
362
74.6k
  if ( col->bbox )
363
635
  {
364
635
    lwfree(col->bbox);
365
635
  }
366
385k
  for ( i = 0; i < col->ngeoms; i++ )
367
311k
  {
368
311k
    LWDEBUGF(4,"freeing geom[%d]", i);
369
311k
    if ( col->geoms && col->geoms[i] )
370
311k
      lwgeom_free(col->geoms[i]);
371
311k
  }
372
74.6k
  if ( col->geoms )
373
74.6k
  {
374
74.6k
    lwfree(col->geoms);
375
74.6k
  }
376
74.6k
  lwfree(col);
377
74.6k
}
378
379
/**
380
* Examines contents of collection and finds the largest coordinate
381
* dimension of all components. Areal > linear > puntal.
382
*/
383
static uint32_t
384
lwcollection_largest_dimension(const LWCOLLECTION *col)
385
0
{
386
0
  int largest_type = 0;
387
0
  size_t i;
388
0
  for (i = 0; i < col->ngeoms; i++)
389
0
  {
390
0
    LWGEOM *g = col->geoms[i];
391
0
    int gtype = lwgeom_get_type(g);
392
0
    if (lwgeom_is_collection(g))
393
0
    {
394
0
      gtype = lwcollection_largest_dimension((LWCOLLECTION*)g);
395
0
    }
396
397
0
    if (gtype == POINTTYPE || gtype == LINETYPE || gtype == POLYGONTYPE)
398
0
    {
399
0
      if (gtype > largest_type)
400
0
        largest_type = gtype;
401
0
    }
402
0
  }
403
0
  return largest_type;
404
0
}
405
406
407
static int
408
lwcollection_extract_recursive(const LWCOLLECTION* col, uint32_t type, LWCOLLECTION *col_out)
409
0
{
410
0
  size_t i;
411
0
  size_t geoms_added = 0;
412
413
0
  for (i = 0; i < col->ngeoms; i++)
414
0
  {
415
0
    LWGEOM *g = col->geoms[i];
416
0
    if (lwgeom_is_collection(g))
417
0
    {
418
0
      LWCOLLECTION *col_part = lwgeom_as_lwcollection(g);
419
0
      geoms_added += lwcollection_extract_recursive(col_part, type, col_out);
420
0
    }
421
422
0
    if (lwgeom_get_type(g) == type && !lwgeom_is_empty(g))
423
0
    {
424
0
      lwcollection_add_lwgeom(col_out, lwgeom_clone(col->geoms[i]));
425
0
      geoms_added++;
426
0
    }
427
0
  }
428
0
  return geoms_added;
429
0
}
430
431
LWCOLLECTION*
432
lwcollection_extract(const LWCOLLECTION* col, uint32_t type)
433
0
{
434
0
  LWCOLLECTION* outcol;
435
436
0
  if (!col) return NULL;
437
438
  /* Self-discover output type when it is not specified */
439
0
  if (!type)
440
0
    type = lwcollection_largest_dimension(col);
441
442
  /*
443
  * If self-discovery failed, there were no primitive points
444
  * lines or polygons in the collection, so send back an
445
  * empty collection.
446
  */
447
0
  if (!type)
448
0
  {
449
0
    return lwcollection_construct_empty(COLLECTIONTYPE,
450
0
             col->srid,
451
0
             FLAGS_GET_Z(col->flags),
452
0
             FLAGS_GET_M(col->flags));
453
0
  }
454
455
0
  if (!(type == POINTTYPE || type == LINETYPE || type == POLYGONTYPE))
456
0
  {
457
0
    lwerror(
458
0
        "Only POLYGON, LINESTRING and POINT are supported by "
459
0
        "lwcollection_extract. %s requested.",
460
0
        lwtype_name(type));
461
0
    return NULL;
462
0
  }
463
464
0
  outcol = lwcollection_construct_empty(lwtype_multitype(type),
465
0
             col->srid,
466
0
             FLAGS_GET_Z(col->flags),
467
0
             FLAGS_GET_M(col->flags));
468
469
0
  lwcollection_extract_recursive(col, type, outcol);
470
0
  lwgeom_add_bbox(lwcollection_as_lwgeom(outcol));
471
0
  return outcol;
472
0
}
473
474
475
LWCOLLECTION*
476
lwcollection_force_dims(const LWCOLLECTION *col, int hasz, int hasm, double zval, double mval)
477
4.11k
{
478
4.11k
  LWCOLLECTION *colout;
479
480
  /* Return 2D empty */
481
4.11k
  if( lwcollection_is_empty(col) )
482
2.42k
  {
483
2.42k
    colout = lwcollection_construct_empty(col->type, col->srid, hasz, hasm);
484
2.42k
  }
485
1.68k
  else
486
1.68k
  {
487
1.68k
    uint32_t i;
488
1.68k
    LWGEOM **geoms = NULL;
489
1.68k
    geoms = lwalloc(sizeof(LWGEOM*) * col->ngeoms);
490
161k
    for( i = 0; i < col->ngeoms; i++ )
491
159k
    {
492
159k
      geoms[i] = lwgeom_force_dims(col->geoms[i], hasz, hasm, zval, mval);
493
159k
    }
494
1.68k
    colout = lwcollection_construct(col->type, col->srid, NULL, col->ngeoms, geoms);
495
1.68k
  }
496
4.11k
  return colout;
497
4.11k
}
498
499
500
uint32_t lwcollection_count_vertices(const LWCOLLECTION *col)
501
0
{
502
0
  uint32_t i = 0;
503
0
  uint32_t v = 0; /* vertices */
504
0
  assert(col);
505
0
  for ( i = 0; i < col->ngeoms; i++ )
506
0
  {
507
0
    v += lwgeom_count_vertices(col->geoms[i]);
508
0
  }
509
0
  return v;
510
0
}
511
512
513
int lwcollection_allows_subtype(int collectiontype, int subtype)
514
905k
{
515
905k
  if ( collectiontype == COLLECTIONTYPE )
516
334k
    return LW_TRUE;
517
571k
  if ( collectiontype == MULTIPOINTTYPE &&
518
428k
          subtype == POINTTYPE )
519
428k
    return LW_TRUE;
520
143k
  if ( collectiontype == MULTILINETYPE &&
521
69.5k
          subtype == LINETYPE )
522
69.5k
    return LW_TRUE;
523
73.5k
  if ( collectiontype == MULTIPOLYGONTYPE &&
524
73.5k
          subtype == POLYGONTYPE )
525
73.5k
    return LW_TRUE;
526
0
  if ( collectiontype == COMPOUNDTYPE &&
527
0
          (subtype == LINETYPE || subtype == CIRCSTRINGTYPE || subtype == NURBSCURVETYPE) )
528
0
    return LW_TRUE;
529
0
  if ( collectiontype == CURVEPOLYTYPE &&
530
0
          (subtype == CIRCSTRINGTYPE || subtype == LINETYPE || subtype == COMPOUNDTYPE ||
531
0
           subtype == NURBSCURVETYPE) )
532
0
    return LW_TRUE;
533
0
  if ( collectiontype == MULTICURVETYPE &&
534
0
          (subtype == CIRCSTRINGTYPE || subtype == LINETYPE || subtype == COMPOUNDTYPE ||
535
0
           subtype == NURBSCURVETYPE) )
536
0
    return LW_TRUE;
537
0
  if ( collectiontype == MULTISURFACETYPE &&
538
0
          (subtype == POLYGONTYPE || subtype == CURVEPOLYTYPE) )
539
0
    return LW_TRUE;
540
0
  if ( collectiontype == POLYHEDRALSURFACETYPE &&
541
0
          subtype == POLYGONTYPE )
542
0
    return LW_TRUE;
543
0
  if ( collectiontype == TINTYPE &&
544
0
          subtype == TRIANGLETYPE )
545
0
    return LW_TRUE;
546
547
  /* Must be a bad combination! */
548
0
  return LW_FALSE;
549
0
}
550
551
int
552
lwcollection_startpoint(const LWCOLLECTION* col, POINT4D* pt)
553
0
{
554
0
  if ( col->ngeoms < 1 )
555
0
    return LW_FAILURE;
556
557
0
  return lwgeom_startpoint(col->geoms[0], pt);
558
0
}