Coverage Report

Created: 2025-11-16 06:25

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
0
{
91
0
    return "LINEARRING";
92
0
}
93
94
/************************************************************************/
95
/*                              WkbSize()                               */
96
/*                                                                      */
97
/*      Disable this method.                                            */
98
/************************************************************************/
99
100
size_t OGRLinearRing::WkbSize() const
101
102
0
{
103
0
    return 0;
104
0
}
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
0
{
148
0
    nBytesConsumedOut = 0;
149
0
    if (nBytesAvailable < 4 && nBytesAvailable != static_cast<size_t>(-1))
150
0
        return OGRERR_NOT_ENOUGH_DATA;
151
152
    /* -------------------------------------------------------------------- */
153
    /*      Get the vertex count.                                           */
154
    /* -------------------------------------------------------------------- */
155
0
    int nNewNumPoints = 0;
156
157
0
    memcpy(&nNewNumPoints, pabyData, 4);
158
159
0
    if (OGR_SWAP(eByteOrder))
160
0
        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
0
    size_t nPointSize = 0;
166
0
    if ((_flags & OGR_G_3D) && (_flags & OGR_G_MEASURED))
167
0
        nPointSize = 32;
168
0
    else if ((_flags & OGR_G_3D) || (_flags & OGR_G_MEASURED))
169
0
        nPointSize = 24;
170
0
    else
171
0
        nPointSize = 16;
172
173
0
    if (nNewNumPoints < 0 ||
174
0
        static_cast<size_t>(nNewNumPoints) >
175
0
            std::numeric_limits<size_t>::max() / nPointSize)
176
0
    {
177
0
        return OGRERR_CORRUPT_DATA;
178
0
    }
179
0
    const size_t nBufferMinSize = nPointSize * nNewNumPoints;
180
0
    if (nBytesAvailable != static_cast<size_t>(-1) &&
181
0
        nBufferMinSize > nBytesAvailable - 4)
182
0
    {
183
0
        CPLError(CE_Failure, CPLE_AppDefined,
184
0
                 "Length of input WKB is too small");
185
0
        return OGRERR_NOT_ENOUGH_DATA;
186
0
    }
187
188
    // (Re)Allocation of paoPoints buffer.
189
0
    setNumPoints(nNewNumPoints, FALSE);
190
191
0
    if (_flags & OGR_G_3D)
192
0
        Make3D();
193
0
    else
194
0
        Make2D();
195
196
0
    if (_flags & OGR_G_MEASURED)
197
0
        AddM();
198
0
    else
199
0
        RemoveM();
200
201
0
    nBytesConsumedOut = 4 + nPointCount * nPointSize;
202
203
    /* -------------------------------------------------------------------- */
204
    /*      Get the vertices                                                */
205
    /* -------------------------------------------------------------------- */
206
0
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
207
0
    {
208
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
209
0
        {
210
0
            memcpy(&(paoPoints[i].x), pabyData + 4 + 32 * i, 8);
211
0
            memcpy(&(paoPoints[i].y), pabyData + 4 + 32 * i + 8, 8);
212
0
            memcpy(padfZ + i, pabyData + 4 + 32 * i + 16, 8);
213
0
            memcpy(padfM + i, pabyData + 4 + 32 * i + 24, 8);
214
0
        }
215
0
    }
216
0
    else if (flags & OGR_G_MEASURED)
217
0
    {
218
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
219
0
        {
220
0
            memcpy(&(paoPoints[i].x), pabyData + 4 + 24 * i, 8);
221
0
            memcpy(&(paoPoints[i].y), pabyData + 4 + 24 * i + 8, 8);
222
0
            memcpy(padfM + i, pabyData + 4 + 24 * i + 16, 8);
223
0
        }
224
0
    }
225
0
    else if (flags & OGR_G_3D)
226
0
    {
227
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
228
0
        {
229
0
            memcpy(&(paoPoints[i].x), pabyData + 4 + 24 * i, 8);
230
0
            memcpy(&(paoPoints[i].y), pabyData + 4 + 24 * i + 8, 8);
231
0
            memcpy(padfZ + i, pabyData + 4 + 24 * i + 16, 8);
232
0
        }
233
0
    }
234
0
    else if (nPointCount != 0)
235
0
    {
236
0
        memcpy(paoPoints, pabyData + 4, 16 * static_cast<size_t>(nPointCount));
237
0
    }
238
239
    /* -------------------------------------------------------------------- */
240
    /*      Byte swap if needed.                                            */
241
    /* -------------------------------------------------------------------- */
242
0
    if (OGR_SWAP(eByteOrder))
243
0
    {
244
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
245
0
        {
246
0
            CPL_SWAPDOUBLE(&(paoPoints[i].x));
247
0
            CPL_SWAPDOUBLE(&(paoPoints[i].y));
248
249
0
            if (flags & OGR_G_3D)
250
0
            {
251
0
                CPL_SWAPDOUBLE(padfZ + i);
252
0
            }
253
0
            if (flags & OGR_G_MEASURED)
254
0
            {
255
0
                CPL_SWAPDOUBLE(padfM + i);
256
0
            }
257
0
        }
258
0
    }
259
260
0
    return OGRERR_NONE;
261
0
}
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
0
{
274
275
    /* -------------------------------------------------------------------- */
276
    /*      Copy in the raw data.                                           */
277
    /* -------------------------------------------------------------------- */
278
0
    memcpy(pabyData, &nPointCount, 4);
279
280
    /* -------------------------------------------------------------------- */
281
    /*      Copy in the raw data.                                           */
282
    /* -------------------------------------------------------------------- */
283
0
    size_t nWords = 0;
284
0
    if ((_flags & OGR_G_3D) && (_flags & OGR_G_MEASURED))
285
0
    {
286
0
        nWords = 4 * static_cast<size_t>(nPointCount);
287
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
288
0
        {
289
0
            memcpy(pabyData + 4 + i * 32, &(paoPoints[i].x), 8);
290
0
            memcpy(pabyData + 4 + i * 32 + 8, &(paoPoints[i].y), 8);
291
0
            if (padfZ == nullptr)
292
0
                memset(pabyData + 4 + i * 32 + 16, 0, 8);
293
0
            else
294
0
                memcpy(pabyData + 4 + i * 32 + 16, padfZ + i, 8);
295
0
            if (padfM == nullptr)
296
0
                memset(pabyData + 4 + i * 32 + 24, 0, 8);
297
0
            else
298
0
                memcpy(pabyData + 4 + i * 32 + 24, padfM + i, 8);
299
0
        }
300
0
        OGRRoundCoordinatesIEEE754XYValues<32>(
301
0
            psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
302
0
        OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nZBitPrecision,
303
0
                                       pabyData + 4 + 2 * sizeof(uint64_t),
304
0
                                       nPointCount);
305
0
        OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nMBitPrecision,
306
0
                                       pabyData + 4 + 3 * sizeof(uint64_t),
307
0
                                       nPointCount);
308
0
    }
309
0
    else if (_flags & OGR_G_MEASURED)
310
0
    {
311
0
        nWords = 3 * static_cast<size_t>(nPointCount);
312
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
313
0
        {
314
0
            memcpy(pabyData + 4 + i * 24, &(paoPoints[i].x), 8);
315
0
            memcpy(pabyData + 4 + i * 24 + 8, &(paoPoints[i].y), 8);
316
0
            if (padfM == nullptr)
317
0
                memset(pabyData + 4 + i * 24 + 16, 0, 8);
318
0
            else
319
0
                memcpy(pabyData + 4 + i * 24 + 16, padfM + i, 8);
320
0
        }
321
0
        OGRRoundCoordinatesIEEE754XYValues<24>(
322
0
            psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
323
0
        OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nMBitPrecision,
324
0
                                       pabyData + 4 + 2 * sizeof(uint64_t),
325
0
                                       nPointCount);
326
0
    }
327
0
    else if (_flags & OGR_G_3D)
328
0
    {
329
0
        nWords = 3 * static_cast<size_t>(nPointCount);
330
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
331
0
        {
332
0
            memcpy(pabyData + 4 + i * 24, &(paoPoints[i].x), 8);
333
0
            memcpy(pabyData + 4 + i * 24 + 8, &(paoPoints[i].y), 8);
334
0
            if (padfZ == nullptr)
335
0
                memset(pabyData + 4 + i * 24 + 16, 0, 8);
336
0
            else
337
0
                memcpy(pabyData + 4 + i * 24 + 16, padfZ + i, 8);
338
0
        }
339
0
        OGRRoundCoordinatesIEEE754XYValues<24>(
340
0
            psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
341
0
        OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nZBitPrecision,
342
0
                                       pabyData + 4 + 2 * sizeof(uint64_t),
343
0
                                       nPointCount);
344
0
    }
345
0
    else
346
0
    {
347
0
        nWords = 2 * static_cast<size_t>(nPointCount);
348
0
        memcpy(pabyData + 4, paoPoints, 16 * static_cast<size_t>(nPointCount));
349
0
        OGRRoundCoordinatesIEEE754XYValues<16>(
350
0
            psOptions->sPrecision.nXYBitPrecision, pabyData + 4, nPointCount);
351
0
    }
352
353
    /* -------------------------------------------------------------------- */
354
    /*      Swap if needed.                                                 */
355
    /* -------------------------------------------------------------------- */
356
0
    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
0
    return OGRERR_NONE;
368
0
}
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
0
{
379
0
    if ((_flags & OGR_G_3D) && (_flags & OGR_G_MEASURED))
380
0
        return 4 + 32 * static_cast<size_t>(nPointCount);
381
0
    else if ((_flags & OGR_G_3D) || (_flags & OGR_G_MEASURED))
382
0
        return 4 + 24 * static_cast<size_t>(nPointCount);
383
0
    else
384
0
        return 4 + 16 * static_cast<size_t>(nPointCount);
385
0
}
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
0
{
399
0
    OGRLinearRing *poNewLinearRing = new OGRLinearRing();
400
0
    poNewLinearRing->assignSpatialReference(getSpatialReference());
401
402
0
    poNewLinearRing->setPoints(nPointCount, paoPoints, padfZ, padfM);
403
0
    poNewLinearRing->flags = flags;
404
405
0
    return poNewLinearRing;
406
0
}
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
0
{
430
0
    if (nPointCount < 2)
431
0
        return;
432
433
0
    if (getX(0) != getX(nPointCount - 1) || getY(0) != getY(nPointCount - 1) ||
434
0
        getZ(0) != getZ(nPointCount - 1))
435
0
    {
436
0
        OGRPoint oFirstPoint;
437
0
        getPoint(0, &oFirstPoint);
438
0
        addPoint(&oFirstPoint);
439
0
    }
440
0
}
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
OGRBoolean OGRLinearRing::isPointInRing(const OGRPoint *poPoint,
453
                                        int bTestEnvelope) const
454
0
{
455
0
    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
0
    if (poPoint->IsEmpty())
463
0
    {
464
0
        return FALSE;
465
0
    }
466
467
0
    const int iNumPoints = getNumPoints();
468
469
    // Simple validation
470
0
    if (iNumPoints < 4)
471
0
        return FALSE;
472
473
0
    const double dfTestX = poPoint->getX();
474
0
    const double dfTestY = poPoint->getY();
475
476
    // Fast test if point is inside extent of the ring.
477
0
    if (bTestEnvelope)
478
0
    {
479
0
        OGREnvelope extent;
480
0
        getEnvelope(&extent);
481
0
        if (!(dfTestX >= extent.MinX && dfTestX <= extent.MaxX &&
482
0
              dfTestY >= extent.MinY && dfTestY <= extent.MaxY))
483
0
        {
484
0
            return FALSE;
485
0
        }
486
0
    }
487
488
    // For every point p in ring,
489
    // test if ray starting from given point crosses segment (p - 1, p)
490
0
    int iNumCrossings = 0;
491
492
0
    double prev_diff_x = getX(0) - dfTestX;
493
0
    double prev_diff_y = getY(0) - dfTestY;
494
495
0
    for (int iPoint = 1; iPoint < iNumPoints; iPoint++)
496
0
    {
497
0
        const double x1 = getX(iPoint) - dfTestX;
498
0
        const double y1 = getY(iPoint) - dfTestY;
499
500
0
        const double x2 = prev_diff_x;
501
0
        const double y2 = prev_diff_y;
502
503
0
        if (((y1 > 0) && (y2 <= 0)) || ((y2 > 0) && (y1 <= 0)))
504
0
        {
505
            // Check if ray intersects with segment of the ring
506
0
            const double dfIntersection = (x1 * y2 - x2 * y1) / (y2 - y1);
507
0
            if (0.0 < dfIntersection)
508
0
            {
509
                // Count intersections
510
0
                iNumCrossings++;
511
0
            }
512
0
        }
513
514
0
        prev_diff_x = x1;
515
0
        prev_diff_y = y1;
516
0
    }
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
0
    return iNumCrossings % 2;  // OGRBoolean
521
0
}
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
OGRBoolean OGRLinearRing::isPointOnRingBoundary(const OGRPoint *poPoint,
534
                                                int bTestEnvelope) const
535
0
{
536
0
    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
0
    const int iNumPoints = getNumPoints();
544
545
    // Simple validation.
546
0
    if (iNumPoints < 4)
547
0
        return 0;
548
549
0
    const double dfTestX = poPoint->getX();
550
0
    const double dfTestY = poPoint->getY();
551
552
    // Fast test if point is inside extent of the ring
553
0
    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
0
    double prev_diff_x = dfTestX - getX(0);
565
0
    double prev_diff_y = dfTestY - getY(0);
566
567
0
    for (int iPoint = 1; iPoint < iNumPoints; iPoint++)
568
0
    {
569
0
        const double dx1 = dfTestX - getX(iPoint);
570
0
        const double dy1 = dfTestY - getY(iPoint);
571
572
0
        const double dx2 = prev_diff_x;
573
0
        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
0
        if (dx1 * dy2 - dx2 * dy1 == 0)
582
0
        {
583
            // If iPoint and iPointPrev are the same, go on.
584
0
            if (!(dx1 == dx2 && dy1 == dy2))
585
0
            {
586
0
                const double dx_segment = getX(iPoint) - getX(iPoint - 1);
587
0
                const double dy_segment = getY(iPoint) - getY(iPoint - 1);
588
0
                const double crossproduct = dx2 * dx_segment + dy2 * dy_segment;
589
0
                if (crossproduct >= 0)
590
0
                {
591
0
                    const double sq_length_seg =
592
0
                        dx_segment * dx_segment + dy_segment * dy_segment;
593
0
                    if (crossproduct <= sq_length_seg)
594
0
                    {
595
0
                        return 1;
596
0
                    }
597
0
                }
598
0
            }
599
0
        }
600
601
0
        prev_diff_x = dx1;
602
0
        prev_diff_y = dy1;
603
0
    }
604
605
0
    return 0;
606
0
}
607
608
/************************************************************************/
609
/*                             transform()                              */
610
/************************************************************************/
611
612
OGRErr OGRLinearRing::transform(OGRCoordinateTransformation *poCT)
613
614
0
{
615
0
    const bool bIsClosed = getNumPoints() > 2 && CPL_TO_BOOL(get_IsClosed());
616
0
    OGRErr eErr = OGRLineString::transform(poCT);
617
0
    if (bIsClosed && eErr == OGRERR_NONE && !get_IsClosed())
618
0
    {
619
0
        CPLDebug("OGR", "Linearring is not closed after coordinate "
620
0
                        "transformation. Forcing last point to be identical to "
621
0
                        "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
0
        OGRPoint oStartPoint;
628
0
        StartPoint(&oStartPoint);
629
630
0
        setPoint(getNumPoints() - 1, &oStartPoint);
631
0
    }
632
0
    return eErr;
633
0
}
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
0
{
650
0
    return TransferMembersAndDestroy(poLR, new OGRLineString());
651
0
}
652
653
//! @cond Doxygen_Suppress
654
/************************************************************************/
655
/*                     GetCasterToLineString()                          */
656
/************************************************************************/
657
658
OGRLineString *OGRLinearRing::CasterToLineString(OGRCurve *poCurve)
659
0
{
660
0
    return OGRLinearRing::CastToLineString(poCurve->toLinearRing());
661
0
}
662
663
OGRCurveCasterToLineString OGRLinearRing::GetCasterToLineString() const
664
0
{
665
0
    return OGRLinearRing::CasterToLineString;
666
0
}
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