WGS84坐标与UTM坐标互换(C++)

     WGS84是全球地理坐标系,用经纬高3个维度表示物体相对于地球的坐标。在局部地图导航中,如果要直接用WGS84,局部地图上的直线和平面会发生弯曲。因此常将WGS84投影到UTM坐标系中,从而方便在平面地图上进行测绘分析。

1.安装Sqlite3以及PROJ库 

sqlite3安装:Sqlite3安装(version 3.8.4.1)_Kanux的博客-CSDN博客

Ubuntu20.04+PROJ7.2.1安装参考:https://www.cnblogs.com/chenjian688/p/15624038.html

2. WGS84坐标与UTM坐标互换网站

 WGS84坐标转UTM坐标

坐标转换纬度经度,GPS,位置,十进制,十六进制,DD DM DMS,KML,ICBM转换经纬度地理坐标,所有格式:十进制,六十进制,GPS DD DM DMS度分秒,通过点击地图搜索。https://www.sunearthtools.com/dp/tools/conversion.php?lang=cnUTM坐标转WGS84坐标

UTM to Latitude and Longitude ConverterFree online UTM to Latitude and Longitude coordinates converter.https://www.engineeringtoolbox.com/utm-latitude-longitude-d_1370.html

3.WGS84坐标与UTM坐标互相转换代码(C++)

WGS84_UTM.cpp

#include "gps.h"
#include <iostream>
#include <iomanip>
#include <proj.h> // 包含 PROJ 库的头文件
#include <proj_api.h>


// #include "util/math.h"
namespace colmap { // 定义命名空间colmap ,保证当前命名空间中的标识符不会与其他命名空间中的标识符冲突
    // typedef Eigen::Vector3d Vec3; // 在该命名空间中,将Eigen::Vector3d缩写为Vec3
    
    // WGS84椭圆轴信息 分别表示椭圆主轴长度和次轴长度
    static const double WGS84_A = 6378137.0;      // major axis
    static const double WGS84_B = 6356752.314245; // minor axis

    /// @brief 将经纬高(WGS84坐标系)转为UTM坐标系下的东北天(ENU)坐标
    /// @return UTM坐标系下的东北天(ENU)坐标
    Eigen::Vector3d GPSTransform::lla_to_utm(double lat, double lon, double alt)
    {
    //    // step 1 WGS84地球椭球模型
    //    double a=WGS84_A;
    //    double b=WGS84_B;
    //    a /= 1000; // meters to kilometers 以下计算是在km单位下计算的,输出时要转回m单位下
    //    b /= 1000; // meters to kilometers

    //    // step 2 定义常量
    //    static const double N0_n = 0;     // 在南半球的情况下,UTM投影的Y坐标的起始值为0
    //    static const double N0_s = 1e4;   // 北半球的情况下,UTM投影的Y坐标的起始值为10000
    //    static const double E0 = 5e2;     // UTM投影的X坐标的起始值为500
    //    static const double k0 = 0.9996;  // UTM投影中的比例因子,用于调整投影坐标的尺度

    //     // step 3 参数计算
    //    const double f = (a - b) / a;     // 椭球体扁平率参数(通过WGS84椭球体主轴和次轴长度)
    //    const double n    = f / (2 - f);  // n n_2 ... 是f的幂次方,用于计算UTM投影中需要的参数
    //    const double n_2  = n   * n;
    //    const double n_3  = n_2 * n;
    //    const double n_4  = n_3 * n;
    //    const double n_5  = n_4 * n;
    //    const double n_6  = n_5 * n;
    //    const double n_7  = n_6 * n;
    //    const double n_8  = n_7 * n;
    //    const double n_9  = n_8 * n;
    //    const double n_10 = n_9 * n;

    //    // step 4 计算经度对应的UTM带号
    //    const int lon_zone = 1 + floor((lon + 180) / 6);
    //    std::cout << "UTM分度带:" << lon_zone << std::endl;
    //    // step 5 计算UTM带号对应的中央经线
    //    double lon_0 = 3 + 6 * (lon_zone - 1) - 180;
    //    std::cout << "UTM带号对应的中央经线:" << lon_0 << std::endl;
    //    // 将 转换后的中央经线lon_0 以及 输入的纬度lat、经度lon 的单位从度(degree)转为弧度(radians)
    //    lon_0=DegToRad(lon_0);
    //    lat = DegToRad(lat);
    //    lon = DegToRad(lon);

    //    // step 6 计算相关参数A a1 a2 ... 用于后续UTM坐标计算 (这些参数由椭球体参数和经纬度计算得到)
    //    const double A = a / (1 + n) * (1 + n_2/4 + n_4/64 + n_6/256 + n_8*25.0/16384.0 + n_10*49.0/65536.0);

    //    const double a1 = (1.0/2.0)*n - (2.0/3.0)*n_2 + (5.0/16.0)*n_3 + (41.0/180.0)*n_4 - (127.0/288.0)*n_5 + (7891.0/37800.0)*n_6 + (72161.0/387072.0)*n_7 - (18975107.0/50803200.0)*n_8 + (60193001.0/290304000.0)*n_9 + (134592031.0/1026432000.0)*n_10;
    //    const double a2 = (13.0/48.0)*n_2 - (3.0/5.0)*n_3 + (557.0/1440.0)*n_4 + (281.0/630.0)*n_5 - (1983433.0/1935360.0)*n_6 + (13769.0/28800.0)*n_7 + (148003883.0/174182400.0)*n_8 - (705286231.0/465696000.0)*n_9 + (1703267974087.0/3218890752000.0)*n_10;
    //    const double a3 = (61.0/240.0)*n_3 - (103.0/140.0)*n_4 + (15061.0/26880.0)*n_5 + (167603.0/181440.0)*n_6 - (67102379.0/29030400.0)*n_7 + (79682431.0/79833600.0)*n_8 + (6304945039.0/2128896000.0)*n_9 - (6601904925257.0/1307674368000.0)*n_10;
    //    const double a4 = (49561.0/161280.0)*n_4 - (179.0/168.0)*n_5 + (6601661.0/7257600.0)*n_6 + (97445.0/49896.0)*n_7 - (40176129013.0/7664025600.0)*n_8 + (138471097.0/66528000.0)*n_9 + (48087451385201.0/5230697472000.0)*n_10;
    //    const double a5 = (34729.0/80640.0)*n_5 - (3418889.0/1995840.0)*n_6 + (14644087.0/9123840.0)*n_7 + (2605413599.0/622702080.0)*n_8 - (31015475399.0/2583060480.0)*n_9 + (5820486440369.0/1307674368000.0)*n_10;
    //    const double a6 = (212378941.0/319334400.0)*n_6 - (30705481.0/10378368.0)*n_7 + (175214326799.0/58118860800.0)*n_8 + (870492877.0/96096000.0)*n_9 - (1328004581729009.0/47823519744000.0)*n_10;
    //    const double a7 = (1522256789.0/1383782400.0)*n_7 - (16759934899.0/3113510400.0)*n_8 + (1315149374443.0/221405184000.0)*n_9 + (71809987837451.0/3629463552000.0)*n_10;
    //    const double a8 = (1424729850961.0/743921418240.0)*n_8 - (256783708069.0/25204608000.0)*n_9 + (2468749292989891.0/203249958912000.0)*n_10;
    //    const double a9 = (21091646195357.0/6080126976000.0)*n_9 - (67196182138355857.0/3379030566912000.0)*n_10;
    //    const double a10 = (77911515623232821.0/12014330904576000.0)*n_10;
       
    //    const double t = sinh(atanh(sin(lat)) - 2*sqrt(n)/(1+n) * atanh(2*sqrt(n)/(1+n)*sin(lat)));// t是UTM投影计算中的一个辅助参数,它由纬度计算得到
    //    const double xi = atan(t/cos(lon-lon_0));                                                  // xi和eta是UTM投影计算中的两个重要参数,由经纬度和其他计算参数得到
    //    const double eta = atanh(sin(lon-lon_0) / sqrt(1+t*t));

    //    const double N0 = (lat > 0 ? N0_n : N0_s);                                                 // N0是UTM投影中,纬度对应的Y坐标的起始值,根据纬度的正负来选择使用N0_n还是N0_s
       
    //    // step 7 计算UTM坐标系下的东北坐标 E N (km)
    //    const double E = E0 + k0 * A * (eta + a1*cos(2*1*xi)*sinh(2*1*eta) + a2*cos(2*2*xi)*sinh(2*2*eta) + a3*cos(2*3*xi)*sinh(2*3*eta) + a4*cos(2*4*xi)*sinh(2*4*eta) + a5*cos(2*5*xi)*sinh(2*5*eta) + a6*cos(2*6*xi)*sinh(2*6*eta) + a7*cos(2*7*xi)*sinh(2*7*eta) + a8*cos(2*8*xi)*sinh(2*8*eta) + a9*cos(2*9*xi)*sinh(2*9*eta) + a10*cos(2*10*xi)*sinh(2*10*eta));
    //    const double N = N0 + k0 * A * (xi + a1*sin(2*1*xi)*cosh(2*1*eta) + a2*sin(2*2*xi)*cosh(2*2*eta) + a3*sin(2*3*xi)*cosh(2*3*eta) + a4*sin(2*4*xi)*cosh(2*4*eta) + a5*sin(2*5*xi)*cosh(2*5*eta) + a6*sin(2*6*xi)*cosh(2*6*eta) + a7*sin(2*7*xi)*cosh(2*7*eta) + a8*sin(2*8*xi)*cosh(2*8*eta) + a9*sin(2*9*xi)*cosh(2*9*eta) + a10*sin(2*10*xi)*cosh(2*10*eta));

    //    // Scale E,N from kilometers to meters
    //    return Eigen::Vector3d(E * 1000, N * 1000, alt);                                                      // 转回m坐标系

        int nUTMZone = (int)((lon + 186.0) / 6.0);
        bool bNorth = lat > 0 ? true : false;
        int m_nUTMZone = -1000;
        bool m_bNorth = false; 
        projPJ m_pj_utm = nullptr;
        const char *wgs84 = "+proj=longlat +datum=WGS84 +no_defs ";//GPS所用坐标系,EPSG:4326
        projPJ m_pj_wgs84 = pj_init_plus(wgs84);
        if(m_nUTMZone != nUTMZone || m_bNorth != bNorth)
        {
            //"+proj=utm +zone=50 +datum=WGS84 +units=m +no_defs";
            // "+proj=utm +zone=48 +south +datum=WGS84 +units=m +no_defs"

            std::string qstrUTM = "+proj=utm +zone=" + std::to_string(nUTMZone);
            if(!bNorth)
            {
                qstrUTM += " +south ";
            }
            qstrUTM += " +datum=WGS84 +units=m +no_defs";
            if(m_pj_utm != nullptr)
            {
                pj_free(m_pj_utm);
                m_pj_utm = NULL;
            }
            std::string strUTM = qstrUTM;
            const char *pUTM = strUTM.c_str();
    
            m_pj_utm = pj_init_plus(pUTM);
    
    
            m_nUTMZone = nUTMZone;
            m_bNorth = bNorth;
        }
        double easting = lon * DEG_TO_RAD;
        double northing = lat * DEG_TO_RAD;
        pj_transform(m_pj_wgs84, m_pj_utm, 1, 1, &easting, &northing, nullptr);
        return Eigen::Vector3d(easting, northing, alt);


    }

    Eigen::Vector3d GPSTransform::utm_to_lla(double easting, double northing, double altitude)
    {
        const char* wgs84 = "+proj=longlat +datum=WGS84 +no_defs ";//GPS所用坐标系,EPSG:4326
        const char* utm_crs = "+proj=utm +zone=51 +ellps=WGS84 +units=m +no_defs";
        projPJ m_pj_wgs84 = pj_init_plus(wgs84);     //球体:wgs84、投影:经纬度投影
        projPJ m_pj_utm = pj_init_plus(utm_crs);;    //球体:wgs84、投影UTM

        // double longitude = easting;
        pj_transform(m_pj_utm, m_pj_wgs84, 1, 1, &easting, &northing, nullptr);
        
        double longitude = easting * RAD_TO_DEG;
        double latitude = northing * RAD_TO_DEG;

        return Eigen::Vector3d(longitude, latitude, altitude);
    }
}

int main()
{
    // step 0 原WGS84下经纬高(十进制度表示)
    double longtitude = 123.439471667;
    double latitude = 33.024801667;
    double altitude = 14.53;
    std::cout << "Origin WGS84 Coordinates (Latitude, Longtitude, Altitude): "
                << std::setiosflags(std::ios::fixed) << longtitude << ", "  << latitude << ", " << altitude << std::endl;

    colmap::GPSTransform gpsTransform;

    // step 1 将经纬高(WGS84坐标系)转为UTM坐标系下的东北天(ENU)坐标
    Eigen::Vector3d utm_coords = gpsTransform.lla_to_utm(latitude, longtitude, altitude);

    // 输出转换后的 UTM 坐标
    std::cout << "UTM Coordinates (Easting, Northing, Altitude): "
              << std::setiosflags(std::ios::fixed) << utm_coords(0) << ", "  << utm_coords(1) << ", " << utm_coords(2) << std::endl;

    // step 2 将UTM坐标系下的东北天(ENU)坐标转为WGS84坐标系下的经纬高(LLA)坐标
    Eigen::Vector3d WGS84_coords = gpsTransform.utm_to_lla(utm_coords(0), utm_coords(1), utm_coords(2));
    // 输出转换后的 WGS84 坐标
    std::cout << "WGS84 Coordinates (Latitude, Longtitude, Altitude): "
              << std::setiosflags(std::ios::fixed) << WGS84_coords(0) << ", "  << WGS84_coords(1) << ", " << WGS84_coords(2) << std::endl;
    // NOTICE 和原始数据对比,没有精度损失
    return 0;

}

gps.h: 

#ifndef GPS_H
#define GPS_H
#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H

#include <vector>
#include <Eigen/Core>

namespace colmap
{
    class GPSTransform
    {
        public:
        Eigen::Vector3d lla_to_utm(double lat, double lon, double alt);
        Eigen::Vector3d utm_to_lla(double E_coords, double N_coords, double Altitude);

    }; // GPSTransform

} // namespace colmap

float DegToRad(const float deg) {
  return deg * 0.0174532925199432954743716805978692718781530857086181640625f;
}




#endif

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(COLMAP_tricks)

set(CMAKE_BUILD_TYPE "Release")
add_definitions("-DENABLE_SSE")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wall -g") 
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(OpenGL_GL_PREFERENCE LEGACY)

# cmake 自动找到PROJ,因为PROJ也是用cmake标准来编译的
find_package(PROJ REQUIRED CONFIG)
#引入PROJ的头文件
include_directories(${PROJ_INCLUDE_DIR})

include_directories(
    ${CMAKE_CURRENT_LIST_DIR}/include/
    "/usr/include/eigen3/"
)
    
add_executable(WGS84_UTM src/WGS_UTM/WGS84_UTM.cpp include/gps.h)
target_link_libraries(WGS84_UTM PRIVATE ${PROJ_LIBRARIES})

参考:WGS84坐标和UTM坐标的互转(c/c++)_kingcrab0710的博客-CSDN博客

猜你喜欢

转载自blog.csdn.net/qq_45068787/article/details/131790501