QGIS API Documentation  3.27.0-Master (aef1b1ec20)
qgsvectorlayereditbuffer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayereditbuffer.cpp
3  ---------------------
4  begin : Dezember 2012
5  copyright : (C) 2012 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
16 
17 #include "qgsgeometry.h"
18 #include "qgslogger.h"
21 #include "qgsvectordataprovider.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsvectorlayerutils.h"
24 #include "qgsmessagelog.h"
25 
26 
28 template <class Key, class T> void mapToReversedLists( const QMap< Key, T > &map, QList<Key> &ks, QList<T> &vs )
29 {
30  ks.reserve( map.size() );
31  vs.reserve( map.size() );
32  typename QMap<Key, T>::const_iterator i = map.constEnd();
33  while ( i-- != map.constBegin() )
34  {
35  ks.append( i.key() );
36  vs.append( i.value() );
37  }
38 }
39 
40 
42  : L( layer )
43 {
44  connect( L->undoStack(), &QUndoStack::indexChanged, this, &QgsVectorLayerEditBuffer::undoIndexChanged ); // TODO[MD]: queued?
45 }
46 
48 {
49  return !L->undoStack()->isClean();
50 }
51 
52 
54 {
55  QgsDebugMsgLevel( QStringLiteral( "undo index changed %1" ).arg( index ), 4 );
56  Q_UNUSED( index )
57  emit layerModified();
58 }
59 
60 
62 {
63  // delete attributes from the higher indices to lower indices
64  for ( int i = mDeletedAttributeIds.count() - 1; i >= 0; --i )
65  {
66  fields.remove( mDeletedAttributeIds.at( i ) );
67  }
68 
69  // rename fields
70  QgsFieldNameMap::const_iterator renameIt = mRenamedAttributes.constBegin();
71  for ( ; renameIt != mRenamedAttributes.constEnd(); ++renameIt )
72  {
73  fields.rename( renameIt.key(), renameIt.value() );
74  }
75 
76  // add new fields
77  for ( int i = 0; i < mAddedAttributes.count(); ++i )
78  {
79  fields.append( mAddedAttributes.at( i ), QgsFields::OriginEdit, i );
80  }
81 }
82 
84 {
85  return mEditBufferGroup;
86 }
87 
89 {
91 }
92 
94 {
95  if ( mChangedGeometries.contains( f.id() ) )
97 }
98 
99 
101 {
102  QgsAttributes attrs = f.attributes();
103 
104  // remove all attributes that will disappear - from higher indices to lower
105  for ( int idx = mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
106  {
107  attrs.remove( mDeletedAttributeIds[idx] );
108  }
109 
110  // adjust size to accommodate added attributes
111  attrs.resize( attrs.count() + mAddedAttributes.count() );
112 
113  // update changed attributes
114  if ( mChangedAttributeValues.contains( f.id() ) )
115  {
116  const QgsAttributeMap &map = mChangedAttributeValues[f.id()];
117  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
118  attrs[it.key()] = it.value();
119  }
120 
121  f.setAttributes( attrs );
122 }
123 
124 
125 
126 
128 {
130  {
131  return false;
132  }
133  if ( L->mFields.count() != f.attributes().count() )
134  {
135  QgsMessageLog::logMessage( tr( "cannot add feature, wrong field count: layer: %1 feature: %2:" ).arg( L->mFields.count() ).arg( f.attributes().count() ) );
136  return false;
137  }
138 
139  // TODO: check correct geometry type
140 
141  L->undoStack()->push( new QgsVectorLayerUndoCommandAddFeature( this, f ) );
142  return true;
143 }
144 
145 
147 {
149  return false;
150 
151  bool result = true;
152  for ( QgsFeatureList::iterator iter = features.begin(); iter != features.end(); ++iter )
153  {
154  result = result && addFeature( *iter );
155  }
156 
157  L->updateExtents();
158  return result;
159 }
160 
161 
162 
164 {
166  {
167  QgsDebugMsg( QStringLiteral( "Cannot delete features (missing DeleteFeature capability)" ) );
168  return false;
169  }
170 
171  if ( FID_IS_NEW( fid ) )
172  {
173  if ( !mAddedFeatures.contains( fid ) )
174  {
175  QgsDebugMsg( QStringLiteral( "Cannot delete features (in the list of added features)" ) );
176  return false;
177  }
178  }
179  else // existing feature
180  {
181  if ( mDeletedFeatureIds.contains( fid ) )
182  {
183  QgsDebugMsg( QStringLiteral( "Cannot delete features (in the list of deleted features)" ) );
184  return false;
185  }
186  }
187 
188  L->undoStack()->push( new QgsVectorLayerUndoCommandDeleteFeature( this, fid ) );
189  return true;
190 }
191 
193 {
195  {
196  QgsDebugMsg( QStringLiteral( "Cannot delete features (missing DeleteFeatures capability)" ) );
197  return false;
198  }
199 
200  bool ok = true;
201  const auto constFids = fids;
202  for ( QgsFeatureId fid : constFids )
203  ok = deleteFeature( fid ) && ok;
204 
205  return ok;
206 }
207 
208 
210 {
211  if ( !L->isSpatial() )
212  {
213  return false;
214  }
215 
216  if ( FID_IS_NEW( fid ) )
217  {
218  if ( !mAddedFeatures.contains( fid ) )
219  return false;
220  }
222  return false;
223 
224  // TODO: check compatible geometry
225 
226  L->undoStack()->push( new QgsVectorLayerUndoCommandChangeGeometry( this, fid, geom ) );
227  return true;
228 }
229 
231 {
232  bool success = true;
233  for ( auto it = newValues.constBegin() ; it != newValues.constEnd(); ++it )
234  {
235  const int field = it.key();
236  const QVariant newValue = it.value();
237  QVariant oldValue;
238 
239  if ( oldValues.contains( field ) )
240  oldValue = oldValues[field];
241 
242  success &= changeAttributeValue( fid, field, newValue, oldValue );
243  }
244 
245  return success;
246 }
247 
248 bool QgsVectorLayerEditBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
249 {
250  if ( FID_IS_NEW( fid ) )
251  {
252  if ( !mAddedFeatures.contains( fid ) )
253  return false;
254  }
256  {
257  return false;
258  }
259 
260  if ( field < 0 || field >= L->fields().count() ||
263  return false;
264 
265  L->undoStack()->push( new QgsVectorLayerUndoCommandChangeAttribute( this, fid, field, newValue, oldValue ) );
266  return true;
267 }
268 
269 
271 {
273  return false;
274 
275  if ( field.name().isEmpty() )
276  return false;
277 
278  const QgsFields fields = L->fields();
279  for ( const QgsField &updatedField : fields )
280  {
281  if ( updatedField.name() == field.name() )
282  return false;
283  }
284 
285  if ( !L->dataProvider()->supportedType( field ) )
286  return false;
287 
288  L->undoStack()->push( new QgsVectorLayerUndoCommandAddAttribute( this, field ) );
289  return true;
290 }
291 
292 
294 {
296  return false;
297 
298  if ( index < 0 || index >= L->fields().count() )
299  return false;
300 
301  // find out source of the field
302  QgsFields::FieldOrigin origin = L->fields().fieldOrigin( index );
303  int originIndex = L->fields().fieldOriginIndex( index );
304 
305  if ( origin == QgsFields::OriginProvider && mDeletedAttributeIds.contains( originIndex ) )
306  return false;
307 
308  if ( origin == QgsFields::OriginJoin )
309  return false;
310 
311  L->undoStack()->push( new QgsVectorLayerUndoCommandDeleteAttribute( this, index ) );
312  return true;
313 }
314 
315 bool QgsVectorLayerEditBuffer::renameAttribute( int index, const QString &newName )
316 {
318  return false;
319 
320  if ( newName.isEmpty() )
321  return false;
322 
323  if ( index < 0 || index >= L->fields().count() )
324  return false;
325 
326  const QgsFields fields = L->fields();
327  for ( const QgsField &updatedField : fields )
328  {
329  if ( updatedField.name() == newName )
330  return false;
331  }
332 
333  L->undoStack()->push( new QgsVectorLayerUndoCommandRenameAttribute( this, index, newName ) );
334  return true;
335 }
336 
337 
338 bool QgsVectorLayerEditBuffer::commitChanges( QStringList &commitErrors )
339 {
340  commitErrors.clear();
341 
342  bool success = true;
343 
344  // geometry updates attribute updates
345  // yes no => changeGeometryValues
346  // no yes => changeAttributeValues
347  // yes yes => changeFeatures
348 
349  // Check geometry types
350  // to fix https://github.com/qgis/QGIS/issues/23663
351  if ( !mAddedFeatures.isEmpty() )
352  success &= commitChangesCheckGeometryTypeCompatibility( commitErrors );
353 
354  const QgsFields oldFields = L->fields();
355 
356  //
357  // delete attributes
358  //
359  bool attributesChanged = false;
360  if ( success && !mDeletedAttributeIds.isEmpty() )
361  {
362  bool attributesDeleted = false;
363  success &= commitChangesDeleteAttributes( attributesDeleted, commitErrors );
364  attributesChanged |= attributesDeleted;
365  }
366 
367  // rename attributes
368  if ( success && !mRenamedAttributes.isEmpty() )
369  {
370  bool attributesRenamed = false;
371  success &= commitChangesRenameAttributes( attributesRenamed, commitErrors );
372  attributesChanged |= attributesRenamed;
373  }
374 
375  //
376  // add attributes
377  //
378  if ( success && !mAddedAttributes.isEmpty() )
379  {
380  bool attributesAdded = false;
381  success &= commitChangesAddAttributes( attributesAdded, commitErrors );
382  attributesChanged |= attributesAdded;
383  }
384 
385  //
386  // check that addition/removal went as expected
387  //
388  if ( success && attributesChanged )
389  success &= commitChangesCheckAttributesModifications( oldFields, commitErrors );
390 
391  //
392  // change attributes
393  //
394  if ( success && ( !mChangedAttributeValues.isEmpty() || !mChangedGeometries.isEmpty() ) )
395  {
396  bool attributesChanged;
397  success &= commitChangesChangeAttributes( attributesChanged, commitErrors );
398  }
399 
400  //
401  // delete features
402  //
403  if ( success && !mDeletedFeatureIds.isEmpty() )
404  {
405  bool featuresDeleted;
406  success &= commitChangesDeleteFeatures( featuresDeleted, commitErrors );
407  }
408 
409  //
410  // add features
411  //
412  if ( success && !mAddedFeatures.isEmpty() )
413  {
414  bool featuresAdded;
415  success &= commitChangesAddFeatures( featuresAdded, commitErrors );
416  }
417 
418  QgsVectorDataProvider *provider = L->dataProvider();
419  if ( !success && provider->hasErrors() )
420  {
421  commitErrors << tr( "\n Provider errors:" );
422  const auto constErrors = provider->errors();
423  for ( QString e : constErrors )
424  {
425  commitErrors << " " + e.replace( '\n', QLatin1String( "\n " ) );
426  }
427  provider->clearErrors();
428  }
429 
430  return success;
431 }
432 
433 
435 {
436  if ( !isModified() )
437  return;
438 
439  // limit canvas redraws to one by jumping to beginning of stack
440  // see QgsUndoWidget::indexChanged
441  L->undoStack()->setIndex( 0 );
442 
443  Q_ASSERT( mAddedAttributes.isEmpty() );
444  Q_ASSERT( mDeletedAttributeIds.isEmpty() );
445  Q_ASSERT( mChangedAttributeValues.isEmpty() );
446  Q_ASSERT( mChangedGeometries.isEmpty() );
447  Q_ASSERT( mAddedFeatures.isEmpty() );
448 }
449 
451 {
452  return qgis::listToSet( mAddedFeatures.keys() ).unite( qgis::listToSet( mChangedAttributeValues.keys() ) ).unite( qgis::listToSet( mChangedGeometries.keys() ) );
453 }
454 
455 #if 0
456 QString QgsVectorLayerEditBuffer::dumpEditBuffer()
457 {
458  QString msg;
459  if ( !mChangedGeometries.isEmpty() )
460  {
461  msg += "CHANGED GEOMETRIES:\n";
462  for ( QgsGeometryMap::const_iterator it = mChangedGeometries.begin(); it != mChangedGeometries.end(); ++it )
463  {
464  // QgsFeatureId, QgsGeometry
465  msg += QString( "- FID %1: %2" ).arg( it.key() ).arg( it.value().to );
466  }
467  }
468  return msg;
469 }
470 #endif
471 
473 {
474  // go through the changed attributes map and adapt indices
475  QgsChangedAttributesMap::iterator it = mChangedAttributeValues.begin();
476  for ( ; it != mChangedAttributeValues.end(); ++it )
477  {
478  updateAttributeMapIndex( it.value(), index, + 1 );
479  }
480 
481  // go through added features and adapt attributes
482  QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
483  for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
484  {
485  QgsAttributes attrs = featureIt->attributes();
486  attrs.insert( index, QVariant() );
487  featureIt->setAttributes( attrs );
488  }
489 
490  // go through renamed attributes and adapt
491  QList< int > sortedRenamedIndices = mRenamedAttributes.keys();
492  //sort keys
493  std::sort( sortedRenamedIndices.begin(), sortedRenamedIndices.end(), std::greater< int >() );
494  const auto constSortedRenamedIndices = sortedRenamedIndices;
495  for ( int renameIndex : constSortedRenamedIndices )
496  {
497  if ( renameIndex >= index )
498  {
499  mRenamedAttributes[ renameIndex + 1 ] = mRenamedAttributes.value( renameIndex );
500  }
501  }
502  //remove last
503  mRenamedAttributes.remove( index );
504 }
505 
507 {
508  // go through the changed attributes map and adapt indices
509  QgsChangedAttributesMap::iterator it = mChangedAttributeValues.begin();
510  for ( ; it != mChangedAttributeValues.end(); ++it )
511  {
512  QgsAttributeMap &attrMap = it.value();
513  // remove the attribute
514  if ( attrMap.contains( index ) )
515  attrMap.remove( index );
516 
517  // update attribute indices
518  updateAttributeMapIndex( attrMap, index, -1 );
519  }
520 
521  // go through added features and adapt attributes
522  QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
523  for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
524  {
525  QgsAttributes attrs = featureIt->attributes();
526  attrs.remove( index );
527  featureIt->setAttributes( attrs );
528  }
529 
530  // go through rename attributes and adapt
531  QList< int > sortedRenamedIndices = mRenamedAttributes.keys();
532  //sort keys
533  std::sort( sortedRenamedIndices.begin(), sortedRenamedIndices.end() );
534  int last = -1;
535  mRenamedAttributes.remove( index );
536  const auto constSortedRenamedIndices = sortedRenamedIndices;
537  for ( int renameIndex : constSortedRenamedIndices )
538  {
539  if ( renameIndex > index )
540  {
541  mRenamedAttributes.insert( renameIndex - 1, mRenamedAttributes.value( renameIndex ) );
542  last = renameIndex;
543  }
544  }
545  //remove last
546  if ( last > -1 )
547  mRenamedAttributes.remove( last );
548 }
549 
550 
551 
553 {
554  QgsAttributeMap updatedMap;
555  for ( QgsAttributeMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
556  {
557  int attrIndex = it.key();
558  updatedMap.insert( attrIndex < index ? attrIndex : attrIndex + offset, it.value() );
559  }
560  map = updatedMap;
561 }
562 
563 
564 
566 {
567  L->updateFields();
568 }
569 
570 bool QgsVectorLayerEditBuffer::commitChangesCheckGeometryTypeCompatibility( QStringList &commitErrors )
571 {
572  if ( mAddedFeatures.isEmpty() )
573  return true;
574 
576  {
578  {
579  for ( const QgsFeature &f : std::as_const( mAddedFeatures ) )
580  {
581  if ( ( ! f.hasGeometry() ) ||
582  ( f.geometry().wkbType() == L->dataProvider()->wkbType() ) )
583  continue;
584 
585  if ( L->dataProvider()->convertToProviderType( f.geometry() ).isNull() )
586  {
587  commitErrors << tr( "ERROR: %n feature(s) not added - geometry type is not compatible with the current layer.", "not added features count", mAddedFeatures.size() );
588  return false;
589  }
590  }
591  }
592  }
593  else
594  {
595  commitErrors << tr( "ERROR: %n feature(s) not added - provider doesn't support adding features.", "not added features count", mAddedFeatures.size() );
596  return false;
597  }
598 
599  return true;
600 }
601 
602 bool QgsVectorLayerEditBuffer::commitChangesDeleteAttributes( bool &attributesDeleted, QStringList &commitErrors )
603 {
604  attributesDeleted = false;
605 
606  if ( mDeletedAttributeIds.isEmpty() )
607  return true;
608 
610  {
611  commitErrors << tr( "SUCCESS: %n attribute(s) deleted.", "deleted attributes count", mDeletedAttributeIds.size() );
612 
614 
615  mDeletedAttributeIds.clear();
616  attributesDeleted = true;
617  }
618  else
619  {
620  commitErrors << tr( "ERROR: %n attribute(s) not deleted.", "not deleted attributes count", mDeletedAttributeIds.size() );
621 #if 0
622  QString list = "ERROR: Pending attribute deletes:";
623  const auto constMDeletedAttributeIds = mDeletedAttributeIds;
624  for ( int idx : constMDeletedAttributeIds )
625  {
626  list.append( ' ' + L->fields().at( idx ).name() );
627  }
628  commitErrors << list;
629 #endif
630  return false;
631  }
632 
633  return true;
634 }
635 
636 bool QgsVectorLayerEditBuffer::commitChangesRenameAttributes( bool &attributesRenamed, QStringList &commitErrors )
637 {
638  attributesRenamed = false;
639 
640  if ( mRenamedAttributes.isEmpty() )
641  return true;
642 
644  {
645  commitErrors << tr( "SUCCESS: %n attribute(s) renamed.", "renamed attributes count", mRenamedAttributes.size() );
646 
648 
649  mRenamedAttributes.clear();
650  attributesRenamed = true;
651  }
652  else
653  {
654  commitErrors << tr( "ERROR: %n attribute(s) not renamed", "not renamed attributes count", mRenamedAttributes.size() );
655  return false;
656  }
657 
658  return true;
659 }
660 
661 bool QgsVectorLayerEditBuffer::commitChangesAddAttributes( bool &attributesAdded, QStringList &commitErrors )
662 {
663  attributesAdded = false;
664 
665  if ( mAddedAttributes.isEmpty() )
666  return true;
667 
669  {
670  commitErrors << tr( "SUCCESS: %n attribute(s) added.", "added attributes count", mAddedAttributes.size() );
672  mAddedAttributes.clear();
673  attributesAdded = true;
674  }
675  else
676  {
677  commitErrors << tr( "ERROR: %n new attribute(s) not added", "not added attributes count", mAddedAttributes.size() );
678 #if 0
679  QString list = "ERROR: Pending adds:";
680  const auto constMAddedAttributes = mAddedAttributes;
681  for ( QgsField f : constMAddedAttributes )
682  {
683  list.append( ' ' + f.name() );
684  }
685  commitErrors << list;
686 #endif
687  return false;
688  }
689 
690  return true;
691 }
692 
693 bool QgsVectorLayerEditBuffer::commitChangesCheckAttributesModifications( const QgsFields oldFields, QStringList &commitErrors )
694 {
695  L->updateFields();
696  QgsFields newFields = L->fields();
697 
698  if ( oldFields.count() != newFields.count() )
699  {
700  commitErrors << tr( "ERROR: the count of fields is incorrect after addition/removal of fields!" );
701  return false; // don't try attribute updates - they'll fail.
702  }
703 
704  for ( int i = 0; i < std::min( oldFields.count(), newFields.count() ); ++i )
705  {
706  QgsField oldField = oldFields.at( i );
707  QgsField newField = newFields.at( i );
708  if ( oldField != newField )
709  {
710  commitErrors
711  << tr( "ERROR: field with index %1 is not the same!" ).arg( i )
712  << tr( "Provider: %1" ).arg( L->providerType() )
713  << tr( "Storage: %1" ).arg( L->storageType() )
714  << QStringLiteral( "%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6" )
715  .arg( tr( "expected field" ),
716  oldField.name(),
717  QVariant::typeToName( oldField.type() ),
718  oldField.typeName() )
719  .arg( oldField.length() )
720  .arg( oldField.precision() )
721  << QStringLiteral( "%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6" )
722  .arg( tr( "retrieved field" ),
723  newField.name(),
724  QVariant::typeToName( newField.type() ),
725  newField.typeName() )
726  .arg( newField.length() )
727  .arg( newField.precision() );
728  return false; // don't try attribute updates - they'll fail.
729  }
730  }
731 
732  return true;
733 }
734 
735 bool QgsVectorLayerEditBuffer::commitChangesChangeAttributes( bool &attributesChanged, QStringList &commitErrors )
736 {
737  attributesChanged = false;
738 
740  {
742 
744  {
745  commitErrors << tr( "SUCCESS: %1 attribute value(s) and %2 geometries changed." ).arg( mChangedAttributeValues.size(), mChangedGeometries.size() );
746  attributesChanged = true;
748  mChangedAttributeValues.clear();
749 
751  mChangedGeometries.clear();
752  }
753  else
754  {
755  commitErrors << tr( "ERROR: %1 attributes and %2 geometries not changed.", "not changed attributes and geometries count" ).arg( mChangedAttributeValues.size() ).arg( mChangedGeometries.size() );
756  return false;
757  }
758  }
759 
760  if ( !mChangedGeometries.isEmpty() )
761  {
764  {
765  commitErrors << tr( "ERROR: %1 geometries not changed. Data provider '%2' does not have ChangeFeatures or ChangeGeometries capabilities", "not changed geometries count" )
766  .arg( mChangedGeometries.size() )
767  .arg( L->dataProvider()->name() );
768  return false;
769  }
770 
772  {
773  commitErrors << tr( "SUCCESS: %n geometries were changed.", "changed geometries count", mChangedGeometries.size() );
774  attributesChanged = true;
776  mChangedGeometries.clear();
777  }
778  else
779  {
780  commitErrors << tr( "ERROR: %n geometries not changed.", "not changed geometries count", mChangedGeometries.size() );
781  return false;
782  }
783  }
784 
785  if ( !mChangedAttributeValues.isEmpty() )
786  {
789  {
790  commitErrors << tr( "ERROR: %1 attribute value change(s) not applied. Data provider '%2' does not have ChangeFeatures or ChangeAttributeValues capabilities", "not changed attribute values count" )
791  .arg( mChangedAttributeValues.size() )
792  .arg( L->dataProvider()->name() );
793  return false;
794  }
795 
797  {
798  commitErrors << tr( "SUCCESS: %n attribute value(s) changed.", "changed attribute values count", mChangedAttributeValues.size() );
799  attributesChanged = true;
801  mChangedAttributeValues.clear();
802  }
803  else
804  {
805  commitErrors << tr( "ERROR: %n attribute value change(s) not applied.", "not changed attribute values count", mChangedAttributeValues.size() );
806 #if 0
807  QString list = "ERROR: pending changes:";
808  const auto constKeys = mChangedAttributeValues.keys();
809  for ( QgsFeatureId id : constKeys )
810  {
811  list.append( "\n " + FID_TO_STRING( id ) + '[' );
812  const auto constKeys = mChangedAttributeValues[ id ].keys();
813  for ( int idx : constKeys )
814  {
815  list.append( QString( " %1:%2" ).arg( L->fields().at( idx ).name() ).arg( mChangedAttributeValues[id][idx].toString() ) );
816  }
817  list.append( " ]" );
818  }
819  commitErrors << list;
820 #endif
821  return false;
822  }
823  }
824 
825  return true;
826 }
827 
828 bool QgsVectorLayerEditBuffer::commitChangesDeleteFeatures( bool &featuresDeleted, QStringList &commitErrors )
829 {
830  featuresDeleted = false;
831 
832  if ( mDeletedFeatureIds.isEmpty() )
833  return true;
834 
836  {
837  commitErrors << tr( "SUCCESS: %n feature(s) deleted.", "deleted features count", mDeletedFeatureIds.size() );
838  featuresDeleted = true;
839  // TODO[MD]: we should not need this here
840  for ( QgsFeatureId id : std::as_const( mDeletedFeatureIds ) )
841  {
842  mChangedAttributeValues.remove( id );
843  mChangedGeometries.remove( id );
844  }
845 
847 
848  mDeletedFeatureIds.clear();
849  }
850  else
851  {
852  commitErrors << tr( "ERROR: %n feature(s) not deleted.", "not deleted features count", mDeletedFeatureIds.size() );
853 #if 0
854  QString list = "ERROR: pending deletes:";
855  const auto constMDeletedFeatureIds = mDeletedFeatureIds;
856  for ( QgsFeatureId id : constMDeletedFeatureIds )
857  {
858  list.append( ' ' + FID_TO_STRING( id ) );
859  }
860  commitErrors << list;
861 #endif
862  return false;
863  }
864 
865  return true;
866 }
867 
868 bool QgsVectorLayerEditBuffer::commitChangesAddFeatures( bool &featuresAdded, QStringList &commitErrors )
869 {
870  featuresAdded = false;
871 
872  if ( mAddedFeatures.isEmpty() )
873  return true;
874 
876  {
877  QList<QgsFeatureId> ids;
878  QgsFeatureList featuresToAdd;
879  // get the list of added features in reversed order
880  // this will preserve the order how they have been added e.g. (-1, -2, -3) while in the map they are ordered (-3, -2, -1)
881  mapToReversedLists( mAddedFeatures, ids, featuresToAdd );
882 
883  // we need to strip any extra attributes here -- e.g. virtual fields, which should
884  // not be sent to the data provider. Refs #18784
885  for ( int i = 0; i < featuresToAdd.count(); ++i )
886  {
888  }
889 
890  if ( L->dataProvider()->addFeatures( featuresToAdd, QgsFeatureSink::Flag::RollBackOnErrors ) )
891  {
892  commitErrors << tr( "SUCCESS: %n feature(s) added.", "added features count", featuresToAdd.size() );
893  featuresAdded = true;
894  emit committedFeaturesAdded( L->id(), featuresToAdd );
895 
896  // notify everyone that the features with temporary ids were updated with permanent ids
897  for ( int i = 0; i < featuresToAdd.count(); ++i )
898  {
899  if ( featuresToAdd[i].id() != ids[i] )
900  {
901  //update selection
902  if ( L->mSelectedFeatureIds.contains( ids[i] ) )
903  {
904  L->mSelectedFeatureIds.remove( ids[i] );
905  L->mSelectedFeatureIds.insert( featuresToAdd[i].id() );
906  }
907  emit featureDeleted( ids[i] );
908  emit featureAdded( featuresToAdd[i].id() );
909  }
910  }
911 
912  mAddedFeatures.clear();
913  }
914  else
915  {
916  commitErrors << tr( "ERROR: %n feature(s) not added.", "not added features count", mAddedFeatures.size() );
917 #if 0
918  QString list = "ERROR: pending adds:";
919  const auto constMAddedFeatures = mAddedFeatures;
920  for ( QgsFeature f : constMAddedFeatures )
921  {
922  list.append( ' ' + FID_TO_STRING( f.id() ) + '[' );
923  for ( int i = 0; i < L->fields().size(); i++ )
924  {
925  list.append( QString( " %1:%2" ).arg( L->fields().at( i ).name() ).arg( f.attributes()[i].toString() ) );
926  }
927  list.append( " ]" );
928  }
929  commitErrors << list;
930 #endif
931  return false;
932  }
933  }
934  else
935  {
936  commitErrors << tr( "ERROR: %n feature(s) not added - provider doesn't support adding features.", "not added features count", mAddedFeatures.size() );
937  return false;
938  }
939 
940  return true;
941 }
A vector of attributes.
Definition: qgsattributes.h:58
virtual QString name() const =0
Returns a provider name.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsAttributes attributes
Definition: qgsfeature.h:65
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:153
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:139
QString name
Definition: qgsfield.h:60
int precision
Definition: qgsfield.h:57
int length
Definition: qgsfield.h:56
QVariant::Type type
Definition: qgsfield.h:58
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
void remove(int fieldIdx)
Removes the field with the given index.
Definition: qgsfields.cpp:101
@ OriginExpression
Field is calculated from an expression.
Definition: qgsfields.h:54
@ OriginEdit
Field has been temporarily added in editing mode (originIndex = index in the list of added attributes...
Definition: qgsfields.h:53
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
@ OriginProvider
Field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition: qgsfields.h:51
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Definition: qgsfields.cpp:189
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int fieldOriginIndex(int fieldIdx) const
Returns the field's origin index (its meaning is specific to each type of origin).
Definition: qgsfields.cpp:197
bool rename(int fieldIdx, const QString &name)
Renames a name of field.
Definition: qgsfields.cpp:72
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
QString providerType() const
Returns the provider type (provider key) for this layer.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QUndoStack * undoStack()
Returns pointer to layer's undo stack.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
This is the base class for vector data providers.
@ ChangeFeatures
Supports joint updates for attributes and geometry. Providers supporting this should still define Cha...
@ ChangeGeometries
Allows modifications of geometries.
@ DeleteAttributes
Allows deletion of attributes (fields)
@ DeleteFeatures
Allows deletion of features.
@ AddAttributes
Allows addition of new attributes (fields)
@ RenameAttributes
Supports renaming attributes (fields). Since QGIS 2.16.
@ ChangeAttributeValues
Allows modification of attribute values.
@ AddFeatures
Allows adding features.
QgsWkbTypes::Type wkbType() const override=0
Returns the geometry type which is returned by this layer.
bool supportedType(const QgsField &field) const
check if provider supports type of field
virtual bool changeGeometryValues(const QgsGeometryMap &geometry_map)
Changes geometries of existing features.
void clearErrors()
Clear recorded errors.
QStringList errors() const
Gets recorded errors.
virtual bool doesStrictFeatureTypeCheck() const
Returns true if the provider is strict about the type of inserted features (e.g.
QgsGeometry convertToProviderType(const QgsGeometry &geom) const
Converts the geometry to the provider type if possible / necessary.
virtual bool changeFeatures(const QgsChangedAttributesMap &attr_map, const QgsGeometryMap &geometry_map)
Changes attribute values and geometries of existing features.
virtual bool changeAttributeValues(const QgsChangedAttributesMap &attr_map)
Changes attribute values of existing features.
virtual bool deleteFeatures(const QgsFeatureIds &id)
Deletes one or more features from the provider.
bool addFeatures(QgsFeatureList &flist, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
QgsFields fields() const override=0
Returns the fields associated with this data provider.
virtual bool addAttributes(const QList< QgsField > &attributes)
Adds new attributes to the provider.
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
virtual bool renameAttributes(const QgsFieldNameMap &renamedAttributes)
Renames existing attributes.
virtual bool deleteAttributes(const QgsAttributeIds &attributes)
Deletes existing attributes from the provider.
bool hasErrors() const
Provider has errors to report.
The edit buffer group manages a group of edit buffers.
void committedAttributesDeleted(const QString &layerId, const QgsAttributeList &deletedAttributes)
Signals emitted after committing changes.
virtual bool deleteFeature(QgsFeatureId fid)
Delete a feature from the layer (but does not commit it)
QgsFeatureMap mAddedFeatures
New features which are not committed.
void setEditBufferGroup(QgsVectorLayerEditBufferGroup *editBufferGroup)
Set the parent edit buffer group for this edit buffer.
void committedAttributeValuesChanges(const QString &layerId, const QgsChangedAttributesMap &changedAttributesValues)
virtual bool renameAttribute(int attr, const QString &newName)
Renames an attribute field (but does not commit it)
void updateFeatureGeometry(QgsFeature &f)
Update feature with uncommitted geometry updates.
virtual bool deleteFeatures(const QgsFeatureIds &fid)
Deletes a set of features from the layer (but does not commit it)
void handleAttributeDeleted(int index)
Update added and changed features after removal of an attribute.
virtual bool addAttribute(const QgsField &field)
Adds an attribute field (but does not commit it) returns true if the field was added.
void committedAttributesAdded(const QString &layerId, const QList< QgsField > &addedAttributes)
virtual bool addFeatures(QgsFeatureList &features)
Insert a copy of the given features into the layer (but does not commit it)
QgsVectorLayerEditBuffer()=default
Constructor for QgsVectorLayerEditBuffer.
QgsFieldNameMap mRenamedAttributes
Renamed attributes which are not committed.
QgsFeatureIds allAddedOrEditedFeatures() const
Returns a list of the features IDs for all newly added or edited features in the buffer.
QgsGeometryMap mChangedGeometries
Changed geometries which are not committed.
void committedAttributesRenamed(const QString &layerId, const QgsFieldNameMap &renamedAttributes)
Emitted after committing an attribute rename.
virtual bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues)
Changes values of attributes (but does not commit it).
QgsAttributeList mDeletedAttributeIds
Deleted attributes fields which are not committed. The list is kept sorted.
QgsFeatureIds mDeletedFeatureIds
Deleted feature IDs which are not committed.
virtual bool isModified() const
Returns true if the provider has been modified since the last commit.
void updateFields(QgsFields &fields)
Updates fields.
void committedFeaturesAdded(const QString &layerId, const QgsFeatureList &addedFeatures)
void featureDeleted(QgsFeatureId fid)
QgsVectorLayerEditBufferGroup * editBufferGroup() const
Returns the parent edit buffer group for this edit buffer, or nullptr if not part of a group.
void committedGeometriesChanges(const QString &layerId, const QgsGeometryMap &changedGeometries)
virtual bool addFeature(QgsFeature &f)
Adds a feature.
virtual void rollBack()
Stop editing and discard the edits.
friend class QgsVectorLayerUndoCommandRenameAttribute
friend class QgsVectorLayerUndoCommandChangeGeometry
QgsVectorLayerEditBufferGroup * mEditBufferGroup
friend class QgsVectorLayerUndoCommandDeleteAttribute
void featureAdded(QgsFeatureId fid)
QgsChangedAttributesMap mChangedAttributeValues
Changed attributes values which are not committed.
virtual bool commitChanges(QStringList &commitErrors)
Attempts to commit any changes to disk.
friend class QgsVectorLayerUndoCommandChangeAttribute
friend class QgsVectorLayerUndoCommandAddAttribute
void handleAttributeAdded(int index)
Update added and changed features after addition of an attribute.
virtual bool deleteAttribute(int attr)
Deletes an attribute field (but does not commit it)
virtual bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
Changed an attribute value (but does not commit it)
void updateAttributeMapIndex(QgsAttributeMap &attrs, int index, int offset) const
Updates an index in an attribute map to a new value (for updates of changed attributes)
QList< QgsField > mAddedAttributes
Added attributes fields which are not committed.
virtual bool changeGeometry(QgsFeatureId fid, const QgsGeometry &geom)
Change feature's geometry.
void layerModified()
Emitted when modifications has been done on layer.
void updateChangedAttributes(QgsFeature &f)
Update feature with uncommitted attribute updates.
void committedFeaturesRemoved(const QString &layerId, const QgsFeatureIds &deletedFeatureIds)
friend class QgsVectorLayerUndoCommandDeleteFeature
static void matchAttributesToFields(QgsFeature &feature, const QgsFields &fields)
Matches the attributes in feature to the specified fields.
Represents a vector layer which manages a vector based data sets.
void updateFields()
Will regenerate the fields property of this layer by obtaining all fields from the dataProvider,...
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
virtual void updateExtents(bool force=false)
Update the extents for the layer.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:882
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
#define FID_IS_NEW(fid)
Definition: qgsfeatureid.h:31
#define FID_TO_STRING(fid)
Definition: qgsfeatureid.h:33
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
const QgsField & field
Definition: qgsfield.h:463
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void mapToReversedLists(const QMap< Key, T > &map, QList< Key > &ks, QList< T > &vs)
populate two lists (ks, vs) from map - in reverse order