WGS84与大地2000坐标转换(Java,C#,Dart)

2023-05-16

一、坐标转换的必要性

平面坐标在道路测绘,隧道测量,农业建筑业等室外勘测等方面有着广泛的应用,各行业基本都会涉及到移动端测量之后不能满足屏幕坐标,所以需要经纬度的转换,移动端勘测结果都是WGS84坐标或者GCJ-02格式坐标,而实际工程项目中却需要的是平面坐标[CGCS2000]或[北京坐标]或[西安坐标系],转换过程都是通过高斯投影来计算结果。高斯投影毕竟是将一个不规则的椭圆投影必定会变形,因此在实际应用中,要考虑尽量减小误差。而移动端进行高斯投影的结果误差太大而无法用到实际应用中。所以通过分析高斯投影过程中产生误差的决定因素来减少误杀对于所有行业都显的很必要。

二、我国三大常用坐标系

我国三大常用坐标系区别

  • 北京54
  • 西安80
  • WGS-84

1、北京54坐标系(BJZ54)

北京54坐标系为参心大地坐标系
北京54坐标系,属三心坐标系,长轴6378245m,短轴6356863,扁率1/298. 3;

2、西安80坐标系

1978年4月在西安召开全国天文大地网平差会议,确定重新定位,建立我国新的坐标系。(即1985国家高程基准)。西安80坐标系,属三心坐标系,长轴6378140m。

3、WGS-84坐标系(World Geodetic System) 是-种国际上采用的地心坐标系。

坐标原点为地球质心,其地心空间直角坐标系的Z轴指向国际时间局(BIH)WGS84坐标系,长轴6378137. 000m,短轴6356752.314,扁率1/298. 257223563。由于采用的椭球基准不一样,并且由于投影的局限性,使的全国各地并不存在一至的转换参数。对于这种转换由于量较大,有条件的话,一般都采用GPS联测己知点,应用GPS软件自动完成坐标的转换。当然若条件不许可,且有足够的重
合点,也可以进行人工解算。

4、2000 国家大地坐标系

英文缩写为CGCS2000。2000国家大地坐标系是全球地心坐标系在我国的具
体体现,其原点为包括海洋和大气的整个地球的质量中心。2000国家大地坐标
系采用的地球椭球参数如下:
长半轴a=6378137m, 扁率f=1/298.257222101,
地心引力常数GM=3. 986004418X 1014m3s-2
自转角速度w=7.292115X 10-5rads- 1

参考资料:

2000国家大地坐标系
中央子午线经度的计算
WGS84 经纬度坐标到北京 54 高斯投影坐标的转换

5,注意点:

1.明确是WGS8和那个坐标系转换,确定参数值影响很大

大地2000的参数:
double a = 6378137; //椭球长半轴
double b = 6356752.3142451795; //椭球短半轴
double e = 0.081819190842621; //第一偏心率
double eC = 0.0820944379496957; //第二偏心率
对应的其他坐标系自己可以百度。

2.明确自己项目点经纬度所在的带号计算出带所在的中央带经度

这里自己可以百度看看,我国有两种分带,3度带,6度带。
360度=3度X. => X=120份
360度=6度
Y => Y=60份
360度=2π弧度
每度=2π/360=π/180

在这里插入图片描述

地球是圆的,如上图东西分界线为0度,左右分为东经西经,如果是三分带,那么可以知道,-1.5度(即西经)和 1.5度(即东经)之间的夹角就是3度。1.5到4.5之间和-4.5度(西经)到-1.5(西经)之间是关于子午线对称的,所以经度纬度相等的有两个。我们大地坐标一般避免经度为负都会将平面地图坐标系向左平移500公里。如下图。
在这里插入图片描述
所谓的项目点所在的中央经度。例如3分带划分,东经1.5度和4.5度之间就是3,同样3.5和6.5之间就是5, 我们项目点东经3.53226,3.74,3.2334348,4.02323,5.023232…那么它的中央经度为带的中点 3。转换为计算机代码,Math.roun(经度/3)*3.

Dart代码

import 'dart:math';
import 'package:luhenchang_plugin/map/bean/latlng.dart';
import 'bean/tuple.dart';
/**
 * 版权:渤海新能 版权所有
 *
 * @author feiWang
 * 版本:1.5
 * 创建日期:2020/10/16
 * 描述:AndroidDaggerStudy
 * E-mail : 1276998208@qq.com
 * CSDN:https://blog.csdn.net/m0_37667770/article
 * GitHub:https://github.com/luhenchang
 */
class LonLatAndXY {
  //@将WGS84经纬度转为大地2000坐标。我们是国家电网项目数据很精确的了。
  //@param B 纬度
  //@param L 经度
  //@param degree //
  //@param withBand 默认=false
  //@return
  Tuple gps84ToXY(double B, double L, double degree, {withBand = false}) {
    List<double> xy = new List();
    xy.add(0);
    xy.add(0);
    double a = 6378137; //椭球长半轴
    double b = 6356752.3142451795; //椭球短半轴
    double e = 0.081819190842621; //第一偏心率
    double eC = 0.0820944379496957; //第二偏心率

    double L0 = 0; //中央子午线经度
    int n = 0; //带号
    if (degree == 6) {
      //6度
      n = ((L + degree / 2) / degree).round().toInt();
      L0 = degree * n - degree / 2;
    } else {
      //3度
      n = (L / degree).round().toInt();
      L0 = degree * n;
    }
    //开始计算
    double radB = B * pi / 180; //纬度(弧度)
    double radL = L * pi / 180; //经度(弧度)
    double deltaL = (L - L0) * pi / 180; //经度差(弧度)
    double N = a * a / b / sqrt(1 + eC * eC * cos(radB) * cos(radB));
    double C1 = 1.0 +
        3.0 / 4 * e * e +
        45.0 / 64 * pow(e, 4) +
        175.0 / 256 * pow(e, 6) +
        11025.0 / 16384 * pow(e, 8);
    double C2 = 3.0 / 4 * e * e +
        15.0 / 16 * pow(e, 4) +
        525.0 / 512 * pow(e, 6) +
        2205.0 / 2048 * pow(e, 8);
    double C3 = 15.0 / 64 * pow(e, 4) +
        105.0 / 256 * pow(e, 6) +
        2205.0 / 4096 * pow(e, 8);
    double C4 = 35.0 / 512 * pow(e, 6) + 315.0 / 2048 * pow(e, 8);
    double C5 = 315.0 / 131072 * pow(e, 8);
    double t = tan(radB);
    double eta = eC * cos(radB);
    double X = a *
        (1 - e * e) *
        (C1 * radB -
            C2 * sin(2 * radB) / 2 +
            C3 * sin(4 * radB) / 4 -
            C4 * sin(6 * radB) / 6 +
            C5 * sin(8 * radB));

    xy[0] = X +
        N *
            sin(radB) *
            cos(radB) *
            pow(deltaL, 2) *
            (1 +
                pow(deltaL * cos(radB), 2) *
                    (5 - t * t + 9 * eta * eta + 4 * pow(eta, 4)) /
                    12 +
                pow(deltaL * cos(radB), 4) *
                    (61 - 58 * t * t + pow(t, 4)) /
                    360) /
            2;
    xy[1] = N *
            deltaL *
            cos(radB) *
            (1 +
                pow(deltaL * cos(radB), 2) * (1 - t * t + eta * eta) / 6 +
                pow(deltaL * cos(radB), 4) *
                    (5 -
                        18 * t * t +
                        pow(t, 4) -
                        14 * eta * eta -
                        58 * eta * eta * t * t) /
                    120) +
        500000; // +n * 1000000;
    return new Tuple(xy[0], xy[1]);
  }

  //@将大地2000转为WGS84
  //高斯投影反算为大地平面。
  // x,y ,高斯平面坐标点
  //L0 通过经纬度来获取中央带所在带的角度
  //return B纬度 , L经度
  LatLng xyTowgs84(double x, double y, double L0) {
    //中央子午线经度
    //WGS-84   椭球体参数
    double a = 6378137.0; //major semi axis
    double efang = 0.0066943799901413; //square of e
    double e2fang = 0.0067394967422764; //suqre of e2

    y = y - 500000;

    //主曲率计算
    double m0, m2, m4, m6, m8;
    m0 = a * (1 - efang);
    m2 = 3.0 / 2.0 * efang * m0;
    m4 = efang * m2 * 5.0 / 4.0;
    m6 = efang * m4 * 7.0 / 6.0;
    m8 = efang * m6 * 9.0 / 8.0;

    //子午线曲率计算
    double a0, a2, a4, a6, a8;
    a0 = m0 + m2 / 2.0 + m4 * 3.0 / 8.0 + m6 * 5.0 / 16.0 + m8 * 35.0 / 128.0;
    a2 = m2 / 2.0 + m4 / 2.0 + m6 * 15.0 / 32.0 + m8 * 7.0 / 16.0;
    a4 = m4 / 8.0 + m6 * 3.0 / 16.0 + m8 * 7.0 / 32.0;
    a6 = m6 / 32.0 + m8 / 16.0;
    a8 = m8 / 128.0;

    double X = x;
    double FBf = 0;
    double Bf0 = X / a0, Bf1 = 0;

    //计算Bf的值,直到满足条件
    while ((Bf0 - Bf1) >= 0.0001) {
      Bf1 = Bf0;
      FBf = -a2*sin(2*Bf0)/2 +a4*sin(4*Bf0)/4 -a6*sin(6*Bf0)/6 + a8*sin(8*Bf0)/8;
      Bf0 = (X-FBf)/a0;
    }
    double Bf = Bf0;
    //计算公式中参数
    double Wf = sqrt(1 - efang * sin(Bf) * sin(Bf));
    double Nf = a / Wf;
    double Mf = a * (1 - efang) / pow(Wf, 3);
    double nffang = e2fang * cos(Bf) * cos(Bf);
    double tf = tan(Bf);
    double B = Bf - tf * y * y / (2 * Mf * Nf) + tf * (5 + 3 * tf * tf + nffang - 9 * nffang * tf * tf) * pow(y, 4) / (24 * Mf * pow(Nf, 3)) - tf * (61 + 90 * tf * tf + 45 * pow(tf, 4)) * pow(y, 6) / (720 * Mf * pow(Nf, 5));
    double l = y/(Nf*cos(Bf)) - (1+2*tf*tf+nffang)*pow(y, 3)/(6*pow(Nf, 3)*cos(Bf)) + (5+28*tf*tf +24*pow(tf, 4))*pow(y, 5)/(120*pow(Nf, 5)*cos(Bf));
    double L = l + L0;
    //转化成为十进制经纬度格式
    var array_B =rad2dms(B);
    var array_L =rad2dms(L);
    double Bdec = dms2dec(array_B);
    double Ldec = dms2dec(array_L);

    return new LatLng(Bdec, Ldec);
  }

  double p = 180.0 / pi * 3600;

  //通过经纬度来获取中央带所在带的角度
  //@param B 纬度
  //@param L 经度
  //@param N 带[3,6带度]
  double gaussLongToDegreen(double B, double L, int N) {
    //计算该地区的中央子午线经度
    double L00 = (L / 3).round() * 3.toDouble();
    double degreen = L00 / 180 * 3.1415926;
    return degreen;
  }

  //将弧度�?�转化为度分��?
  dynamic rad2dms(double rad) {
    List<int> a = [0,0,0];
    double dms = rad * p;
    a[0] = (dms / 3600.0).floor();
    a[1] = ((dms - a[0] * 3600) / 60.0).floor();
    a[2] = ((dms - a[0] * 3600).toInt() - a[1] * 60);
    return a;
  }

  //将度分秒转化为十进制坐标
  double dms2dec(dynamic dms) {
    double dec = 0.0;
    dec = dms[0] + dms[1] / 60.0 + dms[2] / 3600.0;
    return dec;
  }
}

调用

void main() {
  LonLatAndXY latAndXY=new LonLatAndXY();
  Tuple tuple=latAndXY.gps84ToXY(37.203267527777776,115.40381543333334,3);
  print(tuple.toString());
  //我国划分为3或者6分带。这里很多人需要注意。根据你们测量仪器来写。
  //我们这个国家电网项目。精确度很好了已经。
  double degreen=latAndXY.gaussLongToDegreen(37.203267527777776,115.40381543333334,3);
  print("degeljler"+degreen.toString());
  LatLng latLng=latAndXY.xyTowgs84(4119992.7554638153,624625.9309849278,degreen);
  print(latLng.toString());

}
package com.example.androiddaggerstudy.lat

/**
 * 版权:渤海新能 版权所有
 *
 * @author feiWang
 * 版本:1.5
 * 创建日期:2020/10/16
 * 描述:AndroidDaggerStudy
 * E-mail : 1276998208@qq.com
 * CSDN:https://blog.csdn.net/m0_37667770/article
 * GitHub:https://github.com/luhenchang
 */
object RtkUtil {
    //@将大地2000转为WGS84
    //高斯投影反算为大地平面。
    // x,y ,高斯平面坐标点
    //L0 通过经纬度来获取中央带所在带的角度
    //return B纬度 , L经度
    fun xyTowgs84(x: Double, y: Double, L0: Double): Tuple {
        //中央子午线经度
        //WGS-84   椭球体参数
        var y = y
        val a = 6378137.0 //major semi axis
        val efang = 0.0066943799901413 //square of e
        val e2fang = 0.0067394967422764 //suqre of e2
        y = y - 500000
        //主曲率计算
        val m0: Double
        val m2: Double
        val m4: Double
        val m6: Double
        val m8: Double
        m0 = a * (1 - efang)
        m2 = 3.0 / 2.0 * efang * m0
        m4 = efang * m2 * 5.0 / 4.0
        m6 = efang * m4 * 7.0 / 6.0
        m8 = efang * m6 * 9.0 / 8.0

        //子午线曲率计算
        val a0: Double
        val a2: Double
        val a4: Double
        val a6: Double
        val a8: Double
        a0 = m0 + m2 / 2.0 + m4 * 3.0 / 8.0 + m6 * 5.0 / 16.0 + m8 * 35.0 / 128.0
        a2 = m2 / 2.0 + m4 / 2.0 + m6 * 15.0 / 32.0 + m8 * 7.0 / 16.0
        a4 = m4 / 8.0 + m6 * 3.0 / 16.0 + m8 * 7.0 / 32.0
        a6 = m6 / 32.0 + m8 / 16.0
        a8 = m8 / 128.0
        var FBf = 0.0
        var Bf0 = x / a0
        var Bf1 = 0.0

        //计算Bf的值,直到满足条件
        while (Bf0 - Bf1 >= 0.0001) {
            Bf1 = Bf0
            FBf =
                -a2 * Math.sin(2 * Bf0) / 2 + a4 * Math.sin(4 * Bf0) / 4 - a6 * Math.sin(6 * Bf0) / 6 + a8 * Math.sin(
                    8 * Bf0
                ) / 8
            Bf0 = (x - FBf) / a0
        }
        val Bf = Bf0
        //计算公式中参数
        val Wf = Math.sqrt(1 - efang * Math.sin(Bf) * Math.sin(Bf))
        val Nf = a / Wf
        val Mf = a * (1 - efang) / Math.pow(Wf, 3.0)
        val nffang = e2fang * Math.cos(Bf) * Math.cos(Bf)
        val tf = Math.tan(Bf)
        val B =
            Bf - tf * y * y / (2 * Mf * Nf) + tf * (5 + 3 * tf * tf + nffang - 9 * nffang * tf * tf) * Math.pow(
                y,
                4.0
            ) / (24 * Mf * Math.pow(Nf, 3.0)) - tf * (61 + 90 * tf * tf + 45 * Math.pow(
                tf,
                4.0
            )) * Math.pow(y, 6.0) / (720 * Mf * Math.pow(Nf, 5.0))
        val l =
            y / (Nf * Math.cos(Bf)) - (1 + 2 * tf * tf + nffang) * Math.pow(y, 3.0) / (6 * Math.pow(
                Nf,
                3.0
            ) * Math.cos(Bf)) + (5 + 28 * tf * tf + 24 * Math.pow(tf, 4.0)) * Math.pow(
                y,
                5.0
            ) / (120 * Math.pow(Nf, 5.0) * Math.cos(Bf))
        val L = l + L0
        //转化成为十进制经纬度格式
        val array_B = rad2dms(B)
        val array_L = rad2dms(L)
        val Bdec = dms2dec(array_B)
        val Ldec = dms2dec(array_L)
        return Tuple(Bdec, Ldec)
    }

    var p = 180.0 / Math.PI * 3600

    //通过经纬度来获取中央带所在带的角度
    //@param B 纬度
    //@param L 经度
    //@param N 带[3,6带度]
    fun gaussLongToDegreen(B: Double, L: Double, N: Int): Double {
        //计算该地区的中央子午线经度
        val L00 = Math.round(L / 3) * 3.toDouble()
        return L00 / 180 * 3.1415926
    }

    //    //将弧度�?�转化为度分��?
    //    dynamic rad2dms(double rad) {
    //        List<int> a = [0,0,0];
    //        double dms = rad * p;
    //        a[0] = (dms / 3600.0).floor();
    //        a[1] = ((dms - a[0] * 3600) / 60.0).floor();
    //        a[2] = ((dms - a[0] * 3600).toInt() - a[1] * 60);
    //        return a;
    //    }
    //将弧度�?�转化为度分��?
    fun rad2dms(rad: Double): DoubleArray {
        val a = doubleArrayOf(0.0, 0.0, 0.0)
        val dms = rad * p
        a[0] = Math.floor(dms / 3600.0)
        a[1] = Math.floor((dms - a[0] * 3600) / 60.0)
        a[2] = Math.floor(dms - a[0] * 3600).toInt() - a[1] * 60
        return a
    }

    //将度分秒转化为十进制坐标
    fun dms2dec(dms: DoubleArray): Double {
        var dec = 0.0
        dec = dms[0] + dms[1] / 60.0 + dms[2] / 3600.0
        return dec
    }

    /**
     * @将WGS84经纬度转为大地2000坐标。我们是国家电网项目数据很精确的了。
     * @param B        纬度
     * @param L        经度
     * @param degree
     * @param withBand 默认=false
     * @return
     */
    fun GetXY(B: Double, L: Double, degree: Double, withBand: Boolean?): Tuple {
        val xy = doubleArrayOf(0.0, 0.0)
        val a = 6378137.0 //椭球长半轴
        val b = 6356752.3142451795 //椭球短半轴
        val e = 0.081819190842621 //第一偏心率
        val eC = 0.0820944379496957 //第二偏心率
        var L0 = 0.0 //中央子午线经度
        var n = 0 //带号
        if (degree == 6.0) {
            //6度
            n = Math.round((L + degree / 2) / degree).toInt()
            L0 = degree * n - degree / 2
        } else {
            //3度
            n = Math.round(L / degree).toInt()
            L0 = degree * n
        }

        //开始计算
        val radB = B * Math.PI / 180 //纬度(弧度)
        val radL = L * Math.PI / 180 //经度(弧度)
        val deltaL = (L - L0) * Math.PI / 180 //经度差(弧度)
        val N = a * a / b / Math.sqrt(1 + eC * eC * Math.cos(radB) * Math.cos(radB))
        val C1 = 1.0 + 3.0 / 4 * e * e + 45.0 / 64 * Math.pow(e, 4.0) + 175.0 / 256 * Math.pow(
            e,
            6.0
        ) + 11025.0 / 16384 * Math.pow(e, 8.0)
        val C2 = 3.0 / 4 * e * e + 15.0 / 16 * Math.pow(e, 4.0) + 525.0 / 512 * Math.pow(
            e,
            6.0
        ) + 2205.0 / 2048 * Math.pow(e, 8.0)
        val C3 = 15.0 / 64 * Math.pow(e, 4.0) + 105.0 / 256 * Math.pow(
            e,
            6.0
        ) + 2205.0 / 4096 * Math.pow(e, 8.0)
        val C4 = 35.0 / 512 * Math.pow(e, 6.0) + 315.0 / 2048 * Math.pow(e, 8.0)
        val C5 = 315.0 / 131072 * Math.pow(e, 8.0)
        val t = Math.tan(radB)
        val eta = eC * Math.cos(radB)
        val X =
            a * (1 - e * e) * (C1 * radB - C2 * Math.sin(2 * radB) / 2 + C3 * Math.sin(4 * radB) / 4 - C4 * Math.sin(
                6 * radB
            ) / 6 + C5 * Math.sin(8 * radB))
        xy[0] = X + N * Math.sin(radB) * Math.cos(radB) * Math.pow(deltaL, 2.0) * (1 + Math.pow(
            deltaL * Math.cos(radB),
            2.0
        ) * (5 - t * t + 9 * eta * eta + 4 * Math.pow(
            eta,
            4.0
        )) / 12 + Math.pow(deltaL * Math.cos(radB), 4.0) * (61 - 58 * t * t + Math.pow(
            t,
            4.0
        )) / 360) / 2
        xy[1] = N * deltaL * Math.cos(radB) * (1 + Math.pow(
            deltaL * Math.cos(radB),
            2.0
        ) * (1 - t * t + eta * eta) / 6 + Math.pow(
            deltaL * Math.cos(radB),
            4.0
        ) * (5 - 18 * t * t + Math.pow(
            t,
            4.0
        ) - 14 * eta * eta - 58 * eta * eta * t * t) / 120) + 500000 // +n * 1000000;
        return Tuple(xy[0], xy[1])
    }
}

class Tuple(var b: Double, var l: Double) {

    override fun toString(): String {
        return "Tuple{" +
                "B=" + b +
                ", L=" + l +
                '}'
    }
}


kotlin调用如下

object Tests {
    @JvmStatic
    fun main(args: Array<String>) {
        val tuple = RtkUtil.GetXY(37.203267527777776, 115.40381543333334, 3.0, false)
        //Tuple{B=4119992.7554638153, L=624625.9309849278}
        val degreen: Double = RtkUtil.gaussLongToDegreen(37.203267527777776, 115.40381543333334, 3)
        val xyTowgs84 = RtkUtil.xyTowgs84(4119992.7554638153, 624625.9309849278, degreen)
        //main:Tuple{B=37.20305555555556, L=115.40361111111112}
        print("main:$tuple")
        print("main:$xyTowgs84")

    }
}

Java代码如下

package com.example.androiddaggerstudy.lat;
import org.jetbrains.annotations.NotNull;
import kotlin.jvm.internal.Intrinsics;
/**
 * 版权:渤海新能 版权所有
 *
 * @author feiWang
 * 版本:1.5
 * 创建日期:2020/10/16
 * 描述:AndroidDaggerStudy
 * E-mail : 1276998208@qq.com
 * CSDN:https://blog.csdn.net/m0_37667770/article
 * GitHub:https://github.com/luhenchang
 */
public class JavaRtkUtils {
    private  double p = 206264.80624709636D;
    @NotNull
    public  Tuple xyTowgs84(double x, double y, double L0) {
        double a = 6378137.0D;
        double efang = 0.0066943799901413D;
        double e2fang = 0.0067394967422764D;
        y = y - (double)500000;
        double m0 = 0.0D;
        double m2 = 0.0D;
        double m4 = 0.0D;
        double m6 = 0.0D;
        double m8 = 0.0D;
        m0 = a * ((double)1 - efang);
        m2 = 1.5D * efang * m0;
        m4 = efang * m2 * 5.0D / 4.0D;
        m6 = efang * m4 * 7.0D / 6.0D;
        m8 = efang * m6 * 9.0D / 8.0D;
        double a0 = 0.0D;
        double a2 = 0.0D;
        double a4 = 0.0D;
        double a6 = 0.0D;
        double a8 = 0.0D;
        a0 = m0 + m2 / 2.0D + m4 * 3.0D / 8.0D + m6 * 5.0D / 16.0D + m8 * 35.0D / 128.0D;
        a2 = m2 / 2.0D + m4 / 2.0D + m6 * 15.0D / 32.0D + m8 * 7.0D / 16.0D;
        a4 = m4 / 8.0D + m6 * 3.0D / 16.0D + m8 * 7.0D / 32.0D;
        a6 = m6 / 32.0D + m8 / 16.0D;
        a8 = m8 / 128.0D;
        double FBf = 0.0D;
        double Bf0 = x / a0;

        for(double Bf1 = 0.0D; Bf0 - Bf1 >= 1.0E-4D; Bf0 = (x - FBf) / a0) {
            Bf1 = Bf0;
            FBf = -a2 * Math.sin((double)2 * Bf0) / (double)2 + a4 * Math.sin((double)4 * Bf0) / (double)4 - a6 * Math.sin((double)6 * Bf0) / (double)6 + a8 * Math.sin((double)8 * Bf0) / (double)8;
        }

        double Wf = Math.sqrt((double)1 - efang * Math.sin(Bf0) * Math.sin(Bf0));
        double Nf = a / Wf;
        double Mf = a * ((double)1 - efang) / Math.pow(Wf, 3.0D);
        double nffang = e2fang * Math.cos(Bf0) * Math.cos(Bf0);
        double tf = Math.tan(Bf0);
        double B = Bf0 - tf * y * y / ((double)2 * Mf * Nf) + tf * ((double)5 + (double)3 * tf * tf + nffang - (double)9 * nffang * tf * tf) * Math.pow(y, 4.0D) / ((double)24 * Mf * Math.pow(Nf, 3.0D)) - tf * ((double)61 + (double)90 * tf * tf + (double)45 * Math.pow(tf, 4.0D)) * Math.pow(y, 6.0D) / ((double)720 * Mf * Math.pow(Nf, 5.0D));
        double l = y / (Nf * Math.cos(Bf0)) - ((double)1 + (double)2 * tf * tf + nffang) * Math.pow(y, 3.0D) / ((double)6 * Math.pow(Nf, 3.0D) * Math.cos(Bf0)) + ((double)5 + (double)28 * tf * tf + (double)24 * Math.pow(tf, 4.0D)) * Math.pow(y, 5.0D) / ((double)120 * Math.pow(Nf, 5.0D) * Math.cos(Bf0));
        double L = l + L0;
        double[] array_B = this.rad2dms(B);
        double[] array_L = this.rad2dms(L);
        double Bdec = this.dms2dec(array_B);
        double Ldec = this.dms2dec(array_L);
        return new Tuple(Bdec, Ldec);
    }
    public  double gaussLongToDegreen(double B, double L, int N) {
        double L00 = (double)Math.round(L / (double)3) * (double)3;
        return L00 / (double)180 * 3.1415926D;
    }
    @NotNull
    public  double[] rad2dms(double rad) {
        double[] a = new double[]{0.0D, 0.0D, 0.0D};
        double dms = rad * p;
        a[0] = Math.floor(dms / 3600.0D);
        a[1] = Math.floor((dms - a[0] * (double)3600) / 60.0D);
        a[2] = (double)((int)Math.floor(dms - a[0] * (double)3600)) - a[1] * (double)60;
        return a;
    }

    public  double dms2dec(@NotNull double[] dms) {
        Intrinsics.checkNotNullParameter(dms, "dms");
        double dec = 0.0D;
        dec = dms[0] + dms[1] / 60.0D + dms[2] / 3600.0D;
        return dec;
    }

    @NotNull
    public  Tuple GetXY(double B, double L, double degree) {
        double[] xy = new double[]{0.0D, 0.0D};
        double a = 6378137.0D;
        double b = 6356752.314245179D;
        double e = 0.081819190842621D;
        double eC = 0.0820944379496957D;
        double L0 = 0.0D;
        int n;
        if (degree == 6.0D) {
            n = (int)Math.round((L + degree / (double)2) / degree);
            L0 = degree * (double)n - degree / (double)2;
        } else {
            n = (int)Math.round(L / degree);
            L0 = degree * (double)n;
        }

        double radB = B * 3.141592653589793D / (double)180;
        double radL = L * 3.141592653589793D / (double)180;
        double deltaL = (L - L0) * 3.141592653589793D / (double)180;
        double N = a * a / b / Math.sqrt((double)1 + eC * eC * Math.cos(radB) * Math.cos(radB));
        double C1 = 1.0D + 0.75D * e * e + 0.703125D * Math.pow(e, 4.0D) + 0.68359375D * Math.pow(e, 6.0D) + 0.67291259765625D * Math.pow(e, 8.0D);
        double C2 = 0.75D * e * e + 0.9375D * Math.pow(e, 4.0D) + 1.025390625D * Math.pow(e, 6.0D) + 1.07666015625D * Math.pow(e, 8.0D);
        double C3 = 0.234375D * Math.pow(e, 4.0D) + 0.41015625D * Math.pow(e, 6.0D) + 0.538330078125D * Math.pow(e, 8.0D);
        double C4 = 0.068359375D * Math.pow(e, 6.0D) + 0.15380859375D * Math.pow(e, 8.0D);
        double C5 = 0.00240325927734375D * Math.pow(e, 8.0D);
        double t = Math.tan(radB);
        double eta = eC * Math.cos(radB);
        double X = a * ((double)1 - e * e) * (C1 * radB - C2 * Math.sin((double)2 * radB) / (double)2 + C3 * Math.sin((double)4 * radB) / (double)4 - C4 * Math.sin((double)6 * radB) / (double)6 + C5 * Math.sin((double)8 * radB));
        xy[0] = X + N * Math.sin(radB) * Math.cos(radB) * Math.pow(deltaL, 2.0D) * ((double)1 + Math.pow(deltaL * Math.cos(radB), 2.0D) * ((double)5 - t * t + (double)9 * eta * eta + (double)4 * Math.pow(eta, 4.0D)) / (double)12 + Math.pow(deltaL * Math.cos(radB), 4.0D) * ((double)61 - (double)58 * t * t + Math.pow(t, 4.0D)) / (double)360) / (double)2;
        xy[1] = N * deltaL * Math.cos(radB) * ((double)1 + Math.pow(deltaL * Math.cos(radB), 2.0D) * ((double)1 - t * t + eta * eta) / (double)6 + Math.pow(deltaL * Math.cos(radB), 4.0D) * ((double)5 - (double)18 * t * t + Math.pow(t, 4.0D) - (double)14 * eta * eta - (double)58 * eta * eta * t * t) / (double)120) + (double)500000;
        return new Tuple(xy[0], xy[1]);
    }

}

Java调用如下

  var rtkUtils=JavaRtkUtils()
        val tuple1=rtkUtils.GetXY(37.203267527777776, 115.40381543333334, 3.0)
        val degreen1: Double = rtkUtils.gaussLongToDegreen(37.203267527777776, 115.40381543333334, 3)
        val xyTowgs841 = rtkUtils.xyTowgs84(4119992.7554638153, 624625.9309849278, degreen1)
        print("JvMain:$tuple1")
        print("JvMain:$xyTowgs841")

国家电力局rtk测量的如下数据。
在这里插入图片描述

看我转换之后的测试了很多都转换为大地2000坐标精确到0.01了。
Tuple{B: 4119992.7554638153, L: 624625.9309849278}
对比 rtf=4119992.756 624625.9311
这紧缺度我怀疑rtf精确度和我们这个差不多。

我们进行大地2000转为WGS84
LatLng{lat: 37.20305555555556, lng: 115.40361111111112}
下面是rtf测量的结果:精确度0.0001了。地图上测试没啥大问题。基本在一点。距离在0.1米内的误差。
37.203267527777776,115.40381543333334

5、最后我封装成库了。

使用
dependencies:
luhenchang_plugin: ^0.1.2
库地址

  LonLatAndXY latAndXY=new LonLatAndXY();
  Tuple tuple=latAndXY.gps84ToXY(37.203267527777776,115.40381543333334,3);
  //我国划分为3或者6分带。这里很多人需要注意。根据你们测量仪器来写。
  //我们这个国家电网项目。精确度很好了已经。
  double degreen=latAndXY.gaussLongToDegreen(37.203267527777776,115.40381543333334,3);
  LatLng latLng=latAndXY.xyTowgs84(4119992.7554638153,624625.9309849278,degreen);

如下App项目使用转换:

在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WGS84与大地2000坐标转换(Java,C#,Dart) 的相关文章

  • 使用阿里云OSS实现图片上传案例

    文章目录 前言一 OSS是什么 xff1f 二 使用步骤1 开通OSS服务2 创建bucket3 创建子账户4 查看帮助文档 xff0c 编写后端代码5 layui前端 总结 前言 在正式的开发环境中 xff0c 类似图片的静态资源一般不会
  • 使用EasyExcel实现Excel的导入导出

    文章目录 前言一 EasyExcel是什么 xff1f 二 使用步骤1 导入依赖2 编写文件上传配置3 配置表头对应实体类4 监听器编写5 控制层6 前端代码 总结 前言 在真实的开发者场景中 xff0c 经常会使用excel作为数据的载体
  • 使用linux部署项目步骤

    文章目录 前言一 服务器环境配置二 数据库导入三 项目打包1 修改项目中的访问路径2 修改db properties的数据库访问路径3 打包4 修改配置 xff0c 启动服务 四 测试总结 前言 今天学习了在服务器中部署项目 xff0c 记
  • IntelliJ IDEA 2023.1 最新变化 【附带ChatGPT教程】

    ChatGPT开源公众号 xff1a https gitee com wy521a astar weixin mp最新变化 2023 1主要更新用户体验编辑器JavaScala分析器版本控制系统构建工具运行 调试框架和技术DockerKub
  • SpringBoot整合Quartz以及异步调用

    文章目录 前言一 异步方法调用1 导入依赖2 创建异步执行任务线程池3 创建业务层接口和实现类4 创建业务层接口和实现类 二 测试定时任务1 导入依赖2 编写测试类 xff0c 开启扫描定时任务3 测试 三 实现定时发送邮件案例1 邮箱开启
  • SpringCloud详解07-Nacos配置管理

    系列文章目录 文章目录 系列文章目录前言一 nacos配置管理二 使用步骤1 基本配置2 导入Nacos配置 三 配置实时刷新总结 前言 接上篇 xff0c 本篇记录Nacos的配置管理 所有的代码都是基于前篇的基础上 一 nacos配置管
  • 使用SpringBoot+JWT+MybatisPlus实现简单的登陆状态验证

    文章目录 前言一 JWT是什么 xff1f 二 使用步骤1 创建项目 xff0c 导入依赖 xff0c 配置 引入工具类2 编写LoginController和UserController3 编写跨域拦截器和token验证拦截器4 全局拦截
  • 浅谈分布式锁

    文章目录 前言一 分布式锁 xff1f 二 实现思路1 存储一个唯一的键2 使用setnx3 给键设置唯一值4 因为超时导致的并发执行4 Redission5 代码实现 总结 前言 最近学习了分布式锁 xff0c 本篇来记录一下 一 分布式
  • 初识STM32单片机

    目录 一 单片机基本认知 二 STM系列单片机命名规则 三 标准库与HAL库区别 四 通用输入输出端口GPIO 五 推挽输出与开漏输出 六 复位和时钟控制 xff08 RCC xff09 七 时钟控制 八 中断和事件 九 定时器介绍 一 单
  • ubuntu16.04 安装ctags,以及一些使用方法的介绍

    vim对于新手来说太不友好了 xff0c 今天折腾ctags折腾了一天终于是有所收获 xff0c 现在来大概介绍一下 首先ctags顾名思义 xff0c 就算给各种库和 cpp文件打标签 xff0c 得到的标签文件给后面omni compl
  • Lottie 框架Native Heap内存爆炸问题解决

    Lottie框架Native Heap内存爆炸问题解决 Lottie 是在 Android 和 iOS上 原生渲染 的After Effects xff08 AE xff09 动画 Lottie是 Airbnb 开源 的支持Android
  • Kali 美化

    配置完Kali xff0c 作为一个正常人 xff0c 对于默认的Gnome的界面都会感觉到一丝丝不满意 xff0c 毕竟太丑了嘛 那么 xff0c 我们需要对Kali美化一下 首先是桌面切换 xff0c 如果你不喜欢Gnome xff0c
  • 安装Arch(含输入法配置)

    先创建一个虚拟机 xff0c 这是我的虚拟机配置 xff1a 然后查看虚拟机的配置文件 xff0c 编辑Arch vmx文件 xff0c 最后一行添加 firmware 61 34 efi 34 保存退出 xff0c 启动你的虚拟机 不出意
  • 使用ViewPager + FragmentStatePagerAdapter 切换后页面数据加载空白或者丢失

    开头直接上结论 xff01 xff01 xff01 xff01 xff01 xff01 第一种原因 xff1a onCreateView中没有做view持久化 viewpage切换的时候Fragment的生命周期会重新走一遍 xff0c 从
  • DataGrip 2023.1 最新变化 【附带ChatGPT教程】

    ChatGPT开源公众号 xff1a https gitee com wy521a astar weixin mp在浏览器中打开最新变化 DataGrip 2023 1 最新变化 处理代码 DBE 4469 xff1a 我们修正了 SQL
  • awk练习题(一)

    文件中包含名字 xff0c 电话号码和过去三个月里的捐款 用 分隔 具体内容如下 xff1a Mike Harrington 510 548 1278 250 100 175 Christian Dobbins 408 538 2358 1
  • UML类图-基本图示法

    类图 xff1a 分如下三层 类的名称 xff08 如果是抽象类则斜体显示 xff09 类的特性 xff08 字段或属性 xff1a 注意前面的符号 xff1a 43 表示public xff0c 表示private xff0c 表示pro
  • 《Design Patterns》Proxy.积跬步系列

    Proxy xff1a 代理模式 先代码 共用接口 xff1a span class token keyword package span h span class token punctuation span l span class t
  • 《Design Patterns》FactoryMethod.积跬步系列

    FactoryMethod xff1a 工厂方法模式 先代码 父类或接口类 xff1a span class token keyword package span h span class token punctuation span ld
  • Java基础总结二

    Java关键字 xff08 特点及关键字作用 xff09 xff1a xff08 1 xff09 被Java语言赋予特殊含义的单词 xff08 53个含两个保留字 xff09 xff08 2 xff09 关键字都是小写 xff08 3 xf

随机推荐

  • SVN快速使用入门

    协同开发时 xff0c 我们时常会听说SVN这个词 xff0c 那么SVN到底是什么 xff1f 又是怎么玩的 xff1f 笔者在初探SVN后进行一个简单的总结 SVN xff1a Subversion的简称 xff0c 是一个开放源代码的
  • 解决Red Hat6.0以上使用yum命报错Loaded plugins: product-id, refresh-packagekit, security, subscription-manager

    什么是yum xff1a Yum xff08 全称为 Yellow dog Updater Modified xff09 是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器 基于RPM包管理 xff0c 能够从
  • Cause: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found

    org apache ibatis exceptions PersistenceException Error updating database Cause org apache ibatis executor ExecutorExcep
  • [Linux] 记录一次批量开关机操作

    最近公司需要停一部分机器测试下业务稳定性 关停很简单 ansible 就可以了 xff0c 一句命令 ansible guanji m command a 34 shutdown h now 34 定义的关机组 guanji 过了两周 xf
  • 生活篇

    春天 xff0c 一个诗情画意的季节 xff0c 一个生机勃勃的季节 春天 xff0c 为我们带来了温暖 xff0c 为大地带来了生机 在这个春意盎然的季节 xff0c 大自然也开始了她们的春天交响曲 我喜欢三月 xff0c 我也讨厌三月
  • WebStorm 2023.1 最新变化【附带ChatGPT教程】

    ChatGPT开源公众号 xff1a https gitee com wy521a astar weixin mp 观看更新概览视频 在浏览器中打开更新变化 WebStorm 2023 1 最新变化 框架和技术 Astro 支持 备受期待的
  • Ubuntu18.04解决gnome-tweak-tool安装后shell主题提示user-theme extension没有启用的问题

    换用Ubuntu18之后 xff0c 桌面又回到了gnome xff0c 因此找到了gnome tweak tool对桌面进行美化 1 安装gnome tweak tool sudo apt get install gnome tweak
  • 多生产者和多消费者问题

    题目描述 桌子上有一个盘子 xff0c 每次只能向其中放入一个水果 爸爸专门向盘子里面放苹果 xff0c 妈妈专门向盘子里面放橘子 xff1b 只有盘子为空时 xff0c 爸爸或妈妈才可以向其中放入水果 xff1b 仅当盘子里有自己需要的水
  • ssh远程连接服务器常用命令

    命令行下 xff0c 使用ssh 远程登录服务器 ssh 39 用户名 39 64 39 IP地址 39 不用加 39 号 xff0c 这里是为了作区分 39 用户名 39 64 39 IP地址 39 39 s password xxx 项
  • 记录在安卓webview上,gif,apng,pixi.js,lottie-web动画导致闪屏问题

    随着公司项目对动画要求越来越高 xff0c 从由美术提供简单的gif 或者css js开发简单动画变成了使用渲染引擎pixi js使用序列帧动画 xff0c 或者使用龙骨 xff0c spine等更加复杂炫酷的动画 但是发现屏幕在播放动画的
  • 黑苹果_万能OpenCore_0.8.4_EFI

    对于喜欢折腾黑苹果的人来说 xff0c 安装Mac系统就是家常便饭 xff0c 其中最重要的就是EFI的配置 xff0c 配置出一个适合自己电脑的EFI很重要 xff0c 今天发布这篇文章就是为了提供给新加入的伙伴们 xff0c 让更多的伙
  • 黑苹果_OpenCore_0.8.4各项功能精解

    黑苹果已经延续有些年了 xff0c 引导也更新换代过好几次 xff0c 安装黑苹果的第一个条件就是需要拥有一个支持引导苹果系统的EFI xff0c 否则 xff0c 连苹果皮都看不到 xff0c 虽然网上可以直接下载EFI xff0c 但是
  • Github域名解析连接慢问题

    Github域名解析连接慢问题 1 Github访问慢问题2 Github连接解决方案2 1 使用 Gitee 的镜像仓库2 2 配置本地的 hosts 文件 3 DNS域名解析分析3 1 根域名服务器3 1 顶级域名服务器3 1 域名解析
  • C++判断素数(求素数)

    素数又称质数 所谓素数是指除了 1 和它本身以外 xff0c 不能被任何整数整除的数 xff0c 例如17就是素数 xff0c 因为它不能被 2 16 的任一整数整除 思路1 xff1a 因此判断一个整数m是否是素数 xff0c 只需把 m
  • Directx工具修复工具,专注修复C++动态链接DLL文件

    问题 xff1a 方法一 xff1a 可以直接去360管家中搜索DirectX xff0c 然后下载 xff0c 进行修复 方法二 xff1a 如下 xff1a DirectX修复工具最新版 xff1a DirectX Repair V3
  • shell脚本实现在任意虚拟机上 一键重启/关闭 多台虚拟机

    shell脚本实现在任意虚拟机上 一键重启 关闭 多台虚拟机 span class token operator span span class token operator span bin span class token operat
  • ChatGPT体验地址,超多功能,附公众号源码

    GPT 说明效果演示地址体验公众号源码 说明 ChatGPT是一种基于深度学习的自然语言处理 xff08 NLP xff09 技术 xff0c 它可以实现自然的文字对话 ChatGPT是基于预训练的语言模型 xff0c 使用大量的数据和计算
  • Android Native内存泄漏案例

    文章目录 背景现状malloc debugLeakTracer综合评估功能性能稳定性治理实践 案例 使用Raphael 定位内存泄漏 项目中遇到一个内存泄漏的情形 xff1a usb camera预览时出现了内存泄漏 xff0c 但内存泄漏
  • 【The 2017 BAPC】C题-Collatz Conjecture ---- GCD+优化去重

    题意 给你一个大小为n的序列 xff0c 让你求里面所有子串的GCD xff0c 求里面最多有多少不同的GCD 思路 xff1a 利用集合set tmp维护 到当前子串的最后一个元素的所有GCD xff0c set ans保存所有不同种类的
  • WGS84与大地2000坐标转换(Java,C#,Dart)

    一 坐标转换的必要性 平面坐标在道路测绘 隧道测量 农业建筑业等室外勘测等方面有着广泛的应用 各行业基本都会涉及到移动端测量之后不能满足屏幕坐标 所以需要经纬度的转换 移动端勘测结果都是WGS84坐标或者GCJ 02格式坐标 而实际工程项目