C++(Qt)-GIS开发-简易瓦片地图下载器

Qt-GIS开发-简易瓦片地图下载器

文章目录

  • Qt-GIS开发-简易瓦片地图下载器
    • 1、概述
    • 2、安装openssl
    • 3、实现效果
    • 4、主要代码
      • 4.1 算法函数
      • 4.2 瓦片地图下载url拼接
      • 4.3 多线程下载
    • 5、源码地址
    • 6、参考

更多精彩内容
👉个人内容分类汇总 👈
👉GIS开发 👈

1、概述

  1. 支持单线程、多线程下载瓦片地图。
  2. 使用QNetworkAccessManager、QNetworkReply实现http、https下载功能;
  3. 支持下载多样式arcGis瓦片地图;
  4. 支持下载多样式高德瓦片地图;
  5. 支持多样式Bing地图下载;
  6. Qt中https下载功能需要安装openssl库。
  7. 本文中不会详细说瓦片地图的原理,写得好的文章太多了。

开发环境说明

  • 系统:Windows11、Ubuntu20.04
  • Qt版本:Qt 5.14.2
  • 编译器:MSVC2017-64、GCC/G++64

2、安装openssl

  • qt使用QNetworkReply/https下载瓦片地图需要ssl支持,qt默认是没有ssl库的;

  • 使用下列代码打印qt版本支持的ssl版本;

    qDebug() << "输出当前QT支持的openSSL版本: " << QSslSocket::sslLibraryBuildVersionString();
    qDebug() << "OpenSSL支持情况: " <<QSslSocket::supportsSsl();
    qDebug() << "OpenSSL运行时SSL库版本: " << QSslSocket::sslLibraryBuildVersionString();
    
  • windows可以下载对应版本的openssl,然后进行安装(轻量级就可以);

  • linux可以通过命令行安装,也可以下载源码自己编译。

  • openssl的github仓库

  • openssl官网

  • 安装后将openssl/bin文件夹下的libcrypto-1_1-x64.dll、libssl-1_1-x64.dll两个动态库拷贝到qt的编译器路径下,注意区分32和64位

    • D:\Qt\Qt5.14.2\5.14.2\msvc2017_64\bin
    • D:\Qt\Qt5.14.2\5.14.2\mingw73_64\bin

3、实现效果

  1. 无需注册、无需key进行瓦片地图下载;
  2. 地址可能会失效;
  3. 大量下载可能会限速;
  4. 仅作为学习使用。

在这里插入图片描述

4、主要代码

  • 项目文件结构
    在这里插入图片描述

4.1 算法函数

  • bingformula.h文件

    #ifndef BINGFORMULA_H
    #define BINGFORMULA_H
    #include <QPoint>
    #include <QtGlobal>namespace Bing {
    qreal clip(qreal n, qreal min, qreal max);
    qreal clipLon(qreal lon);   // 裁剪经度范围
    qreal clipLat(qreal lat);   // 裁剪纬度范围uint mapSize(int level);                        // 根据地图级别计算世界地图总宽高(以像素为单位)
    qreal groundResolution(qreal lat, int level);   // 计算地面分辨率
    qreal mapScale(qreal lat, int level, int screenDpi);   // 计算比例尺QPoint latLongToPixelXY(qreal lon, qreal lat, int level);               // 经纬度转像素 XY坐标
    void pixelXYToLatLong(QPoint pos, int level, qreal& lon, qreal& lat);   // 像素坐标转WGS-84墨卡托坐标QPoint pixelXYToTileXY(QPoint pos);    // 像素坐标转瓦片编号
    QPoint tileXYToPixelXY(QPoint tile);   // 瓦片编号转像素坐标QPoint latLongToTileXY(qreal lon, qreal lat, int level);   // 经纬度转瓦片编号
    QPointF tileXYToLatLong(QPoint tile, int level);           // 瓦片编号转经纬度QString tileXYToQuadKey(QPoint tile, int level);                             // 瓦片编号转QuadKey
    void quadKeyToTileXY(QString quadKey, int& tileX, int& tileY, int& level);   // QuadKey转瓦片编号、级别
    }   // namespace Bing
    #endif   // BINGFORMULA_H
  • bingformula.cpp文件

    /********************************************************************* 文件名: bingformula.cpp* 时间:   2024-04-05 21:36:16* 开发者:  mhf* 邮箱:   1603291350@qq.com* 说明:   适用于Bing瓦片地图的算法* ******************************************************************/
    #include "bingformula.h"
    #include <qstring.h>
    #include <QtMath>static const qreal g_EarthRadius = 6'378'137;   // 赤道半径/*** @brief      限定最小值,最大值范围* @param n    需要限定的值* @param min* @param max* @return*/
    qreal Bing::clip(qreal n, qreal min, qreal max)
    {n = qMax(n, min);n = qMin(n, max);return n;
    }/*** @brief      限定经度范围值,防止超限,经度范围[-180, 180]* @param lon  输入的经度* @return     裁剪后的经度*/
    qreal Bing::clipLon(qreal lon)
    {return clip(lon, -180.0, 180);
    }/*** @brief      限定纬度范围值,防止超限,经度范围[-85.05112878, 85.05112878]* @param lat  输入的纬度* @return     裁剪后的纬度*/
    qreal Bing::clipLat(qreal lat)
    {return clip(lat, -85.05112878, 85.05112878);
    }/*** @brief       根据输入的瓦片级别计算全地图总宽高,适用于墨卡托投影* @param level 1-23(bing地图没有0级别,最低级别为1,由4块瓦片组成)* @return      以像素为单位的地图宽度和高度。*/
    uint Bing::mapSize(int level)
    {uint w = 256;   // 第0级别为256*256return (w << level);
    }/*** @brief        计算指定纬度、级别的地面分辨率(不同纬度分辨率不同)* @param lat    纬度* @param level  地图级别 1-23(bing地图没有0级别,最低级别为1,由4块瓦片组成)* @return       地面分辨率 单位(米/像素)*/
    qreal Bing::groundResolution(qreal lat, int level)
    {lat = clipLat(lat);return qCos(lat * M_PI / 180) * 2 * M_PI * g_EarthRadius / mapSize(level);
    }/*** @brief           计算地图比例尺,地面分辨率和地图比例尺也随纬度而变化* @param lat       纬度* @param level     地图级别 1-23(bing地图没有0级别,最低级别为1,由4块瓦片组成)* @param screenDpi 屏幕分辨率,单位为点/英寸  通常为 96 dpi* @return          地图比例尺 1:N(地图上1厘米表示实际N厘米)*/
    qreal Bing::mapScale(qreal lat, int level, int screenDpi)
    {return groundResolution(lat, level) * screenDpi / 0.0254;   // 1英寸等于0.0254米
    }/*** @brief         将一个点从纬度/经度WGS-84墨卡托坐标(以度为单位)转换为指定细节级别的像素XY坐标。* @param lon     经度* @param lat     纬度* @param level   地图级别* @return        像素坐标*/
    QPoint Bing::latLongToPixelXY(qreal lon, qreal lat, int level)
    {lon = clipLon(lon);lat = clipLat(lat);qreal x = (lon + 180) / 360;qreal sinLat = qSin(lat * M_PI / 180);qreal y = 0.5 - qLn((1 + sinLat) / (1 - sinLat)) / (4 * M_PI);uint size = mapSize(level);qreal pixelX = x * size + 0.5;pixelX = clip(pixelX, 0, size - 1);qreal pixelY = y * size + 0.5;pixelY = clip(pixelY, 0, size - 1);return QPoint(pixelX, pixelY);
    }/*** @brief         将像素从指定细节级别的像素XY坐标转换为经纬度WGS-84坐标(以度为单位)* @param pos    像素坐标* @param level* @param lon* @param lat*/
    void Bing::pixelXYToLatLong(QPoint pos, int level, qreal& lon, qreal& lat)
    {uint size = mapSize(level);qreal x = (clip(pos.x(), 0, size - 1) / size) - 0.5;qreal y = 0.5 - (clip(pos.y(), 0, size - 1) / size);lon = x * 360;lat = 90 - (360 * qAtan(qExp(-y * 2 * M_PI)) / M_PI);
    }/*** @brief     像素坐标转瓦片编号* @param pos  像素坐标* @return    瓦片编号*/
    QPoint Bing::pixelXYToTileXY(QPoint pos)
    {int x = pos.x() / 256;int y = pos.y() / 256;return QPoint(x, y);
    }/*** @brief       瓦片编号转像素坐标* @param tile  瓦片编号* @return      像素坐标*/
    QPoint Bing::tileXYToPixelXY(QPoint tile)
    {int x = tile.x() * 256;int y = tile.y() * 256;return QPoint(x, y);
    }/*** @brief       经纬度转瓦片编号* @param lon* @param lat* @param level* @return*/
    QPoint Bing::latLongToTileXY(qreal lon, qreal lat, int level)
    {return pixelXYToTileXY(latLongToPixelXY(lon, lat, level));
    }/*** @brief         瓦片编号转经纬度* @param tile* @param level* @return       经纬度 x:经度  y纬度*/
    QPointF Bing::tileXYToLatLong(QPoint tile, int level)
    {qreal lon = 0;qreal lat = 0;QPoint pos = tileXYToPixelXY(tile);pixelXYToLatLong(pos, level, lon, lat);return QPointF(lon, lat);
    }/*** @brief         瓦片编号转 bing请求的QuadKey* @param tile   瓦片编号* @param level  瓦片级别* @return*/
    QString Bing::tileXYToQuadKey(QPoint tile, int level)
    {QString key;for (int i = level; i > 0; i--){char digit = '0';int mask = 1 << (i - 1);if ((tile.x() & mask) != 0){digit++;}if ((tile.y() & mask) != 0){digit += 2;}key.append(digit);}return key;
    }/*** @brief            将一个QuadKey转换为瓦片XY坐标。* @param quadKey* @param tileX      返回瓦片X编号* @param tileY      返回瓦片Y编号* @param level      返回瓦片等级*/
    void Bing::quadKeyToTileXY(QString quadKey, int& tileX, int& tileY, int& level)
    {tileX = 0;tileY = 0;level = quadKey.count();QByteArray buf = quadKey.toUtf8();for (int i = level; i > 0; i--){int mask = 1 << (i - 1);switch (buf.at(i - 1)){case '0':break;case '1':tileX |= mask;break;case '2':tileY |= mask;break;case '3':tileX |= mask;tileY |= mask;break;default:break;}}
    }

4.2 瓦片地图下载url拼接

  • mapinput.h

    #ifndef MAPINPUT_H
    #define MAPINPUT_H#include <QWidget>
    #include "mapStruct.h"namespace Ui {
    class MapInput;
    }class MapInput : public QWidget
    {Q_OBJECTpublic:explicit MapInput(QWidget *parent = nullptr);~MapInput();const QList<ImageInfo> &getInputInfo();       // 获取下载地图所需的输入信息private:// ArcGisvoid initArcGis();void getArcGisMapInfo();// 高德void initAMap();void getAMapInfo();// Bing地图void initBing();void getBingMapInfo();private:Ui::MapInput *ui;QList<ImageInfo> m_infos;                // 保存下载瓦片图片的信息
    };#endif // MAPINPUT_H
  • mapinput.cpp

    /********************************************************************* 文件名: mapinput.cpp* 时间:   2024-01-19 22:22:37* 开发者:  mhf* 邮箱:   1603291350@qq.com* 说明:   生成地图下载的输入信息* ******************************************************************/
    #include "mapinput.h"
    #include "bingformula.h"
    #include "formula.h"
    #include "ui_mapinput.h"
    #include <QDebug>MapInput::MapInput(QWidget* parent): QWidget(parent), ui(new Ui::MapInput)
    {ui->setupUi(this);initArcGis();initAMap();initBing();
    }MapInput::~MapInput()
    {delete ui;
    }/*** @brief 填入ArcGis下载地图类型*/
    void MapInput::initArcGis()
    {for (int i = 0; i < 23; i++){ui->com_z->addItem(QString("%1").arg(i), i);}ui->com_type->addItem("NatGeo_World_Map");ui->com_type->addItem("USA_Topo_Maps ");ui->com_type->addItem("World_Imagery");ui->com_type->addItem("World_Physical_Map");ui->com_type->addItem("World_Shaded_Relief");ui->com_type->addItem("World_Street_Map");ui->com_type->addItem("World_Terrain_Base");ui->com_type->addItem("World_Topo_Map");ui->com_type->addItem("Canvas/World_Dark_Gray_Base");ui->com_type->addItem("Canvas/World_Dark_Gray_Reference");ui->com_type->addItem("Canvas/World_Light_Gray_Base");ui->com_type->addItem("Canvas/World_Light_Gray_Reference");ui->com_type->addItem("Elevation/World_Hillshade_Dark");ui->com_type->addItem("Elevation/World_Hillshade");ui->com_type->addItem("Ocean/World_Ocean_Base");ui->com_type->addItem("Ocean/World_Ocean_Reference");ui->com_type->addItem("Polar/Antarctic_Imagery");ui->com_type->addItem("Polar/Arctic_Imagery");ui->com_type->addItem("Polar/Arctic_Ocean_Base");ui->com_type->addItem("Polar/Arctic_Ocean_Reference");ui->com_type->addItem("Reference/World_Boundaries_and_Places_Alternate ");ui->com_type->addItem("Reference/World_Boundaries_and_Places");ui->com_type->addItem("Reference/World_Reference_Overlay");ui->com_type->addItem("Reference/World_Transportation");ui->com_type->addItem("Specialty/World_Navigation_Charts");// 填入下载格式ui->com_format->addItem("jpg");ui->com_format->addItem("png");ui->com_format->addItem("bmp");
    }/*** @brief   计算并返回需要下载的瓦片地图信息* @return*/
    const QList<ImageInfo>& MapInput::getInputInfo()
    {m_infos.clear();   // 清除之前的内容switch (ui->tabWidget->currentIndex())   // 判断是什么类型的地图源{case 0:   // ArcGis{getArcGisMapInfo();   // 计算ArcGis下载信息break;}case 1:{getAMapInfo();   // 计算高德地图下载信息break;}case 2:{getBingMapInfo();   // 计算bing地图下载信息break;}default:break;}qDebug() << "瓦片数:" << m_infos.count();return m_infos;
    }/*** @brief   通过输入地图信息计算需要下载的瓦片图信息,下载ArcGIS地图,WGS84坐标系,Web墨卡托投影,z y x输入*/
    void MapInput::getArcGisMapInfo()
    {static QString url = "https://server.arcgisonline.com/arcgis/rest/services/%1/MapServer/tile/%2/%3/%4.%5";int z = ui->com_z->currentData().toInt();QString type = ui->com_type->currentText();QString format = ui->com_format->currentText();QStringList lt = ui->line_LTGps->text().trimmed().split(',');   // 左上角经纬度QStringList rd = ui->line_RDGps->text().trimmed().split(',');   // 右下角经纬度if (lt.count() != 2 || rd.count() != 2)return;                                    // 判断输入是否正确int ltX = lonTotile(lt.at(0).toDouble(), z);   // 计算左上角瓦片Xint ltY = latTotile(lt.at(1).toDouble(), z);   // 计算左上角瓦片Yint rdX = lonTotile(rd.at(0).toDouble(), z);   // 计算右下角瓦片Xint rdY = latTotile(rd.at(1).toDouble(), z);   // 计算右下角瓦片YImageInfo info;info.z = z;info.format = format;for (int x = ltX; x <= rdX; x++){info.x = x;for (int y = ltY; y <= rdY; y++){info.y = y;info.url = url.arg(type).arg(z).arg(y).arg(x).arg(format);m_infos.append(info);}}
    }/*** @brief 初始化高德地图下载选项信息*/
    void MapInput::initAMap()
    {for (int i = 1; i < 5; i++){ui->com_amapPrefix->addItem(QString("wprd0%1").arg(i));}for (int i = 1; i < 5; i++){ui->com_amapPrefix->addItem(QString("webst0%1").arg(i));}for (int i = 0; i < 19; i++){ui->com_amapZ->addItem(QString("%1").arg(i), i);}// 语言设置ui->com_amapLang->addItem("中文", "zh_cn");ui->com_amapLang->addItem("英文", "en");// 地图类型ui->com_amapStyle->addItem("卫星影像图", 6);ui->com_amapStyle->addItem("矢量路网", 7);ui->com_amapStyle->addItem("影像路网", 8);        // 支持png透明背景ui->com_amapStyle->addItem("卫星+影像路网", 9);   // 支持png透明背景// 图片尺寸,只在7 8生效ui->com_amapScl->addItem("256x256", 1);ui->com_amapScl->addItem("512x512", 2);// 填入下载格式ui->com_amapFormat->addItem("jpg");ui->com_amapFormat->addItem("png");ui->com_amapFormat->addItem("bmp");
    }/*** @brief 计算高德地图瓦片下载信息*/
    void MapInput::getAMapInfo()
    {static QString url = "https://%1.is.autonavi.com/appmaptile?";int z = ui->com_amapZ->currentData().toInt();QString format = ui->com_amapFormat->currentText();QStringList lt = ui->line_LTGps->text().trimmed().split(',');   // 左上角经纬度QStringList rd = ui->line_RDGps->text().trimmed().split(',');   // 右下角经纬度if (lt.count() != 2 || rd.count() != 2)return;                                    // 判断输入是否正确int ltX = lonTotile(lt.at(0).toDouble(), z);   // 计算左上角瓦片Xint ltY = latTotile(lt.at(1).toDouble(), z);   // 计算左上角瓦片Yint rdX = lonTotile(rd.at(0).toDouble(), z);   // 计算右下角瓦片Xint rdY = latTotile(rd.at(1).toDouble(), z);   // 计算右下角瓦片YImageInfo info;info.z = z;info.format = format;int style = ui->com_amapStyle->currentData().toInt();int count = 1;if (style == 9){count = 2;   // 如果是下载卫星图 + 路网图则循环两次}for (int i = 0; i < count; i++){if (count == 2){if (i == 0){style = 6;   // 第一次下载卫星图info.format = "jpg";}else{style = 8;             // 第二次下载路网图info.format = "png";   // 如果同时下载卫星图和路网图则路网图为透明png格式}}QString tempUrl = url.arg(ui->com_amapPrefix->currentText());                     // 设置域名tempUrl += QString("&style=%1").arg(style);                                       // 设置地图类型tempUrl += QString("&lang=%1").arg(ui->com_amapLang->currentData().toString());   // 设置语言tempUrl += QString("&scl=%1").arg(ui->com_amapScl->currentData().toInt());        // 设置图片尺寸,只在7 8生效tempUrl += QString("&ltype=%1").arg(ui->spin_amapLtype->value());                 // 设置图片中的信息,只有 7 8有效for (int x = ltX; x <= rdX; x++){info.x = x;for (int y = ltY; y <= rdY; y++){info.url = tempUrl + QString("&x=%1&y=%2&z=%3").arg(x).arg(y).arg(z);info.y = y;m_infos.append(info);}}}
    }/*** @brief 初始化Bing地图配置*/
    void MapInput::initBing()
    {// 服务器for (int i = 0; i < 8; i++){ui->com_bingPrefix->addItem(QString("%1").arg(i));}// 地图语言ui->com_bingLang->addItem("中文", "zh-cn");ui->com_bingLang->addItem("英语", "en-US");// 地图类型ui->com_bingType->addItem("卫星地图", "a");ui->com_bingType->addItem("普通地图", "r");ui->com_bingType->addItem("混合地图", "h");ui->com_bingCstl->addItem("默认", "w4c");ui->com_bingCstl->addItem("白天", "vb");    // 白天道路地图ui->com_bingCstl->addItem("夜晚", "vbd");   // 夜晚道路图// 瓦片等级for (int i = 1; i < 21; i++){ui->com_bingZ->addItem(QString("%1").arg(i));}// 填入下载格式ui->com_bingFormat->addItem("jpg");ui->com_bingFormat->addItem("png");ui->com_bingFormat->addItem("bmp");
    }/*** @brief 计算Bing地图的下载信息(这些url可能会失效,后续会使用其他方式下载)*  https://learn.microsoft.com/en-us/bingmaps/rest-services/directly-accessing-the-bing-maps-tiles*/
    void MapInput::getBingMapInfo()
    {//https://r1.tiles.ditu.live.com/tiles/r1321001.png?g=1001&mkt=zh-cn//http://dynamic.t2.tiles.ditu.live.com/comp/ch/r1321001.png?it=G,OS,L&mkt=en-us&cstl=w4c&ur=cn//http://ecn.t{0}.tiles.virtualearth.net/tiles/{1}{2}.png? g={4}//https://t0.dynamic.tiles.ditu.live.com/comp/ch/1320300313132?mkt=zh-CN&ur=CN&it=G,RL&n=z&og=894&cstl=vb//https://t1.dynamic.tiles.ditu.live.com/comp/ch/13203012200201?mkt=zh-CN&ur=cn&it=G,RL&n=z&og=894&cstl=vbd//https://dynamic.t1.tiles.ditu.live.com/comp/ch/1320300313313?it=Z,TF&L&n=z&key=AvquUWQgfy7VPqHn9ergJsp3Q_EiUft0ed70vZsX0_aqPABBdK07OkwrXWoGXsTG&ur=cn&cstl=vbd#define USE_URL 1
    #if (USE_URL == 0)// https://r1.tiles.ditu.live.com/tiles/r1321001.png?g=1001&mkt=zh-cnstatic QString url = "https://r%1.tiles.ditu.live.com/tiles/%2%3.%4?g=1001&mkt=%5";   // 街道图r支持中文
    #elif (USE_URL == 1)// http://dynamic.t2.tiles.ditu.live.com/comp/ch/r1321001.png?it=G,OS,L&mkt=en-us&cstl=w4c&ur=cnstatic QString url = "http://dynamic.t%1.tiles.ditu.live.com/comp/ch/%2%3.%4?it=G,OS,L&mkt=%5&cstl=%6&ur=cn";
    #endifint z = ui->com_bingZ->currentText().toInt();QStringList lt = ui->line_LTGps->text().trimmed().split(',');   // 左上角经纬度QStringList rd = ui->line_RDGps->text().trimmed().split(',');   // 右下角经纬度if (lt.count() != 2 || rd.count() != 2)return;                                    // 判断输入是否正确int ltX = lonTotile(lt.at(0).toDouble(), z);   // 计算左上角瓦片Xint ltY = latTotile(lt.at(1).toDouble(), z);   // 计算左上角瓦片Yint rdX = lonTotile(rd.at(0).toDouble(), z);   // 计算右下角瓦片Xint rdY = latTotile(rd.at(1).toDouble(), z);   // 计算右下角瓦片YQString format = ui->com_bingFormat->currentText();ImageInfo info;info.z = z;info.format = format;int prefix = ui->com_bingPrefix->currentIndex();QString lang = ui->com_bingLang->currentData().toString();   // 语言QString type = ui->com_bingType->currentData().toString();   // 类型QString cstl = ui->com_bingCstl->currentData().toString();   // 样式QPoint point;for (int x = ltX; x <= rdX; x++){info.x = x;point.setX(x);for (int y = ltY; y <= rdY; y++){info.y = y;point.setY(y);QString quadKey = Bing::tileXYToQuadKey(point, z);   // 将xy转为quadkey
    #if (USE_URL == 0)info.url = url.arg(prefix).arg(type).arg(quadKey).arg(format).arg(lang);
    #elif (USE_URL == 1)info.url = url.arg(prefix).arg(type).arg(quadKey).arg(format).arg(lang).arg(cstl);
    #endifm_infos.append(info);}}
    }

4.3 多线程下载

  • downloadthreads.h

    #ifndef DOWNLOADTHREADS_H
    #define DOWNLOADTHREADS_H#include "mapStruct.h"
    #include <QFutureWatcher>
    #include <QObject>class DownloadThreads : public QObject
    {Q_OBJECT
    public:explicit DownloadThreads(QObject* parent = nullptr);~DownloadThreads();// 传入需要下载的瓦片信息void getImage(QList<ImageInfo> infos);void quit();   // 退出下载signals:void finished(ImageInfo info);   // 返回下载后的瓦片,由于QImage为共享内存,所以传递不需要考虑太多性能private:QFuture<void> m_future;QList<ImageInfo> m_infos;
    };#endif   // DOWNLOADTHREADS_H
  • downloadthreads.cpp

    /********************************************************************* 文件名: downloadthreads.cpp* 时间:   2024-03-31 20:32:58* 开发者:  mhf* 邮箱:   1603291350@qq.com* 说明:   多线程下载瓦片地图* ******************************************************************/
    #include "downloadthreads.h"
    #include <QtConcurrent>
    #include <qnetworkaccessmanager.h>
    #include <qnetworkreply.h>static DownloadThreads* g_this = nullptr;
    DownloadThreads::DownloadThreads(QObject *parent) : QObject(parent)
    {g_this = this;  // 记录当前 this指针,用于传递信号
    }DownloadThreads::~DownloadThreads()
    {g_this = nullptr;quit();
    }/*** @brief       下载瓦片* @param info* @return*/
    void getUrl(ImageInfo info)
    {QNetworkAccessManager manager;QNetworkReply* reply = manager.get(QNetworkRequest(QUrl(info.url)));// 等待返回QEventLoop loop;QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);loop.exec();if(reply->error() == QNetworkReply::NoError){QByteArray buf = reply->readAll();info.img.loadFromData(buf);}else{info.count++;if(info.count < 3){getUrl(info);   // 下载失败重新下载return;}else{qWarning() << "下载失败:" << reply->errorString();}}if(g_this){emit g_this->finished(info);  // 通过信号将下载后的瓦片传出去}
    }/*** @brief         调用线程池下载瓦片* @param infos*/
    void DownloadThreads::getImage(QList<ImageInfo> infos)
    {m_infos = infos;    // 这里不能使用infos,因为会在函数退出释放
    #if 0   // 由于map使用的是全局线程池,所以可以查看、设置线程数qDebug() <<QThreadPool::globalInstance()->maxThreadCount();   // 查看最大线程数QThreadPool::globalInstance()->setMaxThreadCount(1);          // 设置最大线程数
    #endifm_future = QtConcurrent::map(m_infos, getUrl);
    }/*** @brief 退出下载*/
    void DownloadThreads::quit()
    {if(m_future.isRunning())   // 判断是否在运行{m_future.cancel();               // 取消下载m_future.waitForFinished();      // 等待退出}
    }

5、源码地址

  • github
  • gitee

6、参考

  • GIS开发一:OpenLayers在线瓦片数据源汇总_在线瓦片图数据-CSDN博客
  • Bing Maps Tile System - Bing Maps | Microsoft Learn

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/42257.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全面解析 TypeScript 泛型的二三事

2024年了相信大家都已经在日常开发的过程中使用上了 TypeScript 了。TypeScript 增强了代码可靠性和可维护性&#xff0c;确保减少运行时错误并提高开发人员的工作效率。 TypeScript 通过类型声明 使得 javascript 拥有了强类型校验。而泛型的是类型声明中最重要的一环&#x…

06浅谈大语言模型可调节参数TopP和TopK

浅谈大模型参数TopP和TopK 大语言模型中的temperature、top_p和top_k参数是用来控制模型生成文本时的随机性和创造性的。下面分享一下topP和topK两个参数的意义及逻辑&#xff1b; top K&#xff08;Top-K Sampling&#xff09; 作用&#xff1a;只从模型认为最可能的k个词中选…

时间、查找、打包、行过滤与指令的运行——linux指令学习(二)

前言&#xff1a;本节内容标题虽然为指令&#xff0c;但是并不只是讲指令&#xff0c; 更多的是和指令相关的一些原理性的东西。 如果友友只想要查一查某个指令的用法&#xff0c; 很抱歉&#xff0c; 本节不是那种带有字典性质的文章。但是如果友友是想要来学习的&#xff0c;…

Amesim中删除计算结果保存计算文件

前言 Amesim在工程应用中计算的结果文件有时会很大&#xff0c;为了节省电脑存储空间&#xff0c;项目结束后可以将计算结果删除进行保存以存档。 操作步骤 具体操作步骤如下&#xff1a; Step1&#xff1a;在①File下打开&#xff08;Open&#xff09;需要删除计算结果的项…

DAMA学习笔记(四)-数据建模与设计

1.引言 数据建模是发现、分析和确定数据需求的过程&#xff0c;用一种称为数据模型的精确形式表示和传递这些数据需求。建模过程中要求组织发现并记录数据组合的方式。数据常见的模式: 关系模式、多维模式、面向对象模式、 事实模式、时间序列模式和NoSQL模式。按照描述详细程度…

第1节、基于太阳能的环境监测系统——MPPT充电板

一、更新时间&#xff1a; 本篇文章更新于&#xff1a;2024年7月6日23:33:30 二、内容简介&#xff1a; 整体系统使用太阳能板为锂电池充电和系统供电&#xff0c;天黑后锂电池为系统供电&#xff0c;本节主要介绍基于CN3722的MPPT太阳能充电模块&#xff0c;这块主要是硬件…

AE的首选项设置

打开AE,点击 编辑->首选项->常规 显示 点击“所有关键帧”&#xff0c;这样显示路径就会显示所有关键帧 导入 将序列素材改为25帧每秒&#xff0c;作为以后制作的默认 媒体和磁盘缓存 根据个人需求选择磁盘缓存的文件夹&#xff0c;如果d盘空间充足&#xff0c;就改成…

Git中两个开发分支merge的原理

一 分支合并 1.1 原理 分支合并&#xff1a;就是将A分支修改后且commit的内容&#xff0c;合并到B分支&#xff0c;这些修改且提交的内容和B分支对应的内容和位置进行比较&#xff1a; 1.不一样的话&#xff0c;提示冲突&#xff0c;需要人工干预。 2.一样的话&#xff0c;…

网页封装APP:让您的网站变身移动应用

网页封装APP&#xff1a;让您的网站变身移动应用 随着移动设备的普及&#xff0c;越来越多的人开始使用移动设备浏览网站。但是&#xff0c;传统的网站设计并不适合移动设备的屏幕尺寸和交互方式&#xff0c;这导致了用户体验不佳和流失。 有没有办法让您的网站变身移动应用&…

14-43 剑和诗人17 - ActiveRAG之主动学习

​​​​​ 大型语言模型 (LLM) 的出现开启了对话式 AI 的新时代。这些模型可以生成非常像人类的文本&#xff0c;并且比以往更好地进行对话。然而&#xff0c;它们在仅依赖预训练知识方面仍然面临限制。为了提高推理能力和准确性&#xff0c;LLM 需要能够整合外部知识。 检索…

每日一题~oj(贪心)

对于位置 i来说&#xff0c;如果 不选她&#xff0c;那她的贡献是 vali-1 *2&#xff0c;如果选他 &#xff0c;那么她的贡献是 ai. 每一个数的贡献 是基于前一个数的贡献 来计算的。只要保证这个数的前一个数的贡献是最优的&#xff0c;那么以此类推下去&#xff0c;整体的val…

AI让大龄程序员重新焕发活力

AI是在帮助开发者还是取代他们&#xff1f; 在软件开发领域&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;正在改变开发者的工作方式。无论是代码生成、错误检测还是自动化测试&#xff0c;AI工具正在成为开发者的得力助手。然而&#xff0c;这也引发了对开发者职业…

华为云简介

前言 华为云是华为的云服务品牌&#xff0c;将华为30多年在ICT领域的技术积累和产品解决方案开放给客户&#xff0c;致力于提供稳定可靠、安全可信、可持续创新的云服务&#xff0c;赋能应用、使能数据、做智能世界的“黑土地”&#xff0c;推进实现“用得起、用得好、用得放心…

优化视频编辑体验:精准高效视频分割,降低误差率,显著提升处理精度与效率

在数字创意飞速发展的今天&#xff0c;视频编辑已成为展现创意与才华的重要舞台。然而&#xff0c;面对海量的视频素材和复杂的编辑需求&#xff0c;如何快速、准确地完成视频分割&#xff0c;成为了每一个视频编辑师心中的难题。今天&#xff0c;我们荣幸地向您介绍一款颠覆性…

摸鱼大数据——Spark SQL——DataFrame详解一

1.DataFrame基本介绍 DataFrame表示的是一个二维的表。二维表&#xff0c;必然存在行、列等表结构描述信息​表结构描述信息(元数据Schema): StructType对象字段: StructField对象&#xff0c;可以描述字段名称、字段数据类型、是否可以为空行: Row对象列: Column对象&#xff…

Rejetto HFS 服务器存在严重漏洞受到攻击

AhnLab 报告称 &#xff0c;黑客正在针对旧版本的 Rejetto HTTP 文件服务器 (HFS) 注入恶意软件和加密货币挖矿程序。 然而&#xff0c;由于存在错误&#xff0c; Rejetto 警告用户不要使用 2.3 至 2.4 版本。 2.3m 版本在个人、小型团队、教育机构和测试网络文件共享的开发…

力扣 第 134 场双周赛 解题报告 | 珂学家

前言 题解 T1/T3是环形的处理技巧&#xff0c;这边可以double数组(更准确地讲&#xff0c;添加一个合适的小尾巴). T4是典题&#xff0c;前不久周赛刚考过&#xff0c;是一道结论题&#xff0c;也可以借助数据结构处理。 T1. 交替组 I 和T3一起讲 T2. 与敌人战斗后的最大分数…

生产调度:flowshop问题数学建模

接上一篇文章&#xff0c;在了解生产调度问题的背景和基本概念之后&#xff0c;我想先从比较基础的 flowshop和 jobshop 数学模型入手&#xff0c;理解实际调度过程中的问题求解思路。这一篇文章主要面向 flowshop 问题进行数学建模&#xff0c;对于这类比较经典的问题&#xf…

2007-2022年 国内各上市公司绿色化转型数据.(Excel文件、dta文件、参考文献、计算方法与说明)

上市公司绿色化转型数据为研究者提供了评估企业在生态文明建设、循环经济和绿色管理方面表现的重要视角。以下是对中国各上市公司绿色化转型数据的介绍&#xff1a; 数据简介 定义&#xff1a;上市公司绿色化转型是指企业在发展模式上向可持续发展转变&#xff0c;实现资源节…

成人高考报名条件及收费标准详解

成人高考报名条件及收费标准详解 您想通过成人高考改变自己的命运&#xff0c;但不知道报名条件和收费标准&#xff1f;本文将为您详细介绍成人高考报名条件和收费标准&#xff0c;并为您提供专业的成人教育服务。 深圳成人高考www.shenzhixun.com 成人高考报名条件 成人高考…