Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrcurve.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRCurve geometry class.
5
 * Author:   Frank Warmerdam, warmerda@home.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogr_geometry.h"
14
#include "ogr_p.h"
15
16
//! @cond Doxygen_Suppress
17
18
/************************************************************************/
19
/*                     operator=( const OGRCurve& )                     */
20
/************************************************************************/
21
22
OGRCurve &OGRCurve::operator=(const OGRCurve &other)
23
0
{
24
0
    if (this != &other)
25
0
    {
26
0
        OGRGeometry::operator=(other);
27
0
    }
28
0
    return *this;
29
0
}
30
31
//! @endcond
32
33
/************************************************************************/
34
/*                            getDimension()                            */
35
/************************************************************************/
36
37
int OGRCurve::getDimension() const
38
39
0
{
40
0
    return 1;
41
0
}
42
43
/************************************************************************/
44
/*                            get_IsClosed()                            */
45
/************************************************************************/
46
47
/**
48
 * \brief Return TRUE if curve is closed.
49
 *
50
 * Tests if a curve is closed. A curve is closed if its start point is
51
 * equal to its end point.
52
 *
53
 * For equality tests, the M dimension is ignored.
54
 *
55
 * This method relates to the SFCOM ICurve::get_IsClosed() method.
56
 *
57
 * @return TRUE if closed, else FALSE.
58
 */
59
60
bool OGRCurve::get_IsClosed() const
61
62
2.01M
{
63
2.01M
    OGRPoint oStartPoint;
64
2.01M
    StartPoint(&oStartPoint);
65
66
2.01M
    OGRPoint oEndPoint;
67
2.01M
    EndPoint(&oEndPoint);
68
69
2.01M
    if (oStartPoint.Is3D() && oEndPoint.Is3D())
70
676k
    {
71
        // XYZ type
72
676k
        return (oStartPoint.getX() == oEndPoint.getX() &&
73
619k
                oStartPoint.getY() == oEndPoint.getY() &&
74
579k
                oStartPoint.getZ() == oEndPoint.getZ());
75
676k
    }
76
77
    // one of the points is 3D
78
1.33M
    else if (oStartPoint.Is3D() != oEndPoint.Is3D())
79
0
    {
80
0
        return false;
81
0
    }
82
83
1.33M
    else
84
1.33M
    {
85
        // XY type
86
1.33M
        return (oStartPoint.getX() == oEndPoint.getX() &&
87
1.05M
                oStartPoint.getY() == oEndPoint.getY());
88
1.33M
    }
89
2.01M
}
90
91
/**
92
 * \fn double OGRCurve::get_Length() const;
93
 *
94
 * \brief Returns the length of the curve.
95
 *
96
 * This method relates to the SFCOM ICurve::get_Length() method.
97
 *
98
 * @return the length of the curve, zero if the curve hasn't been
99
 * initialized.
100
 *
101
 * @see get_GeodesicLength() for an alternative method returning lengths
102
 * computed on the ellipsoid, and in meters.
103
 */
104
105
/**
106
 * \fn double OGRCurve::get_GeodesicLength(const OGRSpatialReference* poSRSOverride = nullptr) const;
107
 *
108
 * \brief Get the length of the curve, considered as a geodesic line on the
109
 * underlying ellipsoid of the SRS attached to the geometry.
110
 *
111
 * The returned length will always be in meters.
112
 *
113
 * <a href="https://geographiclib.sourceforge.io/html/python/geodesics.html">Geodesics</a>
114
 * follow the shortest route on the surface of the ellipsoid.
115
 *
116
 * If the geometry' SRS is not a geographic one, geometries are reprojected to
117
 * the underlying geographic SRS of the geometry' SRS.
118
 * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
119
 *
120
 * Note that geometries with circular arcs will be linearized in their original
121
 * coordinate space first, so the resulting geodesic length will be an
122
 * approximation.
123
 *
124
 * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
125
 * @return the length of the geometry in meters, or a negative value in case
126
 * of error.
127
 *
128
 * @see get_Length() for an alternative method returning areas computed in
129
 * 2D Cartesian space.
130
 *
131
 * @since GDAL 3.10
132
 */
133
134
/**
135
 * \fn void OGRCurve::StartPoint( OGRPoint * poPoint ) const;
136
 *
137
 * \brief Return the curve start point.
138
 *
139
 * This method relates to the SF COM ICurve::get_StartPoint() method.
140
 *
141
 * @param poPoint the point to be assigned the start location.
142
 */
143
144
/**
145
 * \fn void OGRCurve::EndPoint( OGRPoint * poPoint ) const;
146
 *
147
 * \brief Return the curve end point.
148
 *
149
 * This method relates to the SF COM ICurve::get_EndPoint() method.
150
 *
151
 * @param poPoint the point to be assigned the end location.
152
 */
153
154
/**
155
 * \fn void OGRCurve::Value( double dfDistance, OGRPoint * poPoint ) const;
156
 *
157
 * \brief Fetch point at given distance along curve.
158
 *
159
 * This method relates to the SF COM ICurve::get_Value() method.
160
 *
161
 * This function is the same as the C function OGR_G_Value().
162
 *
163
 * @param dfDistance distance along the curve at which to sample position.
164
 *                   This distance should be between zero and get_Length()
165
 *                   for this curve.
166
 * @param poPoint the point to be assigned the curve position.
167
 */
168
169
/**
170
 * \fn OGRLineString* OGRCurve::CurveToLine( double dfMaxAngleStepSizeDegrees,
171
 *     const char* const* papszOptions ) const;
172
 *
173
 * \brief Return a linestring from a curve geometry.
174
 *
175
 * The returned geometry is a new instance whose ownership belongs to the
176
 * caller.
177
 *
178
 * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be
179
 * used.  This is currently 4 degrees unless the user has overridden the
180
 * value with the OGR_ARC_STEPSIZE configuration variable.
181
 *
182
 * This method relates to the ISO SQL/MM Part 3 ICurve::CurveToLine() method.
183
 *
184
 * This function is the same as C function OGR_G_CurveToLine().
185
 *
186
 * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
187
 * arc, zero to use the default setting.
188
 * @param papszOptions options as a null-terminated list of strings or NULL.
189
 *                     See OGRGeometryFactory::curveToLineString() for valid
190
 *                     options.
191
 *
192
 * @return a line string approximating the curve
193
 *
194
 */
195
196
/**
197
 * \fn int OGRCurve::getNumPoints() const;
198
 *
199
 * \brief Return the number of points of a curve geometry.
200
 *
201
 *
202
 * This method, as a method of OGRCurve, does not relate to a standard.
203
 * For circular strings or linestrings, it returns the number of points,
204
 * conforming to SF COM NumPoints().
205
 * For compound curves, it returns the sum of the number of points of each
206
 * of its components (non including intermediate starting/ending points of
207
 * the different parts).
208
 *
209
 * @return the number of points of the curve.
210
 *
211
 */
212
213
/**
214
 * \fn OGRPointIterator* OGRCurve::getPointIterator() const;
215
 *
216
 * \brief Returns a point iterator over the curve.
217
 *
218
 * The curve must not be modified while an iterator exists on it.
219
 *
220
 * The iterator must be destroyed with OGRPointIterator::destroy().
221
 *
222
 * @return a point iterator over the curve.
223
 *
224
 */
225
226
/**
227
 * \fn double OGRCurve::get_Area() const;
228
 *
229
 * \brief Get the area of the (closed) curve.
230
 *
231
 * This method is designed to be used by OGRCurvePolygon::get_Area().
232
 *
233
 * @return the area of the geometry in square units of the spatial reference
234
 * system in use.
235
 *
236
 * @see get_GeodesicArea() for an alternative method returning areas
237
 * computed on the ellipsoid, and in square meters.
238
 *
239
 */
240
241
/**
242
 * \fn double OGRCurve::get_GeodesicArea(const OGRSpatialReference* poSRSOverride = nullptr) const;
243
 *
244
 * \brief Get the area of the (closed) curve, considered as a surface on the
245
 * underlying ellipsoid of the SRS attached to the geometry.
246
 *
247
 * This method is designed to be used by OGRCurvePolygon::get_GeodesicArea().
248
 *
249
 * The returned area will always be in square meters, and assumes that
250
 * polygon edges describe geodesic lines on the ellipsoid.
251
 *
252
 * <a href="https://geographiclib.sourceforge.io/html/python/geodesics.html">Geodesics</a>
253
 * follow the shortest route on the surface of the ellipsoid.
254
 *
255
 * If the geometry' SRS is not a geographic one, geometries are reprojected to
256
 * the underlying geographic SRS of the geometry' SRS.
257
 * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
258
 *
259
 * Note that geometries with circular arcs will be linearized in their original
260
 * coordinate space first, so the resulting geodesic area will be an
261
 * approximation.
262
 *
263
 * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
264
 * @return the area of the geometry in square meters, or a negative value in case
265
 * of error.
266
 *
267
 * @see get_Area() for an alternative method returning areas computed in
268
 * 2D Cartesian space.
269
 *
270
 * @since GDAL 3.9
271
 */
272
273
/**
274
 * \fn double OGRCurve::get_AreaOfCurveSegments() const;
275
 *
276
 * \brief Get the area of the purely curve portions of a (closed) curve.
277
 *
278
 * This method is designed to be used on a closed convex curve.
279
 *
280
 * @return the area of the feature in square units of the spatial reference
281
 * system in use.
282
 *
283
 */
284
285
/************************************************************************/
286
/*                              IsConvex()                              */
287
/************************************************************************/
288
289
/**
290
 * \brief Returns if a (closed) curve forms a convex shape.
291
 *
292
 * @return TRUE if the curve forms a convex shape.
293
 *
294
 */
295
296
bool OGRCurve::IsConvex() const
297
11.8k
{
298
11.8k
    bool bRet = true;
299
11.8k
    OGRPointIterator *poPointIter = getPointIterator();
300
11.8k
    OGRPoint p1;
301
11.8k
    OGRPoint p2;
302
11.8k
    if (poPointIter->getNextPoint(&p1) && poPointIter->getNextPoint(&p2))
303
11.8k
    {
304
11.8k
        OGRPoint p3;
305
54.4k
        while (poPointIter->getNextPoint(&p3))
306
44.3k
        {
307
44.3k
            const double crossproduct =
308
44.3k
                (p2.getX() - p1.getX()) * (p3.getY() - p2.getY()) -
309
44.3k
                (p2.getY() - p1.getY()) * (p3.getX() - p2.getX());
310
44.3k
            if (crossproduct > 0)
311
1.75k
            {
312
1.75k
                bRet = false;
313
1.75k
                break;
314
1.75k
            }
315
42.5k
            p1.setX(p2.getX());
316
42.5k
            p1.setY(p2.getY());
317
42.5k
            p2.setX(p3.getX());
318
42.5k
            p2.setY(p3.getY());
319
42.5k
        }
320
11.8k
    }
321
11.8k
    delete poPointIter;
322
11.8k
    return bRet;
323
11.8k
}
324
325
/************************************************************************/
326
/*                        CastToCompoundCurve()                         */
327
/************************************************************************/
328
329
/**
330
 * \brief Cast to compound curve
331
 *
332
 * The passed in geometry is consumed and a new one returned (or NULL in case
333
 * of failure)
334
 *
335
 * @param poCurve the input geometry - ownership is passed to the method.
336
 * @return new geometry
337
 *
338
 */
339
340
OGRCompoundCurve *OGRCurve::CastToCompoundCurve(OGRCurve *poCurve)
341
0
{
342
0
    OGRCompoundCurve *poCC = new OGRCompoundCurve();
343
0
    if (wkbFlatten(poCurve->getGeometryType()) == wkbLineString)
344
0
        poCurve = CastToLineString(poCurve);
345
0
    if (!poCurve->IsEmpty() && poCC->addCurveDirectly(poCurve) != OGRERR_NONE)
346
0
    {
347
0
        delete poCC;
348
0
        delete poCurve;
349
0
        return nullptr;
350
0
    }
351
0
    poCC->assignSpatialReference(poCurve->getSpatialReference());
352
0
    return poCC;
353
0
}
354
355
/************************************************************************/
356
/*                          CastToLineString()                          */
357
/************************************************************************/
358
359
/**
360
 * \brief Cast to linestring
361
 *
362
 * The passed in geometry is consumed and a new one returned (or NULL in case
363
 * of failure)
364
 *
365
 * @param poCurve the input geometry - ownership is passed to the method.
366
 * @return new geometry.
367
 *
368
 */
369
370
OGRLineString *OGRCurve::CastToLineString(OGRCurve *poCurve)
371
11.5k
{
372
11.5k
    OGRCurveCasterToLineString pfn = poCurve->GetCasterToLineString();
373
11.5k
    return pfn(poCurve);
374
11.5k
}
375
376
/************************************************************************/
377
/*                          CastToLinearRing()                          */
378
/************************************************************************/
379
380
/**
381
 * \brief Cast to linear ring
382
 *
383
 * The passed in geometry is consumed and a new one returned (or NULL in case
384
 * of failure)
385
 *
386
 * @param poCurve the input geometry - ownership is passed to the method.
387
 * @return new geometry.
388
 *
389
 */
390
391
OGRLinearRing *OGRCurve::CastToLinearRing(OGRCurve *poCurve)
392
56.3k
{
393
56.3k
    OGRCurveCasterToLinearRing pfn = poCurve->GetCasterToLinearRing();
394
56.3k
    return pfn(poCurve);
395
56.3k
}
396
397
/************************************************************************/
398
/*                           ContainsPoint()                            */
399
/************************************************************************/
400
401
/**
402
 * \brief Returns if a point is contained in a (closed) curve.
403
 *
404
 * Final users should use OGRGeometry::Contains() instead.
405
 *
406
 * @param p the point to test
407
 * @return TRUE if it is inside the curve, FALSE otherwise or -1 if unknown.
408
 *
409
 */
410
411
int OGRCurve::ContainsPoint(CPL_UNUSED const OGRPoint *p) const
412
0
{
413
0
    return -1;
414
0
}
415
416
/************************************************************************/
417
/*                          IntersectsPoint()                           */
418
/************************************************************************/
419
420
/**
421
 * \brief Returns if a point intersects a (closed) curve.
422
 *
423
 * Final users should use OGRGeometry::Intersects() instead.
424
 *
425
 * @param p the point to test
426
 * @return TRUE if it intersects the curve, FALSE otherwise or -1 if unknown.
427
 *
428
 */
429
430
int OGRCurve::IntersectsPoint(CPL_UNUSED const OGRPoint *p) const
431
0
{
432
0
    return -1;
433
0
}
434
435
/************************************************************************/
436
/*                         ~OGRPointIterator()                          */
437
/************************************************************************/
438
439
125k
OGRPointIterator::~OGRPointIterator() = default;
440
441
/**
442
 * \fn bool OGRPointIterator::getNextPoint(OGRPoint* p);
443
 *
444
 * \brief Returns the next point followed by the iterator.
445
 *
446
 * @param p point to fill.
447
 *
448
 * @return TRUE in case of success, or FALSE if the end of the curve is reached.
449
 *
450
 */
451
452
/************************************************************************/
453
/*                              destroy()                               */
454
/************************************************************************/
455
456
/**
457
 * \brief Destroys a point iterator.
458
 *
459
 */
460
void OGRPointIterator::destroy(OGRPointIterator *poIter)
461
0
{
462
0
    delete poIter;
463
0
}
464
465
/************************************************************************/
466
/*                       OGRSimpleCurve::Iterator                       */
467
/************************************************************************/
468
469
4.69k
OGRIteratedPoint::~OGRIteratedPoint() = default;
470
471
void OGRIteratedPoint::setX(double xIn)
472
0
{
473
0
    OGRPoint::setX(xIn);
474
0
    m_poCurve->setPoint(m_nPos, xIn, getY());
475
0
}
476
477
void OGRIteratedPoint::setY(double yIn)
478
0
{
479
0
    OGRPoint::setY(yIn);
480
0
    m_poCurve->setPoint(m_nPos, getX(), yIn);
481
0
}
482
483
void OGRIteratedPoint::setZ(double zIn)
484
0
{
485
0
    OGRPoint::setZ(zIn);
486
0
    m_poCurve->setZ(m_nPos, zIn);
487
0
}
488
489
void OGRIteratedPoint::setM(double mIn)
490
0
{
491
0
    OGRPoint::setM(mIn);
492
0
    m_poCurve->setM(m_nPos, mIn);
493
0
}
494
495
struct OGRSimpleCurve::Iterator::Private
496
{
497
    CPL_DISALLOW_COPY_ASSIGN(Private)
498
0
    Private() = default;
499
500
    bool m_bUpdateChecked = true;
501
    OGRIteratedPoint m_oPoint{};
502
};
503
504
void OGRSimpleCurve::Iterator::update()
505
0
{
506
0
    if (!m_poPrivate->m_bUpdateChecked)
507
0
    {
508
0
        OGRPoint oPointBefore;
509
0
        m_poPrivate->m_oPoint.m_poCurve->getPoint(m_poPrivate->m_oPoint.m_nPos,
510
0
                                                  &oPointBefore);
511
0
        if (oPointBefore != m_poPrivate->m_oPoint)
512
0
        {
513
0
            if (m_poPrivate->m_oPoint.Is3D())
514
0
                m_poPrivate->m_oPoint.m_poCurve->set3D(true);
515
0
            if (m_poPrivate->m_oPoint.IsMeasured())
516
0
                m_poPrivate->m_oPoint.m_poCurve->setMeasured(true);
517
0
            m_poPrivate->m_oPoint.m_poCurve->setPoint(
518
0
                m_poPrivate->m_oPoint.m_nPos, &m_poPrivate->m_oPoint);
519
0
        }
520
0
        m_poPrivate->m_bUpdateChecked = true;
521
0
    }
522
0
}
523
524
OGRSimpleCurve::Iterator::Iterator(OGRSimpleCurve *poSelf, int nPos)
525
0
    : m_poPrivate(new Private())
526
0
{
527
0
    m_poPrivate->m_oPoint.m_poCurve = poSelf;
528
0
    m_poPrivate->m_oPoint.m_nPos = nPos;
529
0
}
530
531
OGRSimpleCurve::Iterator::~Iterator()
532
0
{
533
0
    update();
534
0
}
535
536
OGRIteratedPoint &OGRSimpleCurve::Iterator::operator*()
537
0
{
538
0
    update();
539
0
    m_poPrivate->m_oPoint.m_poCurve->getPoint(m_poPrivate->m_oPoint.m_nPos,
540
0
                                              &m_poPrivate->m_oPoint);
541
0
    m_poPrivate->m_bUpdateChecked = false;
542
0
    return m_poPrivate->m_oPoint;
543
0
}
544
545
OGRSimpleCurve::Iterator &OGRSimpleCurve::Iterator::operator++()
546
0
{
547
0
    update();
548
0
    ++m_poPrivate->m_oPoint.m_nPos;
549
0
    return *this;
550
0
}
551
552
bool OGRSimpleCurve::Iterator::operator!=(const Iterator &it) const
553
0
{
554
0
    return m_poPrivate->m_oPoint.m_nPos != it.m_poPrivate->m_oPoint.m_nPos;
555
0
}
556
557
OGRSimpleCurve::Iterator OGRSimpleCurve::begin()
558
0
{
559
0
    return {this, 0};
560
0
}
561
562
OGRSimpleCurve::Iterator OGRSimpleCurve::end()
563
0
{
564
0
    return {this, nPointCount};
565
0
}
566
567
/************************************************************************/
568
/*                    OGRSimpleCurve::ConstIterator                     */
569
/************************************************************************/
570
571
struct OGRSimpleCurve::ConstIterator::Private
572
{
573
    CPL_DISALLOW_COPY_ASSIGN(Private)
574
4.69k
    Private() = default;
575
576
    mutable OGRIteratedPoint m_oPoint{};
577
    const OGRSimpleCurve *m_poSelf = nullptr;
578
    int m_nPos = 0;
579
};
580
581
OGRSimpleCurve::ConstIterator::ConstIterator(const OGRSimpleCurve *poSelf,
582
                                             int nPos)
583
4.69k
    : m_poPrivate(new Private())
584
4.69k
{
585
4.69k
    m_poPrivate->m_poSelf = poSelf;
586
4.69k
    m_poPrivate->m_nPos = nPos;
587
4.69k
}
588
589
4.69k
OGRSimpleCurve::ConstIterator::~ConstIterator() = default;
590
591
const OGRPoint &OGRSimpleCurve::ConstIterator::operator*() const
592
65.8k
{
593
65.8k
    m_poPrivate->m_poSelf->getPoint(m_poPrivate->m_nPos,
594
65.8k
                                    &m_poPrivate->m_oPoint);
595
65.8k
    return m_poPrivate->m_oPoint;
596
65.8k
}
597
598
OGRSimpleCurve::ConstIterator &OGRSimpleCurve::ConstIterator::operator++()
599
65.8k
{
600
65.8k
    ++m_poPrivate->m_nPos;
601
65.8k
    return *this;
602
65.8k
}
603
604
bool OGRSimpleCurve::ConstIterator::operator!=(const ConstIterator &it) const
605
68.1k
{
606
68.1k
    return m_poPrivate->m_nPos != it.m_poPrivate->m_nPos;
607
68.1k
}
608
609
OGRSimpleCurve::ConstIterator OGRSimpleCurve::begin() const
610
2.34k
{
611
2.34k
    return {this, 0};
612
2.34k
}
613
614
OGRSimpleCurve::ConstIterator OGRSimpleCurve::end() const
615
2.34k
{
616
2.34k
    return {this, nPointCount};
617
2.34k
}
618
619
/************************************************************************/
620
/*                       OGRCurve::ConstIterator                        */
621
/************************************************************************/
622
623
struct OGRCurve::ConstIterator::Private
624
{
625
    CPL_DISALLOW_COPY_ASSIGN(Private)
626
21.1k
    Private() = default;
627
    Private(Private &&) = delete;
628
    Private &operator=(Private &&) = default;
629
630
    OGRPoint m_oPoint{};
631
    const OGRCurve *m_poCurve{};
632
    int m_nStep = 0;
633
    std::unique_ptr<OGRPointIterator> m_poIterator{};
634
};
635
636
OGRCurve::ConstIterator::ConstIterator(const OGRCurve *poSelf, bool bStart)
637
21.1k
    : m_poPrivate(new Private())
638
21.1k
{
639
21.1k
    m_poPrivate->m_poCurve = poSelf;
640
21.1k
    if (bStart)
641
21.1k
    {
642
21.1k
        m_poPrivate->m_poIterator.reset(poSelf->getPointIterator());
643
21.1k
        if (!m_poPrivate->m_poIterator->getNextPoint(&m_poPrivate->m_oPoint))
644
0
        {
645
0
            m_poPrivate->m_nStep = -1;
646
0
            m_poPrivate->m_poIterator.reset();
647
0
        }
648
21.1k
    }
649
0
    else
650
0
    {
651
0
        m_poPrivate->m_nStep = -1;
652
0
    }
653
21.1k
}
654
655
OGRCurve::ConstIterator::ConstIterator(ConstIterator &&oOther) noexcept
656
0
    : m_poPrivate(std::move(oOther.m_poPrivate))
657
0
{
658
0
}
659
660
OGRCurve::ConstIterator &
661
OGRCurve::ConstIterator::operator=(ConstIterator &&oOther)
662
9.21k
{
663
9.21k
    m_poPrivate = std::move(oOther.m_poPrivate);
664
9.21k
    return *this;
665
9.21k
}
666
667
21.1k
OGRCurve::ConstIterator::~ConstIterator() = default;
668
669
const OGRPoint &OGRCurve::ConstIterator::operator*() const
670
122k
{
671
122k
    return m_poPrivate->m_oPoint;
672
122k
}
673
674
OGRCurve::ConstIterator &OGRCurve::ConstIterator::operator++()
675
99.1k
{
676
99.1k
    CPLAssert(m_poPrivate->m_nStep >= 0);
677
99.1k
    ++m_poPrivate->m_nStep;
678
99.1k
    if (!m_poPrivate->m_poIterator->getNextPoint(&m_poPrivate->m_oPoint))
679
0
    {
680
0
        m_poPrivate->m_nStep = -1;
681
0
        m_poPrivate->m_poIterator.reset();
682
0
    }
683
99.1k
    return *this;
684
99.1k
}
685
686
bool OGRCurve::ConstIterator::operator!=(const ConstIterator &it) const
687
0
{
688
0
    return m_poPrivate->m_poCurve != it.m_poPrivate->m_poCurve ||
689
0
           m_poPrivate->m_nStep != it.m_poPrivate->m_nStep;
690
0
}
691
692
OGRCurve::ConstIterator OGRCurve::begin() const
693
21.1k
{
694
21.1k
    return {this, true};
695
21.1k
}
696
697
OGRCurve::ConstIterator OGRCurve::end() const
698
0
{
699
0
    return {this, false};
700
0
}
701
702
/************************************************************************/
703
/*                            isClockwise()                             */
704
/************************************************************************/
705
706
/**
707
 * \brief Returns TRUE if the ring has clockwise winding (or less than 2 points)
708
 *
709
 * Assumes that the line is closed.
710
 *
711
 * @return TRUE if clockwise otherwise FALSE.
712
 */
713
714
bool OGRCurve::isClockwise() const
715
716
11.8k
{
717
    // WARNING: keep in sync OGRLineString::isClockwise(),
718
    // OGRCurve::isClockwise() and OGRWKBIsClockwiseRing()
719
720
11.8k
    const int nPointCount = getNumPoints();
721
11.8k
    if (nPointCount < 3)
722
0
        return true;
723
724
11.8k
    bool bUseFallback = false;
725
726
    // Find the lowest rightmost vertex.
727
11.8k
    auto oIter = begin();
728
11.8k
    const OGRPoint oStartPoint = *oIter;
729
11.8k
    OGRPoint oPointBefore = oStartPoint;
730
11.8k
    OGRPoint oPointBeforeSel;
731
11.8k
    OGRPoint oPointSel = oStartPoint;
732
11.8k
    OGRPoint oPointNextSel;
733
11.8k
    bool bNextPointIsNextSel = true;
734
11.8k
    int v = 0;
735
736
62.7k
    for (int i = 1; i < nPointCount - 1; i++)
737
50.8k
    {
738
50.8k
        ++oIter;
739
50.8k
        const OGRPoint oPointCur = *oIter;
740
50.8k
        if (bNextPointIsNextSel)
741
20.2k
        {
742
20.2k
            oPointNextSel = oPointCur;
743
20.2k
            bNextPointIsNextSel = false;
744
20.2k
        }
745
50.8k
        if (oPointCur.getY() < oPointSel.getY() ||
746
44.4k
            (oPointCur.getY() == oPointSel.getY() &&
747
20.1k
             oPointCur.getX() > oPointSel.getX()))
748
8.49k
        {
749
8.49k
            v = i;
750
8.49k
            oPointBeforeSel = oPointBefore;
751
8.49k
            oPointSel = oPointCur;
752
8.49k
            bUseFallback = false;
753
8.49k
            bNextPointIsNextSel = true;
754
8.49k
        }
755
42.3k
        else if (oPointCur.getY() == oPointSel.getY() &&
756
17.9k
                 oPointCur.getX() == oPointSel.getX())
757
13.8k
        {
758
            // Two vertex with same coordinates are the lowest rightmost
759
            // vertex.  Cannot use that point as the pivot (#5342).
760
13.8k
            bUseFallback = true;
761
13.8k
        }
762
50.8k
        oPointBefore = oPointCur;
763
50.8k
    }
764
11.8k
    const OGRPoint oPointN_m2 = *oIter;
765
766
11.8k
    if (bNextPointIsNextSel)
767
150
    {
768
150
        oPointNextSel = oPointN_m2;
769
150
    }
770
771
    // Previous.
772
11.8k
    if (v == 0)
773
5.00k
    {
774
5.00k
        oPointBeforeSel = oPointN_m2;
775
5.00k
    }
776
777
11.8k
    constexpr double EPSILON = 1.0E-5;
778
11.8k
    const auto epsilonEqual = [](double a, double b, double eps)
779
27.2k
    { return ::fabs(a - b) < eps; };
780
781
11.8k
    if (epsilonEqual(oPointBeforeSel.getX(), oPointSel.getX(), EPSILON) &&
782
2.52k
        epsilonEqual(oPointBeforeSel.getY(), oPointSel.getY(), EPSILON))
783
2.45k
    {
784
        // Don't try to be too clever by retrying with a next point.
785
        // This can lead to false results as in the case of #3356.
786
2.45k
        bUseFallback = true;
787
2.45k
    }
788
789
11.8k
    const double dx0 = oPointBeforeSel.getX() - oPointSel.getX();
790
11.8k
    const double dy0 = oPointBeforeSel.getY() - oPointSel.getY();
791
792
    // Following.
793
11.8k
    if (v + 1 >= nPointCount - 1)
794
150
    {
795
150
        oPointNextSel = oStartPoint;
796
150
    }
797
798
11.8k
    if (epsilonEqual(oPointNextSel.getX(), oPointSel.getX(), EPSILON) &&
799
894
        epsilonEqual(oPointNextSel.getY(), oPointSel.getY(), EPSILON))
800
858
    {
801
        // Don't try to be too clever by retrying with a next point.
802
        // This can lead to false results as in the case of #3356.
803
858
        bUseFallback = true;
804
858
    }
805
806
11.8k
    const double dx1 = oPointNextSel.getX() - oPointSel.getX();
807
11.8k
    const double dy1 = oPointNextSel.getY() - oPointSel.getY();
808
809
11.8k
    const double crossproduct = dx1 * dy0 - dx0 * dy1;
810
811
11.8k
    if (!bUseFallback)
812
6.90k
    {
813
6.90k
        if (crossproduct > 0)  // CCW
814
1.35k
            return false;
815
5.55k
        else if (crossproduct < 0)  // CW
816
1.33k
            return true;
817
6.90k
    }
818
819
    // This is a degenerate case: the extent of the polygon is less than EPSILON
820
    // or 2 nearly identical points were found.
821
    // Try with Green Formula as a fallback, but this is not a guarantee
822
    // as we'll probably be affected by numerical instabilities.
823
9.21k
    oIter = begin();
824
9.21k
    oPointBefore = oStartPoint;
825
9.21k
    ++oIter;
826
9.21k
    auto oPointCur = *oIter;
827
9.21k
    double dfSum = oStartPoint.getX() * (oPointCur.getY() - oStartPoint.getY());
828
829
48.3k
    for (int i = 1; i < nPointCount - 1; i++)
830
39.1k
    {
831
39.1k
        ++oIter;
832
39.1k
        const auto &oPointNext = *oIter;
833
39.1k
        dfSum += oPointCur.getX() * (oPointNext.getY() - oPointBefore.getY());
834
39.1k
        oPointBefore = oPointCur;
835
39.1k
        oPointCur = oPointNext;
836
39.1k
    }
837
838
9.21k
    dfSum += oPointCur.getX() * (oStartPoint.getY() - oPointBefore.getY());
839
840
9.21k
    return dfSum < 0;
841
11.8k
}
842
843
/**
844
 * \fn void OGRCurve::reversePoints();
845
 *
846
 * \brief Reverse point order.
847
 *
848
 * This method updates the points in this curve in place
849
 * reversing the point ordering (first for last, etc) and component ordering
850
 * for a compound curve.
851
 *
852
 * @since 3.10
853
 */