QGraphicsView实现简易地图2『瓦片经纬度』

news2024/9/23 23:30:31

前文链接:QGraphicsView实现简易地图1『加载离线瓦片地图』
地图采用GCJ02 Web 墨卡托投影,最小坐标:(-180.00000000000000,-85.05112877980655),最大坐标:(180.00000000000000,85.05112877980655)。瓦片地图单张图片像素大小为256*256,经度均分256像素,但纬度分布不均匀,需要公式计算。
1、动态演示效果
在这里插入图片描述

2、静态展示图片
在这里插入图片描述

核心代码
1、数据定义GeoData.h

#pragma once
#include <QMetaType>

/*
 * 地理几何数据
 * 瓦片地图坐标投影:GCJ02 Web 墨卡托投影
 */

#define PIXMAP_SIZE 256

// 瓦片坐标
struct TileCoord
{
	TileCoord(double _x, double _y) : x(_x), y(_y) {}
	double x;
	double y;
};

// 经纬度坐标
struct GeoCoord
{
	GeoCoord() = default;
	GeoCoord(double _lon, double _lat) : lon(_lon), lat(_lat) {}
	double lon;  // 经度
	double lat;  // 纬度
};
Q_DECLARE_METATYPE(GeoCoord)

2、MapUtility类

#pragma once
#include <QPointF>
#include "DataStruct/GeoData.h"

class MapUtility
{
public:
	// 场景坐标转经纬度
	static GeoCoord geoCoordFromScene(QPointF scenePos, int level);

private:
	// 场景坐标转瓦片坐标(瓦片坐标系)
	static TileCoord tileCoordFromScene(QPointF scenePos, int level);
	// 场景坐标转所在瓦片像素点坐标
	static QPointF tilePixelCoordFromScene(QPointF scenePos, int level);
};
#include "MapUtility.h"
#include "TileUtility.h"

GeoCoord MapUtility::geoCoordFromScene(QPointF scenePos, int level)
{
	TileCoord tileCoord = tileCoordFromScene(scenePos, level);
	QPointF tilePixelCoord = tilePixelCoordFromScene(scenePos, level);
	
	return TileUtility::pixelToLonLat(tilePixelCoord.x(), tilePixelCoord.y(), tileCoord.x, tileCoord.y, level);
}

TileCoord MapUtility::tileCoordFromScene(QPointF scenePos, int level)
{
	int tileX = std::floor(scenePos.x() / PIXMAP_SIZE);
	int tileY = std::floor(scenePos.y() / PIXMAP_SIZE);

	return TileCoord(tileX, tileY);
}

QPointF MapUtility::tilePixelCoordFromScene(QPointF scenePos, int level)
{
	TileCoord tileCoord = tileCoordFromScene(scenePos, level);
	double left = tileCoord.x * PIXMAP_SIZE;	// scenePos所在瓦片的左侧位于场景中的坐标
	double top = tileCoord.y * PIXMAP_SIZE;		// scenePos所在瓦片的上侧位于场景中的坐标

	return QPointF(scenePos.x() - left, scenePos.y() - top);
}

3、TileUtility类

class TileUtility
{
	friend class MapUtility;

private:
	/**
	* 将地图层级下瓦片的像素点转换到经纬度
	* 瓦片地图左上角为(0, 0)点
	* @param pixelX   瓦片像素点X
	* @param pixelY   瓦片像素点Y
	* @param tileX    瓦片坐标X
	* @param tileY    瓦片坐标Y
	* @param level    瓦片层级
	* @return         经纬度坐标
	*/
	static GeoCoord pixelToLonLat(double pixelX, double pixelY, int tileX, int tileY, int level);
	static double pixelXToLon(double pixelX, double tileX, int level);
	static double pixelXToLat(double pixelY, double tileY, int level);
	static double mathSinH(double value);

	/**
	* 获取地图层级下X/Y轴上的瓦片数量
	* @param level    瓦片层级
	* @return 		  瓦片数量
	*/
	static int mapSize(int level);
};
GeoCoord TileUtility::pixelToLonLat(double pixelX, double pixelY, int tileX, int tileY, int level)
{
	double lon = pixelXToLon(pixelX, tileX, level);
	double lat = pixelXToLat(pixelY, tileY, level);

	return GeoCoord(lon, lat);
}

double TileUtility::pixelXToLon(double pixelX, double tileX, int level)
{
	const double pixelXToTileAddition = pixelX / PIXMAP_SIZE;
	const double lon = (tileX + pixelXToTileAddition) / mapSize(level) * 360 - 180;

	return lon;
}

double TileUtility::pixelXToLat(double pixelY, double tileY, int level)
{
	const double pixelYToTileAddition = pixelY / PIXMAP_SIZE;
	const double lat = qAtan(mathSinH(M_PI * (1 - 2 * (tileY + pixelYToTileAddition) / mapSize(level)))) * 180.0 / M_PI;

	return lat;
}

double TileUtility::mathSinH(double value)
{
	return (qExp(value) - qExp(-value)) / 2;
}

int TileUtility::mapSize(int level)
{
	return pow(2, level);
}

4、场景鼠标移动事件

void MapScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
	m_coordPos = MapUtility::geoCoordFromScene(mouseEvent->scenePos(), m_curLevel);
	emit geoCoordChanged(m_coordPos);
}

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

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

相关文章

如何通俗理解扩散模型?

扩散模型(Diffusion Model)是一类十分先进的基于扩散思想的深度学习生 成模型。生成模型除了扩散模型之外&#xff0c;还有出现较早的 VAE ( Variational Auto- Encoder&#xff0c;变分自编码器) 和 GAN ( Generative Adversarial Net &#xff0c;生成对抗网络) 等。 虽然它们…

Cpp学习——类与对象(2)

思维导图&#xff1a; 一&#xff0c;构造函数 1.定义 对于构造函数首先就要知道构造函数怎么写&#xff0c;构造函数怎么写呢&#xff1f;你要知道如下两点&#xff1a; 1.构造函数的函数名要与类名相同 2.构造函数是没有返回值的&#xff0c;但可以有参数。 因为这第二点&am…

ubuntu目录分析

在Ubuntu根目录下&#xff0c;以下是一些常见文件夹的含义&#xff1a; /bin&#xff1a;存放可执行文件&#xff0c;包含一些基本的命令和工具。 /boot&#xff1a;存放启动时所需的文件&#xff0c;如内核和引导加载程序。 /dev&#xff1a;包含设备文件&#xff0c;用于与硬…

关于计算机视觉的Open3D简介

一、说明 Open3D 是一个开源库&#xff0c;使开发人员能够处理 3D 数据。它提供了一组用于 3D 数据处理、可视化和机器学习任务的工具。该库支持各种数据格式&#xff0c;例如 .ply、.obj、.stl 和 .xyz&#xff0c;并允许用户创建自定义数据结构并在程序中访问它们。 Open3D 广…

【雕爷学编程】MicroPython动手做(16)——掌控板之图片图像显示

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

进程_PCB 的理解

目录 一. PCB 的概念 1. 为什么需要PCB 2. PCB的属性 二. task struct 1. task struct 介绍 2. 查看进程指令 3. PID 4. PPID 父进程是什么&#xff1f; 为什么要有父进程&#xff1f; 5. fork 创建子进程 1) fork 后的现象 为什么会打印两次&#xff1f; 2) 的返…

用C语言实现插入排序算法

1.设计思路 用插入排序对长度为n的待排序数组A进行排序的伪代码&#xff08;在代码中&#xff0c;A中元素的数目n用A.length来表示&#xff09;。 伪代码如下&#xff1a; INSERTION-SORT(A) for j2 to A.length:keyA[j] //将A[j]插入已排序序列A[1..j-1]ij-1while i>0…

【MTI 6.S081 Lab】Copy-on-write

【MTI 6.S081 Lab】Copy-on-write The problemThe solutionImplement copy-on-write fork (hard)实验任务Hints解决方案问题解决思考uvmcopykfreekallockpagerefcow_handlertrap 虚拟内存提供了一定程度的间接性&#xff1a;内核可以通过将PTE标记为无效或只读来拦截内存引用&a…

Quartz项目搭建与任务执行源码分析

数据库准备 准备一个MySQL数据库&#xff0c;版本为8.0&#xff0c;然后创建一个库&#xff0c;并从quartz官方的版本包中找到名称为tables_mysql_innodb.sql的脚本执行进去&#xff08;脚本内容文后也有提供&#xff09;。 项目依赖说明 创建一个Maven项目&#xff0c;引入…

Python方式实现简易弹道计算机

1 问题 本周无意间刷到了德国豹2A5坦克的火控介绍&#xff0c;想自己编写一个不考虑空气阻力以及测风影响的简易弹道计算机&#xff08;大口径火炮&#xff09;。 2 方法 由高中物理知识了解到&#xff0c;炮弹出膛之后基本就是抛物线列个抛物线方程就好了; 百度得火炮的弹道方…

【Django学习】(十六)session_token认证过程与区别_响应定制

一、认识session与token 这里就直接引用别人的文章&#xff0c;不做过多说明 网络应用中session和token本质是一样的吗&#xff0c;有什么区别&#xff1f; - 知乎 二、token响应定制 在全局配置表中配置 DEFAULT_AUTHENTICATION_CLASSES: [# 指定jwt Token认证rest_framew…

python_day17_多线程

threading模块 import timedef sing():while True:print("唱歌~~~~~~~~~~~~")time.sleep(1)def dance():while True:print("跳舞############")time.sleep(1) if __name__ __main__:sing()dance()此时为单线程 import threading import timedef sing(…

Java 异常处理的使用和思考

概念 异常处理的概念起源于早期的编程语言&#xff0c;如 LISP、PL/I 和 CLU。这些编程语言首次引入了异常处理机制&#xff0c;以便在程序执行过程中检测和处理错误情况。异常处理机制随后在 Ada、Modula-3、C、Python、Java 等编程语言中得到了广泛采用和发展。在 Java 中&a…

迁移学习《Efficient and Robust Pseudo-Labeling for Unsupervised Domain Adaptation》

1 摘要 问题&#xff1a;无监督域适应传统方法将超过一定置信度阈值的数据视为目标域的伪标记数据&#xff0c;因此选择合适的阈值会影响目标性能。 在本文中&#xff0c;提出了一种新的基于置信度的加权方案来获得伪标签&#xff0c;并提出了一种自适应阈值调整策略&#xff0…

oCPC实践录 | oCPC下机制设计变得毫无意义?(2)无声的战争

接上回oCPC实践录 | oCPC下机制设计变得毫无意义&#xff1f;&#xff08;1&#xff09;事出异常必有妖&#xff0c;互联网广告最开始采用的广义第一价格密封拍卖&#xff08;GFP)&#xff0c;对广告主而言&#xff0c;需要不断感知竞争对手的变化&#xff0c;修改报价&#xf…

text-generation-webui加载chatglm2-6b时,报错,要求set the option trust_remote_code=True

背景 使用text-generation-webui加载chatglm2-6b大模型时报错&#xff0c;要求设置option trust_remote_codeTrue&#xff0c;一开始没注意界面&#xff0c;去翻找配置文件&#xff0c;后来发现&#xff0c;就在Model界面&#xff0c;有一个复选框&#xff0c;可以进行设置&am…

IO模型epoll

通过epoll实现一个并发服务器 服务器 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h…

【新星计划】STM32F103C8T6 - C语言 - 蓝牙JDY-31-SPP串口通信实验

文章目录 蓝牙技术的发展历史SPP蓝牙串口BLE协议&#xff08;超低功耗应用蓝牙协议&#xff09; 常见通用蓝牙模块JDY-31-SPPHC05/06 Keil 工程开发模版main.c 源文件&#xff1a;接线方式&#xff1a;烧录工具&#xff1a;FlyMcu串口调试工具&#xff1a;XCOM蓝牙调试助手APP …

Yolov8训练自己的数据集合(最新实验数据)

一、安装环境 笔者环境如下&#xff1a; win10 anaconda python3.8二、clone代码 地址如下&#xff0c;可以直接使用git命令进行clone,也可以直接去网站下载 git clone https://github.com/ultralytics/ultralytics代码目录如下所示 三、安装必要的库 其实这里比较重要的是…

java的instanceof操作符

说明 java的instanceof操作符可以用于类型的比较或者模式匹配比较。instanceof关键字左边是对象的引用&#xff0c;右边是引用类型或者模式。 如果instanceof关键字右边是一个引用类型&#xff0c;那么instanceof关键字是一个类型比较操作符。 如果instanceof关键字右边是一个…