47 #include <QDomDocument>
50 #include <QRegularExpression>
52 #include <ogr_srs_api.h>
59 #ifdef HAVE_SPATIALITE
62 #include <spatialite.h>
66 #define CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE "isOfflineEditable"
67 #define CUSTOM_PROPERTY_REMOTE_SOURCE "remoteSource"
68 #define CUSTOM_PROPERTY_REMOTE_PROVIDER "remoteProvider"
69 #define CUSTOM_SHOW_FEATURE_COUNT "showFeatureCount"
70 #define CUSTOM_PROPERTY_ORIGINAL_LAYERID "remoteLayerId"
71 #define CUSTOM_PROPERTY_LAYERNAME_SUFFIX "layerNameSuffix"
72 #define PROJECT_ENTRY_SCOPE_OFFLINE "OfflineEditingPlugin"
73 #define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH "/OfflineDbPath"
98 if ( layerIds.isEmpty() )
103 QString dbPath = QDir( offlineDataPath ).absoluteFilePath( offlineDbFile );
104 if ( createOfflineDb( dbPath, containerType ) )
107 int rc = database.
open( dbPath );
108 if ( rc != SQLITE_OK )
110 showWarning( tr(
"Could not open the SpatiaLite database" ) );
115 createLoggingTables( database.get() );
119 QMap<QString, QgsVectorJoinList > joinInfoBuffer;
120 QMap<QString, QgsVectorLayer *> layerIdMapping;
122 for (
const QString &layerId : layerIds )
128 QgsDebugMsgLevel( QStringLiteral(
"Layer %1 is invalid" ).arg( layerId ), 4 );
137 QgsVectorJoinList::iterator joinIt = joins.begin();
138 while ( joinIt != joins.end() )
140 if ( joinIt->prefix().isNull() )
145 joinIt->setPrefix( vl->
name() +
'_' );
149 joinInfoBuffer.insert( vl->
id(), joins );
155 for (
int i = 0; i < layerIds.count(); i++ )
163 QString origLayerId = vl->
id();
164 QgsVectorLayer *newLayer = copyVectorLayer( vl, database.get(), dbPath, onlySelected, containerType, layerNameSuffix );
165 if ( newLayer && newLayer->
isValid() )
167 layerIdMapping.insert( origLayerId, newLayer );
170 snappingConfig.
removeLayers( QList<QgsMapLayer *>() << vl );
174 QStringList() << origLayerId );
182 QMap<QString, QgsVectorJoinList >::ConstIterator it;
183 for ( it = joinInfoBuffer.constBegin(); it != joinInfoBuffer.constEnd(); ++it )
187 if ( newLayer && newLayer->
isValid() )
189 const QList<QgsVectorLayerJoinInfo> joins = it.value();
192 QgsVectorLayer *newJoinedLayer = layerIdMapping.value( join.joinLayerId() );
193 if ( newJoinedLayer && newJoinedLayer->
isValid() )
196 join.setJoinLayer( newJoinedLayer );
207 if ( projectTitle.isEmpty() )
211 projectTitle += QLatin1String(
" (offline)" );
242 QList<QgsMapLayer *> offlineLayers;
244 for ( QMap<QString, QgsMapLayer *>::iterator layer_it = mapLayers.begin() ; layer_it != mapLayers.end(); ++layer_it )
252 QgsDebugMsgLevel( QStringLiteral(
"Skipping offline layer %1 because it is an invalid layer" ).arg( layer->
id() ), 4 );
256 offlineLayers << layer;
260 QgsDebugMsgLevel( QStringLiteral(
"Found %1 offline layers" ).arg( offlineLayers.count() ), 4 );
261 for (
int l = 0; l < offlineLayers.count(); l++ )
269 QString remoteName = layer->
name();
271 if ( remoteName.endsWith( remoteNameSuffix ) )
272 remoteName.chop( remoteNameSuffix.size() );
278 if ( remoteLayer->
providerType().contains( QLatin1String(
"WFS" ), Qt::CaseInsensitive ) )
288 QgsVectorLayer *offlineLayer = qobject_cast<QgsVectorLayer *>( layer );
296 copySymbology( offlineLayer, remoteLayer );
297 updateRelations( offlineLayer, remoteLayer );
298 updateMapThemes( offlineLayer, remoteLayer );
299 updateLayerOrder( offlineLayer, remoteLayer );
303 snappingConfig.
removeLayers( QList<QgsMapLayer *>() << offlineLayer );
311 QString qgisLayerId = layer->
id();
312 QString sql = QStringLiteral(
"SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
313 int layerId = sqlQueryInt( database.get(), sql, -1 );
319 int commitNo = getCommitNo( database.get() );
320 QgsDebugMsgLevel( QStringLiteral(
"Found %1 commits" ).arg( commitNo ), 4 );
321 for (
int i = 0; i < commitNo; i++ )
325 applyAttributesAdded( remoteLayer, database.get(), layerId, i );
326 applyAttributeValueChanges( offlineLayer, remoteLayer, database.get(), layerId, i );
327 applyGeometryChanges( remoteLayer, database.get(), layerId, i );
330 applyFeaturesAdded( offlineLayer, remoteLayer, database.get(), layerId );
331 applyFeaturesRemoved( remoteLayer, database.get(), layerId );
336 updateFidLookup( remoteLayer, database.get(), layerId );
339 sql = QStringLiteral(
"DELETE FROM 'log_added_attrs' WHERE \"layer_id\" = %1" ).arg( layerId );
340 sqlExec( database.get(), sql );
341 sql = QStringLiteral(
"DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
342 sqlExec( database.get(), sql );
343 sql = QStringLiteral(
"DELETE FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
344 sqlExec( database.get(), sql );
345 sql = QStringLiteral(
"DELETE FROM 'log_feature_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
346 sqlExec( database.get(), sql );
347 sql = QStringLiteral(
"DELETE FROM 'log_geometry_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
348 sqlExec( database.get(), sql );
352 showWarning( remoteLayer->
commitErrors().join( QLatin1Char(
'\n' ) ) );
357 QgsDebugMsg( QStringLiteral(
"Could not find the layer id in the edit logs!" ) );
368 projectTitle.remove( QRegExp(
" \\(offline\\)$" ) );
375 QgsDebugMsg( QStringLiteral(
"Offline layer %1 is not valid!" ).arg( offlineLayer->
id() ) );
380 QgsDebugMsg( QStringLiteral(
"Remote layer %1 is not valid!" ).arg( remoteLayer->
id() ) );
385 QString sql = QStringLiteral(
"UPDATE 'log_indices' SET 'last_index' = 0 WHERE \"name\" = 'commit_no'" );
386 sqlExec( database.get(), sql );
393 void QgsOfflineEditing::initializeSpatialMetadata(
sqlite3 *sqlite_handle )
395 #ifdef HAVE_SPATIALITE
397 if ( !sqlite_handle )
400 char **results =
nullptr;
402 int ret = sqlite3_get_table( sqlite_handle,
"select count(*) from sqlite_master", &results, &rows, &columns,
nullptr );
403 if ( ret != SQLITE_OK )
408 for (
int i = 1; i <= rows; i++ )
409 count = atoi( results[( i * columns ) + 0] );
412 sqlite3_free_table( results );
417 bool above41 =
false;
418 ret = sqlite3_get_table( sqlite_handle,
"select spatialite_version()", &results, &rows, &columns,
nullptr );
419 if ( ret == SQLITE_OK && rows == 1 && columns == 1 )
421 QString version = QString::fromUtf8( results[1] );
422 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
423 QStringList parts = version.split(
' ', QString::SkipEmptyParts );
425 QStringList parts = version.split(
' ', Qt::SkipEmptyParts );
427 if ( !parts.empty() )
429 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
430 QStringList verparts = parts.at( 0 ).split(
'.', QString::SkipEmptyParts );
432 QStringList verparts = parts.at( 0 ).split(
'.', Qt::SkipEmptyParts );
434 above41 = verparts.size() >= 2 && ( verparts.at( 0 ).toInt() > 4 || ( verparts.at( 0 ).toInt() == 4 && verparts.at( 1 ).toInt() >= 1 ) );
438 sqlite3_free_table( results );
441 char *errMsg =
nullptr;
442 ret = sqlite3_exec( sqlite_handle, above41 ?
"SELECT InitSpatialMetadata(1)" :
"SELECT InitSpatialMetadata()",
nullptr,
nullptr, &errMsg );
444 if ( ret != SQLITE_OK )
446 QString errCause = tr(
"Unable to initialize SpatialMetadata:\n" );
447 errCause += QString::fromUtf8( errMsg );
448 showWarning( errCause );
449 sqlite3_free( errMsg );
452 spatial_ref_sys_init( sqlite_handle, 0 );
454 ( void )sqlite_handle;
458 bool QgsOfflineEditing::createOfflineDb(
const QString &offlineDbPath, ContainerType containerType )
461 char *errMsg =
nullptr;
462 QFile newDb( offlineDbPath );
463 if ( newDb.exists() )
465 QFile::remove( offlineDbPath );
470 QFileInfo fullPath = QFileInfo( offlineDbPath );
471 QDir path = fullPath.dir();
474 QDir().mkpath( path.absolutePath() );
477 QString dbPath = newDb.fileName();
480 switch ( containerType )
484 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
487 showWarning( tr(
"Creation of database failed. GeoPackage driver not found." ) );
494 showWarning( tr(
"Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
506 ret = database.
open_v2( dbPath, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
nullptr );
510 QString errCause = tr(
"Could not create a new database\n" );
512 showWarning( errCause );
516 ret = sqlite3_exec( database.get(),
"PRAGMA foreign_keys = 1",
nullptr,
nullptr, &errMsg );
517 if ( ret != SQLITE_OK )
519 showWarning( tr(
"Unable to activate FOREIGN_KEY constraints" ) );
520 sqlite3_free( errMsg );
523 initializeSpatialMetadata( database.get() );
527 void QgsOfflineEditing::createLoggingTables(
sqlite3 *db )
530 QString sql = QStringLiteral(
"CREATE TABLE 'log_indices' ('name' TEXT, 'last_index' INTEGER)" );
533 sql = QStringLiteral(
"INSERT INTO 'log_indices' VALUES ('commit_no', 0)" );
536 sql = QStringLiteral(
"INSERT INTO 'log_indices' VALUES ('layer_id', 0)" );
540 sql = QStringLiteral(
"CREATE TABLE 'log_layer_ids' ('id' INTEGER, 'qgis_id' TEXT)" );
544 sql = QStringLiteral(
"CREATE TABLE 'log_fids' ('layer_id' INTEGER, 'offline_fid' INTEGER, 'remote_fid' INTEGER)" );
548 sql = QStringLiteral(
"CREATE TABLE 'log_added_attrs' ('layer_id' INTEGER, 'commit_no' INTEGER, " );
549 sql += QLatin1String(
"'name' TEXT, 'type' INTEGER, 'length' INTEGER, 'precision' INTEGER, 'comment' TEXT)" );
553 sql = QStringLiteral(
"CREATE TABLE 'log_added_features' ('layer_id' INTEGER, 'fid' INTEGER)" );
557 sql = QStringLiteral(
"CREATE TABLE 'log_removed_features' ('layer_id' INTEGER, 'fid' INTEGER)" );
561 sql = QStringLiteral(
"CREATE TABLE 'log_feature_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'attr' INTEGER, 'value' TEXT)" );
565 sql = QStringLiteral(
"CREATE TABLE 'log_geometry_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'geom_wkt' TEXT)" );
573 QgsVectorLayer *QgsOfflineEditing::copyVectorLayer(
QgsVectorLayer *layer,
sqlite3 *db,
const QString &offlineDbPath,
bool onlySelected, ContainerType containerType,
const QString &layerNameSuffix )
575 if ( !layer || !layer->
isValid() )
577 QgsDebugMsgLevel( QStringLiteral(
"Layer %1 is invalid and cannot be copied" ).arg( layer ? layer->
id() : QStringLiteral(
"<UNKNOWN>" ) ), 4 );
581 QString tableName = layer->
id();
582 QgsDebugMsgLevel( QStringLiteral(
"Creating offline table %1 ..." ).arg( tableName ), 4 );
587 switch ( containerType )
591 #ifdef HAVE_SPATIALITE
593 QString sql = QStringLiteral(
"CREATE TABLE '%1' (" ).arg( tableName );
596 for (
const auto &
field : providerFields )
600 if ( type == QVariant::Int || type == QVariant::LongLong )
602 dataType = QStringLiteral(
"INTEGER" );
604 else if ( type == QVariant::Double )
606 dataType = QStringLiteral(
"REAL" );
608 else if ( type == QVariant::String )
610 dataType = QStringLiteral(
"TEXT" );
612 else if ( type == QVariant::StringList || type == QVariant::List )
614 dataType = QStringLiteral(
"TEXT" );
615 showWarning( tr(
"Field '%1' from layer %2 has been converted from a list to a string of comma-separated values." ).arg(
field.
name(), layer->
name() ) );
619 showWarning( tr(
"%1: Unknown data type %2. Not using type affinity for the field." ).arg(
field.
name(), QVariant::typeToName( type ) ) );
622 sql += delim + QStringLiteral(
"'%1' %2" ).arg(
field.
name(), dataType );
627 int rc = sqlExec( db, sql );
638 geomType = QStringLiteral(
"POINT" );
641 geomType = QStringLiteral(
"MULTIPOINT" );
644 geomType = QStringLiteral(
"LINESTRING" );
647 geomType = QStringLiteral(
"MULTILINESTRING" );
650 geomType = QStringLiteral(
"POLYGON" );
653 geomType = QStringLiteral(
"MULTIPOLYGON" );
660 QString zmInfo = QStringLiteral(
"XY" );
669 if ( layer->
crs().
authid().startsWith( QLatin1String(
"EPSG:" ), Qt::CaseInsensitive ) )
671 epsgCode = layer->
crs().
authid().mid( 5 );
676 showWarning( tr(
"Layer %1 has unsupported Coordinate Reference System (%2)." ).arg( layer->
name(), layer->
crs().
authid() ) );
679 QString sqlAddGeom = QStringLiteral(
"SELECT AddGeometryColumn('%1', 'Geometry', %2, '%3', '%4')" )
680 .arg( tableName, epsgCode, geomType, zmInfo );
683 QString sqlCreateIndex = QStringLiteral(
"SELECT CreateSpatialIndex('%1', 'Geometry')" ).arg( tableName );
685 if ( rc == SQLITE_OK )
687 rc = sqlExec( db, sqlAddGeom );
688 if ( rc == SQLITE_OK )
690 rc = sqlExec( db, sqlCreateIndex );
695 if ( rc != SQLITE_OK )
697 showWarning( tr(
"Filling SpatiaLite for layer %1 failed" ).arg( layer->
name() ) );
702 QString connectionString = QStringLiteral(
"dbname='%1' table='%2'%3 sql=" )
704 tableName, layer->
isSpatial() ?
"(Geometry)" :
"" );
707 layer->
name() + layerNameSuffix, QStringLiteral(
"spatialite" ), options );
711 showWarning( tr(
"No Spatialite support available" ) );
719 char **options =
nullptr;
721 options = CSLSetNameValue( options,
"OVERWRITE",
"YES" );
722 options = CSLSetNameValue( options,
"IDENTIFIER", tr(
"%1 (offline)" ).arg( layer->
id() ).toUtf8().constData() );
723 options = CSLSetNameValue( options,
"DESCRIPTION", layer->
dataComment().toUtf8().constData() );
726 QString fidBase( QStringLiteral(
"fid" ) );
727 QString fid = fidBase;
731 fid = fidBase +
'_' + QString::number( counter );
734 if ( counter == 10000 )
736 showWarning( tr(
"Cannot make FID-name for GPKG " ) );
740 options = CSLSetNameValue( options,
"FID", fid.toUtf8().constData() );
744 options = CSLSetNameValue( options,
"GEOMETRY_COLUMN",
"geom" );
745 options = CSLSetNameValue( options,
"SPATIAL_INDEX",
"YES" );
748 OGRSFDriverH hDriver =
nullptr;
751 OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS,
static_cast<OGRwkbGeometryType
>( layer->
wkbType() ), options );
752 CSLDestroy( options );
757 showWarning( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
762 for (
const auto &
field : providerFields )
765 const QVariant::Type type =
field.
type();
766 OGRFieldType ogrType( OFTString );
767 OGRFieldSubType ogrSubType = OFSTNone;
768 if ( type == QVariant::Int )
769 ogrType = OFTInteger;
770 else if ( type == QVariant::LongLong )
771 ogrType = OFTInteger64;
772 else if ( type == QVariant::Double )
774 else if ( type == QVariant::Time )
776 else if ( type == QVariant::Date )
778 else if ( type == QVariant::DateTime )
779 ogrType = OFTDateTime;
780 else if ( type == QVariant::Bool )
782 ogrType = OFTInteger;
783 ogrSubType = OFSTBoolean;
785 else if ( type == QVariant::StringList || type == QVariant::List )
788 ogrSubType = OFSTJSON;
789 showWarning( tr(
"Field '%1' from layer %2 has been converted from a list to a JSON-formatted string value." ).arg( fieldName, layer->
name() ) );
797 OGR_Fld_SetWidth( fld.get(), ogrWidth );
798 if ( ogrSubType != OFSTNone )
799 OGR_Fld_SetSubType( fld.get(), ogrSubType );
801 if ( OGR_L_CreateField( hLayer, fld.get(),
true ) != OGRERR_NONE )
803 showWarning( tr(
"Creation of field %1 failed (OGR error: %2)" )
804 .arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
812 OGR_L_ResetReading( hLayer );
813 if ( CPLGetLastErrorType() != CE_None )
815 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
821 QString uri = QStringLiteral(
"%1|layername=%2" ).arg( offlineDbPath, tableName );
823 newLayer =
new QgsVectorLayer( uri, layer->
name() + layerNameSuffix, QStringLiteral(
"ogr" ), layerOptions );
828 if ( newLayer && newLayer->
isValid() )
840 if ( !selectedFids.isEmpty() )
854 long long featureCount = 1;
856 QList<QgsFeatureId> remoteFeatureIds;
859 remoteFeatureIds << f.
id();
866 QgsAttributes newAttrs( containerType ==
GPKG ? attrs.count() + 1 : attrs.count() );
867 for (
int it = 0; it < attrs.count(); ++it )
869 QVariant attr = attrs.at( it );
874 newAttrs[column++] = attr;
888 int layerId = getOrCreateLayerId( db, newLayer->
id() );
889 QList<QgsFeatureId> offlineFeatureIds;
894 offlineFeatureIds << f.
id();
898 sqlExec( db, QStringLiteral(
"BEGIN" ) );
899 int remoteCount = remoteFeatureIds.size();
900 for (
int i = 0; i < remoteCount; i++ )
903 if ( i < offlineFeatureIds.count() )
905 addFidLookup( db, layerId, offlineFeatureIds.at( i ), remoteFeatureIds.at( i ) );
909 showWarning( tr(
"Feature cannot be copied to the offline layer, please check if the online layer '%1' is still accessible." ).arg( layer->
name() ) );
914 sqlExec( db, QStringLiteral(
"COMMIT" ) );
918 showWarning( newLayer->
commitErrors().join( QLatin1Char(
'\n' ) ) );
935 QList<QgsMapLayer *>() << newLayer );
938 copySymbology( layer, newLayer );
941 const auto fields = layer->
fields();
953 if ( layerTreeLayer )
956 if ( parentTreeGroup )
958 int index = parentTreeGroup->
children().indexOf( layerTreeLayer );
961 if ( newLayerTreeLayer )
975 updateRelations( layer, newLayer );
976 updateMapThemes( layer, newLayer );
977 updateLayerOrder( layer, newLayer );
985 void QgsOfflineEditing::applyAttributesAdded(
QgsVectorLayer *remoteLayer,
sqlite3 *db,
int layerId,
int commitNo )
987 Q_ASSERT( remoteLayer );
989 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 );
990 QList<QgsField> fields = sqlQueryAttributesAdded( db, sql );
993 QList<QgsVectorDataProvider::NativeType> nativeTypes = provider->
nativeTypes();
996 QMap < QVariant::Type, QString > typeNameLookup;
997 for (
int i = 0; i < nativeTypes.size(); i++ )
1005 for (
int i = 0; i < fields.size(); i++ )
1009 if ( typeNameLookup.contains(
field.
type() ) )
1017 showWarning( QStringLiteral(
"Could not add attribute '%1' of type %2" ).arg(
field.
name() ).arg(
field.
type() ) );
1026 Q_ASSERT( offlineLayer );
1027 Q_ASSERT( remoteLayer );
1029 QString sql = QStringLiteral(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
1030 const QList<int> featureIdInts = sqlQueryInts( db, sql );
1032 for (
int id : featureIdInts )
1034 newFeatureIds << id;
1045 features << feature;
1052 int newAttrsCount = remoteLayer->
fields().
count();
1053 for ( QgsFeatureList::iterator it = features.begin(); it != features.end(); ++it )
1057 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
1060 for (
int it = 0; it < attrs.count(); ++it )
1062 int remoteAttributeIndex = attrLookup[ it ];
1063 QVariant attr = attrs.at( it );
1064 if ( remoteLayer->
fields().
at( remoteAttributeIndex ).
type() == QVariant::StringList )
1066 if ( attr.type() == QVariant::StringList || attr.type() == QVariant::List )
1068 attr = attr.toStringList();
1075 else if ( remoteLayer->
fields().
at( remoteAttributeIndex ).
type() == QVariant::List )
1077 if ( attr.type() == QVariant::StringList || attr.type() == QVariant::List )
1079 attr = attr.toList();
1086 newAttrs[ remoteAttributeIndex ] = attr;
1097 void QgsOfflineEditing::applyFeaturesRemoved(
QgsVectorLayer *remoteLayer,
sqlite3 *db,
int layerId )
1099 Q_ASSERT( remoteLayer );
1101 QString sql = QStringLiteral(
"SELECT \"fid\" FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
1107 for ( QgsFeatureIds::const_iterator it = values.constBegin(); it != values.constEnd(); ++it )
1118 Q_ASSERT( offlineLayer );
1119 Q_ASSERT( remoteLayer );
1121 QString sql = QStringLiteral(
"SELECT \"fid\", \"attr\", \"value\" FROM 'log_feature_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2 " ).arg( layerId ).arg( commitNo );
1122 AttributeValueChanges values = sqlQueryAttributeValueChanges( db, sql );
1126 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
1128 for (
int i = 0; i < values.size(); i++ )
1130 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
1131 QgsDebugMsgLevel( QStringLiteral(
"Offline changeAttributeValue %1 = %2" ).arg( QString( attrLookup[ values.at( i ).attr ] ), values.at( i ).value ), 4 );
1133 int remoteAttributeIndex = attrLookup[ values.at( i ).attr ];
1134 QVariant attr = values.at( i ).value;
1135 if ( remoteLayer->
fields().
at( remoteAttributeIndex ).
type() == QVariant::StringList )
1139 else if ( remoteLayer->
fields().
at( remoteAttributeIndex ).
type() == QVariant::List )
1150 void QgsOfflineEditing::applyGeometryChanges(
QgsVectorLayer *remoteLayer,
sqlite3 *db,
int layerId,
int commitNo )
1152 Q_ASSERT( remoteLayer );
1154 QString sql = QStringLiteral(
"SELECT \"fid\", \"geom_wkt\" FROM 'log_geometry_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2" ).arg( layerId ).arg( commitNo );
1155 GeometryChanges values = sqlQueryGeometryChanges( db, sql );
1159 for (
int i = 0; i < values.size(); i++ )
1161 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
1171 Q_ASSERT( remoteLayer );
1187 if ( offlineFid( db, layerId, f.
id() ) == -1 )
1189 newRemoteFids[ f.
id()] =
true;
1197 QString sql = QStringLiteral(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
1198 QList<int> newOfflineFids = sqlQueryInts( db, sql );
1200 if ( newRemoteFids.size() != newOfflineFids.size() )
1208 sqlExec( db, QStringLiteral(
"BEGIN" ) );
1209 for ( QMap<QgsFeatureId, bool>::const_iterator it = newRemoteFids.constBegin(); it != newRemoteFids.constEnd(); ++it )
1211 addFidLookup( db, layerId, newOfflineFids.at( i++ ), it.key() );
1213 sqlExec( db, QStringLiteral(
"COMMIT" ) );
1219 Q_ASSERT( sourceLayer );
1220 Q_ASSERT( targetLayer );
1230 if ( error.isEmpty() )
1234 if ( !error.isEmpty() )
1236 showWarning( error );
1242 Q_ASSERT( sourceLayer );
1243 Q_ASSERT( targetLayer );
1246 const QList<QgsRelation> referencedRelations = relationManager->
referencedRelations( sourceLayer );
1248 for (
QgsRelation relation : referencedRelations )
1251 relation.setReferencedLayer( targetLayer->
id() );
1255 const QList<QgsRelation> referencingRelations = relationManager->
referencingRelations( sourceLayer );
1257 for (
QgsRelation relation : referencingRelations )
1260 relation.setReferencingLayer( targetLayer->
id() );
1267 Q_ASSERT( sourceLayer );
1268 Q_ASSERT( targetLayer );
1271 const QStringList mapThemeNames = mapThemeCollection->
mapThemes();
1273 for (
const QString &mapThemeName : mapThemeNames )
1281 if ( layerRecord.layer() == sourceLayer )
1283 layerRecord.setLayer( targetLayer );
1295 Q_ASSERT( sourceLayer );
1296 Q_ASSERT( targetLayer );
1300 auto iterator = layerOrder.begin();
1302 while ( iterator != layerOrder.end() )
1304 if ( *iterator == targetLayer )
1306 iterator = layerOrder.erase( iterator );
1307 if ( iterator == layerOrder.end() )
1311 if ( *iterator == sourceLayer )
1313 *iterator = targetLayer;
1325 Q_ASSERT( offlineLayer );
1326 Q_ASSERT( remoteLayer );
1330 QMap <
int ,
int > attrLookup;
1333 for (
int i = 0; i < offlineAttrs.size(); i++ )
1342 void QgsOfflineEditing::showWarning(
const QString &message )
1344 emit
warning( tr(
"Offline Editing Plugin" ), message );
1351 if ( !dbPath.isEmpty() )
1354 int rc = database.
open( absoluteDbPath );
1355 if ( rc != SQLITE_OK )
1357 QgsDebugMsg( QStringLiteral(
"Could not open the SpatiaLite logging database" ) );
1358 showWarning( tr(
"Could not open the SpatiaLite logging database" ) );
1363 QgsDebugMsg( QStringLiteral(
"dbPath is empty!" ) );
1368 int QgsOfflineEditing::getOrCreateLayerId(
sqlite3 *db,
const QString &qgisLayerId )
1370 QString sql = QStringLiteral(
"SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
1371 int layerId = sqlQueryInt( db, sql, -1 );
1372 if ( layerId == -1 )
1375 sql = QStringLiteral(
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'layer_id'" );
1376 int newLayerId = sqlQueryInt( db, sql, -1 );
1379 sql = QStringLiteral(
"INSERT INTO 'log_layer_ids' VALUES (%1, '%2')" ).arg( newLayerId ).arg( qgisLayerId );
1384 sql = QStringLiteral(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'layer_id'" ).arg( newLayerId + 1 );
1387 layerId = newLayerId;
1393 int QgsOfflineEditing::getCommitNo(
sqlite3 *db )
1395 QString sql = QStringLiteral(
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'commit_no'" );
1396 return sqlQueryInt( db, sql, -1 );
1399 void QgsOfflineEditing::increaseCommitNo(
sqlite3 *db )
1401 QString sql = QStringLiteral(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'commit_no'" ).arg( getCommitNo( db ) + 1 );
1407 QString sql = QStringLiteral(
"INSERT INTO 'log_fids' VALUES ( %1, %2, %3 )" ).arg( layerId ).arg( offlineFid ).arg( remoteFid );
1413 QString sql = QStringLiteral(
"SELECT \"remote_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"offline_fid\" = %2" ).arg( layerId ).arg( offlineFid );
1414 return sqlQueryInt( db, sql, -1 );
1419 QString sql = QStringLiteral(
"SELECT \"offline_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"remote_fid\" = %2" ).arg( layerId ).arg( remoteFid );
1420 return sqlQueryInt( db, sql, -1 );
1425 QString sql = QStringLiteral(
"SELECT COUNT(\"fid\") FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( fid );
1426 return ( sqlQueryInt( db, sql, 0 ) > 0 );
1429 int QgsOfflineEditing::sqlExec(
sqlite3 *db,
const QString &sql )
1431 char *errmsg =
nullptr;
1432 int rc = sqlite3_exec( db, sql.toUtf8(),
nullptr,
nullptr, &errmsg );
1433 if ( rc != SQLITE_OK )
1435 showWarning( errmsg );
1440 int QgsOfflineEditing::sqlQueryInt(
sqlite3 *db,
const QString &sql,
int defaultValue )
1442 sqlite3_stmt *stmt =
nullptr;
1443 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1445 showWarning( sqlite3_errmsg( db ) );
1446 return defaultValue;
1449 int value = defaultValue;
1450 int ret = sqlite3_step( stmt );
1451 if ( ret == SQLITE_ROW )
1453 value = sqlite3_column_int( stmt, 0 );
1455 sqlite3_finalize( stmt );
1460 QList<int> QgsOfflineEditing::sqlQueryInts(
sqlite3 *db,
const QString &sql )
1464 sqlite3_stmt *stmt =
nullptr;
1465 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1467 showWarning( sqlite3_errmsg( db ) );
1471 int ret = sqlite3_step( stmt );
1472 while ( ret == SQLITE_ROW )
1474 values << sqlite3_column_int( stmt, 0 );
1476 ret = sqlite3_step( stmt );
1478 sqlite3_finalize( stmt );
1483 QList<QgsField> QgsOfflineEditing::sqlQueryAttributesAdded(
sqlite3 *db,
const QString &sql )
1485 QList<QgsField> values;
1487 sqlite3_stmt *stmt =
nullptr;
1488 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1490 showWarning( sqlite3_errmsg( db ) );
1494 int ret = sqlite3_step( stmt );
1495 while ( ret == SQLITE_ROW )
1497 QgsField field( QString(
reinterpret_cast< const char *
>( sqlite3_column_text( stmt, 0 ) ) ),
1498 static_cast< QVariant::Type
>( sqlite3_column_int( stmt, 1 ) ),
1500 sqlite3_column_int( stmt, 2 ),
1501 sqlite3_column_int( stmt, 3 ),
1502 QString(
reinterpret_cast< const char *
>( sqlite3_column_text( stmt, 4 ) ) ) );
1505 ret = sqlite3_step( stmt );
1507 sqlite3_finalize( stmt );
1516 sqlite3_stmt *stmt =
nullptr;
1517 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1519 showWarning( sqlite3_errmsg( db ) );
1523 int ret = sqlite3_step( stmt );
1524 while ( ret == SQLITE_ROW )
1526 values << sqlite3_column_int( stmt, 0 );
1528 ret = sqlite3_step( stmt );
1530 sqlite3_finalize( stmt );
1535 QgsOfflineEditing::AttributeValueChanges QgsOfflineEditing::sqlQueryAttributeValueChanges(
sqlite3 *db,
const QString &sql )
1537 AttributeValueChanges values;
1539 sqlite3_stmt *stmt =
nullptr;
1540 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1542 showWarning( sqlite3_errmsg( db ) );
1546 int ret = sqlite3_step( stmt );
1547 while ( ret == SQLITE_ROW )
1549 AttributeValueChange change;
1550 change.fid = sqlite3_column_int( stmt, 0 );
1551 change.attr = sqlite3_column_int( stmt, 1 );
1552 change.value = QString(
reinterpret_cast< const char *
>( sqlite3_column_text( stmt, 2 ) ) );
1555 ret = sqlite3_step( stmt );
1557 sqlite3_finalize( stmt );
1562 QgsOfflineEditing::GeometryChanges QgsOfflineEditing::sqlQueryGeometryChanges(
sqlite3 *db,
const QString &sql )
1564 GeometryChanges values;
1566 sqlite3_stmt *stmt =
nullptr;
1567 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1569 showWarning( sqlite3_errmsg( db ) );
1573 int ret = sqlite3_step( stmt );
1574 while ( ret == SQLITE_ROW )
1576 GeometryChange change;
1577 change.fid = sqlite3_column_int( stmt, 0 );
1578 change.geom_wkt = QString(
reinterpret_cast< const char *
>( sqlite3_column_text( stmt, 1 ) ) );
1581 ret = sqlite3_step( stmt );
1583 sqlite3_finalize( stmt );
1588 void QgsOfflineEditing::committedAttributesAdded(
const QString &qgisLayerId,
const QList<QgsField> &addedAttributes )
1595 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1596 int commitNo = getCommitNo( database.get() );
1600 QString sql = QStringLiteral(
"INSERT INTO 'log_added_attrs' VALUES ( %1, %2, '%3', %4, %5, %6, '%7' )" )
1608 sqlExec( database.get(), sql );
1611 increaseCommitNo( database.get() );
1614 void QgsOfflineEditing::committedFeaturesAdded(
const QString &qgisLayerId,
const QgsFeatureList &addedFeatures )
1621 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1625 QString dataSourceString = layer->
source();
1631 if ( !offlinePath.contains(
".gpkg" ) )
1633 tableName = uri.
table();
1638 QVariantMap decodedUri = ogrProviderMetaData->
decodeUri( dataSourceString );
1639 tableName = decodedUri.value( QStringLiteral(
"layerName" ) ).toString();
1640 if ( tableName.isEmpty() )
1642 showWarning( tr(
"Could not deduce table name from data source %1." ).arg( dataSourceString ) );
1647 QString sql = QStringLiteral(
"SELECT ROWID FROM '%1' ORDER BY ROWID DESC LIMIT %2" ).arg( tableName ).arg( addedFeatures.size() );
1648 QList<int> newFeatureIds = sqlQueryInts( database.get(), sql );
1649 for (
int i = newFeatureIds.size() - 1; i >= 0; i-- )
1651 QString sql = QStringLiteral(
"INSERT INTO 'log_added_features' VALUES ( %1, %2 )" )
1653 .arg( newFeatureIds.at( i ) );
1654 sqlExec( database.get(), sql );
1658 void QgsOfflineEditing::committedFeaturesRemoved(
const QString &qgisLayerId,
const QgsFeatureIds &deletedFeatureIds )
1665 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1669 if ( isAddedFeature( database.get(), layerId,
id ) )
1672 QString sql = QStringLiteral(
"DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg(
id );
1673 sqlExec( database.get(), sql );
1677 QString sql = QStringLiteral(
"INSERT INTO 'log_removed_features' VALUES ( %1, %2)" )
1680 sqlExec( database.get(), sql );
1685 void QgsOfflineEditing::committedAttributeValuesChanges(
const QString &qgisLayerId,
const QgsChangedAttributesMap &changedAttrsMap )
1692 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1693 int commitNo = getCommitNo( database.get() );
1695 for ( QgsChangedAttributesMap::const_iterator cit = changedAttrsMap.begin(); cit != changedAttrsMap.end(); ++cit )
1698 if ( isAddedFeature( database.get(), layerId, fid ) )
1704 for ( QgsAttributeMap::const_iterator it = attrMap.constBegin(); it != attrMap.constEnd(); ++it )
1706 QString value = it.value().type() == QVariant::StringList || it.value().type() == QVariant::List ?
QgsJsonUtils::encodeValue( it.value() ) : it.value().toString();
1707 value.replace( QLatin1String(
"'" ), QLatin1String(
"''" ) );
1708 QString sql = QStringLiteral(
"INSERT INTO 'log_feature_updates' VALUES ( %1, %2, %3, %4, '%5' )" )
1714 sqlExec( database.get(), sql );
1718 increaseCommitNo( database.get() );
1721 void QgsOfflineEditing::committedGeometriesChanges(
const QString &qgisLayerId,
const QgsGeometryMap &changedGeometries )
1728 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1729 int commitNo = getCommitNo( database.get() );
1731 for ( QgsGeometryMap::const_iterator it = changedGeometries.begin(); it != changedGeometries.end(); ++it )
1734 if ( isAddedFeature( database.get(), layerId, fid ) )
1740 QString sql = QStringLiteral(
"INSERT INTO 'log_geometry_updates' VALUES ( %1, %2, %3, '%4' )" )
1744 .arg( geom.
asWkt() );
1745 sqlExec( database.get(), sql );
1750 increaseCommitNo( database.get() );
1753 void QgsOfflineEditing::startListenFeatureChanges()
1755 QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( sender() );
1764 this, &QgsOfflineEditing::committedAttributesAdded );
1766 this, &QgsOfflineEditing::committedAttributeValuesChanges );
1768 this, &QgsOfflineEditing::committedGeometriesChanges );
1771 this, &QgsOfflineEditing::committedFeaturesAdded );
1773 this, &QgsOfflineEditing::committedFeaturesRemoved );
1776 void QgsOfflineEditing::stopListenFeatureChanges()
1778 QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( sender() );
1787 this, &QgsOfflineEditing::committedAttributesAdded );
1789 this, &QgsOfflineEditing::committedAttributeValuesChanges );
1791 this, &QgsOfflineEditing::committedGeometriesChanges );
1794 this, &QgsOfflineEditing::committedFeaturesAdded );
1796 this, &QgsOfflineEditing::committedFeaturesRemoved );
1799 void QgsOfflineEditing::layerAdded(
QgsMapLayer *layer )
1806 QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( layer );
QString authid() const
Returns the authority identifier for the CRS.
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
virtual void invalidateConnections(const QString &connection)
Invalidate connections corresponding to specified name.
Class for storing the component parts of a RDBMS data source URI (e.g.
QString table() const
Returns the table name stored in the URI.
QString database() const
Returns the database name stored in the URI.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
FilterType filterType() const
Returns the filter type which is currently set on this request.
@ FilterFids
Filter using feature IDs.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
@ ConstraintNotNull
Field may not be null.
Encapsulate a field in an attribute table or data source.
QVariant::Type subType() const
If the field is a collection, gets its element's type.
void setTypeName(const QString &typeName)
Set the field type.
Container of fields for a vector layer.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
int count() const
Returns number of items.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int fieldOriginIndex(int fieldIdx) const
Returns the field's origin index (its meaning is specific to each type of origin).
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A geometry is the spatial representation of a feature.
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
QString asWkt(int precision=17) const
Exports the geometry to WKT.
static Q_INVOKABLE QString encodeValue(const QVariant &value)
Encodes a value to a JSON string representation, adding appropriate quotations and escaping where req...
static Q_INVOKABLE QVariantList parseArray(const QString &json, QVariant::Type type=QVariant::Invalid)
Parse a simple array (depth=1)
Layer tree group node serves as a container for layers and further groups.
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position.
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
QgsLayerTreeLayer * clone() const override
Create a copy of the node. Returns new instance.
This class is a base class for nodes in a layer tree.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well)
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
void setCustomLayerOrder(const QList< QgsMapLayer * > &customLayerOrder)
The order in which layers will be rendered on the canvas.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
void copyStylesFrom(QgsMapLayerStyleManager *other)
Copies all styles from other.
Base class for all map layer types.
virtual void exportNamedStyle(QDomDocument &doc, QString &errorMsg, const QgsReadWriteContext &context=QgsReadWriteContext(), QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const
Export the properties of this layer as named style in a QDomDocument.
virtual bool importNamedStyle(QDomDocument &doc, QString &errorMsg, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Import the properties of this layer from a QDomDocument.
QString source() const
Returns the source for the layer.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
void setCustomProperties(const QgsObjectCustomProperties &properties)
Set custom properties for layer.
QgsCoordinateReferenceSystem crs
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Q_INVOKABLE void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
@ CustomProperties
Custom properties (by plugins for instance)
const QgsObjectCustomProperties & customProperties() const
Read all custom properties from layer.
Individual record of a visible layer in a map theme record.
Individual map theme record of visible layers and styles.
void removeLayerRecord(QgsMapLayer *layer)
Removes a record for layer if present.
QList< QgsMapThemeCollection::MapThemeLayerRecord > layerRecords() const
Returns a list of records for all visible layer belonging to the theme.
void addLayerRecord(const QgsMapThemeCollection::MapThemeLayerRecord &record)
Add a new record for a layer.
Container class that allows storage of map themes consisting of visible map layers and layer styles.
QgsMapThemeCollection::MapThemeRecord mapThemeState(const QString &name) const
Returns the recorded state of a map theme.
void update(const QString &name, const QgsMapThemeCollection::MapThemeRecord &state)
Updates a map theme within the collection.
void progressModeSet(QgsOfflineEditing::ProgressMode mode, long long maximum)
Emitted when the mode for the progress of the current operation is set.
void progressUpdated(long long progress)
Emitted with the progress of the current mode.
void layerProgressUpdated(int layer, int numLayers)
Emitted whenever a new layer is being processed.
bool isOfflineProject() const
Returns true if current project is offline.
bool convertToOfflineProject(const QString &offlineDataPath, const QString &offlineDbFile, const QStringList &layerIds, bool onlySelected=false, ContainerType containerType=SpatiaLite, const QString &layerNameSuffix=QStringLiteral(" (offline)"))
Convert current project for offline editing.
void warning(const QString &title, const QString &message)
Emitted when a warning needs to be displayed.
void synchronize()
Synchronize to remote layers.
void progressStopped()
Emitted when the processing of all layers has finished.
ContainerType
Type of offline database container file.
void progressStarted()
Emitted when the process has started.
QgsRelationManager * relationManager
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
QString title() const
Returns the project's title.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the registry.
QgsSnappingConfig snappingConfig
void removeMapLayers(const QStringList &layerIds)
Remove a set of layers from the registry by layer ID.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Reads a string from the specified scope and key.
QgsMapThemeCollection * mapThemeCollection
QgsCoordinateTransformContext transformContext
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
void setTitle(const QString &title)
Sets the project's title.
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean value to the project file.
QString readPath(const QString &filename) const
Transforms a filename read from the project file to an absolute path.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key from the specified scope.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
The class is used as a container of context for various read/write operations on other objects.
This class manages a set of relations between layers.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
QList< QgsRelation > referencingRelations(const QgsVectorLayer *layer=nullptr, int fieldIdx=-2) const
Gets all relations where the specified layer (and field) is the referencing part (i....
void removeRelation(const QString &id)
Remove a relation.
void addRelation(const QgsRelation &relation)
Add a relation.
This is a container for configuration of the snapping of the project.
QHash< QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings > individualLayerSettings() const
Returns individual snapping settings for all layers.
bool removeLayers(const QList< QgsMapLayer * > &layers)
Removes the specified layers from the individual layer configuration.
void setIndividualLayerSettings(QgsVectorLayer *vl, const QgsSnappingConfig::IndividualLayerSettings &individualLayerSettings)
Sets individual layer snappings settings (applied if mode is AdvancedConfiguration)
This is the base class for vector data providers.
long long featureCount() const override=0
Number of features in the layer.
QList< QgsVectorDataProvider::NativeType > nativeTypes() const
Returns the names of the supported types.
virtual QString defaultValueClause(int fieldIndex) const
Returns any default value clauses which are present at the provider for a specified field index.
QgsFields fields() const override=0
Returns the fields associated with this data provider.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override=0
Query the provider for features specified in request.
void committedAttributeValuesChanges(const QString &layerId, const QgsChangedAttributesMap &changedAttributesValues)
void committedAttributesAdded(const QString &layerId, const QList< QgsField > &addedAttributes)
void committedGeometriesChanges(const QString &layerId, const QgsGeometryMap &changedGeometries)
Defines left outer join from our vector layer to some other vector layer.
static QgsFeature createFeature(const QgsVectorLayer *layer, const QgsGeometry &geometry=QgsGeometry(), const QgsAttributeMap &attributes=QgsAttributeMap(), QgsExpressionContext *context=nullptr)
Creates a new feature ready for insertion into a layer.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
void committedFeaturesAdded(const QString &layerId, const QgsFeatureList &addedFeatures)
Emitted when features are added to the provider.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
Q_INVOKABLE bool startEditing()
Makes the layer editable.
bool addJoin(const QgsVectorLayerJoinInfo &joinInfo)
Joins another vector layer to this layer.
bool addAttribute(const QgsField &field)
Add an attribute field (but does not commit it) returns true if the field was added.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
bool deleteFeature(QgsFeatureId fid, DeleteContext *context=nullptr)
Deletes a feature from the layer (but does not commit it).
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
void removeFieldConstraint(int index, QgsFieldConstraints::Constraint constraint)
Removes a constraint for a specified field index.
void editingStarted()
Emitted when editing on this layer has started.
void committedFeaturesRemoved(const QString &layerId, const QgsFeatureIds &deletedFeatureIds)
Emitted when features are deleted from the provider.
QgsExpressionContext createExpressionContext() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QStringList commitErrors() const
Returns a list containing any error messages generated when attempting to commit changes to the layer...
QString dataComment() const
Returns a description for this layer as defined in the data provider.
Q_INVOKABLE bool commitChanges(bool stopEditing=true)
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
void reload() FINAL
Synchronises with changes in the datasource.
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
Q_INVOKABLE QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on.
bool changeGeometry(QgsFeatureId fid, QgsGeometry &geometry, bool skipDefaultValue=false)
Changes a feature's geometry within the layer's edit buffer (but does not immediately commit the chan...
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Type
The WKB type describes the number of dimensions a geometry has.
static QString displayString(Type type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Unique pointer for spatialite databases, which automatically closes the database when the pointer goe...
int open(const QString &path)
Opens the database at the specified file path.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
QString errorMessage() const
Returns the most recent error message encountered by the database.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
int open(const QString &path)
Opens the database at the specified file path.
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
QMap< int, QVariant > QgsAttributeMap
void * OGRSpatialReferenceH
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
QList< QgsFeature > QgsFeatureList
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
#define QgsDebugMsgLevel(str, level)
#define CUSTOM_PROPERTY_ORIGINAL_LAYERID
#define CUSTOM_SHOW_FEATURE_COUNT
#define PROJECT_ENTRY_SCOPE_OFFLINE
#define CUSTOM_PROPERTY_REMOTE_PROVIDER
#define CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE
#define CUSTOM_PROPERTY_LAYERNAME_SUFFIX
#define CUSTOM_PROPERTY_REMOTE_SOURCE
#define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH
QList< QgsVectorLayerJoinInfo > QgsVectorJoinList
Setting options for loading vector layers.