4

I have a QGIS project with plenty of geometric layers coming from a PostGIS database.

Each and every layer has the same filter expression to limit the display to a same object: the filter simply being "id=<whatever_id_i_need_to_display>".

This allows me to limit the display to only the desired feature. Indeed, there are more than 200'000 features in the DB, displaying them all tends to slow down, if not crashing QGIS.

But it's somewhat tedious to manually change the filter on dozens of layers.

Is it possible to set such filter expression to many layers in one operation using the GUI?

I "naturally" tried to right click on the selection of all the layers I'd like to change the filter, but when the query builder window opens, it specifically targets the one layer I mouse-clicked on it, not taking into account all the selected layers.

There seems to be a Python solution: How to apply a filter to multiple vector layers in QGIS? but unfortunately there is a bug with SIP on my installation, so all Python related stuff is not available. Therefore, I am looking for a simple operation that I can perform with the graphical interface.

Info:

QGIS version 3.24.2-Tisler
QGIS code revision 13c1a02865
Qt version 5.15.3
Python version 3.10.4
GDAL/OGR version 3.4.1
PROJ version 8.2.1
EPSG Registry database version v10.041 (2021-12-03)
GEOS version 3.10.2-CAPI-1.16.0
SQLite version 3.37.2
PDAL version 2.3.0
PostgreSQL client version unknown
SpatiaLite version 5.0.1
QWT version 6.1.4
QScintilla2 version 2.11.6
OS version Ubuntu 22.04 LTS

EDIT:

Babel's answers are valid and his first one is really nice when working with few simple features.
But in my situation, I do have some layers with large data (up to hundreds of MB), therefore, when I remove the filter expression on those layers and rely only on the style definition, QGIS loads each and every features even so it has been told to display only one. And by loading them all, it consumes all the memory, up to the crash.
Therefore, I absolutely need to build a filter expression at the provider level, namely my PostgreSQL database, to only retrieve one single (or a small subset of) features for visualization purposes, and of course being able to switch from feature N to M in less than 2 seconds, for all my 100 layers.
The key idea is really to be able to switch the visualization to any other feature based on its id, for all layers in one quick and single operation.

EDIT 2:

The advanced filter expression within the attribute table is not working. Indeed, it permits the filtering of feature, but only once everything has been loaded inside QGIS. Therefore, if I apply a filter based on @id_def as suggested, it will still show 200'000 features when I show the feature count in the table of content, and when enabling a single layer having large data, it again eats all my memory up to the crash:

QGIS eating the RAM

So, it's mandatory that this filter is being applied as the provider level, i.e. before querying the features. Well actually not really before but for the querying of the feature(s).

3 Answers 3

2

If you don't use the Provider Feaure Filter to hide some features, but use Layer Styling > Enable Symbol layer to hide some of the features, you can use a custom variable with QGIS expressions to create conditions what should be hidden.

  1. Define a custom variable Menu Project > Properties > Variables. In my case, I name it id_def and assign a value of 21.

  2. In Layer Styling, use data driven override of Enable Symbol layer and set an expression. In my case, I use id < @id_def to show only features with id < 21.

  3. You can copy/paste style between layers to avoid repeating this separately for each layer. If you now change the variable's value to 5, all layers will just show features with id < 5.

enter image description here


Edit

You could also try to define the filter using the variable at the bottom of the attribute table with Advanced Filter (Expression).

6
  • Thank you very much. That's tricky but clever; it seems to work well but it's still very laggy because it tries to load all features under the hood if you don't set a filter at the provider level. Could you also expand your answer on how you could (if possible) manage something similar to a range filter such as using a predefined set of values: "id in (1,2,3,4,9,11)" or using two limits "id between 11 and 20"`? Just in case you'd like to display a subset of features. May 1, 2022 at 13:20
  • If you want to use it in provider feature filter, the query "id" in (1,2,3,4,9,11) and "id" >=11 and "id" <=20 works as intended. So not so clear what you're asking about.
    – Babel
    May 1, 2022 at 15:01
  • Yes it works as intended, but if I'd like to change it for 100 layers, I have to manually enter each and every layer's expression builder to put the same expression in each one. This is time consuming. I wish I could switch my id at only one place and that it's being applied to every layer in one shot. May 1, 2022 at 19:54
  • Advanced Filter in the attribute table (see updated answer) is an option?
    – Babel
    May 1, 2022 at 20:04
  • Nice idea, but same drawback: it loads all features before being able to apply the advanced filter expression within the attribute table: so QGIS crashes again when I try to display a single large data set. It's therefore mandatory that this filter is being applied at the provider level, i.e. that it's being used to actually filter the features during the initial DB query in my case (but this could also be a WFS call for example), not after having loaded them all. I updated the main post to explain all that. May 1, 2022 at 20:42
1

Another option would be to filter the objects in the PostGIS database, creating a view before loading to QGIS and that load this view to work with in QGIS.

Something like the following to generate a view of all features with id >= 5 or id >= 10:

create view myview as select * from mytable as t
where t.id <= 5 or t.id >= 10
1
  • 1
    I only want to be able to rapidly switch between features in my QGIS project when I visualize them. Building a view inside PostgreSQL is overkill and at least as tedious as changing the filter in the query builder. May 1, 2022 at 19:54
1

Too late answer, I think, but may be useful for others:

I had the same problem, needing to use the same filter expression on a number of layers, '"versjon"=<some version number>' So I did a quick and dirty python script:

def filterlayers(versjon):
    for layername in ('sitedata','kabelsjekk','kabeldata'):
        print(layername)
        layer = QgsProject.instance().mapLayersByName(layername)[0]
        layer.setSubsetString(f'"versjon" = {versjon}')

Running this script once in the python terminal after starting QGIS, I can later on run

filterlayer(13)

to get the three layers filtered on "versjon"=13.

If you want to be more flexible on the content of the filter, you can send in the entire filterstring instead:

def filterlayers(filter):
    for layername in ('sitedata','kabelsjekk','kabeldata'):
        print(layername)
        layer = QgsProject.instance().mapLayersByName(layername)[0]
        layer.setSubsetString(filter)

The caveat is that the layer names need to be unique, or be the first layer in the project with the actual name. If this can not be guaranteed, one needs to dig in and find the layer ids and use them instead.

There is also (of course) a plugin for filtering multiple layers: FilterLayersAtOnce. I did install it, and could have used it, but as it is either filtering everything or the selected layers, I would need to make sure I had the right layers selected each time I was using it, so I found the script easier.

I am also using postgis as the data storage and as far as I understand, the filter is working on the data provider level, so only the data with versjon=13 will be fetched from the server (although, in my case the dataset is so small it does not matter where the filtering takes place)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.