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

有难度哦/Qt基于通用地图组件实现航迹规划和模拟/动态标注轨迹线/带序号和方向箭头指示

一、前言说明

1、功能概述

航迹规划功能允许用户在地图上通过单击操作逐个添加航线途经点,系统自动生成带有方向指示的连续航迹线,并支持对航线进行动态编辑。主要功能包括:

  • 支持在地图上单击添加标注点,点位按添加顺序自动递增编号;
  • 自动生成带箭头方向指示的航迹线,清晰展示航行方向;
  • 实现航线标注点的拖曳编辑,实时更新航迹与箭头方向;
  • 支持删除指定航点或清空全部航点,删除后自动重排序并重绘航线;
  • 地图上的标注点与表格数据联动:点击地图点可选中对应表格行,选中表格行时地图点自动高亮;
  • 航点支持带序号图标的可视化显示,提升可读性;
  • 航线数据支持自动加载与持久化保存,保障用户体验连续性。

2、核心难点与技术实现

2.1 通用化方向箭头绘制

尽管部分主流地图平台(如百度地图、高德地图)提供了原生的箭头线绘制接口,但其样式固定、扩展性差,难以满足多样化业务场景的需求。为此,系统采用基于标注点的通用化箭头实现方案,确保兼容所有支持标注点(Marker)的地图组件。

具体实现思路如下:

  • 在每两个相邻航点之间,计算其连线的方位角(即方向角);
  • 根据计算出的角度,动态生成一个代表箭头方向的标注点;
  • 该箭头标注点使用自定义图标(如SVG箭头图像),并通过设置旋转属性(rotation)与航向保持一致;
  • 箭头图标可灵活替换,仅需更换图片资源即可适配不同视觉风格,极大增强了系统的可维护性与一致性。

此方法避免了依赖特定地图厂商的高级绘图接口,实现了“一次开发,多平台适配”的目标。

2.2 航点拖曳编辑与实时更新机制

为提升交互体验,系统支持用户直接在地图上拖动航点以调整航线。此功能的核心挑战在于:拖动一个航点时,需同步更新与其相邻的两条航段及其对应的箭头方向

实现方案如下:

  • 为每个航点标注绑定“拖曳开始”与“拖曳结束”事件监听;
  • 拖曳过程中,实时获取当前鼠标位置,更新该航点坐标;
  • 动态重新计算受影响的前后两段航迹线的坐标数据,并刷新对应的折线(Polyline)对象;
  • 同时重新计算相邻两段航迹的方向角,更新前后两个箭头标注点的旋转角度;
  • 拖曳结束后,触发数据持久化,保存最新航线状态。

得益于前期地图组件良好的事件封装机制,各类覆盖物的事件监听可按需绑定与解绑,保证了系统性能与稳定性。

二、效果图

Snipaste_2025-10-08_16-12-50

三、代码使用

#include "frmmapdrawmarkerline.h"
#include "ui_frmmapdrawmarkerline.h"
#include "qthelper.h"
#include "maphelper.h"
#include "webview.h"
#include "mapdrawmarkerline.h"frmMapDrawMarkerLine::frmMapDrawMarkerLine(QWidget *parent) : QWidget(parent), ui(new Ui::frmMapDrawMarkerLine)
{ui->setupUi(this);this->initForm();this->initTable();this->loadTable();//mapObj->load();QTimer::singleShot(500, mapObj, SLOT(load()));//QMetaObject::invokeMethod(mapObj, "load", Qt::QueuedConnection);
}frmMapDrawMarkerLine::~frmMapDrawMarkerLine()
{delete ui;
}void frmMapDrawMarkerLine::initForm()
{//设置右侧固定宽度ui->frameRight->setFixedWidth(AppData::RightWidth);flag = "moveMarker";center = "121.56358,31.11566";//实例化浏览器控件并加入到布局webView = new WebView(this);webView->setLayout(ui->gridLayout);connect(webView, SIGNAL(loadSuccess()), this, SLOT(on_btnDrawMarkerLine_clicked()));//实例化地图类MapCore mapCore = (MapCore)AppConfig::MapDrawCore;    int zoom = MapHelper::getMapZoom(mapCore, this->objectName());mapObj = MapHelper::getMapObj(this, mapCore);mapObj->setWebView(webView);mapObj->setSaveFile(SaveFile);mapObj->setMapLocal(AppConfig::MapDrawLocal);mapObj->setMapType(AppConfig::MapDrawType);mapObj->setCenterPoint(center);mapObj->setZoom(zoom);//实例化封装类drawMarkerLine = new MapDrawMarkerLine(this);connect(drawMarkerLine, SIGNAL(updatePoints()), this, SLOT(on_btnGetMarkerLine_clicked()));connect(drawMarkerLine, SIGNAL(markerClick(QString)), this, SLOT(markerClick(QString)));connect(drawMarkerLine, SIGNAL(receivePoints(QStringList)), this, SLOT(receivePoints(QStringList)));connect(webView, SIGNAL(receiveDataFromJs(QString, QVariant)), drawMarkerLine, SLOT(receiveDataFromJs(QString, QVariant)));
}void frmMapDrawMarkerLine::initTable()
{QList<QString> columnNames;QList<int> columnWidths;columnNames << "经纬度";columnWidths << 50;int columnCount = columnNames.count();ui->tableWidget->setColumnCount(columnCount);ui->tableWidget->setHorizontalHeaderLabels(columnNames);for (int i = 0; i < columnCount; ++i) {ui->tableWidget->setColumnWidth(i, columnWidths.at(i));}//通用函数设置表格控件QtHelper::initTableView(ui->tableWidget, 25, true);
}void frmMapDrawMarkerLine::loadTable()
{int count = AppConfig::MarkerPoints.count();ui->tableWidget->setRowCount(count);for (int i = 0; i < count; ++i) {QString point = AppConfig::MarkerPoints.at(i);ui->tableWidget->setItem(i, 0, new QTableWidgetItem(point));}
}void frmMapDrawMarkerLine::loadPoint()
{QStringList points;int count = ui->tableWidget->rowCount();for (int i = 0; i < count; ++i) {points << ui->tableWidget->item(i, 0)->text();}//设置默认中心点/没有数据的时候取一个/有的话从坐标点集合取第一个QString center = points.count() == 0 ? "121.56358,31.11566" : points.first();drawMarkerLine->clear();drawMarkerLine->setPara(points);drawMarkerLine->init("test", center, 0);
}void frmMapDrawMarkerLine::runJs(const QString &js)
{mapObj->runJs(js);
}void frmMapDrawMarkerLine::markerClick(const QString &point)
{//先恢复上一个图标QString p = ui->txtPoint->text();if (!p.isEmpty()) {int index = AppConfig::MarkerPoints.lastIndexOf(p);QString flag = QString("marker%1_%0").arg("test").arg(index);QString image = MapHelper::getMarkerIcon("blue", index);this->runJs(QString("setMarker('%1', null, null, null, '%2')").arg(flag).arg(image));}//切换选中图标int index = AppConfig::MarkerPoints.lastIndexOf(point);QString flag = QString("marker%1_%0").arg("test").arg(index);QString image = MapHelper::getMarkerIcon("yellow", index);this->runJs(QString("setMarker('%1', null, null, null, '%2')").arg(flag).arg(image));ui->txtPoint->setText(point);ui->tableWidget->selectRow(index);
}void frmMapDrawMarkerLine::receivePoints(const QStringList &points)
{AppConfig::MarkerPoints = points;AppConfig::writeConfig();this->loadTable();
}void frmMapDrawMarkerLine::on_btnDrawMarkerLine_clicked()
{//开启标注后禁用双击放大if (ui->btnDrawMarkerLine->text() == "开启标注") {mapObj->setEnable(EnableType_DoubleClickZoom, false);drawMarkerLine->setMapObj(mapObj);ui->btnDrawMarkerLine->setText("停止标注");this->loadPoint();} else {mapObj->setEnable(EnableType_DoubleClickZoom, true);drawMarkerLine->stop();drawMarkerLine->setMapObj(NULL);ui->btnDrawMarkerLine->setText("开启标注");}
}void frmMapDrawMarkerLine::on_btnGetMarkerLine_clicked()
{drawMarkerLine->getPoints();int row = ui->tableWidget->currentRow();if (row > 0) {ui->txtPoint->setText(ui->tableWidget->item(row, 0)->text());}
}void frmMapDrawMarkerLine::on_btnDeleteMarkerLine_clicked()
{if (ui->btnDrawMarkerLine->text() == "开启标注") {return;}//移除单个点并重新加载QString point = ui->txtPoint->text().trimmed();if (point.contains(",")) {AppConfig::MarkerPoints.removeOne(point);AppConfig::writeConfig();this->loadTable();this->loadPoint();on_tableWidget_itemSelectionChanged();}
}void frmMapDrawMarkerLine::on_btnClearMarkerLine_clicked()
{if (ui->btnDrawMarkerLine->text() == "开启标注") {return;}if (QtHelper::showMessageBoxQuestion("确定要清空吗? 清空后无法恢复!") == QMessageBox::Yes) {AppConfig::MarkerPoints.clear();AppConfig::writeConfig();this->loadTable();this->loadPoint();}
}void frmMapDrawMarkerLine::on_tableWidget_itemSelectionChanged()
{int row = ui->tableWidget->currentRow();if (row >= 0) {QString point = ui->tableWidget->item(row, 0)->text();this->markerClick(point);}
}void frmMapDrawMarkerLine::on_btnStart_clicked()
{//没有一个数据则不用继续QStringList datas = AppConfig::MarkerPoints;if (datas.count() <= 0) {return;}ui->btnPause->setText("暂停回放");if (ui->btnStart->text() == "开始回放") {QString image = MapHelper::getFlyIcon(mapObj);this->runJs(QString("removeMoveMarker('%1')").arg(flag));this->runJs(QString("addMove('%1', '%2', %3, true, true, '%4', 48, 48)").arg(flag).arg(datas.join("|")).arg(500).arg(image));this->runJs(QString("moveStart('%1')").arg(flag));ui->btnStart->setText("停止回放");ui->widgetBtn->setEnabled(false);} else {this->runJs(QString("moveStop('%1')").arg(flag));ui->btnStart->setText("开始回放");ui->widgetBtn->setEnabled(true);}
}void frmMapDrawMarkerLine::on_btnPause_clicked()
{//启动阶段才能暂停if (ui->btnStart->text() == "开始回放") {return;}if (ui->btnPause->text() == "暂停回放") {this->runJs(QString("movePause('%1')").arg(flag));ui->btnPause->setText("继续回放");} else {this->runJs(QString("moveNext('%1')").arg(flag));ui->btnPause->setText("暂停回放");}
}

四、相关地址

  1. 国内站点:https://gitee.com/feiyangqingyun
  2. 国际站点:https://github.com/feiyangqingyun
  3. 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 文件地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取码:o05q 文件名:bin_map.zip

五、功能特点

5.1 地图功能

  1. 支持多种地图内核,默认采用百度地图,可选高德地图、天地图、腾讯地图、谷歌地图等。
  2. 同时支持在线地图和离线地图两种模式,离线地图方便在不联网的场景中使用。
  3. 支持各种地图控件的启用,比如地图导航、地图类型、缩略图、比例尺、全景导航、实时路况、绘图工具、结果面板等。
  4. 支持多种地图功能的动态启用禁用,比如地图拖曳、键盘操作、滚轮缩放、双击放大、连续缩放、地图测距等。
  5. 提供众多js函数接口用于交互,参数极其丰富,能够想到的应用场景需求都有。
  6. 统一的信号槽机制,地图中的结果统一信号发送出去,收到后根据type类型区分。
  7. 支持地图交互,比如鼠标按下获取对应位置的经纬度。单击标注点弹出对应点的信息。
  8. 支持添加标注、删除标注、移动标注、清空标注。
  9. 标注点可以指定图标图片和尺寸,支持gif动图,支持指定以图片中心对齐还是底部中心对齐。可以设置旋转角度,带富文本提示信息。
  10. 标注点事件支持单击发信号通知和自己弹框显示信息。
  11. 提供地址转坐标和坐标转地址接口。
  12. 支持各种图形绘制,包括折线图、多边形、矩形、圆形、弧线等。
  13. 可显示悬浮的绘图工具栏,直接在地图上划线、标注点、矩形、圆形等。
  14. 支持各种区域搜索,比如矩形区域、圆形区域,可以按照关键字匹配将搜索结果显示在地图中。
  15. 可动态添加离线的行政区边界点数据。可以搜索行政区划并获取该区域的边界点数据。数据可以保存到文件以便离线使用。
  16. 支持点聚合功能,多个小标注点合并到一个大标注点,防止点密集导致交互不友好。
  17. 可以添加海量点,每个点都可以单击获取对应坐标和信息。
  18. 所有的覆盖物信息比如标注点、矩形、多边形、折线图等,都可以主动获取对应的信息比如坐标点和路径等。
  19. 支持路径规划,支持公交路线、自驾路线、步行路线、骑行路线,不同查询支持不同策略,可选最少时间、最少换乘、不走高架等。
  20. 路径规划结果可以显示在地图中,也可以获取到路径点坐标集合。这个数据可以保存到文件,以便发给机器人或者无人机做导航用来轨迹移动。
  21. 可以设置不同的地图视图比如街道图、卫星图、混合图。
  22. 可以设置不同的样式,比如午夜蓝、青草绿等样式风格。
  23. 可以设置地图的旋转角度和倾斜角度。
  24. 提供经纬度坐标纠偏转换功能,比如传入的GPS坐标需要转换到百度地图坐标或者高德地图坐标。各种坐标系转换全部离线函数,支持地球坐标系WGS-84、火星坐标系GCJ-02、百度坐标系BD-09之间的互相转换,涵盖了各种地图的坐标系。
  25. 提供动态轨迹点移动功能,按照给定的经纬度坐标集合平滑移动。
  26. 同时支持qwidget和qml,支持编译到安卓系统运行。

5.2 其他功能

  1. 提供离线地图下载模块,可以选择不同的地图内核比如百度地图或者谷歌地图,不同的地图类型比如下载街道图还是卫星图,不同的地图层级,多线程极速下载。
  2. 表格行实时显示对应的瓦片下载进度,有下载超时时间,重试次数,每个瓦片下载完成都发送信号通知,参数包括下载用时。
  3. 提供省市轮廓图下载模块,自动下载各个地区的轮廓图,保存到脚本文件或者文本文件。
  4. 支持手动调整不同区域的轮廓边界,调整后可以主动获取调整后的边界点集合。
  5. 提供动态点位示例,手动在地图上选点并添加标注,附带自定义的信息比如速度和时间等。
  6. 提供海量点位示例,批量添加标注点、点聚合、海量点。用于测试环境中支持的最大点位性能。
  7. 提供动态轨迹示例,在地图上鼠标按下选择起点和终点后,查询路线,获取路径轨迹点,模拟轨迹平滑移动。可以筛选数据将过多的路径点筛选到设定的点数。
  8. 提供轨迹回放示例,按照指定的轨迹点列表回放,也可以导入轨迹点数据进行回放。同时支持在街道图、卫星图、混合图中回放轨迹。
  9. 提供省市区域地图示例,采用echart组件,同时支持闪烁点图、迁徙图、区域地图、世界地图、仪表盘等。可以设置标题、提示信息、背景颜色、文字颜色、线条颜色、区域颜色等各种颜色。
  10. 省市区域地图示例,内置世界地图、全国地图、省份地图、地区地图,可以精确到县,所有地图全部离线使用。可设置城市的名称、值、经纬度集合。
  11. 内置通用浏览器组件,同时支持webkit/webengine/miniblink等内核。提供网页控件示例,演示打开网页和本地网页文件。
  12. 支持任意Qt版本、任意系统、任意编译器。
http://www.hskmm.com/?act=detail&tid=27076

相关文章:

  • 【GitHub每日速递 251009】AI时代必备!Stagehand浏览器自动化框架解锁高效新玩法
  • iOS应用商店遭遇首次大规模恶意软件攻击
  • VMware ESXi 9.0.1.0 macOS Unlocker OEM BIOS 2.7 集成网卡驱动和 NVMe 驱动 (集成驱动版)
  • vue3使用ts传参教程
  • 解密prompt系列61. 手搓代码沙箱与FastAPI-MCP实战
  • MySQL 高可用构建方案详解
  • VMware ESXi 9.0.1.0 macOS Unlocker OEM BIOS 2.7 标准版和厂商定制版
  • 多机器人协同首现基础模型技术突破
  • PHP 图像处理实战 GD/Imagick 从入门到精通,构建高性能图像服务
  • 2025氧化镁厂家、活性氧化镁厂家、肥料级氧化镁厂家最新推荐榜:实力生产与优质供应之选
  • 2025学校家具定制厂家/书包柜厂家推荐榜:专业设计与安全品质首选
  • 2025农机带厂家最新推荐榜:质量可靠与耐用性能兼备之选
  • 2025深圳电源适配器厂家最新推荐榜:高效耐用与安全认证深度解析
  • 2025试验机厂家最新推荐榜:精准测量与高效检测口碑之选
  • 2025喷砂厂家 / 热喷锌厂家 / 热喷铝厂家 / 油漆涂装厂家 / 热喷耐磨材料厂家 / 防腐工程厂家最新推荐榜:高效作业与优质工艺口碑之选
  • 2025上海经济纠纷律师/民事纠纷律所最新推荐榜:专业辩护与胜诉保障口碑之选
  • Semantic Kernel + AutoGen = 开源 Microsoft Agent Framework
  • 读技术之外:社会联结中的人工智能04数据
  • Potplayer Official Download LINK
  • 二廿计划(25.10.09 - 25.10.29)
  • 生成式AI与计算教育融合研究
  • 博科SAN switch 所有端口的led闪橙色
  • 251008
  • 2025年R系列斜齿轮减速机厂家最新推荐:R系列斜齿轮减速机/F系列平行轴齿轮减速机/K系列螺旋斜齿轮减速机/S系列蜗轮减速机实力厂家精准传动解决方案
  • 2025化工泵厂家权威推荐榜:磁力泵/多级泵/高温泵/混流泵/浆液泵/螺杆泵/陶瓷泵/脱硫泵/旋涡泵/液下泵/轴流泵/自吸泵厂家,高效节能与耐用品质实力之选
  • C语言 strtol() 函数用法
  • 课程作业
  • 国庆七日赛训总结
  • SpringCloud实用篇02-(Nacos配置管理,Feign远程调用,Gateway服务网关) - a
  • 总资料汇总关联化站点形式的尝试(未完成)