Coverage Report

Created: 2026-09-26 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrlinearring.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRLinearRing geometry class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "ogr_geometry.h"
16
17
#include <climits>
18
#include <cmath>
19
#include <cstring>
20
#include <limits>
21
22
#include "cpl_error.h"
23
#include "ogr_core.h"
24
#include "ogr_geometry.h"
25
#include "ogr_p.h"
26
27
/************************************************************************/
28
/*                OGRLinearRing( const OGRLinearRing& )                 */
29
/************************************************************************/
30
31
/**
32
 * \brief Copy constructor.
33
 */
34
35
0
OGRLinearRing::OGRLinearRing(const OGRLinearRing &) = default;
36
37
/************************************************************************/
38
/*                           OGRLinearRing()                            */
39
/************************************************************************/
40
41
/** Constructor
42
 * @param poSrcRing source ring.
43
 */
44
OGRLinearRing::OGRLinearRing(const OGRLinearRing *poSrcRing)
45
46
0
{
47
0
    if (poSrcRing == nullptr)
48
0
    {
49
0
        CPLDebug("OGR",
50
0
                 "OGRLinearRing::OGRLinearRing(OGRLinearRing*poSrcRing) - "
51
0
                 "passed in ring is NULL!");
52
0
        return;
53
0
    }
54
55
0
    setNumPoints(poSrcRing->getNumPoints(), FALSE);
56
57
0
    memcpy(paoPoints, poSrcRing->paoPoints,
58
0
           sizeof(OGRRawPoint) * getNumPoints());
59
60
0
    if (poSrcRing->padfZ)
61
0
    {
62
0
        Make3D();
63
0
        memcpy(padfZ, poSrcRing->padfZ, sizeof(double) * getNumPoints());
64
0
    }
65
0
}
66
67
/************************************************************************/
68
/*                  operator=( const OGRLinearRing& )                   */
69
/************************************************************************/
70
71
/**
72
 * \brief Assignment operator.
73
 */
74
75
OGRLinearRing &OGRLinearRing::operator=(const OGRLinearRing &other)
76
0
{
77
0
    if (this != &other)
78
0
    {
79
0
        OGRLineString::operator=(other);
80
0
    }
81
0
    return *this;
82
0
}
83
84
/************************************************************************/
85
/*                          getGeometryName()                           */
86
/************************************************************************/
87
88
const char *OGRLinearRing::getGeometryName() const
89
90
5.49M
{
91
5.49M
    return "LINEARRING";
92
5.49M
}
93
94
/************************************************************************/
95
/*                              WkbSize()                               */
96
/*                                                                      */
97
/*      Disable this method.                                            */
98
/************************************************************************/
99
100
size_t OGRLinearRing::WkbSize() const
101
102
778k
{
103
778k
    return 0;
104
778k
}
105
106
/************************************************************************/
107
/*                           importFromWkb()                            */
108
/*                                                                      */
109
/*      Disable method for this class.                                  */
110
/************************************************************************/
111
112
OGRErr OGRLinearRing::importFromWkb(const unsigned char * /*pabyData*/,
113
                                    size_t /*nSize*/,
114
                                    OGRwkbVariant /*eWkbVariant*/,
115
                                    size_t & /* nBytesConsumedOut */)
116
117
0
{
118
0
    return OGRERR_UNSUPPORTED_OPERATION;
119
0
}
120
121
/************************************************************************/
122
/*                            exportToWkb()                             */
123
/*                                                                      */
124
/*      Disable method for this class.                                  */
125
/************************************************************************/
126
127
OGRErr OGRLinearRing::exportToWkb(CPL_UNUSED unsigned char *pabyData,
128
                                  CPL_UNUSED const OGRwkbExportOptions *) const
129
130
0
{
131
0
    return OGRERR_UNSUPPORTED_OPERATION;
132
0
}
133
134
/************************************************************************/
135
/*                           _importFromWkb()                           */
136
/*                                                                      */
137
/*      Helper method for OGRPolygon.  NOT A NORMAL importFromWkb()     */
138
/*      method.                                                         */
139
/************************************************************************/
140
141
//! @cond Doxygen_Suppress
142
OGRErr OGRLinearRing::_importFromWkb(OGRwkbByteOrder eByteOrder, int _flags,
143
                                     const unsigned char *pabyData,
144
                                     size_t nBytesAvailable,
145
                                     size_t &nBytesConsumedOut)
146
147
1.60M
{
148
1.60M
    nBytesConsumedOut = 0;
149
1.60M
    if (nBytesAvailable < 4 && nBytesAvailable != static_cast<size_t>(-1))
150
719
        return OGRERR_NOT_ENOUGH_DATA;
151
152
    /* -------------------------------------------------------------------- */
153
    /*      Get the vertex count.                                           */
154
    /* -------------------------------------------------------------------- */
155
1.60M
    int nNewNumPoints = 0;
156
157
1.60M
    memcpy(&nNewNumPoints, pabyData, 4);
158
159
1.60M
    if (OGR_SWAP(eByteOrder))
160
881k
        nNewNumPoints = CPL_SWAP32(nNewNumPoints);
161
162
    // Check if the wkb stream buffer is big enough to store
163
    // fetched number of points.
164
    // 16, 24, or 32 - size of point structure.
165
1.60M
    size_t nPointSize = 0;
166
1.60M
    if ((_flags & OGR_G_3D) && (_flags & OGR_G_MEASURED))
167
93.7k
        nPointSize = 32;
168
1.51M
    else if ((_flags & OGR_G_3D) || (_flags & OGR_G_MEASURED))
169
1.14M
        nPointSize = 24;
170
370k
    else
171
370k
        nPointSize = 16;
172
173
1.60M
    if (nNewNumPoints < 0 ||
174
1.60M
        static_cast<size_t>(nNewNumPoints) >
175
1.60M
            std::numeric_limits<size_t>::max() / nPointSize)
176
1.42k
    {
177
1.42k
        return OGRERR_CORRUPT_DATA;
178
1.42k
    }
179
1.60M
    const size_t nBufferMinSize = nPointSize * nNewNumPoints;
180
1.60M
    if (nBytesAvailable != static_cast<size_t>(-1) &&
181
1.60M
        nBufferMinSize > nBytesAvailable - 4)
182
21.3k
    {
183
21.3k
        CPLError(CE_Failure, CPLE_AppDefined,
184
21.3k
                 "Length of input WKB is too small");
185
21.3k
        return OGRERR_NOT_ENOUGH_DATA;
186
21.3k
    }
187
188
    // (Re)Allocation of paoPoints buffer.
189
1.58M
    setNumPoints(nNewNumPoints, FALSE);
190
191
1.58M
    if (_flags & OGR_G_3D)
192
1.18M
        Make3D();
193
400k
    else
194
400k
        Make2D();
195
196
1.58M
    if (_flags & OGR_G_MEASURED)
197
136k
        AddM();
198
1.45M
    else
199
1.45M
        RemoveM();
200
201
1.58M
    nBytesConsumedOut = 4 + nPointCount * nPointSize;
202
203
    /* -------------------------------------------------------------------- */
204
    /*      Get the vertices                                                */
205
    /* -------------------------------------------------------------------- */
206
1.58M
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
207
91.7k
    {
208
120k
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
209
28.7k
        {
210
28.7k
            memcpy(&(paoPoints[i].x), pabyData + 4 + 32 * i, 8);
211
28.7k
            memcpy(&(paoPoints[i].y), pabyData + 4 + 32 * i + 8, 8);
212
28.7k
            memcpy(padfZ + i, pabyData + 4 + 32 * i + 16, 8);
213
28.7k
            memcpy(padfM + i, pabyData + 4 + 32 * i + 24, 8);
214
28.7k
        }
215
91.7k
    }
216
1.49M
    else if (flags & OGR_G_MEASURED)
217
44.3k
    {
218
56.1k
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
219
11.8k
        {
220
11.8k
            memcpy(&(paoPoints[i].x), pabyData + 4 + 24 * i, 8);
221
11.8k
            memcpy(&(paoPoints[i].y), pabyData + 4 + 24 * i + 8, 8);
222
11.8k
            memcpy(padfM + i, pabyData + 4 + 24 * i + 16, 8);
223
11.8k
        }
224
44.3k
    }
225
1.45M
    else if (flags & OGR_G_3D)
226
1.09M
    {
227
1.14M
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
228
53.9k
        {
229
53.9k
            memcpy(&(paoPoints[i].x), pabyData + 4 + 24 * i, 8);
230
53.9k
            memcpy(&(paoPoints[i].y), pabyData + 4 + 24 * i + 8, 8);
231
53.9k
            memcpy(padfZ + i, pabyData + 4 + 24 * i + 16, 8);
232
53.9k
        }
233
1.09M
    }
234
356k
    else if (nPointCount != 0)
235
19.5k
    {
236
19.5k
        memcpy(paoPoints, pabyData + 4, 16 * static_cast<size_t>(nPointCount));
237
19.5k
    }
238
239
    /* -------------------------------------------------------------------- */
240
    /*      Byte swap if needed.                                            */
241
    /* -------------------------------------------------------------------- */
242
1.58M
    if (OGR_SWAP(eByteOrder))
243
860k
    {
244
1.07M
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
245
218k
        {
246
218k
            CPL_SWAPDOUBLE(&(paoPoints[i].x));
247
218k
            CPL_SWAPDOUBLE(&(paoPoints[i].y));
248
249
218k
            if (flags & OGR_G_3D)
250
65.2k
            {
251
65.2k
                CPL_SWAPDOUBLE(padfZ + i);
252
65.2k
            }
253
218k
            if (flags & OGR_G_MEASURED)
254
37.9k
            {
255
37.9k
                CPL_SWAPDOUBLE(padfM + i);
256
37.9k
            }
257
218k
        }
258
860k
    }
259
260
1.58M
    return OGRERR_NONE;
261
1.60M
}
262
263
/************************************************************************/
264
/*                            _exportToWkb()                            */
265
/*                                                                      */
266
/*      Helper method for OGRPolygon.  THIS IS NOT THE NORMAL           */
267
/*      exportToWkb() METHOD.                                           */
268
/************************************************************************/
269
270
OGRErr OGRLinearRing::_exportToWkb(int _flags, unsigned char *pabyData,
271
                                   const OGRwkbExportOptions *psOptions) const
272
273
2.80M
{
274
275
    /* -------------------------------------------------------------------- */
276
    /*      Copy in the raw data.                                           */
277
    /* -------------------------------------------------------------------- */
278
2.80M
    memcpy(pabyData, &nPointCount, 4);
279
280
    /* -------------------------------------------------------------------- */
281
    /*      Copy in the raw data.                                           */
282
    /* -------------------------------------------------------------------- */
283
2.80M
    size_t nWords = 0;
284
2.80M
    if ((_flags & OGR_G_3D) && (_flags & OGR_G_MEASURED))
285
454k
    {
286
454k
        nWords = 4 * static_cast<size_t>(nPointCount);
287
537k
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
288
82.8k
        {
289
82.8k
            memcpy(pabyData + 4 + i * 32, &(paoPoints[i].x), 8);
290
82.8k
            memcpy(pabyData + 4 + i * 32 + 8, &(paoPoints[i].y), 8);
291
82.8k
            if (padfZ == nullptr)
292
0
                memset(pabyData + 4 + i * 32 + 16, 0, 8);
293
82.8k
            else
294
82.8k
                memcpy(pabyData + 4 + i * 32 + 16, padfZ + i, 8);
295
82.8k
            if (padfM == nullptr)
296
0
                memset(pabyData + 4 + i * 32 + 24, 0, 8);
297
82.8k
            else
298
82.8k
                memcpy(pabyData + 4 + i * 32 + 24, padfM + i, 8);
299
82.8k
        }
300
454k
        OGRRoundCoordinatesIEEE754XYValues<32>(
301
454k
            psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
302
454k
        OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nZBitPrecision,
303
454k
                                       pabyData + 4 + 2 * sizeof(uint64_t),
304
454k
                                       nPointCount);
305
454k
        OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nMBitPrecision,
306
454k
                                       pabyData + 4 + 3 * sizeof(uint64_t),
307
454k
                                       nPointCount);
308
454k
    }
309
2.35M
    else if (_flags & OGR_G_MEASURED)
310
52.2k
    {
311
52.2k
        nWords = 3 * static_cast<size_t>(nPointCount);
312
69.5k
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
313
17.3k
        {
314
17.3k
            memcpy(pabyData + 4 + i * 24, &(paoPoints[i].x), 8);
315
17.3k
            memcpy(pabyData + 4 + i * 24 + 8, &(paoPoints[i].y), 8);
316
17.3k
            if (padfM == nullptr)
317
0
                memset(pabyData + 4 + i * 24 + 16, 0, 8);
318
17.3k
            else
319
17.3k
                memcpy(pabyData + 4 + i * 24 + 16, padfM + i, 8);
320
17.3k
        }
321
52.2k
        OGRRoundCoordinatesIEEE754XYValues<24>(
322
52.2k
            psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
323
52.2k
        OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nMBitPrecision,
324
52.2k
                                       pabyData + 4 + 2 * sizeof(uint64_t),
325
52.2k
                                       nPointCount);
326
52.2k
    }
327
2.30M
    else if (_flags & OGR_G_3D)
328
1.94M
    {
329
1.94M
        nWords = 3 * static_cast<size_t>(nPointCount);
330
2.07M
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
331
124k
        {
332
124k
            memcpy(pabyData + 4 + i * 24, &(paoPoints[i].x), 8);
333
124k
            memcpy(pabyData + 4 + i * 24 + 8, &(paoPoints[i].y), 8);
334
124k
            if (padfZ == nullptr)
335
0
                memset(pabyData + 4 + i * 24 + 16, 0, 8);
336
124k
            else
337
124k
                memcpy(pabyData + 4 + i * 24 + 16, padfZ + i, 8);
338
124k
        }
339
1.94M
        OGRRoundCoordinatesIEEE754XYValues<24>(
340
1.94M
            psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
341
1.94M
        OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nZBitPrecision,
342
1.94M
                                       pabyData + 4 + 2 * sizeof(uint64_t),
343
1.94M
                                       nPointCount);
344
1.94M
    }
345
355k
    else
346
355k
    {
347
355k
        nWords = 2 * static_cast<size_t>(nPointCount);
348
355k
        memcpy(pabyData + 4, paoPoints, 16 * static_cast<size_t>(nPointCount));
349
355k
        OGRRoundCoordinatesIEEE754XYValues<16>(
350
355k
            psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
351
355k
    }
352
353
    /* -------------------------------------------------------------------- */
354
    /*      Swap if needed.                                                 */
355
    /* -------------------------------------------------------------------- */
356
2.80M
    if (OGR_SWAP(psOptions->eByteOrder))
357
0
    {
358
0
        const int nCount = CPL_SWAP32(nPointCount);
359
0
        memcpy(pabyData, &nCount, 4);
360
361
0
        for (size_t i = 0; i < nWords; i++)
362
0
        {
363
0
            CPL_SWAPDOUBLE(pabyData + 4 + 8 * i);
364
0
        }
365
0
    }
366
367
2.80M
    return OGRERR_NONE;
368
2.80M
}
369
370
/************************************************************************/
371
/*                              _WkbSize()                              */
372
/*                                                                      */
373
/*      Helper method for OGRPolygon.  NOT THE NORMAL WkbSize() METHOD. */
374
/************************************************************************/
375
376
size_t OGRLinearRing::_WkbSize(int _flags) const
377
378
10.9M
{
379
10.9M
    if ((_flags & OGR_G_3D) && (_flags & OGR_G_MEASURED))
380
1.16M
        return 4 + 32 * static_cast<size_t>(nPointCount);
381
9.77M
    else if ((_flags & OGR_G_3D) || (_flags & OGR_G_MEASURED))
382
6.93M
        return 4 + 24 * static_cast<size_t>(nPointCount);
383
2.84M
    else
384
2.84M
        return 4 + 16 * static_cast<size_t>(nPointCount);
385
10.9M
}
386
387
//! @endcond
388
389
/************************************************************************/
390
/*                               clone()                                */
391
/*                                                                      */
392
/*      We override the OGRCurve clone() to ensure that we get the      */
393
/*      correct virtual table.                                          */
394
/************************************************************************/
395
396
OGRLinearRing *OGRLinearRing::clone() const
397
398
657k
{
399
657k
    OGRLinearRing *poNewLinearRing = new OGRLinearRing();
400
657k
    poNewLinearRing->assignSpatialReference(getSpatialReference());
401
402
657k
    poNewLinearRing->setPoints(nPointCount, paoPoints, padfZ, padfM);
403
657k
    poNewLinearRing->flags = flags;
404
405
657k
    return poNewLinearRing;
406
657k
}
407
408
/************************************************************************/
409
/*                        reverseWindingOrder()                         */
410
/************************************************************************/
411
412
//! @cond Doxygen_Suppress
413
/** Reverse order of points.
414
 */
415
void OGRLinearRing::reverseWindingOrder()
416
417
0
{
418
0
    reversePoints();
419
0
}
420
421
//! @endcond
422
423
/************************************************************************/
424
/*                             closeRing()                              */
425
/************************************************************************/
426
427
void OGRLinearRing::closeRings()
428
429
517k
{
430
517k
    if (nPointCount < 2)
431
41.2k
        return;
432
433
476k
    if (getX(0) != getX(nPointCount - 1) || getY(0) != getY(nPointCount - 1) ||
434
91.1k
        getZ(0) != getZ(nPointCount - 1))
435
412k
    {
436
412k
        OGRPoint oFirstPoint;
437
412k
        getPoint(0, &oFirstPoint);
438
412k
        addPoint(&oFirstPoint);
439
412k
    }
440
476k
}
441
442
/************************************************************************/
443
/*                           isPointInRing()                            */
444
/************************************************************************/
445
446
/** Returns whether the point is inside the ring.
447
 * @param poPoint point
448
 * @param bTestEnvelope set to TRUE if the presence of the point inside the
449
 *                      ring envelope must be checked first.
450
 * @return TRUE or FALSE.
451
 */
452
bool OGRLinearRing::isPointInRing(const OGRPoint *poPoint,
453
                                  int bTestEnvelope) const
454
64.2M
{
455
64.2M
    if (nullptr == poPoint)
456
0
    {
457
0
        CPLDebug("OGR",
458
0
                 "OGRLinearRing::isPointInRing(const OGRPoint* poPoint) - "
459
0
                 "passed point is NULL!");
460
0
        return FALSE;
461
0
    }
462
64.2M
    if (poPoint->IsEmpty())
463
319k
    {
464
319k
        return FALSE;
465
319k
    }
466
467
63.9M
    const int iNumPoints = getNumPoints();
468
469
    // Simple validation
470
63.9M
    if (iNumPoints < 4)
471
390
        return FALSE;
472
473
63.9M
    const double dfTestX = poPoint->getX();
474
63.9M
    const double dfTestY = poPoint->getY();
475
476
    // Fast test if point is inside extent of the ring.
477
63.9M
    if (bTestEnvelope)
478
32
    {
479
32
        OGREnvelope extent;
480
32
        getEnvelope(&extent);
481
32
        if (!(dfTestX >= extent.MinX && dfTestX <= extent.MaxX &&
482
20
              dfTestY >= extent.MinY && dfTestY <= extent.MaxY))
483
19
        {
484
19
            return FALSE;
485
19
        }
486
32
    }
487
488
    // For every point p in ring,
489
    // test if ray starting from given point crosses segment (p - 1, p)
490
63.9M
    int iNumCrossings = 0;
491
492
63.9M
    double prev_diff_x = getX(0) - dfTestX;
493
63.9M
    double prev_diff_y = getY(0) - dfTestY;
494
495
1.53G
    for (int iPoint = 1; iPoint < iNumPoints; iPoint++)
496
1.46G
    {
497
1.46G
        const double x1 = getX(iPoint) - dfTestX;
498
1.46G
        const double y1 = getY(iPoint) - dfTestY;
499
500
1.46G
        const double x2 = prev_diff_x;
501
1.46G
        const double y2 = prev_diff_y;
502
503
1.46G
        if (((y1 > 0) && (y2 <= 0)) || ((y2 > 0) && (y1 <= 0)))
504
101M
        {
505
            // Check if ray intersects with segment of the ring
506
101M
            const double dfIntersection = (x1 * y2 - x2 * y1) / (y2 - y1);
507
101M
            if (0.0 < dfIntersection)
508
23.5M
            {
509
                // Count intersections
510
23.5M
                iNumCrossings++;
511
23.5M
            }
512
101M
        }
513
514
1.46G
        prev_diff_x = x1;
515
1.46G
        prev_diff_y = y1;
516
1.46G
    }
517
518
    // If iNumCrossings number is even, given point is outside the ring,
519
    // when the crossings number is odd, the point is inside the ring.
520
63.9M
    return (iNumCrossings % 2) != 0;
521
63.9M
}
522
523
/************************************************************************/
524
/*                       isPointOnRingBoundary()                        */
525
/************************************************************************/
526
527
/** Returns whether the point is on the ring boundary.
528
 * @param poPoint point
529
 * @param bTestEnvelope set to TRUE if the presence of the point inside the
530
 *                      ring envelope must be checked first.
531
 * @return TRUE or FALSE.
532
 */
533
bool OGRLinearRing::isPointOnRingBoundary(const OGRPoint *poPoint,
534
                                          int bTestEnvelope) const
535
604M
{
536
604M
    if (nullptr == poPoint)
537
0
    {
538
0
        CPLDebug("OGR", "OGRLinearRing::isPointOnRingBoundary(const OGRPoint* "
539
0
                        "poPoint) - passed point is NULL!");
540
0
        return 0;
541
0
    }
542
543
604M
    const int iNumPoints = getNumPoints();
544
545
    // Simple validation.
546
604M
    if (iNumPoints < 4)
547
376
        return 0;
548
549
604M
    const double dfTestX = poPoint->getX();
550
604M
    const double dfTestY = poPoint->getY();
551
552
    // Fast test if point is inside extent of the ring
553
604M
    if (bTestEnvelope)
554
0
    {
555
0
        OGREnvelope extent;
556
0
        getEnvelope(&extent);
557
0
        if (!(dfTestX >= extent.MinX && dfTestX <= extent.MaxX &&
558
0
              dfTestY >= extent.MinY && dfTestY <= extent.MaxY))
559
0
        {
560
0
            return 0;
561
0
        }
562
0
    }
563
564
604M
    double prev_diff_x = dfTestX - getX(0);
565
604M
    double prev_diff_y = dfTestY - getY(0);
566
567
12.1G
    for (int iPoint = 1; iPoint < iNumPoints; iPoint++)
568
12.0G
    {
569
12.0G
        const double dx1 = dfTestX - getX(iPoint);
570
12.0G
        const double dy1 = dfTestY - getY(iPoint);
571
572
12.0G
        const double dx2 = prev_diff_x;
573
12.0G
        const double dy2 = prev_diff_y;
574
575
        // If the point is on the segment, return immediately.
576
        // FIXME? If the test point is not exactly identical to one of
577
        // the vertices of the ring, but somewhere on a segment, there's
578
        // little chance that we get 0. So that should be tested against some
579
        // epsilon.
580
581
12.0G
        if (dx1 * dy2 - dx2 * dy1 == 0)
582
2.92G
        {
583
            // If iPoint and iPointPrev are the same, go on.
584
2.92G
            if (!(dx1 == dx2 && dy1 == dy2))
585
1.02G
            {
586
1.02G
                const double dx_segment = getX(iPoint) - getX(iPoint - 1);
587
1.02G
                const double dy_segment = getY(iPoint) - getY(iPoint - 1);
588
1.02G
                const double crossproduct = dx2 * dx_segment + dy2 * dy_segment;
589
1.02G
                if (crossproduct >= 0)
590
857M
                {
591
857M
                    const double sq_length_seg =
592
857M
                        dx_segment * dx_segment + dy_segment * dy_segment;
593
857M
                    if (crossproduct <= sq_length_seg)
594
540M
                    {
595
540M
                        return 1;
596
540M
                    }
597
857M
                }
598
1.02G
            }
599
2.92G
        }
600
601
11.5G
        prev_diff_x = dx1;
602
11.5G
        prev_diff_y = dy1;
603
11.5G
    }
604
605
64.2M
    return 0;
606
604M
}
607
608
/************************************************************************/
609
/*                             transform()                              */
610
/************************************************************************/
611
612
OGRErr OGRLinearRing::transform(OGRCoordinateTransformation *poCT)
613
614
143k
{
615
143k
    const bool bIsClosed = getNumPoints() > 2 && get_IsClosed();
616
143k
    OGRErr eErr = OGRLineString::transform(poCT);
617
143k
    if (bIsClosed && eErr == OGRERR_NONE && !get_IsClosed())
618
5.72k
    {
619
5.72k
        CPLDebug("OGR", "Linearring is not closed after coordinate "
620
5.72k
                        "transformation. Forcing last point to be identical to "
621
5.72k
                        "first one");
622
        // Force last point to be identical to first point.
623
        // This is a safety belt in case the reprojection of the same coordinate
624
        // isn't perfectly stable. This can for example happen in very rare
625
        // cases when reprojecting a cutline with a RPC transform with a DEM
626
        // that is a VRT whose sources are resampled...
627
5.72k
        OGRPoint oStartPoint;
628
5.72k
        StartPoint(&oStartPoint);
629
630
5.72k
        setPoint(getNumPoints() - 1, &oStartPoint);
631
5.72k
    }
632
143k
    return eErr;
633
143k
}
634
635
/************************************************************************/
636
/*                          CastToLineString()                          */
637
/************************************************************************/
638
639
/**
640
 * \brief Cast to line string.
641
 *
642
 * The passed in geometry is consumed and a new one returned .
643
 *
644
 * @param poLR the input geometry - ownership is passed to the method.
645
 * @return new geometry.
646
 */
647
648
OGRLineString *OGRLinearRing::CastToLineString(OGRLinearRing *poLR)
649
4.37k
{
650
4.37k
    return TransferMembersAndDestroy(poLR, new OGRLineString());
651
4.37k
}
652
653
//! @cond Doxygen_Suppress
654
/************************************************************************/
655
/*                       GetCasterToLineString()                        */
656
/************************************************************************/
657
658
OGRLineString *OGRLinearRing::CasterToLineString(OGRCurve *poCurve)
659
4.37k
{
660
4.37k
    return OGRLinearRing::CastToLineString(poCurve->toLinearRing());
661
4.37k
}
662
663
OGRCurveCasterToLineString OGRLinearRing::GetCasterToLineString() const
664
4.37k
{
665
4.37k
    return OGRLinearRing::CasterToLineString;
666
4.37k
}
667
668
/************************************************************************/
669
/*                       GetCasterToLinearRing()                        */
670
/************************************************************************/
671
672
static OGRLinearRing *CasterToLinearRing(OGRCurve *poCurve)
673
0
{
674
0
    return poCurve->toLinearRing();
675
0
}
676
677
OGRCurveCasterToLinearRing OGRLinearRing::GetCasterToLinearRing() const
678
0
{
679
0
    return ::CasterToLinearRing;
680
0
}
681
682
//! @endcond