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