Qt編寫地圖綜合應用51-離線瓦片地圖下載

一、前言

寫這個離線地圖下載器的初衷,就是爲了方便自己的幾個需要離線地圖的程序,客戶需求,既然地圖程序已經可以支持離線地圖,那如何獲取到這些離線瓦片地圖文件是個關鍵,而且這是這個功能的關鍵,拿到這些一張張的瓦片圖片文件,才能根據js函數繪製組合成離線地圖。

網上其實有很多各種各樣的離線地圖下載器,大部分都是要收費的,免費的要麼是限制了下載的瓦片數量或者級別,要麼是下載的瓦片圖打上了水印,看起來很難看,由於經常需要用到離線地圖,擺脫這個限制,特意花了點時間重新研究了瓦片地圖的原理,做了個離線地圖下載器,其實瓦片地圖下載沒有那麼複雜,其實就是從開放的幾個服務器地址組建要請求的瓦片地圖的地址,發送請求以後會自動將圖片返回給你,你只需要拿到圖片數據保存成圖片即可。

瓦片地圖下載流程步驟如下:

  1. 獲取可視區域或者行政區域的範圍。
  2. 拿到區域的左下角右上角經緯度座標。
  3. 根據層級數計算對應層級的瓦片數。
  4. 自動生成下載瓦片地圖的地址併發出請求。
  5. 解析收到的數據保存成圖片。
  6. 更新對應界面的下載數量和進度。
  7. 可選擇對應保存的目錄、全選層級、中途停止下載等。
  8. 可選擇是下載街道圖還是衛星圖等。

二、功能特點

  1. 多線程同步下載多級別瓦片地圖,不卡界面。
  2. 內置多個離線地圖下載請求地址,自動隨機選擇一個發送請求。
  3. 下載地圖類型同時支持街道圖和衛星圖。
  4. 自動計算可視區域或者行政區域的下載瓦片數量。
  5. 下載的級別可以自定義範圍和選擇。
  6. 每個瓦片下載完成都發送信號通知,參數包括下載用時。
  7. 可設置下載最大超時時間,超過了則丟棄跳到下一個下載任務。
  8. 實時顯示下載進度,以及當前級別已經下載的瓦片數和總瓦片數。
  9. 下載過程中可以停止下載,下載完成自動統計總用時。
  10. 內置經緯度和屏幕座標互相轉換函數。
  11. 目前支持百度地圖,其他地圖比如谷歌地圖、騰訊地圖、高德地圖可以定製。
  12. 函數接口友好和統一,使用簡單方便,就一個類。
  13. 支持任意Qt版本、任意系統、任意編譯器。

三、體驗地址

  1. 體驗地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取碼:o05q 文件名:bin_map.zip
  2. 國內站點:https://gitee.com/feiyangqingyun
  3. 國際站點:https://github.com/feiyangqingyun
  4. 個人主頁:https://blog.csdn.net/feiyangqingyun
  5. 知乎主頁: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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章