高斯坐标和地理坐标的转换代码

由于项目需要,了解了关于高斯坐标和地理坐标的相关知识,当然也只是略懂,因为需要在代码中实现,因此也在网上搜索了相关资料,在此记录一下

这里一共两份高斯坐标与地理坐标转换的代码,各有优缺点


代码1

这原本是java代码,我略微修改了一下变成C++代码了

// 高斯坐标和地理坐标的相互转换
class Convertor {
public:

    ~Convertor() {}

    double a;//'椭球体长半轴
    double b;// '椭球体短半轴
    double f; //'扁率
    double e;// '第一偏心率
    double e1; //'第二偏心率
    double FE;//'东偏移
    double FN;//'北偏移
    double L0;//'中央经度
    double W0;//'原点纬线
    double k0;//'比例因子
    /**
     * 幂函数
     * @param e
     * @param i
     * @return
     */
    double MZ(double e, int i) {
        return pow(e, i);
    }

    /**
     * 说明: 用于初始化转换参数
     *
     * @param TuoqiuCanshu    枚举类型,提供北京54、西安80和WGS84三个椭球参数
     * @param CentralMeridian 中央经线
     * @param OriginLatitude  原点纬度,如果是标准的分幅,则该参数是0
     * @param EastOffset      东偏移
     * @param NorthOffset     北偏移
     */
    Convertor(int TuoqiuCanshu, double CentralMeridian, double OriginLatitude, double EastOffset, double NorthOffset) {
        /**
         * 'Krassovsky (北京54采用) 6378245 6356863.0188
         * 'IAG 75(西安80采用) 6378140 6356755.2882
         * 'WGS 84 6378137 6356752.3142
         * 'CGC 2000 6378137 6356752.31414
         */
        if (TuoqiuCanshu == 0)//北京五四
        {
            a = 6378245;
            b = 6356863.0188;
        }
        else if (TuoqiuCanshu == 1)// '西安八零
        {
            a = 6378140;
            b = 6356755.2882;
        }
        if (TuoqiuCanshu == 2)//'WGS84
        {
            a = 6378137;
            b = 6356752.3142;
        }
        if (TuoqiuCanshu == 3)//'CGC2000坐标
        {
            a = 6378137;
            b = 6356752.31414;
        }
        f = (a - b) / a;//扁率
        //e = sqrt(1 - MZ((b / a) ,2));//'第一偏心率
        e = sqrt(2 * f - MZ(f, 2));//'第一偏心率
        //eq = sqrt(MZ((a / b) , 2) - 1);//'第二偏心率
        e1 = e / sqrt(1 - MZ(e, 2));//'第二偏心率
        L0 = CentralMeridian;//中央经
        W0 = OriginLatitude;//原点纬线
        k0 = 1;//'比例因子
        FE = EastOffset;//东偏移
        FN = NorthOffset;//北偏移
    }

    /**
     * 经纬度坐标转高斯投影坐标
     * @param J 经度
     * @param W 纬度
     * @return
     */
    void JWgetGK(double J, double W, double& X, double& Y) {
        //'给出经纬度坐标,转换为高克投影坐标
        double PI = 3.14159265358979;
        double* resultP = new double[2];
        double BR = (W - W0) * PI / 180;//纬度弧长
        double lo = (J - L0) * PI / 180; //经差弧度
        double N = a / sqrt(1 - MZ((e * sin(BR)), 2)); //卯酉圈曲率半径
        //求解参数s
        double B0;
        double B2;
        double B4;
        double B6;
        double B8;
        double C = MZ(a, 2) / b;
        B0 = 1 - 3 * MZ(e1, 2) / 4 + 45 * MZ(e1, 4) / 64 - 175 * MZ(e1, 6) / 256 + 11025 * MZ(e1, 8) / 16384;
        B2 = B0 - 1;
        B4 = 15 / 32 * MZ(e1, 4) - 175 / 384 * MZ(e1, 6) + 3675 / 8192 * MZ(e1, 8);
        B6 = 0 - 35 / 96 * MZ(e1, 6) + 735 / 2048 * MZ(e1, 8);
        B8 = 315 / 1024 * MZ(e1, 8);
        double s = C * (B0 * BR + sin(BR) * (B2 * cos(BR) + B4 * MZ((cos(BR)), 3) + B6 * MZ((cos(BR)), 5) + B8 * MZ((cos(BR)), 7)));
        double t = tan(BR);
        double g = e1 * cos(BR);
        double XR = s + MZ(lo, 2) / 2 * N * sin(BR) * cos(BR) + MZ(lo, 4) * N * sin(BR) * MZ((cos(BR)), 3) / 24 * (5 - MZ(t, 2) + 9 * MZ(g, 2) + 4 * MZ(g, 4)) + MZ(lo, 6) * N * sin(BR) * MZ((cos(BR)), 5) * (61 - 58 * MZ(t, 2) + MZ(t, 4)) / 720;
        double YR = lo * N * cos(BR) + MZ(lo, 3) * N / 6 * MZ((cos(BR)), 3) * (1 - MZ(t, 2) + MZ(g, 2)) + MZ(lo, 5) * N / 120 * MZ((cos(BR)), 5) * (5 - 18 * MZ(t, 2) + MZ(t, 4) + 14 * MZ(g, 2) - 58 * MZ(g, 2) * MZ(t, 2));
        //resultP[0] = YR + FE;
        //resultP[1] = XR + FN;
        X = YR + FE;
        Y = XR + FN;
        //return resultP;
    }

    /**
     * 高斯投影坐标 转为 经纬度坐标
     * @param X 高斯投影坐标X
     * @param Y 高斯投影坐标Y
     * @return
     */
    void GKgetJW(double X, double Y, double& Lon, double& Lat) {
        //'给出高克投影坐标,转换为经纬度坐标
        double PI = 3.14159265358979;
        double* resultP = new double[2];
        double El1 = (1 - sqrt(1 - MZ(e, 2))) / (1 + sqrt(1 - MZ(e, 2)));
        double Mf = (Y - FN) / k0;//真实坐标值
        double Q = Mf / (a * (1 - MZ(e, 2) / 4 - 3 * MZ(e, 4) / 64 - 5 * MZ(e, 6) / 256));//角度
        double Bf = Q + (3 * El1 / 2 - 27 * MZ(El1, 3) / 32) * sin(2 * Q) + (21 * MZ(El1, 2) / 16 - 55 * MZ(El1, 4) / 32) * sin(4 * Q) + (151 * MZ(El1, 3) / 96) * sin(6 * Q) + 1097 / 512 * MZ(El1, 4) * sin(8 * Q);
        double Rf = a * (1 - MZ(e, 2)) / sqrt(MZ((1 - MZ((e * sin(Bf)), 2)), 3));
        double Nf = a / sqrt(1 - MZ((e * sin(Bf)), 2));//卯酉圈曲率半径
        double Tf = MZ((tan(Bf)), 2);
        double D = (X - FE) / (k0 * Nf);
        double Cf = MZ(e1, 2) * MZ((cos(Bf)), 2);
        double B = Bf - Nf * tan(Bf) / Rf * (MZ(D, 2) / 2 - (5 + 3 * Tf + 10 * Cf - 9 * Tf * Cf - 4 * MZ(Cf, 2) - 9 * MZ(e1, 2)) * MZ(D, 4) / 24 + (61 + 90 * Tf + 45 * MZ(Tf, 2) - 256 * MZ(e1, 2) - 3 * MZ(Cf, 2)) * MZ(D, 6) / 720);
        double L = L0 * PI / 180 + 1 / cos(Bf) * (D - (1 + 2 * Tf + Cf) * MZ(D, 3) / 6 + (5 - 2 * Cf + 28 * Tf - 3 * MZ(Cf, 2) + 8 * MZ(e1, 2) + 24 * MZ(Tf, 2)) * MZ(D, 5) / 120);
        double Bangle = B * 180 / PI;
        double Langle = L * 180 / PI;
        //resultP[0] = Langle;//RW * 180 / PI;
        //resultP[1] = Bangle + W0;//RJ * 180 / PI;
        Lat = Langle;//RW * 180 / PI;
        Lon = Bangle + W0;//RJ * 180 / PI;
        //return resultP;
    }
};

代码2

代码2的方法提供的的可变参数(坐标系、投影等)较少,所以能够做的转换也比较有限,对数据精度和数据范围要求较高的不建议使用

/* 功能说明: 将绝对高斯坐标(y,x)转换成绝对的地理坐标(wd,jd)。        */
// double y;     输入参数: 高斯坐标的横坐标,以米为单位 
// double x;  输入参数: 高斯坐标的纵坐标,以米为单位
// short  DH;     输入参数: 带号,表示上述高斯坐标是哪个带的
// double *L;     输出参数: 指向经度坐标的指针,其中经度坐标以秒为单位
// double *B;     输出参数: 指向纬度坐标的指针,其中纬度坐标以秒为单位
void GaussToGeo(double y, double x, short DH, double *L, double *B, double LP)
{
	double L0;    //  经差
	double tf;    //  tf = tg(Bf0),注意要将Bf转换成以弧度为单位
	double nf;    //  n = y * sqrt( 1 + etf ** 2) / c, 其中etf = e'**2 * cos(Bf0) ** 2
	double t_L0;   //  l0,经差,以度为单位
	double t_B0;   //  B0,纬度,以度为单位
	double Bf0;    //  Bf0
	double etf;    //  etf,其中etf = e'**2 * cos(Bf0) ** 2
	double X_3;

	double PI = 3.14159265358979;
	double b_e2 = 0.0067385254147;
	double b_c = 6399698.90178271;

	X_3 = x / 1000000.00 - 3;      // 以兆米(1000000)为单位
	// 对于克拉索夫斯基椭球,计算Bf0
	Bf0 = 27.11115372595 + 9.02468257083 * X_3 - 0.00579740442 * pow(X_3, 2)
		- 0.00043532572 * pow(X_3, 3) + 0.00004857285 * pow(X_3, 4)
		+ 0.00000215727 * pow(X_3, 5) - 0.00000019399 * pow(X_3, 6);
	tf = tan(Bf0*PI / 180);       //  tf = tg(Bf),注意这里将Bf转换成以弧度为单位
	etf = b_e2 * pow(cos(Bf0*PI / 180), 2);   //  etf = e'**2 * cos(Bf) ** 2
	nf = y * sqrt(1 + etf) / b_c;     //  n = y * sqrt( 1 + etf ** 2) / c
				// 计算纬度,注意这里计算出来的结果是以度为单位的
	t_B0 = Bf0 - (1.0 + etf) * tf / PI * (90.0 * pow(nf, 2)
		- 7.5 * (5.0 + 3 * pow(tf, 2) + etf - 9 * etf * pow(tf, 2)) * pow(nf, 4)
		+ 0.25 * (61 + 90 * pow(tf, 2) + 45 * pow(tf, 4)) * pow(nf, 6));
	// 计算经差,注意这里计算出来的结果是以度为单位的
	t_L0 = (180 * nf - 30 * (1 + 2 * pow(tf, 2) + etf) * pow(nf, 3)
		+ 1.5 * (5 + 28 * pow(tf, 2) + 24 * pow(tf, 4)) * pow(nf, 5))
		/ (PI * cos(Bf0*PI / 180));
	L0 = (t_L0 * 3600.0);       //  将经差转成秒

	if (LP == -1000)
	{
		*L = (double)((DH * 6 - 3) * 3600.0 + L0);  // 根据带号计算出以秒为单位的绝对经度,返回指针
	}
	else
	{
		*L = LP * 3600.0 + L0;  // 根据带号计算出以秒为单位的绝对经度,返回指针
	}
	//----------------------------------

	*B = (double)(t_B0 * 3600.0);     //  将纬差转成秒,并返回指针
}


/* 功能说明: (1)将地理坐标(wd,jd)转换成绝对的高斯坐标(y,x)
	(2)本函数支持基于六度带(或三度带)、克拉索夫斯基椭球进行转换                             */
	/* 适用范围: 本函数适用于将地球东半球中北半球(即东经0度到东经180度,北纬0度至90度)范围
		内所有地理坐标到高斯坐标的转换            */
		/* 使用说明: 调用本函数后返回的结果应在满足精度的条件下进行四舍五入      */
		// double jd;         输入参数: 地理坐标的经度,以秒为单位
		// double wd;         输入参数: 地理坐标的纬度,以秒为单位
		// short  DH;      输入参数: 三度带或六度带的带号
		/*  六度带(三度带)的带号是这样得到的:从东经0度到东经180度自西向东按每6度(3度)顺序编号
		 (编号从1开始),这个顺序编号就称为六度带(三度带)的带号。因此,六度带的带号的范围是1-30,
		 三度带的带号的范围是1-60。
		  如果一个点在图号为TH的图幅中,那麽该点所处的六度带的带号就可以这样得到:将该图号的
		 第3、4位组成的字符串先转换成数字,再减去30。例如某点在图幅06490701中,该点所在的带号就
		 是49-30,即19。
		  如果调用本函数去进行一般的从地理坐标到基于六度带高斯坐标的变换(非邻带转换),则参
		 数DH的选取按前一段的方法去确定。
		  如果调用本函数去进行基于六度带邻带转换,则参数DH的选取先按上述方法去确定,然后看是
		 往前一个带还是后一个带进行邻带转换再确定是加1还是减1。         */
void GeoToGauss(double jd, double wd, short DH, short DH_width, double *y, double *x, double LP)
{
	double t;     //  t=tgB
	double L;     //  中央经线的经度
	double l0;    //  经差
	double jd_hd, wd_hd;  //  将jd、wd转换成以弧度为单位
	double et2;    //  et2 = (e' ** 2) * (cosB ** 2)
	double N;     //  N = C / sqrt(1 + et2)
	double X;     //  克拉索夫斯基椭球中子午弧长
	double m;     //  m = cosB * PI/180 * l0 
	double tsin, tcos;   //  sinB,cosB

	double PI = 3.14159265358979;
	double b_e2 = 0.0067385254147;
	double b_c = 6399698.90178271;

	jd_hd = jd / 3600.0 * PI / 180.0;    // 将以秒为单位的经度转换成弧度
	wd_hd = wd / 3600.0 * PI / 180.0;    // 将以秒为单位的纬度转换成弧度

	// 如果不设中央经线(缺省参数: -1000),则计算中央经线,
	// 否则,使用传入的中央经线,不再使用带号和带宽参数
	//L = (DH - 0.5) * DH_width ;      // 计算中央经线的经度
	if (LP == -1000)
	{
		L = (DH - 0.5) * DH_width;      // 计算中央经线的经度
	}
	else
	{
		L = LP;
	}

	l0 = jd / 3600.0 - L;       // 计算经差
	tsin = sin(wd_hd);        // 计算sinB
	tcos = cos(wd_hd);        // 计算cosB
				// 计算克拉索夫斯基椭球中子午弧长X
	X = 111134.8611 / 3600.0 * wd - (32005.7799 * tsin + 133.9238 * pow(tsin, 3)
		+ 0.6976 * pow(tsin, 5) + 0.0039 * pow(tsin, 7)) * tcos;
	et2 = b_e2 * pow(tcos, 2);      //  et2 = (e' ** 2) * (cosB ** 2)
	N = b_c / sqrt(1 + et2);      //  N = C / sqrt(1 + et2)
	t = tan(wd_hd);         //  t=tgB
	m = PI / 180 * l0 * tcos;       //  m = cosB * PI/180 * l0 
	*x = X + N * t * (0.5 * pow(m, 2)
		+ (5.0 - pow(t, 2) + 9.0 * et2 + 4 * pow(et2, 2)) * pow(m, 4) / 24.0
		+ (61.0 - 58.0 * pow(t, 2) + pow(t, 4)) * pow(m, 6) / 720.0);
	*y = N * (m + (1.0 - pow(t, 2) + et2) * pow(m, 3) / 6.0
		+ (5.0 - 18.0 * pow(t, 2) + pow(t, 4) + 14.0 * et2
			- 58.0 * et2 * pow(t, 2)) * pow(m, 5) / 120.0);

}

原文链接:https://www.cnblogs.com/SuperMagic/archive/2007/12/04/982817.html

发布了85 篇原创文章 · 获赞 61 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/Leo_csdn_/article/details/102569063