I need to find the endpoint using the start point, distance and azimuth. Here are the values of each one:
latitude: 37.624942,
longitude": -7.896333,
azimute: 233.0
distance : 20.0
Here is my function:
private List<double> findEndPoint(string latitudeStart, string longitudeStart, string azimute,double distancia)
{
List<double> endPoint = new List<double>();
double latitudeStartDouble = Convert.ToDouble(latitudeStart, System.Globalization.CultureInfo.InvariantCulture);
double longitudeStartDouble = Convert.ToDouble(longitudeStart, System.Globalization.CultureInfo.InvariantCulture);
double azimuteDouble = Convert.ToDouble(azimute, System.Globalization.CultureInfo.InvariantCulture);
double azimuteRadians = ConvertToRadians(azimuteDouble);
double R = 6371.0; // Raio da Terra em km
double latitudeEnd = Math.Asin(Math.Sin(latitudeStartDouble) * Math.Cos(distancia / R) +
Math.Cos(latitudeStartDouble) * Math.Sin(distancia / R) * Math.Cos(azimuteRadians));
endPoint.Add(latitudeEnd);
double longitudeEnd = longitudeStartDouble + Math.Atan2(
Math.Sin(azimuteRadians) * Math.Sin(distancia / R) * Math.Cos(latitudeStartDouble),
Math.Cos(distancia / R) - Math.Sin(latitudeStartDouble) * Math.Sin(latitudeEnd));
endPoint.Add(longitudeEnd);
return endPoint;
}
It returns:
latitude: -0.0760588400705975
longitude:-7.8988473639987093
The latitude must be wrong, but I don't know why is it giving me that value.
You forgot to add latitudeStartDouble. You just calculate the delta. So latitudeEnd = latitudeStartDouble + ...etc...
I need to be working with radians and then converting it back to degrees:
private List<double> findEndPoint(string latitudeStart, string longitudeStart, string azimute,double distancia)
{
List<double> endPoint = new List<double>();
double latitudeStartDouble = Convert.ToDouble(latitudeStart, System.Globalization.CultureInfo.InvariantCulture);
latitudeStartDouble = ConvertToRadians(latitudeStartDouble);
double longitudeStartDouble = Convert.ToDouble(longitudeStart, System.Globalization.CultureInfo.InvariantCulture);
longitudeStartDouble = ConvertToRadians(longitudeStartDouble);
double azimuteDouble = Convert.ToDouble(azimute, System.Globalization.CultureInfo.InvariantCulture);
double azimuteRadians = ConvertToRadians(azimuteDouble);
double R = 6371; // Raio da Terra em km
double latitudeEnd = Math.Asin(Math.Sin(latitudeStartDouble) * Math.Cos(distancia / R) +
Math.Cos(latitudeStartDouble) * Math.Sin(distancia / R) * Math.Cos(azimuteRadians));
latitudeEnd = ConvertToDegrees(latitudeEnd);
endPoint.Add(latitudeEnd);
double longitudeEnd = longitudeStartDouble + Math.Atan2(Math.Sin(azimuteRadians) * Math.Sin(distancia / R) * Math.Cos(latitudeStartDouble),
Math.Cos(distancia / R) - Math.Sin(latitudeStartDouble) * Math.Sin(latitudeEnd));
longitudeEnd = ConvertToDegrees(longitudeEnd);
endPoint.Add(longitudeEnd);
return endPoint;
}
Related
I'm looking for a high precision solution to find the center point of a circle from 3 data points on a canvas (x,y). I found this example in the attached screenshot above, now I'm using the Math.NET package to solve the equation and I'm comparing the results against this online tool: https://planetcalc.com/8116/.
However, when I calculate the radius its completely off and often a negative number???
using MathNet.Numerics.LinearAlgebra.Double.Solvers;
using MathNet.Numerics.LinearAlgebra.Double;
using System;
namespace ConsoleAppTestBed
{
class Program
{
static void Main(string[] args)
{
var dataPoints = new double[,]
{
{ 5, 80 },
{ 20, 100 },
{ 40, 140 }
};
var fitter = new CircleFitter();
var result = fitter.Fit(dataPoints);
var x = -result[0];
var y = -result[1];
var c = result[2];
Console.WriteLine("Center Point:");
Console.WriteLine(x);
Console.WriteLine(y);
Console.WriteLine(c);
//// (x^2 + y^2 - c^2)
var radius = Math.Pow(x, 2) + Math.Pow(y, 2) - Math.Pow(c, 2);
//// sqrt((x^2 + y^2 - c^2))
radius = Math.Sqrt(radius);
Console.WriteLine("Radius:");
Console.WriteLine(radius);
Console.ReadLine();
}
public class CircleFitter
{
public double[] Fit(double[,] v)
{
var xy1 = new double[] { v[0,0], v[0,1] };
var xy2= new double[] { v[1, 0], v[1, 1] };
var xy3 = new double[] { v[2, 0], v[2, 1] };
// Create Left Side Matrix of Equation
var a = CreateLeftSide_(xy1);
var b = CreateLeftSide_(xy2);
var c = CreateLeftSide_(xy3);
var matrixA = DenseMatrix.OfArray(new[,]
{
{ a[0], a[1], a[2] },
{ b[0], b[1], b[2] },
{ c[0], c[1], c[2] }
});
// Create Right Side Vector of Equation
var d = CreateRightSide_(xy1);
var e = CreateRightSide_(xy2);
var f = CreateRightSide_(xy3);
double[] vector = { d, e, f };
var vectorB = Vector<double>.Build.Dense(vector);
// Solve Equation
var r = matrixA.Solve(vectorB);
var result = r.ToArray();
return result;
}
//2x, 2y, 1
public double[] CreateLeftSide_(double[] d)
{
return new double[] { (2 * d[0]), (2 * d[1]) , 1};
}
// -(x^2 + y^2)
public double CreateRightSide_(double[] d)
{
return -(Math.Pow(d[0], 2) + Math.Pow(d[1], 2));
}
}
}
}
Any ideas?
Thanks in advance.
The solution to your problem is here: The NumberDecimalDigits property
Code:
using System;
using System.Globalization;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
double x1 = 1, y1 = 1;
double x2 = 2, y2 = 4;
double x3 = 5, y3 = -3;
findCircle(x1, y1, x2, y2, x3, y3);
Console.ReadKey();
}
static void findCircle(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
NumberFormatInfo setPrecision = new NumberFormatInfo();
setPrecision.NumberDecimalDigits = 3; // 3 digits after the double point
double x12 = x1 - x2;
double x13 = x1 - x3;
double y12 = y1 - y2;
double y13 = y1 - y3;
double y31 = y3 - y1;
double y21 = y2 - y1;
double x31 = x3 - x1;
double x21 = x2 - x1;
double sx13 = (double)(Math.Pow(x1, 2) -
Math.Pow(x3, 2));
double sy13 = (double)(Math.Pow(y1, 2) -
Math.Pow(y3, 2));
double sx21 = (double)(Math.Pow(x2, 2) -
Math.Pow(x1, 2));
double sy21 = (double)(Math.Pow(y2, 2) -
Math.Pow(y1, 2));
double f = ((sx13) * (x12)
+ (sy13) * (x12)
+ (sx21) * (x13)
+ (sy21) * (x13))
/ (2 * ((y31) * (x12) - (y21) * (x13)));
double g = ((sx13) * (y12)
+ (sy13) * (y12)
+ (sx21) * (y13)
+ (sy21) * (y13))
/ (2 * ((x31) * (y12) - (x21) * (y13)));
double c = -(double)Math.Pow(x1, 2) - (double)Math.Pow(y1, 2) -
2 * g * x1 - 2 * f * y1;
double h = -g;
double k = -f;
double sqr_of_r = h * h + k * k - c;
// r is the radius
double r = Math.Round(Math.Sqrt(sqr_of_r), 5);
Console.WriteLine("Center of a circle: x = " + h.ToString("N", setPrecision) +
", y = " + k.ToString("N", setPrecision));
Console.WriteLine("Radius: " + r.ToString("N", setPrecision));
}
}
}
I have just converted William Li's answer to Swift 5 for
those who like to cmd+C and cmd+V like me :)
func calculateCircle(){
let x1:Float = 0
let y1:Float = 0
let x2:Float = 0.5
let y2:Float = 0.5
let x3:Float = 1
let y3:Float = 0
let x12 = x1 - x2
let x13 = x1 - x3
let y12 = y1 - y2
let y13 = y1 - y3
let y31 = y3 - y1
let y21 = y2 - y1
let x31 = x3 - x1
let x21 = x2 - x1
let sx13 = pow(x1, 2) - pow(x3, 2)
let sy13 = pow(y1, 2) - pow(y3, 2)
let sx21 = pow(x2, 2) - pow(x1, 2)
let sy21 = pow(y2, 2) - pow(y1, 2)
let f = ((sx13) * (x12)
+ (sy13) * (x12)
+ (sx21) * (x13)
+ (sy21) * (x13))
/ (2 * ((y31) * (x12) - (y21) * (x13)))
let g = ((sx13) * (y12)
+ (sy13) * (y12)
+ (sx21) * (y13)
+ (sy21) * (y13))
/ (2 * ((x31) * (y12) - (x21) * (y13)))
let c = -pow(x1, 2) - pow(y1, 2) - 2 * g * x1 - 2 * f * y1
let h = -g
let k = -f
let r = sqrt(h * h + k * k - c)
print("center x = \(h)")
print("center y = \(k)")
print("r = \(r)")
}
Updated Answer
The equation for radius is incorrect; it should be (not c squared):
which is why you get incorrect values for the radius.
The original answer was incorrect, but it is still 'interesting'.
(Incorrect) Original Answer
The cause of the problematic calculation is not solely due to the precision of the numbers, but more because problem is ill-conditioned. If you look at the three points and where they are located on the circle, you'll see that they are bunched on a small segment of the circumference. When the points are so close to each other, it is a tough ask to find the circle's centre and radius accurately.
So the intermediate calculations, which will have small rounding errors, result in hugely exaggerated errors.
You can see the ill-conditioned nature of the problem by adding the ConditionNumber() method.
// Solve Equation
Console.WriteLine(matrixA.ConditionNumber()); // <<< Returns 5800 -> Big!
var r = matrixA.Solve(vectorB); // Existing code
A large result indicates an ill-conditioned problem. In this case a value of 5800 is returned, which is large. You might get better results using Gaussian Elimination with partial pivoting, but it still does not address the fact that the basic problem is ill-conditioned, which is why you get wildly incorrect answers.
I am trying to convert CH1903+ coordinates into WGS84 coordinates based on the following example provided by the swiss "Bundesamt für Statistik" using C#.
Example:
As far i can calculate all values like shown in the example. But in the end when i try to calculate the variable "S" based on the values in the example i am getting wrong results.
double E = 2679520.05;
double alpha = 1.00072913843038;
double phi = 0.820535226;
double b = 0.820535226;
double K = 0.0030667323772751
I tried both implementations:
double S = Math.Log(Math.Tan(Math.PI / 4.0 + phi / 2.0)); --> result: 0.931969600979248
or
double S = 1/alpha * (Math.Log(Math.PI/4 + b/2) - K) + E * Math.Log(Math.Tan((Math.PI/4) + (Math.Asin(E * Math.Sin(phi))/2.0))); --> result: NaN
Can somebody tell what's wrong in my implementation that i am getting this wrong results? If i understand the example correctly both calculations should return 0.933114264192610 as result for the given values.
This is my implementation for converting swiss CH1903+ coordinates -> decimal coordinates (WGS84) which works good so far
Example CH1903+ Coordinates N = 1249251.544, E = 2645320.487;
// swiss CH1903+ coordinates
double N = 1249251.544,
E = 2645320.487;
const double K = 0.0030667323772751; // Konstante der Breitenformel
const double BESSEL_GH = 6377397.155; // grosse Halbachse des Bessel-Ellipsoids
const double BESSEL_SH = 6356078.963; // kleine Halbachse des Bessel-Ellipsoids
double GRS_GH = 6.378137 * Math.Pow(10, 6); // grosse Halbachse des GRS84-Ellipsoids
double GRS_SH = 6.356752314 * Math.Pow(10, 6); ; // kleine Halbachse des GRS84-Ellipsoids
double E2_BESSEL = (Math.Pow(BESSEL_GH, 2) - Math.Pow(BESSEL_SH, 2)) / Math.Pow(BESSEL_GH, 2), // 1.numerische Exzentrizität (im Quadrat) des Bessel-Ellipsoids
E_BESSEL = Math.Sqrt(E2_BESSEL), // 1.numerische Exzentrizität des Bessel-Ellipsoids
E2_GRS = (Math.Pow(GRS_GH, 2) - Math.Pow(GRS_SH, 2)) / Math.Pow(GRS_GH, 2); // 1.numerische Exzentrizität (im Quadrat) des GRS84-Ellipsoids
const double TOLERANCE = 0.0000000000000001;
///// swiss coordinates -> ellipsoid coordinates /////
double
Y = E - 2600000.0, // swiss CH1903 coordinates
X = N - 1200000.0, // swiss CH1903 coordinates
PHI_NULL = (46 + 57 / 60.0 + 8.66 / 3600.0) * Math.PI / 180, // geogr. Breite des Nullpunkts in Bern
LAMBDA_NULL = (7 + 26 / 60.0 + 22.5 / 3600.0) * Math.PI / 180, // geogr. Länge des Nullpunkts in Bern
R = (BESSEL_GH * Math.Sqrt(1 - E2_BESSEL)) / (1 - E2_BESSEL * Math.Pow(Math.Sin(PHI_NULL), 2)), // Radius der Projektionskugel
ALPHA = Math.Sqrt(1 + E2_BESSEL / (1 - E2_BESSEL) * Math.Pow(Math.Cos(PHI_NULL), 4)), // Verhältnis Kugellänge zu Ellipsoidlänge
B_GLOBAL_NULL = Math.Asin(Math.Sin(PHI_NULL) / ALPHA), // Breite des Nullpunkts auf der Kugel
L_PSEUDO = Y / R, // Kugelkoordinaten bezüglich Pseudoäquatorsystem in Bern
B_PSEUDO = 2 * (Math.Atan(Math.Pow(Math.E, (X / R))) - (Math.PI / 4)), // Kugelkoordinaten bezüglich Pseudoäquatorsystem in Bern
L = Math.Atan(Math.Sin(L_PSEUDO) / (Math.Cos(B_GLOBAL_NULL) * Math.Cos(L_PSEUDO) - Math.Sin(B_GLOBAL_NULL) * Math.Tan(B_PSEUDO))), // Kugelkoordinaten bezüglich Nullpunkt Bern
B = Math.Asin(Math.Cos(B_GLOBAL_NULL) * Math.Sin(B_PSEUDO) + Math.Sin(B_GLOBAL_NULL) * Math.Cos(B_PSEUDO) * Math.Cos(L_PSEUDO)), // Kugelkoordinaten bezüglich Nullpunkt Bern
LAMBDA = LAMBDA_NULL + L / ALPHA,
PHI = B,
S = (Math.Log(Math.Tan(Math.PI / 4.0 + B / 2.0)) - K) / ALPHA + E_BESSEL * Math.Log(Math.Tan(Math.PI / 4.0 + Math.Asin(E_BESSEL * Math.Sin(PHI)) / 2.0));
bool cont = true;
// iterate to tolerance
while (cont)
{
double PHI_TMP = 2 * Math.Atan(Math.Pow(Math.E, S)) - Math.PI / 2;
double S_TMP = (Math.Log(Math.Tan(Math.PI / 4 + B / 2)) - K) / ALPHA + E_BESSEL * Math.Log(Math.Tan(Math.PI / 4 + Math.Asin(E_BESSEL * Math.Sin(PHI_TMP)) / 2));
if (Math.Abs(PHI - PHI_TMP) < TOLERANCE)
{
cont = false;
}
else
{
PHI = PHI_TMP;
S = S_TMP;
}
}
///// ellipsoid coordinates (CH1903) -> karth. coordinates (CH1903) /////
double RN_CH1903 = BESSEL_GH / (Math.Sqrt(1 - E2_BESSEL * Math.Pow(Math.Sin(PHI), 2))), // Normalkrümmungsradius
X_KARTH = RN_CH1903 * Math.Cos(PHI) * Math.Cos(LAMBDA),
Y_KARTH = RN_CH1903 * Math.Cos(PHI) * Math.Sin(LAMBDA),
Z_KARTH = (RN_CH1903 * (1 - E2_BESSEL)) * Math.Sin(PHI);
///// karth. coordinates (CH1903) -> karth. coordinates (ETRS89/CHTRS95) /////
double X_CH1930P = X_KARTH + 674.374,
Y_CH1930P = Y_KARTH + 15.056,
Z_CH1930P = Z_KARTH + 405.346;
///// karth. coordinates (ETRS89/CHTRS95) -> ellipsoid coordinates (ETRS89/CHTRS95) /////
double PHI_ETRS = Math.Atan(Z_CH1930P / Math.Sqrt(Math.Pow(X_CH1930P, 2) + Math.Pow(Y_CH1930P, 2))),
RN_ETRS = BESSEL_GH / (Math.Sqrt(1 - E2_GRS * Math.Pow(Math.Sin(PHI_ETRS), 2)));
cont = true;
// iterate to tolerance
while (cont)
{
double PHI_ETRS_TMP = Math.Atan((Z_CH1930P / (Math.Sqrt(Math.Pow(X_CH1930P, 2) + Math.Pow(Y_CH1930P, 2)))) / (1 - (RN_ETRS * E2_GRS) / (RN_ETRS + 0))),
RN_ETRS_TMP = BESSEL_GH / (Math.Sqrt(1 - E2_GRS * Math.Pow(Math.Sin(PHI_ETRS_TMP), 2)));
if (Math.Abs(PHI_ETRS - PHI_ETRS_TMP) < TOLERANCE)
{
cont = false;
}
else
{
PHI_ETRS = PHI_ETRS_TMP;
RN_ETRS = RN_ETRS_TMP;
}
}
double LAMBDA_ETRS = Math.Atan(Y_CH1930P / X_CH1930P);
///// ellipsoid coordinates (ETRS89/CHTRS95) -> decimal coordinates (WGS84) /////
double lat = PHI_ETRS * 180 / Math.PI;
double lon = LAMBDA_ETRS * 180 / Math.PI;
I have a Histogram statistics bar chart with below data.
Count, HistogramBin
0, -1615.25
0, -1056.42
0, -497.48
1, 61.25
1, 620.05
1, 1178.92
0, 1737.76
0, 2296.59
I need to form Gauss curve based on above values. Could anyone guide me how to achieve the same.
I have written a function based on Wikipedia link: https://en.wikipedia.org/wiki/Gaussian_function
Our average is : 340.67
SD: Standard deviation: 488.98001098632812
private DataTable GenerateGaussTable1(DataTable histogramDataTable,
HistogramValueItem histogramValueDBItem)
{
double amplitude = (Average + 3 * Sigma) / 2;
double mean = Average;
double sd = Sigma;
DataTable dt = new DataTable();
dt.Columns.Add("x", typeof(float));
dt.Columns.Add("Y", typeof(float));
foreach (DataRow row in histogramDataTable.Rows)// top provided data
{
double x = Convert.ToDouble(row[1]) / 2;
double var1 = 1 / sd * Math.Sqrt(2 * 3.14);
double var2 = -0.5 * Math.Pow((x - mean)/sd, 2);
double var4= Math.Exp(var2);
double var5 = var1 * var4;
// Y = Amplitude * exp(-0.5 * ((X - Mean) / SD) ^ 2)
double y = var5;
dt.Rows.Add((float)x, (float)y);
}
return dt;
}
Here is my code:
double gauss(double x, double a, double b, double c)
{
var v1 = ( x - b) / (2d * c * c);
var v2 = -v1 * v1 / 2d;
var v3 = a * Math.Exp(v2);
return v3;
}
and:
private void button_Click(object sender, EventArgs e)
{
Series s1 = chart2.Series[0];
s1.ChartType = SeriesChartType.Line;
s1.Name = "Line";
Series s2 = chart2.Series.Add("Spline");
s2.ChartType = SeriesChartType.Spline;
double avg = 1.8;
double amp = 3;
double sd = 0.53;
List<double> xes = new List<double>
{ 0, 0, 0.05, 0.1, 0.4, 0.9, 1.3, 1.6, 2, 2.4, 2.8, 3.2, 4 };
foreach (var x in xes)
{
s1.Points.AddXY(x, gauss(x, amp, avg, sd));
s2.Points.AddXY(x, gauss(x, amp, avg, sd));
}
}
The math was taken from wikipedia
I think your SD is way too large to create a bell curve; try dividing by 10-100..! - Of course your SD actually is very large and so you really won't get a meaningful bell curve for those data..
I've tried your function, but it gives wrong curves,
The gauss function is wrong, why do you use "2d"?
Here the function :
so first, v1 = (x-b). Then v2 = (x-b)² / 2 c²
And finaly v3 = a exp (v2)
double gauss(double x, double a, double b, double c)
{
var v1 = (x - b);
var v2 = (v1 * v1) / (2 * (c*c));
var v3 = a * Math.Exp(-v2);
return v3;
}
After this fix, the curves are much better.
I am currently working with lat, lng between different locations and gathering the distance between two different points.
I succesfully get out the distance between different points where I currently sort them by the closest distance (in kilometer).
What I however am struggeling with is getting the correct name to the correct distance (that is being made out of the two different lat, lngs that is in the same row as the name).
So one row in the db looks like this (I have like 10 different rows with different locations with their correct latitude, longtitud):
Lat ----------- Lng ------- Name
45.032 --14.323112-----Iceland
I then take that Lat, Lng and with my code below I get the users current position lat, lng and i then compare the distance between the users current lat, lng position and out of it i have an int cointaining the kilometer to the closest distance.
How can I now connect that distance with the correct Name?
string thename;
string areaLat;
string areaLng;
double storeLat;
double storeLng;
double thedistancebetweenobjects;
async void loadareas()
{
var locator = CrossGeolocator.Current;
locator.DesiredAccuracy = 50;
var position = await CrossGeolocator.Current.GetPositionAsync(10000); //using a plugin that gives me the users current lat, lng.
List <int> theInts = new List<int>();
var getarea = await phpApi.getAreas(); //my db-callout
foreach (var myitems in getarea["results"])
{
thename = myitems["Name"].ToString();
areaLat = myitems["Lat"].ToString();
areaLng = myitems["Lng"].ToString();
storeLat = Double.Parse(areaLat, CultureInfo.InvariantCulture);
storeLng = Double.Parse(areaLng, CultureInfo.InvariantCulture);
thedistancebetweenobjects = distance(position.Latitude, position.Longitude,storeLat, storeLng, 'K'); //so the users current position lat + lng, and then the lat (storelat), and lng (storelng) i get from the db.
int someOtherInt = Convert.ToInt32(thedistancebetweenobjects); //converting double to int
theInts.Add(someOtherInt);
}
int TheSmallestInt = theInts.Min();
System.Diagnostics.Debug.WriteLine(TheSmallestInt);
//I succesfully get the smallest int out. But how do I connect this int with the correct name from the db?
}
And this is the function that takes the two different values of lat, lng and gives me the distance result in kilometer:
private double distance(double lat1, double lon1, double lat2, double lon2, char unit)
{
double theta = lon1 - lon2;
double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) +
Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
dist = Math.Acos(dist);
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
if (unit == 'K')
{
dist = dist * 1.609344;
}
else if (unit == 'N')
{
dist = dist * 0.8684;
}
return (dist);
}
private double deg2rad(double deg)
{
return (deg * Math.PI / 180.0);
}
private double rad2deg(double rad)
{
return (rad / Math.PI * 180.0);
}
string closest = string.Empty;
int dist = int.MaxValue;
foreach (var myitems in getarea["results"])
{
thename = myitems["Name"].ToString();
// do all of your calcs here
int someOtherInt = Convert.ToInt32(thedistancebetweenobjects);
// for every loc, check if it is closer than the previous loc
// if it is, save it's name.
if (someOtherInt < dist) {
dist = someOtherInt;
closest = thename;
}
theInts.Add(someOtherInt);
}
I see you're using the Haversine Formula which in this case is the correct way I would do it.
What I would do is then loop around your list of locations until you get to the closest one
List<double> distList = new List<double>(); //Create a list to store distance values
List<cityLocations> locList = new List<cityLocations>();
distList.Add(50000000); // default huge distance to compare against
foreach (var city in cityList)
{
//diff between longitudes
double theta = (double)ci.longitude - (double)ll.longitude;
//harversine implimentation
double dist = Math.Sin(Utils.deg2rad(ci.latitude)) * Math.Sin(Utils.deg2rad((double)ll.latitude)) + Math.Cos(Utils.deg2rad(ci.latitude)) * Math.Cos(Utils.deg2rad((double)ll.latitude)) * Math.Cos(Utils.deg2rad(theta));
dist = Math.Acos(dist);
dist = Utils.rad2deg(dist);
dist = dist * 60 * 1.1515;
if (dist < distList[0])
{
//always keep 1 record in list
distList.RemoveAt(0);
distList.Add(dist);
if (locList.Count > 0)
{
locList.RemoveAt(0);
}
locList.Add(new cityLocations { cityName = ll.cityName, latitude = ll.latitude, longitude = ll.longitude, country = ll.country });
}
}
Not the prettiest or efficent way of doing it by all means
The problem:
Given an input float (value), round another float (anotherValue) so it has the same significant figures as the first float (value).
What I have tried so far:
private static void Test()
{
var value = 0.12345f;
// ?? Strategy suggested by this post: http://stackoverflow.com/questions/3683718/is-there-a-way-to-get-the-significant-figures-of-a-decimal
var significantFigures = decimal.GetBits((decimal)value);
var anotherValue = 3.987654321;
// ERROR: Argument 2: cannot convert from int[] to int
var resultValue = (float) SetSignificantFigures((double)anotherValue, significantFigures.Length);
// Desired result: resultValue = 3.988
}
This is the definition of SetSignificantFigures:
// Function suggested by this post: http://stackoverflow.com/questions/374316/round-a-double-to-x-significant-figures
public static double SetSignificantFigures(double d, int digits)
{
double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
return scale * Math.Round(d / scale, digits);
}
Blocking point: since decimal.GetBits returns int[], I don't know how to proceed (or if it is the correct strategy).
If you just want the number of digits why don't you parse the string equivalent of the number
var value = 12.123f;
var str = value.ToString().Split('.');
int decimalCount = 0;
if (str.Count() == 2)
{
decimalCount = str[1].Length; // this will give you 3.
}
Replace this
var resultValue = SetSignificantDigits(anotherValue, significantDigits);
with this
var resultValue = SetSignificantDigits(anotherValue, significantDigits.Length);
Or try this
var significantDigits = value.ToString().Split('.')[1].Length; //perhaps replace '.' with ','
And in the SetSignificantDigits-Function replace this
return scale * Math.Round(d / scale, digits);
with this
return Math.Round(scale * (d / scale), digits);
Then it looks like this:
var value = 0.12345f;
var significantDigits = value.ToString().Split(',')[1].Length;
var anotherValue = 3.987654321;
var resultValue = SetSignificantDigits(anotherValue, significantDigits);
public static double SetSignificantDigits(double d, int digits)
{
double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
return Math.Round(scale * (d / scale), digits);
}
And the result is 3.98765