3

Is it possible to edit labels with python or an expression?

I have an atlas layer and a shape with countries. the atlas layer has several features and each feature intersects with several countries.

I want a label on my pdf map that gets the country name (attribute in country layer) as a function of the intersection of the atlas layer. is something like this possible?

Example: Feature 1 of Atlas Layer lays over 3 Countries (C1, C2, C3). I want a label on my map that says: "C1, C2, C3". Feature 1 of Atlas Layer lays over 2 Countries (C5, C6). I want a label on my map that says: "C5, C6".

2

As a variation of How to count points within the current Print composer atlas feature in QGIS 2.8? try this:

Create an expression script in the Expression Function Editor with this code:

from qgis.core import *
from qgis.gui import *
from qgis.utils import iface

@qgsfunction(args="auto", group='Custom')
def getAttribudeIntersectAtlasFeature(intersectLayername, intersectFieldname, geomAtlas, feature, parent):
    # If geomAtlas is empty, return empty string
    if (geomAtlas is None):
        return ''

    # Get layer reference from layername
    intersectLayer = QgsMapLayerRegistry.instance().mapLayersByName(intersectLayername)[0]

    # Raise if layer not found
    if intersectLayer is None:
        raise Exception("Layer not found: " + intersectLayername)

    # Count point within current Atlas feature
    countryname = ''
    for ft in intersectLayer.getFeatures():
        intersectGeom = ft.geometry()
        if (intersectGeom is None):
            continue
        if intersectGeom.intersects(geomAtlas):
            countryname += ft[intersectFieldname] + ','

    return countryname[:-1]

Then use the custom function in an label expression in the print composer:

[% getAttribudeIntersectAtlasFeature( 'world', 'country',  $atlasgeometry )%]

Where 'world' is the tabel with countries, 'country' is the columnname with the countryname and $atlasgeometry is the current atlas geometry.

Map: w

Atlas 1: w2

Atlas 2: w3

1

Also as a variation of an answer to How to count points within the current Print composer atlas feature in QGIS 2.8? , from QGIS 2.16 onwards you can list attributes intersecting an atlas feature within the Expression Editor, using the aggregate() function with the concatenate calculation and within($geometry,@atlas_geometry) as the filter.

The full syntax in your text label for a comma-delimited list would be

[% aggregate('LAYER_NAME','concatenate',"ATTRIBUTE",
              within($geometry,@atlas_geometry), ',') %]

Replace the ',' with whatever you wish to separate the values with. Note it may not appear correctly in the preview at the bottom of the Expression Editor, but does work otherwise.

This will concatenate all the values for the specified attribute in your specified layer, but only if the feature geometry is within the atlas geometry.

The [% %] is required to make the expression work. And note the quotation marks - you must use double quotes for the attribute name.

As a bonus you can add on to the above filter, e.g. within($geometry,@atlas_geometry) AND "PPP" > 50000 for countries within the atlas geometry, where the GDP per capita is over 50k.


QGIS 3.0 bonus: By wrapping the above expression in some of the new array functions available in QGIS 3 (2.99), you can easily split up your list of attributes - say only the first 3 attributes in one text box, then the next 3 attributes in another. (apologies for the poor formatting)

array_to_string(
    array_slice(
        string_to_array(
            aggregate('LAYER','concatenate',"ATTRIBUTE",
                      within($geometry,@atlas_geometry),',')
         ,',')
     ,0,19)
)

This takes your concatenated list of attributes as above, converts it into an array specifying the delimiter you used with string_to_array(), extracts the first 20 from the list (here shown as 0,19 because arrays start at 0) with array_slice(), then spits that back out as a string with array_to_string().

Note: I don't yet know how to sort the array.

Also, I haven't been able to use array_agg() within the aggregate() function to cut down on the number of steps, since array_agg() doesn't seem to realise it's looking for an attribute within the layer specified by aggregate().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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