经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
C++(Qt)-GIS开发-简易瓦片地图下载器
来源:cnblogs  作者:mahuifa  时间:2024/7/8 9:59:50  对本文有异议

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

更多精彩内容
??个人内容分类汇总 ??
??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版本;

    1. qDebug() << "输出当前QT支持的openSSL版本: " << QSslSocket::sslLibraryBuildVersionString();
    2. qDebug() << "OpenSSL支持情况: " <<QSslSocket::supportsSsl();
    3. 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文件

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

    1. /********************************************************************
    2. * 文件名: bingformula.cpp
    3. * 时间: 2024-04-05 21:36:16
    4. * 开发者: mhf
    5. * 邮箱: 1603291350@qq.com
    6. * 说明: 适用于Bing瓦片地图的算法
    7. * ******************************************************************/
    8. #include "bingformula.h"
    9. #include <qstring.h>
    10. #include <QtMath>
    11. static const qreal g_EarthRadius = 6'378'137; // 赤道半径
    12. /**
    13. * @brief 限定最小值,最大值范围
    14. * @param n 需要限定的值
    15. * @param min
    16. * @param max
    17. * @return
    18. */
    19. qreal Bing::clip(qreal n, qreal min, qreal max)
    20. {
    21. n = qMax(n, min);
    22. n = qMin(n, max);
    23. return n;
    24. }
    25. /**
    26. * @brief 限定经度范围值,防止超限,经度范围[-180, 180]
    27. * @param lon 输入的经度
    28. * @return 裁剪后的经度
    29. */
    30. qreal Bing::clipLon(qreal lon)
    31. {
    32. return clip(lon, -180.0, 180);
    33. }
    34. /**
    35. * @brief 限定纬度范围值,防止超限,经度范围[-85.05112878, 85.05112878]
    36. * @param lat 输入的纬度
    37. * @return 裁剪后的纬度
    38. */
    39. qreal Bing::clipLat(qreal lat)
    40. {
    41. return clip(lat, -85.05112878, 85.05112878);
    42. }
    43. /**
    44. * @brief 根据输入的瓦片级别计算全地图总宽高,适用于墨卡托投影
    45. * @param level 1-23(bing地图没有0级别,最低级别为1,由4块瓦片组成)
    46. * @return 以像素为单位的地图宽度和高度。
    47. */
    48. uint Bing::mapSize(int level)
    49. {
    50. uint w = 256; // 第0级别为256*256
    51. return (w << level);
    52. }
    53. /**
    54. * @brief 计算指定纬度、级别的地面分辨率(不同纬度分辨率不同)
    55. * @param lat 纬度
    56. * @param level 地图级别 1-23(bing地图没有0级别,最低级别为1,由4块瓦片组成)
    57. * @return 地面分辨率 单位(米/像素)
    58. */
    59. qreal Bing::groundResolution(qreal lat, int level)
    60. {
    61. lat = clipLat(lat);
    62. return qCos(lat * M_PI / 180) * 2 * M_PI * g_EarthRadius / mapSize(level);
    63. }
    64. /**
    65. * @brief 计算地图比例尺,地面分辨率和地图比例尺也随纬度而变化
    66. * @param lat 纬度
    67. * @param level 地图级别 1-23(bing地图没有0级别,最低级别为1,由4块瓦片组成)
    68. * @param screenDpi 屏幕分辨率,单位为点/英寸 通常为 96 dpi
    69. * @return 地图比例尺 1:N(地图上1厘米表示实际N厘米)
    70. */
    71. qreal Bing::mapScale(qreal lat, int level, int screenDpi)
    72. {
    73. return groundResolution(lat, level) * screenDpi / 0.0254; // 1英寸等于0.0254米
    74. }
    75. /**
    76. * @brief 将一个点从纬度/经度WGS-84墨卡托坐标(以度为单位)转换为指定细节级别的像素XY坐标。
    77. * @param lon 经度
    78. * @param lat 纬度
    79. * @param level 地图级别
    80. * @return 像素坐标
    81. */
    82. QPoint Bing::latLongToPixelXY(qreal lon, qreal lat, int level)
    83. {
    84. lon = clipLon(lon);
    85. lat = clipLat(lat);
    86. qreal x = (lon + 180) / 360;
    87. qreal sinLat = qSin(lat * M_PI / 180);
    88. qreal y = 0.5 - qLn((1 + sinLat) / (1 - sinLat)) / (4 * M_PI);
    89. uint size = mapSize(level);
    90. qreal pixelX = x * size + 0.5;
    91. pixelX = clip(pixelX, 0, size - 1);
    92. qreal pixelY = y * size + 0.5;
    93. pixelY = clip(pixelY, 0, size - 1);
    94. return QPoint(pixelX, pixelY);
    95. }
    96. /**
    97. * @brief 将像素从指定细节级别的像素XY坐标转换为经纬度WGS-84坐标(以度为单位)
    98. * @param pos 像素坐标
    99. * @param level
    100. * @param lon
    101. * @param lat
    102. */
    103. void Bing::pixelXYToLatLong(QPoint pos, int level, qreal& lon, qreal& lat)
    104. {
    105. uint size = mapSize(level);
    106. qreal x = (clip(pos.x(), 0, size - 1) / size) - 0.5;
    107. qreal y = 0.5 - (clip(pos.y(), 0, size - 1) / size);
    108. lon = x * 360;
    109. lat = 90 - (360 * qAtan(qExp(-y * 2 * M_PI)) / M_PI);
    110. }
    111. /**
    112. * @brief 像素坐标转瓦片编号
    113. * @param pos 像素坐标
    114. * @return 瓦片编号
    115. */
    116. QPoint Bing::pixelXYToTileXY(QPoint pos)
    117. {
    118. int x = pos.x() / 256;
    119. int y = pos.y() / 256;
    120. return QPoint(x, y);
    121. }
    122. /**
    123. * @brief 瓦片编号转像素坐标
    124. * @param tile 瓦片编号
    125. * @return 像素坐标
    126. */
    127. QPoint Bing::tileXYToPixelXY(QPoint tile)
    128. {
    129. int x = tile.x() * 256;
    130. int y = tile.y() * 256;
    131. return QPoint(x, y);
    132. }
    133. /**
    134. * @brief 经纬度转瓦片编号
    135. * @param lon
    136. * @param lat
    137. * @param level
    138. * @return
    139. */
    140. QPoint Bing::latLongToTileXY(qreal lon, qreal lat, int level)
    141. {
    142. return pixelXYToTileXY(latLongToPixelXY(lon, lat, level));
    143. }
    144. /**
    145. * @brief 瓦片编号转经纬度
    146. * @param tile
    147. * @param level
    148. * @return 经纬度 x:经度 y纬度
    149. */
    150. QPointF Bing::tileXYToLatLong(QPoint tile, int level)
    151. {
    152. qreal lon = 0;
    153. qreal lat = 0;
    154. QPoint pos = tileXYToPixelXY(tile);
    155. pixelXYToLatLong(pos, level, lon, lat);
    156. return QPointF(lon, lat);
    157. }
    158. /**
    159. * @brief 瓦片编号转 bing请求的QuadKey
    160. * @param tile 瓦片编号
    161. * @param level 瓦片级别
    162. * @return
    163. */
    164. QString Bing::tileXYToQuadKey(QPoint tile, int level)
    165. {
    166. QString key;
    167. for (int i = level; i > 0; i--)
    168. {
    169. char digit = '0';
    170. int mask = 1 << (i - 1);
    171. if ((tile.x() & mask) != 0)
    172. {
    173. digit++;
    174. }
    175. if ((tile.y() & mask) != 0)
    176. {
    177. digit += 2;
    178. }
    179. key.append(digit);
    180. }
    181. return key;
    182. }
    183. /**
    184. * @brief 将一个QuadKey转换为瓦片XY坐标。
    185. * @param quadKey
    186. * @param tileX 返回瓦片X编号
    187. * @param tileY 返回瓦片Y编号
    188. * @param level 返回瓦片等级
    189. */
    190. void Bing::quadKeyToTileXY(QString quadKey, int& tileX, int& tileY, int& level)
    191. {
    192. tileX = 0;
    193. tileY = 0;
    194. level = quadKey.count();
    195. QByteArray buf = quadKey.toUtf8();
    196. for (int i = level; i > 0; i--)
    197. {
    198. int mask = 1 << (i - 1);
    199. switch (buf.at(i - 1))
    200. {
    201. case '0':
    202. break;
    203. case '1':
    204. tileX |= mask;
    205. break;
    206. case '2':
    207. tileY |= mask;
    208. break;
    209. case '3':
    210. tileX |= mask;
    211. tileY |= mask;
    212. break;
    213. default:
    214. break;
    215. }
    216. }
    217. }

4.2 瓦片地图下载url拼接

  • mapinput.h

    1. #ifndef MAPINPUT_H
    2. #define MAPINPUT_H
    3. #include <QWidget>
    4. #include "mapStruct.h"
    5. namespace Ui {
    6. class MapInput;
    7. }
    8. class MapInput : public QWidget
    9. {
    10. Q_OBJECT
    11. public:
    12. explicit MapInput(QWidget *parent = nullptr);
    13. ~MapInput();
    14. const QList<ImageInfo> &getInputInfo(); // 获取下载地图所需的输入信息
    15. private:
    16. // ArcGis
    17. void initArcGis();
    18. void getArcGisMapInfo();
    19. // 高德
    20. void initAMap();
    21. void getAMapInfo();
    22. // Bing地图
    23. void initBing();
    24. void getBingMapInfo();
    25. private:
    26. Ui::MapInput *ui;
    27. QList<ImageInfo> m_infos; // 保存下载瓦片图片的信息
    28. };
    29. #endif // MAPINPUT_H
  • mapinput.cpp

    1. /********************************************************************
    2. * 文件名: mapinput.cpp
    3. * 时间: 2024-01-19 22:22:37
    4. * 开发者: mhf
    5. * 邮箱: 1603291350@qq.com
    6. * 说明: 生成地图下载的输入信息
    7. * ******************************************************************/
    8. #include "mapinput.h"
    9. #include "bingformula.h"
    10. #include "formula.h"
    11. #include "ui_mapinput.h"
    12. #include <QDebug>
    13. MapInput::MapInput(QWidget* parent)
    14. : QWidget(parent)
    15. , ui(new Ui::MapInput)
    16. {
    17. ui->setupUi(this);
    18. initArcGis();
    19. initAMap();
    20. initBing();
    21. }
    22. MapInput::~MapInput()
    23. {
    24. delete ui;
    25. }
    26. /**
    27. * @brief 填入ArcGis下载地图类型
    28. */
    29. void MapInput::initArcGis()
    30. {
    31. for (int i = 0; i < 23; i++)
    32. {
    33. ui->com_z->addItem(QString("%1").arg(i), i);
    34. }
    35. ui->com_type->addItem("NatGeo_World_Map");
    36. ui->com_type->addItem("USA_Topo_Maps ");
    37. ui->com_type->addItem("World_Imagery");
    38. ui->com_type->addItem("World_Physical_Map");
    39. ui->com_type->addItem("World_Shaded_Relief");
    40. ui->com_type->addItem("World_Street_Map");
    41. ui->com_type->addItem("World_Terrain_Base");
    42. ui->com_type->addItem("World_Topo_Map");
    43. ui->com_type->addItem("Canvas/World_Dark_Gray_Base");
    44. ui->com_type->addItem("Canvas/World_Dark_Gray_Reference");
    45. ui->com_type->addItem("Canvas/World_Light_Gray_Base");
    46. ui->com_type->addItem("Canvas/World_Light_Gray_Reference");
    47. ui->com_type->addItem("Elevation/World_Hillshade_Dark");
    48. ui->com_type->addItem("Elevation/World_Hillshade");
    49. ui->com_type->addItem("Ocean/World_Ocean_Base");
    50. ui->com_type->addItem("Ocean/World_Ocean_Reference");
    51. ui->com_type->addItem("Polar/Antarctic_Imagery");
    52. ui->com_type->addItem("Polar/Arctic_Imagery");
    53. ui->com_type->addItem("Polar/Arctic_Ocean_Base");
    54. ui->com_type->addItem("Polar/Arctic_Ocean_Reference");
    55. ui->com_type->addItem("Reference/World_Boundaries_and_Places_Alternate ");
    56. ui->com_type->addItem("Reference/World_Boundaries_and_Places");
    57. ui->com_type->addItem("Reference/World_Reference_Overlay");
    58. ui->com_type->addItem("Reference/World_Transportation");
    59. ui->com_type->addItem("Specialty/World_Navigation_Charts");
    60. // 填入下载格式
    61. ui->com_format->addItem("jpg");
    62. ui->com_format->addItem("png");
    63. ui->com_format->addItem("bmp");
    64. }
    65. /**
    66. * @brief 计算并返回需要下载的瓦片地图信息
    67. * @return
    68. */
    69. const QList<ImageInfo>& MapInput::getInputInfo()
    70. {
    71. m_infos.clear(); // 清除之前的内容
    72. switch (ui->tabWidget->currentIndex()) // 判断是什么类型的地图源
    73. {
    74. case 0: // ArcGis
    75. {
    76. getArcGisMapInfo(); // 计算ArcGis下载信息
    77. break;
    78. }
    79. case 1:
    80. {
    81. getAMapInfo(); // 计算高德地图下载信息
    82. break;
    83. }
    84. case 2:
    85. {
    86. getBingMapInfo(); // 计算bing地图下载信息
    87. break;
    88. }
    89. default:
    90. break;
    91. }
    92. qDebug() << "瓦片数:" << m_infos.count();
    93. return m_infos;
    94. }
    95. /**
    96. * @brief 通过输入地图信息计算需要下载的瓦片图信息,下载ArcGIS地图,WGS84坐标系,Web墨卡托投影,z y x输入
    97. */
    98. void MapInput::getArcGisMapInfo()
    99. {
    100. static QString url = "https://server.arcgisonline.com/arcgis/rest/services/%1/MapServer/tile/%2/%3/%4.%5";
    101. int z = ui->com_z->currentData().toInt();
    102. QString type = ui->com_type->currentText();
    103. QString format = ui->com_format->currentText();
    104. QStringList lt = ui->line_LTGps->text().trimmed().split(','); // 左上角经纬度
    105. QStringList rd = ui->line_RDGps->text().trimmed().split(','); // 右下角经纬度
    106. if (lt.count() != 2 || rd.count() != 2)
    107. return; // 判断输入是否正确
    108. int ltX = lonTotile(lt.at(0).toDouble(), z); // 计算左上角瓦片X
    109. int ltY = latTotile(lt.at(1).toDouble(), z); // 计算左上角瓦片Y
    110. int rdX = lonTotile(rd.at(0).toDouble(), z); // 计算右下角瓦片X
    111. int rdY = latTotile(rd.at(1).toDouble(), z); // 计算右下角瓦片Y
    112. ImageInfo info;
    113. info.z = z;
    114. info.format = format;
    115. for (int x = ltX; x <= rdX; x++)
    116. {
    117. info.x = x;
    118. for (int y = ltY; y <= rdY; y++)
    119. {
    120. info.y = y;
    121. info.url = url.arg(type).arg(z).arg(y).arg(x).arg(format);
    122. m_infos.append(info);
    123. }
    124. }
    125. }
    126. /**
    127. * @brief 初始化高德地图下载选项信息
    128. */
    129. void MapInput::initAMap()
    130. {
    131. for (int i = 1; i < 5; i++)
    132. {
    133. ui->com_amapPrefix->addItem(QString("wprd0%1").arg(i));
    134. }
    135. for (int i = 1; i < 5; i++)
    136. {
    137. ui->com_amapPrefix->addItem(QString("webst0%1").arg(i));
    138. }
    139. for (int i = 0; i < 19; i++)
    140. {
    141. ui->com_amapZ->addItem(QString("%1").arg(i), i);
    142. }
    143. // 语言设置
    144. ui->com_amapLang->addItem("中文", "zh_cn");
    145. ui->com_amapLang->addItem("英文", "en");
    146. // 地图类型
    147. ui->com_amapStyle->addItem("卫星影像图", 6);
    148. ui->com_amapStyle->addItem("矢量路网", 7);
    149. ui->com_amapStyle->addItem("影像路网", 8); // 支持png透明背景
    150. ui->com_amapStyle->addItem("卫星+影像路网", 9); // 支持png透明背景
    151. // 图片尺寸,只在7 8生效
    152. ui->com_amapScl->addItem("256x256", 1);
    153. ui->com_amapScl->addItem("512x512", 2);
    154. // 填入下载格式
    155. ui->com_amapFormat->addItem("jpg");
    156. ui->com_amapFormat->addItem("png");
    157. ui->com_amapFormat->addItem("bmp");
    158. }
    159. /**
    160. * @brief 计算高德地图瓦片下载信息
    161. */
    162. void MapInput::getAMapInfo()
    163. {
    164. static QString url = "https://%1.is.autonavi.com/appmaptile?";
    165. int z = ui->com_amapZ->currentData().toInt();
    166. QString format = ui->com_amapFormat->currentText();
    167. QStringList lt = ui->line_LTGps->text().trimmed().split(','); // 左上角经纬度
    168. QStringList rd = ui->line_RDGps->text().trimmed().split(','); // 右下角经纬度
    169. if (lt.count() != 2 || rd.count() != 2)
    170. return; // 判断输入是否正确
    171. int ltX = lonTotile(lt.at(0).toDouble(), z); // 计算左上角瓦片X
    172. int ltY = latTotile(lt.at(1).toDouble(), z); // 计算左上角瓦片Y
    173. int rdX = lonTotile(rd.at(0).toDouble(), z); // 计算右下角瓦片X
    174. int rdY = latTotile(rd.at(1).toDouble(), z); // 计算右下角瓦片Y
    175. ImageInfo info;
    176. info.z = z;
    177. info.format = format;
    178. int style = ui->com_amapStyle->currentData().toInt();
    179. int count = 1;
    180. if (style == 9)
    181. {
    182. count = 2; // 如果是下载卫星图 + 路网图则循环两次
    183. }
    184. for (int i = 0; i < count; i++)
    185. {
    186. if (count == 2)
    187. {
    188. if (i == 0)
    189. {
    190. style = 6; // 第一次下载卫星图
    191. info.format = "jpg";
    192. }
    193. else
    194. {
    195. style = 8; // 第二次下载路网图
    196. info.format = "png"; // 如果同时下载卫星图和路网图则路网图为透明png格式
    197. }
    198. }
    199. QString tempUrl = url.arg(ui->com_amapPrefix->currentText()); // 设置域名
    200. tempUrl += QString("&style=%1").arg(style); // 设置地图类型
    201. tempUrl += QString("&lang=%1").arg(ui->com_amapLang->currentData().toString()); // 设置语言
    202. tempUrl += QString("&scl=%1").arg(ui->com_amapScl->currentData().toInt()); // 设置图片尺寸,只在7 8生效
    203. tempUrl += QString("&ltype=%1").arg(ui->spin_amapLtype->value()); // 设置图片中的信息,只有 7 8有效
    204. for (int x = ltX; x <= rdX; x++)
    205. {
    206. info.x = x;
    207. for (int y = ltY; y <= rdY; y++)
    208. {
    209. info.url = tempUrl + QString("&x=%1&y=%2&z=%3").arg(x).arg(y).arg(z);
    210. info.y = y;
    211. m_infos.append(info);
    212. }
    213. }
    214. }
    215. }
    216. /**
    217. * @brief 初始化Bing地图配置
    218. */
    219. void MapInput::initBing()
    220. {
    221. // 服务器
    222. for (int i = 0; i < 8; i++)
    223. {
    224. ui->com_bingPrefix->addItem(QString("%1").arg(i));
    225. }
    226. // 地图语言
    227. ui->com_bingLang->addItem("中文", "zh-cn");
    228. ui->com_bingLang->addItem("英语", "en-US");
    229. // 地图类型
    230. ui->com_bingType->addItem("卫星地图", "a");
    231. ui->com_bingType->addItem("普通地图", "r");
    232. ui->com_bingType->addItem("混合地图", "h");
    233. ui->com_bingCstl->addItem("默认", "w4c");
    234. ui->com_bingCstl->addItem("白天", "vb"); // 白天道路地图
    235. ui->com_bingCstl->addItem("夜晚", "vbd"); // 夜晚道路图
    236. // 瓦片等级
    237. for (int i = 1; i < 21; i++)
    238. {
    239. ui->com_bingZ->addItem(QString("%1").arg(i));
    240. }
    241. // 填入下载格式
    242. ui->com_bingFormat->addItem("jpg");
    243. ui->com_bingFormat->addItem("png");
    244. ui->com_bingFormat->addItem("bmp");
    245. }
    246. /**
    247. * @brief 计算Bing地图的下载信息(这些url可能会失效,后续会使用其他方式下载)
    248. * https://learn.microsoft.com/en-us/bingmaps/rest-services/directly-accessing-the-bing-maps-tiles
    249. */
    250. void MapInput::getBingMapInfo()
    251. {
    252. //https://r1.tiles.ditu.live.com/tiles/r1321001.png?g=1001&mkt=zh-cn
    253. //http://dynamic.t2.tiles.ditu.live.com/comp/ch/r1321001.png?it=G,OS,L&mkt=en-us&cstl=w4c&ur=cn
    254. //http://ecn.t{0}.tiles.virtualearth.net/tiles/{1}{2}.png? g={4}
    255. //https://t0.dynamic.tiles.ditu.live.com/comp/ch/1320300313132?mkt=zh-CN&ur=CN&it=G,RL&n=z&og=894&cstl=vb
    256. //https://t1.dynamic.tiles.ditu.live.com/comp/ch/13203012200201?mkt=zh-CN&ur=cn&it=G,RL&n=z&og=894&cstl=vbd
    257. //https://dynamic.t1.tiles.ditu.live.com/comp/ch/1320300313313?it=Z,TF&L&n=z&key=AvquUWQgfy7VPqHn9ergJsp3Q_EiUft0ed70vZsX0_aqPABBdK07OkwrXWoGXsTG&ur=cn&cstl=vbd
    258. #define USE_URL 1
    259. #if (USE_URL == 0)
    260. // https://r1.tiles.ditu.live.com/tiles/r1321001.png?g=1001&mkt=zh-cn
    261. static QString url = "https://r%1.tiles.ditu.live.com/tiles/%2%3.%4?g=1001&mkt=%5"; // 街道图r支持中文
    262. #elif (USE_URL == 1)
    263. // http://dynamic.t2.tiles.ditu.live.com/comp/ch/r1321001.png?it=G,OS,L&mkt=en-us&cstl=w4c&ur=cn
    264. 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";
    265. #endif
    266. int z = ui->com_bingZ->currentText().toInt();
    267. QStringList lt = ui->line_LTGps->text().trimmed().split(','); // 左上角经纬度
    268. QStringList rd = ui->line_RDGps->text().trimmed().split(','); // 右下角经纬度
    269. if (lt.count() != 2 || rd.count() != 2)
    270. return; // 判断输入是否正确
    271. int ltX = lonTotile(lt.at(0).toDouble(), z); // 计算左上角瓦片X
    272. int ltY = latTotile(lt.at(1).toDouble(), z); // 计算左上角瓦片Y
    273. int rdX = lonTotile(rd.at(0).toDouble(), z); // 计算右下角瓦片X
    274. int rdY = latTotile(rd.at(1).toDouble(), z); // 计算右下角瓦片Y
    275. QString format = ui->com_bingFormat->currentText();
    276. ImageInfo info;
    277. info.z = z;
    278. info.format = format;
    279. int prefix = ui->com_bingPrefix->currentIndex();
    280. QString lang = ui->com_bingLang->currentData().toString(); // 语言
    281. QString type = ui->com_bingType->currentData().toString(); // 类型
    282. QString cstl = ui->com_bingCstl->currentData().toString(); // 样式
    283. QPoint point;
    284. for (int x = ltX; x <= rdX; x++)
    285. {
    286. info.x = x;
    287. point.setX(x);
    288. for (int y = ltY; y <= rdY; y++)
    289. {
    290. info.y = y;
    291. point.setY(y);
    292. QString quadKey = Bing::tileXYToQuadKey(point, z); // 将xy转为quadkey
    293. #if (USE_URL == 0)
    294. info.url = url.arg(prefix).arg(type).arg(quadKey).arg(format).arg(lang);
    295. #elif (USE_URL == 1)
    296. info.url = url.arg(prefix).arg(type).arg(quadKey).arg(format).arg(lang).arg(cstl);
    297. #endif
    298. m_infos.append(info);
    299. }
    300. }
    301. }

4.3 多线程下载

  • downloadthreads.h

    1. #ifndef DOWNLOADTHREADS_H
    2. #define DOWNLOADTHREADS_H
    3. #include "mapStruct.h"
    4. #include <QFutureWatcher>
    5. #include <QObject>
    6. class DownloadThreads : public QObject
    7. {
    8. Q_OBJECT
    9. public:
    10. explicit DownloadThreads(QObject* parent = nullptr);
    11. ~DownloadThreads();
    12. // 传入需要下载的瓦片信息
    13. void getImage(QList<ImageInfo> infos);
    14. void quit(); // 退出下载
    15. signals:
    16. void finished(ImageInfo info); // 返回下载后的瓦片,由于QImage为共享内存,所以传递不需要考虑太多性能
    17. private:
    18. QFuture<void> m_future;
    19. QList<ImageInfo> m_infos;
    20. };
    21. #endif // DOWNLOADTHREADS_H
  • downloadthreads.cpp

    1. /********************************************************************
    2. * 文件名: downloadthreads.cpp
    3. * 时间: 2024-03-31 20:32:58
    4. * 开发者: mhf
    5. * 邮箱: 1603291350@qq.com
    6. * 说明: 多线程下载瓦片地图
    7. * ******************************************************************/
    8. #include "downloadthreads.h"
    9. #include <QtConcurrent>
    10. #include <qnetworkaccessmanager.h>
    11. #include <qnetworkreply.h>
    12. static DownloadThreads* g_this = nullptr;
    13. DownloadThreads::DownloadThreads(QObject *parent) : QObject(parent)
    14. {
    15. g_this = this; // 记录当前 this指针,用于传递信号
    16. }
    17. DownloadThreads::~DownloadThreads()
    18. {
    19. g_this = nullptr;
    20. quit();
    21. }
    22. /**
    23. * @brief 下载瓦片
    24. * @param info
    25. * @return
    26. */
    27. void getUrl(ImageInfo info)
    28. {
    29. QNetworkAccessManager manager;
    30. QNetworkReply* reply = manager.get(QNetworkRequest(QUrl(info.url)));
    31. // 等待返回
    32. QEventLoop loop;
    33. QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    34. loop.exec();
    35. if(reply->error() == QNetworkReply::NoError)
    36. {
    37. QByteArray buf = reply->readAll();
    38. info.img.loadFromData(buf);
    39. }
    40. else
    41. {
    42. info.count++;
    43. if(info.count < 3)
    44. {
    45. getUrl(info); // 下载失败重新下载
    46. return;
    47. }
    48. else
    49. {
    50. qWarning() << "下载失败:" << reply->errorString();
    51. }
    52. }
    53. if(g_this)
    54. {
    55. emit g_this->finished(info); // 通过信号将下载后的瓦片传出去
    56. }
    57. }
    58. /**
    59. * @brief 调用线程池下载瓦片
    60. * @param infos
    61. */
    62. void DownloadThreads::getImage(QList<ImageInfo> infos)
    63. {
    64. m_infos = infos; // 这里不能使用infos,因为会在函数退出释放
    65. #if 0 // 由于map使用的是全局线程池,所以可以查看、设置线程数
    66. qDebug() <<QThreadPool::globalInstance()->maxThreadCount(); // 查看最大线程数
    67. QThreadPool::globalInstance()->setMaxThreadCount(1); // 设置最大线程数
    68. #endif
    69. m_future = QtConcurrent::map(m_infos, getUrl);
    70. }
    71. /**
    72. * @brief 退出下载
    73. */
    74. void DownloadThreads::quit()
    75. {
    76. if(m_future.isRunning()) // 判断是否在运行
    77. {
    78. m_future.cancel(); // 取消下载
    79. m_future.waitForFinished(); // 等待退出
    80. }
    81. }

5、源码地址

6、参考

原文链接:https://www.cnblogs.com/IntelligencePointer/p/18287298

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号