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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AWS-亚马逊网络服务(基础服务)-AWS 定价计算器-概述与动手部署:

让我们来概述并亲身实践如何使用 AWS 定价计算器来计算 概述&#xff1a; AWS 定价计算器是 Amazon Web Services (AWS) 提供的基于 Web 的工具&#xff0c;可帮助用户估算其特定用例的 AWS 服务成本。欢迎来到雲闪世界。 它允许客户建模他们的基础设施并根据他们打算使用的…

大数据智能风控核心:模型

概述 模型 线性判别分析方法&#xff0c;Sir Ronald Fisher最早提出模型评分的概念。 个人FICO模型信用分。 巴塞尔委员会发布巴塞尔Ⅱ协议&#xff0c;推出内部评级法&#xff08;Internal Rating Based Approach&#xff0c;IRB&#xff09;​。IRB综合考虑客户评级和债项…

【STM32】BKP备份寄存器与RTC实时时钟

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 BKP简介 BKP代码注解 读写备份寄存器 复位备份寄存器 BKP代码 RTC简介 RTC代码注解 RTCCLK时钟源选择 分频器配置 时钟同步 RTC代码 MyRTC.h MyRTC.c main.c BKP简介 BKP&…

手把手教你从开发进度划分测试

一.单元测试&#xff08;Unit Testing&#xff09; 单元测试&#xff1a;软件单元测试的对象是可独立编译或汇编的程序模块。测试的对象是软件测试中的最小单位&#xff1a;模块。 测试阶段&#xff1a;编码后或者编码前&#xff08;TDD&#xff1a;测试驱动开发&#xff09;…

【网络基础】探索 NAT 技术:IP 转换、NAPT、NAT穿越及代理服务器

文章目录 1. 前言2. IP 转换过程3. NAPT① 基本概念② 工作原理③ 优缺点④ 实际应用 4. 缺陷5. NAT 穿越① 概述② 示例 6. NAT 与 代理服务器① 代理服务器与NAT的区别&#xff1a;② 正向代理 / 反向代理 服务器 1. 前言 NAT&#xff08;网络地址转换&#xff09;是一种常见…

Python数据清洗基础

在Python中进行数据清洗和可视化是一个多步骤的过程&#xff0c;涉及到数据的读取、预处理、分析和图形表示。以下是一些关键步骤和代码示例&#xff0c;这些步骤可以帮助你从原始数据中提取有价值的信息&#xff0c;并以直观的方式展示。 数据清洗 读取数据&#xff1a; im…

【Python 千题 —— 基础篇】评论倾向分析

Python 千题持续更新中 …… 脑图地址 👉:⭐https://twilight-fanyi.gitee.io/mind-map/Python千题.html⭐ 题目描述 在某个电商平台的评论系统中,用户可以提交商品评论。为了分析评论的情感倾向,我们需要编写一个程序来处理用户评论,并对评论内容进行简单的分析和处理。…

Mac/Linux系统matplotlib中文支持问题

背景 matplotlib是python中最常用的数据可视化分析工具&#xff0c;Mac和Linux系统无中文字体&#xff0c;不支持中文显示&#xff08;希望后续可以改进&#xff09;&#xff0c;需要进行字体的下载和设置才能解决。笔者经过实践&#xff0c;发现Mac系统和Linux系统解决方案略…

1.【R语言】R语言的下载和安装

R语言是一种开源编程语言&#xff0c;它提供了丰富的统计模型和图形绘制功能&#xff0c;广泛用于数据科学、统计分析、数据挖掘和机器学习。R有一个活跃的社区和大量的包&#xff0c;可以满足各种需求&#xff0c;如数据清洗、绘图和报告生成。其强大的数据处理能力和灵活的可…

Ant Design vue 多层for循环form表单自定义校验

数据结构如下&#xff1a;三维数组。 注意&#xff1a;<a-form-model>一定得写在for外面&#xff01;&#xff01;&#xff01;&#xff01; <!-- 弹出框 --> <a-modal:title"title":dialog-style"{ top: 20px }":visible"visible&quo…

Datawhale X 李宏毅苹果书 AI夏令营 入门 Task2-了解线性模型

目录 线性模型分段线性曲线模型变形 线性模型 输入的特征 x 乘上一个权重&#xff0c;再加上一个偏置就得到预测的结果&#xff0c;这样的模型称为线性模型。 分段线性曲线 线性模型有很大的限制&#xff0c;这一种来自于模型的限制称为模型的偏差&#xff0c;无法模拟真实的…

Bluetooth: att protocol

一篇搞懂 ATT 支持的东西都有什么。 READ_BY_GROUP_TYPE_REQ/RSP 如下是 Spec 内容&#xff1a; The attributes returned shall be the attributes with the lowest handles within the handle range. These are known as the requested attributes.If the attributes with th…

零知识证明-基础数学(二)

零知识证明(Zero—Knowledge Proof)&#xff0c;是指一种密码学工具&#xff0c;允许互不信任的通信双方之间证明某个命题的有效性&#xff0c;同时不泄露任何额外信息 导数、偏导数 ,互质数&#xff0c;费马小定理&#xff0c;欧拉定理 1 导数 导数是微积分学中的重要概念&am…

科研绘图系列:R语言组合图形绘图

介绍 柱状图、箱线图和棒棒图组合 加载R包 # Library library(ggplot2) library(dplyr) library(forcats)读取数据 data <- data.frame(name=c("north","south","south-east","north-west","south-west","north…

jquery下载的例子如何应用到vue中

参考测试圈相亲平台开发流程&#xff08;4&#xff09;&#xff1a;选个漂亮的首页 (qq.com) 下载的文件夹解压到v_love项目的pubilc下的static文件夹内&#xff0c;这里放的都是我们的静态资源。 打开文件夹内的index.html&#xff0c;我们先确定下它是不是我们要的东西&…

基于yolov10的PCB检测算法研究

内容&#xff1a;项目将YOLOV10创新后的PCB检测算法成功部署到GD32H757上&#xff0c;实现PCB缺陷的工业产线实时检测。 项目主要支持开源代码&#xff1a;HomiKetalys/gd32ai-modelzoo: Provide deployable deep learning models on gd32 (github.com) &#xff08;想了解将…

信息打点-红队工具篇FofaQuakeKunyuSuize水泽Arl灯塔

知识点&#xff1a; 1、网络空间四大引擎-Fofa&Quake&Shodan&Zoomeye 2、自动化信息收集项目-ARL灯塔&Suize水泽&Kunyu坤舆 3、单点功能信息收集项目-企查&子域名&指纹识别&社工信息 黑暗引擎&#xff1a; https://fofa.info https://qua…

GPT-4 vs LLaMA3.1:核心技术架构与应用场景对比

目录 前言 一、GPT-4 的核心技术架构 1.1 Transformer 结构概述 1.2 GPT-4 的主要组成部分 1.3 GPT-4 的创新与改进 二、LLaMA3.1 的核心技术架构 2.1 模型概述 2.2 LLaMA3.1 的主要组成部分 2.3 LLaMA3.1 的创新与改进 三、GPT-4 和 LLaMA3.1 的主要差异 3.1 模型规…

Native开发与逆向第五篇 - hook log打印

开发demo 新建native项目&#xff0c;实现log打印字符串。 下载地址&#xff1a;https://download.csdn.net/download/u013170888/89698015 #include <android/log.h> #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "JNI_LOG", __VA_ARGS__)exte…

WireShark网络分析~部署方式

一、《Wireshark网络分析就这么简单》 第一章学习 声明&#xff1a;文章只限于网络学习和实验&#xff0c;请遵守《网络安全法》。 第一章问题一&#xff1a;两台服务器A和B的网络配置如下(见图1)&#xff0c;B的子网掩码本应该是255.255.255.0&#xff0c;被不小心配成了255.…