QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgsalgorithmextractbylocation.cpp
Go to the documentation of this file.
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 
19 #include "qgsgeometryengine.h"
20 #include "qgsvectorlayer.h"
21 
23 
24 void QgsLocationBasedAlgorithm::addPredicateParameter()
25 {
26  std::unique_ptr< QgsProcessingParameterEnum > predicateParam( new QgsProcessingParameterEnum( QStringLiteral( "PREDICATE" ),
27  QObject::tr( "Where the features (geometric predicate)" ),
28  predicateOptionsList(), true, QVariant::fromValue( QList< int >() << 0 ) ) );
29 
30  QVariantMap predicateMetadata;
31  QVariantMap widgetMetadata;
32  widgetMetadata.insert( QStringLiteral( "useCheckBoxes" ), true );
33  widgetMetadata.insert( QStringLiteral( "columns" ), 2 );
34  predicateMetadata.insert( QStringLiteral( "widget_wrapper" ), widgetMetadata );
35  predicateParam->setMetadata( predicateMetadata );
36 
37  addParameter( predicateParam.release() );
38 }
39 
40 QgsLocationBasedAlgorithm::Predicate QgsLocationBasedAlgorithm::reversePredicate( QgsLocationBasedAlgorithm::Predicate predicate ) const
41 {
42  switch ( predicate )
43  {
44  case Intersects:
45  return Intersects;
46  case Contains:
47  return Within;
48  case Disjoint:
49  return Disjoint;
50  case IsEqual:
51  return IsEqual;
52  case Touches:
53  return Touches;
54  case Overlaps:
55  return Overlaps;
56  case Within:
57  return Contains;
58  case Crosses:
59  return Crosses;
60  }
61  // no warnings
62  return Intersects;
63 }
64 
65 QStringList QgsLocationBasedAlgorithm::predicateOptionsList() const
66 {
67  return QStringList() << QObject::tr( "intersect" )
68  << QObject::tr( "contain" )
69  << QObject::tr( "disjoint" )
70  << QObject::tr( "equal" )
71  << QObject::tr( "touch" )
72  << QObject::tr( "overlap" )
73  << QObject::tr( "are within" )
74  << QObject::tr( "cross" );
75 }
76 
77 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  QgsFeedback *feedback )
83 {
84  // build a list of 'reversed' predicates, because in this function
85  // we actually test the reverse of what the user wants (allowing us
86  // to prepare geometries and optimise the algorithm)
87  QList< Predicate > predicates;
88  predicates.reserve( selectedPredicates.count() );
89  for ( int i : selectedPredicates )
90  {
91  predicates << reversePredicate( static_cast< Predicate >( i ) );
92  }
93 
94  QgsFeatureIds disjointSet;
95  if ( predicates.contains( Disjoint ) )
96  disjointSet = targetSource->allFeatureIds();
97 
98  QgsFeatureIds foundSet;
100  QgsFeatureIterator fIt = intersectSource->getFeatures( request );
101  double step = intersectSource->featureCount() > 0 ? 100.0 / intersectSource->featureCount() : 1;
102  int current = 0;
103  QgsFeature f;
104  std::unique_ptr< QgsGeometryEngine > engine;
105  while ( fIt.nextFeature( f ) )
106  {
107  if ( feedback->isCanceled() )
108  break;
109 
110  if ( !f.hasGeometry() )
111  continue;
112 
113  engine.reset();
114 
115  QgsRectangle bbox = f.geometry().boundingBox();
116  request = QgsFeatureRequest().setFilterRect( bbox );
117  if ( onlyRequireTargetIds )
118  request.setNoAttributes();
119 
120  QgsFeatureIterator testFeatureIt = targetSource->getFeatures( request );
121  QgsFeature testFeature;
122  while ( testFeatureIt.nextFeature( testFeature ) )
123  {
124  if ( feedback->isCanceled() )
125  break;
126 
127  if ( foundSet.contains( testFeature.id() ) )
128  {
129  // already added this one, no need for further tests
130  continue;
131  }
132  if ( predicates.count() == 1 && predicates.at( 0 ) == Disjoint && !disjointSet.contains( testFeature.id() ) )
133  {
134  // calculating only the disjoint set, and we've already eliminated this feature so no need for further tests
135  continue;
136  }
137 
138  if ( !engine )
139  {
140  engine.reset( QgsGeometry::createGeometryEngine( f.geometry().constGet() ) );
141  engine->prepareGeometry();
142  }
143 
144  for ( Predicate predicate : qgis::as_const( predicates ) )
145  {
146  bool isMatch = false;
147  switch ( predicate )
148  {
149  case Intersects:
150  isMatch = engine->intersects( testFeature.geometry().constGet() );
151  break;
152  case Contains:
153  isMatch = engine->contains( testFeature.geometry().constGet() );
154  break;
155  case Disjoint:
156  if ( engine->intersects( testFeature.geometry().constGet() ) )
157  {
158  disjointSet.remove( testFeature.id() );
159  }
160  break;
161  case IsEqual:
162  isMatch = engine->isEqual( testFeature.geometry().constGet() );
163  break;
164  case Touches:
165  isMatch = engine->touches( testFeature.geometry().constGet() );
166  break;
167  case Overlaps:
168  isMatch = engine->overlaps( testFeature.geometry().constGet() );
169  break;
170  case Within:
171  isMatch = engine->within( testFeature.geometry().constGet() );
172  break;
173  case Crosses:
174  isMatch = engine->crosses( testFeature.geometry().constGet() );
175  break;
176  }
177  if ( isMatch )
178  {
179  foundSet.insert( testFeature.id() );
180  handleFeatureFunction( testFeature );
181  }
182  }
183 
184  }
185 
186  current += 1;
187  feedback->setProgress( current * step );
188  }
189 
190  if ( predicates.contains( Disjoint ) )
191  {
192  disjointSet = disjointSet.subtract( foundSet );
193  QgsFeatureRequest disjointReq = QgsFeatureRequest().setFilterFids( disjointSet );
194  if ( onlyRequireTargetIds )
196  QgsFeatureIterator disjointIt = targetSource->getFeatures( disjointReq );
197  QgsFeature f;
198  while ( disjointIt.nextFeature( f ) )
199  {
200  handleFeatureFunction( f );
201  }
202  }
203 }
204 
205 
206 //
207 // QgsSelectByLocationAlgorithm
208 //
209 
210 void QgsSelectByLocationAlgorithm::initAlgorithm( const QVariantMap & )
211 {
212  QStringList methods = QStringList() << QObject::tr( "creating new selection" )
213  << QObject::tr( "adding to current selection" )
214  << QObject::tr( "selecting within current selection" )
215  << QObject::tr( "removing from current selection" );
216 
217  addParameter( new QgsProcessingParameterVectorLayer( QStringLiteral( "INPUT" ), QObject::tr( "Select features from" ),
218  QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
219  addPredicateParameter();
220  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
221  QObject::tr( "By comparing to the features from" ),
222  QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
223 
224  addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ),
225  QObject::tr( "Modify current selection by" ),
226  methods, false, 0 ) );
227 }
228 
229 QString QgsSelectByLocationAlgorithm::name() const
230 {
231  return QStringLiteral( "selectbylocation" );
232 }
233 
234 QgsProcessingAlgorithm::Flags QgsSelectByLocationAlgorithm::flags() const
235 {
237 }
238 
239 QString QgsSelectByLocationAlgorithm::displayName() const
240 {
241  return QObject::tr( "Select by location" );
242 }
243 
244 QStringList QgsSelectByLocationAlgorithm::tags() const
245 {
246  return QObject::tr( "select,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
247 }
248 
249 QString QgsSelectByLocationAlgorithm::group() const
250 {
251  return QObject::tr( "Vector selection" );
252 }
253 
254 QString QgsSelectByLocationAlgorithm::groupId() const
255 {
256  return QStringLiteral( "vectorselection" );
257 }
258 
259 QString QgsSelectByLocationAlgorithm::shortHelpString() const
260 {
261  return QObject::tr( "This algorithm creates a selection in a vector layer. The criteria for selecting "
262  "features is based on the spatial relationship between each feature and the features in an additional layer." );
263 }
264 
265 QgsSelectByLocationAlgorithm *QgsSelectByLocationAlgorithm::createInstance() const
266 {
267  return new QgsSelectByLocationAlgorithm();
268 }
269 
270 QVariantMap QgsSelectByLocationAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
271 {
272  QgsVectorLayer *selectLayer = parameterAsVectorLayer( parameters, QStringLiteral( "INPUT" ), context );
273  if ( !selectLayer )
274  throw QgsProcessingException( QObject::tr( "Could not load source layer for INPUT" ) );
275 
276  QgsVectorLayer::SelectBehavior method = static_cast< QgsVectorLayer::SelectBehavior >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
277  std::unique_ptr< QgsFeatureSource > intersectSource( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) );
278  if ( !intersectSource )
279  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INTERSECT" ) ) );
280 
281  const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context );
282 
283  QgsFeatureIds selectedIds;
284  auto addToSelection = [&]( const QgsFeature & feature )
285  {
286  selectedIds.insert( feature.id() );
287  };
288  process( context, selectLayer, intersectSource.get(), selectedPredicates, addToSelection, true, feedback );
289 
290  selectLayer->selectByIds( selectedIds, method );
291  QVariantMap results;
292  results.insert( QStringLiteral( "OUTPUT" ), parameters.value( QStringLiteral( "INPUT" ) ) );
293  return results;
294 }
295 
296 
297 //
298 // QgsExtractByLocationAlgorithm
299 //
300 
301 void QgsExtractByLocationAlgorithm::initAlgorithm( const QVariantMap & )
302 {
303  addParameter( new QgsProcessingParameterVectorLayer( QStringLiteral( "INPUT" ), QObject::tr( "Extract features from" ),
304  QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
305  addPredicateParameter();
306  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
307  QObject::tr( "By comparing to the features from" ),
308  QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
309 
310  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extracted (location)" ) ) );
311 }
312 
313 QString QgsExtractByLocationAlgorithm::name() const
314 {
315  return QStringLiteral( "extractbylocation" );
316 }
317 
318 QString QgsExtractByLocationAlgorithm::displayName() const
319 {
320  return QObject::tr( "Extract by location" );
321 }
322 
323 QStringList QgsExtractByLocationAlgorithm::tags() const
324 {
325  return QObject::tr( "extract,filter,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
326 }
327 
328 QString QgsExtractByLocationAlgorithm::group() const
329 {
330  return QObject::tr( "Vector selection" );
331 }
332 
333 QString QgsExtractByLocationAlgorithm::groupId() const
334 {
335  return QStringLiteral( "vectorselection" );
336 }
337 
338 QString QgsExtractByLocationAlgorithm::shortHelpString() const
339 {
340  return QObject::tr( "This algorithm creates a new vector layer that only contains matching features from an "
341  "input layer. The criteria for adding features to the resulting layer is defined "
342  "based on the spatial relationship between each feature and the features in an additional layer." );
343 }
344 
345 QgsExtractByLocationAlgorithm *QgsExtractByLocationAlgorithm::createInstance() const
346 {
347  return new QgsExtractByLocationAlgorithm();
348 }
349 
350 QVariantMap QgsExtractByLocationAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
351 {
352  std::unique_ptr< QgsFeatureSource > input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
353  if ( !input )
354  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
355  std::unique_ptr< QgsFeatureSource > intersectSource( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) );
356  if ( !intersectSource )
357  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INTERSECT" ) ) );
358 
359  const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context );
360  QString dest;
361  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, input->fields(), input->wkbType(), input->sourceCrs() ) );
362 
363  if ( !sink )
364  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
365 
366  auto addToSink = [&]( const QgsFeature & feature )
367  {
368  QgsFeature f = feature;
369  sink->addFeature( f, QgsFeatureSink::FastInsert );
370  };
371  process( context, input.get(), intersectSource.get(), selectedPredicates, addToSink, false, feedback );
372 
373  QVariantMap results;
374  results.insert( QStringLiteral( "OUTPUT" ), dest );
375  return results;
376 }
377 
379 
380 
381 
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature&#39;s geometries.
QgsFeatureId id
Definition: qgsfeature.h:64
Wrapper for iterator of features from vector data provider or vector layer.
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
Base class for providing feedback from a processing algorithm.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
virtual Flags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users...
void selectByIds(const QgsFeatureIds &ids, SelectBehavior behavior=SetSelection)
Selects matching features using a list of feature IDs.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
A feature sink output for processing algorithms.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
An enum based parameter for processing algorithms, allowing for selection from predefined values...
Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which mani...
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
A vector layer (with or without geometry) parameter for processing algorithms.
virtual QgsCoordinateReferenceSystem sourceCrs() const =0
Returns the coordinate reference system for features in the source.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
SelectBehavior
Selection behavior.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
An interface for objects which provide features via a getFeatures method.
An input feature source (such as vector layers) parameter for processing algorithms.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
QgsGeometry geometry
Definition: qgsfeature.h:67
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Represents a vector layer which manages a vector based data sets.
virtual QgsFeatureIds allFeatureIds() const
Returns a list of all feature IDs for features present in the source.
Contains information about the context in which a processing algorithm is executed.
Any vector layer with geometry.
Definition: qgsprocessing.h:47
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const =0
Returns an iterator for the features in the source.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
virtual long featureCount() const =0
Returns the number of features contained in the source, or -1 if the feature count is unknown...