Skip to content

Commit cecdf33

Browse files
committedSep 8, 2017
Native c++ Extract by Location algorithm
1 parent f799d3a commit cecdf33

File tree

2 files changed

+158
-40
lines changed

2 files changed

+158
-40
lines changed
 

‎src/core/processing/qgsnativealgorithms.cpp‎

Lines changed: 111 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
7777
addAlgorithm( new QgsConvexHullAlgorithm() );
7878
addAlgorithm( new QgsPromoteToMultipartAlgorithm() );
7979
addAlgorithm( new QgsSelectByLocationAlgorithm() );
80+
addAlgorithm( new QgsExtractByLocationAlgorithm() );
8081
}
8182

8283
void QgsCentroidAlgorithm::initAlgorithm( const QVariantMap & )
@@ -1342,15 +1343,6 @@ QgsCollectAlgorithm *QgsCollectAlgorithm::createInstance() const
13421343

13431344
void QgsSelectByLocationAlgorithm::initAlgorithm( const QVariantMap & )
13441345
{
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-
13541346
QStringList methods = QStringList() << QObject::tr( "creating new selection" )
13551347
<< QObject::tr( "adding to current selection" )
13561348
<< QObject::tr( "select within current selection" )
@@ -1362,7 +1354,7 @@ void QgsSelectByLocationAlgorithm::initAlgorithm( const QVariantMap & )
13621354

13631355
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "PREDICATE" ),
13641356
QObject::tr( "Where the features are (geometric predicate)" ),
1365-
predicates, true, QVariant::fromValue( QList< int >() << 0 ) ) );
1357+
predicateOptionsList(), true, QVariant::fromValue( QList< int >() << 0 ) ) );
13661358

13671359
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
13681360
QObject::tr( "By comparing to the features from" ),
@@ -1391,6 +1383,26 @@ QVariantMap QgsSelectByLocationAlgorithm::processAlgorithm( const QVariantMap &p
13911383
QgsFeatureSource *intersectSource = parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context );
13921384
const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context );
13931385

1386+
QgsFeatureIds selectedIds;
1387+
auto addToSelection = [&]( const QgsFeature & feature )
1388+
{
1389+
selectedIds.insert( feature.id() );
1390+
};
1391+
process( selectLayer, intersectSource, selectedPredicates, addToSelection, true, feedback );
1392+
1393+
selectLayer->selectByIds( selectedIds, method );
1394+
QVariantMap results;
1395+
results.insert( QStringLiteral( "OUTPUT" ), parameters.value( QStringLiteral( "INPUT" ) ) );
1396+
return results;
1397+
}
1398+
1399+
void QgsLocationBasedAlgorithm::process( QgsFeatureSource *targetSource,
1400+
QgsFeatureSource *intersectSource,
1401+
const QList< int > &selectedPredicates,
1402+
std::function < void( const QgsFeature & ) > handleFeatureFunction,
1403+
bool onlyRequireTargetIds,
1404+
QgsFeedback *feedback )
1405+
{
13941406
// build a list of 'reversed' predicates, because in this function
13951407
// we actually test the reverse of what the user wants (allowing us
13961408
// to prepare geometries and optimise the algorithm)
@@ -1402,10 +1414,10 @@ QVariantMap QgsSelectByLocationAlgorithm::processAlgorithm( const QVariantMap &p
14021414

14031415
QgsFeatureIds disjointSet;
14041416
if ( predicates.contains( Disjoint ) )
1405-
disjointSet = selectLayer->allFeatureIds();
1417+
disjointSet = targetSource->allFeatureIds();
14061418

1407-
QgsFeatureIds selectedSet;
1408-
QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ).setDestinationCrs( selectLayer->crs() );
1419+
QgsFeatureIds foundSet;
1420+
QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ).setDestinationCrs( targetSource->sourceCrs() );
14091421
QgsFeatureIterator fIt = intersectSource->getFeatures( request );
14101422
double step = intersectSource->featureCount() > 0 ? 100.0 / intersectSource->featureCount() : 1;
14111423
int current = 0;
@@ -1422,15 +1434,18 @@ QVariantMap QgsSelectByLocationAlgorithm::processAlgorithm( const QVariantMap &p
14221434
engine->prepareGeometry();
14231435
QgsRectangle bbox = f.geometry().boundingBox();
14241436

1425-
request = QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() );
1426-
QgsFeatureIterator testFeatureIt = selectLayer->getFeatures( request );
1437+
request = QgsFeatureRequest().setFilterRect( bbox );
1438+
if ( onlyRequireTargetIds )
1439+
request.setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( QgsAttributeList() );
1440+
1441+
QgsFeatureIterator testFeatureIt = targetSource->getFeatures( request );
14271442
QgsFeature testFeature;
14281443
while ( testFeatureIt.nextFeature( testFeature ) )
14291444
{
14301445
if ( feedback->isCanceled() )
14311446
break;
14321447

1433-
if ( selectedSet.contains( testFeature.id() ) )
1448+
if ( foundSet.contains( testFeature.id() ) )
14341449
{
14351450
// already added this one, no need for further tests
14361451
continue;
@@ -1470,26 +1485,33 @@ QVariantMap QgsSelectByLocationAlgorithm::processAlgorithm( const QVariantMap &p
14701485
break;
14711486
}
14721487
if ( isMatch )
1473-
selectedSet.insert( testFeature.id() );
1488+
{
1489+
foundSet.insert( testFeature.id() );
1490+
handleFeatureFunction( testFeature );
1491+
}
14741492
}
14751493

14761494
}
14771495

1478-
feedback->setProgress( int( current * step ) );
1496+
feedback->setProgress( current * step );
14791497
}
14801498

14811499
if ( predicates.contains( Disjoint ) )
14821500
{
1483-
selectedSet = selectedSet.unite( disjointSet );
1501+
disjointSet = disjointSet.subtract( foundSet );
1502+
QgsFeatureRequest disjointReq = QgsFeatureRequest().setFilterFids( disjointSet );
1503+
if ( onlyRequireTargetIds )
1504+
disjointReq.setSubsetOfAttributes( QgsAttributeList() ).setFlags( QgsFeatureRequest::NoGeometry );
1505+
QgsFeatureIterator disjointIt = targetSource->getFeatures( disjointReq );
1506+
QgsFeature f;
1507+
while ( disjointIt.nextFeature( f ) )
1508+
{
1509+
handleFeatureFunction( f );
1510+
}
14841511
}
1485-
1486-
selectLayer->selectByIds( selectedSet, method );
1487-
QVariantMap results;
1488-
results.insert( QStringLiteral( "OUTPUT" ), parameters.value( QStringLiteral( "INPUT" ) ) );
1489-
return results;
14901512
}
14911513

1492-
QgsSelectByLocationAlgorithm::Predicate QgsSelectByLocationAlgorithm::reversePredicate( QgsSelectByLocationAlgorithm::Predicate predicate ) const
1514+
QgsLocationBasedAlgorithm::Predicate QgsLocationBasedAlgorithm::reversePredicate( QgsLocationBasedAlgorithm::Predicate predicate ) const
14931515
{
14941516
switch ( predicate )
14951517
{
@@ -1514,8 +1536,71 @@ QgsSelectByLocationAlgorithm::Predicate QgsSelectByLocationAlgorithm::reversePre
15141536
return Intersects;
15151537
}
15161538

1539+
QStringList QgsLocationBasedAlgorithm::predicateOptionsList() const
1540+
{
1541+
return QStringList() << QObject::tr( "intersects" )
1542+
<< QObject::tr( "contains" )
1543+
<< QObject::tr( "is disjoint" )
1544+
<< QObject::tr( "equals" )
1545+
<< QObject::tr( "touches" )
1546+
<< QObject::tr( "overlaps" )
1547+
<< QObject::tr( "within" )
1548+
<< QObject::tr( "crosses" );
1549+
}
1550+
15171551

15181552

1519-
///@endcond
1553+
void QgsExtractByLocationAlgorithm::initAlgorithm( const QVariantMap & )
1554+
{
1555+
addParameter( new QgsProcessingParameterVectorLayer( QStringLiteral( "INPUT" ), QObject::tr( "Extract features from" ),
1556+
QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
1557+
1558+
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "PREDICATE" ),
1559+
QObject::tr( "Where the features are (geometric predicate)" ),
1560+
predicateOptionsList(), true, QVariant::fromValue( QList< int >() << 0 ) ) );
15201561

1562+
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
1563+
QObject::tr( "By comparing to the features from" ),
1564+
QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
1565+
1566+
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extracted (location)" ) ) );
1567+
}
1568+
1569+
QString QgsExtractByLocationAlgorithm::shortHelpString() const
1570+
{
1571+
return QObject::tr( "This algorithm creates a new vector layer that only contains matching features from an "
1572+
"input layer. The criteria for adding features to the resulting layer is defined "
1573+
"based on the spatial relationship between each feature and the features in an additional layer." );
1574+
}
1575+
1576+
QgsExtractByLocationAlgorithm *QgsExtractByLocationAlgorithm::createInstance() const
1577+
{
1578+
return new QgsExtractByLocationAlgorithm();
1579+
}
1580+
1581+
QVariantMap QgsExtractByLocationAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
1582+
{
1583+
QgsFeatureSource *input = parameterAsSource( parameters, QStringLiteral( "INPUT" ), context );
1584+
QgsFeatureSource *intersectSource = parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context );
1585+
const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context );
1586+
QString dest;
1587+
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, input->fields(), input->wkbType(), input->sourceCrs() ) );
1588+
1589+
if ( !sink )
1590+
return QVariantMap();
1591+
1592+
auto addToSink = [&]( const QgsFeature & feature )
1593+
{
1594+
QgsFeature f = feature;
1595+
sink->addFeature( f, QgsFeatureSink::FastInsert );
1596+
};
1597+
process( input, intersectSource, selectedPredicates, addToSink, true, feedback );
1598+
1599+
QVariantMap results;
1600+
results.insert( QStringLiteral( "OUTPUT" ), dest );
1601+
return results;
1602+
}
1603+
1604+
1605+
///@endcond
15211606

‎src/core/processing/qgsnativealgorithms.h‎

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -483,10 +483,35 @@ class QgsConvexHullAlgorithm : public QgsProcessingFeatureBasedAlgorithm
483483
};
484484

485485

486+
/**
487+
* Base class for location based extraction/selection algorithms.
488+
*/
489+
class QgsLocationBasedAlgorithm : public QgsProcessingAlgorithm
490+
{
491+
492+
protected:
493+
494+
enum Predicate
495+
{
496+
Intersects,
497+
Contains,
498+
Disjoint,
499+
IsEqual,
500+
Touches,
501+
Overlaps,
502+
Within,
503+
Crosses,
504+
};
505+
506+
Predicate reversePredicate( Predicate predicate ) const;
507+
QStringList predicateOptionsList() const;
508+
void process( QgsFeatureSource *targetSource, QgsFeatureSource *intersectSource, const QList<int> &selectedPredicates, std::function<void ( const QgsFeature & )> handleFeatureFunction, bool onlyRequireTargetIds, QgsFeedback *feedback );
509+
};
510+
486511
/**
487512
* Native select by location algorithm
488513
*/
489-
class QgsSelectByLocationAlgorithm : public QgsProcessingAlgorithm
514+
class QgsSelectByLocationAlgorithm : public QgsLocationBasedAlgorithm
490515
{
491516

492517
public:
@@ -505,21 +530,29 @@ class QgsSelectByLocationAlgorithm : public QgsProcessingAlgorithm
505530
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
506531
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
507532

508-
private:
533+
};
509534

510-
enum Predicate
511-
{
512-
Intersects,
513-
Contains,
514-
Disjoint,
515-
IsEqual,
516-
Touches,
517-
Overlaps,
518-
Within,
519-
Crosses,
520-
};
535+
/**
536+
* Native extract by location algorithm
537+
*/
538+
class QgsExtractByLocationAlgorithm : public QgsLocationBasedAlgorithm
539+
{
521540

522-
Predicate reversePredicate( Predicate predicate ) const;
541+
public:
542+
543+
QgsExtractByLocationAlgorithm() = default;
544+
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
545+
QString name() const override { return QStringLiteral( "extractbylocation" ); }
546+
QString displayName() const override { return QObject::tr( "Extract by location" ); }
547+
virtual QStringList tags() const override { return QObject::tr( "extract,filter,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' ); }
548+
QString group() const override { return QObject::tr( "Vector selection" ); }
549+
QString shortHelpString() const override;
550+
QgsExtractByLocationAlgorithm *createInstance() const override SIP_FACTORY;
551+
552+
protected:
553+
554+
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
555+
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
523556

524557
};
525558

0 commit comments

Comments
 (0)
Please sign in to comment.