Coverage Report

Created: 2025-11-16 06:25

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