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