【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);    
    }    
}  
相关推荐
-SGlow-5 小时前
Linux相关概念和易错知识点(40)(HTML资源交互、网页管理、搜索引擎)
linux·运维·服务器·网络·html·交互
进取星辰14 小时前
28、动画魔法圣典:Framer Motion 时空奥义全解——React 19 交互动效
前端·react.js·交互
MARS_AI_14 小时前
人工智能外呼系统:重构智能交互的全维度进化
人工智能·自然语言处理·重构·交互·语音识别·信息与通信
binggoling16 小时前
实用工具:微软软件PowerToys(完全免费),实现多台电脑共享鼠标和键盘(支持window系统)
microsoft·计算机外设·电脑
lkbhua莱克瓦2417 小时前
用C语言实现了——一个基于顺序表的插入排序演示系统
c语言·开发语言·数据结构·程序人生·github·排序算法·交互
玉笥寻珍1 天前
Web安全渗测试基础知识之SSL交互异常利用篇
网络协议·安全·web安全·网络安全·交互·ssl
rrokoko1 天前
微软向现实低头:悄悄延长Windows 10的Microsoft 365支持
microsoft
kooboo china.1 天前
在UI 原型设计中,交互规则有哪些核心要素?
ui·编辑器·交互
编程乐趣1 天前
推荐一个Winform开源的UI工具包
microsoft·ui·开源