Index de l'article

Edit layers

Add a point in a shape

It is like projecting points. You have another full example about how to do it in a loop and from an API here (search Autres exemples d'APIs).

Just below a simple example.

(Attention to &)

myPath = r'C:\\Users\\georg\\Downloads\\'
 
project = QgsProject.instance()
project.removeAllMapLayers()
project.clear()
iface.mapCanvas().refresh()
 
myLayer = QgsVectorLayer(myPath + 'peaks_selection/peaks_selection.shp', 'Peaks', 'ogr')
project.addMapLayer(myLayer)
 
crs = QgsCoordinateReferenceSystem.fromEpsgId(4326)
project.setCrs(crs)
project.setEllipsoid('EPSG:4326')
 
# Add a point
myPoints = project.mapLayersByName('Peaks')[0]
aPoint = QgsVectorLayer(myPoints.dataProvider().dataSourceUri(), '', 'ogr')
caps = aPoint.dataProvider().capabilities()
 
if caps & QgsVectorDataProvider.AddFeatures:
    feat = QgsFeature(aPoint.fields())
    feat.setAttributes([1, 'Fake peak'])
    feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(6.0631, 44.8844)))
    res, outFeats = aPoint.dataProvider().addFeatures([feat])

Pack a shape/commit your edits

When you write a lot of features in a shape, or after deletion, it is good to pack your shape (to avoid some blank lines in the .dbf file for example). It is a kind of commit.

For example, if after some deletions, you remove your layer from QGIS, then you re-open it, and then then your do new changes, if you check the attributes table you will see some empty blank lines.

We will use GDAL/OGR and some SQL to pack our layer, with a id field named here OSM_ID:

import subprocess
 
myOriginalShape = myPath + 'peaks_selection/peaks_selection.shp'
myTempShape = myPath + 'peaks_selection/temp_peaks_selection.shp'
 
# Pack with GDAL/OGR filtering with SQL
cmd = [
    'ogr2ogr', '-f', 'ESRI Shapefile',
    myTempShape, myOriginalShape,
    '-sql', 'SELECT * FROM peaks_selection WHERE OSM_ID IS NOT NULL'
]
subprocess.run(cmd)

And here a full example to edit, pack, rename and see the last edits:

(Attention to &)

import subprocess
import time
 
myPath = r'C:\\Users\\georg\\Downloads\\'
 
myOriginalShape = myPath + 'peaks_selection/peaks_selection.shp'
myTempShape = myPath + 'peaks_selection/temp_peaks_selection.shp'
 
project = QgsProject.instance()
project.removeAllMapLayers()
project.clear()
iface.mapCanvas().refresh()
 
myLayer = QgsVectorLayer(myOriginalShape, 'Peaks', 'ogr')
myLayer.setProviderEncoding('ISO-8859-1')
 
project.addMapLayer(myLayer)
crs = QgsCoordinateReferenceSystem.fromEpsgId(4326)
project.setCrs(crs)
project.setEllipsoid('EPSG:4326')
 
# Add a point
def addPoint():
    myPoints = project.mapLayersByName('Peaks')[0]
    aPoint = QgsVectorLayer(myPoints.dataProvider().dataSourceUri(), '', 'ogr')
    caps = aPoint.dataProvider().capabilities()
 
    if caps & QgsVectorDataProvider.AddFeatures:
        feat = QgsFeature(aPoint.fields())
        feat.setAttributes([1, 'Fake peak'])
        feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(6.0631, 44.8844)))
        res, outFeats = aPoint.dataProvider().addFeatures([feat])
 
QTimer.singleShot(1000, addPoint)
 
# Then pack/commit
def myPacking():
    cmd = [
        'ogr2ogr', '-f', 'ESRI Shapefile',
        myTempShape, myOriginalShape,
        '-sql', 'SELECT * FROM peaks_selection WHERE OSM_ID IS NOT NULL'
    ]
    subprocess.run(cmd)
 
QTimer.singleShot(200, myPacking)
 
# Clear project
project = QgsProject.instance()
project.removeAllMapLayers()
project.clear()
iface.mapCanvas().refresh()
 
# Then delete/rename
def myDeleteRename():
    for ext in ['.shp', '.shx', '.dbf', '.prj']:
        old_file = myOriginalShape.replace('.shp', ext)
        new_file = myTempShape.replace('.shp', ext)
 
    if os.path.exists(old_file):
        os.remove(old_file)
 
    os.rename(new_file, old_file)
 
QTimer.singleShot(200, myDeleteRename)
 
# Clear project
project = QgsProject.instance()
project.removeAllMapLayers()
project.clear()
iface.mapCanvas().refresh()
 
# Then reopen the layer updated
def myReopen():
    myLayer = QgsVectorLayer(myOriginalShape, 'Peaks', 'ogr')
    myLayer.setProviderEncoding('ISO-8859-1')
    project.addMapLayer(myLayer)
 
    crs = QgsCoordinateReferenceSystem.fromEpsgId(4326)
    project.setCrs(crs)
    project.setEllipsoid('EPSG:4326')
 
QTimer.singleShot(200, myReopen)
 
# Final refresh to see the last edits
time.sleep(1)
QgsProject.instance().reloadAllLayers()

Add fields in a layer

myLayer = QgsVectorLayer(r'C:/Users/Georges/Downloads/my layer/new_file.tab', 'new file', 'ogr')
myLayerInstance = QgsProject.instance().mapLayersByName('new file')[0]
 
myLayerInstance.startEditing()
 
myLayerInstance.dataProvider().addAttributes([QgsField("newfield1", QVariant.String), QgsField("newfield2", QVariant.String)])
 
myLayerInstance.updateFields()
 
myLayerInstance.commitChanges()

Delete a field

myLayer.dataProvider().deleteAttributes(['My field'])

Fill a field

myLayerInstance = QgsProject.instance().mapLayersByName('new file')[0]
 
with edit(myLayerInstance):
    for feature in myLayerInstance.getFeatures():
        # A VALUE
        feature['newfield1'] = 'Abc Xyz'
 
        # CONCATENATE OTHER FIELD
        feature['newfield2'] = feature['name'] + '-' + feature['id']
 
        myLayerInstance.updateFeature(feature)

Create a shape

layer = QgsProject.instance().mapLayersByName('peaks')[0]
layer.selectByExpression('"NAME" LIKE \'Aiguille%\'')
 
root = r'C:/Users/georg/Downloads/Aiguilles/'
if not os.path.exists(root):
    os.makedirs(root)
 
file = str(root)+'Aiguilles.shp'
writer = QgsVectorFileWriter.writeAsVectorFormat(layer, file, 'utf-8', driverName='ESRI Shapefile', onlySelected=True)