Coverage Report

Created: 2025-06-13 06:29

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