C++(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例2

2024-08-31 01:28

本文主要是介绍C++(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C++(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例2

文章目录

  • C++(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例2
    • 1、概述
    • 2、实现效果
    • 3、主要代码
    • 4、源码地址

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

1、概述

  1. 支持多线程加载显示本地离线瓦片地图(墨卡托投影);
  2. 瓦片切片规则以左上角为原点(谷歌、高德、ArcGis等),不支持百度瓦片规则;
  3. 支持显示瓦片网格、编号信息。
  4. 支持鼠标滚轮缩放切换地图层级。
  5. 支持鼠标拖拽。
  6. 采用z/x/y层级瓦片存储格式。
  7. 在单文件中实现所有主要功能,简单便于理解。
  8. 以北纬85.05,西经-180为坐标原点【绝对像素坐标】。

开发环境说明

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

2、实现效果

使用瓦片地图工具下载z/x/y存储格式的瓦片地图进行显示。
在这里插入图片描述

3、主要代码

  • 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;}}
    }
  • mapgraphicsview.h文件

    #ifndef MAPGRAPHICSVIEW_H
    #define MAPGRAPHICSVIEW_H#include "mapStruct.h"
    #include <QGraphicsView>class MapGraphicsView : public QGraphicsView
    {Q_OBJECT
    public:explicit MapGraphicsView(QWidget* parent = nullptr);~MapGraphicsView() override;void setRect(QRect rect);void drawImg(const ImageInfo& info);void clear();signals:void updateImage(const ImageInfo& info);   // 添加瓦片图void zoom(bool flag);                      // 缩放 true:放大void showRect(QRect rect);void mousePos(QPoint pos);protected:void mouseMoveEvent(QMouseEvent* event) override;void wheelEvent(QWheelEvent* event) override;private:void getShowRect();   // 获取显示范围private:QGraphicsScene* m_scene = nullptr;QPointF m_pos;QPointF m_scenePos;
    };#endif   // MAPGRAPHICSVIEW_H
  • mapgraphicsview.cpp文件

    #include "mapgraphicsview.h"#include "bingformula.h"
    #include <QDebug>
    #include <QGraphicsItem>
    #include <QMouseEvent>
    #include <QScrollBar>
    #include <QWheelEvent>MapGraphicsView::MapGraphicsView(QWidget* parent): QGraphicsView(parent)
    {m_scene = new QGraphicsScene();this->setScene(m_scene);this->setDragMode(QGraphicsView::ScrollHandDrag);   // 鼠标拖拽this->setMouseTracking(true);                       // 开启鼠标追踪connect(this, &MapGraphicsView::updateImage, this, &MapGraphicsView::drawImg);
    }MapGraphicsView::~MapGraphicsView() {}/*** @brief       缩放后设置场景大小范围* @param rect*/
    void MapGraphicsView::setRect(QRect rect)
    {m_scene->setSceneRect(rect);// 将显示位置移动到缩放之前的位置this->horizontalScrollBar()->setValue(qRound(m_scenePos.x() - m_pos.x()));this->verticalScrollBar()->setValue(qRound(m_scenePos.y() - m_pos.y()));getShowRect();
    }/*** @brief       绘制瓦片图* @param info*/
    void MapGraphicsView::drawImg(const ImageInfo& info)
    {// 绘制瓦片图auto item = m_scene->addPixmap(info.img);QPoint pos = Bing::tileXYToPixelXY(QPoint(info.x, info.y));item->setPos(pos);// 绘制边框auto itemR = m_scene->addRect(0, 0, 255, 255, QPen(Qt::red));itemR->setPos(pos);
    }/*** @brief 清空所有瓦片*/
    void MapGraphicsView::clear()
    {m_scene->clear();
    }/*** @brief        获取鼠标移动坐标* @param event*/
    void MapGraphicsView::mouseMoveEvent(QMouseEvent* event)
    {QGraphicsView::mouseMoveEvent(event);emit mousePos(this->mapToScene(event->pos()).toPoint());getShowRect();
    }/*** @brief        鼠标滚轮缩放* @param event*/
    void MapGraphicsView::wheelEvent(QWheelEvent* event)
    {m_pos = event->pos();                          // 鼠标相对于窗口左上角的坐标m_scenePos = this->mapToScene(event->pos());   // 鼠标在场景中的坐标if (event->angleDelta().y() > 0){m_scenePos = m_scenePos * 2;   // 放大emit this->zoom(true);}else{m_scenePos = m_scenePos / 2;   // 缩小emit this->zoom(false);}
    }/*** @brief 获取当前场景的显示范围(场景坐标系)*/
    void MapGraphicsView::getShowRect()
    {QRect rect;rect.setTopLeft(this->mapToScene(0, 0).toPoint());rect.setBottomRight(this->mapToScene(this->width(), this->height()).toPoint());emit this->showRect(rect);
    }

4、源码地址

  • github
  • gitee

这篇关于C++(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1122494

相关文章

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

Redis延迟队列的实现示例

《Redis延迟队列的实现示例》Redis延迟队列是一种使用Redis实现的消息队列,本文主要介绍了Redis延迟队列的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、什么是 Redis 延迟队列二、实现原理三、Java 代码示例四、注意事项五、使用 Redi

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

基于Qt Qml实现时间轴组件

《基于QtQml实现时间轴组件》时间轴组件是现代用户界面中常见的元素,用于按时间顺序展示事件,本文主要为大家详细介绍了如何使用Qml实现一个简单的时间轴组件,需要的可以参考下... 目录写在前面效果图组件概述实现细节1. 组件结构2. 属性定义3. 数据模型4. 事件项的添加和排序5. 事件项的渲染如何使用

在Pandas中进行数据重命名的方法示例

《在Pandas中进行数据重命名的方法示例》Pandas作为Python中最流行的数据处理库,提供了强大的数据操作功能,其中数据重命名是常见且基础的操作之一,本文将通过简洁明了的讲解和丰富的代码示例,... 目录一、引言二、Pandas rename方法简介三、列名重命名3.1 使用字典进行列名重命名3.编

Python使用Colorama库美化终端输出的操作示例

《Python使用Colorama库美化终端输出的操作示例》在开发命令行工具或调试程序时,我们可能会希望通过颜色来区分重要信息,比如警告、错误、提示等,而Colorama是一个简单易用的Python库... 目录python Colorama 库详解:终端输出美化的神器1. Colorama 是什么?2.