I've read several links discussing storing 2 or 3 floats in one float. Here's an example:
Storing two float values in a single float variable
and another:
http://uncommoncode.wordpress.com/2012/11/07/float-packing-in-shaders-encoding-multiple-components-in-one-float/
and yet another:
decode rgb value to single float without bit-shift in glsl
I've seen others but all of them use the same principle. If you want to encode x and y, they multiply y by some factor and then add x to it. Well this makes since on paper, but I don't understand how in the world it can work when stored to a floating value. Floating values only have 7 significant digits. If you add a big number and a small number, the small number is just truncated and lost. The precision only shows the value of the big number.
Since everyone seems to prescribe the same method, I tried it myself and it did exactly what I thought it would do. When I decoded the numbers, the number that wasn't multiplied turned out as 0.0. It was completely lost in the encoded float.
Here's an example of some MaxScript I tried to test it:
cp = 256.0 * 256.0
scaleFac = 16777215
for i = 1 to 20 do (
for j = 1 to 20 do (
x = (i as float / 20.01f) as float;
y = (j as float / 20.01f) as float;
xScaled = x * scaleFac;
yScaled = y * scaleFac;
f = (xScaled + yScaled * cp) as float
print ("x[" + xScaled as string + "] y[" + yScaled as string + "]" + " e[" + f as string + "]")
dy = floor(f / cp)
dx = (f - dy * cp)
print ("x[" + dx as string + "] y[" + dy as string + "]" + " e[" + f as string + "]")
)
)
dx is 0.0 everytime. Can anyone shed some light on this? NOTE: It doesn't matter whether I make cp = 128, 256, 512 or whatever. It still gives me the same types of results.
This method works for storing two integers. You're effectively converting your floating point numbers to large integers by multiplying by scaleFac, which is good, but it would be better to make it explicit with int(). Then you need to make sure of two things: cp is greater than the largest number you're working with (scaleFac), and the square of cp is small enough to fit into a floating point number without truncation (about 7 digits for a single precision float).
Here is a working code in C to pack two floats into one float and unpack them.
You should change scaleFactor and cp parameters as according to your possible value ranges (yourBiggestNumber * scaleFactor < cp). It is a precision battle. Try printing a few results to find good values for your case. The example below allows floats in [0 to 1) range.
#include <math.h>
/* yourBiggestNumber * scaleFactor < cp */
double scaleFactor = 65530.0;
double cp = 256.0 * 256.0;
/* packs given two floats into one float */
float pack_float(float x, float y) {
int x1 = (int) (x * scaleFactor);
int y1 = (int) (y * scaleFactor);
float f = (y1 * cp) + x1;
return f;
}
/* unpacks given float to two floats */
int unpack_float(float f, float* x, float* y){
double dy = floor(f / cp);
double dx = f - (dy * cp);
*y = (float) (dy / scaleFactor);
*x = (float) (dx / scaleFactor);
return 0;
}
It will work only if your individual floats are small enough to be packed into one float location.
So you can pack 2 numbers by "dividing" this into two to store 2 numbers that can be represented by half the space.
Here's the code I use for packing and unpacking floats. It works by packing first float (0..1) into the first four bytes of a 8-bit (0..256) number, and the next float into the remaining 4 bits. The resulting numbers have 16 possible combinations each (2^4). In some cases this is good enough:
private float PackFloatsInto8Bits( float v1, float v2 )
{
var a = Mathf.Round( v1 * 15f );
var b = Mathf.Round( v2 * 15f );
var bitShiftVector = new Vector2( 1f/( 255f/16f ), 1f/255f );
return Vector2.Dot( new Vector2( a, b ), bitShiftVector );
}
private Vector2 UnpackFloatsFrom8Bits( float input )
{
float temp = input * 15.9375f;
float a = Mathf.Floor(temp) / 15.0f;
float b = Frac( temp ) * 1.0667f;
return new Vector2(a, b);
}
Related
I'm trying to calculate the given tile a node is in from OpenStreetMap so that I can manually add nodes directly into the current_nodes table. If I want to add a new node at latitude X and longitude Y, I can then calculate the 'tile' value that's needed before inserting into the current_nodes table.
I'm trying to convert the current PostgreSQL function into C# but it's not giving the output expected. I'm not sure I've converted it correctly or if I'm missing something.
//maptile_for_point(scaled_lat bigint, scaled_lon bigint, zoom integer)
DECLARE
lat CONSTANT DOUBLE PRECISION := scaled_lat / 10000000.0;
lon CONSTANT DOUBLE PRECISION := scaled_lon / 10000000.0;
zscale CONSTANT DOUBLE PRECISION := 2.0 ^ zoom;
pi CONSTANT DOUBLE PRECISION := 3.141592653589793;
r_per_d CONSTANT DOUBLE PRECISION := pi / 180.0;
x int4;
y int4;
BEGIN
-- straight port of the C code. see db/functions/maptile.c
x := floor((lon + 180.0) * zscale / 360.0);
y := floor((1.0 - ln(tan(lat * r_per_d) + 1.0 / cos(lat * r_per_d)) / pi) * zscale / 2.0);
RETURN (x << zoom) | y;
END;
So far I have:
public const double pi = 3.141592653589793;
public const double r_per_d = pi / 180.0;
public static long GetTile(double scaled_lat, double scaled_lon, int zoom)
{
double lat = scaled_lat / 10000000.0;
double lon = scaled_lon / 10000000.0;
return GetTile(lat, lon, zoom);
}
public static long GetTile(long lat, long lon, int zoom)
{
double zscale = Math.Pow(2.0, zoom);
long x = (long)Math.Floor((lon + 180.0) * zscale / 360.0);
long y = (long)Math.Floor((1.0 - Math.Log(Math.Tan(lat * r_per_d) + 1.0 / Math.Cos(lat * r_per_d)) / pi) * zscale / 2.0);
return (x << zoom) | y;
}
I call the function but loop through possible zoom values but none of them are matching the expected output.
Using data that's already in the table (526868344, -18000386) it should return 2062343892.
for (int i = 0; i < int.MaxValue; i++)
{
var r1 = NodeTileHelper.GetTile(526868344, -18000386, i);
if (r1 == 2062343892)
{
;
}
}
It looks like the problem might be how the method overloads are defined.
The following snippet of code is going to call the GetTile(long, long, int) overload, so it may not account for the scaling that is present in the other method.
NodeTileHelper.GetTile(526868344, -18000386, i);
In addition the following code in the scaling method recursively calls itself, because of the variable declarations. In order to account for the different method, you will need to convert lat and lon to long types.
double lat, lon;
// ...
return GetTile(lat, lon, zoom);
Given the original stored procedure definition, I think it would make more sense to consolidate this into one method instead of using overloads. Alternately, I think using different names for the methods would make it clearer than simply using the difference between parameter types (e.g,. long vs. double).
I would like to find a way to make higher precision calculations in a compute shader using multiple floats to represent a single number.
My goal is a mandelbrot renderer where you can zoom deeper than the usual float precision would allow. Performance isn't too crucial, since it will only be calculated once and stored in a texture.
I'm testing the algorithms in C#. So far I managed to add 2 numbers with sufficient accuracy:
double a = Math.PI * 10;
double b = Math.E * .1;
double c = a + b;
float ax = (float)a;
float ay = (float)(a - ax);
float bx = (float)b;
float by = (float)(b - bx);
float cx = ax + bx;
float cy = 0;
if (Math.Abs(ax) > Math.Abs(bx)) {
cy = -cx + ax + bx + ay + by;
} else {
cy = -cx + bx + ax + by + ay;
}
// cx + (double)cy =
// 0 10000000011 1111101100000001000010110001011110001010110010100000 | 31.68775471874380
// c = 0 10000000011 1111101100000001000010110001011110001010110010101001 | 31.68775471874380
Extending this algorithm to 3 floats per number makes the sum exactly equal the double value.
Can you think of a way to multiply two of these numbers? And if you're having fun, division or inverting (to then multiply) a number maybe. The other calculations can be broken down into mul and add.
(I'm also open to other methods of getting a high zoom image of the mandelbrot set.)
Thanks in advance!
I'm attempting to convert from state vectors (position and speed) into Kepler elements, however I'm running into problems where a negative velocity or position will give me wrong results when trying to calculate true anomaly.
Here are the different ways I'm trying to calculate the True Anomaly:
/// <summary>
/// https://en.wikipedia.org/wiki/True_anomaly#From_state_vectors
/// </summary>
public static double TrueAnomaly(Vector4 eccentVector, Vector4 position, Vector4 velocity)
{
var dotEccPos = Vector4.Dot(eccentVector, position);
var talen = eccentVector.Length() * position.Length();
talen = dotEccPos / talen;
talen = GMath.Clamp(talen, -1, 1);
var trueAnomoly = Math.Acos(talen);
if (Vector4.Dot(position, velocity) < 0)
trueAnomoly = Math.PI * 2 - trueAnomoly;
return trueAnomoly;
}
//sgp = standard gravitational parameter
public static double TrueAnomaly(double sgp, Vector4 position, Vector4 velocity)
{
var H = Vector4.Cross(position, velocity).Length();
var R = position.Length();
var q = Vector4.Dot(position, velocity); // dot product of r*v
var TAx = H * H / (R * sgp) - 1;
var TAy = H * q / (R * sgp);
var TA = Math.Atan2(TAy, TAx);
return TA;
}
public static double TrueAnomalyFromEccentricAnomaly(double eccentricity, double eccentricAnomaly)
{
var x = Math.Sqrt(1 - Math.Pow(eccentricity, 2)) * Math.Sin(eccentricAnomaly);
var y = Math.Cos(eccentricAnomaly) - eccentricity;
return Math.Atan2(x, y);
}
public static double TrueAnomalyFromEccentricAnomaly2(double eccentricity, double eccentricAnomaly)
{
var x = Math.Cos(eccentricAnomaly) - eccentricity;
var y = 1 - eccentricity * Math.Cos(eccentricAnomaly);
return Math.Acos(x / y);
}
Edit: another way of doing it which Spectre pointed out:
public static double TrueAnomaly(Vector4 position, double loP)
{
return Math.Atan2(position.Y, position.X) - loP;
}
Positions are all relative to the parent body.
These functions all agree if position.x, position.y and velocity.y are all positive.
How do I fix these so that I get a consistent results when position and velocity are negitive?
Just to clarify: My angles appear to be sort of correct, just pointing in the wrong quadrant depending on the position and or velocity vectors.
Yeah so I was wrong, the above all do return the correct values after all.
So I found an edge case where most of the above calculations fail.
Given position and velocity:
pos = new Vector4() { X = -0.208994076275941, Y = 0.955838328099748 };
vel = new Vector4() { X = -2.1678187689294E-07, Y = -7.93096769486992E-08 };
I get some odd results, ie ~ -31.1 degrees, when I think it should return ` 31.1 (non negative). one of them returns ~ 328.8.
However testing with this position and velocity the results apear to be ok:
pos = new Vector4() { X = -0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(-25) };
See my answer for extra code on how I'm testing and the math I'm using for some of the other variables.
I'm going around in circles on this one. this is a result of a bug in my existing code that shows up under some conditions but not others.
I guess the real question now is WHY am I getting different results with position/velocity above that don't match to my expectations or each other?
Assuming 2D case... I am doing this differently:
compute radius of semi axises and rotation
so you need to remember whole orbit and find 2 most distant points on it that is major axis a. The minor axis b usually is 90 deg from major axis but to be sure just fins 2 perpendicularly most distant points on your orbit to major axis. So now you got both semi axises. The initial rotation is computed from the major axis by atan2.
compute true anomaly E
so if center is x0,y0 (intersection of a,b or center point of both) initial rotation is ang0 (angle of a) and your point on orbit is x,y then:
E = atan2(y-y0,x-x0) - ang0
However in order to match Newton/D'Alembert physics to Kepler orbital parameters you need to boost the integration precision like I did here:
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
see the [Edit3] Improving Newton D'ALembert integration precision even more in there.
For more info and equations see:
Solving Kepler's equation
[Edit1] so you want to compute V I see it like this:
As you got your coordinates relative to parent you can assume they are already in focal point centered so no need for x0,y0 anymore. Of coarse if you want high precision and have more than 2 bodies (focal mass + object + proximity object(s) like moons) then the parent mass will no longer be in focal point of orbit but close to it ... and to remedy you need to use real focal point position so x0,y0 again... So how to do it:
compute center point (cx,cy) and a,b semi axises
so its the same as in previous text.
compute focal point (x0,y0) in orbit axis aligned coordinates
simple:
x0 = cx + sqrt( a^2 + b^2 );
y0 = cy;
initial angle ang0 of a
let xa,ya be the intersection of orbit and major axis a on the side with bigger speeds (near parent object focus). Then:
ang0 = atan2( ya-cy , xa-cx );
and finally the V fore any of yours x,y
V = atan2( y-y0 , x-x0 ) - ang0;
Ok so on further testing it appears my original calcs do all return the correct values, however when I was looking at the outputs I was not taking the LoP into account and basically not recognizing that 180 is essentially the same angle as -180.
(I was also looking at the output in radians and just didn't see what should have been obvious)
Long story short, I have a bug I thought was in this area of the code and got lost in the weeds.
Seems I was wrong above. see OP for edge case.
Here's some code I used to test these,
I used variations of the following inputs:
pos = new Vector4() { X = 0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(25) };
And tested them with the following
double parentMass = 1.989e30;
double objMass = 2.2e+15;
double sgp = GameConstants.Science.GravitationalConstant * (parentMass + objMass) / 3.347928976e33;
Vector4 ev = OrbitMath.EccentricityVector(sgp, pos, vel);
double e = ev.Length();
double specificOrbitalEnergy = Math.Pow(vel.Length(), 2) * 0.5 - sgp / pos.Length();
double a = -sgp / (2 * specificOrbitalEnergy);
double ae = e * a;
double aop = Math.Atan2(ev.Y, ev.X);
double eccentricAnomaly = OrbitMath.GetEccentricAnomalyFromStateVectors(pos, a, ae, aop);
double aopD = Angle.ToDegrees(aop);
double directAngle = Math.Atan2(pos.Y, pos.X);
var θ1 = OrbitMath.TrueAnomaly(sgp, pos, vel);
var θ2 = OrbitMath.TrueAnomaly(ev, pos, vel);
var θ3 = OrbitMath.TrueAnomalyFromEccentricAnomaly(e, eccentricAnomaly);
var θ4 = OrbitMath.TrueAnomalyFromEccentricAnomaly2(e, eccentricAnomaly);
var θ5 = OrbitMath.TrueAnomaly(pos, aop);
double angleΔ = 0.0000001; //this is the "acceptable" amount of error, really only the TrueAnomalyFromEccentricAnomaly() calcs needed this.
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ1), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ2), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ3), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ4), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ5), angleΔ);
and the following to compare the angles:
public static double DifferenceBetweenRadians(double a1, double a2)
{
return Math.PI - Math.Abs(Math.Abs(a1 - a2) - Math.PI);
}
And eccentricity Vector found thus:
public static Vector4 EccentricityVector(double sgp, Vector4 position, Vector4 velocity)
{
Vector4 angularMomentum = Vector4.Cross(position, velocity);
Vector4 foo1 = Vector4.Cross(velocity, angularMomentum) / sgp;
var foo2 = position / position.Length();
return foo1 - foo2;
}
And EccentricAnomaly:
public static double GetEccentricAnomalyFromStateVectors(Vector4 position, double a, double linierEccentricity, double aop)
{
var x = (position.X * Math.Cos(-aop)) - (position.Y * Math.Sin(-aop));
x = linierEccentricity + x;
double foo = GMath.Clamp(x / a, -1, 1); //because sometimes we were getting a floating point error that resulted in numbers infinatly smaller than -1
return Math.Acos(foo);
}
Thanks to Futurogogist and Spektre for their help.
I am assuming you are working in two dimensions?
Two dimensional vectors of position p and velocity v. The constant K is the the product of the gravitational constant and the mass of the gravity generating body. Calculate the eccentricity vector
eccVector = (dot(v, v)*p - dot(v, p)*v) / K - p / sqrt(dot(p, p));
eccentricity = sqrt(dot(eccVector, eccVector));
eccVector = eccVector / eccentricity;
b = { - eccVector.y, eccVector.x}; //unit vector perpendicular to eccVector
r = sqrt(dot(p, p));
cos_TA = dot(p, eccVector) / r; \\ cosine of true anomaly
sin_TA = dot(p, b) / r; \\ sine of true anomaly
if (sin_TA >= 0) {
trueAnomaly = arccos(cos_TA);
}
else if (sin_TA < 0){
trueAnomaly = 2*pi - arccos(cos_TA);
}
When you get the elements for the Array (the coefficients) and then use them in the standard fashion defined for linear algebra to multiply the matrix with a vector, you get a different answer than if you call the TransformPoints method for the Matrix. I believe this is an error in either the Microsoft documentation or in the implementation.
That is, given the matrix M and the Point a, where M(a) is the result of calling M.TranformPoints(a)
M(a).x != M11*ax + M12*ay + dx
and
M(a).y != M21*ax + M22*ay + dy
However, looking at all the definitions I could find for multiply a matrix and a vector and for matrix notation (i,j) where i is the row and j is the column the above equations should be equal.
The only way you get the correct answer is if you switch the positions of M12 and M21 above. However, if you do this the equations no longer agree with the standard notion and common usage of multiplying a matrix and a vector (or point).
See the more detailed explanation below with the MSDN documentation and a sample program I wrote.
The documentation for the Constructor taking six Singles is Matrix Constructor
Matrix Constructor(Single, Single, Single, Single, Single, Single)
Syntax:
public Matrix(
float m11,
float m12,
float m21,
float m22,
float dx,
float dy)
Parameters:
m11 Type: System.Single The value in the first row and first
column of the new Matrix.
m12 Type: System.Single The value in
the first row and second column of the new Matrix.
m21 Type: System.Single The value in the second row and first
column of the new Matrix.
m22 Type: System.Single The value in the second row and second
column of the new Matrix.
dx Type: System.Single The value in the third row and first column
of the new Matrix.
dy Type: System.Single The value in the third row and second
column of the new Matrix.
The documentation for the Elements Property is: Matrix.Elements Property
Gets an array of floating-point values that represents the elements
of this Matrix.
Property Value
Type: System.Single[]
An array of floating-point values that represents the elements of
this Matrix.
Remarks: The elements m11, m12, m21, m22, dx, and dy of the Matrix are represented by the values in the array in that order.
I wrote a sample program to confirm what I was seeing and it is shown below. Note, that using the elements (matrix coefficients) in equations only works if I transpose m12 and m21 which does not agree with general matrix (i,j) (row, column) notation and multiplication. My program is below. My questions is whether Microsoft's documentation/implementation is wrong? Am I overlooking something or doing something wrong?
In my code the X,Y values for PointsA[0] only equals the X and Y values for x1, y1 where the m12 and m21 element values are transposed in the equation. Code is below:
class Program
{
static void Main(string[] args)
{
var pointsA = new Point[1];
pointsA[0].X = 1;
pointsA[0].Y = 1;
var pointsB = new Point[1];
pointsB[0].X = 1;
pointsB[0].Y = 1;
// Transform PointsA using Matrix
Matrix m = new Matrix(1,1,0,1,0,0);
m.TransformPoints(pointsA);
// Transform PointsB using Elements.
var elements = m.Elements;
var m11 = elements[0];
var m12 = elements[1];
var m21 = elements[2];
var m22 = elements[3];
var dx = elements[4];
var dy = elements[5];
var pointB = pointsB[0];
var x = m11 * pointB.X + m12 * pointB.Y + dx;
var y = m21 * pointB.X + m22 * pointB.Y + dy;
// Correct answer but had to transpose positions of m12 and m21 from what would be the normal matrix x vector multiplication.
var x1 = m11*pointB.X + m21*pointB.Y + dx;
var y1 = m12*pointB.X + m22*pointB.Y + dy;
}
}
Yes the correct answer is this:
var x1 = m11*pointB.X + m21*pointB.Y + dx;
var y1 = m12*pointB.X + m22*pointB.Y + dy;
But there's no error. The description of the matrix gives us this, 3 rows, 2 columns (3x2):
[m11 m12]
[m21 m22]
[dx dy ]
When multiplied by the vector which must be 1 row, 3 columns (1x3):
[ax ay 1][m11 m12]
[m21 m22]
[dx dy ]
You get the 1x2 result:
[ax * m11 + ay * m21 + dx , ax * m12 + ay * m22 + dy]
This documentation demonstrates that they use a row-major matrices and row vectors rather than the more common mathematical convention of column-major and column vectors.
So in actual fact the matrix represents a 3x3 so that they can be chained together but this makes no difference as the final column is always 0 0 1 in an affine transformation which is all this class can represent.
[ax ay 1][m11 m12 0] = [ax * m11 + ay * m21 + dx , ax * m12 + ay * m22 + dy , 1]
[m21 m22 0]
[dx dy 1]
Plus side to this order
If I have a point (P) and I want to Translate (T) and then Scale (S) I find it more readable with this notation:
transformedPoint = P*T*S
Rather than:
transformedPoint = S*T*P
And so code that uses this order will also be more readable.
The standard Cartesian coordinate system is right-handed, i.e. you have to turn a vector pointing in x-direction counterclockwise by 90 degrees in order to make it point in y-direction. The coordinate system used in System.Drawing is left-handed, because the y-axis points downwards. This might explain the discrepancy.
| x
--+----->
| Coordinate system used by System.Drawing
y|
v
I'm trying to merge two databases for consolidating two clients' websites. However, Client A has been using regular Lat/Lon pairs for geolocation, while Client B is using Lambert 72 (X/Y) coordinates.
I've built a script that should convert these coordinates (as I'm not sure which coordinates will be used in the final merged database, I'm trying converting them either way).
I took some snippets from here: http://zoologie.umh.ac.be/tc/algorithms.aspx
Please note that all coordinates mentioned below point to locations in Belgium.
I'm converting some coordinates to see if the calculations are correct, but the coordinates I'm getting seem to be way off. For reference, the center of Belgium is roughly (North 50.84323737103243, East 4.355735778808594), so I'd expect all coordinates to be close to these values.
I converted the Lambert 72 value (X: 151488250, Y: 170492909) to a Lat/Lon pair, but the result is: (-87.538.... , -50.724....) which is way off from the expected values.
If I convert full circle (Lambert->LatLon->Lambert and vice versa), I get the same result values as I entered, so I know my conversions are at least consistent and the conversions are perfect inversions of one another.
I tried some online converter tools as well, and they give me the same (-87.538.... , -50.724....) result.
Since multiple sources yield the same results, and my conversions are correct inversions of eachother, I'm figuring the calculations themselves are correct, but the resulting values still need to be converted/offset further?
I consider myself to be sufficient in algebra, but cartographic projections completely elude me.
Can someone please shed some light on this?
Extra Info
I hope I posted this in the correct forum. I'm not really sure where to put this as this is a mix of geography, mathematics and coding/conversion...
The mentioned Lambert coordinates (X: 151488250, Y: 170492909) point to a location in Brussels, so the Lat/Lon result should be very near to (North 50.84323737103243, East 4.355735778808594).
Please find my conversion functions below:
public static Lambert72 LatLon_To_Lambert72(LatLon latlon)
{
var lat = latlon.Lat;
var lng = latlon.Lon;
double LongRef = 0.076042943;
//=4°21'24"983
double bLamb = 6378388 * (1 - (1 / 297));
double aCarre = Math.Pow(6378388, 2);
double eCarre = (aCarre - Math.Pow(bLamb, 2)) / aCarre;
double KLamb = 11565915.812935;
double nLamb = 0.7716421928;
double eLamb = Math.Sqrt(eCarre);
double eSur2 = eLamb / 2;
//conversion to radians
lat = (Math.PI / 180) * lat;
lng = (Math.PI / 180) * lng;
double eSinLatitude = eLamb * Math.Sin(lat);
double TanZDemi = (Math.Tan((Math.PI / 4) - (lat / 2))) * (Math.Pow(((1 + (eSinLatitude)) / (1 - (eSinLatitude))), (eSur2)));
double RLamb = KLamb * (Math.Pow((TanZDemi), nLamb));
double Teta = nLamb * (lng - LongRef);
double x = 0;
double y = 0;
x = 150000 + 0.01256 + RLamb * Math.Sin(Teta - 0.000142043);
y = 5400000 + 88.4378 - RLamb * Math.Cos(Teta - 0.000142043);
return new Lambert72(x, y);
}
public static LatLon Lambert72_To_LatLon(Lambert72 lb72)
{
double X = lb72.X;
double Y = lb72.Y;
double LongRef = 0.076042943;
//=4°21'24"983
double nLamb = 0.7716421928;
double aCarre = Math.Pow(6378388, 2);
double bLamb = 6378388 * (1 - (1 / 297));
double eCarre = (aCarre - Math.Pow(bLamb, 2)) / aCarre;
double KLamb = 11565915.812935;
double eLamb = Math.Sqrt(eCarre);
double eSur2 = eLamb / 2;
double Tan1 = (X - 150000.01256) / (5400088.4378 - Y);
double Lambda = LongRef + (1 / nLamb) * (0.000142043 + Math.Atan(Tan1));
double RLamb = Math.Sqrt(Math.Pow((X - 150000.01256), 2) + Math.Pow((5400088.4378 - Y), 2));
double TanZDemi = Math.Pow((RLamb / KLamb), (1 / nLamb));
double Lati1 = 2 * Math.Atan(TanZDemi);
double eSin = 0;
double Mult1 = 0;
double Mult2 = 0;
double Mult = 0;
double LatiN = 0;
double Diff = 0;
double lat = 0;
double lng = 0;
do {
eSin = eLamb * Math.Sin(Lati1);
Mult1 = 1 - eSin;
Mult2 = 1 + eSin;
Mult = Math.Pow((Mult1 / Mult2), (eLamb / 2));
LatiN = (Math.PI / 2) - (2 * (Math.Atan(TanZDemi * Mult)));
Diff = LatiN - Lati1;
Lati1 = LatiN;
} while (Math.Abs(Diff) > 2.77777E-08);
lat = (LatiN * 180) / Math.PI;
lng = (Lambda * 180) / Math.PI;
return new LatLon(lat, lng);
}
I am the author of the page you mention in your post.
I don't know if you have resolved your problem but the Lambert coordinates you give are not correct. I think that you have to divide them by 1000. That gives x=151488.250 and y=170492.909 which are possible coordinates and corresponding to a street in... Brussels.
Be careful to the choice of the datum when converting to and from lat/lng values.