Coverage Report

Created: 2025-06-13 06:18

/src/gdal/ogr/ogrcompoundcurve.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRCompoundCurve geometry 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 <cmath>
17
#include <cstddef>
18
19
#include "cpl_error.h"
20
#include "ogr_core.h"
21
#include "ogr_geometry.h"
22
#include "ogr_p.h"
23
#include "ogr_spatialref.h"
24
25
/************************************************************************/
26
/*             OGRCompoundCurve( const OGRCompoundCurve& )              */
27
/************************************************************************/
28
29
/**
30
 * \brief Copy constructor.
31
 *
32
 * Note: before GDAL 2.1, only the default implementation of the constructor
33
 * existed, which could be unsafe to use.
34
 *
35
 * @since GDAL 2.1
36
 */
37
38
0
OGRCompoundCurve::OGRCompoundCurve(const OGRCompoundCurve &) = default;
39
40
/************************************************************************/
41
/*                 operator=( const OGRCompoundCurve&)                  */
42
/************************************************************************/
43
44
/**
45
 * \brief Assignment operator.
46
 *
47
 * Note: before GDAL 2.1, only the default implementation of the operator
48
 * existed, which could be unsafe to use.
49
 *
50
 * @since GDAL 2.1
51
 */
52
53
OGRCompoundCurve &OGRCompoundCurve::operator=(const OGRCompoundCurve &other)
54
0
{
55
0
    if (this != &other)
56
0
    {
57
0
        OGRCurve::operator=(other);
58
59
0
        oCC = other.oCC;
60
0
    }
61
0
    return *this;
62
0
}
63
64
/************************************************************************/
65
/*                               clone()                                */
66
/************************************************************************/
67
68
OGRCompoundCurve *OGRCompoundCurve::clone() const
69
70
0
{
71
0
    auto ret = new (std::nothrow) OGRCompoundCurve(*this);
72
0
    if (ret)
73
0
    {
74
0
        if (ret->WkbSize() != WkbSize())
75
0
        {
76
0
            delete ret;
77
0
            ret = nullptr;
78
0
        }
79
0
    }
80
0
    return ret;
81
0
}
82
83
/************************************************************************/
84
/*                          getGeometryType()                           */
85
/************************************************************************/
86
87
OGRwkbGeometryType OGRCompoundCurve::getGeometryType() const
88
89
0
{
90
0
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
91
0
        return wkbCompoundCurveZM;
92
0
    else if (flags & OGR_G_MEASURED)
93
0
        return wkbCompoundCurveM;
94
0
    else if (flags & OGR_G_3D)
95
0
        return wkbCompoundCurveZ;
96
0
    else
97
0
        return wkbCompoundCurve;
98
0
}
99
100
/************************************************************************/
101
/*                          getGeometryName()                           */
102
/************************************************************************/
103
104
const char *OGRCompoundCurve::getGeometryName() const
105
106
0
{
107
0
    return "COMPOUNDCURVE";
108
0
}
109
110
/************************************************************************/
111
/*                              WkbSize()                               */
112
/************************************************************************/
113
size_t OGRCompoundCurve::WkbSize() const
114
0
{
115
0
    return oCC.WkbSize();
116
0
}
117
118
/************************************************************************/
119
/*                       addCurveDirectlyFromWkt()                      */
120
/************************************************************************/
121
122
OGRErr OGRCompoundCurve::addCurveDirectlyFromWkb(OGRGeometry *poSelf,
123
                                                 OGRCurve *poCurve)
124
0
{
125
0
    OGRCompoundCurve *poCC = poSelf->toCompoundCurve();
126
0
    return poCC->addCurveDirectlyInternal(poCurve, DEFAULT_TOLERANCE_EPSILON,
127
0
                                          FALSE);
128
0
}
129
130
/************************************************************************/
131
/*                           importFromWkb()                            */
132
/************************************************************************/
133
134
OGRErr OGRCompoundCurve::importFromWkb(const unsigned char *pabyData,
135
                                       size_t nSize, OGRwkbVariant eWkbVariant,
136
                                       size_t &nBytesConsumedOut)
137
0
{
138
0
    OGRwkbByteOrder eByteOrder = wkbNDR;
139
0
    size_t nDataOffset = 0;
140
    // coverity[tainted_data]
141
0
    OGRErr eErr = oCC.importPreambleFromWkb(this, pabyData, nSize, nDataOffset,
142
0
                                            eByteOrder, 9, eWkbVariant);
143
0
    if (eErr != OGRERR_NONE)
144
0
        return eErr;
145
146
0
    eErr = oCC.importBodyFromWkb(this, pabyData + nDataOffset, nSize,
147
0
                                 false,  // bAcceptCompoundCurve
148
0
                                 addCurveDirectlyFromWkb, eWkbVariant,
149
0
                                 nBytesConsumedOut);
150
0
    if (eErr == OGRERR_NONE)
151
0
        nBytesConsumedOut += nDataOffset;
152
0
    return eErr;
153
0
}
154
155
/************************************************************************/
156
/*                            exportToWkb()                             */
157
/************************************************************************/
158
159
OGRErr OGRCompoundCurve::exportToWkb(unsigned char *pabyData,
160
                                     const OGRwkbExportOptions *psOptions) const
161
0
{
162
0
    OGRwkbExportOptions sOptions(psOptions ? *psOptions
163
0
                                           : OGRwkbExportOptions());
164
165
    // Does not make sense for new geometries, so patch it.
166
0
    if (sOptions.eWkbVariant == wkbVariantOldOgc)
167
0
        sOptions.eWkbVariant = wkbVariantIso;
168
0
    return oCC.exportToWkb(this, pabyData, &sOptions);
169
0
}
170
171
/************************************************************************/
172
/*                       addCurveDirectlyFromWkt()                      */
173
/************************************************************************/
174
175
OGRErr OGRCompoundCurve::addCurveDirectlyFromWkt(OGRGeometry *poSelf,
176
                                                 OGRCurve *poCurve)
177
0
{
178
0
    return poSelf->toCompoundCurve()->addCurveDirectly(poCurve);
179
0
}
180
181
/************************************************************************/
182
/*                           importFromWkt()                            */
183
/************************************************************************/
184
185
OGRErr OGRCompoundCurve::importFromWkt(const char **ppszInput)
186
0
{
187
0
    return importCurveCollectionFromWkt(ppszInput,
188
0
                                        FALSE,  // bAllowEmptyComponent
189
0
                                        TRUE,   // bAllowLineString
190
0
                                        TRUE,   // bAllowCurve
191
0
                                        FALSE,  // bAllowCompoundCurve
192
0
                                        addCurveDirectlyFromWkt);
193
0
}
194
195
/************************************************************************/
196
/*                            exportToWkt()                             */
197
/************************************************************************/
198
std::string OGRCompoundCurve::exportToWkt(const OGRWktOptions &opts,
199
                                          OGRErr *err) const
200
0
{
201
0
    return oCC.exportToWkt(this, opts, err);
202
0
}
203
204
/************************************************************************/
205
/*                               empty()                                */
206
/************************************************************************/
207
208
void OGRCompoundCurve::empty()
209
0
{
210
0
    oCC.empty(this);
211
0
}
212
213
/************************************************************************/
214
/*                            getEnvelope()                             */
215
/************************************************************************/
216
217
void OGRCompoundCurve::getEnvelope(OGREnvelope *psEnvelope) const
218
0
{
219
0
    oCC.getEnvelope(psEnvelope);
220
0
}
221
222
/************************************************************************/
223
/*                            getEnvelope()                             */
224
/************************************************************************/
225
226
void OGRCompoundCurve::getEnvelope(OGREnvelope3D *psEnvelope) const
227
0
{
228
0
    oCC.getEnvelope(psEnvelope);
229
0
}
230
231
/************************************************************************/
232
/*                               IsEmpty()                              */
233
/************************************************************************/
234
235
OGRBoolean OGRCompoundCurve::IsEmpty() const
236
0
{
237
0
    return oCC.IsEmpty();
238
0
}
239
240
/************************************************************************/
241
/*                             get_Length()                             */
242
/*                                                                      */
243
/*      For now we return a simple euclidean 2D distance.               */
244
/************************************************************************/
245
246
double OGRCompoundCurve::get_Length() const
247
0
{
248
0
    double dfLength = 0.0;
249
0
    for (int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++)
250
0
        dfLength += oCC.papoCurves[iGeom]->get_Length();
251
0
    return dfLength;
252
0
}
253
254
/************************************************************************/
255
/*                             StartPoint()                             */
256
/************************************************************************/
257
258
void OGRCompoundCurve::StartPoint(OGRPoint *p) const
259
0
{
260
0
    CPLAssert(oCC.nCurveCount > 0);
261
0
    oCC.papoCurves[0]->StartPoint(p);
262
0
}
263
264
/************************************************************************/
265
/*                              EndPoint()                              */
266
/************************************************************************/
267
268
void OGRCompoundCurve::EndPoint(OGRPoint *p) const
269
0
{
270
0
    CPLAssert(oCC.nCurveCount > 0);
271
0
    oCC.papoCurves[oCC.nCurveCount - 1]->EndPoint(p);
272
0
}
273
274
/************************************************************************/
275
/*                               Value()                                */
276
/************************************************************************/
277
278
void OGRCompoundCurve::Value(double dfDistance, OGRPoint *poPoint) const
279
0
{
280
281
0
    if (dfDistance < 0)
282
0
    {
283
0
        StartPoint(poPoint);
284
0
        return;
285
0
    }
286
287
0
    double dfLength = 0.0;
288
0
    for (int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++)
289
0
    {
290
0
        const double dfSegLength = oCC.papoCurves[iGeom]->get_Length();
291
0
        if (dfSegLength > 0)
292
0
        {
293
0
            if ((dfLength <= dfDistance) &&
294
0
                ((dfLength + dfSegLength) >= dfDistance))
295
0
            {
296
0
                oCC.papoCurves[iGeom]->Value(dfDistance - dfLength, poPoint);
297
298
0
                return;
299
0
            }
300
301
0
            dfLength += dfSegLength;
302
0
        }
303
0
    }
304
305
0
    EndPoint(poPoint);
306
0
}
307
308
/************************************************************************/
309
/*                         CurveToLineInternal()                        */
310
/************************************************************************/
311
312
OGRLineString *
313
OGRCompoundCurve::CurveToLineInternal(double dfMaxAngleStepSizeDegrees,
314
                                      const char *const *papszOptions,
315
                                      int bIsLinearRing) const
316
0
{
317
0
    OGRLineString *const poLine =
318
0
        bIsLinearRing ? new OGRLinearRing() : new OGRLineString();
319
0
    poLine->assignSpatialReference(getSpatialReference());
320
0
    for (int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++)
321
0
    {
322
0
        OGRLineString *poSubLS = oCC.papoCurves[iGeom]->CurveToLine(
323
0
            dfMaxAngleStepSizeDegrees, papszOptions);
324
0
        poLine->addSubLineString(poSubLS, (iGeom == 0) ? 0 : 1);
325
0
        delete poSubLS;
326
0
    }
327
0
    return poLine;
328
0
}
329
330
/************************************************************************/
331
/*                          CurveToLine()                               */
332
/************************************************************************/
333
334
OGRLineString *
335
OGRCompoundCurve::CurveToLine(double dfMaxAngleStepSizeDegrees,
336
                              const char *const *papszOptions) const
337
0
{
338
0
    return CurveToLineInternal(dfMaxAngleStepSizeDegrees, papszOptions, FALSE);
339
0
}
340
341
/************************************************************************/
342
/*                               Equals()                                */
343
/************************************************************************/
344
345
OGRBoolean OGRCompoundCurve::Equals(const OGRGeometry *poOther) const
346
0
{
347
0
    if (poOther == this)
348
0
        return TRUE;
349
350
0
    if (poOther->getGeometryType() != getGeometryType())
351
0
        return FALSE;
352
353
0
    return oCC.Equals(&(poOther->toCompoundCurve()->oCC));
354
0
}
355
356
/************************************************************************/
357
/*                       setCoordinateDimension()                       */
358
/************************************************************************/
359
360
bool OGRCompoundCurve::setCoordinateDimension(int nNewDimension)
361
0
{
362
0
    return oCC.setCoordinateDimension(this, nNewDimension);
363
0
}
364
365
bool OGRCompoundCurve::set3D(OGRBoolean bIs3D)
366
0
{
367
0
    return oCC.set3D(this, bIs3D);
368
0
}
369
370
bool OGRCompoundCurve::setMeasured(OGRBoolean bIsMeasured)
371
0
{
372
0
    return oCC.setMeasured(this, bIsMeasured);
373
0
}
374
375
/************************************************************************/
376
/*                       assignSpatialReference()                       */
377
/************************************************************************/
378
379
void OGRCompoundCurve::assignSpatialReference(const OGRSpatialReference *poSR)
380
0
{
381
0
    oCC.assignSpatialReference(this, poSR);
382
0
}
383
384
/************************************************************************/
385
/*                          getNumCurves()                              */
386
/************************************************************************/
387
388
/**
389
 * \brief Return the number of curves.
390
 *
391
 * Note that the number of curves making this compound curve.
392
 *
393
 * Relates to the ISO SQL/MM ST_NumCurves() function.
394
 *
395
 * @return number of curves.
396
 */
397
398
int OGRCompoundCurve::getNumCurves() const
399
0
{
400
0
    return oCC.nCurveCount;
401
0
}
402
403
/************************************************************************/
404
/*                           getCurve()                                 */
405
/************************************************************************/
406
407
/**
408
 * \brief Fetch reference to indicated internal ring.
409
 *
410
 * Note that the returned curve pointer is to an internal data object of the
411
 * OGRCompoundCurve.  It should not be modified or deleted by the application,
412
 * and the pointer is only valid till the polygon is next modified.  Use the
413
 * OGRGeometry::clone() method to make a separate copy within the application.
414
 *
415
 * Relates to the ISO SQL/MM ST_CurveN() function.
416
 *
417
 * @param iRing curve index from 0 to getNumCurves() - 1.
418
 *
419
 * @return pointer to curve.  May be NULL.
420
 */
421
422
OGRCurve *OGRCompoundCurve::getCurve(int iRing)
423
0
{
424
0
    return oCC.getCurve(iRing);
425
0
}
426
427
/************************************************************************/
428
/*                           getCurve()                                 */
429
/************************************************************************/
430
431
/**
432
 * \brief Fetch reference to indicated internal ring.
433
 *
434
 * Note that the returned curve pointer is to an internal data object of the
435
 * OGRCompoundCurve.  It should not be modified or deleted by the application,
436
 * and the pointer is only valid till the polygon is next modified.  Use the
437
 * OGRGeometry::clone() method to make a separate copy within the application.
438
 *
439
 * Relates to the ISO SQL/MM ST_CurveN() function.
440
 *
441
 * @param iCurve curve index from 0 to getNumCurves() - 1.
442
 *
443
 * @return pointer to curve.  May be NULL.
444
 */
445
446
const OGRCurve *OGRCompoundCurve::getCurve(int iCurve) const
447
0
{
448
0
    return oCC.getCurve(iCurve);
449
0
}
450
451
/************************************************************************/
452
/*                           stealCurve()                               */
453
/************************************************************************/
454
455
/**
456
 * \brief "Steal" reference to curve.
457
 *
458
 * @param iCurve curve index from 0 to getNumCurves() - 1.
459
 *
460
 * @return pointer to curve.  May be NULL.
461
 */
462
463
OGRCurve *OGRCompoundCurve::stealCurve(int iCurve)
464
0
{
465
0
    return oCC.stealCurve(iCurve);
466
0
}
467
468
/************************************************************************/
469
/*                            addCurve()                                */
470
/************************************************************************/
471
472
/**
473
 * \brief Add a curve to the container.
474
 *
475
 * The passed geometry is cloned to make an internal copy.
476
 *
477
 * There is no ISO SQL/MM analog to this method.
478
 *
479
 * This method is the same as the C function OGR_G_AddGeometry().
480
 *
481
 * @param poCurve geometry to add to the container.
482
 * @param dfToleranceEps relative tolerance when checking that the first point
483
 * of a segment matches then end point of the previous one. Default value:
484
 * OGRCompoundCurve::DEFAULT_TOLERANCE_EPSILON.
485
 *
486
 * @return OGRERR_NONE if successful, or OGRERR_FAILURE in case of error
487
 * (for example if curves are not contiguous)
488
 */
489
490
OGRErr OGRCompoundCurve::addCurve(const OGRCurve *poCurve,
491
                                  double dfToleranceEps)
492
0
{
493
0
    OGRCurve *poClonedCurve = poCurve->clone();
494
0
    const OGRErr eErr = addCurveDirectly(poClonedCurve, dfToleranceEps);
495
0
    if (eErr != OGRERR_NONE)
496
0
        delete poClonedCurve;
497
0
    return eErr;
498
0
}
499
500
/************************************************************************/
501
/*                          addCurveDirectly()                          */
502
/************************************************************************/
503
504
/**
505
 * \brief Add a curve directly to the container.
506
 *
507
 * Ownership of the passed geometry is taken by the container rather than
508
 * cloning as addCurve() does, but only if the method is successful.
509
 * If the method fails, ownership still belongs to the caller.
510
 *
511
 * There is no ISO SQL/MM analog to this method.
512
 *
513
 * This method is the same as the C function OGR_G_AddGeometryDirectly().
514
 *
515
 * @param poCurve geometry to add to the container.
516
 * @param dfToleranceEps relative tolerance when checking that the first point
517
 * of a segment matches then end point of the previous one. Default value:
518
 * OGRCompoundCurve::DEFAULT_TOLERANCE_EPSILON.
519
 *
520
 * @return OGRERR_NONE if successful, or OGRERR_FAILURE in case of error
521
 * (for example if curves are not contiguous)
522
 */
523
OGRErr OGRCompoundCurve::addCurveDirectly(OGRCurve *poCurve,
524
                                          double dfToleranceEps)
525
0
{
526
0
    return addCurveDirectlyInternal(poCurve, dfToleranceEps, TRUE);
527
0
}
528
529
OGRErr OGRCompoundCurve::addCurveDirectlyInternal(OGRCurve *poCurve,
530
                                                  double dfToleranceEps,
531
                                                  int bNeedRealloc)
532
0
{
533
0
    if (poCurve->getNumPoints() == 1)
534
0
    {
535
0
        CPLError(CE_Failure, CPLE_AppDefined,
536
0
                 "Invalid curve: not enough points");
537
0
        return OGRERR_FAILURE;
538
0
    }
539
540
0
    const OGRwkbGeometryType eCurveType =
541
0
        wkbFlatten(poCurve->getGeometryType());
542
0
    if (EQUAL(poCurve->getGeometryName(), "LINEARRING"))
543
0
    {
544
0
        CPLError(CE_Failure, CPLE_AppDefined, "Linearring not allowed.");
545
0
        return OGRERR_FAILURE;
546
0
    }
547
0
    else if (eCurveType == wkbCompoundCurve)
548
0
    {
549
0
        CPLError(CE_Failure, CPLE_AppDefined,
550
0
                 "Cannot add a compound curve inside a compound curve");
551
0
        return OGRERR_FAILURE;
552
0
    }
553
554
0
    if (oCC.nCurveCount > 0)
555
0
    {
556
0
        if (oCC.papoCurves[oCC.nCurveCount - 1]->IsEmpty() ||
557
0
            poCurve->IsEmpty())
558
0
        {
559
0
            CPLError(CE_Failure, CPLE_AppDefined, "Non contiguous curves");
560
0
            return OGRERR_FAILURE;
561
0
        }
562
563
0
        OGRPoint oEnd;
564
0
        OGRPoint start;
565
0
        oCC.papoCurves[oCC.nCurveCount - 1]->EndPoint(&oEnd);
566
0
        poCurve->StartPoint(&start);
567
0
        if (fabs(oEnd.getX() - start.getX()) >
568
0
                dfToleranceEps * fabs(start.getX()) ||
569
0
            fabs(oEnd.getY() - start.getY()) >
570
0
                dfToleranceEps * fabs(start.getY()) ||
571
0
            fabs(oEnd.getZ() - start.getZ()) >
572
0
                dfToleranceEps * fabs(start.getZ()))
573
0
        {
574
0
            poCurve->EndPoint(&start);
575
0
            if (fabs(oEnd.getX() - start.getX()) >
576
0
                    dfToleranceEps * fabs(start.getX()) ||
577
0
                fabs(oEnd.getY() - start.getY()) >
578
0
                    dfToleranceEps * fabs(start.getY()) ||
579
0
                fabs(oEnd.getZ() - start.getZ()) >
580
0
                    dfToleranceEps * fabs(start.getZ()))
581
0
            {
582
0
                CPLError(CE_Failure, CPLE_AppDefined, "Non contiguous curves");
583
0
                return OGRERR_FAILURE;
584
0
            }
585
586
0
            CPLDebug("GML", "reversing curve");
587
0
            poCurve->toSimpleCurve()->reversePoints();
588
0
        }
589
        // Patch so that it matches exactly.
590
0
        poCurve->toSimpleCurve()->setPoint(0, &oEnd);
591
0
    }
592
593
0
    return oCC.addCurveDirectly(this, poCurve, bNeedRealloc);
594
0
}
595
596
/************************************************************************/
597
/*                          addCurve()                                  */
598
/************************************************************************/
599
600
/**
601
 * \brief Add a curve directly to the container.
602
 *
603
 * There is no ISO SQL/MM analog to this method.
604
 *
605
 * This method is the same as the C function OGR_G_AddGeometryDirectly().
606
 *
607
 * @param poCurve geometry to add to the container.
608
 * @param dfToleranceEps relative tolerance when checking that the first point
609
 * of a segment matches then end point of the previous one. Default value:
610
 * OGRCompoundCurve::DEFAULT_TOLERANCE_EPSILON.
611
 *
612
 * @return OGRERR_NONE if successful, or OGRERR_FAILURE in case of error
613
 * (for example if curves are not contiguous)
614
 */
615
OGRErr OGRCompoundCurve::addCurve(std::unique_ptr<OGRCurve> poCurve,
616
                                  double dfToleranceEps)
617
0
{
618
0
    OGRCurve *poCurvePtr = poCurve.release();
619
0
    OGRErr eErr = addCurveDirectlyInternal(poCurvePtr, dfToleranceEps, TRUE);
620
0
    if (eErr != OGRERR_NONE)
621
0
        delete poCurvePtr;
622
0
    return eErr;
623
0
}
624
625
/************************************************************************/
626
/*                             transform()                              */
627
/************************************************************************/
628
629
OGRErr OGRCompoundCurve::transform(OGRCoordinateTransformation *poCT)
630
0
{
631
0
    return oCC.transform(this, poCT);
632
0
}
633
634
/************************************************************************/
635
/*                            flattenTo2D()                             */
636
/************************************************************************/
637
638
void OGRCompoundCurve::flattenTo2D()
639
0
{
640
0
    oCC.flattenTo2D(this);
641
0
}
642
643
/************************************************************************/
644
/*                              segmentize()                            */
645
/************************************************************************/
646
647
bool OGRCompoundCurve::segmentize(double dfMaxLength)
648
0
{
649
0
    return oCC.segmentize(dfMaxLength);
650
0
}
651
652
/************************************************************************/
653
/*                               swapXY()                               */
654
/************************************************************************/
655
656
void OGRCompoundCurve::swapXY()
657
0
{
658
0
    oCC.swapXY();
659
0
}
660
661
/************************************************************************/
662
/*                         hasCurveGeometry()                           */
663
/************************************************************************/
664
665
OGRBoolean OGRCompoundCurve::hasCurveGeometry(int bLookForNonLinear) const
666
0
{
667
0
    if (bLookForNonLinear)
668
0
    {
669
0
        return oCC.hasCurveGeometry(bLookForNonLinear);
670
0
    }
671
672
0
    return TRUE;
673
0
}
674
675
/************************************************************************/
676
/*                         getLinearGeometry()                        */
677
/************************************************************************/
678
679
OGRGeometry *
680
OGRCompoundCurve::getLinearGeometry(double dfMaxAngleStepSizeDegrees,
681
                                    const char *const *papszOptions) const
682
0
{
683
0
    return CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions);
684
0
}
685
686
/************************************************************************/
687
/*                           getNumPoints()                             */
688
/************************************************************************/
689
690
int OGRCompoundCurve::getNumPoints() const
691
0
{
692
0
    int nPoints = 0;
693
0
    for (int i = 0; i < oCC.nCurveCount; i++)
694
0
    {
695
0
        nPoints += oCC.papoCurves[i]->getNumPoints();
696
0
        if (i != 0)
697
0
            nPoints--;
698
0
    }
699
0
    return nPoints;
700
0
}
701
702
/************************************************************************/
703
/*                      OGRCompoundCurvePointIterator                   */
704
/************************************************************************/
705
706
class OGRCompoundCurvePointIterator final : public OGRPointIterator
707
{
708
    CPL_DISALLOW_COPY_ASSIGN(OGRCompoundCurvePointIterator)
709
710
    const OGRCompoundCurve *poCC = nullptr;
711
    int iCurCurve = 0;
712
    OGRPointIterator *poCurveIter = nullptr;
713
714
  public:
715
    explicit OGRCompoundCurvePointIterator(const OGRCompoundCurve *poCCIn)
716
0
        : poCC(poCCIn)
717
0
    {
718
0
    }
719
720
    ~OGRCompoundCurvePointIterator() override
721
0
    {
722
0
        delete poCurveIter;
723
0
    }
724
725
    OGRBoolean getNextPoint(OGRPoint *p) override;
726
};
727
728
/************************************************************************/
729
/*                            getNextPoint()                            */
730
/************************************************************************/
731
732
OGRBoolean OGRCompoundCurvePointIterator::getNextPoint(OGRPoint *p)
733
0
{
734
0
    if (iCurCurve == poCC->getNumCurves())
735
0
        return FALSE;
736
0
    if (poCurveIter == nullptr)
737
0
        poCurveIter = poCC->getCurve(0)->getPointIterator();
738
0
    if (!poCurveIter->getNextPoint(p))
739
0
    {
740
0
        iCurCurve++;
741
0
        if (iCurCurve == poCC->getNumCurves())
742
0
            return FALSE;
743
0
        delete poCurveIter;
744
0
        poCurveIter = poCC->getCurve(iCurCurve)->getPointIterator();
745
        // Skip first point.
746
0
        return poCurveIter->getNextPoint(p) && poCurveIter->getNextPoint(p);
747
0
    }
748
0
    return TRUE;
749
0
}
750
751
/************************************************************************/
752
/*                         getPointIterator()                           */
753
/************************************************************************/
754
755
OGRPointIterator *OGRCompoundCurve::getPointIterator() const
756
0
{
757
0
    return new OGRCompoundCurvePointIterator(this);
758
0
}
759
760
/************************************************************************/
761
/*                         CastToLineString()                        */
762
/************************************************************************/
763
764
//! @cond Doxygen_Suppress
765
OGRLineString *OGRCompoundCurve::CastToLineString(OGRCompoundCurve *poCC)
766
0
{
767
0
    for (int i = 0; i < poCC->oCC.nCurveCount; i++)
768
0
    {
769
0
        poCC->oCC.papoCurves[i] =
770
0
            OGRCurve::CastToLineString(poCC->oCC.papoCurves[i]);
771
0
        if (poCC->oCC.papoCurves[i] == nullptr)
772
0
        {
773
0
            delete poCC;
774
0
            return nullptr;
775
0
        }
776
0
    }
777
778
0
    if (poCC->oCC.nCurveCount == 1)
779
0
    {
780
0
        OGRLineString *poLS = poCC->oCC.papoCurves[0]->toLineString();
781
0
        poLS->assignSpatialReference(poCC->getSpatialReference());
782
0
        poCC->oCC.papoCurves[0] = nullptr;
783
0
        delete poCC;
784
0
        return poLS;
785
0
    }
786
787
0
    OGRLineString *poLS = poCC->CurveToLineInternal(0, nullptr, FALSE);
788
0
    delete poCC;
789
0
    return poLS;
790
0
}
791
792
/************************************************************************/
793
/*                           CastToLinearRing()                         */
794
/************************************************************************/
795
796
/**
797
 * \brief Cast to linear ring.
798
 *
799
 * The passed in geometry is consumed and a new one returned (or NULL in case
800
 * of failure)
801
 *
802
 * @param poCC the input geometry - ownership is passed to the method.
803
 * @return new geometry.
804
 */
805
806
OGRLinearRing *OGRCompoundCurve::CastToLinearRing(OGRCompoundCurve *poCC)
807
0
{
808
0
    for (int i = 0; i < poCC->oCC.nCurveCount; i++)
809
0
    {
810
0
        poCC->oCC.papoCurves[i] =
811
0
            OGRCurve::CastToLineString(poCC->oCC.papoCurves[i]);
812
0
        if (poCC->oCC.papoCurves[i] == nullptr)
813
0
        {
814
0
            delete poCC;
815
0
            return nullptr;
816
0
        }
817
0
    }
818
819
0
    if (poCC->oCC.nCurveCount == 1)
820
0
    {
821
0
        OGRLinearRing *poLR =
822
0
            OGRCurve::CastToLinearRing(poCC->oCC.papoCurves[0]);
823
0
        if (poLR != nullptr)
824
0
        {
825
0
            poLR->assignSpatialReference(poCC->getSpatialReference());
826
0
        }
827
0
        poCC->oCC.papoCurves[0] = nullptr;
828
0
        delete poCC;
829
0
        return poLR;
830
0
    }
831
832
0
    OGRLinearRing *poLR =
833
0
        poCC->CurveToLineInternal(0, nullptr, TRUE)->toLinearRing();
834
0
    delete poCC;
835
0
    return poLR;
836
0
}
837
838
/************************************************************************/
839
/*                     GetCasterToLineString()                          */
840
/************************************************************************/
841
842
OGRLineString *OGRCompoundCurve::CasterToLineString(OGRCurve *poCurve)
843
0
{
844
0
    OGRCompoundCurve *poCC = poCurve->toCompoundCurve();
845
0
    return OGRCompoundCurve::CastToLineString(poCC);
846
0
}
847
848
OGRCurveCasterToLineString OGRCompoundCurve::GetCasterToLineString() const
849
0
{
850
0
    return OGRCompoundCurve::CasterToLineString;
851
0
}
852
853
/************************************************************************/
854
/*                        GetCasterToLinearRing()                       */
855
/************************************************************************/
856
857
OGRLinearRing *OGRCompoundCurve::CasterToLinearRing(OGRCurve *poCurve)
858
0
{
859
0
    OGRCompoundCurve *poCC = poCurve->toCompoundCurve();
860
0
    return OGRCompoundCurve::CastToLinearRing(poCC);
861
0
}
862
863
OGRCurveCasterToLinearRing OGRCompoundCurve::GetCasterToLinearRing() const
864
0
{
865
0
    return OGRCompoundCurve::CasterToLinearRing;
866
0
}
867
868
//! @endcond
869
870
/************************************************************************/
871
/*                           get_Area()                                 */
872
/************************************************************************/
873
874
double OGRCompoundCurve::get_Area() const
875
0
{
876
0
    if (IsEmpty() || !get_IsClosed())
877
0
        return 0;
878
879
    // Optimization for convex rings.
880
0
    if (IsConvex())
881
0
    {
882
        // Compute area of shape without the circular segments.
883
0
        OGRPointIterator *poIter = getPointIterator();
884
0
        OGRLineString oLS;
885
0
        oLS.setNumPoints(getNumPoints());
886
0
        OGRPoint p;
887
0
        for (int i = 0; poIter->getNextPoint(&p); i++)
888
0
        {
889
0
            oLS.setPoint(i, p.getX(), p.getY());
890
0
        }
891
0
        double dfArea = oLS.get_Area();
892
0
        delete poIter;
893
894
        // Add the area of the spherical segments.
895
0
        dfArea += get_AreaOfCurveSegments();
896
897
0
        return dfArea;
898
0
    }
899
900
0
    OGRLineString *poLS = CurveToLine();
901
0
    double dfArea = poLS->get_Area();
902
0
    delete poLS;
903
904
0
    return dfArea;
905
0
}
906
907
/************************************************************************/
908
/*                        get_GeodesicArea()                            */
909
/************************************************************************/
910
911
double OGRCompoundCurve::get_GeodesicArea(
912
    const OGRSpatialReference *poSRSOverride) const
913
0
{
914
0
    if (IsEmpty())
915
0
        return 0;
916
917
0
    if (!get_IsClosed())
918
0
    {
919
0
        CPLError(CE_Failure, CPLE_AppDefined, "Non-closed geometry");
920
0
        return -1;
921
0
    }
922
923
0
    if (!poSRSOverride)
924
0
        poSRSOverride = getSpatialReference();
925
926
0
    auto poLS = std::unique_ptr<OGRLineString>(CurveToLine());
927
0
    return poLS->get_GeodesicArea(poSRSOverride);
928
0
}
929
930
/************************************************************************/
931
/*                        get_GeodesicLength()                          */
932
/************************************************************************/
933
934
double OGRCompoundCurve::get_GeodesicLength(
935
    const OGRSpatialReference *poSRSOverride) const
936
0
{
937
0
    if (IsEmpty())
938
0
        return 0;
939
940
0
    if (!poSRSOverride)
941
0
        poSRSOverride = getSpatialReference();
942
943
0
    auto poLS = std::unique_ptr<OGRLineString>(CurveToLine());
944
0
    return poLS->get_GeodesicLength(poSRSOverride);
945
0
}
946
947
/************************************************************************/
948
/*                       get_AreaOfCurveSegments()                      */
949
/************************************************************************/
950
951
/** Return area of curve segments
952
 * @return area.
953
 */
954
double OGRCompoundCurve::get_AreaOfCurveSegments() const
955
0
{
956
0
    double dfArea = 0;
957
0
    for (int i = 0; i < getNumCurves(); i++)
958
0
    {
959
0
        const OGRCurve *poPart = getCurve(i);
960
0
        dfArea += poPart->get_AreaOfCurveSegments();
961
0
    }
962
0
    return dfArea;
963
0
}
964
965
/************************************************************************/
966
/*                           hasEmptyParts()                            */
967
/************************************************************************/
968
969
bool OGRCompoundCurve::hasEmptyParts() const
970
0
{
971
0
    return oCC.hasEmptyParts();
972
0
}
973
974
/************************************************************************/
975
/*                          removeEmptyParts()                          */
976
/************************************************************************/
977
978
void OGRCompoundCurve::removeEmptyParts()
979
0
{
980
0
    oCC.removeEmptyParts();
981
0
}
982
983
/************************************************************************/
984
/*                           reversePoints()                            */
985
/************************************************************************/
986
987
/**
988
 * \brief Reverse point order.
989
 *
990
 * This method updates the points in this curve in place
991
 * reversing the point ordering (first for last, etc) and component ordering.
992
 *
993
 * @since 3.10
994
 */
995
void OGRCompoundCurve::reversePoints()
996
997
0
{
998
0
    oCC.reversePoints();
999
0
}