1 : /******************************************************************************
2 : * $Id: ogrshapelayer.cpp 25268 2012-11-29 20:21:41Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRShapeLayer class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Les Technologies SoftMap Inc.
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 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include "ogr_p.h"
34 :
35 : #if defined(_WIN32_WCE)
36 : # include <wce_errno.h>
37 : #endif
38 :
39 : #define FD_OPENED 0
40 : #define FD_CLOSED 1
41 : #define FD_CANNOT_REOPEN 2
42 :
43 : #define UNSUPPORTED_OP_READ_ONLY "%s : unsupported operation on a read-only datasource."
44 :
45 : CPL_CVSID("$Id: ogrshapelayer.cpp 25268 2012-11-29 20:21:41Z rouault $");
46 :
47 : /************************************************************************/
48 : /* OGRShapeLayer() */
49 : /************************************************************************/
50 :
51 3908 : OGRShapeLayer::OGRShapeLayer( OGRShapeDataSource* poDSIn,
52 : const char * pszFullNameIn,
53 : SHPHandle hSHPIn, DBFHandle hDBFIn,
54 : OGRSpatialReference *poSRSIn, int bSRSSetIn,
55 : int bUpdate,
56 : OGRwkbGeometryType eReqType ) :
57 3908 : OGRAbstractProxiedLayer(poDSIn->GetPool())
58 :
59 : {
60 3908 : poDS = poDSIn;
61 3908 : poSRS = poSRSIn;
62 3908 : bSRSSet = bSRSSetIn;
63 :
64 3908 : pszFullName = CPLStrdup(pszFullNameIn);
65 :
66 3908 : hSHP = hSHPIn;
67 3908 : hDBF = hDBFIn;
68 3908 : bUpdateAccess = bUpdate;
69 :
70 3908 : iNextShapeId = 0;
71 3908 : panMatchingFIDs = NULL;
72 :
73 3908 : nSpatialFIDCount = 0;
74 3908 : panSpatialFIDs = NULL;
75 3908 : m_poFilterGeomLastValid = NULL;
76 :
77 3908 : bCheckedForQIX = FALSE;
78 3908 : hQIX = NULL;
79 :
80 3908 : bCheckedForSBN = FALSE;
81 3908 : hSBN = NULL;
82 :
83 3908 : bSbnSbxDeleted = FALSE;
84 :
85 3908 : bHeaderDirty = FALSE;
86 :
87 3908 : if( hSHP != NULL )
88 : {
89 3870 : nTotalShapeCount = hSHP->nRecords;
90 3870 : if( hDBF != NULL && hDBF->nRecords != nTotalShapeCount )
91 : {
92 : CPLDebug("Shape", "Inconsistant record number in .shp (%d) and in .dbf (%d)",
93 0 : hSHP->nRecords, hDBF->nRecords);
94 : }
95 : }
96 : else
97 38 : nTotalShapeCount = hDBF->nRecords;
98 :
99 3908 : eRequestedGeomType = eReqType;
100 :
101 3908 : bTruncationWarningEmitted = FALSE;
102 :
103 3908 : bHSHPWasNonNULL = hSHPIn != NULL;
104 3908 : bHDBFWasNonNULL = hDBFIn != NULL;
105 3908 : eFileDescriptorsState = FD_OPENED;
106 3908 : TouchLayer();
107 :
108 3908 : bResizeAtClose = FALSE;
109 :
110 3908 : if( hDBF != NULL && hDBF->pszCodePage != NULL )
111 : {
112 : CPLDebug( "Shape", "DBF Codepage = %s for %s",
113 3639 : hDBF->pszCodePage, pszFullName );
114 :
115 : // Not too sure about this, but it seems like better than nothing.
116 3639 : osEncoding = ConvertCodePage( hDBF->pszCodePage );
117 : }
118 :
119 3908 : if( CPLGetConfigOption( "SHAPE_ENCODING", NULL ) != NULL )
120 0 : osEncoding = CPLGetConfigOption( "SHAPE_ENCODING", "" );
121 :
122 3908 : if( osEncoding != "" )
123 : {
124 3639 : CPLDebug( "Shape", "Treating as encoding '%s'.", osEncoding.c_str() );
125 :
126 3639 : if (!TestCapability(OLCStringsAsUTF8))
127 : {
128 1 : CPLDebug( "Shape", "Cannot recode from '%s'. Disabling recoding", osEncoding.c_str() );
129 1 : osEncoding = "";
130 : }
131 : }
132 :
133 : poFeatureDefn = SHPReadOGRFeatureDefn( CPLGetBasename(pszFullName),
134 3908 : hSHP, hDBF, osEncoding );
135 3908 : }
136 :
137 : /************************************************************************/
138 : /* ~OGRShapeLayer() */
139 : /************************************************************************/
140 :
141 3908 : OGRShapeLayer::~OGRShapeLayer()
142 :
143 : {
144 3908 : if( bResizeAtClose && hDBF != NULL )
145 : {
146 1 : ResizeDBF();
147 : }
148 :
149 3908 : if( m_nFeaturesRead > 0 && poFeatureDefn != NULL )
150 : {
151 : CPLDebug( "Shape", "%d features read on layer '%s'.",
152 : (int) m_nFeaturesRead,
153 1442 : poFeatureDefn->GetName() );
154 : }
155 :
156 3908 : ClearMatchingFIDs();
157 3908 : ClearSpatialFIDs();
158 :
159 3908 : CPLFree( pszFullName );
160 :
161 3908 : if( poFeatureDefn != NULL )
162 3908 : poFeatureDefn->Release();
163 :
164 3908 : if( poSRS != NULL )
165 418 : poSRS->Release();
166 :
167 3908 : if( hDBF != NULL )
168 1887 : DBFClose( hDBF );
169 :
170 3908 : if( hSHP != NULL )
171 1869 : SHPClose( hSHP );
172 :
173 3908 : if( hQIX != NULL )
174 23 : SHPCloseDiskTree( hQIX );
175 :
176 3908 : if( hSBN != NULL )
177 8 : SBNCloseDiskTree( hSBN );
178 3908 : }
179 :
180 : /************************************************************************/
181 : /* ConvertCodePage() */
182 : /************************************************************************/
183 :
184 3639 : CPLString OGRShapeLayer::ConvertCodePage( const char *pszCodePage )
185 :
186 : {
187 3639 : CPLString osEncoding;
188 :
189 3639 : if( pszCodePage == NULL )
190 0 : return osEncoding;
191 :
192 3639 : if( EQUALN(pszCodePage,"LDID/",5) )
193 : {
194 3638 : int nCP = -1; // windows code page.
195 :
196 : //http://www.autopark.ru/ASBProgrammerGuide/DBFSTRUC.HTM
197 3638 : switch( atoi(pszCodePage+5) )
198 : {
199 0 : case 1: nCP = 437; break;
200 0 : case 2: nCP = 850; break;
201 0 : case 3: nCP = 1252; break;
202 0 : case 4: nCP = 10000; break;
203 0 : case 8: nCP = 865; break;
204 0 : case 10: nCP = 850; break;
205 0 : case 11: nCP = 437; break;
206 0 : case 13: nCP = 437; break;
207 0 : case 14: nCP = 850; break;
208 0 : case 15: nCP = 437; break;
209 0 : case 16: nCP = 850; break;
210 0 : case 17: nCP = 437; break;
211 0 : case 18: nCP = 850; break;
212 0 : case 19: nCP = 932; break;
213 0 : case 20: nCP = 850; break;
214 0 : case 21: nCP = 437; break;
215 0 : case 22: nCP = 850; break;
216 0 : case 23: nCP = 865; break;
217 0 : case 24: nCP = 437; break;
218 0 : case 25: nCP = 437; break;
219 0 : case 26: nCP = 850; break;
220 0 : case 27: nCP = 437; break;
221 0 : case 28: nCP = 863; break;
222 0 : case 29: nCP = 850; break;
223 0 : case 31: nCP = 852; break;
224 0 : case 34: nCP = 852; break;
225 0 : case 35: nCP = 852; break;
226 0 : case 36: nCP = 860; break;
227 0 : case 37: nCP = 850; break;
228 0 : case 38: nCP = 866; break;
229 0 : case 55: nCP = 850; break;
230 0 : case 64: nCP = 852; break;
231 1 : case 77: nCP = 936; break;
232 0 : case 78: nCP = 949; break;
233 0 : case 79: nCP = 950; break;
234 0 : case 80: nCP = 874; break;
235 3637 : case 87: return CPL_ENC_ISO8859_1;
236 0 : case 88: nCP = 1252; break;
237 0 : case 89: nCP = 1252; break;
238 0 : case 100: nCP = 852; break;
239 0 : case 101: nCP = 866; break;
240 0 : case 102: nCP = 865; break;
241 0 : case 103: nCP = 861; break;
242 0 : case 104: nCP = 895; break;
243 0 : case 105: nCP = 620; break;
244 0 : case 106: nCP = 737; break;
245 0 : case 107: nCP = 857; break;
246 0 : case 108: nCP = 863; break;
247 0 : case 120: nCP = 950; break;
248 0 : case 121: nCP = 949; break;
249 0 : case 122: nCP = 936; break;
250 0 : case 123: nCP = 932; break;
251 0 : case 124: nCP = 874; break;
252 0 : case 134: nCP = 737; break;
253 0 : case 135: nCP = 852; break;
254 0 : case 136: nCP = 857; break;
255 0 : case 150: nCP = 10007; break;
256 0 : case 151: nCP = 10029; break;
257 0 : case 200: nCP = 1250; break;
258 0 : case 201: nCP = 1251; break;
259 0 : case 202: nCP = 1254; break;
260 0 : case 203: nCP = 1253; break;
261 0 : case 204: nCP = 1257; break;
262 : default: break;
263 : }
264 :
265 1 : if( nCP != -1 )
266 : {
267 1 : osEncoding.Printf( "CP%d", nCP );
268 1 : return osEncoding;
269 : }
270 : }
271 :
272 : // From the CPG file
273 : // http://resources.arcgis.com/fr/content/kbase?fa=articleShow&d=21106
274 :
275 1 : if( (atoi(pszCodePage) >= 437 && atoi(pszCodePage) <= 950)
276 : || (atoi(pszCodePage) >= 1250 && atoi(pszCodePage) <= 1258) )
277 : {
278 0 : osEncoding.Printf( "CP%d", atoi(pszCodePage) );
279 0 : return osEncoding;
280 : }
281 1 : if( EQUALN(pszCodePage,"8859",4) )
282 : {
283 0 : if( pszCodePage[4] == '-' )
284 0 : osEncoding.Printf( "ISO-8859-%s", pszCodePage + 5 );
285 : else
286 0 : osEncoding.Printf( "ISO-8859-%s", pszCodePage + 4 );
287 0 : return osEncoding;
288 : }
289 1 : if( EQUALN(pszCodePage,"UTF-8",5) )
290 0 : return CPL_ENC_UTF8;
291 :
292 : // try just using the CPG value directly. Works for stuff like Big5.
293 1 : return pszCodePage;
294 : }
295 :
296 : /************************************************************************/
297 : /* CheckForQIX() */
298 : /************************************************************************/
299 :
300 29809 : int OGRShapeLayer::CheckForQIX()
301 :
302 : {
303 : const char *pszQIXFilename;
304 :
305 29809 : if( bCheckedForQIX )
306 28522 : return hQIX != NULL;
307 :
308 1287 : pszQIXFilename = CPLResetExtension( pszFullName, "qix" );
309 :
310 1287 : hQIX = SHPOpenDiskTree( pszQIXFilename, NULL );
311 :
312 1287 : bCheckedForQIX = TRUE;
313 :
314 1287 : return hQIX != NULL;
315 : }
316 :
317 : /************************************************************************/
318 : /* CheckForSBN() */
319 : /************************************************************************/
320 :
321 29756 : int OGRShapeLayer::CheckForSBN()
322 :
323 : {
324 : const char *pszSBNFilename;
325 :
326 29756 : if( bCheckedForSBN )
327 28501 : return hSBN != NULL;
328 :
329 1255 : pszSBNFilename = CPLResetExtension( pszFullName, "sbn" );
330 :
331 1255 : hSBN = SBNOpenDiskTree( pszSBNFilename, NULL );
332 :
333 1255 : bCheckedForSBN = TRUE;
334 :
335 1255 : return hSBN != NULL;
336 : }
337 :
338 : /************************************************************************/
339 : /* ScanIndices() */
340 : /* */
341 : /* Utilize optional spatial and attribute indices if they are */
342 : /* available. */
343 : /************************************************************************/
344 :
345 17931 : int OGRShapeLayer::ScanIndices()
346 :
347 : {
348 17931 : iMatchingFID = 0;
349 :
350 : /* -------------------------------------------------------------------- */
351 : /* Utilize attribute index if appropriate. */
352 : /* -------------------------------------------------------------------- */
353 17931 : if( m_poAttrQuery != NULL )
354 : {
355 400 : CPLAssert( panMatchingFIDs == NULL );
356 :
357 400 : InitializeIndexSupport( pszFullName );
358 :
359 : panMatchingFIDs = m_poAttrQuery->EvaluateAgainstIndices( this,
360 400 : NULL );
361 : }
362 :
363 : /* -------------------------------------------------------------------- */
364 : /* Check for spatial index if we have a spatial query. */
365 : /* -------------------------------------------------------------------- */
366 :
367 17931 : if( m_poFilterGeom == NULL || hSHP == NULL )
368 398 : return TRUE;
369 :
370 17533 : OGREnvelope oSpatialFilterEnvelope;
371 17533 : int bTryQIXorSBN = TRUE;
372 :
373 17533 : m_poFilterGeom->getEnvelope( &oSpatialFilterEnvelope );
374 :
375 17533 : OGREnvelope oLayerExtent;
376 17533 : if (GetExtent(&oLayerExtent, TRUE) == OGRERR_NONE)
377 : {
378 17533 : if (oSpatialFilterEnvelope.Contains(oLayerExtent))
379 : {
380 : // The spatial filter is larger than the layer extent. No use of .qix file for now
381 5 : return TRUE;
382 : }
383 17528 : else if (!oSpatialFilterEnvelope.Intersects(oLayerExtent))
384 : {
385 : /* No intersection : no need to check for .qix or .sbn */
386 11 : bTryQIXorSBN = FALSE;
387 :
388 : /* Set an empty result for spatial FIDs */
389 11 : free(panSpatialFIDs);
390 11 : panSpatialFIDs = (int*) calloc(1, sizeof(int));
391 11 : nSpatialFIDCount = 0;
392 :
393 11 : delete m_poFilterGeomLastValid;
394 11 : m_poFilterGeomLastValid = m_poFilterGeom->clone();
395 : }
396 : }
397 :
398 17528 : if( bTryQIXorSBN )
399 : {
400 17517 : if( !bCheckedForQIX )
401 32 : CheckForQIX();
402 17517 : if( hQIX == NULL && !bCheckedForSBN )
403 24 : CheckForSBN();
404 : }
405 :
406 : /* -------------------------------------------------------------------- */
407 : /* Compute spatial index if appropriate. */
408 : /* -------------------------------------------------------------------- */
409 17528 : if( bTryQIXorSBN && (hQIX != NULL || hSBN != NULL) && panSpatialFIDs == NULL )
410 : {
411 : double adfBoundsMin[4], adfBoundsMax[4];
412 :
413 17480 : adfBoundsMin[0] = oSpatialFilterEnvelope.MinX;
414 17480 : adfBoundsMin[1] = oSpatialFilterEnvelope.MinY;
415 17480 : adfBoundsMin[2] = 0.0;
416 17480 : adfBoundsMin[3] = 0.0;
417 17480 : adfBoundsMax[0] = oSpatialFilterEnvelope.MaxX;
418 17480 : adfBoundsMax[1] = oSpatialFilterEnvelope.MaxY;
419 17480 : adfBoundsMax[2] = 0.0;
420 17480 : adfBoundsMax[3] = 0.0;
421 :
422 17480 : if( hQIX != NULL )
423 : panSpatialFIDs = SHPSearchDiskTreeEx( hQIX,
424 : adfBoundsMin, adfBoundsMax,
425 12316 : &nSpatialFIDCount );
426 : else
427 : panSpatialFIDs = SBNSearchDiskTree( hSBN,
428 : adfBoundsMin, adfBoundsMax,
429 5164 : &nSpatialFIDCount );
430 :
431 : CPLDebug( "SHAPE", "Used spatial index, got %d matches.",
432 17480 : nSpatialFIDCount );
433 :
434 17480 : delete m_poFilterGeomLastValid;
435 17480 : m_poFilterGeomLastValid = m_poFilterGeom->clone();
436 : }
437 :
438 : /* -------------------------------------------------------------------- */
439 : /* Use spatial index if appropriate. */
440 : /* -------------------------------------------------------------------- */
441 17528 : if( panSpatialFIDs != NULL )
442 : {
443 : // Use resulting list as matching FID list (but reallocate and
444 : // terminate with OGRNullFID).
445 :
446 17494 : if( panMatchingFIDs == NULL )
447 : {
448 : int i;
449 :
450 : panMatchingFIDs = (long *)
451 17492 : CPLMalloc(sizeof(long) * (nSpatialFIDCount+1) );
452 274116 : for( i = 0; i < nSpatialFIDCount; i++ )
453 256624 : panMatchingFIDs[i] = (long) panSpatialFIDs[i];
454 17492 : panMatchingFIDs[nSpatialFIDCount] = OGRNullFID;
455 : }
456 :
457 : // Cull attribute index matches based on those in the spatial index
458 : // result set. We assume that the attribute results are in sorted
459 : // order.
460 : else
461 : {
462 2 : int iRead, iWrite=0, iSpatial=0;
463 :
464 4 : for( iRead = 0; panMatchingFIDs[iRead] != OGRNullFID; iRead++ )
465 : {
466 23 : while( iSpatial < nSpatialFIDCount
467 14 : && panSpatialFIDs[iSpatial] < panMatchingFIDs[iRead] )
468 5 : iSpatial++;
469 :
470 2 : if( iSpatial == nSpatialFIDCount )
471 0 : continue;
472 :
473 2 : if( panSpatialFIDs[iSpatial] == panMatchingFIDs[iRead] )
474 2 : panMatchingFIDs[iWrite++] = panMatchingFIDs[iRead];
475 : }
476 2 : panMatchingFIDs[iWrite] = OGRNullFID;
477 : }
478 :
479 17494 : if (nSpatialFIDCount > 100000)
480 : {
481 0 : ClearSpatialFIDs();
482 : }
483 : }
484 :
485 17528 : return TRUE;
486 : }
487 :
488 : /************************************************************************/
489 : /* ResetReading() */
490 : /************************************************************************/
491 :
492 37723 : void OGRShapeLayer::ResetReading()
493 :
494 : {
495 37723 : if (!TouchLayer())
496 0 : return;
497 :
498 37723 : iMatchingFID = 0;
499 :
500 37723 : iNextShapeId = 0;
501 :
502 37723 : if( bHeaderDirty && bUpdateAccess )
503 15 : SyncToDisk();
504 : }
505 :
506 : /************************************************************************/
507 : /* ClearMatchingFIDs() */
508 : /************************************************************************/
509 :
510 23044 : void OGRShapeLayer::ClearMatchingFIDs()
511 : {
512 : /* -------------------------------------------------------------------- */
513 : /* Clear previous index search result, if any. */
514 : /* -------------------------------------------------------------------- */
515 23044 : CPLFree( panMatchingFIDs );
516 23044 : panMatchingFIDs = NULL;
517 23044 : }
518 :
519 : /************************************************************************/
520 : /* ClearSpatialFIDs() */
521 : /************************************************************************/
522 :
523 21375 : void OGRShapeLayer::ClearSpatialFIDs()
524 : {
525 21375 : if ( panSpatialFIDs != NULL )
526 : {
527 17488 : CPLDebug("SHAPE", "Clear panSpatialFIDs");
528 17488 : free( panSpatialFIDs );
529 : }
530 21375 : panSpatialFIDs = NULL;
531 21375 : nSpatialFIDCount = 0;
532 :
533 21375 : delete m_poFilterGeomLastValid;
534 21375 : m_poFilterGeomLastValid = NULL;
535 21375 : }
536 :
537 : /************************************************************************/
538 : /* SetSpatialFilter() */
539 : /************************************************************************/
540 :
541 18197 : void OGRShapeLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
542 : {
543 18197 : ClearMatchingFIDs();
544 :
545 18197 : if( poGeomIn == NULL )
546 : {
547 : /* Do nothing */
548 : }
549 34995 : else if ( m_poFilterGeomLastValid != NULL &&
550 17467 : m_poFilterGeomLastValid->Equals(poGeomIn) )
551 : {
552 : /* Do nothing */
553 : }
554 17522 : else if ( panSpatialFIDs != NULL )
555 : {
556 : /* We clear the spatialFIDs only if we have a new non-NULL */
557 : /* spatial filter, otherwise we keep the previous result */
558 : /* cached. This can be usefull when several SQL layers */
559 : /* rely on the same table layer, and use the same spatial */
560 : /* filters. But as there is in the destructor of OGRGenSQLResultsLayer */
561 : /* a clearing of the spatial filter of the table layer, we */
562 : /* need this trick. */
563 17461 : ClearSpatialFIDs();
564 : }
565 :
566 18197 : return OGRLayer::SetSpatialFilter(poGeomIn);
567 : }
568 :
569 : /************************************************************************/
570 : /* SetAttributeFilter() */
571 : /************************************************************************/
572 :
573 939 : OGRErr OGRShapeLayer::SetAttributeFilter( const char * pszAttributeFilter )
574 : {
575 939 : ClearMatchingFIDs();
576 :
577 939 : return OGRLayer::SetAttributeFilter(pszAttributeFilter);
578 : }
579 :
580 : /************************************************************************/
581 : /* SetNextByIndex() */
582 : /* */
583 : /* If we already have an FID list, we can easily resposition */
584 : /* ourselves in it. */
585 : /************************************************************************/
586 :
587 22 : OGRErr OGRShapeLayer::SetNextByIndex( long nIndex )
588 :
589 : {
590 22 : if (!TouchLayer())
591 0 : return OGRERR_FAILURE;
592 :
593 22 : if( nIndex < 0 )
594 5 : return OGRERR_FAILURE;
595 :
596 : // Eventually we should try to use panMatchingFIDs list
597 : // if available and appropriate.
598 17 : if( m_poFilterGeom != NULL || m_poAttrQuery != NULL )
599 0 : return OGRLayer::SetNextByIndex( nIndex );
600 :
601 17 : iNextShapeId = nIndex;
602 :
603 17 : return OGRERR_NONE;
604 : }
605 :
606 : /************************************************************************/
607 : /* FetchShape() */
608 : /* */
609 : /* Take a shape id, a geometry, and a feature, and set the feature */
610 : /* if the shapeid bbox intersects the geometry. */
611 : /************************************************************************/
612 :
613 169539 : OGRFeature *OGRShapeLayer::FetchShape(int iShapeId /*, OGREnvelope* psShapeExtent */)
614 :
615 : {
616 : OGRFeature *poFeature;
617 :
618 297738 : if (m_poFilterGeom != NULL && hSHP != NULL )
619 : {
620 : SHPObject *psShape;
621 :
622 128199 : psShape = SHPReadObject( hSHP, iShapeId );
623 :
624 : // do not trust degenerate bounds on non-point geometries
625 : // or bounds on null shapes.
626 128238 : if( psShape == NULL
627 : || (psShape->nSHPType != SHPT_POINT
628 : && psShape->nSHPType != SHPT_POINTZ
629 : && psShape->nSHPType != SHPT_POINTM
630 : && (psShape->dfXMin == psShape->dfXMax
631 : || psShape->dfYMin == psShape->dfYMax))
632 : || psShape->nSHPType == SHPT_NULL )
633 : {
634 : poFeature = SHPReadOGRFeature( hSHP, hDBF, poFeatureDefn,
635 39 : iShapeId, psShape, osEncoding );
636 : }
637 219098 : else if( m_sFilterEnvelope.MaxX < psShape->dfXMin
638 : || m_sFilterEnvelope.MaxY < psShape->dfYMin
639 : || psShape->dfXMax < m_sFilterEnvelope.MinX
640 : || psShape->dfYMax < m_sFilterEnvelope.MinY )
641 : {
642 90938 : SHPDestroyObject(psShape);
643 90938 : poFeature = NULL;
644 : }
645 : else
646 : {
647 : /*psShapeExtent->MinX = psShape->dfXMin;
648 : psShapeExtent->MinY = psShape->dfYMin;
649 : psShapeExtent->MaxX = psShape->dfXMax;
650 : psShapeExtent->MaxY = psShape->dfYMax;*/
651 : poFeature = SHPReadOGRFeature( hSHP, hDBF, poFeatureDefn,
652 37222 : iShapeId, psShape, osEncoding );
653 : }
654 : }
655 : else
656 : {
657 : poFeature = SHPReadOGRFeature( hSHP, hDBF, poFeatureDefn,
658 41340 : iShapeId, NULL, osEncoding );
659 : }
660 :
661 169539 : return poFeature;
662 : }
663 :
664 : /************************************************************************/
665 : /* GetNextFeature() */
666 : /************************************************************************/
667 :
668 74798 : OGRFeature *OGRShapeLayer::GetNextFeature()
669 :
670 : {
671 74798 : if (!TouchLayer())
672 0 : return NULL;
673 :
674 74798 : OGRFeature *poFeature = NULL;
675 :
676 : /* -------------------------------------------------------------------- */
677 : /* Collect a matching list if we have attribute or spatial */
678 : /* indices. Only do this on the first request for a given pass */
679 : /* of course. */
680 : /* -------------------------------------------------------------------- */
681 74798 : if( (m_poAttrQuery != NULL || m_poFilterGeom != NULL)
682 : && iNextShapeId == 0 && panMatchingFIDs == NULL )
683 : {
684 17883 : ScanIndices();
685 : }
686 :
687 : /* -------------------------------------------------------------------- */
688 : /* Loop till we find a feature matching our criteria. */
689 : /* -------------------------------------------------------------------- */
690 95409 : while( TRUE )
691 : {
692 : //OGREnvelope oShapeExtent;
693 :
694 170207 : if( panMatchingFIDs != NULL )
695 : {
696 125042 : if( panMatchingFIDs[iMatchingFID] == OGRNullFID )
697 : {
698 19 : return NULL;
699 : }
700 :
701 : // Check the shape object's geometry, and if it matches
702 : // any spatial filter, return it.
703 125023 : poFeature = FetchShape(panMatchingFIDs[iMatchingFID] /*, &oShapeExtent*/);
704 :
705 125023 : iMatchingFID++;
706 :
707 : }
708 : else
709 : {
710 45165 : if( iNextShapeId >= nTotalShapeCount )
711 : {
712 642 : return NULL;
713 : }
714 :
715 44523 : if( hDBF )
716 : {
717 44496 : if (DBFIsRecordDeleted( hDBF, iNextShapeId ))
718 7 : poFeature = NULL;
719 44489 : else if( VSIFEofL((VSILFILE*)hDBF->fp) )
720 0 : return NULL; /* There's an I/O error */
721 : else
722 44489 : poFeature = FetchShape(iNextShapeId /*, &oShapeExtent */);
723 : }
724 : else
725 27 : poFeature = FetchShape(iNextShapeId /*, &oShapeExtent */);
726 :
727 44523 : iNextShapeId++;
728 : }
729 :
730 169546 : if( poFeature != NULL )
731 : {
732 78601 : OGRGeometry* poGeom = poFeature->GetGeometryRef();
733 78601 : if( poGeom != NULL )
734 : {
735 75296 : poGeom->assignSpatialReference( GetSpatialRef() );
736 : }
737 :
738 78601 : m_nFeaturesRead++;
739 :
740 78601 : if( (m_poFilterGeom == NULL || FilterGeometry( poGeom /*, &oShapeExtent*/ ) )
741 : && (m_poAttrQuery == NULL || m_poAttrQuery->Evaluate( poFeature )) )
742 : {
743 74137 : return poFeature;
744 : }
745 :
746 4464 : delete poFeature;
747 : }
748 : }
749 : }
750 :
751 : /************************************************************************/
752 : /* GetFeature() */
753 : /************************************************************************/
754 :
755 81 : OGRFeature *OGRShapeLayer::GetFeature( long nFeatureId )
756 :
757 : {
758 81 : if (!TouchLayer())
759 0 : return NULL;
760 :
761 81 : OGRFeature *poFeature = NULL;
762 : poFeature = SHPReadOGRFeature( hSHP, hDBF, poFeatureDefn, nFeatureId, NULL,
763 81 : osEncoding );
764 :
765 81 : if( poFeature != NULL )
766 : {
767 69 : if( poFeature->GetGeometryRef() != NULL )
768 : {
769 64 : poFeature->GetGeometryRef()->assignSpatialReference( GetSpatialRef() );
770 : }
771 :
772 69 : m_nFeaturesRead++;
773 :
774 69 : return poFeature;
775 : }
776 :
777 : /*
778 : * Reading shape feature failed.
779 : */
780 12 : return NULL;
781 : }
782 :
783 : /************************************************************************/
784 : /* SetFeature() */
785 : /************************************************************************/
786 :
787 30 : OGRErr OGRShapeLayer::SetFeature( OGRFeature *poFeature )
788 :
789 : {
790 30 : if (!TouchLayer())
791 0 : return OGRERR_FAILURE;
792 :
793 30 : if( !bUpdateAccess )
794 : {
795 : CPLError( CE_Failure, CPLE_NotSupported,
796 : UNSUPPORTED_OP_READ_ONLY,
797 1 : "SetFeature");
798 1 : return OGRERR_FAILURE;
799 : }
800 :
801 29 : long nFID = poFeature->GetFID();
802 29 : if( nFID < 0
803 : || (hSHP != NULL && nFID >= hSHP->nRecords)
804 : || (hDBF != NULL && nFID >= hDBF->nRecords) )
805 : {
806 : CPLError( CE_Failure, CPLE_AppDefined,
807 : "Attempt to set shape with feature id (%ld) which does "
808 2 : "not exist.", nFID );
809 2 : return OGRERR_FAILURE;
810 : }
811 :
812 27 : bHeaderDirty = TRUE;
813 27 : if( CheckForQIX() || CheckForSBN() )
814 1 : DropSpatialIndex();
815 :
816 : return SHPWriteOGRFeature( hSHP, hDBF, poFeatureDefn, poFeature,
817 27 : osEncoding, &bTruncationWarningEmitted );
818 : }
819 :
820 : /************************************************************************/
821 : /* DeleteFeature() */
822 : /************************************************************************/
823 :
824 11 : OGRErr OGRShapeLayer::DeleteFeature( long nFID )
825 :
826 : {
827 11 : if (!TouchLayer())
828 0 : return OGRERR_FAILURE;
829 :
830 11 : if( !bUpdateAccess )
831 : {
832 : CPLError( CE_Failure, CPLE_NotSupported,
833 : UNSUPPORTED_OP_READ_ONLY,
834 1 : "DeleteFeature");
835 1 : return OGRERR_FAILURE;
836 : }
837 :
838 10 : if( nFID < 0
839 : || (hSHP != NULL && nFID >= hSHP->nRecords)
840 : || (hDBF != NULL && nFID >= hDBF->nRecords) )
841 : {
842 : CPLError( CE_Failure, CPLE_AppDefined,
843 : "Attempt to delete shape with feature id (%ld) which does "
844 1 : "not exist.", nFID );
845 1 : return OGRERR_FAILURE;
846 : }
847 :
848 9 : if( !hDBF )
849 : {
850 : CPLError( CE_Failure, CPLE_AppDefined,
851 : "Attempt to delete shape in shapefile with no .dbf file.\n"
852 : "Deletion is done by marking record deleted in dbf\n"
853 1 : "and is not supported without a .dbf file." );
854 1 : return OGRERR_FAILURE;
855 : }
856 :
857 8 : if( DBFIsRecordDeleted( hDBF, nFID ) )
858 : {
859 : CPLError( CE_Failure, CPLE_AppDefined,
860 : "Attempt to delete shape with feature id (%ld), but it is marked deleted already.",
861 1 : nFID );
862 1 : return OGRERR_FAILURE;
863 : }
864 :
865 7 : if( !DBFMarkRecordDeleted( hDBF, nFID, TRUE ) )
866 0 : return OGRERR_FAILURE;
867 :
868 7 : bHeaderDirty = TRUE;
869 7 : if( CheckForQIX() || CheckForSBN() )
870 1 : DropSpatialIndex();
871 :
872 7 : return OGRERR_NONE;
873 : }
874 :
875 : /************************************************************************/
876 : /* CreateFeature() */
877 : /************************************************************************/
878 :
879 29699 : OGRErr OGRShapeLayer::CreateFeature( OGRFeature *poFeature )
880 :
881 : {
882 : OGRErr eErr;
883 :
884 29699 : if (!TouchLayer())
885 0 : return OGRERR_FAILURE;
886 :
887 29699 : if( !bUpdateAccess )
888 : {
889 : CPLError( CE_Failure, CPLE_NotSupported,
890 : UNSUPPORTED_OP_READ_ONLY,
891 1 : "CreateFeature");
892 1 : return OGRERR_FAILURE;
893 : }
894 :
895 29698 : bHeaderDirty = TRUE;
896 29698 : if( CheckForQIX() || CheckForSBN() )
897 2 : DropSpatialIndex();
898 :
899 29698 : poFeature->SetFID( OGRNullFID );
900 :
901 29698 : if( nTotalShapeCount == 0
902 : && eRequestedGeomType == wkbUnknown
903 : && poFeature->GetGeometryRef() != NULL )
904 : {
905 574 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
906 : int nShapeType;
907 :
908 574 : switch( poGeom->getGeometryType() )
909 : {
910 : case wkbPoint:
911 520 : nShapeType = SHPT_POINT;
912 520 : eRequestedGeomType = wkbPoint;
913 520 : break;
914 :
915 : case wkbPoint25D:
916 3 : nShapeType = SHPT_POINTZ;
917 3 : eRequestedGeomType = wkbPoint25D;
918 3 : break;
919 :
920 : case wkbMultiPoint:
921 4 : nShapeType = SHPT_MULTIPOINT;
922 4 : eRequestedGeomType = wkbMultiPoint;
923 4 : break;
924 :
925 : case wkbMultiPoint25D:
926 2 : nShapeType = SHPT_MULTIPOINTZ;
927 2 : eRequestedGeomType = wkbMultiPoint25D;
928 2 : break;
929 :
930 : case wkbLineString:
931 : case wkbMultiLineString:
932 8 : nShapeType = SHPT_ARC;
933 8 : eRequestedGeomType = wkbLineString;
934 8 : break;
935 :
936 : case wkbLineString25D:
937 : case wkbMultiLineString25D:
938 5 : nShapeType = SHPT_ARCZ;
939 5 : eRequestedGeomType = wkbLineString25D;
940 5 : break;
941 :
942 : case wkbPolygon:
943 : case wkbMultiPolygon:
944 28 : nShapeType = SHPT_POLYGON;
945 28 : eRequestedGeomType = wkbPolygon;
946 28 : break;
947 :
948 : case wkbPolygon25D:
949 : case wkbMultiPolygon25D:
950 4 : nShapeType = SHPT_POLYGONZ;
951 4 : eRequestedGeomType = wkbPolygon25D;
952 4 : break;
953 :
954 : default:
955 0 : nShapeType = -1;
956 : break;
957 : }
958 :
959 574 : if( nShapeType != -1 )
960 : {
961 574 : ResetGeomType( nShapeType );
962 : }
963 : }
964 :
965 : eErr = SHPWriteOGRFeature( hSHP, hDBF, poFeatureDefn, poFeature,
966 29698 : osEncoding, &bTruncationWarningEmitted );
967 :
968 29698 : if( hSHP != NULL )
969 29671 : nTotalShapeCount = hSHP->nRecords;
970 : else
971 27 : nTotalShapeCount = hDBF->nRecords;
972 :
973 29698 : return eErr;
974 : }
975 :
976 : /************************************************************************/
977 : /* GetFeatureCountWithSpatialFilterOnly() */
978 : /* */
979 : /* Specialized implementation of GetFeatureCount() when there is *only* */
980 : /* a spatial filter and no attribute filter. */
981 : /************************************************************************/
982 :
983 48 : int OGRShapeLayer::GetFeatureCountWithSpatialFilterOnly()
984 :
985 : {
986 : /* -------------------------------------------------------------------- */
987 : /* Collect a matching list if we have attribute or spatial */
988 : /* indices. Only do this on the first request for a given pass */
989 : /* of course. */
990 : /* -------------------------------------------------------------------- */
991 48 : if( panMatchingFIDs == NULL )
992 : {
993 48 : ScanIndices();
994 : }
995 :
996 48 : int nFeatureCount = 0;
997 48 : int iLocalMatchingFID = 0;
998 48 : int iLocalNextShapeId = 0;
999 48 : int bExpectPoints = FALSE;
1000 :
1001 48 : if (wkbFlatten(poFeatureDefn->GetGeomType()) == wkbPoint)
1002 27 : bExpectPoints = TRUE;
1003 :
1004 : /* -------------------------------------------------------------------- */
1005 : /* Loop till we find a feature matching our criteria. */
1006 : /* -------------------------------------------------------------------- */
1007 :
1008 : SHPObject sShape;
1009 48 : memset(&sShape, 0, sizeof(sShape));
1010 :
1011 48 : VSILFILE* fpSHP = (VSILFILE*) hSHP->fpSHP;
1012 :
1013 17622 : while( TRUE )
1014 : {
1015 17670 : SHPObject* psShape = NULL;
1016 17670 : int iShape = -1;
1017 :
1018 17670 : if( panMatchingFIDs != NULL )
1019 : {
1020 17549 : iShape = panMatchingFIDs[iLocalMatchingFID];
1021 17549 : if( iShape == OGRNullFID )
1022 37 : break;
1023 17512 : iLocalMatchingFID++;
1024 : }
1025 : else
1026 : {
1027 121 : if( iLocalNextShapeId >= nTotalShapeCount )
1028 11 : break;
1029 110 : iShape = iLocalNextShapeId ++;
1030 :
1031 110 : if( hDBF )
1032 : {
1033 110 : if (DBFIsRecordDeleted( hDBF, iShape ))
1034 0 : continue;
1035 :
1036 110 : if (VSIFEofL((VSILFILE*)hDBF->fp))
1037 0 : break;
1038 : }
1039 : }
1040 :
1041 : /* Read full shape for point layers */
1042 17622 : if (bExpectPoints)
1043 1120 : psShape = SHPReadObject( hSHP, iShape);
1044 :
1045 : /* -------------------------------------------------------------------- */
1046 : /* Only read feature type and bounding box for now. In case of */
1047 : /* inconclusive tests on bounding box only, we will read the full */
1048 : /* shape later. */
1049 : /* -------------------------------------------------------------------- */
1050 33004 : else if (iShape >= 0 && iShape < hSHP->nRecords &&
1051 16502 : hSHP->panRecSize[iShape] > 4 + 8 * 4 )
1052 : {
1053 : GByte abyBuf[4 + 8 * 4];
1054 16502 : if( VSIFSeekL( fpSHP, hSHP->panRecOffset[iShape] + 8, 0 ) == 0 &&
1055 : VSIFReadL( abyBuf, sizeof(abyBuf), 1, fpSHP ) == 1 )
1056 : {
1057 16502 : memcpy(&(sShape.nSHPType), abyBuf, 4);
1058 : CPL_LSBPTR32(&(sShape.nSHPType));
1059 16502 : if ( sShape.nSHPType != SHPT_NULL &&
1060 : sShape.nSHPType != SHPT_POINT &&
1061 : sShape.nSHPType != SHPT_POINTM &&
1062 : sShape.nSHPType != SHPT_POINTZ)
1063 : {
1064 16502 : psShape = &sShape;
1065 16502 : memcpy(&(sShape.dfXMin), abyBuf + 4, 8);
1066 16502 : memcpy(&(sShape.dfYMin), abyBuf + 12, 8);
1067 16502 : memcpy(&(sShape.dfXMax), abyBuf + 20, 8);
1068 16502 : memcpy(&(sShape.dfYMax), abyBuf + 28, 8);
1069 : CPL_LSBPTR64(&(sShape.dfXMin));
1070 : CPL_LSBPTR64(&(sShape.dfYMin));
1071 : CPL_LSBPTR64(&(sShape.dfXMax));
1072 : CPL_LSBPTR64(&(sShape.dfYMax));
1073 : }
1074 : }
1075 : else
1076 : {
1077 0 : break;
1078 : }
1079 : }
1080 :
1081 35244 : if( psShape != NULL && psShape->nSHPType != SHPT_NULL )
1082 : {
1083 17622 : OGRGeometry* poGeometry = NULL;
1084 17622 : OGREnvelope sGeomEnv;
1085 : /* Test if we have a degenerated bounding box */
1086 17627 : if (psShape->nSHPType != SHPT_POINT
1087 : && psShape->nSHPType != SHPT_POINTZ
1088 : && psShape->nSHPType != SHPT_POINTM
1089 : && (psShape->dfXMin == psShape->dfXMax
1090 : || psShape->dfYMin == psShape->dfYMax))
1091 : {
1092 : /* We need to read the full geometry */
1093 : /* to compute the envelope */
1094 5 : if (psShape == &sShape)
1095 5 : psShape = SHPReadObject( hSHP, iShape);
1096 5 : if (psShape)
1097 : {
1098 5 : poGeometry = SHPReadOGRObject( hSHP, iShape, psShape );
1099 5 : poGeometry->getEnvelope( &sGeomEnv );
1100 5 : psShape = NULL;
1101 : }
1102 : }
1103 : else
1104 : {
1105 : /* Trust the shape bounding box as the shape envelope */
1106 17617 : sGeomEnv.MinX = psShape->dfXMin;
1107 17617 : sGeomEnv.MinY = psShape->dfYMin;
1108 17617 : sGeomEnv.MaxX = psShape->dfXMax;
1109 17617 : sGeomEnv.MaxY = psShape->dfYMax;
1110 : }
1111 :
1112 : /* -------------------------------------------------------------------- */
1113 : /* If there is no */
1114 : /* intersection between the envelopes we are sure not to have */
1115 : /* any intersection. */
1116 : /* -------------------------------------------------------------------- */
1117 17622 : if( sGeomEnv.MaxX < m_sFilterEnvelope.MinX
1118 : || sGeomEnv.MaxY < m_sFilterEnvelope.MinY
1119 : || m_sFilterEnvelope.MaxX < sGeomEnv.MinX
1120 : || m_sFilterEnvelope.MaxY < sGeomEnv.MinY )
1121 : {
1122 : }
1123 : /* -------------------------------------------------------------------- */
1124 : /* If the filter geometry is its own envelope and if the */
1125 : /* envelope of the geometry is inside the filter geometry, */
1126 : /* the geometry itself is inside the filter geometry */
1127 : /* -------------------------------------------------------------------- */
1128 34457 : else if( m_bFilterIsEnvelope &&
1129 : sGeomEnv.MinX >= m_sFilterEnvelope.MinX &&
1130 : sGeomEnv.MinY >= m_sFilterEnvelope.MinY &&
1131 : sGeomEnv.MaxX <= m_sFilterEnvelope.MaxX &&
1132 : sGeomEnv.MaxY <= m_sFilterEnvelope.MaxY)
1133 : {
1134 16949 : nFeatureCount ++;
1135 : }
1136 : else
1137 : {
1138 : /* -------------------------------------------------------------------- */
1139 : /* Fallback to full intersect test (using GEOS) if we still */
1140 : /* don't know for sure. */
1141 : /* -------------------------------------------------------------------- */
1142 559 : if( OGRGeometryFactory::haveGEOS() )
1143 : {
1144 : /* We need to read the full geometry */
1145 559 : if (poGeometry == NULL)
1146 : {
1147 559 : if (psShape == &sShape)
1148 559 : psShape = SHPReadObject( hSHP, iShape);
1149 559 : if (psShape)
1150 : {
1151 : poGeometry =
1152 559 : SHPReadOGRObject( hSHP, iShape, psShape );
1153 559 : psShape = NULL;
1154 : }
1155 : }
1156 559 : if( poGeometry == NULL )
1157 0 : nFeatureCount ++;
1158 559 : else if ( m_pPreparedFilterGeom != NULL )
1159 : {
1160 559 : if( OGRPreparedGeometryIntersects(m_pPreparedFilterGeom,
1161 : poGeometry) )
1162 : {
1163 544 : nFeatureCount ++;
1164 : }
1165 : }
1166 0 : else if( m_poFilterGeom->Intersects( poGeometry ) )
1167 0 : nFeatureCount ++;
1168 : }
1169 : else
1170 0 : nFeatureCount ++;
1171 : }
1172 :
1173 17622 : delete poGeometry;
1174 : }
1175 : else
1176 0 : nFeatureCount ++;
1177 :
1178 17622 : if (psShape && psShape != &sShape)
1179 1120 : SHPDestroyObject( psShape );
1180 : }
1181 :
1182 48 : return nFeatureCount;
1183 : }
1184 :
1185 : /************************************************************************/
1186 : /* GetFeatureCount() */
1187 : /************************************************************************/
1188 :
1189 426 : int OGRShapeLayer::GetFeatureCount( int bForce )
1190 :
1191 : {
1192 : /* Check if the spatial filter is non-trivial */
1193 : int bHasTrivialSpatialFilter;
1194 426 : if (m_poFilterGeom != NULL)
1195 : {
1196 52 : OGREnvelope oSpatialFilterEnvelope;
1197 52 : m_poFilterGeom->getEnvelope( &oSpatialFilterEnvelope );
1198 :
1199 52 : OGREnvelope oLayerExtent;
1200 52 : if (GetExtent(&oLayerExtent, TRUE) == OGRERR_NONE)
1201 : {
1202 52 : if (oSpatialFilterEnvelope.Contains(oLayerExtent))
1203 : {
1204 4 : bHasTrivialSpatialFilter = TRUE;
1205 : }
1206 : else
1207 48 : bHasTrivialSpatialFilter = FALSE;
1208 : }
1209 : else
1210 0 : bHasTrivialSpatialFilter = FALSE;
1211 : }
1212 : else
1213 374 : bHasTrivialSpatialFilter = TRUE;
1214 :
1215 :
1216 426 : if( bHasTrivialSpatialFilter && m_poAttrQuery == NULL )
1217 244 : return nTotalShapeCount;
1218 :
1219 182 : if (!TouchLayer())
1220 0 : return 0;
1221 :
1222 : /* Spatial filter only */
1223 182 : if( m_poAttrQuery == NULL && hSHP != NULL )
1224 : {
1225 48 : return GetFeatureCountWithSpatialFilterOnly();
1226 : }
1227 :
1228 : /* Attribute filter only */
1229 134 : if( m_poAttrQuery != NULL )
1230 : {
1231 : /* Let's see if we can ignore reading geometries */
1232 134 : int bSaveGeometryIgnored = poFeatureDefn->IsGeometryIgnored();
1233 134 : if (!AttributeFilterEvaluationNeedsGeometry())
1234 134 : poFeatureDefn->SetGeometryIgnored(TRUE);
1235 :
1236 134 : int nRet = OGRLayer::GetFeatureCount( bForce );
1237 :
1238 134 : poFeatureDefn->SetGeometryIgnored(bSaveGeometryIgnored);
1239 134 : return nRet;
1240 : }
1241 :
1242 0 : return OGRLayer::GetFeatureCount( bForce );
1243 : }
1244 :
1245 : /************************************************************************/
1246 : /* GetExtent() */
1247 : /* */
1248 : /* Fetch extent of the data currently stored in the dataset. */
1249 : /* The bForce flag has no effect on SHP files since that value */
1250 : /* is always in the header. */
1251 : /* */
1252 : /* Returns OGRERR_NONE/OGRRERR_FAILURE. */
1253 : /************************************************************************/
1254 :
1255 17658 : OGRErr OGRShapeLayer::GetExtent (OGREnvelope *psExtent, int bForce)
1256 :
1257 : {
1258 : UNREFERENCED_PARAM( bForce );
1259 :
1260 17658 : if (!TouchLayer())
1261 0 : return OGRERR_FAILURE;
1262 :
1263 : double adMin[4], adMax[4];
1264 :
1265 17658 : if( hSHP == NULL )
1266 1 : return OGRERR_FAILURE;
1267 :
1268 17657 : SHPGetInfo(hSHP, NULL, NULL, adMin, adMax);
1269 :
1270 17657 : psExtent->MinX = adMin[0];
1271 17657 : psExtent->MinY = adMin[1];
1272 17657 : psExtent->MaxX = adMax[0];
1273 17657 : psExtent->MaxY = adMax[1];
1274 :
1275 17657 : return OGRERR_NONE;
1276 : }
1277 :
1278 : /************************************************************************/
1279 : /* TestCapability() */
1280 : /************************************************************************/
1281 :
1282 4197 : int OGRShapeLayer::TestCapability( const char * pszCap )
1283 :
1284 : {
1285 4197 : if (!TouchLayer())
1286 0 : return FALSE;
1287 :
1288 4197 : if( EQUAL(pszCap,OLCRandomRead) )
1289 9 : return TRUE;
1290 :
1291 4188 : else if( EQUAL(pszCap,OLCSequentialWrite)
1292 : || EQUAL(pszCap,OLCRandomWrite) )
1293 9 : return bUpdateAccess;
1294 :
1295 4179 : else if( EQUAL(pszCap,OLCFastFeatureCount) )
1296 : {
1297 103 : if( !(m_poFilterGeom == NULL || CheckForQIX() || CheckForSBN()) )
1298 0 : return FALSE;
1299 :
1300 103 : if( m_poAttrQuery != NULL )
1301 : {
1302 67 : InitializeIndexSupport( pszFullName );
1303 67 : return m_poAttrQuery->CanUseIndex(this);
1304 : }
1305 36 : return TRUE;
1306 : }
1307 :
1308 4076 : else if( EQUAL(pszCap,OLCDeleteFeature) )
1309 2 : return bUpdateAccess;
1310 :
1311 4074 : else if( EQUAL(pszCap,OLCFastSpatialFilter) )
1312 6 : return CheckForQIX() || CheckForSBN();
1313 :
1314 4068 : else if( EQUAL(pszCap,OLCFastGetExtent) )
1315 6 : return TRUE;
1316 :
1317 4062 : else if( EQUAL(pszCap,OLCFastSetNextByIndex) )
1318 18 : return m_poFilterGeom == NULL && m_poAttrQuery == NULL;
1319 :
1320 4044 : else if( EQUAL(pszCap,OLCCreateField) )
1321 1 : return bUpdateAccess;
1322 :
1323 4043 : else if( EQUAL(pszCap,OLCDeleteField) )
1324 2 : return bUpdateAccess;
1325 :
1326 4041 : else if( EQUAL(pszCap,OLCReorderFields) )
1327 2 : return bUpdateAccess;
1328 :
1329 4039 : else if( EQUAL(pszCap,OLCAlterFieldDefn) )
1330 2 : return bUpdateAccess;
1331 :
1332 4037 : else if( EQUAL(pszCap,OLCIgnoreFields) )
1333 380 : return TRUE;
1334 :
1335 3657 : else if( EQUAL(pszCap,OLCStringsAsUTF8) )
1336 : {
1337 : /* No encoding defined : we don't know */
1338 3654 : if( osEncoding.size() == 0)
1339 4 : return FALSE;
1340 :
1341 3650 : if( hDBF == NULL || DBFGetFieldCount( hDBF ) == 0 )
1342 1224 : return TRUE;
1343 :
1344 2426 : CPLClearRecodeWarningFlags();
1345 :
1346 : /* Otherwise test that we can re-encode field names to UTF-8 */
1347 2426 : int nFieldCount = DBFGetFieldCount( hDBF );
1348 5525 : for(int i=0;i<nFieldCount;i++)
1349 : {
1350 : char szFieldName[20];
1351 : int nWidth, nPrecision;
1352 :
1353 : DBFGetFieldInfo( hDBF, i, szFieldName,
1354 3100 : &nWidth, &nPrecision );
1355 :
1356 3100 : CPLErrorReset();
1357 3100 : CPLPushErrorHandler(CPLQuietErrorHandler);
1358 : char *pszUTF8Field = CPLRecode( szFieldName,
1359 3100 : osEncoding, CPL_ENC_UTF8);
1360 3100 : CPLPopErrorHandler();
1361 3100 : CPLFree( pszUTF8Field );
1362 :
1363 3100 : if (CPLGetLastErrorType() != 0)
1364 : {
1365 1 : return FALSE;
1366 : }
1367 : }
1368 :
1369 2425 : return TRUE;
1370 : }
1371 : else
1372 3 : return FALSE;
1373 : }
1374 :
1375 : /************************************************************************/
1376 : /* CreateField() */
1377 : /************************************************************************/
1378 :
1379 4354 : OGRErr OGRShapeLayer::CreateField( OGRFieldDefn *poFieldDefn, int bApproxOK )
1380 :
1381 : {
1382 4354 : if (!TouchLayer())
1383 0 : return OGRERR_FAILURE;
1384 :
1385 4354 : CPLAssert( NULL != poFieldDefn );
1386 :
1387 : int iNewField;
1388 :
1389 4354 : if( !bUpdateAccess )
1390 : {
1391 : CPLError( CE_Failure, CPLE_NotSupported,
1392 : UNSUPPORTED_OP_READ_ONLY,
1393 1 : "CreateField");
1394 1 : return OGRERR_FAILURE;
1395 :
1396 : }
1397 :
1398 4353 : int bDBFJustCreated = FALSE;
1399 4353 : if( hDBF == NULL )
1400 : {
1401 1 : CPLString osFilename = CPLResetExtension( pszFullName, "dbf" );
1402 1 : hDBF = DBFCreate( osFilename );
1403 :
1404 1 : if( hDBF == NULL )
1405 : {
1406 : CPLError( CE_Failure, CPLE_OpenFailed,
1407 : "Failed to create DBF file `%s'.\n",
1408 0 : osFilename.c_str() );
1409 0 : return OGRERR_FAILURE;
1410 : }
1411 :
1412 1 : bDBFJustCreated = TRUE;
1413 : }
1414 :
1415 4353 : CPLErrorReset();
1416 :
1417 4353 : if ( poFeatureDefn->GetFieldCount() == 255 )
1418 : {
1419 : CPLError( CE_Warning, CPLE_AppDefined,
1420 2 : "Creating a 256th field, but some DBF readers might only support 255 fields" );
1421 : }
1422 4353 : if ( hDBF->nHeaderLength + 32 > 65535 )
1423 : {
1424 : CPLError( CE_Failure, CPLE_NotSupported,
1425 1 : "Cannot add more fields in DBF file.");
1426 1 : return OGRERR_FAILURE;
1427 : }
1428 :
1429 : /* -------------------------------------------------------------------- */
1430 : /* Normalize field name */
1431 : /* -------------------------------------------------------------------- */
1432 :
1433 : char szNewFieldName[10 + 1];
1434 4352 : char * pszTmp = NULL;
1435 4352 : int nRenameNum = 1;
1436 :
1437 4352 : CPLString osFieldName;
1438 4352 : if( osEncoding.size() )
1439 : {
1440 4351 : CPLClearRecodeWarningFlags();
1441 4351 : CPLPushErrorHandler(CPLQuietErrorHandler);
1442 4351 : CPLErr eLastErr = CPLGetLastErrorType();
1443 4351 : char* pszRecoded = CPLRecode( poFieldDefn->GetNameRef(), CPL_ENC_UTF8, osEncoding);
1444 4351 : CPLPopErrorHandler();
1445 4351 : osFieldName = pszRecoded;
1446 4351 : CPLFree(pszRecoded);
1447 4351 : if( CPLGetLastErrorType() != eLastErr )
1448 : {
1449 : CPLError(CE_Failure, CPLE_AppDefined,
1450 : "Failed to create field name '%s' : cannot convert to %s",
1451 1 : poFieldDefn->GetNameRef(), osEncoding.c_str());
1452 1 : return OGRERR_FAILURE;
1453 : }
1454 : }
1455 : else
1456 1 : osFieldName = poFieldDefn->GetNameRef();
1457 :
1458 4351 : size_t nNameSize = osFieldName.size();
1459 : pszTmp = CPLScanString( osFieldName,
1460 4351 : MIN( nNameSize, 10) , TRUE, TRUE);
1461 4351 : strncpy(szNewFieldName, pszTmp, 10);
1462 4351 : szNewFieldName[10] = '\0';
1463 :
1464 4351 : if( !bApproxOK &&
1465 : ( DBFGetFieldIndex( hDBF, szNewFieldName ) >= 0 ||
1466 : !EQUAL(osFieldName,szNewFieldName) ) )
1467 : {
1468 : CPLError( CE_Failure, CPLE_NotSupported,
1469 : "Failed to add field named '%s'",
1470 0 : poFieldDefn->GetNameRef() );
1471 :
1472 0 : CPLFree( pszTmp );
1473 0 : return OGRERR_FAILURE;
1474 : }
1475 :
1476 8879 : while( DBFGetFieldIndex( hDBF, szNewFieldName ) >= 0 && nRenameNum < 10 )
1477 177 : sprintf( szNewFieldName, "%.8s_%.1d", pszTmp, nRenameNum++ );
1478 8707 : while( DBFGetFieldIndex( hDBF, szNewFieldName ) >= 0 && nRenameNum < 100 )
1479 5 : sprintf( szNewFieldName, "%.8s%.2d", pszTmp, nRenameNum++ );
1480 :
1481 4351 : CPLFree( pszTmp );
1482 4351 : pszTmp = NULL;
1483 :
1484 4351 : if( DBFGetFieldIndex( hDBF, szNewFieldName ) >= 0 )
1485 : {
1486 : CPLError( CE_Failure, CPLE_NotSupported,
1487 : "Too many field names like '%s' when truncated to 10 letters "
1488 : "for Shapefile format.",
1489 0 : poFieldDefn->GetNameRef() );//One hundred similar field names!!?
1490 : }
1491 :
1492 4351 : OGRFieldDefn oModFieldDefn(poFieldDefn);
1493 :
1494 4351 : if( !EQUAL(osFieldName,szNewFieldName) )
1495 : {
1496 : CPLError( CE_Warning, CPLE_NotSupported,
1497 : "Normalized/laundered field name: '%s' to '%s'",
1498 : poFieldDefn->GetNameRef(),
1499 46 : szNewFieldName );
1500 :
1501 : // Set field name with normalized value
1502 46 : oModFieldDefn.SetName(szNewFieldName);
1503 : }
1504 :
1505 : /* -------------------------------------------------------------------- */
1506 : /* Add field to layer */
1507 : /* -------------------------------------------------------------------- */
1508 :
1509 4351 : char chType = 'C';
1510 4351 : int nWidth = 0;
1511 4351 : int nDecimals = 0;
1512 :
1513 4351 : switch( oModFieldDefn.GetType() )
1514 : {
1515 : case OFTInteger:
1516 2093 : chType = 'N';
1517 2093 : nWidth = oModFieldDefn.GetWidth();
1518 2093 : if (nWidth == 0) nWidth = 10;
1519 2093 : break;
1520 :
1521 : case OFTReal:
1522 148 : chType = 'N';
1523 148 : nWidth = oModFieldDefn.GetWidth();
1524 148 : nDecimals = oModFieldDefn.GetPrecision();
1525 148 : if (nWidth == 0)
1526 : {
1527 8 : nWidth = 24;
1528 8 : nDecimals = 15;
1529 : }
1530 148 : break;
1531 :
1532 : case OFTString:
1533 2109 : chType = 'C';
1534 2109 : nWidth = oModFieldDefn.GetWidth();
1535 2109 : if (nWidth == 0) nWidth = 80;
1536 80 : else if (nWidth > 255)
1537 : {
1538 : CPLError( CE_Warning, CPLE_AppDefined,
1539 : "Field %s of width %d truncated to %d.",
1540 1 : szNewFieldName, nWidth, 255 );
1541 1 : nWidth = 255;
1542 : }
1543 2109 : break;
1544 :
1545 : case OFTDate:
1546 1 : chType = 'D';
1547 1 : nWidth = 8;
1548 1 : break;
1549 :
1550 : case OFTDateTime:
1551 : CPLError( CE_Warning, CPLE_NotSupported,
1552 : "Field %s create as date field, though DateTime requested.",
1553 0 : szNewFieldName );
1554 0 : chType = 'D';
1555 0 : nWidth = 8;
1556 0 : oModFieldDefn.SetType( OFTDate );
1557 0 : break;
1558 :
1559 : default:
1560 : CPLError( CE_Failure, CPLE_NotSupported,
1561 : "Can't create fields of type %s on shapefile layers.",
1562 0 : OGRFieldDefn::GetFieldTypeName(oModFieldDefn.GetType()) );
1563 :
1564 0 : return OGRERR_FAILURE;
1565 : break;
1566 : }
1567 :
1568 4351 : oModFieldDefn.SetWidth( nWidth );
1569 4351 : oModFieldDefn.SetPrecision( nDecimals );
1570 :
1571 4351 : if ( hDBF->nRecordLength + nWidth > 65535 )
1572 : {
1573 : CPLError( CE_Failure, CPLE_NotSupported,
1574 : "Can't create field %s in Shape DBF file. "
1575 : "Maximum record length reached.",
1576 1 : szNewFieldName );
1577 1 : return OGRERR_FAILURE;
1578 : }
1579 :
1580 : iNewField =
1581 : DBFAddNativeFieldType( hDBF, szNewFieldName,
1582 4350 : chType, nWidth, nDecimals );
1583 :
1584 4350 : if( iNewField != -1 )
1585 : {
1586 4350 : poFeatureDefn->AddFieldDefn( &oModFieldDefn );
1587 :
1588 4350 : if( bDBFJustCreated )
1589 : {
1590 2 : for(int i=0;i<nTotalShapeCount;i++)
1591 : {
1592 1 : DBFWriteNULLAttribute( hDBF, i, 0 );
1593 : }
1594 : }
1595 :
1596 4350 : return OGRERR_NONE;
1597 : }
1598 : else
1599 : {
1600 : CPLError( CE_Failure, CPLE_AppDefined,
1601 : "Can't create field %s in Shape DBF file, reason unknown.",
1602 0 : szNewFieldName );
1603 :
1604 0 : return OGRERR_FAILURE;
1605 0 : }
1606 : }
1607 :
1608 : /************************************************************************/
1609 : /* DeleteField() */
1610 : /************************************************************************/
1611 :
1612 10 : OGRErr OGRShapeLayer::DeleteField( int iField )
1613 : {
1614 10 : if (!TouchLayer())
1615 0 : return OGRERR_FAILURE;
1616 :
1617 10 : if( !bUpdateAccess )
1618 : {
1619 : CPLError( CE_Failure, CPLE_NotSupported,
1620 : UNSUPPORTED_OP_READ_ONLY,
1621 1 : "DeleteField");
1622 1 : return OGRERR_FAILURE;
1623 : }
1624 :
1625 9 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
1626 : {
1627 : CPLError( CE_Failure, CPLE_NotSupported,
1628 3 : "Invalid field index");
1629 3 : return OGRERR_FAILURE;
1630 : }
1631 :
1632 6 : if ( DBFDeleteField( hDBF, iField ) )
1633 : {
1634 6 : TruncateDBF();
1635 :
1636 6 : return poFeatureDefn->DeleteFieldDefn( iField );
1637 : }
1638 : else
1639 0 : return OGRERR_FAILURE;
1640 : }
1641 :
1642 : /************************************************************************/
1643 : /* ReorderFields() */
1644 : /************************************************************************/
1645 :
1646 15 : OGRErr OGRShapeLayer::ReorderFields( int* panMap )
1647 : {
1648 15 : if (!TouchLayer())
1649 0 : return OGRERR_FAILURE;
1650 :
1651 15 : if( !bUpdateAccess )
1652 : {
1653 : CPLError( CE_Failure, CPLE_NotSupported,
1654 : UNSUPPORTED_OP_READ_ONLY,
1655 1 : "ReorderFields");
1656 1 : return OGRERR_FAILURE;
1657 : }
1658 :
1659 14 : if (poFeatureDefn->GetFieldCount() == 0)
1660 2 : return OGRERR_NONE;
1661 :
1662 12 : OGRErr eErr = OGRCheckPermutation(panMap, poFeatureDefn->GetFieldCount());
1663 12 : if (eErr != OGRERR_NONE)
1664 2 : return eErr;
1665 :
1666 10 : if ( DBFReorderFields( hDBF, panMap ) )
1667 : {
1668 10 : return poFeatureDefn->ReorderFieldDefns( panMap );
1669 : }
1670 : else
1671 0 : return OGRERR_FAILURE;
1672 : }
1673 :
1674 : /************************************************************************/
1675 : /* AlterFieldDefn() */
1676 : /************************************************************************/
1677 :
1678 14 : OGRErr OGRShapeLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags )
1679 : {
1680 14 : if (!TouchLayer())
1681 0 : return OGRERR_FAILURE;
1682 :
1683 14 : if( !bUpdateAccess )
1684 : {
1685 : CPLError( CE_Failure, CPLE_NotSupported,
1686 : UNSUPPORTED_OP_READ_ONLY,
1687 1 : "AlterFieldDefn");
1688 1 : return OGRERR_FAILURE;
1689 : }
1690 :
1691 13 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
1692 : {
1693 : CPLError( CE_Failure, CPLE_NotSupported,
1694 3 : "Invalid field index");
1695 3 : return OGRERR_FAILURE;
1696 : }
1697 :
1698 10 : OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1699 :
1700 : char chNativeType;
1701 : char szFieldName[20];
1702 : int nWidth, nPrecision;
1703 10 : OGRFieldType eType = poFieldDefn->GetType();
1704 : DBFFieldType eDBFType;
1705 :
1706 10 : chNativeType = DBFGetNativeFieldType( hDBF, iField );
1707 : eDBFType = DBFGetFieldInfo( hDBF, iField, szFieldName,
1708 10 : &nWidth, &nPrecision );
1709 :
1710 10 : if ((nFlags & ALTER_TYPE_FLAG) &&
1711 : poNewFieldDefn->GetType() != poFieldDefn->GetType())
1712 : {
1713 3 : if (poNewFieldDefn->GetType() != OFTString)
1714 : {
1715 : CPLError( CE_Failure, CPLE_NotSupported,
1716 1 : "Can only convert to OFTString");
1717 1 : return OGRERR_FAILURE;
1718 : }
1719 : else
1720 : {
1721 2 : chNativeType = 'C';
1722 2 : eType = poNewFieldDefn->GetType();
1723 : }
1724 : }
1725 :
1726 9 : if (nFlags & ALTER_NAME_FLAG)
1727 : {
1728 9 : CPLString osFieldName;
1729 9 : if( osEncoding.size() )
1730 : {
1731 9 : CPLClearRecodeWarningFlags();
1732 9 : CPLErrorReset();
1733 9 : CPLPushErrorHandler(CPLQuietErrorHandler);
1734 9 : char* pszRecoded = CPLRecode( poNewFieldDefn->GetNameRef(), CPL_ENC_UTF8, osEncoding);
1735 9 : CPLPopErrorHandler();
1736 9 : osFieldName = pszRecoded;
1737 9 : CPLFree(pszRecoded);
1738 9 : if( CPLGetLastErrorType() != 0 )
1739 : {
1740 : CPLError(CE_Failure, CPLE_AppDefined,
1741 : "Failed to rename field name to '%s' : cannot convert to %s",
1742 1 : poNewFieldDefn->GetNameRef(), osEncoding.c_str());
1743 1 : return OGRERR_FAILURE;
1744 : }
1745 : }
1746 : else
1747 0 : osFieldName = poNewFieldDefn->GetNameRef();
1748 :
1749 8 : strncpy(szFieldName, osFieldName, 10);
1750 8 : szFieldName[10] = '\0';
1751 : }
1752 8 : if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
1753 : {
1754 7 : nWidth = poNewFieldDefn->GetWidth();
1755 7 : nPrecision = poNewFieldDefn->GetPrecision();
1756 : }
1757 :
1758 8 : if ( DBFAlterFieldDefn( hDBF, iField, szFieldName,
1759 : chNativeType, nWidth, nPrecision) )
1760 : {
1761 8 : if (nFlags & ALTER_TYPE_FLAG)
1762 7 : poFieldDefn->SetType(eType);
1763 8 : if (nFlags & ALTER_NAME_FLAG)
1764 8 : poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
1765 8 : if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
1766 : {
1767 7 : poFieldDefn->SetWidth(nWidth);
1768 7 : poFieldDefn->SetPrecision(nPrecision);
1769 :
1770 7 : TruncateDBF();
1771 : }
1772 8 : return OGRERR_NONE;
1773 : }
1774 : else
1775 0 : return OGRERR_FAILURE;
1776 : }
1777 :
1778 : /************************************************************************/
1779 : /* GetSpatialRef() */
1780 : /************************************************************************/
1781 :
1782 75845 : OGRSpatialReference *OGRShapeLayer::GetSpatialRef()
1783 :
1784 : {
1785 75845 : if (bSRSSet)
1786 74916 : return poSRS;
1787 :
1788 929 : bSRSSet = TRUE;
1789 :
1790 : /* -------------------------------------------------------------------- */
1791 : /* Is there an associated .prj file we can read? */
1792 : /* -------------------------------------------------------------------- */
1793 929 : const char *pszPrjFile = CPLResetExtension( pszFullName, "prj" );
1794 : char **papszLines;
1795 :
1796 929 : char* apszOptions[] = { (char*)"EMIT_ERROR_IF_CANNOT_OPEN_FILE=FALSE", NULL };
1797 929 : papszLines = CSLLoad2( pszPrjFile, -1, -1, apszOptions );
1798 929 : if (papszLines == NULL)
1799 : {
1800 779 : pszPrjFile = CPLResetExtension( pszFullName, "PRJ" );
1801 779 : papszLines = CSLLoad2( pszPrjFile, -1, -1, apszOptions );
1802 : }
1803 :
1804 929 : if( papszLines != NULL )
1805 : {
1806 316 : poSRS = new OGRSpatialReference();
1807 316 : if( poSRS->importFromESRI( papszLines ) != OGRERR_NONE )
1808 : {
1809 0 : delete poSRS;
1810 0 : poSRS = NULL;
1811 : }
1812 316 : CSLDestroy( papszLines );
1813 : }
1814 :
1815 929 : return poSRS;
1816 : }
1817 :
1818 : /************************************************************************/
1819 : /* ResetGeomType() */
1820 : /* */
1821 : /* Modify the geometry type for this file. Used to convert to */
1822 : /* a different geometry type when a layer was created with a */
1823 : /* type of unknown, and we get to the first feature to */
1824 : /* establish the type. */
1825 : /************************************************************************/
1826 :
1827 574 : int OGRShapeLayer::ResetGeomType( int nNewGeomType )
1828 :
1829 : {
1830 : char abyHeader[100];
1831 : int nStartPos;
1832 :
1833 574 : if( nTotalShapeCount > 0 )
1834 0 : return FALSE;
1835 :
1836 574 : if( hSHP->fpSHX == NULL)
1837 : {
1838 : CPLError( CE_Failure, CPLE_NotSupported,
1839 0 : " OGRShapeLayer::ResetGeomType failed : SHX file is closed");
1840 0 : return FALSE;
1841 : }
1842 :
1843 : /* -------------------------------------------------------------------- */
1844 : /* Update .shp header. */
1845 : /* -------------------------------------------------------------------- */
1846 574 : nStartPos = (int)( hSHP->sHooks.FTell( hSHP->fpSHP ) );
1847 :
1848 574 : if( hSHP->sHooks.FSeek( hSHP->fpSHP, 0, SEEK_SET ) != 0
1849 : || hSHP->sHooks.FRead( abyHeader, 100, 1, hSHP->fpSHP ) != 1 )
1850 0 : return FALSE;
1851 :
1852 574 : *((GInt32 *) (abyHeader + 32)) = CPL_LSBWORD32( nNewGeomType );
1853 :
1854 574 : if( hSHP->sHooks.FSeek( hSHP->fpSHP, 0, SEEK_SET ) != 0
1855 : || hSHP->sHooks.FWrite( abyHeader, 100, 1, hSHP->fpSHP ) != 1 )
1856 0 : return FALSE;
1857 :
1858 574 : if( hSHP->sHooks.FSeek( hSHP->fpSHP, nStartPos, SEEK_SET ) != 0 )
1859 0 : return FALSE;
1860 :
1861 : /* -------------------------------------------------------------------- */
1862 : /* Update .shx header. */
1863 : /* -------------------------------------------------------------------- */
1864 574 : nStartPos = (int)( hSHP->sHooks.FTell( hSHP->fpSHX ) );
1865 :
1866 574 : if( hSHP->sHooks.FSeek( hSHP->fpSHX, 0, SEEK_SET ) != 0
1867 : || hSHP->sHooks.FRead( abyHeader, 100, 1, hSHP->fpSHX ) != 1 )
1868 0 : return FALSE;
1869 :
1870 574 : *((GInt32 *) (abyHeader + 32)) = CPL_LSBWORD32( nNewGeomType );
1871 :
1872 574 : if( hSHP->sHooks.FSeek( hSHP->fpSHX, 0, SEEK_SET ) != 0
1873 : || hSHP->sHooks.FWrite( abyHeader, 100, 1, hSHP->fpSHX ) != 1 )
1874 0 : return FALSE;
1875 :
1876 574 : if( hSHP->sHooks.FSeek( hSHP->fpSHX, nStartPos, SEEK_SET ) != 0 )
1877 0 : return FALSE;
1878 :
1879 : /* -------------------------------------------------------------------- */
1880 : /* Update other information. */
1881 : /* -------------------------------------------------------------------- */
1882 574 : hSHP->nShapeType = nNewGeomType;
1883 :
1884 574 : return TRUE;
1885 : }
1886 :
1887 : /************************************************************************/
1888 : /* SyncToDisk() */
1889 : /************************************************************************/
1890 :
1891 31 : OGRErr OGRShapeLayer::SyncToDisk()
1892 :
1893 : {
1894 31 : if (!TouchLayer())
1895 0 : return OGRERR_FAILURE;
1896 :
1897 31 : if( bHeaderDirty )
1898 : {
1899 24 : if( hSHP != NULL )
1900 23 : SHPWriteHeader( hSHP );
1901 :
1902 24 : if( hDBF != NULL )
1903 24 : DBFUpdateHeader( hDBF );
1904 :
1905 24 : bHeaderDirty = FALSE;
1906 : }
1907 :
1908 31 : if( hSHP != NULL )
1909 : {
1910 29 : hSHP->sHooks.FFlush( hSHP->fpSHP );
1911 29 : if( hSHP->fpSHX != NULL )
1912 29 : hSHP->sHooks.FFlush( hSHP->fpSHX );
1913 : }
1914 :
1915 31 : if( hDBF != NULL )
1916 31 : hDBF->sHooks.FFlush( hDBF->fp );
1917 :
1918 31 : return OGRERR_NONE;
1919 : }
1920 :
1921 : /************************************************************************/
1922 : /* DropSpatialIndex() */
1923 : /************************************************************************/
1924 :
1925 7 : OGRErr OGRShapeLayer::DropSpatialIndex()
1926 :
1927 : {
1928 7 : if (!TouchLayer())
1929 0 : return OGRERR_FAILURE;
1930 :
1931 7 : if( !CheckForQIX() && !CheckForSBN() )
1932 : {
1933 : CPLError( CE_Warning, CPLE_AppDefined,
1934 : "Layer %s has no spatial index, DROP SPATIAL INDEX failed.",
1935 1 : poFeatureDefn->GetName() );
1936 1 : return OGRERR_FAILURE;
1937 : }
1938 :
1939 6 : int bHadQIX = hQIX != NULL;
1940 :
1941 6 : SHPCloseDiskTree( hQIX );
1942 6 : hQIX = NULL;
1943 6 : bCheckedForQIX = FALSE;
1944 :
1945 6 : SBNCloseDiskTree( hSBN );
1946 6 : hSBN = NULL;
1947 6 : bCheckedForSBN = FALSE;
1948 :
1949 6 : if( bHadQIX )
1950 : {
1951 : const char *pszQIXFilename;
1952 :
1953 6 : pszQIXFilename = CPLResetExtension( pszFullName, "qix" );
1954 6 : CPLDebug( "SHAPE", "Unlinking index file %s", pszQIXFilename );
1955 :
1956 6 : if( VSIUnlink( pszQIXFilename ) != 0 )
1957 : {
1958 : CPLError( CE_Failure, CPLE_AppDefined,
1959 : "Failed to delete file %s.\n%s",
1960 0 : pszQIXFilename, VSIStrerror( errno ) );
1961 0 : return OGRERR_FAILURE;
1962 : }
1963 : }
1964 :
1965 6 : if( !bSbnSbxDeleted )
1966 : {
1967 : const char *pszIndexFilename;
1968 5 : const char papszExt[2][4] = { "sbn", "sbx" };
1969 : int i;
1970 15 : for( i = 0; i < 2; i++ )
1971 : {
1972 10 : pszIndexFilename = CPLResetExtension( pszFullName, papszExt[i] );
1973 10 : CPLDebug( "SHAPE", "Trying to unlink index file %s", pszIndexFilename );
1974 :
1975 10 : if( VSIUnlink( pszIndexFilename ) != 0 )
1976 : {
1977 : CPLDebug( "SHAPE",
1978 : "Failed to delete file %s.\n%s",
1979 4 : pszIndexFilename, VSIStrerror( errno ) );
1980 : }
1981 : }
1982 : }
1983 6 : bSbnSbxDeleted = TRUE;
1984 :
1985 6 : ClearSpatialFIDs();
1986 :
1987 6 : return OGRERR_NONE;
1988 : }
1989 :
1990 : /************************************************************************/
1991 : /* CreateSpatialIndex() */
1992 : /************************************************************************/
1993 :
1994 14 : OGRErr OGRShapeLayer::CreateSpatialIndex( int nMaxDepth )
1995 :
1996 : {
1997 14 : if (!TouchLayer())
1998 0 : return OGRERR_FAILURE;
1999 :
2000 : /* -------------------------------------------------------------------- */
2001 : /* If we have an existing spatial index, blow it away first. */
2002 : /* -------------------------------------------------------------------- */
2003 14 : if( CheckForQIX() )
2004 1 : DropSpatialIndex();
2005 :
2006 14 : bCheckedForQIX = FALSE;
2007 :
2008 : /* -------------------------------------------------------------------- */
2009 : /* Build a quadtree structure for this file. */
2010 : /* -------------------------------------------------------------------- */
2011 : SHPTree *psTree;
2012 :
2013 14 : SyncToDisk();
2014 14 : psTree = SHPCreateTree( hSHP, 2, nMaxDepth, NULL, NULL );
2015 :
2016 14 : if( NULL == psTree )
2017 : {
2018 : // TODO - mloskot: Is it better to return OGRERR_NOT_ENOUGH_MEMORY?
2019 :
2020 : CPLDebug( "SHAPE",
2021 0 : "Index creation failure. Likely, memory allocation error." );
2022 :
2023 0 : return OGRERR_FAILURE;
2024 : }
2025 :
2026 : /* -------------------------------------------------------------------- */
2027 : /* Trim unused nodes from the tree. */
2028 : /* -------------------------------------------------------------------- */
2029 14 : SHPTreeTrimExtraNodes( psTree );
2030 :
2031 : /* -------------------------------------------------------------------- */
2032 : /* Dump tree to .qix file. */
2033 : /* -------------------------------------------------------------------- */
2034 : char *pszQIXFilename;
2035 :
2036 14 : pszQIXFilename = CPLStrdup(CPLResetExtension( pszFullName, "qix" ));
2037 :
2038 14 : CPLDebug( "SHAPE", "Creating index file %s", pszQIXFilename );
2039 :
2040 14 : SHPWriteTree( psTree, pszQIXFilename );
2041 14 : CPLFree( pszQIXFilename );
2042 :
2043 :
2044 : /* -------------------------------------------------------------------- */
2045 : /* cleanup */
2046 : /* -------------------------------------------------------------------- */
2047 14 : SHPDestroyTree( psTree );
2048 :
2049 14 : CheckForQIX();
2050 :
2051 14 : return OGRERR_NONE;
2052 : }
2053 :
2054 : /************************************************************************/
2055 : /* Repack() */
2056 : /* */
2057 : /* Repack the shape and dbf file, dropping deleted records. */
2058 : /* FIDs may change. */
2059 : /************************************************************************/
2060 :
2061 6 : OGRErr OGRShapeLayer::Repack()
2062 :
2063 : {
2064 6 : if (!TouchLayer())
2065 0 : return OGRERR_FAILURE;
2066 :
2067 6 : if( !bUpdateAccess )
2068 : {
2069 : CPLError( CE_Failure, CPLE_NotSupported,
2070 : UNSUPPORTED_OP_READ_ONLY,
2071 1 : "Repack");
2072 1 : return OGRERR_FAILURE;
2073 : }
2074 :
2075 5 : if( hDBF == NULL )
2076 : {
2077 : CPLError( CE_Failure, CPLE_NotSupported,
2078 1 : "Attempt to repack a shapefile with no .dbf file not supported.");
2079 1 : return OGRERR_FAILURE;
2080 : }
2081 :
2082 : /* -------------------------------------------------------------------- */
2083 : /* Build a list of records to be dropped. */
2084 : /* -------------------------------------------------------------------- */
2085 : int *panRecordsToDelete = (int *)
2086 4 : CPLMalloc(sizeof(int)*(nTotalShapeCount+1));
2087 4 : int nDeleteCount = 0;
2088 4 : int iShape = 0;
2089 4 : OGRErr eErr = OGRERR_NONE;
2090 :
2091 35 : for( iShape = 0; iShape < nTotalShapeCount; iShape++ )
2092 : {
2093 31 : if( DBFIsRecordDeleted( hDBF, iShape ) )
2094 3 : panRecordsToDelete[nDeleteCount++] = iShape;
2095 : }
2096 4 : panRecordsToDelete[nDeleteCount] = -1;
2097 :
2098 : /* -------------------------------------------------------------------- */
2099 : /* If there are no records marked for deletion, we take no */
2100 : /* action. */
2101 : /* -------------------------------------------------------------------- */
2102 4 : if( nDeleteCount == 0 )
2103 : {
2104 1 : CPLFree( panRecordsToDelete );
2105 1 : return OGRERR_NONE;
2106 : }
2107 :
2108 : /* -------------------------------------------------------------------- */
2109 : /* Find existing filenames with exact case (see #3293). */
2110 : /* -------------------------------------------------------------------- */
2111 3 : CPLString osDirname(CPLGetPath(pszFullName));
2112 3 : CPLString osBasename(CPLGetBasename(pszFullName));
2113 :
2114 3 : CPLString osDBFName, osSHPName, osSHXName;
2115 3 : char **papszCandidates = CPLReadDir( osDirname );
2116 3 : int i = 0;
2117 25 : while(papszCandidates != NULL && papszCandidates[i] != NULL)
2118 : {
2119 22 : CPLString osCandidateBasename = CPLGetBasename(papszCandidates[i]);
2120 22 : CPLString osCandidateExtension = CPLGetExtension(papszCandidates[i]);
2121 22 : if (osCandidateBasename.compare(osBasename) == 0)
2122 : {
2123 9 : if (EQUAL(osCandidateExtension, "dbf"))
2124 3 : osDBFName = CPLFormFilename(osDirname, papszCandidates[i], NULL);
2125 6 : else if (EQUAL(osCandidateExtension, "shp"))
2126 3 : osSHPName = CPLFormFilename(osDirname, papszCandidates[i], NULL);
2127 3 : else if (EQUAL(osCandidateExtension, "shx"))
2128 3 : osSHXName = CPLFormFilename(osDirname, papszCandidates[i], NULL);
2129 : }
2130 :
2131 22 : i++;
2132 : }
2133 3 : CSLDestroy(papszCandidates);
2134 3 : papszCandidates = NULL;
2135 :
2136 3 : if (osDBFName.size() == 0)
2137 : {
2138 : /* Should not happen, really */
2139 0 : CPLFree( panRecordsToDelete );
2140 0 : return OGRERR_FAILURE;
2141 : }
2142 :
2143 : /* -------------------------------------------------------------------- */
2144 : /* Cleanup any existing spatial index. It will become */
2145 : /* meaningless when the fids change. */
2146 : /* -------------------------------------------------------------------- */
2147 3 : if( CheckForQIX() || CheckForSBN() )
2148 0 : DropSpatialIndex();
2149 :
2150 : /* -------------------------------------------------------------------- */
2151 : /* Create a new dbf file, matching the old. */
2152 : /* -------------------------------------------------------------------- */
2153 3 : DBFHandle hNewDBF = NULL;
2154 :
2155 3 : CPLString oTempFile(CPLFormFilename(osDirname, osBasename, NULL));
2156 3 : oTempFile += "_packed.dbf";
2157 :
2158 3 : hNewDBF = DBFCloneEmpty( hDBF, oTempFile );
2159 3 : if( hNewDBF == NULL )
2160 : {
2161 0 : CPLFree( panRecordsToDelete );
2162 :
2163 : CPLError( CE_Failure, CPLE_OpenFailed,
2164 : "Failed to create temp file %s.",
2165 0 : oTempFile.c_str() );
2166 0 : return OGRERR_FAILURE;
2167 : }
2168 :
2169 : /* -------------------------------------------------------------------- */
2170 : /* Copy over all records that are not deleted. */
2171 : /* -------------------------------------------------------------------- */
2172 3 : int iDestShape = 0;
2173 3 : int iNextDeletedShape = 0;
2174 :
2175 34 : for( iShape = 0;
2176 : iShape < nTotalShapeCount && eErr == OGRERR_NONE;
2177 : iShape++ )
2178 : {
2179 31 : if( panRecordsToDelete[iNextDeletedShape] == iShape )
2180 3 : iNextDeletedShape++;
2181 : else
2182 : {
2183 28 : void *pTuple = (void *) DBFReadTuple( hDBF, iShape );
2184 28 : if( pTuple == NULL )
2185 0 : eErr = OGRERR_FAILURE;
2186 28 : else if( !DBFWriteTuple( hNewDBF, iDestShape++, pTuple ) )
2187 0 : eErr = OGRERR_FAILURE;
2188 : }
2189 : }
2190 :
2191 3 : if( eErr != OGRERR_NONE )
2192 : {
2193 0 : CPLFree( panRecordsToDelete );
2194 0 : VSIUnlink( oTempFile );
2195 0 : return eErr;
2196 : }
2197 :
2198 : /* -------------------------------------------------------------------- */
2199 : /* Cleanup the old .dbf and rename the new one. */
2200 : /* -------------------------------------------------------------------- */
2201 3 : DBFClose( hDBF );
2202 3 : DBFClose( hNewDBF );
2203 3 : hDBF = hNewDBF = NULL;
2204 :
2205 3 : VSIUnlink( osDBFName );
2206 :
2207 3 : if( VSIRename( oTempFile, osDBFName ) != 0 )
2208 : {
2209 0 : CPLDebug( "Shape", "Can not rename DBF file: %s", VSIStrerror( errno ) );
2210 0 : CPLFree( panRecordsToDelete );
2211 0 : return OGRERR_FAILURE;
2212 : }
2213 :
2214 : /* -------------------------------------------------------------------- */
2215 : /* Now create a shapefile matching the old one. */
2216 : /* -------------------------------------------------------------------- */
2217 3 : if( hSHP != NULL )
2218 : {
2219 3 : SHPHandle hNewSHP = NULL;
2220 :
2221 3 : if (osSHPName.size() == 0 || osSHXName.size() == 0)
2222 : {
2223 : /* Should not happen, really */
2224 0 : CPLFree( panRecordsToDelete );
2225 0 : return OGRERR_FAILURE;
2226 : }
2227 :
2228 3 : oTempFile = CPLFormFilename(osDirname, osBasename, NULL);
2229 3 : oTempFile += "_packed.shp";
2230 :
2231 3 : hNewSHP = SHPCreate( oTempFile, hSHP->nShapeType );
2232 3 : if( hNewSHP == NULL )
2233 : {
2234 0 : CPLFree( panRecordsToDelete );
2235 0 : return OGRERR_FAILURE;
2236 : }
2237 :
2238 : /* -------------------------------------------------------------------- */
2239 : /* Copy over all records that are not deleted. */
2240 : /* -------------------------------------------------------------------- */
2241 3 : iNextDeletedShape = 0;
2242 :
2243 34 : for( iShape = 0;
2244 : iShape < nTotalShapeCount && eErr == OGRERR_NONE;
2245 : iShape++ )
2246 : {
2247 31 : if( panRecordsToDelete[iNextDeletedShape] == iShape )
2248 3 : iNextDeletedShape++;
2249 : else
2250 : {
2251 : SHPObject *hObject;
2252 :
2253 28 : hObject = SHPReadObject( hSHP, iShape );
2254 28 : if( hObject == NULL )
2255 0 : eErr = OGRERR_FAILURE;
2256 28 : else if( SHPWriteObject( hNewSHP, -1, hObject ) == -1 )
2257 0 : eErr = OGRERR_FAILURE;
2258 :
2259 28 : if( hObject )
2260 28 : SHPDestroyObject( hObject );
2261 : }
2262 : }
2263 :
2264 3 : if( eErr != OGRERR_NONE )
2265 : {
2266 0 : CPLFree( panRecordsToDelete );
2267 0 : VSIUnlink( CPLResetExtension( oTempFile, "shp" ) );
2268 0 : VSIUnlink( CPLResetExtension( oTempFile, "shx" ) );
2269 0 : return eErr;
2270 : }
2271 :
2272 : /* -------------------------------------------------------------------- */
2273 : /* Cleanup the old .shp/.shx and rename the new one. */
2274 : /* -------------------------------------------------------------------- */
2275 3 : SHPClose( hSHP );
2276 3 : SHPClose( hNewSHP );
2277 3 : hSHP = hNewSHP = NULL;
2278 :
2279 3 : VSIUnlink( osSHPName );
2280 3 : VSIUnlink( osSHXName );
2281 :
2282 3 : oTempFile = CPLResetExtension( oTempFile, "shp" );
2283 3 : if( VSIRename( oTempFile, osSHPName ) != 0 )
2284 : {
2285 0 : CPLDebug( "Shape", "Can not rename SHP file: %s", VSIStrerror( errno ) );
2286 0 : CPLFree( panRecordsToDelete );
2287 0 : return OGRERR_FAILURE;
2288 : }
2289 :
2290 3 : oTempFile = CPLResetExtension( oTempFile, "shx" );
2291 3 : if( VSIRename( oTempFile, osSHXName ) != 0 )
2292 : {
2293 0 : CPLDebug( "Shape", "Can not rename SHX file: %s", VSIStrerror( errno ) );
2294 0 : CPLFree( panRecordsToDelete );
2295 0 : return OGRERR_FAILURE;
2296 : }
2297 : }
2298 :
2299 3 : CPLFree( panRecordsToDelete );
2300 3 : panRecordsToDelete = NULL;
2301 :
2302 : /* -------------------------------------------------------------------- */
2303 : /* Reopen the shapefile */
2304 : /* */
2305 : /* We do not need to reimplement OGRShapeDataSource::OpenFile() here */
2306 : /* with the fully featured error checking. */
2307 : /* If all operations above succeeded, then all necessery files are */
2308 : /* in the right place and accessible. */
2309 : /* -------------------------------------------------------------------- */
2310 3 : CPLAssert( NULL == hSHP );
2311 3 : CPLAssert( NULL == hDBF && NULL == hNewDBF );
2312 :
2313 3 : CPLPushErrorHandler( CPLQuietErrorHandler );
2314 :
2315 3 : const char* pszAccess = NULL;
2316 3 : if( bUpdateAccess )
2317 3 : pszAccess = "r+";
2318 : else
2319 0 : pszAccess = "r";
2320 :
2321 3 : hSHP = SHPOpen ( CPLResetExtension( pszFullName, "shp" ) , pszAccess );
2322 3 : hDBF = DBFOpen ( CPLResetExtension( pszFullName, "dbf" ) , pszAccess );
2323 :
2324 3 : CPLPopErrorHandler();
2325 :
2326 3 : if( NULL == hSHP || NULL == hDBF )
2327 : {
2328 0 : CPLString osMsg(CPLGetLastErrorMsg());
2329 0 : CPLError( CE_Failure, CPLE_OpenFailed, "%s", osMsg.c_str() );
2330 :
2331 0 : return OGRERR_FAILURE;
2332 : }
2333 :
2334 : /* -------------------------------------------------------------------- */
2335 : /* Update total shape count. */
2336 : /* -------------------------------------------------------------------- */
2337 3 : nTotalShapeCount = hDBF->nRecords;
2338 :
2339 3 : return OGRERR_NONE;
2340 : }
2341 :
2342 : /************************************************************************/
2343 : /* ResizeDBF() */
2344 : /* */
2345 : /* Autoshrink columns of the DBF file to their minimum */
2346 : /* size, according to the existing data. */
2347 : /************************************************************************/
2348 :
2349 2 : OGRErr OGRShapeLayer::ResizeDBF()
2350 :
2351 : {
2352 2 : if (!TouchLayer())
2353 0 : return OGRERR_FAILURE;
2354 :
2355 2 : if( !bUpdateAccess )
2356 : {
2357 : CPLError( CE_Failure, CPLE_NotSupported,
2358 : UNSUPPORTED_OP_READ_ONLY,
2359 0 : "ResizeDBF");
2360 0 : return OGRERR_FAILURE;
2361 : }
2362 :
2363 2 : if( hDBF == NULL )
2364 : {
2365 : CPLError( CE_Failure, CPLE_NotSupported,
2366 0 : "Attempt to RESIZE a shapefile with no .dbf file not supported.");
2367 0 : return OGRERR_FAILURE;
2368 : }
2369 :
2370 : int i, j;
2371 :
2372 : /* Look which columns must be examined */
2373 2 : int* panColMap = (int*) CPLMalloc(poFeatureDefn->GetFieldCount() * sizeof(int));
2374 2 : int* panBestWidth = (int*) CPLMalloc(poFeatureDefn->GetFieldCount() * sizeof(int));
2375 2 : int nStringCols = 0;
2376 8 : for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
2377 : {
2378 6 : if( poFeatureDefn->GetFieldDefn(i)->GetType() == OFTString ||
2379 : poFeatureDefn->GetFieldDefn(i)->GetType() == OFTInteger )
2380 : {
2381 6 : panColMap[nStringCols] = i;
2382 6 : panBestWidth[nStringCols] = 1;
2383 6 : nStringCols ++;
2384 : }
2385 : }
2386 :
2387 2 : if (nStringCols == 0)
2388 : {
2389 : /* Nothing to do */
2390 0 : CPLFree(panColMap);
2391 0 : CPLFree(panBestWidth);
2392 0 : return OGRERR_NONE;
2393 : }
2394 :
2395 2 : CPLDebug("SHAPE", "Computing optimal column size...");
2396 :
2397 2 : int bAlreadyWarned = FALSE;
2398 10 : for( i = 0; i < hDBF->nRecords; i++ )
2399 : {
2400 8 : if( !DBFIsRecordDeleted( hDBF, i ) )
2401 : {
2402 24 : for( j = 0; j < nStringCols; j ++)
2403 : {
2404 18 : if (DBFIsAttributeNULL(hDBF, i, panColMap[j]))
2405 6 : continue;
2406 :
2407 12 : const char* pszVal = DBFReadStringAttribute(hDBF, i, panColMap[j]);
2408 12 : int nLen = strlen(pszVal);
2409 12 : if (nLen > panBestWidth[j])
2410 6 : panBestWidth[j] = nLen;
2411 : }
2412 : }
2413 2 : else if (!bAlreadyWarned)
2414 : {
2415 2 : bAlreadyWarned = TRUE;
2416 : CPLDebug("SHAPE",
2417 2 : "DBF file would also need a REPACK due to deleted records");
2418 : }
2419 : }
2420 :
2421 8 : for( j = 0; j < nStringCols; j ++)
2422 : {
2423 6 : int iField = panColMap[j];
2424 6 : OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
2425 :
2426 : char szFieldName[20];
2427 : int nOriWidth, nPrecision;
2428 : char chNativeType;
2429 : DBFFieldType eDBFType;
2430 :
2431 6 : chNativeType = DBFGetNativeFieldType( hDBF, iField );
2432 : eDBFType = DBFGetFieldInfo( hDBF, iField, szFieldName,
2433 6 : &nOriWidth, &nPrecision );
2434 :
2435 6 : if (panBestWidth[j] < nOriWidth)
2436 : {
2437 : CPLDebug("SHAPE", "Shrinking field %d (%s) from %d to %d characters",
2438 3 : iField, poFieldDefn->GetNameRef(), nOriWidth, panBestWidth[j]);
2439 :
2440 3 : if (!DBFAlterFieldDefn( hDBF, iField, szFieldName,
2441 : chNativeType, panBestWidth[j], nPrecision ))
2442 : {
2443 : CPLError(CE_Failure, CPLE_AppDefined,
2444 : "Shrinking field %d (%s) from %d to %d characters failed",
2445 0 : iField, poFieldDefn->GetNameRef(), nOriWidth, panBestWidth[j]);
2446 :
2447 0 : CPLFree(panColMap);
2448 0 : CPLFree(panBestWidth);
2449 :
2450 0 : return OGRERR_FAILURE;
2451 : }
2452 : else
2453 : {
2454 3 : poFieldDefn->SetWidth(panBestWidth[j]);
2455 : }
2456 : }
2457 : }
2458 :
2459 2 : TruncateDBF();
2460 :
2461 2 : CPLFree(panColMap);
2462 2 : CPLFree(panBestWidth);
2463 :
2464 2 : return OGRERR_NONE;
2465 : }
2466 :
2467 : /************************************************************************/
2468 : /* TruncateDBF() */
2469 : /************************************************************************/
2470 :
2471 15 : void OGRShapeLayer::TruncateDBF()
2472 : {
2473 15 : if (hDBF == NULL)
2474 0 : return;
2475 :
2476 15 : VSILFILE* fp = (VSILFILE*)(hDBF->fp);
2477 15 : VSIFSeekL(fp, 0, SEEK_END);
2478 15 : vsi_l_offset nOldSize = VSIFTellL(fp);
2479 : vsi_l_offset nNewSize = hDBF->nRecordLength * (SAOffset) hDBF->nRecords
2480 15 : + hDBF->nHeaderLength;
2481 15 : if (nNewSize < nOldSize)
2482 : {
2483 : CPLDebug("SHAPE",
2484 : "Truncating DBF file from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB " bytes",
2485 11 : nOldSize, nNewSize);
2486 11 : VSIFTruncateL(fp, nNewSize);
2487 : }
2488 15 : VSIFSeekL(fp, 0, SEEK_SET);
2489 : }
2490 :
2491 : /************************************************************************/
2492 : /* RecomputeExtent() */
2493 : /* */
2494 : /* Force recomputation of the extent of the .SHP file */
2495 : /************************************************************************/
2496 :
2497 2 : OGRErr OGRShapeLayer::RecomputeExtent()
2498 : {
2499 2 : if (!TouchLayer())
2500 0 : return OGRERR_FAILURE;
2501 :
2502 2 : if( !bUpdateAccess )
2503 : {
2504 : CPLError( CE_Failure, CPLE_NotSupported,
2505 : UNSUPPORTED_OP_READ_ONLY,
2506 1 : "RecomputeExtent");
2507 1 : return OGRERR_FAILURE;
2508 : }
2509 :
2510 1 : if( hSHP == NULL )
2511 : {
2512 : CPLError( CE_Failure, CPLE_AppDefined,
2513 0 : "The RECOMPUTE EXTENT operation is not permitted on a layer without .SHP file." );
2514 0 : return OGRERR_FAILURE;
2515 : }
2516 :
2517 1 : double adBoundsMin[4] = { 0.0, 0.0, 0.0, 0.0 };
2518 1 : double adBoundsMax[4] = { 0.0, 0.0, 0.0, 0.0 };
2519 :
2520 1 : int bHasBeenInit = FALSE;
2521 :
2522 2 : for( int iShape = 0;
2523 : iShape < nTotalShapeCount;
2524 : iShape++ )
2525 : {
2526 1 : if( hDBF == NULL || !DBFIsRecordDeleted( hDBF, iShape ) )
2527 : {
2528 1 : SHPObject *psObject = SHPReadObject( hSHP, iShape );
2529 1 : if ( psObject != NULL &&
2530 : psObject->nSHPType != SHPT_NULL &&
2531 : psObject->nVertices != 0 )
2532 : {
2533 1 : if( !bHasBeenInit )
2534 : {
2535 1 : bHasBeenInit = TRUE;
2536 1 : adBoundsMin[0] = adBoundsMax[0] = psObject->padfX[0];
2537 1 : adBoundsMin[1] = adBoundsMax[1] = psObject->padfY[0];
2538 1 : adBoundsMin[2] = adBoundsMax[2] = psObject->padfZ[0];
2539 1 : adBoundsMin[3] = adBoundsMax[3] = psObject->padfM[0];
2540 : }
2541 :
2542 2 : for( int i = 0; i < psObject->nVertices; i++ )
2543 : {
2544 1 : adBoundsMin[0] = MIN(adBoundsMin[0],psObject->padfX[i]);
2545 1 : adBoundsMin[1] = MIN(adBoundsMin[1],psObject->padfY[i]);
2546 1 : adBoundsMin[2] = MIN(adBoundsMin[2],psObject->padfZ[i]);
2547 1 : adBoundsMin[3] = MIN(adBoundsMin[3],psObject->padfM[i]);
2548 1 : adBoundsMax[0] = MAX(adBoundsMax[0],psObject->padfX[i]);
2549 1 : adBoundsMax[1] = MAX(adBoundsMax[1],psObject->padfY[i]);
2550 1 : adBoundsMax[2] = MAX(adBoundsMax[2],psObject->padfZ[i]);
2551 1 : adBoundsMax[3] = MAX(adBoundsMax[3],psObject->padfM[i]);
2552 : }
2553 : }
2554 1 : SHPDestroyObject(psObject);
2555 : }
2556 : }
2557 :
2558 1 : if( memcmp(hSHP->adBoundsMin, adBoundsMin, 4*sizeof(double)) != 0 ||
2559 : memcmp(hSHP->adBoundsMax, adBoundsMax, 4*sizeof(double)) != 0 )
2560 : {
2561 1 : bHeaderDirty = TRUE;
2562 1 : hSHP->bUpdated = TRUE;
2563 1 : memcpy(hSHP->adBoundsMin, adBoundsMin, 4*sizeof(double));
2564 1 : memcpy(hSHP->adBoundsMax, adBoundsMax, 4*sizeof(double));
2565 : }
2566 :
2567 1 : return OGRERR_NONE;
2568 : }
2569 :
2570 :
2571 : /************************************************************************/
2572 : /* TouchLayer() */
2573 : /************************************************************************/
2574 :
2575 172764 : int OGRShapeLayer::TouchLayer()
2576 : {
2577 172764 : poDS->SetLastUsedLayer(this);
2578 :
2579 172764 : if (eFileDescriptorsState == FD_OPENED)
2580 172759 : return TRUE;
2581 5 : else if (eFileDescriptorsState == FD_CANNOT_REOPEN)
2582 0 : return FALSE;
2583 : else
2584 5 : return ReopenFileDescriptors();
2585 : }
2586 :
2587 : /************************************************************************/
2588 : /* ReopenFileDescriptors() */
2589 : /************************************************************************/
2590 :
2591 5 : int OGRShapeLayer::ReopenFileDescriptors()
2592 : {
2593 5 : CPLDebug("SHAPE", "ReopenFileDescriptors(%s)", pszFullName);
2594 :
2595 5 : if( bHSHPWasNonNULL )
2596 : {
2597 5 : if( bUpdateAccess )
2598 1 : hSHP = SHPOpen( pszFullName, "r+" );
2599 : else
2600 4 : hSHP = SHPOpen( pszFullName, "r" );
2601 :
2602 5 : if (hSHP == NULL)
2603 : {
2604 0 : eFileDescriptorsState = FD_CANNOT_REOPEN;
2605 0 : return FALSE;
2606 : }
2607 : }
2608 :
2609 5 : if( bHDBFWasNonNULL )
2610 : {
2611 5 : if( bUpdateAccess )
2612 1 : hDBF = DBFOpen( pszFullName, "r+" );
2613 : else
2614 4 : hDBF = DBFOpen( pszFullName, "r" );
2615 :
2616 5 : if (hDBF == NULL)
2617 : {
2618 : CPLError(CE_Failure, CPLE_OpenFailed,
2619 0 : "Cannot reopen %s", CPLResetExtension(pszFullName, "dbf"));
2620 0 : eFileDescriptorsState = FD_CANNOT_REOPEN;
2621 0 : return FALSE;
2622 : }
2623 : }
2624 :
2625 5 : eFileDescriptorsState = FD_OPENED;
2626 :
2627 5 : return TRUE;
2628 : }
2629 :
2630 : /************************************************************************/
2631 : /* CloseUnderlyingLayer() */
2632 : /************************************************************************/
2633 :
2634 2006 : void OGRShapeLayer::CloseUnderlyingLayer()
2635 : {
2636 2006 : CPLDebug("SHAPE", "CloseUnderlyingLayer(%s)", pszFullName);
2637 :
2638 2006 : if( hDBF != NULL )
2639 2006 : DBFClose( hDBF );
2640 2006 : hDBF = NULL;
2641 :
2642 2006 : if( hSHP != NULL )
2643 2006 : SHPClose( hSHP );
2644 2006 : hSHP = NULL;
2645 :
2646 : /* We close QIX and reset the check flag, so that CheckForQIX() */
2647 : /* will retry opening it if necessary when the layer is active again */
2648 2006 : if( hQIX != NULL )
2649 0 : SHPCloseDiskTree( hQIX );
2650 2006 : hQIX = NULL;
2651 2006 : bCheckedForQIX = FALSE;
2652 :
2653 2006 : if( hSBN != NULL )
2654 0 : SBNCloseDiskTree( hSBN );
2655 2006 : hSBN = NULL;
2656 2006 : bCheckedForSBN = FALSE;
2657 :
2658 2006 : eFileDescriptorsState = FD_CLOSED;
2659 2006 : }
|