5

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".

3 Answers 3

5

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.

Click on the 'Insert Expression' button in your text box properties and use an expression like the one below, replacing the attribute/layer name/concatenator (,) as required.:

aggregate('LAYER_NAME','concatenate',"ATTRIBUTE",
              intersects($geometry,@atlas_geometry), ',')

If ATTRIBUTE field is a numeric datatype you may need to use to_string("ATTRIBUTE") instead of just plain "ATTRIBUTE" to allow concatenation.

As a bonus you can add additional filters within the same expression, e.g. within($geometry,@atlas_geometry) AND "PPP" > 50000 for countries within the atlas geometry, where the PPP is also over 50k.


QGIS 3.8 onwards: Using additional aggregate array functions in QGIS 3 you have more flexibility with displaying attributes.

To have your list of concatenated attributes sorted by attribute name (see also this answer), showing only unique values, you could use:

aggregate('LAYER_NAME','concatenate_unique',"ATTRIBUTE",
              intersects($geometry,@atlas_geometry), ',', "ATTRIBUTE")

Arrays will let you slice and dice the list which is useful if you have limited space. The expression below will return a comma-separated list of the selected attribute from the first three intersecting features (0,2).

array_to_string(
    array_slice(
            aggregate('LAYER','array_agg',"ATTRIBUTE",
                      intersects($geometry,@atlas_geometry))
     ,0,2)
,',')

If you want to get attributes from features intersecting the extent of a layout map item (but not necessarily intersecting with the atlas geometry) - for example if your coverage layer is a point layer, or you have a 10% buffer around your atlas feature in Atlas settings - instead of @atlas_geometry you can extract the extent of the layout map item using item_variables() as below:

aggregate('LAYER_NAME','concatenate_unique',"ATTRIBUTE",
              intersects($geometry,
              map_get(item_variables('LAYOUT_MAP_NAME'),'map_extent')), ',', "ATTRIBUTE")

Note you may need to change the name of your layout map item from the default name (e.g. Map 1)

See below example showing attributes and count of the line features intersecting with the inserts map item - map_get(item_variables('inserts'),'map_extent') - even though the atlas itself is driven by a point layer (and @atlas_geometry would be of no use here).

enter image description here

4

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

0

You could do something like this under the label's Main Properties in QGIS3:

array_to_string( aggregate('COUNTRY_LAYER','array_agg',"COUNTRY_NAME_FIELD",intersects($geometry,@atlas_geometry)))

QGIS3 label Main Properties

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.