QGIS API Documentation  2.2.0-Valmiera
 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 #include "qgsgeometrysimplifier.h"
23 #include "qgssimplifymethod.h"
24 
26  : QgsAbstractFeatureIterator( request ), L( layer ), mEditGeometrySimplifier( NULL )
27 {
28  QgsVectorLayerJoinBuffer* joinBuffer = L->mJoinBuffer;
29 
31 
32  if ( L->editBuffer() )
33  {
34  if ( request.filterType() == QgsFeatureRequest::FilterFid )
35  {
36  // only copy relevant parts
37  if ( L->editBuffer()->addedFeatures().contains( request.filterFid() ) )
38  mAddedFeatures.insert( request.filterFid(), L->editBuffer()->addedFeatures()[ request.filterFid()] );
39 
40  if ( L->editBuffer()->changedGeometries().contains( request.filterFid() ) )
41  mChangedGeometries.insert( request.filterFid(), L->editBuffer()->changedGeometries()[ request.filterFid()] );
42 
43  if ( L->editBuffer()->deletedFeatureIds().contains( request.filterFid() ) )
44  mDeletedFeatureIds.insert( request.filterFid() );
45 
46  if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
47  mChangedAttributeValues.insert( request.filterFid(), L->editBuffer()->changedAttributeValues()[ request.filterFid()] );
48 
49  if ( L->editBuffer()->changedAttributeValues().contains( request.filterFid() ) )
51  }
52  else
53  {
59  }
60 
61  mAddedAttributes = QList<QgsField>( L->editBuffer()->addedAttributes() );
63  }
64 
65  // prepare joins: may add more attributes to fetch (in order to allow join)
66  if ( joinBuffer->containsJoins() )
67  prepareJoins();
68 
69  // by default provider's request is the same
71 
73  {
74  // prepare list of attributes to match provider fields
75  QgsAttributeList providerSubset;
77  const QgsFields &pendingFields = L->pendingFields();
78  int nPendingFields = pendingFields.count();
79  for ( int i = 0; i < subset.count(); ++i )
80  {
81  int attrIndex = subset[i];
82  if ( attrIndex < 0 || attrIndex >= nPendingFields ) continue;
83  if ( L->pendingFields().fieldOrigin( attrIndex ) == QgsFields::OriginProvider )
84  providerSubset << L->pendingFields().fieldOriginIndex( attrIndex );
85  }
86  mProviderRequest.setSubsetOfAttributes( providerSubset );
87  }
88 
89  if ( request.filterType() == QgsFeatureRequest::FilterFid )
90  {
91  mFetchedFid = false;
92  }
93  else // no filter or filter by rect
94  {
95  if ( L->editBuffer() )
96  {
98  }
99  else
100  {
102  }
103 
105  }
106 
108  {
110  }
111 }
112 
113 
115 {
118 
119  close();
120 }
121 
122 
123 
125 {
126  f.setValid( false );
127 
128  if ( mClosed )
129  return false;
130 
132  {
133  if ( mFetchedFid )
134  return false;
135  bool res = nextFeatureFid( f );
136  mFetchedFid = true;
137  return res;
138  }
139 
141  {
142  if ( fetchNextChangedGeomFeature( f ) )
143  return true;
144 
145  // no more changed geometries
146  }
147 
149  {
151  return true;
152 
153  // no more changed features
154  }
155 
156  while ( fetchNextAddedFeature( f ) )
157  {
158  return true;
159  }
160  // no more added features
161 
162  if ( mProviderIterator.isClosed() )
163  {
166  }
167 
168  while ( mProviderIterator.nextFeature( f ) )
169  {
170  if ( mFetchConsidered.contains( f.id() ) )
171  continue;
172 
173  // TODO[MD]: just one resize of attributes
174  f.setFields( &L->mUpdatedFields );
175 
176  // update attributes
178 
179  if ( !mFetchJoinInfo.isEmpty() )
180  addJoinedAttributes( f );
181 
182  // update geometry
183  // TODO[MK]: FilterRect check after updating the geometry
186 
187  return true;
188  }
189  // no more provider features
190 
191  close();
192  return false;
193 }
194 
195 
196 
198 {
199  if ( mClosed )
200  return false;
201 
203  {
204  mFetchedFid = false;
205  }
206  else
207  {
210  }
211 
212  return true;
213 }
214 
216 {
217  if ( mClosed )
218  return false;
219 
221 
222  mClosed = true;
223  return true;
224 }
225 
226 
227 
228 
230 {
231  while ( mFetchAddedFeaturesIt-- != mAddedFeatures.constBegin() )
232  {
234 
235  if ( mFetchConsidered.contains( fid ) )
236  // must have changed geometry outside rectangle
237  continue;
238 
240  // skip features which are not accepted by the filter
241  continue;
242 
244 
245  return true;
246  }
247 
248  mFetchAddedFeaturesIt = mAddedFeatures.constBegin();
249  return false; // no more added features
250 }
251 
252 
254 {
255  f.setFeatureId( src.id() );
256  f.setValid( true );
257  f.setFields( &L->mUpdatedFields );
258 
259  if ( src.geometry() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
260  {
261  f.setGeometry( *src.geometry() );
262 
263  // simplify the edited geometry using its simplifier configured
265  {
266  QgsGeometry* geometry = f.geometry();
267  QGis::GeometryType geometryType = geometry->type();
268  if ( geometryType == QGis::Line || geometryType == QGis::Polygon ) mEditGeometrySimplifier->simplifyGeometry( geometry );
269  }
270  }
271 
272  // TODO[MD]: if subset set just some attributes
273 
274  f.setAttributes( src.attributes() );
275 
276  if ( !mFetchJoinInfo.isEmpty() )
277  addJoinedAttributes( f );
278 }
279 
280 
281 
283 {
284  // check if changed geometries are in rectangle
286  {
287  QgsFeatureId fid = mFetchChangedGeomIt.key();
288 
289  if ( mFetchConsidered.contains( fid ) )
290  // skip deleted features
291  continue;
292 
293  mFetchConsidered << fid;
294 
295  if ( !mFetchChangedGeomIt->intersects( mRequest.filterRect() ) )
296  // skip changed geometries not in rectangle and don't check again
297  continue;
298 
300 
301  // return complete feature
303  return true;
304  }
305 
306  return false; // no more changed geometries
307 }
308 
310 {
312  {
313  mFetchConsidered << f.id();
314 
316 
318  {
319  if ( mRequest.filterExpression()->evaluate( &f ).toBool() )
320  {
321  return true;
322  }
323  }
324  else
325  {
326  return true;
327  }
328  }
329 
330  return false;
331 }
332 
333 
335 {
336  f.setFeatureId( fid );
337  f.setValid( true );
338  f.setFields( &L->mUpdatedFields );
339 
341  {
342  f.setGeometry( geom );
343 
344  // simplify the edited geometry using its simplifier configured
346  {
347  QgsGeometry* geometry = f.geometry();
348  QGis::GeometryType geometryType = geometry->type();
349  if ( geometryType == QGis::Line || geometryType == QGis::Polygon ) mEditGeometrySimplifier->simplifyGeometry( geometry );
350  }
351  }
352 
353  bool subsetAttrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes );
354  if ( !subsetAttrs || ( subsetAttrs && mRequest.subsetOfAttributes().count() > 0 ) )
355  {
356  // retrieve attributes from provider
357  QgsFeature tmp;
358  //mDataProvider->featureAtId( fid, tmp, false, mFetchProvAttributes );
359  QgsFeatureRequest request;
361  if ( subsetAttrs )
362  {
364  }
365  QgsFeatureIterator fi = L->dataProvider()->getFeatures( request );
366  if ( fi.nextFeature( tmp ) )
367  {
369  f.setAttributes( tmp.attributes() );
370  }
371  }
372 
373  if ( !mFetchJoinInfo.isEmpty() )
374  addJoinedAttributes( f );
375 }
376 
377 
378 
380 {
382 
385 }
386 
387 
388 
390 {
392  QgsAttributeList sourceJoinFields; // attributes that also need to be fetched from this layer in order to have joins working
393 
394  mFetchJoinInfo.clear();
395 
396  QgsVectorLayerJoinBuffer* joinBuffer = L->mJoinBuffer;
397  const QgsFields& fields = L->pendingFields();
398 
399  for ( QgsAttributeList::const_iterator attIt = fetchAttributes.constBegin(); attIt != fetchAttributes.constEnd(); ++attIt )
400  {
401  if ( !fields.exists( *attIt ) )
402  continue;
403 
404  if ( fields.fieldOrigin( *attIt ) != QgsFields::OriginJoin )
405  continue;
406 
407  int sourceLayerIndex;
408  const QgsVectorJoinInfo* joinInfo = joinBuffer->joinForFieldIndex( *attIt, fields, sourceLayerIndex );
409  Q_ASSERT( joinInfo );
410 
411  QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo->joinLayerId ) );
412  Q_ASSERT( joinLayer );
413 
414  if ( !mFetchJoinInfo.contains( joinLayer ) )
415  {
416  FetchJoinInfo info;
417  info.joinInfo = joinInfo;
418  info.joinLayer = joinLayer;
419 
420  if ( joinInfo->targetFieldName.isEmpty() )
421  info.targetField = joinInfo->targetFieldIndex; //for compatibility with 1.x
422  else
423  info.targetField = fields.indexFromName( joinInfo->targetFieldName );
424 
425  if ( joinInfo->joinFieldName.isEmpty() )
426  info.joinField = joinInfo->joinFieldIndex; //for compatibility with 1.x
427  else
428  info.joinField = joinLayer->pendingFields().indexFromName( joinInfo->joinFieldName );
429 
430  info.indexOffset = *attIt - sourceLayerIndex;
431  if ( info.joinField < sourceLayerIndex )
432  info.indexOffset++;
433 
434  // for joined fields, we always need to request the targetField from the provider too
435  if ( !fetchAttributes.contains( info.targetField ) )
436  sourceJoinFields << info.targetField;
437 
438  mFetchJoinInfo.insert( joinLayer, info );
439  }
440 
441  // store field source index - we'll need it when fetching from provider
442  mFetchJoinInfo[ joinLayer ].attributes.push_back( sourceLayerIndex );
443  }
444 
445  // add sourceJoinFields if we're using a subset
448 }
449 
450 
452 {
453  // make sure we have space for newly added attributes
454  f.attributes().resize( L->pendingFields().count() ); // f.attributes().count() + mJoinedAttributesCount );
455 
456  QMap<QgsVectorLayer*, FetchJoinInfo>::const_iterator joinIt = mFetchJoinInfo.constBegin();
457  for ( ; joinIt != mFetchJoinInfo.constEnd(); ++joinIt )
458  {
459  const FetchJoinInfo& info = joinIt.value();
460  Q_ASSERT( joinIt.key() );
461 
462  QVariant targetFieldValue = f.attribute( info.targetField );
463  if ( !targetFieldValue.isValid() )
464  continue;
465 
466  const QHash< QString, QgsAttributes>& memoryCache = info.joinInfo->cachedAttributes;
467  if ( memoryCache.isEmpty() )
468  info.addJoinedAttributesDirect( f, targetFieldValue );
469  else
470  info.addJoinedAttributesCached( f, targetFieldValue );
471  }
472 }
473 
475 {
478 
479  // setup simplification for edited geometries to fetch
481  {
483  return mEditGeometrySimplifier != NULL;
484  }
485  return false;
486 }
487 
489 {
490  QgsVectorDataProvider* provider = L->dataProvider();
491 
492  if ( provider && methodType != QgsSimplifyMethod::NoSimplification )
493  {
494  int capabilities = provider->capabilities();
495 
496  if ( methodType == QgsSimplifyMethod::OptimizeForRendering )
497  {
498  return ( capabilities & QgsVectorDataProvider::SimplifyGeometries );
499  }
500  else if ( methodType == QgsSimplifyMethod::PreserveTopology )
501  {
503  }
504  }
505  return false;
506 }
507 
508 
510 {
511  const QHash<QString, QgsAttributes>& memoryCache = joinInfo->cachedAttributes;
512  QHash<QString, QgsAttributes>::const_iterator it = memoryCache.find( joinValue.toString() );
513  if ( it == memoryCache.constEnd() )
514  return; // joined value not found -> leaving the attributes empty (null)
515 
516  int index = indexOffset;
517 
518  const QgsAttributes& featureAttributes = it.value();
519  for ( int i = 0; i < featureAttributes.count(); ++i )
520  {
521  // skip the join field to avoid double field names (fields often have the same name)
522  if ( i == joinField )
523  continue;
524 
525  f.setAttribute( index++, featureAttributes[i] );
526  }
527 }
528 
529 
530 
532 {
533  // no memory cache, query the joined values by setting substring
534  QString subsetString = joinLayer->dataProvider()->subsetString(); // provider might already have a subset string
535  QString bkSubsetString = subsetString;
536  if ( !subsetString.isEmpty() )
537  {
538  subsetString.prepend( "(" ).append( ") AND " );
539  }
540 
541  QString joinFieldName;
542  if ( joinInfo->joinFieldName.isEmpty() && joinInfo->joinFieldIndex >= 0 && joinInfo->joinFieldIndex < joinLayer->pendingFields().count() )
543  joinFieldName = joinLayer->pendingFields().field( joinInfo->joinFieldIndex ).name(); // for compatibility with 1.x
544  else
545  joinFieldName = joinInfo->joinFieldName;
546 
547  subsetString.append( QString( "\"%1\"" ).arg( joinFieldName ) );
548 
549  if ( joinValue.isNull() )
550  {
551  subsetString += " IS NULL";
552  }
553  else
554  {
555  QString v = joinValue.toString();
556  switch ( joinValue.type() )
557  {
558  case QVariant::Int:
559  case QVariant::LongLong:
560  case QVariant::Double:
561  break;
562 
563  default:
564  case QVariant::String:
565  v.replace( "'", "''" );
566  v.prepend( "'" ).append( "'" );
567  break;
568  }
569  subsetString += "=" + v;
570  }
571 
572  joinLayer->dataProvider()->setSubsetString( subsetString, false );
573 
574  // select (no geometry)
575  QgsFeatureRequest request;
577  request.setSubsetOfAttributes( attributes );
578  QgsFeatureIterator fi = joinLayer->getFeatures( request );
579 
580  // get first feature
581  QgsFeature fet;
582  if ( fi.nextFeature( fet ) )
583  {
584  int index = indexOffset;
585  const QgsAttributes& attr = fet.attributes();
586  for ( int i = 0; i < attr.count(); ++i )
587  {
588  if ( i == joinField )
589  continue;
590 
591  f.setAttribute( index++, attr[i] );
592  }
593  }
594  else
595  {
596  // no suitable join feature found, keeping empty (null) attributes
597  }
598 
599  joinLayer->dataProvider()->setSubsetString( bkSubsetString, false );
600 }
601 
602 
603 
604 
606 {
607  QgsFeatureId featureId = mRequest.filterFid();
608 
609  // deleted already?
610  if ( mDeletedFeatureIds.contains( featureId ) )
611  return false;
612 
613  // has changed geometry?
614  if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && mChangedGeometries.contains( featureId ) )
615  {
616  useChangedAttributeFeature( featureId, mChangedGeometries[featureId], f );
617  return true;
618  }
619 
620  // added features
621  for ( QgsFeatureMap::ConstIterator iter = mAddedFeatures.constBegin(); iter != mAddedFeatures.constEnd(); ++iter )
622  {
623  if ( iter->id() == featureId )
624  {
625  useAddedFeature( *iter, f );
626  return true;
627  }
628  }
629 
630  // regular features
632  if ( fi.nextFeature( f ) )
633  {
635 
636  if ( !mFetchJoinInfo.isEmpty() )
637  addJoinedAttributes( f );
638 
639  return true;
640  }
641 
642  return false;
643 }
644 
646 {
647  QgsAttributes& attrs = f.attributes();
648 
649  // remove all attributes that will disappear - from higher indices to lower
650  for ( int idx = mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
651  {
652  attrs.remove( mDeletedAttributeIds[idx] );
653  }
654 
655  // adjust size to accommodate added attributes
656  attrs.resize( attrs.count() + mAddedAttributes.count() );
657 
658  // update changed attributes
659  if ( mChangedAttributeValues.contains( f.id() ) )
660  {
661  const QgsAttributeMap &map = mChangedAttributeValues[f.id()];
662  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
663  attrs[it.key()] = it.value();
664  }
665 }
666 
668 {
669  if ( mChangedGeometries.contains( f.id() ) )
671 }