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