Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrcircularstring.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRCircularString geometry class.
5
 * Author:   Even Rouault, even dot rouault at spatialys dot com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2010, 2014, Even Rouault <even dot rouault at spatialys dot
9
 *com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "ogr_geometry.h"
16
17
#include <cmath>
18
#include <cstring>
19
20
#include <algorithm>
21
#include <limits>
22
#include <vector>
23
24
#include "cpl_error.h"
25
#include "ogr_core.h"
26
#include "ogr_geometry.h"
27
#include "ogr_p.h"
28
29
static inline double dist(double x0, double y0, double x1, double y1)
30
0
{
31
0
    return std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
32
0
}
33
34
/************************************************************************/
35
/*              OGRCircularString( const OGRCircularString& )           */
36
/************************************************************************/
37
38
/**
39
 * \brief Copy constructor.
40
 */
41
42
0
OGRCircularString::OGRCircularString(const OGRCircularString &) = default;
43
44
/************************************************************************/
45
/*                  operator=( const OGRCircularString& )               */
46
/************************************************************************/
47
48
/**
49
 * \brief Assignment operator.
50
 */
51
52
OGRCircularString &OGRCircularString::operator=(const OGRCircularString &other)
53
0
{
54
0
    if (this != &other)
55
0
    {
56
0
        OGRSimpleCurve::operator=(other);
57
0
    }
58
0
    return *this;
59
0
}
60
61
/************************************************************************/
62
/*                          getGeometryType()                           */
63
/************************************************************************/
64
65
OGRwkbGeometryType OGRCircularString::getGeometryType() const
66
67
9
{
68
9
    if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
69
5
        return wkbCircularStringZM;
70
4
    else if (flags & OGR_G_MEASURED)
71
0
        return wkbCircularStringM;
72
4
    else if (flags & OGR_G_3D)
73
3
        return wkbCircularStringZ;
74
1
    else
75
1
        return wkbCircularString;
76
9
}
77
78
/************************************************************************/
79
/*                          getGeometryName()                           */
80
/************************************************************************/
81
82
const char *OGRCircularString::getGeometryName() const
83
84
11
{
85
11
    return "CIRCULARSTRING";
86
11
}
87
88
/************************************************************************/
89
/*                               clone()                                */
90
/************************************************************************/
91
92
OGRCircularString *OGRCircularString::clone() const
93
94
0
{
95
0
    return new (std::nothrow) OGRCircularString(*this);
96
0
}
97
98
/************************************************************************/
99
/*                           importFromWkb()                            */
100
/*                                                                      */
101
/*      Initialize from serialized stream in well known binary          */
102
/*      format.                                                         */
103
/************************************************************************/
104
105
OGRErr OGRCircularString::importFromWkb(const unsigned char *pabyData,
106
                                        size_t nSize, OGRwkbVariant eWkbVariant,
107
                                        size_t &nBytesConsumedOut)
108
109
0
{
110
0
    OGRErr eErr = OGRSimpleCurve::importFromWkb(pabyData, nSize, eWkbVariant,
111
0
                                                nBytesConsumedOut);
112
0
    if (eErr == OGRERR_NONE)
113
0
    {
114
0
        if (!IsValidFast())
115
0
        {
116
0
            empty();
117
0
            return OGRERR_CORRUPT_DATA;
118
0
        }
119
0
    }
120
0
    return eErr;
121
0
}
122
123
/************************************************************************/
124
/*                            exportToWkb()                             */
125
/*                                                                      */
126
/*      Build a well known binary representation of this object.        */
127
/************************************************************************/
128
129
OGRErr
130
OGRCircularString::exportToWkb(unsigned char *pabyData,
131
                               const OGRwkbExportOptions *psOptions) const
132
133
0
{
134
0
    if (!IsValidFast())
135
0
    {
136
0
        return OGRERR_FAILURE;
137
0
    }
138
139
0
    OGRwkbExportOptions sOptions(psOptions ? *psOptions
140
0
                                           : OGRwkbExportOptions());
141
142
    // Does not make sense for new geometries, so patch it.
143
0
    if (sOptions.eWkbVariant == wkbVariantOldOgc)
144
0
        sOptions.eWkbVariant = wkbVariantIso;
145
0
    return OGRSimpleCurve::exportToWkb(pabyData, &sOptions);
146
0
}
147
148
/************************************************************************/
149
/*                           importFromWkt()                            */
150
/*                                                                      */
151
/*      Instantiate from well known text format.  Currently this is     */
152
/*      `CIRCULARSTRING [Z] ( x y [z], x y [z], ...)',                  */
153
/************************************************************************/
154
155
OGRErr OGRCircularString::importFromWkt(const char **ppszInput)
156
157
11
{
158
11
    const OGRErr eErr = OGRSimpleCurve::importFromWkt(ppszInput);
159
11
    if (eErr == OGRERR_NONE)
160
11
    {
161
11
        if (!IsValidFast())
162
2
        {
163
2
            empty();
164
2
            return OGRERR_CORRUPT_DATA;
165
2
        }
166
11
    }
167
9
    return eErr;
168
11
}
169
170
/************************************************************************/
171
/*                            exportToWkt()                             */
172
/************************************************************************/
173
174
std::string OGRCircularString::exportToWkt(const OGRWktOptions &opts,
175
                                           OGRErr *err) const
176
0
{
177
0
    if (!IsValidFast())
178
0
    {
179
0
        if (err)
180
0
            *err = OGRERR_FAILURE;
181
0
        return std::string();
182
0
    }
183
184
0
    OGRWktOptions optsModified(opts);
185
0
    optsModified.variant = wkbVariantIso;
186
0
    return OGRSimpleCurve::exportToWkt(optsModified, err);
187
0
}
188
189
/************************************************************************/
190
/*                             get_Length()                             */
191
/*                                                                      */
192
/*      For now we return a simple euclidean 2D distance.               */
193
/************************************************************************/
194
195
double OGRCircularString::get_Length() const
196
197
0
{
198
0
    double dfLength = 0.0;
199
0
    for (int i = 0; i < nPointCount - 2; i += 2)
200
0
    {
201
0
        const double x0 = paoPoints[i].x;
202
0
        const double y0 = paoPoints[i].y;
203
0
        const double x1 = paoPoints[i + 1].x;
204
0
        const double y1 = paoPoints[i + 1].y;
205
0
        const double x2 = paoPoints[i + 2].x;
206
0
        const double y2 = paoPoints[i + 2].y;
207
0
        double R = 0.0;
208
0
        double cx = 0.0;
209
0
        double cy = 0.0;
210
0
        double alpha0 = 0.0;
211
0
        double alpha1 = 0.0;
212
0
        double alpha2 = 0.0;
213
0
        if (OGRGeometryFactory::GetCurveParameters(
214
0
                x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
215
0
        {
216
0
            dfLength += fabs(alpha2 - alpha0) * R;
217
0
        }
218
0
        else
219
0
        {
220
0
            dfLength += dist(x0, y0, x2, y2);
221
0
        }
222
0
    }
223
0
    return dfLength;
224
0
}
225
226
/************************************************************************/
227
/*                       ExtendEnvelopeWithCircular()                   */
228
/************************************************************************/
229
230
void OGRCircularString::ExtendEnvelopeWithCircular(
231
    OGREnvelope *psEnvelope) const
232
0
{
233
0
    if (!IsValidFast() || nPointCount == 0)
234
0
        return;
235
236
    // Loop through circular portions and determine if they include some
237
    // extremities of the circle.
238
0
    for (int i = 0; i < nPointCount - 2; i += 2)
239
0
    {
240
0
        const double x0 = paoPoints[i].x;
241
0
        const double y0 = paoPoints[i].y;
242
0
        const double x1 = paoPoints[i + 1].x;
243
0
        const double y1 = paoPoints[i + 1].y;
244
0
        const double x2 = paoPoints[i + 2].x;
245
0
        const double y2 = paoPoints[i + 2].y;
246
0
        double R = 0.0;
247
0
        double cx = 0.0;
248
0
        double cy = 0.0;
249
0
        double alpha0 = 0.0;
250
0
        double alpha1 = 0.0;
251
0
        double alpha2 = 0.0;
252
0
        if (OGRGeometryFactory::GetCurveParameters(
253
0
                x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
254
0
        {
255
0
            if (std::isnan(alpha0) || std::isnan(alpha2))
256
0
            {
257
0
                CPLError(CE_Failure, CPLE_AppDefined,
258
0
                         "GetCurveParameters returned NaN");
259
0
                continue;
260
0
            }
261
0
            int quadrantStart =
262
0
                static_cast<int>(std::floor(alpha0 / (M_PI / 2)));
263
0
            int quadrantEnd = static_cast<int>(std::floor(alpha2 / (M_PI / 2)));
264
0
            if (quadrantStart > quadrantEnd)
265
0
            {
266
0
                std::swap(quadrantStart, quadrantEnd);
267
0
            }
268
            // Transition through quadrants in counter-clock wise direction.
269
0
            for (int j = quadrantStart + 1; j <= quadrantEnd; ++j)
270
0
            {
271
0
                switch ((j + 8) % 4)
272
0
                {
273
0
                    case 0:
274
0
                        psEnvelope->MaxX = std::max(psEnvelope->MaxX, cx + R);
275
0
                        break;
276
0
                    case 1:
277
0
                        psEnvelope->MaxY = std::max(psEnvelope->MaxY, cy + R);
278
0
                        break;
279
0
                    case 2:
280
0
                        psEnvelope->MinX = std::min(psEnvelope->MinX, cx - R);
281
0
                        break;
282
0
                    case 3:
283
0
                        psEnvelope->MinY = std::min(psEnvelope->MaxY, cy - R);
284
0
                        break;
285
0
                    default:
286
0
                        CPLAssert(false);
287
0
                        break;
288
0
                }
289
0
            }
290
0
        }
291
0
    }
292
0
}
293
294
/************************************************************************/
295
/*                            getEnvelope()                             */
296
/************************************************************************/
297
298
void OGRCircularString::getEnvelope(OGREnvelope *psEnvelope) const
299
300
0
{
301
0
    OGRSimpleCurve::getEnvelope(psEnvelope);
302
0
    ExtendEnvelopeWithCircular(psEnvelope);
303
0
}
304
305
/************************************************************************/
306
/*                            getEnvelope()                             */
307
/************************************************************************/
308
309
void OGRCircularString::getEnvelope(OGREnvelope3D *psEnvelope) const
310
311
0
{
312
0
    OGRSimpleCurve::getEnvelope(psEnvelope);
313
0
    ExtendEnvelopeWithCircular(psEnvelope);
314
0
}
315
316
/************************************************************************/
317
/*                     OGRCircularString::segmentize()                  */
318
/************************************************************************/
319
320
bool OGRCircularString::segmentize(double dfMaxLength)
321
0
{
322
0
    if (!IsValidFast())
323
0
        return false;
324
0
    if (nPointCount == 0)
325
0
        return true;
326
327
    // So as to make sure that the same line followed in both directions
328
    // result in the same segmentized line.
329
0
    if (paoPoints[0].x < paoPoints[nPointCount - 1].x ||
330
0
        (paoPoints[0].x == paoPoints[nPointCount - 1].x &&
331
0
         paoPoints[0].y < paoPoints[nPointCount - 1].y))
332
0
    {
333
0
        reversePoints();
334
0
        const bool bRet = segmentize(dfMaxLength);
335
0
        reversePoints();
336
0
        return bRet;
337
0
    }
338
339
0
    std::vector<OGRRawPoint> aoRawPoint;
340
0
    std::vector<double> adfZ;
341
0
    std::vector<double> adfM;
342
0
    bool bRet = true;
343
0
    for (int i = 0; i < nPointCount - 2; i += 2)
344
0
    {
345
0
        const double x0 = paoPoints[i].x;
346
0
        const double y0 = paoPoints[i].y;
347
0
        const double x1 = paoPoints[i + 1].x;
348
0
        const double y1 = paoPoints[i + 1].y;
349
0
        const double x2 = paoPoints[i + 2].x;
350
0
        const double y2 = paoPoints[i + 2].y;
351
0
        double R = 0.0;
352
0
        double cx = 0.0;
353
0
        double cy = 0.0;
354
0
        double alpha0 = 0.0;
355
0
        double alpha1 = 0.0;
356
0
        double alpha2 = 0.0;
357
358
0
        aoRawPoint.emplace_back(x0, y0);
359
0
        if (padfZ)
360
0
            adfZ.emplace_back(padfZ[i]);
361
0
        if (padfM)
362
0
            adfM.emplace_back(padfM[i]);
363
364
0
        constexpr int kMax = 2 << 26;
365
366
        // We have strong constraints on the number of intermediate points
367
        // we can add.
368
369
0
        if (OGRGeometryFactory::GetCurveParameters(
370
0
                x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
371
0
        {
372
            // It is an arc circle.
373
0
            const double dfSegmentLength1 = fabs(alpha1 - alpha0) * R;
374
0
            const double dfSegmentLength2 = fabs(alpha2 - alpha1) * R;
375
0
            if (dfSegmentLength1 > dfMaxLength ||
376
0
                dfSegmentLength2 > dfMaxLength)
377
0
            {
378
0
                const double dfVal =
379
0
                    1 + 2 * std::floor(dfSegmentLength1 / dfMaxLength / 2.0);
380
0
                if (dfVal < 0.0 || std::isnan(dfVal) ||
381
0
                    dfVal >= kMax - static_cast<int>(aoRawPoint.size()))
382
0
                {
383
0
                    CPLError(CE_Failure, CPLE_AppDefined,
384
0
                             "segmentize nIntermediatePoints invalid: %lf",
385
0
                             dfVal);
386
0
                    return false;
387
0
                }
388
0
                const int nIntermediatePoints = static_cast<int>(dfVal);
389
0
                const double dfStep =
390
0
                    (alpha1 - alpha0) / (nIntermediatePoints + 1);
391
0
                for (int j = 1; j <= nIntermediatePoints; ++j)
392
0
                {
393
0
                    double alpha = alpha0 + dfStep * j;
394
0
                    const double x = cx + R * cos(alpha);
395
0
                    const double y = cy + R * sin(alpha);
396
0
                    aoRawPoint.emplace_back(x, y);
397
0
                    if (padfZ)
398
0
                    {
399
0
                        const double z = padfZ[i] + (padfZ[i + 1] - padfZ[i]) *
400
0
                                                        (alpha - alpha0) /
401
0
                                                        (alpha1 - alpha0);
402
0
                        adfZ.emplace_back(z);
403
0
                    }
404
0
                    if (padfM)
405
0
                    {
406
0
                        adfM.emplace_back(padfM[i]);
407
0
                    }
408
0
                }
409
0
            }
410
0
            aoRawPoint.emplace_back(x1, y1);
411
0
            if (padfZ)
412
0
                adfZ.emplace_back(padfZ[i + 1]);
413
0
            if (padfM)
414
0
                adfM.emplace_back(padfM[i + 1]);
415
416
0
            if (dfSegmentLength1 > dfMaxLength ||
417
0
                dfSegmentLength2 > dfMaxLength)
418
0
            {
419
0
                const double dfVal =
420
0
                    1 + 2 * std::floor(dfSegmentLength2 / dfMaxLength / 2.0);
421
0
                if (dfVal < 0.0 || std::isnan(dfVal) ||
422
0
                    dfVal >= kMax - static_cast<int>(aoRawPoint.size()))
423
0
                {
424
0
                    CPLError(CE_Failure, CPLE_AppDefined,
425
0
                             "segmentize nIntermediatePoints invalid 2: %lf",
426
0
                             dfVal);
427
0
                    return false;
428
0
                }
429
0
                int nIntermediatePoints = static_cast<int>(dfVal);
430
0
                const double dfStep =
431
0
                    (alpha2 - alpha1) / (nIntermediatePoints + 1);
432
0
                for (int j = 1; j <= nIntermediatePoints; ++j)
433
0
                {
434
0
                    const double alpha = alpha1 + dfStep * j;
435
0
                    const double x = cx + R * cos(alpha);
436
0
                    const double y = cy + R * sin(alpha);
437
0
                    aoRawPoint.emplace_back(x, y);
438
0
                    if (padfZ)
439
0
                    {
440
0
                        const double z =
441
0
                            padfZ[i + 1] + (padfZ[i + 2] - padfZ[i + 1]) *
442
0
                                               (alpha - alpha1) /
443
0
                                               (alpha2 - alpha1);
444
0
                        adfZ.emplace_back(z);
445
0
                    }
446
0
                    if (padfM)
447
0
                    {
448
0
                        adfM.emplace_back(padfM[i + 1]);
449
0
                    }
450
0
                }
451
0
            }
452
0
        }
453
0
        else
454
0
        {
455
            // It is a straight line.
456
0
            const double dfSegmentLength1 = dist(x0, y0, x1, y1);
457
0
            const double dfSegmentLength2 = dist(x1, y1, x2, y2);
458
0
            if (dfSegmentLength1 > dfMaxLength ||
459
0
                dfSegmentLength2 > dfMaxLength)
460
0
            {
461
0
                const double dfVal =
462
0
                    1 + 2 * std::ceil(dfSegmentLength1 / dfMaxLength / 2.0);
463
0
                if (dfVal < 0.0 || std::isnan(dfVal) ||
464
0
                    dfVal >= kMax - static_cast<int>(aoRawPoint.size()))
465
0
                {
466
0
                    CPLError(CE_Failure, CPLE_AppDefined,
467
0
                             "segmentize nIntermediatePoints invalid 2: %lf",
468
0
                             dfVal);
469
0
                    return false;
470
0
                }
471
0
                int nIntermediatePoints = static_cast<int>(dfVal);
472
0
                for (int j = 1; j <= nIntermediatePoints; ++j)
473
0
                {
474
0
                    aoRawPoint.emplace_back(
475
0
                        x0 + j * (x1 - x0) / (nIntermediatePoints + 1),
476
0
                        y0 + j * (y1 - y0) / (nIntermediatePoints + 1));
477
0
                    if (padfZ)
478
0
                        adfZ.emplace_back(padfZ[i] +
479
0
                                          j * (padfZ[i + 1] - padfZ[i]) /
480
0
                                              (nIntermediatePoints + 1));
481
0
                    if (padfM)
482
0
                        adfM.emplace_back(padfM[i]);
483
0
                }
484
0
            }
485
486
0
            aoRawPoint.emplace_back(x1, y1);
487
0
            if (padfZ)
488
0
                adfZ.emplace_back(padfZ[i + 1]);
489
0
            if (padfM)
490
0
                adfM.emplace_back(padfM[i + 1]);
491
492
0
            if (dfSegmentLength1 > dfMaxLength ||
493
0
                dfSegmentLength2 > dfMaxLength)
494
0
            {
495
0
                const double dfVal =
496
0
                    1 + 2 * std::ceil(dfSegmentLength2 / dfMaxLength / 2.0);
497
0
                if (dfVal < 0.0 || std::isnan(dfVal) ||
498
0
                    dfVal >= kMax - static_cast<int>(aoRawPoint.size()))
499
0
                {
500
0
                    CPLError(CE_Failure, CPLE_AppDefined,
501
0
                             "segmentize nIntermediatePoints invalid 3: %lf",
502
0
                             dfVal);
503
0
                    return false;
504
0
                }
505
0
                const int nIntermediatePoints = static_cast<int>(dfVal);
506
507
0
                for (int j = 1; j <= nIntermediatePoints; ++j)
508
0
                {
509
0
                    aoRawPoint.emplace_back(
510
0
                        x1 + j * (x2 - x1) / (nIntermediatePoints + 1),
511
0
                        y1 + j * (y2 - y1) / (nIntermediatePoints + 1));
512
0
                    if (padfZ)
513
0
                        adfZ.emplace_back(padfZ[i + 1] +
514
0
                                          j * (padfZ[i + 2] - padfZ[i + 1]) /
515
0
                                              (nIntermediatePoints + 1));
516
0
                    if (padfM)
517
0
                        adfM.emplace_back(padfM[i + 1]);
518
0
                }
519
0
            }
520
0
        }
521
0
    }
522
0
    aoRawPoint.push_back(paoPoints[nPointCount - 1]);
523
0
    if (padfZ)
524
0
        adfZ.push_back(padfZ[nPointCount - 1]);
525
0
    if (padfM)
526
0
        adfM.push_back(padfM[nPointCount - 1]);
527
528
0
    CPLAssert(aoRawPoint.empty() ||
529
0
              (aoRawPoint.size() >= 3 && (aoRawPoint.size() % 2) == 1));
530
0
    if (padfZ)
531
0
    {
532
0
        CPLAssert(adfZ.size() == aoRawPoint.size());
533
0
    }
534
0
    if (padfM)
535
0
    {
536
0
        CPLAssert(adfM.size() == aoRawPoint.size());
537
0
    }
538
539
    // Is there actually something to modify?
540
0
    if (nPointCount < static_cast<int>(aoRawPoint.size()))
541
0
    {
542
0
        nPointCount = static_cast<int>(aoRawPoint.size());
543
0
        paoPoints = static_cast<OGRRawPoint *>(
544
0
            CPLRealloc(paoPoints, sizeof(OGRRawPoint) * nPointCount));
545
0
        memcpy(paoPoints, &aoRawPoint[0], sizeof(OGRRawPoint) * nPointCount);
546
0
        if (padfZ)
547
0
        {
548
0
            padfZ = static_cast<double *>(
549
0
                CPLRealloc(padfZ, sizeof(double) * aoRawPoint.size()));
550
0
            memcpy(padfZ, &adfZ[0], sizeof(double) * nPointCount);
551
0
        }
552
0
        if (padfM)
553
0
        {
554
0
            padfM = static_cast<double *>(
555
0
                CPLRealloc(padfM, sizeof(double) * aoRawPoint.size()));
556
0
            memcpy(padfM, &adfM[0], sizeof(double) * nPointCount);
557
0
        }
558
0
    }
559
0
    return bRet;
560
0
}
561
562
/************************************************************************/
563
/*                               Value()                                */
564
/*                                                                      */
565
/*      Get an interpolated point at some distance along the curve.     */
566
/************************************************************************/
567
568
void OGRCircularString::Value(double dfDistance, OGRPoint *poPoint) const
569
570
0
{
571
0
    if (dfDistance < 0)
572
0
    {
573
0
        StartPoint(poPoint);
574
0
        return;
575
0
    }
576
577
0
    double dfLength = 0;
578
579
0
    for (int i = 0; i < nPointCount - 2; i += 2)
580
0
    {
581
0
        const double x0 = paoPoints[i].x;
582
0
        const double y0 = paoPoints[i].y;
583
0
        const double x1 = paoPoints[i + 1].x;
584
0
        const double y1 = paoPoints[i + 1].y;
585
0
        const double x2 = paoPoints[i + 2].x;
586
0
        const double y2 = paoPoints[i + 2].y;
587
0
        double R = 0.0;
588
0
        double cx = 0.0;
589
0
        double cy = 0.0;
590
0
        double alpha0 = 0.0;
591
0
        double alpha1 = 0.0;
592
0
        double alpha2 = 0.0;
593
594
        // We have strong constraints on the number of intermediate points
595
        // we can add.
596
597
0
        if (OGRGeometryFactory::GetCurveParameters(
598
0
                x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
599
0
        {
600
            // It is an arc circle.
601
0
            const double dfSegLength = fabs(alpha2 - alpha0) * R;
602
0
            if (dfSegLength > 0)
603
0
            {
604
0
                if ((dfLength <= dfDistance) &&
605
0
                    ((dfLength + dfSegLength) >= dfDistance))
606
0
                {
607
0
                    const double dfRatio =
608
0
                        (dfDistance - dfLength) / dfSegLength;
609
610
0
                    const double alpha =
611
0
                        alpha0 * (1 - dfRatio) + alpha2 * dfRatio;
612
0
                    const double x = cx + R * cos(alpha);
613
0
                    const double y = cy + R * sin(alpha);
614
615
0
                    poPoint->setX(x);
616
0
                    poPoint->setY(y);
617
618
0
                    if (getCoordinateDimension() == 3)
619
0
                        poPoint->setZ(padfZ[i] * (1 - dfRatio) +
620
0
                                      padfZ[i + 2] * dfRatio);
621
622
0
                    return;
623
0
                }
624
625
0
                dfLength += dfSegLength;
626
0
            }
627
0
        }
628
0
        else
629
0
        {
630
            // It is a straight line.
631
0
            const double dfSegLength = dist(x0, y0, x2, y2);
632
0
            if (dfSegLength > 0)
633
0
            {
634
0
                if ((dfLength <= dfDistance) &&
635
0
                    ((dfLength + dfSegLength) >= dfDistance))
636
0
                {
637
0
                    const double dfRatio =
638
0
                        (dfDistance - dfLength) / dfSegLength;
639
640
0
                    poPoint->setX(paoPoints[i].x * (1 - dfRatio) +
641
0
                                  paoPoints[i + 2].x * dfRatio);
642
0
                    poPoint->setY(paoPoints[i].y * (1 - dfRatio) +
643
0
                                  paoPoints[i + 2].y * dfRatio);
644
645
0
                    if (getCoordinateDimension() == 3)
646
0
                        poPoint->setZ(padfZ[i] * (1 - dfRatio) +
647
0
                                      padfZ[i + 2] * dfRatio);
648
649
0
                    return;
650
0
                }
651
652
0
                dfLength += dfSegLength;
653
0
            }
654
0
        }
655
0
    }
656
657
0
    EndPoint(poPoint);
658
0
}
659
660
/************************************************************************/
661
/*                          CurveToLine()                               */
662
/************************************************************************/
663
664
OGRLineString *
665
OGRCircularString::CurveToLine(double dfMaxAngleStepSizeDegrees,
666
                               const char *const *papszOptions) const
667
0
{
668
0
    OGRLineString *poLine = new OGRLineString();
669
0
    poLine->assignSpatialReference(getSpatialReference());
670
671
0
    const bool bHasZ = getCoordinateDimension() == 3;
672
0
    for (int i = 0; i < nPointCount - 2; i += 2)
673
0
    {
674
0
        OGRLineString *poArc = OGRGeometryFactory::curveToLineString(
675
0
            paoPoints[i].x, paoPoints[i].y, padfZ ? padfZ[i] : 0.0,
676
0
            paoPoints[i + 1].x, paoPoints[i + 1].y, padfZ ? padfZ[i + 1] : 0.0,
677
0
            paoPoints[i + 2].x, paoPoints[i + 2].y, padfZ ? padfZ[i + 2] : 0.0,
678
0
            bHasZ, dfMaxAngleStepSizeDegrees, papszOptions);
679
0
        poLine->addSubLineString(poArc, (i == 0) ? 0 : 1);
680
0
        delete poArc;
681
0
    }
682
0
    return poLine;
683
0
}
684
685
/************************************************************************/
686
/*                        IsValidFast()                                 */
687
/************************************************************************/
688
689
OGRBoolean OGRCircularString::IsValidFast() const
690
691
11
{
692
11
    if (nPointCount == 1 || nPointCount == 2 ||
693
9
        (nPointCount >= 3 && (nPointCount % 2) == 0))
694
2
    {
695
2
        CPLError(CE_Failure, CPLE_NotSupported,
696
2
                 "Bad number of points in circular string : %d", nPointCount);
697
2
        return FALSE;
698
2
    }
699
9
    return TRUE;
700
11
}
701
702
/************************************************************************/
703
/*                            IsValid()                                 */
704
/************************************************************************/
705
706
OGRBoolean OGRCircularString::IsValid() const
707
708
0
{
709
0
    return IsValidFast() && OGRGeometry::IsValid();
710
0
}
711
712
/************************************************************************/
713
/*                         hasCurveGeometry()                           */
714
/************************************************************************/
715
716
OGRBoolean
717
OGRCircularString::hasCurveGeometry(int /* bLookForNonLinear */) const
718
9
{
719
9
    return TRUE;
720
9
}
721
722
/************************************************************************/
723
/*                         getLinearGeometry()                        */
724
/************************************************************************/
725
726
OGRGeometry *
727
OGRCircularString::getLinearGeometry(double dfMaxAngleStepSizeDegrees,
728
                                     const char *const *papszOptions) const
729
0
{
730
0
    return CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions);
731
0
}
732
733
//! @cond Doxygen_Suppress
734
/************************************************************************/
735
/*                     GetCasterToLineString()                          */
736
/************************************************************************/
737
738
static OGRLineString *CasterToLineString(OGRCurve *poGeom)
739
0
{
740
0
    CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
741
0
             poGeom->getGeometryName());
742
0
    delete poGeom;
743
0
    return nullptr;
744
0
}
745
746
OGRCurveCasterToLineString OGRCircularString::GetCasterToLineString() const
747
0
{
748
0
    return ::CasterToLineString;
749
0
}
750
751
/************************************************************************/
752
/*                        GetCasterToLinearRing()                       */
753
/************************************************************************/
754
755
static OGRLinearRing *CasterToLinearRing(OGRCurve *poGeom)
756
0
{
757
0
    CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
758
0
             poGeom->getGeometryName());
759
0
    delete poGeom;
760
0
    return nullptr;
761
0
}
762
763
OGRCurveCasterToLinearRing OGRCircularString::GetCasterToLinearRing() const
764
0
{
765
0
    return ::CasterToLinearRing;
766
0
}
767
768
//! @endcond
769
770
/************************************************************************/
771
/*                            IsFullCircle()                            */
772
/************************************************************************/
773
774
int OGRCircularString::IsFullCircle(double &cx, double &cy,
775
                                    double &square_R) const
776
0
{
777
0
    if (getNumPoints() == 3 && get_IsClosed())
778
0
    {
779
0
        const double x0 = getX(0);
780
0
        const double y0 = getY(0);
781
0
        const double x1 = getX(1);
782
0
        const double y1 = getY(1);
783
0
        cx = (x0 + x1) / 2;
784
0
        cy = (y0 + y1) / 2;
785
0
        square_R = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy);
786
0
        return TRUE;
787
0
    }
788
    // Full circle defined by 2 arcs?
789
0
    else if (getNumPoints() == 5 && get_IsClosed())
790
0
    {
791
0
        double R_1 = 0.0;
792
0
        double cx_1 = 0.0;
793
0
        double cy_1 = 0.0;
794
0
        double alpha0_1 = 0.0;
795
0
        double alpha1_1 = 0.0;
796
0
        double alpha2_1 = 0.0;
797
0
        double R_2 = 0.0;
798
0
        double cx_2 = 0.0;
799
0
        double cy_2 = 0.0;
800
0
        double alpha0_2 = 0.0;
801
0
        double alpha1_2 = 0.0;
802
0
        double alpha2_2 = 0.0;
803
0
        if (OGRGeometryFactory::GetCurveParameters(
804
0
                getX(0), getY(0), getX(1), getY(1), getX(2), getY(2), R_1, cx_1,
805
0
                cy_1, alpha0_1, alpha1_1, alpha2_1) &&
806
0
            OGRGeometryFactory::GetCurveParameters(
807
0
                getX(2), getY(2), getX(3), getY(3), getX(4), getY(4), R_2, cx_2,
808
0
                cy_2, alpha0_2, alpha1_2, alpha2_2) &&
809
0
            fabs(R_1 - R_2) < 1e-10 && fabs(cx_1 - cx_2) < 1e-10 &&
810
0
            fabs(cy_1 - cy_2) < 1e-10 &&
811
0
            (alpha2_1 - alpha0_1) * (alpha2_2 - alpha0_2) > 0)
812
0
        {
813
0
            cx = cx_1;
814
0
            cy = cy_1;
815
0
            square_R = R_1 * R_1;
816
0
            return TRUE;
817
0
        }
818
0
    }
819
0
    return FALSE;
820
0
}
821
822
/************************************************************************/
823
/*                       get_AreaOfCurveSegments()                      */
824
/************************************************************************/
825
826
//! @cond Doxygen_Suppress
827
double OGRCircularString::get_AreaOfCurveSegments() const
828
0
{
829
0
    double dfArea = 0.0;
830
0
    for (int i = 0; i < getNumPoints() - 2; i += 2)
831
0
    {
832
0
        const double x0 = getX(i);
833
0
        const double y0 = getY(i);
834
0
        const double x1 = getX(i + 1);
835
0
        const double y1 = getY(i + 1);
836
0
        const double x2 = getX(i + 2);
837
0
        const double y2 = getY(i + 2);
838
0
        double R = 0.0;
839
0
        double cx = 0.0;
840
0
        double cy = 0.0;
841
0
        double alpha0 = 0.0;
842
0
        double alpha1 = 0.0;
843
0
        double alpha2 = 0.0;
844
0
        if (OGRGeometryFactory::GetCurveParameters(
845
0
                x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
846
0
        {
847
            // Should be <= PI in absolute value.
848
0
            const double delta_alpha01 = alpha1 - alpha0;
849
0
            const double delta_alpha12 = alpha2 - alpha1;  // Same.
850
            // http://en.wikipedia.org/wiki/Circular_segment
851
0
            dfArea += 0.5 * R * R *
852
0
                      fabs(delta_alpha01 - sin(delta_alpha01) + delta_alpha12 -
853
0
                           sin(delta_alpha12));
854
0
        }
855
0
    }
856
0
    return dfArea;
857
0
}
858
859
//! @endcond
860
861
/************************************************************************/
862
/*                           get_Area()                                 */
863
/************************************************************************/
864
865
double OGRCircularString::get_Area() const
866
0
{
867
0
    if (IsEmpty() || !get_IsClosed())
868
0
        return 0;
869
870
0
    double cx = 0.0;
871
0
    double cy = 0.0;
872
0
    double square_R = 0.0;
873
874
0
    if (IsFullCircle(cx, cy, square_R))
875
0
    {
876
0
        return M_PI * square_R;
877
0
    }
878
879
    // Optimization for convex rings.
880
0
    if (IsConvex())
881
0
    {
882
        // Compute area of shape without the circular segments.
883
0
        double dfArea = get_LinearArea();
884
885
        // Add the area of the spherical segments.
886
0
        dfArea += get_AreaOfCurveSegments();
887
888
0
        return dfArea;
889
0
    }
890
891
0
    OGRLineString *poLS = CurveToLine();
892
0
    const double dfArea = poLS->get_Area();
893
0
    delete poLS;
894
895
0
    return dfArea;
896
0
}
897
898
/************************************************************************/
899
/*                        get_GeodesicArea()                            */
900
/************************************************************************/
901
902
double OGRCircularString::get_GeodesicArea(
903
    const OGRSpatialReference *poSRSOverride) const
904
0
{
905
0
    if (IsEmpty())
906
0
        return 0;
907
908
0
    if (!get_IsClosed())
909
0
    {
910
0
        CPLError(CE_Failure, CPLE_AppDefined, "Non-closed geometry");
911
0
        return -1;
912
0
    }
913
914
0
    if (!poSRSOverride)
915
0
        poSRSOverride = getSpatialReference();
916
917
0
    auto poLS = std::unique_ptr<OGRLineString>(CurveToLine());
918
0
    return poLS->get_GeodesicArea(poSRSOverride);
919
0
}
920
921
/************************************************************************/
922
/*                        get_GeodesicLength()                          */
923
/************************************************************************/
924
925
double OGRCircularString::get_GeodesicLength(
926
    const OGRSpatialReference *poSRSOverride) const
927
0
{
928
0
    if (IsEmpty())
929
0
        return 0;
930
931
0
    if (!poSRSOverride)
932
0
        poSRSOverride = getSpatialReference();
933
934
0
    auto poLS = std::unique_ptr<OGRLineString>(CurveToLine());
935
0
    return poLS->get_GeodesicLength(poSRSOverride);
936
0
}
937
938
//! @cond Doxygen_Suppress
939
940
/************************************************************************/
941
/*                           ContainsPoint()                            */
942
/************************************************************************/
943
944
int OGRCircularString::ContainsPoint(const OGRPoint *p) const
945
0
{
946
0
    double cx = 0.0;
947
0
    double cy = 0.0;
948
0
    double square_R = 0.0;
949
0
    if (IsFullCircle(cx, cy, square_R))
950
0
    {
951
0
        const double square_dist = (p->getX() - cx) * (p->getX() - cx) +
952
0
                                   (p->getY() - cy) * (p->getY() - cy);
953
0
        return square_dist < square_R;
954
0
    }
955
0
    return -1;
956
0
}
957
958
/************************************************************************/
959
/*                       IntersectsPoint()                              */
960
/************************************************************************/
961
962
int OGRCircularString::IntersectsPoint(const OGRPoint *p) const
963
0
{
964
0
    double cx = 0.0;
965
0
    double cy = 0.0;
966
0
    double square_R = 0.0;
967
0
    if (IsFullCircle(cx, cy, square_R))
968
0
    {
969
0
        const double square_dist = (p->getX() - cx) * (p->getX() - cx) +
970
0
                                   (p->getY() - cy) * (p->getY() - cy);
971
0
        return square_dist <= square_R;
972
0
    }
973
0
    return -1;
974
0
}
975
976
//! @endcond