Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsfeatureiterator.h 3 : : --------------------- 4 : : begin : Juli 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 : : ***************************************************************************/ 15 : : #ifndef QGSFEATUREITERATOR_H 16 : : #define QGSFEATUREITERATOR_H 17 : : 18 : : #include "qgis_core.h" 19 : : #include "qgsfeaturerequest.h" 20 : : #include "qgsindexedfeature.h" 21 : : 22 : : class QgsFeedback; 23 : : 24 : : /** 25 : : * \ingroup core 26 : : * \brief Internal feature iterator to be implemented within data providers 27 : : */ 28 : : class CORE_EXPORT QgsAbstractFeatureIterator 29 : : { 30 : : public: 31 : : 32 : : //! Status of expression compilation for filter expression requests 33 : : enum CompileStatus 34 : : { 35 : : NoCompilation, //!< Expression could not be compiled or not attempt was made to compile expression 36 : : PartiallyCompiled, //!< Expression was partially compiled, but extra checks need to be applied to features 37 : : Compiled, //!< Expression was fully compiled and delegated to data provider source 38 : : }; 39 : : 40 : : //! base class constructor - stores the iteration parameters 41 : : QgsAbstractFeatureIterator( const QgsFeatureRequest &request ); 42 : : 43 : : //! destructor makes sure that the iterator is closed properly 44 : 318 : virtual ~QgsAbstractFeatureIterator() = default; 45 : : 46 : : //! fetch next feature, return TRUE on success 47 : : virtual bool nextFeature( QgsFeature &f ); 48 : : 49 : : //! reset the iterator to the starting position 50 : : virtual bool rewind() = 0; 51 : : //! end of iterating: free the resources / lock 52 : : virtual bool close() = 0; 53 : : 54 : : /** 55 : : * Attach an object that can be queried regularly by the iterator to check 56 : : * if it must stopped. This is mostly useful for iterators where a single 57 : : * nextFeature()/fetchFeature() iteration might be very long. A typical use case is the 58 : : * WFS provider. When nextFeature()/fetchFeature() is reasonably fast, it is not necessary 59 : : * to implement this method. The default implementation does nothing. 60 : : * \note not available in Python bindings 61 : : * \since QGIS 2.16 62 : : */ 63 : : virtual void setInterruptionChecker( QgsFeedback *interruptionChecker ) SIP_SKIP; 64 : : 65 : : /** 66 : : * Returns the status of expression compilation for filter expression requests. 67 : : * \since QGIS 2.16 68 : : */ 69 : : CompileStatus compileStatus() const { return mCompileStatus; } 70 : : 71 : : /** 72 : : * Returns if this iterator is valid. 73 : : * An invalid feature iterator is not able to provide a reliable source for data. 74 : : * If an iterator is invalid, either give up or try to send the request again (preferably 75 : : * after a timeout to give the system some time to stay responsive). 76 : : * 77 : : * If you want to check if the iterator successfully completed, better use QgsFeatureIterator::isClosed(). 78 : : * 79 : : * \since QGIS 3.0 80 : : */ 81 : 0 : virtual bool isValid() const 82 : : { 83 : 0 : return mValid; 84 : : } 85 : : 86 : : /** 87 : : * Indicator if there was an error when sending the compiled query to the server. 88 : : * This indicates that there is something wrong with the expression compiler. 89 : : * 90 : : * \since QGIS 3.2 91 : : */ 92 : : bool compileFailed() const; 93 : : 94 : : protected: 95 : : 96 : : /** 97 : : * If you write a feature iterator for your provider, this is the method you 98 : : * need to implement!! 99 : : * 100 : : * \param f The feature to write to 101 : : * \returns TRUE if a feature was written to f 102 : : */ 103 : : virtual bool fetchFeature( QgsFeature &f ) = 0; 104 : : 105 : : /** 106 : : * By default, the iterator will fetch all features and check if the feature 107 : : * matches the expression. 108 : : * If you have a more sophisticated metodology (SQL request for the features...) 109 : : * and you check for the expression in your fetchFeature method, you can just 110 : : * redirect this call to fetchFeature so the default check will be omitted. 111 : : * 112 : : * \param f The feature to write to 113 : : * \returns TRUE if a feature was written to f 114 : : */ 115 : : virtual bool nextFeatureFilterExpression( QgsFeature &f ); 116 : : 117 : : /** 118 : : * By default, the iterator will fetch all features and check if the id 119 : : * is in the request. 120 : : * If you have a more sophisticated metodology (SQL request for the features...) 121 : : * and you are sure, that any feature you return from fetchFeature will match 122 : : * if the request was FilterFids you can just redirect this call to fetchFeature 123 : : * so the default check will be omitted. 124 : : * 125 : : * \param f The feature to write to 126 : : * \returns TRUE if a feature was written to f 127 : : */ 128 : : virtual bool nextFeatureFilterFids( QgsFeature &f ); 129 : : 130 : : /** 131 : : * Transforms \a feature's geometry according to the specified coordinate \a transform. 132 : : * If \a feature has no geometry or \a transform is invalid then calling this method 133 : : * has no effect and will be shortcut. 134 : : * Iterators should call this method before returning features to ensure that any 135 : : * QgsFeatureRequest::destinationCrs() set on the request is respected. 136 : : * \since QGIS 3.0 137 : : */ 138 : : void geometryToDestinationCrs( QgsFeature &feature, const QgsCoordinateTransform &transform ) const; 139 : : 140 : : 141 : : /** 142 : : * Returns a rectangle representing the original request's QgsFeatureRequest::filterRect(). 143 : : * If \a transform is a valid coordinate transform, the return rectangle will represent 144 : : * the requested filterRect() transformed to the source's coordinate reference system. 145 : : * Iterators should call this method and use the returned rectangle for filtering 146 : : * features to ensure that any QgsFeatureRequest::destinationCrs() set on the request is respected. 147 : : * Will throw a QgsCsException if the rect cannot be transformed from the destination CRS. 148 : : * \since QGIS 3.0 149 : : */ 150 : : QgsRectangle filterRectToSourceCrs( const QgsCoordinateTransform &transform ) const SIP_THROW( QgsCsException ); 151 : : 152 : : //! A copy of the feature request. 153 : : QgsFeatureRequest mRequest; 154 : : 155 : : //! Sets to TRUE, as soon as the iterator is closed. 156 : : bool mClosed = false; 157 : : 158 : : /** 159 : : * A feature iterator may be closed already but still be serving features from the cache. 160 : : * This is done when we serve features which have been pre-fetched and the order by has 161 : : * been locally sorted. 162 : : * In such a scenario, all resources have been released (mClosed is TRUE) but the deads 163 : : * are still alive. 164 : : */ 165 : : bool mZombie = false; 166 : : 167 : : // TODO QGIS 4: make this private 168 : : 169 : : /** 170 : : * reference counting (to allow seamless copying of QgsFeatureIterator instances) 171 : : */ 172 : : int refs = 0; 173 : : //! Add reference 174 : : void ref(); 175 : : //! Remove reference, delete if refs == 0 176 : : void deref(); 177 : : friend class QgsFeatureIterator; 178 : : 179 : : //! Number of features already fetched by iterator 180 : : long mFetchedCount = 0; 181 : : 182 : : //! Status of compilation of filter expression 183 : : CompileStatus mCompileStatus = NoCompilation; 184 : : 185 : : bool mCompileFailed = false; 186 : : 187 : : //! Setup the simplification of geometries to fetch using the specified simplify method 188 : : virtual bool prepareSimplification( const QgsSimplifyMethod &simplifyMethod ); 189 : : 190 : : /** 191 : : * An invalid state of a feature iterator indicates that there was a problem with 192 : : * even getting it up and running. 193 : : * This should be set to FALSE by subclasses if they have problems connecting to 194 : : * the provider. 195 : : * Do NOT set this to FALSE when the feature iterator closes or has no features but 196 : : * we are sure, that it's just an empty dataset. 197 : : */ 198 : : bool mValid = true; 199 : : 200 : : private: 201 : : bool mUseCachedFeatures = false; 202 : : QList<QgsIndexedFeature> mCachedFeatures; 203 : : QList<QgsIndexedFeature>::ConstIterator mFeatureIterator; 204 : : 205 : : //! returns whether the iterator supports simplify geometries on provider side 206 : : virtual bool providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const; 207 : : 208 : : /** 209 : : * Should be overwritten by providers which implement an own order by strategy 210 : : * If the own order by strategy is successful, return TRUE, if not, return FALSE 211 : : * and a local order by will be triggered instead. 212 : : * By default returns FALSE 213 : : * 214 : : * \since QGIS 2.14 215 : : */ 216 : : virtual bool prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys ); 217 : : 218 : : /** 219 : : * Setup the orderby. Internally calls prepareOrderBy and if FALSE is returned will 220 : : * cache all features and order them with local expression evaluation. 221 : : * 222 : : * \since QGIS 2.14 223 : : */ 224 : : void setupOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys ); 225 : : }; 226 : : 227 : : 228 : : /** 229 : : * \ingroup core 230 : : * \brief Helper template that cares of two things: 1. automatic deletion of source if owned by iterator, 2. notification of open/closed iterator. 231 : : * \note not available in Python bindings (although present in SIP file) 232 : : */ 233 : : template<typename T> 234 : : class QgsAbstractFeatureIteratorFromSource : public QgsAbstractFeatureIterator 235 : : { 236 : : public: 237 : 318 : QgsAbstractFeatureIteratorFromSource( T *source, bool ownSource, const QgsFeatureRequest &request ) 238 : 318 : : QgsAbstractFeatureIterator( request ) 239 : 318 : , mSource( source ) 240 : 318 : , mOwnSource( ownSource ) 241 : 636 : { 242 : 318 : mSource->iteratorOpened( this ); 243 : 318 : } 244 : : 245 : 318 : ~QgsAbstractFeatureIteratorFromSource() override 246 : 318 : { 247 : 318 : if ( mOwnSource ) 248 : 133 : delete mSource; 249 : 318 : } 250 : : 251 : : protected: 252 : : //! to be called by from subclass in close() 253 : 318 : void iteratorClosed() { mSource->iteratorClosed( this ); } 254 : : 255 : : T *mSource = nullptr; 256 : : bool mOwnSource; 257 : : }; 258 : : 259 : : 260 : : /** 261 : : * \ingroup core 262 : : * \brief Wrapper for iterator of features from vector data provider or vector layer 263 : : */ 264 : : class CORE_EXPORT QgsFeatureIterator 265 : : { 266 : : public: 267 : : 268 : : #ifdef SIP_RUN 269 : : QgsFeatureIterator *__iter__(); 270 : : % MethodCode 271 : : sipRes = sipCpp; 272 : : % End 273 : : 274 : : SIP_PYOBJECT __next__() SIP_TYPEHINT( QgsFeature ); 275 : : % MethodCode 276 : : std::unique_ptr< QgsFeature > f = std::make_unique< QgsFeature >(); 277 : : bool result = false; 278 : : Py_BEGIN_ALLOW_THREADS 279 : : result = ( sipCpp->nextFeature( *f ) ); 280 : : Py_END_ALLOW_THREADS 281 : : if ( result ) 282 : : sipRes = sipConvertFromType( f.release(), sipType_QgsFeature, Py_None ); 283 : : else 284 : : { 285 : : PyErr_SetString( PyExc_StopIteration, "" ); 286 : : } 287 : : % End 288 : : #endif 289 : : 290 : : //! Construct invalid iterator 291 : 316 : QgsFeatureIterator() = default; 292 : : //! Construct a valid iterator 293 : : QgsFeatureIterator( QgsAbstractFeatureIterator *iter SIP_TRANSFER ); 294 : : //! Copy constructor copies the iterator, increases ref.count 295 : : QgsFeatureIterator( const QgsFeatureIterator &fi ); 296 : : //! Destructor deletes the iterator if it has no more references 297 : : ~QgsFeatureIterator(); 298 : : 299 : : QgsFeatureIterator &operator=( const QgsFeatureIterator &other ); 300 : : 301 : : bool nextFeature( QgsFeature &f ); 302 : : bool rewind(); 303 : : bool close(); 304 : : 305 : : /** 306 : : * Will return if this iterator is valid. 307 : : * An invalid iterator was probably introduced by a failed attempt to acquire a connection 308 : : * or is a default constructed iterator. 309 : : * 310 : : * \see isClosed to check if the iterator successfully completed and returned all the features. 311 : : * 312 : : * \since QGIS 3.0 313 : : */ 314 : : bool isValid() const; 315 : : 316 : : //! find out whether the iterator is still valid or closed already 317 : : bool isClosed() const; 318 : : 319 : : /** 320 : : * Attach an object that can be queried regularly by the iterator to check 321 : : * if it must stopped. This is mostly useful for iterators where a single 322 : : * nextFeature()/fetchFeature() iteration might be very long. A typical use case is the 323 : : * WFS provider. 324 : : * \note not available in Python bindings 325 : : * \since QGIS 2.16 326 : : */ 327 : : void setInterruptionChecker( QgsFeedback *interruptionChecker ) SIP_SKIP; 328 : : 329 : : /** 330 : : * Returns the status of expression compilation for filter expression requests. 331 : : * \since QGIS 2.16 332 : : */ 333 : : QgsAbstractFeatureIterator::CompileStatus compileStatus() const { return mIter->compileStatus(); } 334 : : 335 : : /** 336 : : * Indicator if there was an error when sending the compiled query to the server. 337 : : * This indicates that there is something wrong with the expression compiler. 338 : : * 339 : : * \since QGIS 3.2 340 : : */ 341 : : bool compileFailed() const { return mIter->compileFailed(); } 342 : : 343 : : friend bool operator== ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 ) SIP_SKIP; 344 : : friend bool operator!= ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 ) SIP_SKIP; 345 : : 346 : : protected: 347 : 316 : QgsAbstractFeatureIterator *mIter = nullptr; 348 : : 349 : : 350 : : }; 351 : : 352 : : #ifndef SIP_RUN 353 : : 354 : 318 : inline QgsFeatureIterator::QgsFeatureIterator( QgsAbstractFeatureIterator *iter ) 355 : 318 : : mIter( iter ) 356 : : { 357 : 318 : if ( iter ) 358 : 318 : iter->ref(); 359 : 318 : } 360 : : 361 : 14 : inline QgsFeatureIterator::QgsFeatureIterator( const QgsFeatureIterator &fi ) 362 : 14 : : mIter( fi.mIter ) 363 : : { 364 : 14 : if ( mIter ) 365 : 14 : mIter->ref(); 366 : 14 : } 367 : : 368 : 648 : inline QgsFeatureIterator::~QgsFeatureIterator() 369 : : { 370 : 648 : if ( mIter ) 371 : 468 : mIter->deref(); 372 : 648 : } 373 : : 374 : 2501 : inline bool QgsFeatureIterator::nextFeature( QgsFeature &f ) 375 : : { 376 : 2501 : return mIter ? mIter->nextFeature( f ) : false; 377 : : } 378 : : 379 : 0 : inline bool QgsFeatureIterator::rewind() 380 : : { 381 : 0 : if ( mIter ) 382 : 0 : mIter->mFetchedCount = 0; 383 : : 384 : 0 : return mIter ? mIter->rewind() : false; 385 : : } 386 : : 387 : 160 : inline bool QgsFeatureIterator::close() 388 : : { 389 : 160 : if ( mIter ) 390 : 135 : mIter->mFetchedCount = 0; 391 : : 392 : 160 : return mIter ? mIter->close() : false; 393 : : } 394 : : 395 : 1159 : inline bool QgsFeatureIterator::isClosed() const 396 : : { 397 : 1159 : return mIter ? mIter->mClosed && !mIter->mZombie : true; 398 : : } 399 : : 400 : : inline bool operator== ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 ) 401 : : { 402 : : return fi1.mIter == fi2.mIter; 403 : : } 404 : : 405 : : inline bool operator!= ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 ) 406 : : { 407 : : return !( fi1 == fi2 ); 408 : : } 409 : : 410 : 2 : inline void QgsFeatureIterator::setInterruptionChecker( QgsFeedback *interruptionChecker ) 411 : : { 412 : 2 : if ( mIter ) 413 : 2 : mIter->setInterruptionChecker( interruptionChecker ); 414 : 2 : } 415 : : 416 : : #endif 417 : : 418 : : #endif // QGSFEATUREITERATOR_H