Coverage Report

Created: 2026-07-25 06:50

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
0
{
81
0
    empty(nullptr);
82
0
}
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
0
{
161
0
    poGeom->HomogenizeDimensionalityWith(poCurve);
162
163
0
    if (bNeedRealloc)
164
0
    {
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
0
        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
0
#endif
179
180
0
        OGRCurve **papoNewCurves = static_cast<OGRCurve **>(VSI_REALLOC_VERBOSE(
181
0
            papoCurves, sizeof(OGRCurve *) * (nCurveCount + 1)));
182
0
        if (papoNewCurves == nullptr)
183
0
            return OGRERR_FAILURE;
184
0
        papoCurves = papoNewCurves;
185
0
    }
186
187
0
    papoCurves[nCurveCount] = poCurve;
188
189
0
    nCurveCount++;
190
191
0
    return OGRERR_NONE;
192
0
}
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
0
{
452
0
    if (papoCurves != nullptr)
453
0
    {
454
0
        for (auto &&poSubGeom : *this)
455
0
        {
456
0
            delete poSubGeom;
457
0
        }
458
0
        CPLFree(papoCurves);
459
0
    }
460
461
0
    nCurveCount = 0;
462
0
    papoCurves = nullptr;
463
0
    if (poGeom)
464
0
        poGeom->setCoordinateDimension(2);
465
0
}
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
bool 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
bool 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
0
{
554
0
    for (auto &&poSubGeom : *this)
555
0
    {
556
0
        if (!poSubGeom->setCoordinateDimension(nNewDimension))
557
0
            return false;
558
0
    }
559
560
0
    return poGeom->OGRGeometry::setCoordinateDimension(nNewDimension);
561
0
}
562
563
bool OGRCurveCollection::set3D(OGRGeometry *poGeom, bool bIs3D)
564
0
{
565
0
    for (auto &&poSubGeom : *this)
566
0
    {
567
0
        if (!poSubGeom->set3D(bIs3D))
568
0
            return false;
569
0
    }
570
571
0
    return poGeom->OGRGeometry::set3D(bIs3D);
572
0
}
573
574
bool OGRCurveCollection::setMeasured(OGRGeometry *poGeom, bool bIsMeasured)
575
0
{
576
0
    for (auto &&poSubGeom : *this)
577
0
    {
578
0
        if (!poSubGeom->setMeasured(bIsMeasured))
579
0
            return false;
580
0
    }
581
582
0
    return poGeom->OGRGeometry::setMeasured(bIsMeasured);
583
0
}
584
585
/************************************************************************/
586
/*                       assignSpatialReference()                       */
587
/************************************************************************/
588
589
void OGRCurveCollection::assignSpatialReference(OGRGeometry *poGeom,
590
                                                const OGRSpatialReference *poSR)
591
0
{
592
0
    for (auto &&poSubGeom : *this)
593
0
    {
594
0
        poSubGeom->assignSpatialReference(poSR);
595
0
    }
596
0
    poGeom->OGRGeometry::assignSpatialReference(poSR);
597
0
}
598
599
/************************************************************************/
600
/*                            getNumCurves()                            */
601
/************************************************************************/
602
603
int OGRCurveCollection::getNumCurves() const
604
0
{
605
0
    return nCurveCount;
606
0
}
607
608
/************************************************************************/
609
/*                              getCurve()                              */
610
/************************************************************************/
611
612
OGRCurve *OGRCurveCollection::getCurve(int i)
613
0
{
614
0
    if (i < 0 || i >= nCurveCount)
615
0
        return nullptr;
616
0
    return papoCurves[i];
617
0
}
618
619
/************************************************************************/
620
/*                              getCurve()                              */
621
/************************************************************************/
622
623
const OGRCurve *OGRCurveCollection::getCurve(int i) const
624
0
{
625
0
    if (i < 0 || i >= nCurveCount)
626
0
        return nullptr;
627
0
    return papoCurves[i];
628
0
}
629
630
/************************************************************************/
631
/*                             stealCurve()                             */
632
/************************************************************************/
633
634
OGRCurve *OGRCurveCollection::stealCurve(int i)
635
0
{
636
0
    if (i < 0 || i >= nCurveCount)
637
0
        return nullptr;
638
0
    OGRCurve *poRet = papoCurves[i];
639
0
    if (i < nCurveCount - 1)
640
0
    {
641
0
        memmove(papoCurves + i, papoCurves + i + 1,
642
0
                (nCurveCount - i - 1) * sizeof(OGRCurve *));
643
0
    }
644
0
    nCurveCount--;
645
0
    return poRet;
646
0
}
647
648
/************************************************************************/
649
/*                             transform()                              */
650
/************************************************************************/
651
652
OGRErr OGRCurveCollection::transform(OGRGeometry *poGeom,
653
                                     OGRCoordinateTransformation *poCT)
654
0
{
655
0
    for (int iGeom = 0; iGeom < nCurveCount; iGeom++)
656
0
    {
657
0
        const OGRErr eErr = papoCurves[iGeom]->transform(poCT);
658
0
        if (eErr != OGRERR_NONE)
659
0
        {
660
0
            if (iGeom != 0)
661
0
            {
662
0
                CPLDebug("OGR", "OGRCurveCollection::transform() failed for a "
663
0
                                "geometry other than the first, meaning some "
664
0
                                "geometries are transformed and some are not!");
665
666
0
                return OGRERR_FAILURE;
667
0
            }
668
669
0
            return eErr;
670
0
        }
671
0
    }
672
673
0
    poGeom->assignSpatialReference(poCT->GetTargetCS());
674
675
0
    return OGRERR_NONE;
676
0
}
677
678
/************************************************************************/
679
/*                            flattenTo2D()                             */
680
/************************************************************************/
681
682
void OGRCurveCollection::flattenTo2D(OGRGeometry *poGeom)
683
0
{
684
0
    for (auto &&poSubGeom : *this)
685
0
    {
686
0
        poSubGeom->flattenTo2D();
687
0
    }
688
689
0
    poGeom->setCoordinateDimension(2);
690
0
}
691
692
/************************************************************************/
693
/*                             segmentize()                             */
694
/************************************************************************/
695
696
bool OGRCurveCollection::segmentize(double dfMaxLength)
697
0
{
698
0
    for (auto &&poSubGeom : *this)
699
0
    {
700
0
        if (!poSubGeom->segmentize(dfMaxLength))
701
0
            return false;
702
0
    }
703
0
    return true;
704
0
}
705
706
/************************************************************************/
707
/*                               swapXY()                               */
708
/************************************************************************/
709
710
void OGRCurveCollection::swapXY()
711
0
{
712
0
    for (auto &&poSubGeom : *this)
713
0
    {
714
0
        poSubGeom->swapXY();
715
0
    }
716
0
}
717
718
/************************************************************************/
719
/*                          hasCurveGeometry()                          */
720
/************************************************************************/
721
722
bool OGRCurveCollection::hasCurveGeometry(int bLookForNonLinear) const
723
0
{
724
0
    for (auto &&poSubGeom : *this)
725
0
    {
726
0
        if (poSubGeom->hasCurveGeometry(bLookForNonLinear))
727
0
            return TRUE;
728
0
    }
729
0
    return FALSE;
730
0
}
731
732
/************************************************************************/
733
/*                            removeCurve()                             */
734
/************************************************************************/
735
736
/**
737
 * \brief Remove a geometry from the container.
738
 *
739
 * Removing a geometry will cause the geometry count to drop by one, and all
740
 * "higher" geometries will shuffle down one in index.
741
 *
742
 * @param iIndex the index of the geometry to delete.  A value of -1 is a
743
 * special flag meaning that all geometries should be removed.
744
 *
745
 * @param bDelete if true the geometry will be deallocated, otherwise it will
746
 * not.  The default is true as the container is considered to own the
747
 * geometries in it.
748
 *
749
 * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
750
 * out of range.
751
 */
752
753
OGRErr OGRCurveCollection::removeCurve(int iIndex, bool bDelete)
754
755
0
{
756
0
    if (iIndex < -1 || iIndex >= nCurveCount)
757
0
        return OGRERR_FAILURE;
758
759
    // Special case.
760
0
    if (iIndex == -1)
761
0
    {
762
0
        while (nCurveCount > 0)
763
0
            removeCurve(nCurveCount - 1, bDelete);
764
0
        return OGRERR_NONE;
765
0
    }
766
767
0
    if (bDelete)
768
0
        delete papoCurves[iIndex];
769
770
0
    memmove(papoCurves + iIndex, papoCurves + iIndex + 1,
771
0
            sizeof(void *) * (nCurveCount - iIndex - 1));
772
773
0
    nCurveCount--;
774
775
0
    return OGRERR_NONE;
776
0
}
777
778
/************************************************************************/
779
/*                           hasEmptyParts()                            */
780
/************************************************************************/
781
782
/**
783
 * \brief Returns whether a geometry has empty parts/rings.
784
 *
785
 * Returns true if removeEmptyParts() will modify the geometry.
786
 *
787
 * This is different from IsEmpty().
788
 *
789
 * @since GDAL 3.10
790
 */
791
bool OGRCurveCollection::hasEmptyParts() const
792
0
{
793
0
    for (int i = 0; i < nCurveCount; ++i)
794
0
    {
795
0
        if (papoCurves[i]->IsEmpty() || papoCurves[i]->hasEmptyParts())
796
0
            return true;
797
0
    }
798
0
    return false;
799
0
}
800
801
/************************************************************************/
802
/*                          removeEmptyParts()                          */
803
/************************************************************************/
804
805
/**
806
 * \brief Remove empty parts/rings from this geometry.
807
 *
808
 * @since GDAL 3.10
809
 */
810
void OGRCurveCollection::removeEmptyParts()
811
0
{
812
0
    for (int i = nCurveCount - 1; i >= 0; --i)
813
0
    {
814
0
        papoCurves[i]->removeEmptyParts();
815
0
        if (papoCurves[i]->IsEmpty())
816
0
            removeCurve(i, true);
817
0
    }
818
0
}
819
820
/************************************************************************/
821
/*                           reversePoints()                            */
822
/************************************************************************/
823
824
/**
825
 * \brief Reverse point order.
826
 *
827
 * This method updates the points in this curve in place
828
 * reversing the point ordering (first for last, etc) and component ordering.
829
 *
830
 * @since 3.10
831
 */
832
void OGRCurveCollection::reversePoints()
833
834
0
{
835
0
    for (int i = 0; i < nCurveCount / 2; ++i)
836
0
    {
837
0
        std::swap(papoCurves[i], papoCurves[nCurveCount - 1 - i]);
838
0
    }
839
0
    for (int i = 0; i < nCurveCount; ++i)
840
0
    {
841
0
        papoCurves[i]->reversePoints();
842
0
    }
843
0
}
844
845
//! @endcond