vc 做网站源码,注册一个免费的网站吗,消防网站建设目标,做网站语言服务器 空间Qt-GIS开发-简易瓦片地图下载器 文章目录 Qt-GIS开发-简易瓦片地图下载器1、概述2、安装openssl3、实现效果4、主要代码4.1 算法函数4.2 瓦片地图下载url拼接4.3 多线程下载 5、源码地址6、参考 更多精彩内容#x1f449;个人内容分类汇总 #x1f448;#x1f449;GIS开发 …Qt-GIS开发-简易瓦片地图下载器 文章目录 Qt-GIS开发-简易瓦片地图下载器1、概述2、安装openssl3、实现效果4、主要代码4.1 算法函数4.2 瓦片地图下载url拼接4.3 多线程下载 5、源码地址6、参考 更多精彩内容个人内容分类汇总 GIS开发
1、概述 支持单线程、多线程下载瓦片地图。使用QNetworkAccessManager、QNetworkReply实现http、https下载功能支持下载多样式arcGis瓦片地图支持下载多样式高德瓦片地图支持多样式Bing地图下载Qt中https下载功能需要安装openssl库。本文中不会详细说瓦片地图的原理写得好的文章太多了。 开发环境说明
系统Windows11、Ubuntu20.04Qt版本Qt 5.14.2编译器MSVC2017-64、GCC/G64
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\binD:\Qt\Qt5.14.2\5.14.2\mingw73_64\bin
3、实现效果 无需注册、无需key进行瓦片地图下载地址可能会失效大量下载可能会限速仅作为学习使用。 4、主要代码
项目文件结构
4.1 算法函数 bingformula.h文件 #ifndef BINGFORMULA_H
#define BINGFORMULA_H
#include QPoint
#include QtGlobalnamespace 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* 邮箱 1603291350qq.com* 说明 适用于Bing瓦片地图的算法* ******************************************************************/
#include bingformula.h
#include qstring.h
#include QtMathstatic const qreal g_EarthRadius 6378137; // 赤道半径/*** 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-23bing地图没有0级别最低级别为1由4块瓦片组成* return 以像素为单位的地图宽度和高度。*/
uint Bing::mapSize(int level)
{uint w 256; // 第0级别为256*256return (w level);
}/*** brief 计算指定纬度、级别的地面分辨率不同纬度分辨率不同* param lat 纬度* param level 地图级别 1-23bing地图没有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-23bing地图没有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.hnamespace Ui {
class MapInput;
}class MapInput : public QWidget
{Q_OBJECTpublic:explicit MapInput(QWidget *parent nullptr);~MapInput();const QListImageInfo getInputInfo(); // 获取下载地图所需的输入信息private:// ArcGisvoid initArcGis();void getArcGisMapInfo();// 高德void initAMap();void getAMapInfo();// Bing地图void initBing();void getBingMapInfo();private:Ui::MapInput *ui;QListImageInfo m_infos; // 保存下载瓦片图片的信息
};#endif // MAPINPUT_H mapinput.cpp /********************************************************************* 文件名 mapinput.cpp* 时间 2024-01-19 22:22:37* 开发者 mhf* 邮箱 1603291350qq.com* 说明 生成地图下载的输入信息* ******************************************************************/
#include mapinput.h
#include bingformula.h
#include formula.h
#include ui_mapinput.h
#include QDebugMapInput::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 QListImageInfo 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%1y%2z%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?g1001mktzh-cn//http://dynamic.t2.tiles.ditu.live.com/comp/ch/r1321001.png?itG,OS,Lmkten-uscstlw4curcn//http://ecn.t{0}.tiles.virtualearth.net/tiles/{1}{2}.png? g{4}//https://t0.dynamic.tiles.ditu.live.com/comp/ch/1320300313132?mktzh-CNurCNitG,RLnzog894cstlvb//https://t1.dynamic.tiles.ditu.live.com/comp/ch/13203012200201?mktzh-CNurcnitG,RLnzog894cstlvbd//https://dynamic.t1.tiles.ditu.live.com/comp/ch/1320300313313?itZ,TFLnzkeyAvquUWQgfy7VPqHn9ergJsp3Q_EiUft0ed70vZsX0_aqPABBdK07OkwrXWoGXsTGurcncstlvbd#define USE_URL 1
#if (USE_URL 0)// https://r1.tiles.ditu.live.com/tiles/r1321001.png?g1001mktzh-cnstatic QString url https://r%1.tiles.ditu.live.com/tiles/%2%3.%4?g1001mkt%5; // 街道图r支持中文
#elif (USE_URL 1)// http://dynamic.t2.tiles.ditu.live.com/comp/ch/r1321001.png?itG,OS,Lmkten-uscstlw4curcnstatic QString url http://dynamic.t%1.tiles.ditu.live.com/comp/ch/%2%3.%4?itG,OS,Lmkt%5cstl%6urcn;
#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 QObjectclass DownloadThreads : public QObject
{Q_OBJECT
public:explicit DownloadThreads(QObject* parent nullptr);~DownloadThreads();// 传入需要下载的瓦片信息void getImage(QListImageInfo infos);void quit(); // 退出下载signals:void finished(ImageInfo info); // 返回下载后的瓦片由于QImage为共享内存所以传递不需要考虑太多性能private:QFuturevoid m_future;QListImageInfo m_infos;
};#endif // DOWNLOADTHREADS_H downloadthreads.cpp /********************************************************************* 文件名 downloadthreads.cpp* 时间 2024-03-31 20:32:58* 开发者 mhf* 邮箱 1603291350qq.com* 说明 多线程下载瓦片地图* ******************************************************************/
#include downloadthreads.h
#include QtConcurrent
#include qnetworkaccessmanager.h
#include qnetworkreply.hstatic 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(QListImageInfo 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、源码地址
githubgitee
6、参考
GIS开发一OpenLayers在线瓦片数据源汇总_在线瓦片图数据-CSDN博客Bing Maps Tile System - Bing Maps | Microsoft Learn