惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
The Cloudflare Blog
J
Java Code Geeks
Apple Machine Learning Research
Apple Machine Learning Research
T
Tailwind CSS Blog
L
LangChain Blog
Recent Announcements
Recent Announcements
Hugging Face - Blog
Hugging Face - Blog
Microsoft Security Blog
Microsoft Security Blog
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
V
V2EX
I
InfoQ
博客园 - 司徒正美
T
The Blog of Author Tim Ferriss
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
H
Help Net Security
博客园 - 三生石上(FineUI控件)
S
SegmentFault 最新的问题
B
Blog RSS Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

博客园 - 飞扬青云

坚持古法编程有意义吗/纯手搓代码/监控Onvif调试神器 AI编程道心崩塌/十几年Qt开发经验一文不值 就说逆不逆天/支持高达500路监控画面/多个屏幕同时显示/轻松实现跨屏实时监控 高程海拔离线实时查询显示终极版/体验友好/全网首创极精简版本/可输入地址或者经纬度查询 Qt实现很好玩的滤镜效果/高斯模糊径向模糊/底片锐化暗角鱼眼/多点矫正几何矫正边缘融合/扭曲网格调整 震惊都6202年了/地图控件居然支持Qt4这个老古董/支持国产军工低配Qt环境 Qt代码实现地图瓦片电子放大/无极缩放/可将层级拓展到22级别/精确到0.03米 纯QWidget绘制打造地图组件/极速加载瓦片地图/放大缩小地图过渡效果 这才是地图控件该有的样子/不依赖js和qml/纯古法原创自研/纯代码绘制/最新版含泪发布 效果拉满/纯Qt实现海图地图展示/矢量底图+海图+路网标记任意多层叠加/完美支持海图叠加与多投影系 GB28181视频监控推流服务/点播预览推流/录像回放推流/指令预览推流/指令回放推流/自动预览推流 Qt电子地图叠加海图/全世界海图叠加到天地图图层/1-18级海图实时查看 超级屌的动态测距交互功能/耗时几万个脑细胞/自研原创地图控件 Qt纯原创代码实现GB28181组件/自动点播拉流并推流/支持录像回放和下载/性能凶残 全网原创再次熬夜升级/支持云台预置位的onvif设备模拟器/虚拟监控摄像头/可设置主码流子码流 使用ffmpeg播放不同节目/实时切换节目并推流/推流不同的节目流 新版监控GB28181调试神器V2026发布/对标wvp/绿色版开箱即用 对标国际Onvif工具odm/新版视频监控onvif调试助手V2026发布/简单好用开箱即用 全网唯一/不依赖浏览器和qlocation/纯代码绘制实现的地图组件/短小精悍 监控设备模拟神器V2026全新含泪发布/支持onvif和gb28181/支持云台控制和预置位 将地图组件运行在浏览器上/qtlocation都做不到/轻巧好用/自研原创 不依赖任何AI代码/纯qpainter实现的航线规划/飞行轨迹规划和模拟/无人机轨迹 如何快速搭建一套支持ONVIF的安防监控系统 没有想不到只有做不到/万能地图标注点marker设计/各种参数需求场景/你要的都有 呕心沥血/历时半年/古法编程/纯QPainter实现的地图控件/性能爆表 绘制1000个无人机只需7ms/优化到极致/地图组件海量标注点/纯QPainter绘制的威力 记一个优化改良的实施过程/纯QPainter实现的地图组件/支持天地图谷歌地图等 性能提升百倍/Qt地图组件纯QWidget绘制/缓存瓦片技术/多线程加载绘制 纯QWidget绘制实现电子地图控件/非qml非web/多线程下载和加载瓦片/支持各种图形 Qt实现离线地图下载/瓦片地图下载/划区域下载离线地图/多线程批量下载
实时轨迹/轨迹回放/多条轨迹同时回放/轨迹平滑移动/移动速度...
飞扬青云 · 2026-03-17 · via 博客园 - 飞扬青云

一、前言说明

地图的二次开发中,显示实时轨迹或者轨迹回放都是必备的功能,也是目前最流行的无人机系统中的核心功能,这个功能思考了很久,主要是考虑需要哪些接口,比如可设置标注点的图片、移动的速度、移动的间隔、数据轨迹的颜色、移动轨迹的颜色等,可以是传入轨迹点集合进行轨迹回放,也可以动态append添加新的点做实时轨迹显示,轨迹点必须是平滑移动,根据移动的速度自动生成中间的点,比如两个点之间距离是1000米,速度是100米每次,则需要在这两个点之间产生9个点进行均等分,定时器每次取出一个点移动过去,如果没有这个处理,则上一个点移动到下一个点,都是瞬间移动过去,理论上很难看,因为实际上很可能是慢慢的平滑移动过去的。

一般都会绘制一个实时的轨迹,为了突出接口丰富,方便用户使用,这里还增加函数可以动态设置数据轨迹以及实时轨迹线是否可见,不需要的时候可以设置不可见,每种轨迹都可以动态设置颜色和粗细,上述功能全部封装成了movemarker类,只需要new就可以用,有几条就new几个实例就行,多次反复测试,效果非常棒,不枉费这些天这么久才把这个类思考好。我写程序一般是写草稿设计好思考好,然后再写,持续迭代,这个习惯保持了快20年,廉颇老矣,哎,年轻真好。

二、效果图

Snipaste_2026-03-16_14-01-44

三、相关代码

#include "movemarker.h"
#include "maputil.h"
#include "overlayhelper.h"

MoveMarker::MoveMarker(MapWidget *mapWidget, const QString &flag, const QPixmap &pixmap, const QList<QPointF> &points, int speed, int interval, bool smooth, bool moveInCenter) : QObject(mapWidget)
{
    //生成对应覆盖物的唯一标识
    this->dataLineFlag = "dataLine_" + flag;
    this->moveLineFlag = "moveLine_" + flag;
    this->moveMarkerFlag = "moveMarker_" + flag;

    this->mapWidget = mapWidget;
    this->pixmap = pixmap;
    this->speed = speed;
    this->smooth = smooth;
    this->moveInCenter = moveInCenter;

    //约定第一个点是起始点/如果只有一个点则说明是动态添加数据生成轨迹/多个点则是历史轨迹回放
    angle = 0;
    home = QPointF(121.424362, 31.175942);
    int count = points.count();
    if (count > 0) {
        home = points.first();
    }

    //多个数据则说明是轨迹回放/设置到可视区域/实时轨迹则设置起点作为中心点
    if (count > 1) {
        mapWidget->setAutoView(points);
        angle = MapUtil::getAngle(points.at(0), points.at(1));

        //传入速度也就是每次移动的距离/逐个取出点生成平滑的点
        if (smooth) {
            for (int i = 0; i < count - 1; ++i) {
                this->points << MapUtil::getLinePoints(points.at(i), points.at(i + 1), speed);
            }
        } else {
            this->points = points;
        }
    } else {
        this->points = points;
        mapWidget->setCenter(home.x(), home.y());
    }

    //添加数据轨迹线
    mapWidget->addPolyline(dataLineFlag, points, QColor(255, 0, 0), 5);
    //添加移动轨迹线
    mapWidget->addPolyline(moveLineFlag, QList<QPointF>() << home, QColor(0, 0, 0), 3);
    //添加移动标注点
    marker = mapWidget->addMarker(moveMarkerFlag, home.x(), home.y(), QString(), pixmap, angle, 2);

    //定时器移动标注点
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(move()));
    timer->setInterval(interval);
}

MoveMarker::~MoveMarker()
{
    //停止定时器
    timer->stop();
    this->points.clear();

    //删除覆盖物
    mapWidget->deleteOverlay(dataLineFlag);
    mapWidget->deleteOverlay(moveLineFlag);
    mapWidget->deleteOverlay(moveMarkerFlag);
}

void MoveMarker::start()
{
    index = 0;
    if (points.count() > 1) {
        angle = MapUtil::getAngle(points.at(0), points.at(1));
    }

    timer->start();
    this->move();
}

void MoveMarker::pause()
{
    timer->stop();
}

void MoveMarker::next()
{
    timer->start();
}

void MoveMarker::stop()
{
    timer->stop();
    this->reset();
}

void MoveMarker::append(qreal lng, qreal lat)
{
    QPointF curPoint(lng, lat);
    QPointF prePoint = points.last();
    if (smooth) {
        this->points << MapUtil::getLinePoints(prePoint, curPoint, speed);
    } else {
        this->points << curPoint;
    }

    //添加到数据轨迹
    mapWidget->addPolylineData(dataLineFlag, curPoint.x(), curPoint.y());
}

void MoveMarker::setInterval(int interval)
{
    timer->setInterval(interval);
}

void MoveMarker::setSmooth(bool smooth)
{
    this->smooth = smooth;
}

void MoveMarker::setMoveInCenter(bool moveInCenter)
{
    this->moveInCenter = moveInCenter;
}

void MoveMarker::setDataLineVisible(bool visible)
{
    mapWidget->setOverlayVisible(dataLineFlag, visible);
}

void MoveMarker::setMoveLineVisible(bool visible)
{
    mapWidget->setOverlayVisible(moveLineFlag, visible);
}

void MoveMarker::setDataLineColor(const QColor &color, int width)
{
    mapWidget->updatePolyline(dataLineFlag, QList<QPointF>(), color, width);
}

void MoveMarker::setMoveLineColor(const QColor &color, int width)
{
    mapWidget->updatePolyline(moveLineFlag, QList<QPointF>(), color, width);
}

void MoveMarker::setText(const QString &text, const QColor &textColor, int textAlign, int textOffset, const QString &fontName, int fontSize, const QColor &bgColor, int bgAlpha, const QColor &borderColor, int borderWidth)
{
    OverlayHelper::updateText(marker, text, textColor, textAlign, textOffset, fontName, fontSize, bgColor, bgAlpha, borderColor, borderWidth);
}

void MoveMarker::move()
{
    //每次索引递增
    index++;
    //到了末尾则重来
    if (index >= points.count()) {
        this->reset();
        return;
    }

    //过滤重复的坐标
    QPointF curPoint = points.at(index);
    QPointF prePoint = points.at(index - 1);
    if (prePoint == curPoint) {
        this->move();
        return;
    }

    //移动到新的位置/带旋转角度
    int angle = MapUtil::getAngle(prePoint, curPoint);
    //mapWidget->updateMarker(moveMarkerFlag, curPoint.x(), curPoint.y(), "", QPixmap(), angle);
    mapWidget->addPolylineData(moveLineFlag, curPoint.x(), curPoint.y());

    //最新点一直作为中心点
    if (moveInCenter) {
        mapWidget->setCenter(curPoint.x(), curPoint.y());
    }
}

void MoveMarker::reset()
{
    //复位索引
    index = 0;
    //移动到起始点
    //mapWidget->updateMarker(moveMarkerFlag, home.x(), home.y(), QString(), QPixmap(), angle, 2);
    //移动轨迹复位
    mapWidget->setPolylineData(moveLineFlag, QList<QPointF>() << home);
}

四、相关地址

  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_mapwidget.zip

五、功能特点

  1. 支持各种地图源,包括天地图、高德地图、腾讯地图、谷歌地图、微软地图等。
  2. 标准WGS-84地球坐标系,采用默卡托投影,可以拓展其他坐标系和投影规则。
  3. 支持在线和离线两种场景需求,可以自定义在线瓦片地址格式和离线瓦片地址格式。
  4. 多线程下载和加载瓦片图片文件,多线程绘制,自动缓存瓦片文件。
  5. 在线模式下,可以开启是否缓存文件,指定缓存路径,将下载的瓦片文件存放到本地,默认优先从缓存文件查找,如果存在缓存文件则加载缓存文件,不存在则联网下载。
  6. 可以拖动地图,鼠标滚轮放大和缩小地图,以鼠标所在位置作为缩放中心点,提供缩放控件手动单击进行操作。
  7. 多图层机制,支持多个瓦片叠加图层和图形绘制图层,全部采用双缓冲技术,所有的图形和瓦片全部绘制到一个图片文件上,最终再将图片文件绘制到地图控件。不可见区域的图层包括覆盖物不会触发绘制,降低CPU占用。
  8. 预加载机制,默认绘制的图层大小以当前区域往四周放大两倍,这样在鼠标拖动和缩放的时候,不会看到明显的加载过程,体验更佳。
  9. 内置了多种图形覆盖物,包括标注点、折线、多边形、矩形、圆形等,可以设置边框颜色粗细、填充颜色和透明度等参数。
  10. 标注点支持旋转角度和提示文本,其中提示文本可以设置在标注点的相对位置,标注点图片支持gif动图,可以动态切换静态图和动图。
  11. 标注点和提示文本可以设置相对位置,位置包括左侧、右侧、上侧、下侧、中间、左上角、右上角、左下角、右下角。标注点默认按照底部居中对齐,一般圆形图标可以设置中心点对齐。
  12. 标注点提示文本可设置背景颜色,透明度、颜色边框和粗细,支持html富文本。
  13. 所有的覆盖物可以动态更新前景色、颜色粗细、背景颜色、颜色透明度等。
  14. 支持删除单个覆盖物、删除一种类型的覆盖物、删除所有覆盖物、隐藏指定覆盖物等。
  15. 可以动态启动禁用比例尺、十字线、缩放控件、地图拖曳、键盘操作、滚轮缩放、双击放大等特性。
  16. 可以任意指定经纬度区域进行瓦片拼接保存成图片文件,也可以直接对整个可视区域或者缓存区域的地图图片文件保存。支持任意多边形轮廓保存成图片,比如某个行政区的瓦片保存。
  17. 覆盖物可以动态设置zindex层叠顺序,值越大,越显示在前面,内部维护着一个zindex表,默认按照添加的先后顺序增加,后面添加的显示在前面,主动设置后,按照设置的zindex来绘制。
  18. 支持将QWidget对象作为覆盖物添加到地图控件中,跟随地图移动位置,极大提高灵活性,比如可以将自定义控件直接作为地图控件的子对象加入进去。
  19. 内置moveMarker轨迹移动类,支持历史轨迹数据回放和实时轨迹移动,可设置图标、轨迹线的颜色和粗细、移动速度、移动间隔、平滑移动等,支持多条轨迹线条同时移动。
  20. 大量使用按需绘制机制,包括内部提供合理的默认值来触发绘制,也可以手动传入参数指定是否需要立即绘制,比如删除了某个覆盖物,有些频繁的操作可以不指定立即绘制,等操作完成后再统一一起绘制,效率更高。
  21. 默认开启缓存瓦片机制,所有加载过的瓦片文件都存储在内存中,下次再次绘制直接从内存取出来绘制,既不需要从联网获取,也不需要从缓存文件获取,直接内存取出来绘制,响应迅速效率最高体验最佳。
  22. 支持批量添加覆盖物,比如几万个标注点和圆形,都是瞬间完成绘制,相比web网页的方式,性能提升百倍以上。
  23. 支持街道图、卫星图、混合图、路网图等各种图层,可以任意叠加N个图层,甚至杂交不同地图厂家的瓦片文件。
  24. 纯QWidget绘制,非qml也非web,不依赖qml或者浏览器控件,支持极低性能的嵌入式环境。
  25. 支持任意Qt版本、任意系统、任意编译器,包括嵌入式linux和各种国产电脑环境。古法编程,不含任何AI代码,品质保证。