2

The following code behaves as I expect when I run it in QGIS Python Console, but when I run it as a standalone from the console, the layer is invalid.

urlWithParams = 'type=xyz&url=https://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=19&zmin=0'
raster_layer = QgsRasterLayer(urlWithParams, 'OpenStreetMap', 'wms')
l = raster_layer
                    #==stand alone==#==QGIS Console===

print(l.isValid()) # False # True print(l.dataProvider()) # None # QgsRasterDataProvider<> print(l.providerType()) # "wms" # "wms" print(l.rasterType()) # 0 # 3

What do I need to set up for stand alone script to be successful with adding Open Street Maps to my project.

Complete code below

from qgis.PyQt.QtCore import Qt, QSize
from qgis.core import QgsApplication, QgsProject, \
    QgsRasterLayer, QgsCoordinateReferenceSystem, \
    QgsRectangle, QgsMapRendererParallelJob
from qgis.gui import QgsMapCanvas, QgsLayerTreeMapCanvasBridge

crs = QgsCoordinateReferenceSystem("EPSG:3857")

APPLICATION

QgsApplication.setPrefixPath("/usr/bin/qgis", True) qgs = QgsApplication([], False) qgs.initQgis()

PROJECT

proj = QgsProject.instance() proj.setCrs(crs) root = proj.layerTreeRoot()

CANVAS

canvas = QgsMapCanvas() bridge = QgsLayerTreeMapCanvasBridge(proj.layerTreeRoot(), canvas) canvas.setCanvasColor(Qt.red) canvas.setDestinationCrs(crs) extent = QgsRectangle(1042595, 7522132,1131975, 7536048) canvas.setExtent(extent) canvas.refresh() canvas.update() mapSettings = canvas.mapSettings() mapSettings.setOutputSize( QSize(800,800) )

LAYERS

urlWithParams = 'type=xyz&url=https://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=19&zmin=0' raster_layer = QgsRasterLayer(urlWithParams, 'OpenStreetMap', 'wms') proj.addMapLayer(raster_layer) assert raster_layer.isValid() canvas.setLayers([raster_layer])

RENDERING

job = QgsMapRendererParallelJob(mapSettings) job.start() job.waitForFinished() image = job.renderedImage() image.save('foo.png') proj.write('foo.qgz')

Environment

The Python path looks slightly different for the two cases, but I believe they are essentially running the same environment. The following code produced the exact same output.

import sys, qgis, qgis.core
print(sys.version) # 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0]
print(qgis.__file__) # /usr/lib/python3/dist-packages/qgis/__init__.py
print(qgis.core.Qgis.QGIS_VERSION) # 3.24.3-Tisler
Taras
  • 32,823
  • 4
  • 66
  • 137
Mads Skjern
  • 759
  • 3
  • 15
  • Do you have the same environment as QGIS when you run the python interpreter? – Andreas Müller Jun 06 '22 at 09:48
  • @AndreasMüller: I have added a section; "Environment". Please let me know if there are more details you need. – Mads Skjern Jun 07 '22 at 02:38
  • Have you set a GDAL_DATA environnement variable ? At start, QGIS sets different environnement variables so if they are missing, your code in standalone won't work. On my configuration, your code works perfectly. – J. Monticolo Jun 07 '22 at 08:11
  • I'm on windows, but you should start python with the same environment (variables) than qgis uses. There is probably a shell script to start qgis you can use as a template. – Andreas Müller Jun 08 '22 at 06:20

1 Answers1

1

After learning how (source), I set my code to print the error message associated with the layer being invalid:

if not raster_layer.isValid():
    print("error summary:", raster_layer.error().summary())
assert raster_layer.isValid()

And the resulting error summary was:

"Cannot instantiate the 'wms' data provider"

So I googled for this problem and found this Q&A (Can't get dataProvider object outside of QGIS python Interpreter).

In the end I simply had to use a different prefix path. And after that the same data providers were available in the stand alone as within QGIS, and now it the layer was valid.

QgsApplication.setPrefixPath("/usr", True) # instead of /usr/bin/qgis

More details

I set my code to also print the available data providers:

if not raster_layer.isValid():
    print("error summary:", raster_layer.error().summary())
from qgis.core import QgsProviderRegistry
print("providers: ", QgsProviderRegistry.instance().providerList())
assert raster_layer.isValid()

The results, when prefix path was "/usr/bin/python" were:

  • QGIS Python Console: ['DB2', 'OAPIF', 'WFS', 'arcgisfeatureserver', 'arcgismapserver', 'delimitedtext', 'ept', 'gdal', 'geonode', 'gpx', 'grass', 'grassraster', 'hana', 'mdal', 'memory', 'mesh_memory', 'mssql', 'ogr', 'pdal', 'postgres', 'postgresraster', 'spatialite', 'vectortile', 'virtual', 'virtualraster', 'wcs', 'wms']
  • standalone script / shell: ['ept', 'gdal', 'memory', 'mesh_memory', 'ogr', 'vectortile']
Mads Skjern
  • 759
  • 3
  • 15