I'm trying to make some map tiles (with a transparent background) showing the speed limit in different areas of the UK. I've successfully managed to do this using the QGIS GUI on Windows 10, but I'd now like to automate the process so I can easily keep the tiles up to date with changes to OpenStreetMap.
I've managed to get the process running in a docker container and it all runs through without any errors, but all of the generated tiles are blank (unlike when I run it with the QGIS GUI). The way the script works (after producing a bunch of .osm
files for different speed limits), is to load the project (.qgs
) that I had previously created in the QGIS GUI on Windows 10 and then run the qgis:tilesxyzdirectory
process with the same parameters that were used in the GUI.
I suspect that the problem is in loading the project; I found a bit of code in the processing algorithm that creates a list of layers. The result of findLayers()
is a list of QgsLayerTreeLayer
s, whereas the result of layerOrder()
is a list of QgsVectorLayer
s, so the check that the entries in layerOrder
appear in the visible list produces no results and hence self.layers
gets set to []
. Of course I might be completely wrong about the problem and, even if I'm right, I have no idea what to do about it. I'd like to be able to load a QGIS project as it allows me to use the GUI for styling.
The complete project containing the docker container details and all the generation scripts is on bitbucket here. After building with docker-compose build
, I run it using docker-compose up
. The actual python script for generating the tiles from the project is here.
The bash script that runs the overall process is here.
Can anyone see what I'm doing wrong?
1 Answer 1
As suggested by @spatialthoughts in his comment, I ended up having to load the layers manually in code. It seems a shame that the project can't be set up in the GUI and then run over and over again in the command line, but in the end it wasn't that hard to get the command line functionality working. The final code that generates the tiles is here in case anyone is interested:
import sys
import os
scriptdir = os.path.abspath(os.path.dirname(__file__))
print("Importing modules")
from qgis.core import (
QgsApplication,
QgsProject,
QgsProcessingFeedback,
QgsVectorLayer
)
from qgis.PyQt.Qt import QColor
def run():
print("Setting prefix path")
QgsApplication.setPrefixPath('/usr', True) # Check this
print("Creating application")
qgs = QgsApplication([], False)
print("Initialising application")
qgs.initQgis()
sys.path.append('/usr/share/qgis/python/plugins')
print("Importing processing")
import processing
from processing.core.Processing import Processing
print("Initialising processing")
Processing.initialize()
tiledir = sys.argv[1]
####
layer_specifications = [
{
'file': '20_or_less.osm',
'colour': [190,0,3,255],
'alpha': 1.0,
},
{
'file': 'low_speed_directional.osm',
'colour': [255,127,0,255],
'alpha': 1.0,
},
{
'file': '30.osm',
'colour': [164,113,88,255],
'alpha': 1.0,
},
{
'file': '40.osm',
'colour': [255,158,23,255],
'alpha': 1.0,
},
{
'file': '50.osm',
'colour': [231,113,72,255],
'alpha': 1.0,
},
{
'file': 'nsl.osm',
'colour': [141,90,153,255],
'alpha': 1.0,
},
]
for layer in layer_specifications:
vlayer = QgsVectorLayer('./' + layer['file'] + '|layername=lines', 'name', 'ogr')
if not vlayer.isValid():
print("Layer failed to load: %s" % layer['file'])
sys.exit(4)
vlayer.renderer().symbol().setColor(QColor(*layer['colour']))
vlayer.renderer().symbol().setOpacity(layer['alpha'])
# One of these?:
#QgsMapLayerRegistry.instance().addMapLayer(vlayer) # Import didn't work...
QgsProject.instance().addMapLayer(vlayer)
parameters = {
'DPI' : 96,
# Extent is GB limits
# From the passed parameters, it looks like it is: west, east, south, north
# This was the extent shown in the dialogue box in QGIS, but it causes
# a maths domain error as the plugin expects degrees:
#'EXTENT' : '-890009.4821566814,282147.8954041847,6361787.307678058,8643227.720708402 [EPSG:3857]',
# Extracted from https://gist.githubusercontent.com/UsabilityEtc/6d2059bd4f0181a98d76/raw/12fa5f7b489c9babac255ab6bfe1c5cd7410ad64/uk-bounding-box.geojson
# and manually bodged into shape...
'EXTENT' : '-10.8544921875,2.021484375,49.82380908513249,59.478568831926395 [EPSG:3857]',
# Small extent used for testing:
#'EXTENT' : '-2.8544921875,-1.721484375,51.56380908513249,51.919568831926395 [EPSG:3857]',
'OUTPUT_DIRECTORY' : tiledir + "/maxspeed",
'OUTPUT_HTML' : './leaflet.html',
'QUALITY' : 75,
'TILE_FORMAT' : 0,
'TILE_HEIGHT' : 256,
'TILE_WIDTH' : 256,
'TMS_CONVENTION' : False,
'ZOOM_MAX' : 13,
'ZOOM_MIN' : 9,
}
algorithm = "qgis:tilesxyzdirectory"
print("Preparing feedback mechanism")
feedback = QgsProcessingFeedback()
print("Running process")
res = processing.run(algorithm, parameters, feedback=feedback)
print("Process complete: %r" % res)
print("Exiting")
sys.stdout.flush()
qgs.exitQgis()
print("Exiting function")
sys.stdout.flush()
# If the process isn't run in a function, QGIS core dumps at the end for some reason
run()
print("All done")
sys.stdout.flush()
.qgs
not a.qgz
?.qgz
but it failed (I can't remember the error message) so I unzipped the.qgz
and extracted the.qgs
that was inside. That loaded without any error messages.