Coverage Report

Created: 2025-06-22 06:59

/src/gdal/ogr/ogrlinestring.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRSimpleCurve and OGRLineString geometry classes.
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 "ogr_geometry.h"
15
#include "ogr_geos.h"
16
#include "ogr_p.h"
17
18
#include "geodesic.h"  // from PROJ
19
20
#include <cmath>
21
#include <cstdlib>
22
#include <algorithm>
23
#include <limits>
24
#include <new>
25
26
namespace
27
{
28
29
int DoubleToIntClamp(double dfValue)
30
0
{
31
0
    if (std::isnan(dfValue))
32
0
        return 0;
33
0
    if (dfValue >= std::numeric_limits<int>::max())
34
0
        return std::numeric_limits<int>::max();
35
0
    if (dfValue <= std::numeric_limits<int>::min())
36
0
        return std::numeric_limits<int>::min();
37
0
    return static_cast<int>(dfValue);
38
0
}
39
40
}  // namespace
41
42
/************************************************************************/
43
/*                OGRSimpleCurve( const OGRSimpleCurve& )               */
44
/************************************************************************/
45
46
/**
47
 * \brief Copy constructor.
48
 *
49
 * Note: before GDAL 2.1, only the default implementation of the constructor
50
 * existed, which could be unsafe to use.
51
 *
52
 * @since GDAL 2.1
53
 */
54
55
OGRSimpleCurve::OGRSimpleCurve(const OGRSimpleCurve &other)
56
0
    : OGRCurve(other), nPointCount(0), paoPoints(nullptr), padfZ(nullptr),
57
0
      padfM(nullptr)
58
0
{
59
0
    if (other.nPointCount > 0)
60
0
        setPoints(other.nPointCount, other.paoPoints, other.padfZ, other.padfM);
61
0
}
62
63
/************************************************************************/
64
/*                OGRSimpleCurve( OGRSimpleCurve&& )                    */
65
/************************************************************************/
66
67
/**
68
 * \brief Move constructor.
69
 *
70
 * @since GDAL 3.11
71
 */
72
73
// cppcheck-suppress-begin accessMoved
74
OGRSimpleCurve::OGRSimpleCurve(OGRSimpleCurve &&other)
75
0
    : OGRCurve(std::move(other)), nPointCount(other.nPointCount),
76
0
      m_nPointCapacity(other.m_nPointCapacity), paoPoints(other.paoPoints),
77
0
      padfZ(other.padfZ), padfM(other.padfM)
78
0
{
79
0
    other.nPointCount = 0;
80
0
    other.m_nPointCapacity = 0;
81
0
    other.paoPoints = nullptr;
82
0
    other.padfZ = nullptr;
83
0
    other.padfM = nullptr;
84
0
}
85
86
// cppcheck-suppress-end accessMoved
87
88
/************************************************************************/
89
/*                          ~OGRSimpleCurve()                           */
90
/************************************************************************/
91
92
OGRSimpleCurve::~OGRSimpleCurve()
93
94
5.01k
{
95
5.01k
    CPLFree(paoPoints);
96
5.01k
    CPLFree(padfZ);
97
5.01k
    CPLFree(padfM);
98
5.01k
}
99
100
/************************************************************************/
101
/*                 operator=(const OGRSimpleCurve &other)               */
102
/************************************************************************/
103
104
/**
105
 * \brief Assignment operator.
106
 *
107
 * Note: before GDAL 2.1, only the default implementation of the operator
108
 * existed, which could be unsafe to use.
109
 *
110
 * @since GDAL 2.1
111
 */
112
113
OGRSimpleCurve &OGRSimpleCurve::operator=(const OGRSimpleCurve &other)
114
0
{
115
0
    if (this == &other)
116
0
        return *this;
117
118
0
    OGRCurve::operator=(other);
119
120
0
    setPoints(other.nPointCount, other.paoPoints, other.padfZ, other.padfM);
121
0
    flags = other.flags;
122
123
0
    return *this;
124
0
}
125
126
/************************************************************************/
127
/*                     operator=(OGRSimpleCurve &&other)                */
128
/************************************************************************/
129
130
/**
131
 * \brief Move assignment operator.
132
 *
133
 * @since GDAL 3.11
134
 */
135
136
OGRSimpleCurve &OGRSimpleCurve::operator=(OGRSimpleCurve &&other)
137
0
{
138
0
    if (this != &other)
139
0
    {
140
        // cppcheck-suppress-begin accessMoved
141
0
        OGRCurve::operator=(std::move(other));
142
143
0
        nPointCount = other.nPointCount;
144
0
        m_nPointCapacity = other.m_nPointCapacity;
145
0
        CPLFree(paoPoints);
146
0
        paoPoints = other.paoPoints;
147
0
        CPLFree(padfZ);
148
0
        padfZ = other.padfZ;
149
0
        CPLFree(padfM);
150
0
        padfM = other.padfM;
151
0
        flags = other.flags;
152
0
        other.nPointCount = 0;
153
0
        other.m_nPointCapacity = 0;
154
0
        other.paoPoints = nullptr;
155
0
        other.padfZ = nullptr;
156
0
        other.padfM = nullptr;
157
        // cppcheck-suppress-end accessMoved
158
0
    }
159
160
0
    return *this;
161
0
}
162
163
/************************************************************************/
164
/*                            flattenTo2D()                             */
165
/************************************************************************/
166
167
void OGRSimpleCurve::flattenTo2D()
168
169
0
{
170
0
    Make2D();
171
0
    setMeasured(FALSE);
172
0
}
173
174
/************************************************************************/
175
/*                               empty()                                */
176
/************************************************************************/
177
178
void OGRSimpleCurve::empty()
179
180
446
{
181
446
    setNumPoints(0);
182
446
}
183
184
/************************************************************************/
185
/*                       setCoordinateDimension()                       */
186
/************************************************************************/
187
188
bool OGRSimpleCurve::setCoordinateDimension(int nNewDimension)
189
190
0
{
191
0
    setMeasured(FALSE);
192
0
    if (nNewDimension == 2)
193
0
        Make2D();
194
0
    else if (nNewDimension == 3)
195
0
        return Make3D();
196
0
    return true;
197
0
}
198
199
bool OGRSimpleCurve::set3D(OGRBoolean bIs3D)
200
201
1.54k
{
202
1.54k
    if (bIs3D)
203
1.54k
        return Make3D();
204
1
    else
205
1
        Make2D();
206
1
    return true;
207
1.54k
}
208
209
bool OGRSimpleCurve::setMeasured(OGRBoolean bIsMeasured)
210
211
1.39k
{
212
1.39k
    if (bIsMeasured)
213
1.37k
        return AddM();
214
16
    else
215
16
        RemoveM();
216
16
    return true;
217
1.39k
}
218
219
/************************************************************************/
220
/*                              WkbSize()                               */
221
/*                                                                      */
222
/*      Return the size of this object in well known binary             */
223
/*      representation including the byte order, and type information.  */
224
/************************************************************************/
225
226
size_t OGRSimpleCurve::WkbSize() const
227
228
0
{
229
0
    return 5 + 4 + 8 * static_cast<size_t>(nPointCount) * CoordinateDimension();
230
0
}
231
232
//! @cond Doxygen_Suppress
233
234
/************************************************************************/
235
/*                               Make2D()                               */
236
/************************************************************************/
237
238
void OGRSimpleCurve::Make2D()
239
240
1
{
241
1
    if (padfZ != nullptr)
242
0
    {
243
0
        CPLFree(padfZ);
244
0
        padfZ = nullptr;
245
0
    }
246
1
    flags &= ~OGR_G_3D;
247
1
}
248
249
/************************************************************************/
250
/*                               Make3D()                               */
251
/************************************************************************/
252
253
bool OGRSimpleCurve::Make3D()
254
255
4.28k
{
256
4.28k
    if (padfZ == nullptr)
257
2.85k
    {
258
2.85k
        padfZ = static_cast<double *>(
259
2.85k
            VSI_CALLOC_VERBOSE(sizeof(double), std::max(1, m_nPointCapacity)));
260
2.85k
        if (padfZ == nullptr)
261
0
        {
262
0
            flags &= ~OGR_G_3D;
263
0
            CPLError(CE_Failure, CPLE_AppDefined,
264
0
                     "OGRSimpleCurve::Make3D() failed");
265
0
            return false;
266
0
        }
267
2.85k
    }
268
4.28k
    flags |= OGR_G_3D;
269
4.28k
    return true;
270
4.28k
}
271
272
/************************************************************************/
273
/*                               RemoveM()                              */
274
/************************************************************************/
275
276
void OGRSimpleCurve::RemoveM()
277
278
16
{
279
16
    if (padfM != nullptr)
280
0
    {
281
0
        CPLFree(padfM);
282
0
        padfM = nullptr;
283
0
    }
284
16
    flags &= ~OGR_G_MEASURED;
285
16
}
286
287
/************************************************************************/
288
/*                               AddM()                                 */
289
/************************************************************************/
290
291
bool OGRSimpleCurve::AddM()
292
293
3.39k
{
294
3.39k
    if (padfM == nullptr)
295
2.18k
    {
296
2.18k
        padfM = static_cast<double *>(
297
2.18k
            VSI_CALLOC_VERBOSE(sizeof(double), std::max(1, m_nPointCapacity)));
298
2.18k
        if (padfM == nullptr)
299
0
        {
300
0
            flags &= ~OGR_G_MEASURED;
301
0
            CPLError(CE_Failure, CPLE_AppDefined,
302
0
                     "OGRSimpleCurve::AddM() failed");
303
0
            return false;
304
0
        }
305
2.18k
    }
306
3.39k
    flags |= OGR_G_MEASURED;
307
3.39k
    return true;
308
3.39k
}
309
310
//! @endcond
311
312
/************************************************************************/
313
/*                              getPoint()                              */
314
/************************************************************************/
315
316
/**
317
 * \brief Fetch a point in line string.
318
 *
319
 * This method relates to the SFCOM ILineString::get_Point() method.
320
 *
321
 * @param i the vertex to fetch, from 0 to getNumPoints()-1.
322
 * @param poPoint a point to initialize with the fetched point.
323
 */
324
325
void OGRSimpleCurve::getPoint(int i, OGRPoint *poPoint) const
326
327
3.17k
{
328
3.17k
    CPLAssert(i >= 0);
329
3.17k
    CPLAssert(i < nPointCount);
330
3.17k
    CPLAssert(poPoint != nullptr);
331
332
3.17k
    poPoint->setX(paoPoints[i].x);
333
3.17k
    poPoint->setY(paoPoints[i].y);
334
335
3.17k
    if ((flags & OGR_G_3D) && padfZ != nullptr)
336
1.88k
        poPoint->setZ(padfZ[i]);
337
3.17k
    if ((flags & OGR_G_MEASURED) && padfM != nullptr)
338
2.16k
        poPoint->setM(padfM[i]);
339
3.17k
}
340
341
/**
342
 * \fn int OGRSimpleCurve::getNumPoints() const;
343
 *
344
 * \brief Fetch vertex count.
345
 *
346
 * Returns the number of vertices in the line string.
347
 *
348
 * @return vertex count.
349
 */
350
351
/**
352
 * \fn double OGRSimpleCurve::getX( int iVertex ) const;
353
 *
354
 * \brief Get X at vertex.
355
 *
356
 * Returns the X value at the indicated vertex.   If iVertex is out of range a
357
 * crash may occur, no internal range checking is performed.
358
 *
359
 * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
360
 *
361
 * @return X value.
362
 */
363
364
/**
365
 * \fn double OGRSimpleCurve::getY( int iVertex ) const;
366
 *
367
 * \brief Get Y at vertex.
368
 *
369
 * Returns the Y value at the indicated vertex.   If iVertex is out of range a
370
 * crash may occur, no internal range checking is performed.
371
 *
372
 * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
373
 *
374
 * @return X value.
375
 */
376
377
/************************************************************************/
378
/*                                getZ()                                */
379
/************************************************************************/
380
381
/**
382
 * \brief Get Z at vertex.
383
 *
384
 * Returns the Z (elevation) value at the indicated vertex.  If no Z
385
 * value is available, 0.0 is returned.  If iVertex is out of range a
386
 * crash may occur, no internal range checking is performed.
387
 *
388
 * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
389
 *
390
 * @return Z value.
391
 */
392
393
double OGRSimpleCurve::getZ(int iVertex) const
394
395
0
{
396
0
    if (padfZ != nullptr && iVertex >= 0 && iVertex < nPointCount &&
397
0
        (flags & OGR_G_3D))
398
0
        return (padfZ[iVertex]);
399
0
    else
400
0
        return 0.0;
401
0
}
402
403
/************************************************************************/
404
/*                                getM()                                */
405
/************************************************************************/
406
407
/**
408
 * \brief Get measure at vertex.
409
 *
410
 * Returns the M (measure) value at the indicated vertex.  If no M
411
 * value is available, 0.0 is returned.
412
 *
413
 * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
414
 *
415
 * @return M value.
416
 */
417
418
double OGRSimpleCurve::getM(int iVertex) const
419
420
0
{
421
0
    if (padfM != nullptr && iVertex >= 0 && iVertex < nPointCount &&
422
0
        (flags & OGR_G_MEASURED))
423
0
        return (padfM[iVertex]);
424
0
    else
425
0
        return 0.0;
426
0
}
427
428
/************************************************************************/
429
/*                            setNumPoints()                            */
430
/************************************************************************/
431
432
/**
433
 * \brief Set number of points in geometry.
434
 *
435
 * This method primary exists to preset the number of points in a linestring
436
 * geometry before setPoint() is used to assign them to avoid reallocating
437
 * the array larger with each call to addPoint().
438
 *
439
 * This method has no SFCOM analog.
440
 *
441
 * @param nNewPointCount the new number of points for geometry.
442
 * @param bZeroizeNewContent whether to set to zero the new elements of arrays
443
 *                           that are extended.
444
 * @return (since 3.10) true in case of success, false in case of memory allocation error
445
 */
446
447
bool OGRSimpleCurve::setNumPoints(int nNewPointCount, int bZeroizeNewContent)
448
449
4.74k
{
450
4.74k
    CPLAssert(nNewPointCount >= 0);
451
452
4.74k
    if (nNewPointCount > m_nPointCapacity)
453
4.30k
    {
454
        // Overflow of sizeof(OGRRawPoint) * nNewPointCount can only occur on
455
        // 32 bit, but we don't really want to allocate 2 billion points even on
456
        // 64 bit...
457
4.30k
        if (nNewPointCount > std::numeric_limits<int>::max() /
458
4.30k
                                 static_cast<int>(sizeof(OGRRawPoint)))
459
0
        {
460
0
            CPLError(CE_Failure, CPLE_IllegalArg,
461
0
                     "Too many points on line/curve (%d points exceeds the "
462
0
                     "limit of %d points)",
463
0
                     nNewPointCount,
464
0
                     std::numeric_limits<int>::max() /
465
0
                         static_cast<int>(sizeof(OGRRawPoint)));
466
0
            return false;
467
0
        }
468
469
        // If first allocation, just aim for nNewPointCount
470
        // Otherwise aim for nNewPointCount + nNewPointCount / 3 to have
471
        // exponential growth.
472
4.30k
        const int nNewCapacity =
473
4.30k
            (nPointCount == 0 ||
474
4.30k
             nNewPointCount > std::numeric_limits<int>::max() /
475
0
                                      static_cast<int>(sizeof(OGRRawPoint)) -
476
0
                                  nNewPointCount / 3)
477
4.30k
                ? nNewPointCount
478
4.30k
                : nNewPointCount + nNewPointCount / 3;
479
480
4.30k
        if (nPointCount == 0 && paoPoints)
481
0
        {
482
            // If there was an allocated array, but the old number of points is
483
            // 0, then free the arrays before allocating them, to avoid
484
            // potential costly recopy of useless data.
485
0
            VSIFree(paoPoints);
486
0
            paoPoints = nullptr;
487
0
            VSIFree(padfZ);
488
0
            padfZ = nullptr;
489
0
            VSIFree(padfM);
490
0
            padfM = nullptr;
491
0
            m_nPointCapacity = 0;
492
0
        }
493
494
4.30k
        OGRRawPoint *paoNewPoints = static_cast<OGRRawPoint *>(
495
4.30k
            VSI_REALLOC_VERBOSE(paoPoints, sizeof(OGRRawPoint) * nNewCapacity));
496
4.30k
        if (paoNewPoints == nullptr)
497
0
        {
498
0
            return false;
499
0
        }
500
4.30k
        paoPoints = paoNewPoints;
501
502
4.30k
        if (flags & OGR_G_3D)
503
1.13k
        {
504
1.13k
            double *padfNewZ = static_cast<double *>(
505
1.13k
                VSI_REALLOC_VERBOSE(padfZ, sizeof(double) * nNewCapacity));
506
1.13k
            if (padfNewZ == nullptr)
507
0
            {
508
0
                return false;
509
0
            }
510
1.13k
            padfZ = padfNewZ;
511
1.13k
        }
512
513
4.30k
        if (flags & OGR_G_MEASURED)
514
1.07k
        {
515
1.07k
            double *padfNewM = static_cast<double *>(
516
1.07k
                VSI_REALLOC_VERBOSE(padfM, sizeof(double) * nNewCapacity));
517
1.07k
            if (padfNewM == nullptr)
518
0
            {
519
0
                return false;
520
0
            }
521
1.07k
            padfM = padfNewM;
522
1.07k
        }
523
524
4.30k
        m_nPointCapacity = nNewCapacity;
525
4.30k
    }
526
527
4.74k
    if (nNewPointCount > nPointCount && bZeroizeNewContent)
528
0
    {
529
        // gcc 8.0 (dev) complains about -Wclass-memaccess since
530
        // OGRRawPoint() has a constructor. So use a void* pointer.  Doing
531
        // the memset() here is correct since the constructor sets to 0.  We
532
        // could instead use a std::fill(), but at every other place, we
533
        // treat this class as a regular POD (see above use of realloc())
534
0
        void *dest = static_cast<void *>(paoPoints + nPointCount);
535
0
        memset(dest, 0, sizeof(OGRRawPoint) * (nNewPointCount - nPointCount));
536
537
0
        if ((flags & OGR_G_3D) && padfZ)
538
0
            memset(padfZ + nPointCount, 0,
539
0
                   sizeof(double) * (nNewPointCount - nPointCount));
540
541
0
        if ((flags & OGR_G_MEASURED) && padfM)
542
0
            memset(padfM + nPointCount, 0,
543
0
                   sizeof(double) * (nNewPointCount - nPointCount));
544
0
    }
545
546
4.74k
    nPointCount = nNewPointCount;
547
4.74k
    return true;
548
4.74k
}
549
550
/************************************************************************/
551
/*                              setPoint()                              */
552
/************************************************************************/
553
554
/**
555
 * \brief Set the location of a vertex in line string.
556
 *
557
 * If iPoint is larger than the number of necessary the number of existing
558
 * points in the line string, the point count will be increased to
559
 * accommodate the request.
560
 *
561
 * There is no SFCOM analog to this method.
562
 *
563
 * @param iPoint the index of the vertex to assign (zero based).
564
 * @param poPoint the value to assign to the vertex.
565
 * @return (since 3.10) true in case of success, false in case of memory allocation error
566
 */
567
568
bool OGRSimpleCurve::setPoint(int iPoint, OGRPoint *poPoint)
569
570
391
{
571
391
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
572
86
        return setPoint(iPoint, poPoint->getX(), poPoint->getY(),
573
86
                        poPoint->getZ(), poPoint->getM());
574
305
    else if (flags & OGR_G_3D)
575
305
        return setPoint(iPoint, poPoint->getX(), poPoint->getY(),
576
305
                        poPoint->getZ());
577
0
    else if (flags & OGR_G_MEASURED)
578
0
        return setPointM(iPoint, poPoint->getX(), poPoint->getY(),
579
0
                         poPoint->getM());
580
0
    else
581
0
        return setPoint(iPoint, poPoint->getX(), poPoint->getY());
582
391
}
583
584
/************************************************************************/
585
/*                           CheckPointCount()                          */
586
/************************************************************************/
587
588
static inline bool CheckPointCount(int iPoint)
589
0
{
590
0
    if (iPoint == std::numeric_limits<int>::max())
591
0
    {
592
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too big point count.");
593
0
        return false;
594
0
    }
595
0
    return true;
596
0
}
597
598
/************************************************************************/
599
/*                              setPoint()                              */
600
/************************************************************************/
601
602
/**
603
 * \brief Set the location of a vertex in line string.
604
 *
605
 * If iPoint is larger than the number of necessary the number of existing
606
 * points in the line string, the point count will be increased to
607
 * accommodate the request.
608
 *
609
 * There is no SFCOM analog to this method.
610
 *
611
 * @param iPoint the index of the vertex to assign (zero based).
612
 * @param xIn input X coordinate to assign.
613
 * @param yIn input Y coordinate to assign.
614
 * @param zIn input Z coordinate to assign (defaults to zero).
615
 * @return (since 3.10) true in case of success, false in case of memory allocation error
616
 */
617
618
bool OGRSimpleCurve::setPoint(int iPoint, double xIn, double yIn, double zIn)
619
620
305
{
621
305
    if (!(flags & OGR_G_3D))
622
0
    {
623
0
        if (!Make3D())
624
0
            return false;
625
0
    }
626
627
305
    if (iPoint >= nPointCount)
628
0
    {
629
0
        if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
630
0
            return false;
631
0
    }
632
305
#ifdef DEBUG
633
305
    if (paoPoints == nullptr)
634
0
        return false;
635
305
#endif
636
637
305
    paoPoints[iPoint].x = xIn;
638
305
    paoPoints[iPoint].y = yIn;
639
640
305
    if (padfZ != nullptr)
641
305
    {
642
305
        padfZ[iPoint] = zIn;
643
305
    }
644
305
    return true;
645
305
}
646
647
/**
648
 * \brief Set the location of a vertex in line string.
649
 *
650
 * If iPoint is larger than the number of necessary the number of existing
651
 * points in the line string, the point count will be increased to
652
 * accommodate the request.
653
 *
654
 * There is no SFCOM analog to this method.
655
 *
656
 * @param iPoint the index of the vertex to assign (zero based).
657
 * @param xIn input X coordinate to assign.
658
 * @param yIn input Y coordinate to assign.
659
 * @param mIn input M coordinate to assign (defaults to zero).
660
 * @return (since 3.10) true in case of success, false in case of memory allocation error
661
 */
662
663
bool OGRSimpleCurve::setPointM(int iPoint, double xIn, double yIn, double mIn)
664
665
0
{
666
0
    if (!(flags & OGR_G_MEASURED))
667
0
    {
668
0
        if (!AddM())
669
0
            return false;
670
0
    }
671
672
0
    if (iPoint >= nPointCount)
673
0
    {
674
0
        if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
675
0
            return false;
676
0
    }
677
0
#ifdef DEBUG
678
0
    if (paoPoints == nullptr)
679
0
        return false;
680
0
#endif
681
682
0
    paoPoints[iPoint].x = xIn;
683
0
    paoPoints[iPoint].y = yIn;
684
685
0
    if (padfM != nullptr)
686
0
    {
687
0
        padfM[iPoint] = mIn;
688
0
    }
689
0
    return true;
690
0
}
691
692
/**
693
 * \brief Set the location of a vertex in line string.
694
 *
695
 * If iPoint is larger than the number of necessary the number of existing
696
 * points in the line string, the point count will be increased to
697
 * accommodate the request.
698
 *
699
 * There is no SFCOM analog to this method.
700
 *
701
 * @param iPoint the index of the vertex to assign (zero based).
702
 * @param xIn input X coordinate to assign.
703
 * @param yIn input Y coordinate to assign.
704
 * @param zIn input Z coordinate to assign (defaults to zero).
705
 * @param mIn input M coordinate to assign (defaults to zero).
706
 * @return (since 3.10) true in case of success, false in case of memory allocation error
707
 */
708
709
bool OGRSimpleCurve::setPoint(int iPoint, double xIn, double yIn, double zIn,
710
                              double mIn)
711
712
86
{
713
86
    if (!(flags & OGR_G_3D))
714
0
    {
715
0
        if (!Make3D())
716
0
            return false;
717
0
    }
718
86
    if (!(flags & OGR_G_MEASURED))
719
0
    {
720
0
        if (!AddM())
721
0
            return false;
722
0
    }
723
724
86
    if (iPoint >= nPointCount)
725
0
    {
726
0
        if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
727
0
            return false;
728
0
    }
729
86
#ifdef DEBUG
730
86
    if (paoPoints == nullptr)
731
0
        return false;
732
86
#endif
733
734
86
    paoPoints[iPoint].x = xIn;
735
86
    paoPoints[iPoint].y = yIn;
736
737
86
    if (padfZ != nullptr)
738
86
    {
739
86
        padfZ[iPoint] = zIn;
740
86
    }
741
86
    if (padfM != nullptr)
742
86
    {
743
86
        padfM[iPoint] = mIn;
744
86
    }
745
86
    return true;
746
86
}
747
748
/**
749
 * \brief Set the location of a vertex in line string.
750
 *
751
 * If iPoint is larger than the number of necessary the number of existing
752
 * points in the line string, the point count will be increased to
753
 * accommodate the request.
754
 *
755
 * There is no SFCOM analog to this method.
756
 *
757
 * @param iPoint the index of the vertex to assign (zero based).
758
 * @param xIn input X coordinate to assign.
759
 * @param yIn input Y coordinate to assign.
760
 * @return (since 3.10) true in case of success, false in case of memory allocation error
761
 */
762
763
bool OGRSimpleCurve::setPoint(int iPoint, double xIn, double yIn)
764
765
0
{
766
0
    if (iPoint >= nPointCount)
767
0
    {
768
0
        if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1) || !paoPoints)
769
0
            return false;
770
0
    }
771
772
0
    paoPoints[iPoint].x = xIn;
773
0
    paoPoints[iPoint].y = yIn;
774
0
    return true;
775
0
}
776
777
/************************************************************************/
778
/*                                setZ()                                */
779
/************************************************************************/
780
781
/**
782
 * \brief Set the Z of a vertex in line string.
783
 *
784
 * If iPoint is larger than the number of necessary the number of existing
785
 * points in the line string, the point count will be increased to
786
 * accommodate the request.
787
 *
788
 * There is no SFCOM analog to this method.
789
 *
790
 * @param iPoint the index of the vertex to assign (zero based).
791
 * @param zIn input Z coordinate to assign.
792
 * @return (since 3.10) true in case of success, false in case of memory allocation error
793
 */
794
795
bool OGRSimpleCurve::setZ(int iPoint, double zIn)
796
0
{
797
0
    if (getCoordinateDimension() == 2)
798
0
    {
799
0
        if (!Make3D())
800
0
            return false;
801
0
    }
802
803
0
    if (iPoint >= nPointCount)
804
0
    {
805
0
        if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
806
0
            return false;
807
0
    }
808
809
0
    if (padfZ != nullptr)
810
0
        padfZ[iPoint] = zIn;
811
0
    return true;
812
0
}
813
814
/************************************************************************/
815
/*                                setM()                                */
816
/************************************************************************/
817
818
/**
819
 * \brief Set the M of a vertex in line string.
820
 *
821
 * If iPoint is larger than the number of necessary the number of existing
822
 * points in the line string, the point count will be increased to
823
 * accommodate the request.
824
 *
825
 * There is no SFCOM analog to this method.
826
 *
827
 * @param iPoint the index of the vertex to assign (zero based).
828
 * @param mIn input M coordinate to assign.
829
 * @return (since 3.10) true in case of success, false in case of memory allocation error
830
 */
831
832
bool OGRSimpleCurve::setM(int iPoint, double mIn)
833
0
{
834
0
    if (!(flags & OGR_G_MEASURED))
835
0
    {
836
0
        if (!AddM())
837
0
            return false;
838
0
    }
839
840
0
    if (iPoint >= nPointCount)
841
0
    {
842
0
        if (!CheckPointCount(iPoint) || !setNumPoints(iPoint + 1))
843
0
            return false;
844
0
    }
845
846
0
    if (padfM != nullptr)
847
0
        padfM[iPoint] = mIn;
848
0
    return true;
849
0
}
850
851
/************************************************************************/
852
/*                              addPoint()                              */
853
/************************************************************************/
854
855
/**
856
 * \brief Add a point to a line string.
857
 *
858
 * The vertex count of the line string is increased by one, and assigned from
859
 * the passed location value.
860
 *
861
 * There is no SFCOM analog to this method.
862
 *
863
 * @param poPoint the point to assign to the new vertex.
864
 * @return (since 3.10) true in case of success, false in case of memory allocation error
865
 */
866
867
bool OGRSimpleCurve::addPoint(const OGRPoint *poPoint)
868
869
0
{
870
0
    if (poPoint->Is3D() && poPoint->IsMeasured())
871
0
        return setPoint(nPointCount, poPoint->getX(), poPoint->getY(),
872
0
                        poPoint->getZ(), poPoint->getM());
873
0
    else if (poPoint->Is3D())
874
0
        return setPoint(nPointCount, poPoint->getX(), poPoint->getY(),
875
0
                        poPoint->getZ());
876
0
    else if (poPoint->IsMeasured())
877
0
        return setPointM(nPointCount, poPoint->getX(), poPoint->getY(),
878
0
                         poPoint->getM());
879
0
    else
880
0
        return setPoint(nPointCount, poPoint->getX(), poPoint->getY());
881
0
}
882
883
/************************************************************************/
884
/*                              addPoint()                              */
885
/************************************************************************/
886
887
/**
888
 * \brief Add a point to a line string.
889
 *
890
 * The vertex count of the line string is increased by one, and assigned from
891
 * the passed location value.
892
 *
893
 * There is no SFCOM analog to this method.
894
 *
895
 * @param x the X coordinate to assign to the new point.
896
 * @param y the Y coordinate to assign to the new point.
897
 * @param z the Z coordinate to assign to the new point (defaults to zero).
898
 * @param m the M coordinate to assign to the new point (defaults to zero).
899
 * @return (since 3.10) true in case of success, false in case of memory allocation error
900
 */
901
902
bool OGRSimpleCurve::addPoint(double x, double y, double z, double m)
903
904
0
{
905
0
    return setPoint(nPointCount, x, y, z, m);
906
0
}
907
908
/**
909
 * \brief Add a point to a line string.
910
 *
911
 * The vertex count of the line string is increased by one, and assigned from
912
 * the passed location value.
913
 *
914
 * There is no SFCOM analog to this method.
915
 *
916
 * @param x the X coordinate to assign to the new point.
917
 * @param y the Y coordinate to assign to the new point.
918
 * @param z the Z coordinate to assign to the new point (defaults to zero).
919
 * @return (since 3.10) true in case of success, false in case of memory allocation error
920
 */
921
922
bool OGRSimpleCurve::addPoint(double x, double y, double z)
923
924
0
{
925
0
    return setPoint(nPointCount, x, y, z);
926
0
}
927
928
/**
929
 * \brief Add a point to a line string.
930
 *
931
 * The vertex count of the line string is increased by one, and assigned from
932
 * the passed location value.
933
 *
934
 * There is no SFCOM analog to this method.
935
 *
936
 * @param x the X coordinate to assign to the new point.
937
 * @param y the Y coordinate to assign to the new point.
938
 * @return (since 3.10) true in case of success, false in case of memory allocation error
939
 */
940
941
bool OGRSimpleCurve::addPoint(double x, double y)
942
943
0
{
944
0
    return setPoint(nPointCount, x, y);
945
0
}
946
947
/**
948
 * \brief Add a point to a line string.
949
 *
950
 * The vertex count of the line string is increased by one, and assigned from
951
 * the passed location value.
952
 *
953
 * There is no SFCOM analog to this method.
954
 *
955
 * @param x the X coordinate to assign to the new point.
956
 * @param y the Y coordinate to assign to the new point.
957
 * @param m the M coordinate to assign to the new point.
958
 * @return (since 3.10) true in case of success, false in case of memory allocation error
959
 */
960
961
bool OGRSimpleCurve::addPointM(double x, double y, double m)
962
963
0
{
964
0
    return setPointM(nPointCount, x, y, m);
965
0
}
966
967
/************************************************************************/
968
/*                            removePoint()                             */
969
/************************************************************************/
970
971
/**
972
 * \brief Remove a point from a line string.
973
 *
974
 * There is no SFCOM analog to this method.
975
 *
976
 * @param nIndex Point index
977
 * @since GDAL 3.3
978
 */
979
980
bool OGRSimpleCurve::removePoint(int nIndex)
981
0
{
982
0
    if (nIndex < 0 || nIndex >= nPointCount)
983
0
        return false;
984
0
    if (nIndex < nPointCount - 1)
985
0
    {
986
0
        memmove(paoPoints + nIndex, paoPoints + nIndex + 1,
987
0
                sizeof(OGRRawPoint) * (nPointCount - 1 - nIndex));
988
0
        if (padfZ)
989
0
        {
990
0
            memmove(padfZ + nIndex, padfZ + nIndex + 1,
991
0
                    sizeof(double) * (nPointCount - 1 - nIndex));
992
0
        }
993
0
        if (padfM)
994
0
        {
995
0
            memmove(padfM + nIndex, padfM + nIndex + 1,
996
0
                    sizeof(double) * (nPointCount - 1 - nIndex));
997
0
        }
998
0
    }
999
0
    nPointCount--;
1000
0
    return true;
1001
0
}
1002
1003
/************************************************************************/
1004
/*                             setPointsM()                             */
1005
/************************************************************************/
1006
1007
/**
1008
 * \brief Assign all points in a line string.
1009
 *
1010
 * This method clears any existing points assigned to this line string,
1011
 * and assigns a whole new set.  It is the most efficient way of assigning
1012
 * the value of a line string.
1013
 *
1014
 * There is no SFCOM analog to this method.
1015
 *
1016
 * @param nPointsIn number of points being passed in paoPointsIn
1017
 * @param paoPointsIn list of points being assigned.
1018
 * @param padfMIn the M values that go with the points.
1019
 * @return (since 3.10) true in case of success, false in case of memory allocation error
1020
 */
1021
1022
bool OGRSimpleCurve::setPointsM(int nPointsIn, const OGRRawPoint *paoPointsIn,
1023
                                const double *padfMIn)
1024
1025
1.22k
{
1026
1.22k
    if (!setNumPoints(nPointsIn, FALSE)
1027
1.22k
#ifdef DEBUG
1028
1.22k
        || paoPoints == nullptr
1029
1.22k
#endif
1030
1.22k
    )
1031
0
        return false;
1032
1033
1.22k
    if (nPointsIn)
1034
1.22k
    {
1035
1.22k
        const void *pUnaligned = paoPointsIn;
1036
1.22k
        memcpy(paoPoints, pUnaligned, sizeof(OGRRawPoint) * nPointsIn);
1037
1.22k
    }
1038
1039
    /* -------------------------------------------------------------------- */
1040
    /*      Check measures.                                                 */
1041
    /* -------------------------------------------------------------------- */
1042
1.22k
    if (padfMIn == nullptr && (flags & OGR_G_MEASURED))
1043
0
    {
1044
0
        RemoveM();
1045
0
    }
1046
1.22k
    else if (padfMIn)
1047
1.22k
    {
1048
1.22k
        if (!AddM())
1049
0
            return false;
1050
1.22k
        if (padfM && nPointsIn)
1051
1.22k
        {
1052
1.22k
            const void *pUnaligned = padfMIn;
1053
1.22k
            memcpy(padfM, pUnaligned, sizeof(double) * nPointsIn);
1054
1.22k
        }
1055
1.22k
    }
1056
1.22k
    return true;
1057
1.22k
}
1058
1059
/************************************************************************/
1060
/*                             setPoints()                              */
1061
/************************************************************************/
1062
1063
/**
1064
 * \brief Assign all points in a line string.
1065
 *
1066
 * This method clears any existing points assigned to this line string,
1067
 * and assigns a whole new set.  It is the most efficient way of assigning
1068
 * the value of a line string.
1069
 *
1070
 * There is no SFCOM analog to this method.
1071
 *
1072
 * @param nPointsIn number of points being passed in paoPointsIn
1073
 * @param paoPointsIn list of points being assigned.
1074
 * @param padfZIn the Z values that go with the points.
1075
 * @param padfMIn the M values that go with the points.
1076
 * @return (since 3.10) true in case of success, false in case of memory allocation error
1077
 */
1078
1079
bool OGRSimpleCurve::setPoints(int nPointsIn, const OGRRawPoint *paoPointsIn,
1080
                               const double *padfZIn, const double *padfMIn)
1081
1082
792
{
1083
792
    if (!setNumPoints(nPointsIn, FALSE)
1084
792
#ifdef DEBUG
1085
792
        || paoPoints == nullptr
1086
792
#endif
1087
792
    )
1088
0
        return false;
1089
1090
792
    if (nPointsIn)
1091
792
    {
1092
792
        const void *pUnaligned = paoPointsIn;
1093
792
        memcpy(paoPoints, pUnaligned, sizeof(OGRRawPoint) * nPointsIn);
1094
792
    }
1095
1096
    /* -------------------------------------------------------------------- */
1097
    /*      Check 2D/3D.                                                    */
1098
    /* -------------------------------------------------------------------- */
1099
792
    if (padfZIn == nullptr && getCoordinateDimension() > 2)
1100
0
    {
1101
0
        Make2D();
1102
0
    }
1103
792
    else if (padfZIn)
1104
792
    {
1105
792
        if (!Make3D())
1106
0
            return false;
1107
792
        if (padfZ && nPointsIn)
1108
792
        {
1109
792
            const void *pUnaligned = padfZIn;
1110
792
            memcpy(padfZ, pUnaligned, sizeof(double) * nPointsIn);
1111
792
        }
1112
792
    }
1113
1114
    /* -------------------------------------------------------------------- */
1115
    /*      Check measures.                                                 */
1116
    /* -------------------------------------------------------------------- */
1117
792
    if (padfMIn == nullptr && (flags & OGR_G_MEASURED))
1118
0
    {
1119
0
        RemoveM();
1120
0
    }
1121
792
    else if (padfMIn)
1122
792
    {
1123
792
        if (!AddM())
1124
0
            return false;
1125
792
        if (padfM && nPointsIn)
1126
792
        {
1127
792
            const void *pUnaligned = padfMIn;
1128
792
            memcpy(padfM, pUnaligned, sizeof(double) * nPointsIn);
1129
792
        }
1130
792
    }
1131
792
    return true;
1132
792
}
1133
1134
/************************************************************************/
1135
/*                             setPoints()                              */
1136
/************************************************************************/
1137
1138
/**
1139
 * \brief Assign all points in a line string.
1140
 *
1141
 * This method clears any existing points assigned to this line string,
1142
 * and assigns a whole new set.  It is the most efficient way of assigning
1143
 * the value of a line string.
1144
 *
1145
 * There is no SFCOM analog to this method.
1146
 *
1147
 * @param nPointsIn number of points being passed in paoPointsIn
1148
 * @param paoPointsIn list of points being assigned.
1149
 * @param padfZIn the Z values that go with the points (optional, may be NULL).
1150
 * @return (since 3.10) true in case of success, false in case of memory allocation error
1151
 */
1152
1153
bool OGRSimpleCurve::setPoints(int nPointsIn, const OGRRawPoint *paoPointsIn,
1154
                               const double *padfZIn)
1155
1156
2.28k
{
1157
2.28k
    if (!setNumPoints(nPointsIn, FALSE)
1158
2.28k
#ifdef DEBUG
1159
2.28k
        || paoPoints == nullptr
1160
2.28k
#endif
1161
2.28k
    )
1162
0
        return false;
1163
1164
2.28k
    if (nPointsIn)
1165
2.28k
    {
1166
2.28k
        const void *pUnaligned = paoPointsIn;
1167
2.28k
        memcpy(paoPoints, pUnaligned, sizeof(OGRRawPoint) * nPointsIn);
1168
2.28k
    }
1169
1170
    /* -------------------------------------------------------------------- */
1171
    /*      Check 2D/3D.                                                    */
1172
    /* -------------------------------------------------------------------- */
1173
2.28k
    if (padfZIn == nullptr && getCoordinateDimension() > 2)
1174
0
    {
1175
0
        Make2D();
1176
0
    }
1177
2.28k
    else if (padfZIn)
1178
1.95k
    {
1179
1.95k
        if (!Make3D())
1180
0
            return false;
1181
1.95k
        if (padfZ && nPointsIn)
1182
1.95k
        {
1183
1.95k
            const void *pUnaligned = padfZIn;
1184
1.95k
            memcpy(padfZ, pUnaligned, sizeof(double) * nPointsIn);
1185
1.95k
        }
1186
1.95k
    }
1187
2.28k
    return true;
1188
2.28k
}
1189
1190
/************************************************************************/
1191
/*                             setPoints()                              */
1192
/************************************************************************/
1193
1194
/**
1195
 * \brief Assign all points in a line string.
1196
 *
1197
 * This method clear any existing points assigned to this line string,
1198
 * and assigns a whole new set.
1199
 *
1200
 * There is no SFCOM analog to this method.
1201
 *
1202
 * @param nPointsIn number of points being passed in padfX and padfY.
1203
 * @param padfX list of X coordinates of points being assigned.
1204
 * @param padfY list of Y coordinates of points being assigned.
1205
 * @param padfZIn list of Z coordinates of points being assigned (defaults to
1206
 * NULL for 2D objects).
1207
 * @return (since 3.10) true in case of success, false in case of memory allocation error
1208
 */
1209
1210
bool OGRSimpleCurve::setPoints(int nPointsIn, const double *padfX,
1211
                               const double *padfY, const double *padfZIn)
1212
1213
0
{
1214
    /* -------------------------------------------------------------------- */
1215
    /*      Check 2D/3D.                                                    */
1216
    /* -------------------------------------------------------------------- */
1217
0
    if (padfZIn == nullptr)
1218
0
        Make2D();
1219
0
    else
1220
0
    {
1221
0
        if (!Make3D())
1222
0
            return false;
1223
0
    }
1224
1225
    /* -------------------------------------------------------------------- */
1226
    /*      Assign values.                                                  */
1227
    /* -------------------------------------------------------------------- */
1228
0
    if (!setNumPoints(nPointsIn, FALSE))
1229
0
        return false;
1230
1231
0
    for (int i = 0; i < nPointsIn; i++)
1232
0
    {
1233
0
        paoPoints[i].x = padfX[i];
1234
0
        paoPoints[i].y = padfY[i];
1235
0
    }
1236
1237
0
    if (padfZ && padfZIn && nPointsIn)
1238
0
    {
1239
0
        const void *pUnaligned = padfZIn;
1240
0
        memcpy(padfZ, pUnaligned, sizeof(double) * nPointsIn);
1241
0
    }
1242
0
    return true;
1243
0
}
1244
1245
/************************************************************************/
1246
/*                             setPointsM()                             */
1247
/************************************************************************/
1248
1249
/**
1250
 * \brief Assign all points in a line string.
1251
 *
1252
 * This method clear any existing points assigned to this line string,
1253
 * and assigns a whole new set.
1254
 *
1255
 * There is no SFCOM analog to this method.
1256
 *
1257
 * @param nPointsIn number of points being passed in padfX and padfY.
1258
 * @param padfX list of X coordinates of points being assigned.
1259
 * @param padfY list of Y coordinates of points being assigned.
1260
 * @param padfMIn list of M coordinates of points being assigned.
1261
 * @return (since 3.10) true in case of success, false in case of memory allocation error
1262
 */
1263
1264
bool OGRSimpleCurve::setPointsM(int nPointsIn, const double *padfX,
1265
                                const double *padfY, const double *padfMIn)
1266
1267
0
{
1268
    /* -------------------------------------------------------------------- */
1269
    /*      Check 2D/3D.                                                    */
1270
    /* -------------------------------------------------------------------- */
1271
0
    if (padfMIn == nullptr)
1272
0
        RemoveM();
1273
0
    else
1274
0
    {
1275
0
        if (!AddM())
1276
0
            return false;
1277
0
    }
1278
1279
    /* -------------------------------------------------------------------- */
1280
    /*      Assign values.                                                  */
1281
    /* -------------------------------------------------------------------- */
1282
0
    if (!setNumPoints(nPointsIn, FALSE))
1283
0
        return false;
1284
1285
0
    for (int i = 0; i < nPointsIn; i++)
1286
0
    {
1287
0
        paoPoints[i].x = padfX[i];
1288
0
        paoPoints[i].y = padfY[i];
1289
0
    }
1290
1291
0
    if (padfMIn && padfM && nPointsIn)
1292
0
    {
1293
0
        const void *pUnaligned = padfMIn;
1294
0
        memcpy(padfM, pUnaligned, sizeof(double) * nPointsIn);
1295
0
    }
1296
0
    return true;
1297
0
}
1298
1299
/************************************************************************/
1300
/*                             setPoints()                              */
1301
/************************************************************************/
1302
1303
/**
1304
 * \brief Assign all points in a line string.
1305
 *
1306
 * This method clear any existing points assigned to this line string,
1307
 * and assigns a whole new set.
1308
 *
1309
 * There is no SFCOM analog to this method.
1310
 *
1311
 * @param nPointsIn number of points being passed in padfX and padfY.
1312
 * @param padfX list of X coordinates of points being assigned.
1313
 * @param padfY list of Y coordinates of points being assigned.
1314
 * @param padfZIn list of Z coordinates of points being assigned.
1315
 * @param padfMIn list of M coordinates of points being assigned.
1316
 * @return (since 3.10) true in case of success, false in case of memory allocation error
1317
 */
1318
1319
bool OGRSimpleCurve::setPoints(int nPointsIn, const double *padfX,
1320
                               const double *padfY, const double *padfZIn,
1321
                               const double *padfMIn)
1322
1323
0
{
1324
    /* -------------------------------------------------------------------- */
1325
    /*      Check 2D/3D.                                                    */
1326
    /* -------------------------------------------------------------------- */
1327
0
    if (padfZIn == nullptr)
1328
0
        Make2D();
1329
0
    else
1330
0
    {
1331
0
        if (!Make3D())
1332
0
            return false;
1333
0
    }
1334
1335
    /* -------------------------------------------------------------------- */
1336
    /*      Check measures.                                                 */
1337
    /* -------------------------------------------------------------------- */
1338
0
    if (padfMIn == nullptr)
1339
0
        RemoveM();
1340
0
    else
1341
0
    {
1342
0
        if (!AddM())
1343
0
            return false;
1344
0
    }
1345
1346
    /* -------------------------------------------------------------------- */
1347
    /*      Assign values.                                                  */
1348
    /* -------------------------------------------------------------------- */
1349
0
    if (!setNumPoints(nPointsIn, FALSE))
1350
0
        return false;
1351
1352
0
    for (int i = 0; i < nPointsIn; i++)
1353
0
    {
1354
0
        paoPoints[i].x = padfX[i];
1355
0
        paoPoints[i].y = padfY[i];
1356
0
    }
1357
1358
0
    if (padfZ != nullptr && padfZIn && nPointsIn)
1359
0
    {
1360
0
        const void *pUnaligned = padfZIn;
1361
0
        memcpy(padfZ, pUnaligned, sizeof(double) * nPointsIn);
1362
0
    }
1363
0
    if (padfM != nullptr && padfMIn && nPointsIn)
1364
0
    {
1365
0
        const void *pUnaligned = padfMIn;
1366
0
        memcpy(padfM, pUnaligned, sizeof(double) * nPointsIn);
1367
0
    }
1368
0
    return true;
1369
0
}
1370
1371
/************************************************************************/
1372
/*                          getPoints()                                 */
1373
/************************************************************************/
1374
1375
/**
1376
 * \brief Returns all points of line string.
1377
 *
1378
 * This method copies all points into user list. This list must be at
1379
 * least sizeof(OGRRawPoint) * OGRGeometry::getNumPoints() byte in size.
1380
 * It also copies all Z coordinates.
1381
 *
1382
 * There is no SFCOM analog to this method.
1383
 *
1384
 * @param paoPointsOut a buffer into which the points is written.
1385
 * @param padfZOut the Z values that go with the points (optional, may be NULL).
1386
 */
1387
1388
void OGRSimpleCurve::getPoints(OGRRawPoint *paoPointsOut,
1389
                               double *padfZOut) const
1390
0
{
1391
0
    if (!paoPointsOut || nPointCount == 0)
1392
0
        return;
1393
1394
0
    {
1395
0
        void *pUnaligned = paoPointsOut;
1396
0
        memcpy(pUnaligned, paoPoints, sizeof(OGRRawPoint) * nPointCount);
1397
0
    }
1398
1399
    /* -------------------------------------------------------------------- */
1400
    /*      Check 2D/3D.                                                    */
1401
    /* -------------------------------------------------------------------- */
1402
0
    if (padfZOut)
1403
0
    {
1404
0
        void *pUnaligned = padfZOut;
1405
0
        if (padfZ)
1406
0
            memcpy(pUnaligned, padfZ, sizeof(double) * nPointCount);
1407
0
        else
1408
0
            memset(pUnaligned, 0, sizeof(double) * nPointCount);
1409
0
    }
1410
0
}
1411
1412
/**
1413
 * \brief Returns all points of line string.
1414
 *
1415
 * This method copies all points into user arrays. The user provides the
1416
 * stride between 2 consecutive elements of the array.
1417
 *
1418
 * On some CPU architectures, care must be taken so that the arrays are properly
1419
 * aligned.
1420
 *
1421
 * There is no SFCOM analog to this method.
1422
 *
1423
 * @param pabyX a buffer of at least (nXStride * nPointCount) bytes, may be
1424
 * NULL.
1425
 * @param nXStride the number of bytes between 2 elements of pabyX.
1426
 * @param pabyY a buffer of at least (nYStride * nPointCount) bytes, may be
1427
 * NULL.
1428
 * @param nYStride the number of bytes between 2 elements of pabyY.
1429
 * @param pabyZ a buffer of at last size (nZStride * nPointCount) bytes, may be
1430
 * NULL.
1431
 * @param nZStride the number of bytes between 2 elements of pabyZ.
1432
 * @param pabyM a buffer of at last size (nMStride * nPointCount) bytes, may be
1433
 * NULL.
1434
 * @param nMStride the number of bytes between 2 elements of pabyM.
1435
 *
1436
 * @since OGR 2.1.0
1437
 */
1438
1439
void OGRSimpleCurve::getPoints(void *pabyX, int nXStride, void *pabyY,
1440
                               int nYStride, void *pabyZ, int nZStride,
1441
                               void *pabyM, int nMStride) const
1442
860
{
1443
860
    if (pabyX != nullptr && nXStride == 0)
1444
0
        return;
1445
860
    if (pabyY != nullptr && nYStride == 0)
1446
0
        return;
1447
860
    if (pabyZ != nullptr && nZStride == 0)
1448
0
        return;
1449
860
    if (pabyM != nullptr && nMStride == 0)
1450
0
        return;
1451
860
    if (nXStride == sizeof(OGRRawPoint) && nYStride == sizeof(OGRRawPoint) &&
1452
860
        static_cast<char *>(pabyY) ==
1453
0
            static_cast<char *>(pabyX) + sizeof(double) &&
1454
860
        (pabyZ == nullptr || nZStride == sizeof(double)))
1455
0
    {
1456
0
        getPoints(static_cast<OGRRawPoint *>(pabyX),
1457
0
                  static_cast<double *>(pabyZ));
1458
0
    }
1459
860
    else
1460
860
    {
1461
4.55k
        for (int i = 0; i < nPointCount; i++)
1462
3.69k
        {
1463
3.69k
            if (pabyX)
1464
3.69k
                *reinterpret_cast<double *>(static_cast<char *>(pabyX) +
1465
3.69k
                                            i * nXStride) = paoPoints[i].x;
1466
3.69k
            if (pabyY)
1467
3.69k
                *reinterpret_cast<double *>(static_cast<char *>(pabyY) +
1468
3.69k
                                            i * nYStride) = paoPoints[i].y;
1469
3.69k
        }
1470
1471
860
        if (pabyZ)
1472
860
        {
1473
860
            if (nZStride == sizeof(double))
1474
0
            {
1475
0
                if (padfZ)
1476
0
                    memcpy(pabyZ, padfZ, sizeof(double) * nPointCount);
1477
0
                else
1478
0
                    memset(pabyZ, 0, sizeof(double) * nPointCount);
1479
0
            }
1480
860
            else
1481
860
            {
1482
4.55k
                for (int i = 0; i < nPointCount; i++)
1483
3.69k
                {
1484
3.69k
                    *reinterpret_cast<double *>(static_cast<char *>(pabyZ) +
1485
3.69k
                                                i * nZStride) =
1486
3.69k
                        (padfZ) ? padfZ[i] : 0.0;
1487
3.69k
                }
1488
860
            }
1489
860
        }
1490
860
    }
1491
860
    if (pabyM)
1492
0
    {
1493
0
        if (nMStride == sizeof(double))
1494
0
        {
1495
0
            if (padfM)
1496
0
                memcpy(pabyM, padfM, sizeof(double) * nPointCount);
1497
0
            else
1498
0
                memset(pabyM, 0, sizeof(double) * nPointCount);
1499
0
        }
1500
0
        else
1501
0
        {
1502
0
            for (int i = 0; i < nPointCount; i++)
1503
0
            {
1504
0
                *reinterpret_cast<double *>(static_cast<char *>(pabyM) +
1505
0
                                            i * nMStride) =
1506
0
                    (padfM) ? padfM[i] : 0.0;
1507
0
            }
1508
0
        }
1509
0
    }
1510
860
}
1511
1512
/************************************************************************/
1513
/*                           reversePoints()                            */
1514
/************************************************************************/
1515
1516
/**
1517
 * \brief Reverse point order.
1518
 *
1519
 * This method updates the points in this line string in place
1520
 * reversing the point ordering (first for last, etc).
1521
 */
1522
1523
void OGRSimpleCurve::reversePoints()
1524
1525
188
{
1526
468
    for (int i = 0; i < nPointCount / 2; i++)
1527
280
    {
1528
280
        std::swap(paoPoints[i], paoPoints[nPointCount - i - 1]);
1529
280
        if (padfZ)
1530
280
        {
1531
280
            std::swap(padfZ[i], padfZ[nPointCount - i - 1]);
1532
280
        }
1533
1534
280
        if (padfM)
1535
61
        {
1536
61
            std::swap(padfM[i], padfM[nPointCount - i - 1]);
1537
61
        }
1538
280
    }
1539
188
}
1540
1541
/************************************************************************/
1542
/*                          addSubLineString()                          */
1543
/************************************************************************/
1544
1545
/**
1546
 * \brief Add a segment of another linestring to this one.
1547
 *
1548
 * Adds the request range of vertices to the end of this line string
1549
 * in an efficient manner.  If the nStartVertex is larger than the
1550
 * nEndVertex then the vertices will be reversed as they are copied.
1551
 *
1552
 * @param poOtherLine the other OGRLineString.
1553
 * @param nStartVertex the first vertex to copy, defaults to 0 to start
1554
 * with the first vertex in the other linestring.
1555
 * @param nEndVertex the last vertex to copy, defaults to -1 indicating
1556
 * the last vertex of the other line string.
1557
 */
1558
1559
void OGRSimpleCurve::addSubLineString(const OGRLineString *poOtherLine,
1560
                                      int nStartVertex, int nEndVertex)
1561
1562
0
{
1563
0
    int nOtherLineNumPoints = poOtherLine->getNumPoints();
1564
0
    if (nOtherLineNumPoints == 0)
1565
0
        return;
1566
1567
    /* -------------------------------------------------------------------- */
1568
    /*      Do a bit of argument defaulting and validation.                 */
1569
    /* -------------------------------------------------------------------- */
1570
0
    if (nEndVertex == -1)
1571
0
        nEndVertex = nOtherLineNumPoints - 1;
1572
1573
0
    if (nStartVertex < 0 || nEndVertex < 0 ||
1574
0
        nStartVertex >= nOtherLineNumPoints ||
1575
0
        nEndVertex >= nOtherLineNumPoints)
1576
0
    {
1577
0
        CPLAssert(false);
1578
0
        return;
1579
0
    }
1580
1581
    /* -------------------------------------------------------------------- */
1582
    /*      Grow this linestring to hold the additional points.             */
1583
    /* -------------------------------------------------------------------- */
1584
0
    int nOldPoints = nPointCount;
1585
0
    int nPointsToAdd = std::abs(nEndVertex - nStartVertex) + 1;
1586
1587
0
    if (!setNumPoints(nPointsToAdd + nOldPoints, FALSE)
1588
0
#ifdef DEBUG
1589
0
        || paoPoints == nullptr
1590
0
#endif
1591
0
    )
1592
0
        return;
1593
1594
    /* -------------------------------------------------------------------- */
1595
    /*      Copy the x/y points - forward copies use memcpy.                */
1596
    /* -------------------------------------------------------------------- */
1597
0
    if (nEndVertex >= nStartVertex)
1598
0
    {
1599
0
        memcpy(paoPoints + nOldPoints, poOtherLine->paoPoints + nStartVertex,
1600
0
               sizeof(OGRRawPoint) * nPointsToAdd);
1601
0
        if (poOtherLine->padfZ != nullptr)
1602
0
        {
1603
0
            Make3D();
1604
0
            if (padfZ != nullptr)
1605
0
            {
1606
0
                memcpy(padfZ + nOldPoints, poOtherLine->padfZ + nStartVertex,
1607
0
                       sizeof(double) * nPointsToAdd);
1608
0
            }
1609
0
        }
1610
0
        if (poOtherLine->padfM != nullptr)
1611
0
        {
1612
0
            AddM();
1613
0
            if (padfM != nullptr)
1614
0
            {
1615
0
                memcpy(padfM + nOldPoints, poOtherLine->padfM + nStartVertex,
1616
0
                       sizeof(double) * nPointsToAdd);
1617
0
            }
1618
0
        }
1619
0
    }
1620
1621
    /* -------------------------------------------------------------------- */
1622
    /*      Copy the x/y points - reverse copies done double by double.     */
1623
    /* -------------------------------------------------------------------- */
1624
0
    else
1625
0
    {
1626
0
        for (int i = 0; i < nPointsToAdd; i++)
1627
0
        {
1628
0
            paoPoints[i + nOldPoints].x =
1629
0
                poOtherLine->paoPoints[nStartVertex - i].x;
1630
0
            paoPoints[i + nOldPoints].y =
1631
0
                poOtherLine->paoPoints[nStartVertex - i].y;
1632
0
        }
1633
1634
0
        if (poOtherLine->padfZ != nullptr)
1635
0
        {
1636
0
            Make3D();
1637
0
            if (padfZ != nullptr)
1638
0
            {
1639
0
                for (int i = 0; i < nPointsToAdd; i++)
1640
0
                {
1641
0
                    padfZ[i + nOldPoints] =
1642
0
                        poOtherLine->padfZ[nStartVertex - i];
1643
0
                }
1644
0
            }
1645
0
        }
1646
0
        if (poOtherLine->padfM != nullptr)
1647
0
        {
1648
0
            AddM();
1649
0
            if (padfM != nullptr)
1650
0
            {
1651
0
                for (int i = 0; i < nPointsToAdd; i++)
1652
0
                {
1653
0
                    padfM[i + nOldPoints] =
1654
0
                        poOtherLine->padfM[nStartVertex - i];
1655
0
                }
1656
0
            }
1657
0
        }
1658
0
    }
1659
0
}
1660
1661
/************************************************************************/
1662
/*                           importFromWkb()                            */
1663
/*                                                                      */
1664
/*      Initialize from serialized stream in well known binary          */
1665
/*      format.                                                         */
1666
/************************************************************************/
1667
1668
OGRErr OGRSimpleCurve::importFromWkb(const unsigned char *pabyData,
1669
                                     size_t nSize, OGRwkbVariant eWkbVariant,
1670
                                     size_t &nBytesConsumedOut)
1671
1672
0
{
1673
0
    OGRwkbByteOrder eByteOrder;
1674
0
    size_t nDataOffset = 0;
1675
0
    int nNewNumPoints = 0;
1676
1677
0
    nBytesConsumedOut = 0;
1678
0
    OGRErr eErr = importPreambleOfCollectionFromWkb(pabyData, nSize,
1679
0
                                                    nDataOffset, eByteOrder, 16,
1680
0
                                                    nNewNumPoints, eWkbVariant);
1681
0
    if (eErr != OGRERR_NONE)
1682
0
        return eErr;
1683
1684
    // Check if the wkb stream buffer is big enough to store
1685
    // fetched number of points.
1686
0
    const int dim = CoordinateDimension();
1687
0
    const size_t nPointSize = dim * sizeof(double);
1688
0
    if (nNewNumPoints < 0 ||
1689
0
        static_cast<size_t>(nNewNumPoints) >
1690
0
            std::numeric_limits<size_t>::max() / nPointSize)
1691
0
    {
1692
0
        return OGRERR_CORRUPT_DATA;
1693
0
    }
1694
0
    const size_t nBufferMinSize = nPointSize * nNewNumPoints;
1695
1696
0
    if (nSize != static_cast<size_t>(-1) && nBufferMinSize > nSize)
1697
0
    {
1698
0
        CPLError(CE_Failure, CPLE_AppDefined,
1699
0
                 "Length of input WKB is too small");
1700
0
        return OGRERR_NOT_ENOUGH_DATA;
1701
0
    }
1702
1703
0
    if (!setNumPoints(nNewNumPoints, FALSE))
1704
0
        return OGRERR_NOT_ENOUGH_MEMORY;
1705
1706
0
    nBytesConsumedOut = 9 + 8 * static_cast<size_t>(nPointCount) *
1707
0
                                (2 + ((flags & OGR_G_3D) ? 1 : 0) +
1708
0
                                 ((flags & OGR_G_MEASURED) ? 1 : 0));
1709
1710
    /* -------------------------------------------------------------------- */
1711
    /*      Get the vertex.                                                 */
1712
    /* -------------------------------------------------------------------- */
1713
0
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
1714
0
    {
1715
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1716
0
        {
1717
0
            memcpy(paoPoints + i, pabyData + 9 + i * 32, 16);
1718
0
            memcpy(padfZ + i, pabyData + 9 + 16 + i * 32, 8);
1719
0
            memcpy(padfM + i, pabyData + 9 + 24 + i * 32, 8);
1720
0
        }
1721
0
    }
1722
0
    else if (flags & OGR_G_MEASURED)
1723
0
    {
1724
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1725
0
        {
1726
0
            memcpy(paoPoints + i, pabyData + 9 + i * 24, 16);
1727
0
            memcpy(padfM + i, pabyData + 9 + 16 + i * 24, 8);
1728
0
        }
1729
0
    }
1730
0
    else if (flags & OGR_G_3D)
1731
0
    {
1732
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1733
0
        {
1734
0
            memcpy(paoPoints + i, pabyData + 9 + i * 24, 16);
1735
0
            memcpy(padfZ + i, pabyData + 9 + 16 + i * 24, 8);
1736
0
        }
1737
0
    }
1738
0
    else if (nPointCount)
1739
0
    {
1740
0
        memcpy(paoPoints, pabyData + 9, 16 * static_cast<size_t>(nPointCount));
1741
0
    }
1742
1743
    /* -------------------------------------------------------------------- */
1744
    /*      Byte swap if needed.                                            */
1745
    /* -------------------------------------------------------------------- */
1746
0
    if (OGR_SWAP(eByteOrder))
1747
0
    {
1748
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1749
0
        {
1750
0
            CPL_SWAPDOUBLE(&(paoPoints[i].x));
1751
0
            CPL_SWAPDOUBLE(&(paoPoints[i].y));
1752
0
        }
1753
1754
0
        if (flags & OGR_G_3D)
1755
0
        {
1756
0
            for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1757
0
            {
1758
0
                CPL_SWAPDOUBLE(padfZ + i);
1759
0
            }
1760
0
        }
1761
1762
0
        if (flags & OGR_G_MEASURED)
1763
0
        {
1764
0
            for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1765
0
            {
1766
0
                CPL_SWAPDOUBLE(padfM + i);
1767
0
            }
1768
0
        }
1769
0
    }
1770
1771
0
    return OGRERR_NONE;
1772
0
}
1773
1774
/************************************************************************/
1775
/*                            exportToWkb()                             */
1776
/*                                                                      */
1777
/*      Build a well known binary representation of this object.        */
1778
/************************************************************************/
1779
1780
OGRErr OGRSimpleCurve::exportToWkb(unsigned char *pabyData,
1781
                                   const OGRwkbExportOptions *psOptions) const
1782
1783
0
{
1784
0
    if (psOptions == nullptr)
1785
0
    {
1786
0
        static const OGRwkbExportOptions defaultOptions;
1787
0
        psOptions = &defaultOptions;
1788
0
    }
1789
1790
    /* -------------------------------------------------------------------- */
1791
    /*      Set the byte order.                                             */
1792
    /* -------------------------------------------------------------------- */
1793
0
    pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(
1794
0
        static_cast<unsigned char>(psOptions->eByteOrder));
1795
1796
    /* -------------------------------------------------------------------- */
1797
    /*      Set the geometry feature type.                                  */
1798
    /* -------------------------------------------------------------------- */
1799
0
    GUInt32 nGType = getGeometryType();
1800
1801
0
    if (psOptions->eWkbVariant == wkbVariantPostGIS1)
1802
0
    {
1803
0
        nGType = wkbFlatten(nGType);
1804
0
        if (Is3D())
1805
            // Explicitly set wkb25DBit.
1806
0
            nGType =
1807
0
                static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
1808
0
        if (IsMeasured())
1809
0
            nGType = static_cast<OGRwkbGeometryType>(nGType | 0x40000000);
1810
0
    }
1811
0
    else if (psOptions->eWkbVariant == wkbVariantIso)
1812
0
        nGType = getIsoGeometryType();
1813
1814
0
    if (psOptions->eByteOrder == wkbNDR)
1815
0
    {
1816
0
        CPL_LSBPTR32(&nGType);
1817
0
    }
1818
0
    else
1819
0
    {
1820
0
        CPL_MSBPTR32(&nGType);
1821
0
    }
1822
1823
0
    memcpy(pabyData + 1, &nGType, 4);
1824
1825
    /* -------------------------------------------------------------------- */
1826
    /*      Copy in the data count.                                         */
1827
    /* -------------------------------------------------------------------- */
1828
0
    memcpy(pabyData + 5, &nPointCount, 4);
1829
1830
    /* -------------------------------------------------------------------- */
1831
    /*      Copy in the raw data.                                           */
1832
    /* -------------------------------------------------------------------- */
1833
0
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
1834
0
    {
1835
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1836
0
        {
1837
0
            memcpy(pabyData + 9 + 32 * i, paoPoints + i, 16);
1838
0
            memcpy(pabyData + 9 + 16 + 32 * i, padfZ + i, 8);
1839
0
            memcpy(pabyData + 9 + 24 + 32 * i, padfM + i, 8);
1840
0
        }
1841
0
        OGRRoundCoordinatesIEEE754XYValues<32>(
1842
0
            psOptions->sPrecision.nXYBitPrecision, pabyData + 9, nPointCount);
1843
0
        OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nZBitPrecision,
1844
0
                                       pabyData + 9 + 2 * sizeof(uint64_t),
1845
0
                                       nPointCount);
1846
0
        OGRRoundCoordinatesIEEE754<32>(psOptions->sPrecision.nMBitPrecision,
1847
0
                                       pabyData + 9 + 3 * sizeof(uint64_t),
1848
0
                                       nPointCount);
1849
0
    }
1850
0
    else if (flags & OGR_G_MEASURED)
1851
0
    {
1852
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1853
0
        {
1854
0
            memcpy(pabyData + 9 + 24 * i, paoPoints + i, 16);
1855
0
            memcpy(pabyData + 9 + 16 + 24 * i, padfM + i, 8);
1856
0
        }
1857
0
        OGRRoundCoordinatesIEEE754XYValues<24>(
1858
0
            psOptions->sPrecision.nXYBitPrecision, pabyData + 9, nPointCount);
1859
0
        OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nMBitPrecision,
1860
0
                                       pabyData + 9 + 2 * sizeof(uint64_t),
1861
0
                                       nPointCount);
1862
0
    }
1863
0
    else if (flags & OGR_G_3D)
1864
0
    {
1865
0
        for (size_t i = 0; i < static_cast<size_t>(nPointCount); i++)
1866
0
        {
1867
0
            memcpy(pabyData + 9 + 24 * i, paoPoints + i, 16);
1868
0
            memcpy(pabyData + 9 + 16 + 24 * i, padfZ + i, 8);
1869
0
        }
1870
0
        OGRRoundCoordinatesIEEE754XYValues<24>(
1871
0
            psOptions->sPrecision.nXYBitPrecision, pabyData + 9, nPointCount);
1872
0
        OGRRoundCoordinatesIEEE754<24>(psOptions->sPrecision.nZBitPrecision,
1873
0
                                       pabyData + 9 + 2 * sizeof(uint64_t),
1874
0
                                       nPointCount);
1875
0
    }
1876
0
    else if (nPointCount)
1877
0
    {
1878
0
        memcpy(pabyData + 9, paoPoints, 16 * static_cast<size_t>(nPointCount));
1879
0
        OGRRoundCoordinatesIEEE754XYValues<16>(
1880
0
            psOptions->sPrecision.nXYBitPrecision, pabyData + 9, nPointCount);
1881
0
    }
1882
1883
    /* -------------------------------------------------------------------- */
1884
    /*      Swap if needed.                                                 */
1885
    /* -------------------------------------------------------------------- */
1886
0
    if (OGR_SWAP(psOptions->eByteOrder))
1887
0
    {
1888
0
        const int nCount = CPL_SWAP32(nPointCount);
1889
0
        memcpy(pabyData + 5, &nCount, 4);
1890
1891
0
        const size_t nCoords =
1892
0
            CoordinateDimension() * static_cast<size_t>(nPointCount);
1893
0
        for (size_t i = 0; i < nCoords; i++)
1894
0
        {
1895
0
            CPL_SWAP64PTR(pabyData + 9 + 8 * i);
1896
0
        }
1897
0
    }
1898
1899
0
    return OGRERR_NONE;
1900
0
}
1901
1902
/************************************************************************/
1903
/*                           importFromWkt()                            */
1904
/*                                                                      */
1905
/*      Instantiate from well known text format.  Currently this is     */
1906
/*      `LINESTRING ( x y, x y, ...)',                                  */
1907
/************************************************************************/
1908
1909
OGRErr OGRSimpleCurve::importFromWkt(const char **ppszInput)
1910
1911
444
{
1912
444
    int bHasZ = FALSE;
1913
444
    int bHasM = FALSE;
1914
444
    bool bIsEmpty = false;
1915
444
    const OGRErr eErr =
1916
444
        importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
1917
444
    flags = 0;
1918
444
    if (eErr != OGRERR_NONE)
1919
4
        return eErr;
1920
440
    if (bHasZ)
1921
21
        flags |= OGR_G_3D;
1922
440
    if (bHasM)
1923
40
        flags |= OGR_G_MEASURED;
1924
440
    if (bIsEmpty)
1925
39
    {
1926
39
        return OGRERR_NONE;
1927
39
    }
1928
1929
401
    const char *pszInput = *ppszInput;
1930
1931
    /* -------------------------------------------------------------------- */
1932
    /*      Read the point list.                                            */
1933
    /* -------------------------------------------------------------------- */
1934
401
    int flagsFromInput = flags;
1935
401
    nPointCount = 0;
1936
1937
401
    pszInput =
1938
401
        OGRWktReadPointsM(pszInput, &paoPoints, &padfZ, &padfM, &flagsFromInput,
1939
401
                          &m_nPointCapacity, &nPointCount);
1940
401
    if (pszInput == nullptr)
1941
25
        return OGRERR_CORRUPT_DATA;
1942
1943
376
    if ((flagsFromInput & OGR_G_3D) && !(flags & OGR_G_3D))
1944
270
    {
1945
270
        if (!set3D(TRUE))
1946
0
            return OGRERR_NOT_ENOUGH_MEMORY;
1947
270
    }
1948
376
    if ((flagsFromInput & OGR_G_MEASURED) && !(flags & OGR_G_MEASURED))
1949
132
    {
1950
132
        if (!setMeasured(TRUE))
1951
0
            return OGRERR_NOT_ENOUGH_MEMORY;
1952
132
    }
1953
1954
376
    *ppszInput = pszInput;
1955
1956
376
    return OGRERR_NONE;
1957
376
}
1958
1959
//! @cond Doxygen_Suppress
1960
/************************************************************************/
1961
/*                        importFromWKTListOnly()                       */
1962
/*                                                                      */
1963
/*      Instantiate from "(x y, x y, ...)"                              */
1964
/************************************************************************/
1965
1966
OGRErr OGRSimpleCurve::importFromWKTListOnly(const char **ppszInput, int bHasZ,
1967
                                             int bHasM,
1968
                                             OGRRawPoint *&paoPointsIn,
1969
                                             int &nMaxPointsIn,
1970
                                             double *&padfZIn)
1971
1972
2.38k
{
1973
2.38k
    const char *pszInput = *ppszInput;
1974
1975
    /* -------------------------------------------------------------------- */
1976
    /*      Read the point list.                                            */
1977
    /* -------------------------------------------------------------------- */
1978
2.38k
    int flagsFromInput = flags;
1979
2.38k
    int nPointCountRead = 0;
1980
2.38k
    double *padfMIn = nullptr;
1981
2.38k
    if (flagsFromInput == 0)  // Flags was not set, this is not called by us.
1982
2.38k
    {
1983
2.38k
        if (bHasM)
1984
879
            flagsFromInput |= OGR_G_MEASURED;
1985
2.38k
        if (bHasZ)
1986
6
            flagsFromInput |= OGR_G_3D;
1987
2.38k
    }
1988
1989
2.38k
    pszInput =
1990
2.38k
        OGRWktReadPointsM(pszInput, &paoPointsIn, &padfZIn, &padfMIn,
1991
2.38k
                          &flagsFromInput, &nMaxPointsIn, &nPointCountRead);
1992
1993
2.38k
    if (pszInput == nullptr)
1994
250
    {
1995
250
        CPLFree(padfMIn);
1996
250
        return OGRERR_CORRUPT_DATA;
1997
250
    }
1998
2.13k
    if ((flagsFromInput & OGR_G_3D) && !(flags & OGR_G_3D))
1999
1.13k
    {
2000
1.13k
        flags |= OGR_G_3D;
2001
1.13k
        bHasZ = TRUE;
2002
1.13k
    }
2003
2.13k
    if ((flagsFromInput & OGR_G_MEASURED) && !(flags & OGR_G_MEASURED))
2004
1.07k
    {
2005
1.07k
        flags |= OGR_G_MEASURED;
2006
1.07k
        bHasM = TRUE;
2007
1.07k
    }
2008
2009
2.13k
    *ppszInput = pszInput;
2010
2011
2.13k
    if (bHasM && bHasZ)
2012
429
        setPoints(nPointCountRead, paoPointsIn, padfZIn, padfMIn);
2013
1.70k
    else if (bHasM && !bHasZ)
2014
641
        setPointsM(nPointCountRead, paoPointsIn, padfMIn);
2015
1.06k
    else
2016
1.06k
        setPoints(nPointCountRead, paoPointsIn, padfZIn);
2017
2018
2.13k
    CPLFree(padfMIn);
2019
2020
2.13k
    return OGRERR_NONE;
2021
2.38k
}
2022
2023
//! @endcond
2024
2025
/************************************************************************/
2026
/*                            exportToWkt()                             */
2027
/*                                                                      */
2028
/*      Translate this structure into its well known text format       */
2029
/*      equivalent.  This could be made a lot more CPU efficient.       */
2030
/************************************************************************/
2031
2032
std::string OGRSimpleCurve::exportToWkt(const OGRWktOptions &opts,
2033
                                        OGRErr *err) const
2034
0
{
2035
    // LINEARRING or LINESTRING or CIRCULARSTRING
2036
0
    std::string wkt = getGeometryName();
2037
0
    wkt += wktTypeString(opts.variant);
2038
0
    if (IsEmpty())
2039
0
    {
2040
0
        wkt += "EMPTY";
2041
0
    }
2042
0
    else
2043
0
    {
2044
0
        wkt += '(';
2045
2046
0
        OGRBoolean hasZ = Is3D();
2047
0
        OGRBoolean hasM =
2048
0
            (opts.variant != wkbVariantIso ? FALSE : IsMeasured());
2049
2050
0
        try
2051
0
        {
2052
0
            const int nOrdinatesPerVertex =
2053
0
                2 + ((hasZ) ? 1 : 0) + ((hasM) ? 1 : 0);
2054
            // At least 2 bytes per ordinate: one for the value,
2055
            // and one for the separator...
2056
0
            wkt.reserve(wkt.size() + 2 * static_cast<size_t>(nPointCount) *
2057
0
                                         nOrdinatesPerVertex);
2058
2059
0
            for (int i = 0; i < nPointCount; i++)
2060
0
            {
2061
0
                if (i > 0)
2062
0
                    wkt += ',';
2063
2064
0
                wkt += OGRMakeWktCoordinateM(
2065
0
                    paoPoints[i].x, paoPoints[i].y, padfZ ? padfZ[i] : 0.0,
2066
0
                    padfM ? padfM[i] : 0.0, hasZ, hasM, opts);
2067
0
            }
2068
0
            wkt += ')';
2069
0
        }
2070
0
        catch (const std::bad_alloc &e)
2071
0
        {
2072
0
            CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
2073
0
            if (err)
2074
0
                *err = OGRERR_FAILURE;
2075
0
            return std::string();
2076
0
        }
2077
0
    }
2078
0
    if (err)
2079
0
        *err = OGRERR_NONE;
2080
0
    return wkt;
2081
0
}
2082
2083
/************************************************************************/
2084
/*                             get_Length()                             */
2085
/*                                                                      */
2086
/*      For now we return a simple euclidean 2D distance.               */
2087
/************************************************************************/
2088
2089
double OGRSimpleCurve::get_Length() const
2090
2091
0
{
2092
0
    double dfLength = 0.0;
2093
2094
0
    for (int i = 0; i < nPointCount - 1; i++)
2095
0
    {
2096
2097
0
        const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
2098
0
        const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
2099
0
        dfLength += sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
2100
0
    }
2101
2102
0
    return dfLength;
2103
0
}
2104
2105
/************************************************************************/
2106
/*                             StartPoint()                             */
2107
/************************************************************************/
2108
2109
void OGRSimpleCurve::StartPoint(OGRPoint *poPoint) const
2110
2111
1.48k
{
2112
1.48k
    getPoint(0, poPoint);
2113
1.48k
}
2114
2115
/************************************************************************/
2116
/*                              EndPoint()                              */
2117
/************************************************************************/
2118
2119
void OGRSimpleCurve::EndPoint(OGRPoint *poPoint) const
2120
2121
1.68k
{
2122
1.68k
    getPoint(nPointCount - 1, poPoint);
2123
1.68k
}
2124
2125
/************************************************************************/
2126
/*                               Value()                                */
2127
/*                                                                      */
2128
/*      Get an interpolated point at some distance along the curve.     */
2129
/************************************************************************/
2130
2131
void OGRSimpleCurve::Value(double dfDistance, OGRPoint *poPoint) const
2132
2133
0
{
2134
0
    if (dfDistance < 0)
2135
0
    {
2136
0
        StartPoint(poPoint);
2137
0
        return;
2138
0
    }
2139
2140
0
    double dfLength = 0.0;
2141
2142
0
    for (int i = 0; i < nPointCount - 1; i++)
2143
0
    {
2144
0
        const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
2145
0
        const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
2146
0
        const double dfSegLength =
2147
0
            sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
2148
2149
0
        if (dfSegLength > 0)
2150
0
        {
2151
0
            if ((dfLength <= dfDistance) &&
2152
0
                ((dfLength + dfSegLength) >= dfDistance))
2153
0
            {
2154
0
                double dfRatio = (dfDistance - dfLength) / dfSegLength;
2155
2156
0
                poPoint->setX(paoPoints[i].x * (1 - dfRatio) +
2157
0
                              paoPoints[i + 1].x * dfRatio);
2158
0
                poPoint->setY(paoPoints[i].y * (1 - dfRatio) +
2159
0
                              paoPoints[i + 1].y * dfRatio);
2160
2161
0
                if (getCoordinateDimension() == 3)
2162
0
                    poPoint->setZ(padfZ[i] * (1 - dfRatio) +
2163
0
                                  padfZ[i + 1] * dfRatio);
2164
2165
0
                return;
2166
0
            }
2167
2168
0
            dfLength += dfSegLength;
2169
0
        }
2170
0
    }
2171
2172
0
    EndPoint(poPoint);
2173
0
}
2174
2175
/************************************************************************/
2176
/*                              Project()                               */
2177
/*                                                                      */
2178
/* Return distance of point projected on line from origin of this line. */
2179
/************************************************************************/
2180
2181
/**
2182
 * \brief Project point on linestring.
2183
 *
2184
 * The input point projected on linestring. This is the shortest distance
2185
 * from point to the linestring. The distance from begin of linestring to
2186
 * the point projection returned.
2187
 *
2188
 * This method is built on the GEOS library. Check it for the
2189
 * definition of the geometry operation.
2190
 * If OGR is built without the GEOS library, this method will always return -1,
2191
 * issuing a CPLE_NotSupported error.
2192
 *
2193
 * @return a distance from the begin of the linestring to the projected point.
2194
 */
2195
2196
double OGRSimpleCurve::Project(const OGRPoint *poPoint) const
2197
2198
0
{
2199
0
    double dfResult = -1;
2200
0
#ifndef HAVE_GEOS
2201
0
    CPL_IGNORE_RET_VAL(poPoint);
2202
0
    CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
2203
0
    return dfResult;
2204
#else
2205
    GEOSGeom hThisGeosGeom = nullptr;
2206
    GEOSGeom hPointGeosGeom = nullptr;
2207
2208
    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
2209
    hThisGeosGeom = exportToGEOS(hGEOSCtxt);
2210
    hPointGeosGeom = poPoint->exportToGEOS(hGEOSCtxt);
2211
    if (hThisGeosGeom != nullptr && hPointGeosGeom != nullptr)
2212
    {
2213
        dfResult = GEOSProject_r(hGEOSCtxt, hThisGeosGeom, hPointGeosGeom);
2214
    }
2215
    GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
2216
    GEOSGeom_destroy_r(hGEOSCtxt, hPointGeosGeom);
2217
    freeGEOSContext(hGEOSCtxt);
2218
2219
    return dfResult;
2220
2221
#endif  // HAVE_GEOS
2222
0
}
2223
2224
/************************************************************************/
2225
/*                            getSubLine()                              */
2226
/*                                                                      */
2227
/*  Extracts a portion of this OGRLineString into a new OGRLineString.  */
2228
/************************************************************************/
2229
2230
/**
2231
 * \brief Get the portion of linestring.
2232
 *
2233
 * The portion of the linestring extracted to new one. The input distances
2234
 * (maybe present as ratio of length of linestring) set begin and end of
2235
 * extracted portion.
2236
 *
2237
 * @param dfDistanceFrom The distance from the origin of linestring, where the
2238
 * subline should begins
2239
 * @param dfDistanceTo The distance from the origin of linestring, where the
2240
 * subline should ends
2241
 * @param bAsRatio The flag indicating that distances are the ratio of the
2242
 * linestring length.
2243
 *
2244
 * @return a newly allocated linestring now owned by the caller, or NULL on
2245
 * failure.
2246
 *
2247
 * @since OGR 1.11.0
2248
 */
2249
2250
OGRLineString *OGRSimpleCurve::getSubLine(double dfDistanceFrom,
2251
                                          double dfDistanceTo,
2252
                                          int bAsRatio) const
2253
2254
0
{
2255
0
    auto poNewLineString = std::make_unique<OGRLineString>();
2256
2257
0
    poNewLineString->assignSpatialReference(getSpatialReference());
2258
0
    poNewLineString->setCoordinateDimension(getCoordinateDimension());
2259
2260
0
    const double dfLen = get_Length();
2261
0
    if (bAsRatio == TRUE)
2262
0
    {
2263
        // Convert to real distance.
2264
0
        dfDistanceFrom *= dfLen;
2265
0
        dfDistanceTo *= dfLen;
2266
0
    }
2267
2268
0
    if (dfDistanceFrom < 0)
2269
0
        dfDistanceFrom = 0;
2270
0
    if (dfDistanceTo > dfLen)
2271
0
        dfDistanceTo = dfLen;
2272
2273
0
    if (dfDistanceFrom > dfDistanceTo || dfDistanceFrom >= dfLen)
2274
0
    {
2275
0
        CPLError(CE_Failure, CPLE_IllegalArg, "Input distances are invalid.");
2276
2277
0
        return nullptr;
2278
0
    }
2279
2280
0
    double dfLength = 0.0;
2281
2282
    // Get first point.
2283
2284
0
    int i = 0;  // Used after if blocks.
2285
0
    if (dfDistanceFrom == 0)
2286
0
    {
2287
0
        bool bRet;
2288
0
        if (getCoordinateDimension() == 3)
2289
0
            bRet = poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y,
2290
0
                                             padfZ[0]);
2291
0
        else
2292
0
            bRet = poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y);
2293
0
        if (!bRet)
2294
0
            return nullptr;
2295
0
    }
2296
0
    else
2297
0
    {
2298
0
        for (i = 0; i < nPointCount - 1; i++)
2299
0
        {
2300
0
            const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
2301
0
            const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
2302
0
            const double dfSegLength =
2303
0
                sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
2304
2305
0
            if (dfSegLength > 0)
2306
0
            {
2307
0
                if ((dfLength <= dfDistanceFrom) &&
2308
0
                    ((dfLength + dfSegLength) >= dfDistanceFrom))
2309
0
                {
2310
0
                    double dfRatio = (dfDistanceFrom - dfLength) / dfSegLength;
2311
2312
0
                    double dfX = paoPoints[i].x * (1 - dfRatio) +
2313
0
                                 paoPoints[i + 1].x * dfRatio;
2314
0
                    double dfY = paoPoints[i].y * (1 - dfRatio) +
2315
0
                                 paoPoints[i + 1].y * dfRatio;
2316
2317
0
                    bool bRet;
2318
0
                    if (getCoordinateDimension() == 3)
2319
0
                    {
2320
0
                        bRet = poNewLineString->addPoint(
2321
0
                            dfX, dfY,
2322
0
                            padfZ[i] * (1 - dfRatio) + padfZ[i + 1] * dfRatio);
2323
0
                    }
2324
0
                    else
2325
0
                    {
2326
0
                        bRet = poNewLineString->addPoint(dfX, dfY);
2327
0
                    }
2328
0
                    if (!bRet)
2329
0
                        return nullptr;
2330
2331
                    // Check if dfDistanceTo is in same segment.
2332
0
                    if (dfLength <= dfDistanceTo &&
2333
0
                        (dfLength + dfSegLength) >= dfDistanceTo)
2334
0
                    {
2335
0
                        dfRatio = (dfDistanceTo - dfLength) / dfSegLength;
2336
2337
0
                        dfX = paoPoints[i].x * (1 - dfRatio) +
2338
0
                              paoPoints[i + 1].x * dfRatio;
2339
0
                        dfY = paoPoints[i].y * (1 - dfRatio) +
2340
0
                              paoPoints[i + 1].y * dfRatio;
2341
2342
0
                        if (getCoordinateDimension() == 3)
2343
0
                        {
2344
0
                            bRet = poNewLineString->addPoint(
2345
0
                                dfX, dfY,
2346
0
                                padfZ[i] * (1 - dfRatio) +
2347
0
                                    padfZ[i + 1] * dfRatio);
2348
0
                        }
2349
0
                        else
2350
0
                        {
2351
0
                            bRet = poNewLineString->addPoint(dfX, dfY);
2352
0
                        }
2353
2354
0
                        if (!bRet || poNewLineString->getNumPoints() < 2)
2355
0
                        {
2356
0
                            return nullptr;
2357
0
                        }
2358
2359
0
                        return poNewLineString.release();
2360
0
                    }
2361
0
                    i++;
2362
0
                    dfLength += dfSegLength;
2363
0
                    break;
2364
0
                }
2365
2366
0
                dfLength += dfSegLength;
2367
0
            }
2368
0
        }
2369
0
    }
2370
2371
    // Add points.
2372
0
    for (; i < nPointCount - 1; i++)
2373
0
    {
2374
0
        bool bRet;
2375
0
        if (getCoordinateDimension() == 3)
2376
0
            bRet = poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y,
2377
0
                                             padfZ[i]);
2378
0
        else
2379
0
            bRet = poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y);
2380
0
        if (!bRet)
2381
0
            return nullptr;
2382
2383
0
        const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
2384
0
        const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
2385
0
        const double dfSegLength =
2386
0
            sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
2387
2388
0
        if (dfSegLength > 0)
2389
0
        {
2390
0
            if ((dfLength <= dfDistanceTo) &&
2391
0
                ((dfLength + dfSegLength) >= dfDistanceTo))
2392
0
            {
2393
0
                const double dfRatio = (dfDistanceTo - dfLength) / dfSegLength;
2394
2395
0
                const double dfX = paoPoints[i].x * (1 - dfRatio) +
2396
0
                                   paoPoints[i + 1].x * dfRatio;
2397
0
                const double dfY = paoPoints[i].y * (1 - dfRatio) +
2398
0
                                   paoPoints[i + 1].y * dfRatio;
2399
2400
0
                if (getCoordinateDimension() == 3)
2401
0
                    bRet = poNewLineString->addPoint(
2402
0
                        dfX, dfY,
2403
0
                        padfZ[i] * (1 - dfRatio) + padfZ[i + 1] * dfRatio);
2404
0
                else
2405
0
                    bRet = poNewLineString->addPoint(dfX, dfY);
2406
0
                if (!bRet)
2407
0
                    return nullptr;
2408
2409
0
                return poNewLineString.release();
2410
0
            }
2411
2412
0
            dfLength += dfSegLength;
2413
0
        }
2414
0
    }
2415
2416
0
    bool bRet;
2417
0
    if (getCoordinateDimension() == 3)
2418
0
        bRet = poNewLineString->addPoint(paoPoints[nPointCount - 1].x,
2419
0
                                         paoPoints[nPointCount - 1].y,
2420
0
                                         padfZ[nPointCount - 1]);
2421
0
    else
2422
0
        bRet = poNewLineString->addPoint(paoPoints[nPointCount - 1].x,
2423
0
                                         paoPoints[nPointCount - 1].y);
2424
2425
0
    if (!bRet || poNewLineString->getNumPoints() < 2)
2426
0
    {
2427
0
        return nullptr;
2428
0
    }
2429
2430
0
    return poNewLineString.release();
2431
0
}
2432
2433
/************************************************************************/
2434
/*                            getEnvelope()                             */
2435
/************************************************************************/
2436
2437
void OGRSimpleCurve::getEnvelope(OGREnvelope *psEnvelope) const
2438
2439
0
{
2440
0
    if (IsEmpty())
2441
0
    {
2442
0
        psEnvelope->MinX = 0.0;
2443
0
        psEnvelope->MaxX = 0.0;
2444
0
        psEnvelope->MinY = 0.0;
2445
0
        psEnvelope->MaxY = 0.0;
2446
0
        return;
2447
0
    }
2448
2449
0
    double dfMinX = paoPoints[0].x;
2450
0
    double dfMaxX = paoPoints[0].x;
2451
0
    double dfMinY = paoPoints[0].y;
2452
0
    double dfMaxY = paoPoints[0].y;
2453
2454
0
    for (int iPoint = 1; iPoint < nPointCount; iPoint++)
2455
0
    {
2456
0
        if (dfMaxX < paoPoints[iPoint].x)
2457
0
            dfMaxX = paoPoints[iPoint].x;
2458
0
        if (dfMaxY < paoPoints[iPoint].y)
2459
0
            dfMaxY = paoPoints[iPoint].y;
2460
0
        if (dfMinX > paoPoints[iPoint].x)
2461
0
            dfMinX = paoPoints[iPoint].x;
2462
0
        if (dfMinY > paoPoints[iPoint].y)
2463
0
            dfMinY = paoPoints[iPoint].y;
2464
0
    }
2465
2466
0
    psEnvelope->MinX = dfMinX;
2467
0
    psEnvelope->MaxX = dfMaxX;
2468
0
    psEnvelope->MinY = dfMinY;
2469
0
    psEnvelope->MaxY = dfMaxY;
2470
0
}
2471
2472
/************************************************************************/
2473
/*                            getEnvelope()                             */
2474
/************************************************************************/
2475
2476
void OGRSimpleCurve::getEnvelope(OGREnvelope3D *psEnvelope) const
2477
2478
0
{
2479
0
    getEnvelope(static_cast<OGREnvelope *>(psEnvelope));
2480
2481
0
    if (IsEmpty() || padfZ == nullptr)
2482
0
    {
2483
0
        psEnvelope->MinZ = 0.0;
2484
0
        psEnvelope->MaxZ = 0.0;
2485
0
        return;
2486
0
    }
2487
2488
0
    double dfMinZ = padfZ[0];
2489
0
    double dfMaxZ = padfZ[0];
2490
2491
0
    for (int iPoint = 1; iPoint < nPointCount; iPoint++)
2492
0
    {
2493
0
        if (dfMinZ > padfZ[iPoint])
2494
0
            dfMinZ = padfZ[iPoint];
2495
0
        if (dfMaxZ < padfZ[iPoint])
2496
0
            dfMaxZ = padfZ[iPoint];
2497
0
    }
2498
2499
0
    psEnvelope->MinZ = dfMinZ;
2500
0
    psEnvelope->MaxZ = dfMaxZ;
2501
0
}
2502
2503
/************************************************************************/
2504
/*                               Equals()                               */
2505
/************************************************************************/
2506
2507
OGRBoolean OGRSimpleCurve::Equals(const OGRGeometry *poOther) const
2508
2509
0
{
2510
0
    if (poOther == this)
2511
0
        return TRUE;
2512
2513
0
    if (poOther->getGeometryType() != getGeometryType())
2514
0
        return FALSE;
2515
2516
0
    if (IsEmpty() && poOther->IsEmpty())
2517
0
        return TRUE;
2518
2519
    // TODO(schwehr): Test the SRS.
2520
2521
0
    auto poOLine = poOther->toSimpleCurve();
2522
0
    if (getNumPoints() != poOLine->getNumPoints())
2523
0
        return FALSE;
2524
2525
0
    for (int iPoint = 0; iPoint < getNumPoints(); iPoint++)
2526
0
    {
2527
0
        if (getX(iPoint) != poOLine->getX(iPoint) ||
2528
0
            getY(iPoint) != poOLine->getY(iPoint) ||
2529
0
            getZ(iPoint) != poOLine->getZ(iPoint))
2530
0
            return FALSE;
2531
0
    }
2532
2533
0
    return TRUE;
2534
0
}
2535
2536
/************************************************************************/
2537
/*                             transform()                              */
2538
/************************************************************************/
2539
2540
OGRErr OGRSimpleCurve::transform(OGRCoordinateTransformation *poCT)
2541
2542
0
{
2543
    /* -------------------------------------------------------------------- */
2544
    /*   Make a copy of the points to operate on, so as to be able to       */
2545
    /*   keep only valid reprojected points if partial reprojection enabled */
2546
    /*   or keeping intact the original geometry if only full reprojection  */
2547
    /*   allowed.                                                           */
2548
    /* -------------------------------------------------------------------- */
2549
0
    double *xyz = static_cast<double *>(
2550
0
        VSI_MALLOC_VERBOSE(sizeof(double) * nPointCount * 3));
2551
0
    int *pabSuccess =
2552
0
        static_cast<int *>(VSI_CALLOC_VERBOSE(sizeof(int), nPointCount));
2553
0
    if (xyz == nullptr || pabSuccess == nullptr)
2554
0
    {
2555
0
        VSIFree(xyz);
2556
0
        VSIFree(pabSuccess);
2557
0
        return OGRERR_NOT_ENOUGH_MEMORY;
2558
0
    }
2559
2560
0
    for (int i = 0; i < nPointCount; i++)
2561
0
    {
2562
0
        xyz[i] = paoPoints[i].x;
2563
0
        xyz[i + nPointCount] = paoPoints[i].y;
2564
0
        if (padfZ)
2565
0
            xyz[i + nPointCount * 2] = padfZ[i];
2566
0
        else
2567
0
            xyz[i + nPointCount * 2] = 0.0;
2568
0
    }
2569
2570
    /* -------------------------------------------------------------------- */
2571
    /*      Transform and reapply.                                          */
2572
    /* -------------------------------------------------------------------- */
2573
0
    poCT->Transform(nPointCount, xyz, xyz + nPointCount, xyz + nPointCount * 2,
2574
0
                    nullptr, pabSuccess);
2575
2576
0
    const char *pszEnablePartialReprojection = nullptr;
2577
2578
0
    int j = 0;  // Used after for.
2579
0
    for (int i = 0; i < nPointCount; i++)
2580
0
    {
2581
0
        if (pabSuccess[i])
2582
0
        {
2583
0
            xyz[j] = xyz[i];
2584
0
            xyz[j + nPointCount] = xyz[i + nPointCount];
2585
0
            xyz[j + 2 * nPointCount] = xyz[i + 2 * nPointCount];
2586
0
            j++;
2587
0
        }
2588
0
        else
2589
0
        {
2590
0
            if (pszEnablePartialReprojection == nullptr)
2591
0
                pszEnablePartialReprojection = CPLGetConfigOption(
2592
0
                    "OGR_ENABLE_PARTIAL_REPROJECTION", nullptr);
2593
0
            if (pszEnablePartialReprojection == nullptr)
2594
0
            {
2595
0
                static bool bHasWarned = false;
2596
0
                if (!bHasWarned)
2597
0
                {
2598
                    // Check that there is at least one valid reprojected point
2599
                    // and issue an error giving an hint to use
2600
                    // OGR_ENABLE_PARTIAL_REPROJECTION.
2601
0
                    bool bHasOneValidPoint = j != 0;
2602
0
                    for (; i < nPointCount && !bHasOneValidPoint; i++)
2603
0
                    {
2604
0
                        if (pabSuccess[i])
2605
0
                            bHasOneValidPoint = true;
2606
0
                    }
2607
0
                    if (bHasOneValidPoint)
2608
0
                    {
2609
0
                        bHasWarned = true;
2610
0
                        CPLError(CE_Failure, CPLE_AppDefined,
2611
0
                                 "Full reprojection failed, but partial is "
2612
0
                                 "possible if you define "
2613
0
                                 "OGR_ENABLE_PARTIAL_REPROJECTION "
2614
0
                                 "configuration option to TRUE");
2615
0
                    }
2616
0
                }
2617
2618
0
                CPLFree(xyz);
2619
0
                CPLFree(pabSuccess);
2620
0
                return OGRERR_FAILURE;
2621
0
            }
2622
0
            else if (!CPLTestBool(pszEnablePartialReprojection))
2623
0
            {
2624
0
                CPLFree(xyz);
2625
0
                CPLFree(pabSuccess);
2626
0
                return OGRERR_FAILURE;
2627
0
            }
2628
0
        }
2629
0
    }
2630
2631
0
    if (j == 0 && nPointCount != 0)
2632
0
    {
2633
0
        CPLFree(xyz);
2634
0
        CPLFree(pabSuccess);
2635
0
        return OGRERR_FAILURE;
2636
0
    }
2637
2638
0
    setPoints(j, xyz, xyz + nPointCount,
2639
0
              (padfZ) ? xyz + nPointCount * 2 : nullptr);
2640
0
    CPLFree(xyz);
2641
0
    CPLFree(pabSuccess);
2642
2643
0
    assignSpatialReference(poCT->GetTargetCS());
2644
2645
0
    return OGRERR_NONE;
2646
0
}
2647
2648
/************************************************************************/
2649
/*                               IsEmpty()                              */
2650
/************************************************************************/
2651
2652
OGRBoolean OGRSimpleCurve::IsEmpty() const
2653
1.88k
{
2654
1.88k
    return (nPointCount == 0);
2655
1.88k
}
2656
2657
/************************************************************************/
2658
/*                     OGRSimpleCurve::segmentize()                     */
2659
/************************************************************************/
2660
2661
bool OGRSimpleCurve::segmentize(double dfMaxLength)
2662
0
{
2663
0
    if (dfMaxLength <= 0)
2664
0
    {
2665
0
        CPLError(CE_Failure, CPLE_AppDefined,
2666
0
                 "dfMaxLength must be strictly positive");
2667
0
        return false;
2668
0
    }
2669
0
    if (nPointCount < 2)
2670
0
        return true;
2671
2672
    // So as to make sure that the same line followed in both directions
2673
    // result in the same segmentized line.
2674
0
    if (paoPoints[0].x < paoPoints[nPointCount - 1].x ||
2675
0
        (paoPoints[0].x == paoPoints[nPointCount - 1].x &&
2676
0
         paoPoints[0].y < paoPoints[nPointCount - 1].y))
2677
0
    {
2678
0
        reversePoints();
2679
0
        bool bRet = segmentize(dfMaxLength);
2680
0
        reversePoints();
2681
0
        return bRet;
2682
0
    }
2683
2684
0
    int nNewPointCount = 0;
2685
0
    const double dfSquareMaxLength = dfMaxLength * dfMaxLength;
2686
2687
    // First pass to compute new number of points
2688
0
    constexpr double REL_EPSILON_LENGTH_SQUARE = 1e-5;
2689
0
    constexpr double REL_EPSILON_ROUND = 1e-2;
2690
0
    for (int i = 0; i < nPointCount; i++)
2691
0
    {
2692
0
        nNewPointCount++;
2693
2694
0
        if (i == nPointCount - 1)
2695
0
            break;
2696
2697
        // Must be kept in sync with the second pass loop
2698
0
        const double dfX = paoPoints[i + 1].x - paoPoints[i].x;
2699
0
        const double dfY = paoPoints[i + 1].y - paoPoints[i].y;
2700
0
        const double dfSquareDist = dfX * dfX + dfY * dfY;
2701
0
        if (dfSquareDist - dfSquareMaxLength >
2702
0
            REL_EPSILON_LENGTH_SQUARE * dfSquareMaxLength)
2703
0
        {
2704
0
            const double dfIntermediatePoints = floor(
2705
0
                sqrt(dfSquareDist / dfSquareMaxLength) - REL_EPSILON_ROUND);
2706
0
            const int nIntermediatePoints =
2707
0
                DoubleToIntClamp(dfIntermediatePoints);
2708
2709
            // TODO(schwehr): Can these be tighter?
2710
            // Limit allocation of paoNewPoints to a few GB of memory.
2711
            // An OGRRawPoint is 2 doubles.
2712
            // kMax is a guess of what a reasonable max might be.
2713
0
            constexpr int kMax = 2 << 26;
2714
0
            if (nNewPointCount > kMax || nIntermediatePoints > kMax)
2715
0
            {
2716
0
                CPLError(CE_Failure, CPLE_AppDefined,
2717
0
                         "Too many points in a segment: %d or %d",
2718
0
                         nNewPointCount, nIntermediatePoints);
2719
0
                return false;
2720
0
            }
2721
2722
0
            nNewPointCount += nIntermediatePoints;
2723
0
        }
2724
0
    }
2725
2726
0
    if (nPointCount == nNewPointCount)
2727
0
        return true;
2728
2729
    // Allocate new arrays
2730
0
    OGRRawPoint *paoNewPoints = static_cast<OGRRawPoint *>(
2731
0
        VSI_MALLOC_VERBOSE(sizeof(OGRRawPoint) * nNewPointCount));
2732
0
    if (paoNewPoints == nullptr)
2733
0
        return false;
2734
0
    double *padfNewZ = nullptr;
2735
0
    double *padfNewM = nullptr;
2736
0
    if (padfZ != nullptr)
2737
0
    {
2738
0
        padfNewZ = static_cast<double *>(
2739
0
            VSI_MALLOC_VERBOSE(sizeof(double) * nNewPointCount));
2740
0
        if (padfNewZ == nullptr)
2741
0
        {
2742
0
            VSIFree(paoNewPoints);
2743
0
            return false;
2744
0
        }
2745
0
    }
2746
0
    if (padfM != nullptr)
2747
0
    {
2748
0
        padfNewM = static_cast<double *>(
2749
0
            VSI_MALLOC_VERBOSE(sizeof(double) * nNewPointCount));
2750
0
        if (padfNewM == nullptr)
2751
0
        {
2752
0
            VSIFree(paoNewPoints);
2753
0
            VSIFree(padfNewZ);
2754
0
            return false;
2755
0
        }
2756
0
    }
2757
2758
    // Second pass to fill new arrays
2759
    // Must be kept in sync with the first pass loop
2760
0
    nNewPointCount = 0;
2761
0
    for (int i = 0; i < nPointCount; i++)
2762
0
    {
2763
0
        paoNewPoints[nNewPointCount] = paoPoints[i];
2764
2765
0
        if (padfZ != nullptr)
2766
0
        {
2767
0
            padfNewZ[nNewPointCount] = padfZ[i];
2768
0
        }
2769
2770
0
        if (padfM != nullptr)
2771
0
        {
2772
0
            padfNewM[nNewPointCount] = padfM[i];
2773
0
        }
2774
2775
0
        nNewPointCount++;
2776
2777
0
        if (i == nPointCount - 1)
2778
0
            break;
2779
2780
0
        const double dfX = paoPoints[i + 1].x - paoPoints[i].x;
2781
0
        const double dfY = paoPoints[i + 1].y - paoPoints[i].y;
2782
0
        const double dfSquareDist = dfX * dfX + dfY * dfY;
2783
2784
        // Must be kept in sync with the initial pass loop
2785
0
        if (dfSquareDist - dfSquareMaxLength >
2786
0
            REL_EPSILON_LENGTH_SQUARE * dfSquareMaxLength)
2787
0
        {
2788
0
            const double dfIntermediatePoints = floor(
2789
0
                sqrt(dfSquareDist / dfSquareMaxLength) - REL_EPSILON_ROUND);
2790
0
            const int nIntermediatePoints =
2791
0
                DoubleToIntClamp(dfIntermediatePoints);
2792
0
            const double dfRatioX =
2793
0
                dfX / (static_cast<double>(nIntermediatePoints) + 1);
2794
0
            const double dfRatioY =
2795
0
                dfY / (static_cast<double>(nIntermediatePoints) + 1);
2796
2797
0
            for (int j = 1; j <= nIntermediatePoints; j++)
2798
0
            {
2799
                // coverity[overflow_const]
2800
0
                const int newI = nNewPointCount + j - 1;
2801
0
                paoNewPoints[newI].x = paoPoints[i].x + j * dfRatioX;
2802
0
                paoNewPoints[newI].y = paoPoints[i].y + j * dfRatioY;
2803
0
                if (padfZ != nullptr)
2804
0
                {
2805
                    // No interpolation.
2806
0
                    padfNewZ[newI] = padfZ[i];
2807
0
                }
2808
0
                if (padfM != nullptr)
2809
0
                {
2810
                    // No interpolation.
2811
0
                    padfNewM[newI] = padfM[i];
2812
0
                }
2813
0
            }
2814
2815
0
            nNewPointCount += nIntermediatePoints;
2816
0
        }
2817
0
    }
2818
2819
0
    CPLFree(paoPoints);
2820
0
    paoPoints = paoNewPoints;
2821
0
    nPointCount = nNewPointCount;
2822
0
    m_nPointCapacity = nNewPointCount;
2823
2824
0
    if (padfZ != nullptr)
2825
0
    {
2826
0
        CPLFree(padfZ);
2827
0
        padfZ = padfNewZ;
2828
0
    }
2829
0
    if (padfM != nullptr)
2830
0
    {
2831
0
        CPLFree(padfM);
2832
0
        padfM = padfNewM;
2833
0
    }
2834
0
    return true;
2835
0
}
2836
2837
/************************************************************************/
2838
/*                               swapXY()                               */
2839
/************************************************************************/
2840
2841
void OGRSimpleCurve::swapXY()
2842
0
{
2843
0
    for (int i = 0; i < nPointCount; i++)
2844
0
    {
2845
0
        std::swap(paoPoints[i].x, paoPoints[i].y);
2846
0
    }
2847
0
}
2848
2849
/************************************************************************/
2850
/*                       OGRSimpleCurvePointIterator                    */
2851
/************************************************************************/
2852
2853
class OGRSimpleCurvePointIterator final : public OGRPointIterator
2854
{
2855
    CPL_DISALLOW_COPY_ASSIGN(OGRSimpleCurvePointIterator)
2856
2857
    const OGRSimpleCurve *poSC = nullptr;
2858
    int iCurPoint = 0;
2859
2860
  public:
2861
    explicit OGRSimpleCurvePointIterator(const OGRSimpleCurve *poSCIn)
2862
0
        : poSC(poSCIn)
2863
0
    {
2864
0
    }
2865
2866
    OGRBoolean getNextPoint(OGRPoint *p) override;
2867
};
2868
2869
/************************************************************************/
2870
/*                            getNextPoint()                            */
2871
/************************************************************************/
2872
2873
OGRBoolean OGRSimpleCurvePointIterator::getNextPoint(OGRPoint *p)
2874
0
{
2875
0
    if (iCurPoint >= poSC->getNumPoints())
2876
0
        return FALSE;
2877
0
    poSC->getPoint(iCurPoint, p);
2878
0
    iCurPoint++;
2879
0
    return TRUE;
2880
0
}
2881
2882
/************************************************************************/
2883
/*                         getPointIterator()                           */
2884
/************************************************************************/
2885
2886
OGRPointIterator *OGRSimpleCurve::getPointIterator() const
2887
0
{
2888
0
    return new OGRSimpleCurvePointIterator(this);
2889
0
}
2890
2891
/************************************************************************/
2892
/*                  OGRLineString( const OGRLineString& )               */
2893
/************************************************************************/
2894
2895
/**
2896
 * \brief Copy constructor.
2897
 *
2898
 * Note: before GDAL 2.1, only the default implementation of the constructor
2899
 * existed, which could be unsafe to use.
2900
 *
2901
 * @since GDAL 2.1
2902
 */
2903
2904
0
OGRLineString::OGRLineString(const OGRLineString &) = default;
2905
2906
/************************************************************************/
2907
/*                  OGRLineString( OGRLineString&& )                    */
2908
/************************************************************************/
2909
2910
/**
2911
 * \brief Move constructor.
2912
 *
2913
 * @since GDAL 3.11
2914
 */
2915
2916
0
OGRLineString::OGRLineString(OGRLineString &&) = default;
2917
2918
/************************************************************************/
2919
/*                    operator=( const OGRLineString& )                 */
2920
/************************************************************************/
2921
2922
/**
2923
 * \brief Assignment operator.
2924
 *
2925
 * Note: before GDAL 2.1, only the default implementation of the operator
2926
 * existed, which could be unsafe to use.
2927
 *
2928
 * @since GDAL 2.1
2929
 */
2930
2931
OGRLineString &OGRLineString::operator=(const OGRLineString &other)
2932
0
{
2933
0
    if (this != &other)
2934
0
    {
2935
0
        OGRSimpleCurve::operator=(other);
2936
0
    }
2937
0
    return *this;
2938
0
}
2939
2940
/************************************************************************/
2941
/*                    operator=( OGRLineString&& )                      */
2942
/************************************************************************/
2943
2944
/**
2945
 * \brief Move assignment operator.
2946
 *
2947
 * @since GDAL 3.11
2948
 */
2949
2950
OGRLineString &OGRLineString::operator=(OGRLineString &&other)
2951
0
{
2952
0
    if (this != &other)
2953
0
    {
2954
0
        OGRSimpleCurve::operator=(std::move(other));
2955
0
    }
2956
0
    return *this;
2957
0
}
2958
2959
/************************************************************************/
2960
/*                          getGeometryType()                           */
2961
/************************************************************************/
2962
2963
OGRwkbGeometryType OGRLineString::getGeometryType() const
2964
2965
16.1k
{
2966
16.1k
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
2967
6.00k
        return wkbLineStringZM;
2968
10.1k
    else if (flags & OGR_G_MEASURED)
2969
1.12k
        return wkbLineStringM;
2970
9.04k
    else if (flags & OGR_G_3D)
2971
8.63k
        return wkbLineString25D;
2972
405
    else
2973
405
        return wkbLineString;
2974
16.1k
}
2975
2976
/************************************************************************/
2977
/*                          getGeometryName()                           */
2978
/************************************************************************/
2979
2980
const char *OGRLineString::getGeometryName() const
2981
2982
1.94k
{
2983
1.94k
    return "LINESTRING";
2984
1.94k
}
2985
2986
/************************************************************************/
2987
/*                          curveToLine()                               */
2988
/************************************************************************/
2989
2990
OGRLineString *OGRLineString::CurveToLine(
2991
    CPL_UNUSED double /* dfMaxAngleStepSizeDegrees */,
2992
    CPL_UNUSED const char *const * /* papszOptions */) const
2993
0
{
2994
0
    return clone();
2995
0
}
2996
2997
/************************************************************************/
2998
/*                          get_LinearArea()                            */
2999
/************************************************************************/
3000
3001
/**
3002
 * \brief Compute area of ring / closed linestring.
3003
 *
3004
 * The area is computed according to Green's Theorem:
3005
 *
3006
 * Area is "Sum(x(i)*(y(i+1) - y(i-1)))/2" for i = 0 to pointCount-1,
3007
 * assuming the last point is a duplicate of the first.
3008
 *
3009
 * @return computed area.
3010
 */
3011
3012
double OGRSimpleCurve::get_LinearArea() const
3013
3014
0
{
3015
0
    if (nPointCount < 2 ||
3016
0
        (WkbSize() != 0 && /* if not a linearring, check it is closed */
3017
0
         (paoPoints[0].x != paoPoints[nPointCount - 1].x ||
3018
0
          paoPoints[0].y != paoPoints[nPointCount - 1].y)))
3019
0
    {
3020
0
        return 0;
3021
0
    }
3022
3023
0
    double dfAreaSum =
3024
0
        paoPoints[0].x * (paoPoints[1].y - paoPoints[nPointCount - 1].y);
3025
3026
0
    for (int i = 1; i < nPointCount - 1; i++)
3027
0
    {
3028
0
        dfAreaSum += paoPoints[i].x * (paoPoints[i + 1].y - paoPoints[i - 1].y);
3029
0
    }
3030
3031
0
    dfAreaSum += paoPoints[nPointCount - 1].x *
3032
0
                 (paoPoints[0].y - paoPoints[nPointCount - 2].y);
3033
3034
0
    return 0.5 * fabs(dfAreaSum);
3035
0
}
3036
3037
/************************************************************************/
3038
/*                             getCurveGeometry()                       */
3039
/************************************************************************/
3040
3041
OGRGeometry *
3042
OGRLineString::getCurveGeometry(const char *const *papszOptions) const
3043
0
{
3044
0
    return OGRGeometryFactory::curveFromLineString(this, papszOptions);
3045
0
}
3046
3047
/************************************************************************/
3048
/*                      TransferMembersAndDestroy()                     */
3049
/************************************************************************/
3050
//! @cond Doxygen_Suppress
3051
OGRLineString *OGRLineString::TransferMembersAndDestroy(OGRLineString *poSrc,
3052
                                                        OGRLineString *poDst)
3053
0
{
3054
0
    if (poSrc->Is3D())
3055
0
        poDst->flags |= OGR_G_3D;
3056
0
    if (poSrc->IsMeasured())
3057
0
        poDst->flags |= OGR_G_MEASURED;
3058
0
    poDst->assignSpatialReference(poSrc->getSpatialReference());
3059
0
    poDst->nPointCount = poSrc->nPointCount;
3060
0
    poDst->m_nPointCapacity = poSrc->m_nPointCapacity;
3061
0
    poDst->paoPoints = poSrc->paoPoints;
3062
0
    poDst->padfZ = poSrc->padfZ;
3063
0
    poDst->padfM = poSrc->padfM;
3064
0
    poSrc->nPointCount = 0;
3065
0
    poSrc->m_nPointCapacity = 0;
3066
0
    poSrc->paoPoints = nullptr;
3067
0
    poSrc->padfZ = nullptr;
3068
0
    poSrc->padfM = nullptr;
3069
0
    delete poSrc;
3070
0
    return poDst;
3071
0
}
3072
3073
//! @endcond
3074
/************************************************************************/
3075
/*                         CastToLinearRing()                           */
3076
/************************************************************************/
3077
3078
/**
3079
 * \brief Cast to linear ring.
3080
 *
3081
 * The passed in geometry is consumed and a new one returned (or NULL in case
3082
 * of failure)
3083
 *
3084
 * @param poLS the input geometry - ownership is passed to the method.
3085
 * @return new geometry.
3086
 */
3087
3088
OGRLinearRing *OGRLineString::CastToLinearRing(OGRLineString *poLS)
3089
0
{
3090
0
    if (poLS->nPointCount < 2 || !poLS->get_IsClosed())
3091
0
    {
3092
0
        CPLError(CE_Failure, CPLE_AppDefined,
3093
0
                 "Cannot convert non-closed linestring to linearring");
3094
0
        delete poLS;
3095
0
        return nullptr;
3096
0
    }
3097
0
    OGRLinearRing *poLR = new OGRLinearRing();
3098
0
    TransferMembersAndDestroy(poLS, poLR);
3099
0
    return poLR;
3100
0
}
3101
3102
/************************************************************************/
3103
/*                               clone()                                */
3104
/************************************************************************/
3105
3106
OGRLineString *OGRLineString::clone() const
3107
0
{
3108
0
    auto ret = new (std::nothrow) OGRLineString(*this);
3109
0
    if (ret)
3110
0
    {
3111
0
        if (ret->getNumPoints() != getNumPoints())
3112
0
        {
3113
0
            delete ret;
3114
0
            ret = nullptr;
3115
0
        }
3116
0
    }
3117
0
    return ret;
3118
0
}
3119
3120
//! @cond Doxygen_Suppress
3121
3122
/************************************************************************/
3123
/*                     GetCasterToLineString()                          */
3124
/************************************************************************/
3125
3126
static OGRLineString *CasterToLineString(OGRCurve *poCurve)
3127
0
{
3128
0
    return poCurve->toLineString();
3129
0
}
3130
3131
OGRCurveCasterToLineString OGRLineString::GetCasterToLineString() const
3132
0
{
3133
0
    return ::CasterToLineString;
3134
0
}
3135
3136
/************************************************************************/
3137
/*                        GetCasterToLinearRing()                       */
3138
/************************************************************************/
3139
3140
OGRLinearRing *OGRLineString::CasterToLinearRing(OGRCurve *poCurve)
3141
0
{
3142
0
    return OGRLineString::CastToLinearRing(poCurve->toLineString());
3143
0
}
3144
3145
OGRCurveCasterToLinearRing OGRLineString::GetCasterToLinearRing() const
3146
0
{
3147
0
    return OGRLineString::CasterToLinearRing;
3148
0
}
3149
3150
/************************************************************************/
3151
/*                            get_Area()                                */
3152
/************************************************************************/
3153
3154
double OGRLineString::get_Area() const
3155
0
{
3156
0
    return get_LinearArea();
3157
0
}
3158
3159
/************************************************************************/
3160
/*                           GetGeodesicInputs()                        */
3161
/************************************************************************/
3162
3163
static bool GetGeodesicInputs(const OGRLineString *poLS,
3164
                              const OGRSpatialReference *poSRSOverride,
3165
                              const char *pszComputationType, geod_geodesic &g,
3166
                              std::vector<double> &adfLat,
3167
                              std::vector<double> &adfLon)
3168
0
{
3169
0
    if (!poSRSOverride)
3170
0
        poSRSOverride = poLS->getSpatialReference();
3171
3172
0
    if (!poSRSOverride)
3173
0
    {
3174
0
        CPLError(CE_Failure, CPLE_AppDefined,
3175
0
                 "Cannot compute %s on ellipsoid due to missing SRS",
3176
0
                 pszComputationType);
3177
0
        return false;
3178
0
    }
3179
3180
0
    OGRErr eErr = OGRERR_NONE;
3181
0
    double dfSemiMajor = poSRSOverride->GetSemiMajor(&eErr);
3182
0
    if (eErr != OGRERR_NONE)
3183
0
        return false;
3184
0
    const double dfInvFlattening = poSRSOverride->GetInvFlattening(&eErr);
3185
0
    if (eErr != OGRERR_NONE)
3186
0
        return false;
3187
3188
0
    geod_init(&g, dfSemiMajor,
3189
0
              dfInvFlattening != 0 ? 1.0 / dfInvFlattening : 0.0);
3190
3191
0
    const int nPointCount = poLS->getNumPoints();
3192
0
    adfLat.reserve(nPointCount);
3193
0
    adfLon.reserve(nPointCount);
3194
3195
0
    OGRSpatialReference oGeogCRS;
3196
0
    if (oGeogCRS.CopyGeogCSFrom(poSRSOverride) != OGRERR_NONE)
3197
0
    {
3198
0
        CPLError(CE_Failure, CPLE_AppDefined,
3199
0
                 "Cannot reproject geometry to geographic CRS");
3200
0
        return false;
3201
0
    }
3202
0
    oGeogCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3203
0
    auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
3204
0
        OGRCreateCoordinateTransformation(poSRSOverride, &oGeogCRS));
3205
0
    if (!poCT)
3206
0
    {
3207
0
        CPLError(CE_Failure, CPLE_AppDefined,
3208
0
                 "Cannot reproject geometry to geographic CRS");
3209
0
        return false;
3210
0
    }
3211
0
    for (int i = 0; i < nPointCount; ++i)
3212
0
    {
3213
0
        adfLon.push_back(poLS->getX(i));
3214
0
        adfLat.push_back(poLS->getY(i));
3215
0
    }
3216
0
#ifdef __GNUC__
3217
0
#pragma GCC diagnostic push
3218
0
#pragma GCC diagnostic ignored "-Wnull-dereference"
3219
0
#endif
3220
0
    std::vector<int> anSuccess;
3221
0
    anSuccess.resize(adfLon.size());
3222
0
#ifdef __GNUC__
3223
0
#pragma GCC diagnostic pop
3224
0
#endif
3225
0
    poCT->Transform(adfLon.size(), adfLon.data(), adfLat.data(), nullptr,
3226
0
                    anSuccess.data());
3227
0
    double dfToDegrees =
3228
0
        oGeogCRS.GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
3229
0
    if (std::fabs(dfToDegrees - 1) <= 1e-10)
3230
0
        dfToDegrees = 1.0;
3231
0
    for (int i = 0; i < nPointCount; ++i)
3232
0
    {
3233
0
        if (!anSuccess[i])
3234
0
        {
3235
0
            CPLError(CE_Failure, CPLE_AppDefined,
3236
0
                     "Cannot reproject geometry to geographic CRS");
3237
0
            return false;
3238
0
        }
3239
0
        adfLon[i] *= dfToDegrees;
3240
0
        adfLat[i] *= dfToDegrees;
3241
0
    }
3242
3243
0
    return true;
3244
0
}
3245
3246
/************************************************************************/
3247
/*                        get_GeodesicArea()                            */
3248
/************************************************************************/
3249
3250
double
3251
OGRLineString::get_GeodesicArea(const OGRSpatialReference *poSRSOverride) const
3252
0
{
3253
0
    geod_geodesic g;
3254
0
    std::vector<double> adfLat;
3255
0
    std::vector<double> adfLon;
3256
0
    if (!GetGeodesicInputs(this, poSRSOverride, "area", g, adfLat, adfLon))
3257
0
        return -1.0;
3258
0
    double dfArea = -1.0;
3259
0
    geod_polygonarea(&g, adfLat.data(), adfLon.data(),
3260
0
                     static_cast<int>(adfLat.size()), &dfArea, nullptr);
3261
0
    return std::fabs(dfArea);
3262
0
}
3263
3264
/************************************************************************/
3265
/*                        get_GeodesicLength()                          */
3266
/************************************************************************/
3267
3268
double OGRLineString::get_GeodesicLength(
3269
    const OGRSpatialReference *poSRSOverride) const
3270
0
{
3271
0
    geod_geodesic g;
3272
0
    std::vector<double> adfLat;
3273
0
    std::vector<double> adfLon;
3274
0
    if (!GetGeodesicInputs(this, poSRSOverride, "length", g, adfLat, adfLon))
3275
0
        return -1.0;
3276
0
    double dfLength = 0;
3277
0
    for (size_t i = 0; i + 1 < adfLon.size(); ++i)
3278
0
    {
3279
0
        double dfSegmentLength = 0;
3280
0
        geod_inverse(&g, adfLat[i], adfLon[i], adfLat[i + 1], adfLon[i + 1],
3281
0
                     &dfSegmentLength, nullptr, nullptr);
3282
0
        dfLength += dfSegmentLength;
3283
0
    }
3284
0
    return dfLength;
3285
0
}
3286
3287
/************************************************************************/
3288
/*                       get_AreaOfCurveSegments()                      */
3289
/************************************************************************/
3290
3291
double OGRLineString::get_AreaOfCurveSegments() const
3292
0
{
3293
0
    return 0;
3294
0
}
3295
3296
/************************************************************************/
3297
/*                            isClockwise()                             */
3298
/************************************************************************/
3299
3300
/**
3301
 * \brief Returns TRUE if the ring has clockwise winding (or less than 2 points)
3302
 *
3303
 * Assumes that the line is closed.
3304
 *
3305
 * @return TRUE if clockwise otherwise FALSE.
3306
 */
3307
3308
int OGRLineString::isClockwise() const
3309
3310
0
{
3311
    // WARNING: keep in sync OGRLineString::isClockwise(),
3312
    // OGRCurve::isClockwise() and OGRWKBIsClockwiseRing()
3313
3314
0
    if (nPointCount < 2)
3315
0
        return TRUE;
3316
3317
0
    bool bUseFallback = false;
3318
3319
    // Find the lowest rightmost vertex.
3320
0
    int v = 0;  // Used after for.
3321
0
    for (int i = 1; i < nPointCount - 1; i++)
3322
0
    {
3323
        // => v < end.
3324
0
        if (paoPoints[i].y < paoPoints[v].y ||
3325
0
            (paoPoints[i].y == paoPoints[v].y &&
3326
0
             paoPoints[i].x > paoPoints[v].x))
3327
0
        {
3328
0
            v = i;
3329
0
            bUseFallback = false;
3330
0
        }
3331
0
        else if (paoPoints[i].y == paoPoints[v].y &&
3332
0
                 paoPoints[i].x == paoPoints[v].x)
3333
0
        {
3334
            // Two vertex with same coordinates are the lowest rightmost
3335
            // vertex.  Cannot use that point as the pivot (#5342).
3336
0
            bUseFallback = true;
3337
0
        }
3338
0
    }
3339
3340
    // Previous.
3341
0
    int next = v - 1;
3342
0
    if (next < 0)
3343
0
    {
3344
0
        next = nPointCount - 1 - 1;
3345
0
    }
3346
3347
0
    constexpr double EPSILON = 1.0E-5;
3348
0
    const auto epsilonEqual = [](double a, double b, double eps)
3349
0
    { return ::fabs(a - b) < eps; };
3350
3351
0
    if (epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) &&
3352
0
        epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON))
3353
0
    {
3354
        // Don't try to be too clever by retrying with a next point.
3355
        // This can lead to false results as in the case of #3356.
3356
0
        bUseFallback = true;
3357
0
    }
3358
3359
0
    const double dx0 = paoPoints[next].x - paoPoints[v].x;
3360
0
    const double dy0 = paoPoints[next].y - paoPoints[v].y;
3361
3362
    // Following.
3363
0
    next = v + 1;
3364
0
    if (next >= nPointCount - 1)
3365
0
    {
3366
0
        next = 0;
3367
0
    }
3368
3369
0
    if (epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) &&
3370
0
        epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON))
3371
0
    {
3372
        // Don't try to be too clever by retrying with a next point.
3373
        // This can lead to false results as in the case of #3356.
3374
0
        bUseFallback = true;
3375
0
    }
3376
3377
0
    const double dx1 = paoPoints[next].x - paoPoints[v].x;
3378
0
    const double dy1 = paoPoints[next].y - paoPoints[v].y;
3379
3380
0
    const double crossproduct = dx1 * dy0 - dx0 * dy1;
3381
3382
0
    if (!bUseFallback)
3383
0
    {
3384
0
        if (crossproduct > 0)  // CCW
3385
0
            return FALSE;
3386
0
        else if (crossproduct < 0)  // CW
3387
0
            return TRUE;
3388
0
    }
3389
3390
    // This is a degenerate case: the extent of the polygon is less than EPSILON
3391
    // or 2 nearly identical points were found.
3392
    // Try with Green Formula as a fallback, but this is not a guarantee
3393
    // as we'll probably be affected by numerical instabilities.
3394
3395
0
    double dfSum =
3396
0
        paoPoints[0].x * (paoPoints[1].y - paoPoints[nPointCount - 1].y);
3397
3398
0
    for (int i = 1; i < nPointCount - 1; i++)
3399
0
    {
3400
0
        dfSum += paoPoints[i].x * (paoPoints[i + 1].y - paoPoints[i - 1].y);
3401
0
    }
3402
3403
0
    dfSum += paoPoints[nPointCount - 1].x *
3404
0
             (paoPoints[0].y - paoPoints[nPointCount - 2].y);
3405
3406
0
    return dfSum < 0;
3407
0
}
3408
3409
//! @endcond