Coverage Report

Created: 2025-06-13 06:29

/src/gdal/ogr/ogrcurvecollection.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRCurveCollection class.
5
 * Author:   Even Rouault, even dot rouault at spatialys dot com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "ogr_geometry.h"
15
16
#include <cstddef>
17
#include <cstring>
18
#include <limits>
19
#include <new>
20
21
#include "ogr_core.h"
22
#include "ogr_p.h"
23
#include "ogr_spatialref.h"
24
#include "cpl_conv.h"
25
#include "cpl_error.h"
26
#include "cpl_string.h"
27
#include "cpl_vsi.h"
28
29
//! @cond Doxygen_Suppress
30
31
/************************************************************************/
32
/*             OGRCurveCollection( const OGRCurveCollection& )          */
33
/************************************************************************/
34
35
/**
36
 * \brief Copy constructor.
37
 *
38
 * Note: before GDAL 2.1, only the default implementation of the constructor
39
 * existed, which could be unsafe to use.
40
 *
41
 * @since GDAL 2.1
42
 */
43
44
OGRCurveCollection::OGRCurveCollection(const OGRCurveCollection &other)
45
0
{
46
0
    if (other.nCurveCount > 0)
47
0
    {
48
0
        nCurveCount = other.nCurveCount;
49
0
        papoCurves = static_cast<OGRCurve **>(
50
0
            VSI_CALLOC_VERBOSE(sizeof(void *), nCurveCount));
51
52
0
        if (papoCurves)
53
0
        {
54
0
            for (int i = 0; i < nCurveCount; i++)
55
0
            {
56
0
                papoCurves[i] = other.papoCurves[i]->clone();
57
0
            }
58
0
        }
59
0
    }
60
0
}
61
62
/************************************************************************/
63
/*             OGRCurveCollection( OGRCurveCollection&& )               */
64
/************************************************************************/
65
66
/**
67
 * \brief Move constructor.
68
 *
69
 * @since GDAL 3.11
70
 */
71
72
OGRCurveCollection::OGRCurveCollection(OGRCurveCollection &&other)
73
0
    : nCurveCount(other.nCurveCount), papoCurves(other.papoCurves)
74
0
{
75
0
    other.nCurveCount = 0;
76
0
    other.papoCurves = nullptr;
77
0
}
78
79
/************************************************************************/
80
/*                         ~OGRCurveCollection()                        */
81
/************************************************************************/
82
83
OGRCurveCollection::~OGRCurveCollection()
84
85
3.04k
{
86
3.04k
    empty(nullptr);
87
3.04k
}
88
89
/************************************************************************/
90
/*                 operator=( const OGRCurveCollection& )               */
91
/************************************************************************/
92
93
/**
94
 * \brief Assignment operator.
95
 *
96
 * Note: before GDAL 2.1, only the default implementation of the operator
97
 * existed, which could be unsafe to use.
98
 *
99
 * @since GDAL 2.1
100
 */
101
102
OGRCurveCollection &
103
OGRCurveCollection::operator=(const OGRCurveCollection &other)
104
0
{
105
0
    if (this != &other)
106
0
    {
107
0
        empty(nullptr);
108
109
0
        if (other.nCurveCount > 0)
110
0
        {
111
0
            nCurveCount = other.nCurveCount;
112
0
            papoCurves = static_cast<OGRCurve **>(
113
0
                VSI_MALLOC2_VERBOSE(sizeof(void *), nCurveCount));
114
115
0
            if (papoCurves)
116
0
            {
117
0
                for (int i = 0; i < nCurveCount; i++)
118
0
                {
119
0
                    papoCurves[i] = other.papoCurves[i]->clone();
120
0
                }
121
0
            }
122
0
        }
123
0
    }
124
0
    return *this;
125
0
}
126
127
/************************************************************************/
128
/*                    operator=( OGRCurveCollection&& )                 */
129
/************************************************************************/
130
131
/**
132
 * \brief Move assignment operator.
133
 *
134
 * @since GDAL 3.11
135
 */
136
137
OGRCurveCollection &OGRCurveCollection::operator=(OGRCurveCollection &&other)
138
0
{
139
0
    if (this != &other)
140
0
    {
141
0
        empty(nullptr);
142
0
        std::swap(nCurveCount, other.nCurveCount);
143
0
        std::swap(papoCurves, other.papoCurves);
144
0
    }
145
0
    return *this;
146
0
}
147
148
/************************************************************************/
149
/*                              WkbSize()                               */
150
/************************************************************************/
151
152
size_t OGRCurveCollection::WkbSize() const
153
0
{
154
0
    size_t nSize = 9;
155
156
0
    for (auto &&poSubGeom : *this)
157
0
    {
158
0
        nSize += poSubGeom->WkbSize();
159
0
    }
160
161
0
    return nSize;
162
0
}
163
164
/************************************************************************/
165
/*                          addCurveDirectly()                          */
166
/************************************************************************/
167
168
OGRErr OGRCurveCollection::addCurveDirectly(OGRGeometry *poGeom,
169
                                            OGRCurve *poCurve, int bNeedRealloc)
170
2.25k
{
171
2.25k
    poGeom->HomogenizeDimensionalityWith(poCurve);
172
173
2.25k
    if (bNeedRealloc)
174
2.25k
    {
175
#if SIZEOF_VOIDP < 8
176
        if (nCurveCount == std::numeric_limits<int>::max() /
177
                               static_cast<int>(sizeof(OGRCurve *)))
178
        {
179
            CPLError(CE_Failure, CPLE_OutOfMemory, "Too many subgeometries");
180
            return OGRERR_FAILURE;
181
        }
182
#else
183
2.25k
        if (nCurveCount == std::numeric_limits<int>::max())
184
0
        {
185
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many subgeometries");
186
0
            return OGRERR_FAILURE;
187
0
        }
188
2.25k
#endif
189
190
2.25k
        OGRCurve **papoNewCurves = static_cast<OGRCurve **>(VSI_REALLOC_VERBOSE(
191
2.25k
            papoCurves, sizeof(OGRCurve *) * (nCurveCount + 1)));
192
2.25k
        if (papoNewCurves == nullptr)
193
0
            return OGRERR_FAILURE;
194
2.25k
        papoCurves = papoNewCurves;
195
2.25k
    }
196
197
2.25k
    papoCurves[nCurveCount] = poCurve;
198
199
2.25k
    nCurveCount++;
200
201
2.25k
    return OGRERR_NONE;
202
2.25k
}
203
204
/************************************************************************/
205
/*                        importPreambleFromWkb()                      */
206
/************************************************************************/
207
208
OGRErr OGRCurveCollection::importPreambleFromWkb(
209
    OGRGeometry *poGeom, const unsigned char *pabyData, size_t &nSize,
210
    size_t &nDataOffset, OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize,
211
    OGRwkbVariant eWkbVariant)
212
0
{
213
0
    int nCurveCountNew = 0;
214
215
0
    OGRErr eErr = poGeom->importPreambleOfCollectionFromWkb(
216
0
        pabyData, nSize, nDataOffset, eByteOrder, nMinSubGeomSize,
217
0
        nCurveCountNew, eWkbVariant);
218
0
    if (eErr != OGRERR_NONE)
219
0
        return eErr;
220
221
0
    CPLAssert(nCurveCount == 0);
222
0
    nCurveCount = nCurveCountNew;
223
224
    // coverity[tainted_data]
225
0
    papoCurves = static_cast<OGRCurve **>(
226
0
        VSI_CALLOC_VERBOSE(sizeof(void *), nCurveCount));
227
0
    if (nCurveCount != 0 && papoCurves == nullptr)
228
0
    {
229
0
        nCurveCount = 0;
230
0
        return OGRERR_NOT_ENOUGH_MEMORY;
231
0
    }
232
233
0
    return OGRERR_NONE;
234
0
}
235
236
/************************************************************************/
237
/*                       importBodyFromWkb()                            */
238
/************************************************************************/
239
240
OGRErr OGRCurveCollection::importBodyFromWkb(
241
    OGRGeometry *poGeom, const unsigned char *pabyData, size_t nSize,
242
    bool bAcceptCompoundCurve,
243
    OGRErr (*pfnAddCurveDirectlyFromWkb)(OGRGeometry *poGeom,
244
                                         OGRCurve *poCurve),
245
    OGRwkbVariant eWkbVariant, size_t &nBytesConsumedOut)
246
0
{
247
0
    nBytesConsumedOut = 0;
248
    /* -------------------------------------------------------------------- */
249
    /*      Get the Geoms.                                                  */
250
    /* -------------------------------------------------------------------- */
251
0
    const int nIter = nCurveCount;
252
0
    nCurveCount = 0;
253
0
    size_t nDataOffset = 0;
254
0
    for (int iGeom = 0; iGeom < nIter; iGeom++)
255
0
    {
256
0
        OGRGeometry *poSubGeom = nullptr;
257
258
        // Parses sub-geometry.
259
0
        const unsigned char *pabySubData = pabyData + nDataOffset;
260
0
        if (nSize < 9 && nSize != static_cast<size_t>(-1))
261
0
            return OGRERR_NOT_ENOUGH_DATA;
262
263
0
        OGRwkbGeometryType eFlattenSubGeomType = wkbUnknown;
264
0
        if (OGRReadWKBGeometryType(pabySubData, eWkbVariant,
265
0
                                   &eFlattenSubGeomType) != OGRERR_NONE)
266
0
            return OGRERR_FAILURE;
267
0
        eFlattenSubGeomType = wkbFlatten(eFlattenSubGeomType);
268
269
0
        OGRErr eErr = OGRERR_NONE;
270
0
        size_t nSubGeomBytesConsumedOut = 0;
271
0
        if ((eFlattenSubGeomType != wkbCompoundCurve &&
272
0
             OGR_GT_IsCurve(eFlattenSubGeomType)) ||
273
0
            (bAcceptCompoundCurve && eFlattenSubGeomType == wkbCompoundCurve))
274
0
        {
275
0
            eErr = OGRGeometryFactory::createFromWkb(
276
0
                pabySubData, nullptr, &poSubGeom, nSize, eWkbVariant,
277
0
                nSubGeomBytesConsumedOut);
278
0
        }
279
0
        else
280
0
        {
281
0
            CPLDebug(
282
0
                "OGR",
283
0
                "Cannot add geometry of type (%d) to geometry of type (%d)",
284
0
                eFlattenSubGeomType, poGeom->getGeometryType());
285
0
            return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
286
0
        }
287
288
0
        if (eErr == OGRERR_NONE)
289
0
        {
290
0
            CPLAssert(nSubGeomBytesConsumedOut > 0);
291
0
            if (nSize != static_cast<size_t>(-1))
292
0
            {
293
0
                CPLAssert(nSize >= nSubGeomBytesConsumedOut);
294
0
                nSize -= nSubGeomBytesConsumedOut;
295
0
            }
296
297
0
            nDataOffset += nSubGeomBytesConsumedOut;
298
299
0
            OGRCurve *poCurve = poSubGeom->toCurve();
300
0
            eErr = pfnAddCurveDirectlyFromWkb(poGeom, poCurve);
301
0
        }
302
0
        if (eErr != OGRERR_NONE)
303
0
        {
304
0
            delete poSubGeom;
305
0
            return eErr;
306
0
        }
307
0
    }
308
0
    nBytesConsumedOut = nDataOffset;
309
310
0
    return OGRERR_NONE;
311
0
}
312
313
/************************************************************************/
314
/*                            exportToWkt()                             */
315
/************************************************************************/
316
317
std::string OGRCurveCollection::exportToWkt(const OGRGeometry *baseGeom,
318
                                            const OGRWktOptions &opts,
319
                                            OGRErr *err) const
320
0
{
321
0
    try
322
0
    {
323
0
        bool first = true;
324
0
        std::string wkt(baseGeom->getGeometryName());
325
326
0
        OGRWktOptions optsModified(opts);
327
0
        optsModified.variant = wkbVariantIso;
328
0
        wkt += baseGeom->wktTypeString(optsModified.variant);
329
330
0
        for (int i = 0; i < nCurveCount; ++i)
331
0
        {
332
0
            OGRGeometry *geom = papoCurves[i];
333
334
0
            OGRErr subgeomErr = OGRERR_NONE;
335
0
            std::string tempWkt = geom->exportToWkt(optsModified, &subgeomErr);
336
0
            if (subgeomErr != OGRERR_NONE)
337
0
            {
338
0
                if (err)
339
0
                    *err = subgeomErr;
340
0
                return std::string();
341
0
            }
342
343
            // A curve collection has a list of linestrings (OGRCompoundCurve),
344
            // which should have their leader removed, or a OGRCurvePolygon,
345
            // which has leaders for each of its sub-geometries that aren't
346
            // linestrings.
347
0
            if (tempWkt.compare(0, strlen("LINESTRING"), "LINESTRING") == 0)
348
0
            {
349
0
                auto pos = tempWkt.find('(');
350
0
                if (pos != std::string::npos)
351
0
                    tempWkt = tempWkt.substr(pos);
352
0
            }
353
354
0
            if (tempWkt.find("EMPTY") != std::string::npos)
355
0
                continue;
356
357
0
            if (first)
358
0
                wkt += '(';
359
0
            else
360
0
                wkt += ',';
361
0
            first = false;
362
0
            wkt += tempWkt;
363
0
        }
364
365
0
        if (err)
366
0
            *err = OGRERR_NONE;
367
0
        if (first)
368
0
            wkt += "EMPTY";
369
0
        else
370
0
            wkt += ')';
371
0
        return wkt;
372
0
    }
373
0
    catch (const std::bad_alloc &e)
374
0
    {
375
0
        CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
376
0
        if (err)
377
0
            *err = OGRERR_FAILURE;
378
0
        return std::string();
379
0
    }
380
0
}
381
382
/************************************************************************/
383
/*                            exportToWkb()                             */
384
/************************************************************************/
385
386
OGRErr
387
OGRCurveCollection::exportToWkb(const OGRGeometry *poGeom,
388
                                unsigned char *pabyData,
389
                                const OGRwkbExportOptions *psOptions) const
390
0
{
391
0
    if (psOptions == nullptr)
392
0
    {
393
0
        static const OGRwkbExportOptions defaultOptions;
394
0
        psOptions = &defaultOptions;
395
0
    }
396
397
    /* -------------------------------------------------------------------- */
398
    /*      Set the byte order.                                             */
399
    /* -------------------------------------------------------------------- */
400
0
    pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(
401
0
        static_cast<unsigned char>(psOptions->eByteOrder));
402
403
    /* -------------------------------------------------------------------- */
404
    /*      Set the geometry feature type, ensuring that 3D flag is         */
405
    /*      preserved.                                                      */
406
    /* -------------------------------------------------------------------- */
407
0
    GUInt32 nGType = poGeom->getIsoGeometryType();
408
0
    if (psOptions->eWkbVariant == wkbVariantPostGIS1)
409
0
    {
410
0
        const bool bIs3D = wkbHasZ(static_cast<OGRwkbGeometryType>(nGType));
411
0
        nGType = wkbFlatten(nGType);
412
0
        if (nGType == wkbCurvePolygon)
413
0
            nGType = POSTGIS15_CURVEPOLYGON;
414
0
        if (bIs3D)
415
            // Explicitly set wkb25DBit.
416
0
            nGType =
417
0
                static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
418
0
    }
419
420
0
    if (OGR_SWAP(psOptions->eByteOrder))
421
0
    {
422
0
        nGType = CPL_SWAP32(nGType);
423
0
    }
424
425
0
    memcpy(pabyData + 1, &nGType, 4);
426
427
    /* -------------------------------------------------------------------- */
428
    /*      Copy in the raw data.                                           */
429
    /* -------------------------------------------------------------------- */
430
0
    if (OGR_SWAP(psOptions->eByteOrder))
431
0
    {
432
0
        const int nCount = CPL_SWAP32(nCurveCount);
433
0
        memcpy(pabyData + 5, &nCount, 4);
434
0
    }
435
0
    else
436
0
    {
437
0
        memcpy(pabyData + 5, &nCurveCount, 4);
438
0
    }
439
440
    // TODO(schwehr): Where do these 9 values come from?
441
0
    size_t nOffset = 9;
442
443
    /* ==================================================================== */
444
    /*      Serialize each of the Geoms.                                    */
445
    /* ==================================================================== */
446
0
    for (auto &&poSubGeom : *this)
447
0
    {
448
0
        poSubGeom->exportToWkb(pabyData + nOffset, psOptions);
449
450
0
        nOffset += poSubGeom->WkbSize();
451
0
    }
452
453
0
    return OGRERR_NONE;
454
0
}
455
456
/************************************************************************/
457
/*                               empty()                                */
458
/************************************************************************/
459
460
void OGRCurveCollection::empty(OGRGeometry *poGeom)
461
4.18k
{
462
4.18k
    if (papoCurves != nullptr)
463
2.76k
    {
464
2.76k
        for (auto &&poSubGeom : *this)
465
6.06k
        {
466
6.06k
            delete poSubGeom;
467
6.06k
        }
468
2.76k
        CPLFree(papoCurves);
469
2.76k
    }
470
471
4.18k
    nCurveCount = 0;
472
4.18k
    papoCurves = nullptr;
473
4.18k
    if (poGeom)
474
1.13k
        poGeom->setCoordinateDimension(2);
475
4.18k
}
476
477
/************************************************************************/
478
/*                            getEnvelope()                             */
479
/************************************************************************/
480
481
void OGRCurveCollection::getEnvelope(OGREnvelope *psEnvelope) const
482
0
{
483
0
    OGREnvelope3D oEnv3D;
484
0
    getEnvelope(&oEnv3D);
485
0
    psEnvelope->MinX = oEnv3D.MinX;
486
0
    psEnvelope->MinY = oEnv3D.MinY;
487
0
    psEnvelope->MaxX = oEnv3D.MaxX;
488
0
    psEnvelope->MaxY = oEnv3D.MaxY;
489
0
}
490
491
/************************************************************************/
492
/*                            getEnvelope()                             */
493
/************************************************************************/
494
495
void OGRCurveCollection::getEnvelope(OGREnvelope3D *psEnvelope) const
496
0
{
497
0
    OGREnvelope3D oGeomEnv;
498
0
    bool bExtentSet = false;
499
500
0
    *psEnvelope = OGREnvelope3D();
501
0
    for (int iGeom = 0; iGeom < nCurveCount; iGeom++)
502
0
    {
503
0
        if (!papoCurves[iGeom]->IsEmpty())
504
0
        {
505
0
            bExtentSet = true;
506
0
            papoCurves[iGeom]->getEnvelope(&oGeomEnv);
507
0
            psEnvelope->Merge(oGeomEnv);
508
0
        }
509
0
    }
510
511
0
    if (!bExtentSet)
512
0
    {
513
        // To be backward compatible when called on empty geom
514
0
        psEnvelope->MinX = 0.0;
515
0
        psEnvelope->MinY = 0.0;
516
0
        psEnvelope->MinZ = 0.0;
517
0
        psEnvelope->MaxX = 0.0;
518
0
        psEnvelope->MaxY = 0.0;
519
0
        psEnvelope->MaxZ = 0.0;
520
0
    }
521
0
}
522
523
/************************************************************************/
524
/*                               IsEmpty()                              */
525
/************************************************************************/
526
527
OGRBoolean OGRCurveCollection::IsEmpty() const
528
0
{
529
0
    for (auto &&poSubGeom : *this)
530
0
    {
531
0
        if (!poSubGeom->IsEmpty())
532
0
            return FALSE;
533
0
    }
534
0
    return TRUE;
535
0
}
536
537
/************************************************************************/
538
/*                               Equals()                                */
539
/************************************************************************/
540
541
OGRBoolean OGRCurveCollection::Equals(const OGRCurveCollection *poOCC) const
542
0
{
543
0
    if (getNumCurves() != poOCC->getNumCurves())
544
0
        return FALSE;
545
546
    // Should eventually test the SRS.
547
548
0
    for (int iGeom = 0; iGeom < nCurveCount; iGeom++)
549
0
    {
550
0
        if (!getCurve(iGeom)->Equals(poOCC->getCurve(iGeom)))
551
0
            return FALSE;
552
0
    }
553
554
0
    return TRUE;
555
0
}
556
557
/************************************************************************/
558
/*                       setCoordinateDimension()                       */
559
/************************************************************************/
560
561
bool OGRCurveCollection::setCoordinateDimension(OGRGeometry *poGeom,
562
                                                int nNewDimension)
563
1.13k
{
564
1.13k
    for (auto &&poSubGeom : *this)
565
0
    {
566
0
        if (!poSubGeom->setCoordinateDimension(nNewDimension))
567
0
            return false;
568
0
    }
569
570
1.13k
    return poGeom->OGRGeometry::setCoordinateDimension(nNewDimension);
571
1.13k
}
572
573
bool OGRCurveCollection::set3D(OGRGeometry *poGeom, OGRBoolean bIs3D)
574
1.14k
{
575
1.14k
    for (auto &&poSubGeom : *this)
576
1.38k
    {
577
1.38k
        if (!poSubGeom->set3D(bIs3D))
578
0
            return false;
579
1.38k
    }
580
581
1.14k
    return poGeom->OGRGeometry::set3D(bIs3D);
582
1.14k
}
583
584
bool OGRCurveCollection::setMeasured(OGRGeometry *poGeom,
585
                                     OGRBoolean bIsMeasured)
586
1.85k
{
587
1.85k
    for (auto &&poSubGeom : *this)
588
774
    {
589
774
        if (!poSubGeom->setMeasured(bIsMeasured))
590
0
            return false;
591
774
    }
592
593
1.85k
    return poGeom->OGRGeometry::setMeasured(bIsMeasured);
594
1.85k
}
595
596
/************************************************************************/
597
/*                       assignSpatialReference()                       */
598
/************************************************************************/
599
600
void OGRCurveCollection::assignSpatialReference(OGRGeometry *poGeom,
601
                                                const OGRSpatialReference *poSR)
602
3.29k
{
603
3.29k
    for (auto &&poSubGeom : *this)
604
4.40k
    {
605
4.40k
        poSubGeom->assignSpatialReference(poSR);
606
4.40k
    }
607
3.29k
    poGeom->OGRGeometry::assignSpatialReference(poSR);
608
3.29k
}
609
610
/************************************************************************/
611
/*                          getNumCurves()                              */
612
/************************************************************************/
613
614
int OGRCurveCollection::getNumCurves() const
615
0
{
616
0
    return nCurveCount;
617
0
}
618
619
/************************************************************************/
620
/*                           getCurve()                                 */
621
/************************************************************************/
622
623
OGRCurve *OGRCurveCollection::getCurve(int i)
624
9.75k
{
625
9.75k
    if (i < 0 || i >= nCurveCount)
626
35
        return nullptr;
627
9.72k
    return papoCurves[i];
628
9.75k
}
629
630
/************************************************************************/
631
/*                           getCurve()                                 */
632
/************************************************************************/
633
634
const OGRCurve *OGRCurveCollection::getCurve(int i) const
635
0
{
636
0
    if (i < 0 || i >= nCurveCount)
637
0
        return nullptr;
638
0
    return papoCurves[i];
639
0
}
640
641
/************************************************************************/
642
/*                           stealCurve()                               */
643
/************************************************************************/
644
645
OGRCurve *OGRCurveCollection::stealCurve(int i)
646
0
{
647
0
    if (i < 0 || i >= nCurveCount)
648
0
        return nullptr;
649
0
    OGRCurve *poRet = papoCurves[i];
650
0
    if (i < nCurveCount - 1)
651
0
    {
652
0
        memmove(papoCurves + i, papoCurves + i + 1,
653
0
                (nCurveCount - i - 1) * sizeof(OGRCurve *));
654
0
    }
655
0
    nCurveCount--;
656
0
    return poRet;
657
0
}
658
659
/************************************************************************/
660
/*                             transform()                              */
661
/************************************************************************/
662
663
OGRErr OGRCurveCollection::transform(OGRGeometry *poGeom,
664
                                     OGRCoordinateTransformation *poCT)
665
0
{
666
0
    for (int iGeom = 0; iGeom < nCurveCount; iGeom++)
667
0
    {
668
0
        const OGRErr eErr = papoCurves[iGeom]->transform(poCT);
669
0
        if (eErr != OGRERR_NONE)
670
0
        {
671
0
            if (iGeom != 0)
672
0
            {
673
0
                CPLDebug("OGR", "OGRCurveCollection::transform() failed for a "
674
0
                                "geometry other than the first, meaning some "
675
0
                                "geometries are transformed and some are not!");
676
677
0
                return OGRERR_FAILURE;
678
0
            }
679
680
0
            return eErr;
681
0
        }
682
0
    }
683
684
0
    poGeom->assignSpatialReference(poCT->GetTargetCS());
685
686
0
    return OGRERR_NONE;
687
0
}
688
689
/************************************************************************/
690
/*                            flattenTo2D()                             */
691
/************************************************************************/
692
693
void OGRCurveCollection::flattenTo2D(OGRGeometry *poGeom)
694
0
{
695
0
    for (auto &&poSubGeom : *this)
696
0
    {
697
0
        poSubGeom->flattenTo2D();
698
0
    }
699
700
0
    poGeom->setCoordinateDimension(2);
701
0
}
702
703
/************************************************************************/
704
/*                              segmentize()                            */
705
/************************************************************************/
706
707
bool OGRCurveCollection::segmentize(double dfMaxLength)
708
0
{
709
0
    for (auto &&poSubGeom : *this)
710
0
    {
711
0
        if (!poSubGeom->segmentize(dfMaxLength))
712
0
            return false;
713
0
    }
714
0
    return true;
715
0
}
716
717
/************************************************************************/
718
/*                               swapXY()                               */
719
/************************************************************************/
720
721
void OGRCurveCollection::swapXY()
722
0
{
723
0
    for (auto &&poSubGeom : *this)
724
0
    {
725
0
        poSubGeom->swapXY();
726
0
    }
727
0
}
728
729
/************************************************************************/
730
/*                         hasCurveGeometry()                           */
731
/************************************************************************/
732
733
OGRBoolean OGRCurveCollection::hasCurveGeometry(int bLookForNonLinear) const
734
0
{
735
0
    for (auto &&poSubGeom : *this)
736
0
    {
737
0
        if (poSubGeom->hasCurveGeometry(bLookForNonLinear))
738
0
            return TRUE;
739
0
    }
740
0
    return FALSE;
741
0
}
742
743
/************************************************************************/
744
/*                           removeCurve()                              */
745
/************************************************************************/
746
747
/**
748
 * \brief Remove a geometry from the container.
749
 *
750
 * Removing a geometry will cause the geometry count to drop by one, and all
751
 * "higher" geometries will shuffle down one in index.
752
 *
753
 * @param iIndex the index of the geometry to delete.  A value of -1 is a
754
 * special flag meaning that all geometries should be removed.
755
 *
756
 * @param bDelete if true the geometry will be deallocated, otherwise it will
757
 * not.  The default is true as the container is considered to own the
758
 * geometries in it.
759
 *
760
 * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
761
 * out of range.
762
 */
763
764
OGRErr OGRCurveCollection::removeCurve(int iIndex, bool bDelete)
765
766
0
{
767
0
    if (iIndex < -1 || iIndex >= nCurveCount)
768
0
        return OGRERR_FAILURE;
769
770
    // Special case.
771
0
    if (iIndex == -1)
772
0
    {
773
0
        while (nCurveCount > 0)
774
0
            removeCurve(nCurveCount - 1, bDelete);
775
0
        return OGRERR_NONE;
776
0
    }
777
778
0
    if (bDelete)
779
0
        delete papoCurves[iIndex];
780
781
0
    memmove(papoCurves + iIndex, papoCurves + iIndex + 1,
782
0
            sizeof(void *) * (nCurveCount - iIndex - 1));
783
784
0
    nCurveCount--;
785
786
0
    return OGRERR_NONE;
787
0
}
788
789
/************************************************************************/
790
/*                           hasEmptyParts()                            */
791
/************************************************************************/
792
793
/**
794
 * \brief Returns whether a geometry has empty parts/rings.
795
 *
796
 * Returns true if removeEmptyParts() will modify the geometry.
797
 *
798
 * This is different from IsEmpty().
799
 *
800
 * @since GDAL 3.10
801
 */
802
bool OGRCurveCollection::hasEmptyParts() const
803
0
{
804
0
    for (int i = 0; i < nCurveCount; ++i)
805
0
    {
806
0
        if (papoCurves[i]->IsEmpty() || papoCurves[i]->hasEmptyParts())
807
0
            return true;
808
0
    }
809
0
    return false;
810
0
}
811
812
/************************************************************************/
813
/*                          removeEmptyParts()                          */
814
/************************************************************************/
815
816
/**
817
 * \brief Remove empty parts/rings from this geometry.
818
 *
819
 * @since GDAL 3.10
820
 */
821
void OGRCurveCollection::removeEmptyParts()
822
0
{
823
0
    for (int i = nCurveCount - 1; i >= 0; --i)
824
0
    {
825
0
        papoCurves[i]->removeEmptyParts();
826
0
        if (papoCurves[i]->IsEmpty())
827
0
            removeCurve(i, true);
828
0
    }
829
0
}
830
831
/************************************************************************/
832
/*                           reversePoints()                            */
833
/************************************************************************/
834
835
/**
836
 * \brief Reverse point order.
837
 *
838
 * This method updates the points in this curve in place
839
 * reversing the point ordering (first for last, etc) and component ordering.
840
 *
841
 * @since 3.10
842
 */
843
void OGRCurveCollection::reversePoints()
844
845
0
{
846
0
    for (int i = 0; i < nCurveCount / 2; ++i)
847
0
    {
848
0
        std::swap(papoCurves[i], papoCurves[nCurveCount - 1 - i]);
849
0
    }
850
0
    for (int i = 0; i < nCurveCount; ++i)
851
0
    {
852
0
        papoCurves[i]->reversePoints();
853
0
    }
854
0
}
855
856
//! @endcond