【QGIS二次开发】地图显示与交互-03
系列目录:
【QGIS二次开发】地图显示与交互-01_qgis二次开发加载地图案例-CSDN博客
【QGIS二次开发】地图显示与交互-02_setlayerlabeling-CSDN博客
3. 地图符号与色表
3.1 矢量图层符号设置
任务要求:双击图层树节点,实现图层中图元的符号的设置并更新地图显示。图层可以是点、线、面图层中的任何一种。
完成的逻辑是在QgsLayerTreeViewMenuProvider的实例类中添加一个openSymbologyDialog方法,实现矢量图层符号化功能。
代码逻辑如下:
首先,检查是否有当前选中的图层,如果没有则直接返回。接下来,尝试将该图层转换为矢量图层。如果转换成功,获取当前图层的渲染器和默认样式。然后,使用QgsSingleSymbolRendererWidget类创建一个新的符号化设置窗口。创建一个对话框,并将其布局设置为垂直布局。将符号化设置窗口添加到布局中。创建一个按钮框,其中包含“应用”、“确定”、“取消”和“保存样式”按钮,并将其添加到布局中。连接“应用”按钮的clicked信号到一个槽函数。在槽函数中,获取符号化设置窗口的渲染器,并将其设置到矢量图层中。然后,触发图层的重绘。最后,显示对话框并等待用户的操作。如果用户点击了“确定”按钮,执行与“应用”按钮相同的操作。这样,整个操作将图层转换为矢量图层,并提供用户在符号化设置窗口中进行样式调整的选项。
实现的效果是,在图层管理器中图层的右键菜单中添加图层符号化菜单选项,点击图层符号化菜单选项打开对话框,由于直接调用了QGIS的QgsSingleSymbolRendererWidget类,因此界面逻辑和QGIS中修改矢量图层符号化相似。
核心代码如下:
void MenuProvider::openSymbologyDialog()
{ // 获取当前选中的图层 QModelIndex index = mView->currentIndex(); // 获取图层树模型 QgsLayerTreeModel* layerTreeModel = mView->layerTreeModel(); // 将当前选中的索引转换为一个图层树节点 QgsLayerTreeNode* node = layerTreeModel->index2node(index); // 将节点转换为一个地图图层 QgsMapLayer* layer = QgsLayerTree::toLayer(node)->layer(); // 如果获取的图层为空,则返回 if (!layer) return; // 尝试将图层转换为矢量图层 QgsVectorLayer* vLayer = qobject_cast<QgsVectorLayer*>(layer); // 尝试将图层转换为栅格图层 QgsRasterLayer* rLayer = qobject_cast<QgsRasterLayer*>(layer); if (vLayer) { // 获取当前图层的渲染器 QgsFeatureRenderer* renderer = vLayer->renderer(); // 获取默认的样式 QgsStyle* style = QgsStyle::defaultStyle(); // 创建一个新的符号化设置窗口 QgsSingleSymbolRendererWidget* rendererWidget = new QgsSingleSymbolRendererWidget(vLayer, style, renderer->clone()); // 创建一个对话框 QDialog* dialog = new QDialog(); // 创建一个垂直布局,并将其设置到对话框中 QVBoxLayout* layout = new QVBoxLayout(dialog); // 将符号化设置窗口添加到布局中 layout->addWidget(rendererWidget); dialog->resize(600, 800); // 创建一个包含“应用”、“确定”、“取消”和“保存样式”按钮的按钮框,并将其添加到布局中 QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog); QPushButton* saveStyleButton = new QPushButton("保存样式"); buttonBox->addButton(saveStyleButton, QDialogButtonBox::ActionRole); layout->addWidget(buttonBox); layout->addWidget(buttonBox); // 获取应用按钮并连接其clicked信号到一个自定义的槽 QPushButton* applyButton = buttonBox->button(QDialogButtonBox::Apply); QObject::connect(applyButton, &QPushButton::clicked, [=]() { // 这个槽执行和“确定”按钮一样的操作,但是不关闭对话框 vLayer->setRenderer(rendererWidget->renderer()); vLayer->triggerRepaint(); }); // 连接按钮框的“应用”、“确定”和“取消”信号到对话框的“接受”和“拒绝”槽 QObject::connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept); QObject::connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject); // 显示对话框,并等待用户的操作 if (dialog->exec() == QDialog::Accepted) {
// 如果用户点击了“确定”按钮,就获取符号化设置窗口的渲染器,并将其设置到矢量图层中 vLayer->setRenderer(rendererWidget->renderer()); // 触发图层的重绘,以更新图层的显示 vLayer->triggerRepaint(); } } else if (rLayer) { // 获取当前图层的渲染器 QgsRasterRenderer* renderer = rLayer->renderer(); // 创建一个新的符号化设置窗口 QgsSingleBandPseudoColorRendererWidget* rendererWidget = new QgsSingleBandPseudoColorRendererWidget(rLayer, rLayer->extent()); // 创建一个对话框 QDialog* dialog = new QDialog(); // 创建一个垂直布局,并将其设置到对话框中 QVBoxLayout* layout = new QVBoxLayout(dialog); // 将符号化设置窗口添加到布局中 layout->addWidget(rendererWidget); // 创建一个包含“应用”、“确定”和“取消”按钮的按钮框,并将其添加到布局中 QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog); layout->addWidget(buttonBox); // 获取应用按钮并连接其clicked信号到一个自定义的槽 QPushButton* applyButton = buttonBox->button(QDialogButtonBox::Apply); QObject::connect(applyButton, &QPushButton::clicked, [=]() { // 这个槽执行和“确定”按钮一样的操作,但是不关闭对话框 rLayer->setRenderer(rendererWidget->renderer()); rLayer->triggerRepaint(); }); // 连接按钮框的“应用”、“确定”和“取消”信号到对话框的“接受”和“拒绝”槽 QObject::connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept); QObject::connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject); // 显示对话框,并等待用户的操作 if (dialog->exec() == QDialog::Accepted) { // 如果用户点击了“确定”按钮,就获取符号化设置窗口的渲染器,并将其设置到栅格图层中 rLayer->setRenderer(rendererWidget->renderer()); // 触发图层的重绘,以更新图层的显示 rLayer->triggerRepaint(); } }
}
实现的效果如下:
图 20 示例数据
图 21 修改符号设置
3.2 分层赋色
任务要求: 对一个栅格图层,选择一个色表,根据图层的像元值的范围,对图层进行分层设色,并在视图中进行显示。
完成的逻辑是在QgsLayerTreeViewMenuProvider的实例类中添加一个openSymbologyDialog方法,实现栅格图层分层设色功能。
代码逻辑如下:
首先,进行检查以确保当前选中的图层是栅格图层,如果是栅格图层则继续执行,否则直接返回。接着,获取当前栅格图层的渲染器。随后,创建一个新的符号化设置窗口,用于显示和编辑栅格图层的符号设置。创建一个对话框,并将其布局设置为垂直布局。将符号化设置窗口添加到对话框的布局中。创建一个按钮框,其中包含“应用”、“确定”和“取消”按钮,并将其添加到对话框的布局中。连接“应用”按钮的clicked信号到一个槽函数。在槽函数中,获取符号化设置窗口的渲染器,并将其设置到栅格图层中。然后,触发图层的重绘。最后,显示对话框并等待用户的操作。如果用户点击了“确定”按钮,执行与“应用”按钮相同的操作。这一系列步骤允许用户在符号化设置窗口中调整栅格图层的样式,并在确认或应用后将更改应用到图层中。
实现的效果是,在图层管理器中图层的右键菜单中添加图层符号化菜单选项,点击图层符号化菜单选项打开对话框,由于直接调用了QGIS的QgsSingleBandPseudoColorRendererWidget类,因此界面逻辑和QGIS中实现栅格图层分层设色相似。
代码如下:
void MenuProvider::openSymbologyDialog()
{ // 获取当前选中的图层 QModelIndex index = mView->currentIndex(); // 获取图层树模型 QgsLayerTreeModel* layerTreeModel = mView->layerTreeModel(); // 将当前选中的索引转换为一个图层树节点 QgsLayerTreeNode* node = layerTreeModel->index2node(index); // 将节点转换为一个地图图层 QgsMapLayer* layer = QgsLayerTree::toLayer(node)->layer(); // 如果获取的图层为空,则返回 if (!layer) return; // 尝试将图层转换为矢量图层 QgsVectorLayer* vLayer = qobject_cast<QgsVectorLayer*>(layer); // 尝试将图层转换为栅格图层 QgsRasterLayer* rLayer = qobject_cast<QgsRasterLayer*>(layer); if (vLayer) { // 获取当前图层的渲染器 QgsFeatureRenderer* renderer = vLayer->renderer(); // 获取默认的样式 QgsStyle* style = QgsStyle::defaultStyle(); // 创建一个新的符号化设置窗口 QgsSingleSymbolRendererWidget* rendererWidget = new QgsSingleSymbolRendererWidget(vLayer, style, renderer->clone()); // 创建一个对话框 QDialog* dialog = new QDialog(); // 创建一个垂直布局,并将其设置到对话框中 QVBoxLayout* layout = new QVBoxLayout(dialog); // 将符号化设置窗口添加到布局中 layout->addWidget(rendererWidget); dialog->resize(600, 800); // 创建一个包含“应用”、“确定”、“取消”和“保存样式”按钮的按钮框,并将其添加到布局中 QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog); QPushButton* saveStyleButton = new QPushButton("保存样式"); buttonBox->addButton(saveStyleButton, QDialogButtonBox::ActionRole); layout->addWidget(buttonBox); layout->addWidget(buttonBox); // 获取应用按钮并连接其clicked信号到一个自定义的槽 QPushButton* applyButton = buttonBox->button(QDialogButtonBox::Apply); QObject::connect(applyButton, &QPushButton::clicked, [=]() { // 这个槽执行和“确定”按钮一样的操作,但是不关闭对话框 vLayer->setRenderer(rendererWidget->renderer()); vLayer->triggerRepaint(); }); // 连接按钮框的“应用”、“确定”和“取消”信号到对话框的“接受”和“拒绝”槽 QObject::connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept); QObject::connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject); // 显示对话框,并等待用户的操作 if (dialog->exec() == QDialog::Accepted) { // 如果用户点击了“确定”按钮,就获取符号化设置窗口的渲染器,并将其设置到矢量图层中 vLayer->setRenderer(rendererWidget->renderer()); // 触发图层的重绘,以更新图层的显示 vLayer->triggerRepaint(); } } else if (rLayer) { // 获取当前图层的渲染器 QgsRasterRenderer* renderer = rLayer->renderer(); // 创建一个新的符号化设置窗口 QgsSingleBandPseudoColorRendererWidget* rendererWidget = new QgsSingleBandPseudoColorRendererWidget(rLayer, rLayer->extent()); // 创建一个对话框 QDialog* dialog = new QDialog(); // 创建一个垂直布局,并将其设置到对话框中 QVBoxLayout* layout = new QVBoxLayout(dialog); // 将符号化设置窗口添加到布局中 layout->addWidget(rendererWidget); // 创建一个包含“应用”、“确定”和“取消”按钮的按钮框,并将其添加到布局中 QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog); layout->addWidget(buttonBox); // 获取应用按钮并连接其clicked信号到一个自定义的槽 QPushButton* applyButton = buttonBox->button(QDialogButtonBox::Apply); QObject::connect(applyButton, &QPushButton::clicked, [=]() { // 这个槽执行和“确定”按钮一样的操作,但是不关闭对话框 rLayer->setRenderer(rendererWidget->renderer()); rLayer->triggerRepaint(); }); // 连接按钮框的“应用”、“确定”和“取消”信号到对话框的“接受”和“拒绝”槽 QObject::connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept); QObject::connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject); // 显示对话框,并等待用户的操作 if (dialog->exec() == QDialog::Accepted) { // 如果用户点击了“确定”按钮,就获取符号化设置窗口的渲染器,并将其设置到栅格图层中 rLayer->setRenderer(rendererWidget->renderer()); // 触发图层的重绘,以更新图层的显示 rLayer->triggerRepaint(); } }
}
实现的效果:
图 22 示例数据
图 23 分层赋色效果
3.3 符号库导入输出
符号库通过调用ui文件并进行相关配置实现了符号库的相关功能,能够导入、导出符号库文件等多种功能,实现代码如下,该函数实现了通过文件对话框选择栅格数据文件(支持img、tif、tiff格式),创建对应的QgsRasterLayer对象,并将有效的图层添加到QgsProject中。如果图层无效或文件不存在,将显示相应的提示信息。最后,通过刷新地图画布,确保新添加的图层在界面上可见。
void DataViewer::on_actionAddRasterData_triggered()
{ QStringList layerPathList = QFileDialog::getOpenFileNames(this, QStringLiteral("选择栅格数据"), "", "Image (*.img *.tif *.tiff)"); QList<QgsMapLayer*> layerList; for each (QString layerPath in layerPathList) { QFileInfo fi(layerPath); if (!fi.exists()) { return; } QString layerBaseName = fi.baseName(); // 图层名称 QgsRasterLayer* rasterLayer = new QgsRasterLayer(layerPath, layerBaseName); if (!rasterLayer) { return; } if (!rasterLayer->isValid()) { QMessageBox::information(0, "", "layer is invalid"); return; } layerList << rasterLayer; } QgsProject::instance()->addMapLayers(layerList); m_mapCanvas->refresh();
}
通过符号库能够实现符号库的设计、存储与显示,以及实现颜色、点、线、面符号的库的增删查改操作
图 24 增加点
图 25 增加线数据
通过对符号右键能够出现删改符号的选项,操作如下图所示:
图 26 删除符号
在右下角的搜索框中输入需要搜索的内容,就能够实现对符号库内容的检索,操作如下图所示:
图 27 搜索符号
最后通过在MenuProvider类中添加响应槽函数实现导出图层样式为能够在GeoSever中能够使用的sld文件。操作时点击相应矢量图层或栅格图层右键弹出右键菜单,点击导出图层样式(Sld)弹出保存文件对话框,用户选择恰当的路径就能够实现Sld格式的图层样式的导出,操作流程如下图所示:
图 28 右键菜单-导出图层样式(Sld)
图 29 保存Sld文件对话框
Sld图层样式的导出实现代码实现,通过在MenuProvider类中添加成员函数outputToSld函数实现将当前地图视图中选中的图层的样式保存为Sld文件。通过获取当前图层的指针,打开保存对话框以允许用户选择保存路径和文件名,然后利用saveSldStyle方法将样式保存为用户指定的Sld文件。
void MenuProvider::outputToSld()
{ QgsMapLayer* currentLayer = mView->currentLayer(); if (currentLayer) { bool error; QString filename = QFileDialog::getSaveFileName(nullptr, "样式保存为", "", "Sld (*.sld)"); currentLayer->saveSldStyle(filename, error); }
}