With QGIS 3.14 and the field calculator :

I have two layers, one with stream network and one with watersheds.

I would like to attribute the name of the watershed to the streams contained inside it with an expression in the field calculator. How to include the "within" condition ?

I guess it should be something like that

WHEN  within(  $geometry , $geometry) THEN
get_feature('watersheds',"name", $currentfeature )

But replacing the second $geometry with a reference to the polygons of the watershed layer

With PyQGIS 3

I also tried the following PyQGIS code , that requires some more informations.

streamInput The layer containing the stream network has one unique ID per each stream ('IDjoin2')

The layer of watersheds contains several "levels" of watersheds. The main streams belongs to the bigger watersheds, and the smallest have their own little watershed, but also belongs to bigger ones. There are 7 levels of watersheds : the 1st one is the bigger one, the last one is the smallest.

streamBvInput A spatial join (1-to-many) with the watersheds layer allowed me to have, for each stream, all the watersheds in which it is contained.

csv_path I need to get, for each stream, the name of the smallest watershed that contains it, that's why I calculated statistics on the above layer, to obtain the maximum level of watershed for each IDjoin2/stream.

project = QgsProject.instance()

#input layers        
streamInput = QgsProject.instance().mapLayersByName('Reseau_Hydro_..._Nom')[0]
streamBvInput = QgsProject.instance().mapLayersByName('Reseau_Hydro_..._Nom_intersectBVmulti')[0]
csv_path = 'C:/Users/.../IDjoin2_intesect_maxBV.csv'
streamLayer = project.mapLayer(streamInput.id())
streamBvLayer = project.mapLayer(streamBvInput.id())
if not (streamLayer.isEditable()):
layerStreamFields = streamLayer.fields()
with open(csv_path, newline='') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader: #broswe into statistics csv file
    # get the highest level of watershed for each stream
        IDcsv = int(row['IDjoin2'])
        searchBVmax = row['max']
        iterStreamBV = streamBvLayer.getFeatures()
        for featureBV in iterStreamBV: #broswe into the join file
            searchIDbv = featureBV['IDjoin2']
            if (IDcsv == searchIDbv): # if ID in csv and bvInput are identical
                if (featureBV['NIVEAU_BAS'] == searchBVmax) :
                #if watershed level in csv and bvInput are identical
                    BVname = featureBV['NOM_COURS_1'] 
                    # get the name of the watershed
                    iterStreamFeatures = streamLayer.getFeatures()
                    for featureS in iterStreamFeatures:
                        searchIDj = featureS['IDjoin2']
                        if (searchIDj == searchIDbv):
                            attribute the name to the stream in streamInput


But as soon as I run this script QGIS is freezing, and I received no error messages. I began testing the code almost line per line, and it looks like a part of the problem comes from the size of the layer "streamBvInput", that contains more than 114 000 features.

1 Answer 1


(Answer A) QGIS 3 with pure Field Calculator expressions

aggregate(layer:='watersheds', aggregate:= 'concatenate', 
          expression:= "name", 
          filter:= within(geometry(@parent), $geometry))

(Answer B) QGIS 3 with a plugin

Using refFunctions plugin:

geomwithin('watersheds', 'name')

(Answer C) QGIS 3.16

array_to_string(overlay_within('watersheds', name))
  • overlay_within() returns the name field from the watersheds layer, if the current geometry (stream) is within the corresponding feature in the watersheds layer.
  • Because overlay_within() returns the value in an array, array_to_string() is required to convert it to a string (text).

enter image description here

  • Thank you! unfortunately I have to work with QGIS 3.14 Commented Feb 10, 2021 at 17:17
  • @JulietteLapeyre Added two approaches (A and B) both of which should work in QGIS 3.14. Enjoy!
    – Kazuhito
    Commented Feb 11, 2021 at 2:50

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.