Coverage Report

Created: 2025-11-16 06:25

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