Outdated version of the documentation. Find the latest one here.

Using Map Canvas

The Map canvas widget is probably the most important widget within QGIS because it shows the map composed from overlaid map layers and allows interaction with the map and layers. The canvas shows always a part of the map defined by the current canvas extent. The interaction is done through the use of map tools: there are tools for panning, zooming, identifying layers, measuring, vector editing and others. Similar to other graphics programs, there is always one tool active and the user can switch between the available tools.

Map canvas is implemented as QgsMapCanvas class in qgis.gui module. The implementation is based on the Qt Graphics View framework. This framework generally provides a surface and a view where custom graphics items are placed and user can interact with them. We will assume that you are familiar enough with Qt to understand the concepts of the graphics scene, view and items. If not, please make sure to read the overview of the framework.

Whenever the map has been panned, zoomed in/out (or some other action triggers a refresh), the map is rendered again within the current extent. The layers are rendered to an image (using QgsMapRenderer class) and that image is then displayed in the canvas. The graphics item (in terms of the Qt graphics view framework) responsible for showing the map is QgsMapCanvasMap class. This class also controls refreshing of the rendered map. Besides this item which acts as a background, there may be more map canvas items. Typical map canvas items are rubber bands (used for measuring, vector editing etc.) or vertex markers. The canvas items are usually used to give some visual feedback for map tools, for example, when creating a new polygon, the map tool creates a rubber band canvas item that shows the current shape of the polygon. All map canvas items are subclasses of QgsMapCanvasItem which adds some more functionality to the basic QGraphicsItem objects.

Pour résumer, l’architecture du canevas de carte repose sur trois concepts:

  • le canevas de carte — pour visualiser la carte

  • map canvas items — additional items that can be displayed in map canvas
  • map tools — for interaction with map canvas

Intégrer un canevas de carte

Le canevas de carte est un objet comme tous les autres objets Qt, on peut donc l’utiliser simplement en le créant et en l’affichant:

canvas = QgsMapCanvas()
canvas.show()

This produces a standalone window with map canvas. It can be also embedded into an existing widget or window. When using .ui files and Qt Designer, place a QWidget on the form and promote it to a new class: set QgsMapCanvas as class name and set qgis.gui as header file. The pyuic4 utility will take care of it. This is a very convenient way of embedding the canvas. The other possibility is to manually write the code to construct map canvas and other widgets (as children of a main window or dialog) and create a layout.

Par défaut, le canevas de carte a un arrière-plan noir et n’utilise pas l’anticrénelage. Pour afficher un arrière-plan blanc et activer l’anticrénelage pour un rendu plus lisse:

canvas.setCanvasColor(Qt.white)
canvas.enableAntiAliasing(True)

(In case you are wondering, Qt comes from PyQt4.QtCore module and Qt.white is one of the predefined QColor instances.)

Now it is time to add some map layers. We will first open a layer and add it to the map layer registry. Then we will set the canvas extent and set the list of layers for canvas

layer = QgsVectorLayer(path, name, provider)
if not layer.isValid():
  raise IOError, "Failed to open the layer"

# add layer to the registry
QgsMapLayerRegistry.instance().addMapLayer(layer)

# set extent to the extent of our layer
canvas.setExtent(layer.extent())

# set the map canvas layer set
canvas.setLayerSet([QgsMapCanvasLayer(layer)])

Après exécution de ces commandes, le canevas de carte devrait afficher la couche chargée.

Utiliser les outils cartographiques avec le canevas

The following example constructs a window that contains a map canvas and basic map tools for map panning and zooming. Actions are created for activation of each tool: panning is done with QgsMapToolPan, zooming in/out with a pair of QgsMapToolZoom instances. The actions are set as checkable and later assigned to the tools to allow automatic handling of checked/unchecked state of the actions – when a map tool gets activated, its action is marked as selected and the action of the previous map tool is deselected. The map tools are activated using setMapTool() method.

from qgis.gui import *
from PyQt4.QtGui import QAction, QMainWindow
from PyQt4.QtCore import SIGNAL, Qt, QString

class MyWnd(QMainWindow):
  def __init__(self, layer):
    QMainWindow.__init__(self)

    self.canvas = QgsMapCanvas()
    self.canvas.setCanvasColor(Qt.white)

    self.canvas.setExtent(layer.extent())
    self.canvas.setLayerSet([QgsMapCanvasLayer(layer)])

    self.setCentralWidget(self.canvas)

    actionZoomIn = QAction(QString("Zoom in"), self)
    actionZoomOut = QAction(QString("Zoom out"), self)
    actionPan = QAction(QString("Pan"), self)

    actionZoomIn.setCheckable(True)
    actionZoomOut.setCheckable(True)
    actionPan.setCheckable(True)

    self.connect(actionZoomIn, SIGNAL("triggered()"), self.zoomIn)
    self.connect(actionZoomOut, SIGNAL("triggered()"), self.zoomOut)
    self.connect(actionPan, SIGNAL("triggered()"), self.pan)

    self.toolbar = self.addToolBar("Canvas actions")
    self.toolbar.addAction(actionZoomIn)
    self.toolbar.addAction(actionZoomOut)
    self.toolbar.addAction(actionPan)

    # create the map tools
    self.toolPan = QgsMapToolPan(self.canvas)
    self.toolPan.setAction(actionPan)
    self.toolZoomIn = QgsMapToolZoom(self.canvas, False) # false = in
    self.toolZoomIn.setAction(actionZoomIn)
    self.toolZoomOut = QgsMapToolZoom(self.canvas, True) # true = out
    self.toolZoomOut.setAction(actionZoomOut)

    self.pan()

  def zoomIn(self):
    self.canvas.setMapTool(self.toolZoomIn)

  def zoomOut(self):
    self.canvas.setMapTool(self.toolZoomOut)

  def pan(self):
    self.canvas.setMapTool(self.toolPan)

You can put the above code to a file, e.g. mywnd.py and try it out in Python console within QGIS. This code will put the currently selected layer into newly created canvas

import mywnd
w = mywnd.MyWnd(qgis.utils.iface.activeLayer())
w.show()

Just make sure that the mywnd.py file is located within Python search path (sys.path). If it isn’t, you can simply add it: sys.path.insert(0, '/my/path') — otherwise the import statement will fail, not finding the module.

Contour d’édition et symboles de sommets

To show some additional data on top of the map in canvas, use map canvas items. It is possible to create custom canvas item classes (covered below), however there are two useful canvas item classes for convenience: QgsRubberBand for drawing polylines or polygons, and QgsVertexMarker for drawing points. They both work with map coordinates, so the shape is moved/scaled automatically when the canvas is being panned or zoomed.

Pour afficher une polyligne:

r = QgsRubberBand(canvas, False)  # False = not a polygon
points = [QgsPoint(-1, -1), QgsPoint(0, 1), QgsPoint(1, -1)]
r.setToGeometry(QgsGeometry.fromPolyline(points), None)

Pour afficher un polygone:

r = QgsRubberBand(canvas, True)  # True = a polygon
points = [[QgsPoint(-1, -1), QgsPoint(0, 1), QgsPoint(1, -1)]]
r.setToGeometry(QgsGeometry.fromPolygon(points), None)

Veuillez noter que les points d’un polygone ne sont pas stockés dans une liste. En fait, il s’agit d’une liste d’anneaux contenants les anneaux linéaires du polygones: le premier anneau est la limite extérieure, les autres (optionnels) anneaux correspondent aux trous dans le polygone.

Les contours d’édition peut être personnalisés pour changer leur couleur ou la taille de la ligne:

r.setColor(QColor(0, 0, 255))
r.setWidth(3)

The canvas items are bound to the canvas scene. To temporarily hide them (and show again, use the hide() and show() combo. To completely remove the item, you have to remove it from the scene of the canvas

canvas.scene().removeItem(r)

(en C++, il est possible de juste supprimer l’objet mais sous Python del r détruira juste la référence et l’objet existera toujours étant donné qu’il appartient au canevas).

Rubber band can be also used for drawing points, however QgsVertexMarker class is better suited for this (QgsRubberBand would only draw a rectangle around the desired point). How to use the vertex marker

m = QgsVertexMarker(canvas)
m.setCenter(QgsPoint(0, 0))

This will draw a red cross on position [0,0]. It is possible to customize the icon type, size, color and pen width

m.setColor(QColor(0, 255, 0))
m.setIconSize(5)
m.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X
m.setPenWidth(3)

For temporary hiding of vertex markers and removing them from canvas, the same applies as for the rubber bands.

Ecrire des outils cartographiques personnalisés

You can write your custom tools, to implement a custom behaviour to actions performed by users on the canvas.

Map tools should inherit from the QgsMapTool class or any derived class, and selected as active tools in the canvas using the setMapTool() method as we have already seen.

Voici un exemple d’outil cartographique qui permet de définir une emprise rectangulaire en cliquant et en déplaçant la souris sur le canevas. Lorsque le rectangle est dessiné, il exporte les coordonnées de ses limites dans la console. On utilise des éléments de contour d’édition décrits auparavant pour afficher le rectangle sélectionné au fur et à mesure de son dessin.

class RectangleMapTool(QgsMapToolEmitPoint):
  def __init__(self, canvas):
      self.canvas = canvas
      QgsMapToolEmitPoint.__init__(self, self.canvas)
      self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
      self.rubberBand.setColor(Qt.red)
      self.rubberBand.setWidth(1)
      self.reset()

  def reset(self):
      self.startPoint = self.endPoint = None
      self.isEmittingPoint = False
      self.rubberBand.reset(QGis.Polygon)

  def canvasPressEvent(self, e):
      self.startPoint = self.toMapCoordinates(e.pos())
      self.endPoint = self.startPoint
      self.isEmittingPoint = True
      self.showRect(self.startPoint, self.endPoint)

  def canvasReleaseEvent(self, e):
      self.isEmittingPoint = False
      r = self.rectangle()
      if r is not None:
        print "Rectangle:", r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum()

  def canvasMoveEvent(self, e):
      if not self.isEmittingPoint:
        return

      self.endPoint = self.toMapCoordinates(e.pos())
      self.showRect(self.startPoint, self.endPoint)

  def showRect(self, startPoint, endPoint):
      self.rubberBand.reset(QGis.Polygon)
      if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
        return

      point1 = QgsPoint(startPoint.x(), startPoint.y())
      point2 = QgsPoint(startPoint.x(), endPoint.y())
      point3 = QgsPoint(endPoint.x(), endPoint.y())
      point4 = QgsPoint(endPoint.x(), startPoint.y())

      self.rubberBand.addPoint(point1, False)
      self.rubberBand.addPoint(point2, False)
      self.rubberBand.addPoint(point3, False)
      self.rubberBand.addPoint(point4, True)    # true to update canvas
      self.rubberBand.show()

  def rectangle(self):
      if self.startPoint is None or self.endPoint is None:
        return None
      elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y():
        return None

      return QgsRectangle(self.startPoint, self.endPoint)

  def deactivate(self):
      super(RectangleMapTool, self).deactivate()
      self.emit(SIGNAL("deactivated()"))

Ecrire des éléments de canevas de carte personnalisés

A FAIRE :

Comment créer un objet de canevas de carte ?

import sys
from qgis.core import QgsApplication
from qgis.gui import QgsMapCanvas

def init():
  a = QgsApplication(sys.argv, True)
  QgsApplication.setPrefixPath('/home/martin/qgis/inst', True)
  QgsApplication.initQgis()
  return a

def show_canvas(app):
  canvas = QgsMapCanvas()
  canvas.show()
  app.exec_()
app = init()
show_canvas(app)