From a8109513fdb01e99efb2a79c934988fc2601be27 Mon Sep 17 00:00:00 2001 From: Etienne Tourigny Date: Fri, 20 Apr 2012 15:28:05 -0300 Subject: [PATCH 1/5] add more QgsZipItem tests and data --- src/core/qgsdataitem.cpp | 2 + tests/src/core/testziplayer.cpp | 254 ++++++++++++++++++++++++++++--- tests/testdata/landsat_b1.tif.gz | Bin 0 -> 12008 bytes tests/testdata/landsat_b1.zip | Bin 0 -> 12153 bytes tests/testdata/points.geojson.gz | Bin 0 -> 660 bytes tests/testdata/testzip.zip | Bin 0 -> 30764 bytes 6 files changed, 234 insertions(+), 22 deletions(-) create mode 100644 tests/testdata/landsat_b1.tif.gz create mode 100644 tests/testdata/landsat_b1.zip create mode 100644 tests/testdata/points.geojson.gz create mode 100644 tests/testdata/testzip.zip diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp index 1aa787f45337..95367c294d22 100644 --- a/src/core/qgsdataitem.cpp +++ b/src/core/qgsdataitem.cpp @@ -835,6 +835,8 @@ QVector QgsZipItem::createChildren( ) { QgsDebugMsg( QString( "Zip error: %1" ).arg( zip.getZipError() ) ); } +#else + QgsDebugMsg( QString( "Cannot open file %1 with quazip - zlib not configured" ).arg( path() ) ); #endif // loop over files inside zip diff --git a/tests/src/core/testziplayer.cpp b/tests/src/core/testziplayer.cpp index 0f014ce9c567..7aeccc421356 100644 --- a/tests/src/core/testziplayer.cpp +++ b/tests/src/core/testziplayer.cpp @@ -2,8 +2,8 @@ testziplayer.cpp -------------------------------------- Date : Sun Sep 16 12:22:23 AKDT 2007 - Copyright : (C) 2012 Tim Sutton - Email : tim@linfiniti.com + Copyright : (C) 2012 Etienne Tourigny and Tim Sutton + Email : etourigny.dev@gmail.com *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * @@ -20,9 +20,12 @@ #include //qgis includes... -#include #include #include +#include +#include +#include +#include "qgsconfig.h" /** \ingroup UnitTests * This is a unit test to verify that zip vector layers work @@ -31,30 +34,237 @@ class TestZipLayer: public QObject { Q_OBJECT; + private: + + QString mDataDir; + QSettings mSettings; + int mMaxScanZipSetting; + int mScanZipSetting; + + bool testPassthruVector( QString myFileName ); + bool testPassthruRaster( QString myFileName ); + bool testZipItem( QString myFileName, QString myChildName ); + private slots: - void testZipLayer() + // init / cleanup + void initTestCase();// will be called before the first testfunction is executed. + void cleanupTestCase();// will be called after the last testfunction was executed. + void init() {};// will be called before each testfunction is executed. + void cleanup() {};// will be called after every testfunction. + + // tests + // test for .zip and .gz files using all options + void testPassthruVectorZip(); + void testPassthruVectorGzip(); + void testPassthruRasterZip(); + void testPassthruRasterGzip(); + // test both "Basic Scan" and "Full scan" for .zip files + void testZipItemRaster(); + void testZipItemVector(); + void testZipItemAll(); + +}; + + +bool TestZipLayer::testPassthruVector( QString myFileName ) +{ + QFileInfo myFileInfo( myFileName ); + QgsVectorLayer * myVectorLayer; + myVectorLayer = new QgsVectorLayer( myFileInfo.filePath(), + myFileInfo.completeBaseName(), "ogr" ); + bool ok = myVectorLayer->isValid(); + delete myVectorLayer; + return ok; +} + +bool TestZipLayer::testPassthruRaster( QString myFileName ) +{ + QFileInfo myFileInfo( myFileName ); + QgsRasterLayer * myRasterLayer; + myRasterLayer = new QgsRasterLayer( myFileInfo.filePath(), + myFileInfo.completeBaseName(), "gdal" ); + bool ok = myRasterLayer->isValid(); + delete myRasterLayer; + return ok; +} + +bool TestZipLayer::testZipItem( QString myFileName, QString myChildName = "" ) +{ + QgsDebugMsg( QString( "\n=======================================\nfile = %1 name = %2" ).arg( myFileName ).arg( myChildName ) ); + QFileInfo myFileInfo( myFileName ); + QgsZipItem *myZipItem = new QgsZipItem( NULL, myFileInfo.fileName(), myFileName ); + myZipItem->populate(); + bool ok = false; + QVector myChildren = myZipItem->children(); + + if ( myChildren.size() > 0 ) + { + QgsDebugMsg( QString( "has %1 items" ).arg( myChildren.size() ) ); + foreach( QgsDataItem* item, myChildren ) { - // init QGIS's paths - true means that all path will be inited from prefix - QString qgisPath = QCoreApplication::applicationDirPath(); - QgsApplication::setPrefixPath( INSTALL_PREFIX, true ); - // Instantiate the plugin directory so that providers are loaded - QgsProviderRegistry::instance( QgsApplication::pluginPath() ); - // - //create a point layer that will be used in all tests... - // - QString myDataDir( TEST_DATA_DIR ); - myDataDir += QDir::separator(); - QString myPointsFileName = myDataDir + "points.zip"; - QFileInfo myPointFileInfo( myPointsFileName ); - QgsVectorLayer * mypPointsLayer; - mypPointsLayer = new QgsVectorLayer( myPointFileInfo.filePath(), - myPointFileInfo.completeBaseName(), "ogr" ); - QVERIFY( mypPointsLayer->isValid() ); - delete mypPointsLayer; + QgsDebugMsg( QString( "child name=%1" ).arg( item->name() ) ); + QgsLayerItem *layerItem = dynamic_cast( item ); + if ( layerItem ) + { + QgsDebugMsg( QString( "child name=%1 provider=%2 path=%3" ).arg( layerItem->name() ).arg( layerItem->providerKey() ).arg( layerItem->path() ) ); + if ( myChildName == "" || myChildName == item->name() ) + { + QgsMapLayer* myLayer = NULL; + if ( layerItem->providerKey() == "ogr" ) + { + myLayer = new QgsVectorLayer( item->path(), item->name(), "ogr" ); + } + else if ( layerItem->providerKey() == "gdal" ) + { + myLayer = new QgsRasterLayer( item->path(), item->name(), "gdal" ); + } + else + { + // item should not have other provider key, but if it does the test will fail + ok = false; + QWARN( QString( "Invalid provider %1" ).arg( layerItem->providerKey() ).toLocal8Bit().data() ); + break; + } + if ( myLayer != NULL ) + { + // we got a layer, check if it is valid and exit + QgsDebugMsg( QString( "valid: %1" ).arg( myLayer->isValid() ) ); + ok = myLayer->isValid(); + delete myLayer; + if ( ! ok ) + { + QWARN( QString( "Invalid item %1" ).arg( layerItem->path() ).toLocal8Bit().data() ); + } + // if no child name given, then pass to next one (unless current child is invalid) + if ( myChildName == "" ) + { + if ( ! ok ) + break; + } + else + { + break; + } + } + else + { + QWARN( QString( "Invalid item %1" ).arg( layerItem->path() ).toLocal8Bit().data() ); + break; + } + } + } + else + { + QWARN( QString( "Invalid item %1" ).arg( layerItem->path() ).toLocal8Bit().data() ); + break; + } } + } + delete myZipItem; + return ok; +} -}; +void TestZipLayer::initTestCase() +{ + // init QGIS's paths - true means that all path will be inited from prefix + QString qgisPath = QCoreApplication::applicationDirPath(); + QgsApplication::setPrefixPath( INSTALL_PREFIX, true ); + // Instantiate the plugin directory so that providers are loaded + QgsProviderRegistry::instance( QgsApplication::pluginPath() ); + // save data dir + mDataDir = QString( TEST_DATA_DIR ) + QDir::separator(); + // set zipSetting to 1 (Passthru) and save current value + mScanZipSetting = mSettings.value( "/qgis/scanZipInBrowser", 1 ).toInt(); + mSettings.setValue( "/qgis/scanZipInBrowser", 1 ); + // max zipSetting value, depending on zlib presence + mMaxScanZipSetting = 1; +#ifdef HAVE_ZLIB + mMaxScanZipSetting = 3; +#endif + +} + +void TestZipLayer::cleanupTestCase() +{ + // restore zipSetting + mSettings.setValue( "/qgis/scanZipInBrowser", mScanZipSetting ); +} + + +void TestZipLayer::testPassthruVectorZip() +{ + for ( int i = 1 ; i <= mMaxScanZipSetting ; i++ ) + { + mSettings.setValue( "/qgis/scanZipInBrowser", i ); + QVERIFY( testPassthruVector( mDataDir + "points.zip" ) ); + } +} + +void TestZipLayer::testPassthruVectorGzip() +{ + for ( int i = 1 ; i <= mMaxScanZipSetting ; i++ ) + { + mSettings.setValue( "/qgis/scanZipInBrowser", i ); + QVERIFY( testPassthruVector( mDataDir + "points.geojson.gz" ) ); + } +} + +void TestZipLayer::testPassthruRasterZip() +{ + for ( int i = 1 ; i <= mMaxScanZipSetting ; i++ ) + { + mSettings.setValue( "/qgis/scanZipInBrowser", i ); + QVERIFY( testPassthruRaster( mDataDir + "landsat_b1.zip" ) ); + } +} + +void TestZipLayer::testPassthruRasterGzip() +{ + for ( int i = 1 ; i <= mMaxScanZipSetting ; i++ ) + { + mSettings.setValue( "/qgis/scanZipInBrowser", i ); + QVERIFY( testPassthruRaster( mDataDir + "landsat_b1.tif.gz" ) ); + } +} + +void TestZipLayer::testZipItemRaster() +{ +#ifndef HAVE_ZLIB + QSKIP( "This test requires ZLIB", SkipSingle ); +#endif + + for ( int i = 2 ; i <= mMaxScanZipSetting ; i++ ) + { + mSettings.setValue( "/qgis/scanZipInBrowser", i ); + QVERIFY( testZipItem( mDataDir + "testzip.zip", "landsat_b1.tif" ) ); + } +} + +void TestZipLayer::testZipItemVector() +{ +#ifndef HAVE_ZLIB + QSKIP( "This test requires ZLIB", SkipSingle ); +#endif + for ( int i = 2 ; i <= mMaxScanZipSetting ; i++ ) + { + mSettings.setValue( "/qgis/scanZipInBrowser", i ); + QVERIFY( testZipItem( mDataDir + "testzip.zip", "points.shp" ) ); + } +} + +void TestZipLayer::testZipItemAll() +{ +#ifndef HAVE_ZLIB + QSKIP( "This test requires ZLIB", SkipSingle ); +#endif + // test file contains invalid items (tmp1.tif, tmp1.txt and tmp1.xml) + // test for all items inside zip, using zipSetting 3 (Full Scan) which will ignore invalid items + // using zipSetting 2 (Basic Scan) would raise errors, because QgsZipItem would not test for valid items + // and return child names of the invalid items + mSettings.setValue( "/qgis/scanZipInBrowser", 3 ); + QVERIFY( testZipItem( mDataDir + "testzip.zip", "" ) ); +} QTEST_MAIN( TestZipLayer ) #include "moc_testziplayer.cxx" diff --git a/tests/testdata/landsat_b1.tif.gz b/tests/testdata/landsat_b1.tif.gz new file mode 100644 index 0000000000000000000000000000000000000000..abbf941028459d7ae93400cbec34e4b9d6a937e6 GIT binary patch literal 12008 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 Go&W&JyCb*& literal 0 HcmV?d00001 diff --git a/tests/testdata/landsat_b1.zip b/tests/testdata/landsat_b1.zip new file mode 100644 index 0000000000000000000000000000000000000000..7b150b311c4757b1ff051a546ea74a2a0833927a GIT binary patch literal 12153 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 literal 0 HcmV?d00001 diff --git a/tests/testdata/points.geojson.gz b/tests/testdata/points.geojson.gz new file mode 100644 index 0000000000000000000000000000000000000000..b8109bfb7d0c3057d1084773d7075e99bb11e5b9 GIT binary patch literal 660 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

rxqJH}ILieB0VQ7$Hzq@=asRZbD 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 PZ_>|L@8R<|ARzw_92yyG literal 0 HcmV?d00001 From 40de7c0c81a6f81c416e2940ad8644330857bc2a Mon Sep 17 00:00:00 2001 From: Etienne Tourigny Date: Sun, 22 Apr 2012 13:48:05 -0300 Subject: [PATCH 2/5] QgsRasterLayer(8 args) : load default style if provider is gdal and if no style was given --- src/core/raster/qgsrasterlayer.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/raster/qgsrasterlayer.cpp b/src/core/raster/qgsrasterlayer.cpp index 1e422a0ae360..aa95161df073 100644 --- a/src/core/raster/qgsrasterlayer.cpp +++ b/src/core/raster/qgsrasterlayer.cpp @@ -155,6 +155,14 @@ QgsRasterLayer::QgsRasterLayer( int dummy, bool loadDefaultStyleFlag = false ; // ??? setDataProvider( providerKey, layers, styles, format, crs, loadDefaultStyleFlag ); + // load default style if provider is gdal and if no style was given + // this should be an argument like in the other constructor + if ( mValid && providerKey == "gdal" && layers.isEmpty() && styles.isEmpty() ) + { + bool defaultLoadedFlag = false; + loadDefaultStyle( defaultLoadedFlag ); + } + // Default for the popup menu // TODO: popMenu = 0; From 79a5c3db28aac62caaaaa549a9cfacfc4ea63489 Mon Sep 17 00:00:00 2001 From: Etienne Tourigny Date: Sun, 22 Apr 2012 21:36:14 -0300 Subject: [PATCH 3/5] fix .qml file loading in /vsigzip and /vsizip items --- src/core/qgsdataitem.cpp | 43 ++++++++------ src/core/qgsdataitem.h | 7 +++ src/core/qgsmaplayer.cpp | 42 ++++++++++++-- src/core/qgsmaplayer.h | 8 +++ src/core/qgsvectorlayer.cpp | 7 +++ src/core/raster/qgsrasterlayer.cpp | 6 ++ src/providers/gdal/qgsgdaldataitems.cpp | 76 +++++++++++++------------ src/providers/ogr/qgsogrdataitems.cpp | 66 +++++++++++---------- src/providers/ogr/qgsogrprovider.cpp | 6 ++ 9 files changed, 174 insertions(+), 87 deletions(-) diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp index 95367c294d22..05b709ae871b 100644 --- a/src/core/qgsdataitem.cpp +++ b/src/core/qgsdataitem.cpp @@ -469,8 +469,7 @@ 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 + // vsizip support was added to GDAL/OGR 1.6 but GDAL_VERSION_NUM not available here if ( fileInfo.suffix() == "zip" && scanZip ) { QgsDataItem * item = QgsZipItem::itemFromPath( this, path, name ); @@ -480,7 +479,6 @@ QVector QgsDirectoryItem::createChildren( ) continue; } } -// #endif foreach( QLibrary *library, mLibraries ) { @@ -780,13 +778,14 @@ QgsZipItem::~QgsZipItem() QVector QgsZipItem::createChildren( ) { QVector children; - QStringList zipFileList; QString tmpPath; QString childPath; QSettings settings; int scanZipSetting = settings.value( "/qgis/scanZipInBrowser", 1 ).toInt(); + mZipFileList.clear(); + QgsDebugMsg( QString( "path = %1 name= %2 scanZipSetting= %3" ).arg( path() ).arg( name() ).arg( scanZipSetting ) ); // if scanZipBrowser == 0 (No): skip to the next file @@ -804,7 +803,7 @@ QVector QgsZipItem::createChildren( ) } #endif - // if scanZipBrowser == 1 (Passthru): do not scan zip and allow to open directly with /vsigzip/ + // if scanZipBrowser == 1 (Passthru): do not scan zip and allow to open directly with /vsizip/ if ( scanZipSetting == 1 ) { mPath = "/vsizip/" + path(); // should check for extension @@ -827,10 +826,10 @@ QVector QgsZipItem::createChildren( ) tmpPath = zip.getCurrentFileName(); // skip directories (files ending with /) if ( tmpPath.right( 1 ) != "/" ) - zipFileList << tmpPath; + mZipFileList << tmpPath; } + zip.close(); } - zip.close(); if ( zip.getZipError() != UNZ_OK ) { QgsDebugMsg( QString( "Zip error: %1" ).arg( zip.getZipError() ) ); @@ -840,7 +839,7 @@ QVector QgsZipItem::createChildren( ) #endif // loop over files inside zip - foreach( QString fileName, zipFileList ) + foreach( QString fileName, mZipFileList ) { QFileInfo info( fileName ); tmpPath = "/vsizip/" + path() + "/" + fileName; @@ -854,7 +853,7 @@ QVector QgsZipItem::createChildren( ) { if ( info.suffix() == "dbf" ) { - if ( zipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 ) + if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 ) continue; } if ( info.completeSuffix().toLower() == "shp.xml" ) @@ -901,8 +900,9 @@ QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QStrin QSettings settings; int scanZipSetting = settings.value( "/qgis/scanZipInBrowser", 1 ).toInt(); + QString vsizipPath = path; + int zipFileCount = 0; QFileInfo fileInfo( path ); - QString tmpPath = path; QgsZipItem * zipItem = 0; QgsDebugMsg( QString( "path = %1 name= %2 scanZipSetting= %3" ).arg( path ).arg( name ).arg( scanZipSetting ) ); @@ -912,10 +912,10 @@ QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QStrin { return 0; } - // if scanZipBrowser == 1 (Passthru): do not scan zip and allow to open directly with /vsigzip/ + // if scanZipBrowser == 1 (Passthru): do not scan zip and allow to open directly with /vsizip/ else if ( scanZipSetting == 1 ) { - tmpPath = "/vsizip/" + path; + vsizipPath = "/vsizip/" + path; zipItem = 0; } else @@ -937,16 +937,17 @@ QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QStrin QgsDebugMsg( "returning zipItem" ); return zipItem; } -// if 1 or 0 child found, create a data item using 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 ) { - tmpPath = zipItem->path(); + vsizipPath = zipItem->path(); + zipFileCount = zipItem->getZipFileList().count(); delete zipItem; } - QgsDebugMsg( QString( "will try to create a normal dataItem from path= %2" ).arg( tmpPath ) ); + QgsDebugMsg( QString( "will try to create a normal dataItem from path= %2 or %3" ).arg( path ).arg( vsizipPath ) ); // try to open using registered providers (gdal and ogr) for ( int i = 0; i < mProviderNames.size(); i++ ) @@ -954,7 +955,17 @@ QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QStrin dataItem_t *dataItem = mDataItemPtr[i]; if ( dataItem ) { - QgsDataItem *item = dataItem( tmpPath, parent ); + QgsDataItem *item = 0; + // try first with normal path (Passthru) + // this is to simplify .qml handling, and without this some tests will fail + // (e.g. testZipItemVectorTransparency(), second test) + if (( scanZipSetting == 1 ) || + ( mProviderNames[i] == "ogr" ) || + ( mProviderNames[i] == "gdal" && zipFileCount == 1 ) ) + item = dataItem( path, parent ); + // try with /vsizip/ + if ( ! item ) + item = dataItem( vsizipPath, parent ); if ( item ) return item; } diff --git a/src/core/qgsdataitem.h b/src/core/qgsdataitem.h index c69058480c31..d441e1a0e9d7 100644 --- a/src/core/qgsdataitem.h +++ b/src/core/qgsdataitem.h @@ -289,6 +289,10 @@ class CORE_EXPORT QgsFavouritesItem : public QgsDataCollectionItem class CORE_EXPORT QgsZipItem : public QgsDataCollectionItem { Q_OBJECT + + protected: + QStringList mZipFileList; + public: QgsZipItem( QgsDataItem* parent, QString name, QString path ); ~QgsZipItem(); @@ -302,6 +306,9 @@ class CORE_EXPORT QgsZipItem : public QgsDataCollectionItem 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 3db1cea8d6a6..105156eba77a 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -549,25 +549,57 @@ QString QgsMapLayer::capitaliseLayerName( const QString name ) return layerName; } -QString QgsMapLayer::loadDefaultStyle( bool & theResultFlag ) +QString QgsMapLayer::getStyleURI( ) { QString myURI = publicSource(); + + // if file is using the /vsizip/ or /vsigzip/ mechanism, cleanup the name + if ( myURI.left( 9 ) == "/vsigzip/" ) + { + myURI.remove( 1, 9 ); + } + else if ( myURI.left( 8 ) == "/vsizip/" && myURI.right( 4 ) == ".zip" ) + { + // ideally we should look for .qml file inside zip file + myURI.remove( 1, 8 ); + } + QFileInfo myFileInfo( myURI ); QString key; + if ( myFileInfo.exists() ) { + // if file is using the /vsizip/ or /vsigzip/ mechanism, cleanup the name + if ( myURI.right( 3 ) == ".gz" ) + { + myURI.chop( 3 ); + myFileInfo.setFile( myURI ); + } + else if ( myURI.right( 4 ) == ".zip" ) + { + myURI.chop( 4 ); + myFileInfo.setFile( myURI ); + } // get the file name for our .qml style file key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml"; } else { - key = myURI; + key = publicSource(); } - return loadNamedStyle( key, theResultFlag ); + + return key; +} + +QString QgsMapLayer::loadDefaultStyle( bool & theResultFlag ) +{ + return loadNamedStyle( getStyleURI(), theResultFlag ); } bool QgsMapLayer::loadNamedStyleFromDb( const QString db, const QString theURI, QString &qml ) { + QgsDebugMsg( QString( "db = %1 uri = %2" ).arg( db ).arg( theURI ) ); + bool theResultFlag = false; // read from database @@ -610,6 +642,8 @@ bool QgsMapLayer::loadNamedStyleFromDb( const QString db, const QString theURI, QString QgsMapLayer::loadNamedStyle( const QString theURI, bool &theResultFlag ) { + QgsDebugMsg( QString( "uri = %1 myURI = %2" ).arg( theURI ).arg( publicSource() ) ); + theResultFlag = false; QDomDocument myDocument( "qgis" ); @@ -692,7 +726,7 @@ QString QgsMapLayer::loadNamedStyle( const QString theURI, bool &theResultFlag ) QString QgsMapLayer::saveDefaultStyle( bool & theResultFlag ) { - return saveNamedStyle( publicSource(), theResultFlag ); + return saveNamedStyle( getStyleURI(), theResultFlag ); } QString QgsMapLayer::saveNamedStyle( const QString theURI, bool & theResultFlag ) diff --git a/src/core/qgsmaplayer.h b/src/core/qgsmaplayer.h index eb164559ae35..6b455970e0e9 100644 --- a/src/core/qgsmaplayer.h +++ b/src/core/qgsmaplayer.h @@ -237,6 +237,14 @@ class CORE_EXPORT QgsMapLayer : public QObject /** A convenience function to capitalise the layer name */ static QString capitaliseLayerName( const QString name ); + /** Retrieve the style URI for this layer + * (either as a .qml file on disk or as a + * record in the users style table in their personal qgis.db) + * @return a QString withe the style file name + * @see also loadNamedStyle () and saveNamedStyle (); + */ + virtual QString getStyleURI( ); + /** Retrieve the default style for this layer if one * exists (either as a .qml file on disk or as a * record in the users style table in their personal qgis.db) diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index a2ee5d6d31e7..5c1d7355a281 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2844,6 +2844,13 @@ bool QgsVectorLayer::setDataProvider( QString const & provider ) // make sure that the "observer" has been removed from URI to avoid crashes mDataSource = mDataProvider->dataSourceUri(); } + else if ( provider == "ogr" ) + { + // make sure that the /vsigzip or /vsizip is added to uri, if applicable + mDataSource = mDataProvider->dataSourceUri(); + if ( mDataSource.right( 10 ) == "|layerid=0" ) + mDataSource.chop( 10 ); + } // label mLabel = new QgsLabel( mDataProvider->fields() ); diff --git a/src/core/raster/qgsrasterlayer.cpp b/src/core/raster/qgsrasterlayer.cpp index aa95161df073..669b9fc79558 100644 --- a/src/core/raster/qgsrasterlayer.cpp +++ b/src/core/raster/qgsrasterlayer.cpp @@ -2278,6 +2278,12 @@ void QgsRasterLayer::setDataProvider( QString const & provider, return; } + if ( provider == "gdal" ) + { + // make sure that the /vsigzip or /vsizip is added to uri, if applicable + mDataSource = mDataProvider->dataSourceUri(); + } + mDataProvider->addLayers( layers, styles ); mDataProvider->setImageEncoding( format ); mDataProvider->setImageCrs( theCrs ); diff --git a/src/providers/gdal/qgsgdaldataitems.cpp b/src/providers/gdal/qgsgdaldataitems.cpp index b123e9af2ced..2f08b11c5dc3 100644 --- a/src/providers/gdal/qgsgdaldataitems.cpp +++ b/src/providers/gdal/qgsgdaldataitems.cpp @@ -105,20 +105,27 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) if ( thePath.isEmpty() ) return 0; - QgsDebugMsg( "thePath= " + thePath ); - - QString uri = thePath; - QFileInfo info( thePath ); + // zip settings + info QSettings settings; - //extract basename with extension - QString name = info.fileName(); int scanItemsSetting = settings.value( "/qgis/scanItemsInBrowser", 0 ).toInt(); int scanZipSetting = settings.value( "/qgis/scanZipInBrowser", 1 ).toInt(); + bool is_vsigzip = ( thePath.left( 9 ) == "/vsigzip/" ) || ( thePath.right( 3 ) == ".gz" ) ; + bool is_vsizip = ( thePath.left( 8 ) == "/vsizip/" ) || ( thePath.right( 4 ) == ".zip" ) ; + + // get suffix, removing .gz if present + QString tmpPath = thePath; //path used for testing, not for layer creation + if ( is_vsigzip ) + tmpPath.chop( 3 ); + QFileInfo info( tmpPath ); + QString suffix = info.suffix().toLower(); + // extract basename with extension + info.setFile( thePath ); + QString name = info.fileName(); + + QgsDebugMsg( "thePath= " + thePath + " tmpPath= " + tmpPath ); - // allow normal files or VSIFILE items to pass - if ( ! info.isFile() && - thePath.left( 8 ) != "/vsizip/" && - thePath.left( 9 ) != "/vsigzip/" ) + // allow only normal files or VSIFILE items to continue + if ( ! info.isFile() && ! is_vsizip && ! is_vsigzip ) return 0; // get supported extensions @@ -140,7 +147,7 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) return 0; // Filter files by extension - if ( extensions.indexOf( info.suffix().toLower() ) < 0 ) + if ( extensions.indexOf( suffix ) < 0 ) { bool matches = false; foreach( QString wildcard, wildcards ) @@ -156,48 +163,43 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) return 0; } - // vsifile : depending on options we should just add the item without testing - if ( thePath.left( 8 ) == "/vsizip/" ) + // add /vsizip/ or /vsigzip/ to path if file extension is .zip or .gz + if ( is_vsigzip ) { - // if this is a /vsigzip/path.zip/file_inside_zip change the name + if ( thePath.left( 9 ) != "/vsigzip/" ) + thePath = "/vsigzip/" + thePath; + } + else if ( is_vsizip ) + { + if ( thePath.left( 8 ) != "/vsizip/" ) + thePath = "/vsizip/" + 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() ) { name = thePath; name = name.replace( "/vsizip/" + parentItem->path() + "/", "" ); } - - // if setting = 2 (Basic scan), return an item without testing - if ( scanZipSetting == 2 ) - { - 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; - } } - // if scan items == "Check extension", add item here without trying to open - if ( scanItemsSetting == 1 ) + // if setting = 2 (Basic scan), return a /vsizip/ item without testing + if ( is_vsizip && scanZipSetting == 2 ) { QStringList sublayers; - QgsDebugMsg( QString( "adding item name=%1 thePath=%2 uri=%3" ).arg( name ).arg( thePath ).arg( uri ) ); + QgsDebugMsg( QString( "adding item name=%1 thePath=%2" ).arg( name ).arg( thePath ) ); 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 scan items == "Check extension", add item here without trying to open + // unless item is /vsizip + if ( scanItemsSetting == 1 && !is_vsizip ) { - if ( thePath.left( 9 ) != "/vsigzip/" ) - thePath = "/vsigzip/" + thePath; + QStringList sublayers; + QgsDebugMsg( QString( "adding item name=%1 thePath=%2" ).arg( name ).arg( thePath ) ); + QgsLayerItem * item = new QgsGdalLayerItem( parentItem, name, thePath, thePath, &sublayers ); + if ( item ) + return item; } // test that file is valid with GDAL diff --git a/src/providers/ogr/qgsogrdataitems.cpp b/src/providers/ogr/qgsogrdataitems.cpp index 8c84ef81302d..be0d780d8aee 100644 --- a/src/providers/ogr/qgsogrdataitems.cpp +++ b/src/providers/ogr/qgsogrdataitems.cpp @@ -228,16 +228,25 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) QgsDebugMsg( "thePath: " + thePath ); - QFileInfo info( thePath ); - QString name = info.fileName(); + // zip settings + info QSettings settings; int scanItemsSetting = settings.value( "/qgis/scanItemsInBrowser", 0 ).toInt(); int scanZipSetting = settings.value( "/qgis/scanZipInBrowser", 1 ).toInt(); + bool is_vsizip = ( thePath.left( 8 ) == "/vsizip/" ) || ( thePath.right( 4 ) == ".zip" ) ; + bool is_vsigzip = ( thePath.left( 9 ) == "/vsigzip/" ) || ( thePath.right( 3 ) == ".gz" ) ; + + // get suffix, removing .gz if present + QString tmpPath = thePath; //path used for testing, not for layer creation + if ( is_vsigzip ) + tmpPath.chop( 3 ); + QFileInfo info( tmpPath ); + QString suffix = info.suffix().toLower(); + // extract basename with extension + info.setFile( thePath ); + QString name = info.fileName(); - // allow normal files or VSIFILE items to pass - if ( ! info.isFile() && - thePath.left( 8 ) != "/vsizip/" && - thePath.left( 9 ) != "/vsigzip/" ) + // allow only normal files or VSIFILE items to continue + if ( ! info.isFile() && ! is_vsizip && ! is_vsigzip ) return 0; QStringList myExtensions = fileExtensions(); @@ -281,44 +290,41 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem ) return 0; } - // vsifile : depending on options we should just add the item without testing - if ( thePath.left( 8 ) == "/vsizip/" ) + // add /vsizip/ or /vsigzip/ to path if file extension is .zip or .gz + if ( is_vsigzip ) + { + if ( thePath.left( 9 ) != "/vsigzip/" ) + thePath = "/vsigzip/" + thePath; + } + else if ( is_vsizip ) { - // if this is a /vsigzip/path.zip/file_inside_zip change the name - if ( thePath.left( 8 ) == "/vsizip/" && - thePath != "/vsizip/" + parentItem->path() ) + if ( thePath.left( 8 ) != "/vsizip/" ) + thePath = "/vsizip/" + 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() ) { name = thePath; name = name.replace( "/vsizip/" + parentItem->path() + "/", "" ); } - - // if setting== 2 (Basic scan), return an item without testing - if ( scanZipSetting == 2 ) - { - QgsLayerItem * item = new QgsOgrLayerItem( parentItem, name, thePath, thePath, QgsLayerItem::Vector ); - if ( item ) - return item; - } } - // if scan items == "Check extension", add item here without trying to open - if ( scanItemsSetting == 1 ) + // if setting = 2 (Basic scan), return a /vsizip/ item without testing + if ( is_vsizip && scanZipSetting == 2 ) { + QStringList sublayers; + QgsDebugMsg( QString( "adding item name=%1 thePath=%2" ).arg( name ).arg( thePath ) ); 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/" ) - thePath = "/vsizip/" + thePath; - } - else if ( thePath.right( 3 ) == ".gz" ) + // if scan items == "Check extension", add item here without trying to open + // unless item is /vsizip + if ( scanItemsSetting == 1 && !is_vsizip && !is_vsigzip ) { - if ( thePath.left( 9 ) != "/vsigzip/" ) - thePath = "/vsigzip/" + thePath; + QgsLayerItem * item = new QgsOgrLayerItem( parentItem, name, thePath, thePath, QgsLayerItem::Vector ); + if ( item ) + return item; } // test that file is valid with OGR diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 7287b90f7f33..7f34a8dff58d 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -270,13 +270,19 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri ) // cannot be interleaved, so for now just use read-only. openReadOnly = true; if ( mFilePath.left( 8 ) != "/vsizip/" ) + { mFilePath = "/vsizip/" + mFilePath; + setDataSourceUri( mFilePath ); + } QgsDebugMsg( QString( "Trying /vsizip syntax, mFilePath= %1" ).arg( mFilePath ) ); } else if ( mFilePath.right( 3 ) == ".gz" ) { if ( mFilePath.left( 9 ) != "/vsigzip/" ) + { mFilePath = "/vsigzip/" + mFilePath; + setDataSourceUri( mFilePath ); + } QgsDebugMsg( QString( "Trying /vsigzip syntax, mFilePath= %1" ).arg( mFilePath ) ); } From 80bb0fdcb0eb284e15dbbdcf50606e452587b887 Mon Sep 17 00:00:00 2001 From: Etienne Tourigny Date: Fri, 20 Apr 2012 23:50:03 -0300 Subject: [PATCH 4/5] test that styles are loaded from qml files outside zip files --- tests/src/core/testziplayer.cpp | 169 +++++++++++++----- tests/testdata/landsat_b1.qml | 32 ++++ tests/testdata/landsat_b1.tif.qml | 32 ++++ tests/testdata/landsat_b1.zip | Bin 12153 -> 12153 bytes tests/testdata/points.qml | 2 +- tests/testdata/points.zip | Bin 2393 -> 0 bytes tests/testdata/points2.qml | 93 ++++++++++ tests/testdata/points2.zip | Bin 0 -> 2393 bytes .../{points.geojson.gz => points3.geojson.gz} | Bin tests/testdata/points3.qml | 93 ++++++++++ 10 files changed, 372 insertions(+), 49 deletions(-) create mode 100644 tests/testdata/landsat_b1.qml create mode 100644 tests/testdata/landsat_b1.tif.qml delete mode 100644 tests/testdata/points.zip create mode 100644 tests/testdata/points2.qml create mode 100644 tests/testdata/points2.zip rename tests/testdata/{points.geojson.gz => points3.geojson.gz} (100%) create mode 100644 tests/testdata/points3.qml diff --git a/tests/src/core/testziplayer.cpp b/tests/src/core/testziplayer.cpp index 7aeccc421356..efbc577fe300 100644 --- a/tests/src/core/testziplayer.cpp +++ b/tests/src/core/testziplayer.cpp @@ -26,6 +26,8 @@ #include #include #include "qgsconfig.h" +#include +#include /** \ingroup UnitTests * This is a unit test to verify that zip vector layers work @@ -41,9 +43,15 @@ class TestZipLayer: public QObject int mMaxScanZipSetting; int mScanZipSetting; - bool testPassthruVector( QString myFileName ); - bool testPassthruRaster( QString myFileName ); + // get map layer using Passthru + QgsMapLayer * getLayer( QString myPath, QString myName, QString myProviderKey ); + bool testZipItemPassthru( QString myFileName, QString myProviderKey ); + // get map layer using QgsZipItem (only 1 child) + QgsMapLayer * getZipLayer( QString myPath, QString myName ); + // test item(s) in zip item (supply name or test all) bool testZipItem( QString myFileName, QString myChildName ); + // get layer transparency to test for .qml loading + int getLayerTransparency( QString myFileName, QString myProviderKey, int myScanZipSetting = 1 ); private slots: @@ -63,29 +71,57 @@ class TestZipLayer: public QObject void testZipItemRaster(); void testZipItemVector(); void testZipItemAll(); - + // test that styles are loaded from .qml files outside zip files + void testZipItemVectorTransparency(); + void testGipItemVectorTransparency(); + void testZipItemRasterTransparency(); + void testGZipItemRasterTransparency(); }; -bool TestZipLayer::testPassthruVector( QString myFileName ) +QgsMapLayer *TestZipLayer::getLayer( QString myPath, QString myName, QString myProviderKey ) { - QFileInfo myFileInfo( myFileName ); - QgsVectorLayer * myVectorLayer; - myVectorLayer = new QgsVectorLayer( myFileInfo.filePath(), - myFileInfo.completeBaseName(), "ogr" ); - bool ok = myVectorLayer->isValid(); - delete myVectorLayer; - return ok; + if ( myName == "" ) + { + QFileInfo myFileInfo( myPath ); + myName = myFileInfo.completeBaseName(); + } + QgsMapLayer *myLayer = NULL; + + if ( myProviderKey == "ogr" ) + { + myLayer = new QgsVectorLayer( myPath, myName, "ogr" ); + } + else if ( myProviderKey == "gdal" ) + { + myLayer = new QgsRasterLayer( myPath, myName, "gdal" ); + } + // item should not have other provider key, but if it does will return NULL + + return myLayer; } -bool TestZipLayer::testPassthruRaster( QString myFileName ) +QgsMapLayer *TestZipLayer::getZipLayer( QString myPath, QString myName ) { - QFileInfo myFileInfo( myFileName ); - QgsRasterLayer * myRasterLayer; - myRasterLayer = new QgsRasterLayer( myFileInfo.filePath(), - myFileInfo.completeBaseName(), "gdal" ); - bool ok = myRasterLayer->isValid(); - delete myRasterLayer; + QgsMapLayer *myLayer = NULL; + QgsDirectoryItem *dirItem = new QgsDirectoryItem( NULL, "/", "" ); + QgsDataItem* myItem = QgsZipItem::itemFromPath( dirItem, myPath, myName ); + if ( myItem ) + { + QgsLayerItem *layerItem = dynamic_cast( myItem ); + if ( layerItem ) + myLayer = getLayer( layerItem->path(), layerItem->name(), layerItem->providerKey() ); + } + delete dirItem; + return myLayer; +} + +bool TestZipLayer::testZipItemPassthru( QString myFileName, QString myProviderKey ) +{ + QgsMapLayer * myLayer = getLayer( myFileName, "", myProviderKey ); + bool ok = myLayer && myLayer->isValid(); + if ( myLayer ) + delete myLayer; return ok; } @@ -110,33 +146,18 @@ bool TestZipLayer::testZipItem( QString myFileName, QString myChildName = "" ) QgsDebugMsg( QString( "child name=%1 provider=%2 path=%3" ).arg( layerItem->name() ).arg( layerItem->providerKey() ).arg( layerItem->path() ) ); if ( myChildName == "" || myChildName == item->name() ) { - QgsMapLayer* myLayer = NULL; - if ( layerItem->providerKey() == "ogr" ) - { - myLayer = new QgsVectorLayer( item->path(), item->name(), "ogr" ); - } - else if ( layerItem->providerKey() == "gdal" ) - { - myLayer = new QgsRasterLayer( item->path(), item->name(), "gdal" ); - } - else - { - // item should not have other provider key, but if it does the test will fail - ok = false; - QWARN( QString( "Invalid provider %1" ).arg( layerItem->providerKey() ).toLocal8Bit().data() ); - break; - } - if ( myLayer != NULL ) + QgsMapLayer* layer = getLayer( layerItem->path(), layerItem->name(), layerItem->providerKey() ); + if ( layer != NULL ) { // we got a layer, check if it is valid and exit - QgsDebugMsg( QString( "valid: %1" ).arg( myLayer->isValid() ) ); - ok = myLayer->isValid(); - delete myLayer; + // if no child name given in argument, then pass to next one (unless current child is invalid) + QgsDebugMsg( QString( "valid: %1" ).arg( layer->isValid() ) ); + ok = layer->isValid(); + delete layer; if ( ! ok ) { - QWARN( QString( "Invalid item %1" ).arg( layerItem->path() ).toLocal8Bit().data() ); + QWARN( QString( "Invalid layer %1" ).arg( layerItem->path() ).toLocal8Bit().data() ); } - // if no child name given, then pass to next one (unless current child is invalid) if ( myChildName == "" ) { if ( ! ok ) @@ -149,14 +170,14 @@ bool TestZipLayer::testZipItem( QString myFileName, QString myChildName = "" ) } else { - QWARN( QString( "Invalid item %1" ).arg( layerItem->path() ).toLocal8Bit().data() ); + QWARN( QString( "Invalid layer %1" ).arg( layerItem->path() ).toLocal8Bit().data() ); break; } } } else { - QWARN( QString( "Invalid item %1" ).arg( layerItem->path() ).toLocal8Bit().data() ); + QWARN( QString( "Invalid layer %1" ).arg( layerItem->path() ).toLocal8Bit().data() ); break; } } @@ -165,6 +186,24 @@ bool TestZipLayer::testZipItem( QString myFileName, QString myChildName = "" ) return ok; } +int TestZipLayer::getLayerTransparency( QString myFileName, QString myProviderKey, int myScanZipSetting ) +{ + int myTransparency = -1; + mSettings.setValue( "/qgis/scanZipInBrowser", myScanZipSetting ); + QgsMapLayer * myLayer = NULL; + if ( myScanZipSetting == 1 ) + myLayer = getLayer( myFileName, "", myProviderKey ); + else + myLayer = getZipLayer( myFileName, "" ); + if ( myLayer && myLayer->isValid() ) + myTransparency = myLayer->getTransparency(); + if ( myLayer ) + delete myLayer; + return myTransparency; +} + + +// slots void TestZipLayer::initTestCase() { // init QGIS's paths - true means that all path will be inited from prefix @@ -197,7 +236,7 @@ void TestZipLayer::testPassthruVectorZip() for ( int i = 1 ; i <= mMaxScanZipSetting ; i++ ) { mSettings.setValue( "/qgis/scanZipInBrowser", i ); - QVERIFY( testPassthruVector( mDataDir + "points.zip" ) ); + QVERIFY( testZipItemPassthru( mDataDir + "points2.zip", "ogr" ) ); } } @@ -206,7 +245,7 @@ void TestZipLayer::testPassthruVectorGzip() for ( int i = 1 ; i <= mMaxScanZipSetting ; i++ ) { mSettings.setValue( "/qgis/scanZipInBrowser", i ); - QVERIFY( testPassthruVector( mDataDir + "points.geojson.gz" ) ); + QVERIFY( testZipItemPassthru( mDataDir + "points3.geojson.gz", "ogr" ) ); } } @@ -215,7 +254,7 @@ void TestZipLayer::testPassthruRasterZip() for ( int i = 1 ; i <= mMaxScanZipSetting ; i++ ) { mSettings.setValue( "/qgis/scanZipInBrowser", i ); - QVERIFY( testPassthruRaster( mDataDir + "landsat_b1.zip" ) ); + QVERIFY( testZipItemPassthru( mDataDir + "landsat_b1.zip", "gdal" ) ); } } @@ -224,7 +263,7 @@ void TestZipLayer::testPassthruRasterGzip() for ( int i = 1 ; i <= mMaxScanZipSetting ; i++ ) { mSettings.setValue( "/qgis/scanZipInBrowser", i ); - QVERIFY( testPassthruRaster( mDataDir + "landsat_b1.tif.gz" ) ); + QVERIFY( testZipItemPassthru( mDataDir + "landsat_b1.tif.gz", "gdal" ) ); } } @@ -266,9 +305,43 @@ void TestZipLayer::testZipItemAll() QVERIFY( testZipItem( mDataDir + "testzip.zip", "" ) ); } -QTEST_MAIN( TestZipLayer ) -#include "moc_testziplayer.cxx" +void TestZipLayer::testZipItemVectorTransparency() +{ + int myTarget = 250; + int myTransparency = getLayerTransparency( mDataDir + "points2.zip", "ogr", 1 ); + QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); + myTransparency = getLayerTransparency( mDataDir + "points2.zip", "ogr", 2 ); + QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); +} +void TestZipLayer::testGipItemVectorTransparency() +{ + int myTarget = 250; + int myTransparency = getLayerTransparency( mDataDir + "points3.geojson.gz", "ogr", 1 ); + QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); + myTransparency = getLayerTransparency( mDataDir + "points3.geojson.gz", "ogr", 2 ); + QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); +} + +void TestZipLayer::testZipItemRasterTransparency() +{ + int myTarget = 250; + int myTransparency = getLayerTransparency( mDataDir + "landsat_b1.zip", "gdal", 1 ); + QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); + myTransparency = getLayerTransparency( mDataDir + "landsat_b1.zip", "gdal", 2 ); + QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); +} + +void TestZipLayer::testGZipItemRasterTransparency() +{ + int myTarget = 250; + int myTransparency = getLayerTransparency( mDataDir + "landsat_b1.tif.gz", "gdal", 1 ); + QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); + myTransparency = getLayerTransparency( mDataDir + "landsat_b1.tif.gz", "gdal", 2 ); + QVERIFY2(( myTransparency == myTarget ), QString( "Transparency is %1, should be %2" ).arg( myTransparency ).arg( myTarget ).toLocal8Bit().data() ); +} +QTEST_MAIN( TestZipLayer ) +#include "moc_testziplayer.cxx" diff --git a/tests/testdata/landsat_b1.qml b/tests/testdata/landsat_b1.qml new file mode 100644 index 000000000000..ca6d53485a0f --- /dev/null +++ b/tests/testdata/landsat_b1.qml @@ -0,0 +1,32 @@ + + + 250 + + SingleBandGray + UndefinedShader + + Not Set + Not Set + Not Set + Band 1 + 0 + + + + + StretchAndClipToMinimumMaximum + + + 122 + 130 + + + -32768.000000 + + + + + + + + diff --git a/tests/testdata/landsat_b1.tif.qml b/tests/testdata/landsat_b1.tif.qml new file mode 100644 index 000000000000..4f99453a1122 --- /dev/null +++ b/tests/testdata/landsat_b1.tif.qml @@ -0,0 +1,32 @@ + + + 248 + + SingleBandGray + UndefinedShader + + + + + Band 1 + 0 + + + + + StretchAndClipToMinimumMaximum + + + 122 + 130 + + + -32768.000000 + + + + + + + + diff --git a/tests/testdata/landsat_b1.zip b/tests/testdata/landsat_b1.zip index 7b150b311c4757b1ff051a546ea74a2a0833927a..b341559556a5cb89271d74b5f2e4f671dc2cad56 100644 GIT binary patch delta 14 Vcmewv_cLySDRan=i5o2(^#M4`2CV=9 delta 14 Vcmewv_cLySDYGoggpC%C`T#MF1%Utn diff --git a/tests/testdata/points.qml b/tests/testdata/points.qml index af2e0e9285d3..2771aee26a4a 100644 --- a/tests/testdata/points.qml +++ b/tests/testdata/points.qml @@ -1,6 +1,6 @@ - 255 + 250 Importance Heading Class diff --git a/tests/testdata/points.zip b/tests/testdata/points.zip deleted file mode 100644 index 006239784a43010e3ceabe6ad344460a183e29f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2393 zcmai#3p7;w9>@307|)D}Jj#%WIAt0pMIIU5$QZ|XzaN7TW(?z5DKkl0P7}i!Z;eiK z3=R=4jz>(6uIK1MB1Ig7BO*nV?xed~bWXRu*WP>m*WT;9zQ6tduI~qL1rgc-0Dvd} zvB#LoXt=7M60BwgmpFg`$YG~RkrX5)DAv(I41loTYT6thx@r>@D*=FnrXT>Yar5)R z^L<-jon9z33I*Otyr#XXJfCd*iXRjEvrJ%urgOoR*CR^gDGOn9(`~o! zfv{_sX$8%ED0@FEQZb_d%$CPYOXAP$&M<+n!!XnG_*KQk0DtNOGnHKcev45ZFE)V$ z0svF+S4PQSjH-5g!>G7|tQQJmsZ#o~^2GCTrAm?Y_poaXQSIHW=pCZvNr=HXI~N~P za|0y$11AZjBcEK?bFOqQ&3l%rb<2xH$Ql=V&?S_9F*}6Ph@V`s5J^R#aY&svGAUMP zmZ*Qp&0t={ATSXJPzHrAy*|Y+k|8n5yv)EJ;jT*V#JT1jhb=@eE4frx9UzQ&R%C~w z>i2syv*&xZ8NXV>vG_c@z*K}GZti-h+x1N673#1l@%DD4iUBMuZzfOg#jOAf<(1YC zKQB)hRUr1^=A1g8C9HvUL>OH~8hT}r+zx2E`^4K;)(Up75rS&=6jh&Lc1Qh;p{i6{ z-l9HtFhGhiPVHDA<@A-j-uoiKTIxc3m`z1|Bg!DV=gzb9`EWWy{v4_vo&A&Ch_Q}H zjuS-ta5S`I;qC;pHt4OBa2o>tr(xW|RT2GeA)g3|-+sl-2GPx;KW_T& zA+sq>;wjZK&)O(oH}&5w<8yEkpb&cg5KMjT4JB0C7h70bJx7S&(>&j7ILzl+(7c*H zo;e>+i5eT}Oc1L4=(F+PdfRhE7V02R`K=&Tl#e+@&EisBY#}rsTrA6Xbw3v*Wd)u*L z()-o_Q;mmv1{lbTS1I6M!2_M&?`shwR24z5JFm?3(b9&&a#?ci&6x+(P19dq`K?R1 zLpL`fSdwUWUWBV*thi*`cRK`|#@9=vG4J2x^jb8`R+&?N%yP5Vkgs~^!?cp7Xk5(m z752PZ$g+-rbR10PRs=W<7~erAGi-lknx0At8q3kH(^Vg|gD0VvA1#Ofm1mxF&9W|i z&>ubQJ$^J4Qk>n0&p;&`{K=I`%{r^wZ$S`7gIY>#D|V_lTJFgqV;Pl)ZAV8HCkz>! zxE+W0hCIHRfgj2_hEVR`Keb!=NQkHiAw<>@S6;J?_T+?zD+Efk>2a!XxCBV@qO^vX z{b7B~E~K8!FI9Z|C8=`5-q~=j$@&_XG;}|u(8!VOL`;D9qVxGY#O7SbhWx4ABYLxBg>0utud1YotC3YtJaKS`Ox;(qgNBpYD>Cv z;w;_7I&XMUj;cLhE^(xvd#&s`ugKO{tB#C&Yk>4+_{{%8;)@-6kF7-xb7Cj>oy*;@ zD9<%X&rFr@>Bjh#WKEo9V~Xw6lM_+ghLd(->-&PVW>sA=`iV_5|FYW0GVdzWw6PcK zbkD)D-w|btA_KfgR;-Wu)9ohC>ixy!!q~H?ekY@uAbWR+?@OT4fHmf!!Cm z8eCUmrWY+^G_!QXH(^ZclJ*Zlv91`|VtVW`N7|f&xO8uI(Bp#sDDpuNEm6~Pvgc&wjMeNZYHEJ==FO(SqTNy!ul)4m1*p>;zBlCES0cJQDRfM zeBzcW$Dp-VV7y6mOS$;_2K+zse)PahX@4J|Ab$>_ML3Tlosv?BgKtmJm`}^eVq|B+ zd{?QWq(gDMr?mGs7;TLc4#|yRbcxX|nse|Kuyq0(q-^PD41qwPMcuT#wnm3tE_5F0 zc*V$}*R_|l1n-81hz6-vH^j z(d2_6l3zNtYM4gOdRP*wbLej704^xxh{=VAbvV7Y%zHsflPjHiY84}b(LrEk$hV-a zAovZRk80|t=VpXMKO_7re1EOCm93lgiZ|;CNc(-gzc>G9JpsCbYO1?{-v9Hr!2yBZ zCk}ka-`1pV)?0&q(c8%F_x=4nJe%jO6aJ#N5v6bGK?UCd090_M3I?kZ`Ly~kyRfi{ diff --git a/tests/testdata/points2.qml b/tests/testdata/points2.qml new file mode 100644 index 000000000000..2771aee26a4a --- /dev/null +++ b/tests/testdata/points2.qml @@ -0,0 +1,93 @@ + + + 250 + Importance + Heading + Class + + Class + + B52 + + + svg:/gpsicons/plane.svg + 11 + pixels + Importance + Importance + + + SolidLine + 1 + + SolidPattern + + + + Biplane + + + svg:/gpsicons/plane_orange.svg + 18 + pixels + Heading + Importance + + + SolidLine + 1 + + SolidPattern + + + + Jet + + + svg:/gpsicons/plane.svg + 11 + pixels + Heading + Importance + + + SolidLine + 1 + + SolidPattern + + + + + Class + + + + + + + + + + + + + diff --git a/tests/testdata/points2.zip b/tests/testdata/points2.zip new file mode 100644 index 0000000000000000000000000000000000000000..73505ddbd5e457bf517ad3d9fc37bb904ea6165f GIT binary patch literal 2393 zcmai#2{=@HAIFawV>g5rj3wJG%QWtwDDDimWK4}MTaq!d4MQeti9ubZh7iU&Q|5-U zyIFGWm#h;hB}&rP$fARr<000AgeLP6#$Z!{D*ArI!0GRo156-CnI?g{x5a8mT0t3Ltg+O&n z7ISfOa(BtW*YTv2#bRO@SkZ0aDhoGKQ{rjKQ0FxYA4w~iH4yRPR(w8SUy_|Rb{ocB90U{0;q|-#H zKB>>W5xxP^G!gn>Q&eT{wkNMI0fz#lr5-&c86BGF=_e5qD5WnVd{Z9hd1-1jCO^fx z6Zad0J+8oNHzl{Y%S^1aX)6{D0%lvHkJX!d26Ljva@MBkzJ8wnFFHB!ZD1gjY7G*5 zGcsTQ=5o>w6^tI7x|>ncje#FEtml#~K!3`FcVqZ8kp!}!5rHhDXey|$Xh~L7Bv>NH zp4n!E%i~Vl&@aBf)!SRW*fB1oEE8#)VfG74lupZ?Mw!#YGbxP9E4jrtm^X46OeTLE zUP@YUlljQ|iKvJp&IAWeIYdOZm_O_7wPF4*v3T5<^&Hc;X7odq*WZ%8#GEmVyJhqEP|85ya0Y9VWsE8u3e&Hx zsjOm?+RbGhOB@4%&(RtM? z)t~(V8g(p0XX-ZnOkbh`i^vDjxNTyd5gYJ)L8?<1@A*fHL&xz*JGe;)O~~)6rj z5jw5HO11I=jj85#?>|)S#vIYL1KrfPb%}WYWm}n(snM>k0n6c*e6o~>fy&5n=Y&qL zuXA|?ez|B+gIK6tj(zHg!zKWYIld8pSxknRiHccR^`gSw6Pr0}N`%REj8 z{K38sBTt;r6LO^9RpG6}MbnXqCvgD)3bds^$?m@2hKoY$k8{<@Y|^<-VApm>1lUCW z?(5>i!(%eVyq`WmV;}f8chn;#_zEt=o&{UjI+1D~fCE0}T;|f;8C}tN;qLs6({q%) zg^nbA${2S~J9k1PjYzIGn^-aCje{YvaLu>kF{UBQl-V6K=+QtJ+D{#!%dqWo${mdc z2g>zeN z%ga71*=B|E0_9z@4KA+>O-Y|g*B&i&HI`ke|9JEL_@N@$PuO|uwxRHKkS1?@`yCbS zd~jMTB;n-vyL$QxM(GfYVmN@POf;-j4mqeG(v-e%VMY^trq1WDv?l}Y-mpy*^$Q8bRqT|{a8NL} z6$fPZpl=oXI}&bG4BF<1Kq3$zN$*&VHQ9w|efDHv&_F)Y6`ILF`D%~XbtN=_$V-DV zzEj3LM^Hi~EnJv&^z^Ph84#u#{hV}SCWyHcJuPe&B9~|YX8NF~cbct9N4dICdJN^I zI7YcZvfzJ$_AZX$uSXf>%Y8G#Azu;x9R|MF+e*T(dYpe3D5I?ZPj7>Z|E~8VoHpxK zaAg1MZv#~vy}!ZK_x`pv)8@RkkZ*b$t@ZEzek}E7JptZLdYr=llOBZU%a?|5&Nxn@ JQqC>__zz?WsyzSz literal 0 HcmV?d00001 diff --git a/tests/testdata/points.geojson.gz b/tests/testdata/points3.geojson.gz similarity index 100% rename from tests/testdata/points.geojson.gz rename to tests/testdata/points3.geojson.gz diff --git a/tests/testdata/points3.qml b/tests/testdata/points3.qml new file mode 100644 index 000000000000..2771aee26a4a --- /dev/null +++ b/tests/testdata/points3.qml @@ -0,0 +1,93 @@ + + + 250 + Importance + Heading + Class + + Class + + B52 + + + svg:/gpsicons/plane.svg + 11 + pixels + Importance + Importance + + + SolidLine + 1 + + SolidPattern + + + + Biplane + + + svg:/gpsicons/plane_orange.svg + 18 + pixels + Heading + Importance + + + SolidLine + 1 + + SolidPattern + + + + Jet + + + svg:/gpsicons/plane.svg + 11 + pixels + Heading + Importance + + + SolidLine + 1 + + SolidPattern + + + + + Class + + + + + + + + + + + + + From a5cccc3baf9d5f2105efc422f9964494c464a7e9 Mon Sep 17 00:00:00 2001 From: Etienne Tourigny Date: Tue, 24 Apr 2012 18:00:18 -0300 Subject: [PATCH 5/5] add sip binding for getStyleURI() --- python/core/qgsmaplayer.sip | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/python/core/qgsmaplayer.sip b/python/core/qgsmaplayer.sip index dd4e6f0b8b60..f3c4e4b26d55 100644 --- a/python/core/qgsmaplayer.sip +++ b/python/core/qgsmaplayer.sip @@ -181,7 +181,7 @@ public: void removeCustomProperty( const QString& key ); /** Read the symbology for the current layer from the Dom node supplied. - * @param QDomNode node that will contain the symbology definition for this layer. + * @param node node that will contain the symbology definition for this layer. * @param errorMessage reference to string that will be updated with any error messages * @return true in case of success. */ @@ -244,10 +244,18 @@ public: /** A convenience function to capitalise the layer name */ static QString capitaliseLayerName(const QString name); - /** Retrieve the default style for this layer if one + /** Retrieve the style URI for this layer + * (either as a .qml file on disk or as a + * record in the users style table in their personal qgis.db) + * @return a QString withe the style file name + * @see also loadNamedStyle () and saveNamedStyle (); + */ + virtual QString getStyleURI( ); + + /** Retrieve the default style for this layer if one * exists (either as a .qml file on disk or as a * record in the users style table in their personal qgis.db) - * @param a reference to a flag that will be set to false if + * @param theResultFlag a reference to a flag that will be set to false if * we did not manage to load the default style. * @return a QString with any status messages * @see also loadNamedStyle (); @@ -257,12 +265,12 @@ public: /** Retrieve a named style for this layer if one * exists (either as a .qml file on disk or as a * record in the users style table in their personal qgis.db) - * @param QString theURI - the file name or other URI for the + * @param theURI - the file name or other URI for the * style file. First an attempt will be made to see if this * is a file and load that, if that fails the qgis.db styles * table will be consulted to see if there is a style who's * key matches the URI. - * @param a reference to a flag that will be set to false if + * @param theResultFlag a reference to a flag that will be set to false if * we did not manage to load the default style. * @return a QString with any status messages * @see also loadDefaultStyle (); @@ -274,7 +282,7 @@ public: /** Save the properties of this layer as the default style * (either as a .qml file on disk or as a * record in the users style table in their personal qgis.db) - * @param a reference to a flag that will be set to false if + * @param theResultFlag a reference to a flag that will be set to false if * we did not manage to save the default style. * @return a QString with any status messages * @see also loadNamedStyle () and saveNamedStyle() @@ -284,12 +292,12 @@ public: /** Save the properties of this layer as a named style * (either as a .qml file on disk or as a * record in the users style table in their personal qgis.db) - * @param QString theURI - the file name or other URI for the + * @param theURI - the file name or other URI for the * style file. First an attempt will be made to see if this * is a file and save to that, if that fails the qgis.db styles * table will be used to create a style entry who's * key matches the URI. - * @param a reference to a flag that will be set to false if + * @param theResultFlag a reference to a flag that will be set to false if * we did not manage to save the default style. * @return a QString with any status messages * @see also saveDefaultStyle ();