3

I'm developping a Python standalone app that needs a dynamic map (with the zoom and pan tools).

I chose to use PyQGIS that allows me to work with QGIS projects. I managed to create a class that takes a path to a project, loads it and displays it. However, by default, the largest layer is shown which may be a problem.

My QGIS project (that can vary, it depends on the needs) has the world's map as its largest layer with some smaller layers, for example a specific town. I want to give to my class a layer (throught a name or id) that will be shown by default (the canvas will be zoomed to that layer).

How can I do it ?

I want to add that, obviously, I searched for a solution before asking my question and tried some options with the QgsMapCanvas' methods (zoomToFeatureExtent and the more simple method zoomIn). But I can't make it work. I don't get any error whatsoever. If I check the extent of the canvas, it's well set (the same as the layer i chose) but I don't have the wanted result on the screen. I don't know what I'm doing wrong.

Here's the code I have that show my QGIS project in a canvas (I deleted the part where i add the zoom and pan tool to make it less long, I don't think it's needed) :

class MyWindow(QMainWindow):
 def __init__(self,path):
 QMainWindow.__init__(self)
 self.canvas = QgsMapCanvas()
 self.canvas.setCanvasColor(Qt.white)
 self.project = QgsProject.instance()
 self.bridge = QgsLayerTreeMapCanvasBridge(self.project.layerTreeRoot(), self.canvas)
 self.project.read(path)
 self.canvas.freeze(True)
 self.canvas.refresh()
 self.canvas.freeze(False)
 self.canvas.repaint()
 self.setCentralWidget(self.canvas)
asked Jul 5, 2021 at 14:21
4
  • How did you use zoomToFeatureExtent() ? Commented Jul 6, 2021 at 8:19
  • @JULESG I don't have the specific code anymore so please don't mind the potential syntax errors. It was something like this : layer = QgsProject.instance().mapLayersByName('name')[0] self.canvas.zoomToFeatureExtent(layer.extent()) I might use it the wrong way though. Commented Jul 6, 2021 at 9:47
  • Instead of self.project = QgsProject.instance() try to use self.project = QgsProject() and then replace every QgsProject.instance() by self.project That's what I did in my plugin and it seems to work Commented Jul 6, 2021 at 10:10
  • @JULESG It doesn't really change anything, I now have an exit code different from 0 but that's it (-1073741819). I changed self.project and used it in the line just as you said. Commented Jul 6, 2021 at 10:50

2 Answers 2

2

I find that the QgsLayerTreeMapCanvasBridge is causing some trouble for zooming on a specific layer because it updates the canvas automatically.

You have to remove it from your code to make it work, here is a snippet that allows me to zoom on a specific layer, "Layer 3", in a QMainWindow:

class MapViewer(QMainWindow):
 def __init__(self):
 QMainWindow.__init__(self)
 self.canvas = QgsMapCanvas()
 self.canvas.setCanvasColor(Qt.white)
 path = "test.qgz"
 self.project = QgsProject().instance()
 self.project.read(path)
 # List the layers in your project and add them to your canvas
 layers = [layer for layer in self.project.mapLayers().values()]
 layers.reverse() # reverse to keep the layer order from the project
 self.canvas.setLayers(layers)
 # Get the layers you want to zoom in by name
 layer = self.project.mapLayersByName("Layer 3")[0]
 self.canvas.zoomToFeatureExtent(layer.extent())
 self.setCentralWidget(self.canvas)

Screenshot of the project test.qgz open in QGIS and open in a QMainWindow with a zoom on "Layer 3" :

Screenshot of the result

answered Jul 6, 2021 at 15:27
2
1

I believe that the proper way to deal with this problem is to add the following line after declaring an instance of QgsLayerTreeMapCanvasBridge.

E.g.

self.bridge = QgsLayerTreeMapCanvasBridge(self.project.layerTreeRoot(), self.canvas)
self.bridge.setAutoSetupOnFirstLayer(False)

You can then use the setExtent() method of the QgsMapCanvas class to set the canvas extent to a layer extent. The advantage of this method over zoomToFeatureExtent() is that it works for both raster and vector layers. If you wish to zoom out slightly from the layer extent you can add a call to zoomByFactor() and pass a float value slightly greater than 1.

Below is a minimal but complete standalone script example. Just change the project path and layer name.

from qgis.core import QgsApplication, QgsProject, QgsRasterLayer
from qgis.gui import QgsMapCanvas, QgsLayerTreeMapCanvasBridge
from PyQt5.QtWidgets import QApplication, QMainWindow
class myWindow(QMainWindow):
 def __init__(self, path, layer_name):
 QMainWindow.__init__(self)
 
 self.path = path
 self.layer_name = layer_name
 
 self.setGeometry(150, 150, 750, 500)
 self.canvas = QgsMapCanvas(self)
 self.setCentralWidget(self.canvas)
 self.project = QgsProject.instance()
 self.bridge = QgsLayerTreeMapCanvasBridge(self.project.layerTreeRoot(), self.canvas)
 self.bridge.setAutoSetupOnFirstLayer(False)
 self.project.read(self.path)
 self.layer = self.project.mapLayersByName(self.layer_name)[0]
 self.canvas.setExtent(self.layer.extent())
 self.canvas.zoomByFactor(1.1)
 self.canvas.refresh()
 
def main():
 app = QApplication([])
 qgs = QgsApplication([], False)
 qgs.setPrefixPath("C:/OSGeo4W64/apps/qgis", True)
 qgs.initQgis()
 
 project_path = 'C:\\Users\\Path\\To\\Your_project.qgs'
 name = 'Layer_name'
 
 w = myWindow(project_path, name)
 w.show()
 sys.exit(app.exec_())
 
if __name__ == "__main__":
 main()

Result:

enter image description here

answered Jul 7, 2021 at 0:36

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.