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