CoordinateConvert.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*
  2. * ## 当前互联网地图的坐标系现状
  3. * ### 地球坐标(WGS84)
  4. * - 国际标准,从 GPS 设备中取出的数据的坐标系
  5. * - 国际地图提供商使用的坐标系
  6. * ### 火星坐标(GCJ-02)也叫国测局坐标系
  7. * - 中国标准,从国行移动设备中定位获取的坐标数据使用这个坐标系
  8. * - 国家规定: 国内出版的各种地图系统(包括电子形式),必须至少采用GCJ-02对地理位置进行首次加密.
  9. *
  10. * ### 百度坐标(BD-09)
  11. * - 百度标准,百度 SDK,百度地图,GeoCoding 使用
  12. * -(本来就乱了,百度又在火星坐标上来个二次加密)
  13. *
  14. * ## 开发过程需要注意的事
  15. * - 从设备获取经纬度(GPS)坐标
  16. *
  17. * 如果使用的是百度sdk那么可以获得百度坐标(bd09)或者火星坐标(GCJ02),默认是bd09
  18. * 如果使用的是ios的原生定位库,那么获得的坐标是WGS84
  19. * 如果使用的是高德sdk,那么获取的坐标是GCJ02
  20. * - 互联网在线地图使用的坐标系
  21. *
  22. * 火星坐标系:
  23. * iOS 地图(其实是高德)
  24. * Google国内地图(.cn域名下)
  25. * 搜搜、阿里云、高德地图、腾讯
  26. * 百度坐标系:
  27. * 当然只有百度地图
  28. * WGS84坐标系:
  29. * 国际标准,谷歌国外地图、osm地图等国外的地图一般都是这个
  30. * 从JavaScript版本转到C#版本
  31. * Created by Wander gis on 2015/7/8.
  32. * 提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换
  33. * 修复了,中国范围的经纬度.
  34. */
  35. using System;
  36. namespace Masuit.Tools.Maths;
  37. /// <summary>
  38. /// UMD魔法代码,地图坐标转换偏移代码
  39. /// </summary>
  40. public static class CoordinateConvert
  41. {
  42. private const double x_Pi = 3.14159265358979324 * 3000.0 / 180.0;
  43. private const double a = 6378245.0;
  44. private const double ee = 0.00669342162296594323;
  45. private const double Pi = 3.1415926535897932384626;
  46. /// <summary>
  47. /// WGS84转GCJ02
  48. /// </summary>
  49. /// <param name="wgsLon"></param>
  50. /// <param name="wgsLat"></param>
  51. /// <param name="gcjLon"></param>
  52. /// <param name="gcjLat"></param>
  53. public static void WGS84ToGCJ02(double wgsLon, double wgsLat, out double gcjLon, out double gcjLat)
  54. {
  55. if (OutOfChina(wgsLon, wgsLat))
  56. {
  57. gcjLon = wgsLon;
  58. gcjLat = wgsLat;
  59. return;
  60. }
  61. var dLat = TransformLat(wgsLon - 105.0, wgsLat - 35.0);
  62. var dLon = TransformLon(wgsLon - 105.0, wgsLat - 35.0);
  63. var radLat = wgsLat / 180.0 * Pi;
  64. var magic = Math.Sin(radLat);
  65. magic = 1 - magic * ee * magic;
  66. var sqrtMagic = Math.Sqrt(magic);
  67. dLat = dLat * 180.0 / (a * (1 - ee) / (magic * sqrtMagic) * Pi);
  68. dLon = dLon * 180.0 / (a / sqrtMagic * Math.Cos(radLat) * Pi);
  69. gcjLat = wgsLat + dLat;
  70. gcjLon = wgsLon + dLon;
  71. }
  72. /// <summary>
  73. /// GCJ02 转换为 WGS84
  74. /// </summary>
  75. /// <param name="gcjLon"></param>
  76. /// <param name="gcjLat"></param>
  77. /// <param name="wgsLon"></param>
  78. /// <param name="wgsLat"></param>
  79. public static void GCJ02ToWGS84(double gcjLon, double gcjLat, out double wgsLon, out double wgsLat)
  80. {
  81. if (OutOfChina(gcjLon, gcjLat))
  82. {
  83. wgsLon = gcjLon;
  84. wgsLat = gcjLat;
  85. return;
  86. }
  87. var dLat = TransformLat(gcjLon - 105.0, gcjLat - 35.0);
  88. var dLon = TransformLon(gcjLon - 105.0, gcjLat - 35.0);
  89. var radLat = gcjLat / 180.0 * Pi;
  90. var magic = Math.Sin(radLat);
  91. magic = 1 - magic * magic * ee;
  92. var sqrtMagic = Math.Sqrt(magic);
  93. dLat = dLat * 180.0 / (a * (1 - ee) / (magic * sqrtMagic) * Pi);
  94. dLon = dLon * 180.0 / (a / sqrtMagic * Math.Cos(radLat) * Pi);
  95. wgsLat = gcjLat - dLat;
  96. wgsLon = gcjLon - dLon;
  97. }
  98. /// <summary>
  99. /// 百度坐标系(BD-09) 与 火星坐标系(GCJ-02)的转换,即 百度 转 谷歌、高德
  100. /// </summary>
  101. /// <param name="bdLon"></param>
  102. /// <param name="bdLat"></param>
  103. /// <param name="gcjLon"></param>
  104. /// <param name="gcjLat"></param>
  105. public static void BD09ToGCJ02(double bdLon, double bdLat, out double gcjLon, out double gcjLat)
  106. {
  107. var x = bdLon - 0.0065;
  108. var y = bdLat - 0.006;
  109. var z = Math.Sqrt(x * x + y * y) - 0.00002 * Math.Sin(y * x_Pi);
  110. var theta = Math.Atan2(y, x) - 0.000003 * Math.Cos(x * x_Pi);
  111. gcjLon = z * Math.Cos(theta);
  112. gcjLat = z * Math.Sin(theta);
  113. }
  114. /// <summary>
  115. /// 火星坐标系(GCJ-02) 与百度坐标系(BD-09) 的转换,即谷歌、高德 转 百度
  116. /// </summary>
  117. /// <param name="gcjLon"></param>
  118. /// <param name="gcjLat"></param>
  119. /// <param name="bdLon"></param>
  120. /// <param name="bdLat"></param>
  121. public static void GCJ02ToBD09(double gcjLon, double gcjLat, out double bdLon, out double bdLat)
  122. {
  123. var z = Math.Sqrt(gcjLon * gcjLon + gcjLat * gcjLat) + 0.00002 * Math.Sin(gcjLat * x_Pi);
  124. var theta = Math.Atan2(gcjLat, gcjLon) + 0.000003 * Math.Cos(gcjLon * x_Pi);
  125. bdLon = Math.Cos(theta) * z + 0.0065;
  126. bdLat = Math.Sin(theta) * z + 0.006;
  127. }
  128. /// <summary>
  129. /// 判断是否在国内,不在国内则不做偏移
  130. /// </summary>
  131. /// <param name="lon"></param>
  132. /// <param name="lat"></param>
  133. /// <returns></returns>
  134. private static bool OutOfChina(double lon, double lat) =>
  135. // https://cn.bing.com/search?q=%E4%B8%AD%E5%9B%BD%E7%BB%8F%E7%BA%AC%E5%BA%A6%E8%8C%83%E5%9B%B4&qs=n&form=QBRE&sp=-1&pq=%E4%B8%AD%E5%9B%BD%E7%BB%8F%E7%BA%AC%E5%BA%A6%E8%8C%83%E5%9B%B4&sc=5-7&sk=&cvid=A687C2BEA56F4B08BE0913ADDA0C6674&ghsh=0&ghacc=0&ghpl=
  136. // 经度范围:73°33′E至135°05′E; 纬度范围:3°51′N至53°33′N
  137. lon is < 73.33 or > 135.05 || lat is < 3.51 or > 53.33;
  138. /// <summary>
  139. /// 转换纬度
  140. /// </summary>
  141. /// <param name="lon">经度</param>
  142. /// <param name="lat">纬度</param>
  143. /// <returns></returns>
  144. private static double TransformLat(double lon, double lat)
  145. {
  146. var ret = -100.0 + 2.0 * lon + 3.0 * lat + 0.2 * lat * lat + 0.1 * lon * lat + 0.2 * Math.Sqrt(Math.Abs(lon));
  147. ret += 20.0 * Math.Sin(6.0 * lon * Pi) + 20.0 * Math.Sin(2.0 * lon * Pi) * 2.0 / 3.0;
  148. ret += 20.0 * Math.Sin(lat * Pi) + 40.0 * Math.Sin(lat / 3.0 * Pi) * 2.0 / 3.0;
  149. ret += (160.0 * Math.Sin(lat / 12.0 * Pi) + 320 * Math.Sin(lat * Pi / 30.0) * 2.0) / 3.0;
  150. return ret;
  151. }
  152. /// <summary>
  153. /// 转换经度
  154. /// </summary>
  155. /// <param name="lon">经度</param>
  156. /// <param name="lat">纬度</param>
  157. /// <returns></returns>
  158. private static double TransformLon(double lon, double lat)
  159. {
  160. var ret = 300.0 + lon + 2.0 * lat + 0.1 * lon * lon + 0.1 * lon * lat + 0.1 * Math.Sqrt(Math.Abs(lon));
  161. ret += (20.0 * Math.Sin(6.0 * lon * Pi) + 20.0 * Math.Sin(2.0 * lon * Pi)) * 2.0 / 3.0;
  162. ret += (20.0 * Math.Sin(lon * Pi) + 40.0 * Math.Sin(lon / 3.0 * Pi)) * 2.0 / 3.0;
  163. ret += (150.0 * Math.Sin(lon / 12.0 * Pi) + 300.0 * Math.Sin(lon / 30.0 * Pi)) * 2.0 / 3.0;
  164. return ret;
  165. }
  166. }