Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrpgeogeometry.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implements decoder of shapebin geometry for PGeo
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *           Paul Ramsey, pramsey at cleverelephant.ca
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
10
 * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
11
 * Copyright (c) 2011-2014, Even Rouault <even dot rouault at spatialys.com>
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15
16
// PGeo == ESRI Personal GeoDatabase.
17
18
#include "cpl_port.h"
19
#include "ogrpgeogeometry.h"
20
21
#include <algorithm>
22
#include <cassert>
23
#include <cmath>
24
#include <cstddef>
25
#include <cstring>
26
#include <limits>
27
#include <map>
28
#include <memory>
29
#include <set>
30
#include <utility>
31
#include <vector>
32
33
#include "cpl_conv.h"
34
#include "cpl_error.h"
35
#include "cpl_string.h"
36
#include "cpl_vsi.h"
37
#include "ogr_api.h"
38
#include "ogr_core.h"
39
#include "ogr_p.h"
40
41
#ifdef HAVE_WFLAG_UNREACHABLE_CODE_AGGRESSIVE
42
#if defined(__clang__)
43
#pragma clang diagnostic push
44
#pragma clang diagnostic ignored "-Wunreachable-code"
45
#endif
46
#endif
47
48
constexpr int SHPP_TRISTRIP = 0;
49
constexpr int SHPP_TRIFAN = 1;
50
constexpr int SHPP_OUTERRING = 2;
51
constexpr int SHPP_INNERRING = 3;
52
constexpr int SHPP_FIRSTRING = 4;
53
constexpr int SHPP_RING = 5;
54
constexpr int SHPP_TRIANGLES = 6;  // Multipatch 9.0 specific.
55
56
enum CurveType
57
{
58
    CURVE_ARC_INTERIOR_POINT,
59
    CURVE_ARC_CENTER_POINT,
60
    CURVE_BEZIER,
61
    CURVE_ELLIPSE_BY_CENTER
62
};
63
64
namespace
65
{
66
struct CurveSegment
67
{
68
    int nStartPointIdx;
69
    CurveType eType;
70
71
    union
72
    {
73
        // Arc defined by an intermediate point.
74
        struct
75
        {
76
            double dfX;
77
            double dfY;
78
        } ArcByIntermediatePoint;
79
80
        // Deprecated way of defining circular arc by its center and
81
        // winding order.
82
        struct
83
        {
84
            double dfX;
85
            double dfY;
86
            bool bIsCCW;
87
        } ArcByCenterPoint;
88
89
        struct
90
        {
91
            double dfX1;
92
            double dfY1;
93
            double dfX2;
94
            double dfY2;
95
        } Bezier;
96
97
        struct
98
        {
99
            double dfX;
100
            double dfY;
101
            double dfRotationDeg;
102
            double dfSemiMajor;
103
            double dfRatioSemiMinor;
104
            bool bIsMinor;
105
            bool bIsComplete;
106
        } EllipseByCenter;
107
    } u;
108
};
109
} /* namespace */
110
111
constexpr int EXT_SHAPE_SEGMENT_ARC = 1;
112
constexpr int EXT_SHAPE_SEGMENT_BEZIER = 4;
113
constexpr int EXT_SHAPE_SEGMENT_ELLIPSE = 5;
114
115
constexpr int EXT_SHAPE_ARC_EMPTY = 0x1;
116
constexpr int EXT_SHAPE_ARC_CCW = 0x8;
117
#ifdef DEBUG_VERBOSE
118
constexpr int EXT_SHAPE_ARC_MINOR = 0x10;
119
#endif
120
constexpr int EXT_SHAPE_ARC_LINE = 0x20;
121
constexpr int EXT_SHAPE_ARC_POINT = 0x40;
122
constexpr int EXT_SHAPE_ARC_IP = 0x80;
123
124
#ifdef DEBUG_VERBOSE
125
constexpr int EXT_SHAPE_ELLIPSE_EMPTY = 0x1;
126
constexpr int EXT_SHAPE_ELLIPSE_LINE = 0x40;
127
constexpr int EXT_SHAPE_ELLIPSE_POINT = 0x80;
128
constexpr int EXT_SHAPE_ELLIPSE_CIRCULAR = 0x100;
129
constexpr int EXT_SHAPE_ELLIPSE_CCW = 0x800;
130
#endif
131
132
constexpr int EXT_SHAPE_ELLIPSE_CENTER_TO = 0x200;
133
constexpr int EXT_SHAPE_ELLIPSE_CENTER_FROM = 0x400;
134
constexpr int EXT_SHAPE_ELLIPSE_MINOR = 0x1000;
135
constexpr int EXT_SHAPE_ELLIPSE_COMPLETE = 0x2000;
136
137
/************************************************************************/
138
/*                    OGRCreateFromMultiPatchPart()                     */
139
/************************************************************************/
140
141
static void OGRCreateFromMultiPatchPart(OGRGeometryCollection *poGC,
142
                                        OGRMultiPolygon *&poMP,
143
                                        OGRPolygon *&poLastPoly, int nPartType,
144
                                        int nPartPoints, const double *padfX,
145
                                        const double *padfY,
146
                                        const double *padfZ)
147
52.1k
{
148
52.1k
    nPartType &= 0xf;
149
150
52.1k
    if (nPartType == SHPP_TRISTRIP)
151
15.1k
    {
152
15.1k
        if (poMP != nullptr && poLastPoly != nullptr)
153
3.52k
        {
154
3.52k
            poMP->addGeometryDirectly(poLastPoly);
155
3.52k
            poLastPoly = nullptr;
156
3.52k
        }
157
158
15.1k
        OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
159
80.3k
        for (int iBaseVert = 0; iBaseVert < nPartPoints - 2; iBaseVert++)
160
65.1k
        {
161
65.1k
            const int iSrcVert = iBaseVert;
162
163
65.1k
            OGRPoint oPoint1(padfX[iSrcVert], padfY[iSrcVert], padfZ[iSrcVert]);
164
165
65.1k
            OGRPoint oPoint2(padfX[iSrcVert + 1], padfY[iSrcVert + 1],
166
65.1k
                             padfZ[iSrcVert + 1]);
167
168
65.1k
            OGRPoint oPoint3(padfX[iSrcVert + 2], padfY[iSrcVert + 2],
169
65.1k
                             padfZ[iSrcVert + 2]);
170
171
65.1k
            OGRTriangle *poTriangle =
172
65.1k
                new OGRTriangle(oPoint1, oPoint2, oPoint3);
173
174
65.1k
            poTIN->addGeometryDirectly(poTriangle);
175
65.1k
        }
176
15.1k
        poGC->addGeometryDirectly(poTIN);
177
15.1k
    }
178
36.9k
    else if (nPartType == SHPP_TRIFAN)
179
3.50k
    {
180
3.50k
        if (poMP != nullptr && poLastPoly != nullptr)
181
565
        {
182
565
            poMP->addGeometryDirectly(poLastPoly);
183
565
            poLastPoly = nullptr;
184
565
        }
185
186
3.50k
        OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
187
21.6k
        for (int iBaseVert = 0; iBaseVert < nPartPoints - 2; iBaseVert++)
188
18.1k
        {
189
18.1k
            const int iSrcVert = iBaseVert;
190
191
18.1k
            OGRPoint oPoint1(padfX[0], padfY[0], padfZ[0]);
192
193
18.1k
            OGRPoint oPoint2(padfX[iSrcVert + 1], padfY[iSrcVert + 1],
194
18.1k
                             padfZ[iSrcVert + 1]);
195
196
18.1k
            OGRPoint oPoint3(padfX[iSrcVert + 2], padfY[iSrcVert + 2],
197
18.1k
                             padfZ[iSrcVert + 2]);
198
199
18.1k
            OGRTriangle *poTriangle =
200
18.1k
                new OGRTriangle(oPoint1, oPoint2, oPoint3);
201
202
18.1k
            poTIN->addGeometryDirectly(poTriangle);
203
18.1k
        }
204
3.50k
        poGC->addGeometryDirectly(poTIN);
205
3.50k
    }
206
33.4k
    else if (nPartType == SHPP_OUTERRING || nPartType == SHPP_INNERRING ||
207
23.3k
             nPartType == SHPP_FIRSTRING || nPartType == SHPP_RING)
208
15.0k
    {
209
15.0k
        if (poMP == nullptr)
210
7.18k
        {
211
7.18k
            poMP = new OGRMultiPolygon();
212
7.18k
        }
213
214
15.0k
        if (poMP != nullptr && poLastPoly != nullptr &&
215
3.22k
            (nPartType == SHPP_OUTERRING || nPartType == SHPP_FIRSTRING))
216
1.88k
        {
217
1.88k
            poMP->addGeometryDirectly(poLastPoly);
218
1.88k
            poLastPoly = nullptr;
219
1.88k
        }
220
221
15.0k
        if (poLastPoly == nullptr)
222
13.7k
            poLastPoly = new OGRPolygon();
223
224
15.0k
        OGRLinearRing *poRing = new OGRLinearRing;
225
226
15.0k
        poRing->setPoints(nPartPoints, const_cast<double *>(padfX),
227
15.0k
                          const_cast<double *>(padfY),
228
15.0k
                          const_cast<double *>(padfZ));
229
230
15.0k
        poRing->closeRings();
231
232
15.0k
        poLastPoly->addRingDirectly(poRing);
233
15.0k
    }
234
18.3k
    else if (nPartType == SHPP_TRIANGLES)
235
9.01k
    {
236
9.01k
        if (poMP != nullptr && poLastPoly != nullptr)
237
4.70k
        {
238
4.70k
            poMP->addGeometryDirectly(poLastPoly);
239
4.70k
            poLastPoly = nullptr;
240
4.70k
        }
241
242
9.01k
        OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
243
34.1k
        for (int iBaseVert = 0; iBaseVert < nPartPoints - 2; iBaseVert += 3)
244
25.1k
        {
245
25.1k
            const int iSrcVert = iBaseVert;
246
247
25.1k
            OGRPoint oPoint1(padfX[iSrcVert], padfY[iSrcVert], padfZ[iSrcVert]);
248
249
25.1k
            OGRPoint oPoint2(padfX[iSrcVert + 1], padfY[iSrcVert + 1],
250
25.1k
                             padfZ[iSrcVert + 1]);
251
252
25.1k
            OGRPoint oPoint3(padfX[iSrcVert + 2], padfY[iSrcVert + 2],
253
25.1k
                             padfZ[iSrcVert + 2]);
254
255
25.1k
            OGRTriangle *poTriangle =
256
25.1k
                new OGRTriangle(oPoint1, oPoint2, oPoint3);
257
258
25.1k
            poTIN->addGeometryDirectly(poTriangle);
259
25.1k
        }
260
9.01k
        poGC->addGeometryDirectly(poTIN);
261
9.01k
    }
262
9.33k
    else
263
9.33k
        CPLDebug("OGR", "Unrecognized parttype %d, ignored.", nPartType);
264
52.1k
}
265
266
static bool
267
RegisterEdge(const double *padfX, const double *padfY, const double *padfZ,
268
             int nPart,
269
             std::map<std::vector<double>, std::pair<int, int>> &oMapEdges)
270
9.12k
{
271
9.12k
    int idx = 0;
272
9.12k
    if (padfX[0] > padfX[1])
273
327
    {
274
327
        idx = 1;
275
327
    }
276
8.79k
    else if (padfX[0] == padfX[1])
277
8.46k
    {
278
8.46k
        if (padfY[0] > padfY[1])
279
1.74k
        {
280
1.74k
            idx = 1;
281
1.74k
        }
282
6.72k
        else if (padfY[0] == padfY[1])
283
5.14k
        {
284
5.14k
            if (padfZ[0] > padfZ[1])
285
68
            {
286
68
                idx = 1;
287
68
            }
288
5.14k
        }
289
8.46k
    }
290
9.12k
    std::vector<double> oVector;
291
9.12k
    oVector.push_back(padfX[idx]);
292
9.12k
    oVector.push_back(padfY[idx]);
293
9.12k
    oVector.push_back(padfZ[idx]);
294
9.12k
    oVector.push_back(padfX[1 - idx]);
295
9.12k
    oVector.push_back(padfY[1 - idx]);
296
9.12k
    oVector.push_back(padfZ[1 - idx]);
297
9.12k
    const auto oIter = oMapEdges.find(oVector);
298
9.12k
    if (oIter == oMapEdges.end())
299
5.64k
    {
300
5.64k
        oMapEdges[oVector] = std::pair(nPart, -1);
301
5.64k
    }
302
3.47k
    else
303
3.47k
    {
304
3.47k
        CPLAssert(oIter->second.first >= 0);
305
3.47k
        if (oIter->second.second < 0)
306
1.93k
            oIter->second.second = nPart;
307
1.54k
        else
308
1.54k
            return false;
309
3.47k
    }
310
7.58k
    return true;
311
9.12k
}
312
313
static const std::pair<int, int> &GetEdgeOwners(
314
    const double *padfX, const double *padfY, const double *padfZ,
315
    const std::map<std::vector<double>, std::pair<int, int>> &oMapEdges)
316
0
{
317
0
    int idx = 0;
318
0
    if (padfX[0] > padfX[1])
319
0
    {
320
0
        idx = 1;
321
0
    }
322
0
    else if (padfX[0] == padfX[1])
323
0
    {
324
0
        if (padfY[0] > padfY[1])
325
0
        {
326
0
            idx = 1;
327
0
        }
328
0
        else if (padfY[0] == padfY[1])
329
0
        {
330
0
            if (padfZ[0] > padfZ[1])
331
0
            {
332
0
                idx = 1;
333
0
            }
334
0
        }
335
0
    }
336
0
    std::vector<double> oVector;
337
0
    oVector.push_back(padfX[idx]);
338
0
    oVector.push_back(padfY[idx]);
339
0
    oVector.push_back(padfZ[idx]);
340
0
    oVector.push_back(padfX[1 - idx]);
341
0
    oVector.push_back(padfY[1 - idx]);
342
0
    oVector.push_back(padfZ[1 - idx]);
343
0
    const auto oIter = oMapEdges.find(oVector);
344
0
    CPLAssert(oIter != oMapEdges.end());
345
0
    return oIter->second;
346
0
}
347
348
/************************************************************************/
349
/*                     OGRCreateFromMultiPatch()                        */
350
/*                                                                      */
351
/*      Translate a multipatch representation to an OGR geometry        */
352
/************************************************************************/
353
354
OGRGeometry *OGRCreateFromMultiPatch(int nParts, const GInt32 *panPartStart,
355
                                     const GInt32 *panPartType, int nPoints,
356
                                     const double *padfX, const double *padfY,
357
                                     const double *padfZ)
358
9.49k
{
359
    // Deal with particular case of a patch of OuterRing of 4 points
360
    // that form a TIN. And be robust to consecutive duplicated triangles !
361
9.49k
    std::map<std::vector<double>, std::pair<int, int>> oMapEdges;
362
9.49k
    bool bTINCandidate = nParts >= 2;
363
9.49k
    std::set<int> oSetDuplicated;
364
10.9k
    for (int iPart = 0; iPart < nParts && panPartStart != nullptr; iPart++)
365
10.9k
    {
366
10.9k
        int nPartPoints = 0;
367
368
        // Figure out details about this part's vertex list.
369
10.9k
        if (iPart == nParts - 1)
370
1.48k
            nPartPoints = nPoints - panPartStart[iPart];
371
9.51k
        else
372
9.51k
            nPartPoints = panPartStart[iPart + 1] - panPartStart[iPart];
373
10.9k
        const int nPartStart = panPartStart[iPart];
374
375
10.9k
        if (panPartType[iPart] == SHPP_OUTERRING && nPartPoints == 4 &&
376
4.33k
            padfX[nPartStart] == padfX[nPartStart + 3] &&
377
3.69k
            padfY[nPartStart] == padfY[nPartStart + 3] &&
378
3.25k
            padfZ[nPartStart] == padfZ[nPartStart + 3] &&
379
3.04k
            !std::isnan(padfX[nPartStart]) &&
380
3.04k
            !std::isnan(padfX[nPartStart + 1]) &&
381
3.04k
            !std::isnan(padfX[nPartStart + 2]) &&
382
3.04k
            !std::isnan(padfY[nPartStart]) &&
383
3.04k
            !std::isnan(padfY[nPartStart + 1]) &&
384
3.04k
            !std::isnan(padfY[nPartStart + 2]) &&
385
3.04k
            !std::isnan(padfZ[nPartStart]) &&
386
3.04k
            !std::isnan(padfZ[nPartStart + 1]) &&
387
3.04k
            !std::isnan(padfZ[nPartStart + 2]))
388
3.04k
        {
389
3.04k
            bool bDuplicate = false;
390
3.04k
            if (iPart > 0)
391
0
            {
392
0
                bDuplicate = true;
393
0
                const int nPrevPartStart = panPartStart[iPart - 1];
394
0
                for (int j = 0; j < 3; j++)
395
0
                {
396
0
                    if (padfX[nPartStart + j] == padfX[nPrevPartStart + j] &&
397
0
                        padfY[nPartStart + j] == padfY[nPrevPartStart + j] &&
398
0
                        padfZ[nPartStart + j] == padfZ[nPrevPartStart + j])
399
0
                    {
400
0
                    }
401
0
                    else
402
0
                    {
403
0
                        bDuplicate = false;
404
0
                        break;
405
0
                    }
406
0
                }
407
0
            }
408
3.04k
            if (bDuplicate)
409
0
            {
410
0
                oSetDuplicated.insert(iPart);
411
0
            }
412
3.04k
            else if (RegisterEdge(padfX + nPartStart, padfY + nPartStart,
413
3.04k
                                  padfZ + nPartStart, iPart, oMapEdges) &&
414
3.04k
                     RegisterEdge(padfX + nPartStart + 1,
415
3.04k
                                  padfY + nPartStart + 1,
416
3.04k
                                  padfZ + nPartStart + 1, iPart, oMapEdges) &&
417
3.04k
                     RegisterEdge(padfX + nPartStart + 2,
418
3.04k
                                  padfY + nPartStart + 2,
419
3.04k
                                  padfZ + nPartStart + 2, iPart, oMapEdges))
420
1.50k
            {
421
                // ok
422
1.50k
            }
423
1.54k
            else
424
1.54k
            {
425
1.54k
                bTINCandidate = false;
426
1.54k
                break;
427
1.54k
            }
428
3.04k
        }
429
7.95k
        else
430
7.95k
        {
431
7.95k
            bTINCandidate = false;
432
7.95k
            break;
433
7.95k
        }
434
10.9k
    }
435
9.49k
    if (bTINCandidate && panPartStart != nullptr)
436
0
    {
437
0
        std::set<int> oVisitedParts;
438
0
        std::set<int> oToBeVisitedParts;
439
0
        oToBeVisitedParts.insert(0);
440
0
        while (!oToBeVisitedParts.empty())
441
0
        {
442
0
            const int iPart = *(oToBeVisitedParts.begin());
443
0
            oVisitedParts.insert(iPart);
444
0
            oToBeVisitedParts.erase(iPart);
445
446
0
            const int nPartStart = panPartStart[iPart];
447
0
            for (int j = 0; j < 3; j++)
448
0
            {
449
0
                const auto &oPair = GetEdgeOwners(
450
0
                    padfX + nPartStart + j, padfY + nPartStart + j,
451
0
                    padfZ + nPartStart + j, oMapEdges);
452
0
                const int iOtherPart =
453
0
                    (oPair.first == iPart) ? oPair.second : oPair.first;
454
0
                if (iOtherPart >= 0 &&
455
0
                    oVisitedParts.find(iOtherPart) == oVisitedParts.end())
456
0
                {
457
0
                    oToBeVisitedParts.insert(iOtherPart);
458
0
                }
459
0
            }
460
0
        }
461
0
        if (static_cast<int>(oVisitedParts.size()) ==
462
0
            nParts - static_cast<int>(oSetDuplicated.size()))
463
0
        {
464
0
            OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
465
0
            for (int iPart = 0; iPart < nParts; iPart++)
466
0
            {
467
0
                if (oSetDuplicated.find(iPart) != oSetDuplicated.end())
468
0
                    continue;
469
470
0
                const int nPartStart = panPartStart[iPart];
471
0
                OGRPoint oPoint1(padfX[nPartStart], padfY[nPartStart],
472
0
                                 padfZ[nPartStart]);
473
474
0
                OGRPoint oPoint2(padfX[nPartStart + 1], padfY[nPartStart + 1],
475
0
                                 padfZ[nPartStart + 1]);
476
477
0
                OGRPoint oPoint3(padfX[nPartStart + 2], padfY[nPartStart + 2],
478
0
                                 padfZ[nPartStart + 2]);
479
480
0
                OGRTriangle *poTriangle =
481
0
                    new OGRTriangle(oPoint1, oPoint2, oPoint3);
482
483
0
                poTIN->addGeometryDirectly(poTriangle);
484
0
            }
485
0
            return poTIN;
486
0
        }
487
0
    }
488
489
9.49k
    OGRGeometryCollection *poGC = new OGRGeometryCollection();
490
9.49k
    OGRMultiPolygon *poMP = nullptr;
491
9.49k
    OGRPolygon *poLastPoly = nullptr;
492
61.6k
    for (int iPart = 0; iPart < nParts; iPart++)
493
52.1k
    {
494
52.1k
        int nPartPoints = 0;
495
52.1k
        int nPartStart = 0;
496
497
        // Figure out details about this part's vertex list.
498
52.1k
        if (panPartStart == nullptr)
499
0
        {
500
0
            nPartPoints = nPoints;
501
0
        }
502
52.1k
        else
503
52.1k
        {
504
505
52.1k
            if (iPart == nParts - 1)
506
9.49k
                nPartPoints = nPoints - panPartStart[iPart];
507
42.6k
            else
508
42.6k
                nPartPoints = panPartStart[iPart + 1] - panPartStart[iPart];
509
52.1k
            nPartStart = panPartStart[iPart];
510
52.1k
        }
511
512
52.1k
        OGRCreateFromMultiPatchPart(poGC, poMP, poLastPoly, panPartType[iPart],
513
52.1k
                                    nPartPoints, padfX + nPartStart,
514
52.1k
                                    padfY + nPartStart, padfZ + nPartStart);
515
52.1k
    }
516
517
9.49k
    if (poMP != nullptr && poLastPoly != nullptr)
518
3.06k
    {
519
3.06k
        poMP->addGeometryDirectly(poLastPoly);
520
        // poLastPoly = NULL;
521
3.06k
    }
522
9.49k
    if (poMP != nullptr)
523
7.18k
        poGC->addGeometryDirectly(poMP);
524
525
9.49k
    if (poGC->getNumGeometries() == 1)
526
943
    {
527
943
        OGRGeometry *poResultGeom = poGC->getGeometryRef(0);
528
943
        poGC->removeGeometry(0, FALSE);
529
943
        delete poGC;
530
943
        return poResultGeom;
531
943
    }
532
533
8.55k
    else
534
8.55k
        return poGC;
535
9.49k
}
536
537
/************************************************************************/
538
/*                      OGRWriteToShapeBin()                            */
539
/*                                                                      */
540
/*      Translate OGR geometry to a shapefile binary representation     */
541
/************************************************************************/
542
543
OGRErr OGRWriteToShapeBin(const OGRGeometry *poGeom, GByte **ppabyShape,
544
                          int *pnBytes)
545
0
{
546
0
    int nShpSize = 4;  // All types start with integer type number.
547
548
    /* -------------------------------------------------------------------- */
549
    /*      Null or Empty input maps to SHPT_NULL.                          */
550
    /* -------------------------------------------------------------------- */
551
0
    if (!poGeom || poGeom->IsEmpty())
552
0
    {
553
0
        *ppabyShape = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nShpSize));
554
0
        if (*ppabyShape == nullptr)
555
0
            return OGRERR_FAILURE;
556
0
        GUInt32 zero = SHPT_NULL;
557
0
        memcpy(*ppabyShape, &zero, nShpSize);
558
0
        *pnBytes = nShpSize;
559
0
        return OGRERR_NONE;
560
0
    }
561
562
0
    const OGRwkbGeometryType nOGRType = wkbFlatten(poGeom->getGeometryType());
563
0
    const bool b3d = wkbHasZ(poGeom->getGeometryType());
564
0
    const bool bHasM = wkbHasM(poGeom->getGeometryType());
565
0
    const int nCoordDims = poGeom->CoordinateDimension();
566
567
0
    int nShpZSize = 0;  // Z gets tacked onto the end.
568
0
    GUInt32 nPoints = 0;
569
0
    GUInt32 nParts = 0;
570
571
    /* -------------------------------------------------------------------- */
572
    /*      Calculate the shape buffer size                                 */
573
    /* -------------------------------------------------------------------- */
574
0
    if (nOGRType == wkbPoint)
575
0
    {
576
0
        nShpSize += 8 * nCoordDims;
577
0
    }
578
0
    else if (nOGRType == wkbLineString)
579
0
    {
580
0
        const OGRLineString *poLine = poGeom->toLineString();
581
0
        nPoints = poLine->getNumPoints();
582
0
        nParts = 1;
583
0
        nShpSize += 16 * nCoordDims;           // xy(z)(m) box.
584
0
        nShpSize += 4;                         // nparts.
585
0
        nShpSize += 4;                         // npoints.
586
0
        nShpSize += 4;                         // Parts[1].
587
0
        nShpSize += 8 * nCoordDims * nPoints;  // Points.
588
0
        nShpZSize = 16 + 8 * nPoints;
589
0
    }
590
0
    else if (nOGRType == wkbPolygon)
591
0
    {
592
0
        std::unique_ptr<OGRPolygon> poPoly(poGeom->toPolygon()->clone());
593
0
        poPoly->closeRings();
594
0
        nParts = poPoly->getNumInteriorRings() + 1;
595
0
        for (const auto poRing : *poPoly)
596
0
        {
597
0
            nPoints += poRing->getNumPoints();
598
0
        }
599
0
        nShpSize += 16 * nCoordDims;           // xy(z)(m) box.
600
0
        nShpSize += 4;                         // nparts.
601
0
        nShpSize += 4;                         // npoints.
602
0
        nShpSize += 4 * nParts;                // parts[nparts]
603
0
        nShpSize += 8 * nCoordDims * nPoints;  // Points.
604
0
        nShpZSize = 16 + 8 * nPoints;
605
0
    }
606
0
    else if (nOGRType == wkbMultiPoint)
607
0
    {
608
0
        const OGRMultiPoint *poMPoint = poGeom->toMultiPoint();
609
0
        for (const auto poPoint : *poMPoint)
610
0
        {
611
0
            if (poPoint->IsEmpty())
612
0
                continue;
613
0
            nPoints++;
614
0
        }
615
0
        nShpSize += 16 * nCoordDims;           // xy(z)(m) box.
616
0
        nShpSize += 4;                         // npoints.
617
0
        nShpSize += 8 * nCoordDims * nPoints;  // Points.
618
0
        nShpZSize = 16 + 8 * nPoints;
619
0
    }
620
0
    else if (nOGRType == wkbMultiLineString)
621
0
    {
622
0
        const OGRMultiLineString *poMLine = poGeom->toMultiLineString();
623
0
        for (const auto poLine : *poMLine)
624
0
        {
625
            // Skip empties.
626
0
            if (poLine->IsEmpty())
627
0
                continue;
628
0
            nParts++;
629
0
            nPoints += poLine->getNumPoints();
630
0
        }
631
0
        nShpSize += 16 * nCoordDims;           //* xy(z)(m) box.
632
0
        nShpSize += 4;                         // nparts.
633
0
        nShpSize += 4;                         // npoints.
634
0
        nShpSize += 4 * nParts;                // parts[nparts].
635
0
        nShpSize += 8 * nCoordDims * nPoints;  // Points.
636
0
        nShpZSize = 16 + 8 * nPoints;
637
0
    }
638
0
    else if (nOGRType == wkbMultiPolygon)
639
0
    {
640
0
        std::unique_ptr<OGRMultiPolygon> poMPoly(
641
0
            poGeom->toMultiPolygon()->clone());
642
0
        poMPoly->closeRings();
643
0
        for (const auto poPoly : *poMPoly)
644
0
        {
645
            // Skip empties.
646
0
            if (poPoly->IsEmpty())
647
0
                continue;
648
649
0
            const int nRings = poPoly->getNumInteriorRings() + 1;
650
0
            nParts += nRings;
651
0
            for (const auto poRing : *poPoly)
652
0
            {
653
0
                nPoints += poRing->getNumPoints();
654
0
            }
655
0
        }
656
0
        nShpSize += 16 * nCoordDims;           // xy(z)(m) box.
657
0
        nShpSize += 4;                         // nparts.
658
0
        nShpSize += 4;                         // npoints.
659
0
        nShpSize += 4 * nParts;                // parts[nparts].
660
0
        nShpSize += 8 * nCoordDims * nPoints;  // Points.
661
0
        nShpZSize = 16 + 8 * nPoints;
662
0
    }
663
0
    else
664
0
    {
665
0
        return OGRERR_UNSUPPORTED_OPERATION;
666
0
    }
667
668
// #define WRITE_ARC_HACK
669
#ifdef WRITE_ARC_HACK
670
    int nShpSizeBeforeCurve = nShpSize;
671
    nShpSize += 4 + 4 + 4 + 20;
672
#endif
673
    // Allocate our shape buffer.
674
0
    *ppabyShape = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nShpSize));
675
0
    if (!*ppabyShape)
676
0
        return OGRERR_FAILURE;
677
678
#ifdef WRITE_ARC_HACK
679
    /* To be used with:
680
id,WKT
681
1,"LINESTRING (1 0,0 1)"
682
2,"LINESTRING (5 1,6 0)"
683
3,"LINESTRING (10 1,11 0)"
684
4,"LINESTRING (16 0,15 1)"
685
5,"LINESTRING (21 0,20 1)"
686
6,"LINESTRING (31 0,30 2)" <-- not constant radius
687
    */
688
689
    GUInt32 nTmp = 1;
690
    memcpy((*ppabyShape) + nShpSizeBeforeCurve, &nTmp, 4);
691
    nTmp = 0;
692
    memcpy((*ppabyShape) + nShpSizeBeforeCurve + 4, &nTmp, 4);
693
    nTmp = EXT_SHAPE_SEGMENT_ARC;
694
    memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8, &nTmp, 4);
695
    static int nCounter = 0;
696
    nCounter++;
697
    if (nCounter == 1)
698
    {
699
        double dfVal = 0;
700
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
701
        dfVal = 0;
702
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
703
        nTmp = 0;
704
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
705
    }
706
    else if (nCounter == 2)
707
    {
708
        double dfVal = 5;
709
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
710
        dfVal = 0;
711
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
712
        nTmp = EXT_SHAPE_ARC_MINOR;
713
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
714
    }
715
    else if (nCounter == 3)
716
    {
717
        double dfVal = 10;
718
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
719
        dfVal = 0;
720
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
721
        nTmp = EXT_SHAPE_ARC_CCW;
722
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
723
    }
724
    else if (nCounter == 4)
725
    {
726
        double dfVal = 15;
727
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
728
        dfVal = 0;
729
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
730
        nTmp = EXT_SHAPE_ARC_CCW | EXT_SHAPE_ARC_MINOR;
731
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
732
    }
733
    else if (nCounter == 5)
734
    {
735
        double dfVal = 20;
736
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
737
        dfVal = 0;
738
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
739
        // Inconsistent with SP and EP. Only the CCW/not CCW is taken into
740
        // account by ArcGIS.
741
        nTmp = EXT_SHAPE_ARC_MINOR;
742
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
743
    }
744
    else if (nCounter == 6)
745
    {
746
        double dfVal = 30;  // Radius inconsistent with SP and EP.
747
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4, &dfVal, 8);
748
        dfVal = 0;
749
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 8, &dfVal, 8);
750
        nTmp = EXT_SHAPE_ARC_MINOR;
751
        memcpy((*ppabyShape) + nShpSizeBeforeCurve + 8 + 4 + 16, &nTmp, 4);
752
    }
753
#endif
754
755
    // Fill in the output size.
756
0
    *pnBytes = nShpSize;
757
758
    // Set up write pointers.
759
0
    unsigned char *pabyPtr = *ppabyShape;
760
0
    unsigned char *pabyPtrM = bHasM ? pabyPtr + nShpSize - nShpZSize : nullptr;
761
762
0
    unsigned char *pabyPtrZ = nullptr;
763
0
    if (b3d)
764
0
    {
765
0
        if (bHasM)
766
0
            pabyPtrZ = pabyPtrM - nShpZSize;
767
0
        else
768
0
            pabyPtrZ = pabyPtr + nShpSize - nShpZSize;
769
0
    }
770
771
    /* -------------------------------------------------------------------- */
772
    /*      Write in the Shape type number now                              */
773
    /* -------------------------------------------------------------------- */
774
0
    GUInt32 nGType = SHPT_NULL;
775
776
0
    switch (nOGRType)
777
0
    {
778
0
        case wkbPoint:
779
0
        {
780
0
            nGType = (b3d && bHasM) ? SHPT_POINTZM
781
0
                     : (b3d)        ? SHPT_POINTZ
782
0
                     : (bHasM)      ? SHPT_POINTM
783
0
                                    : SHPT_POINT;
784
0
            break;
785
0
        }
786
0
        case wkbMultiPoint:
787
0
        {
788
0
            nGType = (b3d && bHasM) ? SHPT_MULTIPOINTZM
789
0
                     : (b3d)        ? SHPT_MULTIPOINTZ
790
0
                     : (bHasM)      ? SHPT_MULTIPOINTM
791
0
                                    : SHPT_MULTIPOINT;
792
0
            break;
793
0
        }
794
0
        case wkbLineString:
795
0
        case wkbMultiLineString:
796
0
        {
797
0
            nGType = (b3d && bHasM) ? SHPT_ARCZM
798
0
                     : (b3d)        ? SHPT_ARCZ
799
0
                     : (bHasM)      ? SHPT_ARCM
800
0
                                    : SHPT_ARC;
801
0
            break;
802
0
        }
803
0
        case wkbPolygon:
804
0
        case wkbMultiPolygon:
805
0
        {
806
0
            nGType = (b3d && bHasM) ? SHPT_POLYGONZM
807
0
                     : (b3d)        ? SHPT_POLYGONZ
808
0
                     : (bHasM)      ? SHPT_POLYGONM
809
0
                                    : SHPT_POLYGON;
810
0
            break;
811
0
        }
812
0
        default:
813
0
        {
814
0
            return OGRERR_UNSUPPORTED_OPERATION;
815
0
        }
816
0
    }
817
        // Write in the type number and advance the pointer.
818
#ifdef WRITE_ARC_HACK
819
    nGType = SHPT_GENERALPOLYLINE | 0x20000000;
820
#endif
821
822
0
    CPL_LSBPTR32(&nGType);
823
0
    memcpy(pabyPtr, &nGType, 4);
824
0
    pabyPtr += 4;
825
826
    /* -------------------------------------------------------------------- */
827
    /*      POINT and POINTZ                                                */
828
    /* -------------------------------------------------------------------- */
829
0
    if (nOGRType == wkbPoint)
830
0
    {
831
0
        auto poPoint = poGeom->toPoint();
832
0
        const double x = poPoint->getX();
833
0
        const double y = poPoint->getY();
834
835
        // Copy in the raw data.
836
0
        memcpy(pabyPtr, &x, 8);
837
0
        memcpy(pabyPtr + 8, &y, 8);
838
0
        if (b3d)
839
0
        {
840
0
            double z = poPoint->getZ();
841
0
            memcpy(pabyPtr + 8 + 8, &z, 8);
842
0
        }
843
0
        if (bHasM)
844
0
        {
845
0
            double m = poPoint->getM();
846
0
            memcpy(pabyPtr + 8 + ((b3d) ? 16 : 8), &m, 8);
847
0
        }
848
849
        // Swap if needed. Shape doubles always LSB.
850
        if constexpr (OGR_SWAP(wkbNDR))
851
        {
852
            CPL_SWAPDOUBLE(pabyPtr);
853
            CPL_SWAPDOUBLE(pabyPtr + 8);
854
            if (b3d)
855
                CPL_SWAPDOUBLE(pabyPtr + 8 + 8);
856
            if (bHasM)
857
                CPL_SWAPDOUBLE(pabyPtr + 8 + ((b3d) ? 16 : 8));
858
        }
859
860
0
        return OGRERR_NONE;
861
0
    }
862
863
    /* -------------------------------------------------------------------- */
864
    /*      All the non-POINT types require an envelope next                */
865
    /* -------------------------------------------------------------------- */
866
0
    OGREnvelope3D envelope;
867
0
    poGeom->getEnvelope(&envelope);
868
0
    memcpy(pabyPtr, &(envelope.MinX), 8);
869
0
    memcpy(pabyPtr + 8, &(envelope.MinY), 8);
870
0
    memcpy(pabyPtr + 8 + 8, &(envelope.MaxX), 8);
871
0
    memcpy(pabyPtr + 8 + 8 + 8, &(envelope.MaxY), 8);
872
873
    // Swap box if needed. Shape doubles are always LSB.
874
    if constexpr (OGR_SWAP(wkbNDR))
875
    {
876
        for (int i = 0; i < 4; i++)
877
            CPL_SWAPDOUBLE(pabyPtr + 8 * i);
878
    }
879
0
    pabyPtr += 32;
880
881
    // Write in the Z bounds at the end of the XY buffer.
882
0
    if (b3d)
883
0
    {
884
0
        memcpy(pabyPtrZ, &(envelope.MinZ), 8);
885
0
        memcpy(pabyPtrZ + 8, &(envelope.MaxZ), 8);
886
887
        // Swap Z bounds if necessary.
888
        if constexpr (OGR_SWAP(wkbNDR))
889
        {
890
            for (int i = 0; i < 2; i++)
891
                CPL_SWAPDOUBLE(pabyPtrZ + 8 * i);
892
        }
893
0
        pabyPtrZ += 16;
894
0
    }
895
896
    // Reserve space for the M bounds at the end of the XY buffer.
897
0
    GByte *pabyPtrMBounds = nullptr;
898
0
    double dfMinM = std::numeric_limits<double>::max();
899
0
    double dfMaxM = -dfMinM;
900
0
    if (bHasM)
901
0
    {
902
0
        pabyPtrMBounds = pabyPtrM;
903
0
        pabyPtrM += 16;
904
0
    }
905
906
    /* -------------------------------------------------------------------- */
907
    /*      LINESTRING and LINESTRINGZ                                      */
908
    /* -------------------------------------------------------------------- */
909
0
    if (nOGRType == wkbLineString)
910
0
    {
911
0
        auto poLine = poGeom->toLineString();
912
913
        // Write in the nparts (1).
914
0
        const GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
915
0
        memcpy(pabyPtr, &nPartsLsb, 4);
916
0
        pabyPtr += 4;
917
918
        // Write in the npoints.
919
0
        GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
920
0
        memcpy(pabyPtr, &nPointsLsb, 4);
921
0
        pabyPtr += 4;
922
923
        // Write in the part index (0).
924
0
        GUInt32 nPartIndex = 0;
925
0
        memcpy(pabyPtr, &nPartIndex, 4);
926
0
        pabyPtr += 4;
927
928
        // Write in the point data.
929
0
        poLine->getPoints(reinterpret_cast<OGRRawPoint *>(pabyPtr),
930
0
                          reinterpret_cast<double *>(pabyPtrZ));
931
0
        if (bHasM)
932
0
        {
933
0
            for (GUInt32 k = 0; k < nPoints; k++)
934
0
            {
935
0
                double dfM = poLine->getM(k);
936
0
                memcpy(pabyPtrM + 8 * k, &dfM, 8);
937
0
                if (dfM < dfMinM)
938
0
                    dfMinM = dfM;
939
0
                if (dfM > dfMaxM)
940
0
                    dfMaxM = dfM;
941
0
            }
942
0
        }
943
944
        // Swap if necessary.
945
        if constexpr (OGR_SWAP(wkbNDR))
946
        {
947
            for (GUInt32 k = 0; k < nPoints; k++)
948
            {
949
                CPL_SWAPDOUBLE(pabyPtr + 16 * k);
950
                CPL_SWAPDOUBLE(pabyPtr + 16 * k + 8);
951
                if (b3d)
952
                    CPL_SWAPDOUBLE(pabyPtrZ + 8 * k);
953
                if (bHasM)
954
                    CPL_SWAPDOUBLE(pabyPtrM + 8 * k);
955
            }
956
        }
957
0
    }
958
    /* -------------------------------------------------------------------- */
959
    /*      POLYGON and POLYGONZ                                            */
960
    /* -------------------------------------------------------------------- */
961
0
    else if (nOGRType == wkbPolygon)
962
0
    {
963
0
        auto poPoly = poGeom->toPolygon();
964
965
        // Write in the part count.
966
0
        GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
967
0
        memcpy(pabyPtr, &nPartsLsb, 4);
968
0
        pabyPtr += 4;
969
970
        // Write in the total point count.
971
0
        GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
972
0
        memcpy(pabyPtr, &nPointsLsb, 4);
973
0
        pabyPtr += 4;
974
975
        /* --------------------------------------------------------------------
976
         */
977
        /*      Now we have to visit each ring and write an index number into */
978
        /*      the parts list, and the coordinates into the points list. */
979
        /*      to do it in one pass, we will use three write pointers. */
980
        /*      pabyPtr writes the part indexes */
981
        /*      pabyPoints writes the xy coordinates */
982
        /*      pabyPtrZ writes the z coordinates */
983
        /* --------------------------------------------------------------------
984
         */
985
986
        // Just past the partindex[nparts] array.
987
0
        unsigned char *pabyPoints = pabyPtr + 4 * nParts;
988
989
0
        int nPointIndexCount = 0;
990
991
0
        for (GUInt32 i = 0; i < nParts; i++)
992
0
        {
993
            // Check our Ring and condition it.
994
0
            std::unique_ptr<OGRLinearRing> poRing;
995
0
            if (i == 0)
996
0
            {
997
0
                poRing.reset(poPoly->getExteriorRing()->clone());
998
0
                assert(poRing);
999
                // Outer ring must be clockwise.
1000
0
                if (!poRing->isClockwise())
1001
0
                    poRing->reversePoints();
1002
0
            }
1003
0
            else
1004
0
            {
1005
0
                poRing.reset(poPoly->getInteriorRing(i - 1)->clone());
1006
0
                assert(poRing);
1007
                // Inner rings should be anti-clockwise.
1008
0
                if (poRing->isClockwise())
1009
0
                    poRing->reversePoints();
1010
0
            }
1011
1012
0
            int nRingNumPoints = poRing->getNumPoints();
1013
1014
0
#ifndef WRITE_ARC_HACK
1015
            // Cannot write un-closed rings to shape.
1016
0
            if (nRingNumPoints <= 2 || !poRing->get_IsClosed())
1017
0
                return OGRERR_FAILURE;
1018
0
#endif
1019
1020
            // Write in the part index.
1021
0
            GUInt32 nPartIndex = CPL_LSBWORD32(nPointIndexCount);
1022
0
            memcpy(pabyPtr, &nPartIndex, 4);
1023
1024
            // Write in the point data.
1025
0
            poRing->getPoints(reinterpret_cast<OGRRawPoint *>(pabyPoints),
1026
0
                              reinterpret_cast<double *>(pabyPtrZ));
1027
0
            if (bHasM)
1028
0
            {
1029
0
                for (int k = 0; k < nRingNumPoints; k++)
1030
0
                {
1031
0
                    double dfM = poRing->getM(k);
1032
0
                    memcpy(pabyPtrM + 8 * k, &dfM, 8);
1033
0
                    if (dfM < dfMinM)
1034
0
                        dfMinM = dfM;
1035
0
                    if (dfM > dfMaxM)
1036
0
                        dfMaxM = dfM;
1037
0
                }
1038
0
            }
1039
1040
            // Swap if necessary.
1041
            if constexpr (OGR_SWAP(wkbNDR))
1042
            {
1043
                for (int k = 0; k < nRingNumPoints; k++)
1044
                {
1045
                    CPL_SWAPDOUBLE(pabyPoints + 16 * k);
1046
                    CPL_SWAPDOUBLE(pabyPoints + 16 * k + 8);
1047
                    if (b3d)
1048
                        CPL_SWAPDOUBLE(pabyPtrZ + 8 * k);
1049
                    if (bHasM)
1050
                        CPL_SWAPDOUBLE(pabyPtrM + 8 * k);
1051
                }
1052
            }
1053
1054
0
            nPointIndexCount += nRingNumPoints;
1055
            // Advance the write pointers.
1056
0
            pabyPtr += 4;
1057
0
            pabyPoints += 16 * nRingNumPoints;
1058
0
            if (b3d)
1059
0
                pabyPtrZ += 8 * nRingNumPoints;
1060
0
            if (bHasM)
1061
0
                pabyPtrM += 8 * nRingNumPoints;
1062
0
        }
1063
0
    }
1064
    /* -------------------------------------------------------------------- */
1065
    /*      MULTIPOINT and MULTIPOINTZ                                      */
1066
    /* -------------------------------------------------------------------- */
1067
0
    else if (nOGRType == wkbMultiPoint)
1068
0
    {
1069
0
        auto poMPoint = poGeom->toMultiPoint();
1070
1071
        // Write in the total point count.
1072
0
        GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
1073
0
        memcpy(pabyPtr, &nPointsLsb, 4);
1074
0
        pabyPtr += 4;
1075
1076
        /* --------------------------------------------------------------------
1077
         */
1078
        /*      Now we have to visit each point write it into the points list */
1079
        /*      We will use two write pointers. */
1080
        /*      pabyPtr writes the xy coordinates */
1081
        /*      pabyPtrZ writes the z coordinates */
1082
        /* --------------------------------------------------------------------
1083
         */
1084
1085
0
        for (auto &&poPt : poMPoint)
1086
0
        {
1087
            // Skip empties.
1088
0
            if (poPt->IsEmpty())
1089
0
                continue;
1090
1091
            // Write the coordinates.
1092
0
            double x = poPt->getX();
1093
0
            double y = poPt->getY();
1094
0
            memcpy(pabyPtr, &x, 8);
1095
0
            memcpy(pabyPtr + 8, &y, 8);
1096
0
            if (b3d)
1097
0
            {
1098
0
                double z = poPt->getZ();
1099
0
                memcpy(pabyPtrZ, &z, 8);
1100
0
            }
1101
0
            if (bHasM)
1102
0
            {
1103
0
                double dfM = poPt->getM();
1104
0
                memcpy(pabyPtrM, &dfM, 8);
1105
0
                if (dfM < dfMinM)
1106
0
                    dfMinM = dfM;
1107
0
                if (dfM > dfMaxM)
1108
0
                    dfMaxM = dfM;
1109
0
            }
1110
1111
            // Swap if necessary.
1112
            if constexpr (OGR_SWAP(wkbNDR))
1113
            {
1114
                CPL_SWAPDOUBLE(pabyPtr);
1115
                CPL_SWAPDOUBLE(pabyPtr + 8);
1116
                if (b3d)
1117
                    CPL_SWAPDOUBLE(pabyPtrZ);
1118
                if (bHasM)
1119
                    CPL_SWAPDOUBLE(pabyPtrM);
1120
            }
1121
1122
            // Advance the write pointers.
1123
0
            pabyPtr += 16;
1124
0
            if (b3d)
1125
0
                pabyPtrZ += 8;
1126
0
            if (bHasM)
1127
0
                pabyPtrM += 8;
1128
0
        }
1129
0
    }
1130
1131
    /* -------------------------------------------------------------------- */
1132
    /*      MULTILINESTRING and MULTILINESTRINGZ                            */
1133
    /* -------------------------------------------------------------------- */
1134
0
    else if (nOGRType == wkbMultiLineString)
1135
0
    {
1136
0
        auto poMLine = poGeom->toMultiLineString();
1137
1138
        // Write in the part count.
1139
0
        GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
1140
0
        memcpy(pabyPtr, &nPartsLsb, 4);
1141
0
        pabyPtr += 4;
1142
1143
        // Write in the total point count.
1144
0
        GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
1145
0
        memcpy(pabyPtr, &nPointsLsb, 4);
1146
0
        pabyPtr += 4;
1147
1148
        // Just past the partindex[nparts] array.
1149
0
        unsigned char *pabyPoints = pabyPtr + 4 * nParts;
1150
1151
0
        int nPointIndexCount = 0;
1152
1153
0
        for (auto &&poLine : poMLine)
1154
0
        {
1155
            // Skip empties.
1156
0
            if (poLine->IsEmpty())
1157
0
                continue;
1158
1159
0
            int nLineNumPoints = poLine->getNumPoints();
1160
1161
            // Write in the part index.
1162
0
            GUInt32 nPartIndex = CPL_LSBWORD32(nPointIndexCount);
1163
0
            memcpy(pabyPtr, &nPartIndex, 4);
1164
1165
            // Write in the point data.
1166
0
            poLine->getPoints(reinterpret_cast<OGRRawPoint *>(pabyPoints),
1167
0
                              reinterpret_cast<double *>(pabyPtrZ));
1168
0
            if (bHasM)
1169
0
            {
1170
0
                for (int k = 0; k < nLineNumPoints; k++)
1171
0
                {
1172
0
                    double dfM = poLine->getM(k);
1173
0
                    memcpy(pabyPtrM + 8 * k, &dfM, 8);
1174
0
                    if (dfM < dfMinM)
1175
0
                        dfMinM = dfM;
1176
0
                    if (dfM > dfMaxM)
1177
0
                        dfMaxM = dfM;
1178
0
                }
1179
0
            }
1180
1181
            // Swap if necessary.
1182
            if constexpr (OGR_SWAP(wkbNDR))
1183
            {
1184
                for (int k = 0; k < nLineNumPoints; k++)
1185
                {
1186
                    CPL_SWAPDOUBLE(pabyPoints + 16 * k);
1187
                    CPL_SWAPDOUBLE(pabyPoints + 16 * k + 8);
1188
                    if (b3d)
1189
                        CPL_SWAPDOUBLE(pabyPtrZ + 8 * k);
1190
                    if (bHasM)
1191
                        CPL_SWAPDOUBLE(pabyPtrM + 8 * k);
1192
                }
1193
            }
1194
1195
0
            nPointIndexCount += nLineNumPoints;
1196
1197
            // Advance the write pointers.
1198
0
            pabyPtr += 4;
1199
0
            pabyPoints += 16 * nLineNumPoints;
1200
0
            if (b3d)
1201
0
                pabyPtrZ += 8 * nLineNumPoints;
1202
0
            if (bHasM)
1203
0
                pabyPtrM += 8 * nLineNumPoints;
1204
0
        }
1205
0
    }
1206
    /* -------------------------------------------------------------------- */
1207
    /*      MULTIPOLYGON and MULTIPOLYGONZ                                  */
1208
    /* -------------------------------------------------------------------- */
1209
0
    else  // if( nOGRType == wkbMultiPolygon )
1210
0
    {
1211
0
        auto poMPoly = poGeom->toMultiPolygon();
1212
1213
        // Write in the part count.
1214
0
        GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
1215
0
        memcpy(pabyPtr, &nPartsLsb, 4);
1216
0
        pabyPtr += 4;
1217
1218
        // Write in the total point count.
1219
0
        GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
1220
0
        memcpy(pabyPtr, &nPointsLsb, 4);
1221
0
        pabyPtr += 4;
1222
1223
        /* --------------------------------------------------------------------
1224
         */
1225
        /*      Now we have to visit each ring and write an index number into */
1226
        /*      the parts list, and the coordinates into the points list. */
1227
        /*      to do it in one pass, we will use three write pointers. */
1228
        /*      pabyPtr writes the part indexes */
1229
        /*      pabyPoints writes the xy coordinates */
1230
        /*      pabyPtrZ writes the z coordinates */
1231
        /* --------------------------------------------------------------------
1232
         */
1233
1234
        // Just past the partindex[nparts] array.
1235
0
        unsigned char *pabyPoints = pabyPtr + 4 * nParts;
1236
1237
0
        int nPointIndexCount = 0;
1238
1239
0
        for (auto &&poPoly : poMPoly)
1240
0
        {
1241
            // Skip empties.
1242
0
            if (poPoly->IsEmpty())
1243
0
                continue;
1244
1245
0
            int nRings = 1 + poPoly->getNumInteriorRings();
1246
1247
0
            for (int j = 0; j < nRings; j++)
1248
0
            {
1249
                // Check our Ring and condition it.
1250
0
                std::unique_ptr<OGRLinearRing> poRing;
1251
0
                if (j == 0)
1252
0
                {
1253
0
                    poRing.reset(poPoly->getExteriorRing()->clone());
1254
0
                    assert(poRing != nullptr);
1255
                    // Outer ring must be clockwise.
1256
0
                    if (!poRing->isClockwise())
1257
0
                        poRing->reversePoints();
1258
0
                }
1259
0
                else
1260
0
                {
1261
0
                    poRing.reset(poPoly->getInteriorRing(j - 1)->clone());
1262
0
                    assert(poRing != nullptr);
1263
                    // Inner rings should be anti-clockwise.
1264
0
                    if (poRing->isClockwise())
1265
0
                        poRing->reversePoints();
1266
0
                }
1267
1268
0
                int nRingNumPoints = poRing->getNumPoints();
1269
1270
                // Cannot write closed rings to shape.
1271
0
                if (nRingNumPoints <= 2 || !poRing->get_IsClosed())
1272
0
                    return OGRERR_FAILURE;
1273
1274
                // Write in the part index.
1275
0
                GUInt32 nPartIndex = CPL_LSBWORD32(nPointIndexCount);
1276
0
                memcpy(pabyPtr, &nPartIndex, 4);
1277
1278
                // Write in the point data.
1279
0
                poRing->getPoints(reinterpret_cast<OGRRawPoint *>(pabyPoints),
1280
0
                                  reinterpret_cast<double *>(pabyPtrZ));
1281
0
                if (bHasM)
1282
0
                {
1283
0
                    for (int k = 0; k < nRingNumPoints; k++)
1284
0
                    {
1285
0
                        double dfM = poRing->getM(k);
1286
0
                        memcpy(pabyPtrM + 8 * k, &dfM, 8);
1287
0
                        if (dfM < dfMinM)
1288
0
                            dfMinM = dfM;
1289
0
                        if (dfM > dfMaxM)
1290
0
                            dfMaxM = dfM;
1291
0
                    }
1292
0
                }
1293
1294
                // Swap if necessary.
1295
                if constexpr (OGR_SWAP(wkbNDR))
1296
                {
1297
                    for (int k = 0; k < nRingNumPoints; k++)
1298
                    {
1299
                        CPL_SWAPDOUBLE(pabyPoints + 16 * k);
1300
                        CPL_SWAPDOUBLE(pabyPoints + 16 * k + 8);
1301
                        if (b3d)
1302
                            CPL_SWAPDOUBLE(pabyPtrZ + 8 * k);
1303
                        if (bHasM)
1304
                            CPL_SWAPDOUBLE(pabyPtrM + 8 * k);
1305
                    }
1306
                }
1307
1308
0
                nPointIndexCount += nRingNumPoints;
1309
                // Advance the write pointers.
1310
0
                pabyPtr += 4;
1311
0
                pabyPoints += 16 * nRingNumPoints;
1312
0
                if (b3d)
1313
0
                    pabyPtrZ += 8 * nRingNumPoints;
1314
0
                if (bHasM)
1315
0
                    pabyPtrM += 8 * nRingNumPoints;
1316
0
            }
1317
0
        }
1318
0
    }
1319
1320
0
    if (bHasM)
1321
0
    {
1322
0
        if (dfMinM > dfMaxM)
1323
0
        {
1324
0
            dfMinM = 0.0;
1325
0
            dfMaxM = 0.0;
1326
0
        }
1327
0
        memcpy(pabyPtrMBounds, &(dfMinM), 8);
1328
0
        memcpy(pabyPtrMBounds + 8, &(dfMaxM), 8);
1329
1330
        // Swap M bounds if necessary.
1331
        if constexpr (OGR_SWAP(wkbNDR))
1332
        {
1333
            for (int i = 0; i < 2; i++)
1334
                CPL_SWAPDOUBLE(pabyPtrMBounds + 8 * i);
1335
        }
1336
0
    }
1337
1338
0
    return OGRERR_NONE;
1339
0
}
1340
1341
/************************************************************************/
1342
/*                        OGRCreateMultiPatch()                         */
1343
/************************************************************************/
1344
1345
OGRErr OGRCreateMultiPatch(const OGRGeometry *poGeomConst,
1346
                           int bAllowSHPTTriangle, int &nParts,
1347
                           std::vector<int> &anPartStart,
1348
                           std::vector<int> &anPartType, int &nPoints,
1349
                           std::vector<OGRRawPoint> &aoPoints,
1350
                           std::vector<double> &adfZ)
1351
16
{
1352
16
    const OGRwkbGeometryType eType = wkbFlatten(poGeomConst->getGeometryType());
1353
16
    if (eType != wkbPolygon && eType != wkbTriangle &&
1354
3
        eType != wkbMultiPolygon && eType != wkbMultiSurface &&
1355
3
        eType != wkbTIN && eType != wkbPolyhedralSurface &&
1356
3
        eType != wkbGeometryCollection)
1357
3
    {
1358
3
        return OGRERR_UNSUPPORTED_OPERATION;
1359
3
    }
1360
1361
13
    std::unique_ptr<OGRGeometry> poGeom(poGeomConst->clone());
1362
13
    poGeom->closeRings();
1363
1364
13
    OGRMultiPolygon *poMPoly = nullptr;
1365
13
    std::unique_ptr<OGRGeometry> poGeomToDelete;
1366
13
    if (eType == wkbMultiPolygon)
1367
0
        poMPoly = poGeom->toMultiPolygon();
1368
13
    else
1369
13
    {
1370
13
        poGeomToDelete = std::unique_ptr<OGRGeometry>(
1371
13
            OGRGeometryFactory::forceToMultiPolygon(poGeom->clone()));
1372
13
        if (poGeomToDelete.get() &&
1373
13
            wkbFlatten(poGeomToDelete->getGeometryType()) == wkbMultiPolygon)
1374
13
        {
1375
13
            poMPoly = poGeomToDelete->toMultiPolygon();
1376
13
        }
1377
13
    }
1378
13
    if (poMPoly == nullptr)
1379
0
    {
1380
0
        return OGRERR_UNSUPPORTED_OPERATION;
1381
0
    }
1382
1383
13
    nParts = 0;
1384
13
    anPartStart.clear();
1385
13
    anPartType.clear();
1386
13
    nPoints = 0;
1387
13
    aoPoints.clear();
1388
13
    adfZ.clear();
1389
13
    int nBeginLastPart = 0;
1390
13
    for (const auto poPoly : *poMPoly)
1391
13
    {
1392
        // Skip empties.
1393
13
        if (poPoly->IsEmpty())
1394
0
            continue;
1395
1396
13
        const int nRings = poPoly->getNumInteriorRings() + 1;
1397
13
        const OGRLinearRing *poRing = poPoly->getExteriorRing();
1398
13
        if (nRings == 1 && poRing->getNumPoints() == 4)
1399
0
        {
1400
0
            int nCorrectedPoints = nPoints;
1401
0
            if (nParts > 0 && anPartType[nParts - 1] == SHPP_OUTERRING &&
1402
0
                nPoints - anPartStart[nParts - 1] == 4)
1403
0
            {
1404
0
                nCorrectedPoints--;
1405
0
            }
1406
1407
0
            if (nParts > 0 &&
1408
0
                ((anPartType[nParts - 1] == SHPP_TRIANGLES &&
1409
0
                  nPoints - anPartStart[nParts - 1] == 3) ||
1410
0
                 (anPartType[nParts - 1] == SHPP_OUTERRING &&
1411
0
                  nPoints - anPartStart[nParts - 1] == 4) ||
1412
0
                 anPartType[nParts - 1] == SHPP_TRIFAN) &&
1413
0
                poRing->getX(0) == aoPoints[nBeginLastPart].x &&
1414
0
                poRing->getY(0) == aoPoints[nBeginLastPart].y &&
1415
0
                poRing->getZ(0) == adfZ[nBeginLastPart] &&
1416
0
                poRing->getX(1) == aoPoints[nCorrectedPoints - 1].x &&
1417
0
                poRing->getY(1) == aoPoints[nCorrectedPoints - 1].y &&
1418
0
                poRing->getZ(1) == adfZ[nCorrectedPoints - 1])
1419
0
            {
1420
0
                nPoints = nCorrectedPoints;
1421
0
                anPartType[nParts - 1] = SHPP_TRIFAN;
1422
1423
0
                aoPoints.resize(nPoints + 1);
1424
0
                adfZ.resize(nPoints + 1);
1425
0
                aoPoints[nPoints].x = poRing->getX(2);
1426
0
                aoPoints[nPoints].y = poRing->getY(2);
1427
0
                adfZ[nPoints] = poRing->getZ(2);
1428
0
                nPoints++;
1429
0
            }
1430
0
            else if (nParts > 0 &&
1431
0
                     ((anPartType[nParts - 1] == SHPP_TRIANGLES &&
1432
0
                       nPoints - anPartStart[nParts - 1] == 3) ||
1433
0
                      (anPartType[nParts - 1] == SHPP_OUTERRING &&
1434
0
                       nPoints - anPartStart[nParts - 1] == 4) ||
1435
0
                      anPartType[nParts - 1] == SHPP_TRISTRIP) &&
1436
0
                     poRing->getX(0) == aoPoints[nCorrectedPoints - 2].x &&
1437
0
                     poRing->getY(0) == aoPoints[nCorrectedPoints - 2].y &&
1438
0
                     poRing->getZ(0) == adfZ[nCorrectedPoints - 2] &&
1439
0
                     poRing->getX(1) == aoPoints[nCorrectedPoints - 1].x &&
1440
0
                     poRing->getY(1) == aoPoints[nCorrectedPoints - 1].y &&
1441
0
                     poRing->getZ(1) == adfZ[nCorrectedPoints - 1])
1442
0
            {
1443
0
                nPoints = nCorrectedPoints;
1444
0
                anPartType[nParts - 1] = SHPP_TRISTRIP;
1445
1446
0
                aoPoints.resize(nPoints + 1);
1447
0
                adfZ.resize(nPoints + 1);
1448
0
                aoPoints[nPoints].x = poRing->getX(2);
1449
0
                aoPoints[nPoints].y = poRing->getY(2);
1450
0
                adfZ[nPoints] = poRing->getZ(2);
1451
0
                nPoints++;
1452
0
            }
1453
0
            else
1454
0
            {
1455
0
                if (nParts == 0 || anPartType[nParts - 1] != SHPP_TRIANGLES ||
1456
0
                    !bAllowSHPTTriangle)
1457
0
                {
1458
0
                    nBeginLastPart = nPoints;
1459
1460
0
                    anPartStart.resize(nParts + 1);
1461
0
                    anPartType.resize(nParts + 1);
1462
0
                    anPartStart[nParts] = nPoints;
1463
0
                    anPartType[nParts] =
1464
0
                        bAllowSHPTTriangle ? SHPP_TRIANGLES : SHPP_OUTERRING;
1465
0
                    nParts++;
1466
0
                }
1467
1468
0
                aoPoints.resize(nPoints + 4);
1469
0
                adfZ.resize(nPoints + 4);
1470
0
                for (int i = 0; i < 4; i++)
1471
0
                {
1472
0
                    aoPoints[nPoints + i].x = poRing->getX(i);
1473
0
                    aoPoints[nPoints + i].y = poRing->getY(i);
1474
0
                    adfZ[nPoints + i] = poRing->getZ(i);
1475
0
                }
1476
0
                nPoints += bAllowSHPTTriangle ? 3 : 4;
1477
0
            }
1478
0
        }
1479
13
        else
1480
13
        {
1481
13
            anPartStart.resize(nParts + nRings);
1482
13
            anPartType.resize(nParts + nRings);
1483
1484
39
            for (int i = 0; i < nRings; i++)
1485
26
            {
1486
26
                anPartStart[nParts + i] = nPoints;
1487
26
                if (i == 0)
1488
13
                {
1489
13
                    poRing = poPoly->getExteriorRing();
1490
13
                    anPartType[nParts + i] = SHPP_OUTERRING;
1491
13
                }
1492
13
                else
1493
13
                {
1494
13
                    poRing = poPoly->getInteriorRing(i - 1);
1495
13
                    anPartType[nParts + i] = SHPP_INNERRING;
1496
13
                }
1497
26
                aoPoints.resize(nPoints + poRing->getNumPoints());
1498
26
                adfZ.resize(nPoints + poRing->getNumPoints());
1499
66
                for (int k = 0; k < poRing->getNumPoints(); k++)
1500
40
                {
1501
40
                    aoPoints[nPoints + k].x = poRing->getX(k);
1502
40
                    aoPoints[nPoints + k].y = poRing->getY(k);
1503
40
                    adfZ[nPoints + k] = poRing->getZ(k);
1504
40
                }
1505
26
                nPoints += poRing->getNumPoints();
1506
26
            }
1507
1508
13
            nParts += nRings;
1509
13
        }
1510
13
    }
1511
1512
13
    if (nParts == 1 && anPartType[0] == SHPP_OUTERRING && nPoints == 4)
1513
0
    {
1514
0
        anPartType[0] = SHPP_TRIFAN;
1515
0
        nPoints = 3;
1516
0
    }
1517
1518
13
    return OGRERR_NONE;
1519
13
}
1520
1521
/************************************************************************/
1522
/*                    OGRWriteMultiPatchToShapeBin()                    */
1523
/************************************************************************/
1524
1525
OGRErr OGRWriteMultiPatchToShapeBin(const OGRGeometry *poGeom,
1526
                                    GByte **ppabyShape, int *pnBytes)
1527
0
{
1528
0
    int nParts = 0;
1529
0
    std::vector<int> anPartStart;
1530
0
    std::vector<int> anPartType;
1531
0
    int nPoints = 0;
1532
0
    std::vector<OGRRawPoint> aoPoints;
1533
0
    std::vector<double> adfZ;
1534
0
    OGRErr eErr = OGRCreateMultiPatch(poGeom, TRUE, nParts, anPartStart,
1535
0
                                      anPartType, nPoints, aoPoints, adfZ);
1536
0
    if (eErr != OGRERR_NONE)
1537
0
        return eErr;
1538
1539
0
    const bool bOmitZ =
1540
0
        !poGeom->Is3D() &&
1541
0
        CPLTestBool(CPLGetConfigOption("OGR_MULTIPATCH_OMIT_Z", "NO"));
1542
1543
0
    int nShpSize = 4;             // All types start with integer type number.
1544
0
    nShpSize += 16 * 2;           // xy bbox.
1545
0
    nShpSize += 4;                // nparts.
1546
0
    nShpSize += 4;                // npoints.
1547
0
    nShpSize += 4 * nParts;       // panPartStart[nparts].
1548
0
    nShpSize += 4 * nParts;       // panPartType[nparts].
1549
0
    nShpSize += 8 * 2 * nPoints;  // xy points.
1550
0
    if (!bOmitZ)
1551
0
    {
1552
0
        nShpSize += 16;           // z bbox.
1553
0
        nShpSize += 8 * nPoints;  // z points.
1554
0
    }
1555
1556
0
    *pnBytes = nShpSize;
1557
0
    *ppabyShape = static_cast<GByte *>(CPLMalloc(nShpSize));
1558
1559
0
    GByte *pabyPtr = *ppabyShape;
1560
1561
    // Write in the type number and advance the pointer.
1562
0
    GUInt32 nGType = bOmitZ ? CPL_LSBWORD32(SHPT_GENERALMULTIPATCH)
1563
0
                            : CPL_LSBWORD32(SHPT_MULTIPATCH);
1564
0
    memcpy(pabyPtr, &nGType, 4);
1565
0
    pabyPtr += 4;
1566
1567
0
    OGREnvelope3D envelope;
1568
0
    poGeom->getEnvelope(&envelope);
1569
0
    memcpy(pabyPtr, &(envelope.MinX), 8);
1570
0
    memcpy(pabyPtr + 8, &(envelope.MinY), 8);
1571
0
    memcpy(pabyPtr + 8 + 8, &(envelope.MaxX), 8);
1572
0
    memcpy(pabyPtr + 8 + 8 + 8, &(envelope.MaxY), 8);
1573
1574
    // Swap box if needed. Shape doubles are always LSB.
1575
    if constexpr (OGR_SWAP(wkbNDR))
1576
    {
1577
        for (int i = 0; i < 4; i++)
1578
            CPL_SWAPDOUBLE(pabyPtr + 8 * i);
1579
    }
1580
0
    pabyPtr += 32;
1581
1582
    // Write in the part count.
1583
0
    GUInt32 nPartsLsb = CPL_LSBWORD32(nParts);
1584
0
    memcpy(pabyPtr, &nPartsLsb, 4);
1585
0
    pabyPtr += 4;
1586
1587
    // Write in the total point count.
1588
0
    GUInt32 nPointsLsb = CPL_LSBWORD32(nPoints);
1589
0
    memcpy(pabyPtr, &nPointsLsb, 4);
1590
0
    pabyPtr += 4;
1591
1592
0
    for (int i = 0; i < nParts; i++)
1593
0
    {
1594
0
        int nPartStart = CPL_LSBWORD32(anPartStart[i]);
1595
0
        memcpy(pabyPtr, &nPartStart, 4);
1596
0
        pabyPtr += 4;
1597
0
    }
1598
0
    for (int i = 0; i < nParts; i++)
1599
0
    {
1600
0
        int nPartType = CPL_LSBWORD32(anPartType[i]);
1601
0
        memcpy(pabyPtr, &nPartType, 4);
1602
0
        pabyPtr += 4;
1603
0
    }
1604
1605
0
    if (!aoPoints.empty())
1606
0
        memcpy(pabyPtr, aoPoints.data(), 2 * 8 * nPoints);
1607
1608
    // Swap box if needed. Shape doubles are always LSB.
1609
    if constexpr (OGR_SWAP(wkbNDR))
1610
    {
1611
        for (int i = 0; i < 2 * nPoints; i++)
1612
            CPL_SWAPDOUBLE(pabyPtr + 8 * i);
1613
    }
1614
0
    pabyPtr += 2 * 8 * nPoints;
1615
1616
0
    if (!bOmitZ)
1617
0
    {
1618
0
        memcpy(pabyPtr, &(envelope.MinZ), 8);
1619
0
        memcpy(pabyPtr + 8, &(envelope.MaxZ), 8);
1620
        if constexpr (OGR_SWAP(wkbNDR))
1621
        {
1622
            for (int i = 0; i < 2; i++)
1623
                CPL_SWAPDOUBLE(pabyPtr + 8 * i);
1624
        }
1625
0
        pabyPtr += 16;
1626
1627
0
        if (!adfZ.empty())
1628
0
            memcpy(pabyPtr, adfZ.data(), 8 * nPoints);
1629
        // Swap box if needed. Shape doubles are always LSB.
1630
        if constexpr (OGR_SWAP(wkbNDR))
1631
        {
1632
            for (int i = 0; i < nPoints; i++)
1633
                CPL_SWAPDOUBLE(pabyPtr + 8 * i);
1634
        }
1635
        // pabyPtr += 8 * nPoints;
1636
0
    }
1637
1638
0
    return OGRERR_NONE;
1639
0
}
1640
1641
/************************************************************************/
1642
/*                         GetAngleOnEllipse()                          */
1643
/************************************************************************/
1644
1645
// Return the angle in deg [0, 360] of dfArcX,dfArcY regarding the
1646
// ellipse semi-major axis.
1647
static double GetAngleOnEllipse(double dfPointOnArcX, double dfPointOnArcY,
1648
                                double dfCenterX, double dfCenterY,
1649
                                double dfRotationDeg,  // Ellipse rotation.
1650
                                double dfSemiMajor, double dfSemiMinor)
1651
11.7k
{
1652
    // Invert the following equation where cosA, sinA are unknown:
1653
    //   dfPointOnArcX-dfCenterX = cosA*M*cosRot + sinA*m*sinRot
1654
    //   dfPointOnArcY-dfCenterY = -cosA*M*sinRot + sinA*m*cosRot
1655
1656
11.7k
    if (dfSemiMajor == 0.0 || dfSemiMinor == 0.0)
1657
3.44k
        return 0.0;
1658
8.31k
    const double dfRotationRadians = dfRotationDeg * M_PI / 180.0;
1659
8.31k
    const double dfCosRot = cos(dfRotationRadians);
1660
8.31k
    const double dfSinRot = sin(dfRotationRadians);
1661
8.31k
    const double dfDeltaX = dfPointOnArcX - dfCenterX;
1662
8.31k
    const double dfDeltaY = dfPointOnArcY - dfCenterY;
1663
8.31k
    const double dfCosA =
1664
8.31k
        (dfCosRot * dfDeltaX - dfSinRot * dfDeltaY) / dfSemiMajor;
1665
8.31k
    const double dfSinA =
1666
8.31k
        (dfSinRot * dfDeltaX + dfCosRot * dfDeltaY) / dfSemiMinor;
1667
    // We could check that dfCosA^2 + dfSinA^2 ~= 1 to verify that the point
1668
    // is on the ellipse.
1669
8.31k
    const double dfAngle = atan2(dfSinA, dfCosA) / M_PI * 180;
1670
8.31k
    if (dfAngle < -180)
1671
0
        return dfAngle + 360;
1672
8.31k
    return dfAngle;
1673
8.31k
}
1674
1675
/************************************************************************/
1676
/*                    OGRShapeCreateCompoundCurve()                     */
1677
/************************************************************************/
1678
1679
static OGRCurve *OGRShapeCreateCompoundCurve(int nPartStartIdx, int nPartPoints,
1680
                                             const CurveSegment *pasCurves,
1681
                                             int nCurves, int nFirstCurveIdx,
1682
                                             /* const */ double *padfX,
1683
                                             /* const */ double *padfY,
1684
                                             /* const */ double *padfZ,
1685
                                             /* const */ double *padfM,
1686
                                             int *pnLastCurveIdx)
1687
59.4k
{
1688
59.4k
    auto poCC = std::make_unique<OGRCompoundCurve>();
1689
59.4k
    int nLastPointIdx = nPartStartIdx;
1690
59.4k
    bool bHasCircularArcs = false;
1691
59.4k
    int i = nFirstCurveIdx;  // Used after for.
1692
86.8k
    for (; i < nCurves; i++)
1693
31.9k
    {
1694
31.9k
        const int nStartPointIdx = pasCurves[i].nStartPointIdx;
1695
1696
31.9k
        if (nStartPointIdx < nPartStartIdx)
1697
0
        {
1698
            // Shouldn't happen normally, but who knows.
1699
0
            continue;
1700
0
        }
1701
1702
        // For a multi-part geometry, stop when the start index of the curve
1703
        // exceeds the last point index of the part
1704
31.9k
        if (nStartPointIdx >= nPartStartIdx + nPartPoints)
1705
4.65k
        {
1706
4.65k
            if (pnLastCurveIdx)
1707
4.65k
                *pnLastCurveIdx = i;
1708
4.65k
            break;
1709
4.65k
        }
1710
1711
        // Add linear segments between end of last curve portion (or beginning
1712
        // of the part) and start of current curve.
1713
27.3k
        if (nStartPointIdx > nLastPointIdx)
1714
8.87k
        {
1715
8.87k
            OGRLineString *poLine = new OGRLineString();
1716
8.87k
            poLine->setPoints(
1717
8.87k
                nStartPointIdx - nLastPointIdx + 1, padfX + nLastPointIdx,
1718
8.87k
                padfY + nLastPointIdx,
1719
8.87k
                padfZ != nullptr ? padfZ + nLastPointIdx : nullptr,
1720
8.87k
                padfM != nullptr ? padfM + nLastPointIdx : nullptr);
1721
8.87k
            poCC->addCurveDirectly(poLine);
1722
8.87k
        }
1723
1724
27.3k
        if (pasCurves[i].eType == CURVE_ARC_INTERIOR_POINT &&
1725
10.3k
            nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1726
9.38k
        {
1727
9.38k
            OGRPoint p1(padfX[nStartPointIdx], padfY[nStartPointIdx],
1728
9.38k
                        padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1729
9.38k
                        padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1730
9.38k
            OGRPoint p2(pasCurves[i].u.ArcByIntermediatePoint.dfX,
1731
9.38k
                        pasCurves[i].u.ArcByIntermediatePoint.dfY,
1732
9.38k
                        padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0);
1733
9.38k
            OGRPoint p3(padfX[nStartPointIdx + 1], padfY[nStartPointIdx + 1],
1734
9.38k
                        padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1735
9.38k
                        padfM != nullptr ? padfM[nStartPointIdx + 1] : 0.0);
1736
1737
            // Some software (like QGIS, see https://hub.qgis.org/issues/15116)
1738
            // do not like 3-point circles, so use a 5 point variant.
1739
9.38k
            if (p1.getX() == p3.getX() && p1.getY() == p3.getY())
1740
5.36k
            {
1741
5.36k
                if (p1.getX() != p2.getX() || p1.getY() != p2.getY())
1742
5.35k
                {
1743
5.35k
                    bHasCircularArcs = true;
1744
5.35k
                    OGRCircularString *poCS = new OGRCircularString();
1745
5.35k
                    const double dfCenterX = (p1.getX() + p2.getX()) / 2;
1746
5.35k
                    const double dfCenterY = (p1.getY() + p2.getY()) / 2;
1747
5.35k
                    poCS->addPoint(&p1);
1748
5.35k
                    OGRPoint pInterm1(dfCenterX - (p2.getY() - dfCenterY),
1749
5.35k
                                      dfCenterY + (p1.getX() - dfCenterX),
1750
5.35k
                                      padfZ != nullptr ? padfZ[nStartPointIdx]
1751
5.35k
                                                       : 0.0);
1752
5.35k
                    poCS->addPoint(&pInterm1);
1753
5.35k
                    poCS->addPoint(&p2);
1754
5.35k
                    OGRPoint pInterm2(dfCenterX + (p2.getY() - dfCenterY),
1755
5.35k
                                      dfCenterY - (p1.getX() - dfCenterX),
1756
5.35k
                                      padfZ != nullptr ? padfZ[nStartPointIdx]
1757
5.35k
                                                       : 0.0);
1758
5.35k
                    poCS->addPoint(&pInterm2);
1759
5.35k
                    poCS->addPoint(&p3);
1760
5.35k
                    poCS->set3D(padfZ != nullptr);
1761
5.35k
                    poCS->setMeasured(padfM != nullptr);
1762
5.35k
                    poCC->addCurveDirectly(poCS);
1763
5.35k
                }
1764
5.36k
            }
1765
4.02k
            else
1766
4.02k
            {
1767
4.02k
                bHasCircularArcs = true;
1768
4.02k
                OGRCircularString *poCS = new OGRCircularString();
1769
4.02k
                poCS->addPoint(&p1);
1770
4.02k
                poCS->addPoint(&p2);
1771
4.02k
                poCS->addPoint(&p3);
1772
4.02k
                poCS->set3D(padfZ != nullptr);
1773
4.02k
                poCS->setMeasured(padfM != nullptr);
1774
4.02k
                if (poCC->addCurveDirectly(poCS) != OGRERR_NONE)
1775
0
                {
1776
0
                    delete poCS;
1777
0
                    return nullptr;
1778
0
                }
1779
4.02k
            }
1780
9.38k
        }
1781
1782
17.9k
        else if (pasCurves[i].eType == CURVE_ARC_CENTER_POINT &&
1783
6.08k
                 nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1784
5.81k
        {
1785
5.81k
            const double dfCenterX = pasCurves[i].u.ArcByCenterPoint.dfX;
1786
5.81k
            const double dfCenterY = pasCurves[i].u.ArcByCenterPoint.dfY;
1787
5.81k
            double dfDeltaY = padfY[nStartPointIdx] - dfCenterY;
1788
5.81k
            double dfDeltaX = padfX[nStartPointIdx] - dfCenterX;
1789
5.81k
            double dfAngleStart = atan2(dfDeltaY, dfDeltaX);
1790
5.81k
            dfDeltaY = padfY[nStartPointIdx + 1] - dfCenterY;
1791
5.81k
            dfDeltaX = padfX[nStartPointIdx + 1] - dfCenterX;
1792
5.81k
            double dfAngleEnd = atan2(dfDeltaY, dfDeltaX);
1793
            // Note: This definition from center and 2 points may be
1794
            // not a circle.
1795
5.81k
            double dfRadius = sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
1796
5.81k
            if (pasCurves[i].u.ArcByCenterPoint.bIsCCW)
1797
764
            {
1798
764
                if (dfAngleStart >= dfAngleEnd)
1799
352
                    dfAngleEnd += 2 * M_PI;
1800
764
            }
1801
5.05k
            else
1802
5.05k
            {
1803
5.05k
                if (dfAngleStart <= dfAngleEnd)
1804
3.56k
                    dfAngleEnd -= 2 * M_PI;
1805
5.05k
            }
1806
5.81k
            const double dfMidAngle = (dfAngleStart + dfAngleEnd) / 2;
1807
5.81k
            OGRPoint p1(padfX[nStartPointIdx], padfY[nStartPointIdx],
1808
5.81k
                        padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1809
5.81k
                        padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1810
5.81k
            OGRPoint p2(dfCenterX + dfRadius * cos(dfMidAngle),
1811
5.81k
                        dfCenterY + dfRadius * sin(dfMidAngle),
1812
5.81k
                        padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0);
1813
5.81k
            OGRPoint p3(padfX[nStartPointIdx + 1], padfY[nStartPointIdx + 1],
1814
5.81k
                        padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1815
5.81k
                        padfM != nullptr ? padfM[nStartPointIdx + 1] : 0.0);
1816
1817
5.81k
            bHasCircularArcs = true;
1818
5.81k
            OGRCircularString *poCS = new OGRCircularString();
1819
5.81k
            poCS->addPoint(&p1);
1820
5.81k
            poCS->addPoint(&p2);
1821
5.81k
            poCS->addPoint(&p3);
1822
5.81k
            poCS->set3D(padfZ != nullptr);
1823
5.81k
            poCS->setMeasured(padfM != nullptr);
1824
5.81k
            poCC->addCurveDirectly(poCS);
1825
5.81k
        }
1826
1827
12.1k
        else if (pasCurves[i].eType == CURVE_BEZIER &&
1828
4.89k
                 nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1829
4.77k
        {
1830
4.77k
            OGRLineString *poLine = new OGRLineString();
1831
4.77k
            const double dfX0 = padfX[nStartPointIdx];
1832
4.77k
            const double dfY0 = padfY[nStartPointIdx];
1833
4.77k
            const double dfX1 = pasCurves[i].u.Bezier.dfX1;
1834
4.77k
            const double dfY1 = pasCurves[i].u.Bezier.dfY1;
1835
4.77k
            const double dfX2 = pasCurves[i].u.Bezier.dfX2;
1836
4.77k
            const double dfY2 = pasCurves[i].u.Bezier.dfY2;
1837
4.77k
            const double dfX3 = padfX[nStartPointIdx + 1];
1838
4.77k
            const double dfY3 = padfY[nStartPointIdx + 1];
1839
4.77k
            double dfStartAngle = atan2(dfY1 - dfY0, dfX1 - dfX0);
1840
4.77k
            double dfEndAngle = atan2(dfY3 - dfY2, dfX3 - dfX2);
1841
4.77k
            if (dfStartAngle + M_PI < dfEndAngle)
1842
912
                dfStartAngle += 2 * M_PI;
1843
3.86k
            else if (dfEndAngle + M_PI < dfStartAngle)
1844
1.60k
                dfEndAngle += 2 * M_PI;
1845
4.77k
            const double dfStepSizeRad =
1846
4.77k
                OGRGeometryFactory::GetDefaultArcStepSize() / 180.0 * M_PI;
1847
4.77k
            const double dfLengthTangentStart =
1848
4.77k
                (dfX1 - dfX0) * (dfX1 - dfX0) + (dfY1 - dfY0) * (dfY1 - dfY0);
1849
4.77k
            const double dfLengthTangentEnd =
1850
4.77k
                (dfX3 - dfX2) * (dfX3 - dfX2) + (dfY3 - dfY2) * (dfY3 - dfY2);
1851
4.77k
            const double dfLength =
1852
4.77k
                (dfX3 - dfX0) * (dfX3 - dfX0) + (dfY3 - dfY0) * (dfY3 - dfY0);
1853
            // Heuristics to compute number of steps: we take into account the
1854
            // angular difference between the start and end tangent. And we
1855
            // also take into account the relative length of the tangent vs
1856
            // the length of the straight segment
1857
4.77k
            const int nSteps =
1858
4.77k
                (dfLength < 1e-9)
1859
4.77k
                    ? 1
1860
4.77k
                    : static_cast<int>(std::min(
1861
2.13k
                          1000.0,
1862
2.13k
                          ceil(std::max(2.0, fabs(dfEndAngle - dfStartAngle) /
1863
2.13k
                                                 dfStepSizeRad) *
1864
2.13k
                               std::max(1.0, 5.0 *
1865
2.13k
                                                 (dfLengthTangentStart +
1866
2.13k
                                                  dfLengthTangentEnd) /
1867
2.13k
                                                 dfLength))));
1868
4.77k
            poLine->setNumPoints(nSteps + 1);
1869
4.77k
            poLine->setPoint(0, dfX0, dfY0,
1870
4.77k
                             padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1871
4.77k
                             padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1872
1.24M
            for (int j = 1; j < nSteps; j++)
1873
1.24M
            {
1874
1.24M
                const double t = static_cast<double>(j) / nSteps;
1875
                // Third-order Bezier interpolation.
1876
1.24M
                poLine->setPoint(
1877
1.24M
                    j,
1878
1.24M
                    (1 - t) * (1 - t) * (1 - t) * dfX0 +
1879
1.24M
                        3 * (1 - t) * (1 - t) * t * dfX1 +
1880
1.24M
                        3 * (1 - t) * t * t * dfX2 + t * t * t * dfX3,
1881
1.24M
                    (1 - t) * (1 - t) * (1 - t) * dfY0 +
1882
1.24M
                        3 * (1 - t) * (1 - t) * t * dfY1 +
1883
1.24M
                        3 * (1 - t) * t * t * dfY2 + t * t * t * dfY3);
1884
1.24M
            }
1885
4.77k
            poLine->setPoint(nSteps, dfX3, dfY3,
1886
4.77k
                             padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1887
4.77k
                             padfM != nullptr ? padfM[nStartPointIdx + 1]
1888
4.77k
                                              : 0.0);
1889
4.77k
            poLine->set3D(padfZ != nullptr);
1890
4.77k
            poLine->setMeasured(padfM != nullptr);
1891
4.77k
            if (poCC->addCurveDirectly(poLine) != OGRERR_NONE)
1892
0
            {
1893
0
                delete poLine;
1894
0
                return nullptr;
1895
0
            }
1896
4.77k
        }
1897
1898
7.35k
        else if (pasCurves[i].eType == CURVE_ELLIPSE_BY_CENTER &&
1899
5.95k
                 nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1900
5.87k
        {
1901
5.87k
            const double dfSemiMinor =
1902
5.87k
                pasCurves[i].u.EllipseByCenter.dfSemiMajor *
1903
5.87k
                pasCurves[i].u.EllipseByCenter.dfRatioSemiMinor;
1904
            // Different sign conventions between ext shape
1905
            // (trigonometric, CCW) and approximateArcAngles (CW).
1906
5.87k
            const double dfRotationDeg =
1907
5.87k
                -pasCurves[i].u.EllipseByCenter.dfRotationDeg;
1908
5.87k
            const double dfAngleStart = GetAngleOnEllipse(
1909
5.87k
                padfX[nStartPointIdx], padfY[nStartPointIdx],
1910
5.87k
                pasCurves[i].u.EllipseByCenter.dfX,
1911
5.87k
                pasCurves[i].u.EllipseByCenter.dfY, dfRotationDeg,
1912
5.87k
                pasCurves[i].u.EllipseByCenter.dfSemiMajor, dfSemiMinor);
1913
5.87k
            const double dfAngleEnd = GetAngleOnEllipse(
1914
5.87k
                padfX[nStartPointIdx + 1], padfY[nStartPointIdx + 1],
1915
5.87k
                pasCurves[i].u.EllipseByCenter.dfX,
1916
5.87k
                pasCurves[i].u.EllipseByCenter.dfY, dfRotationDeg,
1917
5.87k
                pasCurves[i].u.EllipseByCenter.dfSemiMajor, dfSemiMinor);
1918
            // CPLDebug("OGR", "Start angle=%f, End angle=%f",
1919
            //          dfAngleStart, dfAngleEnd);
1920
            // Approximatearcangles() use CW.
1921
5.87k
            double dfAngleStartForApprox = -dfAngleStart;
1922
5.87k
            double dfAngleEndForApprox = -dfAngleEnd;
1923
5.87k
            if (pasCurves[i].u.EllipseByCenter.bIsComplete)
1924
926
            {
1925
926
                dfAngleEndForApprox = dfAngleStartForApprox + 360;
1926
926
            }
1927
4.95k
            else if (pasCurves[i].u.EllipseByCenter.bIsMinor)
1928
1.35k
            {
1929
1.35k
                if (dfAngleEndForApprox > dfAngleStartForApprox + 180)
1930
446
                {
1931
446
                    dfAngleEndForApprox -= 360;
1932
446
                }
1933
910
                else if (dfAngleEndForApprox < dfAngleStartForApprox - 180)
1934
199
                {
1935
199
                    dfAngleEndForApprox += 360;
1936
199
                }
1937
1.35k
            }
1938
3.59k
            else
1939
3.59k
            {
1940
3.59k
                if (dfAngleEndForApprox > dfAngleStartForApprox &&
1941
534
                    dfAngleEndForApprox < dfAngleStartForApprox + 180)
1942
206
                {
1943
206
                    dfAngleEndForApprox -= 360;
1944
206
                }
1945
3.38k
                else if (dfAngleEndForApprox < dfAngleStartForApprox &&
1946
1.43k
                         dfAngleEndForApprox > dfAngleStartForApprox - 180)
1947
818
                {
1948
818
                    dfAngleEndForApprox += 360;
1949
818
                }
1950
3.59k
            }
1951
5.87k
            OGRLineString *poLine =
1952
5.87k
                OGRGeometryFactory::approximateArcAngles(
1953
5.87k
                    pasCurves[i].u.EllipseByCenter.dfX,
1954
5.87k
                    pasCurves[i].u.EllipseByCenter.dfY,
1955
5.87k
                    padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1956
5.87k
                    pasCurves[i].u.EllipseByCenter.dfSemiMajor, dfSemiMinor,
1957
5.87k
                    dfRotationDeg, dfAngleStartForApprox, dfAngleEndForApprox,
1958
5.87k
                    0)
1959
5.87k
                    ->toLineString();
1960
5.87k
            if (poLine->getNumPoints() >= 2)
1961
5.87k
            {
1962
5.87k
                poLine->setPoint(
1963
5.87k
                    0, padfX[nStartPointIdx], padfY[nStartPointIdx],
1964
5.87k
                    padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1965
5.87k
                    padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1966
5.87k
                poLine->setPoint(
1967
5.87k
                    poLine->getNumPoints() - 1, padfX[nStartPointIdx + 1],
1968
5.87k
                    padfY[nStartPointIdx + 1],
1969
5.87k
                    padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1970
5.87k
                    padfM != nullptr ? padfM[nStartPointIdx + 1] : 0.0);
1971
5.87k
            }
1972
5.87k
            poLine->set3D(padfZ != nullptr);
1973
5.87k
            poLine->setMeasured(padfM != nullptr);
1974
5.87k
            poCC->addCurveDirectly(poLine);
1975
5.87k
        }
1976
1977
        // Should not happen normally.
1978
1.47k
        else if (nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1979
0
        {
1980
0
            OGRLineString *poLine = new OGRLineString();
1981
0
            poLine->setPoints(
1982
0
                2, padfX + nStartPointIdx, padfY + nStartPointIdx,
1983
0
                padfZ != nullptr ? padfZ + nStartPointIdx : nullptr,
1984
0
                padfM != nullptr ? padfM + nStartPointIdx : nullptr);
1985
0
            poCC->addCurveDirectly(poLine);
1986
0
        }
1987
27.3k
        nLastPointIdx = nStartPointIdx + 1;
1988
27.3k
    }
1989
59.4k
    if (i == nCurves)
1990
54.8k
    {
1991
54.8k
        if (pnLastCurveIdx)
1992
44.1k
            *pnLastCurveIdx = i;
1993
54.8k
    }
1994
1995
    // Add terminating linear segments
1996
59.4k
    if (nLastPointIdx < nPartStartIdx + nPartPoints - 1)
1997
48.1k
    {
1998
48.1k
        OGRLineString *poLine = new OGRLineString();
1999
48.1k
        poLine->setPoints(nPartStartIdx + nPartPoints - 1 - nLastPointIdx + 1,
2000
48.1k
                          padfX + nLastPointIdx, padfY + nLastPointIdx,
2001
48.1k
                          padfZ != nullptr ? padfZ + nLastPointIdx : nullptr,
2002
48.1k
                          padfM != nullptr ? padfM + nLastPointIdx : nullptr);
2003
48.1k
        if (poCC->addCurveDirectly(poLine) != OGRERR_NONE)
2004
0
        {
2005
0
            delete poLine;
2006
0
            return nullptr;
2007
0
        }
2008
48.1k
    }
2009
2010
59.4k
    if (!bHasCircularArcs)
2011
44.3k
    {
2012
44.3k
        OGRwkbGeometryType eLSType = wkbLineString;
2013
44.3k
        if (OGR_GT_HasZ(poCC->getGeometryType()))
2014
15.8k
            eLSType = OGR_GT_SetZ(eLSType);
2015
44.3k
        if (OGR_GT_HasM(poCC->getGeometryType()))
2016
15.4k
            eLSType = OGR_GT_SetM(eLSType);
2017
44.3k
        return reinterpret_cast<OGRCurve *>(OGR_G_ForceTo(
2018
44.3k
            OGRGeometry::ToHandle(poCC.release()), eLSType, nullptr));
2019
44.3k
    }
2020
15.0k
    else
2021
15.0k
        return poCC.release();
2022
59.4k
}
2023
2024
/************************************************************************/
2025
/*                      OGRCreateFromShapeBin()                         */
2026
/*                                                                      */
2027
/*      Translate shapefile binary representation to an OGR             */
2028
/*      geometry.                                                       */
2029
/************************************************************************/
2030
2031
OGRErr OGRCreateFromShapeBin(GByte *pabyShape, OGRGeometry **ppoGeom,
2032
                             int nBytes)
2033
2034
34.9k
{
2035
34.9k
    *ppoGeom = nullptr;
2036
2037
34.9k
    if (nBytes < 4)
2038
0
    {
2039
0
        CPLError(CE_Failure, CPLE_AppDefined,
2040
0
                 "Shape buffer size (%d) too small", nBytes);
2041
0
        return OGRERR_FAILURE;
2042
0
    }
2043
2044
    /* -------------------------------------------------------------------- */
2045
    /*  Detect zlib compressed shapes and uncompress buffer if necessary    */
2046
    /*  NOTE: this seems to be an undocumented feature, even in the         */
2047
    /*  extended_shapefile_format.pdf found in the FileGDB API              */
2048
    /*  documentation.                                                      */
2049
    /* -------------------------------------------------------------------- */
2050
34.9k
    if (nBytes >= 14 && pabyShape[12] == 0x78 &&
2051
0
        pabyShape[13] == 0xDA /* zlib marker */)
2052
0
    {
2053
0
        GInt32 nUncompressedSize = 0;
2054
0
        GInt32 nCompressedSize = 0;
2055
0
        memcpy(&nUncompressedSize, pabyShape + 4, 4);
2056
0
        memcpy(&nCompressedSize, pabyShape + 8, 4);
2057
0
        CPL_LSBPTR32(&nUncompressedSize);
2058
0
        CPL_LSBPTR32(&nCompressedSize);
2059
0
        if (nCompressedSize + 12 == nBytes && nUncompressedSize > 0)
2060
0
        {
2061
0
            GByte *pabyUncompressedBuffer =
2062
0
                static_cast<GByte *>(VSI_MALLOC_VERBOSE(nUncompressedSize));
2063
0
            if (pabyUncompressedBuffer == nullptr)
2064
0
            {
2065
0
                return OGRERR_FAILURE;
2066
0
            }
2067
2068
0
            size_t nRealUncompressedSize = 0;
2069
0
            if (CPLZLibInflate(pabyShape + 12, nCompressedSize,
2070
0
                               pabyUncompressedBuffer, nUncompressedSize,
2071
0
                               &nRealUncompressedSize) == nullptr)
2072
0
            {
2073
0
                CPLError(CE_Failure, CPLE_AppDefined,
2074
0
                         "CPLZLibInflate() failed");
2075
0
                VSIFree(pabyUncompressedBuffer);
2076
0
                return OGRERR_FAILURE;
2077
0
            }
2078
2079
0
            const OGRErr eErr =
2080
0
                OGRCreateFromShapeBin(pabyUncompressedBuffer, ppoGeom,
2081
0
                                      static_cast<int>(nRealUncompressedSize));
2082
2083
0
            VSIFree(pabyUncompressedBuffer);
2084
2085
0
            return eErr;
2086
0
        }
2087
0
    }
2088
2089
34.9k
    int nSHPType = pabyShape[0];
2090
2091
    /* -------------------------------------------------------------------- */
2092
    /*      Return a NULL geometry when SHPT_NULL is encountered.           */
2093
    /*      Watch out, null return does not mean "bad data" it means        */
2094
    /*      "no geometry here". Watch the OGRErr for the error status       */
2095
    /* -------------------------------------------------------------------- */
2096
34.9k
    if (nSHPType == SHPT_NULL)
2097
0
    {
2098
0
        *ppoGeom = nullptr;
2099
0
        return OGRERR_NONE;
2100
0
    }
2101
2102
#if DEBUG_VERBOSE
2103
    CPLDebug("PGeo", "Shape type read from PGeo data is nSHPType = %d",
2104
             nSHPType);
2105
#endif
2106
2107
34.9k
    const bool bIsExtended =
2108
34.9k
        nSHPType >= SHPT_GENERALPOLYLINE && nSHPType <= SHPT_GENERALMULTIPATCH;
2109
2110
34.9k
    const bool bHasZ =
2111
34.9k
        (nSHPType == SHPT_POINTZ || nSHPType == SHPT_POINTZM ||
2112
34.9k
         nSHPType == SHPT_MULTIPOINTZ || nSHPType == SHPT_MULTIPOINTZM ||
2113
34.9k
         nSHPType == SHPT_POLYGONZ || nSHPType == SHPT_POLYGONZM ||
2114
34.9k
         nSHPType == SHPT_ARCZ || nSHPType == SHPT_ARCZM ||
2115
34.9k
         nSHPType == SHPT_MULTIPATCH || nSHPType == SHPT_MULTIPATCHM ||
2116
34.9k
         (bIsExtended && (pabyShape[3] & 0x80) != 0));
2117
2118
34.9k
    const bool bHasM =
2119
34.9k
        (nSHPType == SHPT_POINTM || nSHPType == SHPT_POINTZM ||
2120
34.9k
         nSHPType == SHPT_MULTIPOINTM || nSHPType == SHPT_MULTIPOINTZM ||
2121
34.9k
         nSHPType == SHPT_POLYGONM || nSHPType == SHPT_POLYGONZM ||
2122
34.9k
         nSHPType == SHPT_ARCM || nSHPType == SHPT_ARCZM ||
2123
34.9k
         nSHPType == SHPT_MULTIPATCHM ||
2124
34.9k
         (bIsExtended && (pabyShape[3] & 0x40) != 0));
2125
2126
34.9k
    const bool bHasCurves = (bIsExtended && (pabyShape[3] & 0x20) != 0);
2127
2128
34.9k
    switch (nSHPType)
2129
34.9k
    {
2130
8.50k
        case SHPT_GENERALPOLYLINE:
2131
8.50k
            nSHPType = SHPT_ARC;
2132
8.50k
            break;
2133
26.4k
        case SHPT_GENERALPOLYGON:
2134
26.4k
            nSHPType = SHPT_POLYGON;
2135
26.4k
            break;
2136
0
        case SHPT_GENERALPOINT:
2137
0
            nSHPType = SHPT_POINT;
2138
0
            break;
2139
0
        case SHPT_GENERALMULTIPOINT:
2140
0
            nSHPType = SHPT_MULTIPOINT;
2141
0
            break;
2142
0
        case SHPT_GENERALMULTIPATCH:
2143
0
            nSHPType = SHPT_MULTIPATCH;
2144
34.9k
    }
2145
2146
    /* ==================================================================== */
2147
    /*     Extract vertices for a Polygon or Arc.                           */
2148
    /* ==================================================================== */
2149
34.9k
    if (nSHPType == SHPT_ARC || nSHPType == SHPT_ARCZ ||
2150
26.4k
        nSHPType == SHPT_ARCM || nSHPType == SHPT_ARCZM ||
2151
26.4k
        nSHPType == SHPT_POLYGON || nSHPType == SHPT_POLYGONZ ||
2152
0
        nSHPType == SHPT_POLYGONM || nSHPType == SHPT_POLYGONZM ||
2153
0
        nSHPType == SHPT_MULTIPATCH || nSHPType == SHPT_MULTIPATCHM)
2154
34.9k
    {
2155
34.9k
        if (nBytes < 44)
2156
0
        {
2157
0
            CPLError(CE_Failure, CPLE_AppDefined,
2158
0
                     "Corrupted Shape : nBytes=%d, nSHPType=%d", nBytes,
2159
0
                     nSHPType);
2160
0
            return OGRERR_FAILURE;
2161
0
        }
2162
2163
        /* --------------------------------------------------------------------
2164
         */
2165
        /*      Extract part/point count, and build vertex and part arrays */
2166
        /*      to proper size. */
2167
        /* --------------------------------------------------------------------
2168
         */
2169
34.9k
        GInt32 nPoints = 0;
2170
34.9k
        memcpy(&nPoints, pabyShape + 40, 4);
2171
34.9k
        GInt32 nParts = 0;
2172
34.9k
        memcpy(&nParts, pabyShape + 36, 4);
2173
2174
34.9k
        CPL_LSBPTR32(&nPoints);
2175
34.9k
        CPL_LSBPTR32(&nParts);
2176
2177
34.9k
        if (nPoints < 0 || nParts < 0 || nPoints > 50 * 1000 * 1000 ||
2178
34.9k
            nParts > 10 * 1000 * 1000)
2179
0
        {
2180
0
            CPLError(CE_Failure, CPLE_AppDefined,
2181
0
                     "Corrupted Shape : nPoints=%d, nParts=%d.", nPoints,
2182
0
                     nParts);
2183
0
            return OGRERR_FAILURE;
2184
0
        }
2185
2186
34.9k
        const bool bIsMultiPatch =
2187
34.9k
            nSHPType == SHPT_MULTIPATCH || nSHPType == SHPT_MULTIPATCHM;
2188
2189
        // With the previous checks on nPoints and nParts,
2190
        // we should not overflow here and after
2191
        // since 50 M * (16 + 8 + 8) = 1 600 MB.
2192
34.9k
        int nRequiredSize = 44 + 4 * nParts + 16 * nPoints;
2193
34.9k
        if (bHasZ)
2194
16.0k
        {
2195
16.0k
            nRequiredSize += 16 + 8 * nPoints;
2196
16.0k
        }
2197
34.9k
        if (bHasM)
2198
18.6k
        {
2199
18.6k
            nRequiredSize += 16 + 8 * nPoints;
2200
18.6k
        }
2201
34.9k
        if (bIsMultiPatch)
2202
0
        {
2203
0
            nRequiredSize += 4 * nParts;
2204
0
        }
2205
34.9k
        if (nRequiredSize > nBytes)
2206
0
        {
2207
0
            CPLError(CE_Failure, CPLE_AppDefined,
2208
0
                     "Corrupted Shape : nPoints=%d, nParts=%d, nBytes=%d, "
2209
0
                     "nSHPType=%d, nRequiredSize=%d",
2210
0
                     nPoints, nParts, nBytes, nSHPType, nRequiredSize);
2211
0
            return OGRERR_FAILURE;
2212
0
        }
2213
2214
34.9k
        GInt32 *panPartStart =
2215
34.9k
            static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(nParts, sizeof(GInt32)));
2216
34.9k
        if (nParts != 0 && panPartStart == nullptr)
2217
0
        {
2218
0
            return OGRERR_FAILURE;
2219
0
        }
2220
2221
        /* --------------------------------------------------------------------
2222
         */
2223
        /*      Copy out the part array from the record. */
2224
        /* --------------------------------------------------------------------
2225
         */
2226
34.9k
        memcpy(panPartStart, pabyShape + 44, 4 * nParts);
2227
122k
        for (int i = 0; i < nParts; i++)
2228
88.5k
        {
2229
88.5k
            CPL_LSBPTR32(panPartStart + i);
2230
2231
            // Check that the offset is inside the vertex array.
2232
88.5k
            if (panPartStart[i] < 0 || panPartStart[i] >= nPoints)
2233
325
            {
2234
325
                CPLError(
2235
325
                    CE_Failure, CPLE_AppDefined,
2236
325
                    "Corrupted Shape : panPartStart[%d] = %d, nPoints = %d", i,
2237
325
                    panPartStart[i], nPoints);
2238
325
                CPLFree(panPartStart);
2239
325
                return OGRERR_FAILURE;
2240
325
            }
2241
88.2k
            if (i > 0 && panPartStart[i] <= panPartStart[i - 1])
2242
286
            {
2243
286
                CPLError(CE_Failure, CPLE_AppDefined,
2244
286
                         "Corrupted Shape : panPartStart[%d] = %d, "
2245
286
                         "panPartStart[%d] = %d",
2246
286
                         i, panPartStart[i], i - 1, panPartStart[i - 1]);
2247
286
                CPLFree(panPartStart);
2248
286
                return OGRERR_FAILURE;
2249
286
            }
2250
88.2k
        }
2251
2252
34.3k
        int nOffset = 44 + 4 * nParts;
2253
2254
        /* --------------------------------------------------------------------
2255
         */
2256
        /*      If this is a multipatch, we will also have parts types. */
2257
        /* --------------------------------------------------------------------
2258
         */
2259
34.3k
        GInt32 *panPartType = nullptr;
2260
2261
34.3k
        if (bIsMultiPatch)
2262
0
        {
2263
0
            panPartType = static_cast<GInt32 *>(
2264
0
                VSI_MALLOC2_VERBOSE(nParts, sizeof(GInt32)));
2265
0
            if (panPartType == nullptr)
2266
0
            {
2267
0
                CPLFree(panPartStart);
2268
0
                return OGRERR_FAILURE;
2269
0
            }
2270
2271
0
            memcpy(panPartType, pabyShape + nOffset, 4 * nParts);
2272
0
            for (int i = 0; i < nParts; i++)
2273
0
            {
2274
0
                CPL_LSBPTR32(panPartType + i);
2275
0
            }
2276
0
            nOffset += 4 * nParts;
2277
0
        }
2278
2279
        /* --------------------------------------------------------------------
2280
         */
2281
        /*      Copy out the vertices from the record. */
2282
        /* --------------------------------------------------------------------
2283
         */
2284
34.3k
        double *padfX =
2285
34.3k
            static_cast<double *>(VSI_MALLOC_VERBOSE(sizeof(double) * nPoints));
2286
34.3k
        double *padfY =
2287
34.3k
            static_cast<double *>(VSI_MALLOC_VERBOSE(sizeof(double) * nPoints));
2288
34.3k
        double *padfZ =
2289
34.3k
            static_cast<double *>(VSI_CALLOC_VERBOSE(sizeof(double), nPoints));
2290
34.3k
        double *padfM = static_cast<double *>(
2291
34.3k
            bHasM ? VSI_CALLOC_VERBOSE(sizeof(double), nPoints) : nullptr);
2292
2293
34.3k
        if (nPoints != 0 && (padfX == nullptr || padfY == nullptr ||
2294
34.3k
                             padfZ == nullptr || (bHasM && padfM == nullptr)))
2295
0
        {
2296
0
            CPLFree(panPartStart);
2297
0
            CPLFree(panPartType);
2298
0
            CPLFree(padfX);
2299
0
            CPLFree(padfY);
2300
0
            CPLFree(padfZ);
2301
0
            CPLFree(padfM);
2302
0
            return OGRERR_FAILURE;
2303
0
        }
2304
2305
560k
        for (int i = 0; i < nPoints; i++)
2306
526k
        {
2307
526k
            memcpy(padfX + i, pabyShape + nOffset + i * 16, 8);
2308
526k
            memcpy(padfY + i, pabyShape + nOffset + i * 16 + 8, 8);
2309
526k
            CPL_LSBPTR64(padfX + i);
2310
526k
            CPL_LSBPTR64(padfY + i);
2311
526k
        }
2312
2313
34.3k
        nOffset += 16 * nPoints;
2314
2315
        /* --------------------------------------------------------------------
2316
         */
2317
        /*      If we have a Z coordinate, collect that now. */
2318
        /* --------------------------------------------------------------------
2319
         */
2320
34.3k
        if (bHasZ)
2321
15.4k
        {
2322
165k
            for (int i = 0; i < nPoints; i++)
2323
149k
            {
2324
149k
                memcpy(padfZ + i, pabyShape + nOffset + 16 + i * 8, 8);
2325
149k
                CPL_LSBPTR64(padfZ + i);
2326
149k
            }
2327
2328
15.4k
            nOffset += 16 + 8 * nPoints;
2329
15.4k
        }
2330
2331
        /* --------------------------------------------------------------------
2332
         */
2333
        /*      If we have a M coordinate, collect that now. */
2334
        /* --------------------------------------------------------------------
2335
         */
2336
34.3k
        if (bHasM)
2337
18.2k
        {
2338
18.2k
            bool bIsAllNAN = nPoints > 0;
2339
202k
            for (int i = 0; i < nPoints; i++)
2340
184k
            {
2341
184k
                memcpy(padfM + i, pabyShape + nOffset + 16 + i * 8, 8);
2342
184k
                CPL_LSBPTR64(padfM + i);
2343
184k
                bIsAllNAN &= std::isnan(padfM[i]);
2344
184k
            }
2345
18.2k
            if (bIsAllNAN)
2346
3.89k
            {
2347
3.89k
                CPLFree(padfM);
2348
3.89k
                padfM = nullptr;
2349
3.89k
            }
2350
2351
18.2k
            nOffset += 16 + 8 * nPoints;
2352
18.2k
        }
2353
2354
        /* --------------------------------------------------------------------
2355
         */
2356
        /*      If we have curves, collect them now. */
2357
        /* --------------------------------------------------------------------
2358
         */
2359
34.3k
        int nCurves = 0;
2360
34.3k
        CurveSegment *pasCurves = nullptr;
2361
34.3k
        if (bHasCurves && nOffset + 4 <= nBytes)
2362
34.3k
        {
2363
34.3k
            memcpy(&nCurves, pabyShape + nOffset, 4);
2364
34.3k
            CPL_LSBPTR32(&nCurves);
2365
34.3k
            nOffset += 4;
2366
#ifdef DEBUG_VERBOSE
2367
            CPLDebug("OGR", "nCurves = %d", nCurves);
2368
#endif
2369
34.3k
            if (nCurves < 0 || nCurves > (nBytes - nOffset) / (8 + 20))
2370
0
            {
2371
0
                CPLDebug("OGR", "Invalid nCurves = %d", nCurves);
2372
0
                nCurves = 0;
2373
0
            }
2374
34.3k
            pasCurves = static_cast<CurveSegment *>(
2375
34.3k
                VSI_MALLOC2_VERBOSE(sizeof(CurveSegment), nCurves));
2376
34.3k
            if (pasCurves == nullptr)
2377
0
            {
2378
0
                nCurves = 0;
2379
0
            }
2380
34.3k
            int iCurve = 0;
2381
70.1k
            for (int i = 0; i < nCurves; i++)
2382
38.0k
            {
2383
38.0k
                if (nOffset + 8 > nBytes)
2384
0
                {
2385
0
                    CPLDebug("OGR", "Not enough bytes");
2386
0
                    break;
2387
0
                }
2388
38.0k
                int nStartPointIdx = 0;
2389
38.0k
                memcpy(&nStartPointIdx, pabyShape + nOffset, 4);
2390
38.0k
                CPL_LSBPTR32(&nStartPointIdx);
2391
38.0k
                nOffset += 4;
2392
38.0k
                int nSegmentType = 0;
2393
38.0k
                memcpy(&nSegmentType, pabyShape + nOffset, 4);
2394
38.0k
                CPL_LSBPTR32(&nSegmentType);
2395
38.0k
                nOffset += 4;
2396
#ifdef DEBUG_VERBOSE
2397
                CPLDebug("OGR", "[%d] nStartPointIdx = %d, segmentType = %d", i,
2398
                         nSegmentType, nSegmentType);
2399
#endif
2400
38.0k
                if (nStartPointIdx < 0 || nStartPointIdx >= nPoints ||
2401
36.0k
                    (iCurve > 0 &&
2402
1.91k
                     nStartPointIdx <= pasCurves[iCurve - 1].nStartPointIdx))
2403
2.28k
                {
2404
2.28k
                    CPLDebug("OGR", "Invalid nStartPointIdx = %d",
2405
2.28k
                             nStartPointIdx);
2406
2.28k
                    break;
2407
2.28k
                }
2408
35.7k
                pasCurves[iCurve].nStartPointIdx = nStartPointIdx;
2409
35.7k
                if (nSegmentType == EXT_SHAPE_SEGMENT_ARC)
2410
23.8k
                {
2411
23.8k
                    if (nOffset + 20 > nBytes)
2412
0
                    {
2413
0
                        CPLDebug("OGR", "Not enough bytes");
2414
0
                        break;
2415
0
                    }
2416
23.8k
                    double dfVal1 = 0.0;
2417
23.8k
                    double dfVal2 = 0.0;
2418
23.8k
                    memcpy(&dfVal1, pabyShape + nOffset + 0, 8);
2419
23.8k
                    CPL_LSBPTR64(&dfVal1);
2420
23.8k
                    memcpy(&dfVal2, pabyShape + nOffset + 8, 8);
2421
23.8k
                    CPL_LSBPTR64(&dfVal2);
2422
23.8k
                    int nBits = 0;
2423
23.8k
                    memcpy(&nBits, pabyShape + nOffset + 16, 4);
2424
23.8k
                    CPL_LSBPTR32(&nBits);
2425
2426
#ifdef DEBUG_VERBOSE
2427
                    CPLDebug("OGR", "Arc: ");
2428
                    CPLDebug("OGR", " dfVal1 = %f, dfVal2 = %f, nBits=%X",
2429
                             dfVal1, dfVal2, nBits);
2430
                    if (nBits & EXT_SHAPE_ARC_EMPTY)
2431
                        CPLDebug("OGR", "  IsEmpty");
2432
                    if (nBits & EXT_SHAPE_ARC_CCW)
2433
                        CPLDebug("OGR", "  IsCCW");
2434
                    if (nBits & EXT_SHAPE_ARC_MINOR)
2435
                        CPLDebug("OGR", " IsMinor");
2436
                    if (nBits & EXT_SHAPE_ARC_LINE)
2437
                        CPLDebug("OGR", "  IsLine");
2438
                    if (nBits & EXT_SHAPE_ARC_POINT)
2439
                        CPLDebug("OGR", "  IsPoint");
2440
                    if (nBits & EXT_SHAPE_ARC_IP)
2441
                        CPLDebug("OGR", "  DefinedIP");
2442
#endif
2443
23.8k
                    if ((nBits & EXT_SHAPE_ARC_IP) != 0 &&
2444
11.8k
                        (nBits & EXT_SHAPE_ARC_LINE) == 0)
2445
10.4k
                    {
2446
10.4k
                        pasCurves[iCurve].eType = CURVE_ARC_INTERIOR_POINT;
2447
10.4k
                        pasCurves[iCurve].u.ArcByIntermediatePoint.dfX = dfVal1;
2448
10.4k
                        pasCurves[iCurve].u.ArcByIntermediatePoint.dfY = dfVal2;
2449
10.4k
                        iCurve++;
2450
10.4k
                    }
2451
13.4k
                    else if ((nBits & EXT_SHAPE_ARC_EMPTY) == 0 &&
2452
9.07k
                             (nBits & EXT_SHAPE_ARC_LINE) == 0 &&
2453
6.63k
                             (nBits & EXT_SHAPE_ARC_POINT) == 0)
2454
6.30k
                    {
2455
                        // This is the old deprecated way
2456
6.30k
                        pasCurves[iCurve].eType = CURVE_ARC_CENTER_POINT;
2457
6.30k
                        pasCurves[iCurve].u.ArcByCenterPoint.dfX = dfVal1;
2458
6.30k
                        pasCurves[iCurve].u.ArcByCenterPoint.dfY = dfVal2;
2459
6.30k
                        pasCurves[iCurve].u.ArcByCenterPoint.bIsCCW =
2460
6.30k
                            (nBits & EXT_SHAPE_ARC_CCW) != 0;
2461
6.30k
                        iCurve++;
2462
6.30k
                    }
2463
23.8k
                    nOffset += 16 + 4;
2464
23.8k
                }
2465
11.9k
                else if (nSegmentType == EXT_SHAPE_SEGMENT_BEZIER)
2466
4.89k
                {
2467
4.89k
                    if (nOffset + 32 > nBytes)
2468
0
                    {
2469
0
                        CPLDebug("OGR", "Not enough bytes");
2470
0
                        break;
2471
0
                    }
2472
4.89k
                    double dfX1 = 0.0;
2473
4.89k
                    double dfY1 = 0.0;
2474
4.89k
                    memcpy(&dfX1, pabyShape + nOffset + 0, 8);
2475
4.89k
                    CPL_LSBPTR64(&dfX1);
2476
4.89k
                    memcpy(&dfY1, pabyShape + nOffset + 8, 8);
2477
4.89k
                    CPL_LSBPTR64(&dfY1);
2478
4.89k
                    double dfX2 = 0.0;
2479
4.89k
                    double dfY2 = 0.0;
2480
4.89k
                    memcpy(&dfX2, pabyShape + nOffset + 16, 8);
2481
4.89k
                    CPL_LSBPTR64(&dfX2);
2482
4.89k
                    memcpy(&dfY2, pabyShape + nOffset + 24, 8);
2483
4.89k
                    CPL_LSBPTR64(&dfY2);
2484
#ifdef DEBUG_VERBOSE
2485
                    CPLDebug("OGR", "Bezier:");
2486
                    CPLDebug("OGR", "  dfX1 = %f, dfY1 = %f", dfX1, dfY1);
2487
                    CPLDebug("OGR", "  dfX2 = %f, dfY2 = %f", dfX2, dfY2);
2488
#endif
2489
4.89k
                    pasCurves[iCurve].eType = CURVE_BEZIER;
2490
4.89k
                    pasCurves[iCurve].u.Bezier.dfX1 = dfX1;
2491
4.89k
                    pasCurves[iCurve].u.Bezier.dfY1 = dfY1;
2492
4.89k
                    pasCurves[iCurve].u.Bezier.dfX2 = dfX2;
2493
4.89k
                    pasCurves[iCurve].u.Bezier.dfY2 = dfY2;
2494
4.89k
                    iCurve++;
2495
4.89k
                    nOffset += 32;
2496
4.89k
                }
2497
7.00k
                else if (nSegmentType == EXT_SHAPE_SEGMENT_ELLIPSE)
2498
7.00k
                {
2499
7.00k
                    if (nOffset + 44 > nBytes)
2500
0
                    {
2501
0
                        CPLDebug("OGR", "Not enough bytes");
2502
0
                        break;
2503
0
                    }
2504
7.00k
                    double dfVS0 = 0.0;
2505
7.00k
                    memcpy(&dfVS0, pabyShape + nOffset, 8);
2506
7.00k
                    nOffset += 8;
2507
7.00k
                    CPL_LSBPTR64(&dfVS0);
2508
2509
7.00k
                    double dfVS1 = 0.0;
2510
7.00k
                    memcpy(&dfVS1, pabyShape + nOffset, 8);
2511
7.00k
                    nOffset += 8;
2512
7.00k
                    CPL_LSBPTR64(&dfVS1);
2513
2514
7.00k
                    double dfRotationOrFromV = 0.0;
2515
7.00k
                    memcpy(&dfRotationOrFromV, pabyShape + nOffset, 8);
2516
7.00k
                    nOffset += 8;
2517
7.00k
                    CPL_LSBPTR64(&dfRotationOrFromV);
2518
2519
7.00k
                    double dfSemiMajor = 0.0;
2520
7.00k
                    memcpy(&dfSemiMajor, pabyShape + nOffset, 8);
2521
7.00k
                    nOffset += 8;
2522
7.00k
                    CPL_LSBPTR64(&dfSemiMajor);
2523
2524
7.00k
                    double dfMinorMajorRatioOrDeltaV = 0.0;
2525
7.00k
                    memcpy(&dfMinorMajorRatioOrDeltaV, pabyShape + nOffset, 8);
2526
7.00k
                    nOffset += 8;
2527
7.00k
                    CPL_LSBPTR64(&dfMinorMajorRatioOrDeltaV);
2528
2529
7.00k
                    int nBits = 0;
2530
7.00k
                    memcpy(&nBits, pabyShape + nOffset, 4);
2531
7.00k
                    CPL_LSBPTR32(&nBits);
2532
7.00k
                    nOffset += 4;
2533
2534
#ifdef DEBUG_VERBOSE
2535
                    CPLDebug("OGR", "Ellipse:");
2536
                    CPLDebug("OGR", "  dfVS0 = %f", dfVS0);
2537
                    CPLDebug("OGR", "  dfVS1 = %f", dfVS1);
2538
                    CPLDebug("OGR", "  dfRotationOrFromV = %f",
2539
                             dfRotationOrFromV);
2540
                    CPLDebug("OGR", "  dfSemiMajor = %f", dfSemiMajor);
2541
                    CPLDebug("OGR", "  dfMinorMajorRatioOrDeltaV = %f",
2542
                             dfMinorMajorRatioOrDeltaV);
2543
                    CPLDebug("OGR", "  nBits=%X", nBits);
2544
2545
                    if (nBits & EXT_SHAPE_ELLIPSE_EMPTY)
2546
                        CPLDebug("OGR", "   IsEmpty");
2547
                    if (nBits & EXT_SHAPE_ELLIPSE_LINE)
2548
                        CPLDebug("OGR", "   IsLine");
2549
                    if (nBits & EXT_SHAPE_ELLIPSE_POINT)
2550
                        CPLDebug("OGR", "   IsPoint");
2551
                    if (nBits & EXT_SHAPE_ELLIPSE_CIRCULAR)
2552
                        CPLDebug("OGR", "   IsCircular");
2553
                    if (nBits & EXT_SHAPE_ELLIPSE_CENTER_TO)
2554
                        CPLDebug("OGR", "   CenterTo");
2555
                    if (nBits & EXT_SHAPE_ELLIPSE_CENTER_FROM)
2556
                        CPLDebug("OGR", "   CenterFrom");
2557
                    if (nBits & EXT_SHAPE_ELLIPSE_CCW)
2558
                        CPLDebug("OGR", "   IsCCW");
2559
                    if (nBits & EXT_SHAPE_ELLIPSE_MINOR)
2560
                        CPLDebug("OGR", "   IsMinor");
2561
                    if (nBits & EXT_SHAPE_ELLIPSE_COMPLETE)
2562
                        CPLDebug("OGR", "   IsComplete");
2563
#endif
2564
2565
7.00k
                    if ((nBits & EXT_SHAPE_ELLIPSE_CENTER_TO) == 0 &&
2566
6.47k
                        (nBits & EXT_SHAPE_ELLIPSE_CENTER_FROM) == 0)
2567
5.95k
                    {
2568
5.95k
                        pasCurves[iCurve].eType = CURVE_ELLIPSE_BY_CENTER;
2569
5.95k
                        pasCurves[iCurve].u.EllipseByCenter.dfX = dfVS0;
2570
5.95k
                        pasCurves[iCurve].u.EllipseByCenter.dfY = dfVS1;
2571
5.95k
                        pasCurves[iCurve].u.EllipseByCenter.dfRotationDeg =
2572
5.95k
                            dfRotationOrFromV / M_PI * 180;
2573
5.95k
                        pasCurves[iCurve].u.EllipseByCenter.dfSemiMajor =
2574
5.95k
                            dfSemiMajor;
2575
5.95k
                        pasCurves[iCurve].u.EllipseByCenter.dfRatioSemiMinor =
2576
5.95k
                            dfMinorMajorRatioOrDeltaV;
2577
5.95k
                        pasCurves[iCurve].u.EllipseByCenter.bIsMinor =
2578
5.95k
                            ((nBits & EXT_SHAPE_ELLIPSE_MINOR) != 0);
2579
5.95k
                        pasCurves[iCurve].u.EllipseByCenter.bIsComplete =
2580
5.95k
                            ((nBits & EXT_SHAPE_ELLIPSE_COMPLETE) != 0);
2581
5.95k
                        iCurve++;
2582
5.95k
                    }
2583
7.00k
                }
2584
0
                else
2585
0
                {
2586
0
                    CPLDebug("OGR", "unhandled segmentType = %d", nSegmentType);
2587
0
                }
2588
35.7k
            }
2589
2590
34.3k
            nCurves = iCurve;
2591
34.3k
        }
2592
2593
        /* --------------------------------------------------------------------
2594
         */
2595
        /*      Build corresponding OGR objects. */
2596
        /* --------------------------------------------------------------------
2597
         */
2598
34.3k
        if (nSHPType == SHPT_ARC || nSHPType == SHPT_ARCZ ||
2599
26.1k
            nSHPType == SHPT_ARCM || nSHPType == SHPT_ARCZM)
2600
8.17k
        {
2601
            /* --------------------------------------------------------------------
2602
             */
2603
            /*      Arc - As LineString */
2604
            /* --------------------------------------------------------------------
2605
             */
2606
8.17k
            if (nParts == 1)
2607
1.70k
            {
2608
1.70k
                if (nCurves > 0)
2609
1.56k
                {
2610
1.56k
                    *ppoGeom = OGRShapeCreateCompoundCurve(
2611
1.56k
                        0, nPoints, pasCurves, nCurves, 0, padfX, padfY,
2612
1.56k
                        bHasZ ? padfZ : nullptr, padfM, nullptr);
2613
1.56k
                }
2614
144
                else
2615
144
                {
2616
144
                    OGRLineString *poLine = new OGRLineString();
2617
144
                    *ppoGeom = poLine;
2618
2619
144
                    poLine->setPoints(nPoints, padfX, padfY, padfZ, padfM);
2620
144
                }
2621
1.70k
            }
2622
2623
            /* --------------------------------------------------------------------
2624
             */
2625
            /*      Arc - As MultiLineString */
2626
            /* --------------------------------------------------------------------
2627
             */
2628
6.46k
            else
2629
6.46k
            {
2630
6.46k
                if (nCurves > 0)
2631
5.06k
                {
2632
5.06k
                    OGRMultiCurve *poMulti = new OGRMultiCurve;
2633
5.06k
                    *ppoGeom = poMulti;
2634
2635
5.06k
                    int iCurveIdx = 0;
2636
17.0k
                    for (int i = 0; i < nParts; i++)
2637
11.9k
                    {
2638
11.9k
                        const int nVerticesInThisPart =
2639
11.9k
                            i == nParts - 1
2640
11.9k
                                ? nPoints - panPartStart[i]
2641
11.9k
                                : panPartStart[i + 1] - panPartStart[i];
2642
2643
11.9k
                        OGRCurve *poCurve = OGRShapeCreateCompoundCurve(
2644
11.9k
                            panPartStart[i], nVerticesInThisPart, pasCurves,
2645
11.9k
                            nCurves, iCurveIdx, padfX, padfY,
2646
11.9k
                            bHasZ ? padfZ : nullptr, padfM, &iCurveIdx);
2647
11.9k
                        if (poCurve == nullptr || poMulti->addGeometryDirectly(
2648
11.9k
                                                      poCurve) != OGRERR_NONE)
2649
0
                        {
2650
0
                            delete poCurve;
2651
0
                            delete poMulti;
2652
0
                            *ppoGeom = nullptr;
2653
0
                            break;
2654
0
                        }
2655
11.9k
                    }
2656
5.06k
                }
2657
1.40k
                else
2658
1.40k
                {
2659
1.40k
                    OGRMultiLineString *poMulti = new OGRMultiLineString;
2660
1.40k
                    *ppoGeom = poMulti;
2661
2662
4.23k
                    for (int i = 0; i < nParts; i++)
2663
2.83k
                    {
2664
2.83k
                        OGRLineString *poLine = new OGRLineString;
2665
2.83k
                        const int nVerticesInThisPart =
2666
2.83k
                            i == nParts - 1
2667
2.83k
                                ? nPoints - panPartStart[i]
2668
2.83k
                                : panPartStart[i + 1] - panPartStart[i];
2669
2670
2.83k
                        poLine->setPoints(
2671
2.83k
                            nVerticesInThisPart, padfX + panPartStart[i],
2672
2.83k
                            padfY + panPartStart[i], padfZ + panPartStart[i],
2673
2.83k
                            padfM != nullptr ? padfM + panPartStart[i]
2674
2.83k
                                             : nullptr);
2675
2676
2.83k
                        poMulti->addGeometryDirectly(poLine);
2677
2.83k
                    }
2678
1.40k
                }
2679
6.46k
            }
2680
8.17k
        }  // ARC.
2681
2682
        /* --------------------------------------------------------------------
2683
         */
2684
        /*      Polygon */
2685
        /* --------------------------------------------------------------------
2686
         */
2687
26.1k
        else if (nSHPType == SHPT_POLYGON || nSHPType == SHPT_POLYGONZ ||
2688
0
                 nSHPType == SHPT_POLYGONM || nSHPType == SHPT_POLYGONZM)
2689
26.1k
        {
2690
26.1k
            if (nCurves > 0 && nParts != 0)
2691
20.2k
            {
2692
20.2k
                if (nParts == 1)
2693
9.09k
                {
2694
9.09k
                    OGRCurvePolygon *poOGRPoly = new OGRCurvePolygon;
2695
9.09k
                    *ppoGeom = poOGRPoly;
2696
9.09k
                    const int nVerticesInThisPart = nPoints - panPartStart[0];
2697
2698
9.09k
                    OGRCurve *poRing = OGRShapeCreateCompoundCurve(
2699
9.09k
                        panPartStart[0], nVerticesInThisPart, pasCurves,
2700
9.09k
                        nCurves, 0, padfX, padfY, bHasZ ? padfZ : nullptr,
2701
9.09k
                        padfM, nullptr);
2702
9.09k
                    if (poRing == nullptr ||
2703
9.09k
                        poOGRPoly->addRingDirectly(poRing) != OGRERR_NONE)
2704
1.58k
                    {
2705
1.58k
                        delete poRing;
2706
1.58k
                        delete poOGRPoly;
2707
1.58k
                        *ppoGeom = nullptr;
2708
1.58k
                    }
2709
9.09k
                }
2710
11.1k
                else
2711
11.1k
                {
2712
11.1k
                    std::vector<std::unique_ptr<OGRGeometry>> apoPolygons;
2713
11.1k
                    int iCurveIdx = 0;
2714
45.9k
                    for (int i = 0; i < nParts; i++)
2715
36.8k
                    {
2716
36.8k
                        auto poPoly = std::make_unique<OGRCurvePolygon>();
2717
36.8k
                        const int nVerticesInThisPart =
2718
36.8k
                            i == nParts - 1
2719
36.8k
                                ? nPoints - panPartStart[i]
2720
36.8k
                                : panPartStart[i + 1] - panPartStart[i];
2721
2722
36.8k
                        auto poRing = std::unique_ptr<OGRCurve>(
2723
36.8k
                            OGRShapeCreateCompoundCurve(
2724
36.8k
                                panPartStart[i], nVerticesInThisPart, pasCurves,
2725
36.8k
                                nCurves, iCurveIdx, padfX, padfY,
2726
36.8k
                                bHasZ ? padfZ : nullptr, padfM, &iCurveIdx));
2727
36.8k
                        if (poRing == nullptr ||
2728
36.8k
                            poPoly->addRing(std::move(poRing)) != OGRERR_NONE)
2729
2.05k
                        {
2730
2.05k
                            apoPolygons.clear();
2731
2.05k
                            *ppoGeom = nullptr;
2732
2.05k
                            break;
2733
2.05k
                        }
2734
34.7k
                        apoPolygons.push_back(std::move(poPoly));
2735
34.7k
                    }
2736
2737
11.1k
                    if (!apoPolygons.empty())
2738
9.05k
                    {
2739
9.05k
                        bool isValidGeometry = false;
2740
9.05k
                        const char *const apszOptions[] = {"METHOD=ONLY_CCW",
2741
9.05k
                                                           nullptr};
2742
9.05k
                        auto poOGR = OGRGeometryFactory::organizePolygons(
2743
9.05k
                            apoPolygons, &isValidGeometry, apszOptions);
2744
2745
9.05k
                        if (!isValidGeometry)
2746
284
                        {
2747
284
                            CPLError(CE_Warning, CPLE_AppDefined,
2748
284
                                     "Geometry of polygon cannot be translated "
2749
284
                                     "to Simple Geometry.  All polygons will "
2750
284
                                     "be contained in a multipolygon.");
2751
284
                        }
2752
2753
9.05k
                        *ppoGeom = poOGR.release();
2754
9.05k
                    }
2755
11.1k
                }
2756
20.2k
            }
2757
5.98k
            else if (nParts != 0)
2758
5.98k
            {
2759
5.98k
                if (nParts == 1)
2760
2.32k
                {
2761
2.32k
                    OGRPolygon *poOGRPoly = new OGRPolygon;
2762
2.32k
                    *ppoGeom = poOGRPoly;
2763
2.32k
                    OGRLinearRing *poRing = new OGRLinearRing;
2764
2.32k
                    int nVerticesInThisPart = nPoints - panPartStart[0];
2765
2766
2.32k
                    poRing->setPoints(
2767
2.32k
                        nVerticesInThisPart, padfX + panPartStart[0],
2768
2.32k
                        padfY + panPartStart[0], padfZ + panPartStart[0],
2769
2.32k
                        padfM != nullptr ? padfM + panPartStart[0] : nullptr);
2770
2771
2.32k
                    if (poOGRPoly->addRingDirectly(poRing) != OGRERR_NONE)
2772
0
                    {
2773
0
                        delete poRing;
2774
0
                        delete poOGRPoly;
2775
0
                        *ppoGeom = nullptr;
2776
0
                    }
2777
2.32k
                }
2778
3.66k
                else
2779
3.66k
                {
2780
3.66k
                    std::vector<std::unique_ptr<OGRGeometry>> apoPolygons;
2781
3.66k
                    apoPolygons.reserve(nParts);
2782
24.1k
                    for (int i = 0; i < nParts; i++)
2783
20.5k
                    {
2784
20.5k
                        auto poRing = std::make_unique<OGRLinearRing>();
2785
20.5k
                        const int nVerticesInThisPart =
2786
20.5k
                            i == nParts - 1
2787
20.5k
                                ? nPoints - panPartStart[i]
2788
20.5k
                                : panPartStart[i + 1] - panPartStart[i];
2789
2790
20.5k
                        poRing->setPoints(
2791
20.5k
                            nVerticesInThisPart, padfX + panPartStart[i],
2792
20.5k
                            padfY + panPartStart[i], padfZ + panPartStart[i],
2793
20.5k
                            padfM != nullptr ? padfM + panPartStart[i]
2794
20.5k
                                             : nullptr);
2795
20.5k
                        auto poPoly = std::make_unique<OGRPolygon>();
2796
20.5k
                        if (poPoly->addRing(std::move(poRing)) != OGRERR_NONE)
2797
0
                        {
2798
0
                            apoPolygons.clear();
2799
0
                            *ppoGeom = nullptr;
2800
0
                            break;
2801
0
                        }
2802
20.5k
                        apoPolygons.push_back(std::move(poPoly));
2803
20.5k
                    }
2804
2805
3.66k
                    if (!apoPolygons.empty())
2806
3.66k
                    {
2807
3.66k
                        bool isValidGeometry = false;
2808
                        // The outer ring is supposed to be clockwise oriented
2809
                        // If it is not, then use the default/slow method.
2810
3.66k
                        const char *const apszOptions[] = {
2811
3.66k
                            !(apoPolygons.front()
2812
3.66k
                                  ->toPolygon()
2813
3.66k
                                  ->getExteriorRing()
2814
3.66k
                                  ->isClockwise())
2815
3.66k
                                ? "METHOD=DEFAULT"
2816
3.66k
                                : "METHOD=ONLY_CCW",
2817
3.66k
                            nullptr};
2818
3.66k
                        auto poOGR = OGRGeometryFactory::organizePolygons(
2819
3.66k
                            apoPolygons, &isValidGeometry, apszOptions);
2820
2821
3.66k
                        if (!isValidGeometry)
2822
1.02k
                        {
2823
1.02k
                            CPLError(CE_Warning, CPLE_AppDefined,
2824
1.02k
                                     "Geometry of polygon cannot be translated "
2825
1.02k
                                     "to Simple Geometry. All polygons will be "
2826
1.02k
                                     "contained in a multipolygon.");
2827
1.02k
                        }
2828
2829
3.66k
                        *ppoGeom = poOGR.release();
2830
3.66k
                    }
2831
3.66k
                }
2832
5.98k
            }
2833
0
            else
2834
0
            {
2835
0
                *ppoGeom = new OGRPolygon();
2836
0
            }
2837
26.1k
        }  // Polygon.
2838
2839
        /* --------------------------------------------------------------------
2840
         */
2841
        /*      Multipatch */
2842
        /* --------------------------------------------------------------------
2843
         */
2844
0
        else if (bIsMultiPatch)
2845
0
        {
2846
0
            *ppoGeom =
2847
0
                OGRCreateFromMultiPatch(nParts, panPartStart, panPartType,
2848
0
                                        nPoints, padfX, padfY, padfZ);
2849
0
        }
2850
2851
34.3k
        CPLFree(panPartStart);
2852
34.3k
        CPLFree(panPartType);
2853
34.3k
        CPLFree(padfX);
2854
34.3k
        CPLFree(padfY);
2855
34.3k
        CPLFree(padfZ);
2856
34.3k
        CPLFree(padfM);
2857
34.3k
        CPLFree(pasCurves);
2858
2859
34.3k
        if (*ppoGeom != nullptr)
2860
30.7k
        {
2861
30.7k
            if (!bHasZ)
2862
17.0k
                (*ppoGeom)->set3D(FALSE);
2863
30.7k
        }
2864
2865
34.3k
        return *ppoGeom != nullptr ? OGRERR_NONE : OGRERR_FAILURE;
2866
34.3k
    }
2867
2868
    /* ==================================================================== */
2869
    /*     Extract vertices for a MultiPoint.                               */
2870
    /* ==================================================================== */
2871
0
    else if (nSHPType == SHPT_MULTIPOINT || nSHPType == SHPT_MULTIPOINTM ||
2872
0
             nSHPType == SHPT_MULTIPOINTZ || nSHPType == SHPT_MULTIPOINTZM)
2873
0
    {
2874
0
        GInt32 nPoints = 0;
2875
0
        memcpy(&nPoints, pabyShape + 36, 4);
2876
0
        CPL_LSBPTR32(&nPoints);
2877
2878
0
        if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
2879
0
        {
2880
0
            CPLError(CE_Failure, CPLE_AppDefined,
2881
0
                     "Corrupted Shape : nPoints=%d.", nPoints);
2882
0
            return OGRERR_FAILURE;
2883
0
        }
2884
2885
0
        const GInt32 nOffsetZ = 40 + 2 * 8 * nPoints + 2 * 8;
2886
0
        GInt32 nOffsetM = 0;
2887
0
        if (bHasM)
2888
0
            nOffsetM = bHasZ ? nOffsetZ + 2 * 8 * 8 * nPoints : nOffsetZ;
2889
2890
0
        OGRMultiPoint *poMultiPt = new OGRMultiPoint;
2891
0
        *ppoGeom = poMultiPt;
2892
2893
0
        for (int i = 0; i < nPoints; i++)
2894
0
        {
2895
0
            OGRPoint *poPt = new OGRPoint;
2896
2897
            // Copy X.
2898
0
            double x = 0.0;
2899
0
            memcpy(&x, pabyShape + 40 + i * 16, 8);
2900
0
            CPL_LSBPTR64(&x);
2901
0
            poPt->setX(x);
2902
2903
            // Copy Y.
2904
0
            double y = 0.0;
2905
0
            memcpy(&y, pabyShape + 40 + i * 16 + 8, 8);
2906
0
            CPL_LSBPTR64(&y);
2907
0
            poPt->setY(y);
2908
2909
            // Copy Z.
2910
0
            if (bHasZ)
2911
0
            {
2912
0
                double z = 0.0;
2913
0
                memcpy(&z, pabyShape + nOffsetZ + i * 8, 8);
2914
0
                CPL_LSBPTR64(&z);
2915
0
                poPt->setZ(z);
2916
0
            }
2917
2918
            // Copy M.
2919
0
            if (bHasM)
2920
0
            {
2921
0
                double m = 0.0;
2922
0
                memcpy(&m, pabyShape + nOffsetM + i * 8, 8);
2923
0
                CPL_LSBPTR64(&m);
2924
0
                poPt->setM(m);
2925
0
            }
2926
2927
0
            poMultiPt->addGeometryDirectly(poPt);
2928
0
        }
2929
2930
0
        poMultiPt->set3D(bHasZ);
2931
0
        poMultiPt->setMeasured(bHasM);
2932
2933
0
        return OGRERR_NONE;
2934
0
    }
2935
2936
    /* ==================================================================== */
2937
    /*      Extract vertices for a point.                                   */
2938
    /* ==================================================================== */
2939
0
    else if (nSHPType == SHPT_POINT || nSHPType == SHPT_POINTM ||
2940
0
             nSHPType == SHPT_POINTZ || nSHPType == SHPT_POINTZM)
2941
0
    {
2942
0
        if (nBytes < 4 + 8 + 8 + ((bHasZ) ? 8 : 0) + ((bHasM) ? 8 : 0))
2943
0
        {
2944
0
            CPLError(CE_Failure, CPLE_AppDefined,
2945
0
                     "Corrupted Shape : nBytes=%d, nSHPType=%d", nBytes,
2946
0
                     nSHPType);
2947
0
            return OGRERR_FAILURE;
2948
0
        }
2949
2950
0
        double dfX = 0.0;
2951
0
        double dfY = 0.0;
2952
2953
0
        memcpy(&dfX, pabyShape + 4, 8);
2954
0
        memcpy(&dfY, pabyShape + 4 + 8, 8);
2955
2956
0
        CPL_LSBPTR64(&dfX);
2957
0
        CPL_LSBPTR64(&dfY);
2958
        // int nOffset = 20 + 8;
2959
2960
0
        double dfZ = 0.0;
2961
0
        if (bHasZ)
2962
0
        {
2963
0
            memcpy(&dfZ, pabyShape + 4 + 16, 8);
2964
0
            CPL_LSBPTR64(&dfZ);
2965
0
        }
2966
2967
0
        double dfM = 0.0;
2968
0
        if (bHasM)
2969
0
        {
2970
0
            memcpy(&dfM, pabyShape + 4 + 16 + ((bHasZ) ? 8 : 0), 8);
2971
0
            CPL_LSBPTR64(&dfM);
2972
0
        }
2973
2974
0
        if (bHasZ && bHasM)
2975
0
        {
2976
0
            *ppoGeom = new OGRPoint(dfX, dfY, dfZ, dfM);
2977
0
        }
2978
0
        else if (bHasZ)
2979
0
        {
2980
0
            *ppoGeom = new OGRPoint(dfX, dfY, dfZ);
2981
0
        }
2982
0
        else if (bHasM)
2983
0
        {
2984
0
            OGRPoint *poPoint = new OGRPoint(dfX, dfY);
2985
0
            poPoint->setM(dfM);
2986
0
            *ppoGeom = poPoint;
2987
0
        }
2988
0
        else
2989
0
        {
2990
0
            *ppoGeom = new OGRPoint(dfX, dfY);
2991
0
        }
2992
2993
0
        return OGRERR_NONE;
2994
0
    }
2995
2996
0
    CPLError(CE_Failure, CPLE_AppDefined, "Unsupported geometry type: %d",
2997
0
             nSHPType);
2998
2999
0
    return OGRERR_FAILURE;
3000
34.9k
}
3001
3002
#ifdef HAVE_WFLAG_UNREACHABLE_CODE_AGGRESSIVE
3003
#if defined(__clang__)
3004
#pragma clang diagnostic pop
3005
#endif
3006
#endif