一、前言
上一篇文章寫道拿到路徑規划的軌跡點坐標集合,並且已經可以通過調用addPolyline繪制線段的形式將軌跡點繪制,接下來就是要如何動態的繪制這些軌跡點,最簡單的方法就是開個定時器挨個取出下一個經緯度坐標,然后調用封裝好的moveMarker函數將經緯度坐標傳過去,異步交互使得標注點在移動,也可將將定時器的方法寫在網頁的js函數中,但是就不夠靈活了,還不如直接用Qt的定時器進行控制更方便一些,這里要特別注意的是軌跡點坐標傳過來的是數組的數組,因為很可能是多段線條組成,所以需要循環取出來。
近期在做機器人監控平台,需要動態顯示機器人走過的軌跡,同時還需要在地圖上選點來規划路徑,然后將得到的路徑的軌跡點坐標信息發送給機器人,讓機器人自己根據收到的經緯度坐標自行移動,有時候查詢到的軌跡點坐標集合數量非常多,受限於機器人本身的內存空間,要求能夠設定過濾機制來過濾軌跡點,然后將過濾后的軌跡點信息下發,比如設定只需要50個點,而接收到的有300個點,則自動取平均值進行過濾,比如每隔6個點取一個點坐標,然后首尾坐標必須在,這樣就保證了整條路徑的完整性。
設置標注點產生動態軌跡效果有兩種辦法,一種是直接全部清空標注點,將新的標注點帶經緯度坐標位置,重新加載,也就是說先調用deleteMarker函數清空所有標注點,然后調用addMarker函數挨個設置新的標注點;還有一種方法是傳入移動的標注點的標識信息,讓js函數自動查找到以后執行setPosition方法即可;在標注點很少比如就一個的情況下,兩種辦法效率幾乎一樣,多了的清空下,方法二是最佳選擇,沒必要重復的刪除又添加,就讓存在的標注點對象自己setPosition好了。
- 第一步:動態選取起始點和結束點經緯度坐標。
- 第二步:選擇路徑規划的方式(步行、公交、駕車、騎行)。
- 第三步:選擇路徑規划的策略(最少時間、最短距離等)。
- 第四步:單擊查詢,接收傳過來的路徑點坐標集合存起來。
- 第五步:開啟定時器,挨個取出下一個經緯度坐標,設置標注點的坐標。
- 第六步:直到經緯度坐標取完,停止定時器。
二、功能特點
- 同時支持在線地圖和離線地圖兩種模式。
- 同時支持webkit內核、webengine內核、miniblink內核、IE內核。
- 支持設置多個標注點,信息包括名稱、地址、經緯度。
- 可設置地圖是否可單擊、拖動、鼠標滾輪縮放。
- 可設置協議版本、秘鑰、主題樣式、中心坐標、中心城市、地理編碼位置等。
- 可設置地圖縮放比例和級別,縮略圖、比例尺、路況信息等控件的可見。
- 支持地圖交互,比如鼠標按下獲取對應位置的經緯度。
- 支持查詢路線,可設置起點位置、終點位置、路線模式、路線方式、路線方案(最少時間、最少換乘、最少步行、不乘地鐵、最短距離、避開高速)。
- 可顯示點線面工具,可直接在地圖上划線、點、矩形、圓形等。
- 可設置行政區划,指定某個城市區域繪制圖層,在線地圖自動輸出行政區划邊界點集合到js文件給離線地圖使用。
- 可靜態或者動態添加多個覆蓋物。支持點、折線、多邊形、矩形、圓形、弧線、點聚合等。
- 提供函數接口處理經緯度解析成地址和地址解析成經緯度坐標。
- 提供的demo直接可以單獨選點執行對應的處理比如路線查詢。
- 可以拿到路線查詢到的點坐標信息集合,比如用於機器人坐標導航等。
- 封裝了豐富的函數比如刪除指定點和所有點,刪除指定覆蓋物和所有覆蓋物等。
- 標注點彈框信息可以自定義內容,標准html格式。
- 標注點單擊事件可選 0-不處理 1-自己彈框 2-發送信號。
- 標注點可設置動畫效果 0-不處理 1-跳動 2-墜落
- 標注點可設置本地圖片文件等。
- 函數接口友好和統一,使用簡單方便,就一個類。
- 支持js動態交互添加點、刪除點、清空點、重置點,不需要刷新頁面。
- 支持任意Qt版本、任意系統、任意編譯器。
三、體驗地址
- 體驗地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取碼:o05q 文件名:bin_map.zip
- 國內站點:https://gitee.com/feiyangqingyun
- 國際站點:https://github.com/feiyangqingyun
- 個人主頁:https://blog.csdn.net/feiyangqingyun
- 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/
四、效果圖
五、相關代碼
void MapBaiDu::moveMarker(QStringList &list)
{
//動態移動點
list << QString(" function moveMarker(name, point) {");
list << QString(" if (name.length == 0) {");
list << QString(" return;");
list << QString(" }");
list << QString(" var allOverlay = map.getOverlays();");
list << QString(" var len = allOverlay.length;");
list << QString(" for (var i = 0; i < len; i++) {");
list << QString(" var overlay = allOverlay[i];");
//過濾只需要標注點 Marker 的圖層覆蓋物
list << QString(" if (overlay.toString() != '[object Marker]') {");
list << QString(" continue;");
list << QString(" }");
//過濾沒有標簽的標注點
list << QString(" var label = overlay.getLabel();");
list << QString(" if (label == null) {");
list << QString(" continue;");
list << QString(" }");
list << QString(" if (label.content == name) {");
list << QString(" var list = point.split(',');");
list << QString(" var pot = new BMap.Point(list[0], list[1]);");
list << QString(" var marker = allOverlay[i];");
list << QString(" marker.setPosition(pot);");
list << QString(" break;");
list << QString(" }");
list << QString(" }");
list << QString(" }");
}
void frmMapRoute::on_btnSearchRoute_clicked()
{
QString startAddr = ui->txtStartAddr->text().trimmed();
QString endAddr = ui->txtEndAddr->text().trimmed();
if (startAddr.isEmpty() || endAddr.isEmpty()) {
QUIHelper::showMessageBoxError("起點和終點地址不能為空,請重新填寫!");
return;
}
baidu->setRotueInfo(ui->cboxRouteType->currentIndex(), ui->cboxPolicyType->currentIndex(), startAddr, endAddr);
map->loadMap();
lastPoint = App::MapCenter;
ui->rbtnStartAddr->setChecked(true);
ui->tabWidgetRoute->setCurrentIndex(0);
}
void frmMapRoute::on_btnDrawRoute_clicked()
{
#ifdef webkit
QUIHelper::showMessageBoxError("webkit不支持數組的數據形式返回!");
return;
#endif
if (routeDatas.count() == 0) {
QUIHelper::showMessageBoxError("請先單擊查詢路線獲取路線的坐標點集合!");
return;
}
//將收到的路徑點集合分線段繪制
foreach (QStringList data, routeDatas) {
QString points = data.join("|");
QString js = QString("addPolyline('%1')").arg(points);
map->runJs(js);
}
}
void frmMapRoute::on_btnCheckData_clicked()
{
//第一步:計算總數,求平均值=實際總數/預期總數+1,預期總數>=實際總數則不用處理
int countSrc = ui->listWidgetSrc->count();
int countTarget = ui->txtPointCount->text().trimmed().toInt();
if (countTarget >= countSrc) {
return;
}
//第二步:根據平均值挨個取出值
QStringList points;
int avg = countSrc / countTarget + 1;
for (int i = 0; i < countSrc; i += avg) {
QString point = ui->listWidgetSrc->item(i)->data(Qt::UserRole).toString();
points << point;
}
//必須加上末尾這個作為結束,如果剛好除盡則不用
QString point = ui->listWidgetSrc->item(countSrc - 1)->data(Qt::UserRole).toString();
if (points.last() != point) {
points << point;
}
//第三步:將數據重新填入最終數據列表
ui->listWidgetTarget->clear();
for (int i = 0; i < points.count(); ++i) {
QString point = points.at(i);
addItem(ui->listWidgetTarget, i, point);
}
ui->tabWidgetRoute->setCurrentIndex(1);
qDebug() << TIMEMS << avg << points.count() << points;
}
void frmMapRoute::on_btnMapWeb_clicked()
{
//如果是https開頭則需要在運行的時候帶上openssl的庫
map->loadMap("https://map.baidu.com/");
}
void frmMapRoute::moveMarker()
{
QString name = "軌跡點";
QListWidget *listWidget = ui->listWidgetSrc;
if (!ui->listWidgetSrc->isVisible()) {
listWidget = ui->listWidgetTarget;
}
int row = listWidget->currentRow();
if (row >= 0 && row < listWidget->count()) {
QString point = listWidget->currentItem()->data(Qt::UserRole).toString();
listWidget->setCurrentRow(row + 1);
QString js = QString("moveMarker('%1', '%2')").arg(name).arg(point);
map->runJs(js);
} else {
on_btnTestData_clicked();
}
}
void frmMapRoute::on_btnTestData_clicked()
{
QString name = "軌跡點";
QListWidget *listWidget = ui->listWidgetSrc;
if (!ui->listWidgetSrc->isVisible()) {
listWidget = ui->listWidgetTarget;
}
if (ui->btnTestData->text() == "模擬軌跡") {
//自定義圖標
QString iconfile = "./ipc_robot2.png";
int iconsize = 50;
QString js = QString("addMarker('%1', '', '', '', 60, '%1', 0, 0, '%2', %3)")
.arg(name).arg(iconfile).arg(iconsize);
map->runJs(js);
listWidget->setCurrentRow(0);
ui->tabWidgetRoute->setEnabled(false);
ui->btnTestData->setText("停止模擬");
timer->start(100);
moveMarker();
} else {
QString js = QString("deleteMarker('%1')").arg(name);
map->runJs(js);
timer->stop();
ui->tabWidgetRoute->setEnabled(true);
ui->btnTestData->setText("模擬軌跡");
}
}