QGIS3.28的二次开发九:添加矢量要素

这篇具有很好参考价值的文章主要介绍了QGIS3.28的二次开发九:添加矢量要素。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

对矢量要素的编辑是 GIS 软件很重要的功能点之一,也是最难实现的功能点之一。编辑矢量要素涉及到很多方面的考虑,包括且不限于矢量要素的几何类型,拓扑关系,构成要素的节点的增删改,编辑会话 (session) 的启动、回溯和提交,要素属性的增删改等。本文不会也不可能涉及到属性编辑的方方面面,仅仅实现了一个添加面要素的地图工具,作抛砖引玉的作用。

我们预计实现如下需求:

  • 参照 QGIS 和 ArcGIS,用一个按钮控制编辑会话的开始和结束,即控制图层处于编辑状态与否。按下表示处于编辑状态,弹起处于非编辑状态;
  • 编辑状态下,激活“绘制多边形”按钮,点击激活添加多边形地图工具,弹起取消激活;
  • 添加多边形地图工具激活时,用户可以在画布上点击绘制多边形:左键添加节点,右键结束当前多边形绘制。

运行效果

程序刚运行起来的效果如下,此时的绘制多边形按钮是点不了的,只有点击开始编辑之后,绘制多边形按钮才可见,再点击绘制多边形按钮,即可开始绘制。
QGIS3.28的二次开发九:添加矢量要素,QGIS,qgis
绘制效果如下
QGIS3.28的二次开发九:添加矢量要素,QGIS,qgis

代码解释

其实QGIS 提供了一个 QgsMapToolCapture类,可以实现上述上面绘制多边形的功能(这是 QGIS 软件自己所使用的地图工具),但不幸的是,这个工具的实例化需要引入 QgsAdvancedDigitizingDockWidget 类。

QgsMapToolCapture::QgsMapToolCapture(
	QgsMapCanvas* canvas,
	QgsAdvancedDigitizingDockWidget* cadDockWidget,
	CaptureMode mode 
)	

从 QGIS 开发文档进入 qgsadvanceddigitizingdockwidget.h 源代码,可以发现一行 include

#include "ui_qgsadvanceddigitizingdockwidgetbase.h"

这个类是一个停靠窗口 QDockWidget,在编辑要素的过程中,会弹出这个界面显示一些信息。因此如果我们强行 include 这个类,编译器会提示找不到 ui_qgsadvanceddigitizingdockwidgetbase.h 导致编译错误。原因是,这个类是一个组件,它是自带 UI 的,QGIS 的源代码中提供了这个组件的 .ui 文件(如同我们自己用 Qt Designer 创建的 .ui 文件一样)。所以,要使用QgsMapToolCapture,我们必须从 GitHub 上下载 QgsAdvancedDigitizingDockWidget 的 .ui 文件 qgsadvanceddigitizingdockwidget.ui,用 uic 编译成 ui_qgsadvanceddigitizingdockwidgetbase.h,放到我们的源代码中,才可以通过编译。

然而事情并没有那么简单。如果用 Qt Designer 打开下载回来的 qgsadvanceddigitizingdockwidget.ui,会发现缺失一大堆资源文件,你用 uic 编译也会报同样的错(虽然也可以编译)。因为 QGIS 做的 UI 有不少共享的资源文件,如图标等,都存在 Qt Designer 的资源描述文件(.qrc)文件中。如果要完整编译你还得去把 QGIS 所有的资源文件下载回来。做这么多麻烦事的目的仅仅是让 QgsMapToolCapture 通过编译,有一点本末倒置的感觉。因此,作者决定放弃使用 QgsMapToolCapture 转而手动实现我们所需要的地图工具。

QgsMapToolCapture 的继承链为
QGIS3.28的二次开发九:添加矢量要素,QGIS,qgis
逐级往上翻看源代码,发现 QgsAdvancedDigitizingDockWidget 是在 QgsMapToolAdvancedDigitizing 这一级引入的。因此我们可以直接继承 QgsMapToolEdit,QgsMapToolEdit又继承自QgsMapTool
QGIS3.28的二次开发九:添加矢量要素,QGIS,qgis
QgsMapToolEdit 相对于基本的 QgsMapTool,额外实现了如下重要功能:

  • currentVectorLayer(): 获取当前正在编辑的图层(即工具所属 QMapCanvas 的当前激活图层
  • createRubberBand(): 可以直接从工具创建 QgsRubberBand,创建后自动附着于工具所属的 QMapCanvas上

这让我们可以比较方便的操作工具所属的图层和画布。我们创建一个 QgsMapToolEdit 的派生类AddPolygonTool,作为我们绘制多边形的地图工具。

AddPolygonTool.h

#pragma once
#include <qgsmaptooledit.h>	// 用于编辑矢量几何图形的地图工具的基类
#include <qgsmapcanvas.h>	// 画布
#include <qgsrubberband.h>	// 用于在绘制折线或多边形时跟踪鼠标,记录绘制图形过程中的临时要素
#include <qgsmapmouseevent.h>	// QGIS中的鼠标事件


class AddPolygonTool :
	public QgsMapToolEdit
{
public:

	AddPolygonTool(QgsMapCanvas* pMapCanvas);

	// 清除当前的 RubberBand
	void clearRubberBand();

protected:

	// 重写 QgsMapTool 的鼠标移动事件
	virtual void canvasMoveEvent(QgsMapMouseEvent *e);

	// 重写 QgsMapTool 的鼠标点击事件
	virtual void canvasPressEvent(QgsMapMouseEvent *e);

private:

	// 当前正在工作的 RubberBand
	QgsRubberBand* mpRubberBand = nullptr;

	// 记录是否正在绘制中,构造函数中初始化为 false
	bool mIsDrawing;
};

接下来重写鼠标点击事件,大体思路是:如当前无工作中的 RubberBand,则创建并存入 mpRubberBand 并点下第一个点。之后用户连续点击鼠标左键往 mpRubberBand 加入点,直到点击鼠标右键。点击鼠标右键表示停止绘制,如此时有效点数小于 3,不足以构成多边形,则丢弃,否则将 mpRubberBand 输出为新的 QgsFeature,加入受编辑的 QgsVectorLayer 之中。

// 重写QgsMapTool的鼠标点击事件
void AddPolygonTool::canvasPressEvent(QgsMapMouseEvent * e)
{
	// 如果当前“橡皮筋”没有被创建
	if (!mpRubberBand)
	{
		// 使用QGIS设置中的颜色/线宽创建一个“橡皮筋”,方法来自QgsMapToolEdit
		mpRubberBand = createRubberBand(QgsWkbTypes::GeometryType::PolygonGeometry);
	}
	// 左键按下
	if (e->button() == Qt::MouseButton::LeftButton)
	{
		mIsDrawing = true;	// 开始绘制
		mpRubberBand->addPoint(e->mapPoint());	// 向“橡皮筋”和更新画布添加一个顶点
	}
	// 右键按下
	else if (e->button() == Qt::MouseButton::RightButton)
	{
		// 停止绘制
		mIsDrawing = false;
		// 如果“橡皮筋”中的顶点数大于3
		if (mpRubberBand->numberOfVertices() >= 3)
		{
			// 创建一个QgsFeature来给当前矢量图层添加特征
			QgsFeature f;
			// QgsGeometry QgsRubberBand::asGeometry() const 返回“橡皮筋”当前对应的几何对象
			f.setGeometry(mpRubberBand->asGeometry());
			// QgsMapToolEdit的currentVectorLayer()返回值类型为QgsVectorLayer 
			// DefMainWindow.cpp中必须设置了当前图层才能用这个方法
			currentVectorLayer()->addFeature(f);
			// QgsMapCanvas * QgsMapTool::canvas() const,返回一个指向画布的指针
			// void QgsMapCanvas::refresh() 重新绘制画布地图	
			canvas()->refresh();
		}
		// 绘制完毕清楚“橡皮筋”
		clearRubberBand();
	}
}

上述代码中,如果当前 RubberBand 内顶点数不小于 3,则符合多边形生成的条件。此时创建一个新的要素 (QgsFeature),将当前 RubberBand 绘制好的几何图形 (QgsGeometry 类型,通过 asGeometry() 方法获取) 赋予新建立的要素,并将此要素通过调用 QgsVectorLayer 的 addFeature() 方法,加入到图层之中。当前图层通过QgsMapToolEdit 的 currentVectorLayer() 获取。最后刷新画布。

最后,无论新要素是否生成,删除当前 RubberBand,准备下一个多边形的绘制。

删除当前RubberBand的代码如下

void AddPolygonTool::clearRubberBand()
{
	// 若当前 RubberBand 为空则直接退出
	if (!mpRubberBand)
	{
		return;
	}

	// 清除其内存并将指针置空
	delete mpRubberBand;
	mpRubberBand = nullptr;
}

然后,为了实现绘制的过程中,RubberBand 的最后一个点“跟着鼠标走”的效果,我们重写工具的鼠标移动事件,这样就可以实现绘制时的“动态”效果。:

// 重写QgsMapTool的鼠标移动事件
void AddPolygonTool::canvasMoveEvent(QgsMapMouseEvent * e)
{
	// 如果mpRubberBand未被创建,或者当前未绘画
	if (!mpRubberBand || !mIsDrawing)
	{
		return;
	}
	// “橡皮筋”最后一个点“跟着鼠标走”的效果,实现动态绘制
	// e->mapPoint()是鼠标的最后位置
	mpRubberBand->movePoint(e->mapPoint());
}

以上代码我们完成了自定义地图工具 AddPolygonTool 的编写。

接下来我们回到主程序窗体。为方便起见,这里我们创建一个“内存图层”用于编辑。内存图层是指不来源于任何外部数据。直接创建于内存之中的图层。在 QGIS 中通过 New Scratch Layer (草稿图层) 创建的图层就是内存图层。
内存图层的创建非常简单,在 URL 中通过正确的语法描述几何数据类型、坐标系、字段信息即可。具体可参考 QgsVectorLayer 的开发文档,写得十分详细。

主窗体代码头文件DefMainWindow.h内容如下

#pragma once
#include <qmainwindow.h>
#include "mainWindow.h"
#include "AddPolygonTool.h"
#include <qgsvectorlayer.h>

class DefMainWindow :
	public QMainWindow
{
public:
	DefMainWindow(QWidget * parent = nullptr);

private:
	Ui::MainWindow ui;

	QgsMapCanvas mCanvas;                        // 画布
	QgsVectorLayer* mpStratchLayer = nullptr;    // 内存图层
	AddPolygonTool* mpToolAddPolygon = nullptr;  // “添加多边形”地图工具

	void onStartEditingButtonToggled(bool isChecked);	// 开始编辑按钮的槽函数
	void onDrawPolygonButtonToggled(bool isChecked);	// 绘制多边形按钮的槽函数
};

接下来写主窗体的构造函数:

#include "DefMainWindow.h"

DefMainWindow::DefMainWindow(QWidget *parent) :
	QMainWindow(parent),
	mCanvas(this)
{
	ui.setupUi(this);
	ui.verticalLayout->addWidget(&mCanvas);
	// 在内存中创建一个多边形图层,使用坐标系EPSG : 4326 (WGS 84), "memory" 表示内存图层
	mpStratchLayer = new QgsVectorLayer("polygon?crs=epsg:4326", u8"临时面图层", "memory");
	mCanvas.setLayers(QList<QgsMapLayer*>() << mpStratchLayer);
	// 设置当前图层,因为AddPolygonTool.cpp中添加的特征是添加到当前图层,因此必须设置
	mCanvas.setCurrentLayer(mpStratchLayer);
	// 将画布缩放到 WGS 84 坐标系的边界范围,否则画布的初始范围与坐标系范围不符,会导致绘制出现问题
	mCanvas.setExtent(QgsCoordinateReferenceSystem("EPSG:4326").bounds());
	// 创建一个画多边形的自定义地图工具AddPolygonTool
	mpToolAddPolygon = new AddPolygonTool(&mCanvas);
	// 绑定“开始编辑”按钮和“绘制多边形”按钮点击事件
	QObject::connect(ui.btnStartEditing, &QPushButton::toggled, this, &DefMainWindow::onStartEditingButtonToggled);
	QObject::connect(ui.btnDrawPolygon, &QPushButton::toggled, this, &DefMainWindow::onDrawPolygonButtonToggled);
}

// 点击开始编辑按钮
void DefMainWindow::onStartEditingButtonToggled(bool checked)
{
	// 如果按钮被按下
	if (checked)
	{
		// 使图层可编辑
		mpStratchLayer->startEditing();
		// 使“绘制多边形”按钮可以点击
		ui.btnDrawPolygon->setEnabled(true);
	}
	// 如果按钮被释放
	else
	{
		// 向底层数据提供程序提交自上次调用startEditing()以来所做的任何缓冲更改
		// 即保留当前已经编辑完毕的数据
		mpStratchLayer->commitChanges();
		// 设置“绘制多边形”按钮为释放状态
		ui.btnDrawPolygon->setChecked(false);
		// 设置“绘制多边形”按钮不可点击
		ui.btnDrawPolygon->setEnabled(false);
	}
}

// 点击绘制多边形按钮
void DefMainWindow::onDrawPolygonButtonToggled(bool checked)
{
	// 如果按钮被按下
	if (checked)
	{
		// 设置当前在画布上使用的地图工具
		mCanvas.setMapTool(mpToolAddPolygon);
	}
	// 如果按钮被释放
	else
	{
		// 清除当前的“橡皮筋”
		mpToolAddPolygon->clearRubberBand();
		// 取消设置当前地图工具或最后一个非缩放工具
		mCanvas.unsetMapTool(mpToolAddPolygon);
	}
}

运行程序,先点击“开始编辑”,再点击“绘制多边形”,然后在画布上点击就能绘制多边形了。点击右键结束当前多边形的绘制,绘制完成的多边形会自动变成临时图层的要素。注意我们并没有实现图层保存的功能,因此你退出程序之后图层就从内存里释放了。

参考文章 mriiiron’s blog文章来源地址https://www.toymoban.com/news/detail-651255.html

到了这里,关于QGIS3.28的二次开发九:添加矢量要素的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • QGIS二次开发:集成二维CAD控件MxDraw

    本文记录在QGIS中集成MxDraw控件的方法。 参见笔者博文“FreeCAD二次开发:集成二维CAD控件MxDraw”所述的安装方法。 参见笔者博文\\\"Windows 10下编译安装QGIS\\\"所述的编译安装方法。 按照QGIS的管理,Qt ui文件可放到src/ui目录及其子目录下。因此,可将MxDrawView.ui拷贝到src/ui/gd目录下,

    2024年02月15日
    浏览(36)
  • qt中使用QGIS实现二次开发导入shp格式地图(MSVC2019_64bit+qt5.15+qgis)

    在qt开发过程中可能要导入自定义格式的图层地图,那么可以把这些数据导入qgis然后导出为shp格式文件,然后下载qgis源码,在qt项目中配置环境变量导入qgis有关的头文件,然后再引入shp数据格式的地图。 QGIS安装 QGIS安装 QGIS导入数据 然后保存点击Layer-save as导出为shp格式文件

    2024年02月05日
    浏览(53)
  • QGIS二次开发六:VS不借助QT插件创建UI界面

    上一篇博客我们说了在VS中如何使用QT插件来创建UI界面,但是我们二次开发QGIS的第一篇博客就说了,最好使用OSGeo4W中自动下载的QT进行QGIS二次开发,这样兼容性是最好的,那么该如何在VS中不使用外部安装的QT以及QT的VS插件情况下进行UI界面的创建和使用呢? 如果你仔细看完

    2024年02月13日
    浏览(38)
  • 12.qgis二次开发qt中实现图层树右键图层更改图层颜色,以及图层标注。

    qgis图层树右键图层更改图层颜色,以及图层标注。 新建一个类mygistreeviewmenu用于管理图层树,新建一个窗体类symbolstyle用于选择颜色。新建一个窗体类labelcontrol用于设置标注。 构造函数中添加 添加一个窗体类labelcontrol用于显示标签。

    2024年02月05日
    浏览(48)
  • 【ArcGIS Pro二次开发】(55):给多个要素或表批量添加字段

    在工作中可能会遇到这样的场景:有多个GDB要素、表格,或者是SHP文件,需要给这个要素或表添加相同的多个字段。 在这种情况下,手动添加就变得很繁琐,于是就做了这个工具。 需求具体如下图: 左图是待处理数据,有shp文件也有gdb文件。 右图是待添加字段的属性结构描

    2024年02月14日
    浏览(43)
  • QGIS开发五:使用UI文件

    前面我们说了在创建项目时创建的是一个空项目,即不使用 Qt 提供的综合开发套件 Qt Creator,也不使用 Qt Visual Studio Tools 这类工具。 但是后面发现,如果我想要有更加满意的界面布局,还是要自己写一个UI文件,如果不使用QT插件,在VS中导入自己的UI文件编译起来不是那么容

    2024年02月13日
    浏览(39)
  • Qt下开发基于QGIS的应用程序

    由于有在背景地图上进行动态轨迹(曲线)显示的需要,故采用Qt+QGIS的方案! Qt5.12.12,VS2015编译器,QGIS3.10.1。 在配置之前,电脑是新装机的系统,没有任何相关软件版本。 在Qt官网(https://download.qt.io/)或国内镜像网站(https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt)或网络搜索

    2024年02月14日
    浏览(59)
  • QGIS--开发OpenSCENARIO动态场景(三)--制作动态场景

    目录 一、添加scenario,carla的环境变量 二、打开QGIS3,连接carla 三、添加车辆 1. 在已选地图上添加环境 2. 添加ego小车 3.添加adversary小车  四、添加场景操作 1.设置adversary加速行驶  2.设置adversary车辆匀速行驶: 3.设置adversary车辆减速运动: 界面详细: 五、添加ego车辆 运行时

    2024年02月02日
    浏览(39)
  • QGIS开发五:VS使用QT插件创建UI界面

    前面我们说了在创建项目时创建的是一个空项目,即不使用 Qt 提供的综合开发套件 Qt Creator,也不使用 Qt Visual Studio Tools 这类工具。 但是后面发现,如果我想要有更加满意的界面布局,还是要自己写一个UI文件,如果不使用QT插件,在VS中导入自己的UI文件编译起来不是那么容

    2024年02月13日
    浏览(58)
  • arcpy基于excel中表格字段批量给矢量要素添加对应字段

    将Excel表格中的字段批量添加到矢量要素并将对应内容填写到要素属性表是可以使用Arcpy实现的。以下是一个简单代码,假设有一个包含字段和数据的Excel表格以及要进行批量添加的矢量要素图层: import arcpy import xlrd # 设置输入数据路径 excel_file = r\\\"C:dataattributes.xlsx\\\"  # 包含字

    2024年02月10日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包