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