Qt編寫地圖實(shí)現(xiàn)實(shí)時(shí)動(dòng)態(tài)軌跡效果
一、前言
實(shí)時(shí)動(dòng)態(tài)軌跡經(jīng)歷過很多個(gè)版本的迭代,此功能最初是一個(gè)客戶定制的,主要是需要在地圖上動(dòng)態(tài)顯示GPS的運(yùn)動(dòng)軌跡,有個(gè)應(yīng)用場景就是一個(gè)帶有監(jiān)控的車子,實(shí)時(shí)在運(yùn)動(dòng)中,后臺可以接收到經(jīng)緯度信息,需要繪制對應(yīng)的軌跡,相當(dāng)于這些攝像機(jī)點(diǎn)位是動(dòng)態(tài)移動(dòng)的,這樣就可以觀測到攝像機(jī)的實(shí)時(shí)位置信息,雙擊攝像機(jī)還可以彈出畫面實(shí)時(shí)預(yù)覽,很直觀。
GPS運(yùn)動(dòng)軌跡這個(gè)功能,也需要用到j(luò)s的知識,其實(shí)就是封裝一個(gè)js函數(shù),繪制對應(yīng)的線條路徑,這個(gè)軌跡點(diǎn)可能包括的信息有經(jīng)度、緯度、速度、時(shí)間、是否標(biāo)記、時(shí)間等信息,寫個(gè)結(jié)構(gòu)體封裝下,方便后期拓展,是否標(biāo)記的含義是是否改點(diǎn)同時(shí)作為一個(gè)設(shè)備點(diǎn)添加,分段線的含義。
后面陸續(xù)增加了可以設(shè)置旋轉(zhuǎn)角度、可以過濾坐標(biāo)點(diǎn)這兩個(gè)要點(diǎn),設(shè)置旋轉(zhuǎn)角度采用的是內(nèi)置的setRotation函數(shù),流程是先從一堆覆蓋物中通過唯一標(biāo)識比如name找到當(dāng)前要移動(dòng)的點(diǎn),然后對這個(gè)標(biāo)注點(diǎn)調(diào)用setRotation設(shè)置要旋轉(zhuǎn)的角度值,所以這里衍生了另外一個(gè)需求,如何計(jì)算兩個(gè)點(diǎn)之間的旋轉(zhuǎn)角度值,這個(gè)值必須是提前計(jì)算好的,這就要用到數(shù)學(xué)知識了,用atan2來計(jì)算,同時(shí)做矯正。
二、功能特點(diǎn)
定時(shí)器排隊(duì)下載省市輪廓圖點(diǎn)坐標(biāo)集合存儲到JS文件。
支持一個(gè)行政區(qū)域多個(gè)不規(guī)則區(qū)域下載。
自動(dòng)計(jì)算行政區(qū)域的下載輪廓數(shù)量。
可精確選擇省份、市區(qū)、縣城,也可直接輸入行政區(qū)域的名稱。
可以設(shè)置下載間隔、隨時(shí)開始下載和停止下載。
提供編輯邊界功能,可以直接在地圖上編輯好不規(guī)則區(qū)域的點(diǎn)集合,然后獲取邊界點(diǎn)集合數(shù)據(jù),這個(gè)可以用來自己繪制區(qū)域拿到數(shù)據(jù),比如某個(gè)鄉(xiāng)鎮(zhèn)甚至某個(gè)小區(qū)的行政區(qū)域數(shù)據(jù),很牛逼。
三、體驗(yàn)地址
體驗(yàn)地址:https://pan.baidu.com/s/15ZKAlptW-rDcNq8zlzdYLg 提取碼:uyes 文件名:bin_map.zip
國內(nèi)站點(diǎn):https://gitee.com/feiyangqingyun
國際站點(diǎn):https://github.com/feiyangqingyun
四、效果圖
五、相關(guān)代碼
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()) { //判斷哪里勾選了就設(shè)置到哪里 QString point = WebHelper::getLngLat2(result); //判斷哪里勾選了就設(shè)置到哪里 if (ui->rbtnStartAddr->isChecked()) { ui->txtStartAddr->setText(point); } else { ui->txtEndAddr->setText(point); } } } else if (type == "routepoints") { //將查詢路徑轉(zhuǎn)換成經(jīng)緯度坐標(biāo)點(diǎn)集合數(shù)據(jù)顯示 routeDatas.clear(); ui->tableWidgetSource->clearContents(); //可能會(huì)有多個(gè)路徑集合,目前測試下來都是一個(gè)路徑集合 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) { //找出和上一個(gè)點(diǎn)之間的角度 int angle = 0; QString point = tableWidget->item(row, 1)->data(Qt::UserRole).toString(); //第一個(gè)點(diǎn)和最后一個(gè)點(diǎn)不用處理 if (row > 0 && row < count - 1) { //上一個(gè)點(diǎn)坐標(biāo) QString point2 = tableWidget->item(row - 1, 1)->data(Qt::UserRole).toString(); //計(jì)算當(dāng)前上一個(gè)點(diǎn)和當(dāng)前點(diǎn)的旋轉(zhuǎn)角度 angle = WebHelper::getAngle(point2, point); } //執(zhí)行移動(dòng)設(shè)備點(diǎn)函數(shù),參數(shù)帶旋轉(zhuǎn)角度 QString js = QString("moveMarker('%1', '%2', %3)").arg(name).arg(point).arg(angle); runJs(js); //重新繪制軌跡點(diǎn) if (ui->cboxMoveMode->currentIndex() == 0) { //清空之前的軌跡點(diǎn) js = QString("deleteOverlay('Polyline')"); runJs(js); //取出第一個(gè)點(diǎn)到當(dāng)前焦點(diǎn)所在行的點(diǎn)組成已經(jīng)走過的軌跡點(diǎn)集合重新繪制 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); } //顯示當(dāng)前第幾個(gè)數(shù)據(jù) 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() == "模擬軌跡") { //限制最小數(shù)量 if (tableWidget->rowCount() < 2) { return; } //第一步: 添加一個(gè)標(biāo)記 name = ui->txtDeviceName->text().trimmed(); if (name.isEmpty()) { name = "馬航MH370"; } //圖片文件在可執(zhí)行文件下的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); //第二步: 移到第一個(gè)點(diǎn) tableWidget->setFocus(); tableWidget->setCurrentCell(0, 0); ui->btnTestData->setText("停止模擬"); ui->tabWidget->setTabEnabled(ui->tableWidgetSource->isVisible() ? 1 : 0, false); //第三步: 啟動(dòng)定時(shí)器并立即執(zhí)行一次 int index = ui->cboxMoveInterval->currentIndex(); timer->start(ui->cboxMoveInterval->itemData(index).toInt()); moveMarker(); } else { //清空標(biāo)記 QString js = QString("deleteMarker('%1')").arg(name); runJs(js); //停止定時(shí)器 timer->stop(); ui->btnTestData->setText("模擬軌跡"); ui->tabWidget->setTabEnabled(ui->tableWidgetSource->isVisible() ? 1 : 0, true); } } void frmMapGps::on_btnCheckData_clicked() { if (timer->isActive()) { return; } //第一步: 計(jì)算總數(shù),求平均值=實(shí)際總數(shù)/預(yù)期總數(shù)+1,預(yù)期總數(shù)>=實(shí)際總數(shù)則不用處理 int countSource = ui->tableWidgetSource->rowCount(); int countTarget = ui->txtPointCount->text().trimmed().toInt(); if (countTarget >= countSource) { QUIHelper::showMessageBoxError("目標(biāo)點(diǎn)數(shù)不能大于等于原數(shù)據(jù)點(diǎn)數(shù)!"); ui->txtPointCount->setFocus(); return; } //第二步: 根據(jù)平均值挨個(gè)取出值 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; } //必須加上末尾這個(gè)作為結(jié)束,如果剛好除盡則不用 QString point = ui->tableWidgetSource->item(countSource - 1, 1)->data(Qt::UserRole).toString(); if (points.last() != point) { points << point; } //第三步: 將數(shù)據(jù)重新填入篩選數(shù)據(jù)列表 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("請先單擊查詢路線獲取路線的坐標(biāo)點(diǎn)集合!"); return; } //清空之前的軌跡點(diǎn) runJs("deleteOverlay('Polyline')"); //將收到的路徑點(diǎn)集合分線段繪制 foreach (QStringList data, routeDatas) { QString points = data.join("|"); QString js = QString("addPolyline('%1', '#ff0000')").arg(points); runJs(js); } }
以上就是Qt編寫地圖實(shí)現(xiàn)實(shí)時(shí)動(dòng)態(tài)軌跡效果的詳細(xì)內(nèi)容,更多關(guān)于Qt地圖實(shí)時(shí)動(dòng)態(tài)軌跡的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解析C++無鎖隊(duì)列的實(shí)現(xiàn)代碼
本篇文章是對C++無鎖隊(duì)列的實(shí)現(xiàn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05VC++中HTControl控件類之CHTRichEdit富文本編輯控件實(shí)例
這篇文章主要介紹了VC++中HTControl控件類之CHTRichEdit富文本編輯控件,是一個(gè)比較實(shí)用的功能,需要的朋友可以參考下2014-08-08Qt?TCP網(wǎng)絡(luò)通信學(xué)習(xí)
用于數(shù)據(jù)傳輸?shù)牡蛯泳W(wǎng)絡(luò)協(xié)議,多個(gè)物聯(lián)網(wǎng)協(xié)議都是基于TCP協(xié)議的,這篇文章為大家介紹了Qt?TCP網(wǎng)絡(luò)通信,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08解析為何要關(guān)閉數(shù)據(jù)庫連接,可不可以不關(guān)閉的問題詳解
本篇文章是對為何要關(guān)閉數(shù)據(jù)庫連接,可不可以不關(guān)閉的問題進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++實(shí)現(xiàn)惡搞電腦關(guān)機(jī)小程序的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C++實(shí)現(xiàn)一個(gè)簡單的惡搞電腦關(guān)機(jī)小程序,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以嘗試一下2022-11-11C++基于消息隊(duì)列的多線程實(shí)現(xiàn)示例代碼
這篇文章主要給大家介紹了關(guān)于C++基于消息隊(duì)列的多線程實(shí)現(xiàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04