QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsvectorlayerfeatureiterator.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerfeatureiterator.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 "qgsmaplayerregistry.h"
18 #include "qgsvectordataprovider.h"
19 #include "qgsvectorlayer.h"
22 
24  : QgsAbstractFeatureIterator( request ), L( layer )
25 {
26  QgsVectorLayerJoinBuffer* joinBuffer = L->mJoinBuffer;
27 
28  if ( L->editBuffer() )
29  {
34  mAddedAttributes = QList<QgsField>( L->editBuffer()->addedAttributes() );
36  }
37 
38  // prepare joins: may add more attributes to fetch (in order to allow join)
39  if ( joinBuffer->containsJoins() )
40  prepareJoins();
41 
42  // by default provider's request is the same
44 
46  {
47  // prepare list of attributes to match provider fields
48  QgsAttributeList providerSubset;
50  const QgsFields &pendingFields = L->pendingFields();
51  int nPendingFields = pendingFields.count();
52  for ( int i = 0; i < subset.count(); ++i )
53  {
54  int attrIndex = subset[i];
55  if ( attrIndex < 0 || attrIndex >= nPendingFields ) continue;
56  if ( L->pendingFields().fieldOrigin( attrIndex ) == QgsFields::OriginProvider )
57  providerSubset << L->pendingFields().fieldOriginIndex( attrIndex );
58  }
59  mProviderRequest.setSubsetOfAttributes( providerSubset );
60  }
61 
62  if ( request.filterType() == QgsFeatureRequest::FilterFid )
63  {
64  mFetchedFid = false;
65  }
66  else // no filter or filter by rect
67  {
69 
71  }
72 }
73 
74 
76 {
77  close();
78 }
79 
80 
81 
83 {
84  f.setValid( false );
85 
86  if ( mClosed )
87  return false;
88 
90  {
91  if ( mFetchedFid )
92  return false;
93  bool res = nextFeatureFid( f );
94  mFetchedFid = true;
95  return res;
96  }
97 
99  {
100  if ( fetchNextChangedGeomFeature( f ) )
101  return true;
102  }
103  // no more changed geometries
104 
105  while ( mProviderIterator.nextFeature( f ) )
106  {
107  if ( mFetchConsidered.contains( f.id() ) )
108  continue;
109 
110  // TODO[MD]: just one resize of attributes
111  f.setFields( &L->mUpdatedFields );
112 
113  // update attributes
115 
116  if ( !mFetchJoinInfo.isEmpty() )
117  addJoinedAttributes( f );
118 
119  // update geometry
120  // TODO[MK]: FilterRect check after updating the geometry
123 
124  return true;
125  }
126  // no more provider features
127 
128  if ( fetchNextAddedFeature( f ) )
129  return true;
130  // no more added features
131 
132 
133  close();
134  return false;
135 }
136 
137 
138 
140 {
141  if ( mClosed )
142  return false;
143 
145  {
146  mFetchedFid = false;
147  }
148  else
149  {
152  }
153 
154  return true;
155 }
156 
158 {
159  if ( mClosed )
160  return false;
161 
163 
164  mClosed = true;
165  return true;
166 }
167 
168 
169 
170 
172 {
173  while ( mFetchAddedFeaturesIt-- != mAddedFeatures.constBegin() )
174  {
176 
177  if ( mFetchConsidered.contains( fid ) )
178  // must have changed geometry outside rectangle
179  continue;
180 
182  mFetchAddedFeaturesIt->geometry() &&
183  !mFetchAddedFeaturesIt->geometry()->intersects( mRequest.filterRect() ) )
184  // skip added features not in rectangle
185  continue;
186 
188 
189  return true;
190  }
191 
192  return false; // no more added features
193 }
194 
195 
197 {
198  f.setFeatureId( src.id() );
199  f.setValid( true );
200  f.setFields( &L->mUpdatedFields );
201 
202  if ( src.geometry() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
203  f.setGeometry( *src.geometry() );
204 
205  // TODO[MD]: if subset set just some attributes
206 
207  f.setAttributes( src.attributes() );
208 
209  if ( !mFetchJoinInfo.isEmpty() )
210  addJoinedAttributes( f );
211 }
212 
213 
214 
216 {
217  // check if changed geometries are in rectangle
219  {
220  QgsFeatureId fid = mFetchChangedGeomIt.key();
221 
222  if ( mFetchConsidered.contains( fid ) )
223  // skip deleted features
224  continue;
225 
226  mFetchConsidered << fid;
227 
228  if ( !mFetchChangedGeomIt->intersects( mRequest.filterRect() ) )
229  // skip changed geometries not in rectangle and don't check again
230  continue;
231 
233 
234  // return complete feature
236  return true;
237  }
238 
239  return false; // no more changed geometries
240 }
241 
242 
244 {
245  f.setFeatureId( fid );
246  f.setValid( true );
247  f.setFields( &L->mUpdatedFields );
248 
250  f.setGeometry( geom );
251 
252  bool subsetAttrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes );
253  if ( !subsetAttrs || ( subsetAttrs && mRequest.subsetOfAttributes().count() > 0 ) )
254  {
255  // retrieve attributes from provider
256  QgsFeature tmp;
257  //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
258  QgsFeatureRequest request;
260  if ( subsetAttrs )
261  {
263  }
264  QgsFeatureIterator fi = L->dataProvider()->getFeatures( request );
265  if ( fi.nextFeature( tmp ) )
266  {
268  f.setAttributes( tmp.attributes() );
269  }
270  }
271 
272  if ( !mFetchJoinInfo.isEmpty() )
273  addJoinedAttributes( f );
274 }
275 
276 
277 
279 {
281 
284 }
285 
286 
287 
289 {
291  QgsAttributeList sourceJoinFields; // attributes that also need to be fetched from this layer in order to have joins working
292 
293  mFetchJoinInfo.clear();
294 
295  QgsVectorLayerJoinBuffer* joinBuffer = L->mJoinBuffer;
296  const QgsFields& fields = L->pendingFields();
297 
298  for ( QgsAttributeList::const_iterator attIt = fetchAttributes.constBegin(); attIt != fetchAttributes.constEnd(); ++attIt )
299  {
300  if ( fields.fieldOrigin( *attIt ) != QgsFields::OriginJoin )
301  continue;
302 
303  int sourceLayerIndex;
304  const QgsVectorJoinInfo* joinInfo = joinBuffer->joinForFieldIndex( *attIt, fields, sourceLayerIndex );
305  Q_ASSERT( joinInfo );
306 
307  QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo->joinLayerId ) );
308  Q_ASSERT( joinLayer );
309 
310  if ( !mFetchJoinInfo.contains( joinLayer ) )
311  {
312  FetchJoinInfo info;
313  info.joinInfo = joinInfo;
314  info.joinLayer = joinLayer;
315 
316  if ( joinInfo->targetFieldName.isEmpty() )
317  info.targetField = joinInfo->targetFieldIndex; //for compatibility with 1.x
318  else
319  info.targetField = fields.indexFromName( joinInfo->targetFieldName );
320 
321  if ( joinInfo->joinFieldName.isEmpty() )
322  info.joinField = joinInfo->joinFieldIndex; //for compatibility with 1.x
323  else
324  info.joinField = joinLayer->pendingFields().indexFromName( joinInfo->joinFieldName );
325 
326  info.indexOffset = *attIt - sourceLayerIndex;
327  if ( info.joinField < sourceLayerIndex )
328  info.indexOffset++;
329 
330  // for joined fields, we always need to request the targetField from the provider too
331  if ( !fetchAttributes.contains( info.targetField ) )
332  sourceJoinFields << info.targetField;
333 
334  mFetchJoinInfo.insert( joinLayer, info );
335  }
336 
337  // store field source index - we'll need it when fetching from provider
338  mFetchJoinInfo[ joinLayer ].attributes.push_back( sourceLayerIndex );
339  }
340 
341  // add sourceJoinFields if we're using a subset
344 }
345 
346 
348 {
349  // make sure we have space for newly added attributes
350  f.attributes().resize( L->pendingFields().count() ); // f.attributes().count() + mJoinedAttributesCount );
351 
352  QMap<QgsVectorLayer*, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
353  for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
354  {
355  const FetchJoinInfo& info = joinIt.value();
356  Q_ASSERT( joinIt.key() );
357 
358  QVariant targetFieldValue = f.attribute( info.targetField );
359  if ( !targetFieldValue.isValid() )
360  continue;
361 
362  const QHash< QString, QgsAttributes>& memoryCache = info.joinInfo->cachedAttributes;
363  if ( memoryCache.isEmpty() )
364  info.addJoinedAttributesDirect( f, targetFieldValue );
365  else
366  info.addJoinedAttributesCached( f, targetFieldValue );
367  }
368 }
369 
370 
371 
373 {
374  const QHash<QString, QgsAttributes>& memoryCache = joinInfo->cachedAttributes;
375  QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
376  if ( it == memoryCache.constEnd() )
377  return; // joined value not found -> leaving the attributes empty (null)
378 
379  int index = indexOffset;
380 
381  const QgsAttributes& featureAttributes = it.value();
382  for ( int i = 0; i < featureAttributes.count(); ++i )
383  {
384  // skip the join field to avoid double field names (fields often have the same name)
385  if ( i == joinField )
386  continue;
387 
388  f.setAttribute( index++, featureAttributes[i] );
389  }
390 }
391 
392 
393 
395 {
396  // no memory cache, query the joined values by setting substring
397  QString subsetString = joinLayer->dataProvider()->subsetString(); // provider might already have a subset string
398  QString bkSubsetString = subsetString;
399  if ( !subsetString.isEmpty() )
400  {
401  subsetString.prepend( "(" ).append( ") AND " );
402  }
403 
404  QString joinFieldName;
405  if ( joinInfo->joinFieldName.isEmpty() && joinInfo->joinFieldIndex >= 0 && joinInfo->joinFieldIndex < joinLayer->pendingFields().count() )
406  joinFieldName = joinLayer->pendingFields().field( joinInfo->joinFieldIndex ).name(); // for compatibility with 1.x
407  else
408  joinFieldName = joinInfo->joinFieldName;
409 
410  subsetString.append( QString( "\"%1\"" ).arg( joinFieldName ) );
411 
412  if ( joinValue.isNull() )
413  {
414  subsetString += " IS NULL";
415  }
416  else
417  {
418  QString v = joinValue.toString();
419  switch ( joinValue.type() )
420  {
421  case QVariant::Int:
422  case QVariant::LongLong:
423  case QVariant::Double:
424  break;
425 
426  default:
427  case QVariant::String:
428  v.replace( "'", "''" );
429  v.prepend( "'" ).append( "'" );
430  break;
431  }
432  subsetString += "=" + v;
433  }
434 
435  joinLayer->dataProvider()->setSubsetString( subsetString, false );
436 
437  // select (no geometry)
438  QgsFeatureRequest request;
440  request.setSubsetOfAttributes( attributes );
441  QgsFeatureIterator fi = joinLayer->getFeatures( request );
442 
443  // get first feature
444  QgsFeature fet;
445  if ( fi.nextFeature( fet ) )
446  {
447  int index = indexOffset;
448  const QgsAttributes& attr = fet.attributes();
449  for ( int i = 0; i < attr.count(); ++i )
450  {
451  if ( i == joinField )
452  continue;
453 
454  f.setAttribute( index++, attr[i] );
455  }
456  }
457  else
458  {
459  // no suitable join feature found, keeping empty (null) attributes
460  }
461 
462  joinLayer->dataProvider()->setSubsetString( bkSubsetString, false );
463 }
464 
465 
466 
467 
469 {
470  QgsFeatureId featureId = mRequest.filterFid();
471 
472  // deleted already?
473  if ( mDeletedFeatureIds.contains( featureId ) )
474  return false;
475 
476  // has changed geometry?
477  if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mChangedGeometries.contains( featureId ) )
478  {
479  useChangedAttributeFeature( featureId, mChangedGeometries[featureId], f );
480  return true;
481  }
482 
483  // added features
484  for ( QgsFeatureMap::ConstIterator iter = mAddedFeatures.constBegin(); iter != mAddedFeatures.constEnd(); ++iter )
485  {
486  if ( iter->id() == featureId )
487  {
488  useAddedFeature( *iter, f );
489  return true;
490  }
491  }
492 
493  // regular features
495  if ( fi.nextFeature( f ) )
496  {
498 
499  if ( !mFetchJoinInfo.isEmpty() )
500  addJoinedAttributes( f );
501 
502  return true;
503  }
504 
505  return false;
506 }
507 
509 {
510  QgsAttributes& attrs = f.attributes();
511 
512  // remove all attributes that will disappear - from higher indices to lower
513  for ( int idx = mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
514  {
515  attrs.remove( mDeletedAttributeIds[idx] );
516  }
517 
518  // adjust size to accommodate added attributes
519  attrs.resize( attrs.count() + mAddedAttributes.count() );
520 
521  // update changed attributes
522  if ( mChangedAttributeValues.contains( f.id() ) )
523  {
524  const QgsAttributeMap &map = mChangedAttributeValues[f.id()];
525  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); it++ )
526  attrs[it.key()] = it.value();
527  }
528 }
529 
531 {
532  if ( mChangedGeometries.contains( f.id() ) )
534 }