本文使用 Zhihu On VSCode 创作并发布
基于 QGIS 二次开发,最首要的功能就是显示图层。这是个看似非常简单的功能,但是在 QGIS 中写了非常复杂的代码,以支持各种数据源。
但是我们在二次开发中,一般不会支持那么多的数据源。这篇博客首先以 ESRI Shapefile 数据源为例,展示加载图层的过程。
博客以创建好的工程开始,创建工程的过程网上资料很多,这里就不再赘述了。
添加地图框
要想显示图层,首先要有一个显示图层的地方。在 QGIS SDK 中,使用类 QgsMapCanvas
显示地图。这个类需要 QT 中的 svg 组件,即
# QgsSdkApp.pro
QT += core gui xml svg
如果我们想在 QMainWindow
派生类(我的工程中创建的类名是 QgsSdkApp
,以后直接用这个名称)添加一个 QgsMapCanvas
类型的组件,
有以下三种方法:
- 添加插件的方式:在 QT Designer 中添加插件,在 QT Designer 中绘制(我没有实现成功,理论上可以)
- 提升类型的方式:在 QT Designer 中使用“提升”功能,将 QWidget 组件提升为
QgsMapCanvas
类型 - 手动创建的方式:在 QgsSdkApp.cpp 中手动创建
QgsMapCanvas
类型的对象,添加到窗口中
下面一一展示。
提升类型的方式
选择一个组件,右键单击之后,单击“提升为”按钮,可以弹出提升窗口部件的对话框。
![1bcedfcf78bd530d4d53789b2789e294.png](https://i-blog.csdnimg.cn/blog_migrate/2468c673e61731e7a2d21da28eb1dd5b.png)
![d5eac7a11d93f1f9f796d50076ea50d7.png](https://i-blog.csdnimg.cn/blog_migrate/f139a19f436a043b69e9bca7eeeb040e.png)
上面这个对话框,现在 1 所示的位置输入要提升的类型的名称,然后点击 2 位置上的按钮,在上面的列表上选中刚刚添加的提升类型,
点击 3 位置上的按钮,即可将该组件提升为指定的类型(即 QgsMapCanvas
类型)。
然后,在 cpp 文件中已经可以访问到这个类型的组件了。
手动创建的方式
手动创建的方式就是在窗口类的构造函数中,构造一个 QgsMapCanvas
类型的对象,然后添加到窗口上。
这种情况下和其他在 QT 中手动创建对象没有差别,和其他一样处理即可。
添加图层
添加了地图框(在类内使用一个指针 mMapCanvas
,指向这个地图框)之后,下面就可以来添加图层了。
首先以 ESRI Shapefile 为例,介绍一下添加 QgsVectorLayer
的基本方法。
添加矢量图层的方法
在 QgsSdkApp.ui 中可以添加一个 action ,并拖到工具栏上创建工具按钮。点击这个按钮后开始添加图层。
这个过程网上有很多教程,这里就不再赘述了。
如果要添加 ESRI Shapefile 图层,我们需要先选择这个文件。我们可以直接弹出一个文件选择对话框。
我们以选择的文件路径作为数据源的路径,文件名(不含扩展名)为图层名称。
void QgsSdkApp::on_actionShp_Layer_triggered()
{
QString filePath = QFileDialog::getOpenFileName(this, tr("Open ESRI Shapefile"), tr(""), tr("ESRI Shapefile (*.shp)"));
QFileInfo fileInfo(filePath);
if (fileInfo.exists())
{
QString fileName = fileInfo.baseName();
addVectorLayer(filePath, fileName, "ogr");
}
}
创建 addVectorLayer()
函数,用于添加图层。我们可以以一种简单的方式添加图层,即直接创建 QgsVectorLayer
对象,并保存到一个列表(mMapLayerList
)里。
然后让地图框加载这个列表,即可显示地图。
QgsVectorLayer *QgsSdkApp::addVectorLayer(const QString &vectorLayerPath, const QString &name, const QString &providerKey, bool guiWarning)
{
QgsVectorLayer* vectorLayer = new QgsVectorLayer(uri, layerName, providerKey);
mMapLayerList.append(vectorLayer);
mMapCanvas->setLayers(mMapLayerList);
if (mMapLayerList.size() == 1)
{
QgsMapLayer* firstLayer = mMapLayerList.first();
QgsRectangle extent = mMapCanvas->mapSettings().layerExtentToOutputExtent(firstLayer, firstLayer->extent());
mMapCanvas->setExtent(extent);
}
mMapCanvas->refresh();
}
这个函数中除了添加了地图,同时也限制了地图框地显示范围,即设置为第一个图层地显示范围。最后对地图进行了刷新。但是这种方式会遇到很多问题:
- 如果将来要设计 Layout ,那么这个地图框的内容无法同步到 Layout 中的地图框中。
- 如果图层的投影到地图框的投影有多种转换方式,那么无法选择指定投影方式(尚未实现成功)
- 如果图层有子图层,无法选择子图层(ESRI Shapefile 中不会遇到)
- 如果图层需要访问验证,无法获取图层(ESRI Shapefile 中不会遇到)
在 QGIS 中,使用以下代码以较为完善地设置添加地图层,同时支持了很多其他功能。函数中创建的图层直接添加到 QgsProject
中,以支持 Layout 等功能。
QgsVectorLayer *QgsSdkApp::addVectorLayer(const QString &vectorLayerPath, const QString &name, const QString &providerKey, bool guiWarning)
{
QString baseName = QgsMapLayer::formatLayerName( name );
/* Eliminate the need to instantiate the layer based on provider type.
The caller is responsible for cobbling together the needed information to
open the layer
*/
QgsDebugMsg( "Creating new vector layer using " + vectorLayerPath
+ " with baseName of " + baseName
+ " and providerKey of " + providerKey );
// if th