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