2

I wan't to filter features of a Shapefile layer from a user's selection with PyQGIS. When I select less than 30 features with a method like fid=1 or fid=2 or fid=3 ..., everything works fine but when I select over 30 features filter doesn't work and return me all the features of the layer. If I try a filter like fid >= 1 and fid <=150 (for example), it works. But it doesn't help for lists of unfollowing ids. I'm using QGIS 3.10.2.

Is there something wrong with this ? Does filters have size limits ? Is there a better way to filter features ?

# Layer
layer = iface.activeLayer()
features = layer.selectedFeatures()

List ID of selected features

l = [] for feature in features: l.append(feature.id())

Build arguments for the query

index = 0 query = [] prefix = 'fid=' while index < len(l)-1: query.append(prefix) query.append(l[index]) query.append(' or ') index+=1

Last element of the list

query.append(prefix) query.append(l[index])

Query

fullQuery = str(query).strip('[]').replace(',', '').replace("'", "")

Filter

layer.setSubsetString(fullQuery)

GeoGyro
  • 1,636
  • 1
  • 15
  • 35
  • Hi, there are a couple of things you could try - add a new field and populate the selected rows with a value that you then filter on. Or you could abandon the filter idea and create a new layer from your selection - as done here: https://gis.stackexchange.com/questions/80292/how-can-i-create-a-vector-layer-from-selected-features-with-pyqgis/262405 – ian Nov 19 '20 at 20:10
  • Indeed it's possible to add a new field and populate the selected rows, thanks for this suggestion.But I don't understand why the filter no longer works beyond a certain number of elements. – GeoGyro Nov 24 '20 at 08:53
  • 1
    I don't think this will solve it, but in general, instead of fid = 1 or fid = 2 or... you should use fid in (1,2,...). It improves code readability. – Germán Carrillo Dec 22 '20 at 13:13
  • Thanks @GermánCarrillo, this syntax is more readable and more efficient. Features are well filtered, regardless the number of selected elements instead of my previous method. You should post il as an answer. – GeoGyro Dec 23 '20 at 08:26

2 Answers2

4

I don't know if this solves your question, but in general, instead of

fid = 1 or fid = 2 or...

you should use

fid in (1,2,...)

It improves code readability.

Germán Carrillo
  • 36,307
  • 5
  • 123
  • 178
1

I am not sure what is wrong in your code but I have prepared a simple example that applies a filter based on features' id following the same logic of your example and it works fine with large number of features (e.g., 88 in this example). I am using QGIS 3.10.11:

# 1) Load a vector layer from natural earth database and import it to the project
gpkg_address = r"c:\%DATA_PAATH%\natural_earth_vector_50m_10m.gpkg"
lyr_name = "natural_earth_vector_50m_10m ne_10m_admin_0_countries"
lyr = QgsVectorLayer("{}|{}".format(gpkg_address, lyr_name), lyr_name, "ogr")
QgsProject.instance().addMapLayer(lyr)

2) Count the number of overall features, and select only the features with population over 10 millions

n_feat_tot = lyr.featureCount() s_pop = "POP_EST" lyr.selectByExpression('&quot;{}&quot; > {}'.format(s_pop, 10e6)) n_feat_sel = lyr.selectedFeatureCount() print("Total features: {}, selected features: {}".format(n_feat_tot, n_feat_sel))

Total features: 255, selected features: 88

3) Get the id of selected features. Remove the selection and then prepare the query. Finally, apply the query to the layer and count the remaining features.

feat_id = lyr.selectedFeatureIds() lyr.removeSelection() query = [" or ".join(["fid={}".format(el) for el in feat_id])] lyr.setSubsetString(query[0]) n_feat_subset = lyr.featureCount() print("Subset features: {}".format(n_feat_subset))

Subset features: 88

The input data are from Natural Earth database. I hope this helps.

fastest
  • 1,289
  • 7
  • 14
  • Thanks for this answer. In my case the complexity of the filter is in the fact that the values ​​do not follow each other unlike this example which filters on values ​​greater than a defined threshold. – GeoGyro Nov 24 '20 at 08:56
  • Hi GeoGyro, I am not sure if I understand the problem. In my example, the Ids of the selected features are not consecutive (a short excerpt of the feat_id list is given here: [102, 123, 122, 125, 127, 126, 113, 115, 114, 118, 137, ...] and the selection still works. Do you mean something different with "values do not follow each other"? – fastest Nov 24 '20 at 18:02
  • Maybe I'm misunderstanding something in you're selection lyr.selectByExpression('\"{}\" > {}'.format(s_pop, 10e6)). In my case I'm making a selection with the graphic tool – GeoGyro Nov 25 '20 at 07:53
  • An other big difference between our 2 methods : I use Shapefile while you use Geopackage. – GeoGyro Nov 25 '20 at 08:14
  • Ok, I now understand that your selection is operated through the graphic_tool. However, the workflow you apply after the feature selection is exactly the same as the one shown in my code. Eventually, the difference between shapefile and geopackage can play a role. I can try to run the provided example with a shapefile as well to see what happens. – fastest Nov 25 '20 at 16:26