前文链接: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);
}