mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Merge pull request #3031 from rouault/gpkg_improvements
GeoPackage support related improvements
This commit is contained in:
commit
afdb6c78c0
@ -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
|
||||
|
@ -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>
|
||||
|
394
images/themes/default/mActionNewGeoPackageLayer.svg
Normal file
394
images/themes/default/mActionNewGeoPackageLayer.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 62 KiB |
@ -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
|
||||
|
14
python/gui/qgsnewgeopackagelayerdialog.sip
Normal file
14
python/gui/qgsnewgeopackagelayerdialog.sip
Normal 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();
|
||||
|
||||
};
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -188,6 +188,7 @@ SET(QGIS_CORE_SRCS
|
||||
qgssnappingutils.cpp
|
||||
qgsspatialindex.cpp
|
||||
qgssqlexpressioncompiler.cpp
|
||||
qgssqliteexpressioncompiler.cpp
|
||||
qgsstatisticalsummary.cpp
|
||||
qgsstringutils.cpp
|
||||
qgstextlabelfeature.cpp
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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
|
@ -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();
|
||||
|
@ -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
|
||||
|
515
src/gui/qgsnewgeopackagelayerdialog.cpp
Normal file
515
src/gui/qgsnewgeopackagelayerdialog.cpp
Normal 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;
|
||||
}
|
||||
|
63
src/gui/qgsnewgeopackagelayerdialog.h
Normal file
63
src/gui/qgsnewgeopackagelayerdialog.h
Normal 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
|
@ -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" )
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ class QgsOgrFeatureSource : public QgsAbstractFeatureSource
|
||||
QString mSubsetString;
|
||||
QTextCodec* mEncoding;
|
||||
QgsFields mFields;
|
||||
bool mFirstFieldIsFid;
|
||||
QgsFields mFieldsWithoutFid;
|
||||
OGRwkbGeometryType mOgrGeometryTypeFilter;
|
||||
QString mDriverName;
|
||||
|
||||
|
@ -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 )
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -10,7 +10,6 @@ SET(SPATIALITE_SRCS
|
||||
qgsspatialitedataitems.cpp
|
||||
qgsspatialiteconnection.cpp
|
||||
qgsspatialiteconnpool.cpp
|
||||
qgsspatialiteexpressioncompiler.cpp
|
||||
qgsspatialitefeatureiterator.cpp
|
||||
qgsspatialitesourceselect.cpp
|
||||
qgsspatialitetablemodel.cpp
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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"/>
|
||||
|
485
src/ui/qgsnewgeopackagelayerdialogbase.ui
Normal file
485
src/ui/qgsnewgeopackagelayerdialogbase.ui
Normal 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><html><head/><body><p>Field length / width</p></body></html></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><html><head/><body><p>Human-readable identifier (e.g. short name) for the layer content</p></body></html></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><html><head/><body><p>Name of the geometry column</p></body></html></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><html><head/><body><p>Existing or new GeoPackage database file name</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="mSelectDatabaseButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Select an existing or create a new database</p></body></html></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><html><head/><body><p>Table name in the database</p></body></html></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><html><head/><body><p>Human-readable description for the layer content</p></body></html></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><html><head/><body><p>Geometry type</p></body></html></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><html><head/><body><p>Name of the feature id column</p></body></html></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>
|
@ -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)
|
||||
|
161
tests/src/python/test_provider_ogr_gpkg.py
Normal file
161
tests/src/python/test_provider_ogr_gpkg.py
Normal 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()
|
279
tests/src/python/test_qgsnewgeopackagelayerdialog.py
Normal file
279
tests/src/python/test_qgsnewgeopackagelayerdialog.py
Normal 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()
|
Loading…
x
Reference in New Issue
Block a user