How can I determine direction with GPS readings alone and display in 360 degrees?

11,014

Solution 1

The formula you need (for lon1, lon2 longitudes, and lat1, lat2 latitudes of the two points) is given in many places - for example, at http://www.movable-type.co.uk/scripts/latlong.html . The math goes like this (convert it to your favorite language...):

dLon = lon2 - lon1;
y = sin(dLon) * cos(lat2);
x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) *cos(dLon);
bearing = atan2(y, x) * 180 / Pi;

Note: atan2 reduces the result to a value between [-pi, pi]. After multiplying by 180/pi the number will be between [-180,180]. In order to get a value between 0 and 360 you could do, for example:

if (bearing < 0) bearing += 360;

I hope you can fill in any other details you need to make this work for you.

EDIT I have used the numbers your gave to write a little bit of code: this is Matlab, but it should be pretty readable:

% bearings
lat1 = 42.1234584;
lat2 = 42.1234583;
lon1 = -83.1234577;
lon2 = -83.1234510;

% convert to radians:
g2r = pi/180;
lat1r = lat1 * g2r;
lat2r = lat2 * g2r;
lon1r = lon1 * g2r;
lon2r = lon2 * g2r;
dlonr = lon2r - lon1r;
y = sin(dlonr) * cos(lat2r);
x = cos(lat1r)*sin(lat2r) - sin(lat1r) * cos(lat2r)*cos(dlonr);

% compute bearning and convert back to degrees:
bearing = atan2(y, x) / g2r;

fprintf(1,'x: %+.3e\ny: %+.3e\nbearing: %.3f\n', x, y, bearing);

This results in the output:

x: -1.745e-09
y: +8.673e-08
bearing: 91.153

As you can see, x and y are tiny - they represent the "fraction of the circumference of the earth" that you moved (about 3.5 meters due East, it would seem...). You should be able to debug your implementation with these numbers.

Notice by the way that GPS can have a poor "absolute" accuracy (in your case, uncertainty of > 100 m), yet still be good with "relative" accuracy (it measures the difference between two positions much better than 100 m).

Solution 2

if you are using the Location API then when your location changes you get a callback giving you a Location object which you can get the bearing of the direction moved from 0-360 degrees

Share:
11,014

Related videos on Youtube

Catttdaddy
Author by

Catttdaddy

Updated on June 05, 2022

Comments

  • Catttdaddy
    Catttdaddy almost 2 years

    I am working on an AS3 AIR for Android application. "my problem is this: given a GPS location, I want to retrieve the direction of movement (North, NW, West, SW, South, SE, East, NE)"

    I would like the code for how to use 2 different GPS readings to deduce the direction of travel. I would then like to use that data for 360 degrees.

    I think I understand how I can get general direction like North, South, East, and West. I would get 2 separate GPS readings and compare something like so...

    See if X is greater in the 1st or 2nd GPS reading. See if Y is greater in the 1st or 2nd GPS reading. From this I should be able to see the general direction. Compare the differences of X1 and X2 with the difference of Y1 and Y2. then you can see what direction the user was headed in the most. Then display the direction.

    Im not sure on how to take the data and use it for a 360 interpretation like a compass... can anyone help?

    WORKING CODE-------------------------------------------------------------------------------

    package 
    
    {
    import flash.display.Sprite;
    // Geolocation sensor stuff
    import flash.sensors.Geolocation; 
    import flash.events.GeolocationEvent;
    //Timer setup stuff for setting frequesncy of GPS Update
    import flash.utils.Timer;
    import flash.events.TimerEvent;
    
    // Sprite and Textfield display
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    
    
        public class FlashTest extends Sprite 
    
        {
            //Variable to check if current or prior Geolocation Event fired
            private var gpsCheck:int = 1;
            public var dLon:Number
    
    
            //latitude and longitude in degrees (RAW info from Geolocation)
            public var gpsLat1:Number
            public var gpsLon1:Number
            private var gpsLat2:Number
            private var gpsLon2:Number
            public var bearing:Number
    
    
            // Latitude and longitude in radians converted from Degrees
            public var gpsLat1R:Number
            public var gpsLon1R:Number
            private var gpsLat2R:Number
            private var gpsLon2R:Number     
            private var yy:Number  
            private var xx:Number          
    
    
            public function FlashTest() 
    
            {
                // Text box for displaying results
                var my_txt:TextField = new TextField();
                    my_txt.wordWrap=true; 
                    my_txt.width = 300;
                    my_txt.height = 300;
                     addChild(my_txt);
    
                /*
                //If GPS is on device create Geolocation object named "my_geo"
                //Request updates from my_geo every 2000 milliseconds. Run onGeoUpdate function
                //when Event is triggered. After that create the text box for displaying data.
                if (Geolocation.isSupported)
                {
                    var my_geo:Geolocation = new Geolocation();
                    my_geo.setRequestedUpdateInterval(2000);                
                    my_geo.addEventListener(GeolocationEvent.UPDATE, onGeoUpdate);
                    var my_txt:TextField = new TextField();
                    my_txt.wordWrap=true; 
                    my_txt.width = 300;
                    my_txt.height = 300;
                    addChild(my_txt);
                } 
                // If GPS is not supported on device display "No GPS Signal" 
                else
                {
                    addChild(my_txt);
                    my_txt.wordWrap=true; 
                    my_txt.width = 300;
                    my_txt.height = 300;
                    addChild(my_txt);
                    my_txt.text = "No GPS Signal ";
                }
                */
    
    
                // False GPS reading being passed for testing 
                //COMMENT THESE LINES OUT STARTING HERE---
                    gpsLat1 =  42.1234584;
                    gpsLon1 = -83.1234577;
                    gpsLat2 =  42.1234583;
                    gpsLon2 = -83.1234577;
               // END OF WHAT SHOULD BE COMMENTED OUT---
    
               // Equations to convert all RAW Geolocation information over to radians 
                    gpsLat1R = gpsLat1 * Math.PI / 180;
                    gpsLon1R = gpsLon1 * Math.PI / 180;
                    gpsLat2R = gpsLat2 * Math.PI / 180;
                    gpsLon2R = gpsLon2 * Math.PI / 180;
              // The rest of the math     
                    dLon = gpsLon1 - gpsLon2;
                    yy = Math.sin(dLon) * Math.cos(gpsLat2R);
                    xx = Math.cos(gpsLat1R) * Math.sin(gpsLat2R) - Math.sin(gpsLat1R) * Math.cos(gpsLat2R) * Math.cos(dLon);
                    bearing = Math.atan2(yy, xx) * 180 / Math.PI;
             // Run The Geoupdate function                
                onGeoUpdate();
    
    
            // onGeoUpdate basically displays the information that was collected and converted.
            // This is where you will put what you want the code to do with the results
                function onGeoUpdate():void
                {
                my_txt.text = "My Latitude is "+gpsLat1+ " and my Longitude is "+gpsLon1+ 
                "My 2nd Latitude is "+gpsLat2+" and my 2nd Longitude is "+gpsLon2+ 
                " Bearing is " +bearing;
    
                }            
            }
        }
    }
    
  • Catttdaddy
    Catttdaddy over 10 years
    When I use the Geolocation Object I am only getting the Lat/Lon with no decimal places. Its far to large to be able to get the bearing conveniently. When I look at the Geolocation in my map application I get at least 5 decimal places. Is it a property or something that will let me get most precise readings? I looked at horizontal and Vertical Accuracy but it doesnt match the informatino on other GPS apps. How can I get more precise GPS Info?
  • tyczj
    tyczj over 10 years
    the only way you get a more precise info is by getting a better GPS lock. the better the lock the more decimal places that will get returned. To get the Bearing from the location object all you have to do is location.getBearing() in the callback when your location changes
  • Catttdaddy
    Catttdaddy over 10 years
    I dont see "location" in the Air API. I do have a Geolocation API. In the Geolocation API I have a "heading" property but it does not work for Android. Is the "location" API for Adobe AIR? where is it cause even a google search for "AIR API location" I only get "Geolocation"...
  • tyczj
    tyczj over 10 years
    why do you need to use the AIR API for location just use the android API, it will probably solve most of your problems
  • Catttdaddy
    Catttdaddy over 10 years
    Because I am familiar with Actionscript 3 and not Java.
  • Catttdaddy
    Catttdaddy over 10 years
    I think I got the code implemented. It all looks correct but all I am getting is a zero value.
  • Floris
    Floris over 10 years
    Could you be using integer types anywhere? these are small numbers and small differences - you need a good floating point type (double) for the math to work... Can you show your code (maybe add to your question)? Are you sure there is a different in the longitude and latitudes? Can you print out the values of x and y?
  • Floris
    Floris over 10 years
    It all looks correct... except that the units of longitude and latitude probably don't "play nice" with your current use of the sin and cos functions. The former are presumably in degrees, and the latter expect radians. You convert degrees to radians with rads=degs*Math.PI/180; sometimes, a function sind and cosd is built in which expects the argument in degrees. I would print out x and y to help in the debugging, for sure.
  • Catttdaddy
    Catttdaddy over 10 years
    Im getting LonR=-1.45xxxxxxxxxxxx79 and LatR=0.73xxxxxxxxxxx84
  • Catttdaddy
    Catttdaddy over 10 years
    Im getting gpsLon2R=-1.45xxxxxxxxxxxx79 and gpsLat1R=0.73xxxxxxxxxxx84 I assume those are the radians. so my conversion from degrees to rad is working with gpsLat1R = gpsLat1 * Math.PI / 180; and gpsLon1R = gpsLon1 * Math.PI / 180; The bearing calculation must have a problem. My only difference from your code is where you have x = cos(lat1r)*sin(lat2r) - sin(lat1r) * cos(lat2r)*cos(dlon); I assumed "dlon" was a typo as you never mentioned "dlon" earlier in the code. you have only "dlonr".
  • Floris
    Floris over 10 years
    Sorry yes that was a typo. What do you get for the bearing when you fix the typo?
  • Catttdaddy
    Catttdaddy over 10 years
    The bearing is still "0"
  • Floris
    Floris over 10 years
    How strange. What do you get for x and y? Does your platform even recognize the atan2 function? In some systems the parameters for atan2 are reversed - meaning that you need x,y instead of y,x in order to get the right answer in those. You could also try using atan2(y/x, 1) (which works if x != 0) to see if rounding error in the atan2 function is the problem. What do you get for atan2(1,1) and atan2(1e-10, 1e-14), for example? The answer should be pi/4 and approximately pi/2, respectively...
  • Floris
    Floris over 10 years
    From help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/‌​… : "On Android devices, heading is not supported. The value of the heading property is always NaN (Not a Number)."
  • Floris
    Floris over 10 years
    Looking at your code carefully, it seems like gpsBearing() is only called once - instead of being called after each update of the GPS coordinates. It needs to live inside your onGeoUpdate function, doesn't it?
  • Catttdaddy
    Catttdaddy over 10 years
    X and Y are 0 when I trace them... I thought about bearings placement as well and it is not in the onGeoUpdate function on both sides of the if and then statement...
  • Floris
    Floris over 10 years
    Not sure I understand your last sentence. "I thought about...". what did you conclude? You need to compute bearing each time the location updates. Add gpsHeading(); right before gpsHeading = (bearing);. And don't use gpsHeading = (e.heading); in the gpsCheck == 2 case: it returns NaN on Android.
  • Catttdaddy
    Catttdaddy over 10 years
    Sorry about the last comment. I was very tired. I meant to say I thought about it and changed the code. I just updated my code to the the current try... I moved gpsbearing(); inside of onGeoUpdate . I am still getting 0 for bearing. I am also getting zero for X and Y... Im lost as to where the problem is... I really appreciate your patience and help.
  • Floris
    Floris over 10 years
    I'm stubborn, not touchy. Understand about comments made when tired. Can you create a standalone program that takes known numerical inputs (no geo events) and computes the bearing? Once you have that, litter your code with print statements to figure out where execution is not doing what you expect. I am not familiar with the IDE for Air, so I can't tell you how to step through your code, add breakpoints, etc - but that's what you will need to do once you have the standalone function working (something similar to my code snippet above). Can you confirm gpsBearing() is called?
  • Catttdaddy
    Catttdaddy over 10 years
    From what I can gather, it looks like I get everything until Math.cos and Math.sin are used. Im getting nothing for X,Y. Check it Here
  • Catttdaddy
    Catttdaddy over 10 years
    It looks like changing dLon = gpsLon1R - gpsLon2R; too dLon = gpsLon1 - gpsLon2; makes it not produce zeroes. Im now getting a bearing of 65. Im not good enough with the math to say if it is right or not. I will have to wait to get on my comp to recompile and put it on my phone. Thanks for your help.
  • Floris
    Floris over 10 years
    It was helpful that you created the wonderfl example. It turns out that variables x and y had a built in (inherited) meaning (I was wondering how you got away with not declaring them). Please see my working example code at wonderfl.net/c/bFRa ... I took the geoUpdate() function out to inline everything (because I was confused about the scope of x and y in your code). I told you I was stubborn... :-) Pretty sure you can now take it from here.
  • Catttdaddy
    Catttdaddy over 10 years
    Thank you much! I assume "x" and "y" are reserved for position. I think I got it!
  • Catttdaddy
    Catttdaddy over 10 years
    This is why I am not using the "heading". I am asking how to determine the heading from 2 gps locations. Not how to use heading.