/// <summary> /// Calculates the distance between two <see cref="GeoCoordinate"/> values. This method /// uses the algorithm described at http://www.movable-type.co.uk/scripts/latlong.html. /// </summary> /// <param name="otherCoordinate">An instance of the <see cref="GeoCoordinate"/> class for comparison</param> /// <returns>The distance between this <see cref="GeoCoordinate"/> instances and the provided instance</returns> public double? GetDistanceTo(GeoCoordinate otherCoordinate) { if (otherCoordinate == null) { throw new ArgumentNullException("otherCoordinate"); } // Ensure we have valid longitude and latitude values for the source if (this.Latitude == null || this.Longitude == null) { return null; } // Ensure we have valid longitude and latitude values for the target if (otherCoordinate.Latitude == null || otherCoordinate.Longitude == null) { return null; } const double radius = 6371; // Defines the radius of planet Earth. Often exposed simply as R. // Caclculate the latitude and longitude changes double deltaLat = (this.Latitude.ToDecimal().Value - otherCoordinate.Latitude.ToDecimal().Value).ToRad(); double deltaLng = (this.Longitude.ToDecimal().Value - otherCoordinate.Longitude.ToDecimal().Value).ToRad(); // Calculate the angle double angle = Math.Sin(deltaLat / 2) * Math.Sin(deltaLat / 2) + Math.Cos(this.Latitude.ToDecimal().Value.ToRad()) * Math.Cos(otherCoordinate.Latitude.ToDecimal().Value.ToRad()) * Math.Sin(deltaLng / 2) * Math.Sin(deltaLng / 2); // Calculate the circumference double c = 2 * Math.Atan2(Math.Sqrt(angle), Math.Sqrt(1 - angle)); return radius * c; }
/// <summary> /// Attempts to parse a latitude and longitude string into a /// new GeoCoordinate instance /// </summary> /// <param name="input">The longitude and latitude string</param> /// <param name="output">A newly created GeoCoordinate instance</param> /// <returns>true if the parsing was succesful; otherwsie false</returns> public static bool TryParse(string input, out GeoCoordinate output) { // Validate input parameter if (string.IsNullOrEmpty(input)) { output = null; return false; } // TODO: SUPPORT MGRS IN THE FUTURE // The regex below parses Longitude and Latitude values and supports // the following different formats: // - 38° 47’ 44” N 077° 36’ 50” W // - 38°47’44”N077°36’50”W // - 38°47’44”N,077°36’50”W // - 384744N 0773650W // - 38°47’44”N 077°36’50”W // - 38 47 44 N 77 36 50 W // - 38 47 44.23 N 77 36 50.77 W //const string longlatParseString = "^(?<lat_degrees>\\d{2})[°]?\\s*(?<lat_minutes>\\d{2})[’']?\\s*(?<lat_seconds>[\\d\\.]+)[”\\\"]?\\s*(?<lat_direction>[NnSs]{1})[\\s,_-]?(?<lng_degrees>\\d{2,3})[°]?\\s*(?<lng_minutes>\\d{2})[’']?\\s*(?<lng_seconds>[\\d\\.]+)[”\\\"]?\\s*(?<lng_direction>[EeWw]{1})$"; const string longlatParseString = @"^(?<lat_degrees>\d{2})[°]?\s*(?<lat_minutes>\d{2})[’']?\s*(?<lat_seconds>[\d\.]+)[”\""]?\s*(?<lat_direction>[NnSs]{1})[\s,_-]?(?<lng_degrees>\d{2,3})[°]?\s*(?<lng_minutes>\d{2})[’']?\s*(?<lng_seconds>[\d\.]+)[”\""]?\s*(?<lng_direction>[EeWw]{1})$"; Regex regex = new Regex(longlatParseString); // Check if the regex matches the provided value if (regex.IsMatch(input)) { Match match = regex.Match(input); //TODO: VALIDATE THE GROUPS // Create a DMS instance for Latitude and Longitude DMS latitude = new DMS(int.Parse(match.Groups["lat_degrees"].Value), int.Parse(match.Groups["lat_minutes"].Value), int.Parse(match.Groups["lat_seconds"].Value), (char)match.Groups["lat_direction"].Value[0]); DMS longitude = new DMS(int.Parse(match.Groups["lng_degrees"].Value), int.Parse(match.Groups["lng_minutes"].Value), int.Parse(match.Groups["lng_seconds"].Value), (char)match.Groups["lng_direction"].Value[0]); output = new GeoCoordinate(latitude, longitude); return true; } // The regex does not match so the provided input cannot be parsed as a longitude and latitude output = null; return false; }