当前位置: 首页 > news >正文

【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);    }    
    }  

    相关文章:

  1. 嵌入式开发中使用 MySQL 数据库常见问题及解决办法
  2. CSS3 变形
  3. 行为型模式:责任链模式
  4. RPA 自动化实现自动发布
  5. tomcat项目重构踩坑易错点
  6. 【2025最新】VSCode Cline插件配置教程:免费使用Claude 3.7提升编程效率
  7. Electron 主进程中使用Worker来创建不同间隔的定时器实现过程
  8. Electron 应用的升级机制详解
  9. Electron详解:原理与不足
  10. Windows 环境下 Docker Desktop 安装 + 汉化
  11. MinerU安装(pdf转markdown、json)
  12. win11平台下的docker-desktop中的volume位置问题
  13. UR5e机器人Matlab仿真
  14. 前后端设置跨域并从后端允许发送cookie
  15. 【ROS2】ROS节点启动崩溃:rclcpp::exceptions::RCLInvalidArgument
  16. 【Python】杂乱-[代码]python 批量修改指定文件/目录的名称
  17. linux系统中如何校准时间
  18. 卡洛诗,将高端西餐的冗余价值转化为普惠体验
  19. 【JS】vue3中组件命名问题
  20. 不建议在useEffect中进行数据获取的理由
  21. “养胃骗局”大公开,真正有用的方法究竟是?
  22. 袁思达已任中国科学院办公厅主任
  23. 日月谭天丨这轮中美关税会谈让台湾社会看清了什么?
  24. 持续8年仍难终了的纠纷:败诉方因拒执罪被立案,胜诉方银行账户遭冻结
  25. 继71路之后,上海中心城区将迎来第二条中运量公交
  26. 我国7名优秀护理工作者荣获第50届南丁格尔奖