QT开发框架下调用百度地图实现导航定位

之前在QT开发框架下调用了百度地图,调用流程请见这篇博文添加链接描述
现在是在上面实现导航定位的功能。操作主要在网页内进行。

一、实时定位

实时定位的实现主要是串口的调用,在串口实时输入GPS的坐标,显示在调用的百度地图上。
里面涉及qt程序和html文件的信息交互。
首先在QT程序里面写一个串口的类程序如下所示。

#include "serialtool.h"
#include <QSerialPortInfo>
#include <QDebug>

serialTool::serialTool()
{
    global_port = new QSerialPort();
}

QStringList serialTool::getPortNameList() {  // 这个是用来获取可用串口的函数
    QStringList m_serialPortName;  // 先定义一个QStringList用来存储串口名字
    // 简化版的for循环,foreach(x1,x2):其中每次循环x1都是从x2中取出来的,相当于一个遍历的效果
    // availablePorts返回所有可用的串口,但他是一个QSerialPortInfo对象,我们要做的是在foreach循环里面把他转换成QStringList列表
    // 为什么要转换?答:因为availablePorts对象里面的成员变量太多了,所以把他转换成相对简洁的列表
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        m_serialPortName << info.portName();  // 把每次遍历取到的值写入QStringList中
        qDebug() << "serialportname:" << info.portName();
    }
    return m_serialPortName;
}

void serialTool::open_port() {
    QStringList portNameList = getPortNameList();  // 获取串口列表
    qDebug() << "Debug::serialTool"<< portNameList;
    global_port->setPortName(portNameList[0]);  // 打开第一个,一般来说只有这一个,连接多个串口的时候可能存在选择错误设备的缺陷,最好设置成固定的地址(这个自己查一下)
    global_port->setBaudRate(QSerialPort::Baud115200);  // 固定波特率
    if(global_port->isOpen())// 打开前如果串口还打开着 先给他关闭了
    {
        global_port->clear();
        global_port->close();
    }
    if(!global_port->open(QIODevice::ReadWrite)) //用ReadWrite 的模式尝试打开串口
    {
        qDebug()<<"打开失败!";
        return;
    }
    qDebug()<<"串口打开成功!";
}

void serialTool::close_port() {
    global_port->clear();
    global_port->close();
}

QByteArray serialTool::receive_data() {
    QByteArray array=global_port->readAll();
 //   qDebug()<<array;
    static QByteArray sumData;  // sumData用来做缓存的功能
    static bool arrayContainsGNGGA = false;


    // 第一种:数据帧包含GNGGA,说明是GPS数据帧,先保存到缓存
    if (array.contains("$GNGGA")) {
        sumData.append(array.mid(array.indexOf("$GNGGA")));
        arrayContainsGNGGA = true;
    }
    // 第二种:第一种来的数据不完整,一般是经度数据存在在第一种来的帧的下一帧,
    // 此时把第二帧数据存到缓存sumData中,拼接起来,再做截取处理
    if (array.contains("E")&arrayContainsGNGGA) {
        sumData.append(array);
        arrayContainsGNGGA = false;
        PasteData = sumData;
        sumData.clear();
    }

    // 经过第一种和第二种出来的数据还有一点杂乱,这里进一步过滤
    // 主要是过滤出存在下述三种的帧数据,但是又不存在/r/n这样的换行符数据,
    // 符合要求的就做返回值
    if (PasteData.contains("$GNGGA")&PasteData.contains("N")&PasteData.contains("E")&(!PasteData.contains("\r\n"))) {
        int indexOfGNGGA = PasteData.indexOf("$GNGGA");
        int indexOfN = PasteData.indexOf("N");
        int indexOfE = PasteData.indexOf("E");
        int len = indexOfE - indexOfGNGGA + 1;
        PasteData = PasteData.mid(indexOfGNGGA, len);
        prePasteData = PasteData;
//        qDebug() << PasteData.mid(indexOfGNGGA, len);
        return PasteData;
    } else {
        return prePasteData;
    }
}

每个函数都有详细的注释,在此不再赘述。首先是读取串口列表,打开串口初始化,然后进行缓存。
后面的程序主要是信息的提取。在GPS中提取的数据比较杂乱,要进行数据的拼接然后进提取。

然后在主界面的程序内调用串口类。程序如下。

   static bool serialButtonStatus = false;  // 一个按钮包含开和关的功能,执行不同的方法
    serial = new serialTool();  // 实例化一个串口类对象(这里的串口类对象是左边定义的serialTool类的实例化,你要这个不同的类里面使用它里面的一些函数,肯定要先实例化一个对象出来)
    connect(ui->pushButton, &QPushButton::clicked, [=]() {
        if (serialButtonStatus == false) {  // false时,串口是关闭的,此时打开
            serial->open_port();  // 打开串口
            connect(serial->global_port,&QSerialPort::readyRead,this,&Widget::getGpsData);  // readyRead监听是否来了数据,来了就执行getGpsData函数
            serialButtonStatus = true;
            ui->pushButton->setText("关闭导航");
        } else if (serialButtonStatus == true) {  // true时,串口是打开的,此时关闭
            serial->close_port();  // 关闭串口
            serialButtonStatus = false;
            ui->pushButton->setText("打开导航");
        }

这段程序主要是设置导航的按钮,点击之后,调用串口类,实例化一个串口的对象,打开串口,进行数据的接收。

void Widget::getGpsData() {
    QByteArray gpsData = serial->receive_data();
    double GNGGALat = 0.; // 纬度
    double GNGGALon = 0.; // 经度
    qDebug() << gpsData;
    int firstComma = gpsData.indexOf(",");
    int secondComma = gpsData.indexOf(",",firstComma+1);
    int thirdComma = gpsData.indexOf(",",secondComma+1);
    int forthComma = gpsData.indexOf(",",thirdComma+1);
    int fifthComma = gpsData.indexOf(",",forthComma+1);
    int sixthComma = gpsData.indexOf(",",fifthComma+1);

    //Between third comma and fourth Comma is N or S
    m_strNS = QString(gpsData.mid(thirdComma+1,forthComma-thirdComma-1));
    ui->label_NS->setText(m_strNS);

    //Between fifth comma and sixth Comma is E or W
    m_strEW = QString(gpsData.mid(fifthComma+1,sixthComma-fifthComma-1));
    ui->label_EW->setText(m_strEW);



    GNGGALat = gpsData.mid(secondComma+1,thirdComma-secondComma-1).toDouble();  // 对接收过来的数据进行处理,取得GNGGA格式的经纬度数据
    GNGGALon = gpsData.mid(forthComma+1,fifthComma-forthComma-1).toDouble();  // mid函数用来截取字符串,tofloat将获得的字符串转为float类型
    //qDebug()  << "经度:" << gpsData.mid(secondComma+1,thirdComma-secondComma-1) << firstComma << "secondComma" << secondComma << "thirdComma" <<thirdComma;
    //qDebug() << "纬度" << gpsData.mid(forthComma+1,fifthComma-forthComma-1);
    double gpsLon = (int)(GNGGALon/100) + (((GNGGALon/100) - (int)(GNGGALon/100))/0.6); //将GNGGA格式的经纬度数据转换成gps格式的
    double gpsLat = (int)(GNGGALat/100) + (((GNGGALat/100) - (int)(GNGGALat/100))/0.6);
    ui->dsb1->setValue(gpsLon);
    ui->dsb2->setValue(gpsLat);

    QString jsStr="addMarker("+QString::number(gpsLon)+","+QString::number(gpsLat)+")";
    ui->webView->page()->runJavaScript(jsStr);
}

这段程序是在QT的UI界面内对串口内的数据进行接收,包括经纬度数据类型的转换。
实时定位的功能实现之后,就要实现定位导航的功能。

二、路径规划

首先是要在百度地图界面内添加输入起始点和终点的label,然后设置起点的按钮,设置终点的按钮。
信息都设置好之后,设置一个按键进行路径规划,此时是静态的,并不会有动态图标,但是会规划好最适宜的路径
然后设置一个开始按钮,可以显示动态的路径规划。
程序如下。

<li class="input-wrap"> <input id="x-input-start" class="input" placeholder="经度"></li>
        <li class="input-wrap"> <input id="y-input-start" class="input" placeholder="纬度"> </li>
        <li class = "light btn" onclick = "setStartPoint()">设置起点</li>
        <li class="input-wrap"> <input id="x-input-end" class="input" placeholder="经度"></li>
        <li class="input-wrap"> <input id="y-input-end" class="input" placeholder="纬度"> </li>
        <li class = "light btn" onclick = "setEndPoint()">设置终点</li>
        <li class = "light btn" onclick = "route()">规划</li>
        <li class = "light btn" onclick = "run()">开始</li>
        <li class = "night btn" onclick = "removeOverlay()">清除</li>

实现的界面如下所示。
在这里插入图片描述

然后提供一个供刷新和删除gps标记点的全局变量,
增加手动添加定位点的函数
fly后面的网址只是动态路径规划时的图片信息,不要因为这个而迷惑。

 var preMarker // 定义一个供刷新和删除gps标记点的全局变量
    var fly = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAEGUlEQVRIiZ2Wa4hVVRiGn/fbZ0YnNX+kXUwLtAgsgoJILEXEwDTtT5FFBEKkUEZC9KM/3Sn6FUiRf1MIiexillmSiE5ZEKLZhTBjssAgMbwxZ85eb6y9z5kZJ8cRF6zDZp31fe/7vfu7bB2eXjB8NSVKgpCQwEpEqUlFyzf3F75FjWIqAz4WcIAG31s+LlcXSUoUhu6UMBr02uDC1uUW1wvNxlwBHEP0A38Cx8fyMDpITWQO1sok5pZdmiXTg40KMDyCOQzaC3oHvGNUV6PI1SXxvOBxYHI+z4pUuG4T8FlmOar1ll8szD8j5Qq1bYaOmCbxvuDZDkDFJjs1Zyp5zOkRZMcBT2J9Atz4v0gOTW8MyhPJ3f1F8YHRErWpttn3WmzGHEAcxb4M6QbBcpvFHYZ1kO4dn9JSwfFONNEfBXmfKboYUONlYElNu9bI5okyvFCwG2kO8KikBcD+FF6GWAk60WFtxdxmFG8NqKBEtBA6eM24WmJxa5jdYfdU+oum8DInfZPkjULLRsqQpF0FPETZmmIVu5AuzQqUUjMplhet8vNMNxqGMBT2GlEDVA5KPy20h+CHcA3QedduP4Q9H7wvpH9LvAKcXJ93F6lck7M3u4uAnsAzwPMGQ4avLL1ZJn2czAzXmdEMsc5OyyVeFZyoATWlFbFVEdtBW4YSxXdizyN5ZqMMFgM3Yc0aTDn4FHmuxcL262mS9DDwHg1NjZa2JLzTVRZ6ouzZSPcD7wL31iiaHAWrgX1RRpqW5CmYb2W+BnYCXwCLckh1xmhdBSCewRwEXg+xvQhecK6I3IKc5tvOdp9B5Wev5ROWk36c0Z11c+BMutVmkXXYgCr2OKVFasQB4Gi+4QIa4atJqEw60s7Uj7Dvo+MjdxOpVT+EHzTMxFV6JqAEpiDNrjU3Cs3D2iHzRir8lKyNTvyFeCxTU42yCKm3IlIpTiD2A781IlUXJiWxeFjVD1WrqtO1xr1Ca2VeywVpc7utV4bZTABuO8u2TtWWfp3ePdkwNYW/NLr2XEDtujwV6CUnf+hCd0Xyc84Rj7p8yiUrgMP6eUZ3p3HlzFgxulE7CTysWZ5/7axaDvTHQBEMRLXXJ6kpj26o9s/5AFR3gpQi3m53Z2LCQEnel7TSzq6WN5QS58EZcyUpd49t3anc1GWTt45cOTS3rDTxZFexzcQdcRFQtZw+OC6lJUh9nfMquTobOBn2KvB3FxnILwGrgD63Qd3O55HroPE9hvXD599YcRlvMl4K7Bn537lAssXfQqslLUPenAusLpf/rWOStyr0gFDOzEPnuqS+q4beiUicbkQ1aArF4CcRSQuK5LtbwXVIPST3C/2O2K7wtqq5J5Mnex4dXXnGD2N1oZ9EfZifoJrx4zFNxB+50MZUEvgPAV/eU8A9QgAAAAAASUVORK5CYII='
    // 手动添加定位点的函数
    function movepoint(){
      x = Number(document.getElementById('x-input-start').value)
      y = Number(document.getElementById('y-input-start').value)
      console.log(typeof(x))
      var point=new BMap.Point(x,y);
      var marker=new BMap.Marker(point);
      map.addOverlay(marker);

因为导航定位的功能主要用于机器人等,因此采用百度地图demo中的驾车模式
程序如下

 // 模拟导航功能
    var myP1 = new BMap.Point(document.getElementById('x-input-start').value,document.getElementById('y-input-start').value);    //起点
	var myP2 = new BMap.Point(document.getElementById('x-input-end').value,document.getElementById('y-input-end').value);    //终点
    
    var myIcon = new BMap.Icon(fly, new BMap.Size(48, 48), { anchor: new BMap.Size(24, 24) })
    function route(){
        myP1 = new BMap.Point(document.getElementById('x-input-start').value,document.getElementById('y-input-start').value);    //起点
	    myP2 = new BMap.Point(document.getElementById('x-input-end').value,document.getElementById('y-input-end').value);    //终点
        var driving2 = new BMap.DrivingRoute(map, {renderOptions:{map: map, autoViewport: true}});    //驾车实例
	    driving2.search(myP1, myP2);    //显示一条公交线路
    }
	
	window.run = function (){
		var driving = new BMap.DrivingRoute(map);    //驾车实例
		driving.search(myP1, myP2);
		driving.setSearchCompleteCallback(function(){
			var pts = driving.getResults().getPlan(0).getRoute(0).getPath();    //通过驾车实例,获得一系列点的数组
			var paths = pts.length;    //获得有几个点

			var carMk = new BMap.Marker(pts[0],{icon:myIcon});
			map.addOverlay(carMk);
			i=0;
			function resetMkPoint(i){
				carMk.setPosition(pts[i]);
				if(i < paths){
					setTimeout(function(){
						i++;
						resetMkPoint(i);
					},200);
				}
			}
			setTimeout(function(){
				resetMkPoint(5);
			},1000)

		});
	}

确定起点和终点之后,会获得一系列的数组,获得一系列的点,进行路径轨迹的描绘。

三、最终的效果

在这里插入图片描述

  • 4
    点赞
  • 115
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值