Merge pull request #3031 from rouault/gpkg_improvements

GeoPackage support related improvements
This commit is contained in:
Nyall Dawson 2016-05-02 18:35:28 +10:00
commit afdb6c78c0
27 changed files with 2263 additions and 70 deletions

View File

@ -64,6 +64,11 @@ ELSE(WIN32)
IF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION EQUAL 1 AND GDAL_VERSION_MINOR LESS 4))
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 1.4.0 or higher.")
ENDIF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION EQUAL 1 AND GDAL_VERSION_MINOR LESS 4))
IF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 11))
MESSAGE (WARNING "GDAL version is too old (${GDAL_VERSION}) to support GeoPackage. 1.11.0 or higher is recommended.")
ENDIF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 11))
ENDIF (GDAL_LIBRARY)
SET (CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_save} CACHE STRING "" FORCE)
ENDIF ()
@ -105,6 +110,10 @@ ELSE(WIN32)
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 1.4.0 or higher.")
ENDIF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 4))
IF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 11))
MESSAGE (WARNING "GDAL version is too old (${GDAL_VERSION}) to support GeoPackage. 1.11.0 or higher is recommended.")
ENDIF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 11))
# set INCLUDE_DIR to prefix+include
EXEC_PROGRAM(${GDAL_CONFIG}
ARGS --prefix

View File

@ -259,6 +259,7 @@
<file>themes/default/mActionNewComposer.svg</file>
<file>themes/default/mActionNewFolder.png</file>
<file>themes/default/mActionNewSpatiaLiteLayer.svg</file>
<file>themes/default/mActionNewGeoPackageLayer.svg</file>
<file>themes/default/mActionNewVectorLayer.svg</file>
<file>themes/default/mActionNodeTool.png</file>
<file>themes/default/mActionOffsetCurve.png</file>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -121,6 +121,7 @@
%Include qgsnewmemorylayerdialog.sip
%Include qgsnewnamedialog.sip
%Include qgsnewvectorlayerdialog.sip
%Include qgsnewgeopackagelayerdialog.sip
%Include qgsnumericsortlistviewitem.sip
%Include qgsoptionsdialogbase.sip
%Include qgsorderbydialog.sip

View File

@ -0,0 +1,14 @@
/** Dialog to set up parameters to create a new GeoPackage layer, and on accept() to create it and add it to the layers */
class QgsNewGeoPackageLayerDialog : QDialog
{
%TypeHeaderCode
#include <qgsnewgeopackagelayerdialog.h>
%End
public:
/** Constructor */
QgsNewGeoPackageLayerDialog( QWidget *parent /TransferThis/ = 0, const Qt::WindowFlags& fl = QgisGui::ModalDialogFlags );
~QgsNewGeoPackageLayerDialog();
};

View File

@ -254,8 +254,13 @@
// GDAL/OGR includes
//
#include <ogr_api.h>
#include <gdal_version.h>
#include <proj_api.h>
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(1,11,0)
#define SUPPORT_GEOPACKAGE
#endif
//
// Other includes
//
@ -334,6 +339,7 @@ extern "C"
#include <spatialite.h>
}
#include "qgsnewspatialitelayerdialog.h"
#include "qgsnewgeopackagelayerdialog.h"
#include "qgspythonutils.h"
@ -1431,6 +1437,7 @@ void QgisApp::createActions()
connect( mActionNewVectorLayer, SIGNAL( triggered() ), this, SLOT( newVectorLayer() ) );
connect( mActionNewSpatiaLiteLayer, SIGNAL( triggered() ), this, SLOT( newSpatialiteLayer() ) );
connect( mActionNewGeoPackageLayer, SIGNAL( triggered() ), this, SLOT( newGeoPackageLayer() ) );
connect( mActionNewMemoryLayer, SIGNAL( triggered() ), this, SLOT( newMemoryLayer() ) );
connect( mActionShowRasterCalculator, SIGNAL( triggered() ), this, SLOT( showRasterCalculator() ) );
connect( mActionShowAlignRasterTool, SIGNAL( triggered() ), this, SLOT( showAlignRasterTool() ) );
@ -1725,6 +1732,11 @@ void QgisApp::createMenus()
* For Mac, About and Exit are also automatically moved by Qt to the Application menu.
*/
// Layer menu
#ifndef SUPPORT_GEOPACKAGE
mNewLayerMenu->removeAction( mActionNewGeoPackageLayer );
#endif
// Panel and Toolbar Submenus
mPanelMenu = new QMenu( tr( "Panels" ), this );
mPanelMenu->setObjectName( "mPanelMenu" );
@ -1980,6 +1992,9 @@ void QgisApp::createToolBars()
bt->setPopupMode( QToolButton::MenuButtonPopup );
bt->addAction( mActionNewVectorLayer );
bt->addAction( mActionNewSpatiaLiteLayer );
#ifdef SUPPORT_GEOPACKAGE
bt->addAction( mActionNewGeoPackageLayer );
#endif
bt->addAction( mActionNewMemoryLayer );
QAction* defNewLayerAction = mActionNewVectorLayer;
@ -1994,6 +2009,11 @@ void QgisApp::createToolBars()
case 2:
defNewLayerAction = mActionNewMemoryLayer;
break;
#ifdef SUPPORT_GEOPACKAGE
case 3:
defNewLayerAction = mActionNewGeoPackageLayer;
break;
#endif
}
bt->setDefaultAction( defNewLayerAction );
QAction* newLayerAction = mLayerToolBar->addWidget( bt );
@ -3668,10 +3688,10 @@ void QgisApp::loadOGRSublayers( const QString& layertype, const QString& uri, co
}
QString layerName = elements.value( 0 );
QString layerType = elements.value( 1 );
if ( layerType == "any" )
QString layerGeometryType = elements.value( 1 );
if ( layerGeometryType == "any" )
{
layerType = "";
layerGeometryType = "";
elements.removeAt( 1 );
}
@ -3684,16 +3704,17 @@ void QgisApp::loadOGRSublayers( const QString& layertype, const QString& uri, co
composedURI = uri + "|layerindex=" + layerName;
}
if ( !layerType.isEmpty() )
if ( !layerGeometryType.isEmpty() )
{
composedURI += "|geometrytype=" + layerType;
composedURI += "|geometrytype=" + layerGeometryType;
}
// addVectorLayer( composedURI, list.at( i ), "ogr" );
QgsDebugMsg( "Creating new vector layer using " + composedURI );
QString name = list.at( i );
name.replace( ':', ' ' );
QString name = layerName;
if ( !layerGeometryType.isEmpty() )
name += " " + layerGeometryType;
QgsVectorLayer *layer = new QgsVectorLayer( composedURI, fileName + " " + name, "ogr", false );
if ( layer && layer->isValid() )
{
@ -4366,6 +4387,12 @@ void QgisApp::newSpatialiteLayer()
spatialiteDialog.exec();
}
void QgisApp::newGeoPackageLayer()
{
QgsNewGeoPackageLayerDialog dialog( this );
dialog.exec();
}
void QgisApp::showRasterCalculator()
{
QgsRasterCalcDialog d( this );
@ -11227,6 +11254,8 @@ void QgisApp::toolButtonActionTriggered( QAction *action )
settings.setValue( "/UI/defaultNewLayer", 1 );
else if ( action == mActionNewMemoryLayer )
settings.setValue( "/UI/defaultNewLayer", 2 );
else if ( action == mActionNewGeoPackageLayer )
settings.setValue( "/UI/defaultNewLayer", 3 );
bt->setDefaultAction( action );
}

View File

@ -948,6 +948,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
void newMemoryLayer();
//! Create a new empty spatialite layer
void newSpatialiteLayer();
//! Create a new empty GeoPackage layer
void newGeoPackageLayer();
//! Print the current map view frame
void newPrintComposer();
void showComposerManager();

View File

@ -188,6 +188,7 @@ SET(QGIS_CORE_SRCS
qgssnappingutils.cpp
qgsspatialindex.cpp
qgssqlexpressioncompiler.cpp
qgssqliteexpressioncompiler.cpp
qgsstatisticalsummary.cpp
qgsstringutils.cpp
qgstextlabelfeature.cpp

View File

@ -72,7 +72,7 @@ QgsFields QgsOgrUtils::readOgrFields( OGRFeatureH ogrFet, QTextCodec* encoding )
continue;
}
QString name = encoding->toUnicode( OGR_Fld_GetNameRef( fldDef ) );
QString name = encoding ? encoding->toUnicode( OGR_Fld_GetNameRef( fldDef ) ) : QString::fromUtf8( OGR_Fld_GetNameRef( fldDef ) );
QVariant::Type varType;
switch ( OGR_Fld_GetType( fldDef ) )
{
@ -137,8 +137,13 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
switch ( fields.at( attIndex ).type() )
{
case QVariant::String:
value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
{
if ( encoding )
value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
else
value = QVariant( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
break;
}
case QVariant::Int:
value = QVariant( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) );
break;

View File

@ -1,5 +1,5 @@
/***************************************************************************
qgsspatialiteexpressioncompiler.cpp
qgssqliteexpressioncompiler.cpp
-----------------------------------
begin : November 2015
copyright : (C) 2015 Nyall Dawson
@ -13,16 +13,17 @@
* *
***************************************************************************/
#include "qgsspatialiteexpressioncompiler.h"
#include "qgssqlexpressioncompiler.h"
#include "qgsspatialiteprovider.h"
///@cond PRIVATE
QgsSpatiaLiteExpressionCompiler::QgsSpatiaLiteExpressionCompiler( QgsSpatiaLiteFeatureSource* source )
: QgsSqlExpressionCompiler( source->mFields, QgsSqlExpressionCompiler::LikeIsCaseInsensitive )
#include "qgssqliteexpressioncompiler.h"
#include "qgssqlexpressioncompiler.h"
QgsSQLiteExpressionCompiler::QgsSQLiteExpressionCompiler( const QgsFields& fields )
: QgsSqlExpressionCompiler( fields, QgsSqlExpressionCompiler::LikeIsCaseInsensitive )
{
}
QgsSqlExpressionCompiler::Result QgsSpatiaLiteExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
QgsSqlExpressionCompiler::Result QgsSQLiteExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
{
switch ( node->nodeType() )
{
@ -47,12 +48,14 @@ QgsSqlExpressionCompiler::Result QgsSpatiaLiteExpressionCompiler::compileNode( c
return QgsSqlExpressionCompiler::compileNode( node, result );
}
QString QgsSpatiaLiteExpressionCompiler::quotedIdentifier( const QString& identifier )
QString QgsSQLiteExpressionCompiler::quotedIdentifier( const QString& identifier )
{
return QgsSpatiaLiteProvider::quotedIdentifier( identifier );
QString id( identifier );
id.replace( '\"', "\"\"" );
return id.prepend( '\"' ).append( '\"' );
}
QString QgsSpatiaLiteExpressionCompiler::quotedValue( const QVariant& value, bool& ok )
QString QgsSQLiteExpressionCompiler::quotedValue( const QVariant& value, bool& ok )
{
ok = true;
@ -73,10 +76,12 @@ QString QgsSpatiaLiteExpressionCompiler::quotedValue( const QVariant& value, boo
default:
case QVariant::String:
QString v = value.toString();
v.replace( '\'', "''" );
if ( v.contains( '\\' ) )
return v.replace( '\\', "\\\\" ).prepend( "E'" ).append( '\'' );
else
return v.prepend( '\'' ).append( '\'' );
// https://www.sqlite.org/lang_expr.html :
// """A string constant is formed by enclosing the string in single quotes (').
// A single quote within the string can be encoded by putting two single quotes
// in a row - as in Pascal. C-style escapes using the backslash character are not supported because they are not standard SQL. """
return v.replace( '\'', "''" ).prepend( '\'' ).append( '\'' );
}
}
///@endcond

View File

@ -1,5 +1,5 @@
/***************************************************************************
qgsspatialiteexpressioncompiler.h
qgssqliteexpressioncompiler.h
---------------------------------
begin : November 2015
copyright : (C) 2015 Nyall Dawson
@ -13,18 +13,32 @@
* *
***************************************************************************/
#ifndef QGSSPATIALITEEXPRESSIONCOMPILER_H
#define QGSSPATIALITEEXPRESSIONCOMPILER_H
#ifndef QGSSQLITEEXPRESSIONCOMPILER_H
#define QGSSQLITEEXPRESSIONCOMPILER_H
///@cond PRIVATE
#include "qgssqlexpressioncompiler.h"
#include "qgsexpression.h"
#include "qgsspatialitefeatureiterator.h"
class QgsSpatiaLiteExpressionCompiler : public QgsSqlExpressionCompiler
/** \ingroup core
* \class QgsSQLiteExpressionCompiler
* \brief Expression compiler for translation to SQlite SQL WHERE clauses.
*
* This class is designed to be used by spatialite and OGR providers.
* \note Added in version 2.16
* \note Not part of stable API, may change in future versions of QGIS
* \note Not available in Python bindings
*/
class CORE_EXPORT QgsSQLiteExpressionCompiler : public QgsSqlExpressionCompiler
{
public:
explicit QgsSpatiaLiteExpressionCompiler( QgsSpatiaLiteFeatureSource* source );
/** Constructor for expression compiler.
* @param fields fields from provider
*/
explicit QgsSQLiteExpressionCompiler( const QgsFields& fields );
protected:
@ -34,4 +48,6 @@ class QgsSpatiaLiteExpressionCompiler : public QgsSqlExpressionCompiler
};
#endif // QGSSPATIALITEEXPRESSIONCOMPILER_H
///@endcond
#endif // QGSSQLITEEXPRESSIONCOMPILER_H

View File

@ -982,6 +982,48 @@ QMap<QString, QgsVectorFileWriter::MetaData> QgsVectorFileWriter::initMetaData()
)
);
// GeoPackage
datasetOptions.clear();
layerOptions.clear();
layerOptions.insert( "IDENTIFIER", new StringOption(
QObject::tr( "Human-readable identifier (e.g. short name) for the layer content" ),
"" // Default value
) );
layerOptions.insert( "DESCRIPTION", new StringOption(
QObject::tr( "Human-readable description for the layer content" ),
"" // Default value
) );
layerOptions.insert( "FID", new StringOption(
QObject::tr( "Name for the feature identifier column" ),
"fid" // Default value
) );
layerOptions.insert( "GEOMETRY_NAME", new StringOption(
QObject::tr( "Name for the geometry column" ),
"geometry" // Default value
) );
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,0)
layerOptions.insert( "SPATIAL_INDEX", new BoolOption(
QObject::tr( "If a spatial index must be created." ),
true // Default value
) );
#endif
driverMetadata.insert( "GPKG",
MetaData(
"GeoPackage",
QObject::tr( "GeoPackage" ),
"*.gpkg",
"gpkg",
datasetOptions,
layerOptions
)
);
// Generic Mapping Tools [GMT]
datasetOptions.clear();
layerOptions.clear();

View File

@ -248,6 +248,7 @@ SET(QGIS_GUI_SRCS
qgsnewmemorylayerdialog.cpp
qgsnewnamedialog.cpp
qgsnewvectorlayerdialog.cpp
qgsnewgeopackagelayerdialog.cpp
qgsnumericsortlistviewitem.cpp
qgsoptionsdialogbase.cpp
qgsorderbydialog.cpp
@ -387,6 +388,7 @@ SET(QGIS_GUI_MOC_HDRS
qgsnewmemorylayerdialog.h
qgsnewnamedialog.h
qgsnewvectorlayerdialog.h
qgsnewgeopackagelayerdialog.h
qgsoptionsdialogbase.h
qgsorderbydialog.h
qgsowssourceselect.h

View File

@ -0,0 +1,515 @@
/***************************************************************************
qgsnewgeopackagelayerdialog.cpp
-------------------
begin : April 2016
copyright : (C) 2016 by Even Rouault
email : even.rouault at spatialys.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 "qgsnewgeopackagelayerdialog.h"
#include "qgis.h"
#include "qgsapplication.h"
#include "qgsproviderregistry.h"
#include "qgsvectorlayer.h"
#include "qgsmaplayerregistry.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsgenericprojectionselector.h"
#include "qgslogger.h"
#include <QPushButton>
#include <QSettings>
#include <QLineEdit>
#include <QMessageBox>
#include <QFileDialog>
#include <QLibrary>
#include <ogr_api.h>
#include <ogr_srs_api.h>
#include <gdal_version.h>
#include <cpl_error.h>
#include <cpl_string.h>
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,0)
#define SUPPORT_GEOMETRY_LESS
#define SUPPORT_CURVE_GEOMETRIES
#define SUPPORT_INTEGER64
#define SUPPORT_SPATIAL_INDEX
#define SUPPORT_IDENTIFIER_DESCRIPTION
#define SUPPORT_FIELD_WIDTH
#endif
QgsNewGeoPackageLayerDialog::QgsNewGeoPackageLayerDialog( QWidget *parent, Qt::WindowFlags fl )
: QDialog( parent, fl )
, mOkButton( nullptr )
, mTableNameEdited( false )
, mLayerIdentifierEdited( false )
{
setupUi( this );
QSettings settings;
restoreGeometry( settings.value( "/Windows/NewGeoPackageLayer/geometry" ).toByteArray() );
mAddAttributeButton->setIcon( QgsApplication::getThemeIcon( "/mActionNewAttribute.png" ) );
mRemoveAttributeButton->setIcon( QgsApplication::getThemeIcon( "/mActionDeleteAttribute.png" ) );
#ifdef SUPPORT_GEOMETRY_LESS
mGeometryTypeBox->addItem( tr( "Non spatial" ), wkbNone );
#endif
mGeometryTypeBox->addItem( tr( "Point" ), wkbPoint );
mGeometryTypeBox->addItem( tr( "Line" ), wkbLineString );
mGeometryTypeBox->addItem( tr( "Polygon" ), wkbPolygon );
mGeometryTypeBox->addItem( tr( "Multi point" ), wkbMultiPoint );
mGeometryTypeBox->addItem( tr( "Multi line" ), wkbMultiLineString );
mGeometryTypeBox->addItem( tr( "Multi polygon" ), wkbMultiPolygon );
#ifdef SUPPORT_CURVE_GEOMETRIES
#if 0
// QGIS always create CompoundCurve and there's no real interest of having just CircularString. CompoundCurve are more useful
mGeometryTypeBox->addItem( tr( "Circular string" ), wkbCircularString );
#endif
mGeometryTypeBox->addItem( tr( "Compound curve" ), wkbCompoundCurve );
mGeometryTypeBox->addItem( tr( "Curve polygon" ), wkbCurvePolygon );
mGeometryTypeBox->addItem( tr( "Multi curve" ), wkbMultiCurve );
mGeometryTypeBox->addItem( tr( "Multi surface" ), wkbMultiSurface );
#endif
#ifdef SUPPORT_GEOMETRY_LESS
mGeometryColumnEdit->setEnabled( false );
mCheckBoxCreateSpatialIndex->setEnabled( false );
mCrsSelector->setEnabled( false );
#endif
mFieldTypeBox->addItem( tr( "Text data" ), "text" );
mFieldTypeBox->addItem( tr( "Whole number (integer)" ), "integer" );
#ifdef SUPPORT_INTEGER64
mFieldTypeBox->addItem( tr( "Whole number (integer 64 bit)" ), "integer64" );
#endif
mFieldTypeBox->addItem( tr( "Decimal number (real)" ), "real" );
mFieldTypeBox->addItem( tr( "Date" ), "date" );
mFieldTypeBox->addItem( tr( "Date&time" ), "datetime" );
mOkButton = buttonBox->button( QDialogButtonBox::Ok );
mOkButton->setEnabled( false );
// Set the SRID box to a default of WGS84
QgsCoordinateReferenceSystem defaultCrs;
defaultCrs.createFromOgcWmsCrs( settings.value( "/Projections/layerDefaultCrs", GEO_EPSG_CRS_AUTHID ).toString() );
defaultCrs.validate();
mCrsSelector->setCrs( defaultCrs );
connect( mFieldNameEdit, SIGNAL( textChanged( const QString& ) ), this, SLOT( fieldNameChanged( QString ) ) );
connect( mAttributeView, SIGNAL( itemSelectionChanged() ), this, SLOT( selectionChanged() ) );
connect( mTableNameEdit, SIGNAL( textChanged( const QString& ) ), this, SLOT( checkOk() ) );
connect( mDatabaseEdit, SIGNAL( textChanged( const QString& ) ), this, SLOT( checkOk() ) );
mAddAttributeButton->setEnabled( false );
mRemoveAttributeButton->setEnabled( false );
#ifndef SUPPORT_SPATIAL_INDEX
mCheckBoxCreateSpatialIndex->hide();
mCheckBoxCreateSpatialIndex->setChecked( false );
#else
mCheckBoxCreateSpatialIndex->setChecked( true );
#endif
#ifndef SUPPORT_IDENTIFIER_DESCRIPTION
mLayerIdentifierLabel->hide();
mLayerIdentifierEdit->hide();
mLayerDescriptionLabel->hide();
mLayerDescriptionEdit->hide();
#endif
#ifndef SUPPORT_FIELD_WIDTH
mFieldLengthLabel->hide();
mFieldLengthEdit->hide();
#endif
}
QgsNewGeoPackageLayerDialog::~QgsNewGeoPackageLayerDialog()
{
QSettings settings;
settings.setValue( "/Windows/NewGeoPackageLayer/geometry", saveGeometry() );
}
void QgsNewGeoPackageLayerDialog::on_mFieldTypeBox_currentIndexChanged( int )
{
QString myType = mFieldTypeBox->itemData( mFieldTypeBox->currentIndex(), Qt::UserRole ).toString();
mFieldLengthEdit->setEnabled( myType == "text" );
if ( myType != "text" )
mFieldLengthEdit->setText( "" );
}
void QgsNewGeoPackageLayerDialog::on_mGeometryTypeBox_currentIndexChanged( int )
{
OGRwkbGeometryType geomType = static_cast<OGRwkbGeometryType>
( mGeometryTypeBox->itemData( mGeometryTypeBox->currentIndex(), Qt::UserRole ).toInt() );
bool isSpatial = geomType != wkbNone;
mGeometryColumnEdit->setEnabled( isSpatial );
mCheckBoxCreateSpatialIndex->setEnabled( isSpatial );
mCrsSelector->setEnabled( isSpatial );
}
void QgsNewGeoPackageLayerDialog::on_mSelectDatabaseButton_clicked()
{
QString fileName = QFileDialog::getSaveFileName( this, tr( "Select existing or create new GeoPackage Database File" ),
QDir::homePath(),
tr( "GeoPackage" ) + " (*.gpkg)",
nullptr,
QFileDialog::DontConfirmOverwrite );
if ( fileName.isEmpty() )
return;
if ( !fileName.endsWith( ".gpkg", Qt::CaseInsensitive ) )
{
fileName += ".gpkg";
}
mDatabaseEdit->setText( fileName );
}
void QgsNewGeoPackageLayerDialog::on_mDatabaseEdit_textChanged( const QString& text )
{
if ( !text.isEmpty() && !mTableNameEdited )
{
QFileInfo fileInfo( text );
mTableNameEdit->setText( fileInfo.baseName() );
}
}
void QgsNewGeoPackageLayerDialog::on_mTableNameEdit_textChanged( const QString& text )
{
mTableNameEdited = !text.isEmpty();
if ( !text.isEmpty() && !mLayerIdentifierEdited )
{
mLayerIdentifierEdit->setText( text );
}
}
void QgsNewGeoPackageLayerDialog::on_mTableNameEdit_textEdited( const QString& text )
{
// Remember if the user explicitly defined a name
mTableNameEdited = !text.isEmpty();
}
void QgsNewGeoPackageLayerDialog::on_mLayerIdentifierEdit_textEdited( const QString& text )
{
// Remember if the user explicitly defined a name
mLayerIdentifierEdited = !text.isEmpty();
}
void QgsNewGeoPackageLayerDialog::checkOk()
{
bool ok = !mDatabaseEdit->text().isEmpty() &&
!mTableNameEdit->text().isEmpty();
mOkButton->setEnabled( ok );
}
void QgsNewGeoPackageLayerDialog::on_mAddAttributeButton_clicked()
{
if ( !mFieldNameEdit->text().isEmpty() )
{
QString myName = mFieldNameEdit->text();
if ( myName == mFeatureIdColumnEdit->text() )
{
QMessageBox::critical( this, tr( "Invalid field name" ), tr( "The field cannot have the same name as the feature identifier" ) );
return;
}
//use userrole to avoid translated type string
QString myType = mFieldTypeBox->itemData( mFieldTypeBox->currentIndex(), Qt::UserRole ).toString();
QString length = mFieldLengthEdit->text();
mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << length ) );
checkOk();
mFieldNameEdit->clear();
}
}
void QgsNewGeoPackageLayerDialog::on_mRemoveAttributeButton_clicked()
{
delete mAttributeView->currentItem();
checkOk();
}
void QgsNewGeoPackageLayerDialog::fieldNameChanged( const QString& name )
{
mAddAttributeButton->setDisabled( name.isEmpty() || ! mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
}
void QgsNewGeoPackageLayerDialog::selectionChanged()
{
mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
}
void QgsNewGeoPackageLayerDialog::on_buttonBox_accepted()
{
if ( apply() )
accept();
}
void QgsNewGeoPackageLayerDialog::on_buttonBox_rejected()
{
reject();
}
bool QgsNewGeoPackageLayerDialog::apply()
{
QString fileName( mDatabaseEdit->text() );
bool createNewDb = false;
if ( QFile( fileName ).exists( fileName ) )
{
QMessageBox msgBox;
msgBox.setIcon( QMessageBox::Question );
msgBox.setWindowTitle( tr( "The file already exists." ) );
msgBox.setText( tr( "Do you want to overwrite the existing file with a new database or add a new layer to it ?" ) );
QPushButton *overwriteButton = msgBox.addButton( tr( "Overwrite" ), QMessageBox::ActionRole );
QPushButton *addNewLayerButton = msgBox.addButton( tr( "Add new layer" ), QMessageBox::ActionRole );
msgBox.setStandardButtons( QMessageBox::Cancel );
msgBox.setDefaultButton( addNewLayerButton );
bool overwrite = false;
bool cancel = false;
if ( property( "hideDialogs" ).toBool() )
{
overwrite = property( "question_existing_db_answer_overwrite" ).toBool();
if ( !overwrite )
cancel = !property( "question_existing_db_answer_add_new_layer" ).toBool();
}
else
{
int ret = msgBox.exec();
if ( ret == QMessageBox::Cancel )
cancel = true;
if ( msgBox.clickedButton() == overwriteButton )
overwrite = true;
}
if ( cancel )
{
return false;
}
if ( overwrite )
{
QFile( fileName ).remove();
createNewDb = true;
}
}
else
{
createNewDb = true;
}
OGRSFDriverH hGpkgDriver = OGRGetDriverByName( "GPKG" );
if ( !hGpkgDriver )
{
if ( !property( "hideDialogs" ).toBool() )
QMessageBox::critical( this, tr( "Layer creation failed" ),
tr( "GeoPackage driver not found" ) );
return false;
}
OGRDataSourceH hDS = nullptr;
if ( createNewDb )
{
hDS = OGR_Dr_CreateDataSource( hGpkgDriver, fileName.toUtf8().constData(), nullptr );
if ( !hDS )
{
QString msg( tr( "Creation of database failed (OGR error:%1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
if ( !property( "hideDialogs" ).toBool() )
QMessageBox::critical( this, tr( "Layer creation failed" ), msg );
return false;
}
}
else
{
OGRSFDriverH hDriver = nullptr;
hDS = OGROpen( fileName.toUtf8().constData(), true, &hDriver );
if ( !hDS )
{
QString msg( tr( "Opening of database failed (OGR error:%1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
if ( !property( "hideDialogs" ).toBool() )
QMessageBox::critical( this, tr( "Layer creation failed" ), msg );
return false;
}
if ( hDriver != hGpkgDriver )
{
QString msg( tr( "Opening of file succeeded, but this is not a GeoPackage database" ) );
if ( !property( "hideDialogs" ).toBool() )
QMessageBox::critical( this, tr( "Layer creation failed" ), msg );
OGR_DS_Destroy( hDS );
return false;
}
}
QString tableName( mTableNameEdit->text() );
bool overwriteTable = false;
if ( OGR_DS_GetLayerByName( hDS, tableName.toUtf8().constData() ) != nullptr )
{
if ( property( "hideDialogs" ).toBool() )
{
overwriteTable = property( "question_existing_layer_answer_overwrite" ).toBool();
}
else if ( QMessageBox::question( this, tr( "Existing layer" ),
tr( "A table with the same name already exists. Do you want to overwrite it ?" ),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes )
{
overwriteTable = true;
}
if ( !overwriteTable )
{
OGR_DS_Destroy( hDS );
return false;
}
}
QString layerIdentifier( mLayerIdentifierEdit->text() );
QString layerDescription( mLayerDescriptionEdit->text() );
OGRwkbGeometryType wkbType = static_cast<OGRwkbGeometryType>
( mGeometryTypeBox->itemData( mGeometryTypeBox->currentIndex(), Qt::UserRole ).toInt() );
OGRSpatialReferenceH hSRS = nullptr;
// consider spatial reference system of the layer
int crsId = mCrsSelector->crs().srsid();
if ( wkbType != wkbNone && crsId > 0 )
{
QgsCoordinateReferenceSystem srs( crsId, QgsCoordinateReferenceSystem::InternalCrsId );
QString srsWkt = srs.toWkt();
hSRS = OSRNewSpatialReference( srsWkt.toLocal8Bit().data() );
}
// Set options
char **options = nullptr;
if ( overwriteTable )
options = CSLSetNameValue( options, "OVERWRITE", "YES" );
if ( !layerIdentifier.isEmpty() )
options = CSLSetNameValue( options, "IDENTIFIER", layerIdentifier.toUtf8().constData() );
if ( !layerDescription.isEmpty() )
options = CSLSetNameValue( options, "DESCRIPTION", layerDescription.toUtf8().constData() );
QString featureId( mFeatureIdColumnEdit->text() );
if ( !featureId.isEmpty() )
options = CSLSetNameValue( options, "FID", featureId.toUtf8().constData() );
QString geometryColumn( mGeometryColumnEdit->text() );
if ( wkbType != wkbNone && !geometryColumn.isEmpty() )
options = CSLSetNameValue( options, "GEOMETRY_COLUMN", geometryColumn.toUtf8().constData() );
#ifdef SUPPORT_SPATIAL_INDEX
if ( wkbType != wkbNone )
options = CSLSetNameValue( options, "SPATIAL_INDEX", mCheckBoxCreateSpatialIndex->isChecked() ? "YES" : "NO" );
#endif
OGRLayerH hLayer = OGR_DS_CreateLayer( hDS, tableName.toUtf8().constData(), hSRS, wkbType, options );
CSLDestroy( options );
if ( hSRS != nullptr )
OSRRelease( hSRS );
if ( hLayer == nullptr )
{
QString msg( tr( "Creation of layer failed (OGR error:%1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
if ( !property( "hideDialogs" ).toBool() )
QMessageBox::critical( this, tr( "Layer creation failed" ), msg );
OGR_DS_Destroy( hDS );
return false;
}
QTreeWidgetItemIterator it( mAttributeView );
while ( *it )
{
QString fieldName(( *it )->text( 0 ) );
QString fieldType(( *it )->text( 1 ) );
QString fieldWidth(( *it )->text( 2 ) );
OGRFieldType ogrType( OFTString );
if ( fieldType == "text" )
ogrType = OFTString;
else if ( fieldType == "integer" )
ogrType = OFTInteger;
#ifdef SUPPORT_INTEGER64
else if ( fieldType == "integer64" )
ogrType = OFTInteger64;
#endif
else if ( fieldType == "real" )
ogrType = OFTReal;
else if ( fieldType == "date" )
ogrType = OFTDate;
else if ( fieldType == "datetime" )
ogrType = OFTDateTime;
int ogrWidth = fieldWidth.toInt();
OGRFieldDefnH fld = OGR_Fld_Create( fieldName.toUtf8().constData(), ogrType );
OGR_Fld_SetWidth( fld, ogrWidth );
if ( OGR_L_CreateField( hLayer, fld, true ) != OGRERR_NONE )
{
if ( !property( "hideDialogs" ).toBool() )
{
QMessageBox::critical( this, tr( "Layer creation failed" ),
tr( "Creation of field %1 failed (OGR error: %2)" )
.arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
}
OGR_Fld_Destroy( fld );
OGR_DS_Destroy( hDS );
return false;
}
OGR_Fld_Destroy( fld );
++it;
}
// In GDAL >= 2.0, the driver implements a defered creation strategy, so
// issue a command that will force table creation
CPLErrorReset();
OGR_L_ResetReading( hLayer );
if ( CPLGetLastErrorType() != CE_None )
{
QString msg( tr( "Creation of layer failed (OGR error:%1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
if ( !property( "hideDialogs" ).toBool() )
QMessageBox::critical( this, tr( "Layer creation failed" ), msg );
OGR_DS_Destroy( hDS );
return false;
}
OGR_DS_Destroy( hDS );
QString uri( QString( "%1|layername=%2" ).arg( mDatabaseEdit->text() ).arg( tableName ) );
QString userVisiblelayerName( layerIdentifier.isEmpty() ? tableName : layerIdentifier );
QgsVectorLayer *layer = new QgsVectorLayer( uri, userVisiblelayerName, "ogr" );
if ( layer->isValid() )
{
// register this layer with the central layers registry
QList<QgsMapLayer *> myList;
myList << layer;
//addMapLayers returns a list of all successfully added layers
//so we compare that to our original list.
if ( myList == QgsMapLayerRegistry::instance()->addMapLayers( myList ) )
return true;
}
else
{
if ( !property( "hideDialogs" ).toBool() )
QMessageBox::critical( this, tr( "Invalid Layer" ), tr( "%1 is an invalid layer and cannot be loaded." ).arg( tableName ) );
delete layer;
}
return false;
}

View File

@ -0,0 +1,63 @@
/***************************************************************************
qgsnewgeopackagelayerdialog.h
-------------------
begin : April 2016
copyright : (C) 2016 by Even Rouault
email : even.rouault at spatialys.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 QGSNEWGEOPACKAGELAYERDIALOG_H
#define QGSNEWGEOPACKAGELAYERDIALOG_H
#include "ui_qgsnewgeopackagelayerdialogbase.h"
#include "qgisgui.h"
#include "qgscontexthelp.h"
#include "qgis.h"
/** Dialog to set up parameters to create a new GeoPackage layer, and on accept() to create it and add it to the layers */
class GUI_EXPORT QgsNewGeoPackageLayerDialog: public QDialog, private Ui::QgsNewGeoPackageLayerDialogBase
{
Q_OBJECT
public:
/** Constructor */
QgsNewGeoPackageLayerDialog( QWidget *parent = nullptr, Qt::WindowFlags fl = QgisGui::ModalDialogFlags );
~QgsNewGeoPackageLayerDialog();
private slots:
void on_mAddAttributeButton_clicked();
void on_mRemoveAttributeButton_clicked();
void on_mFieldTypeBox_currentIndexChanged( int index );
void on_mGeometryTypeBox_currentIndexChanged( int index );
void on_mSelectDatabaseButton_clicked();
void on_mDatabaseEdit_textChanged( const QString& text );
void on_mTableNameEdit_textChanged( const QString& text );
void on_mTableNameEdit_textEdited( const QString& text );
void on_mLayerIdentifierEdit_textEdited( const QString& text );
void fieldNameChanged( const QString& );
void selectionChanged();
void checkOk();
void on_buttonBox_helpRequested() { QgsContextHelp::run( metaObject()->className() ); }
void on_buttonBox_accepted();
void on_buttonBox_rejected();
private:
bool apply();
QPushButton *mOkButton;
QString mCrsId;
bool mTableNameEdited;
bool mLayerIdentifierEdited;
};
#endif // QGSNEWVECTORLAYERDIALOG_H

View File

@ -35,8 +35,6 @@ QgsSqlExpressionCompiler::Result QgsOgrExpressionCompiler::compile( const QgsExp
return Fail;
else if ( mSource->mDriverName == "OCI" )
return Fail;
else if ( mSource->mDriverName == "SQLite" )
return Fail;
else if ( mSource->mDriverName == "ODBC" )
return Fail;
else if ( mSource->mDriverName == "PGeo" )

View File

@ -17,6 +17,7 @@
#include "qgsogrprovider.h"
#include "qgsogrgeometrysimplifier.h"
#include "qgsogrexpressioncompiler.h"
#include "qgssqliteexpressioncompiler.h"
#include "qgsogrutils.h"
#include "qgsapplication.h"
@ -82,7 +83,7 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
// filter if we choose to ignore them (fixes #11223)
if (( mSource->mDriverName != "VRT" && mSource->mDriverName != "OGR_VRT" ) || mRequest.filterRect().isNull() )
{
QgsOgrProviderUtils::setRelevantFields( ogrLayer, mSource->mFields.count(), mFetchGeometry, attrs );
QgsOgrProviderUtils::setRelevantFields( ogrLayer, mSource->mFields.count(), mFetchGeometry, attrs, mSource->mFirstFieldIsFid );
}
// spatial query to select features
@ -100,13 +101,20 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
if ( request.filterType() == QgsFeatureRequest::FilterExpression
&& QSettings().value( "/qgis/compileExpressions", true ).toBool() )
{
QgsOgrExpressionCompiler compiler = QgsOgrExpressionCompiler( source );
QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() );
QgsSqlExpressionCompiler* compiler;
if ( source->mDriverName == "SQLite" || source->mDriverName == "GPKG" )
{
compiler = new QgsSQLiteExpressionCompiler( source->mFields );
}
else
{
compiler = new QgsOgrExpressionCompiler( source );
}
QgsSqlExpressionCompiler::Result result = compiler->compile( request.filterExpression() );
if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial )
{
QString whereClause = compiler.result();
QString whereClause = compiler->result();
if ( OGR_L_SetAttributeFilter( ogrLayer, mSource->mEncoding->fromUnicode( whereClause ).constData() ) == OGRERR_NONE )
{
//if only partial success when compiling expression, we need to double-check results using QGIS' expressions
@ -118,6 +126,8 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
{
OGR_L_SetAttributeFilter( ogrLayer, nullptr );
}
delete compiler;
}
else
{
@ -279,8 +289,15 @@ bool QgsOgrFeatureIterator::close()
void QgsOgrFeatureIterator::getFeatureAttribute( OGRFeatureH ogrFet, QgsFeature & f, int attindex )
{
if ( mSource->mFirstFieldIsFid && attindex == 0 )
{
f.setAttribute( 0, static_cast<qint64>( OGR_F_GetFID( ogrFet ) ) );
return;
}
int attindexWithoutFid = ( mSource->mFirstFieldIsFid ) ? attindex - 1 : attindex;
bool ok = false;
QVariant value = QgsOgrUtils::getOgrFeatureAttribute( ogrFet, mSource->mFields, attindex, mSource->mEncoding, &ok );
QVariant value = QgsOgrUtils::getOgrFeatureAttribute( ogrFet, mSource->mFieldsWithoutFid, attindexWithoutFid, mSource->mEncoding, &ok );
if ( !ok )
return;
@ -354,7 +371,10 @@ QgsOgrFeatureSource::QgsOgrFeatureSource( const QgsOgrProvider* p )
mSubsetString = p->mSubsetString;
mEncoding = p->mEncoding; // no copying - this is a borrowed pointer from Qt
mFields = p->mAttributeFields;
for ( int i = ( p->mFirstFieldIsFid ) ? 1 : 0; i < mFields.size(); i++ )
mFieldsWithoutFid.append( mFields.at( i ) );
mDriverName = p->ogrDriverName;
mFirstFieldIsFid = p->mFirstFieldIsFid;
mOgrGeometryTypeFilter = wkbFlatten( p->mOgrGeometryTypeFilter );
QgsOgrConnPool::instance()->ref( mDataSource );
}

View File

@ -40,6 +40,8 @@ class QgsOgrFeatureSource : public QgsAbstractFeatureSource
QString mSubsetString;
QTextCodec* mEncoding;
QgsFields mFields;
bool mFirstFieldIsFid;
QgsFields mFieldsWithoutFid;
OGRwkbGeometryType mOgrGeometryTypeFilter;
QString mDriverName;

View File

@ -271,6 +271,7 @@ QgsVectorLayerImport::ImportError QgsOgrProvider::createEmptyLayer(
QgsOgrProvider::QgsOgrProvider( QString const & uri )
: QgsVectorDataProvider( uri )
, mFirstFieldIsFid( false )
, ogrDataSource( nullptr )
, mExtent( nullptr )
, ogrLayer( nullptr )
@ -280,7 +281,7 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri )
, mOgrGeometryTypeFilter( wkbUnknown )
, ogrDriver( nullptr )
, mValid( false )
, geomType( wkbUnknown )
, mOGRGeomType( wkbUnknown )
, mFeaturesCounted( -1 )
, mWriteAccess( false )
, mShapefileMayBeCorrupted( false )
@ -497,6 +498,38 @@ QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const
case wkbGeometryCollection:
geom = "GeometryCollection";
break;
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,0)
case wkbCircularString:
geom = "CircularString";
break;
case wkbCompoundCurve:
geom = "CompoundCurve";
break;
case wkbCurvePolygon:
geom = "CurvePolygon";
break;
case wkbMultiCurve:
geom = "MultiCurve";
break;
case wkbMultiSurface:
geom = "MultiSurface";
break;
case wkbCircularStringZ:
geom = "CircularStringZ";
break;
case wkbCompoundCurveZ:
geom = "CompoundCurveZ";
break;
case wkbCurvePolygonZ:
geom = "CurvePolygonZ";
break;
case wkbMultiCurveZ:
geom = "MultiCurveZ";
break;
case wkbMultiSurfaceZ:
geom = "MultiSurfaceZ";
break;
#endif
case wkbNone:
geom = "None";
break;
@ -525,7 +558,8 @@ QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const
geom = "GeometryCollection25D";
break;
default:
geom = QString( "Unknown WKB: %1" ).arg( type );
// Do not use ':', as it will mess with the separator used by QgsSublayersDialog::populateLayers()
geom = QString( "Unknown WKB (%1)" ).arg( type );
}
return geom;
}
@ -691,15 +725,30 @@ void QgsOgrProvider::loadFields()
if ( mOgrGeometryTypeFilter != wkbUnknown )
{
geomType = mOgrGeometryTypeFilter;
mOGRGeomType = mOgrGeometryTypeFilter;
}
else
{
geomType = getOgrGeomType( ogrLayer );
mOGRGeomType = getOgrGeomType( ogrLayer );
}
OGRFeatureDefnH fdef = OGR_L_GetLayerDefn( ogrLayer );
if ( fdef )
{
// Expose the OGR FID if it comes from a "real" column (typically GPKG)
// and make sure that this FID column is not exposed as a regular OGR field (shouldn't happen normally)
mFirstFieldIsFid = !( EQUAL( OGR_L_GetFIDColumn( ogrLayer ), "" ) ) &&
OGR_FD_GetFieldIndex( fdef, OGR_L_GetFIDColumn( ogrLayer ) ) < 0;
if ( mFirstFieldIsFid )
{
mAttributeFields.append(
QgsField(
OGR_L_GetFIDColumn( ogrLayer ),
QVariant::LongLong,
"Integer64"
)
);
}
for ( int i = 0; i < OGR_FD_GetFieldCount( fdef ); ++i )
{
OGRFieldDefnH fldDef = OGR_FD_GetFieldDefn( fdef, i );
@ -784,23 +833,23 @@ QString QgsOgrProvider::storageType() const
void QgsOgrProvider::setRelevantFields( OGRLayerH ogrLayer, bool fetchGeometry, const QgsAttributeList &fetchAttributes )
{
QgsOgrProviderUtils::setRelevantFields( ogrLayer, mAttributeFields.count(), fetchGeometry, fetchAttributes );
QgsOgrProviderUtils::setRelevantFields( ogrLayer, mAttributeFields.count(), fetchGeometry, fetchAttributes, mFirstFieldIsFid );
}
void QgsOgrProviderUtils::setRelevantFields( OGRLayerH ogrLayer, int fieldCount, bool fetchGeometry, const QgsAttributeList &fetchAttributes )
void QgsOgrProviderUtils::setRelevantFields( OGRLayerH ogrLayer, int fieldCount, bool fetchGeometry, const QgsAttributeList &fetchAttributes, bool firstAttrIsFid )
{
#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1800
if ( OGR_L_TestCapability( ogrLayer, OLCIgnoreFields ) )
{
QVector<const char*> ignoredFields;
OGRFeatureDefnH featDefn = OGR_L_GetLayerDefn( ogrLayer );
for ( int i = 0; i < fieldCount; i++ )
for ( int i = ( firstAttrIsFid ? 1 : 0 ); i < fieldCount; i++ )
{
if ( !fetchAttributes.contains( i ) )
{
// add to ignored fields
ignoredFields.append( OGR_Fld_GetNameRef( OGR_FD_GetFieldDefn( featDefn, i ) ) );
ignoredFields.append( OGR_Fld_GetNameRef( OGR_FD_GetFieldDefn( featDefn, firstAttrIsFid ? i - 1 : i ) ) );
}
}
@ -907,7 +956,7 @@ size_t QgsOgrProvider::layerCount() const
*/
QGis::WkbType QgsOgrProvider::geometryType() const
{
return static_cast<QGis::WkbType>( geomType );
return static_cast<QGis::WkbType>( mOGRGeomType );
}
/**
@ -933,6 +982,36 @@ bool QgsOgrProvider::isValid()
return mValid;
}
// Drivers may be more tolerant than we really wish (e.g. GeoPackage driver
// may accept any geometry type)
OGRGeometryH QgsOgrProvider::ConvertGeometryIfNecessary( OGRGeometryH hGeom )
{
if ( hGeom == nullptr )
return hGeom;
OGRwkbGeometryType layerGeomType = OGR_L_GetGeomType( ogrLayer );
OGRwkbGeometryType flattenLayerGeomType = wkbFlatten( layerGeomType );
OGRwkbGeometryType geomType = OGR_G_GetGeometryType( hGeom );
OGRwkbGeometryType flattenGeomType = wkbFlatten( geomType );
if ( flattenLayerGeomType == wkbUnknown || flattenLayerGeomType == flattenGeomType )
{
return hGeom;
}
if ( flattenLayerGeomType == wkbMultiPolygon && flattenGeomType == wkbPolygon )
{
return OGR_G_ForceToMultiPolygon( hGeom );
}
if ( flattenLayerGeomType == wkbMultiLineString && flattenGeomType == wkbLineString )
{
return OGR_G_ForceToMultiLineString( hGeom );
}
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,0)
return OGR_G_ForceTo( hGeom, layerGeomType, nullptr );
#else
return hGeom;
#endif
}
bool QgsOgrProvider::addFeature( QgsFeature& f )
{
bool returnValue = true;
@ -951,6 +1030,9 @@ bool QgsOgrProvider::addFeature( QgsFeature& f )
pushError( tr( "OGR error creating wkb for feature %1: %2" ).arg( f.id() ).arg( CPLGetLastErrorMsg() ) );
return false;
}
geom = ConvertGeometryIfNecessary( geom );
OGR_F_SetGeometryDirectly( feature, geom );
}
}
@ -959,45 +1041,65 @@ bool QgsOgrProvider::addFeature( QgsFeature& f )
QgsLocaleNumC l;
int qgisAttId = ( mFirstFieldIsFid ) ? 1 : 0;
// If the first attribute is the FID and the user has set it, then use it
if ( mFirstFieldIsFid && attrs.count() > 0 )
{
QVariant attrFid = attrs.at( 0 );
if ( !attrFid.isNull() )
{
bool ok = false;
qlonglong id = attrFid.toLongLong( &ok );
if ( ok )
{
#if GDAL_VERSION_MAJOR >= 2
OGR_F_SetFID( feature, static_cast<GIntBig>( id ) );
#else
OGR_F_SetFID( feature, static_cast<long>( id ) );
#endif
}
}
}
//add possible attribute information
for ( int targetAttributeId = 0; targetAttributeId < attrs.count(); ++targetAttributeId )
for ( int ogrAttId = 0; qgisAttId < attrs.count(); ++qgisAttId, ++ogrAttId )
{
// don't try to set field from attribute map if it's not present in layer
if ( targetAttributeId < 0 || targetAttributeId >= OGR_FD_GetFieldCount( fdef ) )
if ( ogrAttId >= OGR_FD_GetFieldCount( fdef ) )
continue;
//if(!s.isEmpty())
// continue;
//
OGRFieldDefnH fldDef = OGR_FD_GetFieldDefn( fdef, targetAttributeId );
OGRFieldDefnH fldDef = OGR_FD_GetFieldDefn( fdef, ogrAttId );
OGRFieldType type = OGR_Fld_GetType( fldDef );
QVariant attrVal = attrs.at( targetAttributeId );
QVariant attrVal = attrs.at( qgisAttId );
if ( attrVal.isNull() || ( type != OFTString && attrVal.toString().isEmpty() ) )
{
OGR_F_UnsetField( feature, targetAttributeId );
OGR_F_UnsetField( feature, ogrAttId );
}
else
{
switch ( type )
{
case OFTInteger:
OGR_F_SetFieldInteger( feature, targetAttributeId, attrVal.toInt() );
OGR_F_SetFieldInteger( feature, ogrAttId, attrVal.toInt() );
break;
#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 2000000
case OFTInteger64:
OGR_F_SetFieldInteger64( feature, targetAttributeId, attrVal.toLongLong() );
OGR_F_SetFieldInteger64( feature, ogrAttId, attrVal.toLongLong() );
break;
#endif
case OFTReal:
OGR_F_SetFieldDouble( feature, targetAttributeId, attrVal.toDouble() );
OGR_F_SetFieldDouble( feature, ogrAttId, attrVal.toDouble() );
break;
case OFTDate:
OGR_F_SetFieldDateTime( feature, targetAttributeId,
OGR_F_SetFieldDateTime( feature, ogrAttId,
attrVal.toDate().year(),
attrVal.toDate().month(),
attrVal.toDate().day(),
@ -1006,7 +1108,7 @@ bool QgsOgrProvider::addFeature( QgsFeature& f )
break;
case OFTTime:
OGR_F_SetFieldDateTime( feature, targetAttributeId,
OGR_F_SetFieldDateTime( feature, ogrAttId,
0, 0, 0,
attrVal.toTime().hour(),
attrVal.toTime().minute(),
@ -1015,7 +1117,7 @@ bool QgsOgrProvider::addFeature( QgsFeature& f )
break;
case OFTDateTime:
OGR_F_SetFieldDateTime( feature, targetAttributeId,
OGR_F_SetFieldDateTime( feature, ogrAttId,
attrVal.toDateTime().date().year(),
attrVal.toDateTime().date().month(),
attrVal.toDateTime().date().day(),
@ -1027,14 +1129,14 @@ bool QgsOgrProvider::addFeature( QgsFeature& f )
case OFTString:
QgsDebugMsg( QString( "Writing string attribute %1 with %2, encoding %3" )
.arg( targetAttributeId )
.arg( qgisAttId )
.arg( attrVal.toString(),
mEncoding->name().data() ) );
OGR_F_SetFieldString( feature, targetAttributeId, mEncoding->fromUnicode( attrVal.toString() ).constData() );
OGR_F_SetFieldString( feature, ogrAttId, mEncoding->fromUnicode( attrVal.toString() ).constData() );
break;
default:
QgsMessageLog::logMessage( tr( "type %1 for attribute %2 not found" ).arg( type ).arg( targetAttributeId ), tr( "OGR" ) );
QgsMessageLog::logMessage( tr( "type %1 for attribute %2 not found" ).arg( type ).arg( qgisAttId ), tr( "OGR" ) );
break;
}
}
@ -1047,9 +1149,16 @@ bool QgsOgrProvider::addFeature( QgsFeature& f )
}
else
{
long id = OGR_F_GetFID( feature );
QgsFeatureId id = static_cast<QgsFeatureId>( OGR_F_GetFID( feature ) );
if ( id >= 0 )
{
f.setFeatureId( id );
if ( mFirstFieldIsFid && attrs.count() > 0 )
{
f.setAttribute( 0, id );
}
}
}
OGR_F_Destroy( feature );
@ -1149,6 +1258,12 @@ bool QgsOgrProvider::deleteAttributes( const QgsAttributeIds &attributes )
qSort( attrsLst.begin(), attrsLst.end(), qGreater<int>() );
Q_FOREACH ( int attr, attrsLst )
{
if ( attr == 0 && mFirstFieldIsFid )
{
pushError( tr( "Cannot delete feature id column" ) );
res = false;
break;
}
if ( OGR_L_DeleteField( ogrLayer, attr ) != OGRERR_NONE )
{
pushError( tr( "OGR error deleting field %1: %2" ).arg( attr ).arg( CPLGetLastErrorMsg() ) );
@ -1200,6 +1315,21 @@ bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_
for ( QgsAttributeMap::const_iterator it2 = attr.begin(); it2 != attr.end(); ++it2 )
{
int f = it2.key();
if ( mFirstFieldIsFid )
{
if ( f == 0 )
{
if ( it2->toLongLong() != fid )
{
pushError( tr( "Changing feature id of feature %1 is not allowed." ).arg( fid ) );
continue;
}
}
else
{
--f;
}
}
OGRFieldDefnH fd = OGR_F_GetFieldDefnRef( of, f );
if ( !fd )
@ -1270,6 +1400,8 @@ bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_
{
pushError( tr( "OGR error setting feature %1: %2" ).arg( fid ).arg( CPLGetLastErrorMsg() ) );
}
OGR_F_Destroy( of );
}
if ( OGR_L_SyncToDisk( ogrLayer ) != OGRERR_NONE )
@ -1320,6 +1452,8 @@ bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
continue;
}
theNewGeometry = ConvertGeometryIfNecessary( theNewGeometry );
//set the new geometry
if ( OGR_F_SetGeometryDirectly( theOGRFeature, theNewGeometry ) != OGRERR_NONE )
{
@ -2677,7 +2811,7 @@ void QgsOgrProvider::recalculateFeatureCount()
bool QgsOgrProvider::doesStrictFeatureTypeCheck() const
{
// FIXME probably other drivers too...
return ogrDriverName != "ESRI Shapefile" || ( geomType == wkbPoint || geomType == wkbPoint25D );
return ogrDriverName != "ESRI Shapefile" || ( mOGRGeomType == wkbPoint || mOGRGeomType == wkbPoint25D );
}
OGRwkbGeometryType QgsOgrProvider::ogrWkbSingleFlatten( OGRwkbGeometryType type )

View File

@ -295,6 +295,7 @@ class QgsOgrProvider : public QgsVectorDataProvider
QString ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const;
OGRwkbGeometryType ogrWkbGeometryTypeFromName( const QString& typeName ) const;
QgsFields mAttributeFields;
bool mFirstFieldIsFid;
OGRDataSourceH ogrDataSource;
OGREnvelope* mExtent;
@ -335,7 +336,7 @@ class QgsOgrProvider : public QgsVectorDataProvider
bool mValid;
OGRwkbGeometryType geomType;
OGRwkbGeometryType mOGRGeomType;
long mFeaturesCounted;
mutable QStringList mSubLayerList;
@ -356,13 +357,16 @@ class QgsOgrProvider : public QgsVectorDataProvider
bool mWriteAccess;
bool mShapefileMayBeCorrupted;
/** Converts the geometry to the layer type if necessary. Takes ownership of the passed geometry */
OGRGeometryH ConvertGeometryIfNecessary( OGRGeometryH );
};
class QgsOgrProviderUtils
{
public:
static void setRelevantFields( OGRLayerH ogrLayer, int fieldCount, bool fetchGeometry, const QgsAttributeList &fetchAttributes );
static void setRelevantFields( OGRLayerH ogrLayer, int fieldCount, bool fetchGeometry, const QgsAttributeList &fetchAttributes, bool firstAttrIsFid );
static OGRLayerH setSubsetString( OGRLayerH layer, OGRDataSourceH ds, QTextCodec* encoding, const QString& subsetString );
static QByteArray quotedIdentifier( QByteArray field, const QString& ogrDriverName );

View File

@ -10,7 +10,6 @@ SET(SPATIALITE_SRCS
qgsspatialitedataitems.cpp
qgsspatialiteconnection.cpp
qgsspatialiteconnpool.cpp
qgsspatialiteexpressioncompiler.cpp
qgsspatialitefeatureiterator.cpp
qgsspatialitesourceselect.cpp
qgsspatialitetablemodel.cpp

View File

@ -17,7 +17,7 @@
#include "qgsspatialiteconnection.h"
#include "qgsspatialiteconnpool.h"
#include "qgsspatialiteprovider.h"
#include "qgsspatialiteexpressioncompiler.h"
#include "qgssqliteexpressioncompiler.h"
#include "qgsgeometry.h"
#include "qgslogger.h"
@ -98,7 +98,7 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() )
{
QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source );
QgsSQLiteExpressionCompiler compiler = QgsSQLiteExpressionCompiler( source->mFields );
QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() );
@ -139,7 +139,7 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
{
Q_FOREACH ( const QgsFeatureRequest::OrderByClause& clause, request.orderBy() )
{
QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source );
QgsSQLiteExpressionCompiler compiler = QgsSQLiteExpressionCompiler( source->mFields );
QgsExpression expression = clause.expression();
if ( compiler.compile( &expression ) == QgsSqlExpressionCompiler::Complete )
{

View File

@ -137,6 +137,7 @@
</property>
<addaction name="mActionNewVectorLayer"/>
<addaction name="mActionNewSpatiaLiteLayer"/>
<addaction name="mActionNewGeoPackageLayer"/>
<addaction name="mActionNewMemoryLayer"/>
</widget>
<widget class="QMenu" name="mAddLayerMenu">
@ -2469,6 +2470,15 @@ Acts on currently active editable layer</string>
<string>Layer Diagram Options</string>
</property>
</action>
<action name="mActionNewGeoPackageLayer">
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionNewGeoPackageLayer.svg</normaloff>:/images/themes/default/mActionNewGeoPackageLayer.svg</iconset>
</property>
<property name="text">
<string>New GeoPackage Layer...</string>
</property>
</action>
</widget>
<resources>
<include location="../../images/images.qrc"/>

View File

@ -0,0 +1,485 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsNewGeoPackageLayerDialogBase</class>
<widget class="QDialog" name="QgsNewGeoPackageLayerDialogBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>548</width>
<height>686</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>351</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>New GeoPackage Layer</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="9" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>530</width>
<height>635</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="4" column="0">
<widget class="QCheckBox" name="mCheckBoxCreateSpatialIndex">
<property name="toolTip">
<string>Add an integer id field as the primary key for the new layer</string>
</property>
<property name="text">
<string>Create a spatial index</string>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_6"/>
</item>
<item row="5" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>New field</string>
</property>
<layout class="QGridLayout" name="_2">
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="mFieldNameEdit"/>
</item>
<item row="1" column="1" colspan="3">
<widget class="QComboBox" name="mFieldTypeBox"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mFieldLengthLabel">
<property name="text">
<string>Maximum length</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="mFieldNameLabel">
<property name="text">
<string>Name</string>
</property>
<property name="buddy">
<cstring>mFieldNameEdit</cstring>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QToolButton" name="mAddAttributeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Add field to list</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Add to fields list</string>
</property>
<property name="icon">
<iconset>
<normaloff>../../images/themes/default/mActionNewAttribute.png</normaloff>../../images/themes/default/mActionNewAttribute.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mFieldTypeLabel">
<property name="text">
<string>Type</string>
</property>
<property name="buddy">
<cstring>mFieldTypeBox</cstring>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QLineEdit" name="mFieldLengthEdit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Field length / width&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="4" column="2">
<widget class="QLineEdit" name="mLayerIdentifierEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Human-readable identifier (e.g. short name) for the layer content&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QLineEdit" name="mGeometryColumnEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Name of the geometry column&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>geometry</string>
</property>
</widget>
</item>
<item row="1" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="mDatabaseEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Existing or new GeoPackage database file name&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mSelectDatabaseButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select an existing or create a new database&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QLabel" name="mLayerDescriptionLabel">
<property name="text">
<string>Layer description</string>
</property>
<property name="buddy">
<cstring>mLayerIdentifierEdit</cstring>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="mGeometryTypeLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Geometry type</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="mGeometryColumnLabel">
<property name="text">
<string>Geometry column</string>
</property>
<property name="buddy">
<cstring>mGeometryColumnEdit</cstring>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="mTableNameEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Table name in the database&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLineEdit" name="mLayerDescriptionEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Human-readable description for the layer content&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mDatabaseLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Database</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="mTableNameLabel">
<property name="text">
<string>Table name</string>
</property>
<property name="buddy">
<cstring>mLayerIdentifierEdit</cstring>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="mLayerIdentifierLabel">
<property name="text">
<string>Layer identifier</string>
</property>
<property name="buddy">
<cstring>mLayerIdentifierEdit</cstring>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QComboBox" name="mGeometryTypeBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Geometry type&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="mFeatureIdLabel">
<property name="text">
<string>Feature id column</string>
</property>
<property name="buddy">
<cstring>mGeometryColumnEdit</cstring>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QLineEdit" name="mFeatureIdColumnEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Name of the feature id column&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>fid</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Fields list</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="0">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1" colspan="2">
<widget class="QToolButton" name="mRemoveAttributeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Delete selected field</string>
</property>
<property name="text">
<string>Remove field</string>
</property>
<property name="icon">
<iconset>
<normaloff>../../images/themes/default/mActionDeleteAttribute.png</normaloff>../../images/themes/default/mActionDeleteAttribute.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QTreeWidget" name="mAttributeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="columnCount">
<number>3</number>
</property>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Length</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QgsProjectionSelectionWidget" name="mCrsSelector" native="true">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>QgsProjectionSelectionWidget</class>
<extends>QWidget</extends>
<header location="global">qgsprojectionselectionwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mDatabaseEdit</tabstop>
<tabstop>mSelectDatabaseButton</tabstop>
<tabstop>mTableNameEdit</tabstop>
<tabstop>mLayerIdentifierEdit</tabstop>
<tabstop>mLayerDescriptionEdit</tabstop>
<tabstop>mFeatureIdColumnEdit</tabstop>
<tabstop>mGeometryTypeBox</tabstop>
<tabstop>mGeometryColumnEdit</tabstop>
<tabstop>mCrsSelector</tabstop>
<tabstop>mCheckBoxCreateSpatialIndex</tabstop>
<tabstop>mFieldNameEdit</tabstop>
<tabstop>mFieldTypeBox</tabstop>
<tabstop>mFieldLengthEdit</tabstop>
<tabstop>mAddAttributeButton</tabstop>
<tabstop>mAttributeView</tabstop>
<tabstop>mRemoveAttributeButton</tabstop>
<tabstop>buttonBox</tabstop>
<tabstop>scrollArea</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QgsNewGeoPackageLayerDialogBase</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>290</x>
<y>585</y>
</hint>
<hint type="destinationlabel">
<x>242</x>
<y>308</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -46,6 +46,8 @@ ADD_PYTHON_TEST(PyQgsMemoryProvider test_provider_memory.py)
ADD_PYTHON_TEST(PyQgsMultiEditToolButton test_qgsmultiedittoolbutton.py)
ADD_PYTHON_TEST(PyQgsNetworkContentFetcher test_qgsnetworkcontentfetcher.py)
ADD_PYTHON_TEST(PyQgsNullSymbolRenderer test_qgsnullsymbolrenderer.py)
ADD_PYTHON_TEST(PyQgsNewGeoPackageLayerDialog test_qgsnewgeopackagelayerdialog.py)
ADD_PYTHON_TEST(PyQgsOGRProviderGpkg test_provider_ogr_gpkg.py)
ADD_PYTHON_TEST(PyQgsPalLabelingBase test_qgspallabeling_base.py)
ADD_PYTHON_TEST(PyQgsPalLabelingCanvas test_qgspallabeling_canvas.py)
ADD_PYTHON_TEST(PyQgsPalLabelingComposer test_qgspallabeling_composer.py)

View File

@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for the OGR/GPKG provider.
.. note:: 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.
"""
__author__ = 'Even Rouault'
__date__ = '2016-04-21'
__copyright__ = 'Copyright 2016, Even Rouault'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import qgis # NOQA
import os
import tempfile
import shutil
import glob
from osgeo import gdal, ogr
from qgis.core import QgsVectorLayer, QgsFeature, QgsGeometry, QgsFeatureRequest
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
start_app()
def GDAL_COMPUTE_VERSION(maj, min, rev):
return ((maj) * 1000000 + (min) * 10000 + (rev) * 100)
class TestPyQgsOGRProviderGpkg(unittest.TestCase):
@classmethod
def setUpClass(cls):
"""Run before all tests"""
# Create test layer
cls.basetestpath = tempfile.mkdtemp()
@classmethod
def tearDownClass(cls):
"""Run after all tests"""
shutil.rmtree(cls.basetestpath, True)
def testSingleToMultiPolygonPromotion(self):
tmpfile = os.path.join(self.basetestpath, 'testSingleToMultiPolygonPromotion.gpkg')
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
lyr = ds.CreateLayer('test', geom_type=ogr.wkbMultiPolygon)
ds = None
vl = QgsVectorLayer(u'{}|layerid=0'.format(tmpfile), u'test', u'ogr')
f = QgsFeature()
f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0,0 1,1 1,0 0))'))
vl.dataProvider().addFeatures([f])
got = [f for f in vl.getFeatures()][0]
got_geom = got.geometry()
reference = QgsGeometry.fromWkt('MultiPolygon (((0 0, 0 1, 1 1, 0 0)))')
# The geometries must be binarily identical
self.assertEqual(got_geom.asWkb(), reference.asWkb(), 'Expected {}, got {}'.format(reference.exportToWkt(), got_geom.exportToWkt()))
@unittest.expectedFailure(int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 0))
def testCurveGeometryType(self):
tmpfile = os.path.join(self.basetestpath, 'testCurveGeometryType.gpkg')
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
lyr = ds.CreateLayer('test', geom_type=ogr.wkbCurvePolygon)
ds = None
vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr')
self.assertEqual(vl.dataProvider().subLayers(), [u'0:test:0:CurvePolygon'])
f = QgsFeature()
f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0,0 1,1 1,0 0))'))
vl.dataProvider().addFeatures([f])
got = [f for f in vl.getFeatures()][0]
got_geom = got.geometry()
reference = QgsGeometry.fromWkt('CurvePolygon (((0 0, 0 1, 1 1, 0 0)))')
# The geometries must be binarily identical
self.assertEqual(got_geom.asWkb(), reference.asWkb(), 'Expected {}, got {}'.format(reference.exportToWkt(), got_geom.exportToWkt()))
def testFidSupport(self):
# We do not use @unittest.expectedFailure since the test might actually succeed
# on Linux 64bit with GDAL 1.11, where "long" is 64 bit...
# GDAL 2.0 is guaranteed to properly support it on all platforms
version_num = int(gdal.VersionInfo('VERSION_NUM'))
if version_num < GDAL_COMPUTE_VERSION(2, 0, 0):
return
tmpfile = os.path.join(self.basetestpath, 'testFidSupport.gpkg')
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint)
lyr.CreateField(ogr.FieldDefn('strfield', ogr.OFTString))
f = ogr.Feature(lyr.GetLayerDefn())
f.SetFID(12)
f.SetField(0, 'foo')
lyr.CreateFeature(f)
f = None
ds = None
vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr')
self.assertEqual(len(vl.fields()), 2)
got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures()]
self.assertEqual(got, [(12, 'foo')])
got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("strfield = 'foo'"))]
self.assertEqual(got, [(12, 'foo')])
got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 12"))]
self.assertEqual(got, [(12, 'foo')])
result = [f['strfield'] for f in vl.dataProvider().getFeatures(QgsFeatureRequest().setSubsetOfAttributes(['strfield'], vl.dataProvider().fields()))]
self.assertEqual(result, ['foo'])
result = [f['fid'] for f in vl.dataProvider().getFeatures(QgsFeatureRequest().setSubsetOfAttributes(['fid'], vl.dataProvider().fields()))]
self.assertEqual(result, [12])
# Test that when the 'fid' field is not set, regular insertion is done
f = QgsFeature()
f.setFields(vl.fields())
f.setAttributes([None, 'automatic_id'])
(res, out_f) = vl.dataProvider().addFeatures([f])
self.assertEqual(out_f[0].id(), 13)
self.assertEqual(out_f[0].attribute('fid'), 13)
self.assertEqual(out_f[0].attribute('strfield'), 'automatic_id')
# Test that when the 'fid' field is set, it is really used to set the id
f = QgsFeature()
f.setFields(vl.fields())
f.setAttributes([9876543210, 'bar'])
(res, out_f) = vl.dataProvider().addFeatures([f])
self.assertEqual(out_f[0].id(), 9876543210)
self.assertEqual(out_f[0].attribute('fid'), 9876543210)
self.assertEqual(out_f[0].attribute('strfield'), 'bar')
got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 9876543210"))]
self.assertEqual(got, [(9876543210, 'bar')])
self.assertTrue(vl.dataProvider().changeAttributeValues({9876543210: {1: 'baz'}}))
got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 9876543210"))]
self.assertEqual(got, [(9876543210, 'baz')])
self.assertTrue(vl.dataProvider().changeAttributeValues({9876543210: {0: 9876543210, 1: 'baw'}}))
got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 9876543210"))]
self.assertEqual(got, [(9876543210, 'baw')])
# Not allowed: changing the fid regular field
self.assertTrue(vl.dataProvider().changeAttributeValues({9876543210: {0: 12, 1: 'baw'}}))
got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 9876543210"))]
self.assertEqual(got, [(9876543210, 'baw')])
# Cannot delete fid
self.assertFalse(vl.dataProvider().deleteAttributes([0]))
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,279 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsNewGeoPackageLayerDialog
.. note:: 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.
"""
__author__ = 'Even Rouault'
__date__ = '2016-04-21'
__copyright__ = 'Copyright 2016, Even Rouault'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import hashlib
import os
import sys
import tempfile
import shutil
from PyQt.QtCore import QObject, QCoreApplication, QSettings, Qt, QEventLoop, QItemSelectionModel, QModelIndex
from PyQt.QtWidgets import QApplication, QWidget, QLineEdit, QDialogButtonBox, QTreeWidget, QComboBox, QPushButton, QToolButton
from PyQt.QtTest import QTest
from qgis.core import QGis, QgsMapLayerRegistry
from qgis.gui import QgsNewGeoPackageLayerDialog
from qgis.testing import (start_app,
unittest
)
def GDAL_COMPUTE_VERSION(maj, min, rev):
return ((maj) * 1000000 + (min) * 10000 + (rev) * 100)
class TestPyQgsNewGeoPackageLayerDialog(unittest.TestCase):
@classmethod
def setUpClass(cls):
"""Run before all tests"""
QCoreApplication.setOrganizationName("QGIS_Test")
QCoreApplication.setOrganizationDomain("QGIS_TestPyQgsNewGeoPackageLayerDialog.com")
QCoreApplication.setApplicationName("QGIS_TestPyQgsNewGeoPackageLayerDialog")
QSettings().clear()
start_app()
cls.basetestpath = tempfile.mkdtemp()
@classmethod
def tearDownClass(cls):
"""Run after all tests"""
QSettings().clear()
if cls.basetestpath is not None:
shutil.rmtree(cls.basetestpath, True)
def test(self):
# Skip if GDAL python bindings are not available
try:
from osgeo import gdal, ogr
except:
return
version_num = int(gdal.VersionInfo('VERSION_NUM'))
if version_num < GDAL_COMPUTE_VERSION(1, 11, 0):
return
dialog = QgsNewGeoPackageLayerDialog()
dialog.setProperty("hideDialogs", True)
mDatabaseEdit = dialog.findChild(QLineEdit, "mDatabaseEdit")
buttonBox = dialog.findChild(QDialogButtonBox, "buttonBox")
ok_button = buttonBox.button(QDialogButtonBox.Ok)
mTableNameEdit = dialog.findChild(QLineEdit, "mTableNameEdit")
mLayerIdentifierEdit = dialog.findChild(QLineEdit, "mLayerIdentifierEdit")
mLayerDescriptionEdit = dialog.findChild(QLineEdit, "mLayerDescriptionEdit")
mFeatureIdColumnEdit = dialog.findChild(QLineEdit, "mFeatureIdColumnEdit")
mGeometryTypeBox = dialog.findChild(QComboBox, "mGeometryTypeBox")
mGeometryColumnEdit = dialog.findChild(QLineEdit, "mGeometryColumnEdit")
mFieldNameEdit = dialog.findChild(QLineEdit, "mFieldNameEdit")
mFieldTypeBox = dialog.findChild(QComboBox, "mFieldTypeBox")
mFieldLengthEdit = dialog.findChild(QLineEdit, "mFieldLengthEdit")
mAddAttributeButton = dialog.findChild(QToolButton, "mAddAttributeButton")
mRemoveAttributeButton = dialog.findChild(QToolButton, "mRemoveAttributeButton")
mAttributeView = dialog.findChild(QTreeWidget, "mAttributeView")
dialog.accepted.connect(self.accepted_slot)
mGeometryTypeBox.setCurrentIndex(mGeometryTypeBox.findData(ogr.wkbPoint))
self.assertEqual(mGeometryTypeBox.currentText(), "Point")
self.assertFalse(ok_button.isEnabled())
dbname = os.path.join(self.basetestpath, 'test.gpkg')
mDatabaseEdit.setText(dbname)
self.assertEqual(mTableNameEdit.text(), 'test')
self.assertEqual(mLayerIdentifierEdit.text(), 'test')
self.assertTrue(ok_button.isEnabled())
mGeometryColumnEdit.setText('my_geom')
mFeatureIdColumnEdit.setText('my_fid')
self.assertFalse(mAddAttributeButton.isEnabled())
self.assertFalse(mRemoveAttributeButton.isEnabled())
mFieldNameEdit.setText('strfield')
self.assertTrue(mAddAttributeButton.isEnabled())
mFieldLengthEdit.setText('10')
QTest.mouseClick(mAddAttributeButton, Qt.LeftButton)
mFieldNameEdit.setText('intfield')
mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('integer'))
self.assertFalse(mFieldLengthEdit.isEnabled())
QTest.mouseClick(mAddAttributeButton, Qt.LeftButton)
mFieldNameEdit.setText('realfield')
mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('real'))
self.assertFalse(mFieldLengthEdit.isEnabled())
QTest.mouseClick(mAddAttributeButton, Qt.LeftButton)
mFieldNameEdit.setText('datefield')
mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('date'))
self.assertFalse(mFieldLengthEdit.isEnabled())
QTest.mouseClick(mAddAttributeButton, Qt.LeftButton)
mFieldNameEdit.setText('datetimefield')
mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('datetime'))
self.assertFalse(mFieldLengthEdit.isEnabled())
QTest.mouseClick(mAddAttributeButton, Qt.LeftButton)
if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0):
mFieldNameEdit.setText('int64field')
mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('integer64'))
self.assertFalse(mFieldLengthEdit.isEnabled())
QTest.mouseClick(mAddAttributeButton, Qt.LeftButton)
# Add and remove field
mFieldNameEdit.setText('dummy')
self.assertFalse(mFieldLengthEdit.isEnabled())
QTest.mouseClick(mAddAttributeButton, Qt.LeftButton)
index = mAttributeView.model().index(mAttributeView.model().rowCount() - 1, 0)
mAttributeView.setCurrentIndex(index)
QTest.mouseClick(mRemoveAttributeButton, Qt.LeftButton)
self.accepted = False
QTest.mouseClick(ok_button, Qt.LeftButton)
self.assertTrue(self.accepted)
layers = QgsMapLayerRegistry.instance().mapLayers()
self.assertEqual(len(layers), 1)
layer = layers[list(layers.keys())[0]]
self.assertEqual(layer.name(), 'test')
self.assertEqual(layer.geometryType(), QGis.Point)
QgsMapLayerRegistry.instance().removeAllMapLayers()
ds = ogr.Open(dbname)
lyr = ds.GetLayer(0)
self.assertEqual(lyr.GetFIDColumn(), 'my_fid')
self.assertEqual(lyr.GetGeometryColumn(), 'my_geom')
self.assertEqual(lyr.GetGeomType(), ogr.wkbPoint)
if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0):
self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 6)
else:
self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 5)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(0).GetNameRef(), 'strfield')
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(0).GetType(), ogr.OFTString)
# Only GDAL 2.0 recognizes string field width
if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0):
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(0).GetWidth(), 10)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(1).GetNameRef(), 'intfield')
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(1).GetType(), ogr.OFTInteger)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(1).GetWidth(), 0)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(2).GetNameRef(), 'realfield')
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(2).GetType(), ogr.OFTReal)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(2).GetWidth(), 0)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(3).GetNameRef(), 'datefield')
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(3).GetType(), ogr.OFTDate)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(3).GetWidth(), 0)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(4).GetNameRef(), 'datetimefield')
if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0):
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(4).GetType(), ogr.OFTDateTime)
else:
# There's a bug in OGR 1.11. The field is probably declared as DATETIME in SQL
# but OGR detects it as OFTDate
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(4).GetType(), ogr.OFTDate)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(4).GetWidth(), 0)
if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0):
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(5).GetNameRef(), 'int64field')
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(5).GetType(), ogr.OFTInteger64)
self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(5).GetWidth(), 0)
ds = None
# Try re-adding with different table. It should ask if we want to
# overwrite the DB, and we'll implicitly answer cancel, hence failure
mTableNameEdit.setText('table2')
self.accepted = False
QTest.mouseClick(ok_button, Qt.LeftButton)
self.assertFalse(self.accepted)
# Retry, and ask to keep the DB
self.accepted = False
dialog.setProperty('question_existing_db_answer_add_new_layer', True)
QTest.mouseClick(ok_button, Qt.LeftButton)
dialog.setProperty('question_existing_db_answer_add_new_layer', None)
self.assertTrue(self.accepted)
QgsMapLayerRegistry.instance().removeAllMapLayers()
ds = ogr.Open(dbname)
self.assertEqual(ds.GetLayerCount(), 2)
ds = None
# Retry, and ask to overwrite the DB
self.accepted = False
dialog.setProperty('question_existing_db_answer_overwrite', True)
QTest.mouseClick(ok_button, Qt.LeftButton)
dialog.setProperty('question_existing_db_answer_overwrite', None)
self.assertTrue(self.accepted)
QgsMapLayerRegistry.instance().removeAllMapLayers()
ds = ogr.Open(dbname)
self.assertEqual(ds.GetLayerCount(), 1)
ds = None
# Try re-adding with same parameters. It should ask if we want to
# overwrite the layer, and we'll implicitly answer no, hence failure
# since it already exists with that name
self.accepted = False
dialog.setProperty('question_existing_db_answer_add_new_layer', True)
QTest.mouseClick(ok_button, Qt.LeftButton)
dialog.setProperty('question_existing_db_answer_add_new_layer', None)
self.assertFalse(self.accepted)
# Now answer yes, and change a few things
mLayerIdentifierEdit.setText('my_identifier')
mLayerDescriptionEdit.setText('my_description')
dialog.setProperty('question_existing_db_answer_add_new_layer', True)
dialog.setProperty('question_existing_layer_answer_overwrite', True)
self.accepted = False
QTest.mouseClick(ok_button, Qt.LeftButton)
dialog.setProperty('question_existing_db_answer_add_new_layer', None)
dialog.setProperty('question_existing_layer_answer_overwrite', None)
self.assertTrue(self.accepted)
# Only check with OGR 2.0 since the IDENTIFIER and DESCRIPTION creation options don't exist in OGR 1.11
if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0):
layers = QgsMapLayerRegistry.instance().mapLayers()
self.assertEqual(len(layers), 1)
layer = layers[list(layers.keys())[0]]
self.assertEqual(layer.name(), 'my_identifier')
QgsMapLayerRegistry.instance().removeAllMapLayers()
ds = ogr.Open(dbname)
sql_lyr = ds.ExecuteSQL('SELECT * FROM gpkg_contents')
self.assertEqual(sql_lyr.GetFeatureCount(), 1)
f = sql_lyr.GetNextFeature()
identifier = f.GetField('identifier')
description = f.GetField('description')
f = None
ds.ReleaseResultSet(sql_lyr)
ds = None
self.assertEqual(identifier, 'my_identifier')
self.assertEqual(description, 'my_description')
else:
QgsMapLayerRegistry.instance().removeAllMapLayers()
# Try invalid path
mDatabaseEdit.setText('/this/is/invalid/test.gpkg')
self.accepted = False
QTest.mouseClick(ok_button, Qt.LeftButton)
self.assertFalse(self.accepted)
# dialog.exec_()
def accepted_slot(self):
self.accepted = True
if __name__ == '__main__':
unittest.main()