首页 > 其他分享 >Qt连接GPS模块实现调用高德地图交互显示经纬度信息的功能

Qt连接GPS模块实现调用高德地图交互显示经纬度信息的功能

时间:2024-07-28 09:25:59浏览次数:16  
标签:ui Qt MainWindow myCom1 QString 串口 new 高德 GPS

最近需要设计一个qt界面,在里面通过串口接收并解析出gps模块的经纬度信息,再通过调用高德地图的api进行地图的显示,作为入门qt的项目踩了不少坑,特将这个项目开源出来,供初学者参考学习。项目链接放下边啦,欢迎大家star~

https://github.com/daviddou2023/qt_screen_gps

一. 项目简介

本项目主要实现了电脑和L76K模块连接,模块连接天线把接收到gps数据从串口传给电脑,设计qt界面实现串口数据的接收和解析,并调用高德地图的api进行地图的3D显示。

1. 项目目录

图1 项目目录

2. 项目环境准备

(1)硬件准备

L76K GPS模块
USB转ttl模块

(2)软件准备

笔者采用的是Qt 5.15.2环境,因为需要用到Qt WebEngine 所以采用Desktop Qt 5.15.2 MVSC2019 64bit编译器,具体配置可参考笔者的另一篇文章https://blog.csdn.net/daviddou2022/article/details/140703079

Qt编译器配置

3. 效果展示

可以看到打开串口后可以解析并显示出接收到的经纬度信息,并调用高德api进行地图显示(上图是2D显示),最终实现了3D显示。 

 

 

二. 各部分代码介绍

1. project文件

QT       += core gui widgets serialport webenginewidgets

QMAKE_PROJECT_DEPTH = 0
msvc {
    QMAKE_CFLAGS += /utf-8
    QMAKE_CXXFLAGS += /utf-8
}

CONFIG += c++17

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

这里需要注意的是因为使用到了Qt WebEngine所以需要加入以下一段代码

QT       += core gui widgets serialport webenginewidgets

QMAKE_PROJECT_DEPTH = 0
msvc {
    QMAKE_CFLAGS += /utf-8
    QMAKE_CXXFLAGS += /utf-8
}

具体踩坑过程可以参考笔者上一篇https://blog.csdn.net/daviddou2022/article/details/140703079 说多了都是泪啊

2. mainwindow.h文件

一些函数的声明和库的引用

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSerialPort>
#include <QTimer>
#include <QWebEngineView>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_openMyCom1_clicked(); // 打开串口按钮的槽函数
    void on_closeMyCom1_clicked(); // 关闭串口按钮的槽函数
    void readMyCom1(); // 读取串口数据的槽函数

private:
    void Com1Init(); // 初始化串口
    void sendInitializationCommand(); // 发送初始化指令
    void clear_gps(); // 清除GPS显示数据
    void parseGPSData(const QString &data); // 解析GPS数据
    void processBuffer(); // 处理缓冲区数据

    Ui::MainWindow *ui;
    QSerialPort *myCom1; // 串口对象
    bool isComOpen1; // 串口是否打开的标志
    QTimer *readTimer; // 定时器对象
    QWebEngineView *mapView; // 地图视图对象
    QString buffer; // 缓冲区用于处理串口数据
};

#endif // MAINWINDOW_H

3. mainwindow.cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QSerialPort>
#include <QStringList>
#include <QTimer>
#include <QWebEngineView>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QNetworkProxyFactory>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , myCom1(nullptr)
    , isComOpen1(false)
{
    ui->setupUi(this);

    // 关闭系统代理配置
    QNetworkProxyFactory::setUseSystemConfiguration(false);

    // 创建一个定时器,用于定期读取串口数据
    readTimer = new QTimer(this);
    connect(readTimer, &QTimer::timeout, this, &MainWindow::readMyCom1);

    // 设置UI布局
    QWidget *centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);

    QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);

    // 创建地图显示区域
    mapView = new QWebEngineView(this);
    QString mapHtml = R"(
    <div id="container" style="width:100%;height:100%;"></div>
    <div class="input-card">
        <h4>左击获取经纬度:</h4>
        <div class="input-item">
          <input type="text" readonly="true" id="lnglat">
        </div>
    </div>
    <script type="text/javascript">
      window._AMapSecurityConfig = {
        securityJsCode: "c9e3585d7ef259a3b71826aafccafeec",
      };
    </script>
    <script
      type="text/javascript"
      src="https://webapi.amap.com/maps?v=2.0&key=dca75fbd0390e9266a7f39814e1197c2"
    ></script>
    <script type="text/javascript">
      var map = new AMap.Map('container', {
        rotateEnable: true,
        resizeEnable: true,
        pitchEnable: true,
        zoom: 17,
        pitch: 50,
        rotation: -15,
        viewMode: '3D',
        zooms: [2, 20],
        center: [104.10113, 30.675639],
        layers: [
            new AMap.TileLayer()
        ]
      });

      // 添加 3D 罗盘控制
      AMap.plugin(['AMap.ControlBar'], function() {
        map.addControl(new AMap.ControlBar());
      });

      // 添加点击事件获取经纬度
      map.on('click', function(e) {
        document.getElementById("lnglat").value = e.lnglat.getLng() + ',' + e.lnglat.getLat();
      });

      // 添加卫星图层
      var satelliteLayer = new AMap.TileLayer.Satellite();
      satelliteLayer.setMap(map);

      // 添加实时路况图层
      var trafficLayer = new AMap.TileLayer.Traffic({zIndex: 10});
      trafficLayer.setMap(map);

      // 添加楼块图层
      var buildingsLayer = new AMap.Buildings({
        zooms: [16, 18],
        zIndex: 10,
        heightFactor: 2
      });
      map.add(buildingsLayer);

      // 添加卫星路网图层
      map.addLayer(new AMap.TileLayer.Satellite());
      map.addLayer(new AMap.TileLayer.RoadNet());

      // 添加楼块图层
      map.setMapStyle('amap://styles/light');
      map.addLayer(buildingsLayer);

      var marker = null;
      var infoWindow = null;

      function updateMap(lat, lon) {
        var position = new AMap.LngLat(lon, lat);
        map.setCenter(position);
        map.setZoom(17);

        if (marker) {
          marker.setPosition(position);
        } else {
          marker = new AMap.Marker({
            position: position,
            map: map,
            icon: new AMap.Icon({
              image: 'http://webapi.amap.com/theme/v1.3/markers/n/mark_b.png', // 使用红色标记图标
              size: new AMap.Size(24, 36) // 设置图标大小
            })
          });
        }

        if (infoWindow) {
          infoWindow.setPosition(position);
        } else {
          infoWindow = new AMap.InfoWindow({
            content: "<div style='color: red;'>塔吊1</div>", // 红色文字
            position: position
          });
          infoWindow.open(map, marker.getPosition());
        }
      }
    </script>
    )";
    mapView->setHtml(mapHtml);
    mainLayout->addWidget(mapView, 1);

    // 创建右侧控制区域
    QVBoxLayout *controlLayout = new QVBoxLayout();

    // 添加打开串口按钮
    QPushButton *openButton = new QPushButton("打开串口", this);
    connect(openButton, &QPushButton::clicked, this, &MainWindow::on_openMyCom1_clicked);
    controlLayout->addWidget(openButton);

    // 添加关闭串口按钮
    QPushButton *closeButton = new QPushButton("关闭串口", this);
    closeButton->setEnabled(false); // 默认关闭串口按钮不可用
    connect(closeButton, &QPushButton::clicked, this, &MainWindow::on_closeMyCom1_clicked);
    controlLayout->addWidget(closeButton);

    // 添加经纬度显示标签
    ui->latitudeLabel = new QLabel("纬度: ", this);
    ui->longitudeLabel = new QLabel("经度: ", this);
    controlLayout->addWidget(ui->latitudeLabel);
    controlLayout->addWidget(ui->longitudeLabel);

    mainLayout->addLayout(controlLayout);

    ui->openMyCom1 = openButton;
    ui->closeMyCom1 = closeButton;
}

MainWindow::~MainWindow()
{
    if (myCom1) {
        if (myCom1->isOpen()) {
            myCom1->close();
        }
        delete myCom1;
    }
    delete ui;
}

// 打开串口并读取北斗数据
void MainWindow::on_openMyCom1_clicked()
{
    ui->openMyCom1->setEnabled(false);
    ui->closeMyCom1->setEnabled(true);

    Com1Init(); // 初始化串口
    if (isComOpen1) {
        sendInitializationCommand(); // 发送初始化指令
        readTimer->start(1000); // 启动定时器,每秒读取一次串口数据
    } else {
        ui->latitudeLabel->setText("串口打开失败:" + myCom1->errorString());
    }
}

// 关闭串口
void MainWindow::on_closeMyCom1_clicked()
{
    if (myCom1) {
        if (myCom1->isOpen()) {
            myCom1->close();
        }
        delete myCom1;
        myCom1 = nullptr;
        isComOpen1 = false;
        readTimer->stop();
        clear_gps();
        ui->openMyCom1->setEnabled(true);
        ui->closeMyCom1->setEnabled(false);
    }
}

// 初始化串口
void MainWindow::Com1Init()
{
    if (myCom1) {
        delete myCom1;
    }

    QString portName1 = "COM12"; // 获取串口名
    myCom1 = new QSerialPort(portName1);

    if (!myCom1->open(QIODevice::ReadWrite)) {
        qDebug() << "串口打开失败:" << myCom1->errorString();
        ui->latitudeLabel->setText("串口打开失败:" + myCom1->errorString());
        delete myCom1;
        myCom1 = nullptr;
        isComOpen1 = false;
        return;
    }

    isComOpen1 = true;
    myCom1->setBaudRate(QSerialPort::Baud9600);
    myCom1->setDataBits(QSerialPort::Data8);
    myCom1->setParity(QSerialPort::NoParity);
    myCom1->setStopBits(QSerialPort::OneStop);
    myCom1->setFlowControl(QSerialPort::NoFlowControl);
    qDebug() << "串口打开成功";
}

// 发送初始化指令
void MainWindow::sendInitializationCommand()
{
    if (myCom1 && myCom1->isOpen()) {
        QString command = "$PCAS03,0,0,0,0,1,0,0,0,0,0,,,0,0*03\r\n";
        myCom1->write(command.toUtf8());
    }
}

// 读取串口数据
void MainWindow::readMyCom1()
{
    if (myCom1 && myCom1->isOpen()) {
        QByteArray temp1 = myCom1->readAll();
        if (!temp1.isEmpty()) {
            QString dataStr = QString::fromStdString(temp1.toStdString());
            buffer += dataStr;
            processBuffer();
        }
    } else {
        qDebug() << "尝试读取数据时,串口未打开";
    }
}

// 处理缓冲区中的数据
void MainWindow::processBuffer()
{
    int startIndex = buffer.indexOf("$GNRMC");
    if (startIndex == -1) {
        return;
    }

    int endIndex = buffer.indexOf("\r\n", startIndex);
    if (endIndex == -1) {
        return;
    }

    QString gpsData = buffer.mid(startIndex, endIndex - startIndex);
    buffer.remove(0, endIndex + 2);
    parseGPSData(gpsData);
}

// 解析GPS数据并显示经纬度
void MainWindow::parseGPSData(const QString &data)
{
    if (data.startsWith("$GNRMC")) {
        QStringList dataList = data.split(",");
        if (dataList.size() > 6) {
            QString latitude = dataList[3];
            QString latitudeDirection = dataList[4];
            QString longitude = dataList[5];
            QString longitudeDirection = dataList[6];

            double lat = latitude.mid(0, 2).toDouble() + (latitude.mid(2).toDouble() / 60.0);
            if (latitudeDirection == "S") lat = -lat;

            double lon = longitude.mid(0, 3).toDouble() + (longitude.mid(3).toDouble() / 60.0);
            if (longitudeDirection == "W") lon = -lon;

            qDebug() << "纬度: " << lat << ", 经度: " << lon;

            ui->latitudeLabel->setText("纬度: " + QString::number(lat, 'f', 6));
            ui->longitudeLabel->setText("经度: " + QString::number(lon, 'f', 6));

            QString jsCode = QString("updateMap(%1, %2);").arg(lat).arg(lon);
            mapView->page()->runJavaScript(jsCode);
        }
    }
}

// 清除GPS显示数据
void MainWindow::clear_gps()
{
    ui->latitudeLabel->clear();
    ui->longitudeLabel->clear();
}

(1)高德地图的api调用

图2 高德地图申请key

 

 

图3 申请完成之后

然后结合官方文档可以实现api的调用https://lbs.amap.com/api/javascript-api-v2/guide/abc/jscode

图4 高德api调用文档示例

(2)高德地图的花花设置

本例程主要参考了https://gitee.com/alon-1787/qt_-gao-de_-map_1

采用了3D视图并增加了3D罗盘和地图标注的功能,一些其他的功能读者可以自行查阅,网上应该很多例子。

(3)GPS数据的解析

本示例采用的L76K模块,根据相关手册可知如果不设置串口,则接收到所有的信息

图5 串口不设置后输出结果

但是本例程只需要经纬度数据,所以先给串口发送 $PCAS03,0,0,0,0,1,0,0,0,0,0,,,0,0*03指令让其只打印出经纬度信息

图6 串口设置后输出结果

通过观察输出的数据格式我们进行解析,具体的解析方法如下

// 解析GPS数据并显示经纬度
void MainWindow::parseGPSData(const QString &data)
{
    if (data.startsWith("$GNRMC")) {
        QStringList dataList = data.split(",");
        if (dataList.size() > 6) {
            QString latitude = dataList[3];
            QString latitudeDirection = dataList[4];
            QString longitude = dataList[5];
            QString longitudeDirection = dataList[6];

            double lat = latitude.mid(0, 2).toDouble() + (latitude.mid(2).toDouble() / 60.0);
            if (latitudeDirection == "S") lat = -lat;

            double lon = longitude.mid(0, 3).toDouble() + (longitude.mid(3).toDouble() / 60.0);
            if (longitudeDirection == "W") lon = -lon;

            qDebug() << "纬度: " << lat << ", 经度: " << lon;

            ui->latitudeLabel->setText("纬度: " + QString::number(lat, 'f', 6));
            ui->longitudeLabel->setText("经度: " + QString::number(lon, 'f', 6));

            QString jsCode = QString("updateMap(%1, %2);").arg(lat).arg(lon);
            mapView->page()->runJavaScript(jsCode);
        }
    }
}

(4)一个小tip

对了捏有一个小经验想分享给大家,刚开始写完代码运行发现高德地图特别卡,就是不紧不慢到那种老一辈艺术家的淡定与从容,然后我开始以为是因为代码问题,发现代码没啥问题啊,我就查了一下。

刚开始有说关掉系统代理的https://blog.csdn.net/weixin_30598225/article/details/97578556

QNetworkProxyFactory::setUseSystemConfiguration(false);

但是我试了不行唉,然后接着查,终于找到了一篇救我狗命的文章https://blog.csdn.net/weixin_43196399/article/details/129089971 

只需要改一下左下角的debug版本成release版本,直接答辩通畅了

 

 

4. mainwindow.ui文件 

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QHBoxLayout" name="horizontalLayout">
    <item>
     <widget class="QWebEngineView" name="mapView" native="true">
      <property name="minimumSize">
       <size>
        <width>400</width>
        <height>0</height>
       </size>
      </property>
     </widget>
    </item>
    <item>
     <layout class="QVBoxLayout" name="verticalLayout">
      <item>
       <widget class="QPushButton" name="openMyCom1">
        <property name="text">
         <string>打开串口</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QPushButton" name="closeMyCom1">
        <property name="enabled">
         <bool>false</bool>
        </property>
        <property name="text">
         <string>关闭串口</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QLabel" name="latitudeLabel">
        <property name="text">
         <string>纬度: </string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QLabel" name="longitudeLabel">
        <property name="text">
         <string>经度: </string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>25</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <customwidgets>
  <customwidget>
   <class>QWebEngineView</class>
   <extends>QWidget</extends>
   <header>qwebengineview.h</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>
图6 mainwindow.ui界面展示

三. 写在最后

本例程其实只是笔者最近项目的一个子界面,最终的项目是要实现一个可视化大屏应用,后续项目完成之后的代码笔者也会陆续开源出来,供大家参考学习,也欢迎大家留言交流~

 

 

标签:ui,Qt,MainWindow,myCom1,QString,串口,new,高德,GPS
From: https://blog.csdn.net/daviddou2022/article/details/140741906

相关文章

  • QT实现windows窗口内嵌
    appQSharedMemoryshared("appID");//attach成功表示已经存在该内存了,表示当前存在实例if(shared.attach())//共享内存被占用则直接返回return0;MainWindoww;w.show();qulonglongwinID=(qulonglong)w.winId();shared.cr......
  • Qt+OpenCascade开发笔记(一):occ的windows开发环境搭建(一):OpenCascade介绍、下载和安装过
    若该文为原创文章,转载请注明原文出处本文章博客地址:https://hpzwl.blog.csdn.net/article/details/140604141长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…Qt开发专栏:三方......
  • 在 Python 中获取精确的 Android GPS 位置
    我尝试在Python中获取Android手机的GPS位置(使用QPython3应用程序)。这种可行,但是Android中似乎有几个LocationProvider:gps:纯gps定位,速度慢,耗能,但非常准确,正是我所需要的。网络:GPS和wifi/小区定位的混合,更快,但不太准确被动......
  • Ubuntu14.04QT程序开机自启动(转)
     按语:    linux应用程序设为开机自启动,可修改/etc/rc.local中添加启动代码,但QT应用程序无法自动启动,后参考此文,添加应用(原来做好的desktop文件),成功。1.运行已经编辑好的QT程序,run、debug,生成类似于build-qtplot-Desktop-Debug的文件,当然程序在编译时会显示该debu......
  • QT mainwindow UI界面添加工具栏
    1.在mainwindowUI设计器界面右上角右键mainwindow 弹出如下菜单图1可以看到添加工具栏,移除状态栏等相关操作都在菜单中2.新建action相关菜单项图2在红框中的ActionEdit中,第一行菜单栏按钮(分别是新建,复制,粘贴,删除,修改)点击以进行创建鼠......
  • ubuntu系统中安装PyQt5,并在Pycharm中配置
    本文详细讲述了在Ubuntu22.04中安装PyQt5的过程,并在Pycharm中导入了两个PyQt5的模块。第一步:打开终端,输入以下指令下载pyqt5:sudoaptinstallpyqt5* 输入“y”继续…第二步:输入以下指令,安装pyqt5相应的工具:并输入“y”继续:sudoaptinstallqttools5-dev-tool......
  • QT网络编程(二)——TCP协议工作原理及实战
    目录引言一、TCP协议基础知识1.TCP协议特点2.TCP连接的三个阶段3.三次握手和四次挥手二、Qt中的TCP编程1.引入Qt网络模块2.QTcpServer类常用函数3.QTcpSocket类常用函数三、TCP网络通信流程TCP服务器TCP客户端四、实战示例UI界面核心代码运行结果......
  • vs2022 QT Opencv用到的一些代码
     MyFirstQT.cpp#include"MyFirstQT.h"#include"ui_MyFirstQT.h"#include<QFileDialog>#include<QMessageBox>#include<QPixmap>#include<opencv2/opencv.hpp>#include<QDebug>#include<opencv2/imgp......
  • Qt自定义控件
    开发系统:ubuntu22.04IDE:clion构建工具:cmakeQt自定义控件之插件形式插件形式是指将自定义控件按照一定的规则,生成动态库,放到Qtdesigner插件加载目录/usr/lib/x86_64-linux-gnu/qt5/plugins/designer下,Qtdesigner启动时加载,自定义控件就像内置控件一样可以直接拖拽。下面......
  • qt 连接扫码枪,检查串口插拔
    boolMainWindow::nativeEvent(constQByteArray&eventType,void*message,long*result){Q_UNUSED(result);Q_UNUSED(eventType);MSG*pMsg=reinterpret_cast<MSG*>(message);if(pMsg->message==WM_KEYUP){ushort......