34 #include <QDomDocument>
37 #include <QMessageBox>
42 #include <spatialite.h>
49 #define CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE "isOfflineEditable"
50 #define CUSTOM_PROPERTY_REMOTE_SOURCE "remoteSource"
51 #define CUSTOM_PROPERTY_REMOTE_PROVIDER "remoteProvider"
52 #define PROJECT_ENTRY_SCOPE_OFFLINE "OfflineEditingPlugin"
53 #define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH "/OfflineDbPath"
70 if ( layerIds.isEmpty() )
74 QString dbPath = QDir( offlineDataPath ).absoluteFilePath( offlineDbFile );
75 if ( createSpatialiteDB( dbPath ) )
79 int rc = sqlite3_open( dbPath.toUtf8().constData(), &db );
80 if ( rc != SQLITE_OK )
82 showWarning(
tr(
"Could not open the spatialite database" ) );
87 createLoggingTables( db );
91 QMap<QString, QgsVectorJoinList > joinInfoBuffer;
92 QMap<QString, QgsVectorLayer*> layerIdMapping;
94 for (
int i = 0; i < layerIds.count(); i++ )
104 QgsVectorJoinList::iterator it = joins.begin();
105 while ( it != joins.end() )
107 if (( *it ).prefix.isNull() )
112 ( *it ).prefix = vl->
name() +
"_";
116 joinInfoBuffer.insert( vl->
id(), joins );
120 for (
int i = 0; i < layerIds.count(); i++ )
126 QString origLayerId = vl->
id();
131 layerIdMapping.insert( origLayerId, newLayer );
136 QMap<QString, QgsVectorJoinList >::ConstIterator it;
137 for ( it = joinInfoBuffer.constBegin(); it != joinInfoBuffer.constEnd(); ++it )
146 if ( newJoinedLayer )
163 if ( projectTitle.isEmpty() )
167 projectTitle +=
" (offline)";
207 QList<QgsMapLayer*> offlineLayers;
209 for ( QMap<QString, QgsMapLayer*>::iterator layer_it = mapLayers.begin() ; layer_it != mapLayers.end(); ++layer_it )
214 offlineLayers << layer;
218 for (
int l = 0; l < offlineLayers.count(); l++ )
226 QString remoteName = layer->
name();
227 remoteName.remove( QRegExp(
" \\(offline\\)$" ) );
238 QList<QgsMapLayer *>() << remoteLayer,
true );
241 copySymbology( offlineLayer, remoteLayer );
244 QString qgisLayerId = layer->
id();
245 QString sql = QString(
"SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
246 int layerId = sqlQueryInt( db, sql, -1 );
252 int commitNo = getCommitNo( db );
253 for (
int i = 0; i < commitNo; i++ )
256 applyAttributesAdded( remoteLayer, db, layerId, i );
257 applyAttributeValueChanges( offlineLayer, remoteLayer, db, layerId, i );
258 applyGeometryChanges( remoteLayer, db, layerId, i );
261 applyFeaturesAdded( offlineLayer, remoteLayer, db, layerId );
262 applyFeaturesRemoved( remoteLayer, db, layerId );
267 updateFidLookup( remoteLayer, db, layerId );
270 sql = QString(
"DELETE FROM 'log_added_attrs' WHERE \"layer_id\" = %1" ).arg( layerId );
272 sql = QString(
"DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
274 sql = QString(
"DELETE FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
276 sql = QString(
"DELETE FROM 'log_feature_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
278 sql = QString(
"DELETE FROM 'log_geometry_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
282 QString sql = QString(
"UPDATE 'log_indices' SET 'last_index' = 0 WHERE \"name\" = 'commit_no'" );
287 showWarning( remoteLayer->
commitErrors().join(
"\n" ) );
293 ( QStringList() << qgisLayerId ) );
297 projectTitle.remove( QRegExp(
" \\(offline\\)$" ) );
309 void QgsOfflineEditing::initializeSpatialMetadata(
sqlite3 *sqlite_handle )
312 if ( !sqlite_handle )
317 int ret = sqlite3_get_table( sqlite_handle,
"select count(*) from sqlite_master", &results, &rows, &columns, NULL );
318 if ( ret != SQLITE_OK )
323 for (
int i = 1; i <= rows; i++ )
324 count = atoi( results[( i * columns ) + 0] );
327 sqlite3_free_table( results );
332 bool above41 =
false;
333 ret = sqlite3_get_table( sqlite_handle,
"select spatialite_version()", &results, &rows, &columns, NULL );
334 if ( ret == SQLITE_OK && rows == 1 && columns == 1 )
336 QString version = QString::fromUtf8( results[1] );
337 QStringList parts = version.split(
" ", QString::SkipEmptyParts );
338 if ( parts.size() >= 1 )
340 QStringList verparts = parts[0].split(
".", QString::SkipEmptyParts );
341 above41 = verparts.size() >= 2 && ( verparts[0].toInt() > 4 || ( verparts[0].toInt() == 4 && verparts[1].toInt() >= 1 ) );
345 sqlite3_free_table( results );
349 ret = sqlite3_exec( sqlite_handle, above41 ?
"SELECT InitSpatialMetadata(1)" :
"SELECT InitSpatialMetadata()", NULL, NULL, &errMsg );
351 if ( ret != SQLITE_OK )
353 QString errCause =
tr(
"Unable to initialize SpatialMetadata:\n" );
354 errCause += QString::fromUtf8( errMsg );
355 showWarning( errCause );
356 sqlite3_free( errMsg );
359 spatial_ref_sys_init( sqlite_handle, 0 );
362 bool QgsOfflineEditing::createSpatialiteDB(
const QString& offlineDbPath )
367 QFile newDb( offlineDbPath );
368 if ( newDb.exists() )
370 QFile::remove( offlineDbPath );
375 QFileInfo fullPath = QFileInfo( offlineDbPath );
376 QDir path = fullPath.dir();
379 QDir().mkpath( path.absolutePath() );
382 QString dbPath = newDb.fileName();
383 spatialite_init( 0 );
384 ret = sqlite3_open_v2( dbPath.toUtf8().constData(), &sqlite_handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL );
388 QString errCause =
tr(
"Could not create a new database\n" );
389 errCause += QString::fromUtf8( sqlite3_errmsg( sqlite_handle ) );
390 sqlite3_close( sqlite_handle );
391 showWarning( errCause );
395 ret = sqlite3_exec( sqlite_handle,
"PRAGMA foreign_keys = 1", NULL, 0, &errMsg );
396 if ( ret != SQLITE_OK )
398 showWarning(
tr(
"Unable to activate FOREIGN_KEY constraints" ) );
399 sqlite3_free( errMsg );
400 sqlite3_close( sqlite_handle );
403 initializeSpatialMetadata( sqlite_handle );
406 sqlite3_close( sqlite_handle );
411 void QgsOfflineEditing::createLoggingTables(
sqlite3* db )
414 QString sql =
"CREATE TABLE 'log_indices' ('name' TEXT, 'last_index' INTEGER)";
417 sql =
"INSERT INTO 'log_indices' VALUES ('commit_no', 0)";
420 sql =
"INSERT INTO 'log_indices' VALUES ('layer_id', 0)";
424 sql =
"CREATE TABLE 'log_layer_ids' ('id' INTEGER, 'qgis_id' TEXT)";
428 sql =
"CREATE TABLE 'log_fids' ('layer_id' INTEGER, 'offline_fid' INTEGER, 'remote_fid' INTEGER)";
432 sql =
"CREATE TABLE 'log_added_attrs' ('layer_id' INTEGER, 'commit_no' INTEGER, ";
433 sql +=
"'name' TEXT, 'type' INTEGER, 'length' INTEGER, 'precision' INTEGER, 'comment' TEXT)";
437 sql =
"CREATE TABLE 'log_added_features' ('layer_id' INTEGER, 'fid' INTEGER)";
441 sql =
"CREATE TABLE 'log_removed_features' ('layer_id' INTEGER, 'fid' INTEGER)";
445 sql =
"CREATE TABLE 'log_feature_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'attr' INTEGER, 'value' TEXT)";
449 sql =
"CREATE TABLE 'log_geometry_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'geom_wkt' TEXT)";
464 QString tableName = layer->
id();
467 QString sql = QString(
"CREATE TABLE '%1' (" ).arg( tableName );
470 for (
int idx = 0; idx < fields.
count(); ++idx )
472 QString dataType =
"";
473 QVariant::Type type = fields[idx].type();
474 if ( type == QVariant::Int || type == QVariant::LongLong )
476 dataType =
"INTEGER";
478 else if ( type == QVariant::Double )
482 else if ( type == QVariant::String )
488 showWarning(
tr(
"%1: Unknown data type %2. Not using type affinity for the field." ).arg( fields[idx].name() ).arg( QVariant::typeToName( type ) ) );
491 sql += delim + QString(
"'%1' %2" ).arg( fields[idx].name() ).arg( dataType );
496 int rc = sqlExec( db, sql );
501 QString geomType =
"";
508 geomType =
"MULTIPOINT";
511 geomType =
"LINESTRING";
514 geomType =
"MULTILINESTRING";
517 geomType =
"POLYGON";
520 geomType =
"MULTIPOLYGON";
523 showWarning(
tr(
"QGIS wkbType %1 not supported" ).arg( layer->
wkbType() ) );
526 QString sqlAddGeom = QString(
"SELECT AddGeometryColumn('%1', 'Geometry', %2, '%3', 2)" )
528 .arg( layer->
crs().
authid().startsWith(
"EPSG:", Qt::CaseInsensitive ) ? layer->
crs().
authid().mid( 5 ).toLong() : 0 )
532 QString sqlCreateIndex = QString(
"SELECT CreateSpatialIndex('%1', 'Geometry')" ).arg( tableName );
534 if ( rc == SQLITE_OK )
536 rc = sqlExec( db, sqlAddGeom );
537 if ( rc == SQLITE_OK )
539 rc = sqlExec( db, sqlCreateIndex );
544 if ( rc == SQLITE_OK )
548 .arg( offlineDbPath )
550 layer->
name() +
" (offline)",
"spatialite" );
562 QList<QgsMapLayer *>() << newLayer );
569 copySymbology( layer, newLayer );
575 if ( layerTreeLayer )
578 if ( parentTreeGroup )
580 int index = parentTreeGroup->
children().indexOf( layerTreeLayer );
583 if ( newLayerTreeLayer )
597 copySymbology( layer, newLayer );
610 int featureCount = 1;
612 QList<QgsFeatureId> remoteFeatureIds;
615 remoteFeatureIds << f.
id();
622 for (
int it = 0; it < attrs.count(); ++it )
624 newAttrs[column++] = attrs[it];
638 int layerId = getOrCreateLayerId( db, newLayer->
id() );
639 QList<QgsFeatureId> offlineFeatureIds;
644 offlineFeatureIds << f.
id();
648 sqlExec( db,
"BEGIN" );
649 int remoteCount = remoteFeatureIds.size();
650 for (
int i = 0; i < remoteCount; i++ )
652 addFidLookup( db, layerId, offlineFeatureIds.at( i ), remoteFeatureIds.at( remoteCount - ( i + 1 ) ) );
655 sqlExec( db,
"COMMIT" );
664 QStringList() << layer->
id() );
671 void QgsOfflineEditing::applyAttributesAdded(
QgsVectorLayer* remoteLayer,
sqlite3* db,
int layerId,
int commitNo )
673 QString sql = QString(
"SELECT \"name\", \"type\", \"length\", \"precision\", \"comment\" FROM 'log_added_attrs' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2" ).arg( layerId ).arg( commitNo );
674 QList<QgsField> fields = sqlQueryAttributesAdded( db, sql );
677 QList<QgsVectorDataProvider::NativeType> nativeTypes = provider->
nativeTypes();
680 QMap < QVariant::Type, QString > typeNameLookup;
681 for (
int i = 0; i < nativeTypes.size(); i++ )
689 for (
int i = 0; i < fields.size(); i++ )
693 if ( typeNameLookup.contains( field.
type() ) )
695 QString typeName = typeNameLookup[ field.
type()];
701 showWarning( QString(
"Could not add attribute '%1' of type %2" ).arg( field.
name() ).arg( field.
type() ) );
710 QString sql = QString(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
711 QList<int> newFeatureIds = sqlQueryInts( db, sql );
715 QVector<QVariant> defaultValues( remoteFlds.
count() );
716 for (
int i = 0; i < remoteFlds.
count(); ++i )
724 for (
int i = 0; i < newFeatureIds.size(); i++ )
738 for ( QgsFeatureList::iterator it = features.begin(); it != features.end(); ++it )
744 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
747 for (
int it = 0; it < attrs.count(); ++it )
749 newAttrs[ attrLookup[ it ] ] = attrs[ it ];
754 for (
int k = 0; k < newAttrs.count(); ++k )
756 if ( newAttrs[k].
isNull() && !defaultValues[k].isNull() )
757 newAttrs[k] = defaultValues[k];
768 void QgsOfflineEditing::applyFeaturesRemoved(
QgsVectorLayer* remoteLayer,
sqlite3* db,
int layerId )
770 QString sql = QString(
"SELECT \"fid\" FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
776 for ( QgsFeatureIds::const_iterator it = values.begin(); it != values.end(); ++it )
787 QString sql = QString(
"SELECT \"fid\", \"attr\", \"value\" FROM 'log_feature_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2 " ).arg( layerId ).arg( commitNo );
788 AttributeValueChanges values = sqlQueryAttributeValueChanges( db, sql );
792 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
794 for (
int i = 0; i < values.size(); i++ )
796 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
798 remoteLayer->
changeAttributeValue( fid, attrLookup[ values.at( i ).attr ], values.at( i ).value );
804 void QgsOfflineEditing::applyGeometryChanges(
QgsVectorLayer* remoteLayer,
sqlite3* db,
int layerId,
int commitNo )
806 QString sql = QString(
"SELECT \"fid\", \"geom_wkt\" FROM 'log_geometry_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2" ).arg( layerId ).arg( commitNo );
807 GeometryChanges values = sqlQueryGeometryChanges( db, sql );
811 for (
int i = 0; i < values.size(); i++ )
813 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
836 if ( offlineFid( db, layerId, f.
id() ) == -1 )
838 newRemoteFids[ f.
id()] =
true;
846 QString sql = QString(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
847 QList<int> newOfflineFids = sqlQueryInts( db, sql );
849 if ( newRemoteFids.size() != newOfflineFids.size() )
857 sqlExec( db,
"BEGIN" );
858 for ( QMap<QgsFeatureId, bool>::const_iterator it = newRemoteFids.begin(); it != newRemoteFids.end(); ++it )
860 addFidLookup( db, layerId, newOfflineFids.at( i++ ), it.key() );
862 sqlExec( db,
"COMMIT" );
872 if ( error.isEmpty() )
876 if ( !error.isEmpty() )
878 showWarning( error );
888 QMap <
int ,
int > attrLookup;
890 for (
int i = 0; i < remoteAttrs.size(); i++ )
892 attrLookup.insert( offlineAttrs.at( i ), remoteAttrs.at( i ) );
898 void QgsOfflineEditing::showWarning(
const QString& message )
900 emit
warning(
tr(
"Offline Editing Plugin" ), message );
903 sqlite3* QgsOfflineEditing::openLoggingDb()
907 if ( !dbPath.isEmpty() )
909 int rc = sqlite3_open( dbPath.toUtf8().constData(), &db );
910 if ( rc != SQLITE_OK )
912 showWarning(
tr(
"Could not open the spatialite logging database" ) );
920 int QgsOfflineEditing::getOrCreateLayerId(
sqlite3* db,
const QString& qgisLayerId )
922 QString sql = QString(
"SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
923 int layerId = sqlQueryInt( db, sql, -1 );
927 sql =
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'layer_id'";
928 int newLayerId = sqlQueryInt( db, sql, -1 );
931 sql = QString(
"INSERT INTO 'log_layer_ids' VALUES (%1, '%2')" ).arg( newLayerId ).arg( qgisLayerId );
936 sql = QString(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'layer_id'" ).arg( newLayerId + 1 );
939 layerId = newLayerId;
945 int QgsOfflineEditing::getCommitNo(
sqlite3* db )
947 QString sql =
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'commit_no'";
948 return sqlQueryInt( db, sql, -1 );
951 void QgsOfflineEditing::increaseCommitNo(
sqlite3* db )
953 QString sql = QString(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'commit_no'" ).arg( getCommitNo( db ) + 1 );
957 void QgsOfflineEditing::addFidLookup(
sqlite3* db,
int layerId, QgsFeatureId offlineFid, QgsFeatureId remoteFid )
959 QString sql = QString(
"INSERT INTO 'log_fids' VALUES ( %1, %2, %3 )" ).arg( layerId ).arg( offlineFid ).arg( remoteFid );
963 QgsFeatureId QgsOfflineEditing::remoteFid(
sqlite3* db,
int layerId, QgsFeatureId offlineFid )
965 QString sql = QString(
"SELECT \"remote_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"offline_fid\" = %2" ).arg( layerId ).arg( offlineFid );
966 return sqlQueryInt( db, sql, -1 );
969 QgsFeatureId QgsOfflineEditing::offlineFid(
sqlite3* db,
int layerId, QgsFeatureId remoteFid )
971 QString sql = QString(
"SELECT \"offline_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"remote_fid\" = %2" ).arg( layerId ).arg( remoteFid );
972 return sqlQueryInt( db, sql, -1 );
975 bool QgsOfflineEditing::isAddedFeature(
sqlite3* db,
int layerId, QgsFeatureId fid )
977 QString sql = QString(
"SELECT COUNT(\"fid\") FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( fid );
978 return ( sqlQueryInt( db, sql, 0 ) > 0 );
981 int QgsOfflineEditing::sqlExec(
sqlite3* db,
const QString& sql )
984 int rc = sqlite3_exec( db, sql.toUtf8(), NULL, NULL, &errmsg );
985 if ( rc != SQLITE_OK )
987 showWarning( errmsg );
992 int QgsOfflineEditing::sqlQueryInt(
sqlite3* db,
const QString& sql,
int defaultValue )
994 sqlite3_stmt* stmt = NULL;
995 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
997 showWarning( sqlite3_errmsg( db ) );
1001 int value = defaultValue;
1002 int ret = sqlite3_step( stmt );
1003 if ( ret == SQLITE_ROW )
1005 value = sqlite3_column_int( stmt, 0 );
1007 sqlite3_finalize( stmt );
1012 QList<int> QgsOfflineEditing::sqlQueryInts(
sqlite3* db,
const QString& sql )
1016 sqlite3_stmt* stmt = NULL;
1017 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1019 showWarning( sqlite3_errmsg( db ) );
1023 int ret = sqlite3_step( stmt );
1024 while ( ret == SQLITE_ROW )
1026 values << sqlite3_column_int( stmt, 0 );
1028 ret = sqlite3_step( stmt );
1030 sqlite3_finalize( stmt );
1035 QList<QgsField> QgsOfflineEditing::sqlQueryAttributesAdded(
sqlite3* db,
const QString& sql )
1037 QList<QgsField> values;
1039 sqlite3_stmt* stmt = NULL;
1040 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1042 showWarning( sqlite3_errmsg( db ) );
1046 int ret = sqlite3_step( stmt );
1047 while ( ret == SQLITE_ROW )
1049 QgsField field( QString((
const char* )sqlite3_column_text( stmt, 0 ) ),
1050 ( QVariant::Type )sqlite3_column_int( stmt, 1 ),
1052 sqlite3_column_int( stmt, 2 ),
1053 sqlite3_column_int( stmt, 3 ),
1054 QString((
const char* )sqlite3_column_text( stmt, 4 ) ) );
1057 ret = sqlite3_step( stmt );
1059 sqlite3_finalize( stmt );
1068 sqlite3_stmt* stmt = NULL;
1069 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1071 showWarning( sqlite3_errmsg( db ) );
1075 int ret = sqlite3_step( stmt );
1076 while ( ret == SQLITE_ROW )
1078 values << sqlite3_column_int( stmt, 0 );
1080 ret = sqlite3_step( stmt );
1082 sqlite3_finalize( stmt );
1087 QgsOfflineEditing::AttributeValueChanges QgsOfflineEditing::sqlQueryAttributeValueChanges(
sqlite3* db,
const QString& sql )
1089 AttributeValueChanges values;
1091 sqlite3_stmt* stmt = NULL;
1092 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1094 showWarning( sqlite3_errmsg( db ) );
1098 int ret = sqlite3_step( stmt );
1099 while ( ret == SQLITE_ROW )
1101 AttributeValueChange change;
1102 change.fid = sqlite3_column_int( stmt, 0 );
1103 change.attr = sqlite3_column_int( stmt, 1 );
1104 change.value = QString((
const char* )sqlite3_column_text( stmt, 2 ) );
1107 ret = sqlite3_step( stmt );
1109 sqlite3_finalize( stmt );
1114 QgsOfflineEditing::GeometryChanges QgsOfflineEditing::sqlQueryGeometryChanges(
sqlite3* db,
const QString& sql )
1116 GeometryChanges values;
1118 sqlite3_stmt* stmt = NULL;
1119 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1121 showWarning( sqlite3_errmsg( db ) );
1125 int ret = sqlite3_step( stmt );
1126 while ( ret == SQLITE_ROW )
1128 GeometryChange change;
1129 change.fid = sqlite3_column_int( stmt, 0 );
1130 change.geom_wkt = QString((
const char* )sqlite3_column_text( stmt, 1 ) );
1133 ret = sqlite3_step( stmt );
1135 sqlite3_finalize( stmt );
1140 void QgsOfflineEditing::committedAttributesAdded(
const QString& qgisLayerId,
const QList<QgsField>& addedAttributes )
1142 sqlite3* db = openLoggingDb();
1149 int layerId = getOrCreateLayerId( db, qgisLayerId );
1150 int commitNo = getCommitNo( db );
1152 for ( QList<QgsField>::const_iterator it = addedAttributes.begin(); it != addedAttributes.end(); ++it )
1155 QString sql = QString(
"INSERT INTO 'log_added_attrs' VALUES ( %1, %2, '%3', %4, %5, %6, '%7' )" )
1158 .arg( field.
name() )
1159 .arg( field.
type() )
1166 increaseCommitNo( db );
1167 sqlite3_close( db );
1170 void QgsOfflineEditing::committedFeaturesAdded(
const QString& qgisLayerId,
const QgsFeatureList& addedFeatures )
1172 sqlite3* db = openLoggingDb();
1179 int layerId = getOrCreateLayerId( db, qgisLayerId );
1186 QString sql = QString(
"SELECT ROWID FROM '%1' ORDER BY ROWID DESC LIMIT %2" ).arg( uri.
table() ).arg( addedFeatures.size() );
1187 QList<int> newFeatureIds = sqlQueryInts( db, sql );
1188 for (
int i = newFeatureIds.size() - 1; i >= 0; i-- )
1190 QString sql = QString(
"INSERT INTO 'log_added_features' VALUES ( %1, %2 )" )
1192 .arg( newFeatureIds.at( i ) );
1196 sqlite3_close( db );
1199 void QgsOfflineEditing::committedFeaturesRemoved(
const QString& qgisLayerId,
const QgsFeatureIds& deletedFeatureIds )
1201 sqlite3* db = openLoggingDb();
1208 int layerId = getOrCreateLayerId( db, qgisLayerId );
1210 for ( QgsFeatureIds::const_iterator it = deletedFeatureIds.begin(); it != deletedFeatureIds.end(); ++it )
1212 if ( isAddedFeature( db, layerId, *it ) )
1215 QString sql = QString(
"DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( *it );
1220 QString sql = QString(
"INSERT INTO 'log_removed_features' VALUES ( %1, %2)" )
1227 sqlite3_close( db );
1230 void QgsOfflineEditing::committedAttributeValuesChanges(
const QString& qgisLayerId,
const QgsChangedAttributesMap& changedAttrsMap )
1232 sqlite3* db = openLoggingDb();
1239 int layerId = getOrCreateLayerId( db, qgisLayerId );
1240 int commitNo = getCommitNo( db );
1242 for ( QgsChangedAttributesMap::const_iterator cit = changedAttrsMap.begin(); cit != changedAttrsMap.end(); ++cit )
1244 QgsFeatureId fid = cit.key();
1245 if ( isAddedFeature( db, layerId, fid ) )
1251 for ( QgsAttributeMap::const_iterator it = attrMap.begin(); it != attrMap.end(); ++it )
1253 QString sql = QString(
"INSERT INTO 'log_feature_updates' VALUES ( %1, %2, %3, %4, '%5' )" )
1258 .arg( it.value().toString() );
1263 increaseCommitNo( db );
1264 sqlite3_close( db );
1267 void QgsOfflineEditing::committedGeometriesChanges(
const QString& qgisLayerId,
const QgsGeometryMap& changedGeometries )
1269 sqlite3* db = openLoggingDb();
1276 int layerId = getOrCreateLayerId( db, qgisLayerId );
1277 int commitNo = getCommitNo( db );
1279 for ( QgsGeometryMap::const_iterator it = changedGeometries.begin(); it != changedGeometries.end(); ++it )
1281 QgsFeatureId fid = it.key();
1282 if ( isAddedFeature( db, layerId, fid ) )
1288 QString sql = QString(
"INSERT INTO 'log_geometry_updates' VALUES ( %1, %2, %3, '%4' )" )
1298 increaseCommitNo( db );
1299 sqlite3_close( db );
1302 void QgsOfflineEditing::startListenFeatureChanges()
1306 connect( vLayer->
editBuffer(), SIGNAL( committedAttributesAdded(
const QString&,
const QList<QgsField>& ) ),
1307 this, SLOT( committedAttributesAdded(
const QString&,
const QList<QgsField>& ) ) );
1308 connect( vLayer, SIGNAL( committedFeaturesAdded(
const QString&,
const QgsFeatureList& ) ),
1309 this, SLOT( committedFeaturesAdded(
const QString&,
const QgsFeatureList& ) ) );
1310 connect( vLayer, SIGNAL( committedFeaturesRemoved(
const QString&,
const QgsFeatureIds& ) ),
1311 this, SLOT( committedFeaturesRemoved(
const QString&,
const QgsFeatureIds& ) ) );
1315 this, SLOT( committedGeometriesChanges(
const QString&,
const QgsGeometryMap& ) ) );
1318 void QgsOfflineEditing::stopListenFeatureChanges()
1322 disconnect( vLayer->
editBuffer(), SIGNAL( committedAttributesAdded(
const QString&,
const QList<QgsField>& ) ),
1323 this, SLOT( committedAttributesAdded(
const QString&,
const QList<QgsField>& ) ) );
1324 disconnect( vLayer, SIGNAL( committedFeaturesAdded(
const QString&,
const QgsFeatureList& ) ),
1325 this, SLOT( committedFeaturesAdded(
const QString&,
const QgsFeatureList& ) ) );
1326 disconnect( vLayer, SIGNAL( committedFeaturesRemoved(
const QString&,
const QgsFeatureIds& ) ),
1327 this, SLOT( committedFeaturesRemoved(
const QString&,
const QgsFeatureIds& ) ) );
1331 this, SLOT( committedGeometriesChanges(
const QString&,
const QgsGeometryMap& ) ) );
1334 void QgsOfflineEditing::layerAdded(
QgsMapLayer* layer )
1340 connect( vLayer, SIGNAL( editingStarted() ),
this, SLOT( startListenFeatureChanges() ) );
1341 connect( vLayer, SIGNAL( editingStopped() ),
this, SLOT( stopListenFeatureChanges() ) );