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

文章介绍了如何利用QGraphicsView和自定义的MapUtility及TileUtility类来实现基于GCJ02Web墨卡托投影的离线瓦片地图。通过场景坐标转换为经纬度坐标的方法,实现了地图上的鼠标移动事件,动态显示经纬度坐标。
摘要由CSDN通过智能技术生成

前文链接: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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮生卍流年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值