public void GeographicCoordinate_CanUpdateValidLongitude(double longitude) { ISphericalCoordinate expected = new Longitude(longitude); IGeographicCoordinate sut = InstantiateNewGeographicCoordinate(); sut.Longitude = new Longitude(longitude); var result = sut.Longitude; Assert.Equal(expected, result); }
public void Vincenty_CorrectlyCalculatesDestinationCoordinates(double latA, double lonA, double bearingInRadians, double distanceInMeters, double expectedLat, double expectedLon, int maxIterations, double tolerance) { IGeographicCoordinate pointA = new GeographicCoordinate(latA, lonA); IGeographicCoordinate expected = new GeographicCoordinate(expectedLat, expectedLon); IGeographicCoordinate result = pointA.DestinationCoordinates(new Angle(bearingInRadians, AngleMeasurement.Radians), new Distance(distanceInMeters, DistanceMeasurement.Meters), maxIterations, tolerance); Assert.Equal(expected, result); }
private static VincentyFormulaVariables VincentysDirectFormula(IGeographicCoordinate pointA, IAngle initialBearing, IDistance distance, int maxInterations = 200, double tolerance = 1.0E-12) { // Source: https://www.movable-type.co.uk/scripts/latlong-vincenty.html VincentyFormulaVariables v = PrepareσConvergenceLoop(pointA, initialBearing, distance); v = RecalculateσUntilConvergence(distance, maxInterations, tolerance, v); v.DestinationCoordinates = CalculateDestinationCoordinates(pointA, v); v.FinalBearing = Math.Atan2(v.sinα, -1.0 * (v.SinU1 * v.Sinσ - v.CosU1 * v.Cosσ * v.Cosα1)); return(v); }
private static IGeographicCoordinate CalculateDestinationCoordinates(IGeographicCoordinate pointA, VincentyFormulaVariables v) { var tmp = v.SinU1 * v.Sinσ - v.CosU1 * v.Cosσ * v.Cosα1; v.φ2 = Math.Atan2(v.SinU1 * v.Cosσ + v.CosU1 * v.Sinσ * v.Cosα1, (1 - f) * Math.Sqrt(Math.Pow(v.sinα, 2.0) + Math.Pow(tmp, 2.0))); v.λ = Math.Atan2(v.Sinσ * v.Sinα1, v.CosU1 * v.Cosσ - v.SinU1 * v.Sinσ * v.Cosα1); v.C = f / 16.0 * v.CosSqα * (4.0 + f * (4.0 - 3.0 * v.CosSqα)); v.L = v.λ - (1.0 - v.C) * f * v.sinα * (v.σ + v.C * v.Sinσ * (v.Cos2σM + v.C * v.Cosσ * (-1.0 + 2.0 * Math.Pow(v.Cos2σM, 2.0)))); v.λ2 = (pointA.Longitude.Angle.ToRadians() + v.L + 3.0 * Math.PI) % (2.0 * Math.PI) - Math.PI; // normalise to -180...+180 var latitude = Angle.ToDegrees(v.φ2); var longitude = Angle.ToDegrees(v.λ2); return(new GeographicCoordinate(latitude, longitude)); }
private static VincentyFormulaVariables PrepareλConvergenceLoop(IGeographicCoordinate pointA, IGeographicCoordinate pointB) { var v = new VincentyFormulaVariables { U1 = Math.Atan((1.0 - f) * Math.Tan(pointA.Latitude.Angle.ToRadians())), U2 = Math.Atan((1.0 - f) * Math.Tan(pointB.Latitude.Angle.ToRadians())), L = Angle.ToRadians(pointB.Longitude.Angle.ToDegrees() - pointA.Longitude.Angle.ToDegrees()) }; v.λ = v.L; v.SinU1 = Math.Sin(v.U1); v.CosU1 = Math.Cos(v.U1); v.SinU2 = Math.Sin(v.U2); v.CosU2 = Math.Cos(v.U2); return(v); }
private static IBearingDistance VincentysInverseFormula(IGeographicCoordinate pointA, IGeographicCoordinate pointB, int maxInterations = 200, double tolerance = 1.0E-12) { // Source: https://nathanrooy.github.io/posts/2016-12-18/vincenty-formula-with-python/ VincentyFormulaVariables v = PrepareλConvergenceLoop(pointA, pointB); v = RecalculateλUntilConvergence(v, maxInterations, tolerance); v = CalculateGeodesicDistanceAzimuth(v); IBearingDistance bearingDistance = new BearingDistance() { Distance = new Distance(v.GeodesicLength, DistanceMeasurement.Meters), InitialBearing = new Angle(v.ForwardAzimuth, AngleMeasurement.Radians), FinalBearing = new Angle(v.BackwardAzimuth, AngleMeasurement.Radians) }; return(bearingDistance); }
private static VincentyFormulaVariables PrepareσConvergenceLoop(IGeographicCoordinate pointA, IAngle initialBearing, IDistance distance) { VincentyFormulaVariables v = new VincentyFormulaVariables(); v.Sinα1 = Math.Sin(initialBearing.ToRadians()); v.Cosα1 = Math.Cos(initialBearing.ToRadians()); v.TanU1 = (1.0 - f) * Math.Tan(pointA.Latitude.Angle.ToRadians()); v.CosU1 = 1.0 / Math.Sqrt((1 + Math.Pow(v.TanU1, 2.0))); v.SinU1 = v.TanU1 * v.CosU1; v.σ1 = Math.Atan2(v.TanU1, v.Cosα1); v.sinα = v.CosU1 * v.Sinα1; v.CosSqα = 1.0 - Math.Pow(v.sinα, 2.0); v.USquared = v.CosSqα * (Math.Pow(a, 2.0) - Math.Pow(b, 2.0)) / Math.Pow(b, 2.0); v.A = 1 + v.USquared / 16384.0 * (4096.0 + v.USquared * (-768.0 + v.USquared * (320.0 - 175.0 * v.USquared))); v.B = v.USquared / 1024.0 * (256.0 + v.USquared * (-128.0 + v.USquared * (74.0 - 47.0 * v.USquared))); v.σ = distance.ToMeters() / (b * v.A); return(v); }
public static IAngle BearingFrom(this IGeographicCoordinate pointA, IGeographicCoordinate pointB, int maxInterations = 200, double tolerance = 1.0E-12) { return(VincentysInverseFormula(pointA, pointB, maxInterations, tolerance).FinalBearing); }
public static IAngle BearingFrom(this IGeographicCoordinate pointA, IAngle initialBearing, IDistance distance, int maxInterations = 200, double tolerance = 1.0E-12) { var backwardAzimuth = VincentysDirectFormula(pointA, initialBearing, distance, maxInterations, tolerance).BackwardAzimuth; return(new Angle(backwardAzimuth, AngleMeasurement.Radians)); }
public static IAngle FinalBearing(this IGeographicCoordinate pointA, IAngle initialBearing, IDistance distance, int maxInterations = 200, double tolerance = 1.0E-12) { var finalBearing = VincentysDirectFormula(pointA, initialBearing, distance, maxInterations, tolerance).FinalBearing; return(new Angle(finalBearing, AngleMeasurement.Radians)); }
public static IGeographicCoordinate DestinationCoordinates(this IGeographicCoordinate pointA, IAngle initialBearing, IDistance distance, int maxInterations = 200, double tolerance = 1.0E-12) { return(VincentysDirectFormula(pointA, initialBearing, distance, maxInterations, tolerance).DestinationCoordinates); }
public static IDistance GreatCircleDistanceTo(this IGeographicCoordinate pointA, IGeographicCoordinate pointB, int maxInterations = 200, double tolerance = 1.0E-12) { return(VincentysInverseFormula(pointA, pointB, maxInterations, tolerance).Distance); }
public void GeographicCoordinate_CannotUpdateInvalidLatitude(double angle) { IGeographicCoordinate sut = InstantiateNewGeographicCoordinate(); Assert.Throws <ArgumentOutOfRangeException>(() => sut.Latitude = new Latitude(angle)); }
public void GeographicCoordinate_CanInstantiate() { IGeographicCoordinate result = InstantiateNewGeographicCoordinate(); Assert.NotNull(result); }