Skip to content

Commit

Permalink
Merge pull request #8263 from signedav/gpkg_offline_editing
Browse files Browse the repository at this point in the history
[Bugfix] offline editing with gpkg
  • Loading branch information
m-kuhn committed Oct 25, 2018
2 parents ce0f8e3 + fc8062e commit 0288895
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 11 deletions.
103 changes: 92 additions & 11 deletions src/core/qgsofflineediting.cpp
Expand Up @@ -45,6 +45,8 @@
#include <QFile>
#include <QMessageBox>

#include <ogr_srs_api.h>

extern "C"
{
#include <sqlite3.h>
Expand Down Expand Up @@ -630,19 +632,95 @@ QgsVectorLayer *QgsOfflineEditing::copyVectorLayer( QgsVectorLayer *layer, sqlit
}
case GPKG:
{
QgsVectorFileWriter::SaveVectorOptions options;
options.driverName = QStringLiteral( "GPKG" );
options.layerName = tableName;
options.actionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteLayer;
options.onlySelectedFeatures = onlySelected;

QString error;
if ( QgsVectorFileWriter::writeAsVectorFormat( layer, offlineDbPath, options, &error ) != QgsVectorFileWriter::NoError )
// Set options
char **options = nullptr;

options = CSLSetNameValue( options, "OVERWRITE", "YES" );
options = CSLSetNameValue( options, "IDENTIFIER", tr( "%1 (offline)" ).arg( layer->name() ).toUtf8().constData() );
options = CSLSetNameValue( options, "DESCRIPTION", layer->dataComment().toUtf8().constData() );

//the FID-name should not exist in the original data
QString fidBase( QStringLiteral( "fid" ) );
QString fid = fidBase;
int counter = 1;
while ( layer->dataProvider()->fields().lookupField( fid ) >= 0 && counter < 10000 )
{
fid = fidBase + '_' + QString::number( counter );
counter++;
}
if ( counter == 10000 )
{
showWarning( tr( "Cannot make FID-name for GPKG " ) );
return nullptr;
}

options = CSLSetNameValue( options, "FID", fid.toUtf8().constData() );

if ( layer->isSpatial() )
{
options = CSLSetNameValue( options, "GEOMETRY_COLUMN", "geom" );
options = CSLSetNameValue( options, "SPATIAL_INDEX", "YES" );
}

OGRSFDriverH hDriver = nullptr;
OGRSpatialReferenceH hSRS = OSRNewSpatialReference( layer->crs().toWkt().toLocal8Bit().data() );
gdal::ogr_datasource_unique_ptr hDS( OGROpen( offlineDbPath.toUtf8().constData(), true, &hDriver ) );
OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS, static_cast<OGRwkbGeometryType>( layer->wkbType() ), options );
CSLDestroy( options );
if ( hSRS )
OSRRelease( hSRS );
if ( !hLayer )
{
showWarning( tr( "Packaging layer %1 failed: %2" ).arg( layer->name(), error ) );
showWarning( tr( "Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
return nullptr;
}

const QgsFields providerFields = layer->dataProvider()->fields();
for ( const auto &field : providerFields )
{
const QString fieldName( field.name() );
const QVariant::Type type = field.type();
OGRFieldType ogrType( OFTString );
if ( type == QVariant::Int )
ogrType = OFTInteger;
else if ( type == QVariant::LongLong )
ogrType = OFTInteger64;
else if ( type == QVariant::Double )
ogrType = OFTReal;
else if ( type == QVariant::Time )
ogrType = OFTTime;
else if ( type == QVariant::Date )
ogrType = OFTDate;
else if ( type == QVariant::DateTime )
ogrType = OFTDateTime;
else
ogrType = OFTString;

int ogrWidth = field.length();

gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( fieldName.toUtf8().constData(), ogrType ) );
OGR_Fld_SetWidth( fld.get(), ogrWidth );

if ( OGR_L_CreateField( hLayer, fld.get(), true ) != OGRERR_NONE )
{
showWarning( tr( "Creation of field %1 failed (OGR error: %2)" )
.arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
return nullptr;
}
}

// In GDAL >= 2.0, the driver implements a deferred 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() ) ) );
showWarning( msg );
return nullptr;
}
hDS.reset();

QString uri = QStringLiteral( "%1|layername=%2" ).arg( offlineDbPath, tableName );
newLayer = new QgsVectorLayer( uri, layer->name() + " (offline)", QStringLiteral( "ogr" ) );
break;
Expand Down Expand Up @@ -684,9 +762,12 @@ QgsVectorLayer *QgsOfflineEditing::copyVectorLayer( QgsVectorLayer *layer, sqlit

// NOTE: SpatiaLite provider ignores position of geometry column
// fill gap in QgsAttributeMap if geometry column is not last (WORKAROUND)
int column = 0;
QgsAttributes attrs = f.attributes();
QgsAttributes newAttrs( attrs.count() );
int column = 0;
// on GPKG newAttrs has an addition FID attribute, so we have to add a dummy in the original set
if ( containerType == GPKG )
column++;
QgsAttributes newAttrs( attrs.count() + column );
for ( int it = 0; it < attrs.count(); ++it )
{
newAttrs[column++] = attrs.at( it );
Expand Down
1 change: 1 addition & 0 deletions tests/src/core/testqgsofflineediting.cpp
Expand Up @@ -125,6 +125,7 @@ void TestQgsOfflineEditing::createGeopackageAndSynchronizeBack()
QCOMPARE( mpLayer->featureCount(), numberOfFeatures );
QCOMPARE( mpLayer->fields().size(), numberOfFields );

connect( mOfflineEditing, &QgsOfflineEditing::warning, this, []( const QString & title, const QString & message ) { qDebug() << title << message; } );
//convert
mOfflineEditing->convertToOfflineProject( offlineDataPath, offlineDbFile, layerIds, false, QgsOfflineEditing::GPKG );

Expand Down

0 comments on commit 0288895

Please sign in to comment.