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

news2024/9/27 17:27:53

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*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;
            }
        }
    }
    
    
  • 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

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

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

相关文章

Android 事件分发:为什么有时候会出现事件冲突?事件的顺序是如何的?出现事件冲突如何解决呢?比如为什么左右可以滑动,而上下却不行?

目录&#xff1a; 一、为什么要学习事件呢&#xff1f; 1.在开发复杂的应用时&#xff0c;经常需要处理复杂的用户交互逻辑。学习事件分发机制可以帮助你更好地控制事件的传递和处理流程&#xff0c;从而解决一些复杂的交互问题&#xff0c;如滑动冲突、点击穿透等。 2.面试需…

NLP笔记:BLEU

1 介绍 bleu是一种文本评估算法&#xff0c;它是用来评估机器翻译跟专业人工翻译之间的对应关系核心思想就是机器翻译越接近专业人工翻译&#xff0c;质量就越好&#xff0c;经过bleu算法得出的分数可以作为机器翻译质量的一个指标 2 BLEU原理 2.1 N-gram BLEU采用了N-gram…

NLP(三):词向量

自然语言处理&#xff0c;处理的是自然的需要&#xff0c;通过分词后得到我们想要的词&#xff0c;但是不可能直接把这种自然语言传递给计算机来理解。这时候就有一个概念叫词向量&#xff0c;用来表示词的特征向量或表征。 一&#xff0c;词向量的表示 词向量的表示主要有两…

C——四种排序方法

这一篇文章我将要详细讲解四种排序方法 1.冒泡排序 冒泡排序是我们首先接触的排序方法&#xff0c;他通过两次循环完成。 /*冒泡排序*/&#xff08;升序&#xff09; void maopao(int *a,int n) {int i;for(i 0; i < n-1; i){for (int j 0; j < n - 1 - i; j){if (a[…

mars3D使用 POI 查询、限定范围

mars3D使用 一、mars3D中使用 geocoder 进行 POI 查询二、限定范围1.初始化时渲染2.重新渲染 总结 一、mars3D中使用 geocoder 进行 POI 查询 在json文件或者自己的mapOptions中配置token "token":{"tianditu":"e5c3984ced09bc1f55e8e1107fdc5a6b&q…

论文速览【LLM-agent】—— 【ReAct】Synergizing Reasoning and Acting in Language Models

文章链接&#xff1a;ReAct: Synergizing Reasoning and Acting in Language Models发表&#xff1a;ICLR 2023领域&#xff1a;LLM agent 摘要&#xff1a;尽管大型语言模型&#xff08;LLMs&#xff09;在语言理解和交互式决策任务中展示了令人印象深刻的能力&#xff0c;但它…

正弦波振荡器工作原理及频率稳定性条件

晶发电子专注17年晶振生产,晶振产品包括石英晶体谐振器、振荡器、贴片晶振、32.768Khz时钟晶振、有源晶振、无源晶振等&#xff0c;产品性能稳定,品质过硬,价格好,交期快.国产晶振品牌您值得信赖的晶振供应商。 正弦波振荡器是一种能够自动将直流电转换为特定频率和振幅的正弦交…

解析云原生架构中两大核心原则

1.云原生架构是什么 云原生架构是一种设计和构建应用程序的现代方法&#xff0c;以微服务、容器化、持续集成和持续部署&#xff08;CI/CD&#xff09;等技术为基础&#xff0c;使应用能够在云环境中动态运行。云原生架构强调解耦合、弹性和自动化&#xff0c;开发团队在独立的…

【高级编程】万字整理集合框架 迭代器 泛型(含方法案例)

文章目录 集合框架集合接口集合类ArrayListLinkedListHashSet 迭代器 IteratorMap 接口泛型Collections 工具类 集合框架 如果并不知道程序运行时会需要多少对象&#xff0c;或者需要更复杂方式存储对象——可以使用Java集合框架 Java集合框架提供了一套性能优良、使用方便的…

@EqualsAndHashCode注解使用

一&#xff0c;EqualsAndHashCode注解来自于Lombok EqualsAndHashCode 是 Lombok 库提供的一个注解&#xff0c;用于自动生成 equals 和 hashCode 方法。这两个方法在 Java 中非常重要&#xff0c;特别是在集合框架中使用时&#xff0c;它们确保了对象的正确比较和哈希值的一致…

YOLOv8改进 | 注意力篇 | YOLOv8引入LSK注意力机制

1. LSK介绍 1.1 摘要: 最近关于遥感目标检测的研究主要集中在改进定向边界框的表示上,但忽略了遥感场景中呈现的独特先验知识。 这种先验知识可能很有用,因为在没有参考足够远距离上下文的情况下,可能会错误地检测微小的遥感物体,并且不同类型物体所需的远距离上下文可能…

HarmonyOS开发实战( Beta5版)优化实践/合理使用缓存提升性能

简介 随着应用功能的日益丰富与复杂化&#xff0c;数据加载效率成为了衡量应用性能的重要指标。不合理的加载策略往往导致用户面临长时间的等待&#xff0c;这不仅损害了用户体验&#xff0c;还可能引发用户流失。因此&#xff0c;合理运用缓存技术变得尤为重要。 系统提供了P…

uniapp组件用法

一. 什么是组件,有什么好处? 在uni-app中&#xff0c;组件是构成应用的基本单位&#xff0c;它们是用来定义用户界面的一部分&#xff0c;并且通常包含了视图和逻辑。组件的设计使得开发者能够以声明式的方式构建应用界面&#xff0c;并且通过组件化的开发方式来提高代码的复…

损失函数、成本函数cost 、最大似然估计

一、损失函数 什么是损失函数&#xff1f; 【深度学习】一文读懂机器学习常用损失函数&#xff08;Loss Function&#xff09;-腾讯云开发者社区-腾讯云 损失函数&#xff08;loss function&#xff09;是用来估量模型的预测值f(x)与真实值Y的不一致程度&#xff0c;它是一个…

Python 新手必看:如何用 unittest 写出高质量代码?

文末赠免费精品编程资料~~ 在 Python中 &#xff0c;unittest 模块是进行单元测试的强大工具。无论你是初学者还是有经验的开发者&#xff0c;单元测试都是确保代码质量的重要一环。而 unittest 模块就是让这一过程变得简单、快捷的利器。 什么是单元测试&#xff1f; 在进入…

浩瀚麦克风怎么样?西圣、罗德、神牛领夹麦克风全网巅峰PK测评

​一台优质专业的无线领夹麦克风能够清晰、稳定地收录声音&#xff0c;提升音频录制质量。而劣质的无线领夹麦克风则可能出现声音不清晰、信号不稳定、续航短等各种问题。作为一名资深的数码测评师&#xff0c;我已经测评过了好几十款无线领夹麦克风&#xff0c;今天将从麦克风…

Python应用指南:获取高德地铁站点数据(单城市版)

书接上文&#xff0c;上篇文章是一次性下载全国所以城市的地铁站点数据&#xff0c;但是可视化的过程需要手动把换乘站给一个个复制出来分配到其他各个经过的线路&#xff0c;还需要核对站点顺序不能出错&#xff0c;如果只需要单个城市的数据呢&#xff1f;另外能不能直接生成…

【复杂系统系列(初级)】自动调节动态平衡模型——生物体的稳态机制

【通俗理解】自动调节动态平衡模型——生物体的稳态机制 关键词提炼 #自动调节 #动态平衡 #生物体稳态 #反馈机制 #体温调节 #微分方程模型 第一节&#xff1a;自动调节动态平衡模型的类比与核心概念 1.1 自动调节动态平衡模型的类比 自动调节动态平衡模型可以被视为生物体…

grpc-spring 通信(监控视频传输)

先看效果 这是微软相机&#xff0c;22ms延迟 &#xff08;不走网络存粹寄存器和内存的通信&#xff09;这是程序抓取摄像头然后传给client&#xff0c;client的java窗口展示的&#xff0c;延时也是22ms&#xff08;对了localhost好像也不走网络吧&#xff09; 几个点 1.openc…

大功率舞台灯调光调色方案 | 支持深度调光,多路输出调光 36V/48V/60V FP7126

在舞台演出中&#xff0c;灯光扮演着非常重要的角色&#xff0c;它不仅可以烘托氛围&#xff0c;营造氛围&#xff0c;更能够为表演者增添光彩&#xff0c;塑造形象。在博物馆场所中&#xff0c;突出展品细节。根据灯光用途和适用类型&#xff0c;舞台灯可以细分为聚光灯、泛光…