QGIS API Documentation 3.29.0-Master (ade4f0cf0f)
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"
22#include "qgsvectorlayer.h"
23#include "qgsvectorlayerutils.h"
24#include "qgsmessagelog.h"
25
26
28template <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 --i;
36 ks.append( i.key() );
37 vs.append( i.value() );
38 }
39}
40
41
43 : L( layer )
44{
45 connect( L->undoStack(), &QUndoStack::indexChanged, this, &QgsVectorLayerEditBuffer::undoIndexChanged ); // TODO[MD]: queued?
46}
47
49{
50 return !L->undoStack()->isClean();
51}
52
53
55{
57 return;
58
59 QgsDebugMsgLevel( QStringLiteral( "undo index changed %1" ).arg( index ), 4 );
60 Q_UNUSED( index )
61 emit layerModified();
62}
63
64
66{
67 // delete attributes from the higher indices to lower indices
68 for ( int i = mDeletedAttributeIds.count() - 1; i >= 0; --i )
69 {
70 fields.remove( mDeletedAttributeIds.at( i ) );
71 }
72
73 // rename fields
74 QgsFieldNameMap::const_iterator renameIt = mRenamedAttributes.constBegin();
75 for ( ; renameIt != mRenamedAttributes.constEnd(); ++renameIt )
76 {
77 fields.rename( renameIt.key(), renameIt.value() );
78 }
79
80 // add new fields
81 for ( int i = 0; i < mAddedAttributes.count(); ++i )
82 {
83 fields.append( mAddedAttributes.at( i ), QgsFields::OriginEdit, i );
84 }
85}
86
88{
89 return mEditBufferGroup;
90}
91
93{
95}
96
98{
99 if ( mChangedGeometries.contains( f.id() ) )
101}
102
103
105{
106 QgsAttributes attrs = f.attributes();
107
108 // remove all attributes that will disappear - from higher indices to lower
109 for ( int idx = mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
110 {
111 attrs.remove( mDeletedAttributeIds[idx] );
112 }
113
114 // adjust size to accommodate added attributes
115 attrs.resize( attrs.count() + mAddedAttributes.count() );
116
117 // update changed attributes
118 if ( mChangedAttributeValues.contains( f.id() ) )
119 {
121 for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
122 attrs[it.key()] = it.value();
123 }
124
125 f.setAttributes( attrs );
126}
127
128
129
130
132{
134 {
135 return false;
136 }
137 if ( L->mFields.count() != f.attributes().count() )
138 {
139 QgsMessageLog::logMessage( tr( "cannot add feature, wrong field count: layer: %1 feature: %2:" ).arg( L->mFields.count() ).arg( f.attributes().count() ) );
140 return false;
141 }
142
143 // TODO: check correct geometry type
144
145 L->undoStack()->push( new QgsVectorLayerUndoCommandAddFeature( this, f ) );
146 return true;
147}
148
149
151{
153 return false;
154
155 // we don't want to emit layerModified for every added feature, rather just once for the batch lot
157
158 bool result = true;
159 bool anyAdded = false;
160 for ( QgsFeatureList::iterator iter = features.begin(); iter != features.end(); ++iter )
161 {
162 const bool thisFeatureResult = addFeature( *iter );
163 result = result && thisFeatureResult;
164 anyAdded |= thisFeatureResult;
165 }
166
168
169 if ( anyAdded )
170 emit layerModified();
171
172 L->updateExtents();
173 return result;
174}
175
176
177
179{
181 {
182 QgsDebugMsg( QStringLiteral( "Cannot delete features (missing DeleteFeature capability)" ) );
183 return false;
184 }
185
186 if ( FID_IS_NEW( fid ) )
187 {
188 if ( !mAddedFeatures.contains( fid ) )
189 {
190 QgsDebugMsg( QStringLiteral( "Cannot delete features (in the list of added features)" ) );
191 return false;
192 }
193 }
194 else // existing feature
195 {
196 if ( mDeletedFeatureIds.contains( fid ) )
197 {
198 QgsDebugMsg( QStringLiteral( "Cannot delete features (in the list of deleted features)" ) );
199 return false;
200 }
201 }
202
203 L->undoStack()->push( new QgsVectorLayerUndoCommandDeleteFeature( this, fid ) );
204 return true;
205}
206
208{
210 {
211 QgsDebugMsg( QStringLiteral( "Cannot delete features (missing DeleteFeatures capability)" ) );
212 return false;
213 }
214
215 // we don't want to emit layerModified for every deleted feature, rather just once for the batch lot
217
218 bool ok = true;
219 for ( QgsFeatureId fid : fids )
220 ok = deleteFeature( fid ) && ok;
221
223 emit layerModified();
224
225 return ok;
226}
227
228
230{
231 if ( !L->isSpatial() )
232 {
233 return false;
234 }
235
236 if ( FID_IS_NEW( fid ) )
237 {
238 if ( !mAddedFeatures.contains( fid ) )
239 return false;
240 }
242 return false;
243
244 // TODO: check compatible geometry
245
246 L->undoStack()->push( new QgsVectorLayerUndoCommandChangeGeometry( this, fid, geom ) );
247 return true;
248}
249
251{
252 bool success = true;
253
254 // we don't want to emit layerModified for every changed attribute, rather just once for the batch lot
256
257 for ( auto it = newValues.constBegin() ; it != newValues.constEnd(); ++it )
258 {
259 const int field = it.key();
260 const QVariant newValue = it.value();
261 QVariant oldValue;
262
263 if ( oldValues.contains( field ) )
264 oldValue = oldValues[field];
265
266 success &= changeAttributeValue( fid, field, newValue, oldValue );
267 }
268
270 emit layerModified();
271
272 return success;
273}
274
275bool QgsVectorLayerEditBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
276{
277 if ( FID_IS_NEW( fid ) )
278 {
279 if ( !mAddedFeatures.contains( fid ) )
280 return false;
281 }
283 {
284 return false;
285 }
286
287 if ( field < 0 || field >= L->fields().count() ||
290 return false;
291
292 L->undoStack()->push( new QgsVectorLayerUndoCommandChangeAttribute( this, fid, field, newValue, oldValue ) );
293 return true;
294}
295
296
298{
300 return false;
301
302 if ( field.name().isEmpty() )
303 return false;
304
305 const QgsFields fields = L->fields();
306 for ( const QgsField &updatedField : fields )
307 {
308 if ( updatedField.name() == field.name() )
309 return false;
310 }
311
312 if ( !L->dataProvider()->supportedType( field ) )
313 return false;
314
316 return true;
317}
318
319
321{
323 return false;
324
325 if ( index < 0 || index >= L->fields().count() )
326 return false;
327
328 // find out source of the field
329 QgsFields::FieldOrigin origin = L->fields().fieldOrigin( index );
330 int originIndex = L->fields().fieldOriginIndex( index );
331
332 if ( origin == QgsFields::OriginProvider && mDeletedAttributeIds.contains( originIndex ) )
333 return false;
334
335 if ( origin == QgsFields::OriginJoin )
336 return false;
337
338 L->undoStack()->push( new QgsVectorLayerUndoCommandDeleteAttribute( this, index ) );
339 return true;
340}
341
342bool QgsVectorLayerEditBuffer::renameAttribute( int index, const QString &newName )
343{
345 return false;
346
347 if ( newName.isEmpty() )
348 return false;
349
350 if ( index < 0 || index >= L->fields().count() )
351 return false;
352
353 const QgsFields fields = L->fields();
354 for ( const QgsField &updatedField : fields )
355 {
356 if ( updatedField.name() == newName )
357 return false;
358 }
359
360 L->undoStack()->push( new QgsVectorLayerUndoCommandRenameAttribute( this, index, newName ) );
361 return true;
362}
363
364
365bool QgsVectorLayerEditBuffer::commitChanges( QStringList &commitErrors )
366{
367 commitErrors.clear();
368
369 bool success = true;
370
371 // geometry updates attribute updates
372 // yes no => changeGeometryValues
373 // no yes => changeAttributeValues
374 // yes yes => changeFeatures
375
376 // Check geometry types
377 // to fix https://github.com/qgis/QGIS/issues/23663
378 if ( !mAddedFeatures.isEmpty() )
379 success &= commitChangesCheckGeometryTypeCompatibility( commitErrors );
380
381 const QgsFields oldFields = L->fields();
382
383 //
384 // delete attributes
385 //
386 bool attributesChanged = false;
387 if ( success && !mDeletedAttributeIds.isEmpty() )
388 {
389 bool attributesDeleted = false;
390 success &= commitChangesDeleteAttributes( attributesDeleted, commitErrors );
391 attributesChanged |= attributesDeleted;
392 }
393
394 // rename attributes
395 if ( success && !mRenamedAttributes.isEmpty() )
396 {
397 bool attributesRenamed = false;
398 success &= commitChangesRenameAttributes( attributesRenamed, commitErrors );
399 attributesChanged |= attributesRenamed;
400 }
401
402 //
403 // add attributes
404 //
405 if ( success && !mAddedAttributes.isEmpty() )
406 {
407 bool attributesAdded = false;
408 success &= commitChangesAddAttributes( attributesAdded, commitErrors );
409 attributesChanged |= attributesAdded;
410 }
411
412 //
413 // check that addition/removal went as expected
414 //
415 if ( success && attributesChanged )
416 success &= commitChangesCheckAttributesModifications( oldFields, commitErrors );
417
418 //
419 // change attributes
420 //
421 if ( success && ( !mChangedAttributeValues.isEmpty() || !mChangedGeometries.isEmpty() ) )
422 {
423 bool attributesChanged;
424 success &= commitChangesChangeAttributes( attributesChanged, commitErrors );
425 }
426
427 //
428 // delete features
429 //
430 if ( success && !mDeletedFeatureIds.isEmpty() )
431 {
432 bool featuresDeleted;
433 success &= commitChangesDeleteFeatures( featuresDeleted, commitErrors );
434 }
435
436 //
437 // add features
438 //
439 if ( success && !mAddedFeatures.isEmpty() )
440 {
441 bool featuresAdded;
442 success &= commitChangesAddFeatures( featuresAdded, commitErrors );
443 }
444
445 QgsVectorDataProvider *provider = L->dataProvider();
446 if ( !success && provider->hasErrors() )
447 {
448 commitErrors << tr( "\n Provider errors:" );
449 const auto constErrors = provider->errors();
450 for ( QString e : constErrors )
451 {
452 commitErrors << " " + e.replace( '\n', QLatin1String( "\n " ) );
453 }
454 provider->clearErrors();
455 }
456
457 return success;
458}
459
460
462{
463 if ( !isModified() )
464 return;
465
466 // limit canvas redraws to one by jumping to beginning of stack
467 // see QgsUndoWidget::indexChanged
468 L->undoStack()->setIndex( 0 );
469
470 Q_ASSERT( mAddedAttributes.isEmpty() );
471 Q_ASSERT( mDeletedAttributeIds.isEmpty() );
472 Q_ASSERT( mChangedAttributeValues.isEmpty() );
473 Q_ASSERT( mChangedGeometries.isEmpty() );
474 Q_ASSERT( mAddedFeatures.isEmpty() );
475}
476
478{
479 return qgis::listToSet( mAddedFeatures.keys() ).unite( qgis::listToSet( mChangedAttributeValues.keys() ) ).unite( qgis::listToSet( mChangedGeometries.keys() ) );
480}
481
482#if 0
483QString QgsVectorLayerEditBuffer::dumpEditBuffer()
484{
485 QString msg;
486 if ( !mChangedGeometries.isEmpty() )
487 {
488 msg += "CHANGED GEOMETRIES:\n";
489 for ( QgsGeometryMap::const_iterator it = mChangedGeometries.begin(); it != mChangedGeometries.end(); ++it )
490 {
491 // QgsFeatureId, QgsGeometry
492 msg += QString( "- FID %1: %2" ).arg( it.key() ).arg( it.value().to );
493 }
494 }
495 return msg;
496}
497#endif
498
500{
501 // go through the changed attributes map and adapt indices
502 QgsChangedAttributesMap::iterator it = mChangedAttributeValues.begin();
503 for ( ; it != mChangedAttributeValues.end(); ++it )
504 {
505 updateAttributeMapIndex( it.value(), index, + 1 );
506 }
507
508 // go through added features and adapt attributes
509 QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
510 for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
511 {
512 QgsAttributes attrs = featureIt->attributes();
513 attrs.insert( index, QVariant() );
514 featureIt->setAttributes( attrs );
515 }
516
517 // go through renamed attributes and adapt
518 QList< int > sortedRenamedIndices = mRenamedAttributes.keys();
519 //sort keys
520 std::sort( sortedRenamedIndices.begin(), sortedRenamedIndices.end(), std::greater< int >() );
521 const auto constSortedRenamedIndices = sortedRenamedIndices;
522 for ( int renameIndex : constSortedRenamedIndices )
523 {
524 if ( renameIndex >= index )
525 {
526 mRenamedAttributes[ renameIndex + 1 ] = mRenamedAttributes.value( renameIndex );
527 }
528 }
529 //remove last
530 mRenamedAttributes.remove( index );
531}
532
534{
535 // go through the changed attributes map and adapt indices
536 QgsChangedAttributesMap::iterator it = mChangedAttributeValues.begin();
537 for ( ; it != mChangedAttributeValues.end(); ++it )
538 {
539 QgsAttributeMap &attrMap = it.value();
540 // remove the attribute
541 if ( attrMap.contains( index ) )
542 attrMap.remove( index );
543
544 // update attribute indices
545 updateAttributeMapIndex( attrMap, index, -1 );
546 }
547
548 // go through added features and adapt attributes
549 QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
550 for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
551 {
552 QgsAttributes attrs = featureIt->attributes();
553 attrs.remove( index );
554 featureIt->setAttributes( attrs );
555 }
556
557 // go through rename attributes and adapt
558 QList< int > sortedRenamedIndices = mRenamedAttributes.keys();
559 //sort keys
560 std::sort( sortedRenamedIndices.begin(), sortedRenamedIndices.end() );
561 int last = -1;
562 mRenamedAttributes.remove( index );
563 const auto constSortedRenamedIndices = sortedRenamedIndices;
564 for ( int renameIndex : constSortedRenamedIndices )
565 {
566 if ( renameIndex > index )
567 {
568 mRenamedAttributes.insert( renameIndex - 1, mRenamedAttributes.value( renameIndex ) );
569 last = renameIndex;
570 }
571 }
572 //remove last
573 if ( last > -1 )
574 mRenamedAttributes.remove( last );
575}
576
577
578
580{
581 QgsAttributeMap updatedMap;
582 for ( QgsAttributeMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
583 {
584 int attrIndex = it.key();
585 updatedMap.insert( attrIndex < index ? attrIndex : attrIndex + offset, it.value() );
586 }
587 map = updatedMap;
588}
589
590
591
593{
594 L->updateFields();
595}
596
597bool QgsVectorLayerEditBuffer::commitChangesCheckGeometryTypeCompatibility( QStringList &commitErrors )
598{
599 if ( mAddedFeatures.isEmpty() )
600 return true;
601
603 {
605 {
606 for ( const QgsFeature &f : std::as_const( mAddedFeatures ) )
607 {
608 if ( ( ! f.hasGeometry() ) ||
609 ( f.geometry().wkbType() == L->dataProvider()->wkbType() ) )
610 continue;
611
612 if ( L->dataProvider()->convertToProviderType( f.geometry() ).isNull() )
613 {
614 commitErrors << tr( "ERROR: %n feature(s) not added - geometry type is not compatible with the current layer.", "not added features count", mAddedFeatures.size() );
615 return false;
616 }
617 }
618 }
619 }
620 else
621 {
622 commitErrors << tr( "ERROR: %n feature(s) not added - provider doesn't support adding features.", "not added features count", mAddedFeatures.size() );
623 return false;
624 }
625
626 return true;
627}
628
629bool QgsVectorLayerEditBuffer::commitChangesDeleteAttributes( bool &attributesDeleted, QStringList &commitErrors )
630{
631 attributesDeleted = false;
632
633 if ( mDeletedAttributeIds.isEmpty() )
634 return true;
635
637 {
638 commitErrors << tr( "SUCCESS: %n attribute(s) deleted.", "deleted attributes count", mDeletedAttributeIds.size() );
639
641
642 mDeletedAttributeIds.clear();
643 attributesDeleted = true;
644 }
645 else
646 {
647 commitErrors << tr( "ERROR: %n attribute(s) not deleted.", "not deleted attributes count", mDeletedAttributeIds.size() );
648#if 0
649 QString list = "ERROR: Pending attribute deletes:";
650 const auto constMDeletedAttributeIds = mDeletedAttributeIds;
651 for ( int idx : constMDeletedAttributeIds )
652 {
653 list.append( ' ' + L->fields().at( idx ).name() );
654 }
655 commitErrors << list;
656#endif
657 return false;
658 }
659
660 return true;
661}
662
663bool QgsVectorLayerEditBuffer::commitChangesRenameAttributes( bool &attributesRenamed, QStringList &commitErrors )
664{
665 attributesRenamed = false;
666
667 if ( mRenamedAttributes.isEmpty() )
668 return true;
669
671 {
672 commitErrors << tr( "SUCCESS: %n attribute(s) renamed.", "renamed attributes count", mRenamedAttributes.size() );
673
675
676 mRenamedAttributes.clear();
677 attributesRenamed = true;
678 }
679 else
680 {
681 commitErrors << tr( "ERROR: %n attribute(s) not renamed", "not renamed attributes count", mRenamedAttributes.size() );
682 return false;
683 }
684
685 return true;
686}
687
688bool QgsVectorLayerEditBuffer::commitChangesAddAttributes( bool &attributesAdded, QStringList &commitErrors )
689{
690 attributesAdded = false;
691
692 if ( mAddedAttributes.isEmpty() )
693 return true;
694
696 {
697 commitErrors << tr( "SUCCESS: %n attribute(s) added.", "added attributes count", mAddedAttributes.size() );
699 mAddedAttributes.clear();
700 attributesAdded = true;
701 }
702 else
703 {
704 commitErrors << tr( "ERROR: %n new attribute(s) not added", "not added attributes count", mAddedAttributes.size() );
705#if 0
706 QString list = "ERROR: Pending adds:";
707 const auto constMAddedAttributes = mAddedAttributes;
708 for ( QgsField f : constMAddedAttributes )
709 {
710 list.append( ' ' + f.name() );
711 }
712 commitErrors << list;
713#endif
714 return false;
715 }
716
717 return true;
718}
719
720bool QgsVectorLayerEditBuffer::commitChangesCheckAttributesModifications( const QgsFields oldFields, QStringList &commitErrors )
721{
722 L->updateFields();
723 QgsFields newFields = L->fields();
724
725 if ( oldFields.count() != newFields.count() )
726 {
727 commitErrors << tr( "ERROR: the count of fields is incorrect after addition/removal of fields!" );
728 return false; // don't try attribute updates - they'll fail.
729 }
730
731 for ( int i = 0; i < std::min( oldFields.count(), newFields.count() ); ++i )
732 {
733 QgsField oldField = oldFields.at( i );
734 QgsField newField = newFields.at( i );
735 if ( oldField != newField )
736 {
737 commitErrors
738 << tr( "ERROR: field with index %1 is not the same!" ).arg( i )
739 << tr( "Provider: %1" ).arg( L->providerType() )
740 << tr( "Storage: %1" ).arg( L->storageType() )
741 << QStringLiteral( "%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6" )
742 .arg( tr( "expected field" ),
743 oldField.name(),
744 QVariant::typeToName( oldField.type() ),
745 oldField.typeName() )
746 .arg( oldField.length() )
747 .arg( oldField.precision() )
748 << QStringLiteral( "%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6" )
749 .arg( tr( "retrieved field" ),
750 newField.name(),
751 QVariant::typeToName( newField.type() ),
752 newField.typeName() )
753 .arg( newField.length() )
754 .arg( newField.precision() );
755 return false; // don't try attribute updates - they'll fail.
756 }
757 }
758
759 return true;
760}
761
762bool QgsVectorLayerEditBuffer::commitChangesChangeAttributes( bool &attributesChanged, QStringList &commitErrors )
763{
764 attributesChanged = false;
765
767 {
769
771 {
772 commitErrors << tr( "SUCCESS: %1 attribute value(s) and %2 geometries changed." ).arg( mChangedAttributeValues.size(), mChangedGeometries.size() );
773 attributesChanged = true;
776
778 mChangedGeometries.clear();
779 }
780 else
781 {
782 commitErrors << tr( "ERROR: %1 attributes and %2 geometries not changed.", "not changed attributes and geometries count" ).arg( mChangedAttributeValues.size() ).arg( mChangedGeometries.size() );
783 return false;
784 }
785 }
786
787 if ( !mChangedGeometries.isEmpty() )
788 {
791 {
792 commitErrors << tr( "ERROR: %1 geometries not changed. Data provider '%2' does not have ChangeFeatures or ChangeGeometries capabilities", "not changed geometries count" )
793 .arg( mChangedGeometries.size() )
794 .arg( L->dataProvider()->name() );
795 return false;
796 }
797
799 {
800 commitErrors << tr( "SUCCESS: %n geometries were changed.", "changed geometries count", mChangedGeometries.size() );
801 attributesChanged = true;
803 mChangedGeometries.clear();
804 }
805 else
806 {
807 commitErrors << tr( "ERROR: %n geometries not changed.", "not changed geometries count", mChangedGeometries.size() );
808 return false;
809 }
810 }
811
812 if ( !mChangedAttributeValues.isEmpty() )
813 {
816 {
817 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" )
818 .arg( mChangedAttributeValues.size() )
819 .arg( L->dataProvider()->name() );
820 return false;
821 }
822
824 {
825 commitErrors << tr( "SUCCESS: %n attribute value(s) changed.", "changed attribute values count", mChangedAttributeValues.size() );
826 attributesChanged = true;
829 }
830 else
831 {
832 commitErrors << tr( "ERROR: %n attribute value change(s) not applied.", "not changed attribute values count", mChangedAttributeValues.size() );
833#if 0
834 QString list = "ERROR: pending changes:";
835 const auto constKeys = mChangedAttributeValues.keys();
836 for ( QgsFeatureId id : constKeys )
837 {
838 list.append( "\n " + FID_TO_STRING( id ) + '[' );
839 const auto constKeys = mChangedAttributeValues[ id ].keys();
840 for ( int idx : constKeys )
841 {
842 list.append( QString( " %1:%2" ).arg( L->fields().at( idx ).name() ).arg( mChangedAttributeValues[id][idx].toString() ) );
843 }
844 list.append( " ]" );
845 }
846 commitErrors << list;
847#endif
848 return false;
849 }
850 }
851
852 return true;
853}
854
855bool QgsVectorLayerEditBuffer::commitChangesDeleteFeatures( bool &featuresDeleted, QStringList &commitErrors )
856{
857 featuresDeleted = false;
858
859 if ( mDeletedFeatureIds.isEmpty() )
860 return true;
861
863 {
864 commitErrors << tr( "SUCCESS: %n feature(s) deleted.", "deleted features count", mDeletedFeatureIds.size() );
865 featuresDeleted = true;
866 // TODO[MD]: we should not need this here
867 for ( QgsFeatureId id : std::as_const( mDeletedFeatureIds ) )
868 {
869 mChangedAttributeValues.remove( id );
870 mChangedGeometries.remove( id );
871 }
872
874
875 mDeletedFeatureIds.clear();
876 }
877 else
878 {
879 commitErrors << tr( "ERROR: %n feature(s) not deleted.", "not deleted features count", mDeletedFeatureIds.size() );
880#if 0
881 QString list = "ERROR: pending deletes:";
882 const auto constMDeletedFeatureIds = mDeletedFeatureIds;
883 for ( QgsFeatureId id : constMDeletedFeatureIds )
884 {
885 list.append( ' ' + FID_TO_STRING( id ) );
886 }
887 commitErrors << list;
888#endif
889 return false;
890 }
891
892 return true;
893}
894
895bool QgsVectorLayerEditBuffer::commitChangesAddFeatures( bool &featuresAdded, QStringList &commitErrors )
896{
897 featuresAdded = false;
898
899 if ( mAddedFeatures.isEmpty() )
900 return true;
901
903 {
904 QList<QgsFeatureId> ids;
905 QgsFeatureList featuresToAdd;
906 // get the list of added features in reversed order
907 // 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)
908 mapToReversedLists( mAddedFeatures, ids, featuresToAdd );
909
910 // we need to strip any extra attributes here -- e.g. virtual fields, which should
911 // not be sent to the data provider. Refs #18784
912 for ( int i = 0; i < featuresToAdd.count(); ++i )
913 {
915 }
916
917 if ( L->dataProvider()->addFeatures( featuresToAdd, QgsFeatureSink::Flag::RollBackOnErrors ) )
918 {
919 commitErrors << tr( "SUCCESS: %n feature(s) added.", "added features count", featuresToAdd.size() );
920 featuresAdded = true;
921 emit committedFeaturesAdded( L->id(), featuresToAdd );
922
923 // notify everyone that the features with temporary ids were updated with permanent ids
924 for ( int i = 0; i < featuresToAdd.count(); ++i )
925 {
926 if ( featuresToAdd[i].id() != ids[i] )
927 {
928 //update selection
929 if ( L->mSelectedFeatureIds.contains( ids[i] ) )
930 {
931 L->mSelectedFeatureIds.remove( ids[i] );
932 L->mSelectedFeatureIds.insert( featuresToAdd[i].id() );
933 }
934 emit featureDeleted( ids[i] );
935 emit featureAdded( featuresToAdd[i].id() );
936 }
937 }
938
939 mAddedFeatures.clear();
940 }
941 else
942 {
943 commitErrors << tr( "ERROR: %n feature(s) not added.", "not added features count", mAddedFeatures.size() );
944#if 0
945 QString list = "ERROR: pending adds:";
946 const auto constMAddedFeatures = mAddedFeatures;
947 for ( QgsFeature f : constMAddedFeatures )
948 {
949 list.append( ' ' + FID_TO_STRING( f.id() ) + '[' );
950 for ( int i = 0; i < L->fields().size(); i++ )
951 {
952 list.append( QString( " %1:%2" ).arg( L->fields().at( i ).name() ).arg( f.attributes()[i].toString() ) );
953 }
954 list.append( " ]" );
955 }
956 commitErrors << list;
957#endif
958 return false;
959 }
960 }
961 else
962 {
963 commitErrors << tr( "ERROR: %n feature(s) not added - provider doesn't support adding features.", "not added features count", mAddedFeatures.size() );
964 return false;
965 }
966
967 return true;
968}
A vector of attributes.
Definition: qgsattributes.h:59
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:160
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
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:140
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:164
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
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:42
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:922
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