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