使用ceres库将经纬度坐标GCJ02到WGS84精确转换

Posted luffy5459

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用ceres库将经纬度坐标GCJ02到WGS84精确转换相关的知识,希望对你有一定的参考价值。

    在之前的一篇博客中,介绍了经纬度坐标系的关系,WGS84是世界大地坐标系,属于原始坐标系,在商用中,需要通过火星加密算法将经纬度做转换,转换之后的坐标,称为国测局坐标,也叫火星坐标系,简称GCJ02。这个过程称为坐标偏移算法。反过来,我们将GCJ02坐标系转为WGS84就是纠偏算法,严格来说,他们不是一对正向与反向的可逆操作,但是有理论公式可以推导。

    ceres库是一个c++开源库,用来解决非线性优化问题,可以解决边界约束鲁棒非线性最小二乘法优化问题(Ceres Solver 1 is an open source C++ library for modeling and solving large, complicated optimization problems. It can be used to solve Non-linear Least Squares problems with bounds constraints and general unconstrained optimization problems),通过这个库,我们几乎可以将GCJ02精确转换为WGS84坐标。下面来看示例。

    代码是从github上拿过来的。

    c++头文件:

/***************************************************************************
 *
 * This class WGStoGCJ implements geodetic coordinate transform between geodetic
 * coordinate in WGS84 coordinate system and geodetic coordinate in GCJ-02
 * coordinate system.
 *
 * details: https://blog.csdn.net/gudufuyun/article/details/106738942
 *
 * Copyright (c) 2020.  All rights reserved.
 *
 *  kikkimo
 *  School of Remote Sensing  and Information Engineering,
 *	WuHan University,
 *	Wuhan, Hubei, P.R. China. 430079
 *  fywhu@outlook.com
 *
 ***************************************************************************
 *																		   *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#pragma once
#include <utility>

#ifdef _WGSTOGCJ_API
#if (defined _WIN32 || defined WINCE || defined __CYGWIN__)
#define WGSTOGCJ_API_EXPORTS __declspec(dllexport)
#elif defined __GNUC__ && __GNUC__ >= 4
#define WGSTOGCJ_API_EXPORTS __attribute__((visibility("default")))
#endif
#endif	

#ifndef WGSTOGCJ_API_EXPORTS
#define WGSTOGCJ_API_EXPORTS
#endif

 /**
  *  \\brief Covert geodetic coordinate in WGS84 coordinate system to geodetic coordinate
  *         in GCJ-02 coordinate system
  *
  *  \\param [in] wgs84lon: longitude in WGS84 coordinate system [unit:degree]
  *  \\param [in] wgs84lat: latitude in WGS84 coordinate system [unit:degree]
  *  \\return Returns geodetic coordinate in GCJ-02 coordinate system
  *  \\time 15:47:38 2020/06/12
  */
WGSTOGCJ_API_EXPORTS std::pair<double, double>
Wgs2Gcj(const double& wgs84lon, const double& wgs84lat);

/**
 *  \\brief Covert geodetic coordinate in GCJ-02 coordinate system to geodetic coordinate
 *         in WGS84 coordinate system
 *
 *  \\param [in] gcj02lon: longitude in GCJ-02 coordinate system [unit:degree]
 *  \\param [in] gcj02lat: latitude in GCJ-02 coordinate system [unit:degree]
 *  \\return Returns geodetic coordinate in WGS84 coordinate system
 *  \\remark simple linear iteration
 *	\\detail
 *  \\time 15:51:13 2020/06/12
 */
std::pair<double, double> __declspec(dllexport)
Gcj2Wgs_SimpleIteration(const double& gcj02lon, const double& gcj02lat);

/**
 *  \\brief Covert geodetic coordinate in GCJ-02 coordinate system to geodetic coordinate
 *         in WGS84 coordinate system
 *
 *  \\param [in] gcj02lon: longitude in GCJ-02 coordinate system [unit:degree]
 *  \\param [in] gcj02lat: latitude in GCJ-02 coordinate system [unit:degree]
 *  \\return Returns geodetic coordinate in WGS84 coordinate system
 *  \\remark the encryption formula is known and use an auto-differentiable cost function
 *  \\time 15:51:13 2020/06/12
 */
WGSTOGCJ_API_EXPORTS std::pair<double, double>
Gcj2Wgs_AutoDiff(const double& gcj02lon, const double& gcj02lat);

/**
 *  \\brief Covert geodetic coordinate in GCJ-02 coordinate system to geodetic coordinate
 *         in WGS84 coordinate system
 *
 *  \\param [in] gcj02lon: longitude in GCJ-02 coordinate system [unit:degree]
 *  \\param [in] gcj02lat: latitude in GCJ-02 coordinate system [unit:degree]
 *  \\return Returns geodetic coordinate in WGS84 coordinate system
 *  \\remark the encryption formula is unknown but we can covert point in WGS84 to point
 *          in GCJ-02 with an API,then use the numerical derivation method to solve the
 *          problem
 *	\\detail Assuming the encryption formula is
 *
 *			gcj02lon = Wgs2Gcj(wgs84lon, wgs84lat)
 *			gcj02lat = Wgs2Gcj(wgs84lon, wgs84lat)
 *
 *	 In the rectification process, (wgs84lon, wgs84lat) are unknown items. Obviously,
 *   this is a system of nonlinear equations.
 *
 *   The linear formed error functions of forward intersection come from
 *   consideration of a Taylor series expansion.
 *           V = AX - b
 *    here:
 *    V: The residuals of the observed values
 *    A: The jacobian matrix
 *    X: The modification of the unknown items
 *    b: The constant terms of the error functions
 *
 *    Then the error functions written in vector form are:
 *    | V_lon | = | dlongcj_dlonwgs  dlongcj_dlatwgs |  | d_lonwgs | - | l_lon |
 *    | V_lat | = |         0        dlatgcj_dlatwgs |  | d_latwgs | - | l_lat |
 *    here:
 *    l_lon = longcj - longcj'                 // the modification of longcj
 *    l_lat = latgcj - latgcj'                 // the modification of latgcj
 *    longcj : the observed longitude in GCJ-02
 *    latgcj : the observed latitude in GCJ-02
 *    longcj' = Wgs2Gcj(wgs84lon',wgs84lat')    // estimated longitude in GCJ-02
 *    latgcj' = Wgs2Gcj(wgs84lon',wgs84lat')    // estimated latitude in GCJ-02
 *    wgs84lon' : estimated longitude in WGS84
 *    wgs84lat' : estimated latitude in WGS84
 *    d_lonwgs : unknown items
 *    d_latwgs : unknown items
 *    wgs84lon = wgs84lon' + d_lonwgs                           // update
 *    wgs84lat = wgs84lat' + d_latwgs
 *
 *	  let V = [V_lon V_lat]T = 0, then
 *	  d_latwgs = (l_lon * dlatgcj_dlonwgs - l_lat * dlongcj_dlonwgs) /
 *			(dlongcj_dlatwgs * dlatgcj_dlonwgs - dlatgcj_dlatwgs * dlongcj_dlonwgs)
 *	  d_lonwgs = (l_lon - dlongcj_dlatwgs * d_latwgs) / dlongcj_dlonwgs
 *
 *    This iterative procedure is repeated until X= [d_lonwgs d_latwgs]T are
 *    sufficiently small.
 *  \\time 17:42:01 2020/06/12
 */
WGSTOGCJ_API_EXPORTS std::pair<double, double>
Gcj2Wgs_NumbericDiff(const double& gcj02lon, const double& gcj02lat);

/**
 *  \\brief Covert geodetic coordinate in GCJ-02 coordinate system to geodetic coordinate
 *         in WGS84 coordinate system
 *
 *  \\param [in] gcj02lon: longitude in GCJ-02 coordinate system [unit:degree]
 *  \\param [in] gcj02lat: latitude in GCJ-02 coordinate system [unit:degree]
 *  \\return Returns geodetic coordinate in WGS84 coordinate system
 *  \\remark The encryption formula is known,and use the analytical derivation method to
 *			solve the problem with high precision.
 *	\\detail Assuming the encryption formula is
 *
 *			gcj02lon = Wgs2Gcj(wgs84lon, wgs84lat)
 *			gcj02lat = Wgs2Gcj(wgs84lon, wgs84lat)
 *
 *	 In the rectification process, (wgs84lon, wgs84lat) are unknown items. Obviously,
 *   this is a system of nonlinear equations.
 *
 *   The linear formed error functions of forward intersection come from
 *   consideration of a Taylor series expansion.
 *           V = AX - b
 *    here:
 *    V: The residuals of the observed values
 *    A: The jacobian matrix
 *    X: The modification of the unknown items
 *    b: The constant terms of the error functions
 *
 *    Then the error functions written in vector form are:
 *    | V_lon | = | dlongcj_dlonwgs  dlongcj_dlatwgs |  | d_lonwgs | - | l_lon |
 *    | V_lat | = | dlatgcj_dlonwgs  dlatgcj_dlatwgs |  | d_latwgs | - | l_lat |
 *    here:
 *    l_lon = longcj - longcj'                 // the modification of longcj
 *    l_lat = latgcj - latgcj'                 // the modification of latgcj
 *    longcj : the observed longitude in GCJ-02
 *    latgcj : the observed latitude in GCJ-02
 *    longcj' = Wgs2Gcj(wgs84lon',wgs84lat')    // estimated longitude in GCJ-02
 *    latgcj' = Wgs2Gcj(wgs84lon',wgs84lat')    // estimated latitude in GCJ-02
 *    wgs84lon' : estimated longitude in WGS84
 *    wgs84lat' : estimated latitude in WGS84
 *    d_lonwgs : unknown items
 *    d_latwgs : unknown items
 *    wgs84lon = wgs84lon' + d_lonwgs                           // update
 *    wgs84lat = wgs84lat' + d_latwgs
 *
 *	  let V = [V_lon V_lat]T = 0, then
 *	  d_latwgs = (l_lon * dlatgcj_dlonwgs - l_lat * dlongcj_dlonwgs) /
 *			(dlongcj_dlatwgs * dlatgcj_dlonwgs - dlatgcj_dlatwgs * dlongcj_dlonwgs)
 *	  d_lonwgs = (l_lon - dlongcj_dlatwgs * d_latwgs) / dlongcj_dlonwgs
 *
 *    This iterative procedure is repeated until X= [d_lonwgs d_latwgs]T are
 *    sufficiently small.
 *  \\time 01:54:46 2020/06/13
 */
WGSTOGCJ_API_EXPORTS std::pair<double, double>
Gcj2Wgs_AnalyticDiff(const double& gcj02lon, const double& gcj02lat);

    c++源文件:

#include <iostream>
#include <cmath>
#include "WGS84GCJ02.h"
#include <ceres/ceres.h>

constexpr double kKRASOVSKY_A = 6378245.0;				// equatorial radius [unit: meter]
constexpr double kKRASOVSKY_B = 6356863.0187730473;	// polar radius
constexpr double kKRASOVSKY_ECCSQ = 6.6934216229659332e-3; // first eccentricity squared
constexpr double kKRASOVSKY_ECC2SQ = 6.7385254146834811e-3; // second eccentricity squared
constexpr double PI = 3.14159265358979323846;   //π

constexpr double kDEG2RAD = PI / 180.0;
constexpr double kRAD2DEG = 180.0 / PI;

constexpr inline double Deg2Rad(const double deg) {
	return deg * kDEG2RAD;
}

constexpr inline double Rad2Deg(const double rad) {
	return rad * kRAD2DEG;
}

std::pair<double, double> GetGeodeticOffset(const double& wgs84lon, const double& wgs84lat)
{
	//get geodetic offset relative to 'center china'
	double lon0 = wgs84lon - 105.0;
	double lat0 = wgs84lat - 35.0;

	//generate an pair offset roughly in meters
	double lon1 = 300.0 + lon0 + 2.0 * lat0 + 0.1 * lon0 * lon0 + 0.1 * lon0 * lat0 + 0.1 * sqrt(fabs(lon0));
	lon1 = lon1 + (20.0 * sin(6.0 * lon0 * PI) + 20.0 * sin(2.0 * lon0 * PI)) * 2.0 / 3.0;
	lon1 = lon1 + (20.0 * sin(lon0 * PI) + 40.0 * sin(lon0 / 3.0 * PI)) * 2.0 / 3.0;
	lon1 = lon1 + (150.0 * sin(lon0 / 12.0 * PI) + 300.0 * sin(lon0 * PI / 30.0)) * 2.0 / 3.0;
	double lat1 = -100.0 + 2.0 * lon0 + 3.0 * lat0 + 0.2 * lat0 * lat0 + 0.1 * lon0 * lat0 + 0.2 * sqrt(fabs(lon0));
	lat1 = lat1 + (20.0 * sin(6.0 * lon0 * PI) + 20.0 * sin(2.0 * lon0 * PI)) * 2.0 / 3.0;
	lat1 = lat1 + (20.0 * sin(lat0 * PI) + 40.0 * sin(lat0 / 3.0 * PI)) * 2.0 / 3.0;
	lat1 = lat1 + (160.0 * sin(lat0 / 12.0 * PI) + 320.0 * sin(lat0 * PI / 30.0)) * 2.0 / 3.0;

	//latitude in radian
	double B = Deg2Rad(wgs84lat);
	double sinB = sin(B), cosB = cos(B);
	double W = sqrt(1 - kKRASOVSKY_ECCSQ * sinB * sinB);
	double N = kKRASOVSKY_A / W;

	//geodetic offset used by GCJ-02
	double lon2 = Rad2Deg(lon1 / (N * cosB));
	double lat2 = Rad2Deg(lat1 * W * W / (N * (1 - kKRASOVSKY_ECCSQ)));
	return { lon2, lat2 };
}

std::pair<double, double> Wgs2Gcj(const double& wgs84lon, const double& wgs84lat)
{
	std::pair<double, double> dlonlat = GetGeodeticOffset(wgs84lon, wgs84lat);
	double gcj02lon = wgs84lon + dlonlat.first;
	double gcj02lat = wgs84lat + dlonlat.second;
	return { gcj02lon, gcj02lat };
}

std::pair<double, double> Gcj2Wgs_SimpleIteration(const double& gcj02lon,
	const double& gcj02lat)
{
	std::pair<double, double> lonlat = Wgs2Gcj(gcj02lon, gcj02lat);
	double lon0 = lonlat.first;
	double lat0 = lonlat.second;
	int iterCounts = 0;
	while (++iterCounts < 1000)
	{
		std::pair<double, double> lonlat1 = Wgs2Gcj(lon0, lat0);
		double lon1 = lonlat1.first;
		double lat1 = lonlat1.second;
		double dlon = lon1 - gcj02lon;
		double dlat = lat1 - gcj02lat;
		lon1 = lon0 - dlon;
		lat1 = lat0 - dlat;
		//1.0e-9 degree corresponding to 0.1mm
		if (fabs(dlon) < 1.0e-9 && fabs(dlat) < 1.0e-9)
			break;
		lon0 = lon1;
		lat0 = lat1;
	}
	return { lon0 , lat0 };
}


class AutoDiffCostFunc
{
public:
	AutoDiffCostFunc(const double lon, const double lat) :
		mlonGcj(lon), mlatGcj(lat) {}
	template <typename T>
	bool operator() (const T* const lonWgs, const T* const latWgs, T* residuals) const
	{
		//get geodetic offset relative to 'center china'
		T lon0 = lonWgs[0] - T(105.0);
		T lat0 = latWgs[0] - T(35.0);

		//generate an pair offset roughly in meters
		T lon1 = T(300.0) + lon0 + T(2.0) * lat0 + T(0.1) * lon0 * lon0 + T(0.1) * lon0 * lat0
			+ T(0.1) * ceres::sqrt(ceres::abs(lon0));
		lon1 = lon1 + (T(20.0) * ceres::sin(T(6.0) * lon0 * T(PI)) + T(20.0) * ceres::sin(T(2.0) * lon0 * T(PI))) * T(2.0) / T(3.0);
		lon1 = lon1 + (T(20.0) * ceres::sin(lon0 * T(PI)) + T(40.0) * ceres::sin(lon0 / T(3.0) * T(PI))) * T(2.0) / T(3.0);
		lon1 = lon1 + (T(150.0) * ceres::sin(lon0 / T(12.0) * T(PI)) + T(300.0) * ceres::sin(lon0 * T(PI) / T(30.0))) * T(2.0) / T(3.0);
		T lat1 = T(-100.0) + T(2.0) * lon0 + T(3.0) * lat0 + T(0.2) * lat0 * lat0 + T(0.1) * lon0 * lat0
			+ T(0.2) * ceres::sqrt(ceres::abs(lon0));
		lat1 = lat1 + (T(20.0) * ceres::sin(T(6.0) * lon0 * T(PI)) + T(20.0) * ceres::sin(T(2.0) * lon0 * T(PI))) * T(2.0) / T(3.0);
		lat1 = lat1 + (T(20.0) * ceres::sin(lat0 * T(PI)) + T(40.0) * ceres::sin(lat0 / T(3.0) * T(PI))) * T(2.0) / T(3.0);
		lat1 = lat1 + (T(160.0) * ceres::sin(lat0 / T(12.0) * T(PI)) + T(320.0) * ceres::sin(lat0 * T(PI) / T(30.0))) * T(2.0) / T(3.0);

		//latitude in radian
		T B = latWgs[0] * T(kDEG2RAD);
		T sinB = ceres::sin(B), cosB = ceres::cos(B);
		T W = ceres::sqrt(T(1) - T(kKRASOVSKY_ECCSQ) * sinB * sinB);
		T N = T(kKRASOVSKY_A) / W;

		//geodetic offset used by GCJ-02
		T lon2 = T(kRAD2DEG) * lon1 / (N * cosB);
		T lat2 = T(kRAD2DEG) * (lat1 * W * W / (N * (1 - kKRASOVSKY_ECCSQ)));

		//residuals
		residuals[0] = lonWgs[0] + lon2 - mlonGcj;
		residuals[1] = latWgs[0] + lat2 - mlatGcj;
		return true;
	}

private:
	double mlonGcj;
	double mlatGcj;
};

std::pair<double, double> Gcj2Wgs_AutoDiff(const double& gcj02lon,
	const double& gcj02lat)
{
	ceres::Problem * poProblem = new ceres::Problem;
	AutoDiffCostFunc* pCostFunc = new AutoDiffCostFunc(gcj02lon, gcj02lat);

	double wgslon = gcj02lon, wgslat = gcj02lat;
	poProblem->AddResidualBlock(new ceres::AutoDiffCostFunction<AutoDiffCostFunc, 2, 1, 1>(pCostFunc),
		nullptr,
		&wgslon,
		&wgslat);

	ceres::Solver::Options options;
	options.max_num_iterations = 30;
	options.linear_solver_type = ceres::DENSE_QR;
	options.minimizer_progress_to_stdout = false;
	options.gradient_tolerance = 1e-16;
	options.function_tolerance = 1e-12;
	options.parameter_tolerance = 1e-14;
	ceres::Solver::Summary summary;
	ceres::Solve(options, poProblem, &summary);
	delete poProblem;		//auto free memory of cost function "pCostFunc"
	return { wgslon, wgslat };
}


std::pair<double, double> GetPartialDerivative_Lon(const double& wgs84lon, const double& wgs84lat, const double& dlon)
{
	double lonBk = wgs84lon + dlon;
	double lonFw = wgs84lon - dlon;
	std::pair<double, double > gcjlatlonBk = Wgs2Gcj(lonBk, wgs84lat);
	double gcjlonBk = gcjlatlonBk.first;
	double gcjlatBk = gcjlatlonBk.second;
		
	std::pair<double, double > gcjlatlonFw = Wgs2Gcj(lonFw, wgs84lat);
	double gcjlonFw = gcjlatlonFw.first;
	double gcjlatFw = gcjlatlonFw.second;
	double dlongcj_dlonwgs = (gcjlonBk - gcjlonFw) / (dlon * 2.0);
	double dlatgcj_dlonwgs = (gcjlatBk - gcjlatFw) / (dlon * 2.0);
	return { dlongcj_dlonwgs , dlatgcj_dlonwgs };
}

std::pair<double, double> GetPartialDerivative_Lat(const double& wgs84lon, const double& wgs84lat, const double& dlat)
{
	double latBk = wgs84lat + dlat;
	double latFw = wgs84lat - dlat;
	std::pair<double, double> gcjlonlatBk = Wgs2Gcj(wgs84lon, latBk);
	double gcjlonBk = gcjlonlatBk.first;
	double gcjlatBk = gcjlonlatBk.second;
	std::pair<double, double> gcjlonlatFw = Wgs2Gcj(wgs84lon, latFw);
	double gcjlonFw = gcjlonlatFw.first;
	double gcjlatFw = gcjlonlatFw.second;
	double dlongcj_dlatwgs = (gcjlonBk - gcjlonFw) / (dlat * 2.0);
	double dlatgcj_dlatwgs = (gcjlatBk - gcjlatFw) / (dlat * 2.0);
	return { dlongcj_dlatwgs , dlatgcj_dlatwgs };
}

std::pair<double, double> Gcj2Wgs_NumbericDiff(const double& gcj02lon,
	const double& gcj02lat)
{
	double wgs84lon = gcj02lon, wgs84lat = gcj02lat;
	int nIterCount = 0;
	double tol = 1e-9;
	while (++nIterCount < 1000)
	{
		std::pair<double, double > data1 = GetPartialDerivative_Lon(wgs84lon, wgs84lat, tol);
		double dlongcj_dlonwgs = data1.first;
		double dlatgcj_dlonwgs = data1.second;
		std::pair<double, double > data2 = GetPartialDerivative_Lat(wgs84lon, wgs84lat, tol);
		double dlongcj_dlatwgs = data2.first;
		double dlatgcj_dlatwgs = data2.second;
		std::pair<double, double > data3 = Wgs2Gcj(wgs84lon, wgs84lat);
		double gcj02lonEst = data3.first;
		double gcj02latEst = data3.second;
		double l_lon = gcj02lon - gcj02lonEst;
		double l_lat = gcj02lat - gcj02latEst;
		double d_latwgs = (l_lon * dlatgcj_dlonwgs - l_lat * dlongcj_dlonwgs) /
			(dlongcj_dlatwgs * dlatgcj_dlonwgs - dlatgcj_dlatwgs * dlongcj_dlonwgs);
		double d_lonwgs = (l_lon - dlongcj_dlatwgs * d_latwgs) / dlongcj_dlonwgs;

		if (fabs(d_latwgs) < tol && fabs(d_lonwgs) < tol)
			break;
		wgs84lon = wgs84lon + d_lonwgs;
		wgs84lat = wgs84lat + d_latwgs;
	}
	return { wgs84lon, wgs84lat };
}

std::pair<double, double> Gcj2Wgs_AnalyticDiff(const double& gcj02lon,
	const double& gcj02lat)
{
	double wgs84lon = gcj02lon, wgs84lat = gcj02lat;
	int nIterCount = 0;
	while (++nIterCount < 1000)
	{
		//get geodetic offset relative to 'center china'
		double lon0 = wgs84lon - 105.0;
		double lat0 = wgs84lat - 35.0;

		//generate an pair offset roughly in meters
		double lon1 = 300.0 + lon0 + 2.0 * lat0 + 0.1 * lon0 * lon0 + 0.1 * lon0 * lat0 + 0.1 * sqrt(fabs(lon0));
		lon1 = lon1 + (20.0 * sin(6.0 * lon0 * PI) + 20.0 * sin(2.0 * lon0 * PI)) * 2.0 / 3.0;
		lon1 = lon1 + (20.0 * sin(lon0 * PI) + 40.0 * sin(lon0 / 3.0 * PI)) * 2.0 / 3.0;
		lon1 = lon1 + (150.0 * sin(lon0 / 12.0 * PI) + 300.0 * sin(lon0 * PI / 30.0)) * 2.0 / 3.0;
		double lat1 = -100.0 + 2.0 * lon0 + 3.0 * lat0 + 0.2 * lat0 * lat0 + 0.1 * lon0 * lat0 + 0.2 * sqrt(fabs(lon0));
		lat1 = lat1 + (20.0 * sin(6.0 * lon0 * PI) + 20.0 * sin(2.0 * lon0 * PI)) * 2.0 / 3.0;
		lat1 = lat1 + (20.0 * sin(lat0 * PI) + 40.0 * sin(lat0 / 3.0 * PI)) * 2.0 / 3.0;
		lat1 = lat1 + (160.0 * sin(lat0 / 12.0 * PI) + 320.0 * sin(lat0 * PI / 30.0)) * 2.0 / 3.0;

		double g_lon0 = 0;
		if (lon0 > 0)
			g_lon0 = 0.05 / sqrt(lon0);
		else
			if (lon0 < 0)
				g_lon0 = -0.05 / sqrt(-lon0);
			else
				g_lon0 = 0;

		double PIlon0 = PI * lon0, PIlat0 = PI * lat0;
		double dlon1_dlonwgs = 1 + 0.2 * lon0 + 0.1 * lat0 + g_lon0
			+ ((120 * PI * cos(6 * PIlon0) + 40 * PI * cos(2 * PIlon0))
				+ (20 * PI * cos(PIlon0) + 40 * PI / 3.0 * cos(PIlon0 / 3.0))
				+ (12.5 * PI * cos(PIlon0 / 12.0) + 10 * PI * cos(PIlon0 / 30.0))) * 2.0 / 3.0;
		double dlon1_dlatwgs = 2 + 0.1 * lon0;

		double dlat1_dlonwgs = 2 + 0.1 * lat0 + 2 * g_lon0
			+ (120 * PI * cos(6 * PIlon0) + 40 * PI * cos(2 * PIlon0)) * 2.0 / 3.0;
		double dlat1_dlatwgs = 3 + 0.4 * lat0 + 0.1 * lon0
			+ ((20 * PI * cos(PIlat0) + 40.0 * PI / 3.0 * cos(PIlat0 / 3.0))
				+ (40 * PI / 3.0 * cos(PIlat0 / 12.0) + 32.0 * PI / 3.0 * cos(PIlat0 / 30.0))) * 2.0 / 3.0;

		//latitude in radian
		double B = Deg2Rad(wgs84lat);
		double sinB = sin(B), cosB = cos(B);
		double WSQ = 1 - kKRASOVSKY_ECCSQ * sinB * sinB;
		double W = sqrt(WSQ);
		double N = kKRASOVSKY_A / W;

		double dW_dlatwgs = -PI * kKRASOVSKY_ECCSQ * sinB * cosB / (180.0 * W);
		double dN_dlatwgs = -kKRASOVSKY_A * dW_dlatwgs / WSQ;

		double PIxNxCosB = PI * N * cosB;
		double dlongcj_dlonwgs = 1.0 + 180.0 * dlon1_dlonwgs / PIxNxCosB;
		double dlongcj_dlatwgs = 180 * dlon1_dlatwgs / PIxNxCosB -
			180 * lon1 * PI * (dN_dlatwgs * cosB - PI * N * sinB / 180.0) / (PIxNxCosB * PIxNxCosB);

		double PIxNxSubECCSQ = PI * N * (1 - kKRASOVSKY_ECCSQ);
		double dlatgcj_dlonwgs = 180 * WSQ * dlat1_dlonwgs / PIxNxSubECCSQ;
		double dlatgcj_dlatwgs = 1.0 + 180 * (N * (dlat1_dlatwgs * WSQ + 2.0 * lat1 * W * dW_dlatwgs) - lat1 * WSQ * dN_dlatwgs) /
			(N * PIxNxSubECCSQ);

		std::pair<double, double> gcj02lonlatEst = Wgs2Gcj(wgs84lon, wgs84lat);
		double gcj02lonEst = gcj02lonlatEst.first;
		double gcj02latEst = gcj02lonlatEst.second;
		double l_lon = gcj02lon - gcj02lonEst;
		double l_lat = gcj02lat - gcj02latEst;

		double d_latwgs = (l_lon * dlatgcj_dlonwgs - l_lat * dlongcj_dlonwgs) /
			(dlongcj_dlatwgs * dlatgcj_dlonwgs - dlatgcj_dlatwgs * dlongcj_dlonwgs);
		double d_lonwgs = (l_lon - dlongcj_dlatwgs * d_latwgs) / dlongcj_dlonwgs;

		if (fabs(d_latwgs) < 1.0e-9 && fabs(d_lonwgs) < 1.0e-9)
			break;
		wgs84lon = wgs84lon + d_lonwgs;
		wgs84lat = wgs84lat + d_latwgs;
	}
	return { wgs84lon, wgs84lat };
}


void TestCase1()
{
	double lonWgs = 116.39123343289631;
	double latWgs = 39.9072885060602;
	std::cout.precision(16);
	std::cout << "WGS84 Point: (" << latWgs << ", " <<  lonWgs << ")\\n";
	std::pair<double, double> lonlatGcj = Wgs2Gcj(lonWgs, latWgs);
	double lonGcj = lonlatGcj.first; 
	double latGcj = lonlatGcj.second;
	std::cout << "GCJ-02 Point [Wgs2Gcj]: (" << latGcj << ", " <<  lonGcj << ")\\n";
	//simple linear iteration 
	std::pair<double, double> lonlatWgsNS = Gcj2Wgs_SimpleIteration(lonGcj, latGcj);
	double lonWgsNS = lonlatWgsNS.first;
	double latWgsNS = lonlatWgsNS.second;
	std::cout << "WGS84 Point [simple linear iteration]: (" << latWgsNS << ", " <<  lonWgsNS << ")\\n";

	//numerically differentiated cost function

	std::pair<double, double> lonlatWgsND = Gcj2Wgs_NumbericDiff(lonGcj, latGcj);
	double lonWgsND = lonlatWgsND.first;
	double latWgsND = lonlatWgsND.second;
	std::cout << "WGS84 Point [numerically derivation]: (" << latWgsND << ", " <<  lonWgsND << ")\\n";

	//analytical differentiated cost function

	std::pair<double, double> lonlatWgsNAn = Gcj2Wgs_AnalyticDiff(lonGcj, latGcj);
	double lonWgsNAn = lonlatWgsNAn.first;
	double latWgsNAn = lonlatWgsNAn.second;
	std::cout << "WGS84 Point [analytical derivation]: (" << latWgsNAn << ", " <<  lonWgsNAn << ")\\n";

	//auto differentiable, use ceres
	std::pair<double, double> lonlatWgsNA = Gcj2Wgs_AutoDiff(lonGcj, latGcj);
	double lonWgsNA = lonlatWgsNA.first;
	double latWgsNA = lonlatWgsNA.second;
	std::cout << "WGS84 Point [auto differentiable]: (" << latWgsNA << ", " <<  lonWgsNA << ")\\n";

}

int main() {
	TestCase1();
	return 0;
}

    运行结果:    

     从打印结果来看,普通的转换,虽然精度上可以接近原始gps坐标,但是都无法完全还原,只有ceres代码执行的结果才能达到完全还原的效果。

     有关ceres库的编译以及与c++项目结合,可以移步这里

以上是关于使用ceres库将经纬度坐标GCJ02到WGS84精确转换的主要内容,如果未能解决你的问题,请参考以下文章

WGS84与GCJ02经纬度坐标转换介绍

坐标转换——GCJ-02

iOS 百度坐标、GPS坐标、 高德坐标相互转换

CGCS2000坐标系是不是就是GCJ-02

高德,百度,Google地图定位偏移以及坐标系转换

GPS坐标(WGS84)转换百度坐标(BD09) python测试