服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - Qt编写地图实现实时动态轨迹效果

Qt编写地图实现实时动态轨迹效果

2022-09-15 18:13feiyangqingyun C/C++

实时动态轨迹主要是需要在地图上动态显示GPS的运动轨迹,也是编写地图时一个重要的功能。本文将利用Qt实现这一功能,需要的可以参考一下

一、前言

实时动态轨迹经历过很多个版本的迭代,此功能最初是一个客户定制的,主要是需要在地图上动态显示GPS的运动轨迹,有个应用场景就是一个带有监控的车子,实时在运动中,后台可以接收到经纬度信息,需要绘制对应的轨迹,相当于这些摄像机点位是动态移动的,这样就可以观测到摄像机的实时位置信息,双击摄像机还可以弹出画面实时预览,很直观。

GPS运动轨迹这个功能,也需要用到js的知识,其实就是封装一个js函数,绘制对应的线条路径,这个轨迹点可能包括的信息有经度、纬度、速度、时间、是否标记、时间等信息,写个结构体封装下,方便后期拓展,是否标记的含义是是否改点同时作为一个设备点添加,分段线的含义。

后面陆续增加了可以设置旋转角度、可以过滤坐标点这两个要点,设置旋转角度采用的是内置的setRotation函数,流程是先从一堆覆盖物中通过唯一标识比如name找到当前要移动的点,然后对这个标注点调用setRotation设置要旋转的角度值,所以这里衍生了另外一个需求,如何计算两个点之间的旋转角度值,这个值必须是提前计算好的,这就要用到数学知识了,用atan2来计算,同时做矫正。

二、功能特点

定时器排队下载省市轮廓图点坐标集合存储到JS文件。

支持一个行政区域多个不规则区域下载。

自动计算行政区域的下载轮廓数量。

可精确选择省份、市区、县城,也可直接输入行政区域的名称。

可以设置下载间隔、随时开始下载和停止下载。

提供编辑边界功能,可以直接在地图上编辑好不规则区域的点集合,然后获取边界点集合数据,这个可以用来自己绘制区域拿到数据,比如某个乡镇甚至某个小区的行政区域数据,很牛逼。

三、体验地址

体验地址:https://pan.baidu.com/s/15ZKAlptW-rDcNq8zlzdYLg  提取码:uyes 文件名:bin_map.zip

国内站点:https://gitee.com/feiyangqingyun

国际站点:https://github.com/feiyangqingyun

四、效果图

Qt编写地图实现实时动态轨迹效果

五、相关代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
void frmMapGps::receiveDataFromJs(const QString &type, const QVariant &data)
{
    if (data.isNull()) {
        return;
    }
 
    //qDebug() << "frmMapGps" << type << data;
    QString result = data.toString();
    if (type == "point") {
        if (ui->ckSelectAddr->isChecked()) {
            //判断哪里勾选了就设置到哪里
            QString point = WebHelper::getLngLat2(result);
            //判断哪里勾选了就设置到哪里
            if (ui->rbtnStartAddr->isChecked()) {
                ui->txtStartAddr->setText(point);
            } else {
                ui->txtEndAddr->setText(point);
            }
        }
    } else if (type == "routepoints") {
        //将查询路径转换成经纬度坐标点集合数据显示
        routeDatas.clear();
        ui->tableWidgetSource->clearContents();
        //可能会有多个路径集合,目前测试下来都是一个路径集合
        QStringList datas = result.split("|");
        foreach (QString data, datas) {
            QStringList points = data.split(";");
            routeDatas << points;
            int count = points.count();
            ui->tableWidgetSource->setRowCount(count);
            for (int i = 0; i < count; ++i) {
                addItem(ui->tableWidgetSource, i, points.at(i));
            }
        }
 
        setInfo(0, 0, 0);
    }
}
 
void frmMapGps::runJs(const QString &js)
{
    web->runJs(js);
}
 
void frmMapGps::on_btnSearchData_clicked()
{
    QString startAddr = ui->txtStartAddr->text().trimmed();
    QString endAddr = ui->txtEndAddr->text().trimmed();
    baidu->setRotueInfo(2, 0, startAddr, endAddr);
    this->loadMap();
}
 
void frmMapGps::moveMarker()
{
    QTableWidget *tableWidget = getTableWidget();
    int row = tableWidget->currentRow();
    int count = tableWidget->rowCount();
    if (row >= 0 && row < count) {
        //找出和上一个点之间的角度
        int angle = 0;
        QString point = tableWidget->item(row, 1)->data(Qt::UserRole).toString();
        //第一个点和最后一个点不用处理
        if (row > 0 && row < count - 1) {
            //上一个点坐标
            QString point2 = tableWidget->item(row - 1, 1)->data(Qt::UserRole).toString();
            //计算当前上一个点和当前点的旋转角度
            angle = WebHelper::getAngle(point2, point);
        }
 
        //执行移动设备点函数,参数带旋转角度
        QString js = QString("moveMarker('%1', '%2', %3)").arg(name).arg(point).arg(angle);
        runJs(js);
 
        //重新绘制轨迹点
        if (ui->cboxMoveMode->currentIndex() == 0) {
            //清空之前的轨迹点
            js = QString("deleteOverlay('Polyline')");
            runJs(js);
 
            //取出第一个点到当前焦点所在行的点组成已经走过的轨迹点集合重新绘制
            QStringList points;
            for (int i = 0; i <= row; ++i) {
                points << tableWidget->item(i, 1)->data(Qt::UserRole).toString();
            }
 
            js = QString("addPolyline('%1')").arg(points.join("|"));
            runJs(js);
        }
 
        //显示当前第几个数据
        setInfo(angle, row + 1, count);
        tableWidget->setCurrentCell(row + 1, 0);
    } else {
        on_btnTestData_clicked();
    }
}
 
void frmMapGps::on_btnTestData_clicked()
{
    QTableWidget *tableWidget = getTableWidget();
    if (ui->btnTestData->text() == "模拟轨迹") {
        //限制最小数量
        if (tableWidget->rowCount() < 2) {
            return;
        }
 
        //第一步: 添加一个标记
        name = ui->txtDeviceName->text().trimmed();
        if (name.isEmpty()) {
            name = "马航MH370";
        }
 
        //图片文件在可执行文件下的config/device目录
        QString icon = "./device/device_airplane.png";
        int size = 60;
        QString js = QString("addMarker('%1', '', '', '', 60, '%1', 0, 0, '%2', %3)").arg(name).arg(icon).arg(size);
        runJs(js);
 
        //第二步: 移到第一个点
        tableWidget->setFocus();
        tableWidget->setCurrentCell(0, 0);
        ui->btnTestData->setText("停止模拟");
        ui->tabWidget->setTabEnabled(ui->tableWidgetSource->isVisible() ? 1 : 0, false);
 
        //第三步: 启动定时器并立即执行一次
        int index = ui->cboxMoveInterval->currentIndex();
        timer->start(ui->cboxMoveInterval->itemData(index).toInt());
        moveMarker();
    } else {
        //清空标记
        QString js = QString("deleteMarker('%1')").arg(name);
        runJs(js);
 
        //停止定时器
        timer->stop();
        ui->btnTestData->setText("模拟轨迹");
        ui->tabWidget->setTabEnabled(ui->tableWidgetSource->isVisible() ? 1 : 0, true);
    }
}
 
void frmMapGps::on_btnCheckData_clicked()
{
    if (timer->isActive()) {
        return;
    }
 
    //第一步: 计算总数,求平均值=实际总数/预期总数+1,预期总数>=实际总数则不用处理
    int countSource = ui->tableWidgetSource->rowCount();
    int countTarget = ui->txtPointCount->text().trimmed().toInt();
    if (countTarget >= countSource) {
        QUIHelper::showMessageBoxError("目标点数不能大于等于原数据点数!");
        ui->txtPointCount->setFocus();
        return;
    }
 
    //第二步: 根据平均值挨个取出值
    QStringList points;
    int avg = countSource / countTarget + 1;
    for (int i = 0; i < countSource; i += avg) {
        QString point = ui->tableWidgetSource->item(i, 1)->data(Qt::UserRole).toString();
        points << point;
    }
 
    //必须加上末尾这个作为结束,如果刚好除尽则不用
    QString point = ui->tableWidgetSource->item(countSource - 1, 1)->data(Qt::UserRole).toString();
    if (points.last() != point) {
        points << point;
    }
 
    //第三步: 将数据重新填入筛选数据列表
    int count = points.count();
    ui->tableWidgetTarget->clearContents();
    ui->tableWidgetTarget->setRowCount(count);
    for (int i = 0; i < count; ++i) {
        addItem(ui->tableWidgetTarget, i, points.at(i));
    }
 
    ui->tabWidget->setCurrentIndex(1);
}
 
void frmMapGps::on_btnDrawData_clicked()
{
    if (routeDatas.count() == 0) {
        QUIHelper::showMessageBoxError("请先单击查询路线获取路线的坐标点集合!");
        return;
    }
 
    //清空之前的轨迹点
    runJs("deleteOverlay('Polyline')");
 
    //将收到的路径点集合分线段绘制
    foreach (QStringList data, routeDatas) {
        QString points = data.join("|");
        QString js = QString("addPolyline('%1', '#ff0000')").arg(points);
        runJs(js);
    }
}

以上就是Qt编写地图实现实时动态轨迹效果的详细内容,更多关于Qt地图实时动态轨迹的资料请关注服务器之家其它相关文章!

原文链接:https://blog.csdn.net/feiyangqingyun/article/details/122835428

延伸 · 阅读

精彩推荐
  • C/C++Opencv LBPH人脸识别算法详解

    Opencv LBPH人脸识别算法详解

    这篇文章主要为大家详细介绍了Opencv LBPH人脸识别算法的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    东城青年7882021-07-29
  • C/C++C++指向函数的指针用法详解

    C++指向函数的指针用法详解

    这篇文章主要介绍了C++指向函数的指针用法,对函数指针的声明、优先级、指针类型等概念做了较为详尽的分析,需要的朋友可以参考下...

    C++教程网10932021-01-30
  • C/C++opencv实现颜色检测

    opencv实现颜色检测

    这篇文章主要为大家详细介绍了opencv实现颜色检测,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    努力的小带土5422021-11-01
  • C/C++C++实现LeetCode(557.翻转字符串中的单词之三)

    C++实现LeetCode(557.翻转字符串中的单词之三)

    这篇文章主要介绍了C++实现LeetCode(557.翻转字符串中的单词之三),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以...

    Grandyang9382021-12-10
  • C/C++c++ #include是怎么样工作的?

    c++ #include是怎么样工作的?

    大多数园友可能对“#include”比较熟悉,因为我们写C/C++程序的时候都会写的字符串之一,但是它是具体怎么工作的?或者它的原理是什么呢?...

    C++教程网1712020-11-15
  • C/C++C++ 中"priority_queue" 优先级队列实例详解

    C++ 中"priority_queue" 优先级队列实例详解

    这篇文章主要介绍了C++ 中"priority_queue" 优先级队列实例详解的相关资料,需要的朋友可以参考下...

    caroline_wendy5552021-05-06
  • C/C++Cocos2d-x触摸事件实例

    Cocos2d-x触摸事件实例

    这篇文章主要介绍了Cocos2d-x触摸事件实例,本文代码中包含大量注释来说明Cocos2d-x中的触摸事件使用示例,需要的朋友可以参考下...

    C语言程序设计10492021-02-02
  • C/C++C语言 文件的打开与关闭详解及示例代码

    C语言 文件的打开与关闭详解及示例代码

    本文主要介绍C语言 文件的基础知识,这里整理了相关资料及示例代码,有兴趣的小伙伴可以参考下...

    C语言中文网11362021-04-14