





















































In this article by Joel Lawhead, author of the book, QGIS Python Programming Cookbook - Second Edition, we will cover the following recipes:
(For more resources related to this topic, see here.)
Sometimes, you may just want to display the outline of a polygon in a layer and have the insides of the polygon render transparently, so you can see the other features and background layers inside that space. For example, this technique is common with political boundaries. In this recipe, we will load a polygon layer onto the map, and then interactively change it to just an outline of the polygon.
Download the zipped shapefile and extract it to your qgis_data directory into a folder named ms from https://github.com/GeospatialPython/Learn/raw/master/Mississippi.zip.
In the following steps, we'll load a vector polygon layer, set up a properties dictionary to define the color and style, apply the properties to the layer's symbol, and repaint the layer. In Python Console, execute the following:
lyr = QgsVectorLayer("/qgis_data/ms/mississippi.shp", "Mississippi", "ogr")
QgsMapLayerRegistry.instance().addMapLayer(lyr)
properties = {}
properties["color"] = '#289e26'
properties["color_border"] = '#289e26'
properties["width_border"] = '2'
properties["style"] = 'no'
sym = QgsFillSymbolV2.createSimple(properties)
renderer = lyr.rendererV2()
renderer.setSymbol(sym)
lyr.triggerRepaint()
In this recipe, we used a simple dictionary to define our properties combined with the createSimple method of the QgsFillSymbolV2 class. Note that we could have changed the symbology of the layer before adding it to the canvas, but adding it first allows you to see the change take place interactively.
A newer feature of QGIS is filled marker symbols. Filled marker symbols are powerful features that allow you to use other symbols, such as point markers, lines, and shapebursts as a fill pattern for a polygon. Filled marker symbols allow for an endless set of options for rendering a polygon. In this recipe, we'll do a very simple filled marker symbol that paints a polygon with stars.
Download the zipped shapefile and extract it to your qgis_data directory into a folder named ms from https://github.com/GeospatialPython/Learn/raw/master/Mississippi.zip.
A filled marker symbol requires us to first create the representative star point marker symbol. Then, we'll add that symbol to the filled marker symbol and change it with the layer's default symbol. Finally, we'll repaint the layer to update the symbology:
lyr = QgsVectorLayer("/qgis_data/ms/mississippi.shp", "Mississippi", "ogr")
QgsMapLayerRegistry.instance().addMapLayer(lyr)
marker_props = {}
marker_props["color"] = 'red'
marker_props["color_border"] = 'black'
marker_props["name"] = 'star'
marker_props["size"] = '3'
marker = QgsMarkerSymbolV2.createSimple(marker_props)
filled_marker = QgsPointPatternFillSymbolLayer()
filled_marker.setDistanceX(4.0)
filled_marker.setDistanceY(4.0)
filled_marker.setSubSymbol(marker)
renderer = lyr.rendererV2()
renderer.symbols()[0].changeSymbolLayer(0, filled_marker)
lyr.triggerRepaint()
A color ramp allows you to render a raster using just a few colors to represent different ranges of cell values that have a similar meaning in order to group them. The approach that will be used in this recipe is the most common way to render elevation data.
You can download a sample DEM from
https://github.com/GeospatialPython/Learn/raw/master/dem.zip, which you can unzip in a directory named rasters in your qgis_data directory.
In the following steps, we will set up objects to color a raster, create a list establishing the color ramp ranges, apply the ramp to the layer renderer, and finally, add the layer to the map. To do this, we need to perform the following:
from PyQt4 import QtGui
lyr = QgsRasterLayer("/qgis_data/rasters/dem.asc", "DEM")
s = QgsRasterShader()
c = QgsColorRampShader()
c.setColorRampType(QgsColorRampShader.INTERPOLATED)
i = []
i.append(QgsColorRampShader.ColorRampItem(400, QtGui.QColor('#d7191c'), '400'))
i.append(QgsColorRampShader.ColorRampItem(900, QtGui.QColor('#fdae61'), '900'))
i.append(QgsColorRampShader.ColorRampItem(1500, QtGui.QColor('#ffffbf'), '1500'))
i.append(QgsColorRampShader.ColorRampItem(2000, QtGui.QColor('#abdda4'), '2000'))
i.append(QgsColorRampShader.ColorRampItem(2500, QtGui.QColor('#2b83ba'), '2500'))
c.setColorRampItemList(i)
s.setRasterShaderFunction(c)
ps = QgsSingleBandPseudoColorRenderer(lyr.dataProvider(), 1, s)
lyr.setRenderer(ps)
QgsMapLayerRegistry.instance().addMapLayer(lyr)
While it takes a stack of four objects to create a color ramp, this recipe demonstrates how flexible the PyQGIS API is. Typically, the more number of objects it takes to accomplish an operation in QGIS, the richer the API is, giving you the flexibility to make complex maps. Notice that in each ColorRampItem object, you specify a starting elevation value, the color, and a label as the string. The range for the color ramp ends at any value less than the following item. So, in this case, the first color will be assigned to the cells with a value between 400 and 899. The following screenshot shows the applied color ramp:
Comma Separated Value (CSV) files are an easy way to store basic geospatial information. But you can also store styling properties alongside the geospatial data for QGIS to use in order to dynamically style the feature data. In this recipe, we'll load some points into QGIS from a CSV file and use one of the columns to determine the color of each point.
Download the sample zipped CSV file from the following URL: https://github.com/GeospatialPython/Learn/raw/master/point_colors.csv.zip
Extract it and place it in your qgis_data directory in a directory named shapes.
We'll load the CSV file into QGIS as a vector layer and create a default point symbol. Then we'll specify the property and the CSV column we want to control. Finally we'll assign the symbol to the layer and add the layer to the map:
uri = "file:///qgis_data/shapes/point_colors.csv?"
uri += "type=csv&"
uri += "xField=X&yField=Y&"
uri += "spatialIndex=no&"
uri += "subsetIndex=no&"
uri += "watchFile=no&"
uri += "crs=epsg:4326"
lyr = QgsVectorLayer(uri,"Points","delimitedtext")
sym = QgsSymbolV2.defaultSymbol(lyr.geometryType())
symLyr = sym.symbolLayer(0)
symLyr.setDataDefinedProperty("color", '"COLOR"')
lyr.rendererV2().symbols()[0].changeSymbolLayer(0, symLyr)
QgsMapLayerRegistry.instance().addMapLayers([lyr])
In this example, we pulled feature colors from the CSV, but you could control any symbol layer property in this manner. CSV files can be a simple alternative to databases for lightweight applications or for testing key parts of a large application before investing the overhead to set up a database.
The true power of QGIS symbology lies in its ability to stack multiple symbols in order to create a single complex symbol. This ability makes it possible to create virtually any type of map symbol you can imagine. In this recipe, we'll merge two symbols to create a single symbol and begin unlocking the potential of complex symbols.
For this recipe, we will need a line shapefile, which you can download and extract from https://github.com/GeospatialPython/Learn/raw/master/paths.zip.
Add this shapefile to a directory named shapes in your qgis_data directory.
Using Python Console, we will create a classic railroad line symbol by placing a series of short, rotated line markers along a regular line symbol. To do this, we need to perform the following steps:
lyr = QgsVectorLayer("/qgis_data/shapes/paths.shp", "Route", "ogr")
symbolList = lyr.rendererV2().symbols()
symbol = symbolList[0]
symLyrReg = QgsSymbolLayerV2Registry
lineStyle = {'width':'0.26', 'color':'0,0,0'}
symLyr1Meta = symLyrReg.instance().symbolLayerMetadata("SimpleLine")
symLyr1 = symLyr1Meta.createSymbolLayer(lineStyle)
symbol.appendSymbolLayer(symLyr1)
markerStyle = {}
markerStyle['width'] = '0.26'
markerStyle['color'] = '0,0,0'
markerStyle['interval'] = '3'
markerStyle['interval_unit'] = 'MM'
markerStyle['placement'] = 'interval'
markerStyle['rotate'] = '1'
symLyr2Meta = symLyrReg.instance().symbolLayerMetadata("MarkerLine")
symLyr2 = symLyr2Meta.createSymbolLayer(markerStyle)
sybSym = symLyr2.subSymbol()
sybSym.deleteSymbolLayer(0)
railStyle = {'size':'2', 'color':'0,0,0', 'name':'line', 'angle':'0'}
railMeta = symLyrReg.instance().symbolLayerMetadata("SimpleMarker")
rail = railMeta.createSymbolLayer(railStyle)
sybSym.appendSymbolLayer(rail)
symbol.appendSymbolLayer(symLyr2)
QgsMapLayerRegistry.instance().addMapLayer(lyr)
First, we must create a simple line symbol. The marker line, by itself, will render correctly, but the underlying simple line will be a randomly chosen color. We must also change the subsymbol of the marker line because the default subsymbol is a simple circle.
Font markers open up broad possibilities for icons, but a single-color shape can be hard to see across a varied map background. Recently, QGIS added the ability to place outlines around font marker symbols. In this recipe, we'll use font marker symbol methods to place an outline around the symbol to give it contrast and, therefore, visibility on any type of background.
Download the following zipped shapefile. Extract it and place it in a directory named ms in your qgis_data directory:
https://github.com/GeospatialPython/Learn/raw/master/tourism_points.zip
This recipe will load a layer from a shapefile, set up a font marker symbol, put an outline on it, and then add it to the layer. We'll use a simple text character, an @ sign, as our font marker to keep things simple:
from PyQt4.QtGui import *
src = "/qgis_data/ms/tourism_points.shp"
lyr = QgsVectorLayer(src, "Points of Interest", "ogr")
symLyr = QgsFontMarkerSymbolLayerV2(pointSize=16, color=QColor("cyan"))
symLyr.setFontFamily("'Arial'")
symLyr.setCharacter("@")
symLyr.setOutlineWidth(.5)
symLyr.setOutlineColor(QColor("black"))
lyr.rendererV2().symbols()[0].changeSymbolLayer(0, symLyr)
QgsMapLayerRegistry.instance().addMapLayer(lyr)
We used class methods to set this symbol up, but we also could have used a property dictionary just as easily. Note that the font size and color were set in the object constructor for the font maker symbol instead of using setter methods. QgsFontMarkerSymbolLayerV2 doesn't have methods for these two properties.
Line features convey location, but sometimes you also need to convey a direction along a line. QGIS recently added a symbol that does just that by turning lines into arrows. In this recipe, we'll symbolize some line features showing historical human migration routes around the world. This data requires directional arrows for us to understand it:
We will use two shapefiles in this example. One is a world boundaries shapefile and the other is a route shapefile. You can download the countries shapefile here:
https://github.com/GeospatialPython/Learn/raw/master/countries.zip
You can download the routes shapefile here:
https://github.com/GeospatialPython/Learn/raw/master/human_migration_routes.zip
Download these ZIP files and unzip the shapefiles into your qgis_data directory.
We will load the countries shapefile as a background reference layer and then, the route shapefile. Before we display the layers on the map, we'll create the arrow symbol layer, configure it, and then add it to the routes layer. Finally, we'll add the layers to the map.
countries_shp = "/qgis_data/countries.shp"
routes_shp = "/qgis_data/human_migration_routes.shp"
countries = QgsVectorLayer(countries_shp, "Countries", "ogr")
routes = QgsVectorLayer(routes_shp, "Human Migration Routes", "ogr")
symLyr = QgsArrowSymbolLayer()
symLyr.setIsCurved(True)
symLyr.setIsRepeated(False)
routes.rendererV2().symbols()[0].changeSymbolLayer(0, symLyr)
QgsMapLayerRegistry.instance().addMapLayers([routes,countries])
The symbol calculates the arrow's direction based on the order of the feature's points. You may find that you need to edit the underlying feature data to produce the desired visual effect, especially when using curved arrows. You have limited control over the arc of the curve using the end points plus an optional third vertex. This symbol is one of the several new powerful visual effects added to QGIS, which would have normally been done in a vector illustration program after you produced a map.
In this article, weprogrammatically created dynamic maps using Python to control every aspect of the QGIS map canvas. We learnt to dynamically apply symbology from data in a CSV file. We also learnt how to use some newer QGIS custom symbology including font markers, arrow symbols, null symbols, and the powerful new 2.5D renderer for buildings. Wesaw that every aspect of QGIS is up for grabs with Python, to write your own application. Sometimes, the PyQGIS API may not directly support our application goal, but there is nearly always a way to accomplish what you set out to do with QGIS.
Further resources on this subject: