PyQGisCookbook--使用地图画布(十)


使用地图画布

“地图画布”可能是QGIS中最重要的widget,因为它显示了由叠加的地图图层组成的地图,并允许与地图和图层交互。画布始终显示由当前画布范围定义的地图的一部分。交互是通过使用平移,缩放,识别图层,测量,矢量编辑等地图工具完成的。与其他图形程序类似,总是有一个工具处于激活状态,用户可以在可用工具之间切换。

地图画布是使用 qgis.gui模块中的QgsMapCanvas类实现的。该实现基于Qt Graphics View框架。该框架通常提供放置自定义图元的原生缓存(surface)和视图,并且用户可以与它们交互。我们将假定您足够熟悉Qt的sceen,view和Item的概念。如果没有,请阅读QT框架概述

每当平移,放大,缩小地图(或触发刷新的其他操作)时,都会在当前范围内再次渲染地图。图层被渲染为图片(使用QgsMapRendererJob类),并且显示在画布上。QgsMapCanvas类还控制渲染图的刷新。除了作为背景的Item外,可能还有更多的地图画布Item

典型的地图画布Item是橡皮筋(用于测量,矢量编辑等)或顶点标记。画布Item通常用于为地图工具提供视觉反馈,例如,在创建新多边形时,地图工具会创建橡皮筋画布Item,以显示多边形的当前形状。所有地图画布Item都是QgsMapCanvasItem子类, QgsMapCanvasItem继承自QGraphicsItem对象并添加了许多功能。

总而言之,地图画布架构包含三个概念:

  • 地图画布—用于查看地图

  • 地图画布Item可以在地图画布上显示的Item

  • 地图工具-用于与地图画布进行交互

嵌入地图画布

地图画布是一个与其他任何Qt  Widget一样的Widget,因此使用它就像创建和显示它一样简单

canvas = QgsMapCanvas()
canvas.show()

这将生成带有地图画布的独立窗口。也可以将其嵌入现有的Widget中。使用.ui文件和Qt Designer时,将QWidget放置在表单上并通过置QgsMapCanvas为类名,设置qgis.gui为头文件将其提升为新类。该pyuic5实用程序将处理它。这是嵌入画布非常方便的方法。另一种可能性是手动编写代码以构造地图画布和其他Widget(作为主窗口或对话框的子级)并创建布局。

默认情况下,地图画布具有黑色背景,并且不使用抗锯齿。设置白色背景并启用抗锯齿以平滑渲染

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

(如果您知道Qt Qt.white来自PyQt.QtCore模块,并且是预定义在QColor类的实例之一。)

现在该添加一些地图图层了。我们将首先打开一个图层并将其添加到当前项目中。然后我们将设置画布范围并设置画布的层列表

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

执行完这些命令后,画布应显示您已加载的图层。

橡皮筋和顶点标记

要在画布中的地图顶部显示一些其他数据,请使用地图画布Item。可以创建自定义画布项目类(如下所述),但是为了方便起见,有两个有用的画布项目类: QgsRubberBand用于绘制折线或多边形以及 QgsVertexMarker用于绘制点。它们都使用地图坐标,因此在平移或缩放画布时会自动移动/缩放形状。

显示折线

r = QgsRubberBand(canvas, False)  # False = not a polygon
points = [QgsPoint(-100, 45), QgsPoint(10, 60), QgsPoint(120, 45)]
r.setToGeometry(QgsGeometry.fromPolyline(points), None)

显示多边形

r = QgsRubberBand(canvas, True)  # True = a polygon
points = [[QgsPointXY(-100, 35), QgsPointXY(10, 50), QgsPointXY(120, 35)]]
r.setToGeometry(QgsGeometry.fromPolygonXY(points), None)

请注意,多边形的点并不是一个简单的列表:实际上,它是包含多边形线性环的环的列表:第一个环是外边界,其他(可选)环对应于多边形中的孔。

橡皮筋可以进行一些自定义,比如更改其颜色和线宽

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

画布Item绑定到画布场景。要暂时隐藏它们(并再次显示它们),请使用hide()show()组合。要完全删除该Item,您必须将其从画布的场景中删除

canvas.scene().removeItem(r)

(在C ++中,可以直接删除Item,但是在Python中, del r将删除引用,并且该对象仍然是画布所拥有的对象)

橡皮筋也可以用于绘制点,但是 QgsVertexMarker类更适合于此(QgsRubberBand只会在所需点周围绘制一个矩形)。

您可以像这样使用顶点标记:

m = QgsVertexMarker(canvas)
m.setCenter(QgsPointXY(10,40))

这将在位置[10,45]上绘制一个红叉。可以自定义图标类型,大小,颜色和笔宽

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

要临时隐藏和删除顶点标记,方法与橡皮筋相同。

在画布上使用地图工具

以下示例构造了一个包含地图画布和平移和缩放工具的Window。创建了使用地图工具的工具栏:QgsMapToolPan进行平移,一对QgsMapToolZoom放大/缩小。将Action的设checkable设置为true,以允许设置工具栏自动处理Action的选中/未选中状态,使用setMapTool()方法激活地图工具,激活地图工具后Action将标记为已选择,而先前地图工具的Action将被取消选择标记。

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)

您可以尝试在Python控制台编辑器中上述代码。要调用画布窗口,请添加以下行以实例化MyWnd类。他们将在新创建的画布上渲染当前选定的图层

w = MyWnd(iface.activeLayer())
w.show()

编写自定义地图工具

您可以编写自定义工具,以对用户在画布上执行的操作实施自定义行为。

地图工具应继承自QgsMapTool类或任何派生类,就像我们上面已经看到的那样调用画布的setMapTool() 将其选择为画布的活动工具。

以下是一个地图工具的示例,该工具使用前面描述的橡皮筋元素定义了一个矩形区域,且允许通过在画布上单击和拖动来定义矩形范围。所选矩形将在控制台中打印其边界坐标。

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

编写自定义地图画布项目

TODO:

如何创建地图画布项目

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)
下一个   前一个

©版权所有2002-现在,QGIS项目 最近更新于2020年4月3日09:14。

使用Sphinx使用Read the Docs提供的主题构建。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值