diff --git a/images/images.qrc b/images/images.qrc index 8f435e8357d..fbf67f1702a 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -193,6 +193,7 @@ themes/default/mIconWms.png themes/default/mIconWmsLayer.png themes/default/mIconWarn.png + themes/default/mIconZip.png themes/default/mMapserverExport.png themes/default/plugin.png themes/default/propertyicons/action.png diff --git a/images/themes/default/mIconZip.png b/images/themes/default/mIconZip.png new file mode 100644 index 00000000000..9934c351913 Binary files /dev/null and b/images/themes/default/mIconZip.png differ diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index 7a409f5c978..34244f43e31 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -196,6 +196,14 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WFlags fl ) : cmbPromptRasterSublayers->addItem( tr( "Load all" ) ); cmbPromptRasterSublayers->setCurrentIndex( settings.value( "/qgis/promptForRasterSublayers", 0 ).toInt() ); + // Scan for contents of compressed files (.zip) in browser dock + cmbScanZipInBrowser->clear(); + cmbScanZipInBrowser->addItem( tr( "No" ) ); // 0 + cmbScanZipInBrowser->addItem( tr( "Basic scan" ) ); // 1 + cmbScanZipInBrowser->addItem( tr( "Passthru" ) ); // 2 + cmbScanZipInBrowser->addItem( tr( "Full scan" ) ); // 3 + cmbScanZipInBrowser->setCurrentIndex( settings.value( "/qgis/scanZipInBrowser", 1 ).toInt() ); + // set the display update threshold spinBoxUpdateThreshold->setValue( settings.value( "/Map/updateThreshold" ).toInt() ); //set the default projection behaviour radio buttongs @@ -628,6 +636,7 @@ void QgsOptions::saveOptions() settings.setValue( "/qgis/attributeTableBehaviour", cmbAttrTableBehaviour->currentIndex() ); settings.setValue( "/qgis/attributeTableRowCache", spinBoxAttrTableRowCache->value() ); settings.setValue( "/qgis/promptForRasterSublayers", cmbPromptRasterSublayers->currentIndex() ); + settings.setValue( "/qgis/scanZipInBrowser", cmbScanZipInBrowser->currentIndex() ); settings.setValue( "/qgis/dockIdentifyResults", cbxIdentifyResultsDocked->isChecked() ); settings.setValue( "/qgis/dockSnapping", cbxSnappingOptionsDocked->isChecked() ); settings.setValue( "/qgis/addPostgisDC", cbxAddPostgisDC->isChecked() ); diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp index e60548440ac..181545909a8 100644 --- a/src/core/qgsdataitem.cpp +++ b/src/core/qgsdataitem.cpp @@ -35,6 +35,10 @@ #include "qgslogger.h" #include "qgsproviderregistry.h" +// use internal quazip for /vsizip support +#define QUAZIP_STATIC +#include + // shared icons const QIcon &QgsLayerItem::iconPoint() { @@ -125,14 +129,25 @@ const QIcon &QgsDataCollectionItem::iconDir() const QIcon &QgsFavouritesItem::iconFavourites() { static QIcon icon; - + if ( icon.isNull() ) icon = QIcon( getThemePixmap( "/mIconFavourites.png" ) ); - // this icon added by ET, modfied mIconNew and set colour to that of folder icon + + return icon; +} + +const QIcon &QgsZipItem::iconZip() +{ + static QIcon icon; + + if ( icon.isNull() ) + icon = QIcon( getThemePixmap( "/mIconZip.png" ) ); +// icon from http://www.softicons.com/free-icons/application-icons/mega-pack-icons-1-by-nikolay-verin/winzip-folder-icon return icon; } + QgsDataItem::QgsDataItem( QgsDataItem::Type type, QgsDataItem* parent, QString name, QString path ) : QObject( parent ), mType( type ), mParent( parent ), mPopulated( false ), mName( name ), mPath( path ) { @@ -144,8 +159,10 @@ QPixmap QgsDataItem::getThemePixmap( const QString theName ) { QString myPreferredPath = QgsApplication::activeThemePath() + QDir::separator() + theName; QString myDefaultPath = QgsApplication::defaultThemePath() + QDir::separator() + theName; - //QgsDebugMsg( "myPreferredPath = " + myPreferredPath ); - //QgsDebugMsg( "myDefaultPath = " + myDefaultPath ); + + // QgsDebugMsg( "theName = " + theName ); + // QgsDebugMsg( "myPreferredPath = " + myPreferredPath ); + // QgsDebugMsg( "myDefaultPath = " + myDefaultPath ); if ( QFile::exists( myPreferredPath ) ) { return QPixmap( myPreferredPath ); @@ -214,7 +231,7 @@ bool QgsDataItem::hasChildren() void QgsDataItem::addChildItem( QgsDataItem * child, bool refresh ) { - QgsDebugMsg( QString( "add child #%1 - %2" ).arg( mChildren.size() ).arg( child->mName ) ); + QgsDebugMsg( QString( "add child #%1 - %2 - %3" ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ) ); int i; if ( type() == Directory ) @@ -334,6 +351,8 @@ QgsLayerItem::QgsLayerItem( QgsDataItem* parent, QString name, QString path, QSt case Point: mIcon = iconPoint(); break; case Line: mIcon = iconLine(); break; case Polygon: mIcon = iconPolygon(); break; + // TODO add a new icon for generic Vector layers + case Vector : mIcon = iconPolygon(); break; case TableLayer: mIcon = iconTable(); break; case Raster: mIcon = iconRaster(); break; default: mIcon = iconDefault(); break; @@ -373,7 +392,7 @@ QgsDataCollectionItem::~QgsDataCollectionItem() } //----------------------------------------------------------------------- -QVector QgsDirectoryItem::mProviders = QVector(); +// QVector QgsDirectoryItem::mProviders = QVector(); QVector QgsDirectoryItem::mLibraries = QVector(); @@ -406,6 +425,7 @@ QgsDirectoryItem::QgsDirectoryItem( QgsDataItem* parent, QString name, QString p QgsDebugMsg( library->fileName() + " has NoDataCapabilities" ); continue; } + QgsDebugMsg( QString( "%1 dataCapabilities : %2" ).arg( library->fileName() ).arg( dataCapabilities() ) ); mLibraries.append( library ); } @@ -425,11 +445,14 @@ QVector QgsDirectoryItem::createChildren( ) { QVector children; QDir dir( mPath ); + QSettings settings; + bool scanZip = ( settings.value( "/qgis/scanZipInBrowser", 1 ).toInt() != 0 ); + QStringList entries = dir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase ); foreach( QString subdir, entries ) { QString subdirPath = dir.absoluteFilePath( subdir ); - qDebug( "creating subdir: %s", subdirPath.toAscii().data() ); + QgsDebugMsg( QString( "creating subdir: %1" ).arg( subdirPath ) ); QgsDirectoryItem *item = new QgsDirectoryItem( this, subdir, subdirPath ); // propagate signals up to top @@ -442,6 +465,20 @@ QVector QgsDirectoryItem::createChildren( ) { QString path = dir.absoluteFilePath( name ); QFileInfo fileInfo( path ); + + // vsizip support was added to GDAL/OGR 1.6 but this symbol not available here +// #if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1600 + if ( fileInfo.suffix() == "zip" && scanZip ) + { + QgsDataItem * item = QgsZipItem::itemFromPath( this, path, name ); + if ( item ) + { + children.append( item ); + continue; + } + } +// #endif + foreach( QLibrary *library, mLibraries ) { // we could/should create separate list of providers for each purpose @@ -475,6 +512,7 @@ QVector QgsDirectoryItem::createChildren( ) } } } + return children; } @@ -669,3 +707,238 @@ QVector QgsFavouritesItem::createChildren( ) return children; } + +//----------------------------------------------------------------------- +QStringList QgsZipItem::mProviderNames = QStringList(); +QVector QgsZipItem::mDataItemPtr = QVector(); + + +QgsZipItem::QgsZipItem( QgsDataItem* parent, QString name, QString path ) + : QgsDataCollectionItem( parent, name, path ) +{ + mType = Collection; //Zip?? + mIcon = iconZip(); + + if ( mProviderNames.size() == 0 ) + { + // QStringList keys = QgsProviderRegistry::instance()->providerList(); + // only use GDAL and OGR providers as we use the VSIFILE mechanism + QStringList keys; + // keys << "ogr" << "gdal"; + keys << "gdal" << "ogr"; + + QStringList::const_iterator i; + for ( i = keys.begin(); i != keys.end(); ++i ) + { + QString k( *i ); + QgsDebugMsg( "provider " + k ); + // some providers hangs with empty uri (Postgis) etc... + // -> using libraries directly + QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( k ); + if ( library ) + { + dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) ); + if ( !dataCapabilities ) + { + QgsDebugMsg( library->fileName() + " does not have dataCapabilities" ); + continue; + } + if ( dataCapabilities() == QgsDataProvider::NoDataCapabilities ) + { + QgsDebugMsg( library->fileName() + " has NoDataCapabilities" ); + continue; + } + QgsDebugMsg( QString( "%1 dataCapabilities : %2" ).arg( library->fileName() ).arg( dataCapabilities() ) ); + + dataItem_t * dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) ); + if ( ! dataItem ) + { + QgsDebugMsg( library->fileName() + " does not have dataItem" ); + continue; + } + + // mLibraries.append( library ); + mDataItemPtr.append( dataItem ); + mProviderNames.append( k ); + } + else + { + //QgsDebugMsg ( "Cannot get provider " + k ); + } + } + } + +} + +QgsZipItem::~QgsZipItem() +{ +} + +QVector QgsZipItem::createChildren( ) +{ + QVector children; + QStringList fileList; + QString tmpPath; + QString childPath; + + QSettings settings; + int scanZipSetting = settings.value( "/qgis/scanZipInBrowser", 1 ).toInt(); + + if ( scanZipSetting == 0 ) + { + return children; + } + // if scanZipBrowser == "Passthru": do not scan zip and allow to open directly with /vsigzip/ + if ( scanZipSetting == 2 ) + { + mPath = "/vsizip/" + path(); // should check for extension + QgsDebugMsg( "set path to " + path() ); + return children; + } + + // get list of files inside zip file + QuaZip zip( path() ); + if ( ! zip.open( QuaZip::mdUnzip ) || ! zip.isOpen() ) + { + QgsDebugMsg( QString( "Zip error: %1" ).arg( zip.getZipError() ) ); + } + else + { + for ( bool more = zip.goToFirstFile(); more; more = zip.goToNextFile() ) + { + // tmpPath = "/vsizip/" + path() + "/" + zip.getCurrentFileName(); + // QgsDebugMsg( QString( "file %1 - %2" ).arg( zip.getCurrentFileName() ).arg( tmpPath ) ); + tmpPath = zip.getCurrentFileName(); + // skip directories (files ending with /) + if ( tmpPath.right( 1 ) != "/" ) + fileList << tmpPath; + } + } + zip.close(); + if ( zip.getZipError() != UNZ_OK ) + { + QgsDebugMsg( QString( "Zip error: %1" ).arg( zip.getZipError() ) ); + } + + // loop over files inside zip + foreach( QString fileName, fileList ) + { + QFileInfo info( fileName ); + tmpPath = "/vsizip/" + path() + "/" + fileName; + QgsDebugMsg( "tmpPath = " + tmpPath ); + + // foreach( dataItem_t *dataItem, mDataItemPtr ) + for ( int i = 0; i < mProviderNames.size(); i++ ) + { + // ugly hack to remove .dbf file if there is a .shp file + if ( mProviderNames[i] == "ogr" ) + { + if ( info.suffix() == "dbf" ) + { + if ( fileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 ) + continue; + } + if ( info.completeSuffix().toLower() == "shp.xml" ) + { + continue; + } + } + + // try to get data item from provider + dataItem_t *dataItem = mDataItemPtr[i]; + if ( dataItem ) + { + QgsDebugMsg( QString( "trying to load item %1 with %2" ).arg( tmpPath ).arg( mProviderNames[i] ) ); + QgsDataItem * item = dataItem( tmpPath, this ); + if ( item ) + { + QgsDebugMsg( "loaded item" ); + childPath = tmpPath; + children.append( item ); + break; + } + else + { + QgsDebugMsg( "not loaded item" ); + } + } + } + + } + + if ( children.size() == 1 ) + { + // save the name of the only child so we can get a normal data item from it + mPath = childPath; + } + + return children; +} + + + +QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QString name ) +{ + + QSettings settings; + int scanZipSetting = settings.value( "/qgis/scanZipInBrowser", 1 ).toInt(); + QFileInfo fileInfo( path ); + QString tmpPath = path; + QgsZipItem * zipItem = 0; + + QgsDebugMsg( "path = " + path + " name= " + name ); + + // if scanZipBrowser == 0 (No): skip to the next file + if ( scanZipSetting == 0 ) + { + return 0; + } + // if scanZipBrowser == 2 (Passthru): do not scan zip and allow to open directly with /vsigzip/ + else if ( scanZipSetting == 2 ) + { + tmpPath = "/vsizip/" + path; + zipItem = 0; + } + else + { + zipItem = new QgsZipItem( parent, name, path ); + } + + if ( zipItem ) + { + QgsDebugMsg( QString( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).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 ) + { + QgsDebugMsg( "returning zipItem" ); + return zipItem; + } + // if 1 or 0 child found, create a data item using the full path given by QgsZipItem + else + { + if ( zipItem ) + { + tmpPath = zipItem->path(); + delete zipItem; + } + + QgsDebugMsg( QString( "will try to create a normal dataItem from path= %2" ).arg( tmpPath ) ); + + // try to open using registered providers (gdal and ogr) + for ( int i = 0; i < mProviderNames.size(); i++ ) + { + dataItem_t *dataItem = mDataItemPtr[i]; + if ( dataItem ) + { + QgsDataItem *item = dataItem( tmpPath, parent ); + if ( item ) + return item; + } + } + } + + return 0; +} diff --git a/src/core/qgsdataitem.h b/src/core/qgsdataitem.h index 388a0eee109..c24b80740e4 100644 --- a/src/core/qgsdataitem.h +++ b/src/core/qgsdataitem.h @@ -36,7 +36,6 @@ typedef int dataCapabilities_t(); typedef QgsDataItem * dataItem_t( QString, QgsDataItem* ); - /** base class for all items in the model */ class CORE_EXPORT QgsDataItem : public QObject { @@ -236,7 +235,7 @@ class CORE_EXPORT QgsDirectoryItem : public QgsDataCollectionItem virtual QWidget * paramWidget(); - static QVector mProviders; + /* static QVector mProviders; */ static QVector mLibraries; }; @@ -281,8 +280,28 @@ class CORE_EXPORT QgsFavouritesItem : public QgsDataCollectionItem ~QgsFavouritesItem(); QVector createChildren(); + static const QIcon &iconFavourites(); }; +/** A zip file: contains layers, using GDAL/OGR VSIFILE mechanism */ +class CORE_EXPORT QgsZipItem : public QgsDataCollectionItem +{ + Q_OBJECT + public: + QgsZipItem( QgsDataItem* parent, QString name, QString path ); + ~QgsZipItem(); + + QVector createChildren(); + QStringList getFiles(); + + static QVector mDataItemPtr; + static QStringList mProviderNames; + + static QgsDataItem* itemFromPath( QgsDataItem* parent, QString path, QString name ); + + static const QIcon &iconZip(); +}; + #endif // QGSDATAITEM_H diff --git a/src/providers/gdal/qgsgdaldataitems.cpp b/src/providers/gdal/qgsgdaldataitems.cpp index 9ee7967ce42..97b9239069c 100644 --- a/src/providers/gdal/qgsgdaldataitems.cpp +++ b/src/providers/gdal/qgsgdaldataitems.cpp @@ -3,6 +3,7 @@ #include "qgslogger.h" #include +#include // defined in qgsgdalprovider.cpp void buildSupportedRasterFileFilterAndExtensions( QString & theFileFiltersString, QStringList & theExtensions, QStringList & theWildcards ); @@ -90,6 +91,7 @@ QVector QgsGdalLayerItem::createChildren( ) // --------------------------------------------------------------------------- +static QString filterString; static QStringList extensions = QStringList(); static QStringList wildcards = QStringList(); @@ -103,79 +105,111 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) if ( thePath.isEmpty() ) return 0; + QgsDebugMsg( "thePath= " + thePath ); + + QString uri = thePath; QFileInfo info( thePath ); - if ( info.isFile() ) + QSettings settings; + //extract basename with extension + QString name = info.fileName(); + int scanZipSetting = settings.value( "/qgis/scanZipInBrowser", 1 ).toInt(); + + // allow normal files or VSIFILE items to pass + if ( ! info.isFile() && + thePath.left( 8 ) != "/vsizip/" && + thePath.left( 9 ) != "/vsigzip/" ) + return 0; + + // get supported extensions + if ( extensions.isEmpty() ) { - // get supported extensions - if ( extensions.isEmpty() ) - { - QString filterString; - buildSupportedRasterFileFilterAndExtensions( filterString, extensions, wildcards ); - QgsDebugMsg( "extensions: " + extensions.join( " " ) ); - QgsDebugMsg( "wildcards: " + wildcards.join( " " ) ); - } - - // skip *.aux.xml files (GDAL auxilary metadata files) - // unless that extension is in the list (*.xml might be though) - if ( thePath.right( 8 ) == ".aux.xml" && - extensions.indexOf( "aux.xml" ) < 0 ) - return 0; - - // skip .tar.gz files - if ( thePath.right( 7 ) == ".tar.gz" ) - return 0; - - // Filter files by extension - if ( extensions.indexOf( info.suffix().toLower() ) < 0 ) - { - bool matches = false; - foreach( QString wildcard, wildcards ) - { - QRegExp rx( wildcard, Qt::CaseInsensitive, QRegExp::Wildcard ); - if ( rx.exactMatch( info.fileName() ) ) - { - matches = true; - break; - } - } - if ( !matches ) - return 0; - } - - // try to open using VSIFileHandler - // TODO use the file name of the file inside the zip for layer name - if ( thePath.right( 4 ) == ".zip" ) - { - if ( thePath.left( 8 ) != "/vsizip/" ) - thePath = "/vsizip/" + thePath; - } - else if ( thePath.right( 3 ) == ".gz" ) - { - if ( thePath.left( 9 ) != "/vsigzip/" ) - thePath = "/vsigzip/" + thePath; - } - - GDALAllRegister(); - GDALDatasetH hDS = GDALOpen( TO8F( thePath ), GA_ReadOnly ); - - if ( !hDS ) - return 0; - - // get layers list now so we can pass it to item - QStringList sublayers = QgsGdalProvider::subLayers( hDS ); - - GDALClose( hDS ); - - QgsDebugMsg( "GdalDataset opened " + thePath ); - - //extract basename with extension - QString name = info.completeBaseName() + "." + info.suffix(); - QString uri = thePath; - - QgsLayerItem * item = new QgsGdalLayerItem( parentItem, name, thePath, uri, - &sublayers ); - - return item; + buildSupportedRasterFileFilterAndExtensions( filterString, extensions, wildcards ); + QgsDebugMsg( "extensions: " + extensions.join( " " ) ); + QgsDebugMsg( "wildcards: " + wildcards.join( " " ) ); } - return 0; + + // skip *.aux.xml files (GDAL auxilary metadata files) + // unless that extension is in the list (*.xml might be though) + if ( thePath.right( 8 ).toLower() == ".aux.xml" && + extensions.indexOf( "aux.xml" ) < 0 ) + return 0; + + // skip .tar.gz files + if ( thePath.right( 7 ) == ".tar.gz" ) + return 0; + + // Filter files by extension + if ( extensions.indexOf( info.suffix().toLower() ) < 0 ) + { + bool matches = false; + foreach( QString wildcard, wildcards ) + { + QRegExp rx( wildcard, Qt::CaseInsensitive, QRegExp::Wildcard ); + if ( rx.exactMatch( info.fileName() ) ) + { + matches = true; + break; + } + } + if ( !matches ) + return 0; + } + + // vsifile : depending on options we should just add the item without testing + if ( thePath.left( 8 ) == "/vsizip/" ) + { + // if this is a /vsigzip/path.zip/file_inside_zip change the name + if ( thePath != "/vsizip/" + parentItem->path() ) + { + name = thePath; + name = name.replace( "/vsizip/" + parentItem->path() + "/", "" ); + } + + // unless setting== 2 (passthru) or 3 (Full scan), return an item without testing + if ( scanZipSetting != 2 && scanZipSetting != 3 ) + { + QStringList sublayers; + QgsDebugMsg( QString( "adding item name=%1 thePath=%2 uri=%3" ).arg( name ).arg( thePath ).arg( uri ) ); + QgsLayerItem * item = new QgsGdalLayerItem( parentItem, name, thePath, thePath, &sublayers ); + if ( item ) + return item; + } + } + + // try to open using VSIFileHandler + if ( thePath.right( 4 ) == ".zip" ) + { + if ( thePath.left( 8 ) != "/vsizip/" ) + thePath = "/vsizip/" + thePath; + } + else if ( thePath.right( 3 ) == ".gz" ) + { + if ( thePath.left( 9 ) != "/vsigzip/" ) + thePath = "/vsigzip/" + thePath; + } + + // test that file is valid with GDAL + GDALAllRegister(); + // do not print errors, but write to debug + CPLErrorHandler oErrorHandler = CPLSetErrorHandler( CPLQuietErrorHandler ); + CPLErrorReset(); + GDALDatasetH hDS = GDALOpen( TO8F( thePath ), GA_ReadOnly ); + CPLSetErrorHandler( oErrorHandler ); + + if ( ! hDS ) + { + QgsDebugMsg( QString( "GDALOpen error # %1 : %2 " ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) ); + return 0; + } + + QStringList sublayers = QgsGdalProvider::subLayers( hDS ); + + GDALClose( hDS ); + + QgsDebugMsg( "GdalDataset opened " + thePath ); + + QgsLayerItem * item = new QgsGdalLayerItem( parentItem, name, thePath, thePath, + &sublayers ); + + return item; } diff --git a/src/providers/gdal/qgsgdalprovider.cpp b/src/providers/gdal/qgsgdalprovider.cpp index d262ba99522..eb46014ced0 100644 --- a/src/providers/gdal/qgsgdalprovider.cpp +++ b/src/providers/gdal/qgsgdalprovider.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include "gdalwarper.h" #include "ogr_spatialref.h" @@ -1077,7 +1078,7 @@ bool QgsGdalProvider::identify( const QgsPoint& thePoint, QMap int col = ( int ) floor(( x - mExtent.xMinimum() ) / xres ); int row = ( int ) floor(( mExtent.yMaximum() - y ) / yres ); - QgsDebugMsg( "row = " + QString::number( row ) + " col = " + QString::number( col ) ); + // QgsDebugMsg( "row = " + QString::number( row ) + " col = " + QString::number( col ) ); for ( int i = 1; i <= GDALGetRasterCount( mGdalDataset ); i++ ) { @@ -1093,7 +1094,7 @@ bool QgsGdalProvider::identify( const QgsPoint& thePoint, QMap } //double value = readValue( data, type, 0 ); - QgsDebugMsg( QString( "value=%1" ).arg( value ) ); + // QgsDebugMsg( QString( "value=%1" ).arg( value ) ); QString v; if ( mValidNoDataValue && ( fabs( value - mNoDataValue[i-1] ) <= TINY_VALUE || value != value ) ) @@ -1842,6 +1843,8 @@ void buildSupportedRasterFileFilterAndExtensions( QString & theFileFiltersString // VSIFileHandler (see qgsogrprovider.cpp) #if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1600 + // QSettings settings; + // if ( settings.value( "/qgis/scanZipInBrowser", 1 ).toInt() != 0 ) if ( 1 ) { QString glob = "*.zip"; @@ -1995,3 +1998,4 @@ QGISEXTERN void buildSupportedRasterFileFilter( QString & theFileFiltersString ) QStringList wildcards; buildSupportedRasterFileFilterAndExtensions( theFileFiltersString, exts, wildcards ); } + diff --git a/src/providers/ogr/qgsogrdataitems.cpp b/src/providers/ogr/qgsogrdataitems.cpp index 8d138736ccd..93554285d63 100644 --- a/src/providers/ogr/qgsogrdataitems.cpp +++ b/src/providers/ogr/qgsogrdataitems.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -225,8 +226,17 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) if ( thePath.isEmpty() ) return 0; + QgsDebugMsg( "thePath: " + thePath ); + QFileInfo info( thePath ); - if ( !info.isFile() ) + QString name = info.fileName(); + QSettings settings; + int scanZipSetting = settings.value( "/qgis/scanZipInBrowser", 1 ).toInt(); + + // allow normal files or VSIFILE items to pass + if ( ! info.isFile() && + thePath.left( 8 ) != "/vsizip/" && + thePath.left( 9 ) != "/vsigzip/" ) return 0; QStringList myExtensions = fileExtensions(); @@ -261,7 +271,27 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) return 0; } - // try to open using the /vsizip mechanism + // vsifile : depending on options we should just add the item without testing + if ( thePath.left( 8 ) == "/vsizip/" ) + { + // if this is a /vsigzip/path.zip/file_inside_zip change the name + if ( thePath.left( 8 ) == "/vsizip/" && + thePath != "/vsizip/" + parentItem->path() ) + { + name = thePath; + name = name.replace( "/vsizip/" + parentItem->path() + "/", "" ); + } + + // unless setting== 2 (Passthru) or 3 (Full scan), return an item without testing + if ( scanZipSetting != 2 && scanZipSetting != 3 ) + { + QgsLayerItem * item = new QgsOgrLayerItem( parentItem, name, thePath, thePath, QgsLayerItem::Vector ); + if ( item ) + return item; + } + } + + // try to open using VSIFileHandler if ( thePath.right( 4 ) == ".zip" ) { if ( thePath.left( 8 ) != "/vsizip/" ) @@ -273,12 +303,20 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) thePath = "/vsigzip/" + thePath; } + // test that file is valid with OGR OGRRegisterAll(); OGRSFDriverH hDriver; + // do not print errors, but write to debug + CPLErrorHandler oErrorHandler = CPLSetErrorHandler( CPLQuietErrorHandler ); + CPLErrorReset(); OGRDataSourceH hDataSource = OGROpen( TO8F( thePath ), false, &hDriver ); + CPLSetErrorHandler( oErrorHandler ); - if ( !hDataSource ) + if ( ! hDataSource ) + { + QgsDebugMsg( QString( "OGROpen error # %1 : %2 " ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) ); return 0; + } QString driverName = OGR_Dr_GetName( hDriver ); QgsDebugMsg( "OGR Driver : " + driverName ); @@ -289,13 +327,13 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) if ( numLayers == 1 ) { - //extract basename with extension - QString name = info.completeBaseName() + "." + QFileInfo( thePath ).suffix(); + QgsDebugMsg( QString( "using name = %1" ).arg( name ) ); item = dataItemForLayer( parentItem, name, thePath, hDataSource, 0 ); } else if ( numLayers > 1 ) { - item = new QgsOgrDataCollectionItem( parentItem, info.fileName(), thePath ); + QgsDebugMsg( QString( "using name = %1" ).arg( name ) ); + item = new QgsOgrDataCollectionItem( parentItem, name, thePath ); } OGR_DS_Destroy( hDataSource ); diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 8b7bf7e1c07..7287b90f7f3 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -35,6 +35,7 @@ email : sherman at mrcc.com #include #include #include +#include #include "qgsapplication.h" #include "qgsdataitem.h" @@ -1778,11 +1779,12 @@ QString createFilters( QString type ) // 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 shape (zip) and spatialite (zip and gz). - // Ideally we should add a new subclass of QgsCollectionItem (or QgsDirItem), say QgsZipItem - // and read the files inside the zip (requires unzip.h or cpl_minizip_unzip.h) - // and also add support for /vsitar/ (requires cpl_vsil_tar.cpp). + // 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", 1 ).toInt() != 0 ) if ( 1 ) { myFileFilters += createFileFilter_( QObject::tr( "GDAL/OGR VSIFileHandler" ), "*.zip *.gz" ); diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index e0e8cedad44..79556a6fc98 100644 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -602,6 +602,33 @@ + + + + + + Scan for contents of compressed files (.zip) in browser dock + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + +