The function outputs the wrong value - c#

When ever I input these coordinates I get a wrong output.
static double ReadCoordinateFromConsole(double lat1, double lon1, double
lat2, double lon2)
{
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1);
var dLon = deg2rad(lon2 - lon1);
var a =
Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) *
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
var d = R * c; // Distance in km
return d;
}
static double deg2rad(double deg)
{
return deg * (Math.PI / 180);
}
Then in my function where I input some coordinates. 41.507483 -99.436554 38.504048 -98.315949. These coordinates should equal about 347 but instead, I get the output 7022,88 which is wrong and I have no clue as to why.
static double ReadDoubleFromConsole(string msg)
while (true)
{
Console.Write(msg);
string test = Console.ReadLine();
string[] words = test.Split(' ');
bool inputContainsNumber = Regex.IsMatch(words[0], #"^-*[0-9,\.]+$");
bool inputContainsNumber2 = Regex.IsMatch(words[1], #"^-*[0-9,\.]+$");
bool inputContainsNumber3 = Regex.IsMatch(words[2], #"^-*[0-9,\.]+$");
bool inputContainsNumber4 = Regex.IsMatch(words[3], #"^-*[0-9,\.]+$");
if(inputContainsNumber && inputContainsNumber2 && inputContainsNumber3
&& inputContainsNumber4)
{
double test1 = double.Parse(words[0]);
double test2 = double.Parse(words[1]);
double test3 = double.Parse(words[2]);
double test4 = double.Parse(words[3]);
double test5 = ReadCoordinateFromConsole(test1, test2, test3,
test4);
return test5;
}
Console.WriteLine("hmm, doesn't look correct - try again");
}
}

As you've been shown by the other answer your algorithm is correct - your parsing of the user input is wrong!
Here's the demonstration: http://rextester.com/IEEA93176
And the reason is, that in the culture that rextester runs in the , is used as a decimal separator - and I assume its the same in your environment. When you use double.parse("41.1234") in a culture expecting a , you get the value 411234 not 41.1234.
One fix is to force the culture
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
Live demo(working): http://rextester.com/KALRN89806

There's nothing wrong with your implementation of the Haversine formula - it generates the correct output.
The following prints 347.328348039426:
using System;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
Console.WriteLine(ReadCoordinateFromConsole(41.507483, -99.436554, 38.504048, -98.315949));
}
static double ReadCoordinateFromConsole(double lat1, double lon1, double
lat2, double lon2)
{
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1);
var dLon = deg2rad(lon2 - lon1);
var a =
Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) *
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
var d = R * c; // Distance in km
return d;
}
static double deg2rad(double deg)
{
return deg * (Math.PI / 180);
}
}
}
Therefore you must look elsewhere for your error. I suspect you're not feeding the correct values to the method - I recommend that you single-step your code in the debugger to determine what's happening.
Incidentally, is there any reason that you're not using the .Net GeoCoordinate class to calculate this? For example:
using System;
using System.Device.Location;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
var a = new GeoCoordinate(41.507483, -99.436554);
var b = new GeoCoordinate(38.504048, -98.315949);
Console.WriteLine(a.GetDistanceTo(b)/1000.0);
}
}
}
This prints 347.628192006498.

Related

Implementing Cardano method for a cubic equation solution

I'm trying to find real roots for a cubic equation defined by a set of four coefficients by using Cardano method as described here. The problem is, the roots found by my implementation do not actually work - testing by inserting them in the equation give a significant error (more than the required 10^-6). Is the algorithm implemented wrong, or the error is caused by something else, like rounding accuracy?
static double CubicRoot(double n)
{
return Math.Pow(Math.Abs(n), 1d / 3d) * Math.Sign(n);
}
public static List<double> SolveCubic(double A, double B = 0, double C = 0, double D = 0)
{
List<double> output = new List<double>();
if (A != 0)
{
double A1 = B / A;
double A2 = C / A;
double A3 = D / A;
double P = -((A1 * A1) / 3) + A2;
double Q = ((2.0 * A1 * A1 * A1) / 27.0) - ((A1 * A2) / 3.0) + A3;
double cubeDiscr = Q * Q / 4.0 + P * P * P / 27.0;
if (cubeDiscr > 0)
{
double u = CubicRoot(-Q / 2.0 + Math.Sqrt(cubeDiscr));
double v = CubicRoot(-Q / 2.0 - Math.Sqrt(cubeDiscr));
output.Add(u + v - (A1 / 3.0));
return output;
}
else if (cubeDiscr == 0)
{
double u = CubicRoot(-Q / 2.0);
output.Add(2u - (A1 / 3.0));
output.Add(-u - (A1 / 3.0));
}
else if (cubeDiscr < 0)
{
double r = CubicRoot(Math.Sqrt(-(P * P * P / 27.0)));
double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
output.Add(r * (Math.Cos(alpha / 3.0) + Math.Cos((6 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
output.Add(r * (Math.Cos((2 * Math.PI + alpha) / 3.0) + Math.Cos((4 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
output.Add(r * (Math.Cos((4 * Math.PI + alpha) / 3.0) + Math.Cos((2 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
}
}
return output;
}
A few things
Math.Sign will return zero on zero, which happens to be what you want in this case, but perhaps you are not so lucky with code or algorithm change.
You will have rounding issues and not execute cubeDiscr == 0 branch when you should. You may have rounding issues and execute the wrong > 0 and < 0 branch for the same reason. Test within a delta of zero instead (see below).
But the cubeDiscr == 0 branch is wrong because 1) you didn't calculate v and 2) 2u is an UInt32 with a value of 2, not 2*u.
Calculating alpha is wrong (see below)
(there may be more, but that's all I saw at a quick glance)
On calculating alpha:
double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
is not the same as
double alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0) // if q > 0 the angle becomes PI + alpha
alpha = Math.PI + alpha;
What's wrong with using the code included from that page?
public double Xroot(double a, double x)
{
double i = 1;
if (a < 0)
i = -1;
return (i * Math.Exp( Math.Log(a*i)/x));
}
public int Calc_Cardano() // solve cubic equation according to cardano
{
double p, q, u, v;
double r, alpha;
int res;
res = 0;
if (a1 != 0)
{
a = b / a1;
b = c / a1;
c = d / a1;
p = -(a * a / 3.0) + b;
q = (2.0 / 27.0 * a * a * a) - (a * b / 3.0) + c;
d = q * q / 4.0 + p * p * p / 27.0;
if (Math.Abs(d) < Math.Pow(10.0, -11.0))
d = 0;
// 3 cases D > 0, D == 0 and D < 0
if (d > 1e-20)
{
u = Xroot(-q / 2.0 + Math.Sqrt(d), 3.0);
v = Xroot(-q / 2.0 - Math.Sqrt(d), 3.0);
x1.real = u + v - a / 3.0;
x2.real = -(u + v) / 2.0 - a / 3.0;
x2.imag = Math.Sqrt(3.0) / 2.0 * (u - v);
x3.real = x2.real;
x3.imag = -x2.imag;
res = 1;
}
if (Math.Abs(d) <= 1e-20)
{
u = Xroot(-q / 2.0, 3.0);
v = Xroot(-q / 2.0, 3.0);
x1.real = u + v - a / 3.0;
x2.real = -(u + v) / 2.0 - a / 3.0;
res = 2;
}
if (d < -1e-20)
{
r = Math.Sqrt(-p * p * p / 27.0);
alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0) // if q > 0 the angle becomes PI + alpha
alpha = Math.PI + alpha;
x1.real = Xroot(r, 3.0) * (Math.Cos((6.0 * Math.PI - alpha) / 3.0) + Math.Cos(alpha / 3.0)) - a / 3.0;
x2.real = Xroot(r, 3.0) * (Math.Cos((2.0 * Math.PI + alpha) / 3.0) + Math.Cos((4.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
x3.real = Xroot(r, 3.0) * (Math.Cos((4.0 * Math.PI + alpha) / 3.0) + Math.Cos((2.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
res = 3;
}
}
else
res = 0;
return res;
}

Recursive way to calculate distance among co-ordinates

I am able to calculate the distance between two points(Latitude and Longtitude) by using the below function
public static double GetDistance(double lat1, double lng1, double lat2, double lng2)
{
double sLatitude = 48.672309;
double sLongitude = 15.695585;
double eLatitude = 48.237867;
double eLongitude = 16.389477;
var coordinate1 = new GeoCoordinate(lat1, lng1);
var coordinate2 = new GeoCoordinate(lat2, lng2);
var resultInMeter = coordinate1.GetDistanceTo(coordinate2); //in meters
//convert to KM : 1 meter = 0.001 KM
var resultInKM = resultInMeter * 0.001; //in KM's
return resultInKM ;
}
this works as under
double lat1 = 48.672309;
double lng1 = 15.695585;
double lat2 = 48.237867;
double lng2 = 16.389477;
var distance = GetDistance(lat1,lng1,lat2,lng2);
Now say I have a collection of Latitudes and Longtitudes as under
double[,] arrLatLong = new double[,]
{
{22.57891304, 88.34285622},
{22.54398196, 88.38221001},
{22.58277011, 88.38303798},
{22.59950095, 88.39352995},
{22.59887647, 88.32905243},
};
How to calculate the total distance? Is there any recursive way?
Trying to use recursion will only make this problem more difficult. Plain old iteration is more than sufficient.
double dist = 0.0;
for (int i = 0; i < arrLatLong.GetLength(0) - 1; i++)
{
dist += GetDistance(
arrLatLong[i, 0],
arrLatLong[i, 1],
arrLatLong[i+1, 0],
arrLatLong[i+1, 1]);
}
If you absolutely must use a recursive method, this should do the trick:
double GetDistanceRecursive(double[,] coords, int idx)
{
if (idx + 1 >= coords.Length)
return 0.0;
double dist = GetDistance(
coords[idx, 0],
coords[idx, 1],
coords[idx+1, 0],
coords[idx+1, 1]);
dist += GetDistanceRecursive(coords, idx + 1);
return dist;
}

Levy distribution in C#

I implemented my own Levy distribution for a program but it seems that I get wrong values from it and after spending some time, I still cannot find where I did wrong.
This is used to generate random uniform numbers (double here) as well as the normal.
public class RandomProportional : Random
{
protected override double Sample()
{
return Math.Sqrt(base.Sample());
}
public override double NextDouble()
{
return (double)(Sample());
}
public double NextStdNormal()
{
double d1 = NextDouble();
double d2 = NextDouble();
return (double)(Math.Sqrt(-2.0 * Math.Log(d1)) * Math.Sin(2.0 * Math.PI * d2));
}
}
Here is how I calculate Sigma for the Levy distribution:
static double sigma_lev = Math.Pow(var_class.Gamma(1.0 + var_class.beta) * Math.Sin(Math.PI * var_class.beta / 2.0) / (var_class.Gamma((1.0 + var_class.beta) / 2.0) * var_class.beta * Math.Pow(2.0, (var_class.beta - 1.0) / 2.0)), 1.0 / var_class.beta);
Where var_class.Gamma(double x) is calculated using the code found here : Gamma function
Where var_class.beta = 1.5 (all the time)
The actual function:
public static double[] levy(int n)
{
RandomProportional randObj = new RandomProportional();
double[] step = new double[n];
for (int i = 0; i < n; ++i)
{
step[i] = var_class.gam * randObj.NextStdNormal() * sigma_lev / Math.Pow(Math.Abs(randObj.NextStdNormal()), 1.0 / var_class.beta);
}
return step;
}
Where var_class.gam
0.075
I'm using this formula for implementation:
Some examples of the results I get:
From there I guess I did something wrong but still cannot find what.

Quadratic equation formula

I am trying to make a program that calculates the answer of a quadratic equation with the general formula but i am encountering a few errors.
The way I have my Windows Form Application set up it asks for a, b, and c, and substitutes them in the general formula.
I have 3 text boxes, one for each value of a, b and c and one more for the answer, and it is supposed to work with a button I called "calculate".
My problem is that when I try something other than a perfect trinomial square, the answer comes up as NaN.
Here is some of the code I have:
private void textBox1_TextChanged(object sender, EventArgs e)
{
a = Double.Parse(textBox1.Text);
}
^ This is how I am assigning values to the variables
double sqrtpart = b * b - 4 * a * c;
answer = (b + Math.Sqrt(sqrtpart)) / 2 * a;
textBox4.Text = answer.ToString();
group 2a and make sure values are valid (b^2 > 4ac)
answer = (b + Math.Sqrt(sqrtpart)) / (2 * a);
First, make sure that 4ac < b². The quadratic formula also utilizes the ± sign, so we must make two separate variables to hold the two different answers.
double sqrtpart = (b * b) - (4 * a * c);
answer1 = ((-1)*b + Math.Sqrt(sqrtpart)) / (2 * a);
answer2 = ((-1)*b - Math.Sqrt(sqrtpart)) / (2 * a);
textBox4.Text = answer1.ToString() + " and " + answer2.ToString();
If you want your calculator to be able to compute complex numbers try the following
string ans = "";
double root1 = 0;
double root2 = 0;
double b = 0;
double a = 0;
double c = 0;
double identifier = 0;
a =Convert.ToDouble(Console.ReadLine());
b = Convert.ToDouble(Console.ReadLine());
c = Convert.ToDouble(Console.ReadLine());
identifier = b * b - (4 * a * c);
if (identifier > 0)
{
root1 = (-b+(Math.Sqrt(identifier)/(2*a)));
root2 = (-b - (Math.Sqrt(identifier) / (2 * a)));
string r1 = Convert.ToString(root1);
string r2 = Convert.ToString(root2);
ans = "Root1 =" + r1 + "Root2 = " + r2;
Console.WriteLine(ans);
}
if (identifier < 0)
{
double Real = (-b / (2 * a));
double Complex = ((Math.Sqrt((identifier*(-1.00))) / (2 * a)));
string SReal = Convert.ToString(Real);
string SComplex = Convert.ToString(Complex);
ans = "Roots = " + SReal + "+/-" + SComplex + "i";
Console.WriteLine(ans);
}
if (identifier == 0)
{
root1 = (-b / (2 * a));
string Root = Convert.ToString(root1);
ans = "Repeated roots : " + Root;
}
Use the following code:
using System;
using System.Collections.Generic;
using System.Text;
namespace SoftwareAndFinance
{
class Math
{
// quadratic equation is a second order of polynomial equation in a single variable
// x = [ -b +/- sqrt(b^2 - 4ac) ] / 2a
public static void SolveQuadratic(double a, double b, double c)
{
double sqrtpart = b * b - 4 * a * c;
double x, x1, x2, img;
if (sqrtpart > 0)
{
x1 = (-b + System.Math.Sqrt(sqrtpart)) / (2 * a);
x2 = (-b - System.Math.Sqrt(sqrtpart)) / (2 * a);
Console.WriteLine("Two Real Solutions: {0,8:f4} or {1,8:f4}", x1, x2);
}
else if (sqrtpart < 0)
{
sqrtpart = -sqrtpart;
x = -b / (2 * a);
img = System.Math.Sqrt(sqrtpart) / (2 * a);
Console.WriteLine("Two Imaginary Solutions: {0,8:f4} + {1,8:f4} i or {2,8:f4} + {3,8:f4} i", x, img, x, img);
}
else
{
x = (-b + System.Math.Sqrt(sqrtpart)) / (2 * a);
Console.WriteLine("One Real Solution: {0,8:f4}", x);
}
}
static void Main(string[] args)
{
// 6x^2 + 11x - 35 = 0
SolveQuadratic(6, 11, -35);
// 5x^2 + 6x + 1 = 0
SolveQuadratic(5, 6, 1);
// 2x^2 + 4x + 2 = 0
SolveQuadratic(2, 4, 2);
// 5x^2 + 2x + 1 = 0
SolveQuadratic(5, 2, 1);
}
}
}
Here is the original source.
A little late, but here is my solution:
using System;
using System.Collections.Generic;
namespace MyFunctions
{
class Program
{
static void Main(string[] args)
{
printABCSolution(1, -3, 4);
printABCSolution(1, -4, 4);
printABCSolution(1, -5, 4);
printABCSolution(9, 30, 25);
printABCSolution(9, -15, 25);
Console.ReadKey();
}
private static void printABCSolution(double a, double b, double c)
{
Console.WriteLine($"Solution a={a} b={b} c={c}");
var solution = ABCMath.ABCFormula(a, b, c);
Console.WriteLine($"# Solutions found: {solution.Count}");
solution.ForEach(x => Console.WriteLine($"x={x}"));
}
}
public static class ABCMath
{
public static List<double> ABCFormula(double a, double b, double c)
{
// Local formula
double formula(int plusOrMinus, double d) => (-b + (plusOrMinus * Math.Sqrt(d))) / (2 * a);
double discriminant = b * b - 4 * a * c;
List<double> result = new List<double>();
if (discriminant >= 0)
{
result.Add(formula(1, discriminant));
if (discriminant > 0)
result.Add(formula(-1, discriminant));
}
return result;
}
}
}
Output:
Solution a=1 b=-3 c=4
# Solutions found: 0
Solution a=1 b=-4 c=4
# Solutions found: 1
x=2
Solution a=1 b=-5 c=4
# Solutions found: 2
x=4
x=1
Solution a=9 b=30 c=25
# Solutions found: 1
x=-1,66666666666667
Solution a=9 b=-15 c=25
# Solutions found: 0
Here is code:
using System;
namespace RoomPaintCost
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("This program applies quadratic formula to Ax^2+Bx+C=0 problems");
double[] ABC= getABC();
double[] answerArray=QuadFormula(ABC);
Console.WriteLine("Positive answer: {0:0.000} \n Negative answer: {1:0.000}", answerArray[0], answerArray[1]);
}
//Form of Ax^2 + Bx + C
//Takes array of double containing A,B,C
//Returns array of positive and negative result
public static double[] QuadFormula(double[] abc)
{
NotFiniteNumberException e = new NotFiniteNumberException();
try
{
double a = abc[0];
double b = abc[1];
double c = abc[2];
double discriminant = ((b * b) - (4 * a * c));
if (discriminant < 0.00)
{
throw e;
}
discriminant = (Math.Sqrt(discriminant));
Console.WriteLine("discriminant: {0}",discriminant);
double posAnswer = ((-b + discriminant) / (2 * a));
double negAnswer = ((-b - discriminant) / (2 * a));
double[] xArray= { posAnswer,negAnswer};
return xArray;
}
catch (NotFiniteNumberException)
{
Console.WriteLine("Both answers will be imaginary numbers");
double[] xArray = { Math.Sqrt(-1), Math.Sqrt(-1) };
return xArray;
}
}
public static double[] getABC()
{
Console.Write("A=");
string sA = Console.ReadLine();
Console.Write("B=");
string sB = Console.ReadLine();
Console.Write("C=");
string sC = Console.ReadLine();
int intA = Int32.Parse(sA);
int intB = Int32.Parse(sB);
int intC = Int32.Parse(sC);
double[] ABC = { intA, intB, intC };
return ABC;
}
}
}

Conversion from NZMG to latitude and longitude

I want to convert NEW ZEALAND MAP GRID coordinates i.e. Northing and Easting into WGS84 coordinates i.e. Latitude and Longitude.
I have searched on internet but there is no proper explanation of how to do this, or an online calculator to do this.
My final goal is to write a program in C# or JAVA , which will convert NZMG coordinates into WGS84 coordinates.
Please note: This implementation does not convert with pinpoint accuracy.
Here is a c# implementation I put together for converting from NZMG to NZGD1949 and then to datum shift from NZGD1949 to NZGD2000. NZGD2000 coordinates are apparently compatible with WGS84 source: http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/wgs84-nzgd2000. I have added a 'useStaticCorrection' parameter to the datum shift method because I found my results were consistently off by approximately 40 meters in latitude - this varies depending on the input coordinates. This may be from a mistake in the logic but, as far as i can tell, it appears all correct (as by the sources referenced).
Sources:
Constants and coefficients
http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/projection-conversions/new-zealand-map-grid
Datum info.
http://www.linz.govt.nz/regulatory/25000
NZMG -> NZGD1949 formula
http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/projection-conversions/new-zealand-map-grid
NZ1949 -> NZGD2000 differences
http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/nzgd1949-nzgd2000/three-seven
Equivilent of a Three parameter shift.
http://sas2.elte.hu/tg/eesti_datums_egs9.htm
//Lat / Long fudge amounts http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/datum-transformation-examples
public class Pair<T, U>
{
public Pair()
{
}
public Pair(T first, U second)
{
this.First = first;
this.Second = second;
}
public T First { get; set; }
public U Second { get; set; }
};
using System.Numerics;
public class NZMGConverter
{
//Constants and coefficients
//Source: http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/projection-conversions/new-zealand-map-grid
private static Double N0 = 6023150;
private static Double E0 = 2510000;
private static Double NZGD1949a = 6378388;
private static Double NZMGLatOrigin = -41;
private static Double NZMGLongOrigin = 173;
//some of these are unused but are included for completeness. (unused arrays are for converting from wgs84 / nzgd2000 -> nzmg).
private static Double[] A = new Double[] { 0.6399175073, -0.1358797613, 0.0632944090, -0.0252685300, 0.0117879000, -0.0055161000, 0.0026906000, -0.0013330000, 0.0006700000, -0.0003400000 };
private static Complex[] B = new Complex[] { new Complex(0.7557853228, 0.0), new Complex(0.249204646, 0.003371507), new Complex(-0.001541739, 0.04105856), new Complex(-0.10162907, 0.01727609), new Complex(-0.26623489, -0.36249218), new Complex(-0.6870983, -1.1651967) };
private static Complex[] C = new Complex[] { new Complex(1.3231270439, 0.0), new Complex(-0.577245789, -0.007809598), new Complex(0.508307513, -0.112208952), new Complex(-0.15094762, 0.18200602), new Complex(1.01418179, 1.64497696), new Complex(1.9660549, 2.5127645) };
private static Double[] D = new Double[] { 1.5627014243, 0.5185406398, -0.0333309800, -0.1052906000, -0.0368594000, 0.0073170000, 0.0122000000, 0.0039400000, -0.0013000000 };
//Datum info.
//Source: http://www.linz.govt.nz/regulatory/25000
private static Double NZGD1949f = 0.003367003;
//Double NZGD1949InverseFlattening = 297;
private static Double NZGD1949e2 = (2 * 0.003367003) - Math.Pow(0.003367003, 2);
private static Double NZGD2000a = 6378137;
private static Double NZGD2000f = 0.003352811;
//Double NZGD2000InverseFlattening = 298.2572221;
private static Double NZGD2000e2 = (2 * 0.003352811) - Math.Pow(0.003352811, 2);
//NZ1949 -> NZGD2000 differences
//Source: http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/nzgd1949-nzgd2000/three-seven
private static Double differenceX = 54.4;
private static Double differenceY = -20.1;
private static Double differenceZ = 183.1;
//Source: http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/projection-conversions/new-zealand-map-grid
public static Pair<Double, Double> convertToNZGD1949(Double nzmgX, Double nzmgY)
{
Pair<Double, Double> result = null;
Complex z = new Complex((nzmgY - N0) / NZGD1949a, (nzmgX - E0) / NZGD1949a);
Complex theta0 = new Complex();
for (int i = 0; i < C.Length; ++i) { theta0 += Complex.Multiply(C[i], Complex.Pow(z, i + 1)); }
Double deltaLatWtheta0 = 0;
for (int i = 0; i < D.Length; ++i) { deltaLatWtheta0 += D[i] * Math.Pow(theta0.Real, (i + 1)); }
result = new Pair<double, double>();
result.First = NZMGLatOrigin + (Math.Pow(10, 5) / 3600) * deltaLatWtheta0; //NZGD1949 lat
result.Second = NZMGLongOrigin + 180 / Math.PI * theta0.Imaginary; //NZGD1949 long
return result;
}
//Equivilent of a Three parameter shift.
//Source: http://sas2.elte.hu/tg/eesti_datums_egs9.htm
public static Pair<Double, Double> datumShiftNZGD1949toNZGD2000(Double nzgd1949Lat, Double nzgd1949Long, Boolean useStaticCorrection)
{
Pair<Double, Double> result = null;
Double latInRaidans = nzgd1949Lat * (Math.PI / 180);
Double lngInRaidans = nzgd1949Long * (Math.PI / 180);
Double n = NZGD1949a / Math.Sqrt((1 - NZGD1949e2 * Math.Pow(Math.Sin(latInRaidans), 2)) * (3 / 2));
Double m = NZGD1949a * ((1 - NZGD1949e2) / (1 - NZGD1949e2 * Math.Pow(Math.Sin(latInRaidans), 2) * (3 / 2)));
Double difF = NZGD2000f - NZGD1949f;
Double difA = NZGD2000a - NZGD1949a;
Double changeInLat = (-1 * differenceX * Math.Sin(latInRaidans) * Math.Cos(lngInRaidans)
- differenceY * Math.Sin(latInRaidans) * Math.Sin(lngInRaidans)
+ differenceZ * Math.Cos(latInRaidans) + (NZGD1949a * difF + NZGD1949f * difA) * Math.Sin(2 * latInRaidans))
/ (m * Math.Sin(1));
Double changeInLong = (-1 * differenceX * Math.Sin(lngInRaidans) + differenceY * Math.Cos(lngInRaidans)) / (n * Math.Cos(latInRaidans) * Math.Sin(1));
result = new Pair<Double, Double>();
result.First = (latInRaidans + changeInLat) * (180 / Math.PI);
result.Second = (lngInRaidans + changeInLong) * (180 / Math.PI);
if (useStaticCorrection)
{
//Difference found by 3-parameter test values # http://www.linz.govt.nz/data/geodetic-system/coordinate-conversion/geodetic-datum-conversions/datum-transformation-examples
Double staticLatCorrection = 0.00037432;
Double staticLongCorrection = -0.00003213;
result.First -= staticLatCorrection;
result.Second += staticLongCorrection;
}
return result;
}
}
NZMG to Latitude/Longitude using Swift. Thanks to DavidG's post above. Fixed 40m latitude error.
import Foundation
import Darwin
class NZMGConverter {
private let easting: Double!
private let northing: Double!
private var z: Complex! = nil
private let N0: Double = 6023150; //false Northing
private let E0: Double = 2510000; //false Easting
private let NZMGLatOrigin: Double = -41;
private let NZMGLongOrigin: Double = 173;
private let c: [Complex] = [Complex(real: 1.3231270439, imag: 0.0), Complex(real: -0.577245789, imag: -0.007809598), Complex(real: 0.508307513, imag: -0.112208952), Complex(real: -0.15094762, imag: 0.18200602), Complex(real: 1.01418179, imag: 1.64497696), Complex(real: 1.9660549, imag: 2.5127645)]
private let b: [Complex] = [Complex(real: 0.7557853228, imag: 0.0), Complex(real: 0.249204646, imag: 0.003371507), Complex(real: -0.001541739, imag: 0.04105856), Complex(real: -0.10162907, imag: 0.01727609), Complex(real: -0.26623489, imag: -0.36249218), Complex(real: -0.6870983, imag: -1.1651967)]
private let d: [Double] = [1.5627014243, 0.5185406398, -0.0333309800, -0.1052906000, -0.0368594000, 0.0073170000, 0.0122000000, 0.0039400000, -0.0013000000]
//Datum info.
private let NZGD1949f: Double = 0.003367003;
//Double NZGD1949InverseFlattening = 297;
private let NZGD1949e2: Double = (2 * 0.003367003) - pow(0.003367003, 2);
private let NZGD1949a: Double = 6378388; //Semi-major axis
private let NZGD2000a: Double = 6378137;
private let NZGD2000f: Double = 0.003352811;
//Double NZGD2000InverseFlattening = 298.2572221;
private let NZGD2000e2: Double = (2 * 0.003352811) - pow(0.003352811, 2);
//NZ1949 -> NZGD2000 differences
private let differenceX: Double = 54.4;
private let differenceY: Double = -20.1;
private let differenceZ: Double = 183.1;
init(easting: Double, northing: Double) {
self.easting = easting
self.northing = northing
}
func nzmgToNZGD1949() -> (latitude: Double, longitude: Double) {
z = Complex(real: (northing - N0) / NZGD1949a, imag: (easting - E0) / NZGD1949a)
let theta0 = thetaZero(z)
let theta: Complex = thetaSuccessive(theta0, i: 0)
let latlong = calcLatLong(theta)
print(latlong)
return datumShift(latlong.0, longitude: latlong.1)
}
func thetaZero(z: Complex) -> Complex {
var theta0 = Complex()
for var i = 0; i < c.count; i++ {
theta0 = theta0 + (c[i] * (z ^ (i + 1)))
//print(theta0)
}
return theta0
}
func thetaSuccessive(theta0: Complex, i: Int) -> Complex {
var numerator: Complex = Complex()
var denominator: Complex = Complex()
for var n = 1; n < b.count; n++ {
numerator = numerator + (Complex(real: n) * b[n] * (theta0 ^ (n + 1)))
}
numerator = z + numerator
for var n = 0; n < b.count; n++ {
denominator = denominator + (Complex(real: n + 1) * b[n] * (theta0 ^ n))
}
let theta = numerator / denominator
if i == 2 {
return theta
} else {
return thetaSuccessive(theta, i: i + 1)
}
}
func calcLatLong(theta: Complex) -> (latitude: Double, longitude: Double) {
let deltaPsi = theta.real
let deltaLambda = theta.imag
var deltaPhi: Double = 0
for var n = 0; n < d.count; n++ {
deltaPhi += d[n] * pow(deltaPsi, Double(n + 1))
}
let lat: Double = NZMGLatOrigin + ((pow(10, 5) / 3600) * deltaPhi)
let long: Double = NZMGLongOrigin + ((180 / M_PI) * deltaLambda)
return (latitude: lat, longitude: long)
}
func datumShift(latitude: Double, longitude: Double) -> (latitude: Double, longitude: Double) {
let latInRadians = latitude * (M_PI / 180)
let lngInRadians = longitude * (M_PI / 180)
let v = NZGD1949a / (sqrt(1 - (NZGD1949e2 * pow(sin(latInRadians), 2))))
var x_cart = v * cos(latInRadians) * cos(lngInRadians)
var y_cart = v * cos(latInRadians) * sin(lngInRadians)
var z_cart = (v * (1 - NZGD1949e2)) * sin(latInRadians)
x_cart += differenceX
y_cart += differenceY
z_cart += differenceZ
print(x_cart)
print(y_cart)
print(z_cart)
let p = sqrt(pow(x_cart, 2) + pow(y_cart, 2))
let r = sqrt(pow(p, 2) + pow(z_cart, 2))
let mu = atan((z_cart / p) * ((1 - NZGD2000f) + ((NZGD2000e2 * NZGD2000a) / r)))
let num = (z_cart * (1 - NZGD2000f)) + (NZGD2000e2 * NZGD2000a * pow(sin(mu), 3))
let denom = (1 - NZGD2000f) * (p - (NZGD2000e2 * NZGD2000a * pow(cos(mu), 3)))
var lat = atan(num / denom)
var long = atan(y_cart / x_cart)
print(long)
lat = (180 / M_PI) * lat
long = 180 + (180 / M_PI) * long
return (latitude: lat, longitude: long)
}
}
}
Proj4j provides this functionality:
It is one of the few universal coordinate conversions libs.
http://trac.osgeo.org/proj4j/
Then you have to find the proj4 string that defines the new zeal and datum description:
Depending if you need th eold or the new grid, you need the new zealand datum shift grids,+
which are included in proj4.
As a first step find exactly the name of the datum that you need:
NZGD49 or NZGD2000 ?

Categories

Resources