QGIS API Documentation  3.2.0-Bonn (bc43194)
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 
22 
23 void QgsLocationBasedAlgorithm::addPredicateParameter()
24 {
25  std::unique_ptr< QgsProcessingParameterEnum > predicateParam( new QgsProcessingParameterEnum( QStringLiteral( "PREDICATE" ),
26  QObject::tr( "Where the features (geometric predicate)" ),
27  predicateOptionsList(), true, QVariant::fromValue( QList< int >() << 0 ) ) );
28 
29  QVariantMap predicateMetadata;
30  QVariantMap widgetMetadata;
31  widgetMetadata.insert( QStringLiteral( "class" ), QStringLiteral( "processing.gui.wrappers.EnumWidgetWrapper" ) );
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  for ( int i : selectedPredicates )
89  {
90  predicates << reversePredicate( static_cast< Predicate >( i ) );
91  }
92 
93  QgsFeatureIds disjointSet;
94  if ( predicates.contains( Disjoint ) )
95  disjointSet = targetSource->allFeatureIds();
96 
97  QgsFeatureIds foundSet;
99  QgsFeatureIterator fIt = intersectSource->getFeatures( request );
100  double step = intersectSource->featureCount() > 0 ? 100.0 / intersectSource->featureCount() : 1;
101  int current = 0;
102  QgsFeature f;
103  std::unique_ptr< QgsGeometryEngine > engine;
104  while ( fIt.nextFeature( f ) )
105  {
106  if ( feedback->isCanceled() )
107  break;
108 
109  if ( !f.hasGeometry() )
110  continue;
111 
112  engine.reset();
113 
114  QgsRectangle bbox = f.geometry().boundingBox();
115  request = QgsFeatureRequest().setFilterRect( bbox );
116  if ( onlyRequireTargetIds )
118 
119  QgsFeatureIterator testFeatureIt = targetSource->getFeatures( request );
120  QgsFeature testFeature;
121  while ( testFeatureIt.nextFeature( testFeature ) )
122  {
123  if ( feedback->isCanceled() )
124  break;
125 
126  if ( foundSet.contains( testFeature.id() ) )
127  {
128  // already added this one, no need for further tests
129  continue;
130  }
131  if ( predicates.count() == 1 && predicates.at( 0 ) == Disjoint && !disjointSet.contains( testFeature.id() ) )
132  {
133  // calculating only the disjoint set, and we've already eliminated this feature so no need for further tests
134  continue;
135  }
136 
137  if ( !engine )
138  {
139  engine.reset( QgsGeometry::createGeometryEngine( f.geometry().constGet() ) );
140  engine->prepareGeometry();
141  }
142 
143  for ( Predicate predicate : qgis::as_const( predicates ) )
144  {
145  bool isMatch = false;
146  switch ( predicate )
147  {
148  case Intersects:
149  isMatch = engine->intersects( testFeature.geometry().constGet() );
150  break;
151  case Contains:
152  isMatch = engine->contains( testFeature.geometry().constGet() );
153  break;
154  case Disjoint:
155  if ( engine->intersects( testFeature.geometry().constGet() ) )
156  {
157  disjointSet.remove( testFeature.id() );
158  }
159  break;
160  case IsEqual:
161  isMatch = engine->isEqual( testFeature.geometry().constGet() );
162  break;
163  case Touches:
164  isMatch = engine->touches( testFeature.geometry().constGet() );
165  break;
166  case Overlaps:
167  isMatch = engine->overlaps( testFeature.geometry().constGet() );
168  break;
169  case Within:
170  isMatch = engine->within( testFeature.geometry().constGet() );
171  break;
172  case Crosses:
173  isMatch = engine->crosses( testFeature.geometry().constGet() );
174  break;
175  }
176  if ( isMatch )
177  {
178  foundSet.insert( testFeature.id() );
179  handleFeatureFunction( testFeature );
180  }
181  }
182 
183  }
184 
185  current += 1;
186  feedback->setProgress( current * step );
187  }
188 
189  if ( predicates.contains( Disjoint ) )
190  {
191  disjointSet = disjointSet.subtract( foundSet );
192  QgsFeatureRequest disjointReq = QgsFeatureRequest().setFilterFids( disjointSet );
193  if ( onlyRequireTargetIds )
195  QgsFeatureIterator disjointIt = targetSource->getFeatures( disjointReq );
196  QgsFeature f;
197  while ( disjointIt.nextFeature( f ) )
198  {
199  handleFeatureFunction( f );
200  }
201  }
202 }
203 
204 
205 //
206 // QgsSelectByLocationAlgorithm
207 //
208 
209 void QgsSelectByLocationAlgorithm::initAlgorithm( const QVariantMap & )
210 {
211  QStringList methods = QStringList() << QObject::tr( "creating new selection" )
212  << QObject::tr( "adding to current selection" )
213  << QObject::tr( "select within current selection" )
214  << QObject::tr( "removing from current selection" );
215 
216  addParameter( new QgsProcessingParameterVectorLayer( QStringLiteral( "INPUT" ), QObject::tr( "Select features from" ),
217  QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
218  addPredicateParameter();
219  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
220  QObject::tr( "By comparing to the features from" ),
221  QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
222 
223  addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ),
224  QObject::tr( "Modify current selection by" ),
225  methods, false, 0 ) );
226 }
227 
228 QString QgsSelectByLocationAlgorithm::name() const
229 {
230  return QStringLiteral( "selectbylocation" );
231 }
232 
233 QgsProcessingAlgorithm::Flags QgsSelectByLocationAlgorithm::flags() const
234 {
236 }
237 
238 QString QgsSelectByLocationAlgorithm::displayName() const
239 {
240  return QObject::tr( "Select by location" );
241 }
242 
243 QStringList QgsSelectByLocationAlgorithm::tags() const
244 {
245  return QObject::tr( "select,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
246 }
247 
248 QString QgsSelectByLocationAlgorithm::group() const
249 {
250  return QObject::tr( "Vector selection" );
251 }
252 
253 QString QgsSelectByLocationAlgorithm::groupId() const
254 {
255  return QStringLiteral( "vectorselection" );
256 }
257 
258 QString QgsSelectByLocationAlgorithm::shortHelpString() const
259 {
260  return QObject::tr( "This algorithm creates a selection in a vector layer. The criteria for selecting "
261  "features is based on the spatial relationship between each feature and the features in an additional layer." );
262 }
263 
264 QgsSelectByLocationAlgorithm *QgsSelectByLocationAlgorithm::createInstance() const
265 {
266  return new QgsSelectByLocationAlgorithm();
267 }
268 
269 QVariantMap QgsSelectByLocationAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
270 {
271  QgsVectorLayer *selectLayer = parameterAsVectorLayer( parameters, QStringLiteral( "INPUT" ), context );
272  if ( !selectLayer )
273  throw QgsProcessingException( QObject::tr( "Could not load source layer for INPUT" ) );
274 
275  QgsVectorLayer::SelectBehavior method = static_cast< QgsVectorLayer::SelectBehavior >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
276  std::unique_ptr< QgsFeatureSource > intersectSource( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) );
277  if ( !intersectSource )
278  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INTERSECT" ) ) );
279 
280  const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context );
281 
282  QgsFeatureIds selectedIds;
283  auto addToSelection = [&]( const QgsFeature & feature )
284  {
285  selectedIds.insert( feature.id() );
286  };
287  process( context, selectLayer, intersectSource.get(), selectedPredicates, addToSelection, true, feedback );
288 
289  selectLayer->selectByIds( selectedIds, method );
290  QVariantMap results;
291  results.insert( QStringLiteral( "OUTPUT" ), parameters.value( QStringLiteral( "INPUT" ) ) );
292  return results;
293 }
294 
295 
296 //
297 // QgsExtractByLocationAlgorithm
298 //
299 
300 void QgsExtractByLocationAlgorithm::initAlgorithm( const QVariantMap & )
301 {
302  addParameter( new QgsProcessingParameterVectorLayer( QStringLiteral( "INPUT" ), QObject::tr( "Extract features from" ),
303  QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
304  addPredicateParameter();
305  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INTERSECT" ),
306  QObject::tr( "By comparing to the features from" ),
307  QList< int >() << QgsProcessing::TypeVectorAnyGeometry ) );
308 
309  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extracted (location)" ) ) );
310 }
311 
312 QString QgsExtractByLocationAlgorithm::name() const
313 {
314  return QStringLiteral( "extractbylocation" );
315 }
316 
317 QString QgsExtractByLocationAlgorithm::displayName() const
318 {
319  return QObject::tr( "Extract by location" );
320 }
321 
322 QStringList QgsExtractByLocationAlgorithm::tags() const
323 {
324  return QObject::tr( "extract,filter,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
325 }
326 
327 QString QgsExtractByLocationAlgorithm::group() const
328 {
329  return QObject::tr( "Vector selection" );
330 }
331 
332 QString QgsExtractByLocationAlgorithm::groupId() const
333 {
334  return QStringLiteral( "vectorselection" );
335 }
336 
337 QString QgsExtractByLocationAlgorithm::shortHelpString() const
338 {
339  return QObject::tr( "This algorithm creates a new vector layer that only contains matching features from an "
340  "input layer. The criteria for adding features to the resulting layer is defined "
341  "based on the spatial relationship between each feature and the features in an additional layer." );
342 }
343 
344 QgsExtractByLocationAlgorithm *QgsExtractByLocationAlgorithm::createInstance() const
345 {
346  return new QgsExtractByLocationAlgorithm();
347 }
348 
349 QVariantMap QgsExtractByLocationAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
350 {
351  std::unique_ptr< QgsFeatureSource > input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
352  if ( !input )
353  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
354  std::unique_ptr< QgsFeatureSource > intersectSource( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) );
355  if ( !intersectSource )
356  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INTERSECT" ) ) );
357 
358  const QList< int > selectedPredicates = parameterAsEnums( parameters, QStringLiteral( "PREDICATE" ), context );
359  QString dest;
360  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, input->fields(), input->wkbType(), input->sourceCrs() ) );
361 
362  if ( !sink )
363  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
364 
365  auto addToSink = [&]( const QgsFeature & feature )
366  {
367  QgsFeature f = feature;
368  sink->addFeature( f, QgsFeatureSink::FastInsert );
369  };
370  process( context, input.get(), intersectSource.get(), selectedPredicates, addToSink, false, feedback );
371 
372  QVariantMap results;
373  results.insert( QStringLiteral( "OUTPUT" ), dest );
374  return results;
375 }
376 
378 
379 
380 
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature&#39;s geometries.
QgsFeatureId id
Definition: qgsfeature.h:71
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:40
Base class for providing feedback from a processing algorithm.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:544
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
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)
Select 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:62
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
A feature sink output for processing algorithms.
Base class for feedback objects to be used for cancelation 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...
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.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
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.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
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...