I'm trying to run an NDVI calculation on a set of raster layers. The approach is to adapt this QGIS 2.x code to run in GQIS 3.x. A far as I'm aware it's the "legendInterface" that's changed, but I'm getting lost trying to suss out how it changed, and what to use as an alternative.

lddLrs = qgis.utils.iface.legendInterface().layers()
path = "/QGIS/Project/"
for lyr in lddLrs:
    entries = []
    #Define red band#
    ras1 = QgsRasterCalculatorEntry()
    ras1.ref = 'layer.name() +@121'
    ras1.raster = lyr
    ras1.bandNumber = 121
    #Define nir band#
    ras2 = QgsRasterCalculatorEntry()
    ras2.ref = 'layer.name() +@192'
    ras2.raster = lyr
    ras2.bandNumber = 192
    entries.append( ras2 )
    calc = QgsRasterCalculator( '("layer.name() +@192" -  "layer.name() 
   +@121") / ("layer.name() +@192" + "layer.name() +@121")', path + lyr.name() 
   + "_NDVI.tif", 'GTiff', lyr.extent(), lyr.width(), lyr.height(), 
   entries )

2 Answers 2


Firstly, to update the first line of your code, I would suggest something like this to collect all raster layer objects in the current project:

lddLrs = [l for l in QgsProject().instance().mapLayers().values() if isinstance(l, QgsRasterLayer)]

Secondly, unless I am missing something, it looks like you are referencing a variable "layer" e.g. layer.name() which is undefined. You define a variable lyr when you construct your for loop- this is what will store the layer object on each iteration of the loop.

Therefore, I think you should change layer.name() to lyr.name() throughout your code.

Also, creating a string like: ras1.ref = 'layer.name() +@121' will not work. You should use string formatting such as:

ras1.ref = '{}@121'.format(lyr.name())

I have not tested your script, but it looks like it should work with those changes.

  • Okay Ben, thanks for the input. Close, but no cigar just yet. Your first suggestion seems good, but I'm not sure the string formatting was totally right. It threw up an error about getting an "str" when it expected "QgsRasterCalculatorEntry" So I changed your suggestion ras1.ref = '{}@121'.format(lyr.name()) to r.ref = 'r@121'.format(lyr.name()) and no more errors.... only I'm not getting any output that I can see; just the temp .py file generated.
    – Ricardo
    Jun 24, 2020 at 12:13

Okay, sussed it thanks to Ben and others on this thread: QGIS python script loop for raster calculator

It looks like the references to the layers in the calculation weren't needed, and my pathway wasn't quite right.

Anyone confused and looking to batch calculate NDVIs in QGIS 3x, this worked for me:

from qgis.analysis import QgsRasterCalculatorEntry, QgsRasterCalculator
from qgis.core import QgsProject

lddLrs = lddLrs = [l for l in QgsProject().instance().mapLayers().values() if isinstance(l, QgsRasterLayer)]
path = "/desired/output/location"
for lyr in lddLrs:
    entries = []
    r = QgsRasterCalculatorEntry()
    ir = QgsRasterCalculatorEntry()
    r.ref = 'r@121'
    ir.ref = 'ir@192'
    r.raster = lyr
    ir.raster = lyr
    r.bandNumber = 121
    ir.bandnumber = 192
    entries.append( r )
    entries.append( ir )
    calc = QgsRasterCalculator( '(ir@192 - r@121) / (ir@192 + r@121)', path + lyr.name() + "_c3_ndvi.tif", 'GTiff', lyr.extent(), lyr.width(), lyr.height(), entries )
  • 1
    Glad you got it figured out. Just to clarify- you can get rid of the .format(lyr.name()) and just leave it as r.ref = 'r@121'. Same for ir.ref = 'ir@192'. Unless you use a {} place holder, it's doing absolutely nothing! Evidently it's not needed here so that part of my answer is not useful. See: w3schools.com/python/ref_string_format.asp to understand how string formatting in Python works.
    – Ben W
    Jun 25, 2020 at 6:10
  • Okay, nice one, thanks Ben. Trying to run before I can walk!
    – Ricardo
    Jun 25, 2020 at 11:59
  • Okay, so I jumped the gun with that last comment! The outputs from this calculation are wrong. The statistics are way off and the output images are all really dark and grainy... any idea what could be wrong?
    – Ricardo
    Jul 12, 2020 at 21:49

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.