void QgsSpatialQuery::populateIndexResultDisjoint( QgsFeatureIds &qsetIndexResult, QgsFeatureId idTarget, QgsGeometry * geomTarget, bool ( QgsGeometryEngine::* op )( const QgsAbstractGeometryV2&, QString* ) const ) { QgsFeatureIds listIdReference = mIndexReference.intersects( geomTarget->boundingBox() ).toSet(); if ( listIdReference.isEmpty() ) { qsetIndexResult.insert( idTarget ); return; } //prepare geometry QgsGeometryEngine* geomEngine = geomTarget->createGeometryEngine( geomTarget->geometry() ); geomEngine->prepareGeometry(); QgsFeature featureReference; const QgsGeometry * geomReference; QgsFeatureIterator listIt = mLayerReference->getFeatures( QgsFeatureRequest().setFilterFids( listIdReference ) ); bool addIndex = true; while ( listIt.nextFeature( featureReference ) ) { geomReference = featureReference.constGeometry(); if (( geomEngine->*op )( *( geomReference->geometry() ), 0 ) ) { addIndex = false; break; } } if ( addIndex ) { qsetIndexResult.insert( idTarget ); } delete geomEngine; } // void QgsSpatialQuery::populateIndexResultDisjoint( ...
void QgsSpatialQuery::populateIndexResultDisjoint( QgsFeatureIds &qsetIndexResult, QgsFeatureId idTarget, QgsGeometry * geomTarget, bool ( QgsGeometry::* op )( QgsGeometry * ) ) { QList<QgsFeatureId> listIdReference; listIdReference = mIndexReference.intersects( geomTarget->boundingBox() ); if ( listIdReference.count() == 0 ) { qsetIndexResult.insert( idTarget ); return; } QgsFeature featureReference; QgsGeometry * geomReference; QList<QgsFeatureId>::iterator iterIdReference = listIdReference.begin(); bool addIndex = true; for ( ; iterIdReference != listIdReference.end(); iterIdReference++ ) { mLayerReference->featureAtId( *iterIdReference, featureReference ); geomReference = featureReference.geometry(); if ( !( geomTarget->*op )( geomReference ) ) { addIndex = false; break; } } if ( addIndex ) { qsetIndexResult.insert( idTarget ); } } // void QgsSpatialQuery::populateIndexResultDisjoint( ...
void QgsSpatialQuery::populateIndexResultDisjoint( QgsFeatureIds &qsetIndexResult, QgsFeatureId idTarget, QgsGeometry * geomTarget, bool ( QgsGeometry::* op )( const QgsGeometry * ) const ) { QList<QgsFeatureId> listIdReference; listIdReference = mIndexReference.intersects( geomTarget->boundingBox() ); if ( listIdReference.isEmpty() ) { qsetIndexResult.insert( idTarget ); return; } QgsFeature featureReference; const QgsGeometry * geomReference; QList<QgsFeatureId>::iterator iterIdReference = listIdReference.begin(); bool addIndex = true; for ( ; iterIdReference != listIdReference.end(); ++iterIdReference ) { mLayerReference->getFeatures( QgsFeatureRequest().setFilterFid( *iterIdReference ) ).nextFeature( featureReference ); geomReference = featureReference.constGeometry(); if ( !( geomTarget->*op )( geomReference ) ) { addIndex = false; break; } } if ( addIndex ) { qsetIndexResult.insert( idTarget ); } } // void QgsSpatialQuery::populateIndexResultDisjoint( ...
void QgsAttributeTableView::onVerticalHeaderSectionClicked( int logicalIndex ) { Q_UNUSED( logicalIndex ) QgsFeatureIds selectedFeatures; QModelIndexList selectedRows = selectionModel()->selectedRows(); foreach ( QModelIndex row, selectedRows ) { selectedFeatures.insert( mFilterModel->rowToId( row ) ); }
bool QgsMapToolShowHideLabels::selectedFeatures( QgsVectorLayer* vlayer, QgsFeatureIds& selectedFeatIds ) { // culled from QgsMapToolSelectUtils::setSelectFeatures() QgsGeometry* selectGeometry = mRubberBand->asGeometry(); // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. QgsGeometry selectGeomTrans( *selectGeometry ); if ( mRender->hasCrsTransformEnabled() ) { try { QgsCoordinateTransform ct( mRender->destinationCrs(), vlayer->crs() ); selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) ); QMessageBox::warning( mCanvas, QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system." ) ); return false; } } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectGeomTrans.boundingBox() ).setFlags( QgsFeatureRequest::NoGeometry | QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; while ( fit.nextFeature( f ) ) { QgsGeometry* g = f.geometry(); if ( !selectGeomTrans.intersects( g ) ) continue; selectedFeatIds.insert( f.id() ); } QApplication::restoreOverrideCursor(); return true; }
bool QgsVectorLayerTools::copyMoveFeatures( QgsVectorLayer* layer, QgsFeatureRequest& request, double dx, double dy, QString* errorMsg ) const { bool res = false; if ( !layer || !layer->isEditable() ) { return false; } QgsFeatureIterator fi = layer->getFeatures( request ); QgsFeature f; QgsAttributeList pkAttrList = layer->pkAttributeList(); int browsedFeatureCount = 0; int couldNotWriteCount = 0; int noGeometryCount = 0; QgsFeatureIds fidList; while ( fi.nextFeature( f ) ) { browsedFeatureCount++; // remove pkey values Q_FOREACH ( auto idx, pkAttrList ) { f.setAttribute( idx, QVariant() ); } // translate if ( f.hasGeometry() ) { QgsGeometry geom = f.geometry(); geom.translate( dx, dy ); f.setGeometry( geom ); #ifdef QGISDEBUG const QgsFeatureId fid = f.id(); #endif // paste feature if ( !layer->addFeature( f, false ) ) { couldNotWriteCount++; QgsDebugMsg( QString( "Could not add new feature. Original copied feature id: %1" ).arg( fid ) ); } else { fidList.insert( f.id() ); } } else { noGeometryCount++; } }
void QgsDualView::flashCurrentFeature() { QModelIndex currentIndex = mTableView->currentIndex(); if ( !currentIndex.isValid() ) { return; } QgsFeatureIds ids; ids.insert( mFilterModel->rowToId( currentIndex ) ); QgsMapCanvas *canvas = mFilterModel->mapCanvas(); if ( canvas ) { canvas->flashFeatureIds( mLayer, ids ); } }
void QgsFeaturePool::addFeature( QgsFeature &feature ) { QgsFeatureList features; features.append( feature ); mLayerMutex.lock(); mLayer->dataProvider()->addFeatures( features ); feature.setId( features.front().id() ); if ( mSelectedOnly ) { QgsFeatureIds selectedFeatureIds = mLayer->selectedFeatureIds(); selectedFeatureIds.insert( feature.id() ); mLayer->selectByIds( selectedFeatureIds ); } mLayerMutex.unlock(); mIndexMutex.lock(); mIndex.insertFeature( feature ); mIndexMutex.unlock(); }
QVariantMap QgsSelectByLocationAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { QgsVectorLayer *selectLayer = parameterAsVectorLayer( parameters, QStringLiteral( "INPUT" ), context ); QgsVectorLayer::SelectBehavior method = static_cast< QgsVectorLayer::SelectBehavior >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) ); std::unique_ptr< QgsFeatureSource > intersectSource( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) ); const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context ); QgsFeatureIds selectedIds; auto addToSelection = [&]( const QgsFeature & feature ) { selectedIds.insert( feature.id() ); }; process( selectLayer, intersectSource.get(), selectedPredicates, addToSelection, true, feedback ); selectLayer->selectByIds( selectedIds, method ); QVariantMap results; results.insert( QStringLiteral( "OUTPUT" ), parameters.value( QStringLiteral( "INPUT" ) ) ); return results; }
QgsFeatureIds QgsAfsSharedData::getFeatureIdsInExtent( const QgsRectangle &extent, QgsFeedback *feedback ) { QString errorTitle; QString errorText; const QString authcfg = mDataSource.authConfigId(); const QList<quint32> featuresInRect = QgsArcGisRestUtils::getObjectIdsByExtent( mDataSource.param( QStringLiteral( "url" ) ), mObjectIdFieldName, extent, errorTitle, errorText, authcfg, feedback ); QgsFeatureIds ids; for ( quint32 id : featuresInRect ) { int featureId = mObjectIds.indexOf( id ); if ( featureId >= 0 ) ids.insert( featureId ); } return ids; }
void QgsSpatialQuery::setSpatialIndexReference( QgsFeatureIds &qsetIndexInvalidReference ) { QgsReaderFeatures * readerFeaturesReference = new QgsReaderFeatures( mLayerReference, mUseReferenceSelection ); QgsFeature feature; int step = 1; while ( readerFeaturesReference->nextFeature( feature ) ) { mPb->step( step++ ); if ( ! hasValidGeometry( feature ) ) { qsetIndexInvalidReference.insert( feature.id() ); continue; } mIndexReference.insertFeature( feature ); } delete readerFeaturesReference; } // void QgsSpatialQuery::setSpatialIndexReference()
/** * Selection routine called by the mouse release event * @param thePoint = QgsPoint representing the x, y coordinates of the mouse release event */ void eVisEventIdTool::select( QgsPoint thePoint ) { if ( 0 == mCanvas ) return; QgsVectorLayer* myLayer = ( QgsVectorLayer* )mCanvas->currentLayer( ); // create the search rectangle. this was modeled after the QgsMapIdentifyTool in core QGIS application double searchWidth = mCanvas->extent( ).width( ) * (( double )QGis::DEFAULT_IDENTIFY_RADIUS / 100.0 ); QgsRectangle myRectangle; myRectangle.setXMinimum( thePoint.x( ) - searchWidth ); myRectangle.setXMaximum( thePoint.x( ) + searchWidth ); myRectangle.setYMinimum( thePoint.y( ) - searchWidth ); myRectangle.setYMaximum( thePoint.y( ) + searchWidth ); //Transform rectange to map coordinates myRectangle = toLayerCoordinates( myLayer, myRectangle ); //Rather than add to the current selection, clear all selected features myLayer->removeSelection( false ); //select features QgsFeatureIterator fit = myLayer->getFeatures( QgsFeatureRequest().setFilterRect( myRectangle ).setFlags( QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; QgsFeatureIds newSelectedFeatures; while ( fit.nextFeature( f ) ) { newSelectedFeatures.insert( f.id() ); } myLayer->setSelectedFeatures( newSelectedFeatures ); //Launch a new event browser to view selected features mBrowser = new eVisGenericEventBrowserGui( mCanvas, mCanvas, NULL ); mBrowser->setAttribute( Qt::WA_DeleteOnClose ); }
bool QgsMapToolShowHideLabels::selectedLabelFeatures( QgsVectorLayer* vlayer, QgsFeatureIds& selectedFeatIds ) { // get list of all drawn labels from current layer that intersect rubberband QgsPalLabeling* labelEngine = dynamic_cast<QgsPalLabeling*>( mRender->labelingEngine() ); if ( !labelEngine ) { QgsDebugMsg( "No labeling engine" ); return false; } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsRectangle ext = mRubberBand->asGeometry()->boundingBox(); QList<QgsLabelPosition> labelPosList = labelEngine->labelsWithinRect( ext ); QList<QgsLabelPosition>::const_iterator it; for ( it = labelPosList.constBegin() ; it != labelPosList.constEnd(); ++it ) { mCurrentLabelPos = *it; if ( mCurrentLabelPos.layerID != vlayer->id() ) { // only work with labels from the current active and editable layer continue; } selectedFeatIds.insert( mCurrentLabelPos.featureId ); } QApplication::restoreOverrideCursor(); return true; }
/** * Selection routine called by the mouse release event * @param point = QgsPointXY representing the x, y coordinates of the mouse release event */ void eVisEventIdTool::select( const QgsPointXY &point ) { if ( !mCanvas ) return; QgsVectorLayer *myLayer = ( QgsVectorLayer * )mCanvas->currentLayer(); // create the search rectangle. this was modeled after the QgsMapIdentifyTool in core QGIS application double searchWidth = QgsMapTool::searchRadiusMU( mCanvas ); QgsRectangle myRectangle; myRectangle.setXMinimum( point.x() - searchWidth ); myRectangle.setXMaximum( point.x() + searchWidth ); myRectangle.setYMinimum( point.y() - searchWidth ); myRectangle.setYMaximum( point.y() + searchWidth ); //Transform rectangle to map coordinates myRectangle = toLayerCoordinates( myLayer, myRectangle ); //select features QgsFeatureIterator fit = myLayer->getFeatures( QgsFeatureRequest().setFilterRect( myRectangle ).setFlags( QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; QgsFeatureIds newSelectedFeatures; while ( fit.nextFeature( f ) ) { newSelectedFeatures.insert( f.id() ); } myLayer->selectByIds( newSelectedFeatures ); //Launch a new event browser to view selected features mBrowser = new eVisGenericEventBrowserGui( mCanvas, mCanvas, nullptr ); mBrowser->setAttribute( Qt::WA_DeleteOnClose ); }
bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds, const QString& outputLayer, const QString& outputFormat, int locationField1, int locationField2, int offsetField, double offsetScale, bool forceSingleGeometry, QgsVectorDataProvider* memoryProvider, QProgressDialog* p ) { if ( !lineLayer || !eventLayer || !lineLayer->isValid() || !eventLayer->isValid() ) { return false; } //create line field / id map for line layer QMultiHash< QString, QgsFeature > lineLayerIdMap; //1:n possible (e.g. several linear reference geometries for one feature in the event layer) QgsFeatureIterator fit = lineLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() << lineField ) ); QgsFeature fet; while ( fit.nextFeature( fet ) ) { lineLayerIdMap.insert( fet.attribute( lineField ).toString(), fet ); } //create output datasource or attributes in memory provider QgsVectorFileWriter* fileWriter = nullptr; QgsFeatureList memoryProviderFeatures; if ( !memoryProvider ) { QgsWkbTypes::Type memoryProviderType = QgsWkbTypes::MultiLineString; if ( locationField2 == -1 ) { memoryProviderType = forceSingleGeometry ? QgsWkbTypes::Point : QgsWkbTypes::MultiPoint; } else { memoryProviderType = forceSingleGeometry ? QgsWkbTypes::LineString : QgsWkbTypes::MultiLineString; } fileWriter = new QgsVectorFileWriter( outputLayer, eventLayer->dataProvider()->encoding(), eventLayer->fields(), memoryProviderType, lineLayer->crs(), outputFormat ); } else { memoryProvider->addAttributes( eventLayer->fields().toList() ); } //iterate over eventLayer and write new features to output file or layer fit = eventLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) ); QgsGeometry lrsGeom; double measure1, measure2 = 0.0; int nEventFeatures = eventLayer->featureCount(); int featureCounter = 0; int nOutputFeatures = 0; //number of output features for the current event feature if ( p ) { p->setWindowModality( Qt::WindowModal ); p->setMinimum( 0 ); p->setMaximum( nEventFeatures ); p->show(); } while ( fit.nextFeature( fet ) ) { nOutputFeatures = 0; //update progress dialog if ( p ) { if ( p->wasCanceled() ) { break; } p->setValue( featureCounter ); ++featureCounter; } measure1 = fet.attribute( locationField1 ).toDouble(); if ( locationField2 != -1 ) { measure2 = fet.attribute( locationField2 ).toDouble(); if ( qgsDoubleNear(( measure2 - measure1 ), 0.0 ) ) { continue; } } QList<QgsFeature> featureIdList = lineLayerIdMap.values( fet.attribute( eventField ).toString() ); QList<QgsFeature>::iterator featureIdIt = featureIdList.begin(); for ( ; featureIdIt != featureIdList.end(); ++featureIdIt ) { if ( locationField2 == -1 ) { lrsGeom = locateAlongMeasure( measure1, featureIdIt->geometry() ); } else { lrsGeom = locateBetweenMeasures( measure1, measure2, featureIdIt->geometry() ); } if ( !lrsGeom.isEmpty() ) { ++nOutputFeatures; addEventLayerFeature( fet, lrsGeom, featureIdIt->geometry(), fileWriter, memoryProviderFeatures, offsetField, offsetScale, forceSingleGeometry ); } } if ( nOutputFeatures < 1 ) { unlocatedFeatureIds.insert( fet.id() ); } } if ( p ) { p->setValue( nEventFeatures ); } if ( memoryProvider ) { memoryProvider->addFeatures( memoryProviderFeatures ); } delete fileWriter; return true; }
void QgsSpatialQuery::execQuery( QgsFeatureIds &qsetIndexResult, QgsFeatureIds &qsetIndexInvalidTarget, int relation ) { bool ( QgsGeometry::* operation )( const QgsGeometry * ) const; switch ( relation ) { case Disjoint: operation = &QgsGeometry::disjoint; break; case Equals: operation = &QgsGeometry::equals; break; case Touches: operation = &QgsGeometry::touches; break; case Overlaps: operation = &QgsGeometry::overlaps; break; case Within: operation = &QgsGeometry::within; break; case Contains: operation = &QgsGeometry::contains; break; case Crosses: operation = &QgsGeometry::crosses; break; case Intersects: operation = &QgsGeometry::intersects; break; default: qWarning( "undefined operation" ); return; } // Transform referencer Target = Reference QgsGeometryCoordinateTransform *coordinateTransform = new QgsGeometryCoordinateTransform(); coordinateTransform->setCoordinateTransform( mLayerTarget, mLayerReference ); // Set function for populate result void ( QgsSpatialQuery::* funcPopulateIndexResult )( QgsFeatureIds&, QgsFeatureId, QgsGeometry *, bool ( QgsGeometry::* )( const QgsGeometry * ) const ); funcPopulateIndexResult = ( relation == Disjoint ) ? &QgsSpatialQuery::populateIndexResultDisjoint : &QgsSpatialQuery::populateIndexResult; QgsFeature featureTarget; QgsGeometry * geomTarget; int step = 1; while ( mReaderFeaturesTarget->nextFeature( featureTarget ) ) { mPb->step( step++ ); if ( ! hasValidGeometry( featureTarget ) ) { qsetIndexInvalidTarget.insert( featureTarget.id() ); continue; } geomTarget = featureTarget.geometry(); coordinateTransform->transform( geomTarget ); ( this->*funcPopulateIndexResult )( qsetIndexResult, featureTarget.id(), geomTarget, operation ); } delete coordinateTransform; } // QSet<int> QgsSpatialQuery::execQuery( QSet<int> & qsetIndexResult, int relation)
QVariantMap QgsClipAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !featureSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); std::unique_ptr< QgsFeatureSource > maskSource( parameterAsSource( parameters, QStringLiteral( "OVERLAY" ), context ) ); if ( !maskSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "OVERLAY" ) ) ); QString dest; QgsWkbTypes::GeometryType sinkType = QgsWkbTypes::geometryType( featureSource->wkbType() ); std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, featureSource->fields(), QgsWkbTypes::multiType( featureSource->wkbType() ), featureSource->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); // first build up a list of clip geometries QVector< QgsGeometry > clipGeoms; QgsFeatureIterator it = maskSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QList< int >() ).setDestinationCrs( featureSource->sourceCrs(), context.transformContext() ) ); QgsFeature f; while ( it.nextFeature( f ) ) { if ( f.hasGeometry() ) clipGeoms << f.geometry(); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); if ( clipGeoms.isEmpty() ) return outputs; // are we clipping against a single feature? if so, we can show finer progress reports bool singleClipFeature = false; QgsGeometry combinedClipGeom; if ( clipGeoms.length() > 1 ) { combinedClipGeom = QgsGeometry::unaryUnion( clipGeoms ); if ( combinedClipGeom.isEmpty() ) { throw QgsProcessingException( QObject::tr( "Could not create the combined clip geometry: %1" ).arg( combinedClipGeom.lastError() ) ); } singleClipFeature = false; } else { combinedClipGeom = clipGeoms.at( 0 ); singleClipFeature = true; } // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( combinedClipGeom.constGet() ) ); engine->prepareGeometry(); QgsFeatureIds testedFeatureIds; int i = -1; Q_FOREACH ( const QgsGeometry &clipGeom, clipGeoms ) { i++; if ( feedback->isCanceled() ) { break; } QgsFeatureIterator inputIt = featureSource->getFeatures( QgsFeatureRequest().setFilterRect( clipGeom.boundingBox() ) ); QgsFeatureList inputFeatures; QgsFeature f; while ( inputIt.nextFeature( f ) ) inputFeatures << f; if ( inputFeatures.isEmpty() ) continue; double step = 0; if ( singleClipFeature ) step = 100.0 / inputFeatures.length(); int current = 0; Q_FOREACH ( const QgsFeature &inputFeature, inputFeatures ) { if ( feedback->isCanceled() ) { break; } if ( !inputFeature.hasGeometry() ) continue; if ( testedFeatureIds.contains( inputFeature.id() ) ) { // don't retest a feature we have already checked continue; } testedFeatureIds.insert( inputFeature.id() ); if ( !engine->intersects( inputFeature.geometry().constGet() ) ) continue; QgsGeometry newGeometry; if ( !engine->contains( inputFeature.geometry().constGet() ) ) { QgsGeometry currentGeometry = inputFeature.geometry(); newGeometry = combinedClipGeom.intersection( currentGeometry ); if ( newGeometry.wkbType() == QgsWkbTypes::Unknown || QgsWkbTypes::flatType( newGeometry.wkbType() ) == QgsWkbTypes::GeometryCollection ) { QgsGeometry intCom = inputFeature.geometry().combine( newGeometry ); QgsGeometry intSym = inputFeature.geometry().symDifference( newGeometry ); newGeometry = intCom.difference( intSym ); } } else { // clip geometry totally contains feature geometry, so no need to perform intersection newGeometry = inputFeature.geometry(); } if ( !QgsOverlayUtils::sanitizeIntersectionResult( newGeometry, sinkType ) ) continue; QgsFeature outputFeature; outputFeature.setGeometry( newGeometry ); outputFeature.setAttributes( inputFeature.attributes() ); sink->addFeature( outputFeature, QgsFeatureSink::FastInsert ); if ( singleClipFeature ) feedback->setProgress( current * step ); } if ( !singleClipFeature ) { // coarse progress report for multiple clip geometries feedback->setProgress( 100.0 * static_cast< double >( i ) / clipGeoms.length() ); } }
void QgsAttributeTableDialog::updateRowSelection( int first, int last, int clickType ) { // clickType= 0:Single click, 1:Shift, 2:Ctrl, 3: Dragged click disconnect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) ); // Id must be mapped to table/view row QModelIndex index = mFilterModel->mapToSource( mFilterModel->index( first, 0 ) ); int fid = mModel->rowToId( index.row() ); bool wasSelected = mSelectedFeatures.contains( fid ); // new selection should be created if ( clickType == 0 ) // Single click { if ( mSelectedFeatures.size() == 1 && wasSelected ) // One item selected return; // Click over a selected item doesn't do anything mView->setCurrentIndex( mFilterModel->index( first, 0 ) ); mView->selectRow( first ); mSelectedFeatures.clear(); mSelectedFeatures.insert( fid ); mLayer->removeSelection(); mLayer->select( fid ); connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) ); return; } else if ( clickType == 1 ) // Shift { QgsFeatureIds newSelection; for ( int i = first; i <= last; ++i ) { if ( i >= first ) { // Id must be mapped to table/view row index = mFilterModel->mapToSource( mFilterModel->index( i, 0 ) ); fid = mModel->rowToId( index.row() ); } newSelection.insert( fid ); } // Remove items in mSelectedFeatures if they aren't in mNewSelection QgsFeatureIds::Iterator it = mSelectedFeatures.begin(); while ( it != mSelectedFeatures.end() ) { if ( !newSelection.contains( *it ) ) { it = mSelectedFeatures.erase( it ); } else { ++it; } } // Append the other fids in range first-last to mSelectedFeatures QgsFeatureIds::Iterator itNew = newSelection.begin(); for ( ; itNew != newSelection.end(); ++itNew ) { if ( !mSelectedFeatures.contains( *itNew ) ) { mSelectedFeatures.insert( *itNew ); } } } else if ( clickType == 2 ) // Ctrl { // existing selection should be updated if ( wasSelected ) mSelectedFeatures.remove( fid ); else mSelectedFeatures.insert( fid ); } else if ( clickType == 3 ) // Dragged click { QgsFeatureIds newSelection; for ( int i = first; i <= last; ++i ) { if ( i >= first ) { // Id must be mapped to table/view row index = mFilterModel->mapToSource( mFilterModel->index( i, 0 ) ); fid = mModel->rowToId( index.row() ); } newSelection.insert( fid ); } // Remove items in mSelectedFeatures if they aren't in mNewSelection QgsFeatureIds::Iterator it = mSelectedFeatures.begin(); while ( it != mSelectedFeatures.end() ) { if ( !newSelection.contains( *it ) ) { it = mSelectedFeatures.erase( it ); } else { ++it; } } // Append the other fids in range first-last to mSelectedFeatures QgsFeatureIds::Iterator itNew = newSelection.begin(); for ( ; itNew != newSelection.end(); ++itNew ) { if ( !mSelectedFeatures.contains( *itNew ) ) { mSelectedFeatures.insert( *itNew ); } } } updateSelection(); mLayer->setSelectedFeatures( mSelectedFeatures ); connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) ); }
void QgsLocationBasedAlgorithm::process( QgsFeatureSource *targetSource, QgsFeatureSource *intersectSource, const QList< int > &selectedPredicates, const std::function < void( const QgsFeature & ) > &handleFeatureFunction, bool onlyRequireTargetIds, QgsFeedback *feedback ) { // build a list of 'reversed' predicates, because in this function // we actually test the reverse of what the user wants (allowing us // to prepare geometries and optimise the algorithm) QList< Predicate > predicates; for ( int i : selectedPredicates ) { predicates << reversePredicate( static_cast< Predicate >( i ) ); } QgsFeatureIds disjointSet; if ( predicates.contains( Disjoint ) ) disjointSet = targetSource->allFeatureIds(); QgsFeatureIds foundSet; QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ).setDestinationCrs( targetSource->sourceCrs() ); QgsFeatureIterator fIt = intersectSource->getFeatures( request ); double step = intersectSource->featureCount() > 0 ? 100.0 / intersectSource->featureCount() : 1; int current = 0; QgsFeature f; std::unique_ptr< QgsGeometryEngine > engine; while ( fIt.nextFeature( f ) ) { if ( feedback->isCanceled() ) break; if ( !f.hasGeometry() ) continue; engine.reset(); QgsRectangle bbox = f.geometry().boundingBox(); request = QgsFeatureRequest().setFilterRect( bbox ); if ( onlyRequireTargetIds ) request.setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator testFeatureIt = targetSource->getFeatures( request ); QgsFeature testFeature; while ( testFeatureIt.nextFeature( testFeature ) ) { if ( feedback->isCanceled() ) break; if ( foundSet.contains( testFeature.id() ) ) { // already added this one, no need for further tests continue; } if ( predicates.count() == 1 && predicates.at( 0 ) == Disjoint && !disjointSet.contains( testFeature.id() ) ) { // calculating only the disjoint set, and we've already eliminated this feature so no need for further tests continue; } if ( !engine ) { engine.reset( QgsGeometry::createGeometryEngine( f.geometry().geometry() ) ); engine->prepareGeometry(); } for ( Predicate predicate : qgis::as_const( predicates ) ) { bool isMatch = false; switch ( predicate ) { case Intersects: isMatch = engine->intersects( testFeature.geometry().geometry() ); break; case Contains: isMatch = engine->contains( testFeature.geometry().geometry() ); break; case Disjoint: if ( engine->intersects( testFeature.geometry().geometry() ) ) { disjointSet.remove( testFeature.id() ); } break; case IsEqual: isMatch = engine->isEqual( testFeature.geometry().geometry() ); break; case Touches: isMatch = engine->touches( testFeature.geometry().geometry() ); break; case Overlaps: isMatch = engine->overlaps( testFeature.geometry().geometry() ); break; case Within: isMatch = engine->within( testFeature.geometry().geometry() ); break; case Crosses: isMatch = engine->crosses( testFeature.geometry().geometry() ); break; } if ( isMatch ) { foundSet.insert( testFeature.id() ); handleFeatureFunction( testFeature ); } } } current += 1; feedback->setProgress( current * step ); } if ( predicates.contains( Disjoint ) ) { disjointSet = disjointSet.subtract( foundSet ); QgsFeatureRequest disjointReq = QgsFeatureRequest().setFilterFids( disjointSet ); if ( onlyRequireTargetIds ) disjointReq.setSubsetOfAttributes( QgsAttributeList() ).setFlags( QgsFeatureRequest::NoGeometry ); QgsFeatureIterator disjointIt = targetSource->getFeatures( disjointReq ); QgsFeature f; while ( disjointIt.nextFeature( f ) ) { handleFeatureFunction( f ); } } }
QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, const QgsProject *project ) { QgsFeatureRequest request; QDomNodeList fidNodes = filterElem.elementsByTagName( QStringLiteral( "FeatureId" ) ); QDomNodeList goidNodes = filterElem.elementsByTagName( QStringLiteral( "GmlObjectId" ) ); if ( !fidNodes.isEmpty() ) { QgsFeatureIds fids; QDomElement fidElem; for ( int f = 0; f < fidNodes.size(); f++ ) { fidElem = fidNodes.at( f ).toElement(); if ( !fidElem.hasAttribute( QStringLiteral( "fid" ) ) ) { throw QgsRequestNotWellFormedException( "FeatureId element without fid attribute" ); } QString fid = fidElem.attribute( QStringLiteral( "fid" ) ); if ( fid.contains( QLatin1String( "." ) ) ) { if ( fid.section( QStringLiteral( "." ), 0, 0 ) != typeName ) continue; fid = fid.section( QStringLiteral( "." ), 1, 1 ); } fids.insert( fid.toInt() ); } if ( !fids.isEmpty() ) { request.setFilterFids( fids ); } else { throw QgsRequestNotWellFormedException( QStringLiteral( "No FeatureId element correctly parse against typeName '%1'" ).arg( typeName ) ); } request.setFlags( QgsFeatureRequest::NoFlags ); return request; } else if ( !goidNodes.isEmpty() ) { QgsFeatureIds fids; QDomElement goidElem; for ( int f = 0; f < goidNodes.size(); f++ ) { goidElem = goidNodes.at( f ).toElement(); if ( !goidElem.hasAttribute( QStringLiteral( "id" ) ) && !goidElem.hasAttribute( QStringLiteral( "gml:id" ) ) ) { throw QgsRequestNotWellFormedException( "GmlObjectId element without gml:id attribute" ); } QString fid = goidElem.attribute( QStringLiteral( "id" ) ); if ( fid.isEmpty() ) fid = goidElem.attribute( QStringLiteral( "gml:id" ) ); if ( fid.contains( QLatin1String( "." ) ) ) { if ( fid.section( QStringLiteral( "." ), 0, 0 ) != typeName ) continue; fid = fid.section( QStringLiteral( "." ), 1, 1 ); } fids.insert( fid.toInt() ); } if ( !fids.isEmpty() ) { request.setFilterFids( fids ); } else { throw QgsRequestNotWellFormedException( QStringLiteral( "No GmlObjectId element correctly parse against typeName '%1'" ).arg( typeName ) ); } request.setFlags( QgsFeatureRequest::NoFlags ); return request; } else if ( filterElem.firstChildElement().tagName() == QLatin1String( "BBOX" ) ) { QDomElement bboxElem = filterElem.firstChildElement(); QDomElement childElem = bboxElem.firstChildElement(); while ( !childElem.isNull() ) { if ( childElem.tagName() == QLatin1String( "Box" ) ) { request.setFilterRect( QgsOgcUtils::rectangleFromGMLBox( childElem ) ); } else if ( childElem.tagName() != QLatin1String( "PropertyName" ) ) { QgsGeometry geom = QgsOgcUtils::geometryFromGML( childElem ); request.setFilterRect( geom.boundingBox() ); } childElem = childElem.nextSiblingElement(); } request.setFlags( QgsFeatureRequest::ExactIntersect | QgsFeatureRequest::NoFlags ); return request; } // Apply BBOX through filterRect even inside an And to use spatial index else if ( filterElem.firstChildElement().tagName() == QLatin1String( "And" ) && !filterElem.firstChildElement().firstChildElement( QLatin1String( "BBOX" ) ).isNull() ) { QDomElement childElem = filterElem.firstChildElement().firstChildElement(); while ( !childElem.isNull() ) { QDomElement childFilterElement = filterElem.ownerDocument().createElement( QLatin1String( "Filter" ) ); childFilterElement.appendChild( childElem.cloneNode( true ) ); QgsFeatureRequest childRequest = parseFilterElement( typeName, childFilterElement ); if ( childElem.tagName() == QLatin1String( "BBOX" ) ) { if ( request.filterRect().isEmpty() ) { request.setFilterRect( childRequest.filterRect() ); } else { request.setFilterRect( request.filterRect().intersect( childRequest.filterRect() ) ); } } else { if ( !request.filterExpression() ) { request.setFilterExpression( childRequest.filterExpression()->expression() ); } else { QgsExpressionNode *opLeft = request.filterExpression()->rootNode()->clone(); QgsExpressionNode *opRight = childRequest.filterExpression()->rootNode()->clone(); std::unique_ptr<QgsExpressionNodeBinaryOperator> node = qgis::make_unique<QgsExpressionNodeBinaryOperator>( QgsExpressionNodeBinaryOperator::boAnd, opLeft, opRight ); QgsExpression expr( node->dump() ); request.setFilterExpression( expr ); } } childElem = childElem.nextSiblingElement(); } request.setFlags( QgsFeatureRequest::ExactIntersect | QgsFeatureRequest::NoFlags ); return request; } else { QgsVectorLayer *layer = nullptr; if ( project != nullptr ) { layer = layerByTypeName( project, typeName ); } std::shared_ptr<QgsExpression> filter( QgsOgcUtils::expressionFromOgcFilter( filterElem, layer ) ); if ( filter ) { if ( filter->hasParserError() ) { throw QgsRequestNotWellFormedException( filter->parserErrorString() ); } if ( filter->needsGeometry() ) { request.setFlags( QgsFeatureRequest::NoFlags ); } request.setFilterExpression( filter->expression() ); return request; } } return request; }
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool doDifference, bool singleSelect ) { if ( selectGeometry->type() != QGis::Polygon ) { return; } QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( vlayer == nullptr ) { return; } // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. //QgsGeometry selectGeomTrans( *selectGeometry ); //if ( canvas->mapSettings().hasCrsTransformEnabled() ) //{ // try // { // QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() ); // selectGeomTrans.transform( ct ); // } // catch ( QgsCsException &cse ) // { // Q_UNUSED( cse ); // // catch exception for 'invalid' point and leave existing selection unchanged // QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) ); // LOG_INFO( "CRS Exception\nSelection extends beyond layer's coordinate system" ); // return; // } //} QgsGeometry selectGeomTrans; try{ selectGeomTrans = toLayerCoordinates( canvas, selectGeometry, vlayer ); } catch ( QgsCsException & ) { return; } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) ); QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); QgsFeatureRendererV2* r = vlayer->rendererV2(); if ( r ) r->startRender( context, vlayer->pendingFields() ); QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes(), vlayer->pendingFields() ); else request.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeatureIds newSelectedFeatures; QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { #if (VERSION_INT >= 21601) context.expressionContext().setFeature( f ); //taken from QGIS 2.16.1 // make sure to only use features that are visible if ( r && !r->willRenderFeature( f, context ) ) #else if ( r && !r->willRenderFeature( f ) ) #endif continue; QgsGeometry* g = f.geometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g->distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } if ( r ) r->stopRender( context ); QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); if ( doDifference ) { QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds(); QgsFeatureIds selectedFeatures; QgsFeatureIds deselectedFeatures; QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd(); while ( i != newSelectedFeatures.constBegin() ) { --i; if ( layerSelectedFeatures.contains( *i ) ) { deselectedFeatures.insert( *i ); } else { selectedFeatures.insert( *i ); } } vlayer->modifySelection( selectedFeatures, deselectedFeatures ); } else { SelectFeatures( vlayer, newSelectedFeatures ); // vlayer->setSelectedFeatures( newSelectedFeatures ); } QApplication::restoreOverrideCursor(); }
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool doDifference, bool singleSelect ) { if ( selectGeometry->type() != QGis::Polygon ) { return; } QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( vlayer == NULL ) { return; } // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. QgsGeometry selectGeomTrans( *selectGeometry ); if ( canvas->mapSettings().hasCrsTransformEnabled() ) { try { QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() ); selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) ); QgisApp::instance()->messageBar()->pushMessage( QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system" ), QgsMessageBar::WARNING, QgisApp::instance()->messageTimeout() ); return; } } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) ); QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectGeomTrans.boundingBox() ).setFlags( QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeatureIds newSelectedFeatures; QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { QgsGeometry* g = f.geometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g->distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); if ( doDifference ) { QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds(); QgsFeatureIds selectedFeatures; QgsFeatureIds deselectedFeatures; QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd(); while ( i != newSelectedFeatures.constBegin() ) { --i; if ( layerSelectedFeatures.contains( *i ) ) { deselectedFeatures.insert( *i ); } else { selectedFeatures.insert( *i ); } } vlayer->modifySelection( selectedFeatures, deselectedFeatures ); } else { vlayer->setSelectedFeatures( newSelectedFeatures ); } QApplication::restoreOverrideCursor(); }
QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas *canvas, const QgsGeometry &selectGeometry, bool doContains, bool singleSelect ) { QgsFeatureIds newSelectedFeatures; if ( selectGeometry.type() != QgsWkbTypes::PolygonGeometry ) return newSelectedFeatures; QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( !vlayer ) return newSelectedFeatures; // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. QgsGeometry selectGeomTrans = selectGeometry; try { QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs(), QgsProject::instance() ); if ( !ct.isShortCircuited() && selectGeomTrans.type() == QgsWkbTypes::PolygonGeometry ) { // convert add more points to the edges of the rectangle // improve transformation result QgsPolygonXY poly( selectGeomTrans.asPolygon() ); if ( poly.size() == 1 && poly.at( 0 ).size() == 5 ) { const QgsPolylineXY &ringIn = poly.at( 0 ); QgsPolygonXY newpoly( 1 ); newpoly[0].resize( 41 ); QgsPolylineXY &ringOut = newpoly[0]; ringOut[ 0 ] = ringIn.at( 0 ); int i = 1; for ( int j = 1; j < 5; j++ ) { QgsVector v( ( ringIn.at( j ) - ringIn.at( j - 1 ) ) / 10.0 ); for ( int k = 0; k < 9; k++ ) { ringOut[ i ] = ringOut[ i - 1 ] + v; i++; } ringOut[ i++ ] = ringIn.at( j ); } selectGeomTrans = QgsGeometry::fromPolygonXY( newpoly ); } } selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsDebugMsg( QStringLiteral( "Caught CRS exception " ) ); QgisApp::instance()->messageBar()->pushMessage( QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system" ), Qgis::Warning, QgisApp::instance()->messageTimeout() ); return newSelectedFeatures; } QgsDebugMsgLevel( "Selection layer: " + vlayer->name(), 3 ); QgsDebugMsgLevel( "Selection polygon: " + selectGeomTrans.asWkt(), 3 ); QgsDebugMsgLevel( "doContains: " + QString( doContains ? "T" : "F" ), 3 ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); context.expressionContext() << QgsExpressionContextUtils::layerScope( vlayer ); std::unique_ptr< QgsFeatureRenderer > r; if ( vlayer->renderer() ) { r.reset( vlayer->renderer()->clone() ); r->startRender( context, vlayer->fields() ); } QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes( context ), vlayer->fields() ); else request.setNoAttributes(); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { context.expressionContext().setFeature( f ); // make sure to only use features that are visible if ( r && !r->willRenderFeature( f, context ) ) continue; QgsGeometry g = f.geometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g.distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } if ( r ) r->stopRender( context ); QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); return newSelectedFeatures; }