Coverage Report

Created: 2025-12-31 06:48

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