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

保姆级教程:在Qt/C++项目中用QgsAnnotationItem给地图添加自定义标注(文字+SVG/PNG图片)

Qt/C++与QGIS二次开发实战:自定义地图标注全流程解析

在桌面GIS应用开发中,地图标注功能如同画龙点睛之笔,它能将枯燥的地理数据转化为直观的业务信息。作为QGIS二次开发的核心组件之一,QgsAnnotationItem系列类为开发者提供了高度灵活的地图标注能力。本文将深入探讨如何利用这些工具类,在Qt/C++环境中实现文字、SVG矢量图标和PNG位图等多种标注形式的集成方案。

1. 环境准备与基础概念

1.1 开发环境配置

开始前需要确保开发环境包含以下组件:

  • Qt 5.15+:建议使用LTS版本保证稳定性
  • QGIS开发库:3.22以上版本,包含以下关键模块:
    • qgis_core
    • qgis_gui
    • qgis_analysis
  • CMake构建系统:推荐3.16+版本

在CMakeLists.txt中需配置的关键依赖项:

find_package(Qt5 REQUIRED COMPONENTS Core Widgets) find_package(QGIS REQUIRED) include_directories(${QGIS_INCLUDE_DIRS}) link_directories(${QGIS_LIBRARY_DIRS})

1.2 标注系统架构解析

QGIS标注系统采用分层设计,主要包含三个核心类:

类名职责继承关系
QgsAnnotationLayer标注容器层QgsMapLayer
QgsAnnotationItem标注项基类QObject
QgsMapCanvasAnnotationItem画布呈现适配器QgsMapCanvasItem

内存管理要点

  • 标注项采用显式父子关系管理
  • 符号对象必须使用clone()方法传递所有权
  • 图层对象应由画布接管生命周期

2. 文字标注实现详解

2.1 基础文本标注

创建文字标注需要实例化QgsAnnotationPointTextItem类,其核心参数包括:

// 创建文本项 QgsAnnotationPointTextItem* textItem = new QgsAnnotationPointTextItem( "监测站A", // 文本内容 QgsPointXY(116.404, 39.915) // 坐标位置 ); // 设置文本样式 QgsTextFormat format; format.setFont(QFont("Microsoft YaHei", 12, QFont::Bold)); format.setColor(Qt::red); format.setOpacity(0.8); textItem->setFormat(format); // 设置对齐方式 textItem->setAlignment(Qt::AlignRight | Qt::AlignVCenter); // 添加到标注层 annotationLayer->addItem(textItem);

2.2 高级文本特性

多行文本处理技巧:

QString multiLineText = "设备编号:D-2023-0425\n" "状态:正常运行\n" "最后检测:2023-04-25"; QgsAnnotationPointTextItem* multiTextItem = new QgsAnnotationPointTextItem( multiLineText, QgsPointXY(121.473, 31.230) ); // 设置行间距 QgsTextFormat multiFormat; multiFormat.setLineHeight(1.5); multiTextItem->setFormat(multiFormat);

文本旋转实现方法:

// 设置30度倾斜 textItem->setAngle(30); // 动态旋转示例(随时间变化) connect(&timer, &QTimer::timeout, [textItem]() { static int angle = 0; textItem->setAngle(angle); angle = (angle + 5) % 360; mapCanvas->refresh(); }); timer.start(100);

3. 图形标注实战方案

3.1 SVG矢量图标集成

SVG标注的优势在于无损缩放,特别适合需要动态调整大小的场景:

// 创建SVG符号层 QgsSvgMarkerSymbolLayer* svgLayer = new QgsSvgMarkerSymbolLayer( ":/icons/weather.svg", // 资源路径 15.0 // 初始大小 ); // 构建完整符号 QgsSymbolLayerList layers; layers.append(svgLayer->clone()); QgsMarkerSymbol* symbol = new QgsMarkerSymbol(layers); // 创建标注项 QgsAnnotationMarkerItem* markerItem = new QgsAnnotationMarkerItem( QgsPoint(116.404, 39.915) ); markerItem->setSymbol(symbol->clone()); // 动态调整大小示例 connect(&resizeTimer, &QTimer::timeout, [=]() { static double size = 10.0; svgLayer->setSize(size); size = size < 30.0 ? size + 1.0 : 10.0; mapCanvas->refresh(); }); resizeTimer.start(200);

3.2 位图标注处理技巧

对于PNG/JPG等位图格式,应采用QgsRasterMarkerSymbolLayer

// 创建位图符号层 QgsRasterMarkerSymbolLayer* pngLayer = new QgsRasterMarkerSymbolLayer( ":/icons/traffic_camera.png", 20.0 ); // 设置透明色(适用于带背景的图片) pngLayer->setColor(QColor(255,255,255,180)); // 构建标注项 QgsSymbolLayerList pngLayers; pngLayers.append(pngLayer->clone()); QgsMarkerSymbol* pngSymbol = new QgsMarkerSymbol(pngLayers); QgsAnnotationMarkerItem* pngItem = new QgsAnnotationMarkerItem( QgsPoint(121.473, 31.230) ); pngItem->setSymbol(pngSymbol->clone());

性能提示:频繁更新的动态标注应优先使用SVG格式,其渲染效率通常高于位图

4. 高级应用与性能优化

4.1 标注交互实现

实现标注点击响应的典型方案:

// 自定义地图工具类 class AnnotationTool : public QgsMapTool { public: explicit AnnotationTool(QgsMapCanvas* canvas) : QgsMapTool(canvas), mCanvas(canvas) {} void canvasPressEvent(QgsMapMouseEvent* e) override { QgsPointXY mapPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( e->pos().x(), e->pos().y()); // 查找半径5像素范围内的标注项 const double searchRadius = mCanvas->mapUnitsPerPixel() * 5; if (QgsAnnotationItem* item = findItemAtPosition(mapPoint, searchRadius)) { handleItemClick(item); } } private: QgsMapCanvas* mCanvas; }; // 在视图初始化时启用工具 mAnnotationTool = new AnnotationTool(mapCanvas); mapCanvas->setMapTool(mAnnotationTool);

4.2 批量标注性能优化

当需要处理大量标注时(如500+项),可采用以下策略:

1. 空间索引加速

// 创建空间索引 QgsSpatialIndex index; // 添加标注项时建立索引 for (auto& item : items) { QgsRectangle bbox = item->boundingBox(); index.insertItem(item->id(), bbox); } // 查询优化 QList<QString> candidateIds = index.intersects(queryRect);

2. 细节层次控制(LOD)

// 根据缩放级别显示不同细节 connect(mapCanvas, &QgsMapCanvas::scaleChanged, [=](double scale) { const double threshold1 = 5000; // 1:5000 const double threshold2 = 10000; // 1:10000 for (auto& item : items) { if (auto* textItem = dynamic_cast<QgsAnnotationPointTextItem*>(item)) { textItem->setVisible(scale > threshold1); } else if (scale > threshold2) { item->setVisible(false); } } });

4.3 标注样式模板化

建立可复用的样式模板系统:

namespace AnnotationTemplates { QgsTextFormat warningTextFormat() { QgsTextFormat format; format.setFont(QFont("Arial", 10, QFont::Bold)); format.setColor(Qt::yellow); format.setBackground(QgsTextBackgroundSettings()); format.background().setFillColor(Qt::darkRed); format.background().setSize(QSizeF(1.2, 1.2)); return format; } QgsMarkerSymbol* criticalMarkerSymbol() { QgsSvgMarkerSymbolLayer* layer = new QgsSvgMarkerSymbolLayer( ":/icons/warning.svg", 12); QgsSymbolLayerList layers; layers.append(layer); return new QgsMarkerSymbol(layers); } } // 应用模板 textItem->setFormat(AnnotationTemplates::warningTextFormat()); markerItem->setSymbol(AnnotationTemplates::criticalMarkerSymbol()->clone());

5. 实战案例:气象监测系统标注

以下是一个完整的气象站标注实现示例:

class WeatherStationAnnotation { public: static QgsAnnotationItem* createStationMarker( const QgsPointXY& position, const QString& stationId, float temperature, float humidity, bool isWarning) { // 创建组合标注项 QgsAnnotationItemGroup* group = new QgsAnnotationItemGroup(); // 添加背景图标 QgsAnnotationMarkerItem* marker = createBaseMarker(position, isWarning); group->addItem(marker); // 添加温度文本 QgsAnnotationPointTextItem* tempText = createTemperatureText( position, temperature); group->addItem(tempText); // 添加湿度文本 QgsAnnotationPointTextItem* humiText = createHumidityText( position, humidity); group->addItem(humiText); return group; } private: static QgsAnnotationMarkerItem* createBaseMarker( const QgsPointXY& pos, bool isWarning) { QString iconPath = isWarning ? ":/icons/warning_station.svg" : ":/icons/normal_station.svg"; QgsSvgMarkerSymbolLayer* layer = new QgsSvgMarkerSymbolLayer(iconPath, 20); QgsSymbolLayerList layers; layers.append(layer); QgsMarkerSymbol* symbol = new QgsMarkerSymbol(layers); QgsAnnotationMarkerItem* item = new QgsAnnotationMarkerItem(pos); item->setSymbol(symbol->clone()); return item; } static QgsAnnotationPointTextItem* createTemperatureText( const QgsPointXY& pos, float value) { QString text = QString("%1°C").arg(value, 0, 'f', 1); QgsAnnotationPointTextItem* item = new QgsAnnotationPointTextItem( text, QgsPointXY(pos.x(), pos.y() + 0.01)); QgsTextFormat format; format.setFont(QFont("Arial", 8)); format.setColor(value > 30.0 ? Qt::red : Qt::blue); item->setFormat(format); return item; } };

在实际GIS项目开发中,标注系统的稳定性往往取决于对Qt绘图系统和QGIS渲染引擎的深入理解。一个常见的误区是直接在绘图事件中创建标注项,这会导致性能急剧下降。正确的做法应该是预创建所有标注项,在数据变更时仅更新必要属性。

http://www.jsqmd.com/news/821732/

相关文章:

  • IDA静态分析-使用记录
  • #Python 类(Class)知识点总结
  • 别再搞混了!Web地图开发必懂的EPSG:4326与3857转换(附JavaScript代码)
  • 如何快速使用Bebas Neue:免费开源字体的完整指南
  • 《从王自如到YoooClaw:AI时代的哑铃型商业结构》
  • 别再用暴力循环了!用C++筛法高效分解质因数,附完整代码与时间复杂度分析
  • 手把手教你用Python复现TITAN风暴跟踪算法(附代码与数据)
  • 从零开始:ESP32 Arduino开发环境搭建完整指南
  • 声临其境 安全直达 ——NR2048 赋能矿场高可靠高清语音通信
  • STM32CubeMX配置外部中断后,生成的HAL库代码里AFIO和EXTI都做了啥?
  • Cyber Engine Tweaks终极指南:5步快速解锁赛博朋克2077无限潜能
  • RAG:AI Agent的“开卷考试”秘籍,让你的问题回答不再“瞎编”!
  • 从二叉树到UML:Graphviz的DOT语言保姆级语法手册(附避坑指南)
  • 2026年幻视AI数字工牌与全域零售AI解决方案官方指南
  • 如何轻松将Axure RP界面切换为中文:3个实用技巧让设计更高效
  • 2026最新测评:熬夜亲测5款硬核工具,教你高效降低AI率! - 降AI实验室
  • 基于Spartan-3 FPGA的PCIe单通道DMA传输性能实测与优化
  • 使用 Taotoken CLI 工具一键配置多开发环境接入信息
  • 092、Python在芯片验证中的应用:从脚本小子到验证架构师
  • 基于Telegram官方API的消息自动化获取与导出工具实践
  • 别再写 `new Stack<>()` 了!聊聊Java里更现代的栈实现:ArrayDeque与LinkedList性能实测
  • 【效率革命】3DMAX砖石墙地面插件:从零到一,快速构建写实场景的终极指南
  • 从浏览器输入URL到页面加载完成,Wireshark抓包全记录:一张图看懂HTTP/1.1的完整对话
  • 别让时钟拖后腿!手把手教你搞定PCIe REFCLK的板级设计与常见干扰排查
  • 统信UOS离线部署实战:从在线缓存中提取软件包,构建内网专属软件源
  • 李晓伟律师团队全风险代理 让保险拒赔维权零经济负担 - 铅笔写好字
  • GAIA-DataSet终极指南:如何用6500+指标构建智能运维的黄金标准?
  • 全场景高清语音处理标杆:NR2048 高性能语音处理器技术解析与应用展望
  • Dropout的工程实践指南:从动机剖析到PyTorch/Numpy高效实现与变种对比
  • Cursor Pro功能完全解锁指南:三步实现免费无限使用终极方案