1, Foreword
The real-time dynamic trajectory has undergone many versions of iteration. This function was originally customized by customers. It mainly needs to dynamically display the motion trajectory of GPS on the map. One application scenario is a car with monitoring. In real time, the longitude and latitude information can be received in the background, and the corresponding trajectory needs to be drawn, which is equivalent to that these camera points move dynamically, In this way, the real-time position information of the camera can be observed. Double clicking the camera can also pop up the real-time preview of the picture, which is very intuitive.
The function of GPS trajectory also needs the knowledge of js. In fact, it encapsulates a js function and draws the corresponding line path. The information of this trajectory point may include longitude, latitude, speed, time, whether to mark, time and other information. It is written in a structure package to facilitate later expansion. Whether to mark means whether to change the point and add it as a device point at the same time, Meaning of segment line.
The two key points of setting the rotation angle and filtering the coordinate points are successively added later. The built-in setRotation function is used to set the rotation angle. The process is to first find the current point to be moved from a pile of covers through a unique identifier, such as name, and then call setRotation to set the angle value to be rotated for this marker point. Therefore, another requirement is derived here, How to calculate the rotation angle between two points? This value must be calculated in advance. This requires mathematical knowledge. atan2 is used to calculate and correct it at the same time.
2, Functional features
- The timer queues up to download the point coordinate set of the provincial and municipal outline map and stores it in the JS file.
- It supports downloading from one administrative region to multiple irregular regions.
- Automatically calculate the number of downloaded profiles in the administrative region.
- You can accurately select provinces, urban areas and counties, or directly enter the name of administrative regions.
- You can set the download interval, start and stop downloading at any time.
- It provides the function of editing the boundary. You can directly edit the point set of irregular areas on the map, and then obtain the boundary point set data. This can be used to draw the area and get the data, such as the administrative area data of a township or even a community. It is very awesome.
3, Experience address
- Experience address: https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A Extraction code: o05q file name: bin_map.zip
- Domestic sites: https://gitee.com/feiyangqingyun
- International sites: https://github.com/feiyangqingyun
- Personal homepage: https://blog.csdn.net/feiyangqingyun
- Zhihu homepage: https://www.zhihu.com/people/feiyangqingyun/
4, Renderings
5, Related code
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()) { //Judge where it is checked and set where it is QString point = WebHelper::getLngLat2(result); //Judge where it is checked and set where it is if (ui->rbtnStartAddr->isChecked()) { ui->txtStartAddr->setText(point); } else { ui->txtEndAddr->setText(point); } } } else if (type == "routepoints") { //Convert the query path into longitude and latitude coordinate point set data display routeDatas.clear(); ui->tableWidgetSource->clearContents(); //There may be multiple path sets. At present, the test is one path set 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) { //Find the angle from the previous point int angle = 0; QString point = tableWidget->item(row, 1)->data(Qt::UserRole).toString(); //The first and last points are not handled if (row > 0 && row < count - 1) { //Previous point coordinates QString point2 = tableWidget->item(row - 1, 1)->data(Qt::UserRole).toString(); //Calculates the rotation angle between the current previous point and the current point angle = WebHelper::getAngle(point2, point); } //Execute the mobile device point function, with the parameter of rotation angle QString js = QString("moveMarker('%1', '%2', %3)").arg(name).arg(point).arg(angle); runJs(js); //Redraw track points if (ui->cboxMoveMode->currentIndex() == 0) { //Clear previous track points js = QString("deleteOverlay('Polyline')"); runJs(js); //Take out the points from the first point to the line where the current focus is located to form the set of track points that have been passed and draw again 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); } //Display the current data 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() == "Simulated trajectory") { //Limit minimum quantity if (tableWidget->rowCount() < 2) { return; } //Step 1: add a tag name = ui->txtDeviceName->text().trimmed(); if (name.isEmpty()) { name = "Malaysia Airlines MH370"; } //The image file is in the config/device directory under the executable file 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); //Step 2: move to the first point tableWidget->setFocus(); tableWidget->setCurrentCell(0, 0); ui->btnTestData->setText("Stop simulation"); ui->tabWidget->setTabEnabled(ui->tableWidgetSource->isVisible() ? 1 : 0, false); //Step 3: start the timer and execute it immediately int index = ui->cboxMoveInterval->currentIndex(); timer->start(ui->cboxMoveInterval->itemData(index).toInt()); moveMarker(); } else { //Empty tag QString js = QString("deleteMarker('%1')").arg(name); runJs(js); //Stop Timer timer->stop(); ui->btnTestData->setText("Simulated trajectory"); ui->tabWidget->setTabEnabled(ui->tableWidgetSource->isVisible() ? 1 : 0, true); } } void frmMapGps::on_btnCheckData_clicked() { if (timer->isActive()) { return; } //Step 1: calculate the total number, and find the average value = actual total number / expected total number + 1. If the expected total number > = actual total number, it does not need to be processed int countSource = ui->tableWidgetSource->rowCount(); int countTarget = ui->txtPointCount->text().trimmed().toInt(); if (countTarget >= countSource) { QUIHelper::showMessageBoxError("The target number of points cannot be greater than or equal to the original data!"); ui->txtPointCount->setFocus(); return; } //Step 2: take out the values one by one according to the average value 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; } //The end must be added as the end. If it is just divided, it is not used QString point = ui->tableWidgetSource->item(countSource - 1, 1)->data(Qt::UserRole).toString(); if (points.last() != point) { points << point; } //Step 3: refill the data into the filtered data list 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("Please click query route to obtain the coordinate point collection of the route!"); return; } //Clear previous track points runJs("deleteOverlay('Polyline')"); //Draw the received set of path points into segments foreach (QStringList data, routeDatas) { QString points = data.join("|"); QString js = QString("addPolyline('%1', '#ff0000')").arg(points); runJs(js); } }