Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implements OGRShapeLayer class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999,  Les Technologies SoftMap Inc.
9
 * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "ogrshape.h"
15
16
#include <cerrno>
17
#include <limits>
18
#include <cmath>
19
#include <cstddef>
20
#include <cstdio>
21
#include <cstdlib>
22
#include <cstring>
23
#include <ctime>
24
#include <algorithm>
25
#include <string>
26
27
#include "cpl_conv.h"
28
#include "cpl_error.h"
29
#include "cpl_multiproc.h"
30
#include "cpl_port.h"
31
#include "cpl_string.h"
32
#include "cpl_time.h"
33
#include "cpl_vsi.h"
34
#include "cpl_vsi_virtual.h"
35
#include "ogr_core.h"
36
#include "ogr_feature.h"
37
#include "ogr_geometry.h"
38
#include "ogr_p.h"
39
#include "ogr_spatialref.h"
40
#include "ogr_srs_api.h"
41
#include "ogrlayerpool.h"
42
#include "ograrrowarrayhelper.h"
43
#include "ogrsf_frmts.h"
44
#include "shapefil.h"
45
#include "shp_vsi.h"
46
47
/************************************************************************/
48
/*                           OGRShapeLayer()                            */
49
/************************************************************************/
50
51
OGRShapeLayer::OGRShapeLayer(OGRShapeDataSource *poDSIn,
52
                             const char *pszFullNameIn, SHPHandle hSHPIn,
53
                             DBFHandle hDBFIn,
54
                             const OGRSpatialReference *poSRSIn, bool bSRSSetIn,
55
                             const std::string &osPrjFilename, bool bUpdate,
56
                             OGRwkbGeometryType eReqType, bool bCreateLayer)
57
0
    : OGRAbstractProxiedLayer(poDSIn->GetPool()), m_poDS(poDSIn),
58
0
      m_osFullName(pszFullNameIn), m_hSHP(hSHPIn), m_hDBF(hDBFIn),
59
0
      m_bUpdateAccess(bUpdate), m_eRequestedGeomType(eReqType),
60
0
      m_bHSHPWasNonNULL(hSHPIn != nullptr), m_bHDBFWasNonNULL(hDBFIn != nullptr)
61
0
{
62
0
    if (m_hSHP != nullptr)
63
0
    {
64
0
        m_nTotalShapeCount = m_hSHP->nRecords;
65
0
        if (m_hDBF != nullptr && m_hDBF->nRecords != m_nTotalShapeCount)
66
0
        {
67
0
            CPLError(
68
0
                CE_Warning, CPLE_AppDefined,
69
0
                "Inconsistent record number in .shx (%d) and in .dbf (%d). "
70
0
                "Using (arbitrarily) the former.\n"
71
0
                "This dataset is likely corrupted. You may try to re-open "
72
0
                "this dataset with the SHAPE_RESTORE_SHX configuration "
73
0
                "option set to YES to attempt repairing it, but first "
74
0
                "back up the original .shp, .shx and .dbf files.",
75
0
                m_hSHP->nRecords, m_hDBF->nRecords);
76
0
        }
77
0
    }
78
0
    else if (m_hDBF != nullptr)
79
0
    {
80
0
        m_nTotalShapeCount = m_hDBF->nRecords;
81
0
    }
82
0
#ifdef DEBUG
83
0
    else
84
0
    {
85
0
        CPLError(CE_Fatal, CPLE_AssertionFailed,
86
0
                 "Should not happen: Both m_hSHP and m_hDBF are nullptrs");
87
0
    }
88
0
#endif
89
90
0
    if (!TouchLayer())
91
0
    {
92
0
        CPLDebug("Shape", "TouchLayer in shape ctor failed. ");
93
0
    }
94
95
0
    if (m_hDBF != nullptr && m_hDBF->pszCodePage != nullptr)
96
0
    {
97
0
        CPLDebug("Shape", "DBF Codepage = %s for %s", m_hDBF->pszCodePage,
98
0
                 m_osFullName.c_str());
99
100
        // Not too sure about this, but it seems like better than nothing.
101
0
        m_osEncoding = ConvertCodePage(m_hDBF->pszCodePage);
102
0
    }
103
104
0
    if (m_hDBF != nullptr)
105
0
    {
106
0
        if (!(m_hDBF->nUpdateYearSince1900 == 95 && m_hDBF->nUpdateMonth == 7 &&
107
0
              m_hDBF->nUpdateDay == 26))
108
0
        {
109
0
            SetMetadataItem("DBF_DATE_LAST_UPDATE",
110
0
                            CPLSPrintf("%04d-%02d-%02d",
111
0
                                       m_hDBF->nUpdateYearSince1900 + 1900,
112
0
                                       m_hDBF->nUpdateMonth,
113
0
                                       m_hDBF->nUpdateDay));
114
0
        }
115
0
        struct tm tm;
116
0
        CPLUnixTimeToYMDHMS(time(nullptr), &tm);
117
0
        DBFSetLastModifiedDate(m_hDBF, tm.tm_year, tm.tm_mon + 1, tm.tm_mday);
118
0
    }
119
120
0
    const char *pszShapeEncoding =
121
0
        CSLFetchNameValue(m_poDS->GetOpenOptions(), "ENCODING");
122
0
    if (pszShapeEncoding == nullptr)
123
0
        pszShapeEncoding = CPLGetConfigOption("SHAPE_ENCODING", nullptr);
124
0
    if (pszShapeEncoding != nullptr)
125
0
        m_osEncoding = pszShapeEncoding;
126
127
0
    if (m_osEncoding != "")
128
0
    {
129
0
        CPLDebug("Shape", "Treating as encoding '%s'.", m_osEncoding.c_str());
130
131
0
        if (!OGRShapeLayer::TestCapability(OLCStringsAsUTF8))
132
0
        {
133
0
            CPLDebug("Shape", "Cannot recode from '%s'. Disabling recoding",
134
0
                     m_osEncoding.c_str());
135
0
            m_osEncoding = "";
136
0
        }
137
0
    }
138
0
    SetMetadataItem("SOURCE_ENCODING", m_osEncoding, "SHAPEFILE");
139
140
0
    auto fpShpXML = VSIFilesystemHandler::OpenStatic(
141
0
        CPLResetExtensionSafe(m_osFullName.c_str(), "shp.xml").c_str(), "rb");
142
0
    m_bHasShpXML = fpShpXML != nullptr;
143
144
0
    m_poFeatureDefn = SHPReadOGRFeatureDefn(
145
0
        CPLGetBasenameSafe(m_osFullName.c_str()).c_str(), m_hSHP, m_hDBF,
146
0
        fpShpXML.get(), m_osEncoding,
147
0
        CPLFetchBool(m_poDS->GetOpenOptions(), "ADJUST_TYPE", false));
148
149
    // To make sure that
150
    //  GetLayerDefn()->GetGeomFieldDefn(0)->GetSpatialRef() == GetSpatialRef()
151
0
    OGRwkbGeometryType eGeomType = m_poFeatureDefn->GetGeomType();
152
0
    if (eGeomType != wkbNone)
153
0
    {
154
0
        OGRwkbGeometryType eType = wkbUnknown;
155
156
0
        if (m_eRequestedGeomType == wkbNone)
157
0
        {
158
0
            eType = eGeomType;
159
160
0
            const char *pszAdjustGeomType = CSLFetchNameValueDef(
161
0
                m_poDS->GetOpenOptions(), "ADJUST_GEOM_TYPE", "FIRST_SHAPE");
162
0
            const bool bFirstShape = EQUAL(pszAdjustGeomType, "FIRST_SHAPE");
163
0
            const bool bAllShapes = EQUAL(pszAdjustGeomType, "ALL_SHAPES");
164
0
            if ((m_hSHP != nullptr) && (m_hSHP->nRecords > 0) &&
165
0
                wkbHasM(eType) && (bFirstShape || bAllShapes))
166
0
            {
167
0
                bool bMIsUsed = false;
168
0
                for (int iShape = 0; iShape < m_hSHP->nRecords; iShape++)
169
0
                {
170
0
                    SHPObject *psShape = SHPReadObject(m_hSHP, iShape);
171
0
                    if (psShape)
172
0
                    {
173
0
                        if (psShape->bMeasureIsUsed && psShape->nVertices > 0 &&
174
0
                            psShape->padfM != nullptr)
175
0
                        {
176
0
                            for (int i = 0; i < psShape->nVertices; i++)
177
0
                            {
178
                                // Per the spec, if the M value is smaller than
179
                                // -1e38, it is a nodata value.
180
0
                                if (psShape->padfM[i] > -1e38)
181
0
                                {
182
0
                                    bMIsUsed = true;
183
0
                                    break;
184
0
                                }
185
0
                            }
186
0
                        }
187
188
0
                        SHPDestroyObject(psShape);
189
0
                    }
190
0
                    if (bFirstShape || bMIsUsed)
191
0
                        break;
192
0
                }
193
0
                if (!bMIsUsed)
194
0
                    eType = OGR_GT_SetModifier(eType, wkbHasZ(eType), FALSE);
195
0
            }
196
0
        }
197
0
        else
198
0
        {
199
0
            eType = m_eRequestedGeomType;
200
0
        }
201
202
0
        if (!bCreateLayer)
203
0
        {
204
0
            if (wkbFlatten(eType) == wkbLineString ||
205
0
                wkbFlatten(eType) == wkbPolygon)
206
0
            {
207
0
                if (CPLTestBool(CSLFetchNameValueDef(
208
0
                        m_poDS->GetOpenOptions(), "PROMOTE_TO_MULTI",
209
0
                        CPLGetConfigOption("SHAPE_PROMOTE_TO_MULTI", "YES"))))
210
0
                {
211
0
                    eType = OGR_GT_GetCollection(eType);
212
0
                }
213
0
            }
214
0
        }
215
216
0
        auto poSRSClone = OGRSpatialReferenceRefCountedPtr::makeClone(poSRSIn);
217
0
        if (poSRSClone)
218
0
        {
219
0
            poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
220
0
        }
221
0
        auto poGeomFieldDefn = std::make_unique<OGRShapeGeomFieldDefn>(
222
0
            m_osFullName.c_str(), eType, bSRSSetIn, poSRSClone.get());
223
0
        if (!osPrjFilename.empty())
224
0
            poGeomFieldDefn->SetPrjFilename(osPrjFilename);
225
0
        m_poFeatureDefn->SetGeomType(wkbNone);
226
0
        m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
227
0
    }
228
229
0
    SetDescription(m_poFeatureDefn->GetName());
230
0
    m_bRewindOnWrite = CPLTestBool(CPLGetConfigOption(
231
0
        "SHAPE_REWIND_ON_WRITE",
232
0
        m_hSHP != nullptr && m_hSHP->nShapeType != SHPT_MULTIPATCH ? "NO"
233
0
                                                                   : "YES"));
234
235
0
    m_poFeatureDefn->Seal(/* bSealFields = */ true);
236
0
}
237
238
/************************************************************************/
239
/*                           ~OGRShapeLayer()                           */
240
/************************************************************************/
241
242
OGRShapeLayer::~OGRShapeLayer()
243
244
0
{
245
0
    if (m_eNeedRepack == YES && m_bAutoRepack)
246
0
        Repack();
247
248
0
    if (m_bResizeAtClose && m_hDBF != nullptr)
249
0
    {
250
0
        ResizeDBF();
251
0
    }
252
0
    if (m_bCreateSpatialIndexAtClose && m_hSHP != nullptr)
253
0
    {
254
0
        CreateSpatialIndex(0);
255
0
    }
256
257
0
    if (m_nFeaturesRead > 0 && m_poFeatureDefn != nullptr)
258
0
    {
259
0
        CPLDebug("Shape", "%d features read on layer '%s'.",
260
0
                 static_cast<int>(m_nFeaturesRead), m_poFeatureDefn->GetName());
261
0
    }
262
263
0
    ClearMatchingFIDs();
264
0
    ClearSpatialFIDs();
265
266
0
    if (m_hDBF != nullptr)
267
0
        DBFClose(m_hDBF);
268
269
0
    if (m_hSHP != nullptr)
270
0
        SHPClose(m_hSHP);
271
272
0
    if (m_hQIX != nullptr)
273
0
        SHPCloseDiskTree(m_hQIX);
274
275
0
    if (m_hSBN != nullptr)
276
0
        SBNCloseDiskTree(m_hSBN);
277
0
}
278
279
/************************************************************************/
280
/*                        SetModificationDate()                         */
281
/************************************************************************/
282
283
void OGRShapeLayer::SetModificationDate(const char *pszStr)
284
0
{
285
0
    if (m_hDBF && pszStr)
286
0
    {
287
0
        int year = 0;
288
0
        int month = 0;
289
0
        int day = 0;
290
0
        if ((sscanf(pszStr, "%04d-%02d-%02d", &year, &month, &day) == 3 ||
291
0
             sscanf(pszStr, "%04d/%02d/%02d", &year, &month, &day) == 3) &&
292
0
            (year >= 1900 && year <= 1900 + 255 && month >= 1 && month <= 12 &&
293
0
             day >= 1 && day <= 31))
294
0
        {
295
0
            DBFSetLastModifiedDate(m_hDBF, year - 1900, month, day);
296
0
        }
297
0
    }
298
0
}
299
300
/************************************************************************/
301
/*                         SetWriteDBFEOFChar()                         */
302
/************************************************************************/
303
304
void OGRShapeLayer::SetWriteDBFEOFChar(bool b)
305
0
{
306
0
    if (m_hDBF)
307
0
    {
308
0
        DBFSetWriteEndOfFileChar(m_hDBF, b);
309
0
    }
310
0
}
311
312
/************************************************************************/
313
/*                          ConvertCodePage()                           */
314
/************************************************************************/
315
316
static CPLString GetEncodingFromLDIDNumber(int nLDID)
317
0
{
318
0
    int nCP = -1;  // Windows code page.
319
320
    // http://www.autopark.ru/ASBProgrammerGuide/DBFSTRUC.HTM
321
0
    switch (nLDID)
322
0
    {
323
0
        case 1:
324
0
            nCP = 437;
325
0
            break;
326
0
        case 2:
327
0
            nCP = 850;
328
0
            break;
329
0
        case 3:
330
0
            nCP = 1252;
331
0
            break;
332
0
        case 4:
333
0
            nCP = 10000;
334
0
            break;
335
0
        case 8:
336
0
            nCP = 865;
337
0
            break;
338
0
        case 10:
339
0
            nCP = 850;
340
0
            break;
341
0
        case 11:
342
0
            nCP = 437;
343
0
            break;
344
0
        case 13:
345
0
            nCP = 437;
346
0
            break;
347
0
        case 14:
348
0
            nCP = 850;
349
0
            break;
350
0
        case 15:
351
0
            nCP = 437;
352
0
            break;
353
0
        case 16:
354
0
            nCP = 850;
355
0
            break;
356
0
        case 17:
357
0
            nCP = 437;
358
0
            break;
359
0
        case 18:
360
0
            nCP = 850;
361
0
            break;
362
0
        case 19:
363
0
            nCP = 932;
364
0
            break;
365
0
        case 20:
366
0
            nCP = 850;
367
0
            break;
368
0
        case 21:
369
0
            nCP = 437;
370
0
            break;
371
0
        case 22:
372
0
            nCP = 850;
373
0
            break;
374
0
        case 23:
375
0
            nCP = 865;
376
0
            break;
377
0
        case 24:
378
0
            nCP = 437;
379
0
            break;
380
0
        case 25:
381
0
            nCP = 437;
382
0
            break;
383
0
        case 26:
384
0
            nCP = 850;
385
0
            break;
386
0
        case 27:
387
0
            nCP = 437;
388
0
            break;
389
0
        case 28:
390
0
            nCP = 863;
391
0
            break;
392
0
        case 29:
393
0
            nCP = 850;
394
0
            break;
395
0
        case 31:
396
0
            nCP = 852;
397
0
            break;
398
0
        case 34:
399
0
            nCP = 852;
400
0
            break;
401
0
        case 35:
402
0
            nCP = 852;
403
0
            break;
404
0
        case 36:
405
0
            nCP = 860;
406
0
            break;
407
0
        case 37:
408
0
            nCP = 850;
409
0
            break;
410
0
        case 38:
411
0
            nCP = 866;
412
0
            break;
413
0
        case 55:
414
0
            nCP = 850;
415
0
            break;
416
0
        case 64:
417
0
            nCP = 852;
418
0
            break;
419
0
        case 77:
420
0
            nCP = 936;
421
0
            break;
422
0
        case 78:
423
0
            nCP = 949;
424
0
            break;
425
0
        case 79:
426
0
            nCP = 950;
427
0
            break;
428
0
        case 80:
429
0
            nCP = 874;
430
0
            break;
431
0
        case 87:
432
0
            return CPL_ENC_ISO8859_1;
433
0
        case 88:
434
0
            nCP = 1252;
435
0
            break;
436
0
        case 89:
437
0
            nCP = 1252;
438
0
            break;
439
0
        case 100:
440
0
            nCP = 852;
441
0
            break;
442
0
        case 101:
443
0
            nCP = 866;
444
0
            break;
445
0
        case 102:
446
0
            nCP = 865;
447
0
            break;
448
0
        case 103:
449
0
            nCP = 861;
450
0
            break;
451
0
        case 104:
452
0
            nCP = 895;
453
0
            break;
454
0
        case 105:
455
0
            nCP = 620;
456
0
            break;
457
0
        case 106:
458
0
            nCP = 737;
459
0
            break;
460
0
        case 107:
461
0
            nCP = 857;
462
0
            break;
463
0
        case 108:
464
0
            nCP = 863;
465
0
            break;
466
0
        case 120:
467
0
            nCP = 950;
468
0
            break;
469
0
        case 121:
470
0
            nCP = 949;
471
0
            break;
472
0
        case 122:
473
0
            nCP = 936;
474
0
            break;
475
0
        case 123:
476
0
            nCP = 932;
477
0
            break;
478
0
        case 124:
479
0
            nCP = 874;
480
0
            break;
481
0
        case 134:
482
0
            nCP = 737;
483
0
            break;
484
0
        case 135:
485
0
            nCP = 852;
486
0
            break;
487
0
        case 136:
488
0
            nCP = 857;
489
0
            break;
490
0
        case 150:
491
0
            nCP = 10007;
492
0
            break;
493
0
        case 151:
494
0
            nCP = 10029;
495
0
            break;
496
0
        case 200:
497
0
            nCP = 1250;
498
0
            break;
499
0
        case 201:
500
0
            nCP = 1251;
501
0
            break;
502
0
        case 202:
503
0
            nCP = 1254;
504
0
            break;
505
0
        case 203:
506
0
            nCP = 1253;
507
0
            break;
508
0
        case 204:
509
0
            nCP = 1257;
510
0
            break;
511
0
        default:
512
0
            break;
513
0
    }
514
515
0
    if (nCP < 0)
516
0
        return CPLString();
517
0
    return CPLString().Printf("CP%d", nCP);
518
0
}
519
520
static CPLString GetEncodingFromCPG(const char *pszCPG)
521
0
{
522
    // see https://support.esri.com/en/technical-article/000013192
523
0
    CPLString m_osEncodingFromCPG;
524
0
    const int nCPG = atoi(pszCPG);
525
0
    if ((nCPG >= 437 && nCPG <= 950) || (nCPG >= 1250 && nCPG <= 1258))
526
0
    {
527
0
        m_osEncodingFromCPG.Printf("CP%d", nCPG);
528
0
    }
529
0
    else if (STARTS_WITH_CI(pszCPG, "8859"))
530
0
    {
531
0
        if (pszCPG[4] == '-')
532
0
            m_osEncodingFromCPG.Printf("ISO-8859-%s", pszCPG + 5);
533
0
        else
534
0
            m_osEncodingFromCPG.Printf("ISO-8859-%s", pszCPG + 4);
535
0
    }
536
0
    else if (STARTS_WITH_CI(pszCPG, "UTF-8") || STARTS_WITH_CI(pszCPG, "UTF8"))
537
0
        m_osEncodingFromCPG = CPL_ENC_UTF8;
538
0
    else if (STARTS_WITH_CI(pszCPG, "ANSI 1251"))
539
0
        m_osEncodingFromCPG = "CP1251";
540
0
    else
541
0
    {
542
        // Try just using the CPG value directly.  Works for stuff like Big5.
543
0
        m_osEncodingFromCPG = pszCPG;
544
0
    }
545
0
    return m_osEncodingFromCPG;
546
0
}
547
548
CPLString OGRShapeLayer::ConvertCodePage(const char *pszCodePage)
549
550
0
{
551
0
    CPLString l_m_osEncoding;
552
553
0
    if (pszCodePage == nullptr)
554
0
        return l_m_osEncoding;
555
556
0
    std::string m_osEncodingFromLDID;
557
0
    if (m_hDBF->iLanguageDriver != 0)
558
0
    {
559
0
        SetMetadataItem("LDID_VALUE", CPLSPrintf("%d", m_hDBF->iLanguageDriver),
560
0
                        "SHAPEFILE");
561
562
0
        m_osEncodingFromLDID =
563
0
            GetEncodingFromLDIDNumber(m_hDBF->iLanguageDriver);
564
0
    }
565
0
    if (!m_osEncodingFromLDID.empty())
566
0
    {
567
0
        SetMetadataItem("ENCODING_FROM_LDID", m_osEncodingFromLDID.c_str(),
568
0
                        "SHAPEFILE");
569
0
    }
570
571
0
    std::string m_osEncodingFromCPG;
572
0
    if (!STARTS_WITH_CI(pszCodePage, "LDID/"))
573
0
    {
574
0
        SetMetadataItem("CPG_VALUE", pszCodePage, "SHAPEFILE");
575
576
0
        m_osEncodingFromCPG = GetEncodingFromCPG(pszCodePage);
577
578
0
        if (!m_osEncodingFromCPG.empty())
579
0
            SetMetadataItem("ENCODING_FROM_CPG", m_osEncodingFromCPG.c_str(),
580
0
                            "SHAPEFILE");
581
582
0
        l_m_osEncoding = std::move(m_osEncodingFromCPG);
583
0
    }
584
0
    else if (!m_osEncodingFromLDID.empty())
585
0
    {
586
0
        l_m_osEncoding = std::move(m_osEncodingFromLDID);
587
0
    }
588
589
0
    return l_m_osEncoding;
590
0
}
591
592
/************************************************************************/
593
/*                            CheckForQIX()                             */
594
/************************************************************************/
595
596
bool OGRShapeLayer::CheckForQIX()
597
598
0
{
599
0
    if (m_bCheckedForQIX)
600
0
        return m_hQIX != nullptr;
601
602
0
    const std::string osQIXFilename =
603
0
        CPLResetExtensionSafe(m_osFullName.c_str(), "qix");
604
605
0
    m_hQIX = SHPOpenDiskTree(osQIXFilename.c_str(), nullptr);
606
607
0
    m_bCheckedForQIX = true;
608
609
0
    return m_hQIX != nullptr;
610
0
}
611
612
/************************************************************************/
613
/*                            CheckForSBN()                             */
614
/************************************************************************/
615
616
bool OGRShapeLayer::CheckForSBN()
617
618
0
{
619
0
    if (m_bCheckedForSBN)
620
0
        return m_hSBN != nullptr;
621
622
0
    const std::string osSBNFilename =
623
0
        CPLResetExtensionSafe(m_osFullName.c_str(), "sbn");
624
625
0
    m_hSBN = SBNOpenDiskTree(osSBNFilename.c_str(), nullptr);
626
627
0
    m_bCheckedForSBN = true;
628
629
0
    return m_hSBN != nullptr;
630
0
}
631
632
/************************************************************************/
633
/*                            ScanIndices()                             */
634
/*                                                                      */
635
/*      Utilize optional spatial and attribute indices if they are      */
636
/*      available.                                                      */
637
/************************************************************************/
638
639
bool OGRShapeLayer::ScanIndices()
640
641
0
{
642
0
    m_iMatchingFID = 0;
643
644
    /* -------------------------------------------------------------------- */
645
    /*      Utilize attribute index if appropriate.                         */
646
    /* -------------------------------------------------------------------- */
647
0
    if (m_poAttrQuery != nullptr)
648
0
    {
649
0
        CPLAssert(m_panMatchingFIDs == nullptr);
650
651
0
        InitializeIndexSupport(m_osFullName.c_str());
652
653
0
        m_panMatchingFIDs =
654
0
            m_poAttrQuery->EvaluateAgainstIndices(this, nullptr);
655
0
    }
656
657
    /* -------------------------------------------------------------------- */
658
    /*      Check for spatial index if we have a spatial query.             */
659
    /* -------------------------------------------------------------------- */
660
661
0
    if (m_poFilterGeom == nullptr || m_hSHP == nullptr)
662
0
        return true;
663
664
0
    OGREnvelope oSpatialFilterEnvelope;
665
0
    bool bTryQIXorSBN = true;
666
667
0
    m_poFilterGeom->getEnvelope(&oSpatialFilterEnvelope);
668
669
0
    OGREnvelope oLayerExtent;
670
0
    if (GetExtent(&oLayerExtent, TRUE) == OGRERR_NONE)
671
0
    {
672
0
        if (oSpatialFilterEnvelope.Contains(oLayerExtent))
673
0
        {
674
            // The spatial filter is larger than the layer extent. No use of
675
            // .qix file for now.
676
0
            return true;
677
0
        }
678
0
        else if (!oSpatialFilterEnvelope.Intersects(oLayerExtent))
679
0
        {
680
            // No intersection : no need to check for .qix or .sbn.
681
0
            bTryQIXorSBN = false;
682
683
            // Set an empty result for spatial FIDs.
684
0
            free(m_panSpatialFIDs);
685
0
            m_panSpatialFIDs = static_cast<int *>(calloc(1, sizeof(int)));
686
0
            m_nSpatialFIDCount = 0;
687
688
0
            delete m_poFilterGeomLastValid;
689
0
            m_poFilterGeomLastValid = m_poFilterGeom->clone();
690
0
        }
691
0
    }
692
693
0
    if (bTryQIXorSBN)
694
0
    {
695
0
        if (!m_bCheckedForQIX)
696
0
            CPL_IGNORE_RET_VAL(CheckForQIX());
697
0
        if (m_hQIX == nullptr && !m_bCheckedForSBN)
698
0
            CPL_IGNORE_RET_VAL(CheckForSBN());
699
0
    }
700
701
    /* -------------------------------------------------------------------- */
702
    /*      Compute spatial index if appropriate.                           */
703
    /* -------------------------------------------------------------------- */
704
0
    if (bTryQIXorSBN && (m_hQIX != nullptr || m_hSBN != nullptr) &&
705
0
        m_panSpatialFIDs == nullptr)
706
0
    {
707
0
        double adfBoundsMin[4] = {oSpatialFilterEnvelope.MinX,
708
0
                                  oSpatialFilterEnvelope.MinY, 0.0, 0.0};
709
0
        double adfBoundsMax[4] = {oSpatialFilterEnvelope.MaxX,
710
0
                                  oSpatialFilterEnvelope.MaxY, 0.0, 0.0};
711
712
0
        if (m_hQIX != nullptr)
713
0
            m_panSpatialFIDs = SHPSearchDiskTreeEx(
714
0
                m_hQIX, adfBoundsMin, adfBoundsMax, &m_nSpatialFIDCount);
715
0
        else
716
0
            m_panSpatialFIDs = SBNSearchDiskTree(
717
0
                m_hSBN, adfBoundsMin, adfBoundsMax, &m_nSpatialFIDCount);
718
719
0
        CPLDebug("SHAPE", "Used spatial index, got %d matches.",
720
0
                 m_nSpatialFIDCount);
721
722
0
        delete m_poFilterGeomLastValid;
723
0
        m_poFilterGeomLastValid = m_poFilterGeom->clone();
724
0
    }
725
726
    /* -------------------------------------------------------------------- */
727
    /*      Use spatial index if appropriate.                               */
728
    /* -------------------------------------------------------------------- */
729
0
    if (m_panSpatialFIDs != nullptr)
730
0
    {
731
        // Use resulting list as matching FID list (but reallocate and
732
        // terminate with OGRNullFID).
733
0
        if (m_panMatchingFIDs == nullptr)
734
0
        {
735
0
            m_panMatchingFIDs = static_cast<GIntBig *>(
736
0
                CPLMalloc(sizeof(GIntBig) * (m_nSpatialFIDCount + 1)));
737
0
            for (int i = 0; i < m_nSpatialFIDCount; i++)
738
0
                m_panMatchingFIDs[i] =
739
0
                    static_cast<GIntBig>(m_panSpatialFIDs[i]);
740
0
            m_panMatchingFIDs[m_nSpatialFIDCount] = OGRNullFID;
741
0
        }
742
        // Cull attribute index matches based on those in the spatial index
743
        // result set.  We assume that the attribute results are in sorted
744
        // order.
745
0
        else
746
0
        {
747
0
            int iWrite = 0;
748
0
            int iSpatial = 0;
749
750
0
            for (int iRead = 0; m_panMatchingFIDs[iRead] != OGRNullFID; iRead++)
751
0
            {
752
0
                while (iSpatial < m_nSpatialFIDCount &&
753
0
                       m_panSpatialFIDs[iSpatial] < m_panMatchingFIDs[iRead])
754
0
                    iSpatial++;
755
756
0
                if (iSpatial == m_nSpatialFIDCount)
757
0
                    continue;
758
759
0
                if (m_panSpatialFIDs[iSpatial] == m_panMatchingFIDs[iRead])
760
0
                    m_panMatchingFIDs[iWrite++] = m_panMatchingFIDs[iRead];
761
0
            }
762
0
            m_panMatchingFIDs[iWrite] = OGRNullFID;
763
0
        }
764
765
0
        if (m_nSpatialFIDCount > 100000)
766
0
        {
767
0
            ClearSpatialFIDs();
768
0
        }
769
0
    }
770
771
0
    return true;
772
0
}
773
774
/************************************************************************/
775
/*                            ResetReading()                            */
776
/************************************************************************/
777
778
void OGRShapeLayer::ResetReading()
779
780
0
{
781
0
    if (!TouchLayer())
782
0
        return;
783
784
0
    m_iMatchingFID = 0;
785
786
0
    m_iNextShapeId = 0;
787
788
0
    if (m_bHeaderDirty && m_bUpdateAccess)
789
0
        SyncToDisk();
790
791
0
    if (m_hDBF)
792
0
        VSIFClearErrL(VSI_SHP_GetVSIL(m_hDBF->fp));
793
0
}
794
795
/************************************************************************/
796
/*                         ClearMatchingFIDs()                          */
797
/************************************************************************/
798
799
void OGRShapeLayer::ClearMatchingFIDs()
800
0
{
801
    /* -------------------------------------------------------------------- */
802
    /*      Clear previous index search result, if any.                     */
803
    /* -------------------------------------------------------------------- */
804
0
    CPLFree(m_panMatchingFIDs);
805
0
    m_panMatchingFIDs = nullptr;
806
0
}
807
808
/************************************************************************/
809
/*                          ClearSpatialFIDs()                          */
810
/************************************************************************/
811
812
void OGRShapeLayer::ClearSpatialFIDs()
813
0
{
814
0
    if (m_panSpatialFIDs != nullptr)
815
0
    {
816
0
        CPLDebug("SHAPE", "Clear m_panSpatialFIDs");
817
0
        free(m_panSpatialFIDs);
818
0
    }
819
0
    m_panSpatialFIDs = nullptr;
820
0
    m_nSpatialFIDCount = 0;
821
822
0
    delete m_poFilterGeomLastValid;
823
0
    m_poFilterGeomLastValid = nullptr;
824
0
}
825
826
/************************************************************************/
827
/*                         ISetSpatialFilter()                          */
828
/************************************************************************/
829
830
OGRErr OGRShapeLayer::ISetSpatialFilter(int iGeomField,
831
                                        const OGRGeometry *poGeomIn)
832
0
{
833
0
    ClearMatchingFIDs();
834
835
0
    if (poGeomIn == nullptr)
836
0
    {
837
        // Do nothing.
838
0
    }
839
0
    else if (m_poFilterGeomLastValid != nullptr &&
840
0
             m_poFilterGeomLastValid->Equals(poGeomIn))
841
0
    {
842
        // Do nothing.
843
0
    }
844
0
    else if (m_panSpatialFIDs != nullptr)
845
0
    {
846
        // We clear the spatialFIDs only if we have a new non-NULL spatial
847
        // filter, otherwise we keep the previous result cached. This can be
848
        // useful when several SQL layers rely on the same table layer, and use
849
        // the same spatial filters. But as there is in the destructor of
850
        // OGRGenSQLResultsLayer a clearing of the spatial filter of the table
851
        // layer, we need this trick.
852
0
        ClearSpatialFIDs();
853
0
    }
854
855
0
    return OGRLayer::ISetSpatialFilter(iGeomField, poGeomIn);
856
0
}
857
858
/************************************************************************/
859
/*                         SetAttributeFilter()                         */
860
/************************************************************************/
861
862
OGRErr OGRShapeLayer::SetAttributeFilter(const char *pszAttributeFilter)
863
0
{
864
0
    ClearMatchingFIDs();
865
866
0
    return OGRLayer::SetAttributeFilter(pszAttributeFilter);
867
0
}
868
869
/************************************************************************/
870
/*                           SetNextByIndex()                           */
871
/*                                                                      */
872
/*      If we already have an FID list, we can easily reposition        */
873
/*      ourselves in it.                                                */
874
/************************************************************************/
875
876
OGRErr OGRShapeLayer::SetNextByIndex(GIntBig nIndex)
877
878
0
{
879
0
    if (!TouchLayer())
880
0
        return OGRERR_FAILURE;
881
882
0
    if (nIndex < 0 || nIndex >= m_nTotalShapeCount)
883
0
    {
884
0
        m_iNextShapeId = m_nTotalShapeCount;
885
0
        return OGRERR_NON_EXISTING_FEATURE;
886
0
    }
887
888
    // Eventually we should try to use m_panMatchingFIDs list
889
    // if available and appropriate.
890
0
    if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
891
0
        return OGRLayer::SetNextByIndex(nIndex);
892
893
0
    m_iNextShapeId = static_cast<int>(nIndex);
894
895
0
    return OGRERR_NONE;
896
0
}
897
898
/************************************************************************/
899
/*                             FetchShape()                             */
900
/*                                                                      */
901
/*      Take a shape id, a geometry, and a feature, and set the feature */
902
/*      if the shapeid bbox intersects the geometry.                    */
903
/************************************************************************/
904
905
OGRFeature *OGRShapeLayer::FetchShape(int iShapeId)
906
907
0
{
908
0
    OGRFeature *poFeature = nullptr;
909
910
0
    if (m_poFilterGeom != nullptr && m_hSHP != nullptr)
911
0
    {
912
0
        SHPObject *psShape = SHPReadObject(m_hSHP, iShapeId);
913
914
        // do not trust degenerate bounds on non-point geometries
915
        // or bounds on null shapes.
916
0
        if (psShape == nullptr ||
917
0
            (psShape->nSHPType != SHPT_POINT &&
918
0
             psShape->nSHPType != SHPT_POINTZ &&
919
0
             psShape->nSHPType != SHPT_POINTM &&
920
0
             (psShape->dfXMin == psShape->dfXMax ||
921
0
              psShape->dfYMin == psShape->dfYMax)) ||
922
0
            psShape->nSHPType == SHPT_NULL)
923
0
        {
924
0
            poFeature = SHPReadOGRFeature(m_hSHP, m_hDBF, m_poFeatureDefn.get(),
925
0
                                          iShapeId, psShape, m_osEncoding,
926
0
                                          m_bHasWarnedWrongWindingOrder);
927
0
        }
928
0
        else if (m_sFilterEnvelope.MaxX < psShape->dfXMin ||
929
0
                 m_sFilterEnvelope.MaxY < psShape->dfYMin ||
930
0
                 psShape->dfXMax < m_sFilterEnvelope.MinX ||
931
0
                 psShape->dfYMax < m_sFilterEnvelope.MinY)
932
0
        {
933
0
            SHPDestroyObject(psShape);
934
0
            poFeature = nullptr;
935
0
        }
936
0
        else
937
0
        {
938
0
            poFeature = SHPReadOGRFeature(m_hSHP, m_hDBF, m_poFeatureDefn.get(),
939
0
                                          iShapeId, psShape, m_osEncoding,
940
0
                                          m_bHasWarnedWrongWindingOrder);
941
0
        }
942
0
    }
943
0
    else
944
0
    {
945
0
        poFeature = SHPReadOGRFeature(m_hSHP, m_hDBF, m_poFeatureDefn.get(),
946
0
                                      iShapeId, nullptr, m_osEncoding,
947
0
                                      m_bHasWarnedWrongWindingOrder);
948
0
    }
949
950
0
    return poFeature;
951
0
}
952
953
/************************************************************************/
954
/*                           GetNextFeature()                           */
955
/************************************************************************/
956
957
OGRFeature *OGRShapeLayer::GetNextFeature()
958
959
0
{
960
0
    if (!TouchLayer())
961
0
        return nullptr;
962
963
    /* -------------------------------------------------------------------- */
964
    /*      Collect a matching list if we have attribute or spatial         */
965
    /*      indices.  Only do this on the first request for a given pass    */
966
    /*      of course.                                                      */
967
    /* -------------------------------------------------------------------- */
968
0
    if ((m_poAttrQuery != nullptr || m_poFilterGeom != nullptr) &&
969
0
        m_iNextShapeId == 0 && m_panMatchingFIDs == nullptr)
970
0
    {
971
0
        ScanIndices();
972
0
    }
973
974
    /* -------------------------------------------------------------------- */
975
    /*      Loop till we find a feature matching our criteria.              */
976
    /* -------------------------------------------------------------------- */
977
0
    OGRFeature *poFeature = nullptr;
978
979
0
    while (true)
980
0
    {
981
0
        if (m_panMatchingFIDs != nullptr)
982
0
        {
983
0
            if (m_panMatchingFIDs[m_iMatchingFID] == OGRNullFID)
984
0
            {
985
0
                return nullptr;
986
0
            }
987
988
            // Check the shape object's geometry, and if it matches
989
            // any spatial filter, return it.
990
0
            poFeature =
991
0
                FetchShape(static_cast<int>(m_panMatchingFIDs[m_iMatchingFID]));
992
993
0
            m_iMatchingFID++;
994
0
        }
995
0
        else
996
0
        {
997
0
            if (m_iNextShapeId >= m_nTotalShapeCount)
998
0
            {
999
0
                return nullptr;
1000
0
            }
1001
1002
0
            if (m_hDBF)
1003
0
            {
1004
0
                if (DBFIsRecordDeleted(m_hDBF, m_iNextShapeId))
1005
0
                    poFeature = nullptr;
1006
0
                else if (VSIFEofL(VSI_SHP_GetVSIL(m_hDBF->fp)) ||
1007
0
                         VSIFErrorL(VSI_SHP_GetVSIL(m_hDBF->fp)))
1008
0
                    return nullptr;  //* I/O error.
1009
0
                else
1010
0
                    poFeature = FetchShape(m_iNextShapeId);
1011
0
            }
1012
0
            else
1013
0
                poFeature = FetchShape(m_iNextShapeId);
1014
1015
0
            m_iNextShapeId++;
1016
0
        }
1017
1018
0
        if (poFeature != nullptr)
1019
0
        {
1020
0
            OGRGeometry *poGeom = poFeature->GetGeometryRef();
1021
0
            if (poGeom != nullptr)
1022
0
            {
1023
0
                poGeom->assignSpatialReference(GetSpatialRef());
1024
0
            }
1025
1026
0
            m_nFeaturesRead++;
1027
1028
0
            if ((m_poFilterGeom == nullptr || FilterGeometry(poGeom)) &&
1029
0
                (m_poAttrQuery == nullptr ||
1030
0
                 m_poAttrQuery->Evaluate(poFeature)))
1031
0
            {
1032
0
                return poFeature;
1033
0
            }
1034
1035
0
            delete poFeature;
1036
0
        }
1037
0
    }
1038
0
}
1039
1040
/************************************************************************/
1041
/*                             GetFeature()                             */
1042
/************************************************************************/
1043
1044
OGRFeature *OGRShapeLayer::GetFeature(GIntBig nFeatureId)
1045
1046
0
{
1047
0
    if (!TouchLayer() || nFeatureId > INT_MAX)
1048
0
        return nullptr;
1049
1050
0
    OGRFeature *poFeature = SHPReadOGRFeature(
1051
0
        m_hSHP, m_hDBF, m_poFeatureDefn.get(), static_cast<int>(nFeatureId),
1052
0
        nullptr, m_osEncoding, m_bHasWarnedWrongWindingOrder);
1053
1054
0
    if (poFeature == nullptr)
1055
0
    {
1056
        // Reading shape feature failed.
1057
0
        return nullptr;
1058
0
    }
1059
1060
0
    if (poFeature->GetGeometryRef() != nullptr)
1061
0
    {
1062
0
        poFeature->GetGeometryRef()->assignSpatialReference(GetSpatialRef());
1063
0
    }
1064
1065
0
    m_nFeaturesRead++;
1066
1067
0
    return poFeature;
1068
0
}
1069
1070
/************************************************************************/
1071
/*                            StartUpdate()                             */
1072
/************************************************************************/
1073
1074
bool OGRShapeLayer::StartUpdate(const char *pszOperation)
1075
0
{
1076
0
    if (!m_poDS->UncompressIfNeeded())
1077
0
        return false;
1078
1079
0
    if (!TouchLayer())
1080
0
        return false;
1081
1082
0
    if (!m_bUpdateAccess)
1083
0
    {
1084
0
        CPLError(CE_Failure, CPLE_NotSupported,
1085
0
                 "%s : unsupported operation on a read-only datasource.",
1086
0
                 pszOperation);
1087
0
        return false;
1088
0
    }
1089
1090
0
    return true;
1091
0
}
1092
1093
/************************************************************************/
1094
/*                            ISetFeature()                             */
1095
/************************************************************************/
1096
1097
OGRErr OGRShapeLayer::ISetFeature(OGRFeature *poFeature)
1098
1099
0
{
1100
0
    if (!StartUpdate("SetFeature"))
1101
0
        return OGRERR_FAILURE;
1102
1103
0
    GIntBig nFID = poFeature->GetFID();
1104
0
    if (nFID < 0 || (m_hSHP != nullptr && nFID >= m_hSHP->nRecords) ||
1105
0
        (m_hDBF != nullptr && nFID >= m_hDBF->nRecords))
1106
0
    {
1107
0
        return OGRERR_NON_EXISTING_FEATURE;
1108
0
    }
1109
1110
0
    m_bHeaderDirty = true;
1111
0
    if (CheckForQIX() || CheckForSBN())
1112
0
        DropSpatialIndex();
1113
1114
0
    unsigned int nOffset = 0;
1115
0
    unsigned int nSize = 0;
1116
0
    bool bIsLastRecord = false;
1117
0
    if (m_hSHP != nullptr)
1118
0
    {
1119
0
        nOffset = m_hSHP->panRecOffset[nFID];
1120
0
        nSize = m_hSHP->panRecSize[nFID];
1121
0
        bIsLastRecord = (nOffset + nSize + 8 == m_hSHP->nFileSize);
1122
0
    }
1123
1124
0
    OGRErr eErr = SHPWriteOGRFeature(
1125
0
        m_hSHP, m_hDBF, m_poFeatureDefn.get(), poFeature, m_osEncoding,
1126
0
        &m_bTruncationWarningEmitted, m_bRewindOnWrite);
1127
1128
0
    if (m_hSHP != nullptr)
1129
0
    {
1130
0
        if (bIsLastRecord)
1131
0
        {
1132
            // Optimization: we don't need repacking if this is the last
1133
            // record of the file. Just potential truncation
1134
0
            CPLAssert(nOffset == m_hSHP->panRecOffset[nFID]);
1135
0
            CPLAssert(m_hSHP->panRecOffset[nFID] + m_hSHP->panRecSize[nFID] +
1136
0
                          8 ==
1137
0
                      m_hSHP->nFileSize);
1138
0
            if (m_hSHP->panRecSize[nFID] < nSize)
1139
0
            {
1140
0
                VSIFTruncateL(VSI_SHP_GetVSIL(m_hSHP->fpSHP),
1141
0
                              m_hSHP->nFileSize);
1142
0
            }
1143
0
        }
1144
0
        else if (nOffset != m_hSHP->panRecOffset[nFID] ||
1145
0
                 nSize != m_hSHP->panRecSize[nFID])
1146
0
        {
1147
0
            m_bSHPNeedsRepack = true;
1148
0
            m_eNeedRepack = YES;
1149
0
        }
1150
0
    }
1151
1152
0
    return eErr;
1153
0
}
1154
1155
/************************************************************************/
1156
/*                           DeleteFeature()                            */
1157
/************************************************************************/
1158
1159
OGRErr OGRShapeLayer::DeleteFeature(GIntBig nFID)
1160
1161
0
{
1162
0
    if (!StartUpdate("DeleteFeature"))
1163
0
        return OGRERR_FAILURE;
1164
1165
0
    if (nFID < 0 || (m_hSHP != nullptr && nFID >= m_hSHP->nRecords) ||
1166
0
        (m_hDBF != nullptr && nFID >= m_hDBF->nRecords))
1167
0
    {
1168
0
        return OGRERR_NON_EXISTING_FEATURE;
1169
0
    }
1170
1171
0
    if (!m_hDBF)
1172
0
    {
1173
0
        CPLError(CE_Failure, CPLE_AppDefined,
1174
0
                 "Attempt to delete shape in shapefile with no .dbf file.  "
1175
0
                 "Deletion is done by marking record deleted in dbf "
1176
0
                 "and is not supported without a .dbf file.");
1177
0
        return OGRERR_FAILURE;
1178
0
    }
1179
1180
0
    if (DBFIsRecordDeleted(m_hDBF, static_cast<int>(nFID)))
1181
0
    {
1182
0
        return OGRERR_NON_EXISTING_FEATURE;
1183
0
    }
1184
1185
0
    if (!DBFMarkRecordDeleted(m_hDBF, static_cast<int>(nFID), TRUE))
1186
0
        return OGRERR_FAILURE;
1187
1188
0
    m_bHeaderDirty = true;
1189
0
    if (CheckForQIX() || CheckForSBN())
1190
0
        DropSpatialIndex();
1191
0
    m_eNeedRepack = YES;
1192
1193
0
    return OGRERR_NONE;
1194
0
}
1195
1196
/************************************************************************/
1197
/*                           ICreateFeature()                           */
1198
/************************************************************************/
1199
1200
OGRErr OGRShapeLayer::ICreateFeature(OGRFeature *poFeature)
1201
1202
0
{
1203
0
    if (!StartUpdate("CreateFeature"))
1204
0
        return OGRERR_FAILURE;
1205
1206
0
    if (m_hDBF != nullptr &&
1207
0
        !VSI_SHP_WriteMoreDataOK(m_hDBF->fp, m_hDBF->nRecordLength))
1208
0
    {
1209
0
        return OGRERR_FAILURE;
1210
0
    }
1211
1212
0
    m_bHeaderDirty = true;
1213
0
    if (CheckForQIX() || CheckForSBN())
1214
0
        DropSpatialIndex();
1215
1216
0
    poFeature->SetFID(OGRNullFID);
1217
1218
0
    if (m_nTotalShapeCount == 0 &&
1219
0
        wkbFlatten(m_eRequestedGeomType) == wkbUnknown && m_hSHP != nullptr &&
1220
0
        m_hSHP->nShapeType != SHPT_MULTIPATCH &&
1221
0
        poFeature->GetGeometryRef() != nullptr)
1222
0
    {
1223
0
        OGRGeometry *poGeom = poFeature->GetGeometryRef();
1224
0
        int nShapeType = -1;
1225
1226
0
        switch (poGeom->getGeometryType())
1227
0
        {
1228
0
            case wkbPoint:
1229
0
                nShapeType = SHPT_POINT;
1230
0
                m_eRequestedGeomType = wkbPoint;
1231
0
                break;
1232
1233
0
            case wkbPoint25D:
1234
0
                nShapeType = SHPT_POINTZ;
1235
0
                m_eRequestedGeomType = wkbPoint25D;
1236
0
                break;
1237
1238
0
            case wkbPointM:
1239
0
                nShapeType = SHPT_POINTM;
1240
0
                m_eRequestedGeomType = wkbPointM;
1241
0
                break;
1242
1243
0
            case wkbPointZM:
1244
0
                nShapeType = SHPT_POINTZ;
1245
0
                m_eRequestedGeomType = wkbPointZM;
1246
0
                break;
1247
1248
0
            case wkbMultiPoint:
1249
0
                nShapeType = SHPT_MULTIPOINT;
1250
0
                m_eRequestedGeomType = wkbMultiPoint;
1251
0
                break;
1252
1253
0
            case wkbMultiPoint25D:
1254
0
                nShapeType = SHPT_MULTIPOINTZ;
1255
0
                m_eRequestedGeomType = wkbMultiPoint25D;
1256
0
                break;
1257
1258
0
            case wkbMultiPointM:
1259
0
                nShapeType = SHPT_MULTIPOINTM;
1260
0
                m_eRequestedGeomType = wkbMultiPointM;
1261
0
                break;
1262
1263
0
            case wkbMultiPointZM:
1264
0
                nShapeType = SHPT_MULTIPOINTZ;
1265
0
                m_eRequestedGeomType = wkbMultiPointM;
1266
0
                break;
1267
1268
0
            case wkbLineString:
1269
0
            case wkbMultiLineString:
1270
0
                nShapeType = SHPT_ARC;
1271
0
                m_eRequestedGeomType = wkbMultiLineString;
1272
0
                break;
1273
1274
0
            case wkbLineString25D:
1275
0
            case wkbMultiLineString25D:
1276
0
                nShapeType = SHPT_ARCZ;
1277
0
                m_eRequestedGeomType = wkbMultiLineString25D;
1278
0
                break;
1279
1280
0
            case wkbLineStringM:
1281
0
            case wkbMultiLineStringM:
1282
0
                nShapeType = SHPT_ARCM;
1283
0
                m_eRequestedGeomType = wkbMultiLineStringM;
1284
0
                break;
1285
1286
0
            case wkbLineStringZM:
1287
0
            case wkbMultiLineStringZM:
1288
0
                nShapeType = SHPT_ARCZ;
1289
0
                m_eRequestedGeomType = wkbMultiLineStringZM;
1290
0
                break;
1291
1292
0
            case wkbPolygon:
1293
0
            case wkbMultiPolygon:
1294
0
            case wkbTriangle:
1295
0
                nShapeType = SHPT_POLYGON;
1296
0
                m_eRequestedGeomType = wkbMultiPolygon;
1297
0
                break;
1298
1299
0
            case wkbPolygon25D:
1300
0
            case wkbMultiPolygon25D:
1301
0
            case wkbTriangleZ:
1302
0
                nShapeType = SHPT_POLYGONZ;
1303
0
                m_eRequestedGeomType = wkbMultiPolygon25D;
1304
0
                break;
1305
1306
0
            case wkbPolygonM:
1307
0
            case wkbMultiPolygonM:
1308
0
            case wkbTriangleM:
1309
0
                nShapeType = SHPT_POLYGONM;
1310
0
                m_eRequestedGeomType = wkbMultiPolygonM;
1311
0
                break;
1312
1313
0
            case wkbPolygonZM:
1314
0
            case wkbMultiPolygonZM:
1315
0
            case wkbTriangleZM:
1316
0
                nShapeType = SHPT_POLYGONZ;
1317
0
                m_eRequestedGeomType = wkbMultiPolygonZM;
1318
0
                break;
1319
1320
0
            default:
1321
0
                nShapeType = -1;
1322
0
                break;
1323
0
        }
1324
1325
0
        if (wkbFlatten(poGeom->getGeometryType()) == wkbTIN ||
1326
0
            wkbFlatten(poGeom->getGeometryType()) == wkbPolyhedralSurface)
1327
0
        {
1328
0
            nShapeType = SHPT_MULTIPATCH;
1329
0
            m_eRequestedGeomType = wkbUnknown;
1330
0
        }
1331
1332
0
        if (wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
1333
0
        {
1334
0
            const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
1335
0
            bool bIsMultiPatchCompatible = false;
1336
0
            for (int iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++)
1337
0
            {
1338
0
                OGRwkbGeometryType eSubGeomType =
1339
0
                    wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType());
1340
0
                if (eSubGeomType == wkbTIN ||
1341
0
                    eSubGeomType == wkbPolyhedralSurface)
1342
0
                {
1343
0
                    bIsMultiPatchCompatible = true;
1344
0
                }
1345
0
                else if (eSubGeomType != wkbMultiPolygon)
1346
0
                {
1347
0
                    bIsMultiPatchCompatible = false;
1348
0
                    break;
1349
0
                }
1350
0
            }
1351
0
            if (bIsMultiPatchCompatible)
1352
0
            {
1353
0
                nShapeType = SHPT_MULTIPATCH;
1354
0
                m_eRequestedGeomType = wkbUnknown;
1355
0
            }
1356
0
        }
1357
1358
0
        if (nShapeType != -1)
1359
0
        {
1360
0
            whileUnsealing(m_poFeatureDefn)->SetGeomType(m_eRequestedGeomType);
1361
0
            ResetGeomType(nShapeType);
1362
0
        }
1363
0
    }
1364
1365
0
    const OGRErr eErr = SHPWriteOGRFeature(
1366
0
        m_hSHP, m_hDBF, m_poFeatureDefn.get(), poFeature, m_osEncoding,
1367
0
        &m_bTruncationWarningEmitted, m_bRewindOnWrite);
1368
1369
0
    if (m_hSHP != nullptr)
1370
0
        m_nTotalShapeCount = m_hSHP->nRecords;
1371
0
    else if (m_hDBF != nullptr)
1372
0
        m_nTotalShapeCount = m_hDBF->nRecords;
1373
0
#ifdef DEBUG
1374
0
    else  // Silence coverity.
1375
0
        CPLError(CE_Fatal, CPLE_AssertionFailed,
1376
0
                 "Should not happen: Both m_hSHP and m_hDBF are nullptrs");
1377
0
#endif
1378
1379
0
    return eErr;
1380
0
}
1381
1382
/************************************************************************/
1383
/*               GetFeatureCountWithSpatialFilterOnly()                 */
1384
/*                                                                      */
1385
/* Specialized implementation of GetFeatureCount() when there is *only* */
1386
/* a spatial filter and no attribute filter.                            */
1387
/************************************************************************/
1388
1389
int OGRShapeLayer::GetFeatureCountWithSpatialFilterOnly()
1390
1391
0
{
1392
    /* -------------------------------------------------------------------- */
1393
    /*      Collect a matching list if we have attribute or spatial         */
1394
    /*      indices.  Only do this on the first request for a given pass    */
1395
    /*      of course.                                                      */
1396
    /* -------------------------------------------------------------------- */
1397
0
    if (m_panMatchingFIDs == nullptr)
1398
0
    {
1399
0
        ScanIndices();
1400
0
    }
1401
1402
0
    int nFeatureCount = 0;
1403
0
    int iLocalMatchingFID = 0;
1404
0
    int iLocalNextShapeId = 0;
1405
0
    bool bExpectPoints = false;
1406
1407
0
    if (wkbFlatten(m_poFeatureDefn->GetGeomType()) == wkbPoint)
1408
0
        bExpectPoints = true;
1409
1410
    /* -------------------------------------------------------------------- */
1411
    /*      Loop till we find a feature matching our criteria.              */
1412
    /* -------------------------------------------------------------------- */
1413
1414
0
    SHPObject sShape;
1415
0
    memset(&sShape, 0, sizeof(sShape));
1416
1417
0
    while (true)
1418
0
    {
1419
0
        int iShape = -1;
1420
1421
0
        if (m_panMatchingFIDs != nullptr)
1422
0
        {
1423
0
            iShape = static_cast<int>(m_panMatchingFIDs[iLocalMatchingFID]);
1424
0
            if (iShape == OGRNullFID)
1425
0
                break;
1426
0
            iLocalMatchingFID++;
1427
0
        }
1428
0
        else
1429
0
        {
1430
0
            if (iLocalNextShapeId >= m_nTotalShapeCount)
1431
0
                break;
1432
0
            iShape = iLocalNextShapeId++;
1433
1434
0
            if (m_hDBF)
1435
0
            {
1436
0
                if (DBFIsRecordDeleted(m_hDBF, iShape))
1437
0
                    continue;
1438
1439
0
                if (VSIFEofL(VSI_SHP_GetVSIL(m_hDBF->fp)) ||
1440
0
                    VSIFErrorL(VSI_SHP_GetVSIL(m_hDBF->fp)))
1441
0
                    break;
1442
0
            }
1443
0
        }
1444
1445
        // Read full shape for point layers.
1446
0
        SHPObject *psShape = nullptr;
1447
0
        if (bExpectPoints ||
1448
0
            m_hSHP->panRecOffset[iShape] == 0 /* lazy shx loading case */)
1449
0
            psShape = SHPReadObject(m_hSHP, iShape);
1450
1451
        /* --------------------------------------------------------------------
1452
         */
1453
        /*      Only read feature type and bounding box for now. In case of */
1454
        /*      inconclusive tests on bounding box only, we will read the full
1455
         */
1456
        /*      shape later. */
1457
        /* --------------------------------------------------------------------
1458
         */
1459
0
        else if (iShape >= 0 && iShape < m_hSHP->nRecords &&
1460
0
                 m_hSHP->panRecSize[iShape] > 4 + 8 * 4)
1461
0
        {
1462
0
            GByte abyBuf[4 + 8 * 4] = {};
1463
0
            if (m_hSHP->sHooks.FSeek(
1464
0
                    m_hSHP->fpSHP, m_hSHP->panRecOffset[iShape] + 8, 0) == 0 &&
1465
0
                m_hSHP->sHooks.FRead(abyBuf, sizeof(abyBuf), 1,
1466
0
                                     m_hSHP->fpSHP) == 1)
1467
0
            {
1468
0
                memcpy(&(sShape.nSHPType), abyBuf, 4);
1469
0
                CPL_LSBPTR32(&(sShape.nSHPType));
1470
0
                if (sShape.nSHPType != SHPT_NULL &&
1471
0
                    sShape.nSHPType != SHPT_POINT &&
1472
0
                    sShape.nSHPType != SHPT_POINTM &&
1473
0
                    sShape.nSHPType != SHPT_POINTZ)
1474
0
                {
1475
0
                    psShape = &sShape;
1476
0
                    memcpy(&(sShape.dfXMin), abyBuf + 4, 8);
1477
0
                    memcpy(&(sShape.dfYMin), abyBuf + 12, 8);
1478
0
                    memcpy(&(sShape.dfXMax), abyBuf + 20, 8);
1479
0
                    memcpy(&(sShape.dfYMax), abyBuf + 28, 8);
1480
0
                    CPL_LSBPTR64(&(sShape.dfXMin));
1481
0
                    CPL_LSBPTR64(&(sShape.dfYMin));
1482
0
                    CPL_LSBPTR64(&(sShape.dfXMax));
1483
0
                    CPL_LSBPTR64(&(sShape.dfYMax));
1484
0
                }
1485
0
            }
1486
0
            else
1487
0
            {
1488
0
                break;
1489
0
            }
1490
0
        }
1491
1492
0
        if (psShape != nullptr && psShape->nSHPType != SHPT_NULL)
1493
0
        {
1494
0
            std::unique_ptr<OGRGeometry> poGeometry;
1495
0
            OGREnvelope sGeomEnv;
1496
            // Test if we have a degenerated bounding box.
1497
0
            if (psShape->nSHPType != SHPT_POINT &&
1498
0
                psShape->nSHPType != SHPT_POINTZ &&
1499
0
                psShape->nSHPType != SHPT_POINTM &&
1500
0
                (psShape->dfXMin == psShape->dfXMax ||
1501
0
                 psShape->dfYMin == psShape->dfYMax))
1502
0
            {
1503
                // Need to read the full geometry to compute the envelope.
1504
0
                if (psShape == &sShape)
1505
0
                    psShape = SHPReadObject(m_hSHP, iShape);
1506
1507
0
                if (psShape)
1508
0
                {
1509
0
                    poGeometry = SHPReadOGRObject(m_hSHP, iShape, psShape,
1510
0
                                                  m_bHasWarnedWrongWindingOrder,
1511
0
                                                  GetGeomType());
1512
0
                    if (poGeometry)
1513
0
                        poGeometry->getEnvelope(&sGeomEnv);
1514
0
                    psShape = nullptr;
1515
0
                }
1516
0
            }
1517
0
            else
1518
0
            {
1519
                // Trust the shape bounding box as the shape envelope.
1520
0
                sGeomEnv.MinX = psShape->dfXMin;
1521
0
                sGeomEnv.MinY = psShape->dfYMin;
1522
0
                sGeomEnv.MaxX = psShape->dfXMax;
1523
0
                sGeomEnv.MaxY = psShape->dfYMax;
1524
0
            }
1525
1526
            /* --------------------------------------------------------------------
1527
             */
1528
            /*      If there is no */
1529
            /*      intersection between the envelopes we are sure not to have
1530
             */
1531
            /*      any intersection. */
1532
            /* --------------------------------------------------------------------
1533
             */
1534
0
            if (sGeomEnv.MaxX < m_sFilterEnvelope.MinX ||
1535
0
                sGeomEnv.MaxY < m_sFilterEnvelope.MinY ||
1536
0
                m_sFilterEnvelope.MaxX < sGeomEnv.MinX ||
1537
0
                m_sFilterEnvelope.MaxY < sGeomEnv.MinY)
1538
0
            {
1539
0
            }
1540
            /* --------------------------------------------------------------------
1541
             */
1542
            /*      If the filter geometry is its own envelope and if the */
1543
            /*      envelope of the geometry is inside the filter geometry, */
1544
            /*      the geometry itself is inside the filter geometry */
1545
            /* --------------------------------------------------------------------
1546
             */
1547
0
            else if (m_bFilterIsEnvelope &&
1548
0
                     sGeomEnv.MinX >= m_sFilterEnvelope.MinX &&
1549
0
                     sGeomEnv.MinY >= m_sFilterEnvelope.MinY &&
1550
0
                     sGeomEnv.MaxX <= m_sFilterEnvelope.MaxX &&
1551
0
                     sGeomEnv.MaxY <= m_sFilterEnvelope.MaxY)
1552
0
            {
1553
0
                nFeatureCount++;
1554
0
            }
1555
0
            else
1556
0
            {
1557
                /* --------------------------------------------------------------------
1558
                 */
1559
                /*      Fallback to full intersect test (using GEOS) if we still
1560
                 */
1561
                /*      don't know for sure. */
1562
                /* --------------------------------------------------------------------
1563
                 */
1564
0
                if (OGRGeometryFactory::haveGEOS())
1565
0
                {
1566
                    // Read the full geometry.
1567
0
                    if (poGeometry == nullptr)
1568
0
                    {
1569
0
                        if (psShape == &sShape)
1570
0
                            psShape = SHPReadObject(m_hSHP, iShape);
1571
0
                        if (psShape)
1572
0
                        {
1573
0
                            poGeometry = SHPReadOGRObject(
1574
0
                                m_hSHP, iShape, psShape,
1575
0
                                m_bHasWarnedWrongWindingOrder, GetGeomType());
1576
0
                            psShape = nullptr;
1577
0
                        }
1578
0
                    }
1579
0
                    if (poGeometry == nullptr)
1580
0
                    {
1581
0
                        nFeatureCount++;
1582
0
                    }
1583
0
                    else if (m_pPreparedFilterGeom != nullptr)
1584
0
                    {
1585
0
                        if (OGRPreparedGeometryIntersects(
1586
0
                                m_pPreparedFilterGeom,
1587
0
                                OGRGeometry::ToHandle(poGeometry.get())))
1588
0
                        {
1589
0
                            nFeatureCount++;
1590
0
                        }
1591
0
                    }
1592
0
                    else if (m_poFilterGeom->Intersects(poGeometry.get()))
1593
0
                        nFeatureCount++;
1594
0
                }
1595
0
                else
1596
0
                {
1597
0
                    nFeatureCount++;
1598
0
                }
1599
0
            }
1600
0
        }
1601
0
        else
1602
0
        {
1603
0
            nFeatureCount++;
1604
0
        }
1605
1606
0
        if (psShape && psShape != &sShape)
1607
0
            SHPDestroyObject(psShape);
1608
0
    }
1609
1610
0
    return nFeatureCount;
1611
0
}
1612
1613
/************************************************************************/
1614
/*                          GetFeatureCount()                           */
1615
/************************************************************************/
1616
1617
GIntBig OGRShapeLayer::GetFeatureCount(int bForce)
1618
1619
0
{
1620
    // Check if the spatial filter is non-trivial.
1621
0
    bool bHasTrivialSpatialFilter = false;
1622
0
    if (m_poFilterGeom != nullptr)
1623
0
    {
1624
0
        OGREnvelope oSpatialFilterEnvelope;
1625
0
        m_poFilterGeom->getEnvelope(&oSpatialFilterEnvelope);
1626
1627
0
        OGREnvelope oLayerExtent;
1628
0
        if (GetExtent(&oLayerExtent, TRUE) == OGRERR_NONE)
1629
0
        {
1630
0
            if (oSpatialFilterEnvelope.Contains(oLayerExtent))
1631
0
            {
1632
0
                bHasTrivialSpatialFilter = true;
1633
0
            }
1634
0
            else
1635
0
            {
1636
0
                bHasTrivialSpatialFilter = false;
1637
0
            }
1638
0
        }
1639
0
        else
1640
0
        {
1641
0
            bHasTrivialSpatialFilter = false;
1642
0
        }
1643
0
    }
1644
0
    else
1645
0
    {
1646
0
        bHasTrivialSpatialFilter = true;
1647
0
    }
1648
1649
0
    if (bHasTrivialSpatialFilter && m_poAttrQuery == nullptr)
1650
0
        return m_nTotalShapeCount;
1651
1652
0
    if (!TouchLayer())
1653
0
        return 0;
1654
1655
    // Spatial filter only.
1656
0
    if (m_poAttrQuery == nullptr && m_hSHP != nullptr)
1657
0
    {
1658
0
        return GetFeatureCountWithSpatialFilterOnly();
1659
0
    }
1660
1661
    // Attribute filter only.
1662
0
    if (m_poAttrQuery != nullptr && m_poFilterGeom == nullptr)
1663
0
    {
1664
        // See if we can ignore reading geometries.
1665
0
        const bool bSaveGeometryIgnored =
1666
0
            CPL_TO_BOOL(m_poFeatureDefn->IsGeometryIgnored());
1667
0
        if (!AttributeFilterEvaluationNeedsGeometry())
1668
0
            m_poFeatureDefn->SetGeometryIgnored(TRUE);
1669
1670
0
        GIntBig nRet = OGRLayer::GetFeatureCount(bForce);
1671
1672
0
        m_poFeatureDefn->SetGeometryIgnored(bSaveGeometryIgnored);
1673
0
        return nRet;
1674
0
    }
1675
1676
0
    return OGRLayer::GetFeatureCount(bForce);
1677
0
}
1678
1679
/************************************************************************/
1680
/*                            IGetExtent()                              */
1681
/*                                                                      */
1682
/*      Fetch extent of the data currently stored in the dataset.       */
1683
/*      The bForce flag has no effect on SHP files since that value     */
1684
/*      is always in the header.                                        */
1685
/*                                                                      */
1686
/*      Returns OGRERR_NONE/OGRRERR_FAILURE.                            */
1687
/************************************************************************/
1688
1689
OGRErr OGRShapeLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
1690
                                 bool bForce)
1691
1692
0
{
1693
0
    if (!TouchLayer())
1694
0
        return OGRERR_FAILURE;
1695
1696
0
    if (m_hSHP == nullptr)
1697
0
        return OGRERR_FAILURE;
1698
1699
0
    double adMin[4] = {0.0, 0.0, 0.0, 0.0};
1700
0
    double adMax[4] = {0.0, 0.0, 0.0, 0.0};
1701
1702
0
    SHPGetInfo(m_hSHP, nullptr, nullptr, adMin, adMax);
1703
1704
0
    psExtent->MinX = adMin[0];
1705
0
    psExtent->MinY = adMin[1];
1706
0
    psExtent->MaxX = adMax[0];
1707
0
    psExtent->MaxY = adMax[1];
1708
1709
0
    if (std::isnan(adMin[0]) || std::isnan(adMin[1]) || std::isnan(adMax[0]) ||
1710
0
        std::isnan(adMax[1]))
1711
0
    {
1712
0
        CPLDebug("SHAPE", "Invalid extent in shape header");
1713
1714
        // Disable filters to avoid infinite recursion in GetNextFeature()
1715
        // that calls ScanIndices() that call GetExtent.
1716
0
        OGRFeatureQuery *poAttrQuery = m_poAttrQuery;
1717
0
        m_poAttrQuery = nullptr;
1718
0
        OGRGeometry *poFilterGeom = m_poFilterGeom;
1719
0
        m_poFilterGeom = nullptr;
1720
1721
0
        psExtent->MinX = 0;
1722
0
        psExtent->MinY = 0;
1723
0
        psExtent->MaxX = 0;
1724
0
        psExtent->MaxY = 0;
1725
1726
0
        const OGRErr eErr = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
1727
1728
0
        m_poAttrQuery = poAttrQuery;
1729
0
        m_poFilterGeom = poFilterGeom;
1730
0
        return eErr;
1731
0
    }
1732
1733
0
    return OGRERR_NONE;
1734
0
}
1735
1736
OGRErr OGRShapeLayer::IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent3D,
1737
                                   bool bForce)
1738
0
{
1739
0
    if (m_poFilterGeom || m_poAttrQuery)
1740
0
        return OGRLayer::IGetExtent3D(iGeomField, psExtent3D, bForce);
1741
1742
0
    if (!TouchLayer())
1743
0
        return OGRERR_FAILURE;
1744
1745
0
    if (m_hSHP == nullptr)
1746
0
        return OGRERR_FAILURE;
1747
1748
0
    double adMin[4] = {0.0, 0.0, 0.0, 0.0};
1749
0
    double adMax[4] = {0.0, 0.0, 0.0, 0.0};
1750
1751
0
    SHPGetInfo(m_hSHP, nullptr, nullptr, adMin, adMax);
1752
1753
0
    psExtent3D->MinX = adMin[0];
1754
0
    psExtent3D->MinY = adMin[1];
1755
0
    psExtent3D->MaxX = adMax[0];
1756
0
    psExtent3D->MaxY = adMax[1];
1757
1758
0
    if (OGR_GT_HasZ(m_poFeatureDefn->GetGeomType()))
1759
0
    {
1760
0
        psExtent3D->MinZ = adMin[2];
1761
0
        psExtent3D->MaxZ = adMax[2];
1762
0
    }
1763
0
    else
1764
0
    {
1765
0
        psExtent3D->MinZ = std::numeric_limits<double>::infinity();
1766
0
        psExtent3D->MaxZ = -std::numeric_limits<double>::infinity();
1767
0
    }
1768
1769
0
    if (std::isnan(adMin[0]) || std::isnan(adMin[1]) || std::isnan(adMax[0]) ||
1770
0
        std::isnan(adMax[1]))
1771
0
    {
1772
0
        CPLDebug("SHAPE", "Invalid extent in shape header");
1773
1774
        // Disable filters to avoid infinite recursion in GetNextFeature()
1775
        // that calls ScanIndices() that call GetExtent.
1776
0
        OGRFeatureQuery *poAttrQuery = m_poAttrQuery;
1777
0
        m_poAttrQuery = nullptr;
1778
0
        OGRGeometry *poFilterGeom = m_poFilterGeom;
1779
0
        m_poFilterGeom = nullptr;
1780
1781
0
        const OGRErr eErr =
1782
0
            OGRLayer::IGetExtent3D(iGeomField, psExtent3D, bForce);
1783
1784
0
        m_poAttrQuery = poAttrQuery;
1785
0
        m_poFilterGeom = poFilterGeom;
1786
0
        return eErr;
1787
0
    }
1788
1789
0
    return OGRERR_NONE;
1790
0
}
1791
1792
/************************************************************************/
1793
/*                           TestCapability()                           */
1794
/************************************************************************/
1795
1796
int OGRShapeLayer::TestCapability(const char *pszCap) const
1797
1798
0
{
1799
0
    if (!const_cast<OGRShapeLayer *>(this)->TouchLayer())
1800
0
        return FALSE;
1801
1802
0
    if (EQUAL(pszCap, OLCRandomRead))
1803
0
        return TRUE;
1804
1805
0
    if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite))
1806
0
        return m_bUpdateAccess;
1807
1808
0
    if (EQUAL(pszCap, OLCFastFeatureCount))
1809
0
    {
1810
0
        if (!(m_poFilterGeom == nullptr ||
1811
0
              const_cast<OGRShapeLayer *>(this)->CheckForQIX() ||
1812
0
              const_cast<OGRShapeLayer *>(this)->CheckForSBN()))
1813
0
            return FALSE;
1814
1815
0
        if (m_poAttrQuery != nullptr)
1816
0
        {
1817
0
            const_cast<OGRShapeLayer *>(this)->InitializeIndexSupport(
1818
0
                m_osFullName.c_str());
1819
0
            return m_poAttrQuery->CanUseIndex(
1820
0
                const_cast<OGRShapeLayer *>(this));
1821
0
        }
1822
0
        return TRUE;
1823
0
    }
1824
1825
0
    if (EQUAL(pszCap, OLCDeleteFeature))
1826
0
        return m_bUpdateAccess;
1827
1828
0
    if (EQUAL(pszCap, OLCFastSpatialFilter))
1829
0
        return const_cast<OGRShapeLayer *>(this)->CheckForQIX() ||
1830
0
               const_cast<OGRShapeLayer *>(this)->CheckForSBN();
1831
1832
0
    if (EQUAL(pszCap, OLCFastGetExtent))
1833
0
        return TRUE;
1834
1835
0
    if (EQUAL(pszCap, OLCFastGetExtent3D))
1836
0
        return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
1837
1838
0
    if (EQUAL(pszCap, OLCFastSetNextByIndex))
1839
0
        return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
1840
1841
0
    if (EQUAL(pszCap, OLCCreateField))
1842
0
        return m_bUpdateAccess;
1843
1844
0
    if (EQUAL(pszCap, OLCDeleteField))
1845
0
        return m_bUpdateAccess;
1846
1847
0
    if (EQUAL(pszCap, OLCReorderFields))
1848
0
        return m_bUpdateAccess;
1849
1850
0
    if (EQUAL(pszCap, OLCAlterFieldDefn) ||
1851
0
        EQUAL(pszCap, OLCAlterGeomFieldDefn))
1852
0
        return m_bUpdateAccess;
1853
1854
0
    if (EQUAL(pszCap, OLCRename))
1855
0
        return m_bUpdateAccess;
1856
1857
0
    if (EQUAL(pszCap, OLCIgnoreFields))
1858
0
        return TRUE;
1859
1860
0
    if (EQUAL(pszCap, OLCStringsAsUTF8))
1861
0
    {
1862
        // No encoding defined: we don't know.
1863
0
        if (m_osEncoding.empty())
1864
0
            return FALSE;
1865
1866
0
        if (m_hDBF == nullptr || DBFGetFieldCount(m_hDBF) == 0)
1867
0
            return TRUE;
1868
1869
        // Otherwise test that we can re-encode field names to UTF-8.
1870
0
        const int nFieldCount = DBFGetFieldCount(m_hDBF);
1871
0
        for (int i = 0; i < nFieldCount; i++)
1872
0
        {
1873
0
            char szFieldName[XBASE_FLDNAME_LEN_READ + 1] = {};
1874
0
            int nWidth = 0;
1875
0
            int nPrecision = 0;
1876
1877
0
            DBFGetFieldInfo(m_hDBF, i, szFieldName, &nWidth, &nPrecision);
1878
1879
0
            if (!CPLCanRecode(szFieldName, m_osEncoding, CPL_ENC_UTF8))
1880
0
            {
1881
0
                return FALSE;
1882
0
            }
1883
0
        }
1884
1885
0
        return TRUE;
1886
0
    }
1887
1888
0
    if (EQUAL(pszCap, OLCMeasuredGeometries))
1889
0
        return TRUE;
1890
1891
0
    if (EQUAL(pszCap, OLCZGeometries))
1892
0
        return TRUE;
1893
1894
0
    return FALSE;
1895
0
}
1896
1897
/************************************************************************/
1898
/*                            CreateField()                             */
1899
/************************************************************************/
1900
1901
OGRErr OGRShapeLayer::CreateField(const OGRFieldDefn *poFieldDefn,
1902
                                  int bApproxOK)
1903
1904
0
{
1905
0
    if (!StartUpdate("CreateField"))
1906
0
        return OGRERR_FAILURE;
1907
1908
0
    CPLAssert(nullptr != poFieldDefn);
1909
1910
0
    bool bDBFJustCreated = false;
1911
0
    if (m_hDBF == nullptr)
1912
0
    {
1913
0
        const CPLString osFilename =
1914
0
            CPLResetExtensionSafe(m_osFullName.c_str(), "dbf");
1915
0
        m_hDBF = DBFCreate(osFilename);
1916
1917
0
        if (m_hDBF == nullptr)
1918
0
        {
1919
0
            CPLError(CE_Failure, CPLE_OpenFailed,
1920
0
                     "Failed to create DBF file `%s'.", osFilename.c_str());
1921
0
            return OGRERR_FAILURE;
1922
0
        }
1923
1924
0
        bDBFJustCreated = true;
1925
0
    }
1926
1927
0
    if (m_hDBF->nHeaderLength + XBASE_FLDHDR_SZ > 65535)
1928
0
    {
1929
0
        CPLError(CE_Failure, CPLE_NotSupported,
1930
0
                 "Cannot add field %s. Header length limit reached "
1931
0
                 "(max 65535 bytes, 2046 fields).",
1932
0
                 poFieldDefn->GetNameRef());
1933
0
        return OGRERR_FAILURE;
1934
0
    }
1935
1936
0
    CPLErrorReset();
1937
1938
0
    if (m_poFeatureDefn->GetFieldCount() == 255)
1939
0
    {
1940
0
        CPLError(CE_Warning, CPLE_AppDefined,
1941
0
                 "Creating a 256th field, "
1942
0
                 "but some DBF readers might only support 255 fields");
1943
0
    }
1944
1945
    /* -------------------------------------------------------------------- */
1946
    /*      Normalize field name                                            */
1947
    /* -------------------------------------------------------------------- */
1948
0
    CPLString osFieldName;
1949
0
    if (!m_osEncoding.empty())
1950
0
    {
1951
0
        CPLClearRecodeWarningFlags();
1952
0
        CPLPushErrorHandler(CPLQuietErrorHandler);
1953
0
        CPLErr eLastErr = CPLGetLastErrorType();
1954
0
        char *const pszRecoded =
1955
0
            CPLRecode(poFieldDefn->GetNameRef(), CPL_ENC_UTF8, m_osEncoding);
1956
0
        CPLPopErrorHandler();
1957
0
        osFieldName = pszRecoded;
1958
0
        CPLFree(pszRecoded);
1959
0
        if (CPLGetLastErrorType() != eLastErr)
1960
0
        {
1961
0
            CPLError(CE_Failure, CPLE_AppDefined,
1962
0
                     "Failed to create field name '%s': cannot convert to %s",
1963
0
                     poFieldDefn->GetNameRef(), m_osEncoding.c_str());
1964
0
            return OGRERR_FAILURE;
1965
0
        }
1966
0
    }
1967
0
    else
1968
0
    {
1969
0
        osFieldName = poFieldDefn->GetNameRef();
1970
0
    }
1971
1972
0
    const int nNameSize = static_cast<int>(osFieldName.size());
1973
0
    char szNewFieldName[XBASE_FLDNAME_LEN_WRITE + 1];
1974
0
    CPLString osRadixFieldName;
1975
0
    CPLString osRadixFieldNameUC;
1976
0
    {
1977
0
        char *pszTmp = CPLScanString(
1978
0
            osFieldName, std::min(nNameSize, XBASE_FLDNAME_LEN_WRITE), TRUE,
1979
0
            TRUE);
1980
0
        strncpy(szNewFieldName, pszTmp, sizeof(szNewFieldName) - 1);
1981
0
        szNewFieldName[sizeof(szNewFieldName) - 1] = '\0';
1982
0
        osRadixFieldName = pszTmp;
1983
0
        osRadixFieldNameUC = CPLString(osRadixFieldName).toupper();
1984
0
        CPLFree(pszTmp);
1985
0
    }
1986
1987
0
    CPLString osNewFieldNameUC(szNewFieldName);
1988
0
    osNewFieldNameUC.toupper();
1989
1990
0
    if (m_oSetUCFieldName.empty())
1991
0
    {
1992
0
        for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
1993
0
        {
1994
0
            m_oSetUCFieldName.insert(
1995
0
                CPLString(m_poFeatureDefn->GetFieldDefn(i)->GetNameRef())
1996
0
                    .toupper());
1997
0
        }
1998
0
    }
1999
2000
0
    bool bFoundFieldName =
2001
0
        m_oSetUCFieldName.find(osNewFieldNameUC) != m_oSetUCFieldName.end();
2002
2003
0
    if (!bApproxOK && (bFoundFieldName || !EQUAL(osFieldName, szNewFieldName)))
2004
0
    {
2005
0
        CPLError(CE_Failure, CPLE_NotSupported,
2006
0
                 "Failed to add field named '%s'", poFieldDefn->GetNameRef());
2007
2008
0
        return OGRERR_FAILURE;
2009
0
    }
2010
2011
0
    if (bFoundFieldName)
2012
0
    {
2013
0
        int nRenameNum = 1;
2014
0
        while (bFoundFieldName && nRenameNum < 10)
2015
0
        {
2016
0
            CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.8s_%.1d",
2017
0
                        osRadixFieldName.c_str(), nRenameNum);
2018
0
            osNewFieldNameUC.Printf("%.8s_%.1d", osRadixFieldNameUC.c_str(),
2019
0
                                    nRenameNum);
2020
0
            bFoundFieldName = m_oSetUCFieldName.find(osNewFieldNameUC) !=
2021
0
                              m_oSetUCFieldName.end();
2022
0
            nRenameNum++;
2023
0
        }
2024
2025
0
        while (bFoundFieldName && nRenameNum < 100)
2026
0
        {
2027
0
            CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.8s%.2d",
2028
0
                        osRadixFieldName.c_str(), nRenameNum);
2029
0
            osNewFieldNameUC.Printf("%.8s%.2d", osRadixFieldNameUC.c_str(),
2030
0
                                    nRenameNum);
2031
0
            bFoundFieldName = m_oSetUCFieldName.find(osNewFieldNameUC) !=
2032
0
                              m_oSetUCFieldName.end();
2033
0
            nRenameNum++;
2034
0
        }
2035
2036
0
        if (bFoundFieldName)
2037
0
        {
2038
            // One hundred similar field names!!?
2039
0
            CPLError(
2040
0
                CE_Failure, CPLE_NotSupported,
2041
0
                "Too many field names like '%s' when truncated to %d letters "
2042
0
                "for Shapefile format.",
2043
0
                poFieldDefn->GetNameRef(), XBASE_FLDNAME_LEN_WRITE);
2044
0
            return OGRERR_FAILURE;
2045
0
        }
2046
0
    }
2047
2048
0
    OGRFieldDefn oModFieldDefn(poFieldDefn);
2049
2050
0
    if (!EQUAL(osFieldName, szNewFieldName))
2051
0
    {
2052
0
        CPLError(CE_Warning, CPLE_NotSupported,
2053
0
                 "Normalized/laundered field name: '%s' to '%s'",
2054
0
                 poFieldDefn->GetNameRef(), szNewFieldName);
2055
2056
        // Set field name with normalized value.
2057
0
        oModFieldDefn.SetName(szNewFieldName);
2058
0
    }
2059
2060
    /* -------------------------------------------------------------------- */
2061
    /*      Add field to layer                                              */
2062
    /* -------------------------------------------------------------------- */
2063
0
    char chType = 'C';
2064
0
    int nWidth = 0;
2065
0
    int nDecimals = 0;
2066
2067
0
    switch (oModFieldDefn.GetType())
2068
0
    {
2069
0
        case OFTInteger:
2070
0
            if (oModFieldDefn.GetSubType() == OFSTBoolean)
2071
0
            {
2072
0
                chType = 'L';
2073
0
                nWidth = 1;
2074
0
            }
2075
0
            else
2076
0
            {
2077
0
                chType = 'N';
2078
0
                nWidth = oModFieldDefn.GetWidth();
2079
0
                if (nWidth == 0)
2080
0
                    nWidth = 9;
2081
0
            }
2082
0
            break;
2083
2084
0
        case OFTInteger64:
2085
0
            chType = 'N';
2086
0
            nWidth = oModFieldDefn.GetWidth();
2087
0
            if (nWidth == 0)
2088
0
                nWidth = 18;
2089
0
            break;
2090
2091
0
        case OFTReal:
2092
0
            chType = 'N';
2093
0
            nWidth = oModFieldDefn.GetWidth();
2094
0
            nDecimals = oModFieldDefn.GetPrecision();
2095
0
            if (nWidth == 0)
2096
0
            {
2097
0
                nWidth = 24;
2098
0
                nDecimals = 15;
2099
0
            }
2100
0
            break;
2101
2102
0
        case OFTString:
2103
0
            chType = 'C';
2104
0
            nWidth = oModFieldDefn.GetWidth();
2105
0
            if (nWidth == 0)
2106
0
                nWidth = 80;
2107
0
            else if (nWidth > OGR_DBF_MAX_FIELD_WIDTH)
2108
0
            {
2109
0
                CPLError(CE_Warning, CPLE_AppDefined,
2110
0
                         "Field %s of width %d truncated to %d.",
2111
0
                         szNewFieldName, nWidth, OGR_DBF_MAX_FIELD_WIDTH);
2112
0
                nWidth = OGR_DBF_MAX_FIELD_WIDTH;
2113
0
            }
2114
0
            break;
2115
2116
0
        case OFTDate:
2117
0
            chType = 'D';
2118
0
            nWidth = 8;
2119
0
            break;
2120
2121
0
        case OFTDateTime:
2122
0
            CPLError(
2123
0
                CE_Warning, CPLE_NotSupported,
2124
0
                "Field %s created as String field, though DateTime requested.",
2125
0
                szNewFieldName);
2126
0
            chType = 'C';
2127
0
            nWidth = static_cast<int>(strlen("YYYY-MM-DDTHH:MM:SS.sss+HH:MM"));
2128
0
            oModFieldDefn.SetType(OFTString);
2129
0
            break;
2130
2131
0
        default:
2132
0
            CPLError(CE_Failure, CPLE_NotSupported,
2133
0
                     "Can't create fields of type %s on shapefile layers.",
2134
0
                     OGRFieldDefn::GetFieldTypeName(oModFieldDefn.GetType()));
2135
2136
0
            return OGRERR_FAILURE;
2137
0
    }
2138
2139
0
    oModFieldDefn.SetWidth(nWidth);
2140
0
    oModFieldDefn.SetPrecision(nDecimals);
2141
2142
    // Suppress the dummy FID field if we have created it just before.
2143
0
    if (DBFGetFieldCount(m_hDBF) == 1 && m_poFeatureDefn->GetFieldCount() == 0)
2144
0
    {
2145
0
        DBFDeleteField(m_hDBF, 0);
2146
0
    }
2147
2148
0
    const int iNewField = DBFAddNativeFieldType(m_hDBF, szNewFieldName, chType,
2149
0
                                                nWidth, nDecimals);
2150
2151
0
    if (iNewField != -1)
2152
0
    {
2153
0
        m_oSetUCFieldName.insert(std::move(osNewFieldNameUC));
2154
2155
0
        whileUnsealing(m_poFeatureDefn)->AddFieldDefn(&oModFieldDefn);
2156
2157
0
        if (bDBFJustCreated)
2158
0
        {
2159
0
            for (int i = 0; i < m_nTotalShapeCount; i++)
2160
0
            {
2161
0
                DBFWriteNULLAttribute(m_hDBF, i, 0);
2162
0
            }
2163
0
        }
2164
2165
0
        return OGRERR_NONE;
2166
0
    }
2167
2168
0
    CPLError(CE_Failure, CPLE_AppDefined,
2169
0
             "Can't create field %s in Shape DBF file, reason unknown.",
2170
0
             szNewFieldName);
2171
2172
0
    return OGRERR_FAILURE;
2173
0
}
2174
2175
/************************************************************************/
2176
/*                            DeleteField()                             */
2177
/************************************************************************/
2178
2179
OGRErr OGRShapeLayer::DeleteField(int iField)
2180
0
{
2181
0
    if (!StartUpdate("DeleteField"))
2182
0
        return OGRERR_FAILURE;
2183
2184
0
    if (iField < 0 || iField >= m_poFeatureDefn->GetFieldCount())
2185
0
    {
2186
0
        CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2187
0
        return OGRERR_FAILURE;
2188
0
    }
2189
2190
0
    m_oSetUCFieldName.clear();
2191
2192
0
    if (DBFDeleteField(m_hDBF, iField))
2193
0
    {
2194
0
        TruncateDBF();
2195
2196
0
        return whileUnsealing(m_poFeatureDefn)->DeleteFieldDefn(iField);
2197
0
    }
2198
2199
0
    return OGRERR_FAILURE;
2200
0
}
2201
2202
/************************************************************************/
2203
/*                           ReorderFields()                            */
2204
/************************************************************************/
2205
2206
OGRErr OGRShapeLayer::ReorderFields(int *panMap)
2207
0
{
2208
0
    if (!StartUpdate("ReorderFields"))
2209
0
        return OGRERR_FAILURE;
2210
2211
0
    if (m_poFeatureDefn->GetFieldCount() == 0)
2212
0
        return OGRERR_NONE;
2213
2214
0
    OGRErr eErr = OGRCheckPermutation(panMap, m_poFeatureDefn->GetFieldCount());
2215
0
    if (eErr != OGRERR_NONE)
2216
0
        return eErr;
2217
2218
0
    if (DBFReorderFields(m_hDBF, panMap))
2219
0
    {
2220
0
        return whileUnsealing(m_poFeatureDefn)->ReorderFieldDefns(panMap);
2221
0
    }
2222
2223
0
    return OGRERR_FAILURE;
2224
0
}
2225
2226
/************************************************************************/
2227
/*                           AlterFieldDefn()                           */
2228
/************************************************************************/
2229
2230
OGRErr OGRShapeLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
2231
                                     int nFlagsIn)
2232
0
{
2233
0
    if (!StartUpdate("AlterFieldDefn"))
2234
0
        return OGRERR_FAILURE;
2235
2236
0
    if (iField < 0 || iField >= m_poFeatureDefn->GetFieldCount())
2237
0
    {
2238
0
        CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2239
0
        return OGRERR_FAILURE;
2240
0
    }
2241
2242
0
    m_oSetUCFieldName.clear();
2243
2244
0
    OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
2245
0
    OGRFieldType eType = poFieldDefn->GetType();
2246
2247
0
    auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
2248
2249
    // On reading we support up to 11 characters
2250
0
    char szFieldName[XBASE_FLDNAME_LEN_READ + 1] = {};
2251
0
    int nWidth = 0;
2252
0
    int nPrecision = 0;
2253
0
    DBFGetFieldInfo(m_hDBF, iField, szFieldName, &nWidth, &nPrecision);
2254
0
    char chNativeType = DBFGetNativeFieldType(m_hDBF, iField);
2255
2256
0
    if ((nFlagsIn & ALTER_TYPE_FLAG) &&
2257
0
        poNewFieldDefn->GetType() != poFieldDefn->GetType())
2258
0
    {
2259
0
        if (poNewFieldDefn->GetType() == OFTInteger64 &&
2260
0
            poFieldDefn->GetType() == OFTInteger)
2261
0
        {
2262
0
            eType = poNewFieldDefn->GetType();
2263
0
        }
2264
0
        else if (poNewFieldDefn->GetType() != OFTString)
2265
0
        {
2266
0
            CPLError(CE_Failure, CPLE_NotSupported,
2267
0
                     "Can only convert to OFTString");
2268
0
            return OGRERR_FAILURE;
2269
0
        }
2270
0
        else
2271
0
        {
2272
0
            chNativeType = 'C';
2273
0
            eType = poNewFieldDefn->GetType();
2274
0
        }
2275
0
    }
2276
2277
0
    if (nFlagsIn & ALTER_NAME_FLAG)
2278
0
    {
2279
0
        CPLString osFieldName;
2280
0
        if (!m_osEncoding.empty())
2281
0
        {
2282
0
            CPLClearRecodeWarningFlags();
2283
0
            CPLErrorReset();
2284
0
            CPLPushErrorHandler(CPLQuietErrorHandler);
2285
0
            char *pszRecoded = CPLRecode(poNewFieldDefn->GetNameRef(),
2286
0
                                         CPL_ENC_UTF8, m_osEncoding);
2287
0
            CPLPopErrorHandler();
2288
0
            osFieldName = pszRecoded;
2289
0
            CPLFree(pszRecoded);
2290
0
            if (CPLGetLastErrorType() != 0)
2291
0
            {
2292
0
                CPLError(CE_Failure, CPLE_AppDefined,
2293
0
                         "Failed to rename field name to '%s': "
2294
0
                         "cannot convert to %s",
2295
0
                         poNewFieldDefn->GetNameRef(), m_osEncoding.c_str());
2296
0
                return OGRERR_FAILURE;
2297
0
            }
2298
0
        }
2299
0
        else
2300
0
        {
2301
0
            osFieldName = poNewFieldDefn->GetNameRef();
2302
0
        }
2303
2304
0
        strncpy(szFieldName, osFieldName, sizeof(szFieldName) - 1);
2305
0
        szFieldName[sizeof(szFieldName) - 1] = '\0';
2306
0
    }
2307
0
    if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
2308
0
    {
2309
0
        nWidth = poNewFieldDefn->GetWidth();
2310
0
        nPrecision = poNewFieldDefn->GetPrecision();
2311
0
    }
2312
2313
0
    if (DBFAlterFieldDefn(m_hDBF, iField, szFieldName, chNativeType, nWidth,
2314
0
                          nPrecision))
2315
0
    {
2316
0
        if (nFlagsIn & ALTER_TYPE_FLAG)
2317
0
            poFieldDefn->SetType(eType);
2318
0
        if (nFlagsIn & ALTER_NAME_FLAG)
2319
0
            poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
2320
0
        if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
2321
0
        {
2322
0
            poFieldDefn->SetWidth(nWidth);
2323
0
            poFieldDefn->SetPrecision(nPrecision);
2324
2325
0
            TruncateDBF();
2326
0
        }
2327
0
        return OGRERR_NONE;
2328
0
    }
2329
2330
0
    return OGRERR_FAILURE;
2331
0
}
2332
2333
/************************************************************************/
2334
/*                         AlterGeomFieldDefn()                         */
2335
/************************************************************************/
2336
2337
OGRErr OGRShapeLayer::AlterGeomFieldDefn(
2338
    int iGeomField, const OGRGeomFieldDefn *poNewGeomFieldDefn, int nFlagsIn)
2339
0
{
2340
0
    if (!StartUpdate("AlterGeomFieldDefn"))
2341
0
        return OGRERR_FAILURE;
2342
2343
0
    if (iGeomField < 0 || iGeomField >= m_poFeatureDefn->GetGeomFieldCount())
2344
0
    {
2345
0
        CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
2346
0
        return OGRERR_FAILURE;
2347
0
    }
2348
2349
0
    auto poFieldDefn = cpl::down_cast<OGRShapeGeomFieldDefn *>(
2350
0
        m_poFeatureDefn->GetGeomFieldDefn(iGeomField));
2351
0
    auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
2352
2353
0
    if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG)
2354
0
    {
2355
0
        if (strcmp(poNewGeomFieldDefn->GetNameRef(),
2356
0
                   poFieldDefn->GetNameRef()) != 0)
2357
0
        {
2358
0
            CPLError(CE_Failure, CPLE_NotSupported,
2359
0
                     "Altering the geometry field name is not supported for "
2360
0
                     "shapefiles");
2361
0
            return OGRERR_FAILURE;
2362
0
        }
2363
0
    }
2364
2365
0
    if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
2366
0
    {
2367
0
        if (poFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
2368
0
        {
2369
0
            CPLError(CE_Failure, CPLE_NotSupported,
2370
0
                     "Altering the geometry field type is not supported for "
2371
0
                     "shapefiles");
2372
0
            return OGRERR_FAILURE;
2373
0
        }
2374
0
    }
2375
2376
0
    if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG)
2377
0
    {
2378
0
        const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
2379
0
        if (poNewSRSRef && poNewSRSRef->GetCoordinateEpoch() > 0)
2380
0
        {
2381
0
            CPLError(CE_Failure, CPLE_NotSupported,
2382
0
                     "Setting a coordinate epoch is not supported for "
2383
0
                     "shapefiles");
2384
0
            return OGRERR_FAILURE;
2385
0
        }
2386
0
    }
2387
2388
0
    if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG)
2389
0
    {
2390
0
        if (poFieldDefn->GetPrjFilename().empty())
2391
0
        {
2392
0
            poFieldDefn->SetPrjFilename(
2393
0
                CPLResetExtensionSafe(m_osFullName.c_str(), "prj").c_str());
2394
0
        }
2395
2396
0
        const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
2397
0
        if (poNewSRSRef)
2398
0
        {
2399
0
            char *pszWKT = nullptr;
2400
0
            VSILFILE *fp = nullptr;
2401
0
            const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
2402
0
            if (poNewSRSRef->exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE &&
2403
0
                (fp = VSIFOpenL(poFieldDefn->GetPrjFilename().c_str(), "wt")) !=
2404
0
                    nullptr)
2405
0
            {
2406
0
                VSIFWriteL(pszWKT, strlen(pszWKT), 1, fp);
2407
0
                VSIFCloseL(fp);
2408
0
            }
2409
0
            else
2410
0
            {
2411
0
                CPLError(CE_Failure, CPLE_FileIO, "Cannot write %s",
2412
0
                         poFieldDefn->GetPrjFilename().c_str());
2413
0
                CPLFree(pszWKT);
2414
0
                return OGRERR_FAILURE;
2415
0
            }
2416
2417
0
            CPLFree(pszWKT);
2418
2419
0
            poFieldDefn->SetSpatialRef(
2420
0
                OGRSpatialReferenceRefCountedPtr::makeClone(poNewSRSRef));
2421
0
        }
2422
0
        else
2423
0
        {
2424
0
            poFieldDefn->SetSpatialRef(nullptr);
2425
0
            VSIStatBufL sStat;
2426
0
            if (VSIStatL(poFieldDefn->GetPrjFilename().c_str(), &sStat) == 0 &&
2427
0
                VSIUnlink(poFieldDefn->GetPrjFilename().c_str()) != 0)
2428
0
            {
2429
0
                CPLError(CE_Failure, CPLE_FileIO, "Cannot delete %s",
2430
0
                         poFieldDefn->GetPrjFilename().c_str());
2431
0
                return OGRERR_FAILURE;
2432
0
            }
2433
0
        }
2434
0
        poFieldDefn->SetSRSSet();
2435
0
    }
2436
2437
0
    if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG)
2438
0
        poFieldDefn->SetName(poNewGeomFieldDefn->GetNameRef());
2439
0
    if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
2440
0
        poFieldDefn->SetNullable(poNewGeomFieldDefn->IsNullable());
2441
2442
0
    return OGRERR_NONE;
2443
0
}
2444
2445
/************************************************************************/
2446
/*                           GetSpatialRef()                            */
2447
/************************************************************************/
2448
2449
const OGRSpatialReference *OGRShapeGeomFieldDefn::GetSpatialRef() const
2450
2451
0
{
2452
0
    if (m_bSRSSet)
2453
0
        return poSRS.get();
2454
2455
0
    m_bSRSSet = true;
2456
2457
    /* -------------------------------------------------------------------- */
2458
    /*      Is there an associated .prj file we can read?                   */
2459
    /* -------------------------------------------------------------------- */
2460
0
    std::string l_osPrjFile =
2461
0
        CPLResetExtensionSafe(m_osFullName.c_str(), "prj");
2462
2463
0
    char *apszOptions[] = {
2464
0
        const_cast<char *>("EMIT_ERROR_IF_CANNOT_OPEN_FILE=FALSE"), nullptr};
2465
0
    char **papszLines = CSLLoad2(l_osPrjFile.c_str(), -1, -1, apszOptions);
2466
0
    if (papszLines == nullptr)
2467
0
    {
2468
0
        l_osPrjFile = CPLResetExtensionSafe(m_osFullName.c_str(), "PRJ");
2469
0
        papszLines = CSLLoad2(l_osPrjFile.c_str(), -1, -1, apszOptions);
2470
0
    }
2471
2472
0
    if (papszLines != nullptr)
2473
0
    {
2474
0
        m_osPrjFile = std::move(l_osPrjFile);
2475
2476
0
        auto poTmpSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
2477
0
        poTmpSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2478
        // Remove UTF-8 BOM if found
2479
        // http://lists.osgeo.org/pipermail/gdal-dev/2014-July/039527.html
2480
0
        if (static_cast<unsigned char>(papszLines[0][0]) == 0xEF &&
2481
0
            static_cast<unsigned char>(papszLines[0][1]) == 0xBB &&
2482
0
            static_cast<unsigned char>(papszLines[0][2]) == 0xBF)
2483
0
        {
2484
0
            memmove(papszLines[0], papszLines[0] + 3,
2485
0
                    strlen(papszLines[0] + 3) + 1);
2486
0
        }
2487
0
        if (STARTS_WITH_CI(papszLines[0], "GEOGCS["))
2488
0
        {
2489
            // Strip AXIS[] in GEOGCS to address use case of
2490
            // https://github.com/OSGeo/gdal/issues/8452
2491
0
            std::string osVal;
2492
0
            for (CSLConstList papszIter = papszLines; *papszIter; ++papszIter)
2493
0
                osVal += *papszIter;
2494
0
            OGR_SRSNode oSRSNode;
2495
0
            const char *pszVal = osVal.c_str();
2496
0
            if (oSRSNode.importFromWkt(&pszVal) == OGRERR_NONE)
2497
0
            {
2498
0
                oSRSNode.StripNodes("AXIS");
2499
0
                char *pszWKT = nullptr;
2500
0
                oSRSNode.exportToWkt(&pszWKT);
2501
0
                if (pszWKT)
2502
0
                {
2503
0
                    CSLDestroy(papszLines);
2504
0
                    papszLines =
2505
0
                        static_cast<char **>(CPLCalloc(2, sizeof(char *)));
2506
0
                    papszLines[0] = pszWKT;
2507
0
                }
2508
0
            }
2509
0
        }
2510
0
        if (poTmpSRS->importFromESRI(papszLines) != OGRERR_NONE)
2511
0
        {
2512
0
            poTmpSRS.reset();
2513
0
        }
2514
0
        CSLDestroy(papszLines);
2515
2516
0
        if (poTmpSRS)
2517
0
        {
2518
0
            double adfTOWGS84[7];
2519
0
            const char *pszSRSName = poTmpSRS->GetName();
2520
0
            if (CPLTestBool(
2521
0
                    CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")) &&
2522
                // Below works around bug fixed in PROJ per
2523
                // https://github.com/OSGeo/PROJ/pull/4599
2524
0
                !(pszSRSName && strstr(pszSRSName, "NTF (Paris)") != nullptr &&
2525
0
                  poTmpSRS->GetTOWGS84(adfTOWGS84) == OGRERR_NONE))
2526
0
            {
2527
0
                OGRSpatialReferenceRefCountedPtr poSRSMatch(
2528
0
                    poTmpSRS->FindBestMatch(), /* add_ref = */ false);
2529
0
                if (poSRSMatch)
2530
0
                {
2531
0
                    poTmpSRS = std::move(poSRSMatch);
2532
0
                    poTmpSRS->SetAxisMappingStrategy(
2533
0
                        OAMS_TRADITIONAL_GIS_ORDER);
2534
0
                }
2535
0
            }
2536
0
            else
2537
0
            {
2538
0
                poTmpSRS->AutoIdentifyEPSG();
2539
0
            }
2540
0
            poSRS = std::move(poTmpSRS);
2541
0
        }
2542
0
    }
2543
2544
0
    return poSRS.get();
2545
0
}
2546
2547
/************************************************************************/
2548
/*                           ResetGeomType()                            */
2549
/*                                                                      */
2550
/*      Modify the geometry type for this file.  Used to convert to     */
2551
/*      a different geometry type when a layer was created with a       */
2552
/*      type of unknown, and we get to the first feature to             */
2553
/*      establish the type.                                             */
2554
/************************************************************************/
2555
2556
int OGRShapeLayer::ResetGeomType(int nNewGeomType)
2557
2558
0
{
2559
0
    if (m_nTotalShapeCount > 0)
2560
0
        return FALSE;
2561
2562
0
    if (m_hSHP->fpSHX == nullptr)
2563
0
    {
2564
0
        CPLError(CE_Failure, CPLE_NotSupported,
2565
0
                 "OGRShapeLayer::ResetGeomType failed: SHX file is closed");
2566
0
        return FALSE;
2567
0
    }
2568
2569
    /* -------------------------------------------------------------------- */
2570
    /*      Update .shp header.                                             */
2571
    /* -------------------------------------------------------------------- */
2572
0
    int nStartPos = static_cast<int>(m_hSHP->sHooks.FTell(m_hSHP->fpSHP));
2573
2574
0
    char abyHeader[100] = {};
2575
0
    if (m_hSHP->sHooks.FSeek(m_hSHP->fpSHP, 0, SEEK_SET) != 0 ||
2576
0
        m_hSHP->sHooks.FRead(abyHeader, 100, 1, m_hSHP->fpSHP) != 1)
2577
0
        return FALSE;
2578
2579
0
    *(reinterpret_cast<GInt32 *>(abyHeader + 32)) = CPL_LSBWORD32(nNewGeomType);
2580
2581
0
    if (m_hSHP->sHooks.FSeek(m_hSHP->fpSHP, 0, SEEK_SET) != 0 ||
2582
0
        m_hSHP->sHooks.FWrite(abyHeader, 100, 1, m_hSHP->fpSHP) != 1)
2583
0
        return FALSE;
2584
2585
0
    if (m_hSHP->sHooks.FSeek(m_hSHP->fpSHP, nStartPos, SEEK_SET) != 0)
2586
0
        return FALSE;
2587
2588
    /* -------------------------------------------------------------------- */
2589
    /*      Update .shx header.                                             */
2590
    /* -------------------------------------------------------------------- */
2591
0
    nStartPos = static_cast<int>(m_hSHP->sHooks.FTell(m_hSHP->fpSHX));
2592
2593
0
    if (m_hSHP->sHooks.FSeek(m_hSHP->fpSHX, 0, SEEK_SET) != 0 ||
2594
0
        m_hSHP->sHooks.FRead(abyHeader, 100, 1, m_hSHP->fpSHX) != 1)
2595
0
        return FALSE;
2596
2597
0
    *(reinterpret_cast<GInt32 *>(abyHeader + 32)) = CPL_LSBWORD32(nNewGeomType);
2598
2599
0
    if (m_hSHP->sHooks.FSeek(m_hSHP->fpSHX, 0, SEEK_SET) != 0 ||
2600
0
        m_hSHP->sHooks.FWrite(abyHeader, 100, 1, m_hSHP->fpSHX) != 1)
2601
0
        return FALSE;
2602
2603
0
    if (m_hSHP->sHooks.FSeek(m_hSHP->fpSHX, nStartPos, SEEK_SET) != 0)
2604
0
        return FALSE;
2605
2606
    /* -------------------------------------------------------------------- */
2607
    /*      Update other information.                                       */
2608
    /* -------------------------------------------------------------------- */
2609
0
    m_hSHP->nShapeType = nNewGeomType;
2610
2611
0
    return TRUE;
2612
0
}
2613
2614
/************************************************************************/
2615
/*                             SyncToDisk()                             */
2616
/************************************************************************/
2617
2618
OGRErr OGRShapeLayer::SyncToDisk()
2619
2620
0
{
2621
0
    if (!TouchLayer())
2622
0
        return OGRERR_FAILURE;
2623
2624
0
    if (m_bHeaderDirty)
2625
0
    {
2626
0
        if (m_hSHP != nullptr)
2627
0
            SHPWriteHeader(m_hSHP);
2628
2629
0
        if (m_hDBF != nullptr)
2630
0
            DBFUpdateHeader(m_hDBF);
2631
2632
0
        m_bHeaderDirty = false;
2633
0
    }
2634
2635
0
    if (m_hSHP != nullptr)
2636
0
    {
2637
0
        m_hSHP->sHooks.FFlush(m_hSHP->fpSHP);
2638
0
        if (m_hSHP->fpSHX != nullptr)
2639
0
            m_hSHP->sHooks.FFlush(m_hSHP->fpSHX);
2640
0
    }
2641
2642
0
    if (m_hDBF != nullptr)
2643
0
    {
2644
0
        m_hDBF->sHooks.FFlush(m_hDBF->fp);
2645
0
    }
2646
2647
0
    if (m_eNeedRepack == YES && m_bAutoRepack)
2648
0
        Repack();
2649
2650
0
    return OGRERR_NONE;
2651
0
}
2652
2653
/************************************************************************/
2654
/*                          DropSpatialIndex()                          */
2655
/************************************************************************/
2656
2657
OGRErr OGRShapeLayer::DropSpatialIndex()
2658
2659
0
{
2660
0
    if (!StartUpdate("DropSpatialIndex"))
2661
0
        return OGRERR_FAILURE;
2662
2663
0
    if (!CheckForQIX() && !CheckForSBN())
2664
0
    {
2665
0
        CPLError(CE_Warning, CPLE_AppDefined,
2666
0
                 "Layer %s has no spatial index, DROP SPATIAL INDEX failed.",
2667
0
                 m_poFeatureDefn->GetName());
2668
0
        return OGRERR_FAILURE;
2669
0
    }
2670
2671
0
    const bool bHadQIX = m_hQIX != nullptr;
2672
2673
0
    SHPCloseDiskTree(m_hQIX);
2674
0
    m_hQIX = nullptr;
2675
0
    m_bCheckedForQIX = false;
2676
2677
0
    SBNCloseDiskTree(m_hSBN);
2678
0
    m_hSBN = nullptr;
2679
0
    m_bCheckedForSBN = false;
2680
2681
0
    if (bHadQIX)
2682
0
    {
2683
0
        const std::string osQIXFilename =
2684
0
            CPLResetExtensionSafe(m_osFullName.c_str(), "qix");
2685
0
        CPLDebug("SHAPE", "Unlinking index file %s", osQIXFilename.c_str());
2686
2687
0
        if (VSIUnlink(osQIXFilename.c_str()) != 0)
2688
0
        {
2689
0
            CPLError(CE_Failure, CPLE_AppDefined,
2690
0
                     "Failed to delete file %s.\n%s", osQIXFilename.c_str(),
2691
0
                     VSIStrerror(errno));
2692
0
            return OGRERR_FAILURE;
2693
0
        }
2694
0
    }
2695
2696
0
    if (!m_bSbnSbxDeleted)
2697
0
    {
2698
0
        const char papszExt[2][4] = {"sbn", "sbx"};
2699
0
        for (int i = 0; i < 2; i++)
2700
0
        {
2701
0
            const std::string osIndexFilename =
2702
0
                CPLResetExtensionSafe(m_osFullName.c_str(), papszExt[i]);
2703
0
            CPLDebug("SHAPE", "Trying to unlink index file %s",
2704
0
                     osIndexFilename.c_str());
2705
2706
0
            if (VSIUnlink(osIndexFilename.c_str()) != 0)
2707
0
            {
2708
0
                CPLDebug("SHAPE", "Failed to delete file %s.\n%s",
2709
0
                         osIndexFilename.c_str(), VSIStrerror(errno));
2710
0
            }
2711
0
        }
2712
0
    }
2713
0
    m_bSbnSbxDeleted = true;
2714
2715
0
    ClearSpatialFIDs();
2716
2717
0
    return OGRERR_NONE;
2718
0
}
2719
2720
/************************************************************************/
2721
/*                         CreateSpatialIndex()                         */
2722
/************************************************************************/
2723
2724
OGRErr OGRShapeLayer::CreateSpatialIndex(int nMaxDepth)
2725
2726
0
{
2727
0
    if (!StartUpdate("CreateSpatialIndex"))
2728
0
        return OGRERR_FAILURE;
2729
2730
    /* -------------------------------------------------------------------- */
2731
    /*      If we have an existing spatial index, blow it away first.       */
2732
    /* -------------------------------------------------------------------- */
2733
0
    if (CheckForQIX())
2734
0
        DropSpatialIndex();
2735
2736
0
    m_bCheckedForQIX = false;
2737
2738
    /* -------------------------------------------------------------------- */
2739
    /*      Build a quadtree structure for this file.                       */
2740
    /* -------------------------------------------------------------------- */
2741
0
    OGRShapeLayer::SyncToDisk();
2742
0
    SHPTree *psTree = SHPCreateTree(m_hSHP, 2, nMaxDepth, nullptr, nullptr);
2743
2744
0
    if (nullptr == psTree)
2745
0
    {
2746
        // TODO(mloskot): Is it better to return OGRERR_NOT_ENOUGH_MEMORY?
2747
0
        CPLDebug("SHAPE",
2748
0
                 "Index creation failure. Likely, memory allocation error.");
2749
2750
0
        return OGRERR_FAILURE;
2751
0
    }
2752
2753
    /* -------------------------------------------------------------------- */
2754
    /*      Trim unused nodes from the tree.                                */
2755
    /* -------------------------------------------------------------------- */
2756
0
    SHPTreeTrimExtraNodes(psTree);
2757
2758
    /* -------------------------------------------------------------------- */
2759
    /*      Dump tree to .qix file.                                         */
2760
    /* -------------------------------------------------------------------- */
2761
0
    char *pszQIXFilename =
2762
0
        CPLStrdup(CPLResetExtensionSafe(m_osFullName.c_str(), "qix").c_str());
2763
2764
0
    CPLDebug("SHAPE", "Creating index file %s", pszQIXFilename);
2765
2766
0
    SHPWriteTree(psTree, pszQIXFilename);
2767
0
    CPLFree(pszQIXFilename);
2768
2769
    /* -------------------------------------------------------------------- */
2770
    /*      cleanup                                                         */
2771
    /* -------------------------------------------------------------------- */
2772
0
    SHPDestroyTree(psTree);
2773
2774
0
    CPL_IGNORE_RET_VAL(CheckForQIX());
2775
2776
0
    return OGRERR_NONE;
2777
0
}
2778
2779
/************************************************************************/
2780
/*                         CheckFileDeletion()                          */
2781
/************************************************************************/
2782
2783
static void CheckFileDeletion(const CPLString &osFilename)
2784
0
{
2785
    // On Windows, sometimes the file is still triansiently reported
2786
    // as existing although being deleted, which makes QGIS things that
2787
    // an issue arose. The following helps to reduce that risk.
2788
0
    VSIStatBufL sStat;
2789
0
    if (VSIStatL(osFilename, &sStat) == 0 && VSIStatL(osFilename, &sStat) == 0)
2790
0
    {
2791
0
        CPLDebug("Shape",
2792
0
                 "File %s is still reported as existing whereas "
2793
0
                 "it should have been deleted",
2794
0
                 osFilename.c_str());
2795
0
    }
2796
0
}
2797
2798
/************************************************************************/
2799
/*                          ForceDeleteFile()                           */
2800
/************************************************************************/
2801
2802
static void ForceDeleteFile(const CPLString &osFilename)
2803
0
{
2804
0
    if (VSIUnlink(osFilename) != 0)
2805
0
    {
2806
        // In case of failure retry with a small delay (Windows specific)
2807
0
        CPLSleep(0.1);
2808
0
        if (VSIUnlink(osFilename) != 0)
2809
0
        {
2810
0
            CPLDebug("Shape", "Cannot delete %s : %s", osFilename.c_str(),
2811
0
                     VSIStrerror(errno));
2812
0
        }
2813
0
    }
2814
0
    CheckFileDeletion(osFilename);
2815
0
}
2816
2817
/************************************************************************/
2818
/*                               Repack()                               */
2819
/*                                                                      */
2820
/*      Repack the shape and dbf file, dropping deleted records.        */
2821
/*      FIDs may change.                                                */
2822
/************************************************************************/
2823
2824
OGRErr OGRShapeLayer::Repack()
2825
2826
0
{
2827
0
    if (m_eNeedRepack == NO)
2828
0
    {
2829
0
        CPLDebug("Shape", "REPACK: nothing to do. Was done previously");
2830
0
        return OGRERR_NONE;
2831
0
    }
2832
2833
0
    if (!StartUpdate("Repack"))
2834
0
        return OGRERR_FAILURE;
2835
2836
    /* -------------------------------------------------------------------- */
2837
    /*      Build a list of records to be dropped.                          */
2838
    /* -------------------------------------------------------------------- */
2839
0
    std::vector<int> anRecordsToDelete;
2840
0
    OGRErr eErr = OGRERR_NONE;
2841
2842
0
    CPLDebug("Shape", "REPACK: Checking if features have been deleted");
2843
2844
0
    if (m_hDBF != nullptr)
2845
0
    {
2846
0
        try
2847
0
        {
2848
0
            for (int iShape = 0; iShape < m_nTotalShapeCount; iShape++)
2849
0
            {
2850
0
                if (DBFIsRecordDeleted(m_hDBF, iShape))
2851
0
                {
2852
0
                    anRecordsToDelete.push_back(iShape);
2853
0
                }
2854
0
                if (VSIFEofL(VSI_SHP_GetVSIL(m_hDBF->fp)) ||
2855
0
                    VSIFErrorL(VSI_SHP_GetVSIL(m_hDBF->fp)))
2856
0
                {
2857
0
                    return OGRERR_FAILURE;  // I/O error.
2858
0
                }
2859
0
            }
2860
0
        }
2861
0
        catch (const std::bad_alloc &)
2862
0
        {
2863
0
            CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory in Repack()");
2864
0
            return OGRERR_FAILURE;
2865
0
        }
2866
0
    }
2867
2868
    /* -------------------------------------------------------------------- */
2869
    /*      If there are no records marked for deletion, we take no         */
2870
    /*      action.                                                         */
2871
    /* -------------------------------------------------------------------- */
2872
0
    if (anRecordsToDelete.empty() && !m_bSHPNeedsRepack)
2873
0
    {
2874
0
        CPLDebug("Shape", "REPACK: nothing to do");
2875
0
        return OGRERR_NONE;
2876
0
    }
2877
2878
    /* -------------------------------------------------------------------- */
2879
    /*      Find existing filenames with exact case (see #3293).            */
2880
    /* -------------------------------------------------------------------- */
2881
0
    const CPLString osDirname(CPLGetPathSafe(m_osFullName.c_str()));
2882
0
    const CPLString osBasename(CPLGetBasenameSafe(m_osFullName.c_str()));
2883
2884
0
    CPLString osDBFName;
2885
0
    CPLString osSHPName;
2886
0
    CPLString osSHXName;
2887
0
    CPLString osCPGName;
2888
0
    char **papszCandidates = VSIReadDir(osDirname);
2889
0
    int i = 0;
2890
0
    while (papszCandidates != nullptr && papszCandidates[i] != nullptr)
2891
0
    {
2892
0
        const CPLString osCandidateBasename =
2893
0
            CPLGetBasenameSafe(papszCandidates[i]);
2894
0
        const CPLString osCandidateExtension =
2895
0
            CPLGetExtensionSafe(papszCandidates[i]);
2896
#ifdef _WIN32
2897
        // On Windows, as filenames are case insensitive, a shapefile layer can
2898
        // be made of foo.shp and FOO.DBF, so use case insensitive comparison.
2899
        if (EQUAL(osCandidateBasename, osBasename))
2900
#else
2901
0
        if (osCandidateBasename.compare(osBasename) == 0)
2902
0
#endif
2903
0
        {
2904
0
            if (EQUAL(osCandidateExtension, "dbf"))
2905
0
                osDBFName =
2906
0
                    CPLFormFilenameSafe(osDirname, papszCandidates[i], nullptr);
2907
0
            else if (EQUAL(osCandidateExtension, "shp"))
2908
0
                osSHPName =
2909
0
                    CPLFormFilenameSafe(osDirname, papszCandidates[i], nullptr);
2910
0
            else if (EQUAL(osCandidateExtension, "shx"))
2911
0
                osSHXName =
2912
0
                    CPLFormFilenameSafe(osDirname, papszCandidates[i], nullptr);
2913
0
            else if (EQUAL(osCandidateExtension, "cpg"))
2914
0
                osCPGName =
2915
0
                    CPLFormFilenameSafe(osDirname, papszCandidates[i], nullptr);
2916
0
        }
2917
2918
0
        i++;
2919
0
    }
2920
0
    CSLDestroy(papszCandidates);
2921
0
    papszCandidates = nullptr;
2922
2923
0
    if (m_hDBF != nullptr && osDBFName.empty())
2924
0
    {
2925
0
        CPLError(CE_Failure, CPLE_AppDefined,
2926
0
                 "Cannot find the filename of the DBF file, but we managed to "
2927
0
                 "open it before !");
2928
        // Should not happen, really.
2929
0
        return OGRERR_FAILURE;
2930
0
    }
2931
2932
0
    if (m_hSHP != nullptr && osSHPName.empty())
2933
0
    {
2934
0
        CPLError(CE_Failure, CPLE_AppDefined,
2935
0
                 "Cannot find the filename of the SHP file, but we managed to "
2936
0
                 "open it before !");
2937
        // Should not happen, really.
2938
0
        return OGRERR_FAILURE;
2939
0
    }
2940
2941
0
    if (m_hSHP != nullptr && osSHXName.empty())
2942
0
    {
2943
0
        CPLError(CE_Failure, CPLE_AppDefined,
2944
0
                 "Cannot find the filename of the SHX file, but we managed to "
2945
0
                 "open it before !");
2946
        // Should not happen, really.
2947
0
        return OGRERR_FAILURE;
2948
0
    }
2949
2950
    /* -------------------------------------------------------------------- */
2951
    /*      Cleanup any existing spatial index.  It will become             */
2952
    /*      meaningless when the fids change.                               */
2953
    /* -------------------------------------------------------------------- */
2954
0
    if (CheckForQIX() || CheckForSBN())
2955
0
        DropSpatialIndex();
2956
2957
    /* -------------------------------------------------------------------- */
2958
    /*      Create a new dbf file, matching the old.                        */
2959
    /* -------------------------------------------------------------------- */
2960
0
    bool bMustReopenDBF = false;
2961
0
    CPLString oTempFileDBF;
2962
0
    const int nNewRecords =
2963
0
        m_nTotalShapeCount - static_cast<int>(anRecordsToDelete.size());
2964
2965
0
    if (m_hDBF != nullptr && !anRecordsToDelete.empty())
2966
0
    {
2967
0
        CPLDebug("Shape", "REPACK: repacking .dbf");
2968
0
        bMustReopenDBF = true;
2969
2970
0
        oTempFileDBF = CPLFormFilenameSafe(osDirname, osBasename, nullptr);
2971
0
        oTempFileDBF += "_packed.dbf";
2972
2973
0
        DBFHandle hNewDBF = DBFCloneEmpty(m_hDBF, oTempFileDBF);
2974
0
        if (hNewDBF == nullptr)
2975
0
        {
2976
0
            CPLError(CE_Failure, CPLE_OpenFailed,
2977
0
                     "Failed to create temp file %s.", oTempFileDBF.c_str());
2978
0
            return OGRERR_FAILURE;
2979
0
        }
2980
2981
        // Delete temporary .cpg file if existing.
2982
0
        if (!osCPGName.empty())
2983
0
        {
2984
0
            CPLString oCPGTempFile =
2985
0
                CPLFormFilenameSafe(osDirname, osBasename, nullptr);
2986
0
            oCPGTempFile += "_packed.cpg";
2987
0
            ForceDeleteFile(oCPGTempFile);
2988
0
        }
2989
2990
        /* --------------------------------------------------------------------
2991
         */
2992
        /*      Copy over all records that are not deleted. */
2993
        /* --------------------------------------------------------------------
2994
         */
2995
0
        int iDestShape = 0;
2996
0
        size_t iNextDeletedShape = 0;
2997
2998
0
        for (int iShape = 0; iShape < m_nTotalShapeCount && eErr == OGRERR_NONE;
2999
0
             iShape++)
3000
0
        {
3001
0
            if (iNextDeletedShape < anRecordsToDelete.size() &&
3002
0
                anRecordsToDelete[iNextDeletedShape] == iShape)
3003
0
            {
3004
0
                iNextDeletedShape++;
3005
0
            }
3006
0
            else
3007
0
            {
3008
0
                void *pTuple = const_cast<char *>(DBFReadTuple(m_hDBF, iShape));
3009
0
                if (pTuple == nullptr ||
3010
0
                    !DBFWriteTuple(hNewDBF, iDestShape++, pTuple))
3011
0
                {
3012
0
                    CPLError(CE_Failure, CPLE_AppDefined,
3013
0
                             "Error writing record %d in .dbf", iShape);
3014
0
                    eErr = OGRERR_FAILURE;
3015
0
                }
3016
0
            }
3017
0
        }
3018
3019
0
        DBFClose(hNewDBF);
3020
3021
0
        if (eErr != OGRERR_NONE)
3022
0
        {
3023
0
            VSIUnlink(oTempFileDBF);
3024
0
            return eErr;
3025
0
        }
3026
0
    }
3027
3028
    /* -------------------------------------------------------------------- */
3029
    /*      Now create a shapefile matching the old one.                    */
3030
    /* -------------------------------------------------------------------- */
3031
0
    bool bMustReopenSHP = m_hSHP != nullptr;
3032
0
    CPLString oTempFileSHP;
3033
0
    CPLString oTempFileSHX;
3034
3035
0
    SHPInfo sSHPInfo;
3036
0
    memset(&sSHPInfo, 0, sizeof(sSHPInfo));
3037
0
    unsigned int *panRecOffsetNew = nullptr;
3038
0
    unsigned int *panRecSizeNew = nullptr;
3039
3040
    // On Windows, use the pack-in-place approach, ie copy the content of
3041
    // the _packed files on top of the existing opened files. This avoids
3042
    // many issues with files being locked, at the expense of more I/O
3043
0
    const bool bPackInPlace =
3044
0
        CPLTestBool(CPLGetConfigOption("OGR_SHAPE_PACK_IN_PLACE",
3045
#ifdef _WIN32
3046
                                       "YES"
3047
#else
3048
0
                                       "NO"
3049
0
#endif
3050
0
                                       ));
3051
3052
0
    if (m_hSHP != nullptr)
3053
0
    {
3054
0
        CPLDebug("Shape", "REPACK: repacking .shp + .shx");
3055
3056
0
        oTempFileSHP = CPLFormFilenameSafe(osDirname, osBasename, nullptr);
3057
0
        oTempFileSHP += "_packed.shp";
3058
0
        oTempFileSHX = CPLFormFilenameSafe(osDirname, osBasename, nullptr);
3059
0
        oTempFileSHX += "_packed.shx";
3060
3061
0
        SHPHandle hNewSHP = SHPCreate(oTempFileSHP, m_hSHP->nShapeType);
3062
0
        if (hNewSHP == nullptr)
3063
0
        {
3064
0
            if (!oTempFileDBF.empty())
3065
0
                VSIUnlink(oTempFileDBF);
3066
0
            return OGRERR_FAILURE;
3067
0
        }
3068
3069
        /* --------------------------------------------------------------------
3070
         */
3071
        /*      Copy over all records that are not deleted. */
3072
        /* --------------------------------------------------------------------
3073
         */
3074
0
        size_t iNextDeletedShape = 0;
3075
3076
0
        for (int iShape = 0; iShape < m_nTotalShapeCount && eErr == OGRERR_NONE;
3077
0
             iShape++)
3078
0
        {
3079
0
            if (iNextDeletedShape < anRecordsToDelete.size() &&
3080
0
                anRecordsToDelete[iNextDeletedShape] == iShape)
3081
0
            {
3082
0
                iNextDeletedShape++;
3083
0
            }
3084
0
            else
3085
0
            {
3086
0
                SHPObject *hObject = SHPReadObject(m_hSHP, iShape);
3087
0
                if (hObject == nullptr ||
3088
0
                    SHPWriteObject(hNewSHP, -1, hObject) == -1)
3089
0
                {
3090
0
                    CPLError(CE_Failure, CPLE_AppDefined,
3091
0
                             "Error writing record %d in .shp", iShape);
3092
0
                    eErr = OGRERR_FAILURE;
3093
0
                }
3094
3095
0
                if (hObject)
3096
0
                    SHPDestroyObject(hObject);
3097
0
            }
3098
0
        }
3099
3100
0
        if (bPackInPlace)
3101
0
        {
3102
            // Backup information of the updated shape context so as to
3103
            // restore it later in the current shape context
3104
0
            memcpy(&sSHPInfo, hNewSHP, sizeof(sSHPInfo));
3105
3106
            // Use malloc like shapelib does
3107
0
            panRecOffsetNew = reinterpret_cast<unsigned int *>(
3108
0
                malloc(sizeof(unsigned int) * hNewSHP->nMaxRecords));
3109
0
            panRecSizeNew = reinterpret_cast<unsigned int *>(
3110
0
                malloc(sizeof(unsigned int) * hNewSHP->nMaxRecords));
3111
0
            if (panRecOffsetNew == nullptr || panRecSizeNew == nullptr)
3112
0
            {
3113
0
                CPLError(CE_Failure, CPLE_OutOfMemory,
3114
0
                         "Cannot allocate panRecOffsetNew/panRecSizeNew");
3115
0
                eErr = OGRERR_FAILURE;
3116
0
            }
3117
0
            else
3118
0
            {
3119
0
                memcpy(panRecOffsetNew, hNewSHP->panRecOffset,
3120
0
                       sizeof(unsigned int) * hNewSHP->nRecords);
3121
0
                memcpy(panRecSizeNew, hNewSHP->panRecSize,
3122
0
                       sizeof(unsigned int) * hNewSHP->nRecords);
3123
0
            }
3124
0
        }
3125
3126
0
        SHPClose(hNewSHP);
3127
3128
0
        if (eErr != OGRERR_NONE)
3129
0
        {
3130
0
            VSIUnlink(oTempFileSHP);
3131
0
            VSIUnlink(oTempFileSHX);
3132
0
            if (!oTempFileDBF.empty())
3133
0
                VSIUnlink(oTempFileDBF);
3134
0
            free(panRecOffsetNew);
3135
0
            free(panRecSizeNew);
3136
0
            return eErr;
3137
0
        }
3138
0
    }
3139
3140
    // We could also use pack in place for Unix but this involves extra I/O
3141
    // w.r.t to the delete and rename approach
3142
3143
0
    if (bPackInPlace)
3144
0
    {
3145
0
        if (m_hDBF != nullptr && !oTempFileDBF.empty())
3146
0
        {
3147
0
            if (!OGRShapeDataSource::CopyInPlace(VSI_SHP_GetVSIL(m_hDBF->fp),
3148
0
                                                 oTempFileDBF))
3149
0
            {
3150
0
                CPLError(
3151
0
                    CE_Failure, CPLE_FileIO,
3152
0
                    "An error occurred while copying the content of %s on top "
3153
0
                    "of %s. "
3154
0
                    "The non corrupted version is in the _packed.dbf, "
3155
0
                    "_packed.shp and _packed.shx files that you should rename "
3156
0
                    "on top of the main ones.",
3157
0
                    oTempFileDBF.c_str(), VSI_SHP_GetFilename(m_hDBF->fp));
3158
0
                free(panRecOffsetNew);
3159
0
                free(panRecSizeNew);
3160
3161
0
                DBFClose(m_hDBF);
3162
0
                m_hDBF = nullptr;
3163
0
                if (m_hSHP != nullptr)
3164
0
                {
3165
0
                    SHPClose(m_hSHP);
3166
0
                    m_hSHP = nullptr;
3167
0
                }
3168
3169
0
                return OGRERR_FAILURE;
3170
0
            }
3171
3172
            // Refresh current handle
3173
0
            m_hDBF->nRecords = nNewRecords;
3174
0
        }
3175
3176
0
        if (m_hSHP != nullptr && !oTempFileSHP.empty())
3177
0
        {
3178
0
            if (!OGRShapeDataSource::CopyInPlace(VSI_SHP_GetVSIL(m_hSHP->fpSHP),
3179
0
                                                 oTempFileSHP))
3180
0
            {
3181
0
                CPLError(
3182
0
                    CE_Failure, CPLE_FileIO,
3183
0
                    "An error occurred while copying the content of %s on top "
3184
0
                    "of %s. "
3185
0
                    "The non corrupted version is in the _packed.dbf, "
3186
0
                    "_packed.shp and _packed.shx files that you should rename "
3187
0
                    "on top of the main ones.",
3188
0
                    oTempFileSHP.c_str(), VSI_SHP_GetFilename(m_hSHP->fpSHP));
3189
0
                free(panRecOffsetNew);
3190
0
                free(panRecSizeNew);
3191
3192
0
                if (m_hDBF != nullptr)
3193
0
                {
3194
0
                    DBFClose(m_hDBF);
3195
0
                    m_hDBF = nullptr;
3196
0
                }
3197
0
                SHPClose(m_hSHP);
3198
0
                m_hSHP = nullptr;
3199
3200
0
                return OGRERR_FAILURE;
3201
0
            }
3202
0
            if (!OGRShapeDataSource::CopyInPlace(VSI_SHP_GetVSIL(m_hSHP->fpSHX),
3203
0
                                                 oTempFileSHX))
3204
0
            {
3205
0
                CPLError(
3206
0
                    CE_Failure, CPLE_FileIO,
3207
0
                    "An error occurred while copying the content of %s on top "
3208
0
                    "of %s. "
3209
0
                    "The non corrupted version is in the _packed.dbf, "
3210
0
                    "_packed.shp and _packed.shx files that you should rename "
3211
0
                    "on top of the main ones.",
3212
0
                    oTempFileSHX.c_str(), VSI_SHP_GetFilename(m_hSHP->fpSHX));
3213
0
                free(panRecOffsetNew);
3214
0
                free(panRecSizeNew);
3215
3216
0
                if (m_hDBF != nullptr)
3217
0
                {
3218
0
                    DBFClose(m_hDBF);
3219
0
                    m_hDBF = nullptr;
3220
0
                }
3221
0
                SHPClose(m_hSHP);
3222
0
                m_hSHP = nullptr;
3223
3224
0
                return OGRERR_FAILURE;
3225
0
            }
3226
3227
            // Refresh current handle
3228
0
            m_hSHP->nRecords = sSHPInfo.nRecords;
3229
0
            m_hSHP->nMaxRecords = sSHPInfo.nMaxRecords;
3230
0
            m_hSHP->nFileSize = sSHPInfo.nFileSize;
3231
0
            CPLAssert(sizeof(sSHPInfo.adBoundsMin) == 4 * sizeof(double));
3232
0
            memcpy(m_hSHP->adBoundsMin, sSHPInfo.adBoundsMin,
3233
0
                   sizeof(sSHPInfo.adBoundsMin));
3234
0
            memcpy(m_hSHP->adBoundsMax, sSHPInfo.adBoundsMax,
3235
0
                   sizeof(sSHPInfo.adBoundsMax));
3236
0
            free(m_hSHP->panRecOffset);
3237
0
            free(m_hSHP->panRecSize);
3238
0
            m_hSHP->panRecOffset = panRecOffsetNew;
3239
0
            m_hSHP->panRecSize = panRecSizeNew;
3240
0
        }
3241
0
        else
3242
0
        {
3243
            // The free() are not really necessary but CSA doesn't realize it
3244
0
            free(panRecOffsetNew);
3245
0
            free(panRecSizeNew);
3246
0
        }
3247
3248
        // Now that everything is successful, we can delete the temp files
3249
0
        if (!oTempFileDBF.empty())
3250
0
        {
3251
0
            ForceDeleteFile(oTempFileDBF);
3252
0
        }
3253
0
        if (!oTempFileSHP.empty())
3254
0
        {
3255
0
            ForceDeleteFile(oTempFileSHP);
3256
0
            ForceDeleteFile(oTempFileSHX);
3257
0
        }
3258
0
    }
3259
0
    else
3260
0
    {
3261
        /* --------------------------------------------------------------------
3262
         */
3263
        /*      Cleanup the old .dbf, .shp, .shx and rename the new ones. */
3264
        /* --------------------------------------------------------------------
3265
         */
3266
0
        if (!oTempFileDBF.empty())
3267
0
        {
3268
0
            DBFClose(m_hDBF);
3269
0
            m_hDBF = nullptr;
3270
3271
0
            if (VSIUnlink(osDBFName) != 0)
3272
0
            {
3273
0
                CPLError(CE_Failure, CPLE_FileIO,
3274
0
                         "Failed to delete old DBF file: %s",
3275
0
                         VSIStrerror(errno));
3276
3277
0
                m_hDBF =
3278
0
                    m_poDS->DS_DBFOpen(osDBFName, m_bUpdateAccess ? "r+" : "r");
3279
3280
0
                VSIUnlink(oTempFileDBF);
3281
3282
0
                return OGRERR_FAILURE;
3283
0
            }
3284
3285
0
            if (VSIRename(oTempFileDBF, osDBFName) != 0)
3286
0
            {
3287
0
                CPLError(CE_Failure, CPLE_FileIO,
3288
0
                         "Can not rename new DBF file: %s", VSIStrerror(errno));
3289
0
                return OGRERR_FAILURE;
3290
0
            }
3291
3292
0
            CheckFileDeletion(oTempFileDBF);
3293
0
        }
3294
3295
0
        if (!oTempFileSHP.empty())
3296
0
        {
3297
0
            SHPClose(m_hSHP);
3298
0
            m_hSHP = nullptr;
3299
3300
0
            if (VSIUnlink(osSHPName) != 0)
3301
0
            {
3302
0
                CPLError(CE_Failure, CPLE_FileIO,
3303
0
                         "Can not delete old SHP file: %s", VSIStrerror(errno));
3304
0
                return OGRERR_FAILURE;
3305
0
            }
3306
3307
0
            if (VSIUnlink(osSHXName) != 0)
3308
0
            {
3309
0
                CPLError(CE_Failure, CPLE_FileIO,
3310
0
                         "Can not delete old SHX file: %s", VSIStrerror(errno));
3311
0
                return OGRERR_FAILURE;
3312
0
            }
3313
3314
0
            if (VSIRename(oTempFileSHP, osSHPName) != 0)
3315
0
            {
3316
0
                CPLError(CE_Failure, CPLE_FileIO,
3317
0
                         "Can not rename new SHP file: %s", VSIStrerror(errno));
3318
0
                return OGRERR_FAILURE;
3319
0
            }
3320
3321
0
            if (VSIRename(oTempFileSHX, osSHXName) != 0)
3322
0
            {
3323
0
                CPLError(CE_Failure, CPLE_FileIO,
3324
0
                         "Can not rename new SHX file: %s", VSIStrerror(errno));
3325
0
                return OGRERR_FAILURE;
3326
0
            }
3327
3328
0
            CheckFileDeletion(oTempFileSHP);
3329
0
            CheckFileDeletion(oTempFileSHX);
3330
0
        }
3331
3332
        /* --------------------------------------------------------------------
3333
         */
3334
        /*      Reopen the shapefile */
3335
        /*                                                                      */
3336
        /* We do not need to reimplement OGRShapeDataSource::OpenFile() here */
3337
        /* with the fully featured error checking. */
3338
        /* If all operations above succeeded, then all necessary files are */
3339
        /* in the right place and accessible. */
3340
        /* --------------------------------------------------------------------
3341
         */
3342
3343
0
        const char *const pszAccess = m_bUpdateAccess ? "r+" : "r";
3344
3345
0
        if (bMustReopenSHP)
3346
0
            m_hSHP = m_poDS->DS_SHPOpen(osSHPName, pszAccess);
3347
0
        if (bMustReopenDBF)
3348
0
            m_hDBF = m_poDS->DS_DBFOpen(osDBFName, pszAccess);
3349
3350
0
        if ((bMustReopenSHP && nullptr == m_hSHP) ||
3351
0
            (bMustReopenDBF && nullptr == m_hDBF))
3352
0
            return OGRERR_FAILURE;
3353
0
    }
3354
3355
    /* -------------------------------------------------------------------- */
3356
    /*      Update total shape count.                                       */
3357
    /* -------------------------------------------------------------------- */
3358
0
    if (m_hDBF != nullptr)
3359
0
        m_nTotalShapeCount = m_hDBF->nRecords;
3360
0
    m_bSHPNeedsRepack = false;
3361
0
    m_eNeedRepack = NO;
3362
3363
0
    return OGRERR_NONE;
3364
0
}
3365
3366
/************************************************************************/
3367
/*                               ResizeDBF()                            */
3368
/*                                                                      */
3369
/*      Autoshrink columns of the DBF file to their minimum             */
3370
/*      size, according to the existing data.                           */
3371
/************************************************************************/
3372
3373
OGRErr OGRShapeLayer::ResizeDBF()
3374
3375
0
{
3376
0
    if (!StartUpdate("ResizeDBF"))
3377
0
        return OGRERR_FAILURE;
3378
3379
0
    if (m_hDBF == nullptr)
3380
0
    {
3381
0
        CPLError(
3382
0
            CE_Failure, CPLE_NotSupported,
3383
0
            "Attempt to RESIZE a shapefile with no .dbf file not supported.");
3384
0
        return OGRERR_FAILURE;
3385
0
    }
3386
3387
    /* Look which columns must be examined */
3388
0
    int *panColMap = static_cast<int *>(
3389
0
        CPLMalloc(m_poFeatureDefn->GetFieldCount() * sizeof(int)));
3390
0
    int *panBestWidth = static_cast<int *>(
3391
0
        CPLMalloc(m_poFeatureDefn->GetFieldCount() * sizeof(int)));
3392
0
    int nStringCols = 0;
3393
0
    for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
3394
0
    {
3395
0
        if (m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTString ||
3396
0
            m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTInteger ||
3397
0
            m_poFeatureDefn->GetFieldDefn(i)->GetType() == OFTInteger64)
3398
0
        {
3399
0
            panColMap[nStringCols] = i;
3400
0
            panBestWidth[nStringCols] = 1;
3401
0
            nStringCols++;
3402
0
        }
3403
0
    }
3404
3405
0
    if (nStringCols == 0)
3406
0
    {
3407
        // Nothing to do.
3408
0
        CPLFree(panColMap);
3409
0
        CPLFree(panBestWidth);
3410
0
        return OGRERR_NONE;
3411
0
    }
3412
3413
0
    CPLDebug("SHAPE", "Computing optimal column size...");
3414
3415
0
    bool bAlreadyWarned = false;
3416
0
    for (int i = 0; i < m_hDBF->nRecords; i++)
3417
0
    {
3418
0
        if (!DBFIsRecordDeleted(m_hDBF, i))
3419
0
        {
3420
0
            for (int j = 0; j < nStringCols; j++)
3421
0
            {
3422
0
                if (DBFIsAttributeNULL(m_hDBF, i, panColMap[j]))
3423
0
                    continue;
3424
3425
0
                const char *pszVal =
3426
0
                    DBFReadStringAttribute(m_hDBF, i, panColMap[j]);
3427
0
                const int nLen = static_cast<int>(strlen(pszVal));
3428
0
                if (nLen > panBestWidth[j])
3429
0
                    panBestWidth[j] = nLen;
3430
0
            }
3431
0
        }
3432
0
        else if (!bAlreadyWarned)
3433
0
        {
3434
0
            bAlreadyWarned = true;
3435
0
            CPLDebug(
3436
0
                "SHAPE",
3437
0
                "DBF file would also need a REPACK due to deleted records");
3438
0
        }
3439
0
    }
3440
3441
0
    for (int j = 0; j < nStringCols; j++)
3442
0
    {
3443
0
        const int iField = panColMap[j];
3444
0
        OGRFieldDefn *const poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
3445
3446
0
        const char chNativeType = DBFGetNativeFieldType(m_hDBF, iField);
3447
0
        char szFieldName[XBASE_FLDNAME_LEN_READ + 1] = {};
3448
0
        int nOriWidth = 0;
3449
0
        int nPrecision = 0;
3450
0
        DBFGetFieldInfo(m_hDBF, iField, szFieldName, &nOriWidth, &nPrecision);
3451
3452
0
        if (panBestWidth[j] < nOriWidth)
3453
0
        {
3454
0
            CPLDebug("SHAPE",
3455
0
                     "Shrinking field %d (%s) from %d to %d characters", iField,
3456
0
                     poFieldDefn->GetNameRef(), nOriWidth, panBestWidth[j]);
3457
3458
0
            if (!DBFAlterFieldDefn(m_hDBF, iField, szFieldName, chNativeType,
3459
0
                                   panBestWidth[j], nPrecision))
3460
0
            {
3461
0
                CPLError(
3462
0
                    CE_Failure, CPLE_AppDefined,
3463
0
                    "Shrinking field %d (%s) from %d to %d characters failed",
3464
0
                    iField, poFieldDefn->GetNameRef(), nOriWidth,
3465
0
                    panBestWidth[j]);
3466
3467
0
                CPLFree(panColMap);
3468
0
                CPLFree(panBestWidth);
3469
3470
0
                return OGRERR_FAILURE;
3471
0
            }
3472
0
            else
3473
0
            {
3474
0
                whileUnsealing(poFieldDefn)->SetWidth(panBestWidth[j]);
3475
0
            }
3476
0
        }
3477
0
    }
3478
3479
0
    TruncateDBF();
3480
3481
0
    CPLFree(panColMap);
3482
0
    CPLFree(panBestWidth);
3483
3484
0
    return OGRERR_NONE;
3485
0
}
3486
3487
/************************************************************************/
3488
/*                            TruncateDBF()                             */
3489
/************************************************************************/
3490
3491
void OGRShapeLayer::TruncateDBF()
3492
0
{
3493
0
    if (m_hDBF == nullptr)
3494
0
        return;
3495
3496
0
    m_hDBF->sHooks.FSeek(m_hDBF->fp, 0, SEEK_END);
3497
0
    vsi_l_offset nOldSize = m_hDBF->sHooks.FTell(m_hDBF->fp);
3498
0
    vsi_l_offset nNewSize =
3499
0
        m_hDBF->nRecordLength * static_cast<SAOffset>(m_hDBF->nRecords) +
3500
0
        m_hDBF->nHeaderLength;
3501
0
    if (m_hDBF->bWriteEndOfFileChar)
3502
0
        nNewSize++;
3503
0
    if (nNewSize < nOldSize)
3504
0
    {
3505
0
        CPLDebug("SHAPE",
3506
0
                 "Truncating DBF file from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB
3507
0
                 " bytes",
3508
0
                 nOldSize, nNewSize);
3509
0
        VSIFTruncateL(VSI_SHP_GetVSIL(m_hDBF->fp), nNewSize);
3510
0
    }
3511
0
    m_hDBF->sHooks.FSeek(m_hDBF->fp, 0, SEEK_SET);
3512
0
}
3513
3514
/************************************************************************/
3515
/*                        RecomputeExtent()                             */
3516
/*                                                                      */
3517
/*      Force recomputation of the extent of the .SHP file              */
3518
/************************************************************************/
3519
3520
OGRErr OGRShapeLayer::RecomputeExtent()
3521
0
{
3522
0
    if (!StartUpdate("RecomputeExtent"))
3523
0
        return OGRERR_FAILURE;
3524
3525
0
    if (m_hSHP == nullptr)
3526
0
    {
3527
0
        CPLError(CE_Failure, CPLE_AppDefined,
3528
0
                 "The RECOMPUTE EXTENT operation is not permitted on a layer "
3529
0
                 "without .SHP file.");
3530
0
        return OGRERR_FAILURE;
3531
0
    }
3532
3533
0
    double adBoundsMin[4] = {0.0, 0.0, 0.0, 0.0};
3534
0
    double adBoundsMax[4] = {0.0, 0.0, 0.0, 0.0};
3535
3536
0
    bool bHasBeenInit = false;
3537
3538
0
    for (int iShape = 0; iShape < m_nTotalShapeCount; iShape++)
3539
0
    {
3540
0
        if (m_hDBF == nullptr || !DBFIsRecordDeleted(m_hDBF, iShape))
3541
0
        {
3542
0
            SHPObject *psObject = SHPReadObject(m_hSHP, iShape);
3543
0
            if (psObject != nullptr && psObject->nSHPType != SHPT_NULL &&
3544
0
                psObject->nVertices != 0)
3545
0
            {
3546
0
                if (!bHasBeenInit)
3547
0
                {
3548
0
                    bHasBeenInit = true;
3549
0
                    adBoundsMin[0] = psObject->padfX[0];
3550
0
                    adBoundsMax[0] = psObject->padfX[0];
3551
0
                    adBoundsMin[1] = psObject->padfY[0];
3552
0
                    adBoundsMax[1] = psObject->padfY[0];
3553
0
                    if (psObject->padfZ)
3554
0
                    {
3555
0
                        adBoundsMin[2] = psObject->padfZ[0];
3556
0
                        adBoundsMax[2] = psObject->padfZ[0];
3557
0
                    }
3558
0
                    if (psObject->padfM)
3559
0
                    {
3560
0
                        adBoundsMin[3] = psObject->padfM[0];
3561
0
                        adBoundsMax[3] = psObject->padfM[0];
3562
0
                    }
3563
0
                }
3564
3565
0
                for (int i = 0; i < psObject->nVertices; i++)
3566
0
                {
3567
0
                    adBoundsMin[0] =
3568
0
                        std::min(adBoundsMin[0], psObject->padfX[i]);
3569
0
                    adBoundsMin[1] =
3570
0
                        std::min(adBoundsMin[1], psObject->padfY[i]);
3571
0
                    adBoundsMax[0] =
3572
0
                        std::max(adBoundsMax[0], psObject->padfX[i]);
3573
0
                    adBoundsMax[1] =
3574
0
                        std::max(adBoundsMax[1], psObject->padfY[i]);
3575
0
                    if (psObject->padfZ)
3576
0
                    {
3577
0
                        adBoundsMin[2] =
3578
0
                            std::min(adBoundsMin[2], psObject->padfZ[i]);
3579
0
                        adBoundsMax[2] =
3580
0
                            std::max(adBoundsMax[2], psObject->padfZ[i]);
3581
0
                    }
3582
0
                    if (psObject->padfM)
3583
0
                    {
3584
0
                        adBoundsMax[3] =
3585
0
                            std::max(adBoundsMax[3], psObject->padfM[i]);
3586
0
                        adBoundsMin[3] =
3587
0
                            std::min(adBoundsMin[3], psObject->padfM[i]);
3588
0
                    }
3589
0
                }
3590
0
            }
3591
0
            SHPDestroyObject(psObject);
3592
0
        }
3593
0
    }
3594
3595
0
    if (memcmp(m_hSHP->adBoundsMin, adBoundsMin, 4 * sizeof(double)) != 0 ||
3596
0
        memcmp(m_hSHP->adBoundsMax, adBoundsMax, 4 * sizeof(double)) != 0)
3597
0
    {
3598
0
        m_bHeaderDirty = true;
3599
0
        m_hSHP->bUpdated = TRUE;
3600
0
        memcpy(m_hSHP->adBoundsMin, adBoundsMin, 4 * sizeof(double));
3601
0
        memcpy(m_hSHP->adBoundsMax, adBoundsMax, 4 * sizeof(double));
3602
0
    }
3603
3604
0
    return OGRERR_NONE;
3605
0
}
3606
3607
/************************************************************************/
3608
/*                             TouchLayer()                             */
3609
/************************************************************************/
3610
3611
bool OGRShapeLayer::TouchLayer()
3612
0
{
3613
0
    m_poDS->SetLastUsedLayer(this);
3614
3615
0
    if (m_eFileDescriptorsState == FD_OPENED)
3616
0
        return true;
3617
0
    if (m_eFileDescriptorsState == FD_CANNOT_REOPEN)
3618
0
        return false;
3619
3620
0
    return ReopenFileDescriptors();
3621
0
}
3622
3623
/************************************************************************/
3624
/*                       ReopenFileDescriptors()                        */
3625
/************************************************************************/
3626
3627
bool OGRShapeLayer::ReopenFileDescriptors()
3628
0
{
3629
0
    CPLDebug("SHAPE", "ReopenFileDescriptors(%s)", m_osFullName.c_str());
3630
3631
0
    const bool bRealUpdateAccess =
3632
0
        m_bUpdateAccess &&
3633
0
        (!m_poDS->IsZip() || !m_poDS->GetTemporaryUnzipDir().empty());
3634
3635
0
    if (m_bHSHPWasNonNULL)
3636
0
    {
3637
0
        m_hSHP = m_poDS->DS_SHPOpen(m_osFullName.c_str(),
3638
0
                                    bRealUpdateAccess ? "r+" : "r");
3639
3640
0
        if (m_hSHP == nullptr)
3641
0
        {
3642
0
            m_eFileDescriptorsState = FD_CANNOT_REOPEN;
3643
0
            return false;
3644
0
        }
3645
0
    }
3646
3647
0
    if (m_bHDBFWasNonNULL)
3648
0
    {
3649
0
        m_hDBF = m_poDS->DS_DBFOpen(m_osFullName.c_str(),
3650
0
                                    bRealUpdateAccess ? "r+" : "r");
3651
3652
0
        if (m_hDBF == nullptr)
3653
0
        {
3654
0
            CPLError(
3655
0
                CE_Failure, CPLE_OpenFailed, "Cannot reopen %s",
3656
0
                CPLResetExtensionSafe(m_osFullName.c_str(), "dbf").c_str());
3657
0
            m_eFileDescriptorsState = FD_CANNOT_REOPEN;
3658
0
            return false;
3659
0
        }
3660
0
    }
3661
3662
0
    m_eFileDescriptorsState = FD_OPENED;
3663
3664
0
    return true;
3665
0
}
3666
3667
/************************************************************************/
3668
/*                        CloseUnderlyingLayer()                        */
3669
/************************************************************************/
3670
3671
void OGRShapeLayer::CloseUnderlyingLayer()
3672
0
{
3673
0
    CPLDebug("SHAPE", "CloseUnderlyingLayer(%s)", m_osFullName.c_str());
3674
3675
0
    if (m_hDBF != nullptr)
3676
0
        DBFClose(m_hDBF);
3677
0
    m_hDBF = nullptr;
3678
3679
0
    if (m_hSHP != nullptr)
3680
0
        SHPClose(m_hSHP);
3681
0
    m_hSHP = nullptr;
3682
3683
    // We close QIX and reset the check flag, so that CheckForQIX()
3684
    // will retry opening it if necessary when the layer is active again.
3685
0
    if (m_hQIX != nullptr)
3686
0
        SHPCloseDiskTree(m_hQIX);
3687
0
    m_hQIX = nullptr;
3688
0
    m_bCheckedForQIX = false;
3689
3690
0
    if (m_hSBN != nullptr)
3691
0
        SBNCloseDiskTree(m_hSBN);
3692
0
    m_hSBN = nullptr;
3693
0
    m_bCheckedForSBN = false;
3694
3695
0
    m_eFileDescriptorsState = FD_CLOSED;
3696
0
}
3697
3698
/************************************************************************/
3699
/*                           AddToFileList()                            */
3700
/************************************************************************/
3701
3702
void OGRShapeLayer::AddToFileList(CPLStringList &oFileList)
3703
0
{
3704
0
    if (!TouchLayer())
3705
0
        return;
3706
3707
0
    if (m_hSHP)
3708
0
    {
3709
0
        const char *pszSHPFilename = VSI_SHP_GetFilename(m_hSHP->fpSHP);
3710
0
        oFileList.AddStringDirectly(VSIGetCanonicalFilename(pszSHPFilename));
3711
0
        const std::string osSHPExt = CPLGetExtensionSafe(pszSHPFilename);
3712
0
        const std::string osSHXFilename = CPLResetExtensionSafe(
3713
0
            pszSHPFilename, (osSHPExt[0] == 's') ? "shx" : "SHX");
3714
0
        oFileList.AddStringDirectly(
3715
0
            VSIGetCanonicalFilename(osSHXFilename.c_str()));
3716
0
    }
3717
3718
0
    if (m_hDBF)
3719
0
    {
3720
0
        const char *pszDBFFilename = VSI_SHP_GetFilename(m_hDBF->fp);
3721
0
        oFileList.AddStringDirectly(VSIGetCanonicalFilename(pszDBFFilename));
3722
0
        if (m_hDBF->pszCodePage != nullptr && m_hDBF->iLanguageDriver == 0)
3723
0
        {
3724
0
            const std::string osDBFExt = CPLGetExtensionSafe(pszDBFFilename);
3725
0
            const std::string osCPGFilename = CPLResetExtensionSafe(
3726
0
                pszDBFFilename, (osDBFExt[0] == 'd') ? "cpg" : "CPG");
3727
0
            oFileList.AddStringDirectly(
3728
0
                VSIGetCanonicalFilename(osCPGFilename.c_str()));
3729
0
        }
3730
0
    }
3731
3732
0
    if (m_hSHP)
3733
0
    {
3734
0
        if (GetSpatialRef() != nullptr)
3735
0
        {
3736
0
            const OGRShapeGeomFieldDefn *poGeomFieldDefn =
3737
0
                cpl::down_cast<const OGRShapeGeomFieldDefn *>(
3738
0
                    GetLayerDefn()->GetGeomFieldDefn(0));
3739
0
            oFileList.AddStringDirectly(
3740
0
                VSIGetCanonicalFilename(poGeomFieldDefn->GetPrjFilename()));
3741
0
        }
3742
0
        if (CheckForQIX())
3743
0
        {
3744
0
            const std::string osQIXFilename =
3745
0
                CPLResetExtensionSafe(m_osFullName.c_str(), "qix");
3746
0
            oFileList.AddStringDirectly(
3747
0
                VSIGetCanonicalFilename(osQIXFilename.c_str()));
3748
0
        }
3749
0
        else if (CheckForSBN())
3750
0
        {
3751
0
            const std::string osSBNFilename =
3752
0
                CPLResetExtensionSafe(m_osFullName.c_str(), "sbn");
3753
0
            oFileList.AddStringDirectly(
3754
0
                VSIGetCanonicalFilename(osSBNFilename.c_str()));
3755
0
            const std::string osSBXFilename =
3756
0
                CPLResetExtensionSafe(m_osFullName.c_str(), "sbx");
3757
0
            oFileList.AddStringDirectly(
3758
0
                VSIGetCanonicalFilename(osSBXFilename.c_str()));
3759
0
        }
3760
0
    }
3761
3762
0
    if (m_bHasShpXML)
3763
0
    {
3764
0
        const std::string osSBXFilename =
3765
0
            CPLResetExtensionSafe(m_osFullName.c_str(), "shp.xml");
3766
0
        oFileList.AddStringDirectly(
3767
0
            VSIGetCanonicalFilename(osSBXFilename.c_str()));
3768
0
    }
3769
0
}
3770
3771
/************************************************************************/
3772
/*                  UpdateFollowingDeOrRecompression()                  */
3773
/************************************************************************/
3774
3775
void OGRShapeLayer::UpdateFollowingDeOrRecompression()
3776
0
{
3777
0
    CPLAssert(m_poDS->IsZip());
3778
0
    CPLString osDSDir = m_poDS->GetTemporaryUnzipDir();
3779
0
    if (osDSDir.empty())
3780
0
        osDSDir = m_poDS->GetVSIZipPrefixeDir();
3781
3782
0
    if (GetSpatialRef() != nullptr)
3783
0
    {
3784
0
        OGRShapeGeomFieldDefn *poGeomFieldDefn =
3785
0
            cpl::down_cast<OGRShapeGeomFieldDefn *>(
3786
0
                GetLayerDefn()->GetGeomFieldDefn(0));
3787
0
        poGeomFieldDefn->SetPrjFilename(
3788
0
            CPLFormFilenameSafe(
3789
0
                osDSDir.c_str(),
3790
0
                CPLGetFilename(poGeomFieldDefn->GetPrjFilename().c_str()),
3791
0
                nullptr)
3792
0
                .c_str());
3793
0
    }
3794
3795
0
    m_osFullName = CPLFormFilenameSafe(
3796
0
        osDSDir, CPLGetFilename(m_osFullName.c_str()), nullptr);
3797
0
    CloseUnderlyingLayer();
3798
0
}
3799
3800
/************************************************************************/
3801
/*                               Rename()                               */
3802
/************************************************************************/
3803
3804
OGRErr OGRShapeLayer::Rename(const char *pszNewName)
3805
0
{
3806
0
    if (!TestCapability(OLCRename))
3807
0
        return OGRERR_FAILURE;
3808
3809
0
    if (CPLLaunderForFilenameSafe(pszNewName, nullptr) != pszNewName)
3810
0
    {
3811
0
        CPLError(CE_Failure, CPLE_AppDefined,
3812
0
                 "Illegal characters in '%s' to form a valid filename",
3813
0
                 pszNewName);
3814
0
        return OGRERR_FAILURE;
3815
0
    }
3816
3817
0
    if (m_poDS->GetLayerByName(pszNewName) != nullptr)
3818
0
    {
3819
0
        CPLError(CE_Failure, CPLE_AppDefined, "Layer %s already exists",
3820
0
                 pszNewName);
3821
0
        return OGRERR_FAILURE;
3822
0
    }
3823
3824
0
    if (!m_poDS->UncompressIfNeeded())
3825
0
        return OGRERR_FAILURE;
3826
3827
0
    CPLStringList oFileList;
3828
0
    AddToFileList(oFileList);
3829
3830
0
    const std::string osDirname = CPLGetPathSafe(m_osFullName.c_str());
3831
0
    for (int i = 0; i < oFileList.size(); ++i)
3832
0
    {
3833
0
        const std::string osRenamedFile =
3834
0
            CPLFormFilenameSafe(osDirname.c_str(), pszNewName,
3835
0
                                CPLGetExtensionSafe(oFileList[i]).c_str());
3836
0
        VSIStatBufL sStat;
3837
0
        if (VSIStatL(osRenamedFile.c_str(), &sStat) == 0)
3838
0
        {
3839
0
            CPLError(CE_Failure, CPLE_AppDefined, "File %s already exists",
3840
0
                     osRenamedFile.c_str());
3841
0
            return OGRERR_FAILURE;
3842
0
        }
3843
0
    }
3844
3845
0
    CloseUnderlyingLayer();
3846
3847
0
    for (int i = 0; i < oFileList.size(); ++i)
3848
0
    {
3849
0
        const std::string osRenamedFile =
3850
0
            CPLFormFilenameSafe(osDirname.c_str(), pszNewName,
3851
0
                                CPLGetExtensionSafe(oFileList[i]).c_str());
3852
0
        if (VSIRename(oFileList[i], osRenamedFile.c_str()) != 0)
3853
0
        {
3854
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename %s to %s",
3855
0
                     oFileList[i], osRenamedFile.c_str());
3856
0
            return OGRERR_FAILURE;
3857
0
        }
3858
0
    }
3859
3860
0
    if (GetSpatialRef() != nullptr)
3861
0
    {
3862
0
        OGRShapeGeomFieldDefn *poGeomFieldDefn =
3863
0
            cpl::down_cast<OGRShapeGeomFieldDefn *>(
3864
0
                GetLayerDefn()->GetGeomFieldDefn(0));
3865
0
        poGeomFieldDefn->SetPrjFilename(
3866
0
            CPLFormFilenameSafe(
3867
0
                osDirname.c_str(), pszNewName,
3868
0
                CPLGetExtensionSafe(poGeomFieldDefn->GetPrjFilename().c_str())
3869
0
                    .c_str())
3870
0
                .c_str());
3871
0
    }
3872
3873
0
    m_osFullName =
3874
0
        CPLFormFilenameSafe(osDirname.c_str(), pszNewName,
3875
0
                            CPLGetExtensionSafe(m_osFullName.c_str()).c_str());
3876
3877
0
    if (!ReopenFileDescriptors())
3878
0
        return OGRERR_FAILURE;
3879
3880
0
    SetDescription(pszNewName);
3881
0
    whileUnsealing(m_poFeatureDefn)->SetName(pszNewName);
3882
3883
0
    return OGRERR_NONE;
3884
0
}
3885
3886
/************************************************************************/
3887
/*                             GetDataset()                             */
3888
/************************************************************************/
3889
3890
GDALDataset *OGRShapeLayer::GetDataset()
3891
0
{
3892
0
    return m_poDS;
3893
0
}
3894
3895
/************************************************************************/
3896
/*                         GetNextArrowArray()                          */
3897
/************************************************************************/
3898
3899
// Specialized implementation restricted to situations where only retrieving
3900
// of FID values is asked (without filters)
3901
// In other cases, fall back to generic implementation.
3902
int OGRShapeLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
3903
                                     struct ArrowArray *out_array)
3904
0
{
3905
0
    m_bLastGetNextArrowArrayUsedOptimizedCodePath = false;
3906
0
    if (!TouchLayer())
3907
0
    {
3908
0
        memset(out_array, 0, sizeof(*out_array));
3909
0
        return EIO;
3910
0
    }
3911
3912
0
    if (!m_hDBF || m_poAttrQuery != nullptr || m_poFilterGeom != nullptr)
3913
0
    {
3914
0
        return OGRLayer::GetNextArrowArray(stream, out_array);
3915
0
    }
3916
3917
    // If any field is not ignored, use generic implementation
3918
0
    const int nFieldCount = m_poFeatureDefn->GetFieldCount();
3919
0
    for (int i = 0; i < nFieldCount; ++i)
3920
0
    {
3921
0
        if (!m_poFeatureDefn->GetFieldDefn(i)->IsIgnored())
3922
0
            return OGRLayer::GetNextArrowArray(stream, out_array);
3923
0
    }
3924
0
    if (GetGeomType() != wkbNone &&
3925
0
        !m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
3926
0
        return OGRLayer::GetNextArrowArray(stream, out_array);
3927
3928
0
    OGRArrowArrayHelper sHelper(m_poDS, m_poFeatureDefn.get(),
3929
0
                                m_aosArrowArrayStreamOptions, out_array);
3930
0
    if (out_array->release == nullptr)
3931
0
    {
3932
0
        return ENOMEM;
3933
0
    }
3934
3935
0
    if (!sHelper.m_bIncludeFID)
3936
0
    {
3937
0
        out_array->release(out_array);
3938
0
        return OGRLayer::GetNextArrowArray(stream, out_array);
3939
0
    }
3940
3941
0
    m_bLastGetNextArrowArrayUsedOptimizedCodePath = true;
3942
0
    int nCount = 0;
3943
0
    while (m_iNextShapeId < m_nTotalShapeCount)
3944
0
    {
3945
0
        const bool bIsDeleted =
3946
0
            CPL_TO_BOOL(DBFIsRecordDeleted(m_hDBF, m_iNextShapeId));
3947
0
        if (bIsDeleted)
3948
0
        {
3949
0
            ++m_iNextShapeId;
3950
0
            continue;
3951
0
        }
3952
0
        if (VSIFEofL(VSI_SHP_GetVSIL(m_hDBF->fp)) ||
3953
0
            VSIFErrorL(VSI_SHP_GetVSIL(m_hDBF->fp)))
3954
0
        {
3955
0
            out_array->release(out_array);
3956
0
            memset(out_array, 0, sizeof(*out_array));
3957
0
            return EIO;
3958
0
        }
3959
0
        sHelper.m_panFIDValues[nCount] = m_iNextShapeId;
3960
0
        ++m_iNextShapeId;
3961
0
        ++nCount;
3962
0
        if (nCount == sHelper.m_nMaxBatchSize)
3963
0
            break;
3964
0
    }
3965
0
    sHelper.Shrink(nCount);
3966
0
    if (nCount == 0)
3967
0
    {
3968
0
        out_array->release(out_array);
3969
0
        memset(out_array, 0, sizeof(*out_array));
3970
0
    }
3971
0
    return 0;
3972
0
}
3973
3974
/************************************************************************/
3975
/*                          GetMetadataItem()                           */
3976
/************************************************************************/
3977
3978
const char *OGRShapeLayer::GetMetadataItem(const char *pszName,
3979
                                           const char *pszDomain)
3980
0
{
3981
0
    if (pszName && pszDomain && EQUAL(pszDomain, "__DEBUG__") &&
3982
0
        EQUAL(pszName, "LAST_GET_NEXT_ARROW_ARRAY_USED_OPTIMIZED_CODE_PATH"))
3983
0
    {
3984
0
        return m_bLastGetNextArrowArrayUsedOptimizedCodePath ? "YES" : "NO";
3985
0
    }
3986
0
    return OGRLayer::GetMetadataItem(pszName, pszDomain);
3987
0
}