RealWorldTerrainUTM.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /* INFINITY CODE */
  2. /* https://infinity-code.com */
  3. using System;
  4. using System.Text.RegularExpressions;
  5. namespace InfinityCode.RealWorldTerrain
  6. {
  7. public static class RealWorldTerrainUTM
  8. {
  9. const double flattening = 298.257223563;
  10. const double equatorialRadius = 6378137;
  11. private static double DegToRad(double deg)
  12. {
  13. double pi = Math.PI;
  14. return deg / 180 * pi;
  15. }
  16. private static double FootpointLatitude(double y)
  17. {
  18. const double r = equatorialRadius;
  19. const double v1 = r * (1.0 - 1.0 / flattening);
  20. const double x = (r - v1) / (r + v1);
  21. double v2 = (r + v1) / 2.0 * (1.0 + Math.Pow(x, 2.0) / 4.0 + Math.Pow(x, 4.0) / 64.0);
  22. double v3 = y / v2;
  23. double v4 = 3.0 * x / 2.0 + -27.0 * Math.Pow(x, 3.0) / 32.0 + 269.0 * Math.Pow(x, 5.0) / 512.0;
  24. double v5 = 21.0 * Math.Pow(x, 2.0) / 16.0 + -55.0 * Math.Pow(x, 4.0) / 32.0;
  25. double v6 = 151.0 * Math.Pow(x, 3.0) / 96.0 + -417.0 * Math.Pow(x, 5.0) / 128.0;
  26. double v7 = 1097.0 * Math.Pow(x, 4.0) / 512.0;
  27. return v3 + v4 * Math.Sin(2.0 * v3) + v5 * Math.Sin(4.0 * v3) + v6 * Math.Sin(6.0 * v3) + v7 * Math.Sin(8.0 * v3);
  28. }
  29. private static void Geodetic_To_UPS(double lng, double lat, out string latZone, out int lngZone, out double easting, out double northing)
  30. {
  31. if (Math.Abs(lng + 180.0) < double.Epsilon) lng = 180.0;
  32. double v1 = lat * Math.PI / 180.0;
  33. double v2 = lng * Math.PI / 180.0;
  34. const double x = 1.0 / flattening;
  35. const double v3 = equatorialRadius * (1.0 - x);
  36. double v4 = Math.Sqrt(2.0 * x - Math.Pow(x, 2.0));
  37. double v5 = 2.0 * (equatorialRadius * equatorialRadius) / v3 * Math.Pow((1.0 - v4) / (1.0 + v4), v4 / 2.0) * Math.Tan(Math.PI / 4.0 - Math.Abs(v1) / 2.0) * Math.Pow((1.0 + v4 * Math.Sin(Math.Abs(v1))) / (1.0 - v4 * Math.Sin(Math.Abs(v1))), v4 / 2.0);
  38. double v6 = 2000000.0 + 0.994 * v5 * Math.Sin(v2);
  39. double v7 = lat <= 0.0 ? 2000000.0 + 0.994 * v5 * Math.Cos(v2) : 2000000.0 - 0.994 * v5 * Math.Cos(v2);
  40. string str = lat < 0.0 ? (lng >= 0.0 || lng == -180.0 || lat == -90.0 ? "B" : "A") : (lng >= 0.0 || lng == -180.0 || lat == 90.0 ? "Z" : "Y");
  41. easting = v6;
  42. northing = v7;
  43. lngZone = 0;
  44. latZone = str;
  45. }
  46. private static double RadToDeg(double rad)
  47. {
  48. return rad / Math.PI * 180.0;
  49. }
  50. public static void ToLngLat(string latZone, int lngZone, double easting, double northing, out double lng, out double lat)
  51. {
  52. if (new Regex("[AaBbYyZz]").IsMatch(latZone))
  53. {
  54. UPS_To_Geodetic(latZone, easting, northing, out lng, out lat);
  55. return;
  56. }
  57. bool isNorthHemisphere = new Regex("[CcDdEeFfGgHhJjKkLlMm]").IsMatch(latZone);
  58. double v1 = easting - 500000.0;
  59. const double v2 = 0.9996;
  60. double x = v1 / v2;
  61. if (isNorthHemisphere) northing -= 10000000.0;
  62. UTMtoLatLong(x, northing / v2, UTMCentralMeridian(lngZone), out lng, out lat);
  63. }
  64. public static void ToUTM(double lng, double lat, out string latZone, out int lngZone, out double easting, out double northing)
  65. {
  66. if (lat < -80.0 || lat > 84.0)
  67. {
  68. Geodetic_To_UPS(lng, lat, out latZone, out lngZone, out easting, out northing);
  69. return;
  70. }
  71. const double c0 = 0.9996;
  72. const double c1 = Math.PI / 180.0;
  73. const double c2 = 1.0 / flattening;
  74. const double c3 = equatorialRadius * (1.0 - c2);
  75. const double c4 = 1.0 - c3 / equatorialRadius * (c3 / equatorialRadius);
  76. int v1 = (int)Math.Floor(lng / 6.0 + 31.0);
  77. string str = lat >= -72.0 ? (lat >= -64.0 ? (lat >= -56.0 ? (lat >= -48.0 ? (lat >= -40.0 ? (lat >= -32.0 ? (lat >= -24.0 ? (lat >= -16.0 ? (lat >= -8.0 ? (lat >= 0.0 ? (lat >= 8.0 ? (lat >= 16.0 ? (lat >= 24.0 ? (lat >= 32.0 ? (lat >= 40.0 ? (lat >= 48.0 ? (lat >= 56.0 ? (lat >= 64.0 ? (lat >= 72.0 ? "X" : "W") : "V") : "U") : "T") : "S") : "R") : "Q") : "P") : "N") : "M") : "L") : "K") : "J") : "H") : "G") : "F") : "E") : "D") : "C";
  78. double v2 = Math.Sqrt(1.0 - Math.Pow(c3, 2.0) / Math.Pow(equatorialRadius, 2.0));
  79. double v3 = lat * c1;
  80. double v4 = 3.0 + 6.0 * (1.0 + Math.Floor((lng + 180.0) / 6.0) - 1.0) - 180.0;
  81. double v5 = v2 * v2 / (1.0 - Math.Pow(v2, 2.0));
  82. double v6 = equatorialRadius / Math.Sqrt(1.0 - Math.Pow(v2 * Math.Sin(v3), 2.0));
  83. double v7 = Math.Pow(Math.Tan(v3), 2.0);
  84. double v8 = v5 * Math.Pow(Math.Cos(v3), 2.0);
  85. double v9 = (lng - v4) * c1 * Math.Cos(v3);
  86. double v10 = (v3 * (1.0 - c4 * (0.25 + c4 * (3.0 / 64.0 + 5.0 * c4 / 256.0))) - Math.Sin(2.0 * v3) * (c4 * (0.375 + c4 * (3.0 / 32.0 + 45.0 * c4 / 1024.0))) + Math.Sin(4.0 * v3) * (c4 * c4 * (15.0 / 256.0 + c4 * 45.0 / 1024.0)) - Math.Sin(6.0 * v3) * (c4 * c4 * c4 * 0.0113932291666667)) * equatorialRadius;
  87. double v11 = c0 * v6 * v9 * (1.0 + v9 * v9 * ((1.0 - v7 + v8) / 6.0 + v9 * v9 * (5.0 - 18.0 * v7 + v7 * v7 + 72.0 * v8 - 58.0 * v5) / 120.0)) + 500000.0;
  88. double v12 = c0 * (v10 + v6 * Math.Tan(v3) * (v9 * v9 * (0.5 + v9 * v9 * ((5.0 - v7 + 9.0 * v8 + 4.0 * v8 * v8) / 24.0 + v9 * v9 * (61.0 - 58.0 * v7 + v7 * v7 + 600.0 * v8 - 330.0 * v5) / 720.0))));
  89. latZone = str;
  90. lngZone = v1 == 61? 1: v1;
  91. easting = v11;
  92. northing = v12 < 0.0? 10000000.0 + v12: v12;
  93. }
  94. private static void UPS_To_Geodetic(string latZone, double easting, double northing, out double lng, out double lat)
  95. {
  96. const double x = 1.0 / flattening;
  97. const double v1 = equatorialRadius * (1.0 - x);
  98. double v2 = Math.Sqrt(2 * x - Math.Pow(x, 2));
  99. double v3 = (easting - 2000000) / 0.994;
  100. double v4 = (northing - 2000000) / 0.994;
  101. double v5 = v4;
  102. if (v4 == 0.0) v4 = 1;
  103. bool flag = latZone.ToUpper() != "Z" && latZone.ToUpper() != "Y";
  104. double d1;
  105. double d2;
  106. if (flag)
  107. {
  108. d1 = Math.PI + Math.Atan(v3 / v5);
  109. d2 = Math.PI + Math.Atan(v3 / v4);
  110. }
  111. else
  112. {
  113. d1 = Math.PI - Math.Atan(v3 / v5);
  114. d2 = Math.PI - Math.Atan(v3 / v4);
  115. }
  116. double v6 = 2.0 * Math.Pow(equatorialRadius, 2.0) / v1 * Math.Pow((1.0 - v2) / (1.0 + v2), v2 / 2.0);
  117. double v7 = Math.Abs(v4);
  118. double v8 = Math.Abs(Math.Cos(d2));
  119. double v9 = v6 * v8;
  120. double y = Math.Log(v7 / v9) / Math.Log(Math.E) * -1.0;
  121. double v10 = 2.0 * Math.Atan(Math.Pow(Math.E, y)) - Math.PI / 2.0;
  122. double d3;
  123. double v11;
  124. double v12;
  125. for (d3 = 0.0; Math.Abs(v10 - d3) > 1E-07 && !double.IsInfinity(v10); v10 -= v11 / v12)
  126. {
  127. d3 = v10;
  128. double d4 = (1.0 + Math.Sin(v10)) / (1.0 - Math.Sin(v10)) * Math.Pow((1.0 - v2 * Math.Sin(v10)) / (1.0 + v2 * Math.Sin(v10)), v2);
  129. v11 = -y + 0.5 * Math.Log(d4);
  130. v12 = (1.0 - Math.Pow(v2, 2.0)) / ((1.0 - Math.Pow(v2, 2.0) * Math.Pow(Math.Sin(v10), 2.0)) * Math.Cos(v10));
  131. }
  132. if (!double.IsInfinity(v10)) d3 = v10;
  133. lat = !double.IsNaN(d3) ? d3 * (180.0 / Math.PI) : 90.0;
  134. if (flag) lat *= -1.0;
  135. lng = !double.IsNaN(d1) ? d1 * (180.0 / Math.PI) : 0.0;
  136. if (easting < 2000000.0) lng = (180.0 - lng % 180.0) * -1.0;
  137. else if (lng > 180.0) lng -= 180.0;
  138. else if (lng < -180.0) lng += 180.0;
  139. if (((northing < 2000000.0 ? 0 : v3 == 0.0 ? 1 : 0) & (flag ? 1 : 0)) != 0) lng = 0.0;
  140. if (northing < 2000000.0 && v3 == 0.0 && !flag) lng = 0.0;
  141. }
  142. private static double UTMCentralMeridian(double zone)
  143. {
  144. return DegToRad(zone * 6 - 183);
  145. }
  146. private static void UTMtoLatLong(double x, double y, double zone, out double lng, out double lat)
  147. {
  148. const double r = equatorialRadius;
  149. const double x2 = r * (1 - 1 / flattening);
  150. const double v1 = (r * r - x2 * x2) / (x2 * x2);
  151. double fpLat = FootpointLatitude(y);
  152. double x3 = Math.Cos(fpLat);
  153. double v2 = Math.Pow(x3, 2);
  154. double v3 = v1 * v2;
  155. double v4 = Math.Pow(r, 2) / (x2 * Math.Sqrt(v3 + 1));
  156. double v5 = v4;
  157. double v6 = Math.Tan(fpLat);
  158. double v7 = v6 * v6;
  159. double v8 = v7 * v7;
  160. double v9 = 1 / (v5 * x3);
  161. double v10 = v5 * v4;
  162. double v11 = v6 / (2 * v10);
  163. double v12 = v10 * v4;
  164. double v13 = 1 / (6 * v12 * x3);
  165. double v14 = v12 * v4;
  166. double v15 = v6 / (24 * v14);
  167. double v16 = v14 * v4;
  168. double v17 = 1 / (120 * v16 * x3);
  169. double v18 = v16 * v4;
  170. double v19 = v6 / (720 * v18);
  171. double v20 = v18 * v4;
  172. double v21 = 1.0 / (5040 * v20 * x3);
  173. double v22 = v6 / (40320 * (v20 * v4));
  174. double v23 = -1 - v3;
  175. double v24 = -1 - 2 * v7 - v3;
  176. double v25 = 5 + 3 * v7 + 6 * v3 - 6 * v7 * v3 - 3 * (v3 * v3) - 9 * v7 * (v3 * v3);
  177. double v26 = 5 + 28 * v7 + 24 * v8 + 6 * v3 + 8 * v7 * v3;
  178. double v27 = -61 - 90 * v7 - 45 * v8 - 107 * v3 + 162 * v7 * v3;
  179. double v28 = -61 - 662 * v7 - 1320 * v8 - 720 * (v8 * v7);
  180. double v29 = 1385 + 3633 * v7 + 4095 * v8 + 1575 * (v8 * v7);
  181. double rad1 = fpLat + v11 * v23 * (x * x) + v15 * v25 * Math.Pow(x, 4) + v19 * v27 * Math.Pow(x, 6) + v22 * v29 * Math.Pow(x, 8);
  182. double rad2 = zone + v9 * x + v13 * v24 * Math.Pow(x, 3) + v17 * v26 * Math.Pow(x, 5) + v21 * v28 * Math.Pow(x, 7);
  183. lat = RadToDeg(rad1);
  184. lng = RadToDeg(rad2);
  185. if (lat > 90) lat = 90;
  186. else if (lat < -90) lat = -90;
  187. if (lng > 180) lng = 180;
  188. else if (lng < -180) lng = -180;
  189. }
  190. }
  191. }