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