高斯投影坐标转换(c语言)

  • 一、高斯投影简介
  • 1.1 高斯投影基本概念
  • 1.2 高斯投影的条件
  • 1.3 高斯投影分带
  • 二、高斯投影转换
  • 2.1 几个概念
  • 2.2 计算已知量
  • 2.3 计算步骤
  • 三、具体问题求解
  • 3.1 问题重述
  • 3.2 实现代码
  • 3.3 输出结果



一、高斯投影简介

1.1 高斯投影基本概念

高斯投影又称横轴椭圆柱等角投影,属于正形投影。世界上最先采用高斯投影的国家是奥地利和德国,我国于1952年正式决定采用高斯投影。

除了七参数方法还有其他更简单的实现高斯投影转wgs84 java 高斯投影条件_迭代

1.2 高斯投影的条件

(1)投影后角度不产生变形,满足正形投影要求;

(2)中央子午线投影后是一条直线;

(3)中央子午线投影后长度不变,其投影长度比恒等于1。

除了七参数方法还有其他更简单的实现高斯投影转wgs84 java 高斯投影条件_c语言_02

高斯投影除了在中央子午线上没有长度变形外,不在中央子午线上的各点,其长度比都大于1,且离开中央子午线愈远,长度变形愈大。

1.3 高斯投影分带

为限制长度投影变形,投影分带有6度分带和3度分带两种方法。

除了七参数方法还有其他更简单的实现高斯投影转wgs84 java 高斯投影条件_c语言_03

  • 6°带带号N和中央子午线经度 LN的关系式: LN=6N-3
  • 3°带带号n和中央子午线经度 Ln的关系式: Ln=3n
  • 6°带与3°带带号之间的关系为:n=2N-1

二、高斯投影转换

2.1 几个概念

高斯平面坐标系:指的是以中央子午线与赤道的交点作为坐标原点,以中央子午线的投影为纵坐标轴X,规定X轴向北为正,以赤道的投影为横坐标轴Y,Y轴向东为正,形成的坐标系。

通用坐标:在同一投影带内横坐标有正值、有负值,这对坐标的计算和使用不方便。为了使Y值都为正,将纵坐标x轴西移500km,并在Y坐标前面冠以带号,称为通用坐标。

大地坐标(Geodetic coordinate):是大地测量中以参考椭球面为基准面的坐标,地面点P的位置用大地经度L大地纬度B大地高H表示。

高斯坐标正算:大地坐标(B、L、H)转换为高斯投影坐标(X、Y、Z)

高斯坐标反算:高斯投影坐标(X、Y、Z)转换为大地坐标(B、L、H)

注意:本文高斯坐标投影转换指的是高斯投影的 6° 带转换成 3° 带 或者 3° 带转换成 6° 带。

2.2 计算已知量

地球属性

符号表示


长半轴

a

6378245

短半轴

b

6356863.01877

c

6399698.9018

第一偏心率

e1^2

0.0066934216

第二偏心率

e2^2

0.0067385254

2.3 计算步骤

以6° 带转换成 3° 带为例, 3° 带转换成 6° 带同。

  1. 通用值转换成自然值
  2. 高斯投影坐标反算
  3. 求解6°带中央经线
  4. 求解3°带的经差
  5. 高斯投影坐标正算
  6. 自然值转换成通用值

三、具体问题求解

3.1 问题重述

已知:某点P在1954年北京坐标系6°带的平面直角坐标为

x1 = 3589644.286m
y1 = 20679136.438m

求:P点在3°带第40带的平面坐标(x2,y2)

3.2 实现代码

/*-------------------------------------------
	高斯坐标代号转换代码:6°带转换成3°带
	
	时间:2019年12月1日
	编译:鸽子
	编程语言:C语言
---------------------------------------------*/
#include <stdio.h>
#include <math.h>
/* 定义已知值 */
double a = 6378245;					//长半轴
double b = 6356863.01877;			//短半轴
double c = 6399698.9018;			//c
double e_square = 0.0066934216;		//第一偏心率
double e1_square = 0.0067385254;	//第二偏心率
double e = sqrt(e_square);	
double e1 = sqrt(e1_square);
double beita0 = 1 - 3.0 / 4.0*e1_square + 45 / 65 * pow(e1_square, 2) - 175 / 256 * pow(e1_square, 3) + 11025 / 16384 * pow(e1_square, 4);	//β0,下同
double beita2 = beita0 - 1;
double beita4 = 15 / 32 * pow(e1_square, 2) - 175 / 384 * pow(e1_square, 3) + 3675 / 8192 * pow(e1_square, 4);
double beita6 = -35 / 96 * pow(e1_square, 3) + 735 / 2048 * pow(e1_square, 4);
double beita8 = 315 / 1024 * pow(e1_square, 4);

/*------------------------------高斯坐标反算-------------------------------------*/
double Fx(double B)
{
	return (c*beita2 + (c*beita4 + (c*beita6 + c*beita8*pow(cos(B), 2))*pow(cos(B), 2))*pow(cos(B), 2))*sin(B)*cos(B);
}
double Fx(double B, double l)
{
	double N = a / sqrt(1 - e_square*pow(sin(B), 2));	//卯酉圈曲率半径
	double t = tan(B);
	double fai = e1*cos(B);
	double a2 = 1.0 / 2.0 * N*sin(B)*cos(B);
	double a4 = 1.0 / 24.0 * N*sin(B)*pow(cos(B), 3)*(5 - pow(t, 2) + 9 * pow(fai, 2) + 4 * pow(fai, 4));
	double a6 = 1.0 / 720.0 * N*sin(B)*pow(cos(B), 5)*(61 - 58 * pow(t, 2) + pow(t, 4));
	return (a2*pow(l, 2) + a4*pow(l, 4) + a6*pow(l, 6));
}
double Fy(double B, double l)
{
	double N = a / sqrt(1 - e_square*pow(sin(B), 2));	//卯酉圈曲率半径
	double t = tan(B);
	double fai = e1*cos(B);
	double a3 = 1.0 / 6.0 * N*pow(cos(B), 3)*(1 - pow(t, 2) + pow(fai, 2));
	double a5 = 1.0 / 120.0 * N*pow(cos(B), 5)*(5 - 18 * pow(t, 2) + pow(t, 4) + 14 * pow(fai, 2) - 58 * pow(fai, 2)*pow(t, 2));
	return a3*pow(l, 3) + a5*pow(l, 5);
}
void InverseCalculation(double x, double y, double &B, double &a1, double &l)
{

	B = (x - Fx(B) - Fx(B, l)) / c*beita0;
	a1 = a*cos(B) / sqrt(1 - e_square*pow(sin(B), 2));
	l = (y - Fy(B, l)) / a1;
}
//坐标反算
void InverseCal(double x, double y, double &B, double&l)
{
	//初始值设置
	double B0 = (x / c*beita0);
	double a10 = a*cos(B0) / sqrt(1 - e_square*pow(sin(B0), 2));
	double l0 = (y / a10);
	printf("设置初始值为:\n	B0 = %lf   l0 = %lf\n", B0, l0);
	B = B0;
	double a1 = a10;
	l = l0;
	int n = 1;
	InverseCalculation(x, y, B, a1, l);
	printf("第%d次迭代结果为:	B = %lf   l = %lf\n", n, B, l);
	while (fabs(B0 - B) > 0.0001 || fabs(l0 - l) > 0.0001)
	{
		n++;	//次数
		B0 = B;	
		a10 = a1;
		l0 = l;
		InverseCalculation(x, y, B, a1, l);
		printf("第%d次迭代结果为:	B = %lf   l = %lf\n", n, B, l);
	}
}
/*------------------------------------------------------------------------------*/
/*带号转换*/
void DegreeTrans(double &B, double &l, int &n, double &L, int degree)
{
	switch (degree)
	{
	case 6:
	{
			  int centerLongitude = n * 6 - 3;	//中央经度
			  B = B * 206265 / 3600;
			  l = l * 206265 / 3600;
			  L = l + centerLongitude;
			  break;
	}
	case 3:
	{
			  n = (1.5 + L) / 3;	//计算带号
			  int centerLongitude = n * 3;	//中心经度
			  l = L - centerLongitude;	//经差
			  l = l * 3600 / 206265;	
			  B = B * 3600 / 206265;
			  break;
	}
	}
}
/*坐标正算:大地坐标转化为高斯投影坐标*/
void Calculate(double B, double l, double &x, double &y)
{
	double N = a / sqrt(1 - e_square*pow(sin(B), 2));	//卯酉圈曲率半径
	double t = tan(B);
	double fai = e1*cos(B);
	double X = c*(beita0*B + (beita2*cos(B) + beita4*pow(cos(B), 3) + beita6*pow(cos(B), 5) + beita8*pow(cos(B), 7))*sin(B));	//赤道至纬度B的子午线弧长
	x = X + pow(l, 2)*N*sin(B)*cos(B) + pow(l, 4) / 24 * N*sin(B)*pow(cos(B), 3)*(5 - pow(t, 2) + 9 * pow(fai, 2) + 4 * pow(fai, 4)) +
		pow(l, 6) / 720 * N*sin(B)*pow(cos(B), 5)*(61 - 58 * pow(t, 2) + pow(t, 4));
	y = l*N*cos(B) + pow(l, 3) / 6 * N*pow(cos(B), 3)*(1 - pow(t, 2) + pow(fai, 2)) +
		pow(l, 5) / 120 * N*pow(cos(B), 5)*(5 - 18 * pow(t, 2) + 14 * pow(fai, 2) - 58 * pow(fai, 2)*pow(t, 2));
}

int main()
{
	printf("*********高斯投影带转换(6°——3°)************\n\n");
	double x = 3589544.286;	//初始高斯坐标xy
	double y = 20679136.438;
	printf("需要转换的6°带坐标为:\n	x = %lf   y = %lf\n\n", x, y);
	int n = 20;
	y = y - n * 1000000 - 500000;	//转换成自然值
	printf("计算过程:\n(一)通用值转换成自然值\n	x = %lf   y = %lf\n", x, y);
	printf("\n(二)高斯投影坐标反算\n");
	double B = 0;
	double l = 0;
	InverseCal(x, y, B, l);	//坐标反算
	printf("迭代结果为:\n	B = %lf   l = %lf\n", B, l);
	double L = 0;	//定义大地经度
	DegreeTrans(B, l, n, L, 6);	//求中央经线及大地坐标
	printf("\n(三)求解6°带中央经线\n转换后的大地坐标为:\n	B = %lf   L = %lf\n", B, L);
	printf("\n(四)求解3°带的经差\n");
	int n1 = 0;	//3°带代号
	double l1 = 0;	//经差
	DegreeTrans(B, l1, n1, L, 3);	//求中央经线
	printf("转换后的3°带的经度纬度和代号为:\n	B = %lf   l = %lf   n=%d\n", B, l1, n1);
	printf("\n(五)高斯投影坐标正算\n");
	double x1 = 0, y1 = 0;
	Calculate(B, l1, x1, y1);	//坐标正算
	printf("正算结果为:\n	x = %lf   y = %lf\n", x1, y1);
	y1 = y1 + n1 * 1000000 + 500000;	//高斯投影坐标y
	printf("\n(六)自然值转换成通用值\n	x = %lf   y = %lf\n", x1, y1);
	return 0;
}

注:代码是根据中国矿业大学出版社出版的《应用大地测量学》高斯投影一章的知识编写的,为了理解书本内容,精度没经过验证。

3.3 输出结果

注:以下是编译器输出的内容,未经修改

需要转换的6°带坐标为:
x = 3589544.286000 y = 20679136.438000

计算过程:
(一)通用值转换成自然值
x = 3589544.286000 y = 179136.438000

(二)高斯投影坐标反算

设置初始值为:
B0 = 0.558058 l0 = 0.033078

第1次迭代结果为: B = 0.560073 l = 0.033116
第2次迭代结果为: B = 0.560076 l = 0.033116

迭代结果为:
B = 0.560076 l = 0.033116

(三)求解6°带中央经线
转换后的大地坐标为:
B = 32.090028 L = 118.897435

(四)求解3°带的经差
转换后的3°带的经度纬度和代号为:
B = 0.560076 l = -0.019243 n=40

(五)高斯投影坐标正算
正算结果为:
x = 3552710.678780 y = -104087.421312

(六)自然值转换成通用值
x = 3552710.678780 y = 40395912.578688

总结:测量学与其他学科的不同就是对结果的精度近乎苛刻(尤其是大地测量学),在实际应用中还是需要精度验证的。