Some PyQGIS tips, from the most common to the most practical, to learn or optimize your use of QGIS with Python.
Some links:
- Advanced PyQGIS tutorial
- Full code to generate maps in a loop
- Tips on PyQGIS in standalone mode
- PyQGIS Developer Cookbook
Open layers
Open some shapes
# Main path myPath = r'C:\\Users\\Georges\\Downloads\\' project = QgsProject.instance() project.removeAllMapLayers() project.clear() iface.mapCanvas().refresh() # Path of layers peaks = QgsVectorLayer(myPath + 'peaks_selection/peaks_selection.shp', 'Sommets', 'ogr') glaciers = QgsVectorLayer(myPath + 'glaciers/glaciers.shp', 'Glaciers', 'ogr') eau = QgsVectorLayer(myPath + 'eau/eau.shp', 'Fleuves', 'ogr') troncons_routes = QgsVectorLayer(myPath + 'troncons_routes/troncons_routes.shp', 'Grandes routes', 'ogr') iris = QgsVectorLayer(myPath + 'iris/iris.shp', 'IRIS', 'ogr') # Open layers project.addMapLayer(peaks) project.addMapLayer(glaciers) project.addMapLayer(eau) project.addMapLayer(troncons_routes) project.addMapLayer(iris) iface.mapCanvas().refresh()
Open OSM
(Attention to the &)
project = QgsProject.instance() urlWithParams = "type=xyz&url=http://tile.openstreetmap.org/{z}/{x}/{y}.png" osm = QgsRasterLayer(urlWithParams, "OpenStreetMap", "wms") project.addMapLayer(osm) osm.setOpacity(0.75) iface.mapCanvas().refresh()
As we open OSM, which use a specific projection (3857), better to define and force the projection of the project, and the ellipsoid:
project = QgsProject.instance() ... crs = QgsCoordinateReferenceSystem.fromEpsgId(4326) project.setCrs(crs) project.setEllipsoid('EPSG:4326')
Open a Geojson
Or a GML...
myPath = r'C:\\Users\\georg\\Downloads\\' project = QgsProject.instance() eau = QgsVectorLayer(myPath + 'MyGeoJson.geojson', 'Rivières', 'ogr') project.addMapLayer(eau)
Open a Geojson online
iface.addVectorLayer('https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_populated_places.geojson', 'Populated places', 'ogr')
Change a layer's encoding
myLayer = QgsVectorLayer(r'C:/Users/Georges/Downloads/my layer/new_file.tab', new_file, 'ogr') myLayer.setProviderEncoding('ISO-8859-1')
Open a CSV
csv_path = 'file:///' + myPath + 'World Stats.csv?delimiter=;' my_csv = QgsVectorLayer(csv_path, 'Countries', 'delimitedtext') project.addMapLayer(my_csv)
Analyze layers
Count layer fields
project = QgsProject.instance() myLayer = project.mapLayersByName("My layer")[0] print(myLayer.fields().count())
Count layer features
project = QgsProject.instance() myLayer = project.mapLayersByName("Adresses")[0] layerNumberFeatures = myLayer.featureCount() print(layerNumberFeatures)
Display layer fields names
project = QgsProject.instance() myLayer = project.mapLayersByName("My layer")[0] print(myLayer.fields().names())
Get layer projection
project = QgsProject.instance() myLayer = project.mapLayersByName("Adresses")[0] layerProj = myLayer.crs() print(layerProj)
Check if a layer is valid
myLayer = QgsVectorLayer(monRepertoire, 'My layer', 'ogr') if myLayer.isValid(): print('My layer', 'is valid')
Manage layers
Remove all the layers from the QGIS canvas
project = QgsProject.instance() project.removeAllMapLayers() project.clear() iface.mapCanvas().refresh()
Clone a layer
oldLayer = project.mapLayersByName("Old layer")[0] newLayer = oldLayer.clone() newLayer.setName('New layer') project.addMapLayer(newLayer)
Rename a layer
mes_sommets.setName('New name')
Copy-paste a layer with all his files (tab, shp ...) and open them
If you have a layer named test in a directory named my layer, you can create a copy named new file in the same directory and open them in QGIS:
import os import shutil QgsProject.instance().removeAllMapLayers() iface.mapCanvas().refresh() directory = r'C:/Users/Georges/Downloads/my layer' new_file = 'new file' # REMOVE NEW LAYER IF EXISTS files = os.listdir(directory) for f in files: if f.startswith(new_file): try: os.remove(directory + '/' + f) except: pass # COPY LAYER files = os.listdir(directory) for f in files: if not f.startswith(new_file): shutil.copyfile(directory + '/' + f, directory + '/' + new_file + '.' + f.split('.')[1]) print('Layer copied!') test = QgsVectorLayer(directory + '/test.tab', 'test', 'ogr') QgsProject.instance().addMapLayer(test) new = QgsVectorLayer(directory + '/' + new_file + '.tab', 'new file', 'ogr') QgsProject.instance().addMapLayer(new)
Change a layer's encoding
myLayer = QgsVectorLayer(r'C:/Users/Georges/Downloads/my layer/new_file.tab', new_file, 'ogr') myLayer.setProviderEncoding('ISO-8859-1')
Create a group and put a layer into
directory = r'C:/Users/Georges/Downloads/my layer' newfile = QgsVectorLayer(directory + '/new file.tab', 'newfile', 'ogr') QgsProject.instance().addMapLayer(newfile) groupName = 'Mon joli groupe' root = QgsProject.instance().layerTreeRoot() group = root.addGroup(groupName) layer = QgsProject.instance().mapLayersByName('newfile')[0] mylayer = root.findLayer(layer.id()) myClone = mylayer.clone() parent = mylayer.parent() group = root.findGroup('Mon joli groupe') group.insertChildNode(0, myClone) parent.removeChildNode(mylayer)
Hide the rasters bands
root = QgsProject.instance().layerTreeRoot() for layer in QgsProject.instance().mapLayers().values(): if layer.type() == QgsMapLayerType.RasterLayer: LayerNode = root.findLayer(layer.id()) LayerNode.setExpanded(False)
Uncheck a layer
project = QgsProject.instance() myLayer = project.mapLayersByName('My layer')[0] project.layerTreeRoot().findLayer(myLayer.id()).setItemVisibilityCheckedParentRecursive(False)
Transform a layer projection
This is not a reprojection, which need a processing.run
, here below it is just a transformation, which can be unefficient according your context.
project = QgsProject.instance() myLayer = project.mapLayersByName("My layer")[0] my_projection = QgsCoordinateReferenceSystem(4326) myLayer.setCrs(my_projection) my_transform = QgsCoordinateTransform(myLayer.crs(), iface.mapCanvas().mapSettings().destinationCrs(), project) myLayerExtent = my_transform.transform(myLayer.extent()) iface.mapCanvas().setExtent(myLayerExtent) iface.mapCanvas().refresh()
Manage the layers display order
firstLayer = project.mapLayersByName("First layer")[0] secondLayer = project.mapLayersByName("Second layer")[0] thirdLayer = project.mapLayersByName("Third layer")[0] root = project.layerTreeRoot() root.setHasCustomLayerOrder (True) order = root.customLayerOrder() order.insert(0, order.pop(order.index(firstLayer))) order.insert(1, order.pop(order.index(secondLayer))) order.insert(2, order.pop(order.index(thirdLayer))) root.setCustomLayerOrder(order)
Another way cloning the layer:
myLayer = QgsVectorLayer(myPathLayer, "Buffer area", "ogr") project.addMapLayer(peaks_buffer) root = project.layerTreeRoot() myBelowLayer = root.findLayer(myLayer.id()) myClone = myBelowLayer.clone() parent = myBelowLayer.parent() parent.insertChildNode(-1, myClone) parent.removeChildNode(myBelowLayer)
Zoom on a layer
countries = QgsVectorLayer(myPath + 'simple_countries/simple_countries.shp', 'Countries', 'ogr') extent_countries = countries.extent() iface.mapCanvas().setExtent(extent_countries) iface.mapCanvas().refresh()
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)
Filter/Select
Select features
myPath = r'C:\\Users\\georg\\Downloads\\' project = QgsProject.instance() project.removeAllMapLayers() project.clear() iface.mapCanvas().refresh() myLayer = QgsVectorLayer(myPath + 'peaks_selection/peaks_selection.shp', 'Sommets', 'ogr') QgsProject.instance().addMapLayer(myLayer) # SELECT myExpr = u"NAME LIKE '{}'".format('Grand Pic de la Meije') mySelect = myLayer.getFeatures(QgsFeatureRequest().setFilterExpression(myExpr)) myLayer.selectByIds([f.id() for f in mySelect])
Select function
def SelectAndZoom(layername, field, value): mylayer = QgsProject.instance().mapLayersByName(layername)[0] myselect = mylayer.getFeatures( QgsFeatureRequest().setFilterExpression ( u'"%s" = \'%s\'' % (field, value)) ) mylayer.selectByIds( [ f.id() for f in myselect ] ) iface.mapCanvas().zoomToSelected(mylayer)
Then use it like that:
SelectAndZoom('My layer', 'My field', 'Some value')
Select features after click on another layer
layer_selected = QgsProject.instance().mapLayersByName("decathlon_france")[0] layer_to_select = QgsProject.instance().mapLayersByName("iso_iris")[0] def SelectionAuto(): selected_features = layer_selected.selectedFeatures() for i in selected_features: attrs = i.__geo_interface__ id_mag = i['id'] # print (id_mag) myselect = layer_to_select.getFeatures( QgsFeatureRequest().setFilterExpression ( u'"id_mag" = \'%s\'' % id_mag ) ) layer_to_select.selectByIds( [ f.id() for f in myselect ] ) # iface.mapCanvas().zoomToSelected(layer_to_select) layer_selected.selectionChanged.connect(SelectionAuto)
Filter a layer
myFilter = u"field_a <> '{}'".format('A value') myLayer.setSubsetString(myFilter)
Remove a filter
myLayer.setSubsetString('')
Temporary layers
Add a temporary layers
point_vector = QgsVectorLayer("Point", "my_points", "memory") project.addMapLayer(point_vector)
Add fields
from qgis.PyQt.QtCore import QVariant ... pr = point_vector.dataProvider() pr.addAttributes([QgsField("id", QVariant.Int), QgsField("name", QVariant.String)]) point_vector.updateFields()
Add a point
f = QgsFeature() f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(7.7633340, 48.5785590))) f.setAttributes([8, 'My 8']) pr.addFeature(f) point_vector.updateExtents() project.addMapLayer(point_vector) iface.mapCanvas().refresh()
Symbologies
Simple symbology on points
myLayer = QgsProject.instance().mapLayersByName("My points")[0] symbol_peak = QgsMarkerSymbol.createSimple({'name': 'Triangle', 'color': 'green', 'outline_color': 'black', 'size': '4'}) myLayer.renderer().setSymbol(symbol_peak) myLayer.triggerRepaint() iface.layerTreeView().refreshLayerSymbology(myLayer.id()) iface.mapCanvas().refresh()
Other point markers:
circle square cross rectangle diamond pentagon triangle equilateral_triangle star regular_star arrow filled_arrowhead x
Use PNG on points
You can build your own picture as PNG and use it in symbology with QgsRasterMarkerSymbolLayer
:
myPath = r'C:\\Users\\georg\\Downloads\\' project = QgsProject.instance() project.removeAllMapLayers() project.clear() iface.mapCanvas().refresh() # Path of layers peaks = QgsVectorLayer(myPath + 'peaks_selection/peaks_selection.shp', 'Peaks', 'ogr') project.addMapLayer(peaks) myLayer = QgsProject.instance().mapLayersByName("Peaks")[0] # Use a SVG in symbology myPicture = myPath + 'flower.png' symbolLayer = QgsRasterMarkerSymbolLayer(myPicture) symbolLayer.setSize(10) symbol_peak = QgsMarkerSymbol.createSimple({}) symbol_peak.changeSymbolLayer(0, symbolLayer) myLayer.renderer().setSymbol(symbol_peak) myLayer.triggerRepaint() iface.layerTreeView().refreshLayerSymbology(myLayer.id()) iface.mapCanvas().refresh()
Use SVG on points
Use QgsSvgMarkerSymbolLayer
, but attention, SVG is a very specific format, maybe prefer PNG.
myPath = r'C:\\Users\\georg\\Downloads\\' project = QgsProject.instance() project.removeAllMapLayers() project.clear() iface.mapCanvas().refresh() # Path of layers peaks = QgsVectorLayer(myPath + 'peaks_selection/peaks_selection.shp', 'Peaks', 'ogr') project.addMapLayer(peaks) myLayer = QgsProject.instance().mapLayersByName("Peaks")[0] # Use a SVG in symbology mySvg = 'C:/Program Files/QGIS 3.28.13/apps/qgis-ltr/resources/themes/Night Mapping/icons/eye.svg' svg_symbol_layer = QgsSvgMarkerSymbolLayer(mySvg) svg_symbol_layer.setSize(10) symbol_peak = QgsMarkerSymbol.createSimple({}) symbol_peak.changeSymbolLayer(0, svg_symbol_layer) myLayer.renderer().setSymbol(symbol_peak) myLayer.triggerRepaint() iface.layerTreeView().refreshLayerSymbology(myLayer.id()) iface.mapCanvas().refresh()
Simple symbology on lines
With HTML color.
myroads = QgsProject.instance().mapLayersByName("Roads")[0] myrivers = QgsProject.instance().mapLayersByName("Rivers")[0] # Roads symbol = QgsLineSymbol.createSimple({'line_style': 'dash', 'line_width': '0.5', 'color': 'black'}) myroads.renderer().setSymbol(symbol) myroads.triggerRepaint() iface.layerTreeView().refreshLayerSymbology(myroads.id()) # Rivers symbol = QgsLineSymbol.createSimple({'line_style': 'solid', 'line_width': '0.75', 'color': '#0088CC'}) myrivers.renderer().setSymbol(symbol) myrivers.triggerRepaint() iface.layerTreeView().refreshLayerSymbology(myrivers.id()) iface.mapCanvas().refresh()
Simple symbology on polygons
With RGB color (50,165,77,75
), and some transparency (here 75, from 0 to 255), et on rafraîchit le panneau des couches afin que notre légende y soit aussi prise en compte:
myground = QgsProject.instance().mapLayersByName("Ground")[0] symbol = QgsFillSymbol.createSimple({'line_style': 'solid', 'line_width': '0.2', 'color': '50,165,77,75'}) myground.renderer().setSymbol(symbol) myground.triggerRepaint() iface.layerTreeView().refreshLayerSymbology(myground.id()) iface.mapCanvas().refresh()
Check current symbology properties
my_layer = QgsProject.instance().mapLayersByName("My points")[0] print(my_layer.renderer().symbol().symbolLayers()[0].properties())
Categorized Symbology
For a categorized symbology, we will need to define several symbologies, then assign them based on attribute values.
Here we put all our peaks in blue triangle (▲, then we will look for a single peak based on its name (My best peak, in the NAME field). This will change to a dark blue triangle and larger size (▲).
symbol_peak = QgsMarkerSymbol.createSimple({'name': 'Triangle', 'color': '#0088CC', 'outline_color': 'black', 'size': '4'}) symbol_peak_selected = QgsMarkerSymbol.createSimple({'name': 'Triangle', 'color': 'blue', 'outline_color': 'black', 'size': '7'}) color_peak = QgsRendererCategory(None, symbol_peak, 'Peaks', True) color_peak_selected = QgsRendererCategory('My best peak', symbol_peak_selected, 'My best peak', True) renderer = QgsCategorizedSymbolRenderer("NAME", [color_peak,color_peak_selected]) mylayer.setRenderer(renderer) mylayer.triggerRepaint() iface.mapCanvas().refresh()
Transparency on a basemap (OSM)
osm = QgsRasterLayer(urlWithParams, "OpenStreetMap", "wms") project.addMapLayer(osm) osm.setOpacity(0.75) iface.mapCanvas().refresh()
Labeling
Usual labels
project = QgsProject.instance() myLayers = QgsVectorLayer(myPath + 'myLayers/myLayers.shp', 'myLayers', 'ogr') project.addMapLayer(myLayers) myLayerLabelSettings = QgsPalLayerSettings() myLayerLabelSettings.fieldName = 'name' myLayerLabelSettings.enabled = True myLayerText = QgsTextFormat() myLayerText.setFont(QFont('Verdana', 8)) myLayerText.setSize(8) myLayerLabelSettings.setFormat(myLayerText) myLayerSettings = QgsVectorLayerSimpleLabeling(myLayerLabelSettings) myLayers.setLabeling(myLayerSettings) myLayers.setLabelsEnabled(True) myLayers.triggerRepaint() iface.mapCanvas().refresh() time.sleep(0.5)
Intersection
Below we set processes with a function in a QTimer.singleShot
, to allow multi-execution, but it is not mandatory according your context.
import os import shutil myPath = r'C:\\Users\\georg\\Downloads\\' project = QgsProject.instance() project.removeAllMapLayers() project.clear() iface.mapCanvas().refresh() peaks = QgsVectorLayer(myPath + 'peaks_selection/peaks_selection.shp', 'Peaks', 'ogr') iris = QgsVectorLayer(myPath + 'iris/iris.shp', 'IRIS', 'ogr') QgsProject.instance().addMapLayer(peaks) QgsProject.instance().addMapLayer(iris) # Intersection function def myIntersection(): # Directory for created layer _peaks_intersection = myPath + '_peaks_intersection' if os.path.isdir(_peaks_intersection) == True: shutil.rmtree(_peaks_intersection) if os.path.isdir(_peaks_intersection) == False: os.mkdir(_peaks_intersection) # Intersect peaks and IRIS peaks_intersection_path = _peaks_intersection + r'\\peaks_intersection.shp' processing.run('qgis:intersection', { \ "INPUT": peaks, \ "OVERLAY": iris, \ "INPUT_FIELDS": ["OSM_ID", "NAME", "OTHER_TAGS"], \ "OVERLAY_FIELDS": ["CODE_IRIS", "NOM_COM"], \ "OVERLAY_FIELDS_PREFIX": "", \ "OUTPUT": peaks_intersection_path}) # Remove layers project.removeMapLayer(peaks) project.removeMapLayer(iris) # Open the new intersected layer peaks_intersection = QgsVectorLayer(peaks_intersection_path, 'Peaks', "ogr") project.addMapLayer(peaks_intersection) # Register layer myIntersection = project.mapLayersByName('Peaks')[0] # Run function with delay QTimer.singleShot(200, myIntersection)
Buffer
Below we set processes with a function in a QTimer.singleShot
, to allow multi-execution, but it is not mandatory according your context.
Buffer with 4326 point layer, so with fake units:
import os import shutil myPath = r'C:\\Users\\georg\\Downloads\\' project = QgsProject.instance() project.removeAllMapLayers() project.clear() iface.mapCanvas().refresh() crs = QgsCoordinateReferenceSystem.fromEpsgId(4326) project.setCrs(crs) project.setEllipsoid('EPSG:4326') peaks = QgsVectorLayer(myPath + 'peaks_selection/peaks_selection.shp', 'Peaks', 'ogr') QgsProject.instance().addMapLayer(peaks) # Buffer function def myBuffer(): # Directory for created layer _peaks_buffer = myPath + '_peaks_buffer' if os.path.isdir(_peaks_buffer) == True: shutil.rmtree(_peaks_buffer) if os.path.isdir(_peaks_buffer) == False: os.mkdir(_peaks_buffer) # Buffer on peaks peaks_buffer_path = _peaks_buffer + r'\\peaks_buffer.shp' processing.run('qgis:buffer', { \ "INPUT": peaks, \ "DISTANCE": 0.05, \ "SEGMENTS": 10, \ "END_CAP_STYLE": 0, \ "DISSOLVE": False, \ "OUTPUT": peaks_buffer_path}) # Open the buffer layer peaks_buffer = QgsVectorLayer(peaks_buffer_path, "Buffer area", "ogr") project.addMapLayer(peaks_buffer) # Register layer my_buffer = project.mapLayersByName("Buffer area")[0] # Buffer below root = project.layerTreeRoot() myBelowLayer = root.findLayer(peaks_buffer.id()) myClone = myBelowLayer.clone() parent = myBelowLayer.parent() parent.insertChildNode(-1, myClone) parent.removeChildNode(myBelowLayer) # Run function with delay QTimer.singleShot(200, myBuffer)
Fix geometry
Fix invalid geometry (and register the output to use it later)
Here we register the output with the name MyFixedLayer
. So we can use it later.
alg_params = { 'INPUT':'C:\\Users\\georg\\Downloads\\MyLayer.shp', 'METHOD':1, 'OUTPUT':'C:\\Users\\georg\\Downloads\\MyFixedLayer.shp' } outputs['MyFixedLayer'] = processing.run('native:fixgeometries', alg_params, context=context)
Fix invalid geometry (from a previous output registered)
alg_params = { 'INPUT': outputs['MyPreviousOutput']['OUTPUT'], 'METHOD':1, 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT } outputs['MyNewOutput'] = processing.run('native:fixgeometries', alg_params, context=context)
Merge vector layers
First, create and fill a list with your layers to merge, then:
myPath = r'C:\\Users\\georg\\Downloads\\' project = QgsProject.instance() ... parameters = {'LAYERS': listLayersToMerge,'CRS': 'EPSG:25832','OUTPUT': myPath + 'results.shp'} processing.run("qgis:mergevectorlayers", parameters) myMerge = QgsVectorLayer(myPath + 'results.shp', 'My merge', 'ogr') project.addMapLayer(myMerge)
A full example here, search for Fusion de couches.
Joins
Join a CSV
import os import shutil myPath = r'C:\\Users\\georg\\Downloads\\' project = QgsProject.instance() project.removeAllMapLayers() project.clear() iface.mapCanvas().refresh() crs = QgsCoordinateReferenceSystem.fromEpsgId(4326) project.setCrs(crs) project.setEllipsoid('EPSG:4326') countries = QgsVectorLayer(myPath + 'simple_countries/simple_countries.shp', 'Countries', 'ogr') project.addMapLayer(countries) myCountries = project.mapLayersByName('Countries')[0] extent_countries = countries.extent() iface.mapCanvas().setExtent(extent_countries) iface.mapCanvas().refresh() # IMPORT CSV csv_path = 'file:///' + myPath + 'World Stats.csv?delimiter=;' my_csv = QgsVectorLayer(csv_path, 'Countries', 'delimitedtext') project.addMapLayer(my_csv) # JOIN shpField='COUNTRY_HB' csvField='Country' myJoin = QgsVectorLayerJoinInfo() myJoin.setJoinFieldName(csvField) myJoin.setTargetFieldName(shpField) myJoin.setJoinLayerId(my_csv.id()) myJoin.setUsingMemoryCache(True) myJoin.setJoinLayer(my_csv) myJoin.setJoinFieldNamesSubset(['Population (2024)']) myJoin.setPrefix('') countries.addJoin(myJoin) countries.dataProvider().forceReload()
Classifications
You can use any of these classification method classes:
- QgsClassificationQuantile()
- QgsClassificationEqualInterval()
- QgsClassificationJenks()
- QgsClassificationPrettyBreaks()
- QgsClassificationLogarithmic()
- QgsClassificationStandardDeviation()
Available color ramps
You can check all the ramps availables in your QGIS like that:
default_style = QgsStyle().defaultStyle() available_ramps = default_style.colorRampNames() for ramp in available_ramps: print(ramp)
Equal intervals
# Set layer name and desired params ramp_name = 'Spectral' value_field = 'Population (2024)' num_classes = 10 classification_method = QgsClassificationEqualInterval() layer = QgsProject().instance().mapLayersByName('Countries')[0] format = QgsRendererRangeLabelFormat() format.setTrimTrailingZeroes(True) default_style = QgsStyle().defaultStyle() color_ramp = default_style.colorRamp(ramp_name) renderer = QgsGraduatedSymbolRenderer() renderer.setClassAttribute(value_field) renderer.setClassificationMethod(classification_method) renderer.setLabelFormat(format) renderer.updateClasses(layer, num_classes) renderer.updateColorRamp(color_ramp) layer.setRenderer(renderer) layer.triggerRepaint()
Jenks
# Set layer name and desired params ramp_name = 'PuRd' value_field = 'Population (2024)' num_classes = 20 classification_method = QgsClassificationJenks() layer = QgsProject().instance().mapLayersByName('Countries')[0] format = QgsRendererRangeLabelFormat() format.setTrimTrailingZeroes(True) default_style = QgsStyle().defaultStyle() color_ramp = default_style.colorRamp(ramp_name) renderer = QgsGraduatedSymbolRenderer() renderer.setClassAttribute(value_field) renderer.setClassificationMethod(classification_method) renderer.setLabelFormat(format) renderer.updateClasses(layer, num_classes) renderer.updateColorRamp(color_ramp) layer.setRenderer(renderer) layer.triggerRepaint()
Round zeroes
format = QgsRendererRangeLabelFormat() format.setTrimTrailingZeroes(True)
Decimals
format = QgsRendererRangeLabelFormat() format.setPrecision(2)
Separator
format = QgsRendererRangeLabelFormat() format.setFormat("%1 - %2")
Invert colors
color_ramp = default_style.colorRamp(ramp_name) # Manually invert the color ramp if isinstance(color_ramp, QgsGradientColorRamp): inverted_ramp = QgsGradientColorRamp( color_ramp.color2(), # Use the second color as the start color color_ramp.color1() # Use the first color as the end color ) else: inverted_ramp = color_ramp ... renderer.updateColorRamp(inverted_ramp)
Install a third-party Python module in QGIS
You will have to go through the OSGeo4W shell.
We will use Pip, but Pip is already in the Python of QGIS.
Follow this tutorial to install a module on the version of Python used by your QGIS on Windows, or more simply just below.
In the OSGeo shell (and not in the usual Windows shell), with some old QGIS version, maybe start to target the environment:
py3_env
On more recent versions (QGIS 3.34 for example), just enter:
python -m pip install wikipedia
If you have some users rights restrictions:
python -m pip install wikipedia --user
You may need to restart QGIS to take your new library into account (no need with the Wikipedia lib, but it depends of the library).
Maybe you will need to register Pip or Python in your Path System Variable, just follow the instructions from the OSGeo shell.
Other libraries examples
pip install requests-html
py -m pip install requests-html
pip install wikidata
pip install requests
PIP on Windows
But maybe you would install Pip on Windows. Pip is a Python extensions installer, check this:
Generate maps in a loop
Full code to generate maps in a loop here!
Miscellaneous
Get executed QGIS Python code
In the QGIS menu: Processing/History.
In a plugin (from QGIS 3.28): Advanced/Copy as Python Command
Set projection and ellipsoid
project = QgsProject.instance() ... crs = QgsCoordinateReferenceSystem.fromEpsgId(4326) project.setCrs(crs) project.setEllipsoid('EPSG:4326')
See the available process and scripts
for alg in QgsApplication.processingRegistry().algorithms(): print("{}:{} --> {}".format(alg.provider().name(), alg.name(), alg.displayName()))
Manage the multi-execution of your script
Sometimes it is good to remove existing layers and clean your QGIS instance before to run the working code (but also read on because it is not perfect):
project = QgsProject.instance() project.removeAllMapLayers() project.clear() iface.mapCanvas().refresh()
Sometimes it is good to delete all persistent variables at the begining of your code to manage the multi-execution of your script (to be able to run it, and re-run it, then re-run it... it can be useful when your work, but keep to read on because it is not perfect):
for g in globals(): del g
OK guys, now the real solution 😃: according your working context, and especially when you create/re-create new layers and/or new directories (with a geo-process for example), it is better to do it in a function (def
), then to run your function in a QTimer.singleShot
, with a little delay (below 200 milliseconds):
... def myGeoProcess(): ... processing.run('qgis:anyGeoProcess', { ... }) ... QTimer.singleShot(200, myGeoProcess) ...
Find a full example in the chapter named Processing: Intersection or Processing: Buffer.
Using QGIS Buttons in Python Code
eMenu = iface.viewMenu() eMenu.actions()[12].trigger()
Add custom text in map
project = QgsProject.instance() iface.mapCanvas().refresh() manager = project.layoutManager() layout = QgsPrintLayout(project) layout.initializeDefaults() layout.setName(layoutName) manager.addLayout(layout) map = QgsLayoutItemMap(layout) myBoldFont=QtGui.QFont('Verdana', 11) myBoldFont.setBold(True) TextCustom = QgsLayoutItemLabel(layout) TextCustom.setText("Wikipédia :") TextCustom.setFont(myBoldFont) layout.addLayoutItem(TextCustom) TextCustom.attemptMove(QgsLayoutPoint(230, 90, QgsUnitTypes.LayoutMillimeters)) TextCustom.attemptResize(QgsLayoutSize(60, 100, QgsUnitTypes.LayoutMillimeters))
Download a picture and add a backgound color
from PyQt5 import * from qgis.PyQt.QtGui import QImage, QPainter, QColor # Image 1 url = r'https://j4binv.master-geomatique.org/images/img_site/logos/logo2021-blanc.png' response = requests.get(url) image_originale1 = myPath + 'logo_master.png' path_nouvelle_image1 = myPath + 'logo_master_fond_vert.png' # Dowload image if response.status_code == 200: with open(image_originale1, 'wb') as file: file.write(response.content) else: raise Exception('Impossible de télécharger l\'image !') # Set a black background my_image = QImage(image_originale1) largeur = my_image.width() + 40 hauteur = my_image.height() + 40 nouvelle_image1 = QImage(largeur, hauteur, QImage.Format_ARGB32) nouvelle_image1.fill(QColor(21, 187, 161)) painter = QPainter(nouvelle_image1) x_offset = (largeur - my_image.width()) // 2 y_offset = (hauteur - my_image.height()) // 2 painter.drawImage(x_offset, y_offset, my_image) painter.end() nouvelle_image1.save(path_nouvelle_image1)