这篇博文是在之前理论讲解的基础上,探讨如何用程序来实现。
坐标转换问题的详细了解对于测量很重要,那么请和我一起来讨论这个问题。
首先,我们要弄清楚几种坐标表示方法。大致有三种坐标表示方法:经纬度和高程,空间直角坐标,平面坐标和高程。
我们通常说的WGS-84坐标是经纬度和高程这一种,北京54坐标是平面坐标和高程着一种。
现在,再搞清楚转换的严密性问题,在同一个椭球里的转换都是严密的,而在不同的椭球之间的转换是不严密的。举个例子,在WGS-84坐标和北京54坐标之间是不存在一套转换参数可以全国通用的,在每个地方会不一样,因为它们是两个不同的椭球基准。
那么,两个椭球间的坐标转换应该是怎样的呢?一般而言比较严密的是用七参数法(包括布尔莎模型,一步法模型,海尔曼特等),即X平移,Y平移,Z平移,X旋转,Y旋转,Z旋转,尺度变化K。要求得七参数就需要在一个地区需要3个以上的已知点,如果区域范围不大,最远点间的距离不大于30Km(经验值),这可以用三参数(莫洛登斯基模型),即X平移,Y平移,Z平移,而将X旋转,Y旋转,Z旋转,尺度变化K视为0,所以三参数只是七参数的一种特例。在本软件中提供了计算三参数、七参数的功能。
在一个椭球的不同坐标系中转换可能会用到平面转换,现阶段一般分为四参数和平面网格拟合两种方法,以四参数法在国内用的较多,举个例子,在深圳既有北京54坐标又有深圳坐标,在这两种坐标之间转换就用到四参数,计算四参数需要两个已知点。更精确的可以提供网格拟合数据,本软件提供计算和应用四参数的功能,也提供了网格拟合的功能。
另外,还有高程拟合的问题,大地水准面模型在国内用户中很少会用到,但在国际上已经是标准之一,本软件提供最常用的EGM96模型和Geoid99模型。
最后,本软件提供了ITRF框架转换方法,涉及到ITRF2000和以往用过的ITRF96,ITRF93之间的换算,对于方面的需求的用户是个尝试。
现在举个例子说明:在珠江有一个测区,需要完成WGS-84坐标到珠江坐标系(54椭球)的坐标转换,整个转换过程是
这样的:
其中常用的两种
1.高斯投影的正反算问题\
//高斯投影正、反算
{
NN卯酉圈曲率半径,测量学里面用N表示 M为子午线弧长,测量学里用大X表示 fai为底点纬度,由子午弧长反算公式得到,测量学里用Bf表示 R为底点所对的曲率半径,测量学里用Nf表示 |
方法二:
double PI = 3.14159265358979; X_3 = x / 1000000.00 - 3; // 以兆米(1000000)为单位 if (LP == -1000) *B = (double)(t_B0 * 3600.0); // 将纬差转成秒,并返回指针
double PI = 3.14159265358979; jd_hd = jd / 3600.0 * PI / 180.0; // 将以秒为单位的经度转换成弧度 // 如果不设中央经线(缺省参数: -1000),则计算中央经线, l0 = jd / 3600.0 - L; // 计算经差 } |
来自:http://hi.baidu.com/82085166/blog/item/18e3612a6202159e023bf66e.html
方法三
'高斯反算
Private Sub DadiFs()
Dim t As Double, Itp As Double, X0 As Double, Bf As Double, N As Double
Dim v As Double, ll As Double, W As Double, M As Double, L0 As Double
L0 = Radian(Lo)
X0 = x * 0.000001
y = y - 500000#
If Tq = 0 Then
a = 6378245 '54椭球参数
b = 6356863.01877305
ep = 0.006693421622966
ep1 = 0.006738525414683
f = (a - b) / a
c = a ^ 2 / b
d = b ^ 2 / a
If X0 < 3 Then
Bf = 9.04353301294 * X0 - 0.00000049604 * X0 ^ 2 - 0.00075310733 * X0 ^ 3 - 0.00000084307 * X0 ^ 4 - 0.00000426055 * X0 ^ 5 - 0.00000010148 * X0 ^ 6
ElseIf X0 < 6 Then
Bf = 27.11115372595 + 9.02468257083 * (X0 - 3) - 0.00579740442 * (X0 - 3) ^ 2 - 0.00043532572 * (X0 - 3) ^ 3 + 0.00004857285 * (X0 - 3) ^ 4 + 0.00000215727 * (X0 - 3) ^ 5 - 0.00000019399 * (X0 - 3) ^ 6
End If
Else
a = 6378140 '75椭球参数
b = 6356755.28815753
ep = 0.006694384999588
ep1 = 0.006739501819473
f = (a - b) / a
c = a ^ 2 / b
d = b ^ 2 / a
If X0 < 3 Then
Bf = 9.04369066313 * X0 - 0.00000049618 * X0 ^ 2 - 0.00075325505 * X0 ^ 3 - 0.0000008433 * X0 ^ 4 - 0.00000426157 * X0 ^ 5 - 0.0000001015 * X0 ^ 6
ElseIf X0 < 6 Then
Bf = 27.11162289465 + 9.02483657729 * (X0 - 3) - 0.00579850656 * (X0 - 3) ^ 2 - 0.00043540029 * (X0 - 3) ^ 3 + 0.00004858357 * (X0 - 3) ^ 4 + 0.00000215769 * (X0 - 3) ^ 5 - 0.00000019404 * (X0 - 3) ^ 6
End If
End If
Bf = Bf * Pi / 180#
t = Tan(Bf)
Itp = ep1 * Cos(Bf) ^ 2
W = Sqr(1 - ep * Sin(Bf) ^ 2)
v = Sqr(1 + ep1 * Cos(Bf) ^ 2)
M = c / v ^ 3
N = a / W
Lat = Bf - 0.5 * v ^ 2 * t * ((y / N) ^ 2 - (5 + 3 * t * t + Itp - 9 * Itp * t * t) * (y / N) ^ 4 / 12 + (61 + 90 * t * t + 45 * t ^ 4) * (y / N) ^ 6 / 360)
ll = ((y / N) - (1 + 2 * t * t + Itp) * (y / N) ^ 3 / 6 + (5 + 28 * t * t + 24 * t ^ 4 + 6 * Itp + 8 * Itp * t * t) * (y / N) ^ 5 / 120) / Cos(Bf)
r = y * t / N - y ^ 3 * t * (1 + t * t - Itp) / (3 * N ^ 3) + y ^ 5 * t * (2 + 5 * t * t + 3 * t ^ 4) / (15 * N ^ 5)
Lat = Degree(Lat)
Lon = Degree(L0 + ll)
r = Degree(r)
End Sub
可以精确到0.00000001秒
上面的Degree为将弧度化为DMS的自定义函数
'弧度化角度
Public Function Degree(a As Double) As Double
Dim Bo As Double
Dim Fs As Double
Dim Im As Integer
Dim Id As Integer
If a < 0 Then a = -a: t = 1
Bo = a
Call DMS(Bo, Id, Im, Fs)
If t = 1 Then Degree = -(Id + Im / 100# + Fs / 10000#) Else Degree = Id + Im / 100# + Fs / 10000#
End Function
Public Sub DMS(a As Double, Id As Integer, Im As Integer, Fs As Double)
Dim Bo As Double
Dim c As Double
c = a
c = 180# / Pi * c
Id = Int(c)
Bo = (c - Id) * 60
Im = Int(Bo)
Fs = (Bo - Im) * 60
End Sub
方法四
164
165public class PointCoordinate
166 {
167 private double lon;
168 private double lat;
169 private double alt;
170 public double x;
171 public double y;
172 private static double minX = double.MaxValue;
173 private static double maxX = double.MinValue;
174 private static double minY = double.MaxValue;
175 private static double maxY = double.MinValue;
176 public static double minlon = double.MaxValue;
177 public static double minlat = double.MaxValue;
178 public static double minalt = double.MaxValue;
179 public static double maxlon = double.MinValue;
180 public static double maxlat = double.MinValue;
181 public static double maxalt = double.MinValue;
182
183 Attribute#region Attribute
184
185 public double Lon
186 {
187 get
188 {
189 return lon;
190 }
191 set
192 {
193 lon = value;
194 }
195 }
196 public double Lat
197 {
198 get
199 {
200 return lat;
201 }
202 set
203 {
204 lat = value;
205 }
206 }
207 public double Alt
208 {
209 get
210 {
211 return alt;
212 }
213 set
214 {
215 alt = value;
216 }
217 }
218
219 #endregion
220
221 ConStructions#region ConStructions
222
223 public PointCoordinate(double lon, double lat, double alt)
224 {
225 init(lon, lat, alt);
226 }
227 public PointCoordinate(string lon, string lat, string alt)
228 {
229 init(lon, lat, alt);
230 }
231 public PointCoordinate(string[] OnePoint)
232 {
233 init(OnePoint);
234 }
235
236 public PointCoordinate(string tuples)
237 {
238 init(tuples);
239 }
240
241 #endregion
242
243 Initilation#region Initilation
244
245 private void init(double lon, double lat, double alt)
246 {
247 this.lon = lon;
248 this.lat = lat;
249 this.alt = alt;
250 //LBXY lbxy = new LBXY(this.lon, this.lat);
251 //this.x = lbxy.x;
252 //this.y = lbxy.y;
253 //CheckRange();
254 CheckEarthRange();
255 }
256 private void init(string lon, string lat, string alt)
257 {
258 init(Convert.ToDouble(lon), Convert.ToDouble(lat), Convert.ToDouble(alt));
259 }
260 private void init(string[] OnePoint)
261 {
262 int CheckLen = OnePoint.Length;
263
264 if (CheckLen == 3)
265 init(OnePoint[0], OnePoint[1], OnePoint[2]);
266 else if(CheckLen==2)
267 init(OnePoint[0], OnePoint[1], "0.0");
268 }
269 private void init(string tuples)
270 {
271 string[] OnePoint = tuples.Split(',');
272 init(OnePoint);
273 }
274
275 #endregion
276
277 private void CheckEarthRange()
278 {
279 if (lon < minlon) minlon = lon;
280 if (lon > maxlon) maxlon = lon;
281 if (lat < minlat) minlat = lat;
282 if (lat > maxlat) maxlat = lat;
283 if (alt < minalt) minalt = alt;
284 if (alt > maxalt) maxalt = alt;
285 }
286 public void CheckRange()
287 {
288 if (x < minX) minX = x;
289 if (x > maxX) maxX = x;
290 if (y < minY) minY = y;
291 if (y > maxY) maxY = y;
292 }
293
294 //public double LonRatio
295 //{
296 // get
297 // {
298 // if((maxlon - minlon)!=0)
299 // return (lon - minlon) / (maxlon - minlon);
300 // else
301 // return 0.0;
302 // }
303 //}
304 //public double LatRatio
305 //{
306 // get
307 // {
308 // if((maxlat - minlat)!=0)
309 // return (lat - minlat) / (maxlat - minlat);
310 // else
311 // return 0.0;
312 // }
313 //}
314 public double xRatio
315 {
316 get
317 {
318 if ((maxX - minX) != 0)
319 return (x - minX) / (maxX - minX);
320 else
321 return 0.0;
322 }
323 }
324 public double yRatio
325 {
326 get
327 {
328 if ((maxY - minY) != 0)
329 return (y - minY) / (maxY - minY);
330 else
331 return 0.0;
332 }
333 }
334 public double lonRatio
335 {
336 get
337 {
338 if ((maxlon - minlon) != 0)
339 return (lon - minlon) / (maxlon - minlon);
340 else
341 return 0.0;
342 }
343 }
344 public double latRatio
345 {
346 get
347 {
348 if ((maxlat - minlat) != 0)
349 return (lat - minlat) / (maxlat - minlat);
350 else
351 return 0.0;
352 }
353 }
354
355 }
356
357 public class LBXY
358 {
359 public double l;//角度
360 public double b;//角度
361 private double L;//弧度
362 private double B;//弧度
363 private double l0;
364 private double L0;
365 public double x;
366 public double y;
367
368 public LBXY(double l, double b)
369 {
370 this.l = l;
371 this.b = b;
372 l0 = ML(l);
373 L0 = Deg2Arc(l0);
374 L = Deg2Arc(l);
375 B = Deg2Arc(b);
376 LB2XY();
377 }
378
379 public LBXY(PointCoordinate pc)
380 {
381 this.l = pc.Lon;
382 this.b = pc.Lat;
383 l0 = ML((PointCoordinate.maxlon + PointCoordinate.minlon) / 2.0);
384 L0 = Deg2Arc(l0);
385 L = Deg2Arc(l);
386 B = Deg2Arc(b);
387 LB2XY();
388 pc.x = this.y;
389 pc.y = this.x;
390 pc.CheckRange();
391 }
392
393 public double Arc2Deg(double arc)
394 {
395 return arc * 180 / Math.PI;
396 }
397 public double Deg2Arc(double deg)
398 {
399 return deg * Math.PI / 180;
400 }
401 public double ML(double lon)
402 {
403 int ZoneID=(int)(lon / 6) + 1;
404 double rev = (double)(ZoneID * 6 - 3);
405 return rev;
406 }
407
408 public void LB2XY()
409 {
410 double e2 = 0.006739501819;
411 double c = 6399596.65198801;//单位:m
412 //double e2 = 0.006738525415;
413 //double c = 6399698.9018;//单位:m
414
415 double t = Math.Tan(B);
416 double eta2 = e2 * Math.Cos(B) * Math.Cos(B);
417 double N = c / Math.Sqrt(1 + eta2);
418 double m = Math.Cos(B)*(L-L0);
419 double X = 111134.0047 * b
420 - (
421 32009.8575 * Math.Sin(B)
422 + 133.9602 * Math.Pow(Math.Sin(B), 3)
423 + 0.6976 * Math.Pow(Math.Sin(B), 5)
424 + 0.0039 * Math.Pow(Math.Sin(B), 7)
425 ) * Math.Cos(B);
426 double q1, q2, q3, q4;
427 q1 = t*t;
428 q2 = Math.Pow(t, 4);
429 q3 = m * m;
430 q4 = Math.Pow(m, 4);
431
432 x=X+N*t*q3*(0.5+(5-q1+9*eta2+4*eta2*eta2)*q3/24+(61-58*q1+q2)*q4/720);//纵轴,经线方向
433 y=N*m*(1+(1-q1+eta2)*q3/6+(5-18*q1+q2+14*eta2-58*eta2*q1)*q4/720);//横轴,赤道方向
434 }
435 }
436
437}
具体含义可参考:
http://www.surmap.com/e/DoPrint/?classid=51&id=1577
此类大部分用于GPS测量之中.
第二个涉及到的就是七参转换
http://www.cnblogs.com/holygis/archive/2011/12/30/2307516.html
这个参数不同是因为北京54坐标系为参心大地坐标系,大地上的一点可用经度L54、纬度M54和大地高H54定位,它是以克拉索夫斯基椭球为基础,经局部平差后产生的坐标系.它采用了前苏联的克拉索夫斯基椭球参数,并与前苏联1942年坐标系进行联测,通过计算建立了我国大地坐标系,定名为1954年北京坐标系。因此,1954年北京坐标系可以认为是前苏联1942年坐标系的延伸。它的原点不在北京而是在前苏联的普尔科沃。东经30°19′15〃,北纬59°46′6〃。
而80坐标系是1980年国家大地坐标系采用地球椭球基本参数为1975年国际大地测量与地球物理联合会第十六届大会推荐的数据。该坐标系的大地原点设在我国中部的陕西省泾阳县永乐镇,位于西安市西北方向约60公里,故称1980年西安坐标系,又简称西安大地原点。基准面采用青岛大港验潮站1952-1979年确定的黄海平均海水面(即1985国家高程基准)。
西安80坐标系与北京54坐标系其实是一种椭球参数的转换作为这种转换在同一个椭球里的转换都是严密的,而在不同的椭球之间的转换是不严密,因此不存在一套转换参数可以全国通用的,在每个地方会不一样,因为它们是两个不同的椭球基准