QT实现经纬度转换为图片像素坐标

需求说明

最近在开发中遇到这种需求,给出地球经度和维度,在一张静态图片地图中标示出该经纬度的位置

需求分析

地球经纬度是球面坐标系,图片是平面坐标系,不能直接转换,谷歌地图在球面与平面转换时使用了“墨卡托投影法”(感兴趣的同学可以去百度一下),转换以后经度在平面坐标系里还是均匀分布的,维度则不是均匀分布,需要经过特殊计算才能得到在平面坐标系里的像素点坐标。

代码实现

世界地图示例

下面以世界地图为例,在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;
}

效果图,显示南京的位置
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40355471/article/details/107748833