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->mapRenderer()->hasCrsTransformEnabled() ) { try { QgsCoordinateTransform ct( canvas->mapRenderer()->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( canvas, QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system." ) ); 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" ) ); vlayer->select( QgsAttributeList(), selectGeomTrans.boundingBox(), true, true ); QgsFeatureIds newSelectedFeatures; QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( vlayer->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() ) ); QgsFeatureIds layerSelectedFeatures; if ( doDifference ) { layerSelectedFeatures = vlayer->selectedFeaturesIds(); QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd(); while ( i != newSelectedFeatures.constBegin() ) { --i; if ( layerSelectedFeatures.contains( *i ) ) { layerSelectedFeatures.remove( *i ); } else { layerSelectedFeatures.insert( *i ); } } } else { layerSelectedFeatures = newSelectedFeatures; } vlayer->setSelectedFeatures( layerSelectedFeatures ); QApplication::restoreOverrideCursor(); }
void QgsMergeAttributesDialog::mRemoveFeatureFromSelectionButton_clicked() { if ( !mVectorLayer ) { return; } //find out feature id of selected row QList<QTableWidgetItem *> selectionList = mTableWidget->selectedItems(); if ( selectionList.isEmpty() ) { return; } //assume all selected items to be in the same row QTableWidgetItem *selectedItem = selectionList[0]; int selectedRow = selectedItem->row(); QTableWidgetItem *selectedHeaderItem = mTableWidget->verticalHeaderItem( selectedRow ); if ( !selectedHeaderItem ) { return; } bool conversionSuccess; QgsFeatureId featureId = selectedHeaderItem->text().toLongLong( &conversionSuccess ); if ( !conversionSuccess ) { selectedRowChanged(); return; } mTableWidget->removeRow( selectedRow ); selectedRowChanged(); //remove feature from the vector layer selection QgsFeatureIds selectedIds = mVectorLayer->selectedFeatureIds(); selectedIds.remove( featureId ); mVectorLayer->selectByIds( selectedIds ); mMapCanvas->repaint(); //remove feature option from the combo box (without altering the current merge values) for ( int i = 0; i < mTableWidget->columnCount(); ++i ) { QComboBox *currentComboBox = qobject_cast<QComboBox *>( mTableWidget->cellWidget( 0, i ) ); if ( !currentComboBox ) continue; currentComboBox->blockSignals( true ); currentComboBox->removeItem( currentComboBox->findData( QStringLiteral( "f%1" ).arg( FID_TO_STRING( featureId ) ) ) ); currentComboBox->blockSignals( false ); } //finally remove the feature from mFeatureList for ( QgsFeatureList::iterator f_it = mFeatureList.begin(); f_it != mFeatureList.end(); ++f_it ) { if ( f_it->id() == featureId ) { mFeatureList.erase( f_it ); break; } } }
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 ); } } }