WGS84 coordinates and UTM coordinates exchange (C++)

     WGS84 is a global geographic coordinate system, which uses three dimensions of longitude and latitude to represent the coordinates of objects relative to the earth. In local map navigation, if you want to use WGS84 directly, the straight lines and planes on the local map will be curved. Therefore, WGS84 is often projected into the UTM coordinate system, so as to facilitate surveying and mapping analysis on the planar map.

1. Install Sqlite3 and PROJ library 

sqlite3 installation: Sqlite3 installation (version 3.8.4.1)_Kanux Blog-CSDN Blog

Ubuntu20.04+PROJ7.2.1 installation reference: https://www.cnblogs.com/chenjian688/p/15624038.html

2. WGS84 coordinates and UTM coordinates interchange website

 WGS84 coordinates to UTM coordinates

Coordinates Convert Latitude Longitude, GPS, Location, Decimal, Hexadecimal, DD DM DMS, KML, ICBM Convert Longitude Latitude Geographical Coordinates, All Formats: Decimal, Sexagesimal, GPS DD DM DMS Degrees Minutes Seconds, Search By Clicking Map. https://www.sunearthtools.com/dp/tools/conversion.php?lang=cn UTM coordinates to WGS84 coordinates

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 coordinates and UTM coordinates conversion code (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})

Reference: Mutual conversion between WGS84 coordinates and UTM coordinates (c/c++)_kingcrab0710's blog-CSDN blog

Guess you like

Origin blog.csdn.net/qq_45068787/article/details/131790501