一、前言說明
已經有了最基礎的介面用來新增覆蓋物,而且還有透過進入覆蓋物模式動態新增覆蓋物的功能,為什麼還要來個高階繪圖?因為又有新的需求,給錢就搞,一點底線都沒有。無論哪個地圖廠家,提供的介面都是沒有唯一標識引數的,也就類似於學號,這就是需要自己主動定一個屬性用來儲存唯一標識,這樣方便後面刪除和修改。比如之前的刪除覆蓋物,只能指定一種型別的覆蓋物,指定圓形則刪除所有圓形覆蓋物,這樣還是完全不夠的,很多時候需要指定唯一標識來刪除和修改。
經過大量的模擬測試發現,對覆蓋物的刪除和清空clearoverlay等,高德地圖和谷歌地圖會在合適的時機釋放記憶體,而其他地圖幾乎不會去主動釋放,所以如果遇到需要很多覆蓋物的場景,建議生成一次後,後面只是去改變該覆蓋物的屬性比如座標位置和路徑,而不是清空後再次去生成。這樣就可以極力避免記憶體洩漏,這可能也是web的缺陷,沒有手動釋放機制,說是內部有垃圾自動回收,但是內部很可能判斷失敗,導致一直無法釋放。所以需要重新調整新增和刪除修改覆蓋物的函式介面,增加唯一標識引數,需要搞個動態的雷達掃描,只需要不斷更新覆蓋物經緯度座標即可。
二、相關程式碼
#include "frmmapdraw.h"
#include "ui_frmmapdraw.h"
#include "qthelper.h"
#include "maphelper.h"
#include "webview.h"
#include "frmmapdrawradar.h"
#include "frmmapdrawairline.h"
frmMapDraw::frmMapDraw(QWidget *parent) : QWidget(parent), ui(new Ui::frmMapDraw)
{
ui->setupUi(this);
this->initForm();
this->initConfig();
}
frmMapDraw::~frmMapDraw()
{
delete ui;
}
void frmMapDraw::showEvent(QShowEvent *)
{
//只需要載入一次/避免重複初始化
static bool isLoad = false;
if (!isLoad) {
isLoad = true;
QMetaObject::invokeMethod(this, "on_btnLoadMap_clicked", Qt::QueuedConnection);
}
}
void frmMapDraw::initForm()
{
//設定右側固定寬度
ui->widgetRight->setFixedWidth(AppData::RightWidth - 50);
mapObj = NULL;
//例項化瀏覽器控制元件並加入到佈局
webView = new WebView(this);
webView->setLayout(ui->gridLayout);
connect(webView, SIGNAL(receiveDataFromJs(QString, QVariant)), this, SLOT(receiveDataFromJs(QString, QVariant)));
}
void frmMapDraw::initConfig()
{
MapHelper::loadMapCore(ui->cboxMapCore, AppConfig::MapDrawCore);
connect(ui->cboxMapCore, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
connect(ui->cboxMapCore, SIGNAL(currentIndexChanged(int)), this, SLOT(on_btnLoadMap_clicked()));
ui->cboxMapLocal->setCurrentIndex(AppConfig::MapDrawLocal ? 1 : 0);
connect(ui->cboxMapLocal, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
connect(ui->cboxMapLocal, SIGNAL(currentIndexChanged(int)), this, SLOT(on_btnLoadMap_clicked()));
ui->cboxMapType->setCurrentIndex(AppConfig::MapDrawType);
connect(ui->cboxMapType, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
ui->cboxOverlay->addItem("標註", "marker");
ui->cboxOverlay->addItem("折線", "polyline");
ui->cboxOverlay->addItem("多邊形", "polygon");
ui->cboxOverlay->addItem("矩形", "rectangle");
ui->cboxOverlay->addItem("圓形", "circle");
ui->cboxOverlay->setCurrentIndex(AppConfig::MapDrawOverlay);
connect(ui->cboxOverlay, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
}
void frmMapDraw::saveConfig()
{
AppConfig::MapDrawCore = ui->cboxMapCore->itemData(ui->cboxMapCore->currentIndex()).toInt();
AppConfig::MapDrawLocal = (ui->cboxMapLocal->currentIndex() == 1);
AppConfig::MapDrawType = ui->cboxMapType->currentIndex();
AppConfig::MapDrawOverlay = ui->cboxOverlay->currentIndex();
AppConfig::writeConfig();
}
void frmMapDraw::runJs(const QString &js)
{
mapObj->runJs(js);
}
void frmMapDraw::receiveDataFromJs(const QString &type, const QVariant &data)
{
QString result = data.toString();
if (type == "marker" || type == "polyline" || type == "polygon" || type == "rectangle" || type == "circle") {
//填入當前單擊的覆蓋物的標識
ui->txtFlag->setText(result);
//啟動編輯狀態/方便對比當前按下了哪個
on_btnEditOverlay_clicked();
}
}
void frmMapDraw::on_btnLoadMap_clicked()
{
//根據不同地圖核心例項化地圖類
MapCore mapCore = (MapCore)ui->cboxMapCore->itemData(ui->cboxMapCore->currentIndex()).toInt();
bool mapLocal = (ui->cboxMapLocal->currentIndex() == 1);
int mapType = ui->cboxMapType->currentIndex();
int zoom = MapHelper::getMapZoom(mapCore, this->objectName());
MapHelper::initMapObj(this, &mapObj, mapCore);
mapObj->setWebView(webView);
mapObj->setSaveFile(SaveFile);
mapObj->setMapLocal(mapLocal);
mapObj->setMapType(mapType);
mapObj->setZoom(zoom);
mapObj->load();
//天地圖專用繪圖
ui->frameTian->setEnabled(mapCore == MapCore_Tian);
ui->txtFlag->setText("overlay1");
}
void frmMapDraw::on_btnDrawRadar_clicked()
{
frmMapDrawRadar *form = new frmMapDrawRadar;
form->setAttribute(Qt::WA_DeleteOnClose);
QtHelper::setFormInCenter(form);
form->show();
}
void frmMapDraw::on_btnDrawAirline_clicked()
{
frmMapDrawAirline *form = new frmMapDrawAirline;
form->setAttribute(Qt::WA_DeleteOnClose);
QtHelper::setFormInCenter(form);
form->show();
}
void frmMapDraw::on_btnAddOverlay_clicked()
{
int count = 1;
QString overlay = ui->cboxOverlay->itemData(ui->cboxOverlay->currentIndex()).toString();
if (overlay == "polyline") {
count = 2;
} else if (overlay == "polygon") {
count = 3;
} else if (overlay == "rectangle") {
count = 2;
}
//隨機生成模擬資料
QStringList points = QtHelper::getRandPoint(count, 121.204610, 31.018220, 0.415, 0.3);
QString flag = ui->txtFlag->text().trimmed();
QString color = QtHelper::getRandColor().name();
int radius = QtHelper::getRandValue(1000, 8000);
QString js;
QString data = points.join("|");
if (overlay == "marker") {
js = QString("addMarker('%1', '%2', '', '../mapimage/marker5.png', 100, 100)").arg(flag).arg(data);
} else if (overlay == "polyline") {
js = QString("addPolyline('%1', '%2', '%3')").arg(flag).arg(data).arg(color);
} else if (overlay == "polygon") {
js = QString("addPolygon('%1', '%2', '%3')").arg(flag).arg(data).arg(color);
} else if (overlay == "rectangle") {
js = QString("addRectangle('%1', '%2', '%3')").arg(flag).arg(data).arg(color);
} else if (overlay == "circle") {
js = QString("addCircle('%1', '%2', %4, '%3')").arg(flag).arg(data).arg(color).arg(radius);
}
this->runJs(js);
//自動將序號遞增
if (flag.startsWith("overlay")) {
int index = flag.mid(7, flag.length()).toInt();
flag = QString("overlay%1").arg(index + 1);
ui->txtFlag->setText(flag);
}
}
void frmMapDraw::on_btnDeleteOverlay_clicked()
{
QString flag = ui->txtFlag->text().trimmed();
QString js = QString("deleteOverlay('', '%1')").arg(flag);
this->runJs(js);
}
void frmMapDraw::on_btnUpdateOverlay_clicked()
{
QString overlay = ui->cboxOverlay->itemData(ui->cboxOverlay->currentIndex()).toString();
QString flag = ui->txtFlag->text().trimmed();
QString data = "121.428961,31.249075|121.557167,31.213504|121.469780,31.135397|121.403090,31.198678";
if (overlay == "marker") {
//可以分開執行也可以合併執行
//this->runJs(QString("setMarker('%1', '121.424362,31.175942', 45)").arg(flag));
//this->runJs(QString("setMarker('%1', null, null, '../mapimage/marker.png', 45, 65)").arg(flag));
this->runJs(QString("setMarker('%1', '121.424362,31.175942', 45, '../mapimage/marker.png', 45, 65)").arg(flag));
} else if (overlay == "circle") {
this->runJs(QString("updateOverlay('%1', '%2', '121.424362,31.175942', 10000)").arg(overlay).arg(flag));
} else {
this->runJs(QString("updateOverlay('%1', '%2', '%3')").arg(overlay).arg(flag).arg(data));
}
}
void frmMapDraw::on_btnClearOverlay_clicked()
{
this->runJs("clearOverlay()");
ui->txtFlag->setText("overlay1");
}
void frmMapDraw::on_btnEditOverlay_clicked()
{
QString flag = ui->txtFlag->text().trimmed();
this->runJs(QString("editOverlay('%1', true)").arg(flag));
}
void frmMapDraw::on_btnCloseEdit_clicked()
{
QString flag = ui->txtFlag->text().trimmed();
this->runJs(QString("editOverlay('%1', false)").arg(flag));
}
void frmMapDraw::on_btnListenOverlay_clicked()
{
this->runJs("addListener()");
}
void frmMapDraw::on_btnCloseListen_clicked()
{
this->runJs("removeListener()");
this->runJs("editOverlays(false)");
}
void frmMapDraw::on_btnShowOverlay_clicked()
{
this->runJs("setOverlayVisible('', true)");
}
void frmMapDraw::on_btnHideOverlay_clicked()
{
this->runJs("setOverlayVisible('', false)");
}
void frmMapDraw::on_btnPolylineArrow_clicked()
{
QString points = "121.225890,31.261530|121.371460,31.262120|121.316530,31.353050";
this->runJs(QString("addCover('PolylineArrow', '', '%1', '#753775', 5, 1, 'dashed', null, 0)").arg(points));
}
void frmMapDraw::on_btnStraightArrow_clicked()
{
QString points = "121.192930,31.041760|121.318590,31.140540";
this->runJs(QString("addCover('StraightArrow', '', '%1')").arg(points));
}
void frmMapDraw::on_btnDiagonalArrow_clicked()
{
QString points = "121.623460,31.123500|121.617970,31.021750";
this->runJs(QString("addCover('DiagonalArrow', '', '%1')").arg(points));
}
void frmMapDraw::on_btnSector_clicked()
{
QString points = "121.428961,31.249075|121.557167,31.213504|121.403090,31.198678";
this->runJs(QString("addCover('Sector', '', '%1')").arg(points));
}
void frmMapDraw::on_btnAddSector_clicked()
{
//第一個參數列示唯一標識/是為了方便指定標識來刪除
QString points = "121.344680,31.001150|121.483380,31.041760|121.449050,31.121730";
this->runJs(QString("addCover('Sector', 's1', '%1', '#00ff00')").arg(points));
}
void frmMapDraw::on_btnDeleteSector_clicked()
{
//指定唯一標識刪除
QString js = "deleteOverlay('', 's1')";
this->runJs(js);
}
void frmMapDraw::on_cboxMapType_currentIndexChanged(int index)
{
if (this->isVisible()) {
this->runJs(QString("setMapType(%1)").arg(index));
}
}
三、相關連結
- 體驗地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取碼:o05q 檔名:bin_map.zip
- 國內站點:https://gitee.com/feiyangqingyun
- 國際站點:https://github.com/feiyangqingyun
四、效果圖
五、功能特點
5.1 地圖功能
- 支援多種地圖核心,預設採用百度地圖,可選高德地圖、天地圖、騰訊地圖、谷歌地圖等。
- 同時支援線上地圖和離線地圖兩種模式,離線地圖方便在不聯網的場景中使用。
- 支援各種地圖控制元件的啟用,比如地圖導航、地圖型別、縮圖、比例尺、全景導航、實時路況、繪圖工具、結果皮膚等。
- 支援多種地圖功能的動態啟用禁用,比如地圖拖曳、鍵盤操作、滾輪縮放、雙擊放大、連續縮放、地圖測距等。
- 提供眾多js函式介面用於互動,引數極其豐富,能夠想到的應用場景需求都有。
- 統一的訊號槽機制,地圖中的結果統一訊號傳送出去,收到後根據type型別區分。
- 支援地圖互動,比如滑鼠按下獲取對應位置的經緯度。單擊標註點彈出對應點的資訊。
- 支援新增標註、刪除標註、移動標註、清空標註。
- 標註點可以指定圖示圖片和尺寸,支援gif動圖,支援指定以圖片中心對齊還是底部中心對齊。可以設定旋轉角度,帶富文字提示資訊。
- 標註點事件支援單擊發訊號通知和自己彈框顯示資訊。
- 提供地址轉座標和座標轉地址介面。
- 支援各種圖形繪製,包括折線圖、多邊形、矩形、圓形、弧線等。
- 可顯示懸浮的繪圖工具欄,直接在地圖上劃線、標註點、矩形、圓形等。
- 支援各種區域搜尋,比如矩形區域、圓形區域,可以按照關鍵字匹配將搜尋結果顯示在地圖中。
- 可動態新增離線的行政區邊界點資料。可以搜尋行政區劃並獲取該區域的邊界點資料。資料可以儲存到檔案以便離線使用。
- 支援點聚合功能,多個小標註點合併到一個大標註點,防止點密集導致互動不友好。
- 可以新增海量點,每個點都可以單擊獲取對應座標和資訊。
- 所有的覆蓋物資訊比如標註點、矩形、多邊形、折線圖等,都可以主動獲取對應的資訊比如座標點和路徑等。
- 支援路徑規劃,支援公交路線、自駕路線、步行路線、騎行路線,不同查詢支援不同策略,可選最少時間、最少換乘、不走高架等。
- 路徑規劃結果可以顯示在地圖中,也可以獲取到路徑點座標集合。這個資料可以儲存到檔案,以便發給機器人或者無人機做導航用來軌跡移動。
- 可以設定不同的地圖檢視比如街道圖、衛星圖、混合圖。
- 可以設定不同的樣式,比如午夜藍、青草綠等樣式風格。
- 可以設定地圖的旋轉角度和傾斜角度。
- 提供經緯度座標糾偏轉換功能,比如傳入的GPS座標需要轉換到百度地圖座標或者高德地圖座標。各種座標系轉換全部離線函式,支援地球座標系WGS-84、火星座標系GCJ-02、百度座標系BD-09之間的互相轉換,涵蓋了各種地圖的座標系。
- 提供動態軌跡點移動功能,按照給定的經緯度座標集合平滑移動。
- 同時支援qwidget和qml,支援編譯到安卓系統執行。
5.2 其他功能
- 提供離線地圖下載模組,可以選擇不同的地圖核心比如百度地圖或者谷歌地圖,不同的地圖型別比如下載街道圖還是衛星圖,不同的地圖層級,多執行緒極速下載。
- 表格行實時顯示對應的瓦片下載進度,有下載超時時間,重試次數,每個瓦片下載完成都傳送訊號通知,引數包括下載用時。
- 提供省市輪廓圖下載模組,自動下載各個地區的輪廓圖,儲存到指令碼檔案或者文字檔案。
- 支援手動調整不同區域的輪廓邊界,調整後可以主動獲取調整後的邊界點集合。
- 提供動態點位示例,手動在地圖上選點並新增標註,附帶自定義的資訊比如速度和時間等。
- 提供海量點位示例,批次新增標註點、點聚合、海量點。用於測試環境中支援的最大點位效能。
- 提供動態軌跡示例,在地圖上滑鼠按下選擇起點和終點後,查詢路線,獲取路徑軌跡點,模擬軌跡平滑移動。可以篩選資料將過多的路徑點篩選到設定的點數。
- 提供軌跡回放示例,按照指定的軌跡點列表回放,也可以匯入軌跡點資料進行回放。同時支援在街道圖、衛星圖、混合圖中回放軌跡。
- 提供省市區域地圖示例,採用echart元件,同時支援閃爍點圖、遷徙圖、區域地圖、世界地圖、儀表盤等。可以設定標題、提示資訊、背景顏色、文字顏色、線條顏色、區域顏色等各種顏色。
- 省市區域地圖示例,內建世界地圖、全國地圖、省份地圖、地區地圖,可以精確到縣,所有地圖全部離線使用。可設定城市的名稱、值、經緯度集合。
- 內建通用瀏覽器元件,同時支援webkit/webengine/miniblink等核心。提供網頁控制元件示例,演示開啟網頁和本地網頁檔案。
- 支援任意Qt版本、任意系統、任意編譯器。