Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsmemoryfeatureiterator.cpp 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 : : #include "qgsmemoryfeatureiterator.h" 16 : : #include "qgsmemoryprovider.h" 17 : : 18 : : #include "qgsgeometry.h" 19 : : #include "qgsgeometryengine.h" 20 : : #include "qgslogger.h" 21 : : #include "qgsspatialindex.h" 22 : : #include "qgsmessagelog.h" 23 : : #include "qgsproject.h" 24 : : #include "qgsexception.h" 25 : : #include "qgsexpressioncontextutils.h" 26 : : 27 : : ///@cond PRIVATE 28 : : 29 : 68 : QgsMemoryFeatureIterator::QgsMemoryFeatureIterator( QgsMemoryFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) 30 : 68 : : QgsAbstractFeatureIteratorFromSource<QgsMemoryFeatureSource>( source, ownSource, request ) 31 : 136 : { 32 : 68 : if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) 33 : : { 34 : 0 : mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() ); 35 : 0 : } 36 : : try 37 : : { 38 : 68 : mFilterRect = filterRectToSourceCrs( mTransform ); 39 : 68 : } 40 : : catch ( QgsCsException & ) 41 : : { 42 : : // can't reproject mFilterRect 43 : 0 : close(); 44 : : return; 45 : 0 : } 46 : : 47 : 68 : if ( !mSource->mSubsetString.isEmpty() ) 48 : : { 49 : 0 : mSubsetExpression = std::make_unique< QgsExpression >( mSource->mSubsetString ); 50 : 0 : mSubsetExpression->prepare( mSource->expressionContext() ); 51 : 0 : } 52 : : 53 : 68 : if ( !mFilterRect.isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect ) 54 : : { 55 : 0 : mSelectRectGeom = QgsGeometry::fromRect( mFilterRect ); 56 : 0 : mSelectRectEngine.reset( QgsGeometry::createGeometryEngine( mSelectRectGeom.constGet() ) ); 57 : 0 : mSelectRectEngine->prepareGeometry(); 58 : 0 : } 59 : : 60 : : // if there's spatial index, use it! 61 : : // (but don't use it when selection rect is not specified) 62 : 68 : if ( !mFilterRect.isNull() && mSource->mSpatialIndex ) 63 : : { 64 : 0 : mUsingFeatureIdList = true; 65 : 0 : mFeatureIdList = mSource->mSpatialIndex->intersects( mFilterRect ); 66 : 0 : QgsDebugMsgLevel( "Features returned by spatial index: " + QString::number( mFeatureIdList.count() ), 2 ); 67 : 0 : } 68 : 68 : else if ( mRequest.filterType() == QgsFeatureRequest::FilterFid ) 69 : : { 70 : 0 : mUsingFeatureIdList = true; 71 : 0 : QgsFeatureMap::const_iterator it = mSource->mFeatures.constFind( mRequest.filterFid() ); 72 : 0 : if ( it != mSource->mFeatures.constEnd() ) 73 : 0 : mFeatureIdList.append( mRequest.filterFid() ); 74 : 0 : } 75 : 68 : else if ( mRequest.filterType() == QgsFeatureRequest::FilterFids ) 76 : : { 77 : 120 : mUsingFeatureIdList = true; 78 : 52 : mFeatureIdList = qgis::setToList( mRequest.filterFids() ); 79 : 52 : } 80 : : else 81 : : { 82 : 16 : mUsingFeatureIdList = false; 83 : : } 84 : : 85 : 68 : rewind(); 86 : 68 : } 87 : : 88 : 136 : QgsMemoryFeatureIterator::~QgsMemoryFeatureIterator() 89 : 136 : { 90 : 68 : close(); 91 : 136 : } 92 : : 93 : 139 : bool QgsMemoryFeatureIterator::fetchFeature( QgsFeature &feature ) 94 : : { 95 : 139 : feature.setValid( false ); 96 : : 97 : 139 : if ( mClosed ) 98 : 0 : return false; 99 : : 100 : 139 : if ( mUsingFeatureIdList ) 101 : 106 : return nextFeatureUsingList( feature ); 102 : : else 103 : 33 : return nextFeatureTraverseAll( feature ); 104 : 139 : } 105 : : 106 : : 107 : 106 : bool QgsMemoryFeatureIterator::nextFeatureUsingList( QgsFeature &feature ) 108 : : { 109 : 106 : bool hasFeature = false; 110 : : 111 : : // option 1: we have a list of features to traverse 112 : 106 : QgsFeature candidate; 113 : 106 : while ( mFeatureIdListIterator != mFeatureIdList.constEnd() ) 114 : : { 115 : 57 : candidate = mSource->mFeatures.value( *mFeatureIdListIterator ); 116 : 57 : if ( !mFilterRect.isNull() ) 117 : : { 118 : 0 : if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect ) 119 : : { 120 : : // do exact check in case we're doing intersection 121 : 0 : if ( candidate.hasGeometry() && mSelectRectEngine->intersects( candidate.geometry().constGet() ) ) 122 : 0 : hasFeature = true; 123 : 0 : } 124 : 0 : else if ( mSource->mSpatialIndex ) 125 : : { 126 : : // using a spatial index - so we already know that the bounding box intersects correctly 127 : 0 : hasFeature = true; 128 : 0 : } 129 : : else 130 : : { 131 : : // do bounding box check if we aren't using a spatial index 132 : 0 : if ( candidate.hasGeometry() && candidate.geometry().boundingBoxIntersects( mFilterRect ) ) 133 : 0 : hasFeature = true; 134 : : } 135 : 0 : } 136 : : else 137 : 57 : hasFeature = true; 138 : : 139 : 57 : if ( hasFeature && mSubsetExpression ) 140 : : { 141 : 0 : mSource->expressionContext()->setFeature( candidate ); 142 : 0 : if ( !mSubsetExpression->evaluate( mSource->expressionContext() ).toBool() ) 143 : 0 : hasFeature = false; 144 : 0 : } 145 : : 146 : 57 : if ( hasFeature ) 147 : 57 : break; 148 : : 149 : 0 : ++mFeatureIdListIterator; 150 : : } 151 : : 152 : : // copy feature 153 : 106 : if ( hasFeature ) 154 : : { 155 : 57 : feature = candidate; 156 : 57 : ++mFeatureIdListIterator; 157 : 57 : } 158 : : else 159 : 49 : close(); 160 : : 161 : 106 : if ( hasFeature ) 162 : : { 163 : 57 : feature.setFields( mSource->mFields ); // allow name-based attribute lookups 164 : 57 : geometryToDestinationCrs( feature, mTransform ); 165 : 57 : } 166 : : 167 : 106 : return hasFeature; 168 : 106 : } 169 : : 170 : : 171 : 33 : bool QgsMemoryFeatureIterator::nextFeatureTraverseAll( QgsFeature &feature ) 172 : : { 173 : 33 : bool hasFeature = false; 174 : : 175 : : // option 2: traversing the whole layer 176 : 33 : while ( mSelectIterator != mSource->mFeatures.constEnd() ) 177 : : { 178 : 17 : if ( mFilterRect.isNull() ) 179 : : { 180 : : // selection rect empty => using all features 181 : 17 : hasFeature = true; 182 : 17 : } 183 : : else 184 : : { 185 : 0 : if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect ) 186 : : { 187 : : // using exact test when checking for intersection 188 : 0 : if ( mSelectIterator->hasGeometry() && mSelectRectEngine->intersects( mSelectIterator->geometry().constGet() ) ) 189 : 0 : hasFeature = true; 190 : 0 : } 191 : : else 192 : : { 193 : : // check just bounding box against rect when not using intersection 194 : 0 : if ( mSelectIterator->hasGeometry() && mSelectIterator->geometry().boundingBox().intersects( mFilterRect ) ) 195 : 0 : hasFeature = true; 196 : : } 197 : : } 198 : : 199 : 17 : if ( mSubsetExpression ) 200 : : { 201 : 0 : mSource->expressionContext()->setFeature( *mSelectIterator ); 202 : 0 : if ( !mSubsetExpression->evaluate( mSource->expressionContext() ).toBool() ) 203 : 0 : hasFeature = false; 204 : 0 : } 205 : : 206 : 17 : if ( hasFeature ) 207 : 17 : break; 208 : : 209 : 0 : ++mSelectIterator; 210 : : } 211 : : 212 : : // copy feature 213 : 33 : if ( hasFeature ) 214 : : { 215 : 17 : feature = mSelectIterator.value(); 216 : 17 : ++mSelectIterator; 217 : 17 : feature.setValid( true ); 218 : 17 : feature.setFields( mSource->mFields ); // allow name-based attribute lookups 219 : 17 : geometryToDestinationCrs( feature, mTransform ); 220 : 17 : } 221 : : else 222 : 16 : close(); 223 : : 224 : 33 : return hasFeature; 225 : 0 : } 226 : : 227 : 68 : bool QgsMemoryFeatureIterator::rewind() 228 : : { 229 : 68 : if ( mClosed ) 230 : 0 : return false; 231 : : 232 : 68 : if ( mUsingFeatureIdList ) 233 : 52 : mFeatureIdListIterator = mFeatureIdList.constBegin(); 234 : : else 235 : 16 : mSelectIterator = mSource->mFeatures.constBegin(); 236 : : 237 : 68 : return true; 238 : 68 : } 239 : : 240 : 200 : bool QgsMemoryFeatureIterator::close() 241 : : { 242 : 200 : if ( mClosed ) 243 : 132 : return false; 244 : : 245 : 68 : iteratorClosed(); 246 : : 247 : 68 : mClosed = true; 248 : 68 : return true; 249 : 200 : } 250 : : 251 : : // ------------------------- 252 : : 253 : 134 : QgsMemoryFeatureSource::QgsMemoryFeatureSource( const QgsMemoryProvider *p ) 254 : 67 : : mFields( p->mFields ) 255 : 67 : , mFeatures( p->mFeatures ) 256 : 67 : , mSpatialIndex( p->mSpatialIndex ? std::make_unique< QgsSpatialIndex >( *p->mSpatialIndex ) : nullptr ) // just shallow copy 257 : 67 : , mSubsetString( p->mSubsetString ) 258 : 67 : , mCrs( p->mCrs ) 259 : 134 : { 260 : 67 : } 261 : : 262 : 68 : QgsFeatureIterator QgsMemoryFeatureSource::getFeatures( const QgsFeatureRequest &request ) 263 : : { 264 : 68 : return QgsFeatureIterator( new QgsMemoryFeatureIterator( this, false, request ) ); 265 : 0 : } 266 : : 267 : 0 : QgsExpressionContext *QgsMemoryFeatureSource::expressionContext() 268 : : { 269 : : // lazy construct expression context -- it's not free to calculate, and is only used when 270 : : // iterating over a memory layer with a subset string set 271 : 0 : if ( !mExpressionContext ) 272 : : { 273 : 0 : mExpressionContext = std::make_unique< QgsExpressionContext >( 274 : 0 : QList<QgsExpressionContextScope *>() 275 : 0 : << QgsExpressionContextUtils::globalScope() 276 : 0 : << QgsExpressionContextUtils::projectScope( QgsProject::instance() ) ); 277 : 0 : mExpressionContext->setFields( mFields ); 278 : 0 : } 279 : 0 : return mExpressionContext.get(); 280 : 0 : } 281 : : 282 : : ///@endcond PRIVATE