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