5

How do I specify the input layer so it refers to the layer from which the function was called (without having to explicitly identify it by name)?

I have written a custom function to use in the expression editor. It returns the rank of each feature based on the specified attribute. For example, if I want to highlight the smallest 10 cities I can use an expression along the lines of: top_rank('citypopulation')<=10 to filter the top 10 ranked features.

The function sort of works, however, if I use it on different layers at the same time the outputs for each layer change. After some consideration, I think it might be due to using aLayer = qgis.utils.iface.activeLayer() to specify the layer and that each time I select a new layer it changes aLayer throughout the project.

from qgis.core import *
from qgis.gui import *
from qgis.utils import qgsfunction
from qgis.core import QGis

# this line puts  function into 'Custom' group in the expression builder
@qgsfunction(args="auto", group='Custom')

# custom functions must take (fields,feature,parent as arguments)
# usage is : top_rank('attribute_name')
def top_rank(field, feature, parent):
    aLayer = qgis.utils.iface.activeLayer()
    target_field=feature.fieldNameIndex(field) # gets index of target field - checked
    f_id=feature.id() #gets target feature id - checked
    aList= [(feat[target_field], feat.id()) for feat in aLayer.getFeatures()]
    aList.sort()  # sort list of tuples
    i = 1
    row_index=0
    for feat in aList:
        if feat[1]==f_id:
            row_index=i
        i  += 1
    return(row_index)

(I initially asked this on Stack Overflow but I think it is more relevant here)

(Previously, I have written a function that adds a new column to the attribute table with rank based on an attribute but I don't want to have to add a new column containing the rank for each attribute individually.)

  • 1
    What about @layer_name which returns the name of the current layer? – Joseph Mar 9 '16 at 13:53
  • Thanks, but can you give me some context in how to use @layer_name in this situation? – B_Dabbler Mar 9 '16 at 16:16
  • 1
    Usage would be youfunc(@layer_name, "Field"). You will have to pass it in at this stage. – Nathan W Mar 10 '16 at 5:24
  • I cannot make this work on QGIS 2.18 – Albert Jun 22 '18 at 9:23
  • This still works for me in 2.18.4. Make sure you change the first two lines of the function as shown in my answer below. Also, note that you need to change the quote marks around the field name to single quotes not the default double quotes. – B_Dabbler Jun 25 '18 at 12:50
2

Thanks to Nathan W, I have got the function working properly. This is what I did:

  • I upgraded to QGIS 2.14 (I was on 2.10).
  • I changed the first two lines of the function (the rest remained the same):

    # usage is now: top_rank(@layer_name,'attribute_name')        
    def top_rank(layer, field, feature, parent):
        aLayer = QgsMapLayerRegistry.instance().mapLayersByName(layer)[0]
    

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.