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