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