0

I thought there would be plenty of results when searching for this on Google, but it turns out that in most cases, just like here on stackoverflow, the questions are very specific and include Google Maps, some GIS or the real world with elliptical shape.

In my case,

  • I have an object with lat, long and alt values, where lat and long are floats in degrees and alt is a float as decimal value and representing the distance from the center of the sphere. So there are no minutes (10°30' is 10,5).
  • The object moves on all 3 axes x, y and z, where the movement is relative to its current position
  • I need to calculate new lat, long and alt values

Without thinking about it a lot, I first implemented it like this:

Just added z movement to alt, then calculated the virtual sphere's circumference to get the degree per unit (meter). With that I calculated the new lat first, and new long afterwards. I knew calculating one value after another is wrong, but as long as all the calculations in my "object world" would be done the same way, the overall behaviour would be ok. I put some thought into things like when the object goes around the entire sphere the long value doesn't change, and that going half around the sphere is different on x axis than on y axis (x axis: -180 to 180, y-axis -90 to 90) and things like that and it worked.

But then I realized that I didn't take into account that I calculated the degree per meter on the equator and didn't take other latitude values into account. At that point I knew things were more complicated and I started searching the web. But I didn't find an algorithm that would fit my needs. Now I'm sure this has been done before a lot of times, so I'm asking here in case someone has dealt with this topic before and can point me to a nice implementation :) .

What I found is this:

  • Algorithms converting lat/long to Mercator projection
  • Haversine formular for calculating a distance from two lat/long value pairs
  • other formulars for other purposes

Which didn't help me.

What helped me the most was this: Calculate latitude and longitude having meters distance from another latitude/longitude point

But I think I didn't fully understand it yet.

  • What is the course in radians? (regarding I have x and y movements)
  • The extrapolate method doesn't take the altitude / earth's radius into account?

It would be awesome if someone could help me with this!

(Sidenote: I'm implementing this in Erlang, but that doesn't matter, any kind of algorithm would help)


UPDATE

I implemented the function mentioned above and the one from the answer below. When testing I got wrong values, maybe because I made mistakes in the implementation or calculated wrong testing data. Let's see:

Implementation 1:

% My own implementation that only works on the equator so far. Simple calculations as mentioned above.

Tests (on equator) ok.

Implementation 2:

% @doc calculates new lat+long+alt values for old values + movement vector
% https://stackoverflow.com/questions/5857523/calculate-latitude-and-longitude-having-meters-distance-from-another-latitude-lo
calc_position2(LastCurrLat, LastCurrLong, LastCurrAlt, MoveX, MoveY, MoveZ) ->
    % first the new altitude
    NewCurrAlt = LastCurrAlt + MoveZ,

    % original algorithm: http://williams.best.vwh.net/avform.htm#LL
    % lat=asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
    % dlon=atan2(sin(tc)*sin(d)*cos(lat1),cos(d)-sin(lat1)*sin(lat))
    % lon=mod(lon1+dlon +pi,2*pi)-pi
    % where:
    % lat1, lon1    - start point in radians
    % d             - distance in radians
    % tc            - course in radians

    % -> for the found implementation to work some value conversions are needed
    CourseDeg = calc_course(MoveX, MoveY),
    CourseRad = deg_to_rad(CourseDeg), % todo: cleanup: in course the calculated values are rad anyway, converting to deg is just an extra calculation
    Distance = calc_distance(MoveX, MoveY),
    DistanceDeg = calc_degrees_per_meter_at_equator(NewCurrAlt) * Distance,
    DistanceRad = deg_to_rad(DistanceDeg),
    Lat1Rad = deg_to_rad(LastCurrLat),
    Lon1Rad = deg_to_rad(LastCurrLong),

    LatRad = math:asin(math:sin(Lat1Rad) * math:cos(DistanceRad) + math:cos(Lat1Rad) * math:sin(DistanceRad) * math:cos(CourseRad)),
    Dlon = math:atan2(math:sin(CourseRad) * math:sin(DistanceRad) * math:cos(Lat1Rad), math:cos(DistanceRad) - math:sin(Lat1Rad) * math:sin(LatRad)),
    LonRad = remainder((Lon1Rad + Dlon + math:pi()), (2 * math:pi())) - math:pi(),

    NewCurrLat = rad_to_deg(LatRad),
    NewCurrLong = rad_to_deg(LonRad),

    {NewCurrLat, NewCurrLong, NewCurrAlt}.

% some trigonometry
% returns angle between adjacent and hypotenuse, with MoveX as adjacent and MoveY as opposite
calc_course(MoveX, MoveY) ->
    case MoveX > 0 of
        true ->
            case MoveY > 0 of
                true ->
                    % tan(alpha) = opposite / adjacent
                    % arc tan to get the alpha
                    % erlang returns radians -> convert to degrees
                    Deg = rad_to_deg(math:atan(MoveY / MoveX));
                false ->
                    Temp = 360 - rad_to_deg(math:atan((MoveY * -1) / MoveX)),
                    case Temp == 360 of
                        true ->
                            Deg = 0.0;
                        false ->
                            Deg = Temp
                    end
            end;
        false ->
            % attention! MoveX not > 0 -> can be 0 -> div by zero
            case MoveX == 0 of
                true ->
                    case MoveY > 0 of
                        true ->
                            Deg = 90.0;
                        false ->
                            case MoveY == 0 of
                                true ->
                                    Deg = 0.0;
                                false ->
                                    Deg = 270.0
                            end
                    end;
                false -> % MoveX < 0
                    case MoveY > 0 of
                        true ->
                            Deg = 180 - rad_to_deg(math:atan(MoveY / (MoveX * -1)));
                        false ->
                            Deg = 180 + rad_to_deg(math:atan((MoveY * -1) / (MoveX * -1)))
                    end
            end
    end,
    Deg.

rad_to_deg(X) ->
    X * 180 / math:pi().
deg_to_rad(X) ->
    X * math:pi() / 180.

% distance = hypetenuse in Pythagorean theorem
calc_distance(MoveX, MoveY) ->
    math:sqrt(math:pow(MoveX,2) + math:pow(MoveY,2)).

calc_degrees_per_meter_at_equator(Alt) ->
    Circumference = 2 * math:pi() * Alt,
    360 / Circumference.

% erlang rem only operates with integers
% https://stackoverflow.com/questions/9297424/stdremainder-in-erlang
remainder(A, B) ->
    A_div_B = A / B,
    N = round(A_div_B),

    case (abs(N - A_div_B) == 0.5) of
    true ->
        A_div_B_Trunc = trunc(A_div_B),

        New_N = case ((abs(A_div_B_Trunc) rem 2) == 0) of
        true -> A_div_B_Trunc;
        false ->
            case (A_div_B >= 0) of
            true -> A_div_B_Trunc + 1;
            false -> A_div_B_Trunc - 1
            end
        end,
        A - New_N * B;
    false ->
        A - N * B
    end.

Tests: Object at lat/lon/alt (10°,10°,6371000m). No movement (0m,0m,0m). Expected:

{1.000000e+001,1.000000e+001,6.371000e+006}

But returns:

{1.000000e+001,-3.500000e+002,6.371000e+006}

360 degree less than expected...

Object at (0°,10°,6371000m), moving (10m,0m,0m). Expected:

{0.000000e+000,1.000009e+001,6.371000e+006}

Not sure if some digits are just not being displayed. Should be something like 10.000089932160591 as longitude. Anyway - returns:

{8.993216e-005,-3.500000e+002,6.371000e+006}

Same wrong longitude value although we're moving now? And a changed latitude value although we didn't move on Y-Axis?

What about same position, now moving 5,000,000m east?

{0.000000e+000,5.496608e+001,6.371000e+006} % expected
{4.496608e+001,-3.500000e+002,6.371000e+006} % returned

Implementation 3:

calc_position3(LastCurrLat, LastCurrLong, LastCurrAlt, MoveX, MoveY, MoveZ) ->
    {CurrX, CurrY, CurrZ} = spherical_to_cartesian(LastCurrLat, LastCurrLong, LastCurrAlt),
    NewX = CurrX + MoveX,
    NewY = CurrY + MoveY,
    NewZ = CurrZ + MoveZ,
    {NewCurrLat, NewCurrLong, NewCurrAlt} = cartesian_to_spherical(NewX, NewY, NewZ),
    {NewCurrLat, NewCurrLong, NewCurrAlt}.

spherical_to_cartesian(Lat, Lon, Alt) ->
    X = Alt * math:cos(Lat) * math:cos(Lon),
    Y = Alt * math:cos(Lat) * math:sin(Lon),
    Z = Alt * math:sin(Lat),
    {X, Y, Z}.

cartesian_to_spherical(X, Y, Z) ->
    R = math:sqrt(math:pow(X,2) + math:pow(Y,2)),
    Alt = math:sqrt(math:pow(X,2) + math:pow(Y,2) + math:pow(Z,2)),
    Lat = math:asin(Z / Alt),
    case R > 0 of
        true ->
            Lon = math:acos(X / R);
        false -> % actually: if R == 0, but it can never be negative (see above)
            Lon = 0
    end,
    {Lat, Lon, Alt}.

Tests like above:

Object at (10°,10°,6371000m), no movement

{1.000000e+001,1.000000e+001,6.371000e+006} % expected
{-5.752220e-001,5.752220e-001,6.371000e+006} % returned

At (0°,10°,6371000m), moving (10m,0m,0m)

{0.000000e+000,1.000009e+001,6.371000e+006} % expected
{0.000000e+000,2.566370e+000,6.370992e+006} % returned

At (0°,10°,6371000m), moving (5000000m,0m,0m)

{0.000000e+000,5.496608e+001,6.371000e+006} % expected
{0.000000e+000,1.670216e+000,3.483159e+006} % returned

So: Did I miss some rad to deg conversions or something similar?

P.S.: Sorry for the bad syntax highlighting, but Erlang doesn't seem to be available, so I took Shellscript. Makes it a bit more readable.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
phlow
  • 122
  • 5
  • 12
  • is alt in "meters above see" or "meters from center of world"? In general, wouldn't `to_lat_lon_alt(to_x_y_z( ... ) + movement)` be enough? – Peter Schneider Dec 04 '12 at 20:00
  • Is the object's movement in x,y,z in an independant xyz coordinate base or in coordinates related to its curent position? To clarify, local coordinates would be, for example, x = North, y = west, z = up (from where you are). An independant base might be (approximately), x = direction of pole star, y = direction of galactic centre (z perpendicular to both of these). Once you have sorted this out, the simplest way is probably to convert lat, long, and alt (and x,y,z if they are dependant) into a single independant x,y,z base, then add them together, and convert back into lat, long, and altitude. – Penguino Dec 04 '12 at 20:01
  • @PeterSchneider: alt is in meters from center of world. Your proposed function would work I think, but from what I found out so far the implementations isn't trivial. – phlow Dec 04 '12 at 20:32
  • @Penguino: x, y and z are related to the current position. Okay so both of your answers propose the same method. Maybe I should search for an implementation of that. – phlow Dec 04 '12 at 20:33
  • Have a look at this: http://kartoweb.itc.nl/geometrics/Coordinate%20transformations/coordtrans.html for some coordinate transformation basics. – WaywiserTundish Dec 05 '12 at 04:36
  • convert lat/lon (geodetic) to ECEF (earth centered, earth fixed). Convert x,y,z (NEU - North/East/Up) to ECEF. Add the ECEF. Convert from ECEF to geodetic. Google 'geodetic to ECEF' for the formula. – TreyA Dec 05 '12 at 10:37
  • That's what I meant with specific questions/answers. ECEF seems to be for our earth, with elliptical shape and even the poles not at the rotational axes. Just like UTM doesn't cover the pole regions. Or Mercator distorting the sizes of continents. Or am I all wrong? – phlow Dec 05 '12 at 20:08
  • ECEF is at the rotational axis. If you want a different planet, then you need to find the ellipsoid model (semi major axis and flattening). ECEF is Cartesian (x,y,z) centered at the earth. UTM and Mercator are x,y flat earth. – TreyA Dec 06 '12 at 11:42
  • @phlow for poles you have to use UPS: UTM / UPS is usually a goo d combination – AlexWien Dec 07 '12 at 17:09
  • @phlow I woul dnot use the algo that Ted Hopp suggest: Each projection has its advanatges and drawback, as long as you and he do not know the name of that transformation, you cannot know the "good" and bad things. There exist no transformation from spehrcial 3d to 2d which keeps all attributes: One is ever ( at least slightly) wrong: distance, angle, area. – AlexWien Dec 07 '12 at 17:12
  • @phlow I have working some time with such projections and applications: You fuirst have to state which calculations you need, then you choose the appropriate transormation: E.g Do you want to calculate the distacne between two points? The 2d or 3d distance? How long will the distance max be? 1km? 1000 Km? So pleas estate all operations you want to calculate. – AlexWien Dec 07 '12 at 17:14
  • @AlexWien: I mentioned that I want to calculate new lat,long,alt values from existing lat,long,alt values + x,y,z movement values. No calculation of distance (so far - might come across it in the future). Regarding 1 or 1000 km: I wanted to keep this really general and not related to our earth. Maybe the object moves around the sphere 4 times (huge x value). We shouldn't even assume a surface of a sphere. The object just has an altitude which is its distance to the spheres' center and can change just like the x,y values. – phlow Dec 07 '12 at 20:26
  • @phlow and what the hell are the units of x,y,(z) x,y: delta degrees ?, and where they come from? – AlexWien Dec 07 '12 at 20:40
  • @AlexWien: x,y are not degrees, that's the problem here. They could be miles or metres. You need some kind of conversion, and that's not the same at different latitudes. I might just have found the solution though, which is my own implementation plus the longitudeDistanceAtLatitude method from the mentioned http://stackoverflow.com/questions/5857523/calculate-latitude-and-longitude-having-meters-distance-from-another-latitude-lo. I'll unit test it a bit more and then publish it as an answer (if everything works). – phlow Dec 07 '12 at 23:01
  • @phlow The problem with all calculations is unprecise naming: In which coordinate reference system is your point? There are far over 20 different lat/lon systems. Is that lat/long WGS84? (but the altitude is wrong for that coord sys). So please find out what is the precise name of that coord system. Or is this for a computer game? In that case – AlexWien Dec 08 '12 at 16:38
  • @AlexWien: In the end it's for a computer game, but as I mentioned I'm looking for a really general approach, that just has nothing to do with our earth and its specifics. – phlow Dec 08 '12 at 16:57
  • @phlow: a general solution to your problem has been done. As mentioned, you have ENU and geodetic coordinates. To keep the transformations general, then keep the semi-major axis and flattening as variables in the equations. Navigation around the poles is not trivial. You either decide to stay away from poles or implement a closed for solution. – TreyA Dec 09 '12 at 13:25
  • Even in commercial application its is possible to state: "does not work above 80° or below -80° of latitude (near poles)". I usually state that (for automotive applications) . For games, follow @TreyA advise, and simply stay inbetween latitude [-80, 80]: As i mentioned below there have been two crashes of jet fighters crossing the datum limit caused by a bug that most people will do, So make your life not more difficult as it needs to be. – AlexWien Dec 09 '12 at 16:08

2 Answers2

1

Let's assume:

  • the equator is in the x-y plane
  • latitude 0, longitude 0, altitude > 0 is on the +x axis
  • the poles are at longitude 0

With these assumptions, to convert from spherical (lat, lng, alt) to Cartesian (x, y, z):

x = alt * cos(lat) * cos(lng)
y = alt * cos(lat) * sin(lng)
z = alt * sin(lat)

To convert from Cartesian (x, y, z) to spherical (lat, lng, alt):

r = sqrt(x*x + y*y)
alt = sqrt(x*x + y*y + z*z)
lat = arcsin(z / alt)
       / arccos(x / r) if r > 0
lng = -|
       \ 0 if r == 0

Then follow @Peter's suggestion:

(lat, lng, alt) = toSpherical(movement + toCartesian(lat, lng, alt))
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • Looks promising. I will implement it in Erlang in the next days and point my unit tests to the new implementation. – phlow Dec 04 '12 at 23:28
  • See the updated question. Did I make a mistake while implementing your suggestion? – phlow Dec 05 '12 at 19:59
  • You are projecting spherical to cartesian. How is that specific projection called? – AlexWien Dec 07 '12 at 14:46
  • @AlexWien - It's not a projection. It's a coordinate system transformation. There's no special name for the transformation itself, just as (as far as I know) there's no special name for the transformations between Cartesian and polar coordinates in 2D. – Ted Hopp Dec 07 '12 at 15:48
  • @phlow - I haven't looked at all the details of your calculations, but at first glance it looks like your code is generating the correct numbers except that some of the angles are off by 2π. So, for instance, 10° is the same as -350°. Normalize the angles so that latitude is in the interval [-π, π] and longitude is in [0, 2π); after that, your answers should be much closer to what you expect. – Ted Hopp Dec 07 '12 at 15:55
  • @Tedd Ok it tranforms form ECEF Earth Centric Coordinates to something like a 2d plane + height; If we ignore the z, then it would be a spehrical to cartesian plane projection, which has special advanbtages and drawback, like all projections. There is no perfect projection: one of: distance, angle, area is ever wrong. So if you dont know the name of that matematiucs / algorithm, then you cannot know the drawbacks, therfore it is doubtfull to use it. – AlexWien Dec 07 '12 at 17:08
  • @AlexWien - **Converting to spherical coordinates is not a projection**. A projection (in the 3D sense) maps a point to a lower dimensional subspace. This just converts between _descriptions_ of a single point. There is no loss of information. (Spherical coordinates have an indeterminate longitude at the poles, and another if negative radii are allowed, but that just means that some or all points can have multiple sets of spherical coordinates. The points themselves are unambiguously defined in 3D.) Sure, if you throw away z you lose information, but that's something you introduced, not I. – Ted Hopp Dec 07 '12 at 17:25
  • @Ted Hopp What you show is a faked 3D->3D transformation. To understand better remove the altitude. Asume that altitude is ever 0. Then it is a tramformatin from spehrical to cartesian, which cannot be done without loss of information. This is mathematcialy prooved. The goal of that transformation you provided is obviosuly to use simpler school mathematics to calculate the distance between two coordinates. Transformation from spherical to cartesian will loose information. Consider the distance(p1,p2) angle(p1,p2) area(p1,p2,p3) and compare with sperical versions of that functions – AlexWien Dec 07 '12 at 18:31
  • @AlexWien - You keep throwing away the altitude and then saying my formulas don't work. That is just silly; of course they don't work if you ignore the altitude. Please learn the difference between a coordinate system transformation (what I proposed) and a projection (what you are doing). You might also want to read up about the [spherical coordinate system](http://en.wikipedia.org/wiki/Spherical_coordinate_system); it is a completely valid way of describing points in 3D. (By the way, the x and y of the Cartesian system are not with respect to the surface of the sphere, either.) – Ted Hopp Dec 07 '12 at 18:45
  • @Ted Hopp Simpyl set altitue to 0, Then it is not thrown away. Consider the vehicles are boats on the eartjh surfcase with altitude 0. Now you should see this is equivalen to a sphreical to plane transformation. Which is well known to cannot work whitout of chnaged bahviour in the operations on it. – AlexWien Dec 07 '12 at 18:51
  • @AlexWien - Setting the altitude to 0 is throwing away it's existing value. – Ted Hopp Dec 07 '12 at 19:04
  • 1
    @Ted Hopp I wrote asume altitude is 0 (or more generally all objects have the same altutude) because they are Boats Or water aeroplanes. Reade more carefully. – AlexWien Dec 07 '12 at 19:52
  • @AlexWien - Perhaps you need to read more carefully. OP's question specified: **"object moves on all 3 axes x, y and z"**. OP also asked how to calculate "new longitude, latitude, **and altitude**". Your comments have been irrelevant to OP's question and my answer. – Ted Hopp Dec 07 '12 at 20:18
  • Please don't argue against each other too harsh, let's keep focused on the mathematical problem here :) . I figured that applying the x,y,z movement in the cartesian system works for small movements (10 m) but not for 5,000,000 m, like in one of my tests, because earth's curvature is ignored in that case and the object flies into space (that's why in the tests of your algorithm the altitude has changed although there was no movement on z axis). EDIT: Well, your algo actually doesn't add the movement, it's just the conversion, so I guess there's nothing wrong with it - it just doesn't help me) – phlow Dec 07 '12 at 22:53
  • To correct my last comment: I was wrong with my tests and assumptions. I always interpreted the movement on the x axis as a movement to the east, but that's wrong. So this answer might be absolutely correct. I'll write new tests in the next days and check it out. – phlow Dec 08 '12 at 17:00
1

From your answers to the comments I finally have enough information:

You have given a position of an object in geographical latitude, longitude (something equal or similar to WGS84 lat /long) and altitude value. Where altitude is measured from center of earth.

latitude : [-90 , 90] geographical latitude in decimal degrees, 90° is Northpole<br>
longitude: [-180, 180] longitude in decimal degrees, 0° is Greenwhich
altitude: [0 , INF ] in meters from center of earth.

So these coords are defined as Spherical coordinates, but beware of clockwise vs. counter clockwise (see later)

Further you have given a movement vector (dx,dy,dz) measured in meters which defines the relative movement from current position.
These vector is defined as cartesian vector.

Your application is neither for navigation, nor flight controll, its more in the area of games.

For the calculation you must know where the x,y,z Achsis are related. You should use the same Axis alignment that ECEF uses.

You need to perform these steps:

You first must know that geographical degrees are not mathematical degrees: Sometimes its is important to know that in mathematics degrees are counter clockwise, where in geography they are clockwise measured.
After that your coords must be converted to radians.

Convert the Spherical Coordinates to cartesian space. (Spherical to cartesian transformation: see http://en.wikipedia.org/wiki/Spherical_coordinate_system) But use Math.atan2() not atan().

(You might check your code with http://www.random-science-tools.com/maths/coordinate-converter.htm) Check if this conversion is used in the answer of Ted Hopp, too.

After the transformation you have a coordinate in an cartesian x,y,z space with unit = 1m.

Next is the cartesian movement vector: Be sure, or convert it, such that you have the correct axis alignment, that dx corresponds to your x, -dx to -x, and for y,dy, z,dz, too.

Then add the point to dx:

p2.x = p.x + dx;
p2.y = p.y + dy;
p2.z = p.z + dz;

Then reconvert back to spherical coordinates, as shown in the Wiki link or by Ted Hopp.

The other link (to stackoverflow) that you have given in your comments do other types of conversion, they will not work for your problem.

I recomend one special test case: set a spherical point to longitude = 179.9999999 then add 100m, the result should be -180.0000xx + something after the comma (Two jet fighters crashed for this problem when traversing the Datum limit)

AlexWien
  • 28,470
  • 6
  • 53
  • 83
  • Sorry for getting back to this so late. I had problems getting Ted Hopps answer to work. This one helped me a lot and thus I'll mark it as answer. I probably should update my question as well as show the code that now works, but I'm on a tight schedule atm, sorry. Maybe later! – phlow Jan 04 '13 at 18:43