I have a unit test below running against the code that follows. This test sometimes passes, sometimes fails. Not sure why and hesitate to change things radically since well, it is a formula and sometimes passes...I'm thinking it may have something to do with the precision of the type double? Not sure. Thoughts?
[TestMethod]
public void CircleFromCircumference()
{
var random = new Random();
var circumference = random.NextDouble();
var circle = new Circle("My circle", circumference, Circle.CircleDimensions.Circumference);
var var1 = circumference - circle.Circumference;
var var2 = circumference - 2 * Math.PI * circle.Radius;
var var3 = circumference - Math.PI * circle.Diameter;
var var4 = Math.Pow(circumference / (2 * Math.PI), 2) * Math.PI - circle.Area;
Assert.IsTrue(
circumference - circle.Circumference <= 0 //circumference
&& circumference - 2 * Math.PI * circle.Radius <= 0 //radius
&& circumference - Math.PI * circle.Diameter <= 0 //diameter
&& Math.Pow(circumference / (2 * Math.PI), 2) * Math.PI - circle.Area <= 0 //area
&& string.IsNullOrEmpty(circle.ShapeException));
}
using System;
using System.Runtime.Serialization;
namespace Shapes
{
[DataContract]
public class Circle : Shape
{
[DataMember] public double Radius { get; set; }
[DataMember] public double Diameter { get; set; }
[DataMember] public double Circumference { get; set; }
/// <summary>
/// The name of the value you are sending. Radius is the default
/// </summary>
public enum CircleDimensions
{
Circumference = 1,
Area = 2,
Diameter = 3
}
/// <summary>
///
/// </summary>
/// <param name="circleName">The name of your circle</param>
/// <param name="dimension">The value of the dimension you are providing</param>
/// <param name="circleDimensions">The name of the value you are providing. Radius is default</param>
public Circle(string circleName, double dimension = 0, CircleDimensions circleDimensions = 0)
{
this.ShapeName = circleName;
if (dimension <= 0)
{
this.ShapeException = "Parameters must be greater than zero";
return;
}
switch (circleDimensions)
{
case CircleDimensions.Circumference:
//radius from Circumference
this.Circumference = dimension;
this.Radius = this.RadiusFromCircumference(dimension);
this.Area = this.CalculateArea(this.Radius);
this.Diameter = this.CalculateDiameter(this.Radius);
break;
case CircleDimensions.Area:
//radius from Area
break;
case CircleDimensions.Diameter:
//radius from diameter
break;
default: //calculate from radius
this.Radius = dimension;
this.Diameter = this.CalculateDiameter(dimension);
this.Circumference = this.CalculateCircumference(dimension);
this.Area = this.CalculateArea(dimension);
break;
}
}
private double RadiusFromCircumference(double dimension) => dimension / (2 * Math.PI);
private double CalculateCircumference(double dimension) => 2 * Math.PI * dimension;
private double CalculateDiameter(double dimension) => 2 * dimension;
private double CalculateArea(double dimension) =>
Math.PI * (Math.Pow(dimension, 2));
}
}
The inconsistency has nothing to do with precision per se, it has more to do with how floating point representation works. For example, if you write this:
for (float f = 0.0f; f != 1.0f; f+=0.1f)
{
Console.WriteLine(f);
}
It will never exit. Because 0.1 does not have an exact representation in binary form. (https://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/). I also recommend reading (https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)
Back to the problem at hand, in your code, you are getting the Radius using this :
dimension / (2 * Math.PI); //passed in dimension is the Circumference, returns radius
And then in your test you are asserting that:
circumference - 2 * Math.PI * circle.Radius <= 0
Dividing and then multiplying by the same floating point number is not guaranteed to give you the original floating point number as a result.
Thus, it is a bad idea in general to assert this. The most common way to test "almost equality" is to test equality "within limits". In your case, all you have to do is define a small enough epsilon that you deem "acceptable", greater or equal to double.Epsilon in your tests.
var epsilon = double.Epsilon;
Assert.IsTrue(
Math.Abs(circumference - circle.Circumference) <= epsilon //circumference
&& Math.Abs(circumference - 2 * Math.PI * circle.Radius) <= epsilon //radius
&& Math.Abs(circumference - Math.PI * circle.Diameter) <= epsilon //diameter
&& Math.Abs(Math.Pow(circumference / (2 * Math.PI), 2) * Math.PI - circle.Area) <= epsilon //area
&& string.IsNullOrEmpty(circle.ShapeException));
If instead you have to guarantee exactness, one option is to switch to a non-floating point type like decimal, but expect a performance hit.
Related
I want to calculate bearing between 2 GPS positions, I foollowed this page recommandations for my algorythm:
public static double Bearing(IPointGps pt1, IPointGps pt2)
{
double x = Math.Cos(pt1.Latitude) * Math.Sin(pt2.Latitude) - Math.Sin(pt1.Latitude) * Math.Cos(pt2.Latitude) * Math.Cos(pt2.Longitude - pt1.Longitude);
double y = Math.Sin(pt2.Longitude - pt1.Longitude) * Math.Cos(pt2.Latitude);
// Math.Atan2 can return negative value, 0 <= output value < 2*PI expected
return (Math.Atan2(y, x) + Math.PI * 2)%(Math.PI * 2);
}
Then I transform my value in degrees using this method
public static double RadiansToDegrees(double angle)
{
return (angle * 180.0) / Math.PI;
}
I have the following test sample:
Point1 (lat, long) = 43.6373638888888888888888888888889, 1.35762222222222222222222222222222
Point2 (lat, long) = 43.6156444444444444444444444444444,1.380225
Expected bearing = 323°
However, I obtain a bearing of 315.5° (5.5062235835910762 rad). If i calculate the expected radian value, i get 5.637413 which leaves no doubt that my problem lies in my bearing method.
I already implemented other computation methods using .Net Math package (including Cos, Sin, Tan and ATan methods) and my unit tests pass with 1e-12 precision. What am I missing?
PS: I also tryied to reimplement the Atan2 method in case there is a lack of precision in it. I obtain the very same result
edit: My Latitude and Longitude are double as per the following interface
public interface IPointGps
{
double Latitude { get; }
double Longitude { get; }
}
Math.Sin() and all similar methods expect argument in radians, but your latitudes and longitudes are in degrees. You have to convert IPointGps to radians before you calculate bearing, or modify Bearing calculation, e.g.:
public static double Bearing(IPointGps pt1, IPointGps pt2)
{
double x = Math.Cos(DegreesToRadians(pt1.Latitude)) * Math.Sin(DegreesToRadians(pt2.Latitude)) - Math.Sin(DegreesToRadians(pt1.Latitude)) * Math.Cos(DegreesToRadians(pt2.Latitude)) * Math.Cos(DegreesToRadians(pt2.Longitude - pt1.Longitude));
double y = Math.Sin(DegreesToRadians(pt2.Longitude - pt1.Longitude)) * Math.Cos(DegreesToRadians(pt2.Latitude));
// Math.Atan2 can return negative value, 0 <= output value < 2*PI expected
return (Math.Atan2(y, x) + Math.PI * 2) % (Math.PI * 2);
}
public static double DegreesToRadians(double angle)
{
return angle * Math.PI / 180.0d;
}
returns bearing 5.637716736134105.
It looks like your latitude and longitude variables are float (single precision). If that is the case, then your are facing a precision error.
I want to calculate bearing between 2 GPS positions, I foollowed this page recommandations for my algorythm:
public static double Bearing(IPointGps pt1, IPointGps pt2)
{
double x = Math.Cos(pt1.Latitude) * Math.Sin(pt2.Latitude) - Math.Sin(pt1.Latitude) * Math.Cos(pt2.Latitude) * Math.Cos(pt2.Longitude - pt1.Longitude);
double y = Math.Sin(pt2.Longitude - pt1.Longitude) * Math.Cos(pt2.Latitude);
// Math.Atan2 can return negative value, 0 <= output value < 2*PI expected
return (Math.Atan2(y, x) + Math.PI * 2)%(Math.PI * 2);
}
Then I transform my value in degrees using this method
public static double RadiansToDegrees(double angle)
{
return (angle * 180.0) / Math.PI;
}
I have the following test sample:
Point1 (lat, long) = 43.6373638888888888888888888888889, 1.35762222222222222222222222222222
Point2 (lat, long) = 43.6156444444444444444444444444444,1.380225
Expected bearing = 323°
However, I obtain a bearing of 315.5° (5.5062235835910762 rad). If i calculate the expected radian value, i get 5.637413 which leaves no doubt that my problem lies in my bearing method.
I already implemented other computation methods using .Net Math package (including Cos, Sin, Tan and ATan methods) and my unit tests pass with 1e-12 precision. What am I missing?
PS: I also tryied to reimplement the Atan2 method in case there is a lack of precision in it. I obtain the very same result
edit: My Latitude and Longitude are double as per the following interface
public interface IPointGps
{
double Latitude { get; }
double Longitude { get; }
}
Math.Sin() and all similar methods expect argument in radians, but your latitudes and longitudes are in degrees. You have to convert IPointGps to radians before you calculate bearing, or modify Bearing calculation, e.g.:
public static double Bearing(IPointGps pt1, IPointGps pt2)
{
double x = Math.Cos(DegreesToRadians(pt1.Latitude)) * Math.Sin(DegreesToRadians(pt2.Latitude)) - Math.Sin(DegreesToRadians(pt1.Latitude)) * Math.Cos(DegreesToRadians(pt2.Latitude)) * Math.Cos(DegreesToRadians(pt2.Longitude - pt1.Longitude));
double y = Math.Sin(DegreesToRadians(pt2.Longitude - pt1.Longitude)) * Math.Cos(DegreesToRadians(pt2.Latitude));
// Math.Atan2 can return negative value, 0 <= output value < 2*PI expected
return (Math.Atan2(y, x) + Math.PI * 2) % (Math.PI * 2);
}
public static double DegreesToRadians(double angle)
{
return angle * Math.PI / 180.0d;
}
returns bearing 5.637716736134105.
It looks like your latitude and longitude variables are float (single precision). If that is the case, then your are facing a precision error.
How to calculate joystick sensitivity, taking into account deadzone and the circular nature of the stick?
I'm working on a class that represents a stick of a gamepad. I'm having trouble with the mathematics of it, specifically with the sensitivity part. Sensitivity should make the joystick's distance from center non-linear. I applied sensitivity on a X-Box trigger without problems, but because a joystick has two axis (X and Y), I'm having trouble with the math involved.
I want to apply circular sensitivity to the stick, but I don't really know how to do that, specially taking into account other calculations on the axes (like deadzone, distance from center, etc.). How sould I accomplish that?
Additional details about the problem
Right now, I already have my temporary fix which is not working very well. It seems to be working when the joystick direction is either horizontal or vertical, but when I move it to a diagonal direction, is seems buged. My Joystick class has a Distance property, which retrieves the stick's distance from center (a value from 0 to 1). My Distance property is working well, but when I apply the sensitivity, the retrieved distance is less than 1 on diagonal directions if I move my josytick around, when it should be exactly 1, no matter the direction.
Below, I'm including a simplified version of my Joystick class, where I removed most of the unrelevant code. The calculated X and Y positions of the axes are retrieved by ComputedX and ComputedY properties. Each of this properties should include its axis final position (from -1 to 1) taking into account all the modifiers (deadzone, saturation, sensitivity, etc.).
public class Joystick
{
// Properties
// Physical axis positions
public double X { get; set;}
public double Y { get; set; }
// Virtual axis positions, with all modifiers applied (like deadzone, sensitivity, etc.)
public double ComputedX { get => ComputeX(); }
public double ComputedY {get => ComputeY(); }
// Joystick modifiers, which influence the computed axis positions
public double DeadZone { get; set; }
public double Saturation { get; set; }
public double Sensitivity { get; set; }
public double Range { get; set; }
public bool InvertX { get; set; }
public bool InvertY { get; set; }
// Other properties
public double Distance
{
get => CoerceValue(Math.Sqrt((ComputedX * ComputedX) + (ComputedY * ComputedY)), 0d, 1d);
}
public double Direction { get => ComputeDirection(); }
// Methods
private static double CoerceValue(double value, double minValue, double maxValue)
{
return (value < minValue) ? minValue : ((value > maxValue) ? maxValue : value);
}
protected virtual double ComputeX()
{
double value = X;
value = CalculateDeadZoneAndSaturation(value, DeadZone, Saturation);
value = CalculateSensitivity(value, Sensitivity);
value = CalculateRange(value, Range);
if (InvertX) value = -value;
return CoerceValue(value, -1d, 1d);
}
protected virtual double ComputeY()
{
double value = Y;
value = CalculateDeadZoneAndSaturation(value, DeadZone, Saturation);
value = CalculateSensitivity(value, Sensitivity);
value = CalculateRange(value, Range);
if (InvertY) value = -value;
return CoerceValue(value, -1d, 1d);
}
/// <sumary>Gets the joystick's direction (from 0 to 1).</summary>
private double ComputeDirection()
{
double x = ComputedX;
double y = ComputedY;
if (x != 0d && y != 0d)
{
double angle = Math.Atan2(x, y) / (Math.PI * 2d);
if (angle < 0d) angle += 1d;
return CoerceValue(angle, 0d, 1d);
}
return 0d;
}
private double CalculateDeadZoneAndSaturation(double value, double deadZone, double saturation)
{
deadZone = CoerceValue(deadZone, 0.0d, 1.0d);
saturation = CoerceValue(saturation, 0.0d, 1.0d);
if ((deadZone > 0) | (saturation < 1))
{
double distance = CoerceValue(Math.Sqrt((X * X) + (Y * Y)), 0.0d, 1.0d);
double directionalDeadZone = Math.Abs(deadZone * (value / distance));
double directionalSaturation = 1 - Math.Abs((1 - saturation) * (value / distance));
double edgeSpace = (1 - directionalSaturation) + directionalDeadZone;
double multiplier = 1 / (1 - edgeSpace);
if (multiplier != 0)
{
if (value > 0)
{
value = (value - directionalDeadZone) * multiplier;
value = CoerceValue(value, 0, 1);
}
else
{
value = -((Math.Abs(value) - directionalDeadZone) * multiplier);
value = CoerceValue(value, -1, 0);
}
}
else
{
if (value > 0)
value = CoerceValue(value, directionalDeadZone, directionalSaturation);
else
value = CoerceValue(value, -directionalSaturation, -directionalDeadZone);
}
value = CoerceValue(value, -1, 1);
}
return value;
}
private double CalculateSensitivity(double value, double sensitivity)
{
value = CoerceValue(value, -1d, 1d);
if (sensitivity != 0)
{
double axisLevel = value;
axisLevel = axisLevel + ((axisLevel - Math.Sin(axisLevel * (Math.PI / 2))) * (sensitivity * 2));
if ((value < 0) & (axisLevel > 0))
axisLevel = 0;
if ((value > 0) & (axisLevel < 0))
axisLevel = 0;
value = CoerceValue(axisLevel, -1d, 1d);
}
return value;
}
private double CalculateRange(double value, double range)
{
value = CoerceValue(value, -1.0d, 1.0d);
range = CoerceValue(range, 0.0d, 1.0d);
if (range < 1)
{
double distance = CoerceValue(Math.Sqrt((X * X) + (Y * Y)), 0d, 1d);
double directionalRange = 1 - Math.Abs((1 - range) * (value / distance));
value *= CoerceValue(directionalRange, 0d, 1d);
}
return value;
}
}
I tried to make this question as short as possible, but it's hard for me to explain this specific problem without describing some details about it. I know I should keep it short, but I would like to write at least a few more words:
Thank you for having the time to read all this!
After searching a bit for geometry math on the Internet, I finally found out the solution to my problem. I'm really bad at math, but now I know that it is actually very simple.
Instead of applying deadzone and sensitivity for each axis independently, I should apply them to the joystick radius. So, to do that, I just need to convert my joystick's cartesian coordinates (X and Y) to polar coordinates (Radius and Angle). Then, I apply deadzone sensitivity and all modifiers I want on the radius coordinate and convert it back to cartesian coordianates.
I'm posting here the code I'm using now. This looks far simpler and cleaner than the code on my question above:
private void ComputeCoordinates()
{
// Convert to polar coordinates.
double r = CoerceValue(Math.Sqrt((X * X) + (Y * Y)), 0d, 1d); // Radius;
double a = Math.Atan2(Y, X); // Angle (in radians);
// Apply modifiers.
double value = ComputeModifiers(r);
// Convert to cartesian coordinates.
double x = value * Math.Cos(a);
double y = value * Math.Sin(a);
// Apply axis independent modifiers.
if (InvertX) x = -x;
if (InvertY) y = -y;
// Set calculated values to property values;
_computedX = x;
_computedY = y;
}
private double ComputeModifiers(double value)
{
// Apply dead-zone and saturation.
if (DeadZone > 0d || Saturation < 1d)
{
double edgeSpace = (1 - Saturation) + DeadZone;
if (edgeSpace < 1d)
{
double multiplier = 1 / (1 - edgeSpace);
value = (value - DeadZone) * multiplier;
value = CoerceValue(value, 0d, 1d);
}
else
{
value = Math.Round(value);
}
}
// Apply sensitivity.
if (Sensitivity != 0d)
{
value = value + ((value - Math.Sin(value * (Math.PI / 2))) * (Sensitivity * 2));
value = CoerceValue(value, 0d, 1d);
}
// Apply range.
if (Range < 1d)
{
value = value * Range;
}
// Return calculated value.
return CoerceValue(value, 0d, 1d);
}
Explanation of the code above
Convert the physical joystick's X and Y coordinates to polar coordinates;
Apply deadzone, saturation, sensitivity and range modifiers to the radius coordinate;
Convert back to cartesian coordiantes (X and Y) using the original angle and the modified radius;
Optional: apply axis independent modifiers to each of the new axis (in this case, I'm just inverting each axis if the user wants the axis to be inverted);
Done. Every modifier is now applied in a circular way, no matter the direction I move the joystick;
Well, this situation had cost me about a day of work, because I didn't found anything related to my problem on Internet and I didn't know very well how to search for the solution, but I hope other people getting to this question may find this useful.
Here are some references about cartesian and polar coordinate systems:
https://en.wikipedia.org/wiki/Cartesian_coordinate_system
https://en.wikipedia.org/wiki/Polar_coordinate_system
https://social.msdn.microsoft.com/Forums/vstudio/en-US/9f120a35-dcac-42ab-b763-c65f3c39afdc/conversion-between-cartesian-to-polar-coordinates-and-back?forum=vbgeneral
The below worked well for me. It takes a standard parabola (x^2) and makes sure the result is signed. You can probably adjust the curve to make it closer to what you need by using a graphing calculator.
As it is, f(-1) = -1, f(0) = 0, f(1) = 1 and the curve in between is not too sensitive.
Mathf.Pow(axes.x, 2) * (axes.x < 0 ? -1 : 1)
In my case on picture firstPoint0 - as example my first point and center of the circle, relative this point confine screenings by radius 1 km. I need to show just all points in my radius, others points thisPoint not show by linq query.
var flats = context.Flats;
var first = flats.FirstOrDefault(x => x.Lattitude && x.Longitude);
var getAllInRadius = flats.Where(? take points where distance <= 1 km)
Just use the Haversine formula that returns the great-circle distance between two points on a sphere:
// Returns the great circle distance between two flats, as meters
public static double DistanceBetweenFlats(Flat flat1, Flat flat2)
{
const int EarthRadius = 6371;
double latitude = ToRadian(flat2.Latitude - flat1.Latitude);
double longitude = ToRadian(flat2.Longitude - flat1.Longitude);
double tmp = (Math.Sin(latitude / 2) * Math.Sin(latitude / 2)) +
(Math.Cos(ToRadian(flat1.Latitude)) * Math.Cos(ToRadian(flat2.Latitude)) *
Math.Sin(longitude / 2) * Math.Sin(longitude / 2));
double c = 2 * Math.Asin(Math.Min(1, Math.Sqrt(tmp)));
double d = EarthRadius * c;
return d * 1000;
}
...
var centerFlat = ...;
var getAllInRadius = flats.Where(z => DistanceBetweenFlats(centerFlat, z) <= 1000);
Of course all of this assumes you're using LINQ in memory (not LINQ to Entities). If it's not the case, you'll have to use spatial queries.
I`d like calculate the distance of two geo points. the points are given in longitude and latitude.
the coordinates are:
point 1: 36.578581, -118.291994
point 2: 36.23998, -116.83171
here a website to compare the results:
http://www.movable-type.co.uk/scripts/latlong.html
here the code I used from this link:
Calculate distance between two points in google maps V3
const double PIx = Math.PI;
const double RADIO = 6378.16;
/// <summary>
/// Convert degrees to Radians
/// </summary>
/// <param name="x">Degrees</param>
/// <returns>The equivalent in radians</returns>
public static double Radians(double x)
{
return x * PIx / 180;
}
/// <summary>
/// Calculate the distance between two places.
/// </summary>
/// <param name="lon1"></param>
/// <param name="lat1"></param>
/// <param name="lon2"></param>
/// <param name="lat2"></param>
/// <returns></returns>
public static double DistanceBetweenPlaces(double lon1, double lat1, double lon2, double lat2)
{
double R = 6371; // km
double dLat = Radians(lat2 - lat1);
double dLon = Radians(lon2 - lon1);
lat1 = Radians(lat1);
lat2 = Radians(lat2);
double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Sin(dLon / 2) * Math.Sin(dLon / 2) * Math.Cos(lat1) * Math.Cos(lat2);
double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
double d = R * c;
return d;
}
Console.WriteLine(DistanceAlgorithm.DistanceBetweenPlaces(36.578581, -118.291994, 36.23998, -116.83171));
the issue is that I get two different results.
my result: 163,307 km
result of the website: 136 km
any suggestions???
torti
Your formula is almost correct, but you have to swap parameters for longitude an latitude
Console.WriteLine(DistanceAlgorithm.DistanceBetweenPlaces(-118.291994, 36.578581, -116.83171, 36.23998)); // = 136 km
I'm using simplified formula:
// cos(d) = sin(φА)·sin(φB) + cos(φА)·cos(φB)·cos(λА − λB),
// where φА, φB are latitudes and λА, λB are longitudes
// Distance = d * R
public static double DistanceBetweenPlaces(double lon1, double lat1, double lon2, double lat2)
{
double R = 6371; // km
double sLat1 = Math.Sin(Radians(lat1));
double sLat2 = Math.Sin(Radians(lat2));
double cLat1 = Math.Cos(Radians(lat1));
double cLat2 = Math.Cos(Radians(lat2));
double cLon = Math.Cos(Radians(lon1) - Radians(lon2));
double cosD = sLat1*sLat2 + cLat1*cLat2*cLon;
double d = Math.Acos(cosD);
double dist = R * d;
return dist;
}
Testing:
(Distance at Equator): Longitudes 0, 100; Latitudes = 0,0; DistanceBetweenPlaces(0, 0, 100, 0) = 11119.5 km
(Distance at North Pole): Longitudes 0, 100; Latitudes = 90,90; DistanceBetweenPlaces(0, 90, 100, 90) = 0 km
Longitudes: -118.291994, -116.83171; Latitudes: 36.578581, 36.23998 = 135.6 km
Longitudes: 36.578581, 36.23998; Latitudes: -118.291994, -116.83171 = 163.2 km
Best regards
P.S. At web site you use for result comparison, for every point first text box is latitude, second - longitude
As you are using the framework 4.0, I would suggest the GeoCoordinate class.
// using System.Device.Location;
GeoCoordinate c1 = new GeoCoordinate(36.578581, -118.291994);
GeoCoordinate c2 = new GeoCoordinate(36.23998, -116.83171);
double distanceInKm = c1.GetDistanceTo(c2) / 1000;
// Your result is: 136,111419742602
You have to add a reference to System.Device.dll.
In my article published several years ago (link: http://www.codeproject.com/Articles/469500/Edumatter-School-Math-Calculators-and-Equation-Sol) I have described 3 useful Functions to calculate the distance between 2 geo-points (in other words, great-circle (orthodromic) distance on Earth between 2 geo-points), which differs in terms of accuracy/performance:
// Haversine formula to calculate great-circle distance between two points on Earth
private const double _radiusEarthMiles = 3959;
private const double _radiusEarthKM = 6371;
private const double _m2km = 1.60934;
private const double _toRad = Math.PI / 180;
/// <summary>
/// Haversine formula to calculate
/// great-circle (orthodromic) distance on Earth
/// High Accuracy, Medium speed
/// </summary>
/// <param name="Lat1">double: 1st point Latitude</param>
/// <param name="Lon1">double: 1st point Longitude</param>
/// <param name="Lat2">double: 2nd point Latitude</param>
/// <param name="Lon2">double: 2nd point Longitude</param>
/// <returns>double: distance in miles</returns>
public static double DistanceMilesHaversine(double Lat1,
double Lon1,
double Lat2,
double Lon2)
{
try
{
double _radLat1 = Lat1 * _toRad;
double _radLat2 = Lat2 * _toRad;
double _dLatHalf = (_radLat2 - _radLat1) / 2;
double _dLonHalf = Math.PI * (Lon2 - Lon1) / 360;
// intermediate result
double _a = Math.Sin(_dLatHalf);
_a *= _a;
// intermediate result
double _b = Math.Sin(_dLonHalf);
_b *= _b * Math.Cos(_radLat1) * Math.Cos(_radLat2);
// central angle, aka arc segment angular distance
double _centralAngle = 2 * Math.Atan2(Math.Sqrt(_a + _b), Math.Sqrt(1 - _a - _b));
// great-circle (orthodromic) distance on Earth between 2 points
return _radiusEarthMiles * _centralAngle;
}
catch { throw; }
}
// Spherical law of cosines formula to calculate great-circle distance between two points on Earth
/// <summary>
/// Spherical Law of Cosines formula to calculate
/// great-circle (orthodromic) distance on Earth;
/// High Accuracy, Medium speed
/// http://en.wikipedia.org/wiki/Spherical_law_of_cosines
/// </summary>
/// <param name="Lat1">double: 1st point Latitude</param>
/// <param name="Lon1">double: 1st point Longitude</param>
/// <param name="Lat2">double: 2nd point Latitude</param>
/// <param name="Lon2">double: 2nd point Longitude</param>
/// <returns>double: distance in miles</returns>
public static double DistanceMilesSLC( double Lat1,
double Lon1,
double Lat2,
double Lon2)
{
try
{
double _radLat1 = Lat1 * _toRad;
double _radLat2 = Lat2 * _toRad;
double _radLon1 = Lon1 * _toRad;
double _radLon2 = Lon2 * _toRad;
// central angle, aka arc segment angular distance
double _centralAngle = Math.Acos(Math.Sin(_radLat1) * Math.Sin(_radLat2) +
Math.Cos(_radLat1) * Math.Cos(_radLat2) * Math.Cos(_radLon2 - _radLon1));
// great-circle (orthodromic) distance on Earth between 2 points
return _radiusEarthMiles * _centralAngle;
}
catch { throw; }
}
// Great-circle distance calculation using Spherical Earth projection formula**
/// <summary>
/// Spherical Earth projection to a plane formula (using Pythagorean Theorem)
/// to calculate great-circle (orthodromic) distance on Earth.
/// http://en.wikipedia.org/wiki/Geographical_distance
/// central angle =
/// Sqrt((_radLat2 - _radLat1)^2 + (Cos((_radLat1 + _radLat2)/2) * (Lon2 - Lon1))^2)
/// Medium Accuracy, Fast,
/// relative error less than 0.1% in search area smaller than 250 miles
/// </summary>
/// <param name="Lat1">double: 1st point Latitude</param>
/// <param name="Lon1">double: 1st point Longitude</param>
/// <param name="Lat2">double: 2nd point Latitude</param>
/// <param name="Lon2">double: 2nd point Longitude</param>
/// <returns>double: distance in miles</returns>
public static double DistanceMilesSEP(double Lat1,
double Lon1,
double Lat2,
double Lon2)
{
try
{
double _radLat1 = Lat1 * _toRad;
double _radLat2 = Lat2 * _toRad;
double _dLat = (_radLat2 - _radLat1);
double _dLon = (Lon2 - Lon1) * _toRad;
double _a = (_dLon) * Math.Cos((_radLat1 + _radLat2) / 2);
// central angle, aka arc segment angular distance
double _centralAngle = Math.Sqrt(_a * _a + _dLat * _dLat);
// great-circle (orthodromic) distance on Earth between 2 points
return _radiusEarthMiles * _centralAngle;
}
catch { throw; }
}
Functions return results in miles; to find the distance in km multiply the result by 1.60934 (see private const double _m2km = 1.60934).
Pertinent to the sample: find the distance point1 (36.578581, -118.291994) and point2 (36.23998, -116.83171) the three aforementioned Function produced the following results (km):
136.00206654936932
136.00206654937023
136.00374497149613
and the calculator (link: http://www.movable-type.co.uk/scripts/latlong.html) gave the result: 136.0
Hope this may help. Best regards,
I used the formula from Wikipedia and put it in a lambda function:
Func<double, double, double, double, double> CalcDistance = (lat1, lon1, lat2, lon2) =>
{
Func<double, double> Radians = (angle) =>
{
return angle * (180.0 / Math.PI);
};
const double radius = 6371;
double delataSigma = Math.Acos(Math.Sin(Radians(lat1)) * Math.Sin(Radians(lat2)) +
Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * Math.Cos(Math.Abs(Radians(lon2) - Radians(lon1))));
double distance = radius * delataSigma;
return distance;
};
try this... I have used this is apps before -- its pretty accurate. Forgive me for not giving due credit to the brilliant soul who originally published this, I transposed it from java to C#:
namespace Sample.Geography
{
using System;
public class GeodesicDistance
{
private static double DegsToRadians(double degrees)
{
return (0.017453292519943295 * degrees);
}
public static double? GetDistance(double lat1, double lon1, double lat2, double lon2)
{
long num = 0x615299L;
double num2 = 6356752.3142;
double num3 = 0.0033528106647474805;
double num4 = DegsToRadians(lon2 - lon1);
double a = Math.Atan((1 - num3) * Math.Tan(DegsToRadians(lat1)));
double num6 = Math.Atan((1 - num3) * Math.Tan(DegsToRadians(lat2)));
double num7 = Math.Sin(a);
double num8 = Math.Sin(num6);
double num9 = Math.Cos(a);
double num10 = Math.Cos(num6);
double num11 = num4;
double num12 = 6.2831853071795862;
int num13 = 20;
double y = 0;
double x = 0;
double num18 = 0;
double num20 = 0;
double num22 = 0;
while ((Math.Abs((double) (num11 - num12)) > 1E-12) && (--num13 > 0))
{
double num14 = Math.Sin(num11);
double num15 = Math.Cos(num11);
y = Math.Sqrt(((num10 * num14) * (num10 * num14)) + (((num9 * num8) - ((num7 * num10) * num15)) * ((num9 * num8) - ((num7 * num10) * num15))));
if (y == 0)
{
return 0;
}
x = (num7 * num8) + ((num9 * num10) * num15);
num18 = Math.Atan2(y, x);
double num19 = ((num9 * num10) * num14) / y;
num20 = 1 - (num19 * num19);
if (num20 == 0)
{
num22 = 0;
}
else
{
num22 = x - (((2 * num7) * num8) / num20);
}
double num21 = ((num3 / 16) * num20) * (4 + (num3 * (4 - (3 * num20))));
num12 = num11;
num11 = num4 + ((((1 - num21) * num3) * num19) * (num18 + ((num21 * y) * (num22 + ((num21 * x) * (-1 + ((2 * num22) * num22)))))));
}
if (num13 == 0)
{
return null;
}
double num23 = (num20 * ((num * num) - (num2 * num2))) / (num2 * num2);
double num24 = 1 + ((num23 / 16384) * (4096 + (num23 * (-768 + (num23 * (320 - (175 * num23)))))));
double num25 = (num23 / 1024) * (256 + (num23 * (-128 + (num23 * (74 - (47 * num23))))));
double num26 = (num25 * y) * (num22 + ((num25 / 4) * ((x * (-1 + ((2 * num22) * num22))) - ((((num25 / 6) * num22) * (-3 + ((4 * y) * y))) * (-3 + ((4 * num22) * num22))))));
return new double?((num2 * num24) * (num18 - num26));
}
}
}
I just tried to code at GeoDataSource, and it worked perfectly well:
http://www.geodatasource.com/developers/c-sharp
I think you are interchanging latitude and longitude values. Try correcting those or change sequence of parameters.