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