Usando el Lienzo de Mapa¶
Advertencia
Despite our constant efforts, information beyond this line may not be updated for QGIS 3. Refer to https://qgis.org/pyqgis/master for the python API documentation or, give a hand to update the chapters you know about. Thanks.
El widget del lienzo del mapa es probablemente el widget más importante dentro de QGIS porque muestra el mapa integrado de capas de mapas superpuestos y permite la interacción con el mapa y las capas. El lienzo muestra siempre una parte del mapa definido por el alcance del lienzo actual. La interacción se realiza mediante el uso de herramientas de mapa: hay herramientas para desplazamiento, zum, la identificación de las capas, de medida, para editar vectores y otros. Al igual que en otros programas de gráficos, siempre hay una herramienta activa y el usuario puede cambiar entre las herramientas disponibles.
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.
Para resumir, la arquitectura del lienzo de mapa consiste en tres conceptos:
lienzo de mapa — para la visualización del mapa
Los elementos de lienzo de mapa — los elementos adicionales que se pueden desplegar en un lienzo de mapa
herramientas de mapa — para interactuar con el lienzo del mapa
Lienzo de mapa insertado¶
El lienzo de mapa es un widget como cualquier otro widget Qt, por lo que utilizarlo es tan sencillo como crearlo y mostrarlo
canvas = QgsMapCanvas()
canvas.show()
Esto produce una ventana independiente con el lienzo de mapa. Puede también ser incrustado en un widget existente o ventana. Al utilizar archivo ui y Qt Designer, coloque un QWidget
sobre el formulario y promuévalo a una nueva clase: establezca QgsMapCanvas
como nombre de clase y qgis.gui
como archivo de encabezado. La utilidad pyuic5
se hará cargo de ella. Esta es una forma conveniente de incrustar el lienzo. La otra posibilidad es escribir manualmente el código para construir el lienzo del mapa y otros widgets (como hijos de una ventana principal o diálogo) y crea un diseño.
Por defecto, el lienzo de mapa tiene un fondo negro y no utiliza anti-aliasing. Para establecer el fondo blanco y habilitar el anti-aliasing para suavisar la presentación
canvas.setCanvasColor(Qt.white)
canvas.enableAntiAliasing(True)
(En caso de que se esté preguntando, Qt
viene del modulo PyQt.QtCore
y Qt.white
es uno de lo que predefine las instancias QColor
.)
Ahora es tiempo de añadir algunas capas. Primero, abriremos una capa y lo añadiremos al proyecto actual. A continuación, vamos a establecer la extensión del lienzo y la lista de capas para el lienzo
path_to_ports_layer = os.path.join(QgsProject.instance().homePath(),
"data", "ports", "ports.shp")
vlayer = QgsVectorLayer(path_to_ports_layer, "Ports 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])
Después de ejecutar estos comandos, el lienzo debe mostrar la capa que se ha cargado.
Bandas elásticas y marcadores de vértices¶
Para mostrar algunos datos adicionales en la parte superior del mapa en el lienzo, utilice los elementos del lienzo de mapa. Es posible crear clases de elementos del lienzo personalizada (cubiertas más abajo), sin embargo, hay dos clases de elementos de lienzo útiles para mayor comodidad QgsRubberBand
para dibujar polilíneas o polígonos, y QgsVertexMarker
para dibujar puntos. Ambos trabajan con coordenadas de mapa, por lo que la figura se mueve/ se escala de forma automática cuando el lienzo está siendo desplazado o haciendo zum.
Para mostrar una polilínea
r = QgsRubberBand(canvas, False) # False = not a polygon
points = [QgsPoint(-100, 45), QgsPoint(10, 60), QgsPoint(120, 45)]
r.setToGeometry(QgsGeometry.fromPolyline(points), None)
Para mostrar un polígono
r = QgsRubberBand(canvas, True) # True = a polygon
points = [[QgsPointXY(-100, 35), QgsPointXY(10, 50), QgsPointXY(120, 35)]]
r.setToGeometry(QgsGeometry.fromPolygonXY(points), None)
Tenga en cuenta que los puntos de polígonos no es una lista simple: de hecho, es una lista de anillos que contienen lista de anillos del polígono: el primer anillo es el borde exterior, anillos adicionales (opcional) corresponden a los agujeros en el polígono.
Las bandas elásticas permiten algún tipo de personalizacion, es decir, para cambiar su color o ancho de línea
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)
(en C++ es posible simplemente eliminar el elemento, sin embargo en Python del r
sería simplemente suprimir la referencia y el objeto aún existirá ya que es propiedad del lienzo)
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.
Utilizar las herramientas del mapa con el lienzo¶
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 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()
Escribir herramientas de mapa personalizados¶
Puede escribir sus herramientas personalizadas, para implementar un comportamiento personalizado a las acciones realizadas por los usuarios en el lienzo.
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.
Aquí esta un ejemplo de una herramienta de mapa para definir una extensión rectangular haciendo clic y arrastrando en el lienzo. Cuando se define el rectángulo, imprime su limite de coordenadas en la consola. Utiliza los elementos de la banda elástica descrita antes para mostrar el rectángulo seleccionado ya que se esta definiendo.
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()
Escribir elementos de lienzo de mapa personalizado¶
- PENDIENTE:
cómo crear un elemento de lienzo de mapa
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)