The code snippets on this page need the following imports if you’re outside the pyqgis console:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | from qgis.PyQt.QtGui import (
QColor,
)
from qgis.PyQt.QtCore import Qt, QRectF
from qgis.core import (
QgsVectorLayer,
QgsPoint,
QgsPointXY,
QgsProject,
QgsGeometry,
QgsMapRendererJob,
)
from qgis.gui import (
QgsMapCanvas,
QgsVertexMarker,
QgsMapCanvasItem,
QgsRubberBand,
)
|
9. Using the 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 always shows 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.
The map canvas is implemented with the QgsMapCanvas
class in the 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 read the overview of the framework.
Whenever the map has been panned, zoomed in/out (or some other action that triggers
a refresh), the map is rendered again within the current extent. The layers are
rendered to an image (using the QgsMapRendererJob
class) and that image is
displayed on the canvas. The QgsMapCanvas
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 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.
Pentru a rezuma, arhitectura suportului pentru hartă constă în trei concepte:
suportul de hartă — pentru vizualizarea hărții
map canvas items — additional items that can be displayed on the map canvas
map tools — for interaction with the map canvas
9.1. Încapsularea suportului de hartă¶
Map canvas is a widget like any other Qt widget, so using it is as simple as creating and showing it.
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 pyuic5
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.
În mod implicit, canevasul hărții are un fundal negru și nu utilizează anti-zimțare. Pentru a seta fundalul alb și pentru a permite anti-zimțare pentru o redare mai bună
canvas.setCanvasColor(Qt.white)
canvas.enableAntiAliasing(True)
(In case you are wondering, Qt
comes from PyQt.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 current project. Then we will set the canvas extent and set the list of layers for the canvas.
1 2 3 4 5 6 7 8 9 10 11 12 | vlayer = QgsVectorLayer('testdata/airports.shp', "Airports layer", "ogr")
if not vlayer.isValid():
print("Layer failed to load!")
# add layer to the registry
QgsProject.instance().addMapLayer(vlayer)
# set extent to the extent of our layer
canvas.setExtent(vlayer.extent())
# set the map canvas layer set
canvas.setLayers([vlayer])
|
După executarea acestor comenzi, suportul ar trebui să arate stratul pe care le-ați încărcat.
9.2. Benzile elastice și marcajele nodurilor¶
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.
To show a polyline:
r = QgsRubberBand(canvas, False) # False = not a polygon
points = [QgsPoint(-100, 45), QgsPoint(10, 60), QgsPoint(120, 45)]
r.setToGeometry(QgsGeometry.fromPolyline(points), None)
Pentru a afișa un poligon
r = QgsRubberBand(canvas, True) # True = a polygon
points = [[QgsPointXY(-100, 35), QgsPointXY(10, 50), QgsPointXY(120, 35)]]
r.setToGeometry(QgsGeometry.fromPolygonXY(points), None)
Rețineți că punctele pentru poligon nu reprezintă o simplă listă: în fapt, aceasta este o listă de inele conținând inele liniare ale poligonului: primul inel reprezintă granița exterioară, în plus (opțional) inelele corespund găurilor din poligon.
Benzile elastice acceptă unele personalizări, și anume schimbarea culorii și a lățimii liniei
r.setColor(QColor(0, 0, 255))
r.setWidth(3)
The canvas items are bound to the canvas scene. To temporarily hide them (and
show them 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)
(În C + + este posibilă ștergerea doar a elementului, însă în Python del r
ar șterge doar referința iar obiectul va exista în continuare, acesta fiind deținut de suport)
Rubber band can be also used for drawing points, but the
QgsVertexMarker
class is better suited for this
(QgsRubberBand
would only draw a rectangle around the desired point).
You can use the vertex marker like this:
m = QgsVertexMarker(canvas)
m.setCenter(QgsPointXY(10,40))
This will draw a red cross on position [10,45]. 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, use the same methods as for rubber bands.
9.3. Folosirea instrumentelor în suportul de hartă¶
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | from qgis.gui import *
from qgis.PyQt.QtWidgets import QAction, QMainWindow
from qgis.PyQt.QtCore import Qt
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.setLayers([layer])
self.setCentralWidget(self.canvas)
self.actionZoomIn = QAction("Zoom in", self)
self.actionZoomOut = QAction("Zoom out", self)
self.actionPan = QAction("Pan", self)
self.actionZoomIn.setCheckable(True)
self.actionZoomOut.setCheckable(True)
self.actionPan.setCheckable(True)
self.actionZoomIn.triggered.connect(self.zoomIn)
self.actionZoomOut.triggered.connect(self.zoomOut)
self.actionPan.triggered.connect(self.pan)
self.toolbar = self.addToolBar("Canvas actions")
self.toolbar.addAction(self.actionZoomIn)
self.toolbar.addAction(self.actionZoomOut)
self.toolbar.addAction(self.actionPan)
# create the map tools
self.toolPan = QgsMapToolPan(self.canvas)
self.toolPan.setAction(self.actionPan)
self.toolZoomIn = QgsMapToolZoom(self.canvas, False) # false = in
self.toolZoomIn.setAction(self.actionZoomIn)
self.toolZoomOut = QgsMapToolZoom(self.canvas, True) # true = out
self.toolZoomOut.setAction(self.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 try the above code in the Python console editor. To invoke the canvas window,
add the following lines to instantiate the MyWnd
class. They will render the currently
selected layer on the newly created canvas
w = MyWnd(iface.activeLayer())
w.show()
9.4. Dezvoltarea instrumentelor personalizate pentru suportul de hartă¶
You can write your custom tools, to implement a custom behavior 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.
Iată un exemplu de instrument pentru hartă, care permite definirea unei limite dreptunghiulare, făcând clic și trăgând cursorul mouse-ului pe canevas. După ce este definit dreptunghiul, coordonatele sale sunt afișate în consolă. Se utilizează elementele benzii elastice descrise mai înainte, pentru a arăta dreptunghiul selectat, așa cum a fost definit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | class RectangleMapTool(QgsMapToolEmitPoint):
def __init__(self, canvas):
self.canvas = canvas
QgsMapToolEmitPoint.__init__(self, self.canvas)
self.rubberBand = QgsRubberBand(self.canvas, True)
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(True)
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):
QgsMapTool.deactivate(self)
self.deactivated.emit()
|
9.5. Dezvoltarea elementelor personalizate pentru suportul de hartă¶
Here is an example of a custom canvas item that draws a circle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | class CircleCanvasItem(QgsMapCanvasItem):
def __init__(self, canvas):
super().__init__(canvas)
self.center = QgsPoint(0, 0)
self.size = 100
def setCenter(self, center):
self.center = center
def center(self):
return self.center
def setSize(self, size):
self.size = size
def size(self):
return self.size
def boundingRect(self):
return QRectF(self.center.x() - self.size/2,
self.center.y() - self.size/2,
self.center.x() + self.size/2,
self.center.y() + self.size/2)
def paint(self, painter, option, widget):
path = QPainterPath()
path.moveTo(self.center.x(), self.center.y());
path.arcTo(self.boundingRect(), 0.0, 360.0)
painter.fillPath(path, QColor("red"))
# Using the custom item:
item = CircleCanvasItem(iface.mapCanvas())
item.setCenter(QgsPointXY(200,200))
item.setSize(80)
|