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