Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

Creating Dynamic Maps

Save for later
  • 15 min read
  • 27 Jan 2017

article-image

In this article by Joel Lawhead, author of the book, QGIS Python Programming Cookbook - Second Edition, we will cover the following recipes:

  • Setting a transparent layer fill
  • Using a filled marker symbol
  • Rendering a single band raster using a color ramp algorithm
  • Setting a feature's color using a column in a CSV file
  • Creating a complex vector layer symbol
  • Using an outline for font markers
  • Using arrow symbols

(For more resources related to this topic, see here.)

Setting a transparent layer fill

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.

Getting ready

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.

How to do it…

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:

  1. Create the polygon layer:
    lyr = QgsVectorLayer("/qgis_data/ms/mississippi.shp", "Mississippi", "ogr")
  2. Load the layer onto the map:
    QgsMapLayerRegistry.instance().addMapLayer(lyr)
  3. Now, we’ll create the properties dictionary:
    properties = {}
  4. Next, set each property for the fill color, border color, border width, and a style of no meaning no-brush. Note that we’ll still set a fill color; we are just making it transparent:
    properties["color"] = '#289e26'
    properties["color_border"] = '#289e26'
    properties["width_border"] = '2'
    properties["style"] = 'no'
  5. Now, we create a new symbol and set its new property:
    sym = QgsFillSymbolV2.createSimple(properties)
  6. Next, we access the layer's renderer:
    renderer = lyr.rendererV2()
  7. Then, we set the renderer's symbol to the new symbol we created:
    renderer.setSymbol(sym)
  8. Finally, we repaint the layer to show the style updates:
    lyr.triggerRepaint()

How it works…

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.

Using a filled marker symbol

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.

Getting ready

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.

How to do it…

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:

  1. First, create the layer with our polygon shapefile:
    lyr = QgsVectorLayer("/qgis_data/ms/mississippi.shp", "Mississippi", "ogr")
  2. Next, load the layer onto the map:
    QgsMapLayerRegistry.instance().addMapLayer(lyr)
  3. Now, set up the dictionary with the properties of the star marker symbol:
    marker_props = {}
    marker_props["color"] = 'red'
    marker_props["color_border"] = 'black'
    marker_props["name"] = 'star'
    marker_props["size"] = '3'
  4. Now, create the star marker symbol:
    marker = QgsMarkerSymbolV2.createSimple(marker_props)
  5. Then, we create our filled marker symbol:
    filled_marker = QgsPointPatternFillSymbolLayer()
  6. We need to set the horizontal and vertical spacing of the filled markers in millimeters:
    filled_marker.setDistanceX(4.0)
    filled_marker.setDistanceY(4.0)
  7. Now, we can add the simple star marker to the filled marker symbol:
    filled_marker.setSubSymbol(marker)
  8. Next, access the layer's renderer:
    renderer = lyr.rendererV2()
  9. Now, we swap the first symbol layer of the first symbol with our filled marker using zero indexes to reference them:
    renderer.symbols()[0].changeSymbolLayer(0, filled_marker)
  10. Finally, we repaint the layer to see the changes:
    lyr.triggerRepaint()
  11. Verify that the result looks similar to the following screenshot:

creating-dynamic-maps-img-0

Rendering a single band raster using a color ramp algorithm

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.

Getting ready

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.

How to do it...

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:

  1. First, we import the QtGui library for color objects in Python Console:
    from PyQt4 import QtGui
  2. Next, we load the raster layer, as follows:
    lyr = QgsRasterLayer("/qgis_data/rasters/dem.asc", "DEM")
  3. Now, we create a generic raster shader object:
    s = QgsRasterShader()
  4. Then, we instantiate the specialized ramp shader object:
    c = QgsColorRampShader()
  5. We must name a type for the ramp shader. In this case, we use an INTERPOLATED shader:
    c.setColorRampType(QgsColorRampShader.INTERPOLATED)
  6. Now, we'll create a list of our color ramp definitions:
    i = []
  7. Then, we populate the list with the color ramp values that correspond to the elevation value ranges:
    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'))
  8. Now, we assign the color ramp to our shader:
    c.setColorRampItemList(i)
  9. Now, we tell the generic raster shader to use the color ramp:
    s.setRasterShaderFunction(c)
  10. Next, we create a raster renderer object with the shader:
    ps = QgsSingleBandPseudoColorRenderer(lyr.dataProvider(), 1,  s)
  11. We assign the renderer to the raster layer:
    lyr.setRenderer(ps)
  12. Finally, we add the layer to the canvas in order to view it:
    QgsMapLayerRegistry.instance().addMapLayer(lyr)

How it works…

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:

creating-dynamic-maps-img-1

Setting a feature's color using a column in a CSV file

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.

Getting ready

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.

How to do it…

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:

  1. First, create the URI string needed to load the CSV:
    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"
  2. Next, create the layer using the URI string:
    lyr = QgsVectorLayer(uri,"Points","delimitedtext")
  3. Now, create a default symbol for the layer's geometry type:
    sym = QgsSymbolV2.defaultSymbol(lyr.geometryType())
  4. Then, we access the layer's symbol layer:
    symLyr = sym.symbolLayer(0)
  5. Now, we perform the key step, which is to assign a symbol layer property to a CSV column:
    symLyr.setDataDefinedProperty("color", '"COLOR"')
  6. Then, we change the existing symbol layer with our data-driven symbol layer:
    lyr.rendererV2().symbols()[0].changeSymbolLayer(0, symLyr)
  7. Finally, we add the layer to the map and verify that each point has the correct color, as defined in the CSV:
    QgsMapLayerRegistry.instance().addMapLayers([lyr])

How it works…

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.

Creating a complex vector layer symbol

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.

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at ₹800/month. Cancel anytime

Getting ready

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.

How to do it…

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:

  1. First, we load our line shapefile:
    lyr = QgsVectorLayer("/qgis_data/shapes/paths.shp", "Route", "ogr")
  2. Next, we get the symbol list and reference the default symbol:
    symbolList = lyr.rendererV2().symbols()
    symbol = symbolList[0]
  3. Then,we create a shorter variable name for the symbol layer registry:
    symLyrReg = QgsSymbolLayerV2Registry
  4. Now, we set up the line style for a simple line using a Python dictionary:
    lineStyle = {'width':'0.26', 'color':'0,0,0'}
  5. Then, we create an abstract symbol layer for a simple line:
    symLyr1Meta = symLyrReg.instance().symbolLayerMetadata("SimpleLine")
  6. We instantiate a symbol layer from the abstract layer using the line style properties:
    symLyr1 = symLyr1Meta.createSymbolLayer(lineStyle)
  7. Now, we add the symbol layer to the layer's symbol:
    symbol.appendSymbolLayer(symLyr1)
  8. Now,in order to create the rails on the railroad, we begin building a marker line style with another Python dictionary, as follows:
    markerStyle = {}
    markerStyle['width'] = '0.26'
    markerStyle['color'] = '0,0,0'
    markerStyle['interval'] = '3'
    markerStyle['interval_unit'] = 'MM'
    markerStyle['placement'] = 'interval'
    markerStyle['rotate'] = '1'
  9. Then, we create the marker line abstract symbol layer for the second symbol:
    symLyr2Meta = symLyrReg.instance().symbolLayerMetadata("MarkerLine")
  10. We instatiate the symbol layer, as shown here:
    symLyr2 = symLyr2Meta.createSymbolLayer(markerStyle)
  11. Now, we must work with a subsymbol that defines the markers along the marker line:
    sybSym = symLyr2.subSymbol()
  12. We must delete the default subsymbol:
    sybSym.deleteSymbolLayer(0)
  13. Now, we set up the style for our rail marker using a dictionary:
    railStyle = {'size':'2', 'color':'0,0,0', 'name':'line', 'angle':'0'}
  14. Now, we repeat the process of building a symbol layer and add it to the subsymbol:
    railMeta = symLyrReg.instance().symbolLayerMetadata("SimpleMarker")
    rail = railMeta.createSymbolLayer(railStyle)
    sybSym.appendSymbolLayer(rail)
  15. Then, we add the subsymbol to the second symbol layer:
    symbol.appendSymbolLayer(symLyr2)
  16. Finally, we add the layer to the map:
    QgsMapLayerRegistry.instance().addMapLayer(lyr)

How it works…

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.

Using an outline for font markers

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.

Getting ready

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

How to do it…

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:

  1. First, we need to import the QtGUI library, so we can work with color objects:
    from PyQt4.QtGui import *
  2. Now, we create a path string to our shapefile:
    src = "/qgis_data/ms/tourism_points.shp"
  3. Next, we can create the layer:
    lyr = QgsVectorLayer(src, "Points of Interest", "ogr")
  4. Then, we can create the font marker symbol specifying the font size and color in the constructor:
    symLyr = QgsFontMarkerSymbolLayerV2(pointSize=16, color=QColor("cyan"))
  5. Now, we can set the font family, character, outline width, and outline color:
    symLyr.setFontFamily("'Arial'")
    symLyr.setCharacter("@")
    symLyr.setOutlineWidth(.5)
    symLyr.setOutlineColor(QColor("black"))
  6. We are now ready to assign the symbol to the layer:
    lyr.rendererV2().symbols()[0].changeSymbolLayer(0, symLyr)
  7. Finally, we add the layer to the map:
    QgsMapLayerRegistry.instance().addMapLayer(lyr)
  8. Verify that your map looks similar to the following image:

creating-dynamic-maps-img-2

How it works…

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.

Using arrow symbols

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:

Getting ready

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.

How to do it…

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.

  1. First, we'll create the URI strings for the paths to the two shapefiles:
    countries_shp = "/qgis_data/countries.shp"
    
    routes_shp = "/qgis_data/human_migration_routes.shp"
  2. Next, we'll create our countries and routes layers:
    countries = QgsVectorLayer(countries_shp, "Countries", "ogr")
    
    routes = QgsVectorLayer(routes_shp, "Human Migration Routes", "ogr")
  3. Now, we’ll create the arrow symbol layer:
    symLyr = QgsArrowSymbolLayer()
  4. Then, we’ll configure the layer. We'll use the default configuration except for two paramters--to curve the arrow and to not repeat the arrow symbol for each line segment:
    symLyr.setIsCurved(True)
    
    symLyr.setIsRepeated(False)
  5. Next, we add the symbol layer to the map layer:
    routes.rendererV2().symbols()[0].changeSymbolLayer(0, symLyr)
  6. Finally, we add the layers to the map:
    QgsMapLayerRegistry.instance().addMapLayers([routes,countries])
  7. Verify that your map looks similar to the following image:

creating-dynamic-maps-img-3

How it works…

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.

Summary

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.

Resources for Article:


Further resources on this subject: