QGIS API Documentation  3.27.0-Master (597e8eebd4)
qgsfilebaseddataitemprovider.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfilebaseddataitemprovider.cpp
3  --------------------------------------
4  Date : July 2021
5  Copyright : (C) 2021 by Nyall Dawson
6  Email : nyall dot dawson 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 
17 #include "qgsdataprovider.h"
18 #include "qgsproviderregistry.h"
19 #include "qgslogger.h"
20 #include "qgssettings.h"
21 #include "qgszipitem.h"
22 #include "qgsogrproviderutils.h"
23 #include "qgsstyle.h"
24 #include "qgsgdalutils.h"
25 #include "qgsgeopackagedataitems.h"
27 #include "qgsfieldsitem.h"
28 #include "qgsproviderutils.h"
29 #include "qgsmbtiles.h"
30 #include "qgsvectortiledataitems.h"
31 #include "qgsprovidermetadata.h"
32 #include <QUrlQuery>
33 
34 //
35 // QgsProviderSublayerItem
36 //
37 
39  const QgsProviderSublayerDetails &details, const QString &filePath )
40  : QgsLayerItem( parent, name, filePath.isEmpty() ? details.uri() : filePath, details.uri(), layerTypeFromSublayer( details ), details.providerKey() )
41  , mDetails( details )
42 {
43  mToolTip = details.uri();
44 
45  // no children, except for vector layers, which will show the fields item
47 }
48 
50 {
51  QVector<QgsDataItem *> children;
52 
53  if ( mDetails.type() == QgsMapLayerType::VectorLayer )
54  {
55  // sqlite gets special handling because it delegates to the dedicated spatialite provider
56  if ( mDetails.driverName() == QLatin1String( "SQLite" ) )
57  {
58  children.push_back( new QgsFieldsItem( this,
59  path() + QStringLiteral( "/columns/ " ),
60  QStringLiteral( R"(dbname="%1")" ).arg( parent()->path().replace( '"', QLatin1String( R"(\")" ) ) ),
61  QStringLiteral( "spatialite" ), QString(), name() ) );
62  }
63  else if ( mDetails.providerKey() == QLatin1String( "ogr" ) )
64  {
65  // otherwise we use the default OGR database connection approach, which is the generic way to handle this
66  // for all OGR layer types
67  children.push_back( new QgsFieldsItem( this,
68  path() + QStringLiteral( "/columns/ " ),
69  path(),
70  QStringLiteral( "ogr" ), QString(), name() ) );
71  }
72  }
73  return children;
74 }
75 
77 {
78  return mDetails;
79 }
80 
82 {
83  if ( parent() )
84  {
86  return connection;
87  }
88 
89  if ( mDetails.providerKey() == QLatin1String( "ogr" ) )
90  {
91  if ( QgsProviderMetadata *md = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) ) )
92  {
93  QVariantMap parts;
94  parts.insert( QStringLiteral( "path" ), path() );
95  return static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( md->encodeUri( parts ), {} ) );
96  }
97  }
98 
99  return nullptr;
100 }
101 
102 Qgis::BrowserLayerType QgsProviderSublayerItem::layerTypeFromSublayer( const QgsProviderSublayerDetails &sublayer )
103 {
104  switch ( sublayer.type() )
105  {
107  {
108  switch ( QgsWkbTypes::geometryType( sublayer.wkbType() ) )
109  {
112 
115 
118 
121 
124  }
125 
126  break;
127  }
130 
133 
136 
139 
142 
145  break;
146  }
148 }
149 
151 {
152  return mDetails.name();
153 }
154 
155 //
156 // QgsFileDataCollectionItem
157 //
158 
159 QgsFileDataCollectionItem::QgsFileDataCollectionItem( QgsDataItem *parent, const QString &name, const QString &path, const QList<QgsProviderSublayerDetails> &sublayers )
160  : QgsDataCollectionItem( parent, name, path )
161  , mSublayers( sublayers )
162 {
165  else
167 
168  if ( !qgsVsiPrefix( path ).isEmpty() )
169  {
170  mIconName = QStringLiteral( "/mIconZip.svg" );
171  }
172 }
173 
175 {
176  QList< QgsProviderSublayerDetails> sublayers;
178  || mSublayers.empty() )
179  {
181  }
182  else
183  {
184  sublayers = mSublayers;
185  }
186  // only ever use the initial sublayers for first population -- after that we requery when asked to create children,
187  // or the item won't "refresh" and update its sublayers when the actual file changes
188  mSublayers.clear();
189  // remove the fast flag -- after the first population we need to requery the dataset
191 
192  QVector<QgsDataItem *> children;
193  children.reserve( sublayers.size() );
194  for ( const QgsProviderSublayerDetails &sublayer : std::as_const( sublayers ) )
195  {
196  QgsProviderSublayerItem *item = new QgsProviderSublayerItem( this, sublayer.name(), sublayer, QString() );
197  children.append( item );
198  }
199 
200  return children;
201 }
202 
204 {
205  return true;
206 }
207 
209 {
210  QgsMimeDataUtils::Uri collectionUri;
211  collectionUri.uri = path();
212  collectionUri.layerType = QStringLiteral( "collection" );
213  collectionUri.filePath = path();
214  return { collectionUri };
215 }
216 
218 {
219  // test that file is valid with OGR
220  if ( OGRGetDriverCount() == 0 )
221  {
222  OGRRegisterAll();
223  }
224  // do not print errors, but write to debug
225  CPLPushErrorHandler( CPLQuietErrorHandler );
226  CPLErrorReset();
227  gdal::dataset_unique_ptr hDS( GDALOpenEx( path().toUtf8().constData(), GDAL_OF_VECTOR | GDAL_OF_READONLY, nullptr, nullptr, nullptr ) );
228  CPLPopErrorHandler();
229 
230  if ( ! hDS )
231  {
232  QgsDebugMsgLevel( QStringLiteral( "GDALOpen error # %1 : %2 on %3" ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ).arg( path() ), 2 );
233  return nullptr;
234  }
235 
236  GDALDriverH hDriver = GDALGetDatasetDriver( hDS.get() );
237  QString driverName = GDALGetDriverShortName( hDriver );
238 
240  if ( driverName == QLatin1String( "SQLite" ) )
241  {
242  // sqlite gets special handling, as we delegate to the native spatialite provider
243  if ( QgsProviderMetadata *md = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "spatialite" ) ) )
244  {
245  QgsDataSourceUri uri;
246  uri.setDatabase( path( ) );
247  conn = static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( uri.uri(), {} ) );
248  }
249  }
250  else
251  {
252  // for all other vector types we use the generic OGR provider
253  if ( QgsProviderMetadata *md = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) ) )
254  {
255  QVariantMap parts;
256  parts.insert( QStringLiteral( "path" ), path() );
257  conn = static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( md->encodeUri( parts ), {} ) );
258  }
259  }
260  return conn;
261 }
262 
263 //
264 // QgsFileBasedDataItemProvider
265 //
266 
268 {
269  return QStringLiteral( "files" );
270 }
271 
273 {
275 }
276 
278 {
279  if ( path.isEmpty() )
280  return nullptr;
281 
282  const QFileInfo info( path );
283  QString suffix = info.suffix().toLower();
284  const QString name = info.fileName();
285 
286  // special handling for some suffixes
287  if ( suffix.compare( QLatin1String( "gpkg" ), Qt::CaseInsensitive ) == 0 )
288  {
289  // Geopackage is special -- it gets a dedicated collection item type
290  QgsGeoPackageCollectionItem *item = new QgsGeoPackageCollectionItem( parentItem, name, path );
291  item->setCapabilities( item->capabilities2() | Qgis::BrowserItemCapability::ItemRepresentsFile );
292  return item;
293  }
294  else if ( suffix == QLatin1String( "txt" ) )
295  {
296  // never ever show .txt files as datasets in browser -- they are only used for geospatial data in extremely rare cases
297  // and are predominantly just noise in the browser
298  return nullptr;
299  }
300  // If a .tab exists, then the corresponding .map/.dat is very likely a
301  // side-car file of the .tab
302  else if ( suffix == QLatin1String( "map" ) || suffix == QLatin1String( "dat" ) )
303  {
304  if ( QFile::exists( QDir( info.path() ).filePath( info.baseName() + ".tab" ) ) || QFile::exists( QDir( info.path() ).filePath( info.baseName() + ".TAB" ) ) )
305  return nullptr;
306  }
307  // .dbf and .shx should only appear if .shp is not present
308  else if ( suffix == QLatin1String( "dbf" ) || suffix == QLatin1String( "shx" ) )
309  {
310  if ( QFile::exists( QDir( info.path() ).filePath( info.baseName() + ".shp" ) ) || QFile::exists( QDir( info.path() ).filePath( info.baseName() + ".SHP" ) ) )
311  return nullptr;
312  }
313  // skip QGIS style xml files
314  else if ( suffix == QLatin1String( "xml" ) && QgsStyle::isXmlStyleFile( path ) )
315  {
316  return nullptr;
317  }
318  // GDAL 3.1 Shapefile driver directly handles .shp.zip files
319  else if ( path.endsWith( QLatin1String( ".shp.zip" ), Qt::CaseInsensitive ) &&
320  GDALIdentifyDriverEx( path.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr ) )
321  {
322  suffix = QStringLiteral( "shp.zip" );
323  }
324  // special handling for mbtiles files
325  else if ( suffix == QLatin1String( "mbtiles" ) )
326  {
327  QgsMbTiles reader( path );
328  if ( reader.open() )
329  {
330  if ( reader.metadataValue( QStringLiteral( "format" ) ) == QLatin1String( "pbf" ) )
331  {
332  // these are vector tiles
333  QUrlQuery uq;
334  uq.addQueryItem( QStringLiteral( "type" ), QStringLiteral( "mbtiles" ) );
335  uq.addQueryItem( QStringLiteral( "url" ), path );
336  const QString encodedUri = uq.toString();
337  QgsVectorTileLayerItem *item = new QgsVectorTileLayerItem( parentItem, name, path, encodedUri );
338  item->setCapabilities( item->capabilities2() | Qgis::BrowserItemCapability::ItemRepresentsFile );
339  return item;
340  }
341  else
342  {
343  // handled by WMS provider
344  QUrlQuery uq;
345  uq.addQueryItem( QStringLiteral( "type" ), QStringLiteral( "mbtiles" ) );
346  uq.addQueryItem( QStringLiteral( "url" ), QUrl::fromLocalFile( path ).toString() );
347  const QString encodedUri = uq.toString();
348  QgsLayerItem *item = new QgsLayerItem( parentItem, name, path, encodedUri, Qgis::BrowserLayerType::Raster, QStringLiteral( "wms" ) );
351  return item;
352  }
353  }
354  }
355 
356  // hide blocklisted URIs, such as .aux.xml files
357  if ( QgsProviderRegistry::instance()->uriIsBlocklisted( path ) )
358  return nullptr;
359 
360  QgsSettings settings;
361 
362  Qgis::SublayerQueryFlags queryFlags = Qgis::SublayerQueryFlags();
363 
364  // should we fast scan only?
365  if ( ( settings.value( QStringLiteral( "qgis/scanItemsInBrowser2" ),
366  "extension" ).toString() == QLatin1String( "extension" ) ) ||
367  ( parentItem && settings.value( QStringLiteral( "qgis/scanItemsFastScanUris" ),
368  QStringList() ).toStringList().contains( parentItem->path() ) ) )
369  {
370  queryFlags |= Qgis::SublayerQueryFlag::FastScan;
371  }
372 
373  const QList<QgsProviderSublayerDetails> sublayers = QgsProviderRegistry::instance()->querySublayers( path, queryFlags );
374 
375  if ( sublayers.size() == 1
378  )
379  {
380  QgsProviderSublayerItem *item = new QgsProviderSublayerItem( parentItem, name, sublayers.at( 0 ), path );
382  return item;
383  }
384  else if ( !sublayers.empty() )
385  {
386  QgsFileDataCollectionItem *item = new QgsFileDataCollectionItem( parentItem, name, path, sublayers );
388  return item;
389  }
390  else
391  {
392  return nullptr;
393  }
394 }
395 
397 {
398  QFileInfo info( path );
399  QString suffix = info.suffix().toLower();
400 
401  QStringList dirExtensions = QgsOgrProviderUtils::directoryExtensions();
402  return dirExtensions.contains( suffix );
403 }
@ NotPopulated
Children not yet created.
@ Populated
Children created.
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
@ ItemRepresentsFile
Item's path() directly represents a file on disk (since QGIS 3.22)
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
@ FastScan
Indicates that the provider must scan for sublayers using the fastest possible approach – e....
@ ResolveGeometryType
Attempt to resolve the geometry type for vector sublayers.
BrowserLayerType
Browser item layer types.
Definition: qgis.h:403
@ Point
Vector point layer.
@ Plugin
Plugin based layer.
@ Line
Vector line layer.
@ Polygon
Vector polygon layer.
@ Vector
Generic vector layer.
@ VectorTile
Vector tile layer.
@ Raster
Raster layer.
@ TableLayer
Vector non-spatial layer.
@ PointCloud
Point cloud layer.
The QgsAbstractDatabaseProviderConnection class provides common functionality for DB based connection...
A Collection: logical collection of layers or subcollections, e.g.
Base class for all items in the model.
Definition: qgsdataitem.h:46
QString mToolTip
Definition: qgsdataitem.h:456
QVector< QgsDataItem * > children() const
Definition: qgsdataitem.h:337
QgsDataItem * parent() const
Gets item parent.
Definition: qgsdataitem.h:330
QString mIconName
Definition: qgsdataitem.h:457
QString name() const
Returns the name of the item (the displayed text for the item).
Definition: qgsdataitem.h:345
QString path() const
Definition: qgsdataitem.h:354
virtual void setState(Qgis::BrowserItemState state)
Set item state.
virtual void setCapabilities(Qgis::BrowserItemCapabilities capabilities)
Sets the capabilities for the data item.
Definition: qgsdataitem.h:310
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
Definition: qgsdataitem.h:303
Class for storing the component parts of a RDBMS data source URI (e.g.
QString uri(bool expandAuthConfig=true) const
Returns the complete URI as a string.
void setDatabase(const QString &database)
Sets the URI database name.
A collection of field items with some internal logic to retrieve the fields and a the vector layer in...
Definition: qgsfieldsitem.h:34
QString name() override
Human-readable name of the provider name.
int capabilities() const override
Returns combination of flags from QgsDataProvider::DataCapabilities.
bool handlesDirectoryPath(const QString &path) override
Returns true if the provider will handle the directory at the specified path.
QgsDataItem * createDataItem(const QString &path, QgsDataItem *parentItem) override
Create a new instance of QgsDataItem (or nullptr) for given path and parent item.
A data collection item for file based data collections (e.g.
bool hasDragEnabled() const override
Returns true if the item may be dragged.
QVector< QgsDataItem * > createChildren() override
Create children.
QgsMimeDataUtils::UriList mimeUris() const override
Returns mime URIs for the data item, most data providers will only return a single URI but some data ...
QgsAbstractDatabaseProviderConnection * databaseConnection() const override
For data items that represent a DB connection or one of its children, this method returns a connectio...
QgsFileDataCollectionItem(QgsDataItem *parent, const QString &name, const QString &path, const QList< QgsProviderSublayerDetails > &sublayers)
Constructor for QgsFileDataCollectionItem.
Item that represents a layer that can be opened with one of the providers.
Definition: qgslayeritem.h:30
Utility class for reading and writing MBTiles files (which are SQLite3 databases).
Definition: qgsmbtiles.h:39
QString metadataValue(const QString &key) const
Requests metadata value for the given key.
Definition: qgsmbtiles.cpp:80
bool open()
Tries to open the file, returns true on success.
Definition: qgsmbtiles.cpp:29
QList< QgsMimeDataUtils::Uri > UriList
Holds data provider key, description, and associated shared library file or function pointer informat...
QList< QgsProviderSublayerDetails > querySublayers(const QString &uri, Qgis::SublayerQueryFlags flags=Qgis::SublayerQueryFlags(), QgsFeedback *feedback=nullptr) const
Queries the specified uri and returns a list of any valid sublayers found in the dataset which can be...
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
Contains details about a sub layer available from a dataset.
QgsWkbTypes::Type wkbType() const
Returns the layer's WKB type, or QgsWkbTypes::Unknown if the WKB type is not application or unknown.
QString uri() const
Returns the layer's URI.
QgsMapLayerType type() const
Returns the layer type.
QString driverName() const
Returns the layer's driver name.
QString providerKey() const
Returns the associated data provider key.
QString name() const
Returns the layer's name.
A generic data item for file based layers.
QgsAbstractDatabaseProviderConnection * databaseConnection() const override
For data items that represent a DB connection or one of its children, this method returns a connectio...
QVector< QgsDataItem * > createChildren() override
Create children.
QgsProviderSublayerItem(QgsDataItem *parent, const QString &name, const QgsProviderSublayerDetails &details, const QString &filePath)
Constructor for QgsProviderSublayerItem.
QgsProviderSublayerDetails sublayerDetails() const
Returns the sublayer details for the item.
static bool sublayerDetailsAreIncomplete(const QList< QgsProviderSublayerDetails > &details, QgsProviderUtils::SublayerCompletenessFlags flags=QgsProviderUtils::SublayerCompletenessFlags())
Returns true if the sublayer details are incomplete, and require a more in-depth scan.
@ IgnoreUnknownGeometryType
Indicates that an unknown geometry type should not be considered as incomplete.
@ IgnoreUnknownFeatureCount
Indicates that an unknown feature count should not be considered as incomplete.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
static bool isXmlStyleFile(const QString &path)
Tests if the file at path is a QGIS style XML file.
Definition: qgsstyle.cpp:3034
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:968
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
Definition: qgsogrutils.h:139
QString qgsVsiPrefix(const QString &path)
Definition: qgis.cpp:192
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QString filePath
Path to file, if uri is associated with a file.
QString uri
Identifier of the data source recognized by its providerKey.
QString layerType
Type of URI.