Usando el Lienzo de Mapa

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.

El lienzo de mapa esta implementado como clase QgsMapCanvas en el modulo qgis.gui. La implementación se basa en el marco Qt Graphics View. Este marco generalmente proporciona una superficie y una vista donde elementos gráficos personalizados se colocan y el usuario puede interacturar con ellos. Vamos a suponer que está lo suficientemente familiarizado con Qt para comprender los conceptos de la escena gráfica, visión y objetos. Si no es así, asegúrese de leer la vista general del marco.

Cada vez que el mapa ha sido desplazado, zum para acercar /alejar (o algún otra acción que desencadene una actualización), el mapa se representa de nuevo dentro de la extensión actual. Las capas se presentan a una imagen (usando la clase QgsMapRenderer) y esa imagen después se muestra en el lienzo. El elemento de gráficos (en términos del marco de vista de gráficos Qt) responsable de mostrar el mapa es la clase QgsMapCanvasMap. Esta clase también controla la actualización del mapa. Además este elemento que actúa como un fondo, puede haber más elementos de lienzo de mapas. Los elementos de lienzo de mapas típico son bandas de goma (utilizadas para medir, edición vectorial, etc.) o marcadores de vértices. Los elementos de lienzo generalmente se utilizan para dar una retroalimentación visual para herramientas del mapa, por ejemplo, cuando se crea un nuevo polígono, la herramienta del mapa crea una banda borrador en el elemento de mapa que muestra la figura actual del polígono. Todos los elementos de lienzo de mapa están en subclases de QgsMapCanvasItem que añade más funciónalidad a los objetos básicos``QGraphicsItem``.

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 pyuic4 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 PyQt4.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 registro capa de mapa. A continuación, vamos a establecer la extensión del lienzo y la lista de capas para el lienzo

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)])

Después de ejecutar estos comandos, el lienzo debe mostrar la capa que se ha cargado.

Utilizar las herramientas del mapa con el lienzo

El siguiente ejemplo construye una ventana que contiene un lienzo de mapa y herramientas de mapa básicos para desplazar y hacer zum. Las acciones se crean para la activación de cada herramienta: el desplazamiento se hace con QgsMapToolPan, el zum acercar/alejar con un par de instancias QgsMapToolZoom. Las acciones se establecen como comprobables y posteriormente asignados a las herramientas para permitir la manipulación automática de activar/desactivar el estado de las acciones – cuando una herramienta de mapa se activa, su acción es marcada como seleccionada y la acción de la herramienta anterior es deseleccionable. Las herramientas de mapa se activan utilizando el método setMapTool().

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)

Se puede colocar el código anterior a un archivo, por ejemplo mywnd.py y probarlo en la consola de Python dentro de QGIS. Este código colocará la capa seleccionada actualmente dentro del lienzo recién creado.

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

Sólo asegúrese que el archivo mywnd.py se encuentra dentro de la ruta de búsquedas de Python (sys.path). Si no es así, simplemente puede añadirlo: sys.path.insert(0, '/my/path') — de lo contrario la declaración de la importación fallará, al no encontrar el modulo.

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(-1, -1), QgsPoint(0, 1), QgsPoint(1, -1)]
r.setToGeometry(QgsGeometry.fromPolyline(points), None)

Para mostrar un polígono

r = QgsRubberBand(canvas, True)  # True = a polygon
points = [[QgsPoint(-1, -1), QgsPoint(0, 1), QgsPoint(1, -1)]]
r.setToGeometry(QgsGeometry.fromPolygon(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)

Los elementos del lienzo están ligados a la escena del lienzo. Para ocultarlos temporalmente (y para mostrarlos de nuevo, utiliza el combo hide() y show(). Para eliminar por completo el tema, hay que eliminarlo de la escena del lienzo

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)

La banda elástica puede también ser utilizado para dibujar puntos, sin embargo la clase QgsVertexMarker es más adecuado para esto (QgsRubberBand sólo dibuja un rectangulo alrededor del punto deseado). Cómo utilizar el marcador de vértices

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

Este dibujará una cruz roja en posición [0,0]. Es posible personalizar el tipo de icono, tamaño, color y ancho de pluma

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

Para ocultar temporalmente de los marcadores de vértices y borrarlos del lienzo, lo mismo aplica para las bandas elásticas.

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.

Las herramientas de mapa deben heredar de la clase QgsMapTool o cualquier clase derivada, y seleccione como herramienta activa en el lienzo utilizando el método setMapTool() como ya hemos visto.

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, 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):
      QgsMapTool.deactivate(self)
      self.emit(SIGNAL("deactivated()"))

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)