地图工具是输入设备(一般指鼠标与键盘)与画布(QgsMapCanvas)的交互接口。它负责处理所有用户通过输入设备(鼠标和键盘)和画布互动的操作,例如镜头控制、要素绘制、标识工具等。
QgsMapTool 是地图工具的基类,继承自QObject,提供了地图工具和画布交互的基本接口,地图工具子类通过重写这些虚函数接口实现自己的独特功能,例如
- 鼠标在画布上按下的 void QgsMapTool::canvasPressEvent(QgsMapMouseEvent * e)和释放的void QgsMapTool::canvasReleaseEvent(QgsMapMouseEvent * e)
- 鼠标在画布上移动 void QgsMapTool::canvasMoveEvent(QgsMapMouseEvent * e)
- 鼠标在画布上双击 void QgsMapTool::canvasDoubleClickEvent(QgsMapMouseEvent * e)
- 键盘按键按下 void QgsMapTool::keyPressEvent(QKeyEvent * e) 和键盘按键释放 void QgsMapTool::keyReleaseEvent(QKeyEvent * e) (工具激活时)
- 自定义右键菜单 void QgsMapTool::populateContextMenu(QMenu * menu)
一张画布,即一个QgsMapCanvas实例,在同一时刻仅允许一个地图工具处于“激活”状态。通过继承该基类,QGIS 自己实现了部分地图工具。我们也可以继承QgsMapTool基类来实现自定义地图工具。
我们这里写段代码测试一下QGIS的三个地图工具,分别为
QgsMapToolPan 用于平移地图的地图工具。
QgsMapToolEmitPoint 使用此工具当单击地图时,它只是发出一个点。将一个槽连接到它的canvasClicked()信号可以为传入点实现自定义行为。
QgsMapToolIdentify 用于识别图层特征的地图工具。使用此工具当单击地图时选择一个点,进行识别:对于栅格层显示底层像素值,对于矢量层显示搜索半径内的特征属性(当矢量层处于编辑模式时允许编辑值)。我们用它下面的继承类QgsMapToolIdentifyFeature来写代码。
QgsMapToolIdentifyFeature 继承自上面的QgsMapToolIdentify,用于识别所选层上的特征。用户可以单击地图来自动识别这块区域具有的特征,然后会发出一个信号。
我们为了能清楚看到点击的是地图的哪块区域,需要将这块点击的区域高亮显示,用到的类为QgsHighlight 该类提供了一个透明的覆盖画布项,用于突出显示地图画布上的特征或几何图形。
例如这样可以设置我们的地图某块区域高亮为红色
color = QColor(Qt.red)
highlight = QgsHighlight(mapCanvas, feature, layer)
highlight.setColor(color)
color.setAlpha(50) // 设置颜色透明度,0-255越小越透明
highlight.setFillColor(color)
highlight.show()
我们先看看运行效果
完整代码如下:
main.cpp
#include "qgis03_MapTools.h"
#include <QtWidgets/QApplication>
#include <qgsapplication.h>
#include <qgsproviderregistry.h>
int main(int argc, char *argv[])
{
QgsApplication a(argc, argv, true);
// 设置并检查数据插件目录
QgsProviderRegistry::instance("D:\\OSGeo4W\\apps\\qgis-ltr\\plugins");
// 设置 GDAL 数据目录环境变量
qputenv("GDAL_DATA", "D:\\OSGeo4W\\apps\\gdal\\share\\gdal");
// 实例化一个qgis30_MapTools窗口
qgis03_MapTools w;
// 展示窗口
w.show();
return a.exec();
}
qgis03_MapTools.h
#pragma once
#include <QtWidgets/QMainWindow> // 继承自QMainWindow
#include "ui_qgis03_MapTools.h" // UI界面的头文件
#include <qgsmapcanvas.h> // 创建画布
#include <qgsmaptoolpan.h> // 移动地图
#include <qgsmaptoolemitpoint.h> // 单击发出一个点,自定义槽函数实现行为
#include <qgsmaptoolidentifyfeature.h> // 单击地图,识别选择图层的特征
#include <qgshighlight.h> // 高亮显示某块地图
class qgis03_MapTools : public QMainWindow
{
Q_OBJECT
public:
qgis03_MapTools(QWidget *parent = Q_NULLPTR);
private:
Ui::qgis03_MapToolsClass ui; // 示例化一个UI对象
// 画布
QgsMapCanvas mCanvas;
// 漫游工具
QgsMapToolPan mToolPan;
// 产生点工具
QgsMapToolEmitPoint mToolEmitPoint;
// 标识工具
QgsMapToolIdentifyFeature mToolIdentifyFeature;
// 高亮对象(用于在地图上标出被标识工具选中的要素)
QgsHighlight* mpHighlight = nullptr;
// 地图工具切换选择槽
void onMapToolSelected(bool isChecked);
// 清除高亮对象槽
void clearHighlight();
};
qgis03_MapTools.cpp
#include "qgis03_MapTools.h"
#include <qgsvectorlayer.h>
#include <qmessagebox.h>
#include <qgspointxy.h> // 表示严格的二维位置,只有X和Y坐标
#include <qgsfeature.h> // 封装了一个特性,包括它唯一的ID、几何形状和字段/值属性列表
#include <qgis.h> // 提供了在整个应用程序中使用的全局常量
#include <qgsfield.h> // 矢量图层的属性字段容器
qgis03_MapTools::qgis03_MapTools(QWidget *parent)
: QMainWindow(parent),
mCanvas(this), // QgsMapCanvas (QWidget * parent = nullptr)
mToolPan(&mCanvas), // QgsMapToolPan (QgsMapCanvas *canvas)
mToolEmitPoint(&mCanvas), // QgsMapToolEmitPoint (QgsMapCanvas *canvas)
mToolIdentifyFeature(&mCanvas) // QgsMapToolIdentifyFeature(QgsMapCanvas * canvas, QgsVectorLayer * vl = nullptr)
{
ui.setupUi(this);
// 在主窗体的竖直布局最后添加画布组件
ui.verticalLayout->addWidget(&mCanvas);
// 新建矢量图层
QgsVectorLayer* pVectorLayer = new QgsVectorLayer("E:\\TestImage\\全国省界\\全国省界.shp", "全国省界");
// 将新建的矢量图层添加到画布
mCanvas.setLayers(QList<QgsMapLayer*>() << pVectorLayer);
// 缩放到全图
mCanvas.zoomToFullExtent();
// 自定义QgsMapToolEmitPoint的槽函数,显示一个弹出消息,显示所点击的点位置
// void QgsMapToolEmitPoint::canvasClicked(const QgsPointXY & point,Qt::MouseButton button)
QObject::connect(&mToolEmitPoint, &QgsMapToolEmitPoint::canvasClicked, [=](const QgsPointXY & point, Qt::MouseButton button) {
QMessageBox::information(this, "QgsMapToolEmitPoint", QString("X: %1\nY: %2").arg(QString::number(point.x()), QString::number(point.y())));
});
// 设定QgsMapToolIdentifyFeature工具识别的目标图层
mToolIdentifyFeature.setLayer(pVectorLayer);
// 自定义QgsMapToolIdentifyFeature工具点击后的槽函数
// QgsMapToolIdentifyFeature::featureIdentified信号被重载,要用如下方式来写
QObject::connect(&mToolIdentifyFeature,static_cast<void (QgsMapToolIdentifyFeature::*)(const QgsFeature &)>(&QgsMapToolIdentifyFeature::featureIdentified), [=](const QgsFeature &feature) {
// 清除高亮
clearHighlight();
// 设置高亮对象
mpHighlight = new QgsHighlight(&mCanvas, feature.geometry(), pVectorLayer);
// 设置高亮颜色 DEFAULT_HIGHLIGHT_COLOR = QColor( 255, 0, 0, 128 ), RGBA格式
QColor color = QColor(Qgis::DEFAULT_HIGHLIGHT_COLOR.name());
// 设置高亮颜色透明度
color.setAlpha(Qgis::DEFAULT_HIGHLIGHT_COLOR.alpha());
mpHighlight->setColor(color); // 设置线条/描边颜色
mpHighlight->setFillColor(color); // 填充颜色
// DEFAULT_HIGHLIGHT_BUFFER_MM = 0.5
mpHighlight->setBuffer(Qgis::DEFAULT_HIGHLIGHT_BUFFER_MM); // 设置行/行程缓冲以毫米为单位
// DEFAULT_HIGHLIGHT_MIN_WIDTH_MM = 1.0
mpHighlight->setMinWidth(Qgis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM); // 设置最小线/笔画宽度,以毫米为单位
QString msg;
QgsFields fields = feature.fields(); // QgsFeature.fields()返回与该特性关联的字段映射(不包含属性值)
// qDebug() << fields.names(); // 打印字段名
// qDebug() << feature.fields().names(); // 打印字段名
for (int i = 0; i < fields.count(); i++)
{
// QgsFields.at(int i)返回特定索引处的字段
// QgsFeature.attribute(int fieldIdx) 从其索引查找属性值
msg += QString("%1: %2\n").arg(fields.at(i).name(), feature.attribute(i).toString());
}
// 这样写也可以
/* for (int i = 0; i < feature.fields().count(); i++)
{
msg += QString("%1:%2\n").arg(feature.fields().names()[i], feature.attribute(i).toString());
}*/
QMessageBox::information(this, "QgsMapToolIdentifyFeature", msg);
});
// 工具单选框的选择信号
QObject::connect(ui.radPan, &QRadioButton::toggled, this, &qgis03_MapTools::onMapToolSelected);
QObject::connect(ui.radEmitPoint, &QRadioButton::toggled, this, &qgis03_MapTools::onMapToolSelected);
QObject::connect(ui.radIdentifyFeature, &QRadioButton::toggled, this, &qgis03_MapTools::onMapToolSelected);
}
// 将地图工具和UI界面的按钮绑定
void qgis03_MapTools::onMapToolSelected(bool checked)
{
// 如果按钮被选中,则checked为true,否则为false
if (checked) // 其实在这里,if可以忽略
{
// 清除上一次标识操作留下的高亮
clearHighlight();
// 根据点选的单选框,激活相应的工具
// 注意 QgsMapCanvas 一次只允许有一个工具激活
if (ui.radPan->isChecked())
{
// 设置画布当前正在被使用的地图工具,void QgsMapCanvas::setMapTool(QgsMapTool * mapTool,bool clean = false)
mCanvas.setMapTool(&mToolPan);
}
else if (ui.radEmitPoint->isChecked())
{
mCanvas.setMapTool(&mToolEmitPoint);
}
else if (ui.radIdentifyFeature->isChecked())
{
mCanvas.setMapTool(&mToolIdentifyFeature);
}
}
}
// 清除高亮对象
void qgis03_MapTools::clearHighlight()
{
// 如果高亮对象不为空,则删除该对象
if (mpHighlight)
{
delete mpHighlight;
}
// 使mpHighlight指向nullptr
mpHighlight = nullptr;
}
qgis03_MapTools.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>qgis03_MapToolsClass</class>
<widget class="QMainWindow" name="qgis03_MapToolsClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>qgis03_地图工具</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="radPan">
<property name="text">
<string>QgsMapToolPan</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radEmitPoint">
<property name="text">
<string>QgsMapToolEmitPoint</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radIdentifyFeature">
<property name="text">
<string>QgsMapToolIdentifyFeature</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="qgis03_MapTools.qrc"/>
</resources>
<connections/>
</ui>