QGIS API Documentation  3.6.0-Noosa (5873452)
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.
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)
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: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 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...
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...