C语言空间直角坐标与大地坐标的相互转换实验报告

C语言空间直角坐标与大地坐标的相互转换实验报告


源代码附文末或见Github:⬇️
直角坐标与大地坐标的相互转化

branch:

  • master: 正常读取输入。
  • file_read: 从csv文件获取输入。

一、实验名称:空间直角坐标与大地坐标的相互转换

二、实验目的与要求

三、实验内容

  1. 已知某点的空间直角坐标(X、Y、Z), X = 1546823.34,Y = -3879765.13,Z = 4804185.05,椭球参数a= 6378137m,b= 6356752.3141m,采用下式将其转换为大地坐标(L、B)
    在这里插入图片描述

  2. 根据1所求的大地坐标(L、B)及大地高H及相同的椭球参数将其转化为空间直角坐标(X、Y、Z),计算式为:在这里插入图片描述

四、试验设备与环境

五、实验步骤

1 && 2 分析和算法设计

公理1 :由于 L B H LBH LBH X Y Z XYZ XYZ 是相同物理量在相同参考系下的不同表示方法,因此在空间直角坐标系上的某点 ( X Y Z ) (XYZ) (XYZ),有且只有一个 L B H LBH LBH在大地坐标系与之对应。

推论1 只要 X Y Z XYZ XYZ确定, L B H LBH LBH的值就只与 X Y Z XYZ XYZ的取值有关,而与任何其他变量无关。

下面展开讨论:

由方程可知,虽然 L L L 可以直接求解,但求 B B B 的方程中,包含 B B B 本身,为了验证方程(2)是否可以求解,我们先将方程(2)化简:
B = arctan ⁡ Z + N e 2 sin ⁡ B X 2 + Y 2 tan ⁡ B = Z + N e 2 sin ⁡ B X 2 + Y 2 (2) \begin{matrix} B = \arctan\dfrac{Z + Ne^2\sin B}{\sqrt{X^2 + Y^2}}\\\tan B = \dfrac{Z + Ne^2\sin B}{\sqrt{X^2 + Y^2}} \tag{2} \end{matrix} B=arctanX2+Y2 Z+Ne2sinBtanB=X2+Y2 Z+Ne2sinB(2)
为了方便化简,我们令
X 2 + Y 2 = R (3) \sqrt{X^2 + Y^2} = R \tag{3} X2+Y2 =R(3)
又由于
N = a 1 − e 2 sin ⁡ 2 B (4) N = \frac{a}{\sqrt{1-e^2\sin^2B}} \tag{4} N=1e2sin2B a(4)
将(4)、(3)带入(2)中
tan ⁡ B = Z R + e 2 R N s i n B = Z R + e 2 R sin ⁡ B 1 − e 2 sin ⁡ 2 B = Z R + a e 2 R 1 1 sin ⁡ 2 B − e 2 (5) \begin{matrix} \tan B &= \dfrac{Z}{R} + \dfrac{e^2}{R}NsinB\\&=\dfrac{Z}{R} + \dfrac{e^2}{R}\dfrac{\sin B}{\sqrt{1-e^2\sin^2B}} \\&= \dfrac{Z}{R} + \dfrac{ae^2}{R}\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}} \tag{5} \end{matrix} tanB=RZ+Re2NsinB=RZ+Re21e2sin2B sinB=RZ+Rae2sin2B1e2 1(5)
推论1 可知,上式中 Z R \dfrac ZR RZ a e 2 R \dfrac{ae^2}{R} Rae2 B B B 的值无关,为了方便表示,我们令
Z R = C , a e 2 R = K . \frac{Z}{R} = C, \quad \dfrac{ae^2}{R}=K. RZ=C,Rae2=K.

上式化简为:

tan ⁡ B = K 1 1 sin ⁡ 2 B − e 2 + C (6*) \tan B = K\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}}+C \tag{6*} tanB=Ksin2B1e2 1+C(6*)

在上式中,由于 tan ⁡ B sin ⁡ B = cos ⁡ B \dfrac{\tan B}{\sin B} = \cos B sinBtanB=cosB 不为常量,故无法找到一组 k 1 , k 2 ∈ R k_1,k_2\in R k1,k2R 满足 k 1 tan ⁡ B + k 2 sin ⁡ B = 0 k_1\tan B + k_2 \sin B = 0 k1tanB+k2sinB=0。因此上式左右两端含有变量 B B B 的因子是非线性相关的,所以不可以直接通过方程求解。

由此,为得到 B B B 的值,必须通过其他方式求解。

利用计算机程序可以通过不断猜测 B B B 的值,带入(6)式中,如果(6)左右两端的值足够接近,且由 推论1, 此时猜测到的值 B 猜 B_猜 B 必然也会接近实际的值 B 实 B_实 B

我们将上述求 B B B 的方法称为遍历法,接下来我们设计遍历法的算法。

遍历法

由题设条件,可知 B ∈ ( − π 2 , π 2 ) B \in (-\dfrac{\pi}{2}, \dfrac{\pi}{2}) B(2π,2π)

  • 一个可执行的算法必须由有限步构成。

但是可能出现的 B B B 的值确是无限的,为此,我们将 B B B 进行离散化 ,即每隔很小的值 δ ( d e l t a ) \delta (delta) δ(delta) ,取一个新的值 B B B

为了方便表示,我们设计损失函数 L o s s Loss Loss用来计算方程左右两端的差值。
L o s s ( B ) = tan ⁡ B − K 1 1 sin ⁡ 2 B − e 2 + C (7) Loss(B) = \tan B - K\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}}+C \tag{7} Loss(B)=tanBKsin2B1e2 1+C(7)
当函数Loss的值很小时,则认为此时的 B B B达到了目标值 B 实 B_实 B

用数学语言可以描述为:

若取一个足够小的 ε > 0 \varepsilon > 0 ε>0

对于
B 0 = − π 2 + k δ , 且 ( k ∈ N ∗ ) B 0 < π 2 B_0 = -\frac{\pi}{2} + k\delta, 且(k\in N^*)B_0 < \dfrac{\pi}{2} B0=2π+kδ,(kN)B0<2π
时,如果满足
∣ L o s s ( B 0 ) ∣ < ε |Loss(B_0)| <\varepsilon Loss(B0)<ε
则认为 B = B 0 B = B_0 B=B0


梯度下降法

使用遍历法​求解时,往往会由于需要遍历的变量的数量太多,导致程序的运行时间太长,难以得到正确的结果。当我们遍历时,只是盲目的遍历,我们并不知道到底经过多少个 δ \delta δ 后,能够使 L o s s Loss Loss函数的绝对值变小。这是因为,我们只用 L o s s Loss Loss 函数的值与0比较,这样的比较获得的信息量是十分少的,为了从 L o s s Loss Loss 函数中获取更多信息,我们可以考虑将 L o s s Loss Loss 函数的绝对值进行求导,将其导函数记为 L o s s ′ Loss' Loss

我们的目标是使 L o s s Loss Loss 函数的值趋近于0,对于一个点 ( B , L o s s ( B ) ) (B, Loss(B)) (B,Loss(B)) ,如果 L o s s ( B ) > 0 Loss(B) > 0 Loss(B)>0,我们应该调整 B B B 的值,使得 L o s s ( B ) Loss(B) Loss(B)减少,相反,如果 L o s s ( B ) < 0 Loss(B) < 0 Loss(B)<0, 我们应该调整 B B B的值,使 L o s s ( B ) Loss(B) Loss(B)增加。

即:

  • L o s s ( B ) > 0 Loss(B) > 0 Loss(B)>0, B B B L o s s ′ ( B ) < 0 Loss'(B) < 0 Loss(B)<0的方向移动。
  • L o s s ( B ) < 0 , B Loss(B) < 0, B Loss(B)<0,B L o s s ′ ( B ) > 0 Loss'(B) > 0 Loss(B)>0 的方向移动。

以点 X = − 2758534 , Y = − 4132154 , Z = 3986124 X = -2758534, Y = -4132154, Z = 3986124 X=2758534,Y=4132154,Z=3986124为例,

绘制出 L o s s Loss Loss函数的图像:

在这里插入图片描述

发现 L o s s Loss Loss函数并不是简单的单调函数,用上述算法有可能达到一个使 L o s s ( B ) Loss(B) Loss(B)为0的点,也有可能达到函数的一个极值点,并不是在所有情况下都能符合条件。


二分法

虽然梯度下降法无法实现,但这给我们进一步实现程序提供了思路,即让 B B B 的取值变得更加具有方向性

由于当 B → − π 2 B\to -\dfrac{\pi}{2} B2π 时,
L o s s ( B ) → − ∞ Loss(B) \to -\infin Loss(B)
B → π 2 B\to \dfrac{\pi}{2} B2π时,
L o s s ( B ) → + ∞ Loss(B) \to +\infin Loss(B)+
又由推论2可知,在区间 ( − π 2 , π 2 ) (-\dfrac{\pi}{2}, \dfrac{\pi}{2}) (2π,2π内, L o s s ( B ) Loss(B) Loss(B)有且只有一个一个零点。可以很容易想到二分法的求解方式:

  • L o o p S t a r t : Loop Start: LoopStart:
  • m i d = r i g h t + l e f t 2 mid = \dfrac{right +left}2 mid=2right+left
  • i f L o s s ( m i d ) > ε , l e f t = m i d if \quad Loss(mid) > \varepsilon , left = mid ifLoss(mid)>ε,left=mid
  • e l s e i f L o s s ( m i d ) < − ε , r i g h t = m i d else \quad if \quad Loss(mid) < -\varepsilon, right = mid elseifLoss(mid)<ε,right=mid
  • e l s e B = m i d else\quad B = mid elseB=mid
  • : L o o p E n d :LoopEnd :LoopEnd

迭代法

由于式(6)
tan ⁡ B = K 1 1 sin ⁡ 2 B − e 2 + C (6*) \tan B = K\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}}+C \tag{6*} tanB=Ksin2B1e2 1+C(6*)
如果我们令方程左边的 B = B n + 1 B = B_{n+1} B=Bn+1 方程右边的 B = B n B = B_n B=Bn,就可以得到一个关于 B B B 的迭代方程。
B n + 1 = arctan ⁡ ( K 1 1 sin ⁡ 2 B n − e 2 + C ) (8*) B_{n+1} = \arctan\left(K\frac{1}{\sqrt{\dfrac{1}{\sin^2B_n}}-e^2}+C\right) \tag{8*} Bn+1=arctanKsin2Bn1 e21+C(8*)
我们称上面的递推式为函数 F o r w a r d ( B ) Forward(B) Forward(B)

以点 X = − 2758534 , Y = − 4132154 , Z = 3986124 X = -2758534, Y = -4132154, Z = 3986124 X=2758534,Y=4132154,Z=3986124为例,

绘制出 F o r w a r d Forward Forward函数的图像:

上图中粗线为 F o r w a r d Forward Forward函数,虚线为直线 B n + 1 = B n B_{n+1} = B_n Bn+1=Bn

设曲线 F o r w a r d Forward Forward 与 直线 B n + 1 = B n B_{n+1} = B_n Bn+1=Bn的交点为 A A A A A A的横坐标为 x 0 x_0 x0,很显然 A A A是函数 F o r w a r d Forward Forward一阶不动点。由递推函数的性质可知,

  • B n < x 0 B_n < x_0 Bn<x0 时, B n + 1 > B n B_{n+1} > B_n Bn+1>Bn

  • B n > x 0 B_n > x_0 Bn>x0时, B n + 1 < B n B_{n+1} < B_n Bn+1<Bn

  • B n = x 0 B_n = x_0 Bn=x0时, B n + 1 = B n B_{n+1} = B_n Bn+1=Bn

    因此如果迭代次数无穷大,那么 B n B_n Bn的值会无限接近于 x 0 x_0 x0或等于 x 0 x_0 x0

    我们用 P y t h o n Python Python模拟一下迭代过程:

    from math import *
    import matplotlib.pyplot as plt
    
    a = 6378137
    b = 6356752
    e = sqrt((a * a - b * b) / (a * a))
    X = -2758534
    Y = -4132154
    Z = 3986124
    R = sqrt(X * X + Y * Y)
    K = a * e * e / R
    C = Z / R
    x = []
    y = []
    
    
    def forWard(x):
        N = a / sqrt(1 - e * e * sin(B) * sin(B))
        tmp = sqrt(1/(sin(x) * sin(x)) - e * e)
        return atan(K / tmp + 0.1) + C
    
    
    B = C
    times = 100
    t = 0
    while True:
        t += 1
        nextB = forWard(B)
        #plt.plot(t, nextB, '+')
        B = nextB
        if t >= times:
            break
        x.append(t)
        y.append(B)
    plt.xlabel("times")
    plt.ylabel("B")
    plt.plot(x, y)
    plt.show()
    

在这里插入图片描述

理论和实践都告诉我们,使用迭代公式计算时, B B B 会趋近于某一个特殊的值 x 0 x_0 x0

下证:该 x 0 x_0 x0就是我们的目标值 B 实 B_实 B

​ 当 B = x 0 B = x_0 B=x0时,由于 x 0 x_0 x0 F o r w a r d Forward Forward 函数的一阶不动点,故有
tan ⁡ B = K 1 1 sin ⁡ 2 B − e 2 + C \tan B = K\dfrac{1}{\sqrt{\dfrac{1}{\sin^2 B}-e^2}}+C tanB=Ksin2B1e2 1+C
​ 将上式带入 L o s s Loss Loss函数中,可以得到 L o s s ( B ) = 0 Loss(B) = 0 Loss(B)=0,又由推论1知这样的 B B B是唯一的,因此 B = x 0 B = x_0 B=x0 就是所求值。

​ 证毕。

(上述证明其实显而易见,用迭代法的关键是证明一阶不动点存在, 但是该证明过程较为复杂,我们可以在物理领域内使用反证法:如果不存在一阶不动点,那么函数 L o s s Loss Loss就没有解,这与实际情况不符,故一定存在一阶不动点)。

第二小问直接带入公式计算。

3流程图

遍历法:在这里插入图片描述

二分法:
在这里插入图片描述

迭代法:
在这里插入图片描述

4、 程序代码

本部分只使用迭代法完成代码。

//
//  main.cpp
//  ChangePosition
//
//  Created by Sumbrella on 2020/4/28.
//  Copyright © 2020 Sumbrella All rights reserved.
//
# include <stdio.h>
# include <math.h>

typedef long long ll;

const ll a = 6378137;
const ll b = 6356752;
const double epslion = 1e-15;
double e, R, K, C;

// 配置需要使用的中间参数。
void configure(double X, double Y, double Z)
{
    e = sqrt((a * a - b * b) / (a * a));
    R = sqrt(X * X + Y * Y);
    K = a * e * e / R;
    C = Z / R;
}

// 迭代法迭代一次
double Forward(double B)
{
    double tmp = sqrt(1 / (sin(B) * sin (B)) - e * e );
    return atan(K / tmp + C);
}


void inputXYZ(double *X,double *Y, double *Z)
{
    printf("请输入X,Y,Z的值>>>(空格隔开)");
    scanf("%lf %lf %lf", X, Y, Z);
}


void getLBH(double X, double Y, double Z, double *pN, double *pL, double *pB, double *pH)
{
    double B = C;                   // 让B的初始值为C
    while (1)
    {
        double nB = Forward(B);
        if (nB - B < epslion && nB - B > -epslion)
            break;
        B = nB;
    }
    double N = a / sqrt(1 - e * e * sin(B) * sin(B));
    double L = atan2(Y, X);
    double H = R / cos(B) - N;
    
    *pN = N; *pL = L; *pB = B; *pH = H;
}


void showLBH(double L, double B, double H)
{
    printf("转换得到的大地坐标:\n");
    printf("L = %.3f, B = %.3f, H = %.3f\n", L, B, H);
}


void showXYZ(double N, double L, double B, double H)
{
    double X = (N + H) * cos(B) * cos(L);
    double Y = (N + H) * cos(B) * sin(L);
    double Z = (N * (1 - e * e) + H) * sin(B);
    printf("转换后得到的空间坐标:\n");
    // printf("N = %.6f", N);
    printf("X = %.6f, Y = %.6f, Z = %.6f\n", X, Y, Z);
}

int main()
{
    double X, Y, Z;
    double N, L, B, H;                // 定义变量

    inputXYZ(&X, &Y, &Z);             // 获取XYZ的值

    configure(X, Y, Z);               // 配置参数

    getLBH(X, Y, Z, &N, &L, &B, &H);  // 计算LBH的值

    showLBH(L, B, H);                 // 输出LBH的值

    showXYZ(N, L, B, H);              // 输出计算得到的XYZ的值

    
    return 0;
}

5、 主要函数和变量说明

主要函数说明见上图。
变量和常量说明:
(X,Y,Z)、(L,B,H)分别对应直角坐标和大地坐标下的坐标值。
C = Z / R, K = ae^2 / R。

6、 程序运行结果

在这里插入图片描述
转换后得到的坐标与原始数据完全一致,认为程序通过。

  • 7
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
以下是一个C语言编写的大地坐标空间直角坐标相互转换的代码示例: ```c #include <stdio.h> #include <math.h> #define PI 3.141592653589793 /* 地球椭球体参数 */ #define a 6378137.0 /* 长半轴 */ #define b 6356752.314245 /* 短半轴 */ #define f (a - b) / a /* 扁率 */ /* 弧度转度数 */ #define Rad2Deg (180.0 / PI) /* 度数转弧度 */ #define Deg2Rad (PI / 180.0) /* 由经纬度(单位为度)计算大地坐标 */ void GeodeticToRectangular(double BLH[3], double XYZ[3]) { double L = BLH[0] * Deg2Rad; /* 经度 */ double B = BLH[1] * Deg2Rad; /* 纬度 */ double H = BLH[2]; /* 高程 */ double e2 = f * (2 - f); /* 第一偏心率的平方 */ double N = a / sqrt(1 - e2 * sin(B) * sin(B)); /* 卯酉圈曲率半径 */ double X = (N + H) * cos(B) * cos(L); double Y = (N + H) * cos(B) * sin(L); double Z = (N * (1 - e2) + H) * sin(B); XYZ[0] = X; XYZ[1] = Y; XYZ[2] = Z; } /* 由大地坐标计算经纬度(单位为度) */ void RectangularToGeodetic(double XYZ[3], double BLH[3]) { double X = XYZ[0]; double Y = XYZ[1]; double Z = XYZ[2]; double e2 = f * (2 - f); /* 第一偏心率的平方 */ double r2 = X * X + Y * Y; /* 横坐标与纵坐标的平方和 */ double v = a * a / sqrt(r2 * (a * a / b * b) + Z * Z); /* 卯酉圈曲率半径 */ double alpha = atan(Z / sqrt(r2)); /* 点的地心角 */ double sinalpha = sin(alpha); double cossalpha = cos(alpha); double B = atan((Z + v * e2 * sinalpha * sinalpha * sinalpha) / (r2 - a * a / b * b * v * cossalpha * cossalpha * cossalpha)); /* 纬度 */ double L = atan(Y / X); /* 经度 */ double H = sqrt(r2 / cos(B) - a * a / sqrt(1 - e2 * sin(B) * sin(B))); /* 高程 */ BLH[0] = L * Rad2Deg; BLH[1] = B * Rad2Deg; BLH[2] = H; } int main() { /* 经纬度大地坐标转换空间直角坐标 */ double BLH[3] = {120.189, 30.183, 50.0}; /* 经度(度)、纬度(度)、高程(米)*/ double XYZ[3] = {0.0}; /* 横坐标、纵坐标、高程 */ GeodeticToRectangular(BLH, XYZ); printf("BLH = %lf, %lf, %lf\n", BLH[0], BLH[1], BLH[2]); printf("XYZ = %lf, %lf, %lf\n", XYZ[0], XYZ[1], XYZ[2]); /* 空间直角坐标转换为经纬度大地坐标 */ double XYZ2[3] = {1000000.0, 300000.0, 500000.0}; /* 横坐标、纵坐标、高程 */ double BLH2[3] = {0.0}; /* 经度(度)、纬度(度)、高程(米)*/ RectangularToGeodetic(XYZ2, BLH2); printf("XYZ = %lf, %lf, %lf\n", XYZ2[0], XYZ2[1], XYZ2[2]); printf("BLH = %lf, %lf, %lf\n", BLH2[0], BLH2[1], BLH2[2]); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sumbrella_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值