|
@@ -16,18 +16,18 @@ |
|
|
#include "sqlite3.h" |
|
|
|
|
|
#include "qgsgeopackagedataitems.h" |
|
|
#include "qgsgeopackageconnection.h" |
|
|
#include "qgsogrdbconnection.h" |
|
|
#include "qgslogger.h" |
|
|
#include "qgssettings.h" |
|
|
#include "qgsproject.h" |
|
|
#include "qgsvectorlayer.h" |
|
|
#include "qgsrasterlayer.h" |
|
|
#include "qgsogrprovider.h" |
|
|
#include "qgsogrdataitems.h" |
|
|
#include "qgsnewgeopackagelayerdialog.h" |
|
|
#include "qgsmessageoutput.h" |
|
|
#include "qgsvectorlayerexporter.h" |
|
|
#include "qgsgeopackagerasterwritertask.h" |
|
|
#include "gdal.h" |
|
|
|
|
|
#include <QAction> |
|
|
#include <QMessageBox> |
|
@@ -62,10 +62,10 @@ QgsGeoPackageRootItem::~QgsGeoPackageRootItem() |
|
|
QVector<QgsDataItem *> QgsGeoPackageRootItem::createChildren() |
|
|
{ |
|
|
QVector<QgsDataItem *> connections; |
|
|
|
|
|
Q_FOREACH ( const QString &connName, QgsGeoPackageConnection::connectionList() ) |
|
|
const QStringList connList( QgsOgrDbConnection::connectionList( QStringLiteral( "GPKG" ) ) ); |
|
|
for ( const QString &connName : connList ) |
|
|
{ |
|
|
QgsGeoPackageConnection connection( connName ); |
|
|
QgsOgrDbConnection connection( connName, QStringLiteral( "GPKG" ) ); |
|
|
QgsDataItem *conn = new QgsGeoPackageConnectionItem( this, connection.name(), connection.uri().encodedUri() ); |
|
|
|
|
|
connections.append( conn ); |
|
@@ -102,9 +102,10 @@ void QgsGeoPackageRootItem::connectionsChanged() |
|
|
|
|
|
void QgsGeoPackageRootItem::newConnection() |
|
|
{ |
|
|
// TODO use QgsFileWidget |
|
|
QString path = QFileDialog::getOpenFileName( nullptr, tr( "Open GeoPackage" ), "", tr( "GeoPackage Database (*.gpkg)" ) ); |
|
|
storeConnection( path ); |
|
|
if ( QgsOgrDataCollectionItem::createConnection( QStringLiteral( "GeoPackage" ), QStringLiteral( "GeoPackage Database (*.gpkg)" ), QStringLiteral( "GPKG" ) ) ) |
|
|
{ |
|
|
refreshConnections(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@@ -115,36 +116,13 @@ void QgsGeoPackageRootItem::createDatabase() |
|
|
dialog.setCrs( QgsProject::instance()->defaultCrsForNewLayers() ); |
|
|
if ( dialog.exec() == QDialog::Accepted ) |
|
|
{ |
|
|
storeConnection( dialog.databasePath() ); |
|
|
} |
|
|
} |
|
|
#endif |
|
|
|
|
|
bool QgsGeoPackageRootItem::storeConnection( const QString &path ) |
|
|
{ |
|
|
QFileInfo fileInfo( path ); |
|
|
QString connName = fileInfo.fileName(); |
|
|
if ( ! path.isEmpty() ) |
|
|
{ |
|
|
bool ok = true; |
|
|
while ( ok && ! QgsGeoPackageConnection( connName ).path( ).isEmpty( ) ) |
|
|
if ( QgsOgrDataCollectionItem::storeConnection( dialog.databasePath(), QStringLiteral( "GPKG" ) ) ) |
|
|
{ |
|
|
|
|
|
connName = QInputDialog::getText( nullptr, tr( "Cannot add connection '%1'" ).arg( connName ), |
|
|
tr( "A connection with the same name already exists,\nplease provide a new name:" ), QLineEdit::Normal, |
|
|
QLatin1String( "" ), &ok ); |
|
|
} |
|
|
if ( ok && ! connName.isEmpty() ) |
|
|
{ |
|
|
QgsGeoPackageConnection connection( connName ); |
|
|
connection.setPath( path ); |
|
|
connection.save(); |
|
|
refreshConnections(); |
|
|
return true; |
|
|
} |
|
|
} |
|
|
return false; |
|
|
} |
|
|
#endif |
|
|
|
|
|
|
|
|
QgsGeoPackageConnectionItem::QgsGeoPackageConnectionItem( QgsDataItem *parent, QString name, QString path ) |
|
@@ -154,144 +132,21 @@ QgsGeoPackageConnectionItem::QgsGeoPackageConnectionItem( QgsDataItem *parent, Q |
|
|
mCapabilities |= Collapse; |
|
|
} |
|
|
|
|
|
QList<QgsGeoPackageLayerInfo *> QgsGeoPackageConnectionItem::subLayers( const QString &path ) const |
|
|
{ |
|
|
|
|
|
QList<QgsGeoPackageLayerInfo *> children; |
|
|
|
|
|
// Vector layers |
|
|
QgsVectorLayer layer( mPath, QStringLiteral( "ogr_tmp" ), QStringLiteral( "ogr" ) ); |
|
|
if ( ! layer.isValid( ) ) |
|
|
{ |
|
|
QgsDebugMsgLevel( tr( "Layer is not a valid GeoPackage Vector layer %1" ).arg( mPath ), 3 ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
// Collect mixed-geom layers |
|
|
QMultiMap<int, QStringList> subLayers; |
|
|
Q_FOREACH ( const QString &descriptor, layer.dataProvider()->subLayers( ) ) |
|
|
{ |
|
|
QStringList pieces = descriptor.split( ':' ); |
|
|
subLayers.insert( pieces[0].toInt(), pieces ); |
|
|
} |
|
|
int prevIdx = -1; |
|
|
Q_FOREACH ( const int &idx, subLayers.keys( ) ) |
|
|
{ |
|
|
if ( idx == prevIdx ) |
|
|
{ |
|
|
continue; |
|
|
} |
|
|
prevIdx = idx; |
|
|
QList<QStringList> values = subLayers.values( idx ); |
|
|
for ( int i = 0; i < values.size(); ++i ) |
|
|
{ |
|
|
QStringList pieces = values.at( i ); |
|
|
QString layerId = pieces[0]; |
|
|
QString name = pieces[1]; |
|
|
// QString featuresCount = pieces[2]; // Not used |
|
|
QString geometryType = pieces[3]; |
|
|
QgsLayerItem::LayerType layerType; |
|
|
layerType = layerTypeFromDb( geometryType ); |
|
|
// example URI for mixed-geoms geoms: '/path/gdal_sample_v1.2_no_extensions.gpkg|layerid=7|geometrytype=Point' |
|
|
// example URI for mixed-geoms attr table: '/path/gdal_sample_v1.2_no_extensions.gpkg|layername=MyLayer|layerid=7' |
|
|
// example URI for single geoms: '/path/gdal_sample_v1.2_no_extensions.gpkg|layerid=6' |
|
|
QString uri; |
|
|
// Check if it's a mixed geometry type |
|
|
if ( i == 0 && values.size() > 1 ) |
|
|
{ |
|
|
uri = QStringLiteral( "%1|layerid=%2|layername=%3" ).arg( mPath, layerId, name ); |
|
|
children.append( new QgsGeoPackageLayerInfo( mPath, uri, name, QgsLayerItem::LayerType::TableLayer ) ); |
|
|
} |
|
|
if ( layerType != QgsLayerItem::LayerType::NoType ) |
|
|
{ |
|
|
if ( geometryType.contains( QStringLiteral( "Collection" ), Qt::CaseInsensitive ) ) |
|
|
{ |
|
|
QgsDebugMsgLevel( QStringLiteral( "Layer %1 is a geometry collection: skipping %2" ).arg( name, mPath ), 3 ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
if ( values.size() > 1 ) |
|
|
{ |
|
|
uri = QStringLiteral( "%1|layerid=%2|geometrytype=%3" ).arg( mPath, layerId, geometryType ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
uri = QStringLiteral( "%1|layerid=%2" ).arg( mPath, layerId ); |
|
|
} |
|
|
QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Vector item %1 %2 %3" ).arg( name, uri, geometryType ), 3 ); |
|
|
children.append( new QgsGeoPackageLayerInfo( mPath, uri, name, layerType ) ); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
QgsDebugMsgLevel( QStringLiteral( "Layer type is not a supported GeoPackage Vector layer %1" ).arg( mPath ), 3 ); |
|
|
} |
|
|
QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Vector item %1 %2 %3" ).arg( name, uri, geometryType ), 3 ); |
|
|
} |
|
|
} |
|
|
} |
|
|
// Raster layers |
|
|
QgsRasterLayer rlayer( mPath, QStringLiteral( "gdal_tmp" ), QStringLiteral( "gdal" ), false ); |
|
|
if ( rlayer.dataProvider()->subLayers( ).size() > 0 ) |
|
|
{ |
|
|
Q_FOREACH ( const QString &uri, rlayer.dataProvider()->subLayers( ) ) |
|
|
{ |
|
|
QStringList pieces = uri.split( ':' ); |
|
|
QString name = pieces.value( pieces.length() - 1 ); |
|
|
QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Raster item %1 %2 %3" ).arg( name, uri ), 3 ); |
|
|
children.append( new QgsGeoPackageLayerInfo( mPath, uri, name, QgsLayerItem::LayerType::Raster ) ); |
|
|
} |
|
|
} |
|
|
else if ( rlayer.isValid( ) ) |
|
|
{ |
|
|
// Get the identifier |
|
|
GDALAllRegister(); |
|
|
// do not print errors, but write to debug |
|
|
CPLPushErrorHandler( CPLQuietErrorHandler ); |
|
|
CPLErrorReset(); |
|
|
GDALDatasetH hDS = GDALOpen( mPath.toUtf8().constData(), GA_ReadOnly ); |
|
|
CPLPopErrorHandler(); |
|
|
|
|
|
if ( ! hDS ) |
|
|
{ |
|
|
QgsDebugMsg( QString( "GDALOpen error # %1 : %2 " ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) ); |
|
|
|
|
|
} |
|
|
else |
|
|
{ |
|
|
QString uri( QStringLiteral( "GPKG:%1" ).arg( mPath ) ); |
|
|
QString name = GDALGetMetadataItem( hDS, "IDENTIFIER", NULL ); |
|
|
GDALClose( hDS ); |
|
|
// Fallback: will not be able to delete the table |
|
|
if ( name.isEmpty() ) |
|
|
{ |
|
|
name = QFileInfo( mPath ).fileName(); |
|
|
} |
|
|
else |
|
|
{ |
|
|
uri += QStringLiteral( ":%1" ).arg( name ); |
|
|
} |
|
|
|
|
|
QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Raster item %1 %2 %3" ).arg( name, mPath ), 3 ); |
|
|
children.append( new QgsGeoPackageLayerInfo( mPath, uri, name, QgsLayerItem::LayerType::Raster ) ); |
|
|
} |
|
|
} |
|
|
return children; |
|
|
} |
|
|
|
|
|
QVector<QgsDataItem *> QgsGeoPackageConnectionItem::createChildren() |
|
|
{ |
|
|
QVector<QgsDataItem *> children; |
|
|
QList<QgsGeoPackageLayerInfo *> layers = subLayers( mPath ); |
|
|
for ( const QgsGeoPackageLayerInfo *info : qgsAsConst( layers ) ) |
|
|
QList<QgsOgrDbLayerInfo *> layers = QgsOgrLayerItem::subLayers( mPath, QStringLiteral( "GPKG" ) ); |
|
|
for ( const QgsOgrDbLayerInfo *info : qgsAsConst( layers ) ) |
|
|
{ |
|
|
if ( info->type() == QgsLayerItem::LayerType::Raster ) |
|
|
if ( info->layerType() == QgsLayerItem::LayerType::Raster ) |
|
|
{ |
|
|
children.append( new QgsGeoPackageRasterLayerItem( this, info->name(), info->path(), info->uri() ) ); |
|
|
} |
|
|
else |
|
|
{ |
|
|
children.append( new QgsGeoPackageVectorLayerItem( this, info->name(), info->path(), info->uri(), info->type( ) ) ); |
|
|
children.append( new QgsGeoPackageVectorLayerItem( this, info->name(), info->path(), info->uri(), info->layerType( ) ) ); |
|
|
} |
|
|
} |
|
|
qDeleteAll( layers ); |
|
@@ -479,32 +334,6 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct |
|
|
} |
|
|
|
|
|
|
|
|
QgsLayerItem::LayerType QgsGeoPackageConnectionItem::layerTypeFromDb( const QString &geometryType ) |
|
|
{ |
|
|
if ( geometryType.contains( QStringLiteral( "Point" ), Qt::CaseInsensitive ) ) |
|
|
{ |
|
|
return QgsLayerItem::LayerType::Point; |
|
|
} |
|
|
else if ( geometryType.contains( QStringLiteral( "Polygon" ), Qt::CaseInsensitive ) ) |
|
|
{ |
|
|
return QgsLayerItem::LayerType::Polygon; |
|
|
} |
|
|
else if ( geometryType.contains( QStringLiteral( "LineString" ), Qt::CaseInsensitive ) ) |
|
|
{ |
|
|
return QgsLayerItem::LayerType::Line; |
|
|
} |
|
|
else if ( geometryType.contains( QStringLiteral( "Collection" ), Qt::CaseInsensitive ) ) |
|
|
{ |
|
|
return QgsLayerItem::LayerType::Vector; |
|
|
} |
|
|
// To be moved in a parent class that would also work for gdal and rasters |
|
|
else if ( geometryType.contains( QStringLiteral( "Raster" ), Qt::CaseInsensitive ) ) |
|
|
{ |
|
|
return QgsLayerItem::LayerType::Raster; |
|
|
} |
|
|
return QgsLayerItem::LayerType::TableLayer; |
|
|
} |
|
|
|
|
|
bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri, QString &errCause ) |
|
|
{ |
|
|
bool result = false; |
|
@@ -625,7 +454,7 @@ bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri |
|
|
|
|
|
void QgsGeoPackageConnectionItem::deleteConnection() |
|
|
{ |
|
|
QgsGeoPackageConnection::deleteConnection( name() ); |
|
|
QgsOgrDbConnection::deleteConnection( name(), QStringLiteral( "GeoPackage" ) ); |
|
|
mParent->refreshConnections(); |
|
|
} |
|
|
|
|
@@ -635,7 +464,7 @@ void QgsGeoPackageConnectionItem::addTable() |
|
|
QgsNewGeoPackageLayerDialog dialog( nullptr ); |
|
|
QFileInfo fileInfo( mPath ); |
|
|
QString connName = fileInfo.fileName(); |
|
|
QgsGeoPackageConnection connection( connName ); |
|
|
QgsOgrDbConnection connection( connName, QStringLiteral( "GeoPackage" ) ); |
|
|
if ( ! connection.path().isEmpty() ) |
|
|
{ |
|
|
dialog.setDatabasePath( connection.path() ); |
|
|