Coverage Report

Created: 2026-02-14 09:00

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