Coverage Report

Created: 2026-02-14 06:52

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 std::unique_ptr<OGRLinearRing>
65
CreateLinearRing(SHPObject *psShape, int ring, bool bHasZ, bool bHasM)
66
0
{
67
0
    int nRingStart = 0;
68
0
    int nRingEnd = 0;
69
0
    RingStartEnd(psShape, ring, &nRingStart, &nRingEnd);
70
71
0
    auto poRing = std::make_unique<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
            poOGRPoly->addRing(CreateLinearRing(psShape, 0, bHasZ, bHasM));
304
0
        }
305
0
        else
306
0
        {
307
0
            std::vector<std::unique_ptr<OGRGeometry>> apoPolygons;
308
0
            apoPolygons.reserve(psShape->nParts);
309
0
            for (int iRing = 0; iRing < psShape->nParts; iRing++)
310
0
            {
311
0
                auto poPoly = std::make_unique<OGRPolygon>();
312
0
                poPoly->addRing(CreateLinearRing(psShape, iRing, bHasZ, bHasM));
313
0
                apoPolygons.push_back(std::move(poPoly));
314
0
            }
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
0
            bool bUseSlowMethod = false;
321
0
            if (!bHasZ && !bHasM)
322
0
            {
323
0
                bool bFoundCW = false;
324
0
                for (int iRing = 1; iRing < psShape->nParts; iRing++)
325
0
                {
326
0
                    if (apoPolygons[iRing]
327
0
                            ->toPolygon()
328
0
                            ->getExteriorRing()
329
0
                            ->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
                    const OGRLinearRing *poExteriorRing =
341
0
                        apoPolygons[0]->toPolygon()->getExteriorRing();
342
0
                    poExteriorRing->getEnvelope(&sFirstEnvelope);
343
0
                    for (int iRing = 1; iRing < psShape->nParts; iRing++)
344
0
                    {
345
0
                        apoPolygons[iRing]->getEnvelope(&sCurEnvelope);
346
0
                        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
0
                        else
354
0
                        {
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
0
                            const auto poRing = apoPolygons[iRing]
361
0
                                                    ->toPolygon()
362
0
                                                    ->getExteriorRing();
363
0
                            const auto nNumPoints = poRing->getNumPoints();
364
0
                            OGRPoint p;
365
0
                            OGRPoint leftPoint(
366
0
                                std::numeric_limits<double>::infinity(), 0);
367
0
                            OGRPoint rightPoint(
368
0
                                -std::numeric_limits<double>::infinity(), 0);
369
0
                            OGRPoint bottomPoint(
370
0
                                0, std::numeric_limits<double>::infinity());
371
0
                            OGRPoint topPoint(
372
0
                                0, -std::numeric_limits<double>::infinity());
373
0
                            for (int iPoint = 0; iPoint < nNumPoints - 1;
374
0
                                 ++iPoint)
375
0
                            {
376
0
                                poRing->getPoint(iPoint, &p);
377
0
                                if (p.getX() < leftPoint.getX() ||
378
0
                                    (p.getX() == leftPoint.getX() &&
379
0
                                     p.getY() < leftPoint.getY()))
380
0
                                {
381
0
                                    leftPoint = p;
382
0
                                }
383
0
                                if (p.getX() > rightPoint.getX() ||
384
0
                                    (p.getX() == rightPoint.getX() &&
385
0
                                     p.getY() > rightPoint.getY()))
386
0
                                {
387
0
                                    rightPoint = p;
388
0
                                }
389
0
                                if (p.getY() < bottomPoint.getY() ||
390
0
                                    (p.getY() == bottomPoint.getY() &&
391
0
                                     p.getX() > bottomPoint.getX()))
392
0
                                {
393
0
                                    bottomPoint = p;
394
0
                                }
395
0
                                if (p.getY() > topPoint.getY() ||
396
0
                                    (p.getY() == topPoint.getY() &&
397
0
                                     p.getX() < topPoint.getX()))
398
0
                                {
399
0
                                    topPoint = p;
400
0
                                }
401
0
                            }
402
0
                            if (!poExteriorRing->isPointInRing(&leftPoint) &&
403
0
                                !poExteriorRing->isPointInRing(&rightPoint) &&
404
0
                                !poExteriorRing->isPointInRing(&bottomPoint) &&
405
0
                                !poExteriorRing->isPointInRing(&topPoint))
406
0
                            {
407
0
                                bUseSlowMethod = true;
408
0
                                break;
409
0
                            }
410
0
                        }
411
0
                    }
412
0
                    if (bUseSlowMethod && !bHasWarnedWrongWindingOrder)
413
0
                    {
414
0
                        bHasWarnedWrongWindingOrder = true;
415
0
                        CPLError(CE_Warning, CPLE_AppDefined,
416
0
                                 "%s contains polygon(s) with rings with "
417
0
                                 "invalid winding order. Autocorrecting them, "
418
0
                                 "but that shapefile should be corrected using "
419
0
                                 "ogr2ogr for example.",
420
0
                                 VSI_SHP_GetFilename(hSHP->fpSHP));
421
0
                    }
422
0
                }
423
0
            }
424
425
0
            bool isValidGeometry = false;
426
0
            const char *const apszOptions[] = {
427
0
                bUseSlowMethod ? "METHOD=DEFAULT" : "METHOD=ONLY_CCW", nullptr};
428
0
            poOGR = OGRGeometryFactory::organizePolygons(
429
0
                        apoPolygons, &isValidGeometry, apszOptions)
430
0
                        .release();
431
432
0
            if (!isValidGeometry)
433
0
            {
434
0
                CPLError(
435
0
                    CE_Warning, CPLE_AppDefined,
436
0
                    "Geometry of polygon of fid %d cannot be translated to "
437
0
                    "Simple Geometry. "
438
0
                    "All polygons will be contained in a multipolygon.",
439
0
                    iShape);
440
0
            }
441
0
        }
442
0
    }
443
444
    /* -------------------------------------------------------------------- */
445
    /*      MultiPatch                                                      */
446
    /* -------------------------------------------------------------------- */
447
0
    else if (psShape->nSHPType == SHPT_MULTIPATCH)
448
0
    {
449
0
        poOGR = OGRCreateFromMultiPatch(
450
0
            psShape->nParts, psShape->panPartStart, psShape->panPartType,
451
0
            psShape->nVertices, psShape->padfX, psShape->padfY, psShape->padfZ);
452
0
    }
453
454
    /* -------------------------------------------------------------------- */
455
    /*      Otherwise for now we just ignore the object.                    */
456
    /* -------------------------------------------------------------------- */
457
0
    else
458
0
    {
459
0
        if (psShape->nSHPType != SHPT_NULL)
460
0
        {
461
0
            CPLDebug("OGR", "Unsupported shape type in SHPReadOGRObject()");
462
0
        }
463
464
        // Nothing returned.
465
0
    }
466
467
    /* -------------------------------------------------------------------- */
468
    /*      Cleanup shape, and set feature id.                              */
469
    /* -------------------------------------------------------------------- */
470
0
    SHPDestroyObject(psShape);
471
472
0
    return poOGR;
473
0
}
474
475
/************************************************************************/
476
/*                     CheckNonFiniteCoordinates()                      */
477
/************************************************************************/
478
479
static bool CheckNonFiniteCoordinates(const double *v, size_t vsize)
480
0
{
481
0
    static bool bAllowNonFiniteCoordinates = CPLTestBool(
482
0
        CPLGetConfigOption("OGR_SHAPE_ALLOW_NON_FINITE_COORDINATES", "NO"));
483
    // Do not document this. Only for edge case testing
484
0
    if (bAllowNonFiniteCoordinates)
485
0
    {
486
0
        return true;
487
0
    }
488
0
    for (size_t i = 0; i < vsize; ++i)
489
0
    {
490
0
        if (!std::isfinite(v[i]))
491
0
        {
492
0
            CPLError(CE_Failure, CPLE_NotSupported,
493
0
                     "Coordinates with non-finite values are not allowed");
494
0
            return false;
495
0
        }
496
0
    }
497
0
    return true;
498
0
}
499
500
static bool CheckNonFiniteCoordinates(const std::vector<double> &v)
501
0
{
502
0
    return CheckNonFiniteCoordinates(v.data(), v.size());
503
0
}
504
505
/************************************************************************/
506
/*                         SHPWriteOGRObject()                          */
507
/************************************************************************/
508
static OGRErr SHPWriteOGRObject(SHPHandle hSHP, int iShape,
509
                                const OGRGeometry *poGeom, bool bRewind,
510
                                OGRwkbGeometryType eLayerGeomType)
511
512
0
{
513
    /* ==================================================================== */
514
    /*      Write "shape" with no geometry or with empty geometry           */
515
    /* ==================================================================== */
516
0
    if (poGeom == nullptr || poGeom->IsEmpty())
517
0
    {
518
0
        SHPObject *psShape =
519
0
            SHPCreateObject(SHPT_NULL, -1, 0, nullptr, nullptr, 0, nullptr,
520
0
                            nullptr, nullptr, nullptr);
521
0
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
522
0
        SHPDestroyObject(psShape);
523
0
        if (nReturnedShapeID == -1)
524
0
        {
525
            // Assuming error is reported by SHPWriteObject().
526
0
            return OGRERR_FAILURE;
527
0
        }
528
0
    }
529
530
    /* ==================================================================== */
531
    /*      Write point geometry.                                           */
532
    /* ==================================================================== */
533
0
    else if (hSHP->nShapeType == SHPT_POINT ||
534
0
             hSHP->nShapeType == SHPT_POINTM || hSHP->nShapeType == SHPT_POINTZ)
535
0
    {
536
0
        if (wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
537
0
        {
538
0
            CPLError(CE_Failure, CPLE_AppDefined,
539
0
                     "Attempt to write non-point (%s) geometry to"
540
0
                     " point shapefile.",
541
0
                     poGeom->getGeometryName());
542
543
0
            return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
544
0
        }
545
546
0
        const OGRPoint *poPoint = poGeom->toPoint();
547
0
        const double dfX = poPoint->getX();
548
0
        const double dfY = poPoint->getY();
549
0
        const double dfZ = poPoint->getZ();
550
0
        double dfM = -std::numeric_limits<double>::max();
551
0
        double *pdfM = nullptr;
552
0
        if (wkbHasM(eLayerGeomType) && (hSHP->nShapeType == SHPT_POINTM ||
553
0
                                        hSHP->nShapeType == SHPT_POINTZ))
554
0
        {
555
0
            if (poGeom->IsMeasured())
556
0
                dfM = poPoint->getM();
557
0
            pdfM = &dfM;
558
0
        }
559
0
        if ((!std::isfinite(dfX) || !std::isfinite(dfY) ||
560
0
             !std::isfinite(dfZ) || (pdfM && !std::isfinite(*pdfM))) &&
561
0
            !CPLTestBool(CPLGetConfigOption(
562
0
                "OGR_SHAPE_ALLOW_NON_FINITE_COORDINATES", "NO")))
563
0
        {
564
0
            CPLError(CE_Failure, CPLE_NotSupported,
565
0
                     "Coordinates with non-finite values are not allowed");
566
0
            return OGRERR_FAILURE;
567
0
        }
568
0
        SHPObject *psShape =
569
0
            SHPCreateObject(hSHP->nShapeType, -1, 0, nullptr, nullptr, 1, &dfX,
570
0
                            &dfY, &dfZ, pdfM);
571
0
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
572
0
        SHPDestroyObject(psShape);
573
0
        if (nReturnedShapeID == -1)
574
0
            return OGRERR_FAILURE;
575
0
    }
576
    /* ==================================================================== */
577
    /*      MultiPoint.                                                     */
578
    /* ==================================================================== */
579
0
    else if (hSHP->nShapeType == SHPT_MULTIPOINT ||
580
0
             hSHP->nShapeType == SHPT_MULTIPOINTM ||
581
0
             hSHP->nShapeType == SHPT_MULTIPOINTZ)
582
0
    {
583
0
        if (wkbFlatten(poGeom->getGeometryType()) != wkbMultiPoint)
584
0
        {
585
0
            CPLError(CE_Failure, CPLE_AppDefined,
586
0
                     "Attempt to write non-multipoint (%s) geometry to "
587
0
                     "multipoint shapefile.",
588
0
                     poGeom->getGeometryName());
589
590
0
            return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
591
0
        }
592
593
0
        const OGRMultiPoint *poMP = poGeom->toMultiPoint();
594
0
        const int nNumGeometries = poMP->getNumGeometries();
595
0
        const bool bHasZ = (hSHP->nShapeType == SHPT_MULTIPOINTM ||
596
0
                            hSHP->nShapeType == SHPT_MULTIPOINTZ);
597
0
        const bool bHasM = wkbHasM(eLayerGeomType) && bHasZ;
598
0
        const bool bIsGeomMeasured = CPL_TO_BOOL(poGeom->IsMeasured());
599
600
0
        std::vector<double> adfX;
601
0
        std::vector<double> adfY;
602
0
        std::vector<double> adfZ;
603
0
        std::vector<double> adfM;
604
0
        try
605
0
        {
606
0
            adfX.reserve(nNumGeometries);
607
0
            adfY.reserve(nNumGeometries);
608
0
            if (bHasZ)
609
0
                adfZ.reserve(nNumGeometries);
610
0
            if (bHasM)
611
0
                adfM.reserve(nNumGeometries);
612
0
        }
613
0
        catch (const std::exception &e)
614
0
        {
615
0
            CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
616
0
            return OGRERR_FAILURE;
617
0
        }
618
619
0
        for (const OGRPoint *poPoint : *poMP)
620
0
        {
621
            // Ignore POINT EMPTY.
622
0
            if (!poPoint->IsEmpty())
623
0
            {
624
0
                adfX.push_back(poPoint->getX());
625
0
                adfY.push_back(poPoint->getY());
626
0
                if (bHasZ)
627
0
                    adfZ.push_back(poPoint->getZ());
628
0
                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
0
            }
636
0
            else
637
0
            {
638
0
                CPLDebug("OGR",
639
0
                         "Ignored POINT EMPTY inside MULTIPOINT in shapefile "
640
0
                         "writer.");
641
0
            }
642
0
        }
643
0
        if (!CheckNonFiniteCoordinates(adfX) ||
644
0
            !CheckNonFiniteCoordinates(adfY) ||
645
0
            !CheckNonFiniteCoordinates(adfZ) ||
646
0
            !CheckNonFiniteCoordinates(adfM))
647
0
        {
648
0
            return OGRERR_FAILURE;
649
0
        }
650
651
0
        SHPObject *psShape = SHPCreateObject(
652
0
            hSHP->nShapeType, -1, 0, nullptr, nullptr,
653
0
            static_cast<int>(adfX.size()), adfX.data(), adfY.data(),
654
0
            bHasZ ? adfZ.data() : nullptr, bHasM ? adfM.data() : nullptr);
655
0
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
656
0
        SHPDestroyObject(psShape);
657
658
0
        if (nReturnedShapeID == -1)
659
0
            return OGRERR_FAILURE;
660
0
    }
661
662
    /* ==================================================================== */
663
    /*      Arcs                                                            */
664
    /* ==================================================================== */
665
0
    else if (hSHP->nShapeType == SHPT_ARC || hSHP->nShapeType == SHPT_ARCM ||
666
0
             hSHP->nShapeType == SHPT_ARCZ)
667
0
    {
668
0
        std::unique_ptr<OGRGeometry> poGeomToDelete;  // keep in that scope
669
0
        const OGRMultiLineString *poML = nullptr;
670
0
        OGRMultiLineString oMLFromLineString;
671
0
        const auto eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
672
0
        if (eFlatGeomType == wkbMultiLineString)
673
0
        {
674
0
            poML = poGeom->toMultiLineString();
675
0
        }
676
0
        else if (eFlatGeomType == wkbLineString)
677
0
        {
678
            // Borrow the geometry
679
0
            oMLFromLineString.addGeometryDirectly(
680
0
                const_cast<OGRLineString *>(poGeom->toLineString()));
681
0
            poML = &oMLFromLineString;
682
0
        }
683
0
        else
684
0
        {
685
0
            poGeomToDelete = std::unique_ptr<OGRGeometry>(
686
0
                OGRGeometryFactory::forceToMultiLineString(poGeom->clone()));
687
0
            if (wkbFlatten(poGeomToDelete->getGeometryType()) !=
688
0
                wkbMultiLineString)
689
0
            {
690
0
                CPLError(CE_Failure, CPLE_AppDefined,
691
0
                         "Attempt to write non-linestring (%s) geometry to "
692
0
                         "ARC type shapefile.",
693
0
                         poGeom->getGeometryName());
694
695
0
                return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
696
0
            }
697
0
            poML = poGeomToDelete->toMultiLineString();
698
0
        }
699
700
0
        const int nNumGeometries = poML->getNumGeometries();
701
702
0
        int nTotalPoints = 0;
703
0
        for (const auto poArc : poML)
704
0
        {
705
0
            const int nNumPoints = poArc->getNumPoints();
706
0
            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
0
            nTotalPoints += nNumPoints;
712
0
        }
713
714
0
        std::vector<int> anRingStart;
715
0
        std::vector<double> adfX;
716
0
        std::vector<double> adfY;
717
0
        std::vector<double> adfZ;
718
0
        std::vector<double> adfM;
719
0
        const bool bHasZ =
720
0
            (hSHP->nShapeType == SHPT_ARCM || hSHP->nShapeType == SHPT_ARCZ);
721
0
        const bool bHasM = wkbHasM(eLayerGeomType) && bHasZ;
722
0
        const bool bIsGeomMeasured = CPL_TO_BOOL(poGeom->IsMeasured());
723
724
0
        try
725
0
        {
726
0
            anRingStart.reserve(nNumGeometries);
727
728
0
            adfX.reserve(nTotalPoints);
729
0
            adfY.reserve(nTotalPoints);
730
0
            if (bHasZ)
731
0
            {
732
0
                adfZ.reserve(nTotalPoints);
733
0
            }
734
0
            if (bHasM)
735
0
            {
736
0
                adfM.reserve(nTotalPoints);
737
0
            }
738
0
        }
739
0
        catch (const std::exception &e)
740
0
        {
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
0
        for (const auto poArc : poML)
749
0
        {
750
0
            const int nNumPoints = poArc->getNumPoints();
751
752
            // Ignore LINESTRING EMPTY.
753
0
            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
0
            anRingStart.push_back(static_cast<int>(adfX.size()));
762
763
0
            for (int iPoint = 0; iPoint < nNumPoints; iPoint++)
764
0
            {
765
0
                adfX.push_back(poArc->getX(iPoint));
766
0
                adfY.push_back(poArc->getY(iPoint));
767
0
                if (bHasZ)
768
0
                {
769
0
                    adfZ.push_back(poArc->getZ(iPoint));
770
0
                }
771
0
                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
0
            }
779
0
        }
780
781
        // Give back the borrowed line string
782
0
        if (eFlatGeomType == wkbLineString)
783
0
            oMLFromLineString.removeGeometry(0, /* bDelete=*/false);
784
785
0
        if (!CheckNonFiniteCoordinates(adfX) ||
786
0
            !CheckNonFiniteCoordinates(adfY) ||
787
0
            !CheckNonFiniteCoordinates(adfZ) ||
788
0
            !CheckNonFiniteCoordinates(adfM))
789
0
        {
790
0
            return OGRERR_FAILURE;
791
0
        }
792
793
0
        SHPObject *psShape = SHPCreateObject(
794
0
            hSHP->nShapeType, iShape, static_cast<int>(anRingStart.size()),
795
0
            anRingStart.data(), nullptr, static_cast<int>(adfX.size()),
796
0
            adfX.data(), adfY.data(), bHasZ ? adfZ.data() : nullptr,
797
0
            bHasM ? adfM.data() : nullptr);
798
0
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
799
0
        SHPDestroyObject(psShape);
800
801
0
        if (nReturnedShapeID == -1)
802
0
            return OGRERR_FAILURE;
803
0
    }
804
805
    /* ==================================================================== */
806
    /*      Polygons/MultiPolygons                                          */
807
    /* ==================================================================== */
808
0
    else if (hSHP->nShapeType == SHPT_POLYGON ||
809
0
             hSHP->nShapeType == SHPT_POLYGONM ||
810
0
             hSHP->nShapeType == SHPT_POLYGONZ)
811
0
    {
812
        // bool = true means outer ring
813
0
        std::vector<std::pair<const OGRLinearRing *, bool>> apoRings;
814
0
        const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
815
0
        std::unique_ptr<OGRGeometry> poGeomToDelete;
816
817
0
        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
0
        else if (eType == wkbMultiPolygon || eType == wkbGeometryCollection ||
851
0
                 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
0
        else
927
0
        {
928
0
            CPLError(CE_Failure, CPLE_AppDefined,
929
0
                     "Attempt to write non-polygon (%s) geometry to "
930
0
                     "POLYGON type shapefile.",
931
0
                     poGeom->getGeometryName());
932
933
0
            return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
934
0
        }
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
0
    else if (hSHP->nShapeType == SHPT_MULTIPATCH)
1040
0
    {
1041
0
        int nParts = 0;
1042
0
        std::vector<int> anPartStart;
1043
0
        std::vector<int> anPartType;
1044
0
        int nPoints = 0;
1045
0
        std::vector<OGRRawPoint> aoPoints;
1046
0
        std::vector<double> adfZ;
1047
0
        OGRErr eErr = OGRCreateMultiPatch(poGeom,
1048
0
                                          FALSE,  // no SHPP_TRIANGLES
1049
0
                                          nParts, anPartStart, anPartType,
1050
0
                                          nPoints, aoPoints, adfZ);
1051
0
        if (eErr != OGRERR_NONE)
1052
0
            return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
1053
1054
0
        std::vector<double> adfX(nPoints);
1055
0
        std::vector<double> adfY(nPoints);
1056
0
        for (int i = 0; i < nPoints; ++i)
1057
0
        {
1058
0
            adfX[i] = aoPoints[i].x;
1059
0
            adfY[i] = aoPoints[i].y;
1060
0
        }
1061
1062
0
        if (!CheckNonFiniteCoordinates(adfX.data(), nPoints) ||
1063
0
            !CheckNonFiniteCoordinates(adfY.data(), nPoints) ||
1064
0
            !CheckNonFiniteCoordinates(adfZ.data(), nPoints))
1065
0
        {
1066
0
            return OGRERR_FAILURE;
1067
0
        }
1068
1069
0
        SHPObject *psShape =
1070
0
            SHPCreateObject(hSHP->nShapeType, iShape, nParts,
1071
0
                            anPartStart.data(), anPartType.data(), nPoints,
1072
0
                            adfX.data(), adfY.data(), adfZ.data(), nullptr);
1073
0
        if (bRewind)
1074
0
            SHPRewindObject(hSHP, psShape);
1075
0
        const int nReturnedShapeID = SHPWriteObject(hSHP, iShape, psShape);
1076
0
        SHPDestroyObject(psShape);
1077
1078
0
        if (nReturnedShapeID == -1)
1079
0
            return OGRERR_FAILURE;
1080
0
    }
1081
1082
0
    else
1083
0
    {
1084
0
        return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
1085
0
    }
1086
1087
0
    return OGRERR_NONE;
1088
0
}
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
0
{
1100
0
    int nAdjustableFields = 0;
1101
0
    const int nFieldCount = hDBF ? DBFGetFieldCount(hDBF) : 0;
1102
1103
0
    OGRFeatureDefn *const poDefn = new OGRFeatureDefn(pszName);
1104
0
    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
0
    std::vector<OGRFieldDefn> aFieldsFromSHPXML;
1110
0
    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
0
    for (int iField = 0; iField < nFieldCount; iField++)
1203
0
    {
1204
        // On reading we support up to 11 characters
1205
0
        char szFieldName[XBASE_FLDNAME_LEN_READ + 1] = {};
1206
0
        int nWidth = 0;
1207
0
        int nPrecision = 0;
1208
0
        DBFFieldType eDBFType =
1209
0
            DBFGetFieldInfo(hDBF, iField, szFieldName, &nWidth, &nPrecision);
1210
1211
0
        OGRFieldDefn oField("", OFTInteger);
1212
0
        if (!aFieldsFromSHPXML.empty())
1213
0
        {
1214
0
            oField = aFieldsFromSHPXML[iField];
1215
0
        }
1216
0
        else if (strlen(pszSHPEncoding) > 0)
1217
0
        {
1218
0
            char *const pszUTF8Field =
1219
0
                CPLRecode(szFieldName, pszSHPEncoding, CPL_ENC_UTF8);
1220
0
            oField.SetName(pszUTF8Field);
1221
0
            CPLFree(pszUTF8Field);
1222
0
        }
1223
0
        else
1224
0
        {
1225
0
            oField.SetName(szFieldName);
1226
0
        }
1227
1228
0
        oField.SetWidth(nWidth);
1229
0
        oField.SetPrecision(nPrecision);
1230
1231
0
        if (eDBFType == FTDate)
1232
0
        {
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
0
            oField.SetWidth(nWidth + 2);
1239
0
            oField.SetType(OFTDate);
1240
0
        }
1241
0
        else if (eDBFType == FTDouble)
1242
0
        {
1243
0
            nAdjustableFields += (nPrecision == 0);
1244
0
            if (nPrecision == 0 && nWidth < 19)
1245
0
            {
1246
0
                if (!aFieldsFromSHPXML.empty() &&
1247
0
                    aFieldsFromSHPXML[iField].GetType() == OFTInteger)
1248
0
                    oField.SetType(OFTInteger);
1249
0
                else
1250
0
                    oField.SetType(OFTInteger64);
1251
0
            }
1252
0
            else
1253
0
                oField.SetType(OFTReal);
1254
0
        }
1255
0
        else if (eDBFType == FTInteger)
1256
0
            oField.SetType(OFTInteger);
1257
0
        else if (eDBFType == FTLogical)
1258
0
        {
1259
0
            oField.SetType(OFTInteger);
1260
0
            oField.SetSubType(OFSTBoolean);
1261
0
        }
1262
0
        else
1263
0
            oField.SetType(OFTString);
1264
1265
0
        poDefn->AddFieldDefn(&oField);
1266
0
    }
1267
1268
    // Do an optional past if requested and needed to demote Integer64->Integer
1269
    // or Real->Integer64/Integer.
1270
0
    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
0
    if (hSHP == nullptr)
1324
0
    {
1325
0
        poDefn->SetGeomType(wkbNone);
1326
0
    }
1327
0
    else
1328
0
    {
1329
0
        switch (hSHP->nShapeType)
1330
0
        {
1331
0
            case SHPT_POINT:
1332
0
                poDefn->SetGeomType(wkbPoint);
1333
0
                break;
1334
1335
0
            case SHPT_POINTZ:
1336
0
                poDefn->SetGeomType(wkbPointZM);
1337
0
                break;
1338
1339
0
            case SHPT_POINTM:
1340
0
                poDefn->SetGeomType(wkbPointM);
1341
0
                break;
1342
1343
0
            case SHPT_ARC:
1344
0
                poDefn->SetGeomType(wkbLineString);
1345
0
                break;
1346
1347
0
            case SHPT_ARCZ:
1348
0
                poDefn->SetGeomType(wkbLineStringZM);
1349
0
                break;
1350
1351
0
            case SHPT_ARCM:
1352
0
                poDefn->SetGeomType(wkbLineStringM);
1353
0
                break;
1354
1355
0
            case SHPT_MULTIPOINT:
1356
0
                poDefn->SetGeomType(wkbMultiPoint);
1357
0
                break;
1358
1359
0
            case SHPT_MULTIPOINTZ:
1360
0
                poDefn->SetGeomType(wkbMultiPointZM);
1361
0
                break;
1362
1363
0
            case SHPT_MULTIPOINTM:
1364
0
                poDefn->SetGeomType(wkbMultiPointM);
1365
0
                break;
1366
1367
0
            case SHPT_POLYGON:
1368
0
                poDefn->SetGeomType(wkbPolygon);
1369
0
                break;
1370
1371
0
            case SHPT_POLYGONZ:
1372
0
                poDefn->SetGeomType(wkbPolygonZM);
1373
0
                break;
1374
1375
0
            case SHPT_POLYGONM:
1376
0
                poDefn->SetGeomType(wkbPolygonM);
1377
0
                break;
1378
1379
0
            case SHPT_MULTIPATCH:
1380
0
                poDefn->SetGeomType(wkbUnknown);  // not ideal
1381
0
                break;
1382
0
        }
1383
0
    }
1384
1385
0
    return poDefn;
1386
0
}
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
0
{
1398
0
    if (iShape < 0 || (hSHP != nullptr && iShape >= hSHP->nRecords) ||
1399
0
        (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
0
    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
0
    OGRFeature *poFeature = new OGRFeature(poDefn);
1420
1421
    /* -------------------------------------------------------------------- */
1422
    /*      Fetch geometry from Shapefile to OGRFeature.                    */
1423
    /* -------------------------------------------------------------------- */
1424
0
    if (hSHP != nullptr)
1425
0
    {
1426
0
        if (!poDefn->IsGeometryIgnored())
1427
0
        {
1428
0
            OGRGeometry *poGeometry = SHPReadOGRObject(
1429
0
                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
0
            if (poGeometry)
1440
0
            {
1441
                // Set/unset flags.
1442
0
                const OGRwkbGeometryType eMyGeomType =
1443
0
                    poFeature->GetDefnRef()->GetGeomFieldDefn(0)->GetType();
1444
1445
0
                if (eMyGeomType != wkbUnknown)
1446
0
                {
1447
0
                    OGRwkbGeometryType eGeomInType =
1448
0
                        poGeometry->getGeometryType();
1449
0
                    if (wkbHasZ(eMyGeomType) && !wkbHasZ(eGeomInType))
1450
0
                    {
1451
0
                        poGeometry->set3D(TRUE);
1452
0
                    }
1453
0
                    else if (!wkbHasZ(eMyGeomType) && wkbHasZ(eGeomInType))
1454
0
                    {
1455
0
                        poGeometry->set3D(FALSE);
1456
0
                    }
1457
0
                    if (wkbHasM(eMyGeomType) && !wkbHasM(eGeomInType))
1458
0
                    {
1459
0
                        poGeometry->setMeasured(TRUE);
1460
0
                    }
1461
0
                    else if (!wkbHasM(eMyGeomType) && wkbHasM(eGeomInType))
1462
0
                    {
1463
0
                        poGeometry->setMeasured(FALSE);
1464
0
                    }
1465
0
                }
1466
0
            }
1467
1468
0
            poFeature->SetGeometryDirectly(poGeometry);
1469
0
        }
1470
0
        else if (psShape != nullptr)
1471
0
        {
1472
0
            SHPDestroyObject(psShape);
1473
0
        }
1474
0
    }
1475
1476
    /* -------------------------------------------------------------------- */
1477
    /*      Fetch feature attributes to OGRFeature fields.                  */
1478
    /* -------------------------------------------------------------------- */
1479
1480
0
    for (int iField = 0; hDBF != nullptr && iField < poDefn->GetFieldCount();
1481
0
         iField++)
1482
0
    {
1483
0
        const OGRFieldDefn *const poFieldDefn = poDefn->GetFieldDefn(iField);
1484
0
        if (poFieldDefn->IsIgnored())
1485
0
            continue;
1486
1487
0
        switch (poFieldDefn->GetType())
1488
0
        {
1489
0
            case OFTString:
1490
0
            {
1491
0
                const char *const pszFieldVal =
1492
0
                    DBFReadStringAttribute(hDBF, iShape, iField);
1493
0
                if (pszFieldVal != nullptr && pszFieldVal[0] != '\0')
1494
0
                {
1495
0
                    if (pszSHPEncoding[0] != '\0')
1496
0
                    {
1497
0
                        char *const pszUTF8Field = CPLRecode(
1498
0
                            pszFieldVal, pszSHPEncoding, CPL_ENC_UTF8);
1499
0
                        poFeature->SetField(iField, pszUTF8Field);
1500
0
                        CPLFree(pszUTF8Field);
1501
0
                    }
1502
0
                    else
1503
0
                        poFeature->SetField(iField, pszFieldVal);
1504
0
                }
1505
0
                else
1506
0
                {
1507
0
                    poFeature->SetFieldNull(iField);
1508
0
                }
1509
0
                break;
1510
0
            }
1511
0
            case OFTInteger:
1512
0
            case OFTInteger64:
1513
0
            case OFTReal:
1514
0
            {
1515
0
                if (DBFIsAttributeNULL(hDBF, iShape, iField))
1516
0
                {
1517
0
                    poFeature->SetFieldNull(iField);
1518
0
                }
1519
0
                else
1520
0
                {
1521
0
                    if (poFieldDefn->GetSubType() == OFSTBoolean)
1522
0
                    {
1523
0
                        const char *pszVal =
1524
0
                            DBFReadLogicalAttribute(hDBF, iShape, iField);
1525
0
                        poFeature->SetField(
1526
0
                            iField, pszVal[0] == 'T' || pszVal[0] == 't' ||
1527
0
                                            pszVal[0] == 'Y' || pszVal[0] == 'y'
1528
0
                                        ? 1
1529
0
                                        : 0);
1530
0
                    }
1531
0
                    else
1532
0
                    {
1533
0
                        const char *pszVal =
1534
0
                            DBFReadStringAttribute(hDBF, iShape, iField);
1535
0
                        poFeature->SetField(iField, pszVal);
1536
0
                    }
1537
0
                }
1538
0
                break;
1539
0
            }
1540
0
            case OFTDate:
1541
0
            {
1542
0
                if (DBFIsAttributeNULL(hDBF, iShape, iField))
1543
0
                {
1544
0
                    poFeature->SetFieldNull(iField);
1545
0
                    continue;
1546
0
                }
1547
1548
0
                const char *const pszDateValue =
1549
0
                    DBFReadStringAttribute(hDBF, iShape, iField);
1550
1551
0
                OGRField sFld;
1552
0
                memset(&sFld, 0, sizeof(sFld));
1553
1554
0
                if (strlen(pszDateValue) >= 10 && pszDateValue[2] == '/' &&
1555
0
                    pszDateValue[5] == '/')
1556
0
                {
1557
0
                    sFld.Date.Month =
1558
0
                        static_cast<GByte>(atoi(pszDateValue + 0));
1559
0
                    sFld.Date.Day = static_cast<GByte>(atoi(pszDateValue + 3));
1560
0
                    sFld.Date.Year =
1561
0
                        static_cast<GInt16>(atoi(pszDateValue + 6));
1562
0
                }
1563
0
                else
1564
0
                {
1565
0
                    const int nFullDate = atoi(pszDateValue);
1566
0
                    sFld.Date.Year = static_cast<GInt16>(nFullDate / 10000);
1567
0
                    sFld.Date.Month =
1568
0
                        static_cast<GByte>((nFullDate / 100) % 100);
1569
0
                    sFld.Date.Day = static_cast<GByte>(nFullDate % 100);
1570
0
                }
1571
1572
0
                poFeature->SetField(iField, &sFld);
1573
0
            }
1574
0
            break;
1575
1576
0
            default:
1577
0
                CPLAssert(false);
1578
0
        }
1579
0
    }
1580
1581
0
    if (poFeature != nullptr)
1582
0
        poFeature->SetFID(iShape);
1583
1584
0
    return poFeature;
1585
0
}
1586
1587
/************************************************************************/
1588
/*                             GrowField()                              */
1589
/************************************************************************/
1590
1591
static OGRErr GrowField(DBFHandle hDBF, int iField, OGRFieldDefn *poFieldDefn,
1592
                        int nNewSize)
1593
0
{
1594
0
    char szFieldName[20] = {};
1595
0
    int nOriWidth = 0;
1596
0
    int nPrecision = 0;
1597
0
    DBFGetFieldInfo(hDBF, iField, szFieldName, &nOriWidth, &nPrecision);
1598
1599
0
    CPLDebug("SHAPE", "Extending field %d (%s) from %d to %d characters",
1600
0
             iField, poFieldDefn->GetNameRef(), nOriWidth, nNewSize);
1601
1602
0
    const char chNativeType = DBFGetNativeFieldType(hDBF, iField);
1603
0
    if (!DBFAlterFieldDefn(hDBF, iField, szFieldName, chNativeType, nNewSize,
1604
0
                           nPrecision))
1605
0
    {
1606
0
        CPLError(CE_Failure, CPLE_AppDefined,
1607
0
                 "Extending field %d (%s) from %d to %d characters failed",
1608
0
                 iField, poFieldDefn->GetNameRef(), nOriWidth, nNewSize);
1609
0
        return OGRERR_FAILURE;
1610
0
    }
1611
1612
0
    auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
1613
0
    poFieldDefn->SetWidth(nNewSize);
1614
0
    return OGRERR_NONE;
1615
0
}
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
0
{
1630
    /* -------------------------------------------------------------------- */
1631
    /*      Write the geometry.                                             */
1632
    /* -------------------------------------------------------------------- */
1633
0
    if (hSHP != nullptr)
1634
0
    {
1635
0
        const OGRErr eErr = SHPWriteOGRObject(
1636
0
            hSHP, static_cast<int>(poFeature->GetFID()),
1637
0
            poFeature->GetGeometryRef(), bRewind, poDefn->GetGeomType());
1638
0
        if (eErr != OGRERR_NONE)
1639
0
            return eErr;
1640
0
    }
1641
1642
    /* -------------------------------------------------------------------- */
1643
    /*      If there is no DBF, the job is done now.                        */
1644
    /* -------------------------------------------------------------------- */
1645
0
    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
0
    if (poFeature->GetFID() == OGRNullFID)
1662
0
        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
0
    if (DBFGetRecordCount(hDBF) == 0 && DBFGetFieldCount(hDBF) == 0)
1671
0
    {
1672
0
        CPLDebug(
1673
0
            "OGR",
1674
0
            "Created dummy FID field for shapefile since schema is empty.");
1675
0
        DBFAddField(hDBF, "FID", FTInteger, 11, 0);
1676
0
    }
1677
1678
    /* -------------------------------------------------------------------- */
1679
    /*      Write out dummy field value if it exists.                       */
1680
    /* -------------------------------------------------------------------- */
1681
0
    if (poDefn->GetFieldCount() == 0)
1682
0
    {
1683
0
        if (DBFGetFieldCount(hDBF) == 1)
1684
0
        {
1685
0
            DBFWriteIntegerAttribute(hDBF,
1686
0
                                     static_cast<int>(poFeature->GetFID()), 0,
1687
0
                                     static_cast<int>(poFeature->GetFID()));
1688
0
        }
1689
0
        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
0
    }
1697
1698
    /* -------------------------------------------------------------------- */
1699
    /*      Write all the fields.                                           */
1700
    /* -------------------------------------------------------------------- */
1701
0
    for (int iField = 0; iField < poDefn->GetFieldCount(); iField++)
1702
0
    {
1703
0
        if (!poFeature->IsFieldSetAndNotNull(iField))
1704
0
        {
1705
0
            DBFWriteNULLAttribute(hDBF, static_cast<int>(poFeature->GetFID()),
1706
0
                                  iField);
1707
0
            continue;
1708
0
        }
1709
1710
0
        OGRFieldDefn *const poFieldDefn = poDefn->GetFieldDefn(iField);
1711
1712
0
        switch (poFieldDefn->GetType())
1713
0
        {
1714
0
            case OFTString:
1715
0
            {
1716
0
                const char *pszStr = poFeature->GetFieldAsString(iField);
1717
0
                char *pszEncoded = nullptr;
1718
0
                if (pszSHPEncoding[0] != '\0')
1719
0
                {
1720
0
                    pszEncoded =
1721
0
                        CPLRecode(pszStr, CPL_ENC_UTF8, pszSHPEncoding);
1722
0
                    pszStr = pszEncoded;
1723
0
                }
1724
1725
0
                int nStrLen = static_cast<int>(strlen(pszStr));
1726
0
                if (nStrLen > OGR_DBF_MAX_FIELD_WIDTH)
1727
0
                {
1728
0
                    if (!(*pbTruncationWarningEmitted))
1729
0
                    {
1730
0
                        *pbTruncationWarningEmitted = true;
1731
0
                        CPLError(
1732
0
                            CE_Warning, CPLE_AppDefined,
1733
0
                            "Value '%s' of field %s has been truncated to %d "
1734
0
                            "characters.  This warning will not be emitted any "
1735
0
                            "more for that layer.",
1736
0
                            poFeature->GetFieldAsString(iField),
1737
0
                            poFieldDefn->GetNameRef(), OGR_DBF_MAX_FIELD_WIDTH);
1738
0
                    }
1739
1740
0
                    nStrLen = OGR_DBF_MAX_FIELD_WIDTH;
1741
1742
0
                    if (pszEncoded != nullptr &&  // For Coverity.
1743
0
                        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
0
                }
1765
1766
0
                if (nStrLen > poFieldDefn->GetWidth())
1767
0
                {
1768
0
                    if (GrowField(hDBF, iField, poFieldDefn, nStrLen) !=
1769
0
                        OGRERR_NONE)
1770
0
                    {
1771
0
                        CPLFree(pszEncoded);
1772
0
                        return OGRERR_FAILURE;
1773
0
                    }
1774
0
                }
1775
1776
0
                DBFWriteStringAttribute(hDBF,
1777
0
                                        static_cast<int>(poFeature->GetFID()),
1778
0
                                        iField, pszStr);
1779
1780
0
                CPLFree(pszEncoded);
1781
0
                break;
1782
0
            }
1783
0
            case OFTInteger:
1784
0
            case OFTInteger64:
1785
0
            {
1786
0
                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
0
                else
1793
0
                {
1794
0
                    char szValue[32] = {};
1795
0
                    const int nFieldWidth = poFieldDefn->GetWidth();
1796
0
                    snprintf(szValue, sizeof(szValue),
1797
0
                             "%*" CPL_FRMT_GB_WITHOUT_PREFIX "d",
1798
0
                             std::min(nFieldWidth,
1799
0
                                      static_cast<int>(sizeof(szValue)) - 1),
1800
0
                             poFeature->GetFieldAsInteger64(iField));
1801
1802
0
                    const int nStrLen = static_cast<int>(strlen(szValue));
1803
0
                    if (nStrLen > nFieldWidth)
1804
0
                    {
1805
0
                        if (GrowField(hDBF, iField, poFieldDefn, nStrLen) !=
1806
0
                            OGRERR_NONE)
1807
0
                        {
1808
0
                            return OGRERR_FAILURE;
1809
0
                        }
1810
0
                    }
1811
1812
0
                    DBFWriteAttributeDirectly(
1813
0
                        hDBF, static_cast<int>(poFeature->GetFID()), iField,
1814
0
                        szValue);
1815
0
                }
1816
1817
0
                break;
1818
0
            }
1819
1820
0
            case OFTReal:
1821
0
            {
1822
0
                const double dfVal = poFeature->GetFieldAsDouble(iField);
1823
                // IEEE754 doubles can store exact values of all integers
1824
                // below 2^53.
1825
0
                if (poFieldDefn->GetPrecision() == 0 &&
1826
0
                    fabs(dfVal) > (static_cast<GIntBig>(1) << 53))
1827
0
                {
1828
0
                    static int nCounter = 0;
1829
0
                    if (nCounter <= 10)
1830
0
                    {
1831
0
                        CPLError(CE_Warning, CPLE_AppDefined,
1832
0
                                 "Value %.17g of field %s with 0 decimal of "
1833
0
                                 "feature " CPL_FRMT_GIB
1834
0
                                 " is bigger than 2^53. "
1835
0
                                 "Precision loss likely occurred or going to "
1836
0
                                 "happen.%s",
1837
0
                                 dfVal, poFieldDefn->GetNameRef(),
1838
0
                                 poFeature->GetFID(),
1839
0
                                 (nCounter == 10) ? " This warning will not be "
1840
0
                                                    "emitted anymore."
1841
0
                                                  : "");
1842
0
                        nCounter++;
1843
0
                    }
1844
0
                }
1845
0
                int ret = DBFWriteDoubleAttribute(
1846
0
                    hDBF, static_cast<int>(poFeature->GetFID()), iField, dfVal);
1847
0
                if (!ret)
1848
0
                {
1849
0
                    CPLError(CE_Warning, CPLE_AppDefined,
1850
0
                             "Value %.17g of field %s of feature " CPL_FRMT_GIB
1851
0
                             " not "
1852
0
                             "successfully written. Possibly due to too larger "
1853
0
                             "number "
1854
0
                             "with respect to field width",
1855
0
                             dfVal, poFieldDefn->GetNameRef(),
1856
0
                             poFeature->GetFID());
1857
0
                }
1858
0
                break;
1859
0
            }
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
0
            }
1892
0
        }
1893
0
    }
1894
1895
0
    return OGRERR_NONE;
1896
0
}