mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Merge pull request #5156 from boundlessgeo/gpkg_tab
[feature][needs-docs] Gpkg tab in source select dialog
This commit is contained in:
commit
32dee4480c
@ -565,8 +565,9 @@
|
||||
<file>themes/default/mActionNewMap.svg</file>
|
||||
<file>themes/default/mActionMapSettings.svg</file>
|
||||
<file>themes/default/mActionLockExtent.svg</file>
|
||||
<file>icons/qgis_icon.svg</file>
|
||||
<file>themes/default/mGeoPackage.svg</file>
|
||||
<file>themes/default/mActionAddGeoPackageLayer.svg</file>
|
||||
<file>icons/qgis_icon.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/images/tips">
|
||||
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
|
||||
|
39
images/themes/default/mActionAddGeoPackageLayer.svg
Normal file
39
images/themes/default/mActionAddGeoPackageLayer.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 10 KiB |
@ -414,7 +414,14 @@ Returns provider key
|
||||
|
||||
static QString layerTypeAsString( const LayerType &layerType );
|
||||
%Docstring
|
||||
Returns the string representatio of the given ``layerType``
|
||||
Returns the string representation of the given ``layerType``
|
||||
.. versionadded:: 3
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
static QString iconName( const LayerType &layerType );
|
||||
%Docstring
|
||||
Returns the icon name of the given ``layerType``
|
||||
.. versionadded:: 3
|
||||
:rtype: str
|
||||
%End
|
||||
|
@ -546,31 +546,7 @@ QgsLayerItem::QgsLayerItem( QgsDataItem *parent, const QString &name, const QStr
|
||||
, mUri( uri )
|
||||
, mLayerType( layerType )
|
||||
{
|
||||
switch ( layerType )
|
||||
{
|
||||
case Point:
|
||||
mIconName = QStringLiteral( "/mIconPointLayer.svg" );
|
||||
break;
|
||||
case Line:
|
||||
mIconName = QStringLiteral( "/mIconLineLayer.svg" );
|
||||
break;
|
||||
case Polygon:
|
||||
mIconName = QStringLiteral( "/mIconPolygonLayer.svg" );
|
||||
break;
|
||||
// TODO add a new icon for generic Vector layers
|
||||
case Vector :
|
||||
mIconName = QStringLiteral( "/mIconPolygonLayer.svg" );
|
||||
break;
|
||||
case TableLayer:
|
||||
mIconName = QStringLiteral( "/mIconTableLayer.svg" );
|
||||
break;
|
||||
case Raster:
|
||||
mIconName = QStringLiteral( "/mIconRaster.svg" );
|
||||
break;
|
||||
default:
|
||||
mIconName = QStringLiteral( "/mIconLayer.png" );
|
||||
break;
|
||||
}
|
||||
mIconName = iconName( layerType );
|
||||
}
|
||||
|
||||
QgsMapLayer::LayerType QgsLayerItem::mapLayerType() const
|
||||
@ -588,6 +564,36 @@ QString QgsLayerItem::layerTypeAsString( const QgsLayerItem::LayerType &layerTyp
|
||||
return staticMetaObject.enumerator( enumIdx ).valueToKey( layerType );
|
||||
}
|
||||
|
||||
QString QgsLayerItem::iconName( const QgsLayerItem::LayerType &layerType )
|
||||
{
|
||||
switch ( layerType )
|
||||
{
|
||||
case Point:
|
||||
return QStringLiteral( "/mIconPointLayer.svg" );
|
||||
break;
|
||||
case Line:
|
||||
return QStringLiteral( "/mIconLineLayer.svg" );
|
||||
break;
|
||||
case Polygon:
|
||||
return QStringLiteral( "/mIconPolygonLayer.svg" );
|
||||
break;
|
||||
// TODO add a new icon for generic Vector layers
|
||||
case Vector :
|
||||
return QStringLiteral( "/mIconPolygonLayer.svg" );
|
||||
break;
|
||||
case TableLayer:
|
||||
case Table:
|
||||
return QStringLiteral( "/mIconTableLayer.svg" );
|
||||
break;
|
||||
case Raster:
|
||||
return QStringLiteral( "/mIconRaster.svg" );
|
||||
break;
|
||||
default:
|
||||
return QStringLiteral( "/mIconLayer.png" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsLayerItem::equal( const QgsDataItem *other )
|
||||
{
|
||||
//QgsDebugMsg ( mPath + " x " + other->mPath );
|
||||
|
@ -384,11 +384,16 @@ class CORE_EXPORT QgsLayerItem : public QgsDataItem
|
||||
*/
|
||||
virtual QString comments() const { return QString(); }
|
||||
|
||||
/** Returns the string representatio of the given \a layerType
|
||||
/** Returns the string representation of the given \a layerType
|
||||
* \since QGIS 3
|
||||
*/
|
||||
static QString layerTypeAsString( const LayerType &layerType );
|
||||
|
||||
/** Returns the icon name of the given \a layerType
|
||||
* \since QGIS 3
|
||||
*/
|
||||
static QString iconName( const LayerType &layerType );
|
||||
|
||||
protected:
|
||||
|
||||
//! The provider key
|
||||
|
@ -7,9 +7,11 @@ SET (OGR_SRCS
|
||||
qgsogrexpressioncompiler.cpp
|
||||
qgsogrsourceselect.cpp
|
||||
qgsgeopackagedataitems.cpp
|
||||
qgsgeopackageconnection.cpp
|
||||
qgsgeopackagerasterwriter.cpp
|
||||
qgsgeopackagerasterwritertask.cpp
|
||||
qgsogrdbconnection.cpp
|
||||
qgsogrdbsourceselect.cpp
|
||||
qgsogrdbtablemodel.cpp
|
||||
)
|
||||
|
||||
SET(OGR_MOC_HDRS
|
||||
@ -18,8 +20,10 @@ SET(OGR_MOC_HDRS
|
||||
qgsogrconnpool.h
|
||||
qgsogrsourceselect.h
|
||||
qgsgeopackagedataitems.h
|
||||
qgsgeopackageconnection.h
|
||||
qgsgeopackagerasterwritertask.h
|
||||
qgsogrdbconnection.h
|
||||
qgsogrdbsourceselect.h
|
||||
qgsogrdbtablemodel.h
|
||||
)
|
||||
|
||||
########################################################
|
||||
|
@ -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,37 +116,14 @@ void QgsGeoPackageRootItem::createDatabase()
|
||||
dialog.setCrs( QgsProject::instance()->defaultCrsForNewLayers() );
|
||||
if ( dialog.exec() == QDialog::Accepted )
|
||||
{
|
||||
storeConnection( dialog.databasePath() );
|
||||
if ( QgsOgrDataCollectionItem::storeConnection( dialog.databasePath(), QStringLiteral( "GPKG" ) ) )
|
||||
{
|
||||
refreshConnections();
|
||||
}
|
||||
}
|
||||
}
|
||||
#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( ) )
|
||||
{
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
QgsGeoPackageConnectionItem::QgsGeoPackageConnectionItem( QgsDataItem *parent, QString name, QString path )
|
||||
: QgsDataCollectionItem( parent, name, path )
|
||||
@ -154,133 +132,25 @@ QgsGeoPackageConnectionItem::QgsGeoPackageConnectionItem( QgsDataItem *parent, Q
|
||||
mCapabilities |= Collapse;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QVector<QgsDataItem *> QgsGeoPackageConnectionItem::createChildren()
|
||||
{
|
||||
QVector<QgsDataItem *> children;
|
||||
|
||||
// Vector layers
|
||||
QgsVectorLayer layer( mPath, QStringLiteral( "ogr_tmp" ), QStringLiteral( "ogr" ) );
|
||||
if ( ! layer.isValid( ) )
|
||||
const auto layers = QgsOgrLayerItem::subLayers( mPath, QStringLiteral( "GPKG" ) );
|
||||
for ( const QgsOgrDbLayerInfo *info : layers )
|
||||
{
|
||||
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( ) )
|
||||
if ( info->layerType() == QgsLayerItem::LayerType::Raster )
|
||||
{
|
||||
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 );
|
||||
QgsGeoPackageVectorLayerItem *item = new QgsGeoPackageVectorLayerItem( this, name, mPath, uri, QgsLayerItem::LayerType::TableLayer );
|
||||
children.append( item );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
QgsGeoPackageVectorLayerItem *item = new QgsGeoPackageVectorLayerItem( this, name, mPath, uri, layerType );
|
||||
QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Vector item %1 %2 %3" ).arg( name, uri, geometryType ), 3 );
|
||||
children.append( item );
|
||||
}
|
||||
}
|
||||
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 );
|
||||
QgsGeoPackageRasterLayerItem *item = new QgsGeoPackageRasterLayerItem( this, name, mPath, uri );
|
||||
children.append( item );
|
||||
}
|
||||
}
|
||||
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() ) );
|
||||
|
||||
children.append( new QgsGeoPackageRasterLayerItem( this, info->name(), info->path(), info->uri() ) );
|
||||
}
|
||||
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 );
|
||||
QgsGeoPackageRasterLayerItem *item = new QgsGeoPackageRasterLayerItem( this, name, mPath, uri );
|
||||
children.append( item );
|
||||
children.append( new QgsGeoPackageVectorLayerItem( this, info->name(), info->path(), info->uri(), info->layerType( ) ) );
|
||||
}
|
||||
}
|
||||
qDeleteAll( layers );
|
||||
return children;
|
||||
|
||||
}
|
||||
|
||||
bool QgsGeoPackageConnectionItem::equal( const QgsDataItem *other )
|
||||
@ -327,8 +197,8 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct
|
||||
QStringList importResults;
|
||||
bool hasError = false;
|
||||
|
||||
QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( data );
|
||||
Q_FOREACH ( const QgsMimeDataUtils::Uri &dropUri, lst )
|
||||
const auto lst = QgsMimeDataUtils::decodeUriList( data );
|
||||
for ( const QgsMimeDataUtils::Uri &dropUri : lst )
|
||||
{
|
||||
// Check that we are not copying over self
|
||||
if ( dropUri.uri.startsWith( mPath ) )
|
||||
@ -369,9 +239,8 @@ bool QgsGeoPackageConnectionItem::handleDrop( const QMimeData *data, Qt::DropAct
|
||||
|
||||
// check if the destination layer already exists
|
||||
bool exists = false;
|
||||
// Q_FOREACH won't detach ...
|
||||
const QVector< QgsDataItem *> c = children();
|
||||
for ( const auto child : c )
|
||||
const auto c( children() );
|
||||
for ( const QgsDataItem *child : c )
|
||||
{
|
||||
if ( child->name() == dropUri.name )
|
||||
{
|
||||
@ -464,32 +333,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;
|
||||
@ -536,7 +379,7 @@ bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri
|
||||
QStringList optionalTables;
|
||||
optionalTables << QStringLiteral( "gpkg_extensions" )
|
||||
<< QStringLiteral( "gpkg_metadata_reference" );
|
||||
Q_FOREACH ( const QString &tableName, optionalTables )
|
||||
for ( const QString &tableName : qgsAsConst( optionalTables ) )
|
||||
{
|
||||
char *sql = sqlite3_mprintf( "DELETE FROM %w WHERE table_name = '%q'",
|
||||
tableName.toUtf8().constData(),
|
||||
@ -610,7 +453,7 @@ bool QgsGeoPackageConnectionItem::deleteGeoPackageRasterLayer( const QString uri
|
||||
|
||||
void QgsGeoPackageConnectionItem::deleteConnection()
|
||||
{
|
||||
QgsGeoPackageConnection::deleteConnection( name() );
|
||||
QgsOgrDbConnection::deleteConnection( name(), QStringLiteral( "GeoPackage" ) );
|
||||
mParent->refreshConnections();
|
||||
}
|
||||
|
||||
@ -620,7 +463,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() );
|
||||
@ -652,7 +495,8 @@ void QgsGeoPackageAbstractLayerItem::deleteLayer()
|
||||
{
|
||||
// Check if the layer is in the registry
|
||||
const QgsMapLayer *projectLayer = nullptr;
|
||||
Q_FOREACH ( const QgsMapLayer *layer, QgsProject::instance()->mapLayers() )
|
||||
const auto mapLayers( QgsProject::instance()->mapLayers() );
|
||||
for ( const QgsMapLayer *layer : mapLayers )
|
||||
{
|
||||
if ( layer->publicSource() == mUri )
|
||||
{
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "qgsdataitemprovider.h"
|
||||
#include "qgsdataprovider.h"
|
||||
|
||||
|
||||
/**
|
||||
* \brief The QgsGeoPackageAbstractLayerItem class is the base class for GeoPackage raster and vector layers
|
||||
*/
|
||||
@ -69,7 +70,6 @@ class QgsGeoPackageConnectionItem : public QgsDataCollectionItem
|
||||
|
||||
public:
|
||||
QgsGeoPackageConnectionItem( QgsDataItem *parent, QString name, QString path );
|
||||
|
||||
QVector<QgsDataItem *> createChildren() override;
|
||||
virtual bool equal( const QgsDataItem *other ) override;
|
||||
|
||||
@ -116,10 +116,6 @@ class QgsGeoPackageRootItem : public QgsDataCollectionItem
|
||||
void connectionsChanged();
|
||||
void createDatabase();
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool storeConnection( const QString &path );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,20 +14,26 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsogrdataitems.h"
|
||||
#include "qgsogrdbconnection.h"
|
||||
|
||||
#include "qgslogger.h"
|
||||
#include "qgsmessagelog.h"
|
||||
#include "qgssettings.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsrasterlayer.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QTextStream>
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include <ogr_srs_api.h>
|
||||
#include <cpl_error.h>
|
||||
#include <cpl_conv.h>
|
||||
#include <gdal.h>
|
||||
|
||||
// these are defined in qgsogrprovider.cpp
|
||||
QGISEXTERN QStringList fileExtensions();
|
||||
@ -107,6 +113,156 @@ bool QgsOgrLayerItem::setCrs( const QgsCoordinateReferenceSystem &crs )
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsLayerItem::LayerType QgsOgrLayerItem::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;
|
||||
}
|
||||
|
||||
QList<QgsOgrDbLayerInfo *> QgsOgrLayerItem::subLayers( const QString &path, const QString &driver )
|
||||
{
|
||||
|
||||
QList<QgsOgrDbLayerInfo *> children;
|
||||
|
||||
// Vector layers
|
||||
QgsVectorLayer layer( path, QStringLiteral( "ogr_tmp" ), QStringLiteral( "ogr" ) );
|
||||
if ( ! layer.isValid( ) )
|
||||
{
|
||||
QgsDebugMsgLevel( tr( "Layer is not a valid %1 Vector layer %2" ).arg( path ), 3 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Collect mixed-geom layers
|
||||
QMultiMap<int, QStringList> subLayersMap;
|
||||
const QStringList subLayersList( layer.dataProvider()->subLayers( ) );
|
||||
for ( const QString &descriptor : subLayersList )
|
||||
{
|
||||
QStringList pieces = descriptor.split( ':' );
|
||||
subLayersMap.insert( pieces[0].toInt(), pieces );
|
||||
}
|
||||
int prevIdx = -1;
|
||||
for ( const int &idx : subLayersMap.keys( ) )
|
||||
{
|
||||
if ( idx == prevIdx )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
prevIdx = idx;
|
||||
QList<QStringList> values = subLayersMap.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];
|
||||
QString geometryColumn = pieces[4];
|
||||
QgsLayerItem::LayerType layerType;
|
||||
layerType = QgsOgrLayerItem::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;
|
||||
if ( layerType != QgsLayerItem::LayerType::NoType )
|
||||
{
|
||||
if ( geometryType.contains( QStringLiteral( "Collection" ), Qt::CaseInsensitive ) )
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "Layer %1 is a geometry collection: skipping %2" ).arg( name, path ), 3 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( values.size() > 1 )
|
||||
{
|
||||
uri = QStringLiteral( "%1|layerid=%2|geometrytype=%3" ).arg( path, layerId, geometryType );
|
||||
}
|
||||
else
|
||||
{
|
||||
uri = QStringLiteral( "%1|layerid=%2" ).arg( path, layerId );
|
||||
}
|
||||
QgsDebugMsgLevel( QStringLiteral( "Adding %1 Vector item %2 %3 %4" ).arg( driver, name, uri, geometryType ), 3 );
|
||||
children.append( new QgsOgrDbLayerInfo( path, uri, name, geometryColumn, geometryType, layerType ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "Layer type is not a supported %1 Vector layer %2" ).arg( driver, path ), 3 );
|
||||
uri = QStringLiteral( "%1|layerid=%2|layername=%3" ).arg( path, layerId, name );
|
||||
children.append( new QgsOgrDbLayerInfo( path, uri, name, geometryColumn, geometryType, QgsLayerItem::LayerType::TableLayer ) );
|
||||
}
|
||||
QgsDebugMsgLevel( QStringLiteral( "Adding %1 Vector item %2 %3 %4" ).arg( driver, name, uri, geometryType ), 3 );
|
||||
}
|
||||
}
|
||||
}
|
||||
// Raster layers
|
||||
QgsRasterLayer rlayer( path, QStringLiteral( "gdal_tmp" ), QStringLiteral( "gdal" ), false );
|
||||
if ( rlayer.dataProvider()->subLayers( ).size() > 0 )
|
||||
{
|
||||
const QStringList layers( rlayer.dataProvider()->subLayers( ) );
|
||||
for ( const QString &uri : layers )
|
||||
{
|
||||
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 QgsOgrDbLayerInfo( path, uri, name, QStringLiteral( "" ), QStringLiteral( "Raster" ), 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( path.toUtf8().constData(), GA_ReadOnly );
|
||||
CPLPopErrorHandler();
|
||||
|
||||
if ( ! hDS )
|
||||
{
|
||||
QgsDebugMsg( QString( "GDALOpen error # %1 : %2 " ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
QString uri( QStringLiteral( "%1:%1" ).arg( driver, path ) );
|
||||
QString name = GDALGetMetadataItem( hDS, "IDENTIFIER", NULL );
|
||||
GDALClose( hDS );
|
||||
// Fallback: will not be able to delete the table
|
||||
if ( name.isEmpty() )
|
||||
{
|
||||
name = QFileInfo( path ).fileName();
|
||||
}
|
||||
else
|
||||
{
|
||||
uri += QStringLiteral( ":%1" ).arg( name );
|
||||
}
|
||||
|
||||
QgsDebugMsgLevel( QStringLiteral( "Adding %1 Raster item %2 %3" ).arg( driver, name, path ), 3 );
|
||||
children.append( new QgsOgrDbLayerInfo( path, uri, name, QStringLiteral( "" ), QStringLiteral( "Raster" ), QgsLayerItem::LayerType::Raster ) );
|
||||
}
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
QString QgsOgrLayerItem::layerName() const
|
||||
{
|
||||
QFileInfo info( name() );
|
||||
@ -266,6 +422,38 @@ QVector<QgsDataItem *> QgsOgrDataCollectionItem::createChildren()
|
||||
return children;
|
||||
}
|
||||
|
||||
bool QgsOgrDataCollectionItem::storeConnection( const QString &path, const QString &ogrDriverName )
|
||||
{
|
||||
QFileInfo fileInfo( path );
|
||||
QString connName = fileInfo.fileName();
|
||||
if ( ! path.isEmpty() )
|
||||
{
|
||||
bool ok = true;
|
||||
while ( ok && ! QgsOgrDbConnection( connName, ogrDriverName ).path( ).isEmpty( ) )
|
||||
{
|
||||
|
||||
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() )
|
||||
{
|
||||
QgsOgrDbConnection connection( connName, ogrDriverName );
|
||||
connection.setPath( path );
|
||||
connection.save();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QgsOgrDataCollectionItem::createConnection( const QString &name, const QString &extensions, const QString &ogrDriverName )
|
||||
{
|
||||
QString path = QFileDialog::getOpenFileName( nullptr, tr( "Open %1" ).arg( name ), "", extensions );
|
||||
return storeConnection( path, ogrDriverName );
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
QGISEXTERN int dataCapabilities()
|
||||
|
@ -20,6 +20,39 @@
|
||||
#include "qgsogrprovider.h"
|
||||
#include "qgsdataitemprovider.h"
|
||||
|
||||
|
||||
/**
|
||||
* Holds the information about a gpkg layer
|
||||
*/
|
||||
class QgsOgrDbLayerInfo
|
||||
{
|
||||
public:
|
||||
QgsOgrDbLayerInfo( const QString &path, const QString &uri, const QString &name, const QString &theGeometryColumn, const QString &theGeometryType, const QgsLayerItem::LayerType &theLayerType )
|
||||
: mPath( path )
|
||||
, mUri( uri )
|
||||
, mName( name )
|
||||
, mGeometryColumn( theGeometryColumn )
|
||||
, mGeometryType( theGeometryType )
|
||||
, mLayerType( theLayerType )
|
||||
{
|
||||
}
|
||||
const QString path() const { return mPath; }
|
||||
const QString uri() const { return mUri; }
|
||||
const QString name() const { return mName; }
|
||||
const QString geometryColumn() const { return mGeometryColumn; }
|
||||
const QString geometryType() const { return mGeometryType; }
|
||||
QgsLayerItem::LayerType layerType() const { return mLayerType; }
|
||||
|
||||
private:
|
||||
QString mPath;
|
||||
QString mUri;
|
||||
QString mName;
|
||||
QString mGeometryColumn;
|
||||
QString mGeometryType;
|
||||
QgsLayerItem::LayerType mLayerType = QgsLayerItem::LayerType::NoType;
|
||||
};
|
||||
|
||||
|
||||
class QgsOgrLayerItem : public QgsLayerItem
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -29,6 +62,10 @@ class QgsOgrLayerItem : public QgsLayerItem
|
||||
bool setCrs( const QgsCoordinateReferenceSystem &crs ) override;
|
||||
|
||||
QString layerName() const override;
|
||||
//! Retrieve sub layers from a DB ogr layer \a path with the specified \a driver
|
||||
static QList<QgsOgrDbLayerInfo *> subLayers( const QString &path, const QString &driver );
|
||||
//! Return a LayerType from a geometry type string
|
||||
static QgsLayerItem::LayerType layerTypeFromDb( const QString &geometryType );
|
||||
|
||||
#ifdef HAVE_GUI
|
||||
QList<QAction *> actions() override;
|
||||
@ -47,8 +84,21 @@ class QgsOgrDataCollectionItem : public QgsDataCollectionItem
|
||||
QgsOgrDataCollectionItem( QgsDataItem *parent, QString name, QString path );
|
||||
|
||||
QVector<QgsDataItem *> createChildren() override;
|
||||
|
||||
/** Utility function to store DB connections
|
||||
* \param path to the DB
|
||||
* \param ogrDriverName the OGR/GDAL driver name (e.g. "GPKG")
|
||||
*/
|
||||
static bool storeConnection( const QString &path, const QString &ogrDriverName );
|
||||
|
||||
/** Utility function to create and tore a new DB connection
|
||||
* \param name is the translatable name of the managed layers (e.g. "GeoPackage")
|
||||
* \param extensions is a string with file extensions (e.g. "GeoPackage Database (*.gpkg *.GPKG)")
|
||||
* \param ogrDriverName the OGR/GDAL driver name (e.g. "GPKG")
|
||||
*/
|
||||
static bool createConnection( const QString &name, const QString &extensions, const QString &ogrDriverName );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // QGSOGRDATAITEMS_H
|
||||
|
@ -1,5 +1,5 @@
|
||||
/***************************************************************************
|
||||
qgsgeopackageconnection.cpp - selector for geopackage
|
||||
qgsogrdbconnection.cpp
|
||||
-------------------
|
||||
begin : August 2017
|
||||
copyright : (C) 2017 by Alessandro Pasotti
|
||||
@ -18,72 +18,76 @@
|
||||
#include "qgis.h"
|
||||
#include "qgsdatasourceuri.h"
|
||||
#include "qgssettings.h"
|
||||
#include "qgsgeopackageconnection.h"
|
||||
#include "qgsogrdbconnection.h"
|
||||
#include "qgslogger.h"
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
const QString QgsGeoPackageConnection::SETTINGS_PREFIX = QStringLiteral( "providers/geopackage" );
|
||||
|
||||
|
||||
QgsGeoPackageConnection::QgsGeoPackageConnection( const QString &connName )
|
||||
QgsOgrDbConnection::QgsOgrDbConnection( const QString &connName, const QString &settingsKey )
|
||||
: mConnName( connName )
|
||||
{
|
||||
mSettingsKey = settingsKey ;
|
||||
QgsSettings settings;
|
||||
|
||||
QString key = QStringLiteral( "%1/%2/path" ).arg( connectionsPath( ), mConnName );
|
||||
QString key = QStringLiteral( "%1/%2/path" ).arg( connectionsPath( settingsKey ), mConnName );
|
||||
mPath = settings.value( key ).toString();
|
||||
}
|
||||
|
||||
QgsGeoPackageConnection::~QgsGeoPackageConnection()
|
||||
QgsOgrDbConnection::~QgsOgrDbConnection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QgsDataSourceUri QgsGeoPackageConnection::uri()
|
||||
QgsDataSourceUri QgsOgrDbConnection::uri()
|
||||
{
|
||||
QgsDataSourceUri uri;
|
||||
uri.setEncodedUri( mPath );
|
||||
return uri;
|
||||
}
|
||||
|
||||
void QgsGeoPackageConnection::setPath( const QString &path )
|
||||
void QgsOgrDbConnection::setPath( const QString &path )
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
void QgsGeoPackageConnection::save( )
|
||||
void QgsOgrDbConnection::save( )
|
||||
{
|
||||
QgsSettings settings;
|
||||
settings.setValue( QStringLiteral( "%1/%2/path" ).arg( connectionsPath( ), mConnName ), mPath );
|
||||
settings.setValue( QStringLiteral( "%1/%2/path" ).arg( connectionsPath( mSettingsKey ), mConnName ), mPath );
|
||||
}
|
||||
|
||||
QString QgsGeoPackageConnection::connectionsPath()
|
||||
QString QgsOgrDbConnection::fullKey( const QString &settingsKey )
|
||||
{
|
||||
return QStringLiteral( "%1/connections" ).arg( SETTINGS_PREFIX );
|
||||
return QStringLiteral( "providers/ogr/%1" ).arg( settingsKey );
|
||||
}
|
||||
|
||||
QStringList QgsGeoPackageConnection::connectionList()
|
||||
QString QgsOgrDbConnection::connectionsPath( const QString &settingsKey )
|
||||
{
|
||||
return QStringLiteral( "%1/connections" ).arg( fullKey( settingsKey ) );
|
||||
}
|
||||
|
||||
const QStringList QgsOgrDbConnection::connectionList( const QString &settingsKey )
|
||||
{
|
||||
QgsSettings settings;
|
||||
settings.beginGroup( connectionsPath( ) );
|
||||
settings.beginGroup( connectionsPath( settingsKey ) );
|
||||
return settings.childGroups();
|
||||
}
|
||||
|
||||
QString QgsGeoPackageConnection::selectedConnection()
|
||||
QString QgsOgrDbConnection::selectedConnection( const QString &settingsKey )
|
||||
{
|
||||
QgsSettings settings;
|
||||
return settings.value( QStringLiteral( "%1/selected" ).arg( SETTINGS_PREFIX ) ).toString();
|
||||
return settings.value( QStringLiteral( "%1/selected" ).arg( connectionsPath( settingsKey ) ) ).toString();
|
||||
}
|
||||
|
||||
void QgsGeoPackageConnection::setSelectedConnection( const QString &name )
|
||||
void QgsOgrDbConnection::setSelectedConnection( const QString &connName, const QString &settingsKey )
|
||||
{
|
||||
QgsSettings settings;
|
||||
settings.setValue( QStringLiteral( "%1/selected" ).arg( SETTINGS_PREFIX ), name );
|
||||
settings.setValue( QStringLiteral( "%1/selected" ).arg( connectionsPath( settingsKey ) ), connName );
|
||||
}
|
||||
|
||||
void QgsGeoPackageConnection::deleteConnection( const QString &name )
|
||||
void QgsOgrDbConnection::deleteConnection( const QString &connName, const QString &settingsKey )
|
||||
{
|
||||
QgsSettings settings;
|
||||
settings.remove( QStringLiteral( "%1/%2" ).arg( connectionsPath(), name ) );
|
||||
settings.remove( QStringLiteral( "%1/%2" ).arg( connectionsPath( settingsKey ), connName ) );
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/***************************************************************************
|
||||
qgsgeopackageconnection.h - GeoPackage connection
|
||||
qgsogrdbconnection.h - QgsOgrDbConnection
|
||||
-------------------
|
||||
begin : August 2017
|
||||
copyright : (C) 2017 by Alessandro Pasotti
|
||||
@ -15,32 +15,30 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSGEOPACKAGECONNECTION_H
|
||||
#define QGSGEOPACKAGECONNECTION_H
|
||||
#ifndef QGSGOGRDBSCONNECTION_H
|
||||
#define QGSGOGRDBSCONNECTION_H
|
||||
|
||||
#include "qgsdatasourceuri.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
/*!
|
||||
* \brief Connections management
|
||||
* \brief Generic OGR DB Connections management
|
||||
*/
|
||||
class QgsGeoPackageConnection : public QObject
|
||||
class QgsOgrDbConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//! Constructor
|
||||
explicit QgsGeoPackageConnection( const QString &connName );
|
||||
explicit QgsOgrDbConnection( const QString &connName, const QString &settingsKey );
|
||||
|
||||
~QgsGeoPackageConnection();
|
||||
~QgsOgrDbConnection();
|
||||
|
||||
static QStringList connectionList();
|
||||
|
||||
static void deleteConnection( const QString &name );
|
||||
|
||||
static QString selectedConnection();
|
||||
static void setSelectedConnection( const QString &name );
|
||||
static const QStringList connectionList( const QString &settingsKey );
|
||||
static void deleteConnection( const QString &connName, const QString &settingsKey );
|
||||
static QString selectedConnection( const QString &settingsKey );
|
||||
static void setSelectedConnection( const QString &connName, const QString &settingsKey );
|
||||
|
||||
public:
|
||||
//! Return the uri
|
||||
@ -54,14 +52,14 @@ class QgsGeoPackageConnection : public QObject
|
||||
void setPath( const QString &path );
|
||||
//! Store the connection data in the settings
|
||||
void save();
|
||||
const static QString SETTINGS_PREFIX;
|
||||
|
||||
private:
|
||||
|
||||
static QString connectionsPath( );
|
||||
static QString fullKey( const QString &settingsKey );
|
||||
static QString connectionsPath( const QString &settingsKey );
|
||||
QString mConnName;
|
||||
QString mPath;
|
||||
QString mSettingsKey;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSGEOPACKAGECONNECTION_H
|
||||
#endif // QGSGOGRDBSCONNECTION_H
|
421
src/providers/ogr/qgsogrdbsourceselect.cpp
Normal file
421
src/providers/ogr/qgsogrdbsourceselect.cpp
Normal file
@ -0,0 +1,421 @@
|
||||
/***************************************************************************
|
||||
qgsogrdbsourceselect.cpp - QgsOgrDbSourceSelect
|
||||
|
||||
---------------------
|
||||
begin : 5.9.2017
|
||||
copyright : (C) 2017 by Alessandro Pasotti
|
||||
email : apasotti at boundlessgeo dot com
|
||||
based on work by : (C) 2008 by Sandro Furieri for spatialite source sel.
|
||||
email : a.furieri@lqt.it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgsogrdbsourceselect.h"
|
||||
#include "qgsogrdbconnection.h"
|
||||
#include "qgsogrdataitems.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsquerybuilder.h"
|
||||
#include "qgssettings.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
QgsOgrDbSourceSelect::QgsOgrDbSourceSelect( const QString &theSettingsKey, const QString &theName, const QString &theExtensions, QWidget *parent, Qt::WindowFlags fl, QgsProviderRegistry::WidgetMode theWidgetMode )
|
||||
: QgsAbstractDataSourceWidget( parent, fl, theWidgetMode )
|
||||
, mOgrDriverName( theSettingsKey )
|
||||
, mName( theName )
|
||||
, mExtension( theExtensions )
|
||||
{
|
||||
setupUi( this );
|
||||
setupButtons( buttonBox );
|
||||
connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsOgrDbSourceSelect::showHelp );
|
||||
|
||||
QgsSettings settings;
|
||||
restoreGeometry( settings.value( QStringLiteral( "ogr/%1SourceSelect/geometry" ).arg( ogrDriverName( ) ), QgsSettings::Section::Providers ).toByteArray() );
|
||||
mHoldDialogOpen->setChecked( settings.value( QStringLiteral( "ogr/%1SourceSelect/HoldDialogOpen" ).arg( ogrDriverName( ) ), false, QgsSettings::Section::Providers ).toBool() );
|
||||
|
||||
setWindowTitle( tr( "Add %1 Layer(s)" ).arg( name( ) ) );
|
||||
btnEdit->hide(); // hide the edit button
|
||||
btnSave->hide();
|
||||
btnLoad->hide();
|
||||
|
||||
mBuildQueryButton = new QPushButton( tr( "&Set Filter" ) );
|
||||
connect( mBuildQueryButton, &QAbstractButton::clicked, this, &QgsOgrDbSourceSelect::buildQuery );
|
||||
mBuildQueryButton->setEnabled( false );
|
||||
|
||||
if ( widgetMode() != QgsProviderRegistry::WidgetMode::None )
|
||||
{
|
||||
mHoldDialogOpen->hide();
|
||||
}
|
||||
|
||||
buttonBox->addButton( mBuildQueryButton, QDialogButtonBox::ActionRole );
|
||||
|
||||
populateConnectionList();
|
||||
|
||||
mSearchModeComboBox->addItem( tr( "Wildcard" ) );
|
||||
mSearchModeComboBox->addItem( tr( "RegExp" ) );
|
||||
|
||||
mSearchColumnComboBox->addItem( tr( "All" ) );
|
||||
mSearchColumnComboBox->addItem( tr( "Table" ) );
|
||||
mSearchColumnComboBox->addItem( tr( "Type" ) );
|
||||
mSearchColumnComboBox->addItem( tr( "Geometry column" ) );
|
||||
mSearchColumnComboBox->addItem( tr( "Sql" ) );
|
||||
|
||||
mProxyModel.setParent( this );
|
||||
mProxyModel.setFilterKeyColumn( -1 );
|
||||
mProxyModel.setFilterCaseSensitivity( Qt::CaseInsensitive );
|
||||
mProxyModel.setDynamicSortFilter( true );
|
||||
mProxyModel.setSourceModel( &mTableModel );
|
||||
mTablesTreeView->setModel( &mProxyModel );
|
||||
mTablesTreeView->setSortingEnabled( true );
|
||||
|
||||
connect( mTablesTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsOgrDbSourceSelect::treeWidgetSelectionChanged );
|
||||
|
||||
//for Qt < 4.3.2, passing -1 to include all model columns
|
||||
//in search does not seem to work
|
||||
mSearchColumnComboBox->setCurrentIndex( 1 );
|
||||
|
||||
//hide the search options by default
|
||||
//they will be shown when the user ticks
|
||||
//the search options group box
|
||||
mSearchLabel->setVisible( false );
|
||||
mSearchColumnComboBox->setVisible( false );
|
||||
mSearchColumnsLabel->setVisible( false );
|
||||
mSearchModeComboBox->setVisible( false );
|
||||
mSearchModeLabel->setVisible( false );
|
||||
mSearchTableEdit->setVisible( false );
|
||||
|
||||
cbxAllowGeometrylessTables->setDisabled( true );
|
||||
}
|
||||
|
||||
QgsOgrDbSourceSelect::~QgsOgrDbSourceSelect()
|
||||
{
|
||||
QgsSettings settings;
|
||||
settings.setValue( QStringLiteral( "ogr/%1SourceSelect/geometry" ).arg( ogrDriverName( ) ), saveGeometry(), QgsSettings::Section::Providers );
|
||||
settings.setValue( QStringLiteral( "ogr/%1SourceSelect/HoldDialogOpen" ).arg( ogrDriverName( ) ), mHoldDialogOpen->isChecked(), QgsSettings::Section::Providers );
|
||||
}
|
||||
|
||||
|
||||
// Remember which database is selected
|
||||
void QgsOgrDbSourceSelect::on_cmbConnections_activated( int )
|
||||
{
|
||||
dbChanged();
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::buildQuery()
|
||||
{
|
||||
setSql( mTablesTreeView->currentIndex() );
|
||||
}
|
||||
|
||||
|
||||
void QgsOgrDbSourceSelect::on_cbxAllowGeometrylessTables_stateChanged( int )
|
||||
{
|
||||
on_btnConnect_clicked();
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::on_mTablesTreeView_clicked( const QModelIndex &index )
|
||||
{
|
||||
mBuildQueryButton->setEnabled( index.parent().isValid() && mTablesTreeView->currentIndex().data( Qt::UserRole + 2 ) != QStringLiteral( "Raster" ) );
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::on_mTablesTreeView_doubleClicked( const QModelIndex &index )
|
||||
{
|
||||
setSql( index );
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::on_mSearchGroupBox_toggled( bool checked )
|
||||
{
|
||||
if ( mSearchTableEdit->text().isEmpty() )
|
||||
return;
|
||||
|
||||
on_mSearchTableEdit_textChanged( checked ? mSearchTableEdit->text() : QLatin1String( "" ) );
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::on_mSearchTableEdit_textChanged( const QString &text )
|
||||
{
|
||||
if ( mSearchModeComboBox->currentText() == tr( "Wildcard" ) )
|
||||
{
|
||||
mProxyModel._setFilterWildcard( text );
|
||||
}
|
||||
else if ( mSearchModeComboBox->currentText() == tr( "RegExp" ) )
|
||||
{
|
||||
mProxyModel._setFilterRegExp( text );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::on_mSearchColumnComboBox_currentIndexChanged( const QString &text )
|
||||
{
|
||||
if ( text == tr( "All" ) )
|
||||
{
|
||||
mProxyModel.setFilterKeyColumn( -1 );
|
||||
}
|
||||
else if ( text == tr( "Table" ) )
|
||||
{
|
||||
mProxyModel.setFilterKeyColumn( 0 );
|
||||
}
|
||||
else if ( text == tr( "Type" ) )
|
||||
{
|
||||
mProxyModel.setFilterKeyColumn( 1 );
|
||||
}
|
||||
else if ( text == tr( "Geometry column" ) )
|
||||
{
|
||||
mProxyModel.setFilterKeyColumn( 2 );
|
||||
}
|
||||
else if ( text == tr( "Sql" ) )
|
||||
{
|
||||
mProxyModel.setFilterKeyColumn( 3 );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::on_mSearchModeComboBox_currentIndexChanged( const QString &text )
|
||||
{
|
||||
Q_UNUSED( text );
|
||||
on_mSearchTableEdit_textChanged( mSearchTableEdit->text() );
|
||||
}
|
||||
|
||||
|
||||
void QgsOgrDbSourceSelect::populateConnectionList()
|
||||
{
|
||||
cmbConnections->clear();
|
||||
for ( const QString &name : QgsOgrDbConnection::connectionList( ogrDriverName( ) ) )
|
||||
{
|
||||
// retrieving the SQLite DB name and full path
|
||||
QString text = name + tr( "@" ) + QgsOgrDbConnection( name, ogrDriverName( ) ).path();
|
||||
cmbConnections->addItem( text );
|
||||
}
|
||||
setConnectionListPosition();
|
||||
|
||||
btnConnect->setDisabled( cmbConnections->count() == 0 );
|
||||
btnDelete->setDisabled( cmbConnections->count() == 0 );
|
||||
|
||||
cmbConnections->setDisabled( cmbConnections->count() == 0 );
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::on_btnNew_clicked()
|
||||
{
|
||||
if ( QgsOgrDataCollectionItem::createConnection( name(), extension(), ogrDriverName() ) )
|
||||
{
|
||||
emit connectionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QString QgsOgrDbSourceSelect::layerURI( const QModelIndex &index )
|
||||
{
|
||||
QStandardItem *item = mTableModel.itemFromIndex( index );
|
||||
QString uri( item->data().toString() );
|
||||
QString sql = mTableModel.itemFromIndex( index.sibling( index.row(), 3 ) )->text();
|
||||
if ( ! sql.isEmpty() )
|
||||
{
|
||||
uri += QStringLiteral( "|subset=%1" ).arg( sql );
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
// Slot for deleting an existing connection
|
||||
void QgsOgrDbSourceSelect::on_btnDelete_clicked()
|
||||
{
|
||||
QString subKey = cmbConnections->currentText();
|
||||
int idx = subKey.indexOf( '@' );
|
||||
if ( idx > 0 )
|
||||
subKey.truncate( idx );
|
||||
|
||||
QString msg = tr( "Are you sure you want to remove the %1 connection and all associated settings?" ).arg( subKey );
|
||||
QMessageBox::StandardButton result =
|
||||
QMessageBox::information( this, tr( "Confirm Delete" ), msg, QMessageBox::Ok | QMessageBox::Cancel );
|
||||
if ( result != QMessageBox::Ok )
|
||||
return;
|
||||
|
||||
QgsOgrDbConnection::deleteConnection( subKey, ogrDriverName() );
|
||||
populateConnectionList();
|
||||
emit connectionsChanged();
|
||||
}
|
||||
|
||||
|
||||
void QgsOgrDbSourceSelect::addButtonClicked()
|
||||
{
|
||||
|
||||
typedef QPair<QString, QString> LayerInfo;
|
||||
QList<LayerInfo> selectedVectors;
|
||||
QList<LayerInfo> selectedRasters;
|
||||
|
||||
typedef QMap < int, bool >schemaInfo;
|
||||
QMap < QString, schemaInfo > dbInfo;
|
||||
|
||||
QItemSelection selection = mTablesTreeView->selectionModel()->selection();
|
||||
QModelIndexList selectedIndices = selection.indexes();
|
||||
QStandardItem *currentItem = nullptr;
|
||||
|
||||
QModelIndexList::const_iterator selected_it = selectedIndices.constBegin();
|
||||
for ( ; selected_it != selectedIndices.constEnd(); ++selected_it )
|
||||
{
|
||||
if ( !selected_it->parent().isValid() )
|
||||
{
|
||||
//top level items only contain the schema names
|
||||
continue;
|
||||
}
|
||||
currentItem = mTableModel.itemFromIndex( mProxyModel.mapToSource( *selected_it ) );
|
||||
if ( !currentItem )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QString currentSchemaName = currentItem->parent()->text();
|
||||
|
||||
int currentRow = currentItem->row();
|
||||
if ( !dbInfo[currentSchemaName].contains( currentRow ) )
|
||||
{
|
||||
dbInfo[currentSchemaName][currentRow] = true;
|
||||
if ( currentItem->data( Qt::UserRole + 2 ).toString().contains( QStringLiteral( "Raster" ), Qt::CaseInsensitive ) )
|
||||
{
|
||||
selectedRasters << LayerInfo( layerURI( mProxyModel.mapToSource( *selected_it ) ), currentItem->data( Qt::DisplayRole ).toString() );
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedVectors << LayerInfo( layerURI( mProxyModel.mapToSource( *selected_it ) ), currentItem->data( Qt::DisplayRole ).toString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( selectedVectors.empty() && selectedRasters.empty() )
|
||||
{
|
||||
QMessageBox::information( this, tr( "Select Table" ), tr( "You must select a table in order to add a Layer." ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use OGR
|
||||
for ( const LayerInfo &info : qgsAsConst( selectedVectors ) )
|
||||
{
|
||||
emit addVectorLayer( info.first, info.second );
|
||||
}
|
||||
for ( const LayerInfo &info : qgsAsConst( selectedRasters ) )
|
||||
{
|
||||
emit addRasterLayer( info.first, info.second, QStringLiteral( "gdal" ) );
|
||||
}
|
||||
if ( widgetMode() == QgsProviderRegistry::WidgetMode::None && ! mHoldDialogOpen->isChecked() )
|
||||
{
|
||||
accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::on_btnConnect_clicked()
|
||||
{
|
||||
cbxAllowGeometrylessTables->setEnabled( false );
|
||||
|
||||
QString subKey = cmbConnections->currentText();
|
||||
int idx = subKey.indexOf( '@' );
|
||||
if ( idx > 0 )
|
||||
subKey.truncate( idx );
|
||||
|
||||
QgsOgrDbConnection conn( subKey, ogrDriverName() );
|
||||
|
||||
mPath = conn.path();
|
||||
const QList<QgsOgrDbLayerInfo *> layers = QgsOgrLayerItem::subLayers( mPath, ogrDriverName() );
|
||||
|
||||
QModelIndex rootItemIndex = mTableModel.indexFromItem( mTableModel.invisibleRootItem() );
|
||||
mTableModel.removeRows( 0, mTableModel.rowCount( rootItemIndex ), rootItemIndex );
|
||||
|
||||
// populate the table list
|
||||
// get the list of suitable tables and columns and populate the UI
|
||||
|
||||
mTableModel.setPath( mPath );
|
||||
|
||||
|
||||
for ( const QgsOgrDbLayerInfo *table : layers )
|
||||
{
|
||||
if ( cbxAllowGeometrylessTables->isChecked() || table->geometryType() != QStringLiteral( "None" ) )
|
||||
{
|
||||
mTableModel.addTableEntry( table->layerType(), table->name(), table->uri(), table->geometryColumn(), table->geometryType(), QLatin1String( "" ) );
|
||||
}
|
||||
}
|
||||
|
||||
mTablesTreeView->sortByColumn( 0, Qt::AscendingOrder );
|
||||
|
||||
//expand all the toplevel items
|
||||
int numTopLevelItems = mTableModel.invisibleRootItem()->rowCount();
|
||||
for ( int i = 0; i < numTopLevelItems; ++i )
|
||||
{
|
||||
mTablesTreeView->expand( mProxyModel.mapFromSource( mTableModel.indexFromItem( mTableModel.invisibleRootItem()->child( i ) ) ) );
|
||||
}
|
||||
mTablesTreeView->resizeColumnToContents( 0 );
|
||||
mTablesTreeView->resizeColumnToContents( 1 );
|
||||
|
||||
cbxAllowGeometrylessTables->setEnabled( true );
|
||||
|
||||
// Store selected connection
|
||||
QgsOgrDbConnection::setSelectedConnection( subKey, ogrDriverName() );
|
||||
qDeleteAll( layers );
|
||||
}
|
||||
|
||||
|
||||
void QgsOgrDbSourceSelect::setSql( const QModelIndex &index )
|
||||
{
|
||||
QModelIndex idx = mProxyModel.mapToSource( index );
|
||||
QString tableName = mTableModel.itemFromIndex( idx.sibling( idx.row(), 0 ) )->text();
|
||||
|
||||
std::unique_ptr<QgsVectorLayer> vlayer( new QgsVectorLayer( layerURI( idx ), tableName, QStringLiteral( "ogr" ) ) );
|
||||
|
||||
if ( !vlayer->isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// create a query builder object
|
||||
std::unique_ptr<QgsQueryBuilder> gb( new QgsQueryBuilder( vlayer.get(), this ) );
|
||||
|
||||
if ( gb->exec() )
|
||||
{
|
||||
mTableModel.setSql( mProxyModel.mapToSource( index ), gb->sql() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QgsOgrDbSourceSelect::dbChanged()
|
||||
{
|
||||
// Remember which database was selected.
|
||||
QgsSettings settings;
|
||||
settings.setValue( QStringLiteral( "GeoPackage/connections/selected" ), cmbConnections->currentText() );
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::refresh()
|
||||
{
|
||||
populateConnectionList();
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::setConnectionListPosition()
|
||||
{
|
||||
QString toSelect = QgsOgrDbConnection::selectedConnection( ogrDriverName() );
|
||||
toSelect += '@' + QgsOgrDbConnection( toSelect, ogrDriverName() ).path();
|
||||
|
||||
cmbConnections->setCurrentIndex( cmbConnections->findText( toSelect ) );
|
||||
|
||||
if ( cmbConnections->currentIndex() < 0 )
|
||||
{
|
||||
if ( toSelect.isNull() )
|
||||
cmbConnections->setCurrentIndex( 0 );
|
||||
else
|
||||
cmbConnections->setCurrentIndex( cmbConnections->count() - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::setSearchExpression( const QString ®exp )
|
||||
{
|
||||
Q_UNUSED( regexp );
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::treeWidgetSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
|
||||
{
|
||||
Q_UNUSED( deselected )
|
||||
emit enableButtons( !selected.isEmpty() );
|
||||
}
|
||||
|
||||
void QgsOgrDbSourceSelect::showHelp()
|
||||
{
|
||||
QgsHelp::openHelp( QStringLiteral( "managing_data_source/opening_data.html#GeoPackage-layers" ) );
|
||||
}
|
||||
|
110
src/providers/ogr/qgsogrdbsourceselect.h
Normal file
110
src/providers/ogr/qgsogrdbsourceselect.h
Normal file
@ -0,0 +1,110 @@
|
||||
/***************************************************************************
|
||||
qgsogrdbsourceselect.h - QgsOgrDbSourceSelect
|
||||
|
||||
---------------------
|
||||
begin : 5.9.2017
|
||||
copyright : (C) 2017 by Alessandro Pasotti
|
||||
based on work by : (C) 2008 by Sandro Furieri for spatialite source sel.
|
||||
email : a.furieri@lqt.it
|
||||
email : apasotti at boundlessgeo dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSGOGRDBSOURCESELECT_H
|
||||
#define QGSGOGRDBSOURCESELECT_H
|
||||
|
||||
#include "ui_qgsdbsourceselectbase.h"
|
||||
#include "qgsguiutils.h"
|
||||
#include "qgsdbfilterproxymodel.h"
|
||||
#include "qgshelp.h"
|
||||
#include "qgsabstractdatasourcewidget.h"
|
||||
#include "qgsogrdbtablemodel.h"
|
||||
|
||||
/**
|
||||
* @brief The QgsOgrDbSourceSelect class is a generic class for DB based OGR
|
||||
* source selects.
|
||||
*
|
||||
*/
|
||||
class QgsOgrDbSourceSelect: public QgsAbstractDataSourceWidget, private Ui::QgsDbSourceSelectBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/** Construct a DB Source Select with \a theOgrDriverName specified (i.e. "GPKG", "SQLite" etc.)
|
||||
* and \a theName as string for describing the layers managed by the source select (e.g. : "GeoPackage" etc.)
|
||||
* The \a extensions is a string dscribing the accepted file extensions (e.g. : "GeoPackage Database (*.gpkg *.GPKG)")
|
||||
*/
|
||||
QgsOgrDbSourceSelect( const QString &theOgrDriverName, const QString &theName, const QString &theExtensions, QWidget *parent = nullptr, Qt::WindowFlags fl = QgsGuiUtils::ModalDialogFlags, QgsProviderRegistry::WidgetMode theWidgetMode = QgsProviderRegistry::WidgetMode::None );
|
||||
|
||||
~QgsOgrDbSourceSelect();
|
||||
|
||||
QString layerURI( const QModelIndex &index );
|
||||
|
||||
//! Populate the connection list combo box
|
||||
void populateConnectionList();
|
||||
|
||||
// Store the selected database
|
||||
void dbChanged();
|
||||
|
||||
//! Returns the QSettings key name
|
||||
const QString ogrDriverName( ) { return mOgrDriverName; }
|
||||
|
||||
//! Returns the name of the managed layers, needs to be translatable
|
||||
const QString name( ) { return mName; }
|
||||
|
||||
//! Returns the extensions of the managed layers, needs to be translatable
|
||||
const QString extension() { return mExtension; }
|
||||
|
||||
//! Open file selector to add new connection
|
||||
static bool newConnection( QWidget *parent );
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
//! Triggered when the provider's connections need to be refreshed
|
||||
void refresh() override;
|
||||
void addButtonClicked() override;
|
||||
|
||||
/** Connects to the database using the stored connection parameters.
|
||||
* Once connected, available layers are displayed.
|
||||
*/
|
||||
void on_btnConnect_clicked();
|
||||
void buildQuery();
|
||||
//! Opens the create connection dialog to build a new connection
|
||||
void on_btnNew_clicked();
|
||||
//! Deletes the selected connection
|
||||
void on_btnDelete_clicked();
|
||||
void on_mSearchGroupBox_toggled( bool );
|
||||
void on_mSearchTableEdit_textChanged( const QString &text );
|
||||
void on_mSearchColumnComboBox_currentIndexChanged( const QString &text );
|
||||
void on_mSearchModeComboBox_currentIndexChanged( const QString &text );
|
||||
void on_cbxAllowGeometrylessTables_stateChanged( int );
|
||||
void setSql( const QModelIndex &index );
|
||||
void on_cmbConnections_activated( int );
|
||||
void on_mTablesTreeView_clicked( const QModelIndex &index );
|
||||
void on_mTablesTreeView_doubleClicked( const QModelIndex &index );
|
||||
void treeWidgetSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected );
|
||||
//!Sets a new regular expression to the model
|
||||
void setSearchExpression( const QString ®exp );
|
||||
|
||||
void showHelp();
|
||||
|
||||
private:
|
||||
void setConnectionListPosition();
|
||||
//! Model that acts as datasource for mTableTreeWidget
|
||||
QgsOgrDbTableModel mTableModel;
|
||||
QgsDatabaseFilterProxyModel mProxyModel;
|
||||
QPushButton *mBuildQueryButton = nullptr;
|
||||
QString mPath;
|
||||
QString mOgrDriverName;
|
||||
QString mName;
|
||||
QString mExtension;
|
||||
};
|
||||
|
||||
#endif // QGSGOGRDBSOURCESELECT_H
|
93
src/providers/ogr/qgsogrdbtablemodel.cpp
Normal file
93
src/providers/ogr/qgsogrdbtablemodel.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/***************************************************************************
|
||||
qgsogrdbtablemodel.cpp - QgsOgrDbTableModel
|
||||
|
||||
---------------------
|
||||
begin : 5.9.2017
|
||||
copyright : (C) 2017 by Alessandro Pasotti
|
||||
email : apasotti at boundlessgeo dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgsogrdbtablemodel.h"
|
||||
#include "qgsdataitem.h"
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
QgsOgrDbTableModel::QgsOgrDbTableModel()
|
||||
{
|
||||
QStringList headerLabels;
|
||||
headerLabels << tr( "Table" );
|
||||
headerLabels << tr( "Type" );
|
||||
headerLabels << tr( "Geometry column" );
|
||||
headerLabels << tr( "Sql" );
|
||||
setHorizontalHeaderLabels( headerLabels );
|
||||
}
|
||||
|
||||
void QgsOgrDbTableModel::addTableEntry( const QgsLayerItem::LayerType &layerType, const QString &tableName, const QString &uri, const QString &geometryColName, const QString &geometryType, const QString &sql )
|
||||
{
|
||||
//is there already a root item ?
|
||||
QStandardItem *dbItem = nullptr;
|
||||
QList < QStandardItem * >dbItems = findItems( mPath, Qt::MatchExactly, 0 );
|
||||
|
||||
//there is already an item
|
||||
if ( !dbItems.isEmpty() )
|
||||
{
|
||||
dbItem = dbItems.at( 0 );
|
||||
}
|
||||
else //create a new toplevel item
|
||||
{
|
||||
dbItem = new QStandardItem( mPath );
|
||||
dbItem->setFlags( Qt::ItemIsEnabled );
|
||||
invisibleRootItem()->setChild( invisibleRootItem()->rowCount(), dbItem );
|
||||
}
|
||||
|
||||
QList < QStandardItem * >childItemList;
|
||||
QStandardItem *typeItem = new QStandardItem( QgsApplication::getThemeIcon( QgsLayerItem::iconName( layerType ) ), geometryType );
|
||||
typeItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
|
||||
QStandardItem *tableItem = new QStandardItem( tableName );
|
||||
tableItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
|
||||
QStandardItem *geomItem = new QStandardItem( geometryColName );
|
||||
geomItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
|
||||
QStandardItem *sqlItem = new QStandardItem( sql );
|
||||
sqlItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable );
|
||||
|
||||
// Add uri and geometryType information
|
||||
tableItem->setData( uri, Qt::UserRole + 1 );
|
||||
tableItem->setData( geometryType, Qt::UserRole + 2 );
|
||||
|
||||
childItemList.push_back( tableItem );
|
||||
childItemList.push_back( typeItem );
|
||||
childItemList.push_back( geomItem );
|
||||
childItemList.push_back( sqlItem );
|
||||
|
||||
dbItem->appendRow( childItemList );
|
||||
++mTableCount;
|
||||
}
|
||||
|
||||
void QgsOgrDbTableModel::setSql( const QModelIndex &index, const QString &sql )
|
||||
{
|
||||
if ( !index.isValid() || !index.parent().isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//find out table name
|
||||
QModelIndex tableSibling = index.sibling( index.row(), 0 );
|
||||
QModelIndex geomSibling = index.sibling( index.row(), 2 );
|
||||
|
||||
if ( !tableSibling.isValid() || !geomSibling.isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex sqlIndex = index.sibling( index.row(), 3 );
|
||||
if ( sqlIndex.isValid() )
|
||||
{
|
||||
itemFromIndex( sqlIndex )->setText( sql );
|
||||
}
|
||||
}
|
65
src/providers/ogr/qgsogrdbtablemodel.h
Normal file
65
src/providers/ogr/qgsogrdbtablemodel.h
Normal file
@ -0,0 +1,65 @@
|
||||
/***************************************************************************
|
||||
qgsogrdbtablemodel.h - QgsOgrDbTableModel
|
||||
|
||||
---------------------
|
||||
begin : 5.9.2017
|
||||
copyright : (C) 2017 by Alessandro Pasotti
|
||||
email : apasotti at boundlessgeo dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSOGRDBTABLEMODEL_H
|
||||
#define QGSOGRDBTABLEMODEL_H
|
||||
|
||||
#include "qgis.h"
|
||||
#include "qgsdataitem.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QStandardItemModel>
|
||||
#include <type_traits>
|
||||
|
||||
class QgsOgrDbTableModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
QgsOgrDbTableModel();
|
||||
|
||||
//! Set the geometry type for the table
|
||||
void setGeometryTypesForTable( const QString &table, const QString &attribute, const QString &type );
|
||||
|
||||
//! Adds entry for one database table to the model
|
||||
void addTableEntry( const QgsLayerItem::LayerType &layerType, const QString &tableName, const QString &uri, const QString &geometryColName, const QString &geometryType, const QString &sql );
|
||||
|
||||
//! Sets an sql statement that belongs to a cell specified by a model index
|
||||
void setSql( const QModelIndex &index, const QString &sql );
|
||||
|
||||
//! Returns the number of tables in the model
|
||||
int tableCount() const
|
||||
{
|
||||
return mTableCount;
|
||||
}
|
||||
|
||||
//! Sets the DB full path
|
||||
void setPath( const QString &path )
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
private:
|
||||
//! Number of tables in the model
|
||||
int mTableCount;
|
||||
QString mPath;
|
||||
|
||||
QIcon iconForType( QgsWkbTypes::Type type ) const;
|
||||
QString displayStringForType( QgsWkbTypes::Type type ) const;
|
||||
//! Returns qgis wkbtype from database typename
|
||||
QgsWkbTypes::Type qgisTypeFromDbType( const QString &dbType ) const;
|
||||
};
|
||||
#endif // QGSOGRDBTABLEMODEL_H
|
@ -39,6 +39,7 @@ email : sherman at mrcc.com
|
||||
#ifdef HAVE_GUI
|
||||
#include "qgssourceselectprovider.h"
|
||||
#include "qgsogrsourceselect.h"
|
||||
#include "qgsogrdbsourceselect.h"
|
||||
#endif
|
||||
|
||||
#include "qgis.h"
|
||||
@ -697,7 +698,6 @@ static OGRwkbGeometryType ogrWkbGeometryTypeFromName( const QString &typeName )
|
||||
|
||||
QStringList QgsOgrProvider::subLayers() const
|
||||
{
|
||||
QgsDebugMsg( "Entered." );
|
||||
if ( !mValid )
|
||||
{
|
||||
return QStringList();
|
||||
@ -710,6 +710,14 @@ QStringList QgsOgrProvider::subLayers() const
|
||||
{
|
||||
OGRLayerH layer = OGR_DS_GetLayer( ogrDataSource, i );
|
||||
OGRFeatureDefnH fdef = OGR_L_GetLayerDefn( layer );
|
||||
// Get first column name,
|
||||
// TODO: add support for multiple
|
||||
QString geometryColumnName;
|
||||
OGRGeomFieldDefnH geomH = OGR_FD_GetGeomFieldDefn( fdef, 0 );
|
||||
if ( geomH )
|
||||
{
|
||||
geometryColumnName = QString::fromUtf8( OGR_GFld_GetNameRef( geomH ) );
|
||||
}
|
||||
QString layerName = QString::fromUtf8( OGR_FD_GetName( fdef ) );
|
||||
OGRwkbGeometryType layerGeomType = OGR_FD_GetGeomType( fdef );
|
||||
|
||||
@ -738,7 +746,7 @@ QStringList QgsOgrProvider::subLayers() const
|
||||
|
||||
QString geom = ogrWkbGeometryTypeName( layerGeomType );
|
||||
|
||||
mSubLayerList << QStringLiteral( "%1:%2:%3:%4" ).arg( i ).arg( layerName, layerFeatureCount == -1 ? tr( "Unknown" ) : QString::number( layerFeatureCount ), geom );
|
||||
mSubLayerList << QStringLiteral( "%1:%2:%3:%4:%5" ).arg( i ).arg( layerName, layerFeatureCount == -1 ? tr( "Unknown" ) : QString::number( layerFeatureCount ), geom, geometryColumnName );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -792,13 +800,12 @@ QStringList QgsOgrProvider::subLayers() const
|
||||
{
|
||||
QString geom = ogrWkbGeometryTypeName( ( bIs25D ) ? wkbSetZ( countIt.key() ) : countIt.key() );
|
||||
|
||||
QString sl = QStringLiteral( "%1:%2:%3:%4" ).arg( i ).arg( layerName ).arg( fCount.value( countIt.key() ) ).arg( geom );
|
||||
QString sl = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( i ).arg( layerName ).arg( fCount.value( countIt.key() ) ).arg( geom, geometryColumnName );
|
||||
QgsDebugMsg( "sub layer: " + sl );
|
||||
mSubLayerList << sl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mSubLayerList;
|
||||
}
|
||||
|
||||
@ -4393,12 +4400,48 @@ class QgsOgrVectorSourceSelectProvider : public QgsSourceSelectProvider
|
||||
};
|
||||
|
||||
|
||||
//! Provider for GPKG vector source select
|
||||
class QgsGeoPackageSourceSelectProvider : public QgsSourceSelectProvider
|
||||
{
|
||||
public:
|
||||
|
||||
virtual QString providerKey() const override { return QStringLiteral( "ogr" ); }
|
||||
virtual QString text() const override { return QObject::tr( "GeoPackage" ); }
|
||||
virtual int ordering() const override { return 45; }
|
||||
virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddGeoPackageLayer.svg" ) ); }
|
||||
virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override
|
||||
{
|
||||
return new QgsOgrDbSourceSelect( QStringLiteral( "GPKG" ), QObject::tr( "GeoPackage" ), QObject::tr( "GeoPackage Database (*.gpkg)" ), parent, fl, widgetMode );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* This has been tested and works just fine:
|
||||
//! Provider for SQLite vector source select
|
||||
class QgsSpatiaLiteSourceSelectProvider : public QgsSourceSelectProvider
|
||||
{
|
||||
public:
|
||||
|
||||
virtual QString providerKey() const override { return QStringLiteral( "ogr" ); }
|
||||
virtual QString text() const override { return QObject::tr( "SQLite" ); }
|
||||
virtual int ordering() const override { return 46; }
|
||||
virtual QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/mIconSpatialite.svg" ) ); }
|
||||
virtual QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override
|
||||
{
|
||||
return new QgsOgrDbSourceSelect( QStringLiteral( "SQLite" ), QObject::tr( "SQLite" ), QObject::tr( "SpatiaLite Database (*.db *.sqlite)" ), parent, fl, widgetMode );
|
||||
}
|
||||
};
|
||||
//*/
|
||||
|
||||
|
||||
QGISEXTERN QList<QgsSourceSelectProvider *> *sourceSelectProviders()
|
||||
{
|
||||
QList<QgsSourceSelectProvider *> *providers = new QList<QgsSourceSelectProvider *>();
|
||||
|
||||
*providers
|
||||
<< new QgsOgrVectorSourceSelectProvider;
|
||||
<< new QgsOgrVectorSourceSelectProvider
|
||||
<< new QgsGeoPackageSourceSelectProvider;
|
||||
// << new QgsSpatiaLiteSourceSelectProvider;
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ class PyQgsOGRProvider(unittest.TestCase):
|
||||
vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr')
|
||||
self.assertTrue(vl.isValid())
|
||||
self.assertEqual(len(vl.dataProvider().subLayers()), 1)
|
||||
self.assertEqual(vl.dataProvider().subLayers()[0], '0:testMixOfPolygonCurvePolygon:4:CurvePolygon')
|
||||
self.assertEqual(vl.dataProvider().subLayers()[0], '0:testMixOfPolygonCurvePolygon:4:CurvePolygon:')
|
||||
|
||||
def testMixOfLineStringCompoundCurve(self):
|
||||
|
||||
@ -128,7 +128,7 @@ class PyQgsOGRProvider(unittest.TestCase):
|
||||
vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr')
|
||||
self.assertTrue(vl.isValid())
|
||||
self.assertEqual(len(vl.dataProvider().subLayers()), 1)
|
||||
self.assertEqual(vl.dataProvider().subLayers()[0], '0:testMixOfLineStringCompoundCurve:5:CompoundCurve')
|
||||
self.assertEqual(vl.dataProvider().subLayers()[0], '0:testMixOfLineStringCompoundCurve:5:CompoundCurve:')
|
||||
|
||||
def testGpxElevation(self):
|
||||
# GPX without elevation data
|
||||
|
@ -84,7 +84,7 @@ class TestPyQgsOGRProviderGpkg(unittest.TestCase):
|
||||
ds = None
|
||||
|
||||
vl = QgsVectorLayer('{}'.format(tmpfile), 'test', 'ogr')
|
||||
self.assertEqual(vl.dataProvider().subLayers(), ['0:test:0:CurvePolygon'])
|
||||
self.assertEqual(vl.dataProvider().subLayers(), ['0:test:0:CurvePolygon:geom'])
|
||||
f = QgsFeature()
|
||||
f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0,0 1,1 1,0 0))'))
|
||||
vl.dataProvider().addFeatures([f])
|
||||
|
Loading…
x
Reference in New Issue
Block a user