Skip to content

Commit 1aa76ac

Browse files
committedSep 8, 2017
Port Select by Location to c++
1 parent a9f4540 commit 1aa76ac

File tree

2 files changed

+219
-1
lines changed

2 files changed

+219
-1
lines changed
 

‎src/core/processing/qgsnativealgorithms.cpp

Lines changed: 178 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
7676
addAlgorithm( new QgsMinimumEnclosingCircleAlgorithm() );
7777
addAlgorithm( new QgsConvexHullAlgorithm() );
7878
addAlgorithm( new QgsPromoteToMultipartAlgorithm() );
79+
addAlgorithm( new QgsSelectByLocationAlgorithm() );
7980
}
8081

8182
void QgsCentroidAlgorithm::initAlgorithm( const QVariantMap & )
@@ -1337,8 +1338,184 @@ QgsCollectAlgorithm *QgsCollectAlgorithm::createInstance() const
13371338
return new QgsCollectAlgorithm();
13381339
}
13391340

1340-
///@endcond
13411341

13421342

1343+
void QgsSelectByLocationAlgorithm::initAlgorithm( const QVariantMap & )
1344+
{
1345+
QStringList predicates = QStringList() << QObject::tr( "intersects" )
1346+
<< QObject::tr( "contains" )
1347+
<< QObject::tr( "is disjoint" )
1348+
<< QObject::tr( "equals" )
1349+
<< QObject::tr( "touches" )
1350+
<< QObject::tr( "overlaps" )
1351+
<< QObject::tr( "within" )
1352+
<< QObject::tr( "crosses" );
1353+
1354+
QStringList methods = QStringList() << QObject::tr( "creating new selection" )
1355+
<< QObject::tr( "adding to current selection" )
1356+
<< QObject::tr( "select within current selection" )
1357+
<< QObject::tr( "removing from current selection" );
1358+
1359+
addParameter( new QgsProcessingParameterVectorLayer( QStringLiteral( "INPUT" ), QObject::tr( "Select features from" ),
1360+
QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
1361+
1362+
1363+
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "PREDICATE" ),
1364+
QObject::tr( "Where the features are (geometric predicate)" ),
1365+
predicates, true, QVariant::fromValue( QList< int >() << 0 ) ) );
1366+
1367+
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
1368+
QObject::tr( "By comparing to the features from" ),
1369+
QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
1370+
1371+
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ),
1372+
QObject::tr( "Modify current selection by" ),
1373+
methods, false, 0 ) );
1374+
}
1375+
1376+
QString QgsSelectByLocationAlgorithm::shortHelpString() const
1377+
{
1378+
return QObject::tr( "This algorithm creates a selection in a vector layer. The criteria for selecting "
1379+
"features is based on the spatial relationship between each feature and the features in an additional layer." );
1380+
}
1381+
1382+
QgsSelectByLocationAlgorithm *QgsSelectByLocationAlgorithm::createInstance() const
1383+
{
1384+
return new QgsSelectByLocationAlgorithm();
1385+
}
1386+
1387+
QVariantMap QgsSelectByLocationAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1388+
{
1389+
QgsVectorLayer *selectLayer = parameterAsVectorLayer( parameters, QStringLiteral( "INPUT" ), context );
1390+
QgsVectorLayer::SelectBehavior method = static_cast< QgsVectorLayer::SelectBehavior >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
1391+
QgsFeatureSource *intersectSource = parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context );
1392+
const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context );
1393+
1394+
// build a list of 'reversed' predicates, because in this function
1395+
// we actually test the reverse of what the user wants (allowing us
1396+
// to prepare geometries and optimise the algorithm)
1397+
QList< Predicate > predicates;
1398+
for ( int i : selectedPredicates )
1399+
{
1400+
predicates << reversePredicate( static_cast< Predicate >( i ) );
1401+
}
1402+
1403+
QgsFeatureIds disjointSet;
1404+
if ( predicates.contains( Disjoint ) )
1405+
disjointSet = selectLayer->allFeatureIds();
1406+
1407+
QgsFeatureIds selectedSet;
1408+
QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ).setDestinationCrs( selectLayer->crs() );
1409+
QgsFeatureIterator fIt = intersectSource->getFeatures( request );
1410+
double step = intersectSource->featureCount() > 0 ? 100.0 / intersectSource->featureCount() : 1;
1411+
int current = 0;
1412+
QgsFeature f;
1413+
while ( fIt.nextFeature( f ) )
1414+
{
1415+
if ( feedback->isCanceled() )
1416+
break;
1417+
1418+
if ( !f.hasGeometry() )
1419+
continue;
1420+
1421+
std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( f.geometry().geometry() ) );
1422+
engine->prepareGeometry();
1423+
QgsRectangle bbox = f.geometry().boundingBox();
1424+
1425+
request = QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() );
1426+
QgsFeatureIterator testFeatureIt = selectLayer->getFeatures( request );
1427+
QgsFeature testFeature;
1428+
while ( testFeatureIt.nextFeature( testFeature ) )
1429+
{
1430+
if ( feedback->isCanceled() )
1431+
break;
1432+
1433+
if ( selectedSet.contains( testFeature.id() ) )
1434+
{
1435+
// already added this one, no need for further tests
1436+
continue;
1437+
}
1438+
1439+
for ( Predicate predicate : qgsAsConst( predicates ) )
1440+
{
1441+
bool isMatch = false;
1442+
switch ( predicate )
1443+
{
1444+
case Intersects:
1445+
isMatch = engine->intersects( testFeature.geometry().geometry() );
1446+
break;
1447+
case Contains:
1448+
isMatch = engine->contains( testFeature.geometry().geometry() );
1449+
break;
1450+
case Disjoint:
1451+
if ( engine->intersects( testFeature.geometry().geometry() ) )
1452+
{
1453+
disjointSet.remove( testFeature.id() );
1454+
}
1455+
break;
1456+
case IsEqual:
1457+
isMatch = engine->isEqual( testFeature.geometry().geometry() );
1458+
break;
1459+
case Touches:
1460+
isMatch = engine->touches( testFeature.geometry().geometry() );
1461+
break;
1462+
case Overlaps:
1463+
isMatch = engine->overlaps( testFeature.geometry().geometry() );
1464+
break;
1465+
case Within:
1466+
isMatch = engine->within( testFeature.geometry().geometry() );
1467+
break;
1468+
case Crosses:
1469+
isMatch = engine->crosses( testFeature.geometry().geometry() );
1470+
break;
1471+
}
1472+
if ( isMatch )
1473+
selectedSet.insert( testFeature.id() );
1474+
}
1475+
1476+
}
1477+
1478+
feedback->setProgress( int( current * step ) );
1479+
}
1480+
1481+
if ( predicates.contains( Disjoint ) )
1482+
{
1483+
selectedSet = selectedSet.unite( disjointSet );
1484+
}
1485+
1486+
selectLayer->selectByIds( selectedSet, method );
1487+
QVariantMap results;
1488+
results.insert( QStringLiteral( "OUTPUT" ), parameters.value( QStringLiteral( "INPUT" ) ) );
1489+
return results;
1490+
}
1491+
1492+
QgsSelectByLocationAlgorithm::Predicate QgsSelectByLocationAlgorithm::reversePredicate( QgsSelectByLocationAlgorithm::Predicate predicate ) const
1493+
{
1494+
switch ( predicate )
1495+
{
1496+
case Intersects:
1497+
return Intersects;
1498+
case Contains:
1499+
return Within;
1500+
case Disjoint:
1501+
return Disjoint;
1502+
case IsEqual:
1503+
return IsEqual;
1504+
case Touches:
1505+
return Touches;
1506+
case Overlaps:
1507+
return Overlaps;
1508+
case Within:
1509+
return Contains;
1510+
case Crosses:
1511+
return Crosses;
1512+
}
1513+
// no warnings
1514+
return Intersects;
1515+
}
1516+
1517+
1518+
1519+
///@endcond
13431520

13441521

‎src/core/processing/qgsnativealgorithms.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,47 @@ class QgsConvexHullAlgorithm : public QgsProcessingFeatureBasedAlgorithm
482482

483483
};
484484

485+
486+
/**
487+
* Native select by location algorithm
488+
*/
489+
class QgsSelectByLocationAlgorithm : public QgsProcessingAlgorithm
490+
{
491+
492+
public:
493+
494+
QgsSelectByLocationAlgorithm() = default;
495+
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
496+
QString name() const override { return QStringLiteral( "selectbylocation" ); }
497+
QString displayName() const override { return QObject::tr( "Select by location" ); }
498+
virtual QStringList tags() const override { return QObject::tr( "select,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' ); }
499+
QString group() const override { return QObject::tr( "Vector selection" ); }
500+
QString shortHelpString() const override;
501+
QgsSelectByLocationAlgorithm *createInstance() const override SIP_FACTORY;
502+
503+
protected:
504+
505+
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
506+
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
507+
508+
private:
509+
510+
enum Predicate
511+
{
512+
Intersects,
513+
Contains,
514+
Disjoint,
515+
IsEqual,
516+
Touches,
517+
Overlaps,
518+
Within,
519+
Crosses,
520+
};
521+
522+
Predicate reversePredicate( Predicate predicate ) const;
523+
524+
};
525+
485526
///@endcond PRIVATE
486527

487528
#endif // QGSNATIVEALGORITHMS_H

0 commit comments

Comments
 (0)
Please sign in to comment.