Coverage Report

Created: 2025-06-13 06:18

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