需求说明
最近在开发中遇到这种需求,给出地球经度和维度,在一张静态图片地图中标示出该经纬度的位置
需求分析
地球经纬度是球面坐标系,图片是平面坐标系,不能直接转换,谷歌地图在球面与平面转换时使用了“墨卡托投影法”(感兴趣的同学可以去百度一下),转换以后经度在平面坐标系里还是均匀分布的,维度则不是均匀分布,需要经过特殊计算才能得到在平面坐标系里的像素点坐标。
代码实现
世界地图示例
下面以世界地图为例,在google地图截取最大世界地图:经度-180度至180度,维度-85度至85度(地球最大维度是-90至90,经过墨卡托投影后90度会趋于无穷大,平面无法表示),算法的核心是把不均匀的维度转换为均匀的表示方式。
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPainter>
#include <QImage>
namespace Ui {
class Widget;
}
struct MAP_X_Y{
double x;
double y;
};
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void displayPos();//地图显示位置
MAP_X_Y AxesConvert(double lon,double lat);//经纬度转换为像素坐标
public slots:
void slotpushButton();
private:
Ui::Widget *ui;
QImage *m_pImage;//定义QImage绘图设备
QPainter *m_pPainter;//定义画家
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPixmap>
#include <QDebug>
#include <QPainter>
#include <QImage>
#include <cmath>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("世界地图");
m_pImage = new QImage(1024,1024,QImage::Format_ARGB32);
m_pPainter = new QPainter(m_pImage);
m_pPainter->drawImage(0,0,QImage("./images/worldMap.jpg"));
ui->label->setPixmap(QPixmap::fromImage(*m_pImage));//UI控件显示图片
connect(ui->pushButton,SIGNAL(clicked(bool)),this,SLOT(slotpushButton()));
}
Widget::~Widget()
{
delete ui;
}
/**
* @brief 显示位置
*/
void Widget::displayPos()
{
double lon = ui->lineEdit_lon->text().toDouble();
double lat = ui->lineEdit_lat->text().toDouble();
MAP_X_Y t_MAP_X_Y = AxesConvert(lon,lat);//经纬度转换为像素坐标
m_pPainter->drawImage(0,0,QImage("./images/worldMap.jpg"));
for(int i=t_MAP_X_Y.x;i<t_MAP_X_Y.x+5;i++)
for(int j=t_MAP_X_Y.y;j<t_MAP_X_Y.y+5;j++)
{
m_pImage->setPixel(i,j,qRgb(255,0,0));//按像素点修改图片颜色,图片中显示为红色方块
}
ui->label->setPixmap(QPixmap::fromImage(*m_pImage));
}
/**
* @brief 经纬度转换为像素坐标
* @param lon 经度
* @param lat 维度
* @return 像素坐标
*/
MAP_X_Y Widget::AxesConvert(double lon, double lat)
{
const double pixel_Width = 1024;//截取地图像素宽度
const double pixel_Height = 1024;//截取地图像素高度
const double PI = 3.1415926;
const double MaxLon = 180;//截取地图的最大经度
const double MaxLat = 85;//截取地图的最大维度
int pixel_X;
int pixel_Y;
/**
* @brief google地图中能够截取的最大经度是180度,最大维度是85度
* 转换为平面坐标系后经度是均匀分布的,维度不是均匀分布
*经过下面公式计算后,得到的“修正维度”是均匀的,传入MaxLat计算出来的值是修正维度的最大值
*/
double reviseMaxLat = log(tan((90+MaxLat)*PI/360))/(PI/180);//修正后的最大维度
lat = log(tan((90+lat)*PI/360))/(PI/180);//修正后的维度
//平面坐标系可分为4个象限,坐标落在不同象限的计算方式不同
if(lon>=0 && lat>=0)
{
pixel_X = (lon/MaxLon)*(pixel_Width/2)+pixel_Width/2;
pixel_Y = (pixel_Height/2) - (lat/reviseMaxLat)*(pixel_Height/2);
}
else if(lon>=0 && lat<=0)
{
pixel_X = (lon/MaxLon)*(pixel_Width/2)+pixel_Width/2;
pixel_Y = (pixel_Height/2) + (-lat/reviseMaxLat)*(pixel_Height/2);
}
else if(lon<=0 && lat >=0)
{
pixel_X = pixel_Width/2 - (-lon/MaxLon)*(pixel_Width/2);
pixel_Y = (pixel_Height/2) - (lat/reviseMaxLat)*(pixel_Height/2);
}
else if(lon<=0 && lat<=0)
{
pixel_X = pixel_Width/2 - (-lon/MaxLon)*(pixel_Width/2);
pixel_Y = (pixel_Height/2) + (-lat/reviseMaxLat)*(pixel_Height/2);
}
MAP_X_Y t_sMap;
t_sMap.x = pixel_X;
t_sMap.y = pixel_Y;
return t_sMap;
}
void Widget::slotpushButton()
{
displayPos();
}
效果图,显示北京的位置
定制地图:中国周边
下面以中国周边为例定制地图,截取经度范围60度至160度,维度范围0度至60度,坐标转换函数:
/**
* @brief 经纬度转换为像素坐标
* @param lon
* @param lat
* @return
*/
MAP_X_Y China_Map::AxesConvert_Custom(double lon, double lat)
{
const double pixel_Width = 1143;//截取地图像素宽度
const double pixel_Height = 859;//截取地图像素高度
const double PI = 3.1415926;
const double LeftLon = 60;//地图最左侧经度
const double RightLon = 160;//地图最右侧经度
const double TopLat = 60;//地图顶部维度
const double BottomLat = 0;//地图底部维度
int pixel_X;
int pixel_Y;
/**
* @brief google地图中能够截取的最大经度是180度,最大维度是85度
* 转换为平面坐标系后经度是均匀分布的,维度不是均匀分布
*经过下面公式计算后,得到的“修正维度”是均匀的,传入MaxLat计算出来的值是修正维度的最大值
*/
double reviseMaxLat = log(tan((90+(TopLat))*PI/360))/(PI/180);//修正后的最大维度
lat = log(tan((90+lat)*PI/360))/(PI/180);//修正后的维度
//截取的地图经度和维度都大于0,只集中在一个象限
pixel_X = ((lon - LeftLon)/(RightLon - LeftLon))*pixel_Width;
pixel_Y = pixel_Height - (lat - BottomLat)/(reviseMaxLat - BottomLat)*pixel_Height;
MAP_X_Y t_sMap;
t_sMap.x = pixel_X;
t_sMap.y = pixel_Y;
return t_sMap;
}
效果图,显示南京的位置