From a4a85cbf3762ca0556b71a1b3e986d99165c6b10 Mon Sep 17 00:00:00 2001 From: Etienne Tourigny Date: Fri, 8 Jun 2012 01:27:19 -0300 Subject: [PATCH] add support for .tar/.tgz files and relevant tests ; delay scan of .tgz files and large .zip files until requested --- src/core/qgsdataitem.cpp | 141 +++++++++++++++------ src/core/qgsdataitem.h | 7 +- src/core/qgsmaplayer.cpp | 23 +++- src/providers/gdal/qgsgdaldataitems.cpp | 42 +++---- src/providers/gdal/qgsgdalprovider.cpp | 39 ++---- src/providers/ogr/qgsogrdataitems.cpp | 42 +++---- src/providers/ogr/qgsogrprovider.cpp | 26 ++-- tests/src/core/testziplayer.cpp | 159 +++++++++++++++++++----- tests/testdata/landsat_b1.tar | Bin 0 -> 51200 bytes tests/testdata/points2.tar | Bin 0 -> 20480 bytes tests/testdata/testtar.tgz | Bin 0 -> 33401 bytes 11 files changed, 303 insertions(+), 176 deletions(-) create mode 100644 tests/testdata/landsat_b1.tar create mode 100644 tests/testdata/points2.tar create mode 100644 tests/testdata/testtar.tgz diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp index 17f2e6436fd..4dd677c6a57 100644 --- a/src/core/qgsdataitem.cpp +++ b/src/core/qgsdataitem.cpp @@ -477,7 +477,6 @@ QVector QgsDirectoryItem::createChildren( ) QVector children; QDir dir( mPath ); QSettings settings; - bool scanZip = ( settings.value( "/qgis/scanZipInBrowser", QVariant( "basic" ) ).toString() != "no" ); QStringList entries = dir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase ); foreach( QString subdir, entries ) @@ -497,8 +496,10 @@ QVector QgsDirectoryItem::createChildren( ) QString path = dir.absoluteFilePath( name ); QFileInfo fileInfo( path ); + QString vsiPrefix = QgsZipItem::vsiPrefix( path ); // vsizip support was added to GDAL/OGR 1.6 but GDAL_VERSION_NUM not available here - if ( fileInfo.suffix() == "zip" && scanZip ) + if (( settings.value( "/qgis/scanZipInBrowser", QVariant( "basic" ) ).toString() != "no" ) && + ( vsiPrefix == "/vsizip/" || vsiPrefix == "/vsitar/" ) ) { QgsDataItem * item = QgsZipItem::itemFromPath( this, path, name ); if ( item ) @@ -747,6 +748,7 @@ QgsZipItem::QgsZipItem( QgsDataItem* parent, QString name, QString path ) { mType = Collection; //Zip?? mIcon = iconZip(); + mVsiPrefix = vsiPrefix( path ); if ( mProviderNames.size() == 0 ) { @@ -796,7 +798,6 @@ QgsZipItem::QgsZipItem( QgsDataItem* parent, QString name, QString path ) } } } - } QgsZipItem::~QgsZipItem() @@ -879,13 +880,12 @@ QVector QgsZipItem::createChildren( ) QVector children; QString tmpPath; QString childPath; - QSettings settings; QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser", "basic" ).toString(); mZipFileList.clear(); - QgsDebugMsg( QString( "path = %1 name= %2 scanZipSetting= %3" ).arg( path() ).arg( name() ).arg( scanZipSetting ) ); + QgsDebugMsg( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path() ).arg( name() ).arg( scanZipSetting ).arg( mVsiPrefix ) ); // if scanZipBrowser == no: skip to the next file if ( scanZipSetting == "no" ) @@ -901,31 +901,14 @@ QVector QgsZipItem::createChildren( ) // return children; // } - // get list of files inside zip file - QgsDebugMsg( QString( "Open file %1 with gdal vsi" ).arg( path() ) ); - char **papszSiblingFiles = VSIReadDirRecursive1( QString( "/vsizip/" + path() ).toLocal8Bit().constData() ); - if ( papszSiblingFiles ) - { - for ( int i = 0; i < CSLCount( papszSiblingFiles ); i++ ) - { - tmpPath = papszSiblingFiles[i]; - QgsDebugMsg( QString( "Read file %1" ).arg( tmpPath ) ); - // skip directories (files ending with /) - if ( tmpPath.right( 1 ) != "/" ) - mZipFileList << tmpPath; - } - CSLDestroy( papszSiblingFiles ); - } - else - { - QgsDebugMsg( QString( "Error reading %1" ).arg( path() ) ); - } + // first get list of files + getZipFileList(); // loop over files inside zip foreach( QString fileName, mZipFileList ) { QFileInfo info( fileName ); - tmpPath = "/vsizip/" + path() + "/" + fileName; + tmpPath = mVsiPrefix + path() + "/" + fileName; QgsDebugMsg( "tmpPath = " + tmpPath ); // foreach( dataItem_t *dataItem, mDataItemPtr ) @@ -934,7 +917,7 @@ QVector QgsZipItem::createChildren( ) // ugly hack to remove .dbf file if there is a .shp file if ( mProviderNames[i] == "ogr" ) { - if ( info.suffix() == "dbf" ) + if ( info.suffix().toLower() == "dbf" ) { if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 ) continue; @@ -976,20 +959,38 @@ QVector QgsZipItem::createChildren( ) return children; } - +QString QgsZipItem::vsiPrefix( QString path ) +{ + if ( path.startsWith( "/vsizip/", Qt::CaseInsensitive ) || + path.endsWith( ".zip", Qt::CaseInsensitive ) ) + return "/vsizip/"; + else if ( path.startsWith( "/vsitar/", Qt::CaseInsensitive ) || + path.endsWith( ".tar", Qt::CaseInsensitive ) || + path.endsWith( ".tar.gz", Qt::CaseInsensitive ) || + path.endsWith( ".tgz", Qt::CaseInsensitive ) ) + return "/vsitar/"; + else if ( path.startsWith( "/vsigzip/", Qt::CaseInsensitive ) || + path.endsWith( ".gz", Qt::CaseInsensitive ) ) + return "/vsigzip/"; + else + return ""; +} QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QString name ) { QSettings settings; QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser", "basic" ).toString(); - QString vsizipPath = path; + QString vsiPath = path; int zipFileCount = 0; + QStringList zipFileList; QFileInfo fileInfo( path ); + QString vsiPrefix = QgsZipItem::vsiPrefix( path ); QgsZipItem * zipItem = 0; + bool populated = false; - QgsDebugMsg( QString( "path = %1 name= %2 scanZipSetting= %3" ).arg( path ).arg( name ).arg( scanZipSetting ) ); + QgsDebugMsg( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path ).arg( name ).arg( scanZipSetting ).arg( vsiPrefix ) ); - // if scanZipBrowser == no: skip to the next file + // if scanZipBrowser == no: don't read the zip file if ( scanZipSetting == "no" ) { return 0; @@ -1007,29 +1008,47 @@ QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QStrin if ( zipItem ) { - // force populate zipItem - zipItem->populate(); - QgsDebugMsg( QString( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path() ).arg( zipItem->name() ) ); + // force populate zipItem if it has less than 10 items and is not a .tgz or .tar.gz file (slow loading) + // for other items populating will be delayed until item is opened + // this might be polluting the tree with empty items but is necessary for performance reasons + // could also accept all files smaller than a certain size and add options for file count and/or size + + // first get list of files inside .zip or .tar files + if ( path.endsWith( ".zip", Qt::CaseInsensitive ) || + path.endsWith( ".tar", Qt::CaseInsensitive ) ) + { + zipFileList = zipItem->getZipFileList(); + } + // force populate if less than 10 items + if ( zipFileList.count() > 0 && zipFileList.count() <= 10 ) + { + zipItem->populate(); + populated = true; // there is no QgsDataItem::isPopulated() function + QgsDebugMsg( QString( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path() ).arg( zipItem->name() ) ); + } + else + { + QgsDebugMsg( QString( "Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->path() ).arg( zipItem->name() ) ); + } } -// only display if has children -// other option would be to delay until item is opened, but we would be polluting the tree with empty items - if ( zipItem && zipItem->rowCount() > 1 ) + // only display if has children or if is not populated + if ( zipItem && ( !populated || zipItem->rowCount() > 1 ) ) { QgsDebugMsg( "returning zipItem" ); return zipItem; } -// if 1 or 0 child found, create a single data item using the normal path or the full path given by QgsZipItem + // if 1 or 0 child found, create a single data item using the normal path or the full path given by QgsZipItem else { if ( zipItem ) { - vsizipPath = zipItem->path(); - zipFileCount = zipItem->getZipFileList().count(); + vsiPath = zipItem->path(); + zipFileCount = zipFileList.count(); delete zipItem; } - QgsDebugMsg( QString( "will try to create a normal dataItem from path= %2 or %3" ).arg( path ).arg( vsizipPath ) ); + QgsDebugMsg( QString( "will try to create a normal dataItem from path= %2 or %3" ).arg( path ).arg( vsiPath ) ); // try to open using registered providers (gdal and ogr) for ( int i = 0; i < mProviderNames.size(); i++ ) @@ -1047,7 +1066,7 @@ QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QStrin item = dataItem( path, parent ); // try with /vsizip/ if ( ! item ) - item = dataItem( vsizipPath, parent ); + item = dataItem( vsiPath, parent ); if ( item ) return item; } @@ -1056,3 +1075,43 @@ QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QStrin return 0; } + +const QStringList & QgsZipItem::getZipFileList() +{ + if ( ! mZipFileList.isEmpty() ) + return mZipFileList; + + QString tmpPath; + QSettings settings; + QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser", "basic" ).toString(); + + QgsDebugMsg( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path() ).arg( name() ).arg( scanZipSetting ).arg( mVsiPrefix ) ); + + // if scanZipBrowser == no: skip to the next file + if ( scanZipSetting == "no" ) + { + return mZipFileList; + } + + // get list of files inside zip file + QgsDebugMsg( QString( "Open file %1 with gdal vsi" ).arg( mVsiPrefix + path() ) ); + char **papszSiblingFiles = VSIReadDirRecursive1( QString( mVsiPrefix + path() ).toLocal8Bit().constData() ); + if ( papszSiblingFiles ) + { + for ( int i = 0; i < CSLCount( papszSiblingFiles ); i++ ) + { + tmpPath = papszSiblingFiles[i]; + QgsDebugMsg( QString( "Read file %1" ).arg( tmpPath ) ); + // skip directories (files ending with /) + if ( tmpPath.right( 1 ) != "/" ) + mZipFileList << tmpPath; + } + CSLDestroy( papszSiblingFiles ); + } + else + { + QgsDebugMsg( QString( "Error reading %1" ).arg( path() ) ); + } + + return mZipFileList; +} diff --git a/src/core/qgsdataitem.h b/src/core/qgsdataitem.h index 51feab8173b..82fb70a8a8e 100644 --- a/src/core/qgsdataitem.h +++ b/src/core/qgsdataitem.h @@ -298,6 +298,7 @@ class CORE_EXPORT QgsZipItem : public QgsDataCollectionItem Q_OBJECT protected: + QString mVsiPrefix; QStringList mZipFileList; public: @@ -305,17 +306,17 @@ class CORE_EXPORT QgsZipItem : public QgsDataCollectionItem ~QgsZipItem(); QVector createChildren(); - QStringList getFiles(); + const QStringList & getZipFileList(); static QVector mDataItemPtr; static QStringList mProviderNames; + static QString vsiPrefix( QString uri ); + static QgsDataItem* itemFromPath( QgsDataItem* parent, QString path, QString name ); static const QIcon &iconZip(); - const QStringList & getZipFileList() const { return mZipFileList; } - }; #endif // QGSDATAITEM_H diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp index ea1f5254f2d..b5872451fec 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -642,6 +642,14 @@ QString QgsMapLayer::styleURI( ) // ideally we should look for .qml file inside zip file myURI.remove( 0, 8 ); } + else if ( myURI.startsWith( "/vsitar/", Qt::CaseInsensitive ) && + ( myURI.endsWith( ".tar", Qt::CaseInsensitive ) || + myURI.endsWith( ".tar.gz", Qt::CaseInsensitive ) || + myURI.endsWith( ".tgz", Qt::CaseInsensitive ) ) ) + { + // ideally we should look for .qml file inside tar file + myURI.remove( 0, 8 ); + } QFileInfo myFileInfo( myURI ); QString key; @@ -650,15 +658,18 @@ QString QgsMapLayer::styleURI( ) { // if file is using the /vsizip/ or /vsigzip/ mechanism, cleanup the name if ( myURI.endsWith( ".gz", Qt::CaseInsensitive ) ) - { myURI.chop( 3 ); - myFileInfo.setFile( myURI ); - } else if ( myURI.endsWith( ".zip", Qt::CaseInsensitive ) ) - { myURI.chop( 4 ); - myFileInfo.setFile( myURI ); - } + else if ( myURI.endsWith( ".tar", Qt::CaseInsensitive ) ) + myURI.chop( 4 ); + else if ( myURI.endsWith( ".tar.gz", Qt::CaseInsensitive ) ) + myURI.chop( 7 ); + else if ( myURI.endsWith( ".tgz", Qt::CaseInsensitive ) ) + myURI.chop( 4 ); + else if ( myURI.endsWith( ".gz", Qt::CaseInsensitive ) ) + myURI.chop( 3 ); + myFileInfo.setFile( myURI ); // get the file name for our .qml style file key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml"; } diff --git a/src/providers/gdal/qgsgdaldataitems.cpp b/src/providers/gdal/qgsgdaldataitems.cpp index eb57aa74d57..245742d7f8f 100644 --- a/src/providers/gdal/qgsgdaldataitems.cpp +++ b/src/providers/gdal/qgsgdaldataitems.cpp @@ -399,10 +399,10 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) // zip settings + info QSettings settings; QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser", "basic" ).toString(); - bool is_vsizip = ( thePath.startsWith( "/vsizip/" ) || - thePath.endsWith( ".zip", Qt::CaseInsensitive ) ); - bool is_vsigzip = ( thePath.startsWith( "/vsigzip/" ) || - thePath.endsWith( ".gz", Qt::CaseInsensitive ) ); + QString vsiPrefix = QgsZipItem::vsiPrefix( thePath ); + bool is_vsizip = ( vsiPrefix == "/vsizip/" ); + bool is_vsigzip = ( vsiPrefix == "/vsigzip/" ); + bool is_vsitar = ( vsiPrefix == "/vsitar/" ); // get suffix, removing .gz if present QString tmpPath = thePath; //path used for testing, not for layer creation @@ -414,10 +414,11 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) info.setFile( thePath ); QString name = info.fileName(); - QgsDebugMsg( "thePath= " + thePath + " tmpPath= " + tmpPath + " name= " + name + " suffix= " + suffix ); + QgsDebugMsg( "thePath= " + thePath + " tmpPath= " + tmpPath + " name= " + name + + " suffix= " + suffix + " vsiPrefix= " + vsiPrefix ); // allow only normal files or VSIFILE items to continue - if ( !info.isFile() && !is_vsizip && !is_vsigzip ) + if ( !info.isFile() && vsiPrefix == "" ) return 0; // get supported extensions @@ -434,10 +435,6 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) !extensions.contains( "aux.xml" ) ) return 0; - // skip .tar.gz files - if ( thePath.endsWith( ".tar.gz", Qt::CaseInsensitive ) ) - return 0; - // Filter files by extension if ( !extensions.contains( suffix ) ) { @@ -455,30 +452,27 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) return 0; } - // add /vsizip/ or /vsigzip/ to path if file extension is .zip or .gz - if ( is_vsigzip ) + // fix vsifile path and name + if ( vsiPrefix != "" ) { - if ( !thePath.startsWith( "/vsigzip/" ) ) - thePath = "/vsigzip/" + thePath; - } - else if ( is_vsizip ) - { - if ( !thePath.startsWith( "/vsizip/" ) ) - thePath = "/vsizip/" + thePath; + // add vsiPrefix to path if needed + if ( !thePath.startsWith( vsiPrefix ) ) + thePath = vsiPrefix + thePath; // if this is a /vsigzip/path_to_zip.zip/file_inside_zip remove the full path from the name - if ( thePath != "/vsizip/" + parentItem->path() ) + if (( is_vsizip || is_vsitar ) && ( thePath != vsiPrefix + parentItem->path() ) ) { name = thePath; - name = name.replace( "/vsizip/" + parentItem->path() + "/", "" ); + name = name.replace( vsiPrefix + parentItem->path() + "/", "" ); } } // return a /vsizip/ item without testing if: // zipfile and scan zip == "Basic scan" // not zipfile and scan items == "Check extension" - if (( is_vsizip && scanZipSetting == "basic" ) || - ( !is_vsizip && ( settings.value( "/qgis/scanItemsInBrowser", - "extension" ).toString() == "extension" ) ) ) + if ((( is_vsizip || is_vsitar ) && scanZipSetting == "basic" ) || + ( !is_vsizip && !is_vsitar && + ( settings.value( "/qgis/scanItemsInBrowser", + "extension" ).toString() == "extension" ) ) ) { // if this is a VRT file make sure it is raster VRT to avoid duplicates if ( suffix == "vrt" ) diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index 0ea91bf17cf..512229de43f 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -111,19 +111,12 @@ QgsGdalProvider::QgsGdalProvider( QString const & uri ) mGdalDataset = NULL; // Try to open using VSIFileHandler (see qgsogrprovider.cpp) - // TODO suppress error messages and report in debug, like in OGR provider - // TODO use the file name of the file inside the zip, needs unzip.h - if ( uri.endsWith( ".zip", Qt::CaseInsensitive ) ) + QString vsiPrefix = QgsZipItem::vsiPrefix( uri ); + if ( vsiPrefix != "" ) { - if ( !uri.startsWith( "/vsizip/" ) ) - setDataSourceUri( "/vsizip/" + uri ); - QgsDebugMsg( QString( "Trying /vsizip syntax, uri= %1" ).arg( dataSourceUri() ) ); - } - else if ( uri.endsWith( ".gz", Qt::CaseInsensitive ) ) - { - if ( !uri.startsWith( "/vsigzip/" ) ) - setDataSourceUri( "/vsigzip/" + uri ); - QgsDebugMsg( QString( "Trying /vsigzip syntax, uri= %1" ).arg( dataSourceUri() ) ); + if ( !uri.startsWith( vsiPrefix ) ) + setDataSourceUri( vsiPrefix + uri ); + QgsDebugMsg( QString( "Trying %1 syntax, uri= %2" ).arg( vsiPrefix ).arg( dataSourceUri() ) ); } // The uri is either a file name or encoded parameters for WCS @@ -1920,8 +1913,9 @@ void buildSupportedRasterFileFilterAndExtensions( QString & theFileFiltersString { QString glob = "*.zip"; glob += " *.gz"; + glob += " *.tar *.tar.gz *.tgz"; theFileFiltersString += ";;[GDAL] " + QObject::tr( "GDAL/OGR VSIFileHandler" ) + " (" + glob.toLower() + " " + glob.toUpper() + ")"; - theExtensions << "zip" << "gz"; + theExtensions << "zip" << "gz" << "tar" << "tar.gz" << "tgz"; } #endif @@ -1940,21 +1934,12 @@ QGISEXTERN bool isValidRasterFileName( QString const & theFileNameQString, QStri // Try to open using VSIFileHandler (see qgsogrprovider.cpp) // TODO suppress error messages and report in debug, like in OGR provider - if ( fileName.endsWith( ".zip", Qt::CaseInsensitive ) ) + QString vsiPrefix = QgsZipItem::vsiPrefix( fileName ); + if ( vsiPrefix != "" ) { - if ( !fileName.startsWith( "/vsizip/" ) ) - { - fileName = "/vsizip/" + fileName; - } - QgsDebugMsg( QString( "Trying /vsizip syntax, fileName= %1" ).arg( fileName ) ); - } - if ( fileName.endsWith( ".gz", Qt::CaseInsensitive ) ) - { - if ( !fileName.startsWith( "/vsigzip/" ) ) - { - fileName = "/vsigzip/" + fileName; - } - QgsDebugMsg( QString( "Trying /vsigzip syntax, fileName= %1" ).arg( fileName ) ); + if ( !fileName.startsWith( vsiPrefix ) ) + fileName = vsiPrefix + fileName; + QgsDebugMsg( QString( "Trying %1 syntax, fileName= %2" ).arg( vsiPrefix ).arg( fileName ) ); } //open the file using gdal making sure we have handled locale properly diff --git a/src/providers/ogr/qgsogrdataitems.cpp b/src/providers/ogr/qgsogrdataitems.cpp index 9f29b99f778..154375d1887 100644 --- a/src/providers/ogr/qgsogrdataitems.cpp +++ b/src/providers/ogr/qgsogrdataitems.cpp @@ -240,10 +240,10 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) // zip settings + info QSettings settings; QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser", "basic" ).toString(); - bool is_vsizip = ( thePath.startsWith( "/vsizip/" ) || - thePath.endsWith( ".zip", Qt::CaseInsensitive ) ); - bool is_vsigzip = ( thePath.startsWith( "/vsigzip/" ) || - thePath.endsWith( ".gz", Qt::CaseInsensitive ) ); + QString vsiPrefix = QgsZipItem::vsiPrefix( thePath ); + bool is_vsizip = ( vsiPrefix == "/vsizip/" ); + bool is_vsigzip = ( vsiPrefix == "/vsigzip/" ); + bool is_vsitar = ( vsiPrefix == "/vsitar/" ); // get suffix, removing .gz if present QString tmpPath = thePath; //path used for testing, not for layer creation @@ -255,10 +255,11 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) info.setFile( thePath ); QString name = info.fileName(); - QgsDebugMsg( "thePath= " + thePath + " tmpPath= " + tmpPath + " name= " + name + " suffix= " + suffix ); + QgsDebugMsg( "thePath= " + thePath + " tmpPath= " + tmpPath + " name= " + name + + " suffix= " + suffix + " vsiPrefix= " + vsiPrefix ); // allow only normal files or VSIFILE items to continue - if ( !info.isFile() && !is_vsizip && !is_vsigzip ) + if ( !info.isFile() && vsiPrefix == "" ) return 0; QStringList myExtensions = fileExtensions(); @@ -272,10 +273,6 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) !myExtensions.contains( "shp.xml" ) ) return 0; - // skip .tar.gz files - if ( thePath.endsWith( ".tar.gz", Qt::CaseInsensitive ) ) - return 0; - // We have to filter by extensions, otherwise e.g. all Shapefile files are displayed // because OGR drive can open also .dbf, .shx. if ( myExtensions.indexOf( suffix ) < 0 ) @@ -302,30 +299,27 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) return 0; } - // add /vsizip/ or /vsigzip/ to path if file extension is .zip or .gz - if ( is_vsigzip ) + // fix vsifile path and name + if ( vsiPrefix != "" ) { - if ( !thePath.startsWith( "/vsigzip/" ) ) - thePath = "/vsigzip/" + thePath; - } - else if ( is_vsizip ) - { - if ( !thePath.startsWith( "/vsizip/" ) ) - thePath = "/vsizip/" + thePath; + // add vsiPrefix to path if needed + if ( !thePath.startsWith( vsiPrefix ) ) + thePath = vsiPrefix + thePath; // if this is a /vsigzip/path_to_zip.zip/file_inside_zip remove the full path from the name - if ( thePath != "/vsizip/" + parentItem->path() ) + if (( is_vsizip || is_vsitar ) && ( thePath != vsiPrefix + parentItem->path() ) ) { name = thePath; - name = name.replace( "/vsizip/" + parentItem->path() + "/", "" ); + name = name.replace( vsiPrefix + parentItem->path() + "/", "" ); } } // return a /vsizip/ item without testing if: // zipfile and scan zip == "Basic scan" // not zipfile and scan items == "Check extension" - if (( is_vsizip && scanZipSetting == "basic" ) || - ( !is_vsizip && ( settings.value( "/qgis/scanItemsInBrowser", - "extension" ).toString() == "extension" ) ) ) + if ((( is_vsizip || is_vsitar ) && scanZipSetting == "basic" ) || + ( !is_vsizip && !is_vsitar && + ( settings.value( "/qgis/scanItemsInBrowser", + "extension" ).toString() == "extension" ) ) ) { // if this is a VRT file make sure it is vector VRT to avoid duplicates if ( suffix == "vrt" ) diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index b4049e57a16..86beb6b93d5 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -264,26 +264,18 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri ) // Try to open using VSIFileHandler // see http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip - if ( mFilePath.endsWith( ".zip", Qt::CaseInsensitive ) ) + QString vsiPrefix = QgsZipItem::vsiPrefix( uri ); + if ( vsiPrefix != "" ) { // GDAL>=1.8.0 has write support for zip, but read and write operations // cannot be interleaved, so for now just use read-only. openReadOnly = true; - if ( !mFilePath.startsWith( "/vsizip/" ) ) + if ( !mFilePath.startsWith( vsiPrefix ) ) { - mFilePath = "/vsizip/" + mFilePath; + mFilePath = vsiPrefix + mFilePath; setDataSourceUri( mFilePath ); } - QgsDebugMsg( QString( "Trying /vsizip syntax, mFilePath= %1" ).arg( mFilePath ) ); - } - else if ( mFilePath.endsWith( ".gz", Qt::CaseInsensitive ) ) - { - if ( !mFilePath.startsWith( "/vsigzip/" ) ) - { - mFilePath = "/vsigzip/" + mFilePath; - setDataSourceUri( mFilePath ); - } - QgsDebugMsg( QString( "Trying /vsigzip syntax, mFilePath= %1" ).arg( mFilePath ) ); + QgsDebugMsg( QString( "Trying %1 syntax, mFilePath= %2" ).arg( vsiPrefix ).arg( mFilePath ) ); } QgsDebugMsg( "mFilePath: " + mFilePath ); @@ -1792,16 +1784,14 @@ QString createFilters( QString type ) // VSIFileHandler (.zip and .gz files) // see http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip // Requires GDAL>=1.6.0 with libz support, let's assume we have it. - // For .zip this works only if there is one file (or dataset) in the root of the zip. - // Only tested with tiff, shape (zip) and spatialite (zip and gz). // This does not work for some file types, see VSIFileHandler doc. - // Ideally we should also add support for /vsitar/ (requires cpl_vsil_tar.cpp). #if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1600 QSettings settings; if ( settings.value( "/qgis/scanZipInBrowser", "basic" ).toString() != "no" ) { - myFileFilters += createFileFilter_( QObject::tr( "GDAL/OGR VSIFileHandler" ), "*.zip *.gz" ); - myExtensions << "zip" << "gz"; + myFileFilters += createFileFilter_( QObject::tr( "GDAL/OGR VSIFileHandler" ), "*.zip *.gz *.tar *.tar.gz *.tgz" ); + myExtensions << "zip" << "gz" << "tar" << "tar.gz" << "tgz"; + } #endif diff --git a/tests/src/core/testziplayer.cpp b/tests/src/core/testziplayer.cpp index b4efc9c1f22..cc5991a697b 100644 --- a/tests/src/core/testziplayer.cpp +++ b/tests/src/core/testziplayer.cpp @@ -53,6 +53,7 @@ class TestZipLayer: public QObject bool testZipItem( QString myFileName, QString myChildName = "", QString myDriverName = "" ); // get layer transparency to test for .qml loading int getLayerTransparency( QString myFileName, QString myProviderKey, QString myScanZipSetting = "basic" ); + bool testZipItemTransparency( QString myFileName, QString myProviderKey, int myTarget ); private slots: @@ -65,20 +66,28 @@ class TestZipLayer: public QObject // tests // test for .zip and .gz files using all options void testPassthruVectorZip(); + void testPassthruVectorTar(); void testPassthruVectorGzip(); void testPassthruRasterZip(); + void testPassthruRasterTar(); void testPassthruRasterGzip(); // test both "Basic Scan" and "Full scan" for .zip files void testZipItemRaster(); + void testTarItemRaster(); void testZipItemVector(); + void testTarItemVector(); void testZipItemAll(); + void testTarItemAll(); // test that styles are loaded from .qml files outside zip files void testZipItemVectorTransparency(); - void testGZipItemVectorTransparency(); + void testTarItemVectorTransparency(); + void testGzipItemVectorTransparency(); void testZipItemRasterTransparency(); - void testGZipItemRasterTransparency(); + void testTarItemRasterTransparency(); + void testGzipItemRasterTransparency(); //make sure items inside subfolders can be read void testZipItemSubfolder(); + void testTarItemSubfolder(); //make sure .vrt items are loaded by proper provider (gdal/ogr) void testZipItemVRT(); }; @@ -225,6 +234,21 @@ int TestZipLayer::getLayerTransparency( QString myFileName, QString myProviderKe return myTransparency; } +bool TestZipLayer::testZipItemTransparency( QString myFileName, QString myProviderKey, int myTarget ) +{ + int myTransparency; + foreach( QString s, mScanZipSettings ) + { + myTransparency = getLayerTransparency( myFileName, myProviderKey, s ); + if ( myTransparency != myTarget ) + { + QWARN( QString( "Transparency of %1 is %2, should be %3" ).arg( myFileName ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); + return false; + } + } + return true; +} + // slots void TestZipLayer::initTestCase() @@ -257,7 +281,6 @@ void TestZipLayer::cleanupTestCase() settings.setValue( "/qgis/scanZipInBrowser", mScanZipSetting ); } - void TestZipLayer::testPassthruVectorZip() { QSettings settings; @@ -275,6 +298,21 @@ void TestZipLayer::testPassthruVectorZip() } } +void TestZipLayer::testPassthruVectorTar() +{ + QSettings settings; + QString myFileName = mDataDir + "points2.tar"; +#if GDAL_VERSION_NUM < 1800 + QSKIP( "This test requires GDAL >= 1.8", SkipSingle ); +#endif + foreach( QString s, mScanZipSettings ) + { + settings.setValue( "/qgis/scanZipInBrowser", s ); + QVERIFY( s == settings.value( "/qgis/scanZipInBrowser" ).toString() ); + QVERIFY( testZipItemPassthru( myFileName, "ogr" ) ); + } +} + void TestZipLayer::testPassthruVectorGzip() { QSettings settings; @@ -300,6 +338,20 @@ void TestZipLayer::testPassthruRasterZip() } } +void TestZipLayer::testPassthruRasterTar() +{ +#if GDAL_VERSION_NUM < 1800 + QSKIP( "This test requires GDAL >= 1.8", SkipSingle ); +#endif + QSettings settings; + foreach( QString s, mScanZipSettings ) + { + settings.setValue( "/qgis/scanZipInBrowser", s ); + QVERIFY( s == settings.value( "/qgis/scanZipInBrowser" ).toString() ); + QVERIFY( testZipItemPassthru( mDataDir + "landsat_b1.tar", "gdal" ) ); + } +} + void TestZipLayer::testPassthruRasterGzip() { QSettings settings; @@ -322,6 +374,20 @@ void TestZipLayer::testZipItemRaster() } } +void TestZipLayer::testTarItemRaster() +{ +#if GDAL_VERSION_NUM < 1800 + QSKIP( "This test requires GDAL >= 1.8", SkipSingle ); +#endif + QSettings settings; + foreach( QString s, mScanZipSettings ) + { + settings.setValue( "/qgis/scanZipInBrowser", s ); + QVERIFY( s == settings.value( "/qgis/scanZipInBrowser" ).toString() ); + QVERIFY( testZipItem( mDataDir + "testtar.tgz", "landsat_b1.tif" ) ); + } +} + void TestZipLayer::testZipItemVector() { QSettings settings; @@ -333,6 +399,20 @@ void TestZipLayer::testZipItemVector() } } +void TestZipLayer::testTarItemVector() +{ +#if GDAL_VERSION_NUM < 1800 + QSKIP( "This test requires GDAL >= 1.8", SkipSingle ); +#endif + QSettings settings; + foreach( QString s, mScanZipSettings ) + { + settings.setValue( "/qgis/scanZipInBrowser", s ); + QVERIFY( s == settings.value( "/qgis/scanZipInBrowser" ).toString() ); + QVERIFY( testZipItem( mDataDir + "testtar.tgz", "points.shp" ) ); + } +} + void TestZipLayer::testZipItemAll() { // test file contains invalid items (tmp1.tif, tmp1.txt and tmp1.xml) @@ -345,55 +425,57 @@ void TestZipLayer::testZipItemAll() QVERIFY( testZipItem( mDataDir + "testzip.zip", "" ) ); } +void TestZipLayer::testTarItemAll() +{ +#if GDAL_VERSION_NUM < 1800 + QSKIP( "This test requires GDAL >= 1.8", SkipSingle ); +#endif + QSettings settings; + settings.setValue( "/qgis/scanZipInBrowser", "full" ); + QVERIFY( "full" == settings.value( "/qgis/scanZipInBrowser" ).toString() ); + QVERIFY( testZipItem( mDataDir + "testtar.tgz", "" ) ); +} void TestZipLayer::testZipItemVectorTransparency() { #if GDAL_VERSION_NUM < 1800 QSKIP( "This test requires GDAL >= 1.8", SkipSingle ); #endif - int myTarget = 250; - int myTransparency; - foreach( QString s, mScanZipSettings ) - { - myTransparency = getLayerTransparency( mDataDir + "points2.zip", "ogr", "basic" ); - QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); - } + QVERIFY( testZipItemTransparency( mDataDir + "points2.zip", "ogr", 250 ) ); } -void TestZipLayer::testGZipItemVectorTransparency() +void TestZipLayer::testTarItemVectorTransparency() +{ +#if GDAL_VERSION_NUM < 1800 + QSKIP( "This test requires GDAL >= 1.8", SkipSingle ); +#endif + QVERIFY( testZipItemTransparency( mDataDir + "points2.tar", "ogr", 250 ) ); +} + +void TestZipLayer::testGzipItemVectorTransparency() { #if GDAL_VERSION_NUM < 1700 QSKIP( "This test requires GDAL >= 1.7", SkipSingle ); #endif - int myTarget = 250; - int myTransparency; - foreach( QString s, mScanZipSettings ) - { - myTransparency = getLayerTransparency( mDataDir + "points3.geojson.gz", "ogr", s ); - QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); - } + QVERIFY( testZipItemTransparency( mDataDir + "points3.geojson.gz", "ogr", 250 ) ); } void TestZipLayer::testZipItemRasterTransparency() { - int myTarget = 250; - int myTransparency; - foreach( QString s, mScanZipSettings ) - { - myTransparency = getLayerTransparency( mDataDir + "landsat_b1.zip", "gdal", s ); - QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); - } + QVERIFY( testZipItemTransparency( mDataDir + "landsat_b1.zip", "gdal", 250 ) ); } -void TestZipLayer::testGZipItemRasterTransparency() +void TestZipLayer::testTarItemRasterTransparency() { - int myTarget = 250; - int myTransparency; - foreach( QString s, mScanZipSettings ) - { - myTransparency = getLayerTransparency( mDataDir + "landsat_b1.tif.gz", "gdal", s ); - QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); - } +#if GDAL_VERSION_NUM < 1800 + QSKIP( "This test requires GDAL >= 1.8", SkipSingle ); +#endif + QVERIFY( testZipItemTransparency( mDataDir + "landsat_b1.tar", "gdal", 250 ) ); +} + +void TestZipLayer::testGzipItemRasterTransparency() +{ + QVERIFY( testZipItemTransparency( mDataDir + "landsat_b1.tif.gz", "gdal", 250 ) ); } void TestZipLayer::testZipItemSubfolder() @@ -407,6 +489,17 @@ void TestZipLayer::testZipItemSubfolder() } } +void TestZipLayer::testTarItemSubfolder() +{ + QSettings settings; + foreach( QString s, mScanZipSettings ) + { + settings.setValue( "/qgis/scanZipInBrowser", s ); + QVERIFY( s == settings.value( "/qgis/scanZipInBrowser" ).toString() ); + QVERIFY( testZipItem( mDataDir + "testtar.tgz", "folder/folder2/landsat_b2.tif" ) ); + } +} + void TestZipLayer::testZipItemVRT() { diff --git a/tests/testdata/landsat_b1.tar b/tests/testdata/landsat_b1.tar new file mode 100644 index 0000000000000000000000000000000000000000..a97f57a5a51cbcffcb3169d197ceb2c2f42b03a8 GIT binary patch literal 51200 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 literal 0 HcmV?d00001