LCOV - code coverage report
Current view: top level - core/providers/gdal - qgsgdaldataitems.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 181 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgsgdaldataitems.cpp
       3                 :            :     ---------------------
       4                 :            :     begin                : October 2011
       5                 :            :     copyright            : (C) 2011 by Martin Dobias
       6                 :            :     email                : wonder dot sk at gmail dot com
       7                 :            :  ***************************************************************************
       8                 :            :  *                                                                         *
       9                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      10                 :            :  *   it under the terms of the GNU General Public License as published by  *
      11                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      12                 :            :  *   (at your option) any later version.                                   *
      13                 :            :  *                                                                         *
      14                 :            :  ***************************************************************************/
      15                 :            : 
      16                 :            : #include "qgsgdaldataitems.h"
      17                 :            : 
      18                 :            : ///@cond PRIVATE
      19                 :            : #include "qgsgdalprovider.h"
      20                 :            : #include "qgslogger.h"
      21                 :            : #include "qgsmbtiles.h"
      22                 :            : #include "qgssettings.h"
      23                 :            : #include "qgsogrutils.h"
      24                 :            : #include "qgsproject.h"
      25                 :            : #include "qgsgdalutils.h"
      26                 :            : #include "qgsvectortiledataitems.h"
      27                 :            : #include "qgsproviderregistry.h"
      28                 :            : #include "symbology/qgsstyle.h"
      29                 :            : 
      30                 :            : #include <QFileInfo>
      31                 :            : #include <QAction>
      32                 :            : #include <mutex>
      33                 :            : #include <QMessageBox>
      34                 :            : #include <QUrlQuery>
      35                 :            : #include <QUrl>
      36                 :            : 
      37                 :            : // defined in qgsgdalprovider.cpp
      38                 :            : void buildSupportedRasterFileFilterAndExtensions( QString &fileFiltersString, QStringList &extensions, QStringList &wildcards );
      39                 :            : 
      40                 :            : 
      41                 :          0 : QgsGdalLayerItem::QgsGdalLayerItem( QgsDataItem *parent,
      42                 :            :                                     const QString &name, const QString &path, const QString &uri,
      43                 :            :                                     QStringList *sublayers )
      44                 :          0 :   : QgsLayerItem( parent, name, path, uri, QgsLayerItem::Raster, QStringLiteral( "gdal" ) )
      45                 :          0 : {
      46                 :          0 :   mToolTip = uri;
      47                 :            :   // save sublayers for subsequent access
      48                 :            :   // if there are sublayers, set populated=false so item can be populated on demand
      49                 :          0 :   if ( sublayers && !sublayers->isEmpty() )
      50                 :            :   {
      51                 :          0 :     mSublayers = *sublayers;
      52                 :            :     // We have sublayers: we are able to create children!
      53                 :          0 :     mCapabilities |= Fertile;
      54                 :          0 :     setState( NotPopulated );
      55                 :          0 :   }
      56                 :            :   else
      57                 :          0 :     setState( Populated );
      58                 :          0 : }
      59                 :            : 
      60                 :            : 
      61                 :          0 : bool QgsGdalLayerItem::setCrs( const QgsCoordinateReferenceSystem &crs )
      62                 :            : {
      63                 :          0 :   gdal::dataset_unique_ptr hDS( GDALOpen( mPath.toUtf8().constData(), GA_Update ) );
      64                 :          0 :   if ( !hDS )
      65                 :          0 :     return false;
      66                 :            : 
      67                 :          0 :   QString wkt = crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL );
      68                 :          0 :   if ( GDALSetProjection( hDS.get(), wkt.toLocal8Bit().data() ) != CE_None )
      69                 :            :   {
      70                 :          0 :     QgsDebugMsg( QStringLiteral( "Could not set CRS" ) );
      71                 :          0 :     return false;
      72                 :            :   }
      73                 :            : 
      74                 :          0 :   return true;
      75                 :          0 : }
      76                 :            : 
      77                 :          0 : QVector<QgsDataItem *> QgsGdalLayerItem::createChildren()
      78                 :            : {
      79                 :          0 :   QgsDebugMsgLevel( "Entered, path=" + path(), 3 );
      80                 :          0 :   QVector<QgsDataItem *> children;
      81                 :            : 
      82                 :            :   // get children from sublayers
      83                 :          0 :   if ( !mSublayers.isEmpty() )
      84                 :            :   {
      85                 :          0 :     QgsDataItem *childItem = nullptr;
      86                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "got %1 sublayers" ).arg( mSublayers.count() ), 3 );
      87                 :          0 :     for ( int i = 0; i < mSublayers.count(); i++ )
      88                 :            :     {
      89                 :          0 :       const QStringList parts = mSublayers[i].split( QgsDataProvider::sublayerSeparator() );
      90                 :          0 :       const QString path = parts[0];
      91                 :          0 :       const QString desc = parts[1];
      92                 :          0 :       childItem = new QgsGdalLayerItem( this, desc, path, path );
      93                 :          0 :       if ( childItem )
      94                 :            :       {
      95                 :          0 :         children.append( childItem );
      96                 :          0 :       }
      97                 :          0 :     }
      98                 :          0 :   }
      99                 :            : 
     100                 :          0 :   return children;
     101                 :          0 : }
     102                 :            : 
     103                 :          0 : QString QgsGdalLayerItem::layerName() const
     104                 :            : {
     105                 :          0 :   QFileInfo info( name() );
     106                 :          0 :   if ( info.suffix() == QLatin1String( "gz" ) )
     107                 :          0 :     return info.baseName();
     108                 :            :   else
     109                 :          0 :     return info.completeBaseName();
     110                 :          0 : }
     111                 :            : 
     112                 :            : // ---------------------------------------------------------------------------
     113                 :            : 
     114                 :          0 : QString QgsGdalDataItemProvider::name()
     115                 :            : {
     116                 :          0 :   return QStringLiteral( "GDAL" );
     117                 :            : }
     118                 :            : 
     119                 :          0 : int QgsGdalDataItemProvider::capabilities() const
     120                 :            : {
     121                 :          0 :   return QgsDataProvider::File | QgsDataProvider::Dir | QgsDataProvider::Net;
     122                 :            : }
     123                 :            : 
     124                 :          0 : QgsDataItem *QgsGdalDataItemProvider::createDataItem( const QString &pathIn, QgsDataItem *parentItem )
     125                 :            : {
     126                 :          0 :   static QString sFilterString;
     127                 :          0 :   static QStringList sExtensions = QStringList();
     128                 :          0 :   static QStringList sWildcards = QStringList();
     129                 :          0 :   static QMutex sBuildingFilters;
     130                 :            : 
     131                 :          0 :   QString path( pathIn );
     132                 :          0 :   if ( path.isEmpty() )
     133                 :          0 :     return nullptr;
     134                 :            : 
     135                 :            :   // if another provider has preference for this path, let it win. This allows us to hide known files
     136                 :            :   // more strongly associated with another provider from showing duplicate entries for the ogr provider.
     137                 :            :   // e.g. in particular this hides "ept.json" files from showing as a non-functional ogr data item, and
     138                 :            :   // instead ONLY shows them as the functional EPT point cloud provider items
     139                 :          0 :   if ( QgsProviderRegistry::instance()->shouldDeferUriForOtherProviders( path, QStringLiteral( "gdal" ) ) )
     140                 :            :   {
     141                 :          0 :     return nullptr;
     142                 :            :   }
     143                 :            : 
     144                 :            :   // hide blocklisted URIs, such as .aux.xml files
     145                 :          0 :   if ( QgsProviderRegistry::instance()->uriIsBlocklisted( path ) )
     146                 :          0 :     return nullptr;
     147                 :            : 
     148                 :          0 :   QgsDebugMsgLevel( "thePath = " + path, 2 );
     149                 :            : 
     150                 :            :   // zip settings + info
     151                 :          0 :   QgsSettings settings;
     152                 :          0 :   QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
     153                 :          0 :   QString vsiPrefix = QgsZipItem::vsiPrefix( path );
     154                 :          0 :   bool is_vsizip = ( vsiPrefix == QLatin1String( "/vsizip/" ) );
     155                 :          0 :   bool is_vsigzip = ( vsiPrefix == QLatin1String( "/vsigzip/" ) );
     156                 :          0 :   bool is_vsitar = ( vsiPrefix == QLatin1String( "/vsitar/" ) );
     157                 :            : 
     158                 :            :   // should we check ext. only?
     159                 :            :   // check if scanItemsInBrowser2 == extension or parent dir in scanItemsFastScanUris
     160                 :            :   // TODO - do this in dir item, but this requires a way to inform which extensions are supported by provider
     161                 :            :   // maybe a callback function or in the provider registry?
     162                 :          0 :   bool scanExtSetting = false;
     163                 :          0 :   if ( ( settings.value( QStringLiteral( "qgis/scanItemsInBrowser2" ),
     164                 :          0 :                          "extension" ).toString() == QLatin1String( "extension" ) ) ||
     165                 :          0 :        ( parentItem && settings.value( QStringLiteral( "qgis/scanItemsFastScanUris" ),
     166                 :          0 :                                        QStringList() ).toStringList().contains( parentItem->path() ) ) ||
     167                 :          0 :        ( ( is_vsizip || is_vsitar ) && parentItem && parentItem->parent() &&
     168                 :          0 :          settings.value( QStringLiteral( "qgis/scanItemsFastScanUris" ),
     169                 :          0 :                          QStringList() ).toStringList().contains( parentItem->parent()->path() ) ) )
     170                 :            :   {
     171                 :          0 :     scanExtSetting = true;
     172                 :          0 :   }
     173                 :            : 
     174                 :          0 :   if ( path.endsWith( QLatin1String( ".shp.zip" ), Qt::CaseInsensitive ) )
     175                 :            :   {
     176                 :            :     // .shp.zip are vector datasets
     177                 :          0 :     return nullptr;
     178                 :            :   }
     179                 :            : 
     180                 :            :   // get suffix, removing .gz if present
     181                 :          0 :   QString tmpPath = path; //path used for testing, not for layer creation
     182                 :          0 :   if ( is_vsigzip )
     183                 :          0 :     tmpPath.chop( 3 );
     184                 :          0 :   QFileInfo info( tmpPath );
     185                 :          0 :   QString suffix = info.suffix().toLower();
     186                 :            : 
     187                 :          0 :   if ( suffix == QLatin1String( "txt" ) )
     188                 :            :   {
     189                 :            :     // never ever show .txt files as datasets in browser -- they are only used for geospatial data in extremely rare cases
     190                 :            :     // and are predominantly just noise in the browser
     191                 :          0 :     return nullptr;
     192                 :            :   }
     193                 :            : 
     194                 :            :   // extract basename with extension
     195                 :          0 :   info.setFile( path );
     196                 :          0 :   QString name = info.fileName();
     197                 :            : 
     198                 :            :   // If a .tab exists, then the corresponding .map/.dat is very likely a
     199                 :            :   // side-car file of the .tab
     200                 :          0 :   if ( suffix == QLatin1String( "map" ) || suffix == QLatin1String( "dat" ) )
     201                 :            :   {
     202                 :          0 :     if ( QFileInfo( QDir( info.path() ), info.baseName() + ".tab" ).exists() )
     203                 :          0 :       return nullptr;
     204                 :          0 :   }
     205                 :            : 
     206                 :          0 :   QgsDebugMsgLevel( "path= " + path + " tmpPath= " + tmpPath + " name= " + name
     207                 :            :                     + " suffix= " + suffix + " vsiPrefix= " + vsiPrefix, 3 );
     208                 :            : 
     209                 :            :   // allow only normal files or VSIFILE items to continue
     210                 :          0 :   if ( !info.isFile() && vsiPrefix.isEmpty() )
     211                 :          0 :     return nullptr;
     212                 :            : 
     213                 :            :   // get supported extensions
     214                 :            :   static std::once_flag initialized;
     215                 :          0 :   std::call_once( initialized, [ = ]
     216                 :            :   {
     217                 :          0 :     buildSupportedRasterFileFilterAndExtensions( sFilterString, sExtensions, sWildcards );
     218                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "extensions: " ) + sExtensions.join( ' ' ), 2 );
     219                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "wildcards: " ) + sWildcards.join( ' ' ), 2 );
     220                 :          0 :   } );
     221                 :            : 
     222                 :            :   // skip QGIS style xml files
     223                 :          0 :   if ( path.endsWith( QLatin1String( ".xml" ), Qt::CaseInsensitive ) &&
     224                 :          0 :        QgsStyle::isXmlStyleFile( path ) )
     225                 :          0 :     return nullptr;
     226                 :            : 
     227                 :            :   // Filter files by extension
     228                 :          0 :   if ( !sExtensions.contains( suffix ) )
     229                 :            :   {
     230                 :          0 :     bool matches = false;
     231                 :          0 :     const auto constSWildcards = sWildcards;
     232                 :          0 :     for ( const QString &wildcard : constSWildcards )
     233                 :            :     {
     234                 :          0 :       QRegExp rx( wildcard, Qt::CaseInsensitive, QRegExp::Wildcard );
     235                 :          0 :       if ( rx.exactMatch( info.fileName() ) )
     236                 :            :       {
     237                 :          0 :         matches = true;
     238                 :          0 :         break;
     239                 :            :       }
     240                 :          0 :     }
     241                 :          0 :     if ( !matches )
     242                 :          0 :       return nullptr;
     243                 :          0 :   }
     244                 :            : 
     245                 :            :   // fix vsifile path and name
     246                 :          0 :   if ( !vsiPrefix.isEmpty() )
     247                 :            :   {
     248                 :            :     // add vsiPrefix to path if needed
     249                 :          0 :     if ( !path.startsWith( vsiPrefix ) )
     250                 :          0 :       path = vsiPrefix + path;
     251                 :            :     // if this is a /vsigzip/path_to_zip.zip/file_inside_zip remove the full path from the name
     252                 :            :     // no need to change the name I believe
     253                 :            : #if 0
     254                 :            :     if ( ( is_vsizip || is_vsitar ) && ( path != vsiPrefix + parentItem->path() ) )
     255                 :            :     {
     256                 :            :       name = path;
     257                 :            :       name = name.replace( vsiPrefix + parentItem->path() + '/', "" );
     258                 :            :     }
     259                 :            : #endif
     260                 :          0 :   }
     261                 :            : 
     262                 :          0 :   if ( suffix == QLatin1String( "mbtiles" ) )
     263                 :            :   {
     264                 :          0 :     QgsMbTiles reader( path );
     265                 :          0 :     if ( reader.open() )
     266                 :            :     {
     267                 :          0 :       if ( reader.metadataValue( "format" ) == QLatin1String( "pbf" ) )
     268                 :            :       {
     269                 :            :         // these are vector tiles
     270                 :          0 :         QUrlQuery uq;
     271                 :          0 :         uq.addQueryItem( QStringLiteral( "type" ), QStringLiteral( "mbtiles" ) );
     272                 :          0 :         uq.addQueryItem( QStringLiteral( "url" ), path );
     273                 :          0 :         QString encodedUri = uq.toString();
     274                 :          0 :         return new QgsVectorTileLayerItem( parentItem, name, path, encodedUri );
     275                 :          0 :       }
     276                 :            :       else
     277                 :            :       {
     278                 :            :         // handled by WMS provider
     279                 :          0 :         QUrlQuery uq;
     280                 :          0 :         uq.addQueryItem( QStringLiteral( "type" ), QStringLiteral( "mbtiles" ) );
     281                 :          0 :         uq.addQueryItem( QStringLiteral( "url" ), QUrl::fromLocalFile( path ).toString() );
     282                 :          0 :         QString encodedUri = uq.toString();
     283                 :          0 :         QgsLayerItem *item = new QgsLayerItem( parentItem, name, path, encodedUri, QgsLayerItem::Raster, QStringLiteral( "wms" ) );
     284                 :          0 :         item->setState( QgsDataItem::Populated );
     285                 :          0 :         return item;
     286                 :          0 :       }
     287                 :            :     }
     288                 :          0 :   }
     289                 :            : 
     290                 :            :   // Filters out the OGR/GDAL supported formats that can contain multiple layers
     291                 :            :   // and should be treated like a DB: GeoPackage and SQLite
     292                 :            :   // NOTE: this formats are scanned for rasters too and they are handled
     293                 :            :   //       by the "ogr" provider. For this reason they must
     294                 :            :   //       be skipped by "gdal" provider or the rasters will be listed
     295                 :            :   //       twice. ogrSupportedDbLayersExtensions must be kept in sync
     296                 :            :   //       with the companion variable (same name) in the ogr provider
     297                 :            :   //       class
     298                 :            :   // TODO: add more OGR supported multiple layers formats here!
     299                 :          0 :   QStringList ogrSupportedDbLayersExtensions;
     300                 :          0 :   ogrSupportedDbLayersExtensions << QStringLiteral( "gpkg" ) << QStringLiteral( "sqlite" ) << QStringLiteral( "db" ) << QStringLiteral( "gdb" );
     301                 :          0 :   QStringList ogrSupportedDbDriverNames;
     302                 :          0 :   ogrSupportedDbDriverNames << QStringLiteral( "GPKG" ) << QStringLiteral( "db" ) << QStringLiteral( "gdb" );
     303                 :            : 
     304                 :            :   // return item without testing if:
     305                 :            :   // scanExtSetting
     306                 :            :   // or zipfile and scan zip == "Basic scan"
     307                 :          0 :   if ( ( scanExtSetting ||
     308                 :          0 :          ( ( is_vsizip || is_vsitar ) && scanZipSetting == QLatin1String( "basic" ) ) ) &&
     309                 :          0 :        suffix != QLatin1String( "nc" ) )
     310                 :            :   {
     311                 :            :     // Skip this layer if it's handled by ogr:
     312                 :          0 :     if ( ogrSupportedDbLayersExtensions.contains( suffix ) )
     313                 :            :     {
     314                 :          0 :       return nullptr;
     315                 :            :     }
     316                 :            : 
     317                 :            :     // if this is a VRT file make sure it is raster VRT to avoid duplicates
     318                 :          0 :     if ( suffix == QLatin1String( "vrt" ) )
     319                 :            :     {
     320                 :            :       // do not print errors, but write to debug
     321                 :          0 :       CPLPushErrorHandler( CPLQuietErrorHandler );
     322                 :          0 :       CPLErrorReset();
     323                 :          0 :       GDALDriverH hDriver = GDALIdentifyDriver( path.toUtf8().constData(), nullptr );
     324                 :          0 :       CPLPopErrorHandler();
     325                 :          0 :       if ( !hDriver || GDALGetDriverShortName( hDriver ) == QLatin1String( "OGR_VRT" ) )
     326                 :            :       {
     327                 :          0 :         QgsDebugMsgLevel( QStringLiteral( "Skipping VRT file because root is not a GDAL VRT" ), 2 );
     328                 :          0 :         return nullptr;
     329                 :            :       }
     330                 :          0 :     }
     331                 :            :     // add the item
     332                 :          0 :     QStringList sublayers;
     333                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "adding item name=%1 path=%2" ).arg( name, path ), 2 );
     334                 :          0 :     QgsLayerItem *item = new QgsGdalLayerItem( parentItem, name, path, path, &sublayers );
     335                 :          0 :     if ( item )
     336                 :          0 :       return item;
     337                 :          0 :   }
     338                 :            : 
     339                 :            :   // test that file is valid with GDAL
     340                 :          0 :   GDALAllRegister();
     341                 :            :   // do not print errors, but write to debug
     342                 :          0 :   CPLPushErrorHandler( CPLQuietErrorHandler );
     343                 :          0 :   CPLErrorReset();
     344                 :          0 :   gdal::dataset_unique_ptr hDS( GDALOpen( path.toUtf8().constData(), GA_ReadOnly ) );
     345                 :          0 :   CPLPopErrorHandler();
     346                 :            : 
     347                 :          0 :   if ( ! hDS )
     348                 :            :   {
     349                 :          0 :     QgsDebugMsg( QStringLiteral( "GDALOpen error # %1 : %2 " ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) );
     350                 :          0 :     return nullptr;
     351                 :            :   }
     352                 :            : 
     353                 :          0 :   GDALDriverH hDriver = GDALGetDatasetDriver( hDS.get() );
     354                 :          0 :   QString ogrDriverName = GDALGetDriverShortName( hDriver );
     355                 :            : 
     356                 :            :   // Skip this layer if it's handled by ogr:
     357                 :          0 :   if ( ogrSupportedDbDriverNames.contains( ogrDriverName ) )
     358                 :            :   {
     359                 :          0 :     return nullptr;
     360                 :            :   }
     361                 :            : 
     362                 :          0 :   QStringList sublayers = QgsGdalProvider::subLayers( hDS.get() );
     363                 :          0 :   hDS.reset();
     364                 :            : 
     365                 :          0 :   QgsDebugMsgLevel( "GdalDataset opened " + path, 2 );
     366                 :            : 
     367                 :          0 :   QgsLayerItem *item = new QgsGdalLayerItem( parentItem, name, path, path,
     368                 :            :       &sublayers );
     369                 :            : 
     370                 :          0 :   return item;
     371                 :          0 : }
     372                 :            : 
     373                 :            : ///@endcond

Generated by: LCOV version 1.14