QGIS3.28的二次开发七:创建地图工具

地图工具是输入设备(一般指鼠标与键盘)与画布(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 该类提供了一个透明的覆盖画布项,用于突出显示地图画布上的特征或几何图形。

例如这样可以设置我们的地图某块区域高亮为红色

cpp 复制代码
color = QColor(Qt.red)
highlight = QgsHighlight(mapCanvas, feature, layer)
highlight.setColor(color)
color.setAlpha(50)    // 设置颜色透明度,0-255越小越透明
highlight.setFillColor(color)
highlight.show()

我们先看看运行效果

完整代码如下:

main.cpp

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

cpp 复制代码
#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

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 复制代码
<?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>
相关推荐
GIS思维24 天前
零代码实现下载高德地图AOI矢量shp边界,如武夷山风景区矢量边界
arcgis·gis·qgis·高德地图·aoi
strexGM25 天前
qgis坐标转换
qgis
WW、forever1 个月前
【WRF数据处理】基于GIS4WRF插件将geotiff数据转为tiff(geogrid,WPS所需数据)
qgis·1024程序员节·wrf
wblong_cs2 个月前
QGIS 高程点生成Mesh
qgis·ply·mesh·tin
李佩锦peijin6 个月前
Python导入Shapefile到PostGIS的常见问题和解决方案
python·postgresql·qgis·postgis
水w7 个月前
如何下载省,市,区县行政区Shp数据
gis·qgis·地图·shp
爱地球的曲奇7 个月前
【GIS学习笔记】ArcGIS/QGIS如何修改字段名称、调整字段顺序?
经验分享·笔记·学习·arcgis·qgis
爱地球的曲奇7 个月前
【GIS学习笔记】ArcGIS/QGIS怎么按矢量属性拆分矢量数据/栅格数据
经验分享·笔记·学习·arcgis·qgis
长沙红胖子Qt8 个月前
QGIS开发笔记(一):QGIS介绍、软件下载和加载shp地图数据Demo
gis·qgis·qgis加载shp地图
业余敲代码9 个月前
QGIS教程 加载shape数据 矢量数据(批量)
gis·qgis·地图·矢量数据·shape·地理信息系统