QGIS API Documentation 3.41.0-Master (02257426e5a)
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 {
298 return false;
299 }
300
301 L->undoStack()->push( new QgsVectorLayerUndoCommandChangeAttribute( this, fid, field, newValue, oldValue ) );
302 return true;
303}
304
305
307{
309 return false;
310
311 if ( field.name().isEmpty() )
312 return false;
313
314 const QgsFields fields = L->fields();
315 for ( const QgsField &updatedField : fields )
316 {
317 if ( updatedField.name() == field.name() )
318 return false;
319 }
320
321 if ( !L->dataProvider()->supportedType( field ) )
322 return false;
323
324 L->undoStack()->push( new QgsVectorLayerUndoCommandAddAttribute( this, field ) );
325 return true;
326}
327
328
330{
332 return false;
333
334 if ( index < 0 || index >= L->fields().count() )
335 return false;
336
337 // find out source of the field
338 Qgis::FieldOrigin origin = L->fields().fieldOrigin( index );
339 int originIndex = L->fields().fieldOriginIndex( index );
340
341 if ( origin == Qgis::FieldOrigin::Provider && mDeletedAttributeIds.contains( originIndex ) )
342 return false;
343
344 if ( origin == Qgis::FieldOrigin::Join )
345 return false;
346
347 L->undoStack()->push( new QgsVectorLayerUndoCommandDeleteAttribute( this, index ) );
348 return true;
349}
350
351bool QgsVectorLayerEditBuffer::renameAttribute( int index, const QString &newName )
352{
354 return false;
355
356 if ( newName.isEmpty() )
357 return false;
358
359 if ( index < 0 || index >= L->fields().count() )
360 return false;
361
362 const QgsFields fields = L->fields();
363 for ( const QgsField &updatedField : fields )
364 {
365 if ( updatedField.name() == newName )
366 return false;
367 }
368
369 L->undoStack()->push( new QgsVectorLayerUndoCommandRenameAttribute( this, index, newName ) );
370 return true;
371}
372
373
374bool QgsVectorLayerEditBuffer::commitChanges( QStringList &commitErrors )
375{
376 commitErrors.clear();
377
378 bool success = true;
379
380 // geometry updates attribute updates
381 // yes no => changeGeometryValues
382 // no yes => changeAttributeValues
383 // yes yes => changeFeatures
384
385 // Check geometry types
386 // to fix https://github.com/qgis/QGIS/issues/23663
387 if ( !mAddedFeatures.isEmpty() )
388 success &= commitChangesCheckGeometryTypeCompatibility( commitErrors );
389
390 const QgsFields oldFields = L->fields();
391
392 //
393 // delete attributes
394 //
395 bool attributesChanged = false;
396 if ( success && !mDeletedAttributeIds.isEmpty() )
397 {
398 bool attributesDeleted = false;
399 success &= commitChangesDeleteAttributes( attributesDeleted, commitErrors );
400 attributesChanged |= attributesDeleted;
401 }
402
403 // rename attributes
404 if ( success && !mRenamedAttributes.isEmpty() )
405 {
406 bool attributesRenamed = false;
407 success &= commitChangesRenameAttributes( attributesRenamed, commitErrors );
408 attributesChanged |= attributesRenamed;
409 }
410
411 //
412 // add attributes
413 //
414 if ( success && !mAddedAttributes.isEmpty() )
415 {
416 bool attributesAdded = false;
417 success &= commitChangesAddAttributes( attributesAdded, commitErrors );
418 attributesChanged |= attributesAdded;
419 }
420
421 //
422 // check that addition/removal went as expected
423 //
424 if ( success && attributesChanged )
425 success &= commitChangesCheckAttributesModifications( oldFields, commitErrors );
426
427 //
428 // change attributes
429 //
430 if ( success && ( !mChangedAttributeValues.isEmpty() || !mChangedGeometries.isEmpty() ) )
431 {
432 bool attributesChanged;
433 success &= commitChangesChangeAttributes( attributesChanged, commitErrors );
434 }
435
436 //
437 // delete features
438 //
439 if ( success && !mDeletedFeatureIds.isEmpty() )
440 {
441 bool featuresDeleted;
442 success &= commitChangesDeleteFeatures( featuresDeleted, commitErrors );
443 }
444
445 //
446 // add features
447 //
448 if ( success && !mAddedFeatures.isEmpty() )
449 {
450 bool featuresAdded;
451 success &= commitChangesAddFeatures( featuresAdded, commitErrors );
452 }
453
454 QgsVectorDataProvider *provider = L->dataProvider();
455 if ( !success && provider->hasErrors() )
456 {
457 commitErrors << tr( "\n Provider errors:" );
458 const auto constErrors = provider->errors();
459 for ( QString e : constErrors )
460 {
461 commitErrors << " " + e.replace( '\n', QLatin1String( "\n " ) );
462 }
463 provider->clearErrors();
464 }
465
466 return success;
467}
468
469
471{
472 if ( !isModified() )
473 return;
474
475 // limit canvas redraws to one by jumping to beginning of stack
476 // see QgsUndoWidget::indexChanged
477 L->undoStack()->setIndex( 0 );
478
479 Q_ASSERT( mAddedAttributes.isEmpty() );
480 Q_ASSERT( mDeletedAttributeIds.isEmpty() );
481 Q_ASSERT( mChangedAttributeValues.isEmpty() );
482 Q_ASSERT( mChangedGeometries.isEmpty() );
483 Q_ASSERT( mAddedFeatures.isEmpty() );
484}
485
487{
488 return qgis::listToSet( mAddedFeatures.keys() ).unite( qgis::listToSet( mChangedAttributeValues.keys() ) ).unite( qgis::listToSet( mChangedGeometries.keys() ) );
489}
490
491#if 0
492QString QgsVectorLayerEditBuffer::dumpEditBuffer()
493{
494 QString msg;
495 if ( !mChangedGeometries.isEmpty() )
496 {
497 msg += "CHANGED GEOMETRIES:\n";
498 for ( QgsGeometryMap::const_iterator it = mChangedGeometries.begin(); it != mChangedGeometries.end(); ++it )
499 {
500 // QgsFeatureId, QgsGeometry
501 msg += QString( "- FID %1: %2" ).arg( it.key() ).arg( it.value().to );
502 }
503 }
504 return msg;
505}
506#endif
507
509{
510 // go through the changed attributes map and adapt indices
511 QgsChangedAttributesMap::iterator it = mChangedAttributeValues.begin();
512 for ( ; it != mChangedAttributeValues.end(); ++it )
513 {
514 updateAttributeMapIndex( it.value(), index, + 1 );
515 }
516
517 // go through added features and adapt attributes
518 QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
519 for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
520 {
521 QgsAttributes attrs = featureIt->attributes();
522 attrs.insert( index, QVariant() );
523 featureIt->setAttributes( attrs );
524 QgsFields fields;
525 const QgsFields oldFields = featureIt->fields();
526 for ( int i = 0; i < oldFields.size(); i++ )
527 {
528 if ( i == index )
529 {
530 fields.append( field, L->fields().fieldOrigin( L->fields().indexFromName( field.name() ) ) );
531 }
532 fields.append( oldFields.at( i ), oldFields.fieldOrigin( i ) );
533 }
534 if ( index == oldFields.size() )
535 {
536 fields.append( field, L->fields().fieldOrigin( L->fields().indexFromName( field.name() ) ) );
537 }
538 featureIt->setFields( fields, false );
539 }
540
541 // go through renamed attributes and adapt
542 QList< int > sortedRenamedIndices = mRenamedAttributes.keys();
543 //sort keys
544 std::sort( sortedRenamedIndices.begin(), sortedRenamedIndices.end(), std::greater< int >() );
545 const auto constSortedRenamedIndices = sortedRenamedIndices;
546 for ( int renameIndex : constSortedRenamedIndices )
547 {
548 if ( renameIndex >= index )
549 {
550 mRenamedAttributes[ renameIndex + 1 ] = mRenamedAttributes.value( renameIndex );
551 }
552 }
553 //remove last
554 mRenamedAttributes.remove( index );
555}
556
558{
559 // go through the changed attributes map and adapt indices
560 QgsChangedAttributesMap::iterator it = mChangedAttributeValues.begin();
561 for ( ; it != mChangedAttributeValues.end(); ++it )
562 {
563 QgsAttributeMap &attrMap = it.value();
564 // remove the attribute
565 if ( attrMap.contains( index ) )
566 attrMap.remove( index );
567
568 // update attribute indices
569 updateAttributeMapIndex( attrMap, index, -1 );
570 }
571
572 // go through added features and adapt attributes
573 QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
574 for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
575 {
576 QgsAttributes attrs = featureIt->attributes();
577 attrs.remove( index );
578 featureIt->setAttributes( attrs );
579 QgsFields fields = featureIt->fields();
580 fields.remove( index );
581 featureIt->setFields( fields, false );
582 }
583
584 // go through rename attributes and adapt
585 QList< int > sortedRenamedIndices = mRenamedAttributes.keys();
586 //sort keys
587 std::sort( sortedRenamedIndices.begin(), sortedRenamedIndices.end() );
588 int last = -1;
589 mRenamedAttributes.remove( index );
590 const auto constSortedRenamedIndices = sortedRenamedIndices;
591 for ( int renameIndex : constSortedRenamedIndices )
592 {
593 if ( renameIndex > index )
594 {
595 mRenamedAttributes.insert( renameIndex - 1, mRenamedAttributes.value( renameIndex ) );
596 last = renameIndex;
597 }
598 }
599 //remove last
600 if ( last > -1 )
601 mRenamedAttributes.remove( last );
602}
603
604
605
607{
608 QgsAttributeMap updatedMap;
609 for ( QgsAttributeMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
610 {
611 int attrIndex = it.key();
612 updatedMap.insert( attrIndex < index ? attrIndex : attrIndex + offset, it.value() );
613 }
614 map = updatedMap;
615}
616
617
618
623
624bool QgsVectorLayerEditBuffer::commitChangesCheckGeometryTypeCompatibility( QStringList &commitErrors )
625{
626 if ( mAddedFeatures.isEmpty() )
627 return true;
628
630 {
632 {
633 for ( const QgsFeature &f : std::as_const( mAddedFeatures ) )
634 {
635 if ( ( ! f.hasGeometry() ) ||
636 ( f.geometry().wkbType() == L->dataProvider()->wkbType() ) )
637 continue;
638
639 if ( L->dataProvider()->convertToProviderType( f.geometry() ).isNull() )
640 {
641 commitErrors << tr( "ERROR: %n feature(s) not added - geometry type is not compatible with the current layer.", "not added features count", mAddedFeatures.size() );
642 return false;
643 }
644 }
645 }
646 }
647 else
648 {
649 commitErrors << tr( "ERROR: %n feature(s) not added - provider doesn't support adding features.", "not added features count", mAddedFeatures.size() );
650 return false;
651 }
652
653 return true;
654}
655
656bool QgsVectorLayerEditBuffer::commitChangesDeleteAttributes( bool &attributesDeleted, QStringList &commitErrors )
657{
658 attributesDeleted = false;
659
660 if ( mDeletedAttributeIds.isEmpty() )
661 return true;
662
664 {
665 commitErrors << tr( "SUCCESS: %n attribute(s) deleted.", "deleted attributes count", mDeletedAttributeIds.size() );
666
668
669 mDeletedAttributeIds.clear();
670 attributesDeleted = true;
671 }
672 else
673 {
674 commitErrors << tr( "ERROR: %n attribute(s) not deleted.", "not deleted attributes count", mDeletedAttributeIds.size() );
675#if 0
676 QString list = "ERROR: Pending attribute deletes:";
677 const auto constMDeletedAttributeIds = mDeletedAttributeIds;
678 for ( int idx : constMDeletedAttributeIds )
679 {
680 list.append( ' ' + L->fields().at( idx ).name() );
681 }
682 commitErrors << list;
683#endif
684 return false;
685 }
686
687 return true;
688}
689
690bool QgsVectorLayerEditBuffer::commitChangesRenameAttributes( bool &attributesRenamed, QStringList &commitErrors )
691{
692 attributesRenamed = false;
693
694 if ( mRenamedAttributes.isEmpty() )
695 return true;
696
698 {
699 commitErrors << tr( "SUCCESS: %n attribute(s) renamed.", "renamed attributes count", mRenamedAttributes.size() );
700
702
703 mRenamedAttributes.clear();
704 attributesRenamed = true;
705 }
706 else
707 {
708 commitErrors << tr( "ERROR: %n attribute(s) not renamed", "not renamed attributes count", mRenamedAttributes.size() );
709 return false;
710 }
711
712 return true;
713}
714
715bool QgsVectorLayerEditBuffer::commitChangesAddAttributes( bool &attributesAdded, QStringList &commitErrors )
716{
717 attributesAdded = false;
718
719 if ( mAddedAttributes.isEmpty() )
720 return true;
721
723 {
724 commitErrors << tr( "SUCCESS: %n attribute(s) added.", "added attributes count", mAddedAttributes.size() );
726 mAddedAttributes.clear();
727 attributesAdded = true;
728 }
729 else
730 {
731 commitErrors << tr( "ERROR: %n new attribute(s) not added", "not added attributes count", mAddedAttributes.size() );
732#if 0
733 QString list = "ERROR: Pending adds:";
734 const auto constMAddedAttributes = mAddedAttributes;
735 for ( QgsField f : constMAddedAttributes )
736 {
737 list.append( ' ' + f.name() );
738 }
739 commitErrors << list;
740#endif
741 return false;
742 }
743
744 return true;
745}
746
747bool QgsVectorLayerEditBuffer::commitChangesCheckAttributesModifications( const QgsFields oldFields, QStringList &commitErrors )
748{
749 L->updateFields();
750 QgsFields newFields = L->fields();
751
752 if ( oldFields.count() != newFields.count() )
753 {
754 commitErrors << tr( "ERROR: the count of fields is incorrect after addition/removal of fields!" );
755 return false; // don't try attribute updates - they'll fail.
756 }
757
758 for ( int i = 0; i < std::min( oldFields.count(), newFields.count() ); ++i )
759 {
760 QgsField oldField = oldFields.at( i );
761 QgsField newField = newFields.at( i );
762 if ( oldField != newField )
763 {
764 commitErrors
765 << tr( "ERROR: field with index %1 is not the same!" ).arg( i )
766 << tr( "Provider: %1" ).arg( L->providerType() )
767 << tr( "Storage: %1" ).arg( L->storageType() )
768 << QStringLiteral( "%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6" )
769 .arg( tr( "expected field" ),
770 oldField.name(),
771 QVariant::typeToName( oldField.type() ),
772 oldField.typeName() )
773 .arg( oldField.length() )
774 .arg( oldField.precision() )
775 << QStringLiteral( "%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6" )
776 .arg( tr( "retrieved field" ),
777 newField.name(),
778 QVariant::typeToName( newField.type() ),
779 newField.typeName() )
780 .arg( newField.length() )
781 .arg( newField.precision() );
782 return false; // don't try attribute updates - they'll fail.
783 }
784 }
785
786 return true;
787}
788
789bool QgsVectorLayerEditBuffer::commitChangesChangeAttributes( bool &attributesChanged, QStringList &commitErrors )
790{
791 attributesChanged = false;
792
794 {
795 // cppcheck-suppress assertWithSideEffect
797
799 {
800 commitErrors << tr( "SUCCESS: %1 attribute value(s) and %2 geometries changed." ).arg( mChangedAttributeValues.size(), mChangedGeometries.size() );
801 attributesChanged = true;
804
806 mChangedGeometries.clear();
807 }
808 else
809 {
810 commitErrors << tr( "ERROR: %1 attributes and %2 geometries not changed.", "not changed attributes and geometries count" ).arg( mChangedAttributeValues.size() ).arg( mChangedGeometries.size() );
811 return false;
812 }
813 }
814
815 if ( !mChangedGeometries.isEmpty() )
816 {
819 {
820 commitErrors << tr( "ERROR: %1 geometries not changed. Data provider '%2' does not have ChangeFeatures or ChangeGeometries capabilities", "not changed geometries count" )
821 .arg( mChangedGeometries.size() )
822 .arg( L->dataProvider()->name() );
823 return false;
824 }
825
827 {
828 commitErrors << tr( "SUCCESS: %n geometries were changed.", "changed geometries count", mChangedGeometries.size() );
829 attributesChanged = true;
831 mChangedGeometries.clear();
832 }
833 else
834 {
835 commitErrors << tr( "ERROR: %n geometries not changed.", "not changed geometries count", mChangedGeometries.size() );
836 return false;
837 }
838 }
839
840 if ( !mChangedAttributeValues.isEmpty() )
841 {
844 {
845 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" )
846 .arg( mChangedAttributeValues.size() )
847 .arg( L->dataProvider()->name() );
848 return false;
849 }
850
852 {
853 commitErrors << tr( "SUCCESS: %n attribute value(s) changed.", "changed attribute values count", mChangedAttributeValues.size() );
854 attributesChanged = true;
857 }
858 else
859 {
860 commitErrors << tr( "ERROR: %n attribute value change(s) not applied.", "not changed attribute values count", mChangedAttributeValues.size() );
861#if 0
862 QString list = "ERROR: pending changes:";
863 const auto constKeys = mChangedAttributeValues.keys();
864 for ( QgsFeatureId id : constKeys )
865 {
866 list.append( "\n " + FID_TO_STRING( id ) + '[' );
867 const auto constKeys = mChangedAttributeValues[ id ].keys();
868 for ( int idx : constKeys )
869 {
870 list.append( QString( " %1:%2" ).arg( L->fields().at( idx ).name() ).arg( mChangedAttributeValues[id][idx].toString() ) );
871 }
872 list.append( " ]" );
873 }
874 commitErrors << list;
875#endif
876 return false;
877 }
878 }
879
880 return true;
881}
882
883bool QgsVectorLayerEditBuffer::commitChangesDeleteFeatures( bool &featuresDeleted, QStringList &commitErrors )
884{
885 featuresDeleted = false;
886
887 if ( mDeletedFeatureIds.isEmpty() )
888 return true;
889
891 {
892 commitErrors << tr( "SUCCESS: %n feature(s) deleted.", "deleted features count", mDeletedFeatureIds.size() );
893 featuresDeleted = true;
894 // TODO[MD]: we should not need this here
895 for ( QgsFeatureId id : std::as_const( mDeletedFeatureIds ) )
896 {
897 mChangedAttributeValues.remove( id );
898 mChangedGeometries.remove( id );
899 }
900
902
903 mDeletedFeatureIds.clear();
904 }
905 else
906 {
907 commitErrors << tr( "ERROR: %n feature(s) not deleted.", "not deleted features count", mDeletedFeatureIds.size() );
908#if 0
909 QString list = "ERROR: pending deletes:";
910 const auto constMDeletedFeatureIds = mDeletedFeatureIds;
911 for ( QgsFeatureId id : constMDeletedFeatureIds )
912 {
913 list.append( ' ' + FID_TO_STRING( id ) );
914 }
915 commitErrors << list;
916#endif
917 return false;
918 }
919
920 return true;
921}
922
923bool QgsVectorLayerEditBuffer::commitChangesAddFeatures( bool &featuresAdded, QStringList &commitErrors )
924{
925 featuresAdded = false;
926
927 if ( mAddedFeatures.isEmpty() )
928 return true;
929
931 {
932 QList<QgsFeatureId> ids;
933 QgsFeatureList featuresToAdd;
934 // get the list of added features in reversed order
935 // 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)
936 mapToReversedLists( mAddedFeatures, ids, featuresToAdd );
937
938 // we need to strip any extra attributes here -- e.g. virtual fields, which should
939 // not be sent to the data provider. Refs #18784
940 for ( int i = 0; i < featuresToAdd.count(); ++i )
941 {
942 // Empty the feature's fields so the up-to-date fields from the data provider is used to match attributes
944 }
945
947 {
948 commitErrors << tr( "SUCCESS: %n feature(s) added.", "added features count", featuresToAdd.size() );
949 featuresAdded = true;
950 emit committedFeaturesAdded( L->id(), featuresToAdd );
951
952 // notify everyone that the features with temporary ids were updated with permanent ids
953 for ( int i = 0; i < featuresToAdd.count(); ++i )
954 {
955 if ( featuresToAdd[i].id() != ids[i] )
956 {
957 //update selection
958 if ( L->mSelectedFeatureIds.contains( ids[i] ) )
959 {
960 L->mSelectedFeatureIds.remove( ids[i] );
961 L->mSelectedFeatureIds.insert( featuresToAdd[i].id() );
962 }
963 emit featureDeleted( ids[i] );
964 emit featureAdded( featuresToAdd[i].id() );
965 }
966 }
967
968 mAddedFeatures.clear();
969 }
970 else
971 {
972 commitErrors << tr( "ERROR: %n feature(s) not added.", "not added features count", mAddedFeatures.size() );
973#if 0
974 QString list = "ERROR: pending adds:";
975 const auto constMAddedFeatures = mAddedFeatures;
976 for ( QgsFeature f : constMAddedFeatures )
977 {
978 list.append( ' ' + FID_TO_STRING( f.id() ) + '[' );
979 for ( int i = 0; i < L->fields().size(); i++ )
980 {
981 list.append( QString( " %1:%2" ).arg( L->fields().at( i ).name() ).arg( f.attributes()[i].toString() ) );
982 }
983 list.append( " ]" );
984 }
985 commitErrors << list;
986#endif
987 return false;
988 }
989 }
990 else
991 {
992 commitErrors << tr( "ERROR: %n feature(s) not added - provider doesn't support adding features.", "not added features count", mAddedFeatures.size() );
993 return false;
994 }
995
996 return true;
997}
@ 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
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
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).
int size() const
Returns number of items.
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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
void handleAttributeAdded(int index, const QgsField &field)
Update added and changed features after addition of an attribute.
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
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:41
#define QgsDebugError(str)
Definition qgslogger.h:40
void mapToReversedLists(const QMap< Key, T > &map, QList< Key > &ks, QList< T > &vs)
populate two lists (ks, vs) from map - in reverse order