Coverage Report

Created: 2026-03-20 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/geom/GeometryCollection.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
7
 * Copyright (C) 2005 2006 Refractions Research Inc.
8
 *
9
 * This is free software; you can redistribute and/or modify it under
10
 * the terms of the GNU Lesser General Public Licence as published
11
 * by the Free Software Foundation.
12
 * See the COPYING file for more information.
13
 *
14
 **********************************************************************
15
 *
16
 * Last port: geom/GeometryCollection.java rev. 1.41
17
 *
18
 **********************************************************************/
19
20
#include <geos/geom/GeometryCollection.h>
21
#include <geos/util/IllegalArgumentException.h>
22
#include <geos/geom/CoordinateSequence.h>
23
#include <geos/geom/CoordinateSequenceFilter.h>
24
#include <geos/geom/GeometryFactory.h>
25
#include <geos/geom/GeometryFilter.h>
26
#include <geos/geom/GeometryComponentFilter.h>
27
#include <geos/util.h>
28
29
30
#include <algorithm>
31
#include <vector>
32
#include <memory>
33
34
namespace geos {
35
namespace geom { // geos::geom
36
37
/*protected*/
38
GeometryCollection::GeometryCollection(const GeometryCollection& gc)
39
    :
40
20.7k
    Geometry(gc),
41
20.7k
    geometries(gc.geometries.size()),
42
20.7k
    flags(gc.flags),
43
20.7k
    envelope(gc.envelope)
44
20.7k
{
45
93.8k
    for(std::size_t i = 0; i < geometries.size(); ++i) {
46
73.1k
        geometries[i] = gc.geometries[i]->clone();
47
73.1k
    }
48
20.7k
}
49
50
GeometryCollection&
51
GeometryCollection::operator=(const GeometryCollection& gc)
52
0
{
53
0
    geometries.resize(gc.geometries.size());
54
0
    envelope = gc.envelope;
55
0
    flags = gc.flags;
56
57
0
    for (std::size_t i = 0; i < geometries.size(); i++) {
58
0
        geometries[i] = gc.geometries[i]->clone();
59
0
    }
60
61
0
    return *this;
62
0
}
63
64
GeometryCollection::GeometryCollection(std::vector<std::unique_ptr<Geometry>> && newGeoms, const GeometryFactory& factory) :
65
328k
    Geometry(&factory),
66
328k
    geometries(std::move(newGeoms)),
67
328k
    flags{}, // set all flags to zero
68
328k
    envelope(computeEnvelopeInternal())
69
328k
{
70
71
328k
    if (hasNullElements(&geometries)) {
72
0
        throw util::IllegalArgumentException("geometries must not contain null elements\n");
73
0
    }
74
75
328k
    setSRID(getSRID());
76
328k
}
77
78
void
79
GeometryCollection::setSRID(int newSRID)
80
942k
{
81
942k
    Geometry::setSRID(newSRID);
82
60.3M
    for(auto& g : geometries) {
83
60.3M
        g->setSRID(newSRID);
84
60.3M
    }
85
942k
}
86
87
/*
88
 * Collects all coordinates of all subgeometries into a CoordinateSequence.
89
 *
90
 * Returns a newly the collected coordinates
91
 *
92
 */
93
std::unique_ptr<CoordinateSequence>
94
GeometryCollection::getCoordinates() const
95
0
{
96
0
    auto coordinates = detail::make_unique<CoordinateSequence>(getNumPoints());
97
98
0
    std::size_t k = 0;
99
0
    for(const auto& g : geometries) {
100
0
        auto childCoordinates = g->getCoordinates(); // TODO avoid this copy where getCoordinateRO() exists
101
0
        std::size_t npts = childCoordinates->getSize();
102
0
        for(std::size_t j = 0; j < npts; ++j) {
103
0
            coordinates->setAt(childCoordinates->getAt(j), k);
104
0
            k++;
105
0
        }
106
0
    }
107
0
    return coordinates;
108
0
}
109
110
bool
111
GeometryCollection::isEmpty() const
112
19.7M
{
113
19.7M
    for(const auto& g : geometries) {
114
660k
        if(!g->isEmpty()) {
115
507k
            return false;
116
507k
        }
117
660k
    }
118
19.1M
    return true;
119
19.7M
}
120
121
void
122
3.79M
GeometryCollection::setFlags() const {
123
3.79M
    if (flags.flagsCalculated) {
124
3.62M
        return;
125
3.62M
    }
126
127
28.6M
    for (const auto& geom : geometries) {
128
28.6M
        flags.hasPoints |= geom->hasDimension(Dimension::P);
129
28.6M
        flags.hasLines |= geom->hasDimension(Dimension::L);
130
28.6M
        flags.hasPolygons |= geom->hasDimension(Dimension::A);
131
28.6M
        flags.hasM |= geom->hasM();
132
28.6M
        flags.hasZ |= geom->hasZ();
133
28.6M
        flags.hasCurves |= geom->hasCurvedComponents();
134
28.6M
    }
135
136
165k
    flags.flagsCalculated = true;
137
165k
}
138
139
Dimension::DimensionType
140
GeometryCollection::getDimension() const
141
3.33M
{
142
3.33M
    setFlags();
143
144
3.33M
    if (flags.hasPolygons) {
145
342
        return Dimension::A;
146
342
    }
147
3.33M
    if (flags.hasLines) {
148
85.7k
        return Dimension::L;
149
85.7k
    }
150
3.24M
    if (flags.hasPoints) {
151
4.33k
        return Dimension::P;
152
4.33k
    }
153
3.24M
    return Dimension::False;
154
3.24M
}
155
156
bool
157
58.3k
GeometryCollection::isDimensionStrict(Dimension::DimensionType d) const {
158
58.3k
    setFlags();
159
160
58.3k
    if (isEmpty()) {
161
37.7k
        return true;
162
37.7k
    }
163
164
20.5k
    switch(d) {
165
20.5k
        case Dimension::A: return flags.hasPolygons && !flags.hasLines && !flags.hasPoints;
166
0
        case Dimension::L: return !flags.hasPolygons && flags.hasLines && !flags.hasPoints;
167
0
        case Dimension::P: return !flags.hasPolygons && !flags.hasLines && flags.hasPoints;
168
0
        default:
169
0
            return false;
170
20.5k
    }
171
20.5k
}
172
173
bool
174
5.14k
GeometryCollection::hasDimension(Dimension::DimensionType d) const {
175
5.14k
    setFlags();
176
177
5.14k
    switch (d) {
178
1.71k
        case Dimension:: A: return flags.hasPolygons;
179
1.71k
        case Dimension:: L: return flags.hasLines;
180
1.71k
        case Dimension:: P: return flags.hasPoints;
181
0
        default:
182
0
            return false;
183
5.14k
    }
184
5.14k
}
185
186
int
187
GeometryCollection::getBoundaryDimension() const
188
0
{
189
0
    int dimension = Dimension::False;
190
0
    for(const auto& g : geometries) {
191
0
        dimension = std::max(dimension, g->getBoundaryDimension());
192
0
    }
193
0
    return dimension;
194
0
}
195
196
uint8_t
197
GeometryCollection::getCoordinateDimension() const
198
123k
{
199
123k
    uint8_t dimension = 2;
200
201
3.83M
    for(const auto& g : geometries) {
202
3.83M
        dimension = std::max(dimension, g->getCoordinateDimension());
203
3.83M
    }
204
123k
    return dimension;
205
123k
}
206
207
bool
208
GeometryCollection::hasM() const
209
149k
{
210
149k
    setFlags();
211
149k
    return flags.hasM;
212
149k
}
213
214
bool
215
GeometryCollection::hasZ() const
216
150k
{
217
150k
    setFlags();
218
150k
    return flags.hasZ;
219
150k
}
220
221
size_t
222
GeometryCollection::getNumGeometries() const
223
41.0M
{
224
41.0M
    return geometries.size();
225
41.0M
}
226
227
const Geometry*
228
GeometryCollection::getGeometryN(std::size_t n) const
229
221k
{
230
221k
    return geometries[n].get();
231
221k
}
232
233
std::vector<std::unique_ptr<Geometry>>
234
GeometryCollection::releaseGeometries()
235
35.0k
{
236
35.0k
    auto ret = std::move(geometries);
237
35.0k
    geometryChanged();
238
35.0k
    flags.flagsCalculated = false;
239
35.0k
    return ret;
240
35.0k
}
241
242
size_t
243
GeometryCollection::getNumPoints() const
244
16.5k
{
245
16.5k
    std::size_t numPoints = 0;
246
21.8M
    for(const auto& g : geometries) {
247
21.8M
        numPoints += g->getNumPoints();
248
21.8M
    }
249
16.5k
    return numPoints;
250
16.5k
}
251
252
std::string
253
GeometryCollection::getGeometryType() const
254
9
{
255
9
    return "GeometryCollection";
256
9
}
257
258
std::unique_ptr<Geometry>
259
GeometryCollection::getBoundary() const
260
0
{
261
0
    throw util::IllegalArgumentException("Operation not supported by GeometryCollection\n");
262
0
}
263
264
bool
265
GeometryCollection::equalsExact(const Geometry* other, double tolerance) const
266
0
{
267
0
    if(!isEquivalentClass(other)) {
268
0
        return false;
269
0
    }
270
271
0
    const GeometryCollection* otherCollection = detail::down_cast<const GeometryCollection*>(other);
272
0
    if(geometries.size() != otherCollection->geometries.size()) {
273
0
        return false;
274
0
    }
275
0
    for(std::size_t i = 0; i < geometries.size(); ++i) {
276
0
        if(!(geometries[i]->equalsExact(otherCollection->geometries[i].get(), tolerance))) {
277
0
            return false;
278
0
        }
279
0
    }
280
0
    return true;
281
0
}
282
283
bool
284
GeometryCollection::equalsIdentical(const Geometry* other_g) const
285
0
{
286
0
    if(!isEquivalentClass(other_g)) {
287
0
        return false;
288
0
    }
289
290
0
    const auto& other = static_cast<const GeometryCollection&>(*other_g);
291
0
    if(getNumGeometries() != other.getNumGeometries()) {
292
0
        return false;
293
0
    }
294
295
0
    if (envelope != other.envelope) {
296
0
        return false;
297
0
    }
298
299
0
    for(std::size_t i = 0; i < getNumGeometries(); i++) {
300
0
        if(!(getGeometryN(i)->equalsIdentical(other.getGeometryN(i)))) {
301
0
            return false;
302
0
        }
303
0
    }
304
305
0
    return true;
306
0
}
307
308
void
309
GeometryCollection::apply_rw(const CoordinateFilter* filter)
310
12.1k
{
311
1.11M
    for(auto& g : geometries) {
312
1.11M
        g->apply_rw(filter);
313
1.11M
    }
314
12.1k
}
315
316
void
317
GeometryCollection::apply_ro(CoordinateFilter* filter) const
318
16.5k
{
319
21.8M
    for(const auto& g : geometries) {
320
21.8M
        g->apply_ro(filter);
321
21.8M
    }
322
16.5k
}
323
324
void
325
GeometryCollection::apply_ro(GeometryFilter* filter) const
326
194k
{
327
194k
    filter->filter_ro(this);
328
52.9M
    for(const auto& g : geometries) {
329
52.9M
        g->apply_ro(filter);
330
52.9M
    }
331
194k
}
332
333
void
334
GeometryCollection::apply_rw(GeometryFilter* filter)
335
0
{
336
0
    filter->filter_rw(this);
337
0
    for(auto& g : geometries) {
338
0
        g->apply_rw(filter);
339
0
    }
340
0
}
341
342
void
343
GeometryCollection::normalize()
344
0
{
345
0
    for(auto& g : geometries) {
346
0
        g->normalize();
347
0
    }
348
0
    std::sort(geometries.begin(), geometries.end(), [](const std::unique_ptr<Geometry> & a, const std::unique_ptr<Geometry> & b) {
349
0
        return a->compareTo(b.get()) > 0;
350
0
    });
351
0
}
352
353
Envelope
354
GeometryCollection::computeEnvelopeInternal() const
355
431k
{
356
431k
    Envelope p_envelope;
357
53.5M
    for(const auto& g : geometries) {
358
53.5M
        const Envelope* env = g->getEnvelopeInternal();
359
53.5M
        p_envelope.expandToInclude(env);
360
53.5M
    }
361
431k
    return p_envelope;
362
431k
}
363
364
int
365
GeometryCollection::compareToSameClass(const Geometry* g) const
366
0
{
367
0
    const GeometryCollection* gc = detail::down_cast<const GeometryCollection*>(g);
368
0
    return compare(geometries, gc->geometries);
369
0
}
370
371
96.9k
bool GeometryCollection::hasCurvedComponents() const {
372
96.9k
    setFlags();
373
96.9k
    return flags.hasCurves;
374
96.9k
}
375
376
const CoordinateXY*
377
GeometryCollection::getCoordinate() const
378
0
{
379
0
    for(const auto& g : geometries) {
380
0
        if(!g->isEmpty()) {
381
0
            return g->getCoordinate();
382
0
        }
383
0
    }
384
0
    return nullptr;
385
0
}
386
387
/**
388
 * @return the area of this collection
389
 */
390
double
391
GeometryCollection::getArea() const
392
117k
{
393
117k
    double area = 0.0;
394
4.76M
    for(const auto& g : geometries) {
395
4.76M
        area += g->getArea();
396
4.76M
    }
397
117k
    return area;
398
117k
}
399
400
/**
401
 * @return the total length of this collection
402
 */
403
double
404
GeometryCollection::getLength() const
405
0
{
406
0
    double sum = 0.0;
407
0
    for(const auto& g : geometries) {
408
0
        sum += g->getLength();
409
0
    }
410
0
    return sum;
411
0
}
412
413
void
414
GeometryCollection::apply_rw(GeometryComponentFilter* filter)
415
35.0k
{
416
35.0k
    filter->filter_rw(this);
417
35.0k
    for(auto& g : geometries) {
418
0
        if (filter->isDone()) {
419
0
            return;
420
0
        }
421
0
        g->apply_rw(filter);
422
0
    }
423
35.0k
}
424
425
void
426
GeometryCollection::apply_ro(GeometryComponentFilter* filter) const
427
7.15k
{
428
7.15k
    filter->filter_ro(this);
429
140k
    for(const auto& g : geometries) {
430
140k
        if (filter->isDone()) {
431
0
            return;
432
0
        }
433
140k
        g->apply_ro(filter);
434
140k
    }
435
7.15k
}
436
437
void
438
GeometryCollection::apply_rw(CoordinateSequenceFilter& filter)
439
0
{
440
0
    for(auto& g : geometries) {
441
0
        g->apply_rw(filter);
442
0
        if(filter.isDone()) {
443
0
            break;
444
0
        }
445
0
    }
446
0
    if(filter.isGeometryChanged()) {
447
0
        geometryChanged();
448
0
    }
449
0
}
450
451
void
452
GeometryCollection::apply_ro(CoordinateSequenceFilter& filter) const
453
82.5k
{
454
1.07M
    for(const auto& g : geometries) {
455
1.07M
        g->apply_ro(filter);
456
1.07M
        if(filter.isDone()) {
457
66.2k
            break;
458
66.2k
        }
459
1.07M
    }
460
461
82.5k
    assert(!filter.isGeometryChanged()); // read-only filter...
462
    //if (filter.isGeometryChanged()) geometryChanged();
463
82.5k
}
464
465
GeometryTypeId
466
GeometryCollection::getGeometryTypeId() const
467
93.1k
{
468
93.1k
    return GEOS_GEOMETRYCOLLECTION;
469
93.1k
}
470
471
GeometryCollection*
472
GeometryCollection::reverseImpl() const
473
0
{
474
0
    if(isEmpty()) {
475
0
        return clone().release();
476
0
    }
477
478
0
    std::vector<std::unique_ptr<Geometry>> reversed(geometries.size());
479
480
0
    std::transform(geometries.begin(),
481
0
                   geometries.end(),
482
0
                   reversed.begin(),
483
0
    [](const std::unique_ptr<Geometry> & g) {
484
0
        return g->reverse();
485
0
    });
486
487
0
    return getFactory()->createGeometryCollection(std::move(reversed)).release();
488
0
}
489
490
491
492
} // namespace geos::geom
493
} // namespace geos