【QGIS学习】03.打开矢量和栅格文件

学习目标

了解QGIS C++加载矢量、栅格文件基本方式,并通过菜单调用这些方法。

参考内容

链接1PyQGIS二次开发教程(一):准备工作
链接2PyQGIS二次开发教程(二):加载影像与矢量
链接3PyQGIS二次开发教程(三):学习制作菜单栏功能
作者yoyi
码云镜像4qgisapp.hqgisapp.cpp
注意:没找到比较系统的C++二次开发内容,参考这个Python版的知乎专栏,写得比较详细清晰,非常有帮助,非常感谢该作者分享,下方中部分图片也直接引用专题中的图片,图片中水印均未删除,如涉及侵权,可联系我删除。
而且Python接口和C++基本一致,可能部分细节存在差异,需要注意。所以在学习的时候结合QGIS官方代码,能更准确一些。官方代码参考的是码云(Gitee),至于为什么不看Github,速度实在太慢了,还经常刷不出来,当然也可以下载下来看。当然有精力的话还是可以把qgisapp完整代码阅读一遍的,可以更详细地了解QGIS功能模块。

主要内容

1.初始UI设计

初始UI设计
使用QtDesigner设计图中界面,具体为

  1. 在MainWindow中间添加frame,并右击窗口将布局调整为垂直布局;
  2. 添加Dock Widget,吸附到窗口左侧。
  3. 然后添加一些初始化代码,具体参考链接1

当然与Python不一样,需要为窗口类(App类)创建h文件和cpp文件,按照官方代码,该类直接继承自QMainWindow和对应的Ui类,(Qt默认创建的是将Ui类作为窗口类的一个成员变量的,我猜继承的好处是可以直接访问Ui的成员变量)

class QtGis : public QMainWindow, private Ui::QtGisClass
QtGis::QtGis(QWidget *parent) : QMainWindow(parent)
{
    setupUi(this);
	// 1. set title
	setWindowTitle("QGIS Interface");
	// 2. initial layer tree
	QVBoxLayout* vl = new QVBoxLayout(dockWidgetContents);
	mLayerTreeView = new QgsLayerTreeView(this);
	vl->addWidget(mLayerTreeView);
	// 3. initial map canvas
	mMapCanvas = new QgsMapCanvas(this);
	QHBoxLayout* hl = new QHBoxLayout(frame);
	hl->setContentsMargins(0, 0, 0, 0);
	hl->addWidget(mMapCanvas);
	// 4. set layer tree view style
	mModel = new QgsLayerTreeModel(QgsProject::instance()->layerTreeRoot(), this);
	mModel->setFlag(QgsLayerTreeModel::AllowNodeRename);
	mModel->setFlag(QgsLayerTreeModel::AllowNodeReorder);
	mModel->setFlag(QgsLayerTreeModel::AllowNodeChangeVisibility);
	mModel->setFlag(QgsLayerTreeModel::ShowLegendAsTree);
	mModel->setAutoCollapseLegendNodes(10);
	mLayerTreeView->setModel(mModel);
	// 5. create bridge between layer tree and map canvas
	mLayerTreeBridge = new QgsLayerTreeMapCanvasBridge(QgsProject::instance()->layerTreeRoot(), mMapCanvas, this);
}

基本上就是专栏中的python代码直接转的C++代码,忽略不一样的变量名。

2.创建图层管理类QgsLayerHandling

类名参考的是QGIS源代码,链接2中用的是qgisLayerUtils,主要用于将不同数据源读取为图层Layer,并将其添加到图层树中,算是一个工具类,全是静态函数。简单的情况下也可以不单独创建类,直接将函数写到App类中,因为即使使用了工具类,在App类中也写的封装函数。
工具类目前只有3个静态成员函数和1个静态变量。

QList<QgsMapLayer*> mLayers;
void QgsLayerHandling::addMapLayer(QgsMapLayer* layer, QgsMapCanvas* mapCanvas, bool firstAddLayer)
{
	if (layer->isValid())
	{
		if (firstAddLayer)
		{
			mapCanvas->setDestinationCrs(layer->crs());
			mapCanvas->setExtent(layer->extent());
		}
		while (!QgsProject::instance()->mapLayersByName(layer->name()).isEmpty())
		{
			layer->setName(layer->name() + "_1");
		}
		QgsProject::instance()->addMapLayer(layer);
		mLayers.append(layer);
		mapCanvas->setLayers(mLayers);
		mapCanvas->refresh();
	}
}

QgsRasterLayer* QgsLayerHandling::readRasterFile(QString path, QString name)
{
	QgsRasterLayer* layer = new QgsRasterLayer(path, name);
	return layer;
}

QgsVectorLayer* QgsLayerHandling::readVectorFile(QString path, QString name)
{
	QgsVectorLayer* layer = new QgsVectorLayer(path, name, "ogr");
	return layer;
}

眼泪:最开始的时候,把readVectorFile函数是"ogr"写成了"org",编译是通过了,当时没发现,但加载矢量文件时就是读不出来,查找了好久问题,换了好几个shp文件,最后才发现是拼写错了。其实OGR就是GDAL库中处理矢量的部分,具体可以学习下GDAL。

3.App类中调用

在App类中调用上面工具类,将添加图层函数封装调用,如下:

void QtGis::addRasterLayer(QString fileName)
{
	QStringList temp = fileName.split('/');
	QString basename = temp.at(temp.size() - 1);
	QgsRasterLayer* layer = QgsLayerHandling::readRasterFile(fileName, basename);
	if (mFirstAdd)
	{
		QgsLayerHandling::addMapLayer(layer, mMapCanvas, true);
		mFirstAdd = false;
	}
	else
	{
		QgsLayerHandling::addMapLayer(layer, mMapCanvas);
	}
}

代码只是示例,是根据链接2直接转译成C++的,只是加载栅格图层的,加载矢量图层类似。
如此这般一调用,就可以在窗口中加载GeoTiff和Shp文件了,但和链接2中不一致的是,我添加GeoTiff后,图层树中并没有图例图标,也不能展开单独显示RGB波段,不知道是哪的问题。Shp文件可以显示图例图标。

3.添加菜单并与函数进行链接

在QtDesigner中添加两个功能的菜单项,并将菜单的QAction也添加到工具栏中。
这样(这张图片也是从专栏中拷贝的,但由于图片太小,并没有水印)
菜单
然后再这样
工具栏
然后再这样
QAction添加到工具栏

然后链接

void QtGis::connectFunc()
{
	connect(actionOpenRaster, &QAction::triggered, this, &QtGis::actionOpenRasterTriggered);
	connect(actionOpenShp, &QAction::triggered, this, &QtGis::actionOpenShpTriggered);
}

void QtGis::actionOpenRasterTriggered()
{
	QString fileName = QFileDialog::getOpenFileName(this, tr("Open tif file"), "", "GeoTiff(*.tif;*.tiff)");
	addRasterLayer(fileName);
}

void QtGis::actionOpenShpTriggered()
{
	QString fileName = QFileDialog::getOpenFileName(this, tr("Open shape file"), "", "*.shp");
	addVectorLayer(fileName);
}

注意:在connect的时候,第四个参数一定要加类名,像&QtGis::actionOpenRasterTriggered这样,而不是&actionOpenRasterTriggered这样,否则链接失败,虽然在同一个类里面。
最开始我就偷懒没写类名,结果发现不行,就尝试了另一种信号/槽的connect方法,像这样

connect(actionOpenRaster, SIGNAL(triggered(bool)), this, SLOG(actionOpenRasterTriggered()));

,但也没成功,就只好再研究第一种方法了。

下步打算

  1. 尝试打开更多各类数据格式
  2. 尝试打开WMTS数据源
  3. 丰富界面功能,至少要显示鼠标点坐标
  4. 尝试测量和编辑
  5. 整饰和出图功能

问题总结

在尝试过程中也遇到了许多愚蠢的问题,在前面也基本提到了,这里再总结一下。

  1. 别写错字,就像上面的ogr,这么简单的低级错误可是笑死个人,关键还不容易发现,编译也不报错,运行也没提示,图层树里都有新图层了,但画布里就是没有东西。记得高中第一次接触VB编程的时候,第一个例子就是写错了个单词,结果让老师帮找好久才发现错误。
  2. connect第四个参数要写类名,也就是成员函数完整的名字。
  3. 工具类中,静态成员变量初始化问题,最开始mLayers定义成了静态成员变量,然后在cpp文件中又初始化了一遍,结果链接的时候直接报错,找不到的外部对象,当时还以为缺少哪个lib库了,结果最后是静态成员变量问题。
  4. 在代码中设置中文标题的时候显示乱码,而在QtDesigner中设置就没有问题。经过一番百度和尝试,终于发现,原来是我的cpp文件默认编码是GB2312的,而QString默认是UTF8的,所有需要转换一下,使用函数QString::fromLocal8Bit("中文字符串")方式即可正常显示中文。

当然还有一个问题待解决,虽然不一定去解决:

  1. 程序Debug版本运行无显示,还不知道是什么原因,可能是缺少库吧。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值