Coverage Report

Created: 2025-06-13 06:29

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