QGIS API Documentation  2.0.1-Dufour
 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  qDebug( "undo index changed %d", 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, QVariant value )
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, value ) );
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 }