更多精彩内容 |
---|
👉个人内容分类汇总 👈 |
👉GIS开发 👈 |
开发环境说明
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位
- 无需注册、无需key进行瓦片地图下载;
- 地址可能会失效;
- 大量下载可能会限速;
- 仅作为学习使用。
项目文件结构
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*256
return (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;
}
}
}
mapinput.h
#ifndef MAPINPUT_H
#define MAPINPUT_H
#include <QWidget>
#include "mapStruct.h"
namespace Ui {
class MapInput;
}
class MapInput : public QWidget
{
Q_OBJECT
public:
explicit MapInput(QWidget *parent = nullptr);
~MapInput();
const QList<ImageInfo> &getInputInfo(); // 获取下载地图所需的输入信息
private:
// ArcGis
void 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); // 计算左上角瓦片X
int ltY = latTotile(lt.at(1).toDouble(), z); // 计算左上角瓦片Y
int rdX = lonTotile(rd.at(0).toDouble(), z); // 计算右下角瓦片X
int rdY = latTotile(rd.at(1).toDouble(), z); // 计算右下角瓦片Y
ImageInfo 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); // 计算左上角瓦片X
int ltY = latTotile(lt.at(1).toDouble(), z); // 计算左上角瓦片Y
int rdX = lonTotile(rd.at(0).toDouble(), z); // 计算右下角瓦片X
int rdY = latTotile(rd.at(1).toDouble(), z); // 计算右下角瓦片Y
ImageInfo 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("<ype=%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-cn
static 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=cn
static 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";
#endif
int 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); // 计算左上角瓦片X
int ltY = latTotile(lt.at(1).toDouble(), z); // 计算左上角瓦片Y
int rdX = lonTotile(rd.at(0).toDouble(), z); // 计算右下角瓦片X
int rdY = latTotile(rd.at(1).toDouble(), z); // 计算右下角瓦片Y
QString 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);
#endif
m_infos.append(info);
}
}
}
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); // 设置最大线程数
#endif
m_future = QtConcurrent::map(m_infos, getUrl);
}
/**
* @brief 退出下载
*/
void DownloadThreads::quit()
{
if(m_future.isRunning()) // 判断是否在运行
{
m_future.cancel(); // 取消下载
m_future.waitForFinished(); // 等待退出
}
}