int OGRShapeLayer::TestCapability( const char * pszCap ) { if( EQUAL(pszCap,OLCRandomRead) ) return TRUE; else if( EQUAL(pszCap,OLCSequentialWrite) || EQUAL(pszCap,OLCRandomWrite) ) return bUpdateAccess; else if( EQUAL(pszCap,OLCFastFeatureCount) ) return m_poFilterGeom == NULL || CheckForQIX(); else if( EQUAL(pszCap,OLCDeleteFeature) ) return bUpdateAccess; else if( EQUAL(pszCap,OLCFastSpatialFilter) ) return CheckForQIX(); else if( EQUAL(pszCap,OLCFastGetExtent) ) return TRUE; else if( EQUAL(pszCap,OLCFastSetNextByIndex) ) return m_poFilterGeom == NULL && m_poAttrQuery == NULL; else if( EQUAL(pszCap,OLCCreateField) ) return bUpdateAccess; else if( EQUAL(pszCap,OLCIgnoreFields) ) return TRUE; else return FALSE; }
OGRErr OGRShapeLayer::CreateSpatialIndex( int nMaxDepth ) { /* -------------------------------------------------------------------- */ /* If we have an existing spatial index, blow it away first. */ /* -------------------------------------------------------------------- */ if( CheckForQIX() ) DropSpatialIndex(); bCheckedForQIX = FALSE; /* -------------------------------------------------------------------- */ /* Build a quadtree structure for this file. */ /* -------------------------------------------------------------------- */ SHPTree *psTree; SyncToDisk(); psTree = SHPCreateTree( hSHP, 2, nMaxDepth, NULL, NULL ); if( NULL == psTree ) { // TODO - mloskot: Is it better to return OGRERR_NOT_ENOUGH_MEMORY? CPLDebug( "SHAPE", "Index creation failure. Likely, memory allocation error." ); return OGRERR_FAILURE; } /* -------------------------------------------------------------------- */ /* Trim unused nodes from the tree. */ /* -------------------------------------------------------------------- */ SHPTreeTrimExtraNodes( psTree ); /* -------------------------------------------------------------------- */ /* Dump tree to .qix file. */ /* -------------------------------------------------------------------- */ char *pszQIXFilename; pszQIXFilename = CPLStrdup(CPLResetExtension( pszFullName, "qix" )); CPLDebug( "SHAPE", "Creating index file %s", pszQIXFilename ); SHPWriteTree( psTree, pszQIXFilename ); CPLFree( pszQIXFilename ); /* -------------------------------------------------------------------- */ /* cleanup */ /* -------------------------------------------------------------------- */ SHPDestroyTree( psTree ); CheckForQIX(); return OGRERR_NONE; }
OGRErr OGRShapeLayer::DropSpatialIndex() { if( !CheckForQIX() ) { CPLError( CE_Warning, CPLE_AppDefined, "Layer %s has no spatial index, DROP SPATIAL INDEX failed.", poFeatureDefn->GetName() ); return OGRERR_FAILURE; } VSIFClose( fpQIX ); fpQIX = NULL; bCheckedForQIX = FALSE; const char *pszQIXFilename; pszQIXFilename = CPLResetExtension( pszFullName, "qix" ); CPLDebug( "SHAPE", "Unlinking index file %s", pszQIXFilename ); if( VSIUnlink( pszQIXFilename ) != 0 ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to delete file %s.\n%s", pszQIXFilename, VSIStrerror( errno ) ); return OGRERR_FAILURE; } else return OGRERR_NONE; }
OGRErr OGRShapeLayer::DropSpatialIndex() { if( !CheckForQIX() ) { CPLError( CE_Warning, CPLE_AppDefined, "Layer %s has no spatial index, DROP SPATIAL INDEX failed.", poFeatureDefn->GetName() ); return OGRERR_FAILURE; } VSIFClose( fpQIX ); fpQIX = NULL; bCheckedForQIX = FALSE; const char *pszQIXFilename; pszQIXFilename = CPLResetExtension( pszFullName, "qix" ); CPLDebug( "SHAPE", "Unlinking index file %s", pszQIXFilename ); if( VSIUnlink( pszQIXFilename ) != 0 ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to delete file %s.\n%s", pszQIXFilename, VSIStrerror( errno ) ); return OGRERR_FAILURE; } if( !bSbnSbxDeleted ) { const char *pszIndexFilename; const char papszExt[2][4] = { "sbn", "sbx" }; int i; for( i = 0; i < 2; i++ ) { pszIndexFilename = CPLResetExtension( pszFullName, papszExt[i] ); CPLDebug( "SHAPE", "Trying to unlink index file %s", pszIndexFilename ); if( VSIUnlink( pszIndexFilename ) != 0 ) { CPLDebug( "SHAPE", "Failed to delete file %s.\n%s", pszIndexFilename, VSIStrerror( errno ) ); } } } bSbnSbxDeleted = TRUE; return OGRERR_NONE; }
OGRErr OGRShapeLayer::SetFeature( OGRFeature *poFeature ) { if( !bUpdateAccess ) { CPLError( CE_Failure, CPLE_AppDefined, "The SetFeature() operation is not permitted on a read-only shapefile." ); return OGRERR_FAILURE; } bHeaderDirty = TRUE; if( CheckForQIX() ) DropSpatialIndex(); return SHPWriteOGRFeature( hSHP, hDBF, poFeatureDefn, poFeature ); }
OGRErr OGRShapeLayer::DeleteFeature( long nFID ) { if( !bUpdateAccess ) { CPLError( CE_Failure, CPLE_AppDefined, "The DeleteFeature() operation is not permitted on a read-only shapefile." ); return OGRERR_FAILURE; } if( nFID < 0 || (hSHP != NULL && nFID >= hSHP->nRecords) || (hDBF != NULL && nFID >= hDBF->nRecords) ) { CPLError( CE_Failure, CPLE_AppDefined, "Attempt to delete shape with feature id (%ld) which does " "not exist.", nFID ); return OGRERR_FAILURE; } if( !hDBF ) { CPLError( CE_Failure, CPLE_AppDefined, "Attempt to delete shape in shapefile with no .dbf file.\n" "Deletion is done by marking record deleted in dbf\n" "and is not supported without a .dbf file." ); return OGRERR_FAILURE; } if( DBFIsRecordDeleted( hDBF, nFID ) ) { CPLError( CE_Failure, CPLE_AppDefined, "Attempt to delete shape with feature id (%ld), but it is marked deleted already.", nFID ); return OGRERR_FAILURE; } if( !DBFMarkRecordDeleted( hDBF, nFID, TRUE ) ) return OGRERR_FAILURE; bHeaderDirty = TRUE; if( CheckForQIX() ) DropSpatialIndex(); return OGRERR_NONE; }
OGRErr OGRShapeLayer::CreateFeature( OGRFeature *poFeature ) { OGRErr eErr; if( !bUpdateAccess ) { CPLError( CE_Failure, CPLE_AppDefined, "The CreateFeature() operation is not permitted on a read-only shapefile." ); return OGRERR_FAILURE; } bHeaderDirty = TRUE; if( CheckForQIX() ) DropSpatialIndex(); poFeature->SetFID( OGRNullFID ); if( nTotalShapeCount == 0 && eRequestedGeomType == wkbUnknown && poFeature->GetGeometryRef() != NULL ) { OGRGeometry *poGeom = poFeature->GetGeometryRef(); int nShapeType; switch( poGeom->getGeometryType() ) { case wkbPoint: nShapeType = SHPT_POINT; eRequestedGeomType = wkbPoint; break; case wkbPoint25D: nShapeType = SHPT_POINTZ; eRequestedGeomType = wkbPoint25D; break; case wkbMultiPoint: nShapeType = SHPT_MULTIPOINT; eRequestedGeomType = wkbMultiPoint; break; case wkbMultiPoint25D: nShapeType = SHPT_MULTIPOINTZ; eRequestedGeomType = wkbMultiPoint25D; break; case wkbLineString: case wkbMultiLineString: nShapeType = SHPT_ARC; eRequestedGeomType = wkbLineString; break; case wkbLineString25D: case wkbMultiLineString25D: nShapeType = SHPT_ARCZ; eRequestedGeomType = wkbLineString25D; break; case wkbPolygon: case wkbMultiPolygon: nShapeType = SHPT_POLYGON; eRequestedGeomType = wkbPolygon; break; case wkbPolygon25D: case wkbMultiPolygon25D: nShapeType = SHPT_POLYGONZ; eRequestedGeomType = wkbPolygon25D; break; default: nShapeType = -1; break; } if( nShapeType != -1 ) { ResetGeomType( nShapeType ); } } eErr = SHPWriteOGRFeature( hSHP, hDBF, poFeatureDefn, poFeature ); if( hSHP != NULL ) nTotalShapeCount = hSHP->nRecords; else nTotalShapeCount = hDBF->nRecords; return eErr; }
int OGRShapeLayer::ScanIndices() { iMatchingFID = 0; /* -------------------------------------------------------------------- */ /* Utilize attribute index if appropriate. */ /* -------------------------------------------------------------------- */ if( m_poAttrQuery != NULL ) { CPLAssert( panMatchingFIDs == NULL ); panMatchingFIDs = m_poAttrQuery->EvaluateAgainstIndices( this, NULL ); } /* -------------------------------------------------------------------- */ /* Check for spatial index if we have a spatial query. */ /* -------------------------------------------------------------------- */ if( m_poFilterGeom != NULL && !bCheckedForQIX ) CheckForQIX(); /* -------------------------------------------------------------------- */ /* Utilize spatial index if appropriate. */ /* -------------------------------------------------------------------- */ if( m_poFilterGeom && fpQIX ) { int nSpatialFIDCount, *panSpatialFIDs; double adfBoundsMin[4], adfBoundsMax[4]; OGREnvelope oEnvelope; m_poFilterGeom->getEnvelope( &oEnvelope ); adfBoundsMin[0] = oEnvelope.MinX; adfBoundsMin[1] = oEnvelope.MinY; adfBoundsMin[2] = 0.0; adfBoundsMin[3] = 0.0; adfBoundsMax[0] = oEnvelope.MaxX; adfBoundsMax[1] = oEnvelope.MaxY; adfBoundsMax[2] = 0.0; adfBoundsMax[3] = 0.0; panSpatialFIDs = SHPSearchDiskTree( fpQIX, adfBoundsMin, adfBoundsMax, &nSpatialFIDCount ); CPLDebug( "SHAPE", "Used spatial index, got %d matches.", nSpatialFIDCount ); // Use resulting list as matching FID list (but reallocate and // terminate with OGRNullFID). if( panMatchingFIDs == NULL ) { int i; panMatchingFIDs = (long *) CPLMalloc(sizeof(long) * (nSpatialFIDCount+1) ); for( i = 0; i < nSpatialFIDCount; i++ ) panMatchingFIDs[i] = (long) panSpatialFIDs[i]; panMatchingFIDs[nSpatialFIDCount] = OGRNullFID; } // Cull attribute index matches based on those in the spatial index // result set. We assume that the attribute results are in sorted // order. else { int iRead, iWrite=0, iSpatial=0; for( iRead = 0; panMatchingFIDs[iRead] != OGRNullFID; iRead++ ) { while( iSpatial < nSpatialFIDCount && panSpatialFIDs[iSpatial] < panMatchingFIDs[iRead] ) iSpatial++; if( iSpatial == nSpatialFIDCount ) continue; if( panSpatialFIDs[iSpatial] == panMatchingFIDs[iRead] ) panMatchingFIDs[iWrite++] = panMatchingFIDs[iRead]; } panMatchingFIDs[iWrite] = OGRNullFID; } if ( panSpatialFIDs ) free( panSpatialFIDs ); } return TRUE; }
OGRErr OGRShapeLayer::Repack() { if( !bUpdateAccess ) { CPLError( CE_Failure, CPLE_AppDefined, "The REPACK operation is not permitted on a read-only shapefile." ); return OGRERR_FAILURE; } if( hDBF == NULL ) { CPLError( CE_Failure, CPLE_NotSupported, "Attempt to repack a shapefile with no .dbf file not supported."); return OGRERR_FAILURE; } /* -------------------------------------------------------------------- */ /* Build a list of records to be dropped. */ /* -------------------------------------------------------------------- */ int *panRecordsToDelete = (int *) CPLMalloc(sizeof(int)*(nTotalShapeCount+1)); int nDeleteCount = 0; int iShape = 0; OGRErr eErr = OGRERR_NONE; for( iShape = 0; iShape < nTotalShapeCount; iShape++ ) { if( DBFIsRecordDeleted( hDBF, iShape ) ) panRecordsToDelete[nDeleteCount++] = iShape; } panRecordsToDelete[nDeleteCount] = -1; /* -------------------------------------------------------------------- */ /* If there are no records marked for deletion, we take no */ /* action. */ /* -------------------------------------------------------------------- */ if( nDeleteCount == 0 ) { CPLFree( panRecordsToDelete ); return OGRERR_NONE; } /* -------------------------------------------------------------------- */ /* Find existing filenames with exact case (see #3293). */ /* -------------------------------------------------------------------- */ CPLString osDirname(CPLGetPath(pszFullName)); CPLString osBasename(CPLGetBasename(pszFullName)); CPLString osDBFName, osSHPName, osSHXName; char **papszCandidates = CPLReadDir( osDirname ); int i = 0; while(papszCandidates != NULL && papszCandidates[i] != NULL) { CPLString osCandidateBasename = CPLGetBasename(papszCandidates[i]); CPLString osCandidateExtension = CPLGetExtension(papszCandidates[i]); if (osCandidateBasename.compare(osBasename) == 0) { if (EQUAL(osCandidateExtension, "dbf")) osDBFName = CPLFormFilename(osDirname, papszCandidates[i], NULL); else if (EQUAL(osCandidateExtension, "shp")) osSHPName = CPLFormFilename(osDirname, papszCandidates[i], NULL); else if (EQUAL(osCandidateExtension, "shx")) osSHXName = CPLFormFilename(osDirname, papszCandidates[i], NULL); } i++; } CSLDestroy(papszCandidates); papszCandidates = NULL; if (osDBFName.size() == 0) { /* Should not happen, really */ CPLFree( panRecordsToDelete ); return OGRERR_FAILURE; } /* -------------------------------------------------------------------- */ /* Cleanup any existing spatial index. It will become */ /* meaningless when the fids change. */ /* -------------------------------------------------------------------- */ if( CheckForQIX() ) DropSpatialIndex(); /* -------------------------------------------------------------------- */ /* Create a new dbf file, matching the old. */ /* -------------------------------------------------------------------- */ DBFHandle hNewDBF = NULL; CPLString oTempFile(CPLFormFilename(osDirname, osBasename, NULL)); oTempFile += "_packed.dbf"; hNewDBF = DBFCloneEmpty( hDBF, oTempFile ); if( hNewDBF == NULL ) { CPLFree( panRecordsToDelete ); CPLError( CE_Failure, CPLE_OpenFailed, "Failed to create temp file %s.", oTempFile.c_str() ); return OGRERR_FAILURE; } /* -------------------------------------------------------------------- */ /* Copy over all records that are not deleted. */ /* -------------------------------------------------------------------- */ int iDestShape = 0; int iNextDeletedShape = 0; for( iShape = 0; iShape < nTotalShapeCount && eErr == OGRERR_NONE; iShape++ ) { if( panRecordsToDelete[iNextDeletedShape] == iShape ) iNextDeletedShape++; else { void *pTuple = (void *) DBFReadTuple( hDBF, iShape ); if( pTuple == NULL ) eErr = OGRERR_FAILURE; else if( !DBFWriteTuple( hNewDBF, iDestShape++, pTuple ) ) eErr = OGRERR_FAILURE; } } if( eErr != OGRERR_NONE ) { CPLFree( panRecordsToDelete ); VSIUnlink( oTempFile ); return eErr; } /* -------------------------------------------------------------------- */ /* Cleanup the old .dbf and rename the new one. */ /* -------------------------------------------------------------------- */ DBFClose( hDBF ); DBFClose( hNewDBF ); hDBF = hNewDBF = NULL; VSIUnlink( osDBFName ); if( VSIRename( oTempFile, osDBFName ) != 0 ) { CPLDebug( "Shape", "Can not rename DBF file: %s", VSIStrerror( errno ) ); CPLFree( panRecordsToDelete ); return OGRERR_FAILURE; } /* -------------------------------------------------------------------- */ /* Now create a shapefile matching the old one. */ /* -------------------------------------------------------------------- */ if( hSHP != NULL ) { SHPHandle hNewSHP = NULL; if (osSHPName.size() == 0 || osSHXName.size() == 0) { /* Should not happen, really */ CPLFree( panRecordsToDelete ); return OGRERR_FAILURE; } oTempFile = CPLFormFilename(osDirname, osBasename, NULL); oTempFile += "_packed.shp"; hNewSHP = SHPCreate( oTempFile, hSHP->nShapeType ); if( hNewSHP == NULL ) { CPLFree( panRecordsToDelete ); return OGRERR_FAILURE; } /* -------------------------------------------------------------------- */ /* Copy over all records that are not deleted. */ /* -------------------------------------------------------------------- */ iNextDeletedShape = 0; for( iShape = 0; iShape < nTotalShapeCount && eErr == OGRERR_NONE; iShape++ ) { if( panRecordsToDelete[iNextDeletedShape] == iShape ) iNextDeletedShape++; else { SHPObject *hObject; hObject = SHPReadObject( hSHP, iShape ); if( hObject == NULL ) eErr = OGRERR_FAILURE; else if( SHPWriteObject( hNewSHP, -1, hObject ) == -1 ) eErr = OGRERR_FAILURE; if( hObject ) SHPDestroyObject( hObject ); } } if( eErr != OGRERR_NONE ) { CPLFree( panRecordsToDelete ); VSIUnlink( CPLResetExtension( oTempFile, "shp" ) ); VSIUnlink( CPLResetExtension( oTempFile, "shx" ) ); return eErr; } /* -------------------------------------------------------------------- */ /* Cleanup the old .shp/.shx and rename the new one. */ /* -------------------------------------------------------------------- */ SHPClose( hSHP ); SHPClose( hNewSHP ); hSHP = hNewSHP = NULL; VSIUnlink( osSHPName ); VSIUnlink( osSHXName ); oTempFile = CPLResetExtension( oTempFile, "shp" ); if( VSIRename( oTempFile, osSHPName ) != 0 ) { CPLDebug( "Shape", "Can not rename SHP file: %s", VSIStrerror( errno ) ); CPLFree( panRecordsToDelete ); return OGRERR_FAILURE; } oTempFile = CPLResetExtension( oTempFile, "shx" ); if( VSIRename( oTempFile, osSHXName ) != 0 ) { CPLDebug( "Shape", "Can not rename SHX file: %s", VSIStrerror( errno ) ); CPLFree( panRecordsToDelete ); return OGRERR_FAILURE; } } CPLFree( panRecordsToDelete ); panRecordsToDelete = NULL; /* -------------------------------------------------------------------- */ /* Reopen the shapefile */ /* */ /* We do not need to reimplement OGRShapeDataSource::OpenFile() here */ /* with the fully featured error checking. */ /* If all operations above succeeded, then all necessery files are */ /* in the right place and accessible. */ /* -------------------------------------------------------------------- */ CPLAssert( NULL == hSHP ); CPLAssert( NULL == hDBF && NULL == hNewDBF ); CPLPushErrorHandler( CPLQuietErrorHandler ); const char* pszAccess = NULL; if( bUpdateAccess ) pszAccess = "r+"; else pszAccess = "r"; hSHP = SHPOpen ( CPLResetExtension( pszFullName, "shp" ) , pszAccess ); hDBF = DBFOpen ( CPLResetExtension( pszFullName, "dbf" ) , pszAccess ); CPLPopErrorHandler(); if( NULL == hSHP || NULL == hDBF ) { CPLString osMsg(CPLGetLastErrorMsg()); CPLError( CE_Failure, CPLE_OpenFailed, "%s", osMsg.c_str() ); return OGRERR_FAILURE; } /* -------------------------------------------------------------------- */ /* Update total shape count. */ /* -------------------------------------------------------------------- */ nTotalShapeCount = hDBF->nRecords; return OGRERR_NONE; }