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