Coverage Report

Created: 2026-07-14 06:21

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