Title: Calculate the great circle distance between two latitudes and longitudes in C#
At a small scale, the world is relatively flat. At larger scale, however, is it roughly spherical. (Sorry flat-earthers.) That means the shortest distance between two points on the globe is not a straight line. (Unless you have amazing teleportation or tunneling technology.) In that case, the shortest path between two points is a great circle distance. A great circle is a circle defined by a plane that passes through the two points and the center of the Earth.
This example uses the haversine formula to calculate the great circle distance between two points defined by their latitudes and longitudes. You can find a description of the formula in the Wikipedia article Haversine formula.
The heart of the example is the following method, which calculates the great circle distance.
// Calculate the great circle distance between two points.
private double GreatCircleDistance(
double lat1, double lon1, double lat2, double lon2)
{
const double radius = 6371; // Radius of the Earth in km.
lat1 = DegreesToRadians(lat1);
lon1 = DegreesToRadians(lon1);
lat2 = DegreesToRadians(lat2);
lon2 = DegreesToRadians(lon2);
double d_lat = lat2 - lat1;
double d_lon = lon2 - lon1;
double h = Math.Sin(d_lat / 2) * Math.Sin(d_lat / 2) +
Math.Cos(lat1) * Math.Cos(lat2) *
Math.Sin(d_lon / 2) * Math.Sin(d_lon / 2);
return 2 * radius * Math.Asin(Math.Sqrt(h));
}
The method sets the constant radius equal to the approximate radius of the Earth in kilometers. If you want the result to be in some other unit such as meters or miles, you can change this constant or you can convert the method's result from kilometers into the new units.
The method then converts the input latitudes and longitudes from degrees to radians. It then calculates the result of the haversine formula and returns the result.
The example uses two interesting helper methods. The first is the following DegreesToRadians method.
// Convert the degrees into radians.
private double DegreesToRadians(double degrees)
{
return degrees * Math.PI / 180.0;
}
This method converts an angle in degrees into radians by multiplying it by π and then dividing it by 180.
The second helper method parses a latitude or longitude that is stored in a string. It expects the string to include a measure of degrees, possibly followed by the ° symbol. It can then include the measurement's minutes and seconds, followed either by spaces or by ' or ". The whole thing should then be followed by N, S, E, or W. For example, the method accepts the following formats.
- 1° 14' N
- 1 14' 0" N
- 1° 14 0N
- 1 14 0 N
The following code shows the method.
private const string Deg = "°";
// Parse a latitude or longitude.
private double ParseLatLon(string str)
{
str = str.ToUpper().Replace(Deg, " ").Replace("'", " ").Replace("\"", " ");
str = str.Replace("S", " S").Replace("N", " N");
str = str.Replace("E", " E").Replace("W", " W");
char[] separators = {' '};
string[] fields = str.Split(separators,
StringSplitOptions.RemoveEmptyEntries);
double result = // Degrees.
double.Parse(fields[0]);
if (fields.Length > 2) // Minutes.
result += double.Parse(fields[1]) / 60;
if (fields.Length > 3) // Seconds.
result += double.Parse(fields[2]) / 3600;
if (str.Contains('S') || str.Contains('W')) result *= -1;
return result;
}
This method first capitalizes the string and removes any ° characters. It then replaces the ' and " characters with spaces. It also adds a space in front of any N, S, E, or W characters.
Next, the code splits the value into fields delimited by space characters. It assumes that the first field is the measurement's degrees and parses it. Then, if the value contains more than two fields, it parses the second field and divides the result by 60 to include the value's minutes. If the value includes more than three fields, the code parses the third field and uses it for the value's seconds.
Finally, if the value ends with S or W, it negates the result.
Note that this method isn't all that robust. For example, it doesn't use the ' and " characters to determine which fields represent minutes and seconds. Feel free to make an improved version that does a better job of understanding the possible formats. (You might want to use regular expressions.)
Download the example to experiment with it and to see additional details.
|