Geodesic.cs
上传用户:huazai0421
上传日期:2008-05-30
资源大小:405k
文件大小:9k
源码类别:

SilverLight

开发平台:

C#

  1. using System;
  2. using ESRI.ArcGIS.Client.Geometry;
  3. namespace ESRI.ArcGIS.Samples.Extensions
  4. {
  5. /// <summary>
  6. /// Extension methods for geodesic calculations.
  7. /// </summary>
  8. public static class Geodesic
  9. {
  10. private const double EarthRadius = 6378.137; //kilometers. Change to miles to return all values in miles instead
  11. /// <summary>
  12. /// Gets the distance between two points in Kilometers.
  13. /// </summary>
  14. /// <param name="start">The start point.</param>
  15. /// <param name="end">The end point.</param>
  16. /// <returns></returns>
  17. public static double GetSphericalDistance(this MapPoint start, MapPoint end)
  18. {
  19. double lon1 = start.X / 180 * Math.PI;
  20. double lon2 = end.X / 180 * Math.PI;
  21. double lat1 = start.Y / 180 * Math.PI;
  22. double lat2 = end.Y / 180 * Math.PI;
  23. return 2 * Math.Asin(Math.Sqrt(Math.Pow((Math.Sin((lat1 - lat2) / 2)) , 2) +
  24.  Math.Cos(lat1) * Math.Cos(lat2) * Math.Pow(Math.Sin((lon1 - lon2) / 2), 2))) * EarthRadius;
  25. }
  26. /// <summary>
  27. /// Returns a polygon with a constant distance from the center point measured on the sphere.
  28. /// </summary>
  29. /// <param name="center">The center.</param>
  30. /// <param name="distKM">Radius in kilometers.</param>
  31. /// <returns></returns>
  32. public static Polygon GetRadiusAsPolygon(this MapPoint center, double distKM)
  33. {
  34. Polyline line = GetRadius(center, distKM);
  35. Polygon poly = new Polygon();
  36. if(line.Paths.Count > 1)
  37. {
  38. PointCollection ring = line.Paths[0];
  39. MapPoint last = ring[ring.Count - 1];
  40. for (int i = 1; i < line.Paths.Count; i++)
  41. {
  42. PointCollection pnts = line.Paths[i];
  43. ring.Add(new MapPoint(180 * Math.Sign(last.X), 90 * Math.Sign(center.Y)));
  44. last = pnts[0];
  45. ring.Add(new MapPoint(180 * Math.Sign(last.X), 90 * Math.Sign(center.Y)));
  46. foreach (MapPoint p in pnts)
  47. ring.Add(p);
  48. last = pnts[pnts.Count - 1];
  49. }
  50. poly.Rings.Add(ring);
  51. //pnts.Add(first);
  52. }
  53. else
  54. {
  55. poly.Rings.Add(line.Paths[0]);
  56. }
  57. if (distKM > EarthRadius * Math.PI / 2 && line.Paths.Count != 2)
  58. {
  59. PointCollection pnts = new PointCollection();
  60. pnts.Add(new MapPoint(-180, -90));
  61. pnts.Add(new MapPoint(180, -90));
  62. pnts.Add(new MapPoint(180, 90));
  63. pnts.Add(new MapPoint(-180, 90));
  64. pnts.Add(new MapPoint(-180, -90));
  65. poly.Rings.Add(pnts); //Exterior
  66. }
  67. return poly;
  68. }
  69. /// <summary>
  70. /// Returns a polyline with a constant distance from the center point measured on the sphere.
  71. /// </summary>
  72. /// <param name="center">The center.</param>
  73. /// <param name="distKM">Radius in kilometers.</param>
  74. // <returns></returns>
  75. public static Polyline GetRadius(this MapPoint center, double distKM)
  76. {
  77. Polyline line = new Polyline();
  78. PointCollection pnts = new PointCollection();
  79. line.Paths.Add(pnts);
  80. for (int i = 0; i < 360; i++)
  81. {
  82. //double angle = i / 180.0 * Math.PI;
  83. MapPoint p = GetPointFromHeading(center, distKM, i);
  84. if (pnts.Count > 0)
  85. {
  86. MapPoint lastPoint = pnts[pnts.Count - 1];
  87. int sign = Math.Sign(p.X);
  88. if (Math.Abs(p.X - lastPoint.X) > 180)
  89. {   //We crossed the date line
  90. double lat = LatitudeAtLongitude(lastPoint, p, sign * -180);
  91. pnts.Add(new MapPoint(sign * -180, lat));
  92. pnts = new PointCollection();
  93. line.Paths.Add(pnts);
  94. pnts.Add(new MapPoint(sign * 180, lat));
  95. }
  96. }
  97. pnts.Add(p);
  98. }
  99. pnts.Add(line.Paths[0][0]);
  100. return line;
  101. }
  102. /// <summary>
  103. /// Gets the shortest path line between two points. THe line will be following the great
  104. /// circle described by the two points.
  105. /// </summary>
  106. /// <param name="start">The start point.</param>
  107. /// <param name="end">The end point.</param>
  108. /// <returns></returns>
  109. public static Polyline GetGeodesicLine(this MapPoint start, MapPoint end)
  110. {
  111. Polyline line = new Polyline();
  112. if (Math.Abs(end.X - start.X) <= 180) // Doesn't cross dateline 
  113. {
  114. PointCollection pnts = GetGeodesicPoints(start, end);
  115. line.Paths.Add(pnts);
  116. }
  117. else
  118. {
  119. double lon1 = start.X / 180 * Math.PI;
  120. double lon2 = end.X / 180 * Math.PI;
  121. double lat1 = start.Y / 180 * Math.PI;
  122. double lat2 = end.Y / 180 * Math.PI;
  123. double latA = LatitudeAtLongitude(lat1, lon1, lat2, lon2, Math.PI) / Math.PI * 180;
  124. //double latB = LatitudeAtLongitude(lat1, lon1, lat2, lon2, -180) / Math.PI * 180;
  125. line.Paths.Add(GetGeodesicPoints(start, new MapPoint(start.X < 0 ? -180 : 180, latA)));
  126. line.Paths.Add(GetGeodesicPoints(new MapPoint(start.X < 0 ? 180 : -180, latA), end));
  127. }
  128. return line;
  129. }
  130. private static PointCollection GetGeodesicPoints(MapPoint start, MapPoint end)
  131. {
  132. double lon1 = start.X / 180 * Math.PI;
  133. double lon2 = end.X / 180 * Math.PI;
  134. double lat1 = start.Y / 180 * Math.PI;
  135. double lat2 = end.Y / 180 * Math.PI;
  136. double dX = end.X - start.X;
  137. int points = (int)Math.Floor(Math.Abs(dX));
  138. dX = lon2 - lon1;
  139. PointCollection pnts = new PointCollection();
  140. pnts.Add(start);
  141. for (int i = 1; i < points; i++)
  142. {
  143. double lon = lon1 + dX / points * i;
  144. double lat = LatitudeAtLongitude(lat1, lon1, lat2, lon2, lon);
  145. pnts.Add(new MapPoint(lon / Math.PI * 180, lat / Math.PI * 180));
  146. }
  147. pnts.Add(end);
  148. return pnts;
  149. }
  150. /// <summary>
  151. /// Gets the latitude at a specific longitude for a great circle defined by p1 and p2.
  152. /// </summary>
  153. /// <param name="p1">The p1.</param>
  154. /// <param name="p2">The p2.</param>
  155. /// <param name="lon">The longitude in degrees.</param>
  156. /// <returns></returns>
  157. private static double LatitudeAtLongitude(MapPoint p1, MapPoint p2, double lon)
  158. {
  159. double lon1 = p1.X / 180 * Math.PI;
  160. double lon2 = p2.X / 180 * Math.PI;
  161. double lat1 = p1.Y / 180 * Math.PI;
  162. double lat2 = p2.Y / 180 * Math.PI;
  163. lon = lon / 180 * Math.PI;
  164. return LatitudeAtLongitude(lat1, lon1, lat2, lon2, lon) / Math.PI * 180;
  165. }
  166. /// <summary>
  167. /// Gets the latitude at a specific longitude for a great circle defined by lat1,lon1 and lat2,lon2.
  168. /// </summary>
  169. /// <param name="lat1">The start latitude in radians.</param>
  170. /// <param name="lon1">The start longitude in radians.</param>
  171. /// <param name="lat2">The end latitude in radians.</param>
  172. /// <param name="lon2">The end longitude in radians.</param>
  173. /// <param name="lon">The longitude in radians for where the latitude is.</param>
  174. /// <returns></returns>
  175. private static double LatitudeAtLongitude(double lat1, double lon1, double lat2, double lon2, double lon)
  176. {
  177. return Math.Atan((Math.Sin(lat1) * Math.Cos(lat2) * Math.Sin(lon - lon2)
  178.  - Math.Sin(lat2) * Math.Cos(lat1) * Math.Sin(lon - lon1)) / (Math.Cos(lat1) * Math.Cos(lat2) * Math.Sin(lon1 - lon2)));
  179. }
  180. /// <summary>
  181. /// Gets the true bearing at a distance from the start point towards the new point.
  182. /// </summary>
  183. /// <param name="start">The start point.</param>
  184. /// <param name="end">The point to get the bearing towards.</param>
  185. /// <param name="distanceKM">The distance in kilometers travelled between start and end.</param>
  186. /// <returns></returns>
  187. public static double GetTrueBearing(MapPoint start, MapPoint end, double distanceKM)
  188. {
  189. double d = distanceKM / EarthRadius; //Angular distance in radians
  190. double lon1 = start.X / 180 * Math.PI;
  191. double lat1 = start.Y / 180 * Math.PI;
  192. double lon2 = end.X / 180 * Math.PI;
  193. double lat2 = end.Y / 180 * Math.PI;
  194. double tc1;
  195. if (Math.Sin(lon2 - lon1) < 0)
  196. tc1 = Math.Acos((Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(d)) / (Math.Sin(d) * Math.Cos(lat1)));
  197. else
  198. tc1 = 2 * Math.PI - Math.Acos((Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(d)) / (Math.Sin(d) * Math.Cos(lat1)));
  199. return tc1 / Math.PI * 180;
  200. }
  201. /// <summary>
  202. /// Gets the point based on a start point, a heading and a distance.
  203. /// </summary>
  204. /// <param name="start">The start.</param>
  205. /// <param name="distanceKM">The distance KM.</param>
  206. /// <param name="heading">The heading.</param>
  207. /// <returns></returns>
  208. public static MapPoint GetPointFromHeading(MapPoint start, double distanceKM, double heading)
  209. {
  210. double brng = heading / 180 * Math.PI;
  211. double lon1 = start.X / 180 * Math.PI;
  212. double lat1 = start.Y / 180 * Math.PI;
  213. double dR = distanceKM / 6378.137; //Angular distance in radians
  214. double lat2 = Math.Asin(Math.Sin(lat1) * Math.Cos(dR) + Math.Cos(lat1) * Math.Sin(dR) * Math.Cos(brng));
  215. double lon2 = lon1 + Math.Atan2(Math.Sin(brng) * Math.Sin(dR) * Math.Cos(lat1), Math.Cos(dR) - Math.Sin(lat1) * Math.Sin(lat2));
  216. double lon = lon2 / Math.PI * 180;
  217. double lat = lat2 / Math.PI * 180;
  218. while (lon < -180) lon += 360;
  219. while (lat < -90) lat += 180;
  220. while (lon > 180) lon -= 360;
  221. while (lat > 90) lat -= 180;
  222. return new MapPoint(lon, lat);
  223. }
  224. }
  225. }