先上图
下载链接:程序下载
正轴等角割圆锥投影:由德国数学家Lambert拟定,属于兰勃特正形投影(Lambert projection)之一。
纬线为同心圆弧,经线为放射性直线;相割的两条纬线是标准纬线,其长度比为1;无角度变形;在两条割线之内,纬线长度比小于1,之外长度比大于1;离开标纬越远,变形绝对值越大
正轴等角切圆柱投影,由荷兰制图学家Mercator (Mercator Gerardus,1512-1594)于1569年创建,又被称为墨卡托投影。
经线与纬线是两组相互垂直的平行直线,经线间距相等,纬线间距由赤道向两极逐渐增大。
赤道为标准纬线,其余各纬线与赤道等长;无角度变形,但长度和面积变形随着纬度增高而逐渐增大;等角航线表现为直线
80坐标系(Xian_1980)
长半轴:6378140 偏心率: 1/298.257
短半轴:6356755.28816
极曲率半径:6399596.65199
第一偏心率平方: 0.006694384999588
第二偏心率平方: 0.006739501819473
54坐标系(Krasovsky_1940)
长半轴:6378245 偏心率: 1/298.3
短半轴:6356863.01877
极曲率半径:6399698.90178
第一偏心率平方: 0.006693421622966
第二偏心率平方: 0.006738525414684
兰勃特正角投影公式
界面基本的就是 button、 textbox控件
图可以绘制在 Picturebox 控件,也可以直接画在form窗体里
可以加入splitContainer控件和groupbox控件
思路
1、读入数据
CHINA_Arc.gen.txt的数据,将经纬度读入一个数组,另外定义一个数组专门记录下标
2、投影变换
将经纬度带入带入变换的成员函数进行投影变换
3、画图
一共有四百多个环,用for循环依此绘制每个环,如33号环内12个点顺序画线,注意数组的下标错位现象,不然会导致33号环的最后一个点与34号环的第一个点连线出错。
老师说经常用For循环会变傻,就这么吧我也想不出更好的方法
源码
1、输入参数定义数组
//54坐标系参数
double a = 6378245;
double b = 6356863.01877;
double e1 = Math.Sqrt(0.0066943849995888);
double e2 = Math.Sqrt(0.006739501819473);
static string path;
int[] H = new int[465];
static double[] B = new double[26689];
static double[] L = new double[26689];
double[] BLambert = new double[B.Length];//存储转换后的B坐标
double[] LLambert = new double[L.Length];//存储转换后的L坐标
double[] BMercator = new double[B.Length];//存储转换后的B坐标
double[] LMercator = new double[L.Length];//存储转换后的L坐标
2、路径及数据读入
可以单独写一个事件来选择文件的路径,OpenFileDialog类提供多种文件的操作,可返回路径,文件名、文件后缀等
OpenFileDialog fdlg = new OpenFileDialog();
fdlg.Title = "C# Corner Open FileDialog";
fdlg.InitialDirectory = "";
fdlg.Filter = "All files (*.*)|*.*|All files(*.*)|*.*";
fdlg.FilterIndex = 2;
fdlg.RestoreDirectory = true;
if(fdlg .ShowDialog ()==DialogResult.OK )
{
path = Path.GetFullPath(fdlg.FileName);//获取绝对路径
}
MessageBox.Show("文件选择完成!");
也可以写死路径,两种写路径方法,\ 第一个反斜号是转义字符的意思
path=@"D:\CHINA_Arc.gen.txt";
path="D:\\CHINA_Arc.gen.txt";
数据读入成员函数(注意引用类)
public void inputdata()
{
StreamReader str = new StreamReader(path);
string x;
int i = 0;
int j = 0;
int k = 1;
int T;
while ((x = str.ReadLine()) != null)//把数据与编号分开存,H[]数组存行号
{
if (x.Length <= 3 && !x.Contains("END"))
T = 1;
else if (x.Contains("END"))
T = 2;
else T = 3;
switch (T)
{
case 1:
H[i] = k;
k++;
i++;
continue;
case 2:
k++;
continue;
case 3:
string[] Q = x.Split(',');
double u;
u = Convert.ToDouble(Q[0]);
B[j] = u ;
u = Convert.ToDouble(Q[1]);
L[j] = u ;
j++; k++;
continue;
}
}
for (int rr = 0; rr <= 462; rr++)
{
H[rr] = H[rr] - rr;
H[462] = 27152;
}
str.Close();
Console.ReadLine();
}
3、投影成员函数及事件
button事件
private void BtnLambert_Click(object sender, EventArgs e)
{
DateTime t0 = DateTime.Now;
Graphics g = this.picBox.CreateGraphics();
g.Clear(this.BackColor);
g.Dispose();
if (TxtB0.Text != "" && TxtL0.Text != "" && TxtB1.Text != "" && TxtB2.Text != "")
{
double B0 = Convert.ToDouble(TxtB0.Text) * Math.PI / 180;
double L0 = Convert.ToDouble(TxtL0.Text) * Math.PI / 180;
double B1 = Convert.ToDouble(TxtB1.Text) * Math.PI / 180;
double B2 = Convert.ToDouble(TxtB2.Text) * Math.PI / 180;
inputdata();
Lambert(B0, B1, B2, e1, L0);
drawchina(H, BLambert, LLambert);
}
else
MessageBox.Show("请输入数据");
DateTime t1 = DateTime.Now;
TxtTime.Text = (t1 - t0).ToString();
}
private void btnMocato_Click(object sender, EventArgs e)
{
DateTime t0 = DateTime.Now;
Graphics g = this.picBox.CreateGraphics();
g.Clear(this.BackColor);
g.Dispose();
if (txtB00.Text != "" && txtL00.Text != "" && txtB11.Text != "" )
{
double B0 = Convert.ToDouble(txtB00.Text) * Math.PI / 180;
double L0 = Convert.ToDouble(txtL00.Text) * Math.PI / 180;
double B1 = Convert.ToDouble(txtB11.Text) * Math.PI / 180;
inputdata();
Mercator(B0, B1, e1, e2, L0);
drawchina(H, BMercator, LMercator);
}
else
MessageBox.Show("请输入数据");
DateTime t1 = DateTime.Now;
TxtTime.Text = (t1 - t0).ToString();
}
成员函数
public void Lambert(double B0,double B1,double B2,double e1,double L0)
{
double[] m = new double[B.Length ];
double[] t = new double[B.Length];
double[] r = new double[B.Length];
double[] q = new double[B.Length];
double[] c = new double[B.Length];
double m0 = Math.Cos(B0) / Math.Sqrt(1 - e1 * e1 * Math.Sin(B0) * Math.Sin(B0));//m0常数
double m1 = Math.Cos(B1) / Math.Sqrt(1 - e1 * e1 * Math.Sin(B1) * Math.Sin(B1));//mB1常数
double m2 = Math.Cos(B2) / Math.Sqrt(1 - e1 * e1 * Math.Sin(B2) * Math.Sin(B2));//mB2常数
double t0 = Math.Tan(Math.PI / 4 - B0 / 2) / Math.Pow((1 - e1 * Math.Sin(B0) / (1 + e1 * Math.Sin(B0))), (e1 / 2));//t0常数
double t1 = Math.Tan(Math.PI / 4 - B1 / 2) / Math.Pow((1 - e1 * Math.Sin(B1) / (1 + e1 * Math.Sin(B1))), (e1 / 2));//tB1常数
double t2 = Math.Tan(Math.PI / 4 - B2 / 2) / Math.Pow((1 - e1 * Math.Sin(B2) / (1 + e1 * Math.Sin(B2))), (e1 / 2));//tB2常数
double n = Math.Log10(m1 / m2) / Math.Log10(t1 / t2);
double F = m1 / (n * Math.Pow(t1, n));
double r0 = a * F * Math.Pow(t0, n);
for (int i = 0; i < B.Length; i++)
{
c[i] = B[i];
B[i] = L[i] * Math.PI / 180;
L[i] = c[i] * Math.PI / 180;
m[i] = Math.Cos(B[i]) / (Math.Sqrt(1 - e1 * e1 * Math.Sin(B[i]) * Math.Sin(B[i])));
t[i] = Math.Tan(Math.PI / 4 - B[i] / 2) / Math.Pow((1 - e1 * Math.Sin(B[i]) / (1 + e1 * Math.Sin(B[i]))), (e1 / 2));
r[i] = a * F * Math.Pow(t[i], n);
q[i] = n * (L[i] - L0);
BLambert[i] = (r0 - r[i] * Math.Cos(q[i]));
LLambert[i] = (r[i] * Math.Sin(q[i]));
}
}
public void Mercator(double B0, double B1, double e1, double e2, double L0)
{
double K = Math .Pow (a,2) / b / Math.Sqrt(1 + Math.Pow(e2, 2) * Math.Pow(Math.Cos(B0), 2))
* Math.Cos(B0);
double[] exchange = new double[B.Length];
double[] m = new double[B.Length];
double[] n = new double[B.Length];
for (int i = 0; i < B.Length; i++)
{
exchange [i] = B[i];
B[i] = L[i] * Math.PI / 180;
L[i] = exchange [i] * Math.PI / 180;
m[i] = Math.Tan(Math.PI / 4 + B[i] / 2);
n[i] = (1 - e1 * Math.Sin(B[i])) / (1 + e1 * Math.Sin(B[i]));
BMercator[i] = K*Math.Log(m[i] + Math.Pow(n[i], (e1 / 2)))-2800000;
LMercator [i] = K*(L[i]-L0)-50000;
}
}
画图成员函数
public void drawchina(int []H,double [] BLambert,double [] LLambert)
{
for (int i = 1; i <= 462; i++)
H[i] = H[i] - i - 2;
int j = 0; //定义两个循环体变量,一共462个环,循环462次,每次画一个环
for (int k = 1; k < 462; k++)
{
for (; j < H[k]; j++)
{
if (j == H[k - 1])
{
j++;
}
int x1 = (int)(BLambert[j]);
int y1 = (int)(LLambert[j]);
int x2 = (int)(BLambert[j + 1]);
int y2 = (int)(LLambert[j + 1]);
drawline(x1, y1, x2, y2);
}
}
}
画线函数
用的是随机颜色,Random类和 timer控件很好用
投影坐标系和笛卡尔坐标系是反着的(学测绘的都知道的吧)
投影后的坐标都是几千上万左右的,需要按比例缩放和平移才能显示到屏幕上
public void drawline(int x1, int y1, int x2, int y2)
{
Random rnd = new Random();
Graphics g = this.picBox.CreateGraphics();
Pen map = new Pen(Color.FromArgb (rnd.Next (256), rnd.Next (50), rnd.Next (50)), 2f);//定义画刷,黑色,线条宽度为1,f表示float浮点数
g.ScaleTransform(0.00009f, 0.00009f);//画面缩小
g.TranslateTransform(4000000, 6400000);//平移
g.RotateTransform(-90);//旋转
g.DrawLine(map, x1, y1, x2, y2);//每次两个点连线
}
重写窗口大小
private void ONresize(object sender, EventArgs e)//重写窗口大小事件
{
float newx = (this.Width) / x;
float newy = (this.Height) / y;
setControls(newx, newy, this);
}
private void setTag(Control cons)
{
foreach (Control con in cons.Controls)
{
con.Tag = con.Width + ";" + con.Height + ";" + con.Left + ";" + con.Top + ";" + con.Font.Size;
if (con.Controls.Count > 0)
{
setTag(con);
}
}
}
private void setControls(float newx, float newy, Control cons)
{
//遍历窗体中的控件,重新设置控件的值
foreach (Control con in cons.Controls)
{
//获取控件的Tag属性值,并分割后存储字符串数组
if (con.Tag != null)
{
string[] mytag = con.Tag.ToString().Split(new char[] {
';' });
//根据窗体缩放的比例确定控件的值
con.Width = Convert.ToInt32(System.Convert.ToSingle(mytag[0]) * newx);//宽度
con.Height = Convert.ToInt32(System.Convert.ToSingle(mytag[1]) * newy);//高度
con.Left = Convert.ToInt32(System.Convert.ToSingle(mytag[2]) * newx);//左边距
con.Top = Convert.ToInt32(System.Convert.ToSingle(mytag[3]) * newy);//顶边距
Single currentSize = System.Convert.ToSingle(mytag[4]) * newy;//字体大小
con.Font = new Font(con.Font.Name, currentSize, con.Font.Style, con.Font.Unit);
if (con.Controls.Count > 0)
{
setControls(newx, newy, con);
}
}
}
}