35 #include <QDomDocument> 38 #include <QMessageBox> 43 #include <spatialite.h> 50 #define CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE "isOfflineEditable" 51 #define CUSTOM_PROPERTY_REMOTE_SOURCE "remoteSource" 52 #define CUSTOM_PROPERTY_REMOTE_PROVIDER "remoteProvider" 53 #define PROJECT_ENTRY_SCOPE_OFFLINE "OfflineEditingPlugin" 54 #define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH "/OfflineDbPath" 71 if ( layerIds.isEmpty() )
75 QString dbPath = QDir( offlineDataPath ).absoluteFilePath( offlineDbFile );
76 if ( createSpatialiteDB( dbPath ) )
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();
387 QString errCause =
tr(
"Could not create a new database\n" );
388 errCause += QString::fromUtf8( sqlite3_errmsg( sqlite_handle ) );
389 sqlite3_close( sqlite_handle );
390 showWarning( errCause );
394 ret = sqlite3_exec( sqlite_handle,
"PRAGMA foreign_keys = 1", NULL, 0, &errMsg );
395 if ( ret != SQLITE_OK )
397 showWarning(
tr(
"Unable to activate FOREIGN_KEY constraints" ) );
398 sqlite3_free( errMsg );
402 initializeSpatialMetadata( sqlite_handle );
410 void QgsOfflineEditing::createLoggingTables(
sqlite3* db )
413 QString sql =
"CREATE TABLE 'log_indices' ('name' TEXT, 'last_index' INTEGER)";
416 sql =
"INSERT INTO 'log_indices' VALUES ('commit_no', 0)";
419 sql =
"INSERT INTO 'log_indices' VALUES ('layer_id', 0)";
423 sql =
"CREATE TABLE 'log_layer_ids' ('id' INTEGER, 'qgis_id' TEXT)";
427 sql =
"CREATE TABLE 'log_fids' ('layer_id' INTEGER, 'offline_fid' INTEGER, 'remote_fid' INTEGER)";
431 sql =
"CREATE TABLE 'log_added_attrs' ('layer_id' INTEGER, 'commit_no' INTEGER, ";
432 sql +=
"'name' TEXT, 'type' INTEGER, 'length' INTEGER, 'precision' INTEGER, 'comment' TEXT)";
436 sql =
"CREATE TABLE 'log_added_features' ('layer_id' INTEGER, 'fid' INTEGER)";
440 sql =
"CREATE TABLE 'log_removed_features' ('layer_id' INTEGER, 'fid' INTEGER)";
444 sql =
"CREATE TABLE 'log_feature_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'attr' INTEGER, 'value' TEXT)";
448 sql =
"CREATE TABLE 'log_geometry_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'geom_wkt' TEXT)";
463 QString tableName = layer->
id();
466 QString sql = QString(
"CREATE TABLE '%1' (" ).arg( tableName );
469 for (
int idx = 0; idx < fields.
count(); ++idx )
471 QString dataType =
"";
472 QVariant::Type type = fields[idx].type();
473 if ( type == QVariant::Int || type == QVariant::LongLong )
475 dataType =
"INTEGER";
477 else if ( type == QVariant::Double )
481 else if ( type == QVariant::String )
487 showWarning(
tr(
"%1: Unknown data type %2. Not using type affinity for the field." ).arg( fields[idx].name() ).arg( QVariant::typeToName( type ) ) );
490 sql += delim + QString(
"'%1' %2" ).arg( fields[idx].name() ).arg( dataType );
495 int rc = sqlExec( db, sql );
500 QString geomType =
"";
507 geomType =
"MULTIPOINT";
510 geomType =
"LINESTRING";
513 geomType =
"MULTILINESTRING";
516 geomType =
"POLYGON";
519 geomType =
"MULTIPOLYGON";
522 showWarning(
tr(
"QGIS wkbType %1 not supported" ).arg( layer->
wkbType() ) );
525 QString sqlAddGeom = QString(
"SELECT AddGeometryColumn('%1', 'Geometry', %2, '%3', 2)" )
527 .arg( layer->
crs().
authid().startsWith(
"EPSG:", Qt::CaseInsensitive ) ? layer->
crs().
authid().mid( 5 ).toLong() : 0 )
531 QString sqlCreateIndex = QString(
"SELECT CreateSpatialIndex('%1', 'Geometry')" ).arg( tableName );
533 if ( rc == SQLITE_OK )
535 rc = sqlExec( db, sqlAddGeom );
536 if ( rc == SQLITE_OK )
538 rc = sqlExec( db, sqlCreateIndex );
543 if ( rc == SQLITE_OK )
547 .arg( offlineDbPath )
549 layer->
name() +
" (offline)",
"spatialite" );
561 QList<QgsMapLayer *>() << newLayer );
568 copySymbology( layer, newLayer );
574 if ( layerTreeLayer )
577 if ( parentTreeGroup )
579 int index = parentTreeGroup->
children().indexOf( layerTreeLayer );
582 if ( newLayerTreeLayer )
596 copySymbology( layer, newLayer );
609 int featureCount = 1;
611 QList<QgsFeatureId> remoteFeatureIds;
614 remoteFeatureIds << f.
id();
621 for (
int it = 0; it < attrs.count(); ++it )
623 newAttrs[column++] = attrs[it];
637 int layerId = getOrCreateLayerId( db, newLayer->
id() );
638 QList<QgsFeatureId> offlineFeatureIds;
643 offlineFeatureIds << f.
id();
647 sqlExec( db,
"BEGIN" );
648 int remoteCount = remoteFeatureIds.size();
649 for (
int i = 0; i < remoteCount; i++ )
651 addFidLookup( db, layerId, offlineFeatureIds.at( i ), remoteFeatureIds.at( remoteCount - ( i + 1 ) ) );
654 sqlExec( db,
"COMMIT" );
663 QStringList() << layer->
id() );
670 void QgsOfflineEditing::applyAttributesAdded(
QgsVectorLayer* remoteLayer,
sqlite3* db,
int layerId,
int commitNo )
672 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 );
673 QList<QgsField> fields = sqlQueryAttributesAdded( db, sql );
676 QList<QgsVectorDataProvider::NativeType> nativeTypes = provider->
nativeTypes();
679 QMap < QVariant::Type, QString > typeNameLookup;
680 for (
int i = 0; i < nativeTypes.size(); i++ )
688 for (
int i = 0; i < fields.size(); i++ )
692 if ( typeNameLookup.contains( field.
type() ) )
694 QString typeName = typeNameLookup[ field.
type()];
700 showWarning( QString(
"Could not add attribute '%1' of type %2" ).arg( field.
name() ).arg( field.
type() ) );
709 QString sql = QString(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
710 QList<int> newFeatureIds = sqlQueryInts( db, sql );
714 QVector<QVariant> defaultValues( remoteFlds.
count() );
715 for (
int i = 0; i < remoteFlds.
count(); ++i )
723 for (
int i = 0; i < newFeatureIds.size(); i++ )
737 for ( QgsFeatureList::iterator it = features.begin(); it != features.end(); ++it )
743 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
746 for (
int it = 0; it < attrs.count(); ++it )
748 newAttrs[ attrLookup[ it ] ] = attrs[ it ];
753 for (
int k = 0; k < newAttrs.count(); ++k )
755 if ( newAttrs[k].
isNull() && !defaultValues[k].isNull() )
756 newAttrs[k] = defaultValues[k];
767 void QgsOfflineEditing::applyFeaturesRemoved(
QgsVectorLayer* remoteLayer,
sqlite3* db,
int layerId )
769 QString sql = QString(
"SELECT \"fid\" FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
775 for ( QgsFeatureIds::const_iterator it = values.begin(); it != values.end(); ++it )
786 QString sql = QString(
"SELECT \"fid\", \"attr\", \"value\" FROM 'log_feature_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2 " ).arg( layerId ).arg( commitNo );
787 AttributeValueChanges values = sqlQueryAttributeValueChanges( db, sql );
791 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
793 for (
int i = 0; i < values.size(); i++ )
795 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
797 remoteLayer->
changeAttributeValue( fid, attrLookup[ values.at( i ).attr ], values.at( i ).value );
803 void QgsOfflineEditing::applyGeometryChanges(
QgsVectorLayer* remoteLayer,
sqlite3* db,
int layerId,
int commitNo )
805 QString sql = QString(
"SELECT \"fid\", \"geom_wkt\" FROM 'log_geometry_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2" ).arg( layerId ).arg( commitNo );
806 GeometryChanges values = sqlQueryGeometryChanges( db, sql );
810 for (
int i = 0; i < values.size(); i++ )
812 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
835 if ( offlineFid( db, layerId, f.
id() ) == -1 )
837 newRemoteFids[ f.
id()] =
true;
845 QString sql = QString(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
846 QList<int> newOfflineFids = sqlQueryInts( db, sql );
848 if ( newRemoteFids.size() != newOfflineFids.size() )
856 sqlExec( db,
"BEGIN" );
857 for ( QMap<QgsFeatureId, bool>::const_iterator it = newRemoteFids.begin(); it != newRemoteFids.end(); ++it )
859 addFidLookup( db, layerId, newOfflineFids.at( i++ ), it.key() );
861 sqlExec( db,
"COMMIT" );
871 if ( error.isEmpty() )
875 if ( !error.isEmpty() )
877 showWarning( error );
887 QMap <
int ,
int > attrLookup;
889 for (
int i = 0; i < remoteAttrs.size(); i++ )
891 attrLookup.insert( offlineAttrs.at( i ), remoteAttrs.at( i ) );
897 void QgsOfflineEditing::showWarning(
const QString& message )
899 emit
warning(
tr(
"Offline Editing Plugin" ), message );
902 sqlite3* QgsOfflineEditing::openLoggingDb()
906 if ( !dbPath.isEmpty() )
908 int rc = sqlite3_open( dbPath.toUtf8().constData(), &db );
909 if ( rc != SQLITE_OK )
911 showWarning(
tr(
"Could not open the spatialite logging database" ) );
919 int QgsOfflineEditing::getOrCreateLayerId(
sqlite3* db,
const QString& qgisLayerId )
921 QString sql = QString(
"SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
922 int layerId = sqlQueryInt( db, sql, -1 );
926 sql =
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'layer_id'";
927 int newLayerId = sqlQueryInt( db, sql, -1 );
930 sql = QString(
"INSERT INTO 'log_layer_ids' VALUES (%1, '%2')" ).arg( newLayerId ).arg( qgisLayerId );
935 sql = QString(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'layer_id'" ).arg( newLayerId + 1 );
938 layerId = newLayerId;
944 int QgsOfflineEditing::getCommitNo(
sqlite3* db )
946 QString sql =
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'commit_no'";
947 return sqlQueryInt( db, sql, -1 );
950 void QgsOfflineEditing::increaseCommitNo(
sqlite3* db )
952 QString sql = QString(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'commit_no'" ).arg( getCommitNo( db ) + 1 );
958 QString sql = QString(
"INSERT INTO 'log_fids' VALUES ( %1, %2, %3 )" ).arg( layerId ).arg( offlineFid ).arg( remoteFid );
964 QString sql = QString(
"SELECT \"remote_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"offline_fid\" = %2" ).arg( layerId ).arg( offlineFid );
965 return sqlQueryInt( db, sql, -1 );
970 QString sql = QString(
"SELECT \"offline_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"remote_fid\" = %2" ).arg( layerId ).arg( remoteFid );
971 return sqlQueryInt( db, sql, -1 );
976 QString sql = QString(
"SELECT COUNT(\"fid\") FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( fid );
977 return ( sqlQueryInt( db, sql, 0 ) > 0 );
980 int QgsOfflineEditing::sqlExec(
sqlite3* db,
const QString& sql )
983 int rc = sqlite3_exec( db, sql.toUtf8(), NULL, NULL, &errmsg );
984 if ( rc != SQLITE_OK )
986 showWarning( errmsg );
991 int QgsOfflineEditing::sqlQueryInt(
sqlite3* db,
const QString& sql,
int defaultValue )
993 sqlite3_stmt* stmt = NULL;
994 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
996 showWarning( sqlite3_errmsg( db ) );
1000 int value = defaultValue;
1001 int ret = sqlite3_step( stmt );
1002 if ( ret == SQLITE_ROW )
1004 value = sqlite3_column_int( stmt, 0 );
1006 sqlite3_finalize( stmt );
1011 QList<int> QgsOfflineEditing::sqlQueryInts(
sqlite3* db,
const QString& sql )
1015 sqlite3_stmt* stmt = NULL;
1016 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1018 showWarning( sqlite3_errmsg( db ) );
1022 int ret = sqlite3_step( stmt );
1023 while ( ret == SQLITE_ROW )
1025 values << sqlite3_column_int( stmt, 0 );
1027 ret = sqlite3_step( stmt );
1029 sqlite3_finalize( stmt );
1034 QList<QgsField> QgsOfflineEditing::sqlQueryAttributesAdded(
sqlite3* db,
const QString& sql )
1036 QList<QgsField> values;
1038 sqlite3_stmt* stmt = NULL;
1039 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1041 showWarning( sqlite3_errmsg( db ) );
1045 int ret = sqlite3_step( stmt );
1046 while ( ret == SQLITE_ROW )
1048 QgsField field( QString((
const char* )sqlite3_column_text( stmt, 0 ) ),
1049 ( QVariant::Type )sqlite3_column_int( stmt, 1 ),
1051 sqlite3_column_int( stmt, 2 ),
1052 sqlite3_column_int( stmt, 3 ),
1053 QString((
const char* )sqlite3_column_text( stmt, 4 ) ) );
1056 ret = sqlite3_step( stmt );
1058 sqlite3_finalize( stmt );
1067 sqlite3_stmt* stmt = NULL;
1068 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1070 showWarning( sqlite3_errmsg( db ) );
1074 int ret = sqlite3_step( stmt );
1075 while ( ret == SQLITE_ROW )
1077 values << sqlite3_column_int( stmt, 0 );
1079 ret = sqlite3_step( stmt );
1081 sqlite3_finalize( stmt );
1086 QgsOfflineEditing::AttributeValueChanges QgsOfflineEditing::sqlQueryAttributeValueChanges(
sqlite3* db,
const QString& sql )
1088 AttributeValueChanges values;
1090 sqlite3_stmt* stmt = NULL;
1091 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1093 showWarning( sqlite3_errmsg( db ) );
1097 int ret = sqlite3_step( stmt );
1098 while ( ret == SQLITE_ROW )
1100 AttributeValueChange change;
1101 change.fid = sqlite3_column_int( stmt, 0 );
1102 change.attr = sqlite3_column_int( stmt, 1 );
1103 change.value = QString((
const char* )sqlite3_column_text( stmt, 2 ) );
1106 ret = sqlite3_step( stmt );
1108 sqlite3_finalize( stmt );
1113 QgsOfflineEditing::GeometryChanges QgsOfflineEditing::sqlQueryGeometryChanges(
sqlite3* db,
const QString& sql )
1115 GeometryChanges values;
1117 sqlite3_stmt* stmt = NULL;
1118 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, NULL ) != SQLITE_OK )
1120 showWarning( sqlite3_errmsg( db ) );
1124 int ret = sqlite3_step( stmt );
1125 while ( ret == SQLITE_ROW )
1127 GeometryChange change;
1128 change.fid = sqlite3_column_int( stmt, 0 );
1129 change.geom_wkt = QString((
const char* )sqlite3_column_text( stmt, 1 ) );
1132 ret = sqlite3_step( stmt );
1134 sqlite3_finalize( stmt );
1139 void QgsOfflineEditing::committedAttributesAdded(
const QString& qgisLayerId,
const QList<QgsField>& addedAttributes )
1141 sqlite3* db = openLoggingDb();
1148 int layerId = getOrCreateLayerId( db, qgisLayerId );
1149 int commitNo = getCommitNo( db );
1151 for ( QList<QgsField>::const_iterator it = addedAttributes.begin(); it != addedAttributes.end(); ++it )
1154 QString sql = QString(
"INSERT INTO 'log_added_attrs' VALUES ( %1, %2, '%3', %4, %5, %6, '%7' )" )
1157 .arg( field.
name() )
1158 .arg( field.
type() )
1165 increaseCommitNo( db );
1166 sqlite3_close( db );
1169 void QgsOfflineEditing::committedFeaturesAdded(
const QString& qgisLayerId,
const QgsFeatureList& addedFeatures )
1171 sqlite3* db = openLoggingDb();
1178 int layerId = getOrCreateLayerId( db, qgisLayerId );
1185 QString sql = QString(
"SELECT ROWID FROM '%1' ORDER BY ROWID DESC LIMIT %2" ).arg( uri.
table() ).arg( addedFeatures.size() );
1186 QList<int> newFeatureIds = sqlQueryInts( db, sql );
1187 for (
int i = newFeatureIds.size() - 1; i >= 0; i-- )
1189 QString sql = QString(
"INSERT INTO 'log_added_features' VALUES ( %1, %2 )" )
1191 .arg( newFeatureIds.at( i ) );
1195 sqlite3_close( db );
1198 void QgsOfflineEditing::committedFeaturesRemoved(
const QString& qgisLayerId,
const QgsFeatureIds& deletedFeatureIds )
1200 sqlite3* db = openLoggingDb();
1207 int layerId = getOrCreateLayerId( db, qgisLayerId );
1209 for ( QgsFeatureIds::const_iterator it = deletedFeatureIds.begin(); it != deletedFeatureIds.end(); ++it )
1211 if ( isAddedFeature( db, layerId, *it ) )
1214 QString sql = QString(
"DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( *it );
1219 QString sql = QString(
"INSERT INTO 'log_removed_features' VALUES ( %1, %2)" )
1226 sqlite3_close( db );
1229 void QgsOfflineEditing::committedAttributeValuesChanges(
const QString& qgisLayerId,
const QgsChangedAttributesMap& changedAttrsMap )
1231 sqlite3* db = openLoggingDb();
1238 int layerId = getOrCreateLayerId( db, qgisLayerId );
1239 int commitNo = getCommitNo( db );
1241 for ( QgsChangedAttributesMap::const_iterator cit = changedAttrsMap.begin(); cit != changedAttrsMap.end(); ++cit )
1244 if ( isAddedFeature( db, layerId, fid ) )
1250 for ( QgsAttributeMap::const_iterator it = attrMap.begin(); it != attrMap.end(); ++it )
1252 QString sql = QString(
"INSERT INTO 'log_feature_updates' VALUES ( %1, %2, %3, %4, '%5' )" )
1257 .arg( it.value().toString() );
1262 increaseCommitNo( db );
1263 sqlite3_close( db );
1266 void QgsOfflineEditing::committedGeometriesChanges(
const QString& qgisLayerId,
const QgsGeometryMap& changedGeometries )
1268 sqlite3* db = openLoggingDb();
1275 int layerId = getOrCreateLayerId( db, qgisLayerId );
1276 int commitNo = getCommitNo( db );
1278 for ( QgsGeometryMap::const_iterator it = changedGeometries.begin(); it != changedGeometries.end(); ++it )
1281 if ( isAddedFeature( db, layerId, fid ) )
1287 QString sql = QString(
"INSERT INTO 'log_geometry_updates' VALUES ( %1, %2, %3, '%4' )" )
1297 increaseCommitNo( db );
1298 sqlite3_close( db );
1301 void QgsOfflineEditing::startListenFeatureChanges()
1305 connect( vLayer->
editBuffer(), SIGNAL( committedAttributesAdded(
const QString&,
const QList<QgsField>& ) ),
1306 this, SLOT( committedAttributesAdded(
const QString&,
const QList<QgsField>& ) ) );
1307 connect( vLayer, SIGNAL( committedFeaturesAdded(
const QString&,
const QgsFeatureList& ) ),
1308 this, SLOT( committedFeaturesAdded(
const QString&,
const QgsFeatureList& ) ) );
1309 connect( vLayer, SIGNAL( committedFeaturesRemoved(
const QString&,
const QgsFeatureIds& ) ),
1310 this, SLOT( committedFeaturesRemoved(
const QString&,
const QgsFeatureIds& ) ) );
1314 this, SLOT( committedGeometriesChanges(
const QString&,
const QgsGeometryMap& ) ) );
1317 void QgsOfflineEditing::stopListenFeatureChanges()
1321 disconnect( vLayer->
editBuffer(), SIGNAL( committedAttributesAdded(
const QString&,
const QList<QgsField>& ) ),
1322 this, SLOT( committedAttributesAdded(
const QString&,
const QList<QgsField>& ) ) );
1323 disconnect( vLayer, SIGNAL( committedFeaturesAdded(
const QString&,
const QgsFeatureList& ) ),
1324 this, SLOT( committedFeaturesAdded(
const QString&,
const QgsFeatureList& ) ) );
1325 disconnect( vLayer, SIGNAL( committedFeaturesRemoved(
const QString&,
const QgsFeatureIds& ) ),
1326 this, SLOT( committedFeaturesRemoved(
const QString&,
const QgsFeatureIds& ) ) );
1330 this, SLOT( committedGeometriesChanges(
const QString&,
const QgsGeometryMap& ) ) );
1333 void QgsOfflineEditing::layerAdded(
QgsMapLayer* layer )
1339 connect( vLayer, SIGNAL( editingStarted() ),
this, SLOT( startListenFeatureChanges() ) );
1340 connect( vLayer, SIGNAL( editingStopped() ),
this, SLOT( stopListenFeatureChanges() ) );
virtual QgsLayerTreeNode * clone() const override
Create a copy of the node. Returns new instance.
QgsFeatureId id() const
Get the feature id for this feature.
Layer tree group node serves as a container for layers and further groups.
const QString & name() const
Gets the name of the field.
Wrapper for iterator of features from vector data provider or vector layer.
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
void layerProgressUpdated(int layer, int numLayers)
emit a signal that the next layer of numLayers has started processing
bool addJoin(const QgsVectorJoinInfo &joinInfo)
Joins another vector layer to this layer.
Base class for all map layer types.
const QList< QgsVectorJoinInfo > vectorJoins() const
QMap< int, QVariant > QgsAttributeMap
#define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH
bool convertToOfflineProject(const QString &offlineDataPath, const QString &offlineDbFile, const QStringList &layerIds)
convert current project for offline editing
void setTypeName(const QString &typ)
Set the field type.
bool deleteFeature(QgsFeatureId fid)
delete a feature from the layer (but does not commit it)
QSet< QgsFeatureId > QgsFeatureIds
QList< QgsFeature > QgsFeatureList
#define CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
bool commitChanges()
Attempts to commit any changes to disk.
bool startEditing()
Make layer editable.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
static QgsMapLayerRegistry * instance()
#define CUSTOM_PROPERTY_REMOTE_SOURCE
int precision() const
Gets the precision of the field.
Container of fields for a vector layer.
void setAttributes(const QgsAttributes &attrs)
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group. The node will be deleted.
bool addFeature(QgsFeature &f, bool alsoUpdateExtent=true)
Adds a feature.
field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
static int sqlite3_close(sqlite3 *)
QGis::WkbType wkbType() const
Returns the WKBType or WKBUnknown in case of error.
bool isOfflineProject()
return true if current project is offline
const QString & name() const
Get the display name of the layer.
static int sqlite3_open(const char *filename, sqlite3 **ppDb)
bool writeEntry(const QString &scope, const QString &key, bool value)
void progressUpdated(int progress)
emit a signal with the progress of the current mode
QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on...
void progressStopped()
emit a signal that processing of all layers has finished
const QString & source() const
Returns the source for the layer.
QList< QgsMapLayer * > addMapLayers(QList< QgsMapLayer * > theMapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
static int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs)
QgsLayerTreeNode * parent()
Get pointer to the parent. If parent is a null pointer, the node is a root node.
int fieldOriginIndex(int fieldIdx) const
Get field's origin index (its meaning is specific to each type of origin)
const QList< NativeType > & nativeTypes() const
Returns the names of the supported types.
virtual long featureCount() const =0
Number of features in the layer.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< int > QgsAttributeList
Q_DECL_DEPRECATED bool changeAttributeValue(QgsFeatureId fid, int field, QVariant value, bool emitSignal)
Changes an attribute value (but does not commit it)
const QgsAttributes & attributes() const
This class is a base class for nodes in a layer tree.
QString id() const
Get this layer's unique ID, this ID is used to access this layer from map layer registry.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())=0
Query the provider for features specified in request.
bool removeEntry(const QString &scope, const QString &key)
remove the given key
int count() const
Return number of items.
bool changeGeometry(QgsFeatureId fid, QgsGeometry *geom)
change feature's geometry
Encapsulate a field in an attribute table or data source.
virtual bool importNamedStyle(QDomDocument &doc, QString &errorMsg)
Import the properties of this layer from a QDomDocument.
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
#define PROJECT_ENTRY_SCOPE_OFFLINE
const QStringList & commitErrors()
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
virtual void reload() override
Synchronises with changes in the datasource.
virtual void exportNamedStyle(QDomDocument &doc, QString &errorMsg)
Export the properties of this layer as named style in a QDomDocument.
QString providerType() const
Return the provider type for this layer.
bool hasGeometryType() const
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
virtual long featureCount() const
Number of features in the layer.
void warning(const QString &title, const QString &message)
Emitted when a warning needs to be displayed.
virtual const QgsFields & fields() const =0
Return a map of indexes with field names for this layer.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=0) const
QgsAttributeList pendingAllAttributesList()
returns list of attributes
virtual QVariant defaultValue(int fieldId)
Returns the default value for field specified by fieldId.
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position. The node must not have a parent yet. The node will be own...
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
void removeMapLayers(QStringList theLayerIds)
Remove a set of layers from the registry.
void title(const QString &title)
Every project has an associated title string.
void synchronize()
synchronize to remote layers
virtual bool setSubsetString(QString subset)
Set the string (typically sql) used to define a subset of the layer.
static QgsProject * instance()
access to canonical QgsProject instance
int length() const
Gets the length of the field.
bool hasLabelsEnabled() const
Label is on.
FieldOrigin fieldOrigin(int fieldIdx) const
Get field's origin (value from an enumeration)
const QMap< QString, QgsMapLayer * > & mapLayers()
Retrieve the mapLayers collection (mainly intended for use by projection)
QList< QgsVectorJoinInfo > QgsVectorJoinList
const QString & comment() const
Returns the field comment.
void progressModeSet(QgsOfflineEditing::ProgressMode mode, int maximum)
emit a signal that sets the mode for the progress of the current operation
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
static QgsGeometry * fromWkt(QString wkt)
static method that creates geometry from Wkt
const QgsCoordinateReferenceSystem & crs() const
Returns layer's spatial reference system.
QgsMapLayer * mapLayer(QString theLayerId)
Retrieve a pointer to a loaded layer by id.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
QgsVectorDataProvider * dataProvider()
Returns the data provider.
bool nextFeature(QgsFeature &f)
This is the base class for vector data providers.
QgsLayerTreeGroup * layerTreeRoot() const
Return pointer to the root (invisible) node of the project's layer tree.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Represents a vector layer which manages a vector based data sets.
bool addAttribute(const QgsField &field)
add an attribute field (but does not commit it) returns true if the field was added ...
#define CUSTOM_PROPERTY_REMOTE_PROVIDER
QString joinLayerId
Source layer.
void progressStarted()
emit a signal that processing has started
QString exportToWkt(const int &precision=17) const
Exports the geometry to WKT.
bool isNull(const QVariant &v)
Layer tree node points to a map layer.
QVariant::Type type() const
Gets variant type of the field as it will be retrieved from data source.