一、前言
寫這個離線地圖下載器的初衷,就是爲了方便自己的幾個需要離線地圖的程序,客戶需求,既然地圖程序已經可以支持離線地圖,那如何獲取到這些離線瓦片地圖文件是個關鍵,而且這是這個功能的關鍵,拿到這些一張張的瓦片圖片文件,才能根據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 MapDownload::download(const QString &path, int mapType, int downType, int xmin, int xmax, int ymin, int ymax, int zoom)
{
for (int x = xmin; x <= xmax; x++) {
for (int y = ymin; y <= ymax; y++) {
if (stopped) {
return;
}
QString url = getUrl(mapType, downType, x, y, zoom);
QString dirName = QString("%1/%2/%3/").arg(path).arg(zoom).arg(x);
QString fileName = QString("%1.jpg").arg(y);
downloadImage(url, dirName, fileName, zoom);
}
}
}
void MapDownload::downloadBaiDu(const QString &path, int downType, int xmin, int xmax, int ymin, int ymax, int zoom)
{
download(path, 0, downType, xmin, xmax, ymin, ymax, zoom);
}
void MapDownload::downloadTian(const QString &path, int downType, int xmin, int xmax, int ymin, int ymax, int zoom)
{
download(path, 3, downType, xmin, xmax, ymin, ymax, zoom);
}
void MapDownload::downloadGoogle(const QString &path, int downType, int xmin, int xmax, int ymin, int ymax, int zoom)
{
download(path, 4, downType, xmin, xmax, ymin, ymax, zoom);
}
void MapDownload::downloadImage(const QString &url, const QString &dirName, const QString &fileName, int zoom)
{
if (url.isEmpty()) {
return;
}
//啓動計時
QElapsedTimer time;
time.start();
//先判斷文件夾是否存在,不存在則新建
QDir dir(dirName);
if (!dir.exists()) {
dir.mkpath(dirName);
}
//局部的事件循環,不卡主界面
QEventLoop eventLoop;
//設置超時 5.15開始自帶了超時時間函數 默認30秒
#if (QT_VERSION >= QT_VERSION_CHECK(5,15,0))
manager->setTransferTimeout(timeout);
#else
QTimer timer;
connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
timer.setSingleShot(true);
timer.start(timeout);
#endif
QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec();
bool error = false;
if (reply->bytesAvailable() > 0 && reply->error() == QNetworkReply::NoError) {
//讀取所有數據保存成文件
QByteArray data = reply->readAll();
QFile file(dirName + fileName);
if (file.open(QFile::WriteOnly | QFile::Truncate)) {
file.write(data);
file.close();
}
} else {
//可以自行增加下載失敗的統計
error = true;
qDebug() << TIMEMS << "下載出錯" << reply->errorString();
}
reply->deleteLater();
int useTime = time.elapsed();
emit finsh(url, fileName, zoom, useTime, error);
}