3

I have a number of layers each with a prefix like A_ B_ and so forth.

I'd like to:

  1. Add a column (text) called DENSITY to each shp
  2. Add the prefix letter from the layer name to each row in the DENSITY column for each shp file.

Can it be done?

I'm using QGIS 2.8.1


Update:

I think there was a typo here with a missing +

print "- layer: " child.layerName() + "  ID: " + child.layerId()

and I ran this:

# Iterate through the Legend to find all groups and layers
root = QgsProject.instance().layerTreeRoot()

for child in root.children(): 
    if isinstance(child, QgsLayerTreeGroup):
    print "- group: " + child.name()

# If we find a group, save the prefix name

    prefix = group.name().split('_')[0]
    elif isinstance(child, QgsLayerTreeLayer):
    print "- layer: " + child.layerName() + "  ID: " + child.layerId()
    layer = child.layer()

# Add code to add a field named density to the layer.
#######
# Add code to iterate through each feature in the layer and populate the field with prefix value
#######

but got these errors:

line 4, in root = QgsProject.instance().layerTreeRoot() AttributeError: 'QgsProject' object has no attribute 'layerTreeRoot'

Image if split files:

3
  • 1
    Do you want to do this a automated way or a manual approach will work? It's quite simple to do this manually, but doing it in bulk will require some scripting. Commented Mar 12, 2015 at 10:21
  • Hi. Manually i can do but having 100+ shp files it would be a good excercise to start looking into scripting. Commented Mar 12, 2015 at 10:29
  • Your first task can be done using the Model Builder from the Processing Toolbox and using the Field Calculator algorithm to add a new field. When you save this model, you can right-click on it and run it as a batch process. I agree with @spatialthoughts that for your second task, there will be some scripting involved which you can add to your Model using the Advanced Python field calculator algorithm.
    – Joseph
    Commented Mar 12, 2015 at 10:35

2 Answers 2

3

You can achieve it by running the following code snippet in the QGIS Python console:

from PyQt4.QtCore import QVariant
for layer in QgsMapLayerRegistry.instance().mapLayers().values():
    prefix = layer.name().split("_")[0]
    res = layer.dataProvider().addAttributes([QgsField("density", QVariant.String)])
    layer.updateFields()
    fieldIndex = layer.dataProvider().fieldNameIndex( "density" )
    attrFeatMap = {}
    attrMap = { fieldIndex : prefix }
    for feature in layer.getFeatures():
        attrFeatMap[ feature.id() ] = attrMap
    layer.dataProvider().changeAttributeValues( attrFeatMap )

I assume the only layers you have loaded in the QGIS ToC are those you want to modify. Otherwise, you would need to validate things like the layer type (i.e., you don't want the script to run on raster layers).

The script iterates through layers loaded into QGIS, extracts the prefix from their names, creates the text field density, builds a dictionary like this:

{ featureId : { fieldIndex : value } }

for each feature, and finally, applies the dictionary to change features' density values.

enter image description here

Let me know if something is not clear.

8
  • Hi. I tried the above and got this... Traceback (most recent call last): File "<input>", line 3, in <module> AttributeError: 'QgsRasterDataProvider' object has no attribute 'addAttributes' Commented Mar 13, 2015 at 16:52
  • Ok. My fault here. It was actually working. I had two layers of polygons loaded which arrested the script from running. Thanks alo. Commented Mar 13, 2015 at 17:05
  • As I told you in the answer, if you have other layers loaded in QGIS, you need to filter them out (e.g., via an if like: if layer.type() == 0: right below the for line). Commented Mar 13, 2015 at 17:08
  • Hi. I seem to have a problem with the above after all. I have split a vector shape file by features and have got several vector shape files. Running the script gives me the prefix of the original shape file that i split and not the prefix of the split files. I may have to include a "for each group" after all? Commented Mar 16, 2015 at 8:23
  • Much easier if you just tell us how your layer names look like now and how do you want your layer names to be after running the script. Otherwise, every time you change the original scenario, the solution won't apply anymore. Commented Mar 16, 2015 at 14:07
1

OK. I can point you to code snippets and resources to help you get to your goal.

PyQGIS Cookbook is a great place to look if you are just getting started with scripting.

First you need to iterate through the groups and layers. This is a good reference for it. You can do something like

# Iterate through the Legend to find all groups and layers
root = QgsProject.instance().layerTreeRoot()
for child in root.children(): 
  if isinstance(child, QgsLayerTreeGroup):
    print "- group: " + child.name()
    # If we find a group, save the prefix name
    prefix = group.name().split('_')[0]
  elif isinstance(child, QgsLayerTreeLayer):
    print "- layer: " child.layerName() + "  ID: " + child.layerId()
    layer = child.layer()
    # Add code to add a field named density to the layer.
    #######
    # Add code to iterate through each feature in the layer and populate the field with prefix value
    #######

You will find the code snippets to add a field is at Adding and Removing Fields

And snippet for iterating through the features is at Iterating over Vector Layer

4
  • OK.This sounds great and i will have something to dive into. I keep you posted... Commented Mar 12, 2015 at 10:51
  • The last section regarding iteration. Maybe it's me but i fail to see why it should go through each feature in the layers unless that means "every row" Commented Mar 12, 2015 at 11:08
  • Ok. This is a bit out of my league. cant get it to run... Commented Mar 12, 2015 at 12:31
  • Hi.I tried this: Commented Mar 13, 2015 at 9:54

Not the answer you're looking for? Browse other questions tagged or ask your own question.