QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsofflineediting.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  offline_editing.cpp
3 
4  Offline Editing Plugin
5  a QGIS plugin
6  --------------------------------------
7  Date : 22-Jul-2010
8  Copyright : (C) 2010 by Sourcepole
9  Email : info at sourcepole.ch
10  ***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
19 
20 #include "qgsapplication.h"
21 #include "qgsdatasourceuri.h"
22 #include "qgsgeometry.h"
23 #include "qgslayertreegroup.h"
24 #include "qgslayertreelayer.h"
25 #include "qgsmaplayer.h"
26 #include "qgsofflineediting.h"
27 #include "qgsproject.h"
28 #include "qgsvectordataprovider.h"
31 #include "qgsspatialiteutils.h"
32 #include "qgsfeatureiterator.h"
33 #include "qgslogger.h"
34 #include "qgsvectorlayerutils.h"
35 #include "qgsrelationmanager.h"
36 #include "qgsmapthemecollection.h"
37 #include "qgslayertree.h"
38 #include "qgsogrutils.h"
39 #include "qgsvectorfilewriter.h"
40 #include "qgsvectorlayer.h"
41 #include "qgsproviderregistry.h"
42 #include "qgsprovidermetadata.h"
44 
45 #include <QDir>
46 #include <QDomDocument>
47 #include <QDomNode>
48 #include <QFile>
49 #include <QMessageBox>
50 
51 #include <ogr_srs_api.h>
52 
53 extern "C"
54 {
55 #include <sqlite3.h>
56 #include <spatialite.h>
57 }
58 
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 PROJECT_ENTRY_SCOPE_OFFLINE "OfflineEditingPlugin"
65 #define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH "/OfflineDbPath"
66 
68 {
69  connect( QgsProject::instance(), &QgsProject::layerWasAdded, this, &QgsOfflineEditing::layerAdded );
70 }
71 
88 bool QgsOfflineEditing::convertToOfflineProject( const QString &offlineDataPath, const QString &offlineDbFile, const QStringList &layerIds, bool onlySelected, ContainerType containerType )
89 {
90  if ( layerIds.isEmpty() )
91  {
92  return false;
93  }
94 
95  QString dbPath = QDir( offlineDataPath ).absoluteFilePath( offlineDbFile );
96  if ( createOfflineDb( dbPath, containerType ) )
97  {
99  int rc = database.open( dbPath );
100  if ( rc != SQLITE_OK )
101  {
102  showWarning( tr( "Could not open the SpatiaLite database" ) );
103  }
104  else
105  {
106  // create logging tables
107  createLoggingTables( database.get() );
108 
109  emit progressStarted();
110 
111  QMap<QString, QgsVectorJoinList > joinInfoBuffer;
112  QMap<QString, QgsVectorLayer *> layerIdMapping;
113 
114  for ( const QString &layerId : layerIds )
115  {
116  QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerId );
117  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
118  if ( !vl )
119  continue;
120  QgsVectorJoinList joins = vl->vectorJoins();
121 
122  // Layer names will be appended an _offline suffix
123  // Join fields are prefixed with the layer name and we do not want the
124  // field name to change so we stabilize the field name by defining a
125  // custom prefix with the layername without _offline suffix.
126  QgsVectorJoinList::iterator joinIt = joins.begin();
127  while ( joinIt != joins.end() )
128  {
129  if ( joinIt->prefix().isNull() )
130  {
131  QgsVectorLayer *vl = joinIt->joinLayer();
132 
133  if ( vl )
134  joinIt->setPrefix( vl->name() + '_' );
135  }
136  ++joinIt;
137  }
138  joinInfoBuffer.insert( vl->id(), joins );
139  }
140 
142 
143  // copy selected vector layers to offline layer
144  for ( int i = 0; i < layerIds.count(); i++ )
145  {
146  emit layerProgressUpdated( i + 1, layerIds.count() );
147 
148  QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerIds.at( i ) );
149  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
150  if ( vl )
151  {
152  QString origLayerId = vl->id();
153  QgsVectorLayer *newLayer = copyVectorLayer( vl, database.get(), dbPath, onlySelected, containerType );
154  if ( newLayer )
155  {
156  layerIdMapping.insert( origLayerId, newLayer );
157  //append individual layer setting on snapping settings
158  snappingConfig.setIndividualLayerSettings( newLayer, snappingConfig.individualLayerSettings( vl ) );
159  snappingConfig.removeLayers( QList<QgsMapLayer *>() << vl );
160 
161  // remove remote layer
163  QStringList() << origLayerId );
164  }
165  }
166  }
167 
168  QgsProject::instance()->setSnappingConfig( snappingConfig );
169 
170  // restore join info on new offline layer
171  QMap<QString, QgsVectorJoinList >::ConstIterator it;
172  for ( it = joinInfoBuffer.constBegin(); it != joinInfoBuffer.constEnd(); ++it )
173  {
174  QgsVectorLayer *newLayer = layerIdMapping.value( it.key() );
175 
176  if ( newLayer )
177  {
178  const QList<QgsVectorLayerJoinInfo> joins = it.value();
179  for ( QgsVectorLayerJoinInfo join : joins )
180  {
181  QgsVectorLayer *newJoinedLayer = layerIdMapping.value( join.joinLayerId() );
182  if ( newJoinedLayer )
183  {
184  // If the layer has been offline'd, update join information
185  join.setJoinLayer( newJoinedLayer );
186  }
187  newLayer->addJoin( join );
188  }
189  }
190  }
191 
192  emit progressStopped();
193 
194  // save offline project
195  QString projectTitle = QgsProject::instance()->title();
196  if ( projectTitle.isEmpty() )
197  {
198  projectTitle = QFileInfo( QgsProject::instance()->fileName() ).fileName();
199  }
200  projectTitle += QLatin1String( " (offline)" );
201  QgsProject::instance()->setTitle( projectTitle );
202 
204 
205  return true;
206  }
207  }
208 
209  return false;
210 }
211 
213 {
215 }
216 
218 {
219  // open logging db
220  sqlite3_database_unique_ptr database = openLoggingDb();
221  if ( !database )
222  {
223  return;
224  }
225 
226  emit progressStarted();
227 
229 
230  // restore and sync remote layers
231  QList<QgsMapLayer *> offlineLayers;
232  QMap<QString, QgsMapLayer *> mapLayers = QgsProject::instance()->mapLayers();
233  for ( QMap<QString, QgsMapLayer *>::iterator layer_it = mapLayers.begin() ; layer_it != mapLayers.end(); ++layer_it )
234  {
235  QgsMapLayer *layer = layer_it.value();
236  if ( layer->customProperty( CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE, false ).toBool() )
237  {
238  offlineLayers << layer;
239  }
240  }
241 
242  QgsDebugMsgLevel( QStringLiteral( "Found %1 offline layers" ).arg( offlineLayers.count() ), 4 );
243  for ( int l = 0; l < offlineLayers.count(); l++ )
244  {
245  QgsMapLayer *layer = offlineLayers.at( l );
246 
247  emit layerProgressUpdated( l + 1, offlineLayers.count() );
248 
249  QString remoteSource = layer->customProperty( CUSTOM_PROPERTY_REMOTE_SOURCE, "" ).toString();
250  QString remoteProvider = layer->customProperty( CUSTOM_PROPERTY_REMOTE_PROVIDER, "" ).toString();
251  QString remoteName = layer->name();
252  remoteName.remove( QRegExp( " \\(offline\\)$" ) );
254  QgsVectorLayer *remoteLayer = new QgsVectorLayer( remoteSource, remoteName, remoteProvider, options );
255  if ( remoteLayer->isValid() )
256  {
257  // Rebuild WFS cache to get feature id<->GML fid mapping
258  if ( remoteLayer->providerType().contains( QLatin1String( "WFS" ), Qt::CaseInsensitive ) )
259  {
260  QgsFeatureIterator fit = remoteLayer->getFeatures();
261  QgsFeature f;
262  while ( fit.nextFeature( f ) )
263  {
264  }
265  }
266  // TODO: only add remote layer if there are log entries?
267 
268  QgsVectorLayer *offlineLayer = qobject_cast<QgsVectorLayer *>( layer );
269 
270  // register this layer with the central layers registry
271  QgsProject::instance()->addMapLayers( QList<QgsMapLayer *>() << remoteLayer, true );
272 
273  // copy style
274  copySymbology( offlineLayer, remoteLayer );
275  updateRelations( offlineLayer, remoteLayer );
276  updateMapThemes( offlineLayer, remoteLayer );
277  updateLayerOrder( offlineLayer, remoteLayer );
278 
279  //append individual layer setting on snapping settings
280  snappingConfig.setIndividualLayerSettings( remoteLayer, snappingConfig.individualLayerSettings( offlineLayer ) );
281  snappingConfig.removeLayers( QList<QgsMapLayer *>() << offlineLayer );
282 
283  //set QgsLayerTreeNode properties back
284  QgsLayerTreeLayer *layerTreeLayer = QgsProject::instance()->layerTreeRoot()->findLayer( offlineLayer->id() );
285  QgsLayerTreeLayer *newLayerTreeLayer = QgsProject::instance()->layerTreeRoot()->findLayer( remoteLayer->id() );
287 
288  // apply layer edit log
289  QString qgisLayerId = layer->id();
290  QString sql = QStringLiteral( "SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
291  int layerId = sqlQueryInt( database.get(), sql, -1 );
292  if ( layerId != -1 )
293  {
294  remoteLayer->startEditing();
295 
296  // TODO: only get commitNos of this layer?
297  int commitNo = getCommitNo( database.get() );
298  QgsDebugMsgLevel( QStringLiteral( "Found %1 commits" ).arg( commitNo ), 4 );
299  for ( int i = 0; i < commitNo; i++ )
300  {
301  QgsDebugMsgLevel( QStringLiteral( "Apply commits chronologically" ), 4 );
302  // apply commits chronologically
303  applyAttributesAdded( remoteLayer, database.get(), layerId, i );
304  applyAttributeValueChanges( offlineLayer, remoteLayer, database.get(), layerId, i );
305  applyGeometryChanges( remoteLayer, database.get(), layerId, i );
306  }
307 
308  applyFeaturesAdded( offlineLayer, remoteLayer, database.get(), layerId );
309  applyFeaturesRemoved( remoteLayer, database.get(), layerId );
310 
311  if ( remoteLayer->commitChanges() )
312  {
313  // update fid lookup
314  updateFidLookup( remoteLayer, database.get(), layerId );
315 
316  // clear edit log for this layer
317  sql = QStringLiteral( "DELETE FROM 'log_added_attrs' WHERE \"layer_id\" = %1" ).arg( layerId );
318  sqlExec( database.get(), sql );
319  sql = QStringLiteral( "DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
320  sqlExec( database.get(), sql );
321  sql = QStringLiteral( "DELETE FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
322  sqlExec( database.get(), sql );
323  sql = QStringLiteral( "DELETE FROM 'log_feature_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
324  sqlExec( database.get(), sql );
325  sql = QStringLiteral( "DELETE FROM 'log_geometry_updates' WHERE \"layer_id\" = %1" ).arg( layerId );
326  sqlExec( database.get(), sql );
327  }
328  else
329  {
330  showWarning( remoteLayer->commitErrors().join( QLatin1Char( '\n' ) ) );
331  }
332  }
333  else
334  {
335  QgsDebugMsg( QStringLiteral( "Could not find the layer id in the edit logs!" ) );
336  }
337  // Invalidate the connection to force a reload if the project is put offline
338  // again with the same path
339  offlineLayer->dataProvider()->invalidateConnections( QgsDataSourceUri( offlineLayer->source() ).database() );
340  // remove offline layer
341  QgsProject::instance()->removeMapLayers( QStringList() << qgisLayerId );
342 
343 
344  // disable offline project
345  QString projectTitle = QgsProject::instance()->title();
346  projectTitle.remove( QRegExp( " \\(offline\\)$" ) );
347  QgsProject::instance()->setTitle( projectTitle );
349  remoteLayer->reload(); //update with other changes
350  }
351  else
352  {
353  QgsDebugMsg( QStringLiteral( "Remote layer is not valid!" ) );
354  }
355  }
356 
357  // reset commitNo
358  QString sql = QStringLiteral( "UPDATE 'log_indices' SET 'last_index' = 0 WHERE \"name\" = 'commit_no'" );
359  sqlExec( database.get(), sql );
360 
361  QgsProject::instance()->setSnappingConfig( snappingConfig );
362 
363  emit progressStopped();
364 }
365 
366 void QgsOfflineEditing::initializeSpatialMetadata( sqlite3 *sqlite_handle )
367 {
368  // attempting to perform self-initialization for a newly created DB
369  if ( !sqlite_handle )
370  return;
371  // checking if this DB is really empty
372  char **results = nullptr;
373  int rows, columns;
374  int ret = sqlite3_get_table( sqlite_handle, "select count(*) from sqlite_master", &results, &rows, &columns, nullptr );
375  if ( ret != SQLITE_OK )
376  return;
377  int count = 0;
378  if ( rows >= 1 )
379  {
380  for ( int i = 1; i <= rows; i++ )
381  count = atoi( results[( i * columns ) + 0] );
382  }
383 
384  sqlite3_free_table( results );
385 
386  if ( count > 0 )
387  return;
388 
389  bool above41 = false;
390  ret = sqlite3_get_table( sqlite_handle, "select spatialite_version()", &results, &rows, &columns, nullptr );
391  if ( ret == SQLITE_OK && rows == 1 && columns == 1 )
392  {
393  QString version = QString::fromUtf8( results[1] );
394 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
395  QStringList parts = version.split( ' ', QString::SkipEmptyParts );
396 #else
397  QStringList parts = version.split( ' ', Qt::SkipEmptyParts );
398 #endif
399  if ( !parts.empty() )
400  {
401 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
402  QStringList verparts = parts.at( 0 ).split( '.', QString::SkipEmptyParts );
403 #else
404  QStringList verparts = parts.at( 0 ).split( '.', Qt::SkipEmptyParts );
405 #endif
406  above41 = verparts.size() >= 2 && ( verparts.at( 0 ).toInt() > 4 || ( verparts.at( 0 ).toInt() == 4 && verparts.at( 1 ).toInt() >= 1 ) );
407  }
408  }
409 
410  sqlite3_free_table( results );
411 
412  // all right, it's empty: proceeding to initialize
413  char *errMsg = nullptr;
414  ret = sqlite3_exec( sqlite_handle, above41 ? "SELECT InitSpatialMetadata(1)" : "SELECT InitSpatialMetadata()", nullptr, nullptr, &errMsg );
415 
416  if ( ret != SQLITE_OK )
417  {
418  QString errCause = tr( "Unable to initialize SpatialMetadata:\n" );
419  errCause += QString::fromUtf8( errMsg );
420  showWarning( errCause );
421  sqlite3_free( errMsg );
422  return;
423  }
424  spatial_ref_sys_init( sqlite_handle, 0 );
425 }
426 
427 bool QgsOfflineEditing::createOfflineDb( const QString &offlineDbPath, ContainerType containerType )
428 {
429  int ret;
430  char *errMsg = nullptr;
431  QFile newDb( offlineDbPath );
432  if ( newDb.exists() )
433  {
434  QFile::remove( offlineDbPath );
435  }
436 
437  // see also QgsNewSpatialiteLayerDialog::createDb()
438 
439  QFileInfo fullPath = QFileInfo( offlineDbPath );
440  QDir path = fullPath.dir();
441 
442  // Must be sure there is destination directory ~/.qgis
443  QDir().mkpath( path.absolutePath() );
444 
445  // creating/opening the new database
446  QString dbPath = newDb.fileName();
447 
448  // creating geopackage
449  switch ( containerType )
450  {
451  case GPKG:
452  {
453  OGRSFDriverH hGpkgDriver = OGRGetDriverByName( "GPKG" );
454  if ( !hGpkgDriver )
455  {
456  showWarning( tr( "Creation of database failed. GeoPackage driver not found." ) );
457  return false;
458  }
459 
460  gdal::ogr_datasource_unique_ptr hDS( OGR_Dr_CreateDataSource( hGpkgDriver, dbPath.toUtf8().constData(), nullptr ) );
461  if ( !hDS )
462  {
463  showWarning( tr( "Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
464  return false;
465  }
466  break;
467  }
468  case SpatiaLite:
469  {
470  break;
471  }
472  }
473 
475  ret = database.open_v2( dbPath, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr );
476  if ( ret )
477  {
478  // an error occurred
479  QString errCause = tr( "Could not create a new database\n" );
480  errCause += database.errorMessage();
481  showWarning( errCause );
482  return false;
483  }
484  // activating Foreign Key constraints
485  ret = sqlite3_exec( database.get(), "PRAGMA foreign_keys = 1", nullptr, nullptr, &errMsg );
486  if ( ret != SQLITE_OK )
487  {
488  showWarning( tr( "Unable to activate FOREIGN_KEY constraints" ) );
489  sqlite3_free( errMsg );
490  return false;
491  }
492  initializeSpatialMetadata( database.get() );
493  return true;
494 }
495 
496 void QgsOfflineEditing::createLoggingTables( sqlite3 *db )
497 {
498  // indices
499  QString sql = QStringLiteral( "CREATE TABLE 'log_indices' ('name' TEXT, 'last_index' INTEGER)" );
500  sqlExec( db, sql );
501 
502  sql = QStringLiteral( "INSERT INTO 'log_indices' VALUES ('commit_no', 0)" );
503  sqlExec( db, sql );
504 
505  sql = QStringLiteral( "INSERT INTO 'log_indices' VALUES ('layer_id', 0)" );
506  sqlExec( db, sql );
507 
508  // layername <-> layer id
509  sql = QStringLiteral( "CREATE TABLE 'log_layer_ids' ('id' INTEGER, 'qgis_id' TEXT)" );
510  sqlExec( db, sql );
511 
512  // offline fid <-> remote fid
513  sql = QStringLiteral( "CREATE TABLE 'log_fids' ('layer_id' INTEGER, 'offline_fid' INTEGER, 'remote_fid' INTEGER)" );
514  sqlExec( db, sql );
515 
516  // added attributes
517  sql = QStringLiteral( "CREATE TABLE 'log_added_attrs' ('layer_id' INTEGER, 'commit_no' INTEGER, " );
518  sql += QLatin1String( "'name' TEXT, 'type' INTEGER, 'length' INTEGER, 'precision' INTEGER, 'comment' TEXT)" );
519  sqlExec( db, sql );
520 
521  // added features
522  sql = QStringLiteral( "CREATE TABLE 'log_added_features' ('layer_id' INTEGER, 'fid' INTEGER)" );
523  sqlExec( db, sql );
524 
525  // removed features
526  sql = QStringLiteral( "CREATE TABLE 'log_removed_features' ('layer_id' INTEGER, 'fid' INTEGER)" );
527  sqlExec( db, sql );
528 
529  // feature updates
530  sql = QStringLiteral( "CREATE TABLE 'log_feature_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'attr' INTEGER, 'value' TEXT)" );
531  sqlExec( db, sql );
532 
533  // geometry updates
534  sql = QStringLiteral( "CREATE TABLE 'log_geometry_updates' ('layer_id' INTEGER, 'commit_no' INTEGER, 'fid' INTEGER, 'geom_wkt' TEXT)" );
535  sqlExec( db, sql );
536 
537  /* TODO: other logging tables
538  - attr delete (not supported by SpatiaLite provider)
539  */
540 }
541 
542 QgsVectorLayer *QgsOfflineEditing::copyVectorLayer( QgsVectorLayer *layer, sqlite3 *db, const QString &offlineDbPath, bool onlySelected, ContainerType containerType )
543 {
544  if ( !layer )
545  return nullptr;
546 
547  QString tableName = layer->id();
548  QgsDebugMsgLevel( QStringLiteral( "Creating offline table %1 ..." ).arg( tableName ), 4 );
549 
550  // new layer
551  QgsVectorLayer *newLayer = nullptr;
552 
553  switch ( containerType )
554  {
555  case SpatiaLite:
556  {
557  // create table
558  QString sql = QStringLiteral( "CREATE TABLE '%1' (" ).arg( tableName );
559  QString delim;
560  const QgsFields providerFields = layer->dataProvider()->fields();
561  for ( const auto &field : providerFields )
562  {
563  QString dataType;
564  QVariant::Type type = field.type();
565  if ( type == QVariant::Int || type == QVariant::LongLong )
566  {
567  dataType = QStringLiteral( "INTEGER" );
568  }
569  else if ( type == QVariant::Double )
570  {
571  dataType = QStringLiteral( "REAL" );
572  }
573  else if ( type == QVariant::String )
574  {
575  dataType = QStringLiteral( "TEXT" );
576  }
577  else
578  {
579  showWarning( tr( "%1: Unknown data type %2. Not using type affinity for the field." ).arg( field.name(), QVariant::typeToName( type ) ) );
580  }
581 
582  sql += delim + QStringLiteral( "'%1' %2" ).arg( field.name(), dataType );
583  delim = ',';
584  }
585  sql += ')';
586 
587  int rc = sqlExec( db, sql );
588 
589  // add geometry column
590  if ( layer->isSpatial() )
591  {
592  const QgsWkbTypes::Type sourceWkbType = layer->wkbType();
593 
594  QString geomType;
595  switch ( QgsWkbTypes::flatType( sourceWkbType ) )
596  {
597  case QgsWkbTypes::Point:
598  geomType = QStringLiteral( "POINT" );
599  break;
601  geomType = QStringLiteral( "MULTIPOINT" );
602  break;
604  geomType = QStringLiteral( "LINESTRING" );
605  break;
607  geomType = QStringLiteral( "MULTILINESTRING" );
608  break;
610  geomType = QStringLiteral( "POLYGON" );
611  break;
613  geomType = QStringLiteral( "MULTIPOLYGON" );
614  break;
615  default:
616  showWarning( tr( "Layer %1 has unsupported geometry type %2." ).arg( layer->name(), QgsWkbTypes::displayString( layer->wkbType() ) ) );
617  break;
618  };
619 
620  QString zmInfo = QStringLiteral( "XY" );
621 
622  if ( QgsWkbTypes::hasZ( sourceWkbType ) )
623  zmInfo += 'Z';
624  if ( QgsWkbTypes::hasM( sourceWkbType ) )
625  zmInfo += 'M';
626 
627  QString epsgCode;
628 
629  if ( layer->crs().authid().startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
630  {
631  epsgCode = layer->crs().authid().mid( 5 );
632  }
633  else
634  {
635  epsgCode = '0';
636  showWarning( tr( "Layer %1 has unsupported Coordinate Reference System (%2)." ).arg( layer->name(), layer->crs().authid() ) );
637  }
638 
639  QString sqlAddGeom = QStringLiteral( "SELECT AddGeometryColumn('%1', 'Geometry', %2, '%3', '%4')" )
640  .arg( tableName, epsgCode, geomType, zmInfo );
641 
642  // create spatial index
643  QString sqlCreateIndex = QStringLiteral( "SELECT CreateSpatialIndex('%1', 'Geometry')" ).arg( tableName );
644 
645  if ( rc == SQLITE_OK )
646  {
647  rc = sqlExec( db, sqlAddGeom );
648  if ( rc == SQLITE_OK )
649  {
650  rc = sqlExec( db, sqlCreateIndex );
651  }
652  }
653  }
654 
655  if ( rc != SQLITE_OK )
656  {
657  showWarning( tr( "Filling SpatiaLite for layer %1 failed" ).arg( layer->name() ) );
658  return nullptr;
659  }
660 
661  // add new layer
662  QString connectionString = QStringLiteral( "dbname='%1' table='%2'%3 sql=" )
663  .arg( offlineDbPath,
664  tableName, layer->isSpatial() ? "(Geometry)" : "" );
666  newLayer = new QgsVectorLayer( connectionString,
667  layer->name() + " (offline)", QStringLiteral( "spatialite" ), options );
668  break;
669  }
670  case GPKG:
671  {
672  // Set options
673  char **options = nullptr;
674 
675  options = CSLSetNameValue( options, "OVERWRITE", "YES" );
676  options = CSLSetNameValue( options, "IDENTIFIER", tr( "%1 (offline)" ).arg( layer->id() ).toUtf8().constData() );
677  options = CSLSetNameValue( options, "DESCRIPTION", layer->dataComment().toUtf8().constData() );
678 
679  //the FID-name should not exist in the original data
680  QString fidBase( QStringLiteral( "fid" ) );
681  QString fid = fidBase;
682  int counter = 1;
683  while ( layer->dataProvider()->fields().lookupField( fid ) >= 0 && counter < 10000 )
684  {
685  fid = fidBase + '_' + QString::number( counter );
686  counter++;
687  }
688  if ( counter == 10000 )
689  {
690  showWarning( tr( "Cannot make FID-name for GPKG " ) );
691  return nullptr;
692  }
693 
694  options = CSLSetNameValue( options, "FID", fid.toUtf8().constData() );
695 
696  if ( layer->isSpatial() )
697  {
698  options = CSLSetNameValue( options, "GEOMETRY_COLUMN", "geom" );
699  options = CSLSetNameValue( options, "SPATIAL_INDEX", "YES" );
700  }
701 
702  OGRSFDriverH hDriver = nullptr;
703  OGRSpatialReferenceH hSRS = OSRNewSpatialReference( layer->crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLocal8Bit().data() );
704  gdal::ogr_datasource_unique_ptr hDS( OGROpen( offlineDbPath.toUtf8().constData(), true, &hDriver ) );
705  OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS, static_cast<OGRwkbGeometryType>( layer->wkbType() ), options );
706  CSLDestroy( options );
707  if ( hSRS )
708  OSRRelease( hSRS );
709  if ( !hLayer )
710  {
711  showWarning( tr( "Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
712  return nullptr;
713  }
714 
715  const QgsFields providerFields = layer->dataProvider()->fields();
716  for ( const auto &field : providerFields )
717  {
718  const QString fieldName( field.name() );
719  const QVariant::Type type = field.type();
720  OGRFieldType ogrType( OFTString );
721  OGRFieldSubType ogrSubType = OFSTNone;
722  if ( type == QVariant::Int )
723  ogrType = OFTInteger;
724  else if ( type == QVariant::LongLong )
725  ogrType = OFTInteger64;
726  else if ( type == QVariant::Double )
727  ogrType = OFTReal;
728  else if ( type == QVariant::Time )
729  ogrType = OFTTime;
730  else if ( type == QVariant::Date )
731  ogrType = OFTDate;
732  else if ( type == QVariant::DateTime )
733  ogrType = OFTDateTime;
734  else if ( type == QVariant::Bool )
735  {
736  ogrType = OFTInteger;
737  ogrSubType = OFSTBoolean;
738  }
739  else
740  ogrType = OFTString;
741 
742  int ogrWidth = field.length();
743 
744  gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( fieldName.toUtf8().constData(), ogrType ) );
745  OGR_Fld_SetWidth( fld.get(), ogrWidth );
746  if ( ogrSubType != OFSTNone )
747  OGR_Fld_SetSubType( fld.get(), ogrSubType );
748 
749  if ( OGR_L_CreateField( hLayer, fld.get(), true ) != OGRERR_NONE )
750  {
751  showWarning( tr( "Creation of field %1 failed (OGR error: %2)" )
752  .arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
753  return nullptr;
754  }
755  }
756 
757  // In GDAL >= 2.0, the driver implements a deferred creation strategy, so
758  // issue a command that will force table creation
759  CPLErrorReset();
760  OGR_L_ResetReading( hLayer );
761  if ( CPLGetLastErrorType() != CE_None )
762  {
763  QString msg( tr( "Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
764  showWarning( msg );
765  return nullptr;
766  }
767  hDS.reset();
768 
769  QString uri = QStringLiteral( "%1|layername=%2" ).arg( offlineDbPath, tableName );
771  newLayer = new QgsVectorLayer( uri, layer->name() + " (offline)", QStringLiteral( "ogr" ), layerOptions );
772  break;
773  }
774  }
775 
776  if ( newLayer->isValid() )
777  {
778 
779  // copy features
780  newLayer->startEditing();
781  QgsFeature f;
782 
783  QgsFeatureRequest req;
784 
785  if ( onlySelected )
786  {
787  QgsFeatureIds selectedFids = layer->selectedFeatureIds();
788  if ( !selectedFids.isEmpty() )
789  req.setFilterFids( selectedFids );
790  }
791 
792  QgsFeatureIterator fit = layer->dataProvider()->getFeatures( req );
793 
795  {
797  }
798  else
799  {
801  }
802  int featureCount = 1;
803 
804  QList<QgsFeatureId> remoteFeatureIds;
805  while ( fit.nextFeature( f ) )
806  {
807  remoteFeatureIds << f.id();
808 
809  // NOTE: SpatiaLite provider ignores position of geometry column
810  // fill gap in QgsAttributeMap if geometry column is not last (WORKAROUND)
811  int column = 0;
812  QgsAttributes attrs = f.attributes();
813  // on GPKG newAttrs has an addition FID attribute, so we have to add a dummy in the original set
814  QgsAttributes newAttrs( containerType == GPKG ? attrs.count() + 1 : attrs.count() );
815  for ( int it = 0; it < attrs.count(); ++it )
816  {
817  newAttrs[column++] = attrs.at( it );
818  }
819  f.setAttributes( newAttrs );
820 
821  newLayer->addFeature( f );
822 
823  emit progressUpdated( featureCount++ );
824  }
825  if ( newLayer->commitChanges() )
826  {
828  featureCount = 1;
829 
830  // update feature id lookup
831  int layerId = getOrCreateLayerId( db, newLayer->id() );
832  QList<QgsFeatureId> offlineFeatureIds;
833 
834  QgsFeatureIterator fit = newLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setNoAttributes() );
835  while ( fit.nextFeature( f ) )
836  {
837  offlineFeatureIds << f.id();
838  }
839 
840  // NOTE: insert fids in this loop, as the db is locked during newLayer->nextFeature()
841  sqlExec( db, QStringLiteral( "BEGIN" ) );
842  int remoteCount = remoteFeatureIds.size();
843  for ( int i = 0; i < remoteCount; i++ )
844  {
845  // Check if the online feature has been fetched (WFS download aborted for some reason)
846  if ( i < offlineFeatureIds.count() )
847  {
848  addFidLookup( db, layerId, offlineFeatureIds.at( i ), remoteFeatureIds.at( i ) );
849  }
850  else
851  {
852  showWarning( tr( "Feature cannot be copied to the offline layer, please check if the online layer '%1' is still accessible." ).arg( layer->name() ) );
853  return nullptr;
854  }
855  emit progressUpdated( featureCount++ );
856  }
857  sqlExec( db, QStringLiteral( "COMMIT" ) );
858  }
859  else
860  {
861  showWarning( newLayer->commitErrors().join( QLatin1Char( '\n' ) ) );
862  }
863 
864  // copy the custom properties from original layer
865  newLayer->setCustomProperties( layer->customProperties() );
866 
867  // mark as offline layer
869 
870  // store original layer source and information
874 
875  // register this layer with the central layers registry
877  QList<QgsMapLayer *>() << newLayer );
878 
879  // copy style
880  copySymbology( layer, newLayer );
881 
882  //remove constrainst of fields that use defaultValueClauses from provider on original
883  const auto fields = layer->fields();
884  for ( const QgsField &field : fields )
885  {
886  if ( !layer->dataProvider()->defaultValueClause( layer->fields().fieldOriginIndex( layer->fields().indexOf( field.name() ) ) ).isEmpty() )
887  {
889  }
890  }
891 
893  // Find the parent group of the original layer
894  QgsLayerTreeLayer *layerTreeLayer = layerTreeRoot->findLayer( layer->id() );
895  if ( layerTreeLayer )
896  {
897  QgsLayerTreeGroup *parentTreeGroup = qobject_cast<QgsLayerTreeGroup *>( layerTreeLayer->parent() );
898  if ( parentTreeGroup )
899  {
900  int index = parentTreeGroup->children().indexOf( layerTreeLayer );
901  // Move the new layer from the root group to the new group
902  QgsLayerTreeLayer *newLayerTreeLayer = layerTreeRoot->findLayer( newLayer->id() );
903  if ( newLayerTreeLayer )
904  {
905  QgsLayerTreeNode *newLayerTreeLayerClone = newLayerTreeLayer->clone();
906  //copy the showFeatureCount property to the new node
907  newLayerTreeLayerClone->setCustomProperty( CUSTOM_SHOW_FEATURE_COUNT, layerTreeLayer->customProperty( CUSTOM_SHOW_FEATURE_COUNT ) );
908  newLayerTreeLayerClone->setItemVisibilityChecked( layerTreeLayer->isVisible() );
909  QgsLayerTreeGroup *grp = qobject_cast<QgsLayerTreeGroup *>( newLayerTreeLayer->parent() );
910  parentTreeGroup->insertChildNode( index, newLayerTreeLayerClone );
911  if ( grp )
912  grp->removeChildNode( newLayerTreeLayer );
913  }
914  }
915  }
916 
917  updateRelations( layer, newLayer );
918  updateMapThemes( layer, newLayer );
919  updateLayerOrder( layer, newLayer );
920 
921 
922 
923  }
924  return newLayer;
925 }
926 
927 void QgsOfflineEditing::applyAttributesAdded( QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId, int commitNo )
928 {
929  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 );
930  QList<QgsField> fields = sqlQueryAttributesAdded( db, sql );
931 
932  const QgsVectorDataProvider *provider = remoteLayer->dataProvider();
933  QList<QgsVectorDataProvider::NativeType> nativeTypes = provider->nativeTypes();
934 
935  // NOTE: uses last matching QVariant::Type of nativeTypes
936  QMap < QVariant::Type, QString /*typeName*/ > typeNameLookup;
937  for ( int i = 0; i < nativeTypes.size(); i++ )
938  {
939  QgsVectorDataProvider::NativeType nativeType = nativeTypes.at( i );
940  typeNameLookup[ nativeType.mType ] = nativeType.mTypeName;
941  }
942 
943  emit progressModeSet( QgsOfflineEditing::AddFields, fields.size() );
944 
945  for ( int i = 0; i < fields.size(); i++ )
946  {
947  // lookup typename from layer provider
948  QgsField field = fields[i];
949  if ( typeNameLookup.contains( field.type() ) )
950  {
951  QString typeName = typeNameLookup[ field.type()];
953  remoteLayer->addAttribute( field );
954  }
955  else
956  {
957  showWarning( QStringLiteral( "Could not add attribute '%1' of type %2" ).arg( field.name() ).arg( field.type() ) );
958  }
959 
960  emit progressUpdated( i + 1 );
961  }
962 }
963 
964 void QgsOfflineEditing::applyFeaturesAdded( QgsVectorLayer *offlineLayer, QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId )
965 {
966  QString sql = QStringLiteral( "SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
967  const QList<int> featureIdInts = sqlQueryInts( db, sql );
968  QgsFeatureIds newFeatureIds;
969  for ( int id : featureIdInts )
970  {
971  newFeatureIds << id;
972  }
973 
974  QgsExpressionContext context = remoteLayer->createExpressionContext();
975 
976  // get new features from offline layer
977  QgsFeatureList features;
978  QgsFeatureIterator it = offlineLayer->getFeatures( QgsFeatureRequest().setFilterFids( newFeatureIds ) );
979  QgsFeature feature;
980  while ( it.nextFeature( feature ) )
981  {
982  features << feature;
983  }
984 
985  // copy features to remote layer
986  emit progressModeSet( QgsOfflineEditing::AddFeatures, features.size() );
987 
988  int i = 1;
989  int newAttrsCount = remoteLayer->fields().count();
990  for ( QgsFeatureList::iterator it = features.begin(); it != features.end(); ++it )
991  {
992  // NOTE: SpatiaLite provider ignores position of geometry column
993  // restore gap in QgsAttributeMap if geometry column is not last (WORKAROUND)
994  QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
995  QgsAttributes newAttrs( newAttrsCount );
996  QgsAttributes attrs = it->attributes();
997  for ( int it = 0; it < attrs.count(); ++it )
998  {
999  newAttrs[ attrLookup[ it ] ] = attrs.at( it );
1000  }
1001 
1002  // respect constraints and provider default values
1003  QgsFeature f = QgsVectorLayerUtils::createFeature( remoteLayer, it->geometry(), newAttrs.toMap(), &context );
1004  remoteLayer->addFeature( f );
1005 
1006  emit progressUpdated( i++ );
1007  }
1008 }
1009 
1010 void QgsOfflineEditing::applyFeaturesRemoved( QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId )
1011 {
1012  QString sql = QStringLiteral( "SELECT \"fid\" FROM 'log_removed_features' WHERE \"layer_id\" = %1" ).arg( layerId );
1013  QgsFeatureIds values = sqlQueryFeaturesRemoved( db, sql );
1014 
1015  emit progressModeSet( QgsOfflineEditing::RemoveFeatures, values.size() );
1016 
1017  int i = 1;
1018  for ( QgsFeatureIds::const_iterator it = values.constBegin(); it != values.constEnd(); ++it )
1019  {
1020  QgsFeatureId fid = remoteFid( db, layerId, *it );
1021  remoteLayer->deleteFeature( fid );
1022 
1023  emit progressUpdated( i++ );
1024  }
1025 }
1026 
1027 void QgsOfflineEditing::applyAttributeValueChanges( QgsVectorLayer *offlineLayer, QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId, int commitNo )
1028 {
1029  QString sql = QStringLiteral( "SELECT \"fid\", \"attr\", \"value\" FROM 'log_feature_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2 " ).arg( layerId ).arg( commitNo );
1030  AttributeValueChanges values = sqlQueryAttributeValueChanges( db, sql );
1031 
1032  emit progressModeSet( QgsOfflineEditing::UpdateFeatures, values.size() );
1033 
1034  QMap<int, int> attrLookup = attributeLookup( offlineLayer, remoteLayer );
1035 
1036  for ( int i = 0; i < values.size(); i++ )
1037  {
1038  QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
1039  QgsDebugMsgLevel( QStringLiteral( "Offline changeAttributeValue %1 = %2" ).arg( QString( attrLookup[ values.at( i ).attr ] ), values.at( i ).value ), 4 );
1040  remoteLayer->changeAttributeValue( fid, attrLookup[ values.at( i ).attr ], values.at( i ).value );
1041 
1042  emit progressUpdated( i + 1 );
1043  }
1044 }
1045 
1046 void QgsOfflineEditing::applyGeometryChanges( QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId, int commitNo )
1047 {
1048  QString sql = QStringLiteral( "SELECT \"fid\", \"geom_wkt\" FROM 'log_geometry_updates' WHERE \"layer_id\" = %1 AND \"commit_no\" = %2" ).arg( layerId ).arg( commitNo );
1049  GeometryChanges values = sqlQueryGeometryChanges( db, sql );
1050 
1052 
1053  for ( int i = 0; i < values.size(); i++ )
1054  {
1055  QgsFeatureId fid = remoteFid( db, layerId, values.at( i ).fid );
1056  QgsGeometry newGeom = QgsGeometry::fromWkt( values.at( i ).geom_wkt );
1057  remoteLayer->changeGeometry( fid, newGeom );
1058 
1059  emit progressUpdated( i + 1 );
1060  }
1061 }
1062 
1063 void QgsOfflineEditing::updateFidLookup( QgsVectorLayer *remoteLayer, sqlite3 *db, int layerId )
1064 {
1065  // update fid lookup for added features
1066 
1067  // get remote added fids
1068  // NOTE: use QMap for sorted fids
1069  QMap < QgsFeatureId, bool /*dummy*/ > newRemoteFids;
1070  QgsFeature f;
1071 
1072  QgsFeatureIterator fit = remoteLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setNoAttributes() );
1073 
1075 
1076  int i = 1;
1077  while ( fit.nextFeature( f ) )
1078  {
1079  if ( offlineFid( db, layerId, f.id() ) == -1 )
1080  {
1081  newRemoteFids[ f.id()] = true;
1082  }
1083 
1084  emit progressUpdated( i++ );
1085  }
1086 
1087  // get local added fids
1088  // NOTE: fids are sorted
1089  QString sql = QStringLiteral( "SELECT \"fid\" FROM 'log_added_features' WHERE \"layer_id\" = %1" ).arg( layerId );
1090  QList<int> newOfflineFids = sqlQueryInts( db, sql );
1091 
1092  if ( newRemoteFids.size() != newOfflineFids.size() )
1093  {
1094  //showWarning( QString( "Different number of new features on offline layer (%1) and remote layer (%2)" ).arg(newOfflineFids.size()).arg(newRemoteFids.size()) );
1095  }
1096  else
1097  {
1098  // add new fid lookups
1099  i = 0;
1100  sqlExec( db, QStringLiteral( "BEGIN" ) );
1101  for ( QMap<QgsFeatureId, bool>::const_iterator it = newRemoteFids.constBegin(); it != newRemoteFids.constEnd(); ++it )
1102  {
1103  addFidLookup( db, layerId, newOfflineFids.at( i++ ), it.key() );
1104  }
1105  sqlExec( db, QStringLiteral( "COMMIT" ) );
1106  }
1107 }
1108 
1109 void QgsOfflineEditing::copySymbology( QgsVectorLayer *sourceLayer, QgsVectorLayer *targetLayer )
1110 {
1111  targetLayer->styleManager()->copyStylesFrom( sourceLayer->styleManager() );
1112 
1113  QString error;
1114  QDomDocument doc;
1115  QgsReadWriteContext context;
1116  QgsMapLayer::StyleCategories categories = static_cast<QgsMapLayer::StyleCategories>( QgsMapLayer::AllStyleCategories ) & ~QgsMapLayer::CustomProperties;
1117  sourceLayer->exportNamedStyle( doc, error, context, categories );
1118 
1119  if ( error.isEmpty() )
1120  {
1121  targetLayer->importNamedStyle( doc, error, categories );
1122  }
1123  if ( !error.isEmpty() )
1124  {
1125  showWarning( error );
1126  }
1127 }
1128 
1129 void QgsOfflineEditing::updateRelations( QgsVectorLayer *sourceLayer, QgsVectorLayer *targetLayer )
1130 {
1132  const QList<QgsRelation> referencedRelations = relationManager->referencedRelations( sourceLayer );
1133 
1134  for ( QgsRelation relation : referencedRelations )
1135  {
1136  relationManager->removeRelation( relation );
1137  relation.setReferencedLayer( targetLayer->id() );
1138  relationManager->addRelation( relation );
1139  }
1140 
1141  const QList<QgsRelation> referencingRelations = relationManager->referencingRelations( sourceLayer );
1142 
1143  for ( QgsRelation relation : referencingRelations )
1144  {
1145  relationManager->removeRelation( relation );
1146  relation.setReferencingLayer( targetLayer->id() );
1147  relationManager->addRelation( relation );
1148  }
1149 }
1150 
1151 void QgsOfflineEditing::updateMapThemes( QgsVectorLayer *sourceLayer, QgsVectorLayer *targetLayer )
1152 {
1154  const QStringList mapThemeNames = mapThemeCollection->mapThemes();
1155 
1156  for ( const QString &mapThemeName : mapThemeNames )
1157  {
1158  QgsMapThemeCollection::MapThemeRecord record = mapThemeCollection->mapThemeState( mapThemeName );
1159 
1160  const auto layerRecords = record.layerRecords();
1161 
1162  for ( QgsMapThemeCollection::MapThemeLayerRecord layerRecord : layerRecords )
1163  {
1164  if ( layerRecord.layer() == sourceLayer )
1165  {
1166  layerRecord.setLayer( targetLayer );
1167  record.removeLayerRecord( sourceLayer );
1168  record.addLayerRecord( layerRecord );
1169  }
1170  }
1171 
1172  QgsProject::instance()->mapThemeCollection()->update( mapThemeName, record );
1173  }
1174 }
1175 
1176 void QgsOfflineEditing::updateLayerOrder( QgsVectorLayer *sourceLayer, QgsVectorLayer *targetLayer )
1177 {
1178  QList<QgsMapLayer *> layerOrder = QgsProject::instance()->layerTreeRoot()->customLayerOrder();
1179 
1180  auto iterator = layerOrder.begin();
1181 
1182  while ( iterator != layerOrder.end() )
1183  {
1184  if ( *iterator == targetLayer )
1185  {
1186  iterator = layerOrder.erase( iterator );
1187  if ( iterator == layerOrder.end() )
1188  break;
1189  }
1190 
1191  if ( *iterator == sourceLayer )
1192  {
1193  *iterator = targetLayer;
1194  }
1195 
1196  ++iterator;
1197  }
1198 
1200 }
1201 
1202 // NOTE: use this to map column indices in case the remote geometry column is not last
1203 QMap<int, int> QgsOfflineEditing::attributeLookup( QgsVectorLayer *offlineLayer, QgsVectorLayer *remoteLayer )
1204 {
1205  const QgsAttributeList &offlineAttrs = offlineLayer->attributeList();
1206 
1207  QMap < int /*offline attr*/, int /*remote attr*/ > attrLookup;
1208  // NOTE: though offlineAttrs can have new attributes not yet synced, we take the amount of offlineAttrs
1209  // because we anyway only add mapping for the fields existing in remoteLayer (this because it could contain fid on 0)
1210  for ( int i = 0; i < offlineAttrs.size(); i++ )
1211  {
1212  if ( remoteLayer->fields().lookupField( offlineLayer->fields().field( i ).name() ) >= 0 )
1213  attrLookup.insert( offlineAttrs.at( i ), remoteLayer->fields().indexOf( offlineLayer->fields().field( i ).name() ) );
1214  }
1215 
1216  return attrLookup;
1217 }
1218 
1219 void QgsOfflineEditing::showWarning( const QString &message )
1220 {
1221  emit warning( tr( "Offline Editing Plugin" ), message );
1222 }
1223 
1224 sqlite3_database_unique_ptr QgsOfflineEditing::openLoggingDb()
1225 {
1226  sqlite3_database_unique_ptr database;
1228  if ( !dbPath.isEmpty() )
1229  {
1230  QString absoluteDbPath = QgsProject::instance()->readPath( dbPath );
1231  int rc = database.open( absoluteDbPath );
1232  if ( rc != SQLITE_OK )
1233  {
1234  QgsDebugMsg( QStringLiteral( "Could not open the SpatiaLite logging database" ) );
1235  showWarning( tr( "Could not open the SpatiaLite logging database" ) );
1236  }
1237  }
1238  else
1239  {
1240  QgsDebugMsg( QStringLiteral( "dbPath is empty!" ) );
1241  }
1242  return database;
1243 }
1244 
1245 int QgsOfflineEditing::getOrCreateLayerId( sqlite3 *db, const QString &qgisLayerId )
1246 {
1247  QString sql = QStringLiteral( "SELECT \"id\" FROM 'log_layer_ids' WHERE \"qgis_id\" = '%1'" ).arg( qgisLayerId );
1248  int layerId = sqlQueryInt( db, sql, -1 );
1249  if ( layerId == -1 )
1250  {
1251  // next layer id
1252  sql = QStringLiteral( "SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'layer_id'" );
1253  int newLayerId = sqlQueryInt( db, sql, -1 );
1254 
1255  // insert layer
1256  sql = QStringLiteral( "INSERT INTO 'log_layer_ids' VALUES (%1, '%2')" ).arg( newLayerId ).arg( qgisLayerId );
1257  sqlExec( db, sql );
1258 
1259  // increase layer_id
1260  // TODO: use trigger for auto increment?
1261  sql = QStringLiteral( "UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'layer_id'" ).arg( newLayerId + 1 );
1262  sqlExec( db, sql );
1263 
1264  layerId = newLayerId;
1265  }
1266 
1267  return layerId;
1268 }
1269 
1270 int QgsOfflineEditing::getCommitNo( sqlite3 *db )
1271 {
1272  QString sql = QStringLiteral( "SELECT \"last_index\" FROM 'log_indices' WHERE \"name\" = 'commit_no'" );
1273  return sqlQueryInt( db, sql, -1 );
1274 }
1275 
1276 void QgsOfflineEditing::increaseCommitNo( sqlite3 *db )
1277 {
1278  QString sql = QStringLiteral( "UPDATE 'log_indices' SET 'last_index' = %1 WHERE \"name\" = 'commit_no'" ).arg( getCommitNo( db ) + 1 );
1279  sqlExec( db, sql );
1280 }
1281 
1282 void QgsOfflineEditing::addFidLookup( sqlite3 *db, int layerId, QgsFeatureId offlineFid, QgsFeatureId remoteFid )
1283 {
1284  QString sql = QStringLiteral( "INSERT INTO 'log_fids' VALUES ( %1, %2, %3 )" ).arg( layerId ).arg( offlineFid ).arg( remoteFid );
1285  sqlExec( db, sql );
1286 }
1287 
1288 QgsFeatureId QgsOfflineEditing::remoteFid( sqlite3 *db, int layerId, QgsFeatureId offlineFid )
1289 {
1290  QString sql = QStringLiteral( "SELECT \"remote_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"offline_fid\" = %2" ).arg( layerId ).arg( offlineFid );
1291  return sqlQueryInt( db, sql, -1 );
1292 }
1293 
1294 QgsFeatureId QgsOfflineEditing::offlineFid( sqlite3 *db, int layerId, QgsFeatureId remoteFid )
1295 {
1296  QString sql = QStringLiteral( "SELECT \"offline_fid\" FROM 'log_fids' WHERE \"layer_id\" = %1 AND \"remote_fid\" = %2" ).arg( layerId ).arg( remoteFid );
1297  return sqlQueryInt( db, sql, -1 );
1298 }
1299 
1300 bool QgsOfflineEditing::isAddedFeature( sqlite3 *db, int layerId, QgsFeatureId fid )
1301 {
1302  QString sql = QStringLiteral( "SELECT COUNT(\"fid\") FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( fid );
1303  return ( sqlQueryInt( db, sql, 0 ) > 0 );
1304 }
1305 
1306 int QgsOfflineEditing::sqlExec( sqlite3 *db, const QString &sql )
1307 {
1308  char *errmsg = nullptr;
1309  int rc = sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, &errmsg );
1310  if ( rc != SQLITE_OK )
1311  {
1312  showWarning( errmsg );
1313  }
1314  return rc;
1315 }
1316 
1317 int QgsOfflineEditing::sqlQueryInt( sqlite3 *db, const QString &sql, int defaultValue )
1318 {
1319  sqlite3_stmt *stmt = nullptr;
1320  if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1321  {
1322  showWarning( sqlite3_errmsg( db ) );
1323  return defaultValue;
1324  }
1325 
1326  int value = defaultValue;
1327  int ret = sqlite3_step( stmt );
1328  if ( ret == SQLITE_ROW )
1329  {
1330  value = sqlite3_column_int( stmt, 0 );
1331  }
1332  sqlite3_finalize( stmt );
1333 
1334  return value;
1335 }
1336 
1337 QList<int> QgsOfflineEditing::sqlQueryInts( sqlite3 *db, const QString &sql )
1338 {
1339  QList<int> values;
1340 
1341  sqlite3_stmt *stmt = nullptr;
1342  if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1343  {
1344  showWarning( sqlite3_errmsg( db ) );
1345  return values;
1346  }
1347 
1348  int ret = sqlite3_step( stmt );
1349  while ( ret == SQLITE_ROW )
1350  {
1351  values << sqlite3_column_int( stmt, 0 );
1352 
1353  ret = sqlite3_step( stmt );
1354  }
1355  sqlite3_finalize( stmt );
1356 
1357  return values;
1358 }
1359 
1360 QList<QgsField> QgsOfflineEditing::sqlQueryAttributesAdded( sqlite3 *db, const QString &sql )
1361 {
1362  QList<QgsField> values;
1363 
1364  sqlite3_stmt *stmt = nullptr;
1365  if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1366  {
1367  showWarning( sqlite3_errmsg( db ) );
1368  return values;
1369  }
1370 
1371  int ret = sqlite3_step( stmt );
1372  while ( ret == SQLITE_ROW )
1373  {
1374  QgsField field( QString( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 0 ) ) ),
1375  static_cast< QVariant::Type >( sqlite3_column_int( stmt, 1 ) ),
1376  QString(), // typeName
1377  sqlite3_column_int( stmt, 2 ),
1378  sqlite3_column_int( stmt, 3 ),
1379  QString( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 4 ) ) ) );
1380  values << field;
1381 
1382  ret = sqlite3_step( stmt );
1383  }
1384  sqlite3_finalize( stmt );
1385 
1386  return values;
1387 }
1388 
1389 QgsFeatureIds QgsOfflineEditing::sqlQueryFeaturesRemoved( sqlite3 *db, const QString &sql )
1390 {
1391  QgsFeatureIds values;
1392 
1393  sqlite3_stmt *stmt = nullptr;
1394  if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1395  {
1396  showWarning( sqlite3_errmsg( db ) );
1397  return values;
1398  }
1399 
1400  int ret = sqlite3_step( stmt );
1401  while ( ret == SQLITE_ROW )
1402  {
1403  values << sqlite3_column_int( stmt, 0 );
1404 
1405  ret = sqlite3_step( stmt );
1406  }
1407  sqlite3_finalize( stmt );
1408 
1409  return values;
1410 }
1411 
1412 QgsOfflineEditing::AttributeValueChanges QgsOfflineEditing::sqlQueryAttributeValueChanges( sqlite3 *db, const QString &sql )
1413 {
1414  AttributeValueChanges values;
1415 
1416  sqlite3_stmt *stmt = nullptr;
1417  if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1418  {
1419  showWarning( sqlite3_errmsg( db ) );
1420  return values;
1421  }
1422 
1423  int ret = sqlite3_step( stmt );
1424  while ( ret == SQLITE_ROW )
1425  {
1426  AttributeValueChange change;
1427  change.fid = sqlite3_column_int( stmt, 0 );
1428  change.attr = sqlite3_column_int( stmt, 1 );
1429  change.value = QString( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 2 ) ) );
1430  values << change;
1431 
1432  ret = sqlite3_step( stmt );
1433  }
1434  sqlite3_finalize( stmt );
1435 
1436  return values;
1437 }
1438 
1439 QgsOfflineEditing::GeometryChanges QgsOfflineEditing::sqlQueryGeometryChanges( sqlite3 *db, const QString &sql )
1440 {
1441  GeometryChanges values;
1442 
1443  sqlite3_stmt *stmt = nullptr;
1444  if ( sqlite3_prepare_v2( db, sql.toUtf8().constData(), -1, &stmt, nullptr ) != SQLITE_OK )
1445  {
1446  showWarning( sqlite3_errmsg( db ) );
1447  return values;
1448  }
1449 
1450  int ret = sqlite3_step( stmt );
1451  while ( ret == SQLITE_ROW )
1452  {
1453  GeometryChange change;
1454  change.fid = sqlite3_column_int( stmt, 0 );
1455  change.geom_wkt = QString( reinterpret_cast< const char * >( sqlite3_column_text( stmt, 1 ) ) );
1456  values << change;
1457 
1458  ret = sqlite3_step( stmt );
1459  }
1460  sqlite3_finalize( stmt );
1461 
1462  return values;
1463 }
1464 
1465 void QgsOfflineEditing::committedAttributesAdded( const QString &qgisLayerId, const QList<QgsField> &addedAttributes )
1466 {
1467  sqlite3_database_unique_ptr database = openLoggingDb();
1468  if ( !database )
1469  return;
1470 
1471  // insert log
1472  int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1473  int commitNo = getCommitNo( database.get() );
1474 
1475  for ( const QgsField &field : addedAttributes )
1476  {
1477  QString sql = QStringLiteral( "INSERT INTO 'log_added_attrs' VALUES ( %1, %2, '%3', %4, %5, %6, '%7' )" )
1478  .arg( layerId )
1479  .arg( commitNo )
1480  .arg( field.name() )
1481  .arg( field.type() )
1482  .arg( field.length() )
1483  .arg( field.precision() )
1484  .arg( field.comment() );
1485  sqlExec( database.get(), sql );
1486  }
1487 
1488  increaseCommitNo( database.get() );
1489 }
1490 
1491 void QgsOfflineEditing::committedFeaturesAdded( const QString &qgisLayerId, const QgsFeatureList &addedFeatures )
1492 {
1493  sqlite3_database_unique_ptr database = openLoggingDb();
1494  if ( !database )
1495  return;
1496 
1497  // insert log
1498  int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1499 
1500  // get new feature ids from db
1501  QgsMapLayer *layer = QgsProject::instance()->mapLayer( qgisLayerId );
1502  QString dataSourceString = layer->source();
1503  QgsDataSourceUri uri = QgsDataSourceUri( dataSourceString );
1504 
1506  QString tableName;
1507 
1508  if ( !offlinePath.contains( ".gpkg" ) )
1509  {
1510  tableName = uri.table();
1511  }
1512  else
1513  {
1514  QgsProviderMetadata *ogrProviderMetaData = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) );
1515  QVariantMap decodedUri = ogrProviderMetaData->decodeUri( dataSourceString );
1516  tableName = decodedUri.value( QStringLiteral( "layerName" ) ).toString();
1517  if ( tableName.isEmpty() )
1518  {
1519  showWarning( tr( "Could not deduce table name from data source %1." ).arg( dataSourceString ) );
1520  }
1521  }
1522 
1523  // only store feature ids
1524  QString sql = QStringLiteral( "SELECT ROWID FROM '%1' ORDER BY ROWID DESC LIMIT %2" ).arg( tableName ).arg( addedFeatures.size() );
1525  QList<int> newFeatureIds = sqlQueryInts( database.get(), sql );
1526  for ( int i = newFeatureIds.size() - 1; i >= 0; i-- )
1527  {
1528  QString sql = QStringLiteral( "INSERT INTO 'log_added_features' VALUES ( %1, %2 )" )
1529  .arg( layerId )
1530  .arg( newFeatureIds.at( i ) );
1531  sqlExec( database.get(), sql );
1532  }
1533 }
1534 
1535 void QgsOfflineEditing::committedFeaturesRemoved( const QString &qgisLayerId, const QgsFeatureIds &deletedFeatureIds )
1536 {
1537  sqlite3_database_unique_ptr database = openLoggingDb();
1538  if ( !database )
1539  return;
1540 
1541  // insert log
1542  int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1543 
1544  for ( QgsFeatureId id : deletedFeatureIds )
1545  {
1546  if ( isAddedFeature( database.get(), layerId, id ) )
1547  {
1548  // remove from added features log
1549  QString sql = QStringLiteral( "DELETE FROM 'log_added_features' WHERE \"layer_id\" = %1 AND \"fid\" = %2" ).arg( layerId ).arg( id );
1550  sqlExec( database.get(), sql );
1551  }
1552  else
1553  {
1554  QString sql = QStringLiteral( "INSERT INTO 'log_removed_features' VALUES ( %1, %2)" )
1555  .arg( layerId )
1556  .arg( id );
1557  sqlExec( database.get(), sql );
1558  }
1559  }
1560 }
1561 
1562 void QgsOfflineEditing::committedAttributeValuesChanges( const QString &qgisLayerId, const QgsChangedAttributesMap &changedAttrsMap )
1563 {
1564  sqlite3_database_unique_ptr database = openLoggingDb();
1565  if ( !database )
1566  return;
1567 
1568  // insert log
1569  int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1570  int commitNo = getCommitNo( database.get() );
1571 
1572  for ( QgsChangedAttributesMap::const_iterator cit = changedAttrsMap.begin(); cit != changedAttrsMap.end(); ++cit )
1573  {
1574  QgsFeatureId fid = cit.key();
1575  if ( isAddedFeature( database.get(), layerId, fid ) )
1576  {
1577  // skip added features
1578  continue;
1579  }
1580  QgsAttributeMap attrMap = cit.value();
1581  for ( QgsAttributeMap::const_iterator it = attrMap.constBegin(); it != attrMap.constEnd(); ++it )
1582  {
1583  QString sql = QStringLiteral( "INSERT INTO 'log_feature_updates' VALUES ( %1, %2, %3, %4, '%5' )" )
1584  .arg( layerId )
1585  .arg( commitNo )
1586  .arg( fid )
1587  .arg( it.key() ) // attr
1588  .arg( it.value().toString() ); // value
1589  sqlExec( database.get(), sql );
1590  }
1591  }
1592 
1593  increaseCommitNo( database.get() );
1594 }
1595 
1596 void QgsOfflineEditing::committedGeometriesChanges( const QString &qgisLayerId, const QgsGeometryMap &changedGeometries )
1597 {
1598  sqlite3_database_unique_ptr database = openLoggingDb();
1599  if ( !database )
1600  return;
1601 
1602  // insert log
1603  int layerId = getOrCreateLayerId( database.get(), qgisLayerId );
1604  int commitNo = getCommitNo( database.get() );
1605 
1606  for ( QgsGeometryMap::const_iterator it = changedGeometries.begin(); it != changedGeometries.end(); ++it )
1607  {
1608  QgsFeatureId fid = it.key();
1609  if ( isAddedFeature( database.get(), layerId, fid ) )
1610  {
1611  // skip added features
1612  continue;
1613  }
1614  QgsGeometry geom = it.value();
1615  QString sql = QStringLiteral( "INSERT INTO 'log_geometry_updates' VALUES ( %1, %2, %3, '%4' )" )
1616  .arg( layerId )
1617  .arg( commitNo )
1618  .arg( fid )
1619  .arg( geom.asWkt() );
1620  sqlExec( database.get(), sql );
1621 
1622  // TODO: use WKB instead of WKT?
1623  }
1624 
1625  increaseCommitNo( database.get() );
1626 }
1627 
1628 void QgsOfflineEditing::startListenFeatureChanges()
1629 {
1630  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( sender() );
1631  // enable logging, check if editBuffer is not null
1632  if ( vLayer->editBuffer() )
1633  {
1634  QgsVectorLayerEditBuffer *editBuffer = vLayer->editBuffer();
1636  this, &QgsOfflineEditing::committedAttributesAdded );
1638  this, &QgsOfflineEditing::committedAttributeValuesChanges );
1640  this, &QgsOfflineEditing::committedGeometriesChanges );
1641  }
1642  connect( vLayer, &QgsVectorLayer::committedFeaturesAdded,
1643  this, &QgsOfflineEditing::committedFeaturesAdded );
1644  connect( vLayer, &QgsVectorLayer::committedFeaturesRemoved,
1645  this, &QgsOfflineEditing::committedFeaturesRemoved );
1646 }
1647 
1648 void QgsOfflineEditing::stopListenFeatureChanges()
1649 {
1650  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( sender() );
1651  // disable logging, check if editBuffer is not null
1652  if ( vLayer->editBuffer() )
1653  {
1654  QgsVectorLayerEditBuffer *editBuffer = vLayer->editBuffer();
1655  disconnect( editBuffer, &QgsVectorLayerEditBuffer::committedAttributesAdded,
1656  this, &QgsOfflineEditing::committedAttributesAdded );
1658  this, &QgsOfflineEditing::committedAttributeValuesChanges );
1660  this, &QgsOfflineEditing::committedGeometriesChanges );
1661  }
1662  disconnect( vLayer, &QgsVectorLayer::committedFeaturesAdded,
1663  this, &QgsOfflineEditing::committedFeaturesAdded );
1664  disconnect( vLayer, &QgsVectorLayer::committedFeaturesRemoved,
1665  this, &QgsOfflineEditing::committedFeaturesRemoved );
1666 }
1667 
1668 void QgsOfflineEditing::layerAdded( QgsMapLayer *layer )
1669 {
1670  // detect offline layer
1671  if ( layer->customProperty( CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE, false ).toBool() )
1672  {
1673  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( layer );
1674  connect( vLayer, &QgsVectorLayer::editingStarted, this, &QgsOfflineEditing::startListenFeatureChanges );
1675  connect( vLayer, &QgsVectorLayer::editingStopped, this, &QgsOfflineEditing::stopListenFeatureChanges );
1676  }
1677 }
1678 
1679 
QgsFeatureRequest::NoGeometry
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition: qgsfeaturerequest.h:81
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:993
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:370
QgsProject::relationManager
QgsRelationManager * relationManager
Definition: qgsproject.h:105
QgsMapLayer::crs
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:89
QgsVectorLayer::editBuffer
Q_INVOKABLE QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on.
Definition: qgsvectorlayer.h:1999
QgsProject::writeEntry
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean value to the project file.
Definition: qgsproject.cpp:2441
QgsLayerTreeGroup::findLayer
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Definition: qgslayertreegroup.cpp:195
QgsProject::title
QString title() const
Returns the project's title.
Definition: qgsproject.cpp:489
spatialite_database_unique_ptr::open
int open(const QString &path)
Opens the database at the specified file path.
Definition: qgsspatialiteutils.cpp:41
QgsMapThemeCollection::MapThemeRecord::addLayerRecord
void addLayerRecord(const QgsMapThemeCollection::MapThemeLayerRecord &record)
Add a new record for a layer.
Definition: qgsmapthemecollection.cpp:729
QgsFeature::id
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QgsDataSourceUri
Class for storing the component parts of a RDBMS data source URI (e.g.
Definition: qgsdatasourceuri.h:36
QgsMapThemeCollection::mapThemes
QStringList mapThemes
Definition: qgsmapthemecollection.h:48
QgsLayerTreeNode
This class is a base class for nodes in a layer tree.
Definition: qgslayertreenode.h:75
qgsmaplayerstylemanager.h
sqlite3_database_unique_ptr::open
int open(const QString &path)
Opens the database at the specified file path.
Definition: qgssqliteutils.cpp:78
QgsRelationManager
This class manages a set of relations between layers.
Definition: qgsrelationmanager.h:35
QgsVectorLayer::wkbType
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
Definition: qgsvectorlayer.cpp:664
QgsWkbTypes::displayString
static QString displayString(Type type) SIP_HOLDGIL
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
Definition: qgswkbtypes.cpp:145
QgsOfflineEditing::synchronize
void synchronize()
Synchronize to remote layers.
Definition: qgsofflineediting.cpp:217
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:72
QgsLayerTreeGroup::removeChildNode
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group.
Definition: qgslayertreegroup.cpp:132
CUSTOM_PROPERTY_ORIGINAL_LAYERID
#define CUSTOM_PROPERTY_ORIGINAL_LAYERID
Definition: qgsofflineediting.cpp:63
QgsLayerTreeNode::setItemVisibilityChecked
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
Definition: qgslayertreenode.cpp:78
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:35
QgsWkbTypes::MultiPolygon
@ MultiPolygon
Definition: qgswkbtypes.h:78
qgsmapthemecollection.h
QgsField::length
int length
Definition: qgsfield.h:55
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
Definition: qgscoordinatereferencesystem.h:681
QgsVectorLayerEditBuffer::committedGeometriesChanges
void committedGeometriesChanges(const QString &layerId, const QgsGeometryMap &changedGeometries)
sqlite3
struct sqlite3 sqlite3
Definition: qgscoordinatereferencesystem.h:55
QgsOfflineEditing::isOfflineProject
bool isOfflineProject() const
Returns true if current project is offline.
Definition: qgsofflineediting.cpp:212
QgsProject::mapLayers
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
Definition: qgsproject.cpp:3436
QgsMapLayer::setCustomProperty
Q_INVOKABLE void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
Definition: qgsmaplayer.cpp:1708
QgsVectorDataProvider::fields
QgsFields fields() const override=0
Returns the fields associated with this data provider.
QgsLayerTree::customLayerOrder
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
Definition: qgslayertree.cpp:35
QgsWkbTypes::flatType
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
QgsMapLayer::importNamedStyle
virtual bool importNamedStyle(QDomDocument &doc, QString &errorMsg, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Import the properties of this layer from a QDomDocument.
Definition: qgsmaplayer.cpp:1037
QgsFeatureRequest::filterType
FilterType filterType() const
Returns the filter type which is currently set on this request.
Definition: qgsfeaturerequest.h:312
gdal::ogr_datasource_unique_ptr
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
Definition: qgsogrutils.h:114
QgsMapThemeCollection::MapThemeRecord
Individual map theme record of visible layers and styles.
Definition: qgsmapthemecollection.h:121
QgsWkbTypes::LineString
@ LineString
Definition: qgswkbtypes.h:73
qgsfeatureiterator.h
QgsFields::count
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsDataProvider::invalidateConnections
virtual void invalidateConnections(const QString &connection)
Invalidate connections corresponding to specified name.
Definition: qgsdataprovider.h:429
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsFeatureRequest::FilterFids
@ FilterFids
Filter using feature IDs.
Definition: qgsfeaturerequest.h:95
QgsMapLayer::setCustomProperties
void setCustomProperties(const QgsObjectCustomProperties &properties)
Set custom properties for layer.
Definition: qgsmaplayer.cpp:1713
gdal::ogr_field_def_unique_ptr
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
Definition: qgsogrutils.h:124
qgslayertreelayer.h
QgsLayerTree::setCustomLayerOrder
void setCustomLayerOrder(const QList< QgsMapLayer * > &customLayerOrder)
The order in which layers will be rendered on the canvas.
Definition: qgslayertree.cpp:40
PROJECT_ENTRY_KEY_OFFLINE_DB_PATH
#define PROJECT_ENTRY_KEY_OFFLINE_DB_PATH
Definition: qgsofflineediting.cpp:65
QgsMapLayer::styleManager
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
Definition: qgsmaplayer.cpp:1806
QgsProject::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:101
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
CUSTOM_PROPERTY_REMOTE_SOURCE
#define CUSTOM_PROPERTY_REMOTE_SOURCE
Definition: qgsofflineediting.cpp:60
QgsVectorLayer::isSpatial
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
Definition: qgsvectorlayer.cpp:3599
qgsogrutils.h
QgsMapThemeCollection::MapThemeRecord::layerRecords
QList< QgsMapThemeCollection::MapThemeLayerRecord > layerRecords() const
Returns a list of records for all visible layer belonging to the theme.
Definition: qgsmapthemecollection.h:136
CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE
#define CUSTOM_PROPERTY_IS_OFFLINE_EDITABLE
Definition: qgsofflineediting.cpp:59
QgsOfflineEditing::convertToOfflineProject
bool convertToOfflineProject(const QString &offlineDataPath, const QString &offlineDbFile, const QStringList &layerIds, bool onlySelected=false, ContainerType containerType=SpatiaLite)
Convert current project for offline editing.
Definition: qgsofflineediting.cpp:88
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
QgsProject::layerWasAdded
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the registry.
QgsMapLayerStyleManager::copyStylesFrom
void copyStylesFrom(QgsMapLayerStyleManager *other)
Copies all styles from other.
Definition: qgsmaplayerstylemanager.cpp:234
QgsVectorLayer::startEditing
Q_INVOKABLE bool startEditing()
Makes the layer editable.
Definition: qgsvectorlayer.cpp:1428
QgsChangedAttributesMap
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:569
QgsMapLayer::isValid
bool isValid
Definition: qgsmaplayer.h:91
QgsOfflineEditing::GPKG
@ GPKG
Definition: qgsofflineediting.h:55
QgsProject::readEntry
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.
Definition: qgsproject.cpp:2526
QgsOfflineEditing::warning
void warning(const QString &title, const QString &message)
Emitted when a warning needs to be displayed.
field
const QgsField & field
Definition: qgsfield.h:456
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsVectorDataProvider::NativeType
Definition: qgsvectordataprovider.h:462
QgsProject::mapLayer
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Definition: qgsproject.cpp:3208
QgsProject::removeMapLayers
void removeMapLayers(const QStringList &layerIds)
Remove a set of layers from the registry by layer ID.
Definition: qgsproject.cpp:3385
QgsAttributeList
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsVectorLayer::featureCount
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
Definition: qgsvectorlayer.cpp:751
QgsRelationManager::removeRelation
void removeRelation(const QString &id)
Remove a relation.
Definition: qgsrelationmanager.cpp:84
QgsField::name
QString name
Definition: qgsfield.h:59
QgsFieldConstraints::ConstraintNotNull
@ ConstraintNotNull
Field may not be null.
Definition: qgsfieldconstraints.h:45
QgsVectorLayer::committedFeaturesAdded
void committedFeaturesAdded(const QString &layerId, const QgsFeatureList &addedFeatures)
Emitted when features are added to the provider.
QgsVectorLayerEditBuffer::committedAttributesAdded
void committedAttributesAdded(const QString &layerId, const QList< QgsField > &addedAttributes)
QgsVectorLayer::changeAttributeValue
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).
Definition: qgsvectorlayer.cpp:2966
QgsVectorLayer::attributeList
QgsAttributeList attributeList() const
Returns list of attribute indexes.
Definition: qgsvectorlayer.h:1704
spatialite_database_unique_ptr::open_v2
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
Definition: qgsspatialiteutils.cpp:61
QgsMapLayer::providerType
QString providerType() const
Returns the provider type (provider key) for this layer.
Definition: qgsmaplayer.cpp:1617
QgsVectorJoinList
QList< QgsVectorLayerJoinInfo > QgsVectorJoinList
Definition: qgsvectorlayerjoinbuffer.h:30
QgsOfflineEditing::UpdateFeatures
@ UpdateFeatures
Definition: qgsofflineediting.h:47
qgsapplication.h
OGRSpatialReferenceH
void * OGRSpatialReferenceH
Definition: qgscoordinatereferencesystem.h:60
QgsOfflineEditing::SpatiaLite
@ SpatiaLite
Definition: qgsofflineediting.h:54
QgsOfflineEditing::progressStarted
void progressStarted()
Emitted when the process has started.
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3283
QgsField::precision
int precision
Definition: qgsfield.h:56
QgsMapLayer::customProperties
const QgsObjectCustomProperties & customProperties() const
Read all custom properties from layer.
Definition: qgsmaplayer.cpp:1718
QgsProject::addMapLayers
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
Definition: qgsproject.cpp:3336
QgsVectorLayer::changeGeometry
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...
Definition: qgsvectorlayer.cpp:2942
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:76
QgsWkbTypes::MultiLineString
@ MultiLineString
Definition: qgswkbtypes.h:77
QgsVectorDataProvider::nativeTypes
QList< QgsVectorDataProvider::NativeType > nativeTypes() const
Returns the names of the supported types.
Definition: qgsvectordataprovider.cpp:356
QgsProject::layerTreeRoot
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
Definition: qgsproject.cpp:3100
QgsOfflineEditing::UpdateGeometries
@ UpdateGeometries
Definition: qgsofflineediting.h:48
QgsVectorLayer::dataComment
QString dataComment() const
Returns a description for this layer as defined in the data provider.
Definition: qgsvectorlayer.cpp:359
qgsprovidermetadata.h
QgsVectorLayer::editingStarted
void editingStarted()
Emitted when editing on this layer has started.
QgsField::setTypeName
void setTypeName(const QString &typeName)
Set the field type.
Definition: qgsfield.cpp:189
QgsSnappingConfig::removeLayers
bool removeLayers(const QList< QgsMapLayer * > &layers)
Removes the specified layers from the individual layer configuration.
Definition: qgssnappingconfig.cpp:574
QgsVectorLayer::removeFieldConstraint
void removeFieldConstraint(int index, QgsFieldConstraints::Constraint constraint)
Removes a constraint for a specified field index.
Definition: qgsvectorlayer.cpp:5458
QgsVectorDataProvider::featureCount
long featureCount() const override=0
Number of features in the layer.
QgsOfflineEditing::QgsOfflineEditing
QgsOfflineEditing()
Definition: qgsofflineediting.cpp:67
QgsVectorLayer::commitChanges
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...
Definition: qgsvectorlayer.cpp:3332
QgsVectorLayer::commitErrors
QStringList commitErrors() const
Returns a list containing any error messages generated when attempting to commit changes to the layer...
Definition: qgsvectorlayer.cpp:3382
qgsproviderregistry.h
QgsRelationManager::referencedRelations
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
Definition: qgsrelationmanager.cpp:160
QgsVectorLayerEditBuffer::committedAttributeValuesChanges
void committedAttributeValuesChanges(const QString &layerId, const QgsChangedAttributesMap &changedAttributesValues)
qgsvectorlayerjoinbuffer.h
QgsOfflineEditing::AddFields
@ AddFields
Definition: qgsofflineediting.h:44
qgsdatasourceuri.h
QgsVectorLayer::vectorJoins
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
Definition: qgsvectorlayer.cpp:3732
qgslayertreegroup.h
QgsLayerTreeLayer
Layer tree node points to a map layer.
Definition: qgslayertreelayer.h:44
qgsmaplayer.h
QgsVectorLayer::selectedFeatureIds
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Definition: qgsvectorlayer.cpp:3441
QgsCoordinateReferenceSystem::authid
QString authid() const
Returns the authority identifier for the CRS.
Definition: qgscoordinatereferencesystem.cpp:1321
PROJECT_ENTRY_SCOPE_OFFLINE
#define PROJECT_ENTRY_SCOPE_OFFLINE
Definition: qgsofflineediting.cpp:64
QgsField::comment
QString comment
Definition: qgsfield.h:58
QgsProject::readPath
QString readPath(const QString &filename) const
Transforms a filename read from the project file to an absolute path.
Definition: qgsproject.cpp:2695
QgsLayerTreeGroup
Layer tree group node serves as a container for layers and further groups.
Definition: qgslayertreegroup.h:35
qgsvectordataprovider.h
QgsVectorLayer::reload
void reload() FINAL
Synchronises with changes in the datasource.
Definition: qgsvectorlayer.cpp:378
QgsOfflineEditing::AddFeatures
@ AddFeatures
Definition: qgsofflineediting.h:45
QgsFeatureList
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:583
QgsCoordinateReferenceSystem::toWkt
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1954
QgsAttributeMap
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QgsWkbTypes::hasM
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1093
qgsvectorlayerutils.h
QgsVectorDataProvider::NativeType::mTypeName
QString mTypeName
Definition: qgsvectordataprovider.h:475
spatialite_database_unique_ptr
Unique pointer for spatialite databases, which automatically closes the database when the pointer goe...
Definition: qgsspatialiteutils.h:57
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:148
QgsFeatureRequest::setFilterFids
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
Definition: qgsfeaturerequest.cpp:105
QgsSnappingConfig
This is a container for configuration of the snapping of the project.
Definition: qgssnappingconfig.h:34
QgsOfflineEditing::progressStopped
void progressStopped()
Emitted when the processing of all layers has finished.
QgsProviderRegistry::providerMetadata
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
Definition: qgsproviderregistry.cpp:724
typeName
const QString & typeName
Definition: qgswfsgetfeature.cpp:55
QgsProject::mapThemeCollection
QgsMapThemeCollection * mapThemeCollection
Definition: qgsproject.h:103
QgsVectorLayerJoinInfo
Defines left outer join from our vector layer to some other vector layer.
Definition: qgsvectorlayerjoininfo.h:34
QgsMapLayer::exportNamedStyle
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.
Definition: qgsmaplayer.cpp:1098
QgsProject::setSnappingConfig
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
Definition: qgsproject.cpp:1032
QgsRelationManager::addRelation
void addRelation(const QgsRelation &relation)
Add a relation.
Definition: qgsrelationmanager.cpp:60
qgslayertree.h
qgsrelationmanager.h
QgsVectorDataProvider::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override=0
Query the provider for features specified in request.
QgsFeatureIds
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
QgsOfflineEditing::RemoveFeatures
@ RemoveFeatures
Definition: qgsofflineediting.h:46
QgsGeometry::fromWkt
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
Definition: qgsgeometry.cpp:154
QgsFeature::attributes
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsProject::setTitle
void setTitle(const QString &title)
Sets the project's title.
Definition: qgsproject.cpp:477
QgsFields::field
QgsField field(int fieldIdx) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:168
QgsProviderMetadata
Holds data provider key, description, and associated shared library file or function pointer informat...
Definition: qgsprovidermetadata.h:137
CUSTOM_SHOW_FEATURE_COUNT
#define CUSTOM_SHOW_FEATURE_COUNT
Definition: qgsofflineediting.cpp:62
QgsVectorLayerUtils::createFeature
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.
Definition: qgsvectorlayerutils.cpp:476
QgsSnappingConfig::setIndividualLayerSettings
void setIndividualLayerSettings(QgsVectorLayer *vl, const QgsSnappingConfig::IndividualLayerSettings &individualLayerSettings)
Sets individual layer snappings settings (applied if mode is AdvancedConfiguration)
Definition: qgssnappingconfig.cpp:381
qgsvectorlayer.h
QgsLayerTreeGroup::insertChildNode
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position.
Definition: qgslayertreegroup.cpp:92
QgsVectorLayer::LayerOptions
Setting options for loading vector layers.
Definition: qgsvectorlayer.h:425
QgsOfflineEditing::ContainerType
ContainerType
Type of offline database container file.
Definition: qgsofflineediting.h:53
QgsMapLayer::source
QString source() const
Returns the source for the layer.
Definition: qgsmaplayer.cpp:192
QgsVectorDataProvider::NativeType::mType
QVariant::Type mType
Definition: qgsvectordataprovider.h:476
QgsGeometry::asWkt
QString asWkt(int precision=17) const
Exports the geometry to WKT.
Definition: qgsgeometry.cpp:1288
QgsVectorLayer::editingStopped
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
QgsMapLayer::customProperty
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
Definition: qgsmaplayer.cpp:1723
qgsgeometry.h
spatialite_database_unique_ptr::errorMessage
QString errorMessage() const
Returns the most recent error message encountered by the database.
Definition: qgsspatialiteutils.cpp:86
QgsMapThemeCollection::MapThemeLayerRecord
Individual record of a visible layer in a map theme record.
Definition: qgsmapthemecollection.h:59
QgsOfflineEditing::ProcessFeatures
@ ProcessFeatures
Definition: qgsofflineediting.h:43
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:374
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsLayerTreeNode::setCustomProperty
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.
Definition: qgslayertreenode.cpp:180
QgsVectorLayerEditBuffer
Definition: qgsvectorlayereditbuffer.h:37
QgsSnappingConfig::individualLayerSettings
QHash< QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings > individualLayerSettings() const
Returns individual snapping settings for all layers.
Definition: qgssnappingconfig.cpp:358
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsLayerTreeNode::customProperty
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.
Definition: qgslayertreenode.cpp:189
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
QgsRelationManager::referencingRelations
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....
Definition: qgsrelationmanager.cpp:121
QgsWkbTypes::Polygon
@ Polygon
Definition: qgswkbtypes.h:74
qgsspatialiteutils.h
QgsProject::snappingConfig
QgsSnappingConfig snappingConfig
Definition: qgsproject.h:104
QgsWkbTypes::MultiPoint
@ MultiPoint
Definition: qgswkbtypes.h:76
QgsOfflineEditing::layerProgressUpdated
void layerProgressUpdated(int layer, int numLayers)
Emitted whenever a new layer is being processed.
QgsLayerTreeNode::children
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Definition: qgslayertreenode.h:112
qgsofflineediting.h
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:86
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:627
QgsLayerTreeLayer::clone
QgsLayerTreeLayer * clone() const override
Create a copy of the node. Returns new instance.
Definition: qgslayertreelayer.cpp:188
QgsDataSourceUri::database
QString database() const
Returns the database name stored in the URI.
Definition: qgsdatasourceuri.cpp:279
QgsFields::fieldOriginIndex
int fieldOriginIndex(int fieldIdx) const
Gets field's origin index (its meaning is specific to each type of origin)
Definition: qgsfields.cpp:197
QgsRelation
Definition: qgsrelation.h:42
QgsMapLayer::CustomProperties
@ CustomProperties
Custom properties (by plugins for instance)
Definition: qgsmaplayer.h:173
QgsVectorDataProvider::defaultValueClause
virtual QString defaultValueClause(int fieldIndex) const
Returns any default value clauses which are present at the provider for a specified field index.
Definition: qgsvectordataprovider.cpp:149
QgsOfflineEditing::progressUpdated
void progressUpdated(int progress)
Emitted with the progress of the current mode.
QgsWkbTypes::hasZ
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1043
QgsAttributes
A vector of attributes.
Definition: qgsattributes.h:58
QgsGeometryMap
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
Definition: qgsfeature.h:578
QgsVectorDataProvider
This is the base class for vector data providers.
Definition: qgsvectordataprovider.h:59
QgsMapThemeCollection::MapThemeRecord::removeLayerRecord
void removeLayerRecord(QgsMapLayer *layer)
Removes a record for layer if present.
Definition: qgsmapthemecollection.cpp:720
QgsLayerTreeNode::parent
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
Definition: qgslayertreenode.h:110
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsVectorLayer::deleteFeature
bool deleteFeature(QgsFeatureId fid, DeleteContext *context=nullptr)
Deletes a feature from the layer (but does not commit it).
Definition: qgsvectorlayer.cpp:3251
QgsMapThemeCollection
Container class that allows storage of map themes consisting of visible map layers and layer styles.
Definition: qgsmapthemecollection.h:45
qgslogger.h
QgsLayerTreeNode::isVisible
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well)
Definition: qgslayertreenode.cpp:98
QgsFeature::setAttributes
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:129
qgsvectorfilewriter.h
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
QgsVectorLayer::createExpressionContext
QgsExpressionContext createExpressionContext() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Definition: qgsvectorlayer.cpp:4952
QgsMapLayer::AllStyleCategories
@ AllStyleCategories
Definition: qgsmaplayer.h:178
QgsOfflineEditing::CopyFeatures
@ CopyFeatures
Definition: qgsofflineediting.h:42
QgsDataSourceUri::table
QString table() const
Returns the table name stored in the URI.
Definition: qgsdatasourceuri.cpp:314
sqlite3_database_unique_ptr
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
Definition: qgssqliteutils.h:119
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:265
QgsVectorLayer::addFeature
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
Definition: qgsvectorlayer.cpp:1011
QgsProviderRegistry::instance
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Definition: qgsproviderregistry.cpp:48
QgsMapThemeCollection::mapThemeState
QgsMapThemeCollection::MapThemeRecord mapThemeState(const QString &name) const
Returns the recorded state of a map theme.
Definition: qgsmapthemecollection.h:285
qgsproject.h
CUSTOM_PROPERTY_REMOTE_PROVIDER
#define CUSTOM_PROPERTY_REMOTE_PROVIDER
Definition: qgsofflineediting.cpp:61
QgsField::type
QVariant::Type type
Definition: qgsfield.h:57
qgsvectorlayereditbuffer.h
QgsProviderMetadata::decodeUri
virtual QVariantMap decodeUri(const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
Definition: qgsprovidermetadata.cpp:128
QgsOfflineEditing::progressModeSet
void progressModeSet(QgsOfflineEditing::ProgressMode mode, int maximum)
Emitted when the mode for the progress of the current operation is set.
QgsMapThemeCollection::update
void update(const QString &name, const QgsMapThemeCollection::MapThemeRecord &state)
Updates a map theme within the collection.
Definition: qgsmapthemecollection.cpp:272
QgsProject::removeEntry
bool removeEntry(const QString &scope, const QString &key)
Remove the given key from the specified scope.
Definition: qgsproject.cpp:2623
QgsVectorLayer::committedFeaturesRemoved
void committedFeaturesRemoved(const QString &layerId, const QgsFeatureIds &deletedFeatureIds)
Emitted when features are deleted from the provider.
QgsFields::indexOf
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
QgsFeatureId
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QgsVectorLayer::addAttribute
bool addAttribute(const QgsField &field)
Add an attribute field (but does not commit it) returns true if the field was added.
Definition: qgsvectorlayer.cpp:3055
QgsVectorLayer::addJoin
bool addJoin(const QgsVectorLayerJoinInfo &joinInfo)
Joins another vector layer to this layer.
Definition: qgsvectorlayer.cpp:3721
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50