Advertisements

Previously, we created layers by loaded existing datasets into our project. In this example, we will create a new layer from scratch.

To get started, let’s create a new QgsVectorLayer object for point geometries with the layer name “temp” and a memory data provider:

vl = QgsVectorLayer("Point", "temp", "memory")

Then we can add some attribute fields using the data provider’s addAttributes() function:

from qgis.PyQt.QtCore import QVariant
pr = vl.dataProvider()
pr.addAttributes([QgsField("name", QVariant.String),
                  QgsField("age",  QVariant.Int),
                  QgsField("size", QVariant.Double)])
vl.updateFields() 

A common pattern in this example is “update-after-change”. When making changes to a layer’s data provider, these changes are not automatically reflected by the layer. Therefore, we need to call the vector layer’s updateFields() after adding the attributes.

With the layer set up, we can add a point feature and add the layer to the project:

f = QgsFeature()
f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
f.setAttributes(["Ada L.", 2, 0.3])
pr.addFeature(f)
vl.updateExtents() 
QgsProject.instance().addMapLayer(vl)

Similar to before, here we need to call updateExtents() after adding features to the data provider. Otherwise the layer’s extent will be incorrect.

Let’s have a look at some layer statistics now:

print("No. fields:", len(pr.fields()))
print("No. features:", pr.featureCount())
e = vl.extent()
print("Extent:", e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())

for f in vl.getFeatures():
    print("Feature:", f.id(), f.attributes(), f.geometry().asPoint())

This should print:

No. fields: 3
No. features: 1
Extent: 10.0 10.0 10.0 10.0
Feature: 1 ['Ada L.', 2, 0.3] <QgsPointXY: POINT(10 10)>

So far so good! But what if we decide that we want to add another attribute? Luckily QgsVectorLayers have a handy addAttribute() function. The documentation notes:

Calls to addAttribute() are only valid for layers in which edits have been enabled by a call to startEditing(). Changes made to features using this method are not committed to the underlying data provider until a commitChanges() call is made. Any uncommitted changes can be discarded by calling rollBack().

Ok, let’s do that! Pay attention to the combination of addAttribute() and updateFields().

vl.startEditing()

my_field_name = 'new field'
vl.addAttribute(QgsField(my_field_name, QVariant.String))
vl.updateFields()

for f in vl.getFeatures():
    print("Feature:", f.id(), f.attributes(), f.geometry().asPoint())

This should print:

Feature: 1 ['Ada L.', 2, 0.3, None] <QgsPointXY: POINT(10 10)>

So there is a new attribute field. We can populate it by assigning our desired value to the feature’s field. Again, we have to follow the update-after-changes pattern, this time using updateFeature():

my_field_value = 'Hello world!'
for f in vl.getFeatures():
    f[my_field_name] = my_field_value
    vl.updateFeature(f)

vl.commitChanges()

for f in vl.getFeatures():
    print("Feature:", f.id(), f.attributes(), f.geometry().asPoint())

Finally, this prints:

Feature: 1 ['Ada L.', 2, 0.3, 'Hello world!'] <QgsPointXY: POINT(10 10)>

Now, the only thing left to do is to stop the editing session:

iface.vectorLayerTools().stopEditing(vl) 

These are the basic steps to create a new layer from scratch. You saw how to define attribute fields and add features. Finally, we modified the layer and added another attribute field.


PyQGIS 101 is a work in progress. I’d appreciate any feedback, particularly from beginners!

Advertisements
2 comments
  1. Dear Anita,

    thanks for sharing your code.

    I’m trying to add two fields to an existing shapefile and populate it. Here below the chunk of code:

    # receiver_feat_new_fields is a dict containing the data to populate
    receiver_path='/home/pierluigi/Scrivania/dataset_for_testing/ricevitori.shp'
    receiver_layer=QgsVectorLayer(receiver_path,'ricevitore',"ogr")
    
    fields_number = int(receiver_layer.fields().count())
    
    level_field_index = {}
    print("No. fields before edit:", len(receiver_layer.fields()))
    
    #### Here I have 2 field ####
    
    receiver_layer.startEditing()
    
    #add fields
    receiver_layer.addAttribute(QgsField('gen', QVariant.Double, len=5, prec=1))
    level_field_index['gen'] = fields_number
    fields_number = fields_number + 1
    
    receiver_layer.addAttribute((QgsField('day', QVariant.Double, len=5, prec=1)))
    level_field_index['day'] = fields_number
    fields_number = fields_number + 1
    
    #updatefields
    receiver_layer.updateFields()
    
    #insert attribute data in table
    for f in receiver_layer.getFeatures():
        if 'gen' in level_field_index:
            f['gen'] = receiver_feat_new_fields[f.id()][level_field_index['gen']]
        if 'day' in level_field_index:
            f['day'] = receiver_feat_new_fields[f.id()][level_field_index['day']]
        receiver_layer.updateFeature(f)
    receiver_layer.updateExtents() 
    
    #print some info for debug
    print("No. fields:", len(receiver_layer.fields()))
    print("No. features:", receiver_layer.featureCount())
    e = receiver_layer.extent()
    print("Extent:", e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())
    
    ## Here I have 4 field and it seems working correctly  ###
    
    receiver_layer.commitChanges()
    
    for f in receiver_layer.getFeatures():
        print("Feature:", f.id(), f.attributes(), f.geometry().asPoint())
    
    ## I can see all my data correcly -- It looks nice ###
    
    iface.vectorLayerTools().stopEditing(receiver_layer)
    

    Here the software ask me to stop edit.

    Is it possible to avoid such popup but directly accept the edits?

    Now the problem is that even if I look at the table, it seem that is not changed but simply pressing the “pencil” edit button and closing edit and the table magically appear correctly.

    Where I’m wrong?

    Thanks
    Pierluigi

    • Hi Pierluigi,

      Which QGIS version are you using?
      I tried to reproduce your issue in 3.5. I don’t get the popup, even though it is mentioned in the documentation https://qgis.org/pyqgis/master/core/Vector/QgsVectorLayerTools.html?highlight=stopediting#qgis.core.QgsVectorLayerTools.stopEditing

      The following code works for me:

      QgsVectorFileWriter.writeAsVectorFormat(vl, "C:/temp/pt.gpkg", "CP1250", vl.crs())
      tmp=QgsVectorLayer("C:/temp/pt.gpkg",'tmp',"ogr")
      QgsProject.instance().addMapLayer(tmp)
      
      tmp.startEditing()
      
      tmp.addAttribute(QgsField('gen', QVariant.Double, len=5, prec=1))
      tmp.addAttribute(QgsField('day', QVariant.Double, len=5, prec=1))
      tmp.updateFields()
      
      for f in tmp.getFeatures():
          f['gen'] = 1.1
          f['day'] = 5.2
          tmp.updateFeature(f)
          
      tmp.commitChanges()
      
      for f in tmp.getFeatures():
          print("Feature:", f.id(), f.attributes(), f.geometry().asPoint())
          
      iface.vectorLayerTools().stopEditing(tmp)
      

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: