QGIS Atlas Error – Fixing Eval Error When Selectively Rendering Raster Layers

atlasprint-composerqgisrasterrule-based

Using the Atlas feature in the QGIS print composer, it is possible to use rule-based symbology to show/hide components of vector layers as required or defined by the Atlas?

I have multiple pages, defined by a feature of a vector file ("Name"). I have raster layers in my project that contain "Name" in their respective layer names. Is it possible to create a rule to only render those layers that contain "Name" on the respective Atlas page?

The reason for this is that there is overlap between Atlas pages and the raster layers can sometimes overlap in unwanted ways. Below is an example where the page with "Name" = A is obfuscated by an unwanted raster layer, B. Ideally, only rasters containing A would be rendered.

I believe it should be able to be done using the Expression String Builder and possibly the Function editor from the Layers drop down in the Item Properties for the desired map in the print layout.

I have a function in my Function editor defined:

from qgis.core import *
from qgis.gui import *

@qgsfunction(args='auto', group='Custom')
def find_match_fun(str_to_match):
    names = [layer.name() for layer in QgsProject.instance().mapLayers().values()]
    names_match = [el for e in names for el in e.split() if el.startswith(str_to_match)]
    names_out = '|'.join(map(lambda x: "'" + x + "'", names_match))
    return(names_out)

However attempting to invoke that in the Expression string builder results in an error:

find_match_fun("A")
Eval Error: find_match_fun() takes 1 positional
argument but 3 were given

Calling the function from Python console in QGIS returns the expected list. I am unsure what I'm missing.

Best Answer

I have not tested your function, but the Eval Error you are seeing is because QGIS custom functions take additional feature and parent parameters. This is explained in the help section of the function editor shown below:

enter image description here

And quoted directly here:

Define a new function using the @qgsfunction decorator.

The function accepts the following parameters

: param [any]: Define any parameters you want to pass to your function before the following arguments.
: param feature: The current feature
: param parent: The QgsExpression object
: param context: If there is an argument called context found at the last position, this variable will contain a QgsExpressionContext object, that gives access to various additional information like expression variables. E.g. context.variable( 'layer_id' )
: returns: The result of the expression.

Therefore, you should add feature and parent parameters to your custom function after your str_to_match parameter.

...
@qgsfunction(args='auto', group='Custom')
def find_match_fun(str_to_match, feature, parent):
...

You can also have a look at this helpful tutorial:

Related Question