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

news2024/12/24 2:39:10

地图工具是输入设备(一般指鼠标与键盘)与画布(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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/868745.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

知识付费小程序制作

知识付费小程序是一种通过在线平台提供知识付费服务的应用程序。它为知识提供者和知识需求者之间搭建了一个便捷的交流平台&#xff0c;让用户可以通过支付一定费用来获取专业的知识、技能或经验。 这类小程序通常具有以下核心功能&#xff1a; 1. 课程发布与管理&#xff1a…

内网渗透——入门篇(5%)

内网渗透——入门篇&#xff08;5%&#xff09; 参考文章&#xff1a;​​内网渗透学习&#xff08;一&#xff09;内网入门基础 - leviathan123 - 博客园 (cnblogs.com)​​​ 第一部分 内网常用名词及工具介绍 内网也指局域网&#xff0c;是指在某一区域由多台计算机互连而…

SQLServer 实现数据库表复制到另一个数据库_kaic

SQLServer 实现数据库表复制到另一个数据库 一、如果两个数据库在同一台服务器上 1、复制表结构和数据(A->B)&#xff1a; SELECT * INTO DatabaseB.dbo.TableB FROM DatabaseA.dbo.TableA 2、仅仅复制表结构(A->B)&#xff1a; SELECT * INTO DatabaseB.dbo.TableB …

shell和反弹shell

文章目录 是什么&#xff1f;bash是什么&#xff1f;反弹shell 是什么&#xff1f; Shell 是一个用 C 语言编写的程序&#xff0c;它是用户使用 Linux 的桥梁。Shell 既是一种命令语言&#xff0c;又是一种程序设计语言。 Shell 是指一种应用程序&#xff0c;这个应用程序提供了…

MySQL语句总和之表数据操作(增删改查)

目录 1、增加 insert into 表 (字段1&#xff0c; 字段3&#xff0c; 字段5) values(value1, value2, value3&#xff09; insert into 表 [(字段1&#xff0c; 字段2&#xff0c; 字段3....)] values(value1, value2,value3.....)[,(value1, value2, value3....) .....] in…

2023年即将推出的CSS特性对你影响大不大?

Google开发者大会每年都会提出有关于 Web UI 和 CSS 方面的新特性&#xff0c;今年又上新了许多新功能&#xff0c;今天就从中找出了影响最大的几个功能给大家介绍一下 :has :has() 可以通过检查父元素是否包含特定子元素或这些子元素是否处于特定状态来改变样式&#xff0c;也…

Python中的诡异事:不可见字符!

文章目录 前言1. 起因2. 调查3. 高能4. 释惑 前言 今天分享一件很诡异的事情&#xff0c;我写代码的时候遇到了不可见的字符&#xff01;&#xff01;&#xff01; 1. 起因 今天在使用pipreqs导出项目中所依赖的库时突然报错了&#xff1a; pipreqs . --encodingutf-8 --forc…

AtcoderABC222场

A - Four DigitsA - Four Digits 题目大意 给定一个整数N&#xff0c;其范围在0到9999之间&#xff08;包含边界&#xff09;。在将N转换为四位数的字符串后&#xff0c;输出它。如果N的位数不足四位&#xff0c;则在前面添加必要数量的零。 思路分析 可以使用输出流的格式设…

Petrel解释二维浅地层数据

Petrel是斯伦贝谢开发的一款地质解释和建模软件&#xff0c;有点像地理信息系统的ArcGIS&#xff0c;主要用于数据分析和展示。它不是用来处理原始数据的&#xff0c;而是集成各种处理后的结果数据进行特征分析和目标拾取。当然&#xff0c;它也能读取原始数据&#xff0c;比如…

安装程序指南:FMSoft_uniGUI_Complete_Professional1.9.1567

解压和安装程序指南&#xff1a;FMSoft_uniGUI_Complete_Professional https://t00y.com/dir/1041485-3049764-93c76d?56118062 引言&#xff1a; 在开发软件的过程中&#xff0c;我们经常需要安装各种工具和框架来帮助我们实现项目的目标。本文将为您提供一个详细的指南&…

怎么入驻抖音的产业带服务商呢?

作为互联网行业中的明星企业之一&#xff0c;抖音电商近年来一直备受市场瞩目&#xff0c;甚至于某种角度而言&#xff0c;围绕抖音电商的研究和解读已成为一门“显学”。 如果说2021年之前&#xff0c;抖音试水电商业务的方式大多以主播、品牌及商家申请找cmxyci自发摸索为主…

漫谈拥塞控制: a Decade of Wasted Bandwidth?

梭子蟹终于上市了&#xff0c;早早起来准备去买来尝鲜&#xff0c;出发之前想起大概 2016&#xff0c;2017 年左右温州老板推荐给我的一篇好论文&#xff1a;The Linux Scheduler: a Decade of Wasted Cores&#xff0c;但有点长&#xff0c;就读个梗概&#xff1a;a Decade of…

中科亿海微ROM使用

标题 ROM&#xff08;Read-Only Memory&#xff0c;只读存储器&#xff09;是一种在FPGA&#xff08;Field-Programmable Gate Array&#xff0c;现场可编程门阵列&#xff09;中常用的存储器类型。与RAM&#xff08;Random Access Memory&#xff0c;机存取存储器&#xff09;…

用HTML+JavaScript构建C++类(Class)代码转换为MASM32代码的平台

一、需求分析 在使用MASM32编写Windows应用程序时&#xff0c;经常要调用Windows API接口函数 和 相应的数据结构&#xff0c;这些数据结构中有很多是类&#xff08;Class&#xff09;&#xff0c;对于那些在MASM32没有定义的类&#xff0c;我们需要自己来转换。比如&#xff…

Mybatis 初识

目录 1. MyBatis入门 1.1 MyBatis的定义 1.2 MyBatis的核心 MyBatis的核心 JDBC 的操作回顾 1.3 MyBatis的执行流程 MyBatis基本工作原理 2. MyBatis的使用 2.1 MyBatis环境搭建 2.1.1 创建数据库和表 2.1.2 添加MyBatis框架支持 老项目添加MyBatis 新项目添加MyBatis 2.1.3 设…

第一份工作要怎么找呀

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

LeetCode150道面试经典题--赎金信(简单)

1.题目 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 2.示例 3.思路 统计字…

JVM 中一次完整的 GC 流程和对象如何晋升到老年代?

前言 今天来分享一道比较好的面试题&#xff0c;JVM 中一次完整的 GC 流程是怎样的以及对象如何晋升到老年代&#xff1f;对于这个问题&#xff0c;我们一起看看考察点和比较好的回答吧。 考察点 Java 虚拟机能够替我们回收内存空间&#xff0c;清除垃圾对象&#xff0c;因此…

《嵌入式 - 嵌入式大杂烩》SVC和PendSV异常详解

1 操作模式 在讨论PendSV和SVC异常前,需要先了解Cortex-M的模式和两个特权等级。 Figure 1 1 操作模式和特权等级 两种模式为handler模式和线程(thread)模式,这两种模式是为了区别正在执行代码的类型;handler模式为异常处理例程的代码;线程模式为普通应用程序的代码。 两…

【云原生】Kubernetes 概述

Kubernetes 概述 1.Kubernetes 简介 Kubernetes 是一个可移植的、可扩展的、用于管理容器化工作负载和服务的开源平台&#xff0c;它简化&#xff08;促进&#xff09;了声明式配置和自动化。它有一个庞大的、快速增长的生态系统。Kubernetes 的服务、支持和工具随处可见。 K…