Skip to content
Permalink
Browse files

offline editing gkpg export

export works, problems with log-tables
  • Loading branch information
signedav committed May 22, 2018
1 parent 86c3e8b commit 68e2b52f837bb3a4c300a901a0fd0ce3a365b792
@@ -35,6 +35,8 @@
#include "qgsrelationmanager.h"
#include "qgsmapthemecollection.h"
#include "qgslayertree.h"
#include "qgsogrutils.h"
#include "qgsvectorfilewriter.h"

#include <QDir>
#include <QDomDocument>
@@ -74,14 +76,14 @@ QgsOfflineEditing::QgsOfflineEditing()
* - remove remote layers
* - mark as offline project
*/
bool QgsOfflineEditing::convertToOfflineProject( const QString &offlineDataPath, const QString &offlineDbFile, const QStringList &layerIds, bool onlySelected )
bool QgsOfflineEditing::convertToOfflineProject( const QString &offlineDataPath, const QString &offlineDbFile, const QStringList &layerIds, bool onlySelected, bool gpkg )
{
if ( layerIds.isEmpty() )
{
return false;
}
QString dbPath = QDir( offlineDataPath ).absoluteFilePath( offlineDbFile );
if ( createSpatialiteDB( dbPath ) )
if ( createSpatialiteDB( dbPath, gpkg ) )
{
spatialite_database_unique_ptr database;
int rc = database.open( dbPath );
@@ -136,7 +138,17 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString &offlineDataPath,
if ( vl )
{
QString origLayerId = vl->id();
QgsVectorLayer *newLayer = copyVectorLayer( vl, database.get(), dbPath, onlySelected );
QgsVectorLayer *newLayer;
if ( !gpkg )
{
//spatialite
newLayer = copyVectorLayer( vl, database.get(), dbPath, onlySelected );
}
else
{
//geopackage
newLayer = copyVectorLayerGpkg( vl, database.get(), dbPath );
}
if ( newLayer )
{
layerIdMapping.insert( origLayerId, newLayer );
@@ -383,7 +395,7 @@ void QgsOfflineEditing::initializeSpatialMetadata( sqlite3 *sqlite_handle )
spatial_ref_sys_init( sqlite_handle, 0 );
}

bool QgsOfflineEditing::createSpatialiteDB( const QString &offlineDbPath )
bool QgsOfflineEditing::createSpatialiteDB( const QString &offlineDbPath, bool gpkg )
{
int ret;
char *errMsg = nullptr;
@@ -393,16 +405,36 @@ bool QgsOfflineEditing::createSpatialiteDB( const QString &offlineDbPath )
QFile::remove( offlineDbPath );
}

// see also QgsNewSpatialiteLayerDialog::createDb()
QString dbPath = offlineDbPath;

if( gpkg )
{
OGRSFDriverH hGpkgDriver = OGRGetDriverByName( "GPKG" );
if ( !hGpkgDriver )
{
showWarning( tr( "Creation of database failed. GeoPackage driver not found." ) );
return false;
}

gdal::ogr_datasource_unique_ptr hDS( OGR_Dr_CreateDataSource( hGpkgDriver, dbPath.toUtf8().constData(), nullptr ) );
if ( !hDS )
{
showWarning( tr( "Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
return false;
}
}
else{
// see also QgsNewSpatialiteLayerDialog::createDb()

QFileInfo fullPath = QFileInfo( offlineDbPath );
QDir path = fullPath.dir();
QFileInfo fullPath = QFileInfo( offlineDbPath );
QDir path = fullPath.dir();

// Must be sure there is destination directory ~/.qgis
QDir().mkpath( path.absolutePath() );
// Must be sure there is destination directory ~/.qgis
QDir().mkpath( path.absolutePath() );

// creating/opening the new database
QString dbPath = newDb.fileName();
// creating/opening the new database
dbPath = newDb.fileName();
}
spatialite_database_unique_ptr database;
ret = database.open_v2( dbPath, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
if ( ret )
@@ -719,6 +751,56 @@ QgsVectorLayer *QgsOfflineEditing::copyVectorLayer( QgsVectorLayer *layer, sqlit
return nullptr;
}

QgsVectorLayer *QgsOfflineEditing::copyVectorLayerGpkg( QgsVectorLayer *layer, sqlite3 *db, const QString &offlineDbPath )
{
if ( !layer )
return nullptr;

QString tableName = layer->id();
QgsDebugMsgLevel( QString( "Creating offline table %1 ..." ).arg( tableName ), 4 );

QgsVectorFileWriter::SaveVectorOptions options;
options.driverName = QStringLiteral( "GPKG" );
options.layerName = layer->name();
options.actionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteLayer;

QString error;
if ( QgsVectorFileWriter::writeAsVectorFormat( layer, offlineDbPath, options, &error ) != QgsVectorFileWriter::NoError )
{
showWarning( tr( "Packaging layer %1 failed: %2" ).arg( layer->name(), error ) );
return nullptr;
}

QString uri = QStringLiteral( "%1|layername=%2" ).arg( offlineDbPath, layer->name() );
QgsVectorLayer *newLayer = new QgsVectorLayer( uri, layer->name() + " (offline)", QStringLiteral( "ogr" ) );

if ( newLayer->isValid() )
{
// update feature id lookup
getOrCreateLayerId( db, newLayer->id() );

// mark as offline layer
newLayer->setCustomProperty( CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE, true );

// store original layer source
newLayer->setCustomProperty( CUSTOM_PROPERTY_REMOTE_SOURCE, layer->source() );
newLayer->setCustomProperty( CUSTOM_PROPERTY_REMOTE_PROVIDER, layer->providerType() );

// register this layer with the central layers registry
QgsProject::instance()->addMapLayers(
QList<QgsMapLayer *>() << newLayer );

// copy style
copySymbology( layer, newLayer );
updateRelations( layer, newLayer );
updateMapThemes( layer, newLayer );
updateLayerOrder( layer, newLayer );

return newLayer;
}
return nullptr;
}

void QgsOfflineEditing::applyAttributesAdded( QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId, int commitNo )
{
QString sql = QStringLiteral( "SELECT \"name\", \"type\", \"length\", \"precision\", \"comment\" FROM 'log_added_attrs' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2" ).arg( layerId ).arg( commitNo );
@@ -58,7 +58,7 @@ class CORE_EXPORT QgsOfflineEditing : public QObject
* \param layerIds List of layer names to convert
* \param onlySelected Only copy selected features from layers where a selection is present
*/
bool convertToOfflineProject( const QString &offlineDataPath, const QString &offlineDbFile, const QStringList &layerIds, bool onlySelected = false );
bool convertToOfflineProject( const QString &offlineDataPath, const QString &offlineDbFile, const QStringList &layerIds, bool onlySelected = false, bool gpkg = false );

//! Returns true if current project is offline
bool isOfflineProject() const;
@@ -107,8 +107,10 @@ class CORE_EXPORT QgsOfflineEditing : public QObject

private:
void initializeSpatialMetadata( sqlite3 *sqlite_handle );
bool createSpatialiteDB( const QString &offlineDbPath );
bool createSpatialiteDB( const QString &offlineDbPath, bool gpkg );
void createLoggingTables( sqlite3 *db );

QgsVectorLayer *copyVectorLayerGpkg( QgsVectorLayer *layer, sqlite3 *db, const QString &offlineDbPath );
QgsVectorLayer *copyVectorLayer( QgsVectorLayer *layer, sqlite3 *db, const QString &offlineDbPath, bool onlySelected );

void applyAttributesAdded( QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId, int commitNo );
@@ -104,7 +104,7 @@ void QgsOfflineEditingPlugin::convertProject()
}

mProgressDialog->setTitle( tr( "Converting to Offline Project" ) );
if ( mOfflineEditing->convertToOfflineProject( myPluginGui->offlineDataPath(), myPluginGui->offlineDbFile(), selectedLayerIds, myPluginGui->onlySelected() ) )
if ( mOfflineEditing->convertToOfflineProject( myPluginGui->offlineDataPath(), myPluginGui->offlineDbFile(), selectedLayerIds, myPluginGui->onlySelected(), myPluginGui->isGeopackage() ) )
{
updateActions();
// Redraw, to make the offline layer visible
@@ -106,7 +106,8 @@ QgsOfflineEditingPluginGui::QgsOfflineEditingPluginGui( QWidget *parent, Qt::Win

restoreState();

mOfflineDbFile = QStringLiteral( "offline.sqlite" );
mOnlySelectedCheckBox->setDisabled(true);
mOfflineDbFile = QStringLiteral( "offline.gpkg" );
mOfflineDataPathLineEdit->setText( QDir( mOfflineDataPath ).absoluteFilePath( mOfflineDbFile ) );

QgsLayerTree *rootNode = QgsProject::instance()->layerTreeRoot()->clone();
@@ -116,6 +117,7 @@ QgsOfflineEditingPluginGui::QgsOfflineEditingPluginGui( QWidget *parent, Qt::Win

connect( mSelectAllButton, &QAbstractButton::clicked, this, &QgsOfflineEditingPluginGui::selectAll );
connect( mDeselectAllButton, &QAbstractButton::clicked, this, &QgsOfflineEditingPluginGui::deSelectAll );
connect( mSelectDatatypeCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsOfflineEditingPluginGui::datatypeChanged );
}

QgsOfflineEditingPluginGui::~QgsOfflineEditingPluginGui()
@@ -145,23 +147,52 @@ bool QgsOfflineEditingPluginGui::onlySelected() const
return mOnlySelectedCheckBox->checkState() == Qt::Checked;
}

bool QgsOfflineEditingPluginGui::isGeopackage() const
{
return mSelectDatatypeCombo->currentIndex() == 0;
}

void QgsOfflineEditingPluginGui::mBrowseButton_clicked()
{
QString fileName = QFileDialog::getSaveFileName( this,
tr( "Select target database for offline data" ),
QDir( mOfflineDataPath ).absoluteFilePath( mOfflineDbFile ),
tr( "SpatiaLite DB" ) + " (*.sqlite);;"
+ tr( "All files" ) + " (*.*)" );
if( isGeopackage() )
{
//GeoPackage
QString fileName = QFileDialog::getSaveFileName( this,
tr( "Select target database for offline data" ),
QDir( mOfflineDataPath ).absoluteFilePath( mOfflineDbFile ),
tr( "GeoPackage" ) + " (*.gpkg);;"
+ tr( "All files" ) + " (*.*)" );

if ( !fileName.isEmpty() )
if ( !fileName.isEmpty() )
{
if ( !fileName.endsWith( QLatin1String( ".gpkg" ), Qt::CaseInsensitive ) )
{
fileName += QLatin1String( ".gpkg" );
}
mOfflineDbFile = QFileInfo( fileName ).fileName();
mOfflineDataPath = QFileInfo( fileName ).absolutePath();
mOfflineDataPathLineEdit->setText( fileName );
}
}
else
{
if ( !fileName.endsWith( QLatin1String( ".sqlite" ), Qt::CaseInsensitive ) )
//Spacialite
QString fileName = QFileDialog::getSaveFileName( this,
tr( "Select target database for offline data" ),
QDir( mOfflineDataPath ).absoluteFilePath( mOfflineDbFile ),
tr( "SpatiaLite DB" ) + " (*.sqlite);;"
+ tr( "All files" ) + " (*.*)" );

if ( !fileName.isEmpty() )
{
fileName += QLatin1String( ".sqlite" );
if ( !fileName.endsWith( QLatin1String( ".sqlite" ), Qt::CaseInsensitive ) )
{
fileName += QLatin1String( ".sqlite" );
}
mOfflineDbFile = QFileInfo( fileName ).fileName();
mOfflineDataPath = QFileInfo( fileName ).absolutePath();
mOfflineDataPathLineEdit->setText( fileName );
}
mOfflineDbFile = QFileInfo( fileName ).fileName();
mOfflineDataPath = QFileInfo( fileName ).absolutePath();
mOfflineDataPathLineEdit->setText( fileName );
}
}

@@ -216,9 +247,26 @@ void QgsOfflineEditingPluginGui::selectAll()
nodeLayer->setItemVisibilityChecked( true );
}


void QgsOfflineEditingPluginGui::deSelectAll()
{
Q_FOREACH ( QgsLayerTreeLayer *nodeLayer, mLayerTree->layerTreeModel()->rootGroup()->findLayers() )
nodeLayer->setItemVisibilityChecked( false );
}

void QgsOfflineEditingPluginGui::datatypeChanged( int index )
{
if( index==0 )
{
//GeoPackage
mOnlySelectedCheckBox->setDisabled(true);
mOfflineDbFile = QStringLiteral( "offline.gpkg" );
}
else
{
//Spatialite
mOnlySelectedCheckBox->setEnabled(true);
mOfflineDbFile = QStringLiteral( "offline.sqlite" );
}
mOfflineDataPathLineEdit->setText( QDir( mOfflineDataPath ).absoluteFilePath( mOfflineDbFile ) );
}

@@ -46,11 +46,13 @@ class QgsOfflineEditingPluginGui : public QDialog, private Ui::QgsOfflineEditing
QString offlineDbFile();
QStringList selectedLayerIds();
bool onlySelected() const;
bool isGeopackage() const;

public slots:
//! Change the selection of layers in the list
void selectAll();
void deSelectAll();
void datatypeChanged( int index );

private:
void saveState();
@@ -18,6 +18,43 @@
<normaloff>.</normaloff>.</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="selectDatatypeComboLabel">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Storage Type</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mSelectDatatypeCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>GeoPackage</string>
</property>
</item>
<item>
<property name="text">
<string>Spatialite</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>

0 comments on commit 68e2b52

Please sign in to comment.
You can’t perform that action at this time.