Coverage Report

Created: 2026-11-12 08:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/shape/shape2ogr.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implements translation of Shapefile shapes into OGR
5
 *           representation.
6
 * Author:   Frank Warmerdam, warmerda@home.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1999,  Les Technologies SoftMap Inc.
10
 * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#include "cpl_port.h"
16
#include "ogrshape.h"
17
18
#include <cmath>
19
#include <cstdio>
20
#include <cstdlib>
21
#include <cstring>
22
#include <algorithm>
23
#include <limits>
24
#include <memory>
25
#include <utility>
26
27
#include "cpl_conv.h"
28
#include "cpl_error.h"
29
#include "cpl_string.h"
30
#include "cpl_minixml.h"
31
#include "cpl_vsi_virtual.h"
32
#include "ogr_core.h"
33
#include "ogr_feature.h"
34
#include "ogr_geometry.h"
35
#include "ogrpgeogeometry.h"
36
#include "ogrshape.h"
37
#include "shapefil.h"
38
39
/************************************************************************/
40
/*                        RingStartEnd                                  */
41
/*        Set first and last vertex for given ring.                     */
42
/************************************************************************/
43
static void RingStartEnd(SHPObject *psShape, int ring, int *start, int *end)
44
54
{
45
54
    if (psShape->panPartStart == nullptr)
46
0
    {
47
0
        *start = 0;
48
0
        *end = psShape->nVertices - 1;
49
0
    }
50
54
    else
51
54
    {
52
54
        *start = psShape->panPartStart[ring];
53
54
54
        if (ring == psShape->nParts - 1)
55
34
            *end = psShape->nVertices - 1;
56
20
        else
57
20
            *end = psShape->panPartStart[ring + 1] - 1;
58
54
    }
59
54
}
60
61
/************************************************************************/
62
/*                           CreateLinearRing                           */
63
/************************************************************************/
64
static std::unique_ptr<OGRLinearRing>
65
CreateLinearRing(SHPObject *psShape, int ring, bool bHasZ, bool bHasM)
66
54
{
67
54
    int nRingStart = 0;
68
54
    int nRingEnd = 0;
69
54
    RingStartEnd(psShape, ring, &nRingStart, &nRingEnd);
70
71
54
    auto poRing = std::make_unique<OGRLinearRing>();
72
54
    if (!(nRingEnd >= nRingStart))
73
10
        return poRing;
74
75
44
    const int nRingPoints = nRingEnd - nRingStart + 1;
76
77
44
    if (bHasZ && bHasM)
78
0
        poRing->setPoints(
79
0
            nRingPoints, psShape->padfX + nRingStart,
80
0
            psShape->padfY + nRingStart, psShape->padfZ + nRingStart,
81
0
            psShape->padfM ? psShape->padfM + nRingStart : nullptr);
82
44
    else if (bHasM)
83
2
        poRing->setPointsM(nRingPoints, psShape->padfX + nRingStart,
84
2
                           psShape->padfY + nRingStart,
85
2
                           psShape->padfM ? psShape->padfM + nRingStart
86
2
                                          : nullptr);
87
42
    else
88
42
        poRing->setPoints(nRingPoints, psShape->padfX + nRingStart,
89
42
                          psShape->padfY + nRingStart);
90
91
44
    return poRing;
92
54
}
93
94
/************************************************************************/
95
/*                          SHPReadOGRObject()                          */
96
/*                                                                      */
97
/*      Read an item in a shapefile, and translate to OGR geometry      */
98
/*      representation.                                                 */
99
/************************************************************************/
100
101
std::unique_ptr<OGRGeometry> SHPReadOGRObject(SHPHandle hSHP, int iShape,
102
                                              SHPObject *psShape,
103
                                              bool &bHasWarnedWrongWindingOrder,
104
                                              OGRwkbGeometryType eLayerGeomType)
105
42.8k
{
106
#if DEBUG_VERBOSE
107
    CPLDebug("Shape", "SHPReadOGRObject( iShape=%d )", iShape);
108
#endif
109
110
42.8k
    if (psShape == nullptr)
111
42.8k
        psShape = SHPReadObject(hSHP, iShape);
112
113
42.8k
    if (psShape == nullptr)
114
39.6k
    {
115
39.6k
        return nullptr;
116
39.6k
    }
117
118
3.20k
    std::unique_ptr<OGRGeometry> poOGR;
119
120
    /* -------------------------------------------------------------------- */
121
    /*      Point.                                                          */
122
    /* -------------------------------------------------------------------- */
123
3.20k
    if (psShape->nSHPType == SHPT_POINT)
124
210
    {
125
210
        poOGR =
126
210
            std::make_unique<OGRPoint>(psShape->padfX[0], psShape->padfY[0]);
127
210
    }
128
2.99k
    else if (psShape->nSHPType == SHPT_POINTZ)
129
6
    {
130
6
        if (psShape->bMeasureIsUsed)
131
6
        {
132
6
            poOGR = std::make_unique<OGRPoint>(
133
6
                psShape->padfX[0], psShape->padfY[0], psShape->padfZ[0],
134
6
                psShape->padfM[0]);
135
6
        }
136
0
        else
137
0
        {
138
0
            poOGR = std::make_unique<OGRPoint>(
139
0
                psShape->padfX[0], psShape->padfY[0], psShape->padfZ[0]);
140
0
        }
141
6
    }
142
2.99k
    else if (psShape->nSHPType == SHPT_POINTM)
143
0
    {
144
0
        poOGR = std::make_unique<OGRPoint>(psShape->padfX[0], psShape->padfY[0],
145
0
                                           0.0, psShape->padfM[0]);
146
0
        poOGR->set3D(FALSE);
147
0
    }
148
    /* -------------------------------------------------------------------- */
149
    /*      Multipoint.                                                     */
150
    /* -------------------------------------------------------------------- */
151
2.99k
    else if (psShape->nSHPType == SHPT_MULTIPOINT ||
152
2.93k
             psShape->nSHPType == SHPT_MULTIPOINTM ||
153
2.91k
             psShape->nSHPType == SHPT_MULTIPOINTZ)
154
775
    {
155
775
        if (psShape->nVertices == 0)
156
66
        {
157
66
            poOGR = nullptr;
158
66
        }
159
709
        else
160
709
        {
161
709
            auto poOGRMPoint = std::make_unique<OGRMultiPoint>();
162
163
136k
            for (int i = 0; i < psShape->nVertices; i++)
164
135k
            {
165
135k
                std::unique_ptr<OGRPoint> poPoint;
166
167
135k
                if (psShape->nSHPType == SHPT_MULTIPOINTZ)
168
128k
                {
169
128k
                    if (psShape->padfM)
170
100k
                    {
171
100k
                        poPoint = std::make_unique<OGRPoint>(
172
100k
                            psShape->padfX[i], psShape->padfY[i],
173
100k
                            psShape->padfZ[i], psShape->padfM[i]);
174
100k
                    }
175
28.0k
                    else
176
28.0k
                    {
177
28.0k
                        poPoint = std::make_unique<OGRPoint>(psShape->padfX[i],
178
28.0k
                                                             psShape->padfY[i],
179
28.0k
                                                             psShape->padfZ[i]);
180
28.0k
                    }
181
128k
                }
182
7.10k
                else if (psShape->nSHPType == SHPT_MULTIPOINTM &&
183
16
                         psShape->padfM)
184
16
                {
185
16
                    poPoint = std::make_unique<OGRPoint>(psShape->padfX[i],
186
16
                                                         psShape->padfY[i], 0.0,
187
16
                                                         psShape->padfM[i]);
188
16
                    poPoint->set3D(FALSE);
189
16
                }
190
7.08k
                else
191
7.08k
                {
192
7.08k
                    poPoint = std::make_unique<OGRPoint>(psShape->padfX[i],
193
7.08k
                                                         psShape->padfY[i]);
194
7.08k
                }
195
196
135k
                poOGRMPoint->addGeometry(std::move(poPoint));
197
135k
            }
198
199
709
            poOGR = std::move(poOGRMPoint);
200
709
        }
201
775
    }
202
203
    /* -------------------------------------------------------------------- */
204
    /*      Arc (LineString)                                                */
205
    /*                                                                      */
206
    /*      Ignoring parts though they can apply to arcs as well.           */
207
    /* -------------------------------------------------------------------- */
208
2.21k
    else if (psShape->nSHPType == SHPT_ARC || psShape->nSHPType == SHPT_ARCM ||
209
2.20k
             psShape->nSHPType == SHPT_ARCZ)
210
12
    {
211
12
        if (psShape->nParts == 1)
212
10
        {
213
10
            auto poOGRLine = std::make_unique<OGRLineString>();
214
215
10
            if (psShape->nSHPType == SHPT_ARCZ)
216
0
                poOGRLine->setPoints(psShape->nVertices, psShape->padfX,
217
0
                                     psShape->padfY, psShape->padfZ,
218
0
                                     psShape->padfM);
219
10
            else if (psShape->nSHPType == SHPT_ARCM)
220
7
                poOGRLine->setPointsM(psShape->nVertices, psShape->padfX,
221
7
                                      psShape->padfY, psShape->padfM);
222
3
            else
223
3
                poOGRLine->setPoints(psShape->nVertices, psShape->padfX,
224
3
                                     psShape->padfY);
225
226
10
            if (wkbFlatten(eLayerGeomType) == wkbMultiLineString)
227
0
            {
228
0
                auto poOGRMulti = std::make_unique<OGRMultiLineString>();
229
0
                poOGRMulti->addGeometry(std::move(poOGRLine));
230
0
                poOGR = std::move(poOGRMulti);
231
0
            }
232
10
            else
233
10
            {
234
10
                poOGR = std::move(poOGRLine);
235
10
            }
236
10
        }
237
2
        else if (psShape->nParts > 1)
238
0
        {
239
0
            auto poOGRMulti = std::make_unique<OGRMultiLineString>();
240
241
0
            for (int iRing = 0; iRing < psShape->nParts; iRing++)
242
0
            {
243
0
                int nRingPoints = 0;
244
0
                int nRingStart = 0;
245
246
0
                auto poLine = std::make_unique<OGRLineString>();
247
248
0
                if (psShape->panPartStart == nullptr)
249
0
                {
250
0
                    nRingPoints = psShape->nVertices;
251
0
                    nRingStart = 0;
252
0
                }
253
0
                else
254
0
                {
255
0
                    if (iRing == psShape->nParts - 1)
256
0
                        nRingPoints =
257
0
                            psShape->nVertices - psShape->panPartStart[iRing];
258
0
                    else
259
0
                        nRingPoints = psShape->panPartStart[iRing + 1] -
260
0
                                      psShape->panPartStart[iRing];
261
0
                    nRingStart = psShape->panPartStart[iRing];
262
0
                }
263
264
0
                if (psShape->nSHPType == SHPT_ARCZ)
265
0
                    poLine->setPoints(
266
0
                        nRingPoints, psShape->padfX + nRingStart,
267
0
                        psShape->padfY + nRingStart,
268
0
                        psShape->padfZ + nRingStart,
269
0
                        psShape->padfM ? psShape->padfM + nRingStart : nullptr);
270
0
                else if (psShape->nSHPType == SHPT_ARCM &&
271
0
                         psShape->padfM != nullptr)
272
0
                    poLine->setPointsM(nRingPoints, psShape->padfX + nRingStart,
273
0
                                       psShape->padfY + nRingStart,
274
0
                                       psShape->padfM + nRingStart);
275
0
                else
276
0
                    poLine->setPoints(nRingPoints, psShape->padfX + nRingStart,
277
0
                                      psShape->padfY + nRingStart);
278
279
0
                poOGRMulti->addGeometry(std::move(poLine));
280
0
            }
281
282
0
            poOGR = std::move(poOGRMulti);
283
0
        }
284
12
    }
285
286
    /* -------------------------------------------------------------------- */
287
    /*      Polygon                                                         */
288
    /*                                                                      */
289
    /* As for now Z coordinate is not handled correctly                     */
290
    /* -------------------------------------------------------------------- */
291
2.20k
    else if (psShape->nSHPType == SHPT_POLYGON ||
292
2.18k
             psShape->nSHPType == SHPT_POLYGONM ||
293
2.16k
             psShape->nSHPType == SHPT_POLYGONZ)
294
41
    {
295
41
        const bool bHasZ = psShape->nSHPType == SHPT_POLYGONZ;
296
41
        const bool bHasM = bHasZ || psShape->nSHPType == SHPT_POLYGONM;
297
298
#if DEBUG_VERBOSE
299
        CPLDebug("Shape", "Shape type: polygon with nParts=%d",
300
                 psShape->nParts);
301
#endif
302
303
41
        if (psShape->nParts == 1)
304
14
        {
305
            // Surely outer ring.
306
14
            auto poOGRPoly = std::make_unique<OGRPolygon>();
307
14
            poOGRPoly->addRing(CreateLinearRing(psShape, 0, bHasZ, bHasM));
308
309
14
            if (wkbFlatten(eLayerGeomType) == wkbMultiPolygon)
310
13
            {
311
13
                auto poOGRMulti = std::make_unique<OGRMultiPolygon>();
312
13
                poOGRMulti->addGeometry(std::move(poOGRPoly));
313
13
                poOGR = std::move(poOGRMulti);
314
13
            }
315
1
            else
316
1
            {
317
1
                poOGR = std::move(poOGRPoly);
318
1
            }
319
14
        }
320
27
        else if (psShape->nParts >= 1)
321
20
        {
322
20
            std::vector<std::unique_ptr<OGRGeometry>> apoPolygons;
323
20
            apoPolygons.reserve(psShape->nParts);
324
60
            for (int iRing = 0; iRing < psShape->nParts; iRing++)
325
40
            {
326
40
                auto poPoly = std::make_unique<OGRPolygon>();
327
40
                poPoly->addRing(CreateLinearRing(psShape, iRing, bHasZ, bHasM));
328
40
                apoPolygons.push_back(std::move(poPoly));
329
40
            }
330
331
            // Tries to detect bad geometries where a multi-part multipolygon is
332
            // written as a single-part multipolygon with its parts as inner
333
            // rings, like done by QGIS <= 3.28.11 with GDAL >= 3.7
334
            // Cf https://github.com/qgis/QGIS/issues/54537
335
20
            bool bUseSlowMethod = false;
336
20
            if (!bHasZ && !bHasM)
337
20
            {
338
20
                bool bFoundCW = false;
339
38
                for (int iRing = 1; iRing < psShape->nParts; iRing++)
340
20
                {
341
20
                    if (apoPolygons[iRing]
342
20
                            ->toPolygon()
343
20
                            ->getExteriorRing()
344
20
                            ->isClockwise())
345
2
                    {
346
2
                        bFoundCW = true;
347
2
                        break;
348
2
                    }
349
20
                }
350
20
                if (!bFoundCW)
351
18
                {
352
                    // Only inner rings
353
18
                    OGREnvelope sFirstEnvelope;
354
18
                    OGREnvelope sCurEnvelope;
355
18
                    const OGRLinearRing *poExteriorRing =
356
18
                        apoPolygons[0]->toPolygon()->getExteriorRing();
357
18
                    poExteriorRing->getEnvelope(&sFirstEnvelope);
358
25
                    for (int iRing = 1; iRing < psShape->nParts; iRing++)
359
18
                    {
360
18
                        apoPolygons[iRing]->getEnvelope(&sCurEnvelope);
361
18
                        if (!sFirstEnvelope.Intersects(sCurEnvelope))
362
0
                        {
363
                            // If the envelopes of the rings don't intersect,
364
                            // then it is clearly a multi-part polygon
365
0
                            bUseSlowMethod = true;
366
0
                            break;
367
0
                        }
368
18
                        else
369
18
                        {
370
                            // Otherwise take 4 points at each extremity of
371
                            // the inner rings and check if there are in the
372
                            // outer ring. If none are within it, then it is
373
                            // very likely a outer ring (or an invalid ring
374
                            // which is neither a outer nor a inner ring)
375
18
                            const auto poRing = apoPolygons[iRing]
376
18
                                                    ->toPolygon()
377
18
                                                    ->getExteriorRing();
378
18
                            const auto nNumPoints = poRing->getNumPoints();
379
18
                            OGRPoint p;
380
18
                            OGRPoint leftPoint(
381
18
                                std::numeric_limits<double>::infinity(), 0);
382
18
                            OGRPoint rightPoint(
383
18
                                -std::numeric_limits<double>::infinity(), 0);
384
18
                            OGRPoint bottomPoint(
385
18
                                0, std::numeric_limits<double>::infinity());
386
18
                            OGRPoint topPoint(
387
18
                                0, -std::numeric_limits<double>::infinity());
388
300
                            for (int iPoint = 0; iPoint < nNumPoints - 1;
389
282
                                 ++iPoint)
390
282
                            {
391
282
                                poRing->getPoint(iPoint, &p);
392
282
                                if (p.getX() < leftPoint.getX() ||
393
227
                                    (p.getX() == leftPoint.getX() &&
394
61
                                     p.getY() < leftPoint.getY()))
395
56
                                {
396
56
                                    leftPoint = p;
397
56
                                }
398
282
                                if (p.getX() > rightPoint.getX() ||
399
244
                                    (p.getX() == rightPoint.getX() &&
400
38
                                     p.getY() > rightPoint.getY()))
401
38
                                {
402
38
                                    rightPoint = p;
403
38
                                }
404
282
                                if (p.getY() < bottomPoint.getY() ||
405
237
                                    (p.getY() == bottomPoint.getY() &&
406
75
                                     p.getX() > bottomPoint.getX()))
407
51
                                {
408
51
                                    bottomPoint = p;
409
51
                                }
410
282
                                if (p.getY() > topPoint.getY() ||
411
230
                                    (p.getY() == topPoint.getY() &&
412
58
                                     p.getX() < topPoint.getX()))
413
59
                                {
414
59
                                    topPoint = p;
415
59
                                }
416
282
                            }
417
18
                            if (!poExteriorRing->isPointInRing(&leftPoint) &&
418
12
                                !poExteriorRing->isPointInRing(&rightPoint) &&
419
12
                                !poExteriorRing->isPointInRing(&bottomPoint) &&
420
11
                                !poExteriorRing->isPointInRing(&topPoint))
421
11
                            {
422
11
                                bUseSlowMethod = true;
423
11
                                break;
424
11
                            }
425
18
                        }
426
18
                    }
427
18
                    if (bUseSlowMethod && !bHasWarnedWrongWindingOrder)
428
9
                    {
429
9
                        bHasWarnedWrongWindingOrder = true;
430
9
                        CPLError(CE_Warning, CPLE_AppDefined,
431
9
                                 "%s contains polygon(s) with rings with "
432
9
                                 "invalid winding order. Autocorrecting them, "
433
9
                                 "but that shapefile should be corrected using "
434
9
                                 "ogr2ogr for example.",
435
9
                                 VSI_SHP_GetFilename(hSHP->fpSHP));
436
9
                    }
437
18
                }
438
20
            }
439
440
20
            bool isValidGeometry = false;
441
20
            const char *const apszOptions[] = {
442
20
                bUseSlowMethod ? "METHOD=DEFAULT" : "METHOD=ONLY_CCW", nullptr};
443
20
            poOGR = OGRGeometryFactory::organizePolygons(
444
20
                apoPolygons, &isValidGeometry, apszOptions);
445
446
20
            if (poOGR && wkbFlatten(poOGR->getGeometryType()) == wkbPolygon &&
447
5
                wkbFlatten(eLayerGeomType) == wkbMultiPolygon)
448
2
            {
449
2
                auto poOGRMulti = std::make_unique<OGRMultiPolygon>();
450
2
                poOGRMulti->addGeometryDirectly(poOGR.release()->toPolygon());
451
2
                poOGR = std::move(poOGRMulti);
452
2
            }
453
454
20
            if (!isValidGeometry)
455
5
            {
456
5
                CPLError(
457
5
                    CE_Warning, CPLE_AppDefined,
458
5
                    "Geometry of polygon of fid %d cannot be translated to "
459
5
                    "Simple Geometry. "
460
5
                    "All polygons will be contained in a multipolygon.",
461
5
                    iShape);
462
5
            }
463
20
        }
464
41
    }
465
466
    /* -------------------------------------------------------------------- */
467
    /*      MultiPatch                                                      */
468
    /* -------------------------------------------------------------------- */
469
2.16k
    else if (psShape->nSHPType == SHPT_MULTIPATCH)
470
13
    {
471
13
        poOGR = std::unique_ptr<OGRGeometry>(OGRCreateFromMultiPatch(
472
13
            psShape->nParts, psShape->panPartStart, psShape->panPartType,
473
13
            psShape->nVertices, psShape->padfX, psShape->padfY,
474
13
            psShape->padfZ));
475
13
    }
476
477
    /* -------------------------------------------------------------------- */
478
    /*      Otherwise for now we just ignore the object.                    */
479
    /* -------------------------------------------------------------------- */
480
2.15k
    else
481
2.15k
    {
482
2.15k
        if (psShape->nSHPType != SHPT_NULL)
483
1.43k
        {
484
1.43k
            CPLDebug("OGR", "Unsupported shape type in SHPReadOGRObject()");
485
1.43k
        }
486
487
        // Nothing returned.
488
2.15k
    }
489
490
    /* -------------------------------------------------------------------- */
491
    /*      Cleanup shape, and set feature id.                              */
492
    /* -------------------------------------------------------------------- */
493
3.20k
    SHPDestroyObject(psShape);
494
495
3.20k
    return poOGR;
496
42.8k
}
497
498
/************************************************************************/
499
/*                     CheckNonFiniteCoordinates()                      */
500
/************************************************************************/
501
502
static bool CheckNonFiniteCoordinates(const double *v, size_t vsize)
503
6.12k
{
504
6.12k
    static bool bAllowNonFiniteCoordinates = CPLTestBool(
505
6.12k
        CPLGetConfigOption("OGR_SHAPE_ALLOW_NON_FINITE_COORDINATES", "NO"));
506
    // Do not document this. Only for edge case testing
507
6.12k
    if (bAllowNonFiniteCoordinates)
508
0
    {
509
0
        return true;
510
0
    }
511
236k
    for (size_t i = 0; i < vsize; ++i)
512
230k
    {
513
230k
        if (!std::isfinite(v[i]))
514
97
        {
515
97
            CPLError(CE_Failure, CPLE_NotSupported,
516
97
                     "Coordinates with non-finite values are not allowed");
517
97
            return false;
518
97
        }
519
230k
    }
520
6.02k
    return true;
521
6.12k
}
522
523
static bool CheckNonFiniteCoordinates(const std::vector<double> &v)
524
6.12k
{
525
6.12k
    return CheckNonFiniteCoordinates(v.data(), v.size());
526
6.12k
}
527
528
/************************************************************************/
529
/*                         SHPWriteOGRObject()                          */
530
/************************************************************************/
531
static OGRErr SHPWriteOGRObject(SHPHandle hSHP, int iShape,
532
                                const OGRGeometry *poGeom, bool bRewind,
533
                                OGRwkbGeometryType eLayerGeomType)
534
535
233k
{
536
    /* ==================================================================== */
537
    /*      Write "shape" with no geometry or with empty geometry           */
538
    /* ==================================================================== */
539
233k
    if (poGeom == nullptr || poGeom->IsEmpty())
540
230k
    {
541
230k
        SHPObject *psShape =
542
230k
            SHPCreateObject(SHPT_NULL, -1, 0, nullptr, nullptr, 0, nullptr,
543
230k
                            nullptr, nullptr, nullptr);
544
230k
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
545
230k
        SHPDestroyObject(psShape);
546
230k
        if (nReturnedShapeID == -1)
547
0
        {
548
            // Assuming error is reported by SHPWriteObject().
549
0
            return OGRERR_FAILURE;
550
0
        }
551
230k
    }
552
553
    /* ==================================================================== */
554
    /*      Write point geometry.                                           */
555
    /* ==================================================================== */
556
2.91k
    else if (hSHP->nShapeType == SHPT_POINT ||
557
2.62k
             hSHP->nShapeType == SHPT_POINTM || hSHP->nShapeType == SHPT_POINTZ)
558
493
    {
559
493
        if (wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
560
31
        {
561
31
            CPLError(CE_Failure, CPLE_AppDefined,
562
31
                     "Attempt to write non-point (%s) geometry to"
563
31
                     " point shapefile.",
564
31
                     poGeom->getGeometryName());
565
566
31
            return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
567
31
        }
568
569
462
        const OGRPoint *poPoint = poGeom->toPoint();
570
462
        const double dfX = poPoint->getX();
571
462
        const double dfY = poPoint->getY();
572
462
        const double dfZ = poPoint->getZ();
573
462
        double dfM = -std::numeric_limits<double>::max();
574
462
        double *pdfM = nullptr;
575
462
        if (wkbHasM(eLayerGeomType) && (hSHP->nShapeType == SHPT_POINTM ||
576
6
                                        hSHP->nShapeType == SHPT_POINTZ))
577
11
        {
578
11
            if (poGeom->IsMeasured())
579
10
                dfM = poPoint->getM();
580
11
            pdfM = &dfM;
581
11
        }
582
462
        if ((!std::isfinite(dfX) || !std::isfinite(dfY) ||
583
462
             !std::isfinite(dfZ) || (pdfM && !std::isfinite(*pdfM))) &&
584
2
            !CPLTestBool(CPLGetConfigOption(
585
2
                "OGR_SHAPE_ALLOW_NON_FINITE_COORDINATES", "NO")))
586
2
        {
587
2
            CPLError(CE_Failure, CPLE_NotSupported,
588
2
                     "Coordinates with non-finite values are not allowed");
589
2
            return OGRERR_FAILURE;
590
2
        }
591
460
        SHPObject *psShape =
592
460
            SHPCreateObject(hSHP->nShapeType, -1, 0, nullptr, nullptr, 1, &dfX,
593
460
                            &dfY, &dfZ, pdfM);
594
460
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
595
460
        SHPDestroyObject(psShape);
596
460
        if (nReturnedShapeID == -1)
597
0
            return OGRERR_FAILURE;
598
460
    }
599
    /* ==================================================================== */
600
    /*      MultiPoint.                                                     */
601
    /* ==================================================================== */
602
2.42k
    else if (hSHP->nShapeType == SHPT_MULTIPOINT ||
603
2.41k
             hSHP->nShapeType == SHPT_MULTIPOINTM ||
604
2.41k
             hSHP->nShapeType == SHPT_MULTIPOINTZ)
605
6
    {
606
6
        if (wkbFlatten(poGeom->getGeometryType()) != wkbMultiPoint)
607
1
        {
608
1
            CPLError(CE_Failure, CPLE_AppDefined,
609
1
                     "Attempt to write non-multipoint (%s) geometry to "
610
1
                     "multipoint shapefile.",
611
1
                     poGeom->getGeometryName());
612
613
1
            return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
614
1
        }
615
616
5
        const OGRMultiPoint *poMP = poGeom->toMultiPoint();
617
5
        const int nNumGeometries = poMP->getNumGeometries();
618
5
        const bool bHasZ = (hSHP->nShapeType == SHPT_MULTIPOINTM ||
619
5
                            hSHP->nShapeType == SHPT_MULTIPOINTZ);
620
5
        const bool bHasM = wkbHasM(eLayerGeomType) && bHasZ;
621
5
        const bool bIsGeomMeasured = poGeom->IsMeasured();
622
623
5
        std::vector<double> adfX;
624
5
        std::vector<double> adfY;
625
5
        std::vector<double> adfZ;
626
5
        std::vector<double> adfM;
627
5
        try
628
5
        {
629
5
            adfX.reserve(nNumGeometries);
630
5
            adfY.reserve(nNumGeometries);
631
5
            if (bHasZ)
632
0
                adfZ.reserve(nNumGeometries);
633
5
            if (bHasM)
634
0
                adfM.reserve(nNumGeometries);
635
5
        }
636
5
        catch (const std::exception &e)
637
5
        {
638
0
            CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
639
0
            return OGRERR_FAILURE;
640
0
        }
641
642
5
        for (const OGRPoint *poPoint : *poMP)
643
5
        {
644
            // Ignore POINT EMPTY.
645
5
            if (!poPoint->IsEmpty())
646
5
            {
647
5
                adfX.push_back(poPoint->getX());
648
5
                adfY.push_back(poPoint->getY());
649
5
                if (bHasZ)
650
0
                    adfZ.push_back(poPoint->getZ());
651
5
                if (bHasM)
652
0
                {
653
0
                    if (bIsGeomMeasured)
654
0
                        adfM.push_back(poPoint->getM());
655
0
                    else
656
0
                        adfM.push_back(-std::numeric_limits<double>::max());
657
0
                }
658
5
            }
659
0
            else
660
0
            {
661
0
                CPLDebug("OGR",
662
0
                         "Ignored POINT EMPTY inside MULTIPOINT in shapefile "
663
0
                         "writer.");
664
0
            }
665
5
        }
666
5
        if (!CheckNonFiniteCoordinates(adfX) ||
667
5
            !CheckNonFiniteCoordinates(adfY) ||
668
5
            !CheckNonFiniteCoordinates(adfZ) ||
669
5
            !CheckNonFiniteCoordinates(adfM))
670
0
        {
671
0
            return OGRERR_FAILURE;
672
0
        }
673
674
5
        SHPObject *psShape = SHPCreateObject(
675
5
            hSHP->nShapeType, -1, 0, nullptr, nullptr,
676
5
            static_cast<int>(adfX.size()), adfX.data(), adfY.data(),
677
5
            bHasZ ? adfZ.data() : nullptr, bHasM ? adfM.data() : nullptr);
678
5
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
679
5
        SHPDestroyObject(psShape);
680
681
5
        if (nReturnedShapeID == -1)
682
0
            return OGRERR_FAILURE;
683
5
    }
684
685
    /* ==================================================================== */
686
    /*      Arcs                                                            */
687
    /* ==================================================================== */
688
2.41k
    else if (hSHP->nShapeType == SHPT_ARC || hSHP->nShapeType == SHPT_ARCM ||
689
733
             hSHP->nShapeType == SHPT_ARCZ)
690
2.41k
    {
691
2.41k
        std::unique_ptr<OGRGeometry> poGeomToDelete;  // keep in that scope
692
2.41k
        const OGRMultiLineString *poML = nullptr;
693
2.41k
        OGRMultiLineString oMLFromLineString;
694
2.41k
        const auto eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
695
2.41k
        if (eFlatGeomType == wkbMultiLineString)
696
24
        {
697
24
            poML = poGeom->toMultiLineString();
698
24
        }
699
2.38k
        else if (eFlatGeomType == wkbLineString)
700
1.22k
        {
701
            // Borrow the geometry
702
1.22k
            oMLFromLineString.addGeometryDirectly(
703
1.22k
                const_cast<OGRLineString *>(poGeom->toLineString()));
704
1.22k
            poML = &oMLFromLineString;
705
1.22k
        }
706
1.16k
        else
707
1.16k
        {
708
1.16k
            poGeomToDelete = std::unique_ptr<OGRGeometry>(
709
1.16k
                OGRGeometryFactory::forceToMultiLineString(poGeom->clone()));
710
1.16k
            if (wkbFlatten(poGeomToDelete->getGeometryType()) !=
711
1.16k
                wkbMultiLineString)
712
830
            {
713
830
                CPLError(CE_Failure, CPLE_AppDefined,
714
830
                         "Attempt to write non-linestring (%s) geometry to "
715
830
                         "ARC type shapefile.",
716
830
                         poGeom->getGeometryName());
717
718
830
                return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
719
830
            }
720
331
            poML = poGeomToDelete->toMultiLineString();
721
331
        }
722
723
1.58k
        const int nNumGeometries = poML->getNumGeometries();
724
725
1.58k
        int nTotalPoints = 0;
726
1.58k
        for (const auto poArc : poML)
727
1.67k
        {
728
1.67k
            const int nNumPoints = poArc->getNumPoints();
729
1.67k
            if (nTotalPoints > std::numeric_limits<int>::max() - nNumPoints)
730
0
            {
731
0
                CPLError(CE_Failure, CPLE_AppDefined, "Too big geometry");
732
0
                return OGRERR_FAILURE;
733
0
            }
734
1.67k
            nTotalPoints += nNumPoints;
735
1.67k
        }
736
737
1.58k
        std::vector<int> anRingStart;
738
1.58k
        std::vector<double> adfX;
739
1.58k
        std::vector<double> adfY;
740
1.58k
        std::vector<double> adfZ;
741
1.58k
        std::vector<double> adfM;
742
1.58k
        const bool bHasZ =
743
1.58k
            (hSHP->nShapeType == SHPT_ARCM || hSHP->nShapeType == SHPT_ARCZ);
744
1.58k
        const bool bHasM = wkbHasM(eLayerGeomType) && bHasZ;
745
1.58k
        const bool bIsGeomMeasured = poGeom->IsMeasured();
746
747
1.58k
        try
748
1.58k
        {
749
1.58k
            anRingStart.reserve(nNumGeometries);
750
751
1.58k
            adfX.reserve(nTotalPoints);
752
1.58k
            adfY.reserve(nTotalPoints);
753
1.58k
            if (bHasZ)
754
729
            {
755
729
                adfZ.reserve(nTotalPoints);
756
729
            }
757
1.58k
            if (bHasM)
758
1
            {
759
1
                adfM.reserve(nTotalPoints);
760
1
            }
761
1.58k
        }
762
1.58k
        catch (const std::exception &e)
763
1.58k
        {
764
0
            CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
765
            // Give back the borrowed line string
766
0
            if (eFlatGeomType == wkbLineString)
767
0
                oMLFromLineString.removeGeometry(0, /* bDelete=*/false);
768
0
            return OGRERR_FAILURE;
769
0
        }
770
771
1.58k
        for (const auto poArc : poML)
772
1.67k
        {
773
1.67k
            const int nNumPoints = poArc->getNumPoints();
774
775
            // Ignore LINESTRING EMPTY.
776
1.67k
            if (nNumPoints == 0)
777
0
            {
778
0
                CPLDebug("OGR",
779
0
                         "Ignore LINESTRING EMPTY inside MULTILINESTRING in "
780
0
                         "shapefile writer.");
781
0
                continue;
782
0
            }
783
784
1.67k
            anRingStart.push_back(static_cast<int>(adfX.size()));
785
786
84.3k
            for (int iPoint = 0; iPoint < nNumPoints; iPoint++)
787
82.6k
            {
788
82.6k
                adfX.push_back(poArc->getX(iPoint));
789
82.6k
                adfY.push_back(poArc->getY(iPoint));
790
82.6k
                if (bHasZ)
791
66.8k
                {
792
66.8k
                    adfZ.push_back(poArc->getZ(iPoint));
793
66.8k
                }
794
82.6k
                if (bHasM)
795
5
                {
796
5
                    if (bIsGeomMeasured)
797
5
                        adfM.push_back(poArc->getM(iPoint));
798
0
                    else
799
0
                        adfM.push_back(-std::numeric_limits<double>::max());
800
5
                }
801
82.6k
            }
802
1.67k
        }
803
804
        // Give back the borrowed line string
805
1.58k
        if (eFlatGeomType == wkbLineString)
806
1.22k
            oMLFromLineString.removeGeometry(0, /* bDelete=*/false);
807
808
1.58k
        if (!CheckNonFiniteCoordinates(adfX) ||
809
1.55k
            !CheckNonFiniteCoordinates(adfY) ||
810
1.48k
            !CheckNonFiniteCoordinates(adfZ) ||
811
1.48k
            !CheckNonFiniteCoordinates(adfM))
812
97
        {
813
97
            return OGRERR_FAILURE;
814
97
        }
815
816
1.48k
        SHPObject *psShape = SHPCreateObject(
817
1.48k
            hSHP->nShapeType, iShape, static_cast<int>(anRingStart.size()),
818
1.48k
            anRingStart.data(), nullptr, static_cast<int>(adfX.size()),
819
1.48k
            adfX.data(), adfY.data(), bHasZ ? adfZ.data() : nullptr,
820
1.48k
            bHasM ? adfM.data() : nullptr);
821
1.48k
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
822
1.48k
        SHPDestroyObject(psShape);
823
824
1.48k
        if (nReturnedShapeID == -1)
825
0
            return OGRERR_FAILURE;
826
1.48k
    }
827
828
    /* ==================================================================== */
829
    /*      Polygons/MultiPolygons                                          */
830
    /* ==================================================================== */
831
5
    else if (hSHP->nShapeType == SHPT_POLYGON ||
832
2
             hSHP->nShapeType == SHPT_POLYGONM ||
833
2
             hSHP->nShapeType == SHPT_POLYGONZ)
834
4
    {
835
        // bool = true means outer ring
836
4
        std::vector<std::pair<const OGRLinearRing *, bool>> apoRings;
837
4
        const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
838
4
        std::unique_ptr<OGRGeometry> poGeomToDelete;
839
840
4
        if (eType == wkbPolygon || eType == wkbTriangle)
841
0
        {
842
0
            const OGRPolygon *poPoly = poGeom->toPolygon();
843
844
0
            if (poPoly->getExteriorRing() == nullptr ||
845
0
                poPoly->getExteriorRing()->IsEmpty())
846
0
            {
847
0
                CPLDebug("OGR", "Ignore POLYGON EMPTY in shapefile writer.");
848
0
            }
849
0
            else
850
0
            {
851
0
                const int nSrcRings = poPoly->getNumInteriorRings() + 1;
852
0
                apoRings.reserve(nSrcRings);
853
0
                bool bFirstRing = true;
854
0
                for (const auto poRing : poPoly)
855
0
                {
856
0
                    const int nNumPoints = poRing->getNumPoints();
857
858
                    // Ignore LINEARRING EMPTY.
859
0
                    if (nNumPoints != 0)
860
0
                    {
861
0
                        apoRings.push_back(std::make_pair(poRing, bFirstRing));
862
0
                    }
863
0
                    else
864
0
                    {
865
0
                        CPLDebug("OGR",
866
0
                                 "Ignore LINEARRING EMPTY inside POLYGON in "
867
0
                                 "shapefile writer.");
868
0
                    }
869
0
                    bFirstRing = false;
870
0
                }
871
0
            }
872
0
        }
873
4
        else if (eType == wkbMultiPolygon || eType == wkbGeometryCollection ||
874
4
                 eType == wkbPolyhedralSurface || eType == wkbTIN)
875
0
        {
876
0
            const OGRGeometryCollection *poGC;
877
            // for PolyhedralSurface and TIN
878
0
            if (eType == wkbPolyhedralSurface || eType == wkbTIN)
879
0
            {
880
0
                poGeomToDelete = OGRGeometryFactory::forceTo(
881
0
                    std::unique_ptr<OGRGeometry>(poGeom->clone()),
882
0
                    wkbMultiPolygon, nullptr);
883
0
                poGC = poGeomToDelete->toGeometryCollection();
884
0
            }
885
886
0
            else
887
0
                poGC = poGeom->toGeometryCollection();
888
889
            // Shouldn't happen really, but to please x86_64-w64-mingw32-g++ -O2
890
            // -Wnull-dereference
891
0
            if (poGC == nullptr)
892
0
                return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
893
894
0
            for (const auto poSubGeom : poGC)
895
0
            {
896
0
                if (wkbFlatten(poSubGeom->getGeometryType()) != wkbPolygon)
897
0
                {
898
0
                    CPLError(CE_Failure, CPLE_AppDefined,
899
0
                             "Attempt to write non-polygon (%s) geometry to "
900
0
                             "POLYGON type shapefile.",
901
0
                             poSubGeom->getGeometryName());
902
903
0
                    return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
904
0
                }
905
0
                const OGRPolygon *poPoly = poSubGeom->toPolygon();
906
907
                // Ignore POLYGON EMPTY.
908
0
                if (poPoly->getExteriorRing() == nullptr ||
909
0
                    poPoly->getExteriorRing()->IsEmpty())
910
0
                {
911
0
                    CPLDebug("OGR",
912
0
                             "Ignore POLYGON EMPTY inside MULTIPOLYGON in "
913
0
                             "shapefile writer.");
914
0
                    continue;
915
0
                }
916
917
0
                const int nNumInteriorRings = poPoly->getNumInteriorRings();
918
                // to avoid coverity scan warning: "To avoid a quadratic time
919
                // penalty when using reserve(), always increase the capacity
920
                /// by a multiple of its current value"
921
0
                if (apoRings.size() + nNumInteriorRings + 1 >
922
0
                        apoRings.capacity() &&
923
0
                    apoRings.size() < std::numeric_limits<size_t>::max() / 2)
924
0
                {
925
0
                    apoRings.reserve(std::max(
926
0
                        2 * apoRings.size(), apoRings.size() + apoRings.size() +
927
0
                                                 nNumInteriorRings + 1));
928
0
                }
929
0
                bool bFirstRing = true;
930
0
                for (const auto poRing : poPoly)
931
0
                {
932
0
                    const int nNumPoints = poRing->getNumPoints();
933
934
                    // Ignore LINEARRING EMPTY.
935
0
                    if (nNumPoints != 0)
936
0
                    {
937
0
                        apoRings.push_back(std::make_pair(poRing, bFirstRing));
938
0
                    }
939
0
                    else
940
0
                    {
941
0
                        CPLDebug("OGR",
942
0
                                 "Ignore LINEARRING EMPTY inside POLYGON in "
943
0
                                 "shapefile writer.");
944
0
                    }
945
0
                    bFirstRing = false;
946
0
                }
947
0
            }
948
0
        }
949
4
        else
950
4
        {
951
4
            CPLError(CE_Failure, CPLE_AppDefined,
952
4
                     "Attempt to write non-polygon (%s) geometry to "
953
4
                     "POLYGON type shapefile.",
954
4
                     poGeom->getGeometryName());
955
956
4
            return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
957
4
        }
958
959
        /* --------------------------------------------------------------------
960
         */
961
        /*      If we only had emptypolygons or unacceptable geometries */
962
        /*      write NULL geometry object. */
963
        /* --------------------------------------------------------------------
964
         */
965
0
        if (apoRings.empty())
966
0
        {
967
0
            SHPObject *psShape =
968
0
                SHPCreateObject(SHPT_NULL, -1, 0, nullptr, nullptr, 0, nullptr,
969
0
                                nullptr, nullptr, nullptr);
970
0
            const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
971
0
            SHPDestroyObject(psShape);
972
973
0
            if (nReturnedShapeID == -1)
974
0
                return OGRERR_FAILURE;
975
976
0
            return OGRERR_NONE;
977
0
        }
978
979
        // Count vertices.
980
0
        int nVertex = 0;
981
0
        for (const auto &ring : apoRings)
982
0
            nVertex += ring.first->getNumPoints();
983
984
0
        const bool bHasZ = (hSHP->nShapeType == SHPT_POLYGONM ||
985
0
                            hSHP->nShapeType == SHPT_POLYGONZ);
986
0
        const bool bHasM = wkbHasM(eLayerGeomType) && bHasZ;
987
0
        const bool bIsGeomMeasured = poGeom->IsMeasured();
988
989
0
        std::vector<int> anRingStart;
990
0
        std::vector<double> adfX;
991
0
        std::vector<double> adfY;
992
0
        std::vector<double> adfZ;
993
0
        std::vector<double> adfM;
994
0
        try
995
0
        {
996
0
            anRingStart.reserve(apoRings.size());
997
0
            adfX.reserve(nVertex);
998
0
            adfY.reserve(nVertex);
999
0
            if (bHasZ)
1000
0
                adfZ.reserve(nVertex);
1001
0
            if (bHasM)
1002
0
                adfM.reserve(nVertex);
1003
0
        }
1004
0
        catch (const std::exception &e)
1005
0
        {
1006
0
            CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1007
0
            return OGRERR_FAILURE;
1008
0
        }
1009
1010
        // Collect vertices.
1011
0
        for (const auto &ring : apoRings)
1012
0
        {
1013
0
            const auto poRing = ring.first;
1014
0
            const bool bIsOuterRing = ring.second;
1015
0
            anRingStart.push_back(static_cast<int>(adfX.size()));
1016
1017
0
            const int nNumPoints = poRing->getNumPoints();
1018
            // Exterior ring must be clockwise oriented in shapefiles
1019
0
            const bool bInvertOrder =
1020
0
                !bRewind &&
1021
0
                (bIsOuterRing ? !poRing->isClockwise() : poRing->isClockwise());
1022
0
            for (int i = 0; i < nNumPoints; i++)
1023
0
            {
1024
0
                const int iPoint = bInvertOrder ? nNumPoints - 1 - i : i;
1025
0
                adfX.push_back(poRing->getX(iPoint));
1026
0
                adfY.push_back(poRing->getY(iPoint));
1027
0
                if (bHasZ)
1028
0
                    adfZ.push_back(poRing->getZ(iPoint));
1029
0
                if (bHasM)
1030
0
                {
1031
0
                    adfM.push_back(bIsGeomMeasured
1032
0
                                       ? poRing->getM(iPoint)
1033
0
                                       : -std::numeric_limits<double>::max());
1034
0
                }
1035
0
            }
1036
0
        }
1037
0
        if (!CheckNonFiniteCoordinates(adfX) ||
1038
0
            !CheckNonFiniteCoordinates(adfY) ||
1039
0
            !CheckNonFiniteCoordinates(adfZ) ||
1040
0
            !CheckNonFiniteCoordinates(adfM))
1041
0
        {
1042
0
            return OGRERR_FAILURE;
1043
0
        }
1044
1045
0
        SHPObject *psShape = SHPCreateObject(
1046
0
            hSHP->nShapeType, iShape, static_cast<int>(anRingStart.size()),
1047
0
            anRingStart.data(), nullptr, static_cast<int>(adfX.size()),
1048
0
            adfX.data(), adfY.data(), bHasZ ? adfZ.data() : nullptr,
1049
0
            bHasM ? adfM.data() : nullptr);
1050
0
        if (bRewind)
1051
0
            SHPRewindObject(hSHP, psShape);
1052
0
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
1053
0
        SHPDestroyObject(psShape);
1054
1055
0
        if (nReturnedShapeID == -1)
1056
0
            return OGRERR_FAILURE;
1057
0
    }
1058
1059
    /* ==================================================================== */
1060
    /*      Multipatch                                                      */
1061
    /* ==================================================================== */
1062
1
    else if (hSHP->nShapeType == SHPT_MULTIPATCH)
1063
1
    {
1064
1
        int nParts = 0;
1065
1
        std::vector<int> anPartStart;
1066
1
        std::vector<int> anPartType;
1067
1
        int nPoints = 0;
1068
1
        std::vector<OGRRawPoint> aoPoints;
1069
1
        std::vector<double> adfZ;
1070
1
        OGRErr eErr = OGRCreateMultiPatch(poGeom,
1071
1
                                          FALSE,  // no SHPP_TRIANGLES
1072
1
                                          nParts, anPartStart, anPartType,
1073
1
                                          nPoints, aoPoints, adfZ);
1074
1
        if (eErr != OGRERR_NONE)
1075
1
            return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
1076
1077
0
        std::vector<double> adfX(nPoints);
1078
0
        std::vector<double> adfY(nPoints);
1079
0
        for (int i = 0; i < nPoints; ++i)
1080
0
        {
1081
0
            adfX[i] = aoPoints[i].x;
1082
0
            adfY[i] = aoPoints[i].y;
1083
0
        }
1084
1085
0
        if (!CheckNonFiniteCoordinates(adfX.data(), nPoints) ||
1086
0
            !CheckNonFiniteCoordinates(adfY.data(), nPoints) ||
1087
0
            !CheckNonFiniteCoordinates(adfZ.data(), nPoints))
1088
0
        {
1089
0
            return OGRERR_FAILURE;
1090
0
        }
1091
1092
0
        SHPObject *psShape =
1093
0
            SHPCreateObject(hSHP->nShapeType, iShape, nParts,
1094
0
                            anPartStart.data(), anPartType.data(), nPoints,
1095
0
                            adfX.data(), adfY.data(), adfZ.data(), nullptr);
1096
0
        if (bRewind)
1097
0
            SHPRewindObject(hSHP, psShape);
1098
0
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
1099
0
        SHPDestroyObject(psShape);
1100
1101
0
        if (nReturnedShapeID == -1)
1102
0
            return OGRERR_FAILURE;
1103
0
    }
1104
1105
0
    else
1106
0
    {
1107
0
        return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
1108
0
    }
1109
1110
232k
    return OGRERR_NONE;
1111
233k
}
1112
1113
/************************************************************************/
1114
/*                       SHPReadOGRFeatureDefn()                        */
1115
/************************************************************************/
1116
1117
OGRFeatureDefnRefCountedPtr
1118
SHPReadOGRFeatureDefn(const char *pszName, SHPHandle hSHP, DBFHandle hDBF,
1119
                      VSILFILE *fpSHPXML, const char *pszSHPEncoding,
1120
                      int bAdjustType)
1121
1122
13.0k
{
1123
13.0k
    int nAdjustableFields = 0;
1124
13.0k
    const int nFieldCount = hDBF ? DBFGetFieldCount(hDBF) : 0;
1125
1126
13.0k
    auto poDefn = OGRFeatureDefnRefCountedPtr::makeInstance(pszName);
1127
1128
    // Parse .shp.xml side car if available, to get long field names and aliases
1129
    // but only if they are consistent with the number of DBF fields and their
1130
    // content.
1131
13.0k
    std::vector<OGRFieldDefn> aFieldsFromSHPXML;
1132
13.0k
    if (fpSHPXML)
1133
0
    {
1134
0
        fpSHPXML->Seek(0, SEEK_END);
1135
0
        const auto nSize = fpSHPXML->Tell();
1136
0
        if (nSize < 10 * 1024 * 1024)
1137
0
        {
1138
0
            fpSHPXML->Seek(0, SEEK_SET);
1139
0
            GByte *pabyOut = nullptr;
1140
0
            if (VSIIngestFile(fpSHPXML, nullptr, &pabyOut, nullptr, -1))
1141
0
            {
1142
0
                const char *pszXML = reinterpret_cast<char *>(pabyOut);
1143
0
                CPLXMLTreeCloser oTree(CPLParseXMLString(pszXML));
1144
0
                if (oTree)
1145
0
                {
1146
0
                    const CPLXMLNode *psFields =
1147
0
                        CPLGetXMLNode(oTree.get(), "=metadata.eainfo.detailed");
1148
0
                    int iField = 0;
1149
0
                    for (const CPLXMLNode *psIter = psFields ? psFields->psChild
1150
0
                                                             : nullptr;
1151
0
                         psIter; psIter = psIter->psNext)
1152
0
                    {
1153
0
                        if (psIter->eType != CXT_Element ||
1154
0
                            strcmp(psIter->pszValue, "attr") != 0)
1155
0
                            continue;
1156
0
                        const char *pszType =
1157
0
                            CPLGetXMLValue(psIter, "attrtype", "");
1158
0
                        if (strcmp(pszType, "OID") == 0 ||
1159
0
                            strcmp(pszType, "Geometry") == 0)
1160
0
                            continue;
1161
0
                        const char *pszLabel =
1162
0
                            CPLGetXMLValue(psIter, "attrlabl", "");
1163
0
                        if (iField == nFieldCount)
1164
0
                        {
1165
0
                            CPLDebug("Shape",
1166
0
                                     "More fields in .shp.xml than in .dbf");
1167
0
                            aFieldsFromSHPXML.clear();
1168
0
                            break;
1169
0
                        }
1170
1171
0
                        char szFieldNameFromDBF[XBASE_FLDNAME_LEN_READ + 1] =
1172
0
                            {};
1173
0
                        int nWidth = 0;
1174
0
                        int nPrecision = 0;
1175
0
                        CPL_IGNORE_RET_VAL(
1176
0
                            DBFGetFieldInfo(hDBF, iField, szFieldNameFromDBF,
1177
0
                                            &nWidth, &nPrecision));
1178
0
                        std::string osFieldNameFromDBF(szFieldNameFromDBF);
1179
0
                        if (strlen(pszSHPEncoding) > 0)
1180
0
                        {
1181
0
                            char *pszUTF8Field =
1182
0
                                CPLRecode(szFieldNameFromDBF, pszSHPEncoding,
1183
0
                                          CPL_ENC_UTF8);
1184
0
                            osFieldNameFromDBF = pszUTF8Field;
1185
0
                            CPLFree(pszUTF8Field);
1186
0
                        }
1187
                        // Check that field names are consistent
1188
0
                        if ((strlen(pszLabel) < 10 &&
1189
0
                             pszLabel != osFieldNameFromDBF) ||
1190
0
                            (strlen(pszLabel) >= 10 &&
1191
0
                             osFieldNameFromDBF.size() >= 10 &&
1192
0
                             strncmp(pszLabel, osFieldNameFromDBF.c_str(), 5) !=
1193
0
                                 0))
1194
0
                        {
1195
0
                            CPLDebug("Shape",
1196
0
                                     "For field at index %d, mismatch between "
1197
0
                                     ".shp.xml name (%s) vs .dbf name (%s)",
1198
0
                                     iField, pszLabel, szFieldNameFromDBF);
1199
0
                            aFieldsFromSHPXML.clear();
1200
0
                            break;
1201
0
                        }
1202
1203
0
                        OGRFieldDefn oField(pszLabel, OFTMaxType);
1204
0
                        const char *pszAlias =
1205
0
                            CPLGetXMLValue(psIter, "attalias", "");
1206
0
                        if (pszAlias[0] && strcmp(pszLabel, pszAlias) != 0)
1207
0
                            oField.SetAlternativeName(pszAlias);
1208
0
                        const char *pszWidth =
1209
0
                            CPLGetXMLValue(psIter, "attwidth", "");
1210
0
                        if (strcmp(pszType, "Integer") == 0 &&
1211
0
                            strcmp(pszWidth, "4") == 0)
1212
0
                            oField.SetType(OFTInteger);
1213
0
                        aFieldsFromSHPXML.push_back(std::move(oField));
1214
0
                        ++iField;
1215
0
                    }
1216
0
                    if (iField != nFieldCount)
1217
0
                        aFieldsFromSHPXML.clear();
1218
0
                }
1219
0
            }
1220
0
            CPLFree(pabyOut);
1221
0
        }
1222
0
    }
1223
1224
266k
    for (int iField = 0; iField < nFieldCount; iField++)
1225
253k
    {
1226
        // On reading we support up to 11 characters
1227
253k
        char szFieldName[XBASE_FLDNAME_LEN_READ + 1] = {};
1228
253k
        int nWidth = 0;
1229
253k
        int nPrecision = 0;
1230
253k
        DBFFieldType eDBFType =
1231
253k
            DBFGetFieldInfo(hDBF, iField, szFieldName, &nWidth, &nPrecision);
1232
1233
253k
        OGRFieldDefn oField("", OFTInteger);
1234
253k
        if (!aFieldsFromSHPXML.empty())
1235
0
        {
1236
0
            oField = aFieldsFromSHPXML[iField];
1237
0
        }
1238
253k
        else if (strlen(pszSHPEncoding) > 0)
1239
69.3k
        {
1240
69.3k
            char *const pszUTF8Field =
1241
69.3k
                CPLRecode(szFieldName, pszSHPEncoding, CPL_ENC_UTF8);
1242
69.3k
            oField.SetName(pszUTF8Field);
1243
69.3k
            CPLFree(pszUTF8Field);
1244
69.3k
        }
1245
183k
        else
1246
183k
        {
1247
183k
            oField.SetName(szFieldName);
1248
183k
        }
1249
1250
253k
        oField.SetWidth(nWidth);
1251
253k
        oField.SetPrecision(nPrecision);
1252
1253
253k
        if (eDBFType == FTDate)
1254
3.26k
        {
1255
            // Shapefile date has following 8-chars long format:
1256
            //
1257
            //     20060101.
1258
            //
1259
            // Split as YYYY/MM/DD, so 2 additional characters are required.
1260
3.26k
            oField.SetWidth(nWidth + 2);
1261
3.26k
            oField.SetType(OFTDate);
1262
3.26k
        }
1263
249k
        else if (eDBFType == FTDouble)
1264
14.8k
        {
1265
14.8k
            nAdjustableFields += (nPrecision == 0);
1266
14.8k
            if (nPrecision == 0 && nWidth < 19)
1267
293
            {
1268
293
                if (!aFieldsFromSHPXML.empty() &&
1269
0
                    aFieldsFromSHPXML[iField].GetType() == OFTInteger)
1270
0
                    oField.SetType(OFTInteger);
1271
293
                else
1272
293
                    oField.SetType(OFTInteger64);
1273
293
            }
1274
14.5k
            else
1275
14.5k
                oField.SetType(OFTReal);
1276
14.8k
        }
1277
235k
        else if (eDBFType == FTInteger)
1278
665
            oField.SetType(OFTInteger);
1279
234k
        else if (eDBFType == FTLogical)
1280
5.15k
        {
1281
5.15k
            oField.SetType(OFTInteger);
1282
5.15k
            oField.SetSubType(OFSTBoolean);
1283
5.15k
        }
1284
229k
        else
1285
229k
            oField.SetType(OFTString);
1286
1287
253k
        poDefn->AddFieldDefn(&oField);
1288
253k
    }
1289
1290
    // Do an optional past if requested and needed to demote Integer64->Integer
1291
    // or Real->Integer64/Integer.
1292
13.0k
    if (nAdjustableFields && bAdjustType)
1293
0
    {
1294
0
        int *panAdjustableField =
1295
0
            static_cast<int *>(CPLCalloc(sizeof(int), nFieldCount));
1296
0
        for (int iField = 0; iField < nFieldCount; iField++)
1297
0
        {
1298
0
            OGRFieldType eType = poDefn->GetFieldDefn(iField)->GetType();
1299
0
            if (poDefn->GetFieldDefn(iField)->GetPrecision() == 0 &&
1300
0
                (eType == OFTInteger64 || eType == OFTReal))
1301
0
            {
1302
0
                panAdjustableField[iField] = TRUE;
1303
0
                poDefn->GetFieldDefn(iField)->SetType(OFTInteger);
1304
0
            }
1305
0
        }
1306
1307
0
        const int nRowCount = DBFGetRecordCount(hDBF);
1308
0
        for (int iRow = 0; iRow < nRowCount && nAdjustableFields; iRow++)
1309
0
        {
1310
0
            for (int iField = 0; iField < nFieldCount; iField++)
1311
0
            {
1312
0
                if (panAdjustableField[iField])
1313
0
                {
1314
0
                    const char *pszValue =
1315
0
                        DBFReadStringAttribute(hDBF, iRow, iField);
1316
0
                    const int nValueLength = static_cast<int>(strlen(pszValue));
1317
0
                    if (nValueLength >= 10)
1318
0
                    {
1319
0
                        int bOverflow = FALSE;
1320
0
                        const GIntBig nVal =
1321
0
                            CPLAtoGIntBigEx(pszValue, FALSE, &bOverflow);
1322
0
                        if (bOverflow)
1323
0
                        {
1324
0
                            poDefn->GetFieldDefn(iField)->SetType(OFTReal);
1325
0
                            panAdjustableField[iField] = FALSE;
1326
0
                            nAdjustableFields--;
1327
0
                        }
1328
0
                        else if (!CPL_INT64_FITS_ON_INT32(nVal))
1329
0
                        {
1330
0
                            poDefn->GetFieldDefn(iField)->SetType(OFTInteger64);
1331
0
                            if (poDefn->GetFieldDefn(iField)->GetWidth() <= 18)
1332
0
                            {
1333
0
                                panAdjustableField[iField] = FALSE;
1334
0
                                nAdjustableFields--;
1335
0
                            }
1336
0
                        }
1337
0
                    }
1338
0
                }
1339
0
            }
1340
0
        }
1341
1342
0
        CPLFree(panAdjustableField);
1343
0
    }
1344
1345
13.0k
    if (hSHP == nullptr)
1346
9.46k
    {
1347
9.46k
        poDefn->SetGeomType(wkbNone);
1348
9.46k
    }
1349
3.61k
    else
1350
3.61k
    {
1351
3.61k
        switch (hSHP->nShapeType)
1352
3.61k
        {
1353
54
            case SHPT_POINT:
1354
54
                poDefn->SetGeomType(wkbPoint);
1355
54
                break;
1356
1357
13
            case SHPT_POINTZ:
1358
13
                poDefn->SetGeomType(wkbPointZM);
1359
13
                break;
1360
1361
3
            case SHPT_POINTM:
1362
3
                poDefn->SetGeomType(wkbPointM);
1363
3
                break;
1364
1365
2.45k
            case SHPT_ARC:
1366
2.45k
                poDefn->SetGeomType(wkbLineString);
1367
2.45k
                break;
1368
1369
119
            case SHPT_ARCZ:
1370
119
                poDefn->SetGeomType(wkbLineStringZM);
1371
119
                break;
1372
1373
78
            case SHPT_ARCM:
1374
78
                poDefn->SetGeomType(wkbLineStringM);
1375
78
                break;
1376
1377
18
            case SHPT_MULTIPOINT:
1378
18
                poDefn->SetGeomType(wkbMultiPoint);
1379
18
                break;
1380
1381
16
            case SHPT_MULTIPOINTZ:
1382
16
                poDefn->SetGeomType(wkbMultiPointZM);
1383
16
                break;
1384
1385
4
            case SHPT_MULTIPOINTM:
1386
4
                poDefn->SetGeomType(wkbMultiPointM);
1387
4
                break;
1388
1389
186
            case SHPT_POLYGON:
1390
186
                poDefn->SetGeomType(wkbPolygon);
1391
186
                break;
1392
1393
17
            case SHPT_POLYGONZ:
1394
17
                poDefn->SetGeomType(wkbPolygonZM);
1395
17
                break;
1396
1397
1
            case SHPT_POLYGONM:
1398
1
                poDefn->SetGeomType(wkbPolygonM);
1399
1
                break;
1400
1401
16
            case SHPT_MULTIPATCH:
1402
16
                poDefn->SetGeomType(wkbUnknown);  // not ideal
1403
16
                break;
1404
3.61k
        }
1405
3.61k
    }
1406
1407
13.0k
    return poDefn;
1408
13.0k
}
1409
1410
/************************************************************************/
1411
/*                         SHPReadOGRFeature()                          */
1412
/************************************************************************/
1413
1414
OGRFeature *SHPReadOGRFeature(SHPHandle hSHP, DBFHandle hDBF,
1415
                              OGRFeatureDefn *poDefn, int iShape,
1416
                              SHPObject *psShape, const char *pszSHPEncoding,
1417
                              bool &bHasWarnedWrongWindingOrder)
1418
1419
151k
{
1420
151k
    if (iShape < 0 || (hSHP != nullptr && iShape >= hSHP->nRecords) ||
1421
151k
        (hDBF != nullptr && iShape >= hDBF->nRecords))
1422
0
    {
1423
0
        CPLError(CE_Failure, CPLE_AppDefined,
1424
0
                 "Attempt to read shape with feature id (%d) out of available"
1425
0
                 " range.",
1426
0
                 iShape);
1427
0
        return nullptr;
1428
0
    }
1429
1430
151k
    if (hDBF && DBFIsRecordDeleted(hDBF, iShape))
1431
0
    {
1432
0
        CPLError(CE_Failure, CPLE_AppDefined,
1433
0
                 "Attempt to read shape with feature id (%d), "
1434
0
                 "but it is marked deleted.",
1435
0
                 iShape);
1436
0
        if (psShape != nullptr)
1437
0
            SHPDestroyObject(psShape);
1438
0
        return nullptr;
1439
0
    }
1440
1441
151k
    OGRFeature *poFeature = new OGRFeature(poDefn);
1442
1443
    /* -------------------------------------------------------------------- */
1444
    /*      Fetch geometry from Shapefile to OGRFeature.                    */
1445
    /* -------------------------------------------------------------------- */
1446
151k
    if (hSHP != nullptr)
1447
42.8k
    {
1448
42.8k
        if (!poDefn->IsGeometryIgnored())
1449
42.8k
        {
1450
42.8k
            auto poGeometry = SHPReadOGRObject(hSHP, iShape, psShape,
1451
42.8k
                                               bHasWarnedWrongWindingOrder,
1452
42.8k
                                               poDefn->GetGeomType());
1453
1454
            // Two possibilities are expected here (both are tested by
1455
            // GDAL Autotests):
1456
            //   1. Read valid geometry and assign it directly.
1457
            //   2. Read and assign null geometry if it can not be read
1458
            //      correctly from a shapefile.
1459
            //
1460
            // It is NOT required here to test poGeometry == NULL.
1461
1462
42.8k
            if (poGeometry)
1463
982
            {
1464
                // Set/unset flags.
1465
982
                const OGRwkbGeometryType eMyGeomType =
1466
982
                    poFeature->GetDefnRef()->GetGeomFieldDefn(0)->GetType();
1467
1468
982
                if (eMyGeomType != wkbUnknown)
1469
869
                {
1470
869
                    OGRwkbGeometryType eGeomInType =
1471
869
                        poGeometry->getGeometryType();
1472
869
                    if (wkbHasZ(eMyGeomType) && !wkbHasZ(eGeomInType))
1473
112
                    {
1474
112
                        poGeometry->set3D(TRUE);
1475
112
                    }
1476
757
                    else if (!wkbHasZ(eMyGeomType) && wkbHasZ(eGeomInType))
1477
504
                    {
1478
504
                        poGeometry->set3D(FALSE);
1479
504
                    }
1480
869
                    if (wkbHasM(eMyGeomType) && !wkbHasM(eGeomInType))
1481
107
                    {
1482
107
                        poGeometry->setMeasured(TRUE);
1483
107
                    }
1484
762
                    else if (!wkbHasM(eMyGeomType) && wkbHasM(eGeomInType))
1485
204
                    {
1486
204
                        poGeometry->setMeasured(FALSE);
1487
204
                    }
1488
869
                }
1489
982
            }
1490
1491
42.8k
            poFeature->SetGeometry(std::move(poGeometry));
1492
42.8k
        }
1493
0
        else if (psShape != nullptr)
1494
0
        {
1495
0
            SHPDestroyObject(psShape);
1496
0
        }
1497
42.8k
    }
1498
1499
    /* -------------------------------------------------------------------- */
1500
    /*      Fetch feature attributes to OGRFeature fields.                  */
1501
    /* -------------------------------------------------------------------- */
1502
1503
870k
    for (int iField = 0; hDBF != nullptr && iField < poDefn->GetFieldCount();
1504
719k
         iField++)
1505
719k
    {
1506
719k
        const OGRFieldDefn *const poFieldDefn = poDefn->GetFieldDefn(iField);
1507
719k
        if (poFieldDefn->IsIgnored())
1508
0
            continue;
1509
1510
719k
        switch (poFieldDefn->GetType())
1511
719k
        {
1512
546k
            case OFTString:
1513
546k
            {
1514
546k
                const char *const pszFieldVal =
1515
546k
                    DBFReadStringAttribute(hDBF, iShape, iField);
1516
546k
                if (pszFieldVal != nullptr && pszFieldVal[0] != '\0')
1517
437k
                {
1518
437k
                    if (pszSHPEncoding[0] != '\0')
1519
44.5k
                    {
1520
44.5k
                        char *const pszUTF8Field = CPLRecode(
1521
44.5k
                            pszFieldVal, pszSHPEncoding, CPL_ENC_UTF8);
1522
44.5k
                        poFeature->SetField(iField, pszUTF8Field);
1523
44.5k
                        CPLFree(pszUTF8Field);
1524
44.5k
                    }
1525
392k
                    else
1526
392k
                        poFeature->SetField(iField, pszFieldVal);
1527
437k
                }
1528
109k
                else
1529
109k
                {
1530
109k
                    poFeature->SetFieldNull(iField);
1531
109k
                }
1532
546k
                break;
1533
0
            }
1534
46.4k
            case OFTInteger:
1535
47.3k
            case OFTInteger64:
1536
125k
            case OFTReal:
1537
125k
            {
1538
125k
                if (DBFIsAttributeNULL(hDBF, iShape, iField))
1539
9.85k
                {
1540
9.85k
                    poFeature->SetFieldNull(iField);
1541
9.85k
                }
1542
115k
                else
1543
115k
                {
1544
115k
                    if (poFieldDefn->GetSubType() == OFSTBoolean)
1545
44.4k
                    {
1546
44.4k
                        const char *pszVal =
1547
44.4k
                            DBFReadLogicalAttribute(hDBF, iShape, iField);
1548
44.4k
                        poFeature->SetField(
1549
44.4k
                            iField, pszVal[0] == 'T' || pszVal[0] == 't' ||
1550
43.0k
                                            pszVal[0] == 'Y' || pszVal[0] == 'y'
1551
44.4k
                                        ? 1
1552
44.4k
                                        : 0);
1553
44.4k
                    }
1554
71.1k
                    else
1555
71.1k
                    {
1556
71.1k
                        const char *pszVal =
1557
71.1k
                            DBFReadStringAttribute(hDBF, iShape, iField);
1558
71.1k
                        poFeature->SetField(iField, pszVal);
1559
71.1k
                    }
1560
115k
                }
1561
125k
                break;
1562
47.3k
            }
1563
47.1k
            case OFTDate:
1564
47.1k
            {
1565
47.1k
                if (DBFIsAttributeNULL(hDBF, iShape, iField))
1566
8.58k
                {
1567
8.58k
                    poFeature->SetFieldNull(iField);
1568
8.58k
                    continue;
1569
8.58k
                }
1570
1571
38.5k
                const char *const pszDateValue =
1572
38.5k
                    DBFReadStringAttribute(hDBF, iShape, iField);
1573
1574
38.5k
                OGRField sFld;
1575
38.5k
                memset(&sFld, 0, sizeof(sFld));
1576
1577
38.5k
                if (strlen(pszDateValue) >= 10 && pszDateValue[2] == '/' &&
1578
2.25k
                    pszDateValue[5] == '/')
1579
1.39k
                {
1580
1.39k
                    sFld.Date.Month =
1581
1.39k
                        static_cast<GByte>(atoi(pszDateValue + 0));
1582
1.39k
                    sFld.Date.Day = static_cast<GByte>(atoi(pszDateValue + 3));
1583
1.39k
                    sFld.Date.Year =
1584
1.39k
                        static_cast<GInt16>(atoi(pszDateValue + 6));
1585
1.39k
                }
1586
37.1k
                else
1587
37.1k
                {
1588
37.1k
                    const int nFullDate = atoi(pszDateValue);
1589
37.1k
                    sFld.Date.Year = static_cast<GInt16>(nFullDate / 10000);
1590
37.1k
                    sFld.Date.Month =
1591
37.1k
                        static_cast<GByte>((nFullDate / 100) % 100);
1592
37.1k
                    sFld.Date.Day = static_cast<GByte>(nFullDate % 100);
1593
37.1k
                }
1594
1595
38.5k
                poFeature->SetField(iField, &sFld);
1596
38.5k
            }
1597
0
            break;
1598
1599
0
            default:
1600
0
                CPLAssert(false);
1601
719k
        }
1602
719k
    }
1603
1604
151k
    if (poFeature != nullptr)
1605
151k
        poFeature->SetFID(iShape);
1606
1607
151k
    return poFeature;
1608
151k
}
1609
1610
/************************************************************************/
1611
/*                             GrowField()                              */
1612
/************************************************************************/
1613
1614
static OGRErr GrowField(DBFHandle hDBF, int iField, OGRFieldDefn *poFieldDefn,
1615
                        int nNewSize)
1616
4.11k
{
1617
4.11k
    char szFieldName[20] = {};
1618
4.11k
    int nOriWidth = 0;
1619
4.11k
    int nPrecision = 0;
1620
4.11k
    DBFGetFieldInfo(hDBF, iField, szFieldName, &nOriWidth, &nPrecision);
1621
1622
4.11k
    CPLDebug("SHAPE", "Extending field %d (%s) from %d to %d characters",
1623
4.11k
             iField, poFieldDefn->GetNameRef(), nOriWidth, nNewSize);
1624
1625
4.11k
    const char chNativeType = DBFGetNativeFieldType(hDBF, iField);
1626
4.11k
    if (!DBFAlterFieldDefn(hDBF, iField, szFieldName, chNativeType, nNewSize,
1627
4.11k
                           nPrecision))
1628
1.16k
    {
1629
1.16k
        CPLError(CE_Failure, CPLE_AppDefined,
1630
1.16k
                 "Extending field %d (%s) from %d to %d characters failed",
1631
1.16k
                 iField, poFieldDefn->GetNameRef(), nOriWidth, nNewSize);
1632
1.16k
        return OGRERR_FAILURE;
1633
1.16k
    }
1634
1635
2.95k
    auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
1636
2.95k
    poFieldDefn->SetWidth(nNewSize);
1637
2.95k
    return OGRERR_NONE;
1638
4.11k
}
1639
1640
/************************************************************************/
1641
/*                         SHPWriteOGRFeature()                         */
1642
/*                                                                      */
1643
/*      Write to an existing feature in a shapefile, or create a new    */
1644
/*      feature.                                                        */
1645
/************************************************************************/
1646
1647
OGRErr SHPWriteOGRFeature(SHPHandle hSHP, DBFHandle hDBF,
1648
                          OGRFeatureDefn *poDefn, OGRFeature *poFeature,
1649
                          const char *pszSHPEncoding,
1650
                          bool *pbTruncationWarningEmitted, bool bRewind)
1651
1652
501k
{
1653
    /* -------------------------------------------------------------------- */
1654
    /*      Write the geometry.                                             */
1655
    /* -------------------------------------------------------------------- */
1656
501k
    if (hSHP != nullptr)
1657
233k
    {
1658
233k
        const OGRErr eErr = SHPWriteOGRObject(
1659
233k
            hSHP, static_cast<int>(poFeature->GetFID()),
1660
233k
            poFeature->GetGeometryRef(), bRewind, poDefn->GetGeomType());
1661
233k
        if (eErr != OGRERR_NONE)
1662
966
            return eErr;
1663
233k
    }
1664
1665
    /* -------------------------------------------------------------------- */
1666
    /*      If there is no DBF, the job is done now.                        */
1667
    /* -------------------------------------------------------------------- */
1668
500k
    if (hDBF == nullptr)
1669
0
    {
1670
        /* --------------------------------------------------------------------
1671
         */
1672
        /*      If this is a new feature, establish its feature id. */
1673
        /* --------------------------------------------------------------------
1674
         */
1675
0
        if (hSHP != nullptr && poFeature->GetFID() == OGRNullFID)
1676
0
            poFeature->SetFID(hSHP->nRecords - 1);
1677
1678
0
        return OGRERR_NONE;
1679
0
    }
1680
1681
    /* -------------------------------------------------------------------- */
1682
    /*      If this is a new feature, establish its feature id.             */
1683
    /* -------------------------------------------------------------------- */
1684
500k
    if (poFeature->GetFID() == OGRNullFID)
1685
500k
        poFeature->SetFID(DBFGetRecordCount(hDBF));
1686
1687
    /* -------------------------------------------------------------------- */
1688
    /*      If this is the first feature to be written, verify that we      */
1689
    /*      have at least one attribute in the DBF file.  If not, create    */
1690
    /*      a dummy FID attribute to satisfy the requirement that there     */
1691
    /*      be at least one attribute.                                      */
1692
    /* -------------------------------------------------------------------- */
1693
500k
    if (DBFGetRecordCount(hDBF) == 0 && DBFGetFieldCount(hDBF) == 0)
1694
85
    {
1695
85
        CPLDebug(
1696
85
            "OGR",
1697
85
            "Created dummy FID field for shapefile since schema is empty.");
1698
85
        DBFAddField(hDBF, "FID", FTInteger, 11, 0);
1699
85
    }
1700
1701
    /* -------------------------------------------------------------------- */
1702
    /*      Write out dummy field value if it exists.                       */
1703
    /* -------------------------------------------------------------------- */
1704
500k
    if (poDefn->GetFieldCount() == 0)
1705
11.4k
    {
1706
11.4k
        if (DBFGetFieldCount(hDBF) == 1)
1707
11.3k
        {
1708
11.3k
            DBFWriteIntegerAttribute(hDBF,
1709
11.3k
                                     static_cast<int>(poFeature->GetFID()), 0,
1710
11.3k
                                     static_cast<int>(poFeature->GetFID()));
1711
11.3k
        }
1712
150
        else if (DBFGetFieldCount(hDBF) == 0)
1713
0
        {
1714
            // Far from being nominal... Could happen if deleting all fields
1715
            // of a DBF with rows
1716
0
            DBFWriteAttributeDirectly(
1717
0
                hDBF, static_cast<int>(poFeature->GetFID()), -1, nullptr);
1718
0
        }
1719
11.4k
    }
1720
1721
    /* -------------------------------------------------------------------- */
1722
    /*      Write all the fields.                                           */
1723
    /* -------------------------------------------------------------------- */
1724
2.90M
    for (int iField = 0; iField < poDefn->GetFieldCount(); iField++)
1725
2.40M
    {
1726
2.40M
        if (!poFeature->IsFieldSetAndNotNull(iField))
1727
1.92M
        {
1728
1.92M
            DBFWriteNULLAttribute(hDBF, static_cast<int>(poFeature->GetFID()),
1729
1.92M
                                  iField);
1730
1.92M
            continue;
1731
1.92M
        }
1732
1733
479k
        OGRFieldDefn *const poFieldDefn = poDefn->GetFieldDefn(iField);
1734
1735
479k
        switch (poFieldDefn->GetType())
1736
479k
        {
1737
475k
            case OFTString:
1738
475k
            {
1739
475k
                const char *pszStr = poFeature->GetFieldAsString(iField);
1740
475k
                char *pszEncoded = nullptr;
1741
475k
                if (pszSHPEncoding[0] != '\0')
1742
475k
                {
1743
475k
                    pszEncoded =
1744
475k
                        CPLRecode(pszStr, CPL_ENC_UTF8, pszSHPEncoding);
1745
475k
                    pszStr = pszEncoded;
1746
475k
                }
1747
1748
475k
                int nStrLen = static_cast<int>(strlen(pszStr));
1749
475k
                if (nStrLen > OGR_DBF_MAX_FIELD_WIDTH)
1750
6.96k
                {
1751
6.96k
                    if (!(*pbTruncationWarningEmitted))
1752
692
                    {
1753
692
                        *pbTruncationWarningEmitted = true;
1754
692
                        CPLError(
1755
692
                            CE_Warning, CPLE_AppDefined,
1756
692
                            "Value '%s' of field %s has been truncated to %d "
1757
692
                            "characters.  This warning will not be emitted any "
1758
692
                            "more for that layer.",
1759
692
                            poFeature->GetFieldAsString(iField),
1760
692
                            poFieldDefn->GetNameRef(), OGR_DBF_MAX_FIELD_WIDTH);
1761
692
                    }
1762
1763
6.96k
                    nStrLen = OGR_DBF_MAX_FIELD_WIDTH;
1764
1765
6.96k
                    if (pszEncoded != nullptr &&  // For Coverity.
1766
6.96k
                        EQUAL(pszSHPEncoding, CPL_ENC_UTF8))
1767
0
                    {
1768
                        // Truncate string by making sure we don't cut in the
1769
                        // middle of a UTF-8 multibyte character
1770
                        // Continuation bytes of such characters are of the form
1771
                        // 10xxxxxx (0x80), whereas single-byte are 0xxxxxxx
1772
                        // and the start of a multi-byte is 11xxxxxx
1773
0
                        const char *p = pszStr + nStrLen;
1774
0
                        while (nStrLen > 0)
1775
0
                        {
1776
0
                            if ((*p & 0xc0) != 0x80)
1777
0
                            {
1778
0
                                break;
1779
0
                            }
1780
1781
0
                            nStrLen--;
1782
0
                            p--;
1783
0
                        }
1784
1785
0
                        pszEncoded[nStrLen] = 0;
1786
0
                    }
1787
6.96k
                }
1788
1789
475k
                if (nStrLen > poFieldDefn->GetWidth())
1790
4.07k
                {
1791
4.07k
                    if (GrowField(hDBF, iField, poFieldDefn, nStrLen) !=
1792
4.07k
                        OGRERR_NONE)
1793
1.16k
                    {
1794
1.16k
                        CPLFree(pszEncoded);
1795
1.16k
                        return OGRERR_FAILURE;
1796
1.16k
                    }
1797
4.07k
                }
1798
1799
474k
                DBFWriteStringAttribute(hDBF,
1800
474k
                                        static_cast<int>(poFeature->GetFID()),
1801
474k
                                        iField, pszStr);
1802
1803
474k
                CPLFree(pszEncoded);
1804
474k
                break;
1805
475k
            }
1806
3.15k
            case OFTInteger:
1807
3.24k
            case OFTInteger64:
1808
3.24k
            {
1809
3.24k
                if (poFieldDefn->GetSubType() == OFSTBoolean)
1810
0
                {
1811
0
                    DBFWriteAttributeDirectly(
1812
0
                        hDBF, static_cast<int>(poFeature->GetFID()), iField,
1813
0
                        poFeature->GetFieldAsInteger(iField) ? "T" : "F");
1814
0
                }
1815
3.24k
                else
1816
3.24k
                {
1817
3.24k
                    char szValue[32] = {};
1818
3.24k
                    const int nFieldWidth = poFieldDefn->GetWidth();
1819
3.24k
                    snprintf(szValue, sizeof(szValue),
1820
3.24k
                             "%*" CPL_FRMT_GB_WITHOUT_PREFIX "d",
1821
3.24k
                             std::min(nFieldWidth,
1822
3.24k
                                      static_cast<int>(sizeof(szValue)) - 1),
1823
3.24k
                             poFeature->GetFieldAsInteger64(iField));
1824
1825
3.24k
                    const int nStrLen = static_cast<int>(strlen(szValue));
1826
3.24k
                    if (nStrLen > nFieldWidth)
1827
47
                    {
1828
47
                        if (GrowField(hDBF, iField, poFieldDefn, nStrLen) !=
1829
47
                            OGRERR_NONE)
1830
0
                        {
1831
0
                            return OGRERR_FAILURE;
1832
0
                        }
1833
47
                    }
1834
1835
3.24k
                    DBFWriteAttributeDirectly(
1836
3.24k
                        hDBF, static_cast<int>(poFeature->GetFID()), iField,
1837
3.24k
                        szValue);
1838
3.24k
                }
1839
1840
3.24k
                break;
1841
3.24k
            }
1842
1843
3.24k
            case OFTReal:
1844
503
            {
1845
503
                const double dfVal = poFeature->GetFieldAsDouble(iField);
1846
                // IEEE754 doubles can store exact values of all integers
1847
                // below 2^53.
1848
503
                if (poFieldDefn->GetPrecision() == 0 &&
1849
12
                    fabs(dfVal) > (static_cast<GIntBig>(1) << 53))
1850
1
                {
1851
1
                    static int nCounter = 0;
1852
1
                    if (nCounter <= 10)
1853
1
                    {
1854
1
                        CPLError(CE_Warning, CPLE_AppDefined,
1855
1
                                 "Value %.17g of field %s with 0 decimal of "
1856
1
                                 "feature " CPL_FRMT_GIB
1857
1
                                 " is bigger than 2^53. "
1858
1
                                 "Precision loss likely occurred or going to "
1859
1
                                 "happen.%s",
1860
1
                                 dfVal, poFieldDefn->GetNameRef(),
1861
1
                                 poFeature->GetFID(),
1862
1
                                 (nCounter == 10) ? " This warning will not be "
1863
0
                                                    "emitted anymore."
1864
1
                                                  : "");
1865
1
                        nCounter++;
1866
1
                    }
1867
1
                }
1868
503
                int ret = DBFWriteDoubleAttribute(
1869
503
                    hDBF, static_cast<int>(poFeature->GetFID()), iField, dfVal);
1870
503
                if (!ret)
1871
19
                {
1872
19
                    CPLError(CE_Warning, CPLE_AppDefined,
1873
19
                             "Value %.17g of field %s of feature " CPL_FRMT_GIB
1874
19
                             " not "
1875
19
                             "successfully written. Possibly due to too larger "
1876
19
                             "number "
1877
19
                             "with respect to field width",
1878
19
                             dfVal, poFieldDefn->GetNameRef(),
1879
19
                             poFeature->GetFID());
1880
19
                }
1881
503
                break;
1882
3.24k
            }
1883
0
            case OFTDate:
1884
0
            {
1885
0
                const OGRField *const psField =
1886
0
                    poFeature->GetRawFieldRef(iField);
1887
1888
0
                if (psField->Date.Year < 0 || psField->Date.Year > 9999)
1889
0
                {
1890
0
                    CPLError(
1891
0
                        CE_Warning, CPLE_NotSupported,
1892
0
                        "Year < 0 or > 9999 is not a valid date for shapefile");
1893
0
                }
1894
0
                else if (psField->Date.Year == 0 && psField->Date.Month == 0 &&
1895
0
                         psField->Date.Day == 0)
1896
0
                {
1897
0
                    DBFWriteNULLAttribute(
1898
0
                        hDBF, static_cast<int>(poFeature->GetFID()), iField);
1899
0
                }
1900
0
                else
1901
0
                {
1902
0
                    DBFWriteIntegerAttribute(
1903
0
                        hDBF, static_cast<int>(poFeature->GetFID()), iField,
1904
0
                        psField->Date.Year * 10000 + psField->Date.Month * 100 +
1905
0
                            psField->Date.Day);
1906
0
                }
1907
0
            }
1908
0
            break;
1909
1910
0
            default:
1911
0
            {
1912
                // Ignore fields of other types.
1913
0
                break;
1914
3.24k
            }
1915
479k
        }
1916
479k
    }
1917
1918
499k
    return OGRERR_NONE;
1919
500k
}