Qt編寫地圖實現實時動態軌跡效果

一、前言

實時動態軌跡經歷過很多個版本的迭代,此功能最初是一個客戶定制的,主要是需要在地圖上動態顯示GPS的運動軌跡,有個應用場景就是一個帶有監控的車子,實時在運動中,後臺可以接收到經緯度信息,需要繪制對應的軌跡,相當於這些攝像機點位是動態移動的,這樣就可以觀測到攝像機的實時位置信息,雙擊攝像機還可以彈出畫面實時預覽,很直觀。

GPS運動軌跡這個功能,也需要用到js的知識,其實就是封裝一個js函數,繪制對應的線條路徑,這個軌跡點可能包括的信息有經度、緯度、速度、時間、是否標記、時間等信息,寫個結構體封裝下,方便後期拓展,是否標記的含義是是否改點同時作為一個設備點添加,分段線的含義。

後面陸續增加瞭可以設置旋轉角度、可以過濾坐標點這兩個要點,設置旋轉角度采用的是內置的setRotation函數,流程是先從一堆覆蓋物中通過唯一標識比如name找到當前要移動的點,然後對這個標註點調用setRotation設置要旋轉的角度值,所以這裡衍生瞭另外一個需求,如何計算兩個點之間的旋轉角度值,這個值必須是提前計算好的,這就要用到數學知識瞭,用atan2來計算,同時做矯正。

二、功能特點

定時器排隊下載省市輪廓圖點坐標集合存儲到JS文件。

支持一個行政區域多個不規則區域下載。

自動計算行政區域的下載輪廓數量。

可精確選擇省份、市區、縣城,也可直接輸入行政區域的名稱。

可以設置下載間隔、隨時開始下載和停止下載。

提供編輯邊界功能,可以直接在地圖上編輯好不規則區域的點集合,然後獲取邊界點集合數據,這個可以用來自己繪制區域拿到數據,比如某個鄉鎮甚至某個小區的行政區域數據,很牛逼。

三、體驗地址

體驗地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取碼:o05q 文件名:bin_map.zip

國內站點:https://gitee.com/feiyangqingyun

國際站點:https://github.com/feiyangqingyun

四、效果圖

五、相關代碼

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地圖實時動態軌跡的資料請關註WalkonNet其它相關文章!

推薦閱讀: