Coverage Report

Created: 2025-11-16 06:25

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