Coverage Report

Created: 2025-06-13 06:29

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