6

I am following the QGIS Cookbook and this post where it concerns reading attributes from layers. I would like to exploit this method and implement it into a standalone script.

Essentially, I want to read the first feature of a shapefile from a field called Rank, take the value of this feature (all values in the Rank field are the exact same) and include it into a QGIS algorithm.

I wonder if there's an alternative to using layer.getFeatures() as, I believe, this is used with the canvas when QGIS is running. I have the following basic script but doesn't like fname.getFeatures():

path_dir = home + "\Desktop\Test\\"
path_res = path_dir + "Results\\"

for fname in glob.glob(path_dir + "*.shp"):
    for feature in fname.getFeatures():
        fid = 1
        iterator = fname.getFeatures(QgsFeatureRequest().setFilterFid(fid))
        idx = fname.fieldNameIndex('Rank')
        Rank_value = idx[1]

        #Do some processing, finish with field calculator algorithm

        general.runalg("qgis:fieldcalculator", fname, 'Rank', 1, 10, 0, False, Rank_value, path_res + fname)

Is this the correct method or would it be much easier to read the Rank_value from a shapefile and write to another programatically?


EDIT:

Following @gcarrillo's answer, the script does not produce any results. I have modified the script to match the one suggested but not sure why no output files are produced.

To try and clarify exactly what I'm looking for:

  1. A value from the Rank field of a shapefile is read.
  2. A grid is created.
  3. The same shapefile is clipped onto the grid (it would then have more polygons, thus more features).
  4. The Rank field for this newly clipped shapefile is updated with the value read at the start.
  5. Next shapefile...

    path_dir = home + "\Desktop\Test\\"
    path_res = path_dir + "Results\\"
    
    for fname in glob.glob(path_dir + "*.shp"):
        vLayer = QgsVectorLayer(fname,"any name","ogr")
        idx = fname.fieldNameIndex('Rank')
        Rank_value = vLayer.uniqueValues(idx)[0]
    
        outputs_1=general.runalg("qgis:creategrid", 1000, 1000, 24108, 18351.157175, 258293.802316, 665638.226408, 1, 'EPSG:7405', None)
        outputs_2=general.runalg("qgis:clip", outputs_1['SAVENAME'], fname, None)
        outputs_3=general.runalg("qgis:fieldcalculator", outputs_2, 'Rank', 1, 10, 0, False, Rank_value, path_res + fname)
    
3
+150

I've tested the following script on GNU/Linux, QGIS v.2.8.1, Processing v.2.6 and Processing v.2.9.1.

import sys,os,glob
from qgis.core import *
from PyQt4.QtGui import *

app = QApplication([])
QgsApplication.setPrefixPath("/usr", True) # Adjust it to your path
QgsApplication.initQgis()

path_dir = "/docs/borrar/test/"
path_res = path_dir + "results/"

sys.path.append("/usr/share/qgis/python/plugins/") # Where Processing is installed
from processing.core.Processing import Processing
Processing.initialize()
from processing.tools import *

for fname in glob.glob(path_dir + "*.shp"):
    vLayer = QgsVectorLayer(fname,"any name","ogr")
    idx = vLayer.fieldNameIndex('rank')
    Rank_value = vLayer.uniqueValues(idx)[0] # uV returns a list, get the 1st element

    #Do some processing, finish with field calculator algorithm
    outputs_1=general.runalg("qgis:creategrid",1,"-956314.538361,3285493.72235,-53712.3143749,1885220.75154",250000,250000,None)
    outputs_2=general.runalg("qgis:clip", outputs_1['OUTPUT'], fname, None)
    general.runalg("qgis:advancedpythonfieldcalculator", outputs_2['OUTPUT'], 'rank', 1, 10, 3, False, "value=%f" % Rank_value, path_res + os.path.basename(fname))

QgsApplication.exitQgis()

As you can see, the syntax of the qgis:creategrid algorithm differs from your version and from the version we ran at Import error for qgis.core when running OSGeo4w shell script. Additionally, I ended up using the qgis:advancedpythonfieldcalculator algorithm, which provides a help.

Hope you can make it to work in your environment, pay attention to paths.

  • Modified your script to work with my environment and it works =). Thank you very much @gcarrillo! I have one minor question regarding the advanced python field calculator algorithm: if a Rank field already exists, can it be updated by adding it to the formula (eg. "value=%f(<Rank>)" % Rank_value? If this requires a bit more code then I will research it before asking another question. Thanks again! – Joseph Apr 10 '15 at 11:04
  • 1
    That's great! Regarding the question, I wasn't able to update a field. I'd have a look at the documentation of the algorithm or first try inside QGIS and see how the syntax looks like before writing it in a script. – Germán Carrillo Apr 10 '15 at 12:26
  • 1
    Many thanks! This isn't really a necessity but I will look into the documentation and will try it inside QGIS. Fun to test out =). Will award you the bounty in 2 hours :) – Joseph Apr 10 '15 at 12:32
3

For the first question:

You need to create a QgsVectorLayer object from your Shapefile path. Only then you can access the getFeatures() function. That is:

for fname in glob.glob(path_dir + "*.shp"):
    for feature in QgsVectorLayer(fname,"any name","ogr").getFeatures():

My recommended way of doing it

You don't actually need to access getFeatures() nor to use a for. You can accomplish what you want with this code:

path_dir = home + "\Desktop\Test\\"
path_res = path_dir + "Results\\"

for fname in glob.glob(path_dir + "*.shp"):
    vLayer = QgsVectorLayer(fname,"any name","ogr")
    idx = vLayer.fieldNameIndex('Rank')
    Rank_value = vLayer.uniqueValues(idx)[0] # uV returns a list, get the 1st element

    #Do some processing, finish with field calculator algorithm
    general.runalg("qgis:fieldcalculator", fname, 'Rank', 1, 10, 0, False, Rank_value, path_res + os.path.basename(fname))

Since you'll be using QgsVectorLayer, don't forget to set the prefix path before initializing QGIS, as you did at Getting random values from geometryType() in a standalone PyQGIS script.

  • Awesome, many thanks for this @gcarrillo! Not sure why but no output is being produced. All the prefix paths are set, testing this with a single shapefile but still no result. Strange, still testing this. – Joseph Apr 9 '15 at 11:41
  • The error was on my part (ofcourse!), your script works great! Thank you very much =) – Joseph Apr 9 '15 at 12:26
  • Urgh, my sincerest apologies. Initially, I thought the script worked but later noticed the times when the files were modified which didn't change when running the script. There are still no shapefiles being produced. – Joseph Apr 9 '15 at 14:44

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.