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