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