Coverage Report

Created: 2025-12-31 06:48

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