Gauss.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. var GaussUtil = (function () {
  2. // 保存 Math 函数名和属性
  3. var pow = Math.pow,
  4. sqrt = Math.sqrt,
  5. sin = Math.sin,
  6. cos = Math.cos,
  7. tan = Math.tan,
  8. abs = Math.abs,
  9. floor = Math.floor,
  10. ceil = Math.ceil,
  11. pi = Math.PI;
  12. // 国家2000坐标系椭球
  13. var a = 6378137,
  14. f = 298.257222101,
  15. //b = 6356752.3142,
  16. b = a-a/f;
  17. //f = a / (a - b),
  18. // 第一偏心率
  19. e = (2 * f - 1) / pow(f, 2),
  20. e2 = pow(e, 2),
  21. e3 = pow(e, 3),
  22. e4 = pow(e, 4),
  23. e5 = pow(e, 5),
  24. // 第二偏心率
  25. e_2 = e / (1 - e);
  26. // 计算子午线弧长公式的系数
  27. var cA = 1 + 3 * e / 4 + 45 * e2 / 64 + 175 * e3 / 256 + 11025 * e4 / 16384,
  28. cB = 3 * e / 4 + 15 * e2 / 16 + 525 * e3 / 512 + 2205 * e4 / 2048,
  29. cC = 15 * e2 / 64 + 105 * e3 / 256 + 2205 * e4 / 4096,
  30. cD = 35 * e3 / 512 + 315 * e4 / 2048,
  31. cE = 315 * e4 / 16384;
  32. // 和带号处理相关的参数
  33. var zoneMask = 1000000;
  34. var offsetValue = 500000;
  35. // 根据高斯平面坐标计算经纬度坐标
  36. // 注意:这里的 x, y 坐标是测量坐标系下的纵轴和横轴坐标,与
  37. // 通常意义上的平面坐标有区别。
  38. // @x 纵轴坐标,如 3215242.42
  39. // @y 横轴坐标,如 38540123.52
  40. // @return <object>: { lat: <number>, lon: <number> }
  41. function calcLatLonFromGauss(x, y,zoneType) {
  42. var zoneNum = floor(y / zoneMask);
  43. var L0 = calcCentralMeridian(zoneNum,zoneType);
  44. if (!L0) {
  45. return null;
  46. }
  47. // 整理横轴坐标:去带号,去偏移量
  48. y = y % zoneMask - offsetValue;
  49. // 计算底点 F 的纬度 Bf,这是计算最终结果非常重要的参数
  50. var Bf = calcBf(x, y);
  51. // 计算最终结果时需要的参数,保存会多次使用的值
  52. var cosBf = cos(Bf),
  53. cosBf2 = pow(cosBf, 2),
  54. tanBf = tan(Bf),
  55. tanBf2 = pow(tanBf, 2),
  56. tanBf4 = pow(tanBf, 4),
  57. Nf = a / sqrt(1 - e * pow(sin(Bf), 2)),
  58. y_Nf = y / Nf,
  59. V2 = 1 + e_2 * cosBf2, // tf/Mf
  60. nf2 = e_2 * cosBf2;
  61. // 非常复杂的计算方法,尽量不要改动
  62. // 计算公式可参考:《大地坐标系统及其应用》p188下(8-90)
  63. var B = Bf - V2 * tanBf / 2 * (pow(y_Nf, 2) - (5 + 3 * tanBf2 + nf2 - 9 * nf2 * tanBf2) * pow(y_Nf, 4) / 12 + (61 + 90 * tanBf2 + 45 * tanBf4) * pow(y_Nf, 6) / 360);
  64. var l = (y_Nf - (1 + 2 * tanBf2 + nf2) * pow(y_Nf, 3) / 6 + (5 + 28 * tanBf2 + 24 * tanBf4 + 6 * nf2 + 8 * nf2 * tanBf2) * pow(y_Nf, 5) / 120) / cosBf;
  65. B = B * 180 / pi;
  66. l = l * 180 / pi;
  67. return {
  68. lon: l + L0,
  69. lat: B,
  70. longitude: l + L0,
  71. latitude: B
  72. };
  73. }
  74. function calcGaussFromLatLon(B, L, zoneType)
  75. {
  76. zoneType = zoneType || 6;
  77. var zoneNum = calcZoneNum(L, zoneType);
  78. if (!zoneNum) {
  79. return null;
  80. }
  81. L -= zoneNum * zoneType - (zoneType == 6 ? 3 : 0);
  82. // 角度转换弧度
  83. var rB = B * pi / 180,
  84. tB = tan(rB),
  85. tB2 = pow(tB, 2),
  86. X = cos(rB) * L * pi / 180,
  87. N = a / sqrt(1 - e * sin(rB) * sin(rB)),
  88. it2 = e_2 * pow(cos(rB), 2);
  89. var x = X * X / 2 + (5 - tB2 + 9 * it2 + 4 * it2 * it2) * pow(X, 4) / 24 + (61 - 58 * tB2 + pow(tB, 4)) * pow(X, 6) / 720;
  90. x = calcMeridianLength(B) + N * tB * x;
  91. var y = N * (X + (1 - tB2 + it2) * pow(X, 3) / 6 + (5 - 18 * tB2 + pow(tB, 4) + 14 * it2 - 58 * tB2 * it2) * pow(X, 5) / 120);
  92. // 横轴坐标换算为常见格式:平移,加带号
  93. y += offsetValue;
  94. return {
  95. x: x,
  96. y: y,
  97. zoneNum:zoneNum,
  98. e: y, // 东坐标
  99. n: x // 北坐标
  100. };
  101. }
  102. // 根据纬度计算子午线弧长
  103. function calcMeridianLength(B)
  104. {
  105. // 将度转化为弧度
  106. var rB = B * pi / 180;
  107. return a * (1 - e) *
  108. (cA * rB -
  109. cB * sin(2 * rB) / 2 +
  110. cC * sin(4 * rB) / 4 -
  111. cD * sin(6 * rB) / 6 +
  112. cE * sin(8 * rB) / 8);
  113. }
  114. // 根据带号和分带类型计算中央子午线经度
  115. // @zoneNum 带号
  116. // @type 分带类型,3/6,可选,未提供时根据中国范围带号合理推测
  117. function calcCentralMeridian(zoneNum, zoneType) {
  118. zoneType = zoneType || calcZoneType(zoneNum);
  119. // 中国范围内带号,6度分带:13-21,3度分带:23-45
  120. if (zoneNum >= 0 && zoneNum <= 120 && (zoneType === 3 || zoneType === 6)) {
  121. return zoneType === 3 ? 3 * zoneNum : 6 * zoneNum - 3;
  122. } else {
  123. return null;
  124. }
  125. }
  126. // 根据中国范围内合理的 3度和 6度带号,推测分带类型
  127. function calcZoneType(zoneNum) {
  128. if (zoneNum <= 23) {
  129. return 6;
  130. }
  131. if (zoneNum >= 25) {
  132. return 3;
  133. }
  134. return null;
  135. }
  136. // 根据经度值、分带类型计算带号
  137. function calcZoneNum(lon, zoneType) {
  138. if(lon<=1.5 && zoneType ==3){
  139. lon = 360+lon
  140. }else if(lon<0){
  141. lon = 360+lon
  142. }
  143. if (lon >= 0 && lon <= 361.5 && zoneType && (zoneType === 3 || zoneType === 6)) {
  144. return zoneType === 3 ? floor((lon + 1.5) / 3) : ceil(lon / 6);
  145. } else {
  146. return null;
  147. }
  148. }
  149. // 计算底点纬度 Bf
  150. function calcBf(x, y) {
  151. var LIMIT_VALUE = 0.00000000001;
  152. var lastBf, Bf = x / (a * (1 - e) * cA);
  153. do {
  154. lastBf = Bf;
  155. Bf = (x + a * (1 - e) * (cB * sin(2 * Bf) / 2 - cC * sin(4 * Bf) / 4 + cD * sin(6 * Bf) / 6) - cE * sin(8 * Bf) / 8) / (a * (1 - e) * cA);
  156. }
  157. while (abs(lastBf - Bf) > LIMIT_VALUE);
  158. return Bf;
  159. }
  160. // API
  161. return {
  162. toLatLon: calcLatLonFromGauss,
  163. fromLatLon: calcGaussFromLatLon,
  164. calcZoneNum:calcZoneNum
  165. };
  166. }());