/// <summary> /// Compute the Cartesian distance between two coordinatesusing the Bowring method (moderately fast, moderately accurate) /// </summary> /// <param name="fromX">source X ordinate</param> /// <param name="fromY">source Y ordinate</param> /// <param name="toX">destination X ordinate</param> /// <param name="toY">destination Y ordinate</param> /// <returns>The great circle distance from a to b</returns> public double DistanceBowring(double fromX, double fromY, double toX, double toY) { fromX = AngleUtils.ToRadians(fromX); fromY = AngleUtils.ToRadians(fromY); toX = AngleUtils.ToRadians(toX); toY = AngleUtils.ToRadians(toY); double a = Math.Sqrt(1 + er * Math.Pow(Math.Cos(fromY), 4.0)); double b = Math.Sqrt(1 + er * Math.Pow(Math.Cos(fromY), 2.0)); double c = Math.Sqrt(1 + er); double dLat = toY - fromY; double dLon = toX - fromX; double w = (a * dLon) / 2.0; double d = dLat * Math.Sin(2 * fromY + Constants.TwoThirds * dLat); d = d * (1 + (er3 / (4 * Math.Pow(b, 2.0)))); d = d * (dLat / (2.0 * b)); double ia = 1 / a; double e = Math.Sin(d) * Math.Cos(w); double f = ia * Math.Sin(w) * (b * Math.Cos(fromY) * Math.Cos(d) - Math.Sin(fromY) * Math.Sin(d)); //double g = Math.Atan(f / e); double s = Math.Asin(Math.Sqrt(Math.Pow(e, 2.0) + Math.Pow(f, 2.0)) * 2.0); //double h = ia * (Math.Sin(fromY) + b * Math.Cos(fromY) * Math.Tan(d)) * Math.Tan(w); return((a * c * s) / Math.Pow(b, 2.0)); }
/// <summary> /// Compute the Cartesian distance between two coordinates using the law of cosines method (fast, low accuracy) /// </summary> /// <param name="fromX">source X ordinate</param> /// <param name="fromY">source Y ordinate</param> /// <param name="toX">destination X ordinate</param> /// <param name="toY">destination Y ordinate</param> /// <returns>The great circle distance from a to b</returns> public double DistanceCosines(double fromX, double fromY, double toX, double toY) { fromX = AngleUtils.ToRadians(fromX); fromY = AngleUtils.ToRadians(fromY); toX = AngleUtils.ToRadians(toX); toY = AngleUtils.ToRadians(toY); double a = Math.Acos(Math.Sin(fromY) * Math.Sin(toY) + Math.Cos(fromY) * Math.Cos(toY) * Math.Cos(toX - fromX)); return(a * meanRadius); }
/// <summary> /// Compute the Cartesian distance between two coordinatesusing the Vincente method (slower - iterative, highly accurate) /// </summary> /// <param name="fromX">source X ordinate</param> /// <param name="fromY">source Y ordinate</param> /// <param name="toX">destination X ordinate</param> /// <param name="toY">destination Y ordinate</param> /// <returns>The great circle distance from a to b</returns> public double DistanceVincente(double fromX, double fromY, double toX, double toY) { fromX = AngleUtils.ToRadians(fromX); fromY = AngleUtils.ToRadians(fromY); toX = AngleUtils.ToRadians(toX); toY = AngleUtils.ToRadians(toY); double L = toX - fromX; double U1 = Math.Atan((1 - f) * Math.Tan(fromY)); double U2 = Math.Atan((1 - f) * Math.Tan(toY)); double sinU1 = Math.Sin(U1); double cosU1 = Math.Cos(U1); double sinU2 = Math.Sin(U2); double cosU2 = Math.Cos(U2); double lambda = L; double lambdaP; double iterLimit = 100; double cosSqAlpha = 0.0; double sinSigma = 0.0; double cos2SigmaM = 0.0; double cosSigma = 0.0; double sigma = 0.0; do { double sinLambda = Math.Sin(lambda); double cosLambda = Math.Cos(lambda); sinSigma = Math.Sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)); if (sinSigma == 0) { return(0); //coincident points } cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; sigma = Math.Atan2(sinSigma, cosSigma); double sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma; cosSqAlpha = 1 - sinAlpha * sinAlpha; cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha; if (double.IsNaN(cos2SigmaM)) { cos2SigmaM = 0; //equatorial line: cosSqAlpha=0 } double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)); lambdaP = lambda; lambda = L + (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); } while (Math.Abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0); if (iterLimit == 0) { return(this.DistanceHaversine(fromX, fromY, toX, toY)); // formula failed to converge, use next method } double uSq = cosSqAlpha * (a * a - b * b) / (b * b); double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))); double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); double deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM))); return(b * A * (sigma - deltaSigma)); }
/// <summary> /// Compute the "destination" point by travelling along a great circle path starting at the provided point. /// The path is based upon an initial direction and a fixed distance to travel along the great circle (may circumnavigate the globe) /// </summary> /// <param name="fromX">source X ordinate</param> /// <param name="fromY">source Y ordinate</param> /// <param name="distance">the distance to travel in meters</param> /// <param name="direction">the direction to travel in radians of compass AngleUtils (0 is north)</param> /// <returns>A coordinate of the final location</returns> public Coordinate2 <double> Destination(double fromX, double fromY, double distance, double direction) { fromX = AngleUtils.ToRadians(fromX); fromY = AngleUtils.ToRadians(fromY); double dR = distance / meanRadius; double brng = AngleUtils.ToRadians(direction); double lat2 = Math.Asin(Math.Sin(fromY) * Math.Cos(dR) + Math.Cos(fromY) * Math.Sin(dR) * Math.Cos(brng)); double lon2 = fromX + Math.Atan2(Math.Sin(brng) * Math.Sin(dR) * Math.Cos(fromY), Math.Cos(dR) - Math.Sin(fromY) * Math.Sin(lat2)); return(new Coordinate2 <double>(lon2, lat2)); }
/// <summary> /// Compute the compass direction between two points using the starting bearing as the direction /// </summary> /// <param name="fromX">source X ordinate</param> /// <param name="fromY">source Y ordinate</param> /// <param name="toX">destination X ordinate</param> /// <param name="toY">destination Y ordinate</param> /// <returns>The compass direction from a to b</returns> public double DirectionStartBearing(double fromX, double fromY, double toX, double toY) { fromX = AngleUtils.ToRadians(fromX); fromY = AngleUtils.ToRadians(fromY); toX = AngleUtils.ToRadians(toX); toY = AngleUtils.ToRadians(toY); double dLon = toX - fromX; double y = Math.Sin(dLon) * Math.Cos(toY); double x = Math.Cos(fromY) * Math.Sin(toY) - Math.Sin(fromY) * Math.Cos(toY) * Math.Cos(dLon); return(AngleUtils.ToDegrees(Math.Atan2(y, x))); }
/// <summary> /// Compute the Cartesian distance between two coordinates using the Haversine method (moderately fast, moderately accurate) /// </summary> /// <param name="fromX">source X ordinate</param> /// <param name="fromY">source Y ordinate</param> /// <param name="toX">destination X ordinate</param> /// <param name="toY">destination Y ordinate</param> /// <returns>The great circle distance from a to b</returns> public double DistanceHaversine(double fromX, double fromY, double toX, double toY) { fromX = AngleUtils.ToRadians(fromX); fromY = AngleUtils.ToRadians(fromY); toX = AngleUtils.ToRadians(toX); toY = AngleUtils.ToRadians(toY); double dY = toY - fromY; double dX = toX - fromX; double a = Math.Pow(Math.Sin(dY / 2), 2.0) + (Math.Cos(toY) * Math.Cos(fromY) * Math.Pow(Math.Sin(dX / 2), 2.0)); double c = 2.0 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); return(meanRadius * c); }
/// <summary> /// Compute the compass direction between two points using the Mid-point of the path as the direction /// </summary> /// <param name="fromX">source X ordinate</param> /// <param name="fromY">source Y ordinate</param> /// <param name="toX">destination X ordinate</param> /// <param name="toY">destination Y ordinate</param> /// <returns>The compass direction from a to b</returns> public double DirectionMidBearing(double fromX, double fromY, double toX, double toY) { fromX = AngleUtils.ToRadians(fromX); fromY = AngleUtils.ToRadians(fromY); toX = AngleUtils.ToRadians(toX); toY = AngleUtils.ToRadians(toY); double dLon = toX - fromX; var Bx = Math.Cos(toY) * Math.Cos(dLon); var By = Math.Cos(toY) * Math.Sin(dLon); var lat3 = AngleUtils.ToDegrees(Math.Atan2(Math.Sin(fromY) + Math.Sin(toY), Math.Sqrt((Math.Cos(fromY) + Bx) * (Math.Cos(fromY) + Bx) + By * By))); var lon3 = AngleUtils.ToDegrees(fromX + Math.Atan2(By, Math.Cos(fromY) + Bx)); return(DirectionStartBearing(lon3, lat3, toX, toY)); }
/// <summary> /// Compute the "destination" point by travelling along a loxodrome starting at the provided point. /// The path is based upon an constant direction and a fixed distance to travel along the loxodrome (may circumnavigate the globe, eventually spiral to pole) /// </summary> /// <param name="fromX">source X ordinate</param> /// <param name="fromY">source Y ordinate</param> /// <param name="distance">the distance to travel in meters</param> /// <param name="direction">the direction to travel in radians of compass AngleUtils (0 is north)</param> /// <returns>A coordinate of the final location</returns> public Coordinate2 <double> DestinationLoxodrome(double fromX, double fromY, double distance, double direction) { fromX = AngleUtils.ToRadians(fromX); fromY = AngleUtils.ToRadians(fromY); double brng = AngleUtils.ToRadians(direction); double dLat = distance * Math.Cos(brng); double lat2 = fromY + dLat; double dPhi = Math.Log(Math.Tan(lat2 / 2 + QuarterPi) / Math.Tan(fromY / 2 + QuarterPi)); double q = !double.IsNaN(dLat / dPhi) ? dLat / dPhi : Math.Cos(fromY); double dLon = distance * Math.Sin(brng) / q; if (Math.Abs(lat2) > Constants.HalfPi) { lat2 = lat2 > 0 ? Math.PI - lat2 : -(Math.PI - lat2); } double lon2 = (fromX + dLon + Math.PI) % Constants.TwoPi - Math.PI; return(new Coordinate2 <double>(lon2, lat2)); }
/// <summary> /// Compute the compass direction between two points using the loxodrome path as the direction /// </summary> /// <param name="fromX">source X ordinate</param> /// <param name="fromY">source Y ordinate</param> /// <param name="toX">destination X ordinate</param> /// <param name="toY">destination Y ordinate</param> /// <returns>The compass direction from a to b</returns> public double DirectionLoxodrome(double fromX, double fromY, double toX, double toY) { fromX = AngleUtils.ToRadians(fromX); fromY = AngleUtils.ToRadians(fromY); toX = AngleUtils.ToRadians(toX); toY = AngleUtils.ToRadians(toY); double dLon = toX - fromX; double dLat = toY - fromY; double dPhi = Math.Log(Math.Tan(toY / 2 + Math.PI / 4) / Math.Tan(fromY / 2 + Math.PI / 4)); double q = (!double.IsNaN(dLat / dPhi)) ? dLat / dPhi : Math.Cos(fromY); // E-W line gives dPhi=0 // if dLon over 180° take shorter rhumb across 180° meridian: if (Math.Abs(dLon) > Math.PI) { dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon); } return(Math.Atan2(dLon, dPhi)); }