46 #include <QDomDocument>
49 #include <QMessageBox>
51 #include <ogr_srs_api.h>
56 #include <spatialite.h>
59 #define CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE "isOfflineEditable"
60 #define CUSTOM_PROPERTY_REMOTE_SOURCE "remoteSource"
61 #define CUSTOM_PROPERTY_REMOTE_PROVIDER "remoteProvider"
62 #define CUSTOM_SHOW_FEATURE_COUNT "showFeatureCount"
63 #define CUSTOM_PROPERTY_ORIGINAL_LAYERID "remoteLayerId"
64 #define CUSTOM_PROPERTY_LAYERNAME_SUFFIX "layerNameSuffix"
65 #define PROJECT_ENTRY_SCOPE_OFFLINE "OfflineEditingPlugin"
66 #define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH "/OfflineDbPath"
91 if ( layerIds.isEmpty() )
96 QString dbPath = QDir( offlineDataPath ).absoluteFilePath( offlineDbFile );
97 if ( createOfflineDb( dbPath, containerType ) )
100 int rc = database.
open( dbPath );
101 if ( rc != SQLITE_OK )
103 showWarning( tr(
"Could not open the SpatiaLite database" ) );
108 createLoggingTables( database.get() );
112 QMap<QString, QgsVectorJoinList > joinInfoBuffer;
113 QMap<QString, QgsVectorLayer *> layerIdMapping;
115 for (
const QString &layerId : layerIds )
127 QgsVectorJoinList::iterator joinIt = joins.begin();
128 while ( joinIt != joins.end() )
130 if ( joinIt->prefix().isNull() )
135 joinIt->setPrefix( vl->
name() +
'_' );
139 joinInfoBuffer.insert( vl->
id(), joins );
145 for (
int i = 0; i < layerIds.count(); i++ )
153 QString origLayerId = vl->
id();
154 QgsVectorLayer *newLayer = copyVectorLayer( vl, database.get(), dbPath, onlySelected, containerType, layerNameSuffix );
157 layerIdMapping.insert( origLayerId, newLayer );
160 snappingConfig.
removeLayers( QList<QgsMapLayer *>() << vl );
164 QStringList() << origLayerId );
172 QMap<QString, QgsVectorJoinList >::ConstIterator it;
173 for ( it = joinInfoBuffer.constBegin(); it != joinInfoBuffer.constEnd(); ++it )
179 const QList<QgsVectorLayerJoinInfo> joins = it.value();
182 QgsVectorLayer *newJoinedLayer = layerIdMapping.value( join.joinLayerId() );
183 if ( newJoinedLayer )
186 join.setJoinLayer( newJoinedLayer );
197 if ( projectTitle.isEmpty() )
201 projectTitle += QLatin1String(
" (offline)" );
232 QList<QgsMapLayer *> offlineLayers;
234 for ( QMap<QString, QgsMapLayer *>::iterator layer_it = mapLayers.begin() ; layer_it != mapLayers.end(); ++layer_it )
239 offlineLayers << layer;
243 QgsDebugMsgLevel( QStringLiteral(
"Found %1 offline layers" ).arg( offlineLayers.count() ), 4 );
244 for (
int l = 0; l < offlineLayers.count(); l++ )
252 QString remoteName = layer->
name();
254 if ( remoteName.endsWith( remoteNameSuffix ) )
255 remoteName.chop( remoteNameSuffix.size() );
261 if ( remoteLayer->
providerType().contains( QLatin1String(
"WFS" ), Qt::CaseInsensitive ) )
271 QgsVectorLayer *offlineLayer = qobject_cast<QgsVectorLayer *>( layer );
277 copySymbology( offlineLayer, remoteLayer );
278 updateRelations( offlineLayer, remoteLayer );
279 updateMapThemes( offlineLayer, remoteLayer );
280 updateLayerOrder( offlineLayer, remoteLayer );
284 snappingConfig.
removeLayers( QList<QgsMapLayer *>() << offlineLayer );
292 QString qgisLayerId = layer->
id();
293 QString sql = QStringLiteral(
"SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
294 int layerId = sqlQueryInt( database.get(), sql, -1 );
300 int commitNo = getCommitNo( database.get() );
301 QgsDebugMsgLevel( QStringLiteral(
"Found %1 commits" ).arg( commitNo ), 4 );
302 for (
int i = 0; i < commitNo; i++ )
306 applyAttributesAdded( remoteLayer, database.get(), layerId, i );
307 applyAttributeValueChanges( offlineLayer, remoteLayer, database.get(), layerId, i );
308 applyGeometryChanges( remoteLayer, database.get(), layerId, i );
311 applyFeaturesAdded( offlineLayer, remoteLayer, database.get(), layerId );
312 applyFeaturesRemoved( remoteLayer, database.get(), layerId );
317 updateFidLookup( remoteLayer, database.get(), layerId );
320 sql = QStringLiteral(
"DELETE FROM 'log_added_attrs' WHERE \"layer_id\" = %1" ).arg( layerId );
321 sqlExec( database.get(), sql );
322 sql = QStringLiteral(
"DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
323 sqlExec( database.get(), sql );
324 sql = QStringLiteral(
"DELETE FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
325 sqlExec( database.get(), sql );
326 sql = QStringLiteral(
"DELETE FROM 'log_feature_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
327 sqlExec( database.get(), sql );
328 sql = QStringLiteral(
"DELETE FROM 'log_geometry_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
329 sqlExec( database.get(), sql );
333 showWarning( remoteLayer->
commitErrors().join( QLatin1Char(
'\n' ) ) );
338 QgsDebugMsg( QStringLiteral(
"Could not find the layer id in the edit logs!" ) );
349 projectTitle.remove( QRegExp(
" \\(offline\\)$" ) );
356 QgsDebugMsg( QStringLiteral(
"Remote layer is not valid!" ) );
361 QString sql = QStringLiteral(
"UPDATE 'log_indices' SET 'last_index' = 0 WHERE \"name\" = 'commit_no'" );
362 sqlExec( database.get(), sql );
369 void QgsOfflineEditing::initializeSpatialMetadata(
sqlite3 *sqlite_handle )
372 if ( !sqlite_handle )
375 char **results =
nullptr;
377 int ret = sqlite3_get_table( sqlite_handle,
"select count(*) from sqlite_master", &results, &rows, &columns,
nullptr );
378 if ( ret != SQLITE_OK )
383 for (
int i = 1; i <= rows; i++ )
384 count = atoi( results[( i * columns ) + 0] );
387 sqlite3_free_table( results );
392 bool above41 =
false;
393 ret = sqlite3_get_table( sqlite_handle,
"select spatialite_version()", &results, &rows, &columns,
nullptr );
394 if ( ret == SQLITE_OK && rows == 1 && columns == 1 )
396 QString version = QString::fromUtf8( results[1] );
397 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
398 QStringList parts = version.split(
' ', QString::SkipEmptyParts );
400 QStringList parts = version.split(
' ', Qt::SkipEmptyParts );
402 if ( !parts.empty() )
404 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
405 QStringList verparts = parts.at( 0 ).split(
'.', QString::SkipEmptyParts );
407 QStringList verparts = parts.at( 0 ).split(
'.', Qt::SkipEmptyParts );
409 above41 = verparts.size() >= 2 && ( verparts.at( 0 ).toInt() > 4 || ( verparts.at( 0 ).toInt() == 4 && verparts.at( 1 ).toInt() >= 1 ) );
413 sqlite3_free_table( results );
416 char *errMsg =
nullptr;
417 ret = sqlite3_exec( sqlite_handle, above41 ?
"SELECT InitSpatialMetadata(1)" :
"SELECT InitSpatialMetadata()",
nullptr,
nullptr, &errMsg );
419 if ( ret != SQLITE_OK )
421 QString errCause = tr(
"Unable to initialize SpatialMetadata:\n" );
422 errCause += QString::fromUtf8( errMsg );
423 showWarning( errCause );
424 sqlite3_free( errMsg );
427 spatial_ref_sys_init( sqlite_handle, 0 );
430 bool QgsOfflineEditing::createOfflineDb(
const QString &offlineDbPath, ContainerType containerType )
433 char *errMsg =
nullptr;
434 QFile newDb( offlineDbPath );
435 if ( newDb.exists() )
437 QFile::remove( offlineDbPath );
442 QFileInfo fullPath = QFileInfo( offlineDbPath );
443 QDir path = fullPath.dir();
446 QDir().mkpath( path.absolutePath() );
449 QString dbPath = newDb.fileName();
452 switch ( containerType )
456 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
459 showWarning( tr(
"Creation of database failed. GeoPackage driver not found." ) );
466 showWarning( tr(
"Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
478 ret = database.
open_v2( dbPath, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
nullptr );
482 QString errCause = tr(
"Could not create a new database\n" );
484 showWarning( errCause );
488 ret = sqlite3_exec( database.get(),
"PRAGMA foreign_keys = 1",
nullptr,
nullptr, &errMsg );
489 if ( ret != SQLITE_OK )
491 showWarning( tr(
"Unable to activate FOREIGN_KEY constraints" ) );
492 sqlite3_free( errMsg );
495 initializeSpatialMetadata( database.get() );
499 void QgsOfflineEditing::createLoggingTables(
sqlite3 *db )
502 QString sql = QStringLiteral(
"CREATE TABLE 'log_indices' ('name' TEXT, 'last_index' INTEGER)" );
505 sql = QStringLiteral(
"INSERT INTO 'log_indices' VALUES ('commit_no', 0)" );
508 sql = QStringLiteral(
"INSERT INTO 'log_indices' VALUES ('layer_id', 0)" );
512 sql = QStringLiteral(
"CREATE TABLE 'log_layer_ids' ('id' INTEGER, 'qgis_id' TEXT)" );
516 sql = QStringLiteral(
"CREATE TABLE 'log_fids' ('layer_id' INTEGER, 'offline_fid' INTEGER, 'remote_fid' INTEGER)" );
520 sql = QStringLiteral(
"CREATE TABLE 'log_added_attrs' ('layer_id' INTEGER, 'commit_no' INTEGER, " );
521 sql += QLatin1String(
"'name' TEXT, 'type' INTEGER, 'length' INTEGER, 'precision' INTEGER, 'comment' TEXT)" );
525 sql = QStringLiteral(
"CREATE TABLE 'log_added_features' ('layer_id' INTEGER, 'fid' INTEGER)" );
529 sql = QStringLiteral(
"CREATE TABLE 'log_removed_features' ('layer_id' INTEGER, 'fid' INTEGER)" );
533 sql = QStringLiteral(
"CREATE TABLE 'log_feature_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'attr' INTEGER, 'value' TEXT)" );
537 sql = QStringLiteral(
"CREATE TABLE 'log_geometry_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'geom_wkt' TEXT)" );
545 QgsVectorLayer *QgsOfflineEditing::copyVectorLayer(
QgsVectorLayer *layer,
sqlite3 *db,
const QString &offlineDbPath,
bool onlySelected, ContainerType containerType,
const QString &layerNameSuffix )
550 QString tableName = layer->
id();
551 QgsDebugMsgLevel( QStringLiteral(
"Creating offline table %1 ..." ).arg( tableName ), 4 );
556 switch ( containerType )
561 QString sql = QStringLiteral(
"CREATE TABLE '%1' (" ).arg( tableName );
564 for (
const auto &
field : providerFields )
568 if ( type == QVariant::Int || type == QVariant::LongLong )
570 dataType = QStringLiteral(
"INTEGER" );
572 else if ( type == QVariant::Double )
574 dataType = QStringLiteral(
"REAL" );
576 else if ( type == QVariant::String )
578 dataType = QStringLiteral(
"TEXT" );
582 showWarning( tr(
"%1: Unknown data type %2. Not using type affinity for the field." ).arg(
field.
name(), QVariant::typeToName( type ) ) );
585 sql += delim + QStringLiteral(
"'%1' %2" ).arg(
field.
name(), dataType );
590 int rc = sqlExec( db, sql );
601 geomType = QStringLiteral(
"POINT" );
604 geomType = QStringLiteral(
"MULTIPOINT" );
607 geomType = QStringLiteral(
"LINESTRING" );
610 geomType = QStringLiteral(
"MULTILINESTRING" );
613 geomType = QStringLiteral(
"POLYGON" );
616 geomType = QStringLiteral(
"MULTIPOLYGON" );
623 QString zmInfo = QStringLiteral(
"XY" );
632 if ( layer->
crs().
authid().startsWith( QLatin1String(
"EPSG:" ), Qt::CaseInsensitive ) )
634 epsgCode = layer->
crs().
authid().mid( 5 );
639 showWarning( tr(
"Layer %1 has unsupported Coordinate Reference System (%2)." ).arg( layer->
name(), layer->
crs().
authid() ) );
642 QString sqlAddGeom = QStringLiteral(
"SELECT AddGeometryColumn('%1', 'Geometry', %2, '%3', '%4')" )
643 .arg( tableName, epsgCode, geomType, zmInfo );
646 QString sqlCreateIndex = QStringLiteral(
"SELECT CreateSpatialIndex('%1', 'Geometry')" ).arg( tableName );
648 if ( rc == SQLITE_OK )
650 rc = sqlExec( db, sqlAddGeom );
651 if ( rc == SQLITE_OK )
653 rc = sqlExec( db, sqlCreateIndex );
658 if ( rc != SQLITE_OK )
660 showWarning( tr(
"Filling SpatiaLite for layer %1 failed" ).arg( layer->
name() ) );
665 QString connectionString = QStringLiteral(
"dbname='%1' table='%2'%3 sql=" )
667 tableName, layer->
isSpatial() ?
"(Geometry)" :
"" );
670 layer->
name() + layerNameSuffix, QStringLiteral(
"spatialite" ), options );
676 char **options =
nullptr;
678 options = CSLSetNameValue( options,
"OVERWRITE",
"YES" );
679 options = CSLSetNameValue( options,
"IDENTIFIER", tr(
"%1 (offline)" ).arg( layer->
id() ).toUtf8().constData() );
680 options = CSLSetNameValue( options,
"DESCRIPTION", layer->
dataComment().toUtf8().constData() );
683 QString fidBase( QStringLiteral(
"fid" ) );
684 QString fid = fidBase;
688 fid = fidBase +
'_' + QString::number( counter );
691 if ( counter == 10000 )
693 showWarning( tr(
"Cannot make FID-name for GPKG " ) );
697 options = CSLSetNameValue( options,
"FID", fid.toUtf8().constData() );
701 options = CSLSetNameValue( options,
"GEOMETRY_COLUMN",
"geom" );
702 options = CSLSetNameValue( options,
"SPATIAL_INDEX",
"YES" );
705 OGRSFDriverH hDriver =
nullptr;
708 OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS,
static_cast<OGRwkbGeometryType
>( layer->
wkbType() ), options );
709 CSLDestroy( options );
714 showWarning( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
719 for (
const auto &
field : providerFields )
722 const QVariant::Type type =
field.
type();
723 OGRFieldType ogrType( OFTString );
724 OGRFieldSubType ogrSubType = OFSTNone;
725 if ( type == QVariant::Int )
726 ogrType = OFTInteger;
727 else if ( type == QVariant::LongLong )
728 ogrType = OFTInteger64;
729 else if ( type == QVariant::Double )
731 else if ( type == QVariant::Time )
733 else if ( type == QVariant::Date )
735 else if ( type == QVariant::DateTime )
736 ogrType = OFTDateTime;
737 else if ( type == QVariant::Bool )
739 ogrType = OFTInteger;
740 ogrSubType = OFSTBoolean;
748 OGR_Fld_SetWidth( fld.get(), ogrWidth );
749 if ( ogrSubType != OFSTNone )
750 OGR_Fld_SetSubType( fld.get(), ogrSubType );
752 if ( OGR_L_CreateField( hLayer, fld.get(),
true ) != OGRERR_NONE )
754 showWarning( tr(
"Creation of field %1 failed (OGR error: %2)" )
755 .arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
763 OGR_L_ResetReading( hLayer );
764 if ( CPLGetLastErrorType() != CE_None )
766 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
772 QString uri = QStringLiteral(
"%1|layername=%2" ).arg( offlineDbPath, tableName );
774 newLayer =
new QgsVectorLayer( uri, layer->
name() + layerNameSuffix, QStringLiteral(
"ogr" ), layerOptions );
791 if ( !selectedFids.isEmpty() )
805 int featureCount = 1;
807 QList<QgsFeatureId> remoteFeatureIds;
810 remoteFeatureIds << f.
id();
817 QgsAttributes newAttrs( containerType ==
GPKG ? attrs.count() + 1 : attrs.count() );
818 for (
int it = 0; it < attrs.count(); ++it )
820 newAttrs[column++] = attrs.at( it );
834 int layerId = getOrCreateLayerId( db, newLayer->
id() );
835 QList<QgsFeatureId> offlineFeatureIds;
840 offlineFeatureIds << f.
id();
844 sqlExec( db, QStringLiteral(
"BEGIN" ) );
845 int remoteCount = remoteFeatureIds.size();
846 for (
int i = 0; i < remoteCount; i++ )
849 if ( i < offlineFeatureIds.count() )
851 addFidLookup( db, layerId, offlineFeatureIds.at( i ), remoteFeatureIds.at( i ) );
855 showWarning( tr(
"Feature cannot be copied to the offline layer, please check if the online layer '%1' is still accessible." ).arg( layer->
name() ) );
860 sqlExec( db, QStringLiteral(
"COMMIT" ) );
864 showWarning( newLayer->
commitErrors().join( QLatin1Char(
'\n' ) ) );
881 QList<QgsMapLayer *>() << newLayer );
884 copySymbology( layer, newLayer );
887 const auto fields = layer->
fields();
899 if ( layerTreeLayer )
902 if ( parentTreeGroup )
904 int index = parentTreeGroup->
children().indexOf( layerTreeLayer );
907 if ( newLayerTreeLayer )
921 updateRelations( layer, newLayer );
922 updateMapThemes( layer, newLayer );
923 updateLayerOrder( layer, newLayer );
931 void QgsOfflineEditing::applyAttributesAdded(
QgsVectorLayer *remoteLayer,
sqlite3 *db,
int layerId,
int commitNo )
933 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 );
934 QList<QgsField> fields = sqlQueryAttributesAdded( db, sql );
937 QList<QgsVectorDataProvider::NativeType> nativeTypes = provider->
nativeTypes();
940 QMap < QVariant::Type, QString > typeNameLookup;
941 for (
int i = 0; i < nativeTypes.size(); i++ )
949 for (
int i = 0; i < fields.size(); i++ )
953 if ( typeNameLookup.contains(
field.
type() ) )
961 showWarning( QStringLiteral(
"Could not add attribute '%1' of type %2" ).arg(
field.
name() ).arg(
field.
type() ) );
970 QString sql = QStringLiteral(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
971 const QList<int> featureIdInts = sqlQueryInts( db, sql );
973 for (
int id : featureIdInts )
994 for ( QgsFeatureList::iterator it = features.begin(); it != features.end(); ++it )
998 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
1001 for (
int it = 0; it < attrs.count(); ++it )
1003 newAttrs[ attrLookup[ it ] ] = attrs.at( it );
1014 void QgsOfflineEditing::applyFeaturesRemoved(
QgsVectorLayer *remoteLayer,
sqlite3 *db,
int layerId )
1016 QString sql = QStringLiteral(
"SELECT \"fid\" FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
1022 for ( QgsFeatureIds::const_iterator it = values.constBegin(); it != values.constEnd(); ++it )
1033 QString sql = QStringLiteral(
"SELECT \"fid\", \"attr\", \"value\" FROM 'log_feature_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2 " ).arg( layerId ).arg( commitNo );
1034 AttributeValueChanges values = sqlQueryAttributeValueChanges( db, sql );
1038 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
1040 for (
int i = 0; i < values.size(); i++ )
1042 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
1043 QgsDebugMsgLevel( QStringLiteral(
"Offline changeAttributeValue %1 = %2" ).arg( QString( attrLookup[ values.at( i ).attr ] ), values.at( i ).value ), 4 );
1044 remoteLayer->
changeAttributeValue( fid, attrLookup[ values.at( i ).attr ], values.at( i ).value );
1050 void QgsOfflineEditing::applyGeometryChanges(
QgsVectorLayer *remoteLayer,
sqlite3 *db,
int layerId,
int commitNo )
1052 QString sql = QStringLiteral(
"SELECT \"fid\", \"geom_wkt\" FROM 'log_geometry_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2" ).arg( layerId ).arg( commitNo );
1053 GeometryChanges values = sqlQueryGeometryChanges( db, sql );
1057 for (
int i = 0; i < values.size(); i++ )
1059 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
1083 if ( offlineFid( db, layerId, f.
id() ) == -1 )
1085 newRemoteFids[ f.
id()] =
true;
1093 QString sql = QStringLiteral(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
1094 QList<int> newOfflineFids = sqlQueryInts( db, sql );
1096 if ( newRemoteFids.size() != newOfflineFids.size() )
1104 sqlExec( db, QStringLiteral(
"BEGIN" ) );
1105 for ( QMap<QgsFeatureId, bool>::const_iterator it = newRemoteFids.constBegin(); it != newRemoteFids.constEnd(); ++it )
1107 addFidLookup( db, layerId, newOfflineFids.at( i++ ), it.key() );
1109 sqlExec( db, QStringLiteral(
"COMMIT" ) );
1123 if ( error.isEmpty() )
1127 if ( !error.isEmpty() )
1129 showWarning( error );
1136 const QList<QgsRelation> referencedRelations = relationManager->
referencedRelations( sourceLayer );
1138 for (
QgsRelation relation : referencedRelations )
1141 relation.setReferencedLayer( targetLayer->
id() );
1145 const QList<QgsRelation> referencingRelations = relationManager->
referencingRelations( sourceLayer );
1147 for (
QgsRelation relation : referencingRelations )
1150 relation.setReferencingLayer( targetLayer->
id() );
1158 const QStringList mapThemeNames = mapThemeCollection->
mapThemes();
1160 for (
const QString &mapThemeName : mapThemeNames )
1168 if ( layerRecord.layer() == sourceLayer )
1170 layerRecord.setLayer( targetLayer );
1184 auto iterator = layerOrder.begin();
1186 while ( iterator != layerOrder.end() )
1188 if ( *iterator == targetLayer )
1190 iterator = layerOrder.erase( iterator );
1191 if ( iterator == layerOrder.end() )
1195 if ( *iterator == sourceLayer )
1197 *iterator = targetLayer;
1211 QMap <
int ,
int > attrLookup;
1214 for (
int i = 0; i < offlineAttrs.size(); i++ )
1223 void QgsOfflineEditing::showWarning(
const QString &message )
1225 emit
warning( tr(
"Offline Editing Plugin" ), message );
1232 if ( !dbPath.isEmpty() )
1235 int rc = database.
open( absoluteDbPath );
1236 if ( rc != SQLITE_OK )
1238 QgsDebugMsg( QStringLiteral(
"Could not open the SpatiaLite logging database" ) );
1239 showWarning( tr(
"Could not open the SpatiaLite logging database" ) );
1244 QgsDebugMsg( QStringLiteral(
"dbPath is empty!" ) );
1249 int QgsOfflineEditing::getOrCreateLayerId(
sqlite3 *db,
const QString &qgisLayerId )
1251 QString sql = QStringLiteral(
"SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
1252 int layerId = sqlQueryInt( db, sql, -1 );
1253 if ( layerId == -1 )
1256 sql = QStringLiteral(
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'layer_id'" );
1257 int newLayerId = sqlQueryInt( db, sql, -1 );
1260 sql = QStringLiteral(
"INSERT INTO 'log_layer_ids' VALUES (%1, '%2')" ).arg( newLayerId ).arg( qgisLayerId );
1265 sql = QStringLiteral(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'layer_id'" ).arg( newLayerId + 1 );
1268 layerId = newLayerId;
1274 int QgsOfflineEditing::getCommitNo(
sqlite3 *db )
1276 QString sql = QStringLiteral(
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'commit_no'" );
1277 return sqlQueryInt( db, sql, -1 );
1280 void QgsOfflineEditing::increaseCommitNo(
sqlite3 *db )
1282 QString sql = QStringLiteral(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'commit_no'" ).arg( getCommitNo( db ) + 1 );
1288 QString sql = QStringLiteral(
"INSERT INTO 'log_fids' VALUES ( %1, %2, %3 )" ).arg( layerId ).arg( offlineFid ).arg( remoteFid );
1294 QString sql = QStringLiteral(
"SELECT \"remote_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"offline_fid\" = %2" ).arg( layerId ).arg( offlineFid );
1295 return sqlQueryInt( db, sql, -1 );
1300 QString sql = QStringLiteral(
"SELECT \"offline_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"remote_fid\" = %2" ).arg( layerId ).arg( remoteFid );
1301 return sqlQueryInt( db, sql, -1 );
1306 QString sql = QStringLiteral(
"SELECT COUNT(\"fid\") FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( fid );
1307 return ( sqlQueryInt( db, sql, 0 ) > 0 );
1310 int QgsOfflineEditing::sqlExec(
sqlite3 *db,
const QString &sql )
1312 char *errmsg =
nullptr;
1313 int rc = sqlite3_exec( db, sql.toUtf8(),
nullptr,
nullptr, &errmsg );
1314 if ( rc != SQLITE_OK )
1316 showWarning( errmsg );
1321 int QgsOfflineEditing::sqlQueryInt(
sqlite3 *db,
const QString &sql,
int defaultValue )
1323 sqlite3_stmt *stmt =
nullptr;
1324 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1326 showWarning( sqlite3_errmsg( db ) );
1327 return defaultValue;
1330 int value = defaultValue;
1331 int ret = sqlite3_step( stmt );
1332 if ( ret == SQLITE_ROW )
1334 value = sqlite3_column_int( stmt, 0 );
1336 sqlite3_finalize( stmt );
1341 QList<int> QgsOfflineEditing::sqlQueryInts(
sqlite3 *db,
const QString &sql )
1345 sqlite3_stmt *stmt =
nullptr;
1346 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1348 showWarning( sqlite3_errmsg( db ) );
1352 int ret = sqlite3_step( stmt );
1353 while ( ret == SQLITE_ROW )
1355 values << sqlite3_column_int( stmt, 0 );
1357 ret = sqlite3_step( stmt );
1359 sqlite3_finalize( stmt );
1364 QList<QgsField> QgsOfflineEditing::sqlQueryAttributesAdded(
sqlite3 *db,
const QString &sql )
1366 QList<QgsField> values;
1368 sqlite3_stmt *stmt =
nullptr;
1369 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1371 showWarning( sqlite3_errmsg( db ) );
1375 int ret = sqlite3_step( stmt );
1376 while ( ret == SQLITE_ROW )
1378 QgsField field( QString(
reinterpret_cast< const char *
>( sqlite3_column_text( stmt, 0 ) ) ),
1379 static_cast< QVariant::Type
>( sqlite3_column_int( stmt, 1 ) ),
1381 sqlite3_column_int( stmt, 2 ),
1382 sqlite3_column_int( stmt, 3 ),
1383 QString(
reinterpret_cast< const char *
>( sqlite3_column_text( stmt, 4 ) ) ) );
1386 ret = sqlite3_step( stmt );
1388 sqlite3_finalize( stmt );
1397 sqlite3_stmt *stmt =
nullptr;
1398 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1400 showWarning( sqlite3_errmsg( db ) );
1404 int ret = sqlite3_step( stmt );
1405 while ( ret == SQLITE_ROW )
1407 values << sqlite3_column_int( stmt, 0 );
1409 ret = sqlite3_step( stmt );
1411 sqlite3_finalize( stmt );
1416 QgsOfflineEditing::AttributeValueChanges QgsOfflineEditing::sqlQueryAttributeValueChanges(
sqlite3 *db,
const QString &sql )
1418 AttributeValueChanges values;
1420 sqlite3_stmt *stmt =
nullptr;
1421 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1423 showWarning( sqlite3_errmsg( db ) );
1427 int ret = sqlite3_step( stmt );
1428 while ( ret == SQLITE_ROW )
1430 AttributeValueChange change;
1431 change.fid = sqlite3_column_int( stmt, 0 );
1432 change.attr = sqlite3_column_int( stmt, 1 );
1433 change.value = QString(
reinterpret_cast< const char *
>( sqlite3_column_text( stmt, 2 ) ) );
1436 ret = sqlite3_step( stmt );
1438 sqlite3_finalize( stmt );
1443 QgsOfflineEditing::GeometryChanges QgsOfflineEditing::sqlQueryGeometryChanges(
sqlite3 *db,
const QString &sql )
1445 GeometryChanges values;
1447 sqlite3_stmt *stmt =
nullptr;
1448 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt,
nullptr ) != SQLITE_OK )
1450 showWarning( sqlite3_errmsg( db ) );
1454 int ret = sqlite3_step( stmt );
1455 while ( ret == SQLITE_ROW )
1457 GeometryChange change;
1458 change.fid = sqlite3_column_int( stmt, 0 );
1459 change.geom_wkt = QString(
reinterpret_cast< const char *
>( sqlite3_column_text( stmt, 1 ) ) );
1462 ret = sqlite3_step( stmt );
1464 sqlite3_finalize( stmt );
1469 void QgsOfflineEditing::committedAttributesAdded(
const QString &qgisLayerId,
const QList<QgsField> &addedAttributes )
1476 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1477 int commitNo = getCommitNo( database.get() );
1481 QString sql = QStringLiteral(
"INSERT INTO 'log_added_attrs' VALUES ( %1, %2, '%3', %4, %5, %6, '%7' )" )
1489 sqlExec( database.get(), sql );
1492 increaseCommitNo( database.get() );
1495 void QgsOfflineEditing::committedFeaturesAdded(
const QString &qgisLayerId,
const QgsFeatureList &addedFeatures )
1502 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1506 QString dataSourceString = layer->
source();
1512 if ( !offlinePath.contains(
".gpkg" ) )
1514 tableName = uri.
table();
1519 QVariantMap decodedUri = ogrProviderMetaData->
decodeUri( dataSourceString );
1520 tableName = decodedUri.value( QStringLiteral(
"layerName" ) ).toString();
1521 if ( tableName.isEmpty() )
1523 showWarning( tr(
"Could not deduce table name from data source %1." ).arg( dataSourceString ) );
1528 QString sql = QStringLiteral(
"SELECT ROWID FROM '%1' ORDER BY ROWID DESC LIMIT %2" ).arg( tableName ).arg( addedFeatures.size() );
1529 QList<int> newFeatureIds = sqlQueryInts( database.get(), sql );
1530 for (
int i = newFeatureIds.size() - 1; i >= 0; i-- )
1532 QString sql = QStringLiteral(
"INSERT INTO 'log_added_features' VALUES ( %1, %2 )" )
1534 .arg( newFeatureIds.at( i ) );
1535 sqlExec( database.get(), sql );
1539 void QgsOfflineEditing::committedFeaturesRemoved(
const QString &qgisLayerId,
const QgsFeatureIds &deletedFeatureIds )
1546 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1550 if ( isAddedFeature( database.get(), layerId,
id ) )
1553 QString sql = QStringLiteral(
"DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg(
id );
1554 sqlExec( database.get(), sql );
1558 QString sql = QStringLiteral(
"INSERT INTO 'log_removed_features' VALUES ( %1, %2)" )
1561 sqlExec( database.get(), sql );
1566 void QgsOfflineEditing::committedAttributeValuesChanges(
const QString &qgisLayerId,
const QgsChangedAttributesMap &changedAttrsMap )
1573 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1574 int commitNo = getCommitNo( database.get() );
1576 for ( QgsChangedAttributesMap::const_iterator cit = changedAttrsMap.begin(); cit != changedAttrsMap.end(); ++cit )
1579 if ( isAddedFeature( database.get(), layerId, fid ) )
1585 for ( QgsAttributeMap::const_iterator it = attrMap.constBegin(); it != attrMap.constEnd(); ++it )
1587 QString sql = QStringLiteral(
"INSERT INTO 'log_feature_updates' VALUES ( %1, %2, %3, %4, '%5' )" )
1592 .arg( it.value().toString() );
1593 sqlExec( database.get(), sql );
1597 increaseCommitNo( database.get() );
1600 void QgsOfflineEditing::committedGeometriesChanges(
const QString &qgisLayerId,
const QgsGeometryMap &changedGeometries )
1607 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1608 int commitNo = getCommitNo( database.get() );
1610 for ( QgsGeometryMap::const_iterator it = changedGeometries.begin(); it != changedGeometries.end(); ++it )
1613 if ( isAddedFeature( database.get(), layerId, fid ) )
1619 QString sql = QStringLiteral(
"INSERT INTO 'log_geometry_updates' VALUES ( %1, %2, %3, '%4' )" )
1623 .arg( geom.
asWkt() );
1624 sqlExec( database.get(), sql );
1629 increaseCommitNo( database.get() );
1632 void QgsOfflineEditing::startListenFeatureChanges()
1634 QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( sender() );
1640 this, &QgsOfflineEditing::committedAttributesAdded );
1642 this, &QgsOfflineEditing::committedAttributeValuesChanges );
1644 this, &QgsOfflineEditing::committedGeometriesChanges );
1647 this, &QgsOfflineEditing::committedFeaturesAdded );
1649 this, &QgsOfflineEditing::committedFeaturesRemoved );
1652 void QgsOfflineEditing::stopListenFeatureChanges()
1654 QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( sender() );
1660 this, &QgsOfflineEditing::committedAttributesAdded );
1662 this, &QgsOfflineEditing::committedAttributeValuesChanges );
1664 this, &QgsOfflineEditing::committedGeometriesChanges );
1667 this, &QgsOfflineEditing::committedFeaturesAdded );
1669 this, &QgsOfflineEditing::committedFeaturesRemoved );
1672 void QgsOfflineEditing::layerAdded(
QgsMapLayer *layer )
1677 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 id, geometry and a list of field/values...
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.
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
Gets field at particular index (must be in range 0..N-1)
int fieldOriginIndex(int fieldIdx) const
Gets 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.
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 layerProgressUpdated(int layer, int numLayers)
Emitted whenever a new layer is being processed.
bool isOfflineProject() const
Returns true if current project is offline.
void progressModeSet(QgsOfflineEditing::ProgressMode mode, int maximum)
Emitted when the mode for the progress of the current operation is set.
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.
void progressUpdated(int progress)
Emitted with the progress of the current mode.
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.
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.
long featureCount() const override=0
Number of features in the layer.
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.
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.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
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.