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