Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsalgorithmextractbylocation.cpp
3 : : ---------------------
4 : : begin : April 2017
5 : : copyright : (C) 2017 by Nyall Dawson
6 : : email : nyall dot dawson at gmail dot com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgsalgorithmextractbylocation.h"
19 : : #include "qgsgeometryengine.h"
20 : : #include "qgsvectorlayer.h"
21 : :
22 : : ///@cond PRIVATE
23 : :
24 : 0 : void QgsLocationBasedAlgorithm::addPredicateParameter()
25 : : {
26 : 0 : std::unique_ptr< QgsProcessingParameterEnum > predicateParam( new QgsProcessingParameterEnum( QStringLiteral( "PREDICATE" ),
27 : 0 : QObject::tr( "Where the features (geometric predicate)" ),
28 : 0 : predicateOptionsList(), true, QVariant::fromValue( QList< int >() << 0 ) ) );
29 : :
30 : 0 : QVariantMap predicateMetadata;
31 : 0 : QVariantMap widgetMetadata;
32 : 0 : widgetMetadata.insert( QStringLiteral( "useCheckBoxes" ), true );
33 : 0 : widgetMetadata.insert( QStringLiteral( "columns" ), 2 );
34 : 0 : predicateMetadata.insert( QStringLiteral( "widget_wrapper" ), widgetMetadata );
35 : 0 : predicateParam->setMetadata( predicateMetadata );
36 : :
37 : 0 : addParameter( predicateParam.release() );
38 : 0 : }
39 : :
40 : 0 : QgsLocationBasedAlgorithm::Predicate QgsLocationBasedAlgorithm::reversePredicate( QgsLocationBasedAlgorithm::Predicate predicate ) const
41 : : {
42 : 0 : switch ( predicate )
43 : : {
44 : : case Intersects:
45 : 0 : return Intersects;
46 : : case Contains:
47 : 0 : return Within;
48 : : case Disjoint:
49 : 0 : return Disjoint;
50 : : case IsEqual:
51 : 0 : return IsEqual;
52 : : case Touches:
53 : 0 : return Touches;
54 : : case Overlaps:
55 : 0 : return Overlaps;
56 : : case Within:
57 : 0 : return Contains;
58 : : case Crosses:
59 : 0 : return Crosses;
60 : : }
61 : : // no warnings
62 : 0 : return Intersects;
63 : 0 : }
64 : :
65 : 0 : QStringList QgsLocationBasedAlgorithm::predicateOptionsList() const
66 : : {
67 : 0 : return QStringList() << QObject::tr( "intersect" )
68 : 0 : << QObject::tr( "contain" )
69 : 0 : << QObject::tr( "disjoint" )
70 : 0 : << QObject::tr( "equal" )
71 : 0 : << QObject::tr( "touch" )
72 : 0 : << QObject::tr( "overlap" )
73 : 0 : << QObject::tr( "are within" )
74 : 0 : << QObject::tr( "cross" );
75 : 0 : }
76 : :
77 : 0 : void QgsLocationBasedAlgorithm::process( const QgsProcessingContext &context, QgsFeatureSource *targetSource,
78 : : QgsFeatureSource *intersectSource,
79 : : const QList< int > &selectedPredicates,
80 : : const std::function < void( const QgsFeature & ) > &handleFeatureFunction,
81 : : bool onlyRequireTargetIds,
82 : : QgsProcessingFeedback *feedback )
83 : : {
84 : :
85 : 0 : if ( targetSource->featureCount() > 0 && intersectSource->featureCount() > 0
86 : 0 : && targetSource->featureCount() < intersectSource->featureCount() )
87 : : {
88 : : // joining FEWER features to a layer with MORE features. So we iterate over the FEW features and find matches from the MANY
89 : 0 : processByIteratingOverTargetSource( context, targetSource, intersectSource,
90 : 0 : selectedPredicates, handleFeatureFunction,
91 : 0 : onlyRequireTargetIds, feedback );
92 : 0 : }
93 : : else
94 : : {
95 : : // default -- iterate over the intersect source and match back to the target source. We do this on the assumption that the most common
96 : : // use case is joining a points layer to a polygon layer (e.g. findings points within a polygon), so by iterating
97 : : // over the polygons we can take advantage of prepared geometries for the spatial relationship test.
98 : :
99 : : // TODO - consider using more heuristics to determine whether it's always best to iterate over the intersect
100 : : // source.
101 : 0 : processByIteratingOverIntersectSource( context, targetSource, intersectSource,
102 : 0 : selectedPredicates, handleFeatureFunction,
103 : 0 : onlyRequireTargetIds, feedback );
104 : : }
105 : 0 : }
106 : :
107 : 0 : void QgsLocationBasedAlgorithm::processByIteratingOverTargetSource( const QgsProcessingContext &context, QgsFeatureSource *targetSource,
108 : : QgsFeatureSource *intersectSource,
109 : : const QList< int > &selectedPredicates,
110 : : const std::function < void( const QgsFeature & ) > &handleFeatureFunction,
111 : : bool onlyRequireTargetIds,
112 : : QgsProcessingFeedback *feedback )
113 : : {
114 : 0 : if ( intersectSource->hasSpatialIndex() == QgsFeatureSource::SpatialIndexNotPresent )
115 : 0 : feedback->pushWarning( QObject::tr( "No spatial index exists for intersect layer, performance will be severely degraded" ) );
116 : :
117 : 0 : QgsFeatureIds foundSet;
118 : 0 : QgsFeatureRequest request = QgsFeatureRequest();
119 : 0 : if ( onlyRequireTargetIds )
120 : 0 : request.setNoAttributes();
121 : :
122 : 0 : QgsFeatureIterator fIt = targetSource->getFeatures( request );
123 : 0 : double step = targetSource->featureCount() > 0 ? 100.0 / targetSource->featureCount() : 1;
124 : 0 : int current = 0;
125 : 0 : QgsFeature f;
126 : 0 : std::unique_ptr< QgsGeometryEngine > engine;
127 : 0 : while ( fIt.nextFeature( f ) )
128 : : {
129 : 0 : if ( feedback->isCanceled() )
130 : 0 : break;
131 : :
132 : 0 : if ( !f.hasGeometry() )
133 : 0 : continue;
134 : :
135 : 0 : engine.reset();
136 : :
137 : 0 : QgsRectangle bbox = f.geometry().boundingBox();
138 : 0 : request = QgsFeatureRequest().setFilterRect( bbox ).setNoAttributes().setDestinationCrs( targetSource->sourceCrs(), context.transformContext() );
139 : :
140 : 0 : QgsFeatureIterator testFeatureIt = intersectSource->getFeatures( request );
141 : 0 : QgsFeature testFeature;
142 : 0 : bool isMatch = false;
143 : 0 : bool isDisjoint = true;
144 : 0 : while ( testFeatureIt.nextFeature( testFeature ) )
145 : : {
146 : 0 : if ( feedback->isCanceled() )
147 : 0 : break;
148 : :
149 : 0 : if ( !engine )
150 : : {
151 : 0 : engine.reset( QgsGeometry::createGeometryEngine( f.geometry().constGet() ) );
152 : 0 : engine->prepareGeometry();
153 : 0 : }
154 : :
155 : 0 : for ( int predicate : selectedPredicates )
156 : : {
157 : 0 : switch ( static_cast< Predicate>( predicate ) )
158 : : {
159 : : case Intersects:
160 : 0 : isMatch = engine->intersects( testFeature.geometry().constGet() );
161 : 0 : break;
162 : : case Contains:
163 : 0 : isMatch = engine->contains( testFeature.geometry().constGet() );
164 : 0 : break;
165 : : case Disjoint:
166 : 0 : if ( engine->intersects( testFeature.geometry().constGet() ) )
167 : : {
168 : 0 : isDisjoint = false;
169 : 0 : }
170 : 0 : break;
171 : : case IsEqual:
172 : 0 : isMatch = engine->isEqual( testFeature.geometry().constGet() );
173 : 0 : break;
174 : : case Touches:
175 : 0 : isMatch = engine->touches( testFeature.geometry().constGet() );
176 : 0 : break;
177 : : case Overlaps:
178 : 0 : isMatch = engine->overlaps( testFeature.geometry().constGet() );
179 : 0 : break;
180 : : case Within:
181 : 0 : isMatch = engine->within( testFeature.geometry().constGet() );
182 : 0 : break;
183 : : case Crosses:
184 : 0 : isMatch = engine->crosses( testFeature.geometry().constGet() );
185 : 0 : break;
186 : : }
187 : :
188 : 0 : if ( isMatch )
189 : 0 : break;
190 : : }
191 : :
192 : 0 : if ( isMatch )
193 : : {
194 : 0 : foundSet.insert( f.id() );
195 : 0 : handleFeatureFunction( f );
196 : 0 : break;
197 : : }
198 : : }
199 : 0 : if ( isDisjoint && selectedPredicates.contains( Disjoint ) )
200 : : {
201 : 0 : foundSet.insert( f.id() );
202 : 0 : handleFeatureFunction( f );
203 : 0 : }
204 : :
205 : 0 : current += 1;
206 : 0 : feedback->setProgress( current * step );
207 : 0 : }
208 : 0 : }
209 : :
210 : 0 : void QgsLocationBasedAlgorithm::processByIteratingOverIntersectSource( const QgsProcessingContext &context, QgsFeatureSource *targetSource,
211 : : QgsFeatureSource *intersectSource,
212 : : const QList< int > &selectedPredicates,
213 : : const std::function < void( const QgsFeature & ) > &handleFeatureFunction,
214 : : bool onlyRequireTargetIds,
215 : : QgsProcessingFeedback *feedback )
216 : : {
217 : 0 : if ( targetSource->hasSpatialIndex() == QgsFeatureSource::SpatialIndexNotPresent )
218 : 0 : feedback->pushWarning( QObject::tr( "No spatial index exists for input layer, performance will be severely degraded" ) );
219 : :
220 : : // build a list of 'reversed' predicates, because in this function
221 : : // we actually test the reverse of what the user wants (allowing us
222 : : // to prepare geometries and optimise the algorithm)
223 : 0 : QList< Predicate > predicates;
224 : 0 : predicates.reserve( selectedPredicates.count() );
225 : 0 : for ( int i : selectedPredicates )
226 : : {
227 : 0 : predicates << reversePredicate( static_cast< Predicate >( i ) );
228 : : }
229 : :
230 : 0 : QgsFeatureIds disjointSet;
231 : 0 : if ( predicates.contains( Disjoint ) )
232 : 0 : disjointSet = targetSource->allFeatureIds();
233 : :
234 : 0 : QgsFeatureIds foundSet;
235 : 0 : QgsFeatureRequest request = QgsFeatureRequest().setNoAttributes().setDestinationCrs( targetSource->sourceCrs(), context.transformContext() );
236 : 0 : QgsFeatureIterator fIt = intersectSource->getFeatures( request );
237 : 0 : double step = intersectSource->featureCount() > 0 ? 100.0 / intersectSource->featureCount() : 1;
238 : 0 : int current = 0;
239 : 0 : QgsFeature f;
240 : 0 : std::unique_ptr< QgsGeometryEngine > engine;
241 : 0 : while ( fIt.nextFeature( f ) )
242 : : {
243 : 0 : if ( feedback->isCanceled() )
244 : 0 : break;
245 : :
246 : 0 : if ( !f.hasGeometry() )
247 : 0 : continue;
248 : :
249 : 0 : engine.reset();
250 : :
251 : 0 : QgsRectangle bbox = f.geometry().boundingBox();
252 : 0 : request = QgsFeatureRequest().setFilterRect( bbox );
253 : 0 : if ( onlyRequireTargetIds )
254 : 0 : request.setNoAttributes();
255 : :
256 : 0 : QgsFeatureIterator testFeatureIt = targetSource->getFeatures( request );
257 : 0 : QgsFeature testFeature;
258 : 0 : while ( testFeatureIt.nextFeature( testFeature ) )
259 : : {
260 : 0 : if ( feedback->isCanceled() )
261 : 0 : break;
262 : :
263 : 0 : if ( foundSet.contains( testFeature.id() ) )
264 : : {
265 : : // already added this one, no need for further tests
266 : 0 : continue;
267 : : }
268 : 0 : if ( predicates.count() == 1 && predicates.at( 0 ) == Disjoint && !disjointSet.contains( testFeature.id() ) )
269 : : {
270 : : // calculating only the disjoint set, and we've already eliminated this feature so no need for further tests
271 : 0 : continue;
272 : : }
273 : :
274 : 0 : if ( !engine )
275 : : {
276 : 0 : engine.reset( QgsGeometry::createGeometryEngine( f.geometry().constGet() ) );
277 : 0 : engine->prepareGeometry();
278 : 0 : }
279 : :
280 : 0 : bool isMatch = false;
281 : :
282 : 0 : for ( Predicate predicate : std::as_const( predicates ) )
283 : : {
284 : 0 : switch ( predicate )
285 : : {
286 : : case Intersects:
287 : 0 : isMatch = engine->intersects( testFeature.geometry().constGet() );
288 : 0 : break;
289 : : case Contains:
290 : 0 : isMatch = engine->contains( testFeature.geometry().constGet() );
291 : 0 : break;
292 : : case Disjoint:
293 : 0 : if ( engine->intersects( testFeature.geometry().constGet() ) )
294 : : {
295 : 0 : disjointSet.remove( testFeature.id() );
296 : 0 : }
297 : 0 : break;
298 : : case IsEqual:
299 : 0 : isMatch = engine->isEqual( testFeature.geometry().constGet() );
300 : 0 : break;
301 : : case Touches:
302 : 0 : isMatch = engine->touches( testFeature.geometry().constGet() );
303 : 0 : break;
304 : : case Overlaps:
305 : 0 : isMatch = engine->overlaps( testFeature.geometry().constGet() );
306 : 0 : break;
307 : : case Within:
308 : 0 : isMatch = engine->within( testFeature.geometry().constGet() );
309 : 0 : break;
310 : : case Crosses:
311 : 0 : isMatch = engine->crosses( testFeature.geometry().constGet() );
312 : 0 : break;
313 : : }
314 : 0 : if ( isMatch )
315 : 0 : break;
316 : : }
317 : :
318 : 0 : if ( isMatch )
319 : : {
320 : 0 : foundSet.insert( testFeature.id() );
321 : 0 : handleFeatureFunction( testFeature );
322 : 0 : }
323 : :
324 : : }
325 : :
326 : 0 : current += 1;
327 : 0 : feedback->setProgress( current * step );
328 : 0 : }
329 : :
330 : 0 : if ( predicates.contains( Disjoint ) )
331 : : {
332 : 0 : disjointSet = disjointSet.subtract( foundSet );
333 : 0 : QgsFeatureRequest disjointReq = QgsFeatureRequest().setFilterFids( disjointSet );
334 : 0 : if ( onlyRequireTargetIds )
335 : 0 : disjointReq.setNoAttributes().setFlags( QgsFeatureRequest::NoGeometry );
336 : 0 : QgsFeatureIterator disjointIt = targetSource->getFeatures( disjointReq );
337 : 0 : QgsFeature f;
338 : 0 : while ( disjointIt.nextFeature( f ) )
339 : : {
340 : 0 : handleFeatureFunction( f );
341 : : }
342 : 0 : }
343 : 0 : }
344 : :
345 : :
346 : : //
347 : : // QgsSelectByLocationAlgorithm
348 : : //
349 : :
350 : 0 : void QgsSelectByLocationAlgorithm::initAlgorithm( const QVariantMap & )
351 : : {
352 : 0 : QStringList methods = QStringList() << QObject::tr( "creating new selection" )
353 : 0 : << QObject::tr( "adding to current selection" )
354 : 0 : << QObject::tr( "selecting within current selection" )
355 : 0 : << QObject::tr( "removing from current selection" );
356 : :
357 : 0 : addParameter( new QgsProcessingParameterVectorLayer( QStringLiteral( "INPUT" ), QObject::tr( "Select features from" ),
358 : 0 : QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
359 : 0 : addPredicateParameter();
360 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
361 : 0 : QObject::tr( "By comparing to the features from" ),
362 : 0 : QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
363 : :
364 : 0 : addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ),
365 : 0 : QObject::tr( "Modify current selection by" ),
366 : 0 : methods, false, 0 ) );
367 : 0 : }
368 : :
369 : 0 : QString QgsSelectByLocationAlgorithm::name() const
370 : : {
371 : 0 : return QStringLiteral( "selectbylocation" );
372 : : }
373 : :
374 : 0 : QgsProcessingAlgorithm::Flags QgsSelectByLocationAlgorithm::flags() const
375 : : {
376 : 0 : return QgsProcessingAlgorithm::flags() | QgsProcessingAlgorithm::FlagNoThreading | QgsProcessingAlgorithm::FlagNotAvailableInStandaloneTool;
377 : : }
378 : :
379 : 0 : QString QgsSelectByLocationAlgorithm::displayName() const
380 : : {
381 : 0 : return QObject::tr( "Select by location" );
382 : : }
383 : :
384 : 0 : QStringList QgsSelectByLocationAlgorithm::tags() const
385 : : {
386 : 0 : return QObject::tr( "select,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
387 : 0 : }
388 : :
389 : 0 : QString QgsSelectByLocationAlgorithm::group() const
390 : : {
391 : 0 : return QObject::tr( "Vector selection" );
392 : : }
393 : :
394 : 0 : QString QgsSelectByLocationAlgorithm::groupId() const
395 : : {
396 : 0 : return QStringLiteral( "vectorselection" );
397 : : }
398 : :
399 : 0 : QString QgsSelectByLocationAlgorithm::shortHelpString() const
400 : : {
401 : 0 : return QObject::tr( "This algorithm creates a selection in a vector layer. The criteria for selecting "
402 : : "features is based on the spatial relationship between each feature and the features in an additional layer." );
403 : : }
404 : :
405 : 0 : QgsSelectByLocationAlgorithm *QgsSelectByLocationAlgorithm::createInstance() const
406 : : {
407 : 0 : return new QgsSelectByLocationAlgorithm();
408 : : }
409 : :
410 : 0 : QVariantMap QgsSelectByLocationAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
411 : : {
412 : 0 : QgsVectorLayer *selectLayer = parameterAsVectorLayer( parameters, QStringLiteral( "INPUT" ), context );
413 : 0 : if ( !selectLayer )
414 : 0 : throw QgsProcessingException( QObject::tr( "Could not load source layer for INPUT" ) );
415 : :
416 : 0 : QgsVectorLayer::SelectBehavior method = static_cast< QgsVectorLayer::SelectBehavior >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
417 : 0 : std::unique_ptr< QgsFeatureSource > intersectSource( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) );
418 : 0 : if ( !intersectSource )
419 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INTERSECT" ) ) );
420 : :
421 : 0 : const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context );
422 : :
423 : 0 : QgsFeatureIds selectedIds;
424 : 0 : auto addToSelection = [&]( const QgsFeature & feature )
425 : : {
426 : 0 : selectedIds.insert( feature.id() );
427 : 0 : };
428 : 0 : process( context, selectLayer, intersectSource.get(), selectedPredicates, addToSelection, true, feedback );
429 : :
430 : 0 : selectLayer->selectByIds( selectedIds, method );
431 : 0 : QVariantMap results;
432 : 0 : results.insert( QStringLiteral( "OUTPUT" ), parameters.value( QStringLiteral( "INPUT" ) ) );
433 : 0 : return results;
434 : 0 : }
435 : :
436 : :
437 : : //
438 : : // QgsExtractByLocationAlgorithm
439 : : //
440 : :
441 : 0 : void QgsExtractByLocationAlgorithm::initAlgorithm( const QVariantMap & )
442 : : {
443 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
444 : 0 : QObject::tr( "Extract features from" ),
445 : 0 : QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
446 : 0 : addPredicateParameter();
447 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
448 : 0 : QObject::tr( "By comparing to the features from" ),
449 : 0 : QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
450 : :
451 : 0 : addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extracted (location)" ) ) );
452 : 0 : }
453 : :
454 : 0 : QString QgsExtractByLocationAlgorithm::name() const
455 : : {
456 : 0 : return QStringLiteral( "extractbylocation" );
457 : : }
458 : :
459 : 0 : QString QgsExtractByLocationAlgorithm::displayName() const
460 : : {
461 : 0 : return QObject::tr( "Extract by location" );
462 : : }
463 : :
464 : 0 : QStringList QgsExtractByLocationAlgorithm::tags() const
465 : : {
466 : 0 : return QObject::tr( "extract,filter,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
467 : 0 : }
468 : :
469 : 0 : QString QgsExtractByLocationAlgorithm::group() const
470 : : {
471 : 0 : return QObject::tr( "Vector selection" );
472 : : }
473 : :
474 : 0 : QString QgsExtractByLocationAlgorithm::groupId() const
475 : : {
476 : 0 : return QStringLiteral( "vectorselection" );
477 : : }
478 : :
479 : 0 : QString QgsExtractByLocationAlgorithm::shortHelpString() const
480 : : {
481 : 0 : return QObject::tr( "This algorithm creates a new vector layer that only contains matching features from an "
482 : : "input layer. The criteria for adding features to the resulting layer is defined "
483 : : "based on the spatial relationship between each feature and the features in an additional layer." );
484 : : }
485 : :
486 : 0 : QgsExtractByLocationAlgorithm *QgsExtractByLocationAlgorithm::createInstance() const
487 : : {
488 : 0 : return new QgsExtractByLocationAlgorithm();
489 : : }
490 : :
491 : 0 : QVariantMap QgsExtractByLocationAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
492 : : {
493 : 0 : std::unique_ptr< QgsFeatureSource > input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
494 : 0 : if ( !input )
495 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
496 : 0 : std::unique_ptr< QgsFeatureSource > intersectSource( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) );
497 : 0 : if ( !intersectSource )
498 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INTERSECT" ) ) );
499 : :
500 : 0 : const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context );
501 : 0 : QString dest;
502 : 0 : std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, input->fields(), input->wkbType(), input->sourceCrs() ) );
503 : :
504 : 0 : if ( !sink )
505 : 0 : throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
506 : :
507 : 0 : auto addToSink = [&]( const QgsFeature & feature )
508 : : {
509 : 0 : QgsFeature f = feature;
510 : 0 : sink->addFeature( f, QgsFeatureSink::FastInsert );
511 : 0 : };
512 : 0 : process( context, input.get(), intersectSource.get(), selectedPredicates, addToSink, false, feedback );
513 : :
514 : 0 : QVariantMap results;
515 : 0 : results.insert( QStringLiteral( "OUTPUT" ), dest );
516 : 0 : return results;
517 : 0 : }
518 : :
519 : : ///@endcond
520 : :
521 : :
|