成果展示
主窗口如下:
验证结果如下(以WGS84坐标纬度为30.4691868227°,经度为114.3510760836°,选择3度分带为例):
详细过程待补充
代码如下:
gs.java:
//类的定义及成员变量
public class gs {
private double PI=3.1415926;
private double a, b, e1, e2, A1, B1, C1, D1, E1, F1, G1;//用于存储椭球的参数和中间计算值
// 构造函数初始化椭球参数
public gs(double a1, double b1) {
this.a = a1;
this.b = b1;
this.e1 = Math.sqrt((a1 * a1 - b1 * b1) / (a1 * a1));//计算椭球的第一偏心率
this.e2 = Math.sqrt((a1 * a1 - b1 * b1) / (b1 * b1));//计算椭球的第二偏心率
//计算各参数 A1, B1, C1, D1, E1, F1, G1
this.A1 = 1.0 +3.0/4.0*Math.pow(e1,2)+45.0/64.0*Math.pow(e1,4)+175.0/256.0*Math.pow(e1,6)+
11025.0/16384.0*Math.pow(e1,8)+43659.0/65536.0*Math.pow(e1,10)+693693.0/1048576.0*Math.pow(e1,12);
this.B1 = 3.0/8.0*Math.pow(e1,2)+15.0/32.0*Math.pow(e1,4)+525.0/1024.0*Math.pow(e1,6)+2205.0/4096.0*Math.pow(e1,8)
+72765.0/131072.0*Math.pow(e1,10)+297297.0/524288.0*Math.pow(e1,12);
this.C1 = 15.0/256.0*Math.pow(e1,4)+105.0/1024.0*Math.pow(e1,6)+2205.0/16384.0*Math.pow(e1,8)
+10396.0/65536.0*Math.pow(e1,10)+1486485.0/8388608.0*Math.pow(e1,12);
this.D1 = 35.0/3072.0*Math.pow(e1,6)+105.0/4096.0*Math.pow(e1,8)+10395.0/262144.0*Math.pow(e1,10)
+55055.0/1048576.0*Math.pow(e1,12);
this.E1 = 315.0/131072.0*Math.pow(e1,8)+3465.0/524288.0*Math.pow(e1,10)+99099.0/8388608.0*Math.pow(e1,12);
this.F1 = 693.0/1310720.0*Math.pow(e1,10)+9909.0/5242880.0*Math.pow(e1,12);
this.G1 = 1001.0/8388608.0*Math.pow(e1,12);
}
// 计算卯酉圈曲率半径,输入参数 B 是纬度(以弧度为单位)
private double CalculateN(double B) {
return a/Math.sqrt(1.0 - e1 * e1 * Math.sin(B) * Math.sin(B));
}
// 计算中央子午线弧长,输入参数 B 是纬度(以弧度为单位)
private double CalculateX(double B) {
return a*(1.0-e1*e1)*(A1*B-B1*Math.sin(2*B)+C1*Math.sin(4*B)-D1*Math.sin(6*B)+E1*Math.sin(8*B)-
F1*Math.sin(10*B)+G1*Math.sin(12*B));
}
// 3度分带转换x值求解
public double gsklv3gtransformX(double Lon, double Lat) {
double L0 = 3.0 * (Math.floor((Lon-1.5) / 3.0) + 1.0); //3度分带中央经线
double l = (Lon - L0) * PI / 180.0; //当地经度减去中央子午线经度 (转换为弧度)
double B = Lat * PI / 180.0; //纬度(转换为弧度)
double N = CalculateN(B); //计算曲率半径
double X = CalculateX(B); //计算弧长
double t = Math.tan(B);
double η2 = this.e2 * this.e2 * Math.cos(B) * Math.cos(B);
return X +N*t*Math.cos(B)*Math.cos(B)*l*l*(0.5+(1.0/24.0)*(5.0-t*t+9.0*η2+4.0*Math.pow(η2,2))*
Math.pow(l*Math.cos(B),2)+1.0/720.0*(61.0-58.0*t*t+Math.pow(t,4))*Math.pow(l*Math.cos(B),4));
}
// 3度分带转换y值求解
public double gsklv3gtransformY(double Lon, double Lat) {
double L0 = 3.0 *( Math.floor((Lon-1.5) / 3.0) + 1.0); //3度分带中央经线
double l = (Lon - L0) * PI / 180.0; //当地经度减去中央子午线经度 (转换为弧度)
double B = Lat * PI / 180.0; //纬度(转换为弧度)
double N = CalculateN(B); //计算曲率半径
double t = Math.tan(B);
double η2 = this.e2 * this.e2 * Math.cos(B) * Math.cos(B);
return 500000.0 + N * Math.cos(B)*l*(1.0+(1.0/6.0)*(1.0-t*t+η2)*Math.cos(B)*Math.cos(B)*l*l+(1.0/120.0)*
(5.0-18.0*(t*t)+Math.pow(t,4)+14.0*(η2)-58.0*(t*t*η2))*Math.pow(Math.cos(B),4)*Math.pow(l,4));
}
// 6度分带转换x值求解
public double gsklv6gtransformX(double Lon, double Lat) {
double L0 = 6.0 * (Math.floor(Lon / 6.0) + 1.0)-3.0; //6度分带中央经线
double l = (Lon - L0) * PI / 180.0; //当地经度减去中央子午线经度 (转换为弧度)
double B = Lat * PI / 180.0; //纬度(转换为弧度)
double N = CalculateN(B); //计算曲率半径
double X = CalculateX(B); //计算弧长
double t = Math.tan(B);
double η2 = this.e2*this.e2 * Math.cos(B) * Math.cos(B);
return X + N*t*Math.cos(B)*Math.cos(B)*l*l*(0.5+(1.0/24.0)*(5.0-t*t+9.0*η2+4.0*Math.pow(η2,2))*
Math.pow(l*Math.cos(B),2)+1.0/720.0*(61.0-58.0*t*t+Math.pow(t,4))*Math.pow(l*Math.cos(B),4));
}
// 6度分带转换y值求解
public double gsklv6gtransformY(double Lon, double Lat) {
double L0 = 6.0 * (Math.floor(Lon / 6.0) + 1.0)-3.0; //6度分带中央经线
double l = (Lon - L0) * PI / 180.0; //当地经度减去中央子午线经度 (转换为弧度)
double B = Lat * PI / 180.0; //纬度(转换为弧度)
double N = CalculateN(B); //计算曲率半径
double η2 = this.e2* this.e2 * Math.cos(B) * Math.cos(B);
double t = Math.tan(B);
return 500000.0 + N * Math.cos(B)*l*(1.0+(1.0/6.0)*(1.0-t*t+η2)*Math.cos(B)*Math.cos(B)*l*l+(1.0/120.0)*
(5.0-18.0*(t*t)+Math.pow(t,4)+14.0*(η2)-58.0*(t*t*η2))*Math.pow(Math.cos(B),4)*Math.pow(l,4));
}
}
gsklMain.java:
import javax.swing.JFrame;
public class gsklMain {
public static void main(String args[]) {
JFrame frame = new JFrame("高斯克吕格转换");
frame.setLocation(600,300); //设置窗口在屏幕上的位置
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置窗口的默认关闭操作,当用户点击关闭按钮时,程序将退出
frame.getContentPane().add(new gsklWindow()); //获取窗口的内容面板,并添加一个 gsklWindow 实例
frame.pack(); //调整窗口大小,以适应其内容的大小和布局
frame.setVisible(true); //设置窗口为可见
}
}
gsklWindow.java:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class gsklWindow extends JPanel {
//声明一组私有成员变量,用于创建不同的图形组件,如单选按钮、文本字段和按钮
private JRadioButton radioWGS84, radioBeijing54, radioXian80;
private JTextField textLon, textLat, textX, textY;
private JButton buttonTransform;
private JRadioButton radio3Degree, radio6Degree;
public gsklWindow() { //构造函数
setLayout(new GridLayout(8, 2, 5, 5)); //设置面板的布局为 8 行 2 列的网格布局,每个单元格之间有 5 像素的间距
//创建三个单选按钮,用于选择坐标类型
radioWGS84 = new JRadioButton("WGS84");
radioBeijing54 = new JRadioButton("北京54");
radioXian80 = new JRadioButton("西安80");
//创建一个按钮组 bgCoordinate,并将这三个单选按钮添加到按钮组中,以确保只能选择其中一个
ButtonGroup bgCoordinate = new ButtonGroup();
bgCoordinate.add(radioWGS84);
bgCoordinate.add(radioBeijing54);
bgCoordinate.add(radioXian80);
//添加标签和面板来显示和选择坐标类型
add(new JLabel("选择坐标类型:"));
//将单选按钮添加到 coordinatePanel 中,并将该面板添加到主面板中
JPanel coordinatePanel = new JPanel(new GridLayout(1, 3));
coordinatePanel.add(radioWGS84);
coordinatePanel.add(radioBeijing54);
coordinatePanel.add(radioXian80);
add(coordinatePanel);
//添加标签和文本字段,用于输入经度和纬度
add(new JLabel("输入经度:"));
textLon = new JTextField();
add(textLon);
add(new JLabel("输入纬度:"));
textLat = new JTextField();
add(textLat);
//创建两个单选按钮,用于选择分带类型
radio3Degree = new JRadioButton("3度分带");
radio6Degree = new JRadioButton("6度分带");
//创建一个按钮组 bgDegree,并将这两个单选按钮添加到按钮组中
ButtonGroup bgDegree = new ButtonGroup();
bgDegree.add(radio3Degree);
bgDegree.add(radio6Degree);
//添加标签和面板来显示和选择分带类型
add(new JLabel("选择分带类型:"));
JPanel degreePanel = new JPanel(new GridLayout(1, 2));
//将单选按钮添加到 degreePanel 中,并将该面板添加到主面板中
degreePanel.add(radio3Degree);
degreePanel.add(radio6Degree);
add(degreePanel);
buttonTransform = new JButton("转换");
//创建一个按钮 buttonTransform,用于执行坐标转换
add(buttonTransform);
//添加一个空标签作为占位符,以确保排版合理
add(new JLabel());
//添加标签和文本字段,用于显示转换后的 X 坐标和 Y 坐标
add(new JLabel("X坐标:"));
textX = new JTextField();
//将文本字段设置为不可编辑
textX.setEditable(false);
add(textX);
add(new JLabel("Y坐标:"));
textY = new JTextField();
textY.setEditable(false);
add(textY);
//为转换按钮添加事件监听器,当按钮被点击时,将调用 Controller 类的 actionPerformed 方法
buttonTransform.addActionListener(new Controller());
}
private class Controller implements ActionListener {
public void actionPerformed(ActionEvent e) {
try {
double lon = Double.parseDouble(textLon.getText()); //从文本字段中读取并解析经度和纬度
double lat = Double.parseDouble(textLat.getText());
gs converter;
//根据选择的坐标类型,创建相应的 gs 类实例(转换器)
if (radioWGS84.isSelected()) {
converter = new gs(6378137.0, 6356752.3142);
} else if (radioBeijing54.isSelected()) {
converter = new gs(6378245.0, 6356863.0188);
} else if (radioXian80.isSelected()) {
converter = new gs(6378140.0, 6356755.2882);
} else {
JOptionPane.showMessageDialog(null, "请选择一种坐标类型");//如果没有选择任何坐标类型,显示错误消息并返回
return;
}
//根据选择的分带类型,使用转换器将经纬度转换为 X 坐标和 Y 坐标
double x, y;
if (radio3Degree.isSelected()) {
x = converter.gsklv3gtransformX(lon, lat);
y = converter.gsklv3gtransformY(lon, lat);
} else if (radio6Degree.isSelected()) {
x = converter.gsklv6gtransformX(lon, lat);
y = converter.gsklv6gtransformY(lon, lat);
} else {
JOptionPane.showMessageDialog(null, "请选择一种分带类型");//如果没有选择任何分带类型,显示错误消息并返回
return;
}
//将转换结果格式化为字符串并显示在相应的文本字段中,保留11位小数
textX.setText(String.format("%.11f", x));
textY.setText(String.format("%.11f", y));
} catch (NumberFormatException ex) { //捕获 NumberFormatException 异常,如果输入的经纬度值无效,显示错误消息
JOptionPane.showMessageDialog(null, "请输入有效的经纬度值");
}
}
}
}
GitHub仓库
GitHub仓库:TshiauHuan/GaussKrueger_Transform: XMUT Java课设 高斯克吕格投影正解转换