Coverage Report

Created: 2025-11-16 06:25

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