43 #include <QDomDocument> 46 #include <QMessageBox> 48 #include <ogr_srs_api.h> 53 #include <spatialite.h> 56 #define CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE "isOfflineEditable" 57 #define CUSTOM_PROPERTY_REMOTE_SOURCE "remoteSource" 58 #define CUSTOM_PROPERTY_REMOTE_PROVIDER "remoteProvider" 59 #define CUSTOM_SHOW_FEATURE_COUNT "showFeatureCount" 60 #define PROJECT_ENTRY_SCOPE_OFFLINE "OfflineEditingPlugin" 61 #define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH "/OfflineDbPath" 85 if ( layerIds.isEmpty() )
89 QString dbPath = QDir( offlineDataPath ).absoluteFilePath( offlineDbFile );
90 if ( createOfflineDb( dbPath, containerType ) )
93 int rc = database.
open( dbPath );
94 if ( rc != SQLITE_OK )
96 showWarning( tr(
"Could not open the SpatiaLite database" ) );
101 createLoggingTables( database.get() );
105 QMap<QString, QgsVectorJoinList > joinInfoBuffer;
106 QMap<QString, QgsVectorLayer *> layerIdMapping;
108 Q_FOREACH (
const QString &layerId, layerIds )
120 QgsVectorJoinList::iterator joinIt = joins.begin();
121 while ( joinIt != joins.end() )
123 if ( joinIt->prefix().isNull() )
128 joinIt->setPrefix( vl->
name() +
'_' );
132 joinInfoBuffer.insert( vl->
id(), joins );
136 for (
int i = 0; i < layerIds.count(); i++ )
144 QString origLayerId = vl->
id();
145 QgsVectorLayer *newLayer = copyVectorLayer( vl, database.get(), dbPath, onlySelected, containerType );
148 layerIdMapping.insert( origLayerId, newLayer );
151 QStringList() << origLayerId );
157 QMap<QString, QgsVectorJoinList >::ConstIterator it;
158 for ( it = joinInfoBuffer.constBegin(); it != joinInfoBuffer.constEnd(); ++it )
167 if ( newJoinedLayer )
182 if ( projectTitle.isEmpty() )
186 projectTitle += QLatin1String(
" (offline)" );
215 QList<QgsMapLayer *> offlineLayers;
217 for ( QMap<QString, QgsMapLayer *>::iterator layer_it = mapLayers.begin() ; layer_it != mapLayers.end(); ++layer_it )
222 offlineLayers << layer;
226 QgsDebugMsgLevel( QStringLiteral(
"Found %1 offline layers" ).arg( offlineLayers.count() ), 4 );
227 for (
int l = 0; l < offlineLayers.count(); l++ )
235 QString remoteName = layer->
name();
236 remoteName.remove( QRegExp(
" \\(offline\\)$" ) );
242 if ( remoteLayer->
dataProvider()->
name().contains( QLatin1String(
"WFS" ), Qt::CaseInsensitive ) )
258 copySymbology( offlineLayer, remoteLayer );
259 updateRelations( offlineLayer, remoteLayer );
260 updateMapThemes( offlineLayer, remoteLayer );
261 updateLayerOrder( offlineLayer, remoteLayer );
269 QString qgisLayerId = layer->
id();
270 QString sql = QStringLiteral(
"SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
271 int layerId = sqlQueryInt( database.get(), sql, -1 );
277 int commitNo = getCommitNo( database.get() );
278 QgsDebugMsgLevel( QStringLiteral(
"Found %1 commits" ).arg( commitNo ), 4 );
279 for (
int i = 0; i < commitNo; i++ )
283 applyAttributesAdded( remoteLayer, database.get(), layerId, i );
284 applyAttributeValueChanges( offlineLayer, remoteLayer, database.get(), layerId, i );
285 applyGeometryChanges( remoteLayer, database.get(), layerId, i );
288 applyFeaturesAdded( offlineLayer, remoteLayer, database.get(), layerId );
289 applyFeaturesRemoved( remoteLayer, database.get(), layerId );
294 updateFidLookup( remoteLayer, database.get(), layerId );
297 sql = QStringLiteral(
"DELETE FROM 'log_added_attrs' WHERE \"layer_id\" = %1" ).arg( layerId );
298 sqlExec( database.get(), sql );
299 sql = QStringLiteral(
"DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
300 sqlExec( database.get(), sql );
301 sql = QStringLiteral(
"DELETE FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
302 sqlExec( database.get(), sql );
303 sql = QStringLiteral(
"DELETE FROM 'log_feature_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
304 sqlExec( database.get(), sql );
305 sql = QStringLiteral(
"DELETE FROM 'log_geometry_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
306 sqlExec( database.get(), sql );
310 showWarning( remoteLayer->
commitErrors().join( QStringLiteral(
"\n" ) ) );
315 QgsDebugMsg( QStringLiteral(
"Could not find the layer id in the edit logs!" ) );
326 projectTitle.remove( QRegExp(
" \\(offline\\)$" ) );
333 QgsDebugMsg( QStringLiteral(
"Remote layer is not valid!" ) );
338 QString sql = QStringLiteral(
"UPDATE 'log_indices' SET 'last_index' = 0 WHERE \"name\" = 'commit_no'" );
339 sqlExec( database.get(), sql );
344 void QgsOfflineEditing::initializeSpatialMetadata(
sqlite3 *sqlite_handle )
347 if ( !sqlite_handle )
350 char **results =
nullptr;
352 int ret = sqlite3_get_table( sqlite_handle,
"select count(*) from sqlite_master", &results, &rows, &columns,
nullptr );
353 if ( ret != SQLITE_OK )
358 for (
int i = 1; i <= rows; i++ )
359 count = atoi( results[( i * columns ) + 0] );
362 sqlite3_free_table( results );
367 bool above41 =
false;
368 ret = sqlite3_get_table( sqlite_handle,
"select spatialite_version()", &results, &rows, &columns,
nullptr );
369 if ( ret == SQLITE_OK && rows == 1 && columns == 1 )
371 QString version = QString::fromUtf8( results[1] );
372 QStringList parts = version.split(
' ', QString::SkipEmptyParts );
373 if ( !parts.empty() )
375 QStringList verparts = parts.at( 0 ).split(
'.', QString::SkipEmptyParts );
376 above41 = verparts.size() >= 2 && ( verparts.at( 0 ).toInt() > 4 || ( verparts.at( 0 ).toInt() == 4 && verparts.at( 1 ).toInt() >= 1 ) );
380 sqlite3_free_table( results );
383 char *errMsg =
nullptr;
384 ret = sqlite3_exec( sqlite_handle, above41 ?
"SELECT InitSpatialMetadata(1)" :
"SELECT InitSpatialMetadata()",
nullptr,
nullptr, &errMsg );
386 if ( ret != SQLITE_OK )
388 QString errCause = tr(
"Unable to initialize SpatialMetadata:\n" );
389 errCause += QString::fromUtf8( errMsg );
390 showWarning( errCause );
391 sqlite3_free( errMsg );
394 spatial_ref_sys_init( sqlite_handle, 0 );
397 bool QgsOfflineEditing::createOfflineDb(
const QString &offlineDbPath,
ContainerType containerType )
400 char *errMsg =
nullptr;
401 QFile newDb( offlineDbPath );
402 if ( newDb.exists() )
404 QFile::remove( offlineDbPath );
409 QFileInfo fullPath = QFileInfo( offlineDbPath );
410 QDir path = fullPath.dir();
413 QDir().mkpath( path.absolutePath() );
416 QString dbPath = newDb.fileName();
419 switch ( containerType )
423 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
426 showWarning( tr(
"Creation of database failed. GeoPackage driver not found." ) );
433 showWarning( tr(
"Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
445 ret = database.
open_v2( dbPath, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
nullptr );
449 QString errCause = tr(
"Could not create a new database\n" );
451 showWarning( errCause );
455 ret = sqlite3_exec( database.get(),
"PRAGMA foreign_keys = 1",
nullptr,
nullptr, &errMsg );
456 if ( ret != SQLITE_OK )
458 showWarning( tr(
"Unable to activate FOREIGN_KEY constraints" ) );
459 sqlite3_free( errMsg );
462 initializeSpatialMetadata( database.get() );
466 void QgsOfflineEditing::createLoggingTables(
sqlite3 *db )
469 QString sql = QStringLiteral(
"CREATE TABLE 'log_indices' ('name' TEXT, 'last_index' INTEGER)" );
472 sql = QStringLiteral(
"INSERT INTO 'log_indices' VALUES ('commit_no', 0)" );
475 sql = QStringLiteral(
"INSERT INTO 'log_indices' VALUES ('layer_id', 0)" );
479 sql = QStringLiteral(
"CREATE TABLE 'log_layer_ids' ('id' INTEGER, 'qgis_id' TEXT)" );
483 sql = QStringLiteral(
"CREATE TABLE 'log_fids' ('layer_id' INTEGER, 'offline_fid' INTEGER, 'remote_fid' INTEGER)" );
487 sql = QStringLiteral(
"CREATE TABLE 'log_added_attrs' ('layer_id' INTEGER, 'commit_no' INTEGER, " );
488 sql += QLatin1String(
"'name' TEXT, 'type' INTEGER, 'length' INTEGER, 'precision' INTEGER, 'comment' TEXT)" );
492 sql = QStringLiteral(
"CREATE TABLE 'log_added_features' ('layer_id' INTEGER, 'fid' INTEGER)" );
496 sql = QStringLiteral(
"CREATE TABLE 'log_removed_features' ('layer_id' INTEGER, 'fid' INTEGER)" );
500 sql = QStringLiteral(
"CREATE TABLE 'log_feature_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'attr' INTEGER, 'value' TEXT)" );
504 sql = QStringLiteral(
"CREATE TABLE 'log_geometry_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'geom_wkt' TEXT)" );
517 QString tableName = layer->
id();
518 QgsDebugMsgLevel( QStringLiteral(
"Creating offline table %1 ..." ).arg( tableName ), 4 );
523 switch ( containerType )
528 QString sql = QStringLiteral(
"CREATE TABLE '%1' (" ).arg( tableName );
531 for (
const auto &field : providerFields )
534 QVariant::Type type = field.type();
535 if ( type == QVariant::Int || type == QVariant::LongLong )
537 dataType = QStringLiteral(
"INTEGER" );
539 else if ( type == QVariant::Double )
541 dataType = QStringLiteral(
"REAL" );
543 else if ( type == QVariant::String )
545 dataType = QStringLiteral(
"TEXT" );
549 showWarning( tr(
"%1: Unknown data type %2. Not using type affinity for the field." ).arg( field.name(), QVariant::typeToName( type ) ) );
552 sql += delim + QStringLiteral(
"'%1' %2" ).arg( field.name(), dataType );
557 int rc = sqlExec( db, sql );
568 geomType = QStringLiteral(
"POINT" );
571 geomType = QStringLiteral(
"MULTIPOINT" );
574 geomType = QStringLiteral(
"LINESTRING" );
577 geomType = QStringLiteral(
"MULTILINESTRING" );
580 geomType = QStringLiteral(
"POLYGON" );
583 geomType = QStringLiteral(
"MULTIPOLYGON" );
590 QString zmInfo = QStringLiteral(
"XY" );
599 if ( layer->
crs().
authid().startsWith( QLatin1String(
"EPSG:" ), Qt::CaseInsensitive ) )
601 epsgCode = layer->
crs().
authid().mid( 5 );
606 showWarning( tr(
"Layer %1 has unsupported Coordinate Reference System (%2)." ).arg( layer->
name(), layer->
crs().
authid() ) );
609 QString sqlAddGeom = QStringLiteral(
"SELECT AddGeometryColumn('%1', 'Geometry', %2, '%3', '%4')" )
610 .arg( tableName, epsgCode, geomType, zmInfo );
613 QString sqlCreateIndex = QStringLiteral(
"SELECT CreateSpatialIndex('%1', 'Geometry')" ).arg( tableName );
615 if ( rc == SQLITE_OK )
617 rc = sqlExec( db, sqlAddGeom );
618 if ( rc == SQLITE_OK )
620 rc = sqlExec( db, sqlCreateIndex );
625 if ( rc != SQLITE_OK )
627 showWarning( tr(
"Filling SpatiaLite for layer %1 failed" ).arg( layer->
name() ) );
632 QString connectionString = QStringLiteral(
"dbname='%1' table='%2'%3 sql=" )
634 tableName, layer->
isSpatial() ?
"(Geometry)" :
"" );
636 layer->
name() +
" (offline)", QStringLiteral(
"spatialite" ) );
642 char **options =
nullptr;
644 options = CSLSetNameValue( options,
"OVERWRITE",
"YES" );
645 options = CSLSetNameValue( options,
"IDENTIFIER", tr(
"%1 (offline)" ).arg( layer->
name() ).toUtf8().constData() );
646 options = CSLSetNameValue( options,
"DESCRIPTION", layer->
dataComment().toUtf8().constData() );
649 QString fidBase( QStringLiteral(
"fid" ) );
650 QString fid = fidBase;
654 fid = fidBase +
'_' + QString::number( counter );
657 if ( counter == 10000 )
659 showWarning( tr(
"Cannot make FID-name for GPKG " ) );
663 options = CSLSetNameValue( options,
"FID", fid.toUtf8().constData() );
667 options = CSLSetNameValue( options,
"GEOMETRY_COLUMN",
"geom" );
668 options = CSLSetNameValue( options,
"SPATIAL_INDEX",
"YES" );
671 OGRSFDriverH hDriver =
nullptr;
674 OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS,
static_cast<OGRwkbGeometryType
>( layer->
wkbType() ), options );
675 CSLDestroy( options );
680 showWarning( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
685 for (
const auto &field : providerFields )
687 const QString fieldName( field.name() );
688 const QVariant::Type type = field.type();
689 OGRFieldType ogrType( OFTString );
690 if ( type == QVariant::Int )
691 ogrType = OFTInteger;
692 else if ( type == QVariant::LongLong )
693 ogrType = OFTInteger64;
694 else if ( type == QVariant::Double )
696 else if ( type == QVariant::Time )
698 else if ( type == QVariant::Date )
700 else if ( type == QVariant::DateTime )
701 ogrType = OFTDateTime;
705 int ogrWidth = field.length();
708 OGR_Fld_SetWidth( fld.get(), ogrWidth );
710 if ( OGR_L_CreateField( hLayer, fld.get(), true ) != OGRERR_NONE )
712 showWarning( tr(
"Creation of field %1 failed (OGR error: %2)" )
713 .arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
721 OGR_L_ResetReading( hLayer );
722 if ( CPLGetLastErrorType() != CE_None )
724 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
730 QString uri = QStringLiteral(
"%1|layername=%2" ).arg( offlineDbPath, tableName );
731 newLayer =
new QgsVectorLayer( uri, layer->
name() +
" (offline)", QStringLiteral(
"ogr" ) );
748 if ( !selectedFids.isEmpty() )
762 int featureCount = 1;
764 QList<QgsFeatureId> remoteFeatureIds;
767 remoteFeatureIds << f.
id();
774 QgsAttributes newAttrs( containerType ==
GPKG ? attrs.count() + 1 : attrs.count() );
775 for (
int it = 0; it < attrs.count(); ++it )
777 newAttrs[column++] = attrs.at( it );
791 int layerId = getOrCreateLayerId( db, newLayer->
id() );
792 QList<QgsFeatureId> offlineFeatureIds;
797 offlineFeatureIds << f.
id();
801 sqlExec( db, QStringLiteral(
"BEGIN" ) );
802 int remoteCount = remoteFeatureIds.size();
803 for (
int i = 0; i < remoteCount; i++ )
806 if ( i < offlineFeatureIds.count() )
808 addFidLookup( db, layerId, offlineFeatureIds.at( i ), remoteFeatureIds.at( i ) );
812 showWarning( tr(
"Feature cannot be copied to the offline layer, please check if the online layer '%1' is still accessible." ).arg( layer->
name() ) );
817 sqlExec( db, QStringLiteral(
"COMMIT" ) );
821 showWarning( newLayer->
commitErrors().join( QStringLiteral(
"\n" ) ) );
833 QList<QgsMapLayer *>() << newLayer );
836 copySymbology( layer, newLayer );
841 if ( layerTreeLayer )
844 if ( parentTreeGroup )
846 int index = parentTreeGroup->
children().indexOf( layerTreeLayer );
849 if ( newLayerTreeLayer )
862 updateRelations( layer, newLayer );
863 updateMapThemes( layer, newLayer );
864 updateLayerOrder( layer, newLayer );
872 void QgsOfflineEditing::applyAttributesAdded(
QgsVectorLayer *remoteLayer,
sqlite3 *db,
int layerId,
int commitNo )
874 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 );
875 QList<QgsField> fields = sqlQueryAttributesAdded( db, sql );
878 QList<QgsVectorDataProvider::NativeType> nativeTypes = provider->
nativeTypes();
881 QMap < QVariant::Type, QString > typeNameLookup;
882 for (
int i = 0; i < nativeTypes.size(); i++ )
890 for (
int i = 0; i < fields.size(); i++ )
894 if ( typeNameLookup.contains( field.
type() ) )
902 showWarning( QStringLiteral(
"Could not add attribute '%1' of type %2" ).arg( field.
name() ).arg( field.
type() ) );
911 QString sql = QStringLiteral(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
912 QList<int> featureIdInts = sqlQueryInts( db, sql );
914 Q_FOREACH (
int id, featureIdInts )
935 for ( QgsFeatureList::iterator it = features.begin(); it != features.end(); ++it )
939 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
942 for (
int it = 0; it < attrs.count(); ++it )
944 newAttrs[ attrLookup[ it ] ] = attrs.at( it );
955 void QgsOfflineEditing::applyFeaturesRemoved(
QgsVectorLayer *remoteLayer,
sqlite3 *db,
int layerId )
957 QString sql = QStringLiteral(
"SELECT \"fid\" FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
963 for ( QgsFeatureIds::const_iterator it = values.constBegin(); it != values.constEnd(); ++it )
974 QString sql = QStringLiteral(
"SELECT \"fid\", \"attr\", \"value\" FROM 'log_feature_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2 " ).arg( layerId ).arg( commitNo );
975 AttributeValueChanges values = sqlQueryAttributeValueChanges( db, sql );
979 QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
981 for (
int i = 0; i < values.size(); i++ )
983 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
984 QgsDebugMsgLevel( QStringLiteral(
"Offline changeAttributeValue %1 = %2" ).arg( QString( attrLookup[ values.at( i ).attr ] ), values.at( i ).value ), 4 );
985 remoteLayer->
changeAttributeValue( fid, attrLookup[ values.at( i ).attr ], values.at( i ).value );
991 void QgsOfflineEditing::applyGeometryChanges(
QgsVectorLayer *remoteLayer,
sqlite3 *db,
int layerId,
int commitNo )
993 QString sql = QStringLiteral(
"SELECT \"fid\", \"geom_wkt\" FROM 'log_geometry_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2" ).arg( layerId ).arg( commitNo );
994 GeometryChanges values = sqlQueryGeometryChanges( db, sql );
998 for (
int i = 0; i < values.size(); i++ )
1000 QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
1024 if ( offlineFid( db, layerId, f.
id() ) == -1 )
1026 newRemoteFids[ f.
id()] =
true;
1034 QString sql = QStringLiteral(
"SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
1035 QList<int> newOfflineFids = sqlQueryInts( db, sql );
1037 if ( newRemoteFids.size() != newOfflineFids.size() )
1045 sqlExec( db, QStringLiteral(
"BEGIN" ) );
1046 for ( QMap<QgsFeatureId, bool>::const_iterator it = newRemoteFids.constBegin(); it != newRemoteFids.constEnd(); ++it )
1048 addFidLookup( db, layerId, newOfflineFids.at( i++ ), it.key() );
1050 sqlExec( db, QStringLiteral(
"COMMIT" ) );
1062 if ( error.isEmpty() )
1066 if ( !error.isEmpty() )
1068 showWarning( error );
1075 QList<QgsRelation> relations;
1098 QStringList mapThemeNames = mapThemeCollection->
mapThemes();
1100 Q_FOREACH (
const QString &mapThemeName, mapThemeNames )
1106 if ( layerRecord.
layer() == sourceLayer )
1108 layerRecord.
setLayer( targetLayer );
1122 auto iterator = layerOrder.begin();
1124 while ( iterator != layerOrder.end() )
1126 if ( *iterator == targetLayer )
1128 iterator = layerOrder.erase( iterator );
1129 if ( iterator == layerOrder.end() )
1133 if ( *iterator == sourceLayer )
1135 *iterator = targetLayer;
1149 QMap <
int ,
int > attrLookup;
1152 for (
int i = 0; i < offlineAttrs.size(); i++ )
1161 void QgsOfflineEditing::showWarning(
const QString &message )
1163 emit
warning( tr(
"Offline Editing Plugin" ), message );
1170 if ( !dbPath.isEmpty() )
1173 int rc = database.
open( absoluteDbPath );
1174 if ( rc != SQLITE_OK )
1176 QgsDebugMsg( QStringLiteral(
"Could not open the SpatiaLite logging database" ) );
1177 showWarning( tr(
"Could not open the SpatiaLite logging database" ) );
1182 QgsDebugMsg( QStringLiteral(
"dbPath is empty!" ) );
1187 int QgsOfflineEditing::getOrCreateLayerId(
sqlite3 *db,
const QString &qgisLayerId )
1189 QString sql = QStringLiteral(
"SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
1190 int layerId = sqlQueryInt( db, sql, -1 );
1191 if ( layerId == -1 )
1194 sql = QStringLiteral(
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'layer_id'" );
1195 int newLayerId = sqlQueryInt( db, sql, -1 );
1198 sql = QStringLiteral(
"INSERT INTO 'log_layer_ids' VALUES (%1, '%2')" ).arg( newLayerId ).arg( qgisLayerId );
1203 sql = QStringLiteral(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'layer_id'" ).arg( newLayerId + 1 );
1206 layerId = newLayerId;
1212 int QgsOfflineEditing::getCommitNo(
sqlite3 *db )
1214 QString sql = QStringLiteral(
"SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'commit_no'" );
1215 return sqlQueryInt( db, sql, -1 );
1218 void QgsOfflineEditing::increaseCommitNo(
sqlite3 *db )
1220 QString sql = QStringLiteral(
"UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'commit_no'" ).arg( getCommitNo( db ) + 1 );
1226 QString sql = QStringLiteral(
"INSERT INTO 'log_fids' VALUES ( %1, %2, %3 )" ).arg( layerId ).arg( offlineFid ).arg( remoteFid );
1232 QString sql = QStringLiteral(
"SELECT \"remote_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"offline_fid\" = %2" ).arg( layerId ).arg( offlineFid );
1233 return sqlQueryInt( db, sql, -1 );
1238 QString sql = QStringLiteral(
"SELECT \"offline_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"remote_fid\" = %2" ).arg( layerId ).arg( remoteFid );
1239 return sqlQueryInt( db, sql, -1 );
1244 QString sql = QStringLiteral(
"SELECT COUNT(\"fid\") FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( fid );
1245 return ( sqlQueryInt( db, sql, 0 ) > 0 );
1248 int QgsOfflineEditing::sqlExec(
sqlite3 *db,
const QString &sql )
1250 char *errmsg =
nullptr;
1251 int rc = sqlite3_exec( db, sql.toUtf8(),
nullptr,
nullptr, &errmsg );
1252 if ( rc != SQLITE_OK )
1254 showWarning( errmsg );
1259 int QgsOfflineEditing::sqlQueryInt(
sqlite3 *db,
const QString &sql,
int defaultValue )
1261 sqlite3_stmt *stmt =
nullptr;
1262 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1264 showWarning( sqlite3_errmsg( db ) );
1265 return defaultValue;
1268 int value = defaultValue;
1269 int ret = sqlite3_step( stmt );
1270 if ( ret == SQLITE_ROW )
1272 value = sqlite3_column_int( stmt, 0 );
1274 sqlite3_finalize( stmt );
1279 QList<int> QgsOfflineEditing::sqlQueryInts(
sqlite3 *db,
const QString &sql )
1283 sqlite3_stmt *stmt =
nullptr;
1284 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1286 showWarning( sqlite3_errmsg( db ) );
1290 int ret = sqlite3_step( stmt );
1291 while ( ret == SQLITE_ROW )
1293 values << sqlite3_column_int( stmt, 0 );
1295 ret = sqlite3_step( stmt );
1297 sqlite3_finalize( stmt );
1302 QList<QgsField> QgsOfflineEditing::sqlQueryAttributesAdded(
sqlite3 *db,
const QString &sql )
1304 QList<QgsField> values;
1306 sqlite3_stmt *stmt =
nullptr;
1307 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1309 showWarning( sqlite3_errmsg( db ) );
1313 int ret = sqlite3_step( stmt );
1314 while ( ret == SQLITE_ROW )
1316 QgsField field( QString( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 0 ) ) ),
1317 static_cast< QVariant::Type >( sqlite3_column_int( stmt, 1 ) ),
1319 sqlite3_column_int( stmt, 2 ),
1320 sqlite3_column_int( stmt, 3 ),
1321 QString( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 4 ) ) ) );
1324 ret = sqlite3_step( stmt );
1326 sqlite3_finalize( stmt );
1335 sqlite3_stmt *stmt =
nullptr;
1336 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1338 showWarning( sqlite3_errmsg( db ) );
1342 int ret = sqlite3_step( stmt );
1343 while ( ret == SQLITE_ROW )
1345 values << sqlite3_column_int( stmt, 0 );
1347 ret = sqlite3_step( stmt );
1349 sqlite3_finalize( stmt );
1354 QgsOfflineEditing::AttributeValueChanges QgsOfflineEditing::sqlQueryAttributeValueChanges(
sqlite3 *db,
const QString &sql )
1356 AttributeValueChanges values;
1358 sqlite3_stmt *stmt =
nullptr;
1359 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1361 showWarning( sqlite3_errmsg( db ) );
1365 int ret = sqlite3_step( stmt );
1366 while ( ret == SQLITE_ROW )
1368 AttributeValueChange change;
1369 change.fid = sqlite3_column_int( stmt, 0 );
1370 change.attr = sqlite3_column_int( stmt, 1 );
1371 change.value = QString( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 2 ) ) );
1374 ret = sqlite3_step( stmt );
1376 sqlite3_finalize( stmt );
1381 QgsOfflineEditing::GeometryChanges QgsOfflineEditing::sqlQueryGeometryChanges(
sqlite3 *db,
const QString &sql )
1383 GeometryChanges values;
1385 sqlite3_stmt *stmt =
nullptr;
1386 if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1388 showWarning( sqlite3_errmsg( db ) );
1392 int ret = sqlite3_step( stmt );
1393 while ( ret == SQLITE_ROW )
1395 GeometryChange change;
1396 change.fid = sqlite3_column_int( stmt, 0 );
1397 change.geom_wkt = QString( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 1 ) ) );
1400 ret = sqlite3_step( stmt );
1402 sqlite3_finalize( stmt );
1407 void QgsOfflineEditing::committedAttributesAdded(
const QString &qgisLayerId,
const QList<QgsField> &addedAttributes )
1414 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1415 int commitNo = getCommitNo( database.get() );
1417 for ( QList<QgsField>::const_iterator it = addedAttributes.begin(); it != addedAttributes.end(); ++it )
1420 QString sql = QStringLiteral(
"INSERT INTO 'log_added_attrs' VALUES ( %1, %2, '%3', %4, %5, %6, '%7' )" )
1423 .arg( field.
name() )
1424 .arg( field.
type() )
1428 sqlExec( database.get(), sql );
1431 increaseCommitNo( database.get() );
1434 void QgsOfflineEditing::committedFeaturesAdded(
const QString &qgisLayerId,
const QgsFeatureList &addedFeatures )
1441 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1450 if ( !offlinePath.contains(
".gpkg" ) )
1452 tableName = uri.
table();
1456 tableName = uri.
param( offlinePath +
"|layername" );
1460 QString sql = QStringLiteral(
"SELECT ROWID FROM '%1' ORDER BY ROWID DESC LIMIT %2" ).arg( tableName ).arg( addedFeatures.size() );
1461 QList<int> newFeatureIds = sqlQueryInts( database.get(), sql );
1462 for (
int i = newFeatureIds.size() - 1; i >= 0; i-- )
1464 QString sql = QStringLiteral(
"INSERT INTO 'log_added_features' VALUES ( %1, %2 )" )
1466 .arg( newFeatureIds.at( i ) );
1467 sqlExec( database.get(), sql );
1471 void QgsOfflineEditing::committedFeaturesRemoved(
const QString &qgisLayerId,
const QgsFeatureIds &deletedFeatureIds )
1478 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1480 for ( QgsFeatureIds::const_iterator it = deletedFeatureIds.begin(); it != deletedFeatureIds.end(); ++it )
1482 if ( isAddedFeature( database.get(), layerId, *it ) )
1485 QString sql = QStringLiteral(
"DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( *it );
1486 sqlExec( database.get(), sql );
1490 QString sql = QStringLiteral(
"INSERT INTO 'log_removed_features' VALUES ( %1, %2)" )
1493 sqlExec( database.get(), sql );
1498 void QgsOfflineEditing::committedAttributeValuesChanges(
const QString &qgisLayerId,
const QgsChangedAttributesMap &changedAttrsMap )
1505 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1506 int commitNo = getCommitNo( database.get() );
1508 for ( QgsChangedAttributesMap::const_iterator cit = changedAttrsMap.begin(); cit != changedAttrsMap.end(); ++cit )
1511 if ( isAddedFeature( database.get(), layerId, fid ) )
1517 for ( QgsAttributeMap::const_iterator it = attrMap.constBegin(); it != attrMap.constEnd(); ++it )
1519 QString sql = QStringLiteral(
"INSERT INTO 'log_feature_updates' VALUES ( %1, %2, %3, %4, '%5' )" )
1524 .arg( it.value().toString() );
1525 sqlExec( database.get(), sql );
1529 increaseCommitNo( database.get() );
1532 void QgsOfflineEditing::committedGeometriesChanges(
const QString &qgisLayerId,
const QgsGeometryMap &changedGeometries )
1539 int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1540 int commitNo = getCommitNo( database.get() );
1542 for ( QgsGeometryMap::const_iterator it = changedGeometries.begin(); it != changedGeometries.end(); ++it )
1545 if ( isAddedFeature( database.get(), layerId, fid ) )
1551 QString sql = QStringLiteral(
"INSERT INTO 'log_geometry_updates' VALUES ( %1, %2, %3, '%4' )" )
1555 .arg( geom.
asWkt() );
1556 sqlExec( database.get(), sql );
1561 increaseCommitNo( database.get() );
1564 void QgsOfflineEditing::startListenFeatureChanges()
1572 this, &QgsOfflineEditing::committedAttributesAdded );
1574 this, &QgsOfflineEditing::committedAttributeValuesChanges );
1576 this, &QgsOfflineEditing::committedGeometriesChanges );
1579 this, &QgsOfflineEditing::committedFeaturesAdded );
1581 this, &QgsOfflineEditing::committedFeaturesRemoved );
1584 void QgsOfflineEditing::stopListenFeatureChanges()
1592 this, &QgsOfflineEditing::committedAttributesAdded );
1594 this, &QgsOfflineEditing::committedAttributeValuesChanges );
1596 this, &QgsOfflineEditing::committedGeometriesChanges );
1599 this, &QgsOfflineEditing::committedFeaturesAdded );
1601 this, &QgsOfflineEditing::committedFeaturesRemoved );
1604 void QgsOfflineEditing::layerAdded(
QgsMapLayer *layer )
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QString param(const QString &key) const
Gets generic param (generic mode)
Layer tree group node serves as a container for layers and further groups.
void setJoinLayer(QgsVectorLayer *layer)
Sets weak reference to the joined layer.
The class is used as a container of context for various read/write operations on other objects...
Wrapper for iterator of features from vector data provider or vector layer.
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
void layerProgressUpdated(int layer, int numLayers)
Is emitted whenever a new layer is being processed.
int open(const QString &path)
Opens the database at the specified file path.
bool addJoin(const QgsVectorLayerJoinInfo &joinInfo)
Joins another vector layer to this layer.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer *> &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
QgsMapLayer * layer() const
Returns map layer or null if the layer does not exist anymore.
Base class for all map layer types.
void setLayer(QgsMapLayer *layer)
Sets the map layer for this record.
QString table() const
Returns the table.
Filter using feature IDs.
QSet< QgsFeatureId > QgsFeatureIds
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
virtual bool importNamedStyle(QDomDocument &doc, QString &errorMsg, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Import the properties of this layer from a QDomDocument.
#define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH
QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
bool deleteFeature(QgsFeatureId fid)
Deletes a feature from the layer (but does not commit it).
void committedAttributesAdded(const QString &layerId, const QList< QgsField > &addedAttributes)
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.e.
QgsMapThemeCollection::MapThemeRecord mapThemeState(const QString &name) const
Returns the recorded state of a map theme.
QList< QgsFeature > QgsFeatureList
#define CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE
Individual map theme record of visible layers and styles.
bool commitChanges()
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
QString providerType() const
Returns the provider type (provider key) for this layer.
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
void update(const QString &name, const QgsMapThemeCollection::MapThemeRecord &state)
Updates a map theme within the collection.
bool startEditing()
Makes the layer editable.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
#define CUSTOM_PROPERTY_REMOTE_SOURCE
FilterType filterType() const
Returns the filter type which is currently set on this request.
Container of fields for a vector layer.
A geometry is the spatial representation of a feature.
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group.
Individual record of a visible layer in a map theme record.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
QList< QgsVectorDataProvider::NativeType > nativeTypes() const
Returns the names of the supported types.
bool isValid() const
Returns the status of the layer.
bool isOfflineProject() const
Returns true if current project is offline.
void committedFeaturesRemoved(const QString &layerId, const QgsFeatureIds &deletedFeatureIds)
This signal is emitted, when features are deleted from the provider.
int count() const
Returns number of items.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
virtual QString name() const =0
Returns a provider name.
void removeLayerRecord(QgsMapLayer *layer)
Removes a record for layer if present.
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean entry to the project file.
void progressUpdated(int progress)
Emitted with the progress of the current mode.
void committedGeometriesChanges(const QString &layerId, const QgsGeometryMap &changedGeometries)
QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on...
Type
The WKB type describes the number of dimensions a geometry has.
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsMapThemeCollection mapThemeCollection
void progressStopped()
Emitted when the processing of all layers has finished.
const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
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...
Unique pointer for spatialite databases, which automatically closes the database when the pointer goe...
#define QgsDebugMsgLevel(str, level)
void reload() FINAL
Synchronises with changes in the datasource.
QString dataComment() const
Returns a description for this layer as defined in the data provider.
QgsFields fields() const override=0
Returns the fields associated with this data provider.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void setTypeName(const QString &typeName)
Set the field type.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is a null pointer, the node is a root node. ...
void committedFeaturesAdded(const QString &layerId, const QgsFeatureList &addedFeatures)
This signal is emitted, when features are added to the provider.
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.
Defines left outer join from our vector layer to some other vector layer.
QMap< int, QVariant > QgsAttributeMap
void editingStopped()
Is emitted, when edited changes successfully have been written to the data provider.
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...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
This class is a base class for nodes in a layer tree.
ContainerType
Type of offline database container file.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key.
QgsAttributeMap toMap() const
Returns a QgsAttributeMap of the attribute values.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
QList< QgsRelation > referencedRelations(QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
Encapsulate a field in an attribute table or data source.
QgsRelationManager relationManager
void editingStarted()
Is emitted, when editing on this layer has started.
QgsExpressionContext createExpressionContext() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
int open(const QString &path)
Opens the database at the specified file path.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
#define PROJECT_ENTRY_SCOPE_OFFLINE
void setReferencedLayer(const QString &id)
Set the referenced (parent) layer id.
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.
QString asWkt(int precision=17) const
Exports the geometry to WKT.
QStringList commitErrors() const
Returns a list containing any error messages generated when attempting to commit changes to the layer...
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
#define CUSTOM_SHOW_FEATURE_COUNT
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool convertToOfflineProject(const QString &offlineDataPath, const QString &offlineDbFile, const QStringList &layerIds, bool onlySelected=false, ContainerType containerType=SpatiaLite)
Convert current project for offline editing.
void warning(const QString &title, const QString &message)
Emitted when a warning needs to be displayed.
Custom properties (by plugins for instance)
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
void setCustomLayerOrder(const QList< QgsMapLayer *> &customLayerOrder)
The order in which layers will be rendered on the canvas.
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position.
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override=0
Query the provider for features specified in request.
QString source() const
Returns the source for the layer.
long featureCount() const override=0
Number of features in the layer.
QList< QgsMapThemeCollection::MapThemeLayerRecord > layerRecords() const
Returns a list of records for all visible layer belonging to the theme.
void synchronize()
Synchronize to remote layers.
This class manages a set of relations between layers.
void committedAttributeValuesChanges(const QString &layerId, const QgsChangedAttributesMap &changedAttributesValues)
static QgsProject * instance()
Returns the QgsProject singleton instance.
QString toWkt() const
Returns a WKT representation of this CRS.
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
void setTitle(const QString &title)
Sets the project's title.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) FINAL
Adds a single feature to the sink.
static QString displayString(Type type)
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
void progressModeSet(QgsOfflineEditing::ProgressMode mode, int maximum)
Is emitted when the mode for the progress of the current operation is set.
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void removeMapLayers(const QStringList &layerIds)
Remove a set of layers from the registry by layer ID.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
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).
void removeRelation(const QString &id)
Remove a relation.
Container class that allows storage of map themes consisting of visible map layers and layer styles...
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QString title() const
Returns the project's title.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be null.
QList< int > QgsAttributeList
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
QList< QgsVectorLayerJoinInfo > QgsVectorJoinList
bool nextFeature(QgsFeature &f)
This is the base class for vector data providers.
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
virtual void invalidateConnections(const QString &connection)
Invalidate connections corresponding to specified name.
Represents a vector layer which manages a vector based data sets.
bool addAttribute(const QgsField &field)
Add an attribute field (but does not commit it) returns true if the field was added.
static Type flatType(Type type)
Returns the flat type for a WKB type.
#define CUSTOM_PROPERTY_REMOTE_PROVIDER
void progressStarted()
The signal is emitted when the process has started.
QgsField field(int fieldIdx) const
Gets field at particular index (must be in range 0..N-1)
QgsLayerTreeLayer * clone() const override
Create a copy of the node. Returns new instance.
void addLayerRecord(const QgsMapThemeCollection::MapThemeLayerRecord &record)
Add a new record for a layer.
QString authid() const
Returns the authority identifier for the CRS.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
QgsCoordinateReferenceSystem crs
void * OGRSpatialReferenceH
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the registry.
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...
Layer tree node points to a map layer.
QString joinLayerId() const
ID of the joined layer - may be used to resolve reference to the joined layer.
void addRelation(const QgsRelation &relation)
Add a relation.
QString errorMessage() const
Returns the most recent error message encountered by the database.