Coverage Report

Created: 2025-11-16 06:25

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
0
{
148
0
    nPartType &= 0xf;
149
150
0
    if (nPartType == SHPP_TRISTRIP)
151
0
    {
152
0
        if (poMP != nullptr && poLastPoly != nullptr)
153
0
        {
154
0
            poMP->addGeometryDirectly(poLastPoly);
155
0
            poLastPoly = nullptr;
156
0
        }
157
158
0
        OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
159
0
        for (int iBaseVert = 0; iBaseVert < nPartPoints - 2; iBaseVert++)
160
0
        {
161
0
            const int iSrcVert = iBaseVert;
162
163
0
            OGRPoint oPoint1(padfX[iSrcVert], padfY[iSrcVert], padfZ[iSrcVert]);
164
165
0
            OGRPoint oPoint2(padfX[iSrcVert + 1], padfY[iSrcVert + 1],
166
0
                             padfZ[iSrcVert + 1]);
167
168
0
            OGRPoint oPoint3(padfX[iSrcVert + 2], padfY[iSrcVert + 2],
169
0
                             padfZ[iSrcVert + 2]);
170
171
0
            OGRTriangle *poTriangle =
172
0
                new OGRTriangle(oPoint1, oPoint2, oPoint3);
173
174
0
            poTIN->addGeometryDirectly(poTriangle);
175
0
        }
176
0
        poGC->addGeometryDirectly(poTIN);
177
0
    }
178
0
    else if (nPartType == SHPP_TRIFAN)
179
0
    {
180
0
        if (poMP != nullptr && poLastPoly != nullptr)
181
0
        {
182
0
            poMP->addGeometryDirectly(poLastPoly);
183
0
            poLastPoly = nullptr;
184
0
        }
185
186
0
        OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
187
0
        for (int iBaseVert = 0; iBaseVert < nPartPoints - 2; iBaseVert++)
188
0
        {
189
0
            const int iSrcVert = iBaseVert;
190
191
0
            OGRPoint oPoint1(padfX[0], padfY[0], padfZ[0]);
192
193
0
            OGRPoint oPoint2(padfX[iSrcVert + 1], padfY[iSrcVert + 1],
194
0
                             padfZ[iSrcVert + 1]);
195
196
0
            OGRPoint oPoint3(padfX[iSrcVert + 2], padfY[iSrcVert + 2],
197
0
                             padfZ[iSrcVert + 2]);
198
199
0
            OGRTriangle *poTriangle =
200
0
                new OGRTriangle(oPoint1, oPoint2, oPoint3);
201
202
0
            poTIN->addGeometryDirectly(poTriangle);
203
0
        }
204
0
        poGC->addGeometryDirectly(poTIN);
205
0
    }
206
0
    else if (nPartType == SHPP_OUTERRING || nPartType == SHPP_INNERRING ||
207
0
             nPartType == SHPP_FIRSTRING || nPartType == SHPP_RING)
208
0
    {
209
0
        if (poMP == nullptr)
210
0
        {
211
0
            poMP = new OGRMultiPolygon();
212
0
        }
213
214
0
        if (poMP != nullptr && poLastPoly != nullptr &&
215
0
            (nPartType == SHPP_OUTERRING || nPartType == SHPP_FIRSTRING))
216
0
        {
217
0
            poMP->addGeometryDirectly(poLastPoly);
218
0
            poLastPoly = nullptr;
219
0
        }
220
221
0
        if (poLastPoly == nullptr)
222
0
            poLastPoly = new OGRPolygon();
223
224
0
        OGRLinearRing *poRing = new OGRLinearRing;
225
226
0
        poRing->setPoints(nPartPoints, const_cast<double *>(padfX),
227
0
                          const_cast<double *>(padfY),
228
0
                          const_cast<double *>(padfZ));
229
230
0
        poRing->closeRings();
231
232
0
        poLastPoly->addRingDirectly(poRing);
233
0
    }
234
0
    else if (nPartType == SHPP_TRIANGLES)
235
0
    {
236
0
        if (poMP != nullptr && poLastPoly != nullptr)
237
0
        {
238
0
            poMP->addGeometryDirectly(poLastPoly);
239
0
            poLastPoly = nullptr;
240
0
        }
241
242
0
        OGRTriangulatedSurface *poTIN = new OGRTriangulatedSurface();
243
0
        for (int iBaseVert = 0; iBaseVert < nPartPoints - 2; iBaseVert += 3)
244
0
        {
245
0
            const int iSrcVert = iBaseVert;
246
247
0
            OGRPoint oPoint1(padfX[iSrcVert], padfY[iSrcVert], padfZ[iSrcVert]);
248
249
0
            OGRPoint oPoint2(padfX[iSrcVert + 1], padfY[iSrcVert + 1],
250
0
                             padfZ[iSrcVert + 1]);
251
252
0
            OGRPoint oPoint3(padfX[iSrcVert + 2], padfY[iSrcVert + 2],
253
0
                             padfZ[iSrcVert + 2]);
254
255
0
            OGRTriangle *poTriangle =
256
0
                new OGRTriangle(oPoint1, oPoint2, oPoint3);
257
258
0
            poTIN->addGeometryDirectly(poTriangle);
259
0
        }
260
0
        poGC->addGeometryDirectly(poTIN);
261
0
    }
262
0
    else
263
0
        CPLDebug("OGR", "Unrecognized parttype %d, ignored.", nPartType);
264
0
}
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
0
{
271
0
    int idx = 0;
272
0
    if (padfX[0] > padfX[1])
273
0
    {
274
0
        idx = 1;
275
0
    }
276
0
    else if (padfX[0] == padfX[1])
277
0
    {
278
0
        if (padfY[0] > padfY[1])
279
0
        {
280
0
            idx = 1;
281
0
        }
282
0
        else if (padfY[0] == padfY[1])
283
0
        {
284
0
            if (padfZ[0] > padfZ[1])
285
0
            {
286
0
                idx = 1;
287
0
            }
288
0
        }
289
0
    }
290
0
    std::vector<double> oVector;
291
0
    oVector.push_back(padfX[idx]);
292
0
    oVector.push_back(padfY[idx]);
293
0
    oVector.push_back(padfZ[idx]);
294
0
    oVector.push_back(padfX[1 - idx]);
295
0
    oVector.push_back(padfY[1 - idx]);
296
0
    oVector.push_back(padfZ[1 - idx]);
297
0
    const auto oIter = oMapEdges.find(oVector);
298
0
    if (oIter == oMapEdges.end())
299
0
    {
300
0
        oMapEdges[oVector] = std::pair(nPart, -1);
301
0
    }
302
0
    else
303
0
    {
304
0
        CPLAssert(oIter->second.first >= 0);
305
0
        if (oIter->second.second < 0)
306
0
            oIter->second.second = nPart;
307
0
        else
308
0
            return false;
309
0
    }
310
0
    return true;
311
0
}
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
0
{
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
0
    std::map<std::vector<double>, std::pair<int, int>> oMapEdges;
362
0
    bool bTINCandidate = nParts >= 2;
363
0
    std::set<int> oSetDuplicated;
364
0
    for (int iPart = 0; iPart < nParts && panPartStart != nullptr; iPart++)
365
0
    {
366
0
        int nPartPoints = 0;
367
368
        // Figure out details about this part's vertex list.
369
0
        if (iPart == nParts - 1)
370
0
            nPartPoints = nPoints - panPartStart[iPart];
371
0
        else
372
0
            nPartPoints = panPartStart[iPart + 1] - panPartStart[iPart];
373
0
        const int nPartStart = panPartStart[iPart];
374
375
0
        if (panPartType[iPart] == SHPP_OUTERRING && nPartPoints == 4 &&
376
0
            padfX[nPartStart] == padfX[nPartStart + 3] &&
377
0
            padfY[nPartStart] == padfY[nPartStart + 3] &&
378
0
            padfZ[nPartStart] == padfZ[nPartStart + 3] &&
379
0
            !std::isnan(padfX[nPartStart]) &&
380
0
            !std::isnan(padfX[nPartStart + 1]) &&
381
0
            !std::isnan(padfX[nPartStart + 2]) &&
382
0
            !std::isnan(padfY[nPartStart]) &&
383
0
            !std::isnan(padfY[nPartStart + 1]) &&
384
0
            !std::isnan(padfY[nPartStart + 2]) &&
385
0
            !std::isnan(padfZ[nPartStart]) &&
386
0
            !std::isnan(padfZ[nPartStart + 1]) &&
387
0
            !std::isnan(padfZ[nPartStart + 2]))
388
0
        {
389
0
            bool bDuplicate = false;
390
0
            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
0
            if (bDuplicate)
409
0
            {
410
0
                oSetDuplicated.insert(iPart);
411
0
            }
412
0
            else if (RegisterEdge(padfX + nPartStart, padfY + nPartStart,
413
0
                                  padfZ + nPartStart, iPart, oMapEdges) &&
414
0
                     RegisterEdge(padfX + nPartStart + 1,
415
0
                                  padfY + nPartStart + 1,
416
0
                                  padfZ + nPartStart + 1, iPart, oMapEdges) &&
417
0
                     RegisterEdge(padfX + nPartStart + 2,
418
0
                                  padfY + nPartStart + 2,
419
0
                                  padfZ + nPartStart + 2, iPart, oMapEdges))
420
0
            {
421
                // ok
422
0
            }
423
0
            else
424
0
            {
425
0
                bTINCandidate = false;
426
0
                break;
427
0
            }
428
0
        }
429
0
        else
430
0
        {
431
0
            bTINCandidate = false;
432
0
            break;
433
0
        }
434
0
    }
435
0
    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
0
    OGRGeometryCollection *poGC = new OGRGeometryCollection();
490
0
    OGRMultiPolygon *poMP = nullptr;
491
0
    OGRPolygon *poLastPoly = nullptr;
492
0
    for (int iPart = 0; iPart < nParts; iPart++)
493
0
    {
494
0
        int nPartPoints = 0;
495
0
        int nPartStart = 0;
496
497
        // Figure out details about this part's vertex list.
498
0
        if (panPartStart == nullptr)
499
0
        {
500
0
            nPartPoints = nPoints;
501
0
        }
502
0
        else
503
0
        {
504
505
0
            if (iPart == nParts - 1)
506
0
                nPartPoints = nPoints - panPartStart[iPart];
507
0
            else
508
0
                nPartPoints = panPartStart[iPart + 1] - panPartStart[iPart];
509
0
            nPartStart = panPartStart[iPart];
510
0
        }
511
512
0
        OGRCreateFromMultiPatchPart(poGC, poMP, poLastPoly, panPartType[iPart],
513
0
                                    nPartPoints, padfX + nPartStart,
514
0
                                    padfY + nPartStart, padfZ + nPartStart);
515
0
    }
516
517
0
    if (poMP != nullptr && poLastPoly != nullptr)
518
0
    {
519
0
        poMP->addGeometryDirectly(poLastPoly);
520
        // poLastPoly = NULL;
521
0
    }
522
0
    if (poMP != nullptr)
523
0
        poGC->addGeometryDirectly(poMP);
524
525
0
    if (poGC->getNumGeometries() == 1)
526
0
    {
527
0
        OGRGeometry *poResultGeom = poGC->getGeometryRef(0);
528
0
        poGC->removeGeometry(0, FALSE);
529
0
        delete poGC;
530
0
        return poResultGeom;
531
0
    }
532
533
0
    else
534
0
        return poGC;
535
0
}
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
0
{
1352
0
    const OGRwkbGeometryType eType = wkbFlatten(poGeomConst->getGeometryType());
1353
0
    if (eType != wkbPolygon && eType != wkbTriangle &&
1354
0
        eType != wkbMultiPolygon && eType != wkbMultiSurface &&
1355
0
        eType != wkbTIN && eType != wkbPolyhedralSurface &&
1356
0
        eType != wkbGeometryCollection)
1357
0
    {
1358
0
        return OGRERR_UNSUPPORTED_OPERATION;
1359
0
    }
1360
1361
0
    std::unique_ptr<OGRGeometry> poGeom(poGeomConst->clone());
1362
0
    poGeom->closeRings();
1363
1364
0
    OGRMultiPolygon *poMPoly = nullptr;
1365
0
    std::unique_ptr<OGRGeometry> poGeomToDelete;
1366
0
    if (eType == wkbMultiPolygon)
1367
0
        poMPoly = poGeom->toMultiPolygon();
1368
0
    else
1369
0
    {
1370
0
        poGeomToDelete = std::unique_ptr<OGRGeometry>(
1371
0
            OGRGeometryFactory::forceToMultiPolygon(poGeom->clone()));
1372
0
        if (poGeomToDelete.get() &&
1373
0
            wkbFlatten(poGeomToDelete->getGeometryType()) == wkbMultiPolygon)
1374
0
        {
1375
0
            poMPoly = poGeomToDelete->toMultiPolygon();
1376
0
        }
1377
0
    }
1378
0
    if (poMPoly == nullptr)
1379
0
    {
1380
0
        return OGRERR_UNSUPPORTED_OPERATION;
1381
0
    }
1382
1383
0
    nParts = 0;
1384
0
    anPartStart.clear();
1385
0
    anPartType.clear();
1386
0
    nPoints = 0;
1387
0
    aoPoints.clear();
1388
0
    adfZ.clear();
1389
0
    int nBeginLastPart = 0;
1390
0
    for (const auto poPoly : *poMPoly)
1391
0
    {
1392
        // Skip empties.
1393
0
        if (poPoly->IsEmpty())
1394
0
            continue;
1395
1396
0
        const int nRings = poPoly->getNumInteriorRings() + 1;
1397
0
        const OGRLinearRing *poRing = poPoly->getExteriorRing();
1398
0
        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
0
        else
1480
0
        {
1481
0
            anPartStart.resize(nParts + nRings);
1482
0
            anPartType.resize(nParts + nRings);
1483
1484
0
            for (int i = 0; i < nRings; i++)
1485
0
            {
1486
0
                anPartStart[nParts + i] = nPoints;
1487
0
                if (i == 0)
1488
0
                {
1489
0
                    poRing = poPoly->getExteriorRing();
1490
0
                    anPartType[nParts + i] = SHPP_OUTERRING;
1491
0
                }
1492
0
                else
1493
0
                {
1494
0
                    poRing = poPoly->getInteriorRing(i - 1);
1495
0
                    anPartType[nParts + i] = SHPP_INNERRING;
1496
0
                }
1497
0
                aoPoints.resize(nPoints + poRing->getNumPoints());
1498
0
                adfZ.resize(nPoints + poRing->getNumPoints());
1499
0
                for (int k = 0; k < poRing->getNumPoints(); k++)
1500
0
                {
1501
0
                    aoPoints[nPoints + k].x = poRing->getX(k);
1502
0
                    aoPoints[nPoints + k].y = poRing->getY(k);
1503
0
                    adfZ[nPoints + k] = poRing->getZ(k);
1504
0
                }
1505
0
                nPoints += poRing->getNumPoints();
1506
0
            }
1507
1508
0
            nParts += nRings;
1509
0
        }
1510
0
    }
1511
1512
0
    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
0
    return OGRERR_NONE;
1519
0
}
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
0
{
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
0
    if (dfSemiMajor == 0.0 || dfSemiMinor == 0.0)
1657
0
        return 0.0;
1658
0
    const double dfRotationRadians = dfRotationDeg * M_PI / 180.0;
1659
0
    const double dfCosRot = cos(dfRotationRadians);
1660
0
    const double dfSinRot = sin(dfRotationRadians);
1661
0
    const double dfDeltaX = dfPointOnArcX - dfCenterX;
1662
0
    const double dfDeltaY = dfPointOnArcY - dfCenterY;
1663
0
    const double dfCosA =
1664
0
        (dfCosRot * dfDeltaX - dfSinRot * dfDeltaY) / dfSemiMajor;
1665
0
    const double dfSinA =
1666
0
        (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
0
    const double dfAngle = atan2(dfSinA, dfCosA) / M_PI * 180;
1670
0
    if (dfAngle < -180)
1671
0
        return dfAngle + 360;
1672
0
    return dfAngle;
1673
0
}
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
0
{
1688
0
    auto poCC = std::make_unique<OGRCompoundCurve>();
1689
0
    int nLastPointIdx = nPartStartIdx;
1690
0
    bool bHasCircularArcs = false;
1691
0
    int i = nFirstCurveIdx;  // Used after for.
1692
0
    for (; i < nCurves; i++)
1693
0
    {
1694
0
        const int nStartPointIdx = pasCurves[i].nStartPointIdx;
1695
1696
0
        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
0
        if (nStartPointIdx >= nPartStartIdx + nPartPoints)
1705
0
        {
1706
0
            if (pnLastCurveIdx)
1707
0
                *pnLastCurveIdx = i;
1708
0
            break;
1709
0
        }
1710
1711
        // Add linear segments between end of last curve portion (or beginning
1712
        // of the part) and start of current curve.
1713
0
        if (nStartPointIdx > nLastPointIdx)
1714
0
        {
1715
0
            OGRLineString *poLine = new OGRLineString();
1716
0
            poLine->setPoints(
1717
0
                nStartPointIdx - nLastPointIdx + 1, padfX + nLastPointIdx,
1718
0
                padfY + nLastPointIdx,
1719
0
                padfZ != nullptr ? padfZ + nLastPointIdx : nullptr,
1720
0
                padfM != nullptr ? padfM + nLastPointIdx : nullptr);
1721
0
            poCC->addCurveDirectly(poLine);
1722
0
        }
1723
1724
0
        if (pasCurves[i].eType == CURVE_ARC_INTERIOR_POINT &&
1725
0
            nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1726
0
        {
1727
0
            OGRPoint p1(padfX[nStartPointIdx], padfY[nStartPointIdx],
1728
0
                        padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1729
0
                        padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1730
0
            OGRPoint p2(pasCurves[i].u.ArcByIntermediatePoint.dfX,
1731
0
                        pasCurves[i].u.ArcByIntermediatePoint.dfY,
1732
0
                        padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0);
1733
0
            OGRPoint p3(padfX[nStartPointIdx + 1], padfY[nStartPointIdx + 1],
1734
0
                        padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1735
0
                        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
0
            if (p1.getX() == p3.getX() && p1.getY() == p3.getY())
1740
0
            {
1741
0
                if (p1.getX() != p2.getX() || p1.getY() != p2.getY())
1742
0
                {
1743
0
                    bHasCircularArcs = true;
1744
0
                    OGRCircularString *poCS = new OGRCircularString();
1745
0
                    const double dfCenterX = (p1.getX() + p2.getX()) / 2;
1746
0
                    const double dfCenterY = (p1.getY() + p2.getY()) / 2;
1747
0
                    poCS->addPoint(&p1);
1748
0
                    OGRPoint pInterm1(dfCenterX - (p2.getY() - dfCenterY),
1749
0
                                      dfCenterY + (p1.getX() - dfCenterX),
1750
0
                                      padfZ != nullptr ? padfZ[nStartPointIdx]
1751
0
                                                       : 0.0);
1752
0
                    poCS->addPoint(&pInterm1);
1753
0
                    poCS->addPoint(&p2);
1754
0
                    OGRPoint pInterm2(dfCenterX + (p2.getY() - dfCenterY),
1755
0
                                      dfCenterY - (p1.getX() - dfCenterX),
1756
0
                                      padfZ != nullptr ? padfZ[nStartPointIdx]
1757
0
                                                       : 0.0);
1758
0
                    poCS->addPoint(&pInterm2);
1759
0
                    poCS->addPoint(&p3);
1760
0
                    poCS->set3D(padfZ != nullptr);
1761
0
                    poCS->setMeasured(padfM != nullptr);
1762
0
                    poCC->addCurveDirectly(poCS);
1763
0
                }
1764
0
            }
1765
0
            else
1766
0
            {
1767
0
                bHasCircularArcs = true;
1768
0
                OGRCircularString *poCS = new OGRCircularString();
1769
0
                poCS->addPoint(&p1);
1770
0
                poCS->addPoint(&p2);
1771
0
                poCS->addPoint(&p3);
1772
0
                poCS->set3D(padfZ != nullptr);
1773
0
                poCS->setMeasured(padfM != nullptr);
1774
0
                if (poCC->addCurveDirectly(poCS) != OGRERR_NONE)
1775
0
                {
1776
0
                    delete poCS;
1777
0
                    return nullptr;
1778
0
                }
1779
0
            }
1780
0
        }
1781
1782
0
        else if (pasCurves[i].eType == CURVE_ARC_CENTER_POINT &&
1783
0
                 nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1784
0
        {
1785
0
            const double dfCenterX = pasCurves[i].u.ArcByCenterPoint.dfX;
1786
0
            const double dfCenterY = pasCurves[i].u.ArcByCenterPoint.dfY;
1787
0
            double dfDeltaY = padfY[nStartPointIdx] - dfCenterY;
1788
0
            double dfDeltaX = padfX[nStartPointIdx] - dfCenterX;
1789
0
            double dfAngleStart = atan2(dfDeltaY, dfDeltaX);
1790
0
            dfDeltaY = padfY[nStartPointIdx + 1] - dfCenterY;
1791
0
            dfDeltaX = padfX[nStartPointIdx + 1] - dfCenterX;
1792
0
            double dfAngleEnd = atan2(dfDeltaY, dfDeltaX);
1793
            // Note: This definition from center and 2 points may be
1794
            // not a circle.
1795
0
            double dfRadius = sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
1796
0
            if (pasCurves[i].u.ArcByCenterPoint.bIsCCW)
1797
0
            {
1798
0
                if (dfAngleStart >= dfAngleEnd)
1799
0
                    dfAngleEnd += 2 * M_PI;
1800
0
            }
1801
0
            else
1802
0
            {
1803
0
                if (dfAngleStart <= dfAngleEnd)
1804
0
                    dfAngleEnd -= 2 * M_PI;
1805
0
            }
1806
0
            const double dfMidAngle = (dfAngleStart + dfAngleEnd) / 2;
1807
0
            OGRPoint p1(padfX[nStartPointIdx], padfY[nStartPointIdx],
1808
0
                        padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1809
0
                        padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1810
0
            OGRPoint p2(dfCenterX + dfRadius * cos(dfMidAngle),
1811
0
                        dfCenterY + dfRadius * sin(dfMidAngle),
1812
0
                        padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0);
1813
0
            OGRPoint p3(padfX[nStartPointIdx + 1], padfY[nStartPointIdx + 1],
1814
0
                        padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1815
0
                        padfM != nullptr ? padfM[nStartPointIdx + 1] : 0.0);
1816
1817
0
            bHasCircularArcs = true;
1818
0
            OGRCircularString *poCS = new OGRCircularString();
1819
0
            poCS->addPoint(&p1);
1820
0
            poCS->addPoint(&p2);
1821
0
            poCS->addPoint(&p3);
1822
0
            poCS->set3D(padfZ != nullptr);
1823
0
            poCS->setMeasured(padfM != nullptr);
1824
0
            poCC->addCurveDirectly(poCS);
1825
0
        }
1826
1827
0
        else if (pasCurves[i].eType == CURVE_BEZIER &&
1828
0
                 nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1829
0
        {
1830
0
            OGRLineString *poLine = new OGRLineString();
1831
0
            const double dfX0 = padfX[nStartPointIdx];
1832
0
            const double dfY0 = padfY[nStartPointIdx];
1833
0
            const double dfX1 = pasCurves[i].u.Bezier.dfX1;
1834
0
            const double dfY1 = pasCurves[i].u.Bezier.dfY1;
1835
0
            const double dfX2 = pasCurves[i].u.Bezier.dfX2;
1836
0
            const double dfY2 = pasCurves[i].u.Bezier.dfY2;
1837
0
            const double dfX3 = padfX[nStartPointIdx + 1];
1838
0
            const double dfY3 = padfY[nStartPointIdx + 1];
1839
0
            double dfStartAngle = atan2(dfY1 - dfY0, dfX1 - dfX0);
1840
0
            double dfEndAngle = atan2(dfY3 - dfY2, dfX3 - dfX2);
1841
0
            if (dfStartAngle + M_PI < dfEndAngle)
1842
0
                dfStartAngle += 2 * M_PI;
1843
0
            else if (dfEndAngle + M_PI < dfStartAngle)
1844
0
                dfEndAngle += 2 * M_PI;
1845
0
            const double dfStepSizeRad =
1846
0
                OGRGeometryFactory::GetDefaultArcStepSize() / 180.0 * M_PI;
1847
0
            const double dfLengthTangentStart =
1848
0
                (dfX1 - dfX0) * (dfX1 - dfX0) + (dfY1 - dfY0) * (dfY1 - dfY0);
1849
0
            const double dfLengthTangentEnd =
1850
0
                (dfX3 - dfX2) * (dfX3 - dfX2) + (dfY3 - dfY2) * (dfY3 - dfY2);
1851
0
            const double dfLength =
1852
0
                (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
0
            const int nSteps =
1858
0
                (dfLength < 1e-9)
1859
0
                    ? 1
1860
0
                    : static_cast<int>(std::min(
1861
0
                          1000.0,
1862
0
                          ceil(std::max(2.0, fabs(dfEndAngle - dfStartAngle) /
1863
0
                                                 dfStepSizeRad) *
1864
0
                               std::max(1.0, 5.0 *
1865
0
                                                 (dfLengthTangentStart +
1866
0
                                                  dfLengthTangentEnd) /
1867
0
                                                 dfLength))));
1868
0
            poLine->setNumPoints(nSteps + 1);
1869
0
            poLine->setPoint(0, dfX0, dfY0,
1870
0
                             padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1871
0
                             padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1872
0
            for (int j = 1; j < nSteps; j++)
1873
0
            {
1874
0
                const double t = static_cast<double>(j) / nSteps;
1875
                // Third-order Bezier interpolation.
1876
0
                poLine->setPoint(
1877
0
                    j,
1878
0
                    (1 - t) * (1 - t) * (1 - t) * dfX0 +
1879
0
                        3 * (1 - t) * (1 - t) * t * dfX1 +
1880
0
                        3 * (1 - t) * t * t * dfX2 + t * t * t * dfX3,
1881
0
                    (1 - t) * (1 - t) * (1 - t) * dfY0 +
1882
0
                        3 * (1 - t) * (1 - t) * t * dfY1 +
1883
0
                        3 * (1 - t) * t * t * dfY2 + t * t * t * dfY3);
1884
0
            }
1885
0
            poLine->setPoint(nSteps, dfX3, dfY3,
1886
0
                             padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1887
0
                             padfM != nullptr ? padfM[nStartPointIdx + 1]
1888
0
                                              : 0.0);
1889
0
            poLine->set3D(padfZ != nullptr);
1890
0
            poLine->setMeasured(padfM != nullptr);
1891
0
            if (poCC->addCurveDirectly(poLine) != OGRERR_NONE)
1892
0
            {
1893
0
                delete poLine;
1894
0
                return nullptr;
1895
0
            }
1896
0
        }
1897
1898
0
        else if (pasCurves[i].eType == CURVE_ELLIPSE_BY_CENTER &&
1899
0
                 nStartPointIdx + 1 < nPartStartIdx + nPartPoints)
1900
0
        {
1901
0
            const double dfSemiMinor =
1902
0
                pasCurves[i].u.EllipseByCenter.dfSemiMajor *
1903
0
                pasCurves[i].u.EllipseByCenter.dfRatioSemiMinor;
1904
            // Different sign conventions between ext shape
1905
            // (trigonometric, CCW) and approximateArcAngles (CW).
1906
0
            const double dfRotationDeg =
1907
0
                -pasCurves[i].u.EllipseByCenter.dfRotationDeg;
1908
0
            const double dfAngleStart = GetAngleOnEllipse(
1909
0
                padfX[nStartPointIdx], padfY[nStartPointIdx],
1910
0
                pasCurves[i].u.EllipseByCenter.dfX,
1911
0
                pasCurves[i].u.EllipseByCenter.dfY, dfRotationDeg,
1912
0
                pasCurves[i].u.EllipseByCenter.dfSemiMajor, dfSemiMinor);
1913
0
            const double dfAngleEnd = GetAngleOnEllipse(
1914
0
                padfX[nStartPointIdx + 1], padfY[nStartPointIdx + 1],
1915
0
                pasCurves[i].u.EllipseByCenter.dfX,
1916
0
                pasCurves[i].u.EllipseByCenter.dfY, dfRotationDeg,
1917
0
                pasCurves[i].u.EllipseByCenter.dfSemiMajor, dfSemiMinor);
1918
            // CPLDebug("OGR", "Start angle=%f, End angle=%f",
1919
            //          dfAngleStart, dfAngleEnd);
1920
            // Approximatearcangles() use CW.
1921
0
            double dfAngleStartForApprox = -dfAngleStart;
1922
0
            double dfAngleEndForApprox = -dfAngleEnd;
1923
0
            if (pasCurves[i].u.EllipseByCenter.bIsComplete)
1924
0
            {
1925
0
                dfAngleEndForApprox = dfAngleStartForApprox + 360;
1926
0
            }
1927
0
            else if (pasCurves[i].u.EllipseByCenter.bIsMinor)
1928
0
            {
1929
0
                if (dfAngleEndForApprox > dfAngleStartForApprox + 180)
1930
0
                {
1931
0
                    dfAngleEndForApprox -= 360;
1932
0
                }
1933
0
                else if (dfAngleEndForApprox < dfAngleStartForApprox - 180)
1934
0
                {
1935
0
                    dfAngleEndForApprox += 360;
1936
0
                }
1937
0
            }
1938
0
            else
1939
0
            {
1940
0
                if (dfAngleEndForApprox > dfAngleStartForApprox &&
1941
0
                    dfAngleEndForApprox < dfAngleStartForApprox + 180)
1942
0
                {
1943
0
                    dfAngleEndForApprox -= 360;
1944
0
                }
1945
0
                else if (dfAngleEndForApprox < dfAngleStartForApprox &&
1946
0
                         dfAngleEndForApprox > dfAngleStartForApprox - 180)
1947
0
                {
1948
0
                    dfAngleEndForApprox += 360;
1949
0
                }
1950
0
            }
1951
0
            OGRLineString *poLine =
1952
0
                OGRGeometryFactory::approximateArcAngles(
1953
0
                    pasCurves[i].u.EllipseByCenter.dfX,
1954
0
                    pasCurves[i].u.EllipseByCenter.dfY,
1955
0
                    padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1956
0
                    pasCurves[i].u.EllipseByCenter.dfSemiMajor, dfSemiMinor,
1957
0
                    dfRotationDeg, dfAngleStartForApprox, dfAngleEndForApprox,
1958
0
                    0)
1959
0
                    ->toLineString();
1960
0
            if (poLine->getNumPoints() >= 2)
1961
0
            {
1962
0
                poLine->setPoint(
1963
0
                    0, padfX[nStartPointIdx], padfY[nStartPointIdx],
1964
0
                    padfZ != nullptr ? padfZ[nStartPointIdx] : 0.0,
1965
0
                    padfM != nullptr ? padfM[nStartPointIdx] : 0.0);
1966
0
                poLine->setPoint(
1967
0
                    poLine->getNumPoints() - 1, padfX[nStartPointIdx + 1],
1968
0
                    padfY[nStartPointIdx + 1],
1969
0
                    padfZ != nullptr ? padfZ[nStartPointIdx + 1] : 0.0,
1970
0
                    padfM != nullptr ? padfM[nStartPointIdx + 1] : 0.0);
1971
0
            }
1972
0
            poLine->set3D(padfZ != nullptr);
1973
0
            poLine->setMeasured(padfM != nullptr);
1974
0
            poCC->addCurveDirectly(poLine);
1975
0
        }
1976
1977
        // Should not happen normally.
1978
0
        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
0
        nLastPointIdx = nStartPointIdx + 1;
1988
0
    }
1989
0
    if (i == nCurves)
1990
0
    {
1991
0
        if (pnLastCurveIdx)
1992
0
            *pnLastCurveIdx = i;
1993
0
    }
1994
1995
    // Add terminating linear segments
1996
0
    if (nLastPointIdx < nPartStartIdx + nPartPoints - 1)
1997
0
    {
1998
0
        OGRLineString *poLine = new OGRLineString();
1999
0
        poLine->setPoints(nPartStartIdx + nPartPoints - 1 - nLastPointIdx + 1,
2000
0
                          padfX + nLastPointIdx, padfY + nLastPointIdx,
2001
0
                          padfZ != nullptr ? padfZ + nLastPointIdx : nullptr,
2002
0
                          padfM != nullptr ? padfM + nLastPointIdx : nullptr);
2003
0
        if (poCC->addCurveDirectly(poLine) != OGRERR_NONE)
2004
0
        {
2005
0
            delete poLine;
2006
0
            return nullptr;
2007
0
        }
2008
0
    }
2009
2010
0
    if (!bHasCircularArcs)
2011
0
    {
2012
0
        OGRwkbGeometryType eLSType = wkbLineString;
2013
0
        if (OGR_GT_HasZ(poCC->getGeometryType()))
2014
0
            eLSType = OGR_GT_SetZ(eLSType);
2015
0
        if (OGR_GT_HasM(poCC->getGeometryType()))
2016
0
            eLSType = OGR_GT_SetM(eLSType);
2017
0
        return reinterpret_cast<OGRCurve *>(OGR_G_ForceTo(
2018
0
            OGRGeometry::ToHandle(poCC.release()), eLSType, nullptr));
2019
0
    }
2020
0
    else
2021
0
        return poCC.release();
2022
0
}
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
0
{
2035
0
    *ppoGeom = nullptr;
2036
2037
0
    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
0
    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
0
    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
0
    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
0
    const bool bIsExtended =
2108
0
        nSHPType >= SHPT_GENERALPOLYLINE && nSHPType <= SHPT_GENERALMULTIPATCH;
2109
2110
0
    const bool bHasZ =
2111
0
        (nSHPType == SHPT_POINTZ || nSHPType == SHPT_POINTZM ||
2112
0
         nSHPType == SHPT_MULTIPOINTZ || nSHPType == SHPT_MULTIPOINTZM ||
2113
0
         nSHPType == SHPT_POLYGONZ || nSHPType == SHPT_POLYGONZM ||
2114
0
         nSHPType == SHPT_ARCZ || nSHPType == SHPT_ARCZM ||
2115
0
         nSHPType == SHPT_MULTIPATCH || nSHPType == SHPT_MULTIPATCHM ||
2116
0
         (bIsExtended && (pabyShape[3] & 0x80) != 0));
2117
2118
0
    const bool bHasM =
2119
0
        (nSHPType == SHPT_POINTM || nSHPType == SHPT_POINTZM ||
2120
0
         nSHPType == SHPT_MULTIPOINTM || nSHPType == SHPT_MULTIPOINTZM ||
2121
0
         nSHPType == SHPT_POLYGONM || nSHPType == SHPT_POLYGONZM ||
2122
0
         nSHPType == SHPT_ARCM || nSHPType == SHPT_ARCZM ||
2123
0
         nSHPType == SHPT_MULTIPATCHM ||
2124
0
         (bIsExtended && (pabyShape[3] & 0x40) != 0));
2125
2126
0
    const bool bHasCurves = (bIsExtended && (pabyShape[3] & 0x20) != 0);
2127
2128
0
    switch (nSHPType)
2129
0
    {
2130
0
        case SHPT_GENERALPOLYLINE:
2131
0
            nSHPType = SHPT_ARC;
2132
0
            break;
2133
0
        case SHPT_GENERALPOLYGON:
2134
0
            nSHPType = SHPT_POLYGON;
2135
0
            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
0
    }
2145
2146
    /* ==================================================================== */
2147
    /*     Extract vertices for a Polygon or Arc.                           */
2148
    /* ==================================================================== */
2149
0
    if (nSHPType == SHPT_ARC || nSHPType == SHPT_ARCZ ||
2150
0
        nSHPType == SHPT_ARCM || nSHPType == SHPT_ARCZM ||
2151
0
        nSHPType == SHPT_POLYGON || nSHPType == SHPT_POLYGONZ ||
2152
0
        nSHPType == SHPT_POLYGONM || nSHPType == SHPT_POLYGONZM ||
2153
0
        nSHPType == SHPT_MULTIPATCH || nSHPType == SHPT_MULTIPATCHM)
2154
0
    {
2155
0
        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
0
        GInt32 nPoints = 0;
2170
0
        memcpy(&nPoints, pabyShape + 40, 4);
2171
0
        GInt32 nParts = 0;
2172
0
        memcpy(&nParts, pabyShape + 36, 4);
2173
2174
0
        CPL_LSBPTR32(&nPoints);
2175
0
        CPL_LSBPTR32(&nParts);
2176
2177
0
        if (nPoints < 0 || nParts < 0 || nPoints > 50 * 1000 * 1000 ||
2178
0
            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
0
        const bool bIsMultiPatch =
2187
0
            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
0
        int nRequiredSize = 44 + 4 * nParts + 16 * nPoints;
2193
0
        if (bHasZ)
2194
0
        {
2195
0
            nRequiredSize += 16 + 8 * nPoints;
2196
0
        }
2197
0
        if (bHasM)
2198
0
        {
2199
0
            nRequiredSize += 16 + 8 * nPoints;
2200
0
        }
2201
0
        if (bIsMultiPatch)
2202
0
        {
2203
0
            nRequiredSize += 4 * nParts;
2204
0
        }
2205
0
        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
0
        GInt32 *panPartStart =
2215
0
            static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(nParts, sizeof(GInt32)));
2216
0
        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
0
        memcpy(panPartStart, pabyShape + 44, 4 * nParts);
2227
0
        for (int i = 0; i < nParts; i++)
2228
0
        {
2229
0
            CPL_LSBPTR32(panPartStart + i);
2230
2231
            // Check that the offset is inside the vertex array.
2232
0
            if (panPartStart[i] < 0 || panPartStart[i] >= nPoints)
2233
0
            {
2234
0
                CPLError(
2235
0
                    CE_Failure, CPLE_AppDefined,
2236
0
                    "Corrupted Shape : panPartStart[%d] = %d, nPoints = %d", i,
2237
0
                    panPartStart[i], nPoints);
2238
0
                CPLFree(panPartStart);
2239
0
                return OGRERR_FAILURE;
2240
0
            }
2241
0
            if (i > 0 && panPartStart[i] <= panPartStart[i - 1])
2242
0
            {
2243
0
                CPLError(CE_Failure, CPLE_AppDefined,
2244
0
                         "Corrupted Shape : panPartStart[%d] = %d, "
2245
0
                         "panPartStart[%d] = %d",
2246
0
                         i, panPartStart[i], i - 1, panPartStart[i - 1]);
2247
0
                CPLFree(panPartStart);
2248
0
                return OGRERR_FAILURE;
2249
0
            }
2250
0
        }
2251
2252
0
        int nOffset = 44 + 4 * nParts;
2253
2254
        /* --------------------------------------------------------------------
2255
         */
2256
        /*      If this is a multipatch, we will also have parts types. */
2257
        /* --------------------------------------------------------------------
2258
         */
2259
0
        GInt32 *panPartType = nullptr;
2260
2261
0
        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
0
        double *padfX =
2285
0
            static_cast<double *>(VSI_MALLOC_VERBOSE(sizeof(double) * nPoints));
2286
0
        double *padfY =
2287
0
            static_cast<double *>(VSI_MALLOC_VERBOSE(sizeof(double) * nPoints));
2288
0
        double *padfZ =
2289
0
            static_cast<double *>(VSI_CALLOC_VERBOSE(sizeof(double), nPoints));
2290
0
        double *padfM = static_cast<double *>(
2291
0
            bHasM ? VSI_CALLOC_VERBOSE(sizeof(double), nPoints) : nullptr);
2292
2293
0
        if (nPoints != 0 && (padfX == nullptr || padfY == nullptr ||
2294
0
                             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
0
        for (int i = 0; i < nPoints; i++)
2306
0
        {
2307
0
            memcpy(padfX + i, pabyShape + nOffset + i * 16, 8);
2308
0
            memcpy(padfY + i, pabyShape + nOffset + i * 16 + 8, 8);
2309
0
            CPL_LSBPTR64(padfX + i);
2310
0
            CPL_LSBPTR64(padfY + i);
2311
0
        }
2312
2313
0
        nOffset += 16 * nPoints;
2314
2315
        /* --------------------------------------------------------------------
2316
         */
2317
        /*      If we have a Z coordinate, collect that now. */
2318
        /* --------------------------------------------------------------------
2319
         */
2320
0
        if (bHasZ)
2321
0
        {
2322
0
            for (int i = 0; i < nPoints; i++)
2323
0
            {
2324
0
                memcpy(padfZ + i, pabyShape + nOffset + 16 + i * 8, 8);
2325
0
                CPL_LSBPTR64(padfZ + i);
2326
0
            }
2327
2328
0
            nOffset += 16 + 8 * nPoints;
2329
0
        }
2330
2331
        /* --------------------------------------------------------------------
2332
         */
2333
        /*      If we have a M coordinate, collect that now. */
2334
        /* --------------------------------------------------------------------
2335
         */
2336
0
        if (bHasM)
2337
0
        {
2338
0
            bool bIsAllNAN = nPoints > 0;
2339
0
            for (int i = 0; i < nPoints; i++)
2340
0
            {
2341
0
                memcpy(padfM + i, pabyShape + nOffset + 16 + i * 8, 8);
2342
0
                CPL_LSBPTR64(padfM + i);
2343
0
                bIsAllNAN &= std::isnan(padfM[i]);
2344
0
            }
2345
0
            if (bIsAllNAN)
2346
0
            {
2347
0
                CPLFree(padfM);
2348
0
                padfM = nullptr;
2349
0
            }
2350
2351
0
            nOffset += 16 + 8 * nPoints;
2352
0
        }
2353
2354
        /* --------------------------------------------------------------------
2355
         */
2356
        /*      If we have curves, collect them now. */
2357
        /* --------------------------------------------------------------------
2358
         */
2359
0
        int nCurves = 0;
2360
0
        CurveSegment *pasCurves = nullptr;
2361
0
        if (bHasCurves && nOffset + 4 <= nBytes)
2362
0
        {
2363
0
            memcpy(&nCurves, pabyShape + nOffset, 4);
2364
0
            CPL_LSBPTR32(&nCurves);
2365
0
            nOffset += 4;
2366
#ifdef DEBUG_VERBOSE
2367
            CPLDebug("OGR", "nCurves = %d", nCurves);
2368
#endif
2369
0
            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
0
            pasCurves = static_cast<CurveSegment *>(
2375
0
                VSI_MALLOC2_VERBOSE(sizeof(CurveSegment), nCurves));
2376
0
            if (pasCurves == nullptr)
2377
0
            {
2378
0
                nCurves = 0;
2379
0
            }
2380
0
            int iCurve = 0;
2381
0
            for (int i = 0; i < nCurves; i++)
2382
0
            {
2383
0
                if (nOffset + 8 > nBytes)
2384
0
                {
2385
0
                    CPLDebug("OGR", "Not enough bytes");
2386
0
                    break;
2387
0
                }
2388
0
                int nStartPointIdx = 0;
2389
0
                memcpy(&nStartPointIdx, pabyShape + nOffset, 4);
2390
0
                CPL_LSBPTR32(&nStartPointIdx);
2391
0
                nOffset += 4;
2392
0
                int nSegmentType = 0;
2393
0
                memcpy(&nSegmentType, pabyShape + nOffset, 4);
2394
0
                CPL_LSBPTR32(&nSegmentType);
2395
0
                nOffset += 4;
2396
#ifdef DEBUG_VERBOSE
2397
                CPLDebug("OGR", "[%d] nStartPointIdx = %d, segmentType = %d", i,
2398
                         nSegmentType, nSegmentType);
2399
#endif
2400
0
                if (nStartPointIdx < 0 || nStartPointIdx >= nPoints ||
2401
0
                    (iCurve > 0 &&
2402
0
                     nStartPointIdx <= pasCurves[iCurve - 1].nStartPointIdx))
2403
0
                {
2404
0
                    CPLDebug("OGR", "Invalid nStartPointIdx = %d",
2405
0
                             nStartPointIdx);
2406
0
                    break;
2407
0
                }
2408
0
                pasCurves[iCurve].nStartPointIdx = nStartPointIdx;
2409
0
                if (nSegmentType == EXT_SHAPE_SEGMENT_ARC)
2410
0
                {
2411
0
                    if (nOffset + 20 > nBytes)
2412
0
                    {
2413
0
                        CPLDebug("OGR", "Not enough bytes");
2414
0
                        break;
2415
0
                    }
2416
0
                    double dfVal1 = 0.0;
2417
0
                    double dfVal2 = 0.0;
2418
0
                    memcpy(&dfVal1, pabyShape + nOffset + 0, 8);
2419
0
                    CPL_LSBPTR64(&dfVal1);
2420
0
                    memcpy(&dfVal2, pabyShape + nOffset + 8, 8);
2421
0
                    CPL_LSBPTR64(&dfVal2);
2422
0
                    int nBits = 0;
2423
0
                    memcpy(&nBits, pabyShape + nOffset + 16, 4);
2424
0
                    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
0
                    if ((nBits & EXT_SHAPE_ARC_IP) != 0 &&
2444
0
                        (nBits & EXT_SHAPE_ARC_LINE) == 0)
2445
0
                    {
2446
0
                        pasCurves[iCurve].eType = CURVE_ARC_INTERIOR_POINT;
2447
0
                        pasCurves[iCurve].u.ArcByIntermediatePoint.dfX = dfVal1;
2448
0
                        pasCurves[iCurve].u.ArcByIntermediatePoint.dfY = dfVal2;
2449
0
                        iCurve++;
2450
0
                    }
2451
0
                    else if ((nBits & EXT_SHAPE_ARC_EMPTY) == 0 &&
2452
0
                             (nBits & EXT_SHAPE_ARC_LINE) == 0 &&
2453
0
                             (nBits & EXT_SHAPE_ARC_POINT) == 0)
2454
0
                    {
2455
                        // This is the old deprecated way
2456
0
                        pasCurves[iCurve].eType = CURVE_ARC_CENTER_POINT;
2457
0
                        pasCurves[iCurve].u.ArcByCenterPoint.dfX = dfVal1;
2458
0
                        pasCurves[iCurve].u.ArcByCenterPoint.dfY = dfVal2;
2459
0
                        pasCurves[iCurve].u.ArcByCenterPoint.bIsCCW =
2460
0
                            (nBits & EXT_SHAPE_ARC_CCW) != 0;
2461
0
                        iCurve++;
2462
0
                    }
2463
0
                    nOffset += 16 + 4;
2464
0
                }
2465
0
                else if (nSegmentType == EXT_SHAPE_SEGMENT_BEZIER)
2466
0
                {
2467
0
                    if (nOffset + 32 > nBytes)
2468
0
                    {
2469
0
                        CPLDebug("OGR", "Not enough bytes");
2470
0
                        break;
2471
0
                    }
2472
0
                    double dfX1 = 0.0;
2473
0
                    double dfY1 = 0.0;
2474
0
                    memcpy(&dfX1, pabyShape + nOffset + 0, 8);
2475
0
                    CPL_LSBPTR64(&dfX1);
2476
0
                    memcpy(&dfY1, pabyShape + nOffset + 8, 8);
2477
0
                    CPL_LSBPTR64(&dfY1);
2478
0
                    double dfX2 = 0.0;
2479
0
                    double dfY2 = 0.0;
2480
0
                    memcpy(&dfX2, pabyShape + nOffset + 16, 8);
2481
0
                    CPL_LSBPTR64(&dfX2);
2482
0
                    memcpy(&dfY2, pabyShape + nOffset + 24, 8);
2483
0
                    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
0
                    pasCurves[iCurve].eType = CURVE_BEZIER;
2490
0
                    pasCurves[iCurve].u.Bezier.dfX1 = dfX1;
2491
0
                    pasCurves[iCurve].u.Bezier.dfY1 = dfY1;
2492
0
                    pasCurves[iCurve].u.Bezier.dfX2 = dfX2;
2493
0
                    pasCurves[iCurve].u.Bezier.dfY2 = dfY2;
2494
0
                    iCurve++;
2495
0
                    nOffset += 32;
2496
0
                }
2497
0
                else if (nSegmentType == EXT_SHAPE_SEGMENT_ELLIPSE)
2498
0
                {
2499
0
                    if (nOffset + 44 > nBytes)
2500
0
                    {
2501
0
                        CPLDebug("OGR", "Not enough bytes");
2502
0
                        break;
2503
0
                    }
2504
0
                    double dfVS0 = 0.0;
2505
0
                    memcpy(&dfVS0, pabyShape + nOffset, 8);
2506
0
                    nOffset += 8;
2507
0
                    CPL_LSBPTR64(&dfVS0);
2508
2509
0
                    double dfVS1 = 0.0;
2510
0
                    memcpy(&dfVS1, pabyShape + nOffset, 8);
2511
0
                    nOffset += 8;
2512
0
                    CPL_LSBPTR64(&dfVS1);
2513
2514
0
                    double dfRotationOrFromV = 0.0;
2515
0
                    memcpy(&dfRotationOrFromV, pabyShape + nOffset, 8);
2516
0
                    nOffset += 8;
2517
0
                    CPL_LSBPTR64(&dfRotationOrFromV);
2518
2519
0
                    double dfSemiMajor = 0.0;
2520
0
                    memcpy(&dfSemiMajor, pabyShape + nOffset, 8);
2521
0
                    nOffset += 8;
2522
0
                    CPL_LSBPTR64(&dfSemiMajor);
2523
2524
0
                    double dfMinorMajorRatioOrDeltaV = 0.0;
2525
0
                    memcpy(&dfMinorMajorRatioOrDeltaV, pabyShape + nOffset, 8);
2526
0
                    nOffset += 8;
2527
0
                    CPL_LSBPTR64(&dfMinorMajorRatioOrDeltaV);
2528
2529
0
                    int nBits = 0;
2530
0
                    memcpy(&nBits, pabyShape + nOffset, 4);
2531
0
                    CPL_LSBPTR32(&nBits);
2532
0
                    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
0
                    if ((nBits & EXT_SHAPE_ELLIPSE_CENTER_TO) == 0 &&
2566
0
                        (nBits & EXT_SHAPE_ELLIPSE_CENTER_FROM) == 0)
2567
0
                    {
2568
0
                        pasCurves[iCurve].eType = CURVE_ELLIPSE_BY_CENTER;
2569
0
                        pasCurves[iCurve].u.EllipseByCenter.dfX = dfVS0;
2570
0
                        pasCurves[iCurve].u.EllipseByCenter.dfY = dfVS1;
2571
0
                        pasCurves[iCurve].u.EllipseByCenter.dfRotationDeg =
2572
0
                            dfRotationOrFromV / M_PI * 180;
2573
0
                        pasCurves[iCurve].u.EllipseByCenter.dfSemiMajor =
2574
0
                            dfSemiMajor;
2575
0
                        pasCurves[iCurve].u.EllipseByCenter.dfRatioSemiMinor =
2576
0
                            dfMinorMajorRatioOrDeltaV;
2577
0
                        pasCurves[iCurve].u.EllipseByCenter.bIsMinor =
2578
0
                            ((nBits & EXT_SHAPE_ELLIPSE_MINOR) != 0);
2579
0
                        pasCurves[iCurve].u.EllipseByCenter.bIsComplete =
2580
0
                            ((nBits & EXT_SHAPE_ELLIPSE_COMPLETE) != 0);
2581
0
                        iCurve++;
2582
0
                    }
2583
0
                }
2584
0
                else
2585
0
                {
2586
0
                    CPLDebug("OGR", "unhandled segmentType = %d", nSegmentType);
2587
0
                }
2588
0
            }
2589
2590
0
            nCurves = iCurve;
2591
0
        }
2592
2593
        /* --------------------------------------------------------------------
2594
         */
2595
        /*      Build corresponding OGR objects. */
2596
        /* --------------------------------------------------------------------
2597
         */
2598
0
        if (nSHPType == SHPT_ARC || nSHPType == SHPT_ARCZ ||
2599
0
            nSHPType == SHPT_ARCM || nSHPType == SHPT_ARCZM)
2600
0
        {
2601
            /* --------------------------------------------------------------------
2602
             */
2603
            /*      Arc - As LineString */
2604
            /* --------------------------------------------------------------------
2605
             */
2606
0
            if (nParts == 1)
2607
0
            {
2608
0
                if (nCurves > 0)
2609
0
                {
2610
0
                    *ppoGeom = OGRShapeCreateCompoundCurve(
2611
0
                        0, nPoints, pasCurves, nCurves, 0, padfX, padfY,
2612
0
                        bHasZ ? padfZ : nullptr, padfM, nullptr);
2613
0
                }
2614
0
                else
2615
0
                {
2616
0
                    OGRLineString *poLine = new OGRLineString();
2617
0
                    *ppoGeom = poLine;
2618
2619
0
                    poLine->setPoints(nPoints, padfX, padfY, padfZ, padfM);
2620
0
                }
2621
0
            }
2622
2623
            /* --------------------------------------------------------------------
2624
             */
2625
            /*      Arc - As MultiLineString */
2626
            /* --------------------------------------------------------------------
2627
             */
2628
0
            else
2629
0
            {
2630
0
                if (nCurves > 0)
2631
0
                {
2632
0
                    OGRMultiCurve *poMulti = new OGRMultiCurve;
2633
0
                    *ppoGeom = poMulti;
2634
2635
0
                    int iCurveIdx = 0;
2636
0
                    for (int i = 0; i < nParts; i++)
2637
0
                    {
2638
0
                        const int nVerticesInThisPart =
2639
0
                            i == nParts - 1
2640
0
                                ? nPoints - panPartStart[i]
2641
0
                                : panPartStart[i + 1] - panPartStart[i];
2642
2643
0
                        OGRCurve *poCurve = OGRShapeCreateCompoundCurve(
2644
0
                            panPartStart[i], nVerticesInThisPart, pasCurves,
2645
0
                            nCurves, iCurveIdx, padfX, padfY,
2646
0
                            bHasZ ? padfZ : nullptr, padfM, &iCurveIdx);
2647
0
                        if (poCurve == nullptr || poMulti->addGeometryDirectly(
2648
0
                                                      poCurve) != OGRERR_NONE)
2649
0
                        {
2650
0
                            delete poCurve;
2651
0
                            delete poMulti;
2652
0
                            *ppoGeom = nullptr;
2653
0
                            break;
2654
0
                        }
2655
0
                    }
2656
0
                }
2657
0
                else
2658
0
                {
2659
0
                    OGRMultiLineString *poMulti = new OGRMultiLineString;
2660
0
                    *ppoGeom = poMulti;
2661
2662
0
                    for (int i = 0; i < nParts; i++)
2663
0
                    {
2664
0
                        OGRLineString *poLine = new OGRLineString;
2665
0
                        const int nVerticesInThisPart =
2666
0
                            i == nParts - 1
2667
0
                                ? nPoints - panPartStart[i]
2668
0
                                : panPartStart[i + 1] - panPartStart[i];
2669
2670
0
                        poLine->setPoints(
2671
0
                            nVerticesInThisPart, padfX + panPartStart[i],
2672
0
                            padfY + panPartStart[i], padfZ + panPartStart[i],
2673
0
                            padfM != nullptr ? padfM + panPartStart[i]
2674
0
                                             : nullptr);
2675
2676
0
                        poMulti->addGeometryDirectly(poLine);
2677
0
                    }
2678
0
                }
2679
0
            }
2680
0
        }  // ARC.
2681
2682
        /* --------------------------------------------------------------------
2683
         */
2684
        /*      Polygon */
2685
        /* --------------------------------------------------------------------
2686
         */
2687
0
        else if (nSHPType == SHPT_POLYGON || nSHPType == SHPT_POLYGONZ ||
2688
0
                 nSHPType == SHPT_POLYGONM || nSHPType == SHPT_POLYGONZM)
2689
0
        {
2690
0
            if (nCurves > 0 && nParts != 0)
2691
0
            {
2692
0
                if (nParts == 1)
2693
0
                {
2694
0
                    OGRCurvePolygon *poOGRPoly = new OGRCurvePolygon;
2695
0
                    *ppoGeom = poOGRPoly;
2696
0
                    const int nVerticesInThisPart = nPoints - panPartStart[0];
2697
2698
0
                    OGRCurve *poRing = OGRShapeCreateCompoundCurve(
2699
0
                        panPartStart[0], nVerticesInThisPart, pasCurves,
2700
0
                        nCurves, 0, padfX, padfY, bHasZ ? padfZ : nullptr,
2701
0
                        padfM, nullptr);
2702
0
                    if (poRing == nullptr ||
2703
0
                        poOGRPoly->addRingDirectly(poRing) != OGRERR_NONE)
2704
0
                    {
2705
0
                        delete poRing;
2706
0
                        delete poOGRPoly;
2707
0
                        *ppoGeom = nullptr;
2708
0
                    }
2709
0
                }
2710
0
                else
2711
0
                {
2712
0
                    OGRGeometry *poOGR = nullptr;
2713
0
                    OGRCurvePolygon **tabPolygons =
2714
0
                        new OGRCurvePolygon *[nParts];
2715
2716
0
                    int iCurveIdx = 0;
2717
0
                    for (int i = 0; i < nParts; i++)
2718
0
                    {
2719
0
                        tabPolygons[i] = new OGRCurvePolygon();
2720
0
                        const int nVerticesInThisPart =
2721
0
                            i == nParts - 1
2722
0
                                ? nPoints - panPartStart[i]
2723
0
                                : panPartStart[i + 1] - panPartStart[i];
2724
2725
0
                        OGRCurve *poRing = OGRShapeCreateCompoundCurve(
2726
0
                            panPartStart[i], nVerticesInThisPart, pasCurves,
2727
0
                            nCurves, iCurveIdx, padfX, padfY,
2728
0
                            bHasZ ? padfZ : nullptr, padfM, &iCurveIdx);
2729
0
                        if (poRing == nullptr ||
2730
0
                            tabPolygons[i]->addRingDirectly(poRing) !=
2731
0
                                OGRERR_NONE)
2732
0
                        {
2733
0
                            delete poRing;
2734
0
                            for (; i >= 0; --i)
2735
0
                                delete tabPolygons[i];
2736
0
                            delete[] tabPolygons;
2737
0
                            tabPolygons = nullptr;
2738
0
                            *ppoGeom = nullptr;
2739
0
                            break;
2740
0
                        }
2741
0
                    }
2742
2743
0
                    if (tabPolygons != nullptr)
2744
0
                    {
2745
0
                        int isValidGeometry = FALSE;
2746
0
                        const char *papszOptions[] = {"METHOD=ONLY_CCW",
2747
0
                                                      nullptr};
2748
0
                        poOGR = OGRGeometryFactory::organizePolygons(
2749
0
                            reinterpret_cast<OGRGeometry **>(tabPolygons),
2750
0
                            nParts, &isValidGeometry, papszOptions);
2751
2752
0
                        if (!isValidGeometry)
2753
0
                        {
2754
0
                            CPLError(CE_Warning, CPLE_AppDefined,
2755
0
                                     "Geometry of polygon cannot be translated "
2756
0
                                     "to Simple Geometry.  All polygons will "
2757
0
                                     "be contained in a multipolygon.");
2758
0
                        }
2759
2760
0
                        *ppoGeom = poOGR;
2761
0
                        delete[] tabPolygons;
2762
0
                    }
2763
0
                }
2764
0
            }
2765
0
            else if (nParts != 0)
2766
0
            {
2767
0
                if (nParts == 1)
2768
0
                {
2769
0
                    OGRPolygon *poOGRPoly = new OGRPolygon;
2770
0
                    *ppoGeom = poOGRPoly;
2771
0
                    OGRLinearRing *poRing = new OGRLinearRing;
2772
0
                    int nVerticesInThisPart = nPoints - panPartStart[0];
2773
2774
0
                    poRing->setPoints(
2775
0
                        nVerticesInThisPart, padfX + panPartStart[0],
2776
0
                        padfY + panPartStart[0], padfZ + panPartStart[0],
2777
0
                        padfM != nullptr ? padfM + panPartStart[0] : nullptr);
2778
2779
0
                    if (poOGRPoly->addRingDirectly(poRing) != OGRERR_NONE)
2780
0
                    {
2781
0
                        delete poRing;
2782
0
                        delete poOGRPoly;
2783
0
                        *ppoGeom = nullptr;
2784
0
                    }
2785
0
                }
2786
0
                else
2787
0
                {
2788
0
                    OGRGeometry *poOGR = nullptr;
2789
0
                    OGRPolygon **tabPolygons = new OGRPolygon *[nParts];
2790
2791
0
                    for (int i = 0; i < nParts; i++)
2792
0
                    {
2793
0
                        tabPolygons[i] = new OGRPolygon();
2794
0
                        OGRLinearRing *poRing = new OGRLinearRing;
2795
0
                        const int nVerticesInThisPart =
2796
0
                            i == nParts - 1
2797
0
                                ? nPoints - panPartStart[i]
2798
0
                                : panPartStart[i + 1] - panPartStart[i];
2799
2800
0
                        poRing->setPoints(
2801
0
                            nVerticesInThisPart, padfX + panPartStart[i],
2802
0
                            padfY + panPartStart[i], padfZ + panPartStart[i],
2803
0
                            padfM != nullptr ? padfM + panPartStart[i]
2804
0
                                             : nullptr);
2805
0
                        if (tabPolygons[i]->addRingDirectly(poRing) !=
2806
0
                            OGRERR_NONE)
2807
0
                        {
2808
0
                            delete poRing;
2809
0
                            for (; i >= 0; --i)
2810
0
                                delete tabPolygons[i];
2811
0
                            delete[] tabPolygons;
2812
0
                            tabPolygons = nullptr;
2813
0
                            *ppoGeom = nullptr;
2814
0
                            break;
2815
0
                        }
2816
0
                    }
2817
2818
0
                    if (tabPolygons != nullptr)
2819
0
                    {
2820
0
                        int isValidGeometry = FALSE;
2821
                        // The outer ring is supposed to be clockwise oriented
2822
                        // If it is not, then use the default/slow method.
2823
0
                        const char *papszOptions[] = {
2824
0
                            !(tabPolygons[0]->getExteriorRing()->isClockwise())
2825
0
                                ? "METHOD=DEFAULT"
2826
0
                                : "METHOD=ONLY_CCW",
2827
0
                            nullptr};
2828
0
                        poOGR = OGRGeometryFactory::organizePolygons(
2829
0
                            reinterpret_cast<OGRGeometry **>(tabPolygons),
2830
0
                            nParts, &isValidGeometry, papszOptions);
2831
2832
0
                        if (!isValidGeometry)
2833
0
                        {
2834
0
                            CPLError(CE_Warning, CPLE_AppDefined,
2835
0
                                     "Geometry of polygon cannot be translated "
2836
0
                                     "to Simple Geometry. All polygons will be "
2837
0
                                     "contained in a multipolygon.");
2838
0
                        }
2839
2840
0
                        *ppoGeom = poOGR;
2841
0
                        delete[] tabPolygons;
2842
0
                    }
2843
0
                }
2844
0
            }
2845
0
            else
2846
0
            {
2847
0
                *ppoGeom = new OGRPolygon();
2848
0
            }
2849
0
        }  // Polygon.
2850
2851
        /* --------------------------------------------------------------------
2852
         */
2853
        /*      Multipatch */
2854
        /* --------------------------------------------------------------------
2855
         */
2856
0
        else if (bIsMultiPatch)
2857
0
        {
2858
0
            *ppoGeom =
2859
0
                OGRCreateFromMultiPatch(nParts, panPartStart, panPartType,
2860
0
                                        nPoints, padfX, padfY, padfZ);
2861
0
        }
2862
2863
0
        CPLFree(panPartStart);
2864
0
        CPLFree(panPartType);
2865
0
        CPLFree(padfX);
2866
0
        CPLFree(padfY);
2867
0
        CPLFree(padfZ);
2868
0
        CPLFree(padfM);
2869
0
        CPLFree(pasCurves);
2870
2871
0
        if (*ppoGeom != nullptr)
2872
0
        {
2873
0
            if (!bHasZ)
2874
0
                (*ppoGeom)->set3D(FALSE);
2875
0
        }
2876
2877
0
        return *ppoGeom != nullptr ? OGRERR_NONE : OGRERR_FAILURE;
2878
0
    }
2879
2880
    /* ==================================================================== */
2881
    /*     Extract vertices for a MultiPoint.                               */
2882
    /* ==================================================================== */
2883
0
    else if (nSHPType == SHPT_MULTIPOINT || nSHPType == SHPT_MULTIPOINTM ||
2884
0
             nSHPType == SHPT_MULTIPOINTZ || nSHPType == SHPT_MULTIPOINTZM)
2885
0
    {
2886
0
        GInt32 nPoints = 0;
2887
0
        memcpy(&nPoints, pabyShape + 36, 4);
2888
0
        CPL_LSBPTR32(&nPoints);
2889
2890
0
        if (nPoints < 0 || nPoints > 50 * 1000 * 1000)
2891
0
        {
2892
0
            CPLError(CE_Failure, CPLE_AppDefined,
2893
0
                     "Corrupted Shape : nPoints=%d.", nPoints);
2894
0
            return OGRERR_FAILURE;
2895
0
        }
2896
2897
0
        const GInt32 nOffsetZ = 40 + 2 * 8 * nPoints + 2 * 8;
2898
0
        GInt32 nOffsetM = 0;
2899
0
        if (bHasM)
2900
0
            nOffsetM = bHasZ ? nOffsetZ + 2 * 8 * 8 * nPoints : nOffsetZ;
2901
2902
0
        OGRMultiPoint *poMultiPt = new OGRMultiPoint;
2903
0
        *ppoGeom = poMultiPt;
2904
2905
0
        for (int i = 0; i < nPoints; i++)
2906
0
        {
2907
0
            OGRPoint *poPt = new OGRPoint;
2908
2909
            // Copy X.
2910
0
            double x = 0.0;
2911
0
            memcpy(&x, pabyShape + 40 + i * 16, 8);
2912
0
            CPL_LSBPTR64(&x);
2913
0
            poPt->setX(x);
2914
2915
            // Copy Y.
2916
0
            double y = 0.0;
2917
0
            memcpy(&y, pabyShape + 40 + i * 16 + 8, 8);
2918
0
            CPL_LSBPTR64(&y);
2919
0
            poPt->setY(y);
2920
2921
            // Copy Z.
2922
0
            if (bHasZ)
2923
0
            {
2924
0
                double z = 0.0;
2925
0
                memcpy(&z, pabyShape + nOffsetZ + i * 8, 8);
2926
0
                CPL_LSBPTR64(&z);
2927
0
                poPt->setZ(z);
2928
0
            }
2929
2930
            // Copy M.
2931
0
            if (bHasM)
2932
0
            {
2933
0
                double m = 0.0;
2934
0
                memcpy(&m, pabyShape + nOffsetM + i * 8, 8);
2935
0
                CPL_LSBPTR64(&m);
2936
0
                poPt->setM(m);
2937
0
            }
2938
2939
0
            poMultiPt->addGeometryDirectly(poPt);
2940
0
        }
2941
2942
0
        poMultiPt->set3D(bHasZ);
2943
0
        poMultiPt->setMeasured(bHasM);
2944
2945
0
        return OGRERR_NONE;
2946
0
    }
2947
2948
    /* ==================================================================== */
2949
    /*      Extract vertices for a point.                                   */
2950
    /* ==================================================================== */
2951
0
    else if (nSHPType == SHPT_POINT || nSHPType == SHPT_POINTM ||
2952
0
             nSHPType == SHPT_POINTZ || nSHPType == SHPT_POINTZM)
2953
0
    {
2954
0
        if (nBytes < 4 + 8 + 8 + ((bHasZ) ? 8 : 0) + ((bHasM) ? 8 : 0))
2955
0
        {
2956
0
            CPLError(CE_Failure, CPLE_AppDefined,
2957
0
                     "Corrupted Shape : nBytes=%d, nSHPType=%d", nBytes,
2958
0
                     nSHPType);
2959
0
            return OGRERR_FAILURE;
2960
0
        }
2961
2962
0
        double dfX = 0.0;
2963
0
        double dfY = 0.0;
2964
2965
0
        memcpy(&dfX, pabyShape + 4, 8);
2966
0
        memcpy(&dfY, pabyShape + 4 + 8, 8);
2967
2968
0
        CPL_LSBPTR64(&dfX);
2969
0
        CPL_LSBPTR64(&dfY);
2970
        // int nOffset = 20 + 8;
2971
2972
0
        double dfZ = 0.0;
2973
0
        if (bHasZ)
2974
0
        {
2975
0
            memcpy(&dfZ, pabyShape + 4 + 16, 8);
2976
0
            CPL_LSBPTR64(&dfZ);
2977
0
        }
2978
2979
0
        double dfM = 0.0;
2980
0
        if (bHasM)
2981
0
        {
2982
0
            memcpy(&dfM, pabyShape + 4 + 16 + ((bHasZ) ? 8 : 0), 8);
2983
0
            CPL_LSBPTR64(&dfM);
2984
0
        }
2985
2986
0
        if (bHasZ && bHasM)
2987
0
        {
2988
0
            *ppoGeom = new OGRPoint(dfX, dfY, dfZ, dfM);
2989
0
        }
2990
0
        else if (bHasZ)
2991
0
        {
2992
0
            *ppoGeom = new OGRPoint(dfX, dfY, dfZ);
2993
0
        }
2994
0
        else if (bHasM)
2995
0
        {
2996
0
            OGRPoint *poPoint = new OGRPoint(dfX, dfY);
2997
0
            poPoint->setM(dfM);
2998
0
            *ppoGeom = poPoint;
2999
0
        }
3000
0
        else
3001
0
        {
3002
0
            *ppoGeom = new OGRPoint(dfX, dfY);
3003
0
        }
3004
3005
0
        return OGRERR_NONE;
3006
0
    }
3007
3008
0
    CPLError(CE_Failure, CPLE_AppDefined, "Unsupported geometry type: %d",
3009
0
             nSHPType);
3010
3011
0
    return OGRERR_FAILURE;
3012
0
}
3013
3014
#ifdef HAVE_WFLAG_UNREACHABLE_CODE_AGGRESSIVE
3015
#if defined(__clang__)
3016
#pragma clang diagnostic pop
3017
#endif
3018
#endif