QGIS API Documentation  2.3.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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"
20 #include "qgsvectordataprovider.h"
21 #include "qgsvectorlayer.h"
22 
23 
25  : L( layer )
26 {
27  connect( L->undoStack(), SIGNAL( indexChanged( int ) ), this, SLOT( undoIndexChanged( int ) ) ); // TODO[MD]: queued?
28 }
29 
31 {
32 }
33 
34 
36 {
37  return !L->undoStack()->isClean();
38 }
39 
40 
42 {
43  QgsDebugMsg( QString( "undo index changed %1" ).arg( index ) );
44  Q_UNUSED( index );
45  emit layerModified();
46 }
47 
48 
50 {
51  // delete attributes from the higher indices to lower indices
52  for ( int i = mDeletedAttributeIds.count() - 1; i >= 0; --i )
53  {
54  fields.remove( mDeletedAttributeIds[i] );
55  }
56  // add new fields
57  for ( int i = 0; i < mAddedAttributes.count(); ++i )
58  {
60  }
61 }
62 
63 
65 {
66  if ( mChangedGeometries.contains( f.id() ) )
68 }
69 
70 
72 {
73  QgsAttributes& attrs = f.attributes();
74 
75  // remove all attributes that will disappear - from higher indices to lower
76  for ( int idx = mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
77  {
78  attrs.remove( mDeletedAttributeIds[idx] );
79  }
80 
81  // adjust size to accommodate added attributes
82  attrs.resize( attrs.count() + mAddedAttributes.count() );
83 
84  // update changed attributes
85  if ( mChangedAttributeValues.contains( f.id() ) )
86  {
87  const QgsAttributeMap &map = mChangedAttributeValues[f.id()];
88  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
89  attrs[it.key()] = it.value();
90  }
91 }
92 
93 
94 
95 
97 {
99  {
100  return false;
101  }
102  if ( L->mUpdatedFields.count() != f.attributes().count() )
103  return false;
104 
105  // TODO: check correct geometry type
106 
107  L->undoStack()->push( new QgsVectorLayerUndoCommandAddFeature( this, f ) );
108  return true;
109 }
110 
111 
113 {
115  return false;
116 
117  for ( QgsFeatureList::iterator iter = features.begin(); iter != features.end(); ++iter )
118  {
119  addFeature( *iter );
120  }
121 
122  L->updateExtents();
123  return true;
124 }
125 
126 
127 
129 {
131  return false;
132 
133  if ( FID_IS_NEW( fid ) )
134  {
135  if ( !mAddedFeatures.contains( fid ) )
136  return false;
137  }
138  else // existing feature
139  {
140  if ( mDeletedFeatureIds.contains( fid ) )
141  return false;
142  }
143 
144  L->undoStack()->push( new QgsVectorLayerUndoCommandDeleteFeature( this, fid ) );
145  return true;
146 }
147 
148 
150 {
152  return false;
153 
154  if ( !L->hasGeometryType() )
155  {
156  return false;
157  }
158 
159  if ( FID_IS_NEW( fid ) )
160  {
161  if ( !mAddedFeatures.contains( fid ) )
162  return false;
163  }
164 
165  // TODO: check compatible geometry
166 
167  L->undoStack()->push( new QgsVectorLayerUndoCommandChangeGeometry( this, fid, geom ) );
168  return true;
169 }
170 
171 
172 bool QgsVectorLayerEditBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
173 {
175  return false;
176 
177  if ( FID_IS_NEW( fid ) )
178  {
179  if ( !mAddedFeatures.contains( fid ) )
180  return false;
181  }
182 
183  if ( field < 0 || field >= L->pendingFields().count() ||
185  return false;
186 
187  L->undoStack()->push( new QgsVectorLayerUndoCommandChangeAttribute( this, fid, field, newValue, oldValue ) );
188  return true;
189 }
190 
191 
193 {
195  return false;
196 
197  if ( field.name().isEmpty() )
198  return false;
199 
200  const QgsFields& updatedFields = L->pendingFields();
201  for ( int idx = 0; idx < updatedFields.count(); ++idx )
202  {
203  if ( updatedFields[idx].name() == field.name() )
204  return false;
205  }
206 
207  if ( !L->dataProvider()->supportedType( field ) )
208  return false;
209 
210  L->undoStack()->push( new QgsVectorLayerUndoCommandAddAttribute( this, field ) );
211  return true;
212 }
213 
214 
216 {
218  return false;
219 
220  if ( index < 0 || index >= L->pendingFields().count() )
221  return false;
222 
223  // find out source of the field
224  QgsFields::FieldOrigin origin = L->pendingFields().fieldOrigin( index );
225  int originIndex = L->pendingFields().fieldOriginIndex( index );
226 
227  if ( origin == QgsFields::OriginProvider && mDeletedAttributeIds.contains( originIndex ) )
228  return false;
229 
230  if ( origin == QgsFields::OriginJoin )
231  return false;
232 
233  L->undoStack()->push( new QgsVectorLayerUndoCommandDeleteAttribute( this, index ) );
234  return true;
235 }
236 
237 
238 bool QgsVectorLayerEditBuffer::commitChanges( QStringList& commitErrors )
239 {
240  QgsVectorDataProvider* provider = L->dataProvider();
241  commitErrors.clear();
242 
243  int cap = provider->capabilities();
244  bool success = true;
245 
246  QgsFields oldFields = L->pendingFields();
247 
248  bool hadPendingDeletes = !mDeletedFeatureIds.isEmpty();
249 
250  //
251  // delete attributes
252  //
253  bool attributesChanged = false;
254  if ( !mDeletedAttributeIds.isEmpty() )
255  {
257  {
258  commitErrors << tr( "SUCCESS: %n attribute(s) deleted.", "deleted attributes count", mDeletedAttributeIds.size() );
259 
261 
262  mDeletedAttributeIds.clear();
263  attributesChanged = true;
264  }
265  else
266  {
267  commitErrors << tr( "ERROR: %n attribute(s) not deleted.", "not deleted attributes count", mDeletedAttributeIds.size() );
268 #if 0
269  QString list = "ERROR: Pending attribute deletes:";
270  foreach ( int idx, mDeletedAttributeIds )
271  {
272  list.append( " " + L->pendingFields()[idx].name() );
273  }
274  commitErrors << list;
275 #endif
276  success = false;
277  }
278  }
279 
280  //
281  // add attributes
282  //
283  if ( !mAddedAttributes.isEmpty() )
284  {
286  {
287  commitErrors << tr( "SUCCESS: %n attribute(s) added.", "added attributes count", mAddedAttributes.size() );
288 
290 
291  mAddedAttributes.clear();
292  attributesChanged = true;
293  }
294  else
295  {
296  commitErrors << tr( "ERROR: %n new attribute(s) not added", "not added attributes count", mAddedAttributes.size() );
297 #if 0
298  QString list = "ERROR: Pending adds:";
299  foreach ( QgsField f, mAddedAttributes )
300  {
301  list.append( " " + f.name() );
302  }
303  commitErrors << list;
304 #endif
305  success = false;
306  }
307  }
308 
309  //
310  // check that addition/removal went as expected
311  //
312  bool attributeChangesOk = true;
313  if ( attributesChanged )
314  {
315  L->updateFields();
316  QgsFields newFields = L->pendingFields();
317 
318  if ( oldFields.count() != newFields.count() )
319  {
320  commitErrors << tr( "ERROR: the count of fields is incorrect after addition/removal of fields!" );
321  attributeChangesOk = false; // don't try attribute updates - they'll fail.
322  }
323 
324  for ( int i = 0; i < oldFields.count(); ++i )
325  {
326  const QgsField& oldField = oldFields[i];
327  const QgsField& newField = newFields[i];
328  if ( attributeChangesOk && oldField != newField )
329  {
330  commitErrors << tr( "ERROR: field with index %1 is not the same!" ).arg( i );
331  attributeChangesOk = false; // don't try attribute updates - they'll fail.
332  }
333  }
334  }
335 
336  if ( attributeChangesOk )
337  {
338  //
339  // change attributes
340  //
341  if ( !mChangedAttributeValues.isEmpty() )
342  {
344  {
345  commitErrors << tr( "SUCCESS: %n attribute value(s) changed.", "changed attribute values count", mChangedAttributeValues.size() );
346 
348 
349  mChangedAttributeValues.clear();
350  }
351  else
352  {
353  commitErrors << tr( "ERROR: %n attribute value change(s) not applied.", "not changed attribute values count", mChangedAttributeValues.size() );
354 #if 0
355  QString list = "ERROR: pending changes:";
356  foreach ( QgsFeatureId id, mChangedAttributeValues.keys() )
357  {
358  list.append( "\n " + FID_TO_STRING( id ) + "[" );
359  foreach ( int idx, mChangedAttributeValues[ id ].keys() )
360  {
361  list.append( QString( " %1:%2" ).arg( L->pendingFields()[idx].name() ).arg( mChangedAttributeValues[id][idx].toString() ) );
362  }
363  list.append( " ]" );
364  }
365  commitErrors << list;
366 #endif
367  success = false;
368  }
369  }
370 
371  //
372  // delete features
373  //
374  if ( success && !mDeletedFeatureIds.isEmpty() )
375  {
377  {
378  commitErrors << tr( "SUCCESS: %n feature(s) deleted.", "deleted features count", mDeletedFeatureIds.size() );
379  // TODO[MD]: we should not need this here
380  for ( QgsFeatureIds::const_iterator it = mDeletedFeatureIds.begin(); it != mDeletedFeatureIds.end(); ++it )
381  {
382  mChangedAttributeValues.remove( *it );
383  mChangedGeometries.remove( *it );
384  }
385 
387 
388  mDeletedFeatureIds.clear();
389  }
390  else
391  {
392  commitErrors << tr( "ERROR: %n feature(s) not deleted.", "not deleted features count", mDeletedFeatureIds.size() );
393 #if 0
394  QString list = "ERROR: pending deletes:";
395  foreach ( QgsFeatureId id, mDeletedFeatureIds )
396  {
397  list.append( " " + FID_TO_STRING( id ) );
398  }
399  commitErrors << list;
400 #endif
401  success = false;
402  }
403  }
404 
405  //
406  // add features
407  //
408  if ( success && !mAddedFeatures.isEmpty() )
409  {
411  {
412  QList<QgsFeatureId> ids = mAddedFeatures.keys();
413  QgsFeatureList featuresToAdd = mAddedFeatures.values();
414 
415  if ( provider->addFeatures( featuresToAdd ) )
416  {
417  commitErrors << tr( "SUCCESS: %n feature(s) added.", "added features count", featuresToAdd.size() );
418 
419  emit committedFeaturesAdded( L->id(), featuresToAdd );
420 
421  // notify everyone that the features with temporary ids were updated with permanent ids
422  for ( int i = 0; i < featuresToAdd.count(); ++i )
423  {
424  if ( featuresToAdd[i].id() != ids[i] )
425  {
426  //update selection
427  if ( L->mSelectedFeatureIds.contains( ids[i] ) )
428  {
429  L->mSelectedFeatureIds.remove( ids[i] );
430  L->mSelectedFeatureIds.insert( featuresToAdd[i].id() );
431  }
432  emit featureDeleted( ids[i] );
433  emit featureAdded( featuresToAdd[i].id() );
434  }
435  }
436 
437  mAddedFeatures.clear();
438  }
439  else
440  {
441  commitErrors << tr( "ERROR: %n feature(s) not added.", "not added features count", mAddedFeatures.size() );
442 #if 0
443  QString list = "ERROR: pending adds:";
444  foreach ( QgsFeature f, mAddedFeatures )
445  {
446  list.append( " " + FID_TO_STRING( f.id() ) + "[" );
447  for ( int i = 0; i < L->pendingFields().size(); i++ )
448  {
449  list.append( QString( " %1:%2" ).arg( L->pendingFields()[i].name() ).arg( f.attributes()[i].toString() ) );
450  }
451  list.append( " ]" );
452  }
453  commitErrors << list;
454 #endif
455  success = false;
456  }
457  }
458  else
459  {
460  commitErrors << tr( "ERROR: %n feature(s) not added - provider doesn't support adding features.", "not added features count", mAddedFeatures.size() );
461  success = false;
462  }
463  }
464  }
465  else
466  {
467  success = false;
468  }
469 
470  //
471  // update geometries
472  //
473  if ( success && !mChangedGeometries.isEmpty() )
474  {
476  {
477  commitErrors << tr( "SUCCESS: %n geometries were changed.", "changed geometries count", mChangedGeometries.size() );
478 
480 
481  mChangedGeometries.clear();
482  }
483  else
484  {
485  commitErrors << tr( "ERROR: %n geometries not changed.", "not changed geometries count", mChangedGeometries.size() );
486  success = false;
487  }
488  }
489 
490  // for shapes run a REPACK after each transaction
491  // TODO: enhance provider interface to allow moving this there
492  if ( success && hadPendingDeletes && L->providerType() == "ogr" && L->storageType() == "ESRI Shapefile" )
493  {
494  provider->createSpatialIndex();
495  }
496 
497  if ( !success && provider->hasErrors() )
498  {
499  commitErrors << tr( "\n Provider errors:" );
500  foreach ( QString e, provider->errors() )
501  {
502  commitErrors << " " + e.replace( "\n", "\n " );
503  }
504  provider->clearErrors();
505  }
506 
507  return success;
508 }
509 
510 
512 {
513  if ( !isModified() )
514  return;
515 
516  // limit canvas redraws to one by jumping to beginning of stack
517  // see QgsUndoWidget::indexChanged
518  L->undoStack()->setIndex( 0 );
519 
520  Q_ASSERT( mAddedAttributes.isEmpty() );
521  Q_ASSERT( mDeletedAttributeIds.isEmpty() );
522  Q_ASSERT( mChangedAttributeValues.isEmpty() );
523  Q_ASSERT( mChangedGeometries.isEmpty() );
524  Q_ASSERT( mAddedFeatures.isEmpty() );
525 }
526 
527 #if 0
528 QString QgsVectorLayerEditBuffer::dumpEditBuffer()
529 {
530  QString msg;
531  if ( !mChangedGeometries.isEmpty() )
532  {
533  msg += "CHANGED GEOMETRIES:\n";
534  for ( QgsGeometryMap::const_iterator it = mChangedGeometries.begin(); it != mChangedGeometries.end(); ++it )
535  {
536  // QgsFeatureId, QgsGeometry
537  msg += QString( "- FID %1: %2" ).arg( it.key() ).arg( it.value().to );
538  }
539  }
540  return msg;
541 }
542 #endif
543 
545 {
546  // go through the changed attributes map and adapt indices
547  for ( int i = 0; i < mChangedAttributeValues.size(); ++i )
548  {
550  }
551 
552  // go through added features and adapt attributes
553  QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
554  for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
555  {
556  QgsAttributes& attrs = featureIt->attributes();
557  attrs.insert( index, QVariant() );
558  }
559 }
560 
562 {
563  // go through the changed attributes map and adapt indices
564  for ( int i = 0; i < mChangedAttributeValues.size(); ++i )
565  {
567  // remove the attribute
568  if ( attrMap.contains( index ) )
569  attrMap.remove( index );
570 
571  // update attribute indices
572  updateAttributeMapIndex( attrMap, index, -1 );
573  }
574 
575  // go through added features and adapt attributes
576  QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
577  for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
578  {
579  QgsAttributes& attrs = featureIt->attributes();
580  attrs.remove( index );
581  }
582 }
583 
584 
585 
587 {
588  QgsAttributeMap updatedMap;
589  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
590  {
591  int attrIndex = it.key();
592  updatedMap.insert( attrIndex < index ? attrIndex : attrIndex + offset, it.value() );
593  }
594  map = updatedMap;
595 }
596 
597 
598 
600 {
601  L->updateFields();
602 }
QgsFeatureId id() const
Get the feature id for this feature.
Definition: qgsfeature.cpp:100
void updateFields()
Assembles mUpdatedFields considering provider fields, joined fields and added fields.
void updateChangedAttributes(QgsFeature &f)
Update feature with uncommited attribute updates.
const QString & name() const
Gets the name of the field.
Definition: qgsfield.cpp:55
static unsigned index
void handleAttributeDeleted(int index)
update added and changed features after removal of an attribute
field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfield.h:172
bool addAttribute(const QgsField &field)
add an attribute field (but does not commit it) returns true if the field was added ...
void committedAttributesDeleted(const QString &layerId, const QgsAttributeList &deletedAttributes)
Signals emitted after committing changes.
QMap< int, QVariant > QgsAttributeMap
Definition: qgsfeature.h:98
virtual bool addAttributes(const QList< QgsField > &attributes)
Adds new attributes.
bool addFeatures(QgsFeatureList &features)
Insert a copy of the given features into the layer (but does not commit it)
bool addFeature(QgsFeature &f)
Adds a feature.
field has been temporarily added in editing mode (originIndex = index in the list of added attributes...
Definition: qgsfield.h:173
void committedAttributesAdded(const QString &layerId, const QList< QgsField > &addedAttributes)
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:331
virtual bool deleteFeatures(const QgsFeatureIds &id)
Deletes one or more features.
#define FID_TO_STRING(fid)
Definition: qgsfeature.h:83
friend class QgsVectorLayerUndoCommandChangeGeometry
Container of fields for a vector layer.
Definition: qgsfield.h:164
void rollBack()
Stop editing and discard the edits.
QStringList errors()
Get recorded errors.
friend class QgsVectorLayerUndoCommandAddAttribute
QgsChangedAttributesMap mChangedAttributeValues
Changed attributes values which are not commited.
void updateFeatureGeometry(QgsFeature &f)
Update feature with uncommited geometry updates.
allows deletion of attributes (fields)
field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition: qgsfield.h:171
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:114
virtual bool addFeatures(QgsFeatureList &flist)
Adds a list of features.
friend class QgsVectorLayerUndoCommandDeleteAttribute
QgsVectorLayerEditBuffer(QgsVectorLayer *layer)
virtual void updateExtents()
Update the extents for the layer.
void featureAdded(QgsFeatureId fid)
virtual bool createSpatialIndex()
Creates a spatial index on the datasource (if supported by the provider type).
void setGeometry(const QgsGeometry &geom)
Set this feature's geometry from another QgsGeometry object (deep copy)
Definition: qgsfeature.cpp:134
void committedGeometriesChanges(const QString &layerId, const QgsGeometryMap &changedGeometries)
bool supportedType(const QgsField &field) const
check if provider supports type of field
allows addition of new attributes (fields)
virtual int capabilities() const
Returns a bitmask containing the supported capabilities Note, some capabilities may change depending ...
bool hasErrors()
Provider has errors to report.
virtual bool changeAttributeValues(const QgsChangedAttributesMap &attr_map)
Changes attribute values of existing features.
int fieldOriginIndex(int fieldIdx) const
Get field's origin index (its meaning is specific to each type of origin)
Definition: qgsfield.h:220
QgsGeometryMap mChangedGeometries
Changed geometries which are not commited.
void handleAttributeAdded(int index)
update added and changed features after addition of an attribute
const QgsAttributes & attributes() const
Definition: qgsfeature.h:143
allows modifications of geometries
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfield.cpp:137
QString id() const
Get this layer's unique ID, this ID is used to access this layer from map layer registry.
Definition: qgsmaplayer.cpp:92
void clearErrors()
Clear recorded errors.
int count() const
Return number of items.
Definition: qgsfield.h:198
QgsFeatureIds mDeletedFeatureIds
Deleted feature IDs which are not commited.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:31
void remove(int fieldIdx)
Remove a field with the given index.
Definition: qgsfield.cpp:150
bool commitChanges(QStringList &commitErrors)
Attempts to commit any changes to disk.
bool deleteAttribute(int attr)
delete an attribute field (but does not commit it)
friend class QgsVectorLayerUndoCommandDeleteFeature
virtual bool changeGeometryValues(QgsGeometryMap &geometry_map)
Changes geometries of existing features.
QString providerType() const
Return the provider type for this layer.
bool hasGeometryType() const
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QList< QgsField > mAddedAttributes
added attributes fields which are not commited
QVector< QVariant > QgsAttributes
Definition: qgsfeature.h:100
FieldOrigin fieldOrigin(int fieldIdx) const
Get field's origin (value from an enumeration)
Definition: qgsfield.h:218
void committedAttributeValuesChanges(const QString &layerId, const QgsChangedAttributesMap &changedAttributesValues)
int size() const
Return number of items.
Definition: qgsfield.h:200
virtual bool deleteAttributes(const QgsAttributeIds &attributes)
Deletes existing attributes.
bool deleteFeature(QgsFeatureId fid)
delete a feature from the layer (but does not commit it)
qint64 QgsFeatureId
Definition: qgsfeature.h:30
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
changed an attribute value (but does not commit it)
#define FID_IS_NEW(fid)
Definition: qgsfeature.h:81
QUndoStack * undoStack()
Return pointer to layer's undo stack.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
void committedFeaturesRemoved(const QString &layerId, const QgsFeatureIds &deletedFeatureIds)
QgsVectorDataProvider * dataProvider()
Returns the data provider.
void committedFeaturesAdded(const QString &layerId, const QgsFeatureList &addedFeatures)
This is the base class for vector data providers.
QgsFields mUpdatedFields
field map to commit
QgsFeatureMap mAddedFeatures
New features which are not commited.
void updateFields(QgsFields &fields)
void layerModified()
This signal is emitted when modifications has been done on layer.
Represents a vector layer which manages a vector based data sets.
bool isModified() const
Returns true if the provider has been modified since the last commit.
QgsFeatureIds mSelectedFeatureIds
Set holding the feature IDs that are activated.
QgsAttributeList mDeletedAttributeIds
deleted attributes fields which are not commited.
allows modification of attribute values
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) ...
bool changeGeometry(QgsFeatureId fid, QgsGeometry *geom)
change feature's geometry
void featureDeleted(QgsFeatureId fid)
friend class QgsVectorLayerUndoCommandChangeAttribute
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
#define tr(sourceText)