C++(Qt)-GIS开发-QGraphicsView显示在线瓦片地图

2024-08-31 01:04

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

C++(Qt)-GIS开发-QGraphicsView显示在线瓦片地图

文章目录

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

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

1、概述

  1. 支持加载显示在线瓦片地图(墨卡托投影);
  2. 瓦片切片规则以左上角为原点(谷歌、高德、ArcGis等),不支持百度瓦片规则;
  3. 支持显示瓦片网格、编号信息。
  4. 支持鼠标滚轮缩放切换地图层级。
  5. 支持鼠标拖拽。
  6. 支持显示瓦片编号、瓦片网格;
  7. 支持在线程池中快速下载在线瓦片;
  8. 以北纬85.05,西经-180为坐标原点【绝对像素坐标】。
  9. 默认支持下载显示多格式高德、Bing、ArcGis瓦片地图。
  10. 支持x/y/z、x/z/y、z/y/x任意顺序格式、quadKey格式的url。

开发环境说明

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

2、实现效果

在这里插入图片描述

3、主要代码

  • geturl.h

    #ifndef GETURL_H
    #define GETURL_H#include "mapStruct.h"
    #include <qfuture.h>
    #include <qset.h>
    #include <QObject>class GetUrlInterface : public QObject
    {Q_OBJECT
    public:static GetUrlInterface* getInterface(){static GetUrlInterface interface;return &interface;}signals:void update(ImageInfo info);             // 传出下载的瓦片图信息void updateTitle(int x, int y, int z);   // 传出下载的瓦片编号void showRect(QRect rect);   // 设置显示像素范围void setLevel(int level);    // 设置瓦片层级
    };class GetUrl : public QObject
    {Q_OBJECT
    public:explicit GetUrl(QObject* parent = nullptr);~GetUrl();void setUrl(QString url);   // 设置获取瓦片地图的源地址void getImg(QRect rect, int level);void showRect(QRect rect);void setLevel(int level);   // 设置瓦片层级private:void getTitle(QRect rect, int level);    // 获取所有需要下载的瓦片地图编号void getUrl();                           // 获取用于请求瓦片地图的信息void clear();                            // 清空内容void quit();                             // 退出下载void updateTitle(int x, int y, int z);   // 传出下载的瓦片编号private:QThread* m_thread = nullptr;QFuture<void> m_future;QRect m_rect;      // 显示瓦片地图像素范围int m_level = 5;   // 瓦片地图层级QString m_url;QSet<quint64> m_exist;        // 已经存在的瓦片地图编号QVector<ImageInfo> m_infos;   // 需要下载的瓦片地图信息
    };#endif   // GETURL_H
  • geturl.cpp

    /********************************************************************* 文件名: geturl.cpp* 时间:   2024-05-19 14:29:30* 开发者:  mhf* 邮箱:   1603291350@qq.com* 说明:   瓦片地图网络请求类* ******************************************************************/
    #include "geturl.h"
    #include "bingformula.h"
    #include <qnetworkaccessmanager.h>
    #include <qnetworkreply.h>
    #include <QDebug>
    #include <QSet>
    #include <QtConcurrent>GetUrl::GetUrl(QObject* parent): QObject{parent}
    {m_thread = new QThread;this->moveToThread(m_thread);m_thread->start();m_rect.setTopLeft(Bing::latLongToPixelXY(64.16, 56.115, m_level));m_rect.setBottomRight(Bing::latLongToPixelXY(148.66, 9.34, m_level));connect(GetUrlInterface::getInterface(), &GetUrlInterface::updateTitle, this, &GetUrl::updateTitle);connect(GetUrlInterface::getInterface(), &GetUrlInterface::showRect, this, &GetUrl::showRect);connect(GetUrlInterface::getInterface(), &GetUrlInterface::setLevel, this, &GetUrl::setLevel);
    }GetUrl::~GetUrl()
    {quit();clear();m_thread->quit();m_thread->wait();delete m_thread;
    }/*** @brief     设置瓦片地图源地址* @param url*/
    void GetUrl::setUrl(QString url)
    {if (url.isEmpty())return;quit();   // 退出下载后再清空数组,防止数据竞争clear();m_exist.clear();   // 清空已下载列表m_url = url;getImg(m_rect, m_level);   // 使用默认范围、层级更新地图
    }/*** @brief       下载瓦片* @param info* @return*/
    void httpGet(ImageInfo info)
    {QNetworkAccessManager manager;QSharedPointer<QNetworkReply> reply(manager.get(QNetworkRequest(QUrl(info.url))));// 等待返回QEventLoop loop;QObject::connect(reply.data(), &QNetworkReply::finished, &loop, &QEventLoop::quit);   // 等待获取完成QTimer::singleShot(5000, &loop, &QEventLoop::quit);                                   // 等待超时loop.exec();if (reply->error() == QNetworkReply::NoError){QByteArray buf = reply->readAll();if (!buf.isEmpty()){info.img.loadFromData(buf);if (!info.img.isNull()){emit GetUrlInterface::getInterface() -> update(info);emit GetUrlInterface::getInterface() -> updateTitle(info.x, info.y, info.z);return;}}}info.count++;if (info.count < 3){httpGet(info);   // 下载失败重新下载return;}else{qWarning() << "下载失败:" << reply->errorString();}
    }/*** @brief       获取瓦片地图* @param rect  瓦片地图的像素范围* @param level 瓦片地图的级别*/
    void GetUrl::getImg(QRect rect, int level)
    {if (rect.isEmpty())return;if (level > 22 || level < 0)return;m_rect = rect;m_level = level;if (m_future.isRunning())   // 判断是否在运行{m_future.cancel();   // 取消下载}clear();   // 清空待下载列表getTitle(rect, level);   // 获取所有需要加载的瓦片编号qInfo() << "获取瓦片数:" << m_infos.count();getUrl();                                         // 将瓦片编号转为urlm_future = QtConcurrent::map(m_infos, httpGet);   // 在线程池中下载瓦片图
    }/*** @brief      设置获取瓦片地图的像素范围* @param rect*/
    void GetUrl::showRect(QRect rect)
    {if (rect.isEmpty())return;getImg(rect, m_level);
    }/*** @brief       通过设置显示瓦片层级别完成缩放显示* @param level*/
    void GetUrl::setLevel(int level)
    {if ((level < 0) || (level > 23)){return;}if (m_level != level){m_exist.clear();   // 清空已下载列表}m_level = level;
    }/*** @brief       获取瓦片编号* @param rect* @param level*/
    void GetUrl::getTitle(QRect rect, int level)
    {QPoint tl = Bing::pixelXYToTileXY(rect.topLeft());QPoint br = Bing::pixelXYToTileXY(rect.bottomRight());quint64 value = 0;ImageInfo info;info.z = level;int max = qPow(2, level);   // 最大瓦片编号for (int x = tl.x(); x <= br.x(); x++){if (x < 0)continue;if (x >= max)break;info.x = x;for (int y = tl.y(); y <= br.y(); y++){if (y < 0)continue;if (y >= max)break;value = ((quint64) level << 48) + (x << 24) + y;if (!m_exist.contains(value)){info.y = y;m_infos.append(info);}}}
    }/*** @brief 获取用于请求瓦片地图的信息*/
    void GetUrl::getUrl()
    {if (m_url.contains("{x}"))   // XYZ格式{QString url = m_url;url.replace("{x}", "%1");url.replace("{y}", "%2");url.replace("{z}", "%3");for (int i = 0; i < m_infos.count(); i++){m_infos[i].url = url.arg(m_infos[i].x).arg(m_infos[i].y).arg(m_infos[i].z);}}else if (m_url.contains("{q}"))   // Bing的quadKey格式{QString url = m_url;url.replace("{q}", "%1");QPoint point;for (int i = 0; i < m_infos.count(); i++){point.setX(m_infos[i].x);point.setY(m_infos[i].y);QString quadKey = Bing::tileXYToQuadKey(point, m_infos[i].z);   // 将xy转为quadkeym_infos[i].url = url.arg(quadKey);}}else{qDebug() << "url格式未定义";}
    }/*** @brief 清空内容*/
    void GetUrl::clear()
    {QVector<ImageInfo> info;m_infos.swap(info);
    }/*** @brief 退出下载*/
    void GetUrl::quit()
    {if (m_future.isRunning())   // 判断是否在运行{m_future.cancel();            // 取消下载m_future.waitForFinished();   // 等待退出}
    }/*** @brief     将下载成功的瓦片编号添加进已下载列表,已经下载的瓦片在后续不进行下载* @param x* @param y* @param z*/
    void GetUrl::updateTitle(int x, int y, int z)
    {quint64 value = (quint64(z) << 48) + (x << 24) + y;m_exist.insert(value);
    }
  • mapgraphicsview.h文件

    #ifndef MAPGRAPHICSVIEW_H
    #define MAPGRAPHICSVIEW_H#include "graphicsitemgroup.h"
    #include "mapStruct.h"
    #include <QGraphicsView>class MapGraphicsView : public QGraphicsView
    {Q_OBJECT
    public:explicit MapGraphicsView(QWidget* parent = nullptr);~MapGraphicsView() override;void setRect(int level);void drawImg(const ImageInfo& info);void clear();signals:void updateImage(const ImageInfo& info);   // 添加瓦片图void showRect(QRect rect);void mousePos(QPoint pos);protected:void mousePressEvent(QMouseEvent* event) override;void mouseReleaseEvent(QMouseEvent* event) override;void wheelEvent(QWheelEvent* event) override;void resizeEvent(QResizeEvent* event) override;void showEvent(QShowEvent* event) override;private:void getShowRect();   // 获取显示范围private:QGraphicsScene* m_scene = nullptr;int m_level = 5;           // 当前显示瓦片等级bool m_moveView = false;   // 鼠标移动显示视图QPointF m_pos;QPointF m_scenePos;QHash<quint16, GraphicsItemGroup*> m_itemGroup;   // 瓦片图元组
    };#endif   // MAPGRAPHICSVIEW_H
  • mapgraphicsview.cpp文件

    #include "mapgraphicsview.h"#include "bingformula.h"
    #include "geturl.h"
    #include <qthread.h>
    #include <QDebug>
    #include <QFont>
    #include <QGraphicsItem>
    #include <QMouseEvent>
    #include <QScrollBar>
    #include <QWheelEvent>
    #include <QtMath>MapGraphicsView::MapGraphicsView(QWidget* parent): QGraphicsView(parent)
    {m_scene = new QGraphicsScene();this->setScene(m_scene);this->setDragMode(QGraphicsView::ScrollHandDrag);   // 鼠标拖拽// 窗口左上角初始显示位置(中国)m_scenePos.setX(5700);m_scenePos.setY(2700);//    this->setMouseTracking(true);                       // 开启鼠标追踪connect(GetUrlInterface::getInterface(), &GetUrlInterface::update, this, &MapGraphicsView::drawImg);
    }MapGraphicsView::~MapGraphicsView() {}void MapGraphicsView::setRect(int level)
    {int w = int(qPow(2, level) * 256);QRect rect(0, 0, w, w);m_scene->setSceneRect(rect);// 将显示位置移动到缩放之前的位置this->horizontalScrollBar()->setValue(qRound(m_scenePos.x() - m_pos.x()));this->verticalScrollBar()->setValue(qRound(m_scenePos.y() - m_pos.y()));
    }/*** @brief       绘制瓦片图* @param info*/
    void MapGraphicsView::drawImg(const ImageInfo& info)
    {if (!m_itemGroup.contains(info.z))   // 如果图层不存在则添加{auto* item = new GraphicsItemGroup();m_itemGroup.insert(info.z, item);m_scene->addItem(item);}GraphicsItemGroup* itemGroup = m_itemGroup.value(info.z);if (itemGroup){itemGroup->addImage(info);}
    }/*** @brief 清空所有瓦片*/
    void MapGraphicsView::clear()
    {auto* itemGroup = m_itemGroup.value(m_level);if (itemGroup){delete itemGroup;m_itemGroup.remove(m_level);m_level = 0;}
    }void MapGraphicsView::mousePressEvent(QMouseEvent* event)
    {QGraphicsView::mousePressEvent(event);if (event->buttons() & Qt::LeftButton){m_moveView = true;}
    }/*** @brief          鼠标释放* @param event*/
    void MapGraphicsView::mouseReleaseEvent(QMouseEvent* event)
    {QGraphicsView::mouseReleaseEvent(event);if (m_moveView)   // 在鼠标左键释放时获取新的瓦片地图{emit mousePos(this->mapToScene(event->pos()).toPoint());getShowRect();m_moveView = false;}
    }/*** @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;   // 放大m_level++;}else{m_scenePos = m_scenePos / 2;   // 缩小m_level--;}m_level = qBound(0, m_level, 22);                            // 限制缩放层级setRect(m_level);                                            // 设置缩放后的视图大小emit GetUrlInterface::getInterface() -> setLevel(m_level);   // 设置缩放级别getShowRect();// 隐藏缩放前所有图层for (auto itemG : m_itemGroup){itemG->hide();}if (m_itemGroup.contains(m_level))   // 如果图层存在则显示{GraphicsItemGroup* itemGroup = m_itemGroup.value(m_level);itemGroup->show();}else   // 如果不存在则添加{auto* item = new GraphicsItemGroup();m_itemGroup.insert(m_level, item);m_scene->addItem(item);}
    }/*** @brief       窗口大小变化后获取显示新的地图* @param event*/
    void MapGraphicsView::resizeEvent(QResizeEvent* event)
    {QGraphicsView::resizeEvent(event);//    getShowRect();
    }/*** @brief       窗口显示时设置显示瓦片的视图位置* @param event*/
    void MapGraphicsView::showEvent(QShowEvent* event)
    {QGraphicsView::showEvent(event);setRect(m_level);
    }/*** @brief 获取当前场景的显示范围(场景坐标系)*/
    void MapGraphicsView::getShowRect()
    {QRect rect;int w = int(qPow(2, m_level) * 256);   // 最大范围QPoint tl = this->mapToScene(0, 0).toPoint();QPoint br = this->mapToScene(this->width(), this->height()).toPoint();rect.setX(qMax(tl.x(), 0));rect.setY(qMax(tl.y(), 0));rect.setRight(qMin(br.x(), w));rect.setBottom(qMin(br.y(), w));emit GetUrlInterface::getInterface() -> showRect(rect);
    }

4、源码地址

  • github
  • gitee

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



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

相关文章

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

QT Creator配置Kit的实现示例

《QTCreator配置Kit的实现示例》本文主要介绍了使用Qt5.12.12与VS2022时,因MSVC编译器版本不匹配及WindowsSDK缺失导致配置错误的问题解决,感兴趣的可以了解一下... 目录0、背景:qt5.12.12+vs2022一、症状:二、原因:(可以跳过,直奔后面的解决方法)三、解决方

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

基于Java开发一个极简版敏感词检测工具

《基于Java开发一个极简版敏感词检测工具》这篇文章主要为大家详细介绍了如何基于Java开发一个极简版敏感词检测工具,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录你是否还在为敏感词检测头疼一、极简版Java敏感词检测工具的3大核心优势1.1 优势1:DFA算法驱动,效率提升10

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数