Using Qgis Python Scripting in the NZ Rail Maps Project: 1 – WebMaps

Good evening. As part of the development of the NZ Rail Maps Project, recently custom Python scripting has been incorporated into the main window GUI and Composer / Layout to speed up processing of various aspects. This is summarised in different sections below.

In order to create the content of the NZRM WebMaps site, various steps are required. Broadly summarised, these are exporting the GUI layers to XYZ web tiles, consolidating these tiles into mbtiles files, uploading the mbtiles to the website, and developing the website with PHP and Javascript to display the webmaps.

The first step is custom scripted in the Qgis GUI to automate the generation of the tiles, which is a substantial project that if carried out manually, would require many steps. Although it is similar to the Volumes content creation which is and will always remain a manual process, automating WebMaps creation has been far preferable to a Volumes-like process simply in order to be able to update frequently, whereas Volumes images are generally only updated in the specific sections that have changed. Having WebMaps able to be auto-updated eliminates having to keep track of changes made in a particular area.

The generation of WebMaps relies on defining the areas to be captured in each tile and at different zoom levels. The areas are defined in a Corridor Extents polygon layer manually created for each volume as a once only process. This layer is read by the scripts with multiple generation steps performed for each extent recorded in the corridor extents layer.

The WebMaps incorporate multiple web tiles layers that are overlaid in the web client and each of these is created from a particular combination of source layers in the Web GUI. This has required different Qgis projects which are specifically tailored to WebMaps creation and group the source layers in a different way from the projects that are used to edit the source data and create the Volumes images.

Here is the script. The first part is a few definitions and constants. The user must manually select the source layers to be built into a WebMaps layer, and specify where the tiles are to be saved (the folder is named for one of the WebMaps layers). OutputBase is simply added to the outputFolder to get the full disk path to save tiles. The first part of the code reads a corridor extent from the extents table. It then follows some simple steps to change the irregular polygon shape into a rectangle. The last step calls the Qgis Processing toolbox scripts to generate the actual tiles. NZRM WebMaps can generate and use either jpg or png tiles. PNG is used for all overlays because of transparency, allowing multiple overlays to be simultaneously specified. JPG is used for base layers, so only one base can be selected at a time.


from qgis import processing

extentSource = ‘V3-CorridorExtents’
outputFolder = ‘Overlay-Non-Rail’
tileFormat = 0 # 0=overlay/png 1=base/jpg

outputBase = ‘/home/patrick/Maps/WebMaps/’
zoomMin = 9
zoomMax = 19

corridorExtents = QgsProject.instance().mapLayersByName(extentSource)[0]

outputPath = outputBase + outputFolder

featCount = corridorExtents.featureCount()
featInstance = 0
for feat in corridorExtents.getFeatures():
featInstance += 1
percent = featInstance / featCount * 100
print(“Processing {:.1f}%”.format(percent) + ” from ” + extentSource + ” to ” + outputFolder)
ogcfid = feat[‘ogc_fid’]
location = feat[‘location’]
fromKm = feat[‘fromKm’]
toKm = feat[‘toKm’]
geom = feat.geometry()
poly = geom.asPolygon()
for boundary in poly:
# Rectangularise extent of poly
xMin = 9999999999
xMax = -9999999999
yMin = 9999999999
yMax = -9999999999
for coord in boundary:
xCoord = coord.x();
yCoord = coord.y();
#print (xcoord,’,’,ycoord)
if (xCoord < xMin): xMin = xCoord if (xCoord > xMax):
xMax = xCoord
if (yCoord < yMin): yMin = yCoord if (yCoord > yMax):
yMax = yCoord
#print(“Rectangularised: “,xmin, ‘,’,ymin,” “,xmax,’,’,ymax)

extent = str(xMin) + “,” + str(xMax) + ‘,’ + str(yMin) + “,” + str(yMax) + ‘ [EPSG:3857]’

processing.run(“qgis:tilesxyzdirectory”,
{ ‘BACKGROUND_COLOR’ : QColor(0, 0, 0, 0),
‘DPI’ : 96, ‘EXTENT’ : extent,
‘METATILESIZE’ : 4,
‘OUTPUT_DIRECTORY’ : outputPath,
‘OUTPUT_HTML’ : ‘TEMPORARY_OUTPUT’,
‘QUALITY’ : 75, ‘TILE_FORMAT’ : tileFormat, ‘TILE_HEIGHT’ : 256, ‘TILE_WIDTH’ : 256,
‘TMS_CONVENTION’ : False,
‘ZOOM_MAX’ : zoomMax, ‘ZOOM_MIN’ : zoomMin } )